diff --git a/x-pack/platform/packages/shared/kbn-ai-assistant/src/chat/chat_actions_menu.tsx b/x-pack/platform/packages/shared/kbn-ai-assistant/src/chat/chat_actions_menu.tsx index 4a19272e8938b..45646c701ced7 100644 --- a/x-pack/platform/packages/shared/kbn-ai-assistant/src/chat/chat_actions_menu.tsx +++ b/x-pack/platform/packages/shared/kbn-ai-assistant/src/chat/chat_actions_menu.tsx @@ -25,11 +25,13 @@ export function ChatActionsMenu({ conversationId, disabled, onCopyConversationClick, + onDuplicateConversationClick, }: { connectors: UseGenAIConnectorsResult; conversationId?: string; disabled: boolean; onCopyConversationClick: () => void; + onDuplicateConversationClick: () => void; }) { const { application, http } = useKibana().services; const knowledgeBase = useKnowledgeBase(); @@ -141,6 +143,16 @@ export function ChatActionsMenu({ onCopyConversationClick(); }, }, + { + name: i18n.translate('xpack.aiAssistant.chatHeader.actions.duplicateConversation', { + defaultMessage: 'Duplicate', + }), + disabled: !conversationId, + onClick: () => { + toggleActionsMenu(); + onDuplicateConversationClick(); + }, + }, ], }, { diff --git a/x-pack/platform/packages/shared/kbn-ai-assistant/src/chat/chat_body.tsx b/x-pack/platform/packages/shared/kbn-ai-assistant/src/chat/chat_body.tsx index 693ff8d63687d..97098f335bb30 100644 --- a/x-pack/platform/packages/shared/kbn-ai-assistant/src/chat/chat_body.tsx +++ b/x-pack/platform/packages/shared/kbn-ai-assistant/src/chat/chat_body.tsx @@ -6,13 +6,16 @@ */ import { + EuiButton, EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, + EuiIcon, EuiPanel, euiScrollBarStyles, EuiSpacer, + EuiText, useEuiTheme, } from '@elastic/eui'; import { css, keyframes } from '@emotion/css'; @@ -45,8 +48,8 @@ import { SimulatedFunctionCallingCallout } from './simulated_function_calling_ca import { WelcomeMessage } from './welcome_message'; import { useLicense } from '../hooks/use_license'; import { PromptEditor } from '../prompt_editor/prompt_editor'; -import { deserializeMessage } from '../utils/deserialize_message'; import { useKibana } from '../hooks/use_kibana'; +import { deserializeMessage } from '../utils/deserialize_message'; const fullHeightClassName = css` height: 100%; @@ -113,9 +116,10 @@ export function ChatBody({ onConversationUpdate, onToggleFlyoutPositionMode, navigateToConversation, + onConversationDuplicate, }: { connectors: ReturnType; - currentUser?: Pick; + currentUser?: Pick; flyoutPositionMode?: FlyoutPositionMode; initialTitle?: string; initialMessages?: Message[]; @@ -123,6 +127,7 @@ export function ChatBody({ knowledgeBase: UseKnowledgeBaseResult; showLinkToConversationsApp: boolean; onConversationUpdate: (conversation: { conversation: Conversation['conversation'] }) => void; + onConversationDuplicate: (conversation: Conversation) => void; onToggleFlyoutPositionMode?: (flyoutPositionMode: FlyoutPositionMode) => void; navigateToConversation?: (conversationId?: string) => void; }) { @@ -142,13 +147,26 @@ export function ChatBody({ false ); - const { conversation, messages, next, state, stop, saveTitle } = useConversation({ + const { + conversation, + conversationId, + messages, + next, + state, + stop, + saveTitle, + duplicateConversation, + isConversationOwnedByCurrentUser, + user: conversationUser, + } = useConversation({ + currentUser, initialConversationId, initialMessages, initialTitle, chatService, connectorId: connectors.selectedConnector, onConversationUpdate, + onConversationDuplicate, }); const timelineContainerRef = useRef(null); @@ -385,28 +403,65 @@ export function ChatBody({ } /> ) : ( - { - setStickToBottom(true); - const indexOf = messages.indexOf(editedMessage); - next(messages.slice(0, indexOf).concat(newMessage)); - }} - onFeedback={handleFeedback} - onRegenerate={(message) => { - next(reverseToLastUserMessage(messages, message)); - }} - onSendTelemetry={(eventWithPayload) => - chatService.sendAnalyticsEvent(eventWithPayload) - } - onStopGenerating={stop} - onActionClick={handleActionClick} - /> + <> + { + setStickToBottom(true); + const indexOf = messages.indexOf(editedMessage); + next(messages.slice(0, indexOf).concat(newMessage)); + }} + onFeedback={handleFeedback} + onRegenerate={(message) => { + next(reverseToLastUserMessage(messages, message)); + }} + onSendTelemetry={(eventWithPayload) => + chatService.sendAnalyticsEvent(eventWithPayload) + } + onStopGenerating={stop} + onActionClick={handleActionClick} + /> + {conversationId && !isConversationOwnedByCurrentUser ? ( + <> + + + + + + + +

+ {i18n.translate('xpack.aiAssistant.sharedBanner.title', { + defaultMessage: 'This conversation is shared with your team.', + })} +

+

+ {i18n.translate('xpack.aiAssistant.sharedBanner.description', { + defaultMessage: `You can’t edit or continue this conversation, but you can duplicate + it into a new private conversation. The original conversation will + remain unchanged.`, + })} +

+ + {i18n.translate('xpack.aiAssistant.duplicateButton', { + defaultMessage: 'Duplicate', + })} + +
+
+
+
+ + + ) : null} + )} @@ -432,7 +487,11 @@ export function ChatBody({ className={promptEditorContainerClassName} >