class AgUiEventTypeWire { static const runStarted = 'RUN_STARTED'; static const runFinished = 'RUN_FINISHED'; static const runError = 'RUN_ERROR'; static const stepStarted = 'STEP_STARTED'; static const stepFinished = 'STEP_FINISHED'; static const textMessageEnd = 'TEXT_MESSAGE_END'; static const toolCallStart = 'TOOL_CALL_START'; static const toolCallArgs = 'TOOL_CALL_ARGS'; static const toolCallEnd = 'TOOL_CALL_END'; static const toolCallResult = 'TOOL_CALL_RESULT'; static const toolCallError = 'TOOL_CALL_ERROR'; } enum AgUiEventType { runStarted, runFinished, runError, stepStarted, stepFinished, textMessageEnd, toolCallStart, toolCallArgs, toolCallEnd, toolCallResult, toolCallError, unknown, } const _wireToTypeMap = { AgUiEventTypeWire.runStarted: AgUiEventType.runStarted, AgUiEventTypeWire.runFinished: AgUiEventType.runFinished, AgUiEventTypeWire.runError: AgUiEventType.runError, AgUiEventTypeWire.stepStarted: AgUiEventType.stepStarted, AgUiEventTypeWire.stepFinished: AgUiEventType.stepFinished, AgUiEventTypeWire.textMessageEnd: AgUiEventType.textMessageEnd, AgUiEventTypeWire.toolCallStart: AgUiEventType.toolCallStart, AgUiEventTypeWire.toolCallArgs: AgUiEventType.toolCallArgs, AgUiEventTypeWire.toolCallEnd: AgUiEventType.toolCallEnd, AgUiEventTypeWire.toolCallResult: AgUiEventType.toolCallResult, AgUiEventTypeWire.toolCallError: AgUiEventType.toolCallError, }; const _typeToWireMap = { AgUiEventType.runStarted: AgUiEventTypeWire.runStarted, AgUiEventType.runFinished: AgUiEventTypeWire.runFinished, AgUiEventType.runError: AgUiEventTypeWire.runError, AgUiEventType.stepStarted: AgUiEventTypeWire.stepStarted, AgUiEventType.stepFinished: AgUiEventTypeWire.stepFinished, AgUiEventType.textMessageEnd: AgUiEventTypeWire.textMessageEnd, AgUiEventType.toolCallStart: AgUiEventTypeWire.toolCallStart, AgUiEventType.toolCallArgs: AgUiEventTypeWire.toolCallArgs, AgUiEventType.toolCallEnd: AgUiEventTypeWire.toolCallEnd, AgUiEventType.toolCallResult: AgUiEventTypeWire.toolCallResult, AgUiEventType.toolCallError: AgUiEventTypeWire.toolCallError, AgUiEventType.unknown: '', }; AgUiEventType agUiEventTypeFromWire(String wire) => _wireToTypeMap[wire] ?? AgUiEventType.unknown; String agUiEventTypeToWire(AgUiEventType type) => _typeToWireMap[type] ?? ''; abstract class AgUiEvent { const AgUiEvent({required this.type}); final AgUiEventType type; factory AgUiEvent.fromJson(Map json) { final wireType = json['type']; final type = wireType is String ? agUiEventTypeFromWire(wireType) : AgUiEventType.unknown; return switch (type) { AgUiEventType.runStarted => RunStartedEvent.fromJson(json), AgUiEventType.runFinished => RunFinishedEvent.fromJson(json), AgUiEventType.runError => RunErrorEvent.fromJson(json), AgUiEventType.stepStarted => StepStartedEvent.fromJson(json), AgUiEventType.stepFinished => StepFinishedEvent.fromJson(json), AgUiEventType.textMessageEnd => TextMessageEndEvent.fromJson(json), AgUiEventType.toolCallStart => ToolCallStartEvent.fromJson(json), AgUiEventType.toolCallArgs => ToolCallArgsEvent.fromJson(json), AgUiEventType.toolCallEnd => ToolCallEndEvent.fromJson(json), AgUiEventType.toolCallResult => ToolCallResultEvent.fromJson(json), AgUiEventType.toolCallError => ToolCallErrorEvent.fromJson(json), AgUiEventType.unknown => UnknownAgUiEvent(rawJson: json), }; } } class UnknownAgUiEvent extends AgUiEvent { const UnknownAgUiEvent({required this.rawJson}) : super(type: AgUiEventType.unknown); final Map rawJson; } class RunStartedEvent extends AgUiEvent { RunStartedEvent({required this.threadId, required this.runId}) : super(type: AgUiEventType.runStarted); final String threadId; final String runId; factory RunStartedEvent.fromJson(Map json) => RunStartedEvent( threadId: _asString(json['threadId']), runId: _asString(json['runId']), ); } class RunFinishedEvent extends AgUiEvent { RunFinishedEvent({required this.threadId, required this.runId}) : super(type: AgUiEventType.runFinished); final String threadId; final String runId; factory RunFinishedEvent.fromJson(Map json) => RunFinishedEvent( threadId: _asString(json['threadId']), runId: _asString(json['runId']), ); } class RunErrorEvent extends AgUiEvent { RunErrorEvent({required this.message, this.code}) : super(type: AgUiEventType.runError); final String message; final String? code; factory RunErrorEvent.fromJson(Map json) => RunErrorEvent( message: _asString(json['message'], fallback: 'Unknown error'), code: json['code'] as String?, ); } class StepStartedEvent extends AgUiEvent { StepStartedEvent({required this.stepName}) : super(type: AgUiEventType.stepStarted); final String stepName; factory StepStartedEvent.fromJson(Map json) => StepStartedEvent(stepName: _asString(json['stepName'])); } class StepFinishedEvent extends AgUiEvent { StepFinishedEvent({required this.stepName}) : super(type: AgUiEventType.stepFinished); final String stepName; factory StepFinishedEvent.fromJson(Map json) => StepFinishedEvent(stepName: _asString(json['stepName'])); } class TextMessageEndEvent extends AgUiEvent { TextMessageEndEvent({ required this.messageId, required this.answer, required this.role, required this.status, required this.uiSchema, }) : super(type: AgUiEventType.textMessageEnd); final String messageId; final String answer; final String role; final String status; final Map? uiSchema; factory TextMessageEndEvent.fromJson(Map json) => TextMessageEndEvent( messageId: _asString(json['messageId']), answer: _asString(json['answer']), role: _asString(json['role'], fallback: 'assistant'), status: _asString(json['status'], fallback: 'success'), uiSchema: _asMap(json['ui_schema']), ); } class ToolCallStartEvent extends AgUiEvent { ToolCallStartEvent({required this.toolCallId, required this.toolCallName}) : super(type: AgUiEventType.toolCallStart); final String toolCallId; final String toolCallName; factory ToolCallStartEvent.fromJson(Map json) => ToolCallStartEvent( toolCallId: _asString(json['toolCallId']), toolCallName: _asString(json['toolCallName']), ); } class ToolCallArgsEvent extends AgUiEvent { ToolCallArgsEvent({required this.toolCallId, required this.args}) : super(type: AgUiEventType.toolCallArgs); final String toolCallId; final Map args; factory ToolCallArgsEvent.fromJson(Map json) => ToolCallArgsEvent( toolCallId: _asString(json['toolCallId']), args: _asMap(json['args']) ?? const {}, ); } class ToolCallEndEvent extends AgUiEvent { ToolCallEndEvent({required this.toolCallId}) : super(type: AgUiEventType.toolCallEnd); final String toolCallId; factory ToolCallEndEvent.fromJson(Map json) => ToolCallEndEvent(toolCallId: _asString(json['toolCallId'])); } class ToolCallResultEvent extends AgUiEvent { ToolCallResultEvent({ required this.messageId, required this.toolCallId, required this.toolName, required this.resultSummary, required this.status, required this.uiSchema, }) : super(type: AgUiEventType.toolCallResult); final String messageId; final String toolCallId; final String toolName; final String resultSummary; final String status; final Map? uiSchema; factory ToolCallResultEvent.fromJson(Map json) => ToolCallResultEvent( messageId: _asString(json['messageId']), toolCallId: _asString(json['tool_call_id']), toolName: _asString(json['tool_name']), resultSummary: _asString(json['result']), status: _asString(json['status'], fallback: 'success'), uiSchema: _asMap(json['ui_schema']), ); } class ToolCallErrorEvent extends AgUiEvent { ToolCallErrorEvent({required this.toolCallId, required this.error, this.code}) : super(type: AgUiEventType.toolCallError); final String toolCallId; final String error; final String? code; factory ToolCallErrorEvent.fromJson(Map json) => ToolCallErrorEvent( toolCallId: _asString(json['toolCallId']), error: _asString(json['error'], fallback: 'Tool call failed'), code: json['code'] as String?, ); } class HistorySnapshot { const HistorySnapshot({ required this.scope, required this.threadId, required this.day, required this.hasMore, required this.messages, }); final String scope; final String? threadId; final String? day; final bool hasMore; final List messages; factory HistorySnapshot.fromJson(Map json) { final rawMessages = json['messages']; final messages = rawMessages is List ? rawMessages .whereType>() .map(HistoryMessage.fromJson) .toList() : const []; return HistorySnapshot( scope: _asString(json['scope'], fallback: 'history_day'), threadId: json['threadId'] as String?, day: json['day'] as String?, hasMore: json['hasMore'] == true, messages: messages, ); } } class HistoryMessage { const HistoryMessage({ required this.id, required this.seq, required this.role, required this.content, required this.timestamp, this.attachments = const [], this.uiSchema, }); final String id; final int seq; final String role; final String content; final DateTime timestamp; final List attachments; final Map? uiSchema; factory HistoryMessage.fromJson(Map json) => HistoryMessage( id: _asString(json['id']), seq: _asInt(json['seq']), role: _asString(json['role']), content: _asString(json['content']), timestamp: DateTime.tryParse(_asString(json['timestamp'])) ?? DateTime.now(), attachments: _parseHistoryAttachments(json['attachments']), uiSchema: _asMap(json['ui_schema']), ); } class HistoryAttachment { const HistoryAttachment({required this.url, required this.mimeType}); final String url; final String mimeType; factory HistoryAttachment.fromJson(Map json) { return HistoryAttachment( url: _asString(json['url']), mimeType: _asString(json['mimeType']), ); } } String _asString(Object? value, {String fallback = ''}) { if (value is String) { return value; } return fallback; } int _asInt(Object? value) { if (value is int) { return value; } if (value is double) { return value.toInt(); } if (value is String) { return int.tryParse(value) ?? 0; } return 0; } Map? _asMap(Object? value) { if (value is Map) { return value; } if (value is Map) { final result = {}; for (final entry in value.entries) { final key = entry.key; if (key is String) { result[key] = entry.value; } } return result; } return null; } List _parseHistoryAttachments(Object? value) { if (value is! List) { return const []; } return value .whereType>() .map(HistoryAttachment.fromJson) .where( (attachment) => attachment.url.isNotEmpty && attachment.mimeType.isNotEmpty, ) .toList(); }