@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.
- package/execution/ApprovalCard.d.ts +47 -3
- package/execution/ApprovalCard.d.ts.map +1 -1
- package/execution/ApprovalCard.js +138 -70
- package/execution/ApprovalCard.js.map +1 -1
- package/execution/ApprovalContext.d.ts +72 -0
- package/execution/ApprovalContext.d.ts.map +1 -0
- package/execution/ApprovalContext.js +41 -0
- package/execution/ApprovalContext.js.map +1 -0
- package/execution/ArtifactCard.d.ts.map +1 -1
- package/execution/ArtifactCard.js +3 -1
- package/execution/ArtifactCard.js.map +1 -1
- package/execution/ArtifactPreviewModal.d.ts.map +1 -1
- package/execution/ArtifactPreviewModal.js +5 -3
- package/execution/ArtifactPreviewModal.js.map +1 -1
- package/execution/EmptyChangeNotice.d.ts +38 -0
- package/execution/EmptyChangeNotice.d.ts.map +1 -0
- package/execution/EmptyChangeNotice.js +27 -0
- package/execution/EmptyChangeNotice.js.map +1 -0
- package/execution/FileChangesView.d.ts +81 -0
- package/execution/FileChangesView.d.ts.map +1 -0
- package/execution/FileChangesView.js +164 -0
- package/execution/FileChangesView.js.map +1 -0
- package/execution/FilePathLink.d.ts +24 -4
- package/execution/FilePathLink.d.ts.map +1 -1
- package/execution/FilePathLink.js +31 -9
- package/execution/FilePathLink.js.map +1 -1
- package/execution/FileReviewCard.d.ts +111 -0
- package/execution/FileReviewCard.d.ts.map +1 -0
- package/execution/FileReviewCard.js +558 -0
- package/execution/FileReviewCard.js.map +1 -0
- package/execution/FileReviewContext.d.ts +35 -0
- package/execution/FileReviewContext.d.ts.map +1 -0
- package/execution/FileReviewContext.js +23 -0
- package/execution/FileReviewContext.js.map +1 -0
- package/execution/FileReviewDock.d.ts +73 -0
- package/execution/FileReviewDock.d.ts.map +1 -0
- package/execution/FileReviewDock.js +65 -0
- package/execution/FileReviewDock.js.map +1 -0
- package/execution/McpToolDetail.d.ts.map +1 -1
- package/execution/McpToolDetail.js +38 -5
- package/execution/McpToolDetail.js.map +1 -1
- package/execution/MessageThread.d.ts +42 -3
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +263 -18
- package/execution/MessageThread.js.map +1 -1
- package/execution/PlanArtifactCard.d.ts.map +1 -1
- package/execution/PlanArtifactCard.js +3 -1
- package/execution/PlanArtifactCard.js.map +1 -1
- package/execution/PlanCompletionCard.d.ts +1 -1
- package/execution/PlanCompletionCard.js +1 -1
- package/execution/ResultView.d.ts +19 -1
- package/execution/ResultView.d.ts.map +1 -1
- package/execution/ResultView.js +141 -40
- package/execution/ResultView.js.map +1 -1
- package/execution/SubAgentSection.d.ts.map +1 -1
- package/execution/SubAgentSection.js +45 -6
- package/execution/SubAgentSection.js.map +1 -1
- package/execution/TerminalSession.d.ts +34 -0
- package/execution/TerminalSession.d.ts.map +1 -0
- package/execution/TerminalSession.js +34 -0
- package/execution/TerminalSession.js.map +1 -0
- package/execution/TodoCard.d.ts +53 -0
- package/execution/TodoCard.d.ts.map +1 -0
- package/execution/TodoCard.js +77 -0
- package/execution/TodoCard.js.map +1 -0
- package/execution/ToolArgsView.d.ts +7 -1
- package/execution/ToolArgsView.d.ts.map +1 -1
- package/execution/ToolArgsView.js +10 -9
- package/execution/ToolArgsView.js.map +1 -1
- package/execution/ToolCallDetail.d.ts +10 -1
- package/execution/ToolCallDetail.d.ts.map +1 -1
- package/execution/ToolCallDetail.js +66 -19
- package/execution/ToolCallDetail.js.map +1 -1
- package/execution/ToolCallGroup.d.ts +28 -18
- package/execution/ToolCallGroup.d.ts.map +1 -1
- package/execution/ToolCallGroup.js +39 -148
- package/execution/ToolCallGroup.js.map +1 -1
- package/execution/ToolCallItem.d.ts +36 -8
- package/execution/ToolCallItem.d.ts.map +1 -1
- package/execution/ToolCallItem.js +140 -21
- package/execution/ToolCallItem.js.map +1 -1
- package/execution/ToolRunGroup.d.ts +48 -0
- package/execution/ToolRunGroup.d.ts.map +1 -0
- package/execution/ToolRunGroup.js +168 -0
- package/execution/ToolRunGroup.js.map +1 -0
- package/execution/approval-provenance.d.ts +2 -0
- package/execution/approval-provenance.d.ts.map +1 -0
- package/execution/approval-provenance.js +6 -0
- package/execution/approval-provenance.js.map +1 -0
- package/execution/artifact-utils.d.ts +0 -10
- package/execution/artifact-utils.d.ts.map +1 -1
- package/execution/artifact-utils.js +0 -17
- package/execution/artifact-utils.js.map +1 -1
- package/execution/execution-phases.d.ts +1 -6
- package/execution/execution-phases.d.ts.map +1 -1
- package/execution/execution-phases.js +4 -14
- package/execution/execution-phases.js.map +1 -1
- package/execution/file-path-resolver.d.ts +28 -0
- package/execution/file-path-resolver.d.ts.map +1 -1
- package/execution/file-path-resolver.js +24 -0
- package/execution/file-path-resolver.js.map +1 -1
- package/execution/file-review-status.d.ts +127 -0
- package/execution/file-review-status.d.ts.map +1 -0
- package/execution/file-review-status.js +135 -0
- package/execution/file-review-status.js.map +1 -0
- package/execution/index.d.ts +39 -5
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +23 -3
- package/execution/index.js.map +1 -1
- package/execution/segment-tool-calls.d.ts +46 -0
- package/execution/segment-tool-calls.d.ts.map +1 -0
- package/execution/segment-tool-calls.js +71 -0
- package/execution/segment-tool-calls.js.map +1 -0
- package/execution/tool-categories.d.ts +62 -1
- package/execution/tool-categories.d.ts.map +1 -1
- package/execution/tool-categories.js +96 -2
- package/execution/tool-categories.js.map +1 -1
- package/execution/tool-presenter.d.ts +38 -2
- package/execution/tool-presenter.d.ts.map +1 -1
- package/execution/tool-presenter.js +23 -1
- package/execution/tool-presenter.js.map +1 -1
- package/execution/tool-rendering-primitives.d.ts.map +1 -1
- package/execution/tool-rendering-primitives.js +3 -2
- package/execution/tool-rendering-primitives.js.map +1 -1
- package/execution/useArtifactDownload.d.ts +44 -0
- package/execution/useArtifactDownload.d.ts.map +1 -0
- package/execution/useArtifactDownload.js +80 -0
- package/execution/useArtifactDownload.js.map +1 -0
- package/execution/useArtifactDownloadUrl.d.ts +67 -0
- package/execution/useArtifactDownloadUrl.d.ts.map +1 -0
- package/execution/useArtifactDownloadUrl.js +59 -0
- package/execution/useArtifactDownloadUrl.js.map +1 -0
- package/execution/useFileChangeContent.d.ts +61 -0
- package/execution/useFileChangeContent.d.ts.map +1 -0
- package/execution/useFileChangeContent.js +89 -0
- package/execution/useFileChangeContent.js.map +1 -0
- package/execution/useFileReview.d.ts +89 -0
- package/execution/useFileReview.d.ts.map +1 -0
- package/execution/useFileReview.js +106 -0
- package/execution/useFileReview.js.map +1 -0
- package/execution/useSubmitApproval.d.ts +17 -2
- package/execution/useSubmitApproval.d.ts.map +1 -1
- package/execution/useSubmitApproval.js +27 -23
- package/execution/useSubmitApproval.js.map +1 -1
- package/execution/useToolOutputContent.d.ts +44 -0
- package/execution/useToolOutputContent.d.ts.map +1 -0
- package/execution/useToolOutputContent.js +31 -0
- package/execution/useToolOutputContent.js.map +1 -0
- package/index.d.ts +6 -6
- package/index.d.ts.map +1 -1
- package/index.js +3 -3
- package/index.js.map +1 -1
- package/internal/ApprovalPeekBar.d.ts +23 -0
- package/internal/ApprovalPeekBar.d.ts.map +1 -0
- package/internal/ApprovalPeekBar.js +25 -0
- package/internal/ApprovalPeekBar.js.map +1 -0
- package/internal/BoundedContent.d.ts +49 -0
- package/internal/BoundedContent.d.ts.map +1 -0
- package/internal/BoundedContent.js +50 -0
- package/internal/BoundedContent.js.map +1 -0
- package/internal/DecisionButton.d.ts +47 -0
- package/internal/DecisionButton.d.ts.map +1 -0
- package/internal/DecisionButton.js +42 -0
- package/internal/DecisionButton.js.map +1 -0
- package/internal/InCardDecisionError.d.ts +31 -0
- package/internal/InCardDecisionError.d.ts.map +1 -0
- package/internal/InCardDecisionError.js +23 -0
- package/internal/InCardDecisionError.js.map +1 -0
- package/internal/RevealToggle.d.ts +36 -0
- package/internal/RevealToggle.d.ts.map +1 -0
- package/internal/RevealToggle.js +32 -0
- package/internal/RevealToggle.js.map +1 -0
- package/internal/VirtualizedThread.d.ts +7 -1
- package/internal/VirtualizedThread.d.ts.map +1 -1
- package/internal/VirtualizedThread.js +7 -3
- package/internal/VirtualizedThread.js.map +1 -1
- package/internal/useAutoDisclosure.d.ts +34 -0
- package/internal/useAutoDisclosure.d.ts.map +1 -0
- package/internal/useAutoDisclosure.js +53 -0
- package/internal/useAutoDisclosure.js.map +1 -0
- package/internal/useElapsedSince.d.ts +30 -0
- package/internal/useElapsedSince.d.ts.map +1 -0
- package/internal/useElapsedSince.js +70 -0
- package/internal/useElapsedSince.js.map +1 -0
- package/internal/useIsOverflowing.d.ts +30 -0
- package/internal/useIsOverflowing.d.ts.map +1 -0
- package/internal/useIsOverflowing.js +47 -0
- package/internal/useIsOverflowing.js.map +1 -0
- package/internal/useIsTextTruncated.d.ts +24 -0
- package/internal/useIsTextTruncated.d.ts.map +1 -0
- package/internal/useIsTextTruncated.js +41 -0
- package/internal/useIsTextTruncated.js.map +1 -0
- package/internal/useKeyedSubmission.d.ts +49 -0
- package/internal/useKeyedSubmission.d.ts.map +1 -0
- package/internal/useKeyedSubmission.js +67 -0
- package/internal/useKeyedSubmission.js.map +1 -0
- package/package.json +4 -4
- package/session/SessionViewer.d.ts +2 -2
- package/session/SessionViewer.d.ts.map +1 -1
- package/session/SessionViewer.js +10 -4
- package/session/SessionViewer.js.map +1 -1
- package/session/index.d.ts +2 -0
- package/session/index.d.ts.map +1 -1
- package/session/index.js +1 -0
- package/session/index.js.map +1 -1
- package/session/inspector/ChangesTab.d.ts +8 -5
- package/session/inspector/ChangesTab.d.ts.map +1 -1
- package/session/inspector/ChangesTab.js +11 -8
- package/session/inspector/ChangesTab.js.map +1 -1
- package/session/inspector/SessionInspector.d.ts +4 -4
- package/session/inspector/SessionInspector.d.ts.map +1 -1
- package/session/inspector/SessionInspector.js +4 -4
- package/session/inspector/SessionInspector.js.map +1 -1
- package/session/inspector/index.d.ts +0 -2
- package/session/inspector/index.d.ts.map +1 -1
- package/session/inspector/index.js +0 -1
- package/session/inspector/index.js.map +1 -1
- package/session/inspector/useSessionInspector.d.ts +8 -4
- package/session/inspector/useSessionInspector.d.ts.map +1 -1
- package/session/inspector/useSessionInspector.js +27 -20
- package/session/inspector/useSessionInspector.js.map +1 -1
- package/session/useSessionConversation.d.ts +37 -3
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +18 -2
- package/session/useSessionConversation.js.map +1 -1
- package/session/useSessionFileChanges.d.ts +74 -0
- package/session/useSessionFileChanges.d.ts.map +1 -0
- package/session/useSessionFileChanges.js +178 -0
- package/session/useSessionFileChanges.js.map +1 -0
- package/src/__tests__/styles-border-layer-invariant.test.ts +105 -0
- package/src/execution/ApprovalCard.tsx +368 -230
- package/src/execution/ApprovalContext.ts +109 -0
- package/src/execution/ArtifactCard.tsx +10 -6
- package/src/execution/ArtifactPreviewModal.tsx +12 -6
- package/src/execution/EmptyChangeNotice.tsx +62 -0
- package/src/execution/FileChangesView.tsx +383 -0
- package/src/execution/FilePathLink.tsx +65 -12
- package/src/execution/FileReviewCard.tsx +1118 -0
- package/src/execution/FileReviewContext.ts +53 -0
- package/src/execution/FileReviewDock.tsx +157 -0
- package/src/execution/McpToolDetail.tsx +98 -32
- package/src/execution/MessageThread.tsx +374 -9
- package/src/execution/PlanArtifactCard.tsx +9 -6
- package/src/execution/PlanCompletionCard.tsx +1 -1
- package/src/execution/ResultView.tsx +333 -162
- package/src/execution/SubAgentSection.tsx +58 -11
- package/src/execution/TerminalSession.tsx +91 -0
- package/src/execution/TodoCard.tsx +186 -0
- package/src/execution/ToolArgsView.tsx +28 -17
- package/src/execution/ToolCallDetail.tsx +137 -26
- package/src/execution/ToolCallGroup.tsx +68 -296
- package/src/execution/ToolCallItem.tsx +265 -67
- package/src/execution/ToolRunGroup.tsx +302 -0
- package/src/execution/__tests__/ApprovalCard.test.tsx +395 -5
- package/src/execution/__tests__/ArtifactPreviewModal.test.tsx +0 -1
- package/src/execution/__tests__/EmptyChangeNotice.test.tsx +24 -0
- package/src/execution/__tests__/FileChangesView.test.tsx +296 -0
- package/src/execution/__tests__/FilePathLink.test.tsx +92 -0
- package/src/execution/__tests__/FileReviewCard.test.tsx +1044 -0
- package/src/execution/__tests__/FileReviewDock.test.tsx +134 -0
- package/src/execution/__tests__/McpToolDetail.test.tsx +79 -0
- package/src/execution/__tests__/MessageThread.test.tsx +324 -0
- package/src/execution/__tests__/PlanArtifactCard.test.tsx +37 -16
- package/src/execution/__tests__/ResultView.test.tsx +280 -2
- package/src/execution/__tests__/SubAgentSection.test.tsx +180 -0
- package/src/execution/__tests__/TerminalSession.test.tsx +72 -0
- package/src/execution/__tests__/TodoCard.test.tsx +102 -0
- package/src/execution/__tests__/ToolArgsView.test.tsx +48 -0
- package/src/execution/__tests__/ToolCallDetail.test.tsx +195 -0
- package/src/execution/__tests__/ToolCallGroup.test.tsx +132 -0
- package/src/execution/__tests__/ToolCallItem.test.tsx +521 -0
- package/src/execution/__tests__/ToolRunGroup.test.tsx +137 -0
- package/src/execution/__tests__/blanked-narration.test.tsx +149 -0
- package/src/execution/__tests__/buildThreadItems-approvals.test.ts +304 -0
- package/src/execution/__tests__/buildThreadItems-file-review.test.ts +331 -0
- package/src/execution/__tests__/file-path-resolver.test.ts +44 -0
- package/src/execution/__tests__/file-review-status.test.ts +343 -0
- package/src/execution/__tests__/segment-tool-calls.test.ts +124 -0
- package/src/execution/__tests__/subagent-file-review-badge.test.tsx +273 -0
- package/src/execution/__tests__/thread-keys.test.ts +195 -0
- package/src/execution/__tests__/tool-presenter.test.tsx +95 -0
- package/src/execution/__tests__/useArtifactDownload.test.tsx +67 -0
- package/src/execution/__tests__/useArtifactDownloadUrl.test.tsx +51 -0
- package/src/execution/__tests__/useFileChangeContent.test.tsx +240 -0
- package/src/execution/__tests__/useFileReview.test.tsx +236 -0
- package/src/execution/__tests__/useSubmitApproval.test.tsx +165 -0
- package/src/execution/__tests__/useToolOutputContent.test.tsx +82 -0
- package/src/execution/approval-provenance.ts +8 -0
- package/src/execution/artifact-utils.ts +0 -18
- package/src/execution/execution-phases.ts +4 -16
- package/src/execution/file-path-resolver.ts +38 -0
- package/src/execution/file-review-status.ts +219 -0
- package/src/execution/index.ts +84 -4
- package/src/execution/segment-tool-calls.ts +101 -0
- package/src/execution/tool-categories.ts +128 -1
- package/src/execution/tool-presenter.ts +58 -3
- package/src/execution/tool-rendering-primitives.tsx +11 -14
- package/src/execution/useArtifactDownload.ts +109 -0
- package/src/execution/useArtifactDownloadUrl.ts +116 -0
- package/src/execution/useFileChangeContent.ts +149 -0
- package/src/execution/useFileReview.ts +197 -0
- package/src/execution/useSubmitApproval.ts +49 -27
- package/src/execution/useToolOutputContent.ts +70 -0
- package/src/index.ts +60 -1
- package/src/internal/ApprovalPeekBar.tsx +73 -0
- package/src/internal/BoundedContent.tsx +97 -0
- package/src/internal/DecisionButton.tsx +127 -0
- package/src/internal/InCardDecisionError.tsx +47 -0
- package/src/internal/RevealToggle.tsx +91 -0
- package/src/internal/VirtualizedThread.tsx +28 -2
- package/src/internal/__tests__/ApprovalPeekBar.test.tsx +34 -0
- package/src/internal/__tests__/BoundedContent.test.tsx +92 -0
- package/src/internal/__tests__/DecisionButton.test.tsx +107 -0
- package/src/internal/__tests__/RevealToggle.test.tsx +61 -0
- package/src/internal/__tests__/useAutoDisclosure.test.ts +74 -0
- package/src/internal/__tests__/useElapsedSince.test.tsx +68 -0
- package/src/internal/__tests__/useIsOverflowing.test.ts +36 -0
- package/src/internal/__tests__/useKeyedSubmission.test.tsx +126 -0
- package/src/internal/useAutoDisclosure.ts +58 -0
- package/src/internal/useElapsedSince.ts +72 -0
- package/src/internal/useIsOverflowing.ts +61 -0
- package/src/internal/useIsTextTruncated.ts +55 -0
- package/src/internal/useKeyedSubmission.ts +101 -0
- package/src/session/SessionViewer.tsx +21 -4
- package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +198 -0
- package/src/session/__tests__/audienceWiring.test.tsx +4 -0
- package/src/session/__tests__/useSessionFileChanges.test.ts +303 -0
- package/src/session/index.ts +3 -0
- package/src/session/inspector/ChangesTab.tsx +20 -17
- package/src/session/inspector/SessionInspector.tsx +12 -10
- package/src/session/inspector/__tests__/ArtifactsTab.test.tsx +0 -1
- package/src/session/inspector/__tests__/ChangesTab.test.tsx +38 -0
- package/src/session/inspector/__tests__/useSessionInspector.test.ts +46 -61
- package/src/session/inspector/index.ts +0 -3
- package/src/session/inspector/useSessionInspector.ts +27 -20
- package/src/session/useSessionConversation.ts +80 -3
- package/src/session/useSessionFileChanges.ts +207 -0
- package/src/styles.css +35 -10
- package/src/version-history/DiffViewer.tsx +10 -7
- package/src/version-history/UnifiedDiffView.tsx +66 -0
- package/src/version-history/__tests__/DiffViewer.test.tsx +57 -0
- package/src/version-history/__tests__/UnifiedDiffView.test.tsx +50 -0
- package/src/version-history/__tests__/computeDiff.test.ts +35 -0
- package/src/version-history/__tests__/parseUnifiedDiff.test.ts +82 -0
- package/src/version-history/computeDiff.ts +6 -37
- package/src/version-history/diff-hunks.ts +63 -0
- package/src/version-history/index.ts +1 -0
- package/src/version-history/parseUnifiedDiff.ts +34 -0
- package/src/workflow/WorkflowExecutionApprovalCard.tsx +49 -24
- package/src/workflow/WorkflowExecutionTimeline.tsx +15 -4
- package/src/workflow/WorkflowExecutionTimelineEvent.tsx +8 -3
- package/src/workflow/WorkflowExecutionViewer.tsx +72 -28
- package/src/workflow/WorkflowFileReviewList.tsx +152 -0
- package/src/workflow/WorkflowTaskApprovalCard.tsx +37 -85
- package/src/workflow/__tests__/ExecutionInspector.test.tsx +30 -2
- package/src/workflow/__tests__/WorkflowExecutionApprovalCard.test.tsx +129 -0
- package/src/workflow/__tests__/WorkflowFileReviewList.test.tsx +129 -0
- package/src/workflow/__tests__/WorkflowTaskApprovalCard.test.tsx +43 -11
- package/src/workflow/__tests__/useWorkflowExecutionActions.test.tsx +276 -0
- package/src/workflow/execution-inspector/ExecutionInspector.tsx +42 -18
- package/src/workflow/index.ts +5 -0
- package/src/workflow/useWorkflowExecutionActions.ts +177 -29
- package/styles.css +1 -1
- package/version-history/DiffViewer.d.ts.map +1 -1
- package/version-history/DiffViewer.js +9 -3
- package/version-history/DiffViewer.js.map +1 -1
- package/version-history/UnifiedDiffView.d.ts +29 -0
- package/version-history/UnifiedDiffView.d.ts.map +1 -0
- package/version-history/UnifiedDiffView.js +36 -0
- package/version-history/UnifiedDiffView.js.map +1 -0
- package/version-history/computeDiff.d.ts +3 -1
- package/version-history/computeDiff.d.ts.map +1 -1
- package/version-history/computeDiff.js +5 -35
- package/version-history/computeDiff.js.map +1 -1
- package/version-history/diff-hunks.d.ts +21 -0
- package/version-history/diff-hunks.d.ts.map +1 -0
- package/version-history/diff-hunks.js +58 -0
- package/version-history/diff-hunks.js.map +1 -0
- package/version-history/index.d.ts +1 -0
- package/version-history/index.d.ts.map +1 -1
- package/version-history/index.js +1 -0
- package/version-history/index.js.map +1 -1
- package/version-history/parseUnifiedDiff.d.ts +22 -0
- package/version-history/parseUnifiedDiff.d.ts.map +1 -0
- package/version-history/parseUnifiedDiff.js +34 -0
- package/version-history/parseUnifiedDiff.js.map +1 -0
- package/workflow/WorkflowExecutionApprovalCard.d.ts +8 -1
- package/workflow/WorkflowExecutionApprovalCard.d.ts.map +1 -1
- package/workflow/WorkflowExecutionApprovalCard.js +17 -3
- package/workflow/WorkflowExecutionApprovalCard.js.map +1 -1
- package/workflow/WorkflowExecutionTimeline.d.ts +11 -2
- package/workflow/WorkflowExecutionTimeline.d.ts.map +1 -1
- package/workflow/WorkflowExecutionTimeline.js +2 -2
- package/workflow/WorkflowExecutionTimeline.js.map +1 -1
- package/workflow/WorkflowExecutionTimelineEvent.d.ts +4 -1
- package/workflow/WorkflowExecutionTimelineEvent.d.ts.map +1 -1
- package/workflow/WorkflowExecutionTimelineEvent.js +2 -2
- package/workflow/WorkflowExecutionTimelineEvent.js.map +1 -1
- package/workflow/WorkflowExecutionViewer.d.ts.map +1 -1
- package/workflow/WorkflowExecutionViewer.js +18 -10
- package/workflow/WorkflowExecutionViewer.js.map +1 -1
- package/workflow/WorkflowFileReviewList.d.ts +46 -0
- package/workflow/WorkflowFileReviewList.d.ts.map +1 -0
- package/workflow/WorkflowFileReviewList.js +48 -0
- package/workflow/WorkflowFileReviewList.js.map +1 -0
- package/workflow/WorkflowTaskApprovalCard.d.ts +8 -1
- package/workflow/WorkflowTaskApprovalCard.d.ts.map +1 -1
- package/workflow/WorkflowTaskApprovalCard.js +12 -19
- package/workflow/WorkflowTaskApprovalCard.js.map +1 -1
- package/workflow/execution-inspector/ExecutionInspector.d.ts +21 -4
- package/workflow/execution-inspector/ExecutionInspector.d.ts.map +1 -1
- package/workflow/execution-inspector/ExecutionInspector.js +6 -3
- package/workflow/execution-inspector/ExecutionInspector.js.map +1 -1
- package/workflow/index.d.ts +1 -0
- package/workflow/index.d.ts.map +1 -1
- package/workflow/index.js +1 -0
- package/workflow/index.js.map +1 -1
- package/workflow/useWorkflowExecutionActions.d.ts +62 -4
- package/workflow/useWorkflowExecutionActions.d.ts.map +1 -1
- package/workflow/useWorkflowExecutionActions.js +94 -18
- package/workflow/useWorkflowExecutionActions.js.map +1 -1
- package/session/inspector/PlanTab.d.ts +0 -15
- package/session/inspector/PlanTab.d.ts.map +0 -1
- package/session/inspector/PlanTab.js +0 -19
- package/session/inspector/PlanTab.js.map +0 -1
- 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
|
|
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-
|
|
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
|
-
<
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
):
|
|
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 "
|
|
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 "
|
|
281
|
+
return "danger";
|
|
309
282
|
}
|
|
310
283
|
|
|
311
|
-
return "
|
|
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
|
-
|
|
41
|
-
|
|
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-
|
|
48
|
+
expect(btn.className).toContain("bg-accent");
|
|
49
|
+
expect(btn.className).not.toContain("bg-success");
|
|
45
50
|
});
|
|
46
51
|
|
|
47
|
-
it('outcome named "reject"
|
|
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("
|
|
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"
|
|
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("
|
|
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
|
|
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("
|
|
83
|
+
expect(btn.className).toContain("hover:text-destructive");
|
|
77
84
|
});
|
|
78
85
|
|
|
79
|
-
it("middle outcomes in 3+
|
|
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("
|
|
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} />);
|