diff --git a/src/common/utils/goals/resolveGoalSetIntent.test.ts b/src/common/utils/goals/resolveGoalSetIntent.test.ts index 990def821d..6d0e3f1f62 100644 --- a/src/common/utils/goals/resolveGoalSetIntent.test.ts +++ b/src/common/utils/goals/resolveGoalSetIntent.test.ts @@ -40,12 +40,27 @@ describe("resolveModelGoalSetIntent", () => { expect(intent.turnCap).toBe(8); }); - test("honors no-budget defaults for model tools without treating null as explicit clear", () => { + test("applies the positive budget default even when user-authored goals may omit budgets", () => { const intent = resolveModelGoalSetIntent( { objective: "ship", budgetCents: null, turnCap: null }, { ...defaults, alwaysRequireExplicitBudget: false, defaultTurnCap: null } ); + expect(intent.budgetCents).toBe(500); + expect(intent.turnCap).toBeNull(); + }); + + test("resolves to unbounded only when effective defaults have no positive budget or turn cap", () => { + const intent = resolveModelGoalSetIntent( + { objective: "ship", budgetCents: null, turnCap: null }, + { + ...defaults, + defaultBudgetCents: 0, + defaultTurnCap: null, + alwaysRequireExplicitBudget: false, + } + ); + expect(intent.budgetCents).toBeNull(); expect(intent.turnCap).toBeNull(); }); diff --git a/src/common/utils/goals/resolveGoalSetIntent.ts b/src/common/utils/goals/resolveGoalSetIntent.ts index aaf0c89d3e..9b42e81206 100644 --- a/src/common/utils/goals/resolveGoalSetIntent.ts +++ b/src/common/utils/goals/resolveGoalSetIntent.ts @@ -101,6 +101,8 @@ export function resolveGoalSetIntent( * providers. For agent-created goals, `null` must therefore mean "use the * effective defaults" rather than the UI meaning "the user explicitly cleared * this bound"; otherwise a model could accidentally create an unbounded goal. + * The budget default applies even when user-authored forms allow omitted + * budgets to mean "no budget" — a model omission is not an explicit user clear. */ export function resolveModelGoalSetIntent( input: GoalSetIntentInput, @@ -110,9 +112,7 @@ export function resolveModelGoalSetIntent( const budgetCents = input.budgetCents != null ? normalizeGoalBudgetCents(input.budgetCents) - : defaults.alwaysRequireExplicitBudget - ? normalizeGoalBudgetCents(defaults.defaultBudgetCents) - : null; + : normalizeGoalBudgetCents(defaults.defaultBudgetCents); const turnCap = input.turnCap ?? defaults.defaultTurnCap; return { diff --git a/src/node/services/tools/goal.test.ts b/src/node/services/tools/goal.test.ts index 72c01fb710..9991135f16 100644 --- a/src/node/services/tools/goal.test.ts +++ b/src/node/services/tools/goal.test.ts @@ -158,6 +158,27 @@ describe("goal tools", () => { expect(result).toMatchObject({ goal: { budgetCents: 450, turnCap: 3 } }); }); + test("set_goal uses positive default budget even when omitted user budgets are allowed", async () => { + const tool = createSetGoalTool({ + cwd: "/tmp", + runtimeTempDir: "/tmp", + runtime: inertRuntime, + workspaceId, + goalService, + goalDefaults: { + defaultBudgetCents: 650, + defaultTurnCap: null, + alwaysRequireExplicitBudget: false, + }, + }); + + const result: unknown = await Promise.resolve( + tool.execute!({ objective: "Use global budget default" }, mockToolCallOptions) + ); + + expect(result).toMatchObject({ goal: { budgetCents: 650, turnCap: null } }); + }); + test("set_goal accepts explicit positive budget and turn cap", async () => { const tool = createSetGoalTool({ cwd: "/tmp", diff --git a/src/node/services/tools/set_goal.ts b/src/node/services/tools/set_goal.ts index 13be1d3897..60ba8d3c3a 100644 --- a/src/node/services/tools/set_goal.ts +++ b/src/node/services/tools/set_goal.ts @@ -42,7 +42,7 @@ export const createSetGoalTool: ToolFactory = (config) => { assertResolvedModelGoalBounds(resolved); if (resolved.budgetCents == null && resolved.turnCap == null) { throw new Error( - "set_goal requires a budget or turn cap for model-created goals. Ask the user to provide a budget/turn cap or configure workspace goal defaults." + "set_goal requires a budget or turn cap for model-created goals. Ask the user to provide one, or configure a positive effective default goal budget/turn cap." ); }