环境
- OpenClaw
2026.9.5(Windows)
@tencent-weixin/openclaw-weixin 2.4.9
- 微信渠道已扫码配对成功,收发消息正常
现象
渠道已配对,且 commands.ownerAllowFrom 已配置该渠道 owner:
{
"commands": { "ownerAllowFrom": ["openclaw-weixin:<owner-wechat-id>@im.wechat"] },
"agents": { "defaults": { "heartbeat": { "target": "owner", "every": "2h" } } }
}
openclaw status 的 Heartbeat 行:
Heartbeat 2h (main; waiting for delivery route — set commands.ownerAllowFrom=[...] or channel allowFrom;
explicit delivery: heartbeat.target="openclaw-weixin" with heartbeat.to="...")
提示让去配置已经配好的 commands.ownerAllowFrom(或插件未提供的 channel allowFrom),文案误导;实际是 owner 路由判定 fail closed,心跳投递以 no-route 被丢弃。
期望
agents.defaults.heartbeat.target = "owner" 能解析出直聊投递路由,与内置渠道(telegram / reef / a2a 等)行为一致。
根因
框架 openclaw/dist/targets-*.mjs:
function isPositivelyDirectHeartbeatOwnerTarget(params) {
const to = params.plugin
? stripTargetProviderPrefix(params.to, params.plugin.id, ...params.plugin.messaging?.targetPrefixes ?? [])
: params.to.trim();
return (normalizeChatType(params.chatType) ?? params.plugin?.messaging?.inferTargetChatType?.({ to })) === "direct";
}
owner 目标不携带 chatType(status 探针与运行时解析路径都是如此),因此该判定完全依赖插件的 messaging.inferTargetChatType;框架不会从 capabilities.chatTypes 反推(源码中 12 处 inferTargetChatType 引用无一从 capabilities 推导,未证明的目标一律 fail closed)。
而本插件 channel 定义只声明了:
capabilities: { chatTypes: ["direct"], media: true, blockStreaming: true },
messaging: { targetResolver: { looksLikeId: (raw) => raw.endsWith("@im.wechat") } },
缺少 inferTargetChatType(也没有 resolveAllowFrom / resolveOutboundSessionRoute)。内置渠道均实现了该钩子;官方文档亦将这一分类列为渠道插件职责:
docs/plugins/architecture-internals/channel-surfaces.md → Channel target resolution:
messaging.inferTargetChatType({ to }) decides whether a normalized target should be treated as direct, group, or channel before directory lookup. Implicit owner heartbeat delivery requires this direct classification; without it, Gateway status reports waiting for route.
最小复现
- 安装
2.4.9,完成微信扫码配对;
commands.ownerAllowFrom = ["openclaw-weixin:<owner-wechat-id>@im.wechat"];
agents.defaults.heartbeat = { target: "owner", every: "2h" };
openclaw status → Heartbeat 行显示 waiting for delivery route;
- 对照:改为显式路由
heartbeat.target = "openclaw-weixin" + heartbeat.to = "<owner-wechat-id>@im.wechat" + heartbeat.accountId = "<account-id>" → 立即正常(不回显 waiting for route),说明渠道投递本身没问题,只是 owner 分类缺失。
建议修复
在 channel 定义的 messaging 块补一行(微信 ID 形态恒为 1:1):
messaging: {
inferTargetChatType: ({ to }) => (typeof to === "string" && to.trim().endsWith("@im.wechat") ? "direct" : undefined),
targetResolver: { looksLikeId: (raw) => raw.endsWith("@im.wechat") },
},
语义:凡以 @im.wechat 结尾的目标即 direct,其余返回 undefined(不猜测 group / channel)。如需支持群聊,可再按 ID 形态扩展分类。
影响面
- 仅影响 owner 目标解析(如
heartbeat.target="owner");显式 target + to + accountId 路由不受影响。
- 对使用默认/owner 心跳投递的微信渠道用户,该配置会静默降级为
no-route,且 status 提示文案指向错误方向,排查成本高。
- 已验证的 workaround:
agents.defaults.heartbeat = { target: "openclaw-weixin", to: "<owner-wechat-id>@im.wechat", accountId: "<account-id>" }。
环境
2026.9.5(Windows)@tencent-weixin/openclaw-weixin2.4.9现象
渠道已配对,且
commands.ownerAllowFrom已配置该渠道 owner:{ "commands": { "ownerAllowFrom": ["openclaw-weixin:<owner-wechat-id>@im.wechat"] }, "agents": { "defaults": { "heartbeat": { "target": "owner", "every": "2h" } } } }openclaw status的 Heartbeat 行:提示让去配置已经配好的
commands.ownerAllowFrom(或插件未提供的 channelallowFrom),文案误导;实际是 owner 路由判定 fail closed,心跳投递以no-route被丢弃。期望
agents.defaults.heartbeat.target = "owner"能解析出直聊投递路由,与内置渠道(telegram / reef / a2a 等)行为一致。根因
框架
openclaw/dist/targets-*.mjs:owner 目标不携带
chatType(status 探针与运行时解析路径都是如此),因此该判定完全依赖插件的messaging.inferTargetChatType;框架不会从capabilities.chatTypes反推(源码中 12 处inferTargetChatType引用无一从 capabilities 推导,未证明的目标一律 fail closed)。而本插件 channel 定义只声明了:
缺少
inferTargetChatType(也没有resolveAllowFrom/resolveOutboundSessionRoute)。内置渠道均实现了该钩子;官方文档亦将这一分类列为渠道插件职责:最小复现
2.4.9,完成微信扫码配对;commands.ownerAllowFrom = ["openclaw-weixin:<owner-wechat-id>@im.wechat"];agents.defaults.heartbeat = { target: "owner", every: "2h" };openclaw status→ Heartbeat 行显示waiting for delivery route;heartbeat.target = "openclaw-weixin"+heartbeat.to = "<owner-wechat-id>@im.wechat"+heartbeat.accountId = "<account-id>"→ 立即正常(不回显 waiting for route),说明渠道投递本身没问题,只是 owner 分类缺失。建议修复
在 channel 定义的
messaging块补一行(微信 ID 形态恒为 1:1):语义:凡以
@im.wechat结尾的目标即direct,其余返回undefined(不猜测group/channel)。如需支持群聊,可再按 ID 形态扩展分类。影响面
heartbeat.target="owner");显式target+to+accountId路由不受影响。no-route,且 status 提示文案指向错误方向,排查成本高。agents.defaults.heartbeat = { target: "openclaw-weixin", to: "<owner-wechat-id>@im.wechat", accountId: "<account-id>" }。