Ir al contenido

Arquitectura de almacenamiento de blobs y artefactos

Este documento describe cómo coding-agent almacena cargas útiles grandes/binarias fuera del JSONL de sesión, cómo se persisten las salidas truncadas de herramientas y cómo las URLs internas (artifact://, agent://) se resuelven de vuelta a los datos almacenados.

Por qué existen dos sistemas de almacenamiento

Sección titulada «Por qué existen dos sistemas de almacenamiento»

El runtime utiliza dos mecanismos de persistencia diferentes para diferentes formas de datos:

  • Blobs con direccionamiento por contenido (blob:sha256:<hash>): almacenamiento global orientado a binarios, utilizado para externalizar cargas útiles grandes de imágenes en base64 de las entradas de sesión persistidas.
  • Artefactos con alcance de sesión (archivos bajo <archivoSesión-sin-.jsonl>/): archivos de texto por sesión utilizados para salidas completas de herramientas y salidas de subagentes.

Están separados intencionalmente:

  • el almacenamiento de blobs optimiza la deduplicación y las referencias estables mediante hash de contenido,
  • el almacenamiento de artefactos optimiza las herramientas de sesión de solo adición y la recuperación por humanos/herramientas mediante IDs locales.

Límites de almacenamiento y disposición en disco

Sección titulada «Límites de almacenamiento y disposición en disco»

SessionManager construye BlobStore(getBlobsDir()), por lo que los archivos de blobs residen en un directorio de blobs global compartido (no en una carpeta de sesión).

Nomenclatura de archivos de blobs:

  • ruta del archivo: <blobsDir>/<sha256-hex>
  • sin extensión
  • cadena de referencia almacenada en las entradas: blob:sha256:<sha256-hex>

Implicaciones:

  • el mismo contenido binario en diferentes sesiones se resuelve al mismo hash/ruta,
  • las escrituras son idempotentes a nivel de contenido,
  • los blobs pueden sobrevivir a cualquier archivo de sesión individual.

ArtifactManager deriva el directorio de artefactos a partir de la ruta del archivo de sesión:

  • archivo de sesión: .../<timestamp>_<sessionId>.jsonl
  • directorio de artefactos: .../<timestamp>_<sessionId>/ (se elimina .jsonl)

Los tipos de artefactos comparten este directorio:

  • archivos de salida truncada de herramientas: <numericId>.<toolType>.log (para artifact://)
  • archivos de salida de subagentes: <outputId>.md (para agent://)

BlobStore.put() calcula SHA-256 sobre los bytes binarios en bruto y devuelve:

  • hash: resumen hexadecimal,
  • path: <blobsDir>/<hash>,
  • ref: blob:sha256:<hash>.

No se utiliza ningún contador local de sesión.

IDs de artefactos: entero monotónico local a la sesión

Sección titulada «IDs de artefactos: entero monotónico local a la sesión»

ArtifactManager escanea los archivos de artefactos *.log existentes en el primer uso para encontrar el ID numérico máximo existente y establece nextId = max + 1.

Comportamiento de asignación:

  • formato de archivo: {id}.{toolType}.log
  • los IDs son cadenas secuenciales ("0", "1", …)
  • la reanudación no sobrescribe artefactos existentes porque el escaneo ocurre antes de la asignación.

Si el directorio de artefactos no existe, el escaneo produce una lista vacía y la asignación comienza desde 0.

AgentOutputManager asigna IDs para las salidas de subagentes como <index>-<requestedId> (opcionalmente anidados bajo un prefijo padre, por ejemplo 0-Parent.1-Child). Escanea los archivos .md existentes durante la inicialización para continuar desde el siguiente índice en la reanudación.

1) Ruta de reescritura de persistencia de entradas de sesión

Sección titulada «1) Ruta de reescritura de persistencia de entradas de sesión»

Antes de que las entradas de sesión se escriban (#rewriteFile / persistencia incremental), SessionManager llama a prepareEntryForPersistence() (a través de truncateForPersistence).

Comportamientos clave:

  1. Truncado de cadenas grandes: las cadenas sobredimensionadas se cortan y se les añade el sufijo "[Session persistence truncated large content]".
  2. Eliminación de campos transitorios: partialJson y jsonlEvents se eliminan de las entradas persistidas.
  3. Externalización de imágenes a blobs:
    • solo se aplica a bloques de imagen en arrays content,
    • solo cuando data no es ya una referencia de blob,
    • solo cuando la longitud del base64 alcanza al menos el umbral (BLOB_EXTERNALIZE_THRESHOLD = 1024),
    • reemplaza el base64 en línea con blob:sha256:<hash>.

Esto mantiene el JSONL de sesión compacto mientras preserva la recuperabilidad.

2) Ruta de rehidratación en la carga de sesión

Sección titulada «2) Ruta de rehidratación en la carga de sesión»

Al abrir una sesión (setSessionFile), después de las migraciones, SessionManager ejecuta resolveBlobRefsInEntries().

Para cada bloque de imagen de message/custom-message con blob:sha256:<hash>:

  • lee los bytes del blob desde el almacén de blobs,
  • convierte los bytes de vuelta a base64,
  • muta la entrada en memoria para incluir el base64 en línea para los consumidores del runtime.

Si el blob no se encuentra:

  • resolveImageData() registra una advertencia,
  • devuelve la cadena de referencia original sin cambios,
  • la carga continúa (sin fallo crítico).

3) Ruta de volcado/truncado de salida de herramientas

Sección titulada «3) Ruta de volcado/truncado de salida de herramientas»

OutputSink potencia la salida en streaming en bash/python/ssh y ejecutores relacionados.

Comportamiento:

  1. Cada fragmento se sanitiza y se añade al buffer de cola en memoria.
  2. Cuando los bytes en memoria exceden el umbral de volcado (DEFAULT_MAX_BYTES, 50KB), el sink marca la salida como truncada.
  3. Si hay disponible una ruta de artefacto, el sink abre un escritor de archivos y escribe:
    • el contenido almacenado en buffer existente una vez,
    • todos los fragmentos subsiguientes.
  4. El buffer en memoria siempre se recorta a la ventana de cola para visualización.
  5. dump() devuelve un resumen que incluye artifactId solo cuando el escritor de archivos se creó exitosamente.

Efecto práctico:

  • La UI/retorno de herramienta muestra la cola truncada,
  • la salida completa se preserva en el archivo de artefacto y se referencia como artifact://<id>.

Si la creación del escritor de archivos falla (error de E/S, ruta inexistente, etc.), el sink recurre silenciosamente al truncado solo en memoria; la salida completa no se persiste.

blob:sha256:<hash> es una referencia de persistencia dentro de las cargas útiles de entradas de sesión, no un esquema de URL interno manejado por el enrutador. La resolución la realiza SessionManager durante la carga de sesión.

Manejado por ArtifactProtocolHandler:

  • requiere un directorio de artefactos de sesión activo,
  • el ID debe ser numérico,
  • se resuelve comparando el prefijo del nombre de archivo <id>.,
  • devuelve texto sin formato (text/plain) del archivo .log coincidente,
  • cuando no se encuentra, el error incluye una lista de IDs de artefactos disponibles.

Comportamiento con directorio inexistente:

  • si el directorio de artefactos no existe, lanza No artifacts directory found.

Manejado por AgentProtocolHandler sobre <artifactsDir>/<id>.md:

  • la forma simple devuelve texto markdown,
  • las formas /path o ?q= realizan extracción JSON,
  • la extracción por ruta y por consulta no se pueden combinar,
  • si se solicita extracción, el contenido del archivo debe parsearse como JSON.

Comportamiento con directorio inexistente:

  • lanza No artifacts directory found.

Comportamiento con salida inexistente:

  • lanza Not found: <id> con los IDs disponibles de los archivos .md existentes.

Integración con la herramienta read:

  • read soporta paginación con offset/limit para lecturas de URLs internas sin extracción,
  • rechaza offset/limit cuando se utiliza extracción de agent://.

Semánticas de reanudación, bifurcación y movimiento

Sección titulada «Semánticas de reanudación, bifurcación y movimiento»
  • ArtifactManager escanea los archivos {id}.*.log existentes en la primera asignación y continúa la numeración.
  • AgentOutputManager escanea los IDs de salida .md existentes y continúa la numeración.
  • SessionManager rehidrata las referencias de blob a base64 durante la carga.

SessionManager.fork() crea un nuevo archivo de sesión con un nuevo ID de sesión y un enlace parentSession, luego devuelve las rutas de archivo antigua/nueva. La copia de artefactos es manejada por AgentSession.fork():

  • intenta una copia recursiva del directorio de artefactos antiguo al nuevo directorio de artefactos,
  • se tolera la ausencia del directorio antiguo,
  • los errores de copia que no sean ENOENT se registran como advertencias y la bifurcación se completa igualmente.

Implicaciones de IDs después de la bifurcación:

  • si la copia tuvo éxito, los contadores de artefactos en la nueva sesión continúan después del ID máximo copiado,
  • si la copia falló/se omitió, los IDs de artefactos de la nueva sesión comienzan desde 0.

Implicaciones de blobs después de la bifurcación:

  • los blobs son globales y con direccionamiento por contenido, por lo que no se requiere copia del directorio de blobs.

SessionManager.moveTo() renombra tanto el archivo de sesión como el directorio de artefactos al nuevo directorio de sesión predeterminado, con lógica de rollback si un paso posterior falla. Esto preserva la identidad de los artefactos mientras se reubica el alcance de la sesión.

CasoComportamiento
Archivo de blob inexistente durante la rehidrataciónAdvierte y mantiene la cadena de referencia blob:sha256: en memoria
Blob con ENOENT al leer vía BlobStore.getDevuelve null
Directorio de artefactos inexistente (ArtifactManager.listFiles)Devuelve lista vacía (la asignación puede comenzar desde cero)
Directorio de artefactos inexistente (artifact:// / agent://)Lanza explícitamente No artifacts directory found
ID de artefacto no encontradoLanza con listado de IDs disponibles
Fallo en la inicialización del escritor de artefactos de OutputSinkContinúa con truncado solo de cola (sin artefacto de salida completa)
Sin archivo de sesión (algunas rutas de tareas)La herramienta Task recurre a un directorio temporal de artefactos para las salidas de subagentes

Externalización de blobs binarios vs artefactos de salida de texto

Sección titulada «Externalización de blobs binarios vs artefactos de salida de texto»
  • La externalización de blobs es para cargas útiles de imágenes binarias dentro del contenido de entradas de sesión persistidas; reemplaza el base64 en línea en el JSONL con referencias de contenido estables.
  • Los artefactos son archivos de texto plano para salidas de ejecución y salidas de subagentes; son direccionables mediante IDs locales de sesión a través de URLs internas.

Los dos sistemas se intersectan solo indirectamente (ambos reducen la inflación del JSONL de sesión) pero tienen diferentes identidades, ciclos de vida y rutas de recuperación.