Bisher teilte sich eine Budgetablehnung den Ausgang NichtGelaufen mit dem Herunterfahren. Einmalige Aufgaben wurden den Rest des Tages alle 30 Minuten erneut abgelehnt (jedes Mal ein Beleg) und liefen nach Mitternacht still zu Lasten des neuen Tages. Wiederkehrende verfielen still. - TaskLauf.KeinBudget als eigener Ausgang; der Dispatcher bildet BudgetExceeded darauf ab. - Vier Felder an der Aufgabe: BudgetMissCount, LastBudgetMissAt, DeferredUntil, CatchUpPending (Spalten werden fuer bestehende Datenbanken nachgeruestet). - Einmalige Aufgaben werden mitgenommen: Der Termin bleibt erhalten, faellig ist die Aufgabe wieder ab der naechsten lokalen Mitternacht, nicht vorher. - Wiederkehrende Aufgaben folgen der Regel des Agenten (budget.whenExhausted): expire vermerkt den Ausfall und laesst den Termin verfallen; catchUpOnce fasst alle verpassten Termine zu einem Nachholauf am naechsten Tag zusammen. Ein regulaer faelliger Termin deckt das Nachholen mit ab, es bleibt bei einem Lauf. - Der neue Tag hat Vorfahrt: Mitgenommenes und Nachholaeufe kommen nach allem regulaer Faelligen, unabhaengig von der Prioritaet. - Beim ersten Takt eines neuen Tages eine Zusammenfassung je Agent im Log. - Sichtbar: Die Aufgabenkarte der WebUI zeigt Ausfaelle, Mitnahme und Nachholen; die Regel ist im Desktop je Agent einstellbar. Nebenbei sind "Next:"/"Last:" auf der Karte ins Sprachpaket gewandert. Stufen 2 bis 4 (Reservierung, Spielraum statt pauschalem Anteil, Vorbeugen) folgen. Tests: BudgetMissTests (9 neu). 1.238 gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
264 lines
9.5 KiB
C#
264 lines
9.5 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);
|
|
|
|
/// <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);
|
|
|
|
/// <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 = "");
|
|
|
|
/// <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);
|
|
|
|
/// <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>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);
|
|
|
|
/// <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);
|
|
|
|
/// <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);
|
|
|