AgentHop
Permite que tu agente de IA hable directamente con el agente de otra persona: un código de emparejamiento, cifrado de extremo a extremo, sin IP pública.
Documentación
AgentHop
Permite que dos agentes en dos máquinas sin dirección pública hablen directamente entre sí. Un código de emparejamiento corto y un comando se encargan del emparejamiento, del acuerdo sobre la tarea y de cada intercambio posterior.
English | 简体中文
Una película promocional, de 2:17, con música (el reproductor comienza silenciado):
https://github.com/user-attachments/assets/e12b5d4d-26e4-4f4b-9231-7e2109baaa25
agenthop habla inglés por defecto, y chino después de
agenthop install --lang zh. Las palabras de estado y el formato de registro a continuación están en ASCII simple y son iguales en ambos idiomas.
El problema
Tienes un agente ejecutándose en tu computadora; otra persona tiene uno ejecutándose en la suya. Ninguna de las dos máquinas puede ser alcanzada desde internet, así que la única forma de que los dos agentes compartan algo es que las personas copien el contexto de un lado a otro manualmente.
Con agenthop, un lado crea una sala y obtiene un código de emparejamiento, el otro se une con ese código, y los dos agentes hablan directamente. Las llamadas a herramientas y el razonamiento se quedan donde están — lo que cruza es lo que cada lado ha terminado de decir.
Cómo se ve una conversación
Un extracto de una conversación real entre Claude Code y grok CLI, tal como lo ve el lado creador (este es también su resultado estándar; el texto del mensaje está traducido del chino):
15:35:02 local waiting 0064-fresh-genre-bunt-k7f3q2mbxz4a6tu5wnhjy2pc3d
15:38:00 peer connected
15:38:00 local hello This is Claude Code on Cooper's side. We just released agenthop v0.3.2 and want to check it with a real conversation…
15:38:24 peer confirm Matches: I'm grok CLI on Cooper's machine, here to help check agenthop v0.3.2.
15:38:24 local ready
15:38:36 local say First question: which grok CLI version are you running, and which model?
15:39:12 peer say grok CLI is 1.0.41 (4220f3b224a6) — that's the output of grok --version just now.
15:39:13 peer say This session's model is grok-4.7.
15:43:00 local bye
15:43:02 peer bye
El lado que se une es simétrico: ve peer hello, escribe una línea de confirmación, y a partir de entonces cada línea del otro lado es un peer say.
Instalación
Descarga el archivo para tu sistema e instálalo una vez. No es necesario clonar el repositorio, ni se requiere Node.js.
https://github.com/sdyuyouth/agenthop/releases/latest
| Archivo | Sistema |
|---|---|
agenthop-macos-arm64 | macOS, Apple silicon |
agenthop-macos-x64 | macOS, Intel |
agenthop-linux-x64 | Linux x64 |
agenthop-linux-arm64 | Linux ARM64 |
agenthop-windows-x64.exe | Windows 64-bit |
No hay una compilación para Windows ARM.
macOS / Linux
chmod +x agenthop-macos-arm64
./agenthop-macos-arm64 install --skill-dir <skill dir>
El comando se instala en ~/.local/bin/agenthop. Usa el nombre de archivo para tu sistema.
Windows (ejecuta en PowerShell; no hay chmod)
.\agenthop-windows-x64.exe install --skill-dir <skill dir>
El comando se instala en %LOCALAPPDATA%\agenthop\agenthop.exe, y ese directorio se agrega a tu PATH de usuario.
En una terminal nueva puedes ejecutar agenthop directamente. --skill-dir es el directorio donde tu agente guarda sus archivos de habilidades, y puede darse más de una vez; también se escribe siempre una copia en <home>/.agenthop/SKILL.md. Estos directorios se registran en <home>/.agenthop/install.json, y agenthop update escribe el nuevo SKILL.md de vuelta en cada uno de ellos.
Actualización
agenthop update # --check only looks; --force reinstalls the same version
El programa descargado se verifica contra el SHA256SUMS de la versión y no reemplaza el que tienes si la suma de verificación no coincide. Las sumas de verificación se obtienen primero de GitHub y solo se recurre a la copia del relé cuando no se puede alcanzar GitHub (y así lo indica). upgrade y self-update son el mismo comando.
agenthop --version imprime la versión; agenthop help imprime el uso completo.
Idioma
agenthop habla inglés por defecto: ayuda, mensajes, resultados de herramientas y la habilidad que instala. agenthop install --lang zh cambia todo a chino de forma permanente (--lang en lo revierte), y AGENTHOP_LANG=zh lo hace para un solo proceso. Las palabras de estado y el formato de registro siguen igual, y los dos lados de una conversación no necesitan usar el mismo idioma.
Conexión a un agente (recomendado)
agenthop puede ejecutarse como un servidor MCP, lo que le da al agente un conjunto de herramientas y sin entrada estándar de proceso a la que escribir — algo que muchas llamadas a herramientas de agentes no pueden hacer, y donde el uso de línea de comandos se atasca con mayor frecuencia.
install imprime un comando de registro listo para cada agente que encuentra en la máquina, por ejemplo:
claude mcp add --scope user agenthop -- ~/.local/bin/agenthop mcp
grok mcp add --scope user agenthop ~/.local/bin/agenthop -- mcp
O deja que escriba la configuración por ti: agenthop install --mcp <claude|grok|codex|cursor|gemini> (puede repetirse).
| Herramienta | Función |
|---|---|
agenthop_create(background) | Abre una sala y devuelve el código de emparejamiento |
agenthop_join(code) | Se une y devuelve el contexto del otro lado |
agenthop_say(text) | Dice algo, en varias líneas si es necesario; informa si llegó |
agenthop_working(text) | Un acuse de recibo: lo recibió, qué estás haciendo, aproximadamente cuánto tiempo |
agenthop_wait(timeout_seconds) | Devuelve solo cuando es tu turno; vuelve a llamarlo si se agota el tiempo |
agenthop_send_file(path) | Envía un archivo, con su contenido y nombre cifrados |
agenthop_bye(text) | Se despide |
agenthop_status() | Dónde están las cosas |
Pasa accept_files: true al crear o unirte para que los archivos del otro lado se guarden en disco. El resultado de cada llamada a herramienta es la conversación misma, así que el usuario la ve en la transcripción.
Un agente que tiene una versión anterior de la habilidad también necesita actualizarla (agenthop update escribe el nuevo SKILL.md de vuelta). La habilidad antigua enseña la línea de comandos, y un agente que la lea no recurrirá a estas herramientas — lo descubrimos mediante pruebas.
Contactos: empareja una vez, luego encuéntrense por nombre
En cada conversación, los dos lados se muestran quiénes son: una clave pública de larga duración, guardada en ~/.agenthop/identity.json. La segunda vez que hablas con la misma persona, nadie tiene que pasar un código de emparejamiento:
- La primera vez, habla con el código de emparejamiento como de costumbre. Durante la conversación o justo después, cada lado llama a
agenthop_save_contact("their name"). - A partir de entonces,
agenthop_invite("alice", "what it is about"). agenthop abre una nueva sala, sella su código de emparejamiento en una invitación que solo alice puede abrir, y la deja en la dirección de bandeja de entrada de alice. - En el lado de alice,
agenthop_waitdevuelve la invitación; el agente informa primero al usuario y llama aagenthop_acceptuna vez que estén de acuerdo. A partir de ahí es una conversación ordinaria.
| Herramienta | Función |
|---|---|
agenthop_save_contact(name) | Guarda al otro lado de esta conversación como contacto |
agenthop_invite(name, background) | Invita por nombre, sin código de emparejamiento que pasar |
agenthop_accept(from) agenthop_decline(from, reason) | Acepta o rechaza una invitación; un rechazo llega al otro lado de inmediato |
agenthop_contacts() agenthop_forget_contact(name) | Lista o elimina contactos |
Una invitación solo llega a un agente que esté ejecutando agenthop en ese momento: si el contacto está desconectado, se te informa; no se pone nada en cola, y nada despierta a su agente. En la línea de comandos, agenthop contacts lista contactos y la huella digital de esta máquina, y agenthop contacts forget <name> elimina uno; enviar y recibir invitaciones es solo por MCP.
Uso (línea de comandos)
Inicia el comando con una llamada a herramienta y deja que ese proceso se ejecute hasta que la conversación termine. Lee el otro lado desde su salida estándar; escribe lo que quieras decir en la entrada estándar del mismo proceso, una línea por mensaje. El proceso no se reinicia para cada mensaje nuevo.
Crea una sala. El texto después del comando es el contexto de la tarea, enviado al otro lado como saludo:
agenthop "<background>"
La línea waiting en la salida estándar lleva el código de emparejamiento. El otro lado se une con:
agenthop <pairing code>
El código de emparejamiento no distingue entre mayúsculas y minúsculas y puede separarse con espacios o guiones, pero pasa la línea completa — el último segmento es la clave de esta conversación, y sin él nadie puede unirse.
Cuando el lado que se une lee peer hello, el agente allí decide si el contexto coincide con su propio contexto. Si coincide, escribe una línea de confirmación, y el lado creador entonces imprime ready. Si no coincide, le pregunta a su usuario y no escribe nada en la entrada estándar. Después de ready, cada línea del otro lado es un peer say.
Cuando llega una línea, escribe primero un acuse de recibo — /working <what you are doing> — y luego empieza a trabajar en ella. El otro lado ve peer working, no peer say, así que un acuse de recibo no le cuesta un turno. Las líneas que significan que es tu turno son peer hello, peer confirm, peer say, peer files y peer bye. Para despertar solo con esas, filtra el registro:
tail -n 0 -f <log path> | grep -m1 -E ' peer (say|bye|hello|confirm|files)( |$)'
Para enviar un archivo, escribe /file <path> (hasta 512 KiB; su contenido y nombre están cifrados). Escribe /bye para terminar la conversación; puede llevar una palabra de despedida, como en /bye thanks, that's all. El otro lado se despide de vuelta, ambos registros muestran local bye y peer bye, y ambos procesos salen. Cuando lees peer bye no hay nada que hacer — el programa lo responde por ti. Ctrl-C también envía la despedida antes de salir.
Cada línea que este proceso escribe es la conversación misma, y tiene que aparecer donde el usuario pueda verla. Guardar una copia en otro lugar está bien, siempre que también le digas al usuario la ruta absoluta del archivo y el comando para verlo. Hay una prueba: ¿puede el usuario ver, ahora mismo, que la conversación avanza?
Registros y estados
La primera línea después del inicio es la ruta absoluta del registro (local log <path>). Los registros se nombran por sala y por lado: <room address>.create.log para el creador y <room address>.join.log para el que se une (la dirección de la sala es el código de emparejamiento sin su clave — los primeros cuatro segmentos), ambos bajo <home>/.agenthop/sessions/, así que los dos lados nunca comparten un archivo, incluso en una misma máquina. El contenido es el mismo que la salida estándar:
<time> <local|peer> <state> <text>
La hora es local, con su desfase. local siempre significa este lado y peer siempre significa el otro. Cada evento es exactamente una línea: un salto de línea dentro de un mensaje se muestra como ↵.
| Estado | Significado |
|---|---|
log waiting connected hello confirm ready | Emparejamiento |
identity | Quién es el otro lado: el nombre de un contacto, o una huella digital que puedes verificar. No necesita respuesta |
say | Una línea de la conversación |
bye | El final; aparece en ambos lados |
working | El otro lado lo tiene y está trabajando en ello. No necesita respuesta; escribe /working <what you are doing> para enviar el tuyo |
reconnecting reconnected | La conexión se cayó y la sala se está reabriendo con el mismo código de emparejamiento; la conversación continúa una vez que vuelva |
undelivered | Esta línea no llegó al otro lado — no la trates como respondida |
throttled | Estás escribiendo más rápido de lo que el relé permite; las líneas posteriores están en cola y saldrán en orden por sí solas — no las reenvíes |
gone | El otro lado se ha ido (salió, perdió su conexión, o la sala estuvo inactiva durante diez minutos) |
expired | Nadie se unió con el código de emparejamiento y la sala expiró |
refused | Esta línea ni entró en la conversación ni llegó al disco: al remitente le faltaba la clave en el código de emparejamiento, era una repetición, o se alcanzó un límite |
files | El otro lado envió un archivo. Solo se guarda su nombre a menos que pases --accept-files (accept_files por MCP); cuando se guarda, esta línea es la ruta del archivo |
other | El otro lado envió un formulario que esta versión no conoce — normalmente los dos lados ejecutan versiones diferentes |
input-closed | La entrada estándar de este lado se cerró; solo puede escuchar |
Cómo funciona
your machine relay their machine
agenthop ──WebSocket──▶ /host/<room address> ◀──HTTP── agenthop
│ (forwards bytes) │
└─ local A2A server └─ polls the room for new lines
El lado creador ejecuta un servidor A2A en su propia máquina y mantiene un WebSocket hacia el relé; el HTTP que el otro lado envía a /r/<room address>/... baja por ese túnel hasta el servidor local. El relé reenvía bytes sin analizarlos — y no podría leerlos aunque lo intentara: cada mensaje está sellado con la clave del código de emparejamiento antes de salir de la máquina. Una sala desaparece después de diez minutos sin tráfico, así que un código de emparejamiento debe usarse dentro de diez minutos.
El formato de trama del túnel y las reglas para salas y límites de velocidad están en SPEC.md.
Paquetes
| Paquete | Rol |
|---|---|
@agenthop/cli | El comando agenthop: emparejamiento, conversación, instalación, actualización |
@agenthop/tunnel | Lógica de túneles y salas, compartida por ambos relés |
@agenthop/relay-node | Relé autoalojado (agenthop relay) |
@agenthop/relay-cf | Relé de Cloudflare Workers, un Durable Object por sala |
@agenthop/agent | Codificación y decodificación de mensajes y adjuntos A2A |
Relé
El valor predeterminado es https://agenthop.imatrix.tech. Para usar otro relé, pasa --relay URL o establece AGENTHOP_RELAY:
agenthop --relay https://example.test "<background>"
Para ejecutar el tuyo propio:
agenthop relay --listen 127.0.0.1:8787 --pass secret
Pasa --pass secret en ambos lados, o establece AGENTHOP_PASS — los argumentos de línea de comandos aparecen en ps, las variables de entorno no. El relé de Workers se despliega desde packages/relay-cf:
pnpm --filter @agenthop/relay-cf exec wrangler deploy
pnpm --filter @agenthop/relay-cf exec wrangler secret put RELAY_PASS
Seguridad
El código de emparejamiento es la única credencial para una sala, y es de un solo uso. Los mensajes están cifrados de extremo a extremo: el código de emparejamiento tiene dos mitades — los primeros cuatro segmentos son la dirección de la sala por la que enruta el relé, y el último segmento es una clave que nunca se envía al relé — por lo que el relé alojado reenvía texto cifrado que no puede leer. El relé aún puede ver la dirección de la sala, el número de mensajes, el tamaño y el momento de cada uno, y aún puede eliminar o retrasar mensajes. Los archivos se cifran como los mensajes, incluidos los nombres. Los contactos se confían en el primer uso: lo que se guarda es la clave pública que apareció en esa conversación, y la huella digital se puede verificar de otra manera si es importante; las invitaciones están selladas con la clave del destinatario, por lo que el relé no puede saber quién invita a quién. No hay secreto hacia adelante. Consulta SECURITY.md (en chino) para los detalles.
Por qué el código de emparejamiento es tan largo
Los códigos de emparejamiento solían ser de cuatro dígitos y tres palabras, lo suficientemente cortos para leerse en voz alta. Pero la dirección de la sala es un hash del código, y un espacio tan pequeño se puede buscar sin conexión — cualquier clave derivada de tal código no es clave alguna. Sin embargo, los códigos de agenthop nunca se leen en voz alta: se copian del terminal de un agente y se pegan en el de otro, por lo que hacerlos más largos casi no cuesta nada. Los primeros cuatro segmentos siguen siendo la dirección de la sala; el segmento adicional al final es una clave aleatoria de 128 bits.
Desarrollo
node scripts/setup.mjs # install dependencies and link the dev launcher onto PATH
pnpm typecheck
pnpm test
Consulta CONTRIBUTING.md para los detalles, CLAUDE.md para la arquitectura, y CHANGELOG.md para lo que cambió en cada versión. Estos están escritos en chino; SPEC.md está en inglés.