
# Arquitectura objetivo: Substrato + FORGE sobre Amazon Bedrock AgentCore

Fecha: 2026-07-08 · Estado (2026-07-09):
- **Wave 0 (ModelPlane L1) EN MAIN** — PR #75, merge fdcce16, Bedrock validado en vivo (Haiku 4.5). `local`/`claude-cli` de default (paridad), Bedrock opt-in por clase.
- **Wave 1a (SandboxPort + conformance + spike) EN MAIN** — PR #76, merge 5fe5d04. Puerto async sobre el sandbox de FORGE + fix de seguridad (guard de prod en verify) + spike de Code Interpreter (G1 egress bloqueado / G2 deno+TS / G3 fidelidad, todos verde).
- **Wave 1b (CodeInterpreterAdapter) EN MAIN** — PR #77, merge c79d9f6. Ejecución remota de FORGE en microVMs Firecracker de AgentCore; conformance remota 8/8 = PARIDAD con local. `local` de default; `code-interpreter` opt-in (`FORGE_SANDBOX_ADAPTER=code-interpreter` + `AGENTCORE_CODE_INTERPRETER_ID`). Grant IAM `AgentCoreCodeInterpreterSpike` adjunto a `remotion-lambda`; interpreter `forge_ci_1783617549840-PK1X81ohys` provisionado (no cobra idle). Fast-follows de seguridad PRE-DEFAULT documentados en `docs/spikes/2026-07-09-code-interpreter-conformance-acta.md` (acotar --allow-read; ampliar safety.ts a Deno.* APIs) — DoS-only, threat model hipotético.
- **Wave 2a (spike Gateway/Identity/Policy) EN MAIN** — PR #79. Validó G-IAM + G-MCP (worker externo consume Gateway MCP con SigV4) contra AWS; hallazgos: action Cedar = tool name real target-prefixed, ENFORCE filtra tools/list, credential providers MANAGED usan Secrets Manager, naming gateway con guiones. Grants consolidados a `AgentCoreScoped` (least-privilege).
- **Wave 2b-1 (ToolPlane foundation) EN MAIN** — PR #80. Fundación soberana SIN AWS: `Operation.tool_binding` opcional, ToolPlane port + SelfHostedMcpAdapter, Tool Broker (tabla tool_bindings migración 0036 + grants HMAC + PDP default-deny fail-closed), conformance. Retrocompatible/inerte (nada lo llama aún).
- **Wave 2b-2 (AgentCoreGatewayAdapter) EN MAIN** — PR #81. Adapter que consume Gateway MCP real (SigV4) + provisioning productivo de dos fases (LOG_ONLY→descubrir tools→Cedar→ENFORCE) + proyección Cedar. Conformance ToolPlane 5/5 = PARIDAD con self-hosted contra AWS real (acta `docs/spikes/2026-07-09-toolplane-agentcore-acta.md`). `self-hosted` default; `agentcore-gateway` opt-in.
- **Wave 2b-3 (cablear dispatch) EN MAIN** — PR #82. `dispatchOperation` autoriza ops con `tool_binding` vía el PDP (deny/excepción → step falla fail-closed) e inyecta `ctx.tools` (ToolPlaneClient con el grant privado). Retrocompatible/inerte en prod: solo la op de demo `demo.tool_echo` tiene tool_binding; ninguna op productiva lo tiene, así el path viejo no cambia. Validado e2e con SelfHostedMcpAdapter (sin AWS). Suite 184/1505.
- **Wave 2b-4 (pendiente):** FORGE tools-scoped (escalera de safety.ts: `pure`→`tools-scoped` para que FORGE construya ops impuras que solo llaman tools MCP otorgadas) + migrar la primera capability impura REAL a producción (ej. un slack.post) + auth JWT (hoy SigV4) + acotar el execution role. Es donde una op impura real corre en prod detrás del build-gate humano. CAMBIA comportamiento de prod: requiere checkpoint con Roberto.
- **Wave 3 (Runtime + Strands como Execution Adapter, ADR-001) pendiente.**
· Autor: sesión Claude (cerebro Fable 5, investigación verificada por 5 agentes contra docs oficiales AWS). Manos: Codex CLI (Waves 0/1a/1b-código) + subagentes Sonnet (Wave 1b tramo final, con Codex sin cuota, por autorización de Roberto).

## Principio rector

**El substrato es el plano de control soberano; AgentCore es un plano de ejecución y fontanería alquilado.** AgentCore nunca decide, solo ejecuta. Toda capacidad de AgentCore entra por un puerto (interface nuestra) con al menos 2 adapters, uno de ellos self-hostable. El precedente interno ya existe: FORGE-on-Eve corre el mismo contrato generate→verify→build-gate→register con otro motor y registra de vuelta vía `POST /api/forge/register` sin tocar la DB. Ese patrón se generaliza a tres puertos: **ExecutionPlane**, **ToolPlane**, **ModelPlane**.

## Hechos verificados que condicionan el diseño (jul-2026, docs oficiales)

1. **AgentCore Runtime**: microVM Firecracker dedicada por sesión (destruida y sanitizada al cerrar), afinidad por `runtimeSessionId`, máx 8h/sesión, idle 15 min, 2vCPU/8GB fijo, contenedor ARM64 (máx 2GB) o Direct Code Deploy ZIP (Python y Node), contrato HTTP mínimo `POST /invocations` + `GET /ping`, soporta MCP y A2A como contratos nativos. Pricing $0.0895/vCPU-h (CPU en I/O wait gratis) + $0.00945/GB-h; la memoria se cobra todo el tiempo que la sesión vive, incluso idle.
2. **Code Interpreter**: sandbox gestionado para ejecutar código arbitrario (Python y Node desde mar-2026), 2vCPU/8GB, 10GB disco, 15 min sync / 8h async, internet opcional, 1000 sesiones concurrentes/cuenta.
3. **Gateway**: convierte OpenAPI/Lambda/Smithy en tools MCP, modo agregación (un solo servidor MCP virtual), semantic tool search (límite 25 TPM), targets HTTP passthrough y targets de Runtime (jun-2026). **Consumible desde un cliente MCP externo a AWS** vía URL pública con ingress OAuth JWT (verificado). Pricing: $0.005/1k invocaciones, $0.025/1k búsquedas, $0.02/100 tools indexadas/mes.
4. **Identity**: workload identities + token vault (tokens ligados a agente Y usuario final), modos user-delegated y autonomous, IdPs externos + OIDC genérico, referencia a Secrets Manager existente.
5. **Policy** (GA mar-2026): autorización fine-grained de tools por agente, integración con Bedrock Guardrails a nivel gateway (jun-2026, regiones limitadas).
6. **Intelligent Prompt Routing, la sorpresa**: catálogo CONGELADO en Claude 3/3.5 (Haiku 3, Haiku 3.5, Sonnet 3.5 v1/v2), Llama 3.1-3.3 y Nova Lite/Pro v1. Sin Claude 4.x/5, sin Haiku 4.5. Same-family only (par exacto de 2 modelos + fallback + umbral `responseQualityDifference`). Optimizado SOLO para inglés. Sin feedback loop propio. $1/1k requests + ~85ms P90. Conclusión: IPR no puede ser el centro del routing del substrato (pipeline mayormente en español y los modelos frontera que usamos no son ruteables por IPR).
7. **Modelos Claude en Bedrock jul-2026**: Sonnet 5 (GA 30-jun, 1M ctx), Opus 4.8/4.7, Haiku 4.5, Fable 5 (GA 9-jun, retirado y reinstalado 1-jul con retención obligatoria de 30 días vía `provider_data_share`). Palancas de costo: application inference profiles (atribución por workspace vía tags), batch inference (50% off), prompt caching (Sonnet 5 min 4096 tokens/checkpoint).
8. **Trampas de acoplamiento identificadas** (NO adoptar como primarios): AgentCore Harness (orquestación managed, GA jun-2026, duplica lo que Inngest+gates ya hacen), AgentCore Memory como memoria primaria, Agent Registry como SSOT, Bedrock Agents.
9. **Correlación de traces propios con sesiones AgentCore**: no documentada en detalle por AWS; hay que prototipar (sessionId como ancla + atributos ADOT). Constraint explícito del diseño.

## Estado actual relevante (verificado en el repo)

- Choke points que facilitan la extracción de puertos: TODA llamada LLM pasa por `apps/api/src/inngest/llm.ts` (spawn claude CLI headless; fallback @ai-sdk/anthropic; cero Bedrock en el repo); todo dispatch pasa por `runtime.ts dispatchOperation` con catálogo declarativo en `packages/substrate-spec` (id@semver, schemas, side_effects, `implementations[]`); el sandbox FORGE es `forge/sandbox.ts` (bwrap red-denegada, distinción load-bearing `ok|red|infra` nacida del bug real del PATH); los renders pasan por la cola `compute_jobs` + `compute-worker.ts`.
- Anti-patrón actual: `claude-sonnet-4-5-20250929` hardcodeado en ~15 archivos, sin capa de resolución de modelo. Un solo tier para todo.
- Deudas que el rediseño debe respetar: registry forjado in-memory single-version (gap para multi-instancia), paths absolutos `/home/clawd`, dependencia del venv externo, spans manuales rompen el replay de Inngest (usar InngestSpanProcessor, lección del commit 754bcea).

## Arquitectura objetivo

```
┌────────────────────── PLANO DE CONTROL SOBERANO (Hetzner hoy / VPC mañana) ──────────────────────┐
│ apps/web ─ apps/api (Hono) ─ Nova (MATCH|PLAN|FORGE|CANNOT) ─ FORGE (gen→verify→gate→register)   │
│ Inngest executor (steps durables, human gates, retries, timeouts)                                │
│ Postgres SSOT (intents/plans/steps/traces/step_executions/artifacts/lineage/budgets/policy)      │
│ PDP propio (políticas, budgets, grants) ─ Presidio (PII) ─ Langfuse + OTel (InngestSpanProcessor)│
│        │ ModelPlane (puerto)         │ ExecutionPlane (puerto)        │ ToolPlane (puerto)       │
└────────┼─────────────────────────────┼────────────────────────────────┼─────────────────────────┘
         ▼ adapters                    ▼ adapters                       ▼ adapters
  bedrock-converse (+IPR opcional)  agentcore-runtime            agentcore-gateway (MCP)
  anthropic-api / claude-cli        agentcore-code-interpreter    + agentcore-identity (vault)
  (otros proveedores futuros)       bwrap local / local worker    + agentcore-policy (PEP)
                                                                  mcp-selfhosted (salida)
```

Límites de confianza: TB1 usuario→API (bearer, existente). TB2 plano de control→plano de ejecución (solo sale nuestro envelope + grants con alcance; los resultados vuelven por la API con validación de schema; el workload jamás ve `SUBSTRATE_DB_URL`). TB3 ejecución→tools (JWT por step, Policy+Guardrails como PEP, credenciales de terceros solo en el token vault). TB4 código generado por el modelo: untrusted siempre, microVM o bwrap, sin secrets, datos vectorizables pasan antes por Presidio.

## Q1: Firecracker microVMs debajo del substrato y FORGE

Puerto **ExecutionPlane** con 4 tipos de job y su mapeo:

1. `verify` y `execute` de FORGE → **Code Interpreter** (es literalmente "ejecutar código generado por el modelo en sandbox aislado"). El adapter implementa el MISMO contrato `VerifyResult/ExecResult` incluyendo `kind: ok|red|infra`; un fallo del sandbox gestionado (throttling, sesión caída) se reporta `infra`, nunca como "la generación no convergió". bwrap queda como adapter local/dev y ruta de salida.
2. `agent-session` (loops de agente de larga duración, futuras ops impuras forjadas) → **Runtime**. Empaquetado: harness NUESTRO mínimo (imagen ARM64 o Direct Code Deploy Node) que expone `/invocations` + `/ping` y por dentro ejecuta el envelope `{step_execution_id, trace_id, op_ref, inputs, policy_grant, telemetry_ctx}`. `runtimeSessionId = trace_id:step_id` (afinidad + correlación).
3. `compose/render` (FFmpeg/whisper/insightface) → queda LOCAL en fase inicial; la cola `compute_jobs` ya es el puerto. Migrar recién tras spike de viabilidad ARM64 y con media por URLs presignadas de R2 (payload máx 100MB). La economía es viable (un render de 10 min a 2vCPU cuesta del orden de $0.03-0.05 con los precios verificados) pero el port ARM64 del pipeline es la parte cara.
4. Llamadas LLM: NO van al ExecutionPlane, van al ModelPlane (spawn del CLI desaparece con el provider bedrock/anthropic-api).

Reglas duras: Inngest sigue siendo el director (Runtime NO orquesta); ningún microVM espera un human gate (la espera vive en `step.waitForEvent`; mantener una sesión viva cobrando memoria-hora durante un gate de 72h es tirar dinero y acoplar estado a un componente efímero); estado durable solo en Postgres; sesiones son ganado, no mascotas.

## Q2: Gateway + MCP con FORGE (QUÉ nuestro, CÓMO/DÓNDE delegado)

- El **QUÉ** sigue en `packages/substrate-spec`: la Operation declara `tool_bindings` con nombres de capability abstractos (`crm.contacts.upsert`), schemas nuestros, versionados.
- El **CÓMO/DÓNDE** es del Gateway: cada API externa (OpenAPI/Lambda/MCP server) se registra como target; el Gateway agrega todo en un servidor MCP virtual.
- Componente nuevo nuestro: **Tool Broker**. Mantiene en Postgres la tabla de bindings capability → {gateway_id, tool_name, credential_provider, scopes} y ACUÑA GRANTS: JWT de corta vida por step_execution que enumera exactamente las tools permitidas (derivadas del plan + política del workspace + budgets). Doble enforcement: PDP nuestro decide; AgentCore Policy + ingress JWT del Gateway + Guardrails hacen de PEP del lado AWS.
- Flujo con FORGE: en tiempo de diseño/forjado, FORGE puede usar la búsqueda semántica del Gateway (`x_amz_bedrock_agentcore_search`, solo design-time por el límite de 25 TPM) para DESCUBRIR candidatos; pero VINCULAR una tool a una capability pasa por el build-gate humano existente (extender `forge_candidates` para mostrar los scopes solicitados). Descubrimiento en runtime ≠ binding en runtime: un agente solo llama tools ya otorgadas.
- Esto desbloquea la evolución de FORGE de "solo ops puras" (safety.ts actual) a una escalera de capacidades: `pure` → `tools-scoped` (impura, pero SOLO vía tools MCP otorgadas, sin red/fs directos). El aislamiento no se relaja: se canaliza.
- Simetría bonita: una capability forjada desplegada en Runtime puede exponerse ella misma como tool MCP y registrarse como target del Gateway (Runtime targets, GA jun-2026), siempre detrás del build-gate.
- Portabilidad: MCP es el narrow waist. Los nombres de capability, schemas y política viven en NUESTRO Postgres; si salimos de AWS, el Gateway se reemplaza por un agregador MCP self-hosted y solo cambia el adapter del Tool Broker.

## Q3: Routing de modelos (dos niveles, IPR como plug-in opcional)

- **L1, policy router nuestro (determinista)**: cada Operation y evaluator declara `model_class` (`frontier-judge`, `frontier-compose`, `standard`, `lite`, `bulk`). Una tabla de política en Postgres (por entorno, override por workspace) resuelve class → {provider, model_id, params, fallback_chain}. Reglas de criticidad nuestras: evaluator gates y pasos irreversibles/publish fijan frontier SIEMPRE; extracción masiva va a lite; el compositor Nova a standard. Esto elimina el hardcodeo actual: los ~15 model ids colapsan en UN punto de resolución dentro de `llm.ts`, que ya es el choke point.
- **L2, optimización por prompt (delegable)**: dentro de una banda que L1 autorizó, el target puede ser: (a) un configured router de IPR (par same-family + umbral) SOLO si los modelos de la banda están soportados por IPR y el contenido es inglés; (b) un router de aplicación cross-provider (heurística propia o LiteLLM/RouteLLM); (c) estático. IPR nunca cruza bandas de criticidad ni decide política.
- Proveedores como adapters bajo `llm.ts`: `claude-cli`, `anthropic-api` (existentes), `bedrock-converse` (nuevo: habilita Sonnet 5/Haiku 4.5/Opus 4.8/Nova, ARNs de IPR como modelId, application inference profiles). Model ids namespaced (`bedrock:...`, `anthropic:...`, `bedrock-router:arn:...`).
- Gobernanza de costo: application inference profiles por workspace (atribución en Cost Explorer), budgets + Langfuse como ledger de app, batch inference (50% off) como hint de ejecución para ops no interactivas (ej. `prospect-score-batch`), prompt caching para el prompt del compositor Nova (reenvía el catálogo entero en cada compose).
- Guardia de calidad: loggear por step_execution {model_class pedida, modelo resuelto, razón}; shadow-scoring muestral de outputs ruteados a lite con nuestros evaluators (o AgentCore Evaluations como señal advisory); alarma de degradación revierte la banda. Sin drift silencioso.
- Nota de soberanía de datos: Fable 5 en Bedrock exige retención de 30 días (`provider_data_share`); la política L1 puede excluirlo de task classes con datos sensibles. Exactamente por esto el routing es nuestro.

## Q4: Qué queda, qué se adapta, qué es nuevo

**Queda intacto (núcleo soberano):** esquema y semántica de Postgres; Inngest executor (gates, retries, timeouts); catálogo substrate-spec + dispatchOperation + semver + validación Zod; Nova; loop FORGE con build-gate humano; API Hono; web; R2; Presidio; Langfuse.

**Se adapta (extracción de puertos en choke points existentes):** `llm.ts` → ModelPlane; `forge/sandbox.ts` → SandboxPort (bwrap + Code Interpreter, preservando ok|red|infra); `compute-worker` → adapter detrás de `compute_jobs`; `runtime.ts` → tercera rama de dispatch para implementations `remote-runtime`; observabilidad → envelope `telemetry_ctx` + ADOT en el harness con `trace_id` como atributo (prototipar la correlación, no está documentada); `safety.ts` → escalera pure/tools-scoped; registry forjado → versionado y persistente (prerequisito multi-instancia).

**Nuevo (todo nuestro, delgado):** packages/execution-plane, packages/tool-broker (bindings + grants + cliente MCP), packages/model-plane (política L1 + routers L2 + providers), módulo PDP (extiende budgets/policies y se espeja en AgentCore Policy), identity bridge (workspace/agent → workload identity; oauth_channels → token vault), telemetry fan-in, **suite de conformance** (los mismos contract tests corren contra TODOS los adapters de cada puerto en CI: la garantía de portabilidad ejecutable), imagen base del harness Runtime.

## Q5: Soberanía y trade-offs

**Por qué esto preserva la soberanía:** la lógica de decisión (Nova, planes, gates, evaluators, política de tools y modelos) nunca sale del substrato; AgentCore recibe comandos y grants, jamás objetivos. Cada módulo AgentCore está detrás de un puerto con adapter self-hostable vivo en CI; salir es un cambio de configuración por plano, no un rewrite. Los narrow waists son estándares abiertos: MCP (tools), OCI + HTTP plano (cómputo), OTel (telemetría), nuestro SQL (estado). Deliberadamente NO se adoptan Harness, Memory primario, Registry como SSOT ni Bedrock Agents: son los pozos de gravedad donde el negocio migra a primitivas del vendor.

**Vs. alternativa más acoplada** (Harness + Bedrock Agents + Memory + Registry): más rápida al mercado y consola unificada, pero la semántica de orquestación migra al vendor (Harness comparte cuotas de Runtime, memoria con schema del vendor), duplica lo que Inngest+gates ya resuelven, y el exit es un rewrite. Rechazada por el requisito de soberanía; se adopta selectivamente Evaluations como señal advisory, nunca como gate.

**Vs. alternativa más minimalista** (self-host Firecracker/más fierro + bwrap + LiteLLM): máxima soberanía y menor costo unitario a carga constante, pero re-asumimos exactamente la carga operativa que ya nos mordió (load 91 por jobs vecinos, bug del PATH, registries single-instance), y orquestar microVMs multi-tenant es un producto en sí mismo. Camino medio elegido: alquilar aislamiento y fontanería, conservar el cerebro.

**Costos/riesgos del camino elegido y mitigaciones:** latencia Hetzner→AWS y cold starts ~2-3s (región cercana, reuso de sesión por trace, hot paths como Nova quedan locales); memoria-hora en sesiones vivas (cerrar sesiones agresivamente, jamás cruzar gates); debugging cross-plane (convención de correlación + conformance + taxonomía ok|red|infra generalizada); rebuild ARM64 del pipeline de media (última wave, con spike previo); límites del Gateway (search 25 TPM → solo design-time); IPR inglés-only y catálogo viejo (plug-in opcional, jamás política); blast radius AWS (cuenta separada para el plano de ejecución, IAM condition keys, budgets y alarmas).

## ADR-001: Strands Agents = Execution Adapter puro (directiva de Roberto, 2026-07-09)

**Decisión.** Strands Agents (SDK de AWS, Python `strands-agents` 1.46.0 production-stable) se integra EXCLUSIVAMENTE como adapter de ejecución dentro del ExecutionPlane: el "músculo" que corre UN step ya orquestado por invocación dentro del workload de AgentCore Runtime, y nada más. Toda orquestación, planificación, memoria, policies y decisión arquitectónica permanecen 100% soberanas en el Substrate. AgentCore sigue siendo la infraestructura y el entorno principal de agentes (Runtime lifecycle con `runtimeSessionId`, Identity, Policy, Observability); Strands no sobreescribe ni duplica nada de esa capa. Cualquier funcionalidad de Strands que implique orquestación, gestión de memoria o decisiones de diseño queda desactivada o aislada detrás de interfaces del Substrate. **Criterio rector: Strands = adapter de ejecución; Substrate + AgentCore = mente, memoria y arquitectura. Toda decisión que comprometa esta soberanía se rechaza.**

**Ubicación en capas.** ExecutionPlane (puerto) → adapter agentcore-runtime → imagen del workload → **StrandsExecutionAdapter**: un `Agent` EFÍMERO por request construido desde el envelope del Substrate `{step_id, model_id, system_prompt, messages, prompt, tool_names, schema_id, telemetry_ctx}`. El adapter rechaza payloads sin `model_id`, resuelve `tool_names` contra la allowlist local aprovisionada por el Tool Broker (los tools son wrappers NUESTROS que llaman al Gateway con el grant del step, nunca el McpClient autónomo de Strands), y devuelve solo resultado + stop reason + telemetría. No persiste nada.

**Guardrails obligatorios del adapter** (investigación verificada contra docs oficiales 2026-07-09, detalle completo en `strands-execution-adapter-research.md`):
1. `Agent` efímero DENTRO del handler por request; jamás un agent global a nivel módulo (el ejemplo oficial de AgentCore acumula `agent.messages` cross-request: fuga).
2. `model` SIEMPRE del payload; sin él Strands crea `BedrockModel()` default (Claude por región): rechazar el request.
3. `tools` SIEMPRE lista explícita (o `[]`). Con `tools=None` Strands habilita TODAS las tools disponibles. `load_tools_from_directory=False` siempre.
4. `session_manager=None` (nada de FileSessionManager/S3SessionManager/AgentCore Memory directo); `conversation_manager=NullConversationManager()` (el default SlidingWindow trunca contexto por su cuenta); `state={}` por request.
5. `retry_strategy=None`: el default de Strands reintenta hasta 6 veces con backoff; los retries los gobierna Inngest en el Substrate. Doble retry rompería la disciplina de idempotencia.
6. `callback_handler=None` en Python / `printer:false` en TS: el default imprime prompts y tool usage a stdout (fuga a logs).
7. PROHIBIDO importar `strands.multiagent.*` (Graph, Swarm, Workflow, Agents-as-Tools), `strands.multiagent.a2a.*` y `plugins`: son las primitivas de orquestación de Strands y compiten con el executor del Substrate.
8. El loop model→tool→model dentro del step se acota por contrato externo: tools mínimas del step, timeout y presupuesto del Substrate, hooks de auditoría (`BeforeToolCallEvent`/`AfterToolCallEvent`) exportando a OTel aprobado con redacción. Si un step exige una llamada determinista, la tool se ejecuta FUERA del agent loop.
9. Enforcement ejecutable en la suite de conformance: test que construye el adapter y asserta los flags del constructor + lint de imports que falla el build si aparece `strands.multiagent`, `a2a`, `SessionManager` o `plugins` en el workload.

**Impacto en waves.** Wave 1 (FORGE → Code Interpreter) no usa Strands. Strands entra en la Wave 3 como implementación del harness del workload Runtime (reemplaza al "harness nuestro mínimo" escrito a mano, ganando agent loop + streaming + OTel maduros sin ceder soberanía). SDK Python como base (TS no tiene paridad: session management y structured output sin soporte confirmado).

## Waves de adopción (tallaje, sin fechas)

- Wave 0 (S): ModelPlane L1 + provider bedrock-converse + política en Postgres. Valor inmediato, cero dependencia de AgentCore.
- Wave 1 (M): SandboxPort + adapter Code Interpreter para verify/execute de FORGE + suite de conformance. Saca del box la carga más riesgosa (código del modelo).
- Wave 2 (M): Tool Broker + Gateway + Identity + Policy para la primera clase de capability impura + escalera de safety + scopes en el build-gate.
- Wave 3 (L): adapter Runtime para agent-sessions + prototipo de correlación de telemetría (validación empírica, constraint documentado).
- Wave 4 (L): renders a Runtime solo si el spike ARM64 y la economía lo justifican; si no, quedan locales detrás del mismo puerto.

## Fuentes clave (verificadas 2026-07-08)

docs.aws.amazon.com/bedrock-agentcore (runtime-how-it-works, runtime-sessions, runtime-http-protocol-contract, bedrock-agentcore-limits, gateway-core-concepts, release-notes) · aws.amazon.com/bedrock/agentcore/pricing · docs de Intelligent Prompt Routing y pricing de Bedrock · model cards Sonnet 5/Fable 5 en Bedrock. Mapa del repo verificado contra agent-squad-app (llm.ts, runtime.ts, sandbox.ts, compute-worker.ts, substrate-spec, docs/ARCHITECTURE.md).