@andreprado/agentkit 0.1.0-alpha.23 → 0.1.0-alpha.25

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/src/index.ts CHANGED
@@ -9,6 +9,7 @@ export type DiscordChannelMode = "interactions" | "bot";
9
9
  export type TelegramAllowedUpdate = "message";
10
10
  export type ChannelAudioMode = "off" | "reject" | "transcribe";
11
11
  export type TranscriptionProviderName = "test" | "openai" | "groq";
12
+ export type GenericWebhookDirection = "inbound" | "outbound";
12
13
 
13
14
  export type AgentProvider = {
14
15
  name: AgentProviderName;
@@ -66,6 +67,37 @@ export type ChannelAudioConfig =
66
67
  maxBytes?: number;
67
68
  };
68
69
 
70
+ export type ChannelReplyRouteInput = {
71
+ channel: string;
72
+ recipientFrom?: string;
73
+ };
74
+
75
+ export type ChannelReplyRouteConfig = {
76
+ channel: string;
77
+ recipientFrom?: string;
78
+ };
79
+
80
+ export type GenericWebhookOutputAuthInput =
81
+ | {
82
+ type: "none";
83
+ }
84
+ | {
85
+ type: "bearer";
86
+ tokenSecret: string;
87
+ }
88
+ | {
89
+ type: "header";
90
+ headerName: string;
91
+ valueSecret: string;
92
+ }
93
+ | {
94
+ type: "hmac";
95
+ secret: string;
96
+ headerName?: string;
97
+ };
98
+
99
+ export type GenericWebhookOutputAuthConfig = GenericWebhookOutputAuthInput;
100
+
69
101
  export type AgentTranscriptionLimits = {
70
102
  maxDurationSeconds?: number;
71
103
  maxBytes?: number;
@@ -92,6 +124,7 @@ export type WebsiteChannelInput = {
92
124
  limits?: ChannelLimits;
93
125
  buffer?: ChannelBufferInput;
94
126
  audio?: ChannelAudioInput;
127
+ replyTo?: ChannelReplyRouteInput;
95
128
  };
96
129
 
97
130
  export type WebsiteChannelConfig = {
@@ -103,6 +136,7 @@ export type WebsiteChannelConfig = {
103
136
  limits?: ChannelLimits;
104
137
  buffer?: ChannelBufferConfig;
105
138
  audio?: ChannelAudioConfig;
139
+ replyTo?: ChannelReplyRouteConfig;
106
140
  };
107
141
 
108
142
  export type TelegramChannelInput = {
@@ -112,6 +146,7 @@ export type TelegramChannelInput = {
112
146
  limits?: ChannelLimits;
113
147
  buffer?: ChannelBufferInput;
114
148
  audio?: ChannelAudioInput;
149
+ replyTo?: ChannelReplyRouteInput;
115
150
  };
116
151
 
117
152
  export type TelegramChannelConfig = {
@@ -123,6 +158,7 @@ export type TelegramChannelConfig = {
123
158
  limits?: ChannelLimits;
124
159
  buffer?: ChannelBufferConfig;
125
160
  audio?: ChannelAudioConfig;
161
+ replyTo?: ChannelReplyRouteConfig;
126
162
  };
127
163
 
128
164
  export type WhatsappChannelInput = {
@@ -132,6 +168,7 @@ export type WhatsappChannelInput = {
132
168
  limits?: ChannelLimits;
133
169
  buffer?: ChannelBufferInput;
134
170
  audio?: ChannelAudioInput;
171
+ replyTo?: ChannelReplyRouteInput;
135
172
  };
136
173
 
137
174
  export type WhatsappChannelConfig = {
@@ -142,6 +179,7 @@ export type WhatsappChannelConfig = {
142
179
  limits?: ChannelLimits;
143
180
  buffer?: ChannelBufferConfig;
144
181
  audio?: ChannelAudioConfig;
182
+ replyTo?: ChannelReplyRouteConfig;
145
183
  };
146
184
 
147
185
  export type DiscordChannelInput = {
@@ -150,6 +188,7 @@ export type DiscordChannelInput = {
150
188
  secrets?: string[];
151
189
  limits?: ChannelLimits;
152
190
  buffer?: ChannelBufferInput;
191
+ replyTo?: ChannelReplyRouteInput;
153
192
  };
154
193
 
155
194
  export type DiscordChannelConfig = {
@@ -160,6 +199,7 @@ export type DiscordChannelConfig = {
160
199
  secrets: string[];
161
200
  limits?: ChannelLimits;
162
201
  buffer?: ChannelBufferConfig;
202
+ replyTo?: ChannelReplyRouteConfig;
163
203
  audio?: never;
164
204
  };
165
205
 
@@ -168,6 +208,7 @@ export type SlackChannelInput = {
168
208
  secrets?: string[];
169
209
  limits?: ChannelLimits;
170
210
  buffer?: ChannelBufferInput;
211
+ replyTo?: ChannelReplyRouteInput;
171
212
  };
172
213
 
173
214
  export type SlackChannelConfig = {
@@ -177,6 +218,7 @@ export type SlackChannelConfig = {
177
218
  secrets: string[];
178
219
  limits?: ChannelLimits;
179
220
  buffer?: ChannelBufferConfig;
221
+ replyTo?: ChannelReplyRouteConfig;
180
222
  audio?: never;
181
223
  };
182
224
 
@@ -185,15 +227,42 @@ export type GenericWebhookChannelInput = {
185
227
  secrets?: string[];
186
228
  limits?: ChannelLimits;
187
229
  buffer?: ChannelBufferInput;
230
+ replyTo?: ChannelReplyRouteInput;
188
231
  };
189
232
 
190
233
  export type GenericWebhookChannelConfig = {
191
234
  type: "webhook";
192
235
  provider: "generic";
236
+ direction: "inbound";
193
237
  name: string;
194
238
  secrets: string[];
195
239
  limits?: ChannelLimits;
196
240
  buffer?: ChannelBufferConfig;
241
+ replyTo?: ChannelReplyRouteConfig;
242
+ urlSecret?: never;
243
+ auth?: never;
244
+ audio?: never;
245
+ };
246
+
247
+ export type GenericWebhookOutputChannelInput = {
248
+ name: string;
249
+ urlSecret: string;
250
+ auth?: GenericWebhookOutputAuthInput;
251
+ secrets?: string[];
252
+ limits?: ChannelLimits;
253
+ };
254
+
255
+ export type GenericWebhookOutputChannelConfig = {
256
+ type: "webhook";
257
+ provider: "generic";
258
+ direction: "outbound";
259
+ name: string;
260
+ urlSecret: string;
261
+ auth: GenericWebhookOutputAuthConfig;
262
+ secrets: string[];
263
+ limits?: ChannelLimits;
264
+ buffer?: never;
265
+ replyTo?: never;
197
266
  audio?: never;
198
267
  };
199
268
 
@@ -203,7 +272,8 @@ export type AgentChannel =
203
272
  | WhatsappChannelConfig
204
273
  | DiscordChannelConfig
205
274
  | SlackChannelConfig
206
- | GenericWebhookChannelConfig;
275
+ | GenericWebhookChannelConfig
276
+ | GenericWebhookOutputChannelConfig;
207
277
 
208
278
  export type IntegrationTagsConfig = {
209
279
  enable?: string[];
@@ -454,6 +524,7 @@ export function websiteChannel(input: WebsiteChannelInput): WebsiteChannelConfig
454
524
  ...(input.limits ? { limits: input.limits } : {}),
455
525
  ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "website channel") } : {}),
456
526
  ...(input.audio ? { audio: normalizeChannelAudio(input.audio, "website channel") } : {}),
527
+ ...(input.replyTo ? { replyTo: normalizeChannelReplyRoute(input.replyTo, "website channel") } : {}),
457
528
  };
458
529
  }
459
530
 
@@ -470,6 +541,7 @@ export function telegramChannel(input: TelegramChannelInput): TelegramChannelCon
470
541
  ...(input.limits ? { limits: input.limits } : {}),
471
542
  ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "telegram channel") } : {}),
472
543
  ...(input.audio ? { audio: normalizeChannelAudio(input.audio, "telegram channel") } : {}),
544
+ ...(input.replyTo ? { replyTo: normalizeChannelReplyRoute(input.replyTo, "telegram channel") } : {}),
473
545
  };
474
546
  }
475
547
 
@@ -485,6 +557,7 @@ export function whatsappChannel(input: WhatsappChannelInput): WhatsappChannelCon
485
557
  ...(input.limits ? { limits: input.limits } : {}),
486
558
  ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, `${input.provider} whatsapp channel`) } : {}),
487
559
  ...(input.audio ? { audio: normalizeChannelAudio(input.audio, `${input.provider} whatsapp channel`) } : {}),
560
+ ...(input.replyTo ? { replyTo: normalizeChannelReplyRoute(input.replyTo, `${input.provider} whatsapp channel`) } : {}),
488
561
  };
489
562
  }
490
563
 
@@ -499,6 +572,7 @@ export function discordChannel(input: DiscordChannelInput): DiscordChannelConfig
499
572
  secrets: normalizeChannelSecrets(input.secrets ?? defaultDiscordChannelSecrets(mode), "discord channel"),
500
573
  ...(input.limits ? { limits: input.limits } : {}),
501
574
  ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "discord channel") } : {}),
575
+ ...(input.replyTo ? { replyTo: normalizeChannelReplyRoute(input.replyTo, "discord channel") } : {}),
502
576
  };
503
577
  }
504
578
 
@@ -510,6 +584,7 @@ export function slackChannel(input: SlackChannelInput): SlackChannelConfig {
510
584
  secrets: normalizeChannelSecrets(input.secrets ?? ["SLACK_BOT_TOKEN", "SLACK_SIGNING_SECRET"], "slack channel"),
511
585
  ...(input.limits ? { limits: input.limits } : {}),
512
586
  ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "slack channel") } : {}),
587
+ ...(input.replyTo ? { replyTo: normalizeChannelReplyRoute(input.replyTo, "slack channel") } : {}),
513
588
  };
514
589
  }
515
590
 
@@ -517,10 +592,32 @@ export function webhookChannel(input: GenericWebhookChannelInput): GenericWebhoo
517
592
  return {
518
593
  type: "webhook",
519
594
  provider: "generic",
595
+ direction: "inbound",
520
596
  name: input.name,
521
597
  secrets: normalizeChannelSecrets(input.secrets ?? ["AGENTKIT_WEBHOOK_SECRET"], "generic webhook channel"),
522
598
  ...(input.limits ? { limits: input.limits } : {}),
523
599
  ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "generic webhook channel") } : {}),
600
+ ...(input.replyTo ? { replyTo: normalizeChannelReplyRoute(input.replyTo, "generic webhook channel") } : {}),
601
+ };
602
+ }
603
+
604
+ export function webhookOutputChannel(input: GenericWebhookOutputChannelInput): GenericWebhookOutputChannelConfig {
605
+ const auth = normalizeGenericWebhookOutputAuth(input.auth ?? { type: "none" }, "generic webhook output channel");
606
+ const requiredSecrets = [
607
+ input.urlSecret,
608
+ ...genericWebhookOutputAuthSecrets(auth),
609
+ ...(input.secrets ?? []),
610
+ ];
611
+
612
+ return {
613
+ type: "webhook",
614
+ provider: "generic",
615
+ direction: "outbound",
616
+ name: input.name,
617
+ urlSecret: normalizeSecretName(input.urlSecret, "generic webhook output channel urlSecret"),
618
+ auth,
619
+ secrets: normalizeChannelSecrets(requiredSecrets, "generic webhook output channel"),
620
+ ...(input.limits ? { limits: input.limits } : {}),
524
621
  };
525
622
  }
526
623
 
@@ -548,14 +645,22 @@ function normalizeChannelSecrets(secrets: string[], context: string): string[] {
548
645
  const uniqueSecrets = new Set<string>();
549
646
 
550
647
  for (const secret of secrets) {
551
- if (!/^[A-Z_][A-Z0-9_]*$/.test(secret)) {
552
- throw new Error(`${context} secrets must be environment variable names, not secret values.`);
648
+ uniqueSecrets.add(normalizeSecretName(secret, `${context} secrets`));
649
+ }
650
+
651
+ return Array.from(uniqueSecrets).sort();
652
+ }
653
+
654
+ function normalizeSecretName(secret: string, context: string): string {
655
+ if (!/^[A-Z_][A-Z0-9_]*$/.test(secret)) {
656
+ if (context.endsWith("secrets")) {
657
+ throw new Error(`${context} must be environment variable names, not secret values.`);
553
658
  }
554
659
 
555
- uniqueSecrets.add(secret);
660
+ throw new Error(`${context} must be an environment variable name, not a secret value.`);
556
661
  }
557
662
 
558
- return Array.from(uniqueSecrets).sort();
663
+ return secret;
559
664
  }
560
665
 
561
666
  function normalizeIntegrationName(value: string, context: string): string {
@@ -717,6 +822,87 @@ function normalizeChannelAudio(input: ChannelAudioInput, context: string): Chann
717
822
  };
718
823
  }
719
824
 
825
+ function normalizeChannelReplyRoute(input: ChannelReplyRouteInput, context: string): ChannelReplyRouteConfig {
826
+ const channel = input.channel.trim();
827
+
828
+ if (!/^[a-z][a-z0-9-]*$/.test(channel)) {
829
+ throw new Error(`${context} replyTo.channel must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens.`);
830
+ }
831
+
832
+ return {
833
+ channel,
834
+ ...(input.recipientFrom ? { recipientFrom: normalizeJsonPath(input.recipientFrom, `${context} replyTo.recipientFrom`) } : {}),
835
+ };
836
+ }
837
+
838
+ function normalizeJsonPath(value: string, context: string): string {
839
+ const path = value.trim().replace(/^payload\./, "");
840
+
841
+ if (!/^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(path)) {
842
+ throw new Error(`${context} must be a dotted JSON field path such as "phone" or "customer.phone".`);
843
+ }
844
+
845
+ return path;
846
+ }
847
+
848
+ function normalizeGenericWebhookOutputAuth(
849
+ auth: GenericWebhookOutputAuthInput,
850
+ context: string,
851
+ ): GenericWebhookOutputAuthConfig {
852
+ if (auth.type === "none") {
853
+ return { type: "none" };
854
+ }
855
+
856
+ if (auth.type === "bearer") {
857
+ return {
858
+ type: "bearer",
859
+ tokenSecret: normalizeSecretName(auth.tokenSecret, `${context} auth.tokenSecret`),
860
+ };
861
+ }
862
+
863
+ if (auth.type === "header") {
864
+ if (!/^[A-Za-z][A-Za-z0-9-]*$/.test(auth.headerName)) {
865
+ throw new Error(`${context} auth.headerName must be an HTTP header name.`);
866
+ }
867
+
868
+ return {
869
+ type: "header",
870
+ headerName: auth.headerName,
871
+ valueSecret: normalizeSecretName(auth.valueSecret, `${context} auth.valueSecret`),
872
+ };
873
+ }
874
+
875
+ if (auth.type === "hmac") {
876
+ if (auth.headerName !== undefined && !/^[A-Za-z][A-Za-z0-9-]*$/.test(auth.headerName)) {
877
+ throw new Error(`${context} auth.headerName must be an HTTP header name.`);
878
+ }
879
+
880
+ return {
881
+ type: "hmac",
882
+ secret: normalizeSecretName(auth.secret, `${context} auth.secret`),
883
+ ...(auth.headerName ? { headerName: auth.headerName } : {}),
884
+ };
885
+ }
886
+
887
+ throw new Error(`${context} auth.type must be "none", "bearer", "header", or "hmac".`);
888
+ }
889
+
890
+ function genericWebhookOutputAuthSecrets(auth: GenericWebhookOutputAuthConfig): string[] {
891
+ if (auth.type === "bearer") {
892
+ return [auth.tokenSecret];
893
+ }
894
+
895
+ if (auth.type === "header") {
896
+ return [auth.valueSecret];
897
+ }
898
+
899
+ if (auth.type === "hmac") {
900
+ return [auth.secret];
901
+ }
902
+
903
+ return [];
904
+ }
905
+
720
906
  function validatePositiveInteger(value: number, field: string): void {
721
907
  if (!Number.isInteger(value) || value <= 0) {
722
908
  throw new Error(`${field} must be a positive integer.`);