@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.
- package/README.md +8 -0
- package/docs/guides/add-channel.md +65 -0
- package/docs/guides/add-managed-composio.md +137 -0
- package/docs/guides/channel-security.md +32 -0
- package/docs/guides/channels-production-handoff.md +1 -1
- package/docs/guides/connect-telegram.md +60 -0
- package/docs/guides/connect-whatsapp-zapster.md +65 -0
- package/docs/guides/prepare-deploy.md +3 -1
- package/docs/llms-full.txt +13 -1
- package/docs/llms.txt +7 -0
- package/package.json +1 -1
- package/src/cli/commands/channels.ts +121 -7
- package/src/cli/commands/transcribe.ts +171 -0
- package/src/cli/deploy-readiness.ts +59 -3
- package/src/cli/help.ts +15 -0
- package/src/cli/index.ts +130 -0
- package/src/create-project.ts +4 -32
- package/src/index.ts +231 -0
- package/src/runtime/channel-test-harness.ts +2 -0
- package/src/runtime/channels/telegram.ts +326 -10
- package/src/runtime/channels/whatsapp-zapster.ts +319 -0
- package/src/runtime/channels.ts +47 -1
- package/src/runtime/chat.ts +2 -1
- package/src/runtime/config.ts +214 -4
- package/src/runtime/core/manifest.ts +61 -2
- package/src/runtime/deploy-readiness.ts +31 -1
- package/src/runtime/dev-server.ts +72 -4
- package/src/runtime/inspect.ts +142 -4
- package/src/runtime/integrations/composio.ts +257 -0
- package/src/runtime/skills.ts +95 -0
- package/src/runtime/targets/cloudflare/build.ts +162 -5
- package/src/runtime/tool-runner.ts +2 -1
- package/src/runtime/transcription.ts +483 -0
- package/src/templates/skills/agentkit-capsule/SKILL.md +1 -0
- package/src/templates/skills/agentkit-channels/SKILL.md +36 -1
- package/src/templates/skills/agentkit-channels/references/channel-debugging.md +16 -1
- package/src/templates/skills/agentkit-channels/references/telegram.md +34 -0
- package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +29 -0
- package/src/templates/skills/agentkit-deploy/SKILL.md +1 -1
- package/src/templates/skills/agentkit-integrations/SKILL.md +66 -0
- package/src/templates/skills/agentkit-troubleshooting/SKILL.md +3 -2
package/src/runtime/config.ts
CHANGED
|
@@ -16,7 +16,11 @@ import type {
|
|
|
16
16
|
TelegramAllowedUpdate,
|
|
17
17
|
AgentKnowledgeEmbeddingProvider,
|
|
18
18
|
ChannelBufferMode,
|
|
19
|
+
AgentIntegration,
|
|
20
|
+
ChannelAudioMode,
|
|
21
|
+
TranscriptionProviderName,
|
|
19
22
|
} from "../index";
|
|
23
|
+
import { MANAGED_COMPOSIO_TOOL_NAME } from "./integrations/composio";
|
|
20
24
|
import { AgentKitError } from "./errors";
|
|
21
25
|
import { isValidTimeZone } from "./prompt-context";
|
|
22
26
|
|
|
@@ -28,9 +32,14 @@ const TOOL_VISIBILITIES = new Set<ToolVisibility>(["user", "internal"]);
|
|
|
28
32
|
const CHANNEL_TYPES = new Set<ChannelType>(["website", "telegram", "whatsapp"]);
|
|
29
33
|
const CHANNEL_PROVIDERS = new Set<ChannelProvider>(["agentkit", "telegram", "zapster", "meta"]);
|
|
30
34
|
const CHANNEL_BUFFER_MODES = new Set<ChannelBufferMode>(["off", "debounce"]);
|
|
35
|
+
const CHANNEL_AUDIO_MODES = new Set<ChannelAudioMode>(["off", "reject", "transcribe"]);
|
|
31
36
|
const TELEGRAM_ALLOWED_UPDATES = new Set<TelegramAllowedUpdate>(["message"]);
|
|
32
37
|
const KNOWLEDGE_EMBEDDING_PROVIDERS = new Set<AgentKnowledgeEmbeddingProvider>(["none", "test", "openai"]);
|
|
38
|
+
const TRANSCRIPTION_PROVIDERS = new Set<TranscriptionProviderName>(["test", "openai", "groq"]);
|
|
33
39
|
const CHANNEL_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
40
|
+
const INTEGRATION_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
41
|
+
const COMPOSIO_TOOLKIT_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
42
|
+
const COMPOSIO_TOOL_SLUG_PATTERN = /^[A-Z][A-Z0-9_]*$/;
|
|
34
43
|
const SECRET_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
|
|
35
44
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
36
45
|
let configImportNonce = 0;
|
|
@@ -230,7 +239,9 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
|
|
|
230
239
|
}
|
|
231
240
|
}
|
|
232
241
|
|
|
233
|
-
|
|
242
|
+
validateIntegrations(value.integrations, value.runtime as AgentRuntime, value.tools);
|
|
243
|
+
validateTranscriptionConfig(value.transcription);
|
|
244
|
+
validateChannels(value.channels, value.runtime as AgentRuntime, Boolean(value.transcription));
|
|
234
245
|
validateKnowledgeConfig(value.knowledge);
|
|
235
246
|
|
|
236
247
|
if (!isRecord(value.access) || !ACCESS_MODES.has(value.access.mode as AccessMode)) {
|
|
@@ -253,6 +264,169 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
|
|
|
253
264
|
validateFilesConfig(value.storage.files);
|
|
254
265
|
}
|
|
255
266
|
|
|
267
|
+
function validateIntegrations(
|
|
268
|
+
value: unknown,
|
|
269
|
+
runtime: AgentRuntime,
|
|
270
|
+
tools: AgentConfig["tools"] = [],
|
|
271
|
+
): void {
|
|
272
|
+
if (value === undefined) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (!Array.isArray(value)) {
|
|
277
|
+
throw invalidConfig("integrations must be an array when provided.");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (value.length > 0 && runtime !== "edge") {
|
|
281
|
+
throw invalidConfig('managed integrations require runtime: "edge". Use BYO defineTool wrappers for local-only integrations.');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
let composioCount = 0;
|
|
285
|
+
|
|
286
|
+
for (const [index, integration] of value.entries()) {
|
|
287
|
+
if (!isRecord(integration)) {
|
|
288
|
+
throw invalidConfig(`integrations[${index}] must be an object.`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (integration.provider !== "composio" || integration.type !== "composio" || integration.mode !== "managed") {
|
|
292
|
+
throw invalidConfig(`integrations[${index}] must be composioManaged({...}) or { type: "composio", provider: "composio", mode: "managed", ... }.`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
composioCount += 1;
|
|
296
|
+
validateComposioManagedIntegration(integration as unknown as AgentIntegration, index);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (composioCount > 1) {
|
|
300
|
+
throw invalidConfig("Only one managed Composio integration is supported per agent.");
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const hasExplicitComposioTools = value.some(
|
|
304
|
+
(integration) => isRecord(integration) && integration.provider === "composio" && isRecord(integration.tools),
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
if (hasExplicitComposioTools && tools.some((tool) => isRecord(tool) && tool.name === MANAGED_COMPOSIO_TOOL_NAME)) {
|
|
308
|
+
throw invalidConfig(`tools must not declare reserved managed Composio tool name "${MANAGED_COMPOSIO_TOOL_NAME}".`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function validateComposioManagedIntegration(value: AgentIntegration, index: number): void {
|
|
313
|
+
expectString(value.name, `integrations[${index}].name`);
|
|
314
|
+
|
|
315
|
+
if (!INTEGRATION_NAME_PATTERN.test(value.name)) {
|
|
316
|
+
throw invalidConfig(`integrations[${index}].name must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens.`);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (!Array.isArray(value.toolkits) || value.toolkits.length === 0) {
|
|
320
|
+
throw invalidConfig(`integrations[${index}].toolkits must include at least one Composio toolkit slug.`);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const toolkits = new Set<string>();
|
|
324
|
+
|
|
325
|
+
for (const [toolkitIndex, toolkit] of value.toolkits.entries()) {
|
|
326
|
+
expectString(toolkit, `integrations[${index}].toolkits[${toolkitIndex}]`);
|
|
327
|
+
|
|
328
|
+
if (!COMPOSIO_TOOLKIT_PATTERN.test(toolkit)) {
|
|
329
|
+
throw invalidConfig(`integrations[${index}].toolkits[${toolkitIndex}] must be a lowercase Composio toolkit slug.`);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (toolkits.has(toolkit)) {
|
|
333
|
+
throw invalidConfig(`integrations[${index}].toolkits must not contain duplicates.`);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
toolkits.add(toolkit);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (value.tools !== undefined) {
|
|
340
|
+
if (!isRecord(value.tools)) {
|
|
341
|
+
throw invalidConfig(`integrations[${index}].tools must be an object when provided.`);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
for (const [toolkit, tools] of Object.entries(value.tools)) {
|
|
345
|
+
if (!toolkits.has(toolkit)) {
|
|
346
|
+
throw invalidConfig(`integrations[${index}].tools.${toolkit} must reference a toolkit listed in integrations[${index}].toolkits.`);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (!Array.isArray(tools) || tools.length === 0) {
|
|
350
|
+
throw invalidConfig(`integrations[${index}].tools.${toolkit} must include at least one Composio action slug.`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const seenTools = new Set<string>();
|
|
354
|
+
|
|
355
|
+
for (const [toolIndex, tool] of tools.entries()) {
|
|
356
|
+
expectString(tool, `integrations[${index}].tools.${toolkit}[${toolIndex}]`);
|
|
357
|
+
|
|
358
|
+
if (!COMPOSIO_TOOL_SLUG_PATTERN.test(tool)) {
|
|
359
|
+
throw invalidConfig(`integrations[${index}].tools.${toolkit}[${toolIndex}] must be an uppercase Composio action slug.`);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (seenTools.has(tool)) {
|
|
363
|
+
throw invalidConfig(`integrations[${index}].tools.${toolkit} must not contain duplicates.`);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
seenTools.add(tool);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (value.tags !== undefined) {
|
|
372
|
+
validateIntegrationTags(value.tags, `integrations[${index}].tags`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function validateIntegrationTags(value: unknown, label: string): void {
|
|
377
|
+
if (!isRecord(value)) {
|
|
378
|
+
throw invalidConfig(`${label} must be an object when provided.`);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (value.enable !== undefined) {
|
|
382
|
+
expectStringArray(value.enable, `${label}.enable`);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (value.disable !== undefined) {
|
|
386
|
+
expectStringArray(value.disable, `${label}.disable`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function validateTranscriptionConfig(value: unknown): void {
|
|
391
|
+
if (value === undefined) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (!isRecord(value)) {
|
|
396
|
+
throw invalidConfig("transcription must be an object when provided.");
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (!TRANSCRIPTION_PROVIDERS.has(value.provider as TranscriptionProviderName)) {
|
|
400
|
+
throw invalidConfig(`transcription.provider must be one of: ${Array.from(TRANSCRIPTION_PROVIDERS).join(", ")}.`);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
expectString(value.model, "transcription.model");
|
|
404
|
+
|
|
405
|
+
if (value.secret !== undefined) {
|
|
406
|
+
expectString(value.secret, "transcription.secret");
|
|
407
|
+
validateSecretNames([value.secret], "transcription.secret");
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (value.language !== undefined) {
|
|
411
|
+
expectString(value.language, "transcription.language");
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (value.prompt !== undefined) {
|
|
415
|
+
expectString(value.prompt, "transcription.prompt");
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (value.limits !== undefined) {
|
|
419
|
+
if (!isRecord(value.limits)) {
|
|
420
|
+
throw invalidConfig("transcription.limits must be an object when provided.");
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
validateOptionalPositiveInteger(value.limits.maxDurationSeconds, "transcription.limits.maxDurationSeconds");
|
|
424
|
+
validateOptionalPositiveInteger(value.limits.maxBytes, "transcription.limits.maxBytes");
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
validateOptionalPositiveInteger(value.rawAudioTtlSeconds, "transcription.rawAudioTtlSeconds");
|
|
428
|
+
}
|
|
429
|
+
|
|
256
430
|
function validateKnowledgeConfig(value: unknown): void {
|
|
257
431
|
if (value === undefined) {
|
|
258
432
|
return;
|
|
@@ -381,7 +555,7 @@ function validateKnowledgeRetrieval(value: unknown): void {
|
|
|
381
555
|
}
|
|
382
556
|
}
|
|
383
557
|
|
|
384
|
-
function validateChannels(value: unknown, runtime: AgentRuntime): void {
|
|
558
|
+
function validateChannels(value: unknown, runtime: AgentRuntime, hasTranscriptionConfig: boolean): void {
|
|
385
559
|
if (value === undefined) {
|
|
386
560
|
return;
|
|
387
561
|
}
|
|
@@ -397,7 +571,7 @@ function validateChannels(value: unknown, runtime: AgentRuntime): void {
|
|
|
397
571
|
const names = new Set<string>();
|
|
398
572
|
|
|
399
573
|
for (const [index, channel] of value.entries()) {
|
|
400
|
-
validateChannel(channel, index);
|
|
574
|
+
validateChannel(channel, index, hasTranscriptionConfig);
|
|
401
575
|
|
|
402
576
|
if (names.has(channel.name)) {
|
|
403
577
|
throw invalidConfig(`channels[${index}].name must be unique within the Agent Capsule.`);
|
|
@@ -407,7 +581,7 @@ function validateChannels(value: unknown, runtime: AgentRuntime): void {
|
|
|
407
581
|
}
|
|
408
582
|
}
|
|
409
583
|
|
|
410
|
-
function validateChannel(value: unknown, index: number): asserts value is AgentChannel {
|
|
584
|
+
function validateChannel(value: unknown, index: number, hasTranscriptionConfig: boolean): asserts value is AgentChannel {
|
|
411
585
|
if (!isRecord(value)) {
|
|
412
586
|
throw invalidConfig(`channels[${index}] must be an object.`);
|
|
413
587
|
}
|
|
@@ -432,6 +606,7 @@ function validateChannel(value: unknown, index: number): asserts value is AgentC
|
|
|
432
606
|
validateSecretNames(value.secrets, `channels[${index}].secrets`);
|
|
433
607
|
validateChannelLimits(value.limits, index);
|
|
434
608
|
validateChannelBuffer(value.buffer, index);
|
|
609
|
+
validateChannelAudio(value.audio, index, hasTranscriptionConfig);
|
|
435
610
|
|
|
436
611
|
if (value.type === "website") {
|
|
437
612
|
validateWebsiteChannel(value, index);
|
|
@@ -446,6 +621,31 @@ function validateChannel(value: unknown, index: number): asserts value is AgentC
|
|
|
446
621
|
validateWhatsappChannel(value, index);
|
|
447
622
|
}
|
|
448
623
|
|
|
624
|
+
function validateChannelAudio(value: unknown, index: number, hasTranscriptionConfig: boolean): void {
|
|
625
|
+
if (value === undefined) {
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (!isRecord(value)) {
|
|
630
|
+
throw invalidConfig(`channels[${index}].audio must be an object when provided.`);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (!CHANNEL_AUDIO_MODES.has(value.mode as ChannelAudioMode)) {
|
|
634
|
+
throw invalidConfig(`channels[${index}].audio.mode must be "off", "reject", or "transcribe".`);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (value.mode === "transcribe" && !hasTranscriptionConfig) {
|
|
638
|
+
throw invalidConfig(`channels[${index}].audio.mode "transcribe" requires top-level transcription config.`);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (value.mode !== "off") {
|
|
642
|
+
expectString(value.fallbackMessage, `channels[${index}].audio.fallbackMessage`);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
validateOptionalPositiveInteger(value.maxDurationSeconds, `channels[${index}].audio.maxDurationSeconds`);
|
|
646
|
+
validateOptionalPositiveInteger(value.maxBytes, `channels[${index}].audio.maxBytes`);
|
|
647
|
+
}
|
|
648
|
+
|
|
449
649
|
function validateWebsiteChannel(value: Record<string, unknown>, index: number): void {
|
|
450
650
|
if (value.provider !== "agentkit") {
|
|
451
651
|
throw invalidConfig(`channels[${index}].provider must be "agentkit" when type is "website".`);
|
|
@@ -562,6 +762,16 @@ function validateSecretNames(secrets: string[], field: string): void {
|
|
|
562
762
|
}
|
|
563
763
|
}
|
|
564
764
|
|
|
765
|
+
function validateOptionalPositiveInteger(value: unknown, field: string): void {
|
|
766
|
+
if (value === undefined) {
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
771
|
+
throw invalidConfig(`${field} must be a positive integer when provided.`);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
565
775
|
function expectRequiredSecrets(value: unknown, requiredSecrets: string[], field: string): void {
|
|
566
776
|
const declaredSecrets = new Set(Array.isArray(value) ? value : []);
|
|
567
777
|
const missingSecrets = requiredSecrets.filter((secret) => !declaredSecrets.has(secret));
|
|
@@ -2,8 +2,14 @@ import { relative, resolve } from "node:path";
|
|
|
2
2
|
|
|
3
3
|
import type { AgentConfig, AgentTool } from "../../index";
|
|
4
4
|
import type { LoadedAgentCapsule } from "../config";
|
|
5
|
+
import {
|
|
6
|
+
createManagedComposioTools,
|
|
7
|
+
managedComposioRequiredSecrets,
|
|
8
|
+
resolveManagedComposioIntegrations,
|
|
9
|
+
} from "../integrations/composio";
|
|
5
10
|
import { resolveKnowledgeConfig } from "../knowledge/config";
|
|
6
11
|
import { KNOWLEDGE_SEARCH_TOOL_NAME } from "../knowledge/tool";
|
|
12
|
+
import { resolveTranscriptionConfig } from "../transcription";
|
|
7
13
|
|
|
8
14
|
export type ManifestSchema = {
|
|
9
15
|
source: string;
|
|
@@ -27,7 +33,28 @@ export type SharedAgentManifest = {
|
|
|
27
33
|
permissions: string[];
|
|
28
34
|
timeoutMs: number | null;
|
|
29
35
|
}>;
|
|
36
|
+
integrations: Array<{
|
|
37
|
+
name: string;
|
|
38
|
+
provider: "composio";
|
|
39
|
+
mode: "managed";
|
|
40
|
+
toolkits: string[];
|
|
41
|
+
tools: string[];
|
|
42
|
+
userId: string;
|
|
43
|
+
}>;
|
|
30
44
|
channels: NonNullable<AgentConfig["channels"]>;
|
|
45
|
+
transcription: {
|
|
46
|
+
enabled: boolean;
|
|
47
|
+
provider: string | null;
|
|
48
|
+
model: string | null;
|
|
49
|
+
secret: string | null;
|
|
50
|
+
language: string | null;
|
|
51
|
+
prompt: string | null;
|
|
52
|
+
limits: {
|
|
53
|
+
maxDurationSeconds: number | null;
|
|
54
|
+
maxBytes: number | null;
|
|
55
|
+
};
|
|
56
|
+
rawAudioTtlSeconds: number | null;
|
|
57
|
+
};
|
|
31
58
|
knowledge: {
|
|
32
59
|
enabled: boolean;
|
|
33
60
|
sources: Array<{
|
|
@@ -80,6 +107,9 @@ export function buildSharedAgentManifest(
|
|
|
80
107
|
const database = buildDatabaseManifest(capsule, options.databaseSchema ?? null);
|
|
81
108
|
const files = buildFilesManifest(capsule);
|
|
82
109
|
const knowledge = resolveKnowledgeConfig(capsule.config);
|
|
110
|
+
const integrationTools = createManagedComposioTools(capsule.config);
|
|
111
|
+
const tools = [...integrationTools, ...(capsule.config.tools ?? [])];
|
|
112
|
+
const transcription = resolveTranscriptionConfig(capsule.config.transcription);
|
|
83
113
|
|
|
84
114
|
requiredSecrets.add("AGENTKIT_PRIVATE_TOKEN");
|
|
85
115
|
|
|
@@ -100,7 +130,15 @@ export function buildSharedAgentManifest(
|
|
|
100
130
|
requiredSecrets.add(knowledge.embedding.secret);
|
|
101
131
|
}
|
|
102
132
|
|
|
103
|
-
|
|
133
|
+
if (transcription.secret) {
|
|
134
|
+
requiredSecrets.add(transcription.secret);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (const secret of managedComposioRequiredSecrets(capsule.config)) {
|
|
138
|
+
requiredSecrets.add(secret);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
for (const tool of tools) {
|
|
104
142
|
for (const secret of tool.secrets ?? []) {
|
|
105
143
|
requiredSecrets.add(secret);
|
|
106
144
|
}
|
|
@@ -120,7 +158,7 @@ export function buildSharedAgentManifest(
|
|
|
120
158
|
timeZone: capsule.config.timeZone ?? null,
|
|
121
159
|
prompt: toCapsulePath(capsule, capsule.instructionsPath),
|
|
122
160
|
secrets: Array.from(requiredSecrets).sort(),
|
|
123
|
-
tools:
|
|
161
|
+
tools: tools.map((tool) => ({
|
|
124
162
|
name: tool.name,
|
|
125
163
|
description: tool.description,
|
|
126
164
|
inputSchema: tool.inputSchema,
|
|
@@ -129,7 +167,28 @@ export function buildSharedAgentManifest(
|
|
|
129
167
|
permissions: [...(tool.permissions ?? [])].sort(),
|
|
130
168
|
timeoutMs: tool.timeoutMs ?? null,
|
|
131
169
|
})),
|
|
170
|
+
integrations: resolveManagedComposioIntegrations(capsule.config).map((integration) => ({
|
|
171
|
+
name: integration.name,
|
|
172
|
+
provider: integration.provider,
|
|
173
|
+
mode: integration.mode,
|
|
174
|
+
toolkits: integration.toolkits,
|
|
175
|
+
tools: integration.allowedTools,
|
|
176
|
+
userId: integration.userId,
|
|
177
|
+
})),
|
|
132
178
|
channels: capsule.config.channels ?? [],
|
|
179
|
+
transcription: {
|
|
180
|
+
enabled: transcription.enabled,
|
|
181
|
+
provider: transcription.enabled ? transcription.provider : null,
|
|
182
|
+
model: transcription.enabled ? transcription.model : null,
|
|
183
|
+
secret: transcription.secret,
|
|
184
|
+
language: transcription.language,
|
|
185
|
+
prompt: transcription.prompt,
|
|
186
|
+
limits: {
|
|
187
|
+
maxDurationSeconds: transcription.limits.maxDurationSeconds ?? null,
|
|
188
|
+
maxBytes: transcription.limits.maxBytes ?? null,
|
|
189
|
+
},
|
|
190
|
+
rawAudioTtlSeconds: transcription.rawAudioTtlSeconds,
|
|
191
|
+
},
|
|
133
192
|
knowledge: {
|
|
134
193
|
enabled: knowledge.enabled,
|
|
135
194
|
sources: knowledge.sources.map((source) => ({
|
|
@@ -3,6 +3,10 @@ import { createHash } from "node:crypto";
|
|
|
3
3
|
import { loadAgentCapsule } from "./config";
|
|
4
4
|
import { buildSharedAgentManifest } from "./core/manifest";
|
|
5
5
|
import { listCapsuleEnv } from "./env";
|
|
6
|
+
import {
|
|
7
|
+
MANAGED_COMPOSIO_API_KEY_SECRET,
|
|
8
|
+
resolveManagedComposioIntegrations,
|
|
9
|
+
} from "./integrations/composio";
|
|
6
10
|
|
|
7
11
|
export type DeployReadinessSecretSource = {
|
|
8
12
|
dotenv: boolean;
|
|
@@ -30,6 +34,14 @@ export type DeployReadinessContext = {
|
|
|
30
34
|
embeddingProvider: string;
|
|
31
35
|
databaseDriver: "none" | "turso";
|
|
32
36
|
};
|
|
37
|
+
integrations: {
|
|
38
|
+
managedComposio: {
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
toolkits: string[];
|
|
41
|
+
toolCount: number;
|
|
42
|
+
userId: string | null;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
33
45
|
};
|
|
34
46
|
|
|
35
47
|
export async function loadDeployReadinessContext(
|
|
@@ -38,6 +50,7 @@ export async function loadDeployReadinessContext(
|
|
|
38
50
|
): Promise<DeployReadinessContext> {
|
|
39
51
|
const capsule = await loadAgentCapsule(cwd);
|
|
40
52
|
const manifest = buildSharedAgentManifest(capsule);
|
|
53
|
+
const managedComposio = resolveManagedComposioIntegrations(capsule.config)[0];
|
|
41
54
|
const dotenvNames = new Set(await listCapsuleEnv(capsule.root));
|
|
42
55
|
const requiredSecrets = manifest.secrets;
|
|
43
56
|
const localSecrets: Record<string, DeployReadinessSecretSource> = {};
|
|
@@ -57,6 +70,7 @@ export async function loadDeployReadinessContext(
|
|
|
57
70
|
isAgentKitManagedDeploySecret(name, {
|
|
58
71
|
accessMode: capsule.config.access.mode,
|
|
59
72
|
databaseDriver: manifest.storage.database.driver,
|
|
73
|
+
managedComposioEnabled: Boolean(managedComposio),
|
|
60
74
|
}),
|
|
61
75
|
);
|
|
62
76
|
const managed = new Set(agentKitManagedSecrets);
|
|
@@ -82,6 +96,14 @@ export async function loadDeployReadinessContext(
|
|
|
82
96
|
embeddingProvider: manifest.knowledge.embedding.provider,
|
|
83
97
|
databaseDriver: manifest.storage.database.driver,
|
|
84
98
|
},
|
|
99
|
+
integrations: {
|
|
100
|
+
managedComposio: {
|
|
101
|
+
enabled: Boolean(managedComposio),
|
|
102
|
+
toolkits: managedComposio?.toolkits ?? [],
|
|
103
|
+
toolCount: managedComposio?.allowedTools.length ?? 0,
|
|
104
|
+
userId: managedComposio?.userId ?? null,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
85
107
|
};
|
|
86
108
|
}
|
|
87
109
|
|
|
@@ -91,7 +113,11 @@ export function stableCloudProjectId(name: string, accountId?: string | null): s
|
|
|
91
113
|
|
|
92
114
|
function isAgentKitManagedDeploySecret(
|
|
93
115
|
name: string,
|
|
94
|
-
context: {
|
|
116
|
+
context: {
|
|
117
|
+
accessMode: DeployReadinessContext["accessMode"];
|
|
118
|
+
databaseDriver: "none" | "turso";
|
|
119
|
+
managedComposioEnabled: boolean;
|
|
120
|
+
},
|
|
95
121
|
): boolean {
|
|
96
122
|
if (context.databaseDriver === "turso" && (name === "TURSO_DATABASE_URL" || name === "TURSO_AUTH_TOKEN")) {
|
|
97
123
|
return true;
|
|
@@ -101,5 +127,9 @@ function isAgentKitManagedDeploySecret(
|
|
|
101
127
|
return true;
|
|
102
128
|
}
|
|
103
129
|
|
|
130
|
+
if (context.managedComposioEnabled && name === MANAGED_COMPOSIO_API_KEY_SECRET) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
104
134
|
return false;
|
|
105
135
|
}
|
|
@@ -7,7 +7,7 @@ import { Readable } from "node:stream";
|
|
|
7
7
|
|
|
8
8
|
import { openCapsuleStore, type ConversationRecord } from "../storage/sqlite";
|
|
9
9
|
import type { AgentChannel, ChannelProvider, ChannelType } from "../index";
|
|
10
|
-
import type { ChannelAdapter, RawWebhookEvent } from "./channels";
|
|
10
|
+
import type { ChannelAdapter, NormalizedChannelMessage, RawWebhookEvent } from "./channels";
|
|
11
11
|
import { telegramChannelAdapter } from "./channels/telegram";
|
|
12
12
|
import { websiteChannelAdapter } from "./channels/website";
|
|
13
13
|
import { metaWhatsappChannelAdapter } from "./channels/whatsapp-meta";
|
|
@@ -20,6 +20,7 @@ import { loadCapsuleEnv } from "./env";
|
|
|
20
20
|
import { buildInspectState } from "./inspect";
|
|
21
21
|
import { syncConfiguredKnowledgeSources } from "./knowledge/ingest";
|
|
22
22
|
import { runToolFromCwd } from "./tool-runner";
|
|
23
|
+
import { resolveTranscriptionConfig, transcribeAudio } from "./transcription";
|
|
23
24
|
|
|
24
25
|
export type AgentDevServerOptions = {
|
|
25
26
|
port?: number;
|
|
@@ -515,7 +516,9 @@ async function handlePortableChannel(
|
|
|
515
516
|
|
|
516
517
|
channelRuntime.dedupeKeys.add(event.dedupeKey);
|
|
517
518
|
|
|
518
|
-
|
|
519
|
+
const processedMessage = await processDevChannelMessage(capsule, adapter, channel, event.message, secrets);
|
|
520
|
+
|
|
521
|
+
if (event.kind !== "message" || !processedMessage) {
|
|
519
522
|
deliveries.push({ state: "skipped", dedupeKey: event.dedupeKey, unsupportedReason: event.unsupportedReason });
|
|
520
523
|
continue;
|
|
521
524
|
}
|
|
@@ -531,7 +534,7 @@ async function handlePortableChannel(
|
|
|
531
534
|
bufferDevChannelMessage(capsule, channelRuntime, {
|
|
532
535
|
bufferKey: `${channel.name}:${event.externalIdentity.key}`,
|
|
533
536
|
conversationId,
|
|
534
|
-
content:
|
|
537
|
+
content: processedMessage.content,
|
|
535
538
|
receivedAt: event.receivedAt,
|
|
536
539
|
buffer,
|
|
537
540
|
});
|
|
@@ -545,7 +548,7 @@ async function handlePortableChannel(
|
|
|
545
548
|
}
|
|
546
549
|
|
|
547
550
|
const result = await runAgentMessage(capsule, {
|
|
548
|
-
message:
|
|
551
|
+
message: processedMessage.content,
|
|
549
552
|
conversationId,
|
|
550
553
|
signal: request.signal,
|
|
551
554
|
});
|
|
@@ -560,6 +563,71 @@ async function handlePortableChannel(
|
|
|
560
563
|
return jsonResponse({ deliveries });
|
|
561
564
|
}
|
|
562
565
|
|
|
566
|
+
async function processDevChannelMessage(
|
|
567
|
+
capsule: LoadedAgentCapsule,
|
|
568
|
+
adapter: ChannelAdapter,
|
|
569
|
+
channel: AgentChannel,
|
|
570
|
+
message: NormalizedChannelMessage | undefined,
|
|
571
|
+
secrets: Record<string, string>,
|
|
572
|
+
): Promise<NormalizedChannelMessage | null> {
|
|
573
|
+
if (!message) {
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (message.contentType === "text" && message.content) {
|
|
578
|
+
return message;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (message.contentType !== "audio" || !message.audio || channel.audio?.mode !== "transcribe") {
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (!adapter.downloadMedia) {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const env = await loadCapsuleEnv(capsule.root);
|
|
590
|
+
const transcriptionConfig = resolveTranscriptionConfig(capsule.config.transcription);
|
|
591
|
+
const secretName = transcriptionConfig.secret;
|
|
592
|
+
const download = await adapter.downloadMedia({
|
|
593
|
+
type: channel.type,
|
|
594
|
+
provider: channel.provider,
|
|
595
|
+
channelId: channel.name,
|
|
596
|
+
message,
|
|
597
|
+
secrets: {
|
|
598
|
+
...secrets,
|
|
599
|
+
...(secretName ? { [secretName]: env[secretName] ?? "" } : {}),
|
|
600
|
+
...(env.AGENTKIT_TELEGRAM_API_BASE_URL ? { AGENTKIT_TELEGRAM_API_BASE_URL: env.AGENTKIT_TELEGRAM_API_BASE_URL } : {}),
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
if (!download.ok) {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const transcription = await transcribeAudio({
|
|
609
|
+
config: transcriptionConfig,
|
|
610
|
+
secrets: {
|
|
611
|
+
...(secretName ? { [secretName]: env[secretName] ?? "" } : {}),
|
|
612
|
+
},
|
|
613
|
+
audio: download.audio,
|
|
614
|
+
filename: download.filename,
|
|
615
|
+
...(download.mimeType ? { mimeType: download.mimeType } : {}),
|
|
616
|
+
...(download.durationSeconds !== undefined ? { durationSeconds: download.durationSeconds } : {}),
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
if (!transcription.ok) {
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
return {
|
|
624
|
+
role: "user",
|
|
625
|
+
contentType: "text",
|
|
626
|
+
content: `The user sent an audio message.\n\nTranscript:\n${transcription.text}`,
|
|
627
|
+
providerMessageId: message.providerMessageId,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
|
|
563
631
|
function bufferDevChannelMessage(
|
|
564
632
|
capsule: LoadedAgentCapsule,
|
|
565
633
|
runtime: DevChannelRuntimeState,
|