Pular para o conteúdo principal

Documentation Index

Fetch the complete documentation index at: https://docs.openclaw.ai/llms.txt

Use this file to discover all available pages before exploring further.

Plugins de canal devem expor um adapter message de openclaw/plugin-sdk/channel-message. O adapter descreve o ciclo de vida da mensagem nativa compatível com a plataforma:
receive -> route and record -> agent turn -> durable final send
send -> render batch -> platform I/O -> receipt -> lifecycle side effects
live preview -> final edit or fallback -> receipt
O core é responsável por enfileiramento, durabilidade, política genérica de nova tentativa, hooks, recibos e pela ferramenta message compartilhada. O plugin é responsável por chamadas nativas de send/edit/delete, normalização de destino, encadeamento da plataforma, citações selecionadas, flags de notificação, estado da conta e efeitos colaterais específicos da plataforma. Use esta página junto com Criando plugins de canal. O subcaminho channel-message é intencionalmente leve o bastante para arquivos de bootstrap de plugin ativos, como channel.ts: ele expõe contratos de adapter, provas de capacidade, recibos e fachadas de compatibilidade sem carregar entrega de saída. Helpers de entrega em runtime estão disponíveis em openclaw/plugin-sdk/channel-message-runtime para caminhos de código de monitor/send que já fazem I/O assíncrono de mensagens.

Adapter mínimo

A maioria dos novos plugins de canal pode começar com um adapter pequeno:
import {
  defineChannelMessageAdapter,
  createMessageReceiptFromOutboundResults,
} from "openclaw/plugin-sdk/channel-message";

export const demoMessageAdapter = defineChannelMessageAdapter({
  id: "demo",
  durableFinal: {
    capabilities: {
      text: true,
      replyTo: true,
      thread: true,
      messageSendingHooks: true,
    },
  },
  send: {
    text: async ({ cfg, to, text, accountId, replyToId, threadId, signal }) => {
      const sent = await sendDemoMessage({
        cfg,
        to,
        text,
        accountId: accountId ?? undefined,
        replyToId: replyToId ?? undefined,
        threadId: threadId == null ? undefined : String(threadId),
        signal,
      });

      return {
        receipt: createMessageReceiptFromOutboundResults({
          results: [{ channel: "demo", messageId: sent.id, conversationId: to }],
          kind: "text",
          threadId: threadId == null ? undefined : String(threadId),
          replyToId: replyToId ?? undefined,
        }),
      };
    },
  },
});
Depois, anexe-o ao plugin de canal:
export const demoPlugin = createChatChannelPlugin({
  base: {
    id: "demo",
    message: demoMessageAdapter,
    // other channel plugin fields
  },
});
Declare somente capacidades que o adapter realmente preserva. Toda capacidade declarada deve ter um teste de contrato.

Ponte de saída

Se o canal já tiver um adapter outbound compatível, prefira derivar o adapter de mensagem em vez de duplicar o código de envio:
import { createChannelMessageAdapterFromOutbound } from "openclaw/plugin-sdk/channel-message";

const demoMessageAdapter = createChannelMessageAdapterFromOutbound({
  id: "demo",
  outbound: demoOutboundAdapter,
});
A ponte converte resultados de envio de saída antigos em valores MessageReceipt. Código novo deve passar recibos de ponta a ponta e só derivar ids legados nas bordas de compatibilidade com listMessageReceiptPlatformIds(...) ou resolveMessageReceiptPrimaryId(...). Se nenhuma política de recebimento for fornecida, createChannelMessageAdapterFromOutbound(...) usa a política de confirmação de recebimento manual. Isso torna a confirmação de plataforma pertencente ao plugin explícita sem alterar canais que confirmam webhooks, sockets ou offsets de polling fora do contexto genérico de recebimento.

Envios da ferramenta message

O caminho message(action="send") compartilhado deve usar o mesmo ciclo de vida de entrega do core que as respostas finais. Se um canal precisar de formatação específica de provedor para o envio da ferramenta, implemente actions.prepareSendPayload(...) em vez de enviar a partir de actions.handleAction(...). prepareSendPayload(...) recebe o ReplyPayload normalizado pelo core mais o contexto completo da ação. Retorne um payload com dados específicos do canal em payload.channelData.<channel> e deixe o core chamar sendMessage(...), deliverOutboundPayloads(...), a fila write-ahead, hooks de envio de mensagem, nova tentativa, recuperação e limpeza de ack. Retorne null somente quando o envio não puder ser representado como um payload durável, por exemplo porque contém uma factory de componente não serializável. O core manterá o fallback de ação de plugin legado para compatibilidade, mas novos recursos de envio de canal devem ser expressáveis como dados de payload duráveis.
export const demoActions: ChannelMessageActionAdapter = {
  describeMessageTool: () => ({ actions: ["send"], capabilities: ["presentation"] }),
  prepareSendPayload: ({ ctx, payload }) => {
    if (ctx.action !== "send") {
      return null;
    }
    return {
      ...payload,
      channelData: {
        ...payload.channelData,
        demo: {
          ...(payload.channelData?.demo as object | undefined),
          nativeCard: ctx.params.card,
        },
      },
    };
  },
};
O adapter de saída então lê payload.channelData.demo dentro de sendPayload. Isso mantém a renderização específica da plataforma no plugin enquanto o core continua responsável por persistência, nova tentativa, recuperação, hooks e ack. Payloads preparados de message(action="send") e entrega genérica de resposta final usam entrega do core com enfileiramento de melhor esforço por padrão. Enfileiramento durável obrigatório só é válido depois que o core verifica que o canal consegue reconciliar um envio cujo resultado é desconhecido após uma falha. Se o adapter não puder implementar reconcileUnknownSend, mantenha o caminho de envio preparado como melhor esforço; o core ainda tentará a fila write-ahead, mas persistência de fila ou recuperação incerta de falha não fazem parte do contrato de entrega obrigatório.

Capacidades finais duráveis

A entrega final durável é opt-in por efeito colateral. O core só usará entrega durável genérica quando o adapter declarar todas as capacidades necessárias pelo payload e pelas opções de entrega.
CapacidadeDeclare quando
textO adapter consegue enviar texto e retornar um recibo.
mediaEnvios de mídia retornam recibos para cada mensagem visível da plataforma.
payloadO adapter preserva a semântica de payload de resposta rica, não apenas texto e uma URL de mídia.
replyToDestinos de resposta nativos chegam à plataforma.
threadDestinos nativos de thread, tópico ou thread de canal chegam à plataforma.
silentSupressão de notificação chega à plataforma.
nativeQuoteMetadados de citação selecionada chegam à plataforma.
messageSendingHooksHooks de envio de mensagem do core podem cancelar ou reescrever conteúdo antes do I/O da plataforma.
batchLotes renderizados em múltiplas partes podem ser reproduzidos como um único plano durável.
reconcileUnknownSendO adapter consegue resolver a recuperação unknown_after_send sem repetição cega.
afterSendSuccessEfeitos colaterais locais do canal após envio executam uma vez.
afterCommitEfeitos colaterais locais do canal após commit executam uma vez.
Entrega final de melhor esforço não exige reconcileUnknownSend; ela usa o ciclo de vida compartilhado quando o adapter preserva a semântica visível do payload e recorre ao I/O direto da plataforma se a persistência de fila não estiver disponível. Entrega final durável obrigatória deve exigir explicitamente reconcileUnknownSend. Se o adapter não puder determinar se um envio iniciado/desconhecido chegou à plataforma, não declare essa capacidade; o core rejeitará a entrega durável obrigatória antes do enfileiramento. Quando um chamador precisar de entrega durável, derive os requisitos em vez de criar mapas manualmente:
import { deriveDurableFinalDeliveryRequirements } from "openclaw/plugin-sdk/channel-message";

const requiredCapabilities = deriveDurableFinalDeliveryRequirements({
  payload,
  replyToId,
  threadId,
  silent,
  payloadTransport: true,
  extraCapabilities: {
    nativeQuote: hasSelectedQuote(payload),
  },
});
messageSendingHooks é obrigatório por padrão. Defina messageSendingHooks: false somente para um caminho que intencionalmente não pode executar hooks globais de envio de mensagem.

Contrato de envio durável

Um envio final durável tem semântica mais rígida do que a entrega legada pertencente ao canal:
  • Crie a intenção durável antes do I/O da plataforma.
  • Se a entrega durável retornar um resultado tratado, não recorra ao envio legado.
  • Trate cancelamento por hook e resultados sem envio como terminais.
  • Trate unsupported como resultado pré-intenção somente.
  • Para durabilidade obrigatória, falhe antes do I/O da plataforma se a fila não puder registrar que o envio da plataforma começou.
  • Para entrega final obrigatória e envios preparados obrigatórios da ferramenta de mensagem, faça preflight de reconcileUnknownSend; a recuperação deve conseguir confirmar com ack uma mensagem já enviada ou repetir apenas depois que o adapter provar que o envio original não aconteceu.
  • Para best_effort, falhas de escrita na fila podem recorrer a I/O direto da plataforma.
  • Encaminhe sinais de abort para carregamento de mídia e envios da plataforma.
  • Execute hooks após commit depois do ack da fila; o fallback direto de melhor esforço os executa após I/O de plataforma bem-sucedido porque não há commit de fila durável.
  • Retorne recibos para cada id de mensagem visível da plataforma.
  • Use reconcileUnknownSend quando uma plataforma puder verificar se um envio incerto já chegou ao usuário.
Esse contrato evita envios duplicados após falhas e evita burlar hooks de cancelamento de envio de mensagem.

Recibos

MessageReceipt é o novo registro interno do que a plataforma aceitou:
type MessageReceipt = {
  primaryPlatformMessageId?: string;
  platformMessageIds: string[];
  parts: MessageReceiptPart[];
  threadId?: string;
  replyToId?: string;
  editToken?: string;
  deleteToken?: string;
  sentAt: number;
  raw?: readonly MessageReceiptSourceResult[];
};
Use createMessageReceiptFromOutboundResults(...) ao adaptar um resultado de envio existente. Use createPreviewMessageReceipt(...) quando uma mensagem de pré-visualização em tempo real se tornar o recibo final. Evite adicionar novos campos messageIds locais do proprietário. ChannelDeliveryResult.messageIds legado ainda é produzido nas bordas de compatibilidade.

Pré-visualização em tempo real

Canais que transmitem pré-visualizações de rascunho ou atualizações de progresso devem declarar capacidades em tempo real:
const demoMessageAdapter = defineChannelMessageAdapter({
  id: "demo",
  live: {
    capabilities: {
      draftPreview: true,
      previewFinalization: true,
      progressUpdates: true,
      quietFinalization: true,
    },
    finalizer: {
      capabilities: {
        finalEdit: true,
        normalFallback: true,
        discardPending: true,
        previewReceipt: true,
        retainOnAmbiguousFailure: true,
      },
    },
  },
});
Use defineFinalizableLivePreviewAdapter(...) e deliverWithFinalizableLivePreviewAdapter(...) para finalização em runtime. O finalizador decide se a resposta final edita a pré-visualização no lugar, envia um fallback normal, descarta o estado pendente da pré-visualização, mantém uma edição ambígua com falha sem duplicar a mensagem e retorna o recibo final.

Política de ack de recebimento

Receptores de entrada que controlam o timing de confirmação da plataforma devem declarar política de recebimento:
const demoMessageAdapter = defineChannelMessageAdapter({
  id: "demo",
  receive: {
    defaultAckPolicy: "after_agent_dispatch",
    supportedAckPolicies: ["after_receive_record", "after_agent_dispatch"],
  },
});
Adapters que não declaram política de recebimento usam por padrão:
{
  receive: {
    defaultAckPolicy: "manual",
    supportedAckPolicies: ["manual"],
  },
}
Use o padrão quando a plataforma não tiver nenhum reconhecimento a adiar, já reconhecer antes do processamento assíncrono ou precisar de semântica de resposta específica do protocolo. Declare uma das políticas em estágios somente quando o receptor realmente usar o contexto de recebimento para mover o reconhecimento da plataforma para depois. Políticas:
PolíticaUse quando
after_receive_recordA plataforma pode ser reconhecida depois que o evento de entrada é analisado e registrado.
after_agent_dispatchA plataforma deve aguardar até que o despacho do agente tenha sido aceito.
after_durable_sendA plataforma deve aguardar até que a entrega final tenha uma decisão durável.
manualO plugin controla o reconhecimento porque a semântica da plataforma não corresponde a um estágio genérico.
Use createMessageReceiveContext(...) em receptores que adiam o estado de ack, e shouldAckMessageAfterStage(...) quando o receptor precisar testar se um estágio satisfez a política configurada.

Testes de contrato

Declarações de capacidade fazem parte do contrato do plugin. Dê suporte a elas com testes:
import {
  verifyChannelMessageAdapterCapabilityProofs,
  verifyChannelMessageLiveCapabilityAdapterProofs,
  verifyChannelMessageLiveFinalizerProofs,
  verifyChannelMessageReceiveAckPolicyAdapterProofs,
} from "openclaw/plugin-sdk/channel-message";

it("backs declared message capabilities", async () => {
  await expect(
    verifyChannelMessageAdapterCapabilityProofs({
      adapterName: "demo",
      adapter: demoMessageAdapter,
      proofs: {
        text: async () => {
          const result = await demoMessageAdapter.send!.text!(textCtx);
          expect(result.receipt.platformMessageIds).toContain("msg-1");
        },
        replyTo: async () => {
          await demoMessageAdapter.send!.text!({ ...textCtx, replyToId: "parent-1" });
          expect(sendDemoMessage).toHaveBeenCalledWith(
            expect.objectContaining({
              replyToId: "parent-1",
            }),
          );
        },
        messageSendingHooks: () => {
          expect(demoMessageAdapter.durableFinal!.capabilities!.messageSendingHooks).toBe(true);
        },
      },
    }),
  ).resolves.toContainEqual({ capability: "text", status: "verified" });
});
Adicione conjuntos de provas live e de recebimento quando o adaptador declarar esses recursos. Uma prova ausente deve fazer o teste falhar, em vez de ampliar silenciosamente a superfície durável.

APIs de compatibilidade obsoletas

Estas APIs continuam importáveis para compatibilidade com terceiros. Não as use em código novo de canal.
API obsoletaSubstituição
openclaw/plugin-sdk/channel-reply-pipelineopenclaw/plugin-sdk/channel-message
createChannelTurnReplyPipeline(...)createChannelMessageReplyPipeline(...) para despachantes de compatibilidade, ou um adaptador message para código novo de canal
deliverDurableInboundReplyPayload(...)deliverInboundReplyWithMessageSendContext(...) de openclaw/plugin-sdk/channel-message-runtime
dispatchInboundReplyWithBase(...)dispatchChannelMessageReplyWithBase(...) somente para despachantes de compatibilidade
recordInboundSessionAndDispatchReply(...)recordChannelMessageReplyDispatch(...) somente para despachantes de compatibilidade
resolveChannelSourceReplyDeliveryMode(...)resolveChannelMessageSourceReplyDeliveryMode(...)
deliverFinalizableDraftPreview(...)defineFinalizableLivePreviewAdapter(...) mais deliverWithFinalizableLivePreviewAdapter(...)
DraftPreviewFinalizerDraftLivePreviewFinalizerDraft
DraftPreviewFinalizerResultLivePreviewFinalizerResult
Despachantes de compatibilidade ainda podem usar createReplyPrefixContext(...), createReplyPrefixOptions(...) e createTypingCallbacks(...) por meio da fachada de mensagens. Código novo de ciclo de vida deve evitar o subcaminho antigo channel-reply-pipeline.

Checklist de migração

  1. Adicione message: defineChannelMessageAdapter(...) ou message: createChannelMessageAdapterFromOutbound(...) ao plugin de canal.
  2. Retorne MessageReceipt de envios de texto, mídia e payload.
  3. Declare somente capacidades respaldadas por comportamento nativo e testes.
  4. Substitua mapas de requisitos duráveis escritos manualmente por deriveDurableFinalDeliveryRequirements(...).
  5. Mova a finalização de pré-visualização pelos auxiliares de pré-visualização live quando o canal editar mensagens de rascunho no lugar.
  6. Declare a política de ack de recebimento somente quando o receptor puder realmente adiar o reconhecimento da plataforma.
  7. Mantenha auxiliares legados de despacho de resposta somente nas bordas de compatibilidade.