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