diff --git a/src/agents/tools/message-tool.ts b/src/agents/tools/message-tool.ts index b4ec54d62dd..299e182b460 100644 --- a/src/agents/tools/message-tool.ts +++ b/src/agents/tools/message-tool.ts @@ -2,6 +2,8 @@ import { Type } from "@sinclair/typebox"; import { BLUEBUBBLES_GROUP_ACTIONS } from "../../channels/plugins/bluebubbles-actions.js"; import { listChannelPlugins } from "../../channels/plugins/index.js"; import { + supportsChannelMessageInteractive, + supportsChannelMessageInteractiveForChannel, listChannelMessageActions, supportsChannelMessageButtons, supportsChannelMessageButtonsForChannel, @@ -163,7 +165,37 @@ const discordComponentMessageSchema = Type.Object( }, ); +const interactiveOptionSchema = Type.Object({ + label: Type.String(), + value: Type.String(), +}); + +const interactiveButtonSchema = Type.Object({ + label: Type.String(), + value: Type.String(), + style: Type.Optional(stringEnum(["primary", "secondary", "success", "danger"])), +}); + +const interactiveBlockSchema = Type.Object({ + type: stringEnum(["text", "buttons", "select"]), + text: Type.Optional(Type.String()), + buttons: Type.Optional(Type.Array(interactiveButtonSchema)), + placeholder: Type.Optional(Type.String()), + options: Type.Optional(Type.Array(interactiveOptionSchema)), +}); + +const interactiveMessageSchema = Type.Object( + { + blocks: Type.Array(interactiveBlockSchema), + }, + { + description: + "Shared interactive message payload for buttons and selects. Channels render this into their native components when supported.", + }, +); + function buildSendSchema(options: { + includeInteractive: boolean; includeButtons: boolean; includeCards: boolean; includeComponents: boolean; @@ -208,6 +240,7 @@ function buildSendSchema(options: { description: "Send image/GIF as document to avoid Telegram compression (Telegram only).", }), ), + interactive: Type.Optional(interactiveMessageSchema), buttons: Type.Optional( Type.Array( Type.Array( @@ -236,6 +269,9 @@ function buildSendSchema(options: { if (!options.includeButtons) { delete props.buttons; } + if (!options.includeInteractive) { + delete props.interactive; + } if (!options.includeCards) { delete props.card; } @@ -447,6 +483,7 @@ function buildChannelManagementSchema() { } function buildMessageToolSchemaProps(options: { + includeInteractive: boolean; includeButtons: boolean; includeCards: boolean; includeComponents: boolean; @@ -472,6 +509,7 @@ function buildMessageToolSchemaProps(options: { function buildMessageToolSchemaFromActions( actions: readonly string[], options: { + includeInteractive: boolean; includeButtons: boolean; includeCards: boolean; includeComponents: boolean; @@ -486,6 +524,7 @@ function buildMessageToolSchemaFromActions( } const MessageToolSchema = buildMessageToolSchemaFromActions(AllMessageActions, { + includeInteractive: true, includeButtons: true, includeCards: true, includeComponents: true, @@ -551,6 +590,20 @@ function resolveIncludeComponents(params: { return listChannelSupportedActions({ cfg: params.cfg, channel: "discord" }).length > 0; } +function resolveIncludeInteractive(params: { + cfg: OpenClawConfig; + currentChannelProvider?: string; +}): boolean { + const currentChannel = normalizeMessageChannel(params.currentChannelProvider); + if (currentChannel) { + return supportsChannelMessageInteractiveForChannel({ + cfg: params.cfg, + channel: currentChannel, + }); + } + return supportsChannelMessageInteractive(params.cfg); +} + function resolveIncludeTelegramPollExtras(params: { cfg: OpenClawConfig; currentChannelProvider?: string; @@ -568,6 +621,7 @@ function buildMessageToolSchema(params: { }) { const currentChannel = normalizeMessageChannel(params.currentChannelProvider); const actions = resolveMessageToolSchemaActions(params); + const includeInteractive = resolveIncludeInteractive(params); const includeButtons = currentChannel ? supportsChannelMessageButtonsForChannel({ cfg: params.cfg, channel: currentChannel }) : supportsChannelMessageButtons(params.cfg); @@ -577,6 +631,7 @@ function buildMessageToolSchema(params: { const includeComponents = resolveIncludeComponents(params); const includeTelegramPollExtras = resolveIncludeTelegramPollExtras(params); return buildMessageToolSchemaFromActions(actions.length > 0 ? actions : ["send"], { + includeInteractive, includeButtons, includeCards, includeComponents,