@inflexa-ai/harness 0.16.0 → 0.18.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 (295) hide show
  1. package/dist/agents/conversation-agent.d.ts +3 -0
  2. package/dist/agents/conversation-agent.js +5 -2
  3. package/dist/agents/report-builder.d.ts +2 -0
  4. package/dist/agents/report-builder.js +2 -0
  5. package/dist/agents/sandbox/shared.d.ts +3 -0
  6. package/dist/agents/sandbox/shared.js +5 -0
  7. package/dist/agents/sandbox/types.d.ts +1 -1
  8. package/dist/app/chat-turn.d.ts +2 -0
  9. package/dist/app/chat-turn.js +11 -2
  10. package/dist/app/spawn-report-session.d.ts +85 -0
  11. package/dist/app/spawn-report-session.js +83 -0
  12. package/dist/app/synthesize-run.d.ts +3 -0
  13. package/dist/app/synthesize-run.js +2 -1
  14. package/dist/citations/__fixtures__/resolver.d.ts +3 -0
  15. package/dist/citations/__fixtures__/resolver.js +9 -0
  16. package/dist/citations/aggregate.d.ts +3 -0
  17. package/dist/citations/aggregate.js +44 -0
  18. package/dist/citations/clients/arxiv.d.ts +6 -0
  19. package/dist/citations/clients/arxiv.js +34 -0
  20. package/dist/citations/clients/common.d.ts +11 -0
  21. package/dist/citations/clients/common.js +43 -0
  22. package/dist/citations/clients/crossref.d.ts +8 -0
  23. package/dist/citations/clients/crossref.js +98 -0
  24. package/dist/citations/clients/doi-registry.d.ts +3 -0
  25. package/dist/citations/clients/doi-registry.js +95 -0
  26. package/dist/citations/clients/pubmed.d.ts +6 -0
  27. package/dist/citations/clients/pubmed.js +83 -0
  28. package/dist/citations/clients/semantic-scholar.d.ts +6 -0
  29. package/dist/citations/clients/semantic-scholar.js +60 -0
  30. package/dist/citations/compare.d.ts +9 -0
  31. package/dist/citations/compare.js +72 -0
  32. package/dist/citations/match.d.ts +11 -0
  33. package/dist/citations/match.js +173 -0
  34. package/dist/citations/normalize.d.ts +31 -0
  35. package/dist/citations/normalize.js +187 -0
  36. package/dist/citations/plan.d.ts +2 -0
  37. package/dist/citations/plan.js +59 -0
  38. package/dist/citations/resolve.d.ts +28 -0
  39. package/dist/citations/resolve.js +238 -0
  40. package/dist/citations/types.d.ts +583 -0
  41. package/dist/citations/types.js +129 -0
  42. package/dist/contracts/chat-events.d.ts +9 -0
  43. package/dist/contracts/index.d.ts +6 -2
  44. package/dist/contracts/index.js +3 -1
  45. package/dist/contracts/organ-system.d.ts +49 -0
  46. package/dist/contracts/organ-system.js +67 -0
  47. package/dist/contracts/report-blocks.d.ts +564 -0
  48. package/dist/contracts/report-blocks.js +117 -0
  49. package/dist/contracts/report-reference.d.ts +459 -0
  50. package/dist/contracts/report-reference.js +256 -0
  51. package/dist/contracts/schemas/chat-events.d.ts +2 -0
  52. package/dist/contracts/schemas/chat-events.js +2 -0
  53. package/dist/contracts/schemas/chat-parts.d.ts +8 -8
  54. package/dist/contracts/severity.d.ts +15 -0
  55. package/dist/contracts/severity.js +10 -0
  56. package/dist/contracts/target-dossier.d.ts +8673 -12970
  57. package/dist/contracts/target-dossier.js +534 -591
  58. package/dist/data/safety-panel-schema.d.ts +18 -5
  59. package/dist/data/safety-panel-schema.js +4 -4
  60. package/dist/execution/artifact-metadata.js +1 -0
  61. package/dist/execution/report-runner.js +1 -3
  62. package/dist/execution/run-synthesis.d.ts +3 -0
  63. package/dist/execution/run-synthesis.js +3 -0
  64. package/dist/index.d.ts +12 -1
  65. package/dist/index.js +22 -0
  66. package/dist/lib/async-utils.d.ts +16 -4
  67. package/dist/lib/async-utils.js +20 -15
  68. package/dist/lib/cache.d.ts +8 -0
  69. package/dist/lib/cache.js +35 -0
  70. package/dist/lib/host-concurrency.js +1 -0
  71. package/dist/lib/rate-limit.d.ts +35 -0
  72. package/dist/lib/rate-limit.js +121 -0
  73. package/dist/literature/sources/arxiv.d.ts +22 -0
  74. package/dist/literature/sources/arxiv.js +103 -0
  75. package/dist/literature/sources/http.d.ts +28 -0
  76. package/dist/literature/sources/http.js +97 -0
  77. package/dist/literature/sources/pubmed.d.ts +68 -0
  78. package/dist/literature/sources/pubmed.js +266 -0
  79. package/dist/literature/sources/semantic-scholar.d.ts +24 -0
  80. package/dist/literature/sources/semantic-scholar.js +71 -0
  81. package/dist/loop/__fixtures__/scripted-provider.js +23 -2
  82. package/dist/loop/run-agent.js +58 -10
  83. package/dist/loop/tool-detail.d.ts +40 -0
  84. package/dist/loop/tool-detail.js +35 -3
  85. package/dist/loop/types.d.ts +13 -0
  86. package/dist/memory/thread-history.d.ts +10 -0
  87. package/dist/memory/thread-history.js +12 -1
  88. package/dist/memory/thread-store.d.ts +142 -25
  89. package/dist/memory/thread-store.js +228 -40
  90. package/dist/prompts/literature-reviewer.d.ts +1 -1
  91. package/dist/prompts/literature-reviewer.js +2 -0
  92. package/dist/prompts/target-assessment/briefs/executive-recommendation.d.ts +1 -1
  93. package/dist/prompts/target-assessment/briefs/executive-recommendation.js +5 -5
  94. package/dist/prompts/target-assessment/briefs/liability-bullets.d.ts +1 -1
  95. package/dist/prompts/target-assessment/briefs/liability-bullets.js +1 -1
  96. package/dist/prompts/target-assessment/briefs/target-organ-liabilities.d.ts +1 -1
  97. package/dist/prompts/target-assessment/briefs/target-organ-liabilities.js +11 -3
  98. package/dist/prompts/target-assessment/investigation/adversarial-critique.d.ts +1 -0
  99. package/dist/prompts/target-assessment/investigation/adversarial-critique.js +67 -0
  100. package/dist/prompts/target-assessment/investigation/claim-reverification.d.ts +1 -0
  101. package/dist/prompts/target-assessment/investigation/claim-reverification.js +49 -0
  102. package/dist/prompts/target-assessment/investigation/index.d.ts +3 -0
  103. package/dist/prompts/target-assessment/investigation/index.js +3 -0
  104. package/dist/prompts/target-assessment/investigation/mechanism-proposal.d.ts +1 -0
  105. package/dist/prompts/target-assessment/investigation/mechanism-proposal.js +49 -0
  106. package/dist/prompts/target-assessment/tox-voice/index.d.ts +1 -1
  107. package/dist/prompts/target-assessment/tox-voice/vocabulary.d.ts +8 -5
  108. package/dist/prompts/target-assessment/tox-voice/vocabulary.js +9 -20
  109. package/dist/providers/__fixtures__/session.d.ts +5 -4
  110. package/dist/providers/__fixtures__/session.js +4 -4
  111. package/dist/report-model/block-walk.d.ts +56 -0
  112. package/dist/report-model/block-walk.js +89 -0
  113. package/dist/report-model/draft-finish.d.ts +60 -0
  114. package/dist/report-model/draft-finish.js +56 -0
  115. package/dist/report-model/draft-operations.d.ts +125 -0
  116. package/dist/report-model/draft-operations.js +341 -0
  117. package/dist/report-model/draft-read.d.ts +59 -0
  118. package/dist/report-model/draft-read.js +109 -0
  119. package/dist/report-model/draft.d.ts +51 -0
  120. package/dist/report-model/draft.js +32 -0
  121. package/dist/report-model/fixture-resolver.d.ts +8 -0
  122. package/dist/report-model/fixture-resolver.js +252 -0
  123. package/dist/report-model/mint-snapshot.d.ts +30 -0
  124. package/dist/report-model/mint-snapshot.js +40 -0
  125. package/dist/report-model/reference-resolver.d.ts +113 -0
  126. package/dist/report-model/reference-resolver.js +60 -0
  127. package/dist/report-model/structural-validation.d.ts +22 -0
  128. package/dist/report-model/structural-validation.js +68 -0
  129. package/dist/report-model/validate.d.ts +40 -0
  130. package/dist/report-model/validate.js +80 -0
  131. package/dist/report-render/chart.d.ts +33 -0
  132. package/dist/report-render/chart.js +538 -0
  133. package/dist/report-render/page.d.ts +127 -0
  134. package/dist/report-render/page.js +307 -0
  135. package/dist/report-render/references.d.ts +32 -0
  136. package/dist/report-render/references.js +44 -0
  137. package/dist/report-render/render.d.ts +25 -0
  138. package/dist/report-render/render.js +125 -0
  139. package/dist/report-render/script-json.d.ts +13 -0
  140. package/dist/report-render/script-json.js +15 -0
  141. package/dist/report-render/types.d.ts +39 -0
  142. package/dist/report-render/types.js +10 -0
  143. package/dist/report-render/views/chart-view.d.ts +11 -0
  144. package/dist/report-render/views/chart-view.js +23 -0
  145. package/dist/report-render/views/page-view.d.ts +20 -0
  146. package/dist/report-render/views/page-view.js +49 -0
  147. package/dist/report-render/views/prose.d.ts +28 -0
  148. package/dist/report-render/views/prose.js +75 -0
  149. package/dist/report-render/views/references-view.d.ts +20 -0
  150. package/dist/report-render/views/references-view.js +64 -0
  151. package/dist/report-render/views/values.d.ts +42 -0
  152. package/dist/report-render/views/values.js +43 -0
  153. package/dist/runtime/assemble.d.ts +50 -4
  154. package/dist/runtime/assemble.js +47 -3
  155. package/dist/runtime/boot.d.ts +3 -3
  156. package/dist/runtime/boot.js +22 -5
  157. package/dist/runtime/dbos.d.ts +4 -4
  158. package/dist/state/artifacts.d.ts +16 -0
  159. package/dist/state/artifacts.js +18 -0
  160. package/dist/state/init.js +77 -1
  161. package/dist/state/purge-analysis.js +1 -0
  162. package/dist/state/report-versions.d.ts +132 -0
  163. package/dist/state/report-versions.js +194 -0
  164. package/dist/state/schema.d.ts +2 -2
  165. package/dist/tasks/data-profile.js +1 -0
  166. package/dist/tools/bio/chembl.js +1 -0
  167. package/dist/tools/bio/comptox.js +1 -0
  168. package/dist/tools/bio/drug-gene-interactions.js +1 -0
  169. package/dist/tools/bio/gene-disease-evidence.js +1 -0
  170. package/dist/tools/bio/gene-preclinical-profile.d.ts +1 -1
  171. package/dist/tools/bio/gene-preclinical-profile.js +1 -0
  172. package/dist/tools/bio/keys.d.ts +4 -2
  173. package/dist/tools/bio/keys.js +3 -2
  174. package/dist/tools/bio/lookup-annotation.js +1 -0
  175. package/dist/tools/bio/opentargets.d.ts +3 -3
  176. package/dist/tools/bio/opentargets.js +4 -3
  177. package/dist/tools/bio/pubchem.js +1 -0
  178. package/dist/tools/bio/pubmed.js +4 -4
  179. package/dist/tools/bio/search-clinical-trials.js +1 -0
  180. package/dist/tools/bio/search-faers.js +1 -0
  181. package/dist/tools/bio/search-geo-datasets.js +1 -0
  182. package/dist/tools/bio/search-interactions.js +1 -0
  183. package/dist/tools/bio/target-safety.d.ts +4 -6
  184. package/dist/tools/bio/target-safety.js +7 -6
  185. package/dist/tools/define-tool.d.ts +19 -12
  186. package/dist/tools/define-tool.js +4 -2
  187. package/dist/tools/display/show-user.js +1 -0
  188. package/dist/tools/iterate-report.js +2 -0
  189. package/dist/tools/lib/chembl-client.d.ts +2 -0
  190. package/dist/tools/lib/chembl-client.js +1 -0
  191. package/dist/tools/lib/monarch-client.d.ts +42 -0
  192. package/dist/tools/lib/monarch-client.js +100 -0
  193. package/dist/tools/lib/ncbi-utils.d.ts +3 -58
  194. package/dist/tools/lib/ncbi-utils.js +2 -286
  195. package/dist/tools/lib/pubmed-client.d.ts +5 -12
  196. package/dist/tools/lib/pubmed-client.js +20 -79
  197. package/dist/tools/report/build-report.js +1 -0
  198. package/dist/tools/report/mint-preview-url.js +1 -0
  199. package/dist/tools/report/preview-snapshot.js +1 -0
  200. package/dist/tools/report/submit-report.js +1 -0
  201. package/dist/tools/report/version-fs.js +4 -0
  202. package/dist/tools/report-authoring/authoring-tools.d.ts +134 -0
  203. package/dist/tools/report-authoring/authoring-tools.js +352 -0
  204. package/dist/tools/research/context7-docs.js +2 -0
  205. package/dist/tools/research/generate-analogy-report.js +3 -2
  206. package/dist/tools/research/generate-plan.js +3 -0
  207. package/dist/tools/research/index.d.ts +1 -0
  208. package/dist/tools/research/index.js +1 -0
  209. package/dist/tools/research/inspect-data-profile.js +4 -0
  210. package/dist/tools/research/inspect-run.d.ts +1 -1
  211. package/dist/tools/research/literature-reviewer.d.ts +3 -0
  212. package/dist/tools/research/literature-reviewer.js +3 -0
  213. package/dist/tools/research/resolve-citation.d.ts +3 -0
  214. package/dist/tools/research/resolve-citation.js +15 -0
  215. package/dist/tools/research/search-arxiv.d.ts +3 -19
  216. package/dist/tools/research/search-arxiv.js +10 -105
  217. package/dist/tools/research/search-github-repos.js +1 -0
  218. package/dist/tools/research/search-semantic-scholar.d.ts +8 -33
  219. package/dist/tools/research/search-semantic-scholar.js +30 -88
  220. package/dist/tools/sandbox/list-available-packages.js +23 -0
  221. package/dist/tools/sandbox/list-available-refs.js +37 -2
  222. package/dist/tools/sandbox/report-blocker.js +1 -0
  223. package/dist/tools/sandbox/skills.js +2 -0
  224. package/dist/tools/workspace/file-stat.js +1 -0
  225. package/dist/tools/workspace/show-file.js +2 -0
  226. package/dist/tools/workspace/show-plan.js +1 -0
  227. package/dist/workflows/execute-analysis.d.ts +8 -1
  228. package/dist/workflows/execute-analysis.js +6 -5
  229. package/dist/workflows/execute-target-assessment.d.ts +13 -2
  230. package/dist/workflows/execute-target-assessment.js +78 -16
  231. package/dist/workflows/sandbox-step.d.ts +5 -0
  232. package/dist/workflows/sandbox-step.js +1 -0
  233. package/dist/workflows/target-assessment/assemblers/index.d.ts +1 -1
  234. package/dist/workflows/target-assessment/assemblers/index.js +1 -1
  235. package/dist/workflows/target-assessment/assemblers/literature.d.ts +9 -27
  236. package/dist/workflows/target-assessment/assemblers/literature.js +32 -48
  237. package/dist/workflows/target-assessment/assemblers/orchestrator.d.ts +17 -9
  238. package/dist/workflows/target-assessment/assemblers/orchestrator.js +195 -85
  239. package/dist/workflows/target-assessment/assemblers/safety.d.ts +70 -48
  240. package/dist/workflows/target-assessment/assemblers/safety.js +125 -42
  241. package/dist/workflows/target-assessment/collectors/index.d.ts +5 -2
  242. package/dist/workflows/target-assessment/collectors/index.js +48 -5
  243. package/dist/workflows/target-assessment/coverage.d.ts +43 -27
  244. package/dist/workflows/target-assessment/coverage.js +33 -29
  245. package/dist/workflows/target-assessment/fanout/index.js +1 -0
  246. package/dist/workflows/target-assessment/investigation/index.d.ts +97 -0
  247. package/dist/workflows/target-assessment/investigation/index.js +419 -0
  248. package/dist/workflows/target-assessment/lib/approval-precedents.d.ts +26 -5
  249. package/dist/workflows/target-assessment/lib/approval-precedents.js +95 -29
  250. package/dist/workflows/target-assessment/lib/citation-validator.js +1 -1
  251. package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +2 -2
  252. package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.d.ts +2 -3
  253. package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.js +0 -4
  254. package/dist/workflows/target-assessment/lib/compute-derived.d.ts +6 -5
  255. package/dist/workflows/target-assessment/lib/compute-derived.js +14 -5
  256. package/dist/workflows/target-assessment/lib/family-complex-supplement.d.ts +5 -6
  257. package/dist/workflows/target-assessment/lib/family-complex-supplement.js +2 -3
  258. package/dist/workflows/target-assessment/lib/fda-label-safety.d.ts +72 -0
  259. package/dist/workflows/target-assessment/lib/fda-label-safety.js +166 -0
  260. package/dist/workflows/target-assessment/lib/heterodimer-filter.d.ts +0 -9
  261. package/dist/workflows/target-assessment/lib/heterodimer-filter.js +0 -10
  262. package/dist/workflows/target-assessment/lib/hpo-organ-map.d.ts +34 -0
  263. package/dist/workflows/target-assessment/lib/hpo-organ-map.js +63 -0
  264. package/dist/workflows/target-assessment/lib/impc-organ-map.d.ts +19 -0
  265. package/dist/workflows/target-assessment/lib/impc-organ-map.js +39 -0
  266. package/dist/workflows/target-assessment/lib/meddra-organ-map.d.ts +24 -14
  267. package/dist/workflows/target-assessment/lib/meddra-organ-map.js +59 -47
  268. package/dist/workflows/target-assessment/lib/recommendation-sanitizer.js +1 -1
  269. package/dist/workflows/target-assessment/lib/regulatory-actions.d.ts +2 -3
  270. package/dist/workflows/target-assessment/lib/regulatory-actions.js +33 -4
  271. package/dist/workflows/target-assessment/lib/safety-corroboration.d.ts +44 -0
  272. package/dist/workflows/target-assessment/lib/safety-corroboration.js +273 -0
  273. package/dist/workflows/target-assessment/lib/target-identity-filter.d.ts +5 -7
  274. package/dist/workflows/target-assessment/lib/target-identity-filter.js +5 -7
  275. package/dist/workflows/target-assessment/metrics.d.ts +1 -6
  276. package/dist/workflows/target-assessment/metrics.js +1 -13
  277. package/dist/workflows/target-assessment/phase4-assemble.d.ts +2149 -809
  278. package/dist/workflows/target-assessment/phase4-assemble.js +19 -19
  279. package/dist/workflows/target-assessment/phase5-persist.d.ts +20 -21
  280. package/dist/workflows/target-assessment/phase5-persist.js +56 -173
  281. package/dist/workflows/target-assessment/schemas.d.ts +48 -3
  282. package/dist/workflows/target-assessment/schemas.js +19 -0
  283. package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.d.ts +1 -0
  284. package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.js +2 -0
  285. package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +27 -63
  286. package/dist/workflows/target-assessment/steps/phase2-aggregate.js +0 -4
  287. package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +28 -155
  288. package/dist/workflows/target-assessment/steps/phase3-aggregate.js +0 -6
  289. package/dist/workflows/target-assessment/steps/synthesis/diagnostics.d.ts +0 -6
  290. package/dist/workflows/target-assessment/steps/synthesis/diagnostics.js +0 -18
  291. package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.d.ts +1 -0
  292. package/dist/workflows/target-assessment/synthesis/index.d.ts +52 -5
  293. package/dist/workflows/target-assessment/synthesis/index.js +4 -1
  294. package/dist/workflows/target-assessment/synthesis-probes.d.ts +3 -3
  295. package/package.json +4 -1
@@ -27,6 +27,7 @@ import { genePreclinicalProfileTool } from "../bio/gene-preclinical-profile.js";
27
27
  import { lookupAnnotationTool } from "../bio/lookup-annotation.js";
28
28
  import { searchGeneTool } from "../bio/search-gene.js";
29
29
  import { searchInteractionsTool } from "../bio/search-interactions.js";
30
+ import { createResolveCitationTool } from "./resolve-citation.js";
30
31
  /** Sub-agent identity — appended to `callPath`, set as `agentId`. */
31
32
  const AGENT_ID = "literature-reviewer";
32
33
  /** Multi-tool research budget — search → read → synthesize over many genes. */
@@ -40,6 +41,7 @@ export function createLiteratureReviewerTool(deps) {
40
41
  lookupAnnotationTool,
41
42
  searchInteractionsTool,
42
43
  ncbi.pubmed,
44
+ createResolveCitationTool(deps.citationResolver),
43
45
  chemDb.drugGeneInteractions,
44
46
  genePreclinicalProfileTool,
45
47
  ];
@@ -66,6 +68,7 @@ export function createLiteratureReviewerTool(deps) {
66
68
  "biological features to investigate and the experimental " +
67
69
  "context. The reviewer sees only this — include everything it needs."),
68
70
  }),
71
+ describeCall: "none",
69
72
  execute: async ({ brief }, ctx) => {
70
73
  const { messages: transcript } = await runAgent(agent, [{ role: "user", content: brief }], forSubAgent(ctx.session, AGENT_ID), {
71
74
  provider: deps.provider,
@@ -0,0 +1,3 @@
1
+ import { type CitationResolver } from "../../citations/types.js";
2
+ import { type Tool } from "../define-tool.js";
3
+ export declare function createResolveCitationTool(resolver: CitationResolver): Tool;
@@ -0,0 +1,15 @@
1
+ import { ok } from "neverthrow";
2
+ import { CitationInputSchema } from "../../citations/types.js";
3
+ import { defineTool } from "../define-tool.js";
4
+ export function createResolveCitationTool(resolver) {
5
+ return defineTool({
6
+ id: "resolve_citation",
7
+ description: "Verify one supplied citation against applicable bibliographic authorities. " +
8
+ "Use this for 'does this reference exist and does its metadata agree?', not for topical literature discovery. " +
9
+ "Returns source-labelled records, field comparisons, conflicts, coverage, and one of verified, metadata_mismatch, " +
10
+ "not_found, unverifiable, or inconclusive. Source failure never becomes not_found.",
11
+ inputSchema: CitationInputSchema,
12
+ describeCall: ({ citation }) => `verify ${citation}`,
13
+ execute: async (input, ctx) => ok(await resolver.resolveOne(input, { signal: ctx.signal })),
14
+ });
15
+ }
@@ -1,10 +1,6 @@
1
- /**
2
- * Search arXiv for preprints. Returns Atom XML which we parse without a
3
- * dependency. arXiv is the primary source for ML / physics / math /
4
- * control-theory preprints; Semantic Scholar lags it by months.
5
- *
6
- * Same wire call and envelope as the legacy tool. Stateless HTTP; no deps.
7
- */
1
+ /** Search arXiv for preprints through the shared authority client. */
2
+ import { type ArxivPaper } from "../../literature/sources/arxiv.js";
3
+ export { parseArxivAtom, type ArxivPaper } from "../../literature/sources/arxiv.js";
8
4
  type SearchArxivOutput = {
9
5
  success: false;
10
6
  error: string;
@@ -13,20 +9,8 @@ type SearchArxivOutput = {
13
9
  success: true;
14
10
  papers: ArxivPaper[];
15
11
  };
16
- export interface ArxivPaper {
17
- id: string;
18
- title: string;
19
- abstract: string;
20
- authors: string[];
21
- published: string;
22
- categories: string[];
23
- url: string;
24
- pdfUrl?: string;
25
- }
26
- export declare function parseArxivAtom(xml: string): ArxivPaper[];
27
12
  export declare const searchArxivTool: import("../define-tool.js").Tool<{
28
13
  query: string;
29
14
  limit: number;
30
15
  categories?: string[] | undefined;
31
16
  }, SearchArxivOutput>;
32
- export {};
@@ -1,96 +1,10 @@
1
- /**
2
- * Search arXiv for preprints. Returns Atom XML which we parse without a
3
- * dependency. arXiv is the primary source for ML / physics / math /
4
- * control-theory preprints; Semantic Scholar lags it by months.
5
- *
6
- * Same wire call and envelope as the legacy tool. Stateless HTTP; no deps.
7
- */
1
+ /** Search arXiv for preprints through the shared authority client. */
8
2
  import { ok } from "neverthrow";
9
3
  import { z } from "zod";
4
+ import { createArxivSource } from "../../literature/sources/arxiv.js";
10
5
  import { defineTool } from "../define-tool.js";
11
- import { apiFetch, describeApiError } from "../lib/api-utils.js";
12
- const BASE_URL = "https://export.arxiv.org/api/query";
13
- const stripCdata = (s) => s.replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, "$1");
14
- const decodeXmlEntities = (s) => s
15
- .replace(/&lt;/g, "<")
16
- .replace(/&gt;/g, ">")
17
- .replace(/&quot;/g, '"')
18
- .replace(/&apos;/g, "'")
19
- .replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCodePoint(parseInt(h, 16)))
20
- .replace(/&#(\d+);/g, (_, d) => String.fromCodePoint(parseInt(d, 10)))
21
- .replace(/&amp;/g, "&"); // amp last so we don't double-decode
22
- const collapseWs = (s) => decodeXmlEntities(stripCdata(s)).replace(/\s+/g, " ").trim();
23
- const extractEntries = (xml) => {
24
- const re = /<entry\b[^>]*>([\s\S]*?)<\/entry>/g;
25
- const out = [];
26
- let m;
27
- while ((m = re.exec(xml)) !== null)
28
- out.push(m[1]);
29
- return out;
30
- };
31
- const firstMatch = (entry, tag) => {
32
- const re = new RegExp(`<${tag}\\b[^>]*>([\\s\\S]*?)</${tag}>`);
33
- const m = entry.match(re);
34
- return m?.[1];
35
- };
36
- const allMatches = (entry, tag) => {
37
- const re = new RegExp(`<${tag}\\b[^>]*>([\\s\\S]*?)</${tag}>`, "g");
38
- const out = [];
39
- let m;
40
- while ((m = re.exec(entry)) !== null)
41
- out.push(m[1]);
42
- return out;
43
- };
44
- const linkAttr = (entry, predicate) => {
45
- const re = /<link\b([^>]*)\/>/g;
46
- let m;
47
- while ((m = re.exec(entry)) !== null) {
48
- if (predicate(m[1])) {
49
- const hrefMatch = m[1].match(/href="([^"]+)"/);
50
- if (hrefMatch)
51
- return hrefMatch[1];
52
- }
53
- }
54
- return undefined;
55
- };
56
- const categoryTerms = (entry) => {
57
- const re = /<category\b([^>]*)\/>/g;
58
- const out = [];
59
- let m;
60
- while ((m = re.exec(entry)) !== null) {
61
- const termMatch = m[1].match(/term="([^"]+)"/);
62
- if (termMatch)
63
- out.push(termMatch[1]);
64
- }
65
- return out;
66
- };
67
- export function parseArxivAtom(xml) {
68
- return extractEntries(xml).flatMap((entry) => {
69
- const idRaw = firstMatch(entry, "id");
70
- const title = firstMatch(entry, "title");
71
- if (!idRaw || !title)
72
- return [];
73
- const idMatch = idRaw.match(/abs\/(.+)$/);
74
- const authors = allMatches(entry, "author")
75
- .map((a) => firstMatch(a, "name"))
76
- .filter((n) => !!n)
77
- .map(collapseWs);
78
- const htmlUrl = linkAttr(entry, (a) => /rel="alternate"/.test(a) && /type="text\/html"/.test(a));
79
- const pdfUrl = linkAttr(entry, (a) => /title="pdf"/.test(a));
80
- return [
81
- {
82
- id: idMatch?.[1] ?? collapseWs(idRaw),
83
- title: collapseWs(title),
84
- abstract: collapseWs(firstMatch(entry, "summary") ?? ""),
85
- authors,
86
- published: collapseWs(firstMatch(entry, "published") ?? ""),
87
- categories: categoryTerms(entry),
88
- url: htmlUrl ?? collapseWs(idRaw),
89
- pdfUrl,
90
- },
91
- ];
92
- });
93
- }
6
+ export { parseArxivAtom } from "../../literature/sources/arxiv.js";
7
+ const source = createArxivSource({ maxRetries: 3, retryDelayMs: 1_000, timeoutMs: 90_000 });
94
8
  export const searchArxivTool = defineTool({
95
9
  id: "search_arxiv",
96
10
  description: "Search arXiv for preprints in ML, physics, math, control theory, " +
@@ -104,20 +18,11 @@ export const searchArxivTool = defineTool({
104
18
  .describe('Optional arXiv category filters (e.g., ["cs.LG", "math.OC"]). ' + "Combined with the query using AND."),
105
19
  limit: z.number().int().min(1).max(20).default(10).describe("Maximum results (1–20, default 10)."),
106
20
  }),
107
- execute: async ({ query, categories, limit }) => {
108
- const catPart = categories && categories.length > 0 ? ` AND (${categories.map((c) => `cat:${c}`).join(" OR ")})` : "";
109
- const params = new URLSearchParams({
110
- search_query: `all:${query}${catPart}`,
111
- max_results: String(limit),
112
- sortBy: "relevance",
113
- sortOrder: "descending",
114
- });
115
- const res = await apiFetch(`${BASE_URL}?${params}`, {
116
- parseAs: "text",
117
- });
118
- if (res.isErr()) {
119
- return ok({ success: false, error: describeApiError(res.error), papers: [] });
120
- }
121
- return ok({ success: true, papers: parseArxivAtom(res.value) });
21
+ describeCall: "none",
22
+ execute: async ({ query, categories, limit }, context) => {
23
+ const result = await source.search({ query, ...(categories === undefined ? {} : { categories }), limit }, context.signal);
24
+ if (result.status !== "ok")
25
+ return ok({ success: false, error: result.detail, papers: [] });
26
+ return ok({ success: true, papers: result.value });
122
27
  },
123
28
  });
@@ -61,6 +61,7 @@ export function createSearchGithubReposTool(deps) {
61
61
  language: z.string().optional().describe('Optional language filter (e.g., "python").'),
62
62
  limit: z.number().int().min(1).max(20).default(10).describe("Maximum results (1–20, default 10)."),
63
63
  }),
64
+ describeCall: "none",
64
65
  execute: async ({ query, language, limit }) => {
65
66
  const q = language ? `${query} language:${language}` : query;
66
67
  const params = new URLSearchParams({
@@ -1,33 +1,8 @@
1
- /**
2
- * Search Semantic Scholar Graph API for academic papers across all
3
- * sciences. No API key required; rate-limited but reasonable for agent
4
- * use. Used by the analogical-reasoner for cross-domain analogical search.
5
- *
6
- * Same wire call and envelope as the legacy tool, packaged as `defineTool`.
7
- * Stateless HTTP; no deps.
8
- */
9
- export interface SemanticScholarPaper {
10
- id: string;
11
- title: string;
12
- abstract?: string;
13
- year?: number;
14
- venue?: string;
15
- citationCount?: number;
16
- url?: string;
17
- authors: string[];
18
- externalIds?: Record<string, string>;
19
- }
20
- type SearchSemanticScholarOutput = {
21
- success: false;
22
- error: string;
23
- papers: SemanticScholarPaper[];
24
- } | {
25
- success: true;
26
- papers: SemanticScholarPaper[];
27
- };
28
- export declare function parseSemanticScholarResponse(raw: unknown): SemanticScholarPaper[];
29
- export declare const searchSemanticScholarTool: import("../define-tool.js").Tool<{
30
- query: string;
31
- limit: number;
32
- }, SearchSemanticScholarOutput>;
33
- export {};
1
+ /** Search Semantic Scholar through the shared authority client. */
2
+ import { type SemanticScholarSearchPaper } from "../../literature/sources/semantic-scholar.js";
3
+ import { type Tool } from "../define-tool.js";
4
+ export { parseSemanticScholarResponse } from "../../literature/sources/semantic-scholar.js";
5
+ export type SemanticScholarPaper = SemanticScholarSearchPaper;
6
+ export declare function createSearchSemanticScholarTool(options?: {
7
+ readonly apiKey?: string;
8
+ }): Tool;
@@ -1,93 +1,35 @@
1
- /**
2
- * Search Semantic Scholar Graph API for academic papers across all
3
- * sciences. No API key required; rate-limited but reasonable for agent
4
- * use. Used by the analogical-reasoner for cross-domain analogical search.
5
- *
6
- * Same wire call and envelope as the legacy tool, packaged as `defineTool`.
7
- * Stateless HTTP; no deps.
8
- */
1
+ /** Search Semantic Scholar through the shared authority client. */
9
2
  import { ok } from "neverthrow";
10
3
  import { z } from "zod";
4
+ import { createSemanticScholarSource } from "../../literature/sources/semantic-scholar.js";
11
5
  import { defineTool } from "../define-tool.js";
12
- import { apiFetchValidated, describeApiError } from "../lib/api-utils.js";
13
- const BASE_URL = "https://api.semanticscholar.org/graph/v1";
14
- const DEFAULT_FIELDS = "paperId,title,abstract,year,venue,citationCount,url,authors,externalIds";
15
- // Raw Semantic Scholar wire shape, validated at the fetch boundary. Every field
16
- // is optional — `parseSemanticScholarResponse` already filters/coerces defensively,
17
- // so a partial-but-valid response must still parse; `externalIds` values stay
18
- // unconstrained (`z.unknown`) since the mapper only checks the container is an object.
19
- // The scalar fields are also `.nullable()`: Semantic Scholar sends an explicit
20
- // `null` for a missing abstract/year/venue/url/externalIds (very common), which a
21
- // bare `.optional()` would reject — failing the whole `data` array on one such paper.
22
- const SemanticScholarResponseSchema = z.object({
23
- data: z
24
- .array(z.object({
25
- paperId: z.string().optional(),
26
- title: z.string().optional(),
27
- abstract: z.string().nullable().optional(),
28
- year: z.number().nullable().optional(),
29
- venue: z.string().nullable().optional(),
30
- citationCount: z.number().nullable().optional(),
31
- url: z.string().nullable().optional(),
32
- authors: z.array(z.object({ name: z.string().nullable().optional() })).optional(),
33
- externalIds: z.record(z.string(), z.unknown()).nullable().optional(),
34
- }))
35
- .optional(),
36
- });
37
- export function parseSemanticScholarResponse(raw) {
38
- const data = raw?.data;
39
- if (!Array.isArray(data))
40
- return [];
41
- return data
42
- .filter((p) => typeof p === "object" && p !== null && "paperId" in p && "title" in p)
43
- .map((p) => {
44
- const authors = Array.isArray(p.authors)
45
- ? p.authors.map((a) => a?.name).filter((n) => typeof n === "string")
46
- : [];
47
- return {
48
- id: String(p.paperId),
49
- title: String(p.title),
50
- abstract: typeof p.abstract === "string" ? p.abstract : undefined,
51
- year: typeof p.year === "number" ? p.year : undefined,
52
- venue: typeof p.venue === "string" ? p.venue : undefined,
53
- citationCount: typeof p.citationCount === "number" ? p.citationCount : undefined,
54
- url: typeof p.url === "string" ? p.url : undefined,
55
- authors,
56
- externalIds: p.externalIds && typeof p.externalIds === "object" ? p.externalIds : undefined,
57
- };
6
+ export { parseSemanticScholarResponse } from "../../literature/sources/semantic-scholar.js";
7
+ export function createSearchSemanticScholarTool(options = {}) {
8
+ const source = createSemanticScholarSource({
9
+ maxRetries: 3,
10
+ retryDelayMs: 1_000,
11
+ timeoutMs: 90_000,
12
+ ...(options.apiKey === undefined ? {} : { apiKey: options.apiKey }),
13
+ });
14
+ return defineTool({
15
+ id: "search_semantic_scholar",
16
+ description: "Search Semantic Scholar for academic papers across all sciences " +
17
+ "(biology, ML, physics, math, economics, engineering, etc.). Returns " +
18
+ "paper id, title, abstract, year, venue, citation count, authors, and " +
19
+ "external IDs (DOI, ArXiv). Best for cross-domain analogical search.",
20
+ inputSchema: z.object({
21
+ query: z
22
+ .string()
23
+ .describe("Free-text query. Use natural language phrases such as " +
24
+ '"reinforcement learning for control" or "feedback stabilisation in oscillators".'),
25
+ limit: z.number().int().min(1).max(20).default(10).describe("Maximum number of results to return (1–20, default 10)."),
26
+ }),
27
+ describeCall: "none",
28
+ execute: async ({ query, limit }, context) => {
29
+ const result = await source.search(query, limit, context.signal);
30
+ if (result.status !== "ok")
31
+ return ok({ success: false, error: result.detail, papers: [] });
32
+ return ok({ success: true, papers: result.value });
33
+ },
58
34
  });
59
35
  }
60
- export const searchSemanticScholarTool = defineTool({
61
- id: "search_semantic_scholar",
62
- description: "Search Semantic Scholar for academic papers across all sciences " +
63
- "(biology, ML, physics, math, economics, engineering, etc.). Returns " +
64
- "paper id, title, abstract, year, venue, citation count, authors, and " +
65
- "external IDs (DOI, ArXiv). Best for cross-domain analogical search.",
66
- inputSchema: z.object({
67
- query: z
68
- .string()
69
- .describe("Free-text query. Use natural language phrases such as " + '"reinforcement learning for control" or "feedback stabilisation in oscillators".'),
70
- limit: z.number().int().min(1).max(20).default(10).describe("Maximum number of results to return (1–20, default 10)."),
71
- }),
72
- execute: async ({ query, limit }) => {
73
- const params = new URLSearchParams({
74
- query,
75
- limit: String(limit),
76
- fields: DEFAULT_FIELDS,
77
- });
78
- const apiKey = process.env.SEMANTIC_SCHOLAR_API_KEY;
79
- const headers = {};
80
- if (apiKey)
81
- headers["x-api-key"] = apiKey;
82
- const res = await apiFetchValidated(`${BASE_URL}/paper/search?${params}`, SemanticScholarResponseSchema, {
83
- headers,
84
- });
85
- if (res.isErr()) {
86
- return ok({ success: false, error: describeApiError(res.error), papers: [] });
87
- }
88
- return ok({
89
- success: true,
90
- papers: parseSemanticScholarResponse(res.value),
91
- });
92
- },
93
- });
@@ -26,6 +26,7 @@ import { readFile } from "node:fs/promises";
26
26
  import { ok } from "neverthrow";
27
27
  import { z } from "zod";
28
28
  import { defineTool } from "../define-tool.js";
29
+ import { capCodePoints, DETAIL_NEEDLE_MAX_LENGTH } from "../../loop/tool-detail.js";
29
30
  /**
30
31
  * Where the list lives when the host mounts the library store at the same path
31
32
  * the sandbox does. Hosts that do not — the store is baked into the image and
@@ -187,6 +188,28 @@ export function createListAvailablePackagesTool(deps = {}) {
187
188
  .optional()
188
189
  .describe(`Cap the packages listed. Omit to get the whole set — the store fits well within the ${DEFAULT_LIMIT} ceiling. Ignored when \`names\` is given.`),
189
190
  }),
191
+ // `queryPackages` returns on `names` before it reads another field, thus
192
+ // the presence check comes first. An empty array is not a presence check.
193
+ // `names` are exact identifiers a caller checks for, thus they name the
194
+ // call as they are. A blank query counts as absent, because `queryPackages`
195
+ // trims the needle and applies no filter when nothing is left.
196
+ //
197
+ // A needle rides behind `matching "…"`, because a bare needle reads as a
198
+ // package name. The needle takes its own bound, thus the emit-site cap
199
+ // cannot cut inside the mark and leave the quote open. `language` stays a
200
+ // trailing qualifier, exactly as `execute` treats it.
201
+ describeCall: ({ names, query, language }) => {
202
+ if (names !== undefined && names.length > 0)
203
+ return names.join(", ");
204
+ const needle = query?.trim();
205
+ if (needle !== undefined && needle !== "") {
206
+ const marked = `matching "${capCodePoints(needle, DETAIL_NEEDLE_MAX_LENGTH)}"`;
207
+ return language === undefined ? marked : `${marked} (${language})`;
208
+ }
209
+ if (language !== undefined)
210
+ return `${language} packages`;
211
+ return "full package list";
212
+ },
190
213
  execute: async (input) => {
191
214
  // An unreadable inventory is an expected environment state — model it as an
192
215
  // `available: false` data variant telling the caller the set is UNKNOWN.
@@ -27,6 +27,7 @@ import { lstat, readFile, readdir } from "node:fs/promises";
27
27
  import { join as hostJoin, posix as posixPath } from "node:path";
28
28
  import { ok } from "neverthrow";
29
29
  import { z } from "zod";
30
+ import { capCodePoints, DETAIL_MAX_LENGTH, DETAIL_NEEDLE_MAX_LENGTH } from "../../loop/tool-detail.js";
30
31
  import { REFERENCE_DATA_CATALOG } from "../../reference-data/catalog.js";
31
32
  import { parseReferenceInstallReceipt } from "../../reference-data/receipt.js";
32
33
  import { defineTool } from "../define-tool.js";
@@ -318,9 +319,8 @@ async function scanStore(root, relative, recursive = false) {
318
319
  const entries = [];
319
320
  let scanned = 0;
320
321
  let pathBytes = 0;
321
- let truncated = false;
322
322
  const { values: children, cut } = await cappedEntries(target, Math.min(MAX_ENTRIES + 2, MAX_SCANNED_ENTRIES));
323
- truncated = cut;
323
+ let truncated = cut;
324
324
  for (const child of children) {
325
325
  const childRelative = relative === "." ? child.name : `${relative}/${child.name}`;
326
326
  if (childRelative === ".inflexa" || childRelative.startsWith(".inflexa/") || childRelative === "registry.json")
@@ -511,6 +511,41 @@ export function createListAvailableRefsTool(deps) {
511
511
  "The store is provisioned by the host and is NOT frozen: datasets can be added through the host's own provisioning path, and whatever is added shows up on a later call. " +
512
512
  "So before you report a gap as permanent, check whether any tool you hold reaches that provisioning path — offer that route if one exists, and call the gap permanent only when none does.",
513
513
  inputSchema: ListAvailableRefsInputSchema,
514
+ // `execute` resolves `path ?? category` for the subtree it inspects, and
515
+ // it ignores `category` when `path` is present. `query` is an ADDITIVE
516
+ // filter over that subtree, thus the target keeps naming the call and the
517
+ // filter rides beside it.
518
+ //
519
+ // A needle rides behind `matching "…"`: a bare needle reads as a path, and
520
+ // a filtered subtree must name both halves, not the target alone. The two
521
+ // halves join with a space and a word, as `grep` joins its own two halves.
522
+ // A separator glyph belongs to a host, which owns its line and its own
523
+ // vocabulary; the harness renders nothing and thus borrows nothing.
524
+ //
525
+ // Each half carries its own bound. The emit-site cap cuts the TAIL, and
526
+ // `path` admits 4096 bytes, thus one long target would swallow the very
527
+ // filter this detail exists to state. The needle takes a fixed budget, and
528
+ // the target takes what the bounded filter leaves. Then both halves reach
529
+ // a reader, and each one marks its own cut.
530
+ //
531
+ // A blank value counts as absent, because `execute` treats it that way: it
532
+ // browses the store root for a blank target, and it applies no filter for a
533
+ // blank needle. The precedence stays `??` and never `||`, thus a blank
534
+ // `path` still suppresses `category` exactly as `execute` does.
535
+ describeCall: ({ path, category, query }) => {
536
+ const target = path ?? category;
537
+ const named = target !== undefined && target.trim() !== "" ? target : undefined;
538
+ const needle = query?.trim();
539
+ const filter = needle === undefined || needle === "" ? undefined : `matching "${capCodePoints(needle, DETAIL_NEEDLE_MAX_LENGTH)}"`;
540
+ if (named === undefined)
541
+ return filter ?? "full reference store";
542
+ if (filter === undefined)
543
+ return named;
544
+ // The budget comes off the rendered filter, never off a count of its
545
+ // parts, thus it cannot drift from the template above. One code point
546
+ // pays for the joining space.
547
+ return `${capCodePoints(named, DETAIL_MAX_LENGTH - [...filter].length - 1)} ${filter}`;
548
+ },
514
549
  execute: async ({ path, category, query, limit }) => {
515
550
  // `category` is shorthand for a top-level path; an explicit `path` wins.
516
551
  const requestedInput = path ?? category;
@@ -33,6 +33,7 @@ export function createReportBlockerToolFor(deps) {
33
33
  id: "report_blocker",
34
34
  description: `Terminal. ${deps.blockedWhen} Pass a clear reason. Stop immediately after calling — take no further actions.`,
35
35
  inputSchema: ReportBlockerInputSchema,
36
+ describeCall: "none",
36
37
  execute: async (input) => {
37
38
  deps.record({ kind: "blocker", reason: input.reason });
38
39
  return ok({
@@ -54,6 +54,7 @@ export function createSkillTools(deps) {
54
54
  query: z.string().min(1).describe("Keywords to match (e.g. 'PyDESeq2 contrast syntax')."),
55
55
  topK: z.number().int().min(1).max(MAX_TOP_K).optional().describe(`Max results to return. Defaults to ${DEFAULT_TOP_K}.`),
56
56
  }),
57
+ describeCall: "none",
57
58
  execute: async ({ query, topK }) => {
58
59
  if (allowed.size === 0) {
59
60
  return ok({ status: "no_skills" });
@@ -114,6 +115,7 @@ export function createSkillTools(deps) {
114
115
  skill: z.string().min(1).describe("Skill name (one of your declared skills)."),
115
116
  path: z.string().min(1).describe("File path within the skill, e.g. 'SKILL.md' or 'references/pydeseq2-api.md'."),
116
117
  }),
118
+ describeCall: "none",
117
119
  execute: async ({ skill, path }) => {
118
120
  if (!allowed.has(skill)) {
119
121
  return ok({ status: "skill_not_declared", skill });
@@ -22,6 +22,7 @@ export function createFileStatTool(fs, workingDir) {
22
22
  "in bytes. Use before reading to size a file. Missing and out-of-scope " +
23
23
  "paths return a data variant, not an error.",
24
24
  inputSchema: FileStatInputSchema,
25
+ describeCall: "none",
25
26
  execute: async ({ path }, ctx) => {
26
27
  const result = unwrapOrThrow(await fs.stat({ session: ctx.session, path, workingDir }));
27
28
  switch (result.kind) {
@@ -38,6 +38,8 @@ export const showFileTool = defineTool({
38
38
  "Cards render in call order, so to interleave figures with prose alternate `show_user(markdown)` and `show_file` calls " +
39
39
  "(markdown image syntax pointing at a workspace file does NOT render — this tool is the only way to show one).",
40
40
  inputSchema: ShowFileInputSchema,
41
+ // `.min(1)` on `files` makes index 0 total after the parse.
42
+ describeCall: ({ files }) => (files.length === 1 ? files[0].path : `${files.length} files`),
41
43
  execute: async (input, ctx) => {
42
44
  const card = buildFileReferenceCardData(input);
43
45
  // A malformed/traversal path yields no card — an expected outcome the model can self-correct
@@ -32,6 +32,7 @@ export function createShowPlanTool(pool) {
32
32
  .describe("Plan ID (`pln-<8hex>`) from `generate_plan` (newly created) or `inspect_run` (each run carries the planId it ran)"),
33
33
  title: z.string().optional().describe("Optional card heading; defaults to the plan's own title"),
34
34
  }),
35
+ describeCall: ({ planId }) => planId,
35
36
  execute: async ({ planId, title }, ctx) => {
36
37
  const resourceId = scopeResource(ctx.session.scope).resourceId;
37
38
  // "Plan not found" / "plan no longer parses" are expected outcomes —
@@ -64,6 +64,7 @@ import type { RunSession } from "../auth/types.js";
64
64
  import type { RunAuthorizer } from "../execution/run-authorizer.js";
65
65
  import type { Logger } from "../lib/logger.js";
66
66
  import type { UsageRecorder } from "../billing/usage-recorder.js";
67
+ import type { CitationResolver } from "../citations/types.js";
67
68
  import type { SynthesisStatus, UpdateStepExecutionInput } from "../state/index.js";
68
69
  import type { AnalysisStep } from "../schemas/workflow-state.js";
69
70
  import type { ChatProvider, EmbeddingProvider } from "../providers/types.js";
@@ -277,6 +278,8 @@ export interface ExecuteAnalysisDeps {
277
278
  readonly synthesisModel: string;
278
279
  /** API keys for the bio/chem tools the embedded literature reviewer uses. */
279
280
  readonly bioKeys: BioToolKeys;
281
+ /** Shared resolver for the literature reviewer used during synthesis. */
282
+ readonly citationResolver: CitationResolver;
280
283
  /** Run-level billing-bracket seam (managed: external bracket; OSS: no-op). */
281
284
  readonly runCharge: RunCharge;
282
285
  /** Run-authorization seam — the terminal path revokes through `revoke`. */
@@ -295,8 +298,12 @@ export interface ExecuteAnalysisDeps {
295
298
  * point. Idempotency is the consumer's job, achieved via the deterministic
296
299
  * identifiers the events carry, not via step caching. Every call site is
297
300
  * guarded so a throwing observer never fails the run.
301
+ *
302
+ * Events arrive with the run's `RunSession` from durable workflow input — a
303
+ * persisting realization needs its credential, scope, and identity.
304
+ * Implementations may ignore the parameter.
298
305
  */
299
- readonly emitProvenance?: (event: RunProvenanceEvent) => void;
306
+ readonly emitProvenance?: (event: RunProvenanceEvent, session: RunSession) => void;
300
307
  /**
301
308
  * Optional, fire-and-forget observation of the run's live shape, for a host driving a UI.
302
309
  *
@@ -87,12 +87,12 @@ function emitStreamPart(part) {
87
87
  * swallowed, never corrupt run state — integrity enforcement lives at the
88
88
  * host's own ledger, not here.
89
89
  */
90
- function emitProvenanceGuarded(deps, event) {
90
+ function emitProvenanceGuarded(deps, event, session) {
91
91
  const logger = (deps.logger ?? createNoopLogger()).named("executeAnalysis");
92
92
  if (!deps.emitProvenance)
93
93
  return;
94
94
  try {
95
- deps.emitProvenance(event);
95
+ deps.emitProvenance(event, session);
96
96
  }
97
97
  catch (err) {
98
98
  logger.error("emitProvenance threw", { runId: event.runId, event: event.type, ...logger.errorFields(err) });
@@ -320,6 +320,7 @@ export function synthesizeFindings(args) {
320
320
  resolveWorkspaceRoot: deps.resolveWorkspaceRoot,
321
321
  synthesisModel: deps.synthesisModel,
322
322
  bioKeys: deps.bioKeys,
323
+ citationResolver: deps.citationResolver,
323
324
  usageRecorder: deps.usageRecorder,
324
325
  }, {
325
326
  analysisId,
@@ -436,7 +437,7 @@ export async function runExecuteAnalysisBody(input, deps) {
436
437
  planSummary: input.planSummary,
437
438
  stepCount: input.steps.length,
438
439
  atMs: startedAtMs,
439
- });
440
+ }, input.runSession);
440
441
  // Opening snapshot: every step pending. The scheduler's own map does not exist yet, and an
441
442
  // empty one yields exactly that — so a host has the run's full shape before the first dispatch
442
443
  // rather than learning the step list from whichever transition happens to land first.
@@ -929,7 +930,7 @@ async function runSchedulerLoop(args) {
929
930
  status: stepStatus,
930
931
  ...(stepDurationMs !== undefined ? { durationMs: stepDurationMs } : {}),
931
932
  atMs: settledAtMs,
932
- });
933
+ }, input.runSession);
933
934
  }
934
935
  await drainBudgetExceededNotifications(budgetExceededChildIds);
935
936
  return {
@@ -999,7 +1000,7 @@ async function collectAndComplete(args) {
999
1000
  status,
1000
1001
  atMs: terminalAtMs,
1001
1002
  durationMs: terminalAtMs - startedAtMs,
1002
- });
1003
+ }, input.runSession);
1003
1004
  try {
1004
1005
  await DBOS.runStep(async () => {
1005
1006
  unwrapOrThrow(await updateRunStatus(deps.pool, runId, status, failureReason ?? (status === "canceled" && !budgetExceeded ? "external_cancel" : null)));