@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
@@ -0,0 +1,186 @@
1
+ /**
2
+ * The script tools of a report session: `read_table` over the pinned evidence, and `write_derived`, which pins a
3
+ * derived table to the session.
4
+ *
5
+ * A script reshapes the evidence into the table that a block binds: a join, a pivot, or an aggregate. The script
6
+ * runs in the VM of `run_script`, thus a derivation starts no container. A table over 16 MiB does not derive.
7
+ *
8
+ * `read_table` reads only a member of the served snapshot, and it refuses bytes whose hash is not the pinned hash.
9
+ * `write_derived` keeps the rules of a session derivation: the output lands under the derived directory of the session,
10
+ * each declared input must be a member, a name that the session holds is refused, and the record chains the
11
+ * table to its inputs and to the code of the script. A rerun of a call whose record landed gives that record
12
+ * back, and it emits the derivation again.
13
+ */
14
+ import { join } from "node:path";
15
+ import { ok, err } from "neverthrow";
16
+ import { z } from "zod";
17
+ import { createNoopLogger } from "../../lib/console-logger.js";
18
+ import { computeSha256 } from "../../lib/fs-helpers.js";
19
+ import { bindSessionEmit } from "../../provenance/seam.js";
20
+ import { snapshotEntry } from "../../report-model/reference-resolver.js";
21
+ import { isSafeId, reportSessionDerivedDir } from "../../workspace/paths.js";
22
+ import { defineTool, withToolCallRecord } from "../define-tool.js";
23
+ import { openReportThread } from "../report-authoring/authoring-tools.js";
24
+ import { ReadTableOutputSchema, readTableRows, rowsText } from "../script/file-tools.js";
25
+ import { DERIVED_FILE_CAP_BYTES } from "../script/scratch.js";
26
+ import { confinedWrite } from "../workspace/mutator.js";
27
+ import { syncWrittenFile } from "../../workspace/session-files.js";
28
+ function refused(error) {
29
+ return err({ error, retryable: false });
30
+ }
31
+ const readTableInput = z.object({
32
+ path: z.string().min(1).describe("The path of a pinned artifact or of a derived table of this session, as `list_pinned_artifacts` gives it."),
33
+ columns: z.array(z.string()).min(1).optional().describe("Keep only these columns. Absent: each column."),
34
+ });
35
+ function createReadTableTool(deps) {
36
+ return defineTool({
37
+ id: "read_table",
38
+ description: "Read one table of the pinned evidence as rows: CSV, TSV, JSON (an array of flat objects), or parquet, up to 16 MiB. " +
39
+ "Each row is an object keyed by column, and a cell that holds a plain number reads as a number. " +
40
+ "A larger table does not derive; tell the user which table is absent.",
41
+ inputSchema: readTableInput,
42
+ outputSchema: ReadTableOutputSchema,
43
+ describeCall: ({ path }) => path,
44
+ describeResult: (_input, result) => `${String(result.rowCount)} rows`,
45
+ execute: async ({ path, columns }, ctx) => {
46
+ const opened = await openReportThread(deps.gateway, ctx.session.scope);
47
+ if (opened.isErr())
48
+ return refused(opened.error.detail);
49
+ const entry = snapshotEntry(opened.value.state.snapshot, path);
50
+ if (entry === undefined)
51
+ return refused(`the pinned evidence of this session holds no artifact at ${path}; list_pinned_artifacts gives the paths`);
52
+ const table = await readTableRows({
53
+ workspaceFs: deps.workspaceFs,
54
+ session: ctx.session,
55
+ path,
56
+ columns,
57
+ tooLarge: "a larger table does not derive",
58
+ });
59
+ if (table.isErr())
60
+ return refused(table.error);
61
+ const hash = computeSha256(table.value.source);
62
+ if (hash !== entry.hash)
63
+ return refused(`${path} changed after the session pinned it, thus its bytes are not the evidence of this session`);
64
+ const { rows } = table.value;
65
+ return ok(withToolCallRecord({ path, columns: table.value.columns, rowCount: rows.length, rows }, { reads: [{ path, hash }] }));
66
+ },
67
+ });
68
+ }
69
+ const writeDerivedInput = z.object({
70
+ name: z
71
+ .string()
72
+ .min(1)
73
+ .describe("The file name of the table, with no directory, for example 'yield_by_group.csv'. The extension picks the format: .csv, .tsv, or .json."),
74
+ rows: z
75
+ .array(z.record(z.string(), z.union([z.string(), z.number(), z.boolean(), z.null()])))
76
+ .describe("The rows of the table, each an object keyed by column."),
77
+ inputs: z.array(z.string()).min(1).describe("The path of each pinned table that the script read for this table. The record pins the hash of each one."),
78
+ });
79
+ const DerivedOutputSchema = z.object({ path: z.string(), hash: z.string(), columns: z.array(z.string()).readonly() });
80
+ function derivedOutput(record, size, columns) {
81
+ return withToolCallRecord({ path: record.outputPath, hash: record.outputHash, columns }, { writes: [{ path: record.outputPath, hash: record.outputHash, size }] });
82
+ }
83
+ function createWriteDerivedTool(deps) {
84
+ const logger = (deps.logger ?? createNoopLogger()).named("report-write-derived");
85
+ const observe = bindSessionEmit(deps.provenance, logger);
86
+ const observeDerived = (analysisId, threadId, record) => {
87
+ observe({
88
+ type: "run-derivation",
89
+ analysisId,
90
+ threadId,
91
+ outputPath: record.outputPath,
92
+ outputHash: record.outputHash,
93
+ scriptHash: record.scriptHash,
94
+ sources: record.sources.map((source) => ({ path: source.path, hash: source.hash })),
95
+ });
96
+ };
97
+ return defineTool({
98
+ id: "write_derived",
99
+ description: "Pin one derived table to this session, and give its path for a block to bind. " +
100
+ "Name each pinned table that the script read in `inputs`. A name that this session already holds is refused, thus a second derivation of one table takes a new name.",
101
+ inputSchema: writeDerivedInput,
102
+ outputSchema: DerivedOutputSchema,
103
+ describeCall: ({ name }) => name,
104
+ describeResult: (_input, result) => result.path,
105
+ execute: async ({ name, rows, inputs }, ctx) => {
106
+ if (ctx.script === undefined)
107
+ return refused("write_derived runs inside a script only");
108
+ const opened = await openReportThread(deps.gateway, ctx.session.scope);
109
+ if (opened.isErr())
110
+ return refused(opened.error.detail);
111
+ const { threadId, analysisId, state, derivations } = opened.value;
112
+ const columns = [...new Set(rows.flatMap((row) => Object.keys(row)))];
113
+ // A rerun of a call whose record landed gives that record back. The membership holds the output of
114
+ // the record by now, thus the name test below would refuse the call its own table.
115
+ const recorded = derivations.find((record) => record.toolCallId === ctx.invocationId);
116
+ if (recorded !== undefined) {
117
+ observeDerived(analysisId, threadId, recorded);
118
+ return ok(derivedOutput(recorded, Buffer.byteLength(rowsText(name, rows) ?? "", "utf8"), columns));
119
+ }
120
+ if (!isSafeId(name))
121
+ return refused("the name is one file name, with no directory, no separator, and no traversal");
122
+ const text = rowsText(name, rows);
123
+ if (text === undefined)
124
+ return refused(`${name} names no table format: end the name in .csv, .tsv, or .json`);
125
+ const bytes = Buffer.byteLength(text, "utf8");
126
+ if (bytes > DERIVED_FILE_CAP_BYTES)
127
+ return refused(`the table is ${String(bytes)} bytes, and the cap of a derived table is ${String(DERIVED_FILE_CAP_BYTES)} bytes`);
128
+ const sources = [];
129
+ for (const path of new Set(inputs)) {
130
+ const entry = snapshotEntry(state.snapshot, path);
131
+ if (entry === undefined)
132
+ return refused(`the pinned evidence of this session holds no artifact at ${path}`);
133
+ sources.push({ path, hash: entry.hash });
134
+ }
135
+ const dir = reportSessionDerivedDir(threadId);
136
+ const outputPath = `${dir}/${name}`;
137
+ if (snapshotEntry(state.snapshot, outputPath) !== undefined) {
138
+ return refused(`the evidence of this session already holds ${outputPath}, thus this table takes a new name`);
139
+ }
140
+ const workspaceRoot = await deps.resolveWorkspaceRoot(analysisId);
141
+ const landed = await confinedWrite({
142
+ workspaceRoot,
143
+ analysisId,
144
+ workingDir: join(workspaceRoot, dir),
145
+ path: name,
146
+ content: text,
147
+ invocationId: ctx.invocationId,
148
+ beforeLand: undefined,
149
+ refuseStagedInputs: true,
150
+ });
151
+ if (landed.status !== "ok")
152
+ return refused(`the write of ${outputPath} was refused (${landed.status})`);
153
+ const record = {
154
+ outputPath,
155
+ outputHash: computeSha256(landed.bytes),
156
+ sources,
157
+ scriptHash: computeSha256(Buffer.from(ctx.script.code, "utf8")),
158
+ script: ctx.script.code,
159
+ toolCallId: ctx.invocationId,
160
+ };
161
+ const appended = await deps.derivations.appendDerivation(threadId, record);
162
+ if (appended.isErr()) {
163
+ logger.error("the derivation record did not land", { threadId, ...logger.errorFields(appended.error.cause) });
164
+ return err({ error: "the derivation record did not land, thus the table is not evidence of this session", retryable: true });
165
+ }
166
+ if (appended.value === "duplicate")
167
+ return refused(`another turn recorded ${outputPath} first, thus this table takes a new name`);
168
+ if (appended.value === "absent")
169
+ return refused("no report session state row exists to hold the derivation");
170
+ observeDerived(analysisId, threadId, record);
171
+ await syncWrittenFile(deps.syncSessionFile, logger, {
172
+ session: ctx.session,
173
+ owner: { kind: "thread", threadId },
174
+ path: outputPath,
175
+ hash: record.outputHash,
176
+ size: landed.bytes.length,
177
+ content: landed.bytes,
178
+ });
179
+ return ok(derivedOutput(record, landed.bytes.length, columns));
180
+ },
181
+ });
182
+ }
183
+ /** The script tools of a report session. */
184
+ export function createReportScriptTools(deps) {
185
+ return [createReadTableTool(deps), createWriteDerivedTool(deps)];
186
+ }
@@ -1,13 +1,11 @@
1
1
  /**
2
2
  * generateAnalogyReport — cross-domain analogy report as a sub-agent tool.
3
3
  *
4
- * An inline harness `AgentDefinition` (mirroring `createLiteratureReviewerTool`)
5
- * driven by `runWithSalvage`. The result reaches the wrapper through one of two
6
- * submit tools because the output envelope is a union and `defineTool` takes
7
- * only a top-level object. Neither tool ends the run: the wrapper selects the
8
- * last accepted report from the recorded calls, else the first blocker. A run
9
- * with no outcome gets one salvage request, then an `extraction-failed`
10
- * envelope, so the frontend never renders raw prose.
4
+ * An inline harness `AgentDefinition` driven by `runWithSalvage`. The reasoner records its report in parts: the
5
+ * problem with its planned analogies, then each analogy when its search is done. A provider filter can stop a reply in
6
+ * the middle of a call, and a cut call is lost whole, thus a small part loses little. The wrapper assembles the report
7
+ * from the last accepted record of each part, else it gives the first blocker. A run with no outcome gets one salvage
8
+ * request, then an `extraction-failed` envelope, so the frontend never renders raw prose.
11
9
  */
12
10
  import { z } from "zod";
13
11
  import { type AnalogyReport } from "@inflexa-ai/harness/contracts/analogy-report.js";
@@ -15,12 +13,17 @@ import type { Logger } from "../../lib/logger.js";
15
13
  import { type LoopStep } from "../../loop/run-loop.js";
16
14
  import type { SubAgentRegistrar } from "../../loop/sub-agent.js";
17
15
  import type { ToolOutputStore } from "../../loop/tool-output.js";
16
+ import type { AgentDefinition } from "../../loop/types.js";
17
+ import type { ScriptRuntime } from "../script/run-script.js";
18
18
  import type { ChatProvider } from "../../providers/types.js";
19
19
  import type { UsageRecorder } from "../../billing/usage-recorder.js";
20
20
  import { type Tool } from "../define-tool.js";
21
21
  import { type BioToolKeys } from "../bio/keys.js";
22
- /** The last report of a run that its call accepted. The input schema of the tool checked it. */
23
- export declare function acceptedReportOf(steps: readonly LoopStep[]): AnalogyReport | undefined;
22
+ /**
23
+ * The report that a run recorded, or nothing when it recorded no problem. The analogies keep the planned order, and a
24
+ * recorded analogy that the plan does not name follows them. A planned analogy with no record is not loaded.
25
+ */
26
+ export declare function assembledReportOf(steps: readonly LoopStep[]): AnalogyReport | undefined;
24
27
  export declare const generateAnalogyReportInputSchema: z.ZodObject<{
25
28
  problem: z.ZodString;
26
29
  context: z.ZodOptional<z.ZodString>;
@@ -49,6 +52,10 @@ export type GenerateAnalogyReportDeps = {
49
52
  readonly toolOutputStore?: ToolOutputStore;
50
53
  /** Logging seam; omitted falls back to no-op. Logs the salvage warning and an accounting error of the reasoner loop. */
51
54
  readonly logger?: Logger;
55
+ /** Where the VM of `run_script` comes from. See {@link ScriptRuntime}. */
56
+ readonly scriptRuntime?: ScriptRuntime;
52
57
  };
53
58
  /** Build the `generate_analogy_report` delegation tool bound to its provider. */
59
+ /** The analogical reasoner: its searches run in a script, and it records its report and a blocker directly. */
60
+ export declare function analogyAgentOf(deps: Pick<GenerateAnalogyReportDeps, "bioKeys" | "model" | "toolOutputStore" | "scriptRuntime">): AgentDefinition;
54
61
  export declare function createGenerateAnalogyReportTool(deps: GenerateAnalogyReportDeps, registerSubAgent: SubAgentRegistrar): Tool;
@@ -1,19 +1,17 @@
1
1
  /**
2
2
  * generateAnalogyReport — cross-domain analogy report as a sub-agent tool.
3
3
  *
4
- * An inline harness `AgentDefinition` (mirroring `createLiteratureReviewerTool`)
5
- * driven by `runWithSalvage`. The result reaches the wrapper through one of two
6
- * submit tools because the output envelope is a union and `defineTool` takes
7
- * only a top-level object. Neither tool ends the run: the wrapper selects the
8
- * last accepted report from the recorded calls, else the first blocker. A run
9
- * with no outcome gets one salvage request, then an `extraction-failed`
10
- * envelope, so the frontend never renders raw prose.
4
+ * An inline harness `AgentDefinition` driven by `runWithSalvage`. The reasoner records its report in parts: the
5
+ * problem with its planned analogies, then each analogy when its search is done. A provider filter can stop a reply in
6
+ * the middle of a call, and a cut call is lost whole, thus a small part loses little. The wrapper assembles the report
7
+ * from the last accepted record of each part, else it gives the first blocker. A run with no outcome gets one salvage
8
+ * request, then an `extraction-failed` envelope, so the frontend never renders raw prose.
11
9
  */
12
10
  import { errorMessageOf } from "../../lib/thrown.js";
13
11
  import { textOf } from "../../lib/unknown-text.js";
14
12
  import { ok } from "neverthrow";
15
13
  import { z } from "zod";
16
- import { AnalogyReportSchema } from "@inflexa-ai/harness/contracts/analogy-report.js";
14
+ import { AnalogyProblemSchema, AnalogySchema, } from "@inflexa-ai/harness/contracts/analogy-report.js";
17
15
  import { analogicalReasonerPrompt } from "../../prompts/analogical-reasoner.js";
18
16
  import { composeSystemPrompt } from "../../agents/system-prompt.js";
19
17
  import { forSubAgent } from "../../auth/types.js";
@@ -33,34 +31,80 @@ import { createNcbiTools } from "../bio/keys.js";
33
31
  const AGENT_ID = "analogical-reasoner";
34
32
  /** Tool-call budget for the inner research agent. */
35
33
  const RESEARCH_MAX_ITERATIONS = 40;
36
- const ANALOGY_SALVAGE_NUDGE = "You ended without a terminal outcome. Call submit_analogy_report with " +
37
- "your report now, or report_blocker if phase 1 cannot run. Do not reply " +
38
- "with prose.";
39
- const SUBMIT_ANALOGY_REPORT_TOOL_ID = "submit_analogy_report";
40
- function createSubmitAnalogyReportTool() {
34
+ const ANALOGY_SALVAGE_NUDGE = "You ended without a terminal outcome. Record the problem with record_problem and each analogy with " +
35
+ "record_analogy now, or call report_blocker if phase 1 cannot run. Do not reply with prose.";
36
+ const RECORD_PROBLEM_TOOL_ID = "record_problem";
37
+ const RECORD_ANALOGY_TOOL_ID = "record_analogy";
38
+ const RecordProblemInputSchema = AnalogyProblemSchema.extend({
39
+ plannedAnalogies: z
40
+ .array(z.object({ analogyTitle: z.string(), targetDomain: z.string() }))
41
+ .min(1)
42
+ .describe("Each analogy of phase 1. An analogy that gets no record_analogy call arrives in the report as not loaded."),
43
+ });
44
+ function createRecordProblemTool() {
41
45
  return defineTool({
42
- id: SUBMIT_ANALOGY_REPORT_TOOL_ID,
43
- description: "Submit the analogy report: the problem summary, objects, " +
44
- "relations, and key terms of phase 1, and each analogy with its " +
45
- "coverage and its cited solutions from phase 2. Call it one time with " +
46
- "the full report. If it rejects the report, correct the fields that it " +
47
- "names and call it again. The last accepted report is the result.",
48
- inputSchema: AnalogyReportSchema,
46
+ id: RECORD_PROBLEM_TOOL_ID,
47
+ description: "Record the extraction of phase 1: the problem summary, objects, relations, key terms, and the analogies " +
48
+ "that phase 2 searches. A later call replaces the record.",
49
+ inputSchema: RecordProblemInputSchema,
49
50
  describeCall: "none",
50
- execute: () => Promise.resolve(ok({ recorded: true, message: "Report recorded. Stop now." })),
51
+ execute: () => Promise.resolve(ok({ recorded: true })),
52
+ });
53
+ }
54
+ function createRecordAnalogyTool() {
55
+ return defineTool({
56
+ id: RECORD_ANALOGY_TOOL_ID,
57
+ description: "Record one analogy with its coverage and its cited solutions, when its search is done. A later call " +
58
+ "with the same analogyTitle replaces the record.",
59
+ inputSchema: AnalogySchema,
60
+ describeCall: ({ analogyTitle }) => analogyTitle,
61
+ execute: () => Promise.resolve(ok({ recorded: true })),
51
62
  });
52
63
  }
53
- /** The last report of a run that its call accepted. The input schema of the tool checked it. */
54
- export function acceptedReportOf(steps) {
55
- let accepted;
64
+ /** The parts that a run recorded: the last accepted problem, and the last accepted record of each analogy title. */
65
+ function recordedParts(steps) {
66
+ let problem;
67
+ const analogies = new Map();
56
68
  for (const step of steps) {
57
69
  for (const call of step.calls) {
58
- if (call.toolName !== SUBMIT_ANALOGY_REPORT_TOOL_ID || call.output?.outcome !== "ok")
70
+ if (call.output?.outcome !== "ok")
59
71
  continue;
60
- accepted = recordedInputOf(AnalogyReportSchema, call.input) ?? accepted;
72
+ if (call.toolName === RECORD_PROBLEM_TOOL_ID)
73
+ problem = recordedInputOf(RecordProblemInputSchema, call.input) ?? problem;
74
+ if (call.toolName === RECORD_ANALOGY_TOOL_ID) {
75
+ const analogy = recordedInputOf(AnalogySchema, call.input);
76
+ if (analogy !== undefined)
77
+ analogies.set(analogy.analogyTitle, analogy);
78
+ }
61
79
  }
62
80
  }
63
- return accepted;
81
+ return { problem, analogies };
82
+ }
83
+ /**
84
+ * The report that a run recorded, or nothing when it recorded no problem. The analogies keep the planned order, and a
85
+ * recorded analogy that the plan does not name follows them. A planned analogy with no record is not loaded.
86
+ */
87
+ export function assembledReportOf(steps) {
88
+ const { problem, analogies } = recordedParts(steps);
89
+ if (problem === undefined)
90
+ return undefined;
91
+ const { plannedAnalogies, ...extraction } = problem;
92
+ const planned = plannedAnalogies.map((plan) => analogies.get(plan.analogyTitle) ?? {
93
+ targetDomain: plan.targetDomain,
94
+ analogyTitle: plan.analogyTitle,
95
+ objectMappings: [],
96
+ sharedRelations: "",
97
+ coverage: "not_loaded",
98
+ solutions: [],
99
+ });
100
+ const titles = new Set(plannedAnalogies.map((plan) => plan.analogyTitle));
101
+ const extra = [...analogies.values()].filter((analogy) => !titles.has(analogy.analogyTitle));
102
+ return { schemaVersion: "1", ...extraction, analogies: [...planned, ...extra] };
103
+ }
104
+ /** The run recorded the problem and each analogy that it planned, thus nothing is left to record. */
105
+ function everyPartRecorded(steps) {
106
+ const { problem, analogies } = recordedParts(steps);
107
+ return problem !== undefined && problem.plannedAnalogies.every((plan) => analogies.has(plan.analogyTitle));
64
108
  }
65
109
  function createAnalogyBlockerTool() {
66
110
  return createReportBlockerToolFor({
@@ -124,29 +168,35 @@ export function buildResearchPrompt(input) {
124
168
  return lines.join("\n");
125
169
  }
126
170
  /** Build the `generate_analogy_report` delegation tool bound to its provider. */
171
+ /** The analogical reasoner: its searches run in a script, and it records its report and a blocker directly. */
172
+ export function analogyAgentOf(deps) {
173
+ const ncbi = createNcbiTools(deps.bioKeys);
174
+ return {
175
+ id: AGENT_ID,
176
+ systemPrompt: composeSystemPrompt(analogicalReasonerPrompt),
177
+ model: deps.model,
178
+ // The order and the definitions of the tools stay fixed across calls so the cache-keyed prefix holds.
179
+ tools: [
180
+ ...(deps.toolOutputStore ? [createReadToolOutputTool(deps.toolOutputStore)] : []),
181
+ createRecordProblemTool(),
182
+ createRecordAnalogyTool(),
183
+ createAnalogyBlockerTool(),
184
+ ],
185
+ scriptTools: [
186
+ createSearchSemanticScholarTool({ apiKey: deps.bioKeys.semanticScholar }),
187
+ searchArxivTool,
188
+ createSearchGithubReposTool({ githubToken: deps.bioKeys.github }),
189
+ ncbi.pubmed,
190
+ ],
191
+ scriptRuntime: deps.scriptRuntime,
192
+ maxIterations: RESEARCH_MAX_ITERATIONS,
193
+ };
194
+ }
127
195
  export function createGenerateAnalogyReportTool(deps, registerSubAgent) {
128
196
  const logger = (deps.logger ?? createNoopLogger()).named("generate-analogy-report");
129
- const ncbi = createNcbiTools(deps.bioKeys);
130
- const searchTools = [
131
- createSearchSemanticScholarTool({ apiKey: deps.bioKeys.semanticScholar }),
132
- searchArxivTool,
133
- createSearchGithubReposTool({ githubToken: deps.bioKeys.github }),
134
- ncbi.pubmed,
135
- ...(deps.toolOutputStore ? [createReadToolOutputTool(deps.toolOutputStore)] : []),
136
- ];
137
- const systemPrompt = composeSystemPrompt(analogicalReasonerPrompt);
138
197
  const research = registerSubAgent(AGENT_ID, async ({ payload: input, ctx, loop }) => {
139
198
  const childSession = forSubAgent(ctx.session, AGENT_ID);
140
- // The order and the definitions of the tools stay fixed across calls so the cache-keyed prefix holds.
141
- const submitReportTool = createSubmitAnalogyReportTool();
142
- const blockerTool = createAnalogyBlockerTool();
143
- const agent = {
144
- id: AGENT_ID,
145
- systemPrompt,
146
- model: deps.model,
147
- tools: [...searchTools, submitReportTool, blockerTool],
148
- maxIterations: RESEARCH_MAX_ITERATIONS,
149
- };
199
+ const agent = analogyAgentOf(deps);
150
200
  let steps;
151
201
  try {
152
202
  const run = await runWithSalvage(agent, [{ role: "user", content: buildResearchPrompt(input) }], childSession, {
@@ -155,10 +205,12 @@ export function createGenerateAnalogyReportTool(deps, registerSubAgent) {
155
205
  logger,
156
206
  usageRecorder: deps.usageRecorder,
157
207
  toolOutputStore: deps.toolOutputStore,
208
+ // The run ends when each planned analogy has a record, because the wrapper reads no prose.
209
+ stopWhen: everyPartRecorded,
158
210
  }, {
159
- tools: [SUBMIT_ANALOGY_REPORT_TOOL_ID, REPORT_BLOCKER_TOOL_ID],
211
+ tools: [RECORD_PROBLEM_TOOL_ID, RECORD_ANALOGY_TOOL_ID, REPORT_BLOCKER_TOOL_ID],
160
212
  nudge: ANALOGY_SALVAGE_NUDGE,
161
- hasOutcome: (done) => acceptedReportOf(done) !== undefined || blockerOf(done) !== null,
213
+ hasOutcome: (done) => assembledReportOf(done) !== undefined || blockerOf(done) !== null,
162
214
  });
163
215
  steps = run.steps;
164
216
  }
@@ -173,12 +225,22 @@ export function createGenerateAnalogyReportTool(deps, registerSubAgent) {
173
225
  };
174
226
  }
175
227
  // A report wins over a blocker.
176
- const report = acceptedReportOf(steps);
228
+ const report = assembledReportOf(steps);
177
229
  if (report !== undefined)
178
230
  return report;
179
231
  const blocker = blockerOf(steps);
180
232
  if (blocker !== null)
181
233
  return { schemaVersion: "1", error: { kind: "extraction-failed", message: blocker.reason } };
234
+ if (steps.some((step) => step.finishReason === "content-filter")) {
235
+ logger.warn("the content filter of the provider stopped the analogical reasoner", { steps: steps.length });
236
+ return {
237
+ schemaVersion: "1",
238
+ error: {
239
+ kind: "extraction-failed",
240
+ message: "The content filter of the model provider stopped the reasoner before it recorded the problem. A different wording of the problem can pass the filter.",
241
+ },
242
+ };
243
+ }
182
244
  return buildExtractionFailedEnvelope();
183
245
  });
184
246
  return defineTool({
@@ -22,17 +22,94 @@
22
22
  * last accepted plan, else the last clarification, else the first blocker —
23
23
  * and persists an accepted plan under an id that a rerun of the call gives again.
24
24
  */
25
+ import type { ResolveWorkspaceRoot } from "../../workspace/paths.js";
26
+ import type { ScriptRuntime } from "../script/run-script.js";
27
+ import { type ReadSurfaceDeps } from "../workspace/read-surface.js";
25
28
  import type { Pool } from "pg";
29
+ import { z } from "zod";
26
30
  import type { SubAgentRegistrar } from "../../loop/sub-agent.js";
27
31
  import type { ToolOutputStore } from "../../loop/tool-output.js";
32
+ import type { AgentDefinition } from "../../loop/types.js";
28
33
  import { type ChatProvider } from "../../providers/types.js";
29
34
  import type { UsageRecorder } from "../../billing/usage-recorder.js";
30
35
  import { type Tool } from "../define-tool.js";
31
36
  import type { EnvironmentStorePaths } from "../../config/environment-stores.js";
32
37
  import { type BioToolKeys } from "../bio/keys.js";
33
38
  import { type KnowledgeClient, type KnowledgeRecommendAnswer } from "../knowledge/index.js";
34
- import { type ResourcePolicy } from "../../config/resource-limits.js";
35
39
  import type { Logger } from "../../lib/logger.js";
40
+ import { type DataProfileResult } from "../../state/index.js";
41
+ /** The planner system prompt is deterministic for the process: the catalog is fixed at load. */
42
+ export declare function plannerInstructions(): string;
43
+ /**
44
+ * Format a loaded prior plan as a markdown block the planner can read —
45
+ * narrative + one line per step. Used only when `parentPlanId` is set.
46
+ */
47
+ export declare function formatPriorPlan(parentPlanId: string, plan: unknown): string | null;
48
+ /**
49
+ * What the planner actually holds about the dataset.
50
+ *
51
+ * Every lifecycle state is a variant because every one of them is ordinary: an
52
+ * analysis can be planned before it has been profiled, while profiling is in
53
+ * flight, after an attempt failed, and against a profile whose input files have
54
+ * since changed. None of those is an error, and none of them may stop a plan
55
+ * being produced — a planner told which state it is in can plan accordingly; a
56
+ * planner told nothing plans on facts it cannot know are rotten.
57
+ */
58
+ export type DataGrounding = {
59
+ kind: "ready";
60
+ result: DataProfileResult;
61
+ }
62
+ /** A profile IS available, but may not describe the analysis's current inputs. */
63
+ | {
64
+ kind: "provisional";
65
+ result: DataProfileResult;
66
+ reasons: readonly string[];
67
+ }
68
+ /** Profiling is in flight and no earlier profile exists. */
69
+ | {
70
+ kind: "pending";
71
+ }
72
+ /** Profiling failed and no earlier profile exists. */
73
+ | {
74
+ kind: "failed";
75
+ error: string | null;
76
+ }
77
+ /** Never profiled, cleared, or nothing to profile. */
78
+ | {
79
+ kind: "absent";
80
+ };
81
+ /**
82
+ * Render the seed's `## Data Context` from the grounding.
83
+ *
84
+ * The facts come from `buildDataProfileOrientation` — the same bounded projector
85
+ * the sandbox step seed uses, so a step and the plan that produced it are oriented
86
+ * by identical text. It is not re-derived here, and the planner cannot reach past
87
+ * it: the planner has no `inspect_data_profile` tool, so what this section does not
88
+ * say is simply not known to it.
89
+ *
90
+ * An absent profile renders NOTHING — no heading, no apology. Planning without
91
+ * dataset facts is worse-grounded but entirely possible, and a section announcing
92
+ * its own emptiness is an invitation to fill it. Every other state renders,
93
+ * including the two that carry no facts either: a planner told that its record is
94
+ * still being built, or that its facts may have moved, can act on that; one handed
95
+ * stale facts silently cannot.
96
+ *
97
+ * Pure — the same grounding renders the same string.
98
+ */
99
+ export declare function renderDataContext(grounding: DataGrounding, analysisId: string): string;
100
+ /** The planner agent over its search tools and its terminal tools. */
101
+ export declare function plannerAgentOf(deps: GeneratePlanDeps, searchTools: readonly Tool[], terminal: readonly Tool[]): AgentDefinition;
102
+ /** The terminal tools of a planner with no knowledge answer, for a consumer that runs the planner outside this tool. */
103
+ export declare function plannerTerminalTools(logger: Logger): Tool[];
104
+ /**
105
+ * Render one environment inventory into the seed, reporting a lookup that failed.
106
+ *
107
+ * A failed lookup is written INTO the prompt as prose the planner then plans
108
+ * around — a silent degradation of its grounding that no caller is told about and
109
+ * no outcome distinguishes. The record is what makes "the planner produced a plan
110
+ * naming packages this install does not have" traceable to its cause.
111
+ */
112
+ export declare function inventoryContent(label: string, result: Awaited<ReturnType<Tool["execute"]>>, logger: Logger): string;
36
113
  export type GeneratePlanDeps = EnvironmentStorePaths & {
37
114
  /**
38
115
  * The conversation-role backend the planner is pinned to. Keeping the
@@ -45,12 +122,6 @@ export type GeneratePlanDeps = EnvironmentStorePaths & {
45
122
  };
46
123
  /** Database pool — plan persistence and prior-plan loading. */
47
124
  readonly pool: Pool;
48
- /**
49
- * Host resource policy — stated to the planner as concrete per-step
50
- * ceilings and enforced by `submit_plan`. Absent, the prompt keeps its
51
- * default guidance and validation skips the ceiling check.
52
- */
53
- readonly resourcePolicy?: ResourcePolicy;
54
125
  /** Diagnostic sink. Absent, the tool runs silently — see `LoopOptions.logger`. */
55
126
  readonly logger?: Logger;
56
127
  /** LLM usage-accounting seam for the planner loop; omitted falls back to the no-op recorder. */
@@ -59,6 +130,12 @@ export type GeneratePlanDeps = EnvironmentStorePaths & {
59
130
  readonly toolOutputStore?: ToolOutputStore;
60
131
  /** API keys for the search tools the planner uses to ground a plan. */
61
132
  readonly bioKeys: BioToolKeys;
133
+ /** The read-only workspace: the file tools, the search, the data profile, and the script reads of a PDF and a table. */
134
+ readonly workspace: ReadSurfaceDeps & {
135
+ readonly resolveWorkspaceRoot: ResolveWorkspaceRoot;
136
+ };
137
+ /** Where the VM of `run_script` comes from, for the script reads. See `ScriptRuntime`. */
138
+ readonly scriptRuntime?: ScriptRuntime;
62
139
  /**
63
140
  * The knowledge service client. Bound, the planner gains `knowledge_recommend`
64
141
  * and `knowledge_check`. Absent, no knowledge tool attaches and no
@@ -82,5 +159,23 @@ export type GeneratePlanDeps = EnvironmentStorePaths & {
82
159
  export declare function buildPlannerSearchTools(deps: GeneratePlanDeps, hooks?: {
83
160
  readonly onRecommend?: (answer: KnowledgeRecommendAnswer) => void;
84
161
  }): Tool[];
162
+ /**
163
+ * The seed of the planner: the prior plan when the call revises one, the data context, the two censuses, and then
164
+ * what the caller gave. Each block is optional where the call holds none.
165
+ */
166
+ export declare function composePlannerSeed(parts: {
167
+ readonly priorPlanBlock: string | null;
168
+ readonly dataContextBlock: string;
169
+ readonly groundingBlock: string;
170
+ readonly input: Pick<z.infer<typeof generatePlanInputSchema>, "researchQuestion" | "analystNotes" | "priorRuns" | "userConstraints">;
171
+ }): string;
172
+ declare const generatePlanInputSchema: z.ZodObject<{
173
+ researchQuestion: z.ZodString;
174
+ analystNotes: z.ZodOptional<z.ZodString>;
175
+ priorRuns: z.ZodOptional<z.ZodString>;
176
+ userConstraints: z.ZodOptional<z.ZodString>;
177
+ parentPlanId: z.ZodOptional<z.ZodString>;
178
+ }, z.core.$strip>;
85
179
  /** Build the `generate_plan` tool bound to its provider and pool. */
86
180
  export declare function createGeneratePlanTool(deps: GeneratePlanDeps, registerSubAgent: SubAgentRegistrar): Tool;
181
+ export {};