@andreprado/agentkit 0.1.0-alpha.14 → 0.1.0-alpha.16

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 (56) hide show
  1. package/README.md +2 -0
  2. package/docs/guides/add-channel.md +63 -0
  3. package/docs/guides/channel-security.md +32 -0
  4. package/docs/guides/connect-telegram.md +58 -0
  5. package/docs/guides/connect-whatsapp-zapster.md +65 -0
  6. package/docs/guides/run-evals.md +73 -25
  7. package/docs/llms-full.txt +24 -9
  8. package/docs/llms.txt +2 -0
  9. package/package.json +1 -1
  10. package/src/cli/cloud-client.ts +30 -10
  11. package/src/cli/commands/channels.ts +2 -0
  12. package/src/cli/deploy-readiness.ts +32 -11
  13. package/src/cli/index.ts +20 -6
  14. package/src/cloud/client.ts +4 -3
  15. package/src/cloud/contracts.ts +1 -1
  16. package/src/create-project.ts +1 -1
  17. package/src/index.ts +110 -1
  18. package/src/providers/pi.ts +14 -1
  19. package/src/providers/test.ts +36 -0
  20. package/src/runtime/channel-test-harness.ts +2 -0
  21. package/src/runtime/channels/telegram.ts +326 -10
  22. package/src/runtime/channels/whatsapp-zapster.ts +319 -0
  23. package/src/runtime/channels.ts +47 -1
  24. package/src/runtime/chat.ts +59 -42
  25. package/src/runtime/config.ts +96 -4
  26. package/src/runtime/core/manifest.ts +35 -3
  27. package/src/runtime/deploy-readiness.ts +3 -3
  28. package/src/runtime/dev-server.ts +243 -17
  29. package/src/runtime/env.ts +8 -3
  30. package/src/runtime/evals.ts +404 -69
  31. package/src/runtime/inspect.ts +46 -0
  32. package/src/runtime/prompt-context.ts +141 -0
  33. package/src/runtime/runtime-contract.ts +17 -7
  34. package/src/runtime/targets/cloudflare/build.ts +25 -3
  35. package/src/runtime/targets/container/server.ts +1 -1
  36. package/src/runtime/targets/vps/deploy.ts +25 -8
  37. package/src/runtime/tool-runner.ts +7 -0
  38. package/src/runtime/tools.ts +8 -2
  39. package/src/runtime/transcription.ts +483 -0
  40. package/src/templates/blank.ts +8 -3
  41. package/src/templates/dentista.ts +18 -10
  42. package/src/templates/skills/agentkit-build-agent/SKILL.md +6 -5
  43. package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +2 -1
  44. package/src/templates/skills/agentkit-capsule/SKILL.md +1 -1
  45. package/src/templates/skills/agentkit-channels/SKILL.md +34 -1
  46. package/src/templates/skills/agentkit-channels/references/channel-debugging.md +13 -0
  47. package/src/templates/skills/agentkit-channels/references/telegram.md +32 -0
  48. package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +29 -0
  49. package/src/templates/skills/agentkit-evals/SKILL.md +53 -13
  50. package/src/templates/skills/agentkit-evals/templates/multi-turn.eval.md +13 -6
  51. package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +8 -4
  52. package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +8 -4
  53. package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +16 -7
  54. package/src/templates/skills/agentkit-prompts/SKILL.md +3 -1
  55. package/src/templates/skills/agentkit-tools/SKILL.md +2 -1
  56. package/src/templates/support.ts +8 -3
@@ -16,8 +16,11 @@ import type {
16
16
  TelegramAllowedUpdate,
17
17
  AgentKnowledgeEmbeddingProvider,
18
18
  ChannelBufferMode,
19
+ ChannelAudioMode,
20
+ TranscriptionProviderName,
19
21
  } from "../index";
20
22
  import { AgentKitError } from "./errors";
23
+ import { isValidTimeZone } from "./prompt-context";
21
24
 
22
25
  const CONFIG_FILE = "agentkit.config.ts";
23
26
  const AGENT_RUNTIMES = new Set<AgentRuntime>(["local", "edge"]);
@@ -27,8 +30,10 @@ const TOOL_VISIBILITIES = new Set<ToolVisibility>(["user", "internal"]);
27
30
  const CHANNEL_TYPES = new Set<ChannelType>(["website", "telegram", "whatsapp"]);
28
31
  const CHANNEL_PROVIDERS = new Set<ChannelProvider>(["agentkit", "telegram", "zapster", "meta"]);
29
32
  const CHANNEL_BUFFER_MODES = new Set<ChannelBufferMode>(["off", "debounce"]);
33
+ const CHANNEL_AUDIO_MODES = new Set<ChannelAudioMode>(["off", "reject", "transcribe"]);
30
34
  const TELEGRAM_ALLOWED_UPDATES = new Set<TelegramAllowedUpdate>(["message"]);
31
35
  const KNOWLEDGE_EMBEDDING_PROVIDERS = new Set<AgentKnowledgeEmbeddingProvider>(["none", "test", "openai"]);
36
+ const TRANSCRIPTION_PROVIDERS = new Set<TranscriptionProviderName>(["test", "openai", "groq"]);
32
37
  const CHANNEL_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
33
38
  const SECRET_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
34
39
  const moduleDir = dirname(fileURLToPath(import.meta.url));
@@ -207,8 +212,17 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
207
212
  throw invalidConfig('provider.model must be "fake" when provider.name is "test".');
208
213
  }
209
214
 
215
+ if (value.timeZone !== undefined) {
216
+ expectString(value.timeZone, "timeZone");
217
+
218
+ if (!isValidTimeZone(value.timeZone)) {
219
+ throw invalidConfig('timeZone must be a valid IANA time zone such as "UTC" or "America/New_York".');
220
+ }
221
+ }
222
+
210
223
  expectString(value.instructions, "instructions");
211
224
  expectStringArray(value.secrets, "secrets");
225
+ validateSecretNames(value.secrets, "secrets");
212
226
 
213
227
  if (value.tools !== undefined) {
214
228
  if (!Array.isArray(value.tools)) {
@@ -220,7 +234,8 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
220
234
  }
221
235
  }
222
236
 
223
- validateChannels(value.channels, value.runtime as AgentRuntime);
237
+ validateTranscriptionConfig(value.transcription);
238
+ validateChannels(value.channels, value.runtime as AgentRuntime, Boolean(value.transcription));
224
239
  validateKnowledgeConfig(value.knowledge);
225
240
 
226
241
  if (!isRecord(value.access) || !ACCESS_MODES.has(value.access.mode as AccessMode)) {
@@ -243,6 +258,46 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
243
258
  validateFilesConfig(value.storage.files);
244
259
  }
245
260
 
261
+ function validateTranscriptionConfig(value: unknown): void {
262
+ if (value === undefined) {
263
+ return;
264
+ }
265
+
266
+ if (!isRecord(value)) {
267
+ throw invalidConfig("transcription must be an object when provided.");
268
+ }
269
+
270
+ if (!TRANSCRIPTION_PROVIDERS.has(value.provider as TranscriptionProviderName)) {
271
+ throw invalidConfig(`transcription.provider must be one of: ${Array.from(TRANSCRIPTION_PROVIDERS).join(", ")}.`);
272
+ }
273
+
274
+ expectString(value.model, "transcription.model");
275
+
276
+ if (value.secret !== undefined) {
277
+ expectString(value.secret, "transcription.secret");
278
+ validateSecretNames([value.secret], "transcription.secret");
279
+ }
280
+
281
+ if (value.language !== undefined) {
282
+ expectString(value.language, "transcription.language");
283
+ }
284
+
285
+ if (value.prompt !== undefined) {
286
+ expectString(value.prompt, "transcription.prompt");
287
+ }
288
+
289
+ if (value.limits !== undefined) {
290
+ if (!isRecord(value.limits)) {
291
+ throw invalidConfig("transcription.limits must be an object when provided.");
292
+ }
293
+
294
+ validateOptionalPositiveInteger(value.limits.maxDurationSeconds, "transcription.limits.maxDurationSeconds");
295
+ validateOptionalPositiveInteger(value.limits.maxBytes, "transcription.limits.maxBytes");
296
+ }
297
+
298
+ validateOptionalPositiveInteger(value.rawAudioTtlSeconds, "transcription.rawAudioTtlSeconds");
299
+ }
300
+
246
301
  function validateKnowledgeConfig(value: unknown): void {
247
302
  if (value === undefined) {
248
303
  return;
@@ -371,7 +426,7 @@ function validateKnowledgeRetrieval(value: unknown): void {
371
426
  }
372
427
  }
373
428
 
374
- function validateChannels(value: unknown, runtime: AgentRuntime): void {
429
+ function validateChannels(value: unknown, runtime: AgentRuntime, hasTranscriptionConfig: boolean): void {
375
430
  if (value === undefined) {
376
431
  return;
377
432
  }
@@ -387,7 +442,7 @@ function validateChannels(value: unknown, runtime: AgentRuntime): void {
387
442
  const names = new Set<string>();
388
443
 
389
444
  for (const [index, channel] of value.entries()) {
390
- validateChannel(channel, index);
445
+ validateChannel(channel, index, hasTranscriptionConfig);
391
446
 
392
447
  if (names.has(channel.name)) {
393
448
  throw invalidConfig(`channels[${index}].name must be unique within the Agent Capsule.`);
@@ -397,7 +452,7 @@ function validateChannels(value: unknown, runtime: AgentRuntime): void {
397
452
  }
398
453
  }
399
454
 
400
- function validateChannel(value: unknown, index: number): asserts value is AgentChannel {
455
+ function validateChannel(value: unknown, index: number, hasTranscriptionConfig: boolean): asserts value is AgentChannel {
401
456
  if (!isRecord(value)) {
402
457
  throw invalidConfig(`channels[${index}] must be an object.`);
403
458
  }
@@ -422,6 +477,7 @@ function validateChannel(value: unknown, index: number): asserts value is AgentC
422
477
  validateSecretNames(value.secrets, `channels[${index}].secrets`);
423
478
  validateChannelLimits(value.limits, index);
424
479
  validateChannelBuffer(value.buffer, index);
480
+ validateChannelAudio(value.audio, index, hasTranscriptionConfig);
425
481
 
426
482
  if (value.type === "website") {
427
483
  validateWebsiteChannel(value, index);
@@ -436,6 +492,31 @@ function validateChannel(value: unknown, index: number): asserts value is AgentC
436
492
  validateWhatsappChannel(value, index);
437
493
  }
438
494
 
495
+ function validateChannelAudio(value: unknown, index: number, hasTranscriptionConfig: boolean): void {
496
+ if (value === undefined) {
497
+ return;
498
+ }
499
+
500
+ if (!isRecord(value)) {
501
+ throw invalidConfig(`channels[${index}].audio must be an object when provided.`);
502
+ }
503
+
504
+ if (!CHANNEL_AUDIO_MODES.has(value.mode as ChannelAudioMode)) {
505
+ throw invalidConfig(`channels[${index}].audio.mode must be "off", "reject", or "transcribe".`);
506
+ }
507
+
508
+ if (value.mode === "transcribe" && !hasTranscriptionConfig) {
509
+ throw invalidConfig(`channels[${index}].audio.mode "transcribe" requires top-level transcription config.`);
510
+ }
511
+
512
+ if (value.mode !== "off") {
513
+ expectString(value.fallbackMessage, `channels[${index}].audio.fallbackMessage`);
514
+ }
515
+
516
+ validateOptionalPositiveInteger(value.maxDurationSeconds, `channels[${index}].audio.maxDurationSeconds`);
517
+ validateOptionalPositiveInteger(value.maxBytes, `channels[${index}].audio.maxBytes`);
518
+ }
519
+
439
520
  function validateWebsiteChannel(value: Record<string, unknown>, index: number): void {
440
521
  if (value.provider !== "agentkit") {
441
522
  throw invalidConfig(`channels[${index}].provider must be "agentkit" when type is "website".`);
@@ -552,6 +633,16 @@ function validateSecretNames(secrets: string[], field: string): void {
552
633
  }
553
634
  }
554
635
 
636
+ function validateOptionalPositiveInteger(value: unknown, field: string): void {
637
+ if (value === undefined) {
638
+ return;
639
+ }
640
+
641
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
642
+ throw invalidConfig(`${field} must be a positive integer when provided.`);
643
+ }
644
+ }
645
+
555
646
  function expectRequiredSecrets(value: unknown, requiredSecrets: string[], field: string): void {
556
647
  const declaredSecrets = new Set(Array.isArray(value) ? value : []);
557
648
  const missingSecrets = requiredSecrets.filter((secret) => !declaredSecrets.has(secret));
@@ -636,6 +727,7 @@ function validateTool(value: unknown, index: number): asserts value is AgentTool
636
727
 
637
728
  if (value.secrets !== undefined) {
638
729
  expectStringArray(value.secrets, `tools[${index}].secrets`);
730
+ validateSecretNames(value.secrets, `tools[${index}].secrets`);
639
731
  }
640
732
 
641
733
  if (value.permissions !== undefined) {
@@ -4,6 +4,7 @@ import type { AgentConfig, AgentTool } from "../../index";
4
4
  import type { LoadedAgentCapsule } from "../config";
5
5
  import { resolveKnowledgeConfig } from "../knowledge/config";
6
6
  import { KNOWLEDGE_SEARCH_TOOL_NAME } from "../knowledge/tool";
7
+ import { resolveTranscriptionConfig } from "../transcription";
7
8
 
8
9
  export type ManifestSchema = {
9
10
  source: string;
@@ -15,6 +16,7 @@ export type SharedAgentManifest = {
15
16
  name: string;
16
17
  runtime: AgentConfig["runtime"];
17
18
  provider: AgentConfig["provider"];
19
+ timeZone: string | null;
18
20
  prompt: string;
19
21
  secrets: string[];
20
22
  tools: Array<{
@@ -27,6 +29,19 @@ export type SharedAgentManifest = {
27
29
  timeoutMs: number | null;
28
30
  }>;
29
31
  channels: NonNullable<AgentConfig["channels"]>;
32
+ transcription: {
33
+ enabled: boolean;
34
+ provider: string | null;
35
+ model: string | null;
36
+ secret: string | null;
37
+ language: string | null;
38
+ prompt: string | null;
39
+ limits: {
40
+ maxDurationSeconds: number | null;
41
+ maxBytes: number | null;
42
+ };
43
+ rawAudioTtlSeconds: number | null;
44
+ };
30
45
  knowledge: {
31
46
  enabled: boolean;
32
47
  sources: Array<{
@@ -79,10 +94,9 @@ export function buildSharedAgentManifest(
79
94
  const database = buildDatabaseManifest(capsule, options.databaseSchema ?? null);
80
95
  const files = buildFilesManifest(capsule);
81
96
  const knowledge = resolveKnowledgeConfig(capsule.config);
97
+ const transcription = resolveTranscriptionConfig(capsule.config.transcription);
82
98
 
83
- if (capsule.config.access.mode === "private") {
84
- requiredSecrets.add("AGENTKIT_PRIVATE_TOKEN");
85
- }
99
+ requiredSecrets.add("AGENTKIT_PRIVATE_TOKEN");
86
100
 
87
101
  if (capsule.config.access.mode === "token") {
88
102
  requiredSecrets.add("AGENTKIT_ACCESS_TOKEN");
@@ -101,6 +115,10 @@ export function buildSharedAgentManifest(
101
115
  requiredSecrets.add(knowledge.embedding.secret);
102
116
  }
103
117
 
118
+ if (transcription.secret) {
119
+ requiredSecrets.add(transcription.secret);
120
+ }
121
+
104
122
  for (const tool of capsule.config.tools ?? []) {
105
123
  for (const secret of tool.secrets ?? []) {
106
124
  requiredSecrets.add(secret);
@@ -118,6 +136,7 @@ export function buildSharedAgentManifest(
118
136
  name: capsule.config.name,
119
137
  runtime: capsule.config.runtime,
120
138
  provider: capsule.config.provider,
139
+ timeZone: capsule.config.timeZone ?? null,
121
140
  prompt: toCapsulePath(capsule, capsule.instructionsPath),
122
141
  secrets: Array.from(requiredSecrets).sort(),
123
142
  tools: (capsule.config.tools ?? []).map((tool) => ({
@@ -130,6 +149,19 @@ export function buildSharedAgentManifest(
130
149
  timeoutMs: tool.timeoutMs ?? null,
131
150
  })),
132
151
  channels: capsule.config.channels ?? [],
152
+ transcription: {
153
+ enabled: transcription.enabled,
154
+ provider: transcription.enabled ? transcription.provider : null,
155
+ model: transcription.enabled ? transcription.model : null,
156
+ secret: transcription.secret,
157
+ language: transcription.language,
158
+ prompt: transcription.prompt,
159
+ limits: {
160
+ maxDurationSeconds: transcription.limits.maxDurationSeconds ?? null,
161
+ maxBytes: transcription.limits.maxBytes ?? null,
162
+ },
163
+ rawAudioTtlSeconds: transcription.rawAudioTtlSeconds,
164
+ },
133
165
  knowledge: {
134
166
  enabled: knowledge.enabled,
135
167
  sources: knowledge.sources.map((source) => ({
@@ -85,8 +85,8 @@ export async function loadDeployReadinessContext(
85
85
  };
86
86
  }
87
87
 
88
- export function stableCloudProjectId(name: string): string {
89
- return `proj_${createHash("sha256").update(name).digest("hex").slice(0, 16)}`;
88
+ export function stableCloudProjectId(name: string, accountId?: string | null): string {
89
+ return `proj_${createHash("sha256").update(`${accountId ? `account:${accountId}` : "anonymous"}:${name}`).digest("hex").slice(0, 16)}`;
90
90
  }
91
91
 
92
92
  function isAgentKitManagedDeploySecret(
@@ -97,7 +97,7 @@ function isAgentKitManagedDeploySecret(
97
97
  return true;
98
98
  }
99
99
 
100
- if (context.accessMode === "private" && name === "AGENTKIT_PRIVATE_TOKEN") {
100
+ if (name === "AGENTKIT_PRIVATE_TOKEN") {
101
101
  return true;
102
102
  }
103
103