From be3ad5054b78d4cf238900a7d291df5e6f1d4d9d Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Mon, 1 Sep 2025 13:59:54 +0200 Subject: [PATCH 1/3] Add agent event sections --- .../IncidentDetails/AgentSummary/index.tsx | 76 +++++++++++---- .../IncidentDetails/AgentSummary/styles.ts | 6 ++ .../AgentEvent => }/Accordion/index.tsx | 15 ++- .../AgentEvent => }/Accordion/styles.ts | 4 +- .../AgentEvent => }/Accordion/types.ts | 1 + .../common/AgentChat/AgentChat.stories.tsx | 40 +------- .../Agentic/common/AgentChat/index.tsx | 4 +- .../MarkdownRenderer.stories.tsx | 2 +- .../TypingMarkdown/MarkdownRenderer/index.tsx | 0 .../TypingMarkdown/MarkdownRenderer/styles.ts | 0 .../TypingMarkdown/MarkdownRenderer/types.ts | 0 .../AgentEvent/TypingMarkdown/index.tsx | 0 .../AgentEvent/TypingMarkdown/types.ts | 0 .../AgentEvent/index.tsx | 2 +- .../AgentEvent/styles.ts | 0 .../AgentEvent/types.ts | 0 .../index.tsx | 6 +- .../Agentic/common/AgentEventList/mockData.ts | 70 ++++++++++++++ .../types.ts | 2 +- .../AgentEventSection.stories.tsx | 92 +++++++++++++++++++ .../common/AgentEventSection/index.tsx | 78 ++++++++++++++++ .../common/AgentEventSection/styles.ts | 61 ++++++++++++ .../Agentic/common/AgentEventSection/types.ts | 21 +++++ src/redux/services/types.ts | 8 ++ 24 files changed, 418 insertions(+), 70 deletions(-) rename src/components/Agentic/common/{AgentEventsList/AgentEvent => }/Accordion/index.tsx (57%) rename src/components/Agentic/common/{AgentEventsList/AgentEvent => }/Accordion/styles.ts (76%) rename src/components/Agentic/common/{AgentEventsList/AgentEvent => }/Accordion/types.ts (84%) rename src/components/Agentic/common/{AgentEventsList => AgentEventList}/AgentEvent/TypingMarkdown/MarkdownRenderer/MarkdownRenderer.stories.tsx (97%) rename src/components/Agentic/common/{AgentEventsList => AgentEventList}/AgentEvent/TypingMarkdown/MarkdownRenderer/index.tsx (100%) rename src/components/Agentic/common/{AgentEventsList => AgentEventList}/AgentEvent/TypingMarkdown/MarkdownRenderer/styles.ts (100%) rename src/components/Agentic/common/{AgentEventsList => AgentEventList}/AgentEvent/TypingMarkdown/MarkdownRenderer/types.ts (100%) rename src/components/Agentic/common/{AgentEventsList => AgentEventList}/AgentEvent/TypingMarkdown/index.tsx (100%) rename src/components/Agentic/common/{AgentEventsList => AgentEventList}/AgentEvent/TypingMarkdown/types.ts (100%) rename src/components/Agentic/common/{AgentEventsList => AgentEventList}/AgentEvent/index.tsx (97%) rename src/components/Agentic/common/{AgentEventsList => AgentEventList}/AgentEvent/styles.ts (100%) rename src/components/Agentic/common/{AgentEventsList => AgentEventList}/AgentEvent/types.ts (100%) rename src/components/Agentic/common/{AgentEventsList => AgentEventList}/index.tsx (95%) create mode 100644 src/components/Agentic/common/AgentEventList/mockData.ts rename src/components/Agentic/common/{AgentEventsList => AgentEventList}/types.ts (86%) create mode 100644 src/components/Agentic/common/AgentEventSection/AgentEventSection.stories.tsx create mode 100644 src/components/Agentic/common/AgentEventSection/index.tsx create mode 100644 src/components/Agentic/common/AgentEventSection/styles.ts create mode 100644 src/components/Agentic/common/AgentEventSection/types.ts diff --git a/src/components/Agentic/IncidentDetails/AgentSummary/index.tsx b/src/components/Agentic/IncidentDetails/AgentSummary/index.tsx index f46926fc2..360e21e93 100644 --- a/src/components/Agentic/IncidentDetails/AgentSummary/index.tsx +++ b/src/components/Agentic/IncidentDetails/AgentSummary/index.tsx @@ -1,24 +1,33 @@ import { useMemo } from "react"; import { useParams } from "react-router"; import { useStableSearchParams } from "../../../../hooks/useStableSearchParams"; -import { - useGetIncidentAgentEventsQuery, - useGetIncidentAgentsQuery -} from "../../../../redux/services/digma"; +import { useGetIncidentAgentsQuery } from "../../../../redux/services/digma"; +import type { + AgentStatus, + IncidentAgentEvent +} from "../../../../redux/services/types"; +import { groupBy } from "../../../../utils/groupBy"; import { ThreeCirclesSpinner } from "../../../common/ThreeCirclesSpinner"; import { Spinner } from "../../../common/v3/Spinner"; -import { AgentEventsList } from "../../common/AgentEventsList"; -import { useAutoScroll } from "../useAutoScroll"; +import { mockedAgentEvents } from "../../common/AgentEventList/mockData"; +import { AgentEventSection } from "../../common/AgentEventSection"; +import type { AgentEventSectionType } from "../../common/AgentEventSection/types"; import * as s from "./styles"; const REFRESH_INTERVAL = 10 * 1000; // in milliseconds +const agentEventsData: IncidentAgentEvent[] = mockedAgentEvents.filter( + (event) => event.type !== "human" +); + +const isLoading = false; + export const AgentSummary = () => { const params = useParams(); const incidentId = params.id; const [searchParams] = useStableSearchParams(); const agentId = searchParams.get("agent"); - const { elementRef, handleElementScroll } = useAutoScroll(); + // const { elementRef, handleElementScroll } = useAutoScroll(); const { data: agentsData } = useGetIncidentAgentsQuery( { id: incidentId ?? "" }, @@ -28,13 +37,13 @@ export const AgentSummary = () => { } ); - const { data: agentEventsData, isLoading } = useGetIncidentAgentEventsQuery( - { incidentId: incidentId ?? "", agentId: agentId ?? "" }, - { - pollingInterval: REFRESH_INTERVAL, - skip: !incidentId || !agentId - } - ); + // const { data: agentEventsData, isLoading } = useGetIncidentAgentEventsQuery( + // { incidentId: incidentId ?? "", agentId: agentId ?? "" }, + // { + // pollingInterval: REFRESH_INTERVAL, + // skip: !incidentId || !agentId + // } + // ); const isAgentRunning = useMemo( () => @@ -44,15 +53,50 @@ export const AgentSummary = () => { [agentsData, agentId] ); + const sections: { + id: string; + name: string; + description: string; + status: AgentStatus; + events: IncidentAgentEvent[]; + type: AgentEventSectionType | undefined; + }[] = useMemo(() => { + return Object.entries( + groupBy(agentEventsData, (event) => event.section?.id ?? "__ungrouped") + ).map(([sectionId, sectionEvents], i) => ({ + id: sectionId, + // TODO: get section metadata from corresponding API endpoint + name: sectionEvents[0].section?.name ?? sectionId, + description: sectionEvents[0].section?.description ?? "", + status: sectionEvents[0].section?.status ?? "waiting", + events: sectionEvents, + type: + i === 0 + ? "intro" + : i === agentEventsData.length - 1 + ? "summary" + : undefined + })); + }, [agentEventsData]); + return ( - + {!agentEventsData && isLoading && ( )} {agentEventsData && ( - + + {sections.map((section, i) => ( + + ))} + )} {isAgentRunning && } diff --git a/src/components/Agentic/IncidentDetails/AgentSummary/styles.ts b/src/components/Agentic/IncidentDetails/AgentSummary/styles.ts index 76d904022..e404a3a84 100644 --- a/src/components/Agentic/IncidentDetails/AgentSummary/styles.ts +++ b/src/components/Agentic/IncidentDetails/AgentSummary/styles.ts @@ -33,3 +33,9 @@ export const ToolSummary = styled.summary` export const ToolContent = styled.div` padding: 16px; `; + +export const EventsContainer = styled.div` + display: flex; + flex-direction: column; + gap: 8px; +`; diff --git a/src/components/Agentic/common/AgentEventsList/AgentEvent/Accordion/index.tsx b/src/components/Agentic/common/Accordion/index.tsx similarity index 57% rename from src/components/Agentic/common/AgentEventsList/AgentEvent/Accordion/index.tsx rename to src/components/Agentic/common/Accordion/index.tsx index e222e1dd5..80396f5bd 100644 --- a/src/components/Agentic/common/AgentEventsList/AgentEvent/Accordion/index.tsx +++ b/src/components/Agentic/common/Accordion/index.tsx @@ -1,10 +1,13 @@ -import { useState } from "react"; -import { ChevronIcon } from "../../../../../common/icons/16px/ChevronIcon"; -import { Direction } from "../../../../../common/icons/types"; +import { forwardRef, useState, type ForwardedRef } from "react"; +import { ChevronIcon } from "../../../common/icons/16px/ChevronIcon"; +import { Direction } from "../../../common/icons/types"; import * as s from "./styles"; import type { AccordionProps } from "./types"; -export const Accordion = ({ summary, content }: AccordionProps) => { +export const AccordionComponent = ( + { summary, content, className }: AccordionProps, + ref: ForwardedRef +) => { const [isOpen, setIsOpen] = useState(false); const handleSummaryClick = () => { @@ -12,7 +15,7 @@ export const Accordion = ({ summary, content }: AccordionProps) => { }; return ( - + { ); }; + +export const Accordion = forwardRef(AccordionComponent); diff --git a/src/components/Agentic/common/AgentEventsList/AgentEvent/Accordion/styles.ts b/src/components/Agentic/common/Accordion/styles.ts similarity index 76% rename from src/components/Agentic/common/AgentEventsList/AgentEvent/Accordion/styles.ts rename to src/components/Agentic/common/Accordion/styles.ts index 1121f430d..ab8958bf1 100644 --- a/src/components/Agentic/common/AgentEventsList/AgentEvent/Accordion/styles.ts +++ b/src/components/Agentic/common/Accordion/styles.ts @@ -1,14 +1,14 @@ import styled from "styled-components"; export const Container = styled.div` - border: 1px solid ${({ theme }) => theme.colors.v3.stroke.primary}; + border: 1px solid ${({ theme }) => theme.colors.v3.stroke.dark}; border-radius: 8px; flex-shrink: 0; overflow: hidden; `; export const Summary = styled.div` - background-color: ${({ theme }) => theme.colors.v3.surface.secondary}; + background: ${({ theme }) => theme.colors.v3.surface.secondary}; color: ${({ theme }) => theme.colors.v3.text.primary}; padding: 8px; cursor: pointer; diff --git a/src/components/Agentic/common/AgentEventsList/AgentEvent/Accordion/types.ts b/src/components/Agentic/common/Accordion/types.ts similarity index 84% rename from src/components/Agentic/common/AgentEventsList/AgentEvent/Accordion/types.ts rename to src/components/Agentic/common/Accordion/types.ts index 0146d33fb..83fc32311 100644 --- a/src/components/Agentic/common/AgentEventsList/AgentEvent/Accordion/types.ts +++ b/src/components/Agentic/common/Accordion/types.ts @@ -3,4 +3,5 @@ import type { ReactNode } from "react"; export interface AccordionProps { summary: ReactNode; content: ReactNode; + className?: string; } diff --git a/src/components/Agentic/common/AgentChat/AgentChat.stories.tsx b/src/components/Agentic/common/AgentChat/AgentChat.stories.tsx index 9f2d889e6..7a9773fb5 100644 --- a/src/components/Agentic/common/AgentChat/AgentChat.stories.tsx +++ b/src/components/Agentic/common/AgentChat/AgentChat.stories.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from "react"; import { fn } from "storybook/test"; import { AgentChat } from "."; import type { IncidentAgentEvent } from "../../../../redux/services/types"; +import { mockedAgentEvents } from "../AgentEventList/mockData"; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { @@ -19,45 +20,6 @@ export default meta; type Story = StoryObj; // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args -const mockedAgentEvents: IncidentAgentEvent[] = [ - { - id: "1", - type: "human", - message: "Can you help me understand why my API response time is slow?", - agent_name: "user" - }, - { - id: "2", - type: "token", - message: "Let me analyze your application's performance data...", - agent_name: "agent" - }, - { - id: "3", - type: "token", - message: - "I've found several performance issues in your application:\n\n1. **Database Query Optimization**: Your user lookup queries are taking an average of 2.3 seconds\n2. **Memory Usage**: High memory allocation in the user service\n3. **Cache Misses**: 78% cache miss rate on user data\n\nWould you like me to suggest specific optimizations for any of these areas?", - agent_name: "agent" - }, - { - id: "4", - type: "tool", - agent_name: "agent", - message: - '\n```json\n{\n "success": false,\n "blockers": "Limited tool access prevents thorough investigation of system-wide issues, infrastructure problems, service mesh configurations, and external dependencies. Need access to Kubernetes API, service mesh telemetry, network monitoring tools, and container logs.",\n "result": {\n "is_relevant": true,\n "objective_success": false,\n "blockers": "Limited tool access prevents thorough investigation of system-wide issues, infrastructure problems, service mesh configurations, and external dependencies. Need access to Kubernetes API, service mesh telemetry, network monitoring tools, and container logs.",\n "beyond_the_result": {\n "summary": "Unable to fully investigate alternative causes due to tool limitations, but analysis suggests potential issues in service mesh, network policies, or external dependencies",\n "description": "The investigation revealed a severe performance degradation (2650%) in the PipelineConnector Execute operation that has been ongoing for over 24 hours. While direct investigation was limited by tool access, the pattern and severity suggest potential issues with service mesh routing, network policies, cross-namespace communication, or external service dependencies rather than simple resource constraints.",\n "confidence_level": "30",\n "confidence_level_reason": "Limited tool access prevents thorough investigation of infrastructure and network-related causes. The assessment is based primarily on timing patterns and service impact analysis rather than direct evidence."\n },\n "next_steps_suggestions": "1. Request access to Kubernetes cluster information and API\\n2. Obtain access to service mesh telemetry and dashboard\\n3. Deploy network monitoring tools\\n4. Enable access to container logs\\n5. Once access is granted, conduct thorough analysis of namespace configurations, service mesh settings, network policies, and external service dependencies",\n "actions_taken": [\n {\n "action": "Gathered relevant objects",\n "action_execution_success": true,\n "action_command": "list_relevant_incident_objects",\n "resolution_success_status": "PARTIAL",\n "resolution_explanation": "Successfully identified the critical trace ID but couldn\'t gather infrastructure-related objects",\n "resolution_success_evidence": "Retrieved trace ID FB0C56FA98816BBBFBB934CCEDEA72E4 showing the performance degradation",\n "state_changes_confirmed_due_to_actions": "Confirmed existence of trace showing 2650% performance degradation in PipelineConnector Execute operation"\n },\n {\n "action": "Tracked relevant trace",\n "action_execution_success": true,\n "action_command": "track_incident_relevant_object",\n "resolution_success_status": "PARTIAL",\n "resolution_explanation": "Successfully tracked the critical trace ID for future reference",\n "resolution_success_evidence": "Trace ID FB0C56FA98816BBBFBB934CCEDEA72E4 was successfully tracked",\n "state_changes_confirmed_due_to_actions": "Added trace to tracked objects for future investigation"\n }\n ]\n }\n}\n```\n', - tool_name: "kubernetes_resolution_expert_tool", - mcp_name: "", - status: "success" - }, - { - id: "5", - type: "token", - message: - "Here are my recommendations for optimizing your database queries:\n\n```sql\n-- Add an index on the email column\nCREATE INDEX idx_users_email ON users(email);\n\n-- Use prepared statements\nSELECT id, name, email FROM users WHERE email = ?\n```\n\nThis should reduce your query time from 2.3s to under 100ms.", - agent_name: "agent" - } -]; - const EVENTS_CURSOR = 2; const EVENTS_TIMEOUT = 2000; diff --git a/src/components/Agentic/common/AgentChat/index.tsx b/src/components/Agentic/common/AgentChat/index.tsx index 40258109d..86defcf25 100644 --- a/src/components/Agentic/common/AgentChat/index.tsx +++ b/src/components/Agentic/common/AgentChat/index.tsx @@ -1,7 +1,7 @@ import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent"; import { Chat } from "../../common/Chat"; import { trackingEvents } from "../../tracking"; -import { AgentEventsList } from "../AgentEventsList"; +import { AgentEventList } from "../AgentEventList"; import type { AgentChatProps } from "./types"; export const AgentChat = ({ @@ -42,7 +42,7 @@ export const AgentChat = ({ chatContent={ <> {data && ( - = { title: - "Agentic/common/AgentEventsList/AgentEvent/TypingMarkdown/MarkdownRenderer", + "Agentic/common/AgentEventList/AgentEvent/TypingMarkdown/MarkdownRenderer", component: MarkdownRenderer, parameters: { // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout diff --git a/src/components/Agentic/common/AgentEventsList/AgentEvent/TypingMarkdown/MarkdownRenderer/index.tsx b/src/components/Agentic/common/AgentEventList/AgentEvent/TypingMarkdown/MarkdownRenderer/index.tsx similarity index 100% rename from src/components/Agentic/common/AgentEventsList/AgentEvent/TypingMarkdown/MarkdownRenderer/index.tsx rename to src/components/Agentic/common/AgentEventList/AgentEvent/TypingMarkdown/MarkdownRenderer/index.tsx diff --git a/src/components/Agentic/common/AgentEventsList/AgentEvent/TypingMarkdown/MarkdownRenderer/styles.ts b/src/components/Agentic/common/AgentEventList/AgentEvent/TypingMarkdown/MarkdownRenderer/styles.ts similarity index 100% rename from src/components/Agentic/common/AgentEventsList/AgentEvent/TypingMarkdown/MarkdownRenderer/styles.ts rename to src/components/Agentic/common/AgentEventList/AgentEvent/TypingMarkdown/MarkdownRenderer/styles.ts diff --git a/src/components/Agentic/common/AgentEventsList/AgentEvent/TypingMarkdown/MarkdownRenderer/types.ts b/src/components/Agentic/common/AgentEventList/AgentEvent/TypingMarkdown/MarkdownRenderer/types.ts similarity index 100% rename from src/components/Agentic/common/AgentEventsList/AgentEvent/TypingMarkdown/MarkdownRenderer/types.ts rename to src/components/Agentic/common/AgentEventList/AgentEvent/TypingMarkdown/MarkdownRenderer/types.ts diff --git a/src/components/Agentic/common/AgentEventsList/AgentEvent/TypingMarkdown/index.tsx b/src/components/Agentic/common/AgentEventList/AgentEvent/TypingMarkdown/index.tsx similarity index 100% rename from src/components/Agentic/common/AgentEventsList/AgentEvent/TypingMarkdown/index.tsx rename to src/components/Agentic/common/AgentEventList/AgentEvent/TypingMarkdown/index.tsx diff --git a/src/components/Agentic/common/AgentEventsList/AgentEvent/TypingMarkdown/types.ts b/src/components/Agentic/common/AgentEventList/AgentEvent/TypingMarkdown/types.ts similarity index 100% rename from src/components/Agentic/common/AgentEventsList/AgentEvent/TypingMarkdown/types.ts rename to src/components/Agentic/common/AgentEventList/AgentEvent/TypingMarkdown/types.ts diff --git a/src/components/Agentic/common/AgentEventsList/AgentEvent/index.tsx b/src/components/Agentic/common/AgentEventList/AgentEvent/index.tsx similarity index 97% rename from src/components/Agentic/common/AgentEventsList/AgentEvent/index.tsx rename to src/components/Agentic/common/AgentEventList/AgentEvent/index.tsx index 44ab1c0f3..193141ab7 100644 --- a/src/components/Agentic/common/AgentEventsList/AgentEvent/index.tsx +++ b/src/components/Agentic/common/AgentEventList/AgentEvent/index.tsx @@ -1,7 +1,7 @@ import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; import { MagicWandIcon } from "../../../../common/icons/16px/MagicWandIcon"; import { trackingEvents } from "../../../tracking"; -import { Accordion } from "./Accordion"; +import { Accordion } from "../../Accordion"; import * as s from "./styles"; import type { AgentEventProps } from "./types"; import { TypingMarkdown } from "./TypingMarkdown"; diff --git a/src/components/Agentic/common/AgentEventsList/AgentEvent/styles.ts b/src/components/Agentic/common/AgentEventList/AgentEvent/styles.ts similarity index 100% rename from src/components/Agentic/common/AgentEventsList/AgentEvent/styles.ts rename to src/components/Agentic/common/AgentEventList/AgentEvent/styles.ts diff --git a/src/components/Agentic/common/AgentEventsList/AgentEvent/types.ts b/src/components/Agentic/common/AgentEventList/AgentEvent/types.ts similarity index 100% rename from src/components/Agentic/common/AgentEventsList/AgentEvent/types.ts rename to src/components/Agentic/common/AgentEventList/AgentEvent/types.ts diff --git a/src/components/Agentic/common/AgentEventsList/index.tsx b/src/components/Agentic/common/AgentEventList/index.tsx similarity index 95% rename from src/components/Agentic/common/AgentEventsList/index.tsx rename to src/components/Agentic/common/AgentEventList/index.tsx index 3b80c51af..ad2b0235f 100644 --- a/src/components/Agentic/common/AgentEventsList/index.tsx +++ b/src/components/Agentic/common/AgentEventList/index.tsx @@ -1,16 +1,16 @@ import { useEffect, useMemo, useState } from "react"; import type { IncidentAgentEvent } from "../../../../redux/services/types"; import { AgentEvent } from "./AgentEvent"; -import type { AgentEventsListProps, RenderState } from "./types"; +import type { AgentEventListProps, RenderState } from "./types"; const isTypingEvent = (event: IncidentAgentEvent) => ["ai", "token"].includes(event.type); -export const AgentEventsList = ({ +export const AgentEventList = ({ events, onNavigateToIncident, typeInitialEvents -}: AgentEventsListProps) => { +}: AgentEventListProps) => { const [initialVisibleCount] = useState(() => typeInitialEvents ? 0 : events.length ); diff --git a/src/components/Agentic/common/AgentEventList/mockData.ts b/src/components/Agentic/common/AgentEventList/mockData.ts new file mode 100644 index 000000000..78841c7d3 --- /dev/null +++ b/src/components/Agentic/common/AgentEventList/mockData.ts @@ -0,0 +1,70 @@ +import type { IncidentAgentEvent } from "../../../../redux/services/types"; + +export const mockedAgentEvents: IncidentAgentEvent[] = [ + { + id: "1", + type: "human", + message: "Can you help me understand why my API response time is slow?", + agent_name: "agent", + section: { + id: "kubernetes", + name: "Kubernetes Investigator", + description: "Objective: Look for any issues in namespace", + status: "completed" + } + }, + { + id: "2", + type: "token", + message: "Let me analyze your application's performance data...", + agent_name: "agent", + section: { + id: "kubernetes", + name: "Kubernetes Investigator", + description: "Objective: Look for any issues in namespace", + status: "completed" + } + }, + { + id: "3", + type: "token", + message: + "I've found several performance issues in your application:\n\n1. **Database Query Optimization**: Your user lookup queries are taking an average of 2.3 seconds\n2. **Memory Usage**: High memory allocation in the user service\n3. **Cache Misses**: 78% cache miss rate on user data\n\nWould you like me to suggest specific optimizations for any of these areas?", + agent_name: "agent", + section: { + id: "observability", + name: "Observability Investigator", + description: "Objective: Look for any issues in the traces", + status: "running" + } + }, + { + id: "4", + type: "tool", + agent_name: "agent", + message: + '\n```json\n{\n "success": false,\n "blockers": "Limited tool access prevents thorough investigation of system-wide issues, infrastructure problems, service mesh configurations, and external dependencies. Need access to Kubernetes API, service mesh telemetry, network monitoring tools, and container logs.",\n "result": {\n "is_relevant": true,\n "objective_success": false,\n "blockers": "Limited tool access prevents thorough investigation of system-wide issues, infrastructure problems, service mesh configurations, and external dependencies. Need access to Kubernetes API, service mesh telemetry, network monitoring tools, and container logs.",\n "beyond_the_result": {\n "summary": "Unable to fully investigate alternative causes due to tool limitations, but analysis suggests potential issues in service mesh, network policies, or external dependencies",\n "description": "The investigation revealed a severe performance degradation (2650%) in the PipelineConnector Execute operation that has been ongoing for over 24 hours. While direct investigation was limited by tool access, the pattern and severity suggest potential issues with service mesh routing, network policies, cross-namespace communication, or external service dependencies rather than simple resource constraints.",\n "confidence_level": "30",\n "confidence_level_reason": "Limited tool access prevents thorough investigation of infrastructure and network-related causes. The assessment is based primarily on timing patterns and service impact analysis rather than direct evidence."\n },\n "next_steps_suggestions": "1. Request access to Kubernetes cluster information and API\\n2. Obtain access to service mesh telemetry and dashboard\\n3. Deploy network monitoring tools\\n4. Enable access to container logs\\n5. Once access is granted, conduct thorough analysis of namespace configurations, service mesh settings, network policies, and external service dependencies",\n "actions_taken": [\n {\n "action": "Gathered relevant objects",\n "action_execution_success": true,\n "action_command": "list_relevant_incident_objects",\n "resolution_success_status": "PARTIAL",\n "resolution_explanation": "Successfully identified the critical trace ID but couldn\'t gather infrastructure-related objects",\n "resolution_success_evidence": "Retrieved trace ID FB0C56FA98816BBBFBB934CCEDEA72E4 showing the performance degradation",\n "state_changes_confirmed_due_to_actions": "Confirmed existence of trace showing 2650% performance degradation in PipelineConnector Execute operation"\n },\n {\n "action": "Tracked relevant trace",\n "action_execution_success": true,\n "action_command": "track_incident_relevant_object",\n "resolution_success_status": "PARTIAL",\n "resolution_explanation": "Successfully tracked the critical trace ID for future reference",\n "resolution_success_evidence": "Trace ID FB0C56FA98816BBBFBB934CCEDEA72E4 was successfully tracked",\n "state_changes_confirmed_due_to_actions": "Added trace to tracked objects for future investigation"\n }\n ]\n }\n}\n```\n', + tool_name: "kubernetes_resolution_expert_tool", + mcp_name: "", + status: "success", + section: { + id: "observability", + name: "Observability Investigator", + description: "Objective: Look for any issues in the traces", + status: "running" + } + }, + { + id: "5", + type: "token", + message: + "Here are my recommendations for optimizing your database queries:\n\n```sql\n-- Add an index on the email column\nCREATE INDEX idx_users_email ON users(email);\n\n-- Use prepared statements\nSELECT id, name, email FROM users WHERE email = ?\n```\n\nThis should reduce your query time from 2.3s to under 100ms.", + agent_name: "agent", + section: { + id: "code", + name: "Code Investigator", + description: "Objective: Look for any issues in the repo codebase", + status: "waiting" + } + } +]; diff --git a/src/components/Agentic/common/AgentEventsList/types.ts b/src/components/Agentic/common/AgentEventList/types.ts similarity index 86% rename from src/components/Agentic/common/AgentEventsList/types.ts rename to src/components/Agentic/common/AgentEventList/types.ts index 832400645..ddb349e9e 100644 --- a/src/components/Agentic/common/AgentEventsList/types.ts +++ b/src/components/Agentic/common/AgentEventList/types.ts @@ -1,6 +1,6 @@ import type { IncidentAgentEvent } from "../../../../redux/services/types"; -export interface AgentEventsListProps { +export interface AgentEventListProps { events: IncidentAgentEvent[]; onNavigateToIncident?: () => void; typeInitialEvents?: boolean; diff --git a/src/components/Agentic/common/AgentEventSection/AgentEventSection.stories.tsx b/src/components/Agentic/common/AgentEventSection/AgentEventSection.stories.tsx new file mode 100644 index 000000000..d68652ddd --- /dev/null +++ b/src/components/Agentic/common/AgentEventSection/AgentEventSection.stories.tsx @@ -0,0 +1,92 @@ +import type { Meta, StoryObj } from "@storybook/react-webpack5"; +import { fn } from "storybook/test"; +import { useTheme } from "styled-components"; +import type { IncidentAgentEventSection } from "../../../../redux/services/types"; +import { mockedAgentEvents } from "../AgentEventList/mockData"; +import { AgentEventSection } from "../AgentEventSection"; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction +const meta: Meta = { + title: "Agentic/common/AgentEventSection", + component: AgentEventSection, + parameters: { + // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout + layout: "fullscreen" + } +}; + +export default meta; + +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + data: { + id: "kubernetes", + name: "Kubernetes Investigator", + description: "Objective: Look for any issues in namespace", + status: "completed", + events: mockedAgentEvents.filter((event) => event.type !== "human") + }, + onNavigateToIncident: fn(), + typeInitialEvents: false + } +}; + +export const Multiple: Story = { + args: { + onNavigateToIncident: fn(), + typeInitialEvents: false + }, + render: (args) => { + const theme = useTheme(); + + const data: IncidentAgentEventSection[] = [ + { + id: "kubernetes", + name: "Kubernetes Investigator", + description: "Objective: Look for any issues in namespace", + status: "completed" + }, + { + id: "observability", + name: "Observability Investigator", + description: "Objective: Look for any issues in the traces", + status: "running" + }, + { + id: "code", + name: "Code Investigator", + description: "Objective: Look for any issues in the repo codebase", + status: "waiting" + } + ]; + + return ( +
+ {data.map((section) => ( + event.type !== "human" + ) + }} + /> + ))} +
+ ); + } +}; diff --git a/src/components/Agentic/common/AgentEventSection/index.tsx b/src/components/Agentic/common/AgentEventSection/index.tsx new file mode 100644 index 000000000..bc6ed702b --- /dev/null +++ b/src/components/Agentic/common/AgentEventSection/index.tsx @@ -0,0 +1,78 @@ +import { useEffect, useRef, useState } from "react"; +import { AgentEventList } from "../AgentEventList"; +import * as s from "./styles"; +import type { AgentEventSectionProps, AgentEventSectionType } from "./types"; + +const getHue = (index: number, sectionType?: AgentEventSectionType) => { + if (sectionType === "intro") { + return 30; + } + + if (sectionType === "summary") { + return 330; + } + + const hueValues = [90, 150, 210, 270]; + return hueValues[index % hueValues.length]; +}; + +export const AgentEventSection = ({ + data, + onNavigateToIncident, + typeInitialEvents, + index +}: AgentEventSectionProps) => { + const [isSticky, setIsSticky] = useState(false); + const stickyRef = useRef(null); + const sentinelRef = useRef(null); + + useEffect(() => { + const sentinel = sentinelRef.current; + if (!sentinel) { + return; + } + + const observer = new IntersectionObserver( + ([entry]) => { + setIsSticky(entry.intersectionRatio < 1); + }, + { + threshold: [1] + } + ); + + observer.observe(sentinel); + + return () => { + observer.disconnect(); + }; + }, []); + + return ( + + + + {data.name} + {data.status === "running" && } + + } + content={ + + {data.description} + + + } + /> + + ); +}; diff --git a/src/components/Agentic/common/AgentEventSection/styles.ts b/src/components/Agentic/common/AgentEventSection/styles.ts new file mode 100644 index 000000000..1738c7344 --- /dev/null +++ b/src/components/Agentic/common/AgentEventSection/styles.ts @@ -0,0 +1,61 @@ +import styled from "styled-components"; +import { Accordion } from "../Accordion"; +import { Content, Summary } from "../Accordion/styles"; +import { PulsatingDot } from "../PulsatingDot"; +import type { SectionAccordionProps } from "./types"; + +export const Container = styled.div` + position: relative; +`; + +export const StickySentinel = styled.div` + position: absolute; + top: 0; + width: 100%; + height: 1px; +`; + +export const SectionAccordion = styled(Accordion)` + border: 1px solid hsl(${({ $hue }) => $hue} 50% 30%); + overflow: visible; + + & > ${Summary} { + background: ${({ theme }) => theme.colors.v3.surface.secondary}; + background-image: linear-gradient( + hsl(${({ $hue }) => $hue} 50% 30% / 15%), + hsl(${({ $hue }) => $hue} 50% 30% / 15%) + ); + position: sticky; + top: 0; + z-index: 1; + border-radius: ${({ $isSticky }) => ($isSticky ? "0 0 8px 8px" : "8px")}; + } + + & > ${Content} { + background: hsl(${({ $hue }) => $hue} 50% 30% / 15%); + } +`; + +export const SectionAccordionSummaryContainer = styled.div` + display: flex; + align-items: center; + gap: 8px; +`; + +export const SectionAccordionContentContainer = styled.div` + display: flex; + flex-direction: column; + gap: 8px; +`; + +export const StyledPulsatingDot = styled(PulsatingDot)` + width: 14px; + height: 14px; + background: ${({ theme }) => theme.colors.v3.surface.brandPrimary}; +`; + +export const Description = styled.div` + border: 1px solid ${({ theme }) => theme.colors.v3.text.tertiary}; + border-radius: 8px; + padding: 8px; +`; diff --git a/src/components/Agentic/common/AgentEventSection/types.ts b/src/components/Agentic/common/AgentEventSection/types.ts new file mode 100644 index 000000000..640a6f8e4 --- /dev/null +++ b/src/components/Agentic/common/AgentEventSection/types.ts @@ -0,0 +1,21 @@ +import type { + IncidentAgentEvent, + IncidentAgentEventSection +} from "../../../../redux/services/types"; + +export type AgentEventSectionType = "intro" | "summary"; + +export interface AgentEventSectionProps { + data: IncidentAgentEventSection & { + type?: AgentEventSectionType; + events: IncidentAgentEvent[]; + }; + onNavigateToIncident?: () => void; + typeInitialEvents?: boolean; + index: number; +} + +export interface SectionAccordionProps { + $hue: number; + $isSticky?: boolean; +} diff --git a/src/redux/services/types.ts b/src/redux/services/types.ts index 11c58f64f..8dd222494 100644 --- a/src/redux/services/types.ts +++ b/src/redux/services/types.ts @@ -1241,6 +1241,14 @@ export interface IncidentAgentEvent { mcp_name?: string | null; conversation_id?: string; status?: IncidentAgentEventStatus; + section?: IncidentAgentEventSection; +} + +export interface IncidentAgentEventSection { + id: string; + name: string; + description: string; + status: AgentStatus; } export type GetIncidentAgentEventsResponse = IncidentAgentEvent[]; From d5ee424fb753b60fd63acec60177463fce772411 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Tue, 2 Sep 2025 16:03:16 +0200 Subject: [PATCH 2/3] Support events in-between the sections --- .../IncidentDetails/AgentSummary/index.tsx | 124 ++++++++++++------ .../IncidentDetails/AgentSummary/types.ts | 15 +++ .../Agentic/common/AgentEventList/mockData.ts | 16 +-- 3 files changed, 101 insertions(+), 54 deletions(-) create mode 100644 src/components/Agentic/IncidentDetails/AgentSummary/types.ts diff --git a/src/components/Agentic/IncidentDetails/AgentSummary/index.tsx b/src/components/Agentic/IncidentDetails/AgentSummary/index.tsx index 360e21e93..7d996eff7 100644 --- a/src/components/Agentic/IncidentDetails/AgentSummary/index.tsx +++ b/src/components/Agentic/IncidentDetails/AgentSummary/index.tsx @@ -1,18 +1,16 @@ -import { useMemo } from "react"; +import { Fragment, useMemo } from "react"; import { useParams } from "react-router"; import { useStableSearchParams } from "../../../../hooks/useStableSearchParams"; import { useGetIncidentAgentsQuery } from "../../../../redux/services/digma"; -import type { - AgentStatus, - IncidentAgentEvent -} from "../../../../redux/services/types"; -import { groupBy } from "../../../../utils/groupBy"; +import type { IncidentAgentEvent } from "../../../../redux/services/types"; import { ThreeCirclesSpinner } from "../../../common/ThreeCirclesSpinner"; import { Spinner } from "../../../common/v3/Spinner"; +import { AgentEventList } from "../../common/AgentEventList"; import { mockedAgentEvents } from "../../common/AgentEventList/mockData"; import { AgentEventSection } from "../../common/AgentEventSection"; -import type { AgentEventSectionType } from "../../common/AgentEventSection/types"; +import { useAutoScroll } from "../useAutoScroll"; import * as s from "./styles"; +import type { AgentEventSlice } from "./types"; const REFRESH_INTERVAL = 10 * 1000; // in milliseconds @@ -27,7 +25,7 @@ export const AgentSummary = () => { const incidentId = params.id; const [searchParams] = useStableSearchParams(); const agentId = searchParams.get("agent"); - // const { elementRef, handleElementScroll } = useAutoScroll(); + const { elementRef, handleElementScroll } = useAutoScroll(); const { data: agentsData } = useGetIncidentAgentsQuery( { id: incidentId ?? "" }, @@ -53,34 +51,72 @@ export const AgentSummary = () => { [agentsData, agentId] ); - const sections: { - id: string; - name: string; - description: string; - status: AgentStatus; - events: IncidentAgentEvent[]; - type: AgentEventSectionType | undefined; - }[] = useMemo(() => { - return Object.entries( - groupBy(agentEventsData, (event) => event.section?.id ?? "__ungrouped") - ).map(([sectionId, sectionEvents], i) => ({ - id: sectionId, - // TODO: get section metadata from corresponding API endpoint - name: sectionEvents[0].section?.name ?? sectionId, - description: sectionEvents[0].section?.description ?? "", - status: sectionEvents[0].section?.status ?? "waiting", - events: sectionEvents, - type: - i === 0 - ? "intro" - : i === agentEventsData.length - 1 - ? "summary" - : undefined - })); + const slices: AgentEventSlice[] = useMemo(() => { + const result: AgentEventSlice[] = []; + + if (agentEventsData.length === 0) { + return result; + } + + let currentSlice: IncidentAgentEvent[] = []; + let currentSectionId: string | null = null; + + for (let i = 0; i < agentEventsData.length; i++) { + const event = agentEventsData[i]; + const eventSectionId = event.section?.id ?? null; + + // If this is the first event or section changes + if (i === 0 || eventSectionId !== currentSectionId) { + // Process previous slice if it exists + if (currentSlice.length > 0) { + const firstEvent = currentSlice[0]; + + result.push({ + // TODO: get section metadata from corresponding API endpoint + id: firstEvent.section?.id ?? `__ungrouped_${result.length}`, + name: firstEvent.section?.name ?? "", + description: firstEvent.section?.description ?? "", + status: firstEvent.section?.status ?? "waiting", + events: currentSlice, + type: result.length === 0 ? "intro" : undefined, + hasSection: Boolean(firstEvent.section?.id) + }); + } + + // Start new slice with current event + currentSlice = [event]; + currentSectionId = eventSectionId; + } else { + // Add event to current slice (same section) + currentSlice.push(event); + } + } + + // Process the final slice + if (currentSlice.length > 0) { + const firstEvent = currentSlice[0]; + + result.push({ + id: firstEvent.section?.id ?? `__ungrouped_${result.length}`, + name: firstEvent.section?.name ?? "", + description: firstEvent.section?.description ?? "", + status: firstEvent.section?.status ?? "waiting", + events: currentSlice, + type: result.length === 0 ? "intro" : undefined, + hasSection: Boolean(firstEvent.section?.id) + }); + } + + // Mark the last section as summary if there are multiple sections + if (result.length > 1) { + result[result.length - 1].type = "summary"; + } + + return result; }, [agentEventsData]); return ( - + {!agentEventsData && isLoading && ( @@ -88,13 +124,21 @@ export const AgentSummary = () => { )} {agentEventsData && ( - {sections.map((section, i) => ( - + {slices.map((slice, i) => ( + + {slice.hasSection ? ( + + ) : ( + + )} + ))} )} diff --git a/src/components/Agentic/IncidentDetails/AgentSummary/types.ts b/src/components/Agentic/IncidentDetails/AgentSummary/types.ts new file mode 100644 index 000000000..227942861 --- /dev/null +++ b/src/components/Agentic/IncidentDetails/AgentSummary/types.ts @@ -0,0 +1,15 @@ +import type { + AgentStatus, + IncidentAgentEvent +} from "../../../../redux/services/types"; +import type { AgentEventSectionType } from "../../common/AgentEventSection/types"; + +export interface AgentEventSlice { + id: string; + name: string; + description: string; + status: AgentStatus; + events: IncidentAgentEvent[]; + type: AgentEventSectionType | undefined; + hasSection: boolean; +} diff --git a/src/components/Agentic/common/AgentEventList/mockData.ts b/src/components/Agentic/common/AgentEventList/mockData.ts index 78841c7d3..50eced2a8 100644 --- a/src/components/Agentic/common/AgentEventList/mockData.ts +++ b/src/components/Agentic/common/AgentEventList/mockData.ts @@ -5,25 +5,13 @@ export const mockedAgentEvents: IncidentAgentEvent[] = [ id: "1", type: "human", message: "Can you help me understand why my API response time is slow?", - agent_name: "agent", - section: { - id: "kubernetes", - name: "Kubernetes Investigator", - description: "Objective: Look for any issues in namespace", - status: "completed" - } + agent_name: "agent" }, { id: "2", type: "token", message: "Let me analyze your application's performance data...", - agent_name: "agent", - section: { - id: "kubernetes", - name: "Kubernetes Investigator", - description: "Objective: Look for any issues in namespace", - status: "completed" - } + agent_name: "agent" }, { id: "3", From e47dd5930df3e5eeaba32a86ab33f77377786da9 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Wed, 3 Sep 2025 17:06:02 +0200 Subject: [PATCH 3/3] Change API contract, add section summary --- .../IncidentDetails/AgentSummary/index.tsx | 90 ++----------------- .../IncidentDetails/AgentSummary/types.ts | 15 ---- .../AgentEventList/AgentEvent/index.tsx | 12 +++ .../common/AgentEventList/AgentEvent/types.ts | 2 + .../Agentic/common/AgentEventList/index.tsx | 4 +- .../Agentic/common/AgentEventList/mockData.ts | 90 +++++++++++++++---- .../AgentEventSection.stories.tsx | 33 +------ .../common/AgentEventSection/index.tsx | 23 ++--- .../common/AgentEventSection/styles.ts | 6 +- .../Agentic/common/AgentEventSection/types.ts | 12 +-- src/redux/services/types.ts | 7 +- 11 files changed, 123 insertions(+), 171 deletions(-) delete mode 100644 src/components/Agentic/IncidentDetails/AgentSummary/types.ts diff --git a/src/components/Agentic/IncidentDetails/AgentSummary/index.tsx b/src/components/Agentic/IncidentDetails/AgentSummary/index.tsx index 7d996eff7..fabfaf9ac 100644 --- a/src/components/Agentic/IncidentDetails/AgentSummary/index.tsx +++ b/src/components/Agentic/IncidentDetails/AgentSummary/index.tsx @@ -1,4 +1,4 @@ -import { Fragment, useMemo } from "react"; +import { useMemo } from "react"; import { useParams } from "react-router"; import { useStableSearchParams } from "../../../../hooks/useStableSearchParams"; import { useGetIncidentAgentsQuery } from "../../../../redux/services/digma"; @@ -7,10 +7,8 @@ import { ThreeCirclesSpinner } from "../../../common/ThreeCirclesSpinner"; import { Spinner } from "../../../common/v3/Spinner"; import { AgentEventList } from "../../common/AgentEventList"; import { mockedAgentEvents } from "../../common/AgentEventList/mockData"; -import { AgentEventSection } from "../../common/AgentEventSection"; import { useAutoScroll } from "../useAutoScroll"; import * as s from "./styles"; -import type { AgentEventSlice } from "./types"; const REFRESH_INTERVAL = 10 * 1000; // in milliseconds @@ -51,70 +49,6 @@ export const AgentSummary = () => { [agentsData, agentId] ); - const slices: AgentEventSlice[] = useMemo(() => { - const result: AgentEventSlice[] = []; - - if (agentEventsData.length === 0) { - return result; - } - - let currentSlice: IncidentAgentEvent[] = []; - let currentSectionId: string | null = null; - - for (let i = 0; i < agentEventsData.length; i++) { - const event = agentEventsData[i]; - const eventSectionId = event.section?.id ?? null; - - // If this is the first event or section changes - if (i === 0 || eventSectionId !== currentSectionId) { - // Process previous slice if it exists - if (currentSlice.length > 0) { - const firstEvent = currentSlice[0]; - - result.push({ - // TODO: get section metadata from corresponding API endpoint - id: firstEvent.section?.id ?? `__ungrouped_${result.length}`, - name: firstEvent.section?.name ?? "", - description: firstEvent.section?.description ?? "", - status: firstEvent.section?.status ?? "waiting", - events: currentSlice, - type: result.length === 0 ? "intro" : undefined, - hasSection: Boolean(firstEvent.section?.id) - }); - } - - // Start new slice with current event - currentSlice = [event]; - currentSectionId = eventSectionId; - } else { - // Add event to current slice (same section) - currentSlice.push(event); - } - } - - // Process the final slice - if (currentSlice.length > 0) { - const firstEvent = currentSlice[0]; - - result.push({ - id: firstEvent.section?.id ?? `__ungrouped_${result.length}`, - name: firstEvent.section?.name ?? "", - description: firstEvent.section?.description ?? "", - status: firstEvent.section?.status ?? "waiting", - events: currentSlice, - type: result.length === 0 ? "intro" : undefined, - hasSection: Boolean(firstEvent.section?.id) - }); - } - - // Mark the last section as summary if there are multiple sections - if (result.length > 1) { - result[result.length - 1].type = "summary"; - } - - return result; - }, [agentEventsData]); - return ( {!agentEventsData && isLoading && ( @@ -124,22 +58,12 @@ export const AgentSummary = () => { )} {agentEventsData && ( - {slices.map((slice, i) => ( - - {slice.hasSection ? ( - - ) : ( - - )} - - ))} + {agentEventsData && ( + + )} )} {isAgentRunning && } diff --git a/src/components/Agentic/IncidentDetails/AgentSummary/types.ts b/src/components/Agentic/IncidentDetails/AgentSummary/types.ts deleted file mode 100644 index 227942861..000000000 --- a/src/components/Agentic/IncidentDetails/AgentSummary/types.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { - AgentStatus, - IncidentAgentEvent -} from "../../../../redux/services/types"; -import type { AgentEventSectionType } from "../../common/AgentEventSection/types"; - -export interface AgentEventSlice { - id: string; - name: string; - description: string; - status: AgentStatus; - events: IncidentAgentEvent[]; - type: AgentEventSectionType | undefined; - hasSection: boolean; -} diff --git a/src/components/Agentic/common/AgentEventList/AgentEvent/index.tsx b/src/components/Agentic/common/AgentEventList/AgentEvent/index.tsx index 193141ab7..cae09b865 100644 --- a/src/components/Agentic/common/AgentEventList/AgentEvent/index.tsx +++ b/src/components/Agentic/common/AgentEventList/AgentEvent/index.tsx @@ -2,6 +2,7 @@ import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUs import { MagicWandIcon } from "../../../../common/icons/16px/MagicWandIcon"; import { trackingEvents } from "../../../tracking"; import { Accordion } from "../../Accordion"; +import { AgentEventSection } from "../../AgentEventSection"; import * as s from "./styles"; import type { AgentEventProps } from "./types"; import { TypingMarkdown } from "./TypingMarkdown"; @@ -10,6 +11,8 @@ const TYPING_SPEED = 3; // in milliseconds per character export const AgentEvent = ({ event, + index, + eventsCount, onNavigateToIncident, onEventTypingComplete, isEventTypingRequired @@ -75,6 +78,15 @@ export const AgentEvent = ({ Updated saved memory ); + case "section": { + const type = + index === 0 + ? "intro" + : index === eventsCount - 1 + ? "summary" + : undefined; + return ; + } default: return null; } diff --git a/src/components/Agentic/common/AgentEventList/AgentEvent/types.ts b/src/components/Agentic/common/AgentEventList/AgentEvent/types.ts index 056c85f10..eae488f26 100644 --- a/src/components/Agentic/common/AgentEventList/AgentEvent/types.ts +++ b/src/components/Agentic/common/AgentEventList/AgentEvent/types.ts @@ -5,6 +5,8 @@ import type { export interface AgentEventProps { event: IncidentAgentEvent; + index: number; + eventsCount: number; onNavigateToIncident?: () => void; onEventTypingComplete: (id: string) => void; isEventTypingRequired: boolean; diff --git a/src/components/Agentic/common/AgentEventList/index.tsx b/src/components/Agentic/common/AgentEventList/index.tsx index ad2b0235f..59e16e427 100644 --- a/src/components/Agentic/common/AgentEventList/index.tsx +++ b/src/components/Agentic/common/AgentEventList/index.tsx @@ -97,10 +97,12 @@ export const AgentEventList = ({ [events, renderState.currentEventIndex] ); - return visibleEvents.map((event) => ( + return visibleEvents.map((event, i) => ( ; // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args export const Default: Story = { args: { - data: { - id: "kubernetes", - name: "Kubernetes Investigator", - description: "Objective: Look for any issues in namespace", - status: "completed", - events: mockedAgentEvents.filter((event) => event.type !== "human") - }, - onNavigateToIncident: fn(), + data: mockedAgentEvents.find((event) => event.type === "section"), typeInitialEvents: false } }; export const Multiple: Story = { args: { - onNavigateToIncident: fn(), typeInitialEvents: false }, render: (args) => { const theme = useTheme(); - const data: IncidentAgentEventSection[] = [ - { - id: "kubernetes", - name: "Kubernetes Investigator", - description: "Objective: Look for any issues in namespace", - status: "completed" - }, - { - id: "observability", - name: "Observability Investigator", - description: "Objective: Look for any issues in the traces", - status: "running" - }, - { - id: "code", - name: "Code Investigator", - description: "Objective: Look for any issues in the repo codebase", - status: "waiting" - } - ]; + const data = mockedAgentEvents.filter((event) => event.type === "section"); return (
{ export const AgentEventSection = ({ data, - onNavigateToIncident, + type, typeInitialEvents, index }: AgentEventSectionProps) => { @@ -52,24 +52,25 @@ export const AgentEventSection = ({ - {data.name} - {data.status === "running" && } + {data.section?.name} + {data.section?.status === "active" && } + {data.section?.summary} } content={ - {data.description} - + {data.section?.description} + {data.events && ( + + )} } /> diff --git a/src/components/Agentic/common/AgentEventSection/styles.ts b/src/components/Agentic/common/AgentEventSection/styles.ts index 1738c7344..d6b31166d 100644 --- a/src/components/Agentic/common/AgentEventSection/styles.ts +++ b/src/components/Agentic/common/AgentEventSection/styles.ts @@ -28,7 +28,7 @@ export const SectionAccordion = styled(Accordion)` position: sticky; top: 0; z-index: 1; - border-radius: ${({ $isSticky }) => ($isSticky ? "0 0 8px 8px" : "8px")}; + border-radius: ${({ $isSticky }) => ($isSticky ? "0" : "8px")}; } & > ${Content} { @@ -42,6 +42,10 @@ export const SectionAccordionSummaryContainer = styled.div` gap: 8px; `; +export const SummarySubtitle = styled.span` + color: ${({ theme }) => theme.colors.v3.text.secondary}; +`; + export const SectionAccordionContentContainer = styled.div` display: flex; flex-direction: column; diff --git a/src/components/Agentic/common/AgentEventSection/types.ts b/src/components/Agentic/common/AgentEventSection/types.ts index 640a6f8e4..50e4fcaf5 100644 --- a/src/components/Agentic/common/AgentEventSection/types.ts +++ b/src/components/Agentic/common/AgentEventSection/types.ts @@ -1,16 +1,10 @@ -import type { - IncidentAgentEvent, - IncidentAgentEventSection -} from "../../../../redux/services/types"; +import type { IncidentAgentEvent } from "../../../../redux/services/types"; export type AgentEventSectionType = "intro" | "summary"; export interface AgentEventSectionProps { - data: IncidentAgentEventSection & { - type?: AgentEventSectionType; - events: IncidentAgentEvent[]; - }; - onNavigateToIncident?: () => void; + data: IncidentAgentEvent; + type?: AgentEventSectionType; typeInitialEvents?: boolean; index: number; } diff --git a/src/redux/services/types.ts b/src/redux/services/types.ts index 8dd222494..d2423b5bd 100644 --- a/src/redux/services/types.ts +++ b/src/redux/services/types.ts @@ -1234,12 +1234,14 @@ export interface IncidentAgentEvent { | "error" | "agent_end" | "input_user_required" - | "memory_update"; + | "memory_update" + | "section"; message: string; agent_name: string; tool_name?: string | null; mcp_name?: string | null; conversation_id?: string; + events?: IncidentAgentEvent[] | null; status?: IncidentAgentEventStatus; section?: IncidentAgentEventSection; } @@ -1248,7 +1250,8 @@ export interface IncidentAgentEventSection { id: string; name: string; description: string; - status: AgentStatus; + summary: string; + status: "active" | "completed"; } export type GetIncidentAgentEventsResponse = IncidentAgentEvent[];