normalizeMessage function

Iterable<Map<String, dynamic>> normalizeMessage(
  1. Map<String, dynamic> message, {
  2. required String sessionId,
})

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