@inflexa-ai/harness 0.1.1 → 0.3.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 (330) hide show
  1. package/README.md +48 -0
  2. package/dist/agents/conversation-agent.d.ts +15 -11
  3. package/dist/agents/conversation-agent.js +31 -34
  4. package/dist/agents/report-builder.js +2 -1
  5. package/dist/agents/sandbox/bulk-transcriptomics-agent.js +1 -1
  6. package/dist/agents/sandbox/cheminformatics-agent.js +1 -19
  7. package/dist/agents/sandbox/chromatin-agent.js +1 -10
  8. package/dist/agents/sandbox/dna-methylation-agent.js +1 -10
  9. package/dist/agents/sandbox/drug-repurposing-agent.js +4 -14
  10. package/dist/agents/sandbox/enrichment-agent.js +1 -10
  11. package/dist/agents/sandbox/ephemeral-executor.js +1 -10
  12. package/dist/agents/sandbox/genomic-variant-agent.js +1 -3
  13. package/dist/agents/sandbox/immune-profiling-agent.js +1 -13
  14. package/dist/agents/sandbox/metabolomics-agent.js +1 -1
  15. package/dist/agents/sandbox/microbiome-agent.js +1 -1
  16. package/dist/agents/sandbox/multi-omics-integration-agent.js +1 -12
  17. package/dist/agents/sandbox/multimodal-sc-agent.js +1 -1
  18. package/dist/agents/sandbox/network-agent.js +1 -10
  19. package/dist/agents/sandbox/pkpd-clinical-response-agent.js +2 -8
  20. package/dist/agents/sandbox/proteomics-agent.js +1 -1
  21. package/dist/agents/sandbox/scientific-executor.js +1 -10
  22. package/dist/agents/sandbox/shared.d.ts +20 -6
  23. package/dist/agents/sandbox/shared.js +50 -39
  24. package/dist/agents/sandbox/single-cell-agent.js +1 -1
  25. package/dist/agents/sandbox/spatial-omics-agent.js +1 -1
  26. package/dist/agents/sandbox/statistical-modeling-agent.js +1 -1
  27. package/dist/agents/sandbox/translational-safety-agent.js +3 -9
  28. package/dist/agents/sandbox/types.d.ts +1 -1
  29. package/dist/agents/system-prompt.d.ts +37 -11
  30. package/dist/agents/system-prompt.js +35 -12
  31. package/dist/app/chat-turn.d.ts +3 -0
  32. package/dist/app/chat-turn.js +3 -1
  33. package/dist/app/data-profile-orientation.d.ts +34 -0
  34. package/dist/app/data-profile-orientation.js +117 -0
  35. package/dist/app/synthesize-run.d.ts +3 -0
  36. package/dist/app/synthesize-run.js +3 -1
  37. package/dist/contracts/schemas/chat-parts.d.ts +9 -9
  38. package/dist/execution/artifact-metadata.d.ts +3 -0
  39. package/dist/execution/artifact-metadata.js +10 -3
  40. package/dist/execution/artifact-registration.d.ts +2 -1
  41. package/dist/execution/artifact-registration.js +8 -2
  42. package/dist/execution/ephemeral-runner.d.ts +16 -0
  43. package/dist/execution/ephemeral-runner.js +31 -5
  44. package/dist/execution/post-step-pipeline.d.ts +3 -0
  45. package/dist/execution/post-step-pipeline.js +24 -4
  46. package/dist/execution/reconcile-manifest.d.ts +7 -0
  47. package/dist/execution/reconcile-manifest.js +19 -5
  48. package/dist/execution/report-runner.d.ts +3 -0
  49. package/dist/execution/report-runner.js +4 -4
  50. package/dist/execution/run-synthesis.js +20 -19
  51. package/dist/execution/step-summary.d.ts +3 -0
  52. package/dist/execution/step-summary.js +6 -3
  53. package/dist/index.d.ts +9 -2
  54. package/dist/index.js +24 -5
  55. package/dist/lib/chrome.d.ts +4 -1
  56. package/dist/lib/chrome.js +10 -7
  57. package/dist/lib/console-logger.d.ts +19 -0
  58. package/dist/lib/console-logger.js +62 -0
  59. package/dist/lib/logger.d.ts +88 -0
  60. package/dist/lib/logger.js +37 -0
  61. package/dist/lib/otel.d.ts +2 -1
  62. package/dist/lib/otel.js +8 -3
  63. package/dist/loop/__fixtures__/scripted-provider.d.ts +2 -2
  64. package/dist/loop/__fixtures__/scripted-provider.js +2 -1
  65. package/dist/loop/metrics.d.ts +33 -1
  66. package/dist/loop/metrics.js +56 -2
  67. package/dist/loop/run-agent.d.ts +13 -1
  68. package/dist/loop/run-agent.js +19 -4
  69. package/dist/memory/card-builders.d.ts +8 -1
  70. package/dist/memory/card-builders.js +20 -3
  71. package/dist/memory/reconstruct-cards.js +4 -3
  72. package/dist/memory/thread-history.d.ts +27 -2
  73. package/dist/memory/thread-history.js +45 -5
  74. package/dist/memory/working-memory.d.ts +93 -10
  75. package/dist/memory/working-memory.js +220 -84
  76. package/dist/prompts/SOUL.d.ts +22 -9
  77. package/dist/prompts/SOUL.js +56 -37
  78. package/dist/prompts/analogical-reasoner.d.ts +1 -1
  79. package/dist/prompts/analogical-reasoner.js +2 -2
  80. package/dist/prompts/briefing.d.ts +100 -0
  81. package/dist/prompts/briefing.js +166 -0
  82. package/dist/prompts/conversation.d.ts +1 -1
  83. package/dist/prompts/conversation.js +94 -440
  84. package/dist/prompts/literature-reviewer.d.ts +1 -1
  85. package/dist/prompts/literature-reviewer.js +10 -10
  86. package/dist/prompts/planner.js +28 -59
  87. package/dist/prompts/report-builder.d.ts +1 -1
  88. package/dist/prompts/report-builder.js +71 -24
  89. package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
  90. package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +2 -7
  91. package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
  92. package/dist/prompts/sandbox/cheminformatics-agent.js +11 -15
  93. package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
  94. package/dist/prompts/sandbox/chromatin-agent.js +3 -7
  95. package/dist/prompts/sandbox/data-profiler.d.ts +1 -1
  96. package/dist/prompts/sandbox/data-profiler.js +13 -8
  97. package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
  98. package/dist/prompts/sandbox/dna-methylation-agent.js +2 -7
  99. package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
  100. package/dist/prompts/sandbox/drug-repurposing-agent.js +15 -16
  101. package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
  102. package/dist/prompts/sandbox/enrichment-agent.js +2 -9
  103. package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
  104. package/dist/prompts/sandbox/genomic-variant-agent.js +2 -6
  105. package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
  106. package/dist/prompts/sandbox/immune-profiling-agent.js +3 -8
  107. package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
  108. package/dist/prompts/sandbox/metabolomics-agent.js +1 -7
  109. package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
  110. package/dist/prompts/sandbox/microbiome-agent.js +2 -7
  111. package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
  112. package/dist/prompts/sandbox/multi-omics-integration-agent.js +2 -8
  113. package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
  114. package/dist/prompts/sandbox/multimodal-sc-agent.js +2 -7
  115. package/dist/prompts/sandbox/network-agent.d.ts +1 -1
  116. package/dist/prompts/sandbox/network-agent.js +2 -7
  117. package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
  118. package/dist/prompts/sandbox/proteomics-agent.js +2 -9
  119. package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
  120. package/dist/prompts/sandbox/single-cell-agent.js +2 -9
  121. package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
  122. package/dist/prompts/sandbox/spatial-omics-agent.js +3 -6
  123. package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
  124. package/dist/prompts/sandbox/statistical-modeling-agent.js +2 -7
  125. package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
  126. package/dist/prompts/sandbox/translational-safety-agent.js +13 -14
  127. package/dist/prompts/sandbox-standards.d.ts +22 -14
  128. package/dist/prompts/sandbox-standards.js +91 -71
  129. package/dist/prompts/synthesis-agent.d.ts +1 -1
  130. package/dist/prompts/synthesis-agent.js +33 -131
  131. package/dist/provenance/collector.js +1 -1
  132. package/dist/providers/ai-sdk.js +31 -4
  133. package/dist/providers/prompt-cache.d.ts +74 -0
  134. package/dist/providers/prompt-cache.js +77 -0
  135. package/dist/providers/types.d.ts +33 -0
  136. package/dist/reference-data/catalog.d.ts +131 -0
  137. package/dist/reference-data/catalog.js +348 -0
  138. package/dist/reference-data/receipt.d.ts +41 -0
  139. package/dist/reference-data/receipt.js +40 -0
  140. package/dist/runtime/boot.d.ts +2 -2
  141. package/dist/runtime/boot.js +1 -1
  142. package/dist/runtime/connection-budget.d.ts +2 -2
  143. package/dist/runtime/connection-budget.js +5 -3
  144. package/dist/runtime/dbos.d.ts +7 -7
  145. package/dist/runtime/dbos.js +14 -11
  146. package/dist/runtime/shutdown.d.ts +2 -2
  147. package/dist/runtime/shutdown.js +4 -3
  148. package/dist/sandbox/create-sandbox.d.ts +2 -2
  149. package/dist/sandbox/docker-client.d.ts +2 -2
  150. package/dist/sandbox/docker-client.js +4 -1
  151. package/dist/sandbox/k8s-client.d.ts +3 -0
  152. package/dist/sandbox/k8s-client.js +5 -4
  153. package/dist/sandbox/notification-sweep.d.ts +3 -3
  154. package/dist/sandbox/notification-sweep.js +4 -2
  155. package/dist/sandbox/reaper.d.ts +3 -3
  156. package/dist/sandbox/reaper.js +7 -4
  157. package/dist/sandbox/watchdog.d.ts +3 -3
  158. package/dist/sandbox/watchdog.js +8 -2
  159. package/dist/schemas/data-profile-schemas.js +5 -3
  160. package/dist/schemas/plan-schemas.d.ts +21 -21
  161. package/dist/schemas/plan-schemas.js +9 -3
  162. package/dist/schemas/run-synthesis.d.ts +8 -4
  163. package/dist/schemas/run-synthesis.js +92 -56
  164. package/dist/schemas/validate-plan.js +1 -1
  165. package/dist/schemas/workflow-state.d.ts +7 -1
  166. package/dist/schemas/workflow-state.js +33 -19
  167. package/dist/state/artifacts.d.ts +13 -0
  168. package/dist/state/artifacts.js +27 -0
  169. package/dist/state/data-profile.d.ts +64 -5
  170. package/dist/state/index.d.ts +3 -3
  171. package/dist/state/index.js +1 -1
  172. package/dist/state/init.d.ts +2 -1
  173. package/dist/state/init.js +5 -6
  174. package/dist/tasks/data-profile.d.ts +25 -2
  175. package/dist/tasks/data-profile.js +95 -39
  176. package/dist/tools/bio/chembl.d.ts +38 -0
  177. package/dist/tools/bio/chembl.js +138 -0
  178. package/dist/tools/bio/comptox.d.ts +50 -0
  179. package/dist/tools/bio/comptox.js +149 -0
  180. package/dist/tools/bio/get-impc-ko-profile.js +9 -6
  181. package/dist/tools/bio/index.d.ts +5 -17
  182. package/dist/tools/bio/index.js +5 -17
  183. package/dist/tools/bio/keys.d.ts +14 -9
  184. package/dist/tools/bio/keys.js +16 -16
  185. package/dist/tools/bio/opentargets.d.ts +38 -0
  186. package/dist/tools/bio/opentargets.js +89 -0
  187. package/dist/tools/bio/pubchem.d.ts +29 -0
  188. package/dist/tools/bio/pubchem.js +92 -0
  189. package/dist/tools/bio/pubmed.d.ts +51 -0
  190. package/dist/tools/bio/pubmed.js +100 -0
  191. package/dist/tools/bio/search-bgee-expression.js +12 -6
  192. package/dist/tools/bio/search-clinical-trials.js +16 -5
  193. package/dist/tools/bio/search-dgidb.d.ts +11 -1
  194. package/dist/tools/bio/search-dgidb.js +92 -78
  195. package/dist/tools/bio/search-faers.js +8 -4
  196. package/dist/tools/bio/search-geo-datasets.js +20 -6
  197. package/dist/tools/bio/search-gwas-catalog.d.ts +1 -1
  198. package/dist/tools/bio/search-pharmgkb.js +10 -4
  199. package/dist/tools/display/index.d.ts +1 -0
  200. package/dist/tools/display/index.js +1 -0
  201. package/dist/tools/display/normalize-echart-spec.d.ts +61 -0
  202. package/dist/tools/display/normalize-echart-spec.js +211 -0
  203. package/dist/tools/display/show-user.d.ts +6 -1
  204. package/dist/tools/display/show-user.js +25 -12
  205. package/dist/tools/execute-plan.js +5 -2
  206. package/dist/tools/iterate-report.d.ts +171 -118
  207. package/dist/tools/iterate-report.js +336 -96
  208. package/dist/tools/lib/chembl-client.d.ts +12 -0
  209. package/dist/tools/lib/chembl-client.js +127 -0
  210. package/dist/tools/lib/ctx-ops.d.ts +224 -0
  211. package/dist/tools/lib/ctx-ops.js +450 -0
  212. package/dist/tools/lib/iuphar-client.js +10 -1
  213. package/dist/tools/lib/openfda-client.js +11 -3
  214. package/dist/tools/lib/pathway-client.js +10 -4
  215. package/dist/tools/lib/pubchem-ops.d.ts +74 -0
  216. package/dist/tools/lib/pubchem-ops.js +224 -0
  217. package/dist/tools/memory/update-working-memory.d.ts +45 -7
  218. package/dist/tools/memory/update-working-memory.js +111 -41
  219. package/dist/tools/report/index.d.ts +3 -2
  220. package/dist/tools/report/index.js +3 -2
  221. package/dist/tools/research/generate-analogy-report.js +25 -12
  222. package/dist/tools/research/generate-plan.d.ts +12 -5
  223. package/dist/tools/research/generate-plan.js +168 -37
  224. package/dist/tools/research/index.d.ts +1 -0
  225. package/dist/tools/research/index.js +1 -0
  226. package/dist/tools/research/inspect-data-profile.d.ts +85 -0
  227. package/dist/tools/research/inspect-data-profile.js +161 -0
  228. package/dist/tools/research/literature-reviewer.d.ts +3 -0
  229. package/dist/tools/research/literature-reviewer.js +5 -6
  230. package/dist/tools/sandbox/list-available-packages.d.ts +79 -5
  231. package/dist/tools/sandbox/list-available-packages.js +152 -10
  232. package/dist/tools/sandbox/list-available-refs.d.ts +141 -5
  233. package/dist/tools/sandbox/list-available-refs.js +647 -134
  234. package/dist/tools/sandbox/report-blocker.d.ts +39 -9
  235. package/dist/tools/sandbox/report-blocker.js +43 -20
  236. package/dist/tools/workspace/execute-command.d.ts +3 -0
  237. package/dist/tools/workspace/execute-command.js +3 -1
  238. package/dist/tools/workspace/read-file.js +4 -5
  239. package/dist/tools/workspace/run-exec.d.ts +4 -4
  240. package/dist/tools/workspace/run-exec.js +4 -4
  241. package/dist/tools/workspace/show-file.js +10 -6
  242. package/dist/tools/workspace/show-plan.js +12 -6
  243. package/dist/tools/workspace/workspace-search.d.ts +1 -1
  244. package/dist/tools/workspace/workspace-search.js +11 -5
  245. package/dist/workflows/execute-analysis.d.ts +46 -4
  246. package/dist/workflows/execute-analysis.js +118 -13
  247. package/dist/workflows/execute-target-assessment.d.ts +3 -0
  248. package/dist/workflows/execute-target-assessment.js +19 -15
  249. package/dist/workflows/sandbox-step.d.ts +9 -0
  250. package/dist/workflows/sandbox-step.js +39 -17
  251. package/dist/workflows/target-assessment/lib/citation-validator.js +2 -2
  252. package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.js +2 -1
  253. package/dist/workflows/target-assessment/lib/llm-step.d.ts +3 -0
  254. package/dist/workflows/target-assessment/lib/llm-step.js +3 -1
  255. package/dist/workflows/target-assessment/lib/structured-llm.d.ts +3 -0
  256. package/dist/workflows/target-assessment/lib/structured-llm.js +1 -0
  257. package/dist/workflows/target-assessment/phase5-persist.d.ts +3 -0
  258. package/dist/workflows/target-assessment/phase5-persist.js +8 -3
  259. package/dist/workflows/target-assessment/progress.d.ts +2 -1
  260. package/dist/workflows/target-assessment/progress.js +4 -3
  261. package/dist/workspace/filesystem.js +163 -74
  262. package/dist/workspace/paths.d.ts +2 -2
  263. package/dist/workspace/paths.js +2 -2
  264. package/package.json +95 -96
  265. package/dist/execution/filesystem-artifact-registry.d.ts +0 -57
  266. package/dist/execution/filesystem-artifact-registry.js +0 -92
  267. package/dist/lib/identifiers.d.ts +0 -8
  268. package/dist/lib/identifiers.js +0 -10
  269. package/dist/lib/mime-detect.d.ts +0 -17
  270. package/dist/lib/mime-detect.js +0 -29
  271. package/dist/lib/streamed-download.d.ts +0 -28
  272. package/dist/lib/streamed-download.js +0 -111
  273. package/dist/lib/trace-context.d.ts +0 -11
  274. package/dist/lib/trace-context.js +0 -49
  275. package/dist/providers/llm-capabilities.d.ts +0 -33
  276. package/dist/providers/llm-capabilities.js +0 -58
  277. package/dist/schemas/render-step-prompt.d.ts +0 -32
  278. package/dist/schemas/render-step-prompt.js +0 -76
  279. package/dist/tools/bio/find-approval-precedent.d.ts +0 -32
  280. package/dist/tools/bio/find-approval-precedent.js +0 -72
  281. package/dist/tools/bio/get-article-details.d.ts +0 -11
  282. package/dist/tools/bio/get-article-details.js +0 -20
  283. package/dist/tools/bio/get-article-full-text.d.ts +0 -19
  284. package/dist/tools/bio/get-article-full-text.js +0 -32
  285. package/dist/tools/bio/get-bioactivity.d.ts +0 -11
  286. package/dist/tools/bio/get-bioactivity.js +0 -23
  287. package/dist/tools/bio/get-drug-info.d.ts +0 -9
  288. package/dist/tools/bio/get-drug-info.js +0 -22
  289. package/dist/tools/bio/get-mechanism.d.ts +0 -8
  290. package/dist/tools/bio/get-mechanism.js +0 -18
  291. package/dist/tools/bio/get-pubchem-assays.d.ts +0 -22
  292. package/dist/tools/bio/get-pubchem-assays.js +0 -107
  293. package/dist/tools/bio/get-pubchem-cross-refs.d.ts +0 -15
  294. package/dist/tools/bio/get-pubchem-cross-refs.js +0 -68
  295. package/dist/tools/bio/get-target-safety.d.ts +0 -18
  296. package/dist/tools/bio/get-target-safety.js +0 -26
  297. package/dist/tools/bio/search-compounds.d.ts +0 -22
  298. package/dist/tools/bio/search-compounds.js +0 -160
  299. package/dist/tools/bio/search-ctx-chemical.d.ts +0 -50
  300. package/dist/tools/bio/search-ctx-chemical.js +0 -130
  301. package/dist/tools/bio/search-ctx-exposure.d.ts +0 -55
  302. package/dist/tools/bio/search-ctx-exposure.js +0 -176
  303. package/dist/tools/bio/search-ctx-hazard.d.ts +0 -111
  304. package/dist/tools/bio/search-ctx-hazard.js +0 -178
  305. package/dist/tools/bio/search-opentargets.d.ts +0 -11
  306. package/dist/tools/bio/search-opentargets.js +0 -25
  307. package/dist/tools/bio/search-pubchem-compound.d.ts +0 -26
  308. package/dist/tools/bio/search-pubchem-compound.js +0 -110
  309. package/dist/tools/bio/search-pubmed.d.ts +0 -17
  310. package/dist/tools/bio/search-pubmed.js +0 -31
  311. package/dist/tools/bio/search-targets.d.ts +0 -9
  312. package/dist/tools/bio/search-targets.js +0 -20
  313. package/dist/tools/bio/search-toxcast.d.ts +0 -36
  314. package/dist/tools/bio/search-toxcast.js +0 -141
  315. package/dist/workflows/register-workflows.d.ts +0 -51
  316. package/dist/workflows/register-workflows.js +0 -48
  317. package/dist/workflows/resume-execute-analysis.d.ts +0 -70
  318. package/dist/workflows/resume-execute-analysis.js +0 -80
  319. package/dist/workflows/target-assessment/correction-loop.d.ts +0 -45
  320. package/dist/workflows/target-assessment/correction-loop.js +0 -62
  321. package/dist/workflows/target-assessment/steps/phase0-resolve.d.ts +0 -71
  322. package/dist/workflows/target-assessment/steps/phase0-resolve.js +0 -21
  323. package/dist/workflows/target-assessment/steps/phase1-aggregate.d.ts +0 -1018
  324. package/dist/workflows/target-assessment/steps/phase1-aggregate.js +0 -25
  325. package/dist/workspace/mount-strategy.d.ts +0 -50
  326. package/dist/workspace/mount-strategy.js +0 -67
  327. package/dist/workspace/provenance-collector.d.ts +0 -34
  328. package/dist/workspace/provenance-collector.js +0 -16
  329. package/dist/workspace/workspace-paths.d.ts +0 -26
  330. package/dist/workspace/workspace-paths.js +0 -31
@@ -8,8 +8,8 @@
8
8
  * the workspace mutate trio (`execute_command`, `write_file`, `edit_file`)
9
9
  * bound to the shared `SandboxClient` and the step's `allowedWritePrefix`,
10
10
  * the workspace read tools (`read_file`, `grep`) over the shared
11
- * `WorkspaceFilesystem`, and the bio/literature/context7/run-inspection
12
- * tools its `meta.tools` allowlist names.
11
+ * `WorkspaceFilesystem`, `inspect_data_profile` over the shared `Pool`, and the
12
+ * bio/literature/context7/run-inspection tools its `meta.tools` allowlist names.
13
13
  *
14
14
  * Tool resolution is a single registry lookup — every name in `meta.tools`
15
15
  * must map to a concrete `Tool`; unknown names throw at composition time
@@ -18,13 +18,15 @@
18
18
  import { sandboxOrientCorePrompt, sandboxAnalysisStepStandardsPrompt } from "../../prompts/sandbox-standards.js";
19
19
  import { composeSystemPrompt } from "../system-prompt.js";
20
20
  // Sandbox-environment introspection.
21
- import { listAvailablePackagesTool, listAvailableRefsTool } from "../../tools/sandbox/index.js";
21
+ import { createListAvailableRefsTool, listAvailablePackagesTool } from "../../tools/sandbox/index.js";
22
22
  // Context7 docs (pure leaves).
23
23
  import { queryDocsTool, resolveLibraryIdTool } from "../../tools/research/context7-docs.js";
24
24
  // Run-inspection (dependency-bearing).
25
25
  import { createInspectRunTool } from "../../tools/research/inspect-run.js";
26
+ // Data-profile retrieval (dependency-bearing) — always-on, see `createSandboxAgent`.
27
+ import { createInspectDataProfileTool } from "../../tools/research/inspect-data-profile.js";
26
28
  // Bio leaf tools.
27
- import { checkSafetyPanelTool, getBioactivityTool, getDrugInfoTool, getImpcKoProfileTool, getMechanismTool, getPubchemAssaysTool, getPubchemCrossRefsTool, getTargetSafetyTool, lookupGoTermTool, searchBgeeExpressionTool, searchClinicalTrialsTool, searchCompoundsTool, searchDgidbTool, searchFaersTool, searchGeneTool, searchGeoDatasetsTool, searchGwasCatalogTool, searchInteractionsTool, searchOpenTargetsTool, searchPathwayTool, searchPharmgkbTool, searchPubchemCompoundTool, searchTargetsTool, } from "../../tools/bio/index.js";
29
+ import { checkSafetyPanelTool, chemblTool, getImpcKoProfileTool, lookupGoTermTool, openTargetsTool, pubchemTool, searchBgeeExpressionTool, searchClinicalTrialsTool, createSearchDgidbTool, searchFaersTool, searchGeneTool, searchGeoDatasetsTool, searchGwasCatalogTool, searchInteractionsTool, searchPathwayTool, searchPharmgkbTool, } from "../../tools/bio/index.js";
28
30
  import { createNcbiTools, createChemDbTools } from "../../tools/bio/keys.js";
29
31
  // Workspace read surface.
30
32
  import { createFileStatTool, createGrepTool, createListFilesTool, createReadFileTool, createWorkspaceSearchTool } from "../../tools/workspace/index.js";
@@ -44,52 +46,48 @@ import { SANDBOX_AGENT_DEFAULT_MAX_ITERATIONS } from "./types.js";
44
46
  */
45
47
  export const BASE_SANDBOX_TOOLS = ["listAvailablePackages", "listAvailableRefs", "resolveLibraryId", "queryDocs", "inspectRun"];
46
48
  /**
47
- * Resolve `meta.tools` into the bio/research tools record. Workspace
48
- * read/mutate tools are added by `createSandboxAgent` regardless of meta
49
- * — they are the always-on substrate, not in the allowlist.
49
+ * Resolve `meta.tools` into the bio/research tools record. The workspace
50
+ * read/mutate tools and `inspect_data_profile` are added by `createSandboxAgent`
51
+ * regardless of meta — they are the always-on substrate, not in the allowlist.
50
52
  */
51
53
  function resolveSandboxTools(deps, tools) {
52
54
  const ncbi = createNcbiTools(deps.bioKeys);
53
55
  const chemDb = createChemDbTools(deps.bioKeys);
54
56
  const registry = {
55
57
  listAvailablePackages: listAvailablePackagesTool,
56
- listAvailableRefs: listAvailableRefsTool,
58
+ listAvailableRefs: createListAvailableRefsTool({
59
+ sandboxClient: deps.sandboxClient,
60
+ sandbox: deps.step.sandbox,
61
+ workflowId: deps.step.workflowId,
62
+ stepId: deps.step.stepId,
63
+ nextFunctionId: deps.step.nextFunctionId,
64
+ deadlineMs: deps.step.deadlineMs,
65
+ markExecActive: createMarkExecActive(deps),
66
+ }),
57
67
  resolveLibraryId: resolveLibraryIdTool,
58
68
  queryDocs: queryDocsTool,
59
69
  inspectRun: createInspectRunTool(deps.pool),
60
- searchPubMed: ncbi.searchPubMed,
61
- getArticleDetails: ncbi.getArticleDetails,
62
- getArticleFullText: ncbi.getArticleFullText,
70
+ pubmed: ncbi.pubmed,
63
71
  searchGene: searchGeneTool,
64
72
  searchPathway: searchPathwayTool,
65
73
  lookupGoTerm: lookupGoTermTool,
66
74
  searchInteractions: searchInteractionsTool,
67
- searchCompounds: searchCompoundsTool,
68
- getBioactivity: getBioactivityTool,
69
- searchTargets: searchTargetsTool,
70
- getMechanism: getMechanismTool,
71
- getDrugInfo: getDrugInfoTool,
72
- searchPubchemCompound: searchPubchemCompoundTool,
73
- getPubchemCrossRefs: getPubchemCrossRefsTool,
74
- getPubchemAssays: getPubchemAssaysTool,
75
- searchOpenTargets: searchOpenTargetsTool,
76
- getTargetSafety: getTargetSafetyTool,
75
+ chembl: chemblTool,
76
+ pubchem: pubchemTool,
77
+ opentargets: openTargetsTool,
77
78
  searchPharmgkb: searchPharmgkbTool,
78
79
  searchFaers: searchFaersTool,
79
80
  searchClinicalTrials: searchClinicalTrialsTool,
80
81
  searchGeoDatasets: searchGeoDatasetsTool,
81
82
  searchClinvar: ncbi.searchClinvar,
82
- searchDgidb: searchDgidbTool,
83
+ searchDgidb: createSearchDgidbTool({ ...(deps.logger ? { logger: deps.logger } : {}) }),
83
84
  searchGwasCatalog: searchGwasCatalogTool,
84
85
  searchDisgenet: chemDb.searchDisgenet,
85
86
  searchDrugbank: chemDb.searchDrugbank,
86
87
  searchBgeeExpression: searchBgeeExpressionTool,
87
88
  getImpcKoProfile: getImpcKoProfileTool,
88
89
  checkSafetyPanel: checkSafetyPanelTool,
89
- searchToxcast: chemDb.searchToxcast,
90
- searchCtxHazard: chemDb.searchCtxHazard,
91
- searchCtxChemical: chemDb.searchCtxChemical,
92
- searchCtxExposure: chemDb.searchCtxExposure,
90
+ comptox: chemDb.comptox,
93
91
  };
94
92
  const seen = new Set();
95
93
  const resolved = [];
@@ -105,6 +103,9 @@ function resolveSandboxTools(deps, tools) {
105
103
  }
106
104
  return resolved;
107
105
  }
106
+ function createMarkExecActive(deps) {
107
+ return (execId) => setActiveExecId(deps.pool, deps.step.runId, deps.step.stepId, execId).match(() => { }, () => { });
108
+ }
108
109
  /** Build the workspace mutate + read tools every sandbox agent receives. In
109
110
  * `readOnly` mode the write_file/edit_file pair is omitted; execute_command
110
111
  * and the read tools stay. */
@@ -113,7 +114,7 @@ function buildWorkspaceTools(deps, readOnly) {
113
114
  // Registry tagging is a best-effort watchdog backstop (`run-exec.ts` already
114
115
  // swallows a throw here); fold a `DbError` into a no-op so a registry write
115
116
  // failure neither fails the exec nor surfaces as an unhandled rejection.
116
- const markExecActive = (execId) => setActiveExecId(pool, step.runId, step.stepId, execId).match(() => { }, () => { });
117
+ const markExecActive = createMarkExecActive(deps);
117
118
  // The agent's writable working directory: relative paths resolve here, and
118
119
  // writes are confined here. `allowedWritePrefix` is its host path; the
119
120
  // in-sandbox path (the `execute_command` default cwd, see the harness-workspace-tools spec) is the same
@@ -161,26 +162,36 @@ function buildWorkspaceTools(deps, readOnly) {
161
162
  }
162
163
  /**
163
164
  * Build one sandbox `AgentDefinition`. The system prompt is composed once
164
- * (SOUL kernel + agent body + sandbox standards) — no processors, no
165
- * conversational layer (sandbox agents are not user-facing). Tools are
166
- * resolved against the closed allowlist; the workspace surface is always
167
- * wired regardless of meta.
165
+ * (SOUL execution core + agent body + sandbox standards) — no processors, and
166
+ * neither human-facing SOUL layer: a sandbox agent is headless, so the identity
167
+ * and conversational layers are dead weight in its context while every hard
168
+ * guardrail it does need rides in the execution core. Tools are resolved
169
+ * against the closed allowlist; the workspace surface is always wired
170
+ * regardless of meta.
171
+ *
172
+ * The composed `systemPrompt` is a **pure function of the agent type** — nothing
173
+ * from `deps.step` reaches it, so two steps of one run, and two runs of one
174
+ * analysis, send a byte-identical ~20k-char prefix that the provider's prompt
175
+ * cache can actually reuse. Keep it that way: a single interpolated id or path
176
+ * makes every step's prefix unique again, and each step pays a full cache write
177
+ * and reads nothing back. Per-step values belong in the step's briefing (its
178
+ * first user message — see `prompts/briefing.ts`), which names the working
179
+ * directory, the analysis root, the dataset, and what each dependency produced.
168
180
  */
169
181
  export function createSandboxAgent(deps, meta, body, opts = {}) {
170
182
  const appendStandards = opts.appendAnalysisStepStandards ?? true;
171
183
  const sandboxLayer = appendStandards ? [sandboxOrientCorePrompt, sandboxAnalysisStepStandardsPrompt] : [sandboxOrientCorePrompt];
172
184
  const agentBody = [body, ...sandboxLayer].map((s) => s.trim()).join("\n\n");
173
- // Substitute the concrete in-sandbox paths the agent sees so the orient-core
174
- // path model is accurate per step (see the harness-workspace-tools spec), not boilerplate prose.
175
- const analysisRoot = `/${deps.step.analysisId}`;
176
- const workingDir = toSandboxPath(deps.step.workspaceRoot, deps.step.analysisId, deps.step.allowedWritePrefix);
177
- const resolvedBody = agentBody.split("{{WORKING_DIR}}").join(workingDir).split("{{ANALYSIS_ROOT}}").join(analysisRoot);
178
- const systemPrompt = composeSystemPrompt(resolvedBody, {
179
- includeConversationalStyle: false,
180
- });
185
+ const systemPrompt = composeSystemPrompt(agentBody);
181
186
  const skillTools = deps.skillsDir ? Object.values(createSkillTools({ skillsDir: deps.skillsDir, skills: meta.skills })) : [];
182
187
  const tools = [
183
188
  ...buildWorkspaceTools(deps, opts.readOnly ?? false),
189
+ // Always-on, not in the `meta.tools` allowlist: the data profile is the only
190
+ // record of what the analysis's input dataset IS, and no file carries it (the
191
+ // profiler's scratch tree is deleted on completion). An agent that cannot pull
192
+ // it has no fallback but to re-derive organism, dimensions, and format from the
193
+ // raw bytes — so every sandbox agent gets it, whatever its meta declares.
194
+ createInspectDataProfileTool(deps.pool),
184
195
  ...skillTools,
185
196
  ...resolveSandboxTools(deps, meta.tools),
186
197
  ...(deps.blockerHolder ? [createReportBlockerTool(deps.blockerHolder)] : []),
@@ -18,7 +18,7 @@ export const meta = {
18
18
  ],
19
19
  suitableFor: ["scrna-seq", "snrna-seq", "cytof", "mass-cytometry"],
20
20
  skills: ["single-cell", "shared/omics-general"],
21
- tools: [...BASE_SANDBOX_TOOLS, "searchPubMed", "getArticleDetails", "getArticleFullText", "searchGeoDatasets"],
21
+ tools: [...BASE_SANDBOX_TOOLS, "pubmed", "searchGeoDatasets"],
22
22
  };
23
23
  export function createSingleCellAgent(deps) {
24
24
  return createSandboxAgent(deps, meta, singleCellAgentPrompt);
@@ -8,7 +8,7 @@ export const meta = {
8
8
  capabilities: ["spatial statistics", "spatial domain detection", "deconvolution", "niche analysis", "spatial autocorrelation", "ligand-receptor spatial"],
9
9
  suitableFor: ["visium", "merfish", "slide-seq", "codex", "xenium", "mibi", "imc"],
10
10
  skills: ["spatial-omics", "shared/omics-general"],
11
- tools: [...BASE_SANDBOX_TOOLS, "searchPubMed", "getArticleDetails", "getArticleFullText", "searchGeoDatasets"],
11
+ tools: [...BASE_SANDBOX_TOOLS, "pubmed", "searchGeoDatasets"],
12
12
  };
13
13
  export function createSpatialOmicsAgent(deps) {
14
14
  return createSandboxAgent(deps, meta, spatialOmicsAgentPrompt);
@@ -30,7 +30,7 @@ export const meta = {
30
30
  "patient-stratification",
31
31
  ],
32
32
  skills: ["statistical-modeling", "shared/omics-general"],
33
- tools: [...BASE_SANDBOX_TOOLS, "searchPubMed", "getArticleDetails", "getArticleFullText"],
33
+ tools: [...BASE_SANDBOX_TOOLS, "pubmed"],
34
34
  };
35
35
  export function createStatisticalModelingAgent(deps) {
36
36
  return createSandboxAgent(deps, meta, statisticalModelingAgentPrompt);
@@ -34,22 +34,16 @@ export const meta = {
34
34
  tools: [
35
35
  ...BASE_SANDBOX_TOOLS,
36
36
  "checkSafetyPanel",
37
- "searchPubMed",
38
- "getArticleDetails",
39
- "getArticleFullText",
37
+ "pubmed",
40
38
  "searchGene",
41
39
  "searchPathway",
42
40
  "lookupGoTerm",
43
41
  "searchInteractions",
44
- "searchOpenTargets",
45
- "getTargetSafety",
42
+ "opentargets",
46
43
  "searchFaers",
47
44
  "searchPharmgkb",
48
45
  "searchDgidb",
49
- "searchToxcast",
50
- "searchCtxHazard",
51
- "searchCtxChemical",
52
- "searchCtxExposure",
46
+ "comptox",
53
47
  "searchBgeeExpression",
54
48
  "getImpcKoProfile",
55
49
  ],
@@ -12,7 +12,7 @@
12
12
  * envelope. `searchGwasCatalog` is fully public.
13
13
  */
14
14
  /** Closed allowlist of tools any sandbox agent may declare. */
15
- export type SandboxToolName = "listAvailablePackages" | "listAvailableRefs" | "resolveLibraryId" | "queryDocs" | "inspectRun" | "searchPubMed" | "getArticleDetails" | "getArticleFullText" | "searchGene" | "searchPathway" | "lookupGoTerm" | "searchInteractions" | "searchCompounds" | "getBioactivity" | "searchTargets" | "getMechanism" | "getDrugInfo" | "searchPubchemCompound" | "getPubchemCrossRefs" | "getPubchemAssays" | "searchOpenTargets" | "getTargetSafety" | "searchPharmgkb" | "searchFaers" | "searchClinicalTrials" | "searchGeoDatasets" | "searchClinvar" | "searchDgidb" | "searchGwasCatalog" | "searchDisgenet" | "searchDrugbank" | "searchBgeeExpression" | "getImpcKoProfile" | "checkSafetyPanel" | "searchToxcast" | "searchCtxHazard" | "searchCtxChemical" | "searchCtxExposure";
15
+ export type SandboxToolName = "listAvailablePackages" | "listAvailableRefs" | "resolveLibraryId" | "queryDocs" | "inspectRun" | "pubmed" | "searchGene" | "searchPathway" | "lookupGoTerm" | "searchInteractions" | "chembl" | "pubchem" | "opentargets" | "searchPharmgkb" | "searchFaers" | "searchClinicalTrials" | "searchGeoDatasets" | "searchClinvar" | "searchDgidb" | "searchGwasCatalog" | "searchDisgenet" | "searchDrugbank" | "searchBgeeExpression" | "getImpcKoProfile" | "checkSafetyPanel" | "comptox";
16
16
  /** Planner-facing metadata + tool allowlist for one sandbox agent. */
17
17
  export interface AgentMeta {
18
18
  /** Stable agent id — also the key in the catalog and the `AgentDefinition.id`. */
@@ -2,24 +2,50 @@
2
2
  * Harness system-prompt composition.
3
3
  *
4
4
  * SOUL is static text — there is no processor pipeline. `composeSystemPrompt`
5
- * concatenates the SOUL kernel, the optional SOUL conversational layer, and an
6
- * agent's own prompt into the single `AgentDefinition.systemPrompt` string the
7
- * loop sends as the cacheable prefix.
5
+ * concatenates the SOUL layers an agent declares with the agent's own prompt
6
+ * into the single `AgentDefinition.systemPrompt` string the loop sends as the
7
+ * cacheable prefix. It is the only place SOUL is applied, and every agent is
8
+ * built through it — an unguarded agent is not constructible.
8
9
  *
9
- * `includeConversationalStyle` toggles the conversational block: the
10
- * user-facing conversation agent keeps it; internal tool-only specialists
11
- * (planner, run-synthesizer) and sandbox agents omit it — their tight tool
12
- * loops compete with conversational discipline.
10
+ * The layer policy:
11
+ *
12
+ * - `SOULExecutionCore` is **always** included; there is no opt-out. The
13
+ * scientific stance and the hard guardrails (never fabricate, never oversell,
14
+ * never hide uncertainty, confidentiality, prompt-injection defense) bind a
15
+ * headless agent exactly as they bind the user-facing one. The run
16
+ * synthesizer and the report builder author user-facing scientific claims
17
+ * without a human in their loop — they are the last agents that should be
18
+ * free to fabricate.
19
+ *
20
+ * - `identity` (default `false`) adds `SOULIdentity` — the Inflexa name, the
21
+ * scope, and the human-facing refusal / impersonation / external-action
22
+ * guardrails. Only the conversation agent faces a human, so only it opts in.
23
+ *
24
+ * - `conversational` (default `false`) adds `SOULConversationalPrompt` —
25
+ * personality, response policy, and the 7-step reasoning cadence. Conversation
26
+ * agent only: a tool-only specialist's tight loop competes with conversational
27
+ * discipline.
28
+ *
29
+ * Both flags default off, so the zero-argument call yields a fully guarded
30
+ * headless agent — the safe default is also the shortest one to write.
13
31
  */
14
32
  export interface ComposeSystemPromptOptions {
33
+ /**
34
+ * Include the SOUL identity layer (name, scope, refusal of harmful
35
+ * requests, impersonation and external-action guardrails). Default `false`
36
+ * — opt in only for an agent a human talks to.
37
+ */
38
+ readonly identity?: boolean;
15
39
  /**
16
40
  * Include the SOUL conversational layer (personality, response policy,
17
- * 7-step reasoning). Default `true` — opt out for internal specialists.
41
+ * 7-step reasoning). Default `false` — opt in only for the conversation
42
+ * agent.
18
43
  */
19
- readonly includeConversationalStyle?: boolean;
44
+ readonly conversational?: boolean;
20
45
  }
21
46
  /**
22
- * Compose an agent's full system prompt: SOUL kernel, the optional SOUL
23
- * conversational layer, then the agent's own prompt — joined by blank lines.
47
+ * Compose an agent's full system prompt: the always-on SOUL execution core, the
48
+ * layers the agent opted into, then the agent's own prompt — joined by blank
49
+ * lines.
24
50
  */
25
51
  export declare function composeSystemPrompt(agentPrompt: string, options?: ComposeSystemPromptOptions): string;
@@ -2,22 +2,45 @@
2
2
  * Harness system-prompt composition.
3
3
  *
4
4
  * SOUL is static text — there is no processor pipeline. `composeSystemPrompt`
5
- * concatenates the SOUL kernel, the optional SOUL conversational layer, and an
6
- * agent's own prompt into the single `AgentDefinition.systemPrompt` string the
7
- * loop sends as the cacheable prefix.
5
+ * concatenates the SOUL layers an agent declares with the agent's own prompt
6
+ * into the single `AgentDefinition.systemPrompt` string the loop sends as the
7
+ * cacheable prefix. It is the only place SOUL is applied, and every agent is
8
+ * built through it — an unguarded agent is not constructible.
8
9
  *
9
- * `includeConversationalStyle` toggles the conversational block: the
10
- * user-facing conversation agent keeps it; internal tool-only specialists
11
- * (planner, run-synthesizer) and sandbox agents omit it — their tight tool
12
- * loops compete with conversational discipline.
10
+ * The layer policy:
11
+ *
12
+ * - `SOULExecutionCore` is **always** included; there is no opt-out. The
13
+ * scientific stance and the hard guardrails (never fabricate, never oversell,
14
+ * never hide uncertainty, confidentiality, prompt-injection defense) bind a
15
+ * headless agent exactly as they bind the user-facing one. The run
16
+ * synthesizer and the report builder author user-facing scientific claims
17
+ * without a human in their loop — they are the last agents that should be
18
+ * free to fabricate.
19
+ *
20
+ * - `identity` (default `false`) adds `SOULIdentity` — the Inflexa name, the
21
+ * scope, and the human-facing refusal / impersonation / external-action
22
+ * guardrails. Only the conversation agent faces a human, so only it opts in.
23
+ *
24
+ * - `conversational` (default `false`) adds `SOULConversationalPrompt` —
25
+ * personality, response policy, and the 7-step reasoning cadence. Conversation
26
+ * agent only: a tool-only specialist's tight loop competes with conversational
27
+ * discipline.
28
+ *
29
+ * Both flags default off, so the zero-argument call yields a fully guarded
30
+ * headless agent — the safe default is also the shortest one to write.
13
31
  */
14
- import { SOULKernelPrompt, SOULConversationalPrompt } from "../prompts/SOUL.js";
32
+ import { SOULExecutionCore, SOULIdentity, SOULConversationalPrompt } from "../prompts/SOUL.js";
15
33
  /**
16
- * Compose an agent's full system prompt: SOUL kernel, the optional SOUL
17
- * conversational layer, then the agent's own prompt — joined by blank lines.
34
+ * Compose an agent's full system prompt: the always-on SOUL execution core, the
35
+ * layers the agent opted into, then the agent's own prompt — joined by blank
36
+ * lines.
18
37
  */
19
38
  export function composeSystemPrompt(agentPrompt, options = {}) {
20
- const includeConversational = options.includeConversationalStyle ?? true;
21
- const sections = includeConversational ? [SOULKernelPrompt, SOULConversationalPrompt, agentPrompt] : [SOULKernelPrompt, agentPrompt];
39
+ const sections = [SOULExecutionCore];
40
+ if (options.identity ?? false)
41
+ sections.push(SOULIdentity);
42
+ if (options.conversational ?? false)
43
+ sections.push(SOULConversationalPrompt);
44
+ sections.push(agentPrompt);
22
45
  return sections.map((s) => s.trim()).join("\n\n");
23
46
  }
@@ -14,7 +14,10 @@
14
14
  */
15
15
  import type { Pool } from "pg";
16
16
  import { type AssembledMessages } from "./message-assembly.js";
17
+ import type { Logger } from "../lib/logger.js";
17
18
  export interface PrepareChatTurnDeps {
19
+ /** Operational logging seam; omitted falls back to no-op. */
20
+ readonly logger?: Logger;
18
21
  readonly pool: Pool;
19
22
  }
20
23
  export interface PrepareChatTurnParams {
@@ -19,6 +19,7 @@ import { createThreadStore } from "../memory/thread-store.js";
19
19
  import { createWorkingMemory } from "../memory/working-memory.js";
20
20
  import { loadAnalysisStatus } from "../state/index.js";
21
21
  import { assembleMessages } from "./message-assembly.js";
22
+ import { createNoopLogger } from "../lib/console-logger.js";
22
23
  /**
23
24
  * Prepare one chat turn: resolve thread ownership, seed the title, load
24
25
  * analysis status, and assemble the message array. Input sanitization stays
@@ -48,7 +49,8 @@ export async function prepareChatTurn(deps, params) {
48
49
  }
49
50
  }
50
51
  catch (err) {
51
- console.warn("[harness.chat] title-seed failed (non-fatal):", err instanceof Error ? err.message : err);
52
+ const logger = (deps.logger ?? createNoopLogger()).named("harness.chat");
53
+ logger.warn("title-seed failed (non-fatal)", logger.errorFields(err));
52
54
  }
53
55
  const analysisState = await loadAnalysisStatus(pool, analysisId).unwrapOr(null);
54
56
  const history = createThreadHistory(pool);
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Data-profile orientation — the bounded projection of a persisted
3
+ * `DataProfileResult` into the few hundred characters an agent needs to know
4
+ * WHAT DATASET IT IS HOLDING before it does anything else.
5
+ *
6
+ * The persisted profile is deliberately rich (per-file metrics, warnings, tags,
7
+ * the full narrative summary) because the DB row is its only durable home — the
8
+ * profiler's scratch tree is deleted on completion. That richness is right for a
9
+ * pull-on-demand tool (`inspect_data_profile`) and wrong for anything injected
10
+ * unconditionally into a context window, which is what a step seed does. This is
11
+ * the lossy half of that pair: a hard character budget, a fixed field order, and
12
+ * visible truncation counts, so an agent that needs more knows to go pull it.
13
+ *
14
+ * A pure function over the record — no I/O, no LLM, no pipeline. It is the whole
15
+ * mechanism.
16
+ */
17
+ import type { DataProfileResult } from "../state/data-profile.js";
18
+ /** The character budget the projection guarantees it will not exceed. */
19
+ export declare const DATA_PROFILE_ORIENTATION_MAX_CHARS = 1200;
20
+ /**
21
+ * Project a persisted profile into an orientation blurb of at most `maxChars`
22
+ * characters. The bound is a guarantee, not a target: the assembled text is
23
+ * hard-clamped, so no profile — however verbose — can blow a caller's budget.
24
+ *
25
+ * Field order is fixed and reflects what an agent must know first: what the
26
+ * dataset IS, how it was designed, what is wrong with it, then what files carry
27
+ * it. Both list sections state their true total, so an elided tail is visible
28
+ * rather than silent.
29
+ *
30
+ * A snapshot written before the record was widened carries none of the structured
31
+ * fields. Rather than emit a bare file list, that case falls back to the narrative
32
+ * summary — the one orientation a legacy row does have.
33
+ */
34
+ export declare function buildDataProfileOrientation(result: DataProfileResult, maxChars?: number): string;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Data-profile orientation — the bounded projection of a persisted
3
+ * `DataProfileResult` into the few hundred characters an agent needs to know
4
+ * WHAT DATASET IT IS HOLDING before it does anything else.
5
+ *
6
+ * The persisted profile is deliberately rich (per-file metrics, warnings, tags,
7
+ * the full narrative summary) because the DB row is its only durable home — the
8
+ * profiler's scratch tree is deleted on completion. That richness is right for a
9
+ * pull-on-demand tool (`inspect_data_profile`) and wrong for anything injected
10
+ * unconditionally into a context window, which is what a step seed does. This is
11
+ * the lossy half of that pair: a hard character budget, a fixed field order, and
12
+ * visible truncation counts, so an agent that needs more knows to go pull it.
13
+ *
14
+ * A pure function over the record — no I/O, no LLM, no pipeline. It is the whole
15
+ * mechanism.
16
+ */
17
+ /** The character budget the projection guarantees it will not exceed. */
18
+ export const DATA_PROFILE_ORIENTATION_MAX_CHARS = 1200;
19
+ /** Files listed before the tail is elided (the count still reports the total). */
20
+ const MAX_FILES = 8;
21
+ /** Quality concerns listed — the profiler orders them, so these are the top ones. */
22
+ const MAX_CONCERNS = 3;
23
+ const MAX_DESIGN_CHARS = 200;
24
+ const MAX_CONCERN_CHARS = 120;
25
+ const MAX_FILE_DESCRIPTION_CHARS = 100;
26
+ const MAX_SUMMARY_FALLBACK_CHARS = 300;
27
+ /**
28
+ * Clamp to `max` chars, marking any elision with an ellipsis. Whitespace is left
29
+ * alone, so the assembled line structure survives the final bound.
30
+ */
31
+ function clamp(text, max) {
32
+ if (text.length <= max)
33
+ return text;
34
+ if (max <= 1)
35
+ return "";
36
+ return text.slice(0, max - 1).trimEnd() + "…";
37
+ }
38
+ /**
39
+ * Collapse a free-text field to one line and clamp it. Profiler prose (a summary, a
40
+ * design note, a file description) may contain newlines; folding them out is what
41
+ * keeps one record on one line.
42
+ */
43
+ function clip(text, max) {
44
+ return clamp(text.replace(/\s+/g, " ").trim(), max);
45
+ }
46
+ /** Non-empty, whitespace-collapsed value, or `undefined` — nulls and blanks read the same. */
47
+ function present(value) {
48
+ const flat = value?.replace(/\s+/g, " ").trim();
49
+ return flat ? flat : undefined;
50
+ }
51
+ /** `Homo sapiens (taxon 9606)`, plus a confidence marker when the profiler was unsure. */
52
+ function formatOrganism(organism) {
53
+ if (!organism)
54
+ return undefined;
55
+ const name = present(organism.scientificName);
56
+ if (!name)
57
+ return undefined;
58
+ const taxon = present(organism.taxonId);
59
+ const qualifier = organism.confidence === "high" ? "" : ` [${organism.confidence} confidence]`;
60
+ return taxon ? `${name} (taxon ${taxon})${qualifier}` : `${name}${qualifier}`;
61
+ }
62
+ /** `(20000 x 24, CSV)` — whichever of dimensions / format the profiler recorded. */
63
+ function formatFileFacts(file) {
64
+ const dims = typeof file.rows === "number" && typeof file.cols === "number" ? `${file.rows} x ${file.cols}` : undefined;
65
+ const facts = [dims, present(file.format)].filter((f) => f !== undefined);
66
+ return facts.length > 0 ? ` (${facts.join(", ")})` : "";
67
+ }
68
+ /**
69
+ * Project a persisted profile into an orientation blurb of at most `maxChars`
70
+ * characters. The bound is a guarantee, not a target: the assembled text is
71
+ * hard-clamped, so no profile — however verbose — can blow a caller's budget.
72
+ *
73
+ * Field order is fixed and reflects what an agent must know first: what the
74
+ * dataset IS, how it was designed, what is wrong with it, then what files carry
75
+ * it. Both list sections state their true total, so an elided tail is visible
76
+ * rather than silent.
77
+ *
78
+ * A snapshot written before the record was widened carries none of the structured
79
+ * fields. Rather than emit a bare file list, that case falls back to the narrative
80
+ * summary — the one orientation a legacy row does have.
81
+ */
82
+ export function buildDataProfileOrientation(result, maxChars = DATA_PROFILE_ORIENTATION_MAX_CHARS) {
83
+ const lines = [];
84
+ const identity = [
85
+ [present(result.domain), present(result.subtype)].filter((f) => f !== undefined).join(" / ") || undefined,
86
+ formatOrganism(result.organism),
87
+ present(result.tissue) ? `tissue: ${present(result.tissue)}` : undefined,
88
+ present(result.cellType) ? `cells: ${present(result.cellType)}` : undefined,
89
+ present(result.condition) ? `condition: ${present(result.condition)}` : undefined,
90
+ ].filter((f) => f !== undefined);
91
+ if (identity.length > 0) {
92
+ lines.push(`Dataset: ${identity.join(" — ")}`);
93
+ }
94
+ else {
95
+ // A legacy snapshot has no structured identity at all; the summary is the
96
+ // only orientation it carries, so it stands in for the whole header.
97
+ lines.push(`Dataset: ${clip(result.summary, MAX_SUMMARY_FALLBACK_CHARS)}`);
98
+ }
99
+ const design = present(result.experimentalDesign);
100
+ if (design)
101
+ lines.push(`Design: ${clip(design, MAX_DESIGN_CHARS)}`);
102
+ const concerns = (result.qualityAssessment?.concerns ?? []).map((c) => present(c)).filter((c) => c !== undefined);
103
+ if (concerns.length > 0) {
104
+ const shown = concerns.slice(0, MAX_CONCERNS).map((c) => clip(c, MAX_CONCERN_CHARS));
105
+ const more = concerns.length > shown.length ? ` (+${concerns.length - shown.length} more)` : "";
106
+ lines.push(`Concerns: ${shown.join("; ")}${more}`);
107
+ }
108
+ if (result.files.length > 0) {
109
+ const shown = result.files.slice(0, MAX_FILES);
110
+ const header = shown.length < result.files.length ? `Files (${shown.length} of ${result.files.length}):` : `Files (${result.files.length}):`;
111
+ lines.push(header);
112
+ for (const file of shown) {
113
+ lines.push(`- ${file.path} — ${clip(file.description, MAX_FILE_DESCRIPTION_CHARS)}${formatFileFacts(file)}`);
114
+ }
115
+ }
116
+ return clamp(lines.join("\n"), maxChars);
117
+ }
@@ -22,7 +22,10 @@ import type { ChatProvider, EmbeddingProvider } from "../providers/types.js";
22
22
  import type { BioToolKeys } from "../tools/bio/keys.js";
23
23
  import type { RunFinding } from "../workflows/execute-analysis.js";
24
24
  import type { ResolveWorkspaceRoot } from "../workspace/paths.js";
25
+ import type { Logger } from "../lib/logger.js";
25
26
  export interface SynthesizeRunDeps {
27
+ /** Operational logging seam; omitted falls back to no-op. */
28
+ readonly logger?: Logger;
26
29
  readonly pool: Pool;
27
30
  readonly provider: ChatProvider;
28
31
  /** Write-side embedder — its `dimensions` sizes the per-analysis index. */
@@ -20,6 +20,7 @@ import { ensureSearchIndex, searchIndexName } from "../workspace/search-config.j
20
20
  import { createVectorStore } from "../state/vector-store.js";
21
21
  import { loadPlan, queryRun } from "../state/index.js";
22
22
  import { unwrapOrThrow } from "../lib/result.js";
23
+ import { createNoopLogger } from "../lib/console-logger.js";
23
24
  export async function synthesizeRun(deps, params) {
24
25
  const { pool, provider, embedding, resolveWorkspaceRoot, synthesisModel, bioKeys } = deps;
25
26
  const { analysisId, runId, completedSteps, session, emit, onProgress } = params;
@@ -71,7 +72,8 @@ export async function synthesizeRun(deps, params) {
71
72
  }));
72
73
  }
73
74
  catch (indexErr) {
74
- console.warn(`[synthesize-run] vector indexing failed for run ${runId}:`, indexErr instanceof Error ? indexErr.message : indexErr);
75
+ const logger = (deps.logger ?? createNoopLogger()).named("synthesize-run");
76
+ logger.warn("vector indexing failed", { runId, ...logger.errorFields(indexErr) });
75
77
  }
76
78
  await onProgress("persisting", "Persisting synthesis");
77
79
  await persistSynthesis({ workspaceRoot, runId, synthesis });