Skip to content

ToolValidator と autoApprove:実行前の二重のゲート

源码版本v4.0.10

役割

ツールが実際に起動する前に二つの独立したチェックが走る: 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)。三つのグローバルスイッチ (yoloModeToggledautoApproveAllToggledautoApprovalSettings) と、パスが 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 は初回取得後に workspacePathsCacheisMultiRootScenarioCache にキャッシュする (getWorkspaceInfo:23)。task のライフサイクル中は workspace のルートディレクトリが基本的に変わらないため、キャッシュで重複 IPC 呼び出しを省く。

主要ファイル

  • ToolValidator:10 — クラス定義。コンストラクト時に ClineIgnoreController を受け取る。
  • assertRequiredParams:17 — パラメータ名を走査し、空値や純空白をエラーとして返す。
  • checkClineIgnorePath:32clineIgnoreController.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:163local && autoApproveLocal または external && autoApproveLocal && autoApproveExternal のときだけ許可。
  • ToolExecutor delegations:48 — ToolExecutor は自身を callback として bind し、autoApprover に転送する。
  • complete block approval:205 — complete block は shouldAutoApproveToolWithPath で auto-approval か manual ask("tool", ...) かを決める。
  • partial block approval:74 — ストリーミング partial block も同じ auto-approve チェックを走らせ、流しながらダイアログを出すか判断する。

データフロー

handler は block を受け取った後、典型的には Validator でパラメータを検証し、AutoApprove でどの UI パスを通るかを決める。以下は WriteToFileToolHandler が partial block 段階で実際に呼ぶ内容:

typescript
// 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 が返すタプルと組み合わせて最終判定を出す:

typescript
// 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

ここで autoApproveExternalautoApproveLocal も true のときだけチェックされる。つまり「外部自動承認」は「ローカルも自動承認」を暗黙に含む。これは Cline のデフォルトの安全姿勢であり、外部操作は常にローカルより一段厳しくなる。

境界と失敗

  • 空文字列も missing 扱い: assertRequiredParamsString(val).trim() === "" で判定し、空文字列も純空白も missing と見なす (empty check:21)。ただし content フィールドは空文字列を許す (空ファイルの新規作成) ため、handler は == null で独自に判定し、Validator は通さない (content null check:42)。
  • パスなしならデフォルトで承認しない: shouldAutoApproveToolWithPathundefined パスを受け取ると 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 などだけで、ASKATTEMPTNEW_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 へ。

公式資料: Cline 文档 · README