@stigmer/react 3.11.0 → 3.11.1-dev.20260812192248
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/agent-instance/AgentInstanceList.d.ts.map +1 -1
- package/agent-instance/AgentInstanceList.js +2 -1
- package/agent-instance/AgentInstanceList.js.map +1 -1
- package/api-key/ApiKeyListPanel.d.ts.map +1 -1
- package/api-key/ApiKeyListPanel.js +2 -1
- package/api-key/ApiKeyListPanel.js.map +1 -1
- package/attachment/AttachmentChipList.d.ts.map +1 -1
- package/attachment/AttachmentChipList.js +9 -8
- package/attachment/AttachmentChipList.js.map +1 -1
- package/attachment/AttachmentImageLightbox.d.ts.map +1 -1
- package/attachment/AttachmentImageLightbox.js +1 -1
- package/attachment/AttachmentImageLightbox.js.map +1 -1
- package/channel/AgentChannelsPanel.d.ts.map +1 -1
- package/channel/AgentChannelsPanel.js +2 -1
- package/channel/AgentChannelsPanel.js.map +1 -1
- package/channel/ChannelConversationsDialog.d.ts.map +1 -1
- package/channel/ChannelConversationsDialog.js +2 -1
- package/channel/ChannelConversationsDialog.js.map +1 -1
- package/channel/ChannelTemplatesDialog.d.ts.map +1 -1
- package/channel/ChannelTemplatesDialog.js +2 -1
- package/channel/ChannelTemplatesDialog.js.map +1 -1
- package/channel-app/internal.d.ts.map +1 -1
- package/channel-app/internal.js +2 -1
- package/channel-app/internal.js.map +1 -1
- package/composer/ComposerToolbar.d.ts.map +1 -1
- package/composer/ComposerToolbar.js +10 -7
- package/composer/ComposerToolbar.js.map +1 -1
- package/composer/ConfigureMenu.d.ts.map +1 -1
- package/composer/ConfigureMenu.js +2 -1
- package/composer/ConfigureMenu.js.map +1 -1
- package/composer/ContextChip.d.ts.map +1 -1
- package/composer/ContextChip.js +2 -1
- package/composer/ContextChip.js.map +1 -1
- package/composer/ContextPopover.d.ts.map +1 -1
- package/composer/ContextPopover.js +2 -1
- package/composer/ContextPopover.js.map +1 -1
- package/composer/SessionComposer.d.ts +13 -0
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +12 -2
- package/composer/SessionComposer.js.map +1 -1
- package/conversation/ConversationMediaAttachment.d.ts.map +1 -1
- package/conversation/ConversationMediaAttachment.js +2 -1
- package/conversation/ConversationMediaAttachment.js.map +1 -1
- package/conversation/ConversationTimelineView.d.ts.map +1 -1
- package/conversation/ConversationTimelineView.js +14 -4
- package/conversation/ConversationTimelineView.js.map +1 -1
- package/conversation/conversationPresentation.d.ts +28 -0
- package/conversation/conversationPresentation.d.ts.map +1 -1
- package/conversation/conversationPresentation.js +38 -1
- package/conversation/conversationPresentation.js.map +1 -1
- package/cursor-accounts/MemberCoverageTable.d.ts.map +1 -1
- package/cursor-accounts/MemberCoverageTable.js +6 -5
- package/cursor-accounts/MemberCoverageTable.js.map +1 -1
- package/dashboard/DashboardKPICards.d.ts.map +1 -1
- package/dashboard/DashboardKPICards.js +3 -1
- package/dashboard/DashboardKPICards.js.map +1 -1
- package/datastore/CollectionRecordsBrowser.d.ts.map +1 -1
- package/datastore/CollectionRecordsBrowser.js +5 -2
- package/datastore/CollectionRecordsBrowser.js.map +1 -1
- package/environment/EnvironmentVariableEditor.d.ts.map +1 -1
- package/environment/EnvironmentVariableEditor.js +2 -1
- package/environment/EnvironmentVariableEditor.js.map +1 -1
- package/execution/ApprovalCard.d.ts.map +1 -1
- package/execution/ApprovalCard.js +1 -1
- package/execution/ApprovalCard.js.map +1 -1
- package/execution/ArtifactRowView.d.ts.map +1 -1
- package/execution/ArtifactRowView.js +2 -1
- package/execution/ArtifactRowView.js.map +1 -1
- package/execution/FilePathLink.d.ts +4 -4
- package/execution/FilePathLink.d.ts.map +1 -1
- package/execution/FilePathLink.js +4 -4
- package/execution/FilePathLink.js.map +1 -1
- package/execution/McpToolDetail.d.ts.map +1 -1
- package/execution/McpToolDetail.js +2 -1
- package/execution/McpToolDetail.js.map +1 -1
- package/execution/MessageAttachments.d.ts.map +1 -1
- package/execution/MessageAttachments.js +10 -8
- package/execution/MessageAttachments.js.map +1 -1
- package/execution/MessageEntry.d.ts.map +1 -1
- package/execution/MessageEntry.js +2 -1
- package/execution/MessageEntry.js.map +1 -1
- package/execution/PlanArtifactCard.d.ts.map +1 -1
- package/execution/PlanArtifactCard.js +7 -4
- package/execution/PlanArtifactCard.js.map +1 -1
- package/execution/ToolCallDetail.d.ts.map +1 -1
- package/execution/ToolCallDetail.js +1 -1
- package/execution/ToolCallDetail.js.map +1 -1
- package/execution/ToolCallItem.d.ts.map +1 -1
- package/execution/ToolCallItem.js +5 -1
- package/execution/ToolCallItem.js.map +1 -1
- package/execution/WriteBackCard.d.ts.map +1 -1
- package/execution/WriteBackCard.js +6 -4
- package/execution/WriteBackCard.js.map +1 -1
- package/execution/artifact-row-item.d.ts +1 -1
- package/execution/artifact-row-item.d.ts.map +1 -1
- package/file-reference/FileReferenceChipList.d.ts.map +1 -1
- package/file-reference/FileReferenceChipList.js +6 -1
- package/file-reference/FileReferenceChipList.js.map +1 -1
- package/iam-policy/OrgMembersPanel.d.ts.map +1 -1
- package/iam-policy/OrgMembersPanel.js +6 -5
- package/iam-policy/OrgMembersPanel.js.map +1 -1
- package/iam-policy/ProviderBadge.d.ts.map +1 -1
- package/iam-policy/ProviderBadge.js +3 -2
- package/iam-policy/ProviderBadge.js.map +1 -1
- package/iam-policy/RoleSelector.d.ts.map +1 -1
- package/iam-policy/RoleSelector.js +1 -1
- package/iam-policy/RoleSelector.js.map +1 -1
- package/identity-provider/IdentityProviderListPanel.d.ts.map +1 -1
- package/identity-provider/IdentityProviderListPanel.js +2 -1
- package/identity-provider/IdentityProviderListPanel.js.map +1 -1
- package/internal/truncated-text.d.ts +17 -0
- package/internal/truncated-text.d.ts.map +1 -0
- package/internal/truncated-text.js +27 -0
- package/internal/truncated-text.js.map +1 -0
- package/invitation/InvitationManager.d.ts.map +1 -1
- package/invitation/InvitationManager.js +4 -3
- package/invitation/InvitationManager.js.map +1 -1
- package/library/ScopeToggle.d.ts.map +1 -1
- package/library/ScopeToggle.js +10 -2
- package/library/ScopeToggle.js.map +1 -1
- package/library/VisibilitySelector.d.ts.map +1 -1
- package/library/VisibilitySelector.js +2 -1
- package/library/VisibilitySelector.js.map +1 -1
- package/mcp-server/McpServerConfigPanel.d.ts +30 -1
- package/mcp-server/McpServerConfigPanel.d.ts.map +1 -1
- package/mcp-server/McpServerConfigPanel.js +18 -8
- package/mcp-server/McpServerConfigPanel.js.map +1 -1
- package/mcp-server/McpServerConnectDialog.d.ts +12 -1
- package/mcp-server/McpServerConnectDialog.d.ts.map +1 -1
- package/mcp-server/McpServerConnectDialog.js +8 -7
- package/mcp-server/McpServerConnectDialog.js.map +1 -1
- package/mcp-server/McpServerDetailView.d.ts.map +1 -1
- package/mcp-server/McpServerDetailView.js +4 -4
- package/mcp-server/McpServerDetailView.js.map +1 -1
- package/mcp-server/McpServerPicker.d.ts.map +1 -1
- package/mcp-server/McpServerPicker.js +86 -5
- package/mcp-server/McpServerPicker.js.map +1 -1
- package/mcp-server/McpToolSelector.d.ts.map +1 -1
- package/mcp-server/McpToolSelector.js +2 -1
- package/mcp-server/McpToolSelector.js.map +1 -1
- package/mcp-server/VendorApprovalBlockedNotice.d.ts +77 -0
- package/mcp-server/VendorApprovalBlockedNotice.d.ts.map +1 -0
- package/mcp-server/VendorApprovalBlockedNotice.js +62 -0
- package/mcp-server/VendorApprovalBlockedNotice.js.map +1 -0
- package/mcp-server/mcpServerSetupReducer.d.ts +18 -0
- package/mcp-server/mcpServerSetupReducer.d.ts.map +1 -1
- package/mcp-server/mcpServerSetupReducer.js +6 -0
- package/mcp-server/mcpServerSetupReducer.js.map +1 -1
- package/mcp-server/useMcpServerSetup.d.ts.map +1 -1
- package/mcp-server/useMcpServerSetup.js +7 -0
- package/mcp-server/useMcpServerSetup.js.map +1 -1
- package/oauth-app/OAuthAppListPanel.d.ts.map +1 -1
- package/oauth-app/OAuthAppListPanel.js +2 -1
- package/oauth-app/OAuthAppListPanel.js.map +1 -1
- package/package.json +4 -4
- package/platform-client/PlatformClientListPanel.d.ts.map +1 -1
- package/platform-client/PlatformClientListPanel.js +3 -2
- package/platform-client/PlatformClientListPanel.js.map +1 -1
- package/pricing-governance/BaselineEditor.d.ts.map +1 -1
- package/pricing-governance/BaselineEditor.js +7 -1
- package/pricing-governance/BaselineEditor.js.map +1 -1
- package/pricing-governance/ModelCatalogPanel.d.ts.map +1 -1
- package/pricing-governance/ModelCatalogPanel.js +2 -1
- package/pricing-governance/ModelCatalogPanel.js.map +1 -1
- package/pricing-governance/PricingGovernanceConsole.d.ts.map +1 -1
- package/pricing-governance/PricingGovernanceConsole.js +2 -1
- package/pricing-governance/PricingGovernanceConsole.js.map +1 -1
- package/pricing-governance/PricingGovernancePanel.d.ts.map +1 -1
- package/pricing-governance/PricingGovernancePanel.js +2 -1
- package/pricing-governance/PricingGovernancePanel.js.map +1 -1
- package/resource-workbench/components/StatusBadge.d.ts.map +1 -1
- package/resource-workbench/components/StatusBadge.js +2 -1
- package/resource-workbench/components/StatusBadge.js.map +1 -1
- package/schedule/CadenceField.d.ts.map +1 -1
- package/schedule/CadenceField.js +15 -8
- package/schedule/CadenceField.js.map +1 -1
- package/schedule/ScheduleRunsTable.d.ts.map +1 -1
- package/schedule/ScheduleRunsTable.js +3 -2
- package/schedule/ScheduleRunsTable.js.map +1 -1
- package/session/PlanEditor.d.ts.map +1 -1
- package/session/PlanEditor.js +3 -5
- package/session/PlanEditor.js.map +1 -1
- package/sharing/AgentShareList.d.ts.map +1 -1
- package/sharing/AgentShareList.js +3 -1
- package/sharing/AgentShareList.js.map +1 -1
- package/sharing/ShareAgentDialog.d.ts.map +1 -1
- package/sharing/ShareAgentDialog.js +2 -1
- package/sharing/ShareAgentDialog.js.map +1 -1
- package/skill/SkillDetailView.d.ts.map +1 -1
- package/skill/SkillDetailView.js +3 -2
- package/skill/SkillDetailView.js.map +1 -1
- package/src/__tests__/helpers/open-menu.ts +88 -0
- package/src/__tests__/portal-container.test.tsx +3 -3
- package/src/agent-instance/AgentInstanceList.tsx +5 -6
- package/src/agent-instance/__tests__/AgentInstanceList.test.tsx +5 -4
- package/src/api-key/ApiKeyListPanel.tsx +11 -3
- package/src/attachment/AttachmentChipList.tsx +74 -65
- package/src/attachment/AttachmentImageLightbox.tsx +5 -4
- package/src/attachment/__tests__/AttachmentChipList.test.tsx +4 -3
- package/src/channel/AgentChannelsPanel.tsx +5 -6
- package/src/channel/ChannelConversationsDialog.tsx +5 -3
- package/src/channel/ChannelTemplatesDialog.tsx +5 -6
- package/src/channel/__tests__/AgentChannelsPanel.test.tsx +14 -9
- package/src/channel-app/internal.tsx +5 -6
- package/src/composer/ComposerToolbar.tsx +122 -90
- package/src/composer/ConfigureMenu.tsx +33 -24
- package/src/composer/ContextChip.tsx +2 -3
- package/src/composer/ContextPopover.tsx +27 -18
- package/src/composer/SessionComposer.tsx +41 -9
- package/src/composer/__tests__/SessionComposer-agentSetupError.test.tsx +124 -0
- package/src/composer/__tests__/SessionComposer-lockAgent.test.tsx +4 -9
- package/src/conversation/ConversationMediaAttachment.tsx +2 -2
- package/src/conversation/ConversationTimelineView.tsx +19 -2
- package/src/conversation/__tests__/ConversationTimelineView.test.tsx +106 -3
- package/src/conversation/conversationPresentation.ts +52 -1
- package/src/cursor-accounts/MemberCoverageTable.tsx +15 -13
- package/src/cursor-accounts/__tests__/CursorAccountsConsole.test.tsx +4 -4
- package/src/dashboard/DashboardKPICards.tsx +2 -1
- package/src/datastore/CollectionRecordsBrowser.tsx +8 -7
- package/src/environment/EnvironmentVariableEditor.tsx +17 -6
- package/src/execution/ApprovalCard.tsx +1 -3
- package/src/execution/ArtifactRowView.tsx +68 -48
- package/src/execution/FilePathLink.tsx +43 -27
- package/src/execution/McpToolDetail.tsx +6 -2
- package/src/execution/MessageAttachments.tsx +57 -41
- package/src/execution/MessageEntry.tsx +22 -15
- package/src/execution/PlanArtifactCard.tsx +25 -18
- package/src/execution/ToolCallDetail.tsx +1 -4
- package/src/execution/ToolCallItem.tsx +18 -7
- package/src/execution/WriteBackCard.tsx +38 -29
- package/src/execution/__tests__/ArtifactRow.test.tsx +6 -2
- package/src/execution/__tests__/ArtifactRowView.test.tsx +5 -2
- package/src/execution/__tests__/FilePathLink.test.tsx +10 -6
- package/src/execution/__tests__/FileReviewCard.test.tsx +2 -1
- package/src/execution/__tests__/MessageEntry-attachments.test.tsx +3 -2
- package/src/execution/__tests__/MessageThread.test.tsx +6 -1
- package/src/execution/__tests__/artifact-row-tooltips.layout.test.tsx +115 -0
- package/src/execution/artifact-row-item.ts +1 -1
- package/src/file-reference/FileReferenceChipList.tsx +29 -17
- package/src/file-reference/__tests__/FileReferenceChipList.test.tsx +5 -2
- package/src/iam-policy/OrgMembersPanel.tsx +26 -19
- package/src/iam-policy/ProviderBadge.tsx +17 -10
- package/src/iam-policy/RoleSelector.tsx +0 -1
- package/src/identity-provider/IdentityProviderListPanel.tsx +11 -3
- package/src/internal/__tests__/truncated-text.layout.test.tsx +68 -0
- package/src/internal/__tests__/truncated-text.test.tsx +33 -0
- package/src/internal/truncated-text.tsx +81 -0
- package/src/invitation/InvitationManager.tsx +18 -5
- package/src/library/ScopeToggle.tsx +14 -2
- package/src/library/VisibilitySelector.tsx +21 -14
- package/src/mcp-server/McpServerConfigPanel.tsx +79 -37
- package/src/mcp-server/McpServerConnectDialog.tsx +41 -5
- package/src/mcp-server/McpServerDetailView.tsx +23 -39
- package/src/mcp-server/McpServerPicker.tsx +99 -7
- package/src/mcp-server/McpToolSelector.tsx +12 -7
- package/src/mcp-server/VendorApprovalBlockedNotice.tsx +221 -0
- package/src/mcp-server/__tests__/McpServerPicker.test.tsx +504 -0
- package/src/mcp-server/__tests__/VendorApprovalBlocked.surfaces.test.tsx +321 -0
- package/src/mcp-server/__tests__/VendorApprovalBlockedNotice.test.tsx +154 -0
- package/src/mcp-server/mcpServerSetupReducer.ts +24 -0
- package/src/mcp-server/useMcpServerSetup.ts +9 -0
- package/src/oauth-app/OAuthAppListPanel.tsx +11 -3
- package/src/platform-client/PlatformClientListPanel.tsx +35 -18
- package/src/pricing-governance/BaselineEditor.tsx +20 -0
- package/src/pricing-governance/ModelCatalogPanel.tsx +9 -5
- package/src/pricing-governance/PricingGovernanceConsole.tsx +9 -5
- package/src/pricing-governance/PricingGovernancePanel.tsx +13 -4
- package/src/pricing-governance/__tests__/ModelCatalogPanel.test.tsx +37 -0
- package/src/resource-workbench/components/StatusBadge.tsx +7 -3
- package/src/schedule/CadenceField.tsx +38 -27
- package/src/schedule/ScheduleRunsTable.tsx +23 -11
- package/src/schedule/__tests__/cadence-tooltips.layout.test.tsx +86 -0
- package/src/session/PlanEditor.tsx +9 -8
- package/src/sharing/AgentShareList.tsx +28 -22
- package/src/sharing/ShareAgentDialog.tsx +5 -6
- package/src/sharing/__tests__/AgentShareList.test.tsx +4 -4
- package/src/skill/SkillDetailView.tsx +23 -12
- package/src/version-history/VersionTimelineEntry.tsx +21 -12
- package/src/workflow/CanvasTransitionEdge.tsx +23 -16
- package/src/workflow/ExecutionActiveTaskIndicator.tsx +74 -59
- package/src/workflow/TaskPickerPopover.tsx +0 -1
- package/src/workflow/WorkflowCanvasEditor.tsx +95 -64
- package/src/workflow/WorkflowNode.tsx +13 -6
- package/src/workflow/WorkflowTaskApprovalSummary.tsx +18 -11
- package/src/workflow/WorkflowTaskList.tsx +2 -2
- package/src/workflow/WorkflowVersionBadge.tsx +33 -18
- package/src/workflow/WorkflowVersionTimeline.tsx +7 -6
- package/src/workflow/__tests__/WorkflowTaskApprovalSummary.test.tsx +3 -1
- package/src/workflow/__tests__/structured-data-viewer.test.tsx +5 -2
- package/src/workflow/execution-comparison/ExecutionComparisonView.tsx +5 -3
- package/src/workflow/execution-comparison/TaskComparisonTable.tsx +12 -5
- package/src/workflow/execution-history/ExecutionHistoryTable.tsx +24 -15
- package/src/workflow/inspector/InspectorHeader.tsx +20 -11
- package/src/workflow/inspector/NestedTaskList.tsx +18 -11
- package/src/workflow/inspector/WorkflowSummaryPanel.tsx +5 -3
- package/src/workflow/inspector/tabs/BranchesTab.tsx +61 -31
- package/src/workflow/instance/WorkflowInstanceList.tsx +5 -6
- package/src/workflow/instance/__tests__/WorkflowInstanceList.test.tsx +5 -4
- package/src/workflow/node-shell/DiffBadge.tsx +18 -11
- package/src/workflow/node-shell/ExecutionBadge.tsx +73 -46
- package/src/workflow/node-shell/NodeActions.tsx +128 -94
- package/src/workflow/task-detail/StructuredDataViewer.tsx +4 -2
- package/src/workspace/EditorTabs.tsx +102 -85
- package/src/workspace/ExplorerTree.tsx +47 -30
- package/src/workspace/FileViewer.tsx +11 -3
- package/src/workspace/PanelChip.tsx +32 -23
- package/src/workspace/WorkspaceEditor.tsx +21 -9
- package/src/workspace/WorkspaceSummary.tsx +21 -12
- package/src/workspace/WorkspaceSurface.tsx +86 -61
- package/src/workspace/__tests__/EditorTabs.test.tsx +5 -2
- package/src/workspace/__tests__/WorkspaceSurface.test.tsx +5 -2
- package/version-history/VersionTimelineEntry.d.ts.map +1 -1
- package/version-history/VersionTimelineEntry.js +2 -1
- package/version-history/VersionTimelineEntry.js.map +1 -1
- package/workflow/CanvasTransitionEdge.d.ts.map +1 -1
- package/workflow/CanvasTransitionEdge.js +3 -2
- package/workflow/CanvasTransitionEdge.js.map +1 -1
- package/workflow/ExecutionActiveTaskIndicator.d.ts.map +1 -1
- package/workflow/ExecutionActiveTaskIndicator.js +12 -11
- package/workflow/ExecutionActiveTaskIndicator.js.map +1 -1
- package/workflow/TaskPickerPopover.js +1 -1
- package/workflow/TaskPickerPopover.js.map +1 -1
- package/workflow/WorkflowCanvasEditor.d.ts.map +1 -1
- package/workflow/WorkflowCanvasEditor.js +9 -2
- package/workflow/WorkflowCanvasEditor.js.map +1 -1
- package/workflow/WorkflowNode.d.ts.map +1 -1
- package/workflow/WorkflowNode.js +2 -1
- package/workflow/WorkflowNode.js.map +1 -1
- package/workflow/WorkflowTaskApprovalSummary.d.ts.map +1 -1
- package/workflow/WorkflowTaskApprovalSummary.js +6 -4
- package/workflow/WorkflowTaskApprovalSummary.js.map +1 -1
- package/workflow/WorkflowTaskList.js +2 -2
- package/workflow/WorkflowTaskList.js.map +1 -1
- package/workflow/WorkflowVersionBadge.d.ts.map +1 -1
- package/workflow/WorkflowVersionBadge.js +2 -1
- package/workflow/WorkflowVersionBadge.js.map +1 -1
- package/workflow/WorkflowVersionTimeline.d.ts.map +1 -1
- package/workflow/WorkflowVersionTimeline.js +2 -1
- package/workflow/WorkflowVersionTimeline.js.map +1 -1
- package/workflow/execution-comparison/ExecutionComparisonView.d.ts.map +1 -1
- package/workflow/execution-comparison/ExecutionComparisonView.js +2 -1
- package/workflow/execution-comparison/ExecutionComparisonView.js.map +1 -1
- package/workflow/execution-comparison/TaskComparisonTable.d.ts.map +1 -1
- package/workflow/execution-comparison/TaskComparisonTable.js +2 -1
- package/workflow/execution-comparison/TaskComparisonTable.js.map +1 -1
- package/workflow/execution-history/ExecutionHistoryTable.d.ts.map +1 -1
- package/workflow/execution-history/ExecutionHistoryTable.js +6 -4
- package/workflow/execution-history/ExecutionHistoryTable.js.map +1 -1
- package/workflow/inspector/InspectorHeader.d.ts.map +1 -1
- package/workflow/inspector/InspectorHeader.js +4 -2
- package/workflow/inspector/InspectorHeader.js.map +1 -1
- package/workflow/inspector/NestedTaskList.d.ts.map +1 -1
- package/workflow/inspector/NestedTaskList.js +2 -1
- package/workflow/inspector/NestedTaskList.js.map +1 -1
- package/workflow/inspector/WorkflowSummaryPanel.d.ts.map +1 -1
- package/workflow/inspector/WorkflowSummaryPanel.js +2 -1
- package/workflow/inspector/WorkflowSummaryPanel.js.map +1 -1
- package/workflow/inspector/tabs/BranchesTab.d.ts.map +1 -1
- package/workflow/inspector/tabs/BranchesTab.js +5 -2
- package/workflow/inspector/tabs/BranchesTab.js.map +1 -1
- package/workflow/instance/WorkflowInstanceList.d.ts.map +1 -1
- package/workflow/instance/WorkflowInstanceList.js +2 -1
- package/workflow/instance/WorkflowInstanceList.js.map +1 -1
- package/workflow/node-shell/DiffBadge.d.ts.map +1 -1
- package/workflow/node-shell/DiffBadge.js +3 -2
- package/workflow/node-shell/DiffBadge.js.map +1 -1
- package/workflow/node-shell/ExecutionBadge.d.ts.map +1 -1
- package/workflow/node-shell/ExecutionBadge.js +5 -4
- package/workflow/node-shell/ExecutionBadge.js.map +1 -1
- package/workflow/node-shell/NodeActions.d.ts.map +1 -1
- package/workflow/node-shell/NodeActions.js +12 -11
- package/workflow/node-shell/NodeActions.js.map +1 -1
- package/workflow/task-detail/StructuredDataViewer.d.ts.map +1 -1
- package/workflow/task-detail/StructuredDataViewer.js +5 -1
- package/workflow/task-detail/StructuredDataViewer.js.map +1 -1
- package/workspace/EditorTabs.d.ts.map +1 -1
- package/workspace/EditorTabs.js +38 -37
- package/workspace/EditorTabs.js.map +1 -1
- package/workspace/ExplorerTree.d.ts.map +1 -1
- package/workspace/ExplorerTree.js +2 -1
- package/workspace/ExplorerTree.js.map +1 -1
- package/workspace/FileViewer.d.ts.map +1 -1
- package/workspace/FileViewer.js +2 -1
- package/workspace/FileViewer.js.map +1 -1
- package/workspace/PanelChip.d.ts.map +1 -1
- package/workspace/PanelChip.js +2 -1
- package/workspace/PanelChip.js.map +1 -1
- package/workspace/WorkspaceEditor.d.ts.map +1 -1
- package/workspace/WorkspaceEditor.js +2 -1
- package/workspace/WorkspaceEditor.js.map +1 -1
- package/workspace/WorkspaceSummary.d.ts.map +1 -1
- package/workspace/WorkspaceSummary.js +7 -2
- package/workspace/WorkspaceSummary.js.map +1 -1
- package/workspace/WorkspaceSurface.d.ts.map +1 -1
- package/workspace/WorkspaceSurface.js +12 -11
- package/workspace/WorkspaceSurface.js.map +1 -1
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeAll, afterEach } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
render,
|
|
4
|
+
screen,
|
|
5
|
+
cleanup,
|
|
6
|
+
fireEvent,
|
|
7
|
+
waitFor,
|
|
8
|
+
} from "@testing-library/react";
|
|
9
|
+
import { useEffect, useRef, type ReactNode } from "react";
|
|
10
|
+
import { create } from "@bufbuild/protobuf";
|
|
11
|
+
import { createRouterTransport, ConnectError, Code } from "@connectrpc/connect";
|
|
12
|
+
import { Stigmer } from "@stigmer/sdk";
|
|
13
|
+
import type { ResourceRef } from "@stigmer/sdk";
|
|
14
|
+
import { McpServerQueryController } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/query_pb";
|
|
15
|
+
import { McpServerCommandController } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/command_pb";
|
|
16
|
+
import {
|
|
17
|
+
OAuthConnectionHealth,
|
|
18
|
+
GetOAuthGrantStatusOutputSchema,
|
|
19
|
+
InitiateOAuthConnectOutputSchema,
|
|
20
|
+
CompleteOAuthConnectOutputSchema,
|
|
21
|
+
} from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/io_pb";
|
|
22
|
+
import {
|
|
23
|
+
McpServerSpecSchema,
|
|
24
|
+
McpServerAuthSchema,
|
|
25
|
+
HttpServerConfigSchema,
|
|
26
|
+
} from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/spec_pb";
|
|
27
|
+
import {
|
|
28
|
+
McpServerStatusSchema,
|
|
29
|
+
DiscoveredCapabilitiesSchema,
|
|
30
|
+
DiscoveredToolSchema,
|
|
31
|
+
} from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/status_pb";
|
|
32
|
+
import { EnvVarDeclarationSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/spec_pb";
|
|
33
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
34
|
+
import type { McpServer } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/api_pb";
|
|
35
|
+
import { samples } from "../../test/samples";
|
|
36
|
+
import { StigmerContext } from "../../context";
|
|
37
|
+
import { McpServerPicker } from "../McpServerPicker";
|
|
38
|
+
import { useMcpServerSetup } from "../useMcpServerSetup";
|
|
39
|
+
import * as oauthPopup from "../../internal/oauthPopup.js";
|
|
40
|
+
|
|
41
|
+
// The popup machinery is window-dependent (window.open, postMessage,
|
|
42
|
+
// BroadcastChannel); these tests drive the RPC chain, so replace it with
|
|
43
|
+
// a deterministic callback handshake. The `openOAuthPopup` call count is
|
|
44
|
+
// the load-bearing assertion of this suite: a discovery-leg retry must
|
|
45
|
+
// NEVER reopen the popup (stigmer/stigmer#418).
|
|
46
|
+
vi.mock("../../internal/oauthPopup.js", () => ({
|
|
47
|
+
openOAuthPopup: vi.fn(() => ({ location: { href: "" }, closed: false })),
|
|
48
|
+
popupBlockedError: vi.fn(
|
|
49
|
+
() => new Error("Popup was blocked by the browser."),
|
|
50
|
+
),
|
|
51
|
+
waitForOAuthCallback: vi.fn(async () => ({
|
|
52
|
+
code: "auth-code",
|
|
53
|
+
state: "state-1",
|
|
54
|
+
})),
|
|
55
|
+
closeOAuthPopup: vi.fn(),
|
|
56
|
+
OAUTH_CALLBACK_MESSAGE_TYPE: "stigmer:oauth:callback",
|
|
57
|
+
OAUTH_BROADCAST_CHANNEL: "stigmer:oauth:broadcast",
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
const openOAuthPopupMock = vi.mocked(oauthPopup.openOAuthPopup);
|
|
61
|
+
|
|
62
|
+
beforeAll(() => {
|
|
63
|
+
// happy-dom lacks ResizeObserver, which the scroll-shadow machinery
|
|
64
|
+
// observes.
|
|
65
|
+
if (!("ResizeObserver" in globalThis)) {
|
|
66
|
+
(globalThis as unknown as { ResizeObserver: unknown }).ResizeObserver =
|
|
67
|
+
class {
|
|
68
|
+
observe() {}
|
|
69
|
+
unobserve() {}
|
|
70
|
+
disconnect() {}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
afterEach(() => {
|
|
76
|
+
cleanup();
|
|
77
|
+
vi.clearAllMocks();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const ORG = "acme";
|
|
81
|
+
const SLUG = "order-management-api";
|
|
82
|
+
const SERVER_NAME = "Order Management API";
|
|
83
|
+
|
|
84
|
+
function serverRef(slug = SLUG): ResourceRef {
|
|
85
|
+
return { org: ORG, slug, kind: ApiResourceKind.mcp_server };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* An OAuth-only server before any discovery: one declared env var that is
|
|
90
|
+
* also the OAuth target, no discovered capabilities.
|
|
91
|
+
*/
|
|
92
|
+
function buildOAuthServer(o?: { name?: string; slug?: string; id?: string }): McpServer {
|
|
93
|
+
const server = samples.mcpServer({
|
|
94
|
+
name: o?.name ?? SERVER_NAME,
|
|
95
|
+
org: ORG,
|
|
96
|
+
slug: o?.slug ?? SLUG,
|
|
97
|
+
id: o?.id ?? "mcp-00000000-0000-0000-0000-00000000041a",
|
|
98
|
+
});
|
|
99
|
+
server.spec = create(McpServerSpecSchema, {
|
|
100
|
+
description: "REST API for order lookup and return processing.",
|
|
101
|
+
serverType: {
|
|
102
|
+
case: "http",
|
|
103
|
+
value: create(HttpServerConfigSchema, { url: "https://api.acme.com/mcp" }),
|
|
104
|
+
},
|
|
105
|
+
env: {
|
|
106
|
+
API_TOKEN: create(EnvVarDeclarationSchema, {
|
|
107
|
+
isSecret: true,
|
|
108
|
+
description: "OAuth access token",
|
|
109
|
+
}),
|
|
110
|
+
},
|
|
111
|
+
auth: create(McpServerAuthSchema, {
|
|
112
|
+
targetEnvVar: "API_TOKEN",
|
|
113
|
+
oauthOnly: true,
|
|
114
|
+
}),
|
|
115
|
+
});
|
|
116
|
+
return server;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** The same server after a successful discovery: three tools. */
|
|
120
|
+
function buildDiscoveredServer(): McpServer {
|
|
121
|
+
const server = buildOAuthServer();
|
|
122
|
+
server.status = create(McpServerStatusSchema, {
|
|
123
|
+
discoveredCapabilities: create(DiscoveredCapabilitiesSchema, {
|
|
124
|
+
tools: [
|
|
125
|
+
create(DiscoveredToolSchema, { name: "get_order" }),
|
|
126
|
+
create(DiscoveredToolSchema, { name: "list_orders" }),
|
|
127
|
+
create(DiscoveredToolSchema, { name: "process_return" }),
|
|
128
|
+
],
|
|
129
|
+
}),
|
|
130
|
+
});
|
|
131
|
+
return server;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function grantStatusOutput(
|
|
135
|
+
connected: boolean,
|
|
136
|
+
health: OAuthConnectionHealth,
|
|
137
|
+
) {
|
|
138
|
+
return create(GetOAuthGrantStatusOutputSchema, {
|
|
139
|
+
connected,
|
|
140
|
+
connectionHealth: health,
|
|
141
|
+
targetEnvVar: connected ? "API_TOKEN" : "",
|
|
142
|
+
authMethod: connected ? "mcp_oauth" : "",
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Composes the picker with a real `useMcpServerSetup`, wired exactly the
|
|
148
|
+
* way `SessionComposer` wires it. The given refs are added once on mount.
|
|
149
|
+
*/
|
|
150
|
+
function PickerHarness({ refs }: { readonly refs: readonly ResourceRef[] }) {
|
|
151
|
+
const setup = useMcpServerSetup(ORG);
|
|
152
|
+
const addedRef = useRef(false);
|
|
153
|
+
const { addServer } = setup;
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
if (addedRef.current) return;
|
|
156
|
+
addedRef.current = true;
|
|
157
|
+
for (const ref of refs) void addServer(ref);
|
|
158
|
+
}, [refs, addServer]);
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<McpServerPicker
|
|
162
|
+
org={ORG}
|
|
163
|
+
setup={{
|
|
164
|
+
entries: setup.entries,
|
|
165
|
+
onServerAdded: (ref) => void setup.addServer(ref),
|
|
166
|
+
onServerRemoved: setup.removeServer,
|
|
167
|
+
onSubmitEnvVars: (ref, values, opts) =>
|
|
168
|
+
void setup.submitEnvVars(ref, values, opts),
|
|
169
|
+
onEnabledToolsChange: setup.setEnabledTools,
|
|
170
|
+
}}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Mounts the harness against a real Connect router transport. RPCs a test
|
|
177
|
+
* does not register fall through to Connect's `unimplemented`, which the
|
|
178
|
+
* SDK hooks degrade from (search list and personal-environment lookups
|
|
179
|
+
* are irrelevant here and stay unregistered).
|
|
180
|
+
*/
|
|
181
|
+
function renderPicker(
|
|
182
|
+
register: Parameters<typeof createRouterTransport>[0],
|
|
183
|
+
refs: readonly ResourceRef[] = [serverRef()],
|
|
184
|
+
) {
|
|
185
|
+
const client = new Stigmer({
|
|
186
|
+
baseUrl: "/",
|
|
187
|
+
getAccessToken: () => "test-token",
|
|
188
|
+
customTransport: createRouterTransport(register),
|
|
189
|
+
});
|
|
190
|
+
return render(
|
|
191
|
+
<StigmerContext.Provider value={client}>
|
|
192
|
+
<PickerHarness refs={refs} />
|
|
193
|
+
</StigmerContext.Provider>,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function drillIntoConfigure(buttonName: RegExp) {
|
|
198
|
+
const configure = await screen.findByRole("button", { name: buttonName });
|
|
199
|
+
fireEvent.click(configure);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
describe("McpServerPicker — discovery-leg failure (in-flow arm)", () => {
|
|
203
|
+
function registerFailingDiscovery() {
|
|
204
|
+
// Mutable fixture state: the grant flips to connected at token
|
|
205
|
+
// exchange (the two-act persistence contract), and discovery starts
|
|
206
|
+
// failing, then succeeds once `discoveryFails` is cleared.
|
|
207
|
+
const state = {
|
|
208
|
+
server: buildOAuthServer(),
|
|
209
|
+
grantConnected: false,
|
|
210
|
+
discoveryFails: true,
|
|
211
|
+
};
|
|
212
|
+
const connectSpy = vi.fn(() => {
|
|
213
|
+
if (state.discoveryFails) {
|
|
214
|
+
throw new ConnectError("discovery workflow failed", Code.Internal);
|
|
215
|
+
}
|
|
216
|
+
state.server = buildDiscoveredServer();
|
|
217
|
+
return state.server;
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const register: Parameters<typeof createRouterTransport>[0] = (
|
|
221
|
+
router,
|
|
222
|
+
) => {
|
|
223
|
+
router.service(McpServerQueryController, {
|
|
224
|
+
getByReference: () => state.server,
|
|
225
|
+
getOAuthGrantStatus: () =>
|
|
226
|
+
grantStatusOutput(
|
|
227
|
+
state.grantConnected,
|
|
228
|
+
state.grantConnected
|
|
229
|
+
? OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_HEALTHY
|
|
230
|
+
: OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_NO_GRANT,
|
|
231
|
+
),
|
|
232
|
+
});
|
|
233
|
+
router.service(McpServerCommandController, {
|
|
234
|
+
initiateOAuthConnect: () =>
|
|
235
|
+
create(InitiateOAuthConnectOutputSchema, {
|
|
236
|
+
authorizationUrl: "https://vendor.example/authorize",
|
|
237
|
+
state: "state-1",
|
|
238
|
+
}),
|
|
239
|
+
completeOAuthConnect: () => {
|
|
240
|
+
state.grantConnected = true;
|
|
241
|
+
return create(CompleteOAuthConnectOutputSchema, { connected: true });
|
|
242
|
+
},
|
|
243
|
+
connect: connectSpy,
|
|
244
|
+
});
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return { state, connectSpy, register };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function signInAndFailDiscovery() {
|
|
251
|
+
await drillIntoConfigure(new RegExp(`Configure ${SLUG}`));
|
|
252
|
+
fireEvent.click(
|
|
253
|
+
await screen.findByRole("button", {
|
|
254
|
+
name: `Sign in with ${SERVER_NAME}`,
|
|
255
|
+
}),
|
|
256
|
+
);
|
|
257
|
+
// Honest copy: the sign-in succeeded, only the discovery leg broke.
|
|
258
|
+
await screen.findByText(
|
|
259
|
+
/Signed in successfully, but tool discovery failed/,
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
it("renders the honest copy, stranded status, and a Discover tools action", async () => {
|
|
264
|
+
const { register } = registerFailingDiscovery();
|
|
265
|
+
renderPicker(register);
|
|
266
|
+
|
|
267
|
+
await signInAndFailDiscovery();
|
|
268
|
+
|
|
269
|
+
expect(
|
|
270
|
+
screen.getByText("Signed in — tools not discovered yet"),
|
|
271
|
+
).toBeTruthy();
|
|
272
|
+
expect(
|
|
273
|
+
screen.getByRole("button", { name: "Discover tools" }),
|
|
274
|
+
).toBeTruthy();
|
|
275
|
+
expect(
|
|
276
|
+
screen.queryByRole("button", { name: `Sign in with ${SERVER_NAME}` }),
|
|
277
|
+
).toBeNull();
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("retries with a bare connect and never reopens the popup", async () => {
|
|
281
|
+
const { state, connectSpy, register } = registerFailingDiscovery();
|
|
282
|
+
renderPicker(register);
|
|
283
|
+
|
|
284
|
+
await signInAndFailDiscovery();
|
|
285
|
+
expect(openOAuthPopupMock).toHaveBeenCalledTimes(1);
|
|
286
|
+
expect(connectSpy).toHaveBeenCalledTimes(1);
|
|
287
|
+
|
|
288
|
+
state.discoveryFails = false;
|
|
289
|
+
fireEvent.click(screen.getByRole("button", { name: "Discover tools" }));
|
|
290
|
+
|
|
291
|
+
// Discovery succeeded → the entry re-resolves ready with real tools;
|
|
292
|
+
// the stranded state and its affordance retire themselves.
|
|
293
|
+
await waitFor(() =>
|
|
294
|
+
expect(
|
|
295
|
+
screen.queryByText("Signed in — tools not discovered yet"),
|
|
296
|
+
).toBeNull(),
|
|
297
|
+
);
|
|
298
|
+
await screen.findByText("process_return");
|
|
299
|
+
expect(
|
|
300
|
+
screen.queryByRole("button", { name: "Discover tools" }),
|
|
301
|
+
).toBeNull();
|
|
302
|
+
expect(connectSpy).toHaveBeenCalledTimes(2);
|
|
303
|
+
expect(openOAuthPopupMock).toHaveBeenCalledTimes(1);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("keeps a failing bare retry honest and popup-free", async () => {
|
|
307
|
+
const { connectSpy, register } = registerFailingDiscovery();
|
|
308
|
+
renderPicker(register);
|
|
309
|
+
|
|
310
|
+
await signInAndFailDiscovery();
|
|
311
|
+
fireEvent.click(screen.getByRole("button", { name: "Discover tools" }));
|
|
312
|
+
|
|
313
|
+
await waitFor(() => expect(connectSpy).toHaveBeenCalledTimes(2));
|
|
314
|
+
// Still stranded, still honest, still no second popup.
|
|
315
|
+
await screen.findByText(
|
|
316
|
+
/Signed in successfully, but tool discovery failed/,
|
|
317
|
+
);
|
|
318
|
+
expect(
|
|
319
|
+
screen.getByRole("button", { name: "Discover tools" }),
|
|
320
|
+
).toBeTruthy();
|
|
321
|
+
expect(openOAuthPopupMock).toHaveBeenCalledTimes(1);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it("routes a non-discovery failure back through the OAuth popup", async () => {
|
|
325
|
+
renderPicker((router) => {
|
|
326
|
+
router.service(McpServerQueryController, {
|
|
327
|
+
getByReference: () => buildOAuthServer(),
|
|
328
|
+
getOAuthGrantStatus: () =>
|
|
329
|
+
grantStatusOutput(
|
|
330
|
+
false,
|
|
331
|
+
OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_NO_GRANT,
|
|
332
|
+
),
|
|
333
|
+
});
|
|
334
|
+
router.service(McpServerCommandController, {
|
|
335
|
+
initiateOAuthConnect: () =>
|
|
336
|
+
create(InitiateOAuthConnectOutputSchema, {
|
|
337
|
+
authorizationUrl: "https://vendor.example/authorize",
|
|
338
|
+
state: "state-1",
|
|
339
|
+
}),
|
|
340
|
+
completeOAuthConnect: () => {
|
|
341
|
+
throw new ConnectError("token exchange failed", Code.Unavailable);
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
await drillIntoConfigure(new RegExp(`Configure ${SLUG}`));
|
|
347
|
+
const signIn = await screen.findByRole("button", {
|
|
348
|
+
name: `Sign in with ${SERVER_NAME}`,
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
fireEvent.click(signIn);
|
|
352
|
+
await screen.findByText(/token exchange failed/);
|
|
353
|
+
// Sign-in itself failed — no "signed in successfully" framing, and the
|
|
354
|
+
// action remains a full OAuth relaunch.
|
|
355
|
+
expect(
|
|
356
|
+
screen.queryByText(/Signed in successfully/),
|
|
357
|
+
).toBeNull();
|
|
358
|
+
fireEvent.click(
|
|
359
|
+
screen.getByRole("button", { name: `Sign in with ${SERVER_NAME}` }),
|
|
360
|
+
);
|
|
361
|
+
await waitFor(() => expect(openOAuthPopupMock).toHaveBeenCalledTimes(2));
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
describe("McpServerPicker — stranded server truth (reopen/re-add arm)", () => {
|
|
366
|
+
it("offers Discover tools for a usable grant with zero discovered tools", async () => {
|
|
367
|
+
let server = buildOAuthServer();
|
|
368
|
+
const connectSpy = vi.fn(() => {
|
|
369
|
+
server = buildDiscoveredServer();
|
|
370
|
+
return server;
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
renderPicker((router) => {
|
|
374
|
+
router.service(McpServerQueryController, {
|
|
375
|
+
getByReference: () => server,
|
|
376
|
+
getOAuthGrantStatus: () =>
|
|
377
|
+
grantStatusOutput(
|
|
378
|
+
true,
|
|
379
|
+
OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_HEALTHY,
|
|
380
|
+
),
|
|
381
|
+
});
|
|
382
|
+
router.service(McpServerCommandController, { connect: connectSpy });
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Grant exists → the entry resolves ready, but with nothing discovered.
|
|
386
|
+
await drillIntoConfigure(new RegExp(`Configure ${SLUG}`));
|
|
387
|
+
|
|
388
|
+
await screen.findByText("Signed in — tools not discovered yet");
|
|
389
|
+
fireEvent.click(
|
|
390
|
+
await screen.findByRole("button", { name: "Discover tools" }),
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
await waitFor(() =>
|
|
394
|
+
expect(
|
|
395
|
+
screen.queryByText("Signed in — tools not discovered yet"),
|
|
396
|
+
).toBeNull(),
|
|
397
|
+
);
|
|
398
|
+
await screen.findByText("process_return");
|
|
399
|
+
expect(connectSpy).toHaveBeenCalledTimes(1);
|
|
400
|
+
expect(openOAuthPopupMock).not.toHaveBeenCalled();
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it("offers re-auth, not discovery, for an expired grant", async () => {
|
|
404
|
+
renderPicker((router) => {
|
|
405
|
+
router.service(McpServerQueryController, {
|
|
406
|
+
getByReference: () => buildOAuthServer(),
|
|
407
|
+
getOAuthGrantStatus: () =>
|
|
408
|
+
grantStatusOutput(
|
|
409
|
+
true,
|
|
410
|
+
OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_TOKEN_EXPIRED,
|
|
411
|
+
),
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
await drillIntoConfigure(new RegExp(`Configure ${SLUG}`));
|
|
416
|
+
|
|
417
|
+
await screen.findByRole("button", { name: "Sign in to reconnect" });
|
|
418
|
+
expect(screen.queryByRole("button", { name: "Discover tools" })).toBeNull();
|
|
419
|
+
|
|
420
|
+
fireEvent.click(
|
|
421
|
+
screen.getByRole("button", { name: "Sign in to reconnect" }),
|
|
422
|
+
);
|
|
423
|
+
await waitFor(() => expect(openOAuthPopupMock).toHaveBeenCalledTimes(1));
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
describe("McpServerPicker — cross-server scoping", () => {
|
|
428
|
+
it("keeps server A's discovery failure out of server B's configure view", async () => {
|
|
429
|
+
const SLUG_B = "billing-api";
|
|
430
|
+
const NAME_B = "Billing API";
|
|
431
|
+
const serverA = buildOAuthServer();
|
|
432
|
+
const serverB = buildOAuthServer({
|
|
433
|
+
name: NAME_B,
|
|
434
|
+
slug: SLUG_B,
|
|
435
|
+
id: "mcp-00000000-0000-0000-0000-00000000041b",
|
|
436
|
+
});
|
|
437
|
+
let grantConnectedA = false;
|
|
438
|
+
const connectSpy = vi.fn(() => {
|
|
439
|
+
throw new ConnectError("discovery workflow failed", Code.Internal);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
renderPicker(
|
|
443
|
+
(router) => {
|
|
444
|
+
router.service(McpServerQueryController, {
|
|
445
|
+
getByReference: (input) =>
|
|
446
|
+
input.slug === SLUG_B ? serverB : serverA,
|
|
447
|
+
getOAuthGrantStatus: (input) =>
|
|
448
|
+
grantStatusOutput(
|
|
449
|
+
input.resourceId === serverA.metadata!.id && grantConnectedA,
|
|
450
|
+
input.resourceId === serverA.metadata!.id && grantConnectedA
|
|
451
|
+
? OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_HEALTHY
|
|
452
|
+
: OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_NO_GRANT,
|
|
453
|
+
),
|
|
454
|
+
});
|
|
455
|
+
router.service(McpServerCommandController, {
|
|
456
|
+
initiateOAuthConnect: () =>
|
|
457
|
+
create(InitiateOAuthConnectOutputSchema, {
|
|
458
|
+
authorizationUrl: "https://vendor.example/authorize",
|
|
459
|
+
state: "state-1",
|
|
460
|
+
}),
|
|
461
|
+
completeOAuthConnect: () => {
|
|
462
|
+
grantConnectedA = true;
|
|
463
|
+
return create(CompleteOAuthConnectOutputSchema, {
|
|
464
|
+
connected: true,
|
|
465
|
+
});
|
|
466
|
+
},
|
|
467
|
+
connect: connectSpy,
|
|
468
|
+
});
|
|
469
|
+
},
|
|
470
|
+
[serverRef(), serverRef(SLUG_B)],
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
// Server A: sign in, discovery fails.
|
|
474
|
+
await drillIntoConfigure(new RegExp(`Configure ${SLUG}$`));
|
|
475
|
+
fireEvent.click(
|
|
476
|
+
await screen.findByRole("button", {
|
|
477
|
+
name: `Sign in with ${SERVER_NAME}`,
|
|
478
|
+
}),
|
|
479
|
+
);
|
|
480
|
+
await screen.findByText(
|
|
481
|
+
/Signed in successfully, but tool discovery failed/,
|
|
482
|
+
);
|
|
483
|
+
expect(openOAuthPopupMock).toHaveBeenCalledTimes(1);
|
|
484
|
+
|
|
485
|
+
// Back to the list, drill into server B.
|
|
486
|
+
fireEvent.click(
|
|
487
|
+
screen.getByRole("button", { name: "Back to MCP server list" }),
|
|
488
|
+
);
|
|
489
|
+
await drillIntoConfigure(new RegExp(`Configure ${SLUG_B}`));
|
|
490
|
+
|
|
491
|
+
// B must not inherit A's failure: no honest-copy banner, no stranded
|
|
492
|
+
// affordance — a fresh sign-in action.
|
|
493
|
+
await screen.findByRole("button", { name: `Sign in with ${NAME_B}` });
|
|
494
|
+
expect(screen.queryByText(/Signed in successfully/)).toBeNull();
|
|
495
|
+
expect(screen.queryByRole("button", { name: "Discover tools" })).toBeNull();
|
|
496
|
+
|
|
497
|
+
// And B's sign-in goes through the popup — proving it did not take
|
|
498
|
+
// A's bare-discovery retry branch.
|
|
499
|
+
fireEvent.click(
|
|
500
|
+
screen.getByRole("button", { name: `Sign in with ${NAME_B}` }),
|
|
501
|
+
);
|
|
502
|
+
await waitFor(() => expect(openOAuthPopupMock).toHaveBeenCalledTimes(2));
|
|
503
|
+
});
|
|
504
|
+
});
|