( group: GroupAccumulator, )
| 659 | } |
| 660 | |
| 661 | function createCollapsedGroup( |
| 662 | group: GroupAccumulator, |
| 663 | ): CollapsedReadSearchGroup { |
| 664 | const firstMsg = group.messages[0]! |
| 665 | // When file-path-based reads exist, use unique file count (Set.size) only. |
| 666 | // Adding bash operation count on top would double-count — e.g. Read(README.md) |
| 667 | // followed by Bash(wc -l README.md) should still show as 1 file, not 2. |
| 668 | // Fall back to operation count only when there are no file-path reads (bash-only). |
| 669 | const totalReadCount = |
| 670 | group.readFilePaths.size > 0 |
| 671 | ? group.readFilePaths.size |
| 672 | : group.readOperationCount |
| 673 | // memoryReadFilePaths ⊆ readFilePaths (both populated from Read tool calls), |
| 674 | // so this count is safe to subtract from totalReadCount at readCount below. |
| 675 | // Absorbed relevant_memories attachments are NOT in readFilePaths — added |
| 676 | // separately after the subtraction so readCount stays correct. |
| 677 | const toolMemoryReadCount = group.memoryReadFilePaths.size |
| 678 | const memoryReadCount = |
| 679 | toolMemoryReadCount + (group.relevantMemories?.length ?? 0) |
| 680 | // Non-memory read file paths: exclude memory and team memory paths |
| 681 | const teamMemReadPaths = feature('TEAMMEM') |
| 682 | ? group.teamMemoryReadFilePaths |
| 683 | : undefined |
| 684 | const nonMemReadFilePaths = [...group.readFilePaths].filter( |
| 685 | p => |
| 686 | !group.memoryReadFilePaths.has(p) && !(teamMemReadPaths?.has(p) ?? false), |
| 687 | ) |
| 688 | const teamMemSearchCount = feature('TEAMMEM') |
| 689 | ? (group.teamMemorySearchCount ?? 0) |
| 690 | : 0 |
| 691 | const teamMemReadCount = feature('TEAMMEM') |
| 692 | ? (group.teamMemoryReadFilePaths?.size ?? 0) |
| 693 | : 0 |
| 694 | const teamMemWriteCount = feature('TEAMMEM') |
| 695 | ? (group.teamMemoryWriteCount ?? 0) |
| 696 | : 0 |
| 697 | const result: CollapsedReadSearchGroup = { |
| 698 | type: 'collapsed_read_search', |
| 699 | // Subtract memory + team memory counts so regular counts only reflect non-memory operations |
| 700 | searchCount: Math.max( |
| 701 | 0, |
| 702 | group.searchCount - group.memorySearchCount - teamMemSearchCount, |
| 703 | ), |
| 704 | readCount: Math.max( |
| 705 | 0, |
| 706 | totalReadCount - toolMemoryReadCount - teamMemReadCount, |
| 707 | ), |
| 708 | listCount: group.listCount, |
| 709 | // REPL operations are intentionally not collapsed (see isCollapsible: false at line 32), |
| 710 | // so replCount in collapsed groups is always 0. The replCount field is kept for |
| 711 | // sub-agent progress display in AgentTool/UI.tsx which has a separate code path. |
| 712 | replCount: 0, |
| 713 | memorySearchCount: group.memorySearchCount, |
| 714 | memoryReadCount, |
| 715 | memoryWriteCount: group.memoryWriteCount, |
| 716 | readFilePaths: nonMemReadFilePaths, |
| 717 | searchArgs: group.nonMemSearchArgs, |
| 718 | latestDisplayHint: group.latestDisplayHint, |
no test coverage detected