@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
@@ -1,15 +1,165 @@
1
1
  import { describe, it, expect, afterEach, vi } from "vitest";
2
- import { render, cleanup, fireEvent } from "@testing-library/react";
2
+ import { render, screen, cleanup, fireEvent } from "@testing-library/react";
3
3
  import { create } from "@bufbuild/protobuf";
4
4
  import { PendingApprovalSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/approval_pb";
5
- import { ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
5
+ import {
6
+ ApprovalAction,
7
+ ApprovalPolicySource,
8
+ } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
6
9
  import { ToolKind } from "@stigmer/sdk";
7
10
  import { ApprovalCard } from "../ApprovalCard";
8
11
 
12
+ // Under apply-then-review the deny-gate carries no captured `file_changes`
13
+ // (message.proto field 14 was removed in Phase 5 Slice 4); the gate renders the
14
+ // proposed change from the tool args. Captured file review renders via
15
+ // FileReviewCard, tested separately.
16
+
9
17
  afterEach(cleanup);
10
18
 
11
19
  const noop = () => {};
12
20
 
21
+ // ---------------------------------------------------------------------------
22
+ // Tool classification (ToolArgsView fallback path)
23
+ // ---------------------------------------------------------------------------
24
+
25
+ describe("ApprovalCard chrome", () => {
26
+ it("renders neutral Cursor-style chrome with a restrained warning accent (no amber fill)", () => {
27
+ const approval = create(PendingApprovalSchema, {
28
+ toolCallId: "tc-chrome",
29
+ toolName: "write_file",
30
+ toolKind: ToolKind.FILE_EDIT,
31
+ argsPreview: '{"path":"src/x.ts","contents":"x"}',
32
+ });
33
+
34
+ render(<ApprovalCard pendingApproval={approval} onSubmit={noop} />);
35
+
36
+ const card = screen.getByRole("alert");
37
+ // Visible neutral line + a restrained 2px warning left accent — never the
38
+ // old amber background fill. (Class presence only; that the line actually
39
+ // RENDERS is proven by the layer-invariant + e2e computed-style guards —
40
+ // happy-dom cannot resolve `@layer`.)
41
+ expect(card.className).toContain("border-border-prominent");
42
+ expect(card.className).toContain("border-l-warning");
43
+ expect(card.className).not.toContain("bg-warning");
44
+ });
45
+
46
+ it("keeps a destructive red accent for a delete approval (hard safety signal)", () => {
47
+ const approval = create(PendingApprovalSchema, {
48
+ toolCallId: "tc-chrome-del",
49
+ toolName: "delete_file",
50
+ argsPreview: '{"path":"/tmp/x"}',
51
+ });
52
+
53
+ render(<ApprovalCard pendingApproval={approval} onSubmit={noop} />);
54
+
55
+ const card = screen.getByRole("alert");
56
+ expect(card.className).toContain("border-l-destructive");
57
+ expect(card.className).not.toContain("bg-destructive-subtle");
58
+ });
59
+
60
+ it("shows a shell gate's command in the body terminal session, once, not in the header", () => {
61
+ // The command is decision-relevant, so it leads the body's terminal session
62
+ // (`$ npm test`) — but the header stays minimal and does not restate it, so
63
+ // the command appears exactly once on the card.
64
+ const approval = create(PendingApprovalSchema, {
65
+ toolCallId: "tc-shell-gate",
66
+ toolName: "shell",
67
+ toolKind: ToolKind.SHELL,
68
+ argsPreview: '{"command":"npm test"}',
69
+ });
70
+
71
+ const { container } = render(
72
+ <ApprovalCard pendingApproval={approval} onSubmit={noop} />,
73
+ );
74
+
75
+ const session = container.querySelector(
76
+ '[data-cursor-target="terminal-session"]',
77
+ );
78
+ expect(session).toBeTruthy();
79
+ expect(session!.textContent).toContain("$ npm test");
80
+
81
+ const occurrences = (container.textContent!.match(/npm test/g) ?? []).length;
82
+ expect(occurrences).toBe(1);
83
+ });
84
+
85
+ it("states on a shell gate that the command's files are covered by the approval (DD-28)", () => {
86
+ // Consent at grant time: approving the command covers its file effects
87
+ // (the approved-command auto-keep), so the card says so up front — the user
88
+ // never discovers a second gate they weren't told about, nor gets one.
89
+ const shellApproval = create(PendingApprovalSchema, {
90
+ toolCallId: "tc-shell-note",
91
+ toolName: "shell",
92
+ toolKind: ToolKind.SHELL,
93
+ argsPreview: '{"command":"seq 1 5000 > big.txt"}',
94
+ });
95
+ render(<ApprovalCard pendingApproval={shellApproval} onSubmit={noop} />);
96
+ expect(screen.getByText(/kept automatically, with no second review/)).toBeTruthy();
97
+ });
98
+
99
+ it("never shows the auto-keep note on a non-shell gate", () => {
100
+ // A file edit's review model is the FileChangeSet, not the command consent —
101
+ // the note would be false there.
102
+ const editApproval = create(PendingApprovalSchema, {
103
+ toolCallId: "tc-edit-no-note",
104
+ toolName: "write_file",
105
+ toolKind: ToolKind.FILE_EDIT,
106
+ argsPreview: '{"path":"src/x.ts","contents":"x"}',
107
+ });
108
+ render(<ApprovalCard pendingApproval={editApproval} onSubmit={noop} />);
109
+ expect(screen.queryByText(/kept automatically/)).toBeNull();
110
+ });
111
+ });
112
+
113
+ describe("ApprovalCard quiet decision buttons", () => {
114
+ // Class-contract assertions (happy-dom can't resolve `@layer`); the RENDERED
115
+ // quiet treatment is proven by the e2e computed-style guard in
116
+ // tool-card-ux.spec.ts. The shared treatment itself lives in DecisionButton.
117
+ function renderGate() {
118
+ const approval = create(PendingApprovalSchema, {
119
+ toolCallId: "tc-quiet",
120
+ toolName: "write_file",
121
+ toolKind: ToolKind.FILE_EDIT,
122
+ argsPreview: '{"path":"src/x.ts","contents":"x"}',
123
+ });
124
+ return render(<ApprovalCard pendingApproval={approval} onSubmit={noop} />);
125
+ }
126
+
127
+ it("Approve is the neutral chip, never the loud success green", () => {
128
+ renderGate();
129
+ const approve = screen.getByRole("button", { name: "Approve" });
130
+ expect(approve.className).toContain("bg-accent");
131
+ expect(approve.className).toContain("border");
132
+ expect(approve.className).not.toContain("bg-success");
133
+ });
134
+
135
+ it("Skip and Reject are quiet ghosts with no resting fill", () => {
136
+ renderGate();
137
+ const skip = screen.getByRole("button", { name: "Skip" });
138
+ const reject = screen.getByRole("button", { name: "Reject" });
139
+ expect(skip.className).toContain("text-muted-foreground");
140
+ expect(skip.className).not.toMatch(/(?:^|\s)bg-/); // hover-only wash, no rest fill
141
+ // Reject reveals its danger affordance on hover/focus, not as a red fill.
142
+ expect(reject.className).toContain("hover:text-destructive");
143
+ expect(reject.className).not.toMatch(/(?:^|\s)bg-/);
144
+ expect(reject.className).not.toContain("bg-destructive text-destructive-foreground");
145
+ });
146
+
147
+ it("demotes Approve-all to the far right (ml-auto) at the lowest weight", () => {
148
+ renderGate();
149
+ const approveAll = screen.getByRole("button", { name: "Approve all file edits" });
150
+ expect(approveAll.className).toContain("ml-auto");
151
+ expect(approveAll.className).toContain("text-muted-foreground");
152
+ });
153
+
154
+ it("uses no `bg-token/NN` opacity modifiers on any decision button", () => {
155
+ renderGate();
156
+ for (const name of ["Approve", "Skip", "Reject", "Approve all file edits"]) {
157
+ const btn = screen.getByRole("button", { name });
158
+ expect(btn.className).not.toMatch(/\/\d+(?:\s|$)/);
159
+ }
160
+ });
161
+ });
162
+
13
163
  describe("ApprovalCard tool classification", () => {
14
164
  it("honors the denormalized wire tool_kind for a name not in the fallback map", () => {
15
165
  // A future/unknown tool name that the name-based resolver cannot classify —
@@ -46,8 +196,110 @@ describe("ApprovalCard tool classification", () => {
46
196
  });
47
197
  });
48
198
 
199
+ describe("ApprovalCard why-gated provenance", () => {
200
+ it("renders the why-gated line from the projected approval_policy_source", () => {
201
+ // The server projects approval_policy_source onto PendingApproval so the card
202
+ // can explain the gate before any decision. AGENT_OVERRIDE → the agent forced
203
+ // this tool to require approval.
204
+ const approval = create(PendingApprovalSchema, {
205
+ toolCallId: "tc-why",
206
+ toolName: "delete_file",
207
+ argsPreview: '{"path":"/tmp/x"}',
208
+ approvalPolicySource: ApprovalPolicySource.AGENT_OVERRIDE,
209
+ });
210
+
211
+ const { getByText } = render(
212
+ <ApprovalCard pendingApproval={approval} onSubmit={noop} />,
213
+ );
214
+
215
+ expect(getByText("required by agent override")).toBeTruthy();
216
+ });
217
+
218
+ it("renders no why-gated line for a legacy approval (UNSPECIFIED source)", () => {
219
+ const approval = create(PendingApprovalSchema, {
220
+ toolCallId: "tc-why-legacy",
221
+ toolName: "delete_file",
222
+ argsPreview: '{"path":"/tmp/x"}',
223
+ // approvalPolicySource left UNSPECIFIED — legacy execution.
224
+ });
225
+
226
+ const { queryByText } = render(
227
+ <ApprovalCard pendingApproval={approval} onSubmit={noop} />,
228
+ );
229
+
230
+ expect(queryByText(/required by/)).toBeNull();
231
+ });
232
+
233
+ it("smart-suppresses the everyday default gate reason (built-in tool policy)", () => {
234
+ // The boring default explains nothing the user does not already infer from
235
+ // the tool — it is noise, so the card hides it.
236
+ const approval = create(PendingApprovalSchema, {
237
+ toolCallId: "tc-why-default",
238
+ toolName: "delete_file",
239
+ argsPreview: '{"path":"/tmp/x"}',
240
+ approvalPolicySource: ApprovalPolicySource.BUILTIN_CATEGORY,
241
+ });
242
+
243
+ const { queryByText } = render(
244
+ <ApprovalCard pendingApproval={approval} onSubmit={noop} />,
245
+ );
246
+
247
+ expect(queryByText(/required by/)).toBeNull();
248
+ });
249
+
250
+ it("surfaces an informative gate reason (destructive tighten)", () => {
251
+ const approval = create(PendingApprovalSchema, {
252
+ toolCallId: "tc-why-destructive",
253
+ toolName: "delete_file",
254
+ argsPreview: '{"path":"/tmp/x"}',
255
+ approvalPolicySource: ApprovalPolicySource.ANNOTATION_DESTRUCTIVE_TIGHTEN,
256
+ });
257
+
258
+ const { getByText } = render(
259
+ <ApprovalCard pendingApproval={approval} onSubmit={noop} />,
260
+ );
261
+
262
+ expect(getByText("required: marked destructive by the server")).toBeTruthy();
263
+ });
264
+ });
265
+
266
+ describe("ApprovalCard message redundancy", () => {
267
+ it("suppresses a file tool's message that only restates the header", () => {
268
+ // "Write file: <path>" duplicates the header + diff — the card drops it.
269
+ const approval = create(PendingApprovalSchema, {
270
+ toolCallId: "tc-msg-file",
271
+ toolName: "write_file",
272
+ toolKind: ToolKind.FILE_EDIT,
273
+ message: "Write file: src/x.ts",
274
+ argsPreview: '{"path":"src/x.ts","contents":"x"}',
275
+ });
276
+
277
+ const { queryByText } = render(
278
+ <ApprovalCard pendingApproval={approval} onSubmit={noop} />,
279
+ );
280
+
281
+ expect(queryByText("Write file: src/x.ts")).toBeNull();
282
+ });
283
+
284
+ it("keeps a non-file tool's informative message (e.g. an MCP prompt)", () => {
285
+ const approval = create(PendingApprovalSchema, {
286
+ toolCallId: "tc-msg-mcp",
287
+ toolName: "create_issue",
288
+ mcpServerSlug: "github",
289
+ message: "Create an issue in acme/repo titled 'Bug'",
290
+ argsPreview: "{}",
291
+ });
292
+
293
+ const { getByText } = render(
294
+ <ApprovalCard pendingApproval={approval} onSubmit={noop} />,
295
+ );
296
+
297
+ expect(getByText("Create an issue in acme/repo titled 'Bug'")).toBeTruthy();
298
+ });
299
+ });
300
+
49
301
  describe("ApprovalCard approve-all action", () => {
50
- it("renders the subordinate 'Approve & don't ask again' action", () => {
302
+ it("renders the subordinate scope-truthful approve-all action", () => {
51
303
  const approval = create(PendingApprovalSchema, {
52
304
  toolCallId: "tc3",
53
305
  toolName: "delete_file",
@@ -59,7 +311,42 @@ describe("ApprovalCard approve-all action", () => {
59
311
  );
60
312
 
61
313
  expect(getByText("Approve")).toBeTruthy();
62
- expect(getByLabelText("Approve & don't ask again")).toBeTruthy();
314
+ // The label names the leased class (delete), never an unbounded "all".
315
+ expect(getByLabelText("Approve all file deletions")).toBeTruthy();
316
+ });
317
+
318
+ it("labels the approve-all action by the clicked tool's lease scope", () => {
319
+ // delete -> "file deletions"; write/edit collapse to "file edits";
320
+ // shell -> "shell commands"; an MCP tool -> "<server> tools".
321
+ const cases: ReadonlyArray<{
322
+ toolName: string;
323
+ mcpServerSlug?: string;
324
+ expected: string;
325
+ }> = [
326
+ { toolName: "delete_file", expected: "Approve all file deletions" },
327
+ { toolName: "write_file", expected: "Approve all file edits" },
328
+ { toolName: "edit_file", expected: "Approve all file edits" },
329
+ { toolName: "shell", expected: "Approve all shell commands" },
330
+ {
331
+ toolName: "create_issue",
332
+ mcpServerSlug: "github",
333
+ expected: "Approve all github tools",
334
+ },
335
+ ];
336
+
337
+ for (const { toolName, mcpServerSlug, expected } of cases) {
338
+ const approval = create(PendingApprovalSchema, {
339
+ toolCallId: `tc-${toolName}`,
340
+ toolName,
341
+ mcpServerSlug: mcpServerSlug ?? "",
342
+ argsPreview: "{}",
343
+ });
344
+ const { getByLabelText, unmount } = render(
345
+ <ApprovalCard pendingApproval={approval} onSubmit={noop} />,
346
+ );
347
+ expect(getByLabelText(expected)).toBeTruthy();
348
+ unmount();
349
+ }
63
350
  });
64
351
 
65
352
  it("submits APPROVE_ALL when the subordinate action is clicked", () => {
@@ -74,7 +361,7 @@ describe("ApprovalCard approve-all action", () => {
74
361
  <ApprovalCard pendingApproval={approval} onSubmit={onSubmit} />,
75
362
  );
76
363
 
77
- fireEvent.click(getByLabelText("Approve & don't ask again"));
364
+ fireEvent.click(getByLabelText("Approve all file deletions"));
78
365
 
79
366
  expect(onSubmit).toHaveBeenCalledTimes(1);
80
367
  expect(onSubmit).toHaveBeenCalledWith(ApprovalAction.APPROVE_ALL);
@@ -97,3 +384,106 @@ describe("ApprovalCard approve-all action", () => {
97
384
  expect(onSubmit).toHaveBeenCalledWith(ApprovalAction.APPROVE);
98
385
  });
99
386
  });
387
+
388
+ // ---------------------------------------------------------------------------
389
+ // Deny-gate preview from tool args (apply-then-review: no captured file_changes)
390
+ // ---------------------------------------------------------------------------
391
+
392
+ describe("ApprovalCard file-change preview", () => {
393
+ it("shows the proposed content (filename only in the header) when the args carry content", () => {
394
+ const approval = create(PendingApprovalSchema, {
395
+ toolCallId: "tc-fallback",
396
+ toolName: "write_file",
397
+ toolKind: ToolKind.FILE_EDIT,
398
+ argsPreview: '{"path":"src/fallback.ts","contents":"x"}',
399
+ // fileChanges intentionally empty.
400
+ });
401
+
402
+ render(<ApprovalCard pendingApproval={approval} onSubmit={noop} />);
403
+
404
+ // The write content still shows via the "Content" collapsible...
405
+ expect(screen.getByText("Content")).toBeTruthy();
406
+ // ...but the filename is shown exactly once (the header) — the body no
407
+ // longer restates the path the header already shows.
408
+ expect(screen.getAllByText("fallback.ts")).toHaveLength(1);
409
+ expect(screen.queryByText(/binary file changed/i)).toBeNull();
410
+ });
411
+
412
+ it("shows a neutral 'no preview' notice for a path-only edit gate (the resume placeholder)", () => {
413
+ // The irreducible case: a ledger denial whose only known arg is the path
414
+ // (no streamed content, no capture). The body must not restate the bare
415
+ // filename nor misrepresent it as an empty file.
416
+ const approval = create(PendingApprovalSchema, {
417
+ toolCallId: "tc-no-preview",
418
+ toolName: "edit_file",
419
+ toolKind: ToolKind.FILE_EDIT,
420
+ argsPreview: '{"path":"notes.md"}',
421
+ // fileChanges intentionally empty; no content in args.
422
+ });
423
+
424
+ render(<ApprovalCard pendingApproval={approval} onSubmit={noop} />);
425
+
426
+ expect(screen.getByText("No preview available for this change")).toBeTruthy();
427
+ // Filename once — header only.
428
+ expect(screen.getAllByText("notes.md")).toHaveLength(1);
429
+ });
430
+
431
+ it("shows a 'new file' notice (not 'no preview') for a content-less FILE_WRITE create gate", () => {
432
+ // The rare residual after the runner's correlation fix: a whole-file write
433
+ // gate that carries only its path (no captured content/diff — a no-stream
434
+ // synthesis). FILE_WRITE is modeled as a create throughout the runner, so the
435
+ // authoritative toolKind lets the gate say plainly that a new file is being
436
+ // written rather than the misleading non-committal "No preview available".
437
+ const approval = create(PendingApprovalSchema, {
438
+ toolCallId: "tc-new-file",
439
+ toolName: "write_file",
440
+ toolKind: ToolKind.FILE_WRITE,
441
+ argsPreview: '{"path":"notes.md"}',
442
+ // fileChanges intentionally empty; no content in args.
443
+ });
444
+
445
+ render(<ApprovalCard pendingApproval={approval} onSubmit={noop} />);
446
+
447
+ expect(screen.getByText("New file — preview unavailable")).toBeTruthy();
448
+ expect(screen.queryByText("No preview available for this change")).toBeNull();
449
+ // Filename once — header only.
450
+ expect(screen.getAllByText("notes.md")).toHaveLength(1);
451
+ });
452
+ });
453
+
454
+ describe("ApprovalCard in-card decision error", () => {
455
+ function makeApproval() {
456
+ return create(PendingApprovalSchema, {
457
+ toolCallId: "tc-err",
458
+ toolName: "delete_file",
459
+ argsPreview: '{"path":"/tmp/x"}',
460
+ });
461
+ }
462
+
463
+ it("surfaces a failed decision in-card, beside the actions (not via a banner)", () => {
464
+ const { container } = render(
465
+ <ApprovalCard
466
+ pendingApproval={makeApproval()}
467
+ onSubmit={noop}
468
+ error={new Error("gate already resolved")}
469
+ />,
470
+ );
471
+
472
+ const alert = container.querySelector(
473
+ '[data-cursor-target="approval-error"]',
474
+ );
475
+ expect(alert).toBeTruthy();
476
+ expect(alert!.textContent).toContain("gate already resolved");
477
+ // The shared notice announces itself.
478
+ expect(alert!.getAttribute("role")).toBe("alert");
479
+ });
480
+
481
+ it("shows no in-card error when healthy", () => {
482
+ const { container } = render(
483
+ <ApprovalCard pendingApproval={makeApproval()} onSubmit={noop} />,
484
+ );
485
+ expect(
486
+ container.querySelector('[data-cursor-target="approval-error"]'),
487
+ ).toBeNull();
488
+ });
489
+ });
@@ -15,7 +15,6 @@ const binaryArtifact = create(ExecutionArtifactSchema, {
15
15
  kind: ExecutionArtifactKind.FILE,
16
16
  sizeBytes: 128n,
17
17
  storageKey: "artifacts/aex_1/report.bin",
18
- downloadUrl: "https://example.test/report.bin",
19
18
  });
20
19
 
21
20
  function withStigmer(children: ReactNode) {
@@ -0,0 +1,24 @@
1
+ import { describe, it, expect, afterEach } from "vitest";
2
+ import { render, screen, cleanup } from "@testing-library/react";
3
+ import { EmptyChangeNotice } from "../EmptyChangeNotice";
4
+
5
+ afterEach(cleanup);
6
+
7
+ // The notice is the single owner of both empty-state messages so the approval
8
+ // gate and the post-execution detail can never drift apart in wording.
9
+ describe("EmptyChangeNotice", () => {
10
+ it("renders the truthful 'new empty file' message for a proven-empty create", () => {
11
+ render(<EmptyChangeNotice kind="empty-create" />);
12
+ expect(screen.getByText("New empty file")).toBeTruthy();
13
+ });
14
+
15
+ it("renders the non-committal 'no preview' message when content is unavailable", () => {
16
+ render(<EmptyChangeNotice kind="no-preview" />);
17
+ expect(screen.getByText("No preview available for this change")).toBeTruthy();
18
+ });
19
+
20
+ it("renders a 'new file' message for a create whose content could not be captured", () => {
21
+ render(<EmptyChangeNotice kind="create" />);
22
+ expect(screen.getByText("New file — preview unavailable")).toBeTruthy();
23
+ });
24
+ });