fix(imessage): honor disabled reply actions (#93137)

Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
This commit is contained in:
Omar Shahine
2026-06-15 12:53:35 +09:00
committed by GitHub
parent 4fc805320f
commit cc954798f2
2 changed files with 56 additions and 1 deletions

View File

@@ -121,6 +121,30 @@ describe("sendMessageIMessage receipts", () => {
expect(result.receipt.sentAt).toBeGreaterThan(0);
});
it("drops reply metadata from text sends when reply actions are disabled", async () => {
const client = createClient({ guid: "p:0/imsg-plain" });
const result = await sendMessageIMessage("chat_id:42", "hello", {
config: {
channels: {
imessage: {
actions: { reply: false },
accounts: { default: {} },
},
},
},
client,
replyToId: "reply-1",
});
const sendParams = getClientMocks(client).request.mock.calls[0]?.[1] as
| Record<string, unknown>
| undefined;
expect(sendParams).not.toHaveProperty("reply_to");
expect(result.receipt.replyToId).toBeUndefined();
expect(result.receipt.parts[0]?.replyToId).toBeUndefined();
});
it("passes the default RPC send transport", async () => {
const client = createClient({ guid: "p:0/imsg-transport-default" });
@@ -299,6 +323,35 @@ describe("sendMessageIMessage receipts", () => {
expect(client["request"]).not.toHaveBeenCalled();
});
it("drops reply metadata from media sends when reply actions are disabled", async () => {
const client = createClient({ message_id: 12345 });
const runCliJson = vi.fn().mockResolvedValueOnce({ messageId: "p:0/plain-media-guid" });
const result = await sendMessageIMessage("chat_guid:chat-1", "", {
config: {
channels: {
imessage: {
actions: { reply: false },
accounts: { default: {} },
},
},
},
client,
mediaUrl: "/tmp/image.png",
replyToId: "p:0/reply-guid",
resolveAttachmentImpl: async () => ({ path: "/tmp/image.png", contentType: "image/png" }),
runCliJson,
});
expect(result.messageId).toBe("p:0/plain-media-guid");
expect(runCliJson.mock.calls).toEqual([
[["send-attachment", "--chat", "chat-1", "--file", "/tmp/image.png", "--transport", "auto"]],
]);
expect(result.receipt.replyToId).toBeUndefined();
expect(result.receipt.parts[0]?.replyToId).toBeUndefined();
expect(client["request"]).not.toHaveBeenCalled();
});
it("resolves chat_id media-only payloads before using send-attachment", async () => {
const client = createClient({ message_id: 12345 });
const runCliJson = vi

View File

@@ -4,6 +4,7 @@ import { constants, accessSync, readFileSync } from "node:fs";
import { createRequire } from "node:module";
import os from "node:os";
import path from "node:path";
import { createActionGate } from "openclaw/plugin-sdk/channel-actions";
import {
createMessageReceiptFromOutboundResults,
type MessageReceipt,
@@ -948,7 +949,8 @@ export async function sendMessageIMessage(
throw new Error("iMessage send requires text or media");
}
const echoText = resolveOutboundEchoText(message, filePath ? mediaContentType : undefined);
const resolvedReplyToId = sanitizeReplyToId(opts.replyToId);
const replyActionsEnabled = createActionGate(account.config.actions)("reply");
const resolvedReplyToId = replyActionsEnabled ? sanitizeReplyToId(opts.replyToId) : undefined;
const runCliJson =
opts.runCliJson ??
((args: readonly string[]) => runIMessageCliJson(cliPath, dbPath, args, timeoutMs));