Skip to content

ToolValidator y autoApprove: las dos puertas previas a la ejecución ​

源码版本v4.0.10

Responsabilidades ​

Antes de que una herramienta se ejecute de verdad hay dos comprobaciones independientes: ToolValidator se ocupa de la validez de parámetros y rutas; AutoApprove decide si hay que mostrar un cuadro de aprobación. Ninguno ejecuta la herramienta: solo determinan «¿puede correr?» y «¿hay que preguntar al usuario?».

ToolValidator es una herramienta de utilidad muy fina, al servicio de los handlers. Ofrece dos métodos: assertRequiredParams comprueba si están los parámetros obligatorios, y checkClineIgnorePath comprueba si la ruta ha sido bloqueada por .clineignore (assertRequiredParams:17). No tiene efectos secundarios: solo devuelve ValidationResult para que el handler decida cómo tratarlo.

AutoApprove es un poco más complejo: es un campo privado de ToolExecutor, inyectado en TaskConfig vía asToolConfig (autoApprover init:126). El handler lo invoca indirectamente a través de config.callbacks.shouldAutoApproveTool o config.callbacks.shouldAutoApproveToolWithPath (callback wiring:181). Lee tres switches globales —yoloModeToggled, autoApproveAllToggled, autoApprovalSettings— más si la ruta está dentro del workspace, y devuelve uno de tres niveles: «auto-aprobado local», «auto-aprobado externo», «no aprobado».

Motivación de diseño ​

  • Validator y AutoApprove separados: la validación y la aprobación son dos cuestiones ortogonales —parámetros válidos pero el usuario no aprueba, o parámetros inválidos pero el usuario ya había aprobado, son ambos posibles—. Al separarlos en dos objetos, el handler puede no saltarse la aprobación en rutas erróneas y aún así bloquear parámetros inválidos tras la aprobación.
  • El Validator solo observa, no modifica: devuelve ValidationResult en lugar de lanzar, y el handler decide si pushea un resultado de error o entrega una ruta degradada (ValidationResult:4). Así distintos handlers pueden compartir un mismo formato de error.
  • Los tres switches de AutoApprove: yolo da luz verde a todo; autoApproveAll tiene un alcance algo menor; autoApprovalSettings es el más fino (distingue local/externo, comando seguro/comando total) (shouldAutoApproveTool:42). La prioridad es del más permisivo al más estricto.
  • La clasificación de la ruta decide el acceso externo: el resultado del auto-approve para herramientas de lectura/escritura es una tupla [local, external] (tuple result:96). local controla los archivos del workspace; external, los de fuera. Por defecto el acceso externo es más estricto, para evitar modificar archivos del sistema por error.
  • Workspace info cacheado a nivel de tarea: getWorkspaceInfo se obtiene la primera vez y se cachea en workspacePathsCache e isMultiRootScenarioCache (getWorkspaceInfo:23). Como las raíces del workspace no suelen cambiar durante el ciclo de vida de una tarea, el caché ahorra llamadas IPC repetidas.

Archivos clave ​

Flujo de datos ​

Tras recibir el block, el handler valida primero los parámetros con Validator y luego usa AutoApprove para decidir la ruta de UI. A continuación se ve la llamada real de WriteToFileToolHandler en la fase 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(() => {})
}

Dentro de shouldAutoApproveToolWithPath primero se miran los dos switches generales YOLO / autoApproveAll; si cualquiera está activo devuelve true directamente (yolo short circuit:126). Luego se calcula si la ruta está dentro del workspace y se combina con la tupla devuelta por shouldAutoApproveTool para obtener la decisión final:

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

Aquí autoApproveExternal solo se comprueba si autoApproveLocal también es true —lo que implica que «auto-aprobar externo» incluye «auto-aprobar local»—. Esa es la postura de seguridad por defecto de Cline: las operaciones externas siempre son un nivel más estrictas que las locales.

Límites y fallos ​

  • Las cadenas vacías también cuentan como ausentes: assertRequiredParams usa String(val).trim() === "", así que las cadenas vacías o solo espacios se consideran ausentes (empty check:21). Pero el campo content permite cadenas vacías (para crear archivos vacíos), por lo que el handler lo comprueba con == null por su cuenta y no pasa por Validator (content null check:42).
  • Sin ruta, por defecto no se aprueba: cuando shouldAutoApproveToolWithPath recibe una ruta undefined, directamente pone isLocalRead = false (no path default:153). Es el valor por defecto del lado seguro: si la herramienta no tiene ruta, se trata como operación externa.
  • El caché asume que el workspace no cambia: el comentario lo dice literalmente: «assumes that the task has a fixed set of workspace roots» (cache assumption:11). Si el usuario añade una raíz nueva a mitad de tarea, esa tarea seguirá viendo el caché viejo y solo la siguiente tarea lo reflejará.
  • YOLO no cubre todas las herramientas: el switch en modo YOLO solo lista lectura / escritura / bash / browser / web / MCP, no ASK, ATTEMPT, NEW_TASK (yolo switch list:44). Estas herramientas caen al return false por defecto y requieren aprobación normal.
  • La tupla de YOLO solo aplica a file/bash: la tupla [true, true] solo se entrega a read/write/bash/subagents (yolo tuple:55). browser/web/MCP reciben true como valor único, así que en YOLO no se distingue local / externo para estos.
  • Validator no lo usa el ToolExecutor legacy: el comentario de la clase lo dice literalmente: «The legacy ToolExecutor switch remains unchanged and does not depend on this» (legacy note:8). En la rama legacy la validación de parámetros está dispersa en cada case; Validator solo sirve a los handlers nuevos.

Resumen ​

Validator es la comprobación dura de parámetros / rutas: si falla, reporta error para que el modelo reintente. AutoApprove es el switch blando dirigido por preferencias del usuario: decide entre auto-approval o mostrar el cuadro. Juntos actúan de porteros antes de la ejecución de la herramienta; el handler los consulta antes de decidir el siguiente paso. Para ver cómo se registra el catálogo de handlers, consulta /tools/handlers-overview; para ver el ruteo en sí, consulta /tools/coordinator.

Véase la documentación oficial: Cline 文档 · README