collapseReadSearchGroups method

List collapseReadSearchGroups(
  1. List<RenderableMessage> messages
)

Collapse consecutive Read/Search operations into summary groups.

Implementation

List<dynamic> collapseReadSearchGroups(List<RenderableMessage> messages) {
  final result = <dynamic>[];
  var currentGroup = _GroupAccumulator();
  var deferredSkippable = <RenderableMessage>[];

  void flushGroup() {
    if (currentGroup.messages.isEmpty) return;
    result.add(_createCollapsedGroup(currentGroup));
    for (final deferred in deferredSkippable) {
      result.add(deferred);
    }
    deferredSkippable = [];
    currentGroup = _GroupAccumulator();
  }

  for (final msg in messages) {
    // Check if collapsible tool use.
    final toolInfo = _getCollapsibleToolInfo(msg);
    if (toolInfo != null) {
      if (toolInfo.isMemoryWrite) {
        final count = countToolUses(msg);
        if (teamMemEnabled) {
          currentGroup.teamMemoryWriteCount += count;
        } else {
          currentGroup.memoryWriteCount += count;
        }
      } else if (toolInfo.isAbsorbedSilently) {
        // Absorbed silently.
      } else if (toolInfo.mcpServerName != null) {
        final count = countToolUses(msg);
        currentGroup.mcpCallCount += count;
        currentGroup.mcpServerNames.add(toolInfo.mcpServerName!);
      } else if (fullscreenEnabled && (toolInfo.isBash ?? false)) {
        final count = countToolUses(msg);
        currentGroup.bashCount += count;
        final input = _getToolInput(msg);
        final command = input?['command'] as String?;
        if (command != null) {
          currentGroup.latestDisplayHint = commandAsHint(command);
          for (final id in getToolUseIdsFromMessage(msg)) {
            currentGroup.bashCommands[id] = command;
          }
        }
      } else if (toolInfo.isList) {
        currentGroup.listCount += countToolUses(msg);
      } else if (toolInfo.isSearch) {
        final count = countToolUses(msg);
        currentGroup.searchCount += count;
        if (_isMemorySearch(_getToolInput(msg))) {
          currentGroup.memorySearchCount += count;
        } else {
          final input = _getToolInput(msg);
          final pattern = input?['pattern'] as String?;
          if (pattern != null) {
            currentGroup.nonMemSearchArgs.add(pattern);
            currentGroup.latestDisplayHint = '"$pattern"';
          }
        }
      } else {
        // Read operations.
        final filePaths = getFilePathsFromReadMessage(msg);
        for (final filePath in filePaths) {
          currentGroup.readFilePaths.add(filePath);
          if (isAutoManagedMemoryFile?.call(filePath) ?? false) {
            currentGroup.memoryReadFilePaths.add(filePath);
          }
        }
        if (filePaths.isEmpty) {
          currentGroup.readOperationCount += countToolUses(msg);
          final input = _getToolInput(msg);
          final command = input?['command'] as String?;
          if (command != null) {
            currentGroup.latestDisplayHint = commandAsHint(command);
          }
        }
      }

      for (final id in getToolUseIdsFromMessage(msg)) {
        currentGroup.toolUseIds.add(id);
      }
      currentGroup.messages.add(msg);
    } else if (_isCollapsibleToolResult(msg, currentGroup.toolUseIds)) {
      currentGroup.messages.add(msg);
    } else if (currentGroup.messages.isNotEmpty &&
        isPreToolHookSummary(msg)) {
      currentGroup.hookCount += msg.hookCount;
      currentGroup.hookTotalMs +=
          msg.totalDurationMs ??
          msg.hookInfos.fold<int>(0, (s, h) => s + (h.durationMs ?? 0));
      currentGroup.hookInfos.addAll(msg.hookInfos);
    } else if (currentGroup.messages.isNotEmpty &&
        msg.type == 'attachment' &&
        msg.attachment?.type == 'relevant_memories') {
      currentGroup.relevantMemories ??= [];
      currentGroup.relevantMemories!.addAll(msg.attachment?.memories ?? []);
    } else if (shouldSkipMessage(msg)) {
      if (currentGroup.messages.isNotEmpty &&
          !(msg.type == 'attachment' &&
              msg.attachment?.type == 'nested_memory')) {
        deferredSkippable.add(msg);
      } else {
        result.add(msg);
      }
    } else if (isTextBreaker(msg)) {
      flushGroup();
      result.add(msg);
    } else {
      // Non-collapsible tool use or user message breaks the group.
      flushGroup();
      result.add(msg);
    }
  }

  flushGroup();
  return result;
}