diff --git a/api_docs/kbn_elastic_assistant.devdocs.json b/api_docs/kbn_elastic_assistant.devdocs.json
index 4d857134e8acf..4433fc6f8f148 100644
--- a/api_docs/kbn_elastic_assistant.devdocs.json
+++ b/api_docs/kbn_elastic_assistant.devdocs.json
@@ -1484,20 +1484,6 @@
"path": "x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant_context/types.tsx",
"deprecated": false,
"trackAdoption": false
- },
- {
- "parentPluginId": "@kbn/elastic-assistant",
- "id": "def-public.AssistantAvailability.isAssistantSharingEnabled",
- "type": "CompoundType",
- "tags": [],
- "label": "isAssistantSharingEnabled",
- "description": [],
- "signature": [
- "boolean | undefined"
- ],
- "path": "x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant_context/types.tsx",
- "deprecated": false,
- "trackAdoption": false
}
],
"initialIsOpen": false
@@ -2796,20 +2782,6 @@
"path": "x-pack/platform/packages/shared/kbn-elastic-assistant/index.ts",
"deprecated": false,
"trackAdoption": false
- },
- {
- "parentPluginId": "@kbn/elastic-assistant",
- "id": "def-public.UseAssistantAvailability.isAssistantSharingEnabled",
- "type": "CompoundType",
- "tags": [],
- "label": "isAssistantSharingEnabled",
- "description": [],
- "signature": [
- "boolean | undefined"
- ],
- "path": "x-pack/platform/packages/shared/kbn-elastic-assistant/index.ts",
- "deprecated": false,
- "trackAdoption": false
}
],
"initialIsOpen": false
@@ -3240,4 +3212,4 @@
"misc": [],
"objects": []
}
-}
\ No newline at end of file
+}
diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx
index f3b084695879f..d4b74d8c9268c 100644
--- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx
+++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx
@@ -109,7 +109,7 @@ describe('AssistantHeader', () => {
});
it('renders share badge when sharing is enabled', () => {
- render(, {
+ render(, {
wrapper: TestProviders,
});
expect(screen.getByTestId('shareBadgeButton')).not.toBeDisabled();
@@ -120,16 +120,9 @@ describe('AssistantHeader', () => {
});
it('disables share badge when isConversationOwner=false', () => {
- render(
- ,
- {
- wrapper: TestProviders,
- }
- );
+ render(, {
+ wrapper: TestProviders,
+ });
expect(screen.getByTestId('shareBadgeButton')).toBeDisabled();
expect(screen.getByTestId('connector-selector')).toBeDisabled();
expect(
diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx
index 58426557cf659..7ef4890c1dc3a 100644
--- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx
+++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx
@@ -44,7 +44,6 @@ interface OwnProps {
isConversationOwner: boolean;
isDisabled: boolean;
isLoading: boolean;
- isAssistantSharingEnabled?: boolean;
isSettingsModalVisible: boolean;
setIsSettingsModalVisible: React.Dispatch>;
onChatCleared: () => void;
@@ -91,7 +90,6 @@ export const AssistantHeader: React.FC = ({
defaultConnector,
isConversationOwner,
isAssistantEnabled,
- isAssistantSharingEnabled = false,
isDisabled,
isLoading,
isSettingsModalVisible,
@@ -223,7 +221,7 @@ export const AssistantHeader: React.FC = ({
)}
- {!isNewConversation && isAssistantSharingEnabled && (
+ {!isNewConversation && (
'http://localhost:5601') } } as unknown as HttpSetup,
- isAssistantSharingEnabled: true,
isDisabled: false,
selectedConversation: welcomeConvo,
setConversationSettings: mockSetConversationSettings,
diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.tsx
index 1be4db012a9f0..a492d440f114b 100644
--- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.tsx
+++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.tsx
@@ -35,7 +35,6 @@ export interface ConversationSettingsEditorProps {
conversationSettings: Record;
conversationsSettingsBulkActions: ConversationsBulkActions;
http: HttpSetup;
- isAssistantSharingEnabled?: boolean;
isDisabled?: boolean;
selectedConversation: Conversation;
setConversationSettings: React.Dispatch>>;
@@ -52,7 +51,6 @@ export const ConversationSettingsEditor: React.FC
)}
-
- {isAssistantSharingEnabled && (
-
-
-
- )}
+
+
+
>
);
}
diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/index.tsx
index 219fa8bbb1130..9cb437bd5f6ee 100644
--- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/index.tsx
+++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/index.tsx
@@ -55,7 +55,7 @@ const ConversationSettingsManagementComponent: React.FC = ({
}) => {
const {
actionTypeRegistry,
- assistantAvailability: { isAssistantEnabled, isAssistantSharingEnabled },
+ assistantAvailability: { isAssistantEnabled },
http,
nameSpace,
toasts,
@@ -250,7 +250,7 @@ const ConversationSettingsManagementComponent: React.FC = ({
onCancelClick();
}, [closeConfirmModal, handleUnselectAll, onCancelClick]);
- const { getConversationsList, getColumns } = useConversationsTable(isAssistantSharingEnabled);
+ const { getConversationsList, getColumns } = useConversationsTable();
const conversationOptions = getConversationsList({
allSystemPrompts,
@@ -373,7 +373,6 @@ const ConversationSettingsManagementComponent: React.FC = ({
conversationSettings={conversations}
conversationsSettingsBulkActions={conversationsSettingsBulkActions}
http={http}
- isAssistantSharingEnabled={isAssistantSharingEnabled}
isDisabled={isDisabled}
selectedConversation={selectedConversation}
setConversationSettings={setConversationSettings}
diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.test.tsx
index cd0eb8d74b1e6..05e51f778ff5a 100644
--- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.test.tsx
+++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.test.tsx
@@ -31,7 +31,7 @@ const mockActionTypeRegistry: ActionTypeRegistryContract = {
describe('useConversationsTable', () => {
it('should return columns', () => {
- const { result } = renderHook(() => useConversationsTable(true));
+ const { result } = renderHook(() => useConversationsTable());
const columns = result.current.getColumns({
conversationOptions: [],
deletedConversationsIds: [],
diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.tsx
index 518e5362c3faa..6030259c60d5e 100644
--- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.tsx
+++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.tsx
@@ -64,7 +64,7 @@ interface GetColumnsParams {
totalItemCount: number;
}
-export const useConversationsTable = (isAssistantSharingEnabled: boolean = false) => {
+export const useConversationsTable = () => {
const getActions = useInlineActions();
const getColumns = useCallback(
@@ -82,126 +82,113 @@ export const useConversationsTable = (isAssistantSharingEnabled: boolean = false
onDeleteActionClicked,
onEditActionClicked,
totalItemCount,
- }: GetColumnsParams): Array> => {
- const columns: Array> = [
- {
- field: '',
- name: (
-
- ),
- render: (conversation: ConversationTableItem) => (
-
- ),
- width: '40px',
- sortable: false,
+ }: GetColumnsParams): Array> => [
+ {
+ field: '',
+ name: (
+
+ ),
+ render: (conversation: ConversationTableItem) => (
+
+ ),
+ width: '40px',
+ sortable: false,
+ },
+ {
+ name: i18n.CONVERSATIONS_TABLE_COLUMN_TITLE,
+ render: (conversation: ConversationTableItem) => (
+ onEditActionClicked(conversation)}>{conversation.title}
+ ),
+ sortable: ({ title }: ConversationTableItem) => title,
+ },
+ {
+ field: 'systemPromptTitle',
+ name: i18n.CONVERSATIONS_TABLE_COLUMN_SYSTEM_PROMPT,
+ align: 'left',
+ render: (systemPromptTitle: ConversationTableItem['systemPromptTitle']) =>
+ systemPromptTitle ? {systemPromptTitle} : null,
+ sortable: false,
+ },
+ {
+ field: 'connectorTypeTitle',
+ name: i18n.CONVERSATIONS_TABLE_COLUMN_CONNECTOR,
+ align: 'left',
+ render: (connectorTypeTitle: ConversationTableItem['connectorTypeTitle']) =>
+ connectorTypeTitle ? {connectorTypeTitle} : null,
+ sortable: false,
+ },
+ {
+ name: i18n.CONVERSATIONS_TABLE_COLUMN_SHARING,
+ render: (conversation: ConversationTableItem) => {
+ const conversationSharedState = getConversationSharedState(conversation);
+ const sharingMap = {
+ [ConversationSharedState.SHARED]: { tooltip: VISIBLE_SHARED, badge: SHARED },
+ [ConversationSharedState.RESTRICTED]: {
+ tooltip: VISIBLE_RESTRICTED,
+ badge: RESTRICTED,
+ },
+ [ConversationSharedState.PRIVATE]: { tooltip: VISIBLE_PRIVATE, badge: PRIVATE },
+ };
+
+ const { tooltip: tooltipContent, badge: badgeLabel } =
+ sharingMap[conversationSharedState] || sharingMap[ConversationSharedState.PRIVATE];
+ return (
+
+
+
+ );
},
- {
- name: i18n.CONVERSATIONS_TABLE_COLUMN_TITLE,
- render: (conversation: ConversationTableItem) => (
- onEditActionClicked(conversation)}>
- {conversation.title}
-
- ),
- sortable: ({ title }: ConversationTableItem) => title,
- },
- {
- field: 'systemPromptTitle',
- name: i18n.CONVERSATIONS_TABLE_COLUMN_SYSTEM_PROMPT,
- align: 'left',
- render: (systemPromptTitle: ConversationTableItem['systemPromptTitle']) =>
- systemPromptTitle ? {systemPromptTitle} : null,
- sortable: false,
- },
- {
- field: 'connectorTypeTitle',
- name: i18n.CONVERSATIONS_TABLE_COLUMN_CONNECTOR,
- align: 'left',
- render: (connectorTypeTitle: ConversationTableItem['connectorTypeTitle']) =>
- connectorTypeTitle ? {connectorTypeTitle} : null,
- sortable: false,
- },
- ];
-
- // Only include sharing column if assistant sharing is enabled
- if (isAssistantSharingEnabled) {
- columns.push({
- name: i18n.CONVERSATIONS_TABLE_COLUMN_SHARING,
- render: (conversation: ConversationTableItem) => {
- const conversationSharedState = getConversationSharedState(conversation);
- const sharingMap = {
- [ConversationSharedState.SHARED]: { tooltip: VISIBLE_SHARED, badge: SHARED },
- [ConversationSharedState.RESTRICTED]: {
- tooltip: VISIBLE_RESTRICTED,
- badge: RESTRICTED,
- },
- [ConversationSharedState.PRIVATE]: { tooltip: VISIBLE_PRIVATE, badge: PRIVATE },
- };
-
- const { tooltip: tooltipContent, badge: badgeLabel } =
- sharingMap[conversationSharedState] || sharingMap[ConversationSharedState.PRIVATE];
- return (
-
-
-
- );
- },
- width: '100px',
- });
- }
-
- columns.push(
- {
- field: 'updatedAt',
- name: i18n.CONVERSATIONS_TABLE_COLUMN_UPDATED_AT,
- align: 'center',
- render: (updatedAt: ConversationTableItem['updatedAt']) =>
- updatedAt ? (
-
-
-
- ) : null,
- sortable: true,
- },
- {
- width: '120px',
- align: 'center',
- ...getActions({
- isDeleteEnabled,
- isEditEnabled,
- onDelete: onDeleteActionClicked,
- onEdit: onEditActionClicked,
- }),
- }
- );
-
- return columns;
- },
- [getActions, isAssistantSharingEnabled]
+ width: '100px',
+ },
+ {
+ field: 'updatedAt',
+ name: i18n.CONVERSATIONS_TABLE_COLUMN_UPDATED_AT,
+ align: 'center',
+ render: (updatedAt: ConversationTableItem['updatedAt']) =>
+ updatedAt ? (
+
+
+
+ ) : null,
+ sortable: true,
+ },
+ {
+ width: '120px',
+ align: 'center',
+ ...getActions({
+ isDeleteEnabled,
+ isEditEnabled,
+ onDelete: onDeleteActionClicked,
+ onEdit: onEditActionClicked,
+ }),
+ },
+ ],
+ [getActions]
);
const getConversationsList = useCallback(
({
diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.test.tsx
index 330b48193f64e..beb8ca2537887 100644
--- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.test.tsx
+++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.test.tsx
@@ -23,7 +23,6 @@ const testProps = {
conversation: ownerConvo,
handleCopyUrl: mockCopyUrl,
handleDuplicateConversation: mockDuplicate,
- isAssistantSharingEnabled: true,
isActiveConversation: false,
lastConversationId: ownerConvo.id,
onConversationSelected: mockSelect,
@@ -175,12 +174,4 @@ describe('ConversationListItem', () => {
expect(screen.getByTestId('convo-context-menu-item-duplicate')).toBeInTheDocument();
expect(screen.queryByTestId('convo-context-menu-item-delete')).not.toBeInTheDocument();
});
-
- it('renders extraAction when sharing is disabled', () => {
- render();
- // The extraAction is a delete button
- expect(screen.getByTestId('delete-option')).toBeInTheDocument();
- fireEvent.click(screen.getByTestId('delete-option'));
- expect(mockDelete).toHaveBeenCalledWith(expect.objectContaining({ id: ownerConvo.id }));
- });
});
diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.tsx
index ab769a69b7813..94f13c59540df 100644
--- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.tsx
+++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.tsx
@@ -7,7 +7,6 @@
import React, { useCallback, useMemo } from 'react';
import { css } from '@emotion/react';
-import type { EuiListGroupItemExtraActionProps } from '@elastic/eui';
import { EuiFlexGroup, EuiListGroupItem } from '@elastic/eui';
import { ConversationSharedState, getConversationSharedState } from '@kbn/elastic-assistant-common';
import { getSharedIcon } from '../../share_conversation/utils';
@@ -22,7 +21,6 @@ interface Props {
conversation: ConversationWithOwner;
handleCopyUrl: (conversation: Conversation) => Promise;
handleDuplicateConversation: (conversation: Conversation) => Promise;
- isAssistantSharingEnabled?: boolean;
isActiveConversation: boolean;
lastConversationId: string;
onConversationSelected: ({ cId }: { cId: string }) => void;
@@ -34,7 +32,6 @@ export const ConversationListItem: React.FC = ({
conversation,
handleCopyUrl,
handleDuplicateConversation,
- isAssistantSharingEnabled = false,
isActiveConversation,
lastConversationId,
onConversationSelected,
@@ -64,20 +61,18 @@ export const ConversationListItem: React.FC = ({
);
const shouldShowIcon = useMemo(
- () => isAssistantSharingEnabled && conversationSharedState !== ConversationSharedState.PRIVATE,
- [isAssistantSharingEnabled, conversationSharedState]
+ () => conversationSharedState !== ConversationSharedState.PRIVATE,
+ [conversationSharedState]
);
- const { iconType, iconColor, iconTitle } = useMemo(
+ const { iconType, iconTitle } = useMemo(
() =>
conversation.isConversationOwner
? {
iconType: shouldShowIcon ? getSharedIcon(conversationSharedState) : undefined,
- iconColor: 'accent',
iconTitle: i18n.SHARED_BY_YOU,
}
: {
iconType: shouldShowIcon ? getSharedIcon(conversationSharedState) : undefined,
- iconColor: 'default',
iconTitle: i18n.SHARED_WITH_YOU,
},
[conversation.isConversationOwner, shouldShowIcon, conversationSharedState]
@@ -111,21 +106,6 @@ export const ConversationListItem: React.FC = ({
[handleCopyUrl, handleDuplicateConversation, setDeleteConversationItem, conversation]
);
- const extraAction = useMemo(
- () =>
- isAssistantSharingEnabled
- ? undefined
- : {
- color: 'danger',
- onClick: () => setDeleteConversationItem(conversation),
- iconType: 'trash',
- iconSize: 's',
- 'aria-label': i18n.DELETE_CONVERSATION,
- 'data-test-subj': 'delete-option',
- },
- [conversation, isAssistantSharingEnabled, setDeleteConversationItem]
- );
-
return (
@@ -151,13 +131,11 @@ export const ConversationListItem: React.FC = ({
margin-inline-start: 12px;
margin-inline-end: 0px;
`,
- color: iconColor,
}}
data-test-subj={`conversation-select-${conversation.title}`}
isActive={isActiveConversation}
- extraAction={extraAction}
/>
- {isAssistantSharingEnabled && }
+
{/* Observer element for infinite scrolling pagination of conversations */}
{conversation.id === lastConversationId && (
diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/index.tsx
index 2a83a7eafbb2f..2c40facb34f9d 100644
--- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/index.tsx
+++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/index.tsx
@@ -36,7 +36,6 @@ interface Props {
currentConversation?: Conversation;
onConversationSelected: ({ cId }: { cId: string }) => void;
shouldDisableKeyboardShortcut?: () => boolean;
- isAssistantSharingEnabled?: boolean;
isDisabled?: boolean;
isFetchingCurrentUserConversations: boolean;
conversations: Record;
@@ -85,7 +84,6 @@ export const ConversationSidePanel = React.memo(
currentConversation,
onConversationSelected,
shouldDisableKeyboardShortcut = () => false,
- isAssistantSharingEnabled,
isDisabled = false,
isFetchingCurrentUserConversations,
conversations,
@@ -202,7 +200,6 @@ export const ConversationSidePanel = React.memo(
: conversation.title === currentConversation?.title
}
key={conversation.id}
- isAssistantSharingEnabled={isAssistantSharingEnabled}
handleDuplicateConversation={handleDuplicateConversation}
handleCopyUrl={handleCopyUrl}
lastConversationId={lastConversationId}
@@ -222,7 +219,6 @@ export const ConversationSidePanel = React.memo(
currentConversation?.title,
handleCopyUrl,
handleDuplicateConversation,
- isAssistantSharingEnabled,
lastConversationId,
onConversationSelected,
setPaginationObserver,
diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.test.tsx
index e6473664db6b2..7dc27edb1e41b 100644
--- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.test.tsx
+++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.test.tsx
@@ -43,6 +43,7 @@ const fullWelcomeConversation = {
...welcomeConvo,
id: 'welcome_id',
apiConfig,
+ isConversationOwner: true,
};
const fullSheepConversation = {
...welcomeConvo,
@@ -53,6 +54,7 @@ const fullSheepConversation = {
replacements: {},
apiConfig,
updatedAt: '2024-10-10T22:07:44.915Z',
+ isConversationOwner: true,
};
const mockData = {
@@ -169,7 +171,11 @@ describe('Assistant', () => {
fireEvent.click(openConversationMenu);
});
// selecting first conversation proves sort by updatedAt is working
- const deleteButton = screen.getAllByTestId('delete-option')[0];
+ const contextMenuButton = screen.getAllByTestId('convo-context-menu-button')[0];
+ await act(async () => {
+ fireEvent.click(contextMenuButton);
+ });
+ const deleteButton = screen.getByTestId('convo-context-menu-item-delete');
await act(async () => {
fireEvent.click(deleteButton);
});
diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx
index 6550a9ee92b73..63f9d70b6d3ff 100644
--- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx
+++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx
@@ -79,7 +79,7 @@ const AssistantComponent: React.FC = ({
}) => {
const { euiTheme } = useEuiTheme();
const {
- assistantAvailability: { isAssistantEnabled, isAssistantSharingEnabled },
+ assistantAvailability: { isAssistantEnabled },
assistantTelemetry,
currentAppId,
augmentMessageCodeBlocks,
@@ -445,7 +445,6 @@ const AssistantComponent: React.FC = ({
conversations={conversations}
onConversationDeleted={handleOnConversationDeleted}
onConversationCreate={handleCreateConversation}
- isAssistantSharingEnabled={isAssistantSharingEnabled}
isFetchingCurrentUserConversations={isFetchingCurrentUserConversations}
refetchCurrentUserConversations={refetchCurrentUserConversations}
setPaginationObserver={setPaginationObserver}
@@ -477,7 +476,6 @@ const AssistantComponent: React.FC = ({
conversationSharedState={conversationSharedState}
defaultConnector={defaultConnector}
isAssistantEnabled={isAssistantEnabled}
- isAssistantSharingEnabled={isAssistantSharingEnabled}
isConversationOwner={isConversationOwner}
isDisabled={isDisabled || isLoadingChatSend}
isLoading={isInitialLoad}
diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant_context/types.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant_context/types.tsx
index 2627d72a9c28c..cd6cd8ce50413 100644
--- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant_context/types.tsx
+++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant_context/types.tsx
@@ -76,9 +76,6 @@ export interface AssistantAvailability {
hasUpdateAIAssistantAnonymization: boolean;
// When true, user has `Edit` privilege for `Global Knowledge Base`
hasManageGlobalKnowledgeBase: boolean;
- // When true, the Assistant Sharing feature is enabled
- // keep type loose to avoid updating mocks as temp property
- isAssistantSharingEnabled?: boolean;
}
export type GetAssistantMessages = (commentArgs: {
diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/index.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant/index.ts
index d1c82837a4704..5b1a508897631 100644
--- a/x-pack/platform/packages/shared/kbn-elastic-assistant/index.ts
+++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/index.ts
@@ -205,7 +205,4 @@ export interface UseAssistantAvailability {
hasUpdateAIAssistantAnonymization: boolean;
// When true, user has `Edit` privilege for `Global Knowledge Base`
hasManageGlobalKnowledgeBase: boolean;
- // When true, the Assistant Sharing feature is enabled
- // keep type loose to avoid updating mocks as temp property
- isAssistantSharingEnabled?: boolean;
}
diff --git a/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/assistant_availability/use_assistant_availability.ts b/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/assistant_availability/use_assistant_availability.ts
index e63de2962640f..4a35504aa9534 100644
--- a/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/assistant_availability/use_assistant_availability.ts
+++ b/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/assistant_availability/use_assistant_availability.ts
@@ -18,13 +18,8 @@ export const useAssistantAvailability = (): UseAssistantAvailability => {
const isEnterprise = useLicense().isEnterprise();
const {
application: { capabilities },
- featureFlags,
} = useKibana().services;
- const isAssistantSharingEnabled = featureFlags.getBooleanValue(
- 'elasticAssistant.assistantSharingEnabled',
- false
- );
const hasAssistantPrivilege = capabilities[ASSISTANT_FEATURE_ID]?.['ai-assistant'] === true;
const hasUpdateAIAssistantAnonymization =
capabilities[ASSISTANT_FEATURE_ID]?.updateAIAssistantAnonymization === true;
@@ -49,7 +44,6 @@ export const useAssistantAvailability = (): UseAssistantAvailability => {
hasAssistantPrivilege,
hasConnectorsAllPrivilege,
hasConnectorsReadPrivilege,
- isAssistantSharingEnabled,
isAssistantEnabled: isEnterprise,
isAssistantVisible: isEnterprise && isVisible,
isAssistantManagementEnabled: isEnterprise && hasManageAssistantPrivilege,
diff --git a/x-pack/solutions/security/test/security_solution_cypress/config.ts b/x-pack/solutions/security/test/security_solution_cypress/config.ts
index a0e278fd6d7d6..318cf150ceb9d 100644
--- a/x-pack/solutions/security/test/security_solution_cypress/config.ts
+++ b/x-pack/solutions/security/test/security_solution_cypress/config.ts
@@ -53,7 +53,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
// in order to force Fleet to reach out to the registry to download the
// packages listed in fleet_packages.json
// See: https://elastic.slack.com/archives/CNMNXV4RG/p1683033379063079
- '--feature_flags.overrides.elasticAssistant.assistantSharingEnabled=true',
`--xpack.fleet.developer.bundledPackageLocation=./inexistentDir`,
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'bulkEditAlertSuppressionEnabled',
diff --git a/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts b/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts
index 53dea3bbcb1a2..7c8eca4de2f82 100644
--- a/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts
+++ b/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts
@@ -5,7 +5,6 @@
* 2.0.
*/
-import { ROLES } from '@kbn/security-solution-plugin/common/test';
import type { MessageRole } from '@kbn/elastic-assistant-common';
import { closeToast } from '../../tasks/common/toast';
import { IS_SERVERLESS } from '../../env_var_names_constants';
@@ -23,7 +22,6 @@ import {
copyUrlFromConversationSideContextMenu,
copyUrlFromMenu,
copyUrlFromShareModal,
- createAndTitleConversation,
dismissSharedCallout,
duplicateConversation,
duplicateFromConversationSideContextMenu,
@@ -34,7 +32,6 @@ import {
selectConversation,
selectPrivate,
selectShareModal,
- shareConversation,
shareConversations,
shareConversationWithUser,
submitShareModal,
@@ -45,280 +42,266 @@ import {
import { deleteConversations, waitForConversation } from '../../tasks/api_calls/assistant';
import { azureConnectorAPIPayload, createAzureConnector } from '../../tasks/api_calls/connectors';
import { deleteConnectors } from '../../tasks/api_calls/common';
-import { login } from '../../tasks/login';
+import { login, loginWithUser, logout } from '../../tasks/login';
import { visit, visitGetStartedPage } from '../../tasks/navigation';
const userRole: MessageRole = 'user';
const assistantRole: MessageRole = 'assistant';
-describe(
- 'Assistant Conversation Sharing',
- // skip on serverless until feature flag is lifted
- { tags: ['@ess', '@serverless', '@skipInServerless', '@skipInServerlessMKI'] },
- () => {
- const isServerless = Cypress.env(IS_SERVERLESS);
- const primaryUser = isServerless ? 'elastic_admin' : 'system_indices_superuser';
- const secondaryUser = isServerless ? ROLES.soc_manager : 'elastic';
- const mockConvo1 = {
- id: 'spooky',
- title: 'Spooky convo',
- messages: [
- {
- timestamp: '2025-08-14T21:08:24.923Z',
- content: 'Hi spooky robot',
- user: {
- name: primaryUser,
- },
- role: userRole,
+describe('Assistant Conversation Sharing', { tags: ['@ess', '@serverless'] }, () => {
+ const isServerless = Cypress.env(IS_SERVERLESS);
+ const primaryUser = isServerless ? 'elastic_admin' : 'system_indices_superuser';
+ const secondaryUser = isServerless ? 'elastic_serverless' : 'elastic';
+ const mockConvo1 = {
+ id: 'spooky',
+ title: 'Spooky convo',
+ messages: [
+ {
+ timestamp: '2025-08-14T21:08:24.923Z',
+ content: 'Hi spooky robot',
+ user: {
+ name: primaryUser,
},
- {
- timestamp: '2025-08-14T21:08:25.349Z',
- content: 'Hello spooky person',
- role: assistantRole,
+ role: userRole,
+ },
+ {
+ timestamp: '2025-08-14T21:08:25.349Z',
+ content: 'Hello spooky person',
+ role: assistantRole,
+ },
+ ],
+ };
+ const mockConvo2 = {
+ id: 'silly',
+ title: 'Silly convo',
+ messages: [
+ {
+ timestamp: '2025-08-14T21:08:24.923Z',
+ content: 'Hi silly robot',
+ user: {
+ name: primaryUser,
},
- ],
- };
- const mockConvo2 = {
- id: 'silly',
- title: 'Silly convo',
- messages: [
- {
- timestamp: '2025-08-14T21:08:24.923Z',
- content: 'Hi silly robot',
- user: {
- name: primaryUser,
- },
- role: userRole,
- },
- {
- timestamp: '2025-08-14T21:08:25.349Z',
- content: 'Hello silly person',
- role: assistantRole,
- },
- ],
- };
- before(() => {
- if (!isServerless) {
- login(secondaryUser);
- cy.clearCookies();
- }
- });
- beforeEach(() => {
- deleteConnectors();
- deleteConversations();
- login(isServerless ? 'admin' : undefined);
- createAzureConnector();
- waitForConversation(mockConvo1);
- waitForConversation(mockConvo2);
- visitGetStartedPage();
- });
- it('Share modal works to not share, share globally, and share selected', () => {
- openAssistant();
- selectConversation(mockConvo1.title);
- selectConnector(azureConnectorAPIPayload.name);
- // Assert that the conversation is not shared
- assertCalloutState('private');
- // Open the share menu and verify not shared state
- openShareMenu();
- assertShareMenuStatus('Private');
- // Selecting 'not shared' should not change sharing settings
- selectPrivate();
- assertCalloutState('private');
- openShareMenu();
- assertShareMenuStatus('Private');
- // Opening and closing the share modal should not change sharing settings
- selectShareModal();
- closeShareModal();
- assertCalloutState('private');
- openShareMenu();
- assertShareMenuStatus('Private');
- // Slecting global share changes sharing settings
- selectGlobal();
- // submitShareModal(); success toast?
- assertCalloutState('shared-by-me');
- closeToast();
- openShareMenu();
- assertShareMenuStatus('Shared');
- toggleConversationSideMenu();
- assertSharedConversationIcon(mockConvo1.title);
- assertNotSharedConversationIcon(mockConvo2.title);
- toggleConversationSideMenu();
- // Share the other conversation with selected users
- selectConversation(mockConvo2.title);
- selectConnector(azureConnectorAPIPayload.name);
- // Assert that the conversation is not shared
- assertCalloutState('private');
- openShareMenu();
- assertShareMenuStatus('Private');
- selectShareModal();
- // Press save without selecting users
- submitShareModal();
- assertCalloutState('private');
- openShareMenu();
- assertShareMenuStatus('Private');
- selectShareModal();
- // Select secondaryUser to share the conversation
- shareConversationWithUser(isServerless ? 'test_user' : secondaryUser);
- submitShareModal();
- assertCalloutState('shared-by-me');
- openShareMenu();
- assertShareMenuStatus('Restricted');
- // Opens to selected share since conversation is shared with selected users
- selectShareModal();
- assertShareUser(isServerless ? 'test_user' : secondaryUser);
- closeShareModal();
+ role: userRole,
+ },
+ {
+ timestamp: '2025-08-14T21:08:25.349Z',
+ content: 'Hello silly person',
+ role: assistantRole,
+ },
+ ],
+ };
+ before(() => {
+ loginSecondaryUser(isServerless, secondaryUser);
+ logout();
+ });
+ beforeEach(() => {
+ deleteConnectors();
+ deleteConversations();
+ login(isServerless ? 'admin' : undefined);
+ createAzureConnector();
+ waitForConversation(mockConvo1);
+ waitForConversation(mockConvo2);
+ visitGetStartedPage();
+ });
+ it('Share modal works to not share, share globally, and share selected', () => {
+ openAssistant();
+ selectConversation(mockConvo1.title);
+ selectConnector(azureConnectorAPIPayload.name);
+ // Assert that the conversation is not shared
+ assertCalloutState('private');
+ // Open the share menu and verify not shared state
+ openShareMenu();
+ assertShareMenuStatus('Private');
+ // Selecting 'not shared' should not change sharing settings.
+ selectPrivate();
+ assertCalloutState('private');
+ openShareMenu();
+ assertShareMenuStatus('Private');
+ // Opening and closing the share modal should not change sharing settings
+ selectShareModal();
+ closeShareModal();
+ assertCalloutState('private');
+ openShareMenu();
+ assertShareMenuStatus('Private');
+ // Slecting global share changes sharing settings
+ selectGlobal();
+ // submitShareModal(); success toast?
+ assertCalloutState('shared-by-me');
+ closeToast();
+ openShareMenu();
+ assertShareMenuStatus('Shared');
+ toggleConversationSideMenu();
+ assertSharedConversationIcon(mockConvo1.title);
+ assertNotSharedConversationIcon(mockConvo2.title);
+ toggleConversationSideMenu();
+ // Share the other conversation with selected users
+ selectConversation(mockConvo2.title);
+ selectConnector(azureConnectorAPIPayload.name);
+ // Assert that the conversation is not shared
+ assertCalloutState('private');
+ openShareMenu();
+ assertShareMenuStatus('Private');
+ selectShareModal();
+ // Press save without selecting users
+ submitShareModal();
+ assertCalloutState('private');
+ openShareMenu();
+ assertShareMenuStatus('Private');
+ selectShareModal();
+ // Select secondaryUser to share the conversation
+ shareConversationWithUser(secondaryUser);
+ submitShareModal();
+ assertCalloutState('shared-by-me');
+ openShareMenu();
+ assertShareMenuStatus('Restricted');
+ // Opens to selected share since conversation is shared with selected users
+ selectShareModal();
+ assertShareUser(secondaryUser);
+ closeShareModal();
- toggleConversationSideMenu();
- assertSharedConversationIcon(mockConvo2.title);
- });
- it('Shared conversations appear for the user they were shared with', () => {
- shareConversations([
- {
- title: mockConvo1.title,
- share: 'global',
- },
- {
- title: mockConvo2.title,
- // In serverless environments, we use 'test_user' instead of the regular secondary user due to configuration limitations.
- // We can only create conversations via API with the primary user, so to minimize manual conversation creation in the UI,
- // we're testing the 'selected user' sharing functionality only from the secondary user perspective.
- share: isServerless ? 'test_user' : secondaryUser,
- },
- ]);
- // First logout admin user
- cy.clearCookies();
+ toggleConversationSideMenu();
+ assertSharedConversationIcon(mockConvo2.title);
+ });
+ it('Shared conversations appear for the user they were shared with', () => {
+ shareConversations([
+ {
+ title: mockConvo1.title,
+ share: 'global',
+ },
+ {
+ title: mockConvo2.title,
+ share: secondaryUser,
+ },
+ ]);
+ // First logout admin user
+ cy.clearCookies();
- // Login as elastic user who should have access to shared conversations
- login(secondaryUser);
- visitGetStartedPage();
- openAssistant();
+ // Login as elastic user who should have access to shared conversations
+ loginSecondaryUser(isServerless, secondaryUser);
+ visitGetStartedPage();
+ openAssistant();
- // Check if the shared conversations are visible
- toggleConversationSideMenu();
- cy.contains(mockConvo1.title).should('exist');
- if (isServerless) {
- cy.contains(mockConvo2.title).should('not.exist');
- } else {
- cy.contains(mockConvo2.title).should('exist');
- assertSharedConversationIcon(mockConvo2.title);
- }
- assertSharedConversationIcon(mockConvo1.title);
- toggleConversationSideMenu();
+ // Check if the shared conversations are visible
+ toggleConversationSideMenu();
+ cy.contains(mockConvo1.title).should('exist');
- // Verify the first conversation is shared with secondaryUser
- selectConversation(mockConvo1.title);
- assertCalloutState('shared-with-me');
- // Ensure we can view messages in the shared conversation
- assertMessageSent(mockConvo1.messages[0].content);
+ cy.contains(mockConvo2.title).should('exist');
+ assertSharedConversationIcon(mockConvo2.title);
- // only run this test if we are in serverless mode
- // as the secondary user share works in ess
- if (isServerless) {
- const newConvoTitle = 'Other conversation';
- createAndTitleConversation(newConvoTitle);
- shareConversation(primaryUser);
- // First logout admin user
- cy.clearCookies();
- login(isServerless ? 'admin' : undefined);
- openAssistant();
- // Check if the selectedly shared conversation is visible
- toggleConversationSideMenu();
- cy.contains(newConvoTitle).should('exist');
- assertSharedConversationIcon(newConvoTitle);
- toggleConversationSideMenu();
- // Verify the first conversation is shared with secondaryUser
- selectConversation(newConvoTitle);
- assertCalloutState('shared-with-me');
- // Ensure we can view messages in the shared conversation
- assertMessageSent('hello');
- }
- });
- it('Dismissed callout remains dismissed when conversation is unselected and selected again', () => {
- shareConversations([
- {
- title: mockConvo1.title,
- share: 'global',
- },
- {
- title: mockConvo2.title,
- share: 'global',
- },
- ]);
- cy.clearCookies();
+ assertSharedConversationIcon(mockConvo1.title);
+ toggleConversationSideMenu();
- login(secondaryUser);
- visitGetStartedPage();
- openAssistant();
+ // Verify the first conversation is shared with secondaryUser
+ selectConversation(mockConvo1.title);
+ assertCalloutState('shared-with-me');
+ // Ensure we can view messages in the shared conversation
+ assertMessageSent(mockConvo1.messages[0].content);
+ });
+ it('Dismissed callout remains dismissed when conversation is unselected and selected again', () => {
+ shareConversations([
+ {
+ title: mockConvo1.title,
+ share: 'global',
+ },
+ {
+ title: mockConvo2.title,
+ share: 'global',
+ },
+ ]);
+ cy.clearCookies();
- selectConversation(mockConvo1.title);
- assertCalloutState('shared-with-me');
- dismissSharedCallout();
+ loginSecondaryUser(isServerless, secondaryUser);
+ visitGetStartedPage();
+ openAssistant();
- selectConversation(mockConvo2.title);
- assertCalloutState('shared-with-me');
+ selectConversation(mockConvo1.title);
+ assertCalloutState('shared-with-me');
+ dismissSharedCallout();
- selectConversation(mockConvo1.title);
- assertNoSharedCallout();
- });
- it('Duplicate conversation allows user to continue a shared conversation', () => {
- shareConversations([
- {
- title: mockConvo1.title,
- share: 'global',
- },
- ]);
+ selectConversation(mockConvo2.title);
+ assertCalloutState('shared-with-me');
- cy.clearCookies();
+ selectConversation(mockConvo1.title);
+ assertNoSharedCallout();
+ });
+ it('Duplicate conversation allows user to continue a shared conversation', () => {
+ shareConversations([
+ {
+ title: mockConvo1.title,
+ share: 'global',
+ },
+ ]);
- login(secondaryUser);
- visitGetStartedPage();
- openAssistant();
+ cy.clearCookies();
- selectConversation(mockConvo1.title);
- duplicateConversation(mockConvo1.title);
- assertCalloutState('private');
- typeAndSendMessage('goodbye');
- assertMessageUser(primaryUser, 0);
- assertMessageUser(`${isServerless ? 'test ' : ''}${secondaryUser}`, 2);
- });
+ loginSecondaryUser(isServerless, secondaryUser);
+ visitGetStartedPage();
+ openAssistant();
- it('Duplicate conversation from conversation menu creates a duplicate', () => {
- openAssistant();
- selectConversation(mockConvo1.title);
- duplicateFromMenu(mockConvo1.title);
- });
+ selectConversation(mockConvo1.title);
+ duplicateConversation(mockConvo1.title);
+ assertCalloutState('private');
+ typeAndSendMessage('goodbye');
+ assertMessageUser(primaryUser, 0);
+ assertMessageUser(secondaryUser, 2);
+ });
- it('Duplicate conversation from conversation side menu creates a duplicate', () => {
- openAssistant();
- toggleConversationSideMenu();
- duplicateFromConversationSideContextMenu(mockConvo2.title);
- });
+ it('Duplicate conversation from conversation menu creates a duplicate', () => {
+ openAssistant();
+ selectConversation(mockConvo1.title);
+ duplicateFromMenu(mockConvo1.title);
+ });
- it('Copy URL copies the proper url from conversation menu', () => {
- openAssistant();
- selectConversation(mockConvo1.title);
- selectConnector(azureConnectorAPIPayload.name);
- copyUrlFromMenu();
- // Cypress paste (doc.execCommand('paste')) is flaky, so skipping that assertion
- });
- it('Copy URL copies the proper url from conversation side menu', () => {
- openAssistant();
- toggleConversationSideMenu();
- copyUrlFromConversationSideContextMenu();
- // Cypress paste (doc.execCommand('paste')) is flaky, so skipping that assertion
- });
+ it('Duplicate conversation from conversation side menu creates a duplicate and secondary user cannot access', () => {
+ openAssistant();
+ toggleConversationSideMenu();
+ duplicateFromConversationSideContextMenu(mockConvo2.title);
- it('Copy URL copies the proper url from share modal', () => {
- openAssistant();
- selectConversation(mockConvo1.title);
- selectConnector(azureConnectorAPIPayload.name);
- copyUrlFromShareModal();
- // Cypress paste (doc.execCommand('paste')) is flaky, so skipping that assertion
- });
+ cy.clearCookies();
- it('Visiting a URL with the assistant param opens the assistant to the proper conversation', () => {
- cy.location('origin').then((origin) => {
- visit(`${origin}/app/security/get_started?assistant=${mockConvo1.id}`);
- });
- assertConversationTitle(mockConvo1.title);
+ // Login as elastic user who should have access to shared conversations
+ loginSecondaryUser(isServerless, secondaryUser);
+ visitGetStartedPage();
+ openAssistant();
+
+ // Check if the shared conversations are visible
+ toggleConversationSideMenu();
+ cy.contains(mockConvo2.title).should('not.exist');
+ cy.contains(`[Duplicate] ${mockConvo2.title}`).should('not.exist');
+ });
+
+ it('Copy URL copies the proper url from conversation menu', () => {
+ openAssistant();
+ selectConversation(mockConvo1.title);
+ selectConnector(azureConnectorAPIPayload.name);
+ copyUrlFromMenu();
+ // Cypress paste (doc.execCommand('paste')) is flaky, so skipping that assertion
+ });
+ it('Copy URL copies the proper url from conversation side menu', () => {
+ openAssistant();
+ toggleConversationSideMenu();
+ copyUrlFromConversationSideContextMenu();
+ // Cypress paste (doc.execCommand('paste')) is flaky, so skipping that assertion
+ });
+
+ it('Copy URL copies the proper url from share modal', () => {
+ openAssistant();
+ selectConversation(mockConvo1.title);
+ selectConnector(azureConnectorAPIPayload.name);
+ copyUrlFromShareModal();
+ // Cypress paste (doc.execCommand('paste')) is flaky, so skipping that assertion
+ });
+
+ it('Visiting a URL with the assistant param opens the assistant to the proper conversation', () => {
+ cy.location('origin').then((origin) => {
+ visit(`${origin}/app/security/get_started?assistant=${mockConvo1.id}`);
});
+ assertConversationTitle(mockConvo1.title);
+ });
+});
+
+const loginSecondaryUser = (isServerless: boolean, username: string) => {
+ if (isServerless) {
+ loginWithUser({ username, password: 'changeme' });
+ } else {
+ login(username);
}
-);
+};
diff --git a/x-pack/solutions/security/test/security_solution_cypress/cypress/tasks/login.ts b/x-pack/solutions/security/test/security_solution_cypress/cypress/tasks/login.ts
index 28a2d07c1e10f..5c7793d162f70 100644
--- a/x-pack/solutions/security/test/security_solution_cypress/cypress/tasks/login.ts
+++ b/x-pack/solutions/security/test/security_solution_cypress/cypress/tasks/login.ts
@@ -103,4 +103,6 @@ export const loginWithUser = (user: User): void => {
export const logout = (): void => {
cy.visit(LOGOUT_URL);
+ // Wait for logout to complete before proceeding
+ cy.url().should('not.include', '/logout');
};