Skip to content

Subclients

The objects that hang off a sandbox (sbx.commands, sbx.files, sbx.pty, sbx.git, sbx.agent), their handles and the paginator of Sandbox.paginate(). The async variants (AsyncCommandHandle, AsyncWatchHandle, AsyncUploadTicket, AsyncPtyHandle, AsyncGit, AsyncAgentStream, AsyncSandboxListPaginator) have the same surface with coroutines.

Commands

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).

Files

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
read(
    path: str,
    *,
    format: Literal["bytes"],
    gzip: bool = False,
    stream_idle_timeout: float | None = None,
    user: str | None = None,
    request_timeout: float | None = None,
) -> bytes
read(
    path: str,
    *,
    format: Literal["stream"],
    gzip: bool = False,
    stream_idle_timeout: float | None = None,
    user: str | None = None,
    request_timeout: float | None = None,
) -> Iterator[bytes]

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.

is_running: bool property

False tras stop() o cuando el stream terminó por su cuenta.

get_new_events() -> list[FilesystemEvent]

Eventos no entregados desde la última llamada. Si el stream terminó con error lo levanta una sola vez; tras un stop() limpio devuelve listas vacías.

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)

Bases: AsyncCommandHandle

PtyHandle sobre grpc.aio: async for entrega (None, None, bytes).

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 is the alias Literal["soft", "mixed", "hard", "merge", "keep"] for the mode= of git.reset().

AI agent (sbx.agent)

run() runs the agent to completion (raises AgentException if it fails); stream() returns an iterable AgentStream that never raises on an agent failure; prepare() triggers the runtime warm-up in the background.

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í.

abort() -> None

Aborta la ejecución: para los procesos que lanzó el runtime y mata el proceso. Puede llamarse desde otro hilo mientras otro consume el stream.

result() -> AgentResult

Consume el resto del stream y devuelve su AgentResult. AgentFailed se convierte en AgentException.

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.

Specification

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 | None, gateway: str, region: str | None = None, base_path: str = '', prompt_caching: bool = True, id_env: str | None = None, base_url_env: str | None = None, api_key_env: str | None = None) 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.

id=None es un modelo que elige el código del agente por su configuración (ADR-033 D2): sólo vale con un runtime que no construye el modelo (un entrypoint tuyo) y una pasarela que no filtra por modelo. id_env, base_url_env y api_key_env son los nombres de las variables de entorno en las que tu código lee el id, la URL base y la clave ("LLM_MODEL", "LLM_BASE_URL", "LLM_API_KEY"); nunca valores. En MicroVMs Rayito las apunta a la pasarela y al marcador de la clave.

env_names() -> dict[str, str]

Las claves MODEL_ENV_KEYS que están puestas y el nombre de su variable, en ese orden.

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 is the alias McpLocal | McpRemote.

DeepAgents(entrypoint: str | None = None, *, require_entrypoint: bool = False, requirements: str | None = None, package_manager: 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.

require_entrypoint=True es el runtime registrado como "langgraph": el mismo adaptador, pero sin entrypoint no hay agente que construir y build_config() / host_config() fallan con InvalidArgumentException.

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.

install_steps(target: InstallTarget) -> Sequence[TemplateStep]

Copia los requisitos con hash, crea el venv con --require-hashes, añade las dependencias de tu proyecto (requirements, con pip o uv) sin quitar lo que ya hay y copia el runner (0755).

context_files() -> Mapping[str, bytes]

Los requisitos con hash y el runner (datos de paquete).

manifest_entry() -> Mapping[str, object]

El sha256 de los requisitos y el del runner, para el manifiesto, y los de tu proyecto si los pediste.

smoke_checks() -> Sequence[str]

Importa deepagents y los modelos de cada proveedor, y comprueba que el runner es legible, todo como user.

prefetch_paths() -> Sequence[str]

Ninguna: el venv se carga con warmup_steps().

host_config(spec: AgentSpec, *, wiring: ModelWiring, workdir: str) -> HostConfig

La configuración del host para el driver deepagents: el mismo fichero de configuración y las mismas variables que command(), más las estándar del proveedor apuntadas a la pasarela, para que también el cliente de modelo que crea tu grafo pase por ella.

Model gateways

They build the SecretGateway that AgentModel.gateway names.

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>.

Result and events

AgentEvent is the alias of the closed union of events that agent.stream() yields: 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, usage_reported: bool = True, output: Mapping[str, Any] | None = None) 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). usage_reported es False si el agente no informó de tokens (una app HTTP, un comando o una función propios que no devuelven usage): usage vale entonces cero porque se desconoce, no porque no se gastara nada. output es la salida estructurada (un objeto JSON) de un grafo invocado con input; None en cualquier otro caso.

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, usage_reported: bool = True, output: Mapping[str, Any] | None = None) dataclass

Último evento de una ejecución correcta, con los tokens acumulados. usage_reported es False cuando el agente no informó de tokens (una app HTTP, un comando o una función propios): usage es entonces cero porque se desconoce, no porque no se gastara nada. output es la salida estructurada de un grafo llamado con input (None si no la hay). Es un InitVar guardado como atributo y no un campo: dataclasses.asdict, repr y la igualdad de un Done no cambian (los vectores de eventos ya publicados lo comparan así), y dataclasses.replace lo conserva.

Image and fast start

AgentTemplate builds the image with the runtime once (Agent templates); with it, the normal start is Sandbox.create(...) + sbx.agent.run(...). agent_pool_warmup and warm pools are optional: What do I use?.

AgentTemplate(name: str = DEFAULT_AGENT_TEMPLATE_NAME, base: str = DEFAULT_AGENT_TEMPLATE_BASE, runtimes: tuple[str | AgentRuntime, ...] = AGENT_TEMPLATE_RUNTIMES, prefetch: bool = True, kernel_warmup: bool = False, memory_mib: int = AGENT_MIN_MEMORY_MIB, base_version: str | None = None, app: str | None = None, app_exclude: tuple[str, ...] = ()) dataclass

Plantilla de imagen con los runtimes del agente de IA.

runtimes elige qué se instala: nombres del registro ("opencode", "deepagents", "langgraph"...) u objetos que implementen InstallableRuntime; la imagen se compone con los install_steps() de cada uno. app es un directorio local con el código del agente: se copia a AGENT_APP_DIR (de root, sin escritura para user) con las reglas de su .dockerignore más app_exclude (patrones de .dockerignore). 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")

installables() -> tuple[InstallableRuntime, ...]

Los runtimes de la plantilla, ya resueltos.

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, las rutas que precarga el demonio y, desde ADR-028, la entrada de cada runtime (runtimes) y el sha256 del host del agente (host_sha256).

context_files() -> dict[str, bytes]

Los ficheros del contexto de build (nombre relativo -> bytes). El código del usuario (app) no entra aquí: lo escribe build().

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 | AgentRuntime, ...] = AGENT_TEMPLATE_RUNTIMES, prefetch: bool = True, kernel_warmup: bool = False, memory_mib: int = AGENT_MIN_MEMORY_MIB, base_version: str | None = None, app: str | None = None, app_exclude: tuple[str, ...] = ()) dataclass

Bases: AgentTemplate

AgentTemplate con build en versión async (AsyncTemplate).

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.

Agent runtimes and deployment

The runtimes that AgentTemplate, Agent and AgentProject accept. They install their dependencies with pinned hashes and send the model through the secrets gateway (Deploy your agent).

StrandsRuntime(entrypoint: str | None = None, harness: bool = False, language: StrandsLanguage = 'python', requirements: str | None = None, package_manager: str | None = None, node_version: str | None = None)

Bases: HostBackedRuntime

AgentRuntime de Strands Agents (runtime=StrandsRuntime(...); "strands" y "strands-harness" en el registro).

entrypoint es modulo:atributo en Python (un Agent o una función fn(ctx) -> Agent) y la ruta de un módulo bajo AGENT_APP_DIR en TypeScript (obligatoria, y exporta agent o createAgent(ctx)). harness=True usa create_harness del paquete strands-harness (sólo con language="python"; en TypeScript el harness se importa en tu código). requirements es un fichero de requisitos de tu source (app/<requirements>) que se instala en el venv después de los pines de Rayito (sólo Python). Con language="typescript" el source debe llevar package.json y package-lock.json: el build ejecuta npm ci --ignore-scripts con ellos y falla si faltan.

Coste y activación

Activa: pasar StrandsRuntime(...) o runtime="strands" / "strands-harness" a AgentTemplate, AgentProject o sbx.agent.run. Recursos y llamadas AWS: ninguna por sí solo; la imagen que lo instala se construye con AgentTemplate.build (un build de imagen) y cada ejecución llama al modelo de la pasarela que elijas. Coste aproximado: el del build de la imagen (ver AgentTemplate) más el del modelo; el venv de Strands añade unos 0,3 GB al snapshot (medido en AWS_API_NOTES.md cuando se publique). IAM: la política RayitoTemplateBuilder para construir; ninguna para ejecutar (la pasarela guarda la clave). Cómo apagarla: no incluirlo en runtimes ni en el proyecto. Ejemplo: AgentTemplate(runtimes=(StrandsRuntime(entrypoint="agent:agent"),))

from_options(kind: str, options: Mapping[str, object]) -> StrandsRuntime classmethod

El runtime de la tabla [runtime] de rayito-agent.toml (kind es "strands" o "strands-harness").

to_options() -> dict[str, object]

La tabla [runtime] canónica: kind y sus opciones.

wiring_envs(spec: AgentSpec, urls: Mapping[str, str] | None) -> Mapping[str, str]

Las variables estándar del proveedor apuntadas a la pasarela (ver standard_model_envs).

install_steps(target: InstallTarget) -> Sequence[TemplateStep]

Python: los requisitos con hash, el venv (--require-hashes) y, si hay, los del usuario. TypeScript: Node (sha256 fijado), la instalación congelada de tu lockfile (npm, pnpm o yarn) y el puente.

manifest_entry() -> Mapping[str, object]

Versiones y hashes de lo instalado, para el manifiesto.

smoke_checks() -> Sequence[str]

Importa el SDK (y el harness) como user.

prefetch_paths() -> Sequence[str]

El binario de Node (TypeScript); el venv de Python se carga con warmup_steps().

warmup_steps() -> Sequence[WarmupStep]

Carga Strands (o Node con el SDK) en la caché de páginas.

HermesRuntime(toolsets: Sequence[str] = HERMES_DEFAULT_TOOLSETS, skills_dir: str | None = None)

Bases: HostBackedRuntime

AgentRuntime de Hermes Agent (runtime=HermesRuntime(...) o runtime="hermes").

toolsets son los toolsets de Hermes que se activan (por defecto terminal y ficheros); los que AgentPermissions deniega del todo se quitan. skills_dir es una carpeta de skills de tu source (app/<skills_dir>) que Hermes lee como directorio externo, de sólo lectura.

Coste y activación

Activa: pasar HermesRuntime() o runtime="hermes" a AgentTemplate, AgentProject o sbx.agent.run. Recursos y llamadas AWS: ninguna por sí solo; la imagen que lo instala se construye con AgentTemplate.build (un build de imagen) y cada ejecución llama al modelo de la pasarela que elijas. Coste aproximado: el del build de la imagen (ver AgentTemplate) más el del modelo; Hermes suma unos 0,5 GB (CPython 3.14, venv y fuente), así que la imagen es más grande y arranca más despacio que la de Strands (se mide en AWS_API_NOTES.md al publicar). IAM: la política RayitoTemplateBuilder para construir; ninguna para ejecutar (la pasarela guarda la clave). Cómo apagarla: no incluirlo en runtimes ni en el proyecto. Ejemplo: AgentTemplate(runtimes=(HermesRuntime(),))

from_options(options: Mapping[str, object]) -> HermesRuntime classmethod

El runtime de la tabla [runtime] de rayito-agent.toml.

to_options() -> dict[str, object]

La tabla [runtime] canónica.

host_python() -> str

El host (stdlib) corre con el Python de Hermes: es el único intérprete garantizado en la imagen que ya lo trae.

wiring_envs(spec: AgentSpec, urls: Mapping[str, str] | None) -> Mapping[str, str]

Bedrock y Anthropic por sus variables estándar (el marcador como clave y la pasarela como endpoint); provider: custom lo lleva la configuración, y las variables de OpenAI no se exportan para no confundir la resolución de proveedor de Hermes.

install_steps(target: InstallTarget) -> Sequence[TemplateStep]

CPython 3.14 independiente (sha256 fijado), venv con --require-hashes y el checkout del tag verificado contra su commit y recortado.

manifest_entry() -> Mapping[str, object]

Versión, commit, CPython y hashes de lo instalado.

smoke_checks() -> Sequence[str]

Importa Hermes y su API HTTP como user.

prefetch_paths() -> Sequence[str]

Ninguna: se carga con warmup_steps().

warmup_steps() -> Sequence[WarmupStep]

Carga Hermes en la caché de páginas.

CustomRuntime(install: Sequence[str] = (), requirements: str | None = None, command: Sequence[str] | None = None, entrypoint: str | None = None, app: HttpApp | None = None, protocol: CustomProtocol = DEFAULT_CUSTOM_PROTOCOL, python: str | None = None, package_manager: str | None = None, python_version: str | None = None, node_version: str | None = None, adapter: str | None = None)

Bases: HostBackedRuntime

AgentRuntime del agente que escribes tú (ver el módulo).

install son órdenes de shell que se ejecutan al construir la imagen (como root, antes de copiar tu source), por ejemplo un npm ci o un dnf install. requirements es un fichero de dependencias de tu source (app/<requirements>: requirements.txt o, con package_manager="uv", pyproject.toml o uv.lock): se instala en un venv propio, que pasa a ser el intérprete del host y de tu función. python es el intérprete con el que corre todo (por defecto python3).

package_manager elige el gestor: pip o uv (Python) o npm, pnpm o yarn (Node: source debe llevar package.json y su lockfile, y la instalación es la congelada del gestor). python_version (3.11...) y node_version (22, 24) piden un intérprete distinto del de la imagen: se baja un build con sha256 fijado. adapter (callable, langgraph o vercel-ai) dice cómo el puente de Node ejecuta un entrypoint ruta.ts#exportacion.

Coste y activación

Activa: pasar CustomRuntime(...) a AgentTemplate, AgentProject o sbx.agent.run. Recursos y llamadas AWS: ninguna por sí solo; la imagen que lo instala se construye con AgentTemplate.build (un build de imagen) y cada ejecución llama al modelo de la pasarela que elijas. Coste aproximado: el del build de la imagen (ver AgentTemplate) más el del modelo; lo que añada tu install y tus requisitos. IAM: la política RayitoTemplateBuilder para construir; ninguna para ejecutar (la pasarela guarda la clave). Cómo apagarla: no incluirlo en runtimes ni en el proyecto. Ejemplo: CustomRuntime(entrypoint="agente:responde", requirements="requirements.txt")

from_options(options: Mapping[str, object]) -> CustomRuntime classmethod

El runtime de la tabla [runtime] de rayito-agent.toml.

to_options() -> dict[str, object]

La tabla [runtime] canónica.

venv_bin(install_dir: str = AGENT_INSTALL_DIR) -> str

El bin del venv de requirements: va el primero en el PATH de tu app o tu comando, de modo que python y pip son los suyos.

wiring_envs(spec: AgentSpec, urls: Mapping[str, str] | None) -> Mapping[str, str]

Las variables estándar del proveedor más las RAYITO_MODEL_* (RAYITO_MODEL_ID sólo si se conoce el id).

install_steps(target: InstallTarget) -> Sequence[TemplateStep]

Tus órdenes de install y el toolchain que pidas: con requirements, el venv con tus dependencias; con un gestor de Node, Node y la instalación congelada de tu lockfile (y el puente si hay entrypoint).

context_files() -> Mapping[str, bytes]

El puente de Node si hay un entrypoint de JavaScript; si no, ninguno: tu código y tus requisitos van en source.

manifest_entry() -> Mapping[str, object]

Qué se instaló: el driver, el hash de tus órdenes y tus requisitos, y el gestor y las versiones de intérprete que pediste.

smoke_checks() -> Sequence[str]

Node o el CPython fijado, si los pediste; si no, ninguna: no sabemos qué instalas.

HttpApp(command: Sequence[str], port: int, health_path: str = AGENT_SERVICE_HEALTH_PATH, invoke_path: str = AGENT_SERVICE_INVOKE_PATH, protocol: HttpAppProtocol = DEFAULT_HTTP_APP_PROTOCOL, request_field: str | None = None, response_field: str | None = None, port_env: str | None = None, assistant_id: str | None = None) dataclass

Tu servidor HTTP (CustomRuntime(app=HttpApp(...))): command lo arranca, port es donde escucha (dentro del sandbox), health_path es la ruta que debe contestar 200 cuando está listo e invoke_path la que recibe {"prompt": ...}. protocol es "agentcore" (el contrato de AgentCore), "json" (una app con su propio contrato JSON), "openai-chat" (Chat Completions con streaming) o "raw" (sólo se expone).

Cuándo cada uno: con "agentcore" u "openai-chat" el host sirve /ping y /invocations y traduce a tu invoke_path, así que deployment.invoke() funciona pero tus rutas propias no se alcanzan desde fuera; con "json" el host traduce igual (el prompt va en request_field, por defecto prompt, y el texto sale de response_field, o del primer campo de texto de answer, output, text, response, result o content) y además reenvía tus demás rutas, de modo que invoke() funciona y tu /health o tu /chat siguen alcanzables; con "raw" el despliegue reenvía tus rutas tal cual (un servidor MCP) y invoke() no existe: usa deployment.url y deployment.headers con tu cliente. port_env es la variable de entorno con la que se le dice a tu app en qué port escuchar (por ejemplo PORT).

Con "langgraph-api" (experimental, ADR-033 D5) tu command es langgraph dev y el host traduce cada invocación a POST /threads/{thread_id}/runs/wait del LangGraph Platform API: el hilo sale de la sesión y assistant_id (obligatorio, y sólo con este protocolo) es el grafo de langgraph.json que se ejecuta. La salud es /ok si dejas health_path por defecto, la ruta de invocación la fija el API (invoke_path no se cambia) y el texto sale de response_field o del último mensaje. Sólo /ping e /invocations se exponen: el resto del Platform API no se alcanza desde fuera. Tu command se ejecuta con el directorio del código copiado como directorio de trabajo y en su PYTHONPATH; con requirements, python y pip son los del venv (sin él, python es el intérprete de la imagen).

served_health_path: str property

La ruta de salud a la que llama el host: health_path, o /ok con langgraph-api si se dejó la de por defecto.

served_invoke_path: str property

La ruta de invocación a la que llama el host: invoke_path, o la de una ejecución de un hilo (/threads/{thread_id}/runs/wait) con langgraph-api.

hides_own_routes: bool property

Con protocol="agentcore", "openai-chat" o "langgraph-api" el despliegue sólo expone /ping y /invocations (el host traduce a tu invoke_path): tus rutas propias (health_path, invoke_path distintas de esas dos) no se alcanzan desde fuera. Con protocol="json" o "raw" se exponen tal cual. Con langgraph-api, /ok (la salud del API, la que escribe la detección) no es una ruta propia.

from_mapping(table: Mapping[str, object]) -> HttpApp classmethod

La tabla [runtime.app] de rayito-agent.toml.

to_mapping() -> dict[str, object]

La tabla [runtime.app] canónica (sólo lo que no es el valor por defecto).

Project and deployment

AgentProject builds, runs, deploys or exports an agent described in rayito-agent.toml; AgentDeployment invokes it, reads its DeploymentStatus and stops it. They cost (images and MicroVMs) and do nothing until you call the method.

AgentProject(name: str, runtime: str | AgentRuntime, spec: AgentSpec, gateways: Mapping[str, SecretGateway] = dict(), source: str | None = None, exclude: Sequence[str] = (), requirements: str | None = None, limits: AgentLimits = AgentLimits(), serve: ServeOptions = ServeOptions(), allow_internet_access: bool = False, egress: Sequence[str] | None = None, memory_mib: int = AGENT_MIN_MEMORY_MIB, base: str = DEFAULT_AGENT_TEMPLATE_BASE, template: str | None = None, base_dir: str = '.', scaling: ScalingOptions = ScalingOptions(), env: Mapping[str, str] = dict(), network: NetworkPolicy | None = None, region: str | None = None, bucket: str | None = None, base_image_version: str | None = None, language: str | None = None, framework: str | None = None, versions: RuntimeVersions = RuntimeVersions(), graph: str | None = None, secret_env: Mapping[str, SecretEnvValue] = dict(), secret_env_from: Sequence[SecretEnvSource] = (), gateway_presets: Mapping[str, str] = dict()) dataclass

Un agente descrito una vez: runtime, AgentSpec, pasarelas del modelo, código (source), red e imagen. Construirlo valida todo antes de llamar a nadie: name, que cada pasarela que el spec necesita esté en gateways, serve y la memoria.

runtime es un nombre del registro ("opencode", "deepagents", "langgraph"...) o un objeto de runtime. source es el directorio con el código del agente (se copia a AGENT_APP_DIR con exclude); requirements, un fichero de requisitos suyo. base_dir es el directorio contra el que to_dict() escribe las rutas relativas. template es el nombre de una imagen ya construida (por defecto, name).

Para servir el agente en varios VMs: scaling (ScalingOptions), env (variables no secretas del agente), network (política allow_out/deny_out del sandbox, que necesita rayito-base-caps), region, bucket y base_image_version (los valores por defecto de deploy_agent()), language y framework (lo que la detección o el fichero declaran del proyecto) y versions (RuntimeVersions). graph es el nombre del grafo de langgraph.json que se sirve (sólo con el runtime langgraph); to_dict() lo escribe en lugar del entrypoint.

gateway_presets (pasarela -> preset, p. ej. {"model": "bedrock"}) es lo que el fichero declara en [gateways.<nombre>] preset: lo rellenan load(), from_dict() y detect(), sirve para elegir la variable estándar de --create-secret y to_dict() no lo escribe (cada pasarela ya va en su forma custom), así que el documento canónico no cambia.

secret_env (nombre de variable -> nombre o ARN de un secreto, o un SecretEnvRef) y secret_env_from (SecretEnvSource) ponen valores de Secrets Manager en el entorno del agente al arrancar (ADR-033 D4): el proyecto sólo lleva nombres, nunca valores, y el agente puede leer su entorno (ver SecretEnvRef para el coste).

Coste y activación

Activa: nada; AgentProject(...), AgentProject.load(), from_dict() y to_dict() son puros. Lo que cuesta son build(), run() y deploy(). Recursos y llamadas AWS: ninguno aquí; ni se crea un cliente de AWS. Coste aproximado: ninguno; el de las operaciones está en la documentación de AgentTemplate (build()), de Sandbox.create() (run()) y de deploy(). IAM: ninguna para este valor. Cómo apagarla: no llames a build(), run() ni deploy(). Ejemplo: project = AgentProject.load("./support-bot") project.to_dict()

load(path: str | os.PathLike[str] = '.') -> Self classmethod

Lee rayito-agent.toml o rayito-agent.json de path (un fichero, o un directorio donde se busca el primero de AGENT_PROJECT_FILE_NAMES).

detect(path: str | os.PathLike[str] = '.', overrides: DetectOverrides | None = None, *, region: str | None = None) -> tuple[Self, DetectionReport | None] classmethod

El proyecto de un agente que ya tienes y su DetectionReport (None si un fichero de proyecto completo no necesitó detectar nada), sin ejecutar su código ni llamar a AWS. El fichero de proyecto, si existe, gana clave por clave; lo que calla lo completa la detección (lenguaje, framework, punto de entrada, instalación, proveedor y modelo) y overrides fuerza lo que tú indiques. Si el caso no es claro lanza AgentProjectException con code ambiguous, not_detected o unsupported y la clave o bandera que lo resuelve.

from_dict(data: Mapping[str, object], *, base_dir: str, read_text: Callable[[str], str] | None = None) -> Self classmethod

El proyecto de un documento ya leído. Puro: las rutas relativas se resuelven contra base_dir sin tocar el disco, y instructions_file pasa por read_text (el lector que inyecta load; sin él, esa clave es un error).

to_dict() -> dict[str, object]

La forma canónica del documento, la que from_dict vuelve a leer y que comparten los dos SDK. Omite lo que vale su valor por defecto; el texto de las instrucciones va en línea.

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 del proyecto con AgentTemplate (su runtime, su código y su memoria). Cuesta un build de imagen: ver el bloque "Coste y activación" de AgentTemplate.

run(prompt: str, *, session_id: str | None = None, model: str | None = None, reasoning: bool = False, on_event: Callable[[AgentEvent], None] | None = None, region: str | None = None, session: Any = None) -> AgentResult

Una ejecución del agente en un sandbox efímero: lo crea desde la imagen del proyecto con sus pasarelas y su red, llama a sbx.agent.run() y lo termina siempre, también ante un error. Para que el sandbox sobreviva, usa deploy().

deploy(*, region: str | None = None, session: Any = None, access_token: str | None = None, keep_on_failure: bool = False) -> AgentDeployment

Sirve el agente desde un MicroVM y devuelve su AgentDeployment: crea un sandbox desde la imagen del proyecto (con serve.timeout_seconds, serve.idle y las etiquetas de despliegue), escribe la configuración del host, lo arranca en modo servicio y espera su marcador de listo. Si cualquier paso falla, termina el sandbox antes de lanzar (salvo con keep_on_failure=True).

access_token es el del sandbox (como el de Sandbox.create(); sin él, RAYITO_ACCESS_TOKEN o uno nuevo). Con uno nuevo, el valor está en AgentDeployment.access_token: guárdalo para reconectarte desde otro proceso (AgentDeployment.connect(sandbox_id, access_token=...), rayito agent invoke); sin él sólo Sandbox.kill(sandbox_id) llega al sandbox.

Un arranque fallido lanza SandboxException con el código del fallo y el final de la salida de error de la app, sin valores de entorno ni credenciales. Con keep_on_failure=True el sandbox no se termina (para entrar a depurarlo: el error lleva su id en una nota) y hace falta access_token (o RAYITO_ACCESS_TOKEN) para poder conectarse; termínalo tú con Sandbox.kill(sandbox_id), porque sigue facturando. Por defecto es False.

Coste y activación

Activa: llamar a deploy() (o rayito agent deploy); nada más lo crea. Recursos y llamadas AWS: un sandbox (run-microvm) que vive hasta serve.timeout_seconds (como mucho 8 horas) o hasta AgentDeployment.stop(), y el token del proxy (CreateMicrovmAuthToken, gratuito); el modelo lo llama la pasarela del sandbox. Coste aproximado: el del MicroVM por segundo mientras vive (2 GB: ≈ $0,14 por hora en us-east-1, consultado 2026-10-06 en https://aws.amazon.com/lambda/pricing/; serve.idle lo suspende y la suspensión no factura cómputo) más el del modelo del agente. IAM: los de Sandbox.create() (RayitoSandboxRunner) y, por cada pasarela, secretsmanager:GetSecretValue. Cómo apagarla: AgentDeployment.stop() o rayito agent stop; no llames a deploy() y nada se crea. Ejemplo: deployment = AgentProject.load("./support-bot").deploy() deployment.invoke("Resume el README.").text deployment.stop()

export(*, target: str = 'docker', out_dir: str | os.PathLike[str], overwrite: bool = False) -> AgentExport

Escribe en out_dir un contexto de build de contenedor linux/arm64 (target="docker" o "agentcore") sin ninguna llamada de red ni de AWS. Falla si out_dir no está vacío, salvo con overwrite=True. Fuera de Rayito no hay pasarela de secretos: la credencial del modelo la pone la plataforma.

AsyncAgentProject(name: str, runtime: str | AgentRuntime, spec: AgentSpec, gateways: Mapping[str, SecretGateway] = dict(), source: str | None = None, exclude: Sequence[str] = (), requirements: str | None = None, limits: AgentLimits = AgentLimits(), serve: ServeOptions = ServeOptions(), allow_internet_access: bool = False, egress: Sequence[str] | None = None, memory_mib: int = AGENT_MIN_MEMORY_MIB, base: str = DEFAULT_AGENT_TEMPLATE_BASE, template: str | None = None, base_dir: str = '.', scaling: ScalingOptions = ScalingOptions(), env: Mapping[str, str] = dict(), network: NetworkPolicy | None = None, region: str | None = None, bucket: str | None = None, base_image_version: str | None = None, language: str | None = None, framework: str | None = None, versions: RuntimeVersions = RuntimeVersions(), graph: str | None = None, secret_env: Mapping[str, SecretEnvValue] = dict(), secret_env_from: Sequence[SecretEnvSource] = (), gateway_presets: Mapping[str, str] = dict()) dataclass

Bases: AgentProject

AgentProject con las operaciones async: el mismo constructor y la misma superficie (build, run, deploy, export) como corrutinas.

deploy(*, region: str | None = None, session: Any = None, access_token: str | None = None, keep_on_failure: bool = False) -> AsyncAgentDeployment async

Como AgentProject.deploy(), con AsyncAgentDeployment. Mismo bloque de coste: crea un sandbox que vive hasta stop() o hasta serve.timeout_seconds.

Coste y activación

Activa: await deploy() (o rayito agent deploy); nada más lo crea. Recursos y llamadas AWS: un sandbox (run-microvm) que vive hasta serve.timeout_seconds (como mucho 8 horas) o hasta await AsyncAgentDeployment.stop(), y el token del proxy (CreateMicrovmAuthToken, gratuito); el modelo lo llama la pasarela del sandbox. Coste aproximado: el del MicroVM por segundo mientras vive (2 GB: ≈ $0,14 por hora en us-east-1, consultado 2026-10-06 en https://aws.amazon.com/lambda/pricing/) más el del modelo. IAM: los de AsyncSandbox.create() y, por cada pasarela, secretsmanager:GetSecretValue. Cómo apagarla: await deployment.stop(); no llames a deploy() y nada se crea. Ejemplo: deployment = await AsyncAgentProject.load("./support-bot").deploy() (await deployment.invoke("Resume el README.")).text await deployment.stop()

export(*, target: str = 'docker', out_dir: str | os.PathLike[str], overwrite: bool = False) -> AgentExport async

Como AgentProject.export(); la escritura de ficheros corre en un hilo para no bloquear el bucle de eventos.

ServeOptions(port: int = AGENT_SERVICE_PORT, timeout_seconds: float = DEFAULT_AGENT_DEPLOY_TIMEOUT_SECONDS, idle: IdlePolicy | None = DEFAULT_IDLE_POLICY, max_concurrency: int = AGENT_SERVICE_MAX_CONCURRENCY, metadata: Mapping[str, str] = dict(), queue_max: int = AGENT_SERVICE_QUEUE_MAX_DEFAULT, queue_timeout_seconds: float = AGENT_SERVICE_QUEUE_TIMEOUT_SECONDS_DEFAULT) dataclass

Cómo se sirve el agente (AgentProject.deploy()): el port del servicio (no 8080 ni 9000, que son de rayd), la vida del sandbox (timeout_seconds, como mucho el máximo de la plataforma), la política de idle (suspensión y reanudación automáticas; None la quita), la concurrencia por VM (max_concurrency, acotada por las conexiones que admite el MicroVM de AgentProject.memory_mib), las etiquetas metadata del sandbox y la cola del host: queue_max llamadas esperan hasta queue_timeout_seconds cuando el VM está lleno (con queue_max=0, el valor por defecto, la llamada que no cabe recibe 429 enseguida).

AgentExport(path: str, files: tuple[str, ...], port: int, platform: str = EXPORT_PLATFORM) dataclass

Lo que export() escribió: el directorio, los ficheros (rutas relativas, ordenadas), el puerto en el que sirve el contenedor y su plataforma.

AgentDeployment(sandbox: Any, *, port: int, project: str | None = None, raw: bool = False, request_timeout: float = DEFAULT_DEPLOYMENT_REQUEST_TIMEOUT_SECONDS, transport_factory: Any = default_transport)

Un agente servido desde un MicroVM (AgentProject.deploy()).

url y headers son lo que necesita cualquier cliente HTTP: las cabeceras llevan el JWE del proxy de AWS y se leen frescas en cada acceso (el token vive 60 minutos y el SDK lo renueva). invoke() y stream() hablan rayito.agent-service/1; status() lee GET /ping; stop() termina el sandbox. Un despliegue vive como mucho el timeout del sandbox (8 horas como máximo): para más tiempo, vuelve a llamar a deploy() y reconecta con AgentDeployment.connect(sandbox_id).

Coste y activación

Activa: crear el cliente (connect() o el valor de deploy()) no llama a nadie; cada invoke(), stream() o status() es una petición HTTP a tu sandbox. Recursos y llamadas AWS: ninguno nuevo; el sandbox ya lo creó deploy(). Un sandbox suspendido se reanuda con la primera petición (factura cómputo, como cualquier reanudación) y el modelo lo paga la pasarela del sandbox, no este cliente. Coste aproximado: el del sandbox mientras vive, más el del modelo (ver AgentProject.deploy()). IAM: GetMicrovm y CreateMicrovmAuthToken para connect() y para el token del proxy; nada más. Cómo apagarla: stop() termina el sandbox y con él el despliegue; pause() deja de facturar cómputo sin perder el servicio. Ejemplo: deployment = AgentDeployment.connect(sandbox_id) deployment.invoke("Resume el README.").text deployment.stop()

access_token: str property

El access token del sandbox del despliegue. Con el que pasaste a deploy(access_token=...) o, si no pasaste ninguno, el que se generó: guárdalo (nunca lo imprimas ni lo versiones) para reconectarte desde otro proceso con AgentDeployment.connect(sandbox_id, access_token=...) o con rayito agent invoke --token-file.

connect(sandbox_id: str, **sandbox_options: Any) -> Self classmethod

Reconstruye el cliente de un despliegue desde las etiquetas de su sandbox, en cualquier proceso. sandbox_options son los de Sandbox.connect (region, session...).

invoke(prompt: str | None = None, session_id: str | None = None, model: str | None = None, reasoning: bool = False, *, input: Mapping[str, Any] | None = None) -> AgentResult

Una invocación sin streaming: el AgentResult, o AgentException si el agente falló (busy con el cupo lleno). Con input (un objeto JSON, en lugar de prompt) se invoca un grafo con esquema de entrada propio y result.output trae su salida.

stream(prompt: str | None = None, session_id: str | None = None, model: str | None = None, reasoning: bool = False, *, input: Mapping[str, Any] | None = None) -> AgentDeploymentStream

Como invoke(), pero con los eventos a medida que llegan: with deployment.stream("hola") as events: for event in events.

status() -> DeploymentStatus

GET /ping: healthy, busy o unavailable.

pause(*, wait: bool = True) -> bool

Suspende el sandbox del despliegue (Sandbox.pause()): mientras duerme no factura cómputo y el servicio conserva su estado. False si ya estaba suspendido. La siguiente petición (o resume()) lo reanuda; con idle (activo por defecto) también se suspende solo.

resume(*, wait: bool = True) -> None

Reanuda el sandbox suspendido (Sandbox.resume()) y, con wait, espera a que esté listo. No hace falta antes de invoke(): una petición a un sandbox suspendido ya lo reanuda.

set_timeout(timeout: int) -> None

Fija la vida restante del sandbox del despliegue (Sandbox.set_timeout, segundos enteros): rayd lo termina solo al vencer. Una flota lo usa para que un VM sobrante desaparezca sin servidor propio.

refresh_gateways() -> None

Vuelve a mandar las pasarelas del sandbox (sbx.gateways.refresh()): releen de Secrets Manager el valor actual de cada secreto. Una flota lo usa tras renovar una clave (renew_gateway_keys).

stop() -> None

Termina el sandbox del despliegue. Idempotente.

AgentDeploymentStream(response: DeploymentResponse)

Iterable de AgentEvent sobre la respuesta SSE de una invocación. El último evento es Done o AgentFailed; nunca lanza por un fallo del agente (result() sí).

result() -> AgentResult

Consume lo que falte y devuelve el AgentResult; si el agente falló lanza AgentException.

AsyncAgentDeployment(sandbox: Any, *, port: int, host_access: HostAccess, project: str | None = None, raw: bool = False, request_timeout: float = DEFAULT_DEPLOYMENT_REQUEST_TIMEOUT_SECONDS, transport_factory: Any = default_async_transport)

El espejo asíncrono de AgentDeployment: las mismas operaciones como corrutinas (invoke, status, stop, connect), stream() como contexto asíncrono y url/headers como propiedades.

Coste y activación

Activa: crear el cliente (connect() o el valor de deploy()) no llama a nadie salvo a GetMicrovm y CreateMicrovmAuthToken para el token del proxy; cada invoke(), stream() o status() es una petición HTTP a tu sandbox. Recursos y llamadas AWS: ninguno nuevo; el sandbox ya lo creó deploy(). Un sandbox suspendido se reanuda con la primera petición (factura cómputo, como cualquier reanudación) y el modelo lo paga la pasarela del sandbox, no este cliente. Coste aproximado: el del sandbox mientras vive, más el del modelo (ver AsyncAgentProject.deploy()). IAM: GetMicrovm y CreateMicrovmAuthToken; nada más. Cómo apagarla: await stop() termina el sandbox y con él el despliegue; await pause() deja de facturar cómputo sin perder el servicio. Ejemplo: deployment = await AsyncAgentDeployment.connect(sandbox_id) (await deployment.invoke("Resume el README.")).text await deployment.stop()

access_token: str property

El access token del sandbox del despliegue. Con el que pasaste a deploy(access_token=...) o, si no pasaste ninguno, el que se generó: guárdalo (nunca lo imprimas ni lo versiones) para reconectarte desde otro proceso con AsyncAgentDeployment.connect(sandbox_id, access_token=...) o con rayito agent invoke --token-file.

connect(sandbox_id: str, **sandbox_options: Any) -> Self async classmethod

Reconstruye el cliente de un despliegue desde las etiquetas de su sandbox, en cualquier proceso. sandbox_options son los de AsyncSandbox.connect (region, session...).

invoke(prompt: str | None = None, session_id: str | None = None, model: str | None = None, reasoning: bool = False, *, input: Mapping[str, Any] | None = None) -> AgentResult async

Una invocación sin streaming: el AgentResult, o AgentException si el agente falló (busy con el cupo lleno). Con input (un objeto JSON, en lugar de prompt) se invoca un grafo con esquema de entrada propio y result.output trae su salida.

stream(prompt: str | None = None, session_id: str | None = None, model: str | None = None, reasoning: bool = False, *, input: Mapping[str, Any] | None = None) -> AsyncAgentDeploymentStream

Como invoke(), pero con los eventos a medida que llegan: async with deployment.stream("hola") as events: async for event in events.

status() -> DeploymentStatus async

GET /ping: healthy, busy o unavailable.

pause(*, wait: bool = True) -> bool async

Suspende el sandbox del despliegue (AsyncSandbox.pause()): mientras duerme no factura cómputo y el servicio conserva su estado. False si ya estaba suspendido. La siguiente petición (o resume()) lo reanuda; con idle (activo por defecto) también se suspende solo.

resume(*, wait: bool = True) -> None async

Reanuda el sandbox suspendido (AsyncSandbox.resume()) y, con wait, espera a que esté listo. No hace falta antes de invoke(): una petición a un sandbox suspendido ya lo reanuda.

set_timeout(timeout: int) -> None async

Fija la vida restante del sandbox del despliegue (AsyncSandbox.set_timeout, segundos enteros): rayd lo termina solo al vencer. Una flota lo usa para que un VM sobrante desaparezca sin servidor propio.

refresh_gateways() -> None async

Como AgentDeployment.refresh_gateways(): vuelve a mandar las pasarelas del sandbox (sbx.gateways.arefresh()).

stop() -> None async

Termina el sandbox del despliegue. Idempotente.

AsyncAgentDeploymentStream(open_response: Callable[[], Awaitable[AsyncDeploymentResponse]])

Iterable asíncrono de AgentEvent: async with deployment.stream(...) as events: async for event in events. La petición se hace al entrar. El último evento es Done o AgentFailed; nunca lanza por un fallo del agente (result() sí).

result() -> AgentResult async

Consume lo que falte y devuelve el AgentResult; si el agente falló lanza AgentException.

DeploymentStatus(state: DeploymentState, ready: bool, busy: bool, port: int, project: str | None) dataclass

El estado de un despliegue según GET /ping: healthy (listo), busy (con el cupo de ejecuciones lleno) o unavailable (el host no contesta o aún no está listo).

Zero-config deploy and fleets

deploy_agent() detects an agent project you already have, deploys it and returns an AgentFleet: several MicroVMs of the same image with the sessions spread across them (Deploy your agent, Several sessions and several VMs). They cost (images and MicroVMs) and do nothing until you call them; plan_deploy() and AgentProject.detect() make no AWS call.

deploy_agent(path: str | os.PathLike[str] = '.', *, project: AgentProject | None = None, options: AgentDeployOptions | None = None, overrides: DetectOverrides | None = None, region: str | None = None, session: Any = None, target: Literal['microvm', 'agentcore'] = 'microvm', agentcore: AgentCoreOptions | None = None) -> AgentFleet | AgentCoreFleet

deploy_agent(
    path: str | os.PathLike[str] = ...,
    *,
    project: AgentProject | None = ...,
    options: AgentDeployOptions | None = ...,
    overrides: DetectOverrides | None = ...,
    region: str | None = ...,
    session: Any = ...,
    target: Literal["microvm"] = ...,
    agentcore: None = ...,
) -> AgentFleet
deploy_agent(
    path: str | os.PathLike[str] = ...,
    *,
    project: AgentProject | None = ...,
    options: AgentDeployOptions | None = ...,
    overrides: DetectOverrides | None = ...,
    region: str | None = ...,
    session: Any = ...,
    target: Literal["agentcore"],
    agentcore: AgentCoreOptions | None = ...,
) -> AgentCoreFleet

Despliega el agente de path y devuelve su AgentFleet; con target="agentcore", su AgentCoreFleet.

path es un directorio con el proyecto (con o sin rayito-agent.toml): el lenguaje, el framework, el punto de entrada y la instalación se detectan de sus ficheros sin ejecutar su código, y todo lo detectado se puede fijar con overrides o con el fichero. Un project explícito se usa tal cual. options (AgentDeployOptions) dice dónde y cómo; region y session son los de boto3.

El orden de cada valor es argumento explícito, fichero de proyecto, variable de entorno y descubrimiento; ninguno crea nada en silencio: Rayito no crea buckets, un secreto sólo se crea con create_secret_from_env=True y una imagen base ausente sólo se publica si pasas base_publisher. La imagen del agente se construye sólo si cambió su contenido. El access token de la flota es options.access_token, o RAYITO_ACCESS_TOKEN, o uno nuevo en AgentFleet.access_token.

Con target="agentcore" (y agentcore=AgentCoreOptions(...) para los modelos y secretos del rol, los plazos de sesión y las etiquetas) el agente se despliega en Amazon Bedrock AgentCore Runtime sin Docker local: una pila de CloudFormation por agente (rayito-agentcore-<nombre>), la imagen arm64 construida en CodeBuild sólo si cambió el contenido y un runtime creado o actualizado por nombre, con MMDSv2 exigido. No hay pasarela de secretos: el agente lee las credenciales del rol del runtime, que sólo puede invocar los modelos y leer los secretos que le des. Las opciones de MicroVMs que AgentCore no admite (scaling, secret_names, access_token...) son AgentCoreException con código option_not_supported.

Coste y activación

Activa: llamar a deploy_agent() (o rayito agent deploy); nada más lo crea. plan_deploy() (--dry-run) calcula el coste sin llamar a AWS. Con target="agentcore", sólo ese argumento crea nada de AgentCore, CodeBuild o ECR; sin él no se crea ni se carga ningún cliente de esos servicios. Recursos y llamadas AWS: min_vms MicroVMs (run-microvm) y warm_vms plazas aparcadas; una versión de imagen nueva si cambió el contenido (create/update-microvm-image, un objeto en S3); GetCallerIdentity, HeadBucket, GetMicrovmImage y DescribeSecret de lectura; con create_secret_from_env=True, CreateSecret; con base_publisher, una imagen base; con session_index, DynamoDB; tras arrancar la flota, la receta en Parameter Store (PutParameter de un SecureString estándar, salvo con save_recipe=False); con renew_gateway_keys, DescribeSecret y PutSecretValue sobre el secreto de cada pasarela bedrock. Con target="agentcore": la pila rayito-agentcore-<nombre> (repositorio de ECR, proyecto de CodeBuild con su grupo de logs y dos roles), un zip del contexto de build en el bucket de artefactos mientras dura el build, un build de CodeBuild si cambió el contenido, y un runtime de AgentCore (CreateAgentRuntime y UpdateAgentRuntime); de lectura, GetCallerIdentity, HeadBucket, DescribeStacks, DescribeImages, ListAgentRuntimes y GetAgentRuntime. Coste aproximado: por VM, el del MicroVM mientras vive (2 GB: ≈ $0,13 por hora en us-east-1, consultado 2026-10-06 en https://aws.amazon.com/lambda/pricing/); la imagen nueva, ≈ 3,0 GB de snapshot (≈ $0,011 de escritura y ≈ $0,056 por semana mínima de almacenamiento); un secreto, $0,40 al mes (https://aws.amazon.com/secrets-manager/pricing/, consultado 2026-09-30); el modelo lo cobra su proveedor. Con target="agentcore" (us-east-1, consultado 2026-10-19): CodeBuild arm1.small $0,0034 por minuto de build (100 minutos gratis al mes, https://aws.amazon.com/codebuild/pricing/), imágenes en ECR $0,10 por GB-mes, logs $0,50 por GB ingerido (https://aws.amazon.com/cloudwatch/pricing/) y el runtime, V1, $0,0895 por vCPU-hora y $0,00945 por GB-hora de uso real (https://aws.amazon.com/bedrock/agentcore/pricing/); sin sesiones no hay cómputo. IAM: los de Sandbox.create(), Sandbox.list() y AgentTemplate.build() (RayitoSandboxRunner, RayitoTemplateBuilder), secretsmanager:DescribeSecret y, con create_secret_from_env, secretsmanager:CreateSecret, sts:GetCallerIdentity y s3:ListBucket sobre el bucket del artefacto; ssm:PutParameter sobre parameter/rayito/agents/* para la receta (sin él, un aviso recipe_access_denied y el despliegue sigue); con renew_gateway_keys, secretsmanager:PutSecretValue y bedrock:CallWithBearerToken. Con target="agentcore": CloudFormation sobre rayito-agentcore-* con CAPABILITY_IAM, ECR, CodeBuild, S3 sobre rayito/images/agentcore/*, AgentCore (CreateAgentRuntime, UpdateAgentRuntime, GetAgentRuntime, ListAgentRuntimes) e iam:PassRole del rol del runtime. Cómo apagarla: AgentFleet.stop() termina los VMs (y borra la receta); los valores por defecto de AgentDeployOptions no crean ningún secreto, imagen base ni índice; save_recipe=False no guarda la receta; sin llamar a deploy_agent() nada se crea. Un agente de AgentCore se borra con AgentCoreFleet.delete() (rayito agent delete --target agentcore). Ejemplo: options = AgentDeployOptions(bucket="amzn-s3-demo-bucket") fleet = deploy_agent("./mi-agente", options=options) fleet.invoke("Resume el README.").text fleet.stop()

deploy_agent_async(path: str | os.PathLike[str] = '.', *, project: AgentProject | None = None, options: AgentDeployOptions | None = None, overrides: DetectOverrides | None = None, region: str | None = None, session: Any = None, target: Literal['microvm', 'agentcore'] = 'microvm', agentcore: AgentCoreOptions | None = None) -> AsyncAgentFleet | AsyncAgentCoreFleet async

deploy_agent_async(
    path: str | os.PathLike[str] = ...,
    *,
    project: AgentProject | None = ...,
    options: AgentDeployOptions | None = ...,
    overrides: DetectOverrides | None = ...,
    region: str | None = ...,
    session: Any = ...,
    target: Literal["microvm"] = ...,
    agentcore: None = ...,
) -> AsyncAgentFleet
deploy_agent_async(
    path: str | os.PathLike[str] = ...,
    *,
    project: AgentProject | None = ...,
    options: AgentDeployOptions | None = ...,
    overrides: DetectOverrides | None = ...,
    region: str | None = ...,
    session: Any = ...,
    target: Literal["agentcore"],
    agentcore: AgentCoreOptions | None = ...,
) -> AsyncAgentCoreFleet

Como deploy_agent(), con AsyncAgentFleet (o AsyncAgentCoreFleet con target="agentcore"): mismas opciones, mismo orden de resolución y mismo coste. Las lecturas de AWS previas a crear nada corren en un hilo para no bloquear el bucle de eventos.

Coste y activación

Activa: await deploy_agent_async(); nada más lo crea. Recursos y llamadas AWS: los de deploy_agent(). Coste aproximado: el de deploy_agent(), por VM (2 GB: ≈ $0,13 por hora en us-east-1, consultado 2026-10-06 en https://aws.amazon.com/lambda/pricing/); con target="agentcore", el de AgentCore (V1: $0,0895 por vCPU-hora y $0,00945 por GB-hora de uso real, us-east-1, consultado 2026-10-19 en https://aws.amazon.com/bedrock/agentcore/pricing/) más CodeBuild, ECR y logs como se detalla en deploy_agent(). IAM: los de deploy_agent(). Cómo apagarla: await fleet.stop() (en AgentCore, await fleet.delete()); sin llamar a esta función nada se crea. Ejemplo: fleet = await deploy_agent_async("./mi-agente") (await fleet.invoke("Resume el README.")).text await fleet.stop()

plan_deploy(path: str | os.PathLike[str] = '.', *, project: AgentProject | None = None, options: AgentDeployOptions | None = None, overrides: DetectOverrides | None = None, region: str | None = None, session: Any = None, target: Literal['microvm', 'agentcore'] = 'microvm', agentcore: AgentCoreOptions | None = None) -> AgentDeployPlan | AgentCoreDeployPlan

plan_deploy(
    path: str | os.PathLike[str] = ...,
    *,
    project: AgentProject | None = ...,
    options: AgentDeployOptions | None = ...,
    overrides: DetectOverrides | None = ...,
    region: str | None = ...,
    session: Any = ...,
    target: Literal["microvm"] = ...,
    agentcore: None = ...,
) -> AgentDeployPlan
plan_deploy(
    path: str | os.PathLike[str] = ...,
    *,
    project: AgentProject | None = ...,
    options: AgentDeployOptions | None = ...,
    overrides: DetectOverrides | None = ...,
    region: str | None = ...,
    session: Any = ...,
    target: Literal["agentcore"],
    agentcore: AgentCoreOptions | None = ...,
) -> AgentCoreDeployPlan

Lo que deploy_agent() haría con estos argumentos, sin llamar a AWS: detecta el proyecto (lee ficheros locales, no ejecuta su código), y calcula el escalado, el origen de cada valor y el coste de lista. Con target="agentcore" devuelve un AgentCoreDeployPlan: valida las opciones, deriva los nombres de los recursos y dice qué rol tendrá el runtime y qué cuesta; agentcore= sólo vale con ese destino.

Coste y activación

Activa: llamar a plan_deploy() (o rayito agent deploy --dry-run). Recursos y llamadas AWS: ninguno; no crea clientes de AWS ni carga ningún paquete opcional. Coste aproximado: ninguno. IAM: ninguna. Cómo apagarla: no la llamo. Ejemplo: plan = plan_deploy("./mi-agente") plan.cost.running_usd_per_hour

AgentDeployOptions(bucket: str | None = None, base_image_version: str | None = None, secret_names: Mapping[str, str] = dict(), create_secret_from_env: bool = False, ensure_base_image: bool = True, base_publisher: Callable[[], None] | None = None, access_token: str | None = None, keep_on_failure: bool = False, build_image: bool | None = None, scaling: ScalingOptions | None = None, session_index: DynamoDbIndex | None = None, pool_backend: PoolBackend | None = None, on_build_logs: Callable[[str], None] | None = None, on_step: Callable[[AgentDeployStep], None] | None = None, build_timeout_seconds: float = DEFAULT_BUILD_TIMEOUT_SECONDS, warm_timeout_seconds: float = AGENT_FLEET_WARM_READY_TIMEOUT_SECONDS, create_secrets_from: str | None = None, check_env: bool = False, save_recipe: bool = True, scale_in_after_seconds: float | None = None, renew_gateway_keys: bool = False) dataclass

Dónde y cómo se despliega (los argumentos de deploy_agent() que no describen al agente): bucket del artefacto de la imagen, base_image_version (None = la más reciente de la base), secret_names (pasarela → nombre de un secreto que ya existe), create_secret_from_env (crea el que falte desde la variable estándar del proveedor), ensure_base_image y base_publisher (quién publica la base si falta), access_token, keep_on_failure, build_image (None = construir si hay bucket; True = siempre; False = reutilizar la imagen existente), scaling (gana al del proyecto), session_index (índice de sesiones de los VMs dedicados), pool_backend (plazas calientes que sobreviven al proceso), on_build_logs, on_step (recibe cada AgentDeployStep), build_timeout_seconds y warm_timeout_seconds (espera a que las plazas calientes queden aparcadas), create_secrets_from (un .env cuyos valores van a un secreto JSON rayito/agents/<nombre>/env que el agente recibe como secret_env_from) y check_env (avisa de las variables de .env.example que no reciben valor).

save_recipe (por defecto True) guarda la receta de la flota en Parameter Store tras arrancarla, para que AgentFleet.connect(nombre) cree VMs iguales desde otro proceso; scale_in_after_seconds fija tras cuántos segundos sin uso se termina un VM por encima de min_vms, y renew_gateway_keys (por defecto False) renueva la clave de corta duración de Bedrock de las pasarelas bedrock antes de crear cada VM.

Coste y activación

Activa: nada por sí mismo; el valor es puro. deploy_agent() es lo que cuesta: create_secret_from_env=True crea un secreto, create_secrets_from crea o actualiza el secreto de un .env (CreateSecret o PutSecretValue, $0,40 al mes por secreto), base_publisher una imagen base, session_index filas de DynamoDB. save_recipe=True (por defecto, la única excepción de ADR-034) guarda un parámetro SecureString estándar; renew_gateway_keys escribe la clave en el secreto de la pasarela. Recursos y llamadas AWS: ninguno aquí. Con save_recipe, un PutParameter tras arrancar la flota (ver SsmRecipeStore); con renew_gateway_keys, DescribeSecret y PutSecretValue (ver GatewayKeyRenewer). Coste aproximado: ninguno aquí; ver deploy_agent(). La receta: el parámetro estándar no tiene cargo y su petición de KMS entra en las 20 000 gratuitas al mes (después $0,03 por 10 000; https://aws.amazon.com/kms/pricing/, consultado 2026-10-22). IAM: ninguna para este valor. Guardar la receta: ssm:PutParameter sobre parameter/rayito/agents/*. Cómo apagarla: los valores por defecto no crean ningún secreto, imagen base ni índice; check_env sólo lee ficheros locales; save_recipe=False (--no-save-recipe) no guarda la receta. Ejemplo: AgentDeployOptions(bucket="amzn-s3-demo-bucket", secret_names={"bedrock": "mi-clave"})

AgentDeployPlan(name: str, template: str, base: str, memory_mib: int, region: str | None, scaling: ScalingOptions, bucket_source: str, build_image: bool | None, secrets: tuple[AgentPlannedSecret, ...], token_source: str, cost: AgentDeployCostEstimate, notes: tuple[str, ...], detection: object | None = None, recipe: RecipePlan = RECIPE_PLAN_SAVE) dataclass

Lo que deploy_agent() haría, calculado sin llamar a AWS: el agente, el escalado, la región (la del entorno, si la hay), de dónde saldrá el bucket, la imagen base, los secretos, el coste y las notas.

AgentDeployStep(kind: str, detail: str) dataclass

Un paso de deploy_agent() que ya ocurrió: kind es de una lista cerrada (base_image, secret_created, bucket, image, fleet, warm, recipe) y detail un dato corto para mostrar (la pasarela, el origen y el bucket, la versión de la imagen, el código del aviso de una receta que no se guardó); nunca el valor ni el nombre de un secreto. Con target="agentcore", kind es la clave de la línea de progreso (stackDeploy, buildStart, runtimeReady...) y detail la línea ya escrita.

AgentDeployCostEstimate(running_usd_per_hour: float, parked_usd_per_day: float, image_build_usd: float, image_storage_usd_per_week: float) dataclass

Lo que cuesta lo que deploy_agent() crea, en dólares, a precios de lista (limits.json): running_usd_per_hour por los VMs min_vms mientras viven, parked_usd_per_day por las plazas aparcadas (sólo almacenamiento de snapshot), image_build_usd por una versión nueva de la imagen y image_storage_usd_per_week por conservarla (mínimo una semana). No incluye los tokens del modelo.

ScalingOptions(min_vms: int = AGENT_SCALING_MIN_VMS_DEFAULT, max_vms: int = AGENT_SCALING_MAX_VMS_DEFAULT, warm_vms: int = AGENT_SCALING_WARM_VMS_DEFAULT, sessions: SessionMode = DEFAULT_SESSION_MODE, scale_in_after_seconds: float = AGENT_SCALE_IN_AFTER_SECONDS_DEFAULT) dataclass

Cuántos MicroVMs sirven el agente y cómo se reparten las sesiones. min_vms corren siempre, warm_vms esperan suspendidos (una sesión nueva los reanuda en vez de crear un VM) y max_vms acota el total; con sessions="dedicated" cada sesión tiene su propio MicroVM. Los VMs por encima de min_vms se terminan tras scale_in_after_seconds sin uso.

Los valores por defecto son los de un solo VM: nada cambia para quien no pasa ScalingOptions.

Coste y activación

Activa: deploy_agent() (rayito agent deploy) con max_vms mayor que 1, o con warm_vms mayor que 0; por sí sola, esta clase no crea nada. Recursos y llamadas AWS: min_vms MicroVMs en marcha (run-microvm), warm_vms plazas suspendidas de un pool y los VMs extra que cree el reparto hasta max_vms. Coste aproximado: el del MicroVM por segundo mientras vive (2 GB: ≈ $0,126 por hora en us-east-1, consultado 2026-10-06 en https://aws.amazon.com/lambda/pricing/), por cada VM; una plaza aparcada o un VM suspendido sólo pagan el almacenamiento de su snapshot. IAM: los de Sandbox.create(), Sandbox.list() y Sandbox.connect(). Cómo apagarla: los valores por defecto (min_vms=1, max_vms=1, warm_vms=0) son un solo VM; fleet.stop() termina todos los VMs. Ejemplo: ScalingOptions(min_vms=1, max_vms=3, warm_vms=1)

routes_by_handle: bool property

Con más de un VM posible, AgentResult.session_id es un asa que nombra el VM (rs1.<sandbox_id>.<session_id>); con uno solo, el id de sesión queda como siempre.

DetectOverrides(framework: str | None = None, language: str | None = None, entrypoint: str | None = None, protocol: str | None = None, model_id: str | None = None, model_provider: str | None = None, model_base_url: str | None = None, graph: str | None = None) dataclass

Lo que el usuario fuerza por encima de la detección (--framework, --language, --entry, --protocol, --model-id, --model-provider, --model-base-url, --graph). entrypoint="http" sirve la app HTTP del proyecto en vez del framework del agente. model_provider es un preset de pasarela (openai-compatible, litellm...) y model_base_url la URL https de la pasarela de LLM, que pasa a ser su upstream. graph elige un grafo de langgraph.json por su nombre.

AgentFleet(project: AgentProject | None, *, name: str | None = None, access_token: str | None = None, scaling: ScalingOptions | None = None, service: ProjectService | None = None, directory: Any = None, pool_factory: Callable[..., SandboxPool] | None = None, pool_backend: PoolBackend | None = None, session_index: DynamoDbIndex | None = None, keep_on_failure: bool = False, region: str | None = None, session: Any = None, transport_factory: Any = default_transport, clock: Callable[[], float] = time.monotonic, sleep: Callable[[float], None] = time.sleep, renew_gateway_keys: bool = False, recipe: FleetRecipe | None = None, recipe_store: FleetRecipeStore | None = None, key_renewer: GatewayKeyRenewer | None = None)

Un agente servido por uno o varios MicroVMs (deploy_agent()).

invoke() y stream() colocan cada sesión nueva en un VM con cupo (con ScalingOptions.sessions="dedicated", uno por sesión), crean uno si hace falta y cabe, y esperan en una cola cliente hasta ServeOptions.queue_timeout_seconds si la flota está llena (CapacityException con code="agent_capacity" al agotarse). Con más de un VM posible (max_vms > 1), el session_id del resultado es un handle rs1.<sandbox>.<sesión>: al volver a pasarlo, la llamada va derecha a ese VM; si el VM ya no existe, SandboxException con code="agent_vm_lost". Con max_vms == 1 el id de sesión no cambia.

Un id de sesión elegido por el llamante no dice en qué VM está. Con un VM (o shared y un solo proceso) no importa; con dedicated, sólo el mismo proceso lo recuerda, salvo con session_index=DynamoDbIndex(...), que escribe una fila por VM dedicado para encontrarlo desde cualquier proceso. Sin índice, un id desconocido empieza un VM nuevo y el resultado trae el handle con el que seguir.

Los VMs de la flota son todos los MicroVMs no terminados de la imagen del proyecto (un MicroVM no se puede etiquetar): una imagen, una flota. Los VMs por encima de min_vms se terminan solos tras scale_in_after_seconds sin uso (set_timeout de rayd, sin servidor propio).

Coste y activación

Activa: deploy_agent() (rayito agent deploy) crea min_vms VMs y warm_vms plazas aparcadas; invoke() crea más, hasta max_vms, si las sesiones llenan los que hay. Construir o reconectar la flota no crea nada. Recursos y llamadas AWS: un MicroVM por VM (run-microvm), el token del proxy (CreateMicrovmAuthToken, gratuito) y list-microvms al reconectar; con warm_vms, plazas suspendidas del pool; con session_index, una fila de DynamoDB por VM dedicado. Coste aproximado: el del MicroVM por segundo mientras vive (2 GB: ≈ $0,14 por hora en us-east-1, consultado 2026-10-06 en https://aws.amazon.com/lambda/pricing/), por cada VM; una plaza aparcada no factura cómputo pero sí almacenamiento de snapshot. IAM: los de Sandbox.create(), Sandbox.list() y Sandbox.connect() (RayitoSandboxRunner) y secretsmanager:GetSecretValue por pasarela; con session_index, los de DynamoDbIndex. Leer y borrar la receta (connect(nombre) sin project, stop()), los de SsmRecipeStore; con renew_gateway_keys, los de GatewayKeyRenewer. Cómo apagarla: stop() termina todos los VMs; min_vms=1, max_vms=1, warm_vms=0 (por defecto) es un solo VM; sin llamar a deploy_agent() nada se crea. connect(..., use_recipe=False) no lee la receta y stop(keep_recipe=True) no la borra; renew_gateway_keys=False (por defecto) no renueva ninguna clave. Ejemplo: fleet = deploy_agent("./mi-agente") fleet.invoke("Resume el README.").text fleet.stop()

access_token: str property

El access token de todos los VMs de la flota: guárdalo (nunca lo imprimas ni lo versiones) para AgentFleet.connect() desde otro proceso. Una flota reconectada sin token (para listarla o terminarla) no lo tiene: InvalidArgumentException.

deployments: tuple[AgentDeployment, ...] property

Los clientes de los VMs que este proceso ya abrió.

connect(name: str, *, access_token: str | None = None, project: AgentProject | None = None, scaling: ScalingOptions | None = None, region: str | None = None, session: Any = None, use_recipe: bool = True, renew_gateway_keys: bool | None = None, **dependencies: Any) -> Self classmethod

Reconstruye la flota de la imagen name en cualquier proceso: todos sus MicroVMs no terminados, sin reanudar ninguno (un VM se abre al usarlo).

Sin project y con use_recipe (por defecto) lee la receta que guardó deploy_agent() (un GetParameter): con ella la flota crea VMs iguales a los del despliegue (proyecto tras las banderas, versión de la imagen y escalado) y status() da su escalado real. Un scaling= explícito gana a la receta. Las claves de Bedrock sólo se renuevan con renew_gateway_keys=True: si la receta lo pedía y no lo pasas, la flota no renueva y avisa recipe_renewal_ignored. Sin receta (o con project=, o use_recipe=False), la flota es la de 0.16: sin project enruta y consulta pero no puede crear VMs.

start() -> Self

Crea min_vms VMs (todo o nada: si uno falla, termina los que creó) y arranca el pool de plazas calientes. Lo llama deploy_agent().

refresh_members() -> None

Vuelve a leer qué VMs de la imagen siguen vivos (list-microvms): añade los nuevos, olvida los que ya no están y deja fuera las plazas aparcadas del pool de este proceso.

reconcile() -> None

Deja la flota dentro de ScalingOptions: olvida los VMs perdidos, crea los que falten hasta min_vms y termina los que sobren por encima de max_vms (sólo los libres).

scale(*, min_vms: int | None = None, max_vms: int | None = None, warm_vms: int | None = None) -> None

Cambia los límites de la flota (se validan como ScalingOptions) y la reconcilia: crea hasta min_vms, termina los sobrantes y ajusta las plazas calientes.

detach() -> None

Para el relleno de plazas calientes de este proceso y deja los VMs vivos: un proceso de un solo uso (rayito agent deploy) termina así sin tirar lo que creó. Las plazas aparcadas requieren un backend de pool persistente (pool_backend).

stop(*, keep_recipe: bool = False) -> None

Termina todos los VMs de la flota y las plazas del pool y, si este proceso guardó o leyó su receta, la borra (keep_recipe=True la conserva). Sigue aunque uno falle y, al final, lanza el primer error.

status() -> FleetStatus

Estado de la flota: pregunta a los VMs que este proceso ya abrió (GET /ping) y deja como unknown los demás.

wait_warm(timeout: float, poll: float) -> bool

Espera a que las plazas calientes queden aparcadas; False si se agota timeout (la flota funciona igual: las plazas son una optimización de latencia).

invoke(prompt: str | None = None, session_id: str | None = None, model: str | None = None, reasoning: bool = False, *, input: Mapping[str, Any] | None = None) -> AgentResult

Una invocación sin streaming: el AgentResult, con el id de sesión enrutable si la flota puede tener varios VMs. Con input en lugar de prompt, result.output trae la salida del grafo; se comprueba antes de reservar un VM.

stream(prompt: str | None = None, session_id: str | None = None, model: str | None = None, reasoning: bool = False, *, input: Mapping[str, Any] | None = None) -> AgentFleetStream

Como invoke(), con los eventos a medida que llegan. El VM queda ocupado hasta cerrar el stream.

AsyncAgentFleet(project: AgentProject | None, *, name: str | None = None, access_token: str | None = None, scaling: ScalingOptions | None = None, service: AsyncProjectService | None = None, directory: Any = None, pool_factory: Callable[..., AsyncSandboxPool] | None = None, pool_backend: PoolBackend | None = None, session_index: DynamoDbIndex | None = None, keep_on_failure: bool = False, region: str | None = None, session: Any = None, transport_factory: Any = default_async_transport, clock: Callable[[], float] = time.monotonic, sleep: Callable[[float], Awaitable[None]] = asyncio.sleep, renew_gateway_keys: bool = False, recipe: FleetRecipe | None = None, recipe_store: FleetRecipeStore | None = None, key_renewer: GatewayKeyRenewer | None = None)

El espejo asíncrono de AgentFleet: las mismas operaciones como corrutinas (start, invoke, status, scale, reconcile, stop, connect) y stream() como contexto asíncrono. Sesiones, id de sesión enrutable (rs1.<sandbox>.<sesión>), cola cliente, sesiones dedicadas e índice de sesión opcional son los de AgentFleet.

Coste y activación

Activa: await deploy_agent_async() crea min_vms VMs y warm_vms plazas aparcadas; invoke() crea más, hasta max_vms, si las sesiones llenan los que hay. Construir o reconectar la flota no crea nada. Recursos y llamadas AWS: un MicroVM por VM (run-microvm), el token del proxy (CreateMicrovmAuthToken, gratuito) y list-microvms al reconectar; con warm_vms, plazas suspendidas del pool; con session_index, una fila de DynamoDB por VM dedicado. Coste aproximado: el del MicroVM por segundo mientras vive (2 GB: ≈ $0,14 por hora en us-east-1, consultado 2026-10-06 en https://aws.amazon.com/lambda/pricing/), por cada VM; una plaza aparcada no factura cómputo pero sí almacenamiento de snapshot. IAM: los de AsyncSandbox.create(), list() y connect() (RayitoSandboxRunner) y secretsmanager:GetSecretValue por pasarela; con session_index, los de DynamoDbIndex. Cómo apagarla: await stop() termina todos los VMs; min_vms=1, max_vms=1, warm_vms=0 (por defecto) es un solo VM; sin llamar a deploy_agent_async() nada se crea. Ejemplo: fleet = await deploy_agent_async("./mi-agente") (await fleet.invoke("Resume el README.")).text await fleet.stop()

access_token: str property

El access token de todos los VMs de la flota: guárdalo (nunca lo imprimas ni lo versiones) para AsyncAgentFleet.connect() desde otro proceso. Una flota reconectada sin token (para listarla o terminarla) no lo tiene: InvalidArgumentException.

deployments: tuple[AsyncAgentDeployment, ...] property

Los clientes de los VMs que este proceso ya abrió.

connect(name: str, *, access_token: str | None = None, project: AgentProject | None = None, scaling: ScalingOptions | None = None, region: str | None = None, session: Any = None, use_recipe: bool = True, renew_gateway_keys: bool | None = None, **dependencies: Any) -> Self async classmethod

Como AgentFleet.connect(): sin project y con use_recipe lee la receta de la flota (un GetParameter, en un hilo) y con ella crea VMs iguales a los del despliegue; scaling= explícito gana a la receta y las claves sólo se renuevan con renew_gateway_keys=True (la receta nunca lo activa: recipe_renewal_ignored).

start() -> Self async

Crea min_vms VMs (todo o nada: si uno falla, termina los que creó) y arranca el pool de plazas calientes. Lo llama deploy_agent_async().

refresh_members() -> None async

Vuelve a leer qué VMs de la imagen siguen vivos (list-microvms): añade los nuevos, olvida los que ya no están y deja fuera las plazas aparcadas del pool de este proceso.

reconcile() -> None async

Deja la flota dentro de ScalingOptions: olvida los VMs perdidos, crea los que falten hasta min_vms y termina los que sobren por encima de max_vms (sólo los libres).

scale(*, min_vms: int | None = None, max_vms: int | None = None, warm_vms: int | None = None) -> None async

Cambia los límites de la flota (se validan como ScalingOptions) y la reconcilia: crea hasta min_vms, termina los sobrantes y ajusta las plazas calientes.

detach() -> None async

Para el relleno de plazas calientes de este proceso y deja los VMs vivos: un proceso de un solo uso (rayito agent deploy) termina así sin tirar lo que creó. Las plazas aparcadas requieren un backend de pool persistente (pool_backend).

stop(*, keep_recipe: bool = False) -> None async

Termina todos los VMs de la flota y las plazas del pool y, si este proceso guardó o leyó su receta, la borra (keep_recipe=True la conserva). Sigue aunque uno falle y, al final, lanza el primer error.

status() -> FleetStatus async

Estado de la flota: pregunta a los VMs que este proceso ya abrió (GET /ping) y deja como unknown los demás.

wait_warm(timeout: float, poll: float) -> bool async

Espera a que las plazas calientes queden aparcadas; False si se agota timeout (la flota funciona igual: las plazas son una optimización de latencia).

invoke(prompt: str | None = None, session_id: str | None = None, model: str | None = None, reasoning: bool = False, *, input: Mapping[str, Any] | None = None) -> AgentResult async

Una invocación sin streaming: el AgentResult, con el id de sesión enrutable si la flota puede tener varios VMs. Con input en lugar de prompt, result.output trae la salida del grafo; se comprueba antes de reservar un VM.

stream(prompt: str | None = None, session_id: str | None = None, model: str | None = None, reasoning: bool = False, *, input: Mapping[str, Any] | None = None) -> AsyncAgentFleetStream async

Como invoke(), con los eventos a medida que llegan. La petición se hace aquí (un 429 se reintenta) y el VM queda ocupado hasta cerrar el stream.

AgentFleetStream(stream: Any, *, sandbox_id: str, routable: bool, release: Callable[[str | None], None])

El stream de una invocación de la flota: los eventos del VM con el id de sesión enrutable, y el VM liberado al cerrarlo.

AsyncAgentFleetStream(stream: Any, *, sandbox_id: str, routable: bool, release: Callable[[str | None], Awaitable[None]])

El stream de una invocación de la flota asíncrona: los eventos del VM con el id de sesión enrutable, y el VM liberado al cerrarlo.

FleetStatus(name: str, members: tuple[FleetMemberStatus, ...], min_vms: int, max_vms: int, warm_vms: int, warm_ready: int, scaling_source: ScalingSource = 'default') dataclass

El estado de una flota: sus miembros, cuántos están sanos u ocupados y los límites de ScalingOptions. Un miembro que este proceso aún no abrió figura como unknown: preguntarle lo reanudaría. scaling_source dice de dónde salen los límites: con default (sin proyecto ni receta) no describen la flota real.

FleetMemberStatus(sandbox_id: str, state: MemberHealth, extra: bool, inflight: int) dataclass

AgentCore deploy

deploy_agent(target="agentcore") deploys the same project to Amazon Bedrock AgentCore Runtime and returns an AgentCoreFleet with the same invoke() and stream() as AgentFleet (Deploy to AgentCore). Without target, deploy_agent(), deploy_agent_async() and plan_deploy() do what they always did.

AgentCoreOptions(bedrock_models: tuple[str, ...] = (), readable_secrets: tuple[str, ...] = (), idle_session_timeout_seconds: int | None = None, max_session_lifetime_seconds: int | None = None, tags: Mapping[str, str] = dict(), external_model: bool = False) dataclass

Las opciones de un despliegue en AgentCore (deploy_agent(target= "agentcore", agentcore=...)): bedrock_models (ids o ARNs de modelos o perfiles de inferencia que el rol del runtime puede invocar, además del modelo del proyecto), readable_secrets (nombres o ARNs de secretos de Secrets Manager que el rol puede leer), idle_session_timeout_seconds y max_session_lifetime_seconds (60-28800; None, 900 y 28800), tags (etiquetas de la pila y del runtime, para organizaciones que las exigen) y external_model (permite un modelo fuera de Bedrock: el rol no recibe permisos de Bedrock que no pidas y la clave la obtiene tu código con readable_secrets; nunca va en la configuración del runtime). Sólo valida los tipos: los rangos se comprueban al planificar, antes de llamar a AWS.

Coste y activación

Activa: nada por sí mismo; el valor es puro. Lo que cuesta es deploy_agent(target="agentcore"). Recursos y llamadas AWS: ninguno aquí; ver deploy_agent(). Coste aproximado: ninguno aquí; ver deploy_agent(). IAM: ninguna para este valor. Cómo apagarla: los valores por defecto no dan al rol del runtime ningún permiso de Bedrock extra ni de Secrets Manager, y sin external_model=True un proveedor que no es Bedrock es provider_unsupported. Ejemplo: AgentCoreOptions(bedrock_models=("amazon.nova-lite-v1:0",), tags={"team": "agents"})

AgentCoreDeployPlan(name: str, region: str, runtime_name: str, stack_name: str, repository_name: str, build_project_name: str, image_tag: str, bucket: str | None, bucket_source: str, network: str, bedrock_models: tuple[str, ...], readable_secrets: tuple[str, ...], environment: Mapping[str, str], lifecycle: AgentCoreLifecycle, tags: Mapping[str, str], steps: tuple[AgentCoreStep, ...], security: tuple[str, ...], warnings: tuple[str, ...], cost: tuple[str, ...], detection: object | None = None, target: str = 'agentcore', external_model: bool = False, secret_env_names: tuple[str, ...] = ()) dataclass

Lo que deploy_agent(target="agentcore") haría, calculado sin llamar a AWS: los nombres derivados, el bucket (o None si se descubre al aplicar) y de dónde sale, los modelos y secretos del rol, el entorno y el ciclo de vida del runtime, las etiquetas, los tres pasos, y las claves de las líneas de seguridad y de los avisos (_messages.SECURITY_TEXTS y WARNING_TEXTS), el coste de lista y si el modelo está fuera de Bedrock (external_model). secret_env_names son las variables que el host pondrá desde Secrets Manager (ADR-033 D4): sólo nombres, nunca valores.

AgentCoreFleet(status: AgentCoreStatus, *, gateways: AgentCoreGateways, invoker: RuntimeInvoker)

Bases: AgentCoreFleetView

Un agente desplegado en AgentCore Runtime. invoke() y stream() tienen la firma y los resultados de AgentFleet: el session_id del resultado es el que se usó (si pasas uno tuyo, el mismo id llega siempre a la misma sesión), y pasarlo de nuevo continúa la conversación: cada sesión corre en su propia microVM. status() lee el runtime, stop_session() termina la microVM de una sesión y delete() borra el agente.

Coste y activación

Activa: crear la flota con deploy_agent(target="agentcore") o AgentCoreFleet.connect() no llama a AWS más que para leer el runtime; cada invoke() o stream() crea o reanuda una sesión de AgentCore, que se factura por segundo de uso. Recursos y llamadas AWS: InvokeAgentRuntime, StopRuntimeSession; connect() y status() leen con ListAgentRuntimes y GetAgentRuntime; delete() los de AgentCoreDeleter. Coste aproximado: V1 de la plataforma, $0,0895 por vCPU-hora y $0,00945 por GB-hora de uso real, por segundo, sin cargo de CPU en espera de E/S y con la memoria cobrada hasta que la sesión termina (15 min sin uso por defecto); us-east-1, consultado 2026-10-19 en https://aws.amazon.com/bedrock/agentcore/pricing/. El modelo lo cobra Bedrock. IAM: bedrock-agentcore:InvokeAgentRuntime, StopRuntimeSession, ListAgentRuntimes y GetAgentRuntime. Cómo apagarla: stop_session() termina una sesión; delete() borra el agente; sin sesiones activas no hay cómputo. Ejemplo: fleet = AgentCoreFleet.connect("support-bot") result = fleet.invoke("Resume el README.") fleet.invoke("¿Y el de docs?", session_id=result.session_id)

connect(name: str, *, region: str | None = None, session: Any = None) -> Self classmethod

Reconstruye la flota del agente name desde su runtime, en cualquier proceso. Si no hay ningún agente de Rayito con ese nombre en la región, AgentCoreException con código runtime_not_found.

invoke(prompt: str | None = None, session_id: str | None = None, model: str | None = None, reasoning: bool = False, *, input: Mapping[str, Any] | None = None) -> AgentResult

Una invocación sin streaming: el AgentResult, o AgentException si el agente falló. result.session_id es el id que usó AgentCore.

stream(prompt: str | None = None, session_id: str | None = None, model: str | None = None, reasoning: bool = False, *, input: Mapping[str, Any] | None = None) -> AgentCoreFleetStream

Como invoke(), con los eventos a medida que llegan: with fleet.stream("hola") as events: for event in events.

status() -> AgentCoreStatus

El runtime tal como está ahora (GetAgentRuntime).

stop_session(session_id: str) -> bool

Termina la microVM de una sesión (StopRuntimeSession); False si ya no existía. session_id se mapea igual que en invoke().

delete() -> AgentCoreDeleteResult

Borra el agente: el runtime, la pila rayito-agentcore-<nombre> (con el repositorio de ECR, el proyecto de CodeBuild y los roles) y el grupo de logs del runtime. Sólo borra lo que Rayito creó con este nombre.

AsyncAgentCoreFleet(status: AgentCoreStatus, *, gateways: AgentCoreGateways, invoker: RuntimeInvoker)

Bases: AgentCoreFleetView

El espejo asíncrono de AgentCoreFleet: las mismas operaciones como corrutinas (invoke, stream, status, stop_session, delete, connect) y las mismas propiedades.

Coste y activación

Activa: crear la flota con deploy_agent_async(target="agentcore") o AsyncAgentCoreFleet.connect() no llama a AWS más que para leer el runtime; cada invoke() o stream() crea o reanuda una sesión. Recursos y llamadas AWS: los de AgentCoreFleet. Coste aproximado: el de AgentCoreFleet (V1: $0,0895 por vCPU-hora y $0,00945 por GB-hora de uso real, us-east-1, consultado 2026-10-19 en https://aws.amazon.com/bedrock/agentcore/pricing/). IAM: los de AgentCoreFleet. Cómo apagarla: await fleet.stop_session() termina una sesión; await fleet.delete() borra el agente. Ejemplo: fleet = await AsyncAgentCoreFleet.connect("support-bot") result = await fleet.invoke("Resume el README.")

connect(name: str, *, region: str | None = None, session: Any = None) -> Self async classmethod

Reconstruye la flota del agente name desde su runtime.

invoke(prompt: str | None = None, session_id: str | None = None, model: str | None = None, reasoning: bool = False, *, input: Mapping[str, Any] | None = None) -> AgentResult async

Una invocación sin streaming: el AgentResult, o AgentException si el agente falló. result.session_id es el id que usó AgentCore.

stream(prompt: str | None = None, session_id: str | None = None, model: str | None = None, reasoning: bool = False, *, input: Mapping[str, Any] | None = None) -> AsyncAgentCoreFleetStream async

Como invoke(), con los eventos a medida que llegan. La petición se hace aquí, así que un error de AWS salta antes del primer evento.

status() -> AgentCoreStatus async

El runtime tal como está ahora (GetAgentRuntime).

stop_session(session_id: str) -> bool async

Termina la microVM de una sesión (StopRuntimeSession); False si ya no existía.

delete() -> AgentCoreDeleteResult async

Borra el agente: el runtime, la pila y el grupo de logs del runtime.

AgentCoreFleetStream(stream: AgentDeploymentStream, session_id: str)

El stream de una invocación: los eventos del agente con el id de sesión que se usó (session_id); el último es Done o AgentFailed, y result() lanza AgentException si el agente falló.

AsyncAgentCoreFleetStream(stream: AsyncAgentDeploymentStream, session_id: str)

El stream asíncrono de una invocación: los eventos del agente con el id de sesión que se usó (session_id); result() lanza AgentException si el agente falló.

AgentCoreStatus(name: str, region: str, runtime_name: str, runtime_id: str, runtime_arn: str, runtime_version: str, status: str, image_uri: str, log_group: str) dataclass

El estado de un agente desplegado en AgentCore (GetAgentRuntime).

AgentCoreDeleteResult(name: str, region: str, runtime_deleted: bool, stack_deleted: bool, log_group_deleted: bool) dataclass

Lo que delete() borró: el runtime, la pila (con el repositorio, el proyecto de CodeBuild y los roles) y el grupo de logs del runtime.

Runtime ports

They describe how a runtime is installed and served without changing the AgentRuntime port.

InstallableRuntime

Bases: Protocol

Puerto opcional: lo que un runtime pone en la imagen. AgentTemplate compone la imagen con los install_steps() de cada runtime.

ServableRuntime

Bases: Protocol

Puerto opcional: cómo el host sirve un runtime (host_config).

HostConfig(driver: str, python: str, files: RuntimeFiles, envs: Mapping[str, str] = dict(), options: Mapping[str, object] = dict()) dataclass

Lo que el host del agente necesita para servir un runtime: el driver (lista cerrada HOST_DRIVERS), el intérprete python que ejecuta el host, los ficheros a escribir antes de arrancar, las variables de entorno y las opciones propias del driver.

GatewayWiring(urls: Mapping[str, str]) dataclass

El modelo llega por las pasarelas del sandbox: urls por nombre de pasarela (MicroVM). El agente sólo ve un marcador como clave.

DirectWiring() dataclass

Sin pasarela (contenedor exportado): el host lee la credencial de la fuente estándar del proveedor. La garantía de la pasarela no aplica.

ModelWiring: TypeAlias = GatewayWiring | DirectWiring module-attribute

InstallTarget(platform: InstallPlatform = 'microvm', python: str = DEFAULT_INSTALL_PYTHON, install_dir: str = DEFAULT_INSTALL_DIR) dataclass

Dónde instala un InstallableRuntime: descripción pura, sin I/O. python es el intérprete con el que se crean los venvs y install_dir la raíz de lo instalado.

is_installable(runtime: object) -> bool

runtime implementa InstallableRuntime.

is_servable(runtime: object) -> bool

runtime implementa ServableRuntime.

Listing

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.

next_token: str | None property

Antes de la primera llamada, el token recibido; después, el cursor mientras has_next, si no None.

next_items() -> list[SandboxListItem]

La siguiente página de hasta limit items (todos sin limit). Puede venir vacía mientras has_next siga siendo True.

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.