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/"; /// /// Wohin diese Instanz protokolliert. Leer heißt <Instanzverzeichnis>/Logs. /// /// Je Instanz und nicht anwendungsweit (#528, Stufe 1). Vorher galt bei /// „leer" das Log-Verzeichnis aus den Anwendungseinstellungen — ein Ort für alle /// Instanzen. In Startup.log standen dann abwechselnd Zeilen zweier Instanzen /// ohne Kennzeichnung, welche gerade spricht; im Mehrinstanzbetrieb war jede /// Fehlersuche damit Ratearbeit. /// /// Ein relativer Wert zählt gegen das Instanzverzeichnis, nicht gegen das /// Arbeitsverzeichnis des Prozesses (siehe AppHost.ResolveLogDirectory). /// [JsonPropertyName("logDirectory")] public string LogDirectory { get; set; } = ""; /// /// Ob der Instanzmanager diese Instanz am Laufen hält (#528). /// /// Zwei Dinge in einem Schalter, weil es dieselbe Zusage ist: 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. /// /// Ausgeschaltet vorbelegt, 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. /// /// 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. /// [JsonPropertyName("autostartByManager")] public bool AutostartByManager { get; set; } /// /// Wohin die Sicherungen dieser Instanz gehen. Leer heißt /// <Instanzverzeichnis>/Backups. /// /// Je Instanz (#528, Stufe 1). Vorher lagen die Archive aller Instanzen /// in einem Ordner, und die Aufbewahrung () räumt dort /// nach dem Präfix backup_<Instanzname>_ auf. Liegen zwei Namen /// ineinander — Test und Test_2 —, zählt die eine Instanz die Archive /// der anderen mit und wirft die überzähligen weg. /// [JsonPropertyName("backupDirectory")] public string BackupDirectory { get; set; } = ""; /// Erstellt täglich zur eingestellten Uhrzeit eine Sicherung dieser Instanz. [JsonPropertyName("autoBackupEnabled")] public bool AutoBackupEnabled { get; set; } /// Tageszeit der automatischen Sicherung, HH:mm. [JsonPropertyName("autoBackupTime")] public string AutoBackupTime { get; set; } = "03:00"; /// /// Wie viele Sicherungen dieser Instanz aufbewahrt werden. 0 = alle behalten. /// [JsonPropertyName("backupKeepCount")] public int BackupKeepCount { get; set; } = 14; /// /// 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. /// /// Noch ohne [Obsolete]. 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. /// [JsonPropertyName("webServerPort")] public int WebServerPort { get; set; } = 7001; /// /// 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. /// [JsonPropertyName("webUiEnabled")] public bool WebUiEnabled { get; set; } /// /// loopback (Vorgabe) oder any. Bei any ist /// Pflicht, sonst startet die Weboberfläche nicht. /// [JsonPropertyName("webUiBinding")] public string WebUiBinding { get; set; } = "loopback"; /// /// Zugangstoken der Weboberfläche. Steht in ConfigSecrets und liegt damit /// geschützt in der Instanzdatei — wie der OpenRouter-Schlüssel daneben. /// [JsonPropertyName("webUiToken")] public string WebUiToken { get; set; } = ""; [JsonPropertyName("telegramClient")] public TelegramClientConfig? TelegramClient { get; set; } /// Anbindung an das Watchdog-Modul des Deploymentcenters (Instanz-Heartbeat). [JsonPropertyName("watchdog")] public WatchdogConfig Watchdog { get; set; } = new(); /// Tagesgrenzen über alle Agenten der Instanz hinweg. 0 = keine Grenze. [JsonPropertyName("budget")] public InstanceBudget Budget { get; set; } = new(); /// /// #660: How long prompt and response of every run are kept. Default for all agents; /// each agent can override it. /// [JsonPropertyName("promptHistory")] public Transcripts.PromptHistorySettings PromptHistory { get; set; } = new(); /// /// 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. /// [JsonPropertyName("maxDelegationDepth")] public int MaxDelegationDepth { get; set; } = 3; /// /// 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. /// [JsonPropertyName("delegationGateTimeoutSeconds")] public int DelegationGateTimeoutSeconds { get; set; } = 120; /// /// #682: How many scheduled task runs may be in flight at the same time. Due tasks /// beyond this wait in order of priority and due time and start as soon as a run /// finishes. /// /// Why a cap at all. Cron expressions pile up on round times. Without a /// cap, fifty due tasks meant fifty simultaneous model calls — provider rate limits, /// a cost spike, and all fifty passing the instance budget check together, because /// that check runs per run before it starts. /// /// Per instance for now; per unit comes with #685. Values below 1 count as 1. /// A run the human starts by hand does not wait for a free slot, but it occupies one /// while it runs. /// [JsonPropertyName("maxParallelTaskRuns")] public int MaxParallelTaskRuns { get; set; } = 4; [JsonPropertyName("agents")] public List Agents { get; set; } = new(); /// /// #683: Departments, teams, groups — the organisation tree. Empty means no structure: /// every agent can reach every other, as before units existed. /// [JsonPropertyName("units")] public List Units { get; set; } = new(); /// /// #684: Shared workspaces with member lists — one per unit, plus those shared across /// units on purpose. The instance's one shared folder is not listed here; it stays the /// workspace shared, governed by each agent's level as before. /// [JsonPropertyName("workspaces")] public List Workspaces { get; set; } = new(); [JsonPropertyName("services")] public List 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; } }