@andreprado/agentkit 0.1.0-alpha.15 → 0.1.0-alpha.17

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 (41) hide show
  1. package/README.md +8 -0
  2. package/docs/guides/add-channel.md +65 -0
  3. package/docs/guides/add-managed-composio.md +137 -0
  4. package/docs/guides/channel-security.md +32 -0
  5. package/docs/guides/channels-production-handoff.md +1 -1
  6. package/docs/guides/connect-telegram.md +60 -0
  7. package/docs/guides/connect-whatsapp-zapster.md +65 -0
  8. package/docs/guides/prepare-deploy.md +3 -1
  9. package/docs/llms-full.txt +13 -1
  10. package/docs/llms.txt +7 -0
  11. package/package.json +1 -1
  12. package/src/cli/commands/channels.ts +121 -7
  13. package/src/cli/commands/transcribe.ts +171 -0
  14. package/src/cli/deploy-readiness.ts +59 -3
  15. package/src/cli/help.ts +15 -0
  16. package/src/cli/index.ts +130 -0
  17. package/src/create-project.ts +4 -32
  18. package/src/index.ts +231 -0
  19. package/src/runtime/channel-test-harness.ts +2 -0
  20. package/src/runtime/channels/telegram.ts +326 -10
  21. package/src/runtime/channels/whatsapp-zapster.ts +319 -0
  22. package/src/runtime/channels.ts +47 -1
  23. package/src/runtime/chat.ts +2 -1
  24. package/src/runtime/config.ts +214 -4
  25. package/src/runtime/core/manifest.ts +61 -2
  26. package/src/runtime/deploy-readiness.ts +31 -1
  27. package/src/runtime/dev-server.ts +72 -4
  28. package/src/runtime/inspect.ts +142 -4
  29. package/src/runtime/integrations/composio.ts +257 -0
  30. package/src/runtime/skills.ts +95 -0
  31. package/src/runtime/targets/cloudflare/build.ts +162 -5
  32. package/src/runtime/tool-runner.ts +2 -1
  33. package/src/runtime/transcription.ts +483 -0
  34. package/src/templates/skills/agentkit-capsule/SKILL.md +1 -0
  35. package/src/templates/skills/agentkit-channels/SKILL.md +36 -1
  36. package/src/templates/skills/agentkit-channels/references/channel-debugging.md +16 -1
  37. package/src/templates/skills/agentkit-channels/references/telegram.md +34 -0
  38. package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +29 -0
  39. package/src/templates/skills/agentkit-deploy/SKILL.md +1 -1
  40. package/src/templates/skills/agentkit-integrations/SKILL.md +66 -0
  41. package/src/templates/skills/agentkit-troubleshooting/SKILL.md +3 -2
package/src/cli/index.ts CHANGED
@@ -22,6 +22,7 @@ import { runEvalsFromCwd, writeEvalFromConversation } from "../runtime/evals";
22
22
  import { findAgentKitDocs } from "../runtime/docs";
23
23
  import { listCapsuleEnv, setCapsuleEnv, unsetCapsuleEnv } from "../runtime/env";
24
24
  import { checkAgentSpec, initializeAgentSpec } from "../runtime/spec";
25
+ import { checkDefaultSkillStatus, syncDefaultSkillFiles } from "../runtime/skills";
25
26
  import { initializeSyncScaffold, runCapsuleSync } from "../runtime/sync";
26
27
  import { runToolFromCwd } from "../runtime/tool-runner";
27
28
  import { startAgentDevServer } from "../runtime/dev-server";
@@ -67,6 +68,7 @@ import { renderCommandHelp, renderCommandReference, renderHelp } from "./help";
67
68
  import { runInheritedCommand, waitForShutdown } from "./process";
68
69
  import { handleChannelsCommand } from "./commands/channels";
69
70
  import { handleKnowledgeCommand } from "./commands/knowledge";
71
+ import { handleTranscribeCommand } from "./commands/transcribe";
70
72
 
71
73
  async function main() {
72
74
  const args = parseArgs(process.argv.slice(2));
@@ -764,6 +766,110 @@ async function main() {
764
766
  return;
765
767
  }
766
768
 
769
+ if (args.command === "transcribe") {
770
+ await handleTranscribeCommand(args);
771
+ return;
772
+ }
773
+
774
+ if (args.command === "skills") {
775
+ const [subcommand] = args.positional;
776
+ const root = await findAgentCapsuleRoot(process.cwd());
777
+
778
+ if (subcommand === "status") {
779
+ const status = await checkDefaultSkillStatus(root);
780
+ printSkillStatus(status);
781
+ if (status.status !== "current") {
782
+ process.exitCode = 1;
783
+ }
784
+ return;
785
+ }
786
+
787
+ if (subcommand === "sync") {
788
+ const result = await syncDefaultSkillFiles(root);
789
+ console.log(`Synced ${result.files.length} AgentKit skill files.`);
790
+ console.log("Status: current");
791
+ return;
792
+ }
793
+
794
+ throw new Error("Usage: agentkit skills status | agentkit skills sync");
795
+ }
796
+
797
+ if (args.command === "integrations") {
798
+ const [subcommand, provider] = args.positional;
799
+
800
+ if (provider !== undefined && provider !== "composio") {
801
+ throw new Error("Usage: agentkit integrations status | connect composio [--toolkit <slug>] [--callback-url <url>] [--api <url>]");
802
+ }
803
+
804
+ const state = await requireLocalDeployState();
805
+ const apiUrl = await resolveCloudApiUrl(args.flags.api);
806
+
807
+ if (subcommand === "status") {
808
+ const payload = await cloudApiRequest(
809
+ apiUrl,
810
+ `/v1/deploys/${encodeURIComponent(state.deploy_id)}/integrations/composio/status`,
811
+ { method: "GET" },
812
+ );
813
+ printJson(payload);
814
+ return;
815
+ }
816
+
817
+ if (subcommand === "connect" && provider === "composio") {
818
+ const toolkit = parseOptionalStringFlag(
819
+ args.flags.toolkit,
820
+ "toolkit",
821
+ "agentkit integrations connect composio --toolkit <slug>",
822
+ );
823
+ const callbackUrl = parseOptionalStringFlag(
824
+ args.flags["callback-url"],
825
+ "callback-url",
826
+ "agentkit integrations connect composio --callback-url <url>",
827
+ );
828
+ const payload = (await cloudApiRequest(
829
+ apiUrl,
830
+ `/v1/deploys/${encodeURIComponent(state.deploy_id)}/integrations/composio/connect-link`,
831
+ {
832
+ method: "POST",
833
+ body: JSON.stringify({
834
+ ...(toolkit ? { toolkit } : {}),
835
+ ...(callbackUrl ? { callback_url: callbackUrl } : {}),
836
+ }),
837
+ },
838
+ )) as {
839
+ connect_link?: {
840
+ toolkit?: string;
841
+ user_id?: string;
842
+ redirect_url?: string;
843
+ expires_at?: string;
844
+ connected_account_id?: string;
845
+ };
846
+ };
847
+
848
+ const link = payload.connect_link;
849
+
850
+ if (!link?.redirect_url) {
851
+ throw new AgentKitError("integration_response_invalid", "AgentKit Cloud returned an invalid Composio connect-link payload.");
852
+ }
853
+
854
+ console.log("Composio connect link");
855
+ console.log(`Toolkit: ${link.toolkit ?? toolkit ?? "unknown"}`);
856
+ console.log(`User: ${link.user_id ?? "unknown"}`);
857
+ console.log(`URL: ${link.redirect_url}`);
858
+
859
+ if (link.expires_at) {
860
+ console.log(`Expires: ${link.expires_at}`);
861
+ }
862
+
863
+ if (link.connected_account_id) {
864
+ console.log(`Account: ${link.connected_account_id}`);
865
+ }
866
+
867
+ return;
868
+ }
869
+
870
+ throw new Error("Usage: agentkit integrations status | connect composio [--toolkit <slug>] [--callback-url <url>] [--api <url>]");
871
+ }
872
+
767
873
  if (args.command === "env") {
768
874
  const [subcommand, name, ...valueParts] = args.positional;
769
875
  const root = await findAgentCapsuleRoot(process.cwd());
@@ -1078,6 +1184,30 @@ function printJson(value: unknown): void {
1078
1184
  console.log(JSON.stringify(value, null, 2));
1079
1185
  }
1080
1186
 
1187
+ function printSkillStatus(status: Awaited<ReturnType<typeof checkDefaultSkillStatus>>): void {
1188
+ console.log("AgentKit skills");
1189
+ console.log(`Status: ${status.status}`);
1190
+ console.log(`Checked: ${status.checked}`);
1191
+
1192
+ if (status.missing.length > 0) {
1193
+ console.log(`Missing: ${status.missing.length}`);
1194
+ for (const file of status.missing.slice(0, 10)) {
1195
+ console.log(` ${file}`);
1196
+ }
1197
+ }
1198
+
1199
+ if (status.outdated.length > 0) {
1200
+ console.log(`Outdated: ${status.outdated.length}`);
1201
+ for (const file of status.outdated.slice(0, 10)) {
1202
+ console.log(` ${file}`);
1203
+ }
1204
+ }
1205
+
1206
+ if (status.status !== "current") {
1207
+ console.log("Run: npm run agentkit -- skills sync");
1208
+ }
1209
+ }
1210
+
1081
1211
  async function printJsonOrWrite(value: unknown, outFlag: string | boolean | undefined, usage: string): Promise<void> {
1082
1212
  const outPath = parseOptionalStringFlag(outFlag, "out", usage);
1083
1213
 
@@ -1,10 +1,11 @@
1
1
  import { execFile } from "node:child_process";
2
- import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
3
- import { basename, dirname, join, relative, resolve } from "node:path";
2
+ import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
3
+ import { basename, dirname, join, resolve } from "node:path";
4
4
  import { promisify } from "node:util";
5
5
  import { fileURLToPath } from "node:url";
6
6
 
7
- import { getTemplate, type TemplateFile } from "./templates";
7
+ import { loadDefaultSkillFiles } from "./runtime/skills";
8
+ import { getTemplate } from "./templates";
8
9
 
9
10
  const execFileAsync = promisify(execFile);
10
11
  const moduleDir = dirname(fileURLToPath(import.meta.url));
@@ -129,35 +130,6 @@ async function resolveAgentKitDependency(): Promise<string> {
129
130
  return packageJson.version;
130
131
  }
131
132
 
132
- async function loadDefaultSkillFiles(): Promise<TemplateFile[]> {
133
- const sourceRoot = join(moduleDir, "templates", "skills");
134
- const sourceFiles = await listFiles(sourceRoot);
135
-
136
- return Promise.all(
137
- sourceFiles.map(async (sourcePath) => ({
138
- path: join("skills", relative(sourceRoot, sourcePath)).replaceAll("\\", "/"),
139
- contents: await readFile(sourcePath, "utf8"),
140
- })),
141
- );
142
- }
143
-
144
- async function listFiles(directory: string): Promise<string[]> {
145
- const entries = await readdir(directory, { withFileTypes: true });
146
- const files = await Promise.all(
147
- entries.map(async (entry) => {
148
- const path = join(directory, entry.name);
149
-
150
- if (entry.isDirectory()) {
151
- return listFiles(path);
152
- }
153
-
154
- return entry.isFile() ? [path] : [];
155
- }),
156
- );
157
-
158
- return files.flat().sort();
159
- }
160
-
161
133
  async function directoryExists(path: string): Promise<boolean> {
162
134
  try {
163
135
  const result = await stat(path);
package/src/index.ts CHANGED
@@ -6,6 +6,8 @@ export type ChannelType = "website" | "telegram" | "whatsapp";
6
6
  export type ChannelProvider = "agentkit" | "telegram" | "zapster" | "meta";
7
7
  export type WhatsappChannelProvider = "zapster" | "meta";
8
8
  export type TelegramAllowedUpdate = "message";
9
+ export type ChannelAudioMode = "off" | "reject" | "transcribe";
10
+ export type TranscriptionProviderName = "test" | "openai" | "groq";
9
11
 
10
12
  export type AgentProvider = {
11
13
  name: AgentProviderName;
@@ -39,6 +41,45 @@ export type ChannelBufferConfig =
39
41
  maxChars: number;
40
42
  };
41
43
 
44
+ export type ChannelAudioInput = {
45
+ mode?: ChannelAudioMode;
46
+ fallbackMessage?: string;
47
+ maxDurationSeconds?: number;
48
+ maxBytes?: number;
49
+ };
50
+
51
+ export type ChannelAudioConfig =
52
+ | {
53
+ mode: "off";
54
+ }
55
+ | {
56
+ mode: "reject";
57
+ fallbackMessage: string;
58
+ maxDurationSeconds?: number;
59
+ maxBytes?: number;
60
+ }
61
+ | {
62
+ mode: "transcribe";
63
+ fallbackMessage: string;
64
+ maxDurationSeconds?: number;
65
+ maxBytes?: number;
66
+ };
67
+
68
+ export type AgentTranscriptionLimits = {
69
+ maxDurationSeconds?: number;
70
+ maxBytes?: number;
71
+ };
72
+
73
+ export type AgentTranscriptionConfig = {
74
+ provider: TranscriptionProviderName;
75
+ model: string;
76
+ secret?: string;
77
+ language?: string;
78
+ prompt?: string;
79
+ limits?: AgentTranscriptionLimits;
80
+ rawAudioTtlSeconds?: number;
81
+ };
82
+
42
83
  export type WebsiteChannelAccess = {
43
84
  mode: "token";
44
85
  };
@@ -49,6 +90,7 @@ export type WebsiteChannelInput = {
49
90
  secrets?: string[];
50
91
  limits?: ChannelLimits;
51
92
  buffer?: ChannelBufferInput;
93
+ audio?: ChannelAudioInput;
52
94
  };
53
95
 
54
96
  export type WebsiteChannelConfig = {
@@ -59,6 +101,7 @@ export type WebsiteChannelConfig = {
59
101
  secrets: string[];
60
102
  limits?: ChannelLimits;
61
103
  buffer?: ChannelBufferConfig;
104
+ audio?: ChannelAudioConfig;
62
105
  };
63
106
 
64
107
  export type TelegramChannelInput = {
@@ -67,6 +110,7 @@ export type TelegramChannelInput = {
67
110
  allowedUpdates?: TelegramAllowedUpdate[];
68
111
  limits?: ChannelLimits;
69
112
  buffer?: ChannelBufferInput;
113
+ audio?: ChannelAudioInput;
70
114
  };
71
115
 
72
116
  export type TelegramChannelConfig = {
@@ -77,6 +121,7 @@ export type TelegramChannelConfig = {
77
121
  allowedUpdates: TelegramAllowedUpdate[];
78
122
  limits?: ChannelLimits;
79
123
  buffer?: ChannelBufferConfig;
124
+ audio?: ChannelAudioConfig;
80
125
  };
81
126
 
82
127
  export type WhatsappChannelInput = {
@@ -85,6 +130,7 @@ export type WhatsappChannelInput = {
85
130
  secrets?: string[];
86
131
  limits?: ChannelLimits;
87
132
  buffer?: ChannelBufferInput;
133
+ audio?: ChannelAudioInput;
88
134
  };
89
135
 
90
136
  export type WhatsappChannelConfig = {
@@ -94,10 +140,35 @@ export type WhatsappChannelConfig = {
94
140
  secrets: string[];
95
141
  limits?: ChannelLimits;
96
142
  buffer?: ChannelBufferConfig;
143
+ audio?: ChannelAudioConfig;
97
144
  };
98
145
 
99
146
  export type AgentChannel = WebsiteChannelConfig | TelegramChannelConfig | WhatsappChannelConfig;
100
147
 
148
+ export type IntegrationTagsConfig = {
149
+ enable?: string[];
150
+ disable?: string[];
151
+ };
152
+
153
+ export type ComposioManagedIntegrationInput = {
154
+ name?: string;
155
+ toolkits: string[];
156
+ tools?: Record<string, string[]>;
157
+ tags?: IntegrationTagsConfig;
158
+ };
159
+
160
+ export type ComposioManagedIntegration = {
161
+ type: "composio";
162
+ provider: "composio";
163
+ mode: "managed";
164
+ name: string;
165
+ toolkits: string[];
166
+ tools?: Record<string, string[]>;
167
+ tags?: IntegrationTagsConfig;
168
+ };
169
+
170
+ export type AgentIntegration = ComposioManagedIntegration;
171
+
101
172
  export type AgentDatabaseConfig =
102
173
  | {
103
174
  driver: "none";
@@ -269,7 +340,9 @@ export type AgentConfig = {
269
340
  instructions: string;
270
341
  secrets: string[];
271
342
  tools?: AgentTool[];
343
+ integrations?: AgentIntegration[];
272
344
  channels?: AgentChannel[];
345
+ transcription?: AgentTranscriptionConfig;
273
346
  knowledge?: AgentKnowledgeConfig;
274
347
  access: {
275
348
  mode: AccessMode;
@@ -290,6 +363,23 @@ export function defineTool<Input = unknown, Output = unknown>(tool: AgentTool<In
290
363
  return tool;
291
364
  }
292
365
 
366
+ export function composioManaged(input: ComposioManagedIntegrationInput): ComposioManagedIntegration {
367
+ const name = normalizeIntegrationName(input.name ?? "composio", "composio integration name");
368
+ const toolkits = normalizeToolkitList(input.toolkits, "composioManaged.toolkits");
369
+ const tools = input.tools ? normalizeComposioToolMap(input.tools, toolkits) : undefined;
370
+ const tags = input.tags ? normalizeIntegrationTags(input.tags) : undefined;
371
+
372
+ return {
373
+ type: "composio",
374
+ provider: "composio",
375
+ mode: "managed",
376
+ name,
377
+ toolkits,
378
+ ...(tools ? { tools } : {}),
379
+ ...(tags ? { tags } : {}),
380
+ };
381
+ }
382
+
293
383
  export function websiteChannel(input: WebsiteChannelInput): WebsiteChannelConfig {
294
384
  return {
295
385
  type: "website",
@@ -299,6 +389,7 @@ export function websiteChannel(input: WebsiteChannelInput): WebsiteChannelConfig
299
389
  secrets: normalizeChannelSecrets(input.secrets ?? ["AGENTKIT_WEBSITE_CHANNEL_TOKEN"], "website channel"),
300
390
  ...(input.limits ? { limits: input.limits } : {}),
301
391
  ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "website channel") } : {}),
392
+ ...(input.audio ? { audio: normalizeChannelAudio(input.audio, "website channel") } : {}),
302
393
  };
303
394
  }
304
395
 
@@ -314,6 +405,7 @@ export function telegramChannel(input: TelegramChannelInput): TelegramChannelCon
314
405
  allowedUpdates: input.allowedUpdates ?? ["message"],
315
406
  ...(input.limits ? { limits: input.limits } : {}),
316
407
  ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "telegram channel") } : {}),
408
+ ...(input.audio ? { audio: normalizeChannelAudio(input.audio, "telegram channel") } : {}),
317
409
  };
318
410
  }
319
411
 
@@ -328,6 +420,7 @@ export function whatsappChannel(input: WhatsappChannelInput): WhatsappChannelCon
328
420
  ),
329
421
  ...(input.limits ? { limits: input.limits } : {}),
330
422
  ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, `${input.provider} whatsapp channel`) } : {}),
423
+ ...(input.audio ? { audio: normalizeChannelAudio(input.audio, `${input.provider} whatsapp channel`) } : {}),
331
424
  };
332
425
  }
333
426
 
@@ -353,6 +446,109 @@ function normalizeChannelSecrets(secrets: string[], context: string): string[] {
353
446
  return Array.from(uniqueSecrets).sort();
354
447
  }
355
448
 
449
+ function normalizeIntegrationName(value: string, context: string): string {
450
+ const name = value.trim();
451
+
452
+ if (!/^[a-z][a-z0-9-]*$/.test(name)) {
453
+ throw new Error(`${context} must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens.`);
454
+ }
455
+
456
+ return name;
457
+ }
458
+
459
+ function normalizeToolkitList(values: string[], context: string): string[] {
460
+ if (!Array.isArray(values) || values.length === 0) {
461
+ throw new Error(`${context} must include at least one toolkit slug.`);
462
+ }
463
+
464
+ const toolkits = new Set<string>();
465
+
466
+ for (const value of values) {
467
+ if (typeof value !== "string") {
468
+ throw new Error(`${context} entries must be toolkit slug strings.`);
469
+ }
470
+
471
+ const toolkit = value.trim().toLowerCase();
472
+
473
+ if (!/^[a-z][a-z0-9_-]*$/.test(toolkit)) {
474
+ throw new Error(`${context} entries must start with a letter and contain only letters, numbers, underscores, and hyphens.`);
475
+ }
476
+
477
+ toolkits.add(toolkit);
478
+ }
479
+
480
+ return Array.from(toolkits).sort();
481
+ }
482
+
483
+ function normalizeComposioToolMap(values: Record<string, string[]>, toolkits: string[]): Record<string, string[]> | undefined {
484
+ const toolkitSet = new Set(toolkits);
485
+ const normalized: Record<string, string[]> = {};
486
+
487
+ for (const [rawToolkit, rawTools] of Object.entries(values)) {
488
+ const toolkit = rawToolkit.trim().toLowerCase();
489
+
490
+ if (!toolkitSet.has(toolkit)) {
491
+ throw new Error(`composioManaged.tools.${rawToolkit} must reference a toolkit listed in composioManaged.toolkits.`);
492
+ }
493
+
494
+ if (!Array.isArray(rawTools) || rawTools.length === 0) {
495
+ throw new Error(`composioManaged.tools.${rawToolkit} must include at least one Composio action slug.`);
496
+ }
497
+
498
+ const tools = new Set<string>();
499
+
500
+ for (const rawTool of rawTools) {
501
+ if (typeof rawTool !== "string") {
502
+ throw new Error(`composioManaged.tools.${rawToolkit} entries must be Composio action slug strings.`);
503
+ }
504
+
505
+ const tool = rawTool.trim().toUpperCase();
506
+
507
+ if (!/^[A-Z][A-Z0-9_]*$/.test(tool)) {
508
+ throw new Error(`composioManaged.tools.${rawToolkit} entries must be uppercase Composio action slugs.`);
509
+ }
510
+
511
+ tools.add(tool);
512
+ }
513
+
514
+ normalized[toolkit] = Array.from(tools).sort();
515
+ }
516
+
517
+ return Object.keys(normalized).length > 0 ? normalized : undefined;
518
+ }
519
+
520
+ function normalizeIntegrationTags(tags: IntegrationTagsConfig): IntegrationTagsConfig {
521
+ const normalized: IntegrationTagsConfig = {};
522
+
523
+ if (tags.enable !== undefined) {
524
+ normalized.enable = normalizeTagList(tags.enable, "composioManaged.tags.enable");
525
+ }
526
+
527
+ if (tags.disable !== undefined) {
528
+ normalized.disable = normalizeTagList(tags.disable, "composioManaged.tags.disable");
529
+ }
530
+
531
+ return normalized;
532
+ }
533
+
534
+ function normalizeTagList(values: string[], context: string): string[] {
535
+ if (!Array.isArray(values)) {
536
+ throw new Error(`${context} must be an array of tag strings.`);
537
+ }
538
+
539
+ return Array.from(
540
+ new Set(
541
+ values.map((value) => {
542
+ if (typeof value !== "string" || value.trim().length === 0) {
543
+ throw new Error(`${context} entries must be non-empty strings.`);
544
+ }
545
+
546
+ return value.trim();
547
+ }),
548
+ ),
549
+ ).sort();
550
+ }
551
+
356
552
  function normalizeChannelBuffer(input: ChannelBufferInput, context: string): ChannelBufferConfig {
357
553
  if (input.mode === "off") {
358
554
  return { mode: "off" };
@@ -382,6 +578,33 @@ function normalizeChannelBuffer(input: ChannelBufferInput, context: string): Cha
382
578
  return buffer;
383
579
  }
384
580
 
581
+ function normalizeChannelAudio(input: ChannelAudioInput, context: string): ChannelAudioConfig {
582
+ const mode = input.mode ?? "transcribe";
583
+
584
+ if (mode !== "off" && mode !== "reject" && mode !== "transcribe") {
585
+ throw new Error(`${context} audio.mode must be "off", "reject", or "transcribe".`);
586
+ }
587
+
588
+ if (mode === "off") {
589
+ return { mode: "off" };
590
+ }
591
+
592
+ if (input.maxDurationSeconds !== undefined) {
593
+ validatePositiveInteger(input.maxDurationSeconds, `${context} audio.maxDurationSeconds`);
594
+ }
595
+
596
+ if (input.maxBytes !== undefined) {
597
+ validatePositiveInteger(input.maxBytes, `${context} audio.maxBytes`);
598
+ }
599
+
600
+ return {
601
+ mode,
602
+ fallbackMessage: input.fallbackMessage ?? "I received an audio message, but I could not transcribe it. Please send the message as text.",
603
+ ...(input.maxDurationSeconds !== undefined ? { maxDurationSeconds: input.maxDurationSeconds } : {}),
604
+ ...(input.maxBytes !== undefined ? { maxBytes: input.maxBytes } : {}),
605
+ };
606
+ }
607
+
385
608
  function validatePositiveInteger(value: number, field: string): void {
386
609
  if (!Number.isInteger(value) || value <= 0) {
387
610
  throw new Error(`${field} must be a positive integer.`);
@@ -413,10 +636,13 @@ export { AgentKitError } from "./runtime/errors";
413
636
  export type {
414
637
  ChannelAdapter,
415
638
  ChannelAdapterId,
639
+ ChannelAudioAttachment,
416
640
  ChannelDeliveryDirection,
417
641
  ChannelDeliveryState,
418
642
  ChannelEventKind,
419
643
  ChannelExternalIdentity,
644
+ ChannelMediaDownloadInput,
645
+ ChannelMediaDownloadResult,
420
646
  ChannelMessageContentType,
421
647
  ChannelMessageRole,
422
648
  ChannelSendInput,
@@ -429,3 +655,8 @@ export type {
429
655
  WebhookVerificationInput,
430
656
  WebhookVerificationResult,
431
657
  } from "./runtime/channels";
658
+ export type {
659
+ TranscriptionAdapter,
660
+ TranscriptionInput,
661
+ TranscriptionResult,
662
+ } from "./runtime/transcription";
@@ -8,9 +8,11 @@ import type { NormalizedChannelEvent, RawWebhookEvent } from "./channels";
8
8
  export type ChannelFixtureName =
9
9
  | "website-message"
10
10
  | "telegram-message"
11
+ | "telegram-audio-message"
11
12
  | "telegram-duplicate-message"
12
13
  | "telegram-malformed-payload"
13
14
  | "zapster-message"
15
+ | "zapster-audio-message"
14
16
  | "zapster-duplicate-message"
15
17
  | "zapster-unsupported-media";
16
18