diff --git a/src/auto-reply/reply/dispatch-acp.ts b/src/auto-reply/reply/dispatch-acp.ts index 1a31c6a4b86..e9484e9aa50 100644 --- a/src/auto-reply/reply/dispatch-acp.ts +++ b/src/auto-reply/reply/dispatch-acp.ts @@ -106,6 +106,9 @@ async function resolveAcpAttachments( ctx: FinalizedMsgContext, cfg: OpenClawConfig, ): Promise { + if (!hasInboundMediaForAcp(ctx)) { + return []; + } const { MediaAttachmentCache, isMediaUnderstandingSkipError, diff --git a/src/plugin-sdk/acp-runtime.test.ts b/src/plugin-sdk/acp-runtime.test.ts index b0310dce0c0..a9fde108cf3 100644 --- a/src/plugin-sdk/acp-runtime.test.ts +++ b/src/plugin-sdk/acp-runtime.test.ts @@ -16,7 +16,9 @@ import { tryDispatchAcpReplyHook } from "./acp-runtime.js"; const event = { ctx: buildTestCtx({ SessionKey: "agent:test:session", - BodyForAgent: "hello", + CommandBody: "/acp cancel", + BodyForCommands: "/acp cancel", + BodyForAgent: "/acp cancel", }), runId: "run-1", sessionKey: "agent:test:session", diff --git a/src/plugin-sdk/acp-runtime.ts b/src/plugin-sdk/acp-runtime.ts index b339914f581..2729476e1b4 100644 --- a/src/plugin-sdk/acp-runtime.ts +++ b/src/plugin-sdk/acp-runtime.ts @@ -41,10 +41,19 @@ function loadDispatchAcpRuntime() { return dispatchAcpRuntimePromise; } +function hasExplicitCommandCandidate(ctx: PluginHookReplyDispatchEvent["ctx"]): boolean { + return [ctx.CommandBody, ctx.BodyForCommands].some( + (value) => typeof value === "string" && value.trim().length > 0, + ); +} + export async function tryDispatchAcpReplyHook( event: PluginHookReplyDispatchEvent, ctx: PluginHookReplyDispatchContext, ): Promise { + if (event.sendPolicy === "deny" && !hasExplicitCommandCandidate(event.ctx)) { + return; + } const runtime = await loadDispatchAcpRuntime(); const bypassForCommand = await runtime.shouldBypassAcpDispatchForCommand(event.ctx, ctx.cfg);