diff --git a/frontend/__tests__/components/features/conversation-panel/system-message-modal/tool-item.test.tsx b/frontend/__tests__/components/features/conversation-panel/system-message-modal/tool-item.test.tsx
new file mode 100644
index 0000000000..15fcef2b5f
--- /dev/null
+++ b/frontend/__tests__/components/features/conversation-panel/system-message-modal/tool-item.test.tsx
@@ -0,0 +1,551 @@
+import { screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { afterEach, describe, expect, it, vi } from "vitest";
+import { renderWithProviders } from "test-utils";
+import { ToolItem } from "#/components/features/conversation-panel/system-message-modal/tool-item";
+import type { ChatCompletionToolParam } from "#/types/v1/core";
+
+describe("ToolItem", () => {
+ const user = userEvent.setup();
+ const onToggleMock = vi.fn();
+
+ afterEach(() => {
+ vi.clearAllMocks();
+ });
+
+ describe("Name/Title Extraction", () => {
+ it("should display name from V0 format function.name", () => {
+ // Arrange
+ const v0Tool: ChatCompletionToolParam = {
+ type: "function",
+ function: {
+ name: "test_function",
+ description: "Test description",
+ parameters: {},
+ },
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ const toggleButton = screen.getByTestId("toggle-button");
+ expect(toggleButton).toHaveTextContent("test_function");
+ });
+
+ it("should display title from V1 format root level", () => {
+ // Arrange
+ const v1Tool = {
+ title: "V1 Tool Title",
+ description: "V1 description",
+ parameters: { type: "object" },
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ const toggleButton = screen.getByTestId("toggle-button");
+ expect(toggleButton).toHaveTextContent("V1 Tool Title");
+ });
+
+ it("should prioritize root title over annotations.title in V1 format", () => {
+ // Arrange
+ const v1Tool = {
+ title: "Root Title",
+ annotations: {
+ title: "Annotations Title",
+ },
+ description: "Description",
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ const toggleButton = screen.getByTestId("toggle-button");
+ expect(toggleButton).toHaveTextContent("Root Title");
+ });
+
+ it("should fallback to annotations.title when root title is missing", () => {
+ // Arrange
+ const v1Tool = {
+ annotations: {
+ title: "Annotations Title",
+ },
+ description: "Description",
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ const toggleButton = screen.getByTestId("toggle-button");
+ expect(toggleButton).toHaveTextContent("Annotations Title");
+ });
+
+ it("should display empty string when no name or title is available", () => {
+ // Arrange
+ const toolWithoutName = {
+ description: "Description only",
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ const toggleButton = screen.getByTestId("toggle-button");
+ expect(toggleButton).toHaveTextContent("");
+ });
+ });
+
+ describe("Description Extraction", () => {
+ it("should display description from V0 format function.description when expanded", () => {
+ // Arrange
+ const v0Tool: ChatCompletionToolParam = {
+ type: "function",
+ function: {
+ name: "test_function",
+ description: "V0 function description",
+ parameters: {},
+ },
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ const markdownRenderer = screen.getByTestId("markdown-renderer");
+ expect(markdownRenderer).toHaveTextContent("V0 function description");
+ });
+
+ it("should display description from V1 format root level when expanded", () => {
+ // Arrange
+ const v1Tool = {
+ title: "V1 Tool",
+ description: "V1 root description",
+ parameters: {},
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ const markdownRenderer = screen.getByTestId("markdown-renderer");
+ expect(markdownRenderer).toHaveTextContent("V1 root description");
+ });
+
+ it("should prioritize root description over function.description in V1 format", () => {
+ // Arrange
+ const v1Tool = {
+ title: "V1 Tool",
+ description: "Root description",
+ function: {
+ description: "Function description",
+ },
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ const markdownRenderer = screen.getByTestId("markdown-renderer");
+ expect(markdownRenderer).toHaveTextContent("Root description");
+ });
+
+ it("should display empty string when no description is available", () => {
+ // Arrange
+ const toolWithoutDescription = {
+ title: "Tool Name",
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ const markdownRenderer = screen.getByTestId("markdown-renderer");
+ expect(markdownRenderer).toHaveTextContent("");
+ });
+
+ it("should not display description when collapsed", () => {
+ // Arrange
+ const v0Tool: ChatCompletionToolParam = {
+ type: "function",
+ function: {
+ name: "test_function",
+ description: "Should not be visible",
+ parameters: {},
+ },
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ expect(screen.queryByTestId("markdown-renderer")).not.toBeInTheDocument();
+ });
+ });
+
+ describe("Parameters Extraction", () => {
+ it("should display parameters from V0 format function.parameters when expanded", () => {
+ // Arrange
+ const v0Tool: ChatCompletionToolParam = {
+ type: "function",
+ function: {
+ name: "test_function",
+ description: "Description",
+ parameters: {
+ type: "object",
+ properties: {
+ param1: { type: "string" },
+ },
+ },
+ },
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ const toolParameters = screen.getByTestId("tool-parameters");
+ expect(toolParameters).toBeInTheDocument();
+ // Verify that the parameters are rendered (ReactJsonView will render the JSON)
+ expect(toolParameters).toHaveTextContent("param1");
+ });
+
+ it("should display parameters from V1 format root level when expanded", () => {
+ // Arrange
+ const v1Tool = {
+ title: "V1 Tool",
+ description: "Description",
+ parameters: {
+ type: "object",
+ properties: {
+ param2: { type: "number" },
+ },
+ },
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ const toolParameters = screen.getByTestId("tool-parameters");
+ expect(toolParameters).toBeInTheDocument();
+ // Verify that the parameters are rendered (ReactJsonView will render the JSON)
+ expect(toolParameters).toHaveTextContent("param2");
+ });
+
+ it("should prioritize function.parameters over root parameters in V0 format", () => {
+ // Arrange
+ const v0Tool = {
+ type: "function",
+ function: {
+ name: "test_function",
+ description: "Description",
+ parameters: {
+ type: "object",
+ source: "function",
+ },
+ },
+ parameters: {
+ type: "object",
+ source: "root",
+ },
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ const toolParameters = screen.getByTestId("tool-parameters");
+ expect(toolParameters).toBeInTheDocument();
+ // Verify that function parameters are used (not root parameters)
+ expect(toolParameters).toHaveTextContent("function");
+ expect(toolParameters).not.toHaveTextContent("root");
+ });
+
+ it("should not display parameters when they are null", () => {
+ // Arrange
+ const toolWithoutParameters = {
+ title: "Tool Name",
+ description: "Description",
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ expect(screen.queryByTestId("tool-parameters")).not.toBeInTheDocument();
+ });
+
+ it("should not display parameters when collapsed", () => {
+ // Arrange
+ const v0Tool: ChatCompletionToolParam = {
+ type: "function",
+ function: {
+ name: "test_function",
+ description: "Description",
+ parameters: {
+ type: "object",
+ },
+ },
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ expect(screen.queryByTestId("tool-parameters")).not.toBeInTheDocument();
+ });
+ });
+
+ describe("Toggle Functionality", () => {
+ it("should call onToggle with correct index when toggle button is clicked", async () => {
+ // Arrange
+ const v0Tool: ChatCompletionToolParam = {
+ type: "function",
+ function: {
+ name: "test_function",
+ description: "Description",
+ parameters: {},
+ },
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ const toggleButton = screen.getByTestId("toggle-button");
+ await user.click(toggleButton);
+
+ // Assert
+ expect(onToggleMock).toHaveBeenCalledOnce();
+ expect(onToggleMock).toHaveBeenCalledWith(2);
+ });
+
+ it("should show expanded content when isExpanded is true", () => {
+ // Arrange
+ const v0Tool: ChatCompletionToolParam = {
+ type: "function",
+ function: {
+ name: "test_function",
+ description: "Test description",
+ parameters: { type: "object" },
+ },
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ expect(screen.getByTestId("markdown-renderer")).toBeInTheDocument();
+ expect(screen.getByTestId("tool-parameters")).toBeInTheDocument();
+ });
+
+ it("should hide expanded content when isExpanded is false", () => {
+ // Arrange
+ const v0Tool: ChatCompletionToolParam = {
+ type: "function",
+ function: {
+ name: "test_function",
+ description: "Test description",
+ parameters: { type: "object" },
+ },
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ expect(screen.queryByTestId("markdown-renderer")).not.toBeInTheDocument();
+ expect(screen.queryByTestId("tool-parameters")).not.toBeInTheDocument();
+ });
+ });
+
+ describe("Complex Scenarios", () => {
+ it("should handle V0 format with type field correctly", () => {
+ // Arrange
+ const v0Tool = {
+ type: "function",
+ function: {
+ name: "typed_function",
+ description: "Typed description",
+ parameters: { type: "object" },
+ },
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ expect(screen.getByTestId("toggle-button")).toHaveTextContent(
+ "typed_function",
+ );
+ expect(screen.getByTestId("markdown-renderer")).toHaveTextContent(
+ "Typed description",
+ );
+ expect(screen.getByTestId("tool-parameters")).toBeInTheDocument();
+ });
+
+ it("should handle tool data where function is at root level (fallback behavior)", () => {
+ // Arrange
+ const toolWithFunctionAtRoot = {
+ name: "root_function",
+ description: "Root function description",
+ parameters: { type: "object" },
+ };
+
+ // Act
+ renderWithProviders(
+ ,
+ );
+
+ // Assert
+ expect(screen.getByTestId("toggle-button")).toHaveTextContent(
+ "root_function",
+ );
+ expect(screen.getByTestId("markdown-renderer")).toHaveTextContent(
+ "Root function description",
+ );
+ expect(screen.getByTestId("tool-parameters")).toBeInTheDocument();
+ });
+ });
+});
diff --git a/frontend/src/components/features/conversation-panel/system-message-modal/system-message-header.tsx b/frontend/src/components/features/conversation-panel/system-message-modal/system-message-header.tsx
index e1ed8fb401..f974f9c055 100644
--- a/frontend/src/components/features/conversation-panel/system-message-modal/system-message-header.tsx
+++ b/frontend/src/components/features/conversation-panel/system-message-modal/system-message-header.tsx
@@ -1,6 +1,7 @@
import { useTranslation } from "react-i18next";
import { BaseModalTitle } from "#/components/shared/modals/confirmation-modals/base-modal";
import { Typography } from "#/ui/typography";
+import { cn } from "#/utils/utils";
interface SystemMessageHeaderProps {
agentClass: string | null;
@@ -14,7 +15,12 @@ export function SystemMessageHeader({
const { t } = useTranslation();
return (
-
+
{agentClass && (
diff --git a/frontend/src/components/features/conversation-panel/system-message-modal/toggle-button.tsx b/frontend/src/components/features/conversation-panel/system-message-modal/toggle-button.tsx
index cd2d1d972b..eff4ed4a4f 100644
--- a/frontend/src/components/features/conversation-panel/system-message-modal/toggle-button.tsx
+++ b/frontend/src/components/features/conversation-panel/system-message-modal/toggle-button.tsx
@@ -17,6 +17,7 @@ export function ToggleButton({
return (