ToolValidator 與 autoApprove:執行前的兩道閘門
職責
工具真正跑起來之前有兩道獨立的檢查:ToolValidator 管參數和路徑合法性,AutoApprove 管要不要彈審批框。兩者都不執行工具,只決定「能不能跑」和「要不要問使用者」。
ToolValidator 是個非常薄的工具類別,給 handler 用。它提供兩個方法:assertRequiredParams 檢查必需參數有沒有,checkClineIgnorePath 檢查路徑有沒有被 .clineignore 攔掉 (assertRequiredParams:17)。它本身沒有副作用,只回傳 ValidationResult 讓 handler 決定怎麼處理。
AutoApprove 稍微複雜一點,它是 ToolExecutor 私有欄位,被 asToolConfig 注入到 TaskConfig 裡 (autoApprover init:126)。handler 透過 config.callbacks.shouldAutoApproveTool 或 config.callbacks.shouldAutoApproveToolWithPath 間接調它 (callback wiring:181)。它讀三個全域開關:yoloModeToggled、autoApproveAllToggled、autoApprovalSettings,加上路徑在不在 workspace 內,給出「本地自動批准」「外部自動批准」「不批准」三檔結果。
設計動機
- Validator 與 AutoApprove 分離:合法性檢查和審批是兩件正交的事——參數合法但使用者沒批准,或者參數非法但使用者已批准,都可能出現。拆成兩個物件讓 handler 能在錯誤路徑上不繞開審批、在審批通過後還能攔下非法參數。
- Validator 只看不改:回傳
ValidationResult而不是拋錯,handler 自己決定是 push 錯誤結果還是給個降級路徑 (ValidationResult:4)。這樣不同 handler 可以共享同一種錯誤展示方式。 - AutoApprove 的三檔開關:
yolo一切放行、autoApproveAll範圍次之、autoApprovalSettings最細粒度 (區分本地/外部、安全命令/全部命令) (shouldAutoApproveTool:42)。從寬到嚴的開關優先級在前者。 - 路徑分類決定外部存取:讀 / 寫工具的 auto-approve 結果是
[local, external]元組 (tuple result:96)。local控制工作區內檔案,external控制工作區外檔案。預設工作區外更嚴格,避免誤改系統檔案。 - workspace info 快取到 task 範圍:
getWorkspaceInfo第一次取後快取到workspacePathsCache和isMultiRootScenarioCache(getWorkspaceInfo:23)。因為 task 生命週期內 workspace 根目錄基本不變,快取省掉重複 IPC 呼叫。
關鍵檔案
ToolValidator:10— 類別定義,建構接ClineIgnoreController。assertRequiredParams:17— 遍歷參數名,空值或純空格回傳錯誤。checkClineIgnorePath:32— 調clineIgnoreController.validateAccess,被攔回傳錯誤。AutoApprove:8— 類別定義,持有stateManager和兩個快取欄位。getWorkspaceInfo:23— 快取的 workspace 路徑和多根判定。shouldAutoApproveTool:42— 三檔開關:YOLO、autoApproveAll、細粒度 autoApprovalSettings。yolo branch:43— YOLO 模式下唯讀 / 檔案操作回傳[true, true],瀏覽器/web/MCP 回傳true。autoApproveAll branch:66— 跟 YOLO 同範圍,但作為次級開關。autoApprovalSettings:88— 細粒度分支,按 actions.readFiles / editFiles / executeSafeCommands 等細分。shouldAutoApproveToolWithPath:122— 帶路徑的版本,判定本地 / 外部後跟 auto-approve 元組拼。multi-root workspace check:138— 多根場景用isLocatedInWorkspace檢查任意根。final decision:163—local && autoApproveLocal或external && autoApproveLocal && autoApproveExternal才放行。ToolExecutor delegations:48— ToolExecutor 把自己綁成 callback,轉發給autoApprover。complete block approval:205— 完整塊走shouldAutoApproveToolWithPath決定 auto-approval 還是 manualask("tool", ...)。partial block approval:74— 流式 partial 塊也走同一套 auto-approve 檢查,邊流邊決定要不要彈框。
資料流
handler 拿到 block 後,典型流程是先用 Validator 校參數,再用 AutoApprove 決定走哪條 UI 路徑。下面是 WriteToFileToolHandler 在 partial block 階段的實際呼叫:
// apps/vscode/src/core/task/tools/handlers/WriteToFileToolHandler.ts
// Handle auto-approval vs manual approval for partial
if (await uiHelpers.shouldAutoApproveToolWithPath(block.name, relPath)) {
await uiHelpers.removeLastPartialMessageIfExistsWithType("ask", "tool") // in case the user changes auto-approval settings mid stream
await uiHelpers.say("tool", partialMessage, undefined, undefined, block.partial)
} else {
await uiHelpers.removeLastPartialMessageIfExistsWithType("say", "tool")
await uiHelpers.ask("tool", partialMessage, block.partial).catch(() => {})
}shouldAutoApproveToolWithPath 內部先看 YOLO / autoApproveAll 兩個總開關,任何一個為真直接回傳 true (yolo short circuit:126)。再算路徑在不在 workspace 內,跟 shouldAutoApproveTool 回傳的元組拼出最終決策:
// apps/vscode/src/core/task/tools/autoApprove.ts
const autoApproveResult = this.shouldAutoApproveTool(blockname)
const [autoApproveLocal, autoApproveExternal] = Array.isArray(autoApproveResult)
? autoApproveResult
: [autoApproveResult, false]
if ((isLocalRead && autoApproveLocal) || (!isLocalRead && autoApproveLocal && autoApproveExternal)) {
return true
}
return false這裡 autoApproveExternal 只有在 autoApproveLocal 也為真時才被檢查——意味著「外部自動批准」隱含「本地也自動批准」。這是 Cline 的預設安全姿態:外部操作永遠比本地嚴格一檔。
邊界與失敗
- 空字串也算缺失:
assertRequiredParams用String(val).trim() === ""判斷,空串或純空格都算缺失 (empty check:21)。但content欄位允許空字串 (新建空檔案),所以 handler 用== null自行判斷,不走 Validator (content null check:42)。 - 無路徑預設不批:
shouldAutoApproveToolWithPath拿到undefined路徑時直接設isLocalRead = false(no path default:153)。安全側的預設值,工具拿不到路徑就當外部操作處理。 - 快取假設 workspace 不變:註解明說「assumes that the task has a fixed set of workspace roots」(
cache assumption:11)。如果使用者在 task 跑到一半加新根目錄,這個 task 拿到的還是舊快取,要等下一個 task 才生效。 - YOLO 不覆蓋所有工具:YOLO 模式 switch 裡只列了讀 / 寫 / bash / browser / web / MCP 等,沒列
ASK、ATTEMPT、NEW_TASK這些 (yolo switch list:44)。這類工具預設 fallthrough 到return false,需要正常審批。 - YOLO 元組只對 file/bash 類生效:
[true, true]元組只給 read/write/bash/subagents (yolo tuple:55)。browser/web/MCP 拿到的是true單值,意味著這倆在 YOLO 下不區分本地 / 外部。 - Validator 不被舊 ToolExecutor 使用:類別註解明說「The legacy ToolExecutor switch remains unchanged and does not depend on this」(
legacy note:8)。舊路徑裡參數檢查是散落在各 case 裡的,Validator 只服務新 handler。
小結
Validator 是參數 / 路徑的硬檢查,失敗就報錯讓模型重試;AutoApprove 是使用者偏好驅動的軟開關,決定走 auto-approval 還是彈框。兩者一起在工具執行前充當守門員,handler 調它們之後才決定下一步動作。要看 handler 清單怎麼註冊,轉 /tools/handlers-overview;要看路由本身,轉 /tools/coordinator。