# Routing subagent approval to the root session Los subagentes no podían pedir aprobación: al delegar, el harness fija la política de aprobación de toda subsesión a `never` (`captureDelegatedPolicyOverrides` / `appendDelegatedPolicyOverrides` en `@deepseek-ai/dsh-subagent`), de modo que un `{ kind: "ask" }` devuelto por `tools/pre-execute` para una herramienta vigilada se resuelve contra la subsesión y se rechaza antes de llegar a ningún prompt. Decidimos **encaminar la pregunta a la sesión raíz desde el propio plugin** en vez de confiar en la resolución `ask` del harness. ## Considered Options - **Cambiar la política de la subsesión (`approval/policy` → `ask`).** Rechazada. El plugin no tiene un hook en el instante de delegación, y aun con política `ask` el scope de la subsesión no es descendiente del scope de la raíz (los scopes de agente no se encadenan entre sí, solo comparten el preset), por lo que el answerer de la UI, registrado en la sesión raíz, no recibiría el evento. - **Encaminar la aprobación a la sesión raíz desde el plugin.** Elegida. El gate detecta el subagente por `delegationDepth` (ausente en una sesión top-level y en un fork de primer nivel, que sí puede llevar `parentSession` como linaje de semilla) y camina la cadena `parentSession` hasta la raíz viva con `ctx.get("agents")`; después llama `approval.request({ agent: raíz, ... })` y traduce el resultado a `allow`/`deny`. ## Consecuencias - Un solo prompt para top-level y subagentes: la ejecución top-level (o sin agente) conserva el `{ kind: "ask" }` que resuelve el harness; solo el subagente pasa por el ruteo propio. - El prompt del subagente declara que es un subagente y explica qué quiere ejecutar y por qué (`Subagent approval: run "bash" · Why: … · Command: …`), porque la tarjeta de la llamada del subagente no forma parte de la conversación raíz. Los campos van separados por `·` en una sola línea porque el headline del panel de aprobación colapsa los espacios en blanco (un `\n` se aplana a un espacio). Para herramientas sin `description`/`command` se cae a un resumen JSON truncado de los argumentos. - Los seams `approval` y `agents` se leen de forma perezosa en cada ejecución (no se capturan en `apply`), de modo que un servicio montado después del plugin sigue siendo visible. - La detección se apoya en `delegationDepth`, no en `origin` ni en la mera presencia de `parentSession`, para no confundir un fork top-level con un subagente. - Fail closed en todos los ramales: sin canal de aprobación, `rejected`, `cancelled`, `unavailable`, error del request, o linaje roto → denegar. La cadena rota se detiene en el ancestro vivo más profundo alcanzable.