@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
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach, vi } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
|
|
3
|
+
import { create, type JsonObject } from "@bufbuild/protobuf";
|
|
4
|
+
import {
|
|
5
|
+
ToolCallSchema,
|
|
6
|
+
type ToolCall,
|
|
7
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
8
|
+
import {
|
|
9
|
+
PendingApprovalSchema,
|
|
10
|
+
type PendingApproval,
|
|
11
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/approval_pb";
|
|
12
|
+
import {
|
|
13
|
+
CapturedFileChangeSchema,
|
|
14
|
+
FileChangeSetSchema,
|
|
15
|
+
FileDecisionSchema,
|
|
16
|
+
type FileChangeSet,
|
|
17
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
|
|
18
|
+
import {
|
|
19
|
+
ApprovalAction,
|
|
20
|
+
FileChangeKind,
|
|
21
|
+
FileChangeSetStatus,
|
|
22
|
+
FileDecisionAction,
|
|
23
|
+
FileDecisionScope,
|
|
24
|
+
ToolCallStatus,
|
|
25
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
26
|
+
import { ToolCallItem } from "../ToolCallItem";
|
|
27
|
+
import { ApprovalContext, type ApprovalContextValue } from "../ApprovalContext";
|
|
28
|
+
import { FileReviewContext } from "../FileReviewContext";
|
|
29
|
+
|
|
30
|
+
afterEach(cleanup);
|
|
31
|
+
|
|
32
|
+
function makeToolCall(opts: {
|
|
33
|
+
id?: string;
|
|
34
|
+
name: string;
|
|
35
|
+
args?: Record<string, unknown>;
|
|
36
|
+
result?: string;
|
|
37
|
+
mcpServerSlug?: string;
|
|
38
|
+
status?: ToolCallStatus;
|
|
39
|
+
fileChangeSetId?: string;
|
|
40
|
+
}): ToolCall {
|
|
41
|
+
return create(ToolCallSchema, {
|
|
42
|
+
id: opts.id ?? opts.name,
|
|
43
|
+
name: opts.name,
|
|
44
|
+
args: (opts.args ?? {}) as JsonObject,
|
|
45
|
+
result: opts.result ?? "",
|
|
46
|
+
mcpServerSlug: opts.mcpServerSlug ?? "",
|
|
47
|
+
status: opts.status ?? ToolCallStatus.TOOL_CALL_COMPLETED,
|
|
48
|
+
fileChangeSetId: opts.fileChangeSetId ?? "",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// The card-level disclosure (header chevron) lives ONLY on summary / sub-agent
|
|
53
|
+
// rows: a `div[role="button"]` header inside the row. Preview (content-bearing)
|
|
54
|
+
// rows have no such control — their body is always visible — so these helpers
|
|
55
|
+
// target that header specifically rather than "any [aria-expanded]" (a block's
|
|
56
|
+
// own RevealToggle also carries aria-expanded, on a native <button>, which has
|
|
57
|
+
// no role attribute and so is excluded here).
|
|
58
|
+
function rowHeaderToggle(container: HTMLElement): Element | null {
|
|
59
|
+
return container.querySelector(
|
|
60
|
+
'[data-cursor-target="tool-call-row"] [role="button"]',
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
function hasHeaderChevron(container: HTMLElement): boolean {
|
|
64
|
+
return rowHeaderToggle(container) != null;
|
|
65
|
+
}
|
|
66
|
+
function isExpanded(container: HTMLElement): boolean {
|
|
67
|
+
return rowHeaderToggle(container)?.getAttribute("aria-expanded") === "true";
|
|
68
|
+
}
|
|
69
|
+
function previewBody(container: HTMLElement): Element | null {
|
|
70
|
+
return container.querySelector('[data-cursor-target="tool-preview"]');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
describe("ToolCallItem disclosure", () => {
|
|
74
|
+
// --- Summary categories keep the chevron ----------------------------------
|
|
75
|
+
|
|
76
|
+
it("keeps a settled summary tool collapsed behind a chevron", () => {
|
|
77
|
+
// Delete is a `summary` category — its one-line row (the file) tells the
|
|
78
|
+
// story, so its body stays hidden behind the header chevron.
|
|
79
|
+
const { container } = render(
|
|
80
|
+
<ToolCallItem
|
|
81
|
+
toolCall={makeToolCall({ name: "delete_file", args: { path: "/x.ts" } })}
|
|
82
|
+
/>,
|
|
83
|
+
);
|
|
84
|
+
expect(hasHeaderChevron(container)).toBe(true);
|
|
85
|
+
expect(isExpanded(container)).toBe(false);
|
|
86
|
+
expect(previewBody(container)).toBeNull();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("settles a summary tool closed once it finishes running", () => {
|
|
90
|
+
// A running summary tool foregrounds while live, then collapses to its
|
|
91
|
+
// compact summary the moment it completes (the user never touched it). The
|
|
92
|
+
// chevron is present throughout. Proves the React.memo + useAutoDisclosure
|
|
93
|
+
// composition recomputes autoOpen from the new ToolCall on re-render.
|
|
94
|
+
const { container, rerender } = render(
|
|
95
|
+
<ToolCallItem
|
|
96
|
+
toolCall={makeToolCall({
|
|
97
|
+
id: "tc-del",
|
|
98
|
+
name: "delete_file",
|
|
99
|
+
args: { path: "/x.ts" },
|
|
100
|
+
status: ToolCallStatus.TOOL_CALL_RUNNING,
|
|
101
|
+
})}
|
|
102
|
+
/>,
|
|
103
|
+
);
|
|
104
|
+
expect(isExpanded(container)).toBe(true);
|
|
105
|
+
|
|
106
|
+
rerender(
|
|
107
|
+
<ToolCallItem
|
|
108
|
+
toolCall={makeToolCall({
|
|
109
|
+
id: "tc-del",
|
|
110
|
+
name: "delete_file",
|
|
111
|
+
args: { path: "/x.ts" },
|
|
112
|
+
status: ToolCallStatus.TOOL_CALL_COMPLETED,
|
|
113
|
+
})}
|
|
114
|
+
/>,
|
|
115
|
+
);
|
|
116
|
+
expect(hasHeaderChevron(container)).toBe(true);
|
|
117
|
+
expect(isExpanded(container)).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// --- Preview categories: no chevron, always-visible body ------------------
|
|
121
|
+
|
|
122
|
+
it("renders a settled edit as an always-visible diff with no header chevron", () => {
|
|
123
|
+
// Edit is a `preview` category: the diff IS the information, so it renders
|
|
124
|
+
// inline with no competing card chevron. The diff "Show more" is layout-
|
|
125
|
+
// gated (jsdom computes no layout), so it is asserted in e2e, not here.
|
|
126
|
+
const { container } = render(
|
|
127
|
+
<ToolCallItem
|
|
128
|
+
toolCall={makeToolCall({
|
|
129
|
+
name: "StrReplace",
|
|
130
|
+
args: { path: "/x.ts" },
|
|
131
|
+
result:
|
|
132
|
+
'{"status":"success","value":{"linesAdded":1,"linesRemoved":1,"diffString":"@@ -1 +1 @@\\n-a\\n+b"}}',
|
|
133
|
+
})}
|
|
134
|
+
/>,
|
|
135
|
+
);
|
|
136
|
+
expect(hasHeaderChevron(container)).toBe(false);
|
|
137
|
+
expect(previewBody(container)).not.toBeNull();
|
|
138
|
+
// The diff renders as the accessible table.
|
|
139
|
+
expect(container.querySelector("table")).not.toBeNull();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("renders a settled shell as an always-visible terminal session, no chevron", () => {
|
|
143
|
+
// Shell is a `preview` category: its output IS the information. The body
|
|
144
|
+
// reads as one terminal session — the `$ command` prompt line leads, with
|
|
145
|
+
// its output below — visible without any disclosure click.
|
|
146
|
+
const { container } = render(
|
|
147
|
+
<ToolCallItem
|
|
148
|
+
toolCall={makeToolCall({
|
|
149
|
+
name: "Shell",
|
|
150
|
+
args: { command: "echo hello" },
|
|
151
|
+
result: "hello",
|
|
152
|
+
})}
|
|
153
|
+
/>,
|
|
154
|
+
);
|
|
155
|
+
expect(hasHeaderChevron(container)).toBe(false);
|
|
156
|
+
expect(previewBody(container)).not.toBeNull();
|
|
157
|
+
expect(container.textContent).toContain("$ echo hello");
|
|
158
|
+
expect(screen.getByText("hello")).toBeTruthy();
|
|
159
|
+
expect(
|
|
160
|
+
container.querySelectorAll('[data-cursor-target="terminal-session"]'),
|
|
161
|
+
).toHaveLength(1);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("renders a settled MCP tool's result inline with no chevron", () => {
|
|
165
|
+
const { container } = render(
|
|
166
|
+
<ToolCallItem
|
|
167
|
+
toolCall={makeToolCall({
|
|
168
|
+
name: "send_message",
|
|
169
|
+
mcpServerSlug: "acme/slack",
|
|
170
|
+
result: '{"ok":true}',
|
|
171
|
+
})}
|
|
172
|
+
/>,
|
|
173
|
+
);
|
|
174
|
+
expect(hasHeaderChevron(container)).toBe(false);
|
|
175
|
+
expect(previewBody(container)).not.toBeNull();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("keeps the shell header minimal — no command subtitle, no exit summary", () => {
|
|
179
|
+
// The command and exit code live in the terminal session body, so the plain
|
|
180
|
+
// header is just icon + label + status + duration. A failed command must not
|
|
181
|
+
// leak its command or "exit N" into the header row.
|
|
182
|
+
const { container } = render(
|
|
183
|
+
<ToolCallItem
|
|
184
|
+
toolCall={makeToolCall({
|
|
185
|
+
name: "Shell",
|
|
186
|
+
args: { command: "ls /secret/abs/path" },
|
|
187
|
+
result: "denied\n[Command failed with exit code 2]",
|
|
188
|
+
})}
|
|
189
|
+
/>,
|
|
190
|
+
);
|
|
191
|
+
const row = container.querySelector('[data-cursor-target="tool-call-row"]')!;
|
|
192
|
+
const header = row.firstElementChild!;
|
|
193
|
+
expect(header.textContent).toContain("Shell");
|
|
194
|
+
expect(header.textContent).not.toContain("/secret/abs/path");
|
|
195
|
+
expect(header.textContent).not.toContain("exit 2");
|
|
196
|
+
// The exit DOES surface — in the body's terminal session.
|
|
197
|
+
expect(container.textContent).toContain("exit 2");
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("renders a running shell as a single terminal session block, no chevron", () => {
|
|
201
|
+
const { container } = render(
|
|
202
|
+
<ToolCallItem
|
|
203
|
+
toolCall={makeToolCall({
|
|
204
|
+
name: "Shell",
|
|
205
|
+
args: { command: "echo hi" },
|
|
206
|
+
result: "hi",
|
|
207
|
+
status: ToolCallStatus.TOOL_CALL_RUNNING,
|
|
208
|
+
})}
|
|
209
|
+
/>,
|
|
210
|
+
);
|
|
211
|
+
expect(hasHeaderChevron(container)).toBe(false);
|
|
212
|
+
expect(
|
|
213
|
+
container.querySelectorAll('[data-cursor-target="terminal-session"]'),
|
|
214
|
+
).toHaveLength(1);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("suppresses the body for a content-bearing tool that produced nothing", () => {
|
|
218
|
+
// The regression guard: a preview-category tool whose result normalizes to
|
|
219
|
+
// `empty` (no output) must stay a clean one-line row, never an empty padded
|
|
220
|
+
// box. (Shell never hits this — it always carries its command line.)
|
|
221
|
+
const { container } = render(
|
|
222
|
+
<ToolCallItem toolCall={makeToolCall({ name: "some_unknown_tool", result: "" })} />,
|
|
223
|
+
);
|
|
224
|
+
expect(hasHeaderChevron(container)).toBe(false);
|
|
225
|
+
expect(previewBody(container)).toBeNull();
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("shows the +N -M summary once for an edit — header only, not the diff body", () => {
|
|
229
|
+
// The row header carries the result summary; the always-visible diff body
|
|
230
|
+
// must NOT repeat it. With added != removed the header's combined "+3 -0"
|
|
231
|
+
// node and a body "+3" stat span would be distinguishable — and the body has
|
|
232
|
+
// none (suppressed via showStats={false}).
|
|
233
|
+
const { container } = render(
|
|
234
|
+
<ToolCallItem
|
|
235
|
+
toolCall={makeToolCall({
|
|
236
|
+
name: "StrReplace",
|
|
237
|
+
args: { path: "/x.ts" },
|
|
238
|
+
result:
|
|
239
|
+
'{"status":"success","value":{"linesAdded":3,"linesRemoved":0,"diffString":"@@ -0,0 +1,3 @@\\n+a\\n+b\\n+c"}}',
|
|
240
|
+
})}
|
|
241
|
+
/>,
|
|
242
|
+
);
|
|
243
|
+
expect(hasHeaderChevron(container)).toBe(false);
|
|
244
|
+
expect(screen.getByText("+3 -0")).toBeTruthy();
|
|
245
|
+
expect(screen.queryAllByText("+3")).toHaveLength(0);
|
|
246
|
+
expect(container.querySelector("table")).not.toBeNull();
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// --- Approvals -------------------------------------------------------------
|
|
250
|
+
|
|
251
|
+
it("foregrounds a gated summary tool behind its chevron with inline actions", () => {
|
|
252
|
+
const tc = makeToolCall({
|
|
253
|
+
id: "tc-gated",
|
|
254
|
+
name: "delete_file",
|
|
255
|
+
status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
|
|
256
|
+
});
|
|
257
|
+
const approval: PendingApproval = create(PendingApprovalSchema, {
|
|
258
|
+
toolCallId: "tc-gated",
|
|
259
|
+
toolName: "delete_file",
|
|
260
|
+
argsPreview: '{"path":"/tmp/x"}',
|
|
261
|
+
});
|
|
262
|
+
const ctx: ApprovalContextValue = {
|
|
263
|
+
approvalsByToolCallId: new Map([["tc-gated", approval]]),
|
|
264
|
+
onSubmit: () => {},
|
|
265
|
+
submittingIds: new Set(),
|
|
266
|
+
errorsByToolCallId: new Map(),
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const { container } = render(
|
|
270
|
+
<ApprovalContext.Provider value={ctx}>
|
|
271
|
+
<ToolCallItem toolCall={tc} />
|
|
272
|
+
</ApprovalContext.Provider>,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
expect(hasHeaderChevron(container)).toBe(true);
|
|
276
|
+
expect(isExpanded(container)).toBe(true);
|
|
277
|
+
expect(screen.getByLabelText("Approve")).toBeTruthy();
|
|
278
|
+
expect(screen.getByLabelText("Approve all file deletions")).toBeTruthy();
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it("renders a gated preview tool's actions inline with no chevron", () => {
|
|
282
|
+
// A gated shell (a preview category) shows its decision actions in the
|
|
283
|
+
// always-visible body — no header chevron, since there is nothing to reveal.
|
|
284
|
+
const tc = makeToolCall({
|
|
285
|
+
id: "tc-shell-gated",
|
|
286
|
+
name: "Shell",
|
|
287
|
+
args: { command: "rm -rf /tmp/x" },
|
|
288
|
+
status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
|
|
289
|
+
});
|
|
290
|
+
const approval: PendingApproval = create(PendingApprovalSchema, {
|
|
291
|
+
toolCallId: "tc-shell-gated",
|
|
292
|
+
toolName: "Shell",
|
|
293
|
+
argsPreview: '{"command":"rm -rf /tmp/x"}',
|
|
294
|
+
});
|
|
295
|
+
const ctx: ApprovalContextValue = {
|
|
296
|
+
approvalsByToolCallId: new Map([["tc-shell-gated", approval]]),
|
|
297
|
+
onSubmit: () => {},
|
|
298
|
+
submittingIds: new Set(),
|
|
299
|
+
errorsByToolCallId: new Map(),
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const { container } = render(
|
|
303
|
+
<ApprovalContext.Provider value={ctx}>
|
|
304
|
+
<ToolCallItem toolCall={tc} />
|
|
305
|
+
</ApprovalContext.Provider>,
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
expect(hasHeaderChevron(container)).toBe(false);
|
|
309
|
+
expect(previewBody(container)).not.toBeNull();
|
|
310
|
+
expect(screen.getByLabelText("Approve")).toBeTruthy();
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it("gives a pending gate card a restrained destructive accent (no amber fill)", () => {
|
|
314
|
+
const tc = makeToolCall({
|
|
315
|
+
id: "tc-gated",
|
|
316
|
+
name: "delete_file",
|
|
317
|
+
status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
|
|
318
|
+
});
|
|
319
|
+
const approval: PendingApproval = create(PendingApprovalSchema, {
|
|
320
|
+
toolCallId: "tc-gated",
|
|
321
|
+
toolName: "delete_file",
|
|
322
|
+
argsPreview: '{"path":"/tmp/x"}',
|
|
323
|
+
});
|
|
324
|
+
const ctx: ApprovalContextValue = {
|
|
325
|
+
approvalsByToolCallId: new Map([["tc-gated", approval]]),
|
|
326
|
+
onSubmit: () => {},
|
|
327
|
+
submittingIds: new Set(),
|
|
328
|
+
errorsByToolCallId: new Map(),
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const { container } = render(
|
|
332
|
+
<ApprovalContext.Provider value={ctx}>
|
|
333
|
+
<ToolCallItem toolCall={tc} />
|
|
334
|
+
</ApprovalContext.Provider>,
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
const row = container.querySelector('[data-cursor-target="tool-call-row"]')!;
|
|
338
|
+
expect(row.className).toContain("border-l-destructive");
|
|
339
|
+
expect(row.className).not.toContain("bg-warning");
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("routes an inline APPROVE_ALL decision with the gated tool's id", () => {
|
|
343
|
+
const tc = makeToolCall({
|
|
344
|
+
id: "tc-gated",
|
|
345
|
+
name: "delete_file",
|
|
346
|
+
status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
|
|
347
|
+
});
|
|
348
|
+
const approval: PendingApproval = create(PendingApprovalSchema, {
|
|
349
|
+
toolCallId: "tc-gated",
|
|
350
|
+
toolName: "delete_file",
|
|
351
|
+
argsPreview: '{"path":"/tmp/x"}',
|
|
352
|
+
});
|
|
353
|
+
const onSubmit = vi.fn();
|
|
354
|
+
const ctx: ApprovalContextValue = {
|
|
355
|
+
approvalsByToolCallId: new Map([["tc-gated", approval]]),
|
|
356
|
+
onSubmit,
|
|
357
|
+
submittingIds: new Set(),
|
|
358
|
+
errorsByToolCallId: new Map(),
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
render(
|
|
362
|
+
<ApprovalContext.Provider value={ctx}>
|
|
363
|
+
<ToolCallItem toolCall={tc} />
|
|
364
|
+
</ApprovalContext.Provider>,
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
fireEvent.click(screen.getByLabelText("Approve all file deletions"));
|
|
368
|
+
expect(onSubmit).toHaveBeenCalledTimes(1);
|
|
369
|
+
expect(onSubmit).toHaveBeenCalledWith(
|
|
370
|
+
"tc-gated",
|
|
371
|
+
ApprovalAction.APPROVE_ALL,
|
|
372
|
+
undefined,
|
|
373
|
+
);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it("surfaces a keyed approval error in-card on the gated row (the primary route)", () => {
|
|
377
|
+
const tc = makeToolCall({
|
|
378
|
+
id: "tc-gated",
|
|
379
|
+
name: "delete_file",
|
|
380
|
+
status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
|
|
381
|
+
});
|
|
382
|
+
const approval: PendingApproval = create(PendingApprovalSchema, {
|
|
383
|
+
toolCallId: "tc-gated",
|
|
384
|
+
toolName: "delete_file",
|
|
385
|
+
argsPreview: '{"path":"/tmp/x"}',
|
|
386
|
+
});
|
|
387
|
+
const ctx: ApprovalContextValue = {
|
|
388
|
+
approvalsByToolCallId: new Map([["tc-gated", approval]]),
|
|
389
|
+
onSubmit: () => {},
|
|
390
|
+
submittingIds: new Set(),
|
|
391
|
+
// The error for THIS gate, keyed by its tool call id.
|
|
392
|
+
errorsByToolCallId: new Map([["tc-gated", new Error("gate already resolved")]]),
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const { container } = render(
|
|
396
|
+
<ApprovalContext.Provider value={ctx}>
|
|
397
|
+
<ToolCallItem toolCall={tc} />
|
|
398
|
+
</ApprovalContext.Provider>,
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
const alert = container.querySelector(
|
|
402
|
+
'[data-cursor-target="approval-error"]',
|
|
403
|
+
);
|
|
404
|
+
expect(alert).toBeTruthy();
|
|
405
|
+
expect(alert!.textContent).toContain("gate already resolved");
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// --- Card chrome -----------------------------------------------------------
|
|
409
|
+
|
|
410
|
+
it("renders as its own bordered card by default, with a divider-row fallback when nested", () => {
|
|
411
|
+
const tc = makeToolCall({ name: "Shell", args: { command: "echo hi" }, result: "hi" });
|
|
412
|
+
|
|
413
|
+
const { container, rerender } = render(<ToolCallItem toolCall={tc} />);
|
|
414
|
+
let row = container.querySelector('[data-cursor-target="tool-call-row"]')!;
|
|
415
|
+
expect(row.className).toContain("rounded-lg");
|
|
416
|
+
expect(row.className).toContain("border-border-prominent");
|
|
417
|
+
|
|
418
|
+
rerender(<ToolCallItem toolCall={tc} bordered={false} />);
|
|
419
|
+
row = container.querySelector('[data-cursor-target="tool-call-row"]')!;
|
|
420
|
+
expect(row.className).not.toContain("rounded-lg");
|
|
421
|
+
expect(row.className).toContain("border-b");
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
// ---------------------------------------------------------------------------
|
|
426
|
+
// File-review badge — a stamped edit row's live review state
|
|
427
|
+
// ---------------------------------------------------------------------------
|
|
428
|
+
|
|
429
|
+
describe("ToolCallItem file-review badge", () => {
|
|
430
|
+
const SET_ID = "aex-1:0";
|
|
431
|
+
|
|
432
|
+
function reviewSet(
|
|
433
|
+
status: FileChangeSetStatus,
|
|
434
|
+
decisions: { action: FileDecisionAction }[] = [],
|
|
435
|
+
): FileChangeSet {
|
|
436
|
+
return create(FileChangeSetSchema, {
|
|
437
|
+
id: SET_ID,
|
|
438
|
+
status,
|
|
439
|
+
changes: [
|
|
440
|
+
create(CapturedFileChangeSchema, {
|
|
441
|
+
id: `${SET_ID}:notes.md`,
|
|
442
|
+
pathBefore: "notes.md",
|
|
443
|
+
pathAfter: "notes.md",
|
|
444
|
+
kind: FileChangeKind.MODIFY,
|
|
445
|
+
diffComplete: true,
|
|
446
|
+
}),
|
|
447
|
+
],
|
|
448
|
+
decisions: decisions.map((d) =>
|
|
449
|
+
create(FileDecisionSchema, {
|
|
450
|
+
changeSetId: SET_ID,
|
|
451
|
+
scope: FileDecisionScope.CHANGE_SET,
|
|
452
|
+
action: d.action,
|
|
453
|
+
}),
|
|
454
|
+
),
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function renderStampedRow(set: FileChangeSet) {
|
|
459
|
+
const tc = makeToolCall({
|
|
460
|
+
id: "tc-edit",
|
|
461
|
+
name: "edit",
|
|
462
|
+
args: { path: "notes.md", old_string: "a", new_string: "b" },
|
|
463
|
+
fileChangeSetId: SET_ID,
|
|
464
|
+
});
|
|
465
|
+
return render(
|
|
466
|
+
<FileReviewContext.Provider value={{ changeSetsById: new Map([[SET_ID, set]]) }}>
|
|
467
|
+
<ToolCallItem toolCall={tc} />
|
|
468
|
+
</FileReviewContext.Provider>,
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function badge(container: HTMLElement): Element | null {
|
|
473
|
+
return container.querySelector('[data-cursor-target="file-review-row-badge"]');
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
it("badges a stamped row 'Pending review' while its set awaits review", () => {
|
|
477
|
+
const { container } = renderStampedRow(reviewSet(FileChangeSetStatus.AWAITING_REVIEW));
|
|
478
|
+
expect(badge(container)?.textContent).toBe("Pending review");
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it("flips the badge to the file's verdict once the set is decided", () => {
|
|
482
|
+
const kept = renderStampedRow(
|
|
483
|
+
reviewSet(FileChangeSetStatus.RECONCILED, [{ action: FileDecisionAction.APPROVE }]),
|
|
484
|
+
);
|
|
485
|
+
expect(badge(kept.container)?.textContent).toBe("Kept");
|
|
486
|
+
cleanup();
|
|
487
|
+
|
|
488
|
+
const discarded = renderStampedRow(
|
|
489
|
+
reviewSet(FileChangeSetStatus.RECONCILED, [{ action: FileDecisionAction.REJECT }]),
|
|
490
|
+
);
|
|
491
|
+
expect(badge(discarded.container)?.textContent).toBe("Discarded");
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it("shows no badge for an unstamped row or without a provider", () => {
|
|
495
|
+
// Unstamped (a plain edit outside capture mode) — provider present.
|
|
496
|
+
const unstamped = makeToolCall({
|
|
497
|
+
id: "tc-plain",
|
|
498
|
+
name: "edit",
|
|
499
|
+
args: { path: "notes.md", old_string: "a", new_string: "b" },
|
|
500
|
+
});
|
|
501
|
+
const withProvider = render(
|
|
502
|
+
<FileReviewContext.Provider
|
|
503
|
+
value={{ changeSetsById: new Map([[SET_ID, reviewSet(FileChangeSetStatus.AWAITING_REVIEW)]]) }}
|
|
504
|
+
>
|
|
505
|
+
<ToolCallItem toolCall={unstamped} />
|
|
506
|
+
</FileReviewContext.Provider>,
|
|
507
|
+
);
|
|
508
|
+
expect(badge(withProvider.container)).toBeNull();
|
|
509
|
+
cleanup();
|
|
510
|
+
|
|
511
|
+
// Stamped but no provider (a consumer without file review) — no badge.
|
|
512
|
+
const stamped = makeToolCall({
|
|
513
|
+
id: "tc-edit",
|
|
514
|
+
name: "edit",
|
|
515
|
+
args: { path: "notes.md", old_string: "a", new_string: "b" },
|
|
516
|
+
fileChangeSetId: SET_ID,
|
|
517
|
+
});
|
|
518
|
+
const bare = render(<ToolCallItem toolCall={stamped} />);
|
|
519
|
+
expect(badge(bare.container)).toBeNull();
|
|
520
|
+
});
|
|
521
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
|
|
3
|
+
import { create, type JsonObject } from "@bufbuild/protobuf";
|
|
4
|
+
import {
|
|
5
|
+
ToolCallSchema,
|
|
6
|
+
type ToolCall,
|
|
7
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
8
|
+
import {
|
|
9
|
+
PendingApprovalSchema,
|
|
10
|
+
type PendingApproval,
|
|
11
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/approval_pb";
|
|
12
|
+
import { ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
13
|
+
import { ToolRunGroup } from "../ToolRunGroup";
|
|
14
|
+
import { ApprovalContext, type ApprovalContextValue } from "../ApprovalContext";
|
|
15
|
+
|
|
16
|
+
afterEach(cleanup);
|
|
17
|
+
|
|
18
|
+
function makeRead(id: string, path: string, status?: ToolCallStatus): ToolCall {
|
|
19
|
+
return create(ToolCallSchema, {
|
|
20
|
+
id,
|
|
21
|
+
name: "Read",
|
|
22
|
+
args: { path } as JsonObject,
|
|
23
|
+
status: status ?? ToolCallStatus.TOOL_CALL_COMPLETED,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function chipExpanded(container: HTMLElement): boolean {
|
|
28
|
+
return (
|
|
29
|
+
container.querySelector("[aria-expanded]")?.getAttribute("aria-expanded") ===
|
|
30
|
+
"true"
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe("ToolRunGroup", () => {
|
|
35
|
+
it("renders a category-aware collapsed label and hides the rows", () => {
|
|
36
|
+
const { container } = render(
|
|
37
|
+
<ToolRunGroup
|
|
38
|
+
category="read"
|
|
39
|
+
toolCalls={[
|
|
40
|
+
makeRead("r1", "/a.ts"),
|
|
41
|
+
makeRead("r2", "/b.ts"),
|
|
42
|
+
makeRead("r3", "/c.ts"),
|
|
43
|
+
]}
|
|
44
|
+
/>,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(screen.getByText("Read 3 files")).toBeTruthy();
|
|
48
|
+
expect(chipExpanded(container)).toBe(false);
|
|
49
|
+
// Rows are not rendered while folded.
|
|
50
|
+
expect(screen.queryByText("/a.ts")).toBeNull();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("reveals the folded rows on expand", () => {
|
|
54
|
+
render(
|
|
55
|
+
<ToolRunGroup
|
|
56
|
+
category="read"
|
|
57
|
+
toolCalls={[makeRead("r1", "/a.ts"), makeRead("r2", "/b.ts")]}
|
|
58
|
+
/>,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
fireEvent.click(screen.getByText("Read 2 files"));
|
|
62
|
+
// Read rows render filename-first (the leading "/" is a dimmed-or-hidden dir).
|
|
63
|
+
expect(screen.getByText("a.ts")).toBeTruthy();
|
|
64
|
+
expect(screen.getByText("b.ts")).toBeTruthy();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("settles closed when the run has completed", () => {
|
|
68
|
+
const { container } = render(
|
|
69
|
+
<ToolRunGroup
|
|
70
|
+
category="read"
|
|
71
|
+
toolCalls={[makeRead("r1", "/a.ts"), makeRead("r2", "/b.ts")]}
|
|
72
|
+
/>,
|
|
73
|
+
);
|
|
74
|
+
expect(chipExpanded(container)).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("auto-opens when a folded call is awaiting approval, so the gate is reachable", () => {
|
|
78
|
+
const gated = makeRead("r2", "/b.ts", ToolCallStatus.TOOL_CALL_WAITING_APPROVAL);
|
|
79
|
+
const approval: PendingApproval = create(PendingApprovalSchema, {
|
|
80
|
+
toolCallId: "r2",
|
|
81
|
+
toolName: "Read",
|
|
82
|
+
argsPreview: '{"path":"/b.ts"}',
|
|
83
|
+
});
|
|
84
|
+
const ctx: ApprovalContextValue = {
|
|
85
|
+
approvalsByToolCallId: new Map([["r2", approval]]),
|
|
86
|
+
onSubmit: () => {},
|
|
87
|
+
submittingIds: new Set(),
|
|
88
|
+
errorsByToolCallId: new Map(),
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const { container } = render(
|
|
92
|
+
<ApprovalContext.Provider value={ctx}>
|
|
93
|
+
<ToolRunGroup category="read" toolCalls={[makeRead("r1", "/a.ts"), gated]} />
|
|
94
|
+
</ApprovalContext.Provider>,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
expect(chipExpanded(container)).toBe(true);
|
|
98
|
+
// The gate renders inline on its row inside the opened chip.
|
|
99
|
+
expect(screen.getByLabelText("Approve")).toBeTruthy();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("is a bordered card whose expanded children are borderless divider rows", () => {
|
|
103
|
+
const { container } = render(
|
|
104
|
+
<ToolRunGroup
|
|
105
|
+
category="read"
|
|
106
|
+
toolCalls={[makeRead("r1", "/a.ts"), makeRead("r2", "/b.ts")]}
|
|
107
|
+
/>,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const chip = container.querySelector('[data-cursor-target="tool-run-group"]')!;
|
|
111
|
+
expect(chip.className).toContain("rounded-lg");
|
|
112
|
+
// Class presence only — actual rendering is guarded by the layer-invariant +
|
|
113
|
+
// e2e computed-style tests (happy-dom does not resolve `@layer`).
|
|
114
|
+
expect(chip.className).toContain("border-border-prominent");
|
|
115
|
+
|
|
116
|
+
fireEvent.click(screen.getByText("Read 2 files"));
|
|
117
|
+
|
|
118
|
+
// The chip is the card; its child rows are dividers, never nested cards.
|
|
119
|
+
const rows = container.querySelectorAll('[data-cursor-target="tool-call-row"]');
|
|
120
|
+
expect(rows.length).toBe(2);
|
|
121
|
+
for (const row of rows) {
|
|
122
|
+
expect(row.className).not.toContain("rounded-lg");
|
|
123
|
+
expect(row.className).toContain("border-b");
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("honours a custom label formatter", () => {
|
|
128
|
+
render(
|
|
129
|
+
<ToolRunGroup
|
|
130
|
+
category="read"
|
|
131
|
+
toolCalls={[makeRead("r1", "/a.ts"), makeRead("r2", "/b.ts")]}
|
|
132
|
+
formatLabel={(calls) => `Inspected ${calls.length} sources`}
|
|
133
|
+
/>,
|
|
134
|
+
);
|
|
135
|
+
expect(screen.getByText("Inspected 2 sources")).toBeTruthy();
|
|
136
|
+
});
|
|
137
|
+
});
|