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