@inflexa-ai/harness 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/conversation-agent.d.ts +3 -0
- package/dist/agents/conversation-agent.js +5 -2
- package/dist/agents/report-builder.d.ts +2 -0
- package/dist/agents/report-builder.js +2 -0
- package/dist/agents/sandbox/shared.d.ts +3 -0
- package/dist/agents/sandbox/shared.js +5 -0
- package/dist/agents/sandbox/types.d.ts +1 -1
- package/dist/app/chat-turn.d.ts +2 -0
- package/dist/app/chat-turn.js +11 -2
- package/dist/app/spawn-report-session.d.ts +85 -0
- package/dist/app/spawn-report-session.js +83 -0
- package/dist/app/synthesize-run.d.ts +3 -0
- package/dist/app/synthesize-run.js +2 -1
- package/dist/citations/__fixtures__/resolver.d.ts +3 -0
- package/dist/citations/__fixtures__/resolver.js +9 -0
- package/dist/citations/aggregate.d.ts +3 -0
- package/dist/citations/aggregate.js +44 -0
- package/dist/citations/clients/arxiv.d.ts +6 -0
- package/dist/citations/clients/arxiv.js +34 -0
- package/dist/citations/clients/common.d.ts +11 -0
- package/dist/citations/clients/common.js +43 -0
- package/dist/citations/clients/crossref.d.ts +8 -0
- package/dist/citations/clients/crossref.js +98 -0
- package/dist/citations/clients/doi-registry.d.ts +3 -0
- package/dist/citations/clients/doi-registry.js +95 -0
- package/dist/citations/clients/pubmed.d.ts +6 -0
- package/dist/citations/clients/pubmed.js +83 -0
- package/dist/citations/clients/semantic-scholar.d.ts +6 -0
- package/dist/citations/clients/semantic-scholar.js +60 -0
- package/dist/citations/compare.d.ts +9 -0
- package/dist/citations/compare.js +72 -0
- package/dist/citations/match.d.ts +11 -0
- package/dist/citations/match.js +173 -0
- package/dist/citations/normalize.d.ts +31 -0
- package/dist/citations/normalize.js +187 -0
- package/dist/citations/plan.d.ts +2 -0
- package/dist/citations/plan.js +59 -0
- package/dist/citations/resolve.d.ts +28 -0
- package/dist/citations/resolve.js +238 -0
- package/dist/citations/types.d.ts +583 -0
- package/dist/citations/types.js +129 -0
- package/dist/contracts/chat-events.d.ts +9 -0
- package/dist/contracts/index.d.ts +6 -2
- package/dist/contracts/index.js +3 -1
- package/dist/contracts/organ-system.d.ts +49 -0
- package/dist/contracts/organ-system.js +67 -0
- package/dist/contracts/report-blocks.d.ts +564 -0
- package/dist/contracts/report-blocks.js +117 -0
- package/dist/contracts/report-reference.d.ts +459 -0
- package/dist/contracts/report-reference.js +256 -0
- package/dist/contracts/schemas/chat-events.d.ts +2 -0
- package/dist/contracts/schemas/chat-events.js +2 -0
- package/dist/contracts/schemas/chat-parts.d.ts +8 -8
- package/dist/contracts/severity.d.ts +15 -0
- package/dist/contracts/severity.js +10 -0
- package/dist/contracts/target-dossier.d.ts +8673 -12970
- package/dist/contracts/target-dossier.js +534 -591
- package/dist/data/safety-panel-schema.d.ts +18 -5
- package/dist/data/safety-panel-schema.js +4 -4
- package/dist/execution/artifact-metadata.js +1 -0
- package/dist/execution/report-runner.js +1 -3
- package/dist/execution/run-synthesis.d.ts +3 -0
- package/dist/execution/run-synthesis.js +3 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.js +22 -0
- package/dist/lib/async-utils.d.ts +16 -4
- package/dist/lib/async-utils.js +20 -15
- package/dist/lib/cache.d.ts +8 -0
- package/dist/lib/cache.js +35 -0
- package/dist/lib/host-concurrency.js +1 -0
- package/dist/lib/rate-limit.d.ts +35 -0
- package/dist/lib/rate-limit.js +121 -0
- package/dist/literature/sources/arxiv.d.ts +22 -0
- package/dist/literature/sources/arxiv.js +103 -0
- package/dist/literature/sources/http.d.ts +28 -0
- package/dist/literature/sources/http.js +97 -0
- package/dist/literature/sources/pubmed.d.ts +68 -0
- package/dist/literature/sources/pubmed.js +266 -0
- package/dist/literature/sources/semantic-scholar.d.ts +24 -0
- package/dist/literature/sources/semantic-scholar.js +71 -0
- package/dist/loop/__fixtures__/scripted-provider.js +23 -2
- package/dist/loop/run-agent.js +58 -10
- package/dist/loop/tool-detail.d.ts +40 -0
- package/dist/loop/tool-detail.js +35 -3
- package/dist/loop/types.d.ts +13 -0
- package/dist/memory/thread-history.d.ts +10 -0
- package/dist/memory/thread-history.js +12 -1
- package/dist/memory/thread-store.d.ts +142 -25
- package/dist/memory/thread-store.js +228 -40
- package/dist/prompts/literature-reviewer.d.ts +1 -1
- package/dist/prompts/literature-reviewer.js +2 -0
- package/dist/prompts/target-assessment/briefs/executive-recommendation.d.ts +1 -1
- package/dist/prompts/target-assessment/briefs/executive-recommendation.js +5 -5
- package/dist/prompts/target-assessment/briefs/liability-bullets.d.ts +1 -1
- package/dist/prompts/target-assessment/briefs/liability-bullets.js +1 -1
- package/dist/prompts/target-assessment/briefs/target-organ-liabilities.d.ts +1 -1
- package/dist/prompts/target-assessment/briefs/target-organ-liabilities.js +11 -3
- package/dist/prompts/target-assessment/investigation/adversarial-critique.d.ts +1 -0
- package/dist/prompts/target-assessment/investigation/adversarial-critique.js +67 -0
- package/dist/prompts/target-assessment/investigation/claim-reverification.d.ts +1 -0
- package/dist/prompts/target-assessment/investigation/claim-reverification.js +49 -0
- package/dist/prompts/target-assessment/investigation/index.d.ts +3 -0
- package/dist/prompts/target-assessment/investigation/index.js +3 -0
- package/dist/prompts/target-assessment/investigation/mechanism-proposal.d.ts +1 -0
- package/dist/prompts/target-assessment/investigation/mechanism-proposal.js +49 -0
- package/dist/prompts/target-assessment/tox-voice/index.d.ts +1 -1
- package/dist/prompts/target-assessment/tox-voice/vocabulary.d.ts +8 -5
- package/dist/prompts/target-assessment/tox-voice/vocabulary.js +9 -20
- package/dist/providers/__fixtures__/session.d.ts +5 -4
- package/dist/providers/__fixtures__/session.js +4 -4
- package/dist/report-model/block-walk.d.ts +56 -0
- package/dist/report-model/block-walk.js +89 -0
- package/dist/report-model/draft-finish.d.ts +60 -0
- package/dist/report-model/draft-finish.js +56 -0
- package/dist/report-model/draft-operations.d.ts +125 -0
- package/dist/report-model/draft-operations.js +341 -0
- package/dist/report-model/draft-read.d.ts +59 -0
- package/dist/report-model/draft-read.js +109 -0
- package/dist/report-model/draft.d.ts +51 -0
- package/dist/report-model/draft.js +32 -0
- package/dist/report-model/fixture-resolver.d.ts +8 -0
- package/dist/report-model/fixture-resolver.js +252 -0
- package/dist/report-model/mint-snapshot.d.ts +30 -0
- package/dist/report-model/mint-snapshot.js +40 -0
- package/dist/report-model/reference-resolver.d.ts +113 -0
- package/dist/report-model/reference-resolver.js +60 -0
- package/dist/report-model/structural-validation.d.ts +22 -0
- package/dist/report-model/structural-validation.js +68 -0
- package/dist/report-model/validate.d.ts +40 -0
- package/dist/report-model/validate.js +80 -0
- package/dist/report-render/chart.d.ts +33 -0
- package/dist/report-render/chart.js +538 -0
- package/dist/report-render/page.d.ts +127 -0
- package/dist/report-render/page.js +307 -0
- package/dist/report-render/references.d.ts +32 -0
- package/dist/report-render/references.js +44 -0
- package/dist/report-render/render.d.ts +25 -0
- package/dist/report-render/render.js +125 -0
- package/dist/report-render/script-json.d.ts +13 -0
- package/dist/report-render/script-json.js +15 -0
- package/dist/report-render/types.d.ts +39 -0
- package/dist/report-render/types.js +10 -0
- package/dist/report-render/views/chart-view.d.ts +11 -0
- package/dist/report-render/views/chart-view.js +23 -0
- package/dist/report-render/views/page-view.d.ts +20 -0
- package/dist/report-render/views/page-view.js +49 -0
- package/dist/report-render/views/prose.d.ts +28 -0
- package/dist/report-render/views/prose.js +75 -0
- package/dist/report-render/views/references-view.d.ts +20 -0
- package/dist/report-render/views/references-view.js +64 -0
- package/dist/report-render/views/values.d.ts +42 -0
- package/dist/report-render/views/values.js +43 -0
- package/dist/runtime/assemble.d.ts +50 -4
- package/dist/runtime/assemble.js +47 -3
- package/dist/runtime/boot.d.ts +3 -3
- package/dist/runtime/boot.js +22 -5
- package/dist/runtime/dbos.d.ts +4 -4
- package/dist/state/artifacts.d.ts +16 -0
- package/dist/state/artifacts.js +18 -0
- package/dist/state/init.js +77 -1
- package/dist/state/purge-analysis.js +1 -0
- package/dist/state/report-versions.d.ts +132 -0
- package/dist/state/report-versions.js +194 -0
- package/dist/state/schema.d.ts +2 -2
- package/dist/tasks/data-profile.js +1 -0
- package/dist/tools/bio/chembl.js +1 -0
- package/dist/tools/bio/comptox.js +1 -0
- package/dist/tools/bio/drug-gene-interactions.js +1 -0
- package/dist/tools/bio/gene-disease-evidence.js +1 -0
- package/dist/tools/bio/gene-preclinical-profile.d.ts +1 -1
- package/dist/tools/bio/gene-preclinical-profile.js +1 -0
- package/dist/tools/bio/keys.d.ts +4 -2
- package/dist/tools/bio/keys.js +3 -2
- package/dist/tools/bio/lookup-annotation.js +1 -0
- package/dist/tools/bio/opentargets.d.ts +3 -3
- package/dist/tools/bio/opentargets.js +4 -3
- package/dist/tools/bio/pubchem.js +1 -0
- package/dist/tools/bio/pubmed.js +4 -4
- package/dist/tools/bio/search-clinical-trials.js +1 -0
- package/dist/tools/bio/search-faers.js +1 -0
- package/dist/tools/bio/search-geo-datasets.js +1 -0
- package/dist/tools/bio/search-interactions.js +1 -0
- package/dist/tools/bio/target-safety.d.ts +4 -6
- package/dist/tools/bio/target-safety.js +7 -6
- package/dist/tools/define-tool.d.ts +19 -12
- package/dist/tools/define-tool.js +4 -2
- package/dist/tools/display/show-user.js +1 -0
- package/dist/tools/iterate-report.js +2 -0
- package/dist/tools/lib/chembl-client.d.ts +2 -0
- package/dist/tools/lib/chembl-client.js +1 -0
- package/dist/tools/lib/monarch-client.d.ts +42 -0
- package/dist/tools/lib/monarch-client.js +100 -0
- package/dist/tools/lib/ncbi-utils.d.ts +3 -58
- package/dist/tools/lib/ncbi-utils.js +2 -286
- package/dist/tools/lib/pubmed-client.d.ts +5 -12
- package/dist/tools/lib/pubmed-client.js +20 -79
- package/dist/tools/report/build-report.js +1 -0
- package/dist/tools/report/mint-preview-url.js +1 -0
- package/dist/tools/report/preview-snapshot.js +1 -0
- package/dist/tools/report/submit-report.js +1 -0
- package/dist/tools/report/version-fs.js +4 -0
- package/dist/tools/report-authoring/authoring-tools.d.ts +134 -0
- package/dist/tools/report-authoring/authoring-tools.js +352 -0
- package/dist/tools/research/context7-docs.js +2 -0
- package/dist/tools/research/generate-analogy-report.js +3 -2
- package/dist/tools/research/generate-plan.js +3 -0
- package/dist/tools/research/index.d.ts +1 -0
- package/dist/tools/research/index.js +1 -0
- package/dist/tools/research/inspect-data-profile.js +4 -0
- package/dist/tools/research/inspect-run.d.ts +1 -1
- package/dist/tools/research/literature-reviewer.d.ts +3 -0
- package/dist/tools/research/literature-reviewer.js +3 -0
- package/dist/tools/research/resolve-citation.d.ts +3 -0
- package/dist/tools/research/resolve-citation.js +15 -0
- package/dist/tools/research/search-arxiv.d.ts +3 -19
- package/dist/tools/research/search-arxiv.js +10 -105
- package/dist/tools/research/search-github-repos.js +1 -0
- package/dist/tools/research/search-semantic-scholar.d.ts +8 -33
- package/dist/tools/research/search-semantic-scholar.js +30 -88
- package/dist/tools/sandbox/list-available-packages.js +23 -0
- package/dist/tools/sandbox/list-available-refs.js +37 -2
- package/dist/tools/sandbox/report-blocker.js +1 -0
- package/dist/tools/sandbox/skills.js +2 -0
- package/dist/tools/workspace/file-stat.js +1 -0
- package/dist/tools/workspace/show-file.js +2 -0
- package/dist/tools/workspace/show-plan.js +1 -0
- package/dist/workflows/execute-analysis.d.ts +8 -1
- package/dist/workflows/execute-analysis.js +6 -5
- package/dist/workflows/execute-target-assessment.d.ts +13 -2
- package/dist/workflows/execute-target-assessment.js +78 -16
- package/dist/workflows/sandbox-step.d.ts +5 -0
- package/dist/workflows/sandbox-step.js +1 -0
- package/dist/workflows/target-assessment/assemblers/index.d.ts +1 -1
- package/dist/workflows/target-assessment/assemblers/index.js +1 -1
- package/dist/workflows/target-assessment/assemblers/literature.d.ts +9 -27
- package/dist/workflows/target-assessment/assemblers/literature.js +32 -48
- package/dist/workflows/target-assessment/assemblers/orchestrator.d.ts +17 -9
- package/dist/workflows/target-assessment/assemblers/orchestrator.js +195 -85
- package/dist/workflows/target-assessment/assemblers/safety.d.ts +70 -48
- package/dist/workflows/target-assessment/assemblers/safety.js +125 -42
- package/dist/workflows/target-assessment/collectors/index.d.ts +5 -2
- package/dist/workflows/target-assessment/collectors/index.js +48 -5
- package/dist/workflows/target-assessment/coverage.d.ts +43 -27
- package/dist/workflows/target-assessment/coverage.js +33 -29
- package/dist/workflows/target-assessment/fanout/index.js +1 -0
- package/dist/workflows/target-assessment/investigation/index.d.ts +97 -0
- package/dist/workflows/target-assessment/investigation/index.js +419 -0
- package/dist/workflows/target-assessment/lib/approval-precedents.d.ts +26 -5
- package/dist/workflows/target-assessment/lib/approval-precedents.js +95 -29
- package/dist/workflows/target-assessment/lib/citation-validator.js +1 -1
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +2 -2
- package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.d.ts +2 -3
- package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.js +0 -4
- package/dist/workflows/target-assessment/lib/compute-derived.d.ts +6 -5
- package/dist/workflows/target-assessment/lib/compute-derived.js +14 -5
- package/dist/workflows/target-assessment/lib/family-complex-supplement.d.ts +5 -6
- package/dist/workflows/target-assessment/lib/family-complex-supplement.js +2 -3
- package/dist/workflows/target-assessment/lib/fda-label-safety.d.ts +72 -0
- package/dist/workflows/target-assessment/lib/fda-label-safety.js +166 -0
- package/dist/workflows/target-assessment/lib/heterodimer-filter.d.ts +0 -9
- package/dist/workflows/target-assessment/lib/heterodimer-filter.js +0 -10
- package/dist/workflows/target-assessment/lib/hpo-organ-map.d.ts +34 -0
- package/dist/workflows/target-assessment/lib/hpo-organ-map.js +63 -0
- package/dist/workflows/target-assessment/lib/impc-organ-map.d.ts +19 -0
- package/dist/workflows/target-assessment/lib/impc-organ-map.js +39 -0
- package/dist/workflows/target-assessment/lib/meddra-organ-map.d.ts +24 -14
- package/dist/workflows/target-assessment/lib/meddra-organ-map.js +59 -47
- package/dist/workflows/target-assessment/lib/recommendation-sanitizer.js +1 -1
- package/dist/workflows/target-assessment/lib/regulatory-actions.d.ts +2 -3
- package/dist/workflows/target-assessment/lib/regulatory-actions.js +33 -4
- package/dist/workflows/target-assessment/lib/safety-corroboration.d.ts +44 -0
- package/dist/workflows/target-assessment/lib/safety-corroboration.js +273 -0
- package/dist/workflows/target-assessment/lib/target-identity-filter.d.ts +5 -7
- package/dist/workflows/target-assessment/lib/target-identity-filter.js +5 -7
- package/dist/workflows/target-assessment/metrics.d.ts +1 -6
- package/dist/workflows/target-assessment/metrics.js +1 -13
- package/dist/workflows/target-assessment/phase4-assemble.d.ts +2149 -809
- package/dist/workflows/target-assessment/phase4-assemble.js +19 -19
- package/dist/workflows/target-assessment/phase5-persist.d.ts +20 -21
- package/dist/workflows/target-assessment/phase5-persist.js +56 -173
- package/dist/workflows/target-assessment/schemas.d.ts +48 -3
- package/dist/workflows/target-assessment/schemas.js +19 -0
- package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.d.ts +1 -0
- package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.js +2 -0
- package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +27 -63
- package/dist/workflows/target-assessment/steps/phase2-aggregate.js +0 -4
- package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +28 -155
- package/dist/workflows/target-assessment/steps/phase3-aggregate.js +0 -6
- package/dist/workflows/target-assessment/steps/synthesis/diagnostics.d.ts +0 -6
- package/dist/workflows/target-assessment/steps/synthesis/diagnostics.js +0 -18
- package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.d.ts +1 -0
- package/dist/workflows/target-assessment/synthesis/index.d.ts +52 -5
- package/dist/workflows/target-assessment/synthesis/index.js +4 -1
- package/dist/workflows/target-assessment/synthesis-probes.d.ts +3 -3
- package/package.json +4 -1
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The report version store -- the append-only record of report versions.
|
|
3
|
+
*
|
|
4
|
+
* A version is one immutable row. It holds the block document, the pinned
|
|
5
|
+
* snapshot, and the anchor. The store records a version and reads a version. It
|
|
6
|
+
* never updates a recorded row, thus a correction is a new version.
|
|
7
|
+
*
|
|
8
|
+
* A thread holds at most one version, and a named unique constraint on the thread
|
|
9
|
+
* id enforces it. The record reads nothing before the insert. A second record for
|
|
10
|
+
* one thread trips the constraint, and the store maps that trip to the typed
|
|
11
|
+
* refusal thread_already_holds_version.
|
|
12
|
+
*
|
|
13
|
+
* The store keeps the snapshot and the anchor as given, and it never mints a
|
|
14
|
+
* snapshot. A read parses the stored document and the stored snapshot with the
|
|
15
|
+
* current schemas. A row that fails a parse reads as a typed error, and it does
|
|
16
|
+
* not crash. An absent row reads as a normal absence.
|
|
17
|
+
*
|
|
18
|
+
* A version outlives its thread. The anchor columns sit on the row, thus a
|
|
19
|
+
* deleted thread leaves a recorded version in place. Only a purge of the analysis
|
|
20
|
+
* removes the version, through the analysis-id cascade.
|
|
21
|
+
*/
|
|
22
|
+
import { randomUUID } from "node:crypto";
|
|
23
|
+
import { err, errAsync, ok } from "neverthrow";
|
|
24
|
+
import { z } from "zod";
|
|
25
|
+
import { ReportDocumentSchema } from "../contracts/report-blocks.js";
|
|
26
|
+
import { tryMutation, tryQuery } from "../lib/db-result.js";
|
|
27
|
+
/**
|
|
28
|
+
* The name of the unique constraint on thread_id. Postgres reports it on a
|
|
29
|
+
* duplicate-thread refusal, and the record matches it to map the trip to the
|
|
30
|
+
* thread_already_holds_version refusal.
|
|
31
|
+
*/
|
|
32
|
+
const THREAD_UNIQUE_CONSTRAINT = "cortex_report_versions_one_per_thread";
|
|
33
|
+
/**
|
|
34
|
+
* One pinned artifact of a stored snapshot. The value carries the content hash,
|
|
35
|
+
* an optional file type, and optional rows. The `z.ZodType<ReportSnapshot>`
|
|
36
|
+
* annotation below keeps this schema and the `ArtifactSnapshot` type of the
|
|
37
|
+
* reference model in step, thus a change to one shows as a compile error there.
|
|
38
|
+
*/
|
|
39
|
+
const ArtifactSnapshotSchema = z.object({
|
|
40
|
+
hash: z.string(),
|
|
41
|
+
fileType: z.string().nullable().optional(),
|
|
42
|
+
rows: z.array(z.record(z.string(), z.union([z.string(), z.number()]))).optional(),
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* The stored-snapshot schema. The annotation ties the parse output to the
|
|
46
|
+
* `ReportSnapshot` type of the reference model, thus the store and the structural
|
|
47
|
+
* validation never disagree about the snapshot shape. The mint never fills `rows`,
|
|
48
|
+
* but the schema admits what the type admits.
|
|
49
|
+
*/
|
|
50
|
+
const ReportSnapshotSchema = z.object({
|
|
51
|
+
artifacts: z.record(z.string(), ArtifactSnapshotSchema),
|
|
52
|
+
citations: z.array(z.string()).optional(),
|
|
53
|
+
});
|
|
54
|
+
function reduceIssues(error) {
|
|
55
|
+
return error.issues.map((issue) => ({
|
|
56
|
+
path: issue.path.map((segment) => String(segment)).join("."),
|
|
57
|
+
message: issue.message,
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
const _assertRecordError = true;
|
|
61
|
+
const _assertReadError = true;
|
|
62
|
+
/**
|
|
63
|
+
* The full row projection every read shares. `parent_seq` is a bigint, thus the
|
|
64
|
+
* driver hands it back as text, and `rowToVersion` is the single place that turns
|
|
65
|
+
* it into a number.
|
|
66
|
+
*/
|
|
67
|
+
const VERSION_COLUMNS = "version_id, analysis_id, thread_id, parent_thread_id, parent_seq::text AS parent_seq, parent_version_id, document, snapshot, created_at";
|
|
68
|
+
/**
|
|
69
|
+
* The insert. The row carries no ordinal, thus the statement writes the given
|
|
70
|
+
* columns directly. `created_at` takes the column default.
|
|
71
|
+
*/
|
|
72
|
+
const INSERT_SQL = `INSERT INTO cortex_report_versions
|
|
73
|
+
(version_id, analysis_id, thread_id, parent_thread_id, parent_seq,
|
|
74
|
+
parent_version_id, document, snapshot)
|
|
75
|
+
VALUES ($1, $2, $3, $4, $5::bigint, $6, $7::jsonb, $8::jsonb)`;
|
|
76
|
+
function rowToVersion(row) {
|
|
77
|
+
const document = ReportDocumentSchema.safeParse(row.document);
|
|
78
|
+
if (!document.success) {
|
|
79
|
+
return err({ type: "corrupt_version", op: "report-versions.read", versionId: row.version_id, part: "document", issues: reduceIssues(document.error) });
|
|
80
|
+
}
|
|
81
|
+
const snapshot = ReportSnapshotSchema.safeParse(row.snapshot);
|
|
82
|
+
if (!snapshot.success) {
|
|
83
|
+
return err({ type: "corrupt_version", op: "report-versions.read", versionId: row.version_id, part: "snapshot", issues: reduceIssues(snapshot.error) });
|
|
84
|
+
}
|
|
85
|
+
return ok({
|
|
86
|
+
versionId: row.version_id,
|
|
87
|
+
analysisId: row.analysis_id,
|
|
88
|
+
threadId: row.thread_id,
|
|
89
|
+
parentThreadId: row.parent_thread_id,
|
|
90
|
+
parentSeq: row.parent_seq === null ? null : Number(row.parent_seq),
|
|
91
|
+
parentVersionId: row.parent_version_id,
|
|
92
|
+
document: document.data,
|
|
93
|
+
snapshot: snapshot.data,
|
|
94
|
+
createdAt: row.created_at,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Map an insert failure to a record refusal. A trip of the thread constraint is
|
|
99
|
+
* thread_already_holds_version. Every other DbError stays unchanged.
|
|
100
|
+
*/
|
|
101
|
+
function mapInsertError(error, threadId) {
|
|
102
|
+
if (error.type === "constraint_violation" && error.constraint === THREAD_UNIQUE_CONSTRAINT) {
|
|
103
|
+
return { type: "thread_already_holds_version", op: "report-versions.record", threadId };
|
|
104
|
+
}
|
|
105
|
+
return error;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Create a `ReportVersionStore` bound to a Postgres pool. The
|
|
109
|
+
* `cortex_report_versions` table is provisioned by the state-init DDL.
|
|
110
|
+
*/
|
|
111
|
+
export function createReportVersionStore({ pool }) {
|
|
112
|
+
/**
|
|
113
|
+
* The analysis scope of a parent version, or `null` when no such row exists.
|
|
114
|
+
* Absence is not a verdict here: an unknown parent id falls through to the
|
|
115
|
+
* insert, where the self foreign key refuses it as a `DbError`.
|
|
116
|
+
*/
|
|
117
|
+
function parentAnalysis(parentVersionId) {
|
|
118
|
+
return tryQuery("report-versions.record.parentScope", () => pool.query({
|
|
119
|
+
text: "SELECT analysis_id FROM cortex_report_versions WHERE version_id = $1",
|
|
120
|
+
values: [parentVersionId],
|
|
121
|
+
})).map(({ rows }) => rows[0]?.analysis_id ?? null);
|
|
122
|
+
}
|
|
123
|
+
function record(input) {
|
|
124
|
+
const parsed = ReportDocumentSchema.safeParse(input.document);
|
|
125
|
+
if (!parsed.success) {
|
|
126
|
+
return errAsync({ type: "malformed_document", op: "report-versions.record", issues: reduceIssues(parsed.error) });
|
|
127
|
+
}
|
|
128
|
+
const document = parsed.data;
|
|
129
|
+
// The parse guards the row against a caller bug through a cast. The store
|
|
130
|
+
// stores the given value, thus the parse changes no value.
|
|
131
|
+
const parsedSnapshot = ReportSnapshotSchema.safeParse(input.snapshot);
|
|
132
|
+
if (!parsedSnapshot.success) {
|
|
133
|
+
return errAsync({ type: "malformed_snapshot", op: "report-versions.record", issues: reduceIssues(parsedSnapshot.error) });
|
|
134
|
+
}
|
|
135
|
+
const insert = () => {
|
|
136
|
+
const versionId = randomUUID();
|
|
137
|
+
return tryMutation("report-versions.record.insert", async () => {
|
|
138
|
+
await pool.query({
|
|
139
|
+
text: INSERT_SQL,
|
|
140
|
+
values: [
|
|
141
|
+
versionId,
|
|
142
|
+
input.analysisId,
|
|
143
|
+
input.threadId,
|
|
144
|
+
input.parentThreadId,
|
|
145
|
+
input.parentSeq,
|
|
146
|
+
input.parentVersionId ?? null,
|
|
147
|
+
JSON.stringify(document),
|
|
148
|
+
JSON.stringify(input.snapshot),
|
|
149
|
+
],
|
|
150
|
+
});
|
|
151
|
+
return { versionId };
|
|
152
|
+
}).mapErr((error) => mapInsertError(error, input.threadId));
|
|
153
|
+
};
|
|
154
|
+
if (input.parentVersionId === undefined) {
|
|
155
|
+
return insert();
|
|
156
|
+
}
|
|
157
|
+
const parentVersionId = input.parentVersionId;
|
|
158
|
+
return parentAnalysis(parentVersionId).andThen((parentAnalysisId) => {
|
|
159
|
+
if (parentAnalysisId !== null && parentAnalysisId !== input.analysisId) {
|
|
160
|
+
return errAsync({
|
|
161
|
+
type: "parent_analysis_mismatch",
|
|
162
|
+
op: "report-versions.record",
|
|
163
|
+
analysisId: input.analysisId,
|
|
164
|
+
parentVersionId,
|
|
165
|
+
parentAnalysisId,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return insert();
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function getVersion(versionId) {
|
|
172
|
+
return tryQuery("report-versions.getVersion", () => pool.query({
|
|
173
|
+
text: `SELECT ${VERSION_COLUMNS} FROM cortex_report_versions WHERE version_id = $1`,
|
|
174
|
+
values: [versionId],
|
|
175
|
+
})).andThen(({ rows }) => {
|
|
176
|
+
const row = rows[0];
|
|
177
|
+
if (!row)
|
|
178
|
+
return ok(null);
|
|
179
|
+
return rowToVersion(row);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
function getThreadVersion(threadId) {
|
|
183
|
+
return tryQuery("report-versions.getThreadVersion", () => pool.query({
|
|
184
|
+
text: `SELECT ${VERSION_COLUMNS} FROM cortex_report_versions WHERE thread_id = $1`,
|
|
185
|
+
values: [threadId],
|
|
186
|
+
})).andThen(({ rows }) => {
|
|
187
|
+
const row = rows[0];
|
|
188
|
+
if (!row)
|
|
189
|
+
return ok(null);
|
|
190
|
+
return rowToVersion(row);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return { record, getVersion, getThreadVersion };
|
|
194
|
+
}
|
package/dist/state/schema.d.ts
CHANGED
|
@@ -46,10 +46,10 @@ export declare const ArtifactRowSchema: z.ZodObject<{
|
|
|
46
46
|
}, z.core.$strip>;
|
|
47
47
|
export type ArtifactRow = z.infer<typeof ArtifactRowSchema>;
|
|
48
48
|
export declare const RunStatus: z.ZodEnum<{
|
|
49
|
+
partial: "partial";
|
|
49
50
|
running: "running";
|
|
50
51
|
completed: "completed";
|
|
51
52
|
failed: "failed";
|
|
52
|
-
partial: "partial";
|
|
53
53
|
canceled: "canceled";
|
|
54
54
|
suspended_insufficient_funds: "suspended_insufficient_funds";
|
|
55
55
|
}>;
|
|
@@ -77,10 +77,10 @@ export declare const CortexRunRowSchema: z.ZodObject<{
|
|
|
77
77
|
threadId: z.ZodNullable<z.ZodString>;
|
|
78
78
|
workflowName: z.ZodString;
|
|
79
79
|
status: z.ZodEnum<{
|
|
80
|
+
partial: "partial";
|
|
80
81
|
running: "running";
|
|
81
82
|
completed: "completed";
|
|
82
83
|
failed: "failed";
|
|
83
|
-
partial: "partial";
|
|
84
84
|
canceled: "canceled";
|
|
85
85
|
suspended_insufficient_funds: "suspended_insufficient_funds";
|
|
86
86
|
}>;
|
|
@@ -272,6 +272,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
272
272
|
id: "submit_profile",
|
|
273
273
|
description: "Submit the profiling results. Call this tool once after completing " + "all profiling work — it validates and records your findings.",
|
|
274
274
|
inputSchema: ProfilerOutputSchema,
|
|
275
|
+
describeCall: "none",
|
|
275
276
|
execute: async (input) => {
|
|
276
277
|
capturedProfile = input;
|
|
277
278
|
return ok({ status: "accepted" });
|
package/dist/tools/bio/chembl.js
CHANGED
|
@@ -118,6 +118,7 @@ export const chemblTool = defineTool({
|
|
|
118
118
|
"compounds here first. If ChEMBL misses the compound, resolve it via pubchem action='compound' and bridge back with pubchem action='crossrefs'.\n" +
|
|
119
119
|
"An empty array is valid no-data, not an error — do not retry the same call.",
|
|
120
120
|
inputSchema,
|
|
121
|
+
describeCall: "none",
|
|
121
122
|
execute: async ({ action, query, searchType, chemblId, idType, activityType, limit }) => {
|
|
122
123
|
switch (action) {
|
|
123
124
|
case "compounds":
|
|
@@ -106,6 +106,7 @@ export function createComptoxTool(deps) {
|
|
|
106
106
|
"This is environmental/industrial-chemical data: for drug-like compounds prefer PubChem or ChEMBL — dataset 'chemical' returns " +
|
|
107
107
|
"`detail.pubchemCid` to bridge back.",
|
|
108
108
|
inputSchema,
|
|
109
|
+
describeCall: "none",
|
|
109
110
|
execute: async (input) => {
|
|
110
111
|
const headers = getEpaCcteHeaders(deps.apiKey);
|
|
111
112
|
const resolved = await resolveDtxsid(input.query, headers);
|
|
@@ -119,6 +119,7 @@ export function createDrugGeneInteractionsTool(deps) {
|
|
|
119
119
|
"not be reached (a missing DRUGBANK_API_KEY lands here — tell the user and proceed with DGIdb). A `found: false` entry means the identifier " +
|
|
120
120
|
"is not in DGIdb at all, usually a non-HUGO symbol or a brand name. Neither is worth retrying unchanged.",
|
|
121
121
|
inputSchema,
|
|
122
|
+
describeCall: "none",
|
|
122
123
|
execute: async (input) => {
|
|
123
124
|
const inputs = typeof input.query === "string" ? [input.query] : input.query;
|
|
124
125
|
const selected = input.sources ?? ["dgidb"];
|
|
@@ -118,6 +118,7 @@ export function createGeneDiseaseEvidenceTool(deps) {
|
|
|
118
118
|
"could not be reached (a missing DISGENET_API_KEY lands here — tell the user and proceed with the other two); 'not_applicable' means that " +
|
|
119
119
|
"corpus has no lookup for this queryType. Only 'no_data' is evidence of absence, and none is worth retrying unchanged.",
|
|
120
120
|
inputSchema,
|
|
121
|
+
describeCall: "none",
|
|
121
122
|
execute: async (input) => {
|
|
122
123
|
const applicable = applicableSources(input.queryType);
|
|
123
124
|
const requested = input.sources ?? applicable;
|
|
@@ -51,7 +51,7 @@ export declare const genePreclinicalProfileTool: import("../define-tool.js").Too
|
|
|
51
51
|
geneSymbol: string;
|
|
52
52
|
include?: ("expression" | "knockout")[] | undefined;
|
|
53
53
|
species?: ("homo_sapiens" | "mus_musculus" | "rattus_norvegicus" | "canis_lupus_familiaris" | "macaca_mulatta")[] | undefined;
|
|
54
|
-
minRank?: "
|
|
54
|
+
minRank?: "absent" | "high" | "medium" | "low" | undefined;
|
|
55
55
|
tissueLimit?: number | undefined;
|
|
56
56
|
phenotypeLimit?: number | undefined;
|
|
57
57
|
}, PreclinicalOutput>;
|
|
@@ -105,6 +105,7 @@ export const genePreclinicalProfileTool = defineTool({
|
|
|
105
105
|
"SPARSE OUTPUT IS VALID NO-DATA, not an error — do not retry. A null humanEnsemblId means the symbol did not resolve; a null mouseMarkerSymbol " +
|
|
106
106
|
"with no phenotype terms means the gene has not been IMPC-phenotyped, which is common.",
|
|
107
107
|
inputSchema,
|
|
108
|
+
describeCall: "none",
|
|
108
109
|
execute: async ({ geneSymbol, include, species, minRank, tissueLimit, phenotypeLimit }) => {
|
|
109
110
|
const halves = include ?? ["expression", "knockout"];
|
|
110
111
|
const wantExpression = halves.includes("expression");
|
package/dist/tools/bio/keys.d.ts
CHANGED
|
@@ -13,13 +13,15 @@ export interface BioToolKeys {
|
|
|
13
13
|
readonly epaCcte: string;
|
|
14
14
|
readonly ncbi?: string;
|
|
15
15
|
readonly github?: string;
|
|
16
|
+
readonly semanticScholar?: string;
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
18
19
|
* The NCBI-backed literature tool, built from the shared key slice.
|
|
19
20
|
*
|
|
20
21
|
* `pubmed` is the consolidated literature tool (search / details / fulltext
|
|
21
|
-
* behind one `action`). ClinVar
|
|
22
|
-
* corpora behind
|
|
22
|
+
* behind one `action`). ClinVar reaches a caller through
|
|
23
|
+
* `gene_disease_evidence`, as one of the corpora behind it, and it needs the
|
|
24
|
+
* same NCBI key.
|
|
23
25
|
*/
|
|
24
26
|
export declare function createNcbiTools(keys: BioToolKeys): {
|
|
25
27
|
pubmed: Tool;
|
package/dist/tools/bio/keys.js
CHANGED
|
@@ -6,8 +6,9 @@ import { createPubMedTool } from "./pubmed.js";
|
|
|
6
6
|
* The NCBI-backed literature tool, built from the shared key slice.
|
|
7
7
|
*
|
|
8
8
|
* `pubmed` is the consolidated literature tool (search / details / fulltext
|
|
9
|
-
* behind one `action`). ClinVar
|
|
10
|
-
* corpora behind
|
|
9
|
+
* behind one `action`). ClinVar reaches a caller through
|
|
10
|
+
* `gene_disease_evidence`, as one of the corpora behind it, and it needs the
|
|
11
|
+
* same NCBI key.
|
|
11
12
|
*/
|
|
12
13
|
export function createNcbiTools(keys) {
|
|
13
14
|
return {
|
|
@@ -122,6 +122,7 @@ export const lookupAnnotationTool = defineTool({
|
|
|
122
122
|
"search_interactions({action:'enrichment'}), which does the statistics.\n" +
|
|
123
123
|
"An empty terms / annotations / pathways array is valid no-data — do not retry the same input.",
|
|
124
124
|
inputSchema,
|
|
125
|
+
describeCall: "none",
|
|
125
126
|
execute: async ({ vocabulary, query, goId, geneProductId, taxonId, organism, includeGenes, limit }) => {
|
|
126
127
|
const cap = limit ?? DEFAULT_LIMIT;
|
|
127
128
|
if (vocabulary !== "go") {
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* opentargets — one tool over the Open Targets Platform: target-disease
|
|
3
3
|
* association evidence, read from a gene or from a disease.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Curated safety liabilities are not served here: `target_safety` owns them,
|
|
6
|
+
* because it pairs them with the local secondary-pharmacology panel and
|
|
7
|
+
* resolves the Ensembl id they need.
|
|
8
8
|
*
|
|
9
9
|
* The input is a flat object with an `action` discriminator — not a
|
|
10
10
|
* `z.discriminatedUnion`, which `defineTool` rejects (model tool calling needs
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* opentargets — one tool over the Open Targets Platform: target-disease
|
|
3
3
|
* association evidence, read from a gene or from a disease.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Curated safety liabilities are not served here: `target_safety` owns them,
|
|
6
|
+
* because it pairs them with the local secondary-pharmacology panel and
|
|
7
|
+
* resolves the Ensembl id they need.
|
|
8
8
|
*
|
|
9
9
|
* The input is a flat object with an `action` discriminator — not a
|
|
10
10
|
* `z.discriminatedUnion`, which `defineTool` rejects (model tool calling needs
|
|
@@ -62,6 +62,7 @@ export const openTargetsTool = defineTool({
|
|
|
62
62
|
"disease name silently returns an EMPTY result rather than an error — resolve a symbol to its ENSG id with search_gene first.\n" +
|
|
63
63
|
"NO-DATA IS FINAL — do not retry the same id. Empty associations mean no evidence, or an unresolvable id.",
|
|
64
64
|
inputSchema,
|
|
65
|
+
describeCall: "none",
|
|
65
66
|
execute: async (input) => {
|
|
66
67
|
const limit = input.limit ?? DEFAULT_LIMIT;
|
|
67
68
|
switch (input.action) {
|
|
@@ -76,6 +76,7 @@ export const pubchemTool = defineTool({
|
|
|
76
76
|
"then chembl action='mechanism' / 'bioactivity' there for curated activity data. " +
|
|
77
77
|
"An empty results / crossRefs / assays array is valid no-data (no match; the CID is in no external registry; the compound was never screened) — do not retry.",
|
|
78
78
|
inputSchema,
|
|
79
|
+
describeCall: "none",
|
|
79
80
|
execute: async (input) => {
|
|
80
81
|
switch (input.action) {
|
|
81
82
|
case "compound":
|
package/dist/tools/bio/pubmed.js
CHANGED
|
@@ -140,16 +140,16 @@ export function createPubMedTool(deps) {
|
|
|
140
140
|
return `fulltext ${input.pmcId ?? ""}`;
|
|
141
141
|
}
|
|
142
142
|
},
|
|
143
|
-
execute: async (input) => {
|
|
143
|
+
execute: async (input, context) => {
|
|
144
144
|
switch (input.action) {
|
|
145
145
|
case "search":
|
|
146
146
|
return ok(await searchPubmed(deps.ncbiApiKey, input.query, {
|
|
147
147
|
maxResults: input.maxResults ?? DEFAULTS.searchResults,
|
|
148
148
|
...(input.sort ? { sort: input.sort } : {}),
|
|
149
149
|
...(input.dateRange ? { dateRange: input.dateRange } : {}),
|
|
150
|
-
}));
|
|
150
|
+
}, context.signal));
|
|
151
151
|
case "details": {
|
|
152
|
-
const { articles, notFound } = await getArticleDetails(deps.ncbiApiKey, input.pmids);
|
|
152
|
+
const { articles, notFound } = await getArticleDetails(deps.ncbiApiKey, input.pmids, context.signal);
|
|
153
153
|
return ok({
|
|
154
154
|
articles: boundArticleDetails(articles, {
|
|
155
155
|
maxAuthors: input.maxAuthors ?? DEFAULTS.maxAuthors,
|
|
@@ -162,7 +162,7 @@ export function createPubMedTool(deps) {
|
|
|
162
162
|
const parsed = await getArticleFullText(deps.ncbiApiKey, input.pmcId, {
|
|
163
163
|
...(input.sections ? { sections: input.sections } : {}),
|
|
164
164
|
maxChars: input.maxChars ?? DEFAULTS.maxChars,
|
|
165
|
-
});
|
|
165
|
+
}, context.signal);
|
|
166
166
|
// "Not open-access" is an expected outcome — a data variant, not an error.
|
|
167
167
|
if (!parsed)
|
|
168
168
|
return ok({ pmcId: input.pmcId, available: false });
|
|
@@ -39,6 +39,7 @@ export const searchClinicalTrialsTool = defineTool({
|
|
|
39
39
|
"landscape. Set true for the full protocol prose, and narrow `limit` when you do: a full summary runs to several thousand characters " +
|
|
40
40
|
"per trial."),
|
|
41
41
|
}),
|
|
42
|
+
describeCall: "none",
|
|
42
43
|
execute: async ({ query, phase, status, limit = 10, includeSummaries = false }) => {
|
|
43
44
|
const result = await searchTrials(query, { phase, status, limit });
|
|
44
45
|
const trials = includeSummaries
|
|
@@ -23,6 +23,7 @@ export const searchFaersTool = defineTool({
|
|
|
23
23
|
.describe("Max distinct adverse-reaction terms to return, most-reported first (default 15, max 100). totalReports covers the whole drug regardless."),
|
|
24
24
|
serious: z.boolean().default(false).describe("When true, count only reports flagged serious (death, hospitalization, life-threatening, disabling)."),
|
|
25
25
|
}),
|
|
26
|
+
describeCall: "none",
|
|
26
27
|
execute: async ({ drugName, limit = 15, serious = false }) => {
|
|
27
28
|
const result = await getFaersByDrug(drugName, { limit, serious });
|
|
28
29
|
return ok({
|
|
@@ -63,6 +63,7 @@ export const searchGeoDatasetsTool = defineTool({
|
|
|
63
63
|
.default(10)
|
|
64
64
|
.describe("Max datasets to return (default 10, max 50). totalFound reports the true match count regardless."),
|
|
65
65
|
}),
|
|
66
|
+
describeCall: "none",
|
|
66
67
|
execute: async ({ query, organism, datasetType = "gse", limit = 10 }) => {
|
|
67
68
|
let searchQuery = query;
|
|
68
69
|
// Escape backslashes first, then quotes, so a `\` in the organism value
|
|
@@ -35,6 +35,7 @@ export const searchInteractionsTool = defineTool({
|
|
|
35
35
|
.describe(`Max records returned — partners, network edges, or enriched terms depending on action (default ${DEFAULT_LIMIT}, max 500). ` +
|
|
36
36
|
"Rows are ordered best-first (score descending for interactions, FDR ascending for enrichment), so the default keeps the strongest."),
|
|
37
37
|
}),
|
|
38
|
+
describeCall: "none",
|
|
38
39
|
execute: async ({ identifiers, species, action, minScore, limit }) => {
|
|
39
40
|
if (action === "enrichment") {
|
|
40
41
|
return ok(await getEnrichment(identifiers, species, { limit }));
|
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
* sources that carry it: the curated secondary-pharmacology panel that ships
|
|
4
4
|
* with the harness, and Open Targets' curated liabilities.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* (it named two tools that no longer exist). Both are keyed on the same target
|
|
10
|
-
* and answer the same question, so the pairing belongs in the tool.
|
|
6
|
+
* Both sources are keyed on the same target and answer the same question, so
|
|
7
|
+
* the pairing belongs in the tool rather than in prose a caller has to
|
|
8
|
+
* remember: one call consults both and merges what they carry.
|
|
11
9
|
*
|
|
12
10
|
* Identifier resolution is the tool's job, not the caller's: Open Targets
|
|
13
11
|
* accepts ONLY an Ensembl gene id, and it is taken from the panel entry when
|
|
@@ -41,7 +39,7 @@ export declare const targetSafetyTool: import("../define-tool.js").Tool<{
|
|
|
41
39
|
identifiers: string[];
|
|
42
40
|
identifierType?: "auto" | "gene_symbol" | "uniprot" | "chembl_id" | "ensembl_id" | undefined;
|
|
43
41
|
sources?: ("opentargets" | "panel")[] | undefined;
|
|
44
|
-
filterOrgan?: "hematologic" | "metabolic" | "immune" | "hepatic" | "cns" | "renal" | "respiratory" | "cardiac" | "gi" | undefined;
|
|
42
|
+
filterOrgan?: "hematologic" | "metabolic" | "immune" | "hepatic" | "musculoskeletal" | "cns" | "renal" | "reproductive" | "respiratory" | "cardiac" | "vascular" | "pns" | "gi" | "pancreas" | "endocrine_thyroid" | "dermatologic" | "ocular" | "oncology" | undefined;
|
|
45
43
|
minSeverity?: "high" | "medium" | "low" | undefined;
|
|
46
44
|
liabilityLimit?: number | undefined;
|
|
47
45
|
}, {
|
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
* sources that carry it: the curated secondary-pharmacology panel that ships
|
|
4
4
|
* with the harness, and Open Targets' curated liabilities.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* (it named two tools that no longer exist). Both are keyed on the same target
|
|
10
|
-
* and answer the same question, so the pairing belongs in the tool.
|
|
6
|
+
* Both sources are keyed on the same target and answer the same question, so
|
|
7
|
+
* the pairing belongs in the tool rather than in prose a caller has to
|
|
8
|
+
* remember: one call consults both and merges what they carry.
|
|
11
9
|
*
|
|
12
10
|
* Identifier resolution is the tool's job, not the caller's: Open Targets
|
|
13
11
|
* accepts ONLY an Ensembl gene id, and it is taken from the panel entry when
|
|
@@ -15,7 +13,9 @@
|
|
|
15
13
|
*/
|
|
16
14
|
import { ok } from "neverthrow";
|
|
17
15
|
import { z } from "zod";
|
|
18
|
-
import {
|
|
16
|
+
import { ORGAN_SYSTEMS } from "../../contracts/organ-system.js";
|
|
17
|
+
import { SEVERITIES } from "../../contracts/severity.js";
|
|
18
|
+
import { CHEMBL_RE, SafetyPanelFileSchema, UNIPROT_RE } from "../../data/safety-panel-schema.js";
|
|
19
19
|
import panelData from "../../data/safety-panel.json" with { type: "json" };
|
|
20
20
|
import { defineTool } from "../define-tool.js";
|
|
21
21
|
import { resolveSymbolToEnsemblId } from "../lib/ensembl-client.js";
|
|
@@ -100,6 +100,7 @@ export const targetSafetyTool = defineTool({
|
|
|
100
100
|
"means no CURATED liability, not that none exists. Both are valid no-data — do not retry. Read `perSource` and each row's `opentargetsNote`: a " +
|
|
101
101
|
"ChEMBL or UniProt identifier absent from the panel cannot be resolved to an Ensembl id, so its Open Targets half is skipped rather than empty.",
|
|
102
102
|
inputSchema,
|
|
103
|
+
describeCall: "none",
|
|
103
104
|
execute: async ({ identifiers, identifierType, sources, filterOrgan, minSeverity, liabilityLimit }) => {
|
|
104
105
|
const selected = sources ?? [...SOURCES];
|
|
105
106
|
const wantPanel = selected.includes("panel");
|
|
@@ -95,20 +95,28 @@ export interface ToolDefinition<Schema extends z.ZodType, Output> {
|
|
|
95
95
|
readonly inputSchema: Schema;
|
|
96
96
|
readonly executionMode?: ToolExecutionMode;
|
|
97
97
|
/**
|
|
98
|
-
* One line
|
|
98
|
+
* One line that names what THIS call does — the call-time counterpart of
|
|
99
99
|
* `description`. `description` self-describes the tool at attach time, so an
|
|
100
|
-
* agent knows what it holds
|
|
100
|
+
* agent knows what it holds. `describeCall` self-describes one invocation, so
|
|
101
101
|
* a surface can render four `update_working_memory` calls as four distinct
|
|
102
102
|
* lines instead of four identical chips.
|
|
103
103
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
104
|
+
* The decision is REQUIRED. Give a hook, or give the literal `"none"`.
|
|
105
|
+
* `"none"` declares that the input of the tool cannot distinguish its calls,
|
|
106
|
+
* thus a hook can only restate the name of the tool. `defineTool` consumes
|
|
107
|
+
* the sentinel at construction, and the sentinel never reaches a packaged
|
|
108
|
+
* tool. Thus a reader of a `Tool` sees a function, or sees nothing. A hook
|
|
109
|
+
* that returns the string `"none"` is still a hook, because the discriminator
|
|
110
|
+
* is `typeof`, not equality.
|
|
109
111
|
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
+
* A hook lives here, beside `inputSchema`, because that is the one place
|
|
113
|
+
* where the compiler checks the two against each other. A host-side formatter
|
|
114
|
+
* that reads `input.path` by string key breaks silently the day the schema
|
|
115
|
+
* moves. This one fails to build. The tool author writes it, because the tool
|
|
116
|
+
* author knows which field matters.
|
|
117
|
+
*
|
|
118
|
+
* A hook is synchronous and pure — no I/O, no ambient state. Dispatch is what
|
|
119
|
+
* the user waits on, and a description must never fail a call: the loop
|
|
112
120
|
* validates the input against `inputSchema` first, guards the call, and drops
|
|
113
121
|
* the detail on any failure (see the tool-call-detail capability).
|
|
114
122
|
*
|
|
@@ -116,10 +124,9 @@ export interface ToolDefinition<Schema extends z.ZodType, Output> {
|
|
|
116
124
|
* removed, secrets redacted, length capped — happens once at the emit site,
|
|
117
125
|
* never here.
|
|
118
126
|
*
|
|
119
|
-
*
|
|
120
|
-
* exactly as it is today. The hook never reaches the model.
|
|
127
|
+
* The hook never reaches the model.
|
|
121
128
|
*/
|
|
122
|
-
describeCall
|
|
129
|
+
describeCall: ((input: z.infer<Schema>) => string) | "none";
|
|
123
130
|
execute(input: z.infer<Schema>, ctx: ToolContext): Promise<Result<Output, ToolError>>;
|
|
124
131
|
}
|
|
125
132
|
/**
|
|
@@ -44,9 +44,11 @@ export function defineTool(def) {
|
|
|
44
44
|
inputSchema: def.inputSchema,
|
|
45
45
|
jsonSchema,
|
|
46
46
|
executionMode,
|
|
47
|
-
// Spread rather than assigned: a tool that
|
|
47
|
+
// Spread rather than assigned: a tool that declines carries no
|
|
48
48
|
// `describeCall` key at all, so "has a hook" is one property check.
|
|
49
|
-
|
|
49
|
+
// `typeof` is the discriminator, not equality with the sentinel, so a
|
|
50
|
+
// hook that returns the string `"none"` still packages as a hook.
|
|
51
|
+
...(typeof def.describeCall === "function" ? { describeCall: def.describeCall } : {}),
|
|
50
52
|
execute: def.execute,
|
|
51
53
|
};
|
|
52
54
|
}
|
|
@@ -61,6 +61,7 @@ export const showUserTool = defineTool({
|
|
|
61
61
|
"One card per call, rendered in call order; an identical repeat call resolves to the same card, so compose the " +
|
|
62
62
|
"finished content before calling rather than re-rendering a draft.",
|
|
63
63
|
inputSchema: ShowUserInputSchema,
|
|
64
|
+
describeCall: ({ kind, title }) => (title === undefined ? kind : `${kind}: ${title}`),
|
|
64
65
|
execute: async (input, ctx) => {
|
|
65
66
|
// `dataPath` is a reference the host resolves at render time; validate its
|
|
66
67
|
// shape only (existence is a render-time host concern), and treat a
|
|
@@ -413,6 +413,7 @@ export const planReportTool = defineTool({
|
|
|
413
413
|
"existing preview with `modifications` needs no brief — you may go " +
|
|
414
414
|
"straight to `submit_report`.)",
|
|
415
415
|
inputSchema: z.object({}),
|
|
416
|
+
describeCall: "none",
|
|
416
417
|
executionMode: "inline",
|
|
417
418
|
execute: async () => ok({
|
|
418
419
|
schema: REPORT_BRIEF_JSON_SCHEMA,
|
|
@@ -472,6 +473,7 @@ export function createReportSubmitTool(deps) {
|
|
|
472
473
|
"as an `error` string. Returns the preview id + version and emits a " +
|
|
473
474
|
"preview card.",
|
|
474
475
|
inputSchema: submitReportInputSchema,
|
|
476
|
+
describeCall: "none",
|
|
475
477
|
execute: async (input, ctx) => {
|
|
476
478
|
// The brief rides as `unknown` so its ~12k schema stays off the
|
|
477
479
|
// always-on tool surface. Validate it here and return issues as DATA
|
|
@@ -58,6 +58,8 @@ export interface ChemblPolypharmHit {
|
|
|
58
58
|
moleculeChemblId: string;
|
|
59
59
|
targetChemblId: string;
|
|
60
60
|
targetName: string | null;
|
|
61
|
+
/** The assay the reported activity was measured in, when ChEMBL names one. */
|
|
62
|
+
assayChemblId: string | null;
|
|
61
63
|
standardType: string | null;
|
|
62
64
|
standardValue: number | null;
|
|
63
65
|
standardUnits: string | null;
|
|
@@ -431,6 +431,7 @@ export async function getModulatorPolypharmacology(moleculeChemblId, options = {
|
|
|
431
431
|
moleculeChemblId,
|
|
432
432
|
targetChemblId: tid,
|
|
433
433
|
targetName: null,
|
|
434
|
+
assayChemblId: raw.assay_chembl_id ?? null,
|
|
434
435
|
standardType: raw.standard_type ?? null,
|
|
435
436
|
standardValue: parseNumeric(raw.standard_value),
|
|
436
437
|
standardUnits: raw.standard_units ?? null,
|