Crear complementos de canal
Esta guía explica cómo crear un complemento de canal que conecte OpenClaw con una plataforma de mensajería. Al final tendrás un canal funcional con seguridad de MD, emparejamiento, hilos de respuesta y mensajería saliente.
Cómo funcionan los complementos de canal
Sección titulada «Cómo funcionan los complementos de canal»Los plugins de canal no necesitan sus propias herramientas de envío/edición/reacción. OpenClaw mantiene una
herramienta message compartida en el núcleo. Tu plugin es dueño de:
- Config - resolución de cuenta y asistente de configuración
- Seguridad - política de MD y listas de permitidos
- Emparejamiento - flujo de aprobación de MD
- Gramática de sesión - cómo los IDs de conversación específicos del proveedor se asignan a chats base, IDs de hilo y respaldos principales
- Saliente - envío de texto, medios y encuestas a la plataforma
- Hilos - cómo se organizan las respuestas en hilos
- Latido de escritura - señales opcionales de escritura/ocupado para objetivos de entrega de latido
El núcleo es dueño de la herramienta de mensaje compartida, el cableado del prompt, la forma de la clave de sesión externa,
la contabilidad :thread: genérica y el despacho.
Los nuevos complementos de canal también deben exponer un adaptador message con
defineChannelMessageAdapter de openclaw/plugin-sdk/channel-outbound. El
adaptador declara qué capacidades duraderas de envío final soporta realmente el
transporte nativo y dirige los envíos de texto/medios a las mismas funciones de
transporte que el adaptador heredado outbound. Solo declare una capacidad cuando una
prueba de contrato demuestre el efecto secundario nativo y el recibo devuelto.
Para ver el contrato completo de la API, ejemplos, matriz de capacidades,
reglas de recibo, finalización de vista previa en vivo, política de acuse de
recibo, pruebas y tabla de migración, consulte Channel outbound API.
Si el adaptador outbound existente ya tiene los métodos de envío y los
metadatos de capacidad correctos, use createChannelMessageAdapterFromOutbound(...) para
derivar el adaptador message en lugar de escribir manualmente otro puente.
Los envíos del adaptador deben devolver valores MessageReceipt. Cuando el código de
compatibilidad aún necesita identificadores heredados, derívelos con listMessageReceiptPlatformIds(...)
o resolveMessageReceiptPrimaryId(...) en lugar de mantener campos messageIds paralelos
en el nuevo código del ciclo de vida.
Los canales con capacidad de vista previa también deben declarar message.live.capabilities con
el ciclo de vida en vivo exacto que poseen, como draftPreview,
previewFinalization, progressUpdates, nativeStreaming, o
quietFinalization. Los canales que finalicen una vista previa de borrador in situ
también deben declarar message.live.finalizer.capabilities, como finalEdit,
normalFallback, discardPending, previewReceipt, y
retainOnAmbiguousFailure, y enrutar la lógica en tiempo de ejecución a través de
defineFinalizableLivePreviewAdapter(...) más
deliverWithFinalizableLivePreviewAdapter(...). Mantenga esas capacidades respaldadas
por pruebas verifyChannelMessageLiveCapabilityAdapterProofs(...) y
verifyChannelMessageLiveFinalizerProofs(...) para que la vista previa nativa,
el progreso, la edición, la reserva/retención, la limpieza y el comportamiento de
recibo no puedan desviarse silenciosamente.
Los receptores entrantes que difieren los reconocimientos de la plataforma deben
declarar message.receive.defaultAckPolicy y supportedAckPolicies en lugar de ocultar el
tiempo de reconocimiento en el estado local del monitor. Cubra cada política
declarada con verifyChannelMessageReceiveAckPolicyAdapterProofs(...).
Los asistentes de respuesta heredados como createChannelTurnReplyPipeline,
dispatchInboundReplyWithBase y recordInboundSessionAndDispatchReply
permanecen disponibles para los despachadores de compatibilidad. No use esos nombres para el nuevo
código de canal; los nuevos complementos deben comenzar con el adaptador message, recibos y
asistentes del ciclo de vida de recepción/envío en openclaw/plugin-sdk/channel-outbound.
Los canales que migren la autorización de entrada pueden usar la subruta experimental
openclaw/plugin-sdk/channel-ingress-runtime de las rutas de recepción en tiempo de ejecución.
La subruta mantiene la búsqueda de la plataforma y los efectos secundarios en el complemento, mientras
comparte la resolución del estado de la lista blanca, las decisiones de ruta/remitente/comando/evento/activación,
los diagnósticos redactados y el mapeo de admisión de turnos. Mantenga la normalización
de la identidad del complemento en el descriptor que pasa al resolvedor; no
serialice valores de coincidencia brutos del estado resuelto o de la decisión. Vea
Channel ingress API para el diseño de la API,
los límites de propiedad y las expectativas de prueba.
Si su canal admite indicadores de escritura fuera de las respuestas entrantes, exponga
heartbeat.sendTyping(...) en el complemento del canal. Core lo llama con el
objetivo de entrega de latido resuelto antes de que se inicie la ejecución del modelo de latido y
usa el ciclo de vida compartido de keepalive/limpieza de escritura. Agregue heartbeat.clearTyping(...)
cuando la plataforma necesite una señal de detención explícita.
Si su canal agrega parámetros de herramienta de mensaje que transportan fuentes de medios, exponga esos
nombres de parámetros a través de describeMessageTool(...).mediaSourceParams. Core usa
esa lista explícita para la normalización de rutas de sandbox y la política de acceso a medios de salida,
por lo que los complementos no necesitan casos especiales de núcleo compartido para parámetros de
avatar, archivo adjunto o imagen de portada específicos del proveedor.
Prefiera devolver un mapa con clave de acción como
{ "set-profile": ["avatarUrl", "avatarPath"] } para que las acciones no relacionadas no
hereden los argumentos de medios de otra acción. Una matriz plana aún funciona para parámetros que
se comparten intencionalmente en cada acción expuesta.
Si tu canal necesita un formado específico del proveedor para message(action="send"),
prefiere actions.prepareSendPayload(...). Pon las tarjetas nativas, bloques, incrustaciones u
otros datos duraderos bajo payload.channelData.<channel> y deja que el núcleo realice
el envío real a través del adaptador de salida/mensaje. Usa
actions.handleAction(...) solo para enviar como alternativa de compatibilidad para
las cargas útiles que no se pueden serializar y reintentar.
Si tu plataforma almacena un alcance adicional dentro de los ids de conversación, mantén ese análisis
en el complemento con messaging.resolveSessionConversation(...). Ese es el
enganche canónico para mapear rawId al id de conversación base, id de hilo opcional,
baseConversationId explícito y cualquier parentConversationCandidates.
Cuando devuelvas parentConversationCandidates, mantenlos ordenados del
padre más estrecho al más amplio/conversación base.
Use openclaw/plugin-sdk/channel-route cuando el código del complemento necesita normalizar
campos tipo ruta, comparar un hilo secundario con su ruta principal, o construir una
clave de deduplicación estable a partir de { channel, to, accountId, threadId }. El asistente
normaliza los identificadores numéricos de hilo de la misma manera que lo hace el núcleo, por lo que los complementos deben preferirlo
sobre comparaciones String(threadId) ad hoc.
Los complementos con gramática de destino específica del proveedor deben exponer
messaging.resolveOutboundSessionRoute(...) para que el núcleo obtenga la identidad de
sesión e hilo nativa del proveedor sin usar shims del analizador.
Los complementos agrupados que necesitan el mismo análisis antes de que se inicie el registro del canal
también pueden exponer un archivo session-key-api.ts de nivel superior con una exportación
resolveSessionConversation(...) coincidente. El núcleo usa esa superficie segura para el arranque
solo cuando el registro del complemento en tiempo de ejecución aún no está disponible.
messaging.resolveParentConversationCandidates(...) sigue disponible como
alternativa de compatibilidad heredada cuando un complemento solo necesita reservas principales encima
del id genérico/raw. Si ambos enlaces existen, el núcleo usa
resolveSessionConversation(...).parentConversationCandidates primero y solo
recurre a resolveParentConversationCandidates(...) cuando el enlace canónico
los omite.
Aprobaciones y capacidades del canal
Sección titulada «Aprobaciones y capacidades del canal»La mayoría de los complementos de canal no necesitan código específico de aprobaciones.
- El núcleo posee
/approvedel mismo chat, cargas útiles compartidas de botones de aprobación y entrega alternativa genérica. - Prefiera un solo objeto
approvalCapabilityen el complemento del canal cuando el canal necesite un comportamiento específico de aprobación. - Se ha eliminado
ChannelPlugin.approvals. Coloque los hechos de entrega, nativos, renderizado y autenticación de aprobaciones enapprovalCapability. plugin.authes solo para inicio/cierre de sesión; el núcleo ya no lee los enlaces de autenticación de aprobación de ese objeto.approvalCapability.authorizeActorActionyapprovalCapability.getActionAvailabilityStateson la interfaz canónica de autenticación de aprobaciones.- Use
approvalCapability.getActionAvailabilityStatepara la disponibilidad de autenticación de aprobaciones en el mismo chat. - Si su canal expone aprobaciones de ejecución nativas, use
approvalCapability.getExecInitiatingSurfaceStatepara el estado de la superficie iniciadora/cliente nativo cuando difiera de la autenticación de aprobación en el mismo chat. El núcleo usa ese enlace específico de ejecución para distinguir entreenabledydisabled, decidir si el canal iniciador admite aprobaciones de ejecución nativas e incluir el canal en la guía de contingencia del cliente nativo.createApproverRestrictedNativeApprovalCapability(...)completa esto para el caso común. - Use
outbound.shouldSuppressLocalPayloadPromptooutbound.beforeDeliverPayloadpara el comportamiento del ciclo de vida de la carga útil específico del canal, como ocultar avisos de aprobación local duplicados o enviar indicadores de escritura antes de la entrega. - Use
approvalCapability.deliverysolo para el enrutamiento de aprobaciones nativas o la supresión de la alternativa. - Use
approvalCapability.nativeRuntimepara los hechos de aprobación nativos propiedad del canal. Manténgalo diferido en los puntos de entrada de canal activos concreateLazyChannelApprovalNativeRuntimeAdapter(...), que puede importar su módulo de tiempo de ejecución bajo demanda mientras aún permite al núcleo ensamblar el ciclo de vida de la aprobación. - Use
approvalCapability.rendersolo cuando un canal realmente necesite cargas útiles de aprobación personalizadas en lugar del renderizador compartido. - Use
approvalCapability.describeExecApprovalSetupcuando el canal quiera que la respuesta de ruta deshabilitada explique los ajustes de configuración exactos necesarios para habilitar las aprobaciones de ejecución nativas. El enlace recibe{ channel, channelLabel, accountId }; los canales de cuenta con nombre deben renderizar rutas con ámbito de cuenta comochannels.<channel>.accounts.<id>.execApprovals.*en lugar de los valores predeterminados de nivel superior. - Si un canal puede inferir identidades de MD estables similares a las del propietario a partir de la configuración existente, use
createResolvedApproverActionAuthAdapterdeopenclaw/plugin-sdk/approval-runtimepara restringir/approvedel mismo chat sin añadir lógica central específica para aprobaciones. - Si la autenticación de aprobación personalizada permite intencionalmente solo el retorno al mismo chat, devuelva
markImplicitSameChatApprovalAuthorization({ authorized: true })desdeopenclaw/plugin-sdk/approval-auth-runtime; de lo contrario, el núcleo trata el resultado como autorización explícita del aprobador. - Si una devolución de llamada nativa propiedad del canal resuelve las aprobaciones directamente, use
isImplicitSameChatApprovalAuthorization(...)antes de resolver para que el retorno implícito aún pase por la autorización normal de actores del canal. - Si un canal necesita entrega de aprobación nativa, mantenga el código del canal enfocado en la normalización de objetivos más los hechos de transporte/presentación. Use
createChannelExecApprovalProfile,createChannelNativeOriginTargetResolver,createChannelApproverDmTargetResolverycreateApproverRestrictedNativeApprovalCapabilitydeopenclaw/plugin-sdk/approval-runtime. Coloque los hechos específicos del canal detrás deapprovalCapability.nativeRuntime, idealmente a través decreateChannelApprovalNativeRuntimeAdapter(...)ocreateLazyChannelApprovalNativeRuntimeAdapter(...), para que el núcleo pueda ensamblar el controlador y poseer el filtrado de solicitudes, enrutamiento, deduplicación, vencimiento, suscripción a la puerta de enlace y notificaciones de enrutamiento a otro lugar.nativeRuntimese divide en algunas costuras más pequeñas: - Use
createNativeApprovalChannelRouteGatesdeopenclaw/plugin-sdk/approval-native-runtimecuando un canal admite tanto la entrega nativa de origen de sesión como objetivos de reenvío de aprobación explícitos. El asistente centraliza la selección de configuración de aprobación, el manejo demode, los filtros de agente/sesión, el enlace de cuenta, la coincidencia de sesión-objetivo y la coincidencia de lista de objetivos, mientras que las personas que llaman siguen siendo propietarias del ID del canal, el modo de reenvío predeterminado, la búsqueda de cuenta, la verificación de transporte habilitado, la normalización del objetivo y la resolución del objetivo de origen del turno. No lo use para crear valores predeterminados de política de canal propiedad del núcleo; pase explícitamente el modo predeterminado documentado del canal. createChannelNativeOriginTargetResolverusa el comparador de rutas de canal compartido de forma predeterminada para objetivos{ to, accountId, threadId }. PasetargetsMatchsolo cuando un canal tiene reglas de equivalencia específicas del proveedor, como la coincidencia de prefijo de marca de tiempo de Slack.- Pase
normalizeTargetForMatchacreateChannelNativeOriginTargetResolvercuando el canal necesita canonicalizar los ID del proveedor antes de que se ejecute el comparador de rutas predeterminado o una devolución de llamadatargetsMatchpersonalizada, conservando el objetivo original para la entrega. UsenormalizeTargetsolo cuando el objetivo de entrega resuelto en sí deba ser canonicalizado. availability- si la cuenta está configurada y si se debe manejar una solicitudpresentation- asignar el modelo de vista de aprobación compartido a cargas útiles nativas pendientes/resueltas/caducadas o acciones finalestransport- preparar objetivos más enviar/actualizar/eliminar mensajes de aprobación nativosinteractions- ganchos opcionales de bind/unbind/clear-action para botones nativos o reacciones, además de un gancho opcionalcancelDelivered. ImplementecancelDeliveredcuandodeliverPendingregistre estado en proceso o persistente (como un almacén de objetivos de reacción) para que el estado pueda liberarse si una parada del controlador cancela la entrega antes de que se ejecutebindPendingo cuandobindPendingno devuelve ningún manejadorobserve- ganchos opcionales de diagnóstico de entrega- Si el canal necesita objetos propiedad del runtime como un cliente, token, aplicación Bolt o receptor de webhooks, regístrelos a través de
openclaw/plugin-sdk/channel-runtime-context. El registro genérico de contexto de runtime permite al núcleo arrancar controladores impulsados por capacidades desde el estado de inicio del canal sin agregar pegamento específico de aprobación. - Recurra a
createChannelApprovalHandlerocreateChannelNativeApprovalRuntimede menor nivel solo cuando la costura impulsada por capacidades aún no sea lo suficientemente expresiva. - Los canales de aprobación nativos deben enrutar tanto
accountIdcomoapprovalKinda través de esos ayudantes.accountIdmantiene la política de aprobación de múltiples cuentas limitada a la cuenta de bot correcta, yapprovalKindmantiene el comportamiento de aprobación exec vs plugin disponible para el canal sin ramificaciones codificadas en el núcleo. - El núcleo ahora también posee los avisos de rerute de aprobación. Los complementos del canal no deben enviar sus propios mensajes de seguimiento de “la aprobación fue a DMs / otro canal” desde
createChannelNativeApprovalRuntime; en su lugar, exponga el origen exacto + el enrutamiento de DM del aprobador a través de los ayudantes de capacidad de aprobación compartidos y deje que el núcleo agregue las entregas reales antes de publicar cualquier aviso de vuelta al chat iniciador. - Conservar el tipo de id de aprobación entregado de extremo a extremo. Los clientes nativos no deben adivinar ni reescribir el enrutamiento de aprobación exec vs plugin desde el estado local del canal.
- Diferentes tipos de aprobación pueden exponer intencionalmente diferentes superficies nativas.
Ejemplos agrupados actuales:
- Slack mantiene disponible el enrutamiento de aprobación nativo tanto para ids exec como plugin.
- Matrix mantiene el mismo enrutamiento nativo de DM/canal y la UX de reacción para aprobaciones exec y de plugin, al tiempo que sigue permitiendo que la autenticación difiera por tipo de aprobación.
createApproverRestrictedNativeApprovalAdaptertodavía existe como un contenedor de compatibilidad, pero el código nuevo debería preferir el constructor de capacidades y exponerapprovalCapabilityen el complemento.
Para los puntos de entrada de canal activos (hot channel), prefiera las subrutas de tiempo de ejecución más específicas cuando solo necesite una parte de esa familia:
openclaw/plugin-sdk/approval-auth-runtimeopenclaw/plugin-sdk/approval-client-runtimeopenclaw/plugin-sdk/approval-delivery-runtimeopenclaw/plugin-sdk/approval-gateway-runtimeopenclaw/plugin-sdk/approval-handler-adapter-runtimeopenclaw/plugin-sdk/approval-handler-runtimeopenclaw/plugin-sdk/approval-native-runtimeopenclaw/plugin-sdk/approval-reply-runtimeopenclaw/plugin-sdk/channel-runtime-context
Del mismo modo, prefiera openclaw/plugin-sdk/setup-runtime,
openclaw/plugin-sdk/setup-runtime,
openclaw/plugin-sdk/reply-runtime,
openclaw/plugin-sdk/reply-dispatch-runtime,
openclaw/plugin-sdk/reply-reference y
openclaw/plugin-sdk/reply-chunking cuando no necesite la superficie general
(más amplia).
Específicamente para la configuración:
openclaw/plugin-sdk/setup-runtimecubre los asistentes de configuración seguros en tiempo de ejecución:createSetupTranslator, adaptadores de parches de configuración seguros para importaciones (createPatchedAccountSetupAdapter,createEnvPatchedAccountSetupAdapter,createSetupInputPresenceValidator), salida de nota de búsqueda,promptResolvedAllowFrom,splitSetupEntriesy los constructores de proxy de configuración delegadosopenclaw/plugin-sdk/setup-runtimeincluye la costura del adaptador consciente del entorno paracreateEnvPatchedAccountSetupAdapteropenclaw/plugin-sdk/channel-setupcubre los constructores de configuración de instalación opcional además de algunos primitivos seguros para la configuración:createOptionalChannelSetupSurface,createOptionalChannelSetupAdapter,
Si su canal admite configuración o autenticación impulsada por entorno y los flujos genéricos de inicio/configuración deben conocer esos nombres de entorno antes de que se cargue el tiempo de ejecución, declárelos en el manifiesto del complemento con channelEnvVars. Mantenga el tiempo de ejecución del canal envVars o constantes locales solo para el texto orientado al operador.
Si su canal puede aparecer en status, channels list, channels status o
escaneos SecretRef antes de que se inicie el tiempo de ejecución del complemento, agregue openclaw.setupEntry en
package.json. Ese punto de entrada debe ser seguro de importar en rutas de comandos de solo lectura
debe devolver los metadatos del canal, el adaptador de configuración seguro para la configuración, el adaptador de estado
y los metadatos del objetivo secreto del canal necesarios para esos resúmenes. No
inicie clientes, escuchas o tiempos de ejecución de transporte desde el punto de entrada de configuración.
Mantenga también la ruta de importación de entrada del canal principal estrecha. El descubrimiento puede evaluar la
entrada y el módulo del complemento del canal para registrar capacidades sin activar
el canal. Los archivos como channel-plugin-api.ts deben exportar el objeto
del complemento del canal sin importar asistentes de configuración, clientes de transporte, escuchas de socket,
iniciadores de subprocesos o módulos de inicio de servicio. Coloque esas partes
del tiempo de ejecución en módulos cargados desde registerFull(...), definidores de tiempo de ejecución o adaptadores
de capacidades diferidas.
createOptionalChannelSetupWizard, DEFAULT_ACCOUNT_ID,
createTopLevelChannelDmPolicy, setSetupChannelEnabled y
splitSetupEntries
- use la costura
openclaw/plugin-sdk/setupmás amplia solo cuando también necesite los asistentes de configuración/configuración compartida más pesados, comomoveSingleAccountChannelSectionToDefaultAccount(...)
Si su canal solo quiere anunciar “instale primero este complemento” en las superficies
de configuración, prefiera createOptionalChannelSetupSurface(...). El adaptador/asistente
generado falla de forma cerrada en las escrituras y finalización de la configuración, y reutilizan
el mismo mensaje de instalación requerida en la validación, finalización y copia del enlace de documentación.
Para otras rutas activas del canal, prefiera los asistentes estrechos sobre las superficies heredadas más amplias:
openclaw/plugin-sdk/account-core,openclaw/plugin-sdk/account-id,openclaw/plugin-sdk/account-resolutionyopenclaw/plugin-sdk/account-helperspara la configuración multicuenta y la reserva de cuenta predeterminadaopenclaw/plugin-sdk/inbound-envelopeyopenclaw/plugin-sdk/channel-inboundpara la ruta/sobre entrante y el cableado de registro y despachoopenclaw/plugin-sdk/channel-targetspara los asistentes de análisis de objetivosopenclaw/plugin-sdk/outbound-mediapara la carga de medios yopenclaw/plugin-sdk/channel-outboundpara delegados de identidad/envío saliente y planificación de carga útilbuildThreadAwareOutboundSessionRoute(...)deopenclaw/plugin-sdk/channel-corecuando una ruta saliente debe conservar unreplyToId/threadIdexplícito o recuperar la sesión:thread:actual después de que la clave de sesión base siga coincidiendo. Los complementos del proveedor pueden anular la precedencia, el comportamiento del sufijo y la normalización del ID de hilo cuando su plataforma tiene semánticas de entrega de hilos nativas.openclaw/plugin-sdk/thread-bindings-runtimepara el ciclo de vida de vinculación de hilos y el registro de adaptadoresopenclaw/plugin-sdk/agent-media-payloadsolo cuando aún se requiere un diseño de campo de carga útil de agente/medios heredadoopenclaw/plugin-sdk/telegram-command-configpara la normalización de comandos personalizados de Telegram, la validación de duplicados/conflictos y un contrato de configuración de comandos estable de respaldo
Los canales de solo autenticación generalmente pueden detenerse en la ruta predeterminada: el núcleo maneja las aprobaciones y el complemento solo expone capacidades de salida/autenticación. Los canales de aprobación nativos como Matrix, Slack, Telegram y transportes de chat personalizados deben usar los ayudantes nativos compartidos en lugar de crear su propio ciclo de vida de aprobación.
Política de mención entrante
Sección titulada «Política de mención entrante»Mantenga el manejo de menciones entrantes dividido en dos capas:
- recopilación de pruebas propiedad del complemento
- evaluación de políticas compartidas
Use openclaw/plugin-sdk/channel-mention-gating para decisiones sobre políticas de menciones.
Use openclaw/plugin-sdk/channel-inbound solo cuando necesite el contenedor de ayudantes entrantes más amplio.
Adecuado para la lógica local del complemento:
- detección de respuesta al bot
- detección de bot citado
- verificaciones de participación en hilos
- exclusiones de mensajes de servicio/sistema
- cachés nativos de la plataforma necesarios para probar la participación del bot
Adecuado para el ayudante compartido:
requireMention- resultado de mención explícita
- lista de permitidos de mención implícita
- omisión de comandos
- decisión final de omitir
Flujo preferido:
- Calcule los datos locales de menciones.
- Pase esos datos a
resolveInboundMentionDecision({ facts, policy }). - Use
decision.effectiveWasMentioned,decision.shouldBypassMentionydecision.shouldSkipen su puerta de enlace entrante.
import { implicitMentionKindWhen, matchesMentionWithExplicit, resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-inbound";
const mentionMatch = matchesMentionWithExplicit(text, { mentionRegexes, mentionPatterns,});
const facts = { canDetectMention: true, wasMentioned: mentionMatch.matched, hasAnyMention: mentionMatch.hasExplicitMention, implicitMentionKinds: [...implicitMentionKindWhen("reply_to_bot", isReplyToBot), ...implicitMentionKindWhen("quoted_bot", isQuoteOfBot)],};
const decision = resolveInboundMentionDecision({ facts, policy: { isGroup, requireMention, allowedImplicitMentionKinds: requireExplicitMention ? [] : ["reply_to_bot", "quoted_bot"], allowTextCommands, hasControlCommand, commandAuthorized, },});
if (decision.shouldSkip) return;api.runtime.channel.mentions expone los mismos asistentes de mención compartidos para
los complementos de canal incluidos que ya dependen de la inyección en tiempo de ejecución:
buildMentionRegexesmatchesMentionPatternsmatchesMentionWithExplicitimplicitMentionKindWhenresolveInboundMentionDecision
Si solo necesitas implicitMentionKindWhen y
resolveInboundMentionDecision, impórtalos desde
openclaw/plugin-sdk/channel-mention-gating para evitar cargar asistentes de tiempo de ejecución entrantes no relacionados.
Usa resolveInboundMentionDecision({ facts, policy }) para la puerta de enlace de menciones.
Tutorial
Sección titulada «Tutorial»Paquete y manifiesto
Crea los archivos estándar del complemento. El campo
channelenpackage.jsones lo que convierte a esto en un complemento de canal. Para obtener la superficie completa de metadatos del paquete, consulta Configuración y ajustes del complemento:{"name": "@myorg/openclaw-acme-chat","version": "1.0.0","type": "module","openclaw": {"extensions": ["./index.ts"],"setupEntry": "./setup-entry.ts","channel": {"id": "acme-chat","label": "Acme Chat","blurb": "Connect OpenClaw to Acme Chat."}}}{"id": "acme-chat","kind": "channel","channels": ["acme-chat"],"name": "Acme Chat","description": "Acme Chat channel plugin","configSchema": {"type": "object","additionalProperties": false,"properties": {}},"channelConfigs": {"acme-chat": {"schema": {"type": "object","additionalProperties": false,"properties": {"token": { "type": "string" },"allowFrom": {"type": "array","items": { "type": "string" }}}},"uiHints": {"token": {"label": "Bot token","sensitive": true}}}}}configSchemavalidaplugins.entries.acme-chat.config. Úsalo para configuraciones propias del complemento que no son la configuración de la cuenta del canal.channelConfigsvalidachannels.acme-chaty es la fuente de ruta fría utilizada por el esquema de configuración, configuración y superficies de interfaz de usuario antes de que se cargue el tiempo de ejecución del complemento.Construir el objeto del complemento de canal
La interfaz
ChannelPlugintiene muchas superficies de adaptador opcionales. Comience con lo mínimo -idysetup- y agregue adaptadores según los necesite.Cree
src/channel.ts:import {createChatChannelPlugin,createChannelPluginBase,} from "openclaw/plugin-sdk/channel-core";import type { OpenClawConfig } from "openclaw/plugin-sdk/channel-core";import { acmeChatApi } from "./client.js"; // your platform API clienttype ResolvedAccount = {accountId: string | null;token: string;allowFrom: string[];dmPolicy: string | undefined;};function resolveAccount(cfg: OpenClawConfig,accountId?: string | null,): ResolvedAccount {const section = (cfg.channels as Record)?.[“acme-chat”]; const token = section?.token; if (!token) throw new Error(“acme-chat: token is required”); return { accountId: accountId ?? null, token, allowFrom: section?.allowFrom ?? [], dmPolicy: section?.dmSecurity, }; }
export const acmeChatPlugin = createChatChannelPlugin({ base: createChannelPluginBase({ id: “acme-chat”, setup: { resolveAccount, inspectAccount(cfg, accountId) { const section = (cfg.channels as Record
)?.[“acme-chat”]; return { enabled: Boolean(section?.token), configured: Boolean(section?.token), tokenStatus: section?.token ? “available” : “missing”, }; }, }, }),
// DM security: who can message the botsecurity: {dm: {channelKey: "acme-chat",resolvePolicy: (account) => account.dmPolicy,resolveAllowFrom: (account) => account.allowFrom,defaultPolicy: "allowlist",},},// Pairing: approval flow for new DM contactspairing: {text: {idLabel: "Acme Chat username",message: "Send this code to verify your identity:",notify: async ({ target, code }) => {await acmeChatApi.sendDm(target, `Pairing code: ${code}`);},},},// Threading: how replies are deliveredthreading: { topLevelReplyToMode: "reply" },// Outbound: send messages to the platformoutbound: {attachedResults: {sendText: async (params) => {const result = await acmeChatApi.sendMessage(params.to,params.text,);return { messageId: result.id };},},base: {sendMedia: async (params) => {await acmeChatApi.sendFile(params.to, params.filePath);},},},});```Para canales que aceptan tanto claves DM canónicas de nivel superior como claves anidadas heredadas, use los auxiliares de `plugin-sdk/channel-config-helpers`: `resolveChannelDmAccess`, `resolveChannelDmPolicy`, `resolveChannelDmAllowFrom` y `normalizeChannelDmPolicy` mantienen los valores locales de la cuenta por delante de los valores raíz heredados. Empareje el mismo solucionador con la reparación de doctor a través de `normalizeLegacyDmAliases` para que el tiempo de ejecución y la migración lean el mismo contrato.Lo que createChatChannelPlugin hace por usted
En lugar de implementar manualmente interfaces de adaptador de bajo nivel, usted pasa opciones declarativas y el constructor las compone:
Opción Lo que conecta security.dmSolucionador de seguridad DM con alcance desde campos de configuración pairing.textFlujo de emparejamiento DM basado en texto con intercambio de código threadingSolucionador de modo de respuesta (fijo, con alcance de cuenta o personalizado) outbound.attachedResultsFunciones de envío que devuelven metadatos de resultado (ID de mensajes) También puede pasar objetos de adaptador sin procesar en lugar de las opciones declarativas si necesita control total.
Los adaptadores de salida sin procesar pueden definir una función
chunker(text, limit, ctx). Elctx.formattingopcional lleva decisiones de formato en el momento de la entrega comomaxLinesPerMessage; aplíquelo antes de enviar para que el hilado de respuestas y los límites de los fragmentos se resuelvan una sola vez mediante la entrega de salida compartida. Los contextos de envío también incluyenreplyToIdSource(implicitoexplicit) cuando se resolvió un objetivo de respuesta nativo, por lo que los auxiliares de carga útil pueden conservar etiquetas de respuesta explícitas sin consumir un slot de respuesta implícito de un solo uso.Conecte el punto de entrada
Cree
index.ts:import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";import { acmeChatPlugin } from "./src/channel.js";export default defineChannelPluginEntry({id: "acme-chat",name: "Acme Chat",description: "Acme Chat channel plugin",plugin: acmeChatPlugin,registerCliMetadata(api) {api.registerCli(({ program }) => {program.command("acme-chat").description("Acme Chat management");},{descriptors: [{name: "acme-chat",description: "Acme Chat management",hasSubcommands: false,},],},);},registerFull(api) {api.registerGatewayMethod(/* ... */);},});Coloque los descriptores de CLI propiedad del canal en
registerCliMetadata(...)para que OpenClaw pueda mostrarlos en la ayuda raíz sin activar el tiempo de ejecución completo del canal, mientras que las cargas completas normales aún recogen los mismos descriptores para el registro real de comandos. MantengaregisterFull(...)para el trabajo exclusivo del tiempo de ejecución. SiregisterFull(...)registra métodos RPC de puerta de enlace, use un prefijo específico del complemento. Los espacios de nombres de administración principal (config.*,exec.approvals.*,wizard.*,update.*) permanecen reservados y siempre se resuelven enoperator.admin.defineChannelPluginEntrymaneja la división del modo de registro automáticamente. Vea Puntos de entrada para todas las opciones.Añadir una entrada de configuración
Cree
setup-entry.tspara una carga ligera durante la incorporación:import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";import { acmeChatPlugin } from "./src/channel.js";export default defineSetupPluginEntry(acmeChatPlugin);OpenClaw carga esto en lugar de la entrada completa cuando el canal está deshabilitado o sin configurar. Evita introducir código de tiempo de ejecución pesado durante los flujos de configuración. Vea Configuración y ajustes para obtener más detalles.
Los canales de espacio de trabajo empaquetados que dividen las exportaciones seguras para la configuración en módulos complementarios pueden usar
defineBundledChannelSetupEntry(...)deopenclaw/plugin-sdk/channel-entry-contractcuando también necesitan un definidor de tiempo de ejecución explícito en el momento de la configuración.Manejar mensajes entrantes
Su complemento necesita recibir mensajes de la plataforma y reenviarlos a OpenClaw. El patrón típico es un webhook que verifica la solicitud y la despacha a través del controlador de entrada de su canal:
registerFull(api) {api.registerHttpRoute({path: "/acme-chat/webhook",auth: "plugin", // plugin-managed auth (verify signatures yourself)handler: async (req, res) => {const event = parseWebhookPayload(req);// Your inbound handler dispatches the message to OpenClaw.// The exact wiring depends on your platform SDK -// see a real example in the bundled Microsoft Teams or Google Chat plugin package.await handleAcmeChatInbound(api, event);res.statusCode = 200;res.end("ok");return true;},});}Prueba
Escribe pruebas co-localizadas en
src/channel.test.ts:```typescript src/channel.test.tsimport { describe, it, expect } from "vitest";import { acmeChatPlugin } from "./channel.js";describe("acme-chat plugin", () => {it("resolves account from config", () => {const cfg = {channels: {"acme-chat": { token: "test-token", allowFrom: ["user1"] },},} as any;const account = acmeChatPlugin.setup!.resolveAccount(cfg, undefined);expect(account.token).toBe("test-token");});it("inspects account without materializing secrets", () => {const cfg = {channels: { "acme-chat": { token: "test-token" } },} as any;const result = acmeChatPlugin.setup!.inspectAccount!(cfg, undefined);expect(result.configured).toBe(true);expect(result.tokenStatus).toBe("available");});it("reports missing config", () => {const cfg = { channels: {} } as any;const result = acmeChatPlugin.setup!.inspectAccount!(cfg, undefined);expect(result.configured).toBe(false);});});``````bashpnpm test --/acme-chat/ ```
Para ver asistentes de pruebas compartidos, consulta [Pruebas](/es/plugins/sdk-testing).
Estructura de archivos
Sección titulada «Estructura de archivos»<bundled-plugin-root>/acme-chat/├── package.json # openclaw.channel metadata├── openclaw.plugin.json # Manifest with config schema├── index.ts # defineChannelPluginEntry├── setup-entry.ts # defineSetupPluginEntry├── api.ts # Public exports (optional)├── runtime-api.ts # Internal runtime exports (optional)└── src/ ├── channel.ts # ChannelPlugin via createChatChannelPlugin ├── channel.test.ts # Tests ├── client.ts # Platform API client └── runtime.ts # Runtime store (if needed)Temas avanzados
Sección titulada «Temas avanzados»Modos de respuesta fijos, con ámbito de cuenta o personalizados
describeMessageTool y descubrimiento de acciones
inferTargetChatType, looksLikeId, resolveTarget
TTS, STT, medios, subagente a través de api.runtime
Ciclo de vida de eventos de entrada compartidos: ingest, resolve, record, dispatch, finalize
Pasos siguientes
Sección titulada «Pasos siguientes»- Complementos de proveedores: si tu complemento también proporciona modelos
- Descripción general del SDK: referencia completa de importación de subrutas
- Pruebas del SDK: utilidades de pruebas y pruebas de contrato
- Manifiesto del complemento: esquema completo del manifiesto