- Core/Mcp/McpClient: Streamable HTTP (MCP 2025-06-18) - initialize, tools/list with paging, tools/call; answers as JSON or event stream; session kept and opened again when the server forgets it; answers capped at 4 MB. - McpServerTool: one server is one tool (mcp_<id>); its remote tools are the operations, so rights, preflight, staging, lead approvals, votes, audit and budget apply. On approval by default unless the human trusts an operation; reads foreign content; the server's descriptions are cut short and marked. - McpConnections: registers, refreshes every 15 minutes, follows changes. - InstanceConfig.mcpServers with protected headers; /api/mcp-servers; settings card "External MCP servers"; tool catalog includes the servers. - stdio servers are not part of this (sandbox runner, separate step). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
236 lines
10 KiB
C#
236 lines
10 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><Instanzverzeichnis>/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><Instanzverzeichnis>/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_<Instanzname>_</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>
|
|
/// <summary>
|
|
/// #689: Escalations and conflicts — how long the agents have to solve a case before a
|
|
/// human is called, and how many escalations a unit may raise a day before they go
|
|
/// straight to a human.
|
|
/// </summary>
|
|
[JsonPropertyName("cases")]
|
|
public Cases.CaseSettings Cases { get; set; } = new();
|
|
|
|
/// <summary>#717: When unused budget is filled, and when a task counts as large.</summary>
|
|
[JsonPropertyName("fillWork")]
|
|
public Budget.FillWorkSettings FillWork { get; set; } = new();
|
|
|
|
/// <summary>#712: The Claude Code account the instance's claude-code agents run on.</summary>
|
|
[JsonPropertyName("claudeCode")]
|
|
public Runtimes.ClaudeCodeSettings ClaudeCode { get; set; } = new();
|
|
|
|
/// <summary>#719: The history of the workspaces — on by default, 90 days.</summary>
|
|
[JsonPropertyName("fileHistory")]
|
|
public Files.FileHistorySettings FileHistory { get; set; } = new();
|
|
|
|
/// <summary>#197: External MCP servers whose tools the agents may use — set up by a human only.</summary>
|
|
[JsonPropertyName("mcpServers")]
|
|
public List<Mcp.McpServerConfig> McpServers { get; set; } = new();
|
|
|
|
/// <summary>#699: When a change in the units' costs is reported.</summary>
|
|
[JsonPropertyName("costWatch")]
|
|
public Accounting.CostWatchSettings CostWatch { get; set; } = new();
|
|
|
|
[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;
|
|
|
|
/// <summary>
|
|
/// #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.
|
|
///
|
|
/// <para><b>Why a cap at all.</b> 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.</para>
|
|
///
|
|
/// <para>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.</para>
|
|
/// </summary>
|
|
[JsonPropertyName("maxParallelTaskRuns")]
|
|
public int MaxParallelTaskRuns { get; set; } = 4;
|
|
|
|
[JsonPropertyName("agents")]
|
|
public List<AgentConfig> Agents { get; set; } = new();
|
|
|
|
/// <summary>
|
|
/// #683: Departments, teams, groups — the organisation tree. Empty means no structure:
|
|
/// every agent can reach every other, as before units existed.
|
|
/// </summary>
|
|
[JsonPropertyName("units")]
|
|
public List<UnitConfig> Units { get; set; } = new();
|
|
|
|
/// <summary>
|
|
/// #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 <c>shared</c>, governed by each agent's level as before.
|
|
/// </summary>
|
|
[JsonPropertyName("workspaces")]
|
|
public List<WorkspaceConfig> Workspaces { 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; }
|
|
}
|