Vinculaciones persistentes de ACP para canales de Discord y temas de Telegram
Estado: Borrador
Resumen
Introducir vinculaciones ACP persistentes que mapeen:
- Canales de Discord (e hilos existentes, donde sea necesario), y
- Temas de foro de Telegram en grupos/supergrupos (
chatId:topic:topicId)
a sesiones ACP de larga duración, con el estado de vinculación almacenado en entradas de nivel superior bindings[] usando tipos de vinculación explícitos.
Esto hace que el uso de ACP en canales de mensajería de alto tráfico sea predecible y duradero, para que los usuarios puedan crear canales/temas dedicados como codex, claude-1 o claude-myrepo.
Por qué
El comportamiento actual de ACP vinculado a hilos está optimizado para flujos de trabajo efímeros de hilos de Discord. Telegram no tiene el mismo modelo de hilos; tiene temas de foro en grupos/supergrupos. Los usuarios quieren “espacios de trabajo” ACP estables y siempre activos en superficies de chat, no solo sesiones temporales de hilos.
Objetivos
- Soportar vinculación ACP duradera para:
- Canales/hilos de Discord
- Temas de foro de Telegram (grupos/supergrupos)
- Hacer que la fuente de verdad de vinculación sea dirigida por configuración.
- Mantener el comportamiento de
/acp,/new,/reset,/focusy entrega consistente entre Discord y Telegram. - Preservar los flujos de vinculación temporal existentes para uso ad-hoc.
No objetivos
- Rediseño completo de los internos de runtime/sesión ACP.
- Eliminar los flujos de vinculación efímera existentes.
- Expandir a todos los canales en la primera iteración.
- Implementar temas de mensajes directos de Telegram (
direct_messages_topic_id) en esta fase. - Implementar variantes de temas de chat privado de Telegram en esta fase.
Dirección de UX
1) Dos tipos de vinculación
- Vinculación persistente: guardada en la configuración, reconciliada al inicio, pensada para canales/temas de “workspace con nombre”.
- Vinculación temporal: solo en runtime, expira por política de inactividad/edad máxima.
2) Comportamiento de comandos
/acp spawn ... --thread here|auto|offsigue disponible.- Agregar controles explícitos de ciclo de vida de vinculación:
/acp bind [session|agent] [--persist]/acp unbind [--persist]/acp statusincluye si la vinculación espersistentotemporary.
- En conversaciones vinculadas,
/newy/resetreinician la sesión ACP vinculada y mantienen la vinculación adjunta.
3) Identidad de conversación
- Usar IDs de conversación canónicos:
- Discord: ID de canal/hilo.
- Tema de Telegram:
chatId:topic:topicId.
- Nunca indexar vinculaciones de Telegram solo por ID de tema.
Modelo de configuración (Propuesto)
Unificar la configuración de enrutamiento y vinculación ACP persistente en bindings[] de nivel superior con discriminador type explícito:
{
"agents": {
"list": [
{
"id": "main",
"default": true,
"workspace": "~/.openclaw/workspace-main",
"runtime": { "type": "embedded" },
},
{
"id": "codex",
"workspace": "~/.openclaw/workspace-codex",
"runtime": {
"type": "acp",
"acp": {
"agent": "codex",
"backend": "acpx",
"mode": "persistent",
"cwd": "/workspace/repo-a",
},
},
},
{
"id": "claude",
"workspace": "~/.openclaw/workspace-claude",
"runtime": {
"type": "acp",
"acp": {
"agent": "claude",
"backend": "acpx",
"mode": "persistent",
"cwd": "/workspace/repo-b",
},
},
},
],
},
"acp": {
"enabled": true,
"backend": "acpx",
"allowedAgents": ["codex", "claude"],
},
"bindings": [
// Vinculaciones de ruta (comportamiento existente)
{
"type": "route",
"agentId": "main",
"match": { "channel": "discord", "accountId": "default" },
},
{
"type": "route",
"agentId": "main",
"match": { "channel": "telegram", "accountId": "default" },
},
// Vinculaciones ACP persistentes de conversación
{
"type": "acp",
"agentId": "codex",
"match": {
"channel": "discord",
"accountId": "default",
"peer": { "kind": "channel", "id": "222222222222222222" },
},
"acp": {
"label": "codex-main",
"mode": "persistent",
"cwd": "/workspace/repo-a",
"backend": "acpx",
},
},
{
"type": "acp",
"agentId": "claude",
"match": {
"channel": "discord",
"accountId": "default",
"peer": { "kind": "channel", "id": "333333333333333333" },
},
"acp": {
"label": "claude-repo-b",
"mode": "persistent",
"cwd": "/workspace/repo-b",
},
},
{
"type": "acp",
"agentId": "codex",
"match": {
"channel": "telegram",
"accountId": "default",
"peer": { "kind": "group", "id": "-1001234567890:topic:42" },
},
"acp": {
"label": "tg-codex-42",
"mode": "persistent",
},
},
],
"channels": {
"discord": {
"guilds": {
"111111111111111111": {
"channels": {
"222222222222222222": {
"enabled": true,
"requireMention": false,
},
"333333333333333333": {
"enabled": true,
"requireMention": false,
},
},
},
},
},
"telegram": {
"groups": {
"-1001234567890": {
"topics": {
"42": {
"requireMention": false,
},
},
},
},
},
},
}
Ejemplo mínimo (sin sobreescrituras ACP por vinculación)
{
"agents": {
"list": [
{ "id": "main", "default": true, "runtime": { "type": "embedded" } },
{
"id": "codex",
"runtime": {
"type": "acp",
"acp": { "agent": "codex", "backend": "acpx", "mode": "persistent" },
},
},
{
"id": "claude",
"runtime": {
"type": "acp",
"acp": { "agent": "claude", "backend": "acpx", "mode": "persistent" },
},
},
],
},
"acp": { "enabled": true, "backend": "acpx" },
"bindings": [
{
"type": "route",
"agentId": "main",
"match": { "channel": "discord", "accountId": "default" },
},
{
"type": "route",
"agentId": "main",
"match": { "channel": "telegram", "accountId": "default" },
},
{
"type": "acp",
"agentId": "codex",
"match": {
"channel": "discord",
"accountId": "default",
"peer": { "kind": "channel", "id": "222222222222222222" },
},
},
{
"type": "acp",
"agentId": "claude",
"match": {
"channel": "discord",
"accountId": "default",
"peer": { "kind": "channel", "id": "333333333333333333" },
},
},
{
"type": "acp",
"agentId": "codex",
"match": {
"channel": "telegram",
"accountId": "default",
"peer": { "kind": "group", "id": "-1009876543210:topic:5" },
},
},
],
}
Notas:
bindings[].typees explícito:route: enrutamiento normal del agente.acp: vinculación persistente del harness ACP para una conversación coincidente.
- Para
type: "acp",match.peer.ides la clave de conversación canónica:- Canal/hilo de Discord: ID de canal/hilo sin procesar.
- Tema de Telegram:
chatId:topic:topicId.
bindings[].acp.backendes opcional. Orden de fallback del backend:bindings[].acp.backendagents.list[].runtime.acp.backendacp.backendglobal
mode,cwdylabelsiguen el mismo patrón de sobreescritura (sobreescritura de vinculación -> valor por defecto del runtime del agente -> comportamiento global/por defecto).- Mantener los existentes
session.threadBindings.*ychannels.discord.threadBindings.*para políticas de vinculación temporal. - Las entradas persistentes declaran el estado deseado; el runtime reconcilia con las sesiones/vinculaciones ACP reales.
- Una vinculación ACP activa por nodo de conversación es el modelo previsto.
- Compatibilidad hacia atrás:
typeausente se interpreta comoroutepara entradas legacy.
Selección de backend
- La inicialización de sesión ACP ya usa la selección de backend configurada durante el spawn (
acp.backendhoy). - Esta propuesta extiende la lógica de spawn/reconciliación para preferir sobreescrituras de vinculación ACP tipadas:
bindings[].acp.backendpara sobreescritura local de conversación.agents.list[].runtime.acp.backendpara valores por defecto por agente.
- Si no existe sobreescritura, mantener el comportamiento actual (valor por defecto
acp.backend).
Ajuste de arquitectura en el sistema actual
Reutilizar componentes existentes
SessionBindingServiceya soporta referencias de conversación agnósticas al canal.- Los flujos de spawn/bind ACP ya soportan vinculación mediante APIs de servicio.
- Telegram ya transporta contexto de tema/hilo mediante
MessageThreadIdychatId.
Componentes nuevos/extendidos
- Adaptador de vinculación de Telegram (paralelo al adaptador de Discord):
- Registrar adaptador por cuenta de Telegram,
- resolver/listar/vincular/desvincular/touch por ID de conversación canónico.
- Resolvedor/índice de vinculación tipado:
- Dividir
bindings[]en vistasrouteyacp, - mantener
resolveAgentRoutesolo en vinculacionesroute, - resolver intención ACP persistente solo desde vinculaciones
acp.
- Dividir
- Resolución de vinculación entrante para Telegram:
- Resolver sesión vinculada antes de la finalización de ruta (Discord ya hace esto).
- Reconciliador de vinculaciones persistentes:
- Al inicio: cargar vinculaciones
type: "acp"configuradas de nivel superior, asegurar que las sesiones ACP existen, asegurar que las vinculaciones existen. - En cambio de configuración: aplicar deltas de forma segura.
- Al inicio: cargar vinculaciones
- Modelo de transición:
- No se lee fallback de vinculación ACP local al canal,
- Las vinculaciones ACP persistentes se obtienen solo de entradas
bindings[].type="acp"de nivel superior.
Entrega por fases
Fase 1: Base del esquema de vinculación tipada
- Extender el esquema de configuración para soportar el discriminador
bindings[].type:route,acpcon objeto de sobreescrituraacpopcional (mode,backend,cwd,label).
- Extender el esquema de agentes con descriptor de runtime para marcar agentes nativos ACP (
agents.list[].runtime.type). - Agregar división parser/indexador para vinculaciones route vs ACP.
Fase 2: Resolución en runtime + paridad Discord/Telegram
- Resolver vinculaciones ACP persistentes desde entradas
type: "acp"de nivel superior para:- Canales/hilos de Discord,
- Temas de foro de Telegram (IDs canónicos
chatId:topic:topicId).
- Implementar adaptador de vinculación de Telegram y paridad de sobreescritura de sesión vinculada entrante con Discord.
- No incluir variantes de temas directos/privados de Telegram en esta fase.
Fase 3: Paridad de comandos y resets
- Alinear el comportamiento de
/acp,/new,/resety/focusen conversaciones vinculadas de Telegram/Discord. - Asegurar que la vinculación sobrevive a los flujos de reset según la configuración.
Fase 4: Endurecimiento
- Mejores diagnósticos (
/acp status, logs de reconciliación al inicio). - Manejo de conflictos y chequeos de salud.
Barreras y políticas
- Respetar la habilitación de ACP y restricciones de sandbox exactamente como hoy.
- Mantener el alcance explícito de cuenta (
accountId) para evitar filtraciones entre cuentas. - Fallar cerrado ante enrutamiento ambiguo.
- Mantener el comportamiento de política de mención/acceso explícito por configuración de canal.
Plan de testing
- Unitario:
- Normalización de ID de conversación (especialmente IDs de temas de Telegram),
- Rutas de crear/actualizar/eliminar del reconciliador,
- Flujos de
/acp bind --persisty unbind.
- Integración:
- Tema entrante de Telegram -> resolución de sesión ACP vinculada,
- Canal/hilo entrante de Discord -> precedencia de vinculación persistente.
- Regresión:
- Las vinculaciones temporales siguen funcionando,
- Los canales/temas no vinculados mantienen el comportamiento de enrutamiento actual.
Preguntas abiertas
- ¿Debería
/acp spawn --thread autoen temas de Telegram usarherepor defecto? - ¿Las vinculaciones persistentes deberían siempre omitir la restricción de mención en conversaciones vinculadas, o requerir
requireMention=falseexplícito? - ¿Debería
/focusganar--persistcomo alias de/acp bind --persist?
Despliegue
- Publicar como opt-in por conversación (entrada
bindings[].type="acp"presente). - Empezar solo con Discord + Telegram.
- Agregar documentación con ejemplos para:
- “un canal/tema por agente”
- “múltiples canales/temas por el mismo agente con diferente
cwd” - “patrones de nombres de equipo (
codex-1,claude-repo-x)”.