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

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.
Files changed (49) hide show
  1. package/README.md +1 -0
  2. package/docs/guides/add-channel.md +75 -4
  3. package/docs/guides/add-managed-composio.md +3 -1
  4. package/docs/guides/add-tool.md +6 -3
  5. package/docs/guides/channel-security.md +32 -1
  6. package/docs/guides/connect-whatsapp-evolution.md +121 -0
  7. package/docs/guides/connect-whatsapp-uazapi.md +126 -0
  8. package/docs/guides/create-agent.md +6 -4
  9. package/docs/guides/debug-channel.md +3 -3
  10. package/docs/guides/prepare-deploy.md +1 -1
  11. package/docs/guides/run-evals.md +5 -1
  12. package/docs/guides/use-provider.md +26 -2
  13. package/docs/llms-full.txt +82 -9
  14. package/docs/llms.txt +9 -2
  15. package/package.json +1 -1
  16. package/src/cli/commands/channels.ts +285 -14
  17. package/src/cli/deploy-chat-ui.ts +86 -15
  18. package/src/cli/help.ts +2 -2
  19. package/src/cli/index.ts +107 -8
  20. package/src/index.ts +42 -5
  21. package/src/providers/pi.ts +12 -2
  22. package/src/runtime/channel-test-harness.ts +14 -1
  23. package/src/runtime/channels/generic-webhook.ts +225 -0
  24. package/src/runtime/channels/whatsapp-evolution.ts +1357 -0
  25. package/src/runtime/channels/whatsapp-uazapi.ts +1327 -0
  26. package/src/runtime/channels.ts +1 -1
  27. package/src/runtime/chat.ts +7 -1
  28. package/src/runtime/config.ts +40 -5
  29. package/src/runtime/core/targets.ts +5 -5
  30. package/src/runtime/dev-server.ts +21 -5
  31. package/src/runtime/evals.ts +39 -24
  32. package/src/runtime/targets/cloudflare/build.ts +23 -3
  33. package/src/templates/blank.ts +29 -6
  34. package/src/templates/dentista.ts +22 -4
  35. package/src/templates/skills/agentkit-build-agent/SKILL.md +30 -2
  36. package/src/templates/skills/agentkit-capsule/SKILL.md +22 -1
  37. package/src/templates/skills/agentkit-capsule/references/docs-router.md +1 -1
  38. package/src/templates/skills/agentkit-channels/SKILL.md +25 -2
  39. package/src/templates/skills/agentkit-channels/references/channel-debugging.md +1 -1
  40. package/src/templates/skills/agentkit-channels/references/whatsapp-evolution.md +57 -0
  41. package/src/templates/skills/agentkit-channels/references/whatsapp-uazapi.md +61 -0
  42. package/src/templates/skills/agentkit-database/SKILL.md +11 -0
  43. package/src/templates/skills/agentkit-deploy/SKILL.md +2 -0
  44. package/src/templates/skills/agentkit-evals/SKILL.md +15 -0
  45. package/src/templates/skills/agentkit-improve/SKILL.md +14 -4
  46. package/src/templates/skills/agentkit-integrations/SKILL.md +22 -0
  47. package/src/templates/skills/agentkit-provider/SKILL.md +20 -2
  48. package/src/templates/skills/agentkit-tools/SKILL.md +2 -0
  49. package/src/templates/support.ts +30 -7
package/README.md CHANGED
@@ -67,6 +67,7 @@ agentkit secret set OPENAI_API_KEY --from-local-env
67
67
  agentkit deploy
68
68
  agentkit deploy status
69
69
  agentkit channels test support-telegram --message "hello"
70
+ agentkit channels connect webhook n8n-webhook
70
71
  agentkit transcribe smoke --provider groq
71
72
  agentkit chat-ui --deploy
72
73
  ```
@@ -6,7 +6,7 @@ Add a hosted messaging channel to an Agent Capsule, create the hosted channel re
6
6
 
7
7
  ## When To Use It
8
8
 
9
- Use this when the agent should receive messages from website chat, Telegram, WhatsApp, or Discord through AgentKit-owned channel infrastructure.
9
+ Use this when the agent should receive messages from website chat, Telegram, WhatsApp, Discord, Slack, or a generic webhook through AgentKit-owned channel infrastructure.
10
10
 
11
11
  ## Commands
12
12
 
@@ -19,7 +19,10 @@ agentkit channels add telegram support-telegram
19
19
  agentkit channels connect discord support-discord
20
20
  agentkit channels connect discord server-discord --mode bot
21
21
  agentkit channels connect slack support-slack
22
+ agentkit channels connect webhook n8n-webhook
22
23
  agentkit channels add whatsapp support-whatsapp --provider zapster
24
+ agentkit channels connect whatsapp support-uazapi --provider uazapi
25
+ agentkit channels connect whatsapp main-whatsapp --provider evolution
23
26
  agentkit channels setup support-telegram
24
27
  agentkit channels status support-telegram
25
28
  agentkit channels test support-telegram --message "hello"
@@ -31,15 +34,16 @@ Use `--api <url>` only when the owner gives you a non-default AgentKit Cloud API
31
34
 
32
35
  ## Files Created Or Edited
33
36
 
34
- - `agentkit.config.ts`: declare channel config with `websiteChannel`, `telegramChannel`, `whatsappChannel`, `discordChannel`, or `slackChannel`.
37
+ - `agentkit.config.ts`: declare channel config with `websiteChannel`, `telegramChannel`, `whatsappChannel`, `discordChannel`, `slackChannel`, or `webhookChannel`.
35
38
  - `.agentkit/deploy.json`: written by `agentkit deploy`; used by `agentkit channels ...`.
36
39
  - No user Turso tables: AgentKit owns channel resources, dedupe, identities, queue state, and delivery logs.
37
40
  - Website channel clients must send `AGENTKIT_WEBSITE_CHANNEL_TOKEN` as `Authorization: Bearer <token>` or `X-AgentKit-Channel-Token`.
41
+ - Generic webhook clients must send `AGENTKIT_WEBHOOK_SECRET` as `Authorization: Bearer <token>`, `X-AgentKit-Webhook-Secret`, or `X-AgentKit-Webhook-Signature: sha256=<hmac>`.
38
42
 
39
43
  ## Minimal Working Example
40
44
 
41
45
  ```ts
42
- import { defineAgent, discordChannel, slackChannel, telegramChannel, whatsappChannel, websiteChannel } from "@andreprado/agentkit";
46
+ import { defineAgent, discordChannel, slackChannel, telegramChannel, webhookChannel, whatsappChannel, websiteChannel } from "@andreprado/agentkit";
43
47
 
44
48
  export default defineAgent({
45
49
  name: "support-agent",
@@ -52,9 +56,12 @@ export default defineAgent({
52
56
  websiteChannel({ name: "website-chat" }),
53
57
  telegramChannel({ name: "support-telegram" }),
54
58
  whatsappChannel({ name: "support-whatsapp", provider: "zapster" }),
59
+ whatsappChannel({ name: "support-uazapi", provider: "uazapi" }),
60
+ whatsappChannel({ name: "main-whatsapp", provider: "evolution" }),
55
61
  discordChannel({ name: "support-discord" }),
56
62
  discordChannel({ name: "server-discord", mode: "bot" }),
57
63
  slackChannel({ name: "support-slack" }),
64
+ webhookChannel({ name: "n8n-webhook" }),
58
65
  ],
59
66
  access: { mode: "public" },
60
67
  storage: { driver: "agentkit" },
@@ -95,6 +102,57 @@ agentkit channels buffers retry support-whatsapp <conversation-id>
95
102
 
96
103
  Discord supports buffering for slash-command interactions and bot-mode server messages, but it does not support `audio` in V1.
97
104
 
105
+ ## Generic Webhooks
106
+
107
+ Use `webhookChannel` when n8n, Make, Zapier, Pipedream, or a custom server should push a text event into the agent.
108
+
109
+ ```ts
110
+ webhookChannel({
111
+ name: "n8n-webhook",
112
+ })
113
+ ```
114
+
115
+ After deploy:
116
+
117
+ ```sh
118
+ agentkit channels connect webhook n8n-webhook
119
+ ```
120
+
121
+ The hosted webhook URL is:
122
+
123
+ ```txt
124
+ https://<deploy-host>/channels/n8n-webhook/webhook
125
+ ```
126
+
127
+ Send canonical JSON:
128
+
129
+ ```json
130
+ {
131
+ "event_id": "evt_123",
132
+ "external_id": "customer_123",
133
+ "message": "hello from n8n"
134
+ }
135
+ ```
136
+
137
+ `event_id` is required for replay protection. `external_id` is optional but recommended; AgentKit hashes it before storing the channel identity and uses it to continue the same conversation. If `external_id` is omitted, the event id becomes the conversation identity and each event is treated as its own conversation.
138
+
139
+ Authenticate with a shared secret:
140
+
141
+ ```sh
142
+ curl -X POST https://<deploy-host>/channels/n8n-webhook/webhook \
143
+ -H 'content-type: application/json' \
144
+ -H 'authorization: Bearer <AGENTKIT_WEBHOOK_SECRET>' \
145
+ -d '{"event_id":"evt_123","external_id":"customer_123","message":"hello from n8n"}'
146
+ ```
147
+
148
+ For HMAC auth, compute `HMAC-SHA256(raw JSON body, AGENTKIT_WEBHOOK_SECRET)` and send:
149
+
150
+ ```txt
151
+ X-AgentKit-Webhook-Signature: sha256=<hex digest>
152
+ ```
153
+
154
+ Generic webhooks are inbound-only in V1. The agent run is queued and delivery logs show the result; AgentKit does not call back into n8n unless the agent has a separate tool that does so.
155
+
98
156
  ## Auto Transcribe Audio
99
157
 
100
158
  Use `transcription` at the agent level and `audio.mode: "transcribe"` on each Telegram or WhatsApp channel that should accept voice notes or audio files.
@@ -125,6 +183,16 @@ export default defineAgent({
125
183
  provider: "zapster",
126
184
  audio: { mode: "transcribe" },
127
185
  }),
186
+ whatsappChannel({
187
+ name: "support-uazapi",
188
+ provider: "uazapi",
189
+ audio: { mode: "transcribe" },
190
+ }),
191
+ whatsappChannel({
192
+ name: "main-whatsapp",
193
+ provider: "evolution",
194
+ audio: { mode: "transcribe" },
195
+ }),
128
196
  ],
129
197
  access: { mode: "public" },
130
198
  storage: { driver: "agentkit" },
@@ -149,6 +217,8 @@ Processing order:
149
217
  5. The transcription adapter sends the file to the configured transcription provider.
150
218
  6. The agent receives a text message containing the transcript.
151
219
 
220
+ Zapster audio downloads from trusted HTTPS Zapster media URLs. UAZAPI audio downloads through UAZAPI `/message/download` with base64 return enabled. Evolution audio downloads through Evolution API `/chat/getBase64FromMediaMessage/{instance}` with base64 return enabled. AgentKit does not fetch arbitrary UAZAPI or Evolution webhook media hosts.
221
+
152
222
  V1 keeps the raw audio in memory for the request path and delivery metadata only records redacted status/error fields. `rawAudioTtlSeconds` is part of the manifest contract for future object-storage retention, but V1 does not persist raw audio by default.
153
223
 
154
224
  ## Safety Rules
@@ -167,6 +237,7 @@ npm run typecheck
167
237
  bun test
168
238
  agentkit inspect
169
239
  agentkit channels list
240
+ agentkit channels test n8n-webhook --message "hello"
170
241
  agentkit channels test support-telegram --message "hello"
171
242
  agentkit channels test-audio support-telegram --fixture voice-note
172
243
  agentkit transcribe smoke --provider groq
@@ -175,7 +246,7 @@ agentkit channels buffers list support-telegram
175
246
  ```
176
247
 
177
248
  Expected hosted status includes `NAME`, `TYPE`, `PROVIDER`, `STATUS`, and `LAST_EVENT`. Secret output is name plus `set` or `missing`, never the value.
178
- Outbound provider success is `provider_sent`. Explicit dry-run mode is `adapter_stubbed`, which means the provider was not called.
249
+ Outbound provider success is `provider_sent`. `adapter_stubbed` means no outbound provider call was made, either because explicit dry-run mode is enabled or the channel is inbound-only.
179
250
 
180
251
  ## Troubleshooting
181
252
 
@@ -79,7 +79,7 @@ Expected readiness:
79
79
 
80
80
  ## Connect Apps
81
81
 
82
- After deploy, create a hosted Composio Connect Link:
82
+ The Composio connect link is deploy-scoped, so declare `composioManaged(...)` first, deploy, then connect. After deploy, create a hosted Composio Connect Link:
83
83
 
84
84
  ```sh
85
85
  agentkit integrations connect composio --toolkit gmail
@@ -90,6 +90,8 @@ AgentKit prints a URL. Send that URL to the person who owns the app account.
90
90
 
91
91
  If the deploy has only one toolkit, `--toolkit` can be omitted.
92
92
 
93
+ `agentkit deploy` prints the recommended `integrations connect composio --toolkit <slug>` command for each configured toolkit in its production handoff.
94
+
93
95
  ## Calendar Defaults
94
96
 
95
97
  For Google Calendar agents, include read and write actions together. Do not expose only `GOOGLECALENDAR_CREATE_EVENT`; users naturally ask to see availability before booking.
@@ -112,6 +112,8 @@ Direct tool test:
112
112
  npm run agentkit -- tool lookup_order --input '{"orderId":"A100"}'
113
113
  ```
114
114
 
115
+ When the tool touches live data, writes externally, or enforces business rules, also add deterministic checks. Use fixtures, eval-safe branches, seed data, or direct tool inputs for success and failure paths such as missing confirmation, validation errors, provider 429s/timeouts, unavailable records, and privacy/no-leak behavior. Add an eval when the agent should call the tool with specific inputs or refuse to call it until required intake or confirmation is complete.
116
+
115
117
  Expected output:
116
118
 
117
119
  ```json
@@ -129,9 +131,10 @@ Use this when a tool needs agent-owned tables and must work locally and after de
129
131
 
130
132
  Recommended contract:
131
133
 
132
- - Put all agent-owned tables in `schema.sql`.
134
+ - Put the idempotent bootstrap view of agent-owned tables in `schema.sql`.
135
+ - For production-shaped schema evolution, add ordered `migrations/*.sql` files such as `migrations/0001_clients.sql`.
133
136
  - Keep deploy-ready capsules on `storage.driver: "agentkit"`.
134
- - Local `agentkit chat`, `agentkit tool`, `agentkit dev`, and eval runs apply `schema.sql` to local development storage.
137
+ - Local `agentkit chat`, `agentkit tool`, `agentkit dev`, and eval runs apply ordered local migrations before `schema.sql`.
135
138
  - Use `agentkit db migrate`, `agentkit db reset --yes`, `agentkit db seed [--file seed.sql]`, and `agentkit db shell` for local database setup and inspection.
136
139
  - Hosted deploy migrates/provisions the managed database internally and applies the same `schema.sql`.
137
140
  - Tools use `ctx.db` as the canonical helper. `ctx.database` and `ctx.storage.sql` are supported aliases.
@@ -176,7 +179,7 @@ CREATE TABLE IF NOT EXISTS appointments (
176
179
  );
177
180
  ```
178
181
 
179
- `schema.sql` is an idempotent bootstrap file in v1. Prefer `CREATE TABLE IF NOT EXISTS`, `CREATE INDEX IF NOT EXISTS`, and safe additive `ALTER TABLE` statements. AgentKit does not run destructive schema changes or ordered `migrations/*.sql` automatically yet.
182
+ `schema.sql` is an idempotent bootstrap file. Prefer `CREATE TABLE IF NOT EXISTS`, `CREATE INDEX IF NOT EXISTS`, and safe additive `ALTER TABLE` statements. Use ordered `migrations/*.sql` for production-shaped schema evolution; `agentkit db migrate` applies unapplied local migrations before `schema.sql`.
180
183
 
181
184
  `tools/schedule-appointment.ts`:
182
185
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Goal
4
4
 
5
- Configure website, Telegram, WhatsApp, Discord, and Slack channels without leaking secrets, storing raw provider payloads, or treating public webhook URLs as authorization.
5
+ Configure website, Telegram, WhatsApp, Discord, Slack, and generic webhook channels without leaking secrets, storing raw provider payloads, or treating public webhook URLs as authorization.
6
6
 
7
7
  ## When To Use It
8
8
 
@@ -33,6 +33,9 @@ agentkit channels deliveries show <delivery-id>
33
33
  - Do not store raw webhook bodies, raw audio, or provider secret values in tools, prompts, evals, fixtures, or delivery notes.
34
34
  - Do not expose Discord interaction tokens or bot tokens. Interaction tokens are reply tokens, not public identifiers.
35
35
  - Do not expose Slack bot tokens or signing secrets. Slack signing secrets authenticate webhook origin; Slack bot tokens authorize outbound replies.
36
+ - Do not expose `AGENTKIT_WEBHOOK_SECRET`. Generic webhook clients must authenticate with bearer/header token auth or an HMAC signature over the exact raw body.
37
+ - Do not remove `EVOLUTION_WEBHOOK_TOKEN` from Evolution WhatsApp webhooks. Evolution provider docs expose webhook delivery setup but no webhook signing-secret contract, so AgentKit requires a secret token query parameter and rejects requests without it.
38
+ - Keep `EVOLUTION_API_BASE_URL` on a public HTTPS origin. AgentKit rejects localhost, private network, link-local, and metadata-service hosts for Evolution outbound and media-download requests.
36
39
  - Inspect delivery IDs, hashes, redacted metadata, and statuses instead of raw provider payloads.
37
40
  - Keep channel plumbing out of the agent's application database. The user's Turso tables are for the agent's business data.
38
41
  - Enable audio transcription only when the capsule declares a transcription provider and bounded `limits`.
@@ -112,6 +115,34 @@ slackChannel({
112
115
 
113
116
  AgentKit validates `X-Slack-Signature` and `X-Slack-Request-Timestamp` against the raw body, rejects stale timestamps, answers Slack `url_verification` with the literal challenge, skips bot/subtype messages, and sends replies with Slack control mentions escaped.
114
117
 
118
+ Generic webhooks use an AgentKit shared secret:
119
+
120
+ ```ts
121
+ webhookChannel({
122
+ name: "n8n-webhook",
123
+ secrets: ["AGENTKIT_WEBHOOK_SECRET"],
124
+ });
125
+ ```
126
+
127
+ AgentKit validates `Authorization: Bearer <secret>`, `X-AgentKit-Webhook-Secret`, or `X-AgentKit-Webhook-Signature` against the raw body before parsing business fields. Payloads must include `event_id` for replay protection. `external_id` is hashed before storage.
128
+
129
+ Evolution WhatsApp uses a self-hosted API key and an AgentKit webhook token:
130
+
131
+ ```ts
132
+ whatsappChannel({
133
+ name: "support-whatsapp",
134
+ provider: "evolution",
135
+ secrets: [
136
+ "EVOLUTION_API_BASE_URL",
137
+ "EVOLUTION_API_KEY",
138
+ "EVOLUTION_INSTANCE_NAME",
139
+ "EVOLUTION_WEBHOOK_TOKEN",
140
+ ],
141
+ });
142
+ ```
143
+
144
+ AgentKit configures Evolution's webhook URL with `?token=<EVOLUTION_WEBHOOK_TOKEN>`, validates the token on ingress, redacts Evolution secret values, and rejects unsafe Evolution API base URLs before sending messages or downloading audio media.
145
+
115
146
  ## Verification
116
147
 
117
148
  ```sh
@@ -0,0 +1,121 @@
1
+ # Connect WhatsApp Through Evolution API
2
+
3
+ ## Goal
4
+
5
+ Connect a self-hosted Evolution API WhatsApp instance to a hosted AgentKit WhatsApp channel.
6
+
7
+ ## When To Use It
8
+
9
+ Use this after `whatsappChannel({ name: "main-whatsapp", provider: "evolution" })` exists in `agentkit.config.ts` and the capsule has been deployed.
10
+
11
+ ## Commands
12
+
13
+ ```sh
14
+ agentkit deploy
15
+ agentkit channels connect whatsapp main-whatsapp --provider evolution
16
+ agentkit channels status main-whatsapp
17
+ agentkit channels test main-whatsapp --message "hello"
18
+ agentkit channels deliveries list main-whatsapp
19
+ agentkit channels buffers list main-whatsapp
20
+ ```
21
+
22
+ Required secrets:
23
+
24
+ ```txt
25
+ EVOLUTION_API_BASE_URL
26
+ EVOLUTION_API_KEY
27
+ EVOLUTION_INSTANCE_NAME
28
+ EVOLUTION_WEBHOOK_TOKEN
29
+ ```
30
+
31
+ If WhatsApp audio transcription is enabled, also set the transcription provider secret declared by `agentkit inspect`, usually `OPENAI_API_KEY` or `GROQ_API_KEY`.
32
+
33
+ ## Minimal Working Example
34
+
35
+ ```ts
36
+ import { defineAgent, whatsappChannel } from "@andreprado/agentkit";
37
+
38
+ export default defineAgent({
39
+ name: "my-agent",
40
+ runtime: "edge",
41
+ provider: { name: "test", model: "fake" },
42
+ instructions: "./prompts/instructions.md",
43
+ secrets: [],
44
+ tools: [],
45
+ channels: [whatsappChannel({ name: "main-whatsapp", provider: "evolution" })],
46
+ access: { mode: "public" },
47
+ storage: { driver: "agentkit" },
48
+ });
49
+ ```
50
+
51
+ ## Setup Behavior
52
+
53
+ `agentkit channels connect whatsapp main-whatsapp --provider evolution` creates or updates the hosted channel, verifies managed secrets, calls Evolution API `POST /webhook/set/{instance}`, confirms the configured webhook through `GET /webhook/find/{instance}`, then runs a synthetic inbound smoke.
54
+
55
+ Expected webhook URL shape:
56
+
57
+ ```txt
58
+ https://<deploy-host>/channels/main-whatsapp/whatsapp/evolution/webhook
59
+ ```
60
+
61
+ AgentKit registers the webhook URL with the required token query parameter:
62
+
63
+ ```txt
64
+ https://<deploy-host>/channels/main-whatsapp/whatsapp/evolution/webhook?token=<EVOLUTION_WEBHOOK_TOKEN>
65
+ ```
66
+
67
+ `EVOLUTION_API_BASE_URL` must be the public HTTPS origin for the Evolution API server, for example `https://evolution.example.com`. AgentKit rejects HTTP, localhost, private-network, link-local, metadata, and credential-bearing base URLs before sending `EVOLUTION_API_KEY`.
68
+
69
+ ## Audio
70
+
71
+ Use `transcription` at the agent level and `audio.mode: "transcribe"` on the Evolution channel.
72
+
73
+ ```ts
74
+ whatsappChannel({
75
+ name: "main-whatsapp",
76
+ provider: "evolution",
77
+ audio: {
78
+ mode: "transcribe",
79
+ },
80
+ })
81
+ ```
82
+
83
+ Evolution audio webhooks become normalized audio messages. The retryable channel worker downloads media through Evolution API `POST /chat/getBase64FromMediaMessage/{instance}` and sends the bytes to AgentKit's configured transcription provider.
84
+
85
+ ## Safety Rules
86
+
87
+ - Keep phone numbers redacted in logs by default.
88
+ - Do not store Evolution API keys or webhook tokens in `agentkit.config.ts`.
89
+ - `EVOLUTION_WEBHOOK_TOKEN` is required because AgentKit V1 does not rely on an Evolution webhook body-signature contract.
90
+ - AgentKit ignores `fromMe` messages to avoid reply loops.
91
+ - Text replies call Evolution API `POST /message/sendText/{instance}` with the `apikey` header and a body containing `number` and `text`.
92
+ - Real provider success is recorded as `provider_sent` only when Evolution returns a provider message id.
93
+ - Only set `AGENTKIT_CHANNEL_SEND_DRY_RUN=1` in tests when Evolution should not receive a real message. Dry-run deliveries are recorded as `adapter_stubbed`, not sent.
94
+
95
+ ## Verification
96
+
97
+ ```sh
98
+ agentkit channels setup main-whatsapp --apply
99
+ agentkit channels status main-whatsapp
100
+ agentkit channels test main-whatsapp --message "hello"
101
+ agentkit channels test-audio main-whatsapp --fixture voice-note
102
+ agentkit channels deliveries list main-whatsapp
103
+ agentkit channels deliveries show <delivery-id>
104
+ ```
105
+
106
+ ## Troubleshooting
107
+
108
+ `channel_secret_missing`:
109
+ Set `EVOLUTION_API_BASE_URL`, `EVOLUTION_API_KEY`, `EVOLUTION_INSTANCE_NAME`, and `EVOLUTION_WEBHOOK_TOKEN` as hosted managed secrets.
110
+
111
+ `channel_signature_invalid`:
112
+ The Evolution webhook query token does not match.
113
+
114
+ `channel_provider_setup_invalid`:
115
+ `EVOLUTION_API_BASE_URL` is not an allowed public HTTPS base URL.
116
+
117
+ `channel_provider_setup_failed`:
118
+ AgentKit could not configure or verify the Evolution webhook through `/webhook/set/{instance}` and `/webhook/find/{instance}`.
119
+
120
+ `channel_audio_download_unavailable`:
121
+ Evolution did not return base64 media data from `/chat/getBase64FromMediaMessage/{instance}`, or the audio payload did not include a provider message id.
@@ -0,0 +1,126 @@
1
+ # Connect WhatsApp Through UAZAPI
2
+
3
+ ## Goal
4
+
5
+ Connect a UAZAPI WhatsApp instance to a hosted AgentKit WhatsApp channel.
6
+
7
+ ## When To Use It
8
+
9
+ Use this after `whatsappChannel({ name: "support-whatsapp", provider: "uazapi" })` exists in `agentkit.config.ts` and the capsule has been deployed.
10
+
11
+ ## Commands
12
+
13
+ ```sh
14
+ agentkit deploy
15
+ agentkit channels connect whatsapp support-whatsapp --provider uazapi
16
+ agentkit channels status support-whatsapp
17
+ agentkit channels test support-whatsapp --message "hello"
18
+ agentkit channels deliveries list support-whatsapp
19
+ agentkit channels buffers list support-whatsapp
20
+ ```
21
+
22
+ Required secrets:
23
+
24
+ ```txt
25
+ UAZAPI_BASE_URL
26
+ UAZAPI_TOKEN
27
+ ```
28
+
29
+ Optional hardening secret:
30
+
31
+ ```txt
32
+ UAZAPI_WEBHOOK_TOKEN
33
+ ```
34
+
35
+ If WhatsApp audio transcription is enabled, also set the transcription provider secret declared by `agentkit inspect`, usually `OPENAI_API_KEY` or `GROQ_API_KEY`.
36
+
37
+ ## Minimal Working Example
38
+
39
+ ```ts
40
+ import { defineAgent, whatsappChannel } from "@andreprado/agentkit";
41
+
42
+ export default defineAgent({
43
+ name: "support-agent",
44
+ runtime: "edge",
45
+ provider: { name: "test", model: "fake" },
46
+ instructions: "./prompts/instructions.md",
47
+ secrets: [],
48
+ tools: [],
49
+ channels: [whatsappChannel({ name: "support-whatsapp", provider: "uazapi" })],
50
+ access: { mode: "public" },
51
+ storage: { driver: "agentkit" },
52
+ });
53
+ ```
54
+
55
+ ## Setup Behavior
56
+
57
+ `agentkit channels connect whatsapp support-whatsapp --provider uazapi` creates or updates the hosted channel, verifies managed secrets, calls UAZAPI `/webhook`, confirms the configured webhook is listed by UAZAPI, then runs a synthetic inbound smoke.
58
+
59
+ Expected webhook URL shape:
60
+
61
+ ```txt
62
+ https://<deploy-host>/channels/support-whatsapp/whatsapp/uazapi/webhook
63
+ ```
64
+
65
+ If the channel declares `UAZAPI_WEBHOOK_TOKEN`, AgentKit registers the webhook URL with the token query parameter:
66
+
67
+ ```txt
68
+ https://<deploy-host>/channels/support-whatsapp/whatsapp/uazapi/webhook?token=<UAZAPI_WEBHOOK_TOKEN>
69
+ ```
70
+
71
+ `UAZAPI_BASE_URL` must be an HTTPS UAZAPI instance URL, for example `https://api.uazapi.com`. AgentKit rejects localhost, private-network, link-local, metadata, and credential-bearing base URLs before sending `UAZAPI_TOKEN`.
72
+
73
+ ## Audio
74
+
75
+ Use `transcription` at the agent level and `audio.mode: "transcribe"` on the UAZAPI channel.
76
+
77
+ ```ts
78
+ whatsappChannel({
79
+ name: "support-whatsapp",
80
+ provider: "uazapi",
81
+ audio: {
82
+ mode: "transcribe",
83
+ },
84
+ })
85
+ ```
86
+
87
+ UAZAPI audio webhooks become normalized audio messages. The retryable channel worker downloads media through UAZAPI `/message/download` using `return_base64: true`; AgentKit does not fetch arbitrary webhook-provided media URLs.
88
+
89
+ ## Safety Rules
90
+
91
+ - Keep phone numbers redacted in logs by default.
92
+ - Do not store UAZAPI tokens in `agentkit.config.ts`.
93
+ - Use optional `UAZAPI_WEBHOOK_TOKEN` when the endpoint should require a secret query token in addition to the public route.
94
+ - UAZAPI does not expose a documented webhook body-signature contract in AgentKit V1; treat the optional query token as the hardening layer.
95
+ - AgentKit ignores `fromMe` and `wasSentByApi` messages to avoid reply loops.
96
+ - Text replies call UAZAPI `POST /send/text` with the `token` header and a body containing `number`, `text`, `readchat`, `async`, `track_source`, and `track_id`.
97
+ - Real provider success is recorded as `provider_sent` only when UAZAPI returns a provider message id.
98
+ - Only set `AGENTKIT_CHANNEL_SEND_DRY_RUN=1` in tests when UAZAPI should not receive a real message. Dry-run deliveries are recorded as `adapter_stubbed`, not sent.
99
+
100
+ ## Verification
101
+
102
+ ```sh
103
+ agentkit channels setup support-whatsapp --apply
104
+ agentkit channels status support-whatsapp
105
+ agentkit channels test support-whatsapp --message "hello"
106
+ agentkit channels test-audio support-whatsapp --fixture voice-note
107
+ agentkit channels deliveries list support-whatsapp
108
+ agentkit channels deliveries show <delivery-id>
109
+ ```
110
+
111
+ ## Troubleshooting
112
+
113
+ `channel_secret_missing`:
114
+ Set `UAZAPI_BASE_URL` and `UAZAPI_TOKEN` as hosted managed secrets. If the channel declares `UAZAPI_WEBHOOK_TOKEN`, set that managed secret too.
115
+
116
+ `channel_signature_invalid`:
117
+ The optional UAZAPI webhook query token does not match.
118
+
119
+ `channel_provider_setup_invalid`:
120
+ `UAZAPI_BASE_URL` is not an allowed HTTPS public base URL.
121
+
122
+ `channel_provider_setup_failed`:
123
+ AgentKit could not configure or verify the UAZAPI webhook through `/webhook`.
124
+
125
+ `channel_audio_download_unavailable`:
126
+ UAZAPI did not return base64 media data from `/message/download`, or the audio payload did not include a provider message id.
@@ -89,7 +89,7 @@ Runtime files created after chat:
89
89
 
90
90
  ## Handoff To A Coding Agent
91
91
 
92
- The owner does not need to fill a separate brief file. The natural-language request they type into Codex, Claude Code, or another coding agent is the brief.
92
+ The owner does not need to run a brief wizard or fill a separate brief file. The natural-language request they type into Codex, Claude Code, or another coding agent is the brief.
93
93
 
94
94
  Primary flow:
95
95
 
@@ -97,9 +97,9 @@ Primary flow:
97
97
  Develop an appointment and intake agent for an ophthalmology office.
98
98
  ```
99
99
 
100
- The generated `AGENTS.md`, `AGENTKIT.md`, `CLAUDE.md`, and `skills/` pack tell the coding agent which files to edit, which task skill to load, and which verification commands to run. There is no wizard or recipe layer: the coding agent edits the capsule directly from the scaffold, contract, and owner request. The default router is `skills/agentkit-capsule/SKILL.md`; `llms-full.txt` is reserved for complete-contract checks.
100
+ The generated `AGENTS.md`, `AGENTKIT.md`, `CLAUDE.md`, and `skills/` pack tell the coding agent which files to edit, which task skill to load, and which verification commands to run. There is no AgentKit CLI wizard in the normal flow: the coding agent edits the capsule directly from the scaffold, contract, and owner request. The default router is `skills/agentkit-capsule/SKILL.md`; `llms-full.txt` is reserved for complete-contract checks.
101
101
 
102
- After the owner gives the general idea, the coding agent should create or update the implementation contract itself:
102
+ After the owner gives the general idea inside the coding-agent chat, the coding agent should create or update the implementation contract itself:
103
103
 
104
104
  ```sh
105
105
  npm run agentkit -- spec init --brief "Develop an appointment and intake agent for an ophthalmology office."
@@ -108,6 +108,8 @@ npm run agentkit -- spec check
108
108
 
109
109
  `AGENT_SPEC.md` is an internal working contract for the coding agent. It is not a form the owner must fill before work starts.
110
110
 
111
+ The coding agent should build a testable capsule, not only a prompt. For each meaningful requirement in the brief or `AGENT_SPEC.md`, decide whether it needs a prompt instruction, tool, schema/migration, fixture, eval, direct tool check, or deploy/readiness check. Privacy, confirmation, external writes, bookings, customer data, business hours, dates, and integration failures should have evals or deterministic checks before the capsule is called done.
112
+
111
113
  Optional shortcut when copying a prompt into another coding agent:
112
114
 
113
115
  ```sh
@@ -176,7 +178,7 @@ npm run agentkit -- chat-ui --deploy
176
178
 
177
179
  Open the printed `Chat:` URL and tell the owner this local UI is connected to the hosted deploy.
178
180
 
179
- `test/fake` is deterministic. It validates the scaffold, direct tool checks, and fake-provider evals, but it does not validate natural conversation quality. Before claiming real conversation behavior is tested, ask the owner which provider to use: OpenRouter, OpenAI, Anthropic, or another supported provider.
181
+ `test/fake` is deterministic. It validates the scaffold, direct tool checks, and fake-provider evals, but it does not validate natural conversation quality. Before claiming real conversation behavior is tested, ask the owner which provider to use: OpenRouter, OpenAI, Anthropic, OpenCode Zen, OpenCode Go, or another supported provider.
180
182
 
181
183
  ## Safety Rules
182
184
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Goal
4
4
 
5
- Diagnose website, Telegram, WhatsApp, Discord, or Slack channel setup, webhook validation, dedupe, buffering, outbound sends, and delivery failures from the Agent Capsule CLI.
5
+ Diagnose website, Telegram, WhatsApp, Discord, Slack, or generic webhook channel setup, webhook validation, dedupe, buffering, outbound sends, and delivery failures from the Agent Capsule CLI.
6
6
 
7
7
  ## When To Use It
8
8
 
@@ -69,7 +69,7 @@ Expected `show` output includes signature status, dedupe key, queue/run state, o
69
69
 
70
70
  - Inspect delivery IDs, hashes, statuses, and redacted metadata rather than raw provider payloads.
71
71
  - Never paste provider secrets into fixtures or prompts.
72
- - Treat `adapter_stubbed` as a dry-run send, not proof that a client received a message.
72
+ - Treat `adapter_stubbed` as "no outbound provider call happened", not proof that a client received a message. It can mean explicit dry-run mode or an inbound-only channel such as a generic webhook.
73
73
  - Before replaying or flushing buffered conversations, confirm the channel name and conversation ID.
74
74
  - Keep local `.env` values ignored and upload hosted production values through AgentKit secret commands.
75
75
 
@@ -117,7 +117,7 @@ The provider retried an already-processed event. No second agent run should be c
117
117
  The message is accepted and waiting inside a per-conversation channel buffer. It should move to `queued` after the quiet window, max wait, max message count, or max character count.
118
118
 
119
119
  `adapter_stubbed`:
120
- The adapter built the outbound request in explicit dry-run mode. The provider was not called.
120
+ The adapter completed without calling an outbound provider. This can happen in explicit dry-run mode or for an inbound-only channel such as a generic webhook.
121
121
 
122
122
  `provider_sent`:
123
123
  The provider API accepted the outbound request and returned a provider message ID.
@@ -154,7 +154,7 @@ npm run agentkit -- chat-ui --deploy
154
154
  npm run agentkit -- access token list
155
155
  ```
156
156
 
157
- For hosted deploys, `npm run agentkit -- deploy` writes the local chat/UI access token to `.agentkit/chat-access-token.json`. Use `npm run agentkit -- deploy --smoke "hello"` for the official hosted chat smoke, and use `npm run agentkit -- chat-ui --deploy` for hosted UI testing. The hosted Chat UI shows the conversation id, tool calls, tool errors, and a new-conversation control; use `npm run agentkit -- conversations trace <conversation-id> --deploy` to pull the hosted trace from the last deploy. Use `npm run agentkit -- access token create <name> --out <path>` only for additional clients.
157
+ For hosted deploys, `npm run agentkit -- deploy` writes the local chat/UI access token to `.agentkit/chat-access-token.json` and prints a production handoff with the deploy URL, UI command, secret status, database/schema artifact, integration connect commands, smoke status, and next recommended command. Use `npm run agentkit -- deploy --smoke "hello"` for the official hosted chat smoke, and use `npm run agentkit -- chat-ui --deploy` for hosted UI testing. The hosted Chat UI shows the conversation id, tool calls, tool errors, and a new-conversation control; use `npm run agentkit -- conversations trace <conversation-id> --deploy` to pull the hosted trace from the last deploy. Use `npm run agentkit -- access token create <name> --out <path>` only for additional clients.
158
158
 
159
159
  When the UI is running, open the printed `Chat:` URL and tell the owner the exact URL. If the capsule is still on `test/fake`, say the UI was tested only with the deterministic fake provider.
160
160
 
@@ -22,6 +22,8 @@ Run evals:
22
22
  agentkit eval run
23
23
  ```
24
24
 
25
+ `agentkit eval run` uses temporary local SQLite storage for eval execution. This keeps eval conversations and tool calls isolated from `.agentkit/agentkit.db`, so evals can run while a local chat or dev server is using the normal development database.
26
+
25
27
  If the capsule uses npm scripts and Windows PowerShell blocks `npm.ps1`, use:
26
28
 
27
29
  ```sh
@@ -54,6 +56,8 @@ Create or edit:
54
56
  evals/<name>.eval.ts
55
57
  ```
56
58
 
59
+ Create evals proactively from the agent brief and `AGENT_SPEC.md`. High-value evals cover identity and scope, required intake fields, confirmation before writes, no-leak/privacy rules, timezone and business-hour behavior, default durations or limits, tool-call payloads, unavailable slots, empty results, missing auth, rate limits, and timeouts. If a real conversation reveals a bug, add the smallest regression eval that would have failed before the fix.
60
+
57
61
  Use conversations as source material:
58
62
 
59
63
  ```txt
@@ -152,7 +156,7 @@ export default defineEval({
152
156
  });
153
157
  ```
154
158
 
155
- `tools.persisted` validates the tool call saved in local SQLite `tool_calls`, not a provider-specific raw response shape. It can be a tool name string or an object with `name`, `input`, `output`, `rendered`, `status`, and/or `visibility`.
159
+ `tools.persisted` validates the tool call saved in the eval run's local SQLite `tool_calls`, not a provider-specific raw response shape. It can be a tool name string or an object with `name`, `input`, `output`, `rendered`, `status`, and/or `visibility`.
156
160
 
157
161
  Use `tools.count` for the exact number of persisted calls in that turn, `tools.calledOnce` for exactly one call by name, and `tools.order` for required relative order. `tools.order` allows extra calls before, between, or after the named calls; pair it with `tools.count` when the exact call set matters.
158
162