@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,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sub-agent file-review badges (DD-24/T05_0 follow-up).
|
|
3
|
+
*
|
|
4
|
+
* A sub-agent's flowed file-edit row is stamped by the runner with the PARENT
|
|
5
|
+
* turn's change set id (sub-agent writes fold into the parent set). These tests
|
|
6
|
+
* lock the React side: a stamped row rendered inside a `SubAgentSection`
|
|
7
|
+
* resolves its review-state badge against the session-wide `FileReviewContext`
|
|
8
|
+
* map, and — end to end through `MessageThread` — that map is built from the
|
|
9
|
+
* parent execution's `status.fileChangeSets` and reaches the nested row.
|
|
10
|
+
*
|
|
11
|
+
* The per-row badge resolution itself (pending/kept/discarded/none) is covered
|
|
12
|
+
* exhaustively in `ToolCallItem.test.tsx`; here the emphasis is the SUB-AGENT
|
|
13
|
+
* wiring — the novel path this change enables.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
17
|
+
import { render, screen, cleanup } from "@testing-library/react";
|
|
18
|
+
import { create } from "@bufbuild/protobuf";
|
|
19
|
+
import {
|
|
20
|
+
AgentExecutionSchema,
|
|
21
|
+
AgentExecutionStatusSchema,
|
|
22
|
+
type AgentExecution,
|
|
23
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
24
|
+
import { AgentExecutionSpecSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/spec_pb";
|
|
25
|
+
import { ApiResourceMetadataSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
|
|
26
|
+
import {
|
|
27
|
+
AgentMessageSchema,
|
|
28
|
+
ToolCallSchema,
|
|
29
|
+
FileContentSchema,
|
|
30
|
+
type AgentMessage,
|
|
31
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
32
|
+
import {
|
|
33
|
+
SubAgentExecutionSchema,
|
|
34
|
+
type SubAgentExecution,
|
|
35
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/subagent_pb";
|
|
36
|
+
import {
|
|
37
|
+
CapturedFileChangeSchema,
|
|
38
|
+
FileChangeSetSchema,
|
|
39
|
+
FileDecisionSchema,
|
|
40
|
+
type FileChangeSet,
|
|
41
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
|
|
42
|
+
import {
|
|
43
|
+
DiffCompleteness,
|
|
44
|
+
ExecutionPhase,
|
|
45
|
+
FileChangeKind,
|
|
46
|
+
FileChangeSetStatus,
|
|
47
|
+
FileDecisionAction,
|
|
48
|
+
FileDecisionScope,
|
|
49
|
+
MessageType,
|
|
50
|
+
SubAgentStatus,
|
|
51
|
+
ToolCallStatus,
|
|
52
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
53
|
+
import { SubAgentSection } from "../SubAgentSection";
|
|
54
|
+
import { ApprovalContext, type ApprovalContextValue } from "../ApprovalContext";
|
|
55
|
+
import { FileReviewContext } from "../FileReviewContext";
|
|
56
|
+
import { MessageThread } from "../MessageThread";
|
|
57
|
+
|
|
58
|
+
const SET_ID = "exec-1:0";
|
|
59
|
+
const ROW_PATH = "src/a.ts";
|
|
60
|
+
|
|
61
|
+
const emptyApprovalCtx: ApprovalContextValue = {
|
|
62
|
+
approvalsByToolCallId: new Map(),
|
|
63
|
+
submittingIds: new Set(),
|
|
64
|
+
errorsByToolCallId: new Map(),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
afterEach(cleanup);
|
|
68
|
+
|
|
69
|
+
/** A sub-agent whose message carries one stamped, flowed file-edit row. */
|
|
70
|
+
function subAgentWithStampedEdit(
|
|
71
|
+
status: SubAgentStatus,
|
|
72
|
+
fileChangeSetId: string,
|
|
73
|
+
rowPath = ROW_PATH,
|
|
74
|
+
): SubAgentExecution {
|
|
75
|
+
return create(SubAgentExecutionSchema, {
|
|
76
|
+
id: "sa-1",
|
|
77
|
+
name: "code_editor",
|
|
78
|
+
status,
|
|
79
|
+
messages: [
|
|
80
|
+
create(AgentMessageSchema, {
|
|
81
|
+
type: MessageType.MESSAGE_AI,
|
|
82
|
+
toolCalls: [
|
|
83
|
+
create(ToolCallSchema, {
|
|
84
|
+
id: "sa-tc-edit",
|
|
85
|
+
name: "edit",
|
|
86
|
+
status: ToolCallStatus.TOOL_CALL_COMPLETED,
|
|
87
|
+
args: { path: rowPath, old_string: "a", new_string: "b" },
|
|
88
|
+
fileChangeSetId,
|
|
89
|
+
}),
|
|
90
|
+
],
|
|
91
|
+
}),
|
|
92
|
+
],
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** A change set carrying one change for {@link ROW_PATH}, with optional verdict. */
|
|
97
|
+
function reviewSet(
|
|
98
|
+
status: FileChangeSetStatus,
|
|
99
|
+
decisions: FileDecisionAction[] = [],
|
|
100
|
+
): FileChangeSet {
|
|
101
|
+
return create(FileChangeSetSchema, {
|
|
102
|
+
id: SET_ID,
|
|
103
|
+
status,
|
|
104
|
+
aggregateDigest: "agg-1",
|
|
105
|
+
diffCompleteness: DiffCompleteness.COMPLETE,
|
|
106
|
+
changes: [
|
|
107
|
+
create(CapturedFileChangeSchema, {
|
|
108
|
+
id: `${SET_ID}:${ROW_PATH}`,
|
|
109
|
+
pathBefore: ROW_PATH,
|
|
110
|
+
pathAfter: ROW_PATH,
|
|
111
|
+
kind: FileChangeKind.MODIFY,
|
|
112
|
+
before: create(FileContentSchema, { body: { case: "inline", value: "old\n" } }),
|
|
113
|
+
after: create(FileContentSchema, { body: { case: "inline", value: "new\n" } }),
|
|
114
|
+
fileDigest: "d-a",
|
|
115
|
+
diffComplete: true,
|
|
116
|
+
}),
|
|
117
|
+
],
|
|
118
|
+
decisions: decisions.map((action) =>
|
|
119
|
+
create(FileDecisionSchema, {
|
|
120
|
+
changeSetId: SET_ID,
|
|
121
|
+
scope: FileDecisionScope.CHANGE_SET,
|
|
122
|
+
action,
|
|
123
|
+
}),
|
|
124
|
+
),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function renderSubAgentRow(sub: SubAgentExecution, set: FileChangeSet | null) {
|
|
129
|
+
const map = set ? new Map([[set.id, set]]) : new Map<string, FileChangeSet>();
|
|
130
|
+
return render(
|
|
131
|
+
<ApprovalContext.Provider value={emptyApprovalCtx}>
|
|
132
|
+
<FileReviewContext.Provider value={{ changeSetsById: map }}>
|
|
133
|
+
{/* collapsible=false renders the nested rows flat — deterministic, no
|
|
134
|
+
dependence on the card's auto-disclosure state. */}
|
|
135
|
+
<SubAgentSection subAgentExecution={sub} collapsible={false} />
|
|
136
|
+
</FileReviewContext.Provider>
|
|
137
|
+
</ApprovalContext.Provider>,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function badge(container: HTMLElement): Element | null {
|
|
142
|
+
return container.querySelector('[data-cursor-target="file-review-row-badge"]');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
describe("sub-agent row badges (SubAgentSection)", () => {
|
|
146
|
+
it("badges a stamped sub-agent edit row 'Pending review' while its set awaits review", () => {
|
|
147
|
+
const { container } = renderSubAgentRow(
|
|
148
|
+
subAgentWithStampedEdit(SubAgentStatus.SUB_AGENT_COMPLETED, SET_ID),
|
|
149
|
+
reviewSet(FileChangeSetStatus.AWAITING_REVIEW),
|
|
150
|
+
);
|
|
151
|
+
expect(badge(container)?.textContent).toBe("Pending review");
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("flips a sub-agent row to the file's verdict once the set is decided", () => {
|
|
155
|
+
const kept = renderSubAgentRow(
|
|
156
|
+
subAgentWithStampedEdit(SubAgentStatus.SUB_AGENT_COMPLETED, SET_ID),
|
|
157
|
+
reviewSet(FileChangeSetStatus.RECONCILED, [FileDecisionAction.APPROVE]),
|
|
158
|
+
);
|
|
159
|
+
expect(badge(kept.container)?.textContent).toBe("Kept");
|
|
160
|
+
cleanup();
|
|
161
|
+
|
|
162
|
+
const discarded = renderSubAgentRow(
|
|
163
|
+
subAgentWithStampedEdit(SubAgentStatus.SUB_AGENT_COMPLETED, SET_ID),
|
|
164
|
+
reviewSet(FileChangeSetStatus.RECONCILED, [FileDecisionAction.REJECT]),
|
|
165
|
+
);
|
|
166
|
+
expect(badge(discarded.container)?.textContent).toBe("Discarded");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("shows no badge when the stamped set cannot be resolved (graceful degrade, DD-24 D3)", () => {
|
|
170
|
+
// Row stamped with a set id absent from the session map.
|
|
171
|
+
const orphan = renderSubAgentRow(
|
|
172
|
+
subAgentWithStampedEdit(SubAgentStatus.SUB_AGENT_COMPLETED, "exec-1:missing"),
|
|
173
|
+
reviewSet(FileChangeSetStatus.AWAITING_REVIEW),
|
|
174
|
+
);
|
|
175
|
+
expect(badge(orphan.container)).toBeNull();
|
|
176
|
+
cleanup();
|
|
177
|
+
|
|
178
|
+
// Row stamped with the right (settled) set, but its path is not in the set's
|
|
179
|
+
// changes: the per-file verdict cannot be resolved, so no badge. (An
|
|
180
|
+
// AWAITING_REVIEW set badges every stamped row 'pending' regardless of path —
|
|
181
|
+
// path resolution only decides the verdict of a SETTLED set.)
|
|
182
|
+
const pathMiss = renderSubAgentRow(
|
|
183
|
+
subAgentWithStampedEdit(SubAgentStatus.SUB_AGENT_COMPLETED, SET_ID, "src/other.ts"),
|
|
184
|
+
reviewSet(FileChangeSetStatus.RECONCILED, [FileDecisionAction.APPROVE]),
|
|
185
|
+
);
|
|
186
|
+
expect(badge(pathMiss.container)).toBeNull();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
// End-to-end through MessageThread: proves the session-wide FileReviewContext
|
|
192
|
+
// map is built from the PARENT execution's status.fileChangeSets and reaches a
|
|
193
|
+
// stamped row nested inside a sub-agent (the map + provider + nested-render path).
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
describe("sub-agent row badges (MessageThread integration)", () => {
|
|
197
|
+
beforeEach(() => {
|
|
198
|
+
// useAutoScroll depends on browser APIs absent in happy-dom.
|
|
199
|
+
vi.stubGlobal(
|
|
200
|
+
"IntersectionObserver",
|
|
201
|
+
vi.fn(() => ({
|
|
202
|
+
observe: vi.fn(),
|
|
203
|
+
unobserve: vi.fn(),
|
|
204
|
+
disconnect: vi.fn(),
|
|
205
|
+
takeRecords: vi.fn(() => []),
|
|
206
|
+
root: null,
|
|
207
|
+
rootMargin: "",
|
|
208
|
+
thresholds: [0],
|
|
209
|
+
})),
|
|
210
|
+
);
|
|
211
|
+
vi.stubGlobal(
|
|
212
|
+
"ResizeObserver",
|
|
213
|
+
vi.fn(() => ({ observe: vi.fn(), unobserve: vi.fn(), disconnect: vi.fn() })),
|
|
214
|
+
);
|
|
215
|
+
vi.stubGlobal(
|
|
216
|
+
"requestAnimationFrame",
|
|
217
|
+
vi.fn((cb: FrameRequestCallback) => {
|
|
218
|
+
cb(performance.now());
|
|
219
|
+
return 1;
|
|
220
|
+
}),
|
|
221
|
+
);
|
|
222
|
+
vi.stubGlobal("cancelAnimationFrame", vi.fn());
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
afterEach(() => {
|
|
226
|
+
vi.restoreAllMocks();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* A live execution that delegated to a sub-agent which edited a file this
|
|
231
|
+
* turn. The parent carries the `task` tool call (matched to the sub-agent by
|
|
232
|
+
* id) and the change set on `status.fileChangeSets`; the sub-agent (running,
|
|
233
|
+
* so its card auto-opens) carries the stamped edit row.
|
|
234
|
+
*/
|
|
235
|
+
function makeExecWithSubAgentEdit(): AgentExecution {
|
|
236
|
+
const exec = create(AgentExecutionSchema);
|
|
237
|
+
exec.metadata = create(ApiResourceMetadataSchema, { id: "exec-1" });
|
|
238
|
+
exec.spec = create(AgentExecutionSpecSchema, { message: "Delegate an edit" });
|
|
239
|
+
|
|
240
|
+
const status = create(AgentExecutionStatusSchema);
|
|
241
|
+
status.phase = ExecutionPhase.EXECUTION_IN_PROGRESS;
|
|
242
|
+
const aiMsg: AgentMessage = create(AgentMessageSchema, {
|
|
243
|
+
type: MessageType.MESSAGE_AI,
|
|
244
|
+
content: "Delegating.",
|
|
245
|
+
toolCalls: [
|
|
246
|
+
create(ToolCallSchema, {
|
|
247
|
+
id: "sa-1", // matches the SubAgentExecution id
|
|
248
|
+
name: "task",
|
|
249
|
+
status: ToolCallStatus.TOOL_CALL_COMPLETED,
|
|
250
|
+
}),
|
|
251
|
+
],
|
|
252
|
+
});
|
|
253
|
+
status.messages = [aiMsg];
|
|
254
|
+
status.subAgentExecutions = [
|
|
255
|
+
subAgentWithStampedEdit(SubAgentStatus.SUB_AGENT_IN_PROGRESS, SET_ID),
|
|
256
|
+
];
|
|
257
|
+
status.fileChangeSets = [reviewSet(FileChangeSetStatus.AWAITING_REVIEW)];
|
|
258
|
+
exec.status = status;
|
|
259
|
+
return exec;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
it("badges a stamped sub-agent row from a set on the parent execution's fileChangeSets", () => {
|
|
263
|
+
render(
|
|
264
|
+
<MessageThread
|
|
265
|
+
executions={[]}
|
|
266
|
+
activeStreamExecution={makeExecWithSubAgentEdit()}
|
|
267
|
+
showFileReviewRecords
|
|
268
|
+
/>,
|
|
269
|
+
);
|
|
270
|
+
const el = document.querySelector('[data-cursor-target="file-review-row-badge"]');
|
|
271
|
+
expect(el?.textContent).toBe("Pending review");
|
|
272
|
+
});
|
|
273
|
+
});
|
|
@@ -12,9 +12,14 @@ import {
|
|
|
12
12
|
ToolCallSchema,
|
|
13
13
|
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
14
14
|
import { SubAgentExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/subagent_pb";
|
|
15
|
+
import {
|
|
16
|
+
TodoItemSchema,
|
|
17
|
+
type TodoItem,
|
|
18
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/todo_pb";
|
|
15
19
|
import {
|
|
16
20
|
ExecutionPhase,
|
|
17
21
|
MessageType,
|
|
22
|
+
TodoStatus,
|
|
18
23
|
ToolCallStatus,
|
|
19
24
|
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
20
25
|
import { buildThreadItems } from "../MessageThread";
|
|
@@ -55,12 +60,24 @@ function makeSubAgent(id: string) {
|
|
|
55
60
|
return sa;
|
|
56
61
|
}
|
|
57
62
|
|
|
63
|
+
function makeTodo(id: string, content: string, status: TodoStatus): TodoItem {
|
|
64
|
+
return create(TodoItemSchema, { id, content, status });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Build the `status.todos` proto map from a list of items, keyed by id. */
|
|
68
|
+
function todoMap(items: TodoItem[]): { [id: string]: TodoItem } {
|
|
69
|
+
const map: { [id: string]: TodoItem } = {};
|
|
70
|
+
for (const t of items) map[t.id] = t;
|
|
71
|
+
return map;
|
|
72
|
+
}
|
|
73
|
+
|
|
58
74
|
function makeExecution(opts: {
|
|
59
75
|
id: string;
|
|
60
76
|
specMessage?: string;
|
|
61
77
|
phase?: ExecutionPhase;
|
|
62
78
|
messages?: ReturnType<typeof makeMessage>[];
|
|
63
79
|
subAgents?: ReturnType<typeof makeSubAgent>[];
|
|
80
|
+
todos?: { [id: string]: TodoItem };
|
|
64
81
|
}): AgentExecution {
|
|
65
82
|
const exec = create(AgentExecutionSchema);
|
|
66
83
|
|
|
@@ -82,6 +99,9 @@ function makeExecution(opts: {
|
|
|
82
99
|
if (opts.subAgents) {
|
|
83
100
|
status.subAgentExecutions = opts.subAgents;
|
|
84
101
|
}
|
|
102
|
+
if (opts.todos) {
|
|
103
|
+
status.todos = opts.todos;
|
|
104
|
+
}
|
|
85
105
|
exec.status = status;
|
|
86
106
|
|
|
87
107
|
return exec;
|
|
@@ -612,3 +632,178 @@ describe("buildThreadItems key generation", () => {
|
|
|
612
632
|
});
|
|
613
633
|
});
|
|
614
634
|
});
|
|
635
|
+
|
|
636
|
+
// ---------------------------------------------------------------------------
|
|
637
|
+
// Inline todos card
|
|
638
|
+
// ---------------------------------------------------------------------------
|
|
639
|
+
|
|
640
|
+
describe("buildThreadItems todos card", () => {
|
|
641
|
+
it("emits no todos card when status.todos is empty", () => {
|
|
642
|
+
const exec = makeExecution({
|
|
643
|
+
id: "exec-no-todos",
|
|
644
|
+
messages: [makeMessage(MessageType.MESSAGE_AI, "Done")],
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
const items = buildThreadItems([exec], null, null, false, undefined);
|
|
648
|
+
expect(items.filter((i) => i.kind === "todos")).toHaveLength(0);
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
it("anchors the card immediately after the opening AI message (anchor 1)", () => {
|
|
652
|
+
const exec = makeExecution({
|
|
653
|
+
id: "exec-a1",
|
|
654
|
+
messages: [
|
|
655
|
+
makeMessage(MessageType.MESSAGE_AI, "Here is my plan"),
|
|
656
|
+
makeMessage(MessageType.MESSAGE_AI, "Working on it"),
|
|
657
|
+
],
|
|
658
|
+
todos: todoMap([makeTodo("t1", "Step 1", TodoStatus.TODO_IN_PROGRESS)]),
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
const items = buildThreadItems([exec], null, null, false, undefined);
|
|
662
|
+
const keys = extractKeys(items);
|
|
663
|
+
// ...-m0 (opening AI) -> todos -> ...-m1
|
|
664
|
+
expect(keys).toEqual(["exec-a1-m0", "exec-a1-todos", "exec-a1-m1"]);
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
it("anchors the card after the opening thinking message", () => {
|
|
668
|
+
const exec = makeExecution({
|
|
669
|
+
id: "exec-think",
|
|
670
|
+
messages: [
|
|
671
|
+
makeMessage(MessageType.MESSAGE_THINKING, "Let me think"),
|
|
672
|
+
makeMessage(MessageType.MESSAGE_AI, "Answer"),
|
|
673
|
+
],
|
|
674
|
+
todos: todoMap([makeTodo("t1", "Step 1", TodoStatus.TODO_PENDING)]),
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
const items = buildThreadItems([exec], null, null, false, undefined);
|
|
678
|
+
const keys = extractKeys(items);
|
|
679
|
+
expect(keys).toEqual(["exec-think-m0", "exec-think-todos", "exec-think-m1"]);
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
it("anchors before the first tool-group when work precedes any narration (anchor 2)", () => {
|
|
683
|
+
// An empty-content AI message that only carries a Shell tool call: no
|
|
684
|
+
// rendered narration, so the plan must lead the work.
|
|
685
|
+
const shellTc = makeToolCall("Shell", "tc-shell");
|
|
686
|
+
const exec = makeExecution({
|
|
687
|
+
id: "exec-a2",
|
|
688
|
+
messages: [
|
|
689
|
+
makeMessage(MessageType.MESSAGE_AI, " ", { toolCalls: [shellTc] }),
|
|
690
|
+
],
|
|
691
|
+
todos: todoMap([makeTodo("t1", "Step 1", TodoStatus.TODO_IN_PROGRESS)]),
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
const items = buildThreadItems([exec], null, null, false, undefined);
|
|
695
|
+
const keys = extractKeys(items);
|
|
696
|
+
expect(keys).toEqual(["exec-a2-todos", "exec-a2-m0-tc"]);
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
it("falls back to the turn tail when there is no narration or work (anchor 3)", () => {
|
|
700
|
+
// Only a suppressed/internal todo tool call, empty content: nothing renders
|
|
701
|
+
// except the plan itself, which must still surface.
|
|
702
|
+
const todoTc = makeToolCall("updateTodos", "tc-todo");
|
|
703
|
+
const exec = makeExecution({
|
|
704
|
+
id: "exec-a3",
|
|
705
|
+
messages: [
|
|
706
|
+
makeMessage(MessageType.MESSAGE_AI, " ", { toolCalls: [todoTc] }),
|
|
707
|
+
],
|
|
708
|
+
todos: todoMap([makeTodo("t1", "Step 1", TodoStatus.TODO_COMPLETED)]),
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
const items = buildThreadItems([exec], null, null, false, undefined);
|
|
712
|
+
const todoItems = items.filter((i) => i.kind === "todos");
|
|
713
|
+
expect(todoItems).toHaveLength(1);
|
|
714
|
+
expect(todoItems[0].key).toBe("exec-a3-todos");
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
it("emits exactly one card per execution even with multiple AI messages", () => {
|
|
718
|
+
const exec = makeExecution({
|
|
719
|
+
id: "exec-one",
|
|
720
|
+
messages: [
|
|
721
|
+
makeMessage(MessageType.MESSAGE_AI, "First"),
|
|
722
|
+
makeMessage(MessageType.MESSAGE_AI, "Second"),
|
|
723
|
+
makeMessage(MessageType.MESSAGE_AI, "Third"),
|
|
724
|
+
],
|
|
725
|
+
todos: todoMap([makeTodo("t1", "Step 1", TodoStatus.TODO_PENDING)]),
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
const items = buildThreadItems([exec], null, null, false, undefined);
|
|
729
|
+
expect(items.filter((i) => i.kind === "todos")).toHaveLength(1);
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
it("emits one card per turn across a multi-turn session", () => {
|
|
733
|
+
const turn1 = makeExecution({
|
|
734
|
+
id: "exec-1",
|
|
735
|
+
messages: [makeMessage(MessageType.MESSAGE_AI, "Plan A")],
|
|
736
|
+
todos: todoMap([makeTodo("t1", "A", TodoStatus.TODO_COMPLETED)]),
|
|
737
|
+
});
|
|
738
|
+
// A follow-up turn that did not write todos shows no card.
|
|
739
|
+
const turn2 = makeExecution({
|
|
740
|
+
id: "exec-2",
|
|
741
|
+
messages: [makeMessage(MessageType.MESSAGE_AI, "No plan here")],
|
|
742
|
+
});
|
|
743
|
+
const turn3 = makeExecution({
|
|
744
|
+
id: "exec-3",
|
|
745
|
+
messages: [makeMessage(MessageType.MESSAGE_AI, "Plan C")],
|
|
746
|
+
todos: todoMap([makeTodo("t1", "C", TodoStatus.TODO_IN_PROGRESS)]),
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
const items = buildThreadItems([turn1, turn2, turn3], null, null, false, undefined);
|
|
750
|
+
const todoKeys = items.filter((i) => i.kind === "todos").map((i) => i.key);
|
|
751
|
+
expect(todoKeys).toEqual(["exec-1-todos", "exec-3-todos"]);
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
it("carries the live status.todos reference (no clone) for memoization", () => {
|
|
755
|
+
const todos = todoMap([makeTodo("t1", "Step 1", TodoStatus.TODO_PENDING)]);
|
|
756
|
+
const exec = makeExecution({
|
|
757
|
+
id: "exec-ref",
|
|
758
|
+
messages: [makeMessage(MessageType.MESSAGE_AI, "Plan")],
|
|
759
|
+
todos,
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
const items = buildThreadItems([exec], null, null, false, undefined);
|
|
763
|
+
const todoItem = items.find((i) => i.kind === "todos");
|
|
764
|
+
expect(todoItem?.kind === "todos" && todoItem.todos).toBe(exec.status!.todos);
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
it("native-harness shape: filters the write_todos tool call but still shows the card", () => {
|
|
768
|
+
// Native keeps write_todos as a (filtered) tool call AND populates status.todos.
|
|
769
|
+
const writeTodosTc = makeToolCall("write_todos", "tc-wt");
|
|
770
|
+
const exec = makeExecution({
|
|
771
|
+
id: "exec-native",
|
|
772
|
+
messages: [
|
|
773
|
+
makeMessage(MessageType.MESSAGE_AI, "Planning", {
|
|
774
|
+
toolCalls: [writeTodosTc],
|
|
775
|
+
}),
|
|
776
|
+
],
|
|
777
|
+
todos: todoMap([makeTodo("t1", "Step 1", TodoStatus.TODO_IN_PROGRESS)]),
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
const items = buildThreadItems([exec], null, null, false, undefined);
|
|
781
|
+
expect(items.filter((i) => i.kind === "tool-group")).toHaveLength(0);
|
|
782
|
+
expect(items.filter((i) => i.kind === "todos")).toHaveLength(1);
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
it("keeps a stable key as todos update (no remount)", () => {
|
|
786
|
+
const before = makeExecution({
|
|
787
|
+
id: "exec-stable",
|
|
788
|
+
messages: [makeMessage(MessageType.MESSAGE_AI, "Plan")],
|
|
789
|
+
todos: todoMap([makeTodo("t1", "Step 1", TodoStatus.TODO_PENDING)]),
|
|
790
|
+
});
|
|
791
|
+
const after = makeExecution({
|
|
792
|
+
id: "exec-stable",
|
|
793
|
+
messages: [makeMessage(MessageType.MESSAGE_AI, "Plan")],
|
|
794
|
+
todos: todoMap([
|
|
795
|
+
makeTodo("t1", "Step 1", TodoStatus.TODO_COMPLETED),
|
|
796
|
+
makeTodo("t2", "Step 2", TodoStatus.TODO_IN_PROGRESS),
|
|
797
|
+
]),
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
const k1 = buildThreadItems([before], null, null, false, undefined).find(
|
|
801
|
+
(i) => i.kind === "todos",
|
|
802
|
+
)?.key;
|
|
803
|
+
const k2 = buildThreadItems([after], null, null, false, undefined).find(
|
|
804
|
+
(i) => i.kind === "todos",
|
|
805
|
+
)?.key;
|
|
806
|
+
expect(k1).toBe("exec-stable-todos");
|
|
807
|
+
expect(k2).toBe("exec-stable-todos");
|
|
808
|
+
});
|
|
809
|
+
});
|
|
@@ -75,4 +75,99 @@ describe("useToolPresentation", () => {
|
|
|
75
75
|
const { result } = renderHook(() => useToolPresentation(tc));
|
|
76
76
|
expect(result.current.label).toBe("Shell");
|
|
77
77
|
});
|
|
78
|
+
|
|
79
|
+
describe("disclosure", () => {
|
|
80
|
+
it("keeps known compact tools as summary", () => {
|
|
81
|
+
// Shell/edit/write are intentionally excluded — they are `preview`
|
|
82
|
+
// categories (output / diff is the information), asserted separately below.
|
|
83
|
+
for (const name of ["Read", "Grep", "Glob"]) {
|
|
84
|
+
const tc = makeToolCall({ name, args: { path: "/x", command: "ls", pattern: "p" } });
|
|
85
|
+
const { result } = renderHook(() => useToolPresentation(tc));
|
|
86
|
+
expect(result.current.disclosure).toBe("summary");
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("previews file edits and writes (the diff is the information)", () => {
|
|
91
|
+
for (const name of ["StrReplace", "Write"]) {
|
|
92
|
+
const tc = makeToolCall({
|
|
93
|
+
name,
|
|
94
|
+
args: { path: "/x.ts", contents: "x", old_string: "a", new_string: "b" },
|
|
95
|
+
});
|
|
96
|
+
const { result } = renderHook(() => useToolPresentation(tc));
|
|
97
|
+
expect(result.current.disclosure).toBe("preview");
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("keeps a shell tool's output as a persistent preview", () => {
|
|
102
|
+
const tc = makeToolCall({ name: "Shell", args: { command: "ls" }, result: "files" });
|
|
103
|
+
const { result } = renderHook(() => useToolPresentation(tc));
|
|
104
|
+
expect(result.current.category).toBe("shell");
|
|
105
|
+
expect(result.current.disclosure).toBe("preview");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("foregrounds MCP tools as preview", () => {
|
|
109
|
+
const tc = makeToolCall({
|
|
110
|
+
name: "send_message",
|
|
111
|
+
mcpServerSlug: "acme/slack",
|
|
112
|
+
args: { channel: "general" },
|
|
113
|
+
});
|
|
114
|
+
const { result } = renderHook(() => useToolPresentation(tc));
|
|
115
|
+
expect(result.current.category).toBe("mcp");
|
|
116
|
+
expect(result.current.disclosure).toBe("preview");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("foregrounds an unrecognized tool as preview", () => {
|
|
120
|
+
const tc = makeToolCall({ name: "frobnicate_widget", args: { x: "1" } });
|
|
121
|
+
const { result } = renderHook(() => useToolPresentation(tc));
|
|
122
|
+
expect(result.current.category).toBe("unknown");
|
|
123
|
+
expect(result.current.disclosure).toBe("preview");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("lets a registered presenter override the disclosure", () => {
|
|
127
|
+
const dispose = registerToolPresenter(ToolKind.MCP, {
|
|
128
|
+
disclosure: () => "summary",
|
|
129
|
+
});
|
|
130
|
+
try {
|
|
131
|
+
const tc = makeToolCall({
|
|
132
|
+
name: "noisy_tool",
|
|
133
|
+
mcpServerSlug: "acme/noisy",
|
|
134
|
+
});
|
|
135
|
+
const { result } = renderHook(() => useToolPresentation(tc));
|
|
136
|
+
expect(result.current.disclosure).toBe("summary");
|
|
137
|
+
} finally {
|
|
138
|
+
dispose();
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("runGroupable", () => {
|
|
144
|
+
it("folds read-only repetitive categories", () => {
|
|
145
|
+
for (const name of ["Read", "Grep", "Glob"]) {
|
|
146
|
+
const tc = makeToolCall({ name, args: { path: "/x", pattern: "p" } });
|
|
147
|
+
const { result } = renderHook(() => useToolPresentation(tc));
|
|
148
|
+
expect(result.current.runGroupable).toBe(true);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("never folds high-signal categories", () => {
|
|
153
|
+
for (const name of ["Shell", "StrReplace"]) {
|
|
154
|
+
const tc = makeToolCall({ name, args: { command: "ls", path: "/x" } });
|
|
155
|
+
const { result } = renderHook(() => useToolPresentation(tc));
|
|
156
|
+
expect(result.current.runGroupable).toBe(false);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("lets a registered presenter override run-grouping", () => {
|
|
161
|
+
const dispose = registerToolPresenter(ToolKind.SHELL, {
|
|
162
|
+
runGroupable: () => true,
|
|
163
|
+
});
|
|
164
|
+
try {
|
|
165
|
+
const tc = makeToolCall({ name: "Shell", args: { command: "ls" } });
|
|
166
|
+
const { result } = renderHook(() => useToolPresentation(tc));
|
|
167
|
+
expect(result.current.runGroupable).toBe(true);
|
|
168
|
+
} finally {
|
|
169
|
+
dispose();
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
});
|
|
78
173
|
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { renderHook, act } from "@testing-library/react";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import type { Stigmer } from "@stigmer/sdk";
|
|
5
|
+
import { StigmerContext } from "../../context";
|
|
6
|
+
import { useArtifactDownload } from "../useArtifactDownload";
|
|
7
|
+
|
|
8
|
+
function wrapperFor(stigmer: Stigmer) {
|
|
9
|
+
return ({ children }: { children: ReactNode }) => (
|
|
10
|
+
<StigmerContext.Provider value={stigmer}>{children}</StigmerContext.Provider>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function makeStigmer(getArtifactDownloadUrl = vi.fn()) {
|
|
15
|
+
return { agentExecution: { getArtifactDownloadUrl } } as unknown as Stigmer;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Browser download is triggered via a transient anchor; stub the click so the
|
|
19
|
+
// jsdom navigation no-op doesn't add noise and we can keep the test focused on
|
|
20
|
+
// the URL-minting behavior.
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
vi.spyOn(HTMLAnchorElement.prototype, "click").mockImplementation(() => {});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("useArtifactDownload", () => {
|
|
26
|
+
it("mints a fresh URL at click time and toggles isDownloading", async () => {
|
|
27
|
+
const fn = vi.fn().mockResolvedValue({ downloadUrl: "https://fresh/dl" });
|
|
28
|
+
const { result } = renderHook(() => useArtifactDownload("aex_1"), {
|
|
29
|
+
wrapper: wrapperFor(makeStigmer(fn)),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(result.current.isDownloading).toBe(false);
|
|
33
|
+
await act(async () => {
|
|
34
|
+
await result.current.download("artifacts/aex_1/file.bin", "file.bin");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
38
|
+
const req = fn.mock.calls[0][0];
|
|
39
|
+
expect(req.executionId).toBe("aex_1");
|
|
40
|
+
expect(req.storageKey).toBe("artifacts/aex_1/file.bin");
|
|
41
|
+
expect(result.current.isDownloading).toBe(false);
|
|
42
|
+
expect(result.current.error).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("is a no-op when the execution id is unknown", async () => {
|
|
46
|
+
const fn = vi.fn();
|
|
47
|
+
const { result } = renderHook(() => useArtifactDownload(null), {
|
|
48
|
+
wrapper: wrapperFor(makeStigmer(fn)),
|
|
49
|
+
});
|
|
50
|
+
await act(async () => {
|
|
51
|
+
await result.current.download("artifacts/x/file.bin");
|
|
52
|
+
});
|
|
53
|
+
expect(fn).not.toHaveBeenCalled();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("surfaces an error when the refresh RPC fails", async () => {
|
|
57
|
+
const fn = vi.fn().mockRejectedValue(new Error("boom"));
|
|
58
|
+
const { result } = renderHook(() => useArtifactDownload("aex_1"), {
|
|
59
|
+
wrapper: wrapperFor(makeStigmer(fn)),
|
|
60
|
+
});
|
|
61
|
+
await act(async () => {
|
|
62
|
+
await result.current.download("artifacts/aex_1/file.bin");
|
|
63
|
+
});
|
|
64
|
+
expect(result.current.error?.message).toBe("boom");
|
|
65
|
+
expect(result.current.isDownloading).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
});
|