@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
@@ -1,15 +1,15 @@
1
1
  import { findAgentCapsuleRoot, loadAgentCapsule } from "../runtime/config";
2
- import { readLocalDeployStateIfExists } from "../runtime/core/deploy-state";
3
- import { loadDeployReadinessContext, stableCloudProjectId, type DeployReadinessContext } from "../runtime/deploy-readiness";
2
+ import { loadDeployReadinessContext, type DeployReadinessContext } from "../runtime/deploy-readiness";
4
3
  import { loadCapsuleEnv } from "../runtime/env";
5
4
  import { isAgentKitError } from "../runtime/errors";
6
5
  import {
7
6
  cloudApiRequest,
8
7
  formatCloudAuthSource,
9
- readCloudAuth,
8
+ readCloudAuthForApiUrl,
10
9
  type CloudCapabilitiesResponse,
11
10
  type CloudLimitsResponse,
12
11
  type CloudMeResponse,
12
+ type CloudProjectResolveResponse,
13
13
  type CloudSecretsResponse,
14
14
  } from "./cloud-client";
15
15
 
@@ -34,7 +34,7 @@ export async function checkDeployReadiness(options: {
34
34
  requireLogin: boolean;
35
35
  }): Promise<DeployReadinessReport> {
36
36
  const context = await loadDeployReadinessContext(process.cwd(), process.env);
37
- const auth = await readCloudAuth();
37
+ const auth = options.requireLogin ? await readCloudAuthForApiUrl(options.apiUrl) : null;
38
38
  const checks: DeployReadinessCheck[] = [];
39
39
  let cloudAuthUsable = false;
40
40
 
@@ -82,6 +82,19 @@ export async function checkDeployReadiness(options: {
82
82
  }
83
83
  }
84
84
 
85
+ if (cloudAuthUsable) {
86
+ try {
87
+ context.projectId = await resolveCloudProjectId(options.apiUrl, context.projectName);
88
+ } catch (error) {
89
+ checks.push({
90
+ status: "fail",
91
+ title: "Project identity",
92
+ message: `Could not resolve the account-scoped AgentKit Cloud project. ${formatReadinessError(error)}`,
93
+ });
94
+ cloudAuthUsable = false;
95
+ }
96
+ }
97
+
85
98
  if (cloudAuthUsable) {
86
99
  try {
87
100
  const payload = (await cloudApiRequest(options.apiUrl, "/v1/me/capabilities", {
@@ -315,16 +328,24 @@ export function formatReadinessError(error: unknown): string {
315
328
  return error instanceof Error ? error.message : String(error);
316
329
  }
317
330
 
318
- export async function resolveProjectIdForSecretCommand(cwd: string): Promise<string> {
319
- const state = await readLocalDeployStateIfExists(cwd);
331
+ export async function resolveProjectIdForSecretCommand(cwd: string, apiUrl: string): Promise<string> {
332
+ const root = await findAgentCapsuleRoot(cwd);
333
+ const capsule = await loadAgentCapsule(root);
334
+ return resolveCloudProjectId(apiUrl, capsule.config.name);
335
+ }
320
336
 
321
- if (state?.project_id) {
322
- return state.project_id;
337
+ async function resolveCloudProjectId(apiUrl: string, projectName: string): Promise<string> {
338
+ const payload = (await cloudApiRequest(apiUrl, "/v1/projects/resolve", {
339
+ method: "POST",
340
+ body: JSON.stringify({ project_name: projectName }),
341
+ })) as CloudProjectResolveResponse;
342
+ const projectId = payload.project?.id;
343
+
344
+ if (!projectId) {
345
+ throw new Error("AgentKit Cloud did not return a project id.");
323
346
  }
324
347
 
325
- const root = await findAgentCapsuleRoot(cwd);
326
- const capsule = await loadAgentCapsule(root);
327
- return stableCloudProjectId(capsule.config.name);
348
+ return projectId;
328
349
  }
329
350
 
330
351
  function secretSetCommand(secret: string, source: DeployReadinessContext["localSecrets"][string] | undefined): string {
package/src/cli/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { existsSync } from "node:fs";
3
- import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
4
4
  import { dirname, join, relative, resolve } from "node:path";
5
5
 
6
6
  import { createProject } from "../create-project";
@@ -38,6 +38,7 @@ import {
38
38
  clearCloudAuth,
39
39
  cloudApiRequest,
40
40
  readCloudAuth,
41
+ readCloudAuthForApiUrl,
41
42
  readCloudError,
42
43
  resolveCloudApiUrl,
43
44
  verifyCloudLogin,
@@ -607,10 +608,10 @@ async function main() {
607
608
  }
608
609
 
609
610
  const apiUrl = await resolveCloudApiUrl(args.flags.api);
610
- const auth = await readCloudAuth();
611
+ const auth = anonymous ? null : await readCloudAuthForApiUrl(apiUrl);
611
612
  const deploy = await deployBuildToAgentKitCloud(result, {
612
613
  apiUrl,
613
- apiToken: auth?.token,
614
+ apiToken: anonymous ? null : auth?.token,
614
615
  anonymous: anonymous || !auth?.token,
615
616
  });
616
617
  const statePath = await writeLocalDeployState(result.root, deploy);
@@ -659,8 +660,8 @@ async function main() {
659
660
 
660
661
  if (args.command === "secret") {
661
662
  const [subcommand, name, ...valueParts] = args.positional;
662
- const projectId = await resolveProjectIdForSecretCommand(process.cwd());
663
663
  const apiUrl = await resolveCloudApiUrl(args.flags.api);
664
+ const projectId = await resolveProjectIdForSecretCommand(process.cwd(), apiUrl);
664
665
 
665
666
  if (subcommand === "list") {
666
667
  const payload = await cloudApiRequest(apiUrl, `/v1/projects/${encodeURIComponent(projectId)}/secrets`, {
@@ -949,9 +950,12 @@ async function ensureDeployChatAccessToken(options: {
949
950
  `/v1/deploys/${encodeURIComponent(options.deployId)}/access-tokens`,
950
951
  { method: "GET" },
951
952
  )) as DeployAccessTokenListResponse;
952
- const stillExists = (listed.access_tokens ?? []).some((token) => token.id === existing.id);
953
+ const stillUsable = (listed.access_tokens ?? []).some(
954
+ (token) => token.id === existing.id && deployAccessTokenMetadataIsUsable(token),
955
+ );
953
956
 
954
- if (stillExists) {
957
+ if (stillUsable) {
958
+ await chmod(path, 0o600);
955
959
  return { status: "ready", path, tokenId: existing.id, created: false };
956
960
  }
957
961
  }
@@ -977,6 +981,15 @@ async function ensureDeployChatAccessToken(options: {
977
981
  }
978
982
  }
979
983
 
984
+ function deployAccessTokenMetadataIsUsable(token: { expires_at?: string }): boolean {
985
+ if (!token.expires_at) {
986
+ return true;
987
+ }
988
+
989
+ const expiresAtMs = Date.parse(token.expires_at);
990
+ return Number.isFinite(expiresAtMs) && expiresAtMs > Date.now();
991
+ }
992
+
980
993
  async function readDeployAccessTokenFile(path: string): Promise<{ id?: string; deploy_id?: string } | null> {
981
994
  try {
982
995
  const parsed = JSON.parse(await readFile(path, "utf8")) as DeployAccessTokenCreateResponse;
@@ -989,6 +1002,7 @@ async function readDeployAccessTokenFile(path: string): Promise<{ id?: string; d
989
1002
  async function writeSecretJson(path: string, value: unknown): Promise<void> {
990
1003
  await mkdir(dirname(path), { recursive: true });
991
1004
  await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
1005
+ await chmod(path, 0o600);
992
1006
  }
993
1007
 
994
1008
  async function requireLocalDeployState(): Promise<LocalDeployState> {
@@ -1,4 +1,4 @@
1
- import { mkdir, writeFile } from "node:fs/promises";
1
+ import { chmod, mkdir, writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
 
4
4
  import type { AgentBuildResult } from "../runtime/targets/cloudflare/build";
@@ -15,7 +15,7 @@ export async function deployBuildToAgentKitCloud(
15
15
  const headers = new Headers({
16
16
  "Content-Type": "application/json",
17
17
  });
18
- const apiToken = options.apiToken ?? process.env.AGENTKIT_CLOUD_API_TOKEN;
18
+ const apiToken = options.anonymous ? undefined : options.apiToken ?? process.env.AGENTKIT_CLOUD_API_TOKEN;
19
19
 
20
20
  if (apiToken) {
21
21
  headers.set("Authorization", `Bearer ${apiToken}`);
@@ -70,7 +70,8 @@ export async function writeLocalDeployState(root: string, deploy: CreateDeployRe
70
70
  };
71
71
 
72
72
  await mkdir(join(root, ".agentkit"), { recursive: true });
73
- await writeFile(statePath, `${JSON.stringify(state, null, 2)}\n`);
73
+ await writeFile(statePath, `${JSON.stringify(state, null, 2)}\n`, { mode: 0o600 });
74
+ await chmod(statePath, 0o600);
74
75
  return statePath;
75
76
  }
76
77
 
@@ -58,6 +58,6 @@ export type CreateDeployResponse = {
58
58
 
59
59
  export type AgentKitCloudDeployOptions = {
60
60
  apiUrl: string;
61
- apiToken?: string;
61
+ apiToken?: string | null;
62
62
  anonymous?: boolean;
63
63
  };
@@ -114,7 +114,7 @@ async function resolveAgentKitDependency(): Promise<string> {
114
114
 
115
115
  const packageRoot = resolve(moduleDir, "..");
116
116
 
117
- if (await directoryExists(resolve(packageRoot, "../..", ".git"))) {
117
+ if (await pathExists(resolve(packageRoot, "../..", ".git"))) {
118
118
  return `file:${packageRoot}`;
119
119
  }
120
120
 
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,6 +140,7 @@ 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;
@@ -210,6 +257,7 @@ export type ToolRenderResult = {
210
257
  export type ToolRenderContext<Input = unknown> = {
211
258
  input: Input;
212
259
  runtime: ToolRuntimeContext;
260
+ clock: ToolClockContext;
213
261
  };
214
262
 
215
263
  export type ToolRuntimeContext = {
@@ -219,10 +267,20 @@ export type ToolRuntimeContext = {
219
267
  database: "sqlite" | "turso" | "none";
220
268
  };
221
269
 
270
+ export type ToolClockContext = {
271
+ now: Date;
272
+ isoTimestamp: string;
273
+ timeZone: string;
274
+ localDate: string;
275
+ localWeekday: string;
276
+ localDateTime: string;
277
+ };
278
+
222
279
  export type ToolContext = {
223
280
  secrets: Record<string, string>;
224
281
  signal: AbortSignal;
225
282
  runtime: ToolRuntimeContext;
283
+ clock: ToolClockContext;
226
284
  database: DatabaseRunner;
227
285
  db: DatabaseRunner;
228
286
  storage: {
@@ -254,10 +312,12 @@ export type AgentConfig = {
254
312
  name: string;
255
313
  runtime: AgentRuntime;
256
314
  provider: AgentProvider;
315
+ timeZone?: string;
257
316
  instructions: string;
258
317
  secrets: string[];
259
318
  tools?: AgentTool[];
260
319
  channels?: AgentChannel[];
320
+ transcription?: AgentTranscriptionConfig;
261
321
  knowledge?: AgentKnowledgeConfig;
262
322
  access: {
263
323
  mode: AccessMode;
@@ -287,6 +347,7 @@ export function websiteChannel(input: WebsiteChannelInput): WebsiteChannelConfig
287
347
  secrets: normalizeChannelSecrets(input.secrets ?? ["AGENTKIT_WEBSITE_CHANNEL_TOKEN"], "website channel"),
288
348
  ...(input.limits ? { limits: input.limits } : {}),
289
349
  ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "website channel") } : {}),
350
+ ...(input.audio ? { audio: normalizeChannelAudio(input.audio, "website channel") } : {}),
290
351
  };
291
352
  }
292
353
 
@@ -302,6 +363,7 @@ export function telegramChannel(input: TelegramChannelInput): TelegramChannelCon
302
363
  allowedUpdates: input.allowedUpdates ?? ["message"],
303
364
  ...(input.limits ? { limits: input.limits } : {}),
304
365
  ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "telegram channel") } : {}),
366
+ ...(input.audio ? { audio: normalizeChannelAudio(input.audio, "telegram channel") } : {}),
305
367
  };
306
368
  }
307
369
 
@@ -316,6 +378,7 @@ export function whatsappChannel(input: WhatsappChannelInput): WhatsappChannelCon
316
378
  ),
317
379
  ...(input.limits ? { limits: input.limits } : {}),
318
380
  ...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, `${input.provider} whatsapp channel`) } : {}),
381
+ ...(input.audio ? { audio: normalizeChannelAudio(input.audio, `${input.provider} whatsapp channel`) } : {}),
319
382
  };
320
383
  }
321
384
 
@@ -370,6 +433,33 @@ function normalizeChannelBuffer(input: ChannelBufferInput, context: string): Cha
370
433
  return buffer;
371
434
  }
372
435
 
436
+ function normalizeChannelAudio(input: ChannelAudioInput, context: string): ChannelAudioConfig {
437
+ const mode = input.mode ?? "transcribe";
438
+
439
+ if (mode !== "off" && mode !== "reject" && mode !== "transcribe") {
440
+ throw new Error(`${context} audio.mode must be "off", "reject", or "transcribe".`);
441
+ }
442
+
443
+ if (mode === "off") {
444
+ return { mode: "off" };
445
+ }
446
+
447
+ if (input.maxDurationSeconds !== undefined) {
448
+ validatePositiveInteger(input.maxDurationSeconds, `${context} audio.maxDurationSeconds`);
449
+ }
450
+
451
+ if (input.maxBytes !== undefined) {
452
+ validatePositiveInteger(input.maxBytes, `${context} audio.maxBytes`);
453
+ }
454
+
455
+ return {
456
+ mode,
457
+ fallbackMessage: input.fallbackMessage ?? "I received an audio message, but I could not transcribe it. Please send the message as text.",
458
+ ...(input.maxDurationSeconds !== undefined ? { maxDurationSeconds: input.maxDurationSeconds } : {}),
459
+ ...(input.maxBytes !== undefined ? { maxBytes: input.maxBytes } : {}),
460
+ };
461
+ }
462
+
373
463
  function validatePositiveInteger(value: number, field: string): void {
374
464
  if (!Number.isInteger(value) || value <= 0) {
375
465
  throw new Error(`${field} must be a positive integer.`);
@@ -379,7 +469,18 @@ function validatePositiveInteger(value: number, field: string): void {
379
469
  export { loadAgentCapsule, findAgentCapsuleRoot } from "./runtime/config";
380
470
  export { runAgentMessage, runAgentMessageFromCwd } from "./runtime/chat";
381
471
  export { listConversationsFromCwd, getConversationFromCwd } from "./runtime/conversations";
382
- export { runEvalsFromCwd } from "./runtime/evals";
472
+ export { defineEval, runEvalsFromCwd } from "./runtime/evals";
473
+ export type {
474
+ EvalCase,
475
+ EvalExpect,
476
+ EvalResponseExpectation,
477
+ EvalRunSummary,
478
+ EvalResult,
479
+ EvalToolsContainerExpectation,
480
+ EvalToolsExpectation,
481
+ EvalTurn,
482
+ ToolExpectation,
483
+ } from "./runtime/evals";
383
484
  export { getConversationTraceFromCwd } from "./runtime/traces";
384
485
  export { runToolFromCwd } from "./runtime/tool-runner";
385
486
  export { startAgentDevServer } from "./runtime/dev-server";
@@ -390,10 +491,13 @@ export { AgentKitError } from "./runtime/errors";
390
491
  export type {
391
492
  ChannelAdapter,
392
493
  ChannelAdapterId,
494
+ ChannelAudioAttachment,
393
495
  ChannelDeliveryDirection,
394
496
  ChannelDeliveryState,
395
497
  ChannelEventKind,
396
498
  ChannelExternalIdentity,
499
+ ChannelMediaDownloadInput,
500
+ ChannelMediaDownloadResult,
397
501
  ChannelMessageContentType,
398
502
  ChannelMessageRole,
399
503
  ChannelSendInput,
@@ -406,3 +510,8 @@ export type {
406
510
  WebhookVerificationInput,
407
511
  WebhookVerificationResult,
408
512
  } from "./runtime/channels";
513
+ export type {
514
+ TranscriptionAdapter,
515
+ TranscriptionInput,
516
+ TranscriptionResult,
517
+ } from "./runtime/transcription";
@@ -97,7 +97,7 @@ async function runPiProvider(input: ProviderRunInput): Promise<ProviderRunResult
97
97
  content: [
98
98
  {
99
99
  type: "text",
100
- text: result.rendered?.text ?? JSON.stringify(result.output),
100
+ text: providerToolResultText(result),
101
101
  },
102
102
  ],
103
103
  isError: false,
@@ -109,6 +109,19 @@ async function runPiProvider(input: ProviderRunInput): Promise<ProviderRunResult
109
109
  throw new AgentKitError("runtime_error", `Pi provider exceeded ${MAX_TOOL_TURNS} tool turns.`);
110
110
  }
111
111
 
112
+ export function providerToolResultText(result: {
113
+ name: string;
114
+ visibility?: "user" | "internal";
115
+ rendered?: { text?: string };
116
+ output: unknown;
117
+ }): string {
118
+ if (result.visibility === "internal") {
119
+ return `Internal tool ${result.name} completed.`;
120
+ }
121
+
122
+ return result.rendered?.text ?? JSON.stringify(result.output);
123
+ }
124
+
112
125
  async function runPiTurn(
113
126
  model: Model<any>,
114
127
  context: Parameters<typeof stream>[1],
@@ -37,6 +37,17 @@ export const testProviderAdapter: ProviderAdapter = {
37
37
  };
38
38
  }
39
39
 
40
+ const currentDate = answerCurrentDate(input.instructions, userMessage.content);
41
+
42
+ if (currentDate) {
43
+ return {
44
+ message: {
45
+ role: "assistant",
46
+ content: currentDate,
47
+ },
48
+ };
49
+ }
50
+
40
51
  const rememberedName = answerRememberedName(input.messages);
41
52
 
42
53
  if (rememberedName) {
@@ -95,6 +106,31 @@ function answerRememberedName(messages: AgentMessage[]): string | null {
95
106
  return null;
96
107
  }
97
108
 
109
+ function answerCurrentDate(instructions: string, content: string): string | null {
110
+ if (!/(today|current date|what date|data de hoje|hoje)/i.test(content)) {
111
+ return null;
112
+ }
113
+
114
+ const localDate = readRuntimeContextValue(instructions, "Local date");
115
+
116
+ if (!localDate) {
117
+ return null;
118
+ }
119
+
120
+ const localWeekday = readRuntimeContextValue(instructions, "Local weekday");
121
+ const timeZone = readRuntimeContextValue(instructions, "Time zone");
122
+ const weekday = localWeekday ? ` (${localWeekday})` : "";
123
+ const zone = timeZone ? ` in ${timeZone}` : "";
124
+
125
+ return `Today is ${localDate}${weekday}${zone}.`;
126
+ }
127
+
128
+ function readRuntimeContextValue(instructions: string, label: string): string | null {
129
+ const escapedLabel = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
130
+ const match = instructions.match(new RegExp(`^- ${escapedLabel}: (.+)$`, "m"));
131
+ return match?.[1]?.trim() || null;
132
+ }
133
+
98
134
  function asksForName(content: string): boolean {
99
135
  return /what(?:'s| is) my name\??/i.test(content) || /(como|qual)\s+(é|e)\s*(o\s+)?meu nome\??/i.test(content);
100
136
  }
@@ -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