feat(worker): add get_effective_run_config MCP tool
Adds a read-only get_effective_run_config(taskId) tool that reports the model/max-turns/effort/permission-mode/agent-path/system-prompt/skills a task will actually run with, each tagged with its source (task/list/ preset/global), plus max-turns' raw requested value and clamp status. Extracted the model/max-turns/agent-path resolution out of TaskRunner.ResolveConfigAsync into EffectiveRunConfigResolver so the run path and the new reporting tool share one codepath instead of risking drift, per docs/explore-notes/worker-task-pipeline.md's max-turns trap.
This commit is contained in:
@@ -559,29 +559,25 @@ public sealed class TaskRunner
|
||||
var requestedSkills = UnionSkillNames(task.SessionSkills, listConfig?.SessionSkills, global.SessionSkills);
|
||||
var skillNames = await FilterToInstalledSkillsAsync(task.Id, requestedSkills, ct);
|
||||
|
||||
// The model decides the global effort/turn defaults: one preset row per model alias
|
||||
// (Settings → General). List- and task-level max-turns overrides still win.
|
||||
var model = task.Model ?? listConfig?.Model ?? global.DefaultModel;
|
||||
var preset = Data.Models.ModelPresets.For(global.ModelPresets, model, global.DefaultMaxTurns);
|
||||
|
||||
var requestedMaxTurns = task.MaxTurns ?? listConfig?.MaxTurns ?? preset.MaxTurns;
|
||||
var maxTurns = ResolveMaxTurns(task.MaxTurns, listConfig?.MaxTurns, preset.MaxTurns, global.MaxTurnsCeiling);
|
||||
if (maxTurns < requestedMaxTurns)
|
||||
// Model/max-turns/effort/agent-path resolution is shared with get_effective_run_config
|
||||
// so the two can never report different numbers for the same task.
|
||||
var effective = EffectiveRunConfigResolver.Resolve(task, listConfig, global, systemFile, improvementPrompt);
|
||||
if (effective.MaxTurnsClamped)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Task {TaskId}: max turns clamped to ceiling (requested={Requested}, effective={Effective}, ceiling={Ceiling})",
|
||||
task.Id, requestedMaxTurns, maxTurns, global.MaxTurnsCeiling);
|
||||
task.Id, effective.RequestedMaxTurns, effective.MaxTurns, global.MaxTurnsCeiling);
|
||||
}
|
||||
|
||||
return new ClaudeRunConfig(
|
||||
Model: model,
|
||||
Model: effective.Model,
|
||||
SystemPrompt: string.IsNullOrWhiteSpace(instructions) ? null : instructions,
|
||||
AgentPath: task.AgentPath ?? listConfig?.AgentPath,
|
||||
AgentPath: effective.AgentPath,
|
||||
ResumeSessionId: resumeSessionId,
|
||||
MaxTurns: maxTurns,
|
||||
PermissionMode: global.DefaultPermissionMode,
|
||||
MaxTurns: effective.MaxTurns,
|
||||
PermissionMode: effective.PermissionMode,
|
||||
SkillNames: skillNames,
|
||||
Effort: preset.Effort);
|
||||
Effort: effective.Effort);
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyList<string>> FilterToInstalledSkillsAsync(
|
||||
@@ -597,7 +593,7 @@ public sealed class TaskRunner
|
||||
}
|
||||
|
||||
var installedNames = installed.Select(s => s.Name).ToHashSet(StringComparer.Ordinal);
|
||||
var resolved = requestedSkills.Where(installedNames.Contains).ToList();
|
||||
var resolved = FilterToInstalled(requestedSkills, installedNames);
|
||||
var dropped = requestedSkills.Where(n => !installedNames.Contains(n)).ToList();
|
||||
if (dropped.Count > 0)
|
||||
{
|
||||
@@ -609,6 +605,12 @@ public sealed class TaskRunner
|
||||
return resolved;
|
||||
}
|
||||
|
||||
/// <summary>Shared with get_effective_run_config so reported skill names match what a run
|
||||
/// would actually filter down to.</summary>
|
||||
internal static IReadOnlyList<string> FilterToInstalled(
|
||||
IReadOnlyList<string> requestedSkills, IReadOnlySet<string> installedNames)
|
||||
=> requestedSkills.Where(installedNames.Contains).ToList();
|
||||
|
||||
internal static IReadOnlyList<string> UnionSkillNames(params string?[] jsonArrays)
|
||||
{
|
||||
var names = new List<string>();
|
||||
|
||||
Reference in New Issue
Block a user