Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D
mcpServer: `${ELASTIC_DOCS}solutions/search/agent-builder/mcp-server`,
a2aServer: `${ELASTIC_DOCS}solutions/search/agent-builder/a2a-server`,
limitationsKnownIssues: `${ELASTIC_DOCS}solutions/search/agent-builder/limitations-known-issues`,
learnMore: `${ELASTIC_DOCS}explore-analyze/ai-features/ai-agent-or-ai-assistant`,
},
inferenceManagement: {
inferenceAPIDocumentation: isServerless
Expand Down
1 change: 1 addition & 0 deletions src/platform/packages/shared/kbn-doc-links/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,7 @@ export interface DocLinks {
readonly mcpServer: string;
readonly a2aServer: string;
readonly limitationsKnownIssues: string;
readonly learnMore: string;
};
readonly indexManagement: {
readonly componentTemplate: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const AIAssistantHeaderButton: React.FC<AIAssistantHeaderButtonProps> = (

const { getUrlForApp } = coreStart.application;
const { toasts } = coreStart.notifications;
const { links: docLinks } = coreStart.docLinks;

const hasAgentBuilder = coreStart.application.capabilities.agentBuilder?.manageAgents === true;
const isAiAgentsEnabled = getIsAiAgentsEnabled(coreStart.featureFlags);
Expand Down Expand Up @@ -160,9 +161,8 @@ export const AIAssistantHeaderButton: React.FC<AIAssistantHeaderButtonProps> = (
</EuiLink>
),
learnMoreLink: (
// TODO: Update link when documentation is ready
<EuiLink
href="#"
href={docLinks.agentBuilder.learnMore}
target="_blank"
data-test-subj="aiAgentBuilderLearnMoreLink"
>
Expand Down Expand Up @@ -299,6 +299,7 @@ export const AIAssistantHeaderButton: React.FC<AIAssistantHeaderButtonProps> = (
<AIAgentConfirmationModal
onConfirm={handleConfirmAgent}
onCancel={() => setConfirmModalOpen(false)}
docLinks={docLinks}
/>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,9 @@ export class AIAssistantManagementSelectionPlugin
getValue: async ({ request }: { request?: KibanaRequest } = {}) => {
if (request) {
try {
const [coreStart, startServices] = await core.getStartServices();
// Avoid security exceptions before login
const user = coreStart.security.authc.getCurrentUser(request);
if (startServices.spaces && user) {
const [, startServices] = await core.getStartServices();
// Avoid security exceptions before login - only check space when authenticated
if (startServices.spaces && request.auth.isAuthenticated) {
const activeSpace = await startServices.spaces.spacesService.getActiveSpace(
request
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,17 @@
*/

import { i18n } from '@kbn/i18n';

import { schema } from '@kbn/config-schema';
import type { UiSettingsParams } from '@kbn/core-ui-settings-common';
import { AIChatExperience } from '@kbn/ai-assistant-common';
import { AI_AGENT, CHAT_EXPERIENCE_TITLE, CLASSIC_AI_ASSISTANT } from './translations';

// Define the chatExperienceSetting with proper typing
export const chatExperienceSetting: Omit<UiSettingsParams<AIChatExperience>, 'value'> = {
name: CHAT_EXPERIENCE_TITLE,
description: i18n.translate(
'aiAssistantManagementSelection.preferredChatExperienceSettingDescription',
{
defaultMessage: 'Choose which chat experience to use for everyone in this space. {link}',
values: {
// TODO: add the actual link when available
link: '<a href="" target="_blank" rel="noopener noreferrer" class="euiLink euiLink--primary">Learn more</a>',
},
defaultMessage: 'Choose which chat experience to use for all users in this space.',
}
),
schema: schema.oneOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ export const CHAT_EXPERIENCE_TITLE = i18n.translate(

export const CLASSIC_AI_ASSISTANT = i18n.translate(
'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueClassic',
{ defaultMessage: 'Classic AI Assistant (default)' }
{ defaultMessage: 'Classic AI Assistant' }
);
export const AI_AGENT = i18n.translate(
'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueAgent',
{ defaultMessage: 'AI Agent' }
{ defaultMessage: 'AI Agent (Beta)' }
);
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,26 @@ import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { AIAgentConfirmationModal } from './ai_agent_confirmation_modal';
import { I18nProvider } from '@kbn/i18n-react';
import type { DocLinks } from '@kbn/doc-links';

describe('AIAgentConfirmationModal', () => {
const mockOnConfirm = jest.fn();
const mockOnCancel = jest.fn();

const mockDocLinks: DocLinks = {
agentBuilder: {
learnMore: 'https://www.elastic.co/docs/explore-analyze/ai-features/ai-agent-or-ai-assistant',
},
} as DocLinks;

const renderComponent = () => {
return render(
<I18nProvider>
<AIAgentConfirmationModal onConfirm={mockOnConfirm} onCancel={mockOnCancel} />
<AIAgentConfirmationModal
onConfirm={mockOnConfirm}
onCancel={mockOnCancel}
docLinks={mockDocLinks}
/>
</I18nProvider>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import type { DocLinks } from '@kbn/doc-links';

export interface AIAgentConfirmationModalProps {
onConfirm: () => void;
onCancel: () => void;
docLinks: DocLinks;
}

export const AIAgentConfirmationModal: React.FC<AIAgentConfirmationModalProps> = ({
onConfirm,
onCancel,
docLinks,
}) => {
const confirmModalTitleId = useGeneratedHtmlId({ prefix: 'aiAgentConfirmModalTitle' });

return (
<EuiConfirmModal
title={
Expand Down Expand Up @@ -75,9 +77,8 @@ export const AIAgentConfirmationModal: React.FC<AIAgentConfirmationModalProps> =
br: <br />,
bold: (str) => <strong>{str}</strong>,
learnMoreLink: (
// TODO: Update link when documentation is ready
<EuiLink
href="#"
href={docLinks.agentBuilder.learnMore}
target="_blank"
data-test-subj="AIAgentConfirmationModalLearnMoreLink"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ project:
dependsOn:
- '@kbn/i18n'
- '@kbn/i18n-react'
- '@kbn/doc-links'
tags:
- shared-browser
- package
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@
"kbn_references": [
"@kbn/i18n",
"@kbn/i18n-react",
"@kbn/doc-links",
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function ChatActionsMenu({
isConversationApp: boolean;
navigateToConnectorsManagementApp: (application: ApplicationStart) => void;
}) {
const { application, http, triggersActionsUi } = useKibana().services;
const { application, http, triggersActionsUi, docLinks } = useKibana().services;
const knowledgeBase = useKnowledgeBase();
const [isOpen, setIsOpen] = useState(false);
const [connectorFlyoutOpen, setConnectorFlyoutOpen] = useState(false);
Expand Down Expand Up @@ -244,10 +244,11 @@ export function ChatActionsMenu({
/>
)}

{isAgentBuilderConfirmationModalOpen && (
{isAgentBuilderConfirmationModalOpen && docLinks?.links && (
<AIAgentConfirmationModal
onConfirm={confirmAgentBuilderOptIn}
onCancel={closeAgentBuilderConfirmationModal}
docLinks={docLinks.links}
/>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const AssistantSettingsContextMenu: React.FC<Params> = React.memo(
showAssistantOverlay,
settings,
toasts,
docLinks,
} = useAssistantContext();

const [isPopoverOpen, setPopover] = useState(false);
Expand Down Expand Up @@ -281,6 +282,7 @@ export const AssistantSettingsContextMenu: React.FC<Params> = React.memo(
<AIAgentConfirmationModal
onConfirm={handleConfirmAIAgent}
onCancel={handleCancelAIAgent}
docLinks={docLinks.links}
/>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import React from 'react';
import { EuiSpacer } from '@elastic/eui';
import { FieldRow, FieldRowProvider } from '@kbn/management-settings-components-field-row';
import {
AI_ASSISTANT_PREFERRED_AI_ASSISTANT_TYPE,
Expand Down Expand Up @@ -49,17 +50,21 @@ export const AIAssistantVisibility: React.FC = () => {
const canEditAdvancedSettings = application.capabilities.advancedSettings?.save;

return (
<FieldRowProvider
links={docLinks.links.management}
showDanger={(message: string) => notifications.toasts.addDanger(message)}
validateChange={(key: string, value: any) => settings.client.validateValue(key, value)}
>
<FieldRow
field={field}
isSavingEnabled={!!canEditAdvancedSettings}
onFieldChange={handleFieldChange}
unsavedChange={unsavedChanges[AI_ASSISTANT_PREFERRED_AI_ASSISTANT_TYPE]}
/>
</FieldRowProvider>
<>
<EuiSpacer size="l" />
<EuiSpacer size="l" />
<FieldRowProvider
links={docLinks.links.management}
showDanger={(message: string) => notifications.toasts.addDanger(message)}
validateChange={(key: string, value: any) => settings.client.validateValue(key, value)}
>
<FieldRow
field={field}
isSavingEnabled={!!canEditAdvancedSettings}
onFieldChange={handleFieldChange}
unsavedChange={unsavedChanges[AI_ASSISTANT_PREFERRED_AI_ASSISTANT_TYPE]}
/>
</FieldRowProvider>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
* 2.0.
*/

import React, { useState, useCallback } from 'react';
import React, { useState, useCallback, useMemo } from 'react';
import { EuiLink, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { FieldRow, FieldRowProvider } from '@kbn/management-settings-components-field-row';
import { AI_CHAT_EXPERIENCE_TYPE } from '@kbn/management-settings-ids';
import { AIChatExperience } from '@kbn/ai-assistant-common';
Expand All @@ -22,8 +24,9 @@ export const ChatExperience: React.FC = () => {

const [isConfirmModalOpen, setConfirmModalOpen] = useState(false);
const isAiAgentsEnabled = getIsAiAgentsEnabled(featureFlags);
const field = fields[AI_CHAT_EXPERIENCE_TYPE];
const canEditAdvancedSettings = Boolean(application.capabilities.advancedSettings?.save);

// Show confirmation modal for AI Agents selection
const wrappedHandleFieldChange: typeof handleFieldChange = useCallback(
(id, change) => {
if (id === AI_CHAT_EXPERIENCE_TYPE && change?.unsavedValue === AIChatExperience.Agent) {
Expand All @@ -44,33 +47,61 @@ export const ChatExperience: React.FC = () => {
handleFieldChange(AI_CHAT_EXPERIENCE_TYPE, undefined);
}, [handleFieldChange]);

// Don't render if AI Agents feature is disabled
if (!isAiAgentsEnabled) {
const description = useMemo(
() => (
<FormattedMessage
id="aiAssistantManagementSelection.preferredChatExperienceSettingDescription"
defaultMessage="Choose which chat experience to use for everyone in this space. {learnMoreLink}"
values={{
learnMoreLink: (
<EuiLink
href={docLinks.links.agentBuilder.learnMore}
target="_blank"
data-test-subj="aiAgentBuilderLearnMoreLink"
>
<FormattedMessage
id="aiAssistantManagementSelection.preferredChatExperienceSettingDescription.learnMoreLink"
defaultMessage="Learn more"
/>
</EuiLink>
),
}}
/>
),
[docLinks.links.agentBuilder.learnMore]
);

if (!isAiAgentsEnabled || !field) {
return null;
}

const field = fields[AI_CHAT_EXPERIENCE_TYPE];
if (!field) return null;

const canEditAdvancedSettings = application.capabilities.advancedSettings?.save;
const fieldWithDescription = {
...field,
description,
};

return (
<>
<EuiSpacer size="l" />
<FieldRowProvider
links={docLinks.links.management}
showDanger={(message: string) => notifications.toasts.addDanger(message)}
validateChange={(key: string, value: any) => settings.client.validateValue(key, value)}
>
<FieldRow
field={field}
isSavingEnabled={!!canEditAdvancedSettings}
field={fieldWithDescription}
isSavingEnabled={canEditAdvancedSettings}
onFieldChange={wrappedHandleFieldChange}
unsavedChange={unsavedChanges[AI_CHAT_EXPERIENCE_TYPE]}
/>
</FieldRowProvider>

{isConfirmModalOpen && (
<AIAgentConfirmationModal onConfirm={handleConfirmAgent} onCancel={handleCancelAgent} />
<AIAgentConfirmationModal
onConfirm={handleConfirmAgent}
onCancel={handleCancelAgent}
docLinks={docLinks.links}
/>
)}
</>
);
Expand Down
Loading