Skip to content

Commit 92bea97

Browse files
vincentkocsteipete
authored andcommitted
Channels: add message action capabilities
1 parent 69a8532 commit 92bea97

File tree

15 files changed

+235
-139
lines changed

15 files changed

+235
-139
lines changed

extensions/discord/src/channel-actions.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,10 @@ export const discordMessageActions: ChannelMessageActionAdapter = {
106106
}
107107
return Array.from(actions);
108108
},
109-
supportsInteractive: ({ cfg }) =>
110-
listTokenSourcedAccounts(listEnabledDiscordAccounts(cfg)).length > 0,
109+
getCapabilities: ({ cfg }) =>
110+
listTokenSourcedAccounts(listEnabledDiscordAccounts(cfg)).length > 0
111+
? (["interactive", "components"] as const)
112+
: [],
111113
extractToolSend: ({ args }) => {
112114
const action = typeof args.action === "string" ? args.action.trim() : "";
113115
if (action === "sendMessage") {

extensions/feishu/src/channel.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,11 +219,11 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
219219
}
220220
return Array.from(actions);
221221
},
222-
supportsCards: ({ cfg }) => {
223-
return (
224-
cfg.channels?.feishu?.enabled !== false &&
222+
getCapabilities: ({ cfg }) => {
223+
return cfg.channels?.feishu?.enabled !== false &&
225224
Boolean(resolveFeishuCredentials(cfg.channels?.feishu as FeishuConfig | undefined))
226-
);
225+
? (["cards"] as const)
226+
: [];
227227
},
228228
handleAction: async (ctx) => {
229229
const account = resolveFeishuAccount({ cfg: ctx.cfg, accountId: ctx.accountId ?? undefined });

extensions/mattermost/src/channel.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,11 @@ const mattermostMessageActions: ChannelMessageActionAdapter = {
7171
supportsAction: ({ action }) => {
7272
return action === "send" || action === "react";
7373
},
74-
supportsButtons: ({ cfg }) => {
74+
getCapabilities: ({ cfg }) => {
7575
const accounts = listMattermostAccountIds(cfg)
7676
.map((id) => resolveMattermostAccount({ cfg, accountId: id }))
7777
.filter((a) => a.enabled && a.botToken?.trim() && a.baseUrl?.trim());
78-
return accounts.length > 0;
78+
return accounts.length > 0 ? (["buttons"] as const) : [];
7979
},
8080
handleAction: async ({ action, params, cfg, accountId }) => {
8181
if (action === "react") {

extensions/msteams/src/channel.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -366,11 +366,11 @@ export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount> = {
366366
}
367367
return ["poll"] satisfies ChannelMessageActionName[];
368368
},
369-
supportsCards: ({ cfg }) => {
370-
return (
371-
cfg.channels?.msteams?.enabled !== false &&
369+
getCapabilities: ({ cfg }) => {
370+
return cfg.channels?.msteams?.enabled !== false &&
372371
Boolean(resolveMSTeamsCredentials(cfg.channels?.msteams))
373-
);
372+
? (["cards"] as const)
373+
: [];
374374
},
375375
handleAction: async (ctx) => {
376376
// Handle send action with card parameter

extensions/slack/src/channel.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,16 @@ export const slackPlugin: ChannelPlugin<ResolvedSlackAccount> = {
292292
},
293293
actions: {
294294
listActions: ({ cfg }) => listSlackMessageActions(cfg),
295+
getCapabilities: ({ cfg }) => {
296+
const capabilities = new Set<"interactive" | "blocks">();
297+
if (listSlackMessageActions(cfg).includes("send")) {
298+
capabilities.add("blocks");
299+
}
300+
if (isSlackInteractiveRepliesEnabled({ cfg })) {
301+
capabilities.add("interactive");
302+
}
303+
return Array.from(capabilities);
304+
},
295305
extractToolSend: ({ args }) => extractSlackToolSend(args),
296306
handleAction: async (ctx) =>
297307
await handleSlackMessageAction({

extensions/telegram/src/channel-actions.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ import {
2424
listEnabledTelegramAccounts,
2525
resolveTelegramPollActionGateState,
2626
} from "./accounts.js";
27+
import { buildTelegramInteractiveButtons } from "./button-types.js";
2728
import { isTelegramInlineButtonsEnabled } from "./inline-buttons.js";
28-
import { buildTelegramInteractiveButtons } from "./shared-interactive.js";
2929

3030
const providerId = "telegram";
3131

@@ -124,23 +124,15 @@ export const telegramMessageActions: ChannelMessageActionAdapter = {
124124
}
125125
return Array.from(actions);
126126
},
127-
supportsInteractive: ({ cfg }) => {
127+
getCapabilities: ({ cfg }) => {
128128
const accounts = listTokenSourcedAccounts(listEnabledTelegramAccounts(cfg));
129129
if (accounts.length === 0) {
130-
return false;
131-
}
132-
return accounts.some((account) =>
133-
isTelegramInlineButtonsEnabled({ cfg, accountId: account.accountId }),
134-
);
135-
},
136-
supportsButtons: ({ cfg }) => {
137-
const accounts = listTokenSourcedAccounts(listEnabledTelegramAccounts(cfg));
138-
if (accounts.length === 0) {
139-
return false;
130+
return [];
140131
}
141-
return accounts.some((account) =>
132+
const buttonsEnabled = accounts.some((account) =>
142133
isTelegramInlineButtonsEnabled({ cfg, accountId: account.accountId }),
143134
);
135+
return buttonsEnabled ? (["interactive", "buttons"] as const) : [];
144136
},
145137
extractToolSend: ({ args }) => {
146138
return extractToolSend(args, "sendMessage");

extensions/zalo/src/actions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const zaloMessageActions: ChannelMessageActionAdapter = {
2424
const actions = new Set<ChannelMessageActionName>(["send"]);
2525
return Array.from(actions);
2626
},
27-
supportsButtons: () => false,
27+
getCapabilities: () => [],
2828
extractToolSend: ({ args }) => extractToolSend(args, "sendMessage"),
2929
handleAction: async ({ action, params, cfg, accountId }) => {
3030
if (action === "send") {

src/agents/tools/message-tool.test.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { afterEach, describe, expect, it, vi } from "vitest";
2+
import type { ChannelMessageCapability } from "../../channels/plugins/message-capabilities.js";
23
import type { ChannelMessageActionName, ChannelPlugin } from "../../channels/plugins/types.js";
34
import type { MessageActionRunResult } from "../../infra/outbound/message-action-runner.js";
45
import { setActivePluginRegistry } from "../../plugins/runtime.js";
@@ -47,9 +48,10 @@ function createChannelPlugin(params: {
4748
blurb: string;
4849
actions?: ChannelMessageActionName[];
4950
listActions?: NonNullable<NonNullable<ChannelPlugin["actions"]>["listActions"]>;
50-
supportsButtons?: boolean;
51+
capabilities?: readonly ChannelMessageCapability[];
5152
messaging?: ChannelPlugin["messaging"];
5253
}): ChannelPlugin {
54+
const actionCapabilities = params.capabilities;
5355
return {
5456
id: params.id as ChannelPlugin["id"],
5557
meta: {
@@ -71,7 +73,9 @@ function createChannelPlugin(params: {
7173
(() => {
7274
return (params.actions ?? []) as never;
7375
}),
74-
...(params.supportsButtons ? { supportsButtons: () => true } : {}),
76+
...(actionCapabilities
77+
? { getCapabilities: (_params: { cfg: unknown }) => actionCapabilities }
78+
: {}),
7579
},
7680
};
7781
}
@@ -145,7 +149,7 @@ describe("message tool schema scoping", () => {
145149
docsPath: "/channels/telegram",
146150
blurb: "Telegram test plugin.",
147151
actions: ["send", "react", "poll"],
148-
supportsButtons: true,
152+
capabilities: ["interactive", "buttons"],
149153
});
150154

151155
const discordPlugin = createChannelPlugin({
@@ -154,6 +158,16 @@ describe("message tool schema scoping", () => {
154158
docsPath: "/channels/discord",
155159
blurb: "Discord test plugin.",
156160
actions: ["send", "poll", "poll-vote"],
161+
capabilities: ["interactive", "components"],
162+
});
163+
164+
const slackPlugin = createChannelPlugin({
165+
id: "slack",
166+
label: "Slack",
167+
docsPath: "/channels/slack",
168+
blurb: "Slack test plugin.",
169+
actions: ["send", "react"],
170+
capabilities: ["interactive", "blocks"],
157171
});
158172

159173
afterEach(() => {
@@ -164,6 +178,7 @@ describe("message tool schema scoping", () => {
164178
{
165179
provider: "telegram",
166180
expectComponents: false,
181+
expectBlocks: false,
167182
expectButtons: true,
168183
expectButtonStyle: true,
169184
expectTelegramPollExtras: true,
@@ -172,16 +187,27 @@ describe("message tool schema scoping", () => {
172187
{
173188
provider: "discord",
174189
expectComponents: true,
190+
expectBlocks: false,
175191
expectButtons: false,
176192
expectButtonStyle: false,
177193
expectTelegramPollExtras: true,
178194
expectedActions: ["send", "poll", "poll-vote", "react"],
179195
},
196+
{
197+
provider: "slack",
198+
expectComponents: false,
199+
expectBlocks: true,
200+
expectButtons: false,
201+
expectButtonStyle: false,
202+
expectTelegramPollExtras: true,
203+
expectedActions: ["send", "react", "poll", "poll-vote"],
204+
},
180205
])(
181206
"scopes schema fields for $provider",
182207
({
183208
provider,
184209
expectComponents,
210+
expectBlocks,
185211
expectButtons,
186212
expectButtonStyle,
187213
expectTelegramPollExtras,
@@ -191,6 +217,7 @@ describe("message tool schema scoping", () => {
191217
createTestRegistry([
192218
{ pluginId: "telegram", source: "test", plugin: telegramPlugin },
193219
{ pluginId: "discord", source: "test", plugin: discordPlugin },
220+
{ pluginId: "slack", source: "test", plugin: slackPlugin },
194221
]),
195222
);
196223

@@ -206,6 +233,11 @@ describe("message tool schema scoping", () => {
206233
} else {
207234
expect(properties.components).toBeUndefined();
208235
}
236+
if (expectBlocks) {
237+
expect(properties.blocks).toBeDefined();
238+
} else {
239+
expect(properties.blocks).toBeUndefined();
240+
}
209241
if (expectButtons) {
210242
expect(properties.buttons).toBeDefined();
211243
} else {
@@ -263,7 +295,7 @@ describe("message tool schema scoping", () => {
263295
.channels?.telegram;
264296
return telegramCfg?.actions?.poll === false ? ["send", "react"] : ["send", "react", "poll"];
265297
},
266-
supportsButtons: true,
298+
capabilities: ["interactive", "buttons"],
267299
});
268300

269301
setActivePluginRegistry(

0 commit comments

Comments
 (0)