Delegación y SubAgentes
Un SubAgent es una ejecución hija efímera generada por un agente padre que hereda la identidad del padre de forma predeterminada: el mismo alias de agente, la misma SecurityPolicy, la misma lista de permitidos de memoria, el mismo proveedor de modelo configurado, el mismo registro de herramientas. Auditable como hija mediante un intervalo de rastreo agent.<alias>.subagent.<run_id>.
Los SubAgents no son un concepto de configuración independiente. No existe un bloque [subagents.*] en el esquema. La identidad de cada SubAgent es la del bucle de agente del elemento padre que lo generó.
Cuándo usar spawn_subagent vs delegate
Hay dos herramientas cerca. No son intercambiables.
spawn_subagent: ejecuta el MISMO agente nuevamente bajo su propia identidad para una subtarea específica. El hijo ve el envoltorio completo de permisos del padre menos cualquier restricción adicional. Úselo cuando el padre quiere aislar una subtarea interna fuera de su historial de conversación principal sin cambiar de identidad.delegate: transfiere la solicitud a un agente DIFERENTE configurado (nombrado por alias). El agente objetivo se ejecuta bajo su propia identidad y proveedor de modelo, pero la delegación está sujeta a restricciones: el perfil de riesgo del llamador debe establecerdelegation_policy mode = "allow"(el valor predeterminado es"forbidden"), y el objetivo debe ser accesible como un par del mismo perfil o como una entrada explícita dedelegates. Las entradas explícitas eligenmode = "bounded"omode = "independent", lo que determina si el límite de herramientas del llamador sigue aplicándose. Úsalo cuando otro especialista configurado deba encargarse del trabajo. Consulta Delegation gating abajo.
Esta página documenta spawn_subagent de principio a fin. delegate se encuentra en crates/zeroclaw-runtime/src/tools/delegate.rs y es una superficie independiente.
Cómo se instancia un SubAgent
Dos sitios de generación convergen en SubAgentSpawn (crates/zeroclaw-runtime/src/subagent/mod.rs:97):
- Desde un bucle de agente: el modelo llama a la herramienta
spawn_subagentcon una cadenaprompt. La herramienta se registra como cualquier otra en el registro (crates/zeroclaw-runtime/src/tools/mod.rs,SpawnSubagentTool::new). - Desde cron: los trabajos
JobType::Agentse ejecutan a través derun_agent_job(crates/zeroclaw-runtime/src/cron/scheduler.rs), que construye el mismoSubAgentContextpero marca al hijo como una ejecución de nivel superior (no como un SubAgent) para que pueda generar por sí mismo un nivel de subagente.
Ambas rutas invocan:
#![allow(unused)]
fn main() {
SubAgentSpawn::for_agent(config, parent_alias)? // resolver identidad del padre
.build(SubAgentOverrides::default())? // validar cualquier estrechamiento
}
for_agent lee el risk_profile del padre y [agents.<alias>.workspace.read_memory_from] para construir la lista de permitidos heredada; el propio alias del padre siempre se agrega, de modo que un SubAgent siempre ve las filas de memoria propias de su padre. build aplica una restricción opcional (consulta Herencia de permisos más abajo) y devuelve un SubAgentContext validado.
Ciclo de vida
Síncrono, en proceso, un único runtime de tokio. Nada cruza el límite del proceso.
- El bucle de herramientas del padre despacha
spawn_subagent. La herramienta lee su argumentoprompty rechaza si está vacío. - La herramienta comprueba dos guardas en orden:
- Límite de profundidad 1. Si la ejecución que realiza la llamada era en sí misma un SubAgent (
AgentRunOverrides.is_subagent == true), rechaza con"spawn_subagent: a subagent may not spawn its own subagents (depth-1 cap)". Los SubAgents no pueden hacer recursión. - Control de herramientas por perfil de riesgo. Si
[risk_profiles.<alias>].deny_all_toolsdel padre estrue, o siallowed_toolsno está vacío y no incluyespawn_subagent, o siexcluded_toolslo incluye, rechaza la operación con un mensaje que nombre el alias del padre. Unallowed_toolsomitido o vacío deja la herramienta disponible (el estado heredado sin restricciones).
- Límite de profundidad 1. Si la ejecución que realiza la llamada era en sí misma un SubAgent (
- La herramienta llama a
SubAgentSpawn::for_agent+build. Los fallos (alias de padre desconocido, anulación de escalado) se manifiestan comoToolResult { success: false, error: "subagent spawn failed: ..." }. - La herramienta construye
AgentRunOverrides { security, memory: None, is_subagent: true, suppress_memory_inject: true }(el origenSubTurndel hijo ya omite la inyección de memoria del motor; la bandera hace explícita la exclusión) y espera acrate::agent::run(crates/zeroclaw-runtime/src/agent/loop_.rs,pub async fn run) dentro de un ámbito de trazado con clavesubagent-<uuid>. La ejecucióntooldel padre se bloquea hasta que el hijo devuelve. - El bucle del agente hijo se ejecuta hasta completarse. Su registro de herramientas se construye desde cero, con
is_subagent_caller: truefluyendo hacia su propioSpawnSubagentTool, de modo que cualquier intento de recursión se rechaza en la misma barrera de profundidad 1. - El hijo devuelve
Result<String>. La herramientaspawn_subagentdel padre lo envuelve:- Éxito:
ToolResult { success: true, output: <child's final response>, error: None }. La salida vacía se reemplaza con el literal"subagent completed without output". - Fallo:
ToolResult { success: false, error: Some("subagent run failed: ...") }.
- Éxito:
- El bucle de herramientas del padre continúa con ese
ToolResulten su contexto de conversación. Los turnos intermedios y las llamadas a herramientas del hijo NO se reproducen en el historial del padre; solo aparece la respuesta final.
Lo que se entrega de vuelta upstream
Una cosa: el mensaje final del asistente del proceso hijo, como cadena, envuelto en ToolResult.output.
- Las llamadas a herramientas del hijo, los turnos de razonamiento intermedios y cualquier escritura en memoria que el hijo haya realizado son observables en los registros estructurados bajo el span de rastreo del hijo, pero no entran en el historial de conversación del padre.
- La sesión del proceso hijo reside bajo la ruta
subagent-<uuid>(ocron-<uuid>para ejecuciones iniciadas por cron). Esta es la clave del historial de conversación, no una ubicación del sistema de archivos; aísla el historial del proceso hijo del historial del proceso padre. - Las escrituras de memoria realizadas por el hijo se escriben en la identidad del padre (el mismo UUID de agente en los backends SQL/Postgres; el mismo directorio del workspace para Markdown). Las ejecuciones generadas por cron deshabilitan
memory.auto_save, por lo que las escrituras opcionales siguen funcionando, pero la recuperación rutinaria no se acumula.
Una delegación síncrona no tiene un canal de streaming ni de progreso parcial hacia el padre: un SubAgent de larga duración detiene la ejecución de herramientas del padre durante toda su duración, y no existe un control de tiempo de espera por llamada. Una delegación en segundo plano informa progreso en vivo limitado mediante check_result y await_sessions (ver el campo progress en la forma del resultado en segundo plano más abajo).
Presupuesto de iteraciones del árbol de ejecución
Establezca max_execution_tree_iterations en la entrada [runtime_profiles.<alias>] del destino para limitar el número total de iteraciones del bucle de herramientas en un único árbol de ejecución en primer plano:
[runtime_profiles.default]
max_execution_tree_iterations = 12
El campo es opcional. Omitirlo desactiva el presupuesto agregado, mientras que un 0 explícito no es válido. Este presupuesto es independiente de max_tool_iterations, que sigue siendo el límite local por bucle para cada turno del agente.
Un turno raíz lógico posee un único manejador de presupuesto atómico. La ejecución en primer plano de spawn_subagent, los destinos delegate síncronos o paralelos acotados, y el trabajo hijo SOP en vivo explícito consumen de ese manejador; un hijo solo puede reservar mientras quede más de una ranura. Cuando la raíz solo tiene su última ranura disponible, consume esa ranura para la ruta de finalización existente sin herramientas. Un hijo que agota su vista devuelve un ToolResult fallido, lo que permite que el turno raíz padre continúe, y las reservas concurrentes no pueden hacer que el contador dé la vuelta.
Los destinos delegate independientes en primer plano usan el presupuesto de su propio perfil de ejecución, no el saldo restante del llamador. Si se omite el límite del destino, ese turno independiente queda sin tope de presupuesto del árbol de ejecución, incluso cuando el llamador sí tiene uno. Las tareas delegate en segundo plano separadas y los turnos entre pares también crean una nueva raíz a partir del perfil de ejecución del destinatario, en lugar de compartir el presupuesto del turno que las lanza.
Múltiples llamadas en un solo turno
El bucle del agente aplica una protección contra llamadas duplicadas por turno: una herramienta llamada dos veces con argumentos idénticos en el mismo turno normalmente tiene la segunda llamada omitida. spawn_subagent y delegate están exentos de esa protección. Lanzar varios con el mismo prompt (redundancia, muestreo, distribución en paralelo) es un patrón intencional, no una repetición accidental, por lo que cada llamada idéntica se ejecuta y cada resultado se devuelve. Sin la exención, solo se ejecutaría la primera llamada idéntica y solo su salida llegaría al modelo.
Cuando la ejecución paralela de herramientas está habilitada (parallel_tools = true en el perfil de runtime), múltiples llamadas a spawn_subagent en un mismo turno se ejecutan de forma concurrente y la respuesta final de cada hijo se devuelve al padre, asociada a su propia llamada de herramienta. delegate tiene su propio fan-out explícito mediante el argumento parallel: [...] (consulte la sección de cadenas de salida); esa ruta genera cada destino en su propia tarea y agrega todos los resultados.
Inspección de transcripciones de ZeroCode
Las llamadas en primer plano a spawn_subagent y delegate tienen tarjetas de transcripción expandibles que muestran la tarea, los nombres de los destinos de delegación cuando se proporcionan, y el texto del resultado conservado. La delegación en paralelo sigue siendo una única invocación con los bloques de resultado existentes por destino. Los argumentos adicionales, como context, siguen visibles en el campo de entrada. Los argumentos malformados o demasiado grandes recurren a la tarjeta de herramienta genérica. La expansión conserva los límites de visualización de herramientas de 8 KiB o 100 líneas por campo; Copy usa la entrada original conservada, cuyo resultado puede haberse limitado ya a 16 KiB.
Los nombres de los destinos delegados aparecen en el encabezado de la tarjeta, seguidos de la tarea y de una sección Response. Para un sobre de delegación síncrono que coincida, la respuesta devuelta aparece primero y los metadatos de proveedor/modelo permanecen en Details expandido. Los demás formatos de resultado se mantienen intactos. Si falta una respuesta, se muestra Result not recorded. La tarjeta no afirma que un hijo se esté ejecutando actualmente ni que haya tenido éxito, incluso en el historial restaurado o en el texto de diagnóstico devuelto. Los lanzamientos en segundo plano y las acciones de gestión como check_result y await_sessions mantienen tarjetas genéricas, por lo que un acuse de recibo de lanzamiento no se muestra como finalización del hijo.
La inspección de subagentes en vivo aún necesita metadatos de ejecución correlacionados con el ID de la llamada de herramienta padre: identidad de la tarea o sesión hija, ciclo de vida y resultado autoritativos, marcas de tiempo, y eventos de herramientas hijas o un enlace autorizado a la transcripción. Las notificaciones actuales de herramientas padre no incluyen estos campos. ZeroCode no los infiere a partir del texto de los resultados, de los registros de trazas, de la coincidencia de prompts ni de los alias de destino; el plano de control del runtime sigue siendo el responsable del ciclo de vida. Esta es una implementación parcial de #8763, no una supervisión de subagentes en vivo.
Herencia de permisos
Un SubAgent hereda los permisos del padre de forma literal a menos que el sitio de generación proporcione un SubAgentOverrides restrictivo. Actualmente, ambos sitios de generación in-tree pasan SubAgentOverrides::default() (heredarlo todo). La superficie de override se entrega y valida; una futura ruta de restricción proporcionada por el llamador se integra sin cambios en tiempo de ejecución.
Herencia eje por eje:
SecurityPolicy: se hereda mediante la clonación deArc<SecurityPolicy>. La ruta de sobrescritura (SubAgentOverrides::policy = Some(policy)) ejecutaSecurityPolicy::ensure_no_escalation_beyond(crates/zeroclaw-config/src/policy.rs) y rechaza cualquier campo que añada privilegios que el padre no tenga. Los ejes validados incluyen el nivel de autonomía, allowed_roots (rw + ro + solo escritura), allowed_commands, workspace_only, forbidden_paths en la dirección padre ⊆ hijo, shell_env_passthrough,max_actions_per_hour,max_cost_per_day_cents,shell_timeout_secs,block_high_risk_commandsyrequire_approval_for_medium_risk. Los rechazos encadenan unaEscalationViolationprecisa para que los diagnósticos identifiquen el campo infractor.- Presupuestos de acciones / costos:
PerSenderTrackerse comparte entre padre e hijo mediante clonación deArc. Ruta de herencia textual: el hijo mantiene el mismoArc<SecurityPolicy>, por lo que las escrituras enrecord_action()/record_cost()afectan al mismo bucket. Ruta de anulación:SubAgentSpawn::buildcopia explícitamente el campotrackerdel padre en la política restringida del hijo. Un SubAgent no puede eludirmax_actions_per_hournimax_cost_per_day_centsmediante la generación de procesos, el límite es compartido. - Registro de herramientas: el registro del proceso hijo se construye desde cero mediante
tools::all_tools_with_runtimebajo la política heredada. A continuación, el registro pasa porapply_policy_tool_filter(crates/zeroclaw-runtime/src/agent/loop_.rs), que descarta cualquier herramienta cuyo nombre no supere alguna de las dos verificaciones:- Las
allowed_tools/excluded_toolsde la política (obtenidas delrisk_profiledel elemento padre). - El argumento
allowed_toolsproporcionado por el llamador aagent::run.spawn_subagentestá en el registro, pero su flagis_subagent_callerestá establecido entruepara el hijo, por lo que el rechazo de profundidad 1 se activa antes de cualquier trabajo de generación. El mismo flagis_subagent_callerelimina por completomodel_switchdel registro del hijo: un SubAgent hereda el modelo del padre tal cual (ver eje 5) y no debe poder cambiar el modelo activo a espaldas del padre, por lo que la herramienta simplemente no se le ofrece.
- Las
- Lista de permitidos de memoria: las concesiones de hermanos del padre a partir de
workspace.read_memory_frommás el alias propio del padre. Las restricciones de categoría siguen siendo exactas y nunca las amplía un SubAgent; el alias propio del padre siempre se vuelve a añadir para que un SubAgent siempre vea las filas del padre. La anulación actualSubAgentOverrides::allowed_agent_aliasesse valida como subconjunto de nombres, por lo que solo puede restringir qué hermanos concedidos son visibles. - Proveedor de modelo: heredado de la resolución de
[agents.<alias>] model_providerdel padre. La temperatura proviene de la entrada del proveedor del padre (config.model_provider_for_agent(parent_alias).and_then(|e| e.temperature)). Esta herencia se aplica de forma obligatoria, no es simplemente un valor predeterminado:model_switchestá excluido del registro de herramientas del SubAgent (véase el eje 3), por lo que un SubAgent no puede cambiar su propio modelo. Para ejecutar una subtarea en un modelo diferente, usedelegatehacia un agente hermano cuyomodel_providernombre ese modelo. - Identidad en la capa de datos: el mismo UUID en la tabla
agents(backends SQL), el mismo directorio de workspace para Markdown, el mismo almacén de secretos. La distinción entre padre e hijo es puramente de observabilidad: un span de trazado separado y una clave de sesión de historial de conversación separada.
Cómo un usuario activa uno
Tú no llamas estas herramientas directamente; lo hace el bot, desde dentro de su turno. Como usuario, influyes en la elección del bot según cómo formules la solicitud. No hay ningún comando especial, ni sintaxis de barra, ni JSON que el usuario escriba. Que el modelo elija spawn_subagent o delegate depende de su prompt del sistema, del texto de description de la herramienta (visible para el modelo) y de la redacción del usuario. La formulación influye; no obliga.
Lo que sí puede hacerse determinista es la disponibilidad: las herramientas que no están en el registro del agente principal no pueden ser seleccionadas. La restricción por perfil de riesgo se define en [risk_profiles.<alias>].allowed_tools, [risk_profiles.<alias>].deny_all_tools y [risk_profiles.<alias>].excluded_tools. Una lista allowed_tools no vacía debe incluir spawn_subagent o delegate para que el modelo vea esa herramienta. Si allowed_tools se omite o está vacía, la disponibilidad de herramientas no está restringida, a menos que excluded_tools nombre la herramienta. deny_all_tools = true es la restricción explícita de denegación total y oculta ambas herramientas (una lista allowed_tools vacía no equivale a denegación total). Reinicia el demonio después de editar la configuración.
Lo que es verificable de extremo a extremo:
- Las cadenas de salida y rechazo controladas por el protocolo son contratos literales de Rust. La entrega de errores de autocompletado del terminal visibles para el usuario es un contrato del catálogo de Fluent: la fuente en inglés se indica a continuación, y un catálogo en un idioma distinto del inglés o una sustitución en disco que defina la misma clave puede mostrarla de forma diferente.
- Los parámetros de configuración literales que cambian el comportamiento (
allowed_tools,max_delegation_depth, etc.). - La forma estructurada del span de trazado que delimita todo lo emitido durante la ejecución secundaria.
Lo que NO es verificable a partir de esta documentación:
- Si tu bot específico, con tu modelo específico, con tu prompt de sistema específico, elegirá la herramienta cuando se le pida “Genera un subagente para …”. La redacción marca la diferencia; los resultados varían. Si el bot no elige la herramienta, la palanca más fiable es ampliar el prompt de sistema del bot con instrucciones explícitas (“Cuando se solicite una subtarea concreta, usa la herramienta
spawn_subagent”). - El texto exacto que el bot te escribe en su respuesta final. El bot lee la salida de la herramienta y genera su propia respuesta a partir de ella. El texto de salida de la herramienta puede citarse, parafrasearse o resumirse.
spawn_subagent: cadenas de rechazo que el modelo ve
Estos son exactos, obtenidos de crates/zeroclaw-runtime/src/tools/spawn_subagent.rs. El modelo los recibe como la cadena de error de la herramienta y reacciona en consecuencia. La respuesta del bot visible para el usuario es lo que el modelo escriba a continuación; suele hacer referencia al rechazo o reproducirlo.
- Argumento
promptvacío o ausente:Missing or empty 'prompt' parameter - Caller is itself a SubAgent (depth-1 cap):
spawn_subagent: a subagent may not spawn its own subagents (depth-1 cap) - El control de herramientas por perfil de riesgo del agente padre excluye
spawn_subagent:spawn_subagent: refused: agent '<parent_alias>' risk_profile does not list spawn_subagent in allowed_tools - Error de alias padre desconocido / spawn build:
subagent spawn failed: <wrapped error> - La ejecución secundaria devolvió un error:
subagent run failed: <wrapped error>
Si la operación tiene éxito, la salida de la herramienta ES el texto de respuesta final del proceso hijo. Si el proceso hijo devolvió una cadena vacía, la salida es el marcador de posición literal: subagent completed without output. No hay un prefijo fijo que buscar con grep en el caso de éxito.
spawn_subagent: cómo verificar que se ejecutó realmente
Inspecciona el final de tu log. El proceso hijo generado por la herramienta se ejecuta dentro de un scope! que emite un tracing span llamado zeroclaw_scope (con target zeroclaw_log_internal_scope) que transporta agent_alias=<parent> y session_key=<uuid>. Cada línea de log emitida durante la ejecución del hijo transporta esos campos. El propio turno del padre tiene su propio session_key; un NUEVO valor de session_key que aparece a mitad de turno para el mismo agent_alias es la señal de que se ejecutó un SubAgent. La ruta de sesión del historial de conversación del hijo es subagent-<uuid> (un identificador tipo sistema de archivos, distinto del campo de tracing).
Los trabajos de agentes lanzados por cron usan un nombre de span diferente y más explícito: subagent (literal) con los campos category="cron", agent_alias=<owning agent>, cron_job_id=<id>, run_id=<uuid>, spawn_site="cron". Las rutas de cron se pueden filtrar fácilmente con grep: grep 'spawn_site="cron"' zeroclaw.log. Ten en cuenta que las ejecuciones lanzadas por cron son de nivel superior (is_subagent=false); ellas mismas pueden llamar a spawn_subagent una vez.
Esta es una señal débil para la ruta de generación de subagentes del bucle del agente. Un registro dedicado de “subagente iniciado / completado” enrutado a través de attribution_span!(tool) está registrado como un seguimiento pendiente en el código; una vez que el bucle del agente envuelva la ejecución de herramientas en un span de atribución, cada record! dentro de la herramienta llevará tool=spawn_subagent automáticamente y la pregunta se convertirá en un grep trivial.
Control de delegación
delegate aplica dos controles en crates/zeroclaw-runtime/src/tools/delegate.rs antes de que se ejecute un agente de destino, en este orden:
-
delegation_policy.mode: el perfil de riesgo del invocador debe permitir la delegación.[risk_profiles.<alias>].delegation_policyes{ mode = "forbidden" }por defecto; establezcamode = "allow"para permitir la delegación en absoluto. Cuando está prohibida, el rechazo es:la delegación está prohibida para el llamador "<caller>" por la política delegation_policy del perfil de riesgo "<caller_profile>"; establezca [risk_profiles.<caller_profile>].delegation_policy mode = "allow"Esto se puede editar en el panel del gateway y en zerocode en Config → Risk profiles →
<profile>→delegation_policy.mode(un selector forbidden/allow). -
Reachability: el agente objetivo debe estar en el conjunto alcanzable del llamador, resuelto por
Config::reachable_delegate_target_configs. El conjunto alcanzable es la unión de dos fuentes por agente en[agents.<caller>], menos el propio llamador:-
pares del mismo perfil: cualquier otro agente que comparta el perfil de riesgo del llamador, incluido cuando
delegate_same_risk_profile = true(el valor predeterminado). Establécelo enfalsepara excluir al llamador de la autorización automática de pares. -
lista explícita de delegación:
delegates, una lista posiblemente vacía de destinos a los que el llamador puede delegar incluso a través de perfiles de riesgo. Las entradas de cadena son convenientes para la edición manual y representan destinos acotados. Las entradas de objeto hacen explícito el modo:delegates = [ "reviewer", { agent = "sysadmin", mode = "independent" }, ]Cuando se guarda la configuración, cada entrada se escribe en forma de objeto con
mode = "bounded"omode = "independent". No despliegue este formato de configuración antes de que los binarios del daemon y de la UI se hayan actualizado a una versión que admita modos delegados. Los binarios anteriores esperan quedelegatescontenga solo cadenas; una entrada de objeto hace que la secciónagentssea inválida para ese binario y el cargador resiliente descarta la sección para que las superficies de reparación puedan seguir iniciando. Cuando el objetivo está fuera de ese conjunto, la negativa nombra la causa. Por ejemplo:
delegate target “
<target>” no es accesible desde “<caller>”: perfil de riesgo diferente (el llamador usa “<caller_profile>”, el objetivo usa “<target_profile>”). delegate_same_risk_profile solo alcanza agentes con el mismo perfil de riesgo; añade una entrada explícita [agents.<caller>].delegates con el modo previsto, o cambia el risk_profile de uno de los agentes.delegate target “<target>” no es alcanzable desde “<caller>”: delegate_same_risk_profile está deshabilitado y el objetivo no aparece en [agents.<caller>].delegatesdelegate target "<target>" no es accesible desde "<caller>": el agente de destino está deshabilitadoUn objetivo acotado hereda el rastreador de acciones y costes del llamador, con los límites efectivos ajustados a los valores del llamador en cada salto, de modo que el presupuesto de acciones del llamador limita toda su subárbol, independientemente de lo que permita el perfil propio del objetivo. Los workers en segundo plano y paralelos generados restauran el bucket de límite de tasa del remitente originario antes de su primera admisión, de modo que el presupuesto se mantiene por remitente a través de los límites de generación, en lugar de recurrir al bucket sin ámbito. El límite de coste se combina del mismo modo en cada salto y se aplica dentro del sub-bucle delegado frente al gasto diario del propio agente objetivo (véase el párrafo sobre seguimiento de costes más abajo). Cuando el objetivo acotado comparte el perfil de riesgo del llamador, también hereda el límite del workspace de sesión del llamador. Un objetivo acotado de perfil cruzado está permitido cuando es accesible a través de la lista de delegados del llamador y de
delegation_policy; se ejecuta bajo la política resuelta del objetivo, mientras que la disponibilidad de herramientas agentivas queda limitada por el registro de herramientas del llamador.Un objetivo independiente solo está disponible cuando se enumera explícitamente con
mode = "independent". Sigue requiriendodelegation_policy.mode = "allow"y alcanzabilidad mediantedelegates, pero una vez seleccionado resuelve la propia política del agente objetivo sin el techo de no escalación, la invalidación del espacio de trabajo de la sesión ni el rastreador de acciones/costes del llamante.La aplicación de los límites de costo se realiza dentro de cada sub-bucle delegado, tanto los acotados como los independientes: el sub-bucle delegado se ejecuta bajo un ámbito de seguimiento de costos atribuido al alias del agente destino, de modo que su uso de tokens se registra en el libro mayor compartido de costos diarios bajo ese alias. El límite por salto efectivo del destino, definido en
[runtime_profiles.<target>].max_cost_per_day_cents(un valor de0hereda el límite global), se verifica frente al gasto diario del propio agente destino en ese libro mayor, mientras que[cost] daily_limit_usdy los límites mensuales, que son globales del proceso, siguen aplicándose al total compartido; cuando el límite compartido es el menor de los dos, prevalece. El tipo de ámbito (por agente o compartido con límite, según[cost] track_per_agent), si una ejecución delegada tiene ámbito o no ([cost] enabled) y la atribución por agente de sus filas del libro mayor se deciden una sola vez en la delegación raíz de un árbol y son heredados por cada salto anidado: una delegación anidada deriva su rastreador del padre que delega, de modo que el modo con el que empezó el árbol rige todo el árbol, y una recarga en caliente de estos indicadores no puede dejar sin seguimiento una ejecución con ámbito ni empezar a reatribuir a mitad de ejecución una que no estaba atribuida. Los límites y el mapa de precios siguen activos en cada salto: el rastreador derivado comparte el manejador de configuración de la base y el mapa de precios se reconstruye a partir de la configuración resuelta, de modo que si un operador recarga los límites globales de[cost], estos se aplican a los delegados en ejecución en su siguiente comprobación de presupuesto. Cuando el gasto propio del destino ya supera su límite, la delegación falla antes de que se llame al proveedor del destino, y la denegación llega al llamador como error de la herramienta delegate que nombra al agente. El límite de un ancestro también cubre todo lo que delegó: cada ámbito de presupuesto de un salto acotado incluye la cadena de ancestros, de modo que el gasto de un descendiente acotado cuenta contra el límite por salto de cada ancestro, aunque las filas del libro mayor del descendiente sigan atribuidas al alias propio de este. Un destino independiente se ejecuta bajo su propio límite por salto y los límites globales compartidos: hereda el modo de aplicación del árbol, pero no el límite ni la cadena del llamador, de modo que ni el límite agotado del llamador ni su acumulador de descendientes pueden bloquear el salto independiente. La contabilidad de descendientes es válida por vida del proceso y por día UTC: se reinicia junto con los totales diarios del libro mayor a medianoche UTC, y un reinicio del demonio olvida el gasto de descendientes mientras que los totales propios del libro mayor de cada agente persisten. Con[cost] track_per_agent = falseno existen totales diarios por agente, por lo que el límite por salto se degrada a una comprobación frente al total diario compartido. Los destinos de delegación no agentivos (una única llamada al modelo, sin bucle de herramientas) se ejecutan bajo el mismo ámbito, pero su ruta de despacho no informa del uso de tokens, por lo que actualmente esas llamadas únicas no se tarifican ni se comprueban contra el presupuesto. -
La lista anunciada se incluye en la descripción del parámetro agent en el esquema de la herramienta. Enumera exactamente este conjunto alcanzable, y solo cuando delegation_policy.mode = "allow". Los agentes deshabilitados (enabled = false) nunca son alcanzables, ya sea como pares del mismo perfil o como entradas explícitas de delegates.
En la delegación agéntica acotada, las herramientas del subagente se obtienen del registro del llamador ya filtrado por política, intersecado con los allowed_tools propios del destino. Un allowed_tools omitido o vacío en el destino significa «heredar»: el subagente se ejecuta con el registro delegable completo del llamador en lugar de ser rechazado (una lista vacía es el estado heredado sin restricciones, no denegar todo). deny_all_tools = true es la puerta explícita de denegación total y produce un registro hijo vacío. Una lista no vacía se interseca con ese registro. En cualquier caso, el registro del llamador es el techo: un destino acotado entre perfiles cuyo perfil de riesgo nombra una herramienta que el llamador nunca recibió no la obtiene. Por tanto, la delegación acotada está limitada por herramientas, no es una comprobación completa de SecurityPolicy::ensure_no_escalation_beyond. Si esa intersección está vacía, el destino sigue recibiendo un turno normal de modelo agéntico sin herramientas.
La propia herramienta delegate sigue la misma forma acotada: se elimina del registro del hijo a menos que el perfil de riesgo del destino establezca delegation_policy mode = "allow" (el valor por defecto "forbidden" mantiene el comportamiento actual de no subdelegar), y las comprobaciones del lado del llamador siguen aplicándose: allowed_tools/excluded_tools del llamador deben admitir delegate, y la política de herramientas del destino no debe excluirla. Cuando se concede, la instancia de delegate del hijo queda vinculada a la identidad del destino: sus llamadas resuelven delegation_policy, la accesibilidad y la lista de delegados anunciada a partir de la configuración de alias y delegates propia del destino, nunca de la del padre que delega, y cada salto concedido incrementa el contador de profundidad (véase más abajo).
Se aplican dos reglas de aprobación y alcance a la instancia concedida. Primero, es solo delegación: las acciones de gestión de tareas (check_result, list_results, cancel_task, await_sessions) se rechazan, porque la identidad de la instancia hija es transitoria. La fila de la tarea registra la cadena de delegación de alias de quien llama, por lo que la verificación de propietario admite a todos los ancestros del hijo; solo un ancestro cuya propia herramienta de delegación incluya gestión de tareas (normalmente el agente raíz que delega, ya que los hijos intermedios acotados también son solo de delegación) puede actuar sobre esa admisión, de modo que un hijo pasa el id de la tarea hacia arriba hasta llegar a uno. Conviene precisar el límite: esto solo limita la superficie de la API de delegación. El artefacto subyacente delegate_results/*.json sigue siendo un archivo ordinario del espacio de trabajo que contiene únicamente el id de la tarea y la salida, y todo lo que controla la verificación de propietario reside en la fila de la tarea. Segundo, los bucles de subagentes no tienen una vía de aprobación del operador. Si el perfil de riesgo del destino pediría confirmación para delegate (el valor supervisado por defecto, o la herramienta listada en always_ask), la puerta de aprobación del hijo rechaza la llamada antes de que se ejecute la implementación de delegate, informando de que no había ninguna decisión del operador disponible. La implementación de delegate conserva su propia verificación de política del destino para las llamadas que llegan hasta ella. Por tanto, la delegación anidada requiere que el perfil del destino apruebe explícitamente delegate automáticamente o que funcione con autonomía total; un perfil de quien llama que pediría confirmación seguirá pidiéndola en su propio bucle, sin cambios.
El mismo límite sin operador se aplica a las llamadas a herramientas en un hijo agéntico acotado. El bucle del hijo crea un gestor de aprobaciones nuevo y no interactivo a partir del perfil de riesgo del destino, en el punto de despacho. Una llamada que requiere confirmación se deniega antes de que se ejecute la implementación de la herramienta, mientras que una llamada incluida explícitamente en auto_approve (o permitida por la autonomía total) puede proceder. En el caso de las herramientas de comandos heredadas y propiedad del llamador (shell, schedule, cron_add, cron_update y cron_run), esa decisión basada en el perfil del destino aprueba únicamente la invocación de la herramienta: no proporciona ni anula la aprobación específica del comando que consume la política de comandos del llamador. La política de comandos del llamador sigue siendo autoritativa, de modo que estas herramientas continúan aplicando sus requisitos de aprobación. El registro heredado y limitado por el llamador no cambia: la aprobación evalúa las llamadas que superaron el filtrado de capacidades; no concede herramientas adicionales ni reenvía la sesión de aprobación del llamador.
En la delegación agéntica independiente, las herramientas del subagente se construyen a partir de la política configurada y del registro de ejecución del propio agente de destino, como si se abriera un chat nuevo con ese destino. El registro del agente padre no se usa como límite máximo. La herramienta delegate se elimina de ese registro hijo: la delegación independiente no se repite de forma recursiva mediante otra llamada a delegate.
La profundidad se limita según runtime_profile.max_delegation_depth del agente padre que delega, y cada salto concedido lleva ese límite reducido (min) con el límite de perfil propio del siguiente destino, de modo que el límite de un padre se aplica a todo su subárbol y una cadena solo puede restringirse. Establézcalo en 1 para permitir al agente superior un único salto de delegación sin subdelegación posterior.
Política de herramienta objetivo agéntica
Si [runtime_profiles.<target>].agentic = true del agente de destino, delegate construye el registro de herramientas del subbucle de destino a partir de las herramientas disponibles del padre (mode = "bounded") o del propio registro en tiempo de ejecución del destino (mode = "independent"). El perfil de riesgo del destino luego filtra ese registro:
- Un campo
[risk_profiles.<target_profile>].allowed_toolsomitido o vacío deja el registro seleccionado sin restricciones.deny_all_tools = truees la puerta explícita de denegación total y produce un registro hijo vacío. - Una lista
allowed_toolsno vacía conserva solo los nombres de herramientas que coincidan exactamente. [risk_profiles.<target_profile>].excluded_toolssiempre se resta del resultado.delegatese elimina del registro hijo a menos que se conceda delegación acotada: un destino acotado cuyo perfil de riesgo establecedelegation_policy mode = "allow"(y cuyas puertas de control del llamador admiten la herramienta) recibe una instancia de solo delegación vinculada al destino, que además rechaza la delegación cuando el perfil del destino pediría confirmación para ella; los registros hijos independientes siempre descartandelegate.
Esta directiva reside en el objetivo, no en el llamador. Los pares del mismo perfil usan el perfil de riesgo compartido. Los delegados explícitos entre perfiles usan el perfil de riesgo del objetivo tras los controles de accesibilidad y de política de delegación. Los delegados agentivos acotados reciben solo el registro de herramientas limitado por el llamador e intersectado con la política de herramientas del objetivo; los delegados agentivos independientes reciben el registro de herramientas propiedad del objetivo. Un perfil de riesgo del objetivo ausente rechaza antes de que comience el subbucle. Un perfil configurado que deja cero herramientas hijas ejecutables sigue permitiendo una pasada normal del modelo sin herramientas.
Cuando la cadena de proveedores Reliable configurada para el destino mezcla candidatos compatibles con herramientas nativas y candidatos que solo admiten texto, strict_tool_parsing = false usa un único protocolo de herramientas de texto/XML para todo el turno agéntico, de modo que todas las alternativas de reserva accesibles puedan ejecutar herramientas. Si siguen existiendo herramientas efectivas y strict_tool_parsing = true, ZeroClaw rechaza la cadena mixta antes de realizar una solicitud al proveedor, porque el análisis estricto prohíbe ese protocolo de reserva de texto/XML. Las cadenas uniformes no cambian: las cadenas compuestas exclusivamente por proveedores nativos usan transporte de herramientas nativas, mientras que las cadenas deliberadamente compuestas solo por texto siguen la política de herramientas de texto configurada.
Aprobaciones independientes de delegados
Un destino agéntico independiente puede enviar solicitudes de aprobación de herramientas al operador seleccionado por el approval_route de su propio perfil de riesgo. Establezca tanto approver_channel como un approver_recipient no vacío, y mantenga ese canal habilitado y registrado en el daemon en ejecución. El llamador debe haber autorizado ya ese destino nombrado en modo independiente; una ruta de aprobación no concede alcance de delegación ni herramientas adicionales.
Por ejemplo, un destino que usa el perfil de riesgo worker puede enviar sus aprobaciones a un canal de operaciones configurado:
[risk_profiles.worker.approval_route]
approver_channel = "telegram.ops"
approver_recipient = "operations-room"
on_no_approver = "deny"
timeout_secs = 120
Los delegados en primer plano, en segundo plano y paralelos independientes usan la misma puerta de aprobación de la ruta de destino. El mensaje identifica el destino y el llamador, conserva el nombre real de la herramienta y el resumen de los argumentos, y usa el destinatario configurado. Una aprobación real permite la llamada propiedad del destino, sujeta a su política existente de herramientas y comandos. Una denegación, un canal ausente, una superficie de aprobación no compatible, una desconexión o un tiempo de espera agotado deja la herramienta sin ejecutar. El tiempo de espera de ejecución del hijo también limita la espera de aprobación y puede agotarse antes que el tiempo de espera de la ruta.
Una ruta de destino completa también se aplica cuando el perfil ya estaba configurado para los turnos de canal propios del destino. Para un hijo independiente en modo Supervisado, las llamadas a shell ahora requieren una decisión del operador, salvo que el destino apruebe automáticamente shell de forma explícita. Esto incluye los comandos de bajo riesgo que antes se enviaban directamente a la política de comandos de la herramienta shell. En un proceso sin registro de canales, como una ejecución única de la CLI, esas llamadas que requieren confirmación se deniegan. La autonomía total y las entradas explícitas de auto_approve mantienen su comportamiento existente, mientras que always_ask sigue requiriendo una decisión. Elimina la ruta para restaurar la ruta de shell no interactiva anterior solo si eso coincide con la política prevista para el destino.
Cada ejecución hija tiene un gestor de aprobaciones nuevo. Una respuesta “Always” se aplica únicamente dentro de esa ejecución hija; no puede aprobar las llamadas del llamador, ejecuciones delegadas posteriores ni hermanos paralelos. Las herramientas en always_ask siguen requiriendo una decisión para cada llamada, incluso con Full autonomy. Las herramientas aprobadas explícitamente de forma automática conservan, por lo demás, el comportamiento de su perfil de destino.
Los delegados no heredan la sesión de aprobación de la CLI, de ACP, de RPC ni del panel del llamador. Incluso on_no_approver = "inherit-originator" deniega la solicitud cuando el aprobador configurado no puede responder, porque un hijo independiente no tiene un canal de aprobación de origen al que recurrir. La aprobación pendiente es local al proceso y se abandona al cancelar el hijo o al agotarse su tiempo de espera; reiniciar el daemon no reanuda esa solicitud ni restaura las concesiones de aprobación del hijo.
Sin una ruta de destino completa, los destinos independientes con always_ask no vacío se rechazan antes de la ejecución, incluida la admisión en segundo plano y en paralelo. Las demás llamadas que requieren confirmación siguen fallando de forma segura (fail closed) en el despacho de herramientas. Los delegados agénticos acotados mantienen su comportamiento existente sin operador y la política de comandos controlada por el llamador.
delegate: cadenas de salida que el modelo ve
Las cadenas de error visibles para el usuario son mensajes de Fluent localizados. Su fuente de verdad en inglés es crates/zeroclaw-runtime/locales/en/cli.ftl; los ejemplos siguientes muestran los valores actuales del catálogo en inglés, no un contrato de cadenas a nivel de protocolo. Las cadenas restantes enumeradas son salidas de protocolo/herramientas, a menos que esta sección las etiquete como claves de Fluent.
-
Éxito síncrono: la salida comienza con
[Agent '<target>' (<provider_type>/<model>)]\n, seguida de una respuesta no vacía del agente de destino. Cuando el destino se recupera mediante una alternativa de proveedor configurada, su encabezado identifica en su lugar el proveedor/modelo solicitado y el proveedor/modelo servido, por ejemplo[Agent 'reviewer' (requested: anthropic.primary/claude; served: openai.terra/gpt-5.6-terra, agentic)]. En el caso de un destino agéntico, esta atribución describe la solicitud del modelo que produjo la respuesta final, no una solicitud anterior que solo produjo una llamada a una herramienta. El resultado también termina con eldelegate-provider-fallback-warninglocalizado. En inglés:Warning: The delegated agent recovered through a provider fallback. Provider failure details were logged and omitted from this result.Esta atribución y esta advertencia pertenecen al resultado delegado; no deben presentarse como una alternativa del agente que realiza la llamada. Omiten intencionadamente los detalles del error del proveedor rechazado, los endpoints y las credenciales. Reintentar el mismo candidato configurado no produce esta advertencia; llegar a un candidato configurado posterior sí la produce, incluso cuando sus etiquetas de proveedor y modelo coinciden con las del primer candidato. -
Una respuesta vacía de terminal es un fallo síncrono: su campo de error usa
cli-delegate-error-invalid-semantic-completion, conagent_nameestablecido en el destino. En inglés:Agent '<target>' failed: model provider returned an invalid semantic completion. -
Otros errores síncronos: el campo de error comienza con
Agent '<target>' failed: <wrapped error>. Si todos los candidatos de proveedor configurados fallan,<wrapped error>es el resumen seguro y ordenado de Reliable de los eventos de fallo, el recuento de reintentos, la clase de fallo, la fase y la indicación fija de remediación. Los cuerpos de respuesta del proveedor, los puntos de conexión, los alias, los modelos y las credenciales no se devuelven al agente solicitante; cuando se necesiten más detalles, investigue los registros de intentos del proveedor conforme a la política habitual de registro de operadores de la instalación. El resultado sigue siendo un error, no una advertencia de recuperación. -
Tiempo de espera síncrono (cuando el perfil de tiempo de ejecución del destino establece
delegation_timeout_secs): el campo de error esAgent '<target>' timed out after <N>s. -
Generación en segundo plano exitosa: la salida es el literal de tres líneas
Background task started for agent '<target>'. task_id: <uuid> Use action='check_result' with task_id='<uuid>' to retrieve the result.El estado del ciclo de vida se almacena en
data/control_plane.db. El artefacto de workspace en<workspace>/delegate_results/<uuid>.jsoncontiene solo la identidad y la salida de la tarea; no es un segundo registro de estado. La vistacheck_result(y cada entrada de un resultado deawait_sessions) incluye un objetoprogressuna vez que la tarea ha comenzado, onullpara las filas que nunca registraron uno. Sus campos son:last_activity_at,iterations(solicitudes al modelo emitidas),tools_completed,last_tool(name,started_at,finished_at,success: la llamada más reciente que aún se está ejecutando, confinished_aten null; si no, la última llamada que terminó),timeout_budget_secs(el tiempo de espera agéntico, o el tiempo de espera de una sola llamada para un destino sin bucle de herramientas),recent_tools(las últimas 5 llamadas terminadas),receipt_tail(los últimos 5 comprobantes recopilados hasta el momento, como<tool>: <token>; vacío cuando los comprobantes de herramientas están desactivados; los comprobantes se recopilan cuando un lote de herramientas devuelve sus resultados, por lo que una llamada puede contarse antes de que aparezca su comprobante), yelapsed_secs, calculado en el momento de la lectura. Un destino de llamada única cuenta su única solicitud eniterations. Los nombres de herramientas se truncan a 64 caracteres en todos los lugares donde aparecen, incluidoreceipt_tail; los tokens firmados se conservan completos. Los argumentos de herramientas, los resultados de herramientas, el texto de los errores y el contenido de los prompts nunca se registran. El progreso se almacena en la fila de la tarea, por lo que es visible exactamente para los llamadores que pueden leer la salida de la tarea. Mientras la tarea se ejecuta, el latido de su fila se actualiza al menos una vez por minuto. -
action="check_result"con un id de tarea desconocido: el error esNo result found for task_id '<uuid>'. -
action="await_sessions"contask_ids: [<uuid>, ...]espera varios archivos de resultados en segundo plano a la vez. La salida es un objeto JSON constatus(completeotimeout),completed,pending,missing,failedyresults.timeout_mstiene un valor predeterminado de 30000 y un máximo de 120000; en caso de timeout, la herramienta devuelve resultados parciales y un error que indica que una o más tareas siguen pendientes o faltan. Los IDs de tarea duplicados se rechazan. -
Salida de fan-out paralelo: comienza con
[Parallel delegation: <N> agents]\n\n, seguida de bloques por agente separados por\n\n, cada uno de los cuales comienza con--- <target> (success=<bool>) ---\n. Un destino recuperado conserva la atribución de solicitado frente a servido y la advertencia de respaldo genérico dentro de su propio bloque. En caso de error por agente, el bloque interno es--- <target> (success=false) ---\nError: <wrapped error>. -
Agente de destino desconocido: el error es
Unknown agent '<target>'. Available agents: <comma-separated list>. -
Profundidad excedida (controlada por el
runtime_profile.max_delegation_depthdel padre, predeterminado 3): el error esDelegation depth limit reached (<depth>/<max>). -
Acción desconocida: error es
Acción desconocida '<value>'. Use delegate/check_result/list_results/cancel_task/await_sessions. -
Destino cuyo perfil de riesgo tiene entradas always_ask: un destino independiente sin una ruta de aprobación explícita completa devuelve
delegate target "<target>" cannot run in independent mode from "<caller>": risk profile "<profile>" has always_ask entries (<list>). See ZeroClaw docs, "Delegation & SubAgents" > "What's not supported".Un destino agéntico acotado devuelve el mismo error concannot run in bounded agentic mode. -
Destino agéntico con un perfil de riesgo de destino faltante: el error es
Agent '<target>' is agentic but risk_profile '<target_profile>' is not configured. -
Objetivo agentivo sin herramientas secundarias ejecutables: no se emite ningún error para el conjunto de herramientas vacío en sí; el objetivo recibe una interacción normal del modelo sin herramientas.
delegate: cómo verificar que realmente se ejecutó
delegate no emite actualmente un span de rastreo específico. La señal es que aparezca en el registro el bucle del agente destino, que hereda el ámbito en el que estaba el despacho de la llamada a herramienta del padre. En el modo en segundo plano, usa check_result o await_sessions para leer juntos el estado del ciclo de vida y la salida autoritativos. Esas acciones, el listado y la cancelación solo se aplican a las filas de delegación cuya cadena registrada contiene el alias del llamador. El artefacto del espacio de trabajo puede confirmar que la salida se persistió, pero no incluye el estado. Cuando los recibos de herramientas están habilitados, las delegaciones en segundo plano se ejecutan con un ámbito de recibos que comparte el generador del padre y usa su propio recolector, por lo que sus resultados de herramientas están firmados y son verificables, pero no se añaden al bloque de recibos del turno que las lanzó.
(Los trabajos de agentes lanzados por cron son un sitio de generación independiente y usan el span subagent explícito descrito anteriormente; delegate y cron no son la misma ruta.)
delegate: qué cancela la detención del turno
Detener el turno que llamó a delegate (por ejemplo POST /api/sessions/{id}/abort, o una interrupción de canal) alcanza el trabajo que ese turno inició, aunque los hijos se ejecutan en sus propias tareas y sobreviven al futuro de la herramienta padre que fue descartado:
- Los hijos agénticos (acotados o independientes) están vinculados a la cancelación del turno que los lanzó y se detienen con él.
- Los hijos paralelos (
parallel: [...]) que ejecutan su propio bucle de herramientas están sujetos a los mismos límites en todos los niveles de profundidad. Un hijo paralelo que realiza una llamada simple al modelo sin bucle de herramientas no se interrumpe a mitad de la llamada: termina con el tiempo de espera propio de esa llamada. - Las tareas en segundo plano (
background: true) no están vinculadas al turno en ningún nivel de profundidad, incluidas las lanzadas por un proceso hijo que a su vez se inició en primer plano. Sobreviven al turno por diseño y solo se detienen mediantecancel_task.
spawn_subagent no tiene nada de esto: no tiene modo en segundo plano ni modo paralelo, y su proceso hijo se cancela junto con el future padre.
Lo que no está en esta página (intencionalmente)
- Transcripciones de conversaciones de ejemplo. Cualquier cosa que escribiera aquí describiendo “lo que dirá el bot” dependería del modelo. La respuesta del bot es consecuencia de la salida de la herramienta, el modelo, el prompt del sistema y el estado actual de la conversación, ninguno de los cuales está controlado por esta página. La capa verificable es lo que devuelve la herramienta (arriba) y lo que captura el registro.
- Un marcador de registro dedicado de “subagent fired” / “delegate fired”. Registrado como seguimiento del lado del código. Hoy, los operadores verifican mediante la forma del scope descrita arriba (que es la señal estructural existente) y mediante el archivo de resultados del modo en segundo plano.
Elección entre spawn_subagent y delegate
spawn_subagent | delegate | |
|---|---|---|
| Identidad | Igual que el elemento padre (mismo UUID, mismo perfil de riesgo) | Identidad del agente de destino (alias diferente; par del mismo perfil o delegado explícito de perfil cruzado) |
| Modelo de permisos | La política del padre textualmente (o un subconjunto restringido) | Los destinos acotados se ejecutan bajo la política de destino con el registro de herramientas agénticas del llamador como límite superior; los destinos independientes se ejecutan bajo la política de destino y el registro propiedad del destino |
| Proveedor de modelos | Del padre | Proveedor configurado del agente de destino |
| Profundidad de generación | Límite máximo en 1 | Hasta runtime_profile.max_delegation_depth (predeterminado 3) |
| Modo en segundo plano | No compatible | background: true devuelve un task_id |
| Distribución en paralelo | Sin argumento integrado; las llamadas múltiples en un mismo turno se ejecutan de forma concurrente cuando parallel_tools = true | parallel: [...] ejecuta múltiples objetivos de forma concurrente |
| Restricción de acceso | Si risk_profile.allowed_tools se omite o está vacío, spawn_subagent permanece disponible; deny_all_tools = true lo deniega; una lista no vacía debe incluir spawn_subagent; excluded_tools no debe enumerarlo | Si allowed_tools del llamador se omite o está vacío, delegate sigue disponible; deny_all_tools = true lo deniega; una lista no vacía debe incluir delegate; excluded_tools no debe listarlo; delegation_policy mode = "allow" debe estar en el llamador; y el destino debe estar en el conjunto alcanzable del llamador (un par del mismo perfil o una entrada explícita en delegates). Un destino acotado solo puede recibir la herramienta delegate si su propio perfil de riesgo establece delegation_policy mode = "allow"; la instancia concedida es de solo delegación y rechaza delegar si el perfil del destino solicitaría confirmación para ello. |
| Usar cuando | Subtarea interna que debe permanecer dentro de la misma identidad | ¿Quieres que otro especialista configurado diferente (otro modelo, otro alias) asuma la tarea bajo delegación limitada o independiente? |
Qué no es compatible
- Recursión más allá del nivel 1. Un SubAgent no puede generar su propio SubAgent. El límite es un rechazo absoluto en la herramienta, no un presupuesto. Las ejecuciones lanzadas por cron comienzan en el nivel 0 y pueden generar un nivel; los SubAgent lanzados por el bucle del agente están en el nivel 1 y rechazan generar más.
- Una identidad independiente para el hijo. Los SubAgents comparten el UUID del agente padre. Para ejecutarse con una identidad diferente, usa
delegatepara delegar en un agente hermano configurado. - Presupuesto de tiempo por lanzamiento. No existe un argumento
timeout_secs. El padre se bloquea durante toda la duración de la ejecución del hijo; la cancelación debe propagarse a través del ámbito de interrupción más amplio (paradelegate, véase qué cancela detener el turno). - Transmisión del progreso de vuelta al elemento principal. El elemento principal ve la respuesta final del elemento secundario como una sola cadena tras la finalización.
- Un bloque de configuración
[agents.<alias>].subagent_*. El validador y el tipo de override se incluyen hoy; la superficie de configuración orientada al operador que conecta el narrowing definido por el llamador no está en esta versión. Ambos sitios de spawn pasanSubAgentOverrides::default()hasta que esa superficie esté disponible. - Reenvío de aprobaciones a la sesión de origen y aprobaciones acotadas. Los delegados independientes pueden usar una ruta de aprobación explícita propiedad del destino, pero no pueden reenviar la aprobación a la CLI, ACP, RPC o al panel de control de la sesión de origen del llamador. Los destinos agénticos acotados con
always_askno vacío siguen bloqueados antes de la ejecución, incluida la delegación en segundo plano y en paralelo. La delegación acotada no agéntica sigue disponible porque no puede ejecutar herramientas hijas.