@stigmer/react 3.0.9-dev.20260616060535 → 3.1.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 (426) hide show
  1. package/execution/ApprovalCard.d.ts +47 -3
  2. package/execution/ApprovalCard.d.ts.map +1 -1
  3. package/execution/ApprovalCard.js +138 -70
  4. package/execution/ApprovalCard.js.map +1 -1
  5. package/execution/ApprovalContext.d.ts +72 -0
  6. package/execution/ApprovalContext.d.ts.map +1 -0
  7. package/execution/ApprovalContext.js +41 -0
  8. package/execution/ApprovalContext.js.map +1 -0
  9. package/execution/ArtifactCard.d.ts.map +1 -1
  10. package/execution/ArtifactCard.js +3 -1
  11. package/execution/ArtifactCard.js.map +1 -1
  12. package/execution/ArtifactPreviewModal.d.ts.map +1 -1
  13. package/execution/ArtifactPreviewModal.js +5 -3
  14. package/execution/ArtifactPreviewModal.js.map +1 -1
  15. package/execution/EmptyChangeNotice.d.ts +38 -0
  16. package/execution/EmptyChangeNotice.d.ts.map +1 -0
  17. package/execution/EmptyChangeNotice.js +27 -0
  18. package/execution/EmptyChangeNotice.js.map +1 -0
  19. package/execution/FileChangesView.d.ts +81 -0
  20. package/execution/FileChangesView.d.ts.map +1 -0
  21. package/execution/FileChangesView.js +164 -0
  22. package/execution/FileChangesView.js.map +1 -0
  23. package/execution/FilePathLink.d.ts +24 -4
  24. package/execution/FilePathLink.d.ts.map +1 -1
  25. package/execution/FilePathLink.js +31 -9
  26. package/execution/FilePathLink.js.map +1 -1
  27. package/execution/FileReviewCard.d.ts +111 -0
  28. package/execution/FileReviewCard.d.ts.map +1 -0
  29. package/execution/FileReviewCard.js +558 -0
  30. package/execution/FileReviewCard.js.map +1 -0
  31. package/execution/FileReviewContext.d.ts +35 -0
  32. package/execution/FileReviewContext.d.ts.map +1 -0
  33. package/execution/FileReviewContext.js +23 -0
  34. package/execution/FileReviewContext.js.map +1 -0
  35. package/execution/FileReviewDock.d.ts +73 -0
  36. package/execution/FileReviewDock.d.ts.map +1 -0
  37. package/execution/FileReviewDock.js +65 -0
  38. package/execution/FileReviewDock.js.map +1 -0
  39. package/execution/McpToolDetail.d.ts.map +1 -1
  40. package/execution/McpToolDetail.js +38 -5
  41. package/execution/McpToolDetail.js.map +1 -1
  42. package/execution/MessageThread.d.ts +42 -3
  43. package/execution/MessageThread.d.ts.map +1 -1
  44. package/execution/MessageThread.js +263 -18
  45. package/execution/MessageThread.js.map +1 -1
  46. package/execution/PlanArtifactCard.d.ts.map +1 -1
  47. package/execution/PlanArtifactCard.js +3 -1
  48. package/execution/PlanArtifactCard.js.map +1 -1
  49. package/execution/PlanCompletionCard.d.ts +1 -1
  50. package/execution/PlanCompletionCard.js +1 -1
  51. package/execution/ResultView.d.ts +19 -1
  52. package/execution/ResultView.d.ts.map +1 -1
  53. package/execution/ResultView.js +141 -40
  54. package/execution/ResultView.js.map +1 -1
  55. package/execution/SubAgentSection.d.ts.map +1 -1
  56. package/execution/SubAgentSection.js +45 -6
  57. package/execution/SubAgentSection.js.map +1 -1
  58. package/execution/TerminalSession.d.ts +34 -0
  59. package/execution/TerminalSession.d.ts.map +1 -0
  60. package/execution/TerminalSession.js +34 -0
  61. package/execution/TerminalSession.js.map +1 -0
  62. package/execution/TodoCard.d.ts +53 -0
  63. package/execution/TodoCard.d.ts.map +1 -0
  64. package/execution/TodoCard.js +77 -0
  65. package/execution/TodoCard.js.map +1 -0
  66. package/execution/ToolArgsView.d.ts +7 -1
  67. package/execution/ToolArgsView.d.ts.map +1 -1
  68. package/execution/ToolArgsView.js +10 -9
  69. package/execution/ToolArgsView.js.map +1 -1
  70. package/execution/ToolCallDetail.d.ts +10 -1
  71. package/execution/ToolCallDetail.d.ts.map +1 -1
  72. package/execution/ToolCallDetail.js +66 -19
  73. package/execution/ToolCallDetail.js.map +1 -1
  74. package/execution/ToolCallGroup.d.ts +28 -18
  75. package/execution/ToolCallGroup.d.ts.map +1 -1
  76. package/execution/ToolCallGroup.js +39 -148
  77. package/execution/ToolCallGroup.js.map +1 -1
  78. package/execution/ToolCallItem.d.ts +36 -8
  79. package/execution/ToolCallItem.d.ts.map +1 -1
  80. package/execution/ToolCallItem.js +140 -21
  81. package/execution/ToolCallItem.js.map +1 -1
  82. package/execution/ToolRunGroup.d.ts +48 -0
  83. package/execution/ToolRunGroup.d.ts.map +1 -0
  84. package/execution/ToolRunGroup.js +168 -0
  85. package/execution/ToolRunGroup.js.map +1 -0
  86. package/execution/approval-provenance.d.ts +2 -0
  87. package/execution/approval-provenance.d.ts.map +1 -0
  88. package/execution/approval-provenance.js +6 -0
  89. package/execution/approval-provenance.js.map +1 -0
  90. package/execution/artifact-utils.d.ts +0 -10
  91. package/execution/artifact-utils.d.ts.map +1 -1
  92. package/execution/artifact-utils.js +0 -17
  93. package/execution/artifact-utils.js.map +1 -1
  94. package/execution/execution-phases.d.ts +1 -6
  95. package/execution/execution-phases.d.ts.map +1 -1
  96. package/execution/execution-phases.js +4 -14
  97. package/execution/execution-phases.js.map +1 -1
  98. package/execution/file-path-resolver.d.ts +28 -0
  99. package/execution/file-path-resolver.d.ts.map +1 -1
  100. package/execution/file-path-resolver.js +24 -0
  101. package/execution/file-path-resolver.js.map +1 -1
  102. package/execution/file-review-status.d.ts +127 -0
  103. package/execution/file-review-status.d.ts.map +1 -0
  104. package/execution/file-review-status.js +135 -0
  105. package/execution/file-review-status.js.map +1 -0
  106. package/execution/index.d.ts +39 -5
  107. package/execution/index.d.ts.map +1 -1
  108. package/execution/index.js +23 -3
  109. package/execution/index.js.map +1 -1
  110. package/execution/segment-tool-calls.d.ts +46 -0
  111. package/execution/segment-tool-calls.d.ts.map +1 -0
  112. package/execution/segment-tool-calls.js +71 -0
  113. package/execution/segment-tool-calls.js.map +1 -0
  114. package/execution/tool-categories.d.ts +62 -1
  115. package/execution/tool-categories.d.ts.map +1 -1
  116. package/execution/tool-categories.js +96 -2
  117. package/execution/tool-categories.js.map +1 -1
  118. package/execution/tool-presenter.d.ts +38 -2
  119. package/execution/tool-presenter.d.ts.map +1 -1
  120. package/execution/tool-presenter.js +23 -1
  121. package/execution/tool-presenter.js.map +1 -1
  122. package/execution/tool-rendering-primitives.d.ts.map +1 -1
  123. package/execution/tool-rendering-primitives.js +3 -2
  124. package/execution/tool-rendering-primitives.js.map +1 -1
  125. package/execution/useArtifactDownload.d.ts +44 -0
  126. package/execution/useArtifactDownload.d.ts.map +1 -0
  127. package/execution/useArtifactDownload.js +80 -0
  128. package/execution/useArtifactDownload.js.map +1 -0
  129. package/execution/useArtifactDownloadUrl.d.ts +67 -0
  130. package/execution/useArtifactDownloadUrl.d.ts.map +1 -0
  131. package/execution/useArtifactDownloadUrl.js +59 -0
  132. package/execution/useArtifactDownloadUrl.js.map +1 -0
  133. package/execution/useFileChangeContent.d.ts +61 -0
  134. package/execution/useFileChangeContent.d.ts.map +1 -0
  135. package/execution/useFileChangeContent.js +89 -0
  136. package/execution/useFileChangeContent.js.map +1 -0
  137. package/execution/useFileReview.d.ts +89 -0
  138. package/execution/useFileReview.d.ts.map +1 -0
  139. package/execution/useFileReview.js +106 -0
  140. package/execution/useFileReview.js.map +1 -0
  141. package/execution/useSubmitApproval.d.ts +17 -2
  142. package/execution/useSubmitApproval.d.ts.map +1 -1
  143. package/execution/useSubmitApproval.js +27 -23
  144. package/execution/useSubmitApproval.js.map +1 -1
  145. package/execution/useToolOutputContent.d.ts +44 -0
  146. package/execution/useToolOutputContent.d.ts.map +1 -0
  147. package/execution/useToolOutputContent.js +31 -0
  148. package/execution/useToolOutputContent.js.map +1 -0
  149. package/index.d.ts +6 -6
  150. package/index.d.ts.map +1 -1
  151. package/index.js +3 -3
  152. package/index.js.map +1 -1
  153. package/internal/ApprovalPeekBar.d.ts +23 -0
  154. package/internal/ApprovalPeekBar.d.ts.map +1 -0
  155. package/internal/ApprovalPeekBar.js +25 -0
  156. package/internal/ApprovalPeekBar.js.map +1 -0
  157. package/internal/BoundedContent.d.ts +49 -0
  158. package/internal/BoundedContent.d.ts.map +1 -0
  159. package/internal/BoundedContent.js +50 -0
  160. package/internal/BoundedContent.js.map +1 -0
  161. package/internal/DecisionButton.d.ts +47 -0
  162. package/internal/DecisionButton.d.ts.map +1 -0
  163. package/internal/DecisionButton.js +42 -0
  164. package/internal/DecisionButton.js.map +1 -0
  165. package/internal/InCardDecisionError.d.ts +31 -0
  166. package/internal/InCardDecisionError.d.ts.map +1 -0
  167. package/internal/InCardDecisionError.js +23 -0
  168. package/internal/InCardDecisionError.js.map +1 -0
  169. package/internal/RevealToggle.d.ts +36 -0
  170. package/internal/RevealToggle.d.ts.map +1 -0
  171. package/internal/RevealToggle.js +32 -0
  172. package/internal/RevealToggle.js.map +1 -0
  173. package/internal/VirtualizedThread.d.ts +7 -1
  174. package/internal/VirtualizedThread.d.ts.map +1 -1
  175. package/internal/VirtualizedThread.js +7 -3
  176. package/internal/VirtualizedThread.js.map +1 -1
  177. package/internal/useAutoDisclosure.d.ts +34 -0
  178. package/internal/useAutoDisclosure.d.ts.map +1 -0
  179. package/internal/useAutoDisclosure.js +53 -0
  180. package/internal/useAutoDisclosure.js.map +1 -0
  181. package/internal/useElapsedSince.d.ts +30 -0
  182. package/internal/useElapsedSince.d.ts.map +1 -0
  183. package/internal/useElapsedSince.js +70 -0
  184. package/internal/useElapsedSince.js.map +1 -0
  185. package/internal/useIsOverflowing.d.ts +30 -0
  186. package/internal/useIsOverflowing.d.ts.map +1 -0
  187. package/internal/useIsOverflowing.js +47 -0
  188. package/internal/useIsOverflowing.js.map +1 -0
  189. package/internal/useIsTextTruncated.d.ts +24 -0
  190. package/internal/useIsTextTruncated.d.ts.map +1 -0
  191. package/internal/useIsTextTruncated.js +41 -0
  192. package/internal/useIsTextTruncated.js.map +1 -0
  193. package/internal/useKeyedSubmission.d.ts +49 -0
  194. package/internal/useKeyedSubmission.d.ts.map +1 -0
  195. package/internal/useKeyedSubmission.js +67 -0
  196. package/internal/useKeyedSubmission.js.map +1 -0
  197. package/package.json +4 -4
  198. package/session/SessionViewer.d.ts +2 -2
  199. package/session/SessionViewer.d.ts.map +1 -1
  200. package/session/SessionViewer.js +10 -4
  201. package/session/SessionViewer.js.map +1 -1
  202. package/session/index.d.ts +2 -0
  203. package/session/index.d.ts.map +1 -1
  204. package/session/index.js +1 -0
  205. package/session/index.js.map +1 -1
  206. package/session/inspector/ChangesTab.d.ts +8 -5
  207. package/session/inspector/ChangesTab.d.ts.map +1 -1
  208. package/session/inspector/ChangesTab.js +11 -8
  209. package/session/inspector/ChangesTab.js.map +1 -1
  210. package/session/inspector/SessionInspector.d.ts +4 -4
  211. package/session/inspector/SessionInspector.d.ts.map +1 -1
  212. package/session/inspector/SessionInspector.js +4 -4
  213. package/session/inspector/SessionInspector.js.map +1 -1
  214. package/session/inspector/index.d.ts +0 -2
  215. package/session/inspector/index.d.ts.map +1 -1
  216. package/session/inspector/index.js +0 -1
  217. package/session/inspector/index.js.map +1 -1
  218. package/session/inspector/useSessionInspector.d.ts +8 -4
  219. package/session/inspector/useSessionInspector.d.ts.map +1 -1
  220. package/session/inspector/useSessionInspector.js +27 -20
  221. package/session/inspector/useSessionInspector.js.map +1 -1
  222. package/session/useSessionConversation.d.ts +37 -3
  223. package/session/useSessionConversation.d.ts.map +1 -1
  224. package/session/useSessionConversation.js +18 -2
  225. package/session/useSessionConversation.js.map +1 -1
  226. package/session/useSessionFileChanges.d.ts +74 -0
  227. package/session/useSessionFileChanges.d.ts.map +1 -0
  228. package/session/useSessionFileChanges.js +178 -0
  229. package/session/useSessionFileChanges.js.map +1 -0
  230. package/src/__tests__/styles-border-layer-invariant.test.ts +105 -0
  231. package/src/execution/ApprovalCard.tsx +368 -230
  232. package/src/execution/ApprovalContext.ts +109 -0
  233. package/src/execution/ArtifactCard.tsx +10 -6
  234. package/src/execution/ArtifactPreviewModal.tsx +12 -6
  235. package/src/execution/EmptyChangeNotice.tsx +62 -0
  236. package/src/execution/FileChangesView.tsx +383 -0
  237. package/src/execution/FilePathLink.tsx +65 -12
  238. package/src/execution/FileReviewCard.tsx +1118 -0
  239. package/src/execution/FileReviewContext.ts +53 -0
  240. package/src/execution/FileReviewDock.tsx +157 -0
  241. package/src/execution/McpToolDetail.tsx +98 -32
  242. package/src/execution/MessageThread.tsx +374 -9
  243. package/src/execution/PlanArtifactCard.tsx +9 -6
  244. package/src/execution/PlanCompletionCard.tsx +1 -1
  245. package/src/execution/ResultView.tsx +333 -162
  246. package/src/execution/SubAgentSection.tsx +58 -11
  247. package/src/execution/TerminalSession.tsx +91 -0
  248. package/src/execution/TodoCard.tsx +186 -0
  249. package/src/execution/ToolArgsView.tsx +28 -17
  250. package/src/execution/ToolCallDetail.tsx +137 -26
  251. package/src/execution/ToolCallGroup.tsx +68 -296
  252. package/src/execution/ToolCallItem.tsx +265 -67
  253. package/src/execution/ToolRunGroup.tsx +302 -0
  254. package/src/execution/__tests__/ApprovalCard.test.tsx +395 -5
  255. package/src/execution/__tests__/ArtifactPreviewModal.test.tsx +0 -1
  256. package/src/execution/__tests__/EmptyChangeNotice.test.tsx +24 -0
  257. package/src/execution/__tests__/FileChangesView.test.tsx +296 -0
  258. package/src/execution/__tests__/FilePathLink.test.tsx +92 -0
  259. package/src/execution/__tests__/FileReviewCard.test.tsx +1044 -0
  260. package/src/execution/__tests__/FileReviewDock.test.tsx +134 -0
  261. package/src/execution/__tests__/McpToolDetail.test.tsx +79 -0
  262. package/src/execution/__tests__/MessageThread.test.tsx +324 -0
  263. package/src/execution/__tests__/PlanArtifactCard.test.tsx +37 -16
  264. package/src/execution/__tests__/ResultView.test.tsx +280 -2
  265. package/src/execution/__tests__/SubAgentSection.test.tsx +180 -0
  266. package/src/execution/__tests__/TerminalSession.test.tsx +72 -0
  267. package/src/execution/__tests__/TodoCard.test.tsx +102 -0
  268. package/src/execution/__tests__/ToolArgsView.test.tsx +48 -0
  269. package/src/execution/__tests__/ToolCallDetail.test.tsx +195 -0
  270. package/src/execution/__tests__/ToolCallGroup.test.tsx +132 -0
  271. package/src/execution/__tests__/ToolCallItem.test.tsx +521 -0
  272. package/src/execution/__tests__/ToolRunGroup.test.tsx +137 -0
  273. package/src/execution/__tests__/blanked-narration.test.tsx +149 -0
  274. package/src/execution/__tests__/buildThreadItems-approvals.test.ts +304 -0
  275. package/src/execution/__tests__/buildThreadItems-file-review.test.ts +331 -0
  276. package/src/execution/__tests__/file-path-resolver.test.ts +44 -0
  277. package/src/execution/__tests__/file-review-status.test.ts +343 -0
  278. package/src/execution/__tests__/segment-tool-calls.test.ts +124 -0
  279. package/src/execution/__tests__/subagent-file-review-badge.test.tsx +273 -0
  280. package/src/execution/__tests__/thread-keys.test.ts +195 -0
  281. package/src/execution/__tests__/tool-presenter.test.tsx +95 -0
  282. package/src/execution/__tests__/useArtifactDownload.test.tsx +67 -0
  283. package/src/execution/__tests__/useArtifactDownloadUrl.test.tsx +51 -0
  284. package/src/execution/__tests__/useFileChangeContent.test.tsx +240 -0
  285. package/src/execution/__tests__/useFileReview.test.tsx +236 -0
  286. package/src/execution/__tests__/useSubmitApproval.test.tsx +165 -0
  287. package/src/execution/__tests__/useToolOutputContent.test.tsx +82 -0
  288. package/src/execution/approval-provenance.ts +8 -0
  289. package/src/execution/artifact-utils.ts +0 -18
  290. package/src/execution/execution-phases.ts +4 -16
  291. package/src/execution/file-path-resolver.ts +38 -0
  292. package/src/execution/file-review-status.ts +219 -0
  293. package/src/execution/index.ts +84 -4
  294. package/src/execution/segment-tool-calls.ts +101 -0
  295. package/src/execution/tool-categories.ts +128 -1
  296. package/src/execution/tool-presenter.ts +58 -3
  297. package/src/execution/tool-rendering-primitives.tsx +11 -14
  298. package/src/execution/useArtifactDownload.ts +109 -0
  299. package/src/execution/useArtifactDownloadUrl.ts +116 -0
  300. package/src/execution/useFileChangeContent.ts +149 -0
  301. package/src/execution/useFileReview.ts +197 -0
  302. package/src/execution/useSubmitApproval.ts +49 -27
  303. package/src/execution/useToolOutputContent.ts +70 -0
  304. package/src/index.ts +60 -1
  305. package/src/internal/ApprovalPeekBar.tsx +73 -0
  306. package/src/internal/BoundedContent.tsx +97 -0
  307. package/src/internal/DecisionButton.tsx +127 -0
  308. package/src/internal/InCardDecisionError.tsx +47 -0
  309. package/src/internal/RevealToggle.tsx +91 -0
  310. package/src/internal/VirtualizedThread.tsx +28 -2
  311. package/src/internal/__tests__/ApprovalPeekBar.test.tsx +34 -0
  312. package/src/internal/__tests__/BoundedContent.test.tsx +92 -0
  313. package/src/internal/__tests__/DecisionButton.test.tsx +107 -0
  314. package/src/internal/__tests__/RevealToggle.test.tsx +61 -0
  315. package/src/internal/__tests__/useAutoDisclosure.test.ts +74 -0
  316. package/src/internal/__tests__/useElapsedSince.test.tsx +68 -0
  317. package/src/internal/__tests__/useIsOverflowing.test.ts +36 -0
  318. package/src/internal/__tests__/useKeyedSubmission.test.tsx +126 -0
  319. package/src/internal/useAutoDisclosure.ts +58 -0
  320. package/src/internal/useElapsedSince.ts +72 -0
  321. package/src/internal/useIsOverflowing.ts +61 -0
  322. package/src/internal/useIsTextTruncated.ts +55 -0
  323. package/src/internal/useKeyedSubmission.ts +101 -0
  324. package/src/session/SessionViewer.tsx +21 -4
  325. package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +198 -0
  326. package/src/session/__tests__/audienceWiring.test.tsx +4 -0
  327. package/src/session/__tests__/useSessionFileChanges.test.ts +303 -0
  328. package/src/session/index.ts +3 -0
  329. package/src/session/inspector/ChangesTab.tsx +20 -17
  330. package/src/session/inspector/SessionInspector.tsx +12 -10
  331. package/src/session/inspector/__tests__/ArtifactsTab.test.tsx +0 -1
  332. package/src/session/inspector/__tests__/ChangesTab.test.tsx +38 -0
  333. package/src/session/inspector/__tests__/useSessionInspector.test.ts +46 -61
  334. package/src/session/inspector/index.ts +0 -3
  335. package/src/session/inspector/useSessionInspector.ts +27 -20
  336. package/src/session/useSessionConversation.ts +80 -3
  337. package/src/session/useSessionFileChanges.ts +207 -0
  338. package/src/styles.css +35 -10
  339. package/src/version-history/DiffViewer.tsx +10 -7
  340. package/src/version-history/UnifiedDiffView.tsx +66 -0
  341. package/src/version-history/__tests__/DiffViewer.test.tsx +57 -0
  342. package/src/version-history/__tests__/UnifiedDiffView.test.tsx +50 -0
  343. package/src/version-history/__tests__/computeDiff.test.ts +35 -0
  344. package/src/version-history/__tests__/parseUnifiedDiff.test.ts +82 -0
  345. package/src/version-history/computeDiff.ts +6 -37
  346. package/src/version-history/diff-hunks.ts +63 -0
  347. package/src/version-history/index.ts +1 -0
  348. package/src/version-history/parseUnifiedDiff.ts +34 -0
  349. package/src/workflow/WorkflowExecutionApprovalCard.tsx +49 -24
  350. package/src/workflow/WorkflowExecutionTimeline.tsx +15 -4
  351. package/src/workflow/WorkflowExecutionTimelineEvent.tsx +8 -3
  352. package/src/workflow/WorkflowExecutionViewer.tsx +72 -28
  353. package/src/workflow/WorkflowFileReviewList.tsx +152 -0
  354. package/src/workflow/WorkflowTaskApprovalCard.tsx +37 -85
  355. package/src/workflow/__tests__/ExecutionInspector.test.tsx +30 -2
  356. package/src/workflow/__tests__/WorkflowExecutionApprovalCard.test.tsx +129 -0
  357. package/src/workflow/__tests__/WorkflowFileReviewList.test.tsx +129 -0
  358. package/src/workflow/__tests__/WorkflowTaskApprovalCard.test.tsx +43 -11
  359. package/src/workflow/__tests__/useWorkflowExecutionActions.test.tsx +276 -0
  360. package/src/workflow/execution-inspector/ExecutionInspector.tsx +42 -18
  361. package/src/workflow/index.ts +5 -0
  362. package/src/workflow/useWorkflowExecutionActions.ts +177 -29
  363. package/styles.css +1 -1
  364. package/version-history/DiffViewer.d.ts.map +1 -1
  365. package/version-history/DiffViewer.js +9 -3
  366. package/version-history/DiffViewer.js.map +1 -1
  367. package/version-history/UnifiedDiffView.d.ts +29 -0
  368. package/version-history/UnifiedDiffView.d.ts.map +1 -0
  369. package/version-history/UnifiedDiffView.js +36 -0
  370. package/version-history/UnifiedDiffView.js.map +1 -0
  371. package/version-history/computeDiff.d.ts +3 -1
  372. package/version-history/computeDiff.d.ts.map +1 -1
  373. package/version-history/computeDiff.js +5 -35
  374. package/version-history/computeDiff.js.map +1 -1
  375. package/version-history/diff-hunks.d.ts +21 -0
  376. package/version-history/diff-hunks.d.ts.map +1 -0
  377. package/version-history/diff-hunks.js +58 -0
  378. package/version-history/diff-hunks.js.map +1 -0
  379. package/version-history/index.d.ts +1 -0
  380. package/version-history/index.d.ts.map +1 -1
  381. package/version-history/index.js +1 -0
  382. package/version-history/index.js.map +1 -1
  383. package/version-history/parseUnifiedDiff.d.ts +22 -0
  384. package/version-history/parseUnifiedDiff.d.ts.map +1 -0
  385. package/version-history/parseUnifiedDiff.js +34 -0
  386. package/version-history/parseUnifiedDiff.js.map +1 -0
  387. package/workflow/WorkflowExecutionApprovalCard.d.ts +8 -1
  388. package/workflow/WorkflowExecutionApprovalCard.d.ts.map +1 -1
  389. package/workflow/WorkflowExecutionApprovalCard.js +17 -3
  390. package/workflow/WorkflowExecutionApprovalCard.js.map +1 -1
  391. package/workflow/WorkflowExecutionTimeline.d.ts +11 -2
  392. package/workflow/WorkflowExecutionTimeline.d.ts.map +1 -1
  393. package/workflow/WorkflowExecutionTimeline.js +2 -2
  394. package/workflow/WorkflowExecutionTimeline.js.map +1 -1
  395. package/workflow/WorkflowExecutionTimelineEvent.d.ts +4 -1
  396. package/workflow/WorkflowExecutionTimelineEvent.d.ts.map +1 -1
  397. package/workflow/WorkflowExecutionTimelineEvent.js +2 -2
  398. package/workflow/WorkflowExecutionTimelineEvent.js.map +1 -1
  399. package/workflow/WorkflowExecutionViewer.d.ts.map +1 -1
  400. package/workflow/WorkflowExecutionViewer.js +18 -10
  401. package/workflow/WorkflowExecutionViewer.js.map +1 -1
  402. package/workflow/WorkflowFileReviewList.d.ts +46 -0
  403. package/workflow/WorkflowFileReviewList.d.ts.map +1 -0
  404. package/workflow/WorkflowFileReviewList.js +48 -0
  405. package/workflow/WorkflowFileReviewList.js.map +1 -0
  406. package/workflow/WorkflowTaskApprovalCard.d.ts +8 -1
  407. package/workflow/WorkflowTaskApprovalCard.d.ts.map +1 -1
  408. package/workflow/WorkflowTaskApprovalCard.js +12 -19
  409. package/workflow/WorkflowTaskApprovalCard.js.map +1 -1
  410. package/workflow/execution-inspector/ExecutionInspector.d.ts +21 -4
  411. package/workflow/execution-inspector/ExecutionInspector.d.ts.map +1 -1
  412. package/workflow/execution-inspector/ExecutionInspector.js +6 -3
  413. package/workflow/execution-inspector/ExecutionInspector.js.map +1 -1
  414. package/workflow/index.d.ts +1 -0
  415. package/workflow/index.d.ts.map +1 -1
  416. package/workflow/index.js +1 -0
  417. package/workflow/index.js.map +1 -1
  418. package/workflow/useWorkflowExecutionActions.d.ts +62 -4
  419. package/workflow/useWorkflowExecutionActions.d.ts.map +1 -1
  420. package/workflow/useWorkflowExecutionActions.js +94 -18
  421. package/workflow/useWorkflowExecutionActions.js.map +1 -1
  422. package/session/inspector/PlanTab.d.ts +0 -15
  423. package/session/inspector/PlanTab.d.ts.map +0 -1
  424. package/session/inspector/PlanTab.js +0 -19
  425. package/session/inspector/PlanTab.js.map +0 -1
  426. package/src/session/inspector/PlanTab.tsx +0 -31
@@ -23,6 +23,7 @@ const mockResume = vi.fn();
23
23
  const mockRecover = vi.fn();
24
24
  const mockSubmitApproval = vi.fn();
25
25
  const mockSubmitWorkflowTaskApproval = vi.fn();
26
+ const mockSubmitFileDecision = vi.fn();
26
27
 
27
28
  function makeMockClient(): Stigmer {
28
29
  return {
@@ -34,6 +35,7 @@ function makeMockClient(): Stigmer {
34
35
  recover: mockRecover,
35
36
  submitApproval: mockSubmitApproval,
36
37
  submitWorkflowTaskApproval: mockSubmitWorkflowTaskApproval,
38
+ submitFileDecision: mockSubmitFileDecision,
37
39
  },
38
40
  } as unknown as Stigmer;
39
41
  }
@@ -278,3 +280,277 @@ describe("onSuccess callback", () => {
278
280
  }
279
281
  });
280
282
  });
283
+
284
+ // ---------------------------------------------------------------------------
285
+ // Per-gate approval state (keyed; separate from the lifecycle scalar)
286
+ // ---------------------------------------------------------------------------
287
+
288
+ describe("per-gate approval state", () => {
289
+ it("tracks an in-flight tool approval keyed by toolCallId, not the lifecycle scalar", async () => {
290
+ let resolve!: (v: unknown) => void;
291
+ mockSubmitApproval.mockReturnValueOnce(new Promise((r) => (resolve = r)));
292
+
293
+ const { result } = renderHook(
294
+ () => useWorkflowExecutionActions("wex-001"),
295
+ { wrapper: createWrapper(makeMockClient()) },
296
+ );
297
+
298
+ let pending: Promise<unknown>;
299
+ act(() => {
300
+ pending = result.current.submitApproval("tc-1", 1 as any);
301
+ });
302
+
303
+ expect(result.current.approvalSubmittingToolCallIds.has("tc-1")).toBe(true);
304
+ // The lifecycle scalar is untouched by an approval.
305
+ expect(result.current.isSubmitting).toBe(false);
306
+
307
+ await act(async () => {
308
+ resolve(makeExecution());
309
+ await pending;
310
+ });
311
+
312
+ expect(result.current.approvalSubmittingToolCallIds.size).toBe(0);
313
+ });
314
+
315
+ it("records a failed tool approval in the keyed map, not the lifecycle scalar, and resolves null", async () => {
316
+ mockSubmitApproval.mockRejectedValueOnce(new Error("gate already resolved"));
317
+
318
+ const { result } = renderHook(
319
+ () => useWorkflowExecutionActions("wex-001"),
320
+ { wrapper: createWrapper(makeMockClient()) },
321
+ );
322
+
323
+ let returned: any;
324
+ await act(async () => {
325
+ returned = await result.current.submitApproval("tc-1", 1 as any);
326
+ });
327
+
328
+ expect(returned).toBeNull();
329
+ expect(result.current.approvalErrorsByToolCallId.get("tc-1")?.message).toBe(
330
+ "gate already resolved",
331
+ );
332
+ // The lifecycle scalar (header banner) must NOT pick up an approval failure.
333
+ expect(result.current.error).toBeNull();
334
+ expect(result.current.isSubmitting).toBe(false);
335
+ });
336
+
337
+ it("records a failed task approval keyed by taskName, not the lifecycle scalar", async () => {
338
+ mockSubmitWorkflowTaskApproval.mockRejectedValueOnce(new Error("signal failed"));
339
+
340
+ const { result } = renderHook(
341
+ () => useWorkflowExecutionActions("wex-001"),
342
+ { wrapper: createWrapper(makeMockClient()) },
343
+ );
344
+
345
+ let returned: any;
346
+ await act(async () => {
347
+ returned = await result.current.submitTaskApproval("review_task", "approved");
348
+ });
349
+
350
+ expect(returned).toBeNull();
351
+ expect(result.current.taskApprovalErrorsByTaskName.get("review_task")?.message).toBe(
352
+ "signal failed",
353
+ );
354
+ expect(result.current.error).toBeNull();
355
+ });
356
+
357
+ it("keeps tool and task approval errors in separate keyed spaces", async () => {
358
+ mockSubmitApproval.mockRejectedValueOnce(new Error("tool fail"));
359
+ mockSubmitWorkflowTaskApproval.mockRejectedValueOnce(new Error("task fail"));
360
+
361
+ const { result } = renderHook(
362
+ () => useWorkflowExecutionActions("wex-001"),
363
+ { wrapper: createWrapper(makeMockClient()) },
364
+ );
365
+
366
+ await act(async () => {
367
+ await result.current.submitApproval("tc-1", 1 as any);
368
+ });
369
+ await act(async () => {
370
+ await result.current.submitTaskApproval("review_task", "approved");
371
+ });
372
+
373
+ expect(result.current.approvalErrorsByToolCallId.get("tc-1")?.message).toBe("tool fail");
374
+ expect(result.current.taskApprovalErrorsByTaskName.get("review_task")?.message).toBe("task fail");
375
+ // Neither keyed failure crosses into the other space or the scalar.
376
+ expect(result.current.approvalErrorsByToolCallId.has("review_task")).toBe(false);
377
+ expect(result.current.taskApprovalErrorsByTaskName.has("tc-1")).toBe(false);
378
+ expect(result.current.error).toBeNull();
379
+ });
380
+
381
+ it("keeps a failed lifecycle action in the scalar, never in the keyed approval maps", async () => {
382
+ mockCancel.mockRejectedValueOnce(new Error("temporal down"));
383
+
384
+ const { result } = renderHook(
385
+ () => useWorkflowExecutionActions("wex-001"),
386
+ { wrapper: createWrapper(makeMockClient()) },
387
+ );
388
+
389
+ await act(async () => {
390
+ await result.current.cancel();
391
+ });
392
+
393
+ expect(result.current.error?.message).toBe("temporal down");
394
+ expect(result.current.approvalErrorsByToolCallId.size).toBe(0);
395
+ expect(result.current.taskApprovalErrorsByTaskName.size).toBe(0);
396
+ });
397
+
398
+ it("null executionId returns null without recording keyed state", async () => {
399
+ const { result } = renderHook(
400
+ () => useWorkflowExecutionActions(null),
401
+ { wrapper: createWrapper(makeMockClient()) },
402
+ );
403
+
404
+ let returned: any;
405
+ await act(async () => {
406
+ returned = await result.current.submitApproval("tc-1", 1 as any);
407
+ });
408
+
409
+ expect(returned).toBeNull();
410
+ expect(mockSubmitApproval).not.toHaveBeenCalled();
411
+ expect(result.current.approvalSubmittingToolCallIds.size).toBe(0);
412
+ expect(result.current.approvalErrorsByToolCallId.size).toBe(0);
413
+ });
414
+
415
+ it("keeps a stable return reference across renders when nothing changes", () => {
416
+ const { result, rerender } = renderHook(
417
+ () => useWorkflowExecutionActions("wex-001"),
418
+ { wrapper: createWrapper(makeMockClient()) },
419
+ );
420
+
421
+ const first = result.current;
422
+ rerender();
423
+ expect(result.current).toBe(first);
424
+ });
425
+ });
426
+
427
+ // ---------------------------------------------------------------------------
428
+ // Per-gate file-decision state (forwarding + keyed like FileReviewCard)
429
+ // ---------------------------------------------------------------------------
430
+
431
+ describe("submitFileDecision (workflow-parent file review)", () => {
432
+ it("forwards a CHANGE_SET decision with explicit child routing", async () => {
433
+ mockSubmitFileDecision.mockResolvedValueOnce(makeExecution());
434
+
435
+ const { result } = renderHook(
436
+ () => useWorkflowExecutionActions("wex-001"),
437
+ { wrapper: createWrapper(makeMockClient()) },
438
+ );
439
+
440
+ await act(async () => {
441
+ await result.current.submitFileDecision("aex-child", "fcs-1", 1 /* APPROVE */ as any, {
442
+ expectedDigest: "sha256:agg",
443
+ });
444
+ });
445
+
446
+ expect(mockSubmitFileDecision).toHaveBeenCalledTimes(1);
447
+ const input = mockSubmitFileDecision.mock.calls[0][0];
448
+ expect(input.executionId).toBe("wex-001");
449
+ expect(input.childAgentExecutionId).toBe("aex-child");
450
+ expect(input.changeSetId).toBe("fcs-1");
451
+ expect(input.scope).toBe(1 /* CHANGE_SET */);
452
+ expect(input.fileChangeId).toBe("");
453
+ expect(input.action).toBe(1 /* APPROVE */);
454
+ expect(input.expectedDigest).toBe("sha256:agg");
455
+ });
456
+
457
+ it("derives FILE scope when a fileChangeId is supplied", async () => {
458
+ mockSubmitFileDecision.mockResolvedValueOnce(makeExecution());
459
+
460
+ const { result } = renderHook(
461
+ () => useWorkflowExecutionActions("wex-001"),
462
+ { wrapper: createWrapper(makeMockClient()) },
463
+ );
464
+
465
+ await act(async () => {
466
+ await result.current.submitFileDecision("aex-child", "fcs-1", 2 /* REJECT */ as any, {
467
+ fileChangeId: "fc-9",
468
+ expectedDigest: "sha256:file",
469
+ });
470
+ });
471
+
472
+ const input = mockSubmitFileDecision.mock.calls[0][0];
473
+ expect(input.scope).toBe(2 /* FILE */);
474
+ expect(input.fileChangeId).toBe("fc-9");
475
+ expect(input.action).toBe(2 /* REJECT */);
476
+ });
477
+
478
+ it("tracks in-flight state keyed like FileReviewCard (changeSetId, then changeSetId:fileChangeId)", async () => {
479
+ let resolve!: (v: unknown) => void;
480
+ mockSubmitFileDecision.mockReturnValueOnce(new Promise((r) => (resolve = r)));
481
+
482
+ const { result } = renderHook(
483
+ () => useWorkflowExecutionActions("wex-001"),
484
+ { wrapper: createWrapper(makeMockClient()) },
485
+ );
486
+
487
+ let pending: Promise<unknown>;
488
+ act(() => {
489
+ pending = result.current.submitFileDecision("aex-child", "fcs-1", 1 as any);
490
+ });
491
+
492
+ expect(result.current.fileDecisionSubmittingKeys.has("fcs-1")).toBe(true);
493
+ expect(result.current.isSubmitting).toBe(false); // lifecycle scalar untouched
494
+
495
+ await act(async () => {
496
+ resolve(makeExecution());
497
+ await pending;
498
+ });
499
+
500
+ expect(result.current.fileDecisionSubmittingKeys.size).toBe(0);
501
+ });
502
+
503
+ it("records a failed decision in the keyed map (not the scalar) and resolves null", async () => {
504
+ mockSubmitFileDecision.mockRejectedValueOnce(new Error("expected_digest mismatch"));
505
+
506
+ const { result } = renderHook(
507
+ () => useWorkflowExecutionActions("wex-001"),
508
+ { wrapper: createWrapper(makeMockClient()) },
509
+ );
510
+
511
+ let returned: any;
512
+ await act(async () => {
513
+ returned = await result.current.submitFileDecision("aex-child", "fcs-1", 1 as any, {
514
+ fileChangeId: "fc-9",
515
+ });
516
+ });
517
+
518
+ expect(returned).toBeNull();
519
+ expect(result.current.fileDecisionErrorsByKey.get("fcs-1:fc-9")?.message).toBe(
520
+ "expected_digest mismatch",
521
+ );
522
+ expect(result.current.error).toBeNull(); // header banner unaffected
523
+ });
524
+
525
+ it("does not fire onSuccess", async () => {
526
+ mockSubmitFileDecision.mockResolvedValueOnce(makeExecution());
527
+ const onSuccess = vi.fn();
528
+
529
+ const { result } = renderHook(
530
+ () => useWorkflowExecutionActions("wex-001", { onSuccess }),
531
+ { wrapper: createWrapper(makeMockClient()) },
532
+ );
533
+
534
+ await act(async () => {
535
+ await result.current.submitFileDecision("aex-child", "fcs-1", 1 as any);
536
+ });
537
+
538
+ expect(onSuccess).not.toHaveBeenCalled();
539
+ });
540
+
541
+ it("null executionId returns null without calling the SDK", async () => {
542
+ const { result } = renderHook(
543
+ () => useWorkflowExecutionActions(null),
544
+ { wrapper: createWrapper(makeMockClient()) },
545
+ );
546
+
547
+ let returned: any;
548
+ await act(async () => {
549
+ returned = await result.current.submitFileDecision("aex-child", "fcs-1", 1 as any);
550
+ });
551
+
552
+ expect(returned).toBeNull();
553
+ expect(mockSubmitFileDecision).not.toHaveBeenCalled();
554
+ expect(result.current.fileDecisionSubmittingKeys.size).toBe(0);
555
+ });
556
+ });
@@ -35,8 +35,17 @@ export interface ExecutionInspectorProps {
35
35
  readonly pendingApprovals?: readonly WorkflowPendingApproval[];
36
36
  /** Callback to submit an agent tool approval decision. */
37
37
  readonly onSubmitApproval?: (toolCallId: string, action: ApprovalAction, comment?: string) => Promise<unknown>;
38
- /** Whether an approval submission is in flight. */
39
- readonly isSubmittingApproval?: boolean;
38
+ /**
39
+ * Tool-call ids whose agent-tool approval is in flight, keyed by `toolCallId`
40
+ * so deciding one gate never spins another. Supply
41
+ * {@link useWorkflowExecutionActions}'s `approvalSubmittingToolCallIds`.
42
+ */
43
+ readonly approvalSubmittingToolCallIds?: ReadonlySet<string>;
44
+ /**
45
+ * Per-gate agent-tool approval failures, keyed by `toolCallId`. Supply
46
+ * {@link useWorkflowExecutionActions}'s `approvalErrorsByToolCallId`.
47
+ */
48
+ readonly approvalErrorsByToolCallId?: ReadonlyMap<string, Error>;
40
49
  /** Callback to submit a workflow-level human_input task decision. */
41
50
  readonly onSubmitTaskApproval?: (
42
51
  taskName: string,
@@ -44,8 +53,16 @@ export interface ExecutionInspectorProps {
44
53
  formData?: Record<string, unknown>,
45
54
  comment?: string,
46
55
  ) => Promise<unknown>;
47
- /** Whether a task approval submission is in flight. */
48
- readonly isSubmittingTaskApproval?: boolean;
56
+ /**
57
+ * Task names whose human_input approval is in flight, keyed by `taskName`.
58
+ * Supply {@link useWorkflowExecutionActions}'s `taskApprovalSubmittingTaskNames`.
59
+ */
60
+ readonly taskApprovalSubmittingTaskNames?: ReadonlySet<string>;
61
+ /**
62
+ * Per-gate human_input approval failures, keyed by `taskName`. Supply
63
+ * {@link useWorkflowExecutionActions}'s `taskApprovalErrorsByTaskName`.
64
+ */
65
+ readonly taskApprovalErrorsByTaskName?: ReadonlyMap<string, Error>;
49
66
  /** Additional CSS class names. */
50
67
  readonly className?: string;
51
68
  }
@@ -89,9 +106,11 @@ export const ExecutionInspector = memo(function ExecutionInspector({
89
106
  onNavigateToAgentExecution,
90
107
  pendingApprovals,
91
108
  onSubmitApproval,
92
- isSubmittingApproval,
109
+ approvalSubmittingToolCallIds,
110
+ approvalErrorsByToolCallId,
93
111
  onSubmitTaskApproval,
94
- isSubmittingTaskApproval,
112
+ taskApprovalSubmittingTaskNames,
113
+ taskApprovalErrorsByTaskName,
95
114
  className,
96
115
  }: ExecutionInspectorProps) {
97
116
  const { detail } = useExecutionTaskDetail({
@@ -212,17 +231,21 @@ export const ExecutionInspector = memo(function ExecutionInspector({
212
231
  )}
213
232
  {effectiveTab === "approval" && taskApprovals.length > 0 && onSubmitApproval && (
214
233
  <div className="space-y-2">
215
- {taskApprovals.map((pa) => (
216
- <WorkflowExecutionApprovalCard
217
- key={pa.approval?.toolCallId ?? pa.childAgentExecutionId}
218
- prompt={pa.approval?.message || `Tool "${pa.approval?.toolName}" requires approval`}
219
- toolCallId={pa.approval?.toolCallId ?? ""}
220
- approvers={[]}
221
- timeoutSeconds={0}
222
- onSubmitApproval={onSubmitApproval}
223
- isSubmitting={isSubmittingApproval ?? false}
224
- />
225
- ))}
234
+ {taskApprovals.map((pa) => {
235
+ const toolCallId = pa.approval?.toolCallId ?? "";
236
+ return (
237
+ <WorkflowExecutionApprovalCard
238
+ key={toolCallId || pa.childAgentExecutionId}
239
+ prompt={pa.approval?.message || `Tool "${pa.approval?.toolName}" requires approval`}
240
+ toolCallId={toolCallId}
241
+ approvers={[]}
242
+ timeoutSeconds={0}
243
+ onSubmitApproval={onSubmitApproval}
244
+ isSubmitting={approvalSubmittingToolCallIds?.has(toolCallId) ?? false}
245
+ error={approvalErrorsByToolCallId?.get(toolCallId) ?? null}
246
+ />
247
+ );
248
+ })}
226
249
  </div>
227
250
  )}
228
251
  {effectiveTab === "approval" && detail.approval && (
@@ -234,7 +257,8 @@ export const ExecutionInspector = memo(function ExecutionInspector({
234
257
  outcomes={detail.approval.outcomes}
235
258
  formSchema={detail.approval.formSchema ?? undefined}
236
259
  onSubmit={onSubmitTaskApproval}
237
- isSubmitting={isSubmittingTaskApproval ?? false}
260
+ isSubmitting={taskApprovalSubmittingTaskNames?.has(detail.taskName) ?? false}
261
+ error={taskApprovalErrorsByTaskName?.get(detail.taskName) ?? null}
238
262
  />
239
263
  ) : (
240
264
  // Gate resolved — present the captured decision read-only so a
@@ -208,6 +208,11 @@ export {
208
208
  WorkflowExecutionApprovalCard,
209
209
  type WorkflowExecutionApprovalCardProps,
210
210
  } from "./WorkflowExecutionApprovalCard";
211
+ export {
212
+ WorkflowFileReviewList,
213
+ type WorkflowFileReviewListProps,
214
+ type WorkflowFileDecisionSubmit,
215
+ } from "./WorkflowFileReviewList";
211
216
 
212
217
  export {
213
218
  WorkflowTaskApprovalCard,
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useRef, useState } from "react";
3
+ import { useCallback, useMemo, useRef, useState } from "react";
4
4
  import type { WorkflowExecution } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
5
- import type { ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
5
+ import type { ApprovalAction, FileDecisionAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
6
+ import { FileDecisionScope } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
6
7
  import { create, type JsonObject } from "@bufbuild/protobuf";
7
8
  import {
8
9
  CancelWorkflowExecutionInputSchema,
@@ -12,9 +13,12 @@ import {
12
13
  RecoverWorkflowExecutionInputSchema,
13
14
  SubmitWorkflowApprovalInputSchema,
14
15
  SubmitWorkflowTaskApprovalInputSchema,
16
+ SubmitWorkflowFileDecisionInputSchema,
15
17
  } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/io_pb";
16
18
  import { useStigmer } from "../hooks";
17
19
  import { toError } from "../internal/toError";
20
+ import { useKeyedSubmission } from "../internal/useKeyedSubmission";
21
+ import { fileDecisionKey, type FileDecisionOptions } from "../execution/useFileReview";
18
22
 
19
23
  /** Options for {@link useWorkflowExecutionActions}. */
20
24
  export interface UseWorkflowExecutionActionsOptions {
@@ -71,12 +75,74 @@ export interface UseWorkflowExecutionActionsReturn {
71
75
  formData?: Record<string, unknown>,
72
76
  comment?: string,
73
77
  ) => Promise<WorkflowExecution | null>;
74
- /** `true` while any action is in flight. */
78
+ /**
79
+ * Submit a keep/discard decision for a child agent's file review, surfaced on
80
+ * this workflow via `status.pending_file_reviews`. The file-review sibling of
81
+ * {@link submitApproval}: the server forwards it to the child's
82
+ * `AgentExecution.submitFileDecision`.
83
+ *
84
+ * `childAgentExecutionId` comes from the surfaced
85
+ * `WorkflowPendingFileReview` reference. `options` carries the same
86
+ * per-decision detail as {@link FileReviewCard}'s `onSubmit` (scope,
87
+ * `fileChangeId`, `expectedDigest`, `reason`, `acknowledgeUnreviewable`).
88
+ */
89
+ readonly submitFileDecision: (
90
+ childAgentExecutionId: string,
91
+ changeSetId: string,
92
+ action: FileDecisionAction,
93
+ options?: FileDecisionOptions,
94
+ ) => Promise<WorkflowExecution | null>;
95
+ /**
96
+ * `true` while a **lifecycle** action (cancel/terminate/pause/resume/recover)
97
+ * is in flight. Approvals are excluded — they are per-gate, so read their
98
+ * in-flight state from {@link approvalSubmittingToolCallIds} /
99
+ * {@link taskApprovalSubmittingTaskNames} instead.
100
+ */
75
101
  readonly isSubmitting: boolean;
76
- /** Error from the last failed action, or `null`. */
102
+ /**
103
+ * Error from the last failed **lifecycle** action, or `null`. Approval
104
+ * failures are per-gate and live in {@link approvalErrorsByToolCallId} /
105
+ * {@link taskApprovalErrorsByTaskName} so each surfaces beside the gate that
106
+ * failed — never in this shared scalar (which backs the header banner).
107
+ */
77
108
  readonly error: Error | null;
78
- /** Reset `error` to `null`. */
109
+ /** Reset the lifecycle {@link error} to `null`. */
79
110
  readonly clearError: () => void;
111
+ /**
112
+ * Tool-call ids whose agent-tool approval is currently being submitted, keyed
113
+ * exactly like {@link approvalErrorsByToolCallId}. A workflow can hold many
114
+ * concurrent gates, so deciding one must not spin or disable the others.
115
+ */
116
+ readonly approvalSubmittingToolCallIds: ReadonlySet<string>;
117
+ /**
118
+ * Per-gate agent-tool approval failures, keyed by `toolCallId`. Consumed by
119
+ * {@link WorkflowExecutionApprovalCard} to surface the failure in-card,
120
+ * beside the gate that failed. Cleared for a gate when it is retried.
121
+ */
122
+ readonly approvalErrorsByToolCallId: ReadonlyMap<string, Error>;
123
+ /**
124
+ * Task names whose human_input task approval is currently being submitted,
125
+ * keyed exactly like {@link taskApprovalErrorsByTaskName}.
126
+ */
127
+ readonly taskApprovalSubmittingTaskNames: ReadonlySet<string>;
128
+ /**
129
+ * Per-gate human_input task approval failures, keyed by `taskName`. Consumed
130
+ * by `WorkflowTaskApprovalCard` to surface the failure in-card, beside
131
+ * the gate that failed. Cleared for a gate when it is retried.
132
+ */
133
+ readonly taskApprovalErrorsByTaskName: ReadonlyMap<string, Error>;
134
+ /**
135
+ * Decision keys currently being submitted for file reviews, keyed exactly like
136
+ * {@link FileReviewCard}'s `submittingDecisionKeys` (via {@link fileDecisionKey}
137
+ * — the change set id for a whole-set decision, `changeSetId:fileChangeId` for a
138
+ * per-file one). Pass straight through to each surfaced card.
139
+ */
140
+ readonly fileDecisionSubmittingKeys: ReadonlySet<string>;
141
+ /**
142
+ * Per-decision file-review failures, keyed like {@link fileDecisionSubmittingKeys}.
143
+ * Pass straight through to {@link FileReviewCard}'s `decisionErrors`.
144
+ */
145
+ readonly fileDecisionErrorsByKey: ReadonlyMap<string, Error>;
80
146
  }
81
147
 
82
148
  /**
@@ -107,6 +173,14 @@ export function useWorkflowExecutionActions(
107
173
  const [isSubmitting, setIsSubmitting] = useState(false);
108
174
  const [error, setError] = useState<Error | null>(null);
109
175
 
176
+ // Approvals are per-gate (a workflow can hold many at once), so each kind gets
177
+ // its own keyed in-flight + error state — keyed by tool-call id for agent-tool
178
+ // approvals and by task name for human_input task approvals. The lifecycle
179
+ // actions below stay on the shared scalar (they are header singletons).
180
+ const approvals = useKeyedSubmission<WorkflowExecution>();
181
+ const taskApprovals = useKeyedSubmission<WorkflowExecution>();
182
+ const fileDecisions = useKeyedSubmission<WorkflowExecution>();
183
+
110
184
  const executionIdRef = useRef(executionId);
111
185
  executionIdRef.current = executionId;
112
186
  const stigmerRef = useRef(stigmer);
@@ -202,10 +276,15 @@ export function useWorkflowExecutionActions(
202
276
  [wrap],
203
277
  );
204
278
 
279
+ // Approvals do NOT go through `wrap`: their in-flight + error state is keyed
280
+ // (per gate), and the keyed primitive re-throws after recording, so we swallow
281
+ // to `null` here to keep the `WorkflowExecution | null` contract. They never
282
+ // fire `onSuccess` — their effects arrive via the event stream.
205
283
  const submitApproval = useCallback(
206
- (toolCallId: string, action: ApprovalAction, comment?: string) =>
207
- wrap(
208
- () =>
284
+ (toolCallId: string, action: ApprovalAction, comment?: string) => {
285
+ if (!executionIdRef.current) return Promise.resolve(null);
286
+ return approvals
287
+ .run(toolCallId, () =>
209
288
  stigmerRef.current.workflowExecution.submitApproval(
210
289
  create(SubmitWorkflowApprovalInputSchema, {
211
290
  executionId: executionIdRef.current!,
@@ -214,15 +293,17 @@ export function useWorkflowExecutionActions(
214
293
  comment: comment ?? "",
215
294
  }),
216
295
  ),
217
- false,
218
- ),
219
- [wrap],
296
+ )
297
+ .catch(() => null);
298
+ },
299
+ [approvals.run],
220
300
  );
221
301
 
222
302
  const submitTaskApproval = useCallback(
223
- (taskName: string, outcome: string, formData?: Record<string, unknown>, comment?: string) =>
224
- wrap(
225
- () =>
303
+ (taskName: string, outcome: string, formData?: Record<string, unknown>, comment?: string) => {
304
+ if (!executionIdRef.current) return Promise.resolve(null);
305
+ return taskApprovals
306
+ .run(taskName, () =>
226
307
  stigmerRef.current.workflowExecution.submitWorkflowTaskApproval(
227
308
  create(SubmitWorkflowTaskApprovalInputSchema, {
228
309
  executionId: executionIdRef.current!,
@@ -233,21 +314,88 @@ export function useWorkflowExecutionActions(
233
314
  comment: comment ?? "",
234
315
  }),
235
316
  ),
236
- false,
237
- ),
238
- [wrap],
317
+ )
318
+ .catch(() => null);
319
+ },
320
+ [taskApprovals.run],
239
321
  );
240
322
 
241
- return {
242
- cancel,
243
- terminate,
244
- pause,
245
- resume,
246
- recover,
247
- submitApproval,
248
- submitTaskApproval,
249
- isSubmitting,
250
- error,
251
- clearError,
252
- };
323
+ // File decisions are per-target (a workflow can surface many change sets across
324
+ // parallel children), so they use the keyed primitive keyed exactly like
325
+ // FileReviewCard expects (fileDecisionKey). Never fires onSuccess — the effect
326
+ // arrives via the execution stream.
327
+ const submitFileDecision = useCallback(
328
+ (
329
+ childAgentExecutionId: string,
330
+ changeSetId: string,
331
+ action: FileDecisionAction,
332
+ options?: FileDecisionOptions,
333
+ ) => {
334
+ if (!executionIdRef.current) return Promise.resolve(null);
335
+ const fileChangeId = options?.fileChangeId ?? "";
336
+ const scope =
337
+ options?.scope ??
338
+ (fileChangeId ? FileDecisionScope.FILE : FileDecisionScope.CHANGE_SET);
339
+ const key = fileDecisionKey(changeSetId, fileChangeId || undefined);
340
+ return fileDecisions
341
+ .run(key, () =>
342
+ stigmerRef.current.workflowExecution.submitFileDecision(
343
+ create(SubmitWorkflowFileDecisionInputSchema, {
344
+ executionId: executionIdRef.current!,
345
+ childAgentExecutionId,
346
+ changeSetId,
347
+ scope,
348
+ fileChangeId,
349
+ action,
350
+ expectedDigest: options?.expectedDigest ?? "",
351
+ reason: options?.reason ?? "",
352
+ acknowledgeUnreviewable: options?.acknowledgeUnreviewable ?? false,
353
+ }),
354
+ ),
355
+ )
356
+ .catch(() => null);
357
+ },
358
+ [fileDecisions.run],
359
+ );
360
+
361
+ return useMemo(
362
+ () => ({
363
+ cancel,
364
+ terminate,
365
+ pause,
366
+ resume,
367
+ recover,
368
+ submitApproval,
369
+ submitTaskApproval,
370
+ submitFileDecision,
371
+ isSubmitting,
372
+ error,
373
+ clearError,
374
+ approvalSubmittingToolCallIds: approvals.submittingKeys,
375
+ approvalErrorsByToolCallId: approvals.errorsByKey,
376
+ taskApprovalSubmittingTaskNames: taskApprovals.submittingKeys,
377
+ taskApprovalErrorsByTaskName: taskApprovals.errorsByKey,
378
+ fileDecisionSubmittingKeys: fileDecisions.submittingKeys,
379
+ fileDecisionErrorsByKey: fileDecisions.errorsByKey,
380
+ }),
381
+ [
382
+ cancel,
383
+ terminate,
384
+ pause,
385
+ resume,
386
+ recover,
387
+ submitApproval,
388
+ submitTaskApproval,
389
+ submitFileDecision,
390
+ isSubmitting,
391
+ error,
392
+ clearError,
393
+ approvals.submittingKeys,
394
+ approvals.errorsByKey,
395
+ taskApprovals.submittingKeys,
396
+ taskApprovals.errorsByKey,
397
+ fileDecisions.submittingKeys,
398
+ fileDecisions.errorsByKey,
399
+ ],
400
+ );
253
401
  }