// Agent execution engine // Spawns and manages agent instances import 'dart:async'; import '../api/openrouter_client.dart'; import '../chat/tool_loop_service.dart'; import '../local_state.dart'; import 'agent_context.dart'; /// Executes a single agent class AgentExecutor { static final AgentExecutor _instance = AgentExecutor._internal(); factory AgentExecutor() => _instance; AgentExecutor._internal(); final Map _agents = {}; int _idCounter = 1; /// Spawn and run an agent Future spawnAgent({ required AgentDefinition definition, required AgentContext context, required String task, required String apiKey, required String model, }) async { final agentId = 'agent_${_idCounter++}'; final agent = _RunningAgent( id: agentId, definition: definition, context: context.createChildContext(childAgentId: agentId), task: task, apiKey: apiKey, model: model, ); _agents[agentId] = agent; // Start agent in background agent.run().then((result) { // Agent completed context.recordSubAgentResult(agentId, result); }).catchError((e) { print('Agent $agentId failed: $e'); context.recordSubAgentResult( agentId, AgentResult.failure(agentId: agentId, error: e.toString()), ); }); return agentId; } /// Get agent result (non-blocking) AgentResult? getResult(String agentId) { final agent = _agents[agentId]; if (agent == null) { return null; } return agent.result; } /// Wait for agent completion Future waitForAgent(String agentId, {Duration timeout = const Duration(hours: 24)}) async { final agent = _agents[agentId]; if (agent == null) { throw Exception('Agent $agentId not found'); } return agent.waitForCompletion(timeout: timeout); } /// Get all running agents List> getAllAgents() { return _agents.entries.map((e) { return { 'id': e.key, 'type': e.value.definition.type, 'task': e.value.task, 'status': e.value.isComplete ? 'completed' : 'running', 'result': e.value.result?.toJson(), }; }).toList(); } /// Cancel an agent Future cancelAgent(String agentId) async { final agent = _agents[agentId]; if (agent == null) { return false; } agent.cancel(); _agents.remove(agentId); return true; } } /// Represents a running agent instance class _RunningAgent { final String id; final AgentDefinition definition; final AgentContext context; final String task; final String apiKey; final String model; DateTime _startTime = DateTime.now(); DateTime? _endTime; AgentResult? result; bool _cancelled = false; final Completer _completer = Completer(); _RunningAgent({ required this.id, required this.definition, required this.context, required this.task, required this.apiKey, required this.model, }); bool get isComplete => _endTime != null; /// Run the agent Future run() async { try { // Create API client for this agent final client = OpenRouterClient( config: OpenRouterConfig( apiKey: apiKey, model: model, ), ); // Create tool loop for this agent final toolLoop = ToolLoopService(); // Build agent-specific system prompt final systemPrompt = '''${definition.getSystemPrompt()} Task: $task Context: - Session ID: ${context.sessionId} - Working Directory: ${context.workingDirectory} - Parent Agent: ${context.parentAgentId} ${context.sharedState.isNotEmpty ? '- Shared State: ${context.sharedState}' : ''} Use available tools to complete this task. Report your findings clearly.'''; // Run the tool loop for this agent final toolResult = await toolLoop.runTurn( client: client, model: model, apiKey: apiKey, getSettings: () => context.settings, apiMessages: context.conversationHistory, userText: task, workingDirectory: context.workingDirectory, ); // Extract output final output = toolResult.responseText; // Create result result = AgentResult.success( agentId: id, output: output, data: { 'messages_exchanged': toolResult.apiMessages.length, 'web_searches': toolResult.webSearchRequests, 'web_fetches': toolResult.webFetchRequests, }, duration: DateTime.now().difference(_startTime), ); _endTime = DateTime.now(); _completer.complete(result!); return result!; } catch (e, st) { result = AgentResult.failure( agentId: id, error: e.toString(), duration: DateTime.now().difference(_startTime), ); _endTime = DateTime.now(); _completer.completeError(e, st); return result!; } } /// Wait for agent to complete Future waitForCompletion({Duration timeout = const Duration(hours: 24)}) { return _completer.future.timeout(timeout); } /// Cancel this agent void cancel() { _cancelled = true; } }