@inflexa-ai/harness 0.12.0 → 0.14.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 (207) hide show
  1. package/dist/agents/conversation-agent.d.ts +9 -15
  2. package/dist/agents/conversation-agent.js +32 -31
  3. package/dist/agents/sandbox/cheminformatics-agent.js +1 -1
  4. package/dist/agents/sandbox/chromatin-agent.js +1 -1
  5. package/dist/agents/sandbox/dna-methylation-agent.js +1 -1
  6. package/dist/agents/sandbox/drug-repurposing-agent.js +5 -10
  7. package/dist/agents/sandbox/enrichment-agent.js +1 -1
  8. package/dist/agents/sandbox/genomic-variant-agent.js +1 -11
  9. package/dist/agents/sandbox/immune-profiling-agent.js +1 -1
  10. package/dist/agents/sandbox/index.js +0 -3
  11. package/dist/agents/sandbox/multi-omics-integration-agent.js +1 -1
  12. package/dist/agents/sandbox/network-agent.js +1 -1
  13. package/dist/agents/sandbox/pkpd-clinical-response-agent.js +3 -6
  14. package/dist/agents/sandbox/scientific-executor.js +1 -1
  15. package/dist/agents/sandbox/shared.d.ts +2 -2
  16. package/dist/agents/sandbox/shared.js +8 -14
  17. package/dist/agents/sandbox/translational-safety-agent.js +4 -7
  18. package/dist/agents/sandbox/types.d.ts +5 -5
  19. package/dist/agents/sandbox/types.js +4 -4
  20. package/dist/app/chat-turn.js +4 -1
  21. package/dist/app/message-assembly.d.ts +5 -2
  22. package/dist/app/message-assembly.js +7 -2
  23. package/dist/app/run-activity.d.ts +13 -0
  24. package/dist/app/run-activity.js +51 -0
  25. package/dist/config/resource-limits.d.ts +0 -7
  26. package/dist/config/resource-limits.js +0 -1
  27. package/dist/contracts/target-dossier.d.ts +10 -10
  28. package/dist/execution/dbos-run-launcher.js +1 -26
  29. package/dist/execution/report-runner.js +1 -1
  30. package/dist/execution/run-authorizer.d.ts +1 -1
  31. package/dist/execution/run-authorizer.js +1 -1
  32. package/dist/execution/run-event-parts.d.ts +44 -0
  33. package/dist/execution/run-event-parts.js +0 -0
  34. package/dist/execution/run-event-stream.d.ts +91 -0
  35. package/dist/execution/run-event-stream.js +212 -0
  36. package/dist/execution/run-launcher.d.ts +2 -18
  37. package/dist/execution/run-launcher.js +2 -4
  38. package/dist/execution/run-synthesis.js +1 -1
  39. package/dist/index.d.ts +7 -4
  40. package/dist/index.js +23 -6
  41. package/dist/input-sanitization.d.ts +14 -4
  42. package/dist/input-sanitization.js +27 -4
  43. package/dist/lib/storage.js +4 -1
  44. package/dist/loop/budget-exceeded.d.ts +18 -2
  45. package/dist/loop/budget-exceeded.js +49 -2
  46. package/dist/loop/run-agent.d.ts +22 -1
  47. package/dist/loop/run-agent.js +51 -5
  48. package/dist/loop/run-to-terminal.d.ts +0 -2
  49. package/dist/loop/run-to-terminal.js +8 -1
  50. package/dist/memory/ai-sdk-message-storage.d.ts +37 -4
  51. package/dist/memory/ai-sdk-message-storage.js +45 -4
  52. package/dist/memory/content-to-cortex.js +19 -3
  53. package/dist/memory/message-backfill.js +1 -1
  54. package/dist/memory/reconstruct-cards.d.ts +1 -1
  55. package/dist/memory/reconstruct-cards.js +7 -5
  56. package/dist/memory/thread-history.js +72 -8
  57. package/dist/prompts/conversation.d.ts +1 -1
  58. package/dist/prompts/conversation.js +41 -15
  59. package/dist/prompts/literature-reviewer.d.ts +1 -1
  60. package/dist/prompts/literature-reviewer.js +16 -14
  61. package/dist/prompts/planner.d.ts +3 -0
  62. package/dist/prompts/planner.js +16 -21
  63. package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
  64. package/dist/prompts/sandbox/drug-repurposing-agent.js +31 -22
  65. package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
  66. package/dist/prompts/sandbox/multi-omics-integration-agent.js +9 -10
  67. package/dist/prompts/sandbox/pkpd-clinical-response-agent.d.ts +1 -1
  68. package/dist/prompts/sandbox/pkpd-clinical-response-agent.js +8 -7
  69. package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
  70. package/dist/prompts/sandbox/translational-safety-agent.js +20 -14
  71. package/dist/prompts/sandbox-standards.d.ts +4 -4
  72. package/dist/prompts/sandbox-standards.js +5 -5
  73. package/dist/prompts/synthesis-agent.d.ts +1 -1
  74. package/dist/prompts/synthesis-agent.js +7 -7
  75. package/dist/providers/errors.d.ts +7 -0
  76. package/dist/providers/errors.js +88 -3
  77. package/dist/runtime/assemble.d.ts +5 -10
  78. package/dist/runtime/assemble.js +0 -4
  79. package/dist/runtime/boot.d.ts +2 -2
  80. package/dist/runtime/boot.js +1 -1
  81. package/dist/runtime/dbos.d.ts +4 -5
  82. package/dist/runtime/dbos.js +6 -7
  83. package/dist/runtime/pools.d.ts +15 -1
  84. package/dist/runtime/pools.js +15 -1
  85. package/dist/sandbox/mount-plan.d.ts +1 -1
  86. package/dist/sandbox/sandbox-error.d.ts +1 -1
  87. package/dist/sandbox/sandbox-error.js +1 -1
  88. package/dist/sandbox/sandbox-step-translate.d.ts +15 -2
  89. package/dist/sandbox/sandbox-step-translate.js +17 -2
  90. package/dist/sandbox/types.d.ts +1 -1
  91. package/dist/schemas/validate-plan.d.ts +1 -1
  92. package/dist/schemas/workflow-state.d.ts +2 -2
  93. package/dist/schemas/workflow-state.js +2 -2
  94. package/dist/state/data-profile.d.ts +53 -1
  95. package/dist/state/data-profile.js +66 -6
  96. package/dist/state/index.d.ts +6 -6
  97. package/dist/state/index.js +4 -4
  98. package/dist/state/init.js +37 -2
  99. package/dist/state/plans.js +1 -1
  100. package/dist/state/runs.d.ts +49 -0
  101. package/dist/state/runs.js +150 -0
  102. package/dist/tasks/data-profile-activity.d.ts +87 -0
  103. package/dist/tasks/data-profile-activity.js +56 -0
  104. package/dist/tasks/data-profile.js +66 -4
  105. package/dist/tools/__fixtures__/tool-context.js +1 -0
  106. package/dist/tools/ad-hoc-router.d.ts +31 -0
  107. package/dist/tools/ad-hoc-router.js +132 -0
  108. package/dist/tools/analysis-invocation.d.ts +4 -0
  109. package/dist/tools/analysis-invocation.js +17 -0
  110. package/dist/tools/bio/chembl.d.ts +1 -1
  111. package/dist/tools/bio/chembl.js +51 -54
  112. package/dist/tools/bio/comptox.js +35 -33
  113. package/dist/tools/bio/drug-gene-interactions.d.ts +76 -0
  114. package/dist/tools/bio/drug-gene-interactions.js +175 -0
  115. package/dist/tools/bio/gene-disease-evidence.d.ts +61 -0
  116. package/dist/tools/bio/gene-disease-evidence.js +187 -0
  117. package/dist/tools/bio/gene-preclinical-profile.d.ts +58 -0
  118. package/dist/tools/bio/gene-preclinical-profile.js +125 -0
  119. package/dist/tools/bio/index.d.ts +23 -16
  120. package/dist/tools/bio/index.js +29 -22
  121. package/dist/tools/bio/keys.d.ts +16 -9
  122. package/dist/tools/bio/keys.js +22 -13
  123. package/dist/tools/bio/lookup-annotation.d.ts +48 -0
  124. package/dist/tools/bio/lookup-annotation.js +173 -0
  125. package/dist/tools/bio/opentargets.d.ts +7 -11
  126. package/dist/tools/bio/opentargets.js +24 -35
  127. package/dist/tools/bio/pubchem.js +6 -4
  128. package/dist/tools/bio/pubmed.d.ts +15 -7
  129. package/dist/tools/bio/pubmed.js +79 -20
  130. package/dist/tools/bio/search-clinical-trials.d.ts +20 -1
  131. package/dist/tools/bio/search-clinical-trials.js +26 -5
  132. package/dist/tools/bio/search-faers.js +9 -3
  133. package/dist/tools/bio/search-gene.d.ts +0 -1
  134. package/dist/tools/bio/search-gene.js +4 -5
  135. package/dist/tools/bio/search-geo-datasets.js +10 -4
  136. package/dist/tools/bio/search-interactions.d.ts +8 -4
  137. package/dist/tools/bio/search-interactions.js +28 -12
  138. package/dist/tools/bio/target-safety.d.ts +59 -0
  139. package/dist/tools/bio/target-safety.js +191 -0
  140. package/dist/tools/define-tool.d.ts +6 -2
  141. package/dist/tools/define-tool.js +1 -1
  142. package/dist/tools/execute-analysis.d.ts +44 -0
  143. package/dist/tools/execute-analysis.js +252 -0
  144. package/dist/tools/iterate-report.js +7 -6
  145. package/dist/tools/lib/dgidb-client.d.ts +49 -0
  146. package/dist/tools/lib/dgidb-client.js +168 -0
  147. package/dist/tools/lib/disgenet-client.d.ts +56 -0
  148. package/dist/tools/lib/disgenet-client.js +59 -0
  149. package/dist/tools/lib/drugbank-client.d.ts +83 -0
  150. package/dist/tools/lib/drugbank-client.js +95 -0
  151. package/dist/tools/lib/ensembl-client.d.ts +4 -1
  152. package/dist/tools/lib/ensembl-client.js +5 -3
  153. package/dist/tools/lib/gwas-catalog-client.d.ts +60 -0
  154. package/dist/tools/lib/gwas-catalog-client.js +136 -0
  155. package/dist/tools/lib/pharmgkb-client.d.ts +22 -0
  156. package/dist/tools/lib/pharmgkb-client.js +32 -0
  157. package/dist/tools/lib/pubmed-client.d.ts +57 -4
  158. package/dist/tools/lib/pubmed-client.js +57 -3
  159. package/dist/tools/lib/string-client.d.ts +25 -4
  160. package/dist/tools/lib/string-client.js +22 -5
  161. package/dist/tools/research/generate-plan.d.ts +16 -9
  162. package/dist/tools/research/generate-plan.js +116 -70
  163. package/dist/tools/research/inspect-data-profile.d.ts +31 -6
  164. package/dist/tools/research/inspect-data-profile.js +23 -7
  165. package/dist/tools/research/inspect-run.d.ts +33 -15
  166. package/dist/tools/research/inspect-run.js +155 -66
  167. package/dist/tools/research/literature-reviewer.js +7 -11
  168. package/dist/workflows/execute-analysis.d.ts +68 -5
  169. package/dist/workflows/execute-analysis.js +76 -7
  170. package/dist/workflows/sandbox-step.js +1 -11
  171. package/dist/workflows/target-assessment/decisions/index.d.ts +1 -1
  172. package/dist/workflows/target-assessment/phase4-assemble.d.ts +1 -1
  173. package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +2 -2
  174. package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +1 -1
  175. package/package.json +1 -1
  176. package/dist/agents/sandbox/ephemeral-executor.d.ts +0 -9
  177. package/dist/agents/sandbox/ephemeral-executor.js +0 -20
  178. package/dist/execution/ephemeral-runner.d.ts +0 -98
  179. package/dist/execution/ephemeral-runner.js +0 -189
  180. package/dist/prompts/sandbox/ephemeral-executor.d.ts +0 -1
  181. package/dist/prompts/sandbox/ephemeral-executor.js +0 -66
  182. package/dist/tools/bio/check-safety-panel.d.ts +0 -35
  183. package/dist/tools/bio/check-safety-panel.js +0 -76
  184. package/dist/tools/bio/get-impc-ko-profile.d.ts +0 -9
  185. package/dist/tools/bio/get-impc-ko-profile.js +0 -23
  186. package/dist/tools/bio/lookup-go-term.d.ts +0 -31
  187. package/dist/tools/bio/lookup-go-term.js +0 -116
  188. package/dist/tools/bio/search-bgee-expression.d.ts +0 -10
  189. package/dist/tools/bio/search-bgee-expression.js +0 -30
  190. package/dist/tools/bio/search-clinvar.d.ts +0 -13
  191. package/dist/tools/bio/search-clinvar.js +0 -28
  192. package/dist/tools/bio/search-dgidb.d.ts +0 -53
  193. package/dist/tools/bio/search-dgidb.js +0 -213
  194. package/dist/tools/bio/search-disgenet.d.ts +0 -30
  195. package/dist/tools/bio/search-disgenet.js +0 -84
  196. package/dist/tools/bio/search-drugbank.d.ts +0 -38
  197. package/dist/tools/bio/search-drugbank.js +0 -109
  198. package/dist/tools/bio/search-gwas-catalog.d.ts +0 -33
  199. package/dist/tools/bio/search-gwas-catalog.js +0 -172
  200. package/dist/tools/bio/search-pathway.d.ts +0 -12
  201. package/dist/tools/bio/search-pathway.js +0 -32
  202. package/dist/tools/bio/search-pharmgkb.d.ts +0 -17
  203. package/dist/tools/bio/search-pharmgkb.js +0 -52
  204. package/dist/tools/execute-plan.d.ts +0 -64
  205. package/dist/tools/execute-plan.js +0 -211
  206. package/dist/tools/run-ephemeral.d.ts +0 -33
  207. package/dist/tools/run-ephemeral.js +0 -65
@@ -1,109 +0,0 @@
1
- /**
2
- * searchDrugbank — search DrugBank for drug records and target-driven lookups.
3
- *
4
- * Requires `DRUGBANK_API_KEY`. Without the key, `getDrugbankHeaders` throws
5
- * on first call; the harness surfaces that as a tool `is_error` envelope.
6
- */
7
- import { ok } from "neverthrow";
8
- import { z } from "zod";
9
- import { defineTool } from "../define-tool.js";
10
- import { apiFetchValidated, describeApiError } from "../lib/api-utils.js";
11
- import { DRUGBANK_BASE, getDrugbankHeaders } from "../lib/drugbank-config.js";
12
- // A single schema that both validates and normalizes one DrugBank record.
13
- // The `.object(...)` half is the snake_case wire shape (every field optional —
14
- // the API omits absent values); the `.transform(...)` half maps it into the
15
- // camelCase result we return. Parsing IS the validation: `apiFetchValidated`
16
- // runs this schema over the JSON, so a payload whose field TYPES drift (an
17
- // object where the API used to send an array, a number for a string) is
18
- // rejected as `invalid_response` instead of being silently mis-mapped. Because
19
- // the schema carries the transform, `z.infer` below is the OUTPUT (camelCase)
20
- // type callers receive — there is no separate raw interface or mapper to keep
21
- // in sync.
22
- const DrugResultSchema = z
23
- .object({
24
- drugbank_id: z.string().optional(),
25
- name: z.string().optional(),
26
- description: z.string().optional(),
27
- type: z.string().optional(),
28
- groups: z.array(z.string()).optional(),
29
- categories: z.array(z.object({ category: z.string().optional() })).optional(),
30
- indication: z.string().optional(),
31
- pharmacodynamics: z.string().optional(),
32
- mechanism_of_action: z.string().optional(),
33
- toxicity: z.string().optional(),
34
- half_life: z.string().optional(),
35
- targets: z
36
- .array(z.object({
37
- name: z.string().optional(),
38
- gene_name: z.string().optional(),
39
- actions: z.array(z.string()).optional(),
40
- known_action: z.string().optional(),
41
- }))
42
- .optional(),
43
- drug_interactions: z
44
- .array(z.object({
45
- drugbank_id: z.string().optional(),
46
- name: z.string().optional(),
47
- description: z.string().optional(),
48
- }))
49
- .optional(),
50
- })
51
- .transform((raw) => ({
52
- drugbankId: raw.drugbank_id ?? "",
53
- name: raw.name ?? "",
54
- description: (raw.description ?? "").slice(0, 500),
55
- type: raw.type ?? "",
56
- groups: raw.groups ?? [],
57
- categories: (raw.categories ?? []).map((c) => c.category ?? "").filter(Boolean),
58
- indication: (raw.indication ?? "").slice(0, 500),
59
- pharmacodynamics: (raw.pharmacodynamics ?? "").slice(0, 500),
60
- mechanismOfAction: (raw.mechanism_of_action ?? "").slice(0, 500),
61
- toxicity: (raw.toxicity ?? "").slice(0, 500),
62
- halfLife: raw.half_life ?? "",
63
- targets: (raw.targets ?? []).slice(0, 20).map((t) => ({
64
- name: t.name ?? "",
65
- geneSymbol: t.gene_name ?? "",
66
- actions: t.actions ?? [],
67
- knownAction: t.known_action ?? "",
68
- })),
69
- interactions: (raw.drug_interactions ?? []).slice(0, 20).map((i) => ({
70
- drugbankId: i.drugbank_id ?? "",
71
- name: i.name ?? "",
72
- description: (i.description ?? "").slice(0, 200),
73
- })),
74
- }));
75
- // The /drugs endpoint returns a single object (by-id lookup) or an array
76
- // (query/target lookups); accept either, transforming each element.
77
- const DrugResponseSchema = z.union([DrugResultSchema, z.array(DrugResultSchema)]);
78
- export function createSearchDrugbankTool(deps) {
79
- return defineTool({
80
- id: "search_drugbank",
81
- description: "Search DrugBank for drug information including targets, interactions, indications, " +
82
- "pharmacodynamics, and toxicity. Requires DRUGBANK_API_KEY. Use for drug repurposing " +
83
- "analysis, drug-drug interaction assessment, and connecting omics targets to existing therapeutics.",
84
- inputSchema: z.object({
85
- query: z.string().describe("Drug name (e.g. imatinib), DrugBank ID (e.g. DB00619), or target gene symbol for reverse lookup"),
86
- searchType: z.enum(["drug", "target"]).default("drug").describe("'drug' to search by drug name/ID, 'target' to find drugs for a gene target"),
87
- limit: z.number().int().min(1).max(50).default(10).describe("Max results to return"),
88
- }),
89
- execute: async ({ query, searchType = "drug", limit = 10 }) => {
90
- const headers = getDrugbankHeaders(deps.apiKey);
91
- let url;
92
- if (searchType === "target") {
93
- url = `${DRUGBANK_BASE}/drugs?target=${encodeURIComponent(query)}&limit=${limit}`;
94
- }
95
- else if (query.startsWith("DB")) {
96
- url = `${DRUGBANK_BASE}/drugs/${query}`;
97
- }
98
- else {
99
- url = `${DRUGBANK_BASE}/drugs?q=${encodeURIComponent(query)}&limit=${limit}`;
100
- }
101
- const res = await apiFetchValidated(url, DrugResponseSchema, { headers });
102
- if (res.isErr())
103
- throw new Error(describeApiError(res.error));
104
- // Already validated + normalized by DrugResultSchema's transform.
105
- const drugs = Array.isArray(res.value) ? res.value : [res.value];
106
- return ok({ drugs: drugs.slice(0, limit) });
107
- },
108
- });
109
- }
@@ -1,33 +0,0 @@
1
- /**
2
- * searchGwasCatalog — search NHGRI-EBI GWAS Catalog for SNP-trait associations.
3
- *
4
- * Public REST API, no key required. Three search modes:
5
- * - 'gene' — gene-trait associations (via SNP-by-gene lookup, then assocs)
6
- * - 'trait' — trait-gene associations (via EFO trait search, then assocs)
7
- * - 'variant' — direct rsID lookup
8
- */
9
- interface GwasAssociation {
10
- rsId: string;
11
- pValue: number;
12
- pValueMantissa: number;
13
- pValueExponent: number;
14
- riskAllele: string;
15
- riskFrequency: string;
16
- orBeta: number | null;
17
- ci: string;
18
- trait: string;
19
- mappedGenes: string[];
20
- studyAccession: string;
21
- pubmedId: string;
22
- sampleSize: string;
23
- }
24
- export declare const searchGwasCatalogTool: import("../define-tool.js").Tool<{
25
- query: string;
26
- searchType: "gene" | "variant" | "trait";
27
- pValueThreshold: number;
28
- limit: number;
29
- }, {
30
- totalFound: number;
31
- associations: GwasAssociation[];
32
- }>;
33
- export {};
@@ -1,172 +0,0 @@
1
- /**
2
- * searchGwasCatalog — search NHGRI-EBI GWAS Catalog for SNP-trait associations.
3
- *
4
- * Public REST API, no key required. Three search modes:
5
- * - 'gene' — gene-trait associations (via SNP-by-gene lookup, then assocs)
6
- * - 'trait' — trait-gene associations (via EFO trait search, then assocs)
7
- * - 'variant' — direct rsID lookup
8
- */
9
- import { ok } from "neverthrow";
10
- import { z } from "zod";
11
- import { defineTool } from "../define-tool.js";
12
- import { apiFetchValidated, describeApiError } from "../lib/api-utils.js";
13
- import { GWAS_BASE, GWAS_HEADERS } from "../lib/gwas-catalog-config.js";
14
- // GWAS Catalog raw wire shapes (HAL+JSON), validated at the fetch boundary.
15
- // Every field is optional because the API omits absent values; leaf strings
16
- // that the API can return as an explicit `null` are `.nullable()`.
17
- const RawGwasStudySchema = z.object({
18
- accessionId: z.string().optional(),
19
- pubmedId: z.string().optional(),
20
- initialSampleSize: z.string().optional(),
21
- });
22
- // `_snpRsId` is not on the wire — the 'gene' search path assigns it after
23
- // parsing (`a._snpRsId = snp.rsId`), so it stays a writable optional field.
24
- const RawGwasAssociationSchema = z.object({
25
- _snpRsId: z.string().optional(),
26
- loci: z
27
- .array(z.object({
28
- strongestRiskAlleles: z.array(z.object({ riskAlleleName: z.string().nullable().optional() })).optional(),
29
- authorReportedGenes: z.array(z.object({ geneName: z.string().nullable().optional() })).optional(),
30
- }))
31
- .optional(),
32
- efoTraits: z.array(z.object({ trait: z.string().nullable().optional() })).optional(),
33
- study: RawGwasStudySchema.optional(),
34
- pvalueMantissa: z.number().optional(),
35
- pvalueExponent: z.number().optional(),
36
- riskFrequency: z.string().optional(),
37
- orPerCopyNum: z.number().nullable().optional(),
38
- betaNum: z.number().nullable().optional(),
39
- range: z.string().optional(),
40
- });
41
- const GwasEmbeddedSchema = z.object({
42
- _embedded: z.object({ associations: z.array(RawGwasAssociationSchema).optional() }).optional(),
43
- page: z.object({ totalElements: z.number().optional() }).optional(),
44
- });
45
- const GwasTraitSearchResponseSchema = z.object({
46
- _embedded: z
47
- .object({
48
- efoTraits: z.array(z.object({ _links: z.object({ self: z.object({ href: z.string().optional() }).optional() }).optional() })).optional(),
49
- })
50
- .optional(),
51
- });
52
- const GwasSnpSearchResponseSchema = z.object({
53
- _embedded: z
54
- .object({
55
- singleNucleotidePolymorphisms: z
56
- .array(z.object({
57
- rsId: z.string().optional(),
58
- _links: z.object({ associations: z.object({ href: z.string().optional() }).optional() }).optional(),
59
- }))
60
- .optional(),
61
- })
62
- .optional(),
63
- });
64
- function mapAssociation(a) {
65
- const loci = a.loci ?? [];
66
- const riskAlleles = loci.flatMap((l) => l.strongestRiskAlleles?.map((ra) => String(ra.riskAlleleName ?? "")) ?? []);
67
- const genes = loci.flatMap((l) => l.authorReportedGenes?.map((g) => String(g.geneName ?? "")) ?? []);
68
- const traits = (a.efoTraits ?? []).map((t) => String(t.trait ?? ""));
69
- const study = a.study ?? {};
70
- const pMantissa = a.pvalueMantissa ?? 0;
71
- const pExponent = a.pvalueExponent ?? 0;
72
- return {
73
- rsId: String(a._snpRsId ?? riskAlleles[0]?.split("-")[0] ?? ""),
74
- pValue: pMantissa * Math.pow(10, pExponent),
75
- pValueMantissa: pMantissa,
76
- pValueExponent: pExponent,
77
- riskAllele: riskAlleles.join(", "),
78
- riskFrequency: String(a.riskFrequency ?? ""),
79
- orBeta: (a.orPerCopyNum ?? a.betaNum ?? null),
80
- ci: String(a.range ?? ""),
81
- trait: traits.join("; "),
82
- mappedGenes: [...new Set(genes)],
83
- studyAccession: String(study.accessionId ?? ""),
84
- pubmedId: String(study.pubmedId ?? ""),
85
- sampleSize: String(study.initialSampleSize ?? ""),
86
- };
87
- }
88
- export const searchGwasCatalogTool = defineTool({
89
- id: "search_gwas_catalog",
90
- description: "Search the NHGRI-EBI GWAS Catalog for genome-wide association study results. " +
91
- "Returns SNP associations with traits/diseases, effect sizes, p-values, and mapped genes. " +
92
- "Use to find genetic evidence linking genes to phenotypes for target validation or " +
93
- "Mendelian randomization support.",
94
- inputSchema: z.object({
95
- query: z.string().describe("Gene symbol (e.g. PCSK9), trait keyword (e.g. cholesterol), or rsID (e.g. rs11591147)"),
96
- searchType: z
97
- .enum(["gene", "trait", "variant"])
98
- .describe("'gene' for gene-trait associations, 'trait' for trait-gene associations, 'variant' for a specific rsID"),
99
- pValueThreshold: z.number().default(5e-8).describe("P-value threshold for genome-wide significance"),
100
- limit: z.number().int().min(1).max(100).default(25).describe("Max results to return"),
101
- }),
102
- execute: async ({ query, searchType, pValueThreshold = 5e-8, limit = 25 }) => {
103
- let url;
104
- if (searchType === "variant") {
105
- const rsId = query.startsWith("rs") ? query : `rs${query}`;
106
- url = `${GWAS_BASE}/singleNucleotidePolymorphisms/${rsId}/associations?projection=associationBySnp`;
107
- }
108
- else if (searchType === "gene") {
109
- url = `${GWAS_BASE}/singleNucleotidePolymorphisms/search/findByGene?geneName=${encodeURIComponent(query)}&projection=snpByGene`;
110
- }
111
- else {
112
- url = `${GWAS_BASE}/efoTraits/search/findBySearchQuery?query=${encodeURIComponent(query)}`;
113
- }
114
- if (searchType === "trait") {
115
- const traitRes = await apiFetchValidated(url, GwasTraitSearchResponseSchema, { headers: GWAS_HEADERS });
116
- if (traitRes.isErr())
117
- throw new Error(describeApiError(traitRes.error));
118
- const traits = traitRes.value?._embedded?.efoTraits ?? [];
119
- if (traits.length === 0) {
120
- return ok({ totalFound: 0, associations: [] });
121
- }
122
- const traitUri = traits[0]?._links?.self?.href;
123
- if (!traitUri) {
124
- return ok({ totalFound: 0, associations: [] });
125
- }
126
- const traitId = traitUri.split("/").pop();
127
- url = `${GWAS_BASE}/efoTraits/${traitId}/associations?size=${limit}`;
128
- }
129
- if (searchType === "gene") {
130
- const snpRes = await apiFetchValidated(url, GwasSnpSearchResponseSchema, { headers: GWAS_HEADERS });
131
- if (snpRes.isErr())
132
- throw new Error(describeApiError(snpRes.error));
133
- const snps = snpRes.value?._embedded?.singleNucleotidePolymorphisms ?? [];
134
- const allAssocs = [];
135
- for (const snp of snps.slice(0, Math.min(limit, 10))) {
136
- const assocLink = snp?._links?.associations?.href;
137
- if (!assocLink)
138
- continue;
139
- const aRes = await apiFetchValidated(assocLink, GwasEmbeddedSchema, {
140
- headers: GWAS_HEADERS,
141
- });
142
- if (aRes.isOk()) {
143
- for (const a of aRes.value?._embedded?.associations ?? []) {
144
- a._snpRsId = snp.rsId;
145
- allAssocs.push(a);
146
- }
147
- }
148
- if (allAssocs.length >= limit)
149
- break;
150
- }
151
- return ok({
152
- totalFound: allAssocs.length,
153
- associations: allAssocs.slice(0, limit).map(mapAssociation),
154
- });
155
- }
156
- const res = await apiFetchValidated(url, GwasEmbeddedSchema, { headers: GWAS_HEADERS });
157
- if (res.isErr())
158
- throw new Error(describeApiError(res.error));
159
- const rawAssocs = res.value?._embedded?.associations ?? [];
160
- const totalFound = res.value?.page?.totalElements ?? rawAssocs.length;
161
- const filtered = rawAssocs
162
- .filter((a) => {
163
- const p = (a.pvalueMantissa ?? 1) * Math.pow(10, a.pvalueExponent ?? 0);
164
- return p <= pValueThreshold;
165
- })
166
- .slice(0, limit);
167
- return ok({
168
- totalFound,
169
- associations: filtered.map(mapAssociation),
170
- });
171
- },
172
- });
@@ -1,12 +0,0 @@
1
- /**
2
- * searchPathway — search biological pathways via KEGG and Reactome.
3
- */
4
- export declare const searchPathwayTool: import("../define-tool.js").Tool<{
5
- query: string;
6
- source: "kegg" | "reactome" | "both";
7
- organism: string;
8
- includeGenes: boolean;
9
- maxResults: number;
10
- }, {
11
- pathways: import("../lib/pathway-client.js").Pathway[];
12
- }>;
@@ -1,32 +0,0 @@
1
- /**
2
- * searchPathway — search biological pathways via KEGG and Reactome.
3
- */
4
- import { ok } from "neverthrow";
5
- import { z } from "zod";
6
- import { defineTool } from "../define-tool.js";
7
- import { searchPathways } from "../lib/pathway-client.js";
8
- export const searchPathwayTool = defineTool({
9
- id: "search_pathway",
10
- description: "Search biological pathways in KEGG and/or Reactome databases. " +
11
- "Returns pathway IDs, names, URLs, and optionally gene lists. " +
12
- "Use source='both' to query both databases in parallel.",
13
- inputSchema: z.object({
14
- query: z.string().min(1).describe("Pathway search term (e.g. 'apoptosis', 'MAPK signaling')"),
15
- source: z.enum(["kegg", "reactome", "both"]).default("both").describe("Which database to search"),
16
- organism: z
17
- .string()
18
- .default("hsa")
19
- .describe("KEGG organism code (e.g. 'hsa' for human, 'mmu' for mouse). " + "Automatically mapped to species name for Reactome."),
20
- includeGenes: z.boolean().default(false).describe("Whether to fetch gene lists for each pathway (slower)"),
21
- maxResults: z.number().int().min(1).max(50).default(10),
22
- }),
23
- execute: async ({ query, source, organism, includeGenes, maxResults }) => {
24
- const pathways = await searchPathways(query, {
25
- source,
26
- organism,
27
- includeGenes,
28
- maxResults,
29
- });
30
- return ok({ pathways });
31
- },
32
- });
@@ -1,17 +0,0 @@
1
- /**
2
- * searchPharmgkb — search PharmGKB for pharmacogenomic clinical annotations.
3
- */
4
- export declare const searchPharmgkbTool: import("../define-tool.js").Tool<{
5
- query: string;
6
- searchType: "drug" | "gene";
7
- limit: number;
8
- }, {
9
- annotations: {
10
- gene: string;
11
- drug: string;
12
- phenotype: null;
13
- levelOfEvidence: string;
14
- guidelineSource: null;
15
- summary: null;
16
- }[];
17
- }>;
@@ -1,52 +0,0 @@
1
- /**
2
- * searchPharmgkb — search PharmGKB for pharmacogenomic clinical annotations.
3
- */
4
- import { ok } from "neverthrow";
5
- import { z } from "zod";
6
- import { defineTool } from "../define-tool.js";
7
- import { apiFetchValidated, describeApiError } from "../lib/api-utils.js";
8
- import { PHARMGKB_BASE, PHARMGKB_HEADERS } from "../lib/pharmgkb-config.js";
9
- // Raw PharmGKB clinicalAnnotation wire shape, validated at the fetch boundary.
10
- // Every field is optional because the API omits absent values; the mapper in
11
- // `execute` folds request context (`query`) into its output, so the schema
12
- // models only the raw wire and the mapping stays where it is.
13
- const PharmgkbAnnotationSchema = z.object({
14
- location: z.object({ genes: z.array(z.object({ symbol: z.string().optional() })).optional() }).optional(),
15
- relatedChemicals: z.array(z.object({ name: z.string().optional() })).optional(),
16
- levelOfEvidence: z.object({ term: z.string().optional() }).optional(),
17
- });
18
- const PharmgkbResponseSchema = z.object({
19
- data: z.array(PharmgkbAnnotationSchema).optional(),
20
- });
21
- export const searchPharmgkbTool = defineTool({
22
- id: "search_pharmgkb",
23
- description: "Search PharmGKB clinical annotations for a gene-drug pharmacogenomic link — use it to judge whether a gene affects a drug's metabolism, efficacy, or toxicity. " +
24
- "Returns annotations[]: { gene, drug, levelOfEvidence (PharmGKB 1A…4, where 1A is guideline-backed) }. The phenotype, guidelineSource and summary fields are always null " +
25
- "in this tool's output — treat levelOfEvidence as the only strength signal and do not attribute CPIC/DPWG guideline text to it. " +
26
- "Matching is an exact field filter, not a search: 'CYP2D6' resolves, 'cytochrome P450 2D6' does not. " +
27
- "An empty annotations array is valid no-data (no curated PGx link) — do not retry.",
28
- inputSchema: z.object({
29
- query: z
30
- .string()
31
- .describe("Exact gene symbol (e.g. 'CYP2D6') when searchType='gene', or exact drug name (e.g. 'tamoxifen') when searchType='drug'. No fuzzy matching."),
32
- searchType: z.enum(["gene", "drug"]).describe("'gene' filters PharmGKB on location.genes.symbol; 'drug' filters on relatedChemicals.name."),
33
- limit: z.number().int().min(1).max(50).default(20).describe("Max annotations to return (default 20, max 50); applied client-side after the fetch."),
34
- }),
35
- execute: async ({ query, searchType, limit = 20 }) => {
36
- const filter = searchType === "gene" ? `location.genes.symbol=${encodeURIComponent(query)}` : `relatedChemicals.name=${encodeURIComponent(query)}`;
37
- const endpoint = `${PHARMGKB_BASE}/clinicalAnnotation?${filter}`;
38
- const res = await apiFetchValidated(endpoint, PharmgkbResponseSchema, { headers: PHARMGKB_HEADERS });
39
- if (res.isErr())
40
- throw new Error(describeApiError(res.error));
41
- const data = res.value?.data ?? [];
42
- const annotations = data.slice(0, limit).map((ann) => ({
43
- gene: ann.location?.genes?.map((g) => g.symbol).join(", ") ?? query,
44
- drug: ann.relatedChemicals?.map((c) => c.name).join(", ") ?? "",
45
- phenotype: null,
46
- levelOfEvidence: ann.levelOfEvidence?.term ?? "Unknown",
47
- guidelineSource: null,
48
- summary: null,
49
- }));
50
- return ok({ annotations });
51
- },
52
- });
@@ -1,64 +0,0 @@
1
- /**
2
- * executePlan — launch the DBOS `executeAnalysis` parent workflow for an
3
- * approved plan.
4
- *
5
- * Results are pull-only: the tool does NOT register a `.then(saveRunResult)`
6
- * or any completion callback that writes to the conversation thread. The
7
- * conversation agent retrieves run results via `inspectRun` on a later turn.
8
- *
9
- * Authorization contract: the tool runs in the chat HTTP route, so its session
10
- * carries the live auth. It authorizes through the injected `RunAuthorizer`
11
- * seam and never reads a credential. The flow is dedup → reserve → authorize
12
- * → startWorkflow:
13
- *
14
- * 1. Dedup pre-check via `queryActiveRun`. On hit, return the existing
15
- * `runId` with no authorization and no `startWorkflow`.
16
- * 2. Reserve the slot: `insertRun` the `cortex_runs` row (`runId =
17
- * randomUUID()`, the bare UUID that IS the DBOS workflowID) BEFORE
18
- * authorizing. The partial-unique index is the race backstop; the loser
19
- * returns the winner's `runId` without authorizing — nothing to revoke.
20
- * 3. `runAuthorizer.authorize({ ..., frame: { runId } })` — authorizes the
21
- * run; the authorizer's persisted handle now lands because the row
22
- * exists. On failure, mark the reserved row `failed` (releasing the dedup
23
- * slot for a retry) and rethrow.
24
- * 4. `runLauncher.launch(executeAnalysis, { workflowId: runId }, ...)`,
25
- * passing the `RunSession` in the workflow input. On failure, revoke + mark
26
- * failed.
27
- */
28
- import type { Pool } from "pg";
29
- import type { ResourcePolicy } from "../config/resource-limits.js";
30
- import type { RunAuthorizer } from "../execution/run-authorizer.js";
31
- import type { RunLauncher } from "../execution/run-launcher.js";
32
- import type { ExecuteAnalysisInput, ExecuteAnalysisResult } from "../workflows/execute-analysis.js";
33
- type ExecuteAnalysisWorkflow = (input: ExecuteAnalysisInput) => Promise<ExecuteAnalysisResult>;
34
- export interface ExecutePlanToolDeps {
35
- readonly pool: Pool;
36
- /** Registered `executeAnalysis` workflow callable — produced by `registerExecuteAnalysis` (wired by `assembleCoreRuntime`). */
37
- readonly executeAnalysisWorkflow: ExecuteAnalysisWorkflow;
38
- /** Authorizes the run at the async edge and revokes on the abort paths. */
39
- readonly runAuthorizer: RunAuthorizer;
40
- /** Starts the durable run — the durability engine stays behind this seam. */
41
- readonly runLauncher: RunLauncher;
42
- /**
43
- * Host resource policy. The machine budget is snapshotted into the workflow
44
- * input here at the async edge — the workflow body never reads live config,
45
- * so a mid-run config edit cannot change a running run's admission decisions.
46
- */
47
- readonly resourcePolicy?: ResourcePolicy;
48
- }
49
- export declare class PlanNotFoundError extends Error {
50
- constructor(planId: string);
51
- }
52
- export declare class PlanMalformedError extends Error {
53
- constructor(planId: string);
54
- }
55
- export declare class PlanValidationError extends Error {
56
- readonly errors: string[];
57
- constructor(planId: string, errors: string[]);
58
- }
59
- export declare function createExecutePlanTool(deps: ExecutePlanToolDeps): import("./define-tool.js").Tool<{
60
- planId: string;
61
- }, {
62
- runId: string;
63
- }>;
64
- export {};