diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx index c0d443de29..5cdb9c6a78 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx @@ -7,10 +7,14 @@ import type { ToolCallData } from '../../../../types' import { getAgentIcon } from '../../utils' import { ToolCallItem } from './tool-call-item' +export type AgentGroupItem = + | { type: 'text'; content: string } + | { type: 'tool'; data: ToolCallData } + interface AgentGroupProps { agentName: string agentLabel: string - tools: ToolCallData[] + items: AgentGroupItem[] autoCollapse?: boolean } @@ -19,14 +23,20 @@ const FADE_MS = 300 export function AgentGroup({ agentName, agentLabel, - tools, + items, autoCollapse = false, }: AgentGroupProps) { const AgentIcon = getAgentIcon(agentName) - const hasTools = tools.length > 0 + const hasItems = items.length > 0 + const toolItems = items.filter( + (item): item is Extract => item.type === 'tool' + ) const allDone = - hasTools && - tools.every((t) => t.status === 'success' || t.status === 'error' || t.status === 'cancelled') + toolItems.length > 0 && + toolItems.every( + (t) => + t.data.status === 'success' || t.data.status === 'error' || t.data.status === 'cancelled' + ) const [expanded, setExpanded] = useState(!allDone) const [mounted, setMounted] = useState(!allDone) @@ -49,39 +59,55 @@ export function AgentGroup({ return (
- - {hasTools && mounted && ( + + ) : ( +
+
+ +
+ {agentLabel} +
+ )} + {hasItems && mounted && (
- {tools.map((tool) => ( - - ))} + {items.map((item, idx) => + item.type === 'tool' ? ( + + ) : ( +

+ {item.content.trim()} +

+ ) + )}
)}
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/index.ts b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/index.ts index 57eb4099b2..7a62a95791 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/index.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/index.ts @@ -1,2 +1,3 @@ +export type { AgentGroupItem } from './agent-group' export { AgentGroup } from './agent-group' export { CircleStop } from './tool-call-item' diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx index db2cc4fd92..e4b2c68a24 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx @@ -53,16 +53,16 @@ export function ToolCallItem({ toolName, displayTitle, status }: ToolCallItemPro
{status === 'executing' ? ( - + ) : status === 'cancelled' ? ( - + ) : Icon ? ( - + ) : ( - + )}
- {displayTitle} + {displayTitle}
) } diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/index.ts b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/index.ts index a69eb6ce8b..67b1b0fd82 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/index.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/index.ts @@ -1,3 +1,4 @@ +export type { AgentGroupItem } from './agent-group' export { AgentGroup, CircleStop } from './agent-group' export { ChatContent } from './chat-content' export { Options } from './options' diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/message-content.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/message-content.tsx index 5a37b94ac3..d4f7430478 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/message-content.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/message-content.tsx @@ -2,6 +2,7 @@ import type { ContentBlock, OptionItem, SubagentName, ToolCallData } from '../../types' import { SUBAGENT_LABELS } from '../../types' +import type { AgentGroupItem } from './components' import { AgentGroup, ChatContent, CircleStop, Options } from './components' interface TextSegment { @@ -14,7 +15,7 @@ interface AgentGroupSegment { id: string agentName: string agentLabel: string - tools: ToolCallData[] + items: AgentGroupItem[] } interface OptionsSegment { @@ -64,7 +65,18 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] { for (let i = 0; i < blocks.length; i++) { const block = blocks[i] - if (block.type === 'text' || block.type === 'subagent_text') { + if (block.type === 'subagent_text') { + if (!block.content || !group) continue + const lastItem = group.items[group.items.length - 1] + if (lastItem?.type === 'text') { + lastItem.content += block.content + } else { + group.items.push({ type: 'text', content: block.content }) + } + continue + } + + if (block.type === 'text') { if (!block.content?.trim()) continue if (group) { segments.push(group) @@ -92,7 +104,7 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] { id: `agent-${key}-${i}`, agentName: key, agentLabel: resolveAgentLabel(key), - tools: [], + items: [], } continue } @@ -113,7 +125,7 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] { id: `agent-${tc.name}-${i}`, agentName: tc.name, agentLabel: resolveAgentLabel(tc.name), - tools: [], + items: [], } } continue @@ -122,7 +134,7 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] { const tool = toToolData(tc) if (tc.calledBy && group && group.agentName === tc.calledBy) { - group.tools.push(tool) + group.items.push({ type: 'tool', data: tool }) } else if (tc.calledBy) { if (group) { segments.push(group) @@ -133,11 +145,11 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] { id: `agent-${tc.calledBy}-${i}`, agentName: tc.calledBy, agentLabel: resolveAgentLabel(tc.calledBy), - tools: [tool], + items: [{ type: 'tool', data: tool }], } } else { if (group && group.agentName === 'mothership') { - group.tools.push(tool) + group.items.push({ type: 'tool', data: tool }) } else { if (group) { segments.push(group) @@ -148,7 +160,7 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] { id: `agent-mothership-${i}`, agentName: 'mothership', agentLabel: 'Mothership', - tools: [tool], + items: [{ type: 'tool', data: tool }], } } } @@ -216,10 +228,15 @@ export function MessageContent({ /> ) case 'agent_group': { + const toolItems = segment.items.filter((item) => item.type === 'tool') const allToolsDone = - segment.tools.length > 0 && - segment.tools.every( - (t) => t.status === 'success' || t.status === 'error' || t.status === 'cancelled' + toolItems.length === 0 || + toolItems.every( + (t) => + t.type === 'tool' && + (t.data.status === 'success' || + t.data.status === 'error' || + t.data.status === 'cancelled') ) const hasFollowingText = segments.slice(i + 1).some((s) => s.type === 'text') return ( @@ -227,7 +244,7 @@ export function MessageContent({ diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/utils.ts b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/utils.ts index 655a8304a3..610ed9ffe6 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/utils.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/utils.ts @@ -6,12 +6,12 @@ import { Bug, Calendar, ClipboardList, - Connections, Database, File, FolderCode, Hammer, Integration, + Layout, Library, Pencil, PlayOutline, @@ -42,7 +42,7 @@ const TOOL_ICONS: Record { + const last = blocks[blocks.length - 1] + if (last?.type === 'subagent_text') return last + const b: ContentBlock = { type: 'subagent_text', content: '' } + blocks.push(b) + return b + } + const flush = () => { streamingBlocksRef.current = [...blocks] setMessages((prev) => @@ -450,10 +458,9 @@ export function useChat( lastContentSource !== contentSource && runningText.length > 0 && !runningText.endsWith('\n') - const tb = ensureTextBlock() - const normalizedChunk = needsBoundaryNewline ? `\n${chunk}` : chunk - tb.content = (tb.content ?? '') + normalizedChunk - runningText += normalizedChunk + const tb = activeSubagent ? ensureSubagentTextBlock() : ensureTextBlock() + tb.content = (tb.content ?? '') + chunk + runningText += needsBoundaryNewline ? `\n${chunk}` : chunk lastContentSource = contentSource streamingContentRef.current = runningText flush() diff --git a/apps/sim/app/workspace/[workspaceId]/home/types.ts b/apps/sim/app/workspace/[workspaceId]/home/types.ts index 68a9821eb1..a5f17ba800 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/types.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/types.ts @@ -72,6 +72,7 @@ export type MothershipToolName = | 'plan' | 'debug' | 'edit' + | 'fast_edit' /** * Subagent identifiers dispatched via `subagent_start` SSE events. @@ -93,6 +94,7 @@ export type SubagentName = | 'plan' | 'debug' | 'edit' + | 'fast_edit' export type ToolPhase = | 'workspace' @@ -179,6 +181,7 @@ export const SUBAGENT_LABELS: Record = { plan: 'Plan agent', debug: 'Debug agent', edit: 'Edit agent', + fast_edit: 'Edit agent', } as const export interface ToolUIMetadata { @@ -223,6 +226,7 @@ export const TOOL_UI_METADATA: Partial