From fc7efd0227ad538fe28973be2e03fd490eab2eb7 Mon Sep 17 00:00:00 2001 From: hzhang Date: Sat, 16 May 2026 10:02:47 +0100 Subject: [PATCH] fix(plugin): force automatic source-reply delivery (fixes no-reply) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenClaw defaults group-chat replies to sourceReplyDeliveryMode 'message_tool_only', which suppresses auto-delivery of the agent's text reply (it expects the agent to call a message tool). With ChatType 'group', the Fabric plugin's deliver callback was therefore NEVER invoked — the agent ran but no reply ever returned to Fabric. Fabric already gates *when* an agent speaks via the per-recipient wakeup flag, so once a turn is dispatched the reply must always flow back. Pass replyOptions.sourceReplyDeliveryMode='automatic' so OpenClaw delivers the agent's reply through regardless of the group default (source-reply-delivery-mode honors a truthy requested mode). Verified live end-to-end: human posts -> wakeup -> agent runs -> 'fabric: deliver' + 'fabric: posted reply' -> agent message appears in the Fabric channel. Co-Authored-By: Claude Opus 4.7 (1M context) --- dist/fabric/src/inbound.js | 17 +++++++++++++++-- src/inbound.ts | 17 +++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/dist/fabric/src/inbound.js b/dist/fabric/src/inbound.js index e163985..317bd73 100644 --- a/dist/fabric/src/inbound.js +++ b/dist/fabric/src/inbound.js @@ -233,8 +233,21 @@ export class FabricInbound { }, onRecordError: (err) => this.log.warn(`fabric: session record failed agent=${agentId}: ${String(err)}`), onDispatchError: (err, info) => this.log.warn(`fabric: ${info.kind} dispatch failed agent=${agentId}: ${String(err)}`), - // Fabric has no length limit: deliver the whole reply as ONE message. - replyOptions: { disableBlockStreaming: true }, + // - disableBlockStreaming: Fabric has no length limit, deliver the + // whole reply as ONE message. + // - sourceReplyDeliveryMode 'automatic': OpenClaw defaults group + // chats to "message_tool_only", which SUPPRESSES auto-delivery of + // the agent's text reply (it expects the agent to call a message + // tool). Fabric already gates *when* an agent speaks via the + // per-recipient wakeup flag, so once a turn is dispatched the + // reply must always flow back through `deliver`. Forcing + // 'automatic' overrides the group default so the reply is + // delivered. (source-reply-delivery-mode: a truthy `requested` + // wins unless it's message_tool_only with no tool available.) + replyOptions: { + disableBlockStreaming: true, + sourceReplyDeliveryMode: 'automatic', + }, }); this.log.info(`fabric: dispatch returned agent=${agentId} channel=${channelId}`); } diff --git a/src/inbound.ts b/src/inbound.ts index 3f5fc18..0d82a57 100644 --- a/src/inbound.ts +++ b/src/inbound.ts @@ -279,8 +279,21 @@ export class FabricInbound { this.log.warn(`fabric: session record failed agent=${agentId}: ${String(err)}`), onDispatchError: (err: unknown, info: { kind: string }) => this.log.warn(`fabric: ${info.kind} dispatch failed agent=${agentId}: ${String(err)}`), - // Fabric has no length limit: deliver the whole reply as ONE message. - replyOptions: { disableBlockStreaming: true } as never, + // - disableBlockStreaming: Fabric has no length limit, deliver the + // whole reply as ONE message. + // - sourceReplyDeliveryMode 'automatic': OpenClaw defaults group + // chats to "message_tool_only", which SUPPRESSES auto-delivery of + // the agent's text reply (it expects the agent to call a message + // tool). Fabric already gates *when* an agent speaks via the + // per-recipient wakeup flag, so once a turn is dispatched the + // reply must always flow back through `deliver`. Forcing + // 'automatic' overrides the group default so the reply is + // delivered. (source-reply-delivery-mode: a truthy `requested` + // wins unless it's message_tool_only with no tool available.) + replyOptions: { + disableBlockStreaming: true, + sourceReplyDeliveryMode: 'automatic', + } as never, }); this.log.info(`fabric: dispatch returned agent=${agentId} channel=${channelId}`); } catch (err) {