tool: avoid mutating discovered presets; use local normalized model values/shallow copies
This commit is contained in:
28
src/tool.ts
28
src/tool.ts
@@ -104,6 +104,10 @@ export function createSubagentTool(deps: {
|
||||
const discovery = (deps.discoverSubagentPresets ?? discoverSubagentPresets)(ctx.cwd);
|
||||
const presets = discovery.presets;
|
||||
|
||||
// Note: presets are discovered/owned by the discovery layer and should be treated
|
||||
// as immutable. Do not mutate preset objects in place. When a canonical/normalized
|
||||
// model value is needed, use a local variable or a shallow copy of the preset.
|
||||
|
||||
// Adapter: accept only the flattened shape { callModel?, presetModel? }
|
||||
// to keep the tool logic simple. If a resolver was injected with the
|
||||
// older test/hooks shape, we call it with both key names so it can read
|
||||
@@ -207,7 +211,9 @@ export function createSubagentTool(deps: {
|
||||
params.model = normalized;
|
||||
}
|
||||
|
||||
// Validate preset default model if present
|
||||
// Validate preset default model if present. Do not mutate discovered preset objects —
|
||||
// keep a local normalized value (or a shallow copy when passing to runTask).
|
||||
let normalizedPresetModelForSelection: string | undefined = undefined;
|
||||
if (preset.model !== undefined) {
|
||||
const normalizedPresetModel = normalizeModelReference(preset.model);
|
||||
if (!normalizedPresetModel) {
|
||||
@@ -216,12 +222,11 @@ export function createSubagentTool(deps: {
|
||||
"single",
|
||||
);
|
||||
}
|
||||
// Use canonical preset model
|
||||
preset.model = normalizedPresetModel;
|
||||
normalizedPresetModelForSelection = normalizedPresetModel;
|
||||
}
|
||||
|
||||
// Ensure an effective model exists for this run (explicit override wins)
|
||||
const singleSelection = callResolveChildModel({ callModel: params.model, presetModel: preset.model });
|
||||
const singleSelection = callResolveChildModel({ callModel: params.model, presetModel: normalizedPresetModelForSelection });
|
||||
const singleRequested = singleSelection.requestedModel ?? params.model ?? preset.model;
|
||||
if (!singleRequested) {
|
||||
return makeErrorResult(
|
||||
@@ -231,10 +236,11 @@ export function createSubagentTool(deps: {
|
||||
}
|
||||
|
||||
try {
|
||||
const presetForRun = normalizedPresetModelForSelection === undefined ? preset : { ...preset, model: normalizedPresetModelForSelection };
|
||||
const result = await runTask({
|
||||
task: params.task,
|
||||
cwd: params.cwd,
|
||||
preset,
|
||||
preset: presetForRun,
|
||||
taskModel: params.model,
|
||||
mode: "single",
|
||||
});
|
||||
@@ -273,6 +279,8 @@ export function createSubagentTool(deps: {
|
||||
t.model = normalized;
|
||||
}
|
||||
|
||||
// Validate preset default model. Do not mutate the discovered preset object.
|
||||
let normalizedPresetModelForTask: string | undefined = undefined;
|
||||
if (preset.model !== undefined) {
|
||||
const normalizedPresetModel = normalizeModelReference(preset.model);
|
||||
if (!normalizedPresetModel) {
|
||||
@@ -281,12 +289,12 @@ export function createSubagentTool(deps: {
|
||||
"parallel",
|
||||
);
|
||||
}
|
||||
preset.model = normalizedPresetModel;
|
||||
normalizedPresetModelForTask = normalizedPresetModel;
|
||||
}
|
||||
|
||||
// Ensure an effective model exists for this task
|
||||
const sel = callResolveChildModel({ callModel: t.model, presetModel: preset.model });
|
||||
const requested = sel.requestedModel ?? t.model ?? preset.model;
|
||||
const sel = callResolveChildModel({ callModel: t.model, presetModel: normalizedPresetModelForTask });
|
||||
const requested = sel.requestedModel ?? t.model ?? normalizedPresetModelForTask;
|
||||
if (!requested) {
|
||||
return makeErrorResult(
|
||||
`Parallel task ${index + 1} has no model. Provide an explicit 'model' or set a default model on preset "${preset.name}". Available models: ${availableModelsText}`,
|
||||
@@ -311,12 +319,14 @@ export function createSubagentTool(deps: {
|
||||
const liveResults: SubagentRunResult[] = [];
|
||||
const results = await mapWithConcurrencyLimit(params.tasks, MAX_CONCURRENCY, async (task: any, index) => {
|
||||
const preset = presets.find((p) => p.name === task.preset)!;
|
||||
const normalizedPresetModel = preset.model === undefined ? undefined : normalizeModelReference(preset.model);
|
||||
const presetForRun = normalizedPresetModel === undefined ? preset : { ...preset, model: normalizedPresetModel };
|
||||
const result = await runTask({
|
||||
task: task.task,
|
||||
cwd: task.cwd,
|
||||
taskModel: task.model,
|
||||
taskIndex: index,
|
||||
preset,
|
||||
preset: presetForRun,
|
||||
mode: "parallel",
|
||||
});
|
||||
liveResults[index] = result;
|
||||
|
||||
Reference in New Issue
Block a user