- TaskItem.ParentId, in der Datei als parent, in der DB als ParentId. Die
Elternaufgabe ist der Zweck ("wofuer"), blocked_by die Voraussetzung
("wann").
- GoalChain: Eltern bis zu acht Stufen, Zyklen brechen den Gang ab und
werden beim Setzen abgelehnt. Oben steht das Ziel der Einheit - der
obersten Aufgabe, die zu einer Einheit gehoert (ein Ziel, das ein Mensch
auf der obersten Ebene setzt, gehoert keiner), sonst der naechsten Einheit
darueber mit Ziel.
- Der Lauf einer Aufgabe bekommt den Absatz "Why this task exists" und die
Bitte, zu melden, wenn die Aufgabe ihrem Zweck nicht mehr dient.
- Taskboard-Tool: parent bei task_create/task_update, geprueft auf Existenz,
Sichtbarkeit (#684) und Zyklen; task_list zeigt "Serves".
- WebUI: Feld im Aufgabendialog, Anzeige auf der Karte; die Verwaltung
prueft wie das Tool.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
212 lines
8.3 KiB
C#
212 lines
8.3 KiB
C#
using ClawdDotNet.App.Services;
|
|
using ClawdDotNet.Core.Accounting;
|
|
using ClawdDotNet.Core.Config;
|
|
using ClawdDotNet.Core.Engine;
|
|
using ClawdDotNet.Core.Security;
|
|
using ClawdDotNet.Core.Staging;
|
|
using ClawdDotNet.Core.Tasks;
|
|
|
|
namespace ClawdDotNet.Web.Contracts;
|
|
|
|
/// <summary>
|
|
/// Die eine Stelle, an der Kerntypen zu Übertragungstypen werden.
|
|
///
|
|
/// <para>Eine Stelle, weil die Maskierung von Zugangsdaten hier hängt. Verstreut über
|
|
/// zehn Endpunkte wäre die Frage „kommt hier ein Passwort heraus?" nicht mehr zu
|
|
/// beantworten, sondern nur noch zu hoffen.</para>
|
|
/// </summary>
|
|
internal static class Mapping
|
|
{
|
|
public static AgentDto ToDto(
|
|
this AgentConfig agent,
|
|
IReadOnlyDictionary<string, string> toolDescriptions,
|
|
bool isRunning)
|
|
=> new(
|
|
agent.AgentId,
|
|
agent.DisplayName,
|
|
agent.Model,
|
|
agent.Description,
|
|
agent.PromptCaching,
|
|
agent.MaxToolResultChars,
|
|
agent.LoopGuard.MaxSteps,
|
|
agent.LoopGuard.MaxContextTokens,
|
|
agent.LoopGuard.TimeoutSeconds,
|
|
agent.Budget.DailyCostUsd,
|
|
agent.Budget.DailyTokens,
|
|
isRunning,
|
|
AgentAdministration.KnownTools
|
|
.Select(name => new AgentToolDto(
|
|
name,
|
|
toolDescriptions.GetValueOrDefault(name) ?? "",
|
|
agent.Tools.ContainsKey(name)))
|
|
.ToList());
|
|
|
|
/// <summary>
|
|
/// Die Einstellungen eines Werkzeugs — <b>ohne die Geheimnisse</b>.
|
|
///
|
|
/// <para>Welcher Schlüssel eines ist, entscheidet <see cref="ConfigSecrets"/>: der
|
|
/// Kern weiß es bereits, und eine zweite Liste hier wäre eine, die beim nächsten
|
|
/// neuen Werkzeug vergessen wird. Statt des Werts steht in <c>Secrets</c>, ob
|
|
/// überhaupt einer gesetzt ist.</para>
|
|
/// </summary>
|
|
public static ToolSettingsDto ToDto(
|
|
string toolName, IReadOnlyDictionary<string, object?> settings)
|
|
{
|
|
var values = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
|
|
var secrets = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
foreach (var (key, value) in settings)
|
|
{
|
|
if (ConfigSecrets.IsSecretKey(key))
|
|
secrets[key] = value is not null && value is not string { Length: 0 };
|
|
else
|
|
values[key] = value;
|
|
}
|
|
|
|
return new ToolSettingsDto(toolName, values, secrets);
|
|
}
|
|
|
|
public static ChatEntryDto ToDto(this ChatEntry entry)
|
|
=> new(entry.Role, entry.Content, entry.Timestamp, entry.Source);
|
|
|
|
public static TeamSnapshotDto ToDto(this App.Services.TeamSnapshot snapshot)
|
|
=> new(
|
|
snapshot.Members.Select(m => new TeamMemberDto(
|
|
m.AgentId, m.DisplayName, m.Model, m.State.ToString(),
|
|
m.Runs.Select(ToDto).ToList(),
|
|
m.PendingApprovals, m.LastRunStatus, m.LastRunAtUtc, m.LastRunResult, m.TypicalRunMs,
|
|
m.UnitId, m.IsLead)).ToList(),
|
|
snapshot.RecentLinks.Select(ToDto).ToList(),
|
|
snapshot.AtUtc,
|
|
(snapshot.Units ?? []).Select(u => new TeamUnitDto(
|
|
u.Id, u.Name, u.ParentId, u.Path, u.LeaderAgentId, u.Goal, u.Agents,
|
|
u.States.ToDictionary(s => s.Key.ToString(), s => s.Value),
|
|
u.PendingApprovals, u.Worst.ToString())).ToList());
|
|
|
|
public static ActiveRunDto ToDto(this Core.Activity.ActiveRun run)
|
|
=> new(run.RunId, run.AgentId, run.Source, run.TaskId, run.CalledBy, run.StartedAtUtc);
|
|
|
|
public static AgentLinkDto ToDto(this Core.Activity.AgentLink link)
|
|
=> new(link.From, link.To, link.Kind, link.AtUtc);
|
|
|
|
public static TaskDto ToDto(this TaskOverview overview)
|
|
=> overview.Task.ToDto() with
|
|
{
|
|
NextRunUtc = overview.NextRunUtc,
|
|
LastRun = overview.LastRun is { } lauf
|
|
? new TaskLastRunDto(lauf.OccurredAt, lauf.Status, lauf.CostUsd, lauf.CostIsKnown, lauf.ResultRef)
|
|
: null,
|
|
Hinweis = overview.Hinweis
|
|
};
|
|
|
|
public static TaskInput ToInput(this TaskInputDto input)
|
|
=> new(
|
|
input.Title ?? "",
|
|
input.Body ?? "",
|
|
input.Assignee ?? "",
|
|
input.WhenKind,
|
|
input.WhenValue,
|
|
input.TimeZone,
|
|
input.Acceptance ?? "",
|
|
input.Priority,
|
|
input.ParentId ?? "");
|
|
|
|
public static ForecastDto ToDto(this InstanzPrognose prognose)
|
|
=> new(
|
|
prognose.Aufgaben.Select(a => new ForecastTaskDto(
|
|
a.TaskId, a.Title, a.AgentId, a.Model, a.LaeufeProTag, a.EinmaligOffen,
|
|
a.KostenJeLauf, a.Grundlage.ToString(), a.Stichprobe, a.KostenProTag)).ToList(),
|
|
prognose.AufgabenProTag,
|
|
prognose.SonstigeProTag,
|
|
prognose.SummeProTag,
|
|
prognose.SummeProMonat,
|
|
prognose.EinmaligOffen,
|
|
prognose.OhneKostenbasis,
|
|
prognose.Budgetwarnungen.Select(w => new ForecastBudgetDto(w.AgentId, w.KostenProTag, w.BudgetProTag)).ToList());
|
|
|
|
public static ServiceDto ToDto(this ServiceConfig service)
|
|
=> new(
|
|
service.ServiceId,
|
|
service.Name,
|
|
service.Type,
|
|
service.Port,
|
|
service.Enabled,
|
|
service.BuiltIn,
|
|
service.Description);
|
|
|
|
public static TaskDto ToDto(this TaskItem task)
|
|
=> new(
|
|
task.Id,
|
|
task.Title,
|
|
task.Status.ToString(),
|
|
task.Type.ToString(),
|
|
task.Priority,
|
|
task.Assignee,
|
|
task.RequireApproval,
|
|
task.Acceptance,
|
|
task.BlockedBy,
|
|
task.Body,
|
|
task.CreatedAt,
|
|
task.UpdatedAt,
|
|
task.When is { } when
|
|
? new TaskWhenDto(when.Kind.ToString().ToLowerInvariant(), when.Value, when.TimeZone)
|
|
: null,
|
|
BudgetMiss: task.BudgetMissCount > 0 || task.CatchUpPending || task.DeferredUntil is not null
|
|
? new TaskBudgetMissDto(task.BudgetMissCount, task.LastBudgetMissAt, task.DeferredUntil,
|
|
task.CatchUpPending, task.IsCarriedOver)
|
|
: null,
|
|
CreatedBy: string.IsNullOrWhiteSpace(task.CreatedBy) ? null : task.CreatedBy,
|
|
ParentId: string.IsNullOrWhiteSpace(task.ParentId) ? null : task.ParentId);
|
|
|
|
/// <summary>#684: The board of a task, for a list the human sees whole.</summary>
|
|
public static TaskDto WithBoard(this TaskDto dto, TaskItem task, Core.Organisation.OrgChart org)
|
|
{
|
|
if (!org.HasUnits || Core.Tasks.TaskBoard.UnitOf(task, org) is not { } unit)
|
|
return dto;
|
|
return dto with { Board = unit, BoardPath = org.PathOf(unit) };
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ein wartender Aufruf samt seiner eingefrorenen Argumente.
|
|
///
|
|
/// <para>Die Argumente gehen <b>roh</b> heraus, und das ist der ganze Sinn der
|
|
/// Freigabe: Wer entscheidet, muss sehen, was genau ausgeführt würde — nicht eine
|
|
/// Zusammenfassung davon. Sie sind zugleich Fremdtext aus einem Modell; die Anzeige
|
|
/// setzt sie als Text ein, nie als HTML.</para>
|
|
/// </summary>
|
|
public static StagedCallDto ToDto(this StagedCall call, int bisherFreigegeben = 0, bool dauerfreigabenAktiv = false)
|
|
{
|
|
var erlaubbar = dauerfreigabenAktiv && StagingService.IstDauerhaftErlaubbar(call);
|
|
return new(
|
|
call.Id,
|
|
call.RunId,
|
|
call.AgentId,
|
|
call.Tool,
|
|
call.Action,
|
|
call.Proposal,
|
|
call.Status.ToString(),
|
|
call.ArgumentsJson,
|
|
call.CreatedAt,
|
|
bisherFreigegeben,
|
|
erlaubbar,
|
|
erlaubbar && bisherFreigegeben >= StagingService.VorschlagAb);
|
|
}
|
|
|
|
public static StandingApprovalDto ToDto(this Dauerfreigabe d)
|
|
=> new(d.Id, d.AgentId, d.Tool, d.Operation, d.ErteiltVon, d.ErteiltAm, d.AnlassId, d.WiderrufenAm, d.WiderrufenVon);
|
|
|
|
public static UsageDto ToDto(this RunUsage usage)
|
|
=> new(
|
|
usage.Id,
|
|
usage.AgentId,
|
|
usage.Model,
|
|
usage.PromptTokens,
|
|
usage.CompletionTokens,
|
|
usage.CachedTokens,
|
|
usage.CostUsd,
|
|
usage.CostIsKnown,
|
|
usage.Status,
|
|
usage.StepCount,
|
|
usage.OccurredAt);
|
|
}
|