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; }
}