Subclientes¶
Los objetos que cuelgan de un sandbox (sbx.commands, sbx.files,
sbx.pty, sbx.git, sbx.agent), sus handles y el paginador de
Sandbox.paginate(). Las variantes asíncronas (AsyncCommandHandle,
AsyncWatchHandle, AsyncUploadTicket, AsyncPtyHandle, AsyncGit,
AsyncAgentStream, AsyncSandboxListPaginator) tienen la misma superficie
con corrutinas.
Comandos¶
Commands(sandbox: Sandbox)
¶
Comandos del sandbox (ProcessService).
run(cmd: str, *, background: bool = False, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, on_stdout: OutputCallback | None = None, on_stderr: OutputCallback | None = None, stdin: bool = False, timeout: float | None = DEFAULT_COMMAND_TIMEOUT_SECONDS, request_timeout: float | None = None, tag: str | None = None, secrets: Mapping[str, str | SecretRef] | None = None, max_output_bytes: int = COMMAND_OUTPUT_MAX_BYTES, kill_tree: bool = False) -> CommandResult | CommandHandle
¶
run(
cmd: str,
*,
background: Literal[False] = False,
envs: Mapping[str, str] | None = None,
user: str | None = None,
cwd: str | None = None,
on_stdout: OutputCallback | None = None,
on_stderr: OutputCallback | None = None,
stdin: bool = False,
timeout: float | None = DEFAULT_COMMAND_TIMEOUT_SECONDS,
request_timeout: float | None = None,
tag: str | None = None,
secrets: Mapping[str, str | SecretRef] | None = None,
max_output_bytes: int = COMMAND_OUTPUT_MAX_BYTES,
kill_tree: bool = False,
) -> CommandResult
run(
cmd: str,
*,
background: Literal[True],
envs: Mapping[str, str] | None = None,
user: str | None = None,
cwd: str | None = None,
on_stdout: OutputCallback | None = None,
on_stderr: OutputCallback | None = None,
stdin: bool = False,
timeout: float | None = DEFAULT_COMMAND_TIMEOUT_SECONDS,
request_timeout: float | None = None,
tag: str | None = None,
secrets: Mapping[str, str | SecretRef] | None = None,
max_output_bytes: int = COMMAND_OUTPUT_MAX_BYTES,
kill_tree: bool = False,
) -> CommandHandle
run(
cmd: str,
*,
background: bool,
envs: Mapping[str, str] | None = None,
user: str | None = None,
cwd: str | None = None,
on_stdout: OutputCallback | None = None,
on_stderr: OutputCallback | None = None,
stdin: bool = False,
timeout: float | None = DEFAULT_COMMAND_TIMEOUT_SECONDS,
request_timeout: float | None = None,
tag: str | None = None,
secrets: Mapping[str, str | SecretRef] | None = None,
max_output_bytes: int = COMMAND_OUTPUT_MAX_BYTES,
kill_tree: bool = False,
) -> CommandResult | CommandHandle
Ejecuta cmd con /bin/bash -l -c como user (uid 1000 por defecto).
timeout lo impone el agente sobre el reloj corrido (SIGTERM al
vencer, SIGKILL 5 s después; una pausa no lo consume) y por defecto
son 60 s; None o 0 lo desactivan. En foreground el resultado
llega con la salida y un exit distinto de cero es
CommandExitException; el timeout es TimeoutException. La salida
guardada (la del resultado y la del handle) está acotada a
max_output_bytes por descriptor (64 MiB por defecto): lo más
antiguo se descarta y truncated lo indica; 0 no guarda nada.
on_stdout/on_stderr reciben siempre todo. En background
devuelve un CommandHandle que consume el stream cuando se itera o se
llama a wait(): mientras nadie lo lee, el agente aplica backpressure
y, tras 30 s con el canal lleno, cierra ese suscriptor con
output_truncated. Un stream abierto cuenta como actividad para la
política de idle del MicroVM. request_timeout acota los unarios que
el handle haga después (kill, send_stdin, close_stdin).
kill_tree=True hace que el timeout y kill() alcancen también a los
descendientes que salieron del grupo del proceso (setsid, un doble
fork): el agente lo hace child subreaper de ellos, manda el SIGTERM
del timeout a todo el árbol y, tras la gracia, congela y mata a los
que queden; el fin del comando llega cuando ya no queda ninguno. Con
False (por defecto) sólo se señala el grupo, como en E2B. Lo que el
comando deja vivo al terminar por sí mismo no se toca.
Coste y activación¶
Activa: secrets={"ENV": "nombre" | SecretRef} inyecta secretos de
Secrets Manager como variables de entorno de este comando (más los
del handle; una clave que también esté en envs es
InvalidArgumentException).
Recursos y llamadas AWS: secretsmanager:GetSecretValue sólo en un
fallo de SecretCache (TTL 300 s): tres comandos seguidos = una
lectura. Ningún recurso nuevo.
Coste aproximado: $0,05 por 10 000 llamadas (us-east-1, 2026-09-30).
IAM: secretsmanager:GetSecretValue en las credenciales del llamante.
Cómo apagarla: secrets=None (por defecto).
Ejemplo:
sbx.commands.run("python agent.py", secrets={"OPENAI_API_KEY": "openai"})
Con tracer_provider= en create()/connect(), cada llamada abre un
span rayito.commands.run (nunca con el texto del comando, sus
argumentos ni sus envs): rayito.commands.background siempre, y en
foreground rayito.commands.exit_code al terminar (también cuando sale con
un código distinto de cero). En segundo plano
el span se cierra en cuanto start devuelve el handle, no cuando el
proceso termina.
connect(pid: int, *, from_seq: int = 0, on_stdout: OutputCallback | None = None, on_stderr: OutputCallback | None = None, timeout: float | None = None, request_timeout: float | None = None, max_output_bytes: int = COMMAND_OUTPUT_MAX_BYTES) -> CommandHandle
¶
Se engancha a un proceso vivo o terminado hace menos de 30 s.
from_seq=0 entrega sólo salida nueva; N reenvía primero lo retenido
con seq >= N (último MiB). Un seq ya descartado o un pid
desconocido es NotFoundException. timeout es aquí el deadline gRPC
del stream (no hay timeout de servidor en Connect). max_output_bytes
acota la salida guardada como en run.
list(*, request_timeout: float | None = None) -> list[ProcessInfo]
¶
Procesos y PTY vivos (los terminados desaparecen aunque sigan conectables).
kill(pid: int, *, request_timeout: float | None = None) -> bool
¶
SIGKILL al grupo del proceso (o de la PTY) y, si se lanzó con
kill_tree=True, antes a todo su árbol. False si el pid no existe o
ya terminó.
send_stdin(pid: int, data: str | bytes, *, request_timeout: float | None = None) -> None
¶
Escribe en el stdin de un proceso lanzado con stdin=True (str va
en UTF-8). Un pid de PTY es InvalidArgumentException: usa pty.send_input.
close_stdin(pid: int, *, request_timeout: float | None = None) -> None
¶
EOF en el stdin del proceso; idempotente.
CommandHandle(*, commands: Commands, call: Any, progress: CommandProgress, request_timeout: float | None, deadline_at: float | None = None, foreground: bool = False)
¶
Un proceso en background o enganchado con connect.
Consume el stream perezosamente en el hilo que itera o llama a wait();
disconnect() cancela el stream sin tocar el proceso, que sigue vivo y
listado hasta que termine o alguien lo mate. Si el agente cierra el
stream por un /suspend o el proxy lo corta, la siguiente lectura espera
al agente y se reengancha con Connect(pid, from_seq=last_seq + 1);
reconnects cuenta esas veces. Leer un handle en background nunca
despierta un sandbox suspendido: la lectura se bloquea (sin consumir
reconnect_timeout) hasta el resume(), el auto-resume que provoque
otra llamada o el fin del MicroVM.
last_seq: int
property
¶
Último seq recibido; connect(pid, from_seq=last_seq + 1) reanuda sin huecos.
reconnects: int
property
¶
Veces que el handle se reenganchó tras un suspend/resume o un corte.
wait(on_pty: Callable[[bytes], Any] | None = None, on_stdout: Callable[[str], Any] | None = None, on_stderr: Callable[[str], Any] | None = None) -> CommandResult
¶
Consume el stream hasta el EndEvent. Exit distinto de cero →
CommandExitException; timeout del agente → TimeoutException;
output_truncated → SandboxException. Idempotente.
on_stdout/on_stderr (texto) y on_pty (bytes de una PTY) reciben
cada chunk que esta llamada consume, después de los callbacks de
run(); lo ya leído antes (iterando) no se repite.
disconnect() -> None
¶
Cancela el stream; el proceso sigue corriendo en el sandbox. Un handle desconectado nunca reconecta.
AsyncCommandHandle(*, commands: AsyncCommands, call: Any, progress: CommandProgress, request_timeout: float | None, deadline_at: float | None = None, foreground: bool = False)
¶
CommandHandle sobre grpc.aio: await wait(), async for y
await kill()/send_stdin()/close_stdin(); disconnect() es síncrono.
Reconecta como CommandHandle tras un suspend/resume.
wait(on_pty: Callable[[bytes], Any] | None = None, on_stdout: Callable[[str], Any] | None = None, on_stderr: Callable[[str], Any] | None = None) -> CommandResult
async
¶
Como CommandHandle.wait; los callbacks pueden ser síncronos o
async (su resultado se espera si es awaitable antes del siguiente
chunk).
Ficheros¶
Filesystem(sandbox: Sandbox)
¶
Ficheros del sandbox (FilesystemService).
read(path: str, *, format: str = 'text', gzip: bool = False, stream_idle_timeout: float | None = None, user: str | None = None, request_timeout: float | None = None) -> str | bytes | Iterator[bytes]
¶
read(
path: str,
*,
format: Literal["text"] = "text",
gzip: bool = False,
stream_idle_timeout: float | None = None,
user: str | None = None,
request_timeout: float | None = None,
) -> str
Lee un fichero regular: text (UTF-8 estricto), bytes o stream
(chunks de hasta 256 KiB tal como llegan). Hace Stat primero: un
directorio o un symlink es InvalidArgumentException sin abrir el
stream, y el deadline del Read es 60 s + 1 s por MB del tamaño
salvo request_timeout.
gzip=True pide la respuesta comprimida (inocuo en una imagen
anterior, que responde sin comprimir). stream_idle_timeout (s)
cancela la lectura si el siguiente chunk tarda más y levanta
TimeoutException. Con transfer=S3Staging(...) un fichero de al
menos threshold_bytes se exporta a S3 y se descarga con tus
credenciales (verificando su sha256); gzip no aplica ahí.
Con tracer_provider= abre rayito.files.read (nunca con path ni
el contenido como atributo); en format="text"/"bytes",
rayito.files.bytes al terminar.
write(path: str, data: str | bytes | IO[bytes] | IO[str], *, user: str | None = None, mode: int | None = None, gzip: bool = False, metadata: Mapping[str, str] | None = None, use_octet_stream: bool = False, request_timeout: float | None = None) -> EntryInfo
¶
Escribe data (str en UTF-8, bytes o fichero abierto) de forma
atómica, creando los padres y con mode (0o644 por defecto). Los
padres que falten se crean; el propietario es user. gzip,
metadata y use_octet_stream como en write_files.
Con tracer_provider= abre rayito.files.write (nunca con path ni
data), con rayito.files.bytes al terminar.
write_files(files: Sequence[WriteEntry], *, user: str | None = None, gzip: bool = False, metadata: Mapping[str, str] | None = None, use_octet_stream: bool = False, request_timeout: float | None = None) -> list[EntryInfo]
¶
N ficheros en un stream Write; devuelve sus EntryInfo en
orden. Cada fichero se compromete de forma atómica por separado: si
el stream falla a mitad, los ya escritos quedan y el resto no existe.
Deadline 60 s + 1 s por MB del total salvo request_timeout.
gzip=True comprime el stream (grpc-encoding: gzip). metadata
(claves token de HTTP, se guardan en minúsculas) se aplica a cada
fichero y sustituye el conjunto entero; se valida antes de cualquier
RPC. Ambos exigen un agente 0.3.0 o posterior (UnimplementedError si no).
use_octet_stream se acepta y no tiene efecto (gRPC no usa
formularios). Con transfer=S3Staging(...), cada fichero de al menos
threshold_bytes (o un stream binario no buscable) se sube a S3 con
tus credenciales y rayd lo importa; gzip no aplica ahí.
Con tracer_provider= abre rayito.files.write_files (nunca las
rutas ni los datos), con rayito.files.count y rayito.files.bytes
al terminar.
upload_url(path: str, *, user: str | None = None, expires_in: int = TRANSFER_DEFAULT_EXPIRES_IN_SECONDS, max_bytes: int | None = None, form: bool = False, request_timeout: float | None = None) -> UploadTicket
¶
Una URL de subida a S3 firmada con tus credenciales, con la
importación a path ya armada en el sandbox. Es un str:
requests.put(ticket, data=f, headers=ticket.headers) basta; con
form=True es un formulario POST (ticket.fields más el fichero en
file) que S3 limita a max_bytes. De un solo uso; expires_in se
topa en min(expires_in, transfer.max_expires_in, 604800). El
fichero aparece de forma asíncrona: read, get_info, list, un
comando o una celda esperan a la importación, y ticket.wait() la
espera explícitamente. Sin transfer=S3Staging(...) ni
RAYITO_TRANSFER_BUCKET levanta UnimplementedError.
download_url(path: str, *, user: str | None = None, expires_in: int = TRANSFER_DEFAULT_EXPIRES_IN_SECONDS, filename: str | None = None, request_timeout: float | None = None) -> DownloadLink
¶
Exporta path tal como está ahora a S3 y devuelve una URL de
descarga firmada con tus credenciales (un str con size, sha256
y expires_at); urlopen(link) basta y admite Range. Un fichero
inexistente levanta FileNotFoundException al llamar; un directorio
o un symlink, InvalidArgumentException. filename es el nombre de
Content-Disposition (por defecto el de path).
list(path: str, *, depth: int = 1, user: str | None = None, request_timeout: float | None = None) -> list[EntryInfo]
¶
Entradas hasta depth niveles (1 = sólo hijos directos; 0 vale 1)
en preorden, ordenadas por nombre en cada directorio, sin seguir
symlinks. Más de 10 000 entradas es RateLimitException.
Con tracer_provider= abre rayito.files.list (nunca path), con
rayito.files.count al terminar.
exists(path: str, *, user: str | None = None, request_timeout: float | None = None) -> bool
¶
False sólo si Stat responde NOT_FOUND; otros errores se propagan.
Con tracer_provider= abre rayito.files.exists (nunca path).
get_info(path: str, *, user: str | None = None, request_timeout: float | None = None) -> EntryInfo
¶
Stat sin seguir symlinks (type is FileType.SYMLINK con symlink_target).
Con tracer_provider= abre rayito.files.get_info (nunca path).
remove(path: str, *, recursive: bool = True, user: str | None = None, request_timeout: float | None = None) -> None
¶
Borra un fichero, symlink (sólo el enlace) o directorio; con
recursive=False un directorio no vacío es InvalidArgumentException.
Con tracer_provider= abre rayito.files.remove (nunca path).
rename(old_path: str, new_path: str, *, user: str | None = None, request_timeout: float | None = None) -> EntryInfo
¶
rename(2): reemplaza un fichero regular existente; un destino que
es un directorio no vacío es InvalidArgumentException.
Con tracer_provider= abre rayito.files.rename (nunca las rutas).
make_dir(path: str, *, user: str | None = None, request_timeout: float | None = None) -> bool
¶
Crea el directorio y sus padres (0o755). True si lo creó, False si
ya existía; si existe algo que no es un directorio,
InvalidArgumentException.
Con tracer_provider= abre rayito.files.make_dir (nunca path).
watch_dir(path: str, *, on_event: EventCallback | None = None, on_exit: ExitCallback | None = None, recursive: bool = False, include_entry: bool = False, user: str | None = None, timeout: float | None = 0, request_timeout: float | None = None) -> WatchHandle
¶
Observa un directorio (inotify, sin debounce). Bloquea hasta
WatchStarted y devuelve un WatchHandle que consume el stream en
un hilo daemon: con on_event cada evento va al callback; sin él se
acumulan para get_new_events(). timeout (> 0) es el deadline gRPC
del stream y termina en TimeoutException; 0/None es ilimitado.
request_timeout se acepta por uniformidad y no aplica al stream. Un
files.write en el directorio aparece como un único WRITE.
WatchHandle(*, filesystem: Filesystem, call: Any, path: str, state: WatchState, on_exit: ExitCallback | None, request: filesystem_pb2.WatchDirRequest | None = None, deadline_at: float | None = None)
¶
Un watch_dir vivo. stop() cancela el stream, espera al hilo (≤ 5 s)
y es idempotente; Sandbox.close() lo llama por cada handle vivo. Tras
un suspend/resume (o un corte del proxy) el hilo consumidor espera al
agente y vuelve a emitir WatchDir sin llamar a on_exit; reconnects
cuenta esas veces.
AsyncWatchHandle(*, filesystem: AsyncFilesystem, call: Any, path: str, state: WatchState, on_exit: ExitCallback | None, request: filesystem_pb2.WatchDirRequest | None = None, deadline_at: float | None = None)
¶
WatchHandle sobre grpc.aio: await get_new_events(), await stop()
y async with.
UploadTicket
¶
Bases: UploadTicketBase
Resultado de files.upload_url: la importación ya está armada en
rayd antes de que la URL exista para el caller, y es de un solo uso.
wait() sigue la transferencia hasta su final y devuelve el EntryInfo
escrito (o levanta la excepción mapeada); con timeout agotado levanta
TimeoutException y el ticket sigue armado. status() es una foto y
cancel() la cancela (idempotente).
AsyncUploadTicket
¶
Bases: UploadTicketBase
UploadTicket de AsyncSandbox: wait(), status() y cancel()
son corrutinas.
Terminal (PTY)¶
Pty(sandbox: Sandbox)
¶
Terminales interactivas del sandbox (PtyService).
create(*, size: PtySize | None = None, user: str | None = None, cwd: str | None = None, envs: Mapping[str, str] | None = None, shell: str | None = None, on_data: PtyDataCallback | None = None, timeout: float | None = DEFAULT_PTY_TIMEOUT_SECONDS, request_timeout: float | None = None, secrets: Mapping[str, str | SecretRef] | None = None) -> PtyHandle
¶
Abre una terminal con el shell de login de user (uid 1000 por
defecto) como <shell> -i -l, TERM=xterm-256color y LANG/
LC_ALL=C.UTF-8 (los envs del request ganan). size es 80x24 si
se omite; shell debe ser una ruta absoluta.
timeout (60 s por defecto, como en E2B) lo impone el agente sobre el
reloj corrido: al vencer manda SIGTERM al grupo y wait() termina en
TimeoutException. Para un shell que deba sobrevivir, timeout=None.
on_data recibe cada chunk de bytes de la terminal mientras el
handle se itera o se espera; el SDK nunca los loguea.
Coste y activación¶
Activa: secrets= inyecta secretos como variables de entorno del shell
(más los del handle).
Recursos y llamadas AWS: secretsmanager:GetSecretValue sólo en un
fallo de SecretCache (TTL 300 s).
Coste aproximado: $0,05 por 10 000 llamadas (us-east-1, 2026-09-30).
IAM: secretsmanager:GetSecretValue en las credenciales del llamante.
Cómo apagarla: secrets=None (por defecto).
Ejemplo:
term = sbx.pty.create(secrets={"GITHUB_TOKEN": "gh"})
connect(pid: int, *, from_seq: int = 0, on_data: PtyDataCallback | None = None, timeout: float | None = None, request_timeout: float | None = None) -> PtyHandle
¶
Se reengancha a una PTY viva o terminada hace menos de 30 s.
from_seq=0 entrega sólo salida nueva; N reenvía lo retenido con
seq >= N. timeout es el deadline gRPC de este stream.
send_input(pid: int, data: str | bytes, *, request_timeout: float | None = None) -> None
¶
Escribe en la terminal (str va en UTF-8; un \n final ejecuta la
línea). Una PTY terminada es NotFoundException; el pid de un proceso
normal es InvalidArgumentException.
resize(pid: int, size: PtySize, *, request_timeout: float | None = None) -> None
¶
TIOCSWINSZ sobre la terminal; el shell recibe SIGWINCH.
kill(pid: int, *, request_timeout: float | None = None) -> bool
¶
SIGKILL al grupo de la terminal. False si el pid no existe o ya terminó.
PtyHandle(*, pty: Pty, call: Any, progress: CommandProgress, request_timeout: float | None, deadline_at: float | None = None)
¶
Bases: CommandHandle
Una terminal abierta con pty.create o pty.connect.
Es un CommandHandle (pid, last_seq, stdout con la salida de la
terminal decodificada, stderr vacío, exit_code, error, wait(),
disconnect(), reconnects) cuya iteración entrega (None, None, bytes)
por mensaje y cuyo kill() es PtyService.Kill. send_input escribe en
la terminal y resize cambia su tamaño.
AsyncPtyHandle(*, pty: AsyncPty, call: Any, progress: CommandProgress, request_timeout: float | None, deadline_at: float | None = None)
¶
Git¶
Git(commands: Commands)
¶
Operaciones git en el sandbox con la superficie de E2B 2.51
(sandbox.git). E2B marca su módulo como obsoleto a favor de
commands.run(); aquí se mantiene por paridad.
timeout=None (por defecto) es sin plazo de servidor. Un fallo de
autenticación contra un remoto es GitAuthException, un push/pull sin
upstream GitUpstreamException y cualquier otro fallo el
CommandExitException del comando, sin la contraseña cuando la llamada
llevaba credenciales.
clone(url: str, path: str | None = None, branch: str | None = None, depth: int | None = None, username: str | None = None, password: str | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None, dangerously_store_credentials: bool = False) -> CommandResult
¶
git clone. Con username y password clona desde la URL con
las credenciales y, salvo dangerously_store_credentials=True,
deja origin apuntando a la URL sin ellas.
remote_add(path: str, name: str, url: str, fetch: bool = False, overwrite: bool = False, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
¶
git remote add [-f]; con overwrite cae a remote set-url si
el remoto ya existe.
remote_get(path: str, name: str, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> str | None
¶
La URL del remoto, o None si no existe.
add(path: str, files: Sequence[str] | None = None, all: bool = True, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
¶
git add -- <files>; sin files, -A (o . con all=False).
reset(path: str, mode: GitResetMode | None = None, target: str | None = None, paths: Sequence[str] | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
¶
Un mode fuera de soft/mixed/hard/merge/keep es
InvalidArgumentException sin ejecutar nada.
restore(path: str, paths: Sequence[str], staged: bool | None = None, worktree: bool | None = None, source: str | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
¶
Sin staged ni worktree restaura el árbol de trabajo;
staged=True solo saca del índice. Una lista vacía es
InvalidArgumentException sin ejecutar nada.
push(path: str, remote: str | None = None, branch: str | None = None, set_upstream: bool = True, username: str | None = None, password: str | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
¶
git push. Con credenciales: remote get-url, remote set-url
con la URL con credenciales, el push y, pase lo que pase, remote
set-url con la URL original.
pull(path: str, remote: str | None = None, branch: str | None = None, username: str | None = None, password: str | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
¶
git pull. Sin remote ni branch exige una rama con upstream
(GitUpstreamException si no la hay); las credenciales se inyectan y
se retiran como en push.
set_config(key: str, value: str, scope: str = 'global', path: str | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
¶
git config --global|--local|--system; scope="local" necesita path.
get_config(key: str, scope: str = 'global', path: str | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> str | None
¶
El valor, o None si la clave no está en ese scope.
dangerously_authenticate(username: str, password: str, host: str = 'github.com', protocol: str = 'https', envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
¶
Autentica git para todo el sandbox con el helper store:
credential.helper=store global y git credential approve.
Las credenciales quedan en ~/.git-credentials del usuario del
sandbox y son visibles para cualquier código que corra en él, igual
que en E2B. Usa tokens de vida corta. El SDK nunca las loguea.
configure_user(name: str, email: str, scope: str = 'global', path: str | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
¶
user.name y user.email en el scope pedido.
AsyncGit(commands: AsyncCommands)
¶
Git como corrutinas (la clase Git del SDK async de E2B 2.51). E2B
marca su módulo como obsoleto a favor de commands.run(); aquí se
mantiene por paridad.
timeout=None (por defecto) es sin plazo de servidor. Un fallo de
autenticación contra un remoto es GitAuthException, un push/pull sin
upstream GitUpstreamException y cualquier otro fallo el
CommandExitException del comando, sin la contraseña cuando la llamada
llevaba credenciales.
clone(url: str, path: str | None = None, branch: str | None = None, depth: int | None = None, username: str | None = None, password: str | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None, dangerously_store_credentials: bool = False) -> CommandResult
async
¶
git clone. Con username y password clona desde la URL con
las credenciales y, salvo dangerously_store_credentials=True,
deja origin apuntando a la URL sin ellas.
remote_add(path: str, name: str, url: str, fetch: bool = False, overwrite: bool = False, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
async
¶
git remote add [-f]; con overwrite cae a remote set-url si
el remoto ya existe.
remote_get(path: str, name: str, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> str | None
async
¶
La URL del remoto, o None si no existe.
add(path: str, files: Sequence[str] | None = None, all: bool = True, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
async
¶
git add -- <files>; sin files, -A (o . con all=False).
reset(path: str, mode: GitResetMode | None = None, target: str | None = None, paths: Sequence[str] | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
async
¶
Un mode fuera de soft/mixed/hard/merge/keep es
InvalidArgumentException sin ejecutar nada.
restore(path: str, paths: Sequence[str], staged: bool | None = None, worktree: bool | None = None, source: str | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
async
¶
Sin staged ni worktree restaura el árbol de trabajo;
staged=True solo saca del índice. Una lista vacía es
InvalidArgumentException sin ejecutar nada.
push(path: str, remote: str | None = None, branch: str | None = None, set_upstream: bool = True, username: str | None = None, password: str | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
async
¶
git push. Con credenciales: remote get-url, remote set-url
con la URL con credenciales, el push y, pase lo que pase, remote
set-url con la URL original.
pull(path: str, remote: str | None = None, branch: str | None = None, username: str | None = None, password: str | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
async
¶
git pull. Sin remote ni branch exige una rama con upstream
(GitUpstreamException si no la hay); las credenciales se inyectan y
se retiran como en push.
set_config(key: str, value: str, scope: str = 'global', path: str | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
async
¶
git config --global|--local|--system; scope="local" necesita path.
get_config(key: str, scope: str = 'global', path: str | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> str | None
async
¶
El valor, o None si la clave no está en ese scope.
dangerously_authenticate(username: str, password: str, host: str = 'github.com', protocol: str = 'https', envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
async
¶
Autentica git para todo el sandbox con el helper store:
credential.helper=store global y git credential approve.
Las credenciales quedan en ~/.git-credentials del usuario del
sandbox y son visibles para cualquier código que corra en él, igual
que en E2B. Usa tokens de vida corta. El SDK nunca las loguea.
configure_user(name: str, email: str, scope: str = 'global', path: str | None = None, envs: Mapping[str, str] | None = None, user: str | None = None, cwd: str | None = None, timeout: float | None = None, request_timeout: float | None = None) -> CommandResult
async
¶
user.name y user.email en el scope pedido.
GitStatus(current_branch: str | None, upstream: str | None, ahead: int, behind: int, detached: bool, file_status: list[GitFileStatus] = list())
dataclass
¶
El estado de un repositorio tal como lo modela E2B.
GitFileStatus(name: str, status: str, index_status: str, working_tree_status: str, staged: bool, renamed_from: str | None = None)
dataclass
¶
Una entrada de git status --porcelain=1: status normalizado
(modified, added, untracked, conflict...) y los dos caracteres
crudos del índice y del árbol de trabajo.
GitBranches(branches: list[str] = list(), current_branch: str | None = None)
dataclass
¶
GitResetMode es el alias Literal["soft", "mixed", "hard", "merge", "keep"]
del mode= de git.reset().
Agente de IA (sbx.agent)¶
run() corre el agente hasta el final (lanza AgentException si falla);
stream() devuelve un AgentStream iterable que nunca lanza por un fallo
del agente; prepare() dispara el calentamiento del runtime en segundo
plano.
Agent(sandbox: AgentSandbox)
¶
El agente de IA de este sandbox (ai-agent-core, ADR-025). Una
instancia por Sandbox, creada en su constructor (como
.commands/.files): tocarla no hace ninguna llamada; el coste
empieza con el primer run()/stream().
Coste y activación¶
Activa: llamar a run(), stream() o prepare(). No hace falta
ninguna opción nueva: basta con usar sbx.agent.
Recursos y llamadas AWS: ninguno nuevo de Rayito. El propio runtime
llama al modelo (Bedrock Converse/ConverseStream, o la API que
exponga la pasarela) a través de la pasarela ya configurada con
gateways= en create(); sin ella, run()/stream() fallan con
InvalidArgumentException antes de cualquier llamada. El sandbox
ya factura por segundo, con o sin agente.
Coste aproximado: depende del modelo y de los pasos, no de Rayito.
Guía con precios de lista (us-east-1, consultados 2026-10-06,
https://aws.amazon.com/bedrock/pricing y
https://aws.amazon.com/lambda/pricing/): 10 pasos de Haiku 4.5
regional (15k tokens de entrada + 400 de salida por paso) ≈ $0,187
sin caché / $0,083 con caché de prompts; 5 minutos de MicroVM de
2 GB ≈ $0,0119. El modelo cuesta entre 7x y 21x la VM (cost.md,
"Coste de un agente").
IAM: ninguno adicional a lo que ya pide la pasarela (SecretGateway,
secretsmanager:GetSecretValue).
Cómo apagarla: no llames a sbx.agent.run()/stream()/prepare().
Ejemplo:
model_id = "us.anthropic.claude-haiku-4-5-20251001-v1:0"
with Sandbox.create(
"rayito-agent",
allow_internet_access=False,
gateways={"bedrock": bedrock_gateway(
"bedrock-key", region="us-east-1", models=[model_id],
)},
) as sbx:
result = sbx.agent.run(
"lista los ficheros de /home/user",
spec=AgentSpec(model=AgentModel(
provider="bedrock", id=model_id,
gateway="bedrock", region="us-east-1",
)),
)
run(prompt: str, *, spec: AgentSpec, runtime: str | AgentRuntime = DEFAULT_AGENT_RUNTIME, session_id: str | None = None, model: str | None = None, limits: AgentLimits | None = None, workdir: str = DEFAULT_AGENT_WORKDIR, reasoning: bool = False) -> AgentResult
¶
Corre el agente hasta que termina y devuelve su AgentResult.
Una ejecución fallida lanza AgentException (el último evento era
AgentFailed); un fallo de sandbox o de transporte (SandboxException,
TimeoutException) se propaga tal cual.
stream(prompt: str, *, spec: AgentSpec, runtime: str | AgentRuntime = DEFAULT_AGENT_RUNTIME, session_id: str | None = None, model: str | None = None, limits: AgentLimits | None = None, workdir: str = DEFAULT_AGENT_WORKDIR, reasoning: bool = False) -> AgentStream
¶
Como run(), pero devuelve un AgentStream iterable que nunca
lanza por un fallo del agente: el último evento es Done o
AgentFailed. Un fallo de sandbox o de transporte sí se propaga,
al construir el stream o al iterarlo.
prepare(*, runtime: str | AgentRuntime = DEFAULT_AGENT_RUNTIME) -> None
¶
Dispara los pasos de calentamiento del runtime (warmup_steps)
en segundo plano y vuelve enseguida: no espera a ninguno. Un paso
con background=True corre sin plazo: es un proceso que debe seguir
vivo y moriría al agotarlo.
AgentStream(*, sandbox: AgentSandbox, handle: CommandHandle, runtime: AgentRuntime, state: RuntimeState, limits: AgentLimits, session_id: str | None, span_context: AbstractContextManager[Any], span: Any)
¶
Iterable de AgentEvent sobre un CommandHandle en background:
cada chunk de stdout se troceaba en líneas, cada línea se traduce con
el adaptador del runtime y cada evento pasa por LimitTracker antes de
entregarse. Nunca lanza por un fallo del agente; result() sí.
AsyncAgentStream(*, sandbox: AgentSandbox, handle: AsyncCommandHandle, runtime: AgentRuntime, state: RuntimeState, limits: AgentLimits, session_id: str | None, span_context: AbstractContextManager[Any], span: Any)
¶
Espejo asíncrono de sandbox_sync.agent.AgentStream: implementa
AsyncIterable[AgentEvent]. Cancelar la tarea que corre __anext__
(o un async with del que se sale por cancelación) dispara abort().
abort() -> None
async
¶
Para los procesos que lanzó el runtime y mata el proceso.
Especificación¶
AgentSpec(model: AgentModel, small_model: str | None = None, instructions: str | None = None, mcp: Mapping[str, McpServer] = dict(), permissions: AgentPermissions = AgentPermissions(), agents: Mapping[str, SubAgent] = dict(), raw_config: Mapping[str, object] | None = None, runtime_version: str | None = None)
dataclass
¶
La configuración estática de un agente: modelo, instrucciones, servidores MCP, permisos y subagentes. Construirlo no llama a nada.
small_model (títulos, resúmenes) es por defecto model.id, así que el
runtime nunca elige por su cuenta un modelo que la cuenta quizá no tenga.
instructions se escribe en un AGENTS.md propio bajo
AGENT_STATE_DIR, nunca sobre el del directorio de trabajo (que OpenCode
también lee). raw_config se fusiona en profundidad al final y no puede
tocar RESERVED_CONFIG_KEYS. runtime_version sólo se valida como
texto: hoy no se compara con el manifiesto de la plantilla y nunca
produce runtime_version_mismatch.
gateway_names() -> frozenset[str]
¶
Las pasarelas que el agente necesita: la del modelo y la de cada
McpRemote.
require_gateways(available: Collection[str]) -> None
¶
Falla con InvalidArgumentException si alguna pasarela de
gateway_names() no está en available (los nombres de
sbx.gateways). Un nombre de pasarela nunca es secreto.
AgentModel(provider: ModelProvider, id: str, gateway: str, region: str | None = None, base_path: str = '', prompt_caching: bool = True)
dataclass
¶
El modelo que usa el agente y la pasarela por la que llega a él.
gateway nombra una entrada de sbx.gateways (un SecretGateway ya
aplicado, por ejemplo con bedrock_gateway(...)); si el sandbox no la
tiene, sbx.agent.run() falla con InvalidArgumentException antes de
cualquier RPC. region es obligatoria con provider="bedrock".
base_path sólo se usa con "openai-compatible" ("/v1") y es el
del preset: "/api/v1" con openrouter_gateway, "/openai/v1" con
groq_gateway, "/v1" con mistral_gateway y litellm_gateway, ""
con deepseek_gateway. "openai" va con openai_gateway o
xai_gateway, "google" con gemini_gateway y "azure" con
azure_openai_gateway; esos tres fijan su ruta en el adaptador.
prompt_caching=True deja que el runtime marque puntos de caché
(Bedrock cachePoint, Anthropic cache_control): con dos o más pasos
abarata la entrada repetida.
AgentLimits(max_steps: int = DEFAULT_AGENT_MAX_STEPS, timeout_seconds: float = DEFAULT_AGENT_TIMEOUT_SECONDS, max_output_bytes: int = DEFAULT_AGENT_MAX_OUTPUT_BYTES, max_total_tokens: int | None = DEFAULT_AGENT_MAX_TOTAL_TOKENS)
dataclass
¶
Topes duros de una ejecución, impuestos por el SDK (no por el
runtime): max_steps aborta con max_steps al empezar el paso
max_steps + 1; max_total_tokens se comprueba tras cada paso, así que
puede pasarse en un paso (None lo desactiva); timeout_seconds y
max_output_bytes son los del comando que corre el runtime. Los valores
por defecto acotan el coste de una ejecución (design.md de
ai-agent-core, D4).
AgentPermissions(default: PermissionAction = 'allow', tools: Mapping[str, ToolPermission] = dict())
dataclass
¶
Qué herramientas puede usar el agente. default aplica a toda
herramienta sin entrada propia; tools asigna "allow"/"deny" a una
herramienta ("edit": "deny") o a patrones de su argumento
("bash": {"git *": "allow", "*": "deny"}). "ask" no existe: la
ejecución es desatendida.
No es una frontera de seguridad (SECURITY.md T29): la frontera es la
MicroVM con el egress cerrado. effective_tools() añade
DEFAULT_DENIED_TOOLS por debajo de las entradas del llamante.
effective_tools() -> Mapping[str, ToolPermission]
¶
DEFAULT_DENIED_TOOLS en "deny" y encima las entradas de
tools (una entrada del llamante gana).
SubAgent(description: str, instructions: str, model: str | None = None, permissions: AgentPermissions | None = None)
dataclass
¶
Un subagente que el agente principal puede invocar (OpenCode
mode: subagent, deepagents subagents=). model es un id del mismo
proveedor y pasarela que AgentSpec.model; None hereda el suyo.
McpLocal(command: Sequence[str], envs: Mapping[str, str] = dict(), timeout_seconds: float | None = None)
dataclass
¶
Un servidor MCP que el runtime arranca dentro del sandbox como
proceso hijo (command en forma de lista, sin shell). envs se escribe
en la configuración del runtime, legible por uid 1000: nunca pongas ahí
una credencial, usa una pasarela.
McpRemote(gateway: str, path: str = '/')
dataclass
¶
Un servidor MCP remoto alcanzado a través de la pasarela gateway
(la URL sale de sbx.gateways[gateway].url + path). No hay
headers a propósito: la credencial sólo la pone la pasarela.
McpServer es el alias McpLocal | McpRemote.
DeepAgents(entrypoint: str | None = None)
¶
AgentRuntime de deepagents (runtime=DeepAgents(...) o
runtime="deepagents").
Sin entrypoint, el runner construye create_deep_agent(model,
system_prompt=instructions, subagents, backend=LocalShellBackend(
root_dir=workdir), middleware=ctx.middleware). Con entrypoint=
"pkg.mod:build", importa ese módulo desde el directorio de trabajo y
llama a build(ctx) con un RunnerContext(model, instructions,
subagents, backend, middleware, workdir); debe devolver un grafo
compilado. Si no pasa ctx.middleware a su grafo, se pierden los
permisos de AgentSpec.
El modelo siempre llega por la pasarela (ChatBedrockConverse,
ChatAnthropic o ChatOpenAI con la URL de la pasarela y un marcador
como clave). deepagents marca puntos de caché de prompts para Claude
(Bedrock y Anthropic); AgentModel(prompt_caching=False) lo apaga.
command(request: RunRequest) -> RunCommand
¶
El script (cerrojo, comprobación del runner, exec del Python
del venv) y la petición JSON por stdin.
parse_line(line: bytes, state: RuntimeState) -> Sequence[AgentEvent]
¶
Una línea del protocolo v1 a cero o un evento. Lo que no es JSON, otra versión o un tipo desconocido se ignora y se cuenta.
finish(state: RuntimeState, exit_code: int) -> Done | AgentFailed
¶
Done sólo con salida 0, el done del runner y una sesión.
template_steps() -> Sequence[TemplateStep]
¶
Vacío: la plantilla de agente (ai-agent-fast-start) declara sus
propios pasos.
warmup_steps() -> Sequence[WarmupStep]
¶
Carga deepagents y langchain en la caché de páginas.
Pasarelas del modelo¶
Construyen el SecretGateway que nombra AgentModel.gateway.
bedrock_gateway(secret: str | SecretRef, *, region: str, models: Sequence[str], rate_per_minute: int = 0) -> SecretGateway
¶
Pasarela hacia Bedrock (Converse/ConverseStream) para los
models dados (ids o perfiles de inferencia, como
us.anthropic.claude-haiku-4-5-20251001-v1:0). secret nombra un
secreto con Bearer <clave de API de Bedrock>; una clave de corta
duración se rota con sbx.gateways.refresh().
anthropic_gateway(secret: str | SecretRef, *, rate_per_minute: int = 0) -> SecretGateway
¶
Pasarela hacia la Messages API de Anthropic. secret nombra un
secreto con la clave de API (x-api-key).
openai_compatible_gateway(secret: str | SecretRef, *, upstream: str, base_path: str = '', rate_per_minute: int = 0) -> SecretGateway
¶
Pasarela hacia una API compatible con OpenAI (POST
<base_path>/chat/completions). upstream es https://host;
base_path es el mismo que AgentModel.base_path ("/v1"). secret
nombra un secreto con Bearer <clave>.
Resultado y eventos¶
AgentEvent es el alias de la unión cerrada de eventos que da
agent.stream(): TextDelta | Text | Reasoning | ToolCall | StepStarted |
StepFinished | AgentFailed | Done.
AgentResult(session_id: str, text: str, steps: int, usage: TokenUsage, exit_code: int, tool_calls: tuple[ToolCall, ...] = (), dropped_lines: int = 0)
dataclass
¶
El resultado de sbx.agent.run(). text es el último Text del
asistente; dropped_lines cuenta las líneas del runtime descartadas
(tipo desconocido o más largas que MAX_AGENT_EVENT_LINE_BYTES).
TokenUsage(input: int = 0, output: int = 0, reasoning: int = 0, cache_read: int = 0, cache_write: int = 0)
dataclass
¶
Tokens de un paso o de una ejecución. input excluye los leídos de
caché (cache_read) y los escritos en ella (cache_write), como
Bedrock y Anthropic los facturan por separado; total suma los cinco.
El SDK no da un coste: los precios dependen de región y perfil.
TextDelta(text: str)
dataclass
¶
Un trozo de texto en curso (sólo deepagents; OpenCode emite Text
entero).
Text(text: str)
dataclass
¶
Un bloque de texto completo del asistente.
Reasoning(text: str)
dataclass
¶
Razonamiento del modelo; sólo con run(..., reasoning=True).
ToolCall(call_id: str, name: str, status: ToolCallStatus, input: Mapping[str, object] | None = None, output: str | None = None, output_truncated: bool = False)
dataclass
¶
Una herramienta terminada. output llega recortado a
MAX_TOOL_OUTPUT_PREVIEW_BYTES (output_truncated).
StepStarted(index: int)
dataclass
¶
Empieza el paso index (desde 1): una llamada al modelo.
StepFinished(index: int, usage: TokenUsage, finish_reason: str | None = None)
dataclass
¶
Termina el paso index con sus tokens.
AgentFailed(reason: AgentFailureReason, exit_code: int | None = None, detail_code: str | None = None, session_id: str | None = None)
dataclass
¶
Último evento de una ejecución fallida. reason es de
AGENT_FAILURE_REASONS; detail_code se descarta si no es un
identificador corto.
to_exception(usage: TokenUsage | None = None) -> AgentException
¶
La AgentException que sbx.agent.run() lanza por este evento.
Done(session_id: str, exit_code: int, usage: TokenUsage)
dataclass
¶
Último evento de una ejecución correcta, con los tokens acumulados.
Imagen y arranque rápido¶
AgentTemplate construye una vez la imagen con el runtime
(Templates de agente);
con ella, el arranque normal es Sandbox.create(...) + sbx.agent.run(...).
agent_pool_warmup y los pools calientes son opcionales:
¿Qué uso?.
AgentTemplate(name: str = DEFAULT_AGENT_TEMPLATE_NAME, base: str = DEFAULT_AGENT_TEMPLATE_BASE, runtimes: tuple[str, ...] = AGENT_TEMPLATE_RUNTIMES, prefetch: bool = True, kernel_warmup: bool = False, memory_mib: int = AGENT_MIN_MEMORY_MIB, base_version: str | None = None)
dataclass
¶
Plantilla de imagen con los runtimes del agente de IA.
runtimes elige qué se instala ("opencode", "deepagents");
prefetch=True hornea el demonio que precarga los binarios en la caché
de páginas tras cada restauración del snapshot. kernel_warmup=False
(por defecto) apaga el calentamiento del kernel de run_code (numpy,
pandas, matplotlib, scipy y scikit-learn importados en cada arranque del
kernel, también en la rotación que create() espera): medido en AWS
(AWS_API_NOTES.md Q155), create() vuelve 4,4 s antes (p50) y el
snapshot de memoria pesa 248 MB menos, con el mismo primer token del
agente; a cambio, la primera celda de run_code que importe ese stack
paga la importación. kernel_warmup=True deja el kernel como en la
imagen base. memory_mib por debajo de AGENT_MIN_MEMORY_MIB (2048,
el RSS medido de OpenCode) es InvalidArgumentException.
Coste y activación¶
Activa: AgentTemplate(...).build(bucket=...) o
rayito agent template build.
Recursos y llamadas AWS: los de Template.build (un build de imagen,
s3:PutObject del contexto, una versión de imagen nueva); nada si
no se construye.
Coste aproximado: build de 260-320 s (medido en AWS, 2026-10-07 y
2026-10-09); la versión almacenada ≈ 3,0 GB (código 2,10 + memoria
0,86 + disco 0,03; memoria 0,92 con kernel_warmup=True) por
$0,08/GB-mes, con el mínimo de una semana ≈ $0,056/semana
(≈ $0,24/mes) por versión, y cada lanzamiento lee el snapshot de
memoria (≈ 0,86 GB por $0,00155/GB ≈ $0,0013).
Estimación con precios de lista de Lambda MicroVMs, us-east-1,
consultados 2026-10-06 (https://aws.amazon.com/lambda/pricing/).
IAM: la política RayitoTemplateBuilder (la misma que Template.build).
Cómo apagarla: no la construyas; borra sus versiones con rayito image.
Ejemplo:
AgentTemplate(runtimes=("opencode",)).build(bucket="amzn-s3-demo-bucket")
manifest() -> dict[str, object]
¶
El manifiesto rayito.agent-template/1 que se hornea en
AGENT_TEMPLATE_MANIFEST_PATH: versiones y hashes de lo instalado,
protocolo y las rutas que precarga el demonio.
context_files() -> dict[str, bytes]
¶
Los ficheros del contexto de build (nombre relativo -> bytes).
to_template(template_class: type[Template] = Template) -> Template
¶
El Template equivalente (puro: no lee ni escribe nada).
build(*, bucket: str, force: bool = False, timeout: float = DEFAULT_BUILD_TIMEOUT_SECONDS, on_build_logs: Callable[[str], None] | None = None, region: str | None = None, session: Any = None) -> BuildInfo
¶
Construye la imagen name con Template.build (ver el bloque
de coste de la clase).
AsyncAgentTemplate(name: str = DEFAULT_AGENT_TEMPLATE_NAME, base: str = DEFAULT_AGENT_TEMPLATE_BASE, runtimes: tuple[str, ...] = AGENT_TEMPLATE_RUNTIMES, prefetch: bool = True, kernel_warmup: bool = False, memory_mib: int = AGENT_MIN_MEMORY_MIB, base_version: str | None = None)
dataclass
¶
agent_pool_warmup(runtime: str | AgentRuntime = DEFAULT_AGENT_RUNTIME) -> tuple[WarmupStep, ...]
¶
Los pasos de PoolConfig(warmup=...) para un pool de agentes: cargan
el runtime en la caché de páginas antes de aparcar cada plaza.
Un pool sólo compensa si llegan muchas conversaciones nuevas cuyo
primer mensaje tiene que ser rápido: los turnos de una misma
conversación de menos de 8 h van mejor en una sola VM pausada entre
turnos (pause() y connect(), o la auto-suspensión de IdlePolicy),
y más allá de 8 h, con persist=. Guía "Agente en el sandbox",
"¿Qué uso?".
Coste y activación¶
Activa: PoolConfig(warmup=agent_pool_warmup(...)).
Recursos y llamadas AWS: ninguna llamada nueva; cada plaza corre los
pasos antes de su pause() y su snapshot crece con la caché.
Coste aproximado: ≈ $0,0054 por ciclo de reciclado (≈ 103 al mes) ≈
$0,64/plaza/mes, frente a ≈ $0,60 de una plaza base; cada take()
lee ≈ $0,0014. Tiempos medidos en AWS (AWS_API_NOTES Q147) por
precios de lista, us-east-1, consultados 2026-10-06
(https://aws.amazon.com/lambda/pricing/).
IAM: ninguna además de la del pool.
Cómo apagarla: PoolConfig(warmup=()) (por defecto).
Ejemplo:
PoolConfig(size=2, template="rayito-agent", allow_internet_access=False,
warmup=agent_pool_warmup("opencode"))
WarmupStep(cmd: str, background: bool = False, timeout_seconds: float = DEFAULT_WARMUP_STEP_TIMEOUT_SECONDS, tag: str | None = None)
dataclass
¶
Un paso de calentamiento: agent.prepare() y el warmup= de un pool
(B2) corren la misma lista. background=True no espera a que termine
(un proceso que sigue vivo); si no, se espera su salida con timeout_seconds.
Listado¶
SandboxListPaginator(*, io: ListingIo, request: ListingRequest, resolve_template_arn: Callable[[str], str])
¶
Un listado reanudable (Sandbox.paginate), la forma del
SandboxPaginator de E2B.
next_token es opaco: guárdalo y pásalo a un Sandbox.paginate()
nuevo con los mismos filtros para seguir donde se quedó. No lo loguees:
lleva el ARN de la imagen y la huella de los filtros.
AsyncSandboxListPaginator(*, io: AsyncListingIo, request: ListingRequest, resolve_template_arn: Callable[[str], str])
¶
sandbox_sync.listing.SandboxListPaginator con next_items()
asíncrono. Construirlo no hace E/S.
next_token: str | None
property
¶
Antes de la primera llamada, el token recibido; después, el cursor
mientras has_next, si no None.