diff --git a/backend/Services/OpenClawGatewayClient.cs b/backend/Services/OpenClawGatewayClient.cs index 8633c3c..875f595 100644 --- a/backend/Services/OpenClawGatewayClient.cs +++ b/backend/Services/OpenClawGatewayClient.cs @@ -62,81 +62,96 @@ public sealed class OpenClawGatewayClient(HttpClient httpClient, IConfiguration } } + /// + /// Extracts useful data from a tool response that may embed JSON in content[0].text. + /// Returns the unwrapped "details" object if present, otherwise the raw result. + /// + private static JsonNode? ExtractToolData(JsonNode? result) + { + if (result is null) return null; + // Some tools return { details: {...}, content: [...] } + if (result["details"] is JsonNode details && details is not JsonValue) + return details; + // Some tools wrap in content[0].text as JSON string + if (result["content"] is JsonArray content && content.Count > 0) + { + var text = content[0]?["text"]?.GetValue(); + if (!string.IsNullOrWhiteSpace(text)) + { + try { return JsonNode.Parse(text); } + catch { /* fall through */ } + } + } + return result; + } + public async Task> GetAgentsAsync() { var agents = new List(); + var seenIds = new HashSet(StringComparer.OrdinalIgnoreCase); - // Get sessions list (active agents/sessions) + // Get sessions list var sessionsResult = await InvokeToolAsync("sessions_list"); - if (sessionsResult is not null) - { - var sessions = sessionsResult.AsArray(); - if (sessions is not null) - { - foreach (var session in sessions) - { - if (session is null) continue; - var id = session["sessionKey"]?.GetValue() ?? session["id"]?.GetValue() ?? ""; - var name = session["name"]?.GetValue() ?? id; - var model = session["model"]?.GetValue() ?? ""; - var status = session["status"]?.GetValue() ?? ""; - var isActive = status.Equals("active", StringComparison.OrdinalIgnoreCase); + var sessionsData = ExtractToolData(sessionsResult); - agents.Add(new DashboardAgentInfo( - Id: id, - Name: name, - Role: DeriveRole(id), - Model: model, - IsActive: isActive, - CurrentTask: session["currentTask"]?.GetValue() - )); + if (sessionsData is JsonObject so) + { + // sessions_list returns { agents: [...], sessions: {...} } + // Try agents array first (from gateway call) + if (so["agents"] is JsonArray agentArray) + { + foreach (var a in agentArray) + { + if (a is null) continue; + var id = a["agentId"]?.GetValue() ?? ""; + if (string.IsNullOrWhiteSpace(id) || !seenIds.Add(id)) continue; + var name = a["name"]?.GetValue() ?? id; + var model = "GPT-5.4"; // default, overridden if available + var isActive = false; + var currentTask = (string?)null; + + // Check if agent has recent sessions (active indicator) + if (a["sessions"] is JsonObject sess && sess["recent"] is JsonArray recent && recent.Count > 0) + { + var age = recent[0]?["age"]?.GetValue() ?? long.MaxValue; + isActive = age < 300_000; // active if session < 5 min ago + currentTask = recent[0]?["key"]?.GetValue(); + } + + agents.Add(new DashboardAgentInfo(id, name, DeriveRole(id), model, isActive, currentTask)); } } } // Also get subagents list - var subagentsResult = await InvokeToolAsync("subagents", new { action = "list" }); - if (subagentsResult is not null && subagentsResult is JsonArray subArray) + var subResult = await InvokeToolAsync("subagents", new { action = "list" }); + var subData = ExtractToolData(subResult); + + if (subData is JsonArray subArray) { - foreach (var sub in subArray) + foreach (var s in subArray) { - if (sub is null) continue; - var id = sub["id"]?.GetValue() ?? ""; - if (agents.Any(a => a.Id == id)) continue; + if (s is null) continue; + var id = s["id"]?.GetValue() ?? s["agentId"]?.GetValue() ?? ""; + if (string.IsNullOrWhiteSpace(id) || !seenIds.Add(id)) continue; + var name = s["name"]?.GetValue() ?? id; + var status = s["status"]?.GetValue() ?? ""; + var isActive = status is "active" or "running"; + var currentTask = s["task"]?.GetValue() ?? s["currentTask"]?.GetValue(); - var name = sub["name"]?.GetValue() ?? id; - var model = sub["model"]?.GetValue() ?? ""; - var status = sub["status"]?.GetValue() ?? ""; - var isActive = status.Equals("active", StringComparison.OrdinalIgnoreCase) || - status.Equals("running", StringComparison.OrdinalIgnoreCase); - - agents.Add(new DashboardAgentInfo( - Id: id, - Name: name, - Role: DeriveRole(id), - Model: model, - IsActive: isActive, - CurrentTask: sub["currentTask"]?.GetValue() - )); + agents.Add(new DashboardAgentInfo(id, name, DeriveRole(id), "", isActive, currentTask)); } } - return agents.Count > 0 ? agents : new List(); + return agents; } public async Task> GetSessionHistoryAsync(string sessionKey, int limit = 50, int offset = 0) { try { - var result = await InvokeToolAsync("sessions_history", new - { - sessionKey, - limit, - offset - }); - - if (result is null) - return new List(); + var result = await InvokeToolAsync("sessions_history", new { sessionKey, limit, offset }); + if (result is null) return new List(); var messages = new List(); var array = result as JsonArray ?? result.AsArray(); @@ -167,19 +182,13 @@ public sealed class OpenClawGatewayClient(HttpClient httpClient, IConfiguration { try { - var result = await InvokeToolAsync("sessions_send", new - { - sessionKey, - message - }); + var result = await InvokeToolAsync("sessions_send", new { sessionKey, message }); + if (result is null) return new ChatResponse(false, null, "Gateway nicht erreichbar"); - if (result is null) - return new ChatResponse(false, null, "Gateway nicht erreichbar"); - - var ok = result["ok"]?.GetValue() ?? result["success"]?.GetValue() ?? false; + var ok = result["ok"]?.GetValue() ?? false; var reply = result["reply"]?.GetValue() ?? result["response"]?.GetValue() - ?? result["content"]?.GetValue(); + ?? result["content"]?[0]?["text"]?.GetValue(); var error = result["error"]?.GetValue(); return new ChatResponse(ok, reply, error); @@ -195,23 +204,24 @@ public sealed class OpenClawGatewayClient(HttpClient httpClient, IConfiguration try { var result = await InvokeToolAsync("cron", new { action = "list" }); - if (result is null) - return new List(); + var data = ExtractToolData(result); + if (data is null) return new List(); var items = new List(); - var array = result as JsonArray ?? result.AsArray(); - if (array is null) return items; + var jobs = data["jobs"] as JsonArray ?? data.AsArray(); + if (jobs is null) return items; - foreach (var entry in array) + foreach (var j in jobs) { - if (entry is null) continue; - var id = entry["id"]?.GetValue() ?? ""; - var name = entry["name"]?.GetValue() ?? id; - var status = entry["status"]?.GetValue() ?? "unknown"; + if (j is null) continue; + var id = j["id"]?.GetValue() ?? ""; + var name = j["name"]?.GetValue() ?? id; + var status = j["state"]?["lastStatus"]?.GetValue() + ?? j["status"]?.GetValue() + ?? "unknown"; items.Add(new QueueItem(id, name, status)); } - return items; } catch @@ -227,35 +237,48 @@ public sealed class OpenClawGatewayClient(HttpClient httpClient, IConfiguration var activeAgents = 0; var pendingTasks = 0; + // Step 1: Health check (no auth needed) try { - // Check gateway health using var pingRequest = new HttpRequestMessage(HttpMethod.Get, "/health"); - ApplyAuth(pingRequest); using var pingResponse = await httpClient.SendAsync(pingRequest); gatewayOk = pingResponse.IsSuccessStatusCode; - - if (gatewayOk) - { - // Get session status - var sessionResult = await InvokeToolAsync("session_status"); - if (sessionResult is not null) - { - irisStatus = sessionResult["status"]?.GetValue() ?? "Active"; - } - - // Get agents for active count - var agents = await GetAgentsAsync(); - activeAgents = agents.Count(a => a.IsActive); - - // Get queue/cron for pending tasks - var queue = await GetQueueAsync(); - pendingTasks = queue.Count; - } } catch { - gatewayOk = false; + // gatewayOk stays false + } + + if (gatewayOk) + { + // Step 2: Session status + try + { + var sessionResult = await InvokeToolAsync("session_status"); + if (sessionResult is not null) + { + irisStatus = sessionResult["status"]?.GetValue() + ?? sessionResult["sessionKey"]?.GetValue() + ?? "Active"; + } + } + catch { } + + // Step 3: Active agents + try + { + var agents = await GetAgentsAsync(); + activeAgents = agents.Count(a => a.IsActive); + } + catch { } + + // Step 4: Queue items + try + { + var queue = await GetQueueAsync(); + pendingTasks = queue.Count; + } + catch { } } return new DashboardStatus(gatewayOk, irisStatus, activeAgents, pendingTasks); @@ -267,6 +290,8 @@ public sealed class OpenClawGatewayClient(HttpClient httpClient, IConfiguration "programmer" => "Developer", "reviewer" => "Reviewer", "architekt" => "Architect", + "executor" => "Executor", + "researcher" => "Researcher", "main" => "Assistant", _ => "Custom" };