@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
package/src/runtime/channels.ts
CHANGED
|
@@ -200,7 +200,7 @@ export type ChannelAdapter = ChannelAdapterId & {
|
|
|
200
200
|
};
|
|
201
201
|
|
|
202
202
|
export type PortableChannelRouteContract = {
|
|
203
|
-
route: "/channels/:name/:type/:provider/webhook";
|
|
203
|
+
route: "/channels/:name/:type/:provider/webhook" | "/channels/:name/webhook";
|
|
204
204
|
verification: WebhookVerificationResult;
|
|
205
205
|
dedupe: {
|
|
206
206
|
key: string;
|
package/src/runtime/config.ts
CHANGED
|
@@ -30,8 +30,8 @@ const AGENT_RUNTIMES = new Set<AgentRuntime>(["local", "edge"]);
|
|
|
30
30
|
const PROVIDERS = new Set<AgentProviderName>(["test", "openai", "anthropic", "openrouter", "custom"]);
|
|
31
31
|
const ACCESS_MODES = new Set<AccessMode>(["private", "token", "public"]);
|
|
32
32
|
const TOOL_VISIBILITIES = new Set<ToolVisibility>(["user", "internal"]);
|
|
33
|
-
const CHANNEL_TYPES = new Set<ChannelType>(["website", "telegram", "whatsapp", "discord", "slack"]);
|
|
34
|
-
const CHANNEL_PROVIDERS = new Set<ChannelProvider>(["agentkit", "telegram", "zapster", "meta", "discord", "slack"]);
|
|
33
|
+
const CHANNEL_TYPES = new Set<ChannelType>(["website", "telegram", "whatsapp", "discord", "slack", "webhook"]);
|
|
34
|
+
const CHANNEL_PROVIDERS = new Set<ChannelProvider>(["agentkit", "telegram", "zapster", "meta", "uazapi", "evolution", "discord", "slack", "generic"]);
|
|
35
35
|
const CHANNEL_BUFFER_MODES = new Set<ChannelBufferMode>(["off", "debounce"]);
|
|
36
36
|
const CHANNEL_AUDIO_MODES = new Set<ChannelAudioMode>(["off", "reject", "transcribe"]);
|
|
37
37
|
const DISCORD_CHANNEL_MODES = new Set<DiscordChannelMode>(["interactions", "bot"]);
|
|
@@ -627,6 +627,11 @@ function validateChannel(value: unknown, index: number, hasTranscriptionConfig:
|
|
|
627
627
|
return;
|
|
628
628
|
}
|
|
629
629
|
|
|
630
|
+
if (value.type === "webhook") {
|
|
631
|
+
validateGenericWebhookChannel(value, index);
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
|
|
630
635
|
validateChannelAudio(value.audio, index, hasTranscriptionConfig);
|
|
631
636
|
|
|
632
637
|
if (value.type === "website") {
|
|
@@ -699,7 +704,7 @@ function validateTelegramChannel(value: Record<string, unknown>, index: number):
|
|
|
699
704
|
|
|
700
705
|
function validateWhatsappChannel(value: Record<string, unknown>, index: number): void {
|
|
701
706
|
if (value.type !== "whatsapp") {
|
|
702
|
-
throw invalidConfig(`channels[${index}].type must be "website", "telegram", "whatsapp", "discord", or "
|
|
707
|
+
throw invalidConfig(`channels[${index}].type must be "website", "telegram", "whatsapp", "discord", "slack", or "webhook".`);
|
|
703
708
|
}
|
|
704
709
|
|
|
705
710
|
if (value.provider === "zapster") {
|
|
@@ -720,7 +725,25 @@ function validateWhatsappChannel(value: Record<string, unknown>, index: number):
|
|
|
720
725
|
return;
|
|
721
726
|
}
|
|
722
727
|
|
|
723
|
-
|
|
728
|
+
if (value.provider === "uazapi") {
|
|
729
|
+
expectRequiredSecrets(
|
|
730
|
+
value.secrets,
|
|
731
|
+
["UAZAPI_BASE_URL", "UAZAPI_TOKEN"],
|
|
732
|
+
`channels[${index}]`,
|
|
733
|
+
);
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if (value.provider === "evolution") {
|
|
738
|
+
expectRequiredSecrets(
|
|
739
|
+
value.secrets,
|
|
740
|
+
["EVOLUTION_API_BASE_URL", "EVOLUTION_API_KEY", "EVOLUTION_INSTANCE_NAME", "EVOLUTION_WEBHOOK_TOKEN"],
|
|
741
|
+
`channels[${index}]`,
|
|
742
|
+
);
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
throw invalidConfig(`channels[${index}].provider must be "zapster", "meta", "uazapi", or "evolution" when type is "whatsapp".`);
|
|
724
747
|
}
|
|
725
748
|
|
|
726
749
|
function validateDiscordChannel(value: Record<string, unknown>, index: number): void {
|
|
@@ -755,6 +778,18 @@ function validateSlackChannel(value: Record<string, unknown>, index: number): vo
|
|
|
755
778
|
expectRequiredSecrets(value.secrets, ["SLACK_BOT_TOKEN", "SLACK_SIGNING_SECRET"], `channels[${index}]`);
|
|
756
779
|
}
|
|
757
780
|
|
|
781
|
+
function validateGenericWebhookChannel(value: Record<string, unknown>, index: number): void {
|
|
782
|
+
if (value.provider !== "generic") {
|
|
783
|
+
throw invalidConfig(`channels[${index}].provider must be "generic" when type is "webhook".`);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (value.audio !== undefined) {
|
|
787
|
+
throw invalidConfig(`channels[${index}].audio is not supported when type is "webhook".`);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
expectRequiredSecrets(value.secrets, ["AGENTKIT_WEBHOOK_SECRET"], `channels[${index}]`);
|
|
791
|
+
}
|
|
792
|
+
|
|
758
793
|
function validateChannelLimits(value: unknown, index: number): void {
|
|
759
794
|
if (value === undefined) {
|
|
760
795
|
return;
|
|
@@ -6,7 +6,7 @@ export type RuntimeTargetId = (typeof runtimeTargetIds)[number];
|
|
|
6
6
|
export type RuntimeTargetCapabilityState = "available" | "planned" | "unsupported";
|
|
7
7
|
export type RuntimeStorageCapability = "agentkit" | "durable-object-sqlite" | "local-sqlite" | "postgres";
|
|
8
8
|
export type RuntimeFilesCapability = "none" | "r2" | "local-disk" | "s3-compatible";
|
|
9
|
-
export type RuntimeChannelCapability = "website" | "telegram" | "whatsapp" | "discord" | "slack";
|
|
9
|
+
export type RuntimeChannelCapability = "website" | "telegram" | "whatsapp" | "discord" | "slack" | "webhook";
|
|
10
10
|
export type RuntimeProviderSupport = "pi-local" | "edge-compatible" | "server-env";
|
|
11
11
|
|
|
12
12
|
export type RuntimeTargetCapabilities = {
|
|
@@ -35,7 +35,7 @@ export const runtimeTargets: Readonly<Record<RuntimeTargetId, RuntimeTarget>> =
|
|
|
35
35
|
dev: "available",
|
|
36
36
|
storage: ["local-sqlite"],
|
|
37
37
|
files: ["local-disk"],
|
|
38
|
-
channels: ["website", "slack"],
|
|
38
|
+
channels: ["website", "slack", "webhook"],
|
|
39
39
|
providers: ["pi-local"],
|
|
40
40
|
},
|
|
41
41
|
},
|
|
@@ -48,7 +48,7 @@ export const runtimeTargets: Readonly<Record<RuntimeTargetId, RuntimeTarget>> =
|
|
|
48
48
|
dev: "planned",
|
|
49
49
|
storage: ["agentkit", "durable-object-sqlite", "postgres"],
|
|
50
50
|
files: ["none", "r2"],
|
|
51
|
-
channels: ["website", "telegram", "whatsapp", "discord", "slack"],
|
|
51
|
+
channels: ["website", "telegram", "whatsapp", "discord", "slack", "webhook"],
|
|
52
52
|
providers: ["edge-compatible"],
|
|
53
53
|
},
|
|
54
54
|
},
|
|
@@ -61,7 +61,7 @@ export const runtimeTargets: Readonly<Record<RuntimeTargetId, RuntimeTarget>> =
|
|
|
61
61
|
dev: "planned",
|
|
62
62
|
storage: ["local-sqlite", "postgres"],
|
|
63
63
|
files: ["none", "local-disk", "s3-compatible"],
|
|
64
|
-
channels: ["website", "telegram", "whatsapp", "discord", "slack"],
|
|
64
|
+
channels: ["website", "telegram", "whatsapp", "discord", "slack", "webhook"],
|
|
65
65
|
providers: ["server-env"],
|
|
66
66
|
},
|
|
67
67
|
},
|
|
@@ -74,7 +74,7 @@ export const runtimeTargets: Readonly<Record<RuntimeTargetId, RuntimeTarget>> =
|
|
|
74
74
|
dev: "unsupported",
|
|
75
75
|
storage: ["local-sqlite", "postgres"],
|
|
76
76
|
files: ["none", "local-disk", "s3-compatible"],
|
|
77
|
-
channels: ["website", "telegram", "whatsapp", "discord", "slack"],
|
|
77
|
+
channels: ["website", "telegram", "whatsapp", "discord", "slack", "webhook"],
|
|
78
78
|
providers: ["server-env"],
|
|
79
79
|
},
|
|
80
80
|
},
|
|
@@ -9,10 +9,13 @@ import { openCapsuleStore, type ConversationRecord } from "../storage/sqlite";
|
|
|
9
9
|
import type { AgentChannel, ChannelProvider, ChannelType } from "../index";
|
|
10
10
|
import type { ChannelAdapter, NormalizedChannelMessage, RawWebhookEvent } from "./channels";
|
|
11
11
|
import { discordChannelAdapter } from "./channels/discord";
|
|
12
|
+
import { genericWebhookChannelAdapter } from "./channels/generic-webhook";
|
|
12
13
|
import { slackChannelAdapter } from "./channels/slack";
|
|
13
14
|
import { telegramChannelAdapter } from "./channels/telegram";
|
|
14
15
|
import { websiteChannelAdapter } from "./channels/website";
|
|
16
|
+
import { evolutionWhatsappChannelAdapter } from "./channels/whatsapp-evolution";
|
|
15
17
|
import { metaWhatsappChannelAdapter } from "./channels/whatsapp-meta";
|
|
18
|
+
import { uazapiWhatsappChannelAdapter } from "./channels/whatsapp-uazapi";
|
|
16
19
|
import { zapsterWhatsappChannelAdapter } from "./channels/whatsapp-zapster";
|
|
17
20
|
import { runAgentMessage } from "./chat";
|
|
18
21
|
import { isChannelBufferEnabled, renderBufferedChannelMessage, resolveChannelBuffer } from "./channel-buffer";
|
|
@@ -320,13 +323,14 @@ async function handleDevServerRequest(
|
|
|
320
323
|
return await handleLocalFile(capsule, request, decodeURIComponent(fileMatch[1]));
|
|
321
324
|
}
|
|
322
325
|
|
|
323
|
-
const channelMatch = route.match(/^\/channels\/([^/]+)\/(website|telegram|whatsapp|discord|slack)\/([^/]+)\/webhook$/);
|
|
326
|
+
const channelMatch = route.match(/^\/channels\/([^/]+)\/(website|telegram|whatsapp|discord|slack|webhook)\/([^/]+)\/webhook$/);
|
|
327
|
+
const directWebhookMatch = route.match(/^\/channels\/([^/]+)\/webhook$/);
|
|
324
328
|
|
|
325
|
-
if (channelMatch) {
|
|
329
|
+
if (channelMatch || directWebhookMatch) {
|
|
326
330
|
return await handlePortableChannel(capsule, request, channelRuntime, {
|
|
327
|
-
name: decodeURIComponent(channelMatch[1]),
|
|
328
|
-
type: channelMatch[2] as ChannelType,
|
|
329
|
-
provider: decodeURIComponent(channelMatch[3]) as ChannelProvider,
|
|
331
|
+
name: decodeURIComponent((channelMatch ?? directWebhookMatch)?.[1] ?? ""),
|
|
332
|
+
type: channelMatch ? channelMatch[2] as ChannelType : "webhook",
|
|
333
|
+
provider: channelMatch ? decodeURIComponent(channelMatch[3]) as ChannelProvider : "generic",
|
|
330
334
|
});
|
|
331
335
|
}
|
|
332
336
|
|
|
@@ -1137,6 +1141,14 @@ function adapterForChannel(type: ChannelType, provider: ChannelProvider): Channe
|
|
|
1137
1141
|
return metaWhatsappChannelAdapter;
|
|
1138
1142
|
}
|
|
1139
1143
|
|
|
1144
|
+
if (type === "whatsapp" && provider === "uazapi") {
|
|
1145
|
+
return uazapiWhatsappChannelAdapter;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
if (type === "whatsapp" && provider === "evolution") {
|
|
1149
|
+
return evolutionWhatsappChannelAdapter;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1140
1152
|
if (type === "discord" && provider === "discord") {
|
|
1141
1153
|
return discordChannelAdapter;
|
|
1142
1154
|
}
|
|
@@ -1145,6 +1157,10 @@ function adapterForChannel(type: ChannelType, provider: ChannelProvider): Channe
|
|
|
1145
1157
|
return slackChannelAdapter;
|
|
1146
1158
|
}
|
|
1147
1159
|
|
|
1160
|
+
if (type === "webhook" && provider === "generic") {
|
|
1161
|
+
return genericWebhookChannelAdapter;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1148
1164
|
throw new AgentKitError("channel_not_found", `No channel adapter for ${type}/${provider}.`);
|
|
1149
1165
|
}
|
|
1150
1166
|
|
|
@@ -462,11 +462,30 @@ export function slackChannel(input) {
|
|
|
462
462
|
};
|
|
463
463
|
}
|
|
464
464
|
|
|
465
|
+
export function webhookChannel(input) {
|
|
466
|
+
return {
|
|
467
|
+
type: "webhook",
|
|
468
|
+
provider: "generic",
|
|
469
|
+
name: input.name,
|
|
470
|
+
secrets: normalizeChannelSecrets(input.secrets ?? ["AGENTKIT_WEBHOOK_SECRET"], "generic webhook channel"),
|
|
471
|
+
...(input.limits ? { limits: input.limits } : {}),
|
|
472
|
+
...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "generic webhook channel") } : {}),
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
465
476
|
function defaultWhatsappChannelSecrets(provider) {
|
|
466
477
|
if (provider === "zapster") {
|
|
467
478
|
return ["ZAPSTER_API_KEY", "ZAPSTER_INSTANCE_ID", "ZAPSTER_WEBHOOK_ID"];
|
|
468
479
|
}
|
|
469
480
|
|
|
481
|
+
if (provider === "uazapi") {
|
|
482
|
+
return ["UAZAPI_BASE_URL", "UAZAPI_TOKEN"];
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (provider === "evolution") {
|
|
486
|
+
return ["EVOLUTION_API_BASE_URL", "EVOLUTION_API_KEY", "EVOLUTION_INSTANCE_NAME", "EVOLUTION_WEBHOOK_TOKEN"];
|
|
487
|
+
}
|
|
488
|
+
|
|
470
489
|
return ["META_WHATSAPP_ACCESS_TOKEN", "META_WHATSAPP_APP_SECRET", "META_WHATSAPP_VERIFY_TOKEN"];
|
|
471
490
|
}
|
|
472
491
|
|
|
@@ -1128,9 +1147,10 @@ export default {
|
|
|
1128
1147
|
|
|
1129
1148
|
function isHostedChannelWebhookPath(pathname) {
|
|
1130
1149
|
return (
|
|
1131
|
-
/^\\/channels\\/[^/]+\\/(?:website\\/agentkit|telegram\\/telegram|whatsapp\\/(?:zapster|meta)|discord\\/discord|slack\\/slack)\\/webhook$/.test(pathname) ||
|
|
1132
|
-
/^\\/channels\\/[^/]+\\/(?:website|telegram|discord|slack)\\/webhook$/.test(pathname) ||
|
|
1133
|
-
/^\\/channels\\/[^/]+\\/
|
|
1150
|
+
/^\\/channels\\/[^/]+\\/(?:website\\/agentkit|telegram\\/telegram|whatsapp\\/(?:zapster|meta|uazapi|evolution)|discord\\/discord|slack\\/slack|webhook\\/generic)\\/webhook$/.test(pathname) ||
|
|
1151
|
+
/^\\/channels\\/[^/]+\\/(?:website|telegram|discord|slack|webhook)\\/webhook$/.test(pathname) ||
|
|
1152
|
+
/^\\/channels\\/[^/]+\\/webhook$/.test(pathname) ||
|
|
1153
|
+
/^\\/channels\\/[^/]+\\/whatsapp\\/(?:zapster|meta|uazapi|evolution)\\/webhook$/.test(pathname)
|
|
1134
1154
|
);
|
|
1135
1155
|
}
|
|
1136
1156
|
|
|
@@ -27,7 +27,7 @@ Use this first inside an AgentKit Agent Capsule.
|
|
|
27
27
|
- Improve from hosted or local production evidence: `skills/agentkit-improve/SKILL.md`
|
|
28
28
|
- Prepare hosted deploy: `skills/agentkit-deploy/SKILL.md`
|
|
29
29
|
- Work with secrets, external APIs, public access, channels, or real data: `skills/agentkit-security/SKILL.md`
|
|
30
|
-
- Add or debug website, Telegram, WhatsApp, Discord, or
|
|
30
|
+
- Add or debug website, Telegram, WhatsApp, Discord, Slack, or generic webhook channels: `skills/agentkit-channels/SKILL.md`
|
|
31
31
|
- Investigate command failures: `skills/agentkit-troubleshooting/SKILL.md`
|
|
32
32
|
|
|
33
33
|
For a compact docs map, read `references/docs-router.md`.
|
|
@@ -9,7 +9,7 @@ Prefer the narrowest source that covers the task.
|
|
|
9
9
|
- Safe AgentKit product feedback drafts/submission: `docs/guides/send-feedback.md`
|
|
10
10
|
- Real provider setup: `docs/guides/use-provider.md`
|
|
11
11
|
- Deploy readiness, managed secrets, smoke checks, hosted UI: `docs/guides/prepare-deploy.md`
|
|
12
|
-
- Channels: `docs/guides/add-channel.md`, `connect-discord.md`, `connect-slack.md`, `connect-telegram.md`, `connect-whatsapp-zapster.md`, `debug-channel.md`
|
|
12
|
+
- Channels: `docs/guides/add-channel.md`, `connect-discord.md`, `connect-slack.md`, `connect-telegram.md`, `connect-whatsapp-evolution.md`, `connect-whatsapp-uazapi.md`, `connect-whatsapp-zapster.md`, `debug-channel.md`
|
|
13
13
|
- Security: `docs/guides/security-rules.md`
|
|
14
14
|
|
|
15
15
|
Use `npm run agentkit -- docs full` only for a complete-contract audit, framework internals, or a behavior not covered by the task guide.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agentkit-channels
|
|
3
|
-
description: Use when adding, connecting, testing, buffering, transcribing audio, or debugging AgentKit website, Telegram, WhatsApp, Discord, or
|
|
3
|
+
description: Use when adding, connecting, testing, buffering, transcribing audio, or debugging AgentKit website, Telegram, WhatsApp, Discord, Slack, or generic webhook channels, including channel config helpers, provider secrets, webhook setup, channel tests, delivery logs, burst-message buffers, and transcription provider secrets.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# AgentKit Channels
|
|
@@ -47,7 +47,7 @@ V1 providers:
|
|
|
47
47
|
- `openai`: `gpt-4o-mini-transcribe`, `gpt-4o-transcribe`, `whisper-1`; default secret `OPENAI_API_KEY`.
|
|
48
48
|
- `groq`: `whisper-large-v3-turbo`, `whisper-large-v3`, `distil-whisper-large-v3-en`; default secret `GROQ_API_KEY`.
|
|
49
49
|
|
|
50
|
-
Telegram voice notes are usually OGG/Opus, so use Groq for the default Telegram voice-note path in V1. Zapster audio needs a usable HTTPS Zapster media download URL in the webhook payload; arbitrary hosts are rejected before bearer auth is sent. Hosted channel creation requires the transcription secret automatically when the channel enables transcription. Webhooks only enqueue audio jobs; download and transcription run in the retryable channel worker before the agent run.
|
|
50
|
+
Telegram voice notes are usually OGG/Opus, so use Groq for the default Telegram voice-note path in V1. Zapster audio needs a usable HTTPS Zapster media download URL in the webhook payload; arbitrary hosts are rejected before bearer auth is sent. UAZAPI audio downloads go through UAZAPI `/message/download` with base64 return enabled. Evolution audio downloads go through Evolution API `/chat/getBase64FromMediaMessage/{instance}` with base64 return enabled. Arbitrary UAZAPI and Evolution webhook media hosts are not fetched. Hosted channel creation requires the transcription secret automatically when the channel enables transcription. Webhooks only enqueue audio jobs; download and transcription run in the retryable channel worker before the agent run.
|
|
51
51
|
|
|
52
52
|
Discord channels do not support audio in V1.
|
|
53
53
|
|
|
@@ -80,9 +80,12 @@ npm run agentkit -- channels list
|
|
|
80
80
|
npm run agentkit -- channels add website website-chat
|
|
81
81
|
npm run agentkit -- channels connect telegram support-telegram
|
|
82
82
|
npm run agentkit -- channels add whatsapp support-whatsapp --provider zapster
|
|
83
|
+
npm run agentkit -- channels connect whatsapp support-whatsapp --provider uazapi
|
|
84
|
+
npm run agentkit -- channels connect whatsapp main-whatsapp --provider evolution
|
|
83
85
|
npm run agentkit -- channels connect discord support-discord
|
|
84
86
|
npm run agentkit -- channels connect discord server-discord --mode bot
|
|
85
87
|
npm run agentkit -- channels connect slack support-slack
|
|
88
|
+
npm run agentkit -- channels connect webhook n8n-webhook
|
|
86
89
|
npm run agentkit -- channels doctor support-telegram
|
|
87
90
|
npm run agentkit -- channels test support-telegram --message "hello"
|
|
88
91
|
npm run agentkit -- channels test-audio support-telegram --fixture voice-note
|
|
@@ -95,6 +98,8 @@ npm run agentkit -- channels deliveries list support-telegram
|
|
|
95
98
|
- `references/discord.md`
|
|
96
99
|
- `references/slack.md`
|
|
97
100
|
- `references/telegram.md`
|
|
101
|
+
- `references/whatsapp-evolution.md`
|
|
102
|
+
- `references/whatsapp-uazapi.md`
|
|
98
103
|
- `references/whatsapp-zapster.md`
|
|
99
104
|
- `references/channel-buffering.md`
|
|
100
105
|
- `references/channel-debugging.md`
|
|
@@ -102,3 +107,21 @@ npm run agentkit -- channels deliveries list support-telegram
|
|
|
102
107
|
## Safety
|
|
103
108
|
|
|
104
109
|
Do not paste provider tokens into code, docs, fixtures, prompts, evals, or delivery logs. Webhook URLs are public transport endpoints; authenticity comes from provider validation or channel tokens.
|
|
110
|
+
|
|
111
|
+
## Generic Webhooks
|
|
112
|
+
|
|
113
|
+
Use `webhookChannel({ name: "n8n-webhook" })` for n8n, Make, Zapier, Pipedream, or custom server events.
|
|
114
|
+
|
|
115
|
+
Canonical JSON:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"event_id": "evt_123",
|
|
120
|
+
"external_id": "customer_123",
|
|
121
|
+
"message": "hello from n8n"
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`event_id` is required for dedupe. `external_id` is recommended for conversation continuity and is hashed before storage. Authenticate with `Authorization: Bearer <AGENTKIT_WEBHOOK_SECRET>`, `X-AgentKit-Webhook-Secret`, or `X-AgentKit-Webhook-Signature: sha256=<hmac of raw JSON body>`.
|
|
126
|
+
|
|
127
|
+
Generic webhooks are inbound-only in V1. Add a separate tool if the agent must call back into n8n after it runs.
|
|
@@ -62,5 +62,5 @@ Common errors:
|
|
|
62
62
|
- `channel_limit_exceeded`: backpressure skipped the message.
|
|
63
63
|
- `synthetic_expected_failure`: a synthetic test reached AgentKit, but the provider correctly rejected a fake test recipient.
|
|
64
64
|
- `buffered` delivery state: message is waiting for the channel quiet window or max wait before one coalesced agent run is queued.
|
|
65
|
-
- `adapter_stubbed`: explicit dry-run mode
|
|
65
|
+
- `adapter_stubbed`: adapter completed without calling an outbound provider, either because explicit dry-run mode is enabled or the channel is inbound-only.
|
|
66
66
|
- `provider_sent`: the provider accepted the outbound send and returned a provider message ID.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# WhatsApp Through Evolution API
|
|
2
|
+
|
|
3
|
+
Required secrets:
|
|
4
|
+
|
|
5
|
+
```txt
|
|
6
|
+
EVOLUTION_API_BASE_URL
|
|
7
|
+
EVOLUTION_API_KEY
|
|
8
|
+
EVOLUTION_INSTANCE_NAME
|
|
9
|
+
EVOLUTION_WEBHOOK_TOKEN
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Audio transcription also needs the configured transcription secret, usually `OPENAI_API_KEY` or `GROQ_API_KEY`.
|
|
13
|
+
|
|
14
|
+
Commands:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
agentkit deploy
|
|
18
|
+
agentkit channels add whatsapp main-whatsapp --provider evolution
|
|
19
|
+
agentkit channels setup main-whatsapp --apply
|
|
20
|
+
agentkit channels status main-whatsapp
|
|
21
|
+
agentkit channels test main-whatsapp --message "hello"
|
|
22
|
+
agentkit channels deliveries list main-whatsapp
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
AgentKit applies Evolution setup by calling `/webhook/set/{instance}` with the stable hosted URL and then confirming the configured webhook through `/webhook/find/{instance}`. AgentKit appends `?token=<EVOLUTION_WEBHOOK_TOKEN>` to the registered webhook URL.
|
|
26
|
+
|
|
27
|
+
Inbound Evolution `MESSAGES_UPSERT` webhooks normalize text and audio messages. `fromMe` messages are skipped to avoid reply loops. Unsupported media should be logged as skipped/unsupported without creating an agent run.
|
|
28
|
+
|
|
29
|
+
Outbound replies call Evolution API `POST /message/sendText/{instance}` with the `apikey` header and a JSON body containing `number` and `text`. Only set `AGENTKIT_CHANNEL_SEND_DRY_RUN=1` in tests when Evolution should not receive a real message.
|
|
30
|
+
|
|
31
|
+
Buffer rapid WhatsApp messages:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
whatsappChannel({
|
|
35
|
+
name: "main-whatsapp",
|
|
36
|
+
provider: "evolution",
|
|
37
|
+
buffer: {
|
|
38
|
+
mode: "debounce",
|
|
39
|
+
quietWindowMs: 2500,
|
|
40
|
+
maxWaitMs: 12000,
|
|
41
|
+
maxMessages: 20,
|
|
42
|
+
maxChars: 8000,
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Transcribe WhatsApp audio:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
whatsappChannel({
|
|
51
|
+
name: "main-whatsapp",
|
|
52
|
+
provider: "evolution",
|
|
53
|
+
audio: { mode: "transcribe" },
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Evolution audio downloads use `/chat/getBase64FromMediaMessage/{instance}` and AgentKit rejects unsafe Evolution base URLs before sending `EVOLUTION_API_KEY`; do not use localhost, private-network hosts, or HTTP base URLs.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# WhatsApp Through UAZAPI
|
|
2
|
+
|
|
3
|
+
Required secrets:
|
|
4
|
+
|
|
5
|
+
```txt
|
|
6
|
+
UAZAPI_BASE_URL
|
|
7
|
+
UAZAPI_TOKEN
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Optional hardening secret:
|
|
11
|
+
|
|
12
|
+
```txt
|
|
13
|
+
UAZAPI_WEBHOOK_TOKEN
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Audio transcription also needs the configured transcription secret, usually `OPENAI_API_KEY` or `GROQ_API_KEY`.
|
|
17
|
+
|
|
18
|
+
Commands:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
agentkit deploy
|
|
22
|
+
agentkit channels add whatsapp support-whatsapp --provider uazapi
|
|
23
|
+
agentkit channels setup support-whatsapp --apply
|
|
24
|
+
agentkit channels status support-whatsapp
|
|
25
|
+
agentkit channels test support-whatsapp --message "hello"
|
|
26
|
+
agentkit channels deliveries list support-whatsapp
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
AgentKit applies UAZAPI setup by calling `/webhook` with the stable hosted URL and then confirming that UAZAPI lists the configured webhook. If the channel declares `UAZAPI_WEBHOOK_TOKEN`, AgentKit appends `?token=<UAZAPI_WEBHOOK_TOKEN>` to the registered webhook URL.
|
|
30
|
+
|
|
31
|
+
Inbound UAZAPI `messages` webhooks normalize text and audio messages. `fromMe` and `wasSentByApi` messages are skipped to avoid reply loops. Unsupported media should be logged as skipped/unsupported without creating an agent run.
|
|
32
|
+
|
|
33
|
+
Outbound replies call UAZAPI `POST /send/text` with the `token` header and a JSON body containing `number`, `text`, `readchat`, `async`, `track_source`, and `track_id`. Only set `AGENTKIT_CHANNEL_SEND_DRY_RUN=1` in tests when UAZAPI should not receive a real message.
|
|
34
|
+
|
|
35
|
+
Buffer rapid WhatsApp messages:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
whatsappChannel({
|
|
39
|
+
name: "support-whatsapp",
|
|
40
|
+
provider: "uazapi",
|
|
41
|
+
buffer: {
|
|
42
|
+
mode: "debounce",
|
|
43
|
+
quietWindowMs: 2500,
|
|
44
|
+
maxWaitMs: 12000,
|
|
45
|
+
maxMessages: 20,
|
|
46
|
+
maxChars: 8000,
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Transcribe WhatsApp audio:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
whatsappChannel({
|
|
55
|
+
name: "support-whatsapp",
|
|
56
|
+
provider: "uazapi",
|
|
57
|
+
audio: { mode: "transcribe" },
|
|
58
|
+
})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
UAZAPI audio downloads use `/message/download` with `return_base64: true`, `return_link: false`, `generate_mp3: false`, and `transcribe: false`. AgentKit rejects unsafe UAZAPI base URLs before sending `UAZAPI_TOKEN`; do not use localhost, private-network hosts, or HTTP base URLs.
|