@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.
- package/README.md +1 -0
- package/docs/guides/add-channel.md +75 -4
- package/docs/guides/channel-security.md +32 -1
- package/docs/guides/connect-whatsapp-evolution.md +121 -0
- package/docs/guides/connect-whatsapp-uazapi.md +126 -0
- package/docs/guides/debug-channel.md +3 -3
- package/docs/llms-full.txt +49 -4
- package/docs/llms.txt +5 -0
- package/package.json +1 -1
- package/src/cli/commands/channels.ts +285 -14
- package/src/cli/help.ts +2 -2
- package/src/index.ts +41 -4
- package/src/runtime/channel-test-harness.ts +14 -1
- package/src/runtime/channels/generic-webhook.ts +225 -0
- package/src/runtime/channels/whatsapp-evolution.ts +1357 -0
- package/src/runtime/channels/whatsapp-uazapi.ts +1327 -0
- package/src/runtime/channels.ts +1 -1
- package/src/runtime/config.ts +39 -4
- package/src/runtime/core/targets.ts +5 -5
- package/src/runtime/dev-server.ts +21 -5
- package/src/runtime/targets/cloudflare/build.ts +23 -3
- package/src/templates/skills/agentkit-capsule/SKILL.md +1 -1
- package/src/templates/skills/agentkit-capsule/references/docs-router.md +1 -1
- package/src/templates/skills/agentkit-channels/SKILL.md +25 -2
- package/src/templates/skills/agentkit-channels/references/channel-debugging.md +1 -1
- package/src/templates/skills/agentkit-channels/references/whatsapp-evolution.md +57 -0
- package/src/templates/skills/agentkit-channels/references/whatsapp-uazapi.md +61 -0
|
@@ -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
|
|
302
|
-
|
|
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
|
|
464
|
-
const { response, payload } =
|
|
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 "
|
|
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;
|