@andreprado/agentkit 0.1.0-alpha.20 → 0.1.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,8 +15,8 @@ type DeployState = LocalDeployState;
15
15
  type CloudChannel = {
16
16
  id: string;
17
17
  name: string;
18
- type: "website" | "telegram" | "whatsapp" | "discord" | "slack";
19
- provider: "agentkit" | "telegram" | "zapster" | "meta" | "discord" | "slack";
18
+ type: "website" | "telegram" | "whatsapp" | "discord" | "slack" | "webhook";
19
+ provider: "agentkit" | "telegram" | "zapster" | "meta" | "uazapi" | "evolution" | "discord" | "slack" | "generic";
20
20
  mode?: "interactions" | "bot";
21
21
  status: string;
22
22
  webhook_url: string;
@@ -78,10 +78,12 @@ type ChannelBufferActionResponse = {
78
78
  type ChannelSetupResponse = {
79
79
  channel: CloudChannel;
80
80
  setup?: {
81
- provider: "telegram";
81
+ provider: "telegram" | "uazapi" | "evolution";
82
82
  webhook_url: string;
83
83
  status: "configured";
84
84
  bot_username?: string;
85
+ webhook_count?: number;
86
+ matched_webhook_id?: string;
85
87
  get_webhook_info?: {
86
88
  url: string;
87
89
  pending_update_count: number;
@@ -144,7 +146,7 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
144
146
  const name = second;
145
147
 
146
148
  if (!type || !name || !isCliChannelType(type)) {
147
- throw new Error("Usage: agentkit channels add <website|telegram|whatsapp|discord|slack> <name> [--provider zapster|meta] [--mode interactions|bot]");
149
+ throw new Error("Usage: agentkit channels add <website|telegram|whatsapp|discord|slack|webhook> <name> [--provider zapster|meta|uazapi|evolution] [--mode interactions|bot]");
148
150
  }
149
151
 
150
152
  const response = await createHostedChannel(type, name, args.flags.provider, args.flags.mode, args.flags.api);
@@ -158,7 +160,7 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
158
160
  const name = second;
159
161
 
160
162
  if (!type || !name || !isCliChannelType(type)) {
161
- throw new Error("Usage: agentkit channels connect <website|telegram|whatsapp|discord|slack> <name> [--provider zapster|meta] [--mode interactions|bot]");
163
+ throw new Error("Usage: agentkit channels connect <website|telegram|whatsapp|discord|slack|webhook> <name> [--provider zapster|meta|uazapi|evolution] [--mode interactions|bot]");
162
164
  }
163
165
 
164
166
  const created = await createHostedChannel(type, name, args.flags.provider, args.flags.mode, args.flags.api);
@@ -195,6 +197,37 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
195
197
  return;
196
198
  }
197
199
 
200
+ if (channel.type === "whatsapp" && channel.provider === "uazapi") {
201
+ const setup = await applyProviderWebhook(channel, args.flags.api);
202
+ channel = setup.channel ?? channel;
203
+ console.log("UAZAPI webhook setup: OK");
204
+ console.log("UAZAPI webhook confirmed.");
205
+ if (setup.setup?.webhook_count !== undefined) {
206
+ console.log(`UAZAPI webhooks: ${setup.setup.webhook_count}`);
207
+ }
208
+
209
+ const smoke = await runConnectSmoke(() => runChannelSmoke(channel, args.flags.api, args.flags.message));
210
+ printUazapiHumanNextStep(channel);
211
+ if (!smoke) {
212
+ process.exitCode = 1;
213
+ }
214
+ return;
215
+ }
216
+
217
+ if (channel.type === "whatsapp" && channel.provider === "evolution") {
218
+ const setup = await applyProviderWebhook(channel, args.flags.api);
219
+ channel = setup.channel ?? channel;
220
+ console.log("Evolution webhook setup: OK");
221
+ console.log("Evolution webhook confirmed.");
222
+
223
+ const smoke = await runConnectSmoke(() => runChannelSmoke(channel, args.flags.api, args.flags.message));
224
+ printEvolutionHumanNextStep(channel);
225
+ if (!smoke) {
226
+ process.exitCode = 1;
227
+ }
228
+ return;
229
+ }
230
+
198
231
  if (channel.type === "discord") {
199
232
  const smoke = await runConnectSmoke(() => runChannelSmoke(channel, args.flags.api, args.flags.message));
200
233
  printDiscordHumanNextStep(channel);
@@ -213,6 +246,15 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
213
246
  return;
214
247
  }
215
248
 
249
+ if (channel.type === "webhook") {
250
+ const smoke = await runConnectSmoke(() => runChannelSmoke(channel, args.flags.api, args.flags.message));
251
+ printGenericWebhookHumanNextStep(channel);
252
+ if (!smoke) {
253
+ process.exitCode = 1;
254
+ }
255
+ return;
256
+ }
257
+
216
258
  if (channel.type === "whatsapp" && channel.provider === "zapster") {
217
259
  const smoke = await runConnectSmoke(() => runChannelSmoke(channel, args.flags.api, args.flags.message));
218
260
  console.log(`Next human step: paste ${channel.webhook_url} into ${channel.provider}'s webhook settings.`);
@@ -249,12 +291,34 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
249
291
  console.log(`Setup: call Telegram setWebhook with ${channel.webhook_url}`);
250
292
  console.log("Use --apply to mutate Telegram webhook settings.");
251
293
  }
294
+ } else if (channel.type === "whatsapp" && channel.provider === "uazapi") {
295
+ if (args.flags.apply) {
296
+ const setup = await applyProviderWebhook(channel, args.flags.api);
297
+ console.log("UAZAPI webhook confirmed.");
298
+ printChannelStatus(setup.channel ?? channel);
299
+ printUazapiHumanNextStep(setup.channel ?? channel);
300
+ } else {
301
+ console.log(`Setup: configure UAZAPI /webhook with ${channel.webhook_url}`);
302
+ console.log("Use --apply to mutate UAZAPI webhook settings.");
303
+ }
304
+ } else if (channel.type === "whatsapp" && channel.provider === "evolution") {
305
+ if (args.flags.apply) {
306
+ const setup = await applyProviderWebhook(channel, args.flags.api);
307
+ console.log("Evolution webhook confirmed.");
308
+ printChannelStatus(setup.channel ?? channel);
309
+ printEvolutionHumanNextStep(setup.channel ?? channel);
310
+ } else {
311
+ console.log(`Setup: configure Evolution /webhook/set/{instance} with ${channel.webhook_url}`);
312
+ console.log("Use --apply to mutate Evolution webhook settings.");
313
+ }
252
314
  } else if (channel.type === "whatsapp") {
253
315
  console.log(`Next step: paste this URL into ${channel.provider}'s webhook settings.`);
254
316
  } else if (channel.type === "discord") {
255
317
  printDiscordHumanNextStep(channel);
256
318
  } else if (channel.type === "slack") {
257
319
  printSlackHumanNextStep(channel);
320
+ } else if (channel.type === "webhook") {
321
+ printGenericWebhookHumanNextStep(channel);
258
322
  } else {
259
323
  console.log(`Website endpoint: ${channel.webhook_url}`);
260
324
  console.log("Use this endpoint from the AgentKit website client or a signed server-to-server test.");
@@ -298,8 +362,9 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
298
362
 
299
363
  const channel = await resolveCloudChannelByName(first, args.flags.api);
300
364
  const apiUrl = parseCloudApiUrl(args.flags.api);
301
- const body = await channelTestBody(channel, args.flags.message, args.flags.fixture);
302
- const signed = await runServerSignedChannelTest(channel, apiUrl, body);
365
+ const signed = channel.type === "whatsapp" && channel.provider === "evolution" && !args.flags.fixture
366
+ ? await runServerGeneratedChannelTest(channel, apiUrl, args.flags.message)
367
+ : await runServerSignedChannelTest(channel, apiUrl, await channelTestBody(channel, args.flags.message, args.flags.fixture));
303
368
  const response = signed.response;
304
369
  const payload = signed.payload;
305
370
 
@@ -427,7 +492,7 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
427
492
  }
428
493
 
429
494
  async function createHostedChannel(
430
- type: "website" | "telegram" | "whatsapp" | "discord" | "slack",
495
+ type: "website" | "telegram" | "whatsapp" | "discord" | "slack" | "webhook",
431
496
  name: string,
432
497
  providerFlag: string | boolean | undefined,
433
498
  modeFlag: string | boolean | undefined,
@@ -460,8 +525,10 @@ async function runChannelSmoke(
460
525
  apiFlag: string | boolean | undefined,
461
526
  messageFlag: string | boolean | undefined,
462
527
  ): Promise<{ status: string; delivery_id?: string }> {
463
- const body = await channelTestBody(channel, messageFlag, undefined);
464
- const { response, payload } = await runServerSignedChannelTest(channel, parseCloudApiUrl(apiFlag), body);
528
+ const apiUrl = parseCloudApiUrl(apiFlag);
529
+ const { response, payload } = channel.type === "whatsapp" && channel.provider === "evolution"
530
+ ? await runServerGeneratedChannelTest(channel, apiUrl, messageFlag)
531
+ : await runServerSignedChannelTest(channel, apiUrl, await channelTestBody(channel, messageFlag, undefined));
465
532
 
466
533
  if (!response.ok) {
467
534
  throw new Error(readCloudError(payload, response.status));
@@ -477,6 +544,35 @@ async function runChannelSmoke(
477
544
  return { status: "unknown" };
478
545
  }
479
546
 
547
+ async function runServerGeneratedChannelTest(
548
+ channel: CloudChannel,
549
+ apiUrl: string,
550
+ messageFlag: string | boolean | undefined,
551
+ ): Promise<{ response: Response; payload: unknown }> {
552
+ const body: Record<string, unknown> = {};
553
+
554
+ if (typeof messageFlag === "string") {
555
+ body.message = messageFlag;
556
+ }
557
+
558
+ const response = await cloudFetch(apiUrl, `/v1/channels/${encodeURIComponent(channel.id)}/test`, {
559
+ method: "POST",
560
+ headers: { "Content-Type": "application/json" },
561
+ body: JSON.stringify(body),
562
+ });
563
+ const payload = await response.json().catch(() => null);
564
+
565
+ if (!response.ok && isUnsupportedChannelTestRoute(payload)) {
566
+ return runLocallySignedChannelTest(
567
+ channel,
568
+ apiUrl,
569
+ await channelTestBody(channel, messageFlag, undefined),
570
+ );
571
+ }
572
+
573
+ return { response, payload };
574
+ }
575
+
480
576
  async function runConnectSmoke(
481
577
  run: () => Promise<{ status: string; delivery_id?: string }>,
482
578
  ): Promise<{ status: string; delivery_id?: string } | null> {
@@ -521,6 +617,14 @@ async function runLocallySignedChannelTest(
521
617
  webhookUrl.searchParams.set("token", process.env.ZAPSTER_WEBHOOK_TOKEN ?? "");
522
618
  }
523
619
 
620
+ if (channel.required_secrets.some((secret) => secret.name === "UAZAPI_WEBHOOK_TOKEN")) {
621
+ webhookUrl.searchParams.set("token", process.env.UAZAPI_WEBHOOK_TOKEN ?? "");
622
+ }
623
+
624
+ if (channel.required_secrets.some((secret) => secret.name === "EVOLUTION_WEBHOOK_TOKEN")) {
625
+ webhookUrl.searchParams.set("token", process.env.EVOLUTION_WEBHOOK_TOKEN ?? "");
626
+ }
627
+
524
628
  const path = `${webhookUrl.pathname}${webhookUrl.search}`;
525
629
  const headers = channelTestHeaders(channel, rawBody);
526
630
  const response = await cloudFetch(apiUrl, path, {
@@ -624,6 +728,17 @@ async function applyTelegramWebhook(
624
728
  };
625
729
  }
626
730
 
731
+ async function applyProviderWebhook(
732
+ channel: CloudChannel,
733
+ apiFlag: string | boolean | undefined,
734
+ ): Promise<ChannelSetupResponse> {
735
+ return cloudPost<ChannelSetupResponse>(
736
+ parseCloudApiUrl(apiFlag),
737
+ `/v1/channels/${encodeURIComponent(channel.id)}/setup`,
738
+ {},
739
+ );
740
+ }
741
+
627
742
  function shouldFallbackToLocalTelegramSetup(error: unknown): boolean {
628
743
  if (!(error instanceof Error)) {
629
744
  return false;
@@ -669,6 +784,22 @@ function printTelegramHumanNextStep(channelName: string, botUsername: string | u
669
784
  console.log(` Then run: npm run agentkit -- channels deliveries list ${channelName}`);
670
785
  }
671
786
 
787
+ function printUazapiHumanNextStep(channel: CloudChannel): void {
788
+ console.log("");
789
+ console.log("Next human step:");
790
+ console.log(" Confirm the UAZAPI instance is connected to WhatsApp");
791
+ console.log(' Send "hello" from a real WhatsApp contact');
792
+ console.log(` Then run: npm run agentkit -- channels deliveries list ${channel.name}`);
793
+ }
794
+
795
+ function printEvolutionHumanNextStep(channel: CloudChannel): void {
796
+ console.log("");
797
+ console.log("Next human step:");
798
+ console.log(" Confirm the Evolution API instance is connected to WhatsApp");
799
+ console.log(' Send "hello" from a real WhatsApp contact');
800
+ console.log(` Then run: npm run agentkit -- channels deliveries list ${channel.name}`);
801
+ }
802
+
672
803
  function printDiscordHumanNextStep(channel: CloudChannel): void {
673
804
  console.log("");
674
805
  console.log("Next human step:");
@@ -702,6 +833,15 @@ function printSlackHumanNextStep(channel: CloudChannel): void {
702
833
  console.log(` Then run: npm run agentkit -- channels deliveries list ${channel.name}`);
703
834
  }
704
835
 
836
+ function printGenericWebhookHumanNextStep(channel: CloudChannel): void {
837
+ console.log("");
838
+ console.log("Next human step:");
839
+ console.log(` Point n8n, Make, Zapier, or your server to ${channel.webhook_url}`);
840
+ console.log(" Send JSON with event_id, external_id, and message");
841
+ console.log(" Authenticate with Authorization: Bearer <AGENTKIT_WEBHOOK_SECRET>");
842
+ console.log(` Then run: npm run agentkit -- channels deliveries list ${channel.name}`);
843
+ }
844
+
705
845
  function isRecord(value: unknown): value is Record<string, unknown> {
706
846
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
707
847
  }
@@ -714,8 +854,8 @@ async function readDeployStateIfExists(cwd: string): Promise<DeployState | null>
714
854
  return readLocalDeployStateIfExists(cwd);
715
855
  }
716
856
 
717
- function isCliChannelType(value: string): value is "website" | "telegram" | "whatsapp" | "discord" | "slack" {
718
- return value === "website" || value === "telegram" || value === "whatsapp" || value === "discord" || value === "slack";
857
+ function isCliChannelType(value: string): value is "website" | "telegram" | "whatsapp" | "discord" | "slack" | "webhook" {
858
+ return value === "website" || value === "telegram" || value === "whatsapp" || value === "discord" || value === "slack" || value === "webhook";
719
859
  }
720
860
 
721
861
  async function readDeployStateRequired(cwd: string): Promise<DeployState> {
@@ -1009,10 +1149,18 @@ function channelProviderForCli(type: string, providerFlag: string | boolean | un
1009
1149
  return "slack";
1010
1150
  }
1011
1151
 
1152
+ if (type === "webhook") {
1153
+ if (providerFlag !== undefined) {
1154
+ throw new Error("--provider is not supported for generic webhook channels.");
1155
+ }
1156
+
1157
+ return "generic";
1158
+ }
1159
+
1012
1160
  const provider = providerFlag ?? "zapster";
1013
1161
 
1014
- if (provider !== "zapster" && provider !== "meta") {
1015
- throw new Error('WhatsApp provider must be "zapster" or "meta".');
1162
+ if (provider !== "zapster" && provider !== "meta" && provider !== "uazapi" && provider !== "evolution") {
1163
+ throw new Error('WhatsApp provider must be "zapster", "meta", "uazapi", or "evolution".');
1016
1164
  }
1017
1165
 
1018
1166
  return provider;
@@ -1059,6 +1207,14 @@ function defaultChannelSecretsForCli(type: string, provider: string, mode?: "int
1059
1207
  return ["META_WHATSAPP_ACCESS_TOKEN", "META_WHATSAPP_APP_SECRET", "META_WHATSAPP_VERIFY_TOKEN"];
1060
1208
  }
1061
1209
 
1210
+ if (type === "whatsapp" && provider === "uazapi") {
1211
+ return ["UAZAPI_BASE_URL", "UAZAPI_TOKEN"];
1212
+ }
1213
+
1214
+ if (type === "whatsapp" && provider === "evolution") {
1215
+ return ["EVOLUTION_API_BASE_URL", "EVOLUTION_API_KEY", "EVOLUTION_INSTANCE_NAME", "EVOLUTION_WEBHOOK_TOKEN"];
1216
+ }
1217
+
1062
1218
  if (type === "discord") {
1063
1219
  return mode === "bot" ? ["DISCORD_BOT_TOKEN"] : ["DISCORD_PUBLIC_KEY"];
1064
1220
  }
@@ -1067,6 +1223,10 @@ function defaultChannelSecretsForCli(type: string, provider: string, mode?: "int
1067
1223
  return ["SLACK_BOT_TOKEN", "SLACK_SIGNING_SECRET"];
1068
1224
  }
1069
1225
 
1226
+ if (type === "webhook") {
1227
+ return ["AGENTKIT_WEBHOOK_SECRET"];
1228
+ }
1229
+
1070
1230
  return [];
1071
1231
  }
1072
1232
 
@@ -1117,6 +1277,45 @@ async function channelTestBody(
1117
1277
  });
1118
1278
  }
1119
1279
 
1280
+ if (channel.type === "whatsapp" && channel.provider === "uazapi") {
1281
+ return JSON.stringify({
1282
+ EventType: "messages",
1283
+ event: "messages",
1284
+ message: {
1285
+ id: `uaz_msg_${Date.now()}`,
1286
+ messageid: `uaz_wamid_${Date.now()}`,
1287
+ chatid: "5511999999999@s.whatsapp.net",
1288
+ sender: "5511999999999@s.whatsapp.net",
1289
+ senderName: "AgentKit",
1290
+ fromMe: false,
1291
+ wasSentByApi: false,
1292
+ messageType: "text",
1293
+ text: message,
1294
+ messageTimestamp: Date.now(),
1295
+ },
1296
+ });
1297
+ }
1298
+
1299
+ if (channel.type === "whatsapp" && channel.provider === "evolution") {
1300
+ return JSON.stringify({
1301
+ event: "MESSAGES_UPSERT",
1302
+ instance: process.env.EVOLUTION_INSTANCE_NAME ?? "local",
1303
+ data: {
1304
+ key: {
1305
+ remoteJid: "5511999999999@s.whatsapp.net",
1306
+ fromMe: false,
1307
+ id: `evo_wamid_${Date.now()}`,
1308
+ },
1309
+ pushName: "AgentKit",
1310
+ messageType: "conversation",
1311
+ message: {
1312
+ conversation: message,
1313
+ },
1314
+ messageTimestamp: Math.floor(Date.now() / 1000),
1315
+ },
1316
+ });
1317
+ }
1318
+
1120
1319
  if (channel.type === "whatsapp") {
1121
1320
  return JSON.stringify({
1122
1321
  event_id: `evt_${Date.now()}`,
@@ -1201,6 +1400,14 @@ async function channelTestBody(
1201
1400
  });
1202
1401
  }
1203
1402
 
1403
+ if (channel.type === "webhook") {
1404
+ return JSON.stringify({
1405
+ event_id: `evt_${Date.now()}`,
1406
+ external_id: "agentkit-webhook-test",
1407
+ message,
1408
+ });
1409
+ }
1410
+
1204
1411
  return JSON.stringify({
1205
1412
  event_id: `web_${Date.now()}`,
1206
1413
  visitor_id: "visitor_test",
@@ -1268,6 +1475,62 @@ async function channelAudioTestBody(
1268
1475
  });
1269
1476
  }
1270
1477
 
1478
+ if (channel.type === "whatsapp" && channel.provider === "uazapi") {
1479
+ if (fixture !== "voice-note" && fixture !== "audio-file" && fixture !== "uazapi-audio" && fixture !== "uazapi-audio-message") {
1480
+ throw new Error('UAZAPI audio smoke supports --fixture voice-note, --fixture audio-file, or --fixture uazapi-audio.');
1481
+ }
1482
+
1483
+ return JSON.stringify({
1484
+ EventType: "messages",
1485
+ event: "messages",
1486
+ message: {
1487
+ id: `uaz_audio_${Date.now()}`,
1488
+ messageid: `uaz_wamid_audio_${Date.now()}`,
1489
+ chatid: "5511999999999@s.whatsapp.net",
1490
+ sender: "5511999999999@s.whatsapp.net",
1491
+ senderName: "AgentKit",
1492
+ fromMe: false,
1493
+ wasSentByApi: false,
1494
+ messageType: fixture === "voice-note" ? "ptt" : "audio",
1495
+ mimetype: "audio/ogg",
1496
+ duration: 12,
1497
+ file_size: 42,
1498
+ messageTimestamp: Date.now(),
1499
+ },
1500
+ });
1501
+ }
1502
+
1503
+ if (channel.type === "whatsapp" && channel.provider === "evolution") {
1504
+ if (fixture !== "voice-note" && fixture !== "audio-file" && fixture !== "evolution-audio" && fixture !== "evolution-audio-message") {
1505
+ throw new Error(
1506
+ 'Evolution audio smoke supports --fixture voice-note, --fixture audio-file, --fixture evolution-audio, or --fixture evolution-audio-message.',
1507
+ );
1508
+ }
1509
+
1510
+ return JSON.stringify({
1511
+ event: "MESSAGES_UPSERT",
1512
+ instance: process.env.EVOLUTION_INSTANCE_NAME ?? "local",
1513
+ data: {
1514
+ key: {
1515
+ remoteJid: "5511999999999@s.whatsapp.net",
1516
+ fromMe: false,
1517
+ id: `evo_wamid_audio_${Date.now()}`,
1518
+ },
1519
+ pushName: "AgentKit",
1520
+ messageType: "audioMessage",
1521
+ message: {
1522
+ audioMessage: {
1523
+ mimetype: "audio/ogg",
1524
+ seconds: 12,
1525
+ ptt: fixture === "voice-note",
1526
+ fileLength: 42,
1527
+ },
1528
+ },
1529
+ messageTimestamp: Math.floor(Date.now() / 1000),
1530
+ },
1531
+ });
1532
+ }
1533
+
1271
1534
  throw new Error(`Audio smoke is not supported for ${channel.type}/${channel.provider}.`);
1272
1535
  }
1273
1536
 
@@ -1311,5 +1574,13 @@ function channelTestHeaders(channel: CloudChannel, rawBody: string): Headers {
1311
1574
  }
1312
1575
  }
1313
1576
 
1577
+ if (channel.type === "webhook") {
1578
+ const secret = process.env.AGENTKIT_WEBHOOK_SECRET;
1579
+
1580
+ if (secret) {
1581
+ headers.set("Authorization", `Bearer ${secret}`);
1582
+ }
1583
+ }
1584
+
1314
1585
  return headers;
1315
1586
  }
package/src/cli/help.ts CHANGED
@@ -89,8 +89,8 @@ Usage:
89
89
  agentkit conversations show <conversation-id> [--deploy]
90
90
  agentkit conversations trace <conversation-id> [--deploy]
91
91
  agentkit channels list
92
- agentkit channels add <website|telegram|whatsapp|discord|slack> <name> [--provider zapster|meta] [--mode interactions|bot] [--api <url>]
93
- agentkit channels connect <website|telegram|whatsapp|discord|slack> <name> [--provider zapster|meta] [--mode interactions|bot] [--api <url>]
92
+ agentkit channels add <website|telegram|whatsapp|discord|slack|webhook> <name> [--provider zapster|meta|uazapi|evolution] [--mode interactions|bot] [--api <url>]
93
+ agentkit channels connect <website|telegram|whatsapp|discord|slack|webhook> <name> [--provider zapster|meta|uazapi|evolution] [--mode interactions|bot] [--api <url>]
94
94
  agentkit channels setup <name> [--apply] [--api <url>]
95
95
  agentkit channels status <name> [--api <url>]
96
96
  agentkit channels doctor <name> [--api <url>]
package/src/index.ts CHANGED
@@ -2,9 +2,9 @@ export type AgentRuntime = "local" | "edge";
2
2
  export type AgentProviderName = "test" | "openai" | "anthropic" | "openrouter" | "custom";
3
3
  export type AccessMode = "private" | "token" | "public";
4
4
  export type ToolVisibility = "user" | "internal";
5
- export type ChannelType = "website" | "telegram" | "whatsapp" | "discord" | "slack";
6
- export type ChannelProvider = "agentkit" | "telegram" | "zapster" | "meta" | "discord" | "slack";
7
- export type WhatsappChannelProvider = "zapster" | "meta";
5
+ export type ChannelType = "website" | "telegram" | "whatsapp" | "discord" | "slack" | "webhook";
6
+ export type ChannelProvider = "agentkit" | "telegram" | "zapster" | "meta" | "uazapi" | "evolution" | "discord" | "slack" | "generic";
7
+ export type WhatsappChannelProvider = "zapster" | "meta" | "uazapi" | "evolution";
8
8
  export type DiscordChannelMode = "interactions" | "bot";
9
9
  export type TelegramAllowedUpdate = "message";
10
10
  export type ChannelAudioMode = "off" | "reject" | "transcribe";
@@ -180,12 +180,30 @@ export type SlackChannelConfig = {
180
180
  audio?: never;
181
181
  };
182
182
 
183
+ export type GenericWebhookChannelInput = {
184
+ name: string;
185
+ secrets?: string[];
186
+ limits?: ChannelLimits;
187
+ buffer?: ChannelBufferInput;
188
+ };
189
+
190
+ export type GenericWebhookChannelConfig = {
191
+ type: "webhook";
192
+ provider: "generic";
193
+ name: string;
194
+ secrets: string[];
195
+ limits?: ChannelLimits;
196
+ buffer?: ChannelBufferConfig;
197
+ audio?: never;
198
+ };
199
+
183
200
  export type AgentChannel =
184
201
  | WebsiteChannelConfig
185
202
  | TelegramChannelConfig
186
203
  | WhatsappChannelConfig
187
204
  | DiscordChannelConfig
188
- | SlackChannelConfig;
205
+ | SlackChannelConfig
206
+ | GenericWebhookChannelConfig;
189
207
 
190
208
  export type IntegrationTagsConfig = {
191
209
  enable?: string[];
@@ -495,6 +513,17 @@ export function slackChannel(input: SlackChannelInput): SlackChannelConfig {
495
513
  };
496
514
  }
497
515
 
516
+ export function webhookChannel(input: GenericWebhookChannelInput): GenericWebhookChannelConfig {
517
+ return {
518
+ type: "webhook",
519
+ provider: "generic",
520
+ name: input.name,
521
+ secrets: normalizeChannelSecrets(input.secrets ?? ["AGENTKIT_WEBHOOK_SECRET"], "generic webhook channel"),
522
+ ...(input.limits ? { limits: input.limits } : {}),
523
+ ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "generic webhook channel") } : {}),
524
+ };
525
+ }
526
+
498
527
  function defaultDiscordChannelSecrets(mode: DiscordChannelMode): string[] {
499
528
  return mode === "bot" ? ["DISCORD_BOT_TOKEN"] : ["DISCORD_PUBLIC_KEY"];
500
529
  }
@@ -504,6 +533,14 @@ function defaultWhatsappChannelSecrets(provider: WhatsappChannelProvider): strin
504
533
  return ["ZAPSTER_API_KEY", "ZAPSTER_INSTANCE_ID", "ZAPSTER_WEBHOOK_ID"];
505
534
  }
506
535
 
536
+ if (provider === "uazapi") {
537
+ return ["UAZAPI_BASE_URL", "UAZAPI_TOKEN"];
538
+ }
539
+
540
+ if (provider === "evolution") {
541
+ return ["EVOLUTION_API_BASE_URL", "EVOLUTION_API_KEY", "EVOLUTION_INSTANCE_NAME", "EVOLUTION_WEBHOOK_TOKEN"];
542
+ }
543
+
507
544
  return ["META_WHATSAPP_ACCESS_TOKEN", "META_WHATSAPP_APP_SECRET", "META_WHATSAPP_VERIFY_TOKEN"];
508
545
  }
509
546
 
@@ -15,11 +15,24 @@ export type ChannelFixtureName =
15
15
  | "zapster-audio-message"
16
16
  | "zapster-duplicate-message"
17
17
  | "zapster-unsupported-media"
18
+ | "uazapi-message"
19
+ | "uazapi-audio-message"
20
+ | "uazapi-duplicate-message"
21
+ | "uazapi-unsupported-media"
22
+ | "uazapi-was-sent-by-api"
23
+ | "evolution-message"
24
+ | "evolution-audio-message"
25
+ | "evolution-captioned-media"
26
+ | "evolution-duplicate-message"
27
+ | "evolution-from-me"
28
+ | "evolution-unsupported-media"
18
29
  | "discord-command"
19
30
  | "discord-message-create"
20
31
  | "discord-ping"
21
32
  | "slack-app-mention"
22
- | "slack-direct-message";
33
+ | "slack-direct-message"
34
+ | "generic-webhook-message"
35
+ | "generic-webhook-duplicate";
23
36
 
24
37
  export type ChannelFixture = {
25
38
  name: ChannelFixtureName;