@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
2
|
+
import { ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
2
3
|
import type { JsonObject } from "@bufbuild/protobuf";
|
|
3
4
|
import { ToolKind, resolveToolKind, resolveToolKindByName } from "@stigmer/sdk";
|
|
4
5
|
|
|
@@ -27,6 +28,96 @@ export type ToolCategory =
|
|
|
27
28
|
| "mcp"
|
|
28
29
|
| "unknown";
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* How much of a settled tool call to reveal inline by default in the thread.
|
|
33
|
+
*
|
|
34
|
+
* - `"summary"` — a compact one-line row. Used when the label + primary arg +
|
|
35
|
+
* result summary already tell the story (`Read foo.ts`, `Edit bar.ts +40 -0`,
|
|
36
|
+
* `Search … 2 matches`). The full detail is one click away.
|
|
37
|
+
* - `"preview"` — the row renders its result body **always** (no card chevron),
|
|
38
|
+
* because the one-liner cannot convey what happened so the content itself IS
|
|
39
|
+
* the information (a shell command's output, an MCP result such as a
|
|
40
|
+
* screenshot, a fetched page, a web-search answer, or an unrecognised tool's
|
|
41
|
+
* output). The body's own in-place reveal expands it beyond the height budget.
|
|
42
|
+
*
|
|
43
|
+
* This is the *disclosure* axis (how much of a result to show); it is
|
|
44
|
+
* orthogonal to {@link isRunGroupableCategory} (whether consecutive same-kind
|
|
45
|
+
* calls fold into one chip). A category can be result-rich but never folded
|
|
46
|
+
* (`shell`), or foldable but never previewed (`read`).
|
|
47
|
+
*/
|
|
48
|
+
export type ToolDisclosure = "summary" | "preview";
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Categories whose result is foregrounded as a persistent bounded preview by
|
|
52
|
+
* default — those for which the compact one-liner cannot convey what happened,
|
|
53
|
+
* so the output itself carries the meaning. All other categories default to
|
|
54
|
+
* `"summary"`.
|
|
55
|
+
*
|
|
56
|
+
* File edits and writes are previewed: a `+N -M` summary says how much changed
|
|
57
|
+
* but not *what* — and the change is the decision-relevant content. Surfacing a
|
|
58
|
+
* bounded diff inline (Cursor-style) is the point of a code agent. The body is
|
|
59
|
+
* height-clamped with a single in-place reveal for the rest, so density stays
|
|
60
|
+
* under control even in turns with many edits.
|
|
61
|
+
*/
|
|
62
|
+
const PREVIEW_CATEGORIES: ReadonlySet<ToolCategory> = new Set<ToolCategory>([
|
|
63
|
+
"shell",
|
|
64
|
+
"edit",
|
|
65
|
+
"write",
|
|
66
|
+
"mcp",
|
|
67
|
+
"web-search",
|
|
68
|
+
"fetch",
|
|
69
|
+
"unknown",
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Low-signal categories whose consecutive runs fold into a single collapsible
|
|
74
|
+
* "Read 5 files" chip in the thread. These are the read-only, repetitive
|
|
75
|
+
* operations whose individual rows are noise in bulk; everything else renders
|
|
76
|
+
* as a persistent row. This is the *run-grouping* axis, orthogonal to
|
|
77
|
+
* {@link ToolDisclosure} — see {@link isRunGroupableCategory}.
|
|
78
|
+
*/
|
|
79
|
+
const RUN_GROUPABLE_CATEGORIES: ReadonlySet<ToolCategory> = new Set<ToolCategory>([
|
|
80
|
+
"read",
|
|
81
|
+
"list",
|
|
82
|
+
"search",
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Returns the default inline {@link ToolDisclosure} for a presentation
|
|
87
|
+
* category. This is the headless policy behind the thread's "keep a live
|
|
88
|
+
* preview for shell/MCP/unknown, keep generic tools compact" behaviour;
|
|
89
|
+
* consumers can override per {@link ToolKind} via {@link registerToolPresenter}.
|
|
90
|
+
*/
|
|
91
|
+
export function defaultDisclosureForCategory(
|
|
92
|
+
category: ToolCategory,
|
|
93
|
+
): ToolDisclosure {
|
|
94
|
+
return PREVIEW_CATEGORIES.has(category) ? "preview" : "summary";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Returns whether a presentation category participates in run-grouping — i.e.
|
|
99
|
+
* whether a maximal run of consecutive calls of this category folds into one
|
|
100
|
+
* collapsible chip. See {@link RUN_GROUPABLE_CATEGORIES} and `segmentToolCalls`.
|
|
101
|
+
*/
|
|
102
|
+
export function isRunGroupableCategory(category: ToolCategory): boolean {
|
|
103
|
+
return RUN_GROUPABLE_CATEGORIES.has(category);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Returns whether a category's primary argument is a file path — the file
|
|
108
|
+
* tools (read / write / edit / delete). These render their path filename-first
|
|
109
|
+
* via {@link FilePathLink} (full path on hover) in headers and previews, rather
|
|
110
|
+
* than as a raw, often-absolute string.
|
|
111
|
+
*/
|
|
112
|
+
export function isFileCategory(category: ToolCategory): boolean {
|
|
113
|
+
return (
|
|
114
|
+
category === "read" ||
|
|
115
|
+
category === "write" ||
|
|
116
|
+
category === "edit" ||
|
|
117
|
+
category === "delete"
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
30
121
|
/** Resolved display metadata for a tool call, returned by {@link resolveToolCategory}. */
|
|
31
122
|
export interface ToolCategoryInfo {
|
|
32
123
|
/** Semantic category of the tool. */
|
|
@@ -72,6 +163,31 @@ export function isInternalTool(toolName: string): boolean {
|
|
|
72
163
|
return resolveToolKindByName(toolName) === ToolKind.TODO;
|
|
73
164
|
}
|
|
74
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Returns true for a tool call the runner collapsed in place — a superseded
|
|
168
|
+
* same-turn duplicate of an approval gate.
|
|
169
|
+
*
|
|
170
|
+
* When the agent emits one resource twice in a turn and both attempts are denied,
|
|
171
|
+
* the runner overlays the FIRST as the WAITING_APPROVAL gate and blanks the
|
|
172
|
+
* redundant twin to a content-less SKIPPED row (it cannot drop it — the backend's
|
|
173
|
+
* append-only-at-identity guard forbids removing a committed tool-call id). This
|
|
174
|
+
* predicate recognizes that blanked twin so the thread renders one card, not two.
|
|
175
|
+
*
|
|
176
|
+
* The signature is precise: a SKIPPED row with no approval flag, no result, no
|
|
177
|
+
* error, and no args preview. A genuinely user-skipped tool always retains the
|
|
178
|
+
* preview/args the user reviewed before skipping, so it is never blank and is
|
|
179
|
+
* never hidden by this.
|
|
180
|
+
*/
|
|
181
|
+
export function isCollapsedToolCall(toolCall: ToolCall): boolean {
|
|
182
|
+
return (
|
|
183
|
+
toolCall.status === ToolCallStatus.TOOL_CALL_SKIPPED &&
|
|
184
|
+
!toolCall.requiresApproval &&
|
|
185
|
+
!toolCall.result &&
|
|
186
|
+
!toolCall.error &&
|
|
187
|
+
!toolCall.argsPreview
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
75
191
|
/** Maps a {@link ToolKind} to its presentation metadata. */
|
|
76
192
|
export function toolKindToCategoryInfo(
|
|
77
193
|
kind: ToolKind,
|
|
@@ -226,11 +342,19 @@ export function extractPrimaryArg(toolCall: ToolCall): string | null {
|
|
|
226
342
|
* Extracts the primary argument value from a JSON string
|
|
227
343
|
* (typically `PendingApproval.argsPreview`). Returns `null` if
|
|
228
344
|
* parsing fails or the expected field is not found.
|
|
345
|
+
*
|
|
346
|
+
* When the caller has the denormalized wire `tool_kind` (as `PendingApproval`
|
|
347
|
+
* does), pass it so resolution honors the same kind that drives the label/icon —
|
|
348
|
+
* a file tool whose NAME the classifier doesn't recognize still resolves to its
|
|
349
|
+
* `path` field. This mirrors {@link extractPrimaryArg}, which uses the wire kind
|
|
350
|
+
* via `resolveToolCategoryFromCall`. Falls back to name-based resolution when
|
|
351
|
+
* the kind is unset (legacy executions).
|
|
229
352
|
*/
|
|
230
353
|
export function extractPrimaryArgFromPreview(
|
|
231
354
|
toolName: string,
|
|
232
355
|
argsPreview: string,
|
|
233
356
|
mcpServerSlug?: string,
|
|
357
|
+
toolKind?: ToolKind,
|
|
234
358
|
): string | null {
|
|
235
359
|
if (!argsPreview) return null;
|
|
236
360
|
|
|
@@ -238,7 +362,10 @@ export function extractPrimaryArgFromPreview(
|
|
|
238
362
|
const parsed = JSON.parse(argsPreview);
|
|
239
363
|
if (typeof parsed !== "object" || parsed === null) return null;
|
|
240
364
|
|
|
241
|
-
const info =
|
|
365
|
+
const info =
|
|
366
|
+
toolKind !== undefined
|
|
367
|
+
? resolveToolCategoryFromKind(toolKind, toolName, mcpServerSlug)
|
|
368
|
+
: resolveToolCategory(toolName, mcpServerSlug);
|
|
242
369
|
return extractArgValue(
|
|
243
370
|
parsed as JsonObject,
|
|
244
371
|
info.primaryArgField,
|
|
@@ -16,16 +16,34 @@ import { useMemo } from "react";
|
|
|
16
16
|
import type { ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
17
17
|
import { ToolKind, resolveToolKind, normalizeToolResult } from "@stigmer/sdk";
|
|
18
18
|
import type { ToolResultView } from "@stigmer/sdk";
|
|
19
|
-
import {
|
|
20
|
-
|
|
19
|
+
import {
|
|
20
|
+
resolveToolCategoryFromCall,
|
|
21
|
+
extractPrimaryArg,
|
|
22
|
+
defaultDisclosureForCategory,
|
|
23
|
+
isRunGroupableCategory,
|
|
24
|
+
} from "./tool-categories";
|
|
25
|
+
import type { ToolCategory, ToolDisclosure } from "./tool-categories";
|
|
21
26
|
import { summarizeResultView } from "./ResultView";
|
|
22
27
|
|
|
23
|
-
/** Optional per-kind overrides for label and
|
|
28
|
+
/** Optional per-kind overrides for label, summary, disclosure, and run-grouping. */
|
|
24
29
|
export interface ToolPresenter {
|
|
25
30
|
/** Overrides the display label (e.g. "Shell" -> "Run command"). */
|
|
26
31
|
readonly label?: (toolCall: ToolCall) => string;
|
|
27
32
|
/** Overrides the one-line result summary suffix. */
|
|
28
33
|
readonly summary?: (toolCall: ToolCall, result: ToolResultView) => string | null;
|
|
34
|
+
/**
|
|
35
|
+
* Overrides the default inline disclosure. Use to keep a noisy MCP tool
|
|
36
|
+
* compact (`() => "summary"`) or to foreground a custom tool's output
|
|
37
|
+
* (`() => "preview"`) without forking the components.
|
|
38
|
+
*/
|
|
39
|
+
readonly disclosure?: (toolCall: ToolCall, result: ToolResultView) => ToolDisclosure;
|
|
40
|
+
/**
|
|
41
|
+
* Overrides whether consecutive same-kind calls fold into one collapsible
|
|
42
|
+
* "Read 5 files" chip. Use to fold a chatty custom tool (`() => true`) or to
|
|
43
|
+
* keep a normally-folded category as standalone rows (`() => false`) without
|
|
44
|
+
* forking the thread layout. Defaults to {@link isRunGroupableCategory}.
|
|
45
|
+
*/
|
|
46
|
+
readonly runGroupable?: (toolCall: ToolCall) => boolean;
|
|
29
47
|
}
|
|
30
48
|
|
|
31
49
|
const registry = new Map<ToolKind, ToolPresenter>();
|
|
@@ -72,6 +90,22 @@ export function getToolPresenter(kind: ToolKind): ToolPresenter | undefined {
|
|
|
72
90
|
return registry.get(kind);
|
|
73
91
|
}
|
|
74
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Resolves whether a tool call participates in run-grouping, honouring a
|
|
95
|
+
* registered {@link ToolPresenter.runGroupable} override before the
|
|
96
|
+
* category default ({@link isRunGroupableCategory}).
|
|
97
|
+
*
|
|
98
|
+
* This is the non-hook twin of the `runGroupable` field in
|
|
99
|
+
* {@link useToolPresentation}, so the pure `segmentToolCalls` layout pass and
|
|
100
|
+
* the component layer share one source of truth (and honour the same override).
|
|
101
|
+
*/
|
|
102
|
+
export function resolveRunGroupable(toolCall: ToolCall): boolean {
|
|
103
|
+
const kind = resolveToolKind(toolCall);
|
|
104
|
+
const override = registry.get(kind)?.runGroupable;
|
|
105
|
+
if (override) return override(toolCall);
|
|
106
|
+
return isRunGroupableCategory(resolveToolCategoryFromCall(toolCall).category);
|
|
107
|
+
}
|
|
108
|
+
|
|
75
109
|
/** The fully-resolved presentation for a tool call, returned by {@link useToolPresentation}. */
|
|
76
110
|
export interface ToolPresentation {
|
|
77
111
|
/** Harness-agnostic kind (wire field, with name fallback). */
|
|
@@ -86,6 +120,19 @@ export interface ToolPresentation {
|
|
|
86
120
|
readonly result: ToolResultView;
|
|
87
121
|
/** One-line result summary suffix for the collapsed row (e.g. "+40 -0"). */
|
|
88
122
|
readonly resultSummary: string | null;
|
|
123
|
+
/**
|
|
124
|
+
* Default inline disclosure for this tool — `"preview"` keeps a bounded,
|
|
125
|
+
* persistent preview of the result, `"summary"` keeps a compact row. The
|
|
126
|
+
* active/awaiting state can still force a row open; this is the *settled*
|
|
127
|
+
* default.
|
|
128
|
+
*/
|
|
129
|
+
readonly disclosure: ToolDisclosure;
|
|
130
|
+
/**
|
|
131
|
+
* Whether consecutive same-kind calls fold into one collapsible chip — the
|
|
132
|
+
* run-grouping axis, orthogonal to {@link disclosure}. See
|
|
133
|
+
* {@link isRunGroupableCategory}.
|
|
134
|
+
*/
|
|
135
|
+
readonly runGroupable: boolean;
|
|
89
136
|
}
|
|
90
137
|
|
|
91
138
|
/**
|
|
@@ -105,6 +152,12 @@ export function useToolPresentation(toolCall: ToolCall): ToolPresentation {
|
|
|
105
152
|
const label = override?.label?.(toolCall) ?? categoryInfo.label;
|
|
106
153
|
const resultSummary =
|
|
107
154
|
override?.summary?.(toolCall, result) ?? summarizeResultView(result);
|
|
155
|
+
const disclosure =
|
|
156
|
+
override?.disclosure?.(toolCall, result) ??
|
|
157
|
+
defaultDisclosureForCategory(categoryInfo.category);
|
|
158
|
+
const runGroupable =
|
|
159
|
+
override?.runGroupable?.(toolCall) ??
|
|
160
|
+
isRunGroupableCategory(categoryInfo.category);
|
|
108
161
|
|
|
109
162
|
return {
|
|
110
163
|
kind,
|
|
@@ -113,6 +166,8 @@ export function useToolPresentation(toolCall: ToolCall): ToolPresentation {
|
|
|
113
166
|
primaryArg: extractPrimaryArg(toolCall),
|
|
114
167
|
result,
|
|
115
168
|
resultSummary,
|
|
169
|
+
disclosure,
|
|
170
|
+
runGroupable,
|
|
116
171
|
};
|
|
117
172
|
}, [toolCall]);
|
|
118
173
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { RevealToggle } from "../internal/RevealToggle";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Shared truncation threshold for all collapsible tool rendering
|
|
@@ -48,13 +49,11 @@ export function CollapsibleCode({ label, content, className }: CollapsibleCodePr
|
|
|
48
49
|
{displayContent}
|
|
49
50
|
</pre>
|
|
50
51
|
{needsTruncation && (
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
{isExpanded ? "Show less" : `Show all ${lines.length} lines`}
|
|
57
|
-
</button>
|
|
52
|
+
<RevealToggle
|
|
53
|
+
expanded={isExpanded}
|
|
54
|
+
onToggle={() => setIsExpanded((v) => !v)}
|
|
55
|
+
moreLabel={`Show all ${lines.length} lines`}
|
|
56
|
+
/>
|
|
58
57
|
)}
|
|
59
58
|
</div>
|
|
60
59
|
);
|
|
@@ -97,13 +96,11 @@ export function CollapsiblePre({ content, className }: CollapsiblePreProps) {
|
|
|
97
96
|
{displayContent}
|
|
98
97
|
</pre>
|
|
99
98
|
{needsTruncation && (
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
{isExpanded ? "Show less" : `Show all ${lines.length} lines`}
|
|
106
|
-
</button>
|
|
99
|
+
<RevealToggle
|
|
100
|
+
expanded={isExpanded}
|
|
101
|
+
onToggle={() => setIsExpanded((v) => !v)}
|
|
102
|
+
moreLabel={`Show all ${lines.length} lines`}
|
|
103
|
+
/>
|
|
107
104
|
)}
|
|
108
105
|
</>
|
|
109
106
|
);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
|
+
import { create } from "@bufbuild/protobuf";
|
|
5
|
+
import { GetArtifactDownloadUrlRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
|
|
6
|
+
import { useStigmer } from "../hooks";
|
|
7
|
+
import { toError } from "../internal/toError";
|
|
8
|
+
|
|
9
|
+
/** Return value of {@link useArtifactDownload}. */
|
|
10
|
+
export interface UseArtifactDownloadReturn {
|
|
11
|
+
/**
|
|
12
|
+
* Mint a fresh presigned URL for `storageKey` and trigger a browser
|
|
13
|
+
* download. No-op when the execution id is unknown or a download is already
|
|
14
|
+
* in flight. Resolves once the download has been initiated.
|
|
15
|
+
*
|
|
16
|
+
* @param storageKey - Storage key of the artifact to download.
|
|
17
|
+
* @param fileName - Optional suggested file name for the saved file.
|
|
18
|
+
*/
|
|
19
|
+
readonly download: (storageKey: string, fileName?: string) => Promise<void>;
|
|
20
|
+
|
|
21
|
+
/** `true` while a URL is being minted / the download is being initiated. */
|
|
22
|
+
readonly isDownloading: boolean;
|
|
23
|
+
|
|
24
|
+
/** Error from the last failed download attempt, or `null` when healthy. */
|
|
25
|
+
readonly error: Error | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Behavior hook that downloads an execution artifact on demand.
|
|
30
|
+
*
|
|
31
|
+
* Mirrors the click → mint fresh URL → browser download flow established by
|
|
32
|
+
* `WorkflowExecutionArtifactPanel`, generalized so every artifact "Download"
|
|
33
|
+
* affordance shares one implementation. The URL is minted at click time from
|
|
34
|
+
* the stable `storageKey` via `getArtifactDownloadUrl`, so it is always valid —
|
|
35
|
+
* unlike the previously persisted, short-lived URL that expired after an hour.
|
|
36
|
+
*
|
|
37
|
+
* Returns an imperative `download(storageKey, fileName?)` action rather than a
|
|
38
|
+
* URL, because a download is a user action, not render data. For rendering an
|
|
39
|
+
* image or showing a link, use {@link useArtifactDownloadUrl} instead.
|
|
40
|
+
*
|
|
41
|
+
* @param executionId - Execution that produced the artifacts, or `null` to
|
|
42
|
+
* disable (the action becomes a no-op).
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* const { download, isDownloading } = useArtifactDownload(executionId);
|
|
47
|
+
* <button onClick={() => download(artifact.storageKey, artifact.name)} disabled={isDownloading}>
|
|
48
|
+
* Download
|
|
49
|
+
* </button>
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @see useArtifactDownloadUrl — declarative URL resolver (image src, links)
|
|
53
|
+
*/
|
|
54
|
+
export function useArtifactDownload(
|
|
55
|
+
executionId: string | null,
|
|
56
|
+
): UseArtifactDownloadReturn {
|
|
57
|
+
const stigmer = useStigmer();
|
|
58
|
+
const [isDownloading, setIsDownloading] = useState(false);
|
|
59
|
+
const [error, setError] = useState<Error | null>(null);
|
|
60
|
+
|
|
61
|
+
const download = useCallback(
|
|
62
|
+
async (storageKey: string, fileName?: string) => {
|
|
63
|
+
if (!executionId || !storageKey || isDownloading) return;
|
|
64
|
+
setIsDownloading(true);
|
|
65
|
+
setError(null);
|
|
66
|
+
try {
|
|
67
|
+
const result = await stigmer.agentExecution.getArtifactDownloadUrl(
|
|
68
|
+
create(GetArtifactDownloadUrlRequestSchema, { executionId, storageKey }),
|
|
69
|
+
);
|
|
70
|
+
if (result.downloadUrl) {
|
|
71
|
+
triggerBrowserDownload(result.downloadUrl, fileName);
|
|
72
|
+
}
|
|
73
|
+
} catch (err) {
|
|
74
|
+
setError(toError(err));
|
|
75
|
+
} finally {
|
|
76
|
+
setIsDownloading(false);
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
[executionId, stigmer, isDownloading],
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return useMemo(
|
|
83
|
+
() => ({ download, isDownloading, error }),
|
|
84
|
+
[download, isDownloading, error],
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Trigger a browser download for a URL via a transient anchor click. The
|
|
90
|
+
* `download` hint is best-effort: browsers ignore it for cross-origin URLs
|
|
91
|
+
* (e.g. R2), in which case the navigation still serves the file. Falls back to
|
|
92
|
+
* a new tab in non-DOM environments.
|
|
93
|
+
*/
|
|
94
|
+
function triggerBrowserDownload(url: string, fileName?: string): void {
|
|
95
|
+
if (typeof document === "undefined") {
|
|
96
|
+
if (typeof window !== "undefined") {
|
|
97
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
98
|
+
}
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const anchor = document.createElement("a");
|
|
102
|
+
anchor.href = url;
|
|
103
|
+
if (fileName) anchor.download = fileName;
|
|
104
|
+
anchor.rel = "noopener noreferrer";
|
|
105
|
+
anchor.target = "_blank";
|
|
106
|
+
document.body.appendChild(anchor);
|
|
107
|
+
anchor.click();
|
|
108
|
+
document.body.removeChild(anchor);
|
|
109
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { create } from "@bufbuild/protobuf";
|
|
5
|
+
import { GetArtifactDownloadUrlRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
|
|
6
|
+
import { useStigmer } from "../hooks";
|
|
7
|
+
import { useFetch } from "../internal/useFetch";
|
|
8
|
+
|
|
9
|
+
/** Return value of {@link useArtifactDownloadUrl}. */
|
|
10
|
+
export interface UseArtifactDownloadUrlReturn {
|
|
11
|
+
/**
|
|
12
|
+
* A freshly minted presigned download URL for the artifact, or `null` when:
|
|
13
|
+
* - Fetching is skipped (`enabled` is `false`, or `executionId`/`storageKey` is `null`)
|
|
14
|
+
* - The request is in-flight
|
|
15
|
+
* - The request failed
|
|
16
|
+
*/
|
|
17
|
+
readonly url: string | null;
|
|
18
|
+
|
|
19
|
+
/** `true` while the URL request is in-flight (first load). */
|
|
20
|
+
readonly isLoading: boolean;
|
|
21
|
+
|
|
22
|
+
/** `true` while a background refetch is in flight. */
|
|
23
|
+
readonly isRefetching: boolean;
|
|
24
|
+
|
|
25
|
+
/** Error from the last failed attempt, or `null` when healthy. */
|
|
26
|
+
readonly error: Error | null;
|
|
27
|
+
|
|
28
|
+
/** Re-fetch the URL (e.g. to mint a fresh one after the previous expired). */
|
|
29
|
+
readonly refetch: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface UrlData {
|
|
33
|
+
readonly url: string | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const EMPTY: UrlData = { url: null };
|
|
37
|
+
|
|
38
|
+
/** Options for {@link useArtifactDownloadUrl}. */
|
|
39
|
+
export interface UseArtifactDownloadUrlOptions {
|
|
40
|
+
/**
|
|
41
|
+
* Gate the request. When `false`, the hook stays idle and returns `null` —
|
|
42
|
+
* use this to defer minting a URL until it is actually needed (e.g. only
|
|
43
|
+
* when an image becomes visible, or after a truncation is detected).
|
|
44
|
+
* Defaults to `true`.
|
|
45
|
+
*/
|
|
46
|
+
readonly enabled?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Data hook that resolves a **fresh** presigned download URL for an execution
|
|
51
|
+
* artifact on demand via `stigmer.agentExecution.getArtifactDownloadUrl()`.
|
|
52
|
+
*
|
|
53
|
+
* This exists because presigned URLs expire. The runner once baked a
|
|
54
|
+
* short-lived URL into the persisted execution status and the UI rendered it
|
|
55
|
+
* verbatim — so any link opened after the URL's lifetime failed with
|
|
56
|
+
* `ExpiredRequest`. Minting the URL at view time from the stable `storageKey`
|
|
57
|
+
* guarantees it is always valid, and works for every historical execution
|
|
58
|
+
* (the `storageKey` never expires).
|
|
59
|
+
*
|
|
60
|
+
* Use this for surfaces that genuinely need a URL — an `<img src>` for an
|
|
61
|
+
* offloaded screenshot, or a direct browser download of a large/binary
|
|
62
|
+
* artifact. For text that should render in-app, prefer {@link useArtifactContent}
|
|
63
|
+
* (CORS-safe, no presign).
|
|
64
|
+
*
|
|
65
|
+
* The URL is cached cross-mount by `storageKey` (DD-014), so repeated renders
|
|
66
|
+
* and remounts reuse the same minted URL instead of re-hitting the API.
|
|
67
|
+
*
|
|
68
|
+
* Pass `enabled: false` (or `null` for either id) to skip fetching — consistent
|
|
69
|
+
* with the `useArtifactContent(null, null)` convention.
|
|
70
|
+
*
|
|
71
|
+
* @param executionId - Execution that produced the artifact, or `null` to skip.
|
|
72
|
+
* @param storageKey - Storage key from the artifact/output ref, or `null` to skip.
|
|
73
|
+
* @param options - Optional gating (`enabled`).
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```tsx
|
|
77
|
+
* // Render an offloaded screenshot with an always-fresh URL.
|
|
78
|
+
* const { url, isLoading } = useArtifactDownloadUrl(executionId, storageKey);
|
|
79
|
+
* if (isLoading) return <Skeleton />;
|
|
80
|
+
* return url ? <img src={url} alt="Tool output" /> : null;
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* @see useArtifactContent — reads artifact bytes through the API (text preview)
|
|
84
|
+
* @see useArtifactDownload — imperative click-to-download counterpart
|
|
85
|
+
*/
|
|
86
|
+
export function useArtifactDownloadUrl(
|
|
87
|
+
executionId: string | null,
|
|
88
|
+
storageKey: string | null,
|
|
89
|
+
options?: UseArtifactDownloadUrlOptions,
|
|
90
|
+
): UseArtifactDownloadUrlReturn {
|
|
91
|
+
const stigmer = useStigmer();
|
|
92
|
+
const enabled = options?.enabled ?? true;
|
|
93
|
+
const active = enabled && !!executionId && !!storageKey;
|
|
94
|
+
|
|
95
|
+
const { data, isLoading, isRefetching, error, refetch } = useFetch(
|
|
96
|
+
active
|
|
97
|
+
? () =>
|
|
98
|
+
stigmer.agentExecution
|
|
99
|
+
.getArtifactDownloadUrl(
|
|
100
|
+
create(GetArtifactDownloadUrlRequestSchema, {
|
|
101
|
+
executionId: executionId!,
|
|
102
|
+
storageKey: storageKey!,
|
|
103
|
+
}),
|
|
104
|
+
)
|
|
105
|
+
.then((result): UrlData => ({ url: result.downloadUrl || null }))
|
|
106
|
+
: null,
|
|
107
|
+
[executionId, storageKey, active, stigmer],
|
|
108
|
+
EMPTY,
|
|
109
|
+
{ cacheKey: storageKey ? `artifact-url:${storageKey}` : undefined },
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
return useMemo(
|
|
113
|
+
() => ({ url: data.url, isLoading, isRefetching, error, refetch }),
|
|
114
|
+
[data.url, isLoading, isRefetching, error, refetch],
|
|
115
|
+
);
|
|
116
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import type {
|
|
5
|
+
FileChange,
|
|
6
|
+
FileContent,
|
|
7
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
8
|
+
import { useArtifactContent } from "./useArtifactContent";
|
|
9
|
+
import { useArtifactDownloadUrl } from "./useArtifactDownloadUrl";
|
|
10
|
+
|
|
11
|
+
/** Return value of {@link useFileChangeContent}. */
|
|
12
|
+
export interface UseFileChangeContentReturn {
|
|
13
|
+
/**
|
|
14
|
+
* Before-side text for diffing. `""` for an absent side (e.g. CREATE has no
|
|
15
|
+
* before). `null` while an offloaded side is in-flight or failed to load.
|
|
16
|
+
*/
|
|
17
|
+
readonly beforeText: string | null;
|
|
18
|
+
/**
|
|
19
|
+
* After-side text for diffing. `""` for an absent side (e.g. DELETE has no
|
|
20
|
+
* after). `null` while an offloaded side is in-flight or failed to load.
|
|
21
|
+
*/
|
|
22
|
+
readonly afterText: string | null;
|
|
23
|
+
/** `true` when either side is binary — render "binary file changed", not a diff. */
|
|
24
|
+
readonly isBinary: boolean;
|
|
25
|
+
/** `true` while an offloaded side's content request is in flight. */
|
|
26
|
+
readonly isLoading: boolean;
|
|
27
|
+
/** Error from a failed offloaded-side fetch, or `null`. */
|
|
28
|
+
readonly error: Error | null;
|
|
29
|
+
/**
|
|
30
|
+
* `true` when an offloaded side exceeded the server's content-size limit and
|
|
31
|
+
* was truncated — the inline diff would be incomplete; offer {@link downloadUrl}.
|
|
32
|
+
*/
|
|
33
|
+
readonly isTruncated: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Direct download URL for an offloaded side (the after side preferred), used
|
|
36
|
+
* as the fallback when content is truncated. `null` when neither side is offloaded.
|
|
37
|
+
*/
|
|
38
|
+
readonly downloadUrl: string | null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Behavior hook that resolves a {@link FileChange}'s before/after sides to the
|
|
43
|
+
* text the diff renderer needs.
|
|
44
|
+
*
|
|
45
|
+
* Small bodies are carried inline on the proto and returned with zero cost.
|
|
46
|
+
* Large bodies were offloaded to artifact storage by the runner
|
|
47
|
+
* (`FileContent.ref`); this hook lazily fetches them via
|
|
48
|
+
* {@link useArtifactContent} through the server proxy (CORS-safe). The fetch's
|
|
49
|
+
* `executionId` is derived from the ref's own `storageKey`
|
|
50
|
+
* (`artifacts/{executionId}/...`), so a net diff whose two sides originated in
|
|
51
|
+
* different executions still resolves each side correctly.
|
|
52
|
+
*
|
|
53
|
+
* This is the single place offload refs are dereferenced — the SDK's
|
|
54
|
+
* `tool-view` deliberately stays fetch-free, deferring all ref handling here.
|
|
55
|
+
*
|
|
56
|
+
* Rules-of-hooks safe: both sides always issue a (possibly skipped)
|
|
57
|
+
* `useArtifactContent` call; inline/absent sides pass `null` and never fetch.
|
|
58
|
+
*
|
|
59
|
+
* @param change - The file change whose content to resolve.
|
|
60
|
+
*
|
|
61
|
+
* @see useSessionFileChanges — produces the changes this resolves
|
|
62
|
+
* @see FileChangeDiff — renders the resolved content
|
|
63
|
+
*/
|
|
64
|
+
export function useFileChangeContent(
|
|
65
|
+
change: FileChange,
|
|
66
|
+
): UseFileChangeContentReturn {
|
|
67
|
+
const beforeRef = refOf(change.before);
|
|
68
|
+
const afterRef = refOf(change.after);
|
|
69
|
+
|
|
70
|
+
const beforeFetch = useArtifactContent(
|
|
71
|
+
beforeRef ? execIdFromStorageKey(beforeRef.storageKey) : null,
|
|
72
|
+
beforeRef ? beforeRef.storageKey : null,
|
|
73
|
+
null,
|
|
74
|
+
beforeRef?.contentHash,
|
|
75
|
+
);
|
|
76
|
+
const afterFetch = useArtifactContent(
|
|
77
|
+
afterRef ? execIdFromStorageKey(afterRef.storageKey) : null,
|
|
78
|
+
afterRef ? afterRef.storageKey : null,
|
|
79
|
+
null,
|
|
80
|
+
afterRef?.contentHash,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// The download fallback is needed only when an offloaded side was truncated.
|
|
84
|
+
// Resolve a fresh URL on demand from the stable storageKey (after side
|
|
85
|
+
// preferred), gated on truncation so we don't mint a URL we won't use.
|
|
86
|
+
const isTruncated = beforeFetch.isTruncated || afterFetch.isTruncated;
|
|
87
|
+
const downloadRef = afterRef ?? beforeRef;
|
|
88
|
+
const { url: downloadUrl } = useArtifactDownloadUrl(
|
|
89
|
+
downloadRef ? execIdFromStorageKey(downloadRef.storageKey) : null,
|
|
90
|
+
downloadRef ? downloadRef.storageKey : null,
|
|
91
|
+
{ enabled: isTruncated },
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
return useMemo(
|
|
95
|
+
() => ({
|
|
96
|
+
beforeText: sideText(change.before, beforeFetch.content),
|
|
97
|
+
afterText: sideText(change.after, afterFetch.content),
|
|
98
|
+
isBinary: Boolean(change.before?.isBinary || change.after?.isBinary),
|
|
99
|
+
isLoading: beforeFetch.isLoading || afterFetch.isLoading,
|
|
100
|
+
error: beforeFetch.error ?? afterFetch.error,
|
|
101
|
+
isTruncated,
|
|
102
|
+
downloadUrl,
|
|
103
|
+
}),
|
|
104
|
+
[
|
|
105
|
+
change.before,
|
|
106
|
+
change.after,
|
|
107
|
+
beforeFetch.content,
|
|
108
|
+
afterFetch.content,
|
|
109
|
+
beforeFetch.isLoading,
|
|
110
|
+
afterFetch.isLoading,
|
|
111
|
+
beforeFetch.error,
|
|
112
|
+
afterFetch.error,
|
|
113
|
+
isTruncated,
|
|
114
|
+
downloadUrl,
|
|
115
|
+
],
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** The offload pointer of a side, or null when the side is absent/inline/binary. */
|
|
120
|
+
function refOf(side: FileContent | undefined) {
|
|
121
|
+
if (!side || side.isBinary) return null;
|
|
122
|
+
return side.body.case === "ref" ? side.body.value : null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Resolves one side to text: absent → "" (empty file), inline → its value,
|
|
127
|
+
* offloaded → the fetched content (`null` until it loads).
|
|
128
|
+
*/
|
|
129
|
+
function sideText(
|
|
130
|
+
side: FileContent | undefined,
|
|
131
|
+
fetched: string | null,
|
|
132
|
+
): string | null {
|
|
133
|
+
if (!side || side.isBinary) return "";
|
|
134
|
+
if (side.body.case === "inline") return side.body.value;
|
|
135
|
+
return fetched;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Extracts the execution id from an artifact storage key of the form
|
|
140
|
+
* `artifacts/{executionId}/...`. Returns `null` for an unexpected shape so the
|
|
141
|
+
* caller skips the fetch rather than issuing a request the server would reject.
|
|
142
|
+
*/
|
|
143
|
+
export function execIdFromStorageKey(storageKey: string): string | null {
|
|
144
|
+
const parts = storageKey.split("/");
|
|
145
|
+
if (parts.length >= 3 && parts[0] === "artifacts" && parts[1]) {
|
|
146
|
+
return parts[1];
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|