@inflexa-ai/harness 0.45.1 → 0.46.2

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 (790) hide show
  1. package/README.md +6 -3
  2. package/dist/agents/conversation-agent.d.ts +9 -3
  3. package/dist/agents/conversation-agent.js +34 -33
  4. package/dist/agents/report-session-agent.d.ts +15 -15
  5. package/dist/agents/report-session-agent.js +23 -15
  6. package/dist/agents/sandbox/index.d.ts +1 -1
  7. package/dist/agents/sandbox/index.js +1 -1
  8. package/dist/agents/sandbox/shared.d.ts +12 -14
  9. package/dist/agents/sandbox/shared.js +18 -12
  10. package/dist/agents/sandbox/types.d.ts +4 -4
  11. package/dist/agents/sandbox/validate-skills.d.ts +2 -2
  12. package/dist/agents/sandbox/validate-skills.js +3 -1
  13. package/dist/agents/sandbox-catalog.d.ts +2 -2
  14. package/dist/agents/sandbox-catalog.js +3 -3
  15. package/dist/agents/system-prompt.d.ts +2 -2
  16. package/dist/app/chat-turn-workflow.d.ts +114 -0
  17. package/dist/app/chat-turn-workflow.js +306 -0
  18. package/dist/app/chat-turn.d.ts +85 -57
  19. package/dist/app/chat-turn.js +123 -100
  20. package/dist/app/data-profile-orientation.js +10 -8
  21. package/dist/app/data-profile-view.d.ts +4 -4
  22. package/dist/app/data-profile-view.js +9 -8
  23. package/dist/app/in-process-turn.d.ts +42 -0
  24. package/dist/app/in-process-turn.js +139 -0
  25. package/dist/app/message-assembly.d.ts +10 -4
  26. package/dist/app/message-assembly.js +13 -8
  27. package/dist/app/report-session-runtime.d.ts +4 -4
  28. package/dist/app/report-session-runtime.js +36 -21
  29. package/dist/app/spawn-report-session.d.ts +33 -28
  30. package/dist/app/spawn-report-session.js +25 -40
  31. package/dist/app/synthesize-run.d.ts +22 -16
  32. package/dist/app/synthesize-run.js +41 -35
  33. package/dist/app/work.d.ts +96 -0
  34. package/dist/app/work.js +147 -0
  35. package/dist/auth/local-auth-context.d.ts +1 -3
  36. package/dist/auth/local-auth-context.js +1 -3
  37. package/dist/auth/local-run-authorizer.d.ts +1 -1
  38. package/dist/auth/local-run-authorizer.js +5 -3
  39. package/dist/auth/types.d.ts +26 -26
  40. package/dist/billing/cached-resolver.d.ts +4 -4
  41. package/dist/billing/run-charge.d.ts +5 -8
  42. package/dist/billing/run-charge.js +2 -2
  43. package/dist/billing/usage-recorder.d.ts +9 -9
  44. package/dist/citations/aggregate.js +1 -1
  45. package/dist/citations/clients/arxiv.d.ts +2 -2
  46. package/dist/citations/clients/arxiv.js +12 -4
  47. package/dist/citations/clients/common.js +4 -2
  48. package/dist/citations/clients/crossref.d.ts +2 -2
  49. package/dist/citations/clients/crossref.js +37 -37
  50. package/dist/citations/clients/doi-registry.js +15 -16
  51. package/dist/citations/clients/pubmed.d.ts +2 -2
  52. package/dist/citations/clients/pubmed.js +20 -13
  53. package/dist/citations/clients/semantic-scholar.d.ts +2 -2
  54. package/dist/citations/clients/semantic-scholar.js +9 -9
  55. package/dist/citations/compare.js +2 -2
  56. package/dist/citations/match.d.ts +2 -2
  57. package/dist/citations/match.js +12 -7
  58. package/dist/citations/normalize.js +35 -26
  59. package/dist/citations/plan.js +6 -2
  60. package/dist/citations/resolve.d.ts +6 -6
  61. package/dist/citations/resolve.js +34 -25
  62. package/dist/citations/types.d.ts +77 -77
  63. package/dist/config/environment-stores.d.ts +6 -6
  64. package/dist/config/resource-limits.js +1 -3
  65. package/dist/contracts/analogy-report.d.ts +7 -7
  66. package/dist/contracts/analogy-report.js +2 -2
  67. package/dist/contracts/chat-events.d.ts +38 -24
  68. package/dist/contracts/chat-frame.d.ts +15 -5
  69. package/dist/contracts/chat-frame.js +64 -41
  70. package/dist/contracts/chat-parts.d.ts +130 -67
  71. package/dist/contracts/chat-parts.js +1 -0
  72. package/dist/contracts/content-url.js +16 -2
  73. package/dist/contracts/data-profile.d.ts +38 -38
  74. package/dist/contracts/index.d.ts +2 -2
  75. package/dist/contracts/index.js +1 -1
  76. package/dist/contracts/message.d.ts +6 -6
  77. package/dist/contracts/organ-system.d.ts +11 -11
  78. package/dist/contracts/part-registry.d.ts +5 -4
  79. package/dist/contracts/part-registry.js +5 -0
  80. package/dist/contracts/profile-vocabulary.d.ts +11 -10
  81. package/dist/contracts/profile-vocabulary.js +10 -3
  82. package/dist/contracts/report-blocks.d.ts +329 -329
  83. package/dist/contracts/report-reference.d.ts +105 -81
  84. package/dist/contracts/report-reference.js +30 -13
  85. package/dist/contracts/schemas/chat-events.d.ts +30 -16
  86. package/dist/contracts/schemas/chat-events.js +15 -6
  87. package/dist/contracts/schemas/chat-parts.d.ts +175 -102
  88. package/dist/contracts/schemas/chat-parts.js +33 -7
  89. package/dist/contracts/severity.d.ts +1 -1
  90. package/dist/contracts/structure-source.d.ts +3 -3
  91. package/dist/contracts/structure-source.js +6 -2
  92. package/dist/contracts/subagent-activity.d.ts +2 -2
  93. package/dist/contracts/subagent-activity.js +5 -3
  94. package/dist/contracts/usage.d.ts +2 -2
  95. package/dist/data/safety-panel-schema.d.ts +25 -25
  96. package/dist/data/safety-panel-schema.js +13 -4
  97. package/dist/execution/artifact-metadata.d.ts +23 -15
  98. package/dist/execution/artifact-metadata.js +33 -10
  99. package/dist/execution/artifact-record.d.ts +2 -2
  100. package/dist/execution/artifact-registration.d.ts +5 -5
  101. package/dist/execution/artifact-registry.d.ts +8 -8
  102. package/dist/execution/dbos-run-launcher.d.ts +24 -4
  103. package/dist/execution/dbos-run-launcher.js +58 -4
  104. package/dist/execution/dbos-workflow-purger.d.ts +5 -6
  105. package/dist/execution/dbos-workflow-purger.js +8 -7
  106. package/dist/execution/input-signature.d.ts +2 -2
  107. package/dist/execution/post-step-pipeline.d.ts +9 -9
  108. package/dist/execution/post-step-pipeline.js +1 -1
  109. package/dist/execution/post-step.js +2 -2
  110. package/dist/execution/reconcile-manifest.d.ts +4 -4
  111. package/dist/execution/reconcile-manifest.js +3 -2
  112. package/dist/execution/run-authorizer.d.ts +46 -37
  113. package/dist/execution/run-authorizer.js +8 -8
  114. package/dist/execution/run-canceler.d.ts +7 -7
  115. package/dist/execution/run-canceler.js +14 -7
  116. package/dist/execution/run-end-record.d.ts +2 -3
  117. package/dist/execution/run-event-parts.d.ts +0 -8
  118. package/dist/execution/run-event-parts.js +29 -24
  119. package/dist/execution/run-event-stream.d.ts +8 -8
  120. package/dist/execution/run-event-stream.js +3 -4
  121. package/dist/execution/run-launcher.d.ts +4 -4
  122. package/dist/execution/run-synthesis.d.ts +49 -48
  123. package/dist/execution/run-synthesis.js +96 -255
  124. package/dist/execution/staged-input.d.ts +2 -2
  125. package/dist/execution/step-summary.d.ts +6 -6
  126. package/dist/execution/step-summary.js +4 -8
  127. package/dist/execution/topo-sort.js +4 -3
  128. package/dist/execution/workflow-purger.d.ts +2 -2
  129. package/dist/index.d.ts +17 -20
  130. package/dist/index.js +25 -36
  131. package/dist/input-sanitization.js +9 -2
  132. package/dist/input-scan/clustering.d.ts +6 -6
  133. package/dist/input-scan/clustering.js +11 -5
  134. package/dist/input-scan/companions.js +3 -2
  135. package/dist/input-scan/detect-sets.d.ts +2 -2
  136. package/dist/input-scan/detect-sets.js +25 -21
  137. package/dist/input-scan/enrich.d.ts +6 -6
  138. package/dist/input-scan/enrich.js +29 -12
  139. package/dist/input-scan/formats.d.ts +2 -2
  140. package/dist/input-scan/formats.js +8 -2
  141. package/dist/input-scan/markers.d.ts +2 -2
  142. package/dist/input-scan/markers.js +14 -5
  143. package/dist/input-scan/menu.d.ts +10 -10
  144. package/dist/input-scan/menu.js +0 -0
  145. package/dist/input-scan/quarantine.d.ts +2 -2
  146. package/dist/input-scan/quarantine.js +23 -18
  147. package/dist/input-scan/readout-budget.d.ts +2 -2
  148. package/dist/input-scan/readout-budget.js +1 -1
  149. package/dist/input-scan/readout.d.ts +2 -2
  150. package/dist/input-scan/readout.js +47 -34
  151. package/dist/input-scan/scan.d.ts +2 -2
  152. package/dist/input-scan/scan.js +7 -5
  153. package/dist/input-scan/set-types.d.ts +28 -28
  154. package/dist/input-scan/shapes.d.ts +4 -4
  155. package/dist/input-scan/shapes.js +0 -0
  156. package/dist/input-scan/templates.d.ts +10 -10
  157. package/dist/input-scan/templates.js +30 -22
  158. package/dist/input-scan/tokens.d.ts +6 -4
  159. package/dist/input-scan/tokens.js +100 -38
  160. package/dist/input-scan/types.d.ts +22 -22
  161. package/dist/lib/async-utils.d.ts +3 -7
  162. package/dist/lib/async-utils.js +5 -9
  163. package/dist/lib/cache.js +1 -1
  164. package/dist/lib/chrome.d.ts +46 -6
  165. package/dist/lib/console-logger.js +2 -2
  166. package/dist/lib/db-result.d.ts +22 -6
  167. package/dist/lib/db-result.js +9 -13
  168. package/dist/lib/eyes.d.ts +4 -4
  169. package/dist/lib/eyes.js +1 -1
  170. package/dist/lib/fs-helpers.js +18 -6
  171. package/dist/lib/fs-result.d.ts +1 -0
  172. package/dist/lib/fs-result.js +5 -6
  173. package/dist/lib/hooks.d.ts +32 -7
  174. package/dist/lib/hooks.js +22 -3
  175. package/dist/lib/host-concurrency.js +3 -2
  176. package/dist/lib/logger.d.ts +2 -2
  177. package/dist/lib/logger.js +2 -1
  178. package/dist/lib/metrics.d.ts +1 -1
  179. package/dist/lib/null-proto.d.ts +6 -0
  180. package/dist/lib/null-proto.js +17 -0
  181. package/dist/lib/otel-spans.d.ts +1 -1
  182. package/dist/lib/otel-spans.js +9 -5
  183. package/dist/lib/otel.d.ts +4 -2
  184. package/dist/lib/otel.js +10 -2
  185. package/dist/lib/page-capture.d.ts +8 -8
  186. package/dist/lib/page-capture.js +9 -4
  187. package/dist/lib/parse-json.d.ts +5 -0
  188. package/dist/lib/parse-json.js +12 -0
  189. package/dist/lib/present.d.ts +12 -0
  190. package/dist/lib/present.js +39 -0
  191. package/dist/lib/rate-limit.d.ts +4 -4
  192. package/dist/lib/rate-limit.js +12 -13
  193. package/dist/lib/regulatory-corpus/fetch-fda-guidance.d.ts +4 -4
  194. package/dist/lib/regulatory-corpus/fetch-fda-guidance.js +4 -10
  195. package/dist/lib/regulatory-corpus/fetch-ich.d.ts +4 -4
  196. package/dist/lib/regulatory-corpus/fetch-ich.js +18 -14
  197. package/dist/lib/regulatory-corpus/normalize-whitespace.d.ts +6 -0
  198. package/dist/lib/regulatory-corpus/normalize-whitespace.js +41 -0
  199. package/dist/lib/regulatory-corpus.d.ts +10 -10
  200. package/dist/lib/regulatory-corpus.js +3 -1
  201. package/dist/lib/result.d.ts +10 -11
  202. package/dist/lib/result.js +15 -13
  203. package/dist/lib/storage.d.ts +2 -2
  204. package/dist/lib/test/async-iterable.d.ts +9 -0
  205. package/dist/lib/test/async-iterable.js +28 -0
  206. package/dist/lib/test/request-url.d.ts +5 -0
  207. package/dist/lib/test/request-url.js +7 -0
  208. package/dist/lib/test/result-assert.d.ts +12 -0
  209. package/dist/lib/test/result-assert.js +17 -0
  210. package/dist/lib/test/text-shapes.d.ts +24 -0
  211. package/dist/lib/test/text-shapes.js +63 -0
  212. package/dist/lib/text.d.ts +22 -0
  213. package/dist/lib/text.js +68 -0
  214. package/dist/lib/thrown.d.ts +18 -0
  215. package/dist/lib/thrown.js +30 -0
  216. package/dist/lib/unknown-array.d.ts +6 -0
  217. package/dist/lib/unknown-array.js +8 -0
  218. package/dist/lib/unknown-containers.d.ts +14 -0
  219. package/dist/lib/unknown-containers.js +17 -0
  220. package/dist/lib/unknown-text.d.ts +8 -0
  221. package/dist/lib/unknown-text.js +28 -0
  222. package/dist/lib/zod-issue-shape.d.ts +1 -1
  223. package/dist/lib/zod-issue-shape.js +31 -31
  224. package/dist/lib/zod-issues.d.ts +3 -3
  225. package/dist/lib/zod-issues.js +30 -17
  226. package/dist/literature/sources/arxiv.d.ts +6 -6
  227. package/dist/literature/sources/arxiv.js +92 -35
  228. package/dist/literature/sources/http.d.ts +2 -2
  229. package/dist/literature/sources/http.js +16 -15
  230. package/dist/literature/sources/pubmed.d.ts +16 -16
  231. package/dist/literature/sources/pubmed.js +96 -61
  232. package/dist/literature/sources/semantic-scholar.d.ts +8 -8
  233. package/dist/literature/sources/semantic-scholar.js +22 -13
  234. package/dist/loop/accounting.d.ts +63 -0
  235. package/dist/loop/accounting.js +141 -0
  236. package/dist/loop/call-policy.d.ts +73 -0
  237. package/dist/loop/call-policy.js +109 -0
  238. package/dist/loop/chat-stream.d.ts +62 -0
  239. package/dist/loop/chat-stream.js +237 -0
  240. package/dist/loop/compaction.d.ts +9 -9
  241. package/dist/loop/compaction.js +1 -1
  242. package/dist/loop/metrics.d.ts +12 -15
  243. package/dist/loop/metrics.js +14 -26
  244. package/dist/loop/run-loop.d.ts +233 -0
  245. package/dist/loop/run-loop.js +770 -0
  246. package/dist/loop/sub-agent.d.ts +61 -0
  247. package/dist/loop/sub-agent.js +160 -0
  248. package/dist/loop/tool-adapter.d.ts +98 -0
  249. package/dist/loop/tool-adapter.js +364 -0
  250. package/dist/loop/tool-detail.d.ts +1 -1
  251. package/dist/loop/tool-detail.js +6 -5
  252. package/dist/loop/tool-outcome.d.ts +1 -1
  253. package/dist/loop/tool-output.d.ts +4 -4
  254. package/dist/loop/tool-output.js +2 -2
  255. package/dist/loop/turn-credentials.d.ts +39 -0
  256. package/dist/loop/turn-credentials.js +90 -0
  257. package/dist/loop/types.d.ts +29 -38
  258. package/dist/loop/types.js +2 -7
  259. package/dist/memory/ai-sdk-message-storage.d.ts +5 -5
  260. package/dist/memory/ai-sdk-message-storage.js +13 -11
  261. package/dist/memory/card-builders.d.ts +14 -14
  262. package/dist/memory/card-builders.js +19 -15
  263. package/dist/memory/conversation-display-recorder.d.ts +22 -4
  264. package/dist/memory/conversation-display-recorder.js +43 -24
  265. package/dist/memory/conversation-display-replay.js +3 -2
  266. package/dist/memory/conversation-display-storage.d.ts +15 -7
  267. package/dist/memory/conversation-display-storage.js +141 -36
  268. package/dist/memory/conversation-view.d.ts +4 -4
  269. package/dist/memory/conversation-view.js +5 -4
  270. package/dist/memory/count-tokens.js +10 -8
  271. package/dist/memory/derive-thread-title.d.ts +0 -13
  272. package/dist/memory/derive-thread-title.js +2 -1
  273. package/dist/memory/message-backfill.d.ts +12 -5
  274. package/dist/memory/message-backfill.js +5 -3
  275. package/dist/memory/thread-history.d.ts +36 -21
  276. package/dist/memory/thread-history.js +78 -54
  277. package/dist/memory/thread-store.d.ts +26 -17
  278. package/dist/memory/thread-store.js +15 -9
  279. package/dist/memory/tool-call-integrity.d.ts +2 -2
  280. package/dist/memory/tool-call-integrity.js +4 -3
  281. package/dist/memory/working-memory.d.ts +16 -12
  282. package/dist/memory/working-memory.js +72 -61
  283. package/dist/prompts/briefing.d.ts +12 -12
  284. package/dist/prompts/briefing.js +1 -1
  285. package/dist/prompts/synthesis-agent.d.ts +1 -1
  286. package/dist/prompts/synthesis-agent.js +6 -14
  287. package/dist/provenance/collector.d.ts +6 -6
  288. package/dist/provenance/collector.js +15 -8
  289. package/dist/provenance/exec-frame.d.ts +2 -2
  290. package/dist/provenance/exec-frame.js +6 -1
  291. package/dist/provenance/seam.d.ts +1 -4
  292. package/dist/provenance/types.d.ts +6 -6
  293. package/dist/providers/ai-sdk.d.ts +113 -62
  294. package/dist/providers/ai-sdk.js +283 -518
  295. package/dist/providers/embedding.d.ts +2 -2
  296. package/dist/providers/embedding.js +1 -1
  297. package/dist/providers/errors.d.ts +22 -2
  298. package/dist/providers/errors.js +98 -50
  299. package/dist/providers/prompt-cache.d.ts +8 -8
  300. package/dist/providers/prompt-cache.js +13 -11
  301. package/dist/providers/reasoning.d.ts +2 -2
  302. package/dist/providers/reasoning.js +2 -2
  303. package/dist/providers/request-headers.js +12 -3
  304. package/dist/providers/types.d.ts +55 -94
  305. package/dist/reference-data/catalog.d.ts +2 -15
  306. package/dist/reference-data/catalog.js +25 -8
  307. package/dist/reference-data/receipt.d.ts +2 -2
  308. package/dist/report-model/assert-rules.js +1 -1
  309. package/dist/report-model/authoring-grammar.d.ts +211 -233
  310. package/dist/report-model/block-walk.d.ts +10 -10
  311. package/dist/report-model/block-walk.js +2 -0
  312. package/dist/report-model/draft-finish.d.ts +8 -8
  313. package/dist/report-model/draft-finish.js +1 -1
  314. package/dist/report-model/draft-hash.js +8 -7
  315. package/dist/report-model/draft-operations.d.ts +22 -10
  316. package/dist/report-model/draft-operations.js +48 -30
  317. package/dist/report-model/draft-read.d.ts +19 -7
  318. package/dist/report-model/draft-read.js +16 -6
  319. package/dist/report-model/draft.d.ts +844 -2
  320. package/dist/report-model/draft.js +3 -1
  321. package/dist/report-model/fixture-resolver.js +39 -16
  322. package/dist/report-model/pin-snapshot.d.ts +2 -2
  323. package/dist/report-model/pin-snapshot.js +15 -15
  324. package/dist/report-model/production-resolver.d.ts +18 -18
  325. package/dist/report-model/production-resolver.js +50 -42
  326. package/dist/report-model/reference-resolver.d.ts +26 -10
  327. package/dist/report-model/reference-resolver.js +68 -5
  328. package/dist/report-model/reference-stamp.js +30 -38
  329. package/dist/report-model/structural-validation.js +12 -4
  330. package/dist/report-model/validate.d.ts +7 -7
  331. package/dist/report-model/validate.js +6 -6
  332. package/dist/report-render/assets.d.ts +2 -2
  333. package/dist/report-render/assets.js +27 -3
  334. package/dist/report-render/chart-export.d.ts +10 -2
  335. package/dist/report-render/chart-export.js +37 -11
  336. package/dist/report-render/chart-presets.d.ts +8 -8
  337. package/dist/report-render/chart-presets.js +3 -3
  338. package/dist/report-render/chart-renderers.d.ts +23 -16
  339. package/dist/report-render/chart-renderers.js +74 -53
  340. package/dist/report-render/chart-toolbox.d.ts +2 -2
  341. package/dist/report-render/chart-toolbox.js +7 -6
  342. package/dist/report-render/chart.d.ts +15 -16
  343. package/dist/report-render/chart.js +369 -252
  344. package/dist/report-render/design.d.ts +36 -297
  345. package/dist/report-render/design.js +0 -12
  346. package/dist/report-render/figures/common.d.ts +12 -12
  347. package/dist/report-render/figures/common.js +42 -31
  348. package/dist/report-render/figures/dendrogram.d.ts +3 -3
  349. package/dist/report-render/figures/dendrogram.js +10 -10
  350. package/dist/report-render/figures/dense.js +15 -8
  351. package/dist/report-render/figures/dotplot.js +46 -33
  352. package/dist/report-render/figures/embedding.js +52 -43
  353. package/dist/report-render/figures/equal-units.d.ts +10 -10
  354. package/dist/report-render/figures/equal-units.js +27 -27
  355. package/dist/report-render/figures/forest.js +9 -7
  356. package/dist/report-render/figures/gsea.js +21 -11
  357. package/dist/report-render/figures/heatmap.js +13 -9
  358. package/dist/report-render/figures/index.d.ts +10 -10
  359. package/dist/report-render/figures/index.js +4 -2
  360. package/dist/report-render/figures/km.js +10 -9
  361. package/dist/report-render/figures/label-room.d.ts +12 -12
  362. package/dist/report-render/figures/label-room.js +7 -2
  363. package/dist/report-render/figures/locuszoom.d.ts +2 -2
  364. package/dist/report-render/figures/locuszoom.js +59 -31
  365. package/dist/report-render/figures/lollipop.js +3 -2
  366. package/dist/report-render/figures/ma.js +8 -4
  367. package/dist/report-render/figures/manhattan.d.ts +1 -2
  368. package/dist/report-render/figures/manhattan.js +52 -34
  369. package/dist/report-render/figures/oncoprint.js +4 -5
  370. package/dist/report-render/figures/pca.js +25 -11
  371. package/dist/report-render/figures/qq.js +20 -9
  372. package/dist/report-render/figures/roc.js +1 -1
  373. package/dist/report-render/figures/sankey.d.ts +2 -2
  374. package/dist/report-render/figures/sankey.js +11 -10
  375. package/dist/report-render/figures/upset.d.ts +6 -6
  376. package/dist/report-render/figures/upset.js +7 -4
  377. package/dist/report-render/figures/volcano.js +12 -10
  378. package/dist/report-render/hybrid-svg.d.ts +4 -4
  379. package/dist/report-render/hybrid-svg.js +7 -5
  380. package/dist/report-render/number-format.d.ts +4 -4
  381. package/dist/report-render/number-format.js +62 -27
  382. package/dist/report-render/page.js +2 -1
  383. package/dist/report-render/provenance-data.d.ts +2 -2
  384. package/dist/report-render/references.d.ts +4 -4
  385. package/dist/report-render/render.d.ts +2 -2
  386. package/dist/report-render/render.js +10 -11
  387. package/dist/report-render/script-json.js +1 -1
  388. package/dist/report-render/table-data.d.ts +6 -6
  389. package/dist/report-render/test/options.d.ts +49 -0
  390. package/dist/report-render/test/options.js +143 -0
  391. package/dist/report-render/test/page-function.d.ts +19 -0
  392. package/dist/report-render/test/page-function.js +33 -0
  393. package/dist/report-render/types.d.ts +10 -10
  394. package/dist/report-render/value-bridge.d.ts +4 -4
  395. package/dist/report-render/value-bridge.js +27 -14
  396. package/dist/report-render/views/chart-view.d.ts +2 -2
  397. package/dist/report-render/views/chart-view.js +2 -2
  398. package/dist/report-render/views/lineage.d.ts +6 -6
  399. package/dist/report-render/views/lineage.js +2 -0
  400. package/dist/report-render/views/page-view.d.ts +2 -2
  401. package/dist/report-render/views/page-view.js +1 -0
  402. package/dist/report-render/views/prose.js +25 -8
  403. package/dist/report-render/views/references-view.js +12 -1
  404. package/dist/report-render/views/values.js +1 -1
  405. package/dist/runtime/assemble.d.ts +53 -26
  406. package/dist/runtime/assemble.js +103 -56
  407. package/dist/runtime/boot.d.ts +8 -7
  408. package/dist/runtime/boot.js +1 -1
  409. package/dist/runtime/connection-budget.d.ts +6 -6
  410. package/dist/runtime/connection-budget.js +1 -1
  411. package/dist/runtime/dbos.d.ts +29 -19
  412. package/dist/runtime/dbos.js +35 -20
  413. package/dist/runtime/result-serialization.js +18 -5
  414. package/dist/runtime/shutdown.d.ts +2 -2
  415. package/dist/sandbox/client.d.ts +7 -3
  416. package/dist/sandbox/cpu-files.d.ts +2 -2
  417. package/dist/sandbox/cpu-files.js +21 -4
  418. package/dist/sandbox/create-sandbox.d.ts +7 -8
  419. package/dist/sandbox/create-sandbox.js +6 -12
  420. package/dist/sandbox/docker-client.d.ts +48 -3
  421. package/dist/sandbox/docker-client.js +10 -6
  422. package/dist/sandbox/estimate-data-profile-resources.js +2 -7
  423. package/dist/sandbox/exec-outcome.d.ts +2 -2
  424. package/dist/sandbox/exec-outcome.js +1 -1
  425. package/dist/sandbox/exec.d.ts +10 -6
  426. package/dist/sandbox/exec.js +27 -12
  427. package/dist/sandbox/execution-id.js +14 -6
  428. package/dist/sandbox/farm.d.ts +2 -2
  429. package/dist/sandbox/farm.js +22 -27
  430. package/dist/sandbox/identity.d.ts +7 -0
  431. package/dist/sandbox/identity.js +14 -3
  432. package/dist/sandbox/image-packages.js +15 -27
  433. package/dist/sandbox/k8s-client.d.ts +35 -8
  434. package/dist/sandbox/k8s-client.js +6 -5
  435. package/dist/sandbox/labels.js +1 -0
  436. package/dist/sandbox/liveness.js +3 -1
  437. package/dist/sandbox/mount-plan.d.ts +10 -10
  438. package/dist/sandbox/mount-plan.js +13 -14
  439. package/dist/sandbox/notification-sweep.d.ts +4 -4
  440. package/dist/sandbox/notification-sweep.js +4 -7
  441. package/dist/sandbox/package-identity.d.ts +6 -13
  442. package/dist/sandbox/package-identity.js +46 -28
  443. package/dist/sandbox/reaper.d.ts +10 -10
  444. package/dist/sandbox/reaper.js +4 -6
  445. package/dist/sandbox/sandbox-error.d.ts +2 -9
  446. package/dist/sandbox/sandbox-error.js +21 -8
  447. package/dist/sandbox/sandbox-step-translate.d.ts +8 -5
  448. package/dist/sandbox/sandbox-step-translate.js +22 -8
  449. package/dist/sandbox/step-sandbox.d.ts +47 -0
  450. package/dist/sandbox/step-sandbox.js +53 -0
  451. package/dist/sandbox/types.d.ts +18 -19
  452. package/dist/sandbox/types.js +1 -1
  453. package/dist/schemas/artifact-manifest.d.ts +4 -4
  454. package/dist/schemas/data-profile-schemas.d.ts +343 -298
  455. package/dist/schemas/file-metadata.js +2 -4
  456. package/dist/schemas/plan-schemas.d.ts +31 -31
  457. package/dist/schemas/run-synthesis.d.ts +95 -26
  458. package/dist/schemas/run-synthesis.js +3 -1
  459. package/dist/schemas/validate-plan.d.ts +4 -4
  460. package/dist/schemas/validate-plan.js +17 -5
  461. package/dist/schemas/workflow-state.d.ts +19 -19
  462. package/dist/schemas/workflow-state.js +2 -2
  463. package/dist/state/active-turns.d.ts +36 -0
  464. package/dist/state/active-turns.js +67 -0
  465. package/dist/state/analyses-metrics.d.ts +8 -8
  466. package/dist/state/analyses-metrics.js +4 -4
  467. package/dist/state/analyses.d.ts +0 -14
  468. package/dist/state/analyses.js +0 -32
  469. package/dist/state/artifacts.d.ts +32 -19
  470. package/dist/state/artifacts.js +28 -5
  471. package/dist/state/data-profile.d.ts +2 -2
  472. package/dist/state/db.d.ts +12 -2
  473. package/dist/state/db.js +5 -0
  474. package/dist/state/edit-intents.d.ts +4 -0
  475. package/dist/state/edit-intents.js +71 -0
  476. package/dist/state/index.d.ts +4 -4
  477. package/dist/state/index.js +2 -2
  478. package/dist/state/init.d.ts +2 -1
  479. package/dist/state/init.js +5 -2
  480. package/dist/state/migrations/20260927120000_baseline.js +3 -1
  481. package/dist/state/migrations/20261026120000_add_the_edit_intents.d.ts +7 -0
  482. package/dist/state/migrations/20261026120000_add_the_edit_intents.js +21 -0
  483. package/dist/state/migrations/20261027120000_record_the_applied_memory_edits.d.ts +7 -0
  484. package/dist/state/migrations/20261027120000_record_the_applied_memory_edits.js +16 -0
  485. package/dist/state/migrations/20261027120100_store_the_authoring_results.d.ts +8 -0
  486. package/dist/state/migrations/20261027120100_store_the_authoring_results.js +20 -0
  487. package/dist/state/migrations/20261027120200_key_the_groups_of_a_turn.d.ts +7 -0
  488. package/dist/state/migrations/20261027120200_key_the_groups_of_a_turn.js +12 -0
  489. package/dist/state/migrations/20261027120300_mark_the_active_turn_of_a_thread.d.ts +7 -0
  490. package/dist/state/migrations/20261027120300_mark_the_active_turn_of_a_thread.js +11 -0
  491. package/dist/state/migrations/20261028120000_record_the_paused_work.d.ts +7 -0
  492. package/dist/state/migrations/20261028120000_record_the_paused_work.js +25 -0
  493. package/dist/state/migrations/20261028120100_end_the_suspended_runs.d.ts +8 -0
  494. package/dist/state/migrations/20261028120100_end_the_suspended_runs.js +13 -0
  495. package/dist/state/migrations/20261029120000_reference_the_run_mandate.d.ts +7 -0
  496. package/dist/state/migrations/20261029120000_reference_the_run_mandate.js +23 -0
  497. package/dist/state/migrations/20261029120100_record_the_run_session_of_a_pause.d.ts +7 -0
  498. package/dist/state/migrations/20261029120100_record_the_run_session_of_a_pause.js +11 -0
  499. package/dist/state/migrations/index.js +18 -0
  500. package/dist/state/paused-work.d.ts +52 -0
  501. package/dist/state/paused-work.js +105 -0
  502. package/dist/state/plans.d.ts +27 -8
  503. package/dist/state/plans.js +14 -11
  504. package/dist/state/purge-analysis.d.ts +6 -6
  505. package/dist/state/purge-analysis.js +10 -2
  506. package/dist/state/report-session-state.d.ts +43 -18
  507. package/dist/state/report-session-state.js +43 -4
  508. package/dist/state/report-versions.d.ts +16 -11
  509. package/dist/state/report-versions.js +5 -3
  510. package/dist/state/runs.d.ts +30 -19
  511. package/dist/state/runs.js +43 -51
  512. package/dist/state/schema.d.ts +18 -21
  513. package/dist/state/schema.js +4 -5
  514. package/dist/state/snapshot-parse.d.ts +2 -2
  515. package/dist/state/snapshot-parse.js +14 -8
  516. package/dist/state/step-executions.d.ts +20 -8
  517. package/dist/state/step-executions.js +6 -4
  518. package/dist/state/tool-outputs.d.ts +2 -2
  519. package/dist/state/tool-outputs.js +2 -1
  520. package/dist/state/vector-store.d.ts +16 -16
  521. package/dist/state/vector-store.js +11 -7
  522. package/dist/tasks/data-profile-absorb.d.ts +3 -3
  523. package/dist/tasks/data-profile-absorb.js +21 -16
  524. package/dist/tasks/data-profile-activity.d.ts +5 -5
  525. package/dist/tasks/data-profile-activity.js +5 -5
  526. package/dist/tasks/data-profile-index.d.ts +4 -4
  527. package/dist/tasks/data-profile-index.js +32 -17
  528. package/dist/tasks/data-profile-resolve.d.ts +8 -8
  529. package/dist/tasks/data-profile-resolve.js +88 -62
  530. package/dist/tasks/data-profile.d.ts +40 -31
  531. package/dist/tasks/data-profile.js +138 -95
  532. package/dist/tasks/extract-values.d.ts +26 -79
  533. package/dist/tasks/extract-values.js +48 -108
  534. package/dist/tasks/refresh-regulatory-corpus.d.ts +4 -20
  535. package/dist/tasks/refresh-regulatory-corpus.js +12 -8
  536. package/dist/tasks/regulatory-corpus-boot-refresh.d.ts +2 -2
  537. package/dist/tasks/regulatory-corpus-boot-refresh.js +12 -10
  538. package/dist/testing.d.ts +62 -0
  539. package/dist/testing.js +101 -0
  540. package/dist/tools/ad-hoc-router.d.ts +9 -9
  541. package/dist/tools/ad-hoc-router.js +65 -52
  542. package/dist/tools/analysis-invocation.d.ts +7 -0
  543. package/dist/tools/analysis-invocation.js +29 -5
  544. package/dist/tools/approval/contract.d.ts +8 -52
  545. package/dist/tools/approval/contract.js +7 -36
  546. package/dist/tools/approval/gateway.d.ts +27 -28
  547. package/dist/tools/approval/gateway.js +33 -130
  548. package/dist/tools/approval/queries.d.ts +6 -11
  549. package/dist/tools/approval/queries.js +43 -29
  550. package/dist/tools/approval/uuidv7.js +2 -2
  551. package/dist/tools/bio/chembl.d.ts +3 -3
  552. package/dist/tools/bio/chembl.js +10 -0
  553. package/dist/tools/bio/comptox.d.ts +25 -8
  554. package/dist/tools/bio/comptox.js +4 -2
  555. package/dist/tools/bio/drug-gene-interactions.d.ts +27 -17
  556. package/dist/tools/bio/drug-gene-interactions.js +11 -8
  557. package/dist/tools/bio/gene-disease-evidence.d.ts +61 -15
  558. package/dist/tools/bio/gene-disease-evidence.js +10 -6
  559. package/dist/tools/bio/gene-preclinical-profile.d.ts +11 -11
  560. package/dist/tools/bio/keys.d.ts +2 -2
  561. package/dist/tools/bio/keys.js +3 -3
  562. package/dist/tools/bio/lookup-annotation.d.ts +5 -5
  563. package/dist/tools/bio/lookup-annotation.js +3 -0
  564. package/dist/tools/bio/opentargets.d.ts +1 -1
  565. package/dist/tools/bio/opentargets.js +6 -0
  566. package/dist/tools/bio/pubchem.d.ts +2 -2
  567. package/dist/tools/bio/pubchem.js +6 -0
  568. package/dist/tools/bio/pubmed.d.ts +26 -16
  569. package/dist/tools/bio/pubmed.js +9 -3
  570. package/dist/tools/bio/search-clinical-trials.d.ts +4 -4
  571. package/dist/tools/bio/search-clinical-trials.js +5 -1
  572. package/dist/tools/bio/search-faers.d.ts +2 -2
  573. package/dist/tools/bio/search-faers.js +1 -1
  574. package/dist/tools/bio/search-gene.d.ts +2 -2
  575. package/dist/tools/bio/search-gene.js +1 -0
  576. package/dist/tools/bio/search-geo-datasets.d.ts +4 -4
  577. package/dist/tools/bio/search-geo-datasets.js +5 -5
  578. package/dist/tools/bio/search-interactions.d.ts +1 -1
  579. package/dist/tools/bio/search-patents.d.ts +28 -22
  580. package/dist/tools/bio/search-patents.js +20 -17
  581. package/dist/tools/bio/search-protein.d.ts +2 -2
  582. package/dist/tools/bio/search-protein.js +1 -1
  583. package/dist/tools/bio/search-regulatory-approvals.d.ts +64 -18
  584. package/dist/tools/bio/search-regulatory-approvals.js +21 -16
  585. package/dist/tools/bio/target-safety.d.ts +8 -8
  586. package/dist/tools/bio/target-safety.js +11 -6
  587. package/dist/tools/capability-data.d.ts +9 -5
  588. package/dist/tools/capability-data.js +2 -1
  589. package/dist/tools/define-tool.d.ts +55 -46
  590. package/dist/tools/define-tool.js +39 -27
  591. package/dist/tools/display/normalize-echart-spec.d.ts +2 -2
  592. package/dist/tools/display/normalize-echart-spec.js +51 -27
  593. package/dist/tools/display/show-user.d.ts +2 -2
  594. package/dist/tools/display/show-user.js +1 -1
  595. package/dist/tools/execute-analysis.d.ts +19 -10
  596. package/dist/tools/execute-analysis.js +83 -62
  597. package/dist/tools/knowledge/check.d.ts +166 -44
  598. package/dist/tools/knowledge/check.js +5 -3
  599. package/dist/tools/knowledge/client.d.ts +31 -62
  600. package/dist/tools/knowledge/client.js +44 -27
  601. package/dist/tools/knowledge/environment.d.ts +6 -6
  602. package/dist/tools/knowledge/environment.js +18 -7
  603. package/dist/tools/knowledge/index.d.ts +2 -2
  604. package/dist/tools/knowledge/index.js +9 -9
  605. package/dist/tools/knowledge/local-slots.d.ts +4 -4
  606. package/dist/tools/knowledge/local-slots.js +11 -4
  607. package/dist/tools/knowledge/pin.d.ts +2 -2
  608. package/dist/tools/knowledge/recommend.d.ts +20 -45
  609. package/dist/tools/knowledge/recommend.js +27 -19
  610. package/dist/tools/knowledge/situation.d.ts +40 -40
  611. package/dist/tools/knowledge/situation.js +2 -9
  612. package/dist/tools/knowledge/skeleton.d.ts +4 -4
  613. package/dist/tools/knowledge/skeleton.js +10 -11
  614. package/dist/tools/knowledge/template.d.ts +20 -18
  615. package/dist/tools/knowledge/template.js +38 -32
  616. package/dist/tools/lib/alphafold-client.js +2 -1
  617. package/dist/tools/lib/amass-client.d.ts +28 -28
  618. package/dist/tools/lib/amass-client.js +20 -10
  619. package/dist/tools/lib/api-utils.d.ts +13 -3
  620. package/dist/tools/lib/api-utils.js +17 -3
  621. package/dist/tools/lib/bgee-client.d.ts +6 -6
  622. package/dist/tools/lib/bgee-client.js +16 -10
  623. package/dist/tools/lib/cbioportal-client.d.ts +4 -4
  624. package/dist/tools/lib/cbioportal-client.js +9 -8
  625. package/dist/tools/lib/chembl-client.d.ts +14 -14
  626. package/dist/tools/lib/chembl-client.js +11 -4
  627. package/dist/tools/lib/clinical-benchmarks-client.d.ts +4 -4
  628. package/dist/tools/lib/clinical-benchmarks-client.js +4 -7
  629. package/dist/tools/lib/clinical-trials-client.d.ts +18 -18
  630. package/dist/tools/lib/clinical-trials-client.js +10 -10
  631. package/dist/tools/lib/clinvar-client.d.ts +2 -2
  632. package/dist/tools/lib/clinvar-client.js +6 -6
  633. package/dist/tools/lib/ctx-ops.d.ts +23 -23
  634. package/dist/tools/lib/ctx-ops.js +13 -10
  635. package/dist/tools/lib/dgidb-client.d.ts +6 -6
  636. package/dist/tools/lib/dgidb-client.js +10 -12
  637. package/dist/tools/lib/disgenet-client.d.ts +4 -4
  638. package/dist/tools/lib/ema-client.d.ts +2 -2
  639. package/dist/tools/lib/ensembl-client.d.ts +4 -4
  640. package/dist/tools/lib/ensembl-client.js +5 -4
  641. package/dist/tools/lib/gwas-catalog-client.d.ts +4 -4
  642. package/dist/tools/lib/gwas-catalog-client.js +17 -17
  643. package/dist/tools/lib/html-text.d.ts +6 -0
  644. package/dist/tools/lib/html-text.js +9 -0
  645. package/dist/tools/lib/identifier-resolver.d.ts +4 -4
  646. package/dist/tools/lib/identifier-resolver.js +7 -3
  647. package/dist/tools/lib/impc-client.d.ts +8 -8
  648. package/dist/tools/lib/impc-client.js +46 -35
  649. package/dist/tools/lib/iuphar-client.d.ts +2 -2
  650. package/dist/tools/lib/iuphar-client.js +7 -16
  651. package/dist/tools/lib/monarch-client.d.ts +4 -4
  652. package/dist/tools/lib/openfda-client.d.ts +22 -22
  653. package/dist/tools/lib/openfda-client.js +23 -22
  654. package/dist/tools/lib/opentargets-client.d.ts +12 -12
  655. package/dist/tools/lib/pathway-client.d.ts +4 -4
  656. package/dist/tools/lib/pathway-client.js +15 -19
  657. package/dist/tools/lib/pharmgkb-client.d.ts +2 -2
  658. package/dist/tools/lib/pharmgkb-client.js +1 -1
  659. package/dist/tools/lib/protein-family-modality.d.ts +2 -2
  660. package/dist/tools/lib/protein-family-modality.js +0 -2
  661. package/dist/tools/lib/protein-family-normalize.js +7 -0
  662. package/dist/tools/lib/pubchem-client.d.ts +2 -2
  663. package/dist/tools/lib/pubchem-client.js +1 -0
  664. package/dist/tools/lib/pubchem-ops.d.ts +4 -4
  665. package/dist/tools/lib/pubchem-ops.js +8 -0
  666. package/dist/tools/lib/pubmed-client.d.ts +9 -9
  667. package/dist/tools/lib/pubmed-client.js +1 -1
  668. package/dist/tools/lib/string-client.d.ts +6 -6
  669. package/dist/tools/lib/uniprot-client.d.ts +8 -8
  670. package/dist/tools/lib/uniprot-client.js +5 -1
  671. package/dist/tools/memory/update-working-memory.d.ts +16 -13
  672. package/dist/tools/memory/update-working-memory.js +21 -3
  673. package/dist/tools/read-tool-output.js +8 -2
  674. package/dist/tools/registry.d.ts +2 -2
  675. package/dist/tools/report-authoring/authoring-tools.d.ts +54 -23
  676. package/dist/tools/report-authoring/authoring-tools.js +128 -62
  677. package/dist/tools/report-session/derive-table.d.ts +19 -59
  678. package/dist/tools/report-session/derive-table.js +120 -122
  679. package/dist/tools/report-session/examine-page.d.ts +6 -6
  680. package/dist/tools/report-session/examine-page.js +14 -12
  681. package/dist/tools/report-session/list-artifacts.d.ts +6 -6
  682. package/dist/tools/report-session/list-artifacts.js +5 -5
  683. package/dist/tools/report-session/page-faults.d.ts +6 -6
  684. package/dist/tools/report-session/page-faults.js +3 -1
  685. package/dist/tools/report-session/preview-report.d.ts +4 -3
  686. package/dist/tools/report-session/preview-report.js +26 -7
  687. package/dist/tools/report-session/record-version.d.ts +9 -4
  688. package/dist/tools/report-session/record-version.js +16 -9
  689. package/dist/tools/report-session/session-page-publisher.d.ts +6 -6
  690. package/dist/tools/research/generate-analogy-report.d.ts +14 -7
  691. package/dist/tools/research/generate-analogy-report.js +80 -76
  692. package/dist/tools/research/generate-plan.d.ts +16 -16
  693. package/dist/tools/research/generate-plan.js +410 -374
  694. package/dist/tools/research/guarded-address.js +3 -2
  695. package/dist/tools/research/inspect-data-profile.d.ts +27 -20
  696. package/dist/tools/research/inspect-data-profile.js +26 -25
  697. package/dist/tools/research/inspect-run.d.ts +54 -27
  698. package/dist/tools/research/inspect-run.js +50 -40
  699. package/dist/tools/research/literature-reviewer.d.ts +8 -7
  700. package/dist/tools/research/literature-reviewer.js +29 -28
  701. package/dist/tools/research/search-arxiv.d.ts +1 -1
  702. package/dist/tools/research/search-arxiv.js +1 -1
  703. package/dist/tools/research/search-github-repos.d.ts +10 -7
  704. package/dist/tools/research/search-github-repos.js +25 -22
  705. package/dist/tools/research/search-semantic-scholar.js +1 -1
  706. package/dist/tools/research/searxng-engine.d.ts +2 -2
  707. package/dist/tools/research/searxng-engine.js +6 -4
  708. package/dist/tools/research/web-fetch.d.ts +24 -21
  709. package/dist/tools/research/web-fetch.js +65 -74
  710. package/dist/tools/research/web-search.d.ts +26 -17
  711. package/dist/tools/research/web-search.js +26 -21
  712. package/dist/tools/sandbox/link-packages.d.ts +11 -7
  713. package/dist/tools/sandbox/link-packages.js +11 -8
  714. package/dist/tools/sandbox/list-available-packages.d.ts +17 -8
  715. package/dist/tools/sandbox/list-available-packages.js +31 -25
  716. package/dist/tools/sandbox/list-available-refs.d.ts +17 -14
  717. package/dist/tools/sandbox/list-available-refs.js +61 -36
  718. package/dist/tools/sandbox/report-blocker.d.ts +30 -37
  719. package/dist/tools/sandbox/report-blocker.js +23 -73
  720. package/dist/tools/sandbox/skills.d.ts +16 -12
  721. package/dist/tools/sandbox/skills.js +16 -12
  722. package/dist/tools/sandbox/submit-file-metadata.d.ts +12 -9
  723. package/dist/tools/sandbox/submit-file-metadata.js +18 -21
  724. package/dist/tools/start-report-session.d.ts +13 -5
  725. package/dist/tools/start-report-session.js +35 -8
  726. package/dist/tools/terminal-text.d.ts +5 -2
  727. package/dist/tools/terminal-text.js +35 -3
  728. package/dist/tools/workspace/decision-record.d.ts +4 -4
  729. package/dist/tools/workspace/decision-record.js +23 -13
  730. package/dist/tools/workspace/edit-file.d.ts +59 -20
  731. package/dist/tools/workspace/edit-file.js +81 -16
  732. package/dist/tools/workspace/execute-command.d.ts +15 -13
  733. package/dist/tools/workspace/execute-command.js +11 -9
  734. package/dist/tools/workspace/file-stat.d.ts +6 -3
  735. package/dist/tools/workspace/grep.d.ts +10 -7
  736. package/dist/tools/workspace/grep.js +9 -3
  737. package/dist/tools/workspace/list-files.d.ts +6 -3
  738. package/dist/tools/workspace/mutator.d.ts +28 -15
  739. package/dist/tools/workspace/mutator.js +107 -53
  740. package/dist/tools/workspace/read-file.d.ts +8 -5
  741. package/dist/tools/workspace/result-bounds.d.ts +5 -5
  742. package/dist/tools/workspace/result-bounds.js +4 -2
  743. package/dist/tools/workspace/run-exec.d.ts +2 -2
  744. package/dist/tools/workspace/run-exec.js +3 -3
  745. package/dist/tools/workspace/scan-inputs.d.ts +8 -5
  746. package/dist/tools/workspace/scan-inputs.js +17 -5
  747. package/dist/tools/workspace/show-file.d.ts +1 -1
  748. package/dist/tools/workspace/show-file.js +2 -1
  749. package/dist/tools/workspace/show-plan.d.ts +8 -5
  750. package/dist/tools/workspace/show-plan.js +9 -8
  751. package/dist/tools/workspace/workspace-search.d.ts +41 -13
  752. package/dist/tools/workspace/workspace-search.js +27 -14
  753. package/dist/tools/workspace/write-file.d.ts +9 -7
  754. package/dist/workflows/execute-analysis-scheduler.d.ts +9 -9
  755. package/dist/workflows/execute-analysis-scheduler.js +6 -3
  756. package/dist/workflows/execute-analysis.d.ts +46 -62
  757. package/dist/workflows/execute-analysis.js +137 -219
  758. package/dist/workflows/pause.d.ts +71 -0
  759. package/dist/workflows/pause.js +173 -0
  760. package/dist/workflows/sandbox-step.d.ts +38 -61
  761. package/dist/workflows/sandbox-step.js +99 -106
  762. package/dist/workflows/test/sql-writes.d.ts +21 -0
  763. package/dist/workflows/test/sql-writes.js +36 -0
  764. package/dist/workspace/filesystem.d.ts +6 -6
  765. package/dist/workspace/filesystem.js +10 -8
  766. package/dist/workspace/paths.d.ts +1 -1
  767. package/dist/workspace/paths.js +15 -4
  768. package/dist/workspace/search-config.d.ts +2 -2
  769. package/dist/workspace/search-config.js +1 -1
  770. package/package.json +21 -22
  771. package/dist/loop/call-guard.d.ts +0 -50
  772. package/dist/loop/call-guard.js +0 -90
  773. package/dist/loop/continue-agent.d.ts +0 -40
  774. package/dist/loop/continue-agent.js +0 -38
  775. package/dist/loop/genai-spans.d.ts +0 -47
  776. package/dist/loop/genai-spans.js +0 -134
  777. package/dist/loop/run-agent.d.ts +0 -245
  778. package/dist/loop/run-agent.js +0 -1124
  779. package/dist/loop/run-step.d.ts +0 -29
  780. package/dist/loop/run-step.js +0 -34
  781. package/dist/loop/run-to-terminal.d.ts +0 -66
  782. package/dist/loop/run-to-terminal.js +0 -93
  783. package/dist/loop/tool-mask.d.ts +0 -23
  784. package/dist/loop/tool-mask.js +0 -46
  785. package/dist/providers/streaming-chat.d.ts +0 -32
  786. package/dist/providers/streaming-chat.js +0 -91
  787. package/dist/tasks/derive-table-exec.d.ts +0 -54
  788. package/dist/tasks/derive-table-exec.js +0 -86
  789. package/dist/workflows/suspension.d.ts +0 -32
  790. package/dist/workflows/suspension.js +0 -38
@@ -1,1124 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { jsonSchema, tool as aiTool, } from "ai";
3
- import { createNoopUsageRecorder } from "../billing/noop-usage-recorder.js";
4
- import { stripNulCharacters } from "../input-sanitization.js";
5
- import { createNoopLogger } from "../lib/console-logger.js";
6
- import { deliverNotice } from "../lib/hooks.js";
7
- import { passThroughSpan } from "../lib/otel-spans.js";
8
- import { ResultError, unwrapOrThrow } from "../lib/result.js";
9
- import { describeZodIssueShapes } from "../lib/zod-issue-shape.js";
10
- import { hintForZodIssue, repairToolInput } from "../lib/zod-issues.js";
11
- import { contextRecordOf, markCompactionExchange, markInterruptedMessage, summaryMarkerMessage, syntheticUserMessage, withRequestInputTokens, } from "../memory/ai-sdk-message-storage.js";
12
- import { conversationView, lastRequestInputTokens } from "../memory/conversation-view.js";
13
- import { answerUnansweredToolCalls } from "../memory/tool-call-integrity.js";
14
- import { classifyProviderError, extractStatus } from "../providers/errors.js";
15
- import { DEFAULT_PROMPT_CACHE, withPromptCacheBreakpoint, withSystemPromptBreakpoint } from "../providers/prompt-cache.js";
16
- import { COMPACTION_MAX_REQUESTS } from "./compaction.js";
17
- import { continueAgent } from "./continue-agent.js";
18
- import { resultStep } from "./run-step.js";
19
- import { AskRejectedError, UnavailableAsk } from "../tools/approval/contract.js";
20
- import { isToolError, readToolCallRecord, readToolResultImages, } from "../tools/define-tool.js";
21
- import { labelToolFailure, labelToolValidationFailure, recordToolException, traceAgentRun, traceToolCall } from "./genai-spans.js";
22
- import { addChatUsage, countChatTokens, hasReportedUsage, recordAgentRun } from "./metrics.js";
23
- import { computeDetail, computeResultDetail } from "./tool-detail.js";
24
- import { refusalsFor } from "./tool-mask.js";
25
- import { toolOutcomeForOutputType } from "./tool-outcome.js";
26
- import { excerptOf, keptTextOf, READ_TOOL_OUTPUT_TOOL_ID, resultTextOf, TOOL_RESULT_CAP, toolOutputRef, withExcerpt, } from "./tool-output.js";
27
- const TRUNCATED_PROSE_STEER = "Your previous reply was cut off at the output-token limit; continue concisely, or finish via your terminal tool.";
28
- const TRUNCATED_TOOL_USE_ERROR = "Your previous tool call was cut off at the output-token limit and was not executed. Retry with a smaller payload, writing in incremental pieces.";
29
- export const DEFAULT_STEP_NAME_FORMATTER = {
30
- llm: (i) => `llm-${i}`,
31
- tool: (name, id) => `tool-${name}-${id}`,
32
- };
33
- /** A run that receives no turn accumulator is the turn's root. See `RunAgentOptions.turnUsage`. */
34
- export const isTurnRoot = (opts) => opts.turnUsage === undefined;
35
- export function runAgent(agent, initial, session, opts) {
36
- return traceAgentRun(agent.id, session, isTurnRoot(opts), () => runAgentLoop(agent, initial, session, opts));
37
- }
38
- /** The harness request of the wrap-up at the iteration cap. */
39
- export const WRAP_UP_REQUEST = "You reached the limit of tool rounds for this run, thus no tool can run now. Answer in text with the results that you have.";
40
- /** The cap of wrap-up requests: one answer, and one more request after a refused call. */
41
- export const WRAP_UP_MAX_REQUESTS = 2;
42
- async function runAgentLoop(agent, initial, session, opts) {
43
- const formatStepName = opts.formatStepName ?? DEFAULT_STEP_NAME_FORMATTER;
44
- const loop = openLoop(agent, initial, session, opts, agent.id);
45
- if (opts.turnInput !== undefined) {
46
- const ended = await loop.startTurn(opts.turnInput);
47
- if (ended !== undefined)
48
- return ended;
49
- }
50
- const task = await loop.runSegment({ mask: opts.toolMask, maxRequests: agent.maxIterations, stepNames: formatStepName, firstIndex: 0, checksBudget: true });
51
- if (task !== "capped")
52
- return task;
53
- // The wrap-up refuses calls through the mask, not `toolChoice` (see its
54
- // CAUTION), and keeps step name `llm(maxIterations + k)` so replay finds the
55
- // reply an earlier, single-call wrap-up stored under the same name.
56
- const wrapUp = await loop.runSegment({
57
- mask: "none",
58
- maxRequests: WRAP_UP_MAX_REQUESTS,
59
- stepNames: { llm: (k) => formatStepName.llm(agent.maxIterations + k), tool: formatStepName.tool },
60
- firstIndex: agent.maxIterations,
61
- requestText: WRAP_UP_REQUEST,
62
- closesCappedRun: true,
63
- });
64
- return wrapUp === "capped" ? loop.endCapped() : wrapUp;
65
- }
66
- /**
67
- * Open the loop over one conversation. `metricAgentId` is the id that usage
68
- * and run metrics count under, which is not always `agent.id`.
69
- */
70
- export function openLoop(agent, initial, session, opts, metricAgentId) {
71
- const { provider, signal, emit, runStep } = opts;
72
- // The step of a model call or a tool call writes no span. The `chat` span and
73
- // the `execute_tool` span under it describe the call already, and they attach
74
- // to the `invoke_agent` span of this run.
75
- const callStep = (name, fn) => passThroughSpan(name, () => runStep(name, fn));
76
- const configuredFatalLoopError = opts.isFatalLoopError ?? (() => false);
77
- // AbortError is control flow, not a tool failure. Always compose it with the
78
- // host's fatal predicate so request cancellation cannot be converted into a
79
- // model-visible error result and retried by the loop.
80
- const isFatalLoopError = (err) => ((err instanceof Error || err instanceof DOMException) && err.name === "AbortError") || configuredFatalLoopError(err);
81
- if (agent.tools.length > 0 && !provider.capabilities.toolCalling) {
82
- throw new Error(`Provider/model cannot run tool-required agent "${agent.id}"`);
83
- }
84
- const messages = [...initial];
85
- // The loop never changes a message before `givenCount`, because the sink can hold it already.
86
- let givenCount = initial.length;
87
- // Stays true when the sink rejects, thus the throw path does not call the sink again.
88
- let sinkPending = false;
89
- const giveRound = async () => {
90
- if (opts.onRound === undefined || messages.length === givenCount)
91
- return;
92
- sinkPending = true;
93
- await opts.onRound({ messages: messages.slice(givenCount) });
94
- sinkPending = false;
95
- givenCount = messages.length;
96
- };
97
- const source = {
98
- agentId: session.provenance.agentId ?? agent.id,
99
- callPath: session.provenance.callPath,
100
- ...(opts.invocationId === undefined ? {} : { invocationId: opts.invocationId }),
101
- };
102
- const policy = opts.compaction;
103
- const viewOptions = { keepFirstTurn: policy?.keepFirstTurn === true };
104
- const viewOf = () => (policy === undefined ? messages : conversationView(messages, viewOptions).messages);
105
- // The steer of a truncated prose reply and the reply after it complete one reply, thus no exchange goes between them.
106
- let continuesTruncation = false;
107
- let compactionStopped = false;
108
- let compactions = 0;
109
- // The first request after a summary shows if the summary brought the conversation under the budget.
110
- let summarizedLast = false;
111
- // Bound from the same `source` the emitted events carry: one derivation feeding
112
- // both sinks, so a record and an event cannot disagree about who produced them.
113
- // `callPath` rides as an array rather than a joined string — the queryable form;
114
- // rendering `parent > child` is the sink's choice, not a published package's.
115
- const log = (opts.logger ?? createNoopLogger()).named("loop").with({ agentId: source.agentId, callPath: source.callPath });
116
- // The picture of a tool result rides where the wire renders it. Absent flags
117
- // mean "cannot carry", thus the loop degrades to text and records the drop.
118
- // The collector belongs to this run, thus two loops never share one.
119
- const encoding = { placement: imagePlacementFor(provider.capabilities), deferredImages: [], log };
120
- const toolsById = new Map(agent.tools.map((t) => [t.id, t]));
121
- // Only the read tool of the agent can read a kept text, thus an agent without it keeps nothing.
122
- const cut = {
123
- store: toolsById.has(READ_TOOL_OUTPUT_TOOL_ID) ? opts.toolOutputStore : undefined,
124
- session,
125
- invocationId: opts.invocationId,
126
- log,
127
- };
128
- const toolDefs = Object.fromEntries(agent.tools.map((t) => [
129
- t.id,
130
- aiTool({
131
- description: t.description,
132
- inputSchema: jsonSchema(t.jsonSchema),
133
- }),
134
- ]));
135
- // Approval resolves once to the caller's seam, or the deny-by-default one
136
- // when it wires none: a tool that pauses for a user decision where no
137
- // interactive surface is present is denied rather than left waiting on an
138
- // answer that cannot come.
139
- const unavailableAsk = new UnavailableAsk();
140
- const ask = opts.ask ?? ((request) => unavailableAsk.ask(request));
141
- // A run handed no accumulator is the turn's root: it creates the one every
142
- // descendant loop folds into, and it is the only loop entitled to report a
143
- // turn total. Descendants receive the root's object and mutate it in place.
144
- const turnUsage = opts.turnUsage ?? {};
145
- const turnRoot = isTurnRoot(opts);
146
- const toolCtx = (tu) => ({
147
- invocationId: tu.toolCallId,
148
- session,
149
- signal,
150
- emit,
151
- ask,
152
- turnUsage,
153
- });
154
- let iterations = 0;
155
- let truncationRecoveries = 0;
156
- let stoppedEarly = false;
157
- // Counted rather than listed: the finish record stays one bounded line for a run of
158
- // any length, and "50 iterations, 49 tool calls, 47 of them errored" already separates
159
- // a productive long run from a loop stuck retrying one failing call. The per-iteration
160
- // `debug` records name the tools when that distinction is not enough.
161
- let toolCallCount = 0;
162
- let toolErrorCount = 0;
163
- // Resolved once, not per iteration: an identical policy across every call is
164
- // itself part of the cache contract — the request prefix has to be
165
- // byte-identical to be read back. The message breakpoint is re-derived per
166
- // call because it rides the transcript. The system prompt depends only on
167
- // the agent type, so it is computed once.
168
- const promptCache = opts.promptCache ?? DEFAULT_PROMPT_CACHE;
169
- const system = withSystemPromptBreakpoint(agent.systemPrompt, promptCache);
170
- const reasoningField = opts.reasoning === undefined ? {} : { reasoning: opts.reasoning };
171
- const usage = {};
172
- // Exactly one record per completed run — never one per iteration. That bound is
173
- // what keeps the default level affordable for an agent that runs long; the
174
- // per-iteration detail lives at `debug`, where paying per iteration is the point.
175
- const logFinish = (level, reason, cappedOut) => {
176
- log[level]("run finished", { iterations, reason, cappedOut, truncationRecoveries, toolCalls: toolCallCount, toolErrors: toolErrorCount, usage });
177
- };
178
- /**
179
- * Settle the transcript on the way out — called at every return. A reply can
180
- * carry a complete tool call beside ANY finish reason, because the call
181
- * streams before the stop reason arrives, and only the dispatching branches
182
- * (`tool-calls`, a call-carrying `stop`, and `length`) run one. A call this
183
- * run never dispatched gets a not-run result instead of being executed or
184
- * dropped: every tool call needs a result, or the provider refuses the
185
- * transcript on the next turn. The fix only appends; it never edits or
186
- * removes a message. Scoped past
187
- * `initial`: the caller's prefix is not this run's to repair.
188
- */
189
- const settleTranscript = () => {
190
- const answeredCalls = answerUnansweredToolCalls(messages, initial.length);
191
- if (answeredCalls.length === 0)
192
- return;
193
- log.warn("unanswered tool calls answered at run exit", {
194
- toolCallIds: answeredCalls.map((d) => d.toolCallId),
195
- tools: answeredCalls.map((d) => d.toolName),
196
- });
197
- };
198
- const usageRecorder = opts.usageRecorder ?? createNoopUsageRecorder();
199
- const accounting = {
200
- session,
201
- agentId: source.agentId,
202
- callPath: source.callPath,
203
- ...(opts.invocationId === undefined ? {} : { invocationId: opts.invocationId }),
204
- usageRecorder,
205
- logger: log,
206
- rollups: [usage, turnUsage],
207
- };
208
- /** The rollups this loop stamps on its finish — each absent when nothing reported. */
209
- const finishUsage = () => ({
210
- ...(hasReportedUsage(usage) ? { usage: { ...usage } } : {}),
211
- ...(turnRoot && hasReportedUsage(turnUsage) ? { turnUsage: { ...turnUsage } } : {}),
212
- });
213
- /**
214
- * One model request over the view of the transcript. Token counters grow inside
215
- * the step body, so a replayed step (a stored reply) does not double-count.
216
- */
217
- const callModel = async (stepName, endsOnRequestRefusal) => {
218
- const request = {
219
- system,
220
- messages: withPromptCacheBreakpoint(viewOf(), promptCache),
221
- tools: toolDefs,
222
- ...(opts.toolChoice !== undefined ? { toolChoice: opts.toolChoice } : {}),
223
- ...reasoningField,
224
- };
225
- const chat = () => provider.chat(request, session, signal).map(countChatTokens(metricAgentId));
226
- if (!endsOnRequestRefusal) {
227
- const reply = await resultStep(callStep)(stepName, chat);
228
- accountForChatCall(reply, { ...accounting, stepName });
229
- return withRequestAnchor(reply);
230
- }
231
- // The step gives the `Result` as its value, thus a refusal ends the segment and no step throws for it.
232
- const result = await callStep(stepName, async () => await chat());
233
- const refusal = result.isErr() ? requestRefusalOf(result.error) : undefined;
234
- if (refusal !== undefined)
235
- return { refusal };
236
- const reply = unwrapOrThrow(result);
237
- accountForChatCall(reply, { ...accounting, stepName });
238
- return withRequestAnchor(reply);
239
- };
240
- // The user said no. A subsequent model call would only let the agent argue
241
- // with the decision, or spend a call acknowledging it; the denial tool result
242
- // is itself what the surface renders, so the turn ends the moment a denial
243
- // lands in a dispatch round — after the concurrent siblings in that same round
244
- // have completed and been appended. Mirrors the clean-stop terminal path.
245
- const stopOnDenial = async (index) => {
246
- settleTranscript();
247
- await emit({ type: "iteration", source, index, final: true });
248
- recordAgentRun({ agentId: metricAgentId, iterations, cappedOut: false });
249
- logFinish("warn", "denied", false);
250
- await giveRound();
251
- return { messages, finish: { reason: "denied", cappedOut: false, truncationRecoveries, ...finishUsage() } };
252
- };
253
- /**
254
- * The call details for one dispatch round, positionally aligned with `calls`.
255
- *
256
- * Computed once per round and carried onto both the `tool-started` and the
257
- * `tool-finished` event, so the pair a host renders as one chip cannot show
258
- * two different descriptions of the same call. A tool that describes its own
259
- * result replaces the second half of that pair with the outcome it produced.
260
- */
261
- const roundDetails = (calls) => calls.map((tu) => {
262
- const tool = toolsById.get(tu.toolName);
263
- return tool === undefined ? undefined : computeDetail(tool, tu.input, log);
264
- });
265
- /**
266
- * Fold one dispatch round into the run's counters and the event sink, returning the
267
- * tools whose results the model will read as errors.
268
- *
269
- * Shared by both dispatch paths so a truncated round and a normal one cannot drift
270
- * in what they count. An error tool result is otherwise invisible to every sink —
271
- * it is not a thrown failure, so nothing reports it and the loop simply feeds it
272
- * back — and a run that ends badly is usually a run whose tool calls were failing.
273
- *
274
- * `details`, `resultDetails` and `durations` are positionally aligned with `calls`.
275
- * `dispatchTools` measures a duration around the call itself, because this sink emits
276
- * every finish event only after the whole round settles.
277
- */
278
- const settleRound = async (calls, results, details, resultDetails, durations) => {
279
- const errored = [];
280
- for (let idx = 0; idx < calls.length; idx++) {
281
- const tu = calls[idx];
282
- const outcome = outcomeOf(results[idx]);
283
- toolCallCount++;
284
- // A denial counts here with the errors: the model reads it as a result
285
- // it did not get to act on, which is what these counters describe. The
286
- // event keeps the two apart for the user-facing surface.
287
- if (outcome !== "ok") {
288
- toolErrorCount++;
289
- errored.push(tu.toolName);
290
- }
291
- await emit({
292
- type: "tool-finished",
293
- source,
294
- toolUseId: tu.toolCallId,
295
- name: tu.toolName,
296
- outcome,
297
- // The result detail wins where there is one, and the call detail
298
- // stands otherwise. Only the ok branch of a dispatch computes a
299
- // result detail, thus an error and a denial reach here with none
300
- // and keep the line the `tool-started` carried.
301
- ...detailField(resultDetails[idx] ?? details[idx]),
302
- ...durationField(durations[idx]),
303
- });
304
- }
305
- return errored;
306
- };
307
- // The calls of each tool that passed the mask and the budget in this run.
308
- const used = new Map();
309
- /**
310
- * Dispatch one round under the run's mask and budget. A refused call still
311
- * gets its own step slot, with an error result instead of running.
312
- */
313
- const dispatchRound = (calls, mask, names) => {
314
- const refusals = refusalsFor(calls, mask, opts.toolBudget, used);
315
- return dispatchTools(calls, refusals, toolsById, toolCtx, isFatalLoopError, callStep, names.tool, encoding, cut);
316
- };
317
- const stopOnResolved = async (index) => {
318
- settleTranscript();
319
- await emit({ type: "iteration", source, index, final: true });
320
- recordAgentRun({ agentId: metricAgentId, iterations, cappedOut: false });
321
- await giveRound();
322
- return { messages, finish: { reason: "stop", cappedOut: false, truncationRecoveries } };
323
- };
324
- const endOnRefusal = async (refusal) => {
325
- settleTranscript();
326
- recordAgentRun({ agentId: metricAgentId, iterations, cappedOut: false });
327
- logFinish("warn", "error", false);
328
- await giveRound();
329
- return { messages, finish: { reason: "error", cappedOut: false, truncationRecoveries, ...finishUsage() }, refusal };
330
- };
331
- /** The result of the run when an abort ends the exchange, or `undefined` when the run continues. */
332
- const compact = async (active, tokensBefore, index, trigger) => {
333
- const id = randomUUID();
334
- const stepNamespace = `compaction-${compactions++}`;
335
- const startedAt = performance.now();
336
- const emitPart = (data) => emit({ type: "data-compaction", source, data: { ...data, trigger } });
337
- await emitPart({ id, status: "running", tokensBefore });
338
- const { onRound: _onRound, compaction: _compaction, turnInput: _turnInput, ...conversationOptions } = opts;
339
- let settled = false;
340
- try {
341
- const exchange = await continueAgent(agent, viewOf(), {
342
- text: active.request,
343
- mask: active.mask,
344
- maxRequests: COMPACTION_MAX_REQUESTS,
345
- stepNamespace,
346
- accountingAgentId: `${agent.id}-compaction`,
347
- endOnRequestRefusal: true,
348
- }, session, { ...conversationOptions, provider: active.provider, turnUsage });
349
- messages.push(...exchange.messages.map((message) => markCompactionExchange(message, id)));
350
- await giveRound();
351
- if (exchange.finish.reason === "aborted") {
352
- settled = true;
353
- await emitPart({ id, status: "failed", tokensBefore, durationMs: Math.round(performance.now() - startedAt) });
354
- settleTranscript();
355
- await emit({ type: "iteration", source, index, final: true });
356
- recordAgentRun({ agentId: metricAgentId, iterations, cappedOut: false });
357
- logFinish("info", "aborted", false);
358
- await giveRound();
359
- return { messages, finish: { reason: "aborted", cappedOut: false, truncationRecoveries, ...finishUsage() } };
360
- }
361
- const summary = summaryOf(exchange);
362
- if (summary === undefined) {
363
- settled = true;
364
- const durationMs = Math.round(performance.now() - startedAt);
365
- await emitPart({ id, status: "failed", tokensBefore, durationMs });
366
- const refusal = exchange.refusal === undefined ? {} : { status: exchange.refusal.status, providerError: exchange.refusal.message };
367
- log.error("compaction gave no summary, thus the run compacts no more", { compactionId: id, trigger, tokensBefore, durationMs, ...refusal });
368
- compactionStopped = true;
369
- return undefined;
370
- }
371
- const markerOf = (durationMs) => summaryMarkerMessage(summary, { kind: "summary", id, tokensBefore, durationMs, trigger });
372
- // The figures of a marker do not change the view, thus a draft marker gives the view of the real one.
373
- const records = await active.recordsAfter(conversationView([...messages, markerOf(0)], viewOptions).messages);
374
- const durationMs = Math.round(performance.now() - startedAt);
375
- messages.push(markerOf(durationMs), ...records);
376
- settled = true;
377
- // The `done` part goes out before the marker round: a chat turn gives the rounds after a marker a new
378
- // assistant message, and the `running` and `done` parts of one compaction must stream into the same message.
379
- await emitPart({ id, status: "done", tokensBefore, durationMs });
380
- await giveRound();
381
- log.info("conversation compacted", { compactionId: id, trigger, tokensBefore, durationMs });
382
- summarizedLast = true;
383
- return undefined;
384
- }
385
- finally {
386
- if (!settled)
387
- await emitPart({ id, status: "failed", tokensBefore, durationMs: Math.round(performance.now() - startedAt) });
388
- }
389
- };
390
- const startTurn = async (input) => {
391
- const budget = policy?.turnStartBudget;
392
- const tokens = lastRequestInputTokens(messages);
393
- if (policy === undefined || budget === undefined || tokens === undefined || tokens <= budget) {
394
- messages.push(...input);
395
- return undefined;
396
- }
397
- const ended = await compact(policy, tokens, 0, "turn-start");
398
- if (ended !== undefined)
399
- return ended;
400
- // The records after a summary give the current context, thus a record of the input would only repeat one.
401
- messages.push(...(summarizedLast ? input.filter((message) => contextRecordOf(message) === undefined) : input));
402
- return undefined;
403
- };
404
- const compactOverBudget = async (index) => {
405
- if (policy === undefined || continuesTruncation || compactionStopped)
406
- return undefined;
407
- const tokens = lastRequestInputTokens(messages);
408
- if (tokens === undefined)
409
- return undefined;
410
- const afterSummary = summarizedLast;
411
- summarizedLast = false;
412
- if (tokens <= policy.budget)
413
- return undefined;
414
- if (afterSummary) {
415
- log.warn("the first request after the summary still exceeds the budget, thus the run compacts no more", { tokens, budget: policy.budget });
416
- compactionStopped = true;
417
- return undefined;
418
- }
419
- return compact(policy, tokens, index, "mid-turn");
420
- };
421
- const runSegment = async (segment) => {
422
- const names = segment.stepNames;
423
- if (segment.requestText !== undefined)
424
- messages.push(syntheticUserMessage(segment.requestText));
425
- for (let k = 0; k < segment.maxRequests; k++) {
426
- // The early cap ends a segment before the cap as if the cap were reached, thus a run takes its
427
- // tool-less wrap-up. The segment that closes a capped run is that wrap-up, and it runs to its end.
428
- if (segment.closesCappedRun !== true && opts.stopWhen?.()) {
429
- stoppedEarly = true;
430
- log.warn("run stopped early by the host policy, taking the wrap-up path", { iterations });
431
- return "capped";
432
- }
433
- const index = segment.firstIndex + k;
434
- // The last request of a segment that closes a capped run ends the run,
435
- // whatever its reply, thus its `iteration` event is the final one.
436
- const endsRun = segment.closesCappedRun === true && k === segment.maxRequests - 1;
437
- await giveRound();
438
- if (segment.checksBudget === true) {
439
- const ended = await compactOverBudget(index);
440
- if (ended !== undefined)
441
- return ended;
442
- }
443
- if (segment.closesCappedRun !== true)
444
- iterations++;
445
- const roundStart = messages.length;
446
- const called = await callModel(names.llm(k), segment.endsOnRequestRefusal === true);
447
- continuesTruncation = false;
448
- if ("refusal" in called)
449
- return endOnRefusal(called.refusal);
450
- const reply = called;
451
- if (reply.finishReason === "aborted") {
452
- // An interrupted turn keeps whatever the model produced before the cut, but
453
- // never an empty shell: a partial with no content adds no message. Only the
454
- // partial carries the marker, because the sink can hold each earlier message.
455
- // "aborted" is not "tool-calls", so this falls into the terminal return below.
456
- if (assistantHasContent(reply.message))
457
- messages.push(reply.message);
458
- markLastLoopAssistant(messages, roundStart);
459
- }
460
- else {
461
- messages.push(reply.message);
462
- }
463
- const toolCalls = toolCallParts(reply.message);
464
- if (reply.finishReason === "length") {
465
- truncationRecoveries++;
466
- await emit({ type: "iteration", source, index, final: endsRun });
467
- // `tools` is what the model asked for; the trailing call was cut off at the
468
- // output limit and is never dispatched, which the distinct message records.
469
- log.debug("iteration truncated at output limit", { iteration: index, tools: toolCalls.map((t) => t.toolName), truncationRecoveries });
470
- if (toolCalls.length === 0) {
471
- // Stamped synthetic, not left as a bare `user` message: the wire format needs a user turn
472
- // after a truncated assistant message, but this one is the loop's own nudge, and thread
473
- // storage treats a genuine `user` message as the start of a conversation turn. Unmarked, it
474
- // would split this turn in two everywhere that boundary is read.
475
- messages.push(syntheticUserMessage(TRUNCATED_PROSE_STEER));
476
- continuesTruncation = true;
477
- continue;
478
- }
479
- const trailing = toolCalls[toolCalls.length - 1];
480
- const earlier = toolCalls.slice(0, -1);
481
- const earlierDetails = roundDetails(earlier);
482
- for (const [idx, tu] of earlier.entries()) {
483
- await emit({
484
- type: "tool-started",
485
- source,
486
- toolUseId: tu.toolCallId,
487
- name: tu.toolName,
488
- input: tu.input,
489
- ...detailField(earlierDetails[idx]),
490
- });
491
- }
492
- const { results, durations, resultDetails } = await dispatchRound(earlier, segment.mask, names);
493
- const errored = await settleRound(earlier, results, earlierDetails, resultDetails, durations);
494
- results.push(errorResult(trailing, TRUNCATED_TOOL_USE_ERROR));
495
- // The trailing call was never dispatched, but it reaches the model as an error
496
- // result like any other — so it counts, or `toolErrors` reports a cleaner run
497
- // than the model actually read. It gets no `tool-finished` event: no
498
- // `tool-started` was emitted for it either, and the pair must stay balanced.
499
- toolCallCount++;
500
- toolErrorCount++;
501
- errored.push(trailing.toolName);
502
- log.debug("tool results returned errors", { iteration: index, tools: errored });
503
- messages.push({ role: "tool", content: results });
504
- appendDeferredImages(messages, results, encoding.deferredImages);
505
- if (hasDenial(results))
506
- return stopOnDenial(index);
507
- if (opts.resolved?.())
508
- return stopOnResolved(index);
509
- continue;
510
- }
511
- // A local model behind an OpenAI-compatible server routinely labels a
512
- // tool-calling reply `stop`. The calls are complete and the model waits
513
- // on their results, thus the mislabeled round dispatches like a
514
- // `tool-calls` one. Every other terminal answers its calls with the
515
- // not-run result: a filtered or aborted reply must not act.
516
- const dispatchesRound = reply.finishReason === "tool-calls" || (reply.finishReason === "stop" && toolCalls.length > 0);
517
- if (!dispatchesRound) {
518
- settleTranscript();
519
- await emit({ type: "iteration", source, index, final: true });
520
- if (segment.closesCappedRun === true) {
521
- // The cap was already reached; `reason` still distinguishes an
522
- // abort (user cut the turn) from a plain `max_iterations` cap-out.
523
- const reason = reply.finishReason === "aborted" ? "aborted" : "max_iterations";
524
- recordAgentRun({ agentId: metricAgentId, iterations, cappedOut: true });
525
- if (stoppedEarly)
526
- log.warn("run finished on the early cap", { iterations });
527
- logFinish("warn", reason, true);
528
- await giveRound();
529
- return { messages, finish: { reason, cappedOut: true, truncationRecoveries, ...finishUsage() } };
530
- }
531
- recordAgentRun({ agentId: metricAgentId, iterations, cappedOut: false });
532
- logFinish("info", reply.finishReason, false);
533
- await giveRound();
534
- return {
535
- messages,
536
- finish: {
537
- reason: reply.finishReason,
538
- ...(reply.rawFinishReason === undefined ? {} : { rawFinishReason: reply.rawFinishReason }),
539
- cappedOut: false,
540
- truncationRecoveries,
541
- ...finishUsage(),
542
- },
543
- };
544
- }
545
- await emit({ type: "iteration", source, index, final: endsRun });
546
- log.debug("iteration", { iteration: index, tools: toolCalls.map((t) => t.toolName) });
547
- const details = roundDetails(toolCalls);
548
- for (const [idx, tu] of toolCalls.entries()) {
549
- await emit({ type: "tool-started", source, toolUseId: tu.toolCallId, name: tu.toolName, input: tu.input, ...detailField(details[idx]) });
550
- }
551
- const { results, durations, resultDetails } = await dispatchRound(toolCalls, segment.mask, names);
552
- const errored = await settleRound(toolCalls, results, details, resultDetails, durations);
553
- if (errored.length > 0)
554
- log.debug("tool results returned errors", { iteration: index, tools: errored });
555
- messages.push({ role: "tool", content: results });
556
- appendDeferredImages(messages, results, encoding.deferredImages);
557
- if (hasDenial(results))
558
- return stopOnDenial(index);
559
- if (opts.resolved?.())
560
- return stopOnResolved(index);
561
- }
562
- return "capped";
563
- };
564
- const endCapped = async () => {
565
- settleTranscript();
566
- recordAgentRun({ agentId: metricAgentId, iterations, cappedOut: true });
567
- if (stoppedEarly)
568
- log.warn("run finished on the early cap", { iterations });
569
- logFinish("warn", "max_iterations", true);
570
- await giveRound();
571
- return { messages, finish: { reason: "max_iterations", cappedOut: true, truncationRecoveries, ...finishUsage() } };
572
- };
573
- // A throw keeps the open round, and the not-run results keep that stored round valid on the wire.
574
- const giveRoundBeforeThrow = async (body) => {
575
- if (opts.onRound === undefined)
576
- return body();
577
- try {
578
- return await body();
579
- }
580
- catch (err) {
581
- if (sinkPending)
582
- throw err;
583
- answerUnansweredToolCalls(messages, givenCount);
584
- // A rejection of this call must not hide the error of the run.
585
- await giveRound().catch(() => undefined);
586
- throw err;
587
- }
588
- };
589
- return {
590
- startTurn: (input) => giveRoundBeforeThrow(() => startTurn(input)),
591
- runSegment: (segment) => giveRoundBeforeThrow(() => runSegment(segment)),
592
- endCapped: () => giveRoundBeforeThrow(endCapped),
593
- };
594
- }
595
- /** The statuses of a request that the provider refuses for its content, for example a request past the context window. */
596
- const REFUSAL_STATUSES = new Set([400, 413]);
597
- function requestRefusalOf(error) {
598
- if (error.type !== "provider")
599
- return undefined;
600
- const status = extractStatus(error);
601
- return status !== undefined && REFUSAL_STATUSES.has(status) ? { status, message: error.message } : undefined;
602
- }
603
- /** The text of the last reply of an exchange, when that reply ends the exchange with the finish reason `stop`. */
604
- function summaryOf(exchange) {
605
- if (exchange.finish.reason !== "stop")
606
- return undefined;
607
- const text = finalText(exchange.messages).trim();
608
- return text.length > 0 ? text : undefined;
609
- }
610
- /** The reply whose message holds the input tokens of its request, which the budget checks read. */
611
- function withRequestAnchor(reply) {
612
- const tokens = reply.usage?.inputTokens;
613
- return tokens === undefined ? reply : { ...reply, message: withRequestInputTokens(reply.message, tokens) };
614
- }
615
- /**
616
- * Called at the fold point, before any run-ending branch, so an aborted or
617
- * dead run is still accounted. Token counters grow inside the call's step
618
- * body, not here, because a replay re-delivers the stored reply to this
619
- * function too; the record re-sends under the same idempotency key, which an
620
- * upserting sink counts once.
621
- */
622
- export function accountForChatCall(reply, call) {
623
- for (const rollup of call.rollups)
624
- addChatUsage(rollup, reply.usage);
625
- const reported = reply.usage;
626
- if (reported === undefined || !hasReportedUsage(reported))
627
- return;
628
- const { session } = call;
629
- void deliverNotice(call.logger, "UsageRecorder.record", call.usageRecorder.record({
630
- recordKey: recordKeyFor(session, call.invocationId, call.stepName),
631
- agentId: call.agentId,
632
- callPath: call.callPath,
633
- scope: session.scope,
634
- ...(session.runFrame?.runId === undefined ? {} : { runId: session.runFrame.runId }),
635
- ...(session.runFrame?.stepId === undefined ? {} : { stepId: session.runFrame.stepId }),
636
- ...(reply.requestedModelId === undefined ? {} : { requestedModelId: reply.requestedModelId }),
637
- ...(reply.servedModelId === undefined ? {} : { servedModelId: reply.servedModelId }),
638
- usage: reported,
639
- }));
640
- }
641
- /**
642
- * Delimiter joining the call path into one key segment.
643
- *
644
- * Deliberately not the `:` that separates the key's segments, so a call path
645
- * can never be read as one: agent ids are kebab-case identifiers, run ids are
646
- * UUIDs or the profiler's `data-profile` literal, step ids are path-safe plan
647
- * ids (`T1S1`, `synthesis`, `profile`), tool-call ids are provider-issued
648
- * opaque tokens, a profile attempt's workflow id is
649
- * `dataprofile:{analysisId}:{nonce}`, and the loop's step names are `llm-{n}`
650
- * / `tool-{name}-{id}` with an optional continuation namespace (`salvage:`,
651
- * `file-metadata:`, `step-summary:`) — none of them can contain `>`.
652
- */
653
- const CALL_PATH_DELIMITER = ">";
654
- /**
655
- * Idempotency key for one call's usage record, and for the kept text of one cut
656
- * tool result under the tool step name of its call:
657
- * `{runId}[:{stepId}]:{callPath}[:{invocationId}]:{stepName}`.
658
- *
659
- * Every component is a fact the same call carries again on every replay, so a
660
- * re-executed body re-fires `record` with the byte-identical key and an
661
- * upserting sink counts the call once. Each earns its place by a collision it
662
- * alone resolves:
663
- *
664
- * - `stepId` — `executeAnalysis` runs one child workflow per step under a
665
- * single shared `runId`, and each step's loop names its first call `llm-0`.
666
- * - `callPath` — a step name is unique only within ONE loop invocation (every
667
- * `runAgent` restarts its names at `llm-0`), and several loops routinely run
668
- * under one frame: a step's post-step describer and its summary writer share
669
- * `{runId, stepId}`, and the run-synthesis loop shares a bare `{runId}` with
670
- * the sub-agents its tools run. The provenance path is what separates those
671
- * agent chains.
672
- * - `invocationId` — the same sub-agent dispatched twice in one round has an
673
- * identical call path, so only the dispatching tool-call id tells the two
674
- * child loops apart. It is replay-stable by the harness-tools contract: a
675
- * redelivered call carries the id it was issued under. Each data profile
676
- * runs under the same constant frame, thus the workflow id of its attempt
677
- * takes this place, and a replay of the attempt keeps that id.
678
- * - `stepName` — the loop's own deterministic name, reused rather than
679
- * re-derived. Minting a second *naming* scheme here would create two things
680
- * that must agree about what "the same call" is, and they would drift.
681
- *
682
- * Outside a `RunFrame` there is no replay to be safe against (the HTTP chat
683
- * path runs a call exactly once), so a fresh id is enough and is what keeps two
684
- * calls in one turn distinct.
685
- */
686
- function recordKeyFor(session, invocationId, stepName) {
687
- const frame = session.runFrame;
688
- if (frame === undefined)
689
- return randomUUID();
690
- return [
691
- frame.runId,
692
- ...(frame.stepId === undefined ? [] : [frame.stepId]),
693
- session.provenance.callPath.join(CALL_PATH_DELIMITER),
694
- ...(invocationId === undefined ? [] : [invocationId]),
695
- stepName,
696
- ].join(":");
697
- }
698
- function toolCallParts(message) {
699
- if (typeof message.content === "string")
700
- return [];
701
- return message.content.filter((part) => part.type === "tool-call");
702
- }
703
- /** Whether an aborted partial carries any content worth persisting — an empty partial contributes no message. */
704
- function assistantHasContent(message) {
705
- return message.content.length > 0;
706
- }
707
- /**
708
- * Mark the aborted partial of the last request. A message before `roundStart` stays unmarked,
709
- * because the round sink can hold it already.
710
- */
711
- function markLastLoopAssistant(messages, roundStart) {
712
- for (let idx = messages.length - 1; idx >= roundStart; idx--) {
713
- const message = messages[idx];
714
- if (message.role === "assistant") {
715
- messages[idx] = markInterruptedMessage(message);
716
- return;
717
- }
718
- }
719
- }
720
- /**
721
- * Where a tool picture goes for this wire. The tool result wins, because it is
722
- * the native place and the correlation to the tool call stays implicit. The user
723
- * message is the fallback. When the wire renders neither, the loop drops the
724
- * picture.
725
- */
726
- function imagePlacementFor(capabilities) {
727
- if (capabilities.imageToolResults === true)
728
- return "tool-result";
729
- if (capabilities.imageUserMessages === true)
730
- return "user-message";
731
- return "drop";
732
- }
733
- /**
734
- * The text part that names one deferred picture. The wire holds no structural
735
- * link between a user message and a tool call, thus the line names the call. A
736
- * result with several pictures numbers each one, thus the model reads which
737
- * slice it looks at.
738
- */
739
- function deferredImageLabel(deferred, index) {
740
- const call = `the tool result ${deferred.toolCallId} of ${deferred.toolName}`;
741
- return deferred.images.length === 1 ? `The picture of ${call}.` : `The picture ${index + 1} of ${deferred.images.length} of ${call}.`;
742
- }
743
- /**
744
- * Append one user message that carries each deferred picture of the round, then
745
- * empty the collector.
746
- *
747
- * The tool message must come directly after the assistant message with the
748
- * tool calls. Thus a picture rides a separate message after the whole tool
749
- * message, and one message batches the round. The parts obey the order of
750
- * `results`, which is the order of the tool calls, and the pictures of one
751
- * result keep the order that the tool gave. Each deferred picture has a result
752
- * of this round, because the collector fills during the dispatch and it empties
753
- * here. A text part names the tool call of each picture, because the wire holds
754
- * no structural link between a user message and a tool call.
755
- *
756
- * The message carries the synthetic marker, because a `user` message opens a
757
- * conversation turn. An unmarked one is loop machinery that reads as a turn
758
- * boundary, and it splits one stored turn in two.
759
- */
760
- function appendDeferredImages(messages, results, deferredImages) {
761
- if (deferredImages.length === 0)
762
- return;
763
- const byToolCallId = new Map(deferredImages.map((deferred) => [deferred.toolCallId, deferred]));
764
- const content = [];
765
- for (const result of results) {
766
- const deferred = byToolCallId.get(result.toolCallId);
767
- if (deferred === undefined)
768
- continue;
769
- for (const [index, image] of deferred.images.entries()) {
770
- content.push({ type: "text", text: deferredImageLabel(deferred, index) });
771
- content.push({ type: "file", mediaType: image.mediaType, data: { type: "data", data: image.base64 } });
772
- }
773
- }
774
- messages.push(syntheticUserMessage(content));
775
- deferredImages.length = 0;
776
- }
777
- /**
778
- * Dispatch one round of tool calls, measure the time of each call, and fold the
779
- * call record of each call in call order.
780
- *
781
- * `refusals`, `results`, `durations` and `resultDetails` are positionally aligned
782
- * with `toolUses`; `refusals[i]` is the refusal text, or `undefined` if the call runs.
783
- *
784
- * A refused call still runs inside the same step wrapper and step name as a
785
- * dispatched one, keeping a round's step sequence identical for replay.
786
- *
787
- * Each measurement brackets the same unit that the loop awaits for that call. For a
788
- * step-mode call that unit is `runStep`, thus the figure includes the durable-step
789
- * wrapper. The wrapper is part of what the call cost, and a cached replay of a step
790
- * is genuinely fast. A bracket inside the step would report a body that did not run.
791
- */
792
- async function dispatchTools(toolUses, refusals, toolsById, toolCtx, isFatalLoopError, runStep, toolStepName, encoding, cut) {
793
- // A refusal writes no tool span, because the tool does not run.
794
- const dispatch = (tu, refusal) => refusal !== undefined
795
- ? Promise.resolve({ result: errorResult(tu, refusal) })
796
- : traceToolCall(tu, () => dispatchTool(tu, toolsById, toolCtx(tu), isFatalLoopError, encoding, cut, toolStepName(tu.toolName, tu.toolCallId)), (dispatched) => outcomeOf(dispatched.result));
797
- const results = new Array(toolUses.length);
798
- // The array starts with holes, which read as `undefined`. The element type
799
- // says so, thus it agrees with what `settleRound` accepts. Every index is
800
- // assigned except the index of a refused call, because the mode partition
801
- // below covers each call.
802
- const durations = new Array(toolUses.length);
803
- // A hole stays a hole for a call that described no result: an error, a
804
- // denial, and a tool with no result hook each leave the index unassigned,
805
- // and `settleRound` then reads the call detail.
806
- const resultDetails = new Array(toolUses.length);
807
- const records = new Array(toolUses.length);
808
- const stepTools = [];
809
- const inlineTools = [];
810
- for (const [idx, tu] of toolUses.entries()) {
811
- const mode = toolsById.get(tu.toolName)?.executionMode ?? "step";
812
- if (mode === "inline")
813
- inlineTools.push({ tu, idx });
814
- else
815
- stepTools.push({ tu, idx });
816
- }
817
- await Promise.all(stepTools.map(({ tu, idx }) => {
818
- const refusal = refusals[idx];
819
- const startedAt = performance.now();
820
- return runStep(toolStepName(tu.toolName, tu.toolCallId), () => dispatch(tu, refusal)).then((settled) => {
821
- if (refusal === undefined)
822
- durations[idx] = elapsedMs(startedAt);
823
- const dispatched = readDispatchedStep(settled);
824
- results[idx] = dispatched.result;
825
- if (dispatched.detail !== undefined)
826
- resultDetails[idx] = dispatched.detail;
827
- records[idx] = dispatched.record;
828
- });
829
- }));
830
- for (const { tu, idx } of inlineTools) {
831
- const refusal = refusals[idx];
832
- const startedAt = performance.now();
833
- const dispatched = await dispatch(tu, refusal);
834
- results[idx] = dispatched.result;
835
- if (dispatched.detail !== undefined)
836
- resultDetails[idx] = dispatched.detail;
837
- records[idx] = dispatched.record;
838
- if (refusal === undefined)
839
- durations[idx] = elapsedMs(startedAt);
840
- }
841
- // After the whole round, in call order: the parallel step calls settle in any
842
- // order, and a replay must fold the same records in the same sequence.
843
- for (const [idx, tu] of toolUses.entries()) {
844
- const record = records[idx];
845
- if (record !== undefined)
846
- foldCallRecord(toolsById.get(tu.toolName), tu, record, encoding.log);
847
- }
848
- return { results, durations, resultDetails };
849
- }
850
- /** Hand one call record to the tool that made it. A throw is logged, because the call has settled already. */
851
- function foldCallRecord(tool, tu, record, log) {
852
- if (tool?.foldCallRecord === undefined)
853
- return;
854
- try {
855
- tool.foldCallRecord(record);
856
- }
857
- catch (err) {
858
- log.error("a tool call record did not fold", { tool: tool.id, toolCallId: tu.toolCallId, ...log.errorFields(err) });
859
- }
860
- }
861
- /** Whole milliseconds since `startedAt`, on the monotonic clock that took that mark. */
862
- function elapsedMs(startedAt) {
863
- return Math.round(performance.now() - startedAt);
864
- }
865
- /**
866
- * Read one settled step-mode dispatch, tolerating the bare result beside the pair.
867
- *
868
- * A step-mode call goes through `runStep`, which caches what the body returned and hands
869
- * that cached value back on a replay. The durable step store therefore holds values that
870
- * a DIFFERENT build of this file wrote: a workflow in flight when the shape of the return
871
- * widened replays the bare `ToolResultPart` its own run cached, and it never re-enters the
872
- * body that would produce the pair. The replay boundary is the whole reason for the test:
873
- * a bare value carries no detail, thus the call keeps the line its `tool-started` showed.
874
- */
875
- function readDispatchedStep(settled) {
876
- return "result" in settled ? settled : { result: settled };
877
- }
878
- /** For a step-mode tool this runs inside the durable step of the call, thus a replay gives the stored excerpt. */
879
- async function dispatchTool(tu, toolsById, ctx, isFatalLoopError, encoding, cut, stepName) {
880
- const dispatched = await callTool(tu, toolsById, ctx, isFatalLoopError, encoding);
881
- return { ...dispatched, result: await cutLongResult(dispatched.result, cut, stepName) };
882
- }
883
- /** The excerpt does not depend on the outcome of the put, thus a replay gives the same bytes. */
884
- async function cutLongResult(result, cut, stepName) {
885
- const text = resultTextOf(result.output);
886
- if (text === undefined || text.length <= TOOL_RESULT_CAP)
887
- return result;
888
- if (cut.store === undefined)
889
- return withExcerpt(result, excerptOf(text, undefined));
890
- const { session } = cut;
891
- const ref = toolOutputRef(recordKeyFor(session, cut.invocationId, stepName));
892
- const content = keptTextOf(text);
893
- const put = await cut.store.put({
894
- analysisId: session.scope.analysisId,
895
- ref,
896
- toolName: result.toolName,
897
- toolCallId: result.toolCallId,
898
- // A run session carries the thread of the chat that started the run, but a run belongs to its analysis.
899
- ...(session.runFrame === undefined && session.scope.threadId !== undefined ? { threadId: session.scope.threadId } : {}),
900
- content,
901
- totalLength: text.length,
902
- });
903
- if (put.isErr()) {
904
- cut.log.warn("tool output not kept", { toolName: result.toolName, toolCallId: result.toolCallId, ref, errorType: put.error.type });
905
- }
906
- return withExcerpt(result, excerptOf(text, { ref, keptLength: content.length }));
907
- }
908
- async function callTool(tu, toolsById, ctx, isFatalLoopError, encoding) {
909
- const tool = toolsById.get(tu.toolName);
910
- if (tool === undefined) {
911
- labelToolFailure("unknown_tool");
912
- return { result: errorResult(tu, `unknown tool: ${tu.toolName}`) };
913
- }
914
- const parsed = tool.inputSchema.safeParse(tu.input);
915
- if (parsed.success)
916
- return execute(tu, tool, parsed.data, ctx, isFatalLoopError, encoding);
917
- // A complete JSON argument can arrive as a string behind function-call
918
- // markup or a code fence. Repair only makes the schema reachable — the
919
- // repaired value is validated in full, and the tool's own semantic checks
920
- // still run, so nothing here weakens validation.
921
- const repairedInput = repairToolInput(tu.input, parsed.error);
922
- const repaired = repairedInput === undefined ? undefined : tool.inputSchema.safeParse(repairedInput);
923
- if (repaired?.success === true)
924
- return execute(tu, tool, repaired.data, ctx, isFatalLoopError, encoding);
925
- // The Zod text can quote the input, thus the span and the log get only the shape of the failure.
926
- const issues = describeZodIssueShapes(parsed.error, tool.inputSchema);
927
- labelToolValidationFailure(issues);
928
- encoding.log.warn("tool call rejected", { tool: tool.id, toolCallId: tu.toolCallId, issues });
929
- return { result: errorResult(tu, `input validation failed: ${formatZodIssues(parsed.error, tu.input)}`) };
930
- }
931
- async function execute(tu, tool, input, ctx, isFatalLoopError, encoding) {
932
- try {
933
- const output = await tool.execute(input, ctx);
934
- if (output.isErr()) {
935
- markToolFailure(tu, tool, output.error, encoding.log);
936
- return { result: errorResult(tu, toolErrorContent(output.error)) };
937
- }
938
- // The one place a result description runs: the ok value in hand, the
939
- // input already validated, and the guard inside the compute.
940
- const detail = computeResultDetail(tool, input, output.value, encoding.log);
941
- const record = readToolCallRecord(output.value);
942
- const result = successResult(tu, output.value, encoding);
943
- return { result, ...(detail === undefined ? {} : { detail }), ...(record === undefined ? {} : { record }) };
944
- }
945
- catch (err) {
946
- if (isFatalLoopError(err))
947
- throw err;
948
- if (isAskRejected(err))
949
- return { result: deniedResult(tu, err.feedback) };
950
- markToolFailure(tu, tool, err, encoding.log);
951
- return { result: errorResult(tu, toolErrorContent(err)) };
952
- }
953
- }
954
- /**
955
- * Label the span of a tool call that our tool code failed. A `ToolError` is a
956
- * failure that the tool modeled, thus its own text is the account, with no
957
- * exception and no log. Any other `Error` is a fault that the tool did not
958
- * model: the span records the exception, and the log gets one warn.
959
- */
960
- function markToolFailure(tu, tool, failure, log) {
961
- const toolError = modeledToolError(failure);
962
- if (toolError !== undefined) {
963
- labelToolFailure("tool_error", toolError.error);
964
- }
965
- else if (failure instanceof Error) {
966
- recordToolException(failure);
967
- log.warn("tool call threw", { tool: tool.id, toolCallId: tu.toolCallId, ...log.errorFields(failure) });
968
- }
969
- else {
970
- labelToolFailure("_OTHER");
971
- }
972
- }
973
- /** The `ToolError` of a failure: the value itself, or the value that `unwrapOrThrow` wrapped in a `ResultError`. */
974
- function modeledToolError(failure) {
975
- if (isToolError(failure))
976
- return failure;
977
- return failure instanceof ResultError && isToolError(failure.value) ? failure.value : undefined;
978
- }
979
- /**
980
- * Normalize a tool's return value to plain JSON, stripping NUL from every string
981
- * it holds ({@link stripNulCharacters}). The reviver sees values, not keys — a
982
- * NUL in an object KEY would still reach the row, but keys come from tool result
983
- * types rather than scanned bytes, so nothing produces one.
984
- */
985
- function jsonValue(value) {
986
- return JSON.parse(JSON.stringify(value ?? null), (_key, v) => (typeof v === "string" ? stripNulCharacters(v) : v));
987
- }
988
- /**
989
- * Encode a tool ok value onto a tool-result message. A value with no picture
990
- * stays a plain JSON result, byte-identical to a value that never carried one.
991
- *
992
- * A value that carries pictures splits: the JSON data goes to a text part, and
993
- * the bytes ride as image content blocks after it, in the order that the tool
994
- * gave. Thus the model sees each picture, and the JSON text holds no bytes. When
995
- * the wire carries no picture, the blocks drop and the text stays, because
996
- * base64 text floods the context and the model cannot see it. One warn with the
997
- * count rides the log, thus an operator sees how many pictures did not reach the
998
- * model.
999
- *
1000
- * A wire that renders a picture on a user message only gets the fallback: the
1001
- * result keeps its JSON text, and the pictures go to the collector of the round.
1002
- * The round then sends the bytes after the tool message.
1003
- */
1004
- function successResult(toolCall, value, encoding) {
1005
- const jsonData = jsonValue(value);
1006
- const jsonOnly = {
1007
- type: "tool-result",
1008
- toolCallId: toolCall.toolCallId,
1009
- toolName: toolCall.toolName,
1010
- output: { type: "json", value: jsonData },
1011
- };
1012
- const images = readToolResultImages(value);
1013
- if (images.length === 0)
1014
- return jsonOnly;
1015
- if (encoding.placement === "drop") {
1016
- encoding.log.warn("the wired provider carries no picture in a tool result, thus the pictures are dropped", {
1017
- toolName: toolCall.toolName,
1018
- imageCount: images.length,
1019
- });
1020
- return jsonOnly;
1021
- }
1022
- if (encoding.placement === "user-message") {
1023
- encoding.deferredImages.push({ toolCallId: toolCall.toolCallId, toolName: toolCall.toolName, images });
1024
- return jsonOnly;
1025
- }
1026
- return {
1027
- type: "tool-result",
1028
- toolCallId: toolCall.toolCallId,
1029
- toolName: toolCall.toolName,
1030
- output: {
1031
- type: "content",
1032
- value: [
1033
- { type: "text", text: JSON.stringify(jsonData) },
1034
- ...images.map((image) => ({ type: "file", mediaType: image.mediaType, data: { type: "data", data: image.base64 } })),
1035
- ],
1036
- },
1037
- };
1038
- }
1039
- // NUL is stripped from the prose BEFORE it is serialized: past the stringify it
1040
- // is the six-character escape \u0000 — legitimate JSON text, and no longer
1041
- // distinguishable from an error that happens to quote that escape.
1042
- function toolErrorContent(value) {
1043
- const toolError = modeledToolError(value);
1044
- if (toolError !== undefined) {
1045
- return JSON.stringify({ error: stripNulCharacters(toolError.error), retryable: toolError.retryable });
1046
- }
1047
- const { retryable } = classifyProviderError(value);
1048
- const error = stripNulCharacters(value instanceof Error ? value.message : String(value));
1049
- return JSON.stringify({ error, retryable });
1050
- }
1051
- function errorResult(toolCall, content) {
1052
- return {
1053
- type: "tool-result",
1054
- toolCallId: toolCall.toolCallId,
1055
- toolName: toolCall.toolName,
1056
- // A thrown tool error routinely quotes stderr verbatim — as exposed to NUL as a success payload.
1057
- output: { type: "error-text", value: stripNulCharacters(content) },
1058
- };
1059
- }
1060
- function isAskRejected(err) {
1061
- // Name-based fallback recognizes a rejection thrown from a different module
1062
- // realm, where `instanceof` against this file's class reference would miss it.
1063
- return err instanceof AskRejectedError || (err instanceof Error && err.name === "AskRejectedError");
1064
- }
1065
- /**
1066
- * Map a rejected approval to a model-visible `execution-denied` tool result. The
1067
- * prose is the model's only account of the denial; `outcomeOf` reports it as
1068
- * `denied`, distinct from a fault.
1069
- */
1070
- function deniedResult(toolCall, feedback) {
1071
- const reason = feedback === undefined || feedback.length === 0
1072
- ? "The user rejected this action."
1073
- : `The user rejected this action with the following feedback: ${feedback}`;
1074
- return {
1075
- type: "tool-result",
1076
- toolCallId: toolCall.toolCallId,
1077
- toolName: toolCall.toolName,
1078
- output: { type: "execution-denied", reason },
1079
- };
1080
- }
1081
- function outcomeOf(result) {
1082
- return toolOutcomeForOutputType(result.output.type);
1083
- }
1084
- /**
1085
- * The optional `detail` field, present only when there is one. Spread rather
1086
- * than assigned so an undescribed call emits an event with no `detail` key at
1087
- * all, which is what the contract promises: absent, never empty.
1088
- */
1089
- function detailField(detail) {
1090
- return detail === undefined ? {} : { detail };
1091
- }
1092
- /**
1093
- * The optional `durationMs` field, present only for a call that the loop measured.
1094
- *
1095
- * Spread rather than assigned, for the same reason as {@link detailField}: an
1096
- * unmeasured call emits no `durationMs` key. A zero here is a real measurement of a
1097
- * call that took under half a millisecond, and it is never a stand-in for no figure.
1098
- */
1099
- function durationField(durationMs) {
1100
- return durationMs === undefined ? {} : { durationMs };
1101
- }
1102
- function hasDenial(results) {
1103
- return results.some((r) => r.output.type === "execution-denied");
1104
- }
1105
- export function finalText(messages) {
1106
- const last = messages.at(-1);
1107
- if (last === undefined || last.role !== "assistant")
1108
- return "";
1109
- if (typeof last.content === "string")
1110
- return last.content;
1111
- return last.content
1112
- .filter((block) => block.type === "text")
1113
- .map((block) => block.text)
1114
- .join("");
1115
- }
1116
- function formatZodIssues(error, input) {
1117
- return error.issues
1118
- .map((issue) => {
1119
- const path = issue.path.length > 0 ? issue.path.join(".") : "(root)";
1120
- const hint = hintForZodIssue(issue, input);
1121
- return hint === undefined ? `${path}: ${issue.message}` : `${path}: ${issue.message} — ${hint}`;
1122
- })
1123
- .join("; ");
1124
- }