Cuando escribí sobre MCP, A2A y AHP dejé una idea a medio demostrar: que AHP es la capa que nadie nota. MCP conecta herramientas. A2A delega entre agentes. Y AHP resuelve otra cosa distinta, mucho menos glamurosa y por eso mismo invisible: cómo se sirve y se sincroniza una única sesión de agente a varios clientes a la vez.
El marketing de agentes vive en las dos primeras siglas. La tercera es fontanería. Pero es justo la fontanería la que decide si tu sesión es algo efímero atado a una ventana del editor o un recurso de servidor que dos editores pueden compartir.
Así que hice lo de siempre: en vez de defenderlo con un diagrama bonito, lo compilé. Este artículo acompaña a un repo que reproduce las cuatro ideas de carga del Agent Host Protocol en unas 300 líneas. No es la implementación de referencia de Microsoft. Es un modelo didáctico — deliberadamente pequeño para que la mecánica sea legible y, sobre todo, para que puedas verlo funcionar.
La tesis, en una frase
El host tiene el estado. Los clientes son vistas.
Esa frase es todo el diseño. Léela otra vez, porque el resto del artículo es simplemente esa frase desplegada:
- El host es la fuente de verdad. Los clientes nunca guardan estado autoritativo.
- Estado inmutable, reducers puros. El estado solo avanza con funciones
(state, action) => nextState, sin efectos secundarios. - Reconciliación write-ahead. Cada mutación recibe un número de secuencia; los clientes aplican las mismas acciones en el mismo orden y no pueden divergir.
- Canales direccionados por URI, snapshot + actions. Un cliente nuevo recibe un snapshot; uno que reconecta reproduce solo las acciones que se perdió desde su último
seq.
Cuatro ideas. Ni una más. Todo lo demás es consecuencia.
Por qué el estado no debería perderse nunca
WAL (Write-Ahead Log): apuntas el cambio en un registro ordenado antes de aplicarlo. Así, si algo falla, reconstruyes el estado reproduciendo el log en vez de fiarte de la memoria. Es lo que las bases de datos llevan usando décadas — aquí, aplicado a la sesión de un agente.
Aquí conviene parar, porque es la pregunta que casi nadie hace: ¿por qué tanta ceremonia alrededor del estado? WAL, secuencias monótonas, reducers puros, replay… parece sobreingeniería para «acordarse de una conversación». No lo es. Y para verlo hay que entender qué es el estado en un agente de código.
El estado de una sesión no es el texto del chat. Es el contexto acumulado: los turnos previos, los ficheros que el agente ya ha leído, los cambios que ha propuesto, las herramientas que ha invocado, lo que sabe y lo que ya ha descartado. Es, literalmente, el trabajo hecho. Perder ese estado no es perder un historial bonito: es tirar el razonamiento por el que ya has pagado —en tiempo, en tokens y en atención— y obligar al agente (y a ti) a reconstruirlo desde cero.
Piénsalo como en la contabilidad de doble entrada, o en un log de transacciones de base de datos. No guardas el saldo final y confías en él; guardas cada movimiento en orden, y el saldo es lo que resulta de aplicarlos. Si el saldo se corrompe, lo reconstruyes desde el log. AHP hace exactamente eso con una sesión de agente: el estado nunca es un valor que «se tiene y se puede perder», es la consecuencia determinista de una secuencia de acciones que sí se conserva. Mientras tengas el WAL, tienes el estado. Siempre.
Lo que ganas
Reconstruibilidad (creo que esta palabra no existe). Un cliente se cae, la red parpadea, cierras el portátil. Da igual: al volver, sinceSeq reproduce lo que faltaba y la vista queda idéntica. El estado no vivía en el cliente, así que no se fue con él.
Imposibilidad de divergir. Si dos vistas aplican las mismas acciones en el mismo orden a partir del mismo punto, llegan al mismo sitio. No es que «probablemente coincidan»: es que no pueden no coincidir. La consistencia deja de ser algo que esperas y pasa a ser algo que la aritmética garantiza. Por eso el verificador puede afirmar byte-identical y fallar si miente.
Auditoría gratis. Si toda mutación pasa por un log ordenado antes del fan-out, ese log es la traza de auditoría. Quién pidió qué, en qué orden, qué produjo el agente y cuándo. No hay que instrumentar nada aparte: la propiedad que te da consistencia te da trazabilidad por el mismo precio. Y para alguien que se pasa el día hablando de gobierno y supply chain, ese «gratis» no es menor.
Viaje en el tiempo y confianza para experimentar. Con estado inmutable y acciones ordenadas, «volver a un punto» es truncar la secuencia, no restaurar un backup frágil. Puedes rebobinar, ramificar, comparar. El estado deja de ser algo que temes tocar.
Lo que cuesta (porque todo cuesta algo)
No vendo esto como gratis, que es la parte que el marketing se salta. Tratar el estado como un log inmutable tiene contrapartidas reales:
El log crece, y hay que gestionarlo. Una sesión larga acumula miles de acciones. En serio necesitas compaction (colapsar el histórico en un snapshot periódico y podar el WAL) o el replay de un cliente que reconecta tras horas se vuelve caro. En la demo el WAL es en memoria y no importa; en producción, importa mucho.
Todo tiene que pasar por el reducer. La disciplina que da la garantía es también su rigidez: no puedes «tocar el estado a mano». Cada cambio es una acción con su reducer puro. Es más código y más ceremonia que un state.mutate(), y esa fricción es el peaje que pagas por no poder divergir. Merece la pena, pero es un peaje.
El host es punto único de verdad… y de fallo. Si el estado vive en el host, el host tiene que estar altamente disponible, o replicado, o con su propio log persistido. Centralizar la verdad simplifica la consistencia y concentra el riesgo operativo. Es un intercambio consciente, no una comida gratis.
Latencia de reconciliación. Un cliente que vuelve tras estar mucho tiempo fuera puede tener que reproducir una montaña de acciones antes de estar «al día». Es más barato que un snapshot completo en el caso normal, pero el peor caso existe y hay que diseñar para él (de nuevo: compaction).
La conclusión: es la de siempre en arquitectura, no hay diseños sin coste, hay costes que eliges pagar. AHP elige pagar gestión de log y centralización a cambio de consistencia demostrable, reconstruibilidad y auditoría. Para una sesión de agente —donde el estado es el trabajo y perderlo duele— es un intercambio que sale a cuenta casi siempre.
Qué ves realmente
Dos clientes —cada uno haciendo de una ventana de VS Code Agent Sessions— se conectan al mismo host y al mismo id de sesión. Escribes un prompt en la Ventana A y la respuesta del agente aparece en las dos ventanas a la vez, token a token, idéntica.
=== WindowA | session 4f3a… | seq 12 === === WindowB | session 4f3a… | seq 12 ===
you/other> explain ahp you/other> explain ahp
agent> AHP puts the session on the host: … agent> AHP puts the session on the host: …
[agent is thinking] [agent is thinking]
No son dos agentes respondiendo. Es una sesión, un agente residente en el host, abierto en abanico como acciones ordenadas. La diferencia parece sutil y no lo es: si el estado viviera en las ventanas, tarde o temprano divergirían. Aquí no pueden, por construcción.
La parte que me interesa: la evidencia, no la promesa
Podría contarte que reconecta bien. Prefiero enseñártelo. El repo trae un verificador —npm run verify:reconnect— que levanta el host compilado en un puerto libre y conecta clientes WebSocket reales. Este es un extracto de una ejecución real:
[4/9] LIVE SHARING
prompt visible in WindowB: true
streamed answer visible in both windows: true
both windows at seq: 51
[5/9] WINDOW B OFFLINE
retained local view at seq: 51
[7/9] WINDOW B RECONNECTS
subscribe sinceSeq: 51
replayed session actions: 72
caught up to global seq: 124
second snapshot received: false
[9/9] CONSISTENCY CHECKS
shared session byte-identical: true
final WindowA seq: 124
final WindowB seq: 124
POC result: PASS
Fíjate en tres cosas, porque ahí está todo:
El fanout hace visible la frontera de propiedad. El host recibe cada prompt, lo reduce a acciones secuenciadas en el WAL y reporta fanout=2 mientras ambas vistas están suscritas. Cuando la Ventana B se desconecta, el siguiente turno reporta fanout=1. La propiedad no es una metáfora: es un número en el log.
second snapshot received: false. Cuando B vuelve, pide sinceSeq: 51 y recibe 72 acciones que le faltaban, no otro snapshot. Reconciliación write-ahead de manual: reproduces solo lo nuevo, no vuelves a empezar.
shared session byte-identical: true. El WAL global contenía además una acción session/created no relacionada en seq 52. Avanza la secuencia global pero queda excluida del replay del canal de B. Por eso las comprobaciones prueban dos cosas a la vez: aislamiento de canal y una sesión compartida idéntica byte a byte.
Esa igualdad es la garantía de consistencia que te da AHP. No un adjetivo en un README: un aserto que falla si mientes.
El truco de sinceSeq, paso a paso
Con las dos ventanas en la misma sesión:
# En la Ventana B: cierra el transporte pero conserva su vista local.
/offline
# En la Ventana A: manda uno o más prompts mientras B está fuera.
explain sync
# En la Ventana B: reconecta desde su último número de secuencia.
/online
B se pone al día sin recibir otro snapshot y lo cuenta en su línea de estado. Un matiz importante que aprendí montándolo: mantén vivo el proceso del cliente durante la prueba. sinceSeq asume que el cliente todavía tiene la vista local que produjo el snapshot anterior. Un proceso nuevo de cero, correctamente, arranca desde un snapshot fresco.
Las tres configuraciones (y cuál es esta)
La matriz de self-hosting de Microsoft tiene tres formas. El repo cubre las dos primeras en local y la tercera con una VM:
| Configuración | Dónde corre el host | En este repo |
|---|---|---|
| VS Code + agent host local | misma máquina que el editor | npm run host en tu equipo |
| App de Agentes + agent host local | misma máquina, headless | segundo cliente en el mismo equipo |
| VS Code / App + agent host remoto (SSH / dev tunnel) | otra máquina | la VM de Bicep en infra/ |
La tercera es la interesante, y es donde aterriza el argumento que de verdad me importa: el bucle del agente y el workspace viven en la VM; el cliente es una vista fina que se asoma por un túnel. Las ediciones de fichero y el razonamiento del agente ocurren en el servidor. Nada sobre cómo trabaja el agente se materializa en el cliente.
Un caso concreto: preview contra stable, misma sesión
Aquí está la razón práctica para que te importe «una sesión, muchos clientes». Puedes correr dos builds distintos de VS Code contra la misma sesión —pongamos Insiders (preview) y Stable— para comparar comportamiento sin bifurcar el estado.
Como el host es el dueño del estado y ambos clientes son vistas de reducer puro, la sesión es idéntica en las dos ventanas. Cualquier diferencia que observes es del cliente, no de la conversación. Que es exactamente lo que quieres cuando haces A/B de una preview: aislar la variable. En la demo finjo los dos builds con dos procesos ahp-client; cámbialos por Insiders + Stable reales contra la VM y la historia es la misma.
Ese es el premio de sacar la sesión de la ventana: la sesión deja de ser «algo transitorio atado a un editor» y pasa a ser un recurso.
De dónde sale el argumento de la propiedad intelectual (IP)
La razón por la que «el host tiene el estado, el cliente es una vista» importa más allá de la sincronización elegante: el bucle del agente, el prompt, el cableado de herramientas —las partes que son de verdad tu producto— nunca salen del host.
Mueve ese host a un sandbox aislado por VM estilo Foundry, con un registro privado y una identidad dedicada, y el editor renderiza turnos mientras el «cómo» se queda en tu tenant. Esa es la versión endurecida, y vive en un repo aparte: contenedor firmado en un ACR privado, sandbox aislado por VM, identidad Entra dedicada. Este repo es la mecánica del protocolo; aquel es el sobre de gobierno.
Por eso, en la demo, insisto en algo que parece higiene y es tesis:
No publiques el puerto 4611 abierto a internet. El NSG de la demo lo abre por comodidad. En la práctica, mantén el host privado y llega a él por un dev tunnel de VS Code o un forward SSH (
ssh -L 4611:localhost:4611 azureuser@<ip>). No es solo higiene — es el argumento de IP entero: si el host es privado y el cliente solo tiene una vista, el cliente nunca cruza a tu infraestructura.
Advertencia
No vendo humo, así que lo digo claro: AHP está diseñado para hospedar harnesses de agentes de código, no para que un endpoint de Foundry actúe como servidor AHP. Unir las dos cosas necesita un shim (o que Microsoft apunte el agent host a un harness de Foundry). No es un camino soportado hoy. Es una dirección que la arquitectura hace plausible, y esa distinción importa.
Mi punto de vista personal
Llevamos meses hablando de agentes como si el problema fuera darles herramientas (MCP) o hacer que hablen entre ellos (A2A). Y son problemas reales. Pero hay uno anterior, más aburrido y más determinante: quién es el dueño de la sesión.
Mientras la sesión vive en tu ventana, el estado es frágil, la confidencialidad es un deseo y comparar dos clientes es imposible sin bifurcar la realidad. En cuanto la sesión es un recurso de servidor, las tres cosas se ordenan solas: la sincronización deja de ser magia, la IP tiene dónde vivir, y «dos ventanas, una conversación» pasa de eslogan a aserto verificable.
AHP no es la sigla que va a llenar titulares. Es la que decide si las otras dos se pueden gobernar.
Y esa, para mí, es exactamente la clase de capa que merece la pena entender: la que nadie mira, porque es la que lo sostiene todo.





