Skip to content

Commit 76d6251

Browse files
committed
agent builder nav link at the top on security solutions nav
closes #265847
1 parent 69b4a22 commit 76d6251

9 files changed

Lines changed: 87 additions & 39 deletions

File tree

src/platform/plugins/shared/navigation/common/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10-
export { DEFAULT_ROUTES, DEFAULT_ROUTE_UI_SETTING_ID } from './constants';
10+
export { DEFAULT_ROUTES, DEFAULT_ROUTE_UI_SETTING_ID, AGENT_BUILDER_NAV_AT_TOP_FLAG } from './constants';

src/platform/plugins/shared/navigation/public/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export type {
2222
AddSolutionNavigationArg,
2323
} from './types';
2424

25+
export { AGENT_BUILDER_NAV_AT_TOP_FLAG } from '../common';
26+
2527
// Export plugin after all other imports
2628
import { NavigationPublicPlugin } from './plugin';
2729
export { NavigationPublicPlugin as Plugin };

x-pack/solutions/security/plugins/security_solution_ess/public/navigation/navigation_tree.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,23 @@ import {
1414
import { i18nStrings, securityLink } from '@kbn/security-solution-navigation/links';
1515
import { defaultNavigationTree } from '@kbn/security-solution-navigation/navigation_tree';
1616
import { STACK_MANAGEMENT_NAV_ID, DATA_MANAGEMENT_NAV_ID } from '@kbn/deeplinks-management';
17+
import { AGENT_BUILDER_NAV_AT_TOP_FLAG } from '@kbn/navigation-plugin/public';
1718
import { type Services } from '../common/services';
1819
import { SOLUTION_NAME } from './translations';
1920

2021
export const createNavigationTree = (
2122
services: Services,
2223
chatExperience: AIChatExperience = AIChatExperience.Classic
2324
): NavigationTreeDefinition => {
25+
const showAgentBuilder = chatExperience === AIChatExperience.Agent;
26+
const agentBuilderNavAtTop = services.featureFlags.getBooleanValue(
27+
AGENT_BUILDER_NAV_AT_TOP_FLAG,
28+
false
29+
);
30+
const agentBuilderLink = {
31+
icon: 'productAgent',
32+
link: 'agent_builder' as AppDeepLinkId,
33+
};
2434
const showAlertingV2 = Boolean(services.application.capabilities.alertingVTwo);
2535
return {
2636
body: [
@@ -31,6 +41,7 @@ export const createNavigationTree = (
3141
renderAs: 'home',
3242
title: SOLUTION_NAME,
3343
},
44+
...(showAgentBuilder && agentBuilderNavAtTop ? [agentBuilderLink] : []),
3445
{
3546
link: 'inbox' as AppDeepLinkId,
3647
icon: 'email',
@@ -51,14 +62,8 @@ export const createNavigationTree = (
5162
{
5263
link: 'workflows',
5364
},
54-
...(chatExperience === AIChatExperience.Agent
55-
? [
56-
{
57-
icon: 'productAgent',
58-
link: 'agent_builder' as AppDeepLinkId,
59-
},
60-
]
61-
: []),
65+
// TODO: remove this item when agentBuilderNavAtTop is enabled by default and the Agent Builder link is always at the top of the nav
66+
...(showAgentBuilder && !agentBuilderNavAtTop ? [agentBuilderLink] : []),
6267
{
6368
id: SecurityPageName.attackDiscovery,
6469
icon: 'bolt',
Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
{
22
"type": "plugin",
33
"id": "@kbn/security-solution-serverless",
4-
"owner": [
5-
"@elastic/security-solution"
6-
],
4+
"owner": ["@elastic/security-solution"],
75
"group": "security",
86
"visibility": "private",
97
"description": "Serverless customizations for security.",
108
"plugin": {
119
"id": "securitySolutionServerless",
1210
"browser": true,
1311
"server": true,
14-
"configPath": [
15-
"xpack",
16-
"securitySolutionServerless"
17-
],
12+
"configPath": ["xpack", "securitySolutionServerless"],
1813
"requiredPlugins": [
1914
"kibanaReact",
2015
"management",
@@ -25,13 +20,9 @@
2520
"cloud",
2621
"fleet",
2722
"actions",
28-
"discover"
23+
"discover",
24+
"navigation"
2925
],
30-
"optionalPlugins": [
31-
"securitySolutionEss",
32-
"automaticImport",
33-
"usageApi",
34-
"workflowsManagement"
35-
]
26+
"optionalPlugins": ["securitySolutionEss", "automaticImport", "usageApi", "workflowsManagement"]
3627
}
37-
}
28+
}

x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/ai_navigation/ai_navigation_tree.test.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import { AIChatExperience } from '@kbn/ai-assistant-common';
99
import { createAiNavigationTree } from './ai_navigation_tree';
1010

1111
describe('createAiNavigationTree', () => {
12-
it('returns the Workflows link between Agents and Value report when enabled', () => {
13-
const navigationTree = createAiNavigationTree(AIChatExperience.Agent, true);
12+
it('returns the Workflows link between Agents and Value report when `workflows` is enabled', () => {
13+
const navigationTree = createAiNavigationTree(AIChatExperience.Agent, true, false, false);
1414

1515
const primaryNavSection = navigationTree.body[4];
1616
const children = 'children' in primaryNavSection ? primaryNavSection.children : [];
@@ -25,8 +25,8 @@ describe('createAiNavigationTree', () => {
2525
expect(workflowsIndex).toBe((agentsIndex ?? 0) + 1);
2626
});
2727

28-
it('does not include the Workflows link when disabled', () => {
29-
const navigationTree = createAiNavigationTree(AIChatExperience.Agent, false);
28+
it('does not include the Workflows link when `workflows` is disabled', () => {
29+
const navigationTree = createAiNavigationTree(AIChatExperience.Agent, false, false, false);
3030

3131
const primaryNavSection = navigationTree.body[4];
3232
const children = 'children' in primaryNavSection ? primaryNavSection.children : [];
@@ -37,4 +37,25 @@ describe('createAiNavigationTree', () => {
3737

3838
expect(workflowsIndex).toBe(-1);
3939
});
40+
41+
it('places the Agent Builder link after `home` when `agentBuilderNavAtTop` is enabled', () => {
42+
const navigationTree = createAiNavigationTree(AIChatExperience.Agent, true, false, true);
43+
44+
const secondNavItem = navigationTree.body[1];
45+
46+
expect('link' in secondNavItem && secondNavItem.link).toBe('agent_builder');
47+
});
48+
49+
it('places the Agent Builder later in the nav when `agentBuilderNavAtTop` is disabled', () => {
50+
const navigationTree = createAiNavigationTree(AIChatExperience.Agent, true, false, false);
51+
52+
const primaryNavSection = navigationTree.body[4];
53+
const children = 'children' in primaryNavSection ? primaryNavSection.children : [];
54+
55+
const agentBuilderIndex = children?.findIndex(
56+
(item) => 'link' in item && item.link === 'agent_builder'
57+
);
58+
59+
expect(agentBuilderIndex).toBeGreaterThan(0);
60+
});
4061
});

x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/ai_navigation/ai_navigation_tree.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ const SOLUTION_NAME = i18n.translate(
2323
export const createAiNavigationTree = (
2424
chatExperience: AIChatExperience = AIChatExperience.Classic,
2525
workflowsUiEnabled: boolean = false,
26-
showAlertingV2: boolean = false
26+
showAlertingV2: boolean = false,
27+
showAgentBuilderNavAtTop: boolean = false
2728
): NavigationTreeDefinition => ({
2829
body: [
2930
{
@@ -33,6 +34,14 @@ export const createAiNavigationTree = (
3334
icon: AiNavigationIcon,
3435
renderAs: 'home',
3536
},
37+
...(chatExperience === AIChatExperience.Agent && showAgentBuilderNavAtTop
38+
? [
39+
{
40+
icon: 'productAgent',
41+
link: 'agent_builder' as AppDeepLinkId,
42+
},
43+
]
44+
: []),
3645
{
3746
id: SecurityPageName.alertSummary,
3847
link: securityLink(SecurityPageName.alertSummary),
@@ -83,7 +92,8 @@ export const createAiNavigationTree = (
8392
link: 'discover' as AppDeepLinkId,
8493
icon: 'productDiscover',
8594
},
86-
...(chatExperience === AIChatExperience.Agent
95+
// TODO: remove this item when agentBuilderNavAtTop is enabled by default and the Agent Builder link is always at the top of the nav
96+
...(chatExperience === AIChatExperience.Agent && !showAgentBuilderNavAtTop
8797
? [
8898
{
8999
icon: 'productAgent',

x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/navigation.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ describe('Security Side Nav', () => {
7373
expect(mockedCreateAiNavigationTree).toHaveBeenCalledWith(
7474
AIChatExperience.Classic,
7575
false,
76+
false,
7677
false
7778
);
7879
expect(mockedCreateNavigationTree).not.toHaveBeenCalled();
@@ -119,6 +120,7 @@ describe('Security Side Nav', () => {
119120
expect(mockedCreateAiNavigationTree).toHaveBeenCalledWith(
120121
AIChatExperience.Classic,
121122
true,
123+
false,
122124
false
123125
);
124126
});

x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/navigation.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { firstValueFrom } from 'rxjs';
1010
import { AI_CHAT_EXPERIENCE_TYPE } from '@kbn/management-settings-ids';
1111
import type { AIChatExperience } from '@kbn/ai-assistant-common';
1212
import { WORKFLOWS_UI_SETTING_ID } from '@kbn/workflows/common/constants';
13+
import { AGENT_BUILDER_NAV_AT_TOP_FLAG } from '@kbn/navigation-plugin/public';
1314
import { ProductLine } from '../../common/product';
1415
import type { SecurityProductTypes } from '../../common/config';
1516
import { type Services } from '../common/services';
@@ -24,6 +25,11 @@ export const registerSolutionNavigation = async (
2425
(productType) => productType.product_line === ProductLine.aiSoc
2526
);
2627

28+
const agentBuilderNavAtTop = services.featureFlags.getBooleanValue(
29+
AGENT_BUILDER_NAV_AT_TOP_FLAG,
30+
false
31+
);
32+
2733
// Do not pass a defaultOverride: when userValue is unset, get() must use the registered
2834
// default (e.g. Agent for security spaces from aiAssistantManagementSelection), not Classic.
2935
const chatExperience$ = services.settings.client.get$<AIChatExperience>(AI_CHAT_EXPERIENCE_TYPE);
@@ -39,7 +45,12 @@ export const registerSolutionNavigation = async (
3945
const showAlertingV2 = Boolean(services.application.capabilities.alertingVTwo);
4046

4147
const navigationTree = shouldUseAINavigation
42-
? createAiNavigationTree(initialChatExperience, workflowsUiEnabled, showAlertingV2)
48+
? createAiNavigationTree(
49+
initialChatExperience,
50+
workflowsUiEnabled,
51+
showAlertingV2,
52+
agentBuilderNavAtTop
53+
)
4354
: await createNavigationTree(services, initialChatExperience);
4455

4556
services.securitySolution.setSolutionNavigationTree(navigationTree);

x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/navigation_tree.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
} from '@kbn/security-solution-navigation';
1616
import { i18nStrings, securityLink } from '@kbn/security-solution-navigation/links';
1717
import { defaultNavigationTree } from '@kbn/security-solution-navigation/navigation_tree';
18+
import { AGENT_BUILDER_NAV_AT_TOP_FLAG } from '@kbn/navigation-plugin/public';
1819

1920
import { type Services } from '../common/services';
2021
import { createManagementFooterItemsTree } from './management_footer_items';
@@ -29,6 +30,17 @@ export const createNavigationTree = async (
2930
chatExperience: AIChatExperience = AIChatExperience.Classic
3031
): Promise<NavigationTreeDefinition> => {
3132
const showAlertingV2 = Boolean(services.application.capabilities.alertingVTwo);
33+
34+
const showAgentBuilder = chatExperience === AIChatExperience.Agent;
35+
const agentBuilderNavAtTop = services.featureFlags.getBooleanValue(
36+
AGENT_BUILDER_NAV_AT_TOP_FLAG,
37+
false
38+
);
39+
const agentBuilderLink = {
40+
icon: 'productAgent',
41+
link: 'agent_builder' as AppDeepLinkId,
42+
};
43+
3244
return {
3345
body: [
3446
{
@@ -38,6 +50,7 @@ export const createNavigationTree = async (
3850
icon: 'logoSecurity',
3951
renderAs: 'home',
4052
},
53+
...(showAgentBuilder && agentBuilderNavAtTop ? [agentBuilderLink] : []),
4154
{
4255
link: 'inbox' as AppDeepLinkId,
4356
icon: 'email',
@@ -58,14 +71,7 @@ export const createNavigationTree = async (
5871
{
5972
link: 'workflows',
6073
},
61-
...(chatExperience === AIChatExperience.Agent
62-
? [
63-
{
64-
icon: 'productAgent',
65-
link: 'agent_builder' as AppDeepLinkId,
66-
},
67-
]
68-
: []),
74+
...(showAgentBuilder && !agentBuilderNavAtTop ? [agentBuilderLink] : []),
6975
{
7076
id: SecurityPageName.attackDiscovery,
7177
icon: 'bolt',

0 commit comments

Comments
 (0)