@inflexa-ai/harness 0.46.2 → 0.47.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 (271) hide show
  1. package/README.md +1 -1
  2. package/dist/agents/conversation-agent.d.ts +3 -8
  3. package/dist/agents/conversation-agent.js +24 -4
  4. package/dist/agents/report-session-agent.d.ts +11 -14
  5. package/dist/agents/report-session-agent.js +30 -17
  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 +2 -5
  25. package/dist/index.js +3 -6
  26. package/dist/input-scan/enrich.d.ts +5 -10
  27. package/dist/input-scan/enrich.js +12 -8
  28. package/dist/lib/markdown-pdf.d.ts +27 -0
  29. package/dist/lib/markdown-pdf.js +236 -0
  30. package/dist/lib/metrics.d.ts +31 -0
  31. package/dist/lib/metrics.js +64 -0
  32. package/dist/lib/pdf-text.d.ts +23 -0
  33. package/dist/lib/pdf-text.js +34 -0
  34. package/dist/lib/regulatory-corpus/fetch-fda-guidance.js +3 -5
  35. package/dist/lib/regulatory-corpus/fetch-ich.js +3 -5
  36. package/dist/loop/run-loop.d.ts +5 -0
  37. package/dist/loop/run-loop.js +30 -2
  38. package/dist/loop/tool-name-repair.d.ts +11 -0
  39. package/dist/loop/tool-name-repair.js +23 -0
  40. package/dist/loop/types.d.ts +14 -0
  41. package/dist/memory/card-builders.d.ts +1 -1
  42. package/dist/prompts/SOUL.d.ts +6 -6
  43. package/dist/prompts/SOUL.js +22 -121
  44. package/dist/prompts/analogical-reasoner.d.ts +1 -1
  45. package/dist/prompts/analogical-reasoner.js +35 -101
  46. package/dist/prompts/briefing.d.ts +20 -11
  47. package/dist/prompts/briefing.js +36 -19
  48. package/dist/prompts/conversation.d.ts +1 -1
  49. package/dist/prompts/conversation.js +103 -383
  50. package/dist/prompts/planner.d.ts +1 -5
  51. package/dist/prompts/planner.js +45 -128
  52. package/dist/prompts/report-session.d.ts +1 -1
  53. package/dist/prompts/report-session.js +26 -57
  54. package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
  55. package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +3 -3
  56. package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
  57. package/dist/prompts/sandbox/cheminformatics-agent.js +12 -47
  58. package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
  59. package/dist/prompts/sandbox/chromatin-agent.js +4 -5
  60. package/dist/prompts/sandbox/data-profiler.js +11 -34
  61. package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
  62. package/dist/prompts/sandbox/dna-methylation-agent.js +3 -1
  63. package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
  64. package/dist/prompts/sandbox/drug-repurposing-agent.js +23 -73
  65. package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
  66. package/dist/prompts/sandbox/enrichment-agent.js +3 -1
  67. package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
  68. package/dist/prompts/sandbox/genomic-variant-agent.js +3 -4
  69. package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
  70. package/dist/prompts/sandbox/immune-profiling-agent.js +5 -4
  71. package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
  72. package/dist/prompts/sandbox/metabolomics-agent.js +3 -1
  73. package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
  74. package/dist/prompts/sandbox/microbiome-agent.js +3 -1
  75. package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
  76. package/dist/prompts/sandbox/multi-omics-integration-agent.js +3 -1
  77. package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
  78. package/dist/prompts/sandbox/multimodal-sc-agent.js +3 -1
  79. package/dist/prompts/sandbox/network-agent.d.ts +1 -1
  80. package/dist/prompts/sandbox/network-agent.js +3 -1
  81. package/dist/prompts/sandbox/pkpd-clinical-response-agent.d.ts +1 -1
  82. package/dist/prompts/sandbox/pkpd-clinical-response-agent.js +4 -7
  83. package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
  84. package/dist/prompts/sandbox/proteomics-agent.js +3 -1
  85. package/dist/prompts/sandbox/scientific-executor.d.ts +1 -1
  86. package/dist/prompts/sandbox/scientific-executor.js +0 -12
  87. package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
  88. package/dist/prompts/sandbox/single-cell-agent.js +3 -1
  89. package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
  90. package/dist/prompts/sandbox/spatial-omics-agent.js +3 -1
  91. package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
  92. package/dist/prompts/sandbox/statistical-modeling-agent.js +4 -4
  93. package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
  94. package/dist/prompts/sandbox/translational-safety-agent.js +10 -29
  95. package/dist/prompts/sandbox-standards.d.ts +15 -18
  96. package/dist/prompts/sandbox-standards.js +96 -305
  97. package/dist/prompts/synthesis-agent.d.ts +1 -1
  98. package/dist/prompts/synthesis-agent.js +24 -90
  99. package/dist/provenance/collector.d.ts +2 -5
  100. package/dist/provenance/collector.js +2 -5
  101. package/dist/provenance/seam.d.ts +36 -1
  102. package/dist/provenance/workspace-reads.d.ts +36 -0
  103. package/dist/provenance/workspace-reads.js +33 -0
  104. package/dist/report-model/production-resolver.d.ts +22 -52
  105. package/dist/report-model/production-resolver.js +43 -112
  106. package/dist/report-render/chart-export.d.ts +9 -0
  107. package/dist/report-render/chart-export.js +1 -1
  108. package/dist/report-render/views/chart-view.d.ts +1 -0
  109. package/dist/report-render/views/chart-view.js +1 -0
  110. package/dist/report-render/views/lineage.d.ts +1 -0
  111. package/dist/report-render/views/page-view.d.ts +1 -0
  112. package/dist/report-render/views/page-view.js +1 -0
  113. package/dist/report-render/views/prose.d.ts +1 -0
  114. package/dist/report-render/views/prose.js +1 -0
  115. package/dist/report-render/views/references-view.d.ts +1 -0
  116. package/dist/report-render/views/values.d.ts +1 -0
  117. package/dist/report-render/views/values.js +1 -0
  118. package/dist/runtime/assemble.d.ts +20 -9
  119. package/dist/runtime/assemble.js +47 -38
  120. package/dist/sandbox/client.d.ts +5 -3
  121. package/dist/sandbox/create-sandbox.d.ts +7 -12
  122. package/dist/sandbox/create-sandbox.js +15 -37
  123. package/dist/sandbox/docker-client.d.ts +1 -3
  124. package/dist/sandbox/docker-client.js +3 -5
  125. package/dist/sandbox/exec-outcome.d.ts +11 -11
  126. package/dist/sandbox/exec-outcome.js +22 -22
  127. package/dist/sandbox/exec.d.ts +5 -0
  128. package/dist/sandbox/exec.js +9 -1
  129. package/dist/sandbox/identity.d.ts +4 -16
  130. package/dist/sandbox/identity.js +5 -21
  131. package/dist/sandbox/k8s-client.d.ts +0 -1
  132. package/dist/sandbox/k8s-client.js +8 -13
  133. package/dist/sandbox/lease-names.d.ts +6 -0
  134. package/dist/sandbox/lease-names.js +12 -0
  135. package/dist/sandbox/leases.d.ts +105 -0
  136. package/dist/sandbox/leases.js +519 -0
  137. package/dist/sandbox/liveness.d.ts +5 -2
  138. package/dist/sandbox/mount-plan.d.ts +10 -1
  139. package/dist/sandbox/mount-plan.js +7 -1
  140. package/dist/sandbox/reconciler.d.ts +58 -0
  141. package/dist/sandbox/reconciler.js +153 -0
  142. package/dist/sandbox/sandbox-step-translate.d.ts +1 -1
  143. package/dist/sandbox/types.d.ts +25 -18
  144. package/dist/sandbox/types.js +10 -4
  145. package/dist/schemas/plan-schemas.d.ts +0 -21
  146. package/dist/schemas/plan-schemas.js +2 -3
  147. package/dist/schemas/run-synthesis.d.ts +2 -2
  148. package/dist/schemas/run-synthesis.js +3 -3
  149. package/dist/schemas/validate-plan.d.ts +1 -14
  150. package/dist/schemas/validate-plan.js +5 -24
  151. package/dist/schemas/workflow-state.d.ts +3 -32
  152. package/dist/schemas/workflow-state.js +4 -13
  153. package/dist/state/index.d.ts +0 -1
  154. package/dist/state/index.js +0 -1
  155. package/dist/state/migrations/20261030120000_lease_the_sandboxes.d.ts +7 -0
  156. package/dist/state/migrations/20261030120000_lease_the_sandboxes.js +34 -0
  157. package/dist/state/migrations/20261030120100_drop_the_report_derivations.d.ts +5 -0
  158. package/dist/state/migrations/20261030120100_drop_the_report_derivations.js +9 -0
  159. package/dist/state/migrations/20261030120200_keep_the_report_derivations.d.ts +6 -0
  160. package/dist/state/migrations/20261030120200_keep_the_report_derivations.js +10 -0
  161. package/dist/state/migrations/index.js +6 -0
  162. package/dist/state/purge-analysis.d.ts +4 -2
  163. package/dist/state/purge-analysis.js +22 -11
  164. package/dist/state/sandbox-leases.d.ts +81 -0
  165. package/dist/state/sandbox-leases.js +247 -0
  166. package/dist/state/schema.d.ts +1 -13
  167. package/dist/state/schema.js +1 -6
  168. package/dist/state/step-executions.d.ts +0 -2
  169. package/dist/state/step-executions.js +1 -4
  170. package/dist/tasks/data-profile.d.ts +40 -2
  171. package/dist/tasks/data-profile.js +89 -87
  172. package/dist/tools/ad-hoc-router.d.ts +1 -6
  173. package/dist/tools/ad-hoc-router.js +5 -48
  174. package/dist/tools/bio/comptox.js +1 -1
  175. package/dist/tools/bio/drug-gene-interactions.js +1 -1
  176. package/dist/tools/bio/gene-disease-evidence.js +1 -1
  177. package/dist/tools/bio/keys.d.ts +1 -1
  178. package/dist/tools/bio/pubmed.js +31 -0
  179. package/dist/tools/bio/search-gene.js +17 -0
  180. package/dist/tools/bio/search-geo-datasets.js +13 -0
  181. package/dist/tools/bio/search-regulatory-approvals.js +1 -1
  182. package/dist/tools/define-tool.d.ts +25 -2
  183. package/dist/tools/define-tool.js +11 -1
  184. package/dist/tools/display/show-user.js +1 -1
  185. package/dist/tools/execute-analysis.d.ts +1 -3
  186. package/dist/tools/execute-analysis.js +5 -33
  187. package/dist/tools/report-session/index.d.ts +0 -1
  188. package/dist/tools/report-session/index.js +0 -1
  189. package/dist/tools/report-session/script-tools.d.ts +30 -0
  190. package/dist/tools/report-session/script-tools.js +177 -0
  191. package/dist/tools/research/generate-analogy-report.d.ts +16 -9
  192. package/dist/tools/research/generate-analogy-report.js +111 -49
  193. package/dist/tools/research/generate-plan.d.ts +102 -7
  194. package/dist/tools/research/generate-plan.js +74 -43
  195. package/dist/tools/research/index.d.ts +4 -3
  196. package/dist/tools/research/index.js +4 -3
  197. package/dist/tools/research/inspect-run.d.ts +0 -1
  198. package/dist/tools/research/inspect-run.js +5 -10
  199. package/dist/tools/research/search-arxiv.js +15 -0
  200. package/dist/tools/research/search-github-repos.js +13 -0
  201. package/dist/tools/research/search-semantic-scholar.js +16 -0
  202. package/dist/tools/research/synthesis-lookups.d.ts +21 -0
  203. package/dist/tools/research/synthesis-lookups.js +29 -0
  204. package/dist/tools/research/web-fetch.d.ts +1 -1
  205. package/dist/tools/research/web-fetch.js +14 -12
  206. package/dist/tools/sandbox/lease-tools.d.ts +32 -0
  207. package/dist/tools/sandbox/lease-tools.js +60 -0
  208. package/dist/tools/script/codemode-worker.d.ts +5 -0
  209. package/dist/tools/script/codemode-worker.js +5 -0
  210. package/dist/tools/script/file-tools.d.ts +100 -0
  211. package/dist/tools/script/file-tools.js +377 -0
  212. package/dist/tools/script/metrics.d.ts +21 -0
  213. package/dist/tools/script/metrics.js +38 -0
  214. package/dist/tools/script/quickjs.wasm +0 -0
  215. package/dist/tools/script/run-script.d.ts +76 -0
  216. package/dist/tools/script/run-script.js +326 -0
  217. package/dist/tools/script/scratch.d.ts +81 -0
  218. package/dist/tools/script/scratch.js +142 -0
  219. package/dist/tools/start-report-session.js +1 -1
  220. package/dist/tools/workspace/edit-file.js +2 -1
  221. package/dist/tools/workspace/execute-command.d.ts +9 -13
  222. package/dist/tools/workspace/execute-command.js +23 -19
  223. package/dist/tools/workspace/file-stat.js +1 -1
  224. package/dist/tools/workspace/grep.d.ts +5 -2
  225. package/dist/tools/workspace/grep.js +19 -10
  226. package/dist/tools/workspace/mutator.d.ts +48 -4
  227. package/dist/tools/workspace/mutator.js +43 -12
  228. package/dist/tools/workspace/read-file.d.ts +5 -2
  229. package/dist/tools/workspace/read-file.js +15 -11
  230. package/dist/tools/workspace/read-surface.d.ts +17 -0
  231. package/dist/tools/workspace/read-surface.js +17 -0
  232. package/dist/tools/workspace/result-bounds.d.ts +1 -0
  233. package/dist/tools/workspace/run-exec.d.ts +12 -13
  234. package/dist/tools/workspace/run-exec.js +13 -13
  235. package/dist/tools/workspace/scan-inputs.d.ts +2 -5
  236. package/dist/tools/workspace/scan-inputs.js +2 -3
  237. package/dist/tools/workspace/show-file.js +1 -1
  238. package/dist/tools/workspace/show-plan.js +4 -3
  239. package/dist/tools/workspace/write-file.js +2 -1
  240. package/dist/workflows/execute-analysis-scheduler.d.ts +0 -26
  241. package/dist/workflows/execute-analysis-scheduler.js +12 -32
  242. package/dist/workflows/execute-analysis.d.ts +17 -35
  243. package/dist/workflows/execute-analysis.js +13 -107
  244. package/dist/workflows/pause.js +29 -22
  245. package/dist/workflows/sandbox-step.d.ts +16 -31
  246. package/dist/workflows/sandbox-step.js +36 -71
  247. package/dist/workflows/test/sql-writes.d.ts +0 -1
  248. package/dist/workflows/test/sql-writes.js +0 -2
  249. package/dist/workspace/filesystem.d.ts +2 -2
  250. package/dist/workspace/filesystem.js +10 -5
  251. package/dist/workspace/paths.d.ts +19 -1
  252. package/dist/workspace/paths.js +22 -1
  253. package/package.json +6 -3
  254. package/dist/prompts/literature-reviewer.d.ts +0 -1
  255. package/dist/prompts/literature-reviewer.js +0 -114
  256. package/dist/sandbox/reaper.d.ts +0 -81
  257. package/dist/sandbox/reaper.js +0 -161
  258. package/dist/sandbox/step-sandbox.d.ts +0 -47
  259. package/dist/sandbox/step-sandbox.js +0 -53
  260. package/dist/state/active-sandboxes.d.ts +0 -33
  261. package/dist/state/active-sandboxes.js +0 -65
  262. package/dist/tasks/extract-values-script.d.ts +0 -64
  263. package/dist/tasks/extract-values-script.js +0 -180
  264. package/dist/tasks/extract-values.d.ts +0 -77
  265. package/dist/tasks/extract-values.js +0 -146
  266. package/dist/tools/report-session/derive-table.d.ts +0 -164
  267. package/dist/tools/report-session/derive-table.js +0 -419
  268. package/dist/tools/research/literature-reviewer.d.ts +0 -42
  269. package/dist/tools/research/literature-reviewer.js +0 -107
  270. package/dist/workflows/metrics.d.ts +0 -23
  271. package/dist/workflows/metrics.js +0 -38
package/README.md CHANGED
@@ -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";
@@ -47,6 +46,7 @@ import type { Logger } from "../lib/logger.js";
47
46
  import type { UsageRecorder } from "../billing/usage-recorder.js";
48
47
  import type { CitationResolver } from "../citations/types.js";
49
48
  import { type WebSearchEngine } from "../tools/research/web-search.js";
49
+ import type { ScriptRuntime } from "../tools/script/run-script.js";
50
50
  /** Canonical agent id — the single source of truth. */
51
51
  export declare const CONVERSATION_AGENT_ID: "conversation-agent";
52
52
  /**
@@ -142,13 +142,6 @@ export type ConversationAgentDeps = EnvironmentStorePaths & {
142
142
  * `write-file` session event for each successful write.
143
143
  */
144
144
  readonly provenance?: ProvenanceSeam;
145
- /**
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.
150
- */
151
- readonly resourcePolicy?: ResourcePolicy;
152
145
  /**
153
146
  * Embedder-contributed conversation-tool seam — additional tools the host
154
147
  * supplies, appended after the built-in roster. The harness stays agnostic to
@@ -156,6 +149,8 @@ export type ConversationAgentDeps = EnvironmentStorePaths & {
156
149
  * built-in tool.
157
150
  */
158
151
  readonly hostTools?: readonly Tool[];
152
+ /** Where the VM of `run_script` comes from. A bundled host gives the embedded paths; see `ScriptRuntime`. */
153
+ readonly scriptRuntime?: ScriptRuntime;
159
154
  /**
160
155
  * The farm-extension seam of the embedder. When it is bound,
161
156
  * `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 });
@@ -65,7 +68,18 @@ export function createConversationAgent(deps) {
65
68
  provenance: deps.provenance,
66
69
  logger: deps.logger,
67
70
  });
68
- const tools = [
71
+ // The scratch directory of each conversation: the scripts of `run_script`, and the files that they derive.
72
+ const scriptScratch = createScriptScratch({
73
+ resolveWorkspaceRoot: deps.resolveWorkspaceRoot,
74
+ sessionDir: conversationSessionDir,
75
+ provenance: deps.provenance,
76
+ logger: deps.logger,
77
+ });
78
+ // The lookups and the light work on tables run in a script (`run_script`). Each tool asks no approval, and a
79
+ // script writes only into the scratch directory of its conversation.
80
+ const scriptTools = [
81
+ // Tables of the workspace in, derived files and chart files out.
82
+ ...createScriptFileTools({ workspaceFs, resolveWorkspaceRoot: deps.resolveWorkspaceRoot, scratch: scriptScratch }),
69
83
  // Identifier resolution — the ENSG most other bio tools key off, and the
70
84
  // UniProt accession that the structure tools take.
71
85
  searchGeneTool,
@@ -112,6 +126,8 @@ export function createConversationAgent(deps) {
112
126
  targetSafetyTool,
113
127
  // EPA CompTox (toxcast / hazard / chemical / exposure behind one dataset).
114
128
  chemDb.comptox,
129
+ ];
130
+ const tools = [
115
131
  // What reference data this install actually holds. The conversation agent is
116
132
  // where provisioning is decided — an embedder may give it a way to install more,
117
133
  // and a host that does asks the user to approve the download. Deciding that
@@ -140,10 +156,11 @@ export function createConversationAgent(deps) {
140
156
  createGeneratePlanTool({
141
157
  conversation: { provider, model },
142
158
  pool,
143
- resourcePolicy,
144
159
  usageRecorder,
145
160
  toolOutputStore,
146
161
  bioKeys,
162
+ workspace: { workspaceFs, pool, embedding, resolveWorkspaceRoot: deps.resolveWorkspaceRoot },
163
+ scriptRuntime: deps.scriptRuntime,
147
164
  knowledge: deps.knowledge,
148
165
  refStorePath,
149
166
  farmLockFile,
@@ -156,7 +173,6 @@ export function createConversationAgent(deps) {
156
173
  executeAnalysisWorkflow,
157
174
  runAuthorizer,
158
175
  runLauncher,
159
- resourcePolicy,
160
176
  utilityProvider,
161
177
  utilityModel,
162
178
  farmLockFile,
@@ -193,6 +209,7 @@ export function createConversationAgent(deps) {
193
209
  usageRecorder,
194
210
  toolOutputStore,
195
211
  logger: deps.logger,
212
+ scriptRuntime: deps.scriptRuntime,
196
213
  }, deps.registerSubAgent),
197
214
  // Workspace semantic search + raw read/grep over the read seam.
198
215
  createWorkspaceSearchTool(pool, embedding),
@@ -219,6 +236,9 @@ export function createConversationAgent(deps) {
219
236
  systemPrompt: composeSystemPrompt(conversationPrompt, { identity: true, conversational: true }),
220
237
  model,
221
238
  tools,
239
+ scriptTools,
240
+ scriptRuntime: deps.scriptRuntime,
241
+ scriptScratch,
222
242
  maxIterations: CONVERSATION_MAX_ITERATIONS,
223
243
  compaction: CONVERSATION_COMPACTION,
224
244
  };
@@ -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";
@@ -40,6 +40,7 @@ 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
42
  import { type MakeSessionPagePublisher, type ResolvePageAsset, type ResolvePageUrl } from "../tools/report-session/index.js";
43
+ import type { ScriptRuntime } from "../tools/script/run-script.js";
43
44
  /** Canonical agent id -- distinct from the id of the old templating agent. */
44
45
  export declare const REPORT_SESSION_AGENT_ID: "report-session";
45
46
  /**
@@ -68,14 +69,8 @@ export type ReportSessionAgentDeps = {
68
69
  readonly resolveWorkspaceRoot: ResolveWorkspaceRoot;
69
70
  /** Append-only version store -- the record tool gates the whole document first, then records one version. */
70
71
  readonly store: ReportVersionStore;
71
- /** Derivation ledger -- the derivation tool appends one record for each derived table. */
72
+ /** Derivation ledger -- `write_derived` appends one record for each derived table. */
72
73
  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
74
  /** Thread store -- the record tool reads the anchor of the report thread, and the preview tool names it. */
80
75
  readonly threads: Pick<ThreadStore, "getThread" | "setAutoTitle">;
81
76
  /** Headless-Chrome config -- the eyes tool opens the rendered page. */
@@ -118,7 +113,7 @@ export type ReportSessionAgentDeps = {
118
113
  readonly resolvePageUrl?: ResolvePageUrl;
119
114
  /**
120
115
  * 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
116
+ * analysis as opaque bytes. The tools that change the draft, preview the page, and
122
117
  * record a version each emit through the session member, thus one bound member carries the whole
123
118
  * session. The preview asks the read member before each render, and the page then carries the
124
119
  * document. An unbound member emits nothing, or gives absence, and every tool acts the same.
@@ -128,6 +123,8 @@ export type ReportSessionAgentDeps = {
128
123
  readonly logger?: Logger;
129
124
  /** Adds `read_tool_output`. The loop of a report turn must keep its texts in the same store. */
130
125
  readonly toolOutputStore?: ToolOutputStore;
126
+ /** Where the VM of `run_script` comes from. A bundled host gives the embedded paths; see `ScriptRuntime`. */
127
+ readonly scriptRuntime?: ScriptRuntime;
131
128
  };
132
129
  /** Build the report `AgentDefinition` with every tool bound to its deps. */
133
130
  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 } = 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 } : {};
@@ -82,16 +87,6 @@ export function createReportSessionAgent(deps) {
82
87
  resolveWorkspaceRoot,
83
88
  logger,
84
89
  }),
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
90
  // The render-and-preview tool. It resolves the page root per call.
96
91
  createPreviewReportTool({
97
92
  gateway,
@@ -126,11 +121,29 @@ export function createReportSessionAgent(deps) {
126
121
  logger,
127
122
  }),
128
123
  ];
124
+ // A derivation of a table of up to the read cap runs in a script, thus it starts no container. The scripts
125
+ // of a session stay beside its page.
126
+ const scriptScratch = createScriptScratch({ resolveWorkspaceRoot, sessionDir: reportSessionDir, provenance, logger });
127
+ // A PDF of the report is a document that a script builds from the content of the session, not a print of the
128
+ // page. Its images come from the directory of the session alone, thus no unpinned file reaches it.
129
+ const scriptTools = [
130
+ ...createReportScriptTools({ gateway, workspaceFs, resolveWorkspaceRoot, derivations, provenance, logger }),
131
+ createReadPdfTool({ workspaceFs, resolveWorkspaceRoot }),
132
+ ...createDocumentTools({
133
+ workspaceFs,
134
+ resolveWorkspaceRoot,
135
+ scratch: scriptScratch,
136
+ imageScope: (session) => (session.scope.threadId === undefined ? undefined : reportSessionDir(session.scope.threadId)),
137
+ }),
138
+ ];
129
139
  return {
130
140
  id: REPORT_SESSION_AGENT_ID,
131
141
  systemPrompt: composeSystemPrompt(reportSessionPrompt, { identity: true, conversational: true }),
132
142
  model,
133
143
  tools,
144
+ scriptTools,
145
+ scriptRuntime: deps.scriptRuntime,
146
+ scriptScratch,
134
147
  maxIterations: REPORT_SESSION_MAX_ITERATIONS,
135
148
  compaction: REPORT_SESSION_COMPACTION,
136
149
  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
  }
@@ -12,7 +12,7 @@
12
12
  * in the tool's `perSource` report; the rest of the call still answers.
13
13
  */
14
14
  /** Closed allowlist of tools any sandbox agent may declare. */
15
- export type SandboxToolName = "listAvailablePackages" | "listAvailableRefs" | "scanInputs" | "resolveLibraryId" | "queryDocs" | "inspectRun" | "pubmed" | "resolve_citation" | "searchGene" | "lookupAnnotation" | "searchInteractions" | "chembl" | "pubchem" | "opentargets" | "geneDiseaseEvidence" | "drugGeneInteractions" | "genePreclinicalProfile" | "searchFaers" | "searchClinicalTrials" | "searchGeoDatasets" | "searchRegulatoryApprovals" | "targetSafety" | "comptox" | "knowledgeTemplate";
15
+ export type SandboxToolName = "listAvailablePackages" | "listAvailableRefs" | "scanInputs" | "resolveLibraryId" | "queryDocs" | "pubmed" | "resolve_citation" | "searchGene" | "lookupAnnotation" | "searchInteractions" | "chembl" | "pubchem" | "opentargets" | "geneDiseaseEvidence" | "drugGeneInteractions" | "genePreclinicalProfile" | "searchFaers" | "searchClinicalTrials" | "searchGeoDatasets" | "searchRegulatoryApprovals" | "targetSafety" | "comptox" | "knowledgeTemplate";
16
16
  /**
17
17
  * The reference datasets that one capability of one agent cannot run without.
18
18
  *
@@ -12,6 +12,8 @@ export type PlannerAgentMeta = {
12
12
  readonly id: string;
13
13
  readonly capabilities: readonly string[];
14
14
  readonly suitableFor: readonly string[];
15
+ /** The public databases that the agent queries at run time, thus a step of the agent needs no staged copy of them. */
16
+ readonly liveSources: readonly string[];
15
17
  };
16
18
  /** The sandbox agents the planner may assign to analysis-plan steps. */
17
19
  export declare const PLANNABLE_AGENT_CATALOG: readonly PlannerAgentMeta[];