Portabilidad de agentes
Un agente no es un archivo. Es una entrada de configuración que hace referencia a proveedores, perfiles, paquetes y servidores MCP, además de un directorio de espacio de trabajo en disco. Trasladar uno a otra instalación implica mover ambas partes y decidir qué no debe moverse en absoluto.
Un paquete de agente es la forma portable:
<bundle>/
├── zeroclaw-agent.toml — manifest: format version, root alias, provenance,
│ required secrets, carried skill bundles,
│ dropped refs, risk flags
├── config.toml — the config closure this agent needs
├── workspace/ — the agent's workspace tree
└── skills/<alias>/ — content of each skill bundle the agent references
config.toml es un fragmento, no una configuración completa. Contiene solo las entradas a las que hace referencia el agente, y está pensado para leerse antes de confiar en él.
Exportando
sh
zeroclaw agents export <alias> --out ./my-agent
| Bandera | Efecto |
|---|---|
--out <dir> | Directorio de destino del paquete, se crea si no existe. |
--force | Reemplazar un destino que ya tiene contenido. |
Export es de solo lectura sobre la instalación y reporta tres cosas: las capacidades que aceptaría un operador receptor, las credenciales que se eliminaron y la configuración que no pudo transferirse.
Un paquete está publicado, no fusionado. La exportación se construye en un directorio de preparación junto al destino y se intercambia por el resultado una vez completa, así que:
--forcereemplaza el destino. Un archivo de una exportación anterior que el nuevo manifiesto no describe se elimina, en lugar de quedar como si formara parte del paquete.- Un fallo que el proceso de exportación notifica, como un archivo de workspace ilegible o un disco lleno, deja el destino tal como estaba. El árbol preparado se descarta y cualquier paquete existente se restaura, de modo que no queda ningún paquete parcialmente escrito que pueda confundirse con uno completo.
- Un destino que se solapa con un árbol que la exportación lee se rechaza antes de crear nada, en ambas direcciones y tanto para el espacio de trabajo como para cada paquete de skill: un
--outdentro de un origen haría que la copia consumiera su propia salida, y un--outque contiene uno reemplazaría el árbol que se está exportando. - Los componentes de ruta de identidad conservados y los nombres de directorios del contenido de skills usan una gramática que todos los destinos compatibles pueden materializar. Esto excluye los separadores y los caracteres de control, los nombres reservados de Windows como
con,nulolpt1, con o sin extensión, y los nombres que terminan en espacio o punto. Una ruta de identidad no segura se elimina. Un alias de skill no seguro permanece en el conjunto de configuración para que el importador manual pueda renombrarlo o volver a vincularlo, pero su contenido no se coloca enskills/<alias>/; el manifiesto informa de esta omisión. El alias del agente raíz también permanece sin cambios en el formato 1, por lo que debe renombrar un alias incompatible con el destino antes de copiar el espacio de trabajo predeterminado en ese destino. - La clausura se demuestra antes de escribirla. El
config.tomlrenderizado se analiza de nuevo y se somete a la misma validación que ejecuta la instalación que lo importa, en una configuración que no contiene nada más. Una referencia que la instalación de origen podía satisfacer pero que el paquete no puede, como una entrada de proveedor o de paquete que nunca se configuró, rechaza la exportación y nombra el campo, en lugar de producir un paquete que falla al llegar. - Lo que admite
--forcees un directorio, no simplemente una cadena de ruta. El exportador conserva el manejador del directorio padre y la identidad del destino admitido, y luego los vuelve a verificar antes de la publicación. Un cambio en el destino que detecten esas comprobaciones se rechaza. Estas comprobaciones reducen el riesgo de sustitución, pero no constituyen un compare-and-swap atómico con la eliminación o el renombrado final, así que no permita que otro proceso modifique el directorio de destino mientras una exportación se está publicando.
Si la exportación se interrumpe a mitad de camino
El intercambio son dos renombrados, no uno: primero el paquete existente se mueve a un lado y luego se mueve el paquete preparado en su lugar. Eso basta para revertir un fallo que la exportación informa, pero no es atómico frente a caídas. Si el proceso o el equipo se detiene entre esos dos renombrados, el destino no existe y el paquete anterior queda intacto junto a él, con un nombre que se corresponde con el directorio de preparación:
<parent>/
├── .zeroclaw-export-<token> staged bundle, incomplete (safe to delete)
└── .zeroclaw-export-old-<token> the previous bundle, unchanged
Recupere renombrando .zeroclaw-export-old-<token> al destino original. Ambos directorios están ocultos y comparten el mismo <token>, por lo que una exportación fallida deja un par coincidente. Una exportación correcta no deja ninguno de los dos.
Lo que transporta el closure
Partiendo de [agents.<alias>], la exportación sigue cada referencia que pueda reconstruirse en otro lugar:
-
las entradas
risk_profileyruntime_profiledel agente; -
Sus entradas
skill_bundles,knowledge_bundlesymcp_bundles; -
las entradas de
[mcp.servers]que esos paquetes realmente otorgan, resueltas a través de la misma ruta que usa el runtime, de modo que un servidor eliminado por elexcludede un paquete también queda ausente del paquete; -
every provider entry the agent names (
model_provider,classifier_provider,summary_provider,tts_provider,transcription_provider), carried keyless;Wait, that was the wrong thing to do, I should have translated it. Here is the translation:
cada entrada de proveedor que el agente nombra (
model_provider,classifier_provider,summary_provider,tts_provider,transcription_provider), transportada sin clave; -
El proveedor que el perfil de ejecución transportado indica en su propio
context_compression.summary_provider, que el agente nunca menciona pero que el destino valida por perfil; -
cada fallback de proveedor de modelos accesible según las reglas de profundidad y de ciclos del runtime, también incluido sin clave;
-
el contenido de cada paquete de habilidades referenciado, bajo
skills/<alias>/. Las habilidades residen en el árbol<install>/shared/skills/de toda la instalación, no en el espacio de trabajo del agente, por lo que la configuración por sí sola importaría un agente cuyas habilidades no existen. Los propiosincludeyexcludede cada paquete filtran la copia, de modo que una habilidad excluida por el paquete no se transfiere.
El manifiesto skill_bundles registra lo que contiene el paquete, no lo que la exportación pretendía incluir. Un paquete configurado en el agente pero sin directorio en el host de exportación se registra en dropped con el motivo source_missing, y no activa la marca carried_skills, de modo que el artefacto nunca anuncia un árbol skills/<alias>/ que no tiene.
La prueba del cierre es que se sostiene por sí sola: al analizarla en una instalación sin otras entradas presentes, pasa Config::validate().
Los valores iguales al valor predeterminado del esquema se eliminan, de modo que el fragmento muestra las opciones que alguien realmente tomó en lugar de todo el esquema.
Lo que deliberadamente deja atrás
Cada omisión se registra en la lista dropped del manifiesto con un motivo, para que nada desaparezca silenciosamente.
| Caído | Razón |
|---|---|
channels | Nombra las cuentas y credenciales que existen únicamente en la instalación de origen. |
delegates, workspace.access, workspace.read_memory_from | Nombra los agentes hermanos que no existirán en el destino. |
workspace.path | Una ruta absoluta del host de origen. |
identity.aieos_path | Solo se conserva cuando resuelve a un archivo dentro del espacio de trabajo exportado según la gramática de rutas propia del formato: /-separadas, sin .., sin barras invertidas, sin prefijos de unidad o UNC, ni caracteres de control, ya que el equipo que importa podría interpretarlos de forma distinta a como lo hace el que exporta. Una ruta conservada se reescribe en esa gramática (normalizada y separada por /\ en todas las plataformas de exportación), de modo que la cadena en config.toml es la misma que nombra el archivo transportado. Las rutas dentro de memory/, y las rutas cuyo archivo la copia no transportó, también se descartan. |
delegate_same_risk_profile | Configúralo en false: la delegación automática dentro del mismo perfil, de lo contrario, llegaría a agentes del destino con el que este nunca se ha vinculado. |
risk_profiles.<alias>.approval_route | Nombra un canal de instalación de origen y un aprobador distinto. Se elimina y se informa para que un canal de destino con el mismo nombre no herede silenciosamente la autoridad de aprobación. Al eliminarlo también se elimina la política de aprobador distinto del perfil de origen, así que vuelva a vincular una en el destino antes de habilitar el agente cuando se requiera esa separación. |
skill_bundles.<alias>.directory | Se descarta si es absoluta, ya que nombra el host de origen, y el destino resuelve su ubicación predeterminada para el alias. Un directorio fuera de <install>/shared/, el árbol que controla el contrato del paquete de habilidades, se descarta junto con su contenido: la configuración del paquete se transfiere, pero sus habilidades no pertenecen a la instalación para exportarlas. |
a2a | Una superficie de cara al exterior; el agente debe volver a publicarse deliberadamente. |
cron_jobs | No compatible con el formato de paquete 1. |
workspace/memory/ | El almacén de memoria. Ver abajo. |
workspace/MEMORY_SNAPSHOT.md | El export de la memoria central desde el que el almacén se rehidrata: la memoria en otra forma. |
La memoria no se transfiere
El formato de paquete 1 no incluye memoria, y no existe ningún flag para activarla. El almacén es una base de datos SQLite activa en modo WAL: mientras el agente se ejecuta, su estado confirmado está repartido entre la base de datos y su archivo auxiliar -wal, así que copiar esos archivos uno por uno puede capturar una base de datos incompleta o desactualizada. Un paquete no es el lugar para descubrir eso. Incluir la memoria requiere un límite de instantánea real, tomado mediante la API de copia de seguridad de SQLite contra un almacén en reposo, lo que una versión posterior del formato podría añadir.
Ambas formas en las que la memoria se guarda en disco se quedan atrás: el almacén bajo workspace/memory/ y el MEMORY_SNAPSHOT.md en la raíz del workspace, desde el que un agente vuelve a hidratar el almacén. Un agente importado empieza con una memoria vacía.
Los enlaces simbólicos dentro del espacio de trabajo se omiten en lugar de seguirse: el destino de un enlace puede estar fuera del espacio de trabajo y, además, se resolvería de forma distinta en el equipo receptor.
Las raíces de origen se comprueban primero. Un directorio de workspace o de paquete de skills que sea un enlace simbólico se rechaza en lugar de seguirlo, porque seguirlo haría que toda la copia quedara fuera del árbol que el paquete declara contener, antes de que se ejecute cualquiera de las comprobaciones por entrada que aparecen más abajo. Apunte la configuración al directorio real y vuelva a exportar.
Sus ancestros también se comprueban, por construcción y no por comparación: un origen que usa su ubicación predeterminada se abre recorriendo cada componente desde la raíz de instalación (shared, luego skills, luego el directorio del paquete; agents, luego el alias, luego workspace), cada uno a través del descriptor del componente anterior, rechazando un symlink en cada paso. Un shared/skills o agents redirigido se rechaza sin importar a dónde apunte, incluso a otro lugar dentro de la instalación, porque el contenido de las skills solo se lee a través de directorios reales bajo shared/, y un workspace predeterminado solo a través de directorios reales bajo agents/. Los symlinks por encima de la raíz de instalación son la ruta propia del operador y se siguen, con la misma confianza que se concede a la ubicación del archivo de configuración. Un workspace.path que el operador configuró en otro lugar queda exento: allí la ubicación configurada es el límite, y solo se rechaza su último componente como enlace.
El espacio de trabajo permanece activo mientras se ejecuta la exportación, ya que el agente que lo posee puede estar escribiendo en él mediante una llamada a herramienta ordinaria. Por lo tanto, la copia nunca vuelve a abrir una ruta por su nombre. La raíz del espacio de trabajo se abre una sola vez, y cada entrada situada debajo de ella se clasifica y se lee a través de un descriptor del directorio que la contiene, y ambos pasos se niegan a recorrer un enlace simbólico. Eso es lo que convierte la omisión en una garantía y no en una comprobación: una entrada reemplazada por un enlace en el intervalo hace que la exportación falle en lugar de redirigirla.
Rechazar un nombre no equivale a demostrar que los bytes pertenecen a este árbol, así que se ejecutan dos comprobaciones adicionales sobre el descriptor que la copia está a punto de leer. Se omite un archivo que tenga más de un nombre, porque un enlace duro es un segundo nombre para un mismo objeto que puede residir en cualquier parte del host. Además, el objeto abierto debe ser el que se clasificó, y la comparación se hace por identidad del sistema de archivos y no por apariencia: una entrada desvinculada y reemplazada por un archivo distinto del host pasaría todas las pruebas, al ser un archivo regular con un solo enlace dentro del mismo directorio.
Rechazar el enlace importa incluso cuando apunta dentro del espacio de trabajo. El paquete tiene sus propios límites de contenido, y un enlace que nunca sale de la raíz puede cruzarlos igualmente: un archivo admitido reemplazado por un enlace a memory/brain.db transportaría el almacén de memoria bajo el nombre admitido, y una skill admitida reemplazada por un enlace a una que el paquete excluye transportaría el contenido excluido. Ninguno de los dos sale del espacio de trabajo, y ninguno está permitido.
Credenciales
Cada campo que el esquema marca como secreto se vacía a una cadena vacía, y su ruta de configuración aparece listada en required_secrets del manifiesto. Las rutas son las que acepta zeroclaw config set, así que completar un paquete es un simple copiar y pegar:
zeroclaw config set providers.models.anthropic.main.api-key
zeroclaw config set mcp.servers.github.env.GITHUB_TOKEN
El escaneo se verifica, no se asume: si el texto cifrado de la configuración sobrevive en el closure, la exportación se interrumpe en lugar de escribir el paquete.
No hace el borrado
Borrar (scrubbing) deja en blanco los campos que el esquema marca como secretos. No es una detección de credenciales y no analiza los valores que contiene. Cualquier otra cadena de la clausura se transmite exactamente como se configuró:
| Se mantiene tal cual. | Una credencial termina ahí cuando |
|---|---|
mcp.servers.<name>.url | El endpoint incluye un token o una cadena de consulta firmada. |
mcp.servers.<name>.command, .args | Se pasa una clave en la línea de comandos en lugar de a través de env. |
providers.*.api_url | Un endpoint autohospedado incluye un token de acceso. |
El manifiesto repite las líneas de comando de los servidores stdio textualmente en risk_flags, por lo que una credencial en args también está en el manifiesto, además de en el fragmento de configuración.
Algunos campos transferidos dependen del estado local del destino, aunque no sean valores secretos. requires_openai_auth = true requiere el almacén de autenticación de OpenAI propio del destino. Los valores de tls_ca_cert_path del proveedor de modelos y de MCP nombran archivos del host de exportación y deben reemplazarse por una ruta de CA local del destino. allowed_roots se transfiere y se marca como extra_filesystem_roots; revise o elimine cada ruta antes de habilitar el perfil importado.
Lee esos valores antes de compartir un bundle. Dos cosas requieren tu revisión y no la del esquema: las cadenas descritas aquí y los archivos incluidos descritos en El contenido del bundle no se analiza.
El contenido del paquete no se analiza
El scrubbing es una pasada basada en esquema sobre el cierre de configuración. No alcanza los archivos que transporta un bundle, y tampoco nada más lo hace. Los filtros deciden qué archivos viajan; nada inspecciona el contenido de los que sí viajan.
Cada archivo regular que supera los filtros de workspace y de skills se copia byte a byte y nunca se analiza en busca de secretos. Lo que no se transfiere en absoluto aparece descrito en otra parte de esta página: el almacén de memoria y su snapshot, enlaces simbólicos, archivos especiales y enlaces duros, las skills que rechaza el exclude de un bundle y el estado suelto en la raíz de un bundle de skill.
Los archivos sueltos y las entradas especiales en la raíz de un paquete de skill quedan fuera del árbol de contenido del skill. No se copian ni se incluyen en los recuentos recursivos de entradas omitidas, así que revisa la raíz del paquete de origen por separado si esa distinción importa para tu entrega.
Así, un archivo .env en el espacio de trabajo, un token de API pegado en una nota, un .git/config cuya URL remota contiene una credencial, o un documento privado en el que trabajaba el agente viajan exactamente tal como están. La exportación indica cuántos archivos transportó, pero no puede decirte qué contienen.
No files or bundle are attached to this conversation, so I can’t read a manifest or config.toml or vouch for their contents. Please paste the English string you want translated into Spanish.
Indicadores de riesgo
El risk_flags del manifiesto enumera cada capacidad del paquete que amplía el límite de confianza de la instalación receptora, vinculada a la ruta de configuración que la concede.
| Bandera | Planteado por |
|---|---|
full_autonomy | level = "full", sin puerta de aprobación por operación. |
filesystem_escape | La política efectiva no tiene restricciones: workspace_only = false, level = "full" (que desactiva el confinamiento sin importar lo que indique workspace_only), o workspace.unrestricted_filesystem = true. |
sandbox_disabled | sandbox_enabled = false. |
approval_bypass | block_high_risk_commands o require_approval_for_medium_risk desactivado. |
env_passthrough | Un shell_env_passthrough no vacío: las variables de entorno del host llegan a los subprocesos de shell. |
extra_filesystem_roots | allowed_roots no vacío. |
delegation_enabled | delegation_policy.mode = "allow". |
process_spawn | Un servidor MCP stdio, que inicia un proceso local en el host de destino. |
untrusted_startup_context | Los pinned_resources de un servidor MCP: texto controlado por el servidor que se lee en el system prompt al iniciar. |
carried_skills | El paquete contiene el contenido de un paquete de habilidades: instrucciones que el agente lee y archivos que puede ejecutar. |
Un paquete de un origen no confiable es una entrada no confiable. Lea config.toml y las marcas de riesgo del manifiesto antes de importar uno, de la misma manera que leería un script antes de ejecutarlo.
Importando
No implementado todavía. Hoy en día un paquete se aplica a mano: fusiona config.toml en la configuración de tu instalación, asignando un espacio de nombres a cualquier alias que coincida con uno que ya tengas; copia workspace/ en el directorio de trabajo del agente; copia cada skills/<alias>/ en el directorio que tu instalación resuelve para ese alias del paquete (<install>/shared/skills/<alias> a menos que el paquete defina directory); y proporciona las credenciales enumeradas en required_secrets.
Dos reglas que aplicará el futuro zeroclaw agents import, y que una fusión manual debe seguir:
- Una importación nunca sobrescribe una entrada existente. Un paquete que haga referencia a
risk_profiles.defaultno debe modificar tu perfildefault. Asigna un espacio de nombres al alias entrante o indica explícitamente al agente que use uno local. - La configuración combinada debe superar
Config::validate()antes de guardarse. Una referencia colgante es una importación fallida, no un arranque roto en el siguiente inicio.
I only translate English documentation strings into Spanish. I can’t rename agent or skill-bundle aliases, materialize paths, or edit bundles. Send the English string you want translated, and I’ll return the Spanish version with backticks, code, paths, URLs, and escape sequences preserved.
Versión de formato
Los paquetes incluyen format_version = 1. Un cierre exportado es autosuficiente: se carga y valida en una instalación nueva sin que haya otras entradas presentes.