Files
ClawdDotNet/src/ClawdDotNet.Core/Config/InstanceConfig.cs
T
RichardandClaude Opus 5 5adf4c1ca6 Prompt History und Verbrauchsauswertung (#660), englische Hauptsprache (#661)
Prompt history: every run records its transcript (task, each model step with
tokens and cost, tool calls and results, engine follow-ups) - bounded in size,
scrubbed of recognisable secrets, system prompts stored once per text.
Retention in days per instance (default 14) and per agent (0 = never, -1 =
forever); an hourly janitor deletes what is past it, "never" also purges.

Usage analysis over run receipts: filter by period, agent, model, event, status,
cost and free text; sort by every column; totals over the whole filter; hourly
or daily series grouped by agent, model or event; markers for budget denials and
model changes; run detail with receipt, transcript, tool calls and approvals.

WebUI: Analysis > Token usage > Runs / Timeline, run detail, Prompt History with
retention settings. New code, comments and texts in English; texts come from
language packs (i18n/en.json, de.json) via modules/i18n.js. CLAUDE.md records
English as the main language.

Version 0.1.12.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 15:33:09 +02:00

176 lines
7.6 KiB
C#

using System.Text.Json.Serialization;
namespace ClawdDotNet.Core.Config;
public sealed class InstanceConfig
{
[JsonPropertyName("instanceId")]
public string InstanceId { get; set; } = Guid.NewGuid().ToString("N")[..8];
[JsonPropertyName("instanceName")]
public string InstanceName { get; set; } = "Default";
[JsonPropertyName("openRouterApiKey")]
public string OpenRouterApiKey { get; set; } = "";
[JsonPropertyName("workingDirectory")]
public string WorkingDirectory { get; set; } = "./data/";
/// <summary>
/// Wohin diese Instanz protokolliert. Leer heißt <c>&lt;Instanzverzeichnis&gt;/Logs</c>.
///
/// <para><b>Je Instanz und nicht anwendungsweit</b> (#528, Stufe 1). Vorher galt bei
/// „leer" das Log-Verzeichnis aus den Anwendungseinstellungen — ein Ort für alle
/// Instanzen. In <c>Startup.log</c> standen dann abwechselnd Zeilen zweier Instanzen
/// ohne Kennzeichnung, welche gerade spricht; im Mehrinstanzbetrieb war jede
/// Fehlersuche damit Ratearbeit.</para>
///
/// <para>Ein relativer Wert zählt gegen das Instanzverzeichnis, nicht gegen das
/// Arbeitsverzeichnis des Prozesses (siehe <c>AppHost.ResolveLogDirectory</c>).</para>
/// </summary>
[JsonPropertyName("logDirectory")]
public string LogDirectory { get; set; } = "";
/// <summary>
/// Ob der Instanzmanager diese Instanz am Laufen hält (#528).
///
/// <para><b>Zwei Dinge in einem Schalter, weil es dieselbe Zusage ist:</b> Der Manager
/// startet diese Instanz, wenn er selbst hochkommt, und er startet sie wieder, wenn
/// sie abstürzt. Wer das nicht will, lässt ihn aus — dann startet sie nur, wenn jemand
/// es verlangt.</para>
///
/// <para><b>Ausgeschaltet vorbelegt</b>, und das ist die vorsichtige Seite: Ein
/// Manager, der nach einem Neustart der Maschine alles hochfährt, was er findet,
/// startet auch das, was jemand mit Absicht angehalten hat.</para>
///
/// <para>Der Neustart nach einem Absturz ist begrenzt: Nach mehreren Versuchen in
/// kurzer Folge setzt der Manager aus und meldet es. Was an einer kaputten
/// Konfiguration scheitert, scheitert beim zehnten Versuch genauso, und dann hat man
/// statt einer toten Instanz eine tote Instanz und ein volles Protokoll.</para>
/// </summary>
[JsonPropertyName("autostartByManager")]
public bool AutostartByManager { get; set; }
/// <summary>
/// Wohin die Sicherungen dieser Instanz gehen. Leer heißt
/// <c>&lt;Instanzverzeichnis&gt;/Backups</c>.
///
/// <para><b>Je Instanz</b> (#528, Stufe 1). Vorher lagen die Archive aller Instanzen
/// in einem Ordner, und die Aufbewahrung (<see cref="BackupKeepCount"/>) räumt dort
/// nach dem Präfix <c>backup_&lt;Instanzname&gt;_</c> auf. Liegen zwei Namen
/// ineinander — <c>Test</c> und <c>Test_2</c> —, zählt die eine Instanz die Archive
/// der anderen mit und wirft die überzähligen weg.</para>
/// </summary>
[JsonPropertyName("backupDirectory")]
public string BackupDirectory { get; set; } = "";
/// <summary>Erstellt täglich zur eingestellten Uhrzeit eine Sicherung dieser Instanz.</summary>
[JsonPropertyName("autoBackupEnabled")]
public bool AutoBackupEnabled { get; set; }
/// <summary>Tageszeit der automatischen Sicherung, <c>HH:mm</c>.</summary>
[JsonPropertyName("autoBackupTime")]
public string AutoBackupTime { get; set; } = "03:00";
/// <summary>
/// Wie viele Sicherungen dieser Instanz aufbewahrt werden. 0 = alle behalten.
/// </summary>
[JsonPropertyName("backupKeepCount")]
public int BackupKeepCount { get; set; } = 14;
/// <summary>
/// Port der Weboberfläche. Entfällt mit dem Instanzmanager (E-M5): Instanzen hören
/// künftig ausschließlich auf ihrem Socket/Named Pipe; die Weboberfläche trägt der Manager.
///
/// <para><b>Noch ohne <c>[Obsolete]</c>.</b> Das Attribut stand hier kurz und hat
/// sechs Warnungen erzeugt, ohne dass eine davon jemand hätte abstellen können — der
/// Port ist bis zum Abschluss von E-M5 der einzige Weg, auf dem eine eigenständige
/// Instanz ihre Oberfläche anbietet. Eine Warnung, die nur wegsehen lässt, ist
/// schlechter als keine; sie kommt zurück, wenn der Ersatz steht.</para>
/// </summary>
[JsonPropertyName("webServerPort")]
public int WebServerPort { get; set; } = 7001;
/// <summary>
/// Ob die Weboberfläche mitläuft. Ausgeschaltet vorbelegt — sie macht aus einer
/// lokalen Anwendung einen Netzwerkdienst, der Agenten mit Datei-, Mail- und
/// FTP-Zugriff steuert. Das schaltet man bewusst ein.
/// </summary>
[JsonPropertyName("webUiEnabled")]
public bool WebUiEnabled { get; set; }
/// <summary>
/// <c>loopback</c> (Vorgabe) oder <c>any</c>. Bei <c>any</c> ist
/// <see cref="WebUiToken"/> Pflicht, sonst startet die Weboberfläche nicht.
/// </summary>
[JsonPropertyName("webUiBinding")]
public string WebUiBinding { get; set; } = "loopback";
/// <summary>
/// Zugangstoken der Weboberfläche. Steht in <c>ConfigSecrets</c> und liegt damit
/// geschützt in der Instanzdatei — wie der OpenRouter-Schlüssel daneben.
/// </summary>
[JsonPropertyName("webUiToken")]
public string WebUiToken { get; set; } = "";
[JsonPropertyName("telegramClient")]
public TelegramClientConfig? TelegramClient { get; set; }
/// <summary>Anbindung an das Watchdog-Modul des Deploymentcenters (Instanz-Heartbeat).</summary>
[JsonPropertyName("watchdog")]
public WatchdogConfig Watchdog { get; set; } = new();
/// <summary>Tagesgrenzen über alle Agenten der Instanz hinweg. 0 = keine Grenze.</summary>
[JsonPropertyName("budget")]
public InstanceBudget Budget { get; set; } = new();
/// <summary>
/// #660: How long prompt and response of every run are kept. Default for all agents;
/// each agent can override it.
/// </summary>
[JsonPropertyName("promptHistory")]
public Transcripts.PromptHistorySettings PromptHistory { get; set; } = new();
/// <summary>
/// Wie viele Weitergaben eine Delegationskette höchstens haben darf (B8, #206).
/// A→B ist die Tiefe 1. Zyklen fängt die Kette selbst ab; dieser Wert begrenzt die
/// Staffel A→B→C→D, die niemand beauftragt hat und die jeden Schritt einen vollen
/// Lauf kostet.
///
/// Instanzweit und nicht je Agent: Eine Kette gehört keinem der Beteiligten.
/// </summary>
[JsonPropertyName("maxDelegationDepth")]
public int MaxDelegationDepth { get; set; } = 3;
/// <summary>
/// Wie lange ein delegierter Aufruf höchstens auf einen belegten Zielagenten
/// wartet, in Sekunden (#461). Danach kommt eine Absage statt weiterer Wartezeit.
///
/// Gilt nur für Delegationen. Ein vom Menschen angestoßener Chat reiht sich
/// weiterhin ein und wartet, so lange es dauert.
/// </summary>
[JsonPropertyName("delegationGateTimeoutSeconds")]
public int DelegationGateTimeoutSeconds { get; set; } = 120;
[JsonPropertyName("agents")]
public List<AgentConfig> Agents { get; set; } = new();
[JsonPropertyName("services")]
public List<ServiceConfig> Services { get; set; } = new();
}
public sealed class TelegramClientConfig
{
[JsonPropertyName("apiId")]
public int ApiId { get; set; }
[JsonPropertyName("apiHash")]
public string ApiHash { get; set; } = "";
[JsonPropertyName("phoneNumber")]
public string PhoneNumber { get; set; } = "";
[JsonPropertyName("password2FA")]
public string? Password2FA { get; set; }
}