@inflexa-ai/harness 0.30.3 → 0.31.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/contracts/target-dossier.d.ts +61 -61
  2. package/dist/execution/run-synthesis.js +7 -2
  3. package/dist/lib/zod-issues.d.ts +8 -0
  4. package/dist/lib/zod-issues.js +13 -0
  5. package/dist/literature/sources/semantic-scholar.d.ts +34 -0
  6. package/dist/literature/sources/semantic-scholar.js +15 -4
  7. package/dist/prompts/briefing.d.ts +12 -1
  8. package/dist/prompts/briefing.js +27 -1
  9. package/dist/prompts/sandbox-standards.d.ts +1 -1
  10. package/dist/prompts/sandbox-standards.js +27 -0
  11. package/dist/sandbox/thread-env.d.ts +18 -12
  12. package/dist/sandbox/thread-env.js +36 -25
  13. package/dist/tools/bio/chembl.js +4 -3
  14. package/dist/tools/bio/drug-gene-interactions.d.ts +2 -14
  15. package/dist/tools/bio/drug-gene-interactions.js +10 -18
  16. package/dist/tools/bio/gene-disease-evidence.d.ts +3 -1
  17. package/dist/tools/bio/gene-disease-evidence.js +12 -9
  18. package/dist/tools/bio/keys.js +1 -0
  19. package/dist/tools/bio/lookup-annotation.d.ts +43 -0
  20. package/dist/tools/bio/lookup-annotation.js +19 -4
  21. package/dist/tools/bio/pubchem.js +5 -3
  22. package/dist/tools/bio/search-clinical-trials.js +5 -4
  23. package/dist/tools/bio/search-geo-datasets.d.ts +39 -9
  24. package/dist/tools/bio/search-geo-datasets.js +38 -20
  25. package/dist/tools/lib/api-utils.d.ts +27 -2
  26. package/dist/tools/lib/api-utils.js +43 -3
  27. package/dist/tools/lib/bgee-client.d.ts +16 -0
  28. package/dist/tools/lib/bgee-client.js +16 -5
  29. package/dist/tools/lib/cbioportal-client.d.ts +20 -0
  30. package/dist/tools/lib/cbioportal-client.js +16 -8
  31. package/dist/tools/lib/chembl-client.d.ts +105 -3
  32. package/dist/tools/lib/chembl-client.js +196 -84
  33. package/dist/tools/lib/clinical-benchmarks-client.d.ts +5 -0
  34. package/dist/tools/lib/clinical-benchmarks-client.js +5 -0
  35. package/dist/tools/lib/clinical-trials-client.d.ts +131 -2
  36. package/dist/tools/lib/clinical-trials-client.js +32 -6
  37. package/dist/tools/lib/clinvar-client.d.ts +35 -0
  38. package/dist/tools/lib/clinvar-client.js +34 -28
  39. package/dist/tools/lib/ctx-ops.d.ts +106 -35
  40. package/dist/tools/lib/ctx-ops.js +135 -69
  41. package/dist/tools/lib/dgidb-client.d.ts +73 -0
  42. package/dist/tools/lib/dgidb-client.js +25 -13
  43. package/dist/tools/lib/disgenet-client.d.ts +117 -24
  44. package/dist/tools/lib/disgenet-client.js +127 -34
  45. package/dist/tools/lib/disgenet-config.d.ts +1 -1
  46. package/dist/tools/lib/disgenet-config.js +5 -3
  47. package/dist/tools/lib/drugbank-client.d.ts +57 -58
  48. package/dist/tools/lib/drugbank-client.js +69 -64
  49. package/dist/tools/lib/drugbank-config.d.ts +6 -1
  50. package/dist/tools/lib/drugbank-config.js +6 -1
  51. package/dist/tools/lib/ema-client.d.ts +53 -0
  52. package/dist/tools/lib/ema-client.js +19 -7
  53. package/dist/tools/lib/ensembl-client.d.ts +4 -0
  54. package/dist/tools/lib/ensembl-client.js +4 -0
  55. package/dist/tools/lib/gwas-catalog-client.d.ts +64 -1
  56. package/dist/tools/lib/gwas-catalog-client.js +21 -6
  57. package/dist/tools/lib/identifier-resolver.d.ts +17 -1
  58. package/dist/tools/lib/identifier-resolver.js +13 -4
  59. package/dist/tools/lib/impc-client.d.ts +4 -0
  60. package/dist/tools/lib/impc-client.js +4 -0
  61. package/dist/tools/lib/iuphar-client.d.ts +10 -5
  62. package/dist/tools/lib/iuphar-client.js +49 -13
  63. package/dist/tools/lib/monarch-client.d.ts +4 -0
  64. package/dist/tools/lib/monarch-client.js +4 -0
  65. package/dist/tools/lib/openfda-client.d.ts +34 -0
  66. package/dist/tools/lib/openfda-client.js +22 -8
  67. package/dist/tools/lib/opentargets-client.d.ts +99 -4
  68. package/dist/tools/lib/opentargets-client.js +97 -37
  69. package/dist/tools/lib/pathway-client.d.ts +45 -0
  70. package/dist/tools/lib/pathway-client.js +88 -8
  71. package/dist/tools/lib/pharmgkb-client.d.ts +35 -1
  72. package/dist/tools/lib/pharmgkb-client.js +22 -6
  73. package/dist/tools/lib/pharmgkb-config.d.ts +8 -1
  74. package/dist/tools/lib/pharmgkb-config.js +8 -1
  75. package/dist/tools/lib/pubchem-client.d.ts +22 -0
  76. package/dist/tools/lib/pubchem-client.js +36 -33
  77. package/dist/tools/lib/pubchem-ops.d.ts +85 -5
  78. package/dist/tools/lib/pubchem-ops.js +96 -68
  79. package/dist/tools/lib/pubmed-client.d.ts +4 -0
  80. package/dist/tools/lib/pubmed-client.js +4 -0
  81. package/dist/tools/lib/string-client.d.ts +52 -0
  82. package/dist/tools/lib/string-client.js +12 -7
  83. package/dist/tools/lib/uniprot-client.d.ts +4 -0
  84. package/dist/tools/lib/uniprot-client.js +4 -0
  85. package/dist/tools/research/context7-docs.d.ts +23 -0
  86. package/dist/tools/research/context7-docs.js +49 -9
  87. package/dist/tools/research/search-github-repos.d.ts +4 -0
  88. package/dist/tools/research/search-github-repos.js +4 -0
  89. package/dist/workflows/execute-target-assessment.js +4 -4
  90. package/dist/workflows/target-assessment/assemblers/orchestrator.d.ts +2 -1
  91. package/dist/workflows/target-assessment/assemblers/orchestrator.js +3 -3
  92. package/dist/workflows/target-assessment/collectors/index.d.ts +8 -2
  93. package/dist/workflows/target-assessment/collectors/index.js +37 -10
  94. package/dist/workflows/target-assessment/fanout/index.d.ts +2 -1
  95. package/dist/workflows/target-assessment/fanout/index.js +2 -2
  96. package/dist/workflows/target-assessment/lib/safety-corroboration.js +2 -2
  97. package/dist/workflows/target-assessment/lib/target-identity-filter.d.ts +3 -2
  98. package/dist/workflows/target-assessment/lib/target-identity-filter.js +16 -5
  99. package/dist/workflows/target-assessment/phase4-assemble.d.ts +61 -60
  100. package/dist/workflows/target-assessment/phase4-assemble.js +2 -2
  101. package/dist/workflows/target-assessment/schemas.d.ts +2 -8
  102. package/dist/workflows/target-assessment/schemas.js +8 -2
  103. package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +1 -4
  104. package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +1 -4
  105. package/package.json +1 -1
  106. package/dist/agents/sandbox/__fixtures__/deps.d.ts +0 -23
  107. package/dist/agents/sandbox/__fixtures__/deps.js +0 -102
  108. package/dist/citations/__fixtures__/resolver.d.ts +0 -3
  109. package/dist/citations/__fixtures__/resolver.js +0 -9
  110. package/dist/loop/__fixtures__/scripted-provider.d.ts +0 -31
  111. package/dist/loop/__fixtures__/scripted-provider.js +0 -81
  112. package/dist/providers/__fixtures__/session.d.ts +0 -17
  113. package/dist/providers/__fixtures__/session.js +0 -20
  114. package/dist/tools/__fixtures__/tool-context.d.ts +0 -13
  115. package/dist/tools/__fixtures__/tool-context.js +0 -22
@@ -19,9 +19,15 @@
19
19
  * `withHost(...)` semaphore. The function signature reserves the future
20
20
  * `deps` slot for test injection without churning callers.
21
21
  */
22
+ import type { Logger } from "../../../lib/logger.js";
22
23
  /** Per-run context threaded to collectors that hit keyed external APIs. */
23
24
  export interface CollectorCtx {
24
25
  readonly ncbiApiKey?: string;
26
+ /**
27
+ * The diagnostics seam of the run. A collector degrades to a coverage
28
+ * envelope, thus an unexpected cause is invisible without a record here.
29
+ */
30
+ readonly logger?: Logger;
25
31
  }
26
32
  import type { SerializedError } from "../coverage.js";
27
33
  import type { CbioportalBundle, ChemblModulatorsBundle, ClinvarBundle, CtgovBundle, ExpressionHumanBundle, ExpressionMultiSpeciesBundle, FaersByTargetBundle, FamilyComplexesBundle, ImpcBundle, MonarchBundle, OpenTargetsBundle, PathwaysBundle, PubmedIndexBundle, ResolvedTarget, StringPpiBundle, TherapeuticProgramsBundle } from "../schemas.js";
@@ -39,13 +45,13 @@ type CoverageNotLoaded = {
39
45
  };
40
46
  type CoverageEnvelope<T> = CoverageAvailable<T> | CoverageQueriedNoData | CoverageNotLoaded;
41
47
  export declare function collectCbioportal(input: ResolvedTarget): Promise<CoverageEnvelope<CbioportalBundle>>;
42
- export declare function collectChemblModulators(input: ResolvedTarget): Promise<CoverageEnvelope<ChemblModulatorsBundle>>;
48
+ export declare function collectChemblModulators(input: ResolvedTarget, ctx: CollectorCtx): Promise<CoverageEnvelope<ChemblModulatorsBundle>>;
43
49
  export declare function collectClinvar(input: ResolvedTarget, ctx: CollectorCtx): Promise<CoverageEnvelope<ClinvarBundle>>;
44
50
  export declare function collectCtgov(input: ResolvedTarget): Promise<CoverageEnvelope<CtgovBundle>>;
45
51
  export declare function collectExpressionHuman(input: ResolvedTarget): Promise<CoverageEnvelope<ExpressionHumanBundle>>;
46
52
  export declare function collectExpressionMultiSpecies(input: ResolvedTarget): Promise<CoverageEnvelope<ExpressionMultiSpeciesBundle>>;
47
53
  export declare function collectFaersByTarget(input: ResolvedTarget): Promise<CoverageEnvelope<FaersByTargetBundle>>;
48
- export declare function collectFamilyComplexes(input: ResolvedTarget): Promise<CoverageEnvelope<FamilyComplexesBundle>>;
54
+ export declare function collectFamilyComplexes(input: ResolvedTarget, ctx: CollectorCtx): Promise<CoverageEnvelope<FamilyComplexesBundle>>;
49
55
  export declare function collectImpc(input: ResolvedTarget): Promise<CoverageEnvelope<ImpcBundle>>;
50
56
  export declare function collectMonarch(input: ResolvedTarget): Promise<CoverageEnvelope<MonarchBundle>>;
51
57
  export declare function collectOpenTargets(input: ResolvedTarget): Promise<CoverageEnvelope<OpenTargetsBundle>>;
@@ -19,6 +19,7 @@
19
19
  * `withHost(...)` semaphore. The function signature reserves the future
20
20
  * `deps` slot for test injection without churning callers.
21
21
  */
22
+ import { createNoopLogger } from "../../../lib/console-logger.js";
22
23
  import { withHost } from "../../../lib/host-concurrency.js";
23
24
  import { getMultiSpeciesExpression } from "../../../tools/lib/bgee-client.js";
24
25
  import { getSomaticMutationFrequencies } from "../../../tools/lib/cbioportal-client.js";
@@ -60,18 +61,18 @@ export async function collectCbioportal(input) {
60
61
  }
61
62
  }
62
63
  // ── 2. ChEMBL modulators ─────────────────────────────────────────────
63
- export async function collectChemblModulators(input) {
64
+ export async function collectChemblModulators(input, ctx) {
64
65
  const chemblId = input.ids.chembl;
65
66
  if (!chemblId) {
66
67
  return { coverage: "not_loaded", reason: "no ChEMBL target id resolved" };
67
68
  }
68
69
  try {
69
- const primaryRows = await withHost("chembl", () => getTargetModulators(chemblId, { minPhase: 2, limit: 200 }));
70
+ const primaryRows = await withHost("chembl", () => getTargetModulators(chemblId, { minPhase: 2, limit: 200 }, ctx.logger));
70
71
  const activityRows = await withHost("chembl", () => getModulatorsViaActivity({
71
72
  target_chembl_id: chemblId,
72
73
  max_phase_min: 2,
73
74
  molecule_type: "Small molecule",
74
- }));
75
+ }, ctx.logger));
75
76
  const primaryIds = new Set(primaryRows.map((m) => m.moleculeChemblId));
76
77
  const novelActivityRows = activityRows
77
78
  .filter((m) => !primaryIds.has(m.moleculeChemblId))
@@ -153,18 +154,32 @@ export async function collectExpressionHuman(input) {
153
154
  },
154
155
  };
155
156
  }
156
- const tissues = expressions.map((e) => ({
157
+ // `baselineExpression` mixes datasources under one row type, and each one
158
+ // carries its own unit: GTEx gives bulk-tissue TPM, Tabula Sapiens gives
159
+ // single-cell CPM pseudobulk, and DICE gives immune-cell TPM. The bundle
160
+ // declares one source and one unit, thus it can hold rows of one datasource
161
+ // only. Tabula Sapiens also outnumbers GTEx about 20 to 1, thus an unfiltered
162
+ // list drowns the tissue table in cell types.
163
+ const gtex = expressions.filter((e) => e.datasourceId === "gtex");
164
+ if (gtex.length === 0) {
165
+ return {
166
+ coverage: "queried_no_data",
167
+ error: {
168
+ message: `Open Targets carries no GTEx baseline expression for ${ensemblId} (${expressions.length} rows of other datasources dropped)`,
169
+ },
170
+ };
171
+ }
172
+ const tissues = gtex.map((e) => ({
157
173
  tissueLabel: e.tissueLabel,
158
174
  organSystem: e.organSystem,
159
175
  value: e.rna?.value ?? null,
160
- protein: e.protein?.level ?? null,
161
176
  }));
162
177
  return {
163
178
  coverage: "available",
164
179
  data: {
165
- source: "hpa_consensus",
166
- unit: "consensus_normalized",
167
- normalization_notes: "Open Targets consensus expression (HPA RNA tissue, TMM-normalized nTPM across samples)",
180
+ source: "gtex",
181
+ unit: "tpm",
182
+ normalization_notes: "Open Targets baseline expression, GTEx bulk-tissue rows only, as the median TPM of each tissue",
168
183
  tissues,
169
184
  },
170
185
  };
@@ -241,7 +256,7 @@ export async function collectFaersByTarget(input) {
241
256
  }
242
257
  }
243
258
  // ── 8. Family complexes (IUPHAR) ─────────────────────────────────────
244
- export async function collectFamilyComplexes(input) {
259
+ export async function collectFamilyComplexes(input, ctx) {
245
260
  const geneSymbol = input.geneSymbol;
246
261
  const uniprot = input.ids.uniprot;
247
262
  const lookup = uniprot ?? geneSymbol;
@@ -284,6 +299,12 @@ export async function collectFamilyComplexes(input) {
284
299
  };
285
300
  }
286
301
  catch (err) {
302
+ // `getFamilyHeterodimers` answers an unknown target with an empty list, thus
303
+ // a throw here is always an unexpected cause. The IUPHAR outage of 2026 hid
304
+ // behind this envelope for months, because `queried_no_data` reads as a
305
+ // clean miss to every consumer of the dossier.
306
+ const log = (ctx.logger ?? createNoopLogger()).named("ta-collector.family-complexes");
307
+ log.error("IUPHAR family-complex lookup failed", { lookup, ...log.errorFields(err) });
287
308
  return fail(err);
288
309
  }
289
310
  }
@@ -385,7 +406,13 @@ export async function collectOpenTargets(input) {
385
406
  tractability: info.tractability,
386
407
  associations: info.associations,
387
408
  safetyLiabilities: safety?.safetyLiabilities ?? [],
388
- baselineExpression: expression,
409
+ baselineExpression: expression.map((e) => ({
410
+ tissueId: e.tissueId,
411
+ tissueLabel: e.tissueLabel,
412
+ organSystem: e.organSystem,
413
+ datasourceId: e.datasourceId,
414
+ rna: e.rna,
415
+ })),
389
416
  },
390
417
  };
391
418
  }
@@ -10,6 +10,7 @@
10
10
  * No throws — coverage envelope is the failure mode. Recovery replays the
11
11
  * cached envelope from the DBOS step cache.
12
12
  */
13
+ import type { Logger } from "../../../lib/logger.js";
13
14
  import type { SerializedError } from "../coverage.js";
14
15
  import type { ClassDrugItem, PerClassDrugAEsItem } from "../steps/fanout/aes-for-one-class-drug-step.js";
15
16
  import type { PerTrialAEsItem, TrialItem } from "../steps/fanout/aes-for-one-trial-step.js";
@@ -26,6 +27,6 @@ type CoverageQueriedNoData = {
26
27
  };
27
28
  type CoverageEnvelope<T> = CoverageAvailable<T> | CoverageQueriedNoData;
28
29
  export declare function faersForOneModulator(item: ModulatorItem): Promise<CoverageEnvelope<PerModulatorFaersItem>>;
29
- export declare function polypharmForOneModulator(item: PolypharmInputItem): Promise<CoverageEnvelope<PerModulatorPolypharmItem>>;
30
+ export declare function polypharmForOneModulator(item: PolypharmInputItem, logger?: Logger): Promise<CoverageEnvelope<PerModulatorPolypharmItem>>;
30
31
  export declare function aesForOneTrial(item: TrialItem): Promise<CoverageEnvelope<PerTrialAEsItem>>;
31
32
  export declare function aesForOneClassDrug(item: ClassDrugItem): Promise<CoverageEnvelope<PerClassDrugAEsItem>>;
@@ -61,7 +61,7 @@ export async function faersForOneModulator(item) {
61
61
  }
62
62
  }
63
63
  // ── Per-modulator polypharmacology ──────────────────────────────────
64
- export async function polypharmForOneModulator(item) {
64
+ export async function polypharmForOneModulator(item, logger) {
65
65
  try {
66
66
  const [hits, primaryPchembl] = await Promise.all([
67
67
  withHost("chembl", () => getModulatorPolypharmacology(item.moleculeChemblId, {
@@ -70,7 +70,7 @@ export async function polypharmForOneModulator(item) {
70
70
  limit: 200,
71
71
  })),
72
72
  item.primaryTargetChemblId
73
- ? withHost("chembl", () => getModulatorOnTargetPchembl(item.moleculeChemblId, item.primaryTargetChemblId))
73
+ ? withHost("chembl", () => getModulatorOnTargetPchembl(item.moleculeChemblId, item.primaryTargetChemblId, logger))
74
74
  : Promise.resolve(null),
75
75
  ]);
76
76
  if (hits.length === 0) {
@@ -124,8 +124,8 @@ function extractCuratedSafetyLiabilities(input) {
124
124
  /**
125
125
  * Baseline expression in safety-relevant tissues.
126
126
  *
127
- * The atlas reaches the dossier through Open Targets' consensus expression,
128
- * which is Human Protein Atlas data — the same path
127
+ * The atlas reaches the dossier through the Open Targets `baselineExpression`
128
+ * surface, narrowed to the GTEx bulk-tissue rows — the same path
129
129
  * `reference_biology.normal_tissue_expression` and the off-tissue risk rows
130
130
  * read, with the same safety-relevance and floor rules applied. Only the
131
131
  * highest-expressing tissue per organ is raised: several tissues mapping to one
@@ -10,6 +10,7 @@
10
10
  * resolved arrays as plain inputs so the per-row assembly path stays
11
11
  * synchronous after a single upfront await.
12
12
  */
13
+ import type { Logger } from "../../../lib/logger.js";
13
14
  export type TrialAttributionInput = {
14
15
  assessmentUniprot: string;
15
16
  /** UniProts of related family receptors (resolved from IUPHAR family siblings). */
@@ -41,7 +42,7 @@ export declare function classifyTrialAttribution(input: TrialAttributionInput):
41
42
  * Network failures are surfaced as an empty list so the assembler degrades
42
43
  * to "no alternate-id filtering" rather than blocking the run.
43
44
  */
44
- export declare function resolveOnTargetChemblIds(assessmentUniprot: string): Promise<string[]>;
45
+ export declare function resolveOnTargetChemblIds(assessmentUniprot: string, logger?: Logger): Promise<string[]>;
45
46
  /**
46
47
  * Resolve UniProt accessions of sibling receptors in the same IUPHAR family
47
48
  * as the assessment target. Used by `classifyTrialAttribution` so a trial
@@ -49,7 +50,7 @@ export declare function resolveOnTargetChemblIds(assessmentUniprot: string): Pro
49
50
  * assessment target) lands in the related bucket rather than the
50
51
  * primary trials list.
51
52
  */
52
- export declare function resolveFamilySiblingUniprots(uniprotOrGeneSymbol: string): Promise<string[]>;
53
+ export declare function resolveFamilySiblingUniprots(uniprotOrGeneSymbol: string, logger?: Logger): Promise<string[]>;
53
54
  /**
54
55
  * Returns true if `chemblId` is among the supplied list of on-target ChEMBL
55
56
  * ids for the assessment protein. Caller is responsible for resolving the
@@ -10,6 +10,7 @@
10
10
  * resolved arrays as plain inputs so the per-row assembly path stays
11
11
  * synchronous after a single upfront await.
12
12
  */
13
+ import { createNoopLogger } from "../../../lib/console-logger.js";
13
14
  import { withHost } from "../../../lib/host-concurrency.js";
14
15
  import { getFamilySiblingUniprots } from "../../../tools/lib/iuphar-client.js";
15
16
  import { getChemblIdsByUniProt } from "../../../tools/lib/uniprot-client.js";
@@ -47,13 +48,18 @@ export async function classifyTrialAttribution(input) {
47
48
  * Network failures are surfaced as an empty list so the assembler degrades
48
49
  * to "no alternate-id filtering" rather than blocking the run.
49
50
  */
50
- export async function resolveOnTargetChemblIds(assessmentUniprot) {
51
+ export async function resolveOnTargetChemblIds(assessmentUniprot, logger) {
51
52
  if (!assessmentUniprot)
52
53
  return [];
53
54
  try {
54
55
  return await withHost("uniprot", () => getChemblIdsByUniProt(assessmentUniprot));
55
56
  }
56
- catch {
57
+ catch (err) {
58
+ // UniProt answers an unknown accession with a 404, and the client maps that
59
+ // to an empty record. Thus a throw here is always an unexpected cause, and
60
+ // the empty list silently turns off the self-hit filter of the panel.
61
+ const log = (logger ?? createNoopLogger()).named("ta-identity.on-target-chembl");
62
+ log.error("UniProt ChEMBL cross-reference lookup failed", { uniprot: assessmentUniprot, ...log.errorFields(err) });
57
63
  return [];
58
64
  }
59
65
  }
@@ -64,13 +70,18 @@ export async function resolveOnTargetChemblIds(assessmentUniprot) {
64
70
  * assessment target) lands in the related bucket rather than the
65
71
  * primary trials list.
66
72
  */
67
- export async function resolveFamilySiblingUniprots(uniprotOrGeneSymbol) {
73
+ export async function resolveFamilySiblingUniprots(uniprotOrGeneSymbol, logger) {
68
74
  if (!uniprotOrGeneSymbol)
69
75
  return [];
76
+ const log = (logger ?? createNoopLogger()).named("ta-identity.family-siblings");
70
77
  try {
71
- return await withHost("iuphar", () => getFamilySiblingUniprots(uniprotOrGeneSymbol));
78
+ return await withHost("iuphar", () => getFamilySiblingUniprots(uniprotOrGeneSymbol, log));
72
79
  }
73
- catch {
80
+ catch (err) {
81
+ // IUPHAR answers an unknown target with an empty list, thus a throw here is
82
+ // always an unexpected cause. The empty list then routes a paralog trial into
83
+ // the primary trials list, which is the defect that this resolver prevents.
84
+ log.error("IUPHAR family-sibling lookup failed", { query: uniprotOrGeneSymbol, ...log.errorFields(err) });
74
85
  return [];
75
86
  }
76
87
  }
@@ -19,70 +19,13 @@
19
19
  */
20
20
  import type { Pool } from "pg";
21
21
  import { z } from "zod";
22
+ import type { Logger } from "../../lib/logger.js";
22
23
  import type { ClinicalConsequenceAnnotatorDeps } from "./lib/clinical-consequence-annotator.js";
23
24
  import type { Phase3Bundle } from "./steps/phase3-aggregate.js";
24
25
  import { Phase3BundleSchema } from "./steps/phase3-aggregate.js";
25
26
  export declare const Phase4OutputSchema: z.ZodObject<{
26
27
  assessmentId: z.ZodString;
27
28
  dossier: z.ZodObject<{
28
- drug_interactions: z.ZodDiscriminatedUnion<[z.ZodObject<{
29
- coverage: z.ZodLiteral<"available">;
30
- data: z.ZodObject<{
31
- rows: z.ZodArray<z.ZodObject<{
32
- drug_id: z.ZodNullable<z.ZodString>;
33
- drug_name: z.ZodString;
34
- best_score: z.ZodNumber;
35
- predicates: z.ZodArray<z.ZodString>;
36
- sources: z.ZodArray<z.ZodString>;
37
- paper_count: z.ZodNumber;
38
- dominant_direction: z.ZodEnum<{
39
- unknown: "unknown";
40
- positive: "positive";
41
- negative: "negative";
42
- mixed: "mixed";
43
- }>;
44
- has_human_evidence: z.ZodBoolean;
45
- has_clinical_evidence: z.ZodBoolean;
46
- evidence: z.ZodArray<z.ZodObject<{
47
- pmid: z.ZodOptional<z.ZodString>;
48
- doi: z.ZodOptional<z.ZodString>;
49
- accession: z.ZodOptional<z.ZodString>;
50
- source: z.ZodString;
51
- predicate: z.ZodOptional<z.ZodString>;
52
- score: z.ZodOptional<z.ZodNumber>;
53
- strength: z.ZodOptional<z.ZodString>;
54
- is_human: z.ZodOptional<z.ZodBoolean>;
55
- is_clinical: z.ZodOptional<z.ZodBoolean>;
56
- excerpt: z.ZodOptional<z.ZodString>;
57
- metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
58
- regulatory_reference: z.ZodOptional<z.ZodObject<{
59
- document: z.ZodString;
60
- section: z.ZodOptional<z.ZodString>;
61
- doc_id: z.ZodOptional<z.ZodString>;
62
- doc_url: z.ZodOptional<z.ZodString>;
63
- }, z.core.$strip>>;
64
- }, z.core.$strip>>;
65
- }, z.core.$strip>>;
66
- }, z.core.$strip>;
67
- dropped_count: z.ZodOptional<z.ZodNumber>;
68
- inference_path: z.ZodOptional<z.ZodString>;
69
- truncated: z.ZodOptional<z.ZodBoolean>;
70
- }, z.core.$strip>, z.ZodObject<{
71
- coverage: z.ZodLiteral<"queried_no_data">;
72
- error: z.ZodOptional<z.ZodObject<{
73
- message: z.ZodString;
74
- source: z.ZodOptional<z.ZodString>;
75
- }, z.core.$strip>>;
76
- inference_path: z.ZodOptional<z.ZodString>;
77
- }, z.core.$strip>, z.ZodObject<{
78
- coverage: z.ZodLiteral<"filtered">;
79
- filter: z.ZodString;
80
- dropped_count: z.ZodNumber;
81
- inference_path: z.ZodOptional<z.ZodString>;
82
- }, z.core.$strip>, z.ZodObject<{
83
- coverage: z.ZodLiteral<"not_loaded">;
84
- reason: z.ZodOptional<z.ZodString>;
85
- }, z.core.$strip>], "coverage">;
86
29
  tractability: z.ZodDiscriminatedUnion<[z.ZodObject<{
87
30
  coverage: z.ZodLiteral<"available">;
88
31
  data: z.ZodObject<{
@@ -130,8 +73,8 @@ export declare const Phase4OutputSchema: z.ZodObject<{
130
73
  displayName: z.ZodString;
131
74
  entityType: z.ZodEnum<{
132
75
  gene: "gene";
133
- rna: "rna";
134
76
  protein: "protein";
77
+ rna: "rna";
135
78
  complex: "complex";
136
79
  }>;
137
80
  ontology: z.ZodString;
@@ -429,6 +372,64 @@ export declare const Phase4OutputSchema: z.ZodObject<{
429
372
  coverage: z.ZodLiteral<"not_loaded">;
430
373
  reason: z.ZodOptional<z.ZodString>;
431
374
  }, z.core.$strip>], "coverage">;
375
+ drug_interactions: z.ZodDiscriminatedUnion<[z.ZodObject<{
376
+ coverage: z.ZodLiteral<"available">;
377
+ data: z.ZodObject<{
378
+ rows: z.ZodArray<z.ZodObject<{
379
+ drug_id: z.ZodNullable<z.ZodString>;
380
+ drug_name: z.ZodString;
381
+ best_score: z.ZodNumber;
382
+ predicates: z.ZodArray<z.ZodString>;
383
+ sources: z.ZodArray<z.ZodString>;
384
+ paper_count: z.ZodNumber;
385
+ dominant_direction: z.ZodEnum<{
386
+ unknown: "unknown";
387
+ positive: "positive";
388
+ negative: "negative";
389
+ mixed: "mixed";
390
+ }>;
391
+ has_human_evidence: z.ZodBoolean;
392
+ has_clinical_evidence: z.ZodBoolean;
393
+ evidence: z.ZodArray<z.ZodObject<{
394
+ pmid: z.ZodOptional<z.ZodString>;
395
+ doi: z.ZodOptional<z.ZodString>;
396
+ accession: z.ZodOptional<z.ZodString>;
397
+ source: z.ZodString;
398
+ predicate: z.ZodOptional<z.ZodString>;
399
+ score: z.ZodOptional<z.ZodNumber>;
400
+ strength: z.ZodOptional<z.ZodString>;
401
+ is_human: z.ZodOptional<z.ZodBoolean>;
402
+ is_clinical: z.ZodOptional<z.ZodBoolean>;
403
+ excerpt: z.ZodOptional<z.ZodString>;
404
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
405
+ regulatory_reference: z.ZodOptional<z.ZodObject<{
406
+ document: z.ZodString;
407
+ section: z.ZodOptional<z.ZodString>;
408
+ doc_id: z.ZodOptional<z.ZodString>;
409
+ doc_url: z.ZodOptional<z.ZodString>;
410
+ }, z.core.$strip>>;
411
+ }, z.core.$strip>>;
412
+ }, z.core.$strip>>;
413
+ }, z.core.$strip>;
414
+ dropped_count: z.ZodOptional<z.ZodNumber>;
415
+ inference_path: z.ZodOptional<z.ZodString>;
416
+ truncated: z.ZodOptional<z.ZodBoolean>;
417
+ }, z.core.$strip>, z.ZodObject<{
418
+ coverage: z.ZodLiteral<"queried_no_data">;
419
+ error: z.ZodOptional<z.ZodObject<{
420
+ message: z.ZodString;
421
+ source: z.ZodOptional<z.ZodString>;
422
+ }, z.core.$strip>>;
423
+ inference_path: z.ZodOptional<z.ZodString>;
424
+ }, z.core.$strip>, z.ZodObject<{
425
+ coverage: z.ZodLiteral<"filtered">;
426
+ filter: z.ZodString;
427
+ dropped_count: z.ZodNumber;
428
+ inference_path: z.ZodOptional<z.ZodString>;
429
+ }, z.core.$strip>, z.ZodObject<{
430
+ coverage: z.ZodLiteral<"not_loaded">;
431
+ reason: z.ZodOptional<z.ZodString>;
432
+ }, z.core.$strip>], "coverage">;
432
433
  clinical_development: z.ZodObject<{
433
434
  trials: z.ZodDiscriminatedUnion<[z.ZodObject<{
434
435
  coverage: z.ZodLiteral<"available">;
@@ -3178,4 +3179,4 @@ export type { Phase3Bundle };
3178
3179
  * Run Phase 4 — assemble the dossier body from the Phase-3 bundle.
3179
3180
  * Safely cached on DBOS replay; the caller supplies the annotator seam.
3180
3181
  */
3181
- export declare function phase4Assemble(pool: Pool, phase3: Phase3Bundle, annotatorDeps?: ClinicalConsequenceAnnotatorDeps): Promise<Phase4Output>;
3182
+ export declare function phase4Assemble(pool: Pool, phase3: Phase3Bundle, annotatorDeps?: ClinicalConsequenceAnnotatorDeps, logger?: Logger): Promise<Phase4Output>;
@@ -30,8 +30,8 @@ export { Phase3BundleSchema };
30
30
  * Run Phase 4 — assemble the dossier body from the Phase-3 bundle.
31
31
  * Safely cached on DBOS replay; the caller supplies the annotator seam.
32
32
  */
33
- export async function phase4Assemble(pool, phase3, annotatorDeps) {
34
- const dossier = await assembleDossier(pool, phase3.phase2, phase3, annotatorDeps);
33
+ export async function phase4Assemble(pool, phase3, annotatorDeps, logger) {
34
+ const dossier = await assembleDossier(pool, phase3.phase2, phase3, annotatorDeps, logger);
35
35
  return {
36
36
  assessmentId: phase3.phase2.phase1.resolved.assessmentId,
37
37
  dossier,
@@ -70,13 +70,11 @@ export declare const OpenTargetsBundleSchema: z.ZodObject<{
70
70
  tissueId: z.ZodString;
71
71
  tissueLabel: z.ZodString;
72
72
  organSystem: z.ZodNullable<z.ZodString>;
73
+ datasourceId: z.ZodString;
73
74
  rna: z.ZodNullable<z.ZodObject<{
74
75
  value: z.ZodNumber;
75
76
  unit: z.ZodString;
76
77
  }, z.core.$strip>>;
77
- protein: z.ZodNullable<z.ZodObject<{
78
- level: z.ZodNullable<z.ZodNumber>;
79
- }, z.core.$strip>>;
80
78
  }, z.core.$strip>>;
81
79
  }, z.core.$strip>;
82
80
  export type OpenTargetsBundle = z.infer<typeof OpenTargetsBundleSchema>;
@@ -232,7 +230,6 @@ export declare const ExpressionHumanBundleSchema: z.ZodObject<{
232
230
  tissueLabel: z.ZodString;
233
231
  organSystem: z.ZodNullable<z.ZodString>;
234
232
  value: z.ZodNullable<z.ZodNumber>;
235
- protein: z.ZodNullable<z.ZodNumber>;
236
233
  }, z.core.$strip>>;
237
234
  }, z.core.$strip>;
238
235
  export type ExpressionHumanBundle = z.infer<typeof ExpressionHumanBundleSchema>;
@@ -487,13 +484,11 @@ export declare const Phase1BundleSchema: z.ZodObject<{
487
484
  tissueId: z.ZodString;
488
485
  tissueLabel: z.ZodString;
489
486
  organSystem: z.ZodNullable<z.ZodString>;
487
+ datasourceId: z.ZodString;
490
488
  rna: z.ZodNullable<z.ZodObject<{
491
489
  value: z.ZodNumber;
492
490
  unit: z.ZodString;
493
491
  }, z.core.$strip>>;
494
- protein: z.ZodNullable<z.ZodObject<{
495
- level: z.ZodNullable<z.ZodNumber>;
496
- }, z.core.$strip>>;
497
492
  }, z.core.$strip>>;
498
493
  }, z.core.$strip>;
499
494
  }, z.core.$strip>, z.ZodObject<{
@@ -659,7 +654,6 @@ export declare const Phase1BundleSchema: z.ZodObject<{
659
654
  tissueLabel: z.ZodString;
660
655
  organSystem: z.ZodNullable<z.ZodString>;
661
656
  value: z.ZodNullable<z.ZodNumber>;
662
- protein: z.ZodNullable<z.ZodNumber>;
663
657
  }, z.core.$strip>>;
664
658
  }, z.core.$strip>;
665
659
  }, z.core.$strip>, z.ZodObject<{
@@ -73,12 +73,17 @@ export const OpenTargetsBundleSchema = z.object({
73
73
  effects: z.string().nullable(),
74
74
  source: z.string(),
75
75
  })),
76
+ // One target carries rows of more than one datasource, and each datasource has
77
+ // its own unit. Thus `datasourceId` rides with the row, because a reader that
78
+ // compares two rows without it compares a TPM against a CPM. The
79
+ // `baselineExpression` surface carries no protein measurement, thus the bundle
80
+ // makes no protein claim.
76
81
  baselineExpression: z.array(z.object({
77
82
  tissueId: z.string(),
78
83
  tissueLabel: z.string(),
79
84
  organSystem: z.string().nullable(),
85
+ datasourceId: z.string(),
80
86
  rna: z.object({ value: z.number(), unit: z.string() }).nullable(),
81
- protein: z.object({ level: z.number().nullable() }).nullable(),
82
87
  })),
83
88
  });
84
89
  export const ChemblModulatorsBundleSchema = z.object({
@@ -141,6 +146,8 @@ export const FaersByTargetBundleSchema = z.object({
141
146
  })
142
147
  .nullable(),
143
148
  });
149
+ // The rows come from the Open Targets `baselineExpression` surface, which carries
150
+ // no protein measurement. Thus a row states an RNA value only.
144
151
  export const ExpressionHumanBundleSchema = z.object({
145
152
  source: z.enum(["gtex", "hpa_consensus", "hpa_rna_tissue"]),
146
153
  unit: z.enum(["tpm", "ntpm", "consensus_normalized"]),
@@ -149,7 +156,6 @@ export const ExpressionHumanBundleSchema = z.object({
149
156
  tissueLabel: z.string(),
150
157
  organSystem: z.string().nullable(),
151
158
  value: z.number().nullable(),
152
- protein: z.number().nullable(),
153
159
  })),
154
160
  });
155
161
  export const ExpressionMultiSpeciesBundleSchema = z.object({
@@ -73,13 +73,11 @@ export declare const Phase2BundleSchema: z.ZodObject<{
73
73
  tissueId: z.ZodString;
74
74
  tissueLabel: z.ZodString;
75
75
  organSystem: z.ZodNullable<z.ZodString>;
76
+ datasourceId: z.ZodString;
76
77
  rna: z.ZodNullable<z.ZodObject<{
77
78
  value: z.ZodNumber;
78
79
  unit: z.ZodString;
79
80
  }, z.core.$strip>>;
80
- protein: z.ZodNullable<z.ZodObject<{
81
- level: z.ZodNullable<z.ZodNumber>;
82
- }, z.core.$strip>>;
83
81
  }, z.core.$strip>>;
84
82
  }, z.core.$strip>;
85
83
  }, z.core.$strip>, z.ZodObject<{
@@ -245,7 +243,6 @@ export declare const Phase2BundleSchema: z.ZodObject<{
245
243
  tissueLabel: z.ZodString;
246
244
  organSystem: z.ZodNullable<z.ZodString>;
247
245
  value: z.ZodNullable<z.ZodNumber>;
248
- protein: z.ZodNullable<z.ZodNumber>;
249
246
  }, z.core.$strip>>;
250
247
  }, z.core.$strip>;
251
248
  }, z.core.$strip>, z.ZodObject<{
@@ -66,13 +66,11 @@ export declare const Phase3BundleSchema: z.ZodObject<{
66
66
  tissueId: z.ZodString;
67
67
  tissueLabel: z.ZodString;
68
68
  organSystem: z.ZodNullable<z.ZodString>;
69
+ datasourceId: z.ZodString;
69
70
  rna: z.ZodNullable<z.ZodObject<{
70
71
  value: z.ZodNumber;
71
72
  unit: z.ZodString;
72
73
  }, z.core.$strip>>;
73
- protein: z.ZodNullable<z.ZodObject<{
74
- level: z.ZodNullable<z.ZodNumber>;
75
- }, z.core.$strip>>;
76
74
  }, z.core.$strip>>;
77
75
  }, z.core.$strip>;
78
76
  }, z.core.$strip>, z.ZodObject<{
@@ -238,7 +236,6 @@ export declare const Phase3BundleSchema: z.ZodObject<{
238
236
  tissueLabel: z.ZodString;
239
237
  organSystem: z.ZodNullable<z.ZodString>;
240
238
  value: z.ZodNullable<z.ZodNumber>;
241
- protein: z.ZodNullable<z.ZodNumber>;
242
239
  }, z.core.$strip>>;
243
240
  }, z.core.$strip>;
244
241
  }, z.core.$strip>, z.ZodObject<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inflexa-ai/harness",
3
- "version": "0.30.3",
3
+ "version": "0.31.1",
4
4
  "description": "Host-agnostic bioinformatics agent harness — hand-rolled agent loop, DBOS-durable workflows, sandboxed compute.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,23 +0,0 @@
1
- /**
2
- * Test-only `SandboxAgentDeps` builder. Hands every dependency a stub so
3
- * `createSandboxAgent` builds without touching Postgres, the network, or
4
- * a real sandbox.
5
- */
6
- import type { Pool } from "pg";
7
- import type { ChatProvider } from "../../../providers/types.js";
8
- import type { SandboxClient } from "../../../sandbox/client.js";
9
- import type { WorkspaceFilesystem } from "../../../workspace/filesystem.js";
10
- import type { SandboxAgentDeps } from "../shared.js";
11
- export declare function makeFakeSandboxClient(): SandboxClient;
12
- export declare function makeFakeWorkspaceFs(): WorkspaceFilesystem;
13
- export declare function makeFakeChatProvider(): ChatProvider;
14
- export declare function makeFakePool(): Pool;
15
- export interface MakeDepsOverrides {
16
- readonly model?: string;
17
- readonly analysisId?: string;
18
- readonly runId?: string;
19
- readonly stepId?: string;
20
- readonly workflowId?: string;
21
- readonly allowedWritePrefix?: string;
22
- }
23
- export declare function makeFakeSandboxAgentDeps(over?: MakeDepsOverrides): SandboxAgentDeps;