Bisher gab es nur AppSettings.DefaultPermissionMode global — ein Task, der plan oder acceptEdits braucht, erzwang das Umstellen der globalen Einstellung. Neue Spalten tasks.permission_mode und list_config.permission_mode, Auflösung task -> list -> global im EffectiveRunConfigResolver (den TaskRunner und get_effective_run_config gemeinsam nutzen), ComboBox mit Inherited-Badge im geteilten Agent-Editor. MigrationBaselineTests: das Fixture baute per EnsureCreated das heutige Schema und stempelte Legacy-History darauf — jede Migration nach dem Squash lief damit in 'duplicate column name'. Es migriert jetzt gezielt bis InitialCreate und prueft 'nichts pending' statt 'InitialCreate ist die einzige Zeile'.
55 lines
3.0 KiB
C#
55 lines
3.0 KiB
C#
using ClaudeDo.Data.Models;
|
|
|
|
namespace ClaudeDo.Worker.Runner;
|
|
|
|
/// <summary>The task/list/preset/global resolution <see cref="TaskRunner"/> actually runs with,
|
|
/// plus where each value came from — so a caller can tell a genuine default from an override
|
|
/// without re-deriving the resolution order itself.</summary>
|
|
public sealed record EffectiveRunConfig(
|
|
string Model, string ModelSource,
|
|
int MaxTurns, string MaxTurnsSource, int RequestedMaxTurns, bool MaxTurnsClamped,
|
|
string Effort,
|
|
string? AgentPath, string? AgentPathSource,
|
|
string PermissionMode,
|
|
bool SystemPromptSet, IReadOnlyList<string> SystemPromptSources);
|
|
|
|
/// <summary>Single source of truth for "which value wins" — shared by <see cref="TaskRunner"/>'s
|
|
/// actual run path and any read-only reporting of the same resolution (e.g. the
|
|
/// <c>get_effective_run_config</c> MCP tool), so the two can never drift apart.</summary>
|
|
public static class EffectiveRunConfigResolver
|
|
{
|
|
public static EffectiveRunConfig Resolve(
|
|
TaskEntity task, ListConfigEntity? listConfig, AppSettingsEntity global,
|
|
string? systemFile, string? improvementPrompt)
|
|
{
|
|
var model = task.Model ?? listConfig?.Model ?? global.DefaultModel;
|
|
var modelSource = task.Model is not null ? "task" : listConfig?.Model is not null ? "list" : "global";
|
|
|
|
var preset = ModelPresets.For(global.ModelPresets, model, global.DefaultMaxTurns);
|
|
|
|
var requestedMaxTurns = task.MaxTurns ?? listConfig?.MaxTurns ?? preset.MaxTurns;
|
|
var maxTurnsSource = task.MaxTurns is not null ? "task" : listConfig?.MaxTurns is not null ? "list" : "preset";
|
|
// ResolveMaxTurns is declared int? but always returns a value (Math.Min of non-null inputs).
|
|
var maxTurns = TaskRunner.ResolveMaxTurns(task.MaxTurns, listConfig?.MaxTurns, preset.MaxTurns, global.MaxTurnsCeiling)!.Value;
|
|
|
|
var agentPath = task.AgentPath ?? listConfig?.AgentPath;
|
|
var agentPathSource = task.AgentPath is not null ? "task" : listConfig?.AgentPath is not null ? "list" : null;
|
|
|
|
var systemPromptSources = new List<string>();
|
|
if (!string.IsNullOrWhiteSpace(systemFile)) systemPromptSources.Add("systemFile");
|
|
if (!string.IsNullOrWhiteSpace(improvementPrompt)) systemPromptSources.Add("improvementPrompt");
|
|
if (!string.IsNullOrWhiteSpace(global.DefaultClaudeInstructions)) systemPromptSources.Add("global");
|
|
if (!string.IsNullOrWhiteSpace(listConfig?.SystemPrompt)) systemPromptSources.Add("list");
|
|
if (!string.IsNullOrWhiteSpace(task.SystemPrompt)) systemPromptSources.Add("task");
|
|
|
|
return new EffectiveRunConfig(
|
|
model, modelSource,
|
|
maxTurns, maxTurnsSource, requestedMaxTurns, maxTurns < requestedMaxTurns,
|
|
preset.Effort,
|
|
agentPath, agentPathSource,
|
|
PermissionModeResolver.Resolve(
|
|
model, task.PermissionMode ?? listConfig?.PermissionMode ?? global.DefaultPermissionMode),
|
|
systemPromptSources.Count > 0, systemPromptSources);
|
|
}
|
|
}
|