{body}
+
+ )
+}
diff --git a/packages/client/ui-conversation/src/client/chat/DisclosureRow.module.css b/packages/client/ui-conversation/src/client/chat/DisclosureRow.module.css
new file mode 100644
index 0000000000..04f2d18d0a
--- /dev/null
+++ b/packages/client/ui-conversation/src/client/chat/DisclosureRow.module.css
@@ -0,0 +1,69 @@
+/* Shared Tool calls disclosure header: [16px leading] gap 6 [title 14/24]. */
+
+.root {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ min-width: 0;
+}
+
+.row {
+ position: relative;
+ overflow: hidden;
+ display: flex;
+ align-items: center;
+ height: 24px;
+ min-width: 0;
+}
+
+.row[data-expandable] {
+ cursor: pointer;
+}
+
+.leading {
+ position: relative;
+ flex: none;
+ width: 16px;
+ height: 16px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 6px;
+ padding: 0;
+ border: none;
+ background: none;
+ color: var(--dsw-alias-label-tertiary);
+}
+
+button.leading {
+ cursor: pointer;
+}
+
+.iconIdle {
+ display: inline-flex;
+ opacity: 1;
+ transition: opacity 100ms ease;
+}
+
+.chevronHover {
+ position: absolute;
+ inset: 0;
+ margin: auto;
+ opacity: 0;
+ transition: opacity 100ms ease;
+}
+
+.row:hover .iconIdle {
+ opacity: 0;
+}
+
+.row:hover .chevronHover {
+ opacity: 1;
+}
+
+.title {
+ flex: none;
+ font-size: 14px;
+ line-height: 24px;
+ color: var(--dsw-alias-label-secondary);
+}
diff --git a/packages/client/ui-conversation/src/client/chat/DisclosureRow.tsx b/packages/client/ui-conversation/src/client/chat/DisclosureRow.tsx
new file mode 100644
index 0000000000..0b1e9ea1b0
--- /dev/null
+++ b/packages/client/ui-conversation/src/client/chat/DisclosureRow.tsx
@@ -0,0 +1,101 @@
+import { type KeyboardEvent, type MouseEvent, type ReactNode } from 'react'
+import clsx from 'clsx'
+import { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'
+import css from './DisclosureRow.module.css'
+
+/** Shared 24px disclosure chrome for conversation flow rows. */
+export interface DisclosureRowProps {
+ icon: ReactNode
+ title: string
+ open: boolean
+ expandable: boolean
+ onToggle: () => void
+ /** Makes the complete title row the disclosure target. */
+ expandOnRowClick?: boolean | undefined
+ /** Replaces the collapsed icon with a chevron while the row is hovered. */
+ previewChevron?: boolean | undefined
+ collapsedContent?: ReactNode
+ children?: ReactNode
+ className?: string | undefined
+ rowClassName?: string | undefined
+ leadingClassName?: string | undefined
+ chevronClassName?: string | undefined
+ titleClassName?: string | undefined
+}
+
+/**
+ * Render one disclosure header and its controlled expanded content.
+ * @param props - Visual content, controlled state, and interaction policy.
+ * @returns The disclosure row.
+ */
+export function DisclosureRow({
+ icon,
+ title,
+ open,
+ expandable,
+ onToggle,
+ expandOnRowClick = false,
+ previewChevron = expandable,
+ collapsedContent,
+ children,
+ className,
+ rowClassName,
+ leadingClassName,
+ chevronClassName,
+ titleClassName,
+}: DisclosureRowProps) {
+ const rowExpands = expandable && expandOnRowClick
+ const toggleFromLeading = (event: MouseEvent