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"
};