@inflexa-ai/harness 0.1.1 → 0.3.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 (330) hide show
  1. package/README.md +48 -0
  2. package/dist/agents/conversation-agent.d.ts +15 -11
  3. package/dist/agents/conversation-agent.js +31 -34
  4. package/dist/agents/report-builder.js +2 -1
  5. package/dist/agents/sandbox/bulk-transcriptomics-agent.js +1 -1
  6. package/dist/agents/sandbox/cheminformatics-agent.js +1 -19
  7. package/dist/agents/sandbox/chromatin-agent.js +1 -10
  8. package/dist/agents/sandbox/dna-methylation-agent.js +1 -10
  9. package/dist/agents/sandbox/drug-repurposing-agent.js +4 -14
  10. package/dist/agents/sandbox/enrichment-agent.js +1 -10
  11. package/dist/agents/sandbox/ephemeral-executor.js +1 -10
  12. package/dist/agents/sandbox/genomic-variant-agent.js +1 -3
  13. package/dist/agents/sandbox/immune-profiling-agent.js +1 -13
  14. package/dist/agents/sandbox/metabolomics-agent.js +1 -1
  15. package/dist/agents/sandbox/microbiome-agent.js +1 -1
  16. package/dist/agents/sandbox/multi-omics-integration-agent.js +1 -12
  17. package/dist/agents/sandbox/multimodal-sc-agent.js +1 -1
  18. package/dist/agents/sandbox/network-agent.js +1 -10
  19. package/dist/agents/sandbox/pkpd-clinical-response-agent.js +2 -8
  20. package/dist/agents/sandbox/proteomics-agent.js +1 -1
  21. package/dist/agents/sandbox/scientific-executor.js +1 -10
  22. package/dist/agents/sandbox/shared.d.ts +20 -6
  23. package/dist/agents/sandbox/shared.js +50 -39
  24. package/dist/agents/sandbox/single-cell-agent.js +1 -1
  25. package/dist/agents/sandbox/spatial-omics-agent.js +1 -1
  26. package/dist/agents/sandbox/statistical-modeling-agent.js +1 -1
  27. package/dist/agents/sandbox/translational-safety-agent.js +3 -9
  28. package/dist/agents/sandbox/types.d.ts +1 -1
  29. package/dist/agents/system-prompt.d.ts +37 -11
  30. package/dist/agents/system-prompt.js +35 -12
  31. package/dist/app/chat-turn.d.ts +3 -0
  32. package/dist/app/chat-turn.js +3 -1
  33. package/dist/app/data-profile-orientation.d.ts +34 -0
  34. package/dist/app/data-profile-orientation.js +117 -0
  35. package/dist/app/synthesize-run.d.ts +3 -0
  36. package/dist/app/synthesize-run.js +3 -1
  37. package/dist/contracts/schemas/chat-parts.d.ts +9 -9
  38. package/dist/execution/artifact-metadata.d.ts +3 -0
  39. package/dist/execution/artifact-metadata.js +10 -3
  40. package/dist/execution/artifact-registration.d.ts +2 -1
  41. package/dist/execution/artifact-registration.js +8 -2
  42. package/dist/execution/ephemeral-runner.d.ts +16 -0
  43. package/dist/execution/ephemeral-runner.js +31 -5
  44. package/dist/execution/post-step-pipeline.d.ts +3 -0
  45. package/dist/execution/post-step-pipeline.js +24 -4
  46. package/dist/execution/reconcile-manifest.d.ts +7 -0
  47. package/dist/execution/reconcile-manifest.js +19 -5
  48. package/dist/execution/report-runner.d.ts +3 -0
  49. package/dist/execution/report-runner.js +4 -4
  50. package/dist/execution/run-synthesis.js +20 -19
  51. package/dist/execution/step-summary.d.ts +3 -0
  52. package/dist/execution/step-summary.js +6 -3
  53. package/dist/index.d.ts +9 -2
  54. package/dist/index.js +24 -5
  55. package/dist/lib/chrome.d.ts +4 -1
  56. package/dist/lib/chrome.js +10 -7
  57. package/dist/lib/console-logger.d.ts +19 -0
  58. package/dist/lib/console-logger.js +62 -0
  59. package/dist/lib/logger.d.ts +88 -0
  60. package/dist/lib/logger.js +37 -0
  61. package/dist/lib/otel.d.ts +2 -1
  62. package/dist/lib/otel.js +8 -3
  63. package/dist/loop/__fixtures__/scripted-provider.d.ts +2 -2
  64. package/dist/loop/__fixtures__/scripted-provider.js +2 -1
  65. package/dist/loop/metrics.d.ts +33 -1
  66. package/dist/loop/metrics.js +56 -2
  67. package/dist/loop/run-agent.d.ts +13 -1
  68. package/dist/loop/run-agent.js +19 -4
  69. package/dist/memory/card-builders.d.ts +8 -1
  70. package/dist/memory/card-builders.js +20 -3
  71. package/dist/memory/reconstruct-cards.js +4 -3
  72. package/dist/memory/thread-history.d.ts +27 -2
  73. package/dist/memory/thread-history.js +45 -5
  74. package/dist/memory/working-memory.d.ts +93 -10
  75. package/dist/memory/working-memory.js +220 -84
  76. package/dist/prompts/SOUL.d.ts +22 -9
  77. package/dist/prompts/SOUL.js +56 -37
  78. package/dist/prompts/analogical-reasoner.d.ts +1 -1
  79. package/dist/prompts/analogical-reasoner.js +2 -2
  80. package/dist/prompts/briefing.d.ts +100 -0
  81. package/dist/prompts/briefing.js +166 -0
  82. package/dist/prompts/conversation.d.ts +1 -1
  83. package/dist/prompts/conversation.js +94 -440
  84. package/dist/prompts/literature-reviewer.d.ts +1 -1
  85. package/dist/prompts/literature-reviewer.js +10 -10
  86. package/dist/prompts/planner.js +28 -59
  87. package/dist/prompts/report-builder.d.ts +1 -1
  88. package/dist/prompts/report-builder.js +71 -24
  89. package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
  90. package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +2 -7
  91. package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
  92. package/dist/prompts/sandbox/cheminformatics-agent.js +11 -15
  93. package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
  94. package/dist/prompts/sandbox/chromatin-agent.js +3 -7
  95. package/dist/prompts/sandbox/data-profiler.d.ts +1 -1
  96. package/dist/prompts/sandbox/data-profiler.js +13 -8
  97. package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
  98. package/dist/prompts/sandbox/dna-methylation-agent.js +2 -7
  99. package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
  100. package/dist/prompts/sandbox/drug-repurposing-agent.js +15 -16
  101. package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
  102. package/dist/prompts/sandbox/enrichment-agent.js +2 -9
  103. package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
  104. package/dist/prompts/sandbox/genomic-variant-agent.js +2 -6
  105. package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
  106. package/dist/prompts/sandbox/immune-profiling-agent.js +3 -8
  107. package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
  108. package/dist/prompts/sandbox/metabolomics-agent.js +1 -7
  109. package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
  110. package/dist/prompts/sandbox/microbiome-agent.js +2 -7
  111. package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
  112. package/dist/prompts/sandbox/multi-omics-integration-agent.js +2 -8
  113. package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
  114. package/dist/prompts/sandbox/multimodal-sc-agent.js +2 -7
  115. package/dist/prompts/sandbox/network-agent.d.ts +1 -1
  116. package/dist/prompts/sandbox/network-agent.js +2 -7
  117. package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
  118. package/dist/prompts/sandbox/proteomics-agent.js +2 -9
  119. package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
  120. package/dist/prompts/sandbox/single-cell-agent.js +2 -9
  121. package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
  122. package/dist/prompts/sandbox/spatial-omics-agent.js +3 -6
  123. package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
  124. package/dist/prompts/sandbox/statistical-modeling-agent.js +2 -7
  125. package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
  126. package/dist/prompts/sandbox/translational-safety-agent.js +13 -14
  127. package/dist/prompts/sandbox-standards.d.ts +22 -14
  128. package/dist/prompts/sandbox-standards.js +91 -71
  129. package/dist/prompts/synthesis-agent.d.ts +1 -1
  130. package/dist/prompts/synthesis-agent.js +33 -131
  131. package/dist/provenance/collector.js +1 -1
  132. package/dist/providers/ai-sdk.js +31 -4
  133. package/dist/providers/prompt-cache.d.ts +74 -0
  134. package/dist/providers/prompt-cache.js +77 -0
  135. package/dist/providers/types.d.ts +33 -0
  136. package/dist/reference-data/catalog.d.ts +131 -0
  137. package/dist/reference-data/catalog.js +348 -0
  138. package/dist/reference-data/receipt.d.ts +41 -0
  139. package/dist/reference-data/receipt.js +40 -0
  140. package/dist/runtime/boot.d.ts +2 -2
  141. package/dist/runtime/boot.js +1 -1
  142. package/dist/runtime/connection-budget.d.ts +2 -2
  143. package/dist/runtime/connection-budget.js +5 -3
  144. package/dist/runtime/dbos.d.ts +7 -7
  145. package/dist/runtime/dbos.js +14 -11
  146. package/dist/runtime/shutdown.d.ts +2 -2
  147. package/dist/runtime/shutdown.js +4 -3
  148. package/dist/sandbox/create-sandbox.d.ts +2 -2
  149. package/dist/sandbox/docker-client.d.ts +2 -2
  150. package/dist/sandbox/docker-client.js +4 -1
  151. package/dist/sandbox/k8s-client.d.ts +3 -0
  152. package/dist/sandbox/k8s-client.js +5 -4
  153. package/dist/sandbox/notification-sweep.d.ts +3 -3
  154. package/dist/sandbox/notification-sweep.js +4 -2
  155. package/dist/sandbox/reaper.d.ts +3 -3
  156. package/dist/sandbox/reaper.js +7 -4
  157. package/dist/sandbox/watchdog.d.ts +3 -3
  158. package/dist/sandbox/watchdog.js +8 -2
  159. package/dist/schemas/data-profile-schemas.js +5 -3
  160. package/dist/schemas/plan-schemas.d.ts +21 -21
  161. package/dist/schemas/plan-schemas.js +9 -3
  162. package/dist/schemas/run-synthesis.d.ts +8 -4
  163. package/dist/schemas/run-synthesis.js +92 -56
  164. package/dist/schemas/validate-plan.js +1 -1
  165. package/dist/schemas/workflow-state.d.ts +7 -1
  166. package/dist/schemas/workflow-state.js +33 -19
  167. package/dist/state/artifacts.d.ts +13 -0
  168. package/dist/state/artifacts.js +27 -0
  169. package/dist/state/data-profile.d.ts +64 -5
  170. package/dist/state/index.d.ts +3 -3
  171. package/dist/state/index.js +1 -1
  172. package/dist/state/init.d.ts +2 -1
  173. package/dist/state/init.js +5 -6
  174. package/dist/tasks/data-profile.d.ts +25 -2
  175. package/dist/tasks/data-profile.js +95 -39
  176. package/dist/tools/bio/chembl.d.ts +38 -0
  177. package/dist/tools/bio/chembl.js +138 -0
  178. package/dist/tools/bio/comptox.d.ts +50 -0
  179. package/dist/tools/bio/comptox.js +149 -0
  180. package/dist/tools/bio/get-impc-ko-profile.js +9 -6
  181. package/dist/tools/bio/index.d.ts +5 -17
  182. package/dist/tools/bio/index.js +5 -17
  183. package/dist/tools/bio/keys.d.ts +14 -9
  184. package/dist/tools/bio/keys.js +16 -16
  185. package/dist/tools/bio/opentargets.d.ts +38 -0
  186. package/dist/tools/bio/opentargets.js +89 -0
  187. package/dist/tools/bio/pubchem.d.ts +29 -0
  188. package/dist/tools/bio/pubchem.js +92 -0
  189. package/dist/tools/bio/pubmed.d.ts +51 -0
  190. package/dist/tools/bio/pubmed.js +100 -0
  191. package/dist/tools/bio/search-bgee-expression.js +12 -6
  192. package/dist/tools/bio/search-clinical-trials.js +16 -5
  193. package/dist/tools/bio/search-dgidb.d.ts +11 -1
  194. package/dist/tools/bio/search-dgidb.js +92 -78
  195. package/dist/tools/bio/search-faers.js +8 -4
  196. package/dist/tools/bio/search-geo-datasets.js +20 -6
  197. package/dist/tools/bio/search-gwas-catalog.d.ts +1 -1
  198. package/dist/tools/bio/search-pharmgkb.js +10 -4
  199. package/dist/tools/display/index.d.ts +1 -0
  200. package/dist/tools/display/index.js +1 -0
  201. package/dist/tools/display/normalize-echart-spec.d.ts +61 -0
  202. package/dist/tools/display/normalize-echart-spec.js +211 -0
  203. package/dist/tools/display/show-user.d.ts +6 -1
  204. package/dist/tools/display/show-user.js +25 -12
  205. package/dist/tools/execute-plan.js +5 -2
  206. package/dist/tools/iterate-report.d.ts +171 -118
  207. package/dist/tools/iterate-report.js +336 -96
  208. package/dist/tools/lib/chembl-client.d.ts +12 -0
  209. package/dist/tools/lib/chembl-client.js +127 -0
  210. package/dist/tools/lib/ctx-ops.d.ts +224 -0
  211. package/dist/tools/lib/ctx-ops.js +450 -0
  212. package/dist/tools/lib/iuphar-client.js +10 -1
  213. package/dist/tools/lib/openfda-client.js +11 -3
  214. package/dist/tools/lib/pathway-client.js +10 -4
  215. package/dist/tools/lib/pubchem-ops.d.ts +74 -0
  216. package/dist/tools/lib/pubchem-ops.js +224 -0
  217. package/dist/tools/memory/update-working-memory.d.ts +45 -7
  218. package/dist/tools/memory/update-working-memory.js +111 -41
  219. package/dist/tools/report/index.d.ts +3 -2
  220. package/dist/tools/report/index.js +3 -2
  221. package/dist/tools/research/generate-analogy-report.js +25 -12
  222. package/dist/tools/research/generate-plan.d.ts +12 -5
  223. package/dist/tools/research/generate-plan.js +168 -37
  224. package/dist/tools/research/index.d.ts +1 -0
  225. package/dist/tools/research/index.js +1 -0
  226. package/dist/tools/research/inspect-data-profile.d.ts +85 -0
  227. package/dist/tools/research/inspect-data-profile.js +161 -0
  228. package/dist/tools/research/literature-reviewer.d.ts +3 -0
  229. package/dist/tools/research/literature-reviewer.js +5 -6
  230. package/dist/tools/sandbox/list-available-packages.d.ts +79 -5
  231. package/dist/tools/sandbox/list-available-packages.js +152 -10
  232. package/dist/tools/sandbox/list-available-refs.d.ts +141 -5
  233. package/dist/tools/sandbox/list-available-refs.js +647 -134
  234. package/dist/tools/sandbox/report-blocker.d.ts +39 -9
  235. package/dist/tools/sandbox/report-blocker.js +43 -20
  236. package/dist/tools/workspace/execute-command.d.ts +3 -0
  237. package/dist/tools/workspace/execute-command.js +3 -1
  238. package/dist/tools/workspace/read-file.js +4 -5
  239. package/dist/tools/workspace/run-exec.d.ts +4 -4
  240. package/dist/tools/workspace/run-exec.js +4 -4
  241. package/dist/tools/workspace/show-file.js +10 -6
  242. package/dist/tools/workspace/show-plan.js +12 -6
  243. package/dist/tools/workspace/workspace-search.d.ts +1 -1
  244. package/dist/tools/workspace/workspace-search.js +11 -5
  245. package/dist/workflows/execute-analysis.d.ts +46 -4
  246. package/dist/workflows/execute-analysis.js +118 -13
  247. package/dist/workflows/execute-target-assessment.d.ts +3 -0
  248. package/dist/workflows/execute-target-assessment.js +19 -15
  249. package/dist/workflows/sandbox-step.d.ts +9 -0
  250. package/dist/workflows/sandbox-step.js +39 -17
  251. package/dist/workflows/target-assessment/lib/citation-validator.js +2 -2
  252. package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.js +2 -1
  253. package/dist/workflows/target-assessment/lib/llm-step.d.ts +3 -0
  254. package/dist/workflows/target-assessment/lib/llm-step.js +3 -1
  255. package/dist/workflows/target-assessment/lib/structured-llm.d.ts +3 -0
  256. package/dist/workflows/target-assessment/lib/structured-llm.js +1 -0
  257. package/dist/workflows/target-assessment/phase5-persist.d.ts +3 -0
  258. package/dist/workflows/target-assessment/phase5-persist.js +8 -3
  259. package/dist/workflows/target-assessment/progress.d.ts +2 -1
  260. package/dist/workflows/target-assessment/progress.js +4 -3
  261. package/dist/workspace/filesystem.js +163 -74
  262. package/dist/workspace/paths.d.ts +2 -2
  263. package/dist/workspace/paths.js +2 -2
  264. package/package.json +95 -96
  265. package/dist/execution/filesystem-artifact-registry.d.ts +0 -57
  266. package/dist/execution/filesystem-artifact-registry.js +0 -92
  267. package/dist/lib/identifiers.d.ts +0 -8
  268. package/dist/lib/identifiers.js +0 -10
  269. package/dist/lib/mime-detect.d.ts +0 -17
  270. package/dist/lib/mime-detect.js +0 -29
  271. package/dist/lib/streamed-download.d.ts +0 -28
  272. package/dist/lib/streamed-download.js +0 -111
  273. package/dist/lib/trace-context.d.ts +0 -11
  274. package/dist/lib/trace-context.js +0 -49
  275. package/dist/providers/llm-capabilities.d.ts +0 -33
  276. package/dist/providers/llm-capabilities.js +0 -58
  277. package/dist/schemas/render-step-prompt.d.ts +0 -32
  278. package/dist/schemas/render-step-prompt.js +0 -76
  279. package/dist/tools/bio/find-approval-precedent.d.ts +0 -32
  280. package/dist/tools/bio/find-approval-precedent.js +0 -72
  281. package/dist/tools/bio/get-article-details.d.ts +0 -11
  282. package/dist/tools/bio/get-article-details.js +0 -20
  283. package/dist/tools/bio/get-article-full-text.d.ts +0 -19
  284. package/dist/tools/bio/get-article-full-text.js +0 -32
  285. package/dist/tools/bio/get-bioactivity.d.ts +0 -11
  286. package/dist/tools/bio/get-bioactivity.js +0 -23
  287. package/dist/tools/bio/get-drug-info.d.ts +0 -9
  288. package/dist/tools/bio/get-drug-info.js +0 -22
  289. package/dist/tools/bio/get-mechanism.d.ts +0 -8
  290. package/dist/tools/bio/get-mechanism.js +0 -18
  291. package/dist/tools/bio/get-pubchem-assays.d.ts +0 -22
  292. package/dist/tools/bio/get-pubchem-assays.js +0 -107
  293. package/dist/tools/bio/get-pubchem-cross-refs.d.ts +0 -15
  294. package/dist/tools/bio/get-pubchem-cross-refs.js +0 -68
  295. package/dist/tools/bio/get-target-safety.d.ts +0 -18
  296. package/dist/tools/bio/get-target-safety.js +0 -26
  297. package/dist/tools/bio/search-compounds.d.ts +0 -22
  298. package/dist/tools/bio/search-compounds.js +0 -160
  299. package/dist/tools/bio/search-ctx-chemical.d.ts +0 -50
  300. package/dist/tools/bio/search-ctx-chemical.js +0 -130
  301. package/dist/tools/bio/search-ctx-exposure.d.ts +0 -55
  302. package/dist/tools/bio/search-ctx-exposure.js +0 -176
  303. package/dist/tools/bio/search-ctx-hazard.d.ts +0 -111
  304. package/dist/tools/bio/search-ctx-hazard.js +0 -178
  305. package/dist/tools/bio/search-opentargets.d.ts +0 -11
  306. package/dist/tools/bio/search-opentargets.js +0 -25
  307. package/dist/tools/bio/search-pubchem-compound.d.ts +0 -26
  308. package/dist/tools/bio/search-pubchem-compound.js +0 -110
  309. package/dist/tools/bio/search-pubmed.d.ts +0 -17
  310. package/dist/tools/bio/search-pubmed.js +0 -31
  311. package/dist/tools/bio/search-targets.d.ts +0 -9
  312. package/dist/tools/bio/search-targets.js +0 -20
  313. package/dist/tools/bio/search-toxcast.d.ts +0 -36
  314. package/dist/tools/bio/search-toxcast.js +0 -141
  315. package/dist/workflows/register-workflows.d.ts +0 -51
  316. package/dist/workflows/register-workflows.js +0 -48
  317. package/dist/workflows/resume-execute-analysis.d.ts +0 -70
  318. package/dist/workflows/resume-execute-analysis.js +0 -80
  319. package/dist/workflows/target-assessment/correction-loop.d.ts +0 -45
  320. package/dist/workflows/target-assessment/correction-loop.js +0 -62
  321. package/dist/workflows/target-assessment/steps/phase0-resolve.d.ts +0 -71
  322. package/dist/workflows/target-assessment/steps/phase0-resolve.js +0 -21
  323. package/dist/workflows/target-assessment/steps/phase1-aggregate.d.ts +0 -1018
  324. package/dist/workflows/target-assessment/steps/phase1-aggregate.js +0 -25
  325. package/dist/workspace/mount-strategy.d.ts +0 -50
  326. package/dist/workspace/mount-strategy.js +0 -67
  327. package/dist/workspace/provenance-collector.d.ts +0 -34
  328. package/dist/workspace/provenance-collector.js +0 -16
  329. package/dist/workspace/workspace-paths.d.ts +0 -26
  330. package/dist/workspace/workspace-paths.js +0 -31
@@ -0,0 +1,149 @@
1
+ /**
2
+ * comptox — the single EPA CompTox (CTX) tool: ToxCast/Tox21 bioactivity,
3
+ * in-vivo hazard, chemical identity + physchem, and exposure behind one
4
+ * `dataset` discriminator.
5
+ *
6
+ * All four datasets key off the same chemical resolution and the same
7
+ * EPA_CCTE_API_KEY, so the key contract is stated once in the description
8
+ * rather than four times.
9
+ *
10
+ * The input is a flat object with a `dataset` discriminator — not a
11
+ * `z.discriminatedUnion`, which `defineTool` rejects (the model needs a
12
+ * top-level `"type":"object"`). `dataType` and `limit` mean different things
13
+ * per dataset, so `.refine` guards reject a value that belongs to another
14
+ * dataset (a 'toxval' under dataset 'exposure' would otherwise silently fetch
15
+ * nothing) and a limit above the dataset's own ceiling.
16
+ */
17
+ import { ok } from "neverthrow";
18
+ import { z } from "zod";
19
+ import { defineTool } from "../define-tool.js";
20
+ import { fetchCtxChemicalDetail, fetchCtxChemicalProperties, fetchCtxExposureData, fetchCtxHazardData, fetchToxcastBioactivity, resolveDtxsid, } from "../lib/ctx-ops.js";
21
+ import { getEpaCcteHeaders } from "../lib/toxcast-config.js";
22
+ const HAZARD_DATA_TYPES = ["toxval", "genetox", "cancer", "all"];
23
+ const EXPOSURE_DATA_TYPES = ["seem", "httk", "functional-use", "product-data", "all"];
24
+ function isHazardDataType(value) {
25
+ return value !== undefined && HAZARD_DATA_TYPES.includes(value);
26
+ }
27
+ function isExposureDataType(value) {
28
+ return value !== undefined && EXPOSURE_DATA_TYPES.includes(value);
29
+ }
30
+ const inputSchema = z
31
+ .object({
32
+ dataset: z
33
+ .enum(["toxcast", "hazard", "chemical", "exposure"])
34
+ .describe("Which CompTox dataset to query. " +
35
+ "'toxcast' — ToxCast/Tox21 in-vitro high-throughput screening; profiles toxicological liability across hundreds of assay endpoints (nuclear receptors, stress response, " +
36
+ "mitochondrial toxicity), e.g. 'is this compound an endocrine disruptor?'. Returns the resolved chemical plus assay results — endpoint name, AC50, hit call, sorted by " +
37
+ "AC50 ascending — alongside totalAssays, activeAssays and activeHitRate, which always describe the full tested panel rather than the returned slice. " +
38
+ "'hazard' — ToxValDB / ToxRefDB IN-VIVO hazard: the points of departure a safety evaluation rests on. Returns dose-response rows (toxvalType such as NOAEL/LOAEL/LD50/BMD, " +
39
+ "with value, units, study type and duration, species, exposure route, effect), genotoxicity assay summaries, and cancer classifications. " +
40
+ "'chemical' — identity and physicochemical property profile from the CompTox Chemicals Dashboard, and the way to obtain the DTXSID the other datasets key off. Returns " +
41
+ "DTXSID, CASRN, SMILES, InChIKey, formula and mass, plus (unless includeProperties is false) experimental and predicted property summaries — logP, water solubility, " +
42
+ "vapor pressure, melting/boiling point, bioconcentration factor, Henry's law constant. " +
43
+ "'exposure' — the exposure half of a risk assessment (e.g. pairing a ToxCast AC50 against predicted exposure for a bioactivity-exposure ratio). Returns SEEM predictions " +
44
+ "(production volume and per-pathway exposure probabilities — dietary, residential, pesticide, industrial; NOT a daily-intake dose), HTTK toxicokinetic parameters, " +
45
+ "reported functional-use categories, and consumer-product composition."),
46
+ query: z
47
+ .string()
48
+ .describe("Chemical identifier (all datasets). A DTXSID (e.g. 'DTXSID7020182') is used directly with no lookup; a CASRN (e.g. '80-05-7'), a chemical name (e.g. 'bisphenol A'), " +
49
+ "or — for dataset 'chemical' — an InChIKey is resolved by EXACT match, so a non-canonical or misspelled name yields found: false."),
50
+ dataType: z
51
+ .enum(["toxval", "genetox", "cancer", "seem", "httk", "functional-use", "product-data", "all"])
52
+ .optional()
53
+ .describe("Which sections to fetch — datasets 'hazard' and 'exposure' only; ignored by 'toxcast' and 'chemical', which each return one dataset. Default 'all'. " +
54
+ "For dataset 'hazard': 'toxval' (ToxValDB dose-response values — NOAELs, LOAELs, LD50s, BMDs), 'genetox' (genotoxicity assay summaries), 'cancer' (cancer " +
55
+ "classifications — IARC/EPA/NTP and similar), or 'all'. " +
56
+ "For dataset 'exposure': 'seem' (SEEM exposure-pathway predictions and production volume), 'httk' (high-throughput toxicokinetic parameters — clearance, Css, protein " +
57
+ "binding, …), 'functional-use' (reported chemical use categories), 'product-data' (consumer-product composition), or 'all'. " +
58
+ "'all' fetches every section of that dataset concurrently."),
59
+ activeOnly: z
60
+ .boolean()
61
+ .optional()
62
+ .describe("Dataset 'toxcast' only. Default true — return only hit (active) assays. Set false for the whole tested panel including inactives, needed to judge selectivity or a low hit rate."),
63
+ includeProperties: z
64
+ .boolean()
65
+ .optional()
66
+ .describe("Dataset 'chemical' only. Default true — also fetch the experimental/predicted property summaries. Set false to skip that second call when only identity and mass are needed."),
67
+ limit: z
68
+ .number()
69
+ .int()
70
+ .min(1)
71
+ .max(200)
72
+ .optional()
73
+ .describe("Max rows returned; ignored by dataset 'chemical'. Defaults and ceilings differ per dataset: 'toxcast' default 50, max 200, applied after the activeOnly filter; " +
74
+ "'hazard' default 30, max 100, applied to toxval and genetox only — cancer ALWAYS returns every row; 'exposure' default 25, max 100, applied to functional-use and " +
75
+ "product-data only — seem and httk are ALWAYS returned whole."),
76
+ })
77
+ .refine((d) => (d.dataset !== "toxcast" && d.dataset !== "chemical") || d.dataType === undefined || d.dataType === "all", {
78
+ message: "dataType applies only to dataset 'hazard' (toxval | genetox | cancer) and dataset 'exposure' (seem | httk | functional-use | product-data) — " +
79
+ "'toxcast' and 'chemical' each return one dataset. Select the dataset that owns the sections you want.",
80
+ path: ["dataType"],
81
+ })
82
+ .refine((d) => d.dataset !== "hazard" || d.dataType === undefined || isHazardDataType(d.dataType), {
83
+ message: "dataType must be 'toxval', 'genetox', 'cancer', or 'all' when dataset is 'hazard' — " +
84
+ "seem / httk / functional-use / product-data belong to dataset 'exposure'.",
85
+ path: ["dataType"],
86
+ })
87
+ .refine((d) => d.dataset !== "exposure" || d.dataType === undefined || isExposureDataType(d.dataType), {
88
+ message: "dataType must be 'seem', 'httk', 'functional-use', 'product-data', or 'all' when dataset is 'exposure' — " +
89
+ "toxval / genetox / cancer belong to dataset 'hazard'.",
90
+ path: ["dataType"],
91
+ })
92
+ .refine((d) => d.dataset === "toxcast" || d.limit === undefined || d.limit <= 100, {
93
+ message: "limit is capped at 100 for datasets 'hazard' and 'exposure' (only 'toxcast' accepts up to 200)",
94
+ path: ["limit"],
95
+ });
96
+ export function createComptoxTool(deps) {
97
+ return defineTool({
98
+ id: "comptox",
99
+ description: "Query EPA's CompTox (CTX) chemical-safety databases — pick the dataset with `dataset`: 'toxcast' (in-vitro HTS bioactivity), 'hazard' (in-vivo hazard: NOAEL/LOAEL/LD50, " +
100
+ "genotoxicity, cancer classifications), 'chemical' (identity + physicochemical/ADMET properties), 'exposure' (SEEM exposure predictions, toxicokinetics, uses, products). " +
101
+ "See `dataset` for what each returns. Every dataset resolves the same `query` (DTXSID, CASRN, or exact chemical name) to a DTXSID first. " +
102
+ "Requires EPA_CCTE_API_KEY — a missing key fails the call terminally: do NOT retry, tell the user the key needs configuring and proceed without EPA data. " +
103
+ "found: false means the query did not resolve to a chemical — valid no-data, do not retry the same string; a present-but-empty section is likewise valid no-data. " +
104
+ "This is environmental/industrial-chemical safety data: for drug-like compounds prefer the PubChem or ChEMBL tools — dataset 'chemical' returns `detail.pubchemCid`, which " +
105
+ "bridges back to them.",
106
+ inputSchema,
107
+ execute: async (input) => {
108
+ const headers = getEpaCcteHeaders(deps.apiKey);
109
+ const resolved = await resolveDtxsid(input.query, headers);
110
+ // "No chemical found" is an expected outcome — a data variant, not an error.
111
+ if (!resolved)
112
+ return ok({ found: false, query: input.query });
113
+ const { dtxsid, preferredName, casrn } = resolved;
114
+ switch (input.dataset) {
115
+ case "toxcast": {
116
+ const bioactivity = await fetchToxcastBioactivity(dtxsid, headers, {
117
+ activeOnly: input.activeOnly ?? true,
118
+ limit: input.limit ?? 50,
119
+ });
120
+ return ok({ found: true, chemical: { dtxsid, preferredName, casrn, ...bioactivity } });
121
+ }
122
+ case "hazard": {
123
+ const data = await fetchCtxHazardData(dtxsid, headers, {
124
+ dataType: isHazardDataType(input.dataType) ? input.dataType : "all",
125
+ limit: input.limit ?? 30,
126
+ });
127
+ return ok({ found: true, dtxsid, preferredName, ...data });
128
+ }
129
+ case "chemical": {
130
+ const detail = await fetchCtxChemicalDetail(dtxsid, headers);
131
+ const result = { found: true, detail };
132
+ if (input.includeProperties ?? true) {
133
+ const properties = await fetchCtxChemicalProperties(dtxsid, headers);
134
+ if (properties)
135
+ result.properties = properties;
136
+ }
137
+ return ok(result);
138
+ }
139
+ case "exposure": {
140
+ const data = await fetchCtxExposureData(dtxsid, headers, {
141
+ dataType: isExposureDataType(input.dataType) ? input.dataType : "all",
142
+ limit: input.limit ?? 25,
143
+ });
144
+ return ok({ found: true, dtxsid, ...data });
145
+ }
146
+ }
147
+ },
148
+ });
149
+ }
@@ -8,13 +8,16 @@ import { buildViabilityCalls, derivedViability, getKoPhenotypeProfile, parsePhen
8
8
  export { buildViabilityCalls, derivedViability, parsePhenotypeResponse };
9
9
  export const getImpcKoProfileTool = defineTool({
10
10
  id: "get_impc_ko_profile",
11
- description: "Get mouse knockout phenotype profile from IMPC: significant phenotype terms, " +
12
- "affected organ systems, sex-dimorphism, and pre-weaning viability " +
13
- "(lethal / subviable / viable, with per-zygosity breakdown). " +
14
- "Use to assess loss-of-function consequences and target tractability. " +
15
- "Single human gene symbol per call.",
11
+ description: "Get the mouse-knockout phenotype profile for a gene from IMPC — the in-vivo loss-of-function evidence for target tractability and essentiality " +
12
+ "('what happens to PAX5 knockout mice?', 'is loss of CASP8 lethal?'). " +
13
+ "Takes a human gene symbol and maps it to the mouse ortholog itself. Returns a top-line pre-weaning viability call (lethal_pre_weaning | subviable | viable | null) with its " +
14
+ "per-zygosity breakdown, the significant MP phenotype terms ordered by best p-value, the affected organ systems ranked by phenotype count, and a sex-dimorphism flag. " +
15
+ "An all-empty profile (null mouseMarkerSymbol, null viability, no phenotype terms) means the gene has not been IMPC-phenotyped — common and valid. Do NOT retry on empty output.",
16
16
  inputSchema: z.object({
17
- geneSymbol: z.string().min(1).describe("Human gene symbol, e.g. 'BRCA1'."),
17
+ geneSymbol: z
18
+ .string()
19
+ .min(1)
20
+ .describe("HUMAN gene symbol, e.g. 'BRCA1' — one gene per call. The mouse ortholog is resolved internally; do not pass a mouse symbol or an MGI ID."),
18
21
  }),
19
22
  execute: async ({ geneSymbol }) => ok(await getKoPhenotypeProfile(geneSymbol)),
20
23
  });
@@ -10,19 +10,10 @@ export * from "./search-gene.js";
10
10
  export * from "./search-pathway.js";
11
11
  export * from "./lookup-go-term.js";
12
12
  export * from "./search-interactions.js";
13
- export * from "./search-pubmed.js";
14
- export * from "./get-article-details.js";
15
- export * from "./get-article-full-text.js";
16
- export * from "./search-compounds.js";
17
- export * from "./get-bioactivity.js";
18
- export * from "./search-targets.js";
19
- export * from "./get-mechanism.js";
20
- export * from "./get-drug-info.js";
21
- export * from "./search-pubchem-compound.js";
22
- export * from "./get-pubchem-cross-refs.js";
23
- export * from "./get-pubchem-assays.js";
24
- export * from "./search-opentargets.js";
25
- export * from "./get-target-safety.js";
13
+ export * from "./pubmed.js";
14
+ export * from "./chembl.js";
15
+ export * from "./pubchem.js";
16
+ export * from "./opentargets.js";
26
17
  export * from "./search-pharmgkb.js";
27
18
  export * from "./search-faers.js";
28
19
  export * from "./search-clinical-trials.js";
@@ -35,7 +26,4 @@ export * from "./search-gwas-catalog.js";
35
26
  export * from "./search-bgee-expression.js";
36
27
  export * from "./get-impc-ko-profile.js";
37
28
  export * from "./check-safety-panel.js";
38
- export * from "./search-toxcast.js";
39
- export * from "./search-ctx-hazard.js";
40
- export * from "./search-ctx-chemical.js";
41
- export * from "./search-ctx-exposure.js";
29
+ export * from "./comptox.js";
@@ -12,22 +12,13 @@ export * from "./search-pathway.js";
12
12
  export * from "./lookup-go-term.js";
13
13
  export * from "./search-interactions.js";
14
14
  // Literature
15
- export * from "./search-pubmed.js";
16
- export * from "./get-article-details.js";
17
- export * from "./get-article-full-text.js";
15
+ export * from "./pubmed.js";
18
16
  // ChEMBL
19
- export * from "./search-compounds.js";
20
- export * from "./get-bioactivity.js";
21
- export * from "./search-targets.js";
22
- export * from "./get-mechanism.js";
23
- export * from "./get-drug-info.js";
17
+ export * from "./chembl.js";
24
18
  // PubChem
25
- export * from "./search-pubchem-compound.js";
26
- export * from "./get-pubchem-cross-refs.js";
27
- export * from "./get-pubchem-assays.js";
19
+ export * from "./pubchem.js";
28
20
  // Translational medicine
29
- export * from "./search-opentargets.js";
30
- export * from "./get-target-safety.js";
21
+ export * from "./opentargets.js";
31
22
  export * from "./search-pharmgkb.js";
32
23
  export * from "./search-faers.js";
33
24
  export * from "./search-clinical-trials.js";
@@ -42,7 +33,4 @@ export * from "./search-bgee-expression.js";
42
33
  export * from "./get-impc-ko-profile.js";
43
34
  // Off-target liability / EPA CompTox
44
35
  export * from "./check-safety-panel.js";
45
- export * from "./search-toxcast.js";
46
- export * from "./search-ctx-hazard.js";
47
- export * from "./search-ctx-chemical.js";
48
- export * from "./search-ctx-exposure.js";
36
+ export * from "./comptox.js";
@@ -13,19 +13,24 @@ export interface BioToolKeys {
13
13
  readonly ncbi?: string;
14
14
  readonly github?: string;
15
15
  }
16
- /** The NCBI-backed literature tools, built from the shared key slice. */
16
+ /**
17
+ * The NCBI-backed literature tools, built from the shared key slice.
18
+ *
19
+ * `pubmed` is the consolidated literature tool (search / details / fulltext
20
+ * behind one `action`); `searchClinvar` remains its own single-endpoint tool.
21
+ */
17
22
  export declare function createNcbiTools(keys: BioToolKeys): {
18
- searchPubMed: Tool;
19
- getArticleDetails: Tool;
20
- getArticleFullText: Tool;
23
+ pubmed: Tool;
21
24
  searchClinvar: Tool;
22
25
  };
23
- /** The DrugBank / DisGeNET / EPA CompTox tools, built from the key slice. */
26
+ /**
27
+ * The DrugBank / DisGeNET / EPA CompTox tools, built from the key slice.
28
+ *
29
+ * `comptox` is the consolidated EPA CTX tool (toxcast / hazard / chemical /
30
+ * exposure behind one `dataset`), built over the shared EPA_CCTE key.
31
+ */
24
32
  export declare function createChemDbTools(keys: BioToolKeys): {
25
33
  searchDrugbank: Tool;
26
34
  searchDisgenet: Tool;
27
- searchToxcast: Tool;
28
- searchCtxHazard: Tool;
29
- searchCtxChemical: Tool;
30
- searchCtxExposure: Tool;
35
+ comptox: Tool;
31
36
  };
@@ -1,30 +1,30 @@
1
- import { createSearchPubMedTool } from "./search-pubmed.js";
2
- import { createGetArticleDetailsTool } from "./get-article-details.js";
3
- import { createGetArticleFullTextTool } from "./get-article-full-text.js";
1
+ import { createPubMedTool } from "./pubmed.js";
4
2
  import { createSearchClinvarTool } from "./search-clinvar.js";
5
3
  import { createSearchDrugbankTool } from "./search-drugbank.js";
6
4
  import { createSearchDisgenetTool } from "./search-disgenet.js";
7
- import { createSearchToxcastTool } from "./search-toxcast.js";
8
- import { createSearchCtxHazardTool } from "./search-ctx-hazard.js";
9
- import { createSearchCtxChemicalTool } from "./search-ctx-chemical.js";
10
- import { createSearchCtxExposureTool } from "./search-ctx-exposure.js";
11
- /** The NCBI-backed literature tools, built from the shared key slice. */
5
+ import { createComptoxTool } from "./comptox.js";
6
+ /**
7
+ * The NCBI-backed literature tools, built from the shared key slice.
8
+ *
9
+ * `pubmed` is the consolidated literature tool (search / details / fulltext
10
+ * behind one `action`); `searchClinvar` remains its own single-endpoint tool.
11
+ */
12
12
  export function createNcbiTools(keys) {
13
13
  return {
14
- searchPubMed: createSearchPubMedTool({ ncbiApiKey: keys.ncbi }),
15
- getArticleDetails: createGetArticleDetailsTool({ ncbiApiKey: keys.ncbi }),
16
- getArticleFullText: createGetArticleFullTextTool({ ncbiApiKey: keys.ncbi }),
14
+ pubmed: createPubMedTool({ ncbiApiKey: keys.ncbi }),
17
15
  searchClinvar: createSearchClinvarTool({ ncbiApiKey: keys.ncbi }),
18
16
  };
19
17
  }
20
- /** The DrugBank / DisGeNET / EPA CompTox tools, built from the key slice. */
18
+ /**
19
+ * The DrugBank / DisGeNET / EPA CompTox tools, built from the key slice.
20
+ *
21
+ * `comptox` is the consolidated EPA CTX tool (toxcast / hazard / chemical /
22
+ * exposure behind one `dataset`), built over the shared EPA_CCTE key.
23
+ */
21
24
  export function createChemDbTools(keys) {
22
25
  return {
23
26
  searchDrugbank: createSearchDrugbankTool({ apiKey: keys.drugbank }),
24
27
  searchDisgenet: createSearchDisgenetTool({ apiKey: keys.disgenet }),
25
- searchToxcast: createSearchToxcastTool({ apiKey: keys.epaCcte }),
26
- searchCtxHazard: createSearchCtxHazardTool({ apiKey: keys.epaCcte }),
27
- searchCtxChemical: createSearchCtxChemicalTool({ apiKey: keys.epaCcte }),
28
- searchCtxExposure: createSearchCtxExposureTool({ apiKey: keys.epaCcte }),
28
+ comptox: createComptoxTool({ apiKey: keys.epaCcte }),
29
29
  };
30
30
  }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * opentargets — one tool over the Open Targets Platform: target-disease
3
+ * association evidence (from a gene or from a disease) and the target's
4
+ * curated safety liabilities.
5
+ *
6
+ * The input is a flat object with an `action` discriminator — not a
7
+ * `z.discriminatedUnion`, which `defineTool` rejects (model tool calling needs
8
+ * a top-level `"type":"object"`). Each action's identifier is optional in the
9
+ * schema and enforced by `.refine`, so a call that omits it fails at the loop
10
+ * boundary with an actionable message instead of reaching the API.
11
+ *
12
+ * The identifier fields are deliberately separate (`ensemblId` / `efoId`)
13
+ * rather than one polymorphic `query`: Open Targets accepts ONLY an Ensembl
14
+ * gene ID or an EFO disease ID, and a gene symbol silently returns an empty
15
+ * result rather than an error. Naming the two identifiers apart is what makes
16
+ * that contract visible at the call site.
17
+ */
18
+ import { type Association, type SafetyLiability, type TargetInfo } from "../lib/opentargets-client.js";
19
+ type OpenTargetsOutput = {
20
+ targetInfo: TargetInfo | null;
21
+ associations: Association[];
22
+ } | {
23
+ associations: Association[];
24
+ } | {
25
+ found: false;
26
+ ensemblId: string;
27
+ } | {
28
+ found: true;
29
+ targetSymbol: string;
30
+ safetyLiabilities: SafetyLiability[];
31
+ };
32
+ export declare const openTargetsTool: import("../define-tool.js").Tool<{
33
+ action: "target" | "disease" | "safety";
34
+ ensemblId?: string | undefined;
35
+ efoId?: string | undefined;
36
+ limit?: number | undefined;
37
+ }, OpenTargetsOutput>;
38
+ export {};
@@ -0,0 +1,89 @@
1
+ /**
2
+ * opentargets — one tool over the Open Targets Platform: target-disease
3
+ * association evidence (from a gene or from a disease) and the target's
4
+ * curated safety liabilities.
5
+ *
6
+ * The input is a flat object with an `action` discriminator — not a
7
+ * `z.discriminatedUnion`, which `defineTool` rejects (model tool calling needs
8
+ * a top-level `"type":"object"`). Each action's identifier is optional in the
9
+ * schema and enforced by `.refine`, so a call that omits it fails at the loop
10
+ * boundary with an actionable message instead of reaching the API.
11
+ *
12
+ * The identifier fields are deliberately separate (`ensemblId` / `efoId`)
13
+ * rather than one polymorphic `query`: Open Targets accepts ONLY an Ensembl
14
+ * gene ID or an EFO disease ID, and a gene symbol silently returns an empty
15
+ * result rather than an error. Naming the two identifiers apart is what makes
16
+ * that contract visible at the call site.
17
+ */
18
+ import { ok } from "neverthrow";
19
+ import { z } from "zod";
20
+ import { defineTool } from "../define-tool.js";
21
+ import { getTargetSafetyLiabilities, searchDiseaseAssociations, searchTargetAssociations, } from "../lib/opentargets-client.js";
22
+ const inputSchema = z
23
+ .object({
24
+ action: z
25
+ .enum(["target", "disease", "safety"])
26
+ .describe("'target' (needs ensemblId) — the diseases associated with the gene: targetInfo (approvedSymbol, approvedName, " +
27
+ "tractability across small molecule / antibody / other modalities) plus associations[], each with an overall score and " +
28
+ "its per-datatype breakdown — genetic association, known drug, literature, animal model, somatic mutation. " +
29
+ "'disease' (needs efoId) — the targets ranked for that disease; each association carries targetId/targetSymbol/targetName " +
30
+ "and the same score breakdown. " +
31
+ "'safety' (needs ensemblId) — the target's curated safety liabilities: found: true with targetSymbol and " +
32
+ "safetyLiabilities[] { event, biosamples (affected tissues), effects (direction of effect), source }. This is TARGET-level, " +
33
+ "mechanism-based safety — for the post-market adverse events of a specific marketed DRUG use search_faers instead."),
34
+ ensemblId: z.string().optional().describe("Ensembl gene ID, e.g. ENSG00000141510 (TP53). Required for action 'target' and 'safety'."),
35
+ efoId: z.string().optional().describe("EFO disease ID, e.g. EFO_0000311. Required for action 'disease'."),
36
+ limit: z
37
+ .number()
38
+ .int()
39
+ .min(1)
40
+ .max(100)
41
+ .default(25)
42
+ .optional()
43
+ .describe("Max associations to return (default 25, max 100), ordered by descending association score. Ignored by 'safety'."),
44
+ })
45
+ .refine((d) => d.action === "disease" || (d.ensemblId !== undefined && d.ensemblId.trim().length > 0), {
46
+ message: "ensemblId is required when action is 'target' or 'safety' — Open Targets accepts only Ensembl gene IDs (ENSG…), " +
47
+ "never gene symbols. Resolve the symbol to an ENSG id with search_gene, then retry.",
48
+ path: ["ensemblId"],
49
+ })
50
+ .refine((d) => d.action !== "disease" || (d.efoId !== undefined && d.efoId.trim().length > 0), {
51
+ message: "efoId is required when action is 'disease' — Open Targets accepts only EFO disease IDs (e.g. EFO_0000311), never free-text disease names.",
52
+ path: ["efoId"],
53
+ });
54
+ export const openTargetsTool = defineTool({
55
+ id: "opentargets",
56
+ description: "Query the Open Targets Platform — the preferred FIRST call for target assessment, since one 'target' query yields the " +
57
+ "genetic evidence, tractability, and drug landscape together, and 'safety' adds the mechanism-based liabilities. See the " +
58
+ "action parameter for what each mode needs and returns.\n" +
59
+ "IDENTIFIERS ONLY: it accepts an Ensembl gene ID (action 'target'/'safety') or an EFO disease ID (action 'disease'). A bare " +
60
+ "gene symbol or a free-text disease name silently returns an EMPTY result rather than an error — resolve a symbol to its ENSG " +
61
+ "id with search_gene first.\n" +
62
+ "NO-DATA IS FINAL — do not retry the same id. Empty associations mean no evidence (or an unresolvable id); safety found: false " +
63
+ "means no Open Targets record for that Ensembl id (usually a wrong id); safety found: true with an empty safetyLiabilities " +
64
+ "array means no curated liability.",
65
+ inputSchema,
66
+ execute: async (input) => {
67
+ switch (input.action) {
68
+ case "target": {
69
+ const targetInfo = await searchTargetAssociations(input.ensemblId, input.limit);
70
+ return ok({ targetInfo, associations: targetInfo?.associations ?? [] });
71
+ }
72
+ case "disease": {
73
+ const associations = await searchDiseaseAssociations(input.efoId, input.limit);
74
+ return ok({ associations });
75
+ }
76
+ case "safety": {
77
+ const result = await getTargetSafetyLiabilities(input.ensemblId);
78
+ // "Target not found" is an expected outcome — a data variant, not an error.
79
+ if (!result)
80
+ return ok({ found: false, ensemblId: input.ensemblId });
81
+ return ok({
82
+ found: true,
83
+ targetSymbol: result.targetSymbol,
84
+ safetyLiabilities: result.safetyLiabilities,
85
+ });
86
+ }
87
+ }
88
+ },
89
+ });
@@ -0,0 +1,29 @@
1
+ /**
2
+ * pubchem — the single PubChem tool: compound resolution, registry
3
+ * cross-references, and bioassay screening summaries behind one `action`
4
+ * discriminator.
5
+ *
6
+ * The input is a flat object with an `action` discriminator — not a
7
+ * `z.discriminatedUnion`, which `defineTool` rejects (the model needs a
8
+ * top-level `"type":"object"`). Each action's own parameters are optional and
9
+ * guarded by `.refine`, so a call that omits one (a 'crossrefs' with no `cid`)
10
+ * fails at the loop boundary with a message naming the missing field, rather
11
+ * than reaching an upstream request that cannot be built.
12
+ */
13
+ import { type PubChemAssay, type PubChemCompound, type PubChemCrossRef } from "../lib/pubchem-ops.js";
14
+ type PubchemOutput = {
15
+ results: PubChemCompound[];
16
+ } | {
17
+ crossRefs: PubChemCrossRef[];
18
+ } | {
19
+ assays: PubChemAssay[];
20
+ };
21
+ export declare const pubchemTool: import("../define-tool.js").Tool<{
22
+ action: "compound" | "crossrefs" | "assays";
23
+ query?: string | undefined;
24
+ searchBy?: "name" | "smiles" | "inchikey" | "inchi" | "cid" | undefined;
25
+ cid?: number | undefined;
26
+ activeOnly?: boolean | undefined;
27
+ limit?: number | undefined;
28
+ }, PubchemOutput>;
29
+ export {};
@@ -0,0 +1,92 @@
1
+ /**
2
+ * pubchem — the single PubChem tool: compound resolution, registry
3
+ * cross-references, and bioassay screening summaries behind one `action`
4
+ * discriminator.
5
+ *
6
+ * The input is a flat object with an `action` discriminator — not a
7
+ * `z.discriminatedUnion`, which `defineTool` rejects (the model needs a
8
+ * top-level `"type":"object"`). Each action's own parameters are optional and
9
+ * guarded by `.refine`, so a call that omits one (a 'crossrefs' with no `cid`)
10
+ * fails at the loop boundary with a message naming the missing field, rather
11
+ * than reaching an upstream request that cannot be built.
12
+ */
13
+ import { ok } from "neverthrow";
14
+ import { z } from "zod";
15
+ import { defineTool } from "../define-tool.js";
16
+ import { fetchPubchemAssays, fetchPubchemCompounds, fetchPubchemCrossRefs, } from "../lib/pubchem-ops.js";
17
+ const inputSchema = z
18
+ .object({
19
+ action: z
20
+ .enum(["compound", "crossrefs", "assays"])
21
+ .describe("Which PubChem lookup to run. " +
22
+ "'compound' — resolve `query` (in the namespace named by `searchBy`) to a compound. Returns results[]: identity (cid, canonicalSmiles, inchi, inchiKey, iupacName, " +
23
+ "molecularFormula) and computed properties (molecularWeight, xlogp, tpsa, hbondDonorCount, hbondAcceptorCount, rotatableBondCount, complexity). " +
24
+ "'crossrefs' — external registry ids for a `cid`. Returns crossRefs[]: a flat list of { source, id } pairs across ChEMBL, DrugBank, KEGG, PDB and many other " +
25
+ "registries; filter by `source` for the one you want. This is the bridge out of PubChem. " +
26
+ "'assays' — bioassay screening summaries for a `cid`; answers 'has this compound ever been screened, and against what?'. Returns assays[]: aid, assayName, targetName, " +
27
+ "activityOutcome (Active / Inactive / Inconclusive / Unspecified), activityValue. Broad HTS screening coverage, NOT curated potency — for numbers you intend to quote " +
28
+ "(IC50/Ki), prefer chembl action='bioactivity'."),
29
+ query: z
30
+ .string()
31
+ .min(1)
32
+ .optional()
33
+ .describe("Required for action 'compound'. The identifier itself; its form must match `searchBy` (a name, a SMILES string, an InChI, an InChIKey, or a numeric CID)."),
34
+ searchBy: z
35
+ .enum(["name", "smiles", "inchi", "inchikey", "cid"])
36
+ .optional()
37
+ .describe("Required for action 'compound'. Which PubChem namespace `query` is expressed in. All are exact-match lookups except 'name', which tolerates synonyms and trade names."),
38
+ cid: z
39
+ .number()
40
+ .int()
41
+ .positive()
42
+ .optional()
43
+ .describe("Required for actions 'crossrefs' and 'assays'. PubChem Compound ID as an integer (e.g. 2244 for aspirin), from a prior action 'compound' call."),
44
+ activeOnly: z
45
+ .boolean()
46
+ .optional()
47
+ .describe("Action 'assays' only. Default false (all outcomes). Set true to keep only rows whose activityOutcome is 'Active'."),
48
+ limit: z
49
+ .number()
50
+ .int()
51
+ .min(1)
52
+ .max(500)
53
+ .optional()
54
+ .describe("Action 'assays' only. Max assay records to return (default 50, max 500). Applied after the activeOnly filter."),
55
+ })
56
+ .refine((d) => d.action !== "compound" || (d.query !== undefined && d.query.trim().length > 0), {
57
+ message: "query is required when action is 'compound' — the identifier to resolve (a name, SMILES, InChI, InChIKey, or CID)",
58
+ path: ["query"],
59
+ })
60
+ .refine((d) => d.action !== "compound" || d.searchBy !== undefined, {
61
+ message: "searchBy is required when action is 'compound' — name | smiles | inchi | inchikey | cid, matching the form of `query`",
62
+ path: ["searchBy"],
63
+ })
64
+ .refine((d) => (d.action !== "crossrefs" && d.action !== "assays") || d.cid !== undefined, {
65
+ message: "cid is required when action is 'crossrefs' or 'assays' — resolve the compound with action 'compound' first and copy its cid",
66
+ path: ["cid"],
67
+ });
68
+ export const pubchemTool = defineTool({
69
+ id: "pubchem",
70
+ description: "Query PubChem (110M+ compounds) — pick the lookup with `action`: 'compound' (resolve an identifier), 'crossrefs' (external registry ids for a CID), 'assays' (bioassay screening " +
71
+ "summaries for a CID). See `action` for what each returns. " +
72
+ "Reach for PubChem when ChEMBL misses the compound — it covers metabolites, vendor chemicals, food additives and environmental compounds that ChEMBL does not. " +
73
+ "PubChem carries no curated mechanism or potency data, so resolve broadly HERE and then bridge OUT: action 'compound' to get the cid, action 'crossrefs' to take its ChEMBL ID, " +
74
+ "then chembl action='mechanism' / 'bioactivity' there for curated activity data. " +
75
+ "An empty results / crossRefs / assays array is valid no-data (no match; the CID is in no external registry; the compound was never screened) — do not retry.",
76
+ inputSchema,
77
+ execute: async (input) => {
78
+ switch (input.action) {
79
+ case "compound":
80
+ return ok({ results: await fetchPubchemCompounds(input.query, input.searchBy) });
81
+ case "crossrefs":
82
+ return ok({ crossRefs: await fetchPubchemCrossRefs(input.cid) });
83
+ case "assays":
84
+ return ok({
85
+ assays: await fetchPubchemAssays(input.cid, {
86
+ activeOnly: input.activeOnly ?? false,
87
+ limit: input.limit ?? 50,
88
+ }),
89
+ });
90
+ }
91
+ },
92
+ });
@@ -0,0 +1,51 @@
1
+ /**
2
+ * pubmed — one tool over PubMed and PubMed Central (NCBI E-utilities):
3
+ * search the literature, fetch article metadata, and read the full text of an
4
+ * open-access article.
5
+ *
6
+ * Dependency-bearing: the optional NCBI API key is captured by the factory.
7
+ *
8
+ * The input is a flat object with an `action` discriminator — not a
9
+ * `z.discriminatedUnion`, which `defineTool` rejects (model tool calling needs
10
+ * a top-level `"type":"object"`). Each action's required argument is optional
11
+ * in the schema and enforced by `.refine`, so a malformed call (e.g. 'fulltext'
12
+ * with no pmcId) fails at the loop boundary and surfaces as an `is_error` tool
13
+ * result instead of reaching NCBI.
14
+ *
15
+ * The three actions form one retrieval chain — search yields PMIDs, details
16
+ * turns PMIDs into abstracts + the PMC id, fulltext turns a PMC id into the
17
+ * article body — which is why they share a tool: the identifier for each step
18
+ * is read off the previous step's result.
19
+ */
20
+ import type { ArticleSection } from "../lib/ncbi-utils.js";
21
+ import { type ArticleDetail, type PubMedSummary } from "../lib/pubmed-client.js";
22
+ type PubMedOutput = {
23
+ totalFound: number;
24
+ results: PubMedSummary[];
25
+ } | {
26
+ articles: ArticleDetail[];
27
+ notFound: string[];
28
+ } | {
29
+ pmcId: string;
30
+ available: false;
31
+ } | {
32
+ pmcId: string;
33
+ available: true;
34
+ fullText: string;
35
+ sections: ArticleSection[];
36
+ };
37
+ export declare function createPubMedTool(deps: {
38
+ ncbiApiKey?: string;
39
+ }): import("../define-tool.js").Tool<{
40
+ action: "search" | "details" | "fulltext";
41
+ query?: string | undefined;
42
+ maxResults?: number | undefined;
43
+ sort?: "date" | "relevance" | undefined;
44
+ dateRange?: {
45
+ from: string;
46
+ to: string;
47
+ } | undefined;
48
+ pmids?: string[] | undefined;
49
+ pmcId?: string | undefined;
50
+ }, PubMedOutput>;
51
+ export {};