- TeamMember: Laeufe und Kosten heute, offene und blockierte Aufgaben je Agent (eine Abfrage fuer alle, nicht eine je Agent) - TeamUnit: dieselben Werte samt Untereinheiten, offene Vorgaenge aus der Einheit, heutige Kostenmeldung (#699) - team.js: Lagezeile auf jeder Karte und in der Seitenleiste, Instanz summiert; Spruenge zu Kosten je Einheit und zu den Vorgaengen; i18n en/de - Tests: TeamOverviewTests (+2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
420 lines
16 KiB
C#
420 lines
16 KiB
C#
namespace ClawdDotNet.Web.Contracts;
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// Die Übertragungstypen der Weboberfläche.
|
|
//
|
|
// Warum es sie gibt, statt die Kerntypen zu serialisieren:
|
|
//
|
|
// 1. AgentConfig trägt Zugangsdaten. Ein `return agent;` hätte den
|
|
// Werkzeug-Konfigurationsbaum samt Passwörtern ins Netz geschickt — die Sorte
|
|
// Fehler, die niemandem auffällt, weil alles funktioniert.
|
|
// 2. Die Kerntypen ändern sich mit dem Kern. Ein neues Feld in TaskItem wäre sonst
|
|
// still eine API-Änderung, auf die sich jemand verlassen hat.
|
|
// 3. Die Grenze soll einen Prozesswechsel überstehen (siehe InstanceHttpHost). Über eine
|
|
// Prozessgrenze geht nur, was hier steht.
|
|
//
|
|
// Namen auf Englisch in camelCase wie im übrigen Code; die Beschreibungen auf Deutsch.
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
/// <summary>Antwort auf einen verwaltenden Aufruf — dieselbe Form wie <c>AdminResult</c>.</summary>
|
|
public sealed record OutcomeDto(bool Ok, string Message);
|
|
|
|
/// <summary>Version, Bau und Instanz. Was auf der Info-Seite steht.</summary>
|
|
public sealed record InfoDto(
|
|
string AppVersion,
|
|
string BuildSummary,
|
|
string InstanceId,
|
|
string InstanceName,
|
|
bool EngineActive,
|
|
bool SchedulerActive,
|
|
bool StagingActive,
|
|
bool DeploymentcenterActive);
|
|
|
|
/// <summary>Erreichbarkeit und Guthaben bei OpenRouter.</summary>
|
|
public sealed record StatusDto(
|
|
bool ApiReachable,
|
|
string StatusText,
|
|
string CreditsText,
|
|
double? CreditBalance,
|
|
double? CreditRemaining);
|
|
|
|
/// <summary>Ein Agent, ohne alles, was ein Zugangsdatum sein könnte.</summary>
|
|
public sealed record AgentDto(
|
|
string AgentId,
|
|
string DisplayName,
|
|
string Model,
|
|
string Description,
|
|
string PromptCaching,
|
|
int MaxToolResultChars,
|
|
int MaxSteps,
|
|
int MaxContextTokens,
|
|
int TimeoutSeconds,
|
|
decimal DailyCostUsd,
|
|
long DailyTokens,
|
|
bool IsRunning,
|
|
IReadOnlyList<AgentToolDto> Tools,
|
|
// #715: What makes the agent read foreign content — its tools, or "setting". Empty: standard tier.
|
|
IReadOnlyList<string> ForeignContentSources,
|
|
bool TreatAsForeignReader,
|
|
// #712
|
|
AgentRuntimeDto Runtime);
|
|
|
|
/// <summary>#712: Where an agent's runs happen.</summary>
|
|
public sealed record AgentRuntimeDto(
|
|
string Kind,
|
|
IReadOnlyList<string> AllowedTools,
|
|
int MaxTurns,
|
|
int TimeoutMinutes,
|
|
string Command,
|
|
IReadOnlyList<string> Arguments,
|
|
IReadOnlyList<string> Egress);
|
|
|
|
/// <summary>#712: What foreign runtimes find on this machine — never a key, only whether there is one.</summary>
|
|
public sealed record RuntimesDto(
|
|
bool SandboxAvailable,
|
|
string? SandboxReason,
|
|
IReadOnlyList<string> Kinds,
|
|
IReadOnlyList<string> DefaultAllowedTools,
|
|
ClaudeCodeDto ClaudeCode);
|
|
|
|
public sealed record ClaudeCodeDto(
|
|
string Executable,
|
|
string? Located,
|
|
string Auth,
|
|
bool HasApiKey,
|
|
bool HasOAuthToken);
|
|
|
|
/// <summary>#712: Changes to the Claude Code account. Empty keys keep the stored ones.</summary>
|
|
public sealed record ClaudeCodeInputDto(string? Executable, string? Auth, string? ApiKey, string? OAuthToken);
|
|
|
|
/// <summary>
|
|
/// Ein Werkzeug am Agenten. <b>Ohne die Einstellungen</b> — die stehen in einem freien
|
|
/// Wörterbuch und enthalten regelmäßig Passwörter und Schlüssel. Was einstellbar ist und
|
|
/// ob etwas gesetzt ist, sagt <see cref="ToolSettingsDto"/>.
|
|
/// </summary>
|
|
public sealed record AgentToolDto(string Name, string Description, bool Enabled, bool ReadsForeignContent = false);
|
|
|
|
/// <summary>
|
|
/// Die Einstellungen eines Werkzeugs, maskiert.
|
|
///
|
|
/// <para>Ein Wert, dessen Name ein Zugangsdatum bezeichnet, kommt nie heraus. Statt
|
|
/// seiner steht in <see cref="Secrets"/>, ob überhaupt etwas gesetzt ist — mehr braucht
|
|
/// eine Anzeige nicht, und mehr darf sie nicht wissen.</para>
|
|
/// </summary>
|
|
public sealed record ToolSettingsDto(
|
|
string Name,
|
|
IReadOnlyDictionary<string, object?> Values,
|
|
IReadOnlyDictionary<string, bool> Secrets);
|
|
|
|
/// <summary>Eine Zeile im Chatverlauf.</summary>
|
|
public sealed record ChatEntryDto(
|
|
string Role,
|
|
string Content,
|
|
DateTime Timestamp,
|
|
string? Source);
|
|
|
|
/// <summary>Was der Browser schickt, wenn jemand etwas in den Chat tippt.</summary>
|
|
public sealed record SendMessageDto(string Message);
|
|
|
|
/// <summary>Ein Dienst der Instanz.</summary>
|
|
public sealed record ServiceDto(
|
|
string ServiceId,
|
|
string Name,
|
|
string Type,
|
|
int Port,
|
|
bool Enabled,
|
|
bool BuiltIn,
|
|
string Description);
|
|
|
|
/// <summary>Was einen neuen Dienst ausmacht.</summary>
|
|
public sealed record ServiceInputDto(string Name, string Type, int Port, string Description = "");
|
|
|
|
/// <summary>Ein neuer Agent.</summary>
|
|
public sealed record NewAgentDto(string AgentId, string DisplayName, string Model, string Description = "", string? UnitId = null);
|
|
|
|
/// <summary>Eine Aufgabe vom Taskboard.</summary>
|
|
public sealed record TaskDto(
|
|
string Id,
|
|
string Title,
|
|
string Status,
|
|
string Type,
|
|
int Priority,
|
|
string Assignee,
|
|
bool RequireApproval,
|
|
string Acceptance,
|
|
IReadOnlyList<string> BlockedBy,
|
|
string Body,
|
|
DateTime CreatedAt,
|
|
DateTime UpdatedAt,
|
|
TaskWhenDto? When = null,
|
|
DateTime? NextRunUtc = null,
|
|
TaskLastRunDto? LastRun = null,
|
|
string? Hinweis = null,
|
|
TaskBudgetMissDto? BudgetMiss = null,
|
|
string? CreatedBy = null,
|
|
// #714: the task this one serves.
|
|
string? ParentId = null,
|
|
// #684: the unit whose board the task is on, computed from the organisation; null on the top level.
|
|
string? Board = null,
|
|
string? BoardPath = null,
|
|
// #697: where the task came from — stamped by the engine.
|
|
string? OriginRunId = null,
|
|
string? OriginTaskId = null,
|
|
// #717: fill work — a deadline instead of an appointment — and the author's estimate per run.
|
|
DateTime? FillBy = null,
|
|
decimal? EstimateUsd = null,
|
|
DateTime? FillAlertedAt = null,
|
|
// #686: a meeting's participants, in order.
|
|
IReadOnlyList<string>? Participants = null);
|
|
|
|
/// <summary>#713: A personal MCP token as listed — never the token itself.</summary>
|
|
public sealed record McpTokenDto(
|
|
string Id, string AgentId, string Label, DateTime CreatedAt, DateTime ExpiresAt, DateTime? RevokedAt, bool Active);
|
|
|
|
/// <summary>#713: A new personal token — the only answer that carries it in clear.</summary>
|
|
public sealed record McpTokenIssuedDto(McpTokenDto Info, string Token);
|
|
|
|
/// <summary>#713: What the WebUI sends to issue a personal token.</summary>
|
|
public sealed record McpTokenInputDto(string AgentId, string? Label = null, int? Days = null);
|
|
|
|
/// <summary>#717: A special budget — the amount a human approved for a task tree, and what it used.</summary>
|
|
public sealed record SpecialBudgetDto(string TaskId, decimal AmountUsd, decimal SpentUsd, string Reason, string RequestedBy, DateTime ApprovedAt);
|
|
|
|
/// <summary>
|
|
/// #716: Runs of this task the budget refused — only present while something is open
|
|
/// (misses since the last run that took place, a pending catch-up or a deferral).
|
|
/// </summary>
|
|
public sealed record TaskBudgetMissDto(int Count, DateTime? LastAt, DateTime? DeferredUntil, bool CatchUpPending, bool CarriedOver);
|
|
|
|
/// <summary>#711: The team overview — every agent and unit, and who reached whom recently.</summary>
|
|
public sealed record TeamSnapshotDto(
|
|
IReadOnlyList<TeamMemberDto> Members,
|
|
IReadOnlyList<AgentLinkDto> Links,
|
|
DateTime AtUtc,
|
|
IReadOnlyList<TeamUnitDto> Units,
|
|
IReadOnlyList<ConflictHotspotDto> ConflictHotspots);
|
|
|
|
/// <summary>#720: A file agents keep overwriting each other in — shown with a warning. The path is relative to the instance's Agents folder.</summary>
|
|
public sealed record ConflictHotspotDto(string Workspace, string Path, int Conflicts, DateTime LastAtUtc, IReadOnlyList<string> Agents);
|
|
|
|
/// <summary>
|
|
/// #711, stage 1: A unit with the states of everyone in it and below, counted by the
|
|
/// instance. <see cref="States"/> maps a state name to a count; <see cref="Worst"/> is the
|
|
/// most urgent state inside.
|
|
/// </summary>
|
|
public sealed record TeamUnitDto(
|
|
string Id,
|
|
string Name,
|
|
string? ParentId,
|
|
string Path,
|
|
string? LeaderAgentId,
|
|
string? Goal,
|
|
int Agents,
|
|
IReadOnlyDictionary<string, int> States,
|
|
int PendingApprovals,
|
|
string Worst,
|
|
// #688: the department's situation.
|
|
int RunsToday = 0,
|
|
decimal CostToday = 0,
|
|
int OpenTasks = 0,
|
|
int BlockedTasks = 0,
|
|
int OpenCases = 0,
|
|
string? CostAlert = null);
|
|
|
|
/// <summary>
|
|
/// #711: One agent in the team overview. <see cref="State"/> is one of Idle, Working, Stuck,
|
|
/// WaitingForApproval, Problem, BudgetExhausted — decided by the instance, not the page.
|
|
/// </summary>
|
|
public sealed record TeamMemberDto(
|
|
string AgentId,
|
|
string DisplayName,
|
|
string Model,
|
|
string State,
|
|
IReadOnlyList<ActiveRunDto> Runs,
|
|
int PendingApprovals,
|
|
string? LastRunStatus,
|
|
DateTime? LastRunAtUtc,
|
|
string? LastRunResult,
|
|
long TypicalRunMs,
|
|
string? UnitId,
|
|
bool IsLead,
|
|
// #688: the agent's day.
|
|
int RunsToday = 0,
|
|
decimal CostToday = 0,
|
|
int OpenTasks = 0,
|
|
int BlockedTasks = 0);
|
|
|
|
/// <summary>#711: A run in progress.</summary>
|
|
public sealed record ActiveRunDto(string RunId, string AgentId, string Source, string? TaskId, string? CalledBy, DateTime StartedAtUtc);
|
|
|
|
/// <summary>#711: One agent reaching another.</summary>
|
|
public sealed record AgentLinkDto(string From, string To, string Kind, DateTime AtUtc);
|
|
|
|
/// <summary>Der letzte Lauf einer Aufgabe (#594) - aus dem Beleg, samt Kosten.</summary>
|
|
public sealed record TaskLastRunDto(DateTime OccurredAt, string Status, decimal CostUsd, bool CostIsKnown, string ResultRef);
|
|
|
|
/// <summary>Was die Weboberflaeche beim Anlegen oder Aendern einer Aufgabe schickt (#594).</summary>
|
|
public sealed record TaskInputDto(
|
|
string Title,
|
|
string Body = "",
|
|
string Assignee = "",
|
|
string? WhenKind = null,
|
|
string? WhenValue = null,
|
|
string? TimeZone = null,
|
|
string Acceptance = "",
|
|
int Priority = 3,
|
|
string? ParentId = null,
|
|
string? FillBy = null,
|
|
decimal? EstimateUsd = null,
|
|
// #686: given, the task is a meeting chaired by the assignee.
|
|
IReadOnlyList<string>? Participants = null);
|
|
|
|
/// <summary>Die Kostenprognose einer Instanz (#595).</summary>
|
|
public sealed record ForecastDto(
|
|
IReadOnlyList<ForecastTaskDto> Tasks,
|
|
decimal TasksPerDay,
|
|
decimal OtherPerDay,
|
|
decimal TotalPerDay,
|
|
decimal TotalPerMonth,
|
|
decimal OneOffOpen,
|
|
int WithoutBasis,
|
|
IReadOnlyList<ForecastBudgetDto> BudgetWarnings);
|
|
|
|
/// <summary>Ein Agent, dessen geplante Aufgaben laut Prognose sein Tagesbudget uebersteigen (#608).</summary>
|
|
public sealed record ForecastBudgetDto(string AgentId, decimal CostPerDay, decimal BudgetPerDay);
|
|
|
|
/// <summary>Eine Zeile der Kostenprognose. <c>Basis</c>: Laeufe, Agent, Schaetzung oder Unbekannt.</summary>
|
|
public sealed record ForecastTaskDto(
|
|
string TaskId,
|
|
string Title,
|
|
string AgentId,
|
|
string Model,
|
|
double RunsPerDay,
|
|
bool OneOffOpen,
|
|
decimal? CostPerRun,
|
|
string Basis,
|
|
int Sample,
|
|
decimal CostPerDay);
|
|
|
|
/// <summary>
|
|
/// Der Termin einer Aufgabe (<c>cron</c>, <c>every</c>, <c>at</c>) samt Zeitzone. Ohne ihn
|
|
/// war in der Oberflaeche eine stuendliche Aufgabe von einer einmaligen nicht zu
|
|
/// unterscheiden (#591).
|
|
/// </summary>
|
|
public sealed record TaskWhenDto(string Kind, string Value, string TimeZone);
|
|
|
|
/// <summary>Ein eingefrorener Aufruf, der auf Freigabe wartet.</summary>
|
|
/// <param name="PreviousApprovals">#641: Wie oft dieselbe Operation dieses Agenten zuletzt schon freigegeben wurde.</param>
|
|
/// <param name="CanAllowSimilar">#641: Ob "Gleichartiges kuenftig erlauben" angeboten werden darf.</param>
|
|
/// <param name="SuggestAllowSimilar">#641: Ob die Oberflaeche die Dauerfreigabe vorschlagen soll.</param>
|
|
public sealed record StagedCallDto(
|
|
long Id,
|
|
string RunId,
|
|
string AgentId,
|
|
string Tool,
|
|
string? Action,
|
|
string Proposal,
|
|
string Status,
|
|
string ArgumentsJson,
|
|
DateTime CreatedAt,
|
|
int PreviousApprovals = 0,
|
|
bool CanAllowSimilar = false,
|
|
bool SuggestAllowSimilar = false,
|
|
// #687: the lead that may decide it instead of the human, and until when.
|
|
string? WithLead = null,
|
|
DateTime? LeadUntil = null);
|
|
|
|
/// <summary>#687: How a lead decided the approvals it was given.</summary>
|
|
public sealed record LeadApprovalStatsDto(string LeadId, int Routed, int Approved, int Rejected, int Lapsed, double? ApprovalRate);
|
|
|
|
/// <summary>#641: Eine Dauerfreigabe - "Gleichartiges kuenftig erlauben".</summary>
|
|
public sealed record StandingApprovalDto(
|
|
long Id,
|
|
string AgentId,
|
|
string Tool,
|
|
string Operation,
|
|
string GrantedBy,
|
|
DateTime GrantedAt,
|
|
long? SourceId,
|
|
DateTime? RevokedAt,
|
|
string? RevokedBy);
|
|
|
|
/// <summary>
|
|
/// #641: Die Rechte eines Agenten an einem Werkzeug, je Operation.
|
|
/// <c>Setting</c>: "erlaubt", "verboten" oder "freigabe" (einzeln vorlegen) - so wie es in der
|
|
/// Konfiguration steht. <c>Effective</c>: was tatsaechlich geschieht ("auto", "approve",
|
|
/// "deny") - einschliesslich Dauerfreigaben und Werkzeugvorgabe.
|
|
/// </summary>
|
|
public sealed record ToolRightsDto(
|
|
string Tool,
|
|
bool Configured,
|
|
IReadOnlyList<ToolOperationDto> Operations);
|
|
|
|
public sealed record ToolOperationDto(
|
|
string Operation,
|
|
string Setting,
|
|
string Effective,
|
|
string Reason,
|
|
bool AlwaysApproval,
|
|
long? StandingApprovalId);
|
|
|
|
/// <summary>#641: Neue Rechte. <c>Allowed</c> <c>null</c> = Liste entfernen, Vorgabe des Werkzeugs gilt.</summary>
|
|
public sealed record ToolRightsInputDto(IReadOnlyList<string>? Allowed, IReadOnlyList<string>? Denied);
|
|
|
|
/// <summary>Warum ein Vorschlag abgelehnt wurde.</summary>
|
|
public sealed record RejectDto(string Reason);
|
|
|
|
/// <summary>
|
|
/// Ein Lauf mit dem, was er gekostet hat.
|
|
///
|
|
/// <para><see cref="CostIsKnown"/> ist nicht schmückendes Beiwerk: Ohne geladene
|
|
/// Preisliste steht in <see cref="CostUsd"/> eine Null, die „kostenlos" heißen könnte
|
|
/// und „unbekannt" heißt. Eine Anzeige, die das nicht unterscheidet, lügt.</para>
|
|
/// </summary>
|
|
public sealed record UsageDto(
|
|
long Id,
|
|
string AgentId,
|
|
string Model,
|
|
int PromptTokens,
|
|
int CompletionTokens,
|
|
int CachedTokens,
|
|
decimal CostUsd,
|
|
bool CostIsKnown,
|
|
string Status,
|
|
int StepCount,
|
|
DateTime OccurredAt);
|
|
|
|
/// <summary>Eine Protokollzeile.</summary>
|
|
public sealed record LogLineDto(string Module, string Level, string Text);
|
|
|
|
/// <summary>Eine interne Kommunikation zwischen Agenten (A2A, #208).</summary>
|
|
public sealed record AgentCommunicationDto(
|
|
string TargetAgentId,
|
|
string TargetAgentName,
|
|
string? SourceAgentId,
|
|
string? SourceAgentName,
|
|
string Role,
|
|
string Content,
|
|
DateTime Timestamp);
|
|
|
|
/// <summary>#719: One change in the history. Hashes are not sent — only whether a version exists.</summary>
|
|
public sealed record FileHistoryEntryDto(
|
|
long Id, string Workspace, string Path, string Action, string? AgentId, string? RunId,
|
|
bool HasBefore, bool HasAfter, long? SizeAfter, bool Stored, DateTime OccurredAtUtc, string? Note);
|
|
|
|
/// <summary>#719: A version's content. <see cref="Text"/> only for text of a showable size.</summary>
|
|
public sealed record FileVersionDto(string? Text, bool Binary, bool TooLarge, bool Kept, long Bytes);
|
|
|
|
/// <summary>#719: Putting a version back — which one, and why. Without a reason it is refused.</summary>
|
|
public sealed record FileRestoreInputDto(string? Version, string? Reason);
|
|
|
|
/// <summary>#699: A unit's costs, itself and everything below it. UnitId null: the instance; "": the top level.</summary>
|
|
public sealed record UnitCostDto(
|
|
string? UnitId, string Name, string Path, int Depth, int Agents,
|
|
decimal Today, decimal Last7Days, decimal Last30Days, decimal ThisMonth, decimal PreviousMonth,
|
|
decimal? MaxPerDay, decimal? AverageDay, decimal? ShareOfMaxToday,
|
|
long SubscriptionTokensToday, long SubscriptionTokens30Days);
|
|
|
|
/// <summary>#699: A cost alert as it was raised.</summary>
|
|
public sealed record CostAlertDto(string Period, string Scope, string Kind, string Message, DateTime RaisedAt);
|