Skip to content

Commit 975c425

Browse files
committed
fix: check toolRequirements before ALWAYS_AVAILABLE_TOOLS
Moves the toolRequirements check before the ALWAYS_AVAILABLE_TOOLS early-return in isToolAllowedForMode(). This ensures disabledTools can block always-available tools (switch_mode, new_task, etc.) at execution time, making the validation layer consistent with the filtering layer.
1 parent ea68c49 commit 975c425

File tree

2 files changed

+22
-11
lines changed

2 files changed

+22
-11
lines changed

src/core/tools/__tests__/validateToolUse.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,15 @@ describe("mode-validator", () => {
163163
// Even in code mode which allows all tools, disabled requirement should take precedence
164164
expect(isToolAllowedForMode("apply_diff", codeMode, [], requirements)).toBe(false)
165165
})
166+
167+
it("prioritizes requirements over ALWAYS_AVAILABLE_TOOLS", () => {
168+
// Tools in ALWAYS_AVAILABLE_TOOLS (switch_mode, new_task, etc.) should still
169+
// be blockable via toolRequirements / disabledTools
170+
const requirements = { switch_mode: false, new_task: false, attempt_completion: false }
171+
expect(isToolAllowedForMode("switch_mode", codeMode, [], requirements)).toBe(false)
172+
expect(isToolAllowedForMode("new_task", codeMode, [], requirements)).toBe(false)
173+
expect(isToolAllowedForMode("attempt_completion", codeMode, [], requirements)).toBe(false)
174+
})
166175
})
167176
})
168177

src/core/tools/validateToolUse.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,19 @@ export function isToolAllowedForMode(
126126
experiments?: Record<string, boolean>,
127127
includedTools?: string[], // Opt-in tools explicitly included (e.g., from modelInfo)
128128
): boolean {
129-
// Always allow these tools
129+
// Check tool requirements first — explicit disabling takes priority over everything,
130+
// including ALWAYS_AVAILABLE_TOOLS. This ensures disabledTools works consistently
131+
// at both the filtering layer and the execution-time validation layer.
132+
if (toolRequirements && typeof toolRequirements === "object") {
133+
if (tool in toolRequirements && !toolRequirements[tool]) {
134+
return false
135+
}
136+
} else if (toolRequirements === false) {
137+
// If toolRequirements is a boolean false, all tools are disabled
138+
return false
139+
}
140+
141+
// Always allow these tools (unless explicitly disabled above)
130142
if (ALWAYS_AVAILABLE_TOOLS.includes(tool as any)) {
131143
return true
132144
}
@@ -147,16 +159,6 @@ export function isToolAllowedForMode(
147159
}
148160
}
149161

150-
// Check tool requirements if any exist
151-
if (toolRequirements && typeof toolRequirements === "object") {
152-
if (tool in toolRequirements && !toolRequirements[tool]) {
153-
return false
154-
}
155-
} else if (toolRequirements === false) {
156-
// If toolRequirements is a boolean false, all tools are disabled
157-
return false
158-
}
159-
160162
const mode = getModeBySlug(modeSlug, customModes)
161163

162164
if (!mode) {

0 commit comments

Comments
 (0)