@namzu/sdk 31.1.0 → 33.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (355) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/dist/agents/ReactiveAgent.d.ts.map +1 -1
  3. package/dist/agents/ReactiveAgent.js +4 -0
  4. package/dist/agents/ReactiveAgent.js.map +1 -1
  5. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  6. package/dist/agents/SupervisorAgent.js +4 -0
  7. package/dist/agents/SupervisorAgent.js.map +1 -1
  8. package/dist/agents/runAgent.d.ts +4 -0
  9. package/dist/agents/runAgent.d.ts.map +1 -1
  10. package/dist/agents/runAgent.js +4 -0
  11. package/dist/agents/runAgent.js.map +1 -1
  12. package/dist/authorization/gate.d.ts +1 -1
  13. package/dist/authorization/gate.js +1 -1
  14. package/dist/connector/index.d.ts +1 -1
  15. package/dist/connector/index.d.ts.map +1 -1
  16. package/dist/connector/index.js +1 -1
  17. package/dist/connector/index.js.map +1 -1
  18. package/dist/connector/mcp/adapter.d.ts.map +1 -1
  19. package/dist/connector/mcp/adapter.js +71 -16
  20. package/dist/connector/mcp/adapter.js.map +1 -1
  21. package/dist/connector/mcp/client.d.ts.map +1 -1
  22. package/dist/connector/mcp/client.js +3 -2
  23. package/dist/connector/mcp/client.js.map +1 -1
  24. package/dist/connector/mcp/http-redirect.d.ts +17 -0
  25. package/dist/connector/mcp/http-redirect.d.ts.map +1 -0
  26. package/dist/connector/mcp/http-redirect.js +31 -0
  27. package/dist/connector/mcp/http-redirect.js.map +1 -0
  28. package/dist/connector/mcp/http-sse.d.ts.map +1 -1
  29. package/dist/connector/mcp/http-sse.js +5 -0
  30. package/dist/connector/mcp/http-sse.js.map +1 -1
  31. package/dist/connector/mcp/image-admission.d.ts +16 -0
  32. package/dist/connector/mcp/image-admission.d.ts.map +1 -0
  33. package/dist/connector/mcp/image-admission.js +288 -0
  34. package/dist/connector/mcp/image-admission.js.map +1 -0
  35. package/dist/connector/mcp/stdio.d.ts +13 -0
  36. package/dist/connector/mcp/stdio.d.ts.map +1 -1
  37. package/dist/connector/mcp/stdio.js +66 -23
  38. package/dist/connector/mcp/stdio.js.map +1 -1
  39. package/dist/connector/mcp/streamable-http.d.ts.map +1 -1
  40. package/dist/connector/mcp/streamable-http.js +3 -0
  41. package/dist/connector/mcp/streamable-http.js.map +1 -1
  42. package/dist/constants/tools/index.js +1 -1
  43. package/dist/constants/tools/index.js.map +1 -1
  44. package/dist/directory/derive-supervisor.d.ts.map +1 -1
  45. package/dist/directory/derive-supervisor.js +5 -0
  46. package/dist/directory/derive-supervisor.js.map +1 -1
  47. package/dist/directory/derive.d.ts.map +1 -1
  48. package/dist/directory/derive.js +5 -0
  49. package/dist/directory/derive.js.map +1 -1
  50. package/dist/directory/load.d.ts.map +1 -1
  51. package/dist/directory/load.js +1 -0
  52. package/dist/directory/load.js.map +1 -1
  53. package/dist/directory/types.d.ts +2 -0
  54. package/dist/directory/types.d.ts.map +1 -1
  55. package/dist/execution/base.d.ts +13 -0
  56. package/dist/execution/base.d.ts.map +1 -1
  57. package/dist/execution/base.js +103 -8
  58. package/dist/execution/base.js.map +1 -1
  59. package/dist/execution/code-runtime/types.d.ts +11 -1
  60. package/dist/execution/code-runtime/types.d.ts.map +1 -1
  61. package/dist/execution/code-runtime/types.js.map +1 -1
  62. package/dist/execution/code-runtime/worker.d.ts.map +1 -1
  63. package/dist/execution/code-runtime/worker.js +67 -9
  64. package/dist/execution/code-runtime/worker.js.map +1 -1
  65. package/dist/execution/errors.d.ts +12 -0
  66. package/dist/execution/errors.d.ts.map +1 -0
  67. package/dist/execution/errors.js +21 -0
  68. package/dist/execution/errors.js.map +1 -0
  69. package/dist/execution/factory.d.ts.map +1 -1
  70. package/dist/execution/factory.js +1 -0
  71. package/dist/execution/factory.js.map +1 -1
  72. package/dist/execution/hybrid.d.ts +5 -0
  73. package/dist/execution/hybrid.d.ts.map +1 -1
  74. package/dist/execution/hybrid.js +36 -16
  75. package/dist/execution/hybrid.js.map +1 -1
  76. package/dist/execution/index.d.ts +1 -0
  77. package/dist/execution/index.d.ts.map +1 -1
  78. package/dist/execution/index.js +1 -0
  79. package/dist/execution/index.js.map +1 -1
  80. package/dist/execution/local.d.ts +13 -0
  81. package/dist/execution/local.d.ts.map +1 -1
  82. package/dist/execution/local.js +145 -19
  83. package/dist/execution/local.js.map +1 -1
  84. package/dist/execution/output.d.ts +27 -0
  85. package/dist/execution/output.d.ts.map +1 -0
  86. package/dist/execution/output.js +105 -0
  87. package/dist/execution/output.js.map +1 -0
  88. package/dist/execution/remote.d.ts +26 -2
  89. package/dist/execution/remote.d.ts.map +1 -1
  90. package/dist/execution/remote.js +86 -15
  91. package/dist/execution/remote.js.map +1 -1
  92. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  93. package/dist/manager/agent/lifecycle.js +1 -0
  94. package/dist/manager/agent/lifecycle.js.map +1 -1
  95. package/dist/plugin/lifecycle.d.ts.map +1 -1
  96. package/dist/plugin/lifecycle.js +9 -3
  97. package/dist/plugin/lifecycle.js.map +1 -1
  98. package/dist/process/kill-tree.d.ts +11 -13
  99. package/dist/process/kill-tree.d.ts.map +1 -1
  100. package/dist/process/kill-tree.js +11 -13
  101. package/dist/process/kill-tree.js.map +1 -1
  102. package/dist/provider/errors.d.ts +1 -0
  103. package/dist/provider/errors.d.ts.map +1 -1
  104. package/dist/provider/errors.js +39 -1
  105. package/dist/provider/errors.js.map +1 -1
  106. package/dist/provider/fallback.d.ts.map +1 -1
  107. package/dist/provider/fallback.js +31 -0
  108. package/dist/provider/fallback.js.map +1 -1
  109. package/dist/provider/idle-timeout.d.ts.map +1 -1
  110. package/dist/provider/idle-timeout.js +5 -0
  111. package/dist/provider/idle-timeout.js.map +1 -1
  112. package/dist/provider/retry.d.ts.map +1 -1
  113. package/dist/provider/retry.js +5 -0
  114. package/dist/provider/retry.js.map +1 -1
  115. package/dist/public-runtime.d.ts +7 -5
  116. package/dist/public-runtime.d.ts.map +1 -1
  117. package/dist/public-runtime.js +9 -4
  118. package/dist/public-runtime.js.map +1 -1
  119. package/dist/public-types.d.ts +2 -1
  120. package/dist/public-types.d.ts.map +1 -1
  121. package/dist/registry/tool/execute.d.ts +7 -1
  122. package/dist/registry/tool/execute.d.ts.map +1 -1
  123. package/dist/registry/tool/execute.js +201 -54
  124. package/dist/registry/tool/execute.js.map +1 -1
  125. package/dist/run/command-gate.d.ts.map +1 -1
  126. package/dist/run/command-gate.js +11 -0
  127. package/dist/run/command-gate.js.map +1 -1
  128. package/dist/run/reporter.d.ts.map +1 -1
  129. package/dist/run/reporter.js +6 -1
  130. package/dist/run/reporter.js.map +1 -1
  131. package/dist/run/workspace-fingerprint.d.ts +4 -4
  132. package/dist/run/workspace-fingerprint.d.ts.map +1 -1
  133. package/dist/run/workspace-fingerprint.js +6 -4
  134. package/dist/run/workspace-fingerprint.js.map +1 -1
  135. package/dist/runtime/bidi/index.d.ts +1 -1
  136. package/dist/runtime/bidi/index.d.ts.map +1 -1
  137. package/dist/runtime/bidi/index.js +1 -1
  138. package/dist/runtime/bidi/index.js.map +1 -1
  139. package/dist/runtime/bidi/session.d.ts +19 -0
  140. package/dist/runtime/bidi/session.d.ts.map +1 -1
  141. package/dist/runtime/bidi/session.js +150 -26
  142. package/dist/runtime/bidi/session.js.map +1 -1
  143. package/dist/runtime/query/executor.d.ts +43 -1
  144. package/dist/runtime/query/executor.d.ts.map +1 -1
  145. package/dist/runtime/query/executor.js +824 -174
  146. package/dist/runtime/query/executor.js.map +1 -1
  147. package/dist/runtime/query/index.d.ts +5 -0
  148. package/dist/runtime/query/index.d.ts.map +1 -1
  149. package/dist/runtime/query/index.js +47 -4
  150. package/dist/runtime/query/index.js.map +1 -1
  151. package/dist/runtime/query/iteration/index.d.ts +1 -0
  152. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  153. package/dist/runtime/query/iteration/index.js +34 -14
  154. package/dist/runtime/query/iteration/index.js.map +1 -1
  155. package/dist/runtime/query/iteration/phases/advisory.js +2 -2
  156. package/dist/runtime/query/iteration/phases/advisory.js.map +1 -1
  157. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  158. package/dist/runtime/query/iteration/phases/tool-review.js +84 -22
  159. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  160. package/dist/runtime/query/iteration/provider-rejected-image.d.ts +13 -0
  161. package/dist/runtime/query/iteration/provider-rejected-image.d.ts.map +1 -0
  162. package/dist/runtime/query/iteration/provider-rejected-image.js +91 -0
  163. package/dist/runtime/query/iteration/provider-rejected-image.js.map +1 -0
  164. package/dist/runtime/query/iteration/stream-turn.d.ts +4 -1
  165. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  166. package/dist/runtime/query/iteration/stream-turn.js +7 -3
  167. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  168. package/dist/runtime/query/request-rich-content.d.ts +13 -0
  169. package/dist/runtime/query/request-rich-content.d.ts.map +1 -1
  170. package/dist/runtime/query/request-rich-content.js +173 -17
  171. package/dist/runtime/query/request-rich-content.js.map +1 -1
  172. package/dist/runtime/query/result.d.ts.map +1 -1
  173. package/dist/runtime/query/result.js +1 -0
  174. package/dist/runtime/query/result.js.map +1 -1
  175. package/dist/runtime/query/resume-pending.d.ts +5 -1
  176. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  177. package/dist/runtime/query/resume-pending.js +66 -1
  178. package/dist/runtime/query/resume-pending.js.map +1 -1
  179. package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
  180. package/dist/runtime/query/tool-output-budget.js +45 -16
  181. package/dist/runtime/query/tool-output-budget.js.map +1 -1
  182. package/dist/runtime/query/tooling.d.ts +6 -0
  183. package/dist/runtime/query/tooling.d.ts.map +1 -1
  184. package/dist/runtime/query/tooling.js +4 -0
  185. package/dist/runtime/query/tooling.js.map +1 -1
  186. package/dist/sandbox/provider/local.d.ts.map +1 -1
  187. package/dist/sandbox/provider/local.js +193 -41
  188. package/dist/sandbox/provider/local.js.map +1 -1
  189. package/dist/scheduler/delegating.d.ts +2 -1
  190. package/dist/scheduler/delegating.d.ts.map +1 -1
  191. package/dist/scheduler/delegating.js +6 -5
  192. package/dist/scheduler/delegating.js.map +1 -1
  193. package/dist/scheduler/local.d.ts +2 -1
  194. package/dist/scheduler/local.d.ts.map +1 -1
  195. package/dist/scheduler/local.js +2 -2
  196. package/dist/scheduler/local.js.map +1 -1
  197. package/dist/session/workspace/driver.d.ts +12 -4
  198. package/dist/session/workspace/driver.d.ts.map +1 -1
  199. package/dist/session/workspace/git-worktree.d.ts +17 -3
  200. package/dist/session/workspace/git-worktree.d.ts.map +1 -1
  201. package/dist/session/workspace/git-worktree.js +153 -34
  202. package/dist/session/workspace/git-worktree.js.map +1 -1
  203. package/dist/skills/registry.d.ts +15 -0
  204. package/dist/skills/registry.d.ts.map +1 -1
  205. package/dist/skills/registry.js +33 -0
  206. package/dist/skills/registry.js.map +1 -1
  207. package/dist/store/attachment/index.d.ts +24 -2
  208. package/dist/store/attachment/index.d.ts.map +1 -1
  209. package/dist/store/attachment/index.js +52 -5
  210. package/dist/store/attachment/index.js.map +1 -1
  211. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  212. package/dist/tools/builtins/computer-use.js +39 -2
  213. package/dist/tools/builtins/computer-use.js.map +1 -1
  214. package/dist/tools/builtins/lsp.d.ts +1 -1
  215. package/dist/tools/builtins/run-code.d.ts.map +1 -1
  216. package/dist/tools/builtins/run-code.js +24 -16
  217. package/dist/tools/builtins/run-code.js.map +1 -1
  218. package/dist/tools/builtins/skill.d.ts +2 -1
  219. package/dist/tools/builtins/skill.d.ts.map +1 -1
  220. package/dist/tools/builtins/skill.js +250 -6
  221. package/dist/tools/builtins/skill.js.map +1 -1
  222. package/dist/tools/coordinator/agent.d.ts.map +1 -1
  223. package/dist/tools/coordinator/agent.js +100 -30
  224. package/dist/tools/coordinator/agent.js.map +1 -1
  225. package/dist/types/agent/base.d.ts +4 -0
  226. package/dist/types/agent/base.d.ts.map +1 -1
  227. package/dist/types/agent/factory.d.ts +1 -0
  228. package/dist/types/agent/factory.d.ts.map +1 -1
  229. package/dist/types/agent/manager.d.ts +1 -1
  230. package/dist/types/agent/manager.d.ts.map +1 -1
  231. package/dist/types/agent/scheduler.d.ts +10 -1
  232. package/dist/types/agent/scheduler.d.ts.map +1 -1
  233. package/dist/types/authorization/index.d.ts +12 -12
  234. package/dist/types/bidi/index.d.ts +19 -2
  235. package/dist/types/bidi/index.d.ts.map +1 -1
  236. package/dist/types/computer-use/index.d.ts +18 -0
  237. package/dist/types/computer-use/index.d.ts.map +1 -1
  238. package/dist/types/connector/execution-context.d.ts +3 -1
  239. package/dist/types/connector/execution-context.d.ts.map +1 -1
  240. package/dist/types/execution/index.d.ts +43 -1
  241. package/dist/types/execution/index.d.ts.map +1 -1
  242. package/dist/types/hitl/index.d.ts +11 -0
  243. package/dist/types/hitl/index.d.ts.map +1 -1
  244. package/dist/types/hitl/index.js.map +1 -1
  245. package/dist/types/message/index.d.ts +31 -1
  246. package/dist/types/message/index.d.ts.map +1 -1
  247. package/dist/types/message/index.js +43 -0
  248. package/dist/types/message/index.js.map +1 -1
  249. package/dist/types/plugin/index.d.ts +14 -1
  250. package/dist/types/plugin/index.d.ts.map +1 -1
  251. package/dist/types/plugin/index.js +1 -0
  252. package/dist/types/plugin/index.js.map +1 -1
  253. package/dist/types/provider/error.d.ts +4 -1
  254. package/dist/types/provider/error.d.ts.map +1 -1
  255. package/dist/types/provider/interface.d.ts +14 -0
  256. package/dist/types/provider/interface.d.ts.map +1 -1
  257. package/dist/types/run/events.d.ts +10 -2
  258. package/dist/types/run/events.d.ts.map +1 -1
  259. package/dist/types/run/events.js.map +1 -1
  260. package/dist/types/sandbox/index.d.ts +15 -10
  261. package/dist/types/sandbox/index.d.ts.map +1 -1
  262. package/dist/types/sandbox/index.js.map +1 -1
  263. package/dist/types/tool/index.d.ts +129 -12
  264. package/dist/types/tool/index.d.ts.map +1 -1
  265. package/dist/types/tool/index.js.map +1 -1
  266. package/dist/types/workspace/ref.d.ts +5 -0
  267. package/dist/types/workspace/ref.d.ts.map +1 -1
  268. package/dist/utils/abort.d.ts +8 -0
  269. package/dist/utils/abort.d.ts.map +1 -1
  270. package/dist/utils/abort.js +16 -0
  271. package/dist/utils/abort.js.map +1 -1
  272. package/package.json +4 -4
  273. package/src/agents/ReactiveAgent.ts +4 -0
  274. package/src/agents/SupervisorAgent.ts +4 -0
  275. package/src/agents/runAgent.ts +8 -0
  276. package/src/authorization/gate.ts +1 -1
  277. package/src/connector/index.ts +2 -0
  278. package/src/connector/mcp/adapter.ts +82 -21
  279. package/src/connector/mcp/client.ts +2 -1
  280. package/src/connector/mcp/http-redirect.ts +32 -0
  281. package/src/connector/mcp/http-sse.ts +5 -0
  282. package/src/connector/mcp/image-admission.ts +272 -0
  283. package/src/connector/mcp/stdio.ts +63 -21
  284. package/src/connector/mcp/streamable-http.ts +3 -0
  285. package/src/constants/tools/index.ts +1 -1
  286. package/src/directory/derive-supervisor.ts +5 -0
  287. package/src/directory/derive.ts +5 -0
  288. package/src/directory/load.ts +1 -0
  289. package/src/directory/types.ts +2 -0
  290. package/src/execution/base.ts +124 -7
  291. package/src/execution/code-runtime/types.ts +15 -1
  292. package/src/execution/code-runtime/worker.ts +72 -9
  293. package/src/execution/errors.ts +24 -0
  294. package/src/execution/factory.ts +1 -0
  295. package/src/execution/hybrid.ts +47 -16
  296. package/src/execution/index.ts +1 -0
  297. package/src/execution/local.ts +164 -19
  298. package/src/execution/output.ts +104 -0
  299. package/src/execution/remote.ts +122 -17
  300. package/src/manager/agent/lifecycle.ts +1 -0
  301. package/src/plugin/lifecycle.ts +9 -3
  302. package/src/process/kill-tree.ts +11 -13
  303. package/src/provider/errors.ts +40 -1
  304. package/src/provider/fallback.ts +39 -0
  305. package/src/provider/idle-timeout.ts +8 -0
  306. package/src/provider/retry.ts +8 -0
  307. package/src/public-runtime.ts +23 -2
  308. package/src/public-types.ts +11 -0
  309. package/src/registry/tool/execute.ts +230 -64
  310. package/src/run/command-gate.ts +12 -0
  311. package/src/run/reporter.ts +7 -1
  312. package/src/run/workspace-fingerprint.ts +5 -4
  313. package/src/runtime/bidi/index.ts +1 -1
  314. package/src/runtime/bidi/session.ts +177 -28
  315. package/src/runtime/query/executor.ts +987 -172
  316. package/src/runtime/query/index.ts +59 -6
  317. package/src/runtime/query/iteration/index.ts +69 -29
  318. package/src/runtime/query/iteration/phases/advisory.ts +2 -2
  319. package/src/runtime/query/iteration/phases/tool-review.ts +87 -23
  320. package/src/runtime/query/iteration/provider-rejected-image.ts +106 -0
  321. package/src/runtime/query/iteration/stream-turn.ts +12 -2
  322. package/src/runtime/query/request-rich-content.ts +213 -15
  323. package/src/runtime/query/result.ts +1 -0
  324. package/src/runtime/query/resume-pending.ts +83 -1
  325. package/src/runtime/query/tool-output-budget.ts +52 -17
  326. package/src/runtime/query/tooling.ts +10 -0
  327. package/src/sandbox/provider/local.ts +213 -45
  328. package/src/scheduler/delegating.ts +6 -5
  329. package/src/scheduler/local.ts +3 -2
  330. package/src/session/workspace/driver.ts +12 -4
  331. package/src/session/workspace/git-worktree.ts +173 -34
  332. package/src/skills/registry.ts +47 -0
  333. package/src/store/attachment/index.ts +100 -15
  334. package/src/tools/builtins/computer-use.ts +45 -2
  335. package/src/tools/builtins/run-code.ts +24 -16
  336. package/src/tools/builtins/skill.ts +325 -7
  337. package/src/tools/coordinator/agent.ts +108 -33
  338. package/src/types/agent/base.ts +4 -0
  339. package/src/types/agent/factory.ts +1 -0
  340. package/src/types/agent/manager.ts +1 -1
  341. package/src/types/agent/scheduler.ts +10 -1
  342. package/src/types/bidi/index.ts +19 -2
  343. package/src/types/computer-use/index.ts +19 -0
  344. package/src/types/connector/execution-context.ts +3 -0
  345. package/src/types/execution/index.ts +46 -1
  346. package/src/types/hitl/index.ts +11 -0
  347. package/src/types/message/index.ts +78 -1
  348. package/src/types/plugin/index.ts +15 -0
  349. package/src/types/provider/error.ts +4 -1
  350. package/src/types/provider/interface.ts +18 -0
  351. package/src/types/run/events.ts +18 -4
  352. package/src/types/sandbox/index.ts +15 -10
  353. package/src/types/tool/index.ts +159 -15
  354. package/src/types/workspace/ref.ts +5 -0
  355. package/src/utils/abort.ts +18 -0
@@ -22,6 +22,8 @@ import type { MessageStopReason } from '../../../types/run/stop-reason.js'
22
22
  import { generateMessageId } from '../../../utils/id.js'
23
23
  import type { Logger } from '../../../utils/logger.js'
24
24
  import type { EmitEvent } from '../events.js'
25
+ import type { RequestImageIdentity } from '../request-rich-content.js'
26
+ import { streamWithProviderRejectedImageRecovery } from './provider-rejected-image.js'
25
27
 
26
28
  /**
27
29
  * Map a provider's coarse `finishReason` plus the orchestrator's
@@ -147,6 +149,9 @@ export async function* streamProviderTurn(
147
149
  * Optional, so a caller with no use for the id is unchanged.
148
150
  */
149
151
  announceAs?: import('../../../types/ids/index.js').MessageId,
152
+ imageRecovery?: {
153
+ readonly onAccepted: (identity: RequestImageIdentity) => Promise<void>
154
+ },
150
155
  ): AsyncGenerator<RunEvent, StreamingTurnResult> {
151
156
  // The `chat {model}` span the GenAI conventions require. There was none:
152
157
  // `chatSpanName` existed with zero call sites, so a trace carried no LLM
@@ -224,10 +229,15 @@ export async function* streamProviderTurn(
224
229
  let streamError: string | undefined
225
230
  let streamCause: unknown
226
231
 
227
- const stream = provider.chatStream({
232
+ const streamParams = {
228
233
  ...params,
229
234
  stream: true,
230
- }) as AsyncIterable<StreamChunk>
235
+ } satisfies import('../../../types/provider/index.js').ChatCompletionParams
236
+ const stream = (
237
+ imageRecovery
238
+ ? streamWithProviderRejectedImageRecovery(provider, streamParams, imageRecovery.onAccepted)
239
+ : provider.chatStream(streamParams)
240
+ ) as AsyncIterable<StreamChunk>
231
241
 
232
242
  // Drive the stream manually so each `.next()` can be RACED against the run
233
243
  // abort: a Stop tears the in-flight model request down (the provider got
@@ -34,6 +34,12 @@ interface RichOccurrence {
34
34
  readonly source: 'user' | 'tool'
35
35
  readonly kind: RichKind
36
36
  readonly bytes: number
37
+ readonly reason: 'budget' | 'provider-rejected' | 'invalid-image'
38
+ }
39
+
40
+ export interface RequestImageIdentity {
41
+ readonly data: string
42
+ readonly mediaType: string
37
43
  }
38
44
 
39
45
  const userMarker = (kind: RichKind): string =>
@@ -42,6 +48,18 @@ const userMarker = (kind: RichKind): string =>
42
48
  const toolMarker = (kind: RichKind): string =>
43
49
  `[${kind} omitted from this model request to keep the accumulated rich-content payload within its configured size limit; call the producing tool again if it is still needed.]`
44
50
 
51
+ const rejectedUserImageMarker = (): string =>
52
+ '[image omitted from this model request because the provider rejected this image; attach a corrected image in a new message if it is still needed.]'
53
+
54
+ const rejectedToolImageMarker = (): string =>
55
+ '[image omitted from this model request because the provider rejected this image; call the producing tool again after correcting its image source if it is still needed.]'
56
+
57
+ const invalidUserImageMarker = (): string =>
58
+ '[image omitted from this model request because its encoded bytes are not a complete supported raster matching the declared media type; attach a corrected image in a new message if it is still needed.]'
59
+
60
+ const invalidToolImageMarker = (): string =>
61
+ '[image omitted from this model request because the producing tool returned encoded bytes that are not a complete supported raster matching the declared media type; call the tool again after correcting its image source if it is still needed.]'
62
+
45
63
  function assertInlineAttachment(
46
64
  attachment: MessageAttachment,
47
65
  ): asserts attachment is Exclude<MessageAttachment, { readonly type: 'stored' }> {
@@ -65,6 +83,13 @@ function collectRichOccurrences(messages: readonly Message[]): RichOccurrence[]
65
83
  source: 'user',
66
84
  kind: attachment.type === 'document' ? 'document' : 'image',
67
85
  bytes: attachment.data.length,
86
+ reason:
87
+ attachment.type !== 'document' && attachment.modelOmission?.reason === 'invalid-image'
88
+ ? 'invalid-image'
89
+ : attachment.type !== 'document' &&
90
+ attachment.modelOmission?.reason === 'provider-rejected'
91
+ ? 'provider-rejected'
92
+ : 'budget',
68
93
  })
69
94
  }
70
95
  continue
@@ -79,6 +104,12 @@ function collectRichOccurrences(messages: readonly Message[]): RichOccurrence[]
79
104
  source: 'tool',
80
105
  kind: block.type,
81
106
  bytes: block.data.length,
107
+ reason:
108
+ block.type === 'image' && block.modelOmission?.reason === 'invalid-image'
109
+ ? 'invalid-image'
110
+ : block.type === 'image' && block.modelOmission?.reason === 'provider-rejected'
111
+ ? 'provider-rejected'
112
+ : 'budget',
82
113
  })
83
114
  }
84
115
  }
@@ -86,10 +117,21 @@ function collectRichOccurrences(messages: readonly Message[]): RichOccurrence[]
86
117
  return occurrences
87
118
  }
88
119
 
89
- function appendMarkers(content: string, kinds: readonly RichKind[]): string {
90
- if (kinds.length === 0) return content
91
- const markers = kinds.map(userMarker).join('\n\n')
92
- return content.length > 0 ? `${content}\n\n${markers}` : markers
120
+ function appendTextMarkers(content: string, markers: readonly string[]): string {
121
+ if (markers.length === 0) return content
122
+ const joined = markers.join('\n\n')
123
+ return content.length > 0 ? `${content}\n\n${joined}` : joined
124
+ }
125
+
126
+ function appendMarkers(content: string, occurrences: readonly RichOccurrence[]): string {
127
+ const markers = occurrences.map((occurrence) =>
128
+ occurrence.reason === 'provider-rejected'
129
+ ? rejectedUserImageMarker()
130
+ : occurrence.reason === 'invalid-image'
131
+ ? invalidUserImageMarker()
132
+ : userMarker(occurrence.kind),
133
+ )
134
+ return appendTextMarkers(content, markers)
93
135
  }
94
136
 
95
137
  /**
@@ -103,15 +145,10 @@ function appendMarkers(content: string, kinds: readonly RichKind[]): string {
103
145
  */
104
146
  export function projectRequestRichContent(messages: Message[], maxBytes: number): Message[] {
105
147
  const occurrences = collectRichOccurrences(messages)
106
- if (maxBytes === 0 || occurrences.length === 0) return messages
107
-
108
- let total = occurrences.reduce((sum, occurrence) => sum + occurrence.bytes, 0)
109
- if (total <= maxBytes) return messages
148
+ if (occurrences.length === 0) return messages
110
149
 
111
150
  const omitted = new Map<number, Map<number, RichOccurrence>>()
112
- for (const occurrence of occurrences) {
113
- if (total <= maxBytes) break
114
- total -= occurrence.bytes
151
+ const omit = (occurrence: RichOccurrence): void => {
115
152
  let byItem = omitted.get(occurrence.messageIndex)
116
153
  if (!byItem) {
117
154
  byItem = new Map()
@@ -120,6 +157,26 @@ export function projectRequestRichContent(messages: Message[], maxBytes: number)
120
157
  byItem.set(occurrence.itemIndex, occurrence)
121
158
  }
122
159
 
160
+ for (const occurrence of occurrences) {
161
+ if (occurrence.reason === 'provider-rejected' || occurrence.reason === 'invalid-image') {
162
+ omit(occurrence)
163
+ }
164
+ }
165
+
166
+ let total = occurrences.reduce(
167
+ (sum, occurrence) => sum + (occurrence.reason === 'budget' ? occurrence.bytes : 0),
168
+ 0,
169
+ )
170
+ if (maxBytes > 0 && total > maxBytes) {
171
+ for (const occurrence of occurrences) {
172
+ if (total <= maxBytes) break
173
+ if (occurrence.reason !== 'budget') continue
174
+ total -= occurrence.bytes
175
+ omit(occurrence)
176
+ }
177
+ }
178
+ if (omitted.size === 0) return messages
179
+
123
180
  return messages.map((message, messageIndex) => {
124
181
  const byItem = omitted.get(messageIndex)
125
182
  if (!byItem) return message
@@ -127,13 +184,12 @@ export function projectRequestRichContent(messages: Message[], maxBytes: number)
127
184
  if (message.role === 'user') {
128
185
  const { attachments = [], ...withoutAttachments } = message
129
186
  const kept = attachments.filter((_attachment, itemIndex) => !byItem.has(itemIndex))
130
- const omittedKinds = [...byItem.values()]
187
+ const omittedOccurrences = [...byItem.values()]
131
188
  .filter((occurrence) => occurrence.source === 'user')
132
189
  .sort((a, b) => a.itemIndex - b.itemIndex)
133
- .map((occurrence) => occurrence.kind)
134
190
  return {
135
191
  ...withoutAttachments,
136
- content: appendMarkers(message.content, omittedKinds),
192
+ content: appendMarkers(message.content, omittedOccurrences),
137
193
  ...(kept.length > 0 ? { attachments: kept } : {}),
138
194
  }
139
195
  }
@@ -142,7 +198,15 @@ export function projectRequestRichContent(messages: Message[], maxBytes: number)
142
198
  const content: ToolResultBlock[] = message.content.map((block, itemIndex) => {
143
199
  const occurrence = byItem.get(itemIndex)
144
200
  return occurrence?.source === 'tool'
145
- ? { type: 'text', text: toolMarker(occurrence.kind) }
201
+ ? {
202
+ type: 'text',
203
+ text:
204
+ occurrence.reason === 'provider-rejected'
205
+ ? rejectedToolImageMarker()
206
+ : occurrence.reason === 'invalid-image'
207
+ ? invalidToolImageMarker()
208
+ : toolMarker(occurrence.kind),
209
+ }
146
210
  : block
147
211
  })
148
212
  return { ...message, content }
@@ -151,3 +215,137 @@ export function projectRequestRichContent(messages: Message[], maxBytes: number)
151
215
  return message
152
216
  })
153
217
  }
218
+
219
+ function sameImage(
220
+ image: { readonly data: string; readonly mediaType: string },
221
+ identity: RequestImageIdentity,
222
+ ): boolean {
223
+ return image.data === identity.data && image.mediaType === identity.mediaType
224
+ }
225
+
226
+ /** Find the one distinct image in the exact provider-bound request, if one exists. */
227
+ export function findSingleRequestImage(messages: readonly Message[]): RequestImageIdentity | null {
228
+ let candidate: RequestImageIdentity | null = null
229
+ for (const message of messages) {
230
+ if (message.role === 'user') {
231
+ for (const attachment of message.attachments ?? []) {
232
+ if (attachment.type === 'stored') {
233
+ throw new TypeError(
234
+ 'Cannot inspect an unresolved stored attachment at the provider boundary',
235
+ )
236
+ }
237
+ if (attachment.type === 'document') continue
238
+ if (candidate === null) {
239
+ candidate = {
240
+ data: attachment.data,
241
+ mediaType: attachment.mediaType,
242
+ }
243
+ } else if (!sameImage(attachment, candidate)) {
244
+ return null
245
+ }
246
+ }
247
+ continue
248
+ }
249
+ if (message.role !== 'tool' || !Array.isArray(message.content)) continue
250
+ for (const block of message.content) {
251
+ if (block.type !== 'image') continue
252
+ if (candidate === null) {
253
+ candidate = { data: block.data, mediaType: block.mediaType }
254
+ } else if (!sameImage(block, candidate)) {
255
+ return null
256
+ }
257
+ }
258
+ }
259
+ return candidate
260
+ }
261
+
262
+ /** Build the one-off retry request without changing durable conversation state. */
263
+ export function projectRequestWithoutImage(
264
+ messages: Message[],
265
+ identity: RequestImageIdentity,
266
+ ): Message[] {
267
+ return messages.map((message) => {
268
+ if (message.role === 'user') {
269
+ const attachments = message.attachments ?? []
270
+ const rejected = attachments.filter(
271
+ (attachment) =>
272
+ attachment.type !== 'stored' &&
273
+ attachment.type !== 'document' &&
274
+ sameImage(attachment, identity),
275
+ )
276
+ if (rejected.length === 0) return message
277
+ const kept = attachments.filter(
278
+ (attachment) =>
279
+ attachment.type === 'stored' ||
280
+ attachment.type === 'document' ||
281
+ !sameImage(attachment, identity),
282
+ )
283
+ const { attachments: _attachments, ...withoutAttachments } = message
284
+ return {
285
+ ...withoutAttachments,
286
+ content: appendTextMarkers(
287
+ message.content,
288
+ rejected.map(() => rejectedUserImageMarker()),
289
+ ),
290
+ ...(kept.length > 0 ? { attachments: kept } : {}),
291
+ }
292
+ }
293
+ if (message.role !== 'tool' || !Array.isArray(message.content)) return message
294
+ let changed = false
295
+ const content: ToolResultBlock[] = message.content.map((block) => {
296
+ if (block.type !== 'image' || !sameImage(block, identity)) return block
297
+ changed = true
298
+ return { type: 'text', text: rejectedToolImageMarker() }
299
+ })
300
+ return changed ? { ...message, content } : message
301
+ })
302
+ }
303
+
304
+ /** Persist a successful recovery while retaining exact image bytes. */
305
+ export function markProviderRejectedImage(
306
+ messages: readonly Message[],
307
+ identity: RequestImageIdentity,
308
+ ): { readonly messages: Message[]; readonly count: number } {
309
+ let count = 0
310
+ const marked = messages.map((message) => {
311
+ if (message.role === 'user') {
312
+ let changed = false
313
+ const attachments = message.attachments?.map((attachment) => {
314
+ if (
315
+ attachment.type === 'stored' ||
316
+ attachment.type === 'document' ||
317
+ !sameImage(attachment, identity) ||
318
+ attachment.modelOmission?.reason === 'provider-rejected'
319
+ ) {
320
+ return attachment
321
+ }
322
+ changed = true
323
+ count += 1
324
+ return {
325
+ ...attachment,
326
+ modelOmission: { reason: 'provider-rejected' as const },
327
+ }
328
+ })
329
+ return changed ? { ...message, attachments } : message
330
+ }
331
+ if (message.role !== 'tool' || !Array.isArray(message.content)) return message
332
+ let changed = false
333
+ const content: ToolResultBlock[] = message.content.map((block) => {
334
+ if (
335
+ block.type !== 'image' ||
336
+ !sameImage(block, identity) ||
337
+ block.modelOmission?.reason === 'provider-rejected'
338
+ ) {
339
+ return block
340
+ }
341
+ changed = true
342
+ count += 1
343
+ return {
344
+ ...block,
345
+ modelOmission: { reason: 'provider-rejected' as const },
346
+ }
347
+ })
348
+ return changed ? { ...message, content } : message
349
+ })
350
+ return { messages: marked, count }
351
+ }
@@ -183,6 +183,7 @@ export class ResultAssembler {
183
183
  ? {
184
184
  kind: err.kind,
185
185
  providerId: err.providerId,
186
+ ...(err.providerCode !== undefined ? { providerCode: err.providerCode } : {}),
186
187
  ...(err.status !== undefined ? { status: err.status } : {}),
187
188
  ...(err.retryAfterMs !== undefined ? { retryAfterMs: err.retryAfterMs } : {}),
188
189
  // The provider's own sentence, already truncated and scrubbed
@@ -1,8 +1,11 @@
1
+ import { isDeepStrictEqual } from 'node:util'
2
+
1
3
  import type { RunPersistence } from '../../manager/run/persistence.js'
2
4
  import type {
3
5
  CheckpointId,
4
6
  HITLResumeDecision,
5
7
  IterationCheckpoint,
8
+ ToolCallSummary,
6
9
  } from '../../types/hitl/index.js'
7
10
  import type { AssistantMessage, Message, ToolCall } from '../../types/message/index.js'
8
11
  import type { ChatCompletionResponse } from '../../types/provider/index.js'
@@ -38,6 +41,10 @@ export interface PendingResumePlan {
38
41
  readonly response: ChatCompletionResponse
39
42
  /** Per-call refusals derived from the decision. */
40
43
  readonly denials: ToolCallDenials
44
+ /** Exact projections and gate decisions persisted with a tool review. */
45
+ readonly reviewedCalls?: readonly ToolCallSummary[]
46
+ /** Calls whose raw input the human replaced in the durable decision. */
47
+ readonly modifiedCallIds?: ReadonlySet<string>
41
48
  /**
42
49
  * Answers to deliver to tools that parked on a question, keyed by the
43
50
  * asking call's id. Present only on a question resume.
@@ -117,6 +124,8 @@ export function planPendingResume(
117
124
  assistant,
118
125
  response: synthesizeResponse(assistant),
119
126
  denials,
127
+ reviewedCalls: pending.request.toolCalls,
128
+ modifiedCallIds: modifiedCallIds(decision),
120
129
  }
121
130
  }
122
131
 
@@ -246,13 +255,86 @@ export async function applyPendingResume(
246
255
  executor: ToolExecutor,
247
256
  prior?: PriorToolResults,
248
257
  ): Promise<void> {
258
+ const denials = new Map(plan.denials)
259
+ const reviewedById = new Map(plan.reviewedCalls?.map((call) => [call.id, call]))
260
+
261
+ // A gate denial belongs to the run, not to the process that first evaluated
262
+ // it. Restore it before preparation so denied calls do not even reach a
263
+ // pre-tool hook after restart.
264
+ for (const call of plan.reviewedCalls ?? []) {
265
+ if (call.authorization?.decision !== 'deny' || denials.has(call.id)) continue
266
+ denials.set(
267
+ call.id,
268
+ `Blocked by the authorization gate: ${call.authorization.reason ?? 'the persisted operator policy denied this call'}`,
269
+ )
270
+ }
271
+
272
+ const callsToPrepare = (plan.response.message.toolCalls ?? []).filter(
273
+ (call) => !prior?.has(call.id) && !denials.has(call.id),
274
+ )
275
+ const responseToPrepare: ChatCompletionResponse = {
276
+ ...plan.response,
277
+ message: { ...plan.response.message, toolCalls: callsToPrepare },
278
+ }
279
+ const preparedBatch = await executor.prepareBatchForReview(responseToPrepare)
280
+
281
+ for (const call of preparedBatch.reviewCalls) {
282
+ const reviewed = reviewedById.get(call.id)
283
+ const wasModified = plan.modifiedCallIds?.has(call.id) === true
284
+ if (reviewed && !wasModified) {
285
+ const unchanged = reviewed.name === call.name && isDeepStrictEqual(reviewed.input, call.input)
286
+ if (!unchanged) {
287
+ const reason =
288
+ 'The tool input changed after its durable review; the earlier approval cannot be reused.'
289
+ denials.set(call.id, reason)
290
+ await runMgr.recordAudit({
291
+ what: { action: 'tool_call', tool: call.name },
292
+ outcome: 'refused',
293
+ reason,
294
+ })
295
+ continue
296
+ }
297
+ }
298
+
299
+ const current = executor.evaluatePreparedAuthorization(call.name, call.input)
300
+ let reason: string | undefined
301
+ if (current?.decision === 'deny') {
302
+ reason = `Blocked by the authorization gate after resume: ${current.reason}`
303
+ } else if (current?.decision === 'review' && (wasModified || !reviewed)) {
304
+ reason = wasModified
305
+ ? `Blocked by the authorization gate after resume: the modified prepared value requires a new explicit approval. ${current.reason}`
306
+ : `Blocked by the authorization gate after resume: this recovered call requires operator review. ${current.reason}`
307
+ } else if (!current && reviewed && reviewed.authorization === undefined) {
308
+ reason =
309
+ 'The durable review predates bound authorization metadata; review this call again before execution.'
310
+ }
311
+
312
+ if (reason) {
313
+ denials.set(call.id, reason)
314
+ await runMgr.recordAudit({
315
+ what: { action: 'tool_call', tool: call.name },
316
+ outcome: 'refused',
317
+ reason,
318
+ })
319
+ }
320
+ }
321
+
249
322
  runMgr.pushMessage(plan.assistant)
250
- const batch = await executor.executeBatch(plan.response, plan.denials, prior)
323
+ const batch = await executor.executeBatch(plan.response, denials, prior, preparedBatch)
251
324
  for (const msg of batch.messages) {
252
325
  runMgr.pushMessage(msg)
253
326
  }
254
327
  }
255
328
 
329
+ function modifiedCallIds(decision: HITLResumeDecision): ReadonlySet<string> {
330
+ if (decision.action !== 'modify_tools') return new Set()
331
+ return new Set(
332
+ decision.modifications
333
+ .filter((modification) => modification.action === 'modify')
334
+ .map((modification) => modification.toolCallId),
335
+ )
336
+ }
337
+
256
338
  /**
257
339
  * Results the run already produced for calls in `toolCalls`.
258
340
  *
@@ -37,6 +37,57 @@ export const SPILL_MARKER = 'The full output was written to:'
37
37
  */
38
38
  const HEAD_SHARE = 0.75
39
39
 
40
+ function safeHead(text: string, maxChars: number): string {
41
+ let end = Math.min(text.length, Math.max(0, maxChars))
42
+ if (end > 0) {
43
+ const last = text.charCodeAt(end - 1)
44
+ if (last >= 0xd800 && last <= 0xdbff) end--
45
+ }
46
+ return text.slice(0, end)
47
+ }
48
+
49
+ function safeTail(text: string, maxChars: number): string {
50
+ let start = Math.max(0, text.length - Math.max(0, maxChars))
51
+ if (start < text.length) {
52
+ const first = text.charCodeAt(start)
53
+ if (first >= 0xdc00 && first <= 0xdfff) start++
54
+ }
55
+ return text.slice(start)
56
+ }
57
+
58
+ /**
59
+ * Fit a recoverable head+tail preview inside the actual model-visible cap.
60
+ *
61
+ * The old implementation allocated the entire cap to source text and then
62
+ * appended its diagnostic and recovery instructions. A configured 1,000
63
+ * character cap therefore emitted 1,200–1,300 characters; at very small
64
+ * limits the explanation could be several times larger than the limit.
65
+ */
66
+ function boundedPreview(
67
+ output: string,
68
+ maxChars: number,
69
+ toolName: string,
70
+ recovery: string,
71
+ ): string {
72
+ const detailedMiddle = [
73
+ '',
74
+ `[... characters omitted — "${toolName}" returned ${output.length.toLocaleString()} characters, over the ${maxChars.toLocaleString()}-character budget ...]`,
75
+ recovery,
76
+ '',
77
+ ].join('\n')
78
+ const compactMiddle = '\n[... omitted ...]\n'
79
+ const middle = detailedMiddle.length < maxChars ? detailedMiddle : compactMiddle
80
+
81
+ // A host may deliberately set a tiny positive cap. At that point no
82
+ // truthful recovery sentence fits; the hard bound still wins.
83
+ if (middle.length >= maxChars) return safeHead(middle, maxChars)
84
+
85
+ const sourceChars = maxChars - middle.length
86
+ const headChars = Math.max(1, Math.floor(sourceChars * HEAD_SHARE))
87
+ const tailChars = Math.max(0, sourceChars - headChars)
88
+ return `${safeHead(output, headChars)}${middle}${safeTail(output, tailChars)}`
89
+ }
90
+
40
91
  export interface ToolOutputBudgetResult {
41
92
  /** What the model sees. */
42
93
  readonly output: string
@@ -127,15 +178,6 @@ export function applyToolOutputBudget(opts: ApplyToolOutputBudgetOptions): ToolO
127
178
  return { output, originalLength, truncated: false }
128
179
  }
129
180
 
130
- // Both slices are derived from `maxChars` so the preview honours the
131
- // budget it is enforcing. `slice(-0)` returns the WHOLE string, so the
132
- // zero-tail case must be branched, not computed.
133
- const headChars = Math.max(1, Math.floor(maxChars * HEAD_SHARE))
134
- const tailChars = Math.max(0, maxChars - headChars)
135
- const head = output.slice(0, headChars)
136
- const tail = tailChars > 0 ? output.slice(-tailChars) : ''
137
- const omitted = originalLength - head.length - tail.length
138
-
139
181
  const spillPath = opts.spillDir
140
182
  ? spill(opts.spillDir, opts.toolUseId, output, opts.onError)
141
183
  : undefined
@@ -148,14 +190,7 @@ export function applyToolOutputBudget(opts: ApplyToolOutputBudgetOptions): ToolO
148
190
  : 'The full output was not retained. Re-run with a narrower query, a line range, or a filter.'
149
191
 
150
192
  return {
151
- output: [
152
- head,
153
- '',
154
- `[... ${omitted.toLocaleString()} characters omitted — "${opts.toolName}" returned ${originalLength.toLocaleString()} characters, over the ${maxChars.toLocaleString()}-character budget ...]`,
155
- recovery,
156
- '',
157
- tail,
158
- ].join('\n'),
193
+ output: boundedPreview(output, maxChars, opts.toolName, recovery),
159
194
  originalLength,
160
195
  truncated: true,
161
196
  ...(spillPath ? { spillPath } : {}),
@@ -1,8 +1,10 @@
1
+ import type { AuthorizationGate } from '../../authorization/gate.js'
1
2
  import type { PluginLifecycleManager } from '../../plugin/lifecycle.js'
2
3
  import type { ActivityStore } from '../../store/activity/memory.js'
3
4
  import type { RunId } from '../../types/ids/index.js'
4
5
  import type { InvocationState } from '../../types/invocation/index.js'
5
6
  import type { PermissionMode } from '../../types/permission/index.js'
7
+ import type { AuditEventInput } from '../../types/run/audit.js'
6
8
  import type { RunEvent } from '../../types/run/index.js'
7
9
  import type {
8
10
  RequestToolPause,
@@ -41,6 +43,10 @@ export interface ToolingBootstrapConfig {
41
43
  maxToolContentBytes?: number
42
44
  toolOutputDir?: string
43
45
  repairToolCall?: RepairToolCall
46
+ /** Operator authorization shared with the direct-call review path. */
47
+ authorizationGate?: AuthorizationGate
48
+ /** Durable refusal recorder for nested authorization decisions. */
49
+ recordAudit?: (input: AuditEventInput) => Promise<unknown>
44
50
  /** Builds the durable-pause seam for one tool call; see ToolContext.requestPause. */
45
51
  toolPause?: (toolUseId: string) => RequestToolPause
46
52
  }
@@ -81,6 +87,10 @@ export class ToolingBootstrap {
81
87
  : {}),
82
88
  ...(config.toolOutputDir !== undefined ? { toolOutputDir: config.toolOutputDir } : {}),
83
89
  ...(config.repairToolCall !== undefined ? { repairToolCall: config.repairToolCall } : {}),
90
+ ...(config.authorizationGate !== undefined
91
+ ? { authorizationGate: config.authorizationGate }
92
+ : {}),
93
+ ...(config.recordAudit !== undefined ? { recordAudit: config.recordAudit } : {}),
84
94
  ...(config.toolPause !== undefined ? { toolPause: config.toolPause } : {}),
85
95
  },
86
96
  activityStore,