extractReadFilesFromMessages function
Extract read files from messages to populate the file state cache.
Implementation
FileStateCache extractReadFilesFromMessages(
List<Map<String, dynamic>> messages,
String cwd, {
int maxSize = _askReadFileStateCacheSize,
}) {
final cache = createFileStateCacheWithSizeLimit(maxSize);
// First pass: find tool_use blocks.
final fileReadToolUseIds = <String, String>{}; // toolUseId -> filePath
final fileWriteToolUseIds =
<String, ({String filePath, String content})>{}; // toolUseId -> data
final fileEditToolUseIds = <String, String>{}; // toolUseId -> filePath
for (final message in messages) {
if (message['type'] != 'assistant') continue;
final content = message['message']?['content'];
if (content is! List) continue;
for (final block in content) {
if (block is! Map<String, dynamic> || block['type'] != 'tool_use') {
continue;
}
final name = block['name'] as String?;
final input = block['input'] as Map<String, dynamic>?;
final id = block['id'] as String?;
if (id == null || input == null) continue;
if (name == 'FileRead') {
final path = input['file_path'] as String?;
if (path != null && input['offset'] == null && input['limit'] == null) {
fileReadToolUseIds[id] = _expandPath(path, cwd);
}
} else if (name == 'FileWrite') {
final path = input['file_path'] as String?;
final writeContent = input['content'] as String?;
if (path != null && writeContent != null) {
fileWriteToolUseIds[id] = (
filePath: _expandPath(path, cwd),
content: writeContent,
);
}
} else if (name == 'FileEdit') {
final path = input['file_path'] as String?;
if (path != null) {
fileEditToolUseIds[id] = _expandPath(path, cwd);
}
}
}
}
// Second pass: find corresponding tool results.
for (final message in messages) {
if (message['type'] != 'user') continue;
final content = message['message']?['content'];
if (content is! List) continue;
for (final block in content) {
if (block is! Map<String, dynamic> || block['type'] != 'tool_result') {
continue;
}
final toolUseId = block['tool_use_id'] as String?;
if (toolUseId == null) continue;
// Handle Read tool results.
final readFilePath = fileReadToolUseIds[toolUseId];
if (readFilePath != null) {
final resultContent = block['content'] as String?;
if (resultContent != null &&
!resultContent.startsWith(_fileUnchangedStub)) {
// Remove system-reminder blocks.
final processed = resultContent.replaceAll(
RegExp(r'<system-reminder>[\s\S]*?<\/system-reminder>'),
'',
);
final fileContent = processed
.split('\n')
.map(_stripLineNumberPrefix)
.join('\n')
.trim();
final timestamp = message['timestamp'] as String?;
if (timestamp != null) {
final ts = DateTime.parse(timestamp).millisecondsSinceEpoch;
cache.set(
readFilePath,
FileStateCacheEntry(content: fileContent, timestamp: ts),
);
}
}
}
// Handle Write tool results.
final writeData = fileWriteToolUseIds[toolUseId];
if (writeData != null) {
final timestamp = message['timestamp'] as String?;
if (timestamp != null) {
final ts = DateTime.parse(timestamp).millisecondsSinceEpoch;
cache.set(
writeData.filePath,
FileStateCacheEntry(content: writeData.content, timestamp: ts),
);
}
}
// Handle Edit tool results — read from disk.
final editFilePath = fileEditToolUseIds[toolUseId];
if (editFilePath != null && block['is_error'] != true) {
try {
final file = File(editFilePath);
final diskContent = file.readAsStringSync();
final mtime = file.statSync().modified.millisecondsSinceEpoch;
cache.set(
editFilePath,
FileStateCacheEntry(content: diskContent, timestamp: mtime),
);
} catch (_) {
// File deleted or inaccessible.
}
}
}
}
return cache;
}