[Security Solution] Bulk add alerts to Agent Builder chat#270904
[Security Solution] Bulk add alerts to Agent Builder chat#270904jonwalstedt wants to merge 13 commits into
Conversation
55dad5f to
15ae981
Compare
|
Pinging @elastic/security-threat-hunting (Team:Threat Hunting) |
fake-haris
left a comment
There was a problem hiding this comment.
appex-qa changes LGTM
tcalopes
left a comment
There was a problem hiding this comment.
entity analytics changes LGTM
1cc06fe to
78ba2f0
Compare
ymao1
left a comment
There was a problem hiding this comment.
Entity analytics changes LGTM. Code review only
7759a86 to
aeec2c7
Compare
3991efe to
ebf8a14
Compare
| }, | ||
| output: { | ||
| expected: | ||
| 'The response should prioritise the critical PowerShell alert on workstation-42, ' + |
There was a problem hiding this comment.
This says "critical PowerShell alert on workstation-42", but the generated critical alert (index 49) is "Memory Injection via Process Hollowing" on laptop-finance-07. Doesn't affect scoring (criteria don't reference it), but it's the reference shown in results — worth correcting so it's not misleading.
There was a problem hiding this comment.
Fixed in 486a151. Updated the expected string to match what index 49 actually generates: "Memory Injection via Process Hollowing" on laptop-finance-07. Also corrected the paired high-severity reference from "network-scanning" (medium) to "lateral-movement" (high, index 1).
|
|
||
| const attachments = metadata?.attachments ?? []; | ||
|
|
||
| const raw = (await fetch('/api/agent_builder/converse', { |
There was a problem hiding this comment.
This converse task is duplicated across both specs. Other eval suites (e.g. entity-analytics) keep it in src/. Worth extracting a shared helper.
There was a problem hiding this comment.
Fixed in 486a151. Extracted the fetch('/api/agent_builder/converse', …) block to src/converse_task.ts as a shared callConverse helper. Both specs now call callConverse({ fetch, connectorId, question, attachments, log }). While at it, also extracted the AttachmentReadCompliance CODE evaluator to src/evaluators.ts so it can be shared between both specs as well (the e2e scenario added to alert_triage_quality.spec.ts needs it too).
| connector, | ||
| evaluators, | ||
| executorClient, | ||
| traceEsClient, |
There was a problem hiding this comment.
traceEsClient is threaded through but never used here. Remove?
There was a problem hiding this comment.
Fixed in 486a151. Removed traceEsClient from the createEvaluateAlertBatches parameter type, function destructuring, and fixture call. Also dropped the now-unused EsClient import.
|
Some comments from evals point of view. The suite registers The actual shipped path — bulk → summary mode → attachment_read → triage real alerts — has no end-to-end coverage. No grounding / anti-hallucination criterion. Both datasets only check positive recall ("mentions a critical alert", "names web-server-01"). With 16 real hostnames + procedural rule names, an invented host/rule would pass silently. |
Scout Best Practices ReviewIn scope: 🚨 Blocker
|
js-jankisalvi
left a comment
There was a problem hiding this comment.
Verified response-ops related changes and added few comments, thanks.
|
@MadameSheema Thanks for the Scout review — all points addressed in 1c4e3ad: Blocker — page objects
Major — locator quality
Minor — tags: Added comment: // Serverless excluded: agent builder feature not yet available on serverless Minor — cleanup: Added Minor — shared role: Extracted Nit — connector name: Scoped to |
js-jankisalvi
left a comment
There was a problem hiding this comment.
Verified locally, Add to chat bulk action only appears on security solution alerts table. Response ops changes look good 👍
…stem (elastic#270903) # Summary This PR is extracted from [elastic#270286](elastic#270286) as the first of two stacked PRs. This first PR contains the needed changes in Agent Builder to support adding several attachments as a single batch to the chat. This followup [PR](elastic#270904) adds the possibility bulk add Alerts to the chat. ## What This PR introduces `AttachmentGroup` as a client-side grouping primitive and `group_id` as a first-class optional field on `Attachment`, `VersionedAttachment`, and `AttachmentInput`. The group concept is dissolved at the `flattenAttachments` serialization boundary — individual items are stamped with `group_id` — and the full pipeline (server routes, state manager, agent execution) threads the field through to persistence and presentation. ## Why Enables consumers (e.g. the Security Solution bulk-alerts feature) to attach a batch of items as a single logical group, have them deduplicated to one chip in chat history, and removed atomically. The [follow-up PR](elastic#270904) shows the first concrete use: bulk-adding alerts to chat. Without this platform layer, each consumer would have to roll its own grouping and dedup logic. Per-attachment `maxContentLength` is introduced at the same time because bulk alert documents (full ES source) can be large. A single global truncation limit would either under-truncate small attachments or over-truncate large ones. Delegating the limit to the attachment type registration lets each type declare its own budget independently. Closes: elastic/security-team#17544 Epic: elastic/security-team#17311 ## Changes **Types and contracts** - `AttachmentGroup` type + `isAttachmentGroup` predicate added to `versioned_attachment.ts` - `group_id` and `description` optional fields added to `Attachment`, `AttachmentInput`, and `VersionedAttachment` - `plugin_contract.ts` exports updated to include `AttachmentGroup` - `maxContentLength?: number` added to `AttachmentTypeDefinition` — per-type inline truncation limit (characters), defaults to 10 000 if absent. **Client-side logic** - `flatten_attachments.ts` — new canonical serialization boundary; stamps `group_id` and `description` on group items - `remove_attachment_from_list.ts` — handles group-id-based bulk removal (removes all items sharing a `group_id`) - `upsert_attachments_into_list.ts` — updated to handle `AttachmentGroup` alongside `AttachmentInput` - `build_optimistic_attachments.ts` — preserves `group_id` and `description` on fallback attachments **UI** - `attachment_group_pill.tsx` — new chip component for rendering an `AttachmentGroup` in the input row (same visual style as `AttachmentPill`) - `attachment_pills_row.tsx` / `conversation_input.tsx` — wired to render `AttachmentGroupPill` for groups - `round_attachment_references.tsx` — deduplicates attachment refs by `group_id` (actor-filter check now correctly runs before the `seenGroupIds` slot is consumed) **Server pipeline** - `chat.ts` — accepts and forwards `group_id` / `description` from request body - `validate_attachment.ts` — passes `group_id` and `description` through to state manager - `attachment_state_manager.ts` — persists `group_id` to `VersionedAttachment` - `prepare_conversation.ts` / `attachment_presentation.ts` — threads `group_id` and per-attachment `maxContentLength` through to agent execution - `attachment_presentation.ts` — replaces the flat `maxContentLength` config option with `resolveMaxContentLength?: (attachment) => number | undefined`, a per-attachment resolver. `prepare_conversation.ts` wires this to `attachmentsService.getTypeDefinition(type)?.maxContentLength` so any attachment type can declare its own inline truncation limit via its type registration. - XML attribute `id` renamed to `attachment_id` in inline and summary attachment nodes, and in the LLM instructions, to remove ambiguity. **Tests** - New: `flatten_attachments.test.ts`, `remove_attachment_from_list.test.ts`, `versioned_attachment.test.ts`, `round_attachment_references.test.tsx` - Updated: `upsert_attachments_into_list.test.ts`, `build_optimistic_attachments.test.ts`, `attachment_presentation.test.ts` ### Checklist - [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) - [ ] [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 - [ ] 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) - [ ] 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. - [ ] [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 - **Data loss / corruption:** Low. `group_id` and `description` are optional fields appended to `VersionedAttachment`. Existing saved conversations have neither field; all read paths guard on `!== undefined`, so no migration is needed and existing data is unaffected. - **Regressions:** Low. `AttachmentGroup` has no producers until the follow-up security PR lands. All existing `AttachmentInput` paths are unchanged. The `flattenAttachments` function is additive — it handles both plain `AttachmentInput` and `AttachmentGroup` and existing call sites have been updated. - **Performance:** Negligible. The only new overhead is a `Set`-based group-id dedup during `RoundAttachmentReferences` rendering. - **Hard-to-test:** The actor-filter / `seenGroupIds` ordering edge case (filter must run before consuming the Set slot) is subtle but is now covered by a dedicated test. ### Release note > No user-facing changes. **Suggested label:** `release_note:skip` --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
|
Thanks for the review @enriquesanchez-elastic, I've addressed your feedback, could you please take another look when you have a chance? |
Adds a "Add to chat" bulk action to the Kibana alerts table toolbar, allowing analysts to select multiple security alerts and send them to the Agent Builder AI chat in a single click. - security.alerts attachment type with server-side ES fetch (space-scoped) - alertsToAttachmentGroup helper chunks selections into batches of 20 - Bulk action wired across all 4 surfaces: Alerts page, Cases, Rule Details, and Attack Discovery - disableOnQuery: true prevents use with "Select all N" query mode - alert_count EBT telemetry event - Scout E2E test and kbn-evals-suite-security-alert-triage eval package Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Addresses CodeQL unbounded-string alert: adds .max(512) to the z.string() inside the alertIds array schema (ES _id values are at most 512 bytes). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nt_builder/attachment_types/index.ts Update casing in copy Co-authored-by: Florent LB <florent.leborgne@elastic.co>
…nt_builder/attachment_types/index.ts Update casing in copy Co-authored-by: Florent LB <florent.leborgne@elastic.co>
…eedback
js-jankisalvi:
- Move ConversationAttachmentInput, OpenChatService, BulkAddToChatConfig
exports below the last import in types.ts so all imports are contiguous
- Simplify ConversationAttachmentInput to interface { type: string } —
the payload is only passed through to openChat so response-ops doesn't
need to mirror the attachment schema; Record<string, unknown> &
{ type: string } was tried but breaks TypeScript index-signature
compatibility for concrete types like AttachmentGroup
- Drop the redundant agentBuilderService guard in useBulkActions;
useBulkAddToChatActions already returns [] when the service is absent
MadameSheema (Scout best-practices review):
- Add waitForRuleAlert() and checkAlertRowCheckbox() to AlertsTablePage,
eliminating inline locator chains and the .euiCheckbox__input class
selector from the spec
- Create AgentBuilderPage page object with conversation, attachmentPillsRow,
inputEditor, and submitButton locators; register it in SecurityPageObjects
- Export CUSTOM_QUERY_RULE from @kbn/scout-security public barrel; both
specs now import from '@kbn/scout-security' instead of the internal
subpath
- Extract FULL_KIBANA_SECURITY_ROLE to test/scout/ui/common/roles.ts and
import it in both specs, replacing the duplicate role definitions
- Add defensive pre-test cleanup at the top of beforeEach and
scoutSpace.savedObjects.cleanStandardList() to afterEach
- Add comment explaining serverless tag exclusion
- Scope connector name to scoutSpace.id in the llmProxy fixture
Refs elastic#17496
… 30s The attachment pills row renders one React context propagation cycle after the outer conversation div becomes visible, so the default 10s timeout is not reliable in slower CI environments. Adds waitForAttachmentPillsRow() to AgentBuilderPage with an explicit 30s timeout. Refs elastic#17496
The kbn-ftr-llm-proxy 30s timeout starts at intercept() call time. When interceptors were registered at test start, slow CI steps (waitForRuleAlert up to 60s, waitForAttachmentPillsRow up to 30s) could exhaust the timeout before the submit button was ever clicked, producing a spurious "Interceptor set_title timed out" failure. Moving the registration to immediately before submitButton.click() is safe because autoSendInitialMessage: false guarantees no LLM call fires before the user submits. Refs elastic#17496
Registers the suite alongside the other security evals so it runs automatically in the weekly pipeline against the core model matrix. Refs elastic#17496
…nnector cleanStandardList() deletes saved objects of type 'action', which includes the .gen-ai connector created by the worker-scoped llmProxy fixture. Calling it in beforeEach destroyed the connector before the test ran, making the bulk 'Add to chat' action invisible and causing every subsequent step to fail. The connector lifecycle is owned by llmProxy; this test only needs targeted deleteAll() calls for the objects it creates. Refs elastic#17496
3219a16 to
8589d10
Compare
💔 Build Failed
Failed CI Steps
Test Failures
Metrics [docs]Module Count
Async chunks
Page load bundle
History
cc @jonwalstedt |
Summary
This PR is extracted from #270286 as the second of two stacked PRs. The first PR contains the needed changes in Agent Builder to support adding several attachments as a single batch to the chat. This followup adds the possibility bulk add Alerts to the chat.
What
Adds an "Add to chat" bulk action to the Kibana alerts table toolbar. Analysts can select up to 20 alerts and send them to the Agent Builder AI chat in a single click, where the agent receives the full alert details and can triage them.
Why
Security analysts frequently need to investigate multiple related alerts together. Switching between the alerts table and the AI chat to copy alert IDs manually is slow and error-prone. This feature closes that gap by making alert context available in chat at the point of investigation.
Reviewers: to see only the security changes (without the agent-builder foundation), use this diff.
Closes https://github.com/elastic/security-team/issues/17496
Epic: https://github.com/elastic/security-team/issues/17311
Test plan available here
bulk-add-alerts2.mov
Changes
Attachment type
security.alertsattachment type — server-side ES fetch scoped to the active space, returns full alert source for each selected IDget_alerts_by_idtool — fetches alert documents by ID for the agent to reason overregister_attachments.tsupdated to register the new typeClient-side helpers
alertsToAttachmentGrouphelper — chunks selected alert IDs intoAttachmentGroupbatches (max 20 per group)helpers.tsx/helpers.test.tsx— updated to generate the group label and build attachment groups from alert selectionsBulk action wiring (4 surfaces)
x-pack/packages/response-ops/alerts-table— newaddToChatbulk action inuse_bulk_actions.ts;disableOnQuery: trueprevents use with "Select all N" query modedetections/components/alerts_table)cases/components/ease/table.tsx)tabs/alerts_tab/ease/table.tsx)components/alert_summary/table/table.tsx)Telemetry
alert_countEBT event added touse_report_add_to_chat.tsTests
use_bulk_actions.test.tsx— updated with add-to-chat casestable_bulk_add_to_chat.test.tsx×4 — one per surfacealerts.test.ts— attachment type server logicget_alerts_by_id.test.ts— tool logicbulk_add_alerts_to_chat.spec.ts— Scout E2E testkbn-evals-suite-security-alert-triage— new eval package withalert_triage_quality.spec.tsandbulk_alerts_attachment_read.spec.tsInfrastructure
.buildkite/pipelines/evals/evals.suites.json— registers the new eval suite.github/CODEOWNERS— ownership for the new eval packagetsconfig.base.json,package.json,yarn.lock— new package wiringChecklist
release_note:breakinglabel should be applied in these situations.release_note:*label is applied per the guidelinesbackport:*labels.Identify risks
isAgentBuilderEnabledso it is invisible in environments without the feature flag.disableOnQuery: trueprevents accidental use with "Select all N" (unbounded) query mode, which would otherwise silently truncate to 20 alerts.use_bulk_actions.tsin response-ops are additive. Each of the 4 wired surfaces has a dedicated test. The alerts-table package is shared across solutions, but the new action is conditionally rendered.AttachmentGroupwhich is flattened before being sent to the LLM.Release note
Suggested label:
release_note:feature