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 情報は task スコープにキャッシュ:
getWorkspaceInfoは初回取得後にworkspacePathsCacheとisMultiRootScenarioCacheにキャッシュする (getWorkspaceInfo:23)。task のライフサイクル中は workspace のルートディレクトリが基本的に変わらないため、キャッシュで重複 IPC 呼び出しを省く。
主要ファイル
ToolValidator:10— クラス定義。コンストラクト時にClineIgnoreControllerを受け取る。assertRequiredParams:17— パラメータ名を走査し、空値や純空白をエラーとして返す。checkClineIgnorePath:32—clineIgnoreController.validateAccessを呼び出し、弾かれたらエラーを返す。AutoApprove:8— クラス定義。stateManagerと二つのキャッシュフィールドを保持。getWorkspaceInfo:23— キャッシュされた workspace パスと multi-root 判定。shouldAutoApproveTool:42— 三段階スイッチ: YOLO、autoApproveAll、細粒度 autoApprovalSettings。yolo branch:43— YOLO モードでは読み/ファイル操作が[true, true]、browser/web/MCP はtrueを返す。autoApproveAll branch:66— YOLO と同じ範囲だが、次位のスイッチとして機能。autoApprovalSettings:88— 細粒度分岐。actions.readFiles / editFiles / executeSafeCommands などで細分化。shouldAutoApproveToolWithPath:122— パス付き版。ローカル/外部を判定した上で auto-approve タプルと組み合わせる。multi-root workspace check:138— multi-root シナリオではisLocatedInWorkspaceで任意のルートを検査。final decision:163—local && autoApproveLocalまたはexternal && autoApproveLocal && autoApproveExternalのときだけ許可。ToolExecutor delegations:48— ToolExecutor は自身を callback として bind し、autoApproverに転送する。complete block approval:205— complete block はshouldAutoApproveToolWithPathで auto-approval か manualask("tool", ...)かを決める。partial block approval:74— ストリーミング partial block も同じ 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 なら即座に 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 も true のときだけチェックされる。つまり「外部自動承認」は「ローカルも自動承認」を暗黙に含む。これは Cline のデフォルトの安全姿勢であり、外部操作は常にローカルより一段厳しくなる。
境界と失敗
- 空文字列も missing 扱い:
assertRequiredParamsはString(val).trim() === ""で判定し、空文字列も純空白も missing と見なす (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 に列挙されているのは read / write / 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 へ。