From 7a3d91d4f9039039a3a79f5aa8d0e5ddcb10c38f Mon Sep 17 00:00:00 2001
From: Kenneth Kreindler <42113355+KDKHD@users.noreply.github.com>
Date: Mon, 14 Jul 2025 10:23:41 +0100
Subject: [PATCH 1/3] [Security Solution] [Ai Assistant] Enable the Global
Security AI assistant (#226556)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
Summarize your PR. If it involves visual changes include a screenshot or
gif.
Modify AI assistant visibility settings to allow for the Security AI
assistant to be global.
This fixes an issue in the product that several users have raised. It is
very unintuitive for them that when in the Security Solution view, they
can not open the AI assistant when in the Discover page.
> Users couldn’t add the AI Assistant to the Discover flyout and hadn’t
been able to resolve it for weeks
Changes:
- Add "Security" as an option in the
`aiAssistant:preferredAIAssistantType` setting (this is the setting used
in stateful Kibana to determine assistant visibility).
- Modify setting labels and options inside of serverless oblt and
serverless security to be relevant to the project type.
### Screenshots
Stateful
The same setting is used across all solution views in stateful Kibana.
This is not a perfect solution, as when a user is in the Security
solution, they can technically still select the Oblt assistant (in this
case no assistant will be shown). In the next minor release, this will
be fixed with a new AI assistant settings page.
### Classic
### Oblt
### Security
### ES
Serverless Security


Serverless Oblt
Serverless ES
In serverless ES there is no ability to change the visibility of the AI
assistant. This is the current behaviour and this has not changed in
this PR.

### How to test:
#### Stateful
- Start Kibana (in classic/traditional flavour)
```
// Start ES
yarn es snapshot --license trial -E xpack.security.authc.api_key.enabled=true
// Start Kibana
yarn start --no-base-path
```
- Create spaces for Classic, Security, Oblt and Search
- In Classic, go to Stack Management > Advanced Settings and search for
AI Assistant visibility
- Verify that when selecting:
- Only in their solutions -> only displays the Search/Oblt and Security
Assistants in their respective solutions.
- Observability and Search AI Assistants in other apps -> Shows the Oblt
and Search assistant on the Discover page.
- Security AI Assistants in other apps -> Shows the Security assistant
on the Discover page.
- Hide all assistants -> Does not show assistants anywhere
#### Stateless
- Start Kibana (in serverless)
```
// Start ES
node scripts/es serverless --projectType security
// Start Kibana
node --no-experimental-require-module scripts/kibana --dev --serverless=security --no-base-path
```
(please try try different project types too. Instead of security you can
use `oblt` or `es` )
- Go to stack management > advanced settings and search for AI Assistant
visibility
- Check that the options you have available look like this:

- Verify that when you select security, the Security assistant appears
everywhere (including Discover)
### Checklist
Check the PR satisfies following conditions.
Reviewers should verify this PR satisfies this list as well.
- [X] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [X]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [X] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [X] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [X] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [X] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [X] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [X] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.
### Identify risks
Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.
Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.
- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Elastic Machine
Co-authored-by: Ievgen Sorokopud
Co-authored-by: Dario Gieselaar
(cherry picked from commit 6536c766d7470b1f754803a303fdca9a068e2b7b)
# Conflicts:
# src/platform/plugins/shared/ai_assistant_management/selection/server/plugin.ts
---
config/serverless.security.yml | 3 +
.../settings/security_project/index.ts | 1 +
.../selection/common/ai_assistant_type.ts | 1 +
.../selection/kibana.jsonc | 5 +-
.../selection/public/plugin.test.ts | 46 ++++
.../selection/server/config.ts | 1 +
.../selection/server/plugin.test.ts | 189 ++++++++++++++
.../selection/server/plugin.ts | 91 ++++---
.../server/src/settings/classic_setting.ts | 56 +++++
.../src/settings/observability_setting.ts | 42 ++++
.../server/src/settings/security_setting.ts | 42 ++++
.../server/src/settings/translations.ts | 49 ++++
.../selection/server/types.ts | 2 +
.../selection/tsconfig.json | 4 +-
.../test_suites/core_plugins/rendering.ts | 2 +-
.../hooks/is_nav_control_visible.test.tsx | 237 ++++++++++++++++++
.../public/hooks/is_nav_control_visible.tsx | 6 +-
.../assistant_context/assistant_provider.tsx | 3 +-
.../use_assistant_availability.test.ts | 64 +++++
.../use_assistant_availability.ts | 4 +-
.../use_is_nav_control_visible.test.ts | 118 +++++++++
.../use_is_nav_control_visible.ts | 12 +-
...migrate_conversation_from_local_storage.ts | 5 +
.../public/assistant/provider.tsx | 10 +-
.../use_assistant_availability/index.tsx | 35 +--
.../rule_status_failed_callout.test.tsx | 21 +-
.../rules_table/rules_table_toolbar.tsx | 2 +-
.../rule_upgrade_test_providers.tsx | 9 +-
.../right/hooks/use_assistant.test.tsx | 16 ++
.../right/hooks/use_assistant.ts | 2 +-
30 files changed, 962 insertions(+), 116 deletions(-)
create mode 100644 src/platform/plugins/shared/ai_assistant_management/selection/public/plugin.test.ts
create mode 100644 src/platform/plugins/shared/ai_assistant_management/selection/server/plugin.test.ts
create mode 100644 src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/classic_setting.ts
create mode 100644 src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/observability_setting.ts
create mode 100644 src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/security_setting.ts
create mode 100644 src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/translations.ts
create mode 100644 x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/hooks/is_nav_control_visible.test.tsx
create mode 100644 x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/is_nav_control_visible/use_is_nav_control_visible.test.ts
diff --git a/config/serverless.security.yml b/config/serverless.security.yml
index 3e3a1eb782a33..a607ace5d629d 100644
--- a/config/serverless.security.yml
+++ b/config/serverless.security.yml
@@ -211,3 +211,6 @@ xpack.alerting.rules.run.ruleTypeOverrides:
# These features are disabled in Serverless until fully tested
xpack.securitySolution.enableExperimental:
- privilegedUserMonitoringDisabled
+
+# AI Assistant config
+aiAssistantManagementSelection.preferredAIAssistantType: 'security'
\ No newline at end of file
diff --git a/src/platform/packages/shared/serverless/settings/security_project/index.ts b/src/platform/packages/shared/serverless/settings/security_project/index.ts
index 6dacb7940a3aa..2ccbd97dac10e 100644
--- a/src/platform/packages/shared/serverless/settings/security_project/index.ts
+++ b/src/platform/packages/shared/serverless/settings/security_project/index.ts
@@ -26,4 +26,5 @@ export const SECURITY_PROJECT_SETTINGS = [
settings.SECURITY_SOLUTION_ENABLE_GRAPH_VISUALIZATION_SETTING,
settings.SECURITY_SOLUTION_ENABLE_ASSET_INVENTORY_SETTING,
settings.SECURITY_SOLUTION_ENABLE_CLOUD_CONNECTOR_SETTING,
+ settings.AI_ASSISTANT_PREFERRED_AI_ASSISTANT_TYPE,
];
diff --git a/src/platform/plugins/shared/ai_assistant_management/selection/common/ai_assistant_type.ts b/src/platform/plugins/shared/ai_assistant_management/selection/common/ai_assistant_type.ts
index 7f50f99277a75..d0947535299bb 100644
--- a/src/platform/plugins/shared/ai_assistant_management/selection/common/ai_assistant_type.ts
+++ b/src/platform/plugins/shared/ai_assistant_management/selection/common/ai_assistant_type.ts
@@ -9,6 +9,7 @@
export enum AIAssistantType {
Observability = 'observability',
+ Security = 'security',
Default = 'default',
Never = 'never',
}
diff --git a/src/platform/plugins/shared/ai_assistant_management/selection/kibana.jsonc b/src/platform/plugins/shared/ai_assistant_management/selection/kibana.jsonc
index d1509c7d34262..5508eb3d5e1c3 100644
--- a/src/platform/plugins/shared/ai_assistant_management/selection/kibana.jsonc
+++ b/src/platform/plugins/shared/ai_assistant_management/selection/kibana.jsonc
@@ -13,12 +13,13 @@
"aiAssistantManagementSelection"
],
"requiredPlugins": [
- "management"
+ "management",
],
"optionalPlugins": [
"home",
"serverless",
- "features"
+ "features",
+ "cloud"
],
"requiredBundles": [
"kibanaReact"
diff --git a/src/platform/plugins/shared/ai_assistant_management/selection/public/plugin.test.ts b/src/platform/plugins/shared/ai_assistant_management/selection/public/plugin.test.ts
new file mode 100644
index 0000000000000..c288feb8cbde0
--- /dev/null
+++ b/src/platform/plugins/shared/ai_assistant_management/selection/public/plugin.test.ts
@@ -0,0 +1,46 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+import { CoreStart, PluginInitializerContext } from '@kbn/core/public';
+import { AIAssistantManagementPlugin } from './plugin';
+import { AIAssistantType } from '../common/ai_assistant_type';
+import { PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY } from '../common/ui_setting_keys';
+
+describe('AI Assistant Management Selection Plugin', () => {
+ it('uses the correct setting key to get the correct value from uiSettings', async () => {
+ const plugin = new AIAssistantManagementPlugin({
+ config: {
+ get: jest.fn(),
+ },
+ env: { packageInfo: { buildFlavor: 'traditional', branch: 'main' } },
+ } as unknown as PluginInitializerContext);
+
+ const coreStart = {
+ uiSettings: {
+ get: jest.fn((key: string) => {
+ if (key === PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY) {
+ return AIAssistantType.Default;
+ }
+ }),
+ },
+ } as unknown as CoreStart;
+
+ const result = plugin.start(coreStart);
+
+ const collected: any[] = [];
+ const subscription = result.aiAssistantType$.subscribe((value) => {
+ collected.push(value);
+ });
+ subscription.unsubscribe();
+
+ const allCalls = (coreStart.uiSettings.get as jest.Mock).mock.calls;
+ expect(allCalls).toEqual([['aiAssistant:preferredAIAssistantType']]);
+ expect(collected).toEqual([AIAssistantType.Default]);
+ });
+});
diff --git a/src/platform/plugins/shared/ai_assistant_management/selection/server/config.ts b/src/platform/plugins/shared/ai_assistant_management/selection/server/config.ts
index c8aaee1a80626..9e6b64c2492c2 100644
--- a/src/platform/plugins/shared/ai_assistant_management/selection/server/config.ts
+++ b/src/platform/plugins/shared/ai_assistant_management/selection/server/config.ts
@@ -18,6 +18,7 @@ const configSchema = schema.object({
schema.literal(AIAssistantType.Default),
schema.literal(AIAssistantType.Never),
schema.literal(AIAssistantType.Observability),
+ schema.literal(AIAssistantType.Security),
],
{ defaultValue: AIAssistantType.Default }
),
diff --git a/src/platform/plugins/shared/ai_assistant_management/selection/server/plugin.test.ts b/src/platform/plugins/shared/ai_assistant_management/selection/server/plugin.test.ts
new file mode 100644
index 0000000000000..5d00c575cb7e9
--- /dev/null
+++ b/src/platform/plugins/shared/ai_assistant_management/selection/server/plugin.test.ts
@@ -0,0 +1,189 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+import { PluginInitializerContext, CoreSetup } from '@kbn/core/server';
+import type { AIAssistantManagementSelectionPluginServerDependenciesSetup } from './types';
+import { AIAssistantType } from '../common/ai_assistant_type';
+import { PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY } from '../common/ui_setting_keys';
+import { classicSetting } from './src/settings/classic_setting';
+import { observabilitySolutionSetting } from './src/settings/observability_setting';
+import { securitySolutionSetting } from './src/settings/security_setting';
+import { AIAssistantManagementSelectionPlugin } from './plugin';
+
+describe('plugin', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+ describe('stateless', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+ const initializerContext = {
+ env: {
+ packageInfo: {
+ buildFlavor: 'serverless',
+ },
+ },
+ config: {
+ get: jest.fn(),
+ },
+ } as unknown as PluginInitializerContext;
+
+ const coreSetup = {
+ uiSettings: {
+ register: jest.fn(),
+ },
+ capabilities: {
+ registerProvider: jest.fn(),
+ },
+ } as unknown as CoreSetup;
+
+ const setupDeps = {
+ management: {
+ sections: {
+ getSection: jest.fn(),
+ },
+ },
+ serverless: {
+ uiSettings: {
+ register: jest.fn(),
+ },
+ },
+ };
+
+ it('registers correct uiSettings for serverless oblt', () => {
+ (initializerContext.config.get as jest.Mock).mockReturnValue({
+ preferredAIAssistantType: AIAssistantType.Observability,
+ });
+ const aiAssistantManagementSelectionPlugin = new AIAssistantManagementSelectionPlugin(
+ initializerContext
+ );
+ aiAssistantManagementSelectionPlugin.setup(coreSetup, {
+ ...setupDeps,
+ cloud: {
+ serverless: {
+ projectType: 'observability',
+ },
+ },
+ } as unknown as AIAssistantManagementSelectionPluginServerDependenciesSetup);
+
+ expect(coreSetup.uiSettings.register).toHaveBeenCalledTimes(1);
+
+ expect(coreSetup.uiSettings.register).toHaveBeenCalledWith({
+ [PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY]: {
+ ...observabilitySolutionSetting,
+ value: AIAssistantType.Observability,
+ },
+ });
+ });
+
+ it('registers correct uiSettings for serverless security', () => {
+ (initializerContext.config.get as jest.Mock).mockReturnValue({
+ preferredAIAssistantType: AIAssistantType.Security,
+ });
+ const aiAssistantManagementSelectionPlugin = new AIAssistantManagementSelectionPlugin(
+ initializerContext
+ );
+ aiAssistantManagementSelectionPlugin.setup(coreSetup, {
+ ...setupDeps,
+ cloud: {
+ serverless: {
+ projectType: 'security',
+ },
+ },
+ } as unknown as AIAssistantManagementSelectionPluginServerDependenciesSetup);
+
+ expect(coreSetup.uiSettings.register).toHaveBeenCalledTimes(1);
+
+ expect(coreSetup.uiSettings.register).toHaveBeenCalledWith({
+ [PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY]: {
+ ...securitySolutionSetting,
+ value: AIAssistantType.Security,
+ },
+ });
+ });
+
+ it('registers correct uiSettings for serverless search', () => {
+ (initializerContext.config.get as jest.Mock).mockReturnValue({
+ preferredAIAssistantType: undefined,
+ });
+ const aiAssistantManagementSelectionPlugin = new AIAssistantManagementSelectionPlugin(
+ initializerContext
+ );
+ aiAssistantManagementSelectionPlugin.setup(coreSetup, {
+ ...setupDeps,
+ cloud: {
+ serverless: {
+ projectType: 'search',
+ },
+ },
+ } as unknown as AIAssistantManagementSelectionPluginServerDependenciesSetup);
+
+ expect(coreSetup.uiSettings.register).toHaveBeenCalledTimes(1);
+ expect(coreSetup.uiSettings.register).toHaveBeenCalledWith({
+ [PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY]: {
+ ...classicSetting,
+ value: AIAssistantType.Default,
+ },
+ });
+ });
+ });
+
+ describe('stateful', () => {
+ it('uses the correct setting key to get the correct value from uiSettings', async () => {
+ const initializerContext = {
+ env: {
+ packageInfo: {
+ buildFlavor: 'classic',
+ },
+ },
+ config: {
+ get: jest.fn().mockReturnValue({
+ preferredAIAssistantType: AIAssistantType.Observability,
+ }),
+ },
+ } as unknown as PluginInitializerContext;
+ const aiAssistantManagementSelectionPlugin = new AIAssistantManagementSelectionPlugin(
+ initializerContext
+ );
+
+ const coreSetup = {
+ uiSettings: {
+ register: jest.fn(),
+ },
+ capabilities: {
+ registerProvider: jest.fn(),
+ },
+ } as unknown as CoreSetup;
+
+ const setupDeps = {
+ management: {
+ sections: {
+ getSection: jest.fn(),
+ },
+ },
+ serverless: {
+ uiSettings: {
+ register: jest.fn(),
+ },
+ },
+ } as unknown as AIAssistantManagementSelectionPluginServerDependenciesSetup;
+
+ aiAssistantManagementSelectionPlugin.setup(coreSetup, setupDeps);
+
+ expect(coreSetup.uiSettings.register).toHaveBeenCalledTimes(1);
+ expect(coreSetup.uiSettings.register).toHaveBeenCalledWith({
+ [PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY]: {
+ ...classicSetting,
+ value: AIAssistantType.Observability,
+ },
+ });
+ });
+ });
+});
diff --git a/src/platform/plugins/shared/ai_assistant_management/selection/server/plugin.ts b/src/platform/plugins/shared/ai_assistant_management/selection/server/plugin.ts
index 67a4e000ed78d..742584ceebafe 100644
--- a/src/platform/plugins/shared/ai_assistant_management/selection/server/plugin.ts
+++ b/src/platform/plugins/shared/ai_assistant_management/selection/server/plugin.ts
@@ -16,7 +16,6 @@ import {
Plugin,
DEFAULT_APP_CATEGORIES,
} from '@kbn/core/server';
-import { schema } from '@kbn/config-schema';
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
import type { AIAssistantManagementSelectionConfig } from './config';
import type {
@@ -25,8 +24,11 @@ import type {
AIAssistantManagementSelectionPluginServerSetup,
AIAssistantManagementSelectionPluginServerStart,
} from './types';
-import { AIAssistantType } from '../common/ai_assistant_type';
import { PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY } from '../common/ui_setting_keys';
+import { classicSetting } from './src/settings/classic_setting';
+import { observabilitySolutionSetting } from './src/settings/observability_setting';
+import { securitySolutionSetting } from './src/settings/security_setting';
+import { AIAssistantType } from '../common/ai_assistant_type';
export class AIAssistantManagementSelectionPlugin
implements
@@ -47,52 +49,6 @@ export class AIAssistantManagementSelectionPlugin
core: CoreSetup,
plugins: AIAssistantManagementSelectionPluginServerDependenciesSetup
) {
- core.uiSettings.register({
- [PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY]: {
- name: i18n.translate('aiAssistantManagementSelection.preferredAIAssistantTypeSettingName', {
- defaultMessage: 'AI Assistant for Observability and Search visibility',
- }),
- category: [DEFAULT_APP_CATEGORIES.observability.id],
- value: this.config.preferredAIAssistantType,
- description: i18n.translate(
- 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingDescription',
- {
- defaultMessage:
- '[technical preview] Whether to show the AI Assistant menu item in Observability and Search, everywhere, or nowhere.',
- values: {
- em: (chunks) => `${chunks}`,
- },
- }
- ),
- schema: schema.oneOf(
- [
- schema.literal(AIAssistantType.Default),
- schema.literal(AIAssistantType.Observability),
- schema.literal(AIAssistantType.Never),
- ],
- { defaultValue: this.config.preferredAIAssistantType }
- ),
- options: [AIAssistantType.Default, AIAssistantType.Observability, AIAssistantType.Never],
- type: 'select',
- optionLabels: {
- [AIAssistantType.Default]: i18n.translate(
- 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueDefault',
- { defaultMessage: 'Observability and Search only (default)' }
- ),
- [AIAssistantType.Observability]: i18n.translate(
- 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueObservability',
- { defaultMessage: 'Everywhere' }
- ),
- [AIAssistantType.Never]: i18n.translate(
- 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueNever',
- { defaultMessage: 'Nowhere' }
- ),
- },
- requiresPageReload: true,
- solution: 'oblt',
- },
- });
-
core.capabilities.registerProvider(() => {
return {
management: {
@@ -154,9 +110,48 @@ export class AIAssistantManagementSelectionPlugin
},
});
+ this.registerUiSettings(core, plugins);
+
return {};
}
+ private registerUiSettings(
+ core: CoreSetup,
+ plugins: AIAssistantManagementSelectionPluginServerDependenciesSetup
+ ) {
+ const { cloud } = plugins;
+ const serverlessProjectType = cloud?.serverless.projectType;
+
+ switch (serverlessProjectType) {
+ case 'observability':
+ core.uiSettings.register({
+ [PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY]: {
+ ...observabilitySolutionSetting,
+ value: this.config.preferredAIAssistantType,
+ },
+ });
+ return;
+ case 'security':
+ core.uiSettings.register({
+ [PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY]: {
+ ...securitySolutionSetting,
+ value: this.config.preferredAIAssistantType,
+ },
+ });
+ return;
+ // TODO: Add another case for search with the correct copy of the setting.
+ // see: https://github.com/elastic/kibana/issues/227695
+ default:
+ // This case is hit when in stateful Kibana
+ return core.uiSettings.register({
+ [PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY]: {
+ ...classicSetting,
+ value: this.config.preferredAIAssistantType ?? AIAssistantType.Default,
+ },
+ });
+ }
+ }
+
public start(core: CoreStart) {
return {};
}
diff --git a/src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/classic_setting.ts b/src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/classic_setting.ts
new file mode 100644
index 0000000000000..bf5f9105102b0
--- /dev/null
+++ b/src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/classic_setting.ts
@@ -0,0 +1,56 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+import { i18n } from '@kbn/i18n';
+
+import { schema } from '@kbn/config-schema';
+import { UiSettingsParams } from '@kbn/core-ui-settings-common';
+import { AIAssistantType } from '../../../common/ai_assistant_type';
+import {
+ ONLY_IN_THEIR_SOLUTIONS,
+ OBSERVABILITY_IN_OTHER_APPS,
+ SECURITY_IN_OTHER_APPS,
+ HIDE_ALL_ASSISTANTS,
+ TITLE,
+} from './translations';
+
+// Define the classicSetting with proper typing
+export const classicSetting: Omit, 'value'> = {
+ name: TITLE,
+ description: i18n.translate(
+ 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingDescription',
+ {
+ defaultMessage:
+ 'Choose where and which AI Assistants are available. You can limit the AI Assistants to their own solutions, show either the Observability and Search AI Assistants or the Security AI Assistant in other Kibana apps, or hide AI Assistants entirely.',
+ }
+ ),
+ schema: schema.oneOf(
+ [
+ schema.literal(AIAssistantType.Default),
+ schema.literal(AIAssistantType.Observability),
+ schema.literal(AIAssistantType.Security),
+ schema.literal(AIAssistantType.Never),
+ ],
+ { defaultValue: AIAssistantType.Default }
+ ),
+ options: [
+ AIAssistantType.Default,
+ AIAssistantType.Observability,
+ AIAssistantType.Security,
+ AIAssistantType.Never,
+ ],
+ type: 'select' as const,
+ optionLabels: {
+ [AIAssistantType.Default]: ONLY_IN_THEIR_SOLUTIONS,
+ [AIAssistantType.Observability]: OBSERVABILITY_IN_OTHER_APPS,
+ [AIAssistantType.Security]: SECURITY_IN_OTHER_APPS,
+ [AIAssistantType.Never]: HIDE_ALL_ASSISTANTS,
+ },
+ requiresPageReload: true,
+};
diff --git a/src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/observability_setting.ts b/src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/observability_setting.ts
new file mode 100644
index 0000000000000..caecf5d3099a5
--- /dev/null
+++ b/src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/observability_setting.ts
@@ -0,0 +1,42 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+import { i18n } from '@kbn/i18n';
+
+import { schema } from '@kbn/config-schema';
+import { UiSettingsParams } from '@kbn/core-ui-settings-common';
+import { AIAssistantType } from '../../../common/ai_assistant_type';
+import { SHOW_OBSERVABILITY, HIDE_ASSISTANT, TITLE } from './translations';
+
+// Define the classicSetting with proper typing
+export const observabilitySolutionSetting: Omit<
+ UiSettingsParams,
+ 'value'
+> = {
+ name: TITLE,
+ description: i18n.translate(
+ 'aiAssistantManagementSelection.observabilitySolutionSetting.preferredAIAssistantTypeSettingDescription',
+ {
+ defaultMessage:
+ 'Choose if the Observability AI Assistant is available. Show the Observability AI Assistant, or hide the Assistant entirely.',
+ }
+ ),
+ schema: schema.oneOf(
+ [schema.literal(AIAssistantType.Observability), schema.literal(AIAssistantType.Never)],
+ { defaultValue: AIAssistantType.Observability }
+ ),
+ options: [AIAssistantType.Observability, AIAssistantType.Never],
+ type: 'select' as const,
+ optionLabels: {
+ [AIAssistantType.Observability]: SHOW_OBSERVABILITY,
+ [AIAssistantType.Never]: HIDE_ASSISTANT,
+ },
+ requiresPageReload: true,
+ solution: 'oblt',
+};
diff --git a/src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/security_setting.ts b/src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/security_setting.ts
new file mode 100644
index 0000000000000..03b4553b02860
--- /dev/null
+++ b/src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/security_setting.ts
@@ -0,0 +1,42 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+import { i18n } from '@kbn/i18n';
+
+import { schema } from '@kbn/config-schema';
+import { UiSettingsParams } from '@kbn/core-ui-settings-common';
+import { AIAssistantType } from '../../../common/ai_assistant_type';
+import { SHOW_SECURITY, HIDE_ASSISTANT, TITLE } from './translations';
+
+// Define the securitySolutionSetting with proper typing
+export const securitySolutionSetting: Omit<
+ UiSettingsParams,
+ 'value'
+> = {
+ name: TITLE,
+ description: i18n.translate(
+ 'aiAssistantManagementSelection.securitySolutionSetting.preferredAIAssistantTypeSettingDescription',
+ {
+ defaultMessage:
+ 'Choose if the Security AI Assistant is available. Show the Security AI Assistant, or hide the Assistant entirely.',
+ }
+ ),
+ schema: schema.oneOf(
+ [schema.literal(AIAssistantType.Security), schema.literal(AIAssistantType.Never)],
+ { defaultValue: AIAssistantType.Security }
+ ),
+ options: [AIAssistantType.Security, AIAssistantType.Never],
+ type: 'select' as const,
+ optionLabels: {
+ [AIAssistantType.Security]: SHOW_SECURITY,
+ [AIAssistantType.Never]: HIDE_ASSISTANT,
+ },
+ requiresPageReload: true,
+ solution: 'security',
+};
diff --git a/src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/translations.ts b/src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/translations.ts
new file mode 100644
index 0000000000000..26634464ea1e4
--- /dev/null
+++ b/src/platform/plugins/shared/ai_assistant_management/selection/server/src/settings/translations.ts
@@ -0,0 +1,49 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const TITLE = i18n.translate(
+ 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingName',
+ {
+ defaultMessage: 'AI Assistant visibility',
+ }
+);
+
+export const ONLY_IN_THEIR_SOLUTIONS = i18n.translate(
+ 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueDefault',
+ { defaultMessage: 'Only in their solutions' }
+);
+export const OBSERVABILITY_IN_OTHER_APPS = i18n.translate(
+ 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueObservability',
+ { defaultMessage: 'Observability and Search AI Assistants in other apps' }
+);
+export const SECURITY_IN_OTHER_APPS = i18n.translate(
+ 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueSecurity',
+ { defaultMessage: 'Security AI Assistant in other apps' }
+);
+export const HIDE_ALL_ASSISTANTS = i18n.translate(
+ 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueNever',
+ { defaultMessage: 'Hide all assistants' }
+);
+
+export const SHOW_SECURITY = i18n.translate(
+ 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueNever',
+ { defaultMessage: 'Show Security AI Assistant' }
+);
+
+export const SHOW_OBSERVABILITY = i18n.translate(
+ 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueNever',
+ { defaultMessage: 'Show Observability AI Assistant' }
+);
+
+export const HIDE_ASSISTANT = i18n.translate(
+ 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueNever',
+ { defaultMessage: 'Hide AI Assistant' }
+);
diff --git a/src/platform/plugins/shared/ai_assistant_management/selection/server/types.ts b/src/platform/plugins/shared/ai_assistant_management/selection/server/types.ts
index 4667ad9ab4fbc..d581b8be26540 100644
--- a/src/platform/plugins/shared/ai_assistant_management/selection/server/types.ts
+++ b/src/platform/plugins/shared/ai_assistant_management/selection/server/types.ts
@@ -7,6 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
+import { CloudSetup } from '@kbn/cloud-plugin/server';
import type { FeaturesPluginSetup } from '@kbn/features-plugin/server';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
@@ -14,6 +15,7 @@ export interface AIAssistantManagementSelectionPluginServerDependenciesStart {}
export interface AIAssistantManagementSelectionPluginServerDependenciesSetup {
features?: FeaturesPluginSetup;
+ cloud?: CloudSetup;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
diff --git a/src/platform/plugins/shared/ai_assistant_management/selection/tsconfig.json b/src/platform/plugins/shared/ai_assistant_management/selection/tsconfig.json
index 0fc6cec454817..7d7827387699d 100644
--- a/src/platform/plugins/shared/ai_assistant_management/selection/tsconfig.json
+++ b/src/platform/plugins/shared/ai_assistant_management/selection/tsconfig.json
@@ -18,7 +18,9 @@
"@kbn/core-plugins-server",
"@kbn/features-plugin",
"@kbn/config",
- "@kbn/doc-links"
+ "@kbn/doc-links",
+ "@kbn/core-ui-settings-common",
+ "@kbn/cloud-plugin"
],
"exclude": ["target/**/*"]
}
diff --git a/src/platform/test/plugin_functional/test_suites/core_plugins/rendering.ts b/src/platform/test/plugin_functional/test_suites/core_plugins/rendering.ts
index 746b7f7e8c110..272a87501f8cb 100644
--- a/src/platform/test/plugin_functional/test_suites/core_plugins/rendering.ts
+++ b/src/platform/test/plugin_functional/test_suites/core_plugins/rendering.ts
@@ -380,7 +380,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
'xpack.observabilityAiAssistantManagement.spacesEnabled (boolean?)',
'xpack.observabilityAiAssistantManagement.visibilityEnabled (boolean?)',
'share.new_version.enabled (boolean?)',
- 'aiAssistantManagementSelection.preferredAIAssistantType (default?|never?|observability?)',
+ 'aiAssistantManagementSelection.preferredAIAssistantType (default?|never?|observability?|security?)',
/**
* Rule form V2 feature flags
*/
diff --git a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/hooks/is_nav_control_visible.test.tsx b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/hooks/is_nav_control_visible.test.tsx
new file mode 100644
index 0000000000000..fb4a5e1a7b685
--- /dev/null
+++ b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/hooks/is_nav_control_visible.test.tsx
@@ -0,0 +1,237 @@
+/*
+ * 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 { renderHook } from '@testing-library/react';
+import { useIsNavControlVisible } from './is_nav_control_visible';
+import { CoreStart } from '@kbn/core/public';
+import { ObservabilityAIAssistantAppPluginStartDependencies } from '../types';
+import { of } from 'rxjs';
+import { AIAssistantType } from '@kbn/ai-assistant-management-plugin/public';
+
+describe('isNavControlVisible', () => {
+ describe('with solution:oblt', () => {
+ it('returns true when the current app is discover and the ai assistant type is observability', () => {
+ const coreStart = {
+ application: {
+ currentAppId$: of('discover'),
+ applications$: of(
+ new Map([['discover', { id: 'discover', category: { id: 'kibana' } }]])
+ ),
+ },
+ } as unknown as CoreStart;
+
+ const pluginsStart = {
+ aiAssistantManagementSelection: {
+ aiAssistantType$: of(AIAssistantType.Observability),
+ },
+ spaces: {
+ getActiveSpace$: () => of({ solution: 'oblt' }),
+ },
+ } as unknown as ObservabilityAIAssistantAppPluginStartDependencies;
+
+ const { result } = renderHook(() => useIsNavControlVisible({ coreStart, pluginsStart }));
+
+ expect(result.current.isVisible).toBe(true);
+ });
+
+ it('returns true when the current app is discover and the ai assistant type is default', () => {
+ const coreStart = {
+ application: {
+ currentAppId$: of('discover'),
+ applications$: of(
+ new Map([['discover', { id: 'discover', category: { id: 'kibana' } }]])
+ ),
+ },
+ } as unknown as CoreStart;
+
+ const pluginsStart = {
+ aiAssistantManagementSelection: {
+ aiAssistantType$: of(AIAssistantType.Default),
+ },
+ spaces: {
+ getActiveSpace$: () => of({ solution: 'oblt' }),
+ },
+ } as unknown as ObservabilityAIAssistantAppPluginStartDependencies;
+
+ const { result } = renderHook(() => useIsNavControlVisible({ coreStart, pluginsStart }));
+
+ expect(result.current.isVisible).toBe(true);
+ });
+
+ it('returns true when the current app is observability and the ai assistant type is default', () => {
+ const coreStart = {
+ application: {
+ currentAppId$: of('observability'),
+ applications$: of(
+ new Map([['observability', { id: 'observability', category: { id: 'observability' } }]])
+ ),
+ },
+ } as unknown as CoreStart;
+
+ const pluginsStart = {
+ aiAssistantManagementSelection: {
+ aiAssistantType$: of(AIAssistantType.Default),
+ },
+ spaces: {
+ getActiveSpace$: () => of({ solution: 'oblt' }),
+ },
+ } as unknown as ObservabilityAIAssistantAppPluginStartDependencies;
+
+ const { result } = renderHook(() => useIsNavControlVisible({ coreStart, pluginsStart }));
+
+ expect(result.current.isVisible).toBe(true);
+ });
+
+ it('returns true when the current app is search and the ai assistant type is default', () => {
+ const coreStart = {
+ application: {
+ currentAppId$: of('search'),
+ applications$: of(
+ new Map([['search', { id: 'search', category: { id: 'enterpriseSearch' } }]])
+ ),
+ },
+ } as unknown as CoreStart;
+
+ const pluginsStart = {
+ aiAssistantManagementSelection: {
+ aiAssistantType$: of(AIAssistantType.Default),
+ },
+ spaces: {
+ getActiveSpace$: () => of({ solution: 'oblt' }),
+ },
+ } as unknown as ObservabilityAIAssistantAppPluginStartDependencies;
+
+ const { result } = renderHook(() => useIsNavControlVisible({ coreStart, pluginsStart }));
+
+ expect(result.current.isVisible).toBe(true);
+ });
+
+ it('returns false when the current app is security and the ai assistant type is observability', () => {
+ const coreStart = {
+ application: {
+ currentAppId$: of('security'),
+ applications$: of(
+ new Map([['security', { id: 'security', category: { id: 'securitySolution' } }]])
+ ),
+ },
+ } as unknown as CoreStart;
+
+ const pluginsStart = {
+ aiAssistantManagementSelection: {
+ aiAssistantType$: of(AIAssistantType.Observability),
+ },
+ spaces: {
+ getActiveSpace$: () => of({ solution: 'oblt' }),
+ },
+ } as unknown as ObservabilityAIAssistantAppPluginStartDependencies;
+
+ const { result } = renderHook(() => useIsNavControlVisible({ coreStart, pluginsStart }));
+
+ expect(result.current.isVisible).toBe(false);
+ });
+
+ it('returns false when the ai assistant type is never', () => {
+ const coreStart = {
+ application: {
+ currentAppId$: of('observability'),
+ applications$: of(
+ new Map([['observability', { id: 'observability', category: { id: 'observability' } }]])
+ ),
+ },
+ } as unknown as CoreStart;
+
+ const pluginsStart = {
+ aiAssistantManagementSelection: {
+ aiAssistantType$: of(AIAssistantType.Never),
+ },
+ spaces: {
+ getActiveSpace$: () => of({ solution: 'oblt' }),
+ },
+ } as unknown as ObservabilityAIAssistantAppPluginStartDependencies;
+
+ const { result } = renderHook(() => useIsNavControlVisible({ coreStart, pluginsStart }));
+
+ expect(result.current.isVisible).toBe(false);
+ });
+ });
+
+ describe('with solution:es', () => {
+ it('returns true when the current space is es and the ai assistant type is observability', () => {
+ const coreStart = {
+ application: {
+ currentAppId$: of('discover'),
+ applications$: of(
+ new Map([['discover', { id: 'discover', category: { id: 'kibana' } }]])
+ ),
+ },
+ } as unknown as CoreStart;
+
+ const pluginsStart = {
+ aiAssistantManagementSelection: {
+ aiAssistantType$: of(AIAssistantType.Observability),
+ },
+ spaces: {
+ getActiveSpace$: () => of({ solution: 'es' }),
+ },
+ } as unknown as ObservabilityAIAssistantAppPluginStartDependencies;
+
+ const { result } = renderHook(() => useIsNavControlVisible({ coreStart, pluginsStart }));
+
+ expect(result.current.isVisible).toBe(true);
+ });
+
+ it('returns false when the current space is es and the ai assistant type is never', () => {
+ const coreStart = {
+ application: {
+ currentAppId$: of('discover'),
+ applications$: of(
+ new Map([['discover', { id: 'discover', category: { id: 'kibana' } }]])
+ ),
+ },
+ } as unknown as CoreStart;
+
+ const pluginsStart = {
+ aiAssistantManagementSelection: {
+ aiAssistantType$: of(AIAssistantType.Never),
+ },
+ spaces: {
+ getActiveSpace$: () => of({ solution: 'es' }),
+ },
+ } as unknown as ObservabilityAIAssistantAppPluginStartDependencies;
+
+ const { result } = renderHook(() => useIsNavControlVisible({ coreStart, pluginsStart }));
+
+ expect(result.current.isVisible).toBe(false);
+ });
+ });
+
+ describe('with classic', () => {
+ it('returns false when the ai assistant type is default', () => {
+ const coreStart = {
+ application: {
+ currentAppId$: of('discover'),
+ applications$: of(
+ new Map([['discover', { id: 'discover', category: { id: 'kibana' } }]])
+ ),
+ },
+ } as unknown as CoreStart;
+
+ const pluginsStart = {
+ aiAssistantManagementSelection: {
+ aiAssistantType$: of(AIAssistantType.Default),
+ },
+ spaces: {
+ getActiveSpace$: () => of({ solution: 'classic' }),
+ },
+ } as unknown as ObservabilityAIAssistantAppPluginStartDependencies;
+
+ const { result } = renderHook(() => useIsNavControlVisible({ coreStart, pluginsStart }));
+
+ expect(result.current.isVisible).toBe(false);
+ });
+ });
+});
diff --git a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/hooks/is_nav_control_visible.tsx b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/hooks/is_nav_control_visible.tsx
index ce295737315d9..e8fb82fb22ce0 100644
--- a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/hooks/is_nav_control_visible.tsx
+++ b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/hooks/is_nav_control_visible.tsx
@@ -30,7 +30,11 @@ function getVisibility(
const categoryId =
(appId && applications.get(appId)?.category?.id) || DEFAULT_APP_CATEGORIES.kibana.id;
- if (preferredAssistantType === AIAssistantType.Observability || space.solution === 'es') {
+ if (
+ preferredAssistantType === AIAssistantType.Observability ||
+ space.solution === 'es' ||
+ space.solution === 'oblt'
+ ) {
return categoryId !== DEFAULT_APP_CATEGORIES.security.id;
}
diff --git a/x-pack/solutions/security/plugins/elastic_assistant/public/src/context/assistant_context/assistant_provider.tsx b/x-pack/solutions/security/plugins/elastic_assistant/public/src/context/assistant_context/assistant_provider.tsx
index 3f9ca479e2915..dabff2d8e0d63 100644
--- a/x-pack/solutions/security/plugins/elastic_assistant/public/src/context/assistant_context/assistant_provider.tsx
+++ b/x-pack/solutions/security/plugins/elastic_assistant/public/src/context/assistant_context/assistant_provider.tsx
@@ -46,6 +46,7 @@ export function AssistantProvider({ children }: { children: React.ReactElement }
const inferenceEnabled = useInferenceEnabled();
const basePath = useBasePath();
+ const { isVisible } = useIsNavControlVisible();
const assistantAvailability = useAssistantAvailability();
const assistantTelemetry = useAssistantTelemetry();
@@ -113,8 +114,6 @@ export function AssistantProvider({ children }: { children: React.ReactElement }
);
}, [assistantContextValue, elasticAssistantSharedState.assistantContextValue]);
- const { isVisible } = useIsNavControlVisible();
-
if (!isVisible) {
return null;
}
diff --git a/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/assistant_availability/use_assistant_availability.test.ts b/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/assistant_availability/use_assistant_availability.test.ts
index a5b22556720f8..0be2ab01e3784 100644
--- a/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/assistant_availability/use_assistant_availability.test.ts
+++ b/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/assistant_availability/use_assistant_availability.test.ts
@@ -12,15 +12,24 @@ import { LicenseService } from '../licence/license_service';
import { renderHook } from '@testing-library/react';
import { SECURITY_FEATURE_ID } from '../../../../common/constants';
import { ASSISTANT_FEATURE_ID } from '@kbn/security-solution-features/constants';
+import { useIsNavControlVisible } from '../is_nav_control_visible/use_is_nav_control_visible';
+
jest.mock('../licence/use_licence');
jest.mock('../../context/typed_kibana_context/typed_kibana_context');
+jest.mock('../is_nav_control_visible/use_is_nav_control_visible');
const mockUseLicense = useLicense as jest.MockedFunction;
const mockUseKibana = useKibana as jest.MockedFunction;
+const mockUseIsNavControlVisible = useIsNavControlVisible as jest.MockedFunction<
+ typeof useIsNavControlVisible
+>;
describe('useAssistantAvailability', () => {
beforeEach(() => {
jest.resetAllMocks();
+ mockUseIsNavControlVisible.mockReturnValue({
+ isVisible: true,
+ });
});
it('returns correct values when all privileges are available', () => {
@@ -48,6 +57,9 @@ describe('useAssistantAvailability', () => {
},
},
},
+ aiAssistantManagementSelection: {
+ aiAssistantManagementSelection$: jest.fn(),
+ },
featureFlags: {
getBooleanValue: jest.fn().mockReturnValue(true),
},
@@ -68,6 +80,58 @@ describe('useAssistantAvailability', () => {
});
});
+ it('returns correct values when all privileges are available but assistant his hidden', () => {
+ mockUseLicense.mockReturnValue({
+ isEnterprise: jest.fn().mockReturnValue(true),
+ } as unknown as LicenseService);
+
+ mockUseIsNavControlVisible.mockReturnValue({
+ isVisible: false,
+ });
+
+ mockUseKibana.mockReturnValue({
+ services: {
+ application: {
+ capabilities: {
+ [ASSISTANT_FEATURE_ID]: {
+ 'ai-assistant': true,
+ updateAIAssistantAnonymization: true,
+ manageGlobalKnowledgeBaseAIAssistant: true,
+ },
+ [SECURITY_FEATURE_ID]: {
+ configurations: true,
+ },
+ actions: {
+ show: true,
+ execute: true,
+ save: true,
+ delete: true,
+ },
+ },
+ },
+ aiAssistantManagementSelection: {
+ aiAssistantManagementSelection$: jest.fn(),
+ },
+ featureFlags: {
+ getBooleanValue: jest.fn().mockReturnValue(true),
+ },
+ },
+ } as unknown as ReturnType);
+
+ const { result } = renderHook(() => useAssistantAvailability());
+
+ expect(result.current).toEqual({
+ hasSearchAILakeConfigurations: true,
+ hasAssistantPrivilege: true,
+ hasConnectorsAllPrivilege: true,
+ hasConnectorsReadPrivilege: true,
+ isAssistantEnabled: false,
+ isStarterPromptsEnabled: true,
+ hasUpdateAIAssistantAnonymization: true,
+ hasManageGlobalKnowledgeBase: true,
+ });
+ });
+
it('returns correct values when no privileges are available', () => {
mockUseLicense.mockReturnValue({
isEnterprise: jest.fn().mockReturnValue(false),
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 ced9f5101f8a7..f20598af543f1 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
@@ -11,9 +11,11 @@ import { SECURITY_FEATURE_ID } from '../../../../common/constants';
import { useKibana } from '../../context/typed_kibana_context/typed_kibana_context';
import { useLicense } from '../licence/use_licence';
+import { useIsNavControlVisible } from '../is_nav_control_visible/use_is_nav_control_visible';
export const STARTER_PROMPTS_FEATURE_FLAG = 'elasticAssistant.starterPromptsEnabled' as const;
export const useAssistantAvailability = (): UseAssistantAvailability => {
+ const { isVisible } = useIsNavControlVisible();
const isEnterprise = useLicense().isEnterprise();
const {
application: { capabilities },
@@ -45,7 +47,7 @@ export const useAssistantAvailability = (): UseAssistantAvailability => {
hasConnectorsAllPrivilege,
hasConnectorsReadPrivilege,
isStarterPromptsEnabled,
- isAssistantEnabled: isEnterprise,
+ isAssistantEnabled: isEnterprise && isVisible,
hasUpdateAIAssistantAnonymization,
hasManageGlobalKnowledgeBase,
};
diff --git a/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/is_nav_control_visible/use_is_nav_control_visible.test.ts b/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/is_nav_control_visible/use_is_nav_control_visible.test.ts
new file mode 100644
index 0000000000000..5858d8ac45323
--- /dev/null
+++ b/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/is_nav_control_visible/use_is_nav_control_visible.test.ts
@@ -0,0 +1,118 @@
+/*
+ * 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 { renderHook } from '@testing-library/react';
+import { useIsNavControlVisible } from './use_is_nav_control_visible';
+import { of } from 'rxjs';
+import { AIAssistantType } from '@kbn/ai-assistant-management-plugin/public';
+import { useKibana } from '../../context/typed_kibana_context/typed_kibana_context';
+
+jest.mock('../../context/typed_kibana_context/typed_kibana_context', () => {
+ return {
+ useKibana: jest.fn(),
+ };
+});
+
+describe('isNavControlVisible', () => {
+ it('returns true when the current app is security and the ai assistant type is default', () => {
+ (useKibana as jest.Mock).mockReturnValue({
+ services: {
+ application: {
+ currentAppId$: of('security'),
+ applications$: of(
+ new Map([
+ ['security', { id: 'security', category: { id: 'securitySolution' } }],
+ ['observability', { id: 'observability', category: { id: 'observability' } }],
+ ])
+ ),
+ },
+ aiAssistantManagementSelection: {
+ aiAssistantType$: of(AIAssistantType.Default),
+ },
+ },
+ });
+
+ const { result } = renderHook(() => useIsNavControlVisible());
+ expect(result.current.isVisible).toEqual(true);
+ });
+
+ it('returns false when the current app is observability and the ai assistant type is default', () => {
+ (useKibana as jest.Mock).mockReturnValue({
+ services: {
+ application: {
+ currentAppId$: of('observability'),
+ applications$: of(
+ new Map([['observability', { id: 'observability', category: { id: 'observability' } }]])
+ ),
+ },
+ aiAssistantManagementSelection: {
+ aiAssistantType$: of(AIAssistantType.Default),
+ },
+ },
+ });
+
+ const { result } = renderHook(() => useIsNavControlVisible());
+ expect(result.current.isVisible).toEqual(false);
+ });
+
+ it('returns false when the current app is search and the ai assistant type is default', () => {
+ (useKibana as jest.Mock).mockReturnValue({
+ services: {
+ application: {
+ currentAppId$: of('search'),
+ applications$: of(
+ new Map([['search', { id: 'search', category: { id: 'enterpriseSearch' } }]])
+ ),
+ },
+ aiAssistantManagementSelection: {
+ aiAssistantType$: of(AIAssistantType.Security),
+ },
+ },
+ });
+
+ const { result } = renderHook(() => useIsNavControlVisible());
+ expect(result.current.isVisible).toEqual(false);
+ });
+
+ it('returns false when the current app is discover and the ai assistant type is security', () => {
+ (useKibana as jest.Mock).mockReturnValue({
+ services: {
+ application: {
+ currentAppId$: of('discover'),
+ applications$: of(
+ new Map([['discover', { id: 'discover', category: { id: 'kibana' } }]])
+ ),
+ },
+ aiAssistantManagementSelection: {
+ aiAssistantType$: of(AIAssistantType.Security),
+ },
+ },
+ });
+
+ const { result } = renderHook(() => useIsNavControlVisible());
+ expect(result.current.isVisible).toEqual(true);
+ });
+
+ it('returns false when the current app is discover and the ai assistant type is observability', () => {
+ (useKibana as jest.Mock).mockReturnValue({
+ services: {
+ application: {
+ currentAppId$: of('discover'),
+ applications$: of(
+ new Map([['discover', { id: 'discover', category: { id: 'kibana' } }]])
+ ),
+ },
+ aiAssistantManagementSelection: {
+ aiAssistantType$: of(AIAssistantType.Observability),
+ },
+ },
+ });
+
+ const { result } = renderHook(() => useIsNavControlVisible());
+ expect(result.current.isVisible).toEqual(false);
+ });
+});
diff --git a/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/is_nav_control_visible/use_is_nav_control_visible.ts b/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/is_nav_control_visible/use_is_nav_control_visible.ts
index b21becbefed76..42164fecff600 100644
--- a/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/is_nav_control_visible/use_is_nav_control_visible.ts
+++ b/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/is_nav_control_visible/use_is_nav_control_visible.ts
@@ -16,12 +16,20 @@ function getVisibility(
applications: ReadonlyMap,
preferredAssistantType: AIAssistantType
) {
- // The "Global assistant" stack management setting for the security assistant still needs to be developed.
- // In the meantime, while testing, show the Security assistant everywhere except in Observability.
+ if (preferredAssistantType === AIAssistantType.Never) {
+ return false;
+ }
const categoryId =
(appId && applications.get(appId)?.category?.id) || DEFAULT_APP_CATEGORIES.kibana.id;
+ if (preferredAssistantType === AIAssistantType.Security) {
+ return (
+ DEFAULT_APP_CATEGORIES.observability.id !== categoryId &&
+ DEFAULT_APP_CATEGORIES.enterpriseSearch.id !== categoryId
+ );
+ }
+
return DEFAULT_APP_CATEGORIES.security.id === categoryId;
}
diff --git a/x-pack/solutions/security/plugins/security_solution/public/assistant/migrate_conversations_from_local_storage/use_migrate_conversation_from_local_storage.ts b/x-pack/solutions/security/plugins/security_solution/public/assistant/migrate_conversations_from_local_storage/use_migrate_conversation_from_local_storage.ts
index faaaa3dafe979..2308f6168e671 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/assistant/migrate_conversations_from_local_storage/use_migrate_conversation_from_local_storage.ts
+++ b/x-pack/solutions/security/plugins/security_solution/public/assistant/migrate_conversations_from_local_storage/use_migrate_conversation_from_local_storage.ts
@@ -43,3 +43,8 @@ export const useMigrateConversationsFromLocalStorage = () => {
storage,
]);
};
+
+export const MigrateConversationsFromLocalStorage = () => {
+ useMigrateConversationsFromLocalStorage();
+ return null;
+};
diff --git a/x-pack/solutions/security/plugins/security_solution/public/assistant/provider.tsx b/x-pack/solutions/security/plugins/security_solution/public/assistant/provider.tsx
index 7b4414dde23cc..b48fb45852081 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/assistant/provider.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/assistant/provider.tsx
@@ -11,13 +11,12 @@ import { AssistantProvider as ElasticAssistantProvider } from '@kbn/elastic-assi
import { isEmpty } from 'lodash/fp';
import useObservable from 'react-use/lib/useObservable';
import { useKibana } from '../common/lib/kibana';
-import { useAssistantAvailability } from './use_assistant_availability';
import { licenseService } from '../common/hooks/use_license';
import { useFindPromptContexts } from './content/prompt_contexts/use_find_prompt_contexts';
import { CommentActionsPortal } from './comment_actions/comment_actions_portal';
import { AugmentMessageCodeBlocksPortal } from './use_augment_message_code_blocks/augment_message_code_blocks_portal';
import { useElasticAssistantSharedStateSignalIndex } from './use_elastic_assistant_shared_state_signal_index/use_elastic_assistant_shared_state_signal_index';
-import { useMigrateConversationsFromLocalStorage } from './migrate_conversations_from_local_storage/use_migrate_conversation_from_local_storage';
+import { MigrateConversationsFromLocalStorage } from './migrate_conversations_from_local_storage/use_migrate_conversation_from_local_storage';
/**
* This component configures the Elastic AI Assistant context provider for the Security Solution app.
@@ -29,17 +28,15 @@ export const AssistantProvider: FC> = ({ children })
elasticAssistantSharedState.assistantContextValue.getAssistantContextValue$()
);
- const assistantAvailability = useAssistantAvailability();
const hasEnterpriseLicence = licenseService.isEnterprise();
- useMigrateConversationsFromLocalStorage();
useElasticAssistantSharedStateSignalIndex();
const promptContexts = useFindPromptContexts({
context: {
isAssistantEnabled:
hasEnterpriseLicence &&
- assistantAvailability.isAssistantEnabled &&
- assistantAvailability.hasAssistantPrivilege,
+ (assistantContextValue?.assistantAvailability.isAssistantEnabled ?? false) &&
+ (assistantContextValue?.assistantAvailability.hasAssistantPrivilege ?? false),
httpFetch: http.fetch,
toasts: notifications.toasts,
},
@@ -66,6 +63,7 @@ export const AssistantProvider: FC> = ({ children })
+
{children}
);
diff --git a/x-pack/solutions/security/plugins/security_solution/public/assistant/use_assistant_availability/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/assistant/use_assistant_availability/index.tsx
index e08aa8579c2ae..00118f18473c6 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/assistant/use_assistant_availability/index.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/assistant/use_assistant_availability/index.tsx
@@ -4,10 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-
-import { useLicense } from '../../common/hooks/use_license';
-import { useKibana } from '../../common/lib/kibana';
-import { ASSISTANT_FEATURE_ID, SECURITY_FEATURE_ID } from '../../../common/constants';
+import { useAssistantContext } from '@kbn/elastic-assistant';
export interface UseAssistantAvailability {
// True when searchAiLake configurations is available
@@ -27,32 +24,6 @@ export interface UseAssistantAvailability {
}
export const useAssistantAvailability = (): UseAssistantAvailability => {
- const isEnterprise = useLicense().isEnterprise();
- const capabilities = useKibana().services.application.capabilities;
- const hasAssistantPrivilege = capabilities[ASSISTANT_FEATURE_ID]?.['ai-assistant'] === true;
- const hasUpdateAIAssistantAnonymization =
- capabilities[ASSISTANT_FEATURE_ID]?.updateAIAssistantAnonymization === true;
- const hasManageGlobalKnowledgeBase =
- capabilities[ASSISTANT_FEATURE_ID]?.manageGlobalKnowledgeBaseAIAssistant === true;
- const hasSearchAILakeConfigurations = capabilities[SECURITY_FEATURE_ID]?.configurations === true;
-
- // Connectors & Actions capabilities as defined in x-pack/plugins/actions/server/feature.ts
- // `READ` ui capabilities defined as: { ui: ['show', 'execute'] }
- const hasConnectorsReadPrivilege =
- capabilities.actions?.show === true && capabilities.actions?.execute === true;
- // `ALL` ui capabilities defined as: { ui: ['show', 'execute', 'save', 'delete'] }
- const hasConnectorsAllPrivilege =
- hasConnectorsReadPrivilege &&
- capabilities.actions?.delete === true &&
- capabilities.actions?.save === true;
-
- return {
- hasSearchAILakeConfigurations,
- hasAssistantPrivilege,
- hasConnectorsAllPrivilege,
- hasConnectorsReadPrivilege,
- isAssistantEnabled: isEnterprise,
- hasUpdateAIAssistantAnonymization,
- hasManageGlobalKnowledgeBase,
- };
+ const { assistantAvailability } = useAssistantContext();
+ return assistantAvailability;
};
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/common/components/rule_execution_status/rule_status_failed_callout.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/common/components/rule_execution_status/rule_status_failed_callout.test.tsx
index c57ad6dd4d2cb..3b17180d713b5 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/common/components/rule_execution_status/rule_status_failed_callout.test.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/common/components/rule_execution_status/rule_status_failed_callout.test.tsx
@@ -47,15 +47,6 @@ const ContextWrapper: FC> = ({ children }) => {
};
describe('RuleStatusFailedCallOut', () => {
- const renderWith = (status: RuleExecutionStatus | null | undefined) =>
- render(
-
- );
const renderWithAssistant = (status: RuleExecutionStatus | null | undefined) =>
render(
@@ -64,31 +55,31 @@ describe('RuleStatusFailedCallOut', () => {
date={DATE}
message={MESSAGE}
ruleNameForChat="ruleNameForChat"
- />{' '}
+ />
);
it('is hidden if status is undefined', () => {
- const result = renderWith(undefined);
+ const result = renderWithAssistant(undefined);
expect(result.queryByTestId(TEST_ID)).toBe(null);
});
it('is hidden if status is null', () => {
- const result = renderWith(null);
+ const result = renderWithAssistant(null);
expect(result.queryByTestId(TEST_ID)).toBe(null);
});
it('is hidden if status is "going to run"', () => {
- const result = renderWith(RuleExecutionStatusEnum['going to run']);
+ const result = renderWithAssistant(RuleExecutionStatusEnum['going to run']);
expect(result.queryByTestId(TEST_ID)).toBe(null);
});
it('is hidden if status is "running"', () => {
- const result = renderWith(RuleExecutionStatusEnum.running);
+ const result = renderWithAssistant(RuleExecutionStatusEnum.running);
expect(result.queryByTestId(TEST_ID)).toBe(null);
});
it('is hidden if status is "succeeded"', () => {
- const result = renderWith(RuleExecutionStatusEnum.succeeded);
+ const result = renderWithAssistant(RuleExecutionStatusEnum.succeeded);
expect(result.queryByTestId(TEST_ID)).toBe(null);
});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx
index d16ca060a4e0f..0e2fa2c3f1c06 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx
@@ -103,7 +103,7 @@ export const RulesTableToolbar = React.memo(() => {
- {hasAssistantPrivilege && selectedRules.length > 0 && (
+ {hasAssistantPrivilege && selectedRules.length > 0 && isAssistantEnabled && (
): J
({ eui: euiDarkVars, darkMode: true })}>
-
- {children}
-
+
+
+ {children}
+
+
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.test.tsx
index 66f16c9280923..3180f40aaf6da 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.test.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.test.tsx
@@ -79,6 +79,22 @@ describe('useAssistant', () => {
expect(hookResult.result.current.promptContextId).toEqual('123');
});
+ it(`should return showAssistant false if isAssistantEnabled is false`, () => {
+ jest.mocked(useAssistantAvailability).mockReturnValue({
+ hasSearchAILakeConfigurations: false,
+ hasAssistantPrivilege: true,
+ hasConnectorsAllPrivilege: true,
+ hasConnectorsReadPrivilege: true,
+ hasUpdateAIAssistantAnonymization: true,
+ hasManageGlobalKnowledgeBase: true,
+ isAssistantEnabled: false,
+ });
+
+ hookResult = renderUseAssistant();
+
+ expect(hookResult.result.current.showAssistant).toEqual(false);
+ });
+
it(`should return showAssistant false if hasAssistantPrivilege is false`, () => {
jest.mocked(useAssistantAvailability).mockReturnValue({
hasSearchAILakeConfigurations: false,
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.ts b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.ts
index 7a3c137a396f9..0671aeff35c5e 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.ts
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.ts
@@ -107,7 +107,7 @@ export const useAssistant = ({
);
return {
- showAssistant: hasAssistantPrivilege && promptContextId !== null,
+ showAssistant: isAssistantEnabled && hasAssistantPrivilege && promptContextId !== null,
showAssistantOverlay,
promptContextId: promptContextId || '',
};
From 669eed196acea180e3a828e0d66b41131fdd5e78 Mon Sep 17 00:00:00 2001
From: Kenneth Kreindler
Date: Mon, 14 Jul 2025 11:59:12 +0100
Subject: [PATCH 2/3] i18n
---
.../packages/shared/kbn-management/settings/setting_ids/index.ts | 1 +
.../plugins/private/translations/translations/fr-FR.json | 1 -
.../plugins/private/translations/translations/ja-JP.json | 1 -
.../plugins/private/translations/translations/zh-CN.json | 1 -
4 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/platform/packages/shared/kbn-management/settings/setting_ids/index.ts b/src/platform/packages/shared/kbn-management/settings/setting_ids/index.ts
index 4769049a09c8f..0b5ff35a3422c 100644
--- a/src/platform/packages/shared/kbn-management/settings/setting_ids/index.ts
+++ b/src/platform/packages/shared/kbn-management/settings/setting_ids/index.ts
@@ -169,6 +169,7 @@ export const SECURITY_SOLUTION_ENABLE_ASSET_INVENTORY_SETTING =
'securitySolution:enableAssetInventory' as const;
export const SECURITY_SOLUTION_ENABLE_CLOUD_CONNECTOR_SETTING =
'securitySolution:enableCloudConnector' as const;
+export const AI_ASSISTANT_PREFERRED_AI_ASSISTANT_TYPE = 'aiAssistant:preferredAIAssistantType';
// Timelion settings
export const TIMELION_ES_DEFAULT_INDEX_ID = 'timelion:es.default_index';
diff --git a/x-pack/platform/plugins/private/translations/translations/fr-FR.json b/x-pack/platform/plugins/private/translations/translations/fr-FR.json
index 9f5ff85500a7c..0d36a376b57b6 100644
--- a/x-pack/platform/plugins/private/translations/translations/fr-FR.json
+++ b/x-pack/platform/plugins/private/translations/translations/fr-FR.json
@@ -96,7 +96,6 @@
"aiAssistantManagementSelection.breadcrumb.index": "Assistant d'IA",
"aiAssistantManagementSelection.featureRegistry.featureName": "Assistant d'IA",
"aiAssistantManagementSelection.managementSectionLabel": "Assistants d'IA",
- "aiAssistantManagementSelection.preferredAIAssistantTypeSettingDescription": "[version d'évaluation technique] Afficher ou non l'élément de menu Assistant d’IA dans Observability et Search, partout ou nulle part.",
"aiAssistantManagementSelection.preferredAIAssistantTypeSettingName": "Assistant d’IA de visibilité pour Observability et Search",
"aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueDefault": "Observability et Search uniquement (par défaut)",
"aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueNever": "Nulle part",
diff --git a/x-pack/platform/plugins/private/translations/translations/ja-JP.json b/x-pack/platform/plugins/private/translations/translations/ja-JP.json
index 3fb2ba5d518f6..c6136c351e5ad 100644
--- a/x-pack/platform/plugins/private/translations/translations/ja-JP.json
+++ b/x-pack/platform/plugins/private/translations/translations/ja-JP.json
@@ -96,7 +96,6 @@
"aiAssistantManagementSelection.breadcrumb.index": "AI Assistant",
"aiAssistantManagementSelection.featureRegistry.featureName": "AI Assistant",
"aiAssistantManagementSelection.managementSectionLabel": "AI Assistant",
- "aiAssistantManagementSelection.preferredAIAssistantTypeSettingDescription": "[テクニカルプレビュー]オブザーバビリティとSearchのAI Assistantメニュー項目を、どの場所でも表示するか、どの場所でも表示しないか。",
"aiAssistantManagementSelection.preferredAIAssistantTypeSettingName": "AI Assistant for Observability and Searchの表示",
"aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueDefault": "Observability and Searchのみ(デフォルト)",
"aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueNever": "なし",
diff --git a/x-pack/platform/plugins/private/translations/translations/zh-CN.json b/x-pack/platform/plugins/private/translations/translations/zh-CN.json
index 9e095727fd7e9..b13c338b97763 100644
--- a/x-pack/platform/plugins/private/translations/translations/zh-CN.json
+++ b/x-pack/platform/plugins/private/translations/translations/zh-CN.json
@@ -96,7 +96,6 @@
"aiAssistantManagementSelection.breadcrumb.index": "AI 助手",
"aiAssistantManagementSelection.featureRegistry.featureName": "AI 助手",
"aiAssistantManagementSelection.managementSectionLabel": "AI 助手",
- "aiAssistantManagementSelection.preferredAIAssistantTypeSettingDescription": "[技术预览] Observability 和 Search 中是否显示 AI 助手菜单项:随处显示或者从不显示。",
"aiAssistantManagementSelection.preferredAIAssistantTypeSettingName": "适用于 Observability 和 Search 的 AI 助手可见性",
"aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueDefault": "仅限 Observability 和 Search(默认)",
"aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueNever": "从不显示",
From 2372cf53ba1f21b9998e030f5aab386b9a048c6a Mon Sep 17 00:00:00 2001
From: Kenneth Kreindler
Date: Mon, 14 Jul 2025 14:23:24 +0100
Subject: [PATCH 3/3] allow list AI_ASSISTANT_PREFERRED_AI_ASSISTANT_TYPE
---
.../shared/serverless/settings/observability_project/index.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/platform/packages/shared/serverless/settings/observability_project/index.ts b/src/platform/packages/shared/serverless/settings/observability_project/index.ts
index e12d28de1c81b..297aeb3d87a53 100644
--- a/src/platform/packages/shared/serverless/settings/observability_project/index.ts
+++ b/src/platform/packages/shared/serverless/settings/observability_project/index.ts
@@ -29,4 +29,5 @@ export const OBSERVABILITY_PROJECT_SETTINGS = [
export const OBSERVABILITY_AI_ASSISTANT_PROJECT_SETTINGS = [
settings.OBSERVABILITY_AI_ASSISTANT_SIMULATED_FUNCTION_CALLING,
settings.OBSERVABILITY_AI_ASSISTANT_SEARCH_CONNECTOR_INDEX_PATTERN,
+ settings.AI_ASSISTANT_PREFERRED_AI_ASSISTANT_TYPE,
];