Compare commits

...

4 Commits

Author SHA1 Message Date
devops 6d0dab4889 chore: bump version to v0.2.35 [skip ci] 2026-06-09 22:34:38 +00:00
developer dd509a75be fix: Agents hartcodiert mit File-Activity-Detection
CI - Build & Test / Backend (.NET) (push) Successful in 23s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 15s
CI - Build & Test / Security Check (push) Successful in 3s
- Tools/invoke Visibility scoped auf agent:main → keine Iris-Sessions sichtbar
- Hardcoded 6 Agenten mit korrekten Models
- Activity: checkt /mnt/workspace-{id}/memory Dateizeitstempel
2026-06-10 00:33:49 +02:00
devops e0fc305832 chore: bump version to v0.2.34 [skip ci] 2026-06-09 22:31:24 +00:00
developer c120155170 fix: GatewayClient robuste Response-Parsing + Isolierte Try/Catch
CI - Build & Test / Backend (.NET) (push) Successful in 22s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 16s
CI - Build & Test / Security Check (push) Successful in 2s
- ExtractToolData: unwraps content[0].text JSON + details
- GetStatusAsync: separates try/catch per step (ping, session, agents, queue)
- GetAgentsAsync: parses gateway agents[] array from sessions_list
- GetQueueAsync: extracts from cron response data.jobs
- gatewayOk no longer overridden by downstream tool errors
2026-06-10 00:30:36 +02:00
2 changed files with 123 additions and 103 deletions
+1 -1
View File
@@ -1 +1 @@
0.2.33 0.2.35
+117 -97
View File
@@ -62,81 +62,91 @@ public sealed class OpenClawGatewayClient(HttpClient httpClient, IConfiguration
} }
} }
/// <summary>
/// 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.
/// </summary>
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<string>();
if (!string.IsNullOrWhiteSpace(text))
{
try { return JsonNode.Parse(text); }
catch { /* fall through */ }
}
}
return result;
}
public async Task<List<DashboardAgentInfo>> GetAgentsAsync() public async Task<List<DashboardAgentInfo>> GetAgentsAsync()
{ {
// 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[]
{
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<DashboardAgentInfo>(); var agents = new List<DashboardAgentInfo>();
foreach (var def in agentDefs)
{
var isActive = false;
string? currentTask = null;
// Get sessions list (active agents/sessions) // Check workspace activity via file timestamps
var sessionsResult = await InvokeToolAsync("sessions_list"); var memDir = $"/mnt/workspace-{def.Id}/memory";
if (sessionsResult is not null) try
{ {
var sessions = sessionsResult.AsArray(); if (Directory.Exists(memDir))
if (sessions is not null)
{ {
foreach (var session in sessions) var latestFile = Directory.GetFiles(memDir, "*", SearchOption.AllDirectories)
.Select(f => new FileInfo(f))
.OrderByDescending(f => f.LastWriteTimeUtc)
.FirstOrDefault();
if (latestFile is not null)
{ {
if (session is null) continue; var age = DateTime.UtcNow - latestFile.LastWriteTimeUtc;
var id = session["sessionKey"]?.GetValue<string>() ?? session["id"]?.GetValue<string>() ?? ""; isActive = age.TotalMinutes < 15;
var name = session["name"]?.GetValue<string>() ?? id; if (isActive)
var model = session["model"]?.GetValue<string>() ?? ""; currentTask = $"Modified: {latestFile.Name}";
var status = session["status"]?.GetValue<string>() ?? ""; }
var isActive = status.Equals("active", StringComparison.OrdinalIgnoreCase); }
}
catch { /* workspace not mounted or inaccessible */ }
agents.Add(new DashboardAgentInfo( agents.Add(new DashboardAgentInfo(
Id: id, Id: def.Id,
Name: name, Name: def.Name,
Role: DeriveRole(id), Role: DeriveRole(def.Id),
Model: model, Model: def.Model,
IsActive: isActive, IsActive: isActive,
CurrentTask: session["currentTask"]?.GetValue<string>() CurrentTask: currentTask
)); ));
} }
}
}
// Also get subagents list return agents;
var subagentsResult = await InvokeToolAsync("subagents", new { action = "list" });
if (subagentsResult is not null && subagentsResult is JsonArray subArray)
{
foreach (var sub in subArray)
{
if (sub is null) continue;
var id = sub["id"]?.GetValue<string>() ?? "";
if (agents.Any(a => a.Id == id)) continue;
var name = sub["name"]?.GetValue<string>() ?? id;
var model = sub["model"]?.GetValue<string>() ?? "";
var status = sub["status"]?.GetValue<string>() ?? "";
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<string>()
));
}
}
return agents.Count > 0 ? agents : new List<DashboardAgentInfo>();
} }
public async Task<List<MessageEntry>> GetSessionHistoryAsync(string sessionKey, int limit = 50, int offset = 0) public async Task<List<MessageEntry>> GetSessionHistoryAsync(string sessionKey, int limit = 50, int offset = 0)
{ {
try try
{ {
var result = await InvokeToolAsync("sessions_history", new var result = await InvokeToolAsync("sessions_history", new { sessionKey, limit, offset });
{ if (result is null) return new List<MessageEntry>();
sessionKey,
limit,
offset
});
if (result is null)
return new List<MessageEntry>();
var messages = new List<MessageEntry>(); var messages = new List<MessageEntry>();
var array = result as JsonArray ?? result.AsArray(); var array = result as JsonArray ?? result.AsArray();
@@ -167,19 +177,13 @@ public sealed class OpenClawGatewayClient(HttpClient httpClient, IConfiguration
{ {
try try
{ {
var result = await InvokeToolAsync("sessions_send", new var result = await InvokeToolAsync("sessions_send", new { sessionKey, message });
{ if (result is null) return new ChatResponse(false, null, "Gateway nicht erreichbar");
sessionKey,
message
});
if (result is null) var ok = result["ok"]?.GetValue<bool>() ?? false;
return new ChatResponse(false, null, "Gateway nicht erreichbar");
var ok = result["ok"]?.GetValue<bool>() ?? result["success"]?.GetValue<bool>() ?? false;
var reply = result["reply"]?.GetValue<string>() var reply = result["reply"]?.GetValue<string>()
?? result["response"]?.GetValue<string>() ?? result["response"]?.GetValue<string>()
?? result["content"]?.GetValue<string>(); ?? result["content"]?[0]?["text"]?.GetValue<string>();
var error = result["error"]?.GetValue<string>(); var error = result["error"]?.GetValue<string>();
return new ChatResponse(ok, reply, error); return new ChatResponse(ok, reply, error);
@@ -195,23 +199,24 @@ public sealed class OpenClawGatewayClient(HttpClient httpClient, IConfiguration
try try
{ {
var result = await InvokeToolAsync("cron", new { action = "list" }); var result = await InvokeToolAsync("cron", new { action = "list" });
if (result is null) var data = ExtractToolData(result);
return new List<QueueItem>(); if (data is null) return new List<QueueItem>();
var items = new List<QueueItem>(); var items = new List<QueueItem>();
var array = result as JsonArray ?? result.AsArray(); var jobs = data["jobs"] as JsonArray ?? data.AsArray();
if (array is null) return items; if (jobs is null) return items;
foreach (var entry in array) foreach (var j in jobs)
{ {
if (entry is null) continue; if (j is null) continue;
var id = entry["id"]?.GetValue<string>() ?? ""; var id = j["id"]?.GetValue<string>() ?? "";
var name = entry["name"]?.GetValue<string>() ?? id; var name = j["name"]?.GetValue<string>() ?? id;
var status = entry["status"]?.GetValue<string>() ?? "unknown"; var status = j["state"]?["lastStatus"]?.GetValue<string>()
?? j["status"]?.GetValue<string>()
?? "unknown";
items.Add(new QueueItem(id, name, status)); items.Add(new QueueItem(id, name, status));
} }
return items; return items;
} }
catch catch
@@ -227,35 +232,48 @@ public sealed class OpenClawGatewayClient(HttpClient httpClient, IConfiguration
var activeAgents = 0; var activeAgents = 0;
var pendingTasks = 0; var pendingTasks = 0;
// Step 1: Health check (no auth needed)
try try
{ {
// Check gateway health
using var pingRequest = new HttpRequestMessage(HttpMethod.Get, "/health"); using var pingRequest = new HttpRequestMessage(HttpMethod.Get, "/health");
ApplyAuth(pingRequest);
using var pingResponse = await httpClient.SendAsync(pingRequest); using var pingResponse = await httpClient.SendAsync(pingRequest);
gatewayOk = pingResponse.IsSuccessStatusCode; gatewayOk = pingResponse.IsSuccessStatusCode;
if (gatewayOk)
{
// Get session status
var sessionResult = await InvokeToolAsync("session_status");
if (sessionResult is not null)
{
irisStatus = sessionResult["status"]?.GetValue<string>() ?? "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 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<string>()
?? sessionResult["sessionKey"]?.GetValue<string>()
?? "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); return new DashboardStatus(gatewayOk, irisStatus, activeAgents, pendingTasks);
@@ -267,6 +285,8 @@ public sealed class OpenClawGatewayClient(HttpClient httpClient, IConfiguration
"programmer" => "Developer", "programmer" => "Developer",
"reviewer" => "Reviewer", "reviewer" => "Reviewer",
"architekt" => "Architect", "architekt" => "Architect",
"executor" => "Executor",
"researcher" => "Researcher",
"main" => "Assistant", "main" => "Assistant",
_ => "Custom" _ => "Custom"
}; };