@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,101 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
|
+
import { toError } from "./toError";
|
|
5
|
+
|
|
6
|
+
/** A stable empty set so an idle hook keeps a constant `submittingKeys` ref. */
|
|
7
|
+
const NO_KEYS: ReadonlySet<string> = new Set();
|
|
8
|
+
/** A stable empty map so an error-free hook keeps a constant `errorsByKey` ref. */
|
|
9
|
+
const NO_ERRORS: ReadonlyMap<string, Error> = new Map();
|
|
10
|
+
|
|
11
|
+
/** Return value of {@link useKeyedSubmission}. */
|
|
12
|
+
export interface KeyedSubmission<T> {
|
|
13
|
+
/**
|
|
14
|
+
* Keys whose operation is currently in flight. A consumer reads
|
|
15
|
+
* `submittingKeys.has(key)` to drive per-target loading state when several
|
|
16
|
+
* operations of the same kind can run at once.
|
|
17
|
+
*/
|
|
18
|
+
readonly submittingKeys: ReadonlySet<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Per-key failures, keyed exactly like {@link submittingKeys}. A consumer
|
|
21
|
+
* reads `errorsByKey.get(key)` to surface the failure beside the one control
|
|
22
|
+
* it belongs to — a single scalar cannot say which of several concurrent
|
|
23
|
+
* operations failed. Cleared for a key when that key is retried.
|
|
24
|
+
*/
|
|
25
|
+
readonly errorsByKey: ReadonlyMap<string, Error>;
|
|
26
|
+
/**
|
|
27
|
+
* Run `fn` under the bookkeeping for `key`: clear the key's prior error,
|
|
28
|
+
* mark it in flight, await, and release it. On failure the error is recorded
|
|
29
|
+
* in {@link errorsByKey} and then **re-thrown** — the primitive never decides
|
|
30
|
+
* how the failure propagates, so each caller chooses (mirror to a scalar and
|
|
31
|
+
* rethrow, swallow to a sentinel, etc.).
|
|
32
|
+
*/
|
|
33
|
+
readonly run: (key: string, fn: () => Promise<T>) => Promise<T>;
|
|
34
|
+
/** Reset every recorded error. In-flight tracking is untouched. */
|
|
35
|
+
readonly clearErrors: () => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Tracks per-key in-flight and error state for a family of concurrent async
|
|
40
|
+
* operations that share one identity space (e.g. approval decisions keyed by
|
|
41
|
+
* tool-call id, or by task name).
|
|
42
|
+
*
|
|
43
|
+
* This is the shared substrate behind every "many gates at once" surface: a
|
|
44
|
+
* thread or a workflow can hold several pending decisions simultaneously, so a
|
|
45
|
+
* failure (and the spinner) must be attributable to the *one* gate it belongs
|
|
46
|
+
* to. A single scalar `error`/`isSubmitting` pair cannot do that; a keyed
|
|
47
|
+
* `Set`/`Map` pair can. Singleton controls (a header's cancel/pause) do not
|
|
48
|
+
* need this — they correctly share one scalar.
|
|
49
|
+
*
|
|
50
|
+
* `run` re-throws after recording so the propagation policy stays with the
|
|
51
|
+
* caller: {@link useSubmitApproval} mirrors the failure to a scalar and
|
|
52
|
+
* rethrows; {@link useWorkflowExecutionActions} swallows it to `null`.
|
|
53
|
+
*
|
|
54
|
+
* The return is `useMemo`'d over stable parts (empty-collection sentinels keep
|
|
55
|
+
* the idle refs constant) so it is safe as a `React.memo` dependency (DD-010).
|
|
56
|
+
*
|
|
57
|
+
* @internal Not part of the public API.
|
|
58
|
+
*/
|
|
59
|
+
export function useKeyedSubmission<T = void>(): KeyedSubmission<T> {
|
|
60
|
+
const [submittingKeys, setSubmittingKeys] =
|
|
61
|
+
useState<ReadonlySet<string>>(NO_KEYS);
|
|
62
|
+
const [errorsByKey, setErrorsByKey] =
|
|
63
|
+
useState<ReadonlyMap<string, Error>>(NO_ERRORS);
|
|
64
|
+
|
|
65
|
+
const clearErrors = useCallback(() => setErrorsByKey(NO_ERRORS), []);
|
|
66
|
+
|
|
67
|
+
const run = useCallback(async (key: string, fn: () => Promise<T>): Promise<T> => {
|
|
68
|
+
setSubmittingKeys((prev) => {
|
|
69
|
+
const next = new Set(prev);
|
|
70
|
+
next.add(key);
|
|
71
|
+
return next;
|
|
72
|
+
});
|
|
73
|
+
// Clear this key's prior failure so a retry starts clean.
|
|
74
|
+
setErrorsByKey((prev) => {
|
|
75
|
+
if (!prev.has(key)) return prev;
|
|
76
|
+
const next = new Map(prev);
|
|
77
|
+
next.delete(key);
|
|
78
|
+
return next;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
return await fn();
|
|
83
|
+
} catch (err) {
|
|
84
|
+
const e = toError(err);
|
|
85
|
+
setErrorsByKey((prev) => new Map(prev).set(key, e));
|
|
86
|
+
throw err;
|
|
87
|
+
} finally {
|
|
88
|
+
setSubmittingKeys((prev) => {
|
|
89
|
+
if (!prev.has(key)) return prev;
|
|
90
|
+
const next = new Set(prev);
|
|
91
|
+
next.delete(key);
|
|
92
|
+
return next;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}, []);
|
|
96
|
+
|
|
97
|
+
return useMemo(
|
|
98
|
+
() => ({ submittingKeys, errorsByKey, run, clearErrors }),
|
|
99
|
+
[submittingKeys, errorsByKey, run, clearErrors],
|
|
100
|
+
);
|
|
101
|
+
}
|
|
@@ -12,6 +12,7 @@ import { SelectionStore } from "../internal/store/selection-store";
|
|
|
12
12
|
import { ThreadSelectionContext } from "../execution/ThreadSelectionContext";
|
|
13
13
|
import { useSelectedThreadItem } from "../execution/useThreadSelection";
|
|
14
14
|
import { MessageThread } from "../execution/MessageThread";
|
|
15
|
+
import { FileReviewDock } from "../execution/FileReviewDock";
|
|
15
16
|
import { ThreadSkeleton } from "../execution/ThreadSkeleton";
|
|
16
17
|
import { SessionComposer } from "../composer";
|
|
17
18
|
import { SecretFlowErrorGuide, isSecretFlowError } from "../error";
|
|
@@ -109,8 +110,8 @@ export interface SessionViewerProps {
|
|
|
109
110
|
* Owns `useSessionPageFlow` internally and composes:
|
|
110
111
|
* - **Conversation column** (primary): `MessageThread` + error
|
|
111
112
|
* banners + `SessionComposer`
|
|
112
|
-
* - **SessionInspector** (secondary): tabbed panel with
|
|
113
|
-
* Changes, Artifacts, Usage, and Inspect facets
|
|
113
|
+
* - **SessionInspector** (secondary): tabbed panel with Workspace,
|
|
114
|
+
* Config, Changes, Artifacts, Usage, and Inspect facets
|
|
114
115
|
*
|
|
115
116
|
* Connected via `ResizableSplit` with persisted width.
|
|
116
117
|
*
|
|
@@ -288,8 +289,12 @@ function ConversationColumn({
|
|
|
288
289
|
isEndUser,
|
|
289
290
|
}: ConversationColumnProps) {
|
|
290
291
|
const { conv } = flow;
|
|
291
|
-
|
|
292
|
-
|
|
292
|
+
// Approval failures are NOT folded into this banner: they now surface in-card,
|
|
293
|
+
// beside the exact gate that failed (see MessageThread's approvalErrors →
|
|
294
|
+
// ApprovalCard), matching the file-review card. A single banner cannot name
|
|
295
|
+
// which of several concurrent gates failed. The scalar conv.approvalError
|
|
296
|
+
// stays available for headless/`ink` consumers.
|
|
297
|
+
const sendError = flow.submitError ?? conv.sendError ?? conv.stopError;
|
|
293
298
|
|
|
294
299
|
// Retry a terminal-failed execution by resending its originating message
|
|
295
300
|
// through the full submit pipeline (agent override, runtime-env, workspace).
|
|
@@ -331,6 +336,8 @@ function ConversationColumn({
|
|
|
331
336
|
onRetryExecution={onRetryExecution}
|
|
332
337
|
onApprovalSubmit={flow.submitApproval}
|
|
333
338
|
submittingApprovalIds={conv.submittingApprovalIds}
|
|
339
|
+
approvalErrors={conv.approvalErrors}
|
|
340
|
+
showFileReviewRecords
|
|
334
341
|
onEditMessage={conv.isStoppable ? handleEditMessage : undefined}
|
|
335
342
|
workspaceEntries={conv.workspaceEntries}
|
|
336
343
|
sandboxWorkspaceRoot={flow.sandboxWorkspaceRoot}
|
|
@@ -356,6 +363,16 @@ function ConversationColumn({
|
|
|
356
363
|
{flow.autoApproveAll && (
|
|
357
364
|
<AutoApproveIndicator onTurnOff={() => flow.setAutoApproveAll(false)} />
|
|
358
365
|
)}
|
|
366
|
+
{/* Pending file reviews dock here — pinned above the composer so the
|
|
367
|
+
decision the agent is blocked on can never scroll out of view. The
|
|
368
|
+
thread renders only observational rows (badges) and read-only
|
|
369
|
+
settled records; this is the one decision surface. */}
|
|
370
|
+
<FileReviewDock
|
|
371
|
+
changeSets={conv.fileChangeSets}
|
|
372
|
+
onSubmit={conv.submitFileDecision}
|
|
373
|
+
submittingDecisionKeys={conv.submittingFileDecisionKeys}
|
|
374
|
+
decisionErrors={conv.fileDecisionErrors}
|
|
375
|
+
/>
|
|
359
376
|
<SessionComposer
|
|
360
377
|
ref={composerRef}
|
|
361
378
|
onSubmit={flow.handleSubmit}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
|
|
3
|
+
import { create } from "@bufbuild/protobuf";
|
|
4
|
+
import {
|
|
5
|
+
CapturedFileChangeSchema,
|
|
6
|
+
FileChangeSetSchema,
|
|
7
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
|
|
8
|
+
import { FileContentSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
9
|
+
import {
|
|
10
|
+
DiffCompleteness,
|
|
11
|
+
FileChangeKind,
|
|
12
|
+
FileChangeSetStatus,
|
|
13
|
+
FileDecisionAction,
|
|
14
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Wiring-contract tests for the file-review surfaces on SessionViewer:
|
|
18
|
+
// the pending decision bar renders in the composer-docked FileReviewDock
|
|
19
|
+
// (fed from conv's seam), and the thread is records-only
|
|
20
|
+
// (`showFileReviewRecords`, no decision callbacks). The dock renders REAL —
|
|
21
|
+
// it is the subject; the thread/composer/inspector are prop-capturing probes.
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
type CapturedProps = Record<string, unknown>;
|
|
25
|
+
|
|
26
|
+
const threadProps: CapturedProps[] = [];
|
|
27
|
+
vi.mock("../../execution/MessageThread", () => ({
|
|
28
|
+
MessageThread: (props: CapturedProps) => {
|
|
29
|
+
threadProps.push(props);
|
|
30
|
+
return <div data-testid="thread-probe" />;
|
|
31
|
+
},
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
vi.mock("../../composer", async (importOriginal) => {
|
|
35
|
+
const actual = await importOriginal<typeof import("../../composer")>();
|
|
36
|
+
return {
|
|
37
|
+
...actual,
|
|
38
|
+
SessionComposer: () => <div data-testid="composer-probe" />,
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
vi.mock("../inspector/SessionInspector", () => ({
|
|
43
|
+
SessionInspector: () => <div data-testid="inspector-probe" />,
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
const stubWorkspace = {
|
|
47
|
+
entries: [],
|
|
48
|
+
hasEntries: false,
|
|
49
|
+
toInput: vi.fn().mockReturnValue([]),
|
|
50
|
+
addGitRepo: vi.fn(),
|
|
51
|
+
addLocalPath: vi.fn(),
|
|
52
|
+
removeEntry: vi.fn(),
|
|
53
|
+
clear: vi.fn(),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function pendingChangeSet(id: string) {
|
|
57
|
+
return create(FileChangeSetSchema, {
|
|
58
|
+
id,
|
|
59
|
+
status: FileChangeSetStatus.AWAITING_REVIEW,
|
|
60
|
+
aggregateDigest: `agg-${id}`,
|
|
61
|
+
diffCompleteness: DiffCompleteness.COMPLETE,
|
|
62
|
+
changes: [
|
|
63
|
+
create(CapturedFileChangeSchema, {
|
|
64
|
+
id: `${id}:notes.md`,
|
|
65
|
+
pathBefore: "notes.md",
|
|
66
|
+
pathAfter: "notes.md",
|
|
67
|
+
kind: FileChangeKind.ADD,
|
|
68
|
+
before: create(FileContentSchema, { body: { case: "inline", value: "" } }),
|
|
69
|
+
after: create(FileContentSchema, { body: { case: "inline", value: "# Notes\n" } }),
|
|
70
|
+
fileDigest: "d-notes",
|
|
71
|
+
diffComplete: true,
|
|
72
|
+
}),
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const stubConv = {
|
|
78
|
+
session: { spec: {} },
|
|
79
|
+
isLoading: false,
|
|
80
|
+
loadError: null,
|
|
81
|
+
completedExecutions: [],
|
|
82
|
+
activeStreamExecution: null,
|
|
83
|
+
activePhase: null,
|
|
84
|
+
isStreaming: false,
|
|
85
|
+
isConnecting: false,
|
|
86
|
+
sendFollowUp: vi.fn(),
|
|
87
|
+
canSendFollowUp: true,
|
|
88
|
+
isSending: false,
|
|
89
|
+
sendError: null,
|
|
90
|
+
clearSendError: vi.fn(),
|
|
91
|
+
pendingUserMessage: null,
|
|
92
|
+
workspaceEntries: [],
|
|
93
|
+
mcpServerUsages: [],
|
|
94
|
+
skillRefs: [],
|
|
95
|
+
pendingApprovals: [],
|
|
96
|
+
submitApproval: vi.fn(),
|
|
97
|
+
submittingApprovalIds: new Set<string>(),
|
|
98
|
+
fileChangeSets: [] as ReturnType<typeof pendingChangeSet>[],
|
|
99
|
+
submitFileDecision: vi.fn(),
|
|
100
|
+
submittingFileDecisionKeys: new Set<string>(),
|
|
101
|
+
fileDecisionErrors: new Map<string, Error>(),
|
|
102
|
+
streamError: null,
|
|
103
|
+
reconnectStream: vi.fn(),
|
|
104
|
+
approvalError: null,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const stubSessionPageFlow = {
|
|
108
|
+
conv: stubConv,
|
|
109
|
+
harness: "native" as const,
|
|
110
|
+
executionTarget: undefined,
|
|
111
|
+
model: [undefined, vi.fn()] as const,
|
|
112
|
+
interactionMode: ["agent" as const, vi.fn()] as const,
|
|
113
|
+
agentRef: { org: "acme", slug: "support-bot" },
|
|
114
|
+
setAgentRef: vi.fn(),
|
|
115
|
+
resolution: null,
|
|
116
|
+
setResolution: vi.fn(),
|
|
117
|
+
isDefaultAgent: false,
|
|
118
|
+
mcpServerUsages: [],
|
|
119
|
+
setMcpServerUsages: vi.fn(),
|
|
120
|
+
skillRefs: [],
|
|
121
|
+
setSkillRefs: vi.fn(),
|
|
122
|
+
workspace: stubWorkspace,
|
|
123
|
+
sessionVariables: { entries: [], isEmpty: true, clear: vi.fn() },
|
|
124
|
+
autoApproveAll: false,
|
|
125
|
+
setAutoApproveAll: vi.fn(),
|
|
126
|
+
submitApproval: vi.fn(),
|
|
127
|
+
handleSubmit: vi.fn(),
|
|
128
|
+
submitError: null as Error | null,
|
|
129
|
+
displayExecution: null,
|
|
130
|
+
allExecutions: [],
|
|
131
|
+
sandboxWorkspaceRoot: undefined,
|
|
132
|
+
};
|
|
133
|
+
vi.mock("../useSessionPageFlow", () => ({
|
|
134
|
+
useSessionPageFlow: () => stubSessionPageFlow,
|
|
135
|
+
}));
|
|
136
|
+
|
|
137
|
+
import { SessionViewer } from "../SessionViewer";
|
|
138
|
+
|
|
139
|
+
beforeEach(() => {
|
|
140
|
+
threadProps.length = 0;
|
|
141
|
+
stubConv.fileChangeSets = [];
|
|
142
|
+
stubConv.submitFileDecision = vi.fn();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
afterEach(() => {
|
|
146
|
+
cleanup();
|
|
147
|
+
vi.clearAllMocks();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe("SessionViewer — file-review dock wiring", () => {
|
|
151
|
+
it("renders no dock when nothing is pending", () => {
|
|
152
|
+
render(<SessionViewer sessionId="ses_1" org="acme" />);
|
|
153
|
+
expect(
|
|
154
|
+
document.querySelector('[data-cursor-target="file-review-dock"]'),
|
|
155
|
+
).toBeNull();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("docks a pending set above the composer, wired to conv's decision seam", () => {
|
|
159
|
+
stubConv.fileChangeSets = [pendingChangeSet("cs-1")];
|
|
160
|
+
render(<SessionViewer sessionId="ses_1" org="acme" />);
|
|
161
|
+
|
|
162
|
+
const dock = document.querySelector('[data-cursor-target="file-review-dock"]');
|
|
163
|
+
expect(dock).toBeTruthy();
|
|
164
|
+
expect(screen.getByText("Review file changes")).toBeTruthy();
|
|
165
|
+
|
|
166
|
+
// The dock sits in the fixed strip: a sibling of the composer probe,
|
|
167
|
+
// ordered before it (pinned directly above the input).
|
|
168
|
+
const composer = screen.getByTestId("composer-probe");
|
|
169
|
+
expect(dock!.parentElement).toBe(composer.parentElement);
|
|
170
|
+
expect(
|
|
171
|
+
dock!.compareDocumentPosition(composer) & Node.DOCUMENT_POSITION_FOLLOWING,
|
|
172
|
+
).toBeTruthy();
|
|
173
|
+
|
|
174
|
+
// A decision routes through conv.submitFileDecision with the set's id.
|
|
175
|
+
fireEvent.click(
|
|
176
|
+
document.querySelector<HTMLButtonElement>(
|
|
177
|
+
'[data-cursor-target="file-review-approve"]',
|
|
178
|
+
)!,
|
|
179
|
+
);
|
|
180
|
+
expect(stubConv.submitFileDecision).toHaveBeenCalledWith(
|
|
181
|
+
"cs-1",
|
|
182
|
+
FileDecisionAction.APPROVE,
|
|
183
|
+
expect.objectContaining({ expectedDigest: "agg-cs-1" }),
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("gives the thread records-only file-review props (no decision callbacks)", () => {
|
|
188
|
+
render(<SessionViewer sessionId="ses_1" org="acme" />);
|
|
189
|
+
|
|
190
|
+
expect(threadProps.length).toBeGreaterThan(0);
|
|
191
|
+
const props = threadProps.at(-1)!;
|
|
192
|
+
expect(props.showFileReviewRecords).toBe(true);
|
|
193
|
+
// The decision plumbing must not leak back into the thread.
|
|
194
|
+
expect(props.onFileDecisionSubmit).toBeUndefined();
|
|
195
|
+
expect(props.submittingFileDecisionKeys).toBeUndefined();
|
|
196
|
+
expect(props.fileDecisionErrors).toBeUndefined();
|
|
197
|
+
});
|
|
198
|
+
});
|
|
@@ -97,6 +97,10 @@ const stubConv = {
|
|
|
97
97
|
pendingApprovals: [],
|
|
98
98
|
submitApproval: vi.fn(),
|
|
99
99
|
submittingApprovalIds: new Set<string>(),
|
|
100
|
+
fileChangeSets: [],
|
|
101
|
+
submitFileDecision: vi.fn(),
|
|
102
|
+
submittingFileDecisionKeys: new Set<string>(),
|
|
103
|
+
fileDecisionErrors: new Map<string, Error>(),
|
|
100
104
|
streamError: null,
|
|
101
105
|
reconnectStream: vi.fn(),
|
|
102
106
|
approvalError: null,
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { renderHook } from "@testing-library/react";
|
|
3
|
+
import { create } from "@bufbuild/protobuf";
|
|
4
|
+
import {
|
|
5
|
+
AgentExecutionSchema,
|
|
6
|
+
AgentExecutionStatusSchema,
|
|
7
|
+
type AgentExecution,
|
|
8
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
9
|
+
import { ApiResourceMetadataSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
|
|
10
|
+
import { FileContentSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
11
|
+
import {
|
|
12
|
+
CapturedFileChangeSchema,
|
|
13
|
+
FileChangeSetSchema,
|
|
14
|
+
FileReviewBaselineCapturedSchema,
|
|
15
|
+
FileReviewCandidateCapturedSchema,
|
|
16
|
+
FileReviewEventSchema,
|
|
17
|
+
FileReviewEventStreamSchema,
|
|
18
|
+
type CapturedFileChange,
|
|
19
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
|
|
20
|
+
import {
|
|
21
|
+
ExecutionPhase,
|
|
22
|
+
FileChangeCaptureLevel,
|
|
23
|
+
FileChangeKind,
|
|
24
|
+
FileChangeSetStatus,
|
|
25
|
+
FileChangeType,
|
|
26
|
+
FileReviewEventType,
|
|
27
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
28
|
+
import { useSessionFileChanges } from "../useSessionFileChanges";
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Fixtures
|
|
32
|
+
//
|
|
33
|
+
// Under apply-then-review the hook sources exclusively from the file-review
|
|
34
|
+
// ledger (the live `file_change_sets` projection, or the folded
|
|
35
|
+
// `file_review_event_stream` for a terminal execution). The tool-call-coupled
|
|
36
|
+
// `ToolCall.file_changes` (message.proto field 22) was removed in Phase 5
|
|
37
|
+
// Slice 4, so every fixture here is a CapturedFileChange.
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
function inlineSide(value: string) {
|
|
41
|
+
return create(FileContentSchema, { body: { case: "inline", value } });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function captured(opts: {
|
|
45
|
+
id: string;
|
|
46
|
+
pathBefore?: string;
|
|
47
|
+
pathAfter?: string;
|
|
48
|
+
before?: string;
|
|
49
|
+
after?: string;
|
|
50
|
+
kind?: FileChangeKind;
|
|
51
|
+
}): CapturedFileChange {
|
|
52
|
+
const pathBefore = opts.pathBefore ?? opts.pathAfter ?? "";
|
|
53
|
+
const pathAfter = opts.pathAfter ?? opts.pathBefore ?? "";
|
|
54
|
+
return create(CapturedFileChangeSchema, {
|
|
55
|
+
id: opts.id,
|
|
56
|
+
pathBefore,
|
|
57
|
+
pathAfter,
|
|
58
|
+
kind: opts.kind ?? FileChangeKind.MODIFY,
|
|
59
|
+
before: opts.before !== undefined ? inlineSide(opts.before) : undefined,
|
|
60
|
+
after: opts.after !== undefined ? inlineSide(opts.after) : undefined,
|
|
61
|
+
diffComplete: true,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function execWith(id: string): AgentExecution {
|
|
66
|
+
const exec = create(AgentExecutionSchema);
|
|
67
|
+
exec.metadata = create(ApiResourceMetadataSchema, { id });
|
|
68
|
+
exec.status = create(AgentExecutionStatusSchema);
|
|
69
|
+
return exec;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** An execution carrying a live server projection of one change set. */
|
|
73
|
+
function execWithProjection(id: string, changes: CapturedFileChange[]): AgentExecution {
|
|
74
|
+
const exec = execWith(id);
|
|
75
|
+
exec.status!.fileChangeSets = [
|
|
76
|
+
create(FileChangeSetSchema, {
|
|
77
|
+
id: `${id}:0`,
|
|
78
|
+
status: FileChangeSetStatus.AWAITING_REVIEW,
|
|
79
|
+
changes,
|
|
80
|
+
}),
|
|
81
|
+
];
|
|
82
|
+
return exec;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** A terminal execution: empty projection, changes only in the durable ledger. */
|
|
86
|
+
function execWithLedger(id: string, changes: CapturedFileChange[]): AgentExecution {
|
|
87
|
+
const exec = execWith(id);
|
|
88
|
+
exec.status!.phase = ExecutionPhase.EXECUTION_COMPLETED;
|
|
89
|
+
exec.status!.fileChangeSets = [];
|
|
90
|
+
const changeSetId = `${id}:0`;
|
|
91
|
+
exec.status!.fileReviewEventStream = create(FileReviewEventStreamSchema, {
|
|
92
|
+
executionId: id,
|
|
93
|
+
events: [
|
|
94
|
+
create(FileReviewEventSchema, {
|
|
95
|
+
changeSetId,
|
|
96
|
+
eventType: FileReviewEventType.BASELINE_CAPTURED,
|
|
97
|
+
payload: {
|
|
98
|
+
case: "baselineCaptured",
|
|
99
|
+
value: create(FileReviewBaselineCapturedSchema, { changeSetId }),
|
|
100
|
+
},
|
|
101
|
+
}),
|
|
102
|
+
create(FileReviewEventSchema, {
|
|
103
|
+
changeSetId,
|
|
104
|
+
eventType: FileReviewEventType.CANDIDATE_CAPTURED,
|
|
105
|
+
payload: {
|
|
106
|
+
case: "candidateCaptured",
|
|
107
|
+
value: create(FileReviewCandidateCapturedSchema, { changeSetId, changes }),
|
|
108
|
+
},
|
|
109
|
+
}),
|
|
110
|
+
],
|
|
111
|
+
});
|
|
112
|
+
return exec;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function run(executions: readonly AgentExecution[]) {
|
|
116
|
+
return renderHook(() => useSessionFileChanges(executions)).result.current;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// Tests
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
describe("useSessionFileChanges", () => {
|
|
124
|
+
it("returns empty for no executions", () => {
|
|
125
|
+
const r = run([]);
|
|
126
|
+
expect(r.hasFileChanges).toBe(false);
|
|
127
|
+
expect(r.fileChangeCount).toBe(0);
|
|
128
|
+
expect(r.fileChanges).toEqual([]);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("returns empty when an execution has no file-review changes", () => {
|
|
132
|
+
const r = run([execWith("e1")]);
|
|
133
|
+
expect(r.hasFileChanges).toBe(false);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("collects a single change from the live projection", () => {
|
|
137
|
+
const r = run([
|
|
138
|
+
execWithProjection("e1", [
|
|
139
|
+
captured({ id: "fc1", pathAfter: "src/a.ts", before: "a", after: "b" }),
|
|
140
|
+
]),
|
|
141
|
+
]);
|
|
142
|
+
expect(r.fileChangeCount).toBe(1);
|
|
143
|
+
expect(r.fileChanges[0].path).toBe("src/a.ts");
|
|
144
|
+
expect(r.fileChanges[0].captureLevel).toBe(FileChangeCaptureLevel.WHOLE_FILE);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("collects changes from the durable ledger for a terminal execution", () => {
|
|
148
|
+
const r = run([
|
|
149
|
+
execWithLedger("e1", [
|
|
150
|
+
captured({ id: "fc1", pathAfter: "src/term.ts", before: "a", after: "b" }),
|
|
151
|
+
]),
|
|
152
|
+
]);
|
|
153
|
+
expect(r.fileChangeCount).toBe(1);
|
|
154
|
+
expect(r.fileChanges[0].path).toBe("src/term.ts");
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("net-diffs multiple edits to the same path: first.before -> last.after", () => {
|
|
158
|
+
const r = run([
|
|
159
|
+
execWithProjection("e1", [
|
|
160
|
+
captured({ id: "fc1", pathAfter: "src/a.ts", before: "v0", after: "v1" }),
|
|
161
|
+
captured({ id: "fc2", pathAfter: "src/a.ts", before: "v1", after: "v2" }),
|
|
162
|
+
]),
|
|
163
|
+
]);
|
|
164
|
+
expect(r.fileChangeCount).toBe(1);
|
|
165
|
+
const net = r.fileChanges[0];
|
|
166
|
+
expect(net.before?.body.case === "inline" && net.before.body.value).toBe("v0");
|
|
167
|
+
expect(net.after?.body.case === "inline" && net.after.body.value).toBe("v2");
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("reconciles create-then-modify on the same path to CREATE", () => {
|
|
171
|
+
const r = run([
|
|
172
|
+
execWithProjection("e1", [
|
|
173
|
+
captured({ id: "fc1", pathAfter: "src/new.ts", after: "first", kind: FileChangeKind.ADD }),
|
|
174
|
+
captured({ id: "fc2", pathAfter: "src/new.ts", before: "first", after: "second", kind: FileChangeKind.MODIFY }),
|
|
175
|
+
]),
|
|
176
|
+
]);
|
|
177
|
+
expect(r.fileChanges[0].changeType).toBe(FileChangeType.CREATE);
|
|
178
|
+
expect(r.fileChanges[0].after?.body.case === "inline" && r.fileChanges[0].after.body.value).toBe("second");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("reconciles modify-then-delete on the same path to DELETE", () => {
|
|
182
|
+
const r = run([
|
|
183
|
+
execWithProjection("e1", [
|
|
184
|
+
captured({ id: "fc1", pathAfter: "src/x.ts", before: "a", after: "b" }),
|
|
185
|
+
captured({ id: "fc2", pathBefore: "src/x.ts", pathAfter: "src/x.ts", before: "b", kind: FileChangeKind.DELETE }),
|
|
186
|
+
]),
|
|
187
|
+
]);
|
|
188
|
+
expect(r.fileChanges[0].changeType).toBe(FileChangeType.DELETE);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("reconciles create-then-delete on the same path to DELETE (path is not hidden)", () => {
|
|
192
|
+
const r = run([
|
|
193
|
+
execWithProjection("e1", [
|
|
194
|
+
captured({ id: "fc1", pathAfter: "src/tmp.ts", after: "scratch", kind: FileChangeKind.ADD }),
|
|
195
|
+
captured({ id: "fc2", pathBefore: "src/tmp.ts", pathAfter: "src/tmp.ts", before: "scratch", kind: FileChangeKind.DELETE }),
|
|
196
|
+
]),
|
|
197
|
+
]);
|
|
198
|
+
// A file created and removed within one session still surfaces, as a DELETE
|
|
199
|
+
// anchored on the first change's (empty) before — it is not silently dropped.
|
|
200
|
+
expect(r.fileChangeCount).toBe(1);
|
|
201
|
+
expect(r.fileChanges[0].changeType).toBe(FileChangeType.DELETE);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("reconciles rename-then-modify on the same path to RENAME, preserving rename_from", () => {
|
|
205
|
+
const r = run([
|
|
206
|
+
execWithProjection("e1", [
|
|
207
|
+
captured({ id: "fc1", pathBefore: "src/old-name.ts", pathAfter: "src/new-name.ts", before: "a", after: "a", kind: FileChangeKind.RENAME }),
|
|
208
|
+
captured({ id: "fc2", pathAfter: "src/new-name.ts", before: "a", after: "b", kind: FileChangeKind.MODIFY }),
|
|
209
|
+
]),
|
|
210
|
+
]);
|
|
211
|
+
expect(r.fileChanges[0].changeType).toBe(FileChangeType.RENAME);
|
|
212
|
+
expect(r.fileChanges[0].renameFrom).toBe("src/old-name.ts");
|
|
213
|
+
expect(r.fileChanges[0].after?.body.case === "inline" && r.fileChanges[0].after.body.value).toBe("b");
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("groups changes across change sets by path (the execution-scoped ledger covers sub-agents)", () => {
|
|
217
|
+
const exec = execWith("e1");
|
|
218
|
+
exec.status!.fileChangeSets = [
|
|
219
|
+
create(FileChangeSetSchema, {
|
|
220
|
+
id: "e1:0",
|
|
221
|
+
status: FileChangeSetStatus.RECONCILED,
|
|
222
|
+
changes: [captured({ id: "fc1", pathAfter: "src/shared.ts", before: "v0", after: "v1" })],
|
|
223
|
+
}),
|
|
224
|
+
create(FileChangeSetSchema, {
|
|
225
|
+
id: "e1:1",
|
|
226
|
+
status: FileChangeSetStatus.AWAITING_REVIEW,
|
|
227
|
+
changes: [captured({ id: "fc2", pathAfter: "src/shared.ts", before: "v1", after: "v2" })],
|
|
228
|
+
}),
|
|
229
|
+
];
|
|
230
|
+
const r = run([exec]);
|
|
231
|
+
expect(r.fileChangeCount).toBe(1);
|
|
232
|
+
const net = r.fileChanges[0];
|
|
233
|
+
expect(net.before?.body.case === "inline" && net.before.body.value).toBe("v0");
|
|
234
|
+
expect(net.after?.body.case === "inline" && net.after.body.value).toBe("v2");
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("passes a single change through with its bytes intact (no synthesis)", () => {
|
|
238
|
+
const r = run([
|
|
239
|
+
execWithProjection("e1", [
|
|
240
|
+
captured({ id: "fc1", pathAfter: "src/a.ts", before: "a", after: "b" }),
|
|
241
|
+
]),
|
|
242
|
+
]);
|
|
243
|
+
expect(r.fileChangeCount).toBe(1);
|
|
244
|
+
const only = r.fileChanges[0];
|
|
245
|
+
expect(only.path).toBe("src/a.ts");
|
|
246
|
+
expect(only.before?.body.case === "inline" && only.before.body.value).toBe("a");
|
|
247
|
+
expect(only.after?.body.case === "inline" && only.after.body.value).toBe("b");
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("sorts modified before created/renamed before deleted, then alpha", () => {
|
|
251
|
+
const r = run([
|
|
252
|
+
execWithProjection("e1", [
|
|
253
|
+
captured({ id: "fc1", pathBefore: "z-del.ts", pathAfter: "z-del.ts", before: "x", kind: FileChangeKind.DELETE }),
|
|
254
|
+
captured({ id: "fc2", pathAfter: "a-new.ts", after: "x", kind: FileChangeKind.ADD }),
|
|
255
|
+
captured({ id: "fc3", pathAfter: "m-mod.ts", before: "x", after: "y", kind: FileChangeKind.MODIFY }),
|
|
256
|
+
captured({ id: "fc4", pathAfter: "b-mod.ts", before: "x", after: "y", kind: FileChangeKind.MODIFY }),
|
|
257
|
+
]),
|
|
258
|
+
]);
|
|
259
|
+
expect(r.fileChanges.map((c) => c.path)).toEqual([
|
|
260
|
+
"b-mod.ts",
|
|
261
|
+
"m-mod.ts",
|
|
262
|
+
"a-new.ts",
|
|
263
|
+
"z-del.ts",
|
|
264
|
+
]);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it("net-diffs captures across turns from the ledger", () => {
|
|
268
|
+
// Two change sets touching the same path: first.before -> last.after.
|
|
269
|
+
const exec = execWith("e1");
|
|
270
|
+
exec.status!.fileChangeSets = [
|
|
271
|
+
create(FileChangeSetSchema, {
|
|
272
|
+
id: "e1:0",
|
|
273
|
+
status: FileChangeSetStatus.RECONCILED,
|
|
274
|
+
changes: [captured({ id: "fc1", pathAfter: "src/a.ts", before: "v0", after: "v1" })],
|
|
275
|
+
}),
|
|
276
|
+
create(FileChangeSetSchema, {
|
|
277
|
+
id: "e1:1",
|
|
278
|
+
status: FileChangeSetStatus.AWAITING_REVIEW,
|
|
279
|
+
changes: [captured({ id: "fc2", pathAfter: "src/a.ts", before: "v1", after: "v2" })],
|
|
280
|
+
}),
|
|
281
|
+
];
|
|
282
|
+
const r = run([exec]);
|
|
283
|
+
expect(r.fileChangeCount).toBe(1);
|
|
284
|
+
const net = r.fileChanges[0];
|
|
285
|
+
expect(net.before?.body.case === "inline" && net.before.body.value).toBe("v0");
|
|
286
|
+
expect(net.after?.body.case === "inline" && net.after.body.value).toBe("v2");
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("preserves referential stability across re-renders for the same executions", () => {
|
|
290
|
+
const executions = [
|
|
291
|
+
execWithProjection("e1", [
|
|
292
|
+
captured({ id: "fc1", pathAfter: "src/a.ts", before: "a", after: "b" }),
|
|
293
|
+
]),
|
|
294
|
+
];
|
|
295
|
+
const { result, rerender } = renderHook(
|
|
296
|
+
(e: readonly AgentExecution[]) => useSessionFileChanges(e),
|
|
297
|
+
{ initialProps: executions },
|
|
298
|
+
);
|
|
299
|
+
const first = result.current;
|
|
300
|
+
rerender(executions);
|
|
301
|
+
expect(result.current).toBe(first);
|
|
302
|
+
});
|
|
303
|
+
});
|
package/src/session/index.ts
CHANGED
|
@@ -45,6 +45,9 @@ export type {
|
|
|
45
45
|
UseSessionWriteBacksReturn,
|
|
46
46
|
} from "./useSessionWriteBacks";
|
|
47
47
|
|
|
48
|
+
export { useSessionFileChanges } from "./useSessionFileChanges";
|
|
49
|
+
export type { UseSessionFileChangesReturn } from "./useSessionFileChanges";
|
|
50
|
+
|
|
48
51
|
export { useSessionUsage } from "./useSessionUsage";
|
|
49
52
|
export type {
|
|
50
53
|
ModelCostEntry,
|