El directorio que existe en tu máquina y no en la del agente

Nuestro puente de ACP nos salió barato porque no traduce: mueve líneas de JSON-RPC entre un WebSocket y la entrada estándar de un proceso, y nada más. Ésa era la tesis. Hoy encontramos el lugar exacto donde deja de ser cierta.
El síntoma que no tenía sentido
Conectamos VS Code —la extensión ACP Client, 0.2.0— contra un agente Goose v1.46.0 que
corre en una microVM, a través de nuestro puente ghosty-acp, que es lo que convierte
la dirección wss:// en un comando que un editor sí sabe lanzar.
Funcionaba. Y no funcionaba. Con VS Code abierto sin carpeta, la sesión arrancaba perfecta. Abriendo cualquier carpeta, moría al conectar con esto:
Failed to connect: Invalid params
Nada más. Ni el campo, ni la ruta, ni una pista.
El diagnóstico salió del canal Output
El error de la UI no sirve. La traza sí. En Output → ACP Client estaban los dos mensajes que importaban:
Lo que mandó el editor:
Lo que contestó el agente:
initialize había respondido OK. El que revienta es session/new, y revienta por un
solo campo: cwd.
El editor nombra una carpeta de tu disco. El agente la busca en el suyo.
La extensión lo arma así — cwd: t || void 0. Sin carpeta abierta manda undefined y
el agente usa su propio directorio de trabajo. Con carpeta abierta manda una ruta de
macOS. El agente la busca dentro de su propia microVM, no la encuentra, y contesta
invalid directory path. Por eso el caso raro funcionaba y el caso normal no.
La premisa que ACP nunca hizo explícita
ACP se diseñó para que el cliente lance al agente como proceso hijo. Padre e hijo en la
misma máquina, con el mismo filesystem. Bajo esa premisa cwd es trivial: es una ruta
que los dos ven.
Nosotros rompimos la premisa a propósito —el agente vive aislado, con su propio disco y
su propia terminal— y el protocolo no tiene dónde decirlo. cwd no es opcional ni
negociable: viaja siempre, y siempre es literal.
No es un problema exclusivo de correr en la nube. Le pasa igual a quien tiene el editor
en Windows y el agente dentro de WSL: mismo mensaje, mismo campo. En hermes-agent está
reportado () y
exactamente como
había que arreglarlo: traduciendo el cwd al cruzar. Y en la spec hay un
que ronda el mismo terreno sin cerrarlo.
Quién traduce: el que cruza la frontera
La respuesta no es "que el editor sepa de cajas" ni "que el agente adivine". Ninguno de los dos tiene por qué enterarse de que hay una red en medio; ése es el valor entero de un protocolo.
El que sabe que hay una frontera es el puente, porque él es la frontera. Es el único punto del camino donde una ruta del lado del usuario y una ruta del lado del agente existen simultáneamente en el mismo proceso.
Y no es la primera vez que lo hacemos. Nuestro relé ya reescribe session/new para
inyectarle los mcpServers que le corresponden a ese agente. Ese mensaje es el único
lugar donde el transporte se permite meter mano: pasa una sola vez por sesión, es
declarativo, y todo lo que trae describe el entorno, no el trabajo. Lo que viene
después —los prompts, los eventos, el razonamiento en vivo— sigue pasando intacto.
cwd es exactamente esa clase de campo. Estaba en el mensaje correcto desde el
principio; sólo faltaba mirarlo.
La ruta local no desaparece: vuelve por el canal donde sí tiene sentido, fs/*, que
resuelve el editor en tu máquina. Lo único que se traduce es el directorio de trabajo.
Lo que aprendimos de la forma en que falló
Tres cosas que nos costaron el rato, en orden de utilidad:
El caso que funciona puede ser el degradado. "Sin carpeta abierta sí jala" nos hizo
buscar el bug en la conexión durante un buen rato. undefined no probaba que el
transporte estuviera bien: probaba que el agente estaba cayendo a su propio directorio
porque el editor no le había dicho nada.
Un error de protocolo pierde el detalle al subir a la UI. El JSON-RPC traía
"data":"invalid directory path", que apunta directo al campo. La barra de VS Code
mostró Invalid params. Cuando un cliente ACP falla, la traza cruda es el único lugar
donde vive el diagnóstico.
Un protocolo con supuestos implícitos se rompe justo donde los estiras. ACP no
declara en ninguna parte "cliente y agente comparten filesystem", pero está horneado en
cwd, en fs/read_text_file y en terminal/*. Los últimos dos son capacidades que el
cliente declara —si no las tiene, no se usan—. cwd no: viaja siempre.
El veredicto
Un puente que "sólo cambia el cable" es un buen objetivo y una descripción incompleta. En cuanto los dos extremos dejan de compartir máquina, alguien tiene que traducir lo que sólo tiene sentido de un lado. Son pocos campos —hoy, uno— y viven en un solo mensaje. Vale la pena saber cuál es antes de que un usuario abra una carpeta.