@andreprado/agentkit 0.1.0-alpha.23 → 0.1.0-alpha.24
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/docs/guides/add-channel.md +92 -5
- package/docs/guides/channel-security.md +35 -3
- package/docs/guides/debug-channel.md +16 -4
- package/docs/llms-full.txt +54 -1
- package/docs/llms.txt +2 -0
- package/package.json +1 -1
- package/src/cli/commands/channels.ts +35 -1
- package/src/index.ts +191 -5
- package/src/runtime/channels/generic-webhook.ts +752 -3
- package/src/runtime/channels.ts +3 -0
- package/src/runtime/config.ts +144 -0
- package/src/runtime/dev-server.ts +131 -1
- package/src/runtime/inspect.ts +18 -0
- package/src/runtime/targets/cloudflare/build.ts +112 -4
|
@@ -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, Discord, Slack, or a generic webhook 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, or when an inbound channel should reply through another configured channel.
|
|
10
10
|
|
|
11
11
|
## Commands
|
|
12
12
|
|
|
@@ -34,16 +34,17 @@ Use `--api <url>` only when the owner gives you a non-default AgentKit Cloud API
|
|
|
34
34
|
|
|
35
35
|
## Files Created Or Edited
|
|
36
36
|
|
|
37
|
-
- `agentkit.config.ts`: declare channel config with `websiteChannel`, `telegramChannel`, `whatsappChannel`, `discordChannel`, `slackChannel`, or `
|
|
37
|
+
- `agentkit.config.ts`: declare channel config with `websiteChannel`, `telegramChannel`, `whatsappChannel`, `discordChannel`, `slackChannel`, `webhookChannel`, or `webhookOutputChannel`.
|
|
38
38
|
- `.agentkit/deploy.json`: written by `agentkit deploy`; used by `agentkit channels ...`.
|
|
39
39
|
- No user Turso tables: AgentKit owns channel resources, dedupe, identities, queue state, and delivery logs.
|
|
40
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>`.
|
|
41
|
+
- Generic inbound webhook clients must send `AGENTKIT_WEBHOOK_SECRET` as `Authorization: Bearer <token>`, `X-AgentKit-Webhook-Secret`, or `X-AgentKit-Webhook-Signature: sha256=<hmac>`.
|
|
42
|
+
- Generic outbound webhook channels send replies to an HTTPS URL stored in a managed secret such as `CRM_CALLBACK_URL`.
|
|
42
43
|
|
|
43
44
|
## Minimal Working Example
|
|
44
45
|
|
|
45
46
|
```ts
|
|
46
|
-
import { defineAgent, discordChannel, slackChannel, telegramChannel, webhookChannel, whatsappChannel, websiteChannel } from "@andreprado/agentkit";
|
|
47
|
+
import { defineAgent, discordChannel, slackChannel, telegramChannel, webhookChannel, webhookOutputChannel, whatsappChannel, websiteChannel } from "@andreprado/agentkit";
|
|
47
48
|
|
|
48
49
|
export default defineAgent({
|
|
49
50
|
name: "support-agent",
|
|
@@ -62,6 +63,7 @@ export default defineAgent({
|
|
|
62
63
|
discordChannel({ name: "server-discord", mode: "bot" }),
|
|
63
64
|
slackChannel({ name: "support-slack" }),
|
|
64
65
|
webhookChannel({ name: "n8n-webhook" }),
|
|
66
|
+
webhookOutputChannel({ name: "crm-callback", urlSecret: "CRM_CALLBACK_URL" }),
|
|
65
67
|
],
|
|
66
68
|
access: { mode: "public" },
|
|
67
69
|
storage: { driver: "agentkit" },
|
|
@@ -151,7 +153,92 @@ For HMAC auth, compute `HMAC-SHA256(raw JSON body, AGENTKIT_WEBHOOK_SECRET)` and
|
|
|
151
153
|
X-AgentKit-Webhook-Signature: sha256=<hex digest>
|
|
152
154
|
```
|
|
153
155
|
|
|
154
|
-
|
|
156
|
+
Without `replyTo`, generic webhooks are inbound-only: the agent run is queued and delivery logs show the result, but AgentKit does not call back into the source system.
|
|
157
|
+
|
|
158
|
+
## Reply Through Another Channel
|
|
159
|
+
|
|
160
|
+
Use `replyTo` when an inbound channel should receive on one transport and answer on another. The source channel still owns webhook validation, dedupe, and queueing; the target channel owns the outbound provider call.
|
|
161
|
+
|
|
162
|
+
Receive from a generic webhook and answer on WhatsApp:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
webhookChannel({
|
|
166
|
+
name: "lead-webhook",
|
|
167
|
+
replyTo: {
|
|
168
|
+
channel: "main-whatsapp",
|
|
169
|
+
recipientFrom: "phone",
|
|
170
|
+
},
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
whatsappChannel({
|
|
174
|
+
name: "main-whatsapp",
|
|
175
|
+
provider: "evolution",
|
|
176
|
+
})
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The inbound webhook payload must include the configured recipient field:
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"event_id": "evt_124",
|
|
184
|
+
"external_id": "lead_123",
|
|
185
|
+
"phone": "+15551234567",
|
|
186
|
+
"message": "please follow up"
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`recipientFrom` is a dotted JSON path such as `phone`, `customer.phone`, or `payload.customer.phone`. AgentKit reads it from the raw inbound payload, requires a non-empty scalar value, and formats it for the target channel. Do not use `recipientFrom` for Discord or Slack replies; those require provider-native source identities from their own inbound events.
|
|
191
|
+
|
|
192
|
+
You can declare multiple webhook inputs by giving each `webhookChannel` a unique name:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
webhookChannel({ name: "n8n-webhook" })
|
|
196
|
+
webhookChannel({ name: "make-webhook" })
|
|
197
|
+
webhookChannel({ name: "crm-webhook", replyTo: { channel: "main-whatsapp", recipientFrom: "phone" } })
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Generic Output Webhooks
|
|
201
|
+
|
|
202
|
+
Use `webhookOutputChannel` when the agent should send the final answer to a generic callback URL, such as an internal CRM, n8n callback, Make webhook, or a custom API endpoint.
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
webhookOutputChannel({
|
|
206
|
+
name: "crm-callback",
|
|
207
|
+
urlSecret: "CRM_CALLBACK_URL",
|
|
208
|
+
auth: {
|
|
209
|
+
type: "bearer",
|
|
210
|
+
tokenSecret: "CRM_CALLBACK_TOKEN",
|
|
211
|
+
},
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
webhookChannel({
|
|
215
|
+
name: "n8n-webhook",
|
|
216
|
+
replyTo: {
|
|
217
|
+
channel: "crm-callback",
|
|
218
|
+
recipientFrom: "crm_id",
|
|
219
|
+
},
|
|
220
|
+
})
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Set `CRM_CALLBACK_URL` as a managed secret. It must be a public HTTPS URL; AgentKit rejects localhost, private network, link-local, and metadata-service hosts before sending. Supported output auth modes are `none`, `bearer`, `header`, and `hmac`, and all auth values come from managed secrets.
|
|
224
|
+
|
|
225
|
+
Outbound generic webhook requests contain the agent's final text answer:
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"event_id": "webhook:generic:crm-callback:<conversation-id>",
|
|
230
|
+
"conversation_id": "<conversation-id>",
|
|
231
|
+
"channel_id": "crm-callback",
|
|
232
|
+
"external_identity": "webhook:generic:recipient:acct_123",
|
|
233
|
+
"message": {
|
|
234
|
+
"role": "assistant",
|
|
235
|
+
"content_type": "text",
|
|
236
|
+
"content": "Thanks, I can help with that."
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Generic output channels do not have public inbound webhook URLs. `agentkit channels test <output-name>` is unsupported because there is no inbound endpoint to smoke.
|
|
155
242
|
|
|
156
243
|
## Auto Transcribe Audio
|
|
157
244
|
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Goal
|
|
4
4
|
|
|
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.
|
|
5
|
+
Configure website, Telegram, WhatsApp, Discord, Slack, generic inbound webhook channels, and generic outbound 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
|
|
|
9
|
-
Use this when a coding agent adds or changes channels in an Agent Capsule, enables audio transcription, inspects deliveries,
|
|
9
|
+
Use this when a coding agent adds or changes channels in an Agent Capsule, enables audio transcription, inspects deliveries, prepares a hosted deploy that receives provider webhooks, or routes replies through another channel.
|
|
10
10
|
|
|
11
11
|
## Commands
|
|
12
12
|
|
|
@@ -28,12 +28,15 @@ agentkit channels deliveries show <delivery-id>
|
|
|
28
28
|
## Safety Rules
|
|
29
29
|
|
|
30
30
|
- Public webhook URLs are not permission grants.
|
|
31
|
-
- Keep provider tokens, webhook secrets, bot tokens, transcription keys, and bearer tokens out of source files.
|
|
31
|
+
- Keep provider tokens, webhook secrets, output webhook URLs, bot tokens, transcription keys, and bearer tokens out of source files.
|
|
32
32
|
- Put secret names in `agentkit.config.ts`; put local values in ignored `.env`; upload production values with `agentkit secret set` or `agentkit secret sync`.
|
|
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
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 put generic output webhook URLs or auth values in payloads, prompts, tools, fixtures, or `agentkit.config.ts`. Use `urlSecret` and output auth secret names only.
|
|
38
|
+
- Keep generic output webhook URLs on public HTTPS origins. AgentKit rejects localhost, private network, link-local, and metadata-service hosts before sending.
|
|
39
|
+
- Do not route replies to a channel name from the inbound payload. `replyTo.channel` must be static config, and `recipientFrom` may only select the recipient identifier.
|
|
37
40
|
- 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
41
|
- 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.
|
|
39
42
|
- Inspect delivery IDs, hashes, redacted metadata, and statuses instead of raw provider payloads.
|
|
@@ -126,6 +129,35 @@ webhookChannel({
|
|
|
126
129
|
|
|
127
130
|
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
131
|
|
|
132
|
+
Generic output webhooks use managed URL and auth secrets:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
webhookOutputChannel({
|
|
136
|
+
name: "crm-callback",
|
|
137
|
+
urlSecret: "CRM_CALLBACK_URL",
|
|
138
|
+
auth: {
|
|
139
|
+
type: "hmac",
|
|
140
|
+
secret: "CRM_CALLBACK_SIGNING_SECRET",
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The output URL must be a public HTTPS URL stored in `CRM_CALLBACK_URL`. Supported auth modes are `none`, `bearer`, `header`, and `hmac`. For HMAC auth, AgentKit signs the exact JSON callback body and sends `sha256=<digest>` in `X-AgentKit-Webhook-Signature` unless a different `headerName` is configured.
|
|
146
|
+
|
|
147
|
+
Cross-channel replies use static routing:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
webhookChannel({
|
|
151
|
+
name: "lead-webhook",
|
|
152
|
+
replyTo: {
|
|
153
|
+
channel: "crm-callback",
|
|
154
|
+
recipientFrom: "crm_id",
|
|
155
|
+
},
|
|
156
|
+
})
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`replyTo.channel` must name another configured channel. `recipientFrom` is a dotted JSON path into the inbound payload and must resolve to a non-empty string, number, or boolean. It is for recipient identity only; never use inbound data to choose a callback URL, auth secret, or channel name.
|
|
160
|
+
|
|
129
161
|
Evolution WhatsApp uses a self-hosted API key and an AgentKit webhook token:
|
|
130
162
|
|
|
131
163
|
```ts
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Goal
|
|
4
4
|
|
|
5
|
-
Diagnose website, Telegram, WhatsApp, Discord, Slack,
|
|
5
|
+
Diagnose website, Telegram, WhatsApp, Discord, Slack, generic inbound webhook, generic outbound webhook, cross-channel reply routing, webhook validation, dedupe, buffering, outbound sends, and delivery failures from the Agent Capsule CLI.
|
|
6
6
|
|
|
7
7
|
## When To Use It
|
|
8
8
|
|
|
9
|
-
Use this when a hosted channel is not responding, a provider is retrying messages, audio transcription is failing, or delivery status does not match the user's expectation.
|
|
9
|
+
Use this when a hosted channel is not responding, a provider is retrying messages, audio transcription is failing, cross-channel replies are going to the wrong target, or delivery status does not match the user's expectation.
|
|
10
10
|
|
|
11
11
|
## Commands
|
|
12
12
|
|
|
@@ -20,6 +20,7 @@ agentkit transcribe smoke --provider groq
|
|
|
20
20
|
agentkit channels test <name> --fixture ./fixtures/provider-event.json
|
|
21
21
|
agentkit channels deliveries list <name> --since 24h
|
|
22
22
|
agentkit channels deliveries show <delivery-id>
|
|
23
|
+
agentkit channels deliveries list <reply-target-name> --since 24h
|
|
23
24
|
agentkit channels buffers list <name>
|
|
24
25
|
agentkit channels buffers show <conversation-id>
|
|
25
26
|
agentkit channels buffers flush <conversation-id>
|
|
@@ -70,6 +71,8 @@ Expected `show` output includes signature status, dedupe key, queue/run state, o
|
|
|
70
71
|
- Inspect delivery IDs, hashes, statuses, and redacted metadata rather than raw provider payloads.
|
|
71
72
|
- Never paste provider secrets into fixtures or prompts.
|
|
72
73
|
- 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.
|
|
74
|
+
- For `replyTo` routes, inspect the source channel delivery for validation, dedupe, buffer, queue, and agent-run state, then inspect the target channel delivery for the outbound provider result.
|
|
75
|
+
- Generic output webhook channels do not have inbound endpoints. Use `agentkit channels doctor <name>` to inspect secret readiness and inspect target-channel deliveries after a source event triggers the route.
|
|
73
76
|
- Before replaying or flushing buffered conversations, confirm the channel name and conversation ID.
|
|
74
77
|
- Keep local `.env` values ignored and upload hosted production values through AgentKit secret commands.
|
|
75
78
|
|
|
@@ -117,10 +120,19 @@ The provider retried an already-processed event. No second agent run should be c
|
|
|
117
120
|
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
121
|
|
|
119
122
|
`adapter_stubbed`:
|
|
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
|
|
123
|
+
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 without `replyTo`.
|
|
121
124
|
|
|
122
125
|
`provider_sent`:
|
|
123
|
-
The provider API accepted the outbound request and returned a provider message ID.
|
|
126
|
+
The provider API accepted the outbound request and returned a provider message ID. For cross-channel replies, this status appears on the reply target channel, such as WhatsApp or a generic output webhook.
|
|
127
|
+
|
|
128
|
+
`channel_reply_target_missing`:
|
|
129
|
+
The source channel has `replyTo.channel`, but the hosted channel resource for that target does not exist in the same deploy. Create or reconnect the target channel, then retry the source event.
|
|
130
|
+
|
|
131
|
+
`channel_reply_recipient_missing`:
|
|
132
|
+
The route needs a destination identity but `recipientFrom` was missing, resolved to an empty value, or the source identity cannot be reused by the target channel. Check the inbound payload and the configured dotted JSON path.
|
|
133
|
+
|
|
134
|
+
`channel_reply_target_invalid`:
|
|
135
|
+
The route points to an invalid target, such as an inbound-only generic webhook, or tries to use `recipientFrom` for a provider that requires native source identities such as Discord or Slack.
|
|
124
136
|
|
|
125
137
|
`channel_limit_exceeded`:
|
|
126
138
|
Backpressure skipped the message before queueing.
|
package/docs/llms-full.txt
CHANGED
|
@@ -372,7 +372,7 @@ Channels are hosted inbound/outbound conversation transports. They are separate
|
|
|
372
372
|
Use these helpers in `agentkit.config.ts`:
|
|
373
373
|
|
|
374
374
|
```ts
|
|
375
|
-
import { defineAgent, discordChannel, slackChannel, telegramChannel, webhookChannel, whatsappChannel, websiteChannel } from "@andreprado/agentkit";
|
|
375
|
+
import { defineAgent, discordChannel, slackChannel, telegramChannel, webhookChannel, webhookOutputChannel, whatsappChannel, websiteChannel } from "@andreprado/agentkit";
|
|
376
376
|
|
|
377
377
|
export default defineAgent({
|
|
378
378
|
name: "support-agent",
|
|
@@ -390,6 +390,7 @@ export default defineAgent({
|
|
|
390
390
|
discordChannel({ name: "server-discord", mode: "bot" }),
|
|
391
391
|
slackChannel({ name: "support-slack" }),
|
|
392
392
|
webhookChannel({ name: "n8n-webhook" }),
|
|
393
|
+
webhookOutputChannel({ name: "crm-callback", urlSecret: "CRM_CALLBACK_URL" }),
|
|
393
394
|
],
|
|
394
395
|
access: { mode: "public" },
|
|
395
396
|
storage: { driver: "agentkit" },
|
|
@@ -405,6 +406,8 @@ Rules:
|
|
|
405
406
|
- Do not store channel plumbing in the user's Turso database.
|
|
406
407
|
- Use `buffer.mode: "debounce"` when a channel should coalesce rapid client messages into one agent run.
|
|
407
408
|
- Buffered deliveries show `buffered`, then flush to one `queued` run after `quietWindowMs`, `maxWaitMs`, `maxMessages`, or `maxChars`.
|
|
409
|
+
- Use `replyTo` when an inbound channel should receive on one transport and answer on another configured channel.
|
|
410
|
+
- Use `webhookOutputChannel` when the reply target is a generic HTTPS callback URL stored in a managed secret.
|
|
408
411
|
|
|
409
412
|
Channel buffer example:
|
|
410
413
|
|
|
@@ -495,6 +498,56 @@ Generic webhook payloads should be canonical JSON:
|
|
|
495
498
|
|
|
496
499
|
Use `Authorization: Bearer <AGENTKIT_WEBHOOK_SECRET>`, `X-AgentKit-Webhook-Secret`, or `X-AgentKit-Webhook-Signature: sha256=<hmac>` where the HMAC is SHA-256 over the exact raw JSON body. The hosted URL is `/channels/<name>/webhook`.
|
|
497
500
|
|
|
501
|
+
Without `replyTo`, generic webhooks are inbound-only and do not call back into the source system. To receive on one channel and answer on another, add a static `replyTo` route:
|
|
502
|
+
|
|
503
|
+
```ts
|
|
504
|
+
webhookChannel({
|
|
505
|
+
name: "lead-webhook",
|
|
506
|
+
replyTo: {
|
|
507
|
+
channel: "main-whatsapp",
|
|
508
|
+
recipientFrom: "phone",
|
|
509
|
+
},
|
|
510
|
+
})
|
|
511
|
+
|
|
512
|
+
whatsappChannel({
|
|
513
|
+
name: "main-whatsapp",
|
|
514
|
+
provider: "evolution",
|
|
515
|
+
})
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
`recipientFrom` is a dotted JSON path such as `phone`, `customer.phone`, or `payload.customer.phone`. It must resolve to a non-empty scalar in the inbound payload. Do not use `recipientFrom` for Discord or Slack replies because those transports need provider-native source identities.
|
|
519
|
+
|
|
520
|
+
Multiple webhooks are multiple named channels:
|
|
521
|
+
|
|
522
|
+
```ts
|
|
523
|
+
webhookChannel({ name: "n8n-webhook" })
|
|
524
|
+
webhookChannel({ name: "make-webhook" })
|
|
525
|
+
webhookChannel({ name: "crm-webhook", replyTo: { channel: "main-whatsapp", recipientFrom: "phone" } })
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
Generic output webhook channels send the final agent answer to an HTTPS callback URL:
|
|
529
|
+
|
|
530
|
+
```ts
|
|
531
|
+
webhookOutputChannel({
|
|
532
|
+
name: "crm-callback",
|
|
533
|
+
urlSecret: "CRM_CALLBACK_URL",
|
|
534
|
+
auth: {
|
|
535
|
+
type: "bearer",
|
|
536
|
+
tokenSecret: "CRM_CALLBACK_TOKEN",
|
|
537
|
+
},
|
|
538
|
+
})
|
|
539
|
+
|
|
540
|
+
webhookChannel({
|
|
541
|
+
name: "n8n-webhook",
|
|
542
|
+
replyTo: {
|
|
543
|
+
channel: "crm-callback",
|
|
544
|
+
recipientFrom: "crm_id",
|
|
545
|
+
},
|
|
546
|
+
})
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
`CRM_CALLBACK_URL` and output auth values are managed secrets only. The URL must be public HTTPS; AgentKit rejects localhost, private network, link-local, and metadata-service hosts. Supported output auth modes are `none`, `bearer`, `header`, and `hmac`. Generic output channels do not have public inbound webhook URLs, so `agentkit channels test <output-name>` is unsupported.
|
|
550
|
+
|
|
498
551
|
Common channel verification:
|
|
499
552
|
|
|
500
553
|
```sh
|
package/docs/llms.txt
CHANGED
|
@@ -19,6 +19,7 @@ Task guides:
|
|
|
19
19
|
- Build container artifact: `agentkit build --target container`
|
|
20
20
|
- Generate VPS handoff: `agentkit deploy --target vps --host agent.example.com --dry-run`
|
|
21
21
|
- Add hosted channels: `docs/guides/add-channel.md`
|
|
22
|
+
- Route replies across channels or add generic output webhooks: `docs/guides/add-channel.md#reply-through-another-channel`
|
|
22
23
|
- Add AgentKit-managed Composio: `docs/guides/add-managed-composio.md`
|
|
23
24
|
- Buffer rapid channel messages: `docs/guides/add-channel.md#buffer-bursty-messages`
|
|
24
25
|
- Connect Discord: `docs/guides/connect-discord.md`
|
|
@@ -28,6 +29,7 @@ Task guides:
|
|
|
28
29
|
- Connect WhatsApp through UAZAPI: `docs/guides/connect-whatsapp-uazapi.md`
|
|
29
30
|
- Connect WhatsApp through Zapster: `docs/guides/connect-whatsapp-zapster.md`
|
|
30
31
|
- Follow channel webhook and delivery-log safety rules: `docs/guides/channel-security.md`
|
|
32
|
+
- Debug channel delivery and cross-channel reply routing: `docs/guides/debug-channel.md`
|
|
31
33
|
- Follow secret, access, and tool safety rules: `docs/guides/security-rules.md`
|
|
32
34
|
|
|
33
35
|
Current local commands:
|
package/package.json
CHANGED
|
@@ -18,11 +18,15 @@ type CloudChannel = {
|
|
|
18
18
|
type: "website" | "telegram" | "whatsapp" | "discord" | "slack" | "webhook";
|
|
19
19
|
provider: "agentkit" | "telegram" | "zapster" | "meta" | "uazapi" | "evolution" | "discord" | "slack" | "generic";
|
|
20
20
|
mode?: "interactions" | "bot";
|
|
21
|
+
direction?: "inbound" | "outbound";
|
|
21
22
|
status: string;
|
|
22
23
|
webhook_url: string;
|
|
23
24
|
required_secrets: Array<{ name: string; status: string }>;
|
|
24
25
|
buffer?: AgentChannel["buffer"];
|
|
25
26
|
audio?: AgentChannel["audio"];
|
|
27
|
+
reply_to?: Extract<AgentChannel, { replyTo?: unknown }>["replyTo"];
|
|
28
|
+
url_secret?: string;
|
|
29
|
+
auth?: Extract<AgentChannel, { type: "webhook"; direction: "outbound" }>["auth"];
|
|
26
30
|
};
|
|
27
31
|
|
|
28
32
|
type CloudDelivery = {
|
|
@@ -247,6 +251,12 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
|
|
|
247
251
|
}
|
|
248
252
|
|
|
249
253
|
if (channel.type === "webhook") {
|
|
254
|
+
if (channel.direction === "outbound") {
|
|
255
|
+
console.log("Outbound webhook: configured");
|
|
256
|
+
console.log(`Next human step: make sure ${channel.url_secret ?? "the webhook URL secret"} is set as a managed secret.`);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
250
260
|
const smoke = await runConnectSmoke(() => runChannelSmoke(channel, args.flags.api, args.flags.message));
|
|
251
261
|
printGenericWebhookHumanNextStep(channel);
|
|
252
262
|
if (!smoke) {
|
|
@@ -318,7 +328,11 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
|
|
|
318
328
|
} else if (channel.type === "slack") {
|
|
319
329
|
printSlackHumanNextStep(channel);
|
|
320
330
|
} else if (channel.type === "webhook") {
|
|
321
|
-
|
|
331
|
+
if (channel.direction === "outbound") {
|
|
332
|
+
console.log("Outbound webhook endpoint: configured from managed secrets.");
|
|
333
|
+
} else {
|
|
334
|
+
printGenericWebhookHumanNextStep(channel);
|
|
335
|
+
}
|
|
322
336
|
} else {
|
|
323
337
|
console.log(`Website endpoint: ${channel.webhook_url}`);
|
|
324
338
|
console.log("Use this endpoint from the AgentKit website client or a signed server-to-server test.");
|
|
@@ -361,6 +375,9 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
|
|
|
361
375
|
}
|
|
362
376
|
|
|
363
377
|
const channel = await resolveCloudChannelByName(first, args.flags.api);
|
|
378
|
+
if (channel.type === "webhook" && channel.direction === "outbound") {
|
|
379
|
+
throw new Error("Outbound webhook channels do not have an inbound test endpoint. Test them through a source channel that routes replies to this channel.");
|
|
380
|
+
}
|
|
364
381
|
const apiUrl = parseCloudApiUrl(args.flags.api);
|
|
365
382
|
const signed = channel.type === "whatsapp" && channel.provider === "evolution" && !args.flags.fixture
|
|
366
383
|
? await runServerGeneratedChannelTest(channel, apiUrl, args.flags.message)
|
|
@@ -511,6 +528,7 @@ async function createHostedChannel(
|
|
|
511
528
|
...(localChannel?.limits ? { limits: channelLimitsForApi(localChannel.limits) } : {}),
|
|
512
529
|
...(localChannel?.buffer ? { buffer: localChannel.buffer } : {}),
|
|
513
530
|
...(localChannel?.audio ? { audio: localChannel.audio } : {}),
|
|
531
|
+
...channelExtraConfigForApi(localChannel),
|
|
514
532
|
};
|
|
515
533
|
|
|
516
534
|
return cloudPost<{ channel: CloudChannel }>(
|
|
@@ -890,6 +908,9 @@ function printChannelStatus(channel: CloudChannel): void {
|
|
|
890
908
|
if (channel.type === "discord") {
|
|
891
909
|
console.log(`Mode: ${channel.mode ?? "interactions"}`);
|
|
892
910
|
}
|
|
911
|
+
if (channel.type === "webhook" && channel.direction) {
|
|
912
|
+
console.log(`Direction: ${channel.direction}`);
|
|
913
|
+
}
|
|
893
914
|
console.log(`Status: ${channel.status}`);
|
|
894
915
|
if (channel.webhook_url) {
|
|
895
916
|
console.log(`Webhook URL: ${channel.webhook_url}`);
|
|
@@ -1230,6 +1251,19 @@ function defaultChannelSecretsForCli(type: string, provider: string, mode?: "int
|
|
|
1230
1251
|
return [];
|
|
1231
1252
|
}
|
|
1232
1253
|
|
|
1254
|
+
function channelExtraConfigForApi(channel: AgentChannel | null): Record<string, unknown> {
|
|
1255
|
+
if (!channel) {
|
|
1256
|
+
return {};
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
return {
|
|
1260
|
+
...("direction" in channel && channel.direction ? { direction: channel.direction } : {}),
|
|
1261
|
+
...(channel.replyTo ? { reply_to: channel.replyTo } : {}),
|
|
1262
|
+
...("urlSecret" in channel && channel.urlSecret ? { url_secret: channel.urlSecret } : {}),
|
|
1263
|
+
...("auth" in channel && channel.auth ? { auth: channel.auth } : {}),
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1233
1267
|
async function readLocalChannelConfig(
|
|
1234
1268
|
cwd: string,
|
|
1235
1269
|
type: string,
|