ToolValidator y autoApprove: las dos puertas previas a la ejecución
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
ValidationResulten 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:
yoloda luz verde a todo;autoApproveAlltiene un alcance algo menor;autoApprovalSettingses 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).localcontrola 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:
getWorkspaceInfose obtiene la primera vez y se cachea enworkspacePathsCacheeisMultiRootScenarioCache(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
ToolValidator:10— definición de la clase; el constructor recibeClineIgnoreController.assertRequiredParams:17— recorre los nombres de parámetros; valores vacíos o solo espacios devuelven error.checkClineIgnorePath:32— llama aclineIgnoreController.validateAccess; si está bloqueado devuelve error.AutoApprove:8— definición de la clase; mantienestateManagery dos campos de caché.getWorkspaceInfo:23— rutas del workspace cacheadas y la decisión de multi-root.shouldAutoApproveTool:42— tres switches: YOLO, autoApproveAll, autoApprovalSettings fino.yolo branch:43— en modo YOLO, solo lectura / operaciones de archivo devuelven[true, true]; browser/web/MCP devuelventrue.autoApproveAll branch:66— mismo alcance que YOLO, como switch secundario.autoApprovalSettings:88— rama fina, subdivide por actions.readFiles / editFiles / executeSafeCommands, etc.shouldAutoApproveToolWithPath:122— versión con ruta; decide local / externo y lo combina con la tupla de auto-approve.multi-root workspace check:138— escenario multi-root, comprueba conisLocatedInWorkspacecontra cualquier raíz.final decision:163— solo aprueba silocal && autoApproveLocaloexternal && autoApproveLocal && autoApproveExternal.ToolExecutor delegations:48— ToolExecutor se ata a sí mismo como callback y reenvía aautoApprover.complete block approval:205— el bloque completo pasa porshouldAutoApproveToolWithPathpara decidir auto-approval oask("tool", ...)manual.partial block approval:74— el bloque partial en streaming también pasa por la misma comprobación de auto-approve y decide si mostrar el cuadro mientras se recibe el stream.
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:
// 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:
// 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 falseAquí 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:
assertRequiredParamsusaString(val).trim() === "", así que las cadenas vacías o solo espacios se consideran ausentes (empty check:21). Pero el campocontentpermite cadenas vacías (para crear archivos vacíos), por lo que el handler lo comprueba con== nullpor su cuenta y no pasa por Validator (content null check:42). - Sin ruta, por defecto no se aprueba: cuando
shouldAutoApproveToolWithPathrecibe una rutaundefined, directamente poneisLocalRead = 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 alreturn falsepor 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 recibentruecomo 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.