@stigmer/react 3.1.1 → 3.1.3
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/composer/SessionComposer.d.ts +25 -1
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +19 -8
- package/composer/SessionComposer.js.map +1 -1
- package/execution/ArtifactContentRenderer.d.ts +11 -1
- package/execution/ArtifactContentRenderer.d.ts.map +1 -1
- package/execution/ArtifactContentRenderer.js +39 -15
- package/execution/ArtifactContentRenderer.js.map +1 -1
- package/execution/ArtifactPreviewModal.d.ts +5 -5
- package/execution/ArtifactPreviewModal.js +4 -4
- package/execution/ArtifactPreviewModal.js.map +1 -1
- package/execution/FileChangeProgressBar.d.ts +47 -0
- package/execution/FileChangeProgressBar.d.ts.map +1 -0
- package/execution/FileChangeProgressBar.js +49 -0
- package/execution/FileChangeProgressBar.js.map +1 -0
- package/execution/FileChangesView.d.ts.map +1 -1
- package/execution/FileChangesView.js +19 -4
- package/execution/FileChangesView.js.map +1 -1
- package/execution/FilePathContext.d.ts +8 -1
- package/execution/FilePathContext.d.ts.map +1 -1
- package/execution/FilePathContext.js.map +1 -1
- package/execution/FilePathLink.d.ts.map +1 -1
- package/execution/FilePathLink.js +21 -6
- package/execution/FilePathLink.js.map +1 -1
- package/execution/FileReviewAtoms.d.ts +27 -0
- package/execution/FileReviewAtoms.d.ts.map +1 -0
- package/execution/FileReviewAtoms.js +64 -0
- package/execution/FileReviewAtoms.js.map +1 -0
- package/execution/FileReviewCard.d.ts +2 -2
- package/execution/FileReviewCard.d.ts.map +1 -1
- package/execution/FileReviewCard.js +32 -62
- package/execution/FileReviewCard.js.map +1 -1
- package/execution/MessageEntry.d.ts +20 -0
- package/execution/MessageEntry.d.ts.map +1 -1
- package/execution/MessageEntry.js +19 -5
- package/execution/MessageEntry.js.map +1 -1
- package/execution/MessageThread.d.ts +75 -13
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +195 -27
- package/execution/MessageThread.js.map +1 -1
- package/execution/PlanArtifactCard.d.ts +36 -21
- package/execution/PlanArtifactCard.d.ts.map +1 -1
- package/execution/PlanArtifactCard.js +39 -25
- package/execution/PlanArtifactCard.js.map +1 -1
- package/execution/PlanCompletionCard.d.ts +4 -4
- package/execution/PlanCompletionCard.d.ts.map +1 -1
- package/execution/PlanCompletionCard.js +4 -4
- package/execution/PlanCompletionCard.js.map +1 -1
- package/execution/PlanDocumentMessage.d.ts +32 -0
- package/execution/PlanDocumentMessage.d.ts.map +1 -0
- package/execution/PlanDocumentMessage.js +37 -0
- package/execution/PlanDocumentMessage.js.map +1 -0
- package/execution/PlanStreamingCard.d.ts +50 -0
- package/execution/PlanStreamingCard.d.ts.map +1 -0
- package/execution/PlanStreamingCard.js +40 -0
- package/execution/PlanStreamingCard.js.map +1 -0
- package/execution/index.d.ts +9 -1
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +4 -0
- package/execution/index.js.map +1 -1
- package/execution/use-build-from-plan-hotkey.d.ts +1 -1
- package/execution/use-build-from-plan-hotkey.js +1 -1
- package/execution/useArtifactCopy.d.ts +51 -0
- package/execution/useArtifactCopy.d.ts.map +1 -0
- package/execution/useArtifactCopy.js +75 -0
- package/execution/useArtifactCopy.js.map +1 -0
- package/execution/useArtifactDownload.d.ts.map +1 -1
- package/execution/useArtifactDownload.js +17 -5
- package/execution/useArtifactDownload.js.map +1 -1
- package/execution/useCreateAgentExecution.d.ts +12 -0
- package/execution/useCreateAgentExecution.d.ts.map +1 -1
- package/execution/useCreateAgentExecution.js +5 -1
- package/execution/useCreateAgentExecution.js.map +1 -1
- package/github/decodeGitHubContent.d.ts +29 -0
- package/github/decodeGitHubContent.d.ts.map +1 -0
- package/github/decodeGitHubContent.js +72 -0
- package/github/decodeGitHubContent.js.map +1 -0
- package/github/index.d.ts +1 -0
- package/github/index.d.ts.map +1 -1
- package/github/index.js +1 -0
- package/github/index.js.map +1 -1
- package/github/useGitHubFileReader.d.ts +26 -0
- package/github/useGitHubFileReader.d.ts.map +1 -0
- package/github/useGitHubFileReader.js +106 -0
- package/github/useGitHubFileReader.js.map +1 -0
- package/github/useGitHubTreeLister.d.ts.map +1 -1
- package/github/useGitHubTreeLister.js +6 -2
- package/github/useGitHubTreeLister.js.map +1 -1
- package/index.d.ts +9 -9
- package/index.d.ts.map +1 -1
- package/index.js +5 -5
- package/index.js.map +1 -1
- package/internal/ResizableSplit.d.ts +72 -22
- package/internal/ResizableSplit.d.ts.map +1 -1
- package/internal/ResizableSplit.js +96 -38
- package/internal/ResizableSplit.js.map +1 -1
- package/internal/VirtualizedThread.d.ts +5 -3
- package/internal/VirtualizedThread.d.ts.map +1 -1
- package/internal/VirtualizedThread.js +6 -4
- package/internal/VirtualizedThread.js.map +1 -1
- package/internal/file-icons/index.d.ts +17 -0
- package/internal/file-icons/index.d.ts.map +1 -0
- package/internal/file-icons/index.js +60 -0
- package/internal/file-icons/index.js.map +1 -0
- package/internal/file-tree/FileTreeNode.d.ts +18 -1
- package/internal/file-tree/FileTreeNode.d.ts.map +1 -1
- package/internal/file-tree/FileTreeNode.js +11 -2
- package/internal/file-tree/FileTreeNode.js.map +1 -1
- package/internal/markdown-components.d.ts +38 -1
- package/internal/markdown-components.d.ts.map +1 -1
- package/internal/markdown-components.js +86 -11
- package/internal/markdown-components.js.map +1 -1
- package/{workflow → internal}/motion-preference.d.ts +7 -4
- package/internal/motion-preference.d.ts.map +1 -0
- package/{workflow → internal}/motion-preference.js +7 -4
- package/internal/motion-preference.js.map +1 -0
- package/internal/store/index.d.ts +33 -0
- package/internal/store/index.d.ts.map +1 -1
- package/internal/store/index.js +59 -0
- package/internal/store/index.js.map +1 -1
- package/internal/store/workspace-editors-store.d.ts +145 -0
- package/internal/store/workspace-editors-store.d.ts.map +1 -0
- package/internal/store/workspace-editors-store.js +208 -0
- package/internal/store/workspace-editors-store.js.map +1 -0
- package/internal/store/workspace-file-selection-store.d.ts +38 -0
- package/internal/store/workspace-file-selection-store.d.ts.map +1 -0
- package/internal/store/workspace-file-selection-store.js +62 -0
- package/internal/store/workspace-file-selection-store.js.map +1 -0
- package/internal/useRevealLine.d.ts +49 -0
- package/internal/useRevealLine.d.ts.map +1 -0
- package/internal/useRevealLine.js +43 -0
- package/internal/useRevealLine.js.map +1 -0
- package/library/detect-plan-artifact.d.ts +43 -4
- package/library/detect-plan-artifact.d.ts.map +1 -1
- package/library/detect-plan-artifact.js +45 -5
- package/library/detect-plan-artifact.js.map +1 -1
- package/library/detect-streaming-plan.d.ts +45 -0
- package/library/detect-streaming-plan.d.ts.map +1 -0
- package/library/detect-streaming-plan.js +83 -0
- package/library/detect-streaming-plan.js.map +1 -0
- package/library/index.d.ts +4 -1
- package/library/index.d.ts.map +1 -1
- package/library/index.js +2 -1
- package/library/index.js.map +1 -1
- package/package.json +4 -4
- package/session/NewSessionViewer.d.ts +19 -4
- package/session/NewSessionViewer.d.ts.map +1 -1
- package/session/NewSessionViewer.js +52 -25
- package/session/NewSessionViewer.js.map +1 -1
- package/session/PlanEditor.d.ts +55 -0
- package/session/PlanEditor.d.ts.map +1 -0
- package/session/PlanEditor.js +88 -0
- package/session/PlanEditor.js.map +1 -0
- package/session/PlanStreamingDocument.d.ts +30 -0
- package/session/PlanStreamingDocument.d.ts.map +1 -0
- package/session/PlanStreamingDocument.js +37 -0
- package/session/PlanStreamingDocument.js.map +1 -0
- package/session/SessionPanelChip.d.ts +26 -0
- package/session/SessionPanelChip.d.ts.map +1 -0
- package/session/SessionPanelChip.js +40 -0
- package/session/SessionPanelChip.js.map +1 -0
- package/session/SessionViewer.d.ts +34 -7
- package/session/SessionViewer.d.ts.map +1 -1
- package/session/SessionViewer.js +334 -33
- package/session/SessionViewer.js.map +1 -1
- package/session/{inspector → facets}/ArtifactsTab.d.ts +10 -2
- package/session/facets/ArtifactsTab.d.ts.map +1 -0
- package/session/{inspector → facets}/ArtifactsTab.js +8 -3
- package/session/facets/ArtifactsTab.js.map +1 -0
- package/session/{inspector → facets}/ChangesTab.d.ts +3 -3
- package/session/facets/ChangesTab.d.ts.map +1 -0
- package/session/{inspector → facets}/ChangesTab.js +3 -3
- package/session/facets/ChangesTab.js.map +1 -0
- package/session/facets/InspectTab.d.ts.map +1 -0
- package/session/facets/InspectTab.js.map +1 -0
- package/session/{inspector → facets}/SetupTab.d.ts +11 -2
- package/session/facets/SetupTab.d.ts.map +1 -0
- package/session/{inspector → facets}/SetupTab.js +4 -3
- package/session/facets/SetupTab.js.map +1 -0
- package/session/{inspector → facets}/UsageTab.d.ts +1 -1
- package/session/facets/UsageTab.d.ts.map +1 -0
- package/session/{inspector → facets}/UsageTab.js +1 -1
- package/session/facets/UsageTab.js.map +1 -0
- package/session/{inspector → facets}/index.d.ts +0 -6
- package/session/facets/index.d.ts.map +1 -0
- package/session/{inspector → facets}/index.js +0 -3
- package/session/facets/index.js.map +1 -0
- package/session/index.d.ts +11 -2
- package/session/index.d.ts.map +1 -1
- package/session/index.js +15 -2
- package/session/index.js.map +1 -1
- package/session/plan-document.d.ts +21 -0
- package/session/plan-document.d.ts.map +1 -0
- package/session/plan-document.js +28 -0
- package/session/plan-document.js.map +1 -0
- package/session/useOpenFileChange.d.ts +20 -0
- package/session/useOpenFileChange.d.ts.map +1 -0
- package/session/useOpenFileChange.js +31 -0
- package/session/useOpenFileChange.js.map +1 -0
- package/session/usePlanDraft.d.ts +52 -0
- package/session/usePlanDraft.d.ts.map +1 -0
- package/session/usePlanDraft.js +63 -0
- package/session/usePlanDraft.js.map +1 -0
- package/session/useSessionConversation.d.ts +14 -1
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +20 -2
- package/session/useSessionConversation.js.map +1 -1
- package/session/useSessionPageFlow.d.ts.map +1 -1
- package/session/useSessionPageFlow.js +1 -0
- package/session/useSessionPageFlow.js.map +1 -1
- package/session/useSessionPanel.d.ts +120 -0
- package/session/useSessionPanel.d.ts.map +1 -0
- package/session/useSessionPanel.js +130 -0
- package/session/useSessionPanel.js.map +1 -0
- package/session/useSessionRailViews.d.ts +45 -0
- package/session/useSessionRailViews.d.ts.map +1 -0
- package/session/useSessionRailViews.js +105 -0
- package/session/useSessionRailViews.js.map +1 -0
- package/src/composer/SessionComposer.tsx +53 -10
- package/src/composer/__tests__/SessionComposer-contract.test.tsx +58 -4
- package/src/execution/ArtifactContentRenderer.tsx +101 -24
- package/src/execution/ArtifactPreviewModal.tsx +9 -9
- package/src/execution/FileChangeProgressBar.tsx +121 -0
- package/src/execution/FileChangesView.tsx +18 -4
- package/src/execution/FilePathContext.tsx +8 -1
- package/src/execution/FilePathLink.tsx +22 -6
- package/src/execution/FileReviewAtoms.tsx +89 -0
- package/src/execution/FileReviewCard.tsx +57 -90
- package/src/execution/MessageEntry.tsx +49 -1
- package/src/execution/MessageThread.tsx +308 -39
- package/src/execution/PlanArtifactCard.tsx +174 -64
- package/src/execution/PlanCompletionCard.tsx +5 -5
- package/src/execution/PlanDocumentMessage.tsx +96 -0
- package/src/execution/PlanStreamingCard.tsx +141 -0
- package/src/execution/__tests__/ArtifactContentRenderer.test.tsx +80 -1
- package/src/execution/__tests__/ArtifactPreviewModal.test.tsx +6 -6
- package/src/execution/__tests__/FileChangeProgressBar.test.tsx +125 -0
- package/src/execution/__tests__/FilePathLink.test.tsx +87 -0
- package/src/execution/__tests__/FileReviewCard.test.tsx +66 -7
- package/src/execution/__tests__/MessageThread.test.tsx +102 -1
- package/src/execution/__tests__/PlanArtifactCard.test.tsx +114 -26
- package/src/execution/__tests__/PlanCompletionCard.test.tsx +2 -2
- package/src/execution/__tests__/PlanDocumentMessage.test.tsx +55 -0
- package/src/execution/__tests__/PlanStreamingCard.test.tsx +56 -0
- package/src/execution/__tests__/buildThreadItems-plan-completion.test.ts +600 -22
- package/src/execution/__tests__/message-entry.test.tsx +17 -0
- package/src/execution/__tests__/useArtifactCopy.test.tsx +90 -0
- package/src/execution/__tests__/useArtifactDownload.test.tsx +2 -0
- package/src/execution/__tests__/useCreateAgentExecution.test.tsx +85 -0
- package/src/execution/index.ts +13 -1
- package/src/execution/use-build-from-plan-hotkey.ts +1 -1
- package/src/execution/useArtifactCopy.ts +109 -0
- package/src/execution/useArtifactDownload.ts +17 -5
- package/src/execution/useCreateAgentExecution.ts +18 -1
- package/src/github/__tests__/decodeGitHubContent.test.ts +102 -0
- package/src/github/__tests__/useGitHubFileReader.test.ts +183 -0
- package/src/github/decodeGitHubContent.ts +83 -0
- package/src/github/index.ts +1 -0
- package/src/github/useGitHubFileReader.ts +152 -0
- package/src/github/useGitHubTreeLister.ts +6 -2
- package/src/index.ts +69 -9
- package/src/internal/ResizableSplit.tsx +184 -51
- package/src/internal/VirtualizedThread.tsx +12 -4
- package/src/internal/__tests__/ResizableSplit.test.tsx +125 -3
- package/src/internal/__tests__/markdown-components.test.tsx +68 -0
- package/src/internal/__tests__/useRevealLine.test.tsx +97 -0
- package/src/internal/file-icons/__tests__/file-icons.test.tsx +35 -0
- package/src/internal/file-icons/index.tsx +137 -0
- package/src/internal/file-tree/FileTreeNode.tsx +56 -5
- package/src/internal/file-tree/__tests__/FileTreeNode-explorer.test.tsx +86 -0
- package/src/internal/markdown-components.tsx +116 -11
- package/src/{workflow → internal}/motion-preference.ts +7 -4
- package/src/internal/store/__tests__/workspace-editors-store.test.ts +216 -0
- package/src/internal/store/__tests__/workspace-file-selection-store.test.ts +118 -0
- package/src/internal/store/index.ts +94 -0
- package/src/internal/store/workspace-editors-store.ts +315 -0
- package/src/internal/store/workspace-file-selection-store.ts +88 -0
- package/src/internal/useRevealLine.ts +81 -0
- package/src/library/__tests__/detect-plan-artifact.test.ts +69 -0
- package/src/library/__tests__/detect-streaming-plan.test.ts +234 -0
- package/src/library/detect-plan-artifact.ts +62 -5
- package/src/library/detect-streaming-plan.ts +108 -0
- package/src/library/index.ts +7 -0
- package/src/session/NewSessionViewer.tsx +123 -55
- package/src/session/PlanEditor.tsx +361 -0
- package/src/session/PlanStreamingDocument.tsx +97 -0
- package/src/session/SessionPanelChip.tsx +103 -0
- package/src/session/SessionViewer.tsx +655 -96
- package/src/session/__tests__/NewSessionViewer.test.tsx +148 -0
- package/src/session/__tests__/PlanEditor.test.tsx +217 -0
- package/src/session/__tests__/PlanStreamingDocument.test.tsx +50 -0
- package/src/session/__tests__/SessionPanelChip.test.tsx +42 -0
- package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +66 -4
- package/src/session/__tests__/SessionViewer-transcriptFileOpen.test.tsx +205 -0
- package/src/session/__tests__/audienceWiring.test.tsx +55 -19
- package/src/session/__tests__/useOpenFileChange.test.tsx +82 -0
- package/src/session/__tests__/usePlanDraft.test.ts +102 -0
- package/src/session/__tests__/useSessionConversation.test.tsx +44 -0
- package/src/session/__tests__/useSessionPanel.test.ts +298 -0
- package/src/session/__tests__/useSessionRailViews.test.tsx +124 -0
- package/src/session/{inspector → facets}/ArtifactsTab.tsx +27 -5
- package/src/session/{inspector → facets}/ChangesTab.tsx +3 -3
- package/src/session/{inspector → facets}/SetupTab.tsx +13 -1
- package/src/session/{inspector → facets}/UsageTab.tsx +1 -1
- package/src/session/{inspector → facets}/__tests__/ArtifactsTab.test.tsx +42 -8
- package/src/session/facets/__tests__/SetupTab.test.tsx +44 -0
- package/src/session/{inspector → facets}/index.ts +0 -16
- package/src/session/index.ts +30 -13
- package/src/session/plan-document.ts +30 -0
- package/src/session/useOpenFileChange.ts +53 -0
- package/src/session/usePlanDraft.ts +109 -0
- package/src/session/useSessionConversation.ts +38 -3
- package/src/session/useSessionPageFlow.ts +1 -0
- package/src/session/useSessionPanel.ts +288 -0
- package/src/session/useSessionRailViews.tsx +200 -0
- package/src/workflow/WorkflowExecutionGraph.tsx +1 -1
- package/src/workflow/index.ts +1 -1
- package/src/workflow/useFollowExecution.ts +1 -1
- package/src/workspace/EditorTabs.tsx +174 -0
- package/src/workspace/ExplorerTree.tsx +253 -0
- package/src/workspace/FileViewer.tsx +537 -0
- package/src/workspace/WorkspaceContentSearch.tsx +502 -0
- package/src/workspace/WorkspaceContentSearcher.ts +70 -0
- package/src/workspace/WorkspaceEditor.tsx +19 -16
- package/src/workspace/WorkspaceFileLister.ts +11 -0
- package/src/workspace/WorkspaceFileReader.ts +57 -0
- package/src/workspace/WorkspaceFileSearch.tsx +452 -0
- package/src/workspace/WorkspaceSurface.tsx +839 -0
- package/src/workspace/__tests__/EditorTabs.test.tsx +102 -0
- package/src/workspace/__tests__/ExplorerTree.test.tsx +204 -0
- package/src/workspace/__tests__/FileViewer.test.tsx +439 -0
- package/src/workspace/__tests__/WorkspaceContentSearch.test.tsx +229 -0
- package/src/workspace/__tests__/WorkspaceEditor-initialPanel.test.tsx +19 -0
- package/src/workspace/__tests__/WorkspaceFileSearch.test.tsx +227 -0
- package/src/workspace/__tests__/WorkspaceSurface.test.tsx +418 -0
- package/src/workspace/__tests__/a11y/harness.tsx +140 -0
- package/src/workspace/__tests__/a11y/mock-capabilities.ts +138 -0
- package/src/workspace/__tests__/a11y/workspace-editor.a11y.test.tsx +65 -0
- package/src/workspace/__tests__/a11y/workspace-explorer-search.a11y.test.tsx +112 -0
- package/src/workspace/__tests__/a11y/workspace-viewer-tabs.a11y.test.tsx +69 -0
- package/src/workspace/__tests__/findChangeForSelection.test.ts +132 -0
- package/src/workspace/__tests__/matchContentPreview.test.ts +55 -0
- package/src/workspace/__tests__/matchWorkspaceFiles.test.ts +104 -0
- package/src/workspace/__tests__/resolveWorkspaceFileSelection.test.ts +135 -0
- package/src/workspace/__tests__/useWorkspaceContentSearch.test.tsx +196 -0
- package/src/workspace/__tests__/useWorkspaceFileContent.test.tsx +190 -0
- package/src/workspace/__tests__/useWorkspaceFileSearch.test.tsx +162 -0
- package/src/workspace/findChangeForSelection.ts +61 -0
- package/src/workspace/index.ts +56 -2
- package/src/workspace/matchContentPreview.ts +51 -0
- package/src/workspace/matchWorkspaceFiles.ts +86 -0
- package/src/workspace/resolveWorkspaceFileSelection.ts +119 -0
- package/src/workspace/useWorkspaceContentSearch.ts +228 -0
- package/src/workspace/useWorkspaceFileContent.ts +129 -0
- package/src/workspace/useWorkspaceFileSearch.ts +184 -0
- package/src/workspace/useWorkspaceFiles.ts +27 -27
- package/src/workspace/workspaceListingCache.ts +93 -0
- package/styles.css +1 -1
- package/workflow/WorkflowExecutionGraph.js +1 -1
- package/workflow/WorkflowExecutionGraph.js.map +1 -1
- package/workflow/index.d.ts +1 -1
- package/workflow/index.d.ts.map +1 -1
- package/workflow/index.js +1 -1
- package/workflow/index.js.map +1 -1
- package/workflow/useFollowExecution.js +1 -1
- package/workflow/useFollowExecution.js.map +1 -1
- package/workspace/EditorTabs.d.ts +47 -0
- package/workspace/EditorTabs.d.ts.map +1 -0
- package/workspace/EditorTabs.js +78 -0
- package/workspace/EditorTabs.js.map +1 -0
- package/workspace/ExplorerTree.d.ts +32 -0
- package/workspace/ExplorerTree.d.ts.map +1 -0
- package/workspace/ExplorerTree.js +49 -0
- package/workspace/ExplorerTree.js.map +1 -0
- package/workspace/FileViewer.d.ts +93 -0
- package/workspace/FileViewer.d.ts.map +1 -0
- package/workspace/FileViewer.js +193 -0
- package/workspace/FileViewer.js.map +1 -0
- package/workspace/WorkspaceContentSearch.d.ts +41 -0
- package/workspace/WorkspaceContentSearch.d.ts.map +1 -0
- package/workspace/WorkspaceContentSearch.js +216 -0
- package/workspace/WorkspaceContentSearch.js.map +1 -0
- package/workspace/WorkspaceContentSearcher.d.ts +59 -0
- package/workspace/WorkspaceContentSearcher.d.ts.map +1 -0
- package/workspace/WorkspaceContentSearcher.js +7 -0
- package/workspace/WorkspaceContentSearcher.js.map +1 -0
- package/workspace/WorkspaceEditor.d.ts.map +1 -1
- package/workspace/WorkspaceEditor.js +8 -10
- package/workspace/WorkspaceEditor.js.map +1 -1
- package/workspace/WorkspaceFileLister.d.ts +11 -0
- package/workspace/WorkspaceFileLister.d.ts.map +1 -1
- package/workspace/WorkspaceFileReader.d.ts +48 -0
- package/workspace/WorkspaceFileReader.d.ts.map +1 -0
- package/workspace/WorkspaceFileReader.js +11 -0
- package/workspace/WorkspaceFileReader.js.map +1 -0
- package/workspace/WorkspaceFileSearch.d.ts +32 -0
- package/workspace/WorkspaceFileSearch.d.ts.map +1 -0
- package/workspace/WorkspaceFileSearch.js +175 -0
- package/workspace/WorkspaceFileSearch.js.map +1 -0
- package/workspace/WorkspaceSurface.d.ts +151 -0
- package/workspace/WorkspaceSurface.d.ts.map +1 -0
- package/workspace/WorkspaceSurface.js +220 -0
- package/workspace/WorkspaceSurface.js.map +1 -0
- package/workspace/findChangeForSelection.d.ts +19 -0
- package/workspace/findChangeForSelection.d.ts.map +1 -0
- package/workspace/findChangeForSelection.js +46 -0
- package/workspace/findChangeForSelection.js.map +1 -0
- package/workspace/index.d.ts +24 -2
- package/workspace/index.d.ts.map +1 -1
- package/workspace/index.js +13 -1
- package/workspace/index.js.map +1 -1
- package/workspace/matchContentPreview.d.ts +21 -0
- package/workspace/matchContentPreview.d.ts.map +1 -0
- package/workspace/matchContentPreview.js +36 -0
- package/workspace/matchContentPreview.js.map +1 -0
- package/workspace/matchWorkspaceFiles.d.ts +30 -0
- package/workspace/matchWorkspaceFiles.d.ts.map +1 -0
- package/workspace/matchWorkspaceFiles.js +58 -0
- package/workspace/matchWorkspaceFiles.js.map +1 -0
- package/workspace/resolveWorkspaceFileSelection.d.ts +19 -0
- package/workspace/resolveWorkspaceFileSelection.d.ts.map +1 -0
- package/workspace/resolveWorkspaceFileSelection.js +115 -0
- package/workspace/resolveWorkspaceFileSelection.js.map +1 -0
- package/workspace/useWorkspaceContentSearch.d.ts +69 -0
- package/workspace/useWorkspaceContentSearch.d.ts.map +1 -0
- package/workspace/useWorkspaceContentSearch.js +154 -0
- package/workspace/useWorkspaceContentSearch.js.map +1 -0
- package/workspace/useWorkspaceFileContent.d.ts +73 -0
- package/workspace/useWorkspaceFileContent.d.ts.map +1 -0
- package/workspace/useWorkspaceFileContent.js +57 -0
- package/workspace/useWorkspaceFileContent.js.map +1 -0
- package/workspace/useWorkspaceFileSearch.d.ts +61 -0
- package/workspace/useWorkspaceFileSearch.d.ts.map +1 -0
- package/workspace/useWorkspaceFileSearch.js +119 -0
- package/workspace/useWorkspaceFileSearch.js.map +1 -0
- package/workspace/useWorkspaceFiles.d.ts +13 -5
- package/workspace/useWorkspaceFiles.d.ts.map +1 -1
- package/workspace/useWorkspaceFiles.js +18 -19
- package/workspace/useWorkspaceFiles.js.map +1 -1
- package/workspace/workspaceListingCache.d.ts +47 -0
- package/workspace/workspaceListingCache.d.ts.map +1 -0
- package/workspace/workspaceListingCache.js +55 -0
- package/workspace/workspaceListingCache.js.map +1 -0
- package/session/inspector/ArtifactsTab.d.ts.map +0 -1
- package/session/inspector/ArtifactsTab.js.map +0 -1
- package/session/inspector/ChangesTab.d.ts.map +0 -1
- package/session/inspector/ChangesTab.js.map +0 -1
- package/session/inspector/InspectTab.d.ts.map +0 -1
- package/session/inspector/InspectTab.js.map +0 -1
- package/session/inspector/SessionInspector.d.ts +0 -64
- package/session/inspector/SessionInspector.d.ts.map +0 -1
- package/session/inspector/SessionInspector.js +0 -53
- package/session/inspector/SessionInspector.js.map +0 -1
- package/session/inspector/SetupTab.d.ts.map +0 -1
- package/session/inspector/SetupTab.js.map +0 -1
- package/session/inspector/UsageTab.d.ts.map +0 -1
- package/session/inspector/UsageTab.js.map +0 -1
- package/session/inspector/WorkspaceTab.d.ts +0 -35
- package/session/inspector/WorkspaceTab.d.ts.map +0 -1
- package/session/inspector/WorkspaceTab.js +0 -75
- package/session/inspector/WorkspaceTab.js.map +0 -1
- package/session/inspector/index.d.ts.map +0 -1
- package/session/inspector/index.js.map +0 -1
- package/session/inspector/useSessionInspector.d.ts +0 -49
- package/session/inspector/useSessionInspector.d.ts.map +0 -1
- package/session/inspector/useSessionInspector.js +0 -122
- package/session/inspector/useSessionInspector.js.map +0 -1
- package/src/session/inspector/SessionInspector.tsx +0 -162
- package/src/session/inspector/WorkspaceTab.tsx +0 -245
- package/src/session/inspector/__tests__/useSessionInspector.test.ts +0 -251
- package/src/session/inspector/useSessionInspector.ts +0 -178
- package/src/workspace/WorkspaceEntryFiles.tsx +0 -196
- package/workflow/motion-preference.d.ts.map +0 -1
- package/workflow/motion-preference.js.map +0 -1
- package/workspace/WorkspaceEntryFiles.d.ts +0 -22
- package/workspace/WorkspaceEntryFiles.d.ts.map +0 -1
- package/workspace/WorkspaceEntryFiles.js +0 -58
- package/workspace/WorkspaceEntryFiles.js.map +0 -1
- /package/session/{inspector → facets}/InspectTab.d.ts +0 -0
- /package/session/{inspector → facets}/InspectTab.js +0 -0
- /package/src/{workflow → internal}/__tests__/motion-preference.test.ts +0 -0
- /package/src/session/{inspector → facets}/InspectTab.tsx +0 -0
- /package/src/session/{inspector → facets}/__tests__/ChangesTab.test.tsx +0 -0
|
@@ -1,33 +1,134 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useCallback, useMemo, useRef, type ReactNode } from "react";
|
|
3
|
+
import { memo, useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
|
4
|
+
import { create } from "@bufbuild/protobuf";
|
|
4
5
|
import { cn } from "@stigmer/theme";
|
|
5
|
-
import { getUserMessage, type ResourceRef } from "@stigmer/sdk";
|
|
6
|
+
import { getUserMessage, type AttachmentInput, type ResourceRef } from "@stigmer/sdk";
|
|
7
|
+
import {
|
|
8
|
+
GetArtifactContentRequestSchema,
|
|
9
|
+
UploadAttachmentRequestSchema,
|
|
10
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
|
|
6
11
|
import type { UseGitHubConnectionReturn } from "../github/useGitHubConnection.js";
|
|
7
12
|
import type { WorkspaceFileLister } from "../workspace/WorkspaceFileLister.js";
|
|
13
|
+
import type { WorkspaceFileReader } from "../workspace/WorkspaceFileReader.js";
|
|
14
|
+
import type { WorkspaceContentSearcher } from "../workspace/WorkspaceContentSearcher.js";
|
|
15
|
+
import { resolveWorkspaceFileSelection } from "../workspace/resolveWorkspaceFileSelection.js";
|
|
16
|
+
import {
|
|
17
|
+
WorkspaceSurface,
|
|
18
|
+
type SurfaceVirtualDocument,
|
|
19
|
+
} from "../workspace/WorkspaceSurface.js";
|
|
8
20
|
import type { InteractionModeOption, SessionComposerHandle } from "../composer/index.js";
|
|
9
21
|
import type { ApplyResourceResult } from "../library/useApplyResource.js";
|
|
10
22
|
import { ResizableSplit } from "../internal/ResizableSplit.js";
|
|
11
23
|
import { SelectionStore } from "../internal/store/selection-store.js";
|
|
24
|
+
import { useWorkspaceEditors, isVirtualEntryId } from "../internal/store/index.js";
|
|
12
25
|
import { ThreadSelectionContext } from "../execution/ThreadSelectionContext.js";
|
|
13
26
|
import { useSelectedThreadItem } from "../execution/useThreadSelection.js";
|
|
14
27
|
import { MessageThread } from "../execution/MessageThread.js";
|
|
28
|
+
import { FileChangeProgressBar } from "../execution/FileChangeProgressBar.js";
|
|
15
29
|
import { FileReviewDock } from "../execution/FileReviewDock.js";
|
|
30
|
+
import {
|
|
31
|
+
FilePathContext,
|
|
32
|
+
type FilePathContextValue,
|
|
33
|
+
} from "../execution/FilePathContext.js";
|
|
16
34
|
import { ThreadSkeleton } from "../execution/ThreadSkeleton.js";
|
|
17
35
|
import { SessionComposer } from "../composer/index.js";
|
|
18
36
|
import { SecretFlowErrorGuide, isSecretFlowError } from "../error/index.js";
|
|
37
|
+
import { useStigmer } from "../hooks.js";
|
|
38
|
+
import {
|
|
39
|
+
findLatestSessionPlan,
|
|
40
|
+
findPlanArtifact,
|
|
41
|
+
type SessionPlan,
|
|
42
|
+
} from "../library/detect-plan-artifact.js";
|
|
43
|
+
import { findStreamingPlan } from "../library/detect-streaming-plan.js";
|
|
19
44
|
import { useSessionPageFlow } from "./useSessionPageFlow.js";
|
|
20
|
-
import {
|
|
45
|
+
import { useOpenFileChange } from "./useOpenFileChange.js";
|
|
46
|
+
import { usePlanDraft, planDraftKey, type PlanDraftController } from "./usePlanDraft.js";
|
|
47
|
+
import { PlanEditor } from "./PlanEditor.js";
|
|
48
|
+
import { PlanStreamingDocument } from "./PlanStreamingDocument.js";
|
|
49
|
+
import { PLAN_DOCUMENT_ENTRY_ID, PLAN_DOCUMENT_PATH } from "./plan-document.js";
|
|
50
|
+
import { useSessionRailViews } from "./useSessionRailViews.js";
|
|
51
|
+
import { useSessionPanel, type SessionPanelController } from "./useSessionPanel.js";
|
|
52
|
+
import { SessionPanelChip } from "./SessionPanelChip.js";
|
|
53
|
+
import { useSessionWriteBacks } from "./useSessionWriteBacks.js";
|
|
54
|
+
import { useSessionArtifacts } from "./useSessionArtifacts.js";
|
|
55
|
+
import type { SetupTabProps } from "./facets/SetupTab.js";
|
|
56
|
+
import { ExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
21
57
|
import type { RuntimeEnvProvider } from "./runtime-env.js";
|
|
22
58
|
import type { SessionAudience } from "./audience.js";
|
|
23
59
|
|
|
24
60
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
61
|
+
* Where the approved plan mounts in the implement execution's workspace —
|
|
62
|
+
* the harnesses' standard attachment inputs directory, where the runner's
|
|
63
|
+
* implement-plan directive points the agent (shared/implement-plan-prompt.ts).
|
|
64
|
+
* Attaching under the plan's own filename (e.g. `feature-x_a1b2c3d4.plan.md`) is what
|
|
65
|
+
* makes the build implement the document the user approved (including in-place
|
|
66
|
+
* edits), not a paraphrase from memory; the runner detects it by the plan
|
|
67
|
+
* filename convention (`isPlanArtifactName`), so the exact name is free to vary.
|
|
28
68
|
*/
|
|
29
|
-
|
|
30
|
-
|
|
69
|
+
function approvedPlanMountPath(planFileName: string): string {
|
|
70
|
+
return `.stigmer/inputs/${planFileName}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The build turn's user message — a short human-readable label, NOT the
|
|
75
|
+
* implement instruction. The agent-facing instruction is runner-injected
|
|
76
|
+
* (keyed off `executionConfig.buildFromPlan`). The thread hides the turn
|
|
77
|
+
* entirely from the same flag (the plan card above it is the visible cause);
|
|
78
|
+
* this label exists for surfaces without that treatment (the CLI, execution
|
|
79
|
+
* history, older clients), which show it as-is.
|
|
80
|
+
*/
|
|
81
|
+
const BUILD_FROM_PLAN_MESSAGE = "Build from plan";
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* A plan the active execution is writing right now, resolved at the viewer
|
|
85
|
+
* level (from `findStreamingPlan`) so the panel's plan tab can render it
|
|
86
|
+
* live. The streaming sibling of {@link SessionPlan}: `executionId` keys the
|
|
87
|
+
* document (a new turn's stream resets the tab's view state) and forms the
|
|
88
|
+
* `<executionId>:streaming` identity for the plan-tab auto-open trigger.
|
|
89
|
+
*/
|
|
90
|
+
interface SessionStreamingPlan {
|
|
91
|
+
/** ID of the execution writing the plan. */
|
|
92
|
+
readonly executionId: string;
|
|
93
|
+
/** The live plan text (fence-stripped display projection). */
|
|
94
|
+
readonly displayText: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Reads the published plan's full text for the build handoff. Refuses a
|
|
99
|
+
* truncated read (content RPC caps at 512 KB): attaching a partial plan would
|
|
100
|
+
* silently implement half a document — the caller falls back to
|
|
101
|
+
* conversation-history implement instead.
|
|
102
|
+
*/
|
|
103
|
+
async function fetchPlanText(
|
|
104
|
+
stigmer: ReturnType<typeof useStigmer>,
|
|
105
|
+
plan: SessionPlan,
|
|
106
|
+
): Promise<string> {
|
|
107
|
+
const result = await stigmer.agentExecution.getArtifactContent(
|
|
108
|
+
create(GetArtifactContentRequestSchema, {
|
|
109
|
+
executionId: plan.executionId,
|
|
110
|
+
storageKey: plan.artifact.storageKey,
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
if (result.truncated) {
|
|
114
|
+
throw new Error("plan content truncated — not attaching a partial plan");
|
|
115
|
+
}
|
|
116
|
+
return new TextDecoder().decode(result.content);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Width and anchoring of the conversation reading column, shared by the
|
|
121
|
+
* thread and the composer/banners block so they can never drift apart.
|
|
122
|
+
*
|
|
123
|
+
* Centered (the classic chat reading view, matching Cursor's agent panel):
|
|
124
|
+
* with the session panel collapsed the column sits in the middle of the
|
|
125
|
+
* row; opening the panel narrows the chat pane below the column max, so
|
|
126
|
+
* the column simply fills it. The thread applies the same geometry via
|
|
127
|
+
* `contentColumn="center"`, which adds the item gutter; the composer/banner
|
|
128
|
+
* block's children carry their own edge padding, so only width and
|
|
129
|
+
* anchoring live here.
|
|
130
|
+
*/
|
|
131
|
+
const CONVERSATION_COLUMN_CLASS = "mx-auto w-full max-w-3xl";
|
|
31
132
|
|
|
32
133
|
/** Props for {@link SessionViewer}. */
|
|
33
134
|
export interface SessionViewerProps {
|
|
@@ -70,6 +171,21 @@ export interface SessionViewerProps {
|
|
|
70
171
|
* expandable file tree. (DD-004 capability injection, DD-011 opt-in.)
|
|
71
172
|
*/
|
|
72
173
|
readonly workspaceFileLister?: WorkspaceFileLister;
|
|
174
|
+
/**
|
|
175
|
+
* Platform-injected content reader for the read-only file viewer. When
|
|
176
|
+
* provided, clicking a file in the Workspace tree opens it in a contextual
|
|
177
|
+
* "Viewer" tab. GitHub Contents/blob on web, Tauri fs on desktop; `undefined`
|
|
178
|
+
* degrades the viewer to an "unavailable here" state (DD-004, DD-011 opt-in).
|
|
179
|
+
*/
|
|
180
|
+
readonly workspaceFileReader?: WorkspaceFileReader;
|
|
181
|
+
/**
|
|
182
|
+
* Platform-injected content (text) searcher. When provided, the session
|
|
183
|
+
* panel's Search pane gains a `Name | Text` toggle for full-text search
|
|
184
|
+
* across the workspace. Desktop injects a native ripgrep-backed searcher;
|
|
185
|
+
* web leaves it undefined (git content search needs a branch-accurate
|
|
186
|
+
* backend — DD-09), keeping Search filename-only there (DD-004, DD-011 opt-in).
|
|
187
|
+
*/
|
|
188
|
+
readonly workspaceContentSearcher?: WorkspaceContentSearcher;
|
|
73
189
|
/**
|
|
74
190
|
* Supplies host-app environment variables for every follow-up
|
|
75
191
|
* execution (e.g. short-lived credentials for MCP tools, minted as
|
|
@@ -92,11 +208,18 @@ export interface SessionViewerProps {
|
|
|
92
208
|
*/
|
|
93
209
|
readonly audience?: SessionAudience;
|
|
94
210
|
/**
|
|
95
|
-
* Slot for host-injected header actions
|
|
96
|
-
*
|
|
97
|
-
*
|
|
211
|
+
* Slot for host-injected header actions. Rendered in the top-right corner
|
|
212
|
+
* of the viewer, beside the panel chip. Keeps the SDK organism
|
|
213
|
+
* unopinionated about Console auth (DD-004).
|
|
98
214
|
*/
|
|
99
215
|
readonly headerActions?: ReactNode;
|
|
216
|
+
/**
|
|
217
|
+
* Host-injected access management control (e.g. the Console's
|
|
218
|
+
* `ManageAccessButton` with its own permission gating). Rendered inside
|
|
219
|
+
* the panel's Config facet rather than the header — access is session
|
|
220
|
+
* configuration, not a moment-to-moment action (DD-004 slot injection).
|
|
221
|
+
*/
|
|
222
|
+
readonly accessSlot?: ReactNode;
|
|
100
223
|
/** Called after a resource is applied from the Artifacts tab. */
|
|
101
224
|
readonly onApplied?: (result: ApplyResourceResult) => void;
|
|
102
225
|
/** Additional CSS classes for the root container. */
|
|
@@ -110,10 +233,13 @@ export interface SessionViewerProps {
|
|
|
110
233
|
* Owns `useSessionPageFlow` internally and composes:
|
|
111
234
|
* - **Conversation column** (primary): `MessageThread` + error
|
|
112
235
|
* banners + `SessionComposer`
|
|
113
|
-
* - **
|
|
114
|
-
*
|
|
236
|
+
* - **Session panel** (secondary): the unified `WorkspaceSurface` — explorer,
|
|
237
|
+
* search, read-only editor, and the session facets (Config, Changes,
|
|
238
|
+
* Artifacts, Usage, Inspect) as injected rail views. Collapsed by default
|
|
239
|
+
* to a top-right chip; opening a file (tree, search, or a transcript path)
|
|
240
|
+
* expands it.
|
|
115
241
|
*
|
|
116
|
-
* Connected via `ResizableSplit` with persisted width.
|
|
242
|
+
* Connected via `ResizableSplit` with persisted chat width.
|
|
117
243
|
*
|
|
118
244
|
* Thread selection is provided via `ThreadSelectionContext` (Phase 2
|
|
119
245
|
* opt-in) for render-isolated per-item selection.
|
|
@@ -150,9 +276,12 @@ export function SessionViewer({
|
|
|
150
276
|
enableLocal = false,
|
|
151
277
|
onBrowseLocalFolder,
|
|
152
278
|
workspaceFileLister,
|
|
279
|
+
workspaceFileReader,
|
|
280
|
+
workspaceContentSearcher,
|
|
153
281
|
getRuntimeEnv,
|
|
154
282
|
audience = "integrator",
|
|
155
283
|
headerActions,
|
|
284
|
+
accessSlot,
|
|
156
285
|
onApplied,
|
|
157
286
|
className,
|
|
158
287
|
}: SessionViewerProps) {
|
|
@@ -170,16 +299,182 @@ export function SessionViewer({
|
|
|
170
299
|
}
|
|
171
300
|
const selectionStore = selectionStoreRef.current;
|
|
172
301
|
|
|
302
|
+
const phase =
|
|
303
|
+
flow.displayExecution?.status?.phase ??
|
|
304
|
+
ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED;
|
|
305
|
+
const hasPhase = phase !== ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED;
|
|
306
|
+
|
|
307
|
+
// Facet arrival counts: rail badges inside the panel, the chip's dot-count
|
|
308
|
+
// while collapsed. Arrivals never auto-open the panel (badge-only signal).
|
|
309
|
+
const { hasWriteBacks, writeBackCount } = useSessionWriteBacks(flow.allExecutions);
|
|
310
|
+
const { artifactCount } = useSessionArtifacts(flow.allExecutions);
|
|
311
|
+
|
|
312
|
+
// The session's current plan (latest published plan.md across executions)
|
|
313
|
+
// and its viewer-owned draft. Ownership at this level is deliberate: the
|
|
314
|
+
// panel unmounts facet content on every view switch, so a facet-local
|
|
315
|
+
// draft would silently lose the user's edits (see usePlanDraft).
|
|
316
|
+
const stigmer = useStigmer();
|
|
317
|
+
const sessionPlan = useMemo(
|
|
318
|
+
() => findLatestSessionPlan(flow.allExecutions),
|
|
319
|
+
[flow.allExecutions],
|
|
320
|
+
);
|
|
321
|
+
const planDraft = usePlanDraft(sessionPlan);
|
|
322
|
+
|
|
323
|
+
// The plan the ACTIVE turn is writing right now (the H1 convention — see
|
|
324
|
+
// findStreamingPlan), resolved at this level for the same reason
|
|
325
|
+
// sessionPlan is: the panel's plan tab renders it. Recomputed per stream
|
|
326
|
+
// commit by design — this component already re-renders on every commit
|
|
327
|
+
// (it reads `flow`), and the scan touches only the last AI message.
|
|
328
|
+
const streamingPlan = useMemo<SessionStreamingPlan | undefined>(() => {
|
|
329
|
+
const activeExec = conv.activeStreamExecution;
|
|
330
|
+
const live = findStreamingPlan(activeExec);
|
|
331
|
+
const executionId = activeExec?.metadata?.id;
|
|
332
|
+
return live && executionId
|
|
333
|
+
? { executionId, displayText: live.displayText }
|
|
334
|
+
: undefined;
|
|
335
|
+
}, [conv.activeStreamExecution]);
|
|
336
|
+
const [isBuildingFromPlan, setIsBuildingFromPlan] = useState(false);
|
|
337
|
+
const [planAttachFailed, setPlanAttachFailed] = useState(false);
|
|
338
|
+
|
|
339
|
+
// WHICH plan the panel's plan document tab shows. `null` means the session's
|
|
340
|
+
// current (latest) plan — the editable, buildable one; an execution id
|
|
341
|
+
// selects that turn's historical plan, rendered read-only. One tab, host-
|
|
342
|
+
// controlled content (Decision 3 in DD-16).
|
|
343
|
+
const [openPlanExecutionId, setOpenPlanExecutionId] = useState<string | null>(null);
|
|
344
|
+
|
|
345
|
+
// The plan tab's identity across both plan lifecycles: while a plan
|
|
346
|
+
// streams, the in-flight turn owns the tab (`<executionId>:streaming`);
|
|
347
|
+
// once published, the artifact identity (planDraftKey) takes over. Each
|
|
348
|
+
// transition is a NEW identity, so useSessionPanel's planKey trigger
|
|
349
|
+
// auto-opens the tab the moment a plan starts streaming AND (idempotently)
|
|
350
|
+
// when it settles into the published plan.
|
|
351
|
+
const currentPlanKey = sessionPlan ? planDraftKey(sessionPlan) : null;
|
|
352
|
+
const panelPlanKey = streamingPlan
|
|
353
|
+
? `${streamingPlan.executionId}:streaming`
|
|
354
|
+
: currentPlanKey;
|
|
355
|
+
|
|
356
|
+
// A new plan identity (a plan starting to stream, the first published
|
|
357
|
+
// plan, or a refinement) supersedes any historical plan the user had
|
|
358
|
+
// opened: the tab snaps back to "latest" so the auto-open always surfaces
|
|
359
|
+
// the incoming plan. Adjust-state-during-render — own state only, the
|
|
360
|
+
// established idiom.
|
|
361
|
+
const [prevPlanKey, setPrevPlanKey] = useState(panelPlanKey);
|
|
362
|
+
if (panelPlanKey !== prevPlanKey) {
|
|
363
|
+
setPrevPlanKey(panelPlanKey);
|
|
364
|
+
setOpenPlanExecutionId(null);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// The unified-panel controller: owns the open-editor group store, the
|
|
368
|
+
// open/collapsed state, and the rail-view FSM. Shared with the launcher
|
|
369
|
+
// (DD-016). The editor store is owned here (never subscribed at this level)
|
|
370
|
+
// so opening/switching files re-renders only the panel subtree — the
|
|
371
|
+
// SessionPanelRegion subscribes, not the conversation column — preserving
|
|
372
|
+
// streaming render isolation (DD-009/DD-010, invariant 2).
|
|
373
|
+
const panel = useSessionPanel({
|
|
374
|
+
phase: hasPhase ? phase : null,
|
|
375
|
+
hasChanges: hasWriteBacks,
|
|
376
|
+
planKey: panelPlanKey,
|
|
377
|
+
});
|
|
378
|
+
|
|
173
379
|
const handleBuildFromPlan = useCallback(() => {
|
|
174
380
|
// Switch the picker to Agent (so subsequent turns stay in Agent) and submit
|
|
175
|
-
// the implement message
|
|
381
|
+
// the implement message through the composer's full pipeline.
|
|
176
382
|
// `interactionMode: "agent"` is passed explicitly to win the same-tick race
|
|
177
383
|
// where the composer prop has not yet re-rendered from "plan".
|
|
178
384
|
setInteractionMode("agent");
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
385
|
+
setPlanAttachFailed(false);
|
|
386
|
+
|
|
387
|
+
// `buildFromPlan` (not message text) is what tells the runner to inject
|
|
388
|
+
// the implement directive and the thread to hide the turn's message —
|
|
389
|
+
// with or without the attachment (the runner picks the directive variant).
|
|
390
|
+
const submitImplementTurn = (attachments?: AttachmentInput[]) => {
|
|
391
|
+
composerRef.current?.submit(BUILD_FROM_PLAN_MESSAGE, {
|
|
392
|
+
interactionMode: "agent",
|
|
393
|
+
buildFromPlan: true,
|
|
394
|
+
attachments,
|
|
395
|
+
});
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
// No published artifact (the bare-CTA fallback card) — nothing to attach.
|
|
399
|
+
if (!sessionPlan) {
|
|
400
|
+
submitImplementTurn();
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Deterministic handoff: upload the APPROVED plan (draft if edited, else
|
|
405
|
+
// the artifact text — one uniform path) and attach it to the implement
|
|
406
|
+
// execution. The published artifact itself stays immutable; the approved
|
|
407
|
+
// copy is a new input on the build turn (edit-as-input provenance).
|
|
408
|
+
setIsBuildingFromPlan(true);
|
|
409
|
+
// The approved copy keeps the published plan's filename, so the mounted
|
|
410
|
+
// input, the download, and the plan card all agree on one name.
|
|
411
|
+
const planFileName = sessionPlan.artifact.name;
|
|
412
|
+
void (async () => {
|
|
413
|
+
try {
|
|
414
|
+
const approvedText =
|
|
415
|
+
planDraft.readDraft() ?? (await fetchPlanText(stigmer, sessionPlan));
|
|
416
|
+
const response = await stigmer.agentExecution.uploadAttachment(
|
|
417
|
+
create(UploadAttachmentRequestSchema, {
|
|
418
|
+
filename: planFileName,
|
|
419
|
+
content: new TextEncoder().encode(approvedText),
|
|
420
|
+
contentType: "text/markdown",
|
|
421
|
+
}),
|
|
422
|
+
);
|
|
423
|
+
submitImplementTurn([
|
|
424
|
+
{
|
|
425
|
+
filename: planFileName,
|
|
426
|
+
storageKey: response.storageKey,
|
|
427
|
+
mountPath: approvedPlanMountPath(planFileName),
|
|
428
|
+
contentType: "text/markdown",
|
|
429
|
+
},
|
|
430
|
+
]);
|
|
431
|
+
} catch {
|
|
432
|
+
// Attachment plumbing must never block the build: implement from
|
|
433
|
+
// conversation history alone, and say so (non-blocking notice).
|
|
434
|
+
setPlanAttachFailed(true);
|
|
435
|
+
submitImplementTurn();
|
|
436
|
+
} finally {
|
|
437
|
+
setIsBuildingFromPlan(false);
|
|
438
|
+
}
|
|
439
|
+
})();
|
|
440
|
+
}, [setInteractionMode, sessionPlan, planDraft.readDraft, stigmer]);
|
|
441
|
+
|
|
442
|
+
// "Open plan" (a thread plan card, or the Artifacts facet's plan.md) → the
|
|
443
|
+
// panel's plan document tab. Opening the LATEST plan clears the historical
|
|
444
|
+
// selection (the tab shows the editable current plan); any other execution
|
|
445
|
+
// id opens that turn's plan read-only in the same tab.
|
|
446
|
+
const handleOpenPlan = useCallback(
|
|
447
|
+
(executionId: string) => {
|
|
448
|
+
setOpenPlanExecutionId(
|
|
449
|
+
sessionPlan && executionId === sessionPlan.executionId
|
|
450
|
+
? null
|
|
451
|
+
: executionId,
|
|
452
|
+
);
|
|
453
|
+
panel.openPlanDocument();
|
|
454
|
+
},
|
|
455
|
+
[sessionPlan, panel.openPlanDocument],
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
// Open a transcript tool-call file path in the panel's read-only editor.
|
|
459
|
+
// Resolves the (possibly absolute / subdir-prefixed) path to a
|
|
460
|
+
// repo/root-relative selection the editor can fetch; on a definite hit it
|
|
461
|
+
// opens the file (expanding the panel) and returns `true` (handled),
|
|
462
|
+
// otherwise returns `false` so the path keeps its default copy / GitHub-link
|
|
463
|
+
// behavior. Writing the editor store re-renders only the panel subtree
|
|
464
|
+
// (which subscribes), never this streaming column.
|
|
465
|
+
const handleTranscriptFilePathClick = useCallback(
|
|
466
|
+
(path: string): boolean => {
|
|
467
|
+
const selection = resolveWorkspaceFileSelection(
|
|
468
|
+
path,
|
|
469
|
+
flow.workspace.entries,
|
|
470
|
+
flow.sandboxWorkspaceRoot,
|
|
471
|
+
);
|
|
472
|
+
if (!selection) return false;
|
|
473
|
+
panel.openFile(selection.entryId, selection.path);
|
|
474
|
+
return true;
|
|
475
|
+
},
|
|
476
|
+
[flow.workspace.entries, flow.sandboxWorkspaceRoot, panel.openFile],
|
|
477
|
+
);
|
|
183
478
|
|
|
184
479
|
if (conv.isLoading) {
|
|
185
480
|
return (
|
|
@@ -199,22 +494,36 @@ export function SessionViewer({
|
|
|
199
494
|
|
|
200
495
|
return (
|
|
201
496
|
<div className={cn("relative flex h-full w-full flex-col", className)}>
|
|
202
|
-
{
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
497
|
+
{/* Top-right controls: host actions + the panel chip. The chip is the
|
|
498
|
+
panel's always-mounted toggle; while collapsed it carries only the
|
|
499
|
+
pending-item count. Execution status is never surfaced as header
|
|
500
|
+
chrome — the thread itself communicates run state. */}
|
|
501
|
+
<div className="absolute top-2 right-6 z-10 flex items-center gap-2">
|
|
502
|
+
{headerActions}
|
|
503
|
+
<SessionPanelChip
|
|
504
|
+
isOpen={panel.isOpen}
|
|
505
|
+
onToggle={panel.isOpen ? panel.closePanel : panel.openPanel}
|
|
506
|
+
badgeCount={writeBackCount + artifactCount}
|
|
507
|
+
/>
|
|
508
|
+
</div>
|
|
207
509
|
|
|
208
510
|
<ThreadSelectionContext.Provider value={selectionStore}>
|
|
511
|
+
{/* One layout, collapsed by default: chat fills the row until the panel
|
|
512
|
+
opens, then becomes the fixed narrow pane on the left while the
|
|
513
|
+
panel takes the flexible region. Collapsing goes through the
|
|
514
|
+
split's `collapsedPane` (CSS, not conditional structure), so the
|
|
515
|
+
conversation is always the same first child and an open/close never
|
|
516
|
+
remounts it (invariant 1). */}
|
|
209
517
|
<ResizableSplit
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
518
|
+
resizablePane="primary"
|
|
519
|
+
collapsedPane={panel.isOpen ? "none" : "secondary"}
|
|
520
|
+
defaultSize={420}
|
|
521
|
+
minSize={320}
|
|
522
|
+
maxSize={640}
|
|
523
|
+
storageKey="stgm-session-chat-width"
|
|
524
|
+
responsiveCollapse={panel.isOpen ? "primary" : "none"}
|
|
525
|
+
ariaLabel="Resize chat panel"
|
|
526
|
+
className="min-h-0 flex-1"
|
|
218
527
|
primary={
|
|
219
528
|
<ConversationColumn
|
|
220
529
|
flow={flow}
|
|
@@ -229,23 +538,37 @@ export function SessionViewer({
|
|
|
229
538
|
enableLocal={enableLocal}
|
|
230
539
|
onBrowseLocalFolder={onBrowseLocalFolder}
|
|
231
540
|
onBuildFromPlan={handleBuildFromPlan}
|
|
541
|
+
onOpenPlan={handleOpenPlan}
|
|
542
|
+
isBuildingFromPlan={isBuildingFromPlan}
|
|
543
|
+
planAttachFailed={planAttachFailed}
|
|
544
|
+
onDismissPlanAttachFailed={() => setPlanAttachFailed(false)}
|
|
545
|
+
onFilePathClick={handleTranscriptFilePathClick}
|
|
232
546
|
isEndUser={isEndUser}
|
|
233
547
|
/>
|
|
234
548
|
}
|
|
235
549
|
secondary={
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
550
|
+
panel.isOpen ? (
|
|
551
|
+
<SessionPanelRegion
|
|
552
|
+
flow={flow}
|
|
553
|
+
org={org}
|
|
554
|
+
panel={panel}
|
|
555
|
+
accessSlot={accessSlot}
|
|
556
|
+
onApplied={onApplied}
|
|
557
|
+
onImplementPlan={handleBuildFromPlan}
|
|
558
|
+
implementPlanDisabled={!conv.canSendFollowUp || isBuildingFromPlan}
|
|
559
|
+
sessionPlan={sessionPlan}
|
|
560
|
+
streamingPlan={streamingPlan}
|
|
561
|
+
planDraft={planDraft}
|
|
562
|
+
openPlanExecutionId={openPlanExecutionId}
|
|
563
|
+
onOpenPlan={handleOpenPlan}
|
|
564
|
+
enableLocal={enableLocal}
|
|
565
|
+
onBrowseLocalFolder={onBrowseLocalFolder}
|
|
566
|
+
workspaceFileLister={workspaceFileLister}
|
|
567
|
+
workspaceFileReader={workspaceFileReader}
|
|
568
|
+
workspaceContentSearcher={workspaceContentSearcher}
|
|
569
|
+
isEndUser={isEndUser}
|
|
570
|
+
/>
|
|
571
|
+
) : null
|
|
249
572
|
}
|
|
250
573
|
/>
|
|
251
574
|
</ThreadSelectionContext.Provider>
|
|
@@ -270,10 +593,23 @@ interface ConversationColumnProps {
|
|
|
270
593
|
readonly enableLocal: boolean;
|
|
271
594
|
readonly onBrowseLocalFolder?: () => Promise<string | null>;
|
|
272
595
|
readonly onBuildFromPlan: () => void;
|
|
596
|
+
/** Opens a plan in the panel's plan document tab (thread card "Open plan"). */
|
|
597
|
+
readonly onOpenPlan: (executionId: string) => void;
|
|
598
|
+
/** True while the approved plan is being uploaded ahead of the build turn. */
|
|
599
|
+
readonly isBuildingFromPlan: boolean;
|
|
600
|
+
/** True when the last build fell back to a message-only implement. */
|
|
601
|
+
readonly planAttachFailed: boolean;
|
|
602
|
+
readonly onDismissPlanAttachFailed: () => void;
|
|
603
|
+
/**
|
|
604
|
+
* Opens a transcript tool-call file path in the Viewer. Returns `true` when it
|
|
605
|
+
* resolved and opened the file (suppressing the link's default), `false` to
|
|
606
|
+
* let the path keep its copy / GitHub-link behavior.
|
|
607
|
+
*/
|
|
608
|
+
readonly onFilePathClick: (path: string) => boolean;
|
|
273
609
|
readonly isEndUser: boolean;
|
|
274
610
|
}
|
|
275
611
|
|
|
276
|
-
function ConversationColumn({
|
|
612
|
+
const ConversationColumn = memo(function ConversationColumn({
|
|
277
613
|
flow,
|
|
278
614
|
modelId,
|
|
279
615
|
setModelId,
|
|
@@ -286,6 +622,11 @@ function ConversationColumn({
|
|
|
286
622
|
enableLocal,
|
|
287
623
|
onBrowseLocalFolder,
|
|
288
624
|
onBuildFromPlan,
|
|
625
|
+
onOpenPlan,
|
|
626
|
+
isBuildingFromPlan,
|
|
627
|
+
planAttachFailed,
|
|
628
|
+
onDismissPlanAttachFailed,
|
|
629
|
+
onFilePathClick,
|
|
289
630
|
isEndUser,
|
|
290
631
|
}: ConversationColumnProps) {
|
|
291
632
|
const { conv } = flow;
|
|
@@ -325,6 +666,20 @@ function ConversationColumn({
|
|
|
325
666
|
[conv.stop, composerRef],
|
|
326
667
|
);
|
|
327
668
|
|
|
669
|
+
// The composer-docked FileReviewDock sits OUTSIDE MessageThread, so the
|
|
670
|
+
// thread's own FilePathContext provider does not reach it. This provider
|
|
671
|
+
// gives the dock's file list the same path resolution and click routing
|
|
672
|
+
// (open in the panel's viewer; GitHub/copy fallback) as the transcript —
|
|
673
|
+
// one click behavior for a path everywhere in the session. Memoized so the
|
|
674
|
+
// dock's memoized cards are not invalidated by unrelated renders (DD-010).
|
|
675
|
+
const dockFilePathCtx = useMemo<FilePathContextValue>(
|
|
676
|
+
() => ({
|
|
677
|
+
workspaceEntries: conv.workspaceEntries ?? [],
|
|
678
|
+
onFilePathClick,
|
|
679
|
+
}),
|
|
680
|
+
[conv.workspaceEntries, onFilePathClick],
|
|
681
|
+
);
|
|
682
|
+
|
|
328
683
|
return (
|
|
329
684
|
<div className="flex h-full min-w-0 flex-col">
|
|
330
685
|
<MessageThread
|
|
@@ -340,14 +695,20 @@ function ConversationColumn({
|
|
|
340
695
|
showFileReviewRecords
|
|
341
696
|
onEditMessage={conv.isStoppable ? handleEditMessage : undefined}
|
|
342
697
|
workspaceEntries={conv.workspaceEntries}
|
|
698
|
+
onFilePathClick={onFilePathClick}
|
|
343
699
|
sandboxWorkspaceRoot={flow.sandboxWorkspaceRoot}
|
|
344
700
|
onBuildFromPlan={onBuildFromPlan}
|
|
701
|
+
onOpenPlan={onOpenPlan}
|
|
345
702
|
org={org}
|
|
346
|
-
planActionsDisabled={!conv.canSendFollowUp}
|
|
347
|
-
|
|
703
|
+
planActionsDisabled={!conv.canSendFollowUp || isBuildingFromPlan}
|
|
704
|
+
planBuildPending={isBuildingFromPlan}
|
|
705
|
+
contentColumn="center"
|
|
348
706
|
className="flex-1"
|
|
349
707
|
/>
|
|
350
|
-
<div className=
|
|
708
|
+
<div className={CONVERSATION_COLUMN_CLASS}>
|
|
709
|
+
{planAttachFailed && (
|
|
710
|
+
<PlanAttachFailedNotice onDismiss={onDismissPlanAttachFailed} />
|
|
711
|
+
)}
|
|
351
712
|
{conv.isReconnecting && <ReconnectingIndicator />}
|
|
352
713
|
{conv.connectTimedOut && (
|
|
353
714
|
<ConnectTimedOutBanner onRetry={conv.reconnectStream} />
|
|
@@ -367,12 +728,19 @@ function ConversationColumn({
|
|
|
367
728
|
decision the agent is blocked on can never scroll out of view. The
|
|
368
729
|
thread renders only observational rows (badges) and read-only
|
|
369
730
|
settled records; this is the one decision surface. */}
|
|
370
|
-
<
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
731
|
+
<FilePathContext.Provider value={dockFilePathCtx}>
|
|
732
|
+
{/* Mid-run live capture (DD-32): the "N files changed so far" strip for
|
|
733
|
+
a still-running turn. Mutually exclusive with the dock below —
|
|
734
|
+
progress shows while CAPTURING, the dock once AWAITING_REVIEW — so it
|
|
735
|
+
hands off cleanly when review opens. Non-interactive. */}
|
|
736
|
+
<FileChangeProgressBar progress={conv.fileChangeProgress} />
|
|
737
|
+
<FileReviewDock
|
|
738
|
+
changeSets={conv.fileChangeSets}
|
|
739
|
+
onSubmit={conv.submitFileDecision}
|
|
740
|
+
submittingDecisionKeys={conv.submittingFileDecisionKeys}
|
|
741
|
+
decisionErrors={conv.fileDecisionErrors}
|
|
742
|
+
/>
|
|
743
|
+
</FilePathContext.Provider>
|
|
376
744
|
<SessionComposer
|
|
377
745
|
ref={composerRef}
|
|
378
746
|
onSubmit={flow.handleSubmit}
|
|
@@ -407,41 +775,165 @@ function ConversationColumn({
|
|
|
407
775
|
</div>
|
|
408
776
|
</div>
|
|
409
777
|
);
|
|
410
|
-
}
|
|
778
|
+
});
|
|
411
779
|
|
|
412
780
|
// ---------------------------------------------------------------------------
|
|
413
|
-
//
|
|
781
|
+
// Session panel region (right/secondary) — the unified panel's subtree
|
|
414
782
|
// ---------------------------------------------------------------------------
|
|
415
783
|
|
|
416
|
-
interface
|
|
784
|
+
interface SessionPanelRegionProps {
|
|
417
785
|
readonly flow: ReturnType<typeof useSessionPageFlow>;
|
|
418
786
|
readonly org: string;
|
|
419
|
-
|
|
787
|
+
/**
|
|
788
|
+
* The panel controller owned by `SessionViewer`. Its editor store is
|
|
789
|
+
* subscribed here (not at `SessionViewer`) so opening/switching files
|
|
790
|
+
* re-renders only this region, never the conversation column.
|
|
791
|
+
*/
|
|
792
|
+
readonly panel: SessionPanelController;
|
|
793
|
+
/** Host access management control, surfaced in the Config facet. */
|
|
794
|
+
readonly accessSlot?: ReactNode;
|
|
420
795
|
readonly onApplied?: (result: ApplyResourceResult) => void;
|
|
421
|
-
/** Implement a plan
|
|
796
|
+
/** Implement a plan (plan tab primary + Artifacts preview action). */
|
|
422
797
|
readonly onImplementPlan?: () => void;
|
|
423
|
-
readonly
|
|
798
|
+
readonly implementPlanDisabled?: boolean;
|
|
799
|
+
/** The session's latest plan — the plan tab's editable content. */
|
|
800
|
+
readonly sessionPlan?: SessionPlan;
|
|
801
|
+
/**
|
|
802
|
+
* The plan the active turn is writing right now. While present it OWNS the
|
|
803
|
+
* plan tab: the tab renders the live document (`PlanStreamingDocument`)
|
|
804
|
+
* instead of a published plan — the in-flight plan is the newest
|
|
805
|
+
* deliverable, and the snap-back in `SessionViewer` has already cleared any
|
|
806
|
+
* historical selection when this identity appeared.
|
|
807
|
+
*/
|
|
808
|
+
readonly streamingPlan?: SessionStreamingPlan;
|
|
809
|
+
/** Viewer-owned plan draft (survives panel collapse and view switches). */
|
|
810
|
+
readonly planDraft: PlanDraftController;
|
|
811
|
+
/**
|
|
812
|
+
* Which plan the plan document tab shows: `null` for the latest (editable),
|
|
813
|
+
* or an execution id whose historical plan renders read-only.
|
|
814
|
+
*/
|
|
815
|
+
readonly openPlanExecutionId: string | null;
|
|
816
|
+
/** Opens a plan in the plan tab (routed to the Artifacts facet's plan.md). */
|
|
817
|
+
readonly onOpenPlan: (executionId: string) => void;
|
|
424
818
|
readonly enableLocal: boolean;
|
|
425
|
-
readonly gitHubConnection?: UseGitHubConnectionReturn;
|
|
426
819
|
readonly onBrowseLocalFolder?: () => Promise<string | null>;
|
|
427
820
|
readonly workspaceFileLister?: WorkspaceFileLister;
|
|
821
|
+
readonly workspaceFileReader?: WorkspaceFileReader;
|
|
822
|
+
readonly workspaceContentSearcher?: WorkspaceContentSearcher;
|
|
428
823
|
readonly isEndUser: boolean;
|
|
429
824
|
}
|
|
430
825
|
|
|
431
|
-
function
|
|
826
|
+
function SessionPanelRegion({
|
|
432
827
|
flow,
|
|
433
828
|
org,
|
|
434
|
-
|
|
829
|
+
panel,
|
|
830
|
+
accessSlot,
|
|
435
831
|
onApplied,
|
|
436
832
|
onImplementPlan,
|
|
437
|
-
|
|
833
|
+
implementPlanDisabled,
|
|
834
|
+
sessionPlan,
|
|
835
|
+
streamingPlan,
|
|
836
|
+
planDraft,
|
|
837
|
+
openPlanExecutionId,
|
|
838
|
+
onOpenPlan,
|
|
438
839
|
enableLocal,
|
|
439
|
-
gitHubConnection,
|
|
440
840
|
onBrowseLocalFolder,
|
|
441
841
|
workspaceFileLister,
|
|
842
|
+
workspaceFileReader,
|
|
843
|
+
workspaceContentSearcher,
|
|
442
844
|
isEndUser,
|
|
443
|
-
}:
|
|
845
|
+
}: SessionPanelRegionProps) {
|
|
444
846
|
const selectedItem = useSelectedThreadItem();
|
|
847
|
+
const { editors, activeKey, activeFile, reveal } = useWorkspaceEditors(
|
|
848
|
+
panel.editorsStore,
|
|
849
|
+
);
|
|
850
|
+
|
|
851
|
+
// Honor a pending jump-to-line reveal only while it targets the active editor
|
|
852
|
+
// (an EditorReveal is structurally a RevealTarget). Switching tabs changes
|
|
853
|
+
// activeKey, so a stale reveal for another tab is naturally ignored.
|
|
854
|
+
const activeReveal =
|
|
855
|
+
reveal && reveal.key === activeKey ? reveal : undefined;
|
|
856
|
+
|
|
857
|
+
// This region is the level that subscribes to thread selection; report it to
|
|
858
|
+
// the controller so an open panel can auto-surface the Inspect view. (A
|
|
859
|
+
// collapsed panel never sees — and deliberately never reacts to — selection.)
|
|
860
|
+
useEffect(() => {
|
|
861
|
+
panel.notifySelection(selectedItem);
|
|
862
|
+
}, [selectedItem, panel.notifySelection]);
|
|
863
|
+
|
|
864
|
+
// Correlate the active file with its session change for diff-as-default
|
|
865
|
+
// (DD-06 parity with the transcript's rendering of the same change). A
|
|
866
|
+
// virtual document (the plan tab) is not a workspace file — pass `null` so
|
|
867
|
+
// the hook's no-file guard skips the net-change fold entirely, rather than
|
|
868
|
+
// relying on the correlation harmlessly missing the sentinel id.
|
|
869
|
+
const openFileChange = useOpenFileChange(
|
|
870
|
+
activeFile && !isVirtualEntryId(activeFile.entryId) ? activeFile : null,
|
|
871
|
+
flow.allExecutions,
|
|
872
|
+
flow.workspace.entries,
|
|
873
|
+
flow.sandboxWorkspaceRoot,
|
|
874
|
+
);
|
|
875
|
+
|
|
876
|
+
// Resolve WHICH plan the plan document tab shows: the latest (editable,
|
|
877
|
+
// buildable, draft-backed) unless a historical execution id was opened —
|
|
878
|
+
// that plan renders read-only. A stale id (execution gone, or its plan
|
|
879
|
+
// artifact missing) degrades to the latest rather than an empty tab.
|
|
880
|
+
const openPlan = useMemo<SessionPlan | undefined>(() => {
|
|
881
|
+
if (
|
|
882
|
+
openPlanExecutionId === null ||
|
|
883
|
+
openPlanExecutionId === sessionPlan?.executionId
|
|
884
|
+
) {
|
|
885
|
+
return sessionPlan;
|
|
886
|
+
}
|
|
887
|
+
const execution = flow.allExecutions.find(
|
|
888
|
+
(e) => e.metadata?.id === openPlanExecutionId,
|
|
889
|
+
);
|
|
890
|
+
const artifact = findPlanArtifact(execution);
|
|
891
|
+
return artifact
|
|
892
|
+
? { executionId: openPlanExecutionId, artifact }
|
|
893
|
+
: sessionPlan;
|
|
894
|
+
}, [openPlanExecutionId, sessionPlan, flow.allExecutions]);
|
|
895
|
+
|
|
896
|
+
// The plan document tab (SurfaceVirtualDocument): the panel's editor-area
|
|
897
|
+
// rendering of the session's plan. Three states, in precedence order:
|
|
898
|
+
// a STREAMING plan owns the tab (live document, no actions); otherwise the
|
|
899
|
+
// resolved `openPlan` renders in the editor (keyed by plan identity so
|
|
900
|
+
// switching plans resets view state cleanly); otherwise an honest empty
|
|
901
|
+
// notice (DD-006) — reachable only when a streaming plan auto-opened the
|
|
902
|
+
// tab and its turn then ended without publishing, with no earlier plan to
|
|
903
|
+
// fall back to.
|
|
904
|
+
const openPlanIsLatest = openPlan?.executionId === sessionPlan?.executionId;
|
|
905
|
+
const virtualDocuments = useMemo<readonly SurfaceVirtualDocument[]>(() => {
|
|
906
|
+
return [
|
|
907
|
+
{
|
|
908
|
+
entryId: PLAN_DOCUMENT_ENTRY_ID,
|
|
909
|
+
path: PLAN_DOCUMENT_PATH,
|
|
910
|
+
content: streamingPlan ? (
|
|
911
|
+
<PlanStreamingDocument
|
|
912
|
+
key={`streaming-${streamingPlan.executionId}`}
|
|
913
|
+
displayText={streamingPlan.displayText}
|
|
914
|
+
/>
|
|
915
|
+
) : openPlan ? (
|
|
916
|
+
<PlanEditor
|
|
917
|
+
key={planDraftKey(openPlan)}
|
|
918
|
+
plan={openPlan}
|
|
919
|
+
draft={openPlanIsLatest ? planDraft : undefined}
|
|
920
|
+
onBuildFromPlan={openPlanIsLatest ? onImplementPlan : undefined}
|
|
921
|
+
buildDisabled={implementPlanDisabled}
|
|
922
|
+
readOnly={!openPlanIsLatest}
|
|
923
|
+
/>
|
|
924
|
+
) : (
|
|
925
|
+
<PlanUnavailableNotice />
|
|
926
|
+
),
|
|
927
|
+
},
|
|
928
|
+
];
|
|
929
|
+
}, [
|
|
930
|
+
streamingPlan,
|
|
931
|
+
openPlan,
|
|
932
|
+
openPlanIsLatest,
|
|
933
|
+
planDraft,
|
|
934
|
+
onImplementPlan,
|
|
935
|
+
implementPlanDisabled,
|
|
936
|
+
]);
|
|
445
937
|
|
|
446
938
|
const handleRemoveAgent = useCallback(() => {
|
|
447
939
|
flow.setAgentRef(null);
|
|
@@ -470,7 +962,7 @@ function InspectorPanel({
|
|
|
470
962
|
[flow.skillRefs, flow.setSkillRefs],
|
|
471
963
|
);
|
|
472
964
|
|
|
473
|
-
const sessionConfig = useMemo(
|
|
965
|
+
const sessionConfig = useMemo<SetupTabProps>(
|
|
474
966
|
() => ({
|
|
475
967
|
agentRef: flow.agentRef,
|
|
476
968
|
isDefaultAgent: flow.isDefaultAgent,
|
|
@@ -480,8 +972,8 @@ function InspectorPanel({
|
|
|
480
972
|
harness: flow.harness,
|
|
481
973
|
executionTarget: flow.executionTarget,
|
|
482
974
|
modelId: flow.model[0],
|
|
483
|
-
// End users see the configuration but cannot strip it — the
|
|
484
|
-
//
|
|
975
|
+
// End users see the configuration but cannot strip it — the Config
|
|
976
|
+
// facet renders read-only without mutation callbacks (DD-011).
|
|
485
977
|
mutations: isEndUser
|
|
486
978
|
? undefined
|
|
487
979
|
: {
|
|
@@ -489,42 +981,59 @@ function InspectorPanel({
|
|
|
489
981
|
onRemoveMcp: handleRemoveMcp,
|
|
490
982
|
onRemoveSkill: handleRemoveSkill,
|
|
491
983
|
},
|
|
984
|
+
accessSlot,
|
|
492
985
|
}),
|
|
493
986
|
[
|
|
494
987
|
flow.agentRef, flow.isDefaultAgent, flow.mcpServerUsages, flow.skillRefs,
|
|
495
988
|
flow.sessionVariables, flow.harness, flow.executionTarget, flow.model,
|
|
496
989
|
isEndUser, handleRemoveAgent, handleRemoveMcp, handleRemoveSkill,
|
|
990
|
+
accessSlot,
|
|
497
991
|
],
|
|
498
992
|
);
|
|
499
993
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
)
|
|
994
|
+
// The session facets (Config / Changes / Artifacts / Usage / Inspect) as
|
|
995
|
+
// injected rail views — the full inspector feature set inside one panel.
|
|
996
|
+
const railViews = useSessionRailViews({
|
|
997
|
+
allExecutions: flow.allExecutions,
|
|
998
|
+
org,
|
|
999
|
+
sessionConfig,
|
|
1000
|
+
selectedItem,
|
|
1001
|
+
onApplied,
|
|
1002
|
+
onImplementPlan,
|
|
1003
|
+
onOpenPlan,
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
// Explorer-footer folder attach (desktop only — needs the native picker).
|
|
1007
|
+
// Same wiring the retired Workspace tab used.
|
|
1008
|
+
const canAddLocalFolder = enableLocal && !!onBrowseLocalFolder;
|
|
1009
|
+
const handleAddLocalFolder = useCallback(async () => {
|
|
1010
|
+
const path = await onBrowseLocalFolder?.();
|
|
1011
|
+
if (path) flow.workspace.addLocalPath(path);
|
|
1012
|
+
}, [onBrowseLocalFolder, flow.workspace.addLocalPath]);
|
|
513
1013
|
|
|
514
1014
|
return (
|
|
515
|
-
<
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
1015
|
+
<WorkspaceSurface
|
|
1016
|
+
entries={flow.workspace.entries}
|
|
1017
|
+
lister={workspaceFileLister}
|
|
1018
|
+
reader={workspaceFileReader}
|
|
1019
|
+
searcher={workspaceContentSearcher}
|
|
1020
|
+
view={panel.view}
|
|
1021
|
+
onViewChange={panel.setView}
|
|
1022
|
+
extraViews={railViews}
|
|
1023
|
+
virtualDocuments={virtualDocuments}
|
|
1024
|
+
onRemoveEntry={flow.workspace.remove}
|
|
1025
|
+
onAddLocalFolder={canAddLocalFolder ? handleAddLocalFolder : undefined}
|
|
1026
|
+
editors={editors}
|
|
1027
|
+
selectedFile={activeFile}
|
|
1028
|
+
onOpenFile={panel.openFile}
|
|
1029
|
+
onActivateEditor={panel.activateEditor}
|
|
1030
|
+
onPinEditor={panel.pinEditor}
|
|
1031
|
+
onCloseEditor={panel.closeEditor}
|
|
1032
|
+
onCollapse={panel.closePanel}
|
|
1033
|
+
change={openFileChange ?? undefined}
|
|
1034
|
+
reveal={activeReveal}
|
|
1035
|
+
className="h-full"
|
|
1036
|
+
/>
|
|
528
1037
|
);
|
|
529
1038
|
}
|
|
530
1039
|
|
|
@@ -593,6 +1102,56 @@ function AutoApproveIndicator({ onTurnOff }: { onTurnOff: () => void }) {
|
|
|
593
1102
|
);
|
|
594
1103
|
}
|
|
595
1104
|
|
|
1105
|
+
/**
|
|
1106
|
+
* The plan document tab's empty state — reachable only when a streaming plan
|
|
1107
|
+
* auto-opened the tab and its turn then ended without publishing (stopped or
|
|
1108
|
+
* failed) while the session has no earlier published plan to fall back to.
|
|
1109
|
+
* The partial plan reverts to the conversation (the thread un-collapses it),
|
|
1110
|
+
* so the notice points there. Never a blank pane (DD-006).
|
|
1111
|
+
*/
|
|
1112
|
+
function PlanUnavailableNotice() {
|
|
1113
|
+
return (
|
|
1114
|
+
<div
|
|
1115
|
+
role="status"
|
|
1116
|
+
className="mx-auto flex w-full max-w-3xl flex-col items-center gap-1 px-4 py-8 text-center"
|
|
1117
|
+
>
|
|
1118
|
+
<p className="text-xs font-medium text-foreground">
|
|
1119
|
+
This turn ended before a plan was completed.
|
|
1120
|
+
</p>
|
|
1121
|
+
<p className="text-xs text-muted-foreground">
|
|
1122
|
+
Any partial plan text remains in the conversation.
|
|
1123
|
+
</p>
|
|
1124
|
+
</div>
|
|
1125
|
+
);
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
/**
|
|
1129
|
+
* Non-blocking notice for a build that fell back to message-only implement
|
|
1130
|
+
* (the approved plan.md could not be uploaded/attached). The build itself
|
|
1131
|
+
* proceeded — the agent follows the plan from conversation history — so this
|
|
1132
|
+
* is a status, not an alert, and it is dismissible.
|
|
1133
|
+
*/
|
|
1134
|
+
function PlanAttachFailedNotice({ onDismiss }: { onDismiss: () => void }) {
|
|
1135
|
+
return (
|
|
1136
|
+
<div
|
|
1137
|
+
role="status"
|
|
1138
|
+
className="flex items-center gap-2 border-t border-border-muted px-4 py-1.5 text-xs text-muted-foreground"
|
|
1139
|
+
>
|
|
1140
|
+
<span className="min-w-0 flex-1 truncate">
|
|
1141
|
+
Couldn’t attach plan.md — the agent will follow the plan from the
|
|
1142
|
+
conversation instead.
|
|
1143
|
+
</span>
|
|
1144
|
+
<button
|
|
1145
|
+
type="button"
|
|
1146
|
+
onClick={onDismiss}
|
|
1147
|
+
className="shrink-0 rounded font-medium text-foreground underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
1148
|
+
>
|
|
1149
|
+
Dismiss
|
|
1150
|
+
</button>
|
|
1151
|
+
</div>
|
|
1152
|
+
);
|
|
1153
|
+
}
|
|
1154
|
+
|
|
596
1155
|
function SendErrorBanner({ error }: { error: Error }) {
|
|
597
1156
|
if (isSecretFlowError(error)) {
|
|
598
1157
|
return <SecretFlowErrorGuide error={error} className="mx-4 my-2" />;
|