using System.Text.Json.Serialization; namespace ClawdDotNet.Core.Runtimes; /// #712: The runtimes an agent can work in. public static class RuntimeKinds { /// Our engine with OpenRouter — the default, everything but programming. public const string Native = "native"; /// Claude Code in headless mode, in the sandbox. public const string ClaudeCode = "claude-code"; /// A program whose output is the result, in the sandbox. public const string Command = "command"; public static readonly IReadOnlyList All = [Native, ClaudeCode, Command]; public static bool IsKnown(string? kind) => All.Contains(Normalize(kind)); public static string Normalize(string? kind) => string.IsNullOrWhiteSpace(kind) ? Native : kind.Trim().ToLowerInvariant(); } /// /// #712: In which runtime an agent works (runtime in its configuration). Without it, /// the agent is native, as before. /// public sealed class AgentRuntimeConfig { [JsonPropertyName("kind")] public string Kind { get; set; } = RuntimeKinds.Native; /// /// Claude Code: the tools it may use without asking. Empty: . /// Anything not listed is denied — there is no one to ask. /// [JsonPropertyName("allowedTools")] public List AllowedTools { get; set; } = []; /// Claude Code: the most turns of one run. [JsonPropertyName("maxTurns")] public int MaxTurns { get; set; } = 30; [JsonPropertyName("timeoutMinutes")] public int TimeoutMinutes { get; set; } = 30; /// Command: the program — absolute, on the sandbox's PATH, or relative to the workspace. [JsonPropertyName("command")] public string Command { get; set; } = ""; [JsonPropertyName("arguments")] public List Arguments { get; set; } = []; /// Hosts the run may reach beyond what the runtime itself needs. [JsonPropertyName("egress")] public List Egress { get; set; } = []; [JsonIgnore] public bool IsForeign => RuntimeKinds.Normalize(Kind) != RuntimeKinds.Native; [JsonIgnore] public TimeSpan Timeout => TimeSpan.FromMinutes(Math.Clamp(TimeoutMinutes, 1, 240)); } /// /// #712: The Claude Code account of the instance (claudeCode in instance.json). /// /// Two ways to sign in, deliberately. Whether an automated agent system may run /// on a consumer subscription is not settled, and a subscription shares its quota with the /// human who owns it. The API key is the default; the subscription (a token from /// claude setup-token) is a matter of configuration, not of rebuilding. /// public sealed class ClaudeCodeSettings { public const string ApiKeyAuth = "api-key"; public const string SubscriptionAuth = "subscription"; /// The claude program; empty: looked for in the usual places. [JsonPropertyName("executable")] public string Executable { get; set; } = ""; [JsonPropertyName("auth")] public string Auth { get; set; } = ApiKeyAuth; /// An Anthropic API key — best one of its own, with a spending limit. [JsonPropertyName("apiKey")] public string ApiKey { get; set; } = ""; /// A subscription's token from claude setup-token. [JsonPropertyName("oauthToken")] public string OAuthToken { get; set; } = ""; /// Further read-only host paths the program needs — Node and a package directory for an npm install. [JsonPropertyName("readOnlyPaths")] public List ReadOnlyPaths { get; set; } = []; [JsonIgnore] public bool OnSubscription => string.Equals(Auth, SubscriptionAuth, StringComparison.OrdinalIgnoreCase); }