feat: complete task board workflow gates
This commit is contained in:
@@ -20,7 +20,8 @@ public sealed record AgentConfig
|
||||
public string? AgentDir { get; init; }
|
||||
|
||||
[JsonPropertyName("model")]
|
||||
public string? Model { get; init; }
|
||||
[JsonConverter(typeof(AgentModelConfigConverter))]
|
||||
public AgentModelConfig? Model { get; init; }
|
||||
|
||||
[JsonPropertyName("identity")]
|
||||
public AgentIdentityConfig? Identity { get; init; }
|
||||
@@ -44,6 +45,60 @@ public sealed record AgentIdentityConfig
|
||||
public string Theme { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed record AgentModelConfig
|
||||
{
|
||||
[JsonPropertyName("primary")]
|
||||
public string? Primary { get; init; }
|
||||
}
|
||||
|
||||
public sealed class AgentModelConfigConverter : JsonConverter<AgentModelConfig>
|
||||
{
|
||||
public override AgentModelConfig? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.Null)
|
||||
return null;
|
||||
|
||||
if (reader.TokenType == JsonTokenType.String)
|
||||
{
|
||||
var primary = reader.GetString();
|
||||
return string.IsNullOrWhiteSpace(primary) ? null : new AgentModelConfig { Primary = primary };
|
||||
}
|
||||
|
||||
if (reader.TokenType != JsonTokenType.StartObject)
|
||||
throw new JsonException("Agent model must be either a string or an object.");
|
||||
|
||||
using var document = JsonDocument.ParseValue(ref reader);
|
||||
var root = document.RootElement;
|
||||
|
||||
string? primary = null;
|
||||
foreach (var property in root.EnumerateObject())
|
||||
{
|
||||
if (!string.Equals(property.Name, "primary", StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
primary = property.Value.ValueKind switch
|
||||
{
|
||||
JsonValueKind.String => property.Value.GetString(),
|
||||
JsonValueKind.Null => null,
|
||||
_ => throw new JsonException("Agent model primary must be a string.")
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return new AgentModelConfig { Primary = primary };
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, AgentModelConfig value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
if (!string.IsNullOrWhiteSpace(value.Primary))
|
||||
writer.WriteString("primary", value.Primary);
|
||||
else
|
||||
writer.WriteNull("primary");
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record AgentInfo(
|
||||
string Id,
|
||||
string Name,
|
||||
@@ -94,7 +149,7 @@ public sealed class AgentService(IConfiguration configuration, IAgentRuntime run
|
||||
var agents = new List<AgentInfo>(configs.Count);
|
||||
foreach (var config in configs)
|
||||
{
|
||||
var model = config.Model ?? "deepseek/deepseek-v4-flash";
|
||||
var model = ResolveModel(config);
|
||||
var role = DeriveRole(config.Id);
|
||||
var description = config.Identity?.Theme ?? string.Empty;
|
||||
|
||||
@@ -141,7 +196,7 @@ public sealed class AgentService(IConfiguration configuration, IAgentRuntime run
|
||||
Id: config.Id,
|
||||
Name: config.Identity?.Name ?? config.Name ?? config.Id,
|
||||
Role: role,
|
||||
Model: config.Model ?? "deepseek/deepseek-v4-flash",
|
||||
Model: ResolveModel(config),
|
||||
Status: runtimeStatus.Status,
|
||||
LastSeen: now,
|
||||
Workspace: config.Workspace,
|
||||
@@ -159,36 +214,43 @@ public sealed class AgentService(IConfiguration configuration, IAgentRuntime run
|
||||
return configs
|
||||
.Where(config => !string.IsNullOrWhiteSpace(config.Id))
|
||||
.Select(config => config.Id.Trim().ToLowerInvariant())
|
||||
.DefaultIfEmpty()
|
||||
.Where(id => !string.IsNullOrWhiteSpace(id))
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static string DeriveRole(string agentId) => agentId.ToLowerInvariant() switch
|
||||
{
|
||||
"iris" => "Orchestrator",
|
||||
"product-owner" => "Product Owner",
|
||||
"programmer" => "Developer",
|
||||
"programmer-fast" => "Developer",
|
||||
"reviewer" => "Reviewer",
|
||||
"architekt" => "Architect",
|
||||
"main" => "Assistant",
|
||||
_ => "Custom"
|
||||
};
|
||||
|
||||
private static string ResolveModel(AgentConfig config)
|
||||
=> config.Model?.Primary ?? "deepseek/deepseek-v4-flash";
|
||||
|
||||
private async Task<IReadOnlyList<AgentConfig>> LoadAgentConfigsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var path = configuration.GetValue<string>("AgentConfigPath")
|
||||
?? "/home/node/.openclaw/openclaw.json";
|
||||
|
||||
if (!File.Exists(path))
|
||||
return Array.Empty<AgentConfig>();
|
||||
return BuildFallbackConfigs();
|
||||
|
||||
var json = await File.ReadAllTextAsync(path, cancellationToken);
|
||||
using var document = JsonDocument.Parse(json, new JsonDocumentOptions { AllowTrailingCommas = true });
|
||||
var root = document.RootElement;
|
||||
|
||||
if (!root.TryGetProperty("agents", out var agentsElement))
|
||||
return Array.Empty<AgentConfig>();
|
||||
return BuildFallbackConfigs();
|
||||
|
||||
if (!agentsElement.TryGetProperty("list", out var listElement))
|
||||
return Array.Empty<AgentConfig>();
|
||||
return BuildFallbackConfigs();
|
||||
|
||||
var defaults = agentsElement.TryGetProperty("defaults", out var defaultsElement)
|
||||
? JsonSerializer.Deserialize<AgentDefaults>(defaultsElement.GetRawText(), JsonOptions)
|
||||
@@ -204,29 +266,35 @@ public sealed class AgentService(IConfiguration configuration, IAgentRuntime run
|
||||
// Inherit defaults for missing fields
|
||||
if (string.IsNullOrWhiteSpace(config.Name))
|
||||
config = config with { Name = config.Id };
|
||||
if (string.IsNullOrWhiteSpace(config.Model) && defaults?.Model?.Primary is not null)
|
||||
config = config with { Model = defaults.Model.Primary };
|
||||
if (string.IsNullOrWhiteSpace(config.Model?.Primary) && defaults?.Model?.Primary is not null)
|
||||
config = config with { Model = new AgentModelConfig { Primary = defaults.Model.Primary } };
|
||||
if (string.IsNullOrWhiteSpace(config.Workspace) && defaults?.Workspace is not null)
|
||||
config = config with { Workspace = defaults.Workspace };
|
||||
|
||||
configs.Add(config);
|
||||
}
|
||||
|
||||
return configs.AsReadOnly();
|
||||
return configs.Count > 0 ? configs.AsReadOnly() : BuildFallbackConfigs();
|
||||
}
|
||||
|
||||
private static IReadOnlyList<AgentConfig> BuildFallbackConfigs()
|
||||
=> AgentIdentityCatalog.DefaultConfiguredAgentIds
|
||||
.Select(id => new AgentConfig
|
||||
{
|
||||
Id = id,
|
||||
Name = id,
|
||||
Model = new AgentModelConfig { Primary = "deepseek/deepseek-v4-flash" }
|
||||
})
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
|
||||
private sealed record AgentDefaults
|
||||
{
|
||||
[JsonPropertyName("workspace")]
|
||||
public string? Workspace { get; init; }
|
||||
|
||||
[JsonPropertyName("model")]
|
||||
public AgentDefaultModel? Model { get; init; }
|
||||
}
|
||||
|
||||
private sealed record AgentDefaultModel
|
||||
{
|
||||
[JsonPropertyName("primary")]
|
||||
public string? Primary { get; init; }
|
||||
[JsonConverter(typeof(AgentModelConfigConverter))]
|
||||
public AgentModelConfig? Model { get; init; }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user