diff --git a/x-pack/platform/plugins/shared/observability_ai_assistant/public/hooks/use_agent_builder_opt_in.ts b/x-pack/platform/plugins/shared/observability_ai_assistant/public/hooks/use_agent_builder_opt_in.ts index 8e059f77aa62c..d1499940be1f0 100644 --- a/x-pack/platform/plugins/shared/observability_ai_assistant/public/hooks/use_agent_builder_opt_in.ts +++ b/x-pack/platform/plugins/shared/observability_ai_assistant/public/hooks/use_agent_builder_opt_in.ts @@ -59,14 +59,9 @@ export interface UseAgentBuilderOptInResult { export const useAgentBuilderOptIn = ({ navigateFromConversationApp = false, }: UseAgentBuilderOptInParams = {}): UseAgentBuilderOptInResult => { - const { - application, - notifications, - settings, - plugins: { - start: { onechat }, - }, - } = useKibana().services; + const { application, notifications, settings, plugins } = useKibana().services; + + const onechat = plugins?.start?.onechat; const { hasAgentBuilderAccess, isAgentChatExperienceEnabled } = useIsAgentBuilderEnabled(); diff --git a/x-pack/solutions/observability/plugins/serverless_observability/public/navigation_tree.ts b/x-pack/solutions/observability/plugins/serverless_observability/public/navigation_tree.ts index 1562c19d0f8f8..7c5140ce315b1 100644 --- a/x-pack/solutions/observability/plugins/serverless_observability/public/navigation_tree.ts +++ b/x-pack/solutions/observability/plugins/serverless_observability/public/navigation_tree.ts @@ -459,24 +459,25 @@ export const createNavigationTree = ({ }, overviewAvailable ), - ...filterForFeatureAvailability( - { - title: i18n.translate('xpack.serverlessObservability.nav.projectSettings.ai', { - defaultMessage: 'AI', - }), - children: [ - { - link: 'management:genAiSettings', - breadcrumbStatus: 'hidden', - }, - { - link: 'management:observabilityAiAssistantManagement', - breadcrumbStatus: 'hidden', - }, - ], - }, - overviewAvailable - ), + { + title: i18n.translate('xpack.serverlessObservability.nav.projectSettings.ai', { + defaultMessage: 'AI', + }), + children: [ + { + link: 'management:genAiSettings' as const, + breadcrumbStatus: 'hidden' as const, + }, + ...(showAiAssistant + ? [ + { + link: 'management:observabilityAiAssistantManagement' as const, + breadcrumbStatus: 'hidden' as const, + }, + ] + : []), + ], + }, { id: 'content', title: i18n.translate('xpack.serverlessObservability.nav.projectSettings.content', { diff --git a/x-pack/solutions/search/plugins/serverless_search/moon.yml b/x-pack/solutions/search/plugins/serverless_search/moon.yml index b1f6f48e31bc9..5d2aaa60f29dc 100644 --- a/x-pack/solutions/search/plugins/serverless_search/moon.yml +++ b/x-pack/solutions/search/plugins/serverless_search/moon.yml @@ -60,6 +60,8 @@ dependsOn: - '@kbn/search-shared-ui' - '@kbn/deeplinks-management' - '@kbn/react-query' + - '@kbn/ai-assistant-common' + - '@kbn/management-settings-ids' tags: - plugin - prod diff --git a/x-pack/solutions/search/plugins/serverless_search/public/navigation_tree.test.ts b/x-pack/solutions/search/plugins/serverless_search/public/navigation_tree.test.ts new file mode 100644 index 0000000000000..4021a94a13ab7 --- /dev/null +++ b/x-pack/solutions/search/plugins/serverless_search/public/navigation_tree.test.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createNavigationTree } from './navigation_tree'; + +describe('Navigation Tree', () => { + const mockApplication = { + isAppRegistered: jest.fn(), + } as any; + + it('should generate tree with home link', () => { + const navigation = createNavigationTree(mockApplication); + const { body } = navigation; + expect(body.length).toBeGreaterThan(0); + const homeNode = body[0]; + expect(homeNode).toMatchObject({ + title: 'Elasticsearch', + link: 'searchHomepage', + }); + }); + + it('shows AI section with GenAI settings and AI Assistant when AI Assistant is enabled', () => { + const { footer } = createNavigationTree(mockApplication); + + const adminAndSettingsNode = footer?.find((item: any) => item.id === 'admin_and_settings'); + const aiSection = adminAndSettingsNode?.children?.find( + (item: any) => item.id === 'settings_ai' + ); + + expect(aiSection).toBeDefined(); + expect(aiSection?.children).toContainEqual( + expect.objectContaining({ + link: 'management:genAiSettings', + }) + ); + expect(aiSection?.children).toContainEqual( + expect.objectContaining({ + link: 'management:observabilityAiAssistantManagement', + }) + ); + }); + + it('shows AI section with GenAI settings but hides AI Assistant when AI Assistant is disabled', () => { + const { footer } = createNavigationTree({ ...mockApplication, showAiAssistant: false }); + + const adminAndSettingsNode = footer?.find((item: any) => item.id === 'admin_and_settings'); + const aiSection = adminAndSettingsNode?.children?.find( + (item: any) => item.id === 'settings_ai' + ); + + expect(aiSection).toBeDefined(); + expect(aiSection?.children).toContainEqual( + expect.objectContaining({ + link: 'management:genAiSettings', + }) + ); + expect(aiSection?.children).not.toContainEqual( + expect.objectContaining({ + link: 'management:observabilityAiAssistantManagement', + }) + ); + }); +}); diff --git a/x-pack/solutions/search/plugins/serverless_search/public/navigation_tree.ts b/x-pack/solutions/search/plugins/serverless_search/public/navigation_tree.ts index 44b2ebc26ce47..912baac1065a8 100644 --- a/x-pack/solutions/search/plugins/serverless_search/public/navigation_tree.ts +++ b/x-pack/solutions/search/plugins/serverless_search/public/navigation_tree.ts @@ -47,7 +47,10 @@ const AI_TITLE = i18n.translate('xpack.serverlessSearch.nav.adminAndSettings.ai. defaultMessage: 'AI', }); -export const navigationTree = ({ isAppRegistered }: ApplicationStart): NavigationTreeDefinition => { +export function createNavigationTree({ + isAppRegistered, + showAiAssistant = true, +}: ApplicationStart & { showAiAssistant?: boolean }): NavigationTreeDefinition { return { body: [ { @@ -264,10 +267,14 @@ export const navigationTree = ({ isAppRegistered }: ApplicationStart): Navigatio title: AI_TITLE, children: [ { link: 'management:genAiSettings', breadcrumbStatus: 'hidden' }, - { - link: 'management:observabilityAiAssistantManagement', - breadcrumbStatus: 'hidden', - }, + ...(showAiAssistant + ? [ + { + link: 'management:observabilityAiAssistantManagement' as const, + breadcrumbStatus: 'hidden' as const, + }, + ] + : []), ], }, { @@ -300,4 +307,8 @@ export const navigationTree = ({ isAppRegistered }: ApplicationStart): Navigatio }, ], }; +} + +export const navigationTree = (application: ApplicationStart): NavigationTreeDefinition => { + return createNavigationTree(application); }; diff --git a/x-pack/solutions/search/plugins/serverless_search/public/plugin.ts b/x-pack/solutions/search/plugins/serverless_search/public/plugin.ts index cdb0b004a665f..4d8f8b5f49414 100644 --- a/x-pack/solutions/search/plugins/serverless_search/public/plugin.ts +++ b/x-pack/solutions/search/plugins/serverless_search/public/plugin.ts @@ -10,7 +10,9 @@ import { DEFAULT_APP_CATEGORIES } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { appCategories, appIds } from '@kbn/management-cards-navigation'; import { QueryClient, MutationCache, QueryCache } from '@kbn/react-query'; -import { of } from 'rxjs'; +import { combineLatest, map, of } from 'rxjs'; +import { AIChatExperience } from '@kbn/ai-assistant-common'; +import { AI_CHAT_EXPERIENCE_TYPE } from '@kbn/management-settings-ids'; import { docLinks } from '../common/doc_links'; import type { ServerlessSearchPluginSetup, @@ -19,7 +21,7 @@ import type { ServerlessSearchPluginStartDependencies, } from './types'; import { getErrorCode, getErrorMessage, isKibanaServerError } from './utils/get_error_message'; -import { navigationTree } from './navigation_tree'; +import { createNavigationTree } from './navigation_tree'; import { SEARCH_HOMEPAGE_PATH } from './application/constants'; import { WEB_CRAWLERS_LABEL } from '../common/i18n_string'; @@ -131,7 +133,14 @@ export class ServerlessSearchPlugin serverless.setProjectHome(SEARCH_HOMEPAGE_PATH); const aiAssistantIsEnabled = core.application.capabilities.observabilityAIAssistant?.show; - const navigationTree$ = of(navigationTree(core.application)); + const chatExperience$ = core.settings.client.get$(AI_CHAT_EXPERIENCE_TYPE); + + const navigationTree$ = combineLatest([of(core.application), chatExperience$]).pipe( + map(([application, chatExperience]) => { + const showAiAssistant = chatExperience !== AIChatExperience.Agent; + return createNavigationTree({ ...application, showAiAssistant }); + }) + ); serverless.initNavigation('es', navigationTree$, { dataTestSubj: 'svlSearchSideNav' }); const extendCardNavDefinitions = serverless.getNavigationCards( diff --git a/x-pack/solutions/search/plugins/serverless_search/tsconfig.json b/x-pack/solutions/search/plugins/serverless_search/tsconfig.json index 28c397e53436d..a49afaef5d3c9 100644 --- a/x-pack/solutions/search/plugins/serverless_search/tsconfig.json +++ b/x-pack/solutions/search/plugins/serverless_search/tsconfig.json @@ -56,6 +56,8 @@ "@kbn/core-application-browser", "@kbn/search-shared-ui", "@kbn/deeplinks-management", - "@kbn/react-query" + "@kbn/react-query", + "@kbn/ai-assistant-common", + "@kbn/management-settings-ids" ] }