@inflexa-ai/harness 0.40.0 → 0.41.1
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/app/chat-turn.d.ts +42 -13
- package/dist/app/chat-turn.js +47 -16
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/prompts/briefing.d.ts +28 -1
- package/dist/prompts/briefing.js +44 -1
- package/dist/prompts/compaction.d.ts +2 -2
- package/dist/prompts/compaction.js +9 -5
- package/dist/prompts/sandbox-standards.d.ts +1 -1
- package/dist/prompts/sandbox-standards.js +4 -0
- package/dist/runtime/assemble.d.ts +4 -2
- package/dist/runtime/assemble.js +8 -1
- package/dist/schemas/file-metadata.js +9 -9
- package/dist/schemas/plan-schemas.d.ts +3 -3
- package/dist/tools/ad-hoc-router.js +9 -4
- package/dist/tools/bio/chembl.js +8 -5
- package/dist/tools/bio/comptox.js +4 -4
- package/dist/tools/bio/drug-gene-interactions.js +5 -4
- package/dist/tools/bio/gene-disease-evidence.js +4 -4
- package/dist/tools/bio/lookup-annotation.js +7 -3
- package/dist/tools/bio/opentargets.js +8 -6
- package/dist/tools/bio/pubchem.js +4 -2
- package/dist/tools/bio/pubmed.d.ts +1 -1
- package/dist/tools/bio/pubmed.js +10 -6
- package/dist/tools/bio/search-clinical-trials.js +2 -1
- package/dist/tools/bio/search-gene.js +2 -1
- package/dist/tools/bio/search-interactions.js +8 -4
- package/dist/tools/bio/target-safety.js +2 -1
- package/dist/tools/display/show-user.js +1 -1
- package/dist/tools/execute-analysis.js +14 -4
- package/dist/tools/lib/opentargets-client.d.ts +8 -3
- package/dist/tools/lib/opentargets-client.js +12 -3
- package/dist/tools/read-tool-output.js +7 -4
- package/dist/tools/report-authoring/authoring-tools.js +22 -15
- package/dist/tools/report-session/derive-table.js +5 -1
- package/dist/tools/report-session/examine-page.js +5 -4
- package/dist/tools/report-session/preview-report.js +6 -1
- package/dist/tools/report-session/record-version.js +2 -1
- package/dist/tools/research/context7-docs.js +8 -2
- package/dist/tools/research/generate-analogy-report.js +15 -15
- package/dist/tools/research/generate-plan.js +12 -7
- package/dist/tools/research/inspect-data-profile.js +7 -5
- package/dist/tools/research/inspect-run.js +6 -2
- package/dist/tools/research/literature-reviewer.js +11 -6
- package/dist/tools/research/search-arxiv.js +2 -1
- package/dist/tools/research/search-github-repos.js +9 -3
- package/dist/tools/sandbox/list-available-refs.js +3 -2
- package/dist/tools/sandbox/report-blocker.js +6 -1
- package/dist/tools/sandbox/skills.js +4 -2
- package/dist/tools/start-report-session.js +4 -1
- package/dist/tools/workspace/edit-file.js +4 -3
- package/dist/tools/workspace/execute-command.js +6 -5
- package/dist/tools/workspace/grep.js +9 -7
- package/dist/tools/workspace/list-files.js +1 -1
- package/dist/tools/workspace/read-file.js +2 -1
- package/dist/tools/workspace/scan-inputs.js +3 -1
- package/dist/tools/workspace/show-file.js +2 -0
- package/dist/tools/workspace/workspace-search.js +3 -1
- package/dist/tools/workspace/write-file.js +5 -3
- package/dist/workflows/execute-analysis.d.ts +28 -5
- package/dist/workflows/execute-analysis.js +34 -10
- package/package.json +1 -1
|
@@ -23,10 +23,15 @@ const resourcesSchema = z.object({
|
|
|
23
23
|
gpu: z.object({ count: z.number().int().positive() }).optional(),
|
|
24
24
|
});
|
|
25
25
|
const routeSchema = z.object({
|
|
26
|
-
agentId: z.string().optional(),
|
|
27
|
-
resources: resourcesSchema
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
agentId: z.string().optional().describe("The id of one specialist from the eligible catalog, copied exactly."),
|
|
27
|
+
resources: resourcesSchema
|
|
28
|
+
.optional()
|
|
29
|
+
.describe("The sandbox resources of the step, inside the stated ranges. Omit to take the defaults; a value outside the ranges is replaced by the defaults."),
|
|
30
|
+
rationale: z.string().optional().describe("One or two sentences on why this specialist fits the request. The step agent reads it."),
|
|
31
|
+
packages: z
|
|
32
|
+
.array(z.string())
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("The packages that the scripts of the step import, each as `[python:|r:]<name>[==<version>]`. An empty array is valid."),
|
|
30
35
|
});
|
|
31
36
|
export function defaultAdHocResources(policy) {
|
|
32
37
|
return policy ? { cpu: Math.min(4, policy.perStep.maxCpu), memoryGb: Math.min(8, policy.perStep.maxMemoryGb) } : { cpu: 4, memoryGb: 8 };
|
package/dist/tools/bio/chembl.js
CHANGED
|
@@ -38,10 +38,11 @@ const inputSchema = z
|
|
|
38
38
|
.describe("Which ChEMBL lookup to run; each names its params and return fields.\n" +
|
|
39
39
|
"'compounds' (query + searchType) — molecules by target, name or SMILES: resolve a named compound to its ID and structure, or list " +
|
|
40
40
|
"what was assayed against a target. → chemblId, preferredCompoundName, canonicalSmiles, molecularWeight, alogp, molecularFormula.\n" +
|
|
41
|
-
"'drug' (query) — approved drugs by
|
|
42
|
-
"preferredName, maxPhase (4 = approved), moleculeType, firstApproval, indication.
|
|
43
|
-
"
|
|
44
|
-
"
|
|
41
|
+
"'drug' (query) — approved drugs by drug name or by indication: 'is Y approved, since when?', 'what treats X?'. → moleculeChemblId, " +
|
|
42
|
+
"preferredName, maxPhase (4 = approved), moleculeType, firstApproval, indication. The query is read as a molecule name first: the " +
|
|
43
|
+
"approved molecules (max_phase 4) that it names come back with their indications. A query that names no approved molecule is then " +
|
|
44
|
+
"read as a disease term against the curated indication registry, highest phase first, and those rows can hold clinical-stage " +
|
|
45
|
+
"molecules. Thus a clinical-stage compound queried by name gives an empty array; action 'compounds' resolves it.\n" +
|
|
45
46
|
"'mechanism' (chemblId, molecule only) — curated mechanism of ONE molecule: 'how does X work?'. → mechanismOfAction, actionType " +
|
|
46
47
|
"(INHIBITOR, AGONIST, …), targetChemblId + targetName, moleculeChemblId. Curated mainly for clinical/approved molecules, so tool " +
|
|
47
48
|
"compounds often have none.\n" +
|
|
@@ -62,7 +63,9 @@ const inputSchema = z
|
|
|
62
63
|
searchType: z
|
|
63
64
|
.enum(["target", "compound", "smiles"])
|
|
64
65
|
.optional()
|
|
65
|
-
.describe("Required for 'compounds' — how to read `query`. 'target': resolve to
|
|
66
|
+
.describe("Required for 'compounds' — how to read `query`. 'target': resolve `query` to the TOP ChEMBL target hit only — the result does not " +
|
|
67
|
+
"name that target, and a symbol can hit a non-human ortholog — then return what was assayed against it; a target ChEMBL ID from " +
|
|
68
|
+
"action 'targets' pins the target. " +
|
|
66
69
|
"'compound': free-text over molecule names. 'smiles': flexmatch structure search."),
|
|
67
70
|
chemblId: z
|
|
68
71
|
.string()
|
|
@@ -45,8 +45,8 @@ const inputSchema = z
|
|
|
45
45
|
"HTTK toxicokinetic parameters, reported functional uses, consumer-product composition."),
|
|
46
46
|
query: z
|
|
47
47
|
.string()
|
|
48
|
-
.describe("Chemical identifier (all datasets). A DTXSID ('DTXSID7020182') is used directly; a CASRN ('80-05-7'), a name ('bisphenol A'), or
|
|
49
|
-
"
|
|
48
|
+
.describe("Chemical identifier (all datasets). A DTXSID ('DTXSID7020182') is used directly; a CASRN ('80-05-7'), a name ('bisphenol A'), or " +
|
|
49
|
+
"an InChIKey is resolved by EXACT match, so a non-canonical or misspelled name yields found: false."),
|
|
50
50
|
dataType: z
|
|
51
51
|
.enum(["toxval", "genetox", "cancer", "seem", "httk", "functional-use", "product-data", "all"])
|
|
52
52
|
.optional()
|
|
@@ -103,8 +103,8 @@ export function createComptoxTool(deps) {
|
|
|
103
103
|
"bioactivity), 'hazard' (in-vivo: " +
|
|
104
104
|
"NOAEL/LOAEL/LD50, genotoxicity, cancer), 'chemical' (identity + physicochemical/ADMET), 'exposure' (SEEM predictions, toxicokinetics, uses, " +
|
|
105
105
|
"products). See `dataset` for what each returns; all four resolve `query` to a DTXSID first.\n" +
|
|
106
|
-
"ACCEPTED IDENTIFIERS: a DTXSID ('DTXSID7020182') used directly, or a CASRN ('80-05-7'), a chemical name
|
|
107
|
-
"'
|
|
106
|
+
"ACCEPTED IDENTIFIERS, for every dataset: a DTXSID ('DTXSID7020182') used directly, or a CASRN ('80-05-7'), a chemical name " +
|
|
107
|
+
"('bisphenol A') or an InChIKey, each resolved by EXACT match.\n" +
|
|
108
108
|
"Requires EPA_CCTE_API_KEY — a missing key fails terminally: do NOT retry, tell the user the key needs configuring and proceed without EPA data.\n" +
|
|
109
109
|
"found: false means the query did not resolve to a chemical; a present-but-empty section is likewise valid no-data. Do not retry either.\n" +
|
|
110
110
|
"This is environmental/industrial-chemical data: for drug-like compounds prefer PubChem or ChEMBL — dataset 'chemical' returns " +
|
|
@@ -106,11 +106,12 @@ export function createDrugGeneInteractionsTool(deps) {
|
|
|
106
106
|
description: "Drug↔gene interactions across DGIdb (the Drug Gene Interaction Database), DrugBank and PharmGKB, in one call — 'what drugs hit these " +
|
|
107
107
|
"genes?' and 'what genes does this drug act on?'. See `direction` and `sources` for the modes.\n" +
|
|
108
108
|
"ACCEPTED IDENTIFIERS: a HUGO gene symbol ('EGFR') for direction 'gene_to_drugs'; a drug name ('imatinib'), a DrugBank ID ('DB00619') or a " +
|
|
109
|
-
"DGIdb concept ID for 'drug_to_genes'. A brand name and a non-HUGO symbol match nothing — resolve the symbol with search_gene and
|
|
110
|
-
"name
|
|
111
|
-
"It answers whether an interaction is KNOWN, by whom, and of what type.
|
|
109
|
+
"DGIdb concept ID for 'drug_to_genes'. A brand name and a non-HUGO symbol match nothing — resolve the symbol with search_gene, and pass " +
|
|
110
|
+
"the generic drug name, not the brand name.\n" +
|
|
111
|
+
"It answers whether an interaction is KNOWN, by whom, and of what type. It carries no potency value (IC50/Ki).\n" +
|
|
112
112
|
"Verbose fields (`includeAttributes`, `includeDrugRecord`) are off by default — both cost a lot of context and are rarely what the question needs.\n" +
|
|
113
|
-
"
|
|
113
|
+
"`perSource` gives the outcome of each corpus, and a conclusion that an interaction is unknown rests on it: 'no_data' means that corpus " +
|
|
114
|
+
"has nothing; 'unavailable' means it could " +
|
|
114
115
|
"not be reached (a missing DRUGBANK_API_KEY lands here — tell the user and proceed with DGIdb). A `found: false` entry means the identifier " +
|
|
115
116
|
"is not in DGIdb at all, usually a non-HUGO symbol or a brand name. Neither is worth retrying unchanged.",
|
|
116
117
|
inputSchema,
|
|
@@ -156,15 +156,15 @@ export function createGeneDiseaseEvidenceTool(deps) {
|
|
|
156
156
|
id: "gene_disease_evidence",
|
|
157
157
|
description: "Genetic evidence linking a gene to a disease or trait, across the NHGRI-EBI GWAS Catalog, DisGeNET, NCBI ClinVar and cBioPortal in one call " +
|
|
158
158
|
"— 'is there human genetic support for this target in this indication?'. See `sources` for what each corpus carries.\n" +
|
|
159
|
-
"
|
|
160
|
-
"and
|
|
161
|
-
"frequencies with their studies and PMIDs.\n" +
|
|
159
|
+
"It returns the underlying records — actual SNPs, effect sizes, scores, pathogenicity calls and somatic mutation frequencies with their " +
|
|
160
|
+
"studies and PMIDs — not one integrated score across evidence types.\n" +
|
|
162
161
|
"ACCEPTED IDENTIFIERS, each named by its `queryType`: a HUGO gene symbol or an Entrez ID ('PCSK9', '5008'); a disease or trait name or a UMLS " +
|
|
163
162
|
"CUI ('asthma', 'C0006142'); a dbSNP rsID ('rs11591147'); a GWAS Catalog study accession ('GCST000392'); and a ClinVar accession " +
|
|
164
163
|
"('VCV000012345'). The last two read one record back by the accession that an earlier result of this same tool reported. " +
|
|
165
164
|
"The GWAS Catalog matches the EXACT EFO trait label (case-insensitive): 'asthma' resolves, a paraphrase such as 'LDL cholesterol' reads as " +
|
|
166
165
|
"no_data there ('low density lipoprotein cholesterol measurement' is the label) while the other corpora still answer it.\n" +
|
|
167
|
-
"
|
|
166
|
+
"`perSource` gives the outcome of each corpus, and a conclusion of absence rests on it. 'no_data' means that corpus genuinely has nothing; " +
|
|
167
|
+
"'unavailable' means it " +
|
|
168
168
|
"could not be reached (a missing DISGENET_API_KEY lands here — tell the user and proceed with the others); 'not_applicable' means that " +
|
|
169
169
|
"corpus has no lookup for this queryType. Only 'no_data' is evidence of absence, and none is worth retrying unchanged.",
|
|
170
170
|
inputSchema,
|
|
@@ -84,7 +84,9 @@ const inputSchema = z
|
|
|
84
84
|
vocabulary: z
|
|
85
85
|
.enum(["go", "kegg", "reactome", "pathways"])
|
|
86
86
|
.describe("'go' — Gene Ontology via QuickGO (EBI); needs ONE of goId, query, or geneProductId. Returns terms[] { id, name, definition, aspect } " +
|
|
87
|
-
"and/or annotations[] { geneProductId, goId, goName, aspect, evidenceCode, qualifier }
|
|
87
|
+
"and/or annotations[] { geneProductId, goId, goName, aspect, evidenceCode, qualifier } — the first `limit` rows that QuickGO " +
|
|
88
|
+
"serves, one row for each term, evidence and reference, thus a term can repeat, a well-studied protein holds hundreds of rows, " +
|
|
89
|
+
"and the result gives no total. " +
|
|
88
90
|
"'kegg' / 'reactome' — one pathway database; needs query or pathwayId. 'pathways' — both, in parallel; needs query or pathwayId. " +
|
|
89
91
|
"Returns pathways[] { id, name, source, url, description?, genes? }."),
|
|
90
92
|
query: z
|
|
@@ -110,8 +112,10 @@ const inputSchema = z
|
|
|
110
112
|
includeGenes: z
|
|
111
113
|
.boolean()
|
|
112
114
|
.optional()
|
|
113
|
-
.describe("Pathway vocabularies only. Default FALSE — member
|
|
114
|
-
"
|
|
115
|
+
.describe("Pathway vocabularies only. Default FALSE — member lists cost one request per pathway and run to hundreds of entries. KEGG members " +
|
|
116
|
+
"are NCBI Gene (Entrez) IDs ('7157'), not symbols. Reactome members are participant entity names with their compartment " +
|
|
117
|
+
"('p-T389/412-RPS6KB1 [cytosol]', 'ATP [cytosol]'), small molecules included. True only when you need the members, not the " +
|
|
118
|
+
"pathway identities."),
|
|
115
119
|
limit: z
|
|
116
120
|
.number()
|
|
117
121
|
.int()
|
|
@@ -32,7 +32,9 @@ const inputSchema = z
|
|
|
32
32
|
action: z
|
|
33
33
|
.enum(["target", "disease", "resolve_disease"])
|
|
34
34
|
.describe("'target' (needs ensemblId) — the diseases associated with the gene: targetInfo (approvedSymbol, approvedName, " +
|
|
35
|
-
"tractability
|
|
35
|
+
"tractability — for smallMolecule, antibody and otherModalities, the labels of the positive Open Targets buckets, from " +
|
|
36
|
+
"'Approved Drug' and 'Advanced Clinical' down to 'High-Quality Pocket' or 'UniProt loc high conf'; an empty list means no " +
|
|
37
|
+
"positive bucket and null means no assessment) plus associations[], each with an overall score and " +
|
|
36
38
|
"its per-datatype breakdown — genetic association, known drug, literature, animal model, somatic mutation. " +
|
|
37
39
|
"'disease' (needs efoId) — the targets ranked for that disease; each association carries targetId/targetSymbol/targetName " +
|
|
38
40
|
"and the same score breakdown. " +
|
|
@@ -75,11 +77,11 @@ const inputSchema = z
|
|
|
75
77
|
});
|
|
76
78
|
export const openTargetsTool = defineTool({
|
|
77
79
|
id: "opentargets",
|
|
78
|
-
description: "Query the Open Targets Platform — the integrated target-disease evidence corpus of EMBL-EBI, GSK and their partners
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"It is the integrated view
|
|
82
|
-
"
|
|
80
|
+
description: "Query the Open Targets Platform — the integrated target-disease evidence corpus of EMBL-EBI, GSK and their partners. One 'target' query " +
|
|
81
|
+
"gives the tractability of the target and its disease associations, already scored and ranked, each with a per-datatype score for genetic " +
|
|
82
|
+
"association, known drug, literature, animal model and somatic mutation. See the action parameter for what each mode needs and returns.\n" +
|
|
83
|
+
"It is the integrated view, not the underlying records: it names no drug, no SNP, no effect size, no ClinVar call and no PMID, and it " +
|
|
84
|
+
"holds no safety liability.\n" +
|
|
83
85
|
"ACCEPTED IDENTIFIERS: an Ensembl gene ID for action 'target' (ENSG00000141510); an Open Targets disease ID for action 'disease' — EFO " +
|
|
84
86
|
"(EFO_0000311), MONDO (MONDO_0005148) or HP (HP_0001250), because Open Targets keys a disease on EFO and EFO imports the other two; and a plain " +
|
|
85
87
|
"disease NAME for action 'resolve_disease' ('type 2 diabetes'), which is how you obtain that disease ID. A bare gene symbol is NOT accepted by " +
|
|
@@ -19,14 +19,16 @@ const inputSchema = z
|
|
|
19
19
|
action: z
|
|
20
20
|
.enum(["compound", "crossrefs", "assays"])
|
|
21
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
|
|
22
|
+
"'compound' — resolve `query` (in the namespace named by `searchBy`) to a compound. Returns results[]: identity (cid, canonicalSmiles — " +
|
|
23
|
+
"the PubChem connectivity SMILES, with no stereochemistry and no isotopes, thus inchi is the full structure — inchi, inchiKey, iupacName, " +
|
|
23
24
|
"molecularFormula) and computed properties (molecularWeight, xlogp, tpsa, hbondDonorCount, hbondAcceptorCount, rotatableBondCount, complexity). " +
|
|
24
25
|
"'crossrefs' — external registry ids for a `cid`. Returns crossRefs[]: a flat list of { source, id } entries. `source` names the registry that the id pattern " +
|
|
25
26
|
"identifies — ChEMBL, ChEBI, DrugBank, KEGG, HMDB, CAS, PDB or UNII — and it is null for an id of any other registry; filter by `source` for the one you want. " +
|
|
26
27
|
"This is the bridge out of PubChem. " +
|
|
27
28
|
"'assays' — bioassay screening summaries for a `cid`; answers 'has this compound ever been screened, and against what?'. Returns assays[]: aid, assayName, targetName " +
|
|
28
29
|
"(the target accession, for example a UniProt or PDB id), " +
|
|
29
|
-
"activityOutcome (Active / Inactive / Inconclusive / Unspecified), activityValue
|
|
30
|
+
"activityOutcome (Active / Inactive / Inconclusive / Unspecified), activityValue (in µM; null when the assay reports none). Broad HTS " +
|
|
31
|
+
"screening coverage, NOT curated potency — for numbers you intend to quote " +
|
|
30
32
|
"(IC50/Ki), prefer chembl action='bioactivity'."),
|
|
31
33
|
query: z
|
|
32
34
|
.string()
|
package/dist/tools/bio/pubmed.js
CHANGED
|
@@ -34,8 +34,9 @@ const inputSchema = z
|
|
|
34
34
|
action: z
|
|
35
35
|
.enum(["search", "details", "fulltext"])
|
|
36
36
|
.describe("'search' (needs query) — returns totalFound plus, per hit, PMID, title, journal, year and an author summary. " +
|
|
37
|
-
"'details' (needs pmids, max 20 per call) — each article's full abstract, journal, year, DOI, pmcId (non-null
|
|
38
|
-
"
|
|
37
|
+
"'details' (needs pmids, max 20 per call) — each article's full abstract, journal, year, DOI, pmcId (non-null when the article is in " +
|
|
38
|
+
"PubMed Central, which includes articles outside the open-access subset), plus the leading authors and MeSH terms with authorCount / " +
|
|
39
|
+
"meshTermCount giving the true totals. " +
|
|
39
40
|
"'fulltext' (needs pmcId, taken from a 'details' result) — the body of ONE open-access PMC article as plain text with section headers " +
|
|
40
41
|
"preserved, trimmed to maxChars; `outline` lists every section with its size and whether it came back, and truncated says whether " +
|
|
41
42
|
"anything was left out. available: false when the article is not open-access."),
|
|
@@ -122,10 +123,10 @@ export function createPubMedTool(deps) {
|
|
|
122
123
|
"the U.S. National Library of Medicine, through the NCBI E-utilities. The three actions form a chain " +
|
|
123
124
|
"— search, then details on the relevant hits, then fulltext; see the action parameter for what each needs and returns.\n" +
|
|
124
125
|
"ACCEPTED IDENTIFIERS: an Entrez free-text query with optional field tags for 'search' ('PCSK9[Title] AND 2020:2024[dp]'); PMIDs for " +
|
|
125
|
-
"'details' ('34567890'); and a
|
|
126
|
-
"
|
|
126
|
+
"'details' ('34567890'); and a PMC ID for 'fulltext' ('PMC7096066'), never a PMID — the PMC number of an article differs from its PMID. " +
|
|
127
|
+
"PubMed holds MEDLINE and PMC only, thus a method, a model or a preprint outside MEDLINE is often absent here.\n" +
|
|
127
128
|
"Read " +
|
|
128
|
-
"fulltext sparingly — only where the 'details' abstract is not enough, and only for articles that have a pmcId
|
|
129
|
+
"fulltext sparingly — only where the 'details' abstract is not enough, and only for articles that have a pmcId. " +
|
|
129
130
|
"A full article is larger than everything else this tool returns combined, so it comes back trimmed to a character budget: take the " +
|
|
130
131
|
"`outline` from the first call and re-request the specific `sections` you need rather than raising maxChars. " +
|
|
131
132
|
"An empty result set and available: false are expected outcomes, not errors — an article with no PMC record simply has no open full text. " +
|
|
@@ -172,7 +173,10 @@ export function createPubMedTool(deps) {
|
|
|
172
173
|
// "Not open-access" is an expected outcome — a data variant, not an error.
|
|
173
174
|
if (!parsed)
|
|
174
175
|
return ok({ pmcId: input.pmcId, available: false });
|
|
175
|
-
|
|
176
|
+
// `fullText` already holds each admitted section under its heading, thus the
|
|
177
|
+
// `sections` array would send the same text to the model a second time.
|
|
178
|
+
const { sections: _sections, ...bounded } = parsed;
|
|
179
|
+
return ok({ pmcId: input.pmcId, available: true, ...bounded });
|
|
176
180
|
}
|
|
177
181
|
}
|
|
178
182
|
},
|
|
@@ -23,7 +23,8 @@ export const searchClinicalTrialsTool = defineTool({
|
|
|
23
23
|
"protocol prose.\n" +
|
|
24
24
|
"action 'details' takes ONE nctId and returns that study in full: the same trial record plus outcomes[] (each primary/secondary/other outcome " +
|
|
25
25
|
"measure with its numeric effect where the sponsor posted results) and the reported adverse events with their per-arm counts. This is how you " +
|
|
26
|
-
"read a result, as opposed to finding a trial
|
|
26
|
+
"read a result, as opposed to finding a trial. The 'details' answer is not trimmed: a large trial with posted results carries hundreds of " +
|
|
27
|
+
"adverse-event terms, each with its per-arm counts, thus one answer can run to tens of thousands of characters.\n" +
|
|
27
28
|
"ACCEPTED IDENTIFIERS: `query` is free text matched across the whole study record, so a gene symbol ('KRAS'), a drug or brand name ('imatinib', " +
|
|
28
29
|
"'Gleevec'), a condition ('melanoma') or an NCT ID ('NCT00000102') all work. `nctId` takes one NCT ID and nothing else.\n" +
|
|
29
30
|
"`status` is an optional server-side filter and `phase` narrows the returned page. Omit both to see the full landscape. totalFound reports the true " +
|
|
@@ -34,7 +34,8 @@ export const searchGeneTool = defineTool({
|
|
|
34
34
|
"biotype, assembly, description; plus resolvedFrom[] and notFound[]. Gene-level only — no transcripts or exons. Batch up to 200 identifiers per " +
|
|
35
35
|
"call.\n" +
|
|
36
36
|
"ACCEPTED IDENTIFIERS, mixed freely in one call: a HUGO gene symbol ('BRCA1'), an Ensembl gene ID ('ENSG00000012048'), an HGNC ID ('HGNC:1100'), " +
|
|
37
|
-
"a UniProt accession ('P38398') and a ChEMBL target ID ('CHEMBL5619')
|
|
37
|
+
"a six-character UniProt accession ('P38398') and a ChEMBL target ID ('CHEMBL5619'); a ten-character accession ('A0A0B4J1Y9') is read " +
|
|
38
|
+
"as a symbol and lands in notFound. An input that is not a symbol is anchored on the HGNC and UniProt " +
|
|
38
39
|
"registries first, and the approved symbol it yields is reported in resolvedFrom[] — that anchoring is human, so a non-symbol input with a " +
|
|
39
40
|
"non-human `species` looks the ortholog up by the human approved symbol.\n" +
|
|
40
41
|
"notFound is valid no-data (a deprecated, aliased or mistyped identifier, or one this species has no gene for) — report it and continue, do not " +
|
|
@@ -12,12 +12,16 @@ export const searchInteractionsTool = defineTool({
|
|
|
12
12
|
"evidence into one confidence score — for interactions and gene-set functional enrichment. " +
|
|
13
13
|
"'partners' — the one-hop interaction partners of the input proteins, score-sorted: 'what else does this protein work with?'. " +
|
|
14
14
|
"'network' — the interactions AMONG the input proteins only, no new nodes: 'is my gene set actually connected?'. " +
|
|
15
|
-
"'enrichment' — statistical over-representation of
|
|
15
|
+
"'enrichment' — statistical over-representation of terms in the input set against the whole genome, FDR-sorted, over every STRING " +
|
|
16
|
+
"category: GO Process / Function / Component, KEGG, Reactome (RCTM), Pfam, InterPro, UniProt keywords, COMPARTMENTS, DISEASES, HPO, " +
|
|
17
|
+
"WikiPathways and PubMed publications (PMID) among them, with `category` naming the source of each row. It is the enrichment TEST, as " +
|
|
16
18
|
"opposed to lookup_annotation, which just reads a vocabulary. " +
|
|
17
19
|
"ACCEPTED IDENTIFIERS: STRING resolves each one itself, so a HUGO gene symbol ('TP53'), a UniProt accession ('P04637'), an Ensembl protein ID " +
|
|
18
20
|
"('ENSP00000269305') and a full protein name all work, mixed in one call. `species` takes an NCBI Taxonomy ID (9606 = human).\n" +
|
|
19
|
-
"`limit` applies to all three actions and defaults small: a network over N proteins grows with N² edges.
|
|
20
|
-
"
|
|
21
|
+
"`limit` applies to all three actions and defaults small: a network over N proteins grows with N² edges. For 'partners', STRING applies " +
|
|
22
|
+
"it to EACH input protein, thus N inputs give up to N × limit rows. `totalEdges` / `totalTerms` give the pre-trim counts of 'network' and " +
|
|
23
|
+
"'enrichment'; 'partners' gives no total. An empty result is valid no-data (unconnected set, no enriched term, unresolvable identifiers) — " +
|
|
24
|
+
"report it and continue, do " +
|
|
21
25
|
"not retry.",
|
|
22
26
|
inputSchema: z.object({
|
|
23
27
|
identifiers: z.array(z.string()).min(1).max(100).describe("Protein/gene identifiers (e.g. ['TP53', 'BRCA1'])"),
|
|
@@ -36,7 +40,7 @@ export const searchInteractionsTool = defineTool({
|
|
|
36
40
|
.min(1)
|
|
37
41
|
.max(500)
|
|
38
42
|
.default(DEFAULT_LIMIT)
|
|
39
|
-
.describe(`Max records returned — partners, network edges, or enriched terms depending on action (default ${DEFAULT_LIMIT}, max 500). ` +
|
|
43
|
+
.describe(`Max records returned — partners PER INPUT PROTEIN, network edges, or enriched terms depending on action (default ${DEFAULT_LIMIT}, max 500). ` +
|
|
40
44
|
"Rows are ordered best-first (score descending for interactions, FDR ascending for enrichment), so the default keeps the strongest."),
|
|
41
45
|
}),
|
|
42
46
|
describeCall: "none",
|
|
@@ -93,7 +93,8 @@ export const targetSafetyTool = defineTool({
|
|
|
93
93
|
id: "target_safety",
|
|
94
94
|
description: "TARGET-level, mechanism-based safety liability — the curated secondary-pharmacology panel and Open Targets' curated liabilities in one call. " +
|
|
95
95
|
"Judges what engaging a target is likely to do to an organ system, before any specific molecule exists.\n" +
|
|
96
|
-
"ACCEPTED IDENTIFIERS, mixed freely in one call:
|
|
96
|
+
"ACCEPTED IDENTIFIERS, mixed freely in one call: an approved HUGO gene symbol ('KCNH2' — an alias such as 'hERG' matches no panel entry), a " +
|
|
97
|
+
"ChEMBL target ID ('CHEMBL240'), a UniProt accession " +
|
|
97
98
|
"('Q12809') and an Ensembl gene ID ('ENSG00000055118').\n" +
|
|
98
99
|
"This is the safety of the TARGET, not of a drug. For a marketed molecule's post-market adverse events use search_faers; for a chemical's " +
|
|
99
100
|
"toxicology use comptox; for what removing the gene does in vivo use gene_preclinical_profile.\n" +
|
|
@@ -41,7 +41,7 @@ const ShowUserInputSchema = z.object({
|
|
|
41
41
|
dataPath: z
|
|
42
42
|
.string()
|
|
43
43
|
.optional()
|
|
44
|
-
.describe("echart only: analysis-rooted CSV artifact path (e.g. 'runs/run-abc/step-2/output/de-summary.csv') the host loads as the ECharts `dataset.source` at render time. Author `encode`/dimensions against the CSV's column names (read only its header) and omit `dataset.source` from the spec — the rows are never pulled through the context window. If the data is not chart-ready (needs aggregation, filtering, reshaping), do that in a sandbox step that writes a chart-ready CSV. Inline `dataset.source` is only for a handful of numbers you just computed in conversation that exist nowhere as an artifact."),
|
|
44
|
+
.describe("echart only: analysis-rooted CSV artifact path (e.g. 'runs/run-abc/step-2/output/de-summary.csv') the host loads as the ECharts `dataset.source` at render time. Author `encode`/dimensions against the CSV's column names (read only its header) and omit `dataset.source` from the spec — the rows are never pulled through the context window. The tool checks the shape of the path only, not that the file exists; a wrong path fails only when the card renders. If the data is not chart-ready (needs aggregation, filtering, reshaping), do that in a sandbox step that writes a chart-ready CSV. Inline `dataset.source` is only for a handful of numbers you just computed in conversation that exist nowhere as an artifact."),
|
|
45
45
|
body: z.string().optional().describe("Markdown content to render (kind=markdown)"),
|
|
46
46
|
code: z.string().optional().describe("Code content (kind=code)"),
|
|
47
47
|
language: z.string().optional().describe("Language identifier, e.g. r, python, bash (kind=code)"),
|
|
@@ -23,9 +23,17 @@ import { callExtendAnalysisFarm } from "./sandbox/link-packages.js";
|
|
|
23
23
|
const planIdSchema = z.string().regex(/^pln-[a-f0-9]{8}$/, "planId must be a pln-<8hex> value");
|
|
24
24
|
const inputSchema = z
|
|
25
25
|
.object({
|
|
26
|
-
mode: z
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
mode: z
|
|
27
|
+
.enum(["plan", "adhoc"])
|
|
28
|
+
.describe("`plan` runs a stored plan that the user approved. `adhoc` runs one targeted step for an explicit user request."),
|
|
29
|
+
planId: planIdSchema
|
|
30
|
+
.optional()
|
|
31
|
+
.describe("mode=plan only, and required there: the `pln-<8hex>` id of the approved stored plan. Omit it for mode=adhoc."),
|
|
32
|
+
request: z
|
|
33
|
+
.string()
|
|
34
|
+
.min(1)
|
|
35
|
+
.optional()
|
|
36
|
+
.describe("mode=adhoc only, and required there: the computational request of the user, in the words of the user. Omit it for mode=plan."),
|
|
29
37
|
})
|
|
30
38
|
.superRefine((value, ctx) => {
|
|
31
39
|
if (value.mode === "plan" && (value.planId === undefined || value.request !== undefined)) {
|
|
@@ -319,7 +327,9 @@ export function createExecuteAnalysisTool(deps) {
|
|
|
319
327
|
description: "Launch analysis computation asynchronously. Use mode=plan with the planId only after the user approved that stored plan. " +
|
|
320
328
|
"Use mode=adhoc with the user's exact targeted computational request when they explicitly asked to run/compute/test/compare it; " +
|
|
321
329
|
"that explicit request is consent and needs no synthetic-plan approval. If computation is merely your suggestion, ask first. " +
|
|
322
|
-
"You never choose the sandbox specialist: ad hoc routing does that automatically. Returns runId with status=in_progress; inspect results on a later turn."
|
|
330
|
+
"You never choose the sandbox specialist: ad hoc routing does that automatically. Returns runId with status=in_progress; inspect results on a later turn. " +
|
|
331
|
+
"A plan that already has an active run returns that run, so a second run of one plan does not start. " +
|
|
332
|
+
"The launch fails with an error, and no run starts, when the plan is not found or fails validation, when a package of the plan cannot link, or when the run is not authorized.",
|
|
323
333
|
inputSchema,
|
|
324
334
|
// The mode is what a user most needs to see here — an approved plan
|
|
325
335
|
// running is a different event from an ad hoc request being routed.
|
|
@@ -22,10 +22,15 @@ export interface Association {
|
|
|
22
22
|
somaticMutationScore: number | null;
|
|
23
23
|
literaturePmids: string[];
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* The positive tractability buckets of each modality, by their Open Targets label
|
|
27
|
+
* ('Approved Drug', 'Structure with Ligand', 'UniProt loc high conf', …). An empty
|
|
28
|
+
* list means no positive bucket. `null` means Open Targets gives no row for the modality.
|
|
29
|
+
*/
|
|
25
30
|
export interface TargetTractability {
|
|
26
|
-
smallMolecule:
|
|
27
|
-
antibody:
|
|
28
|
-
otherModalities:
|
|
31
|
+
smallMolecule: string[] | null;
|
|
32
|
+
antibody: string[] | null;
|
|
33
|
+
otherModalities: string[] | null;
|
|
29
34
|
}
|
|
30
35
|
export interface TargetInfo {
|
|
31
36
|
ensemblId: string;
|
|
@@ -277,10 +277,19 @@ export async function searchTargetAssociations(ensemblId, limit = 25) {
|
|
|
277
277
|
if (!target)
|
|
278
278
|
return null;
|
|
279
279
|
const tractabilityEntries = target.tractability ?? [];
|
|
280
|
+
// Open Targets gives one row for each bucket of a modality, from 'Approved Drug' down to a
|
|
281
|
+
// predicted pocket or a localization. The first row alone is the 'Approved Drug' bucket, thus
|
|
282
|
+
// each modality keeps the labels of all of its positive rows.
|
|
283
|
+
const positiveLabels = (modality) => {
|
|
284
|
+
const rows = tractabilityEntries.filter((t) => t.modality === modality);
|
|
285
|
+
if (rows.length === 0)
|
|
286
|
+
return null;
|
|
287
|
+
return rows.flatMap((t) => (t.value === true && t.label ? [t.label] : []));
|
|
288
|
+
};
|
|
280
289
|
const tractability = {
|
|
281
|
-
smallMolecule:
|
|
282
|
-
antibody:
|
|
283
|
-
otherModalities:
|
|
290
|
+
smallMolecule: positiveLabels("SM"),
|
|
291
|
+
antibody: positiveLabels("AB"),
|
|
292
|
+
otherModalities: positiveLabels("OC"),
|
|
284
293
|
};
|
|
285
294
|
const rows = data.associations?.associatedDiseases?.rows ?? [];
|
|
286
295
|
const associations = rows.map((row) => ({
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { ok } from "neverthrow";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { unwrapOrThrow } from "../lib/result.js";
|
|
8
|
-
import { pairSafeIndex, READ_TOOL_OUTPUT_TOOL_ID, TOOL_RESULT_CAP } from "../loop/tool-output.js";
|
|
8
|
+
import { pairSafeIndex, READ_TOOL_OUTPUT_TOOL_ID, TOOL_OUTPUT_KEEP_MAX, TOOL_RESULT_CAP } from "../loop/tool-output.js";
|
|
9
9
|
import { defineTool } from "./define-tool.js";
|
|
10
10
|
const DEFAULT_PAGE_CHARS = 8 * 1024;
|
|
11
11
|
const MAX_PAGE_CHARS = 16 * 1024;
|
|
@@ -105,9 +105,12 @@ function search(ref, text, start, limit, pattern) {
|
|
|
105
105
|
export function createReadToolOutputTool(store) {
|
|
106
106
|
return defineTool({
|
|
107
107
|
id: READ_TOOL_OUTPUT_TOOL_ID,
|
|
108
|
-
description: "Read the
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
description: "Read the kept text of a tool result that came back as an excerpt. A tool result longer than " +
|
|
109
|
+
`${TOOL_RESULT_CAP} characters comes back as its start and its end, and the excerpt gives a reference when the harness kept the text. ` +
|
|
110
|
+
`Pass that reference as \`ref\`. The harness keeps at most ${TOOL_OUTPUT_KEEP_MAX} characters. Of a longer text, it keeps the first ` +
|
|
111
|
+
"half and the last half of that bound, with a line between them that gives the count of characters not kept. " +
|
|
112
|
+
"A reference that the harness did not keep gives the status `not_found`, and the rest of that result cannot be read. " +
|
|
113
|
+
"Read a window with `offset` and `limit`: an offset counts the characters " +
|
|
111
114
|
`(UTF-16 code units) of the kept text from 0, and a page has at most ${MAX_PAGE_CHARS} characters, ` +
|
|
112
115
|
`${DEFAULT_PAGE_CHARS} by default. Or give \`pattern\`, a JavaScript regular expression, to get at most ` +
|
|
113
116
|
`${MAX_MATCHES} matches, each with its offset and the text around it. When a result gives \`more: true\`, ` +
|
|
@@ -54,13 +54,20 @@ const blockPayload = z.union([AuthoringBlockSchema, z.unknown()]);
|
|
|
54
54
|
* present. Under `.optional()` each of those nulls fails the input parse, and the block never lands.
|
|
55
55
|
*/
|
|
56
56
|
const absentable = (schema) => schema.nullish();
|
|
57
|
+
/** The meaning of each flat destination field. The add and the move share it. */
|
|
58
|
+
const DESTINATION_NOTES = {
|
|
59
|
+
parentId: "The id of the section that holds the destination. Leave it out for the root. An anchor in `before` or `after` implies its parent.",
|
|
60
|
+
place: "Put the block at the `start` or the `end` of the parent. The default is `end`. Give a place or an anchor, not both.",
|
|
61
|
+
before: "The id of a sibling block. The block lands directly before it.",
|
|
62
|
+
after: "The id of a sibling block. The block lands directly after it.",
|
|
63
|
+
};
|
|
57
64
|
/** The flat destination fields. An anchor is `before` or `after`, and a place is `start` or `end`. */
|
|
58
65
|
const addBlockInput = z.object({
|
|
59
66
|
block: blockPayload,
|
|
60
|
-
parentId: absentable(z.string()),
|
|
61
|
-
place: absentable(z.enum(["start", "end"])),
|
|
62
|
-
before: absentable(z.string()),
|
|
63
|
-
after: absentable(z.string()),
|
|
67
|
+
parentId: absentable(z.string()).describe(DESTINATION_NOTES.parentId),
|
|
68
|
+
place: absentable(z.enum(["start", "end"])).describe(DESTINATION_NOTES.place),
|
|
69
|
+
before: absentable(z.string()).describe(DESTINATION_NOTES.before),
|
|
70
|
+
after: absentable(z.string()).describe(DESTINATION_NOTES.after),
|
|
64
71
|
});
|
|
65
72
|
/**
|
|
66
73
|
* The change payload is a title for a section target, or a block for an atom target, and exactly one of
|
|
@@ -68,27 +75,27 @@ const addBlockInput = z.object({
|
|
|
68
75
|
* `z.unknown()` as a required key, thus an optional marker is what makes the retitle call representable.
|
|
69
76
|
*/
|
|
70
77
|
const changeBlockInput = z.object({
|
|
71
|
-
targetId: z.string(),
|
|
72
|
-
title: absentable(z.string()),
|
|
73
|
-
block: blockPayload.optional(),
|
|
78
|
+
targetId: z.string().describe("The id of the block to change, as the outline gives it."),
|
|
79
|
+
title: absentable(z.string()).describe("The new title of a section target. Give a title or a block, not both."),
|
|
80
|
+
block: blockPayload.optional().describe("The replacement of an atom target. Give a title or a block, not both."),
|
|
74
81
|
});
|
|
75
82
|
const removeBlockInput = z.object({
|
|
76
|
-
targetId: z.string(),
|
|
83
|
+
targetId: z.string().describe("The id of the block to remove, as the outline gives it."),
|
|
77
84
|
});
|
|
78
85
|
const moveBlockInput = z.object({
|
|
79
|
-
targetId: z.string(),
|
|
80
|
-
parentId: absentable(z.string()),
|
|
81
|
-
place: absentable(z.enum(["start", "end"])),
|
|
82
|
-
before: absentable(z.string()),
|
|
83
|
-
after: absentable(z.string()),
|
|
86
|
+
targetId: z.string().describe("The id of the block to move, as the outline gives it."),
|
|
87
|
+
parentId: absentable(z.string()).describe(DESTINATION_NOTES.parentId),
|
|
88
|
+
place: absentable(z.enum(["start", "end"])).describe(DESTINATION_NOTES.place),
|
|
89
|
+
before: absentable(z.string()).describe(DESTINATION_NOTES.before),
|
|
90
|
+
after: absentable(z.string()).describe(DESTINATION_NOTES.after),
|
|
84
91
|
});
|
|
85
92
|
const readOutlineInput = z.object({});
|
|
86
93
|
/** The read names its target with the same field name as each mutation, thus one id key serves them all. */
|
|
87
94
|
const readBlockInput = z.object({
|
|
88
|
-
targetId: z.string(),
|
|
95
|
+
targetId: z.string().describe("The id of the block to read, as the outline gives it."),
|
|
89
96
|
});
|
|
90
97
|
const setTitleInput = z.object({
|
|
91
|
-
title: z.string().min(1),
|
|
98
|
+
title: z.string().min(1).describe("The title of the whole report."),
|
|
92
99
|
});
|
|
93
100
|
const finishDraftInput = z.object({});
|
|
94
101
|
/**
|
|
@@ -144,7 +144,11 @@ export function createDeriveTableTool(deps) {
|
|
|
144
144
|
"That directory is the one place that the script can write: the analysis mounts read-only, and the container has no network. " +
|
|
145
145
|
"The standard output carries the logs of the script alone, thus a table that it prints there reaches nobody. " +
|
|
146
146
|
"The derived table joins the pinned evidence of this session, thus a block binds its path the same way as a pinned artifact. " +
|
|
147
|
-
"A name that this session already derived is refused, thus a second derivation of one table takes a new name."
|
|
147
|
+
"A name that this session already derived is refused, thus a second derivation of one table takes a new name. " +
|
|
148
|
+
`Limits: the script holds at most ${String(SCRIPT_CAP_BYTES / 1024)} KiB, inputs holds at most ${String(INPUT_CAP)} paths, ` +
|
|
149
|
+
`and the run stops after ${String(DERIVATION_DEADLINE_MS / 1000)} seconds. ` +
|
|
150
|
+
"A failed script gives back exec-failed with the tail of its standard error. " +
|
|
151
|
+
"The outcome unavailable means that this host cannot run a derivation, and a repeat gives the same outcome.",
|
|
148
152
|
inputSchema: deriveTableInput,
|
|
149
153
|
executionMode: "inline",
|
|
150
154
|
describeCall: (input) => `derive ${input.output}`,
|
|
@@ -226,13 +226,14 @@ export function createExaminePageTool(deps) {
|
|
|
226
226
|
const releaseMs = deps.deadlines?.releaseMs ?? RELEASE_DEADLINE_MS;
|
|
227
227
|
return defineTool({
|
|
228
228
|
id: "examine_page",
|
|
229
|
-
description: "Open the rendered report page in a real headless browser, and
|
|
230
|
-
"
|
|
229
|
+
description: "Open the rendered report page in a real headless browser, and give back what a reviewer sees: " +
|
|
230
|
+
"one or more screenshots, the coverage of the look, the console errors, and the failed requests. " +
|
|
231
231
|
"A tall page arrives as consecutive top-to-bottom slices in document order, and the tiles list names the rows of each slice. " +
|
|
232
232
|
"The coverage says whether the pictures hold the whole page, a truncated top portion of it, or the top window alone. " +
|
|
233
233
|
"Run it after the preview to look at the current page, and to confirm that the layout and the charts read clean. " +
|
|
234
|
-
"
|
|
235
|
-
"
|
|
234
|
+
"A look at the page of the current draft is what lets record_report_version record. " +
|
|
235
|
+
"The outcomes no-page and missed-stamp mean that no preview rendered the current page: run preview_report, then look again. " +
|
|
236
|
+
"The outcome no-browser means that this host gives no browser, and a repeat gives the same outcome.",
|
|
236
237
|
inputSchema: examinePageInput,
|
|
237
238
|
executionMode: "inline",
|
|
238
239
|
describeCall: "none",
|
|
@@ -393,9 +393,14 @@ export function createPreviewReportTool(deps) {
|
|
|
393
393
|
const resolvePageAsset = deps.resolvePageAsset ?? resolvePageAssetFromInstallation;
|
|
394
394
|
return defineTool({
|
|
395
395
|
id: "preview_report",
|
|
396
|
-
description: "Render the current draft to
|
|
396
|
+
description: "Render the current draft to an HTML page in the session directory, and give back the page path. " +
|
|
397
|
+
"The page loads its figures, table data, and libraries from an assets directory that the tool stages beside it. " +
|
|
397
398
|
"The tool finishes the draft first: an incomplete draft gives back the gap list, and no page renders. " +
|
|
398
399
|
"On a pass it resolves each reference, stages each bound image beside the page, and writes the page. " +
|
|
400
|
+
"An unresolved reference gives back the block and the binding to repair. " +
|
|
401
|
+
"The outcome resolver-unavailable means that this host gives no reference resolver, and a repeat gives the same outcome. " +
|
|
402
|
+
"A rendered page carries pagePath, and access when the host serves the page: a URL with its expiry, or the reason that no URL was granted. " +
|
|
403
|
+
"The render marks the page that examine_page looks at next. " +
|
|
399
404
|
"Use it to see the report, and to confirm that each reference resolves.",
|
|
400
405
|
inputSchema: previewReportInput,
|
|
401
406
|
executionMode: "inline",
|
|
@@ -128,7 +128,8 @@ export function createRecordVersionTool(deps) {
|
|
|
128
128
|
id: "record_report_version",
|
|
129
129
|
description: "Record the current draft as one report version. The tool runs the whole gate first: it finishes the draft, " +
|
|
130
130
|
"resolves each reference, matches each chart encoding, and matches each assert. An incomplete draft gives back the gap list, " +
|
|
131
|
-
"and a failed reference gives back the block that broke. The tool records a version only after
|
|
131
|
+
"and a failed reference gives back the block that broke. The tool records a version only after examine_page looked at the page of the current draft: " +
|
|
132
|
+
"with no look it gives back never-seen, and after an edit since the look it gives back stale-look. " +
|
|
132
133
|
"A thread holds one version, thus a later record replaces it whole. Amend the draft, look at the page again, and record again. " +
|
|
133
134
|
"After the version lands, the tool removes the file of each derived table that no block of the recorded report binds.",
|
|
134
135
|
inputSchema: recordVersionInput,
|
|
@@ -43,7 +43,10 @@ function renderSnippets(snippets) {
|
|
|
43
43
|
}
|
|
44
44
|
export const resolveLibraryIdTool = defineTool({
|
|
45
45
|
id: "resolve_library_id",
|
|
46
|
-
description: "Resolve a package name (e.g., 'scanpy', 'DESeq2', 'scikit-learn') to
|
|
46
|
+
description: "Resolve a package name (e.g., 'scanpy', 'DESeq2', 'scikit-learn') to the Context7 library ID that `query_docs` needs. " +
|
|
47
|
+
"Returns the single best match as {found: true, libraryId, name, description}, or {found: false} when Context7 holds no library for the name. " +
|
|
48
|
+
"`query` ranks the candidates and only the top one comes back, thus make sure that `name` is the package you meant. " +
|
|
49
|
+
"A failed Context7 request returns an error result.",
|
|
47
50
|
inputSchema: z.object({
|
|
48
51
|
libraryName: z.string().describe("Package or library name to search for (e.g., 'scanpy', 'pysam', 'lifelines')."),
|
|
49
52
|
query: z.string().describe("What you need help with — used to rank results by relevance (e.g., 'how to run differential expression')."),
|
|
@@ -69,7 +72,10 @@ export const resolveLibraryIdTool = defineTool({
|
|
|
69
72
|
});
|
|
70
73
|
export const queryDocsTool = defineTool({
|
|
71
74
|
id: "query_docs",
|
|
72
|
-
description: "Query up-to-date documentation and code examples for a library.
|
|
75
|
+
description: "Query up-to-date documentation and code examples for a library. The libraryId comes from `resolve_library_id`. " +
|
|
76
|
+
"Returns {found: true, documentation}: Markdown snippets, each with a title, a source id, a description, and code blocks. " +
|
|
77
|
+
"Returns {found: false} when Context7 holds no library at that ID or no snippet for the query. A failed Context7 request returns an error result. " +
|
|
78
|
+
"Use this to verify function signatures, parameters, and usage patterns before writing code.",
|
|
73
79
|
inputSchema: z.object({
|
|
74
80
|
libraryId: z.string().describe("Context7 library ID exactly as `resolve_library_id` gave it — an owner and a library, for example '/scverse/scanpy'."),
|
|
75
81
|
query: z.string().describe("Specific question about the library (e.g., 'rank_genes_groups function parameters and usage')."),
|