// CxLLM Studio — iOS Application // Services/AgentService.swift — Agent operations service import Foundation import CxCode @Observable final class AgentService { // MARK: - State private(set) var isHealthy = false private(set) var tools: [AgentToolInfo] = [] private(set) var isRunning = false private(set) var isExecutingTool = false private(set) var error: String? private(set) var lastHealthCheck: Date? private(set) var totalExecutions: Int = 0 private(set) var totalToolCalls: Int = 0 private(set) var history: [AgentExecution] = [] private let gateway: CxGateway init(gateway: CxGateway) { self.gateway = gateway } // MARK: - Health & Discovery func checkHealth() async { do { let _ = try await gateway.agent.health() isHealthy = true error = nil lastHealthCheck = Date() } catch { isHealthy = false self.error = error.localizedDescription lastHealthCheck = Date() } } func loadTools() async { do { tools = try await gateway.agent.listTools() error = nil } catch { self.error = error.localizedDescription } } func initialize() async { await checkHealth() if isHealthy { await loadTools() } } // MARK: - Autopilot func runAutopilot(task: String, maxSteps: Int = 10) async throws -> AgentExecution { guard isHealthy else { throw AgentServiceError.notHealthy } guard !isRunning else { throw AgentServiceError.alreadyRunning } isRunning = true error = nil let startTime = Date() do { let result = try await gateway.agent.autoPilot(task: task, maxSteps: maxSteps) let duration = Date().timeIntervalSince(startTime) let execution = AgentExecution( task: task, steps: [], output: result, time: Date(), duration: duration, success: true ) history.insert(execution, at: 0) totalExecutions += 1 isRunning = false return execution } catch { let duration = Date().timeIntervalSince(startTime) let execution = AgentExecution( task: task, steps: [], output: "Error: \(error.localizedDescription)", time: Date(), duration: duration, success: false ) history.insert(execution, at: 0) self.error = error.localizedDescription isRunning = false throw error } } // MARK: - Tool Execution func callTool(name: String, arguments: [String: Any]) async throws -> String { guard !isExecutingTool else { throw AgentServiceError.alreadyRunning } isExecutingTool = true error = nil do { let result = try await gateway.agent.callTool(name: name, arguments: arguments) totalToolCalls += 1 isExecutingTool = false return result } catch { self.error = error.localizedDescription isExecutingTool = false throw error } } // MARK: - History Management func clearHistory() { history.removeAll() } // MARK: - Computed var toolNames: [String] { tools.map(\.name) } var successRate: Double { guard !history.isEmpty else { return 0 } let successes = history.filter(\.success).count return Double(successes) / Double(history.count) } var averageDuration: TimeInterval { guard !history.isEmpty else { return 0 } return history.reduce(0.0) { $0 + $1.duration } / Double(history.count) } } // MARK: - Supporting Types struct AgentStep: Identifiable { let id = UUID() let index: Int let action: String let result: String let timestamp: Date } struct AgentExecution: Identifiable { let id = UUID() let task: String let steps: [AgentStep] let output: String let time: Date let duration: TimeInterval let success: Bool } enum AgentServiceError: LocalizedError { case notHealthy case alreadyRunning var errorDescription: String? { switch self { case .notHealthy: return "Agent service is not healthy. Check connection." case .alreadyRunning: return "An operation is already in progress." } } }