@inflexa-ai/harness 0.46.2 → 0.48.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 (279) hide show
  1. package/README.md +2 -2
  2. package/dist/agents/conversation-agent.d.ts +7 -6
  3. package/dist/agents/conversation-agent.js +26 -4
  4. package/dist/agents/report-session-agent.d.ts +14 -14
  5. package/dist/agents/report-session-agent.js +40 -26
  6. package/dist/agents/sandbox/shared.d.ts +18 -13
  7. package/dist/agents/sandbox/shared.js +42 -27
  8. package/dist/agents/sandbox/types.d.ts +1 -1
  9. package/dist/agents/sandbox-catalog.d.ts +2 -0
  10. package/dist/agents/sandbox-catalog.js +30 -1
  11. package/dist/app/synthesize-run.d.ts +10 -6
  12. package/dist/app/synthesize-run.js +4 -2
  13. package/dist/contracts/analogy-report.d.ts +46 -2
  14. package/dist/contracts/analogy-report.js +31 -25
  15. package/dist/contracts/chat-parts.d.ts +3 -14
  16. package/dist/contracts/report-reference.d.ts +2 -1
  17. package/dist/contracts/report-reference.js +2 -1
  18. package/dist/contracts/schemas/chat-parts.d.ts +0 -27
  19. package/dist/contracts/schemas/chat-parts.js +1 -3
  20. package/dist/execution/run-canceler.d.ts +2 -0
  21. package/dist/execution/run-canceler.js +9 -0
  22. package/dist/execution/run-synthesis.d.ts +36 -8
  23. package/dist/execution/run-synthesis.js +114 -32
  24. package/dist/index.d.ts +5 -6
  25. package/dist/index.js +5 -7
  26. package/dist/input-scan/enrich.d.ts +5 -10
  27. package/dist/input-scan/enrich.js +12 -8
  28. package/dist/lib/hooks.d.ts +1 -1
  29. package/dist/lib/markdown-pdf.d.ts +27 -0
  30. package/dist/lib/markdown-pdf.js +236 -0
  31. package/dist/lib/metrics.d.ts +31 -0
  32. package/dist/lib/metrics.js +64 -0
  33. package/dist/lib/pdf-text.d.ts +23 -0
  34. package/dist/lib/pdf-text.js +34 -0
  35. package/dist/lib/regulatory-corpus/fetch-fda-guidance.js +3 -5
  36. package/dist/lib/regulatory-corpus/fetch-ich.js +3 -5
  37. package/dist/loop/chat-stream.js +2 -1
  38. package/dist/loop/run-loop.d.ts +5 -0
  39. package/dist/loop/run-loop.js +30 -2
  40. package/dist/loop/tool-name-repair.d.ts +11 -0
  41. package/dist/loop/tool-name-repair.js +23 -0
  42. package/dist/loop/types.d.ts +19 -0
  43. package/dist/memory/card-builders.d.ts +1 -1
  44. package/dist/prompts/SOUL.d.ts +6 -6
  45. package/dist/prompts/SOUL.js +22 -121
  46. package/dist/prompts/analogical-reasoner.d.ts +1 -1
  47. package/dist/prompts/analogical-reasoner.js +35 -101
  48. package/dist/prompts/briefing.d.ts +20 -11
  49. package/dist/prompts/briefing.js +36 -19
  50. package/dist/prompts/conversation.d.ts +1 -1
  51. package/dist/prompts/conversation.js +103 -383
  52. package/dist/prompts/planner.d.ts +1 -5
  53. package/dist/prompts/planner.js +45 -128
  54. package/dist/prompts/report-session.d.ts +1 -1
  55. package/dist/prompts/report-session.js +29 -58
  56. package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
  57. package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +3 -3
  58. package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
  59. package/dist/prompts/sandbox/cheminformatics-agent.js +12 -47
  60. package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
  61. package/dist/prompts/sandbox/chromatin-agent.js +4 -5
  62. package/dist/prompts/sandbox/data-profiler.js +11 -34
  63. package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
  64. package/dist/prompts/sandbox/dna-methylation-agent.js +3 -1
  65. package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
  66. package/dist/prompts/sandbox/drug-repurposing-agent.js +23 -73
  67. package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
  68. package/dist/prompts/sandbox/enrichment-agent.js +3 -1
  69. package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
  70. package/dist/prompts/sandbox/genomic-variant-agent.js +3 -4
  71. package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
  72. package/dist/prompts/sandbox/immune-profiling-agent.js +5 -4
  73. package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
  74. package/dist/prompts/sandbox/metabolomics-agent.js +3 -1
  75. package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
  76. package/dist/prompts/sandbox/microbiome-agent.js +3 -1
  77. package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
  78. package/dist/prompts/sandbox/multi-omics-integration-agent.js +3 -1
  79. package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
  80. package/dist/prompts/sandbox/multimodal-sc-agent.js +3 -1
  81. package/dist/prompts/sandbox/network-agent.d.ts +1 -1
  82. package/dist/prompts/sandbox/network-agent.js +3 -1
  83. package/dist/prompts/sandbox/pkpd-clinical-response-agent.d.ts +1 -1
  84. package/dist/prompts/sandbox/pkpd-clinical-response-agent.js +4 -7
  85. package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
  86. package/dist/prompts/sandbox/proteomics-agent.js +3 -1
  87. package/dist/prompts/sandbox/scientific-executor.d.ts +1 -1
  88. package/dist/prompts/sandbox/scientific-executor.js +0 -12
  89. package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
  90. package/dist/prompts/sandbox/single-cell-agent.js +3 -1
  91. package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
  92. package/dist/prompts/sandbox/spatial-omics-agent.js +3 -1
  93. package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
  94. package/dist/prompts/sandbox/statistical-modeling-agent.js +4 -4
  95. package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
  96. package/dist/prompts/sandbox/translational-safety-agent.js +10 -29
  97. package/dist/prompts/sandbox-standards.d.ts +15 -18
  98. package/dist/prompts/sandbox-standards.js +96 -305
  99. package/dist/prompts/synthesis-agent.d.ts +1 -1
  100. package/dist/prompts/synthesis-agent.js +24 -90
  101. package/dist/provenance/collector.d.ts +2 -5
  102. package/dist/provenance/collector.js +2 -5
  103. package/dist/provenance/seam.d.ts +36 -1
  104. package/dist/provenance/workspace-reads.d.ts +36 -0
  105. package/dist/provenance/workspace-reads.js +33 -0
  106. package/dist/report-model/production-resolver.d.ts +22 -52
  107. package/dist/report-model/production-resolver.js +43 -112
  108. package/dist/report-render/chart-export.d.ts +9 -0
  109. package/dist/report-render/chart-export.js +1 -1
  110. package/dist/report-render/views/chart-view.d.ts +1 -0
  111. package/dist/report-render/views/chart-view.js +1 -0
  112. package/dist/report-render/views/lineage.d.ts +1 -0
  113. package/dist/report-render/views/page-view.d.ts +1 -0
  114. package/dist/report-render/views/page-view.js +1 -0
  115. package/dist/report-render/views/prose.d.ts +1 -0
  116. package/dist/report-render/views/prose.js +1 -0
  117. package/dist/report-render/views/references-view.d.ts +1 -0
  118. package/dist/report-render/views/values.d.ts +1 -0
  119. package/dist/report-render/views/values.js +1 -0
  120. package/dist/runtime/assemble.d.ts +32 -12
  121. package/dist/runtime/assemble.js +49 -38
  122. package/dist/sandbox/client.d.ts +5 -3
  123. package/dist/sandbox/create-sandbox.d.ts +7 -12
  124. package/dist/sandbox/create-sandbox.js +15 -37
  125. package/dist/sandbox/docker-client.d.ts +1 -3
  126. package/dist/sandbox/docker-client.js +3 -5
  127. package/dist/sandbox/exec-outcome.d.ts +11 -11
  128. package/dist/sandbox/exec-outcome.js +22 -22
  129. package/dist/sandbox/exec.d.ts +5 -0
  130. package/dist/sandbox/exec.js +9 -1
  131. package/dist/sandbox/identity.d.ts +4 -16
  132. package/dist/sandbox/identity.js +5 -21
  133. package/dist/sandbox/k8s-client.d.ts +0 -1
  134. package/dist/sandbox/k8s-client.js +8 -13
  135. package/dist/sandbox/lease-names.d.ts +6 -0
  136. package/dist/sandbox/lease-names.js +12 -0
  137. package/dist/sandbox/leases.d.ts +105 -0
  138. package/dist/sandbox/leases.js +519 -0
  139. package/dist/sandbox/liveness.d.ts +5 -2
  140. package/dist/sandbox/mount-plan.d.ts +10 -1
  141. package/dist/sandbox/mount-plan.js +7 -1
  142. package/dist/sandbox/reconciler.d.ts +58 -0
  143. package/dist/sandbox/reconciler.js +153 -0
  144. package/dist/sandbox/sandbox-step-translate.d.ts +1 -1
  145. package/dist/sandbox/types.d.ts +25 -18
  146. package/dist/sandbox/types.js +10 -4
  147. package/dist/schemas/plan-schemas.d.ts +0 -21
  148. package/dist/schemas/plan-schemas.js +2 -3
  149. package/dist/schemas/run-synthesis.d.ts +2 -2
  150. package/dist/schemas/run-synthesis.js +3 -3
  151. package/dist/schemas/validate-plan.d.ts +1 -14
  152. package/dist/schemas/validate-plan.js +5 -24
  153. package/dist/schemas/workflow-state.d.ts +3 -32
  154. package/dist/schemas/workflow-state.js +4 -13
  155. package/dist/state/index.d.ts +2 -3
  156. package/dist/state/index.js +1 -2
  157. package/dist/state/migrations/20261030120000_lease_the_sandboxes.d.ts +7 -0
  158. package/dist/state/migrations/20261030120000_lease_the_sandboxes.js +34 -0
  159. package/dist/state/migrations/20261030120100_drop_the_report_derivations.d.ts +5 -0
  160. package/dist/state/migrations/20261030120100_drop_the_report_derivations.js +9 -0
  161. package/dist/state/migrations/20261030120200_keep_the_report_derivations.d.ts +6 -0
  162. package/dist/state/migrations/20261030120200_keep_the_report_derivations.js +10 -0
  163. package/dist/state/migrations/index.js +6 -0
  164. package/dist/state/purge-analysis.d.ts +4 -2
  165. package/dist/state/purge-analysis.js +22 -11
  166. package/dist/state/sandbox-leases.d.ts +81 -0
  167. package/dist/state/sandbox-leases.js +247 -0
  168. package/dist/state/schema.d.ts +1 -13
  169. package/dist/state/schema.js +1 -6
  170. package/dist/state/step-executions.d.ts +13 -2
  171. package/dist/state/step-executions.js +23 -4
  172. package/dist/tasks/data-profile.d.ts +40 -2
  173. package/dist/tasks/data-profile.js +89 -87
  174. package/dist/tools/ad-hoc-router.d.ts +1 -6
  175. package/dist/tools/ad-hoc-router.js +5 -48
  176. package/dist/tools/bio/comptox.js +1 -1
  177. package/dist/tools/bio/drug-gene-interactions.js +1 -1
  178. package/dist/tools/bio/gene-disease-evidence.js +1 -1
  179. package/dist/tools/bio/keys.d.ts +1 -1
  180. package/dist/tools/bio/pubmed.js +31 -0
  181. package/dist/tools/bio/search-gene.js +17 -0
  182. package/dist/tools/bio/search-geo-datasets.js +13 -0
  183. package/dist/tools/bio/search-regulatory-approvals.js +1 -1
  184. package/dist/tools/define-tool.d.ts +25 -2
  185. package/dist/tools/define-tool.js +11 -1
  186. package/dist/tools/display/show-user.js +1 -1
  187. package/dist/tools/execute-analysis.d.ts +1 -3
  188. package/dist/tools/execute-analysis.js +5 -33
  189. package/dist/tools/report-authoring/authoring-tools.d.ts +2 -2
  190. package/dist/tools/report-authoring/authoring-tools.js +31 -6
  191. package/dist/tools/report-session/index.d.ts +0 -1
  192. package/dist/tools/report-session/index.js +0 -1
  193. package/dist/tools/report-session/script-tools.d.ts +33 -0
  194. package/dist/tools/report-session/script-tools.js +186 -0
  195. package/dist/tools/research/generate-analogy-report.d.ts +16 -9
  196. package/dist/tools/research/generate-analogy-report.js +111 -49
  197. package/dist/tools/research/generate-plan.d.ts +102 -7
  198. package/dist/tools/research/generate-plan.js +74 -43
  199. package/dist/tools/research/index.d.ts +4 -3
  200. package/dist/tools/research/index.js +4 -3
  201. package/dist/tools/research/inspect-run.d.ts +0 -1
  202. package/dist/tools/research/inspect-run.js +5 -10
  203. package/dist/tools/research/search-arxiv.js +15 -0
  204. package/dist/tools/research/search-github-repos.js +13 -0
  205. package/dist/tools/research/search-semantic-scholar.js +16 -0
  206. package/dist/tools/research/synthesis-lookups.d.ts +21 -0
  207. package/dist/tools/research/synthesis-lookups.js +29 -0
  208. package/dist/tools/research/web-fetch.d.ts +1 -1
  209. package/dist/tools/research/web-fetch.js +14 -12
  210. package/dist/tools/sandbox/lease-tools.d.ts +32 -0
  211. package/dist/tools/sandbox/lease-tools.js +60 -0
  212. package/dist/tools/script/archive.d.ts +34 -0
  213. package/dist/tools/script/archive.js +182 -0
  214. package/dist/tools/script/codemode-worker.d.ts +5 -0
  215. package/dist/tools/script/codemode-worker.js +5 -0
  216. package/dist/tools/script/file-tools.d.ts +106 -0
  217. package/dist/tools/script/file-tools.js +491 -0
  218. package/dist/tools/script/metrics.d.ts +30 -0
  219. package/dist/tools/script/metrics.js +53 -0
  220. package/dist/tools/script/quickjs.wasm +0 -0
  221. package/dist/tools/script/run-script.d.ts +76 -0
  222. package/dist/tools/script/run-script.js +413 -0
  223. package/dist/tools/script/scratch.d.ts +84 -0
  224. package/dist/tools/script/scratch.js +145 -0
  225. package/dist/tools/start-report-session.js +1 -1
  226. package/dist/tools/workspace/edit-file.js +2 -1
  227. package/dist/tools/workspace/execute-command.d.ts +9 -13
  228. package/dist/tools/workspace/execute-command.js +23 -19
  229. package/dist/tools/workspace/file-stat.js +1 -1
  230. package/dist/tools/workspace/grep.d.ts +5 -2
  231. package/dist/tools/workspace/grep.js +19 -10
  232. package/dist/tools/workspace/mutator.d.ts +51 -4
  233. package/dist/tools/workspace/mutator.js +54 -13
  234. package/dist/tools/workspace/read-file.d.ts +5 -2
  235. package/dist/tools/workspace/read-file.js +15 -11
  236. package/dist/tools/workspace/read-surface.d.ts +17 -0
  237. package/dist/tools/workspace/read-surface.js +17 -0
  238. package/dist/tools/workspace/result-bounds.d.ts +1 -0
  239. package/dist/tools/workspace/run-exec.d.ts +12 -13
  240. package/dist/tools/workspace/run-exec.js +13 -13
  241. package/dist/tools/workspace/scan-inputs.d.ts +2 -5
  242. package/dist/tools/workspace/scan-inputs.js +2 -3
  243. package/dist/tools/workspace/show-file.js +1 -1
  244. package/dist/tools/workspace/show-plan.js +4 -3
  245. package/dist/tools/workspace/write-file.js +2 -1
  246. package/dist/workflows/execute-analysis-scheduler.d.ts +0 -26
  247. package/dist/workflows/execute-analysis-scheduler.js +12 -32
  248. package/dist/workflows/execute-analysis.d.ts +17 -35
  249. package/dist/workflows/execute-analysis.js +13 -107
  250. package/dist/workflows/pause.js +29 -22
  251. package/dist/workflows/sandbox-step.d.ts +16 -31
  252. package/dist/workflows/sandbox-step.js +36 -71
  253. package/dist/workflows/test/sql-writes.d.ts +0 -1
  254. package/dist/workflows/test/sql-writes.js +0 -2
  255. package/dist/workspace/filesystem.d.ts +2 -2
  256. package/dist/workspace/filesystem.js +10 -5
  257. package/dist/workspace/paths.d.ts +19 -1
  258. package/dist/workspace/paths.js +22 -1
  259. package/dist/workspace/session-files.d.ts +35 -0
  260. package/dist/workspace/session-files.js +16 -0
  261. package/package.json +7 -3
  262. package/dist/prompts/literature-reviewer.d.ts +0 -1
  263. package/dist/prompts/literature-reviewer.js +0 -114
  264. package/dist/sandbox/reaper.d.ts +0 -81
  265. package/dist/sandbox/reaper.js +0 -161
  266. package/dist/sandbox/step-sandbox.d.ts +0 -47
  267. package/dist/sandbox/step-sandbox.js +0 -53
  268. package/dist/state/active-sandboxes.d.ts +0 -33
  269. package/dist/state/active-sandboxes.js +0 -65
  270. package/dist/tasks/extract-values-script.d.ts +0 -64
  271. package/dist/tasks/extract-values-script.js +0 -180
  272. package/dist/tasks/extract-values.d.ts +0 -77
  273. package/dist/tasks/extract-values.js +0 -146
  274. package/dist/tools/report-session/derive-table.d.ts +0 -164
  275. package/dist/tools/report-session/derive-table.js +0 -419
  276. package/dist/tools/research/literature-reviewer.d.ts +0 -42
  277. package/dist/tools/research/literature-reviewer.js +0 -107
  278. package/dist/workflows/metrics.d.ts +0 -23
  279. package/dist/workflows/metrics.js +0 -38
@@ -7,10 +7,12 @@
7
7
  * submission is the synthesis.
8
8
  * - `report_blocker` — the run produced nothing synthesizable. An
9
9
  * accepted synthesis wins over a blocker.
10
- * - `literature_reviewer` — research sub-agent exposed as a tool. The
11
- * runtime registers it one time and gives it here;
12
- * the sub-agent loop's brief is the only context
13
- * handed across the boundary (forSubAgent semantics).
10
+ * - the lookups — the gene, protein, annotation, interaction,
11
+ * PubMed, citation, drug-gene, and preclinical
12
+ * tools (`createSynthesisLookupTools`). Each lookup
13
+ * records the PMIDs of its result, and
14
+ * `submit_synthesis` refuses a PMID that no lookup
15
+ * of the run returned.
14
16
  *
15
17
  * Two deliverables on a happy path: `synthesis.json` written to the run dir
16
18
  * and a `data-run-synthesis` chat data part emitted via the `emit` callback
@@ -27,6 +29,7 @@ import { ok } from "neverthrow";
27
29
  import { z } from "zod";
28
30
  import { writeFileWithinRoot } from "../lib/fs-helpers.js";
29
31
  import { isAllDigits } from "../lib/text.js";
32
+ import { parseUnknownRecord } from "../lib/unknown-containers.js";
30
33
  import { tryFs } from "../lib/fs-result.js";
31
34
  import { safeParseJsonAs } from "../lib/parse-json.js";
32
35
  import { RunSynthesisSchema, SynthesisDraftSchema, } from "../schemas/run-synthesis.js";
@@ -35,8 +38,10 @@ import { composeSystemPrompt } from "../agents/system-prompt.js";
35
38
  import { isSafeId, runDir } from "../workspace/paths.js";
36
39
  import { runWithSalvage } from "../loop/run-loop.js";
37
40
  import { recordedInputOf } from "../loop/tool-adapter.js";
38
- import { defineTool } from "../tools/define-tool.js";
41
+ import { defineTool, readToolResultImages, withToolCallRecord, withToolResultImages } from "../tools/define-tool.js";
39
42
  import { createReadToolOutputTool } from "../tools/read-tool-output.js";
43
+ import { createReadScriptTools } from "../tools/script/file-tools.js";
44
+ import { createReadSurfaceTools } from "../tools/workspace/read-surface.js";
40
45
  import { blockerOf, createReportBlockerToolFor, REPORT_BLOCKER_TOOL_ID } from "../tools/sandbox/report-blocker.js";
41
46
  // ── Agent identity / budgets ────────────────────────────────────────
42
47
  /**
@@ -47,17 +52,75 @@ import { blockerOf, createReportBlockerToolFor, REPORT_BLOCKER_TOOL_ID } from ".
47
52
  */
48
53
  export const SYNTHESIS_AGENT_ID = "run-synthesizer";
49
54
  /**
50
- * Budget for the synthesizer loop: read summaries + 1–3 reviewer delegations
51
- * + draft + 1–2 submit/fix cycles + headroom.
55
+ * Budget for the synthesizer loop: the rounds of lookups, each one a batch of
56
+ * parallel calls, then the draft, 1–2 submit/fix cycles, and headroom.
52
57
  */
53
- const SYNTHESIZER_MAX_ITERATIONS = 25;
54
- /**
55
- * Cap on `literature_reviewer` calls per run. Not named in the prompt: a
56
- * model treats a stated call-count limit as a target to hit, not a maximum.
57
- */
58
- const LITERATURE_REVIEWER_BUDGET = 3;
58
+ const SYNTHESIZER_MAX_ITERATIONS = 40;
59
59
  const SubmitSynthesisOutputSchema = z.object({ accepted: z.boolean() });
60
60
  const SUBMIT_SYNTHESIS_TOOL_ID = "submit_synthesis";
61
+ // ── The PMIDs of the lookups ────────────────────────────────────────
62
+ /**
63
+ * The keys under which a lookup gives back its request: `resolve_citation`
64
+ * returns the citation that it was given, normalized. A PMID there came from
65
+ * the model, not from a source.
66
+ */
67
+ const ECHO_KEYS = new Set(["input", "normalized", "supplied", "query"]);
68
+ /** Each PMID that a lookup result holds under a `pmid` or `pmids` key, outside the echo of the request. */
69
+ export function pmidsIn(value, found = new Set()) {
70
+ if (Array.isArray(value)) {
71
+ for (const item of value)
72
+ pmidsIn(item, found);
73
+ return found;
74
+ }
75
+ const record = parseUnknownRecord(value);
76
+ if (record === undefined)
77
+ return found;
78
+ for (const [key, child] of Object.entries(record)) {
79
+ if (ECHO_KEYS.has(key))
80
+ continue;
81
+ if (key === "pmid" && (typeof child === "string" || typeof child === "number")) {
82
+ if (isAllDigits(String(child)))
83
+ found.add(String(child));
84
+ }
85
+ else if (key === "pmids" && Array.isArray(child)) {
86
+ for (const pmid of child)
87
+ if (typeof pmid === "string" && isAllDigits(pmid))
88
+ found.add(pmid);
89
+ }
90
+ else {
91
+ pmidsIn(child, found);
92
+ }
93
+ }
94
+ return found;
95
+ }
96
+ const PmidRecordSchema = z.object({ pmids: z.array(z.string()) });
97
+ /**
98
+ * A lookup whose ok result records the PMIDs that it holds. The loop folds the
99
+ * record into `seen` after each round, on the first run and on each replay, thus
100
+ * the check of `submit_synthesis` reads the same set in both.
101
+ */
102
+ export function recordingPmids(tool, seen) {
103
+ if (tool.foldCallRecord !== undefined)
104
+ throw new Error(`the lookup ${tool.id} keeps its own call record, which the PMID record would replace`);
105
+ return {
106
+ ...tool,
107
+ execute: async (input, ctx) => (await tool.execute(input, ctx)).map((value) => {
108
+ const record = parseUnknownRecord(value);
109
+ if (record === undefined)
110
+ return value;
111
+ // The parsed record is a copy without the symbol keys of the value, thus the pictures move over by hand.
112
+ const images = readToolResultImages(value);
113
+ const carried = images.length === 0 ? record : withToolResultImages(record, images);
114
+ return withToolCallRecord(carried, { pmids: [...pmidsIn(record)] });
115
+ }),
116
+ foldCallRecord: (record) => {
117
+ const parsed = PmidRecordSchema.safeParse(record);
118
+ if (parsed.success)
119
+ for (const pmid of parsed.data.pmids)
120
+ seen.add(pmid);
121
+ },
122
+ };
123
+ }
61
124
  // ── Validation ──────────────────────────────────────────────────────
62
125
  // ── Semantic checks ─────────────────────────────────────────────────
63
126
  //
@@ -138,6 +201,20 @@ function checkKeyReferencePmidFormat(keyReferences) {
138
201
  }
139
202
  return issues;
140
203
  }
204
+ function checkPmidsLookedUp(synthesis, lookedUp) {
205
+ const cited = [
206
+ ...synthesis.findings.flatMap((f, fi) => f.references.map((r, ri) => ({ pmid: r.pmid, path: `synthesis.findings[${fi}].references[${ri}].pmid` }))),
207
+ ...synthesis.keyReferences.map((r, i) => ({ pmid: r.pmid, path: `synthesis.keyReferences[${i}].pmid` })),
208
+ ];
209
+ return cited
210
+ .filter(({ pmid }) => pmid.length > 0 && isAllDigits(pmid) && !lookedUp.has(pmid))
211
+ .map(({ pmid, path }) => ({
212
+ path,
213
+ code: "semantic",
214
+ message: `pmid "${pmid}" is in no lookup result of this run`,
215
+ hint: "Cite only a PMID that a lookup of this run returned. Look it up, or remove the reference.",
216
+ }));
217
+ }
141
218
  function semanticCheck(synthesis, ctx) {
142
219
  return [
143
220
  ...checkFindingStepIds(synthesis.findings, ctx),
@@ -145,6 +222,7 @@ function semanticCheck(synthesis, ctx) {
145
222
  ...checkKeyReferencesCited(synthesis.keyReferences, synthesis.findings),
146
223
  ...checkFindingPmidFormat(synthesis.findings),
147
224
  ...checkKeyReferencePmidFormat(synthesis.keyReferences),
225
+ ...checkPmidsLookedUp(synthesis, ctx.lookedUpPmids),
148
226
  ];
149
227
  }
150
228
  // ── Inner tools ─────────────────────────────────────────────────────
@@ -153,7 +231,7 @@ function buildSubmitTool(ctx) {
153
231
  id: SUBMIT_SYNTHESIS_TOOL_ID,
154
232
  description: "Submit the synthesis. Checks the payload against the semantic checks " +
155
233
  "the arg schema cannot express (stepId refs, theme→finding refs, " +
156
- "keyReferences PMIDs cited, numeric PMIDs). On success returns " +
234
+ "keyReferences PMIDs cited, numeric PMIDs, each PMID returned by a lookup of this run). On success returns " +
157
235
  "{accepted: true} — STOP after this. On rejection returns " +
158
236
  "{accepted: false, issues} — fix the specific fields at each issue " +
159
237
  "path and call again, or switch to report_blocker if the synthesis " +
@@ -196,9 +274,26 @@ function buildBlockerTool() {
196
274
  * the checks without driving the whole agent loop.
197
275
  */
198
276
  export function __buildInnerToolsForTest(args) {
199
- const ctx = { knownStepIds: args.knownStepIds };
277
+ const ctx = { knownStepIds: args.knownStepIds, lookedUpPmids: args.lookedUpPmids ?? new Set() };
200
278
  return { submit: buildSubmitTool(ctx), blocker: buildBlockerTool() };
201
279
  }
280
+ /** The synthesizer agent of one run. Its submit tool checks the step ids of the summaries and the PMIDs of its lookups. */
281
+ export function synthesizerAgentOf(input) {
282
+ const knownStepIds = new Set(input.summaries.map((s) => s.stepId));
283
+ const lookedUpPmids = new Set();
284
+ const innerCtx = { knownStepIds, lookedUpPmids };
285
+ const readTools = input.toolOutputStore ? [createReadToolOutputTool(input.toolOutputStore)] : [];
286
+ // The lookups run in `run_script`, which gives the PMID record of each nested call to its lookup.
287
+ return {
288
+ id: SYNTHESIS_AGENT_ID,
289
+ systemPrompt: composeSystemPrompt(synthesisAgentPrompt),
290
+ model: input.model,
291
+ tools: [...createReadSurfaceTools(input.workspace), buildSubmitTool(innerCtx), buildBlockerTool(), ...readTools],
292
+ scriptTools: [...input.synthesisLookups.map((tool) => recordingPmids(tool, lookedUpPmids)), ...createReadScriptTools(input.workspace)],
293
+ scriptRuntime: input.scriptRuntime,
294
+ maxIterations: SYNTHESIZER_MAX_ITERATIONS,
295
+ };
296
+ }
202
297
  /**
203
298
  * Drive the synthesizer agent loop to an outcome — the last accepted
204
299
  * synthesis (`submit_synthesis`), else a blocker (`report_blocker`).
@@ -208,21 +303,8 @@ export async function generateRunSynthesis(input) {
208
303
  if (input.summaries.length === 0) {
209
304
  throw new Error("Run synthesis: no step summaries to synthesize");
210
305
  }
211
- const knownStepIds = new Set(input.summaries.map((s) => s.stepId));
212
- const innerCtx = { knownStepIds };
213
- const reviewer = input.literatureReviewer;
214
- const submitTool = buildSubmitTool(innerCtx);
215
- const blockerTool = buildBlockerTool();
216
- const readTools = input.toolOutputStore ? [createReadToolOutputTool(input.toolOutputStore)] : [];
217
- const tools = [submitTool, blockerTool, reviewer, ...readTools];
218
- const agent = {
219
- id: SYNTHESIS_AGENT_ID,
220
- systemPrompt: composeSystemPrompt(synthesisAgentPrompt),
221
- model: input.model,
222
- tools,
223
- maxIterations: SYNTHESIZER_MAX_ITERATIONS,
224
- };
225
- const prompt = buildSynthesizerPrompt(input.planNarrative, input.summaries);
306
+ const agent = synthesizerAgentOf(input);
307
+ const prompt = synthesizerSeedOf(input.planNarrative, input.summaries);
226
308
  const signal = input.signal ?? new AbortController().signal;
227
309
  const emit = input.emit ?? (() => { });
228
310
  const run = await runWithSalvage(agent, [{ role: "user", content: prompt }], input.session, {
@@ -231,7 +313,6 @@ export async function generateRunSynthesis(input) {
231
313
  emit,
232
314
  usageRecorder: input.usageRecorder,
233
315
  toolOutputStore: input.toolOutputStore,
234
- policy: { budget: { [reviewer.id]: LITERATURE_REVIEWER_BUDGET } },
235
316
  pause: input.pause,
236
317
  }, {
237
318
  tools: [SUBMIT_SYNTHESIS_TOOL_ID, REPORT_BLOCKER_TOOL_ID],
@@ -385,7 +466,8 @@ export function formatSynthesisEmbeddingText(synthesis) {
385
466
  ].join("\n");
386
467
  }
387
468
  // ── Prompt builder ──────────────────────────────────────────────────
388
- function buildSynthesizerPrompt(planNarrative, summaries) {
469
+ /** The first message of the synthesizer: the narrative of the plan and the summary of each step. */
470
+ export function synthesizerSeedOf(planNarrative, summaries) {
389
471
  const summaryBlock = summaries.map((s) => `### ${s.stepId} (${s.agentId})\n\n${s.markdown}`).join("\n\n---\n\n");
390
472
  return ["## Analytical Narrative", planNarrative, "", "## Step Summaries", summaryBlock].join("\n");
391
473
  }
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * the agent with its own seam realizations.
6
6
  */
7
7
  export { assembleCoreRuntime } from "./runtime/assemble.js";
8
- export type { CoreRuntime, CoreRuntimeDeps, CoreWorkflowDeps, RegisteredWorkflows, ConversationAssemblyDeps, SandboxStepCallable, MakeReportReferenceResolver, ReportResolverScope, } from "./runtime/assemble.js";
8
+ export type { CoreRuntime, CoreRuntimeDeps, CoreSandboxDeps, CoreWorkflowDeps, RegisteredWorkflows, ConversationAssemblyDeps, SandboxStepCallable, MakeReportReferenceResolver, ReportResolverScope, } from "./runtime/assemble.js";
9
9
  export type { ReferenceResolver, ReportSnapshot, ResolvedValue } from "./report-model/reference-resolver.js";
10
10
  export type { ResolvePageAsset } from "./tools/report-session/index.js";
11
11
  export { PAGE_ASSETS } from "./report-render/assets.js";
@@ -29,6 +29,7 @@ export type { BioToolKeys } from "./tools/bio/keys.js";
29
29
  export { createLocalRunAuthorizer } from "./auth/local-run-authorizer.js";
30
30
  export type { RunAuthorizer, AuthorizeRunInput, RevokeByMandateRef } from "./execution/run-authorizer.js";
31
31
  export type { GateFailure, NoticeFailure } from "./lib/hooks.js";
32
+ export type { SessionFileOwner, SessionFileWrite, SyncSessionFile } from "./workspace/session-files.js";
32
33
  export type { RequestHeaders, ResolveRequestHeaders } from "./providers/request-headers.js";
33
34
  export { createNoopRunCharge } from "./billing/noop-run-charge.js";
34
35
  export type { RunCharge, RunChargeOutcome } from "./billing/run-charge.js";
@@ -43,7 +44,6 @@ export type { MakeSessionPagePublisher, SessionPageGrant, SessionPageMintFailure
43
44
  export type { ResolvePageUrl, SessionPageAccess } from "./tools/report-session/index.js";
44
45
  export { bindReadExport, bindSessionEmit } from "./provenance/seam.js";
45
46
  export type { ProvenanceExport, ProvenanceSeam, RunProvenanceEvent, SessionProvenanceEvent } from "./provenance/seam.js";
46
- export type { DerivationSource } from "./state/report-session-state.js";
47
47
  export { createDbosRunLauncher } from "./execution/dbos-run-launcher.js";
48
48
  export type { RunLauncher, LaunchOptions } from "./execution/run-launcher.js";
49
49
  export { createDbosWorkflowPurger } from "./execution/dbos-workflow-purger.js";
@@ -158,6 +158,7 @@ export type { ResolveWorkspaceRoot } from "./workspace/paths.js";
158
158
  export { createSandboxClient } from "./sandbox/create-sandbox.js";
159
159
  export type { CreateSandboxClientConfig, SandboxBackendConfig } from "./sandbox/create-sandbox.js";
160
160
  export type { SandboxClient } from "./sandbox/client.js";
161
+ export type { AcquireOutcome, BoundLease, LeaseBinding, NoLease, SandboxLeases } from "./sandbox/leases.js";
161
162
  export type { ResolveSandboxLabels, SandboxLabels, SandboxSpec } from "./sandbox/types.js";
162
163
  export type { SpawnSession } from "./auth/types.js";
163
164
  export { describeSandboxError, keepSuspendingRefusal, SandboxFailure } from "./sandbox/sandbox-error.js";
@@ -188,13 +189,13 @@ export type { PlanStep } from "./workflows/execute-analysis-scheduler.js";
188
189
  export { planGraphOf } from "./workflows/plan/graph.js";
189
190
  export { planPartOf } from "./memory/card-builders.js";
190
191
  export { validatePlan } from "./schemas/validate-plan.js";
191
- export type { ValidatePlanOptions } from "./schemas/validate-plan.js";
192
192
  export type { ValidationResult } from "./schemas/validate-plan.js";
193
193
  export { upsertPlan, loadPlan, loadPlanRecord } from "./state/plans.js";
194
194
  export type { UpsertPlanInput, PlanRecord } from "./state/plans.js";
195
195
  export { insertRun, reserveRunById, queryActiveRun, queryActiveRunsByAnalysis, updateRunStatus, markRunCanceledIfActive, queryRun, queryRunsByAnalysis, countRunsByAnalysis, queryRunIdsByPlan, RunDedupCollisionError, RunIdentityCollisionError, } from "./state/runs.js";
196
196
  export type { InsertRunInput, RunReservation } from "./state/runs.js";
197
- export { queryStepsByRun } from "./state/step-executions.js";
197
+ export { queryStepsByRun, countStepsByRuns } from "./state/step-executions.js";
198
+ export type { RunStepCounts } from "./state/step-executions.js";
198
199
  export type { CortexRunRow, StepExecutionRow, RunStatus, TerminalRunStatus } from "./state/schema.js";
199
200
  export type { DbError } from "./lib/db-result.js";
200
201
  export { REFERENCE_DATA_CATALOG, REFERENCE_DATA_CATALOG_VERSION, ReferenceArtifactPathSchema, ReferenceArtifactSchema, ReferenceDataCatalogSchema, ReferenceDatasetSchema, ReferenceSha256Schema, UnknownReferenceDatasetError, referenceArtifactKey, resolveReferenceInstallPlan, } from "./reference-data/catalog.js";
@@ -206,7 +207,5 @@ export { reportSessionDir } from "./workspace/paths.js";
206
207
  export type { LogFields, LogLevel, Logger } from "./lib/logger.js";
207
208
  export { defaultErrorFields } from "./lib/logger.js";
208
209
  export { createConsoleLogger, createNoopLogger } from "./lib/console-logger.js";
209
- export { registerSandboxReaper } from "./sandbox/reaper.js";
210
- export type { RegisterReaperDeps } from "./sandbox/reaper.js";
211
210
  export { registerNotificationSweep } from "./sandbox/notification-sweep.js";
212
211
  export type { RegisterNotificationSweepDeps } from "./sandbox/notification-sweep.js";
package/dist/index.js CHANGED
@@ -55,8 +55,7 @@ export { UnavailableSessionPagePublisher } from "./tools/report-session/session-
55
55
  // back for the report page. Thus the seam and its vocabulary belong on the front door: an embedder
56
56
  // cannot type its realization without them. The two binders ride beside the types, because an
57
57
  // embedder that calls `prepareChatTurn` or one tool factory directly binds the same total forms
58
- // that the assembly binds. `DerivationSource` rides beside the event union, because the derivation
59
- // event carries the chain of the derived table.
58
+ // that the assembly binds.
60
59
  export { bindReadExport, bindSessionEmit } from "./provenance/seam.js";
61
60
  // Run launching.
62
61
  export { createDbosRunLauncher } from "./execution/dbos-run-launcher.js";
@@ -296,7 +295,8 @@ export { validatePlan } from "./schemas/validate-plan.js";
296
295
  // the run-state calls mirror the chat trigger's reserve → dedup → status flow.
297
296
  export { upsertPlan, loadPlan, loadPlanRecord } from "./state/plans.js";
298
297
  export { insertRun, reserveRunById, queryActiveRun, queryActiveRunsByAnalysis, updateRunStatus, markRunCanceledIfActive, queryRun, queryRunsByAnalysis, countRunsByAnalysis, queryRunIdsByPlan, RunDedupCollisionError, RunIdentityCollisionError, } from "./state/runs.js";
299
- export { queryStepsByRun } from "./state/step-executions.js";
298
+ // The step ledger reads of a run, and the step counts of each run of a list in one statement.
299
+ export { queryStepsByRun, countStepsByRuns } from "./state/step-executions.js";
300
300
  // Host-neutral reference-data catalog and shared installation receipts. Each
301
301
  // artifact names the third-party upstream it is fetched from; embedders own all
302
302
  // transfer/storage behavior but never choose a different source.
@@ -314,8 +314,6 @@ export { reportSessionDir } from "./workspace/paths.js";
314
314
  // `defaultErrorFields` is the shipped mapping for one with no opinion.
315
315
  export { defaultErrorFields } from "./lib/logger.js";
316
316
  export { createConsoleLogger, createNoopLogger } from "./lib/console-logger.js";
317
- // Sandbox-hygiene scheduled workflows. Reaper reclaims a dead host's orphaned
318
- // containers; the sweep prunes stale notifications. Each is a
319
- // `@DBOS.scheduled` no-op on an idle system.
320
- export { registerSandboxReaper } from "./sandbox/reaper.js";
317
+ // The scheduled sweep that prunes stale notifications. It is a `@DBOS.scheduled`
318
+ // no-op on an idle system. The sandbox reconciler registers with the runtime.
321
319
  export { registerNotificationSweep } from "./sandbox/notification-sweep.js";
@@ -17,8 +17,7 @@
17
17
  */
18
18
  import type { AgentSession } from "../auth/types.js";
19
19
  import type { EmitFn } from "../tools/define-tool.js";
20
- import type { SandboxClient } from "../sandbox/client.js";
21
- import type { SandboxRef } from "../sandbox/types.js";
20
+ import type { BoundLease } from "../sandbox/leases.js";
22
21
  import type { WorkspaceFilesystem } from "../workspace/filesystem.js";
23
22
  import type { FileShape, HeaderReadout } from "./types.js";
24
23
  /** Members decoded per shape. One is enough to characterise a set whose names already match. */
@@ -45,24 +44,20 @@ export type ReadHeadersArgs = {
45
44
  readonly targets: readonly ReadoutTargetSpec[];
46
45
  readonly session: AgentSession;
47
46
  readonly fs: WorkspaceFilesystem;
48
- /** Omit the sandbox to skip container readouts; every other format still reads. */
49
- readonly sandboxClient?: SandboxClient;
50
- readonly sandbox?: SandboxRef;
47
+ /** Omit the lease to skip container readouts; every other format still reads. */
48
+ readonly lease?: BoundLease;
51
49
  /** Absolute in-sandbox path of the analysis root (`/{analysisId}`). */
52
50
  readonly mountRoot: string;
53
- readonly deadlineMs: number;
54
51
  readonly emit: EmitFn;
55
52
  };
56
53
  export type EnrichShapesArgs = {
57
54
  readonly shapes: readonly FileShape[];
58
55
  readonly session: AgentSession;
59
56
  readonly fs: WorkspaceFilesystem;
60
- /** Omit the sandbox to skip container readouts; every other format still reads. */
61
- readonly sandboxClient?: SandboxClient;
62
- readonly sandbox?: SandboxRef;
57
+ /** Omit the lease to skip container readouts; every other format still reads. */
58
+ readonly lease?: BoundLease;
63
59
  /** Absolute in-sandbox path of the analysis root (`/{analysisId}`). */
64
60
  readonly mountRoot: string;
65
- readonly deadlineMs: number;
66
61
  readonly emit: EmitFn;
67
62
  };
68
63
  /**
@@ -65,9 +65,9 @@ export async function readHeaders(args) {
65
65
  readouts.set(target.path, await readFromPrefix(args, target));
66
66
  }
67
67
  }));
68
- const { sandboxClient, sandbox } = args;
69
- if (containers.length > 0 && sandboxClient && sandbox) {
70
- for (const [path, readout] of await decodeContainers(args, { sandboxClient, sandbox }, containers))
68
+ const { lease } = args;
69
+ if (containers.length > 0 && lease) {
70
+ for (const [path, readout] of await decodeContainers(args, lease, containers))
71
71
  readouts.set(path, readout);
72
72
  }
73
73
  return readouts;
@@ -118,7 +118,7 @@ const ReadoutLineSchema = z.object({
118
118
  fields: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
119
119
  unavailable: z.string().optional(),
120
120
  });
121
- async function decodeContainers(args, machine, containers) {
121
+ async function decodeContainers(args, lease, containers) {
122
122
  const readouts = new Map();
123
123
  const source = await readFile(CONTAINER_DECODER, "utf8").catch(() => undefined);
124
124
  if (source === undefined) {
@@ -129,14 +129,18 @@ async function decodeContainers(args, machine, containers) {
129
129
  const byAbsolute = new Map();
130
130
  for (const target of containers)
131
131
  byAbsolute.set(`${args.mountRoot}/${target.path}`, target);
132
- const result = await runSandboxExec({
133
- sandboxClient: machine.sandboxClient,
134
- sandbox: machine.sandbox,
132
+ const ran = await runSandboxExec({
133
+ lease,
135
134
  command: ["python3", "-c", source, ...byAbsolute.keys()],
136
135
  timeoutSeconds: ENRICH_TIMEOUT_SECONDS,
137
- deadlineMs: args.deadlineMs,
138
136
  emit: args.emit,
139
137
  });
138
+ if (ran.isErr()) {
139
+ for (const { path } of containers)
140
+ readouts.set(path, { path, fields: {}, unavailable: ran.error.error });
141
+ return readouts;
142
+ }
143
+ const result = ran.value;
140
144
  for (const line of result.stdout.split("\n")) {
141
145
  const trimmed = line.trim();
142
146
  if (!trimmed.startsWith("{"))
@@ -50,7 +50,7 @@ export type NoticeFailure = {
50
50
  readonly reason: string;
51
51
  };
52
52
  export type GateName = "RunAuthorizer.authorize" | "RunAuthorizer.renew" | "RunCharge.open" | "ArtifactRegistry.register" | "resolveRequestHeaders" | "resolveSandboxLabels";
53
- export type NoticeName = "RunAuthorizer.revoke" | "RunAuthorizer.revokeByMandateId" | "RunAuthorizer.suspend" | "RunCharge.close" | "ArtifactRegistry.sync" | "UsageRecorder.record" | "notifyDataProfileSettled";
53
+ export type NoticeName = "RunAuthorizer.revoke" | "RunAuthorizer.revokeByMandateId" | "RunAuthorizer.suspend" | "RunCharge.close" | "ArtifactRegistry.sync" | "UsageRecorder.record" | "notifyDataProfileSettled" | "syncSessionFile";
54
54
  export type GateRefusal = {
55
55
  readonly kind: "failed";
56
56
  readonly gate: GateName;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * A PDF from markdown, on `marked` and `pdfmake`, with no browser.
3
+ *
4
+ * `marked` gives the token tree, and each token maps to a `pdfmake` node: a heading, a paragraph, a list, a GFM table,
5
+ * a code block, a quote, a rule, and an image. An SVG image, for example a chart of `render_chart`, lands as vector
6
+ * graphics. The text font is the Roboto that `pdfmake` ships, which holds the Greek letters and the common math signs.
7
+ *
8
+ * `pdfmake` resolves a font by a file path, also inside an SVG, thus the fonts land one time in a private temporary
9
+ * directory. Each URL is refused, and a local path is refused unless it is one of those font files, thus a document
10
+ * reads nothing that the caller did not give it.
11
+ */
12
+ /** An image that a document embeds. The caller reads the bytes, thus the document reaches no file and no URL. */
13
+ export type PdfImage = {
14
+ readonly kind: "svg";
15
+ readonly svg: string;
16
+ } | {
17
+ readonly kind: "png" | "jpeg";
18
+ readonly bytes: Uint8Array;
19
+ };
20
+ /** The source of each image of a markdown text, in the order of the text. */
21
+ export declare function markdownImageSources(markdown: string): string[];
22
+ /** The PDF of a markdown text. `images` holds each image that the text names, by its source. */
23
+ export declare function markdownToPdf(args: {
24
+ readonly markdown: string;
25
+ readonly title?: string;
26
+ readonly images: ReadonlyMap<string, PdfImage>;
27
+ }): Promise<Uint8Array>;