From da5c6ac2b62a6415fcc690a5d8cf1d3e87a7b04c Mon Sep 17 00:00:00 2001 From: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> Date: Fri, 3 Apr 2026 11:11:50 -0500 Subject: [PATCH] fix: honor bluebubbles action discovery account config --- extensions/bluebubbles/src/actions.test.ts | 22 ++++++++++++++++++++++ extensions/bluebubbles/src/actions.ts | 6 +++--- extensions/bluebubbles/src/test-harness.ts | 15 +++++++++++++-- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/extensions/bluebubbles/src/actions.test.ts b/extensions/bluebubbles/src/actions.test.ts index 0ddc603987d..0b72fe0891a 100644 --- a/extensions/bluebubbles/src/actions.test.ts +++ b/extensions/bluebubbles/src/actions.test.ts @@ -125,6 +125,28 @@ describe("bluebubblesMessageActions", () => { expect(actions).toContain("unsend"); }); + it("honors account-scoped action gates during discovery", () => { + const cfg: OpenClawConfig = { + channels: { + bluebubbles: { + serverUrl: "http://localhost:1234", + password: "test-password", + actions: { reactions: false }, + accounts: { + work: { + serverUrl: "http://localhost:5678", + password: "work-password", + actions: { reactions: true }, + }, + }, + }, + }, + }; + + expect(describeMessageTool({ cfg, accountId: "default" })?.actions).not.toContain("react"); + expect(describeMessageTool({ cfg, accountId: "work" })?.actions).toContain("react"); + }); + it("hides private-api actions when private API is disabled", () => { vi.mocked(getCachedBlueBubblesPrivateApiStatus).mockReturnValueOnce(false); const cfg: OpenClawConfig = { diff --git a/extensions/bluebubbles/src/actions.ts b/extensions/bluebubbles/src/actions.ts index dcfb3615012..c96dd526d56 100644 --- a/extensions/bluebubbles/src/actions.ts +++ b/extensions/bluebubbles/src/actions.ts @@ -72,12 +72,12 @@ const PRIVATE_API_ACTIONS = new Set([ ]); export const bluebubblesMessageActions: ChannelMessageActionAdapter = { - describeMessageTool: ({ cfg, currentChannelId }) => { - const account = resolveBlueBubblesAccount({ cfg: cfg }); + describeMessageTool: ({ cfg, accountId, currentChannelId }) => { + const account = resolveBlueBubblesAccount({ cfg, accountId }); if (!account.enabled || !account.configured) { return null; } - const gate = createActionGate(cfg.channels?.bluebubbles?.actions); + const gate = createActionGate(account.config.actions); const actions = new Set(); const macOS26 = isMacOS26OrHigher(account.accountId); const privateApiStatus = getCachedBlueBubblesPrivateApiStatus(account.accountId); diff --git a/extensions/bluebubbles/src/test-harness.ts b/extensions/bluebubbles/src/test-harness.ts index 544625514fb..87379280c48 100644 --- a/extensions/bluebubbles/src/test-harness.ts +++ b/extensions/bluebubbles/src/test-harness.ts @@ -31,9 +31,20 @@ export function resolveBlueBubblesAccountFromConfig(params: { cfg?: { channels?: { bluebubbles?: Record } }; accountId?: string; }) { - const config = params.cfg?.channels?.bluebubbles ?? {}; + const baseConfig = params.cfg?.channels?.bluebubbles ?? {}; + const accountId = params.accountId ?? "default"; + const accountConfig = + accountId === "default" + ? {} + : ((baseConfig.accounts as Record | undefined> | undefined)?.[ + accountId + ] ?? {}); + const config = { + ...baseConfig, + ...accountConfig, + }; return { - accountId: params.accountId ?? "default", + accountId, enabled: config.enabled !== false, configured: Boolean(config.serverUrl && config.password), config,