diff --git a/backend/Services/OpenClawGatewayClient.cs b/backend/Services/OpenClawGatewayClient.cs index 875f595..40e218c 100644 --- a/backend/Services/OpenClawGatewayClient.cs +++ b/backend/Services/OpenClawGatewayClient.cs @@ -87,60 +87,55 @@ public sealed class OpenClawGatewayClient(HttpClient httpClient, IConfiguration public async Task> GetAgentsAsync() { - var agents = new List(); - var seenIds = new HashSet(StringComparer.OrdinalIgnoreCase); - - // Get sessions list - var sessionsResult = await InvokeToolAsync("sessions_list"); - var sessionsData = ExtractToolData(sessionsResult); - - if (sessionsData is JsonObject so) + // Hardcoded agent list (known from OpenClaw config). + // Tools/invoke visibility is restricted to agent:main scope, + // so we can't enumerate Iris sessions programmatically. + var agentDefs = new[] { - // sessions_list returns { agents: [...], sessions: {...} } - // Try agents array first (from gateway call) - if (so["agents"] is JsonArray agentArray) + new { Id = "iris", Name = "Iris", Model = "GPT-5.4" }, + new { Id = "programmer", Name = "Programmer", Model = "DeepSeek V4 Flash" }, + new { Id = "reviewer", Name = "Reviewer", Model = "DeepSeek V4 Pro" }, + new { Id = "architekt", Name = "DevOps", Model = "DeepSeek V4 Pro" }, + new { Id = "executor", Name = "Executor", Model = "DeepSeek V4 Flash" }, + new { Id = "researcher", Name = "Researcher", Model = "DeepSeek V4 Pro" }, + }; + + var agents = new List(); + foreach (var def in agentDefs) + { + var isActive = false; + string? currentTask = null; + + // Check workspace activity via file timestamps + var memDir = $"/mnt/workspace-{def.Id}/memory"; + try { - foreach (var a in agentArray) + if (Directory.Exists(memDir)) { - 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; + var latestFile = Directory.GetFiles(memDir, "*", SearchOption.AllDirectories) + .Select(f => new FileInfo(f)) + .OrderByDescending(f => f.LastWriteTimeUtc) + .FirstOrDefault(); - // Check if agent has recent sessions (active indicator) - if (a["sessions"] is JsonObject sess && sess["recent"] is JsonArray recent && recent.Count > 0) + if (latestFile is not null) { - var age = recent[0]?["age"]?.GetValue() ?? long.MaxValue; - isActive = age < 300_000; // active if session < 5 min ago - currentTask = recent[0]?["key"]?.GetValue(); + var age = DateTime.UtcNow - latestFile.LastWriteTimeUtc; + isActive = age.TotalMinutes < 15; + if (isActive) + currentTask = $"Modified: {latestFile.Name}"; } - - agents.Add(new DashboardAgentInfo(id, name, DeriveRole(id), model, isActive, currentTask)); } } - } + catch { /* workspace not mounted or inaccessible */ } - // Also get subagents list - var subResult = await InvokeToolAsync("subagents", new { action = "list" }); - var subData = ExtractToolData(subResult); - - if (subData is JsonArray subArray) - { - foreach (var s in subArray) - { - 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(); - - agents.Add(new DashboardAgentInfo(id, name, DeriveRole(id), "", isActive, currentTask)); - } + agents.Add(new DashboardAgentInfo( + Id: def.Id, + Name: def.Name, + Role: DeriveRole(def.Id), + Model: def.Model, + IsActive: isActive, + CurrentTask: currentTask + )); } return agents;