+ )
+}
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group-view.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group-view.tsx
index 0b8d1f3886e..412b08b7fe3 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group-view.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group-view.tsx
@@ -1,22 +1,17 @@
'use client'
-import {
- type ComponentType,
- type ReactNode,
- useEffect,
- useLayoutEffect,
- useMemo,
- useRef,
- useState,
-} from 'react'
-import { ChevronDown, cn, Expandable, ExpandableContent, OverflowText } from '@sim/emcn'
-import { ShimmerText } from '@/components/ui'
+import { type ComponentType, type ReactNode, useId, useMemo, useState } from 'react'
+import { ChevronDown, cn, Expandable, ExpandableContent } from '@sim/emcn'
+import { ActivityStatus } from '@/components/ui/activity-status'
import { isBrowserAgentAvailable } from '@/lib/browser-agent/transport'
import { RETIRED_BROWSER_REQUEST_TAKEOVER_ID } from '@/lib/copilot/tools/retired-tools'
+import { ActivityViewport } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/activity-viewport'
import { BrowserAgentIcon } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/browser-agent-icon'
import { renderInlineMarkdown } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/inline-markdown'
-import { getVisibleMainAgentItems } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/main-agent-activity'
+import { MainAgentActivity } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/main-agent-activity'
+import { getToolActivitySummary } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-group'
import type { ToolCallItemProps } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item'
+import { needsToolInput } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-interactions'
import {
getAgentIcon,
isToolDone,
@@ -53,7 +48,7 @@ export interface AgentGroupProps {
isLaneOpen?: boolean
/** Opens a subagent group on first render. */
defaultExpanded?: boolean
- /** Keeps the activity viewport anchored at the top while new rows stream in. */
+ /** Follows incoming activity until the user scrolls up. */
autoScrollActivity?: boolean
}
@@ -78,12 +73,12 @@ function collectGroupTools(items: AgentGroupItem[]): ToolCallData[] {
return tools
}
-/** True when any row in this group (or a nested one) is waiting on a permission decision. */
-function hasAwaitingApproval(items: AgentGroupItem[]): boolean {
+/** Reveal blocking interactions even when a parent group was manually collapsed. */
+function hasPendingInteraction(items: AgentGroupItem[]): boolean {
return items.some((item) => {
- if (item.type === 'tool') return item.data.status === ToolCallStatus.awaiting_approval
+ if (item.type === 'tool') return needsToolInput(item.data)
// Text rows carry no tool calls, so only nested groups need recursing into.
- return item.type === 'agent_group' ? hasAwaitingApproval(item.group.items) : false
+ return item.type === 'agent_group' ? hasPendingInteraction(item.group.items) : false
})
}
@@ -157,6 +152,7 @@ export function AgentGroupView({
ToolCallComponent,
renderBrowserTakeover,
}: AgentGroupViewProps) {
+ const contentId = useId()
const AgentIcon = getAgentIcon(agentName)
const agentIcon =
agentName === 'browser' ? (
@@ -165,14 +161,7 @@ export function AgentGroupView({
)
const isMainAgent = agentName === 'mothership'
- // Collapsed status line: the latest tool call, always in its RUNNING
- // phrasing — it never flips to the completed rewrite (that lives in the
- // expanded log). Work delegated further down bubbles up, so a group whose
- // own turn is idle still narrates what its nested agent is doing rather
- // than freezing on its last own tool. With several tools running at any
- // depth, the most recently started wins and the rest become "+ n"; between
- // rounds the last tool's title stays frozen; a closed lane shows the bare
- // name.
+ /** Open lanes surface their latest work, including work delegated to nested agents. */
const status = useMemo(() => {
if (isMainAgent || !isLaneOpen) return undefined
const tools = collectGroupTools(items)
@@ -187,7 +176,12 @@ export function AgentGroupView({
const last = tools.at(-1)
return last ? toolStatusTitle(last) : undefined
}, [isLaneOpen, isMainAgent, items])
- const headerText = status ? `${agentLabel} — ${status}` : agentLabel
+ const completedTools = !isMainAgent && !isLaneOpen ? collectGroupTools(items) : []
+ const headerText = status
+ ? `${agentLabel} — ${status}`
+ : completedTools.length > 0
+ ? `${agentLabel} — ${getToolActivitySummary(completedTools)}`
+ : agentLabel
const hasItems = items.length > 0
const resolved = isAgentGroupResolved(items)
const browserAgentAvailable = isBrowserAgentAvailable()
@@ -199,11 +193,12 @@ export function AgentGroupView({
const [manualExpanded, setManualExpanded] = useState(defaultExpanded)
const [expandedTakeoverId, setExpandedTakeoverId] = useState(null)
+ const pendingInteraction = hasPendingInteraction(items)
// An outstanding permission prompt overrides a manual collapse: the turn
// cannot proceed until it is answered, so hiding it would deadlock the chat
// with nothing on screen to explain why.
const expanded =
- hasAwaitingApproval(items) ||
+ pendingInteraction ||
nestedBrowserTakeover ||
(activeBrowserTakeover ? expandedTakeoverId === activeBrowserTakeover.id : manualExpanded)
@@ -215,49 +210,64 @@ export function AgentGroupView({
setManualExpanded(!expanded)
}
- const visibleItems = isMainAgent ? getVisibleMainAgentItems(items) : items
- const activity = (
-