@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
@@ -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
 
@@ -0,0 +1,129 @@
1
+ import { describe, it, expect, vi, afterEach } from "vitest";
2
+ import { render, screen, fireEvent, cleanup } from "@testing-library/react";
3
+ import { WorkflowExecutionApprovalCard } from "../WorkflowExecutionApprovalCard";
4
+
5
+ // ApprovalAction enum: 1 = APPROVE, 3 = REJECT.
6
+ const APPROVE = 1;
7
+ const REJECT = 3;
8
+
9
+ const defaultProps = {
10
+ prompt: "The agent wants to delete /tmp/x. Approve?",
11
+ toolCallId: "tc-1",
12
+ approvers: [] as readonly string[],
13
+ timeoutSeconds: 0,
14
+ onSubmitApproval: vi.fn().mockResolvedValue(undefined),
15
+ isSubmitting: false,
16
+ };
17
+
18
+ afterEach(cleanup);
19
+
20
+ describe("WorkflowExecutionApprovalCard", () => {
21
+ describe("content", () => {
22
+ it("renders the prompt", () => {
23
+ render(<WorkflowExecutionApprovalCard {...defaultProps} />);
24
+ expect(screen.getByText(defaultProps.prompt)).toBeTruthy();
25
+ });
26
+
27
+ it("lists approvers and the timeout when provided", () => {
28
+ render(
29
+ <WorkflowExecutionApprovalCard
30
+ {...defaultProps}
31
+ approvers={["alice", "bob"]}
32
+ timeoutSeconds={120}
33
+ />,
34
+ );
35
+ expect(screen.getByText("Approvers: alice, bob")).toBeTruthy();
36
+ expect(screen.getByText("Expires in 2m")).toBeTruthy();
37
+ });
38
+ });
39
+
40
+ describe("submission", () => {
41
+ it("submits APPROVE with the comment", () => {
42
+ const onSubmitApproval = vi.fn().mockResolvedValue(undefined);
43
+ render(
44
+ <WorkflowExecutionApprovalCard {...defaultProps} onSubmitApproval={onSubmitApproval} />,
45
+ );
46
+ fireEvent.change(screen.getByPlaceholderText("Comment (optional)"), {
47
+ target: { value: "ok" },
48
+ });
49
+ fireEvent.click(screen.getByRole("button", { name: "Approve" }));
50
+ expect(onSubmitApproval).toHaveBeenCalledWith("tc-1", APPROVE, "ok");
51
+ });
52
+
53
+ it("submits REJECT with no comment as undefined", () => {
54
+ const onSubmitApproval = vi.fn().mockResolvedValue(undefined);
55
+ render(
56
+ <WorkflowExecutionApprovalCard {...defaultProps} onSubmitApproval={onSubmitApproval} />,
57
+ );
58
+ fireEvent.click(screen.getByRole("button", { name: "Reject" }));
59
+ expect(onSubmitApproval).toHaveBeenCalledWith("tc-1", REJECT, undefined);
60
+ });
61
+ });
62
+
63
+ describe("quiet chrome & buttons (shared DecisionButton)", () => {
64
+ it("uses neutral card chrome + warning accent, never the old amber fill", () => {
65
+ render(<WorkflowExecutionApprovalCard {...defaultProps} />);
66
+ const card = screen.getByRole("alert");
67
+ expect(card.className).toContain("border-border-prominent");
68
+ expect(card.className).toContain("border-l-warning");
69
+ expect(card.className).not.toContain("bg-warning");
70
+ });
71
+
72
+ it("Approve is the neutral chip (not loud green); Reject is a quiet danger ghost (not a red fill)", () => {
73
+ render(<WorkflowExecutionApprovalCard {...defaultProps} />);
74
+ const approve = screen.getByRole("button", { name: "Approve" });
75
+ expect(approve.className).toContain("bg-accent");
76
+ expect(approve.className).not.toContain("bg-success");
77
+
78
+ const reject = screen.getByRole("button", { name: "Reject" });
79
+ expect(reject.className).toContain("hover:text-destructive");
80
+ expect(reject.className).not.toContain("bg-destructive text-destructive-foreground");
81
+ // No opacity-modifier debt (e.g. the old hover:bg-destructive/10).
82
+ expect(reject.className).not.toMatch(/\/\d+(?:\s|$)/);
83
+ });
84
+ });
85
+
86
+ describe("loading state", () => {
87
+ it("disables both buttons while submitting", () => {
88
+ render(<WorkflowExecutionApprovalCard {...defaultProps} isSubmitting />);
89
+ expect(
90
+ (screen.getByRole("button", { name: "Approve" }) as HTMLButtonElement).disabled,
91
+ ).toBe(true);
92
+ expect(
93
+ (screen.getByRole("button", { name: "Reject" }) as HTMLButtonElement).disabled,
94
+ ).toBe(true);
95
+ });
96
+
97
+ it("shows the spinner on the clicked button during submission", () => {
98
+ const { rerender } = render(<WorkflowExecutionApprovalCard {...defaultProps} />);
99
+ fireEvent.click(screen.getByRole("button", { name: "Reject" }));
100
+ rerender(<WorkflowExecutionApprovalCard {...defaultProps} isSubmitting />);
101
+
102
+ expect(
103
+ screen.getByRole("button", { name: "Reject" }).querySelector(".animate-spin"),
104
+ ).toBeTruthy();
105
+ expect(
106
+ screen.getByRole("button", { name: "Approve" }).querySelector(".animate-spin"),
107
+ ).toBeNull();
108
+ });
109
+ });
110
+
111
+ describe("in-card decision error", () => {
112
+ it("renders the failure notice beside the actions when error is set", () => {
113
+ render(
114
+ <WorkflowExecutionApprovalCard
115
+ {...defaultProps}
116
+ error={new Error("gate already resolved")}
117
+ />,
118
+ );
119
+ expect(
120
+ screen.getByText(/Couldn.t submit decision — gate already resolved/),
121
+ ).toBeTruthy();
122
+ });
123
+
124
+ it("renders no failure notice when error is null", () => {
125
+ render(<WorkflowExecutionApprovalCard {...defaultProps} error={null} />);
126
+ expect(screen.queryByText(/Couldn.t submit decision/)).toBeNull();
127
+ });
128
+ });
129
+ });
@@ -0,0 +1,129 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { render, screen, fireEvent, cleanup } from "@testing-library/react";
3
+ import { FileChangeSetStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
4
+
5
+ // Mock the child execution stream: each child id maps to a canned execution.
6
+ const mockExecutions: Record<string, any> = {};
7
+ vi.mock("../../execution/useExecutionStream", () => ({
8
+ useExecutionStream: (id: string | null) => ({
9
+ execution: id ? (mockExecutions[id] ?? null) : null,
10
+ }),
11
+ }));
12
+
13
+ // Stub FileReviewCard to a minimal control that surfaces its wiring, so the test
14
+ // exercises the container's mapping/forwarding — not the card's diff rendering.
15
+ vi.mock("../../execution/FileReviewCard", () => ({
16
+ FileReviewCard: (props: any) => (
17
+ <button
18
+ data-testid={`card-${props.fileChangeSet.id}`}
19
+ onClick={() => props.onSubmit(1 /* APPROVE */, { expectedDigest: "d" })}
20
+ >
21
+ review {props.fileChangeSet.id}
22
+ </button>
23
+ ),
24
+ }));
25
+
26
+ import { WorkflowFileReviewList } from "../WorkflowFileReviewList";
27
+
28
+ function awaitingSet(id: string) {
29
+ return { id, status: FileChangeSetStatus.AWAITING_REVIEW, changes: [{ id: "fc" }] };
30
+ }
31
+
32
+ beforeEach(() => {
33
+ for (const k of Object.keys(mockExecutions)) delete mockExecutions[k];
34
+ });
35
+
36
+ afterEach(() => {
37
+ cleanup();
38
+ });
39
+
40
+ describe("WorkflowFileReviewList", () => {
41
+ it("renders nothing when there are no references", () => {
42
+ const { container } = render(
43
+ <WorkflowFileReviewList pendingFileReviews={[]} onSubmitFileDecision={vi.fn()} />,
44
+ );
45
+ expect(container.firstChild).toBeNull();
46
+ });
47
+
48
+ it("renders a card for a referenced AWAITING_REVIEW set and forwards the decision with child routing", () => {
49
+ mockExecutions["aex-1"] = { status: { fileChangeSets: [awaitingSet("fcs-1")] } };
50
+ const onSubmit = vi.fn();
51
+
52
+ render(
53
+ <WorkflowFileReviewList
54
+ pendingFileReviews={[{ childAgentExecutionId: "aex-1", changeSetId: ["fcs-1"] } as any]}
55
+ onSubmitFileDecision={onSubmit}
56
+ />,
57
+ );
58
+
59
+ const card = screen.getByTestId("card-fcs-1");
60
+ fireEvent.click(card);
61
+ expect(onSubmit).toHaveBeenCalledWith("aex-1", "fcs-1", 1, { expectedDigest: "d" });
62
+ });
63
+
64
+ it("does not render a set that is not in the surfaced reference", () => {
65
+ // Child has an AWAITING set fcs-2, but the parent only surfaced fcs-1.
66
+ mockExecutions["aex-1"] = {
67
+ status: { fileChangeSets: [awaitingSet("fcs-1"), awaitingSet("fcs-2")] },
68
+ };
69
+
70
+ render(
71
+ <WorkflowFileReviewList
72
+ pendingFileReviews={[{ childAgentExecutionId: "aex-1", changeSetId: ["fcs-1"] } as any]}
73
+ onSubmitFileDecision={vi.fn()}
74
+ />,
75
+ );
76
+
77
+ expect(screen.getByTestId("card-fcs-1")).toBeTruthy();
78
+ expect(screen.queryByTestId("card-fcs-2")).toBeNull();
79
+ });
80
+
81
+ it("does not render a referenced set that is no longer AWAITING_REVIEW", () => {
82
+ mockExecutions["aex-1"] = {
83
+ status: { fileChangeSets: [{ id: "fcs-1", status: FileChangeSetStatus.DECIDED, changes: [{ id: "fc" }] }] },
84
+ };
85
+
86
+ render(
87
+ <WorkflowFileReviewList
88
+ pendingFileReviews={[{ childAgentExecutionId: "aex-1", changeSetId: ["fcs-1"] } as any]}
89
+ onSubmitFileDecision={vi.fn()}
90
+ />,
91
+ );
92
+
93
+ expect(screen.queryByTestId("card-fcs-1")).toBeNull();
94
+ });
95
+
96
+ it("renders reviews for multiple parallel children independently", () => {
97
+ mockExecutions["aex-1"] = { status: { fileChangeSets: [awaitingSet("fcs-1")] } };
98
+ mockExecutions["aex-2"] = { status: { fileChangeSets: [awaitingSet("fcs-2")] } };
99
+
100
+ render(
101
+ <WorkflowFileReviewList
102
+ pendingFileReviews={[
103
+ { childAgentExecutionId: "aex-1", changeSetId: ["fcs-1"] } as any,
104
+ { childAgentExecutionId: "aex-2", changeSetId: ["fcs-2"] } as any,
105
+ ]}
106
+ onSubmitFileDecision={vi.fn()}
107
+ />,
108
+ );
109
+
110
+ expect(screen.getByTestId("card-fcs-1")).toBeTruthy();
111
+ expect(screen.getByTestId("card-fcs-2")).toBeTruthy();
112
+ });
113
+
114
+ it("shows a deep-link affordance when onNavigateToAgentExecution is provided", () => {
115
+ mockExecutions["aex-1"] = { status: { fileChangeSets: [awaitingSet("fcs-1")] } };
116
+ const onNavigate = vi.fn();
117
+
118
+ render(
119
+ <WorkflowFileReviewList
120
+ pendingFileReviews={[{ childAgentExecutionId: "aex-1", changeSetId: ["fcs-1"] } as any]}
121
+ onSubmitFileDecision={vi.fn()}
122
+ onNavigateToAgentExecution={onNavigate}
123
+ />,
124
+ );
125
+
126
+ fireEvent.click(screen.getByText("View agent execution"));
127
+ expect(onNavigate).toHaveBeenCalledWith("aex-1");
128
+ });
129
+ });
@@ -37,14 +37,19 @@ describe("WorkflowTaskApprovalCard", () => {
37
37
  });
38
38
  });
39
39
 
40
- describe("button variants", () => {
41
- it("first outcome gets primary variant (success colors)", () => {
40
+ // The buttons now share the quiet, token-only DecisionButton primitive:
41
+ // first outcome = neutral chip (`primary`), reject/deny or the trailing of two
42
+ // = ghost-`danger`, middle outcomes = neutral `ghost`. No loud success/destructive
43
+ // fills, and no `bg-token/NN` opacity modifiers.
44
+ describe("button variants (quiet hierarchy)", () => {
45
+ it("first outcome is the neutral chip (primary), never the loud success green", () => {
42
46
  render(<WorkflowTaskApprovalCard {...defaultProps} />);
43
47
  const btn = screen.getByRole("button", { name: "Approve" });
44
- expect(btn.className).toContain("bg-success");
48
+ expect(btn.className).toContain("bg-accent");
49
+ expect(btn.className).not.toContain("bg-success");
45
50
  });
46
51
 
47
- it('outcome named "reject" gets destructive variant', () => {
52
+ it('outcome named "reject" is a quiet danger ghost (no red fill)', () => {
48
53
  const outcomes: TaskOutcome[] = [
49
54
  { name: "approve", label: "Approve" },
50
55
  { name: "reject", label: "Reject" },
@@ -52,10 +57,11 @@ describe("WorkflowTaskApprovalCard", () => {
52
57
  ];
53
58
  render(<WorkflowTaskApprovalCard {...defaultProps} outcomes={outcomes} />);
54
59
  const btn = screen.getByRole("button", { name: "Reject" });
55
- expect(btn.className).toContain("bg-destructive");
60
+ expect(btn.className).toContain("hover:text-destructive");
61
+ expect(btn.className).not.toContain("bg-destructive text-destructive-foreground");
56
62
  });
57
63
 
58
- it('outcome named "deny" gets destructive variant', () => {
64
+ it('outcome named "deny" is a quiet danger ghost (no red fill)', () => {
59
65
  const outcomes: TaskOutcome[] = [
60
66
  { name: "approve", label: "Approve" },
61
67
  { name: "deny", label: "Deny" },
@@ -63,20 +69,21 @@ describe("WorkflowTaskApprovalCard", () => {
63
69
  ];
64
70
  render(<WorkflowTaskApprovalCard {...defaultProps} outcomes={outcomes} />);
65
71
  const btn = screen.getByRole("button", { name: "Deny" });
66
- expect(btn.className).toContain("bg-destructive");
72
+ expect(btn.className).toContain("hover:text-destructive");
73
+ expect(btn.className).not.toContain("bg-destructive text-destructive-foreground");
67
74
  });
68
75
 
69
- it("second of two outcomes gets destructive variant (binary fallback)", () => {
76
+ it("second of two outcomes is the danger ghost (binary fallback)", () => {
70
77
  const outcomes: TaskOutcome[] = [
71
78
  { name: "accept", label: "Accept" },
72
79
  { name: "decline", label: "Decline" },
73
80
  ];
74
81
  render(<WorkflowTaskApprovalCard {...defaultProps} outcomes={outcomes} />);
75
82
  const btn = screen.getByRole("button", { name: "Decline" });
76
- expect(btn.className).toContain("bg-destructive");
83
+ expect(btn.className).toContain("hover:text-destructive");
77
84
  });
78
85
 
79
- it("middle outcomes in 3+ get secondary variant", () => {
86
+ it("middle outcomes in 3+ are neutral ghosts (no resting fill)", () => {
80
87
  const outcomes: TaskOutcome[] = [
81
88
  { name: "approve", label: "Approve" },
82
89
  { name: "defer", label: "Defer" },
@@ -84,7 +91,16 @@ describe("WorkflowTaskApprovalCard", () => {
84
91
  ];
85
92
  render(<WorkflowTaskApprovalCard {...defaultProps} outcomes={outcomes} />);
86
93
  const btn = screen.getByRole("button", { name: "Defer" });
87
- expect(btn.className).toContain("bg-background");
94
+ expect(btn.className).toContain("text-muted-foreground");
95
+ expect(btn.className).not.toMatch(/(?:^|\s)bg-/);
96
+ });
97
+
98
+ it("the card uses neutral chrome + warning accent, never the old amber fill", () => {
99
+ render(<WorkflowTaskApprovalCard {...defaultProps} />);
100
+ const card = screen.getByRole("form");
101
+ expect(card.className).toContain("border-border-prominent");
102
+ expect(card.className).toContain("border-l-warning");
103
+ expect(card.className).not.toContain("bg-warning");
88
104
  });
89
105
  });
90
106
 
@@ -232,6 +248,22 @@ describe("WorkflowTaskApprovalCard", () => {
232
248
  });
233
249
  });
234
250
 
251
+ describe("in-card decision error", () => {
252
+ it("renders the failure notice beside the outcomes when error is set", () => {
253
+ render(
254
+ <WorkflowTaskApprovalCard {...defaultProps} error={new Error("signal failed")} />,
255
+ );
256
+ expect(
257
+ screen.getByText(/Couldn.t submit decision — signal failed/),
258
+ ).toBeTruthy();
259
+ });
260
+
261
+ it("renders no failure notice when error is null", () => {
262
+ render(<WorkflowTaskApprovalCard {...defaultProps} error={null} />);
263
+ expect(screen.queryByText(/Couldn.t submit decision/)).toBeNull();
264
+ });
265
+ });
266
+
235
267
  describe("accessibility", () => {
236
268
  it('root element has role="form"', () => {
237
269
  render(<WorkflowTaskApprovalCard {...defaultProps} />);