@stigmer/react 3.0.9-dev.20260615153829 → 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 (439) 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/code-highlight.d.ts +24 -0
  178. package/internal/code-highlight.d.ts.map +1 -0
  179. package/internal/code-highlight.js +115 -0
  180. package/internal/code-highlight.js.map +1 -0
  181. package/internal/markdown-components.d.ts.map +1 -1
  182. package/internal/markdown-components.js +13 -2
  183. package/internal/markdown-components.js.map +1 -1
  184. package/internal/useAutoDisclosure.d.ts +34 -0
  185. package/internal/useAutoDisclosure.d.ts.map +1 -0
  186. package/internal/useAutoDisclosure.js +53 -0
  187. package/internal/useAutoDisclosure.js.map +1 -0
  188. package/internal/useElapsedSince.d.ts +30 -0
  189. package/internal/useElapsedSince.d.ts.map +1 -0
  190. package/internal/useElapsedSince.js +70 -0
  191. package/internal/useElapsedSince.js.map +1 -0
  192. package/internal/useIsOverflowing.d.ts +30 -0
  193. package/internal/useIsOverflowing.d.ts.map +1 -0
  194. package/internal/useIsOverflowing.js +47 -0
  195. package/internal/useIsOverflowing.js.map +1 -0
  196. package/internal/useIsTextTruncated.d.ts +24 -0
  197. package/internal/useIsTextTruncated.d.ts.map +1 -0
  198. package/internal/useIsTextTruncated.js +41 -0
  199. package/internal/useIsTextTruncated.js.map +1 -0
  200. package/internal/useKeyedSubmission.d.ts +49 -0
  201. package/internal/useKeyedSubmission.d.ts.map +1 -0
  202. package/internal/useKeyedSubmission.js +67 -0
  203. package/internal/useKeyedSubmission.js.map +1 -0
  204. package/package.json +7 -4
  205. package/session/SessionViewer.d.ts +2 -2
  206. package/session/SessionViewer.d.ts.map +1 -1
  207. package/session/SessionViewer.js +10 -4
  208. package/session/SessionViewer.js.map +1 -1
  209. package/session/index.d.ts +2 -0
  210. package/session/index.d.ts.map +1 -1
  211. package/session/index.js +1 -0
  212. package/session/index.js.map +1 -1
  213. package/session/inspector/ChangesTab.d.ts +8 -5
  214. package/session/inspector/ChangesTab.d.ts.map +1 -1
  215. package/session/inspector/ChangesTab.js +11 -8
  216. package/session/inspector/ChangesTab.js.map +1 -1
  217. package/session/inspector/SessionInspector.d.ts +4 -4
  218. package/session/inspector/SessionInspector.d.ts.map +1 -1
  219. package/session/inspector/SessionInspector.js +4 -4
  220. package/session/inspector/SessionInspector.js.map +1 -1
  221. package/session/inspector/index.d.ts +0 -2
  222. package/session/inspector/index.d.ts.map +1 -1
  223. package/session/inspector/index.js +0 -1
  224. package/session/inspector/index.js.map +1 -1
  225. package/session/inspector/useSessionInspector.d.ts +8 -4
  226. package/session/inspector/useSessionInspector.d.ts.map +1 -1
  227. package/session/inspector/useSessionInspector.js +27 -20
  228. package/session/inspector/useSessionInspector.js.map +1 -1
  229. package/session/useSessionConversation.d.ts +37 -3
  230. package/session/useSessionConversation.d.ts.map +1 -1
  231. package/session/useSessionConversation.js +18 -2
  232. package/session/useSessionConversation.js.map +1 -1
  233. package/session/useSessionFileChanges.d.ts +74 -0
  234. package/session/useSessionFileChanges.d.ts.map +1 -0
  235. package/session/useSessionFileChanges.js +178 -0
  236. package/session/useSessionFileChanges.js.map +1 -0
  237. package/src/__tests__/styles-border-layer-invariant.test.ts +105 -0
  238. package/src/execution/ApprovalCard.tsx +368 -230
  239. package/src/execution/ApprovalContext.ts +109 -0
  240. package/src/execution/ArtifactCard.tsx +10 -6
  241. package/src/execution/ArtifactPreviewModal.tsx +12 -6
  242. package/src/execution/EmptyChangeNotice.tsx +62 -0
  243. package/src/execution/FileChangesView.tsx +383 -0
  244. package/src/execution/FilePathLink.tsx +65 -12
  245. package/src/execution/FileReviewCard.tsx +1118 -0
  246. package/src/execution/FileReviewContext.ts +53 -0
  247. package/src/execution/FileReviewDock.tsx +157 -0
  248. package/src/execution/McpToolDetail.tsx +98 -32
  249. package/src/execution/MessageThread.tsx +374 -9
  250. package/src/execution/PlanArtifactCard.tsx +9 -6
  251. package/src/execution/PlanCompletionCard.tsx +1 -1
  252. package/src/execution/ResultView.tsx +333 -162
  253. package/src/execution/SubAgentSection.tsx +58 -11
  254. package/src/execution/TerminalSession.tsx +91 -0
  255. package/src/execution/TodoCard.tsx +186 -0
  256. package/src/execution/ToolArgsView.tsx +28 -17
  257. package/src/execution/ToolCallDetail.tsx +137 -26
  258. package/src/execution/ToolCallGroup.tsx +68 -296
  259. package/src/execution/ToolCallItem.tsx +265 -67
  260. package/src/execution/ToolRunGroup.tsx +302 -0
  261. package/src/execution/__tests__/ApprovalCard.test.tsx +395 -5
  262. package/src/execution/__tests__/ArtifactContentRenderer.test.tsx +15 -0
  263. package/src/execution/__tests__/ArtifactPreviewModal.test.tsx +0 -1
  264. package/src/execution/__tests__/EmptyChangeNotice.test.tsx +24 -0
  265. package/src/execution/__tests__/FileChangesView.test.tsx +296 -0
  266. package/src/execution/__tests__/FilePathLink.test.tsx +92 -0
  267. package/src/execution/__tests__/FileReviewCard.test.tsx +1044 -0
  268. package/src/execution/__tests__/FileReviewDock.test.tsx +134 -0
  269. package/src/execution/__tests__/McpToolDetail.test.tsx +79 -0
  270. package/src/execution/__tests__/MessageThread.test.tsx +324 -0
  271. package/src/execution/__tests__/PlanArtifactCard.test.tsx +37 -16
  272. package/src/execution/__tests__/ResultView.test.tsx +280 -2
  273. package/src/execution/__tests__/SubAgentSection.test.tsx +180 -0
  274. package/src/execution/__tests__/TerminalSession.test.tsx +72 -0
  275. package/src/execution/__tests__/TodoCard.test.tsx +102 -0
  276. package/src/execution/__tests__/ToolArgsView.test.tsx +48 -0
  277. package/src/execution/__tests__/ToolCallDetail.test.tsx +195 -0
  278. package/src/execution/__tests__/ToolCallGroup.test.tsx +132 -0
  279. package/src/execution/__tests__/ToolCallItem.test.tsx +521 -0
  280. package/src/execution/__tests__/ToolRunGroup.test.tsx +137 -0
  281. package/src/execution/__tests__/blanked-narration.test.tsx +149 -0
  282. package/src/execution/__tests__/buildThreadItems-approvals.test.ts +304 -0
  283. package/src/execution/__tests__/buildThreadItems-file-review.test.ts +331 -0
  284. package/src/execution/__tests__/file-path-resolver.test.ts +44 -0
  285. package/src/execution/__tests__/file-review-status.test.ts +343 -0
  286. package/src/execution/__tests__/message-entry.test.tsx +16 -0
  287. package/src/execution/__tests__/segment-tool-calls.test.ts +124 -0
  288. package/src/execution/__tests__/subagent-file-review-badge.test.tsx +273 -0
  289. package/src/execution/__tests__/thread-keys.test.ts +195 -0
  290. package/src/execution/__tests__/tool-presenter.test.tsx +95 -0
  291. package/src/execution/__tests__/useArtifactDownload.test.tsx +67 -0
  292. package/src/execution/__tests__/useArtifactDownloadUrl.test.tsx +51 -0
  293. package/src/execution/__tests__/useFileChangeContent.test.tsx +240 -0
  294. package/src/execution/__tests__/useFileReview.test.tsx +236 -0
  295. package/src/execution/__tests__/useSubmitApproval.test.tsx +165 -0
  296. package/src/execution/__tests__/useToolOutputContent.test.tsx +82 -0
  297. package/src/execution/approval-provenance.ts +8 -0
  298. package/src/execution/artifact-utils.ts +0 -18
  299. package/src/execution/execution-phases.ts +4 -16
  300. package/src/execution/file-path-resolver.ts +38 -0
  301. package/src/execution/file-review-status.ts +219 -0
  302. package/src/execution/index.ts +84 -4
  303. package/src/execution/segment-tool-calls.ts +101 -0
  304. package/src/execution/tool-categories.ts +128 -1
  305. package/src/execution/tool-presenter.ts +58 -3
  306. package/src/execution/tool-rendering-primitives.tsx +11 -14
  307. package/src/execution/useArtifactDownload.ts +109 -0
  308. package/src/execution/useArtifactDownloadUrl.ts +116 -0
  309. package/src/execution/useFileChangeContent.ts +149 -0
  310. package/src/execution/useFileReview.ts +197 -0
  311. package/src/execution/useSubmitApproval.ts +49 -27
  312. package/src/execution/useToolOutputContent.ts +70 -0
  313. package/src/index.ts +60 -1
  314. package/src/internal/ApprovalPeekBar.tsx +73 -0
  315. package/src/internal/BoundedContent.tsx +97 -0
  316. package/src/internal/DecisionButton.tsx +127 -0
  317. package/src/internal/InCardDecisionError.tsx +47 -0
  318. package/src/internal/RevealToggle.tsx +91 -0
  319. package/src/internal/VirtualizedThread.tsx +28 -2
  320. package/src/internal/__tests__/ApprovalPeekBar.test.tsx +34 -0
  321. package/src/internal/__tests__/BoundedContent.test.tsx +92 -0
  322. package/src/internal/__tests__/DecisionButton.test.tsx +107 -0
  323. package/src/internal/__tests__/RevealToggle.test.tsx +61 -0
  324. package/src/internal/__tests__/code-highlight.test.tsx +59 -0
  325. package/src/internal/__tests__/markdown-components.test.tsx +68 -2
  326. package/src/internal/__tests__/useAutoDisclosure.test.ts +74 -0
  327. package/src/internal/__tests__/useElapsedSince.test.tsx +68 -0
  328. package/src/internal/__tests__/useIsOverflowing.test.ts +36 -0
  329. package/src/internal/__tests__/useKeyedSubmission.test.tsx +126 -0
  330. package/src/internal/code-highlight.tsx +120 -0
  331. package/src/internal/markdown-components.tsx +20 -3
  332. package/src/internal/useAutoDisclosure.ts +58 -0
  333. package/src/internal/useElapsedSince.ts +72 -0
  334. package/src/internal/useIsOverflowing.ts +61 -0
  335. package/src/internal/useIsTextTruncated.ts +55 -0
  336. package/src/internal/useKeyedSubmission.ts +101 -0
  337. package/src/session/SessionViewer.tsx +21 -4
  338. package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +198 -0
  339. package/src/session/__tests__/audienceWiring.test.tsx +4 -0
  340. package/src/session/__tests__/useSessionFileChanges.test.ts +303 -0
  341. package/src/session/index.ts +3 -0
  342. package/src/session/inspector/ChangesTab.tsx +20 -17
  343. package/src/session/inspector/SessionInspector.tsx +12 -10
  344. package/src/session/inspector/__tests__/ArtifactsTab.test.tsx +0 -1
  345. package/src/session/inspector/__tests__/ChangesTab.test.tsx +38 -0
  346. package/src/session/inspector/__tests__/useSessionInspector.test.ts +46 -61
  347. package/src/session/inspector/index.ts +0 -3
  348. package/src/session/inspector/useSessionInspector.ts +27 -20
  349. package/src/session/useSessionConversation.ts +80 -3
  350. package/src/session/useSessionFileChanges.ts +207 -0
  351. package/src/styles.css +123 -10
  352. package/src/version-history/DiffViewer.tsx +10 -7
  353. package/src/version-history/UnifiedDiffView.tsx +66 -0
  354. package/src/version-history/__tests__/DiffViewer.test.tsx +57 -0
  355. package/src/version-history/__tests__/UnifiedDiffView.test.tsx +50 -0
  356. package/src/version-history/__tests__/computeDiff.test.ts +35 -0
  357. package/src/version-history/__tests__/parseUnifiedDiff.test.ts +82 -0
  358. package/src/version-history/computeDiff.ts +6 -37
  359. package/src/version-history/diff-hunks.ts +63 -0
  360. package/src/version-history/index.ts +1 -0
  361. package/src/version-history/parseUnifiedDiff.ts +34 -0
  362. package/src/workflow/WorkflowExecutionApprovalCard.tsx +49 -24
  363. package/src/workflow/WorkflowExecutionTimeline.tsx +15 -4
  364. package/src/workflow/WorkflowExecutionTimelineEvent.tsx +8 -3
  365. package/src/workflow/WorkflowExecutionViewer.tsx +72 -28
  366. package/src/workflow/WorkflowFileReviewList.tsx +152 -0
  367. package/src/workflow/WorkflowTaskApprovalCard.tsx +37 -85
  368. package/src/workflow/__tests__/ExecutionInspector.test.tsx +30 -2
  369. package/src/workflow/__tests__/WorkflowExecutionApprovalCard.test.tsx +129 -0
  370. package/src/workflow/__tests__/WorkflowFileReviewList.test.tsx +129 -0
  371. package/src/workflow/__tests__/WorkflowTaskApprovalCard.test.tsx +43 -11
  372. package/src/workflow/__tests__/useWorkflowExecutionActions.test.tsx +276 -0
  373. package/src/workflow/execution-inspector/ExecutionInspector.tsx +42 -18
  374. package/src/workflow/index.ts +5 -0
  375. package/src/workflow/useWorkflowExecutionActions.ts +177 -29
  376. package/styles.css +1 -1
  377. package/version-history/DiffViewer.d.ts.map +1 -1
  378. package/version-history/DiffViewer.js +9 -3
  379. package/version-history/DiffViewer.js.map +1 -1
  380. package/version-history/UnifiedDiffView.d.ts +29 -0
  381. package/version-history/UnifiedDiffView.d.ts.map +1 -0
  382. package/version-history/UnifiedDiffView.js +36 -0
  383. package/version-history/UnifiedDiffView.js.map +1 -0
  384. package/version-history/computeDiff.d.ts +3 -1
  385. package/version-history/computeDiff.d.ts.map +1 -1
  386. package/version-history/computeDiff.js +5 -35
  387. package/version-history/computeDiff.js.map +1 -1
  388. package/version-history/diff-hunks.d.ts +21 -0
  389. package/version-history/diff-hunks.d.ts.map +1 -0
  390. package/version-history/diff-hunks.js +58 -0
  391. package/version-history/diff-hunks.js.map +1 -0
  392. package/version-history/index.d.ts +1 -0
  393. package/version-history/index.d.ts.map +1 -1
  394. package/version-history/index.js +1 -0
  395. package/version-history/index.js.map +1 -1
  396. package/version-history/parseUnifiedDiff.d.ts +22 -0
  397. package/version-history/parseUnifiedDiff.d.ts.map +1 -0
  398. package/version-history/parseUnifiedDiff.js +34 -0
  399. package/version-history/parseUnifiedDiff.js.map +1 -0
  400. package/workflow/WorkflowExecutionApprovalCard.d.ts +8 -1
  401. package/workflow/WorkflowExecutionApprovalCard.d.ts.map +1 -1
  402. package/workflow/WorkflowExecutionApprovalCard.js +17 -3
  403. package/workflow/WorkflowExecutionApprovalCard.js.map +1 -1
  404. package/workflow/WorkflowExecutionTimeline.d.ts +11 -2
  405. package/workflow/WorkflowExecutionTimeline.d.ts.map +1 -1
  406. package/workflow/WorkflowExecutionTimeline.js +2 -2
  407. package/workflow/WorkflowExecutionTimeline.js.map +1 -1
  408. package/workflow/WorkflowExecutionTimelineEvent.d.ts +4 -1
  409. package/workflow/WorkflowExecutionTimelineEvent.d.ts.map +1 -1
  410. package/workflow/WorkflowExecutionTimelineEvent.js +2 -2
  411. package/workflow/WorkflowExecutionTimelineEvent.js.map +1 -1
  412. package/workflow/WorkflowExecutionViewer.d.ts.map +1 -1
  413. package/workflow/WorkflowExecutionViewer.js +18 -10
  414. package/workflow/WorkflowExecutionViewer.js.map +1 -1
  415. package/workflow/WorkflowFileReviewList.d.ts +46 -0
  416. package/workflow/WorkflowFileReviewList.d.ts.map +1 -0
  417. package/workflow/WorkflowFileReviewList.js +48 -0
  418. package/workflow/WorkflowFileReviewList.js.map +1 -0
  419. package/workflow/WorkflowTaskApprovalCard.d.ts +8 -1
  420. package/workflow/WorkflowTaskApprovalCard.d.ts.map +1 -1
  421. package/workflow/WorkflowTaskApprovalCard.js +12 -19
  422. package/workflow/WorkflowTaskApprovalCard.js.map +1 -1
  423. package/workflow/execution-inspector/ExecutionInspector.d.ts +21 -4
  424. package/workflow/execution-inspector/ExecutionInspector.d.ts.map +1 -1
  425. package/workflow/execution-inspector/ExecutionInspector.js +6 -3
  426. package/workflow/execution-inspector/ExecutionInspector.js.map +1 -1
  427. package/workflow/index.d.ts +1 -0
  428. package/workflow/index.d.ts.map +1 -1
  429. package/workflow/index.js +1 -0
  430. package/workflow/index.js.map +1 -1
  431. package/workflow/useWorkflowExecutionActions.d.ts +62 -4
  432. package/workflow/useWorkflowExecutionActions.d.ts.map +1 -1
  433. package/workflow/useWorkflowExecutionActions.js +94 -18
  434. package/workflow/useWorkflowExecutionActions.js.map +1 -1
  435. package/session/inspector/PlanTab.d.ts +0 -15
  436. package/session/inspector/PlanTab.d.ts.map +0 -1
  437. package/session/inspector/PlanTab.js +0 -19
  438. package/session/inspector/PlanTab.js.map +0 -1
  439. package/src/session/inspector/PlanTab.tsx +0 -31
@@ -20,7 +20,8 @@ import { ExecutionInspector } from "./execution-inspector";
20
20
  import { ExecutionComparisonPicker } from "./execution-comparison/ExecutionComparisonPicker";
21
21
  import { ExecutionComparisonView } from "./execution-comparison/ExecutionComparisonView";
22
22
  import { WorkflowExecutionApprovalCard } from "./WorkflowExecutionApprovalCard";
23
- import type { WorkflowPendingApproval } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
23
+ import { WorkflowFileReviewList, type WorkflowFileDecisionSubmit } from "./WorkflowFileReviewList";
24
+ import type { WorkflowPendingApproval, WorkflowPendingFileReview } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
24
25
  import type { ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
25
26
  import { ResizableSplit } from "../internal/ResizableSplit";
26
27
 
@@ -287,7 +288,9 @@ export const WorkflowExecutionViewer = memo(function WorkflowExecutionViewer({
287
288
  onClose={handleCloseComparePicker}
288
289
  />
289
290
 
290
- {/* Action error banner (lifecycle actions: cancel, pause, recover, etc.) */}
291
+ {/* Action error banner lifecycle actions only (cancel/terminate/pause/
292
+ resume/recover). Approval failures are per-gate and surface in-card on
293
+ the failing approval card, never here. */}
291
294
  {actions.error && (
292
295
  <div className="flex items-center gap-2 border-b border-destructive/20 bg-destructive/5 px-4 py-2">
293
296
  <p className="flex-1 text-xs text-destructive">{actions.error.message}</p>
@@ -365,9 +368,11 @@ export const WorkflowExecutionViewer = memo(function WorkflowExecutionViewer({
365
368
  onNavigateToAgentExecution={onNavigateToAgentExecution}
366
369
  pendingApprovals={execution?.status?.pendingApprovals}
367
370
  onSubmitApproval={actions.submitApproval}
368
- isSubmittingApproval={actions.isSubmitting}
371
+ approvalSubmittingToolCallIds={actions.approvalSubmittingToolCallIds}
372
+ approvalErrorsByToolCallId={actions.approvalErrorsByToolCallId}
369
373
  onSubmitTaskApproval={actions.submitTaskApproval}
370
- isSubmittingTaskApproval={actions.isSubmitting}
374
+ taskApprovalSubmittingTaskNames={actions.taskApprovalSubmittingTaskNames}
375
+ taskApprovalErrorsByTaskName={actions.taskApprovalErrorsByTaskName}
371
376
  className="min-h-0 flex-1"
372
377
  />
373
378
 
@@ -403,9 +408,16 @@ export const WorkflowExecutionViewer = memo(function WorkflowExecutionViewer({
403
408
  onNavigateToAgentExecution={onNavigateToAgentExecution}
404
409
  taskStates={effectiveTaskStates}
405
410
  onSubmitTaskApproval={actions.submitTaskApproval}
406
- isSubmittingApproval={actions.isSubmitting}
411
+ taskApprovalSubmittingTaskNames={actions.taskApprovalSubmittingTaskNames}
412
+ taskApprovalErrorsByTaskName={actions.taskApprovalErrorsByTaskName}
407
413
  pendingApprovals={execution?.status?.pendingApprovals}
408
414
  onSubmitApproval={actions.submitApproval}
415
+ approvalSubmittingToolCallIds={actions.approvalSubmittingToolCallIds}
416
+ approvalErrorsByToolCallId={actions.approvalErrorsByToolCallId}
417
+ pendingFileReviews={execution?.status?.pendingFileReviews}
418
+ onSubmitFileDecision={actions.submitFileDecision}
419
+ fileDecisionSubmittingKeys={actions.fileDecisionSubmittingKeys}
420
+ fileDecisionErrorsByKey={actions.fileDecisionErrorsByKey}
409
421
  />
410
422
  </div>
411
423
  </>
@@ -449,9 +461,16 @@ function ExecutionBottomPanel({
449
461
  onNavigateToAgentExecution,
450
462
  taskStates,
451
463
  onSubmitTaskApproval,
452
- isSubmittingApproval,
464
+ taskApprovalSubmittingTaskNames,
465
+ taskApprovalErrorsByTaskName,
453
466
  pendingApprovals,
454
467
  onSubmitApproval,
468
+ approvalSubmittingToolCallIds,
469
+ approvalErrorsByToolCallId,
470
+ pendingFileReviews,
471
+ onSubmitFileDecision,
472
+ fileDecisionSubmittingKeys,
473
+ fileDecisionErrorsByKey,
455
474
  }: {
456
475
  events: WorkflowExecutionTimelineProps["events"];
457
476
  streamState: WorkflowExecutionTimelineProps["streamState"];
@@ -462,28 +481,39 @@ function ExecutionBottomPanel({
462
481
  onNavigateToAgentExecution?: (id: string) => void;
463
482
  taskStates: ReadonlyMap<string, DerivedTaskState>;
464
483
  onSubmitTaskApproval: WorkflowExecutionTimelineProps["onSubmitTaskApproval"];
465
- isSubmittingApproval: boolean;
484
+ taskApprovalSubmittingTaskNames: ReadonlySet<string>;
485
+ taskApprovalErrorsByTaskName: ReadonlyMap<string, Error>;
466
486
  pendingApprovals?: readonly WorkflowPendingApproval[];
467
487
  onSubmitApproval?: (toolCallId: string, action: ApprovalAction, comment?: string) => Promise<unknown>;
488
+ approvalSubmittingToolCallIds: ReadonlySet<string>;
489
+ approvalErrorsByToolCallId: ReadonlyMap<string, Error>;
490
+ pendingFileReviews?: readonly WorkflowPendingFileReview[];
491
+ onSubmitFileDecision: WorkflowFileDecisionSubmit;
492
+ fileDecisionSubmittingKeys: ReadonlySet<string>;
493
+ fileDecisionErrorsByKey: ReadonlyMap<string, Error>;
468
494
  }) {
469
495
  const [collapsed, setCollapsed] = useState(false);
470
496
  const [activeTab, setActiveTab] = useState<BottomTab>("waterfall");
471
- const prevApprovalCountRef = useRef(0);
497
+ const prevHitlCountRef = useRef(0);
472
498
 
473
499
  const approvalCount = pendingApprovals?.length ?? 0;
474
- const hasApprovals = approvalCount > 0;
500
+ const fileReviewCount = pendingFileReviews?.length ?? 0;
501
+ // Both HITL kinds (tool approvals + file reviews) share the one "Approvals" tab.
502
+ const hitlCount = approvalCount + fileReviewCount;
503
+ const hasHitl = hitlCount > 0;
475
504
 
476
- // Auto-switch to Approvals tab when new approvals arrive
505
+ // Auto-switch to the Approvals tab when a new HITL gate (approval or file
506
+ // review) arrives; leave it when everything is resolved.
477
507
  useEffect(() => {
478
- if (approvalCount > prevApprovalCountRef.current && approvalCount > 0) {
508
+ if (hitlCount > prevHitlCountRef.current && hitlCount > 0) {
479
509
  setActiveTab("approvals");
480
510
  setCollapsed(false);
481
511
  }
482
- if (approvalCount === 0 && activeTab === "approvals") {
512
+ if (hitlCount === 0 && activeTab === "approvals") {
483
513
  setActiveTab("waterfall");
484
514
  }
485
- prevApprovalCountRef.current = approvalCount;
486
- }, [approvalCount, activeTab]);
515
+ prevHitlCountRef.current = hitlCount;
516
+ }, [hitlCount, activeTab]);
487
517
 
488
518
  return (
489
519
  <div className="border-t border-[var(--stgm-border,#e5e5e5)]">
@@ -516,9 +546,9 @@ function ExecutionBottomPanel({
516
546
  isActive={activeTab === "events"}
517
547
  onClick={() => { setActiveTab("events"); setCollapsed(false); }}
518
548
  />
519
- {hasApprovals && (
549
+ {hasHitl && (
520
550
  <TabButton
521
- label={`Approvals (${approvalCount})`}
551
+ label={`Approvals (${hitlCount})`}
522
552
  isActive={activeTab === "approvals"}
523
553
  onClick={() => { setActiveTab("approvals"); setCollapsed(false); }}
524
554
  />
@@ -546,24 +576,38 @@ function ExecutionBottomPanel({
546
576
  onNavigateToAgentExecution={onNavigateToAgentExecution}
547
577
  taskStates={taskStates}
548
578
  onSubmitTaskApproval={onSubmitTaskApproval}
549
- isSubmittingApproval={isSubmittingApproval}
579
+ taskApprovalSubmittingTaskNames={taskApprovalSubmittingTaskNames}
580
+ taskApprovalErrorsByTaskName={taskApprovalErrorsByTaskName}
550
581
  className="h-full"
551
582
  />
552
583
  )}
553
- {activeTab === "approvals" && pendingApprovals && onSubmitApproval && (
584
+ {activeTab === "approvals" && (
554
585
  <div className="h-full overflow-y-auto px-4 py-3">
555
586
  <div className="space-y-3">
556
- {pendingApprovals.map((pa) => (
557
- <WorkflowExecutionApprovalCard
558
- key={pa.approval?.toolCallId ?? pa.childAgentExecutionId}
559
- prompt={pa.approval?.message || `Tool "${pa.approval?.toolName}" requires approval`}
560
- toolCallId={pa.approval?.toolCallId ?? ""}
561
- approvers={[]}
562
- timeoutSeconds={0}
563
- onSubmitApproval={onSubmitApproval}
564
- isSubmitting={isSubmittingApproval}
587
+ {pendingApprovals && onSubmitApproval && pendingApprovals.map((pa) => {
588
+ const toolCallId = pa.approval?.toolCallId ?? "";
589
+ return (
590
+ <WorkflowExecutionApprovalCard
591
+ key={toolCallId || pa.childAgentExecutionId}
592
+ prompt={pa.approval?.message || `Tool "${pa.approval?.toolName}" requires approval`}
593
+ toolCallId={toolCallId}
594
+ approvers={[]}
595
+ timeoutSeconds={0}
596
+ onSubmitApproval={onSubmitApproval}
597
+ isSubmitting={approvalSubmittingToolCallIds.has(toolCallId)}
598
+ error={approvalErrorsByToolCallId.get(toolCallId) ?? null}
599
+ />
600
+ );
601
+ })}
602
+ {pendingFileReviews && pendingFileReviews.length > 0 && (
603
+ <WorkflowFileReviewList
604
+ pendingFileReviews={pendingFileReviews}
605
+ onSubmitFileDecision={onSubmitFileDecision}
606
+ submittingDecisionKeys={fileDecisionSubmittingKeys}
607
+ decisionErrors={fileDecisionErrorsByKey}
608
+ onNavigateToAgentExecution={onNavigateToAgentExecution}
565
609
  />
566
- ))}
610
+ )}
567
611
  </div>
568
612
  </div>
569
613
  )}
@@ -0,0 +1,152 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import {
5
+ FileChangeSetStatus,
6
+ type FileDecisionAction,
7
+ } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
8
+ import type { WorkflowPendingFileReview } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
9
+ import { displayFileChangeSets } from "@stigmer/sdk";
10
+ import { cn } from "@stigmer/theme";
11
+ import { FileReviewCard } from "../execution/FileReviewCard";
12
+ import type { FileDecisionOptions } from "../execution/useFileReview";
13
+ import { useExecutionStream } from "../execution/useExecutionStream";
14
+
15
+ /**
16
+ * Submit handler for a workflow-level file decision, matching
17
+ * `useWorkflowExecutionActions().submitFileDecision`. The child id is bound per
18
+ * card so the surface only needs the card-level `(action, options)`.
19
+ */
20
+ export type WorkflowFileDecisionSubmit = (
21
+ childAgentExecutionId: string,
22
+ changeSetId: string,
23
+ action: FileDecisionAction,
24
+ options?: FileDecisionOptions,
25
+ ) => void;
26
+
27
+ /** Props for {@link WorkflowFileReviewList}. */
28
+ export interface WorkflowFileReviewListProps {
29
+ /**
30
+ * The parent workflow's surfaced file-review references
31
+ * (`status.pending_file_reviews`) — one entry per child agent execution.
32
+ */
33
+ readonly pendingFileReviews: readonly WorkflowPendingFileReview[];
34
+ /** Forwards a decision to the child (see {@link WorkflowFileDecisionSubmit}). */
35
+ readonly onSubmitFileDecision: WorkflowFileDecisionSubmit;
36
+ /** In-flight decision keys (pass `fileDecisionSubmittingKeys` straight through). */
37
+ readonly submittingDecisionKeys?: ReadonlySet<string>;
38
+ /** Per-decision failures (pass `fileDecisionErrorsByKey` straight through). */
39
+ readonly decisionErrors?: ReadonlyMap<string, Error>;
40
+ /**
41
+ * Optional deep-link to open the child agent execution in its own view. When
42
+ * provided, each child's review renders a "View agent execution" affordance.
43
+ * Routing is the host's responsibility (DD-004).
44
+ */
45
+ readonly onNavigateToAgentExecution?: (agentExecutionId: string) => void;
46
+ /** Additional CSS class names for the root container. */
47
+ readonly className?: string;
48
+ }
49
+
50
+ /**
51
+ * Renders the child agent file reviews surfaced on a parent WorkflowExecution.
52
+ *
53
+ * Reference-only, derive-from-child: the parent status carries only
54
+ * {@link WorkflowPendingFileReview} references (child id + change set ids). This
55
+ * list mounts one {@link WorkflowChildFileReview} per referenced child, which
56
+ * streams that child's execution and reuses the agent-level {@link FileReviewCard}
57
+ * to render the actual diff — the heavy content stays single-sourced on the child.
58
+ * A decision is forwarded to the child via `WorkflowExecution.submitFileDecision`.
59
+ *
60
+ * This is the file-review sibling of the workflow tool-approval surface
61
+ * (`WorkflowExecutionApprovalCard`). Empty `pendingFileReviews` renders nothing.
62
+ */
63
+ export function WorkflowFileReviewList({
64
+ pendingFileReviews,
65
+ onSubmitFileDecision,
66
+ submittingDecisionKeys,
67
+ decisionErrors,
68
+ onNavigateToAgentExecution,
69
+ className,
70
+ }: WorkflowFileReviewListProps): React.ReactElement | null {
71
+ if (pendingFileReviews.length === 0) return null;
72
+
73
+ return (
74
+ <div className={cn("stgm space-y-3", className)}>
75
+ {pendingFileReviews.map((ref) => (
76
+ <WorkflowChildFileReview
77
+ key={ref.childAgentExecutionId}
78
+ reference={ref}
79
+ onSubmitFileDecision={onSubmitFileDecision}
80
+ submittingDecisionKeys={submittingDecisionKeys}
81
+ decisionErrors={decisionErrors}
82
+ onNavigateToAgentExecution={onNavigateToAgentExecution}
83
+ />
84
+ ))}
85
+ </div>
86
+ );
87
+ }
88
+
89
+ interface WorkflowChildFileReviewProps {
90
+ readonly reference: WorkflowPendingFileReview;
91
+ readonly onSubmitFileDecision: WorkflowFileDecisionSubmit;
92
+ readonly submittingDecisionKeys?: ReadonlySet<string>;
93
+ readonly decisionErrors?: ReadonlyMap<string, Error>;
94
+ readonly onNavigateToAgentExecution?: (agentExecutionId: string) => void;
95
+ }
96
+
97
+ /**
98
+ * Streams a single referenced child agent execution and renders its
99
+ * AWAITING_REVIEW change sets (intersected with the parent's surfaced reference,
100
+ * so a decision always targets a gate the workflow has actually surfaced).
101
+ */
102
+ function WorkflowChildFileReview({
103
+ reference,
104
+ onSubmitFileDecision,
105
+ submittingDecisionKeys,
106
+ decisionErrors,
107
+ onNavigateToAgentExecution,
108
+ }: WorkflowChildFileReviewProps): React.ReactElement | null {
109
+ const childId = reference.childAgentExecutionId;
110
+ const { execution } = useExecutionStream(childId);
111
+
112
+ const changeSets = useMemo(() => {
113
+ const referenced = new Set(reference.changeSetId);
114
+ return displayFileChangeSets(execution?.status).filter(
115
+ (cs) =>
116
+ cs.status === FileChangeSetStatus.AWAITING_REVIEW &&
117
+ referenced.has(cs.id) &&
118
+ cs.changes.length > 0,
119
+ );
120
+ }, [execution?.status, reference.changeSetId]);
121
+
122
+ // Nothing to show yet (child still loading, or its projection has not caught up
123
+ // to the reference). The parent list already signals "review pending" for this
124
+ // child by virtue of the reference existing.
125
+ if (changeSets.length === 0) return null;
126
+
127
+ return (
128
+ <div className="space-y-2">
129
+ {onNavigateToAgentExecution && (
130
+ <button
131
+ type="button"
132
+ onClick={() => onNavigateToAgentExecution(childId)}
133
+ className="text-xs text-muted-foreground hover:text-foreground underline underline-offset-2"
134
+ >
135
+ View agent execution
136
+ </button>
137
+ )}
138
+ {changeSets.map((changeSet) => (
139
+ <FileReviewCard
140
+ key={changeSet.id}
141
+ fileChangeSet={changeSet}
142
+ interactive
143
+ submittingDecisionKeys={submittingDecisionKeys}
144
+ decisionErrors={decisionErrors}
145
+ onSubmit={(action, options) =>
146
+ onSubmitFileDecision(childId, changeSet.id, action, options)
147
+ }
148
+ />
149
+ ))}
150
+ </div>
151
+ );
152
+ }
@@ -4,6 +4,8 @@ import { memo, useCallback, useEffect, useMemo, useState } from "react";
4
4
  import Markdown from "react-markdown";
5
5
  import { cn } from "@stigmer/theme";
6
6
  import { MARKDOWN_COMPONENTS, REMARK_PLUGINS } from "../internal/markdown-components";
7
+ import { DecisionButton, type DecisionVariant } from "../internal/DecisionButton";
8
+ import { InCardDecisionError } from "../internal/InCardDecisionError";
7
9
 
8
10
  /** Outcome descriptor for UI rendering. */
9
11
  export interface TaskOutcome {
@@ -44,8 +46,15 @@ export interface WorkflowTaskApprovalCardProps {
44
46
  formData?: Record<string, unknown>,
45
47
  comment?: string,
46
48
  ) => Promise<unknown>;
47
- /** True while the submission RPC is in flight. */
49
+ /** True while this gate's submission RPC is in flight. */
48
50
  readonly isSubmitting: boolean;
51
+ /**
52
+ * This gate's last failed decision, or `null`. Surfaced in-card beside the
53
+ * outcome buttons (via the shared {@link InCardDecisionError}) — supply
54
+ * {@link useWorkflowExecutionActions}'s `taskApprovalErrorsByTaskName` for
55
+ * this `taskName`.
56
+ */
57
+ readonly error?: Error | null;
49
58
  /** Additional CSS class names for the root container. */
50
59
  readonly className?: string;
51
60
  }
@@ -92,6 +101,7 @@ export const WorkflowTaskApprovalCard = memo(function WorkflowTaskApprovalCard({
92
101
  formSchema,
93
102
  onSubmit,
94
103
  isSubmitting,
104
+ error,
95
105
  className,
96
106
  }: WorkflowTaskApprovalCardProps) {
97
107
  const outcomes = outcomesProp.length > 0 ? outcomesProp : DEFAULT_OUTCOMES;
@@ -134,8 +144,10 @@ export const WorkflowTaskApprovalCard = memo(function WorkflowTaskApprovalCard({
134
144
  role="form"
135
145
  aria-label={`Approval decision for ${taskName}`}
136
146
  aria-busy={isSubmitting}
147
+ // Neutral card + 2px warning left accent — the quiet Cursor-grade chrome
148
+ // shared with ApprovalCard. (Replaces the old amber `bg-warning/5` fill.)
137
149
  className={cn(
138
- "mt-2 rounded-lg border border-warning/30 bg-warning/5 p-3",
150
+ "mt-2 rounded-lg border border-border-prominent border-l-2 border-l-warning p-3",
139
151
  className,
140
152
  )}
141
153
  >
@@ -198,75 +210,34 @@ export const WorkflowTaskApprovalCard = memo(function WorkflowTaskApprovalCard({
198
210
 
199
211
  <div className="flex flex-wrap gap-2">
200
212
  {outcomes.map((outcome, index) => (
201
- <OutcomeButton
213
+ <DecisionButton
202
214
  key={outcome.name}
203
215
  label={outcome.label || capitalize(outcome.name)}
204
- outcomeName={outcome.name}
205
216
  variant={resolveVariant(outcome.name, index, outcomes.length)}
217
+ onClick={() => handleSubmit(outcome.name)}
206
218
  isActive={activeOutcome === outcome.name}
207
219
  isSubmitting={isSubmitting}
208
- onClick={handleSubmit}
209
220
  />
210
221
  ))}
211
222
  </div>
212
- </div>
213
- );
214
- });
215
-
216
- // ---------------------------------------------------------------------------
217
- // Internal sub-components
218
- // ---------------------------------------------------------------------------
219
223
 
220
- type ButtonVariant = "primary" | "destructive" | "secondary";
221
-
222
- function OutcomeButton({
223
- label,
224
- outcomeName,
225
- variant,
226
- isActive,
227
- isSubmitting,
228
- onClick,
229
- }: {
230
- readonly label: string;
231
- readonly outcomeName: string;
232
- readonly variant: ButtonVariant;
233
- readonly isActive: boolean;
234
- readonly isSubmitting: boolean;
235
- readonly onClick: (name: string) => void;
236
- }) {
237
- const variantClasses: Record<ButtonVariant, string> = {
238
- primary: cn(
239
- "bg-success text-success-foreground hover:bg-success/90",
240
- "disabled:bg-success/50 disabled:text-success-foreground/70",
241
- ),
242
- destructive: cn(
243
- "bg-destructive text-destructive-foreground hover:bg-destructive-hover",
244
- "disabled:bg-destructive-subtle0 disabled:text-destructive-foreground/70",
245
- ),
246
- secondary: cn(
247
- "border border-border bg-background text-foreground hover:bg-muted",
248
- "disabled:bg-muted-faint disabled:text-muted-foreground-faint",
249
- ),
250
- };
251
-
252
- return (
253
- <button
254
- type="button"
255
- disabled={isSubmitting}
256
- onClick={() => onClick(outcomeName)}
257
- aria-label={label}
258
- className={cn(
259
- "inline-flex items-center justify-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors",
260
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
261
- "disabled:cursor-not-allowed",
262
- variantClasses[variant],
224
+ {/* A failed decision surfaces HERE, beside this gate's outcome buttons —
225
+ not in the viewer's lifecycle banner. A workflow can hold many gates at
226
+ once, so a failure must name the one it belongs to; the optimistic
227
+ spinner has already reverted, and this explains the snap-back. Shared
228
+ with the agent ApprovalCard / FileReviewCard via InCardDecisionError. */}
229
+ {error && (
230
+ <div className="mt-2">
231
+ <InCardDecisionError
232
+ error={error}
233
+ leadIn="submit decision"
234
+ cursorTarget="wf-task-approval-error"
235
+ />
236
+ </div>
263
237
  )}
264
- >
265
- {isActive && isSubmitting ? <SpinnerIcon /> : null}
266
- {label}
267
- </button>
238
+ </div>
268
239
  );
269
- }
240
+ });
270
241
 
271
242
  // ---------------------------------------------------------------------------
272
243
  // Helpers
@@ -296,42 +267,23 @@ function resolveVariant(
296
267
  outcomeName: string,
297
268
  index: number,
298
269
  total: number,
299
- ): ButtonVariant {
270
+ ): DecisionVariant {
271
+ // The first outcome is the recommended action (the quiet neutral chip).
300
272
  if (index === 0) return "primary";
301
273
 
302
274
  const lower = outcomeName.toLowerCase();
303
275
  if (lower === "reject" || lower === "deny" || lower.includes("reject")) {
304
- return "destructive";
276
+ return "danger";
305
277
  }
306
278
 
279
+ // In a simple two-outcome form, the trailing outcome is the negative one.
307
280
  if (index === total - 1 && total === 2) {
308
- return "destructive";
281
+ return "danger";
309
282
  }
310
283
 
311
- return "secondary";
284
+ return "ghost";
312
285
  }
313
286
 
314
287
  function capitalize(s: string): string {
315
288
  return s.charAt(0).toUpperCase() + s.slice(1);
316
289
  }
317
-
318
- // ---------------------------------------------------------------------------
319
- // Inline SVG icons
320
- // ---------------------------------------------------------------------------
321
-
322
- function SpinnerIcon() {
323
- return (
324
- <svg
325
- width="10"
326
- height="10"
327
- viewBox="0 0 12 12"
328
- fill="none"
329
- stroke="currentColor"
330
- strokeWidth="1.5"
331
- className="animate-spin"
332
- aria-hidden="true"
333
- >
334
- <path d="M6 1.5A4.5 4.5 0 1 1 1.5 6" strokeLinecap="round" />
335
- </svg>
336
- );
337
- }
@@ -88,7 +88,6 @@ describe("ExecutionInspector — approval gating", () => {
88
88
  events={[approvalRequested]}
89
89
  taskStates={new Map([[TASK, makeTaskState(TASK, "waiting_approval")]])}
90
90
  onSubmitTaskApproval={vi.fn().mockResolvedValue(undefined)}
91
- isSubmittingTaskApproval={false}
92
91
  />,
93
92
  );
94
93
 
@@ -97,6 +96,36 @@ describe("ExecutionInspector — approval gating", () => {
97
96
  expect(screen.getByRole("button", { name: "Reject" })).toBeTruthy();
98
97
  });
99
98
 
99
+ it("surfaces this gate's keyed failure in-card, beside the outcome buttons", () => {
100
+ render(
101
+ <ExecutionInspector
102
+ selectedTaskName={TASK}
103
+ events={[approvalRequested]}
104
+ taskStates={new Map([[TASK, makeTaskState(TASK, "waiting_approval")]])}
105
+ onSubmitTaskApproval={vi.fn().mockResolvedValue(undefined)}
106
+ taskApprovalErrorsByTaskName={new Map([[TASK, new Error("signal failed")]])}
107
+ />,
108
+ );
109
+
110
+ expect(screen.getByText(/Couldn.t submit decision — signal failed/)).toBeTruthy();
111
+ });
112
+
113
+ it("marks only this gate's buttons as submitting via the keyed set", () => {
114
+ render(
115
+ <ExecutionInspector
116
+ selectedTaskName={TASK}
117
+ events={[approvalRequested]}
118
+ taskStates={new Map([[TASK, makeTaskState(TASK, "waiting_approval")]])}
119
+ onSubmitTaskApproval={vi.fn().mockResolvedValue(undefined)}
120
+ taskApprovalSubmittingTaskNames={new Set([TASK])}
121
+ />,
122
+ );
123
+
124
+ expect(
125
+ (screen.getByRole("button", { name: "Approve Plan" }) as HTMLButtonElement).disabled,
126
+ ).toBe(true);
127
+ });
128
+
100
129
  it("renders the read-only decision summary once the gate is resolved", () => {
101
130
  render(
102
131
  <ExecutionInspector
@@ -111,7 +140,6 @@ describe("ExecutionInspector — approval gating", () => {
111
140
  }),
112
141
  ]}
113
142
  onSubmitTaskApproval={vi.fn().mockResolvedValue(undefined)}
114
- isSubmittingTaskApproval={false}
115
143
  />,
116
144
  );
117
145