| 789 | * - Breaks groups when assistant text appears |
| 790 | */ |
| 791 | export function collapseReadSearchGroups( |
| 792 | messages: RenderableMessage[], |
| 793 | tools: Tools, |
| 794 | behavior?: CollapseBehavior, |
| 795 | ): RenderableMessage[] { |
| 796 | const result: RenderableMessage[] = [] |
| 797 | let currentGroup = createEmptyGroup() |
| 798 | let deferredSkippable: RenderableMessage[] = [] |
| 799 | |
| 800 | function flushGroup(): void { |
| 801 | if (currentGroup.messages.length === 0) { |
| 802 | return |
| 803 | } |
| 804 | result.push(createCollapsedGroup(currentGroup)) |
| 805 | for (const deferred of deferredSkippable) { |
| 806 | result.push(deferred) |
| 807 | } |
| 808 | deferredSkippable = [] |
| 809 | currentGroup = createEmptyGroup() |
| 810 | } |
| 811 | |
| 812 | for (const msg of messages) { |
| 813 | if (isCollapsibleToolUse(msg, tools, behavior)) { |
| 814 | // This is a collapsible tool use - type predicate narrows to CollapsibleMessage |
| 815 | const toolInfo = getCollapsibleToolInfo(msg, tools, behavior)! |
| 816 | |
| 817 | if (toolInfo.isMemoryWrite) { |
| 818 | // Memory file write/edit — check if it's team memory |
| 819 | const count = countToolUses(msg) |
| 820 | if ( |
| 821 | feature('TEAMMEM') && |
| 822 | teamMemOps?.isTeamMemoryWriteOrEdit(toolInfo.name, toolInfo.input) |
| 823 | ) { |
| 824 | currentGroup.teamMemoryWriteCount = |
| 825 | (currentGroup.teamMemoryWriteCount ?? 0) + count |
| 826 | } else { |
| 827 | currentGroup.memoryWriteCount += count |
| 828 | } |
| 829 | } else if (toolInfo.isAbsorbedSilently) { |
| 830 | // Snip/ToolSearch absorbed silently — no count, no summary text. |
| 831 | // Hidden from the default view but still shown in verbose mode |
| 832 | // (Ctrl+O) via the groupMessages iteration in CollapsedReadSearchContent. |
| 833 | } else if (toolInfo.mcpServerName) { |
| 834 | // MCP search/read — counted separately so the summary says |
| 835 | // "Queried slack N times" instead of "Read N files". |
| 836 | const count = countToolUses(msg) |
| 837 | currentGroup.mcpCallCount = (currentGroup.mcpCallCount ?? 0) + count |
| 838 | currentGroup.mcpServerNames?.add(toolInfo.mcpServerName) |
| 839 | const input = toolInfo.input as { query?: string } | undefined |
| 840 | if (input?.query) { |
| 841 | currentGroup.latestDisplayHint = `"${input.query}"` |
| 842 | } |
| 843 | } else if (isFullscreenEnvEnabled() && toolInfo.isBash) { |
| 844 | // Non-search/read Bash command — counted separately so the summary |
| 845 | // says "Ran N bash commands" instead of breaking the group. |
| 846 | const count = countToolUses(msg) |
| 847 | currentGroup.bashCount = (currentGroup.bashCount ?? 0) + count |
| 848 | const input = toolInfo.input as { command?: string } | undefined |