collapseReadSearchGroups method
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;
}