@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
package/README.md CHANGED
@@ -50,7 +50,7 @@ No embedder drives the agent loop. A host opens a chat turn with the `open` of `
50
50
 
51
51
  A failure that a person or time can repair pauses a turn, a run, or a data profile, and the work does not fail. Examples are a billing refusal of the host, a provider outage, and a missing credential. A host lists the active work of an analysis with `CoreRuntime.work`. It stops a work in each state, and it resumes or holds paused work.
52
52
 
53
- `assembleCoreRuntime` is the single composition point: it registers the durable workflows and builds the conversation agent over them. The harness declares **four capability seams** an embedder wires — `RunAuthorizer`, `ArtifactRegistry`, `RunCharge`, `UsageRecorder` — plus the **shared `RunLauncher`** (one host-neutral realization, `createDbosRunLauncher`). Local, dependency-free realizations of all four ship from the barrel; an embedder constructs them (or its own) and passes them in. The harness only ever sees the interface. Two optional hooks go into the component that uses their values: `resolveRequestHeaders` into each provider, and `resolveSandboxLabels` into the sandbox client config.
53
+ `assembleCoreRuntime` is the single composition point: it registers the durable workflows and builds the conversation agent over them. The harness declares **four capability seams** an embedder wires — `RunAuthorizer`, `ArtifactRegistry`, `RunCharge`, `UsageRecorder` — plus the **shared `RunLauncher`** (one host-neutral realization, `createDbosRunLauncher`). Local, dependency-free realizations of all four ship from the barrel; an embedder constructs them (or its own) and passes them in. The harness only ever sees the interface. Two optional hooks go into the component that uses their values: `resolveRequestHeaders` into each provider, and `resolveSandboxLabels` into the sandbox client config. A third, `syncSessionFile`, goes into `assembleCoreRuntime`: it receives each file that a chat thread or a report session writes outside a run.
54
54
 
55
55
  ## Logging
56
56
 
@@ -101,7 +101,7 @@ function pinoAsHarnessLogger(p: pino.Logger, names: readonly string[] = []): Log
101
101
 
102
102
  A chat turn is a **durable workflow**. The harness stores the opening, each round, and the outcome of the turn, and a recovery continues a turn from its checkpoints. The harness ships no HTTP layer, thus the host owns the route. Compute-heavy work runs as durable workflows too — `executeAnalysis` starts a child workflow per plan step once its dependencies have completed, and each step drives a sandbox agent inside a container. The same `runLoop` primitive runs in each workflow, and each model call and each tool call is a durable step.
103
103
 
104
- The sandbox protocol is submit-then-poll, which lets a long run survive a host restart. Each exec runs inside one durable step: the host submits the command with `POST /exec`, and then it polls `GET /exec/{execId}?since={cursor}` until the result. A recovered step submits the same exec id again, and the sandbox gives the existing record, thus the command does not run again. The sandbox initiates nothing and has no egress, and confinement keeps each other peer away from its exec port. Refer to the [`harness-sandbox-exec`](./openspec/specs/harness-sandbox-exec/) spec.
104
+ The sandbox protocol is submit-then-poll, which lets a long run survive a host restart. Each exec runs inside one durable step: the host submits the command with `POST /exec`, and then it polls `GET /exec/{execId}?since={cursor}` until the result. A recovered step submits the same exec id again, and the sandbox gives the existing record, thus the command does not run again. The sandbox initiates nothing and has no egress, and confinement keeps each other peer away from its exec port. Refer to the [`harness-sandbox-exec`](./openspec/specs/harness-sandbox-exec/) spec. A consumer holds a sandbox through a lease: the step agent acquires a size with `acquire_sandbox`, and a detached lease workflow owns the machine, its admission against one budget, its idle removal, and its teardown. Refer to the [`sandbox-leases`](./openspec/specs/sandbox-leases/) spec.
105
105
 
106
106
  ## Further reading
107
107
 
@@ -26,7 +26,6 @@
26
26
  import type { Pool } from "pg";
27
27
  import type { ExecuteAnalysisInput, ExecuteAnalysisResult } from "../workflows/execute-analysis.js";
28
28
  import type { EnsureSessionStateResult } from "../app/report-session-runtime.js";
29
- import type { ResourcePolicy } from "../config/resource-limits.js";
30
29
  import type { ChromeConfig } from "../lib/chrome.js";
31
30
  import type { AcquireEyes } from "../lib/eyes.js";
32
31
  import type { ToolOutputStore } from "../loop/tool-output.js";
@@ -43,10 +42,12 @@ import type { EnvironmentStorePaths } from "../config/environment-stores.js";
43
42
  import type { RunAuthorizer } from "../execution/run-authorizer.js";
44
43
  import type { RunLauncher } from "../execution/run-launcher.js";
45
44
  import type { ProvenanceSeam } from "../provenance/seam.js";
45
+ import type { SyncSessionFile } from "../workspace/session-files.js";
46
46
  import type { Logger } from "../lib/logger.js";
47
47
  import type { UsageRecorder } from "../billing/usage-recorder.js";
48
48
  import type { CitationResolver } from "../citations/types.js";
49
49
  import { type WebSearchEngine } from "../tools/research/web-search.js";
50
+ import type { ScriptRuntime } from "../tools/script/run-script.js";
50
51
  /** Canonical agent id — the single source of truth. */
51
52
  export declare const CONVERSATION_AGENT_ID: "conversation-agent";
52
53
  /**
@@ -143,12 +144,10 @@ export type ConversationAgentDeps = EnvironmentStorePaths & {
143
144
  */
144
145
  readonly provenance?: ProvenanceSeam;
145
146
  /**
146
- * Host resource policy — per-step ceilings + machine budget. `generate_plan`
147
- * states the ceilings to the planner and validates against them;
148
- * `execute_analysis` snapshots the budget into the workflow input. Absent,
149
- * planning guidance and scheduling keep their legacy behavior.
147
+ * The host copy of each file that the agent writes: a derived file or a script of `run_script`, owned by the
148
+ * thread, and a `write_file` or `edit_file` write, owned by the analysis. See `SyncSessionFile`.
150
149
  */
151
- readonly resourcePolicy?: ResourcePolicy;
150
+ readonly syncSessionFile?: SyncSessionFile;
152
151
  /**
153
152
  * Embedder-contributed conversation-tool seam — additional tools the host
154
153
  * supplies, appended after the built-in roster. The harness stays agnostic to
@@ -156,6 +155,8 @@ export type ConversationAgentDeps = EnvironmentStorePaths & {
156
155
  * built-in tool.
157
156
  */
158
157
  readonly hostTools?: readonly Tool[];
158
+ /** Where the VM of `run_script` comes from. A bundled host gives the embedded paths; see `ScriptRuntime`. */
159
+ readonly scriptRuntime?: ScriptRuntime;
159
160
  /**
160
161
  * The farm-extension seam of the embedder. When it is bound,
161
162
  * `execute_analysis` links the packages of the plan into the farm of the
@@ -44,6 +44,9 @@ import { createStartReportSessionTool } from "../tools/start-report-session.js";
44
44
  import { createResolveCitationTool } from "../tools/research/resolve-citation.js";
45
45
  import { createWebFetchTool } from "../tools/research/web-fetch.js";
46
46
  import { createWebSearchTool } from "../tools/research/web-search.js";
47
+ import { createScriptFileTools } from "../tools/script/file-tools.js";
48
+ import { createScriptScratch } from "../tools/script/scratch.js";
49
+ import { conversationSessionDir } from "../workspace/paths.js";
47
50
  /** Canonical agent id — the single source of truth. */
48
51
  export const CONVERSATION_AGENT_ID = "conversation-agent";
49
52
  /** Runaway guard — heavy tool-driving turns need generous headroom. */
@@ -52,7 +55,7 @@ const CONVERSATION_MAX_ITERATIONS = 200;
52
55
  const CONVERSATION_COMPACTION = { turnStartBudget: 150_000, budget: 200_000 };
53
56
  /** Build the conversation `AgentDefinition` with every tool fully dep-bound. */
54
57
  export function createConversationAgent(deps) {
55
- const { provider, pool, embedding, workspaceFs, model, utilityProvider, utilityModel, executeAnalysisWorkflow, runAuthorizer, runLauncher, bioKeys, chrome, resourcePolicy, hostTools, refStorePath, farmLockFile, imagePackagesFile, readPoolInventory, usageRecorder, toolOutputStore, citationResolver, webSearch, } = deps;
58
+ const { provider, pool, embedding, workspaceFs, model, utilityProvider, utilityModel, executeAnalysisWorkflow, runAuthorizer, runLauncher, bioKeys, chrome, hostTools, refStorePath, farmLockFile, imagePackagesFile, readPoolInventory, usageRecorder, toolOutputStore, citationResolver, webSearch, } = deps;
56
59
  const workingMemory = createWorkingMemory(pool);
57
60
  const ncbi = createNcbiTools(bioKeys);
58
61
  const chemDb = createChemDbTools(bioKeys, { logger: deps.logger });
@@ -63,9 +66,22 @@ export function createConversationAgent(deps) {
63
66
  const mutator = createSessionWorkspaceMutator({
64
67
  resolveWorkspaceRoot: deps.resolveWorkspaceRoot,
65
68
  provenance: deps.provenance,
69
+ syncSessionFile: deps.syncSessionFile,
66
70
  logger: deps.logger,
67
71
  });
68
- const tools = [
72
+ // The scratch directory of each conversation: the scripts of `run_script`, and the files that they derive.
73
+ const scriptScratch = createScriptScratch({
74
+ resolveWorkspaceRoot: deps.resolveWorkspaceRoot,
75
+ sessionDir: conversationSessionDir,
76
+ provenance: deps.provenance,
77
+ syncSessionFile: deps.syncSessionFile,
78
+ logger: deps.logger,
79
+ });
80
+ // The lookups and the light work on tables run in a script (`run_script`). Each tool asks no approval, and a
81
+ // script writes only into the scratch directory of its conversation.
82
+ const scriptTools = [
83
+ // Tables of the workspace in, derived files and chart files out.
84
+ ...createScriptFileTools({ workspaceFs, resolveWorkspaceRoot: deps.resolveWorkspaceRoot, scratch: scriptScratch }),
69
85
  // Identifier resolution — the ENSG most other bio tools key off, and the
70
86
  // UniProt accession that the structure tools take.
71
87
  searchGeneTool,
@@ -112,6 +128,8 @@ export function createConversationAgent(deps) {
112
128
  targetSafetyTool,
113
129
  // EPA CompTox (toxcast / hazard / chemical / exposure behind one dataset).
114
130
  chemDb.comptox,
131
+ ];
132
+ const tools = [
115
133
  // What reference data this install actually holds. The conversation agent is
116
134
  // where provisioning is decided — an embedder may give it a way to install more,
117
135
  // and a host that does asks the user to approve the download. Deciding that
@@ -140,10 +158,11 @@ export function createConversationAgent(deps) {
140
158
  createGeneratePlanTool({
141
159
  conversation: { provider, model },
142
160
  pool,
143
- resourcePolicy,
144
161
  usageRecorder,
145
162
  toolOutputStore,
146
163
  bioKeys,
164
+ workspace: { workspaceFs, pool, embedding, resolveWorkspaceRoot: deps.resolveWorkspaceRoot },
165
+ scriptRuntime: deps.scriptRuntime,
147
166
  knowledge: deps.knowledge,
148
167
  refStorePath,
149
168
  farmLockFile,
@@ -156,7 +175,6 @@ export function createConversationAgent(deps) {
156
175
  executeAnalysisWorkflow,
157
176
  runAuthorizer,
158
177
  runLauncher,
159
- resourcePolicy,
160
178
  utilityProvider,
161
179
  utilityModel,
162
180
  farmLockFile,
@@ -193,6 +211,7 @@ export function createConversationAgent(deps) {
193
211
  usageRecorder,
194
212
  toolOutputStore,
195
213
  logger: deps.logger,
214
+ scriptRuntime: deps.scriptRuntime,
196
215
  }, deps.registerSubAgent),
197
216
  // Workspace semantic search + raw read/grep over the read seam.
198
217
  createWorkspaceSearchTool(pool, embedding),
@@ -219,6 +238,9 @@ export function createConversationAgent(deps) {
219
238
  systemPrompt: composeSystemPrompt(conversationPrompt, { identity: true, conversational: true }),
220
239
  model,
221
240
  tools,
241
+ scriptTools,
242
+ scriptRuntime: deps.scriptRuntime,
243
+ scriptScratch,
222
244
  maxIterations: CONVERSATION_MAX_ITERATIONS,
223
245
  compaction: CONVERSATION_COMPACTION,
224
246
  };
@@ -11,11 +11,12 @@
11
11
  * the workspace search, the run inspection, and the data-profile inspection. It
12
12
  * holds no planner, no run launcher, no working-memory write, and no sandbox
13
13
  * mutate surface. Thus no tool starts a run, and no tool changes an analysis. The
14
- * roster omits those tools, and no runtime guard blocks them.
14
+ * roster omits those tools, and no runtime guard blocks them. No tool runs a
15
+ * container.
15
16
  *
16
- * The derivation tool is the one tool that runs a container, and it changes no
17
- * analysis either. It mints no run id, it registers no artifact, and it writes under
18
- * the directory of the session alone.
17
+ * A derivation of a table runs in a script (`run_script`), and it changes no
18
+ * analysis: it mints no run id, it registers no artifact, and it writes under the
19
+ * directory of the session alone.
19
20
  *
20
21
  * The definition carries no per-session value. The system prompt composes through
21
22
  * `composeSystemPrompt` with the identity part and the conversational part, the
@@ -26,11 +27,10 @@ import type { Pool } from "pg";
26
27
  import type { AuthContext, Identity } from "../auth/types.js";
27
28
  import type { ToolOutputStore } from "../loop/tool-output.js";
28
29
  import type { AgentDefinition } from "../loop/types.js";
29
- import type { SandboxClient } from "../sandbox/client.js";
30
30
  import type { ReportSessionStateStore } from "../state/report-session-state.js";
31
31
  import type { EmbeddingProvider, ProviderOptions } from "../providers/types.js";
32
32
  import type { WorkspaceFilesystem } from "../workspace/filesystem.js";
33
- import type { ResolveWorkspaceRoot } from "../workspace/paths.js";
33
+ import { type ResolveWorkspaceRoot } from "../workspace/paths.js";
34
34
  import type { ChromeConfig } from "../lib/chrome.js";
35
35
  import type { AcquireEyes } from "../lib/eyes.js";
36
36
  import type { Logger } from "../lib/logger.js";
@@ -39,7 +39,9 @@ import type { ReferenceResolver } from "../report-model/reference-resolver.js";
39
39
  import type { ReportVersionStore } from "../state/report-versions.js";
40
40
  import type { ReportSessionStateGateway } from "../tools/report-authoring/authoring-tools.js";
41
41
  import type { ProvenanceSeam } from "../provenance/seam.js";
42
+ import type { SyncSessionFile } from "../workspace/session-files.js";
42
43
  import { type MakeSessionPagePublisher, type ResolvePageAsset, type ResolvePageUrl } from "../tools/report-session/index.js";
44
+ import type { ScriptRuntime } from "../tools/script/run-script.js";
43
45
  /** Canonical agent id -- distinct from the id of the old templating agent. */
44
46
  export declare const REPORT_SESSION_AGENT_ID: "report-session";
45
47
  /**
@@ -68,14 +70,8 @@ export type ReportSessionAgentDeps = {
68
70
  readonly resolveWorkspaceRoot: ResolveWorkspaceRoot;
69
71
  /** Append-only version store -- the record tool gates the whole document first, then records one version. */
70
72
  readonly store: ReportVersionStore;
71
- /** Derivation ledger -- the derivation tool appends one record for each derived table. */
73
+ /** Derivation ledger -- `write_derived` appends one record for each derived table. */
72
74
  readonly derivations: Pick<ReportSessionStateStore, "appendDerivation">;
73
- /**
74
- * Sandbox client -- the derivation tool runs one ephemeral container for each derived table, inside the
75
- * step of its call. Omitted, the tool reports that the composition gives no sandbox, and it derives
76
- * nothing.
77
- */
78
- readonly sandboxClient?: SandboxClient;
79
75
  /** Thread store -- the record tool reads the anchor of the report thread, and the preview tool names it. */
80
76
  readonly threads: Pick<ThreadStore, "getThread" | "setAutoTitle">;
81
77
  /** Headless-Chrome config -- the eyes tool opens the rendered page. */
@@ -118,16 +114,20 @@ export type ReportSessionAgentDeps = {
118
114
  readonly resolvePageUrl?: ResolvePageUrl;
119
115
  /**
120
116
  * The provenance seam -- the acts of the session as typed events, and the frozen document of the
121
- * analysis as opaque bytes. The tools that change the draft, derive a table, preview the page, and
117
+ * analysis as opaque bytes. The tools that change the draft, preview the page, and
122
118
  * record a version each emit through the session member, thus one bound member carries the whole
123
119
  * session. The preview asks the read member before each render, and the page then carries the
124
120
  * document. An unbound member emits nothing, or gives absence, and every tool acts the same.
125
121
  */
126
122
  readonly provenance?: ProvenanceSeam;
123
+ /** The host copy of each file that the session writes. See `SyncSessionFile`. */
124
+ readonly syncSessionFile?: SyncSessionFile;
127
125
  /** Operational logging seam; omitted falls back to no-op. */
128
126
  readonly logger?: Logger;
129
127
  /** Adds `read_tool_output`. The loop of a report turn must keep its texts in the same store. */
130
128
  readonly toolOutputStore?: ToolOutputStore;
129
+ /** Where the VM of `run_script` comes from. A bundled host gives the embedded paths; see `ScriptRuntime`. */
130
+ readonly scriptRuntime?: ScriptRuntime;
131
131
  };
132
132
  /** Build the report `AgentDefinition` with every tool bound to its deps. */
133
133
  export declare function createReportSessionAgent(deps: ReportSessionAgentDeps): AgentDefinition;
@@ -11,11 +11,12 @@
11
11
  * the workspace search, the run inspection, and the data-profile inspection. It
12
12
  * holds no planner, no run launcher, no working-memory write, and no sandbox
13
13
  * mutate surface. Thus no tool starts a run, and no tool changes an analysis. The
14
- * roster omits those tools, and no runtime guard blocks them.
14
+ * roster omits those tools, and no runtime guard blocks them. No tool runs a
15
+ * container.
15
16
  *
16
- * The derivation tool is the one tool that runs a container, and it changes no
17
- * analysis either. It mints no run id, it registers no artifact, and it writes under
18
- * the directory of the session alone.
17
+ * A derivation of a table runs in a script (`run_script`), and it changes no
18
+ * analysis: it mints no run id, it registers no artifact, and it writes under the
19
+ * directory of the session alone.
19
20
  *
20
21
  * The definition carries no per-session value. The system prompt composes through
21
22
  * `composeSystemPrompt` with the identity part and the conversational part, the
@@ -23,16 +24,20 @@
23
24
  * across threads, and the provider cache reuses it.
24
25
  */
25
26
  import { createReadToolOutputTool } from "../tools/read-tool-output.js";
27
+ import { reportSessionDir } from "../workspace/paths.js";
28
+ import { createDocumentTools, createReadPdfTool } from "../tools/script/file-tools.js";
26
29
  import { createReportAuthoringTools } from "../tools/report-authoring/authoring-tools.js";
27
- import { createDeriveTableTool, createExaminePageTool, createListPinnedArtifactsTool, createPreviewReportTool, createRecordVersionTool, } from "../tools/report-session/index.js";
30
+ import { createExaminePageTool, createListPinnedArtifactsTool, createPreviewReportTool, createRecordVersionTool, } from "../tools/report-session/index.js";
28
31
  import { createFileStatTool, createGrepTool, createListFilesTool, createReadFileTool, createWorkspaceSearchTool } from "../tools/workspace/index.js";
29
32
  import { createInspectDataProfileTool, createInspectRunTool } from "../tools/research/index.js";
30
33
  import { reportSessionPrompt } from "../prompts/report-session.js";
31
34
  import { composeSystemPrompt } from "./system-prompt.js";
35
+ import { createReportScriptTools } from "../tools/report-session/script-tools.js";
36
+ import { createScriptScratch } from "../tools/script/scratch.js";
32
37
  /** Canonical agent id -- distinct from the id of the old templating agent. */
33
38
  export const REPORT_SESSION_AGENT_ID = "report-session";
34
39
  /**
35
- * Runaway guard. A full session — orient, derive, compose each section, preview,
40
+ * Runaway guard. A full session — orient, compose each section, preview,
36
41
  * look, record — drives more small tool calls than a conversation turn, thus it
37
42
  * gets the headroom of the report runner (REPORT_AGENT_MAX_STEPS).
38
43
  */
@@ -50,7 +55,7 @@ export const REPORT_SESSION_PROVIDER_OPTIONS = {
50
55
  /** Build the report `AgentDefinition` with every tool bound to its deps. */
51
56
  export function createReportSessionAgent(deps) {
52
57
  const { model, pool, embedding, workspaceFs, gateway, resolveWorkspaceRoot, store, threads, chrome, eyes, makeResolver, resolvePageAsset, logger } = deps;
53
- const { derivations, sandboxClient, makeSessionPages, resolvePageUrl, provenance } = deps;
58
+ const { derivations, makeSessionPages, resolvePageUrl, provenance, syncSessionFile } = deps;
54
59
  // The seam rides into each tool that lands an act of the session, thus the composition binds it one
55
60
  // time and no tool reads a second source.
56
61
  const observation = provenance ? { provenance } : {};
@@ -65,15 +70,7 @@ export function createReportSessionAgent(deps) {
65
70
  createWorkspaceSearchTool(pool, embedding),
66
71
  createInspectRunTool(pool),
67
72
  createInspectDataProfileTool(pool),
68
- // The composition surface. Each authoring tool binds to the gateway, thus
69
- // two threads never share one draft.
70
- authoring.add_block,
71
- authoring.change_block,
72
- authoring.remove_block,
73
- authoring.move_block,
74
- authoring.set_title,
75
- authoring.read_outline,
76
- authoring.read_block,
73
+ // The gate of the draft. The edits of the draft are script tools below.
77
74
  authoring.finish_draft,
78
75
  // The orientation source of the pinned evidence. It reads the snapshot of the
79
76
  // thread, and it resolves the workspace root per call for the header columns.
@@ -82,16 +79,6 @@ export function createReportSessionAgent(deps) {
82
79
  resolveWorkspaceRoot,
83
80
  logger,
84
81
  }),
85
- // The derivation tool. It reshapes the pinned evidence into one table under the
86
- // session directory, on the sandbox rails of the value tier.
87
- createDeriveTableTool({
88
- gateway,
89
- resolveWorkspaceRoot,
90
- derivations,
91
- sandboxClient,
92
- ...observation,
93
- logger,
94
- }),
95
82
  // The render-and-preview tool. It resolves the page root per call.
96
83
  createPreviewReportTool({
97
84
  gateway,
@@ -126,11 +113,38 @@ export function createReportSessionAgent(deps) {
126
113
  logger,
127
114
  }),
128
115
  ];
116
+ // A derivation of a table of up to the read cap runs in a script, thus it starts no container. The scripts
117
+ // of a session stay beside its page.
118
+ const scriptScratch = createScriptScratch({ resolveWorkspaceRoot, sessionDir: reportSessionDir, provenance, syncSessionFile, logger });
119
+ // A PDF of the report is a document that a script builds from the content of the session, not a print of the
120
+ // page. Its images come from the directory of the session alone, thus no unpinned file reaches it.
121
+ const scriptTools = [
122
+ // The edits of the draft. Each binds to the gateway, thus two threads never share one draft, and a script
123
+ // builds a section in one call. A refused edit comes back as data, thus the script reads `applied`.
124
+ authoring.add_block,
125
+ authoring.change_block,
126
+ authoring.remove_block,
127
+ authoring.move_block,
128
+ authoring.set_title,
129
+ authoring.read_outline,
130
+ authoring.read_block,
131
+ ...createReportScriptTools({ gateway, workspaceFs, resolveWorkspaceRoot, derivations, provenance, syncSessionFile, logger }),
132
+ createReadPdfTool({ workspaceFs, resolveWorkspaceRoot }),
133
+ ...createDocumentTools({
134
+ workspaceFs,
135
+ resolveWorkspaceRoot,
136
+ scratch: scriptScratch,
137
+ imageScope: (session) => (session.scope.threadId === undefined ? undefined : reportSessionDir(session.scope.threadId)),
138
+ }),
139
+ ];
129
140
  return {
130
141
  id: REPORT_SESSION_AGENT_ID,
131
142
  systemPrompt: composeSystemPrompt(reportSessionPrompt, { identity: true, conversational: true }),
132
143
  model,
133
144
  tools,
145
+ scriptTools,
146
+ scriptRuntime: deps.scriptRuntime,
147
+ scriptScratch,
134
148
  maxIterations: REPORT_SESSION_MAX_ITERATIONS,
135
149
  compaction: REPORT_SESSION_COMPACTION,
136
150
  providerOptions: REPORT_SESSION_PROVIDER_OPTIONS,
@@ -10,7 +10,7 @@
10
10
  * `allowedWritePrefix` through the host-side `WorkspaceMutator` —,
11
11
  * the workspace read tools (`read_file`, `grep`) over the shared
12
12
  * `WorkspaceFilesystem`, `inspect_data_profile` over the shared `Pool`, and the
13
- * bio/literature/context7/run-inspection tools its `meta.tools` allowlist names.
13
+ * bio/literature/context7 tools its `meta.tools` allowlist names.
14
14
  *
15
15
  * Tool resolution is a single registry lookup — every name in `meta.tools`
16
16
  * must map to a concrete `Tool`; unknown names throw at composition time
@@ -20,8 +20,8 @@ import type { Querier } from "../../state/db.js";
20
20
  import type { ToolOutputStore } from "../../loop/tool-output.js";
21
21
  import type { AgentDefinition } from "../../loop/types.js";
22
22
  import type { ChatProvider, EmbeddingProvider } from "../../providers/types.js";
23
- import type { SandboxClient } from "../../sandbox/client.js";
24
- import type { ExtendAnalysisFarm, SandboxRef } from "../../sandbox/types.js";
23
+ import type { BoundLease } from "../../sandbox/leases.js";
24
+ import type { ExtendAnalysisFarm, ToolchainSource } from "../../sandbox/types.js";
25
25
  import type { WorkspaceFilesystem } from "../../workspace/filesystem.js";
26
26
  import type { ProvenanceCollector as LineageCollector } from "../../provenance/collector.js";
27
27
  import { type BioToolKeys } from "../../tools/bio/keys.js";
@@ -31,9 +31,10 @@ import type { EnvironmentStorePaths } from "../../config/environment-stores.js";
31
31
  import type { Logger } from "../../lib/logger.js";
32
32
  import type { CitationResolver } from "../../citations/types.js";
33
33
  import { type KnowledgeClient, type TemplateBinding } from "../../tools/knowledge/index.js";
34
+ import type { ScriptRuntime } from "../../tools/script/run-script.js";
34
35
  /**
35
36
  * Tools every sandbox agent receives — sandbox-environment introspection,
36
- * library docs, run inspection, and the knowledge template. Spread into each
37
+ * library docs, and the knowledge template. Spread into each
37
38
  * agent's `meta.tools` so planner-facing metadata and the resolved tool record
38
39
  * stay in sync. Keep minimal — only tools every agent genuinely needs.
39
40
  *
@@ -47,14 +48,13 @@ import { type KnowledgeClient, type TemplateBinding } from "../../tools/knowledg
47
48
  export declare const BASE_SANDBOX_TOOLS: readonly SandboxToolName[];
48
49
  /** Per-step coordinates the composition root threads through every mutate tool. */
49
50
  export type SandboxStepCoords = {
50
- readonly sandbox: SandboxRef;
51
+ /** The lease of the step. `acquire_sandbox` and `release_sandbox` drive it, and each command runs on it. */
52
+ readonly lease: BoundLease;
51
53
  /** Absolute host root of this analysis's workspace tree (resolved at the workflow body). */
52
54
  readonly workspaceRoot: string;
53
55
  readonly analysisId: string;
54
56
  /** Absolute writable artifact directory for this step (e.g. stepWritePrefix(...)). */
55
57
  readonly allowedWritePrefix: string;
56
- /** Absolute unix-ms deadline of each exec. */
57
- readonly deadlineMs: () => number;
58
58
  /**
59
59
  * The plan settings bound to the template of this step, from the durable
60
60
  * step input. Reaches `knowledge_template` alone. Absent when the step
@@ -68,14 +68,16 @@ export type SandboxAgentDeps = EnvironmentStorePaths & {
68
68
  readonly logger?: Logger;
69
69
  readonly provider: ChatProvider;
70
70
  readonly pool: Querier;
71
- readonly sandboxClient: SandboxClient;
71
+ /** The toolchain of the sandboxes, as the sandbox client declares it. The orient-core prompt keys on it. */
72
+ readonly toolchainSource: ToolchainSource;
72
73
  readonly workspaceFs: WorkspaceFilesystem;
73
74
  /** Embedding provider for in-sandbox semantic `workspace_search`. Omit to skip that tool. */
74
75
  readonly embedding?: EmbeddingProvider;
75
76
  /**
76
77
  * Step-scoped lineage collector fed by each `execute_command`'s
77
- * `ExecResult.provenance` frame. Records the input/script edges post-step
78
- * registration translates into provenance parents.
78
+ * `ExecResult.provenance` frame, and by the reads of `read_file`, `grep`, and
79
+ * `read_pdf`. Records the input/script edges post-step registration
80
+ * translates into provenance parents.
79
81
  */
80
82
  readonly lineageCollector?: LineageCollector;
81
83
  /** Model id provenance label — the provider owns the wire model. */
@@ -103,8 +105,9 @@ export type SandboxAgentDeps = EnvironmentStorePaths & {
103
105
  readonly toolOutputStore?: ToolOutputStore;
104
106
  /**
105
107
  * The farm-extension seam of the embedder. When it is bound, every
106
- * sandbox agent gets the `link_packages` tool in its always-on substrate,
107
- * and the package-link prompt layer appends. Without it, neither exists.
108
+ * sandbox agent gets the `link_packages` tool in its always-on substrate.
109
+ * The system prompt is the same with and without it: the descriptions of
110
+ * `link_packages` and `list_available_packages` carry the link rules.
108
111
  */
109
112
  readonly extendAnalysisFarm?: ExtendAnalysisFarm;
110
113
  /**
@@ -114,6 +117,8 @@ export type SandboxAgentDeps = EnvironmentStorePaths & {
114
117
  * of the open-source host and never an error.
115
118
  */
116
119
  readonly knowledge?: KnowledgeClient;
120
+ /** Where the VM of `run_script` comes from, for the lookups of the agent. */
121
+ readonly scriptRuntime?: ScriptRuntime;
117
122
  };
118
123
  /** Per-agent override for the prompt composition and tool surface. */
119
124
  export type SandboxAgentPromptOptions = {
@@ -138,7 +143,7 @@ export type SandboxAgentPromptOptions = {
138
143
  *
139
144
  * The composed `systemPrompt` is a **pure function of the agent type** — nothing
140
145
  * from `deps.step` reaches it, so two steps of one run, and two runs of one
141
- * analysis, send a byte-identical ~20k-char prefix that the provider's prompt
146
+ * analysis, send a byte-identical prefix that the provider's prompt
142
147
  * cache can actually reuse. Keep it that way: a single interpolated id or path
143
148
  * makes every step's prefix unique again, and each step pays a full cache write
144
149
  * and reads nothing back. Per-step values belong in the step's briefing (its
@@ -10,21 +10,20 @@
10
10
  * `allowedWritePrefix` through the host-side `WorkspaceMutator` —,
11
11
  * the workspace read tools (`read_file`, `grep`) over the shared
12
12
  * `WorkspaceFilesystem`, `inspect_data_profile` over the shared `Pool`, and the
13
- * bio/literature/context7/run-inspection tools its `meta.tools` allowlist names.
13
+ * bio/literature/context7 tools its `meta.tools` allowlist names.
14
14
  *
15
15
  * Tool resolution is a single registry lookup — every name in `meta.tools`
16
16
  * must map to a concrete `Tool`; unknown names throw at composition time
17
17
  * so misconfigured agents fail at startup, not at the first LLM call.
18
18
  */
19
19
  import { createEditIntentStore } from "../../state/edit-intents.js";
20
- import { sandboxOrientCorePromptFor, sandboxPackageLinkPrompt, sandboxAnalysisStepStandardsPrompt } from "../../prompts/sandbox-standards.js";
20
+ import { createWorkspaceReadLineage } from "../../provenance/workspace-reads.js";
21
+ import { sandboxOrientCorePromptFor, sandboxAnalysisStepStandardsPrompt } from "../../prompts/sandbox-standards.js";
21
22
  import { composeSystemPrompt } from "../system-prompt.js";
22
23
  // Sandbox-environment introspection, plus the farm-extension seam face.
23
24
  import { createLinkPackagesTool, createListAvailablePackagesTool, createListAvailableRefsTool } from "../../tools/sandbox/index.js";
24
25
  // Context7 docs (pure leaves).
25
26
  import { queryDocsTool, resolveLibraryIdTool } from "../../tools/research/context7-docs.js";
26
- // Run-inspection (dependency-bearing).
27
- import { createInspectRunTool } from "../../tools/research/inspect-run.js";
28
27
  // Data-profile retrieval (dependency-bearing) — always-on, see `createSandboxAgent`.
29
28
  import { createInspectDataProfileTool } from "../../tools/research/inspect-data-profile.js";
30
29
  // Bio leaf tools.
@@ -38,15 +37,17 @@ import { createEditFileTool, createExecuteCommandTool, createWorkspaceMutator, c
38
37
  // Skills (declared per agent via meta.skills).
39
38
  import { createSkillTools } from "../../tools/sandbox/skills.js";
40
39
  import { createReadToolOutputTool } from "../../tools/read-tool-output.js";
40
+ import { createAcquireSandboxTool, createReleaseSandboxTool } from "../../tools/sandbox/lease-tools.js";
41
41
  import { createReportBlockerTool } from "../../tools/sandbox/report-blocker.js";
42
42
  import { createSubmitFileMetadataTool } from "../../tools/sandbox/submit-file-metadata.js";
43
43
  import { toSandboxPath } from "../../workspace/paths.js";
44
44
  import { SANDBOX_AGENT_DEFAULT_MAX_ITERATIONS } from "./types.js";
45
45
  import { createResolveCitationTool } from "../../tools/research/resolve-citation.js";
46
46
  import { createKnowledgeTemplateTool } from "../../tools/knowledge/index.js";
47
+ import { createReadPdfTool } from "../../tools/script/file-tools.js";
47
48
  /**
48
49
  * Tools every sandbox agent receives — sandbox-environment introspection,
49
- * library docs, run inspection, and the knowledge template. Spread into each
50
+ * library docs, and the knowledge template. Spread into each
50
51
  * agent's `meta.tools` so planner-facing metadata and the resolved tool record
51
52
  * stay in sync. Keep minimal — only tools every agent genuinely needs.
52
53
  *
@@ -62,9 +63,13 @@ export const BASE_SANDBOX_TOOLS = [
62
63
  "listAvailableRefs",
63
64
  "resolveLibraryId",
64
65
  "queryDocs",
65
- "inspectRun",
66
66
  "knowledgeTemplate",
67
67
  ];
68
+ /**
69
+ * The tools of the allowlist that stay direct: the environment of the step. Each other tool of the allowlist is a
70
+ * lookup of an outside source, and it runs in `run_script`.
71
+ */
72
+ const DIRECT_ALLOWLIST_TOOLS = new Set(["list_available_packages", "list_available_refs", "scan_inputs", "knowledge_template"]);
68
73
  /**
69
74
  * Resolve `meta.tools` into the bio/research tools record. The workspace
70
75
  * read/mutate tools and `inspect_data_profile` are added by `createSandboxAgent`
@@ -89,13 +94,10 @@ function resolveSandboxTools(deps, tools, mutator) {
89
94
  scanInputs: createScanInputsTool({
90
95
  workspaceFs: deps.workspaceFs,
91
96
  analysisId: deps.step.analysisId,
92
- sandboxClient: deps.sandboxClient,
93
- sandbox: deps.step.sandbox,
94
- deadlineMs: deps.step.deadlineMs,
97
+ lease: deps.step.lease,
95
98
  }),
96
99
  resolveLibraryId: resolveLibraryIdTool,
97
100
  queryDocs: queryDocsTool,
98
- inspectRun: createInspectRunTool(deps.pool),
99
101
  pubmed: ncbi.pubmed,
100
102
  resolve_citation: deps.citationResolver === undefined ? undefined : createResolveCitationTool(deps.citationResolver),
101
103
  searchGene: searchGeneTool,
@@ -146,8 +148,8 @@ function resolveSandboxTools(deps, tools, mutator) {
146
148
  /** Build the workspace mutate + read tools every sandbox agent receives. In
147
149
  * `readOnly` mode the write_file/edit_file pair is omitted; execute_command
148
150
  * and the read tools stay. */
149
- function buildWorkspaceTools(deps, readOnly) {
150
- const { step, sandboxClient, workspaceFs, pool, embedding, lineageCollector } = deps;
151
+ function buildWorkspaceTools(deps, readOnly, readLineage) {
152
+ const { step, workspaceFs, pool, embedding, lineageCollector } = deps;
151
153
  // The agent's writable working directory: relative paths resolve here, and
152
154
  // writes are confined here. `allowedWritePrefix` is its host path; the
153
155
  // in-sandbox path (the `execute_command` default cwd, see the harness-workspace-tools spec) is the same
@@ -170,10 +172,10 @@ function buildWorkspaceTools(deps, readOnly) {
170
172
  ]
171
173
  : [];
172
174
  const tools = [
175
+ createAcquireSandboxTool(step.lease),
176
+ createReleaseSandboxTool(step.lease),
173
177
  createExecuteCommandTool({
174
- sandboxClient,
175
- sandbox: step.sandbox,
176
- deadlineMs: step.deadlineMs,
178
+ lease: step.lease,
177
179
  defaultCwd: sandboxWorkingDir,
178
180
  lineageCollector,
179
181
  mountRoot: `/${step.analysisId}`,
@@ -181,10 +183,10 @@ function buildWorkspaceTools(deps, readOnly) {
181
183
  mutator,
182
184
  }),
183
185
  ...mutateTools,
184
- createReadFileTool(workspaceFs, hostWorkingDir),
186
+ createReadFileTool(workspaceFs, hostWorkingDir, readLineage),
185
187
  createListFilesTool(workspaceFs, hostWorkingDir),
186
188
  createFileStatTool(workspaceFs, hostWorkingDir),
187
- createGrepTool(workspaceFs, hostWorkingDir),
189
+ createGrepTool(workspaceFs, hostWorkingDir, readLineage),
188
190
  ...(embedding ? [createWorkspaceSearchTool(pool, embedding)] : []),
189
191
  ];
190
192
  return { tools, mutator };
@@ -200,7 +202,7 @@ function buildWorkspaceTools(deps, readOnly) {
200
202
  *
201
203
  * The composed `systemPrompt` is a **pure function of the agent type** — nothing
202
204
  * from `deps.step` reaches it, so two steps of one run, and two runs of one
203
- * analysis, send a byte-identical ~20k-char prefix that the provider's prompt
205
+ * analysis, send a byte-identical prefix that the provider's prompt
204
206
  * cache can actually reuse. Keep it that way: a single interpolated id or path
205
207
  * makes every step's prefix unique again, and each step pays a full cache write
206
208
  * and reads nothing back. Per-step values belong in the step's briefing (its
@@ -210,20 +212,27 @@ function buildWorkspaceTools(deps, readOnly) {
210
212
  export function createSandboxAgent(deps, meta, body, opts = {}) {
211
213
  const appendStandards = opts.appendAnalysisStepStandards ?? true;
212
214
  // The layer order is fixed by the harness-sandbox-agents spec: the agent
213
- // body, the orient core keyed on the toolchain of the sandbox client, the
214
- // package-link layer only when the seam is bound, then the step standards.
215
+ // body, the orient core keyed on the toolchain of the sandbox client, then
216
+ // the step standards.
215
217
  // The client is the one declaration: the environment it provisions and the
216
218
  // text that describes it come from the same value, thus a bag cannot
217
219
  // describe a toolchain the sandbox does not have.
218
- const sandboxLayer = [
219
- sandboxOrientCorePromptFor(deps.sandboxClient.toolchainSource),
220
- ...(deps.extendAnalysisFarm ? [sandboxPackageLinkPrompt] : []),
221
- ...(appendStandards ? [sandboxAnalysisStepStandardsPrompt] : []),
222
- ];
220
+ const sandboxLayer = [sandboxOrientCorePromptFor(deps.toolchainSource), ...(appendStandards ? [sandboxAnalysisStepStandardsPrompt] : [])];
223
221
  const agentBody = [body, ...sandboxLayer].map((s) => s.trim()).join("\n\n");
224
222
  const systemPrompt = composeSystemPrompt(agentBody);
225
223
  const skillTools = deps.skillsDir ? Object.values(createSkillTools({ skillsDir: deps.skillsDir, skills: meta.skills })) : [];
226
- const workspace = buildWorkspaceTools(deps, opts.readOnly ?? false);
224
+ // The working directory is the one of the read tools, thus a relative path resolves to the same file.
225
+ const readLineage = deps.lineageCollector
226
+ ? createWorkspaceReadLineage({
227
+ collector: deps.lineageCollector,
228
+ workspaceRoot: deps.step.workspaceRoot,
229
+ analysisId: deps.step.analysisId,
230
+ workingDir: deps.step.allowedWritePrefix,
231
+ logger: deps.logger,
232
+ })
233
+ : undefined;
234
+ const workspace = buildWorkspaceTools(deps, opts.readOnly ?? false, readLineage);
235
+ const allowlisted = resolveSandboxTools(deps, meta.tools, workspace.mutator);
227
236
  const tools = [
228
237
  ...workspace.tools,
229
238
  ...(deps.toolOutputStore ? [createReadToolOutputTool(deps.toolOutputStore)] : []),
@@ -238,7 +247,7 @@ export function createSandboxAgent(deps, meta, body, opts = {}) {
238
247
  // seam (the harness-sandbox-agents spec).
239
248
  ...(deps.extendAnalysisFarm ? [createLinkPackagesTool({ extendAnalysisFarm: deps.extendAnalysisFarm, analysisId: deps.step.analysisId })] : []),
240
249
  ...skillTools,
241
- ...resolveSandboxTools(deps, meta.tools, workspace.mutator),
250
+ ...allowlisted.filter((tool) => DIRECT_ALLOWLIST_TOOLS.has(tool.id)),
242
251
  ...(deps.reportsBlocker === true ? [createReportBlockerTool()] : []),
243
252
  ...(deps.fileMetadata ? [createSubmitFileMetadataTool(deps.fileMetadata)] : []),
244
253
  ];
@@ -247,6 +256,12 @@ export function createSandboxAgent(deps, meta, body, opts = {}) {
247
256
  systemPrompt,
248
257
  model: deps.model,
249
258
  tools,
259
+ // A paper of the analysis is a PDF, and each step can need it, thus each agent reads PDFs in a script.
260
+ scriptTools: [
261
+ ...allowlisted.filter((tool) => !DIRECT_ALLOWLIST_TOOLS.has(tool.id)),
262
+ createReadPdfTool({ workspaceFs: deps.workspaceFs, resolveWorkspaceRoot: () => Promise.resolve(deps.step.workspaceRoot), lineage: readLineage }),
263
+ ],
264
+ scriptRuntime: deps.scriptRuntime,
250
265
  maxIterations: meta.defaultMaxSteps ?? SANDBOX_AGENT_DEFAULT_MAX_ITERATIONS,
251
266
  };
252
267
  }