import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'multi_agent_orchestrator.dart'; import 'runtime_models.dart'; class AgentCliBridgeRequest { const AgentCliBridgeRequest({ required this.sessionId, required this.taskPrompt, required this.workingDirectory, required this.attachments, required this.selectedSkills, required this.aiGatewayBaseUrl, required this.aiGatewayApiKey, }); final String sessionId; final String taskPrompt; final String workingDirectory; final List attachments; final List selectedSkills; final String aiGatewayBaseUrl; final String aiGatewayApiKey; } class AgentCliBridgeResult { const AgentCliBridgeResult({ required this.output, required this.success, required this.errorMessage, this.events = const [], }); final String output; final bool success; final String errorMessage; final List events; } abstract class AgentCliBridge { Future run(AgentCliBridgeRequest request); } class SubprocessCliBridge implements AgentCliBridge { const SubprocessCliBridge({ required this.command, this.defaultArgs = const [], }); final String command; final List defaultArgs; @override Future run(AgentCliBridgeRequest request) async { try { final process = await Process.start( command, [...defaultArgs, request.taskPrompt], workingDirectory: request.workingDirectory.trim().isEmpty ? null : request.workingDirectory, ); await process.stdin.close(); final stdout = await process.stdout.transform(utf8.decoder).join(); final stderr = await process.stderr.transform(utf8.decoder).join(); final exitCode = await process.exitCode; return AgentCliBridgeResult( output: stdout.trim(), success: exitCode == 0, errorMessage: stderr.trim(), ); } catch (error) { return AgentCliBridgeResult( output: '', success: false, errorMessage: error.toString(), ); } } } class JsonRpcCliBridge implements AgentCliBridge { const JsonRpcCliBridge(this.endpoint); final Uri endpoint; @override Future run(AgentCliBridgeRequest request) async { final socket = await WebSocket.connect(endpoint.toString()); final requestId = DateTime.now().microsecondsSinceEpoch.toString(); final completer = Completer(); final events = []; socket.listen( (raw) { final json = jsonDecode(raw as String) as Map; final method = json['method'] as String?; if (method == 'multi_agent.event') { final params = (json['params'] as Map?)?.cast() ?? const {}; events.add(MultiAgentRunEvent.fromJson(params)); return; } if (json['id']?.toString() == requestId && json['result'] is Map) { final result = (json['result'] as Map).cast(); if (!completer.isCompleted) { completer.complete( AgentCliBridgeResult( output: result['summary']?.toString() ?? '', success: result['success'] == true, errorMessage: result['error']?.toString() ?? '', events: events, ), ); } unawaited(socket.close()); return; } if (json['error'] is Map && !completer.isCompleted) { final error = (json['error'] as Map).cast(); completer.complete( AgentCliBridgeResult( output: '', success: false, errorMessage: error['message']?.toString() ?? 'JSON-RPC error', events: events, ), ); unawaited(socket.close()); } }, onError: (error, _) { if (!completer.isCompleted) { completer.complete( AgentCliBridgeResult( output: '', success: false, errorMessage: error.toString(), events: events, ), ); } }, onDone: () { if (!completer.isCompleted) { completer.complete( AgentCliBridgeResult( output: '', success: false, errorMessage: 'JSON-RPC bridge closed before completion', events: events, ), ); } }, cancelOnError: true, ); socket.add( jsonEncode({ 'jsonrpc': '2.0', 'id': requestId, 'method': 'session.start', 'params': { 'sessionId': request.sessionId, 'taskPrompt': request.taskPrompt, 'workingDirectory': request.workingDirectory, 'attachments': request.attachments .map( (item) => { 'name': item.name, 'description': item.description, 'path': item.path, }, ) .toList(growable: false), 'selectedSkills': request.selectedSkills, 'aiGatewayBaseUrl': request.aiGatewayBaseUrl, 'aiGatewayApiKey': request.aiGatewayApiKey, }, }), ); return completer.future; } }