@inflexa-ai/harness 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/dist/agents/report-builder.d.ts +2 -0
  2. package/dist/agents/report-builder.js +2 -0
  3. package/dist/agents/report-session-agent.d.ts +56 -0
  4. package/dist/agents/report-session-agent.js +72 -0
  5. package/dist/app/report-session-runtime.d.ts +72 -0
  6. package/dist/app/report-session-runtime.js +157 -0
  7. package/dist/app/spawn-report-session.d.ts +85 -0
  8. package/dist/app/spawn-report-session.js +83 -0
  9. package/dist/contracts/chat-events.d.ts +9 -0
  10. package/dist/contracts/index.d.ts +6 -2
  11. package/dist/contracts/index.js +3 -1
  12. package/dist/contracts/organ-system.d.ts +49 -0
  13. package/dist/contracts/organ-system.js +67 -0
  14. package/dist/contracts/report-blocks.d.ts +564 -0
  15. package/dist/contracts/report-blocks.js +117 -0
  16. package/dist/contracts/report-reference.d.ts +459 -0
  17. package/dist/contracts/report-reference.js +256 -0
  18. package/dist/contracts/schemas/chat-events.d.ts +2 -0
  19. package/dist/contracts/schemas/chat-events.js +2 -0
  20. package/dist/contracts/severity.d.ts +15 -0
  21. package/dist/contracts/severity.js +10 -0
  22. package/dist/contracts/target-dossier.d.ts +8675 -12972
  23. package/dist/contracts/target-dossier.js +534 -591
  24. package/dist/data/safety-panel-schema.d.ts +18 -5
  25. package/dist/data/safety-panel-schema.js +4 -4
  26. package/dist/execution/artifact-metadata.js +1 -0
  27. package/dist/execution/report-runner.js +1 -3
  28. package/dist/execution/run-synthesis.js +2 -0
  29. package/dist/index.d.ts +6 -0
  30. package/dist/index.js +19 -0
  31. package/dist/lib/async-utils.d.ts +16 -4
  32. package/dist/lib/async-utils.js +20 -15
  33. package/dist/lib/host-concurrency.js +1 -0
  34. package/dist/loop/__fixtures__/scripted-provider.js +23 -2
  35. package/dist/loop/run-agent.js +58 -10
  36. package/dist/loop/tool-detail.d.ts +40 -0
  37. package/dist/loop/tool-detail.js +35 -3
  38. package/dist/loop/types.d.ts +13 -0
  39. package/dist/memory/thread-history.d.ts +10 -0
  40. package/dist/memory/thread-history.js +12 -1
  41. package/dist/memory/thread-store.d.ts +4 -4
  42. package/dist/memory/thread-store.js +18 -9
  43. package/dist/prompts/report-session.d.ts +1 -0
  44. package/dist/prompts/report-session.js +89 -0
  45. package/dist/prompts/target-assessment/briefs/executive-recommendation.d.ts +1 -1
  46. package/dist/prompts/target-assessment/briefs/executive-recommendation.js +5 -5
  47. package/dist/prompts/target-assessment/briefs/liability-bullets.d.ts +1 -1
  48. package/dist/prompts/target-assessment/briefs/liability-bullets.js +1 -1
  49. package/dist/prompts/target-assessment/briefs/target-organ-liabilities.d.ts +1 -1
  50. package/dist/prompts/target-assessment/briefs/target-organ-liabilities.js +11 -3
  51. package/dist/prompts/target-assessment/investigation/adversarial-critique.d.ts +1 -0
  52. package/dist/prompts/target-assessment/investigation/adversarial-critique.js +67 -0
  53. package/dist/prompts/target-assessment/investigation/claim-reverification.d.ts +1 -0
  54. package/dist/prompts/target-assessment/investigation/claim-reverification.js +49 -0
  55. package/dist/prompts/target-assessment/investigation/index.d.ts +3 -0
  56. package/dist/prompts/target-assessment/investigation/index.js +3 -0
  57. package/dist/prompts/target-assessment/investigation/mechanism-proposal.d.ts +1 -0
  58. package/dist/prompts/target-assessment/investigation/mechanism-proposal.js +49 -0
  59. package/dist/prompts/target-assessment/tox-voice/index.d.ts +1 -1
  60. package/dist/prompts/target-assessment/tox-voice/vocabulary.d.ts +8 -5
  61. package/dist/prompts/target-assessment/tox-voice/vocabulary.js +9 -20
  62. package/dist/providers/__fixtures__/session.d.ts +5 -4
  63. package/dist/providers/__fixtures__/session.js +4 -4
  64. package/dist/providers/ai-sdk.d.ts +77 -0
  65. package/dist/providers/ai-sdk.js +149 -19
  66. package/dist/providers/errors.d.ts +14 -0
  67. package/dist/providers/errors.js +103 -18
  68. package/dist/providers/types.d.ts +16 -0
  69. package/dist/providers/types.js +10 -1
  70. package/dist/report-model/block-walk.d.ts +56 -0
  71. package/dist/report-model/block-walk.js +89 -0
  72. package/dist/report-model/draft-finish.d.ts +60 -0
  73. package/dist/report-model/draft-finish.js +56 -0
  74. package/dist/report-model/draft-operations.d.ts +125 -0
  75. package/dist/report-model/draft-operations.js +341 -0
  76. package/dist/report-model/draft-read.d.ts +59 -0
  77. package/dist/report-model/draft-read.js +109 -0
  78. package/dist/report-model/draft.d.ts +51 -0
  79. package/dist/report-model/draft.js +32 -0
  80. package/dist/report-model/fixture-resolver.d.ts +8 -0
  81. package/dist/report-model/fixture-resolver.js +252 -0
  82. package/dist/report-model/pin-snapshot.d.ts +30 -0
  83. package/dist/report-model/pin-snapshot.js +40 -0
  84. package/dist/report-model/reference-resolver.d.ts +113 -0
  85. package/dist/report-model/reference-resolver.js +60 -0
  86. package/dist/report-model/structural-validation.d.ts +22 -0
  87. package/dist/report-model/structural-validation.js +68 -0
  88. package/dist/report-model/validate.d.ts +72 -0
  89. package/dist/report-model/validate.js +95 -0
  90. package/dist/report-render/chart.d.ts +33 -0
  91. package/dist/report-render/chart.js +538 -0
  92. package/dist/report-render/page.d.ts +127 -0
  93. package/dist/report-render/page.js +307 -0
  94. package/dist/report-render/references.d.ts +32 -0
  95. package/dist/report-render/references.js +44 -0
  96. package/dist/report-render/render.d.ts +25 -0
  97. package/dist/report-render/render.js +125 -0
  98. package/dist/report-render/script-json.d.ts +13 -0
  99. package/dist/report-render/script-json.js +15 -0
  100. package/dist/report-render/types.d.ts +39 -0
  101. package/dist/report-render/types.js +10 -0
  102. package/dist/report-render/value-bridge.d.ts +62 -0
  103. package/dist/report-render/value-bridge.js +82 -0
  104. package/dist/report-render/views/chart-view.d.ts +11 -0
  105. package/dist/report-render/views/chart-view.js +23 -0
  106. package/dist/report-render/views/page-view.d.ts +20 -0
  107. package/dist/report-render/views/page-view.js +49 -0
  108. package/dist/report-render/views/prose.d.ts +28 -0
  109. package/dist/report-render/views/prose.js +75 -0
  110. package/dist/report-render/views/references-view.d.ts +20 -0
  111. package/dist/report-render/views/references-view.js +64 -0
  112. package/dist/report-render/views/values.d.ts +42 -0
  113. package/dist/report-render/views/values.js +43 -0
  114. package/dist/runtime/assemble.d.ts +15 -0
  115. package/dist/runtime/assemble.js +28 -6
  116. package/dist/runtime/boot.d.ts +3 -3
  117. package/dist/runtime/boot.js +15 -4
  118. package/dist/runtime/dbos.d.ts +4 -4
  119. package/dist/state/artifacts.d.ts +16 -0
  120. package/dist/state/artifacts.js +18 -0
  121. package/dist/state/init.js +54 -0
  122. package/dist/state/purge-analysis.js +2 -0
  123. package/dist/state/report-session-state.d.ts +112 -0
  124. package/dist/state/report-session-state.js +150 -0
  125. package/dist/state/report-versions.d.ts +129 -0
  126. package/dist/state/report-versions.js +167 -0
  127. package/dist/state/snapshot-parse.d.ts +34 -0
  128. package/dist/state/snapshot-parse.js +82 -0
  129. package/dist/tasks/data-profile.js +1 -0
  130. package/dist/tools/ad-hoc-router.d.ts +7 -2
  131. package/dist/tools/ad-hoc-router.js +2 -1
  132. package/dist/tools/bio/chembl.js +1 -0
  133. package/dist/tools/bio/comptox.js +1 -0
  134. package/dist/tools/bio/drug-gene-interactions.js +1 -0
  135. package/dist/tools/bio/gene-disease-evidence.js +1 -0
  136. package/dist/tools/bio/gene-preclinical-profile.js +1 -0
  137. package/dist/tools/bio/keys.d.ts +3 -2
  138. package/dist/tools/bio/keys.js +3 -2
  139. package/dist/tools/bio/lookup-annotation.js +1 -0
  140. package/dist/tools/bio/opentargets.d.ts +3 -3
  141. package/dist/tools/bio/opentargets.js +4 -3
  142. package/dist/tools/bio/pubchem.js +1 -0
  143. package/dist/tools/bio/search-clinical-trials.js +1 -0
  144. package/dist/tools/bio/search-faers.js +1 -0
  145. package/dist/tools/bio/search-geo-datasets.js +1 -0
  146. package/dist/tools/bio/search-interactions.js +1 -0
  147. package/dist/tools/bio/target-safety.d.ts +4 -6
  148. package/dist/tools/bio/target-safety.js +7 -6
  149. package/dist/tools/define-tool.d.ts +19 -12
  150. package/dist/tools/define-tool.js +4 -2
  151. package/dist/tools/display/show-user.js +1 -0
  152. package/dist/tools/iterate-report.js +2 -0
  153. package/dist/tools/lib/chembl-client.d.ts +2 -0
  154. package/dist/tools/lib/chembl-client.js +1 -0
  155. package/dist/tools/lib/monarch-client.d.ts +42 -0
  156. package/dist/tools/lib/monarch-client.js +100 -0
  157. package/dist/tools/report/build-report.js +1 -0
  158. package/dist/tools/report/mint-preview-url.js +1 -0
  159. package/dist/tools/report/preview-snapshot.js +1 -0
  160. package/dist/tools/report/submit-report.js +1 -0
  161. package/dist/tools/report/version-fs.js +4 -0
  162. package/dist/tools/report-authoring/authoring-tools.d.ts +241 -0
  163. package/dist/tools/report-authoring/authoring-tools.js +460 -0
  164. package/dist/tools/report-session/index.d.ts +8 -0
  165. package/dist/tools/report-session/index.js +8 -0
  166. package/dist/tools/report-session/preview-report.d.ts +90 -0
  167. package/dist/tools/report-session/preview-report.js +245 -0
  168. package/dist/tools/research/context7-docs.js +2 -0
  169. package/dist/tools/research/generate-analogy-report.js +1 -0
  170. package/dist/tools/research/generate-plan.js +7 -2
  171. package/dist/tools/research/inspect-data-profile.js +4 -0
  172. package/dist/tools/research/literature-reviewer.js +1 -0
  173. package/dist/tools/research/search-arxiv.js +1 -0
  174. package/dist/tools/research/search-github-repos.js +1 -0
  175. package/dist/tools/research/search-semantic-scholar.js +1 -0
  176. package/dist/tools/sandbox/list-available-packages.js +23 -0
  177. package/dist/tools/sandbox/list-available-refs.js +36 -0
  178. package/dist/tools/sandbox/report-blocker.js +1 -0
  179. package/dist/tools/sandbox/skills.js +2 -0
  180. package/dist/tools/workspace/file-stat.js +1 -0
  181. package/dist/tools/workspace/show-file.js +2 -0
  182. package/dist/tools/workspace/show-plan.js +1 -0
  183. package/dist/workflows/execute-analysis.d.ts +5 -1
  184. package/dist/workflows/execute-analysis.js +5 -5
  185. package/dist/workflows/execute-target-assessment.d.ts +13 -2
  186. package/dist/workflows/execute-target-assessment.js +78 -16
  187. package/dist/workflows/target-assessment/assemblers/index.d.ts +1 -1
  188. package/dist/workflows/target-assessment/assemblers/index.js +1 -1
  189. package/dist/workflows/target-assessment/assemblers/literature.d.ts +9 -27
  190. package/dist/workflows/target-assessment/assemblers/literature.js +32 -48
  191. package/dist/workflows/target-assessment/assemblers/orchestrator.d.ts +17 -9
  192. package/dist/workflows/target-assessment/assemblers/orchestrator.js +195 -85
  193. package/dist/workflows/target-assessment/assemblers/safety.d.ts +70 -48
  194. package/dist/workflows/target-assessment/assemblers/safety.js +125 -42
  195. package/dist/workflows/target-assessment/collectors/index.d.ts +5 -2
  196. package/dist/workflows/target-assessment/collectors/index.js +48 -5
  197. package/dist/workflows/target-assessment/coverage.d.ts +43 -27
  198. package/dist/workflows/target-assessment/coverage.js +33 -29
  199. package/dist/workflows/target-assessment/fanout/index.js +1 -0
  200. package/dist/workflows/target-assessment/investigation/index.d.ts +97 -0
  201. package/dist/workflows/target-assessment/investigation/index.js +419 -0
  202. package/dist/workflows/target-assessment/lib/approval-precedents.d.ts +26 -5
  203. package/dist/workflows/target-assessment/lib/approval-precedents.js +95 -29
  204. package/dist/workflows/target-assessment/lib/citation-validator.js +1 -1
  205. package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +2 -2
  206. package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.d.ts +2 -3
  207. package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.js +0 -4
  208. package/dist/workflows/target-assessment/lib/compute-derived.d.ts +6 -5
  209. package/dist/workflows/target-assessment/lib/compute-derived.js +14 -5
  210. package/dist/workflows/target-assessment/lib/family-complex-supplement.d.ts +5 -6
  211. package/dist/workflows/target-assessment/lib/family-complex-supplement.js +2 -3
  212. package/dist/workflows/target-assessment/lib/fda-label-safety.d.ts +72 -0
  213. package/dist/workflows/target-assessment/lib/fda-label-safety.js +166 -0
  214. package/dist/workflows/target-assessment/lib/heterodimer-filter.d.ts +0 -9
  215. package/dist/workflows/target-assessment/lib/heterodimer-filter.js +0 -10
  216. package/dist/workflows/target-assessment/lib/hpo-organ-map.d.ts +34 -0
  217. package/dist/workflows/target-assessment/lib/hpo-organ-map.js +63 -0
  218. package/dist/workflows/target-assessment/lib/impc-organ-map.d.ts +19 -0
  219. package/dist/workflows/target-assessment/lib/impc-organ-map.js +39 -0
  220. package/dist/workflows/target-assessment/lib/meddra-organ-map.d.ts +24 -14
  221. package/dist/workflows/target-assessment/lib/meddra-organ-map.js +59 -47
  222. package/dist/workflows/target-assessment/lib/recommendation-sanitizer.js +1 -1
  223. package/dist/workflows/target-assessment/lib/regulatory-actions.d.ts +2 -3
  224. package/dist/workflows/target-assessment/lib/regulatory-actions.js +33 -4
  225. package/dist/workflows/target-assessment/lib/safety-corroboration.d.ts +44 -0
  226. package/dist/workflows/target-assessment/lib/safety-corroboration.js +273 -0
  227. package/dist/workflows/target-assessment/lib/target-identity-filter.d.ts +5 -7
  228. package/dist/workflows/target-assessment/lib/target-identity-filter.js +5 -7
  229. package/dist/workflows/target-assessment/metrics.d.ts +1 -6
  230. package/dist/workflows/target-assessment/metrics.js +1 -13
  231. package/dist/workflows/target-assessment/phase4-assemble.d.ts +2147 -807
  232. package/dist/workflows/target-assessment/phase4-assemble.js +19 -19
  233. package/dist/workflows/target-assessment/phase5-persist.d.ts +20 -21
  234. package/dist/workflows/target-assessment/phase5-persist.js +56 -173
  235. package/dist/workflows/target-assessment/schemas.d.ts +46 -1
  236. package/dist/workflows/target-assessment/schemas.js +19 -0
  237. package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.d.ts +1 -0
  238. package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.js +2 -0
  239. package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +26 -62
  240. package/dist/workflows/target-assessment/steps/phase2-aggregate.js +0 -4
  241. package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +27 -154
  242. package/dist/workflows/target-assessment/steps/phase3-aggregate.js +0 -6
  243. package/dist/workflows/target-assessment/steps/synthesis/diagnostics.d.ts +0 -6
  244. package/dist/workflows/target-assessment/steps/synthesis/diagnostics.js +0 -18
  245. package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.d.ts +1 -0
  246. package/dist/workflows/target-assessment/synthesis/index.d.ts +52 -5
  247. package/dist/workflows/target-assessment/synthesis/index.js +4 -1
  248. package/dist/workflows/target-assessment/synthesis-probes.d.ts +3 -3
  249. package/package.json +4 -1
@@ -0,0 +1,129 @@
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 pins 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 { type ResultAsync } from "neverthrow";
23
+ import type { Pool } from "pg";
24
+ import { type ReportDocument } from "../contracts/report-blocks.js";
25
+ import { type DbError } from "../lib/db-result.js";
26
+ import type { Logger } from "../lib/logger.js";
27
+ import type { ReportSnapshot } from "../report-model/reference-resolver.js";
28
+ import { type SchemaIssue } from "./snapshot-parse.js";
29
+ export type { SchemaIssue };
30
+ /** The anchor and the payload of a version to record. */
31
+ export interface RecordVersionInput {
32
+ /** The block document. It rides as `unknown`, and the record parses it. */
33
+ readonly document: unknown;
34
+ /** The pinned snapshot, stored as given. */
35
+ readonly snapshot: ReportSnapshot;
36
+ readonly analysisId: string;
37
+ readonly threadId: string;
38
+ /** The parent conversation thread of the anchor, or `null` when a root has none. */
39
+ readonly parentThreadId: string | null;
40
+ /** The parent transcript position of the anchor, or `null` beside a null parent thread. */
41
+ readonly parentSeq: number | null;
42
+ /** The earlier version that this one reuses. The parent must belong to the same analysis. */
43
+ readonly parentVersionId?: string;
44
+ }
45
+ /** The stable reference of a freshly recorded version. */
46
+ export interface RecordedVersionRef {
47
+ readonly versionId: string;
48
+ }
49
+ /**
50
+ * A record the store refuses as data, before or in place of a row. Distinct from
51
+ * `DbError`: the request describes a version that the store will not write.
52
+ *
53
+ * The store parses the document and the snapshot before any row. The store
54
+ * refuses a malformed document or a malformed snapshot here as typed data, and
55
+ * no row lands.
56
+ *
57
+ * A second record for one thread is `thread_already_holds_version`. The unique
58
+ * constraint on the thread id trips, and the record maps that one constraint by
59
+ * its name. Every other constraint trip stays a `DbError`.
60
+ *
61
+ * A `parentVersionId` naming no row is not one of these -- the self foreign key
62
+ * refuses an unknown parent id, and `tryMutation` classifies that refusal as the
63
+ * `constraint_violation` variant of `DbError`.
64
+ */
65
+ export type RecordVersionError = {
66
+ readonly type: "malformed_document";
67
+ readonly op: string;
68
+ readonly issues: readonly SchemaIssue[];
69
+ } | {
70
+ readonly type: "malformed_snapshot";
71
+ readonly op: string;
72
+ readonly issues: readonly SchemaIssue[];
73
+ } | {
74
+ readonly type: "parent_analysis_mismatch";
75
+ readonly op: string;
76
+ readonly analysisId: string;
77
+ readonly parentVersionId: string;
78
+ readonly parentAnalysisId: string;
79
+ } | {
80
+ readonly type: "thread_already_holds_version";
81
+ readonly op: string;
82
+ readonly threadId: string;
83
+ } | DbError;
84
+ /**
85
+ * A stored row that a read cannot parse with the current schemas. It names the
86
+ * version id and the part that failed, and it carries the reduced issues.
87
+ */
88
+ export type VersionReadError = {
89
+ readonly type: "corrupt_version";
90
+ readonly op: string;
91
+ readonly versionId: string;
92
+ readonly part: "document" | "snapshot";
93
+ readonly issues: readonly SchemaIssue[];
94
+ };
95
+ /** One recorded version, as the store gives it back. */
96
+ export interface RecordedVersion {
97
+ readonly versionId: string;
98
+ readonly analysisId: string;
99
+ readonly threadId: string;
100
+ readonly parentThreadId: string | null;
101
+ readonly parentSeq: number | null;
102
+ /** The earlier version that this one reuses, or `null` when it has none. */
103
+ readonly parentVersionId: string | null;
104
+ readonly document: ReportDocument;
105
+ readonly snapshot: ReportSnapshot;
106
+ readonly createdAt: Date;
107
+ }
108
+ export interface ReportVersionStore {
109
+ /**
110
+ * Record a version. The record parses the document, refuses a malformed value
111
+ * as data with no row, and refuses a parent from a different analysis. It reads
112
+ * nothing before the insert. A second record for one thread refuses with
113
+ * thread_already_holds_version. The snapshot and the anchor store as given.
114
+ */
115
+ record(input: RecordVersionInput): ResultAsync<RecordedVersionRef, RecordVersionError>;
116
+ /** One version by its id, or `null` when no row holds it. */
117
+ getVersion(versionId: string): ResultAsync<RecordedVersion | null, DbError | VersionReadError>;
118
+ /** The one version of a thread, or `null` for a thread with no version. */
119
+ getThreadVersion(threadId: string): ResultAsync<RecordedVersion | null, DbError | VersionReadError>;
120
+ }
121
+ export interface ReportVersionStoreDeps {
122
+ readonly pool: Pool;
123
+ readonly logger?: Logger;
124
+ }
125
+ /**
126
+ * Create a `ReportVersionStore` bound to a Postgres pool. The
127
+ * `cortex_report_versions` table is provisioned by the state-init DDL.
128
+ */
129
+ export declare function createReportVersionStore({ pool }: ReportVersionStoreDeps): ReportVersionStore;
@@ -0,0 +1,167 @@
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 pins 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 { ReportDocumentSchema } from "../contracts/report-blocks.js";
25
+ import { tryMutation, tryQuery } from "../lib/db-result.js";
26
+ import { parseSnapshot, reduceIssues } from "./snapshot-parse.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
+ const _assertRecordError = true;
34
+ const _assertReadError = true;
35
+ /**
36
+ * The full row projection every read shares. `parent_seq` is a bigint, thus the
37
+ * driver hands it back as text, and `rowToVersion` is the single place that turns
38
+ * it into a number.
39
+ */
40
+ 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";
41
+ /**
42
+ * The insert. The row carries no ordinal, thus the statement writes the given
43
+ * columns directly. `created_at` takes the column default.
44
+ */
45
+ const INSERT_SQL = `INSERT INTO cortex_report_versions
46
+ (version_id, analysis_id, thread_id, parent_thread_id, parent_seq,
47
+ parent_version_id, document, snapshot)
48
+ VALUES ($1, $2, $3, $4, $5::bigint, $6, $7::jsonb, $8::jsonb)`;
49
+ function rowToVersion(row) {
50
+ const document = ReportDocumentSchema.safeParse(row.document);
51
+ if (!document.success) {
52
+ return err({ type: "corrupt_version", op: "report-versions.read", versionId: row.version_id, part: "document", issues: reduceIssues(document.error) });
53
+ }
54
+ const snapshot = parseSnapshot(row.snapshot);
55
+ if (snapshot.isErr()) {
56
+ return err({ type: "corrupt_version", op: "report-versions.read", versionId: row.version_id, part: "snapshot", issues: snapshot.error });
57
+ }
58
+ return ok({
59
+ versionId: row.version_id,
60
+ analysisId: row.analysis_id,
61
+ threadId: row.thread_id,
62
+ parentThreadId: row.parent_thread_id,
63
+ parentSeq: row.parent_seq === null ? null : Number(row.parent_seq),
64
+ parentVersionId: row.parent_version_id,
65
+ document: document.data,
66
+ snapshot: snapshot.value,
67
+ createdAt: row.created_at,
68
+ });
69
+ }
70
+ /**
71
+ * Map an insert failure to a record refusal. A trip of the thread constraint is
72
+ * thread_already_holds_version. Every other DbError stays unchanged.
73
+ */
74
+ function mapInsertError(error, threadId) {
75
+ if (error.type === "constraint_violation" && error.constraint === THREAD_UNIQUE_CONSTRAINT) {
76
+ return { type: "thread_already_holds_version", op: "report-versions.record", threadId };
77
+ }
78
+ return error;
79
+ }
80
+ /**
81
+ * Create a `ReportVersionStore` bound to a Postgres pool. The
82
+ * `cortex_report_versions` table is provisioned by the state-init DDL.
83
+ */
84
+ export function createReportVersionStore({ pool }) {
85
+ /**
86
+ * The analysis scope of a parent version, or `null` when no such row exists.
87
+ * Absence is not a verdict here: an unknown parent id falls through to the
88
+ * insert, where the self foreign key refuses it as a `DbError`.
89
+ */
90
+ function parentAnalysis(parentVersionId) {
91
+ return tryQuery("report-versions.record.parentScope", () => pool.query({
92
+ text: "SELECT analysis_id FROM cortex_report_versions WHERE version_id = $1",
93
+ values: [parentVersionId],
94
+ })).map(({ rows }) => rows[0]?.analysis_id ?? null);
95
+ }
96
+ function record(input) {
97
+ const parsed = ReportDocumentSchema.safeParse(input.document);
98
+ if (!parsed.success) {
99
+ return errAsync({ type: "malformed_document", op: "report-versions.record", issues: reduceIssues(parsed.error) });
100
+ }
101
+ const document = parsed.data;
102
+ // The parse guards the row against a caller bug. The store stores the given
103
+ // value, thus the parse changes no value.
104
+ const parsedSnapshot = parseSnapshot(input.snapshot);
105
+ if (parsedSnapshot.isErr()) {
106
+ return errAsync({ type: "malformed_snapshot", op: "report-versions.record", issues: parsedSnapshot.error });
107
+ }
108
+ const insert = () => {
109
+ const versionId = randomUUID();
110
+ return tryMutation("report-versions.record.insert", async () => {
111
+ await pool.query({
112
+ text: INSERT_SQL,
113
+ values: [
114
+ versionId,
115
+ input.analysisId,
116
+ input.threadId,
117
+ input.parentThreadId,
118
+ input.parentSeq,
119
+ input.parentVersionId ?? null,
120
+ JSON.stringify(document),
121
+ JSON.stringify(input.snapshot),
122
+ ],
123
+ });
124
+ return { versionId };
125
+ }).mapErr((error) => mapInsertError(error, input.threadId));
126
+ };
127
+ if (input.parentVersionId === undefined) {
128
+ return insert();
129
+ }
130
+ const parentVersionId = input.parentVersionId;
131
+ return parentAnalysis(parentVersionId).andThen((parentAnalysisId) => {
132
+ if (parentAnalysisId !== null && parentAnalysisId !== input.analysisId) {
133
+ return errAsync({
134
+ type: "parent_analysis_mismatch",
135
+ op: "report-versions.record",
136
+ analysisId: input.analysisId,
137
+ parentVersionId,
138
+ parentAnalysisId,
139
+ });
140
+ }
141
+ return insert();
142
+ });
143
+ }
144
+ function getVersion(versionId) {
145
+ return tryQuery("report-versions.getVersion", () => pool.query({
146
+ text: `SELECT ${VERSION_COLUMNS} FROM cortex_report_versions WHERE version_id = $1`,
147
+ values: [versionId],
148
+ })).andThen(({ rows }) => {
149
+ const row = rows[0];
150
+ if (!row)
151
+ return ok(null);
152
+ return rowToVersion(row);
153
+ });
154
+ }
155
+ function getThreadVersion(threadId) {
156
+ return tryQuery("report-versions.getThreadVersion", () => pool.query({
157
+ text: `SELECT ${VERSION_COLUMNS} FROM cortex_report_versions WHERE thread_id = $1`,
158
+ values: [threadId],
159
+ })).andThen(({ rows }) => {
160
+ const row = rows[0];
161
+ if (!row)
162
+ return ok(null);
163
+ return rowToVersion(row);
164
+ });
165
+ }
166
+ return { record, getVersion, getThreadVersion };
167
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The one parse of a stored report snapshot, shared by the two stores.
3
+ *
4
+ * The session-state store and the version store each hold a snapshot column, and
5
+ * each reads it back the same way. A path such as `__proto__` is an ordinary
6
+ * artifact key of the ledger, and the pin keeps it as an own member of a
7
+ * null-prototype map (`report-model/pin-snapshot.ts`). Each object-shaped schema
8
+ * of zod drops such a key, thus a store that parsed the map through one would
9
+ * resolve a reference to that artifact before a reload and refuse it as absent
10
+ * after one. The walk reads the own keys of the stored map, and it parses each
11
+ * value on its own, thus the two stores agree on the key set.
12
+ */
13
+ import { type Result } from "neverthrow";
14
+ import { z } from "zod";
15
+ import type { ReportSnapshot } from "../report-model/reference-resolver.js";
16
+ /** One reduced schema issue -- the dotted path and the message, without the rest. */
17
+ export interface SchemaIssue {
18
+ readonly path: string;
19
+ readonly message: string;
20
+ }
21
+ /** Reduce a zod error to the dotted path and the message of each issue. */
22
+ export declare function reduceIssues(error: z.ZodError): SchemaIssue[];
23
+ /**
24
+ * Parse a stored snapshot.
25
+ *
26
+ * The artifacts map never goes through a zod object schema, for the reason that
27
+ * the module header gives: a zod object drops a `__proto__` key and the pin keeps
28
+ * it. The walk reads the own keys of the stored map, and it parses each value on
29
+ * its own.
30
+ *
31
+ * The `ReportSnapshot` return type ties this parse to the type of the reference
32
+ * model, thus a change to one shows as a compile error here.
33
+ */
34
+ export declare function parseSnapshot(stored: unknown): Result<ReportSnapshot, SchemaIssue[]>;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The one parse of a stored report snapshot, shared by the two stores.
3
+ *
4
+ * The session-state store and the version store each hold a snapshot column, and
5
+ * each reads it back the same way. A path such as `__proto__` is an ordinary
6
+ * artifact key of the ledger, and the pin keeps it as an own member of a
7
+ * null-prototype map (`report-model/pin-snapshot.ts`). Each object-shaped schema
8
+ * of zod drops such a key, thus a store that parsed the map through one would
9
+ * resolve a reference to that artifact before a reload and refuse it as absent
10
+ * after one. The walk reads the own keys of the stored map, and it parses each
11
+ * value on its own, thus the two stores agree on the key set.
12
+ */
13
+ import { err, ok } from "neverthrow";
14
+ import { z } from "zod";
15
+ /**
16
+ * One pinned artifact of a stored snapshot. The value carries the content hash, an
17
+ * optional file type, and optional rows.
18
+ */
19
+ const ArtifactSnapshotSchema = z.object({
20
+ hash: z.string(),
21
+ fileType: z.string().nullable().optional(),
22
+ rows: z.array(z.record(z.string(), z.union([z.string(), z.number()]))).optional(),
23
+ });
24
+ /** The citation list of a stored snapshot. A snapshot with no list reads as an absent field. */
25
+ const CitationsSchema = z.array(z.string()).optional();
26
+ /** Reduce a zod error to the dotted path and the message of each issue. */
27
+ export function reduceIssues(error) {
28
+ return error.issues.map((issue) => ({
29
+ path: issue.path.map((segment) => String(segment)).join("."),
30
+ message: issue.message,
31
+ }));
32
+ }
33
+ /** Put a prefix before the path of each issue, thus a nested parse names the key that failed. */
34
+ function prefixIssues(prefix, issues) {
35
+ return issues.map((issue) => ({
36
+ path: issue.path === "" ? prefix : `${prefix}.${issue.path}`,
37
+ message: issue.message,
38
+ }));
39
+ }
40
+ /** A stored map is a plain object. An array and a null carry no own key that a map read can use. */
41
+ function isPlainRecord(value) {
42
+ return typeof value === "object" && value !== null && !Array.isArray(value);
43
+ }
44
+ /**
45
+ * Parse a stored snapshot.
46
+ *
47
+ * The artifacts map never goes through a zod object schema, for the reason that
48
+ * the module header gives: a zod object drops a `__proto__` key and the pin keeps
49
+ * it. The walk reads the own keys of the stored map, and it parses each value on
50
+ * its own.
51
+ *
52
+ * The `ReportSnapshot` return type ties this parse to the type of the reference
53
+ * model, thus a change to one shows as a compile error here.
54
+ */
55
+ export function parseSnapshot(stored) {
56
+ if (!isPlainRecord(stored)) {
57
+ return err([{ path: "", message: "the stored snapshot is not an object" }]);
58
+ }
59
+ if (!isPlainRecord(stored.artifacts)) {
60
+ return err([{ path: "artifacts", message: "the artifacts map is not an object" }]);
61
+ }
62
+ const citations = CitationsSchema.safeParse(stored.citations);
63
+ if (!citations.success) {
64
+ return err(prefixIssues("citations", reduceIssues(citations.error)));
65
+ }
66
+ // The map takes a null prototype, the same as the pin, thus a path such as
67
+ // `__proto__` stays an ordinary entry and never reaches a prototype slot.
68
+ const artifacts = Object.create(null);
69
+ const issues = [];
70
+ for (const [path, value] of Object.entries(stored.artifacts)) {
71
+ const parsed = ArtifactSnapshotSchema.safeParse(value);
72
+ if (!parsed.success) {
73
+ issues.push(...prefixIssues(`artifacts.${path}`, reduceIssues(parsed.error)));
74
+ continue;
75
+ }
76
+ artifacts[path] = parsed.data;
77
+ }
78
+ if (issues.length > 0) {
79
+ return err(issues);
80
+ }
81
+ return citations.data === undefined ? ok({ artifacts }) : ok({ artifacts, citations: citations.data });
82
+ }
@@ -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" });
@@ -2,7 +2,7 @@ import type { Pool } from "pg";
2
2
  import { type AgentSession } from "../auth/types.js";
3
3
  import type { ResourcePolicy, ResourceSpec } from "../config/resource-limits.js";
4
4
  import type { Logger } from "../lib/logger.js";
5
- import type { ChatProvider } from "../providers/types.js";
5
+ import { type ChatProvider } from "../providers/types.js";
6
6
  export declare const AD_HOC_ROUTER_AGENT_ID = "adhoc-router";
7
7
  export declare const AD_HOC_ROUTER_TIMEOUT_MS = 10000;
8
8
  export declare const AD_HOC_FALLBACK_AGENT_ID = "scientific-executor";
@@ -18,7 +18,12 @@ export interface AdHocRouterDeps {
18
18
  readonly pool: Pool;
19
19
  readonly resourcePolicy?: ResourcePolicy;
20
20
  readonly logger?: Logger;
21
- /** Test seam; production always uses {@link AD_HOC_ROUTER_TIMEOUT_MS}. */
21
+ /**
22
+ * Test seam that sets an explicit deadline. An explicit value overrides the
23
+ * derived deadline. The derived deadline is the maximum of
24
+ * {@link AD_HOC_ROUTER_TIMEOUT_MS} and the request-timeout limit that the
25
+ * provider advertises.
26
+ */
22
27
  readonly timeoutMs?: number;
23
28
  }
24
29
  export declare function defaultAdHocResources(policy?: ResourcePolicy): ResourceSpec;
@@ -6,6 +6,7 @@ import { DATA_PROFILE_ORIENTATION_MAX_CHARS, buildDataProfileOrientation } from
6
6
  import { createNoopLogger } from "../lib/console-logger.js";
7
7
  import { unwrapOrThrow } from "../lib/result.js";
8
8
  import { resourceEstimationSection } from "../prompts/planner.js";
9
+ import { effectiveDeadlineMs } from "../providers/types.js";
9
10
  import { loadDataProfileStatus } from "../state/index.js";
10
11
  export const AD_HOC_ROUTER_AGENT_ID = "adhoc-router";
11
12
  export const AD_HOC_ROUTER_TIMEOUT_MS = 10_000;
@@ -72,7 +73,7 @@ export async function routeAdHocRequest(deps, input) {
72
73
  const catalog = PLANNABLE_AGENT_CATALOG.map((agent) => `- ${agent.id}: capabilities [${agent.capabilities.join(", ")}]; suitable for [${agent.suitableFor.join(", ")}]`).join("\n");
73
74
  const orientation = await profileOrientation(deps.pool, input.analysisId, logger);
74
75
  const controller = new AbortController();
75
- const timer = setTimeout(() => controller.abort(new Error("ad hoc routing timed out")), deps.timeoutMs ?? AD_HOC_ROUTER_TIMEOUT_MS);
76
+ const timer = setTimeout(() => controller.abort(new Error("ad hoc routing timed out")), effectiveDeadlineMs(deps.provider, AD_HOC_ROUTER_TIMEOUT_MS, deps.timeoutMs));
76
77
  const signal = AbortSignal.any([input.signal, controller.signal]);
77
78
  let raw;
78
79
  let failure;
@@ -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;
@@ -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");
@@ -19,8 +19,9 @@ export interface BioToolKeys {
19
19
  * The NCBI-backed literature tool, built from the shared key slice.
20
20
  *
21
21
  * `pubmed` is the consolidated literature tool (search / details / fulltext
22
- * behind one `action`). ClinVar is no longer its own tool — it is one of the
23
- * corpora behind `gene_disease_evidence`, which also needs the NCBI key.
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.
24
25
  */
25
26
  export declare function createNcbiTools(keys: BioToolKeys): {
26
27
  pubmed: Tool;
@@ -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 is no longer its own tool — it is one of the
10
- * corpora behind `gene_disease_evidence`, which also needs the NCBI key.
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
- * The curated safety liabilities this tool used to serve now belong to
6
- * `target_safety`, which pairs them with the local secondary-pharmacology panel
7
- * and resolves the Ensembl id they need.
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
- * The curated safety liabilities this tool used to serve now belong to
6
- * `target_safety`, which pairs them with the local secondary-pharmacology panel
7
- * and resolves the Ensembl id they need.
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":
@@ -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 }));