@inflexa-ai/harness 0.35.8 → 0.36.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 (195) hide show
  1. package/dist/auth/types.d.ts +5 -14
  2. package/dist/auth/types.js +3 -10
  3. package/dist/billing/usage-recorder.d.ts +1 -1
  4. package/dist/contracts/analogy-report.d.ts +1 -1
  5. package/dist/contracts/analogy-report.js +1 -1
  6. package/dist/contracts/chat-parts.d.ts +2 -3
  7. package/dist/contracts/index.d.ts +0 -4
  8. package/dist/contracts/index.js +0 -2
  9. package/dist/index.d.ts +0 -7
  10. package/dist/index.js +0 -21
  11. package/dist/lib/metrics.d.ts +1 -1
  12. package/dist/lib/otel-spans.d.ts +0 -2
  13. package/dist/lib/otel-spans.js +0 -2
  14. package/dist/loop/run-agent.d.ts +1 -1
  15. package/dist/runtime/assemble.d.ts +0 -3
  16. package/dist/runtime/assemble.js +0 -3
  17. package/dist/state/index.d.ts +0 -2
  18. package/dist/state/index.js +0 -1
  19. package/dist/state/init.js +1 -60
  20. package/dist/state/purge-analysis.d.ts +1 -2
  21. package/dist/state/purge-analysis.js +1 -2
  22. package/dist/tasks/regulatory-corpus-boot-refresh.js +1 -1
  23. package/dist/tools/bio/gene-preclinical-profile.d.ts +1 -3
  24. package/dist/tools/bio/gene-preclinical-profile.js +1 -3
  25. package/dist/tools/lib/chembl-client.d.ts +1 -4
  26. package/dist/tools/lib/chembl-client.js +1 -4
  27. package/dist/tools/lib/clinical-benchmarks-client.d.ts +1 -1
  28. package/dist/tools/lib/clinical-benchmarks-client.js +1 -1
  29. package/dist/tools/lib/clinical-trials-client.d.ts +0 -3
  30. package/dist/tools/lib/clinical-trials-client.js +0 -3
  31. package/dist/tools/lib/identifier-resolver.d.ts +5 -7
  32. package/dist/tools/lib/identifier-resolver.js +5 -7
  33. package/dist/tools/lib/iuphar-client.d.ts +1 -3
  34. package/dist/tools/lib/iuphar-client.js +1 -3
  35. package/dist/tools/lib/opentargets-client.d.ts +1 -1
  36. package/dist/tools/lib/opentargets-client.js +1 -1
  37. package/dist/tools/lib/protein-family-normalize.d.ts +5 -7
  38. package/dist/tools/lib/protein-family-normalize.js +5 -7
  39. package/dist/tools/lib/uniprot-client.d.ts +0 -7
  40. package/dist/tools/lib/uniprot-client.js +2 -9
  41. package/package.json +1 -1
  42. package/dist/contracts/target-assessment-row.d.ts +0 -85
  43. package/dist/contracts/target-assessment-row.js +0 -37
  44. package/dist/contracts/target-dossier.d.ts +0 -12423
  45. package/dist/contracts/target-dossier.js +0 -1282
  46. package/dist/prompts/target-assessment/briefs/executive-recommendation.d.ts +0 -1
  47. package/dist/prompts/target-assessment/briefs/executive-recommendation.js +0 -85
  48. package/dist/prompts/target-assessment/briefs/index.d.ts +0 -4
  49. package/dist/prompts/target-assessment/briefs/index.js +0 -4
  50. package/dist/prompts/target-assessment/briefs/liability-bullets.d.ts +0 -1
  51. package/dist/prompts/target-assessment/briefs/liability-bullets.js +0 -114
  52. package/dist/prompts/target-assessment/briefs/target-organ-liabilities.d.ts +0 -1
  53. package/dist/prompts/target-assessment/briefs/target-organ-liabilities.js +0 -37
  54. package/dist/prompts/target-assessment/briefs/translational-commentary.d.ts +0 -1
  55. package/dist/prompts/target-assessment/briefs/translational-commentary.js +0 -23
  56. package/dist/prompts/target-assessment/drugs-in-class.d.ts +0 -1
  57. package/dist/prompts/target-assessment/drugs-in-class.js +0 -29
  58. package/dist/prompts/target-assessment/investigation/adversarial-critique.d.ts +0 -1
  59. package/dist/prompts/target-assessment/investigation/adversarial-critique.js +0 -67
  60. package/dist/prompts/target-assessment/investigation/claim-reverification.d.ts +0 -1
  61. package/dist/prompts/target-assessment/investigation/claim-reverification.js +0 -49
  62. package/dist/prompts/target-assessment/investigation/index.d.ts +0 -3
  63. package/dist/prompts/target-assessment/investigation/index.js +0 -3
  64. package/dist/prompts/target-assessment/investigation/mechanism-proposal.d.ts +0 -1
  65. package/dist/prompts/target-assessment/investigation/mechanism-proposal.js +0 -49
  66. package/dist/prompts/target-assessment/modulator-triage.d.ts +0 -1
  67. package/dist/prompts/target-assessment/modulator-triage.js +0 -31
  68. package/dist/prompts/target-assessment/off-target-annotator.d.ts +0 -1
  69. package/dist/prompts/target-assessment/off-target-annotator.js +0 -28
  70. package/dist/prompts/target-assessment/tox-voice/core.d.ts +0 -1
  71. package/dist/prompts/target-assessment/tox-voice/core.js +0 -74
  72. package/dist/prompts/target-assessment/tox-voice/critique.d.ts +0 -7
  73. package/dist/prompts/target-assessment/tox-voice/critique.js +0 -109
  74. package/dist/prompts/target-assessment/tox-voice/exemplars.d.ts +0 -8
  75. package/dist/prompts/target-assessment/tox-voice/exemplars.js +0 -24
  76. package/dist/prompts/target-assessment/tox-voice/index.d.ts +0 -4
  77. package/dist/prompts/target-assessment/tox-voice/index.js +0 -4
  78. package/dist/prompts/target-assessment/tox-voice/vocabulary.d.ts +0 -18
  79. package/dist/prompts/target-assessment/tox-voice/vocabulary.js +0 -61
  80. package/dist/state/target-assessments.d.ts +0 -68
  81. package/dist/state/target-assessments.js +0 -222
  82. package/dist/workflows/execute-target-assessment.d.ts +0 -118
  83. package/dist/workflows/execute-target-assessment.js +0 -631
  84. package/dist/workflows/target-assessment/assemblers/index.d.ts +0 -5
  85. package/dist/workflows/target-assessment/assemblers/index.js +0 -4
  86. package/dist/workflows/target-assessment/assemblers/literature.d.ts +0 -281
  87. package/dist/workflows/target-assessment/assemblers/literature.js +0 -971
  88. package/dist/workflows/target-assessment/assemblers/orchestrator.d.ts +0 -118
  89. package/dist/workflows/target-assessment/assemblers/orchestrator.js +0 -945
  90. package/dist/workflows/target-assessment/assemblers/safety.d.ts +0 -374
  91. package/dist/workflows/target-assessment/assemblers/safety.js +0 -1068
  92. package/dist/workflows/target-assessment/assemblers/trials.d.ts +0 -30
  93. package/dist/workflows/target-assessment/assemblers/trials.js +0 -75
  94. package/dist/workflows/target-assessment/collectors/index.d.ts +0 -114
  95. package/dist/workflows/target-assessment/collectors/index.js +0 -538
  96. package/dist/workflows/target-assessment/coverage.d.ts +0 -97
  97. package/dist/workflows/target-assessment/coverage.js +0 -86
  98. package/dist/workflows/target-assessment/decisions/index.d.ts +0 -86
  99. package/dist/workflows/target-assessment/decisions/index.js +0 -127
  100. package/dist/workflows/target-assessment/fanout/index.d.ts +0 -32
  101. package/dist/workflows/target-assessment/fanout/index.js +0 -156
  102. package/dist/workflows/target-assessment/investigation/index.d.ts +0 -97
  103. package/dist/workflows/target-assessment/investigation/index.js +0 -419
  104. package/dist/workflows/target-assessment/lib/approval-precedents.d.ts +0 -73
  105. package/dist/workflows/target-assessment/lib/approval-precedents.js +0 -185
  106. package/dist/workflows/target-assessment/lib/citation-validator.d.ts +0 -46
  107. package/dist/workflows/target-assessment/lib/citation-validator.js +0 -316
  108. package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +0 -65
  109. package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.js +0 -174
  110. package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.d.ts +0 -44
  111. package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.js +0 -152
  112. package/dist/workflows/target-assessment/lib/compute-derived.d.ts +0 -20
  113. package/dist/workflows/target-assessment/lib/compute-derived.js +0 -261
  114. package/dist/workflows/target-assessment/lib/compute-selectivity.d.ts +0 -15
  115. package/dist/workflows/target-assessment/lib/compute-selectivity.js +0 -18
  116. package/dist/workflows/target-assessment/lib/dedup-modulators.d.ts +0 -37
  117. package/dist/workflows/target-assessment/lib/dedup-modulators.js +0 -157
  118. package/dist/workflows/target-assessment/lib/evidence-conflict-detector.d.ts +0 -17
  119. package/dist/workflows/target-assessment/lib/evidence-conflict-detector.js +0 -100
  120. package/dist/workflows/target-assessment/lib/expression-constants.d.ts +0 -21
  121. package/dist/workflows/target-assessment/lib/expression-constants.js +0 -21
  122. package/dist/workflows/target-assessment/lib/family-complex-supplement.d.ts +0 -20
  123. package/dist/workflows/target-assessment/lib/family-complex-supplement.js +0 -36
  124. package/dist/workflows/target-assessment/lib/fda-label-safety.d.ts +0 -72
  125. package/dist/workflows/target-assessment/lib/fda-label-safety.js +0 -166
  126. package/dist/workflows/target-assessment/lib/heterodimer-filter.d.ts +0 -30
  127. package/dist/workflows/target-assessment/lib/heterodimer-filter.js +0 -44
  128. package/dist/workflows/target-assessment/lib/hpo-organ-map.d.ts +0 -34
  129. package/dist/workflows/target-assessment/lib/hpo-organ-map.js +0 -63
  130. package/dist/workflows/target-assessment/lib/impc-organ-map.d.ts +0 -19
  131. package/dist/workflows/target-assessment/lib/impc-organ-map.js +0 -39
  132. package/dist/workflows/target-assessment/lib/intended-polypharm-filter.d.ts +0 -4
  133. package/dist/workflows/target-assessment/lib/intended-polypharm-filter.js +0 -24
  134. package/dist/workflows/target-assessment/lib/llm-step.d.ts +0 -80
  135. package/dist/workflows/target-assessment/lib/llm-step.js +0 -101
  136. package/dist/workflows/target-assessment/lib/meddra-organ-map.d.ts +0 -40
  137. package/dist/workflows/target-assessment/lib/meddra-organ-map.js +0 -138
  138. package/dist/workflows/target-assessment/lib/recommendation-quality-gates.d.ts +0 -17
  139. package/dist/workflows/target-assessment/lib/recommendation-quality-gates.js +0 -99
  140. package/dist/workflows/target-assessment/lib/recommendation-sanitizer.d.ts +0 -2
  141. package/dist/workflows/target-assessment/lib/recommendation-sanitizer.js +0 -51
  142. package/dist/workflows/target-assessment/lib/regulatory-actions.d.ts +0 -25
  143. package/dist/workflows/target-assessment/lib/regulatory-actions.js +0 -176
  144. package/dist/workflows/target-assessment/lib/safety-corroboration.d.ts +0 -44
  145. package/dist/workflows/target-assessment/lib/safety-corroboration.js +0 -273
  146. package/dist/workflows/target-assessment/lib/structured-llm.d.ts +0 -61
  147. package/dist/workflows/target-assessment/lib/structured-llm.js +0 -102
  148. package/dist/workflows/target-assessment/lib/target-identity-filter.d.ts +0 -59
  149. package/dist/workflows/target-assessment/lib/target-identity-filter.js +0 -95
  150. package/dist/workflows/target-assessment/lib/therapeutic-programs.d.ts +0 -20
  151. package/dist/workflows/target-assessment/lib/therapeutic-programs.js +0 -68
  152. package/dist/workflows/target-assessment/metrics.d.ts +0 -16
  153. package/dist/workflows/target-assessment/metrics.js +0 -30
  154. package/dist/workflows/target-assessment/phase4-assemble.d.ts +0 -3182
  155. package/dist/workflows/target-assessment/phase4-assemble.js +0 -39
  156. package/dist/workflows/target-assessment/phase5-persist.d.ts +0 -59
  157. package/dist/workflows/target-assessment/phase5-persist.js +0 -349
  158. package/dist/workflows/target-assessment/progress-stream.d.ts +0 -75
  159. package/dist/workflows/target-assessment/progress-stream.js +0 -110
  160. package/dist/workflows/target-assessment/progress.d.ts +0 -39
  161. package/dist/workflows/target-assessment/progress.js +0 -77
  162. package/dist/workflows/target-assessment/schemas.d.ts +0 -970
  163. package/dist/workflows/target-assessment/schemas.js +0 -318
  164. package/dist/workflows/target-assessment/steps/collectors/pathways.d.ts +0 -15
  165. package/dist/workflows/target-assessment/steps/collectors/pathways.js +0 -58
  166. package/dist/workflows/target-assessment/steps/fanout/aes-for-one-class-drug-step.d.ts +0 -34
  167. package/dist/workflows/target-assessment/steps/fanout/aes-for-one-class-drug-step.js +0 -31
  168. package/dist/workflows/target-assessment/steps/fanout/aes-for-one-trial-step.d.ts +0 -57
  169. package/dist/workflows/target-assessment/steps/fanout/aes-for-one-trial-step.js +0 -54
  170. package/dist/workflows/target-assessment/steps/fanout/faers-for-one-modulator-step.d.ts +0 -35
  171. package/dist/workflows/target-assessment/steps/fanout/faers-for-one-modulator-step.js +0 -32
  172. package/dist/workflows/target-assessment/steps/fanout/index.d.ts +0 -14
  173. package/dist/workflows/target-assessment/steps/fanout/index.js +0 -10
  174. package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.d.ts +0 -29
  175. package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.js +0 -29
  176. package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +0 -657
  177. package/dist/workflows/target-assessment/steps/phase2-aggregate.js +0 -34
  178. package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +0 -806
  179. package/dist/workflows/target-assessment/steps/phase3-aggregate.js +0 -16
  180. package/dist/workflows/target-assessment/steps/synthesis/diagnostics.d.ts +0 -28
  181. package/dist/workflows/target-assessment/steps/synthesis/diagnostics.js +0 -27
  182. package/dist/workflows/target-assessment/sub-workflows/index.d.ts +0 -15
  183. package/dist/workflows/target-assessment/sub-workflows/index.js +0 -11
  184. package/dist/workflows/target-assessment/sub-workflows/per-class-drug-aes.d.ts +0 -41
  185. package/dist/workflows/target-assessment/sub-workflows/per-class-drug-aes.js +0 -12
  186. package/dist/workflows/target-assessment/sub-workflows/per-modulator-faers.d.ts +0 -41
  187. package/dist/workflows/target-assessment/sub-workflows/per-modulator-faers.js +0 -12
  188. package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.d.ts +0 -37
  189. package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.js +0 -12
  190. package/dist/workflows/target-assessment/sub-workflows/per-trial-aes.d.ts +0 -66
  191. package/dist/workflows/target-assessment/sub-workflows/per-trial-aes.js +0 -12
  192. package/dist/workflows/target-assessment/synthesis/index.d.ts +0 -607
  193. package/dist/workflows/target-assessment/synthesis/index.js +0 -354
  194. package/dist/workflows/target-assessment/synthesis-probes.d.ts +0 -56
  195. package/dist/workflows/target-assessment/synthesis-probes.js +0 -320
@@ -1,631 +0,0 @@
1
- /**
2
- * `executeTargetAssessment` — the parent DBOS workflow.
3
- *
4
- * Shape (every external interaction is a `DBOS.runStep` so a crashed
5
- * workflow replays from the cache instead of repeating side effects):
6
- *
7
- * Phase 0 `phase0Resolve` — only step allowed to throw
8
- * (terminates → `target-unresolved`)
9
- * Phase 1 15 collectors in parallel — coverage-envelope failures
10
- * Progress `emitProgress("deciding")`
11
- * Phase 2 2 decisions in parallel — LLM calls, attempt-numbered
12
- * Progress `emitProgress("fanning_out")`
13
- * Phase 3 4 fan-out blocks in parallel — per-item HTTP, withHost semaphore
14
- * Progress `emitProgress("assembling")`
15
- * Phase 4 `phase4Assemble` — aggregation + cached annotator
16
- * Phase 4a `ta-approval-precedents` — one openFDA lookup, two projections
17
- * Phase 4b `assembleSafetyCorroboration` — pure fold, no step of its own
18
- * Phase 4c `investigateClaims` — propose / critique / re-verify / converge
19
- * Progress `emitProgress("synthesizing")`
20
- * Phase 5a 3 per-section syntheses in parallel
21
- * Phase 5b `dossierRecommendation` sequential
22
- * Phase 5c `phase5Persist` (pure)
23
- * Terminal outermost try/finally:
24
- * - normal: setDossier → status="completed"
25
- * - phase0 throw: markFailed → "target-unresolved"
26
- * - schema violation: markFailed → "schema-violation"
27
- * - other throw: markFailed → "unexpected-throw"
28
- * - 402 self-cancel: markAssessmentSuspended → "suspended_insufficient_funds"
29
- * - operator cancel: markFailed → "operator-cancelled"
30
- * - soft-deleted row: no-op
31
- *
32
- * Workflow ID convention: `workflowID === assessmentId`. TA is one-shot
33
- * (re-runs create new rows), so the workflowID equals the assessmentId
34
- * directly. No `:runId` suffix.
35
- */
36
- import { DBOS } from "@dbos-inc/dbos-sdk";
37
- import { z } from "zod";
38
- import { forSubAgent } from "../auth/types.js";
39
- import { createNoopLogger } from "../lib/console-logger.js";
40
- import { ATTR_INFLEXA_FANOUT_KEY, stableSpan } from "../lib/otel-spans.js";
41
- import { unwrapOrThrow } from "../lib/result.js";
42
- import { durableStep } from "../loop/run-step.js";
43
- import { createPubMedTool } from "../tools/bio/pubmed.js";
44
- import { resolveTarget } from "../tools/lib/identifier-resolver.js";
45
- import { TargetAssessmentInputSchema } from "./target-assessment/schemas.js";
46
- import { getAssessment, markAssessmentSuspended, markFailed, setDossier } from "../state/target-assessments.js";
47
- import { COLLECTOR_MANIFEST } from "./target-assessment/collectors/index.js";
48
- import { drugsInClass, modulatorTriage } from "./target-assessment/decisions/index.js";
49
- import { aesForOneClassDrug, aesForOneTrial, faersForOneModulator, polypharmForOneModulator, } from "./target-assessment/fanout/index.js";
50
- import { fetchApprovalPrecedents, pickIndicationForPrecedents, precedentLabelSafety, renderApprovalPrecedents, } from "./target-assessment/lib/approval-precedents.js";
51
- import { segmentLabelSafety } from "./target-assessment/lib/fda-label-safety.js";
52
- import { readBudgetExceededMarker } from "./target-assessment/lib/llm-step.js";
53
- import { assembleSafetyCorroboration } from "./target-assessment/lib/safety-corroboration.js";
54
- import { investigateClaims, isClaimInvestigationBudgetExceeded } from "./target-assessment/investigation/index.js";
55
- import { recordTerminalReason } from "./target-assessment/metrics.js";
56
- import { elapsedSinceIso, recordRunCompleted } from "../lib/metrics.js";
57
- import { phase4Assemble } from "./target-assessment/phase4-assemble.js";
58
- import { DossierDerivedInvariantError, DossierSchemaViolationError, phase5Persist } from "./target-assessment/phase5-persist.js";
59
- import { emitProgress } from "./target-assessment/progress.js";
60
- import { dossierRecommendation, liabilityBullets, safetyFlagsTrail, translationalCommentary, } from "./target-assessment/synthesis/index.js";
61
- // ── Workflow input ───────────────────────────────────────────────────
62
- export const ExecuteTargetAssessmentInputSchema = z.object({
63
- assessmentId: z.string(),
64
- target: z.string(),
65
- goal: z.string().nullable().optional(),
66
- organizationId: z.string(),
67
- requestedBy: z.string(),
68
- billingContextId: z.string(),
69
- });
70
- // ── Helpers ──────────────────────────────────────────────────────────
71
- /**
72
- * Derive a sub-agent session from the durable `RunSession`. Identity, scope,
73
- * and the opaque `auth` capability come from the parent session — no synthetic
74
- * envelopes.
75
- */
76
- function buildSession(input, agentId) {
77
- return {
78
- ...input.runSession,
79
- provenance: { agentId, callPath: [agentId] },
80
- };
81
- }
82
- /**
83
- * Type guard: any phase result carrying `{kind: "budget-exceeded"}` means
84
- * the LLM step wrapper has already self-cancelled the workflow. The body
85
- * should propagate the sentinel up the call chain so no downstream
86
- * coverage envelope is fabricated for it.
87
- */
88
- function isBudgetCancel(value) {
89
- return typeof value === "object" && value !== null && "kind" in value && value.kind === "budget-exceeded";
90
- }
91
- function wrapCollectorThrow(err) {
92
- return {
93
- coverage: "queried_no_data",
94
- error: { message: err instanceof Error ? err.message : String(err) },
95
- };
96
- }
97
- /**
98
- * Internal sentinel thrown by the workflow body when a 402 marker has
99
- * been observed at a phase boundary — short-circuits the remaining
100
- * phases and lands in the outermost catch, which sets `budgetExceeded`
101
- * for the terminal dispatch.
102
- */
103
- class BudgetExceededSkip extends Error {
104
- constructor() {
105
- super("budget exceeded — skipping remaining phases");
106
- this.name = "BudgetExceededSkip";
107
- }
108
- }
109
- // ── Workflow registration ────────────────────────────────────────────
110
- /**
111
- * Register the executeTargetAssessment workflow with DBOS. Caller is
112
- * responsible for setting `workflowID = assessmentId` via
113
- * `DBOS.startWorkflow(_, { workflowID })`.
114
- */
115
- export function registerExecuteTargetAssessment(deps) {
116
- return DBOS.registerWorkflow(async (input) => {
117
- return runExecuteTargetAssessmentBody(input, deps);
118
- }, { name: "executeTargetAssessment" });
119
- }
120
- /**
121
- * Body extracted so tests can drive it without registering a workflow
122
- * (the DBOS calls inside still rely on a workflow context being present).
123
- */
124
- export async function runExecuteTargetAssessmentBody(input, deps) {
125
- const logger = (deps.logger ?? createNoopLogger()).with({ assessmentId: input.assessmentId });
126
- // (§6.1) Outermost try/finally — every termination path dispatches the
127
- // matching terminal handler. Phase 0 throws are caught here; every
128
- // other phase wraps its body in coverage envelopes and does not throw.
129
- // Budget-exceeded (billing-gateway 402) reaches here via `budgetExceeded` set
130
- // when any LLM step returns the sentinel. The terminal block writes
131
- // `status = "suspended_insufficient_funds"` via DBOS.runStep BEFORE
132
- // materialising the cancel — the trailing `DBOS.cancelWorkflow` +
133
- // `DBOS.runStep("self-cancel-budget-exceeded")` puts the workflow into
134
- // CANCELLED status (the only state `DBOS.resumeWorkflow` replays).
135
- // Ownership defaults to true for inputs persisted before the field existed
136
- // (a workflow recovered across this deploy) — those were always Cortex-owned.
137
- const authorization = {
138
- runSession: input.runSession,
139
- ownsMandate: input.ownsMandate ?? true, // oss-core-managed-ok
140
- };
141
- let phase0Error;
142
- let schemaViolation;
143
- let derivedViolation;
144
- let unexpectedError;
145
- let budgetExceeded = false;
146
- let dossierForPersist;
147
- let bytesForPersist = 0;
148
- try {
149
- // (§5.3, §6.3) Phase 0 — target resolution. The only legitimate throw
150
- // site. On throw, the catch below records `phase0Error` and the
151
- // terminal block dispatches `markFailed({kind: "target-unresolved"})`.
152
- await emitProgress(deps.pool, logger, input.assessmentId, "resolving");
153
- const resolved = await DBOS.runStep(async () => {
154
- const r = await resolveTarget(input.target, logger);
155
- return {
156
- assessmentId: input.assessmentId,
157
- goal: input.goal ?? null,
158
- ...r,
159
- };
160
- }, { name: "ta-phase0-resolve" }).catch((err) => {
161
- phase0Error = err;
162
- throw err;
163
- });
164
- // (§5.4) Phase 1 — 15 collectors in parallel. Each is its own DBOS
165
- // step; coverage failures cache as `queried_no_data` and replay
166
- // returns the cached envelope. The per-collector run functions emit
167
- // different bundle shapes; the union is widened to `unknown` here
168
- // and `Phase1Bundle` is reconstructed by manifest key below.
169
- await emitProgress(deps.pool, logger, input.assessmentId, "collecting");
170
- const manifest = COLLECTOR_MANIFEST;
171
- const collectorCtx = { ncbiApiKey: deps.ncbiApiKey, logger };
172
- const collectorPairs = await Promise.all(manifest.map((entry) => DBOS.runStep(() => entry.run(resolved, collectorCtx), {
173
- name: `ta-collector:${entry.id}`,
174
- })
175
- .catch((err) => wrapCollectorThrow(err))
176
- .then((result) => [entry.id, result])));
177
- const collectorById = Object.fromEntries(collectorPairs);
178
- const phase1 = {
179
- resolved,
180
- collectors: {
181
- opentargets: collectorById["opentargets"],
182
- chemblModulators: collectorById["chembl-modulators"],
183
- ctgov: collectorById["ctgov"],
184
- faersByTarget: collectorById["faers-by-target"],
185
- expressionHuman: collectorById["expression-human"],
186
- expressionMultiSpecies: collectorById["expression-multi-species"],
187
- clinvar: collectorById["clinvar"],
188
- cbioportal: collectorById["cbioportal"],
189
- impc: collectorById["impc"],
190
- monarch: collectorById["monarch"],
191
- pubmedIndex: collectorById["pubmed-index"],
192
- pathways: collectorById["pathways"],
193
- stringPpi: collectorById["string-ppi"],
194
- familyComplexes: collectorById["family-complexes"],
195
- therapeuticPrograms: collectorById["therapeutic-programs"],
196
- },
197
- };
198
- // (§5.5, §5.11) Phase 2 — decisions. Lazy billing resolution happens
199
- // inside the chat provider's closure (TA-aware provider in §12). The
200
- // session carries the `billingContextId` the provider reads from.
201
- await emitProgress(deps.pool, logger, input.assessmentId, "deciding");
202
- const decisionSession = buildSession(input, "ta-decisions");
203
- const [modulatorRes, drugsRes] = await Promise.all([
204
- modulatorTriage(phase1, {
205
- chatProvider: deps.chatProvider,
206
- session: forSubAgent(decisionSession, "modulator-triage"),
207
- model: deps.decisionModel,
208
- attempt: 0,
209
- }),
210
- drugsInClass(phase1, {
211
- chatProvider: deps.chatProvider,
212
- session: forSubAgent(decisionSession, "drugs-in-class"),
213
- model: deps.decisionModel,
214
- attempt: 0,
215
- }),
216
- ]);
217
- if (isBudgetCancel(modulatorRes) || isBudgetCancel(drugsRes)) {
218
- // An LLM step already sent the budget-exceeded marker. Skip the
219
- // remaining phases and fall through to the terminal block, which
220
- // dispatches `markAssessmentSuspended` and materialises the cancel.
221
- budgetExceeded = true;
222
- throw new BudgetExceededSkip();
223
- }
224
- const phase2 = {
225
- phase1,
226
- decisions: {
227
- modulatorTriage: modulatorRes,
228
- failedTrialClassifier: {
229
- coverage: "not_loaded",
230
- reason: "deterministic in Phase 4 (classifyFailureReason)",
231
- },
232
- offTargetCurator: {
233
- coverage: "not_loaded",
234
- reason: "deterministic in Phase 4 (aggregateOffTargetPanel)",
235
- },
236
- drugsInClass: drugsRes,
237
- },
238
- };
239
- // (§5.6) Phase 3 — fan-out blocks. Each block iterates items via
240
- // Promise.all over DBOS.runStep; `withHost` semaphores inside each
241
- // item function cap concurrency to 4 per host.
242
- await emitProgress(deps.pool, logger, input.assessmentId, "fanning_out");
243
- const triage = phase2.decisions.modulatorTriage;
244
- const triageShortlist = triage.coverage === "available"
245
- ? triage.data.shortlist.map((m) => ({
246
- moleculeChemblId: m.moleculeChemblId,
247
- preferredName: m.preferredName,
248
- maxPhase: m.maxPhase,
249
- firstApproval: m.firstApproval,
250
- rationale: m.rationale ?? "",
251
- }))
252
- : [];
253
- const chembl = phase1.collectors.chemblModulators;
254
- const primaryTargetChemblId = chembl.coverage === "available" ? chembl.data.targetChemblId : null;
255
- const polypharmItems = triageShortlist.map((m) => ({
256
- moleculeChemblId: m.moleculeChemblId,
257
- preferredName: m.preferredName,
258
- primaryTargetChemblId,
259
- }));
260
- const ctgov = phase1.collectors.ctgov;
261
- const trialItems = [];
262
- if (ctgov.coverage === "available") {
263
- const seen = new Set();
264
- for (const t of [...ctgov.data.failed, ...ctgov.data.active]) {
265
- if (!t.nctId || seen.has(t.nctId))
266
- continue;
267
- seen.add(t.nctId);
268
- trialItems.push({ nctId: t.nctId, title: t.title });
269
- if (trialItems.length >= 30)
270
- break;
271
- }
272
- }
273
- const dic = phase2.decisions.drugsInClass;
274
- const classDrugItems = dic.coverage === "available"
275
- ? dic.data.drugs.map((d) => ({
276
- moleculeChemblId: d.moleculeChemblId,
277
- preferredName: d.preferredName,
278
- maxPhase: d.maxPhase,
279
- firstApproval: d.firstApproval,
280
- }))
281
- : [];
282
- async function runFanoutBlock(items, stepKindForName, keyOf, fn) {
283
- return Promise.all(items.map((item) => {
284
- const key = keyOf(item);
285
- const stepName = `ta-fanout:${stepKindForName}:${key}`;
286
- return DBOS.runStep(() => {
287
- stableSpan(stepName, `ta-fanout:${stepKindForName}`, { [ATTR_INFLEXA_FANOUT_KEY]: key });
288
- return fn(item);
289
- }, { name: stepName }).catch((err) => wrapCollectorThrow(err));
290
- }));
291
- }
292
- const [perModulatorFaersResults, perTrialAesResults, perModulatorPolypharmResults, perClassDrugAesResults] = await Promise.all([
293
- runFanoutBlock(triageShortlist, "modulator-faers", (item) => item.moleculeChemblId, faersForOneModulator),
294
- runFanoutBlock(trialItems, "trial-aes", (item) => item.nctId, aesForOneTrial),
295
- runFanoutBlock(polypharmItems, "modulator-polypharm", (item) => item.moleculeChemblId, (item) => polypharmForOneModulator(item, logger)),
296
- runFanoutBlock(classDrugItems, "class-drug-aes", (item) => item.moleculeChemblId, aesForOneClassDrug),
297
- ]);
298
- const phase3 = {
299
- phase2,
300
- fanout: {
301
- perModulatorFaers: { results: perModulatorFaersResults },
302
- perTrialAes: { results: perTrialAesResults },
303
- perModulatorPolypharm: { results: perModulatorPolypharmResults },
304
- perClassDrugAes: { results: perClassDrugAesResults },
305
- },
306
- };
307
- // (§5.7) Phase 4 — assembly. Single DBOS step. The annotator seam lets
308
- // the off-target panel's clinical-consequence pass reach its LLM on a
309
- // cache miss; per-row failures leave the row's fallback in place.
310
- await emitProgress(deps.pool, logger, input.assessmentId, "assembling");
311
- const annotatorDeps = {
312
- provider: deps.chatProvider,
313
- session: buildSession(input, "off-target-annotator"),
314
- model: deps.decisionModel,
315
- ...(deps.usageRecorder ? { usageRecorder: deps.usageRecorder } : {}),
316
- };
317
- const phase4 = await DBOS.runStep(() => phase4Assemble(deps.pool, phase3, annotatorDeps, logger), {
318
- name: "ta-phase4-assemble",
319
- });
320
- // (§5.8-pre) Approval-precedent grounding — one deterministic openFDA
321
- // lookup feeding two projections of the same typed labels: the markdown
322
- // block every synthesis prompt receives, and the per-organ regulatory
323
- // signals the dossier carries. A precedent-lookup failure must NOT fail
324
- // the whole assessment: a fetch throw degrades to a "no precedents" block.
325
- const precedentGrounding = await DBOS.runStep(async () => {
326
- const indication = pickIndicationForPrecedents(phase4.dossier);
327
- let result = null;
328
- if (indication !== null) {
329
- try {
330
- result = await fetchApprovalPrecedents({ indication });
331
- }
332
- catch (err) {
333
- logger.named("ta-approval-precedents").warn("openFDA lookup failed", { indication, ...logger.errorFields(err) });
334
- result = null;
335
- }
336
- }
337
- return {
338
- block: renderApprovalPrecedents(indication, result),
339
- // A resolved indication was queried even when the lookup
340
- // degraded to nothing; only an unresolved one was not.
341
- organSignals: indication === null ? null : segmentLabelSafety(precedentLabelSafety(result?.precedents ?? [])),
342
- };
343
- }, { name: "ta-approval-precedents" });
344
- const approvalPrecedents = precedentGrounding.block;
345
- // (§5.8-pre) The corroboration fold. It reads the label signals segmented
346
- // above, so it runs here rather than at assembly — and here rather than at
347
- // persist, because the claim investigation below is its consumer. The fold
348
- // is pure over two cached step outputs, so it needs no step of its own: a
349
- // replay recomputes the same record without a durable write.
350
- const corroboration = assembleSafetyCorroboration({
351
- phase1,
352
- regulatoryOrganSignals: precedentGrounding.organSignals,
353
- });
354
- // (§5.8-pre) The claim investigation — propose a mechanism, argue
355
- // against it, re-verify, converge. Runs before synthesis so the
356
- // synthesis prompts read verdicts rather than bare agreement.
357
- const investigation = await investigateClaims({ corroboration, dossier: phase4.dossier }, {
358
- logger,
359
- chatProvider: deps.chatProvider,
360
- session: buildSession(input, "ta-claim-investigation"),
361
- model: deps.synthesisModel,
362
- attempt: 0,
363
- runStep: durableStep,
364
- critiqueTools: [createPubMedTool({ ...(deps.ncbiApiKey ? { ncbiApiKey: deps.ncbiApiKey } : {}) })],
365
- ...(deps.claimInvestigation ? { config: deps.claimInvestigation } : {}),
366
- ...(deps.usageRecorder ? { usageRecorder: deps.usageRecorder } : {}),
367
- });
368
- if (isClaimInvestigationBudgetExceeded(investigation)) {
369
- budgetExceeded = true;
370
- throw new BudgetExceededSkip();
371
- }
372
- const phase4Investigated = {
373
- ...phase4,
374
- dossier: {
375
- ...phase4.dossier,
376
- safety_corroboration: corroboration,
377
- claim_investigation: investigation,
378
- },
379
- };
380
- // (§5.8) Phase 5 — three per-section syntheses in parallel.
381
- await emitProgress(deps.pool, logger, input.assessmentId, "synthesizing");
382
- const synthesisDeps = (agentId) => ({
383
- chatProvider: deps.chatProvider,
384
- session: { ...buildSession(input, agentId) },
385
- model: deps.synthesisModel,
386
- attempt: 0,
387
- approvalPrecedents,
388
- });
389
- const [bulletsRes, flagsRes, commentaryRes] = await Promise.all([
390
- liabilityBullets(phase4Investigated, synthesisDeps("liability-bullets")),
391
- safetyFlagsTrail(phase4Investigated, synthesisDeps("safety-flags-trail")),
392
- translationalCommentary(phase4Investigated, synthesisDeps("translational-commentary")),
393
- ]);
394
- if (isBudgetCancel(bulletsRes) || isBudgetCancel(flagsRes) || isBudgetCancel(commentaryRes)) {
395
- budgetExceeded = true;
396
- throw new BudgetExceededSkip();
397
- }
398
- // (§5.9) Phase 5b — sequential dossier-recommendation step.
399
- const recommendationRes = await dossierRecommendation({
400
- phase4: phase4Investigated,
401
- perSection: {
402
- liabilityBullets: bulletsRes,
403
- safetyFlagsTrail: flagsRes,
404
- translationalCommentary: commentaryRes,
405
- },
406
- }, synthesisDeps("dossier-recommendation"));
407
- if (isBudgetCancel(recommendationRes)) {
408
- budgetExceeded = true;
409
- throw new BudgetExceededSkip();
410
- }
411
- // (§5.10) Phase 5c — pure persist function. The DB write moves to
412
- // the terminal handler below; this step validates the v5 dossier
413
- // and returns the persisted-shape bundle. Schema/derived violations
414
- // throw and the terminal handler dispatches to `markFailed`.
415
- const persisted = await DBOS.runStep(() => phase5Persist({
416
- logger,
417
- assessmentId: input.assessmentId,
418
- phase4Dossier: phase4Investigated.dossier,
419
- phase2,
420
- regulatoryOrganSignals: precedentGrounding.organSignals,
421
- synthesis: {
422
- bullets: bulletsRes,
423
- flags: flagsRes,
424
- commentary: commentaryRes,
425
- recommendation: recommendationRes,
426
- },
427
- }), { name: "ta-phase5-persist" });
428
- dossierForPersist = persisted.dossier;
429
- bytesForPersist = persisted.bytes;
430
- }
431
- catch (err) {
432
- if (err instanceof BudgetExceededSkip) {
433
- // Already classified; the terminal block reads `budgetExceeded`.
434
- }
435
- else if (err instanceof DossierSchemaViolationError) {
436
- schemaViolation = err;
437
- }
438
- else if (err instanceof DossierDerivedInvariantError) {
439
- derivedViolation = err;
440
- }
441
- else if (phase0Error === undefined) {
442
- unexpectedError = err;
443
- }
444
- // phase0Error is already set if it came from the Phase-0 catch above.
445
- }
446
- // (§6.1, §6.8) Terminal block. Read the row first; if soft-deleted,
447
- // no-op (the user's delete during the run wins).
448
- //
449
- // Each terminal path records its `cortex.run.completed` outcome INSIDE the
450
- // DBOS step that persists it, so a recovery replay reads the cached step
451
- // and records nothing again. The duration runs from the row's `created_at`.
452
- // The deleted path writes no row, so its record rides the read step itself.
453
- const row = await DBOS.runStep(async () => {
454
- const read = unwrapOrThrow(await getAssessment(deps.pool, input.assessmentId, input.organizationId));
455
- if (read?.status === "deleted") {
456
- recordRunCompleted({ workflow: "target_assessment", status: "canceled", durationMs: elapsedSinceIso(read.createdAt) });
457
- }
458
- return read;
459
- }, { name: "ta-terminal-read-row" });
460
- const recordTerminal = (status) => recordRunCompleted({ workflow: "target_assessment", status, durationMs: elapsedSinceIso(row?.createdAt) });
461
- if (row?.status === "deleted") {
462
- recordTerminalReason("deleted");
463
- return {
464
- assessmentId: input.assessmentId,
465
- status: "deleted",
466
- bytes: 0,
467
- };
468
- }
469
- // (§6.2) Normal completion — happy path wrote `dossierForPersist`.
470
- if (!phase0Error && !schemaViolation && !derivedViolation && !unexpectedError && !budgetExceeded && dossierForPersist) {
471
- try {
472
- await DBOS.runStep(async () => {
473
- unwrapOrThrow(await setDossier(deps.pool, input.assessmentId, dossierForPersist));
474
- recordTerminal("completed");
475
- }, { name: "ta-terminal-completed" });
476
- await emitProgress(deps.pool, logger, input.assessmentId, "completed");
477
- }
478
- catch (err) {
479
- logger.named("ta-terminal").error("setDossier failed", logger.errorFields(err));
480
- }
481
- await revokeRunAuthorizationSafe(deps, authorization, "target-assessment-completed");
482
- recordTerminalReason("completed");
483
- return {
484
- assessmentId: input.assessmentId,
485
- status: "completed",
486
- bytes: bytesForPersist,
487
- };
488
- }
489
- // (§6.3) Phase 0 throw — target unresolved.
490
- if (phase0Error) {
491
- await DBOS.runStep(async () => {
492
- unwrapOrThrow(await markFailed(deps.pool, input.assessmentId, {
493
- kind: "target-unresolved",
494
- message: phase0Error instanceof Error ? phase0Error.message : String(phase0Error),
495
- }));
496
- recordTerminal("failed");
497
- }, { name: "ta-terminal-failed-resolve" });
498
- await emitProgress(deps.pool, logger, input.assessmentId, "failed");
499
- await revokeRunAuthorizationSafe(deps, authorization, "target-assessment-failed");
500
- recordTerminalReason("target-unresolved");
501
- return {
502
- assessmentId: input.assessmentId,
503
- status: "failed",
504
- bytes: 0,
505
- };
506
- }
507
- // (§6.4) Schema violation.
508
- if (schemaViolation) {
509
- await DBOS.runStep(async () => {
510
- unwrapOrThrow(await markFailed(deps.pool, input.assessmentId, {
511
- kind: "schema-invariant-violation",
512
- message: schemaViolation.message,
513
- details: schemaViolation.issues,
514
- }));
515
- recordTerminal("failed");
516
- }, { name: "ta-terminal-failed-schema" });
517
- await emitProgress(deps.pool, logger, input.assessmentId, "failed");
518
- await revokeRunAuthorizationSafe(deps, authorization, "target-assessment-failed");
519
- recordTerminalReason("schema-violation");
520
- return {
521
- assessmentId: input.assessmentId,
522
- status: "failed",
523
- bytes: bytesForPersist,
524
- };
525
- }
526
- // (§6.4 — derived invariant variant — same terminal class, different kind.)
527
- if (derivedViolation) {
528
- await DBOS.runStep(async () => {
529
- unwrapOrThrow(await markFailed(deps.pool, input.assessmentId, {
530
- kind: "derived-invariant-violation",
531
- message: derivedViolation.message,
532
- }));
533
- recordTerminal("failed");
534
- }, { name: "ta-terminal-failed-derived" });
535
- await emitProgress(deps.pool, logger, input.assessmentId, "failed");
536
- await revokeRunAuthorizationSafe(deps, authorization, "target-assessment-failed");
537
- recordTerminalReason("derived-invariant-violation");
538
- return {
539
- assessmentId: input.assessmentId,
540
- status: "failed",
541
- bytes: bytesForPersist,
542
- };
543
- }
544
- // (§6.6) Budget exceeded — drain the marker for telemetry, flip the
545
- // row to `suspended_insufficient_funds`, then materialise the cancel.
546
- // The trailing `DBOS.cancelWorkflow` + `DBOS.runStep` puts the
547
- // workflow into CANCELLED status (the only state `DBOS.resumeWorkflow`
548
- // will replay; SUCCESS / ERROR are terminal).
549
- if (budgetExceeded) {
550
- await DBOS.runStep(() => readBudgetExceededMarker(), {
551
- name: "ta-terminal-drain-marker",
552
- }).catch(() => null);
553
- await DBOS.runStep(async () => {
554
- unwrapOrThrow(await markAssessmentSuspended(deps.pool, input.assessmentId));
555
- // A budget pause records as `canceled`, the same status the analysis run ledger gives it.
556
- recordTerminal("canceled");
557
- }, { name: "ta-terminal-suspended" });
558
- await emitProgress(deps.pool, logger, input.assessmentId, "suspended");
559
- await revokeRunAuthorizationSafe(deps, authorization, "target-assessment-canceled");
560
- const workflowId = DBOS.workflowID;
561
- if (workflowId) {
562
- await DBOS.cancelWorkflow(workflowId);
563
- // Force one more durable hop so the cancel materialises before the
564
- // body returns — without this trailing runStep the workflow would
565
- // land SUCCESS in some DBOS versions because the body completed
566
- // without re-entering the SDK after the cancel call.
567
- await DBOS.runStep(async () => undefined, {
568
- name: "ta-self-cancel-budget-exceeded",
569
- });
570
- }
571
- recordTerminalReason("suspended-on-402");
572
- return {
573
- assessmentId: input.assessmentId,
574
- status: "suspended_insufficient_funds",
575
- bytes: 0,
576
- };
577
- }
578
- // (§6.5) Unexpected throw — bug somewhere, coverage envelope failed.
579
- if (unexpectedError) {
580
- await DBOS.runStep(async () => {
581
- unwrapOrThrow(await markFailed(deps.pool, input.assessmentId, {
582
- kind: "unexpected-throw",
583
- message: unexpectedError instanceof Error ? unexpectedError.message : String(unexpectedError),
584
- }));
585
- recordTerminal("failed");
586
- }, { name: "ta-terminal-failed-unexpected" });
587
- await emitProgress(deps.pool, logger, input.assessmentId, "failed");
588
- await revokeRunAuthorizationSafe(deps, authorization, "target-assessment-failed");
589
- recordTerminalReason("unexpected-throw");
590
- return {
591
- assessmentId: input.assessmentId,
592
- status: "failed",
593
- bytes: 0,
594
- };
595
- }
596
- // Defensive: this branch is unreachable in practice (one of the
597
- // categories above must have fired). Fall through to a generic failed
598
- // status so a buggy refactor surfaces visibly.
599
- await DBOS.runStep(async () => {
600
- unwrapOrThrow(await markFailed(deps.pool, input.assessmentId, {
601
- kind: "unexpected-throw",
602
- message: "terminal block reached without classified outcome",
603
- }));
604
- recordTerminal("failed");
605
- }, { name: "ta-terminal-failed-uncategorized" });
606
- recordTerminalReason("unexpected-throw");
607
- return {
608
- assessmentId: input.assessmentId,
609
- status: "failed",
610
- bytes: 0,
611
- };
612
- }
613
- /**
614
- * Best-effort revoke wrapped in a durableStep for replay idempotency. This
615
- * wrapper swallows any runStep-level error so a terminal path is never
616
- * aborted by a transient revoke failure.
617
- */
618
- async function revokeRunAuthorizationSafe(deps, authorization, reason) {
619
- const logger = (deps.logger ?? createNoopLogger()).named("execute-target-assessment");
620
- try {
621
- await DBOS.runStep(() => deps.runAuthorizer.revoke(authorization, reason), {
622
- name: `ta-revoke-run-auth:${reason}`,
623
- });
624
- }
625
- catch (err) {
626
- logger.warn("revokeRunAuthorization failed", { reason, ...logger.errorFields(err) });
627
- }
628
- }
629
- // Re-export the canonical TA input schema (consumers and the managed root
630
- // validate against this shape on the trigger route).
631
- export { TargetAssessmentInputSchema };
@@ -1,5 +0,0 @@
1
- export { assembleEntity, assembleTractability, enrichTractabilityModalitiesWithApprovals, type DrugForTractability, reconcileFaersCoverage, assembleDossier, } from "./orchestrator.js";
2
- export type { TrialOutcomeFilter } from "./trials.js";
3
- export { assembleLiabilitySummary, isSafetyRelevant, meetsTpmFloor, SAFETY_RELEVANT_ORGANS, REPRODUCTIVE_TOX_TISSUES, REPRODUCTIVE_TOX_TPM_FLOOR, DEFAULT_TPM_FLOOR, aggregateFaersAcrossModulators, aggregateTrialAes, aggregateOffTargetPanel, aggregateClassPrecedent, assembleRegulatoryOrganSignals, buildOrganRollup, } from "./safety.js";
4
- export { deterministicTranslationalCommentary, isClinicalMeasurement, isSelfReference } from "./literature.js";
5
- export { aggregateTrialOutcomes } from "./trials.js";
@@ -1,4 +0,0 @@
1
- export { assembleEntity, assembleTractability, enrichTractabilityModalitiesWithApprovals, reconcileFaersCoverage, assembleDossier, } from "./orchestrator.js";
2
- export { assembleLiabilitySummary, isSafetyRelevant, meetsTpmFloor, SAFETY_RELEVANT_ORGANS, REPRODUCTIVE_TOX_TISSUES, REPRODUCTIVE_TOX_TPM_FLOOR, DEFAULT_TPM_FLOOR, aggregateFaersAcrossModulators, aggregateTrialAes, aggregateOffTargetPanel, aggregateClassPrecedent, assembleRegulatoryOrganSignals, buildOrganRollup, } from "./safety.js";
3
- export { deterministicTranslationalCommentary, isClinicalMeasurement, isSelfReference } from "./literature.js";
4
- export { aggregateTrialOutcomes } from "./trials.js";