normalizeMessage function
Normalize a single message for SDK output.
Implementation
Iterable<Map<String, dynamic>> normalizeMessage(
Map<String, dynamic> message, {
required String sessionId,
}) sync* {
final type = message['type'] as String?;
switch (type) {
case 'assistant':
yield {
'type': 'assistant',
'message': message['message'],
'parent_tool_use_id': null,
'session_id': sessionId,
'uuid': message['uuid'],
if (message.containsKey('error')) 'error': message['error'],
};
case 'progress':
final data = message['data'] as Map<String, dynamic>?;
if (data == null) break;
final progressType = data['type'] as String?;
if (progressType == 'agent_progress' ||
progressType == 'skill_progress') {
final innerMsg = data['message'] as Map<String, dynamic>?;
if (innerMsg == null) break;
final innerType = innerMsg['type'] as String?;
if (innerType == 'assistant') {
yield {
'type': 'assistant',
'message': innerMsg['message'],
'parent_tool_use_id': message['parentToolUseID'],
'session_id': sessionId,
'uuid': innerMsg['uuid'],
if (innerMsg.containsKey('error')) 'error': innerMsg['error'],
};
} else if (innerType == 'user') {
yield {
'type': 'user',
'message': innerMsg['message'],
'parent_tool_use_id': message['parentToolUseID'],
'session_id': sessionId,
'uuid': innerMsg['uuid'],
'timestamp': innerMsg['timestamp'],
'isSynthetic':
innerMsg['isMeta'] == true ||
innerMsg['isVisibleInTranscriptOnly'] == true,
'tool_use_result': innerMsg['toolUseResult'],
};
}
} else if (progressType == 'bash_progress' ||
progressType == 'powershell_progress') {
// Throttle: only emit one every 30 seconds.
final trackingKey = message['parentToolUseID'] as String? ?? '';
final now = DateTime.now().millisecondsSinceEpoch;
final lastSent = _toolProgressLastSentTime[trackingKey] ?? 0;
if (now - lastSent >= _toolProgressThrottleMs) {
// Evict oldest entry if at capacity.
if (_toolProgressLastSentTime.length >=
_maxToolProgressTrackingEntries) {
_toolProgressLastSentTime.remove(
_toolProgressLastSentTime.keys.first,
);
}
_toolProgressLastSentTime[trackingKey] = now;
yield {
'type': 'tool_progress',
'tool_use_id': message['toolUseID'],
'tool_name': progressType == 'bash_progress'
? 'Bash'
: 'PowerShell',
'parent_tool_use_id': message['parentToolUseID'],
'elapsed_time_seconds': data['elapsedTimeSeconds'],
'task_id': data['taskId'],
'session_id': sessionId,
'uuid': message['uuid'],
};
}
}
case 'user':
yield {
'type': 'user',
'message': message['message'],
'parent_tool_use_id': null,
'session_id': sessionId,
'uuid': message['uuid'],
'timestamp': message['timestamp'],
'isSynthetic':
message['isMeta'] == true ||
message['isVisibleInTranscriptOnly'] == true,
'tool_use_result': message['toolUseResult'],
};
default:
// yield nothing.
break;
}
}