@inflexa-ai/harness 0.42.0 → 0.42.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/conversation-agent.js +5 -1
- package/dist/agents/sandbox/drug-repurposing-agent.js +1 -0
- package/dist/agents/sandbox/shared.js +3 -1
- package/dist/agents/sandbox/types.d.ts +1 -1
- package/dist/contracts/chat-frame.d.ts +2 -1
- package/dist/contracts/chat-frame.js +9 -2
- package/dist/execution/run-event-parts.d.ts +15 -7
- package/dist/execution/run-event-parts.js +62 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.js +7 -0
- package/dist/tools/bio/index.d.ts +2 -0
- package/dist/tools/bio/index.js +3 -0
- package/dist/tools/bio/keys.d.ts +11 -0
- package/dist/tools/bio/keys.js +15 -0
- package/dist/tools/bio/search-clinical-trials.d.ts +1 -1
- package/dist/tools/bio/search-patents.d.ts +46 -0
- package/dist/tools/bio/search-patents.js +134 -0
- package/dist/tools/bio/search-regulatory-approvals.d.ts +49 -0
- package/dist/tools/bio/search-regulatory-approvals.js +158 -0
- package/dist/tools/lib/amass-client.d.ts +586 -0
- package/dist/tools/lib/amass-client.js +492 -0
- package/dist/tools/lib/amass-config.d.ts +2 -0
- package/dist/tools/lib/amass-config.js +10 -0
- package/package.json +1 -1
|
@@ -29,7 +29,7 @@ import { conversationPrompt } from "../prompts/conversation.js";
|
|
|
29
29
|
import { composeSystemPrompt } from "./system-prompt.js";
|
|
30
30
|
// Bio-lookup leaf tools (pure — no dependencies).
|
|
31
31
|
import { searchGeneTool, searchProteinTool, lookupAnnotationTool, searchInteractionsTool, alphafoldPredictionTool, chemblTool, pubchemTool, openTargetsTool, searchFaersTool, searchClinicalTrialsTool, searchGeoDatasetsTool, genePreclinicalProfileTool, targetSafetyTool, } from "../tools/bio/index.js";
|
|
32
|
-
import { createNcbiTools, createChemDbTools } from "../tools/bio/keys.js";
|
|
32
|
+
import { createAmassTools, createNcbiTools, createChemDbTools } from "../tools/bio/keys.js";
|
|
33
33
|
// Dependency-bearing tool factories.
|
|
34
34
|
import { createGeneratePlanTool, createInspectRunTool, createInspectDataProfileTool, createGenerateAnalogyReportTool, createSearchSemanticScholarTool, searchArxivTool, } from "../tools/research/index.js";
|
|
35
35
|
import { createEditFileTool, createFileStatTool, createGrepTool, createListFilesTool, createReadFileTool, createSessionWorkspaceMutator, createShowPlanTool, createWorkspaceSearchTool, createWriteFileTool, showFileTool, } from "../tools/workspace/index.js";
|
|
@@ -52,6 +52,7 @@ export function createConversationAgent(deps) {
|
|
|
52
52
|
const workingMemory = createWorkingMemory(pool);
|
|
53
53
|
const ncbi = createNcbiTools(bioKeys);
|
|
54
54
|
const chemDb = createChemDbTools(bioKeys, { ...(deps.logger ? { logger: deps.logger } : {}) });
|
|
55
|
+
const amass = createAmassTools(bioKeys);
|
|
55
56
|
// The write path of this agent: coordinates resolve per call from the
|
|
56
57
|
// session's analysis scope, the write prefix is the analysis root, and
|
|
57
58
|
// every successful write emits a `write-file` provenance session event.
|
|
@@ -92,6 +93,9 @@ export function createConversationAgent(deps) {
|
|
|
92
93
|
chemDb.drugGeneInteractions,
|
|
93
94
|
searchFaersTool,
|
|
94
95
|
searchClinicalTrialsTool,
|
|
96
|
+
// FDA and EMA authorizations with their label text, and patents (Amass; keyed).
|
|
97
|
+
amass.regulatoryApprovals,
|
|
98
|
+
amass.patents,
|
|
95
99
|
searchGeoDatasetsTool,
|
|
96
100
|
// Preclinical — baseline expression + mouse knockout in one call.
|
|
97
101
|
genePreclinicalProfileTool,
|
|
@@ -28,7 +28,7 @@ import { createInspectRunTool } from "../../tools/research/inspect-run.js";
|
|
|
28
28
|
import { createInspectDataProfileTool } from "../../tools/research/inspect-data-profile.js";
|
|
29
29
|
// Bio leaf tools.
|
|
30
30
|
import { chemblTool, genePreclinicalProfileTool, lookupAnnotationTool, openTargetsTool, pubchemTool, searchClinicalTrialsTool, searchFaersTool, searchGeneTool, searchGeoDatasetsTool, searchInteractionsTool, targetSafetyTool, } from "../../tools/bio/index.js";
|
|
31
|
-
import { createNcbiTools, createChemDbTools } from "../../tools/bio/keys.js";
|
|
31
|
+
import { createAmassTools, createNcbiTools, createChemDbTools } from "../../tools/bio/keys.js";
|
|
32
32
|
// Workspace read surface.
|
|
33
33
|
import { createFileStatTool, createGrepTool, createListFilesTool, createReadFileTool, createWorkspaceSearchTool } from "../../tools/workspace/index.js";
|
|
34
34
|
import { createScanInputsTool } from "../../tools/workspace/scan-inputs.js";
|
|
@@ -58,6 +58,7 @@ export const BASE_SANDBOX_TOOLS = ["listAvailablePackages", "listAvailableRefs",
|
|
|
58
58
|
function resolveSandboxTools(deps, tools) {
|
|
59
59
|
const ncbi = createNcbiTools(deps.bioKeys);
|
|
60
60
|
const chemDb = createChemDbTools(deps.bioKeys, { ...(deps.logger ? { logger: deps.logger } : {}) });
|
|
61
|
+
const amass = createAmassTools(deps.bioKeys);
|
|
61
62
|
if (tools.includes("resolve_citation") && deps.citationResolver === undefined) {
|
|
62
63
|
throw new Error('createSandboxAgent: SandboxToolName "resolve_citation" requires a CitationResolver dependency.');
|
|
63
64
|
}
|
|
@@ -97,6 +98,7 @@ function resolveSandboxTools(deps, tools) {
|
|
|
97
98
|
searchFaers: searchFaersTool,
|
|
98
99
|
searchClinicalTrials: searchClinicalTrialsTool,
|
|
99
100
|
searchGeoDatasets: searchGeoDatasetsTool,
|
|
101
|
+
searchRegulatoryApprovals: amass.regulatoryApprovals,
|
|
100
102
|
targetSafety: targetSafetyTool,
|
|
101
103
|
comptox: chemDb.comptox,
|
|
102
104
|
};
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* in the tool's `perSource` report; the rest of the call still answers.
|
|
13
13
|
*/
|
|
14
14
|
/** Closed allowlist of tools any sandbox agent may declare. */
|
|
15
|
-
export type SandboxToolName = "listAvailablePackages" | "listAvailableRefs" | "scanInputs" | "resolveLibraryId" | "queryDocs" | "inspectRun" | "pubmed" | "resolve_citation" | "searchGene" | "lookupAnnotation" | "searchInteractions" | "chembl" | "pubchem" | "opentargets" | "geneDiseaseEvidence" | "drugGeneInteractions" | "genePreclinicalProfile" | "searchFaers" | "searchClinicalTrials" | "searchGeoDatasets" | "targetSafety" | "comptox";
|
|
15
|
+
export type SandboxToolName = "listAvailablePackages" | "listAvailableRefs" | "scanInputs" | "resolveLibraryId" | "queryDocs" | "inspectRun" | "pubmed" | "resolve_citation" | "searchGene" | "lookupAnnotation" | "searchInteractions" | "chembl" | "pubchem" | "opentargets" | "geneDiseaseEvidence" | "drugGeneInteractions" | "genePreclinicalProfile" | "searchFaers" | "searchClinicalTrials" | "searchGeoDatasets" | "searchRegulatoryApprovals" | "targetSafety" | "comptox";
|
|
16
16
|
/** Planner-facing metadata + tool allowlist for one sandbox agent. */
|
|
17
17
|
export interface AgentMeta {
|
|
18
18
|
/** Stable agent id — also the key in the catalog and the `AgentDefinition.id`. */
|
|
@@ -24,7 +24,8 @@ export type ChatPartCheck = {
|
|
|
24
24
|
export declare function toChatFrame(event: EmitEvent | ChatStreamEvent | ChatDataPart, fallbackSource: EmitEvent["source"]): ChatFrame | null;
|
|
25
25
|
/**
|
|
26
26
|
* Check a part frame against the schema of its type, one time, where it arrives. The schema drops a field that it
|
|
27
|
-
* does not know. A type that the registry does not know passes unchanged, because a newer emitter can send it.
|
|
27
|
+
* does not know. A type that the registry does not know passes unchanged, because a newer emitter can send it. The
|
|
28
|
+
* source is checked for each type, because {@link isRootFrame} reads its call path.
|
|
28
29
|
*/
|
|
29
30
|
export declare function checkChatPart(frame: ChatPartFrame): ChatPartCheck;
|
|
30
31
|
/** A frame of the root agent of a chat turn, whose call path is `[agent.id]`. A longer path is a sub-agent. */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// The one translation path of a chat stream. Browser-safe: each import from outside `contracts/` is a type import.
|
|
2
2
|
import { isReconciling, PART_REGISTRY } from "./part-registry.js";
|
|
3
|
+
import { EventSourceSchema } from "./schemas/chat-events.js";
|
|
3
4
|
import { ChatPartSchema } from "./schemas/chat-parts.js";
|
|
4
5
|
function toSource(source) {
|
|
5
6
|
return { agentId: source.agentId, callPath: [...source.callPath] };
|
|
@@ -44,12 +45,18 @@ export function toChatFrame(event, fallbackSource) {
|
|
|
44
45
|
}
|
|
45
46
|
/**
|
|
46
47
|
* Check a part frame against the schema of its type, one time, where it arrives. The schema drops a field that it
|
|
47
|
-
* does not know. A type that the registry does not know passes unchanged, because a newer emitter can send it.
|
|
48
|
+
* does not know. A type that the registry does not know passes unchanged, because a newer emitter can send it. The
|
|
49
|
+
* source is checked for each type, because {@link isRootFrame} reads its call path.
|
|
48
50
|
*/
|
|
49
51
|
export function checkChatPart(frame) {
|
|
52
|
+
const { source, ...part } = frame;
|
|
53
|
+
if (source !== undefined) {
|
|
54
|
+
const checkedSource = EventSourceSchema.safeParse(source);
|
|
55
|
+
if (!checkedSource.success)
|
|
56
|
+
return { ok: false, error: checkedSource.error.message };
|
|
57
|
+
}
|
|
50
58
|
if (!Object.hasOwn(PART_REGISTRY, frame.type))
|
|
51
59
|
return { ok: true, frame };
|
|
52
|
-
const { source, ...part } = frame;
|
|
53
60
|
const parsed = ChatPartSchema.safeParse(part);
|
|
54
61
|
if (!parsed.success)
|
|
55
62
|
return { ok: false, error: parsed.error.message };
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
3
|
-
* stream value to the chat data-part contract,
|
|
4
|
-
* fold
|
|
2
|
+
* The rules of the run-event read side that need no database and no durability
|
|
3
|
+
* engine: narrowing a raw durable stream value to the chat data-part contract,
|
|
4
|
+
* and the latest-wins reconciling fold, as a batch for `createRunEventStream`
|
|
5
|
+
* and live for an embedder that reads the run streams itself.
|
|
5
6
|
*
|
|
6
|
-
* They live apart from the reader
|
|
7
|
-
*
|
|
8
|
-
* durability engine, no clock — and keeping them here is what lets that be
|
|
9
|
-
* proven directly instead of inferred from a driven subscription.
|
|
7
|
+
* They live apart from the reader, thus a test drives them directly instead of
|
|
8
|
+
* through a subscription.
|
|
10
9
|
*/
|
|
11
10
|
import type { ChatPart } from "../contracts/chat-parts.js";
|
|
12
11
|
/**
|
|
@@ -42,3 +41,12 @@ export declare function parseRunEventPart(value: unknown): ChatPart | null;
|
|
|
42
41
|
* the parts that are delivered is the order they were written.
|
|
43
42
|
*/
|
|
44
43
|
export declare function foldRunEventParts(parts: readonly ChatPart[]): ChatPart[];
|
|
44
|
+
/**
|
|
45
|
+
* The live form of {@link foldRunEventParts}, for a stream that stays open. An
|
|
46
|
+
* entry with a reconcile key waits `flushMs`, and a later entry with the same
|
|
47
|
+
* key replaces it. Thus a catch-up burst delivers each key once, and a live
|
|
48
|
+
* update lands one window later. Every other entry, also one of a type that the
|
|
49
|
+
* registry does not know, goes to `emit` at once, thus it can overtake a held
|
|
50
|
+
* entry. The held entries go to `emit` when the stream ends.
|
|
51
|
+
*/
|
|
52
|
+
export declare function pipeFoldedRunEvents<T>(entries: AsyncIterable<T>, emit: (entry: T) => Promise<void>, flushMs: number): Promise<void>;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
3
|
-
* stream value to the chat data-part contract,
|
|
4
|
-
* fold
|
|
2
|
+
* The rules of the run-event read side that need no database and no durability
|
|
3
|
+
* engine: narrowing a raw durable stream value to the chat data-part contract,
|
|
4
|
+
* and the latest-wins reconciling fold, as a batch for `createRunEventStream`
|
|
5
|
+
* and live for an embedder that reads the run streams itself.
|
|
5
6
|
*
|
|
6
|
-
* They live apart from the reader
|
|
7
|
-
*
|
|
8
|
-
* durability engine, no clock — and keeping them here is what lets that be
|
|
9
|
-
* proven directly instead of inferred from a driven subscription.
|
|
7
|
+
* They live apart from the reader, thus a test drives them directly instead of
|
|
8
|
+
* through a subscription.
|
|
10
9
|
*/
|
|
11
10
|
import { reconcileKey } from "../contracts/chat-frame.js";
|
|
12
11
|
import { PART_REGISTRY } from "../contracts/part-registry.js";
|
|
@@ -67,3 +66,59 @@ export function foldRunEventParts(parts) {
|
|
|
67
66
|
}
|
|
68
67
|
return folded;
|
|
69
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* The live form of {@link foldRunEventParts}, for a stream that stays open. An
|
|
71
|
+
* entry with a reconcile key waits `flushMs`, and a later entry with the same
|
|
72
|
+
* key replaces it. Thus a catch-up burst delivers each key once, and a live
|
|
73
|
+
* update lands one window later. Every other entry, also one of a type that the
|
|
74
|
+
* registry does not know, goes to `emit` at once, thus it can overtake a held
|
|
75
|
+
* entry. The held entries go to `emit` when the stream ends.
|
|
76
|
+
*/
|
|
77
|
+
export async function pipeFoldedRunEvents(entries, emit, flushMs) {
|
|
78
|
+
const latestByKey = new Map();
|
|
79
|
+
const pendingKeys = new Set();
|
|
80
|
+
let flushTimer = null;
|
|
81
|
+
let flushChain = Promise.resolve();
|
|
82
|
+
const flushPending = async () => {
|
|
83
|
+
flushTimer = null;
|
|
84
|
+
const keys = [...pendingKeys];
|
|
85
|
+
pendingKeys.clear();
|
|
86
|
+
for (const key of keys) {
|
|
87
|
+
const entry = latestByKey.get(key);
|
|
88
|
+
if (entry !== undefined)
|
|
89
|
+
await emit(entry);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const scheduleFlush = () => {
|
|
93
|
+
if (flushTimer !== null)
|
|
94
|
+
return;
|
|
95
|
+
flushTimer = setTimeout(() => {
|
|
96
|
+
// A timed flush has no caller to report to. A lasting emit failure
|
|
97
|
+
// still reaches the caller, through the next direct emit or the
|
|
98
|
+
// final flush.
|
|
99
|
+
flushChain = flushChain.then(flushPending).catch(() => undefined);
|
|
100
|
+
}, flushMs);
|
|
101
|
+
};
|
|
102
|
+
try {
|
|
103
|
+
for await (const entry of entries) {
|
|
104
|
+
const part = parseRunEventPart(entry);
|
|
105
|
+
const key = part === null ? undefined : reconcileKey(part);
|
|
106
|
+
if (key === undefined) {
|
|
107
|
+
await emit(entry);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
latestByKey.set(key, entry);
|
|
111
|
+
pendingKeys.add(key);
|
|
112
|
+
scheduleFlush();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
if (flushTimer !== null) {
|
|
117
|
+
clearTimeout(flushTimer);
|
|
118
|
+
flushTimer = null;
|
|
119
|
+
}
|
|
120
|
+
await flushChain;
|
|
121
|
+
if (pendingKeys.size > 0)
|
|
122
|
+
await flushPending();
|
|
123
|
+
}
|
|
124
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -170,6 +170,7 @@ export { registerSandboxStep } from "./workflows/sandbox-step.js";
|
|
|
170
170
|
export type { SandboxStepDeps, SandboxStepInput, SandboxStepResult, SandboxAgentBuildContext } from "./workflows/sandbox-step.js";
|
|
171
171
|
export { createRunEventStream } from "./execution/run-event-stream.js";
|
|
172
172
|
export type { RunEventStream, RunEventStreamDeps, RunEventSubscribeOptions, RunEventPartHandler } from "./execution/run-event-stream.js";
|
|
173
|
+
export { pipeFoldedRunEvents } from "./execution/run-event-parts.js";
|
|
173
174
|
export { createSandboxAgents, SANDBOX_AGENT_META } from "./agents/sandbox/index.js";
|
|
174
175
|
export type { SandboxAgentDeps, SandboxStepCoords, AgentMeta } from "./agents/sandbox/index.js";
|
|
175
176
|
export { AnalysisPlanSchema } from "./schemas/workflow-state.js";
|
package/dist/index.js
CHANGED
|
@@ -271,6 +271,7 @@ export { registerSandboxStep } from "./workflows/sandbox-step.js";
|
|
|
271
271
|
// Nothing in the signature belongs to the durability engine, so an embedder
|
|
272
272
|
// consumes run events without depending on it.
|
|
273
273
|
export { createRunEventStream } from "./execution/run-event-stream.js";
|
|
274
|
+
export { pipeFoldedRunEvents } from "./execution/run-event-parts.js";
|
|
274
275
|
// Sandbox-agent catalog. `buildAgent` maps a `SandboxAgentBuildContext` onto
|
|
275
276
|
// `SandboxAgentDeps`, then selects the per-step agent by id from this record;
|
|
276
277
|
// `SANDBOX_AGENT_META` is the planner-facing meta the same ids key.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const drugRepurposingAgentPrompt = "# Drug Repurposing Agent\n\nYou are a computational drug repurposing specialist. You identify\nexisting drugs for new therapeutic indications using signature-based,\ntarget-based, network-based, and genetics-based approaches. You\nintegrate evidence from multiple sources to rank and prioritize\nrepurposing candidates.\n\n## Skills\n\nYour skills: `drug-repurposing`, `cheminformatics`,\n`genomic-variants`, `shared/omics-general`.\n\n`drug-repurposing` carries connectivity scoring, network proximity, genetic\nevidence scoring, and multi-evidence integration patterns. `cheminformatics`\nhas the RDKit/datamol API references for assessing candidate compound\nproperties. `genomic-variants` has the Mendelian randomization reference,\nthe test of whether a target's disease association is causal.\n\n## Keyed Sources\n\nSome sources need an API key that a deployment can lack. When a source is\nunavailable, skip the analyses that depend on it, note the gap, and do not\ninvent its output.\n\n- `drug_gene_interactions` \u2014 its `drugbank` source requires\n DRUGBANK_API_KEY; `dgidb` (the default) and `pharmgkb` are public\n- `gene_disease_evidence` \u2014 its `disgenet` source requires\n DISGENET_API_KEY; `gwas` and `clinvar` are public\n- EPA CTX tool (`comptox`, datasets toxcast / hazard / chemical /\n exposure) \u2014 requires EPA_CCTE_API_KEY\n\nBoth multi-source tools report per-corpus availability in `perSource`:\nan unconfigured key degrades that ONE source to `unavailable` and the\nrest of the call still answers. Read `perSource` before concluding that\nevidence is absent.\n\n## Core Capabilities\n\n1. **Signature-based repurposing** \u2014 query disease DE signatures\n against drug perturbation profiles using CMap-style connectivity\n scoring (gseapy.prerank). Drugs that REVERSE the disease\n signature are therapeutic candidates.\n\n2. **Target-based repurposing** \u2014 map disease-relevant targets to\n existing drugs via ChEMBL (`chembl`, actions compounds / drug /\n mechanism) and `drug_gene_interactions`.\n Prioritize approved and clinical-stage drugs.\n\n3. **Genetics-based repurposing** \u2014 use `gene_disease_evidence` to\n identify targets with causal disease links, then map to existing\n drugs. Genetic support increases clinical success probability ~2x.\n\n4. **Network proximity analysis** \u2014 compute PPI network proximity\n between drug target sets and disease gene modules. Closer\n proximity = more likely to modulate disease biology.\n\n5. **Clinical evidence mining** \u2014 use `search_clinical_trials` to\n check if candidates are already in trials for the target\n indication. Use `pubmed` for published evidence.\n\n6. **Safety assessment** \u2014 use `search_faers` to assess real-world\n safety for the proposed patient population. Flag drugs with\n contraindications for the target indication.\n\n## Candidate Ranking\n\nChoose the repurposing approaches that the available data supports. Check\neach candidate against clinical trials, literature, and safety data, then\nrank candidates by a composite score across evidence lines. Report the\ncandidate table with its evidence breakdown, the evidence heatmap, and the\nlimitations.\n\n## Tool Usage for Candidate Discovery\n\n### From Targets (ChEMBL workflow)\n1. `chembl({action:\"targets\"})` \u2014 resolve gene symbols to target ChEMBL IDs.\n2. `chembl({action:\"compounds\", searchType:\"target\"})` \u2014 find active compounds.\n3. `chembl({action:\"drug\"})` \u2014 check approval status and existing indications.\n4. `chembl({action:\"mechanism\"})` \u2014 verify mechanism relevance.\n\n### From Targets (interaction workflow)\n1. `drug_gene_interactions({direction:\"gene_to_drugs\"})` \u2014 find drugs for\n a gene set. Add `sources:[\"dgidb\",\"drugbank\"]` and\n `includeDrugRecord:true` when you need the curated DrugBank record\n (description, modality, approval groups) for a shortlisted candidate \u2014\n not for the whole discovery sweep. DrugBank here carries no indication\n and no toxicity.\n2. Take the existing indications from `chembl({action:\"drug\"})` and the\n safety profile from `search_faers`.\n\n### From Genetic Evidence\n1. `gene_disease_evidence({queryType:\"disease\"})` \u2014 GWAS associations,\n DisGeNET scores and ClinVar classifications for the indication in one\n call.\n2. `opentargets({action:\"target\"})` \u2014 check target tractability and drug scores.\n\n### Validation Layer\n1. `search_clinical_trials` \u2014 existing trials for drug + indication.\n2. `search_faers` \u2014 adverse event profile.\n3. `pubmed` \u2014 published evidence.\n\n### Preclinical Target Intelligence\n1. `gene_preclinical_profile` (geneSymbol) \u2014 returns both halves in one\n call: cross-species baseline expression, confirming the target is\n expressed in tissues relevant to the new indication and that\n model-organism surrogates are sensible; and the mouse-KO phenotype +\n viability (per-zygosity breakdown plus a derived top-line lethal /\n subviable / viable). A complete-penetrance lethal KO is a hard\n tractability flag; a clean viable KO with no organ-system phenotypes\n is a positive tractability signal.\n2. Treat empty/null outputs as valid \"no data\" (not all genes have\n IMPC mouse lines; Bgee can be sparse for dog/macaque). Do NOT retry.\n\n## Required Figures\n\n- **Candidate ranking bar chart** \u2014 top candidates by composite\n score, colored by number of evidence lines.\n- **Evidence heatmap** \u2014 candidates x evidence types, showing which\n evidence supports each candidate.\n- **Connectivity score plot** \u2014 NES distribution for signature-based\n hits (if applicable).\n\n## Domain Anti-Patterns\n\n- Presenting computational repurposing candidates as validated\n therapeutics. They are hypotheses.\n- Connectivity scoring without permutation significance testing.\n- Ranking candidates by a single evidence type.\n- Ignoring existing indications \u2014 a drug already approved for the\n target disease is not a repurposing candidate.\n- Skipping safety assessment for proposed new patient populations.\n- Using outdated perturbation databases without noting the version.\n- Ignoring drug-drug interactions when proposing repurposed drugs for\n patients likely on existing therapies.\n\n## Output Naming\n\n- `output/repurposing_candidates.csv` \u2014 ranked candidate table\n with drug, existing indication, proposed indication, composite\n score, evidence components, development stage.\n- `output/connectivity_results.csv` \u2014 full connectivity scoring\n results (if signature-based).\n- `output/genetic_evidence.csv` \u2014 per-target genetic evidence\n scores (if genetics-based).\n- `figures/candidate_ranking.{png,pdf}`\n- `figures/evidence_heatmap.{png,pdf}`\n";
|
|
1
|
+
export declare const drugRepurposingAgentPrompt = "# Drug Repurposing Agent\n\nYou are a computational drug repurposing specialist. You identify\nexisting drugs for new therapeutic indications using signature-based,\ntarget-based, network-based, and genetics-based approaches. You\nintegrate evidence from multiple sources to rank and prioritize\nrepurposing candidates.\n\n## Skills\n\nYour skills: `drug-repurposing`, `cheminformatics`,\n`genomic-variants`, `shared/omics-general`.\n\n`drug-repurposing` carries connectivity scoring, network proximity, genetic\nevidence scoring, and multi-evidence integration patterns. `cheminformatics`\nhas the RDKit/datamol API references for assessing candidate compound\nproperties. `genomic-variants` has the Mendelian randomization reference,\nthe test of whether a target's disease association is causal.\n\n## Keyed Sources\n\nSome sources need an API key that a deployment can lack. When a source is\nunavailable, skip the analyses that depend on it, note the gap, and do not\ninvent its output.\n\n- `drug_gene_interactions` \u2014 its `drugbank` source requires\n DRUGBANK_API_KEY; `dgidb` (the default) and `pharmgkb` are public\n- `gene_disease_evidence` \u2014 its `disgenet` source requires\n DISGENET_API_KEY; `gwas` and `clinvar` are public\n- EPA CTX tool (`comptox`, datasets toxcast / hazard / chemical /\n exposure) \u2014 requires EPA_CCTE_API_KEY\n- `search_regulatory_approvals` (FDA and EMA authorizations and their\n label text) \u2014 requires AMASS_API_KEY\n\nBoth multi-source tools report per-corpus availability in `perSource`:\nan unconfigured key degrades that ONE source to `unavailable` and the\nrest of the call still answers. Read `perSource` before concluding that\nevidence is absent.\n\n## Core Capabilities\n\n1. **Signature-based repurposing** \u2014 query disease DE signatures\n against drug perturbation profiles using CMap-style connectivity\n scoring (gseapy.prerank). Drugs that REVERSE the disease\n signature are therapeutic candidates.\n\n2. **Target-based repurposing** \u2014 map disease-relevant targets to\n existing drugs via ChEMBL (`chembl`, actions compounds / drug /\n mechanism) and `drug_gene_interactions`.\n Prioritize approved and clinical-stage drugs.\n\n3. **Genetics-based repurposing** \u2014 use `gene_disease_evidence` to\n identify targets with causal disease links, then map to existing\n drugs. Genetic support increases clinical success probability ~2x.\n\n4. **Network proximity analysis** \u2014 compute PPI network proximity\n between drug target sets and disease gene modules. Closer\n proximity = more likely to modulate disease biology.\n\n5. **Clinical evidence mining** \u2014 use `search_clinical_trials` to\n check if candidates are already in trials for the target\n indication. Use `pubmed` for published evidence.\n\n6. **Safety assessment** \u2014 use `search_faers` to assess real-world\n safety for the proposed patient population. Flag drugs with\n contraindications for the target indication.\n\n## Candidate Ranking\n\nChoose the repurposing approaches that the available data supports. Check\neach candidate against clinical trials, literature, and safety data, then\nrank candidates by a composite score across evidence lines. Report the\ncandidate table with its evidence breakdown, the evidence heatmap, and the\nlimitations.\n\n## Tool Usage for Candidate Discovery\n\n### From Targets (ChEMBL workflow)\n1. `chembl({action:\"targets\"})` \u2014 resolve gene symbols to target ChEMBL IDs.\n2. `chembl({action:\"compounds\", searchType:\"target\"})` \u2014 find active compounds.\n3. `chembl({action:\"drug\"})` \u2014 check approval status and existing indications.\n4. `chembl({action:\"mechanism\"})` \u2014 verify mechanism relevance.\n\n### From Targets (interaction workflow)\n1. `drug_gene_interactions({direction:\"gene_to_drugs\"})` \u2014 find drugs for\n a gene set. Add `sources:[\"dgidb\",\"drugbank\"]` and\n `includeDrugRecord:true` when you need the curated DrugBank record\n (description, modality, approval groups) for a shortlisted candidate \u2014\n not for the whole discovery sweep. DrugBank here carries no indication\n and no toxicity.\n2. Take the existing indications from `chembl({action:\"drug\"})` and the\n safety profile from `search_faers`.\n\n### From Genetic Evidence\n1. `gene_disease_evidence({queryType:\"disease\"})` \u2014 GWAS associations,\n DisGeNET scores and ClinVar classifications for the indication in one\n call.\n2. `opentargets({action:\"target\"})` \u2014 check target tractability and drug scores.\n\n### Validation Layer\n1. `search_clinical_trials` \u2014 existing trials for drug + indication.\n2. `search_faers` \u2014 adverse event profile.\n3. `pubmed` \u2014 published evidence.\n4. `search_regulatory_approvals` \u2014 the authorization status of a\n candidate at the FDA and the EMA, its approved indications, its\n designations, and the safety sections of its label (boxed warning,\n contraindications). Read the matched excerpts of a search first, then\n only the `section` that you cite.\n\n### Preclinical Target Intelligence\n1. `gene_preclinical_profile` (geneSymbol) \u2014 returns both halves in one\n call: cross-species baseline expression, confirming the target is\n expressed in tissues relevant to the new indication and that\n model-organism surrogates are sensible; and the mouse-KO phenotype +\n viability (per-zygosity breakdown plus a derived top-line lethal /\n subviable / viable). A complete-penetrance lethal KO is a hard\n tractability flag; a clean viable KO with no organ-system phenotypes\n is a positive tractability signal.\n2. Treat empty/null outputs as valid \"no data\" (not all genes have\n IMPC mouse lines; Bgee can be sparse for dog/macaque). Do NOT retry.\n\n## Required Figures\n\n- **Candidate ranking bar chart** \u2014 top candidates by composite\n score, colored by number of evidence lines.\n- **Evidence heatmap** \u2014 candidates x evidence types, showing which\n evidence supports each candidate.\n- **Connectivity score plot** \u2014 NES distribution for signature-based\n hits (if applicable).\n\n## Domain Anti-Patterns\n\n- Presenting computational repurposing candidates as validated\n therapeutics. They are hypotheses.\n- Connectivity scoring without permutation significance testing.\n- Ranking candidates by a single evidence type.\n- Ignoring existing indications \u2014 a drug already approved for the\n target disease is not a repurposing candidate.\n- Skipping safety assessment for proposed new patient populations.\n- Using outdated perturbation databases without noting the version.\n- Ignoring drug-drug interactions when proposing repurposed drugs for\n patients likely on existing therapies.\n\n## Output Naming\n\n- `output/repurposing_candidates.csv` \u2014 ranked candidate table\n with drug, existing indication, proposed indication, composite\n score, evidence components, development stage.\n- `output/connectivity_results.csv` \u2014 full connectivity scoring\n results (if signature-based).\n- `output/genetic_evidence.csv` \u2014 per-target genetic evidence\n scores (if genetics-based).\n- `figures/candidate_ranking.{png,pdf}`\n- `figures/evidence_heatmap.{png,pdf}`\n";
|
|
@@ -29,6 +29,8 @@ invent its output.
|
|
|
29
29
|
DISGENET_API_KEY; \`gwas\` and \`clinvar\` are public
|
|
30
30
|
- EPA CTX tool (\`comptox\`, datasets toxcast / hazard / chemical /
|
|
31
31
|
exposure) — requires EPA_CCTE_API_KEY
|
|
32
|
+
- \`search_regulatory_approvals\` (FDA and EMA authorizations and their
|
|
33
|
+
label text) — requires AMASS_API_KEY
|
|
32
34
|
|
|
33
35
|
Both multi-source tools report per-corpus availability in \`perSource\`:
|
|
34
36
|
an unconfigured key degrades that ONE source to \`unavailable\` and the
|
|
@@ -99,6 +101,11 @@ limitations.
|
|
|
99
101
|
1. \`search_clinical_trials\` — existing trials for drug + indication.
|
|
100
102
|
2. \`search_faers\` — adverse event profile.
|
|
101
103
|
3. \`pubmed\` — published evidence.
|
|
104
|
+
4. \`search_regulatory_approvals\` — the authorization status of a
|
|
105
|
+
candidate at the FDA and the EMA, its approved indications, its
|
|
106
|
+
designations, and the safety sections of its label (boxed warning,
|
|
107
|
+
contraindications). Read the matched excerpts of a search first, then
|
|
108
|
+
only the \`section\` that you cite.
|
|
102
109
|
|
|
103
110
|
### Preclinical Target Intelligence
|
|
104
111
|
1. \`gene_preclinical_profile\` (geneSymbol) — returns both halves in one
|
|
@@ -34,5 +34,7 @@ export * from "./gene-preclinical-profile.js";
|
|
|
34
34
|
export * from "./search-clinical-trials.js";
|
|
35
35
|
export * from "./search-geo-datasets.js";
|
|
36
36
|
export * from "./search-faers.js";
|
|
37
|
+
export * from "./search-regulatory-approvals.js";
|
|
38
|
+
export * from "./search-patents.js";
|
|
37
39
|
export * from "./target-safety.js";
|
|
38
40
|
export * from "./comptox.js";
|
package/dist/tools/bio/index.js
CHANGED
|
@@ -41,6 +41,9 @@ export * from "./gene-preclinical-profile.js";
|
|
|
41
41
|
export * from "./search-clinical-trials.js";
|
|
42
42
|
export * from "./search-geo-datasets.js";
|
|
43
43
|
export * from "./search-faers.js";
|
|
44
|
+
// Regulatory authorizations and patents (Amass)
|
|
45
|
+
export * from "./search-regulatory-approvals.js";
|
|
46
|
+
export * from "./search-patents.js";
|
|
44
47
|
// Safety / toxicology
|
|
45
48
|
export * from "./target-safety.js";
|
|
46
49
|
export * from "./comptox.js";
|
package/dist/tools/bio/keys.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface BioToolKeys {
|
|
|
14
14
|
readonly ncbi?: string;
|
|
15
15
|
readonly github?: string;
|
|
16
16
|
readonly semanticScholar?: string;
|
|
17
|
+
readonly amass?: string;
|
|
17
18
|
}
|
|
18
19
|
/**
|
|
19
20
|
* The NCBI-backed literature tool, built from the shared key slice.
|
|
@@ -43,3 +44,13 @@ export declare function createChemDbTools(keys: BioToolKeys, deps?: {
|
|
|
43
44
|
drugGeneInteractions: Tool;
|
|
44
45
|
comptox: Tool;
|
|
45
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* The Amass tools: FDA and EMA authorizations, and patents.
|
|
49
|
+
*
|
|
50
|
+
* The key is optional. With no key, each tool is still built, and a call throws
|
|
51
|
+
* before it reaches Amass, the same as `comptox` without its EPA key.
|
|
52
|
+
*/
|
|
53
|
+
export declare function createAmassTools(keys: BioToolKeys): {
|
|
54
|
+
regulatoryApprovals: Tool;
|
|
55
|
+
patents: Tool;
|
|
56
|
+
};
|
package/dist/tools/bio/keys.js
CHANGED
|
@@ -2,6 +2,8 @@ import { createComptoxTool } from "./comptox.js";
|
|
|
2
2
|
import { createDrugGeneInteractionsTool } from "./drug-gene-interactions.js";
|
|
3
3
|
import { createGeneDiseaseEvidenceTool } from "./gene-disease-evidence.js";
|
|
4
4
|
import { createPubMedTool } from "./pubmed.js";
|
|
5
|
+
import { createSearchPatentsTool } from "./search-patents.js";
|
|
6
|
+
import { createSearchRegulatoryApprovalsTool } from "./search-regulatory-approvals.js";
|
|
5
7
|
/**
|
|
6
8
|
* The NCBI-backed literature tool, built from the shared key slice.
|
|
7
9
|
*
|
|
@@ -39,3 +41,16 @@ export function createChemDbTools(keys, deps = {}) {
|
|
|
39
41
|
comptox: createComptoxTool({ apiKey: keys.epaCcte }),
|
|
40
42
|
};
|
|
41
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* The Amass tools: FDA and EMA authorizations, and patents.
|
|
46
|
+
*
|
|
47
|
+
* The key is optional. With no key, each tool is still built, and a call throws
|
|
48
|
+
* before it reaches Amass, the same as `comptox` without its EPA key.
|
|
49
|
+
*/
|
|
50
|
+
export function createAmassTools(keys) {
|
|
51
|
+
const apiKey = keys.amass ?? "";
|
|
52
|
+
return {
|
|
53
|
+
regulatoryApprovals: createSearchRegulatoryApprovalsTool({ apiKey }),
|
|
54
|
+
patents: createSearchPatentsTool({ apiKey }),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -26,6 +26,6 @@ export declare const searchClinicalTrialsTool: import("../define-tool.js").Tool<
|
|
|
26
26
|
query?: string | undefined;
|
|
27
27
|
nctId?: string | undefined;
|
|
28
28
|
phase?: "EARLY_PHASE1" | "PHASE1" | "PHASE2" | "PHASE3" | "PHASE4" | undefined;
|
|
29
|
-
status?: "RECRUITING" | "ACTIVE_NOT_RECRUITING" | "COMPLETED" | "NOT_YET_RECRUITING" | "TERMINATED" | "WITHDRAWN" |
|
|
29
|
+
status?: "SUSPENDED" | "RECRUITING" | "ACTIVE_NOT_RECRUITING" | "COMPLETED" | "NOT_YET_RECRUITING" | "TERMINATED" | "WITHDRAWN" | undefined;
|
|
30
30
|
}, ClinicalTrialsOutput>;
|
|
31
31
|
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* search_patents — patents from Amass PatentCore behind one `action`
|
|
3
|
+
* discriminator: a search that returns no claims and no description, and the
|
|
4
|
+
* details of one patent, with the claims and the description on request.
|
|
5
|
+
*
|
|
6
|
+
* The input is a flat object with an `action` discriminator — not a
|
|
7
|
+
* `z.discriminatedUnion`, which `defineTool` rejects (model tool calling needs a
|
|
8
|
+
* top-level `"type":"object"`).
|
|
9
|
+
*/
|
|
10
|
+
import { type PatentRecord, type PatentSummary } from "../lib/amass-client.js";
|
|
11
|
+
type PatentsOutput = {
|
|
12
|
+
patents: PatentSummary[];
|
|
13
|
+
} | {
|
|
14
|
+
status: "found";
|
|
15
|
+
patent: PatentRecord;
|
|
16
|
+
} | {
|
|
17
|
+
status: "not_found";
|
|
18
|
+
} | {
|
|
19
|
+
status: "ambiguous";
|
|
20
|
+
candidates: string[];
|
|
21
|
+
};
|
|
22
|
+
export declare function createSearchPatentsTool(deps: {
|
|
23
|
+
apiKey: string;
|
|
24
|
+
}): import("../define-tool.js").Tool<{
|
|
25
|
+
action: "search" | "details";
|
|
26
|
+
limit: number;
|
|
27
|
+
includeClaims: boolean;
|
|
28
|
+
includeDescription: boolean;
|
|
29
|
+
query?: string | undefined;
|
|
30
|
+
assignee?: string[] | undefined;
|
|
31
|
+
inventor?: string | undefined;
|
|
32
|
+
cpcCodes?: string[] | undefined;
|
|
33
|
+
countryCode?: string[] | undefined;
|
|
34
|
+
minPublicationDate?: string | undefined;
|
|
35
|
+
maxPublicationDate?: string | undefined;
|
|
36
|
+
minFilingDate?: string | undefined;
|
|
37
|
+
maxFilingDate?: string | undefined;
|
|
38
|
+
minPriorityDate?: string | undefined;
|
|
39
|
+
maxPriorityDate?: string | undefined;
|
|
40
|
+
minGrantDate?: string | undefined;
|
|
41
|
+
maxGrantDate?: string | undefined;
|
|
42
|
+
amassId?: string | undefined;
|
|
43
|
+
publicationNumber?: string | undefined;
|
|
44
|
+
applicationNumber?: string | undefined;
|
|
45
|
+
}, PatentsOutput>;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* search_patents — patents from Amass PatentCore behind one `action`
|
|
3
|
+
* discriminator: a search that returns no claims and no description, and the
|
|
4
|
+
* details of one patent, with the claims and the description on request.
|
|
5
|
+
*
|
|
6
|
+
* The input is a flat object with an `action` discriminator — not a
|
|
7
|
+
* `z.discriminatedUnion`, which `defineTool` rejects (model tool calling needs a
|
|
8
|
+
* top-level `"type":"object"`).
|
|
9
|
+
*/
|
|
10
|
+
import { err, ok } from "neverthrow";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { defineTool } from "../define-tool.js";
|
|
13
|
+
import { describeAmassError, getPatentRecord, lookupPatentIds, searchPatentRecords, } from "../lib/amass-client.js";
|
|
14
|
+
import { getAmassHeaders } from "../lib/amass-config.js";
|
|
15
|
+
/** The largest `limit` that still costs one search price (5 credits for each 20 records). */
|
|
16
|
+
const MAX_LIMIT = 20;
|
|
17
|
+
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
18
|
+
const IDENTIFIER_FIELDS = ["amassId", "publicationNumber", "applicationNumber"];
|
|
19
|
+
function dateField(description) {
|
|
20
|
+
return z.string().regex(ISO_DATE).optional().describe(`'search' only. ${description}, YYYY-MM-DD.`);
|
|
21
|
+
}
|
|
22
|
+
const inputSchema = z
|
|
23
|
+
.object({
|
|
24
|
+
action: z
|
|
25
|
+
.enum(["search", "details"])
|
|
26
|
+
.default("search")
|
|
27
|
+
.describe("'search' (default) — find patents with `query`. 'details' — read one patent by ONE identifier."),
|
|
28
|
+
query: z
|
|
29
|
+
.string()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe("Required for 'search'. Keywords matched over the title, the abstract, the claims and the description: a compound, a target, a " +
|
|
32
|
+
"method or a formulation ('imatinib mesylate crystal form', 'KRAS G12C inhibitor')."),
|
|
33
|
+
assignee: z.array(z.string()).optional().describe("'search' only. Keep the patents of any of these assignees, e.g. ['Novartis']."),
|
|
34
|
+
inventor: z.string().optional().describe("'search' only. Keep the patents of this inventor name."),
|
|
35
|
+
cpcCodes: z.array(z.string()).optional().describe("'search' only. Keep these Cooperative Patent Classification codes, e.g. ['A61K', 'C07D']."),
|
|
36
|
+
countryCode: z.array(z.string()).optional().describe("'search' only. Keep these jurisdictions, e.g. ['US', 'EP', 'WO']."),
|
|
37
|
+
minPublicationDate: dateField("Earliest publication date"),
|
|
38
|
+
maxPublicationDate: dateField("Latest publication date"),
|
|
39
|
+
minFilingDate: dateField("Earliest filing date"),
|
|
40
|
+
maxFilingDate: dateField("Latest filing date"),
|
|
41
|
+
minPriorityDate: dateField("Earliest priority date — the effective date of the invention, the one for prior art"),
|
|
42
|
+
maxPriorityDate: dateField("Latest priority date"),
|
|
43
|
+
minGrantDate: dateField("Earliest grant date (drops each patent that was never granted)"),
|
|
44
|
+
maxGrantDate: dateField("Latest grant date"),
|
|
45
|
+
limit: z
|
|
46
|
+
.number()
|
|
47
|
+
.int()
|
|
48
|
+
.min(1)
|
|
49
|
+
.max(MAX_LIMIT)
|
|
50
|
+
.default(10)
|
|
51
|
+
.describe(`'search' only. Max patents to return (default 10, max ${MAX_LIMIT}). There are no pages: narrow with the filters.`),
|
|
52
|
+
amassId: z.string().optional().describe("'details' only. The Amass ID of one patent ('AMPC_…'), as a search returns it."),
|
|
53
|
+
publicationNumber: z.string().optional().describe("'details' only. A publication number, e.g. 'US-10266485-B2'."),
|
|
54
|
+
applicationNumber: z.string().optional().describe("'details' only. An application number; it can match more than one publication."),
|
|
55
|
+
includeClaims: z.boolean().default(false).describe("'details' only. Add the full claims text. It can run to tens of thousands of characters."),
|
|
56
|
+
includeDescription: z
|
|
57
|
+
.boolean()
|
|
58
|
+
.default(false)
|
|
59
|
+
.describe("'details' only. Add the full description text. It is often longer than the claims; ask for it only when you must read it."),
|
|
60
|
+
})
|
|
61
|
+
.refine((d) => d.action !== "search" || (d.query !== undefined && d.query.trim().length > 0), {
|
|
62
|
+
message: "query is required when action is 'search'.",
|
|
63
|
+
path: ["query"],
|
|
64
|
+
})
|
|
65
|
+
.refine((d) => d.action !== "details" || IDENTIFIER_FIELDS.filter((field) => d[field] !== undefined).length === 1, {
|
|
66
|
+
message: "action 'details' takes exactly one of amassId, publicationNumber, and applicationNumber.",
|
|
67
|
+
path: ["action"],
|
|
68
|
+
});
|
|
69
|
+
/** A request that Amass rejected as invalid is the caller's to repair; every other failure is terminal. */
|
|
70
|
+
function failure(e) {
|
|
71
|
+
if (e.type === "invalid_input")
|
|
72
|
+
return err({ error: describeAmassError(e), retryable: false, cause: e });
|
|
73
|
+
throw new Error(describeAmassError(e));
|
|
74
|
+
}
|
|
75
|
+
export function createSearchPatentsTool(deps) {
|
|
76
|
+
return defineTool({
|
|
77
|
+
id: "search_patents",
|
|
78
|
+
description: "Patents from Amass PatentCore, across the patent offices (US, EP, WO, CN, JP, …), with one member for each patent family: the " +
|
|
79
|
+
"title, the abstract, the assignees, the inventors, the CPC codes, the priority, filing, publication and grant dates, and the citation " +
|
|
80
|
+
"counts. Answers 'who patented this compound, target or method, and when?' and 'what does this patent claim?'.\n" +
|
|
81
|
+
"action 'search' (the default) returns patents with no claims and no description. action 'details' takes ONE identifier and returns " +
|
|
82
|
+
"the patent with its family members; set includeClaims or includeDescription for the full text.\n" +
|
|
83
|
+
"ACCEPTED IDENTIFIERS for 'details': amassId ('AMPC_…'), publicationNumber ('US-10266485-B2') or applicationNumber.\n" +
|
|
84
|
+
"status 'not_found' is a valid no-data answer and status 'ambiguous' lists the candidate amassIds of an application number that " +
|
|
85
|
+
"matches more than one publication — call 'details' again with one of them. An empty search is also valid no-data. Do not retry any " +
|
|
86
|
+
"of these unchanged.\n" +
|
|
87
|
+
"Requires AMASS_API_KEY — a missing key fails terminally: do NOT retry, tell the user the key needs configuring and proceed without " +
|
|
88
|
+
"Amass patent data. Every call spends the user's Amass credits.",
|
|
89
|
+
inputSchema,
|
|
90
|
+
describeCall: "none",
|
|
91
|
+
execute: async (input) => {
|
|
92
|
+
const headers = getAmassHeaders(deps.apiKey);
|
|
93
|
+
if (input.action === "search") {
|
|
94
|
+
const patents = await searchPatentRecords({
|
|
95
|
+
query: input.query,
|
|
96
|
+
limit: input.limit,
|
|
97
|
+
...(input.assignee ? { assignee: input.assignee } : {}),
|
|
98
|
+
...(input.inventor ? { inventor: input.inventor } : {}),
|
|
99
|
+
...(input.cpcCodes ? { cpcCodes: input.cpcCodes } : {}),
|
|
100
|
+
...(input.countryCode ? { countryCode: input.countryCode } : {}),
|
|
101
|
+
...(input.minPublicationDate ? { minPublicationDate: input.minPublicationDate } : {}),
|
|
102
|
+
...(input.maxPublicationDate ? { maxPublicationDate: input.maxPublicationDate } : {}),
|
|
103
|
+
...(input.minFilingDate ? { minFilingDate: input.minFilingDate } : {}),
|
|
104
|
+
...(input.maxFilingDate ? { maxFilingDate: input.maxFilingDate } : {}),
|
|
105
|
+
...(input.minPriorityDate ? { minPriorityDate: input.minPriorityDate } : {}),
|
|
106
|
+
...(input.maxPriorityDate ? { maxPriorityDate: input.maxPriorityDate } : {}),
|
|
107
|
+
...(input.minGrantDate ? { minGrantDate: input.minGrantDate } : {}),
|
|
108
|
+
...(input.maxGrantDate ? { maxGrantDate: input.maxGrantDate } : {}),
|
|
109
|
+
}, headers);
|
|
110
|
+
if (patents.isErr())
|
|
111
|
+
return failure(patents.error);
|
|
112
|
+
return ok({ patents: patents.value });
|
|
113
|
+
}
|
|
114
|
+
let amassId = input.amassId;
|
|
115
|
+
if (amassId === undefined) {
|
|
116
|
+
const ids = await lookupPatentIds({
|
|
117
|
+
...(input.publicationNumber === undefined ? {} : { publicationNumber: input.publicationNumber }),
|
|
118
|
+
...(input.applicationNumber === undefined ? {} : { applicationNumber: input.applicationNumber }),
|
|
119
|
+
}, headers);
|
|
120
|
+
if (ids.isErr())
|
|
121
|
+
return failure(ids.error);
|
|
122
|
+
if (ids.value.length === 0)
|
|
123
|
+
return ok({ status: "not_found" });
|
|
124
|
+
if (ids.value.length > 1)
|
|
125
|
+
return ok({ status: "ambiguous", candidates: ids.value });
|
|
126
|
+
amassId = ids.value[0];
|
|
127
|
+
}
|
|
128
|
+
const patent = await getPatentRecord(amassId, { claims: input.includeClaims, description: input.includeDescription }, headers);
|
|
129
|
+
if (patent.isErr())
|
|
130
|
+
return failure(patent.error);
|
|
131
|
+
return ok(patent.value ? { status: "found", patent: patent.value } : { status: "not_found" });
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* search_regulatory_approvals — the FDA and EMA drug authorizations of Amass
|
|
3
|
+
* RegulatoryCore, with the parsed sections of their labels, reviews, SmPCs and
|
|
4
|
+
* EPARs, behind one `action` discriminator.
|
|
5
|
+
*
|
|
6
|
+
* The three actions read the text in three sizes: `search` gives the matched
|
|
7
|
+
* excerpts, `details` the table of contents, and `section` the full text of one
|
|
8
|
+
* section. Thus an agent reads only the section that it cites.
|
|
9
|
+
*
|
|
10
|
+
* The input is a flat object with an `action` discriminator — not a
|
|
11
|
+
* `z.discriminatedUnion`, which `defineTool` rejects (model tool calling needs a
|
|
12
|
+
* top-level `"type":"object"`).
|
|
13
|
+
*/
|
|
14
|
+
import { type AuthorizationRecord, type AuthorizationSearchRow, type RegulatorySection } from "../lib/amass-client.js";
|
|
15
|
+
type RegulatoryApprovalsOutput = {
|
|
16
|
+
authorizations: AuthorizationSearchRow[];
|
|
17
|
+
} | {
|
|
18
|
+
status: "found";
|
|
19
|
+
authorization: AuthorizationRecord;
|
|
20
|
+
} | {
|
|
21
|
+
status: "found";
|
|
22
|
+
section: RegulatorySection;
|
|
23
|
+
} | {
|
|
24
|
+
status: "not_found";
|
|
25
|
+
} | {
|
|
26
|
+
status: "ambiguous";
|
|
27
|
+
candidates: string[];
|
|
28
|
+
};
|
|
29
|
+
export declare function createSearchRegulatoryApprovalsTool(deps: {
|
|
30
|
+
apiKey: string;
|
|
31
|
+
}): import("../define-tool.js").Tool<{
|
|
32
|
+
action: "search" | "details" | "section";
|
|
33
|
+
limit: number;
|
|
34
|
+
query?: string | undefined;
|
|
35
|
+
agency?: ("FDA" | "EMA")[] | undefined;
|
|
36
|
+
authorizationStatus?: ("UNKNOWN" | "ACTIVE" | "APPROVED_NOT_MARKETED" | "CONDITIONAL" | "SUSPENDED" | "WITHDRAWN_VOLUNTARY" | "WITHDRAWN_FORCED" | "REVOKED" | "LAPSED_SUNSET" | "REFUSED" | "WITHDRAWN_DURING_REVIEW" | "EXPIRED")[] | undefined;
|
|
37
|
+
moleculeType?: ("UNKNOWN" | "SMALL_MOLECULE" | "ANTIBODY" | "PROTEIN" | "ENZYME" | "OLIGONUCLEOTIDE" | "GENE" | "CELL" | "ANTIBODY_DRUG_CONJUGATE" | "VACCINE_COMPONENT" | "VACCINE" | "OLIGOSACCHARIDE")[] | undefined;
|
|
38
|
+
hasDesignation?: ("PRIORITY_REVIEW" | "BREAKTHROUGH_THERAPY" | "FAST_TRACK" | "RMAT" | "ACCELERATED_APPROVAL" | "ACCELERATED_ASSESSMENT" | "PRIME" | "CONDITIONAL_MA" | "EXCEPTIONAL_CIRCUMSTANCES")[] | undefined;
|
|
39
|
+
isOrphan?: boolean | undefined;
|
|
40
|
+
minAuthorizationDate?: string | undefined;
|
|
41
|
+
maxAuthorizationDate?: string | undefined;
|
|
42
|
+
amassId?: string | undefined;
|
|
43
|
+
fdaApplicationNumber?: string | undefined;
|
|
44
|
+
emaProductNumber?: string | undefined;
|
|
45
|
+
ndc?: string | undefined;
|
|
46
|
+
splSetId?: string | undefined;
|
|
47
|
+
documentSectionId?: string | undefined;
|
|
48
|
+
}, RegulatoryApprovalsOutput>;
|
|
49
|
+
export {};
|