@andreprado/agentkit 0.1.0-alpha.16 → 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 (32) hide show
  1. package/README.md +8 -0
  2. package/docs/guides/add-channel.md +2 -0
  3. package/docs/guides/add-managed-composio.md +137 -0
  4. package/docs/guides/channels-production-handoff.md +1 -1
  5. package/docs/guides/connect-telegram.md +3 -1
  6. package/docs/guides/prepare-deploy.md +3 -1
  7. package/docs/llms-full.txt +13 -1
  8. package/docs/llms.txt +7 -0
  9. package/package.json +1 -1
  10. package/src/cli/commands/channels.ts +119 -7
  11. package/src/cli/commands/transcribe.ts +171 -0
  12. package/src/cli/deploy-readiness.ts +59 -3
  13. package/src/cli/help.ts +15 -0
  14. package/src/cli/index.ts +130 -0
  15. package/src/create-project.ts +4 -32
  16. package/src/index.ts +145 -0
  17. package/src/runtime/chat.ts +2 -1
  18. package/src/runtime/config.ts +129 -0
  19. package/src/runtime/core/manifest.ts +29 -2
  20. package/src/runtime/deploy-readiness.ts +31 -1
  21. package/src/runtime/inspect.ts +108 -4
  22. package/src/runtime/integrations/composio.ts +257 -0
  23. package/src/runtime/skills.ts +95 -0
  24. package/src/runtime/targets/cloudflare/build.ts +160 -5
  25. package/src/runtime/tool-runner.ts +2 -1
  26. package/src/templates/skills/agentkit-capsule/SKILL.md +1 -0
  27. package/src/templates/skills/agentkit-channels/SKILL.md +2 -0
  28. package/src/templates/skills/agentkit-channels/references/channel-debugging.md +3 -1
  29. package/src/templates/skills/agentkit-channels/references/telegram.md +3 -1
  30. package/src/templates/skills/agentkit-deploy/SKILL.md +1 -1
  31. package/src/templates/skills/agentkit-integrations/SKILL.md +66 -0
  32. package/src/templates/skills/agentkit-troubleshooting/SKILL.md +3 -2
package/src/index.ts CHANGED
@@ -145,6 +145,30 @@ export type WhatsappChannelConfig = {
145
145
 
146
146
  export type AgentChannel = WebsiteChannelConfig | TelegramChannelConfig | WhatsappChannelConfig;
147
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
+
148
172
  export type AgentDatabaseConfig =
149
173
  | {
150
174
  driver: "none";
@@ -316,6 +340,7 @@ export type AgentConfig = {
316
340
  instructions: string;
317
341
  secrets: string[];
318
342
  tools?: AgentTool[];
343
+ integrations?: AgentIntegration[];
319
344
  channels?: AgentChannel[];
320
345
  transcription?: AgentTranscriptionConfig;
321
346
  knowledge?: AgentKnowledgeConfig;
@@ -338,6 +363,23 @@ export function defineTool<Input = unknown, Output = unknown>(tool: AgentTool<In
338
363
  return tool;
339
364
  }
340
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
+
341
383
  export function websiteChannel(input: WebsiteChannelInput): WebsiteChannelConfig {
342
384
  return {
343
385
  type: "website",
@@ -404,6 +446,109 @@ function normalizeChannelSecrets(secrets: string[], context: string): string[] {
404
446
  return Array.from(uniqueSecrets).sort();
405
447
  }
406
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
+
407
552
  function normalizeChannelBuffer(input: ChannelBufferInput, context: string): ChannelBufferConfig {
408
553
  if (input.mode === "off") {
409
554
  return { mode: "off" };
@@ -7,6 +7,7 @@ import { loadAgentCapsule } from "./config";
7
7
  import { openPreparedCapsuleStore } from "./database";
8
8
  import { loadCapsuleEnv } from "./env";
9
9
  import { AgentKitError, isAgentKitError } from "./errors";
10
+ import { createManagedComposioTools } from "./integrations/composio";
10
11
  import { syncConfiguredKnowledgeSources } from "./knowledge/ingest";
11
12
  import { appendKnowledgePromptPolicy } from "./knowledge/prompt-policy";
12
13
  import { createKnowledgeSearchTool } from "./knowledge/tool";
@@ -145,7 +146,7 @@ export async function runAgentMessage(
145
146
  }
146
147
 
147
148
  function withKnowledgeTools(capsule: LoadedAgentCapsule): NonNullable<LoadedAgentCapsule["config"]["tools"]> {
148
- const tools = [...(capsule.config.tools ?? [])];
149
+ const tools = [...createManagedComposioTools(capsule.config), ...(capsule.config.tools ?? [])];
149
150
  const knowledgeTool = createKnowledgeSearchTool(capsule);
150
151
 
151
152
  if (knowledgeTool) {
@@ -16,9 +16,11 @@ import type {
16
16
  TelegramAllowedUpdate,
17
17
  AgentKnowledgeEmbeddingProvider,
18
18
  ChannelBufferMode,
19
+ AgentIntegration,
19
20
  ChannelAudioMode,
20
21
  TranscriptionProviderName,
21
22
  } from "../index";
23
+ import { MANAGED_COMPOSIO_TOOL_NAME } from "./integrations/composio";
22
24
  import { AgentKitError } from "./errors";
23
25
  import { isValidTimeZone } from "./prompt-context";
24
26
 
@@ -35,6 +37,9 @@ const TELEGRAM_ALLOWED_UPDATES = new Set<TelegramAllowedUpdate>(["message"]);
35
37
  const KNOWLEDGE_EMBEDDING_PROVIDERS = new Set<AgentKnowledgeEmbeddingProvider>(["none", "test", "openai"]);
36
38
  const TRANSCRIPTION_PROVIDERS = new Set<TranscriptionProviderName>(["test", "openai", "groq"]);
37
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_]*$/;
38
43
  const SECRET_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
39
44
  const moduleDir = dirname(fileURLToPath(import.meta.url));
40
45
  let configImportNonce = 0;
@@ -234,6 +239,7 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
234
239
  }
235
240
  }
236
241
 
242
+ validateIntegrations(value.integrations, value.runtime as AgentRuntime, value.tools);
237
243
  validateTranscriptionConfig(value.transcription);
238
244
  validateChannels(value.channels, value.runtime as AgentRuntime, Boolean(value.transcription));
239
245
  validateKnowledgeConfig(value.knowledge);
@@ -258,6 +264,129 @@ function validateAgentConfig(value: unknown): asserts value is AgentConfig {
258
264
  validateFilesConfig(value.storage.files);
259
265
  }
260
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
+
261
390
  function validateTranscriptionConfig(value: unknown): void {
262
391
  if (value === undefined) {
263
392
  return;
@@ -2,6 +2,11 @@ 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";
7
12
  import { resolveTranscriptionConfig } from "../transcription";
@@ -28,6 +33,14 @@ export type SharedAgentManifest = {
28
33
  permissions: string[];
29
34
  timeoutMs: number | null;
30
35
  }>;
36
+ integrations: Array<{
37
+ name: string;
38
+ provider: "composio";
39
+ mode: "managed";
40
+ toolkits: string[];
41
+ tools: string[];
42
+ userId: string;
43
+ }>;
31
44
  channels: NonNullable<AgentConfig["channels"]>;
32
45
  transcription: {
33
46
  enabled: boolean;
@@ -94,6 +107,8 @@ export function buildSharedAgentManifest(
94
107
  const database = buildDatabaseManifest(capsule, options.databaseSchema ?? null);
95
108
  const files = buildFilesManifest(capsule);
96
109
  const knowledge = resolveKnowledgeConfig(capsule.config);
110
+ const integrationTools = createManagedComposioTools(capsule.config);
111
+ const tools = [...integrationTools, ...(capsule.config.tools ?? [])];
97
112
  const transcription = resolveTranscriptionConfig(capsule.config.transcription);
98
113
 
99
114
  requiredSecrets.add("AGENTKIT_PRIVATE_TOKEN");
@@ -119,7 +134,11 @@ export function buildSharedAgentManifest(
119
134
  requiredSecrets.add(transcription.secret);
120
135
  }
121
136
 
122
- for (const tool of capsule.config.tools ?? []) {
137
+ for (const secret of managedComposioRequiredSecrets(capsule.config)) {
138
+ requiredSecrets.add(secret);
139
+ }
140
+
141
+ for (const tool of tools) {
123
142
  for (const secret of tool.secrets ?? []) {
124
143
  requiredSecrets.add(secret);
125
144
  }
@@ -139,7 +158,7 @@ export function buildSharedAgentManifest(
139
158
  timeZone: capsule.config.timeZone ?? null,
140
159
  prompt: toCapsulePath(capsule, capsule.instructionsPath),
141
160
  secrets: Array.from(requiredSecrets).sort(),
142
- tools: (capsule.config.tools ?? []).map((tool) => ({
161
+ tools: tools.map((tool) => ({
143
162
  name: tool.name,
144
163
  description: tool.description,
145
164
  inputSchema: tool.inputSchema,
@@ -148,6 +167,14 @@ export function buildSharedAgentManifest(
148
167
  permissions: [...(tool.permissions ?? [])].sort(),
149
168
  timeoutMs: tool.timeoutMs ?? null,
150
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
+ })),
151
178
  channels: capsule.config.channels ?? [],
152
179
  transcription: {
153
180
  enabled: transcription.enabled,
@@ -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: { accessMode: DeployReadinessContext["accessMode"]; databaseDriver: "none" | "turso" },
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
  }
@@ -4,9 +4,15 @@ import type { AgentChannel, AgentProvider, AgentRuntime, AccessMode } from "../i
4
4
  import type { LoadedAgentCapsule } from "./config";
5
5
  import { loadAgentCapsule } from "./config";
6
6
  import { loadCapsuleEnv } from "./env";
7
+ import {
8
+ createManagedComposioTools,
9
+ managedComposioRequiredSecrets,
10
+ resolveManagedComposioIntegrations,
11
+ } from "./integrations/composio";
7
12
  import { resolveKnowledgeConfig } from "./knowledge/config";
8
13
  import { KNOWLEDGE_SEARCH_TOOL_NAME } from "./knowledge/tool";
9
14
  import { resolveRuntimeTimeZone } from "./prompt-context";
15
+ import { checkDefaultSkillStatus, type DefaultSkillStatus } from "./skills";
10
16
  import { resolveTranscriptionConfig } from "./transcription";
11
17
 
12
18
  export type SecretState = "set" | "missing";
@@ -21,6 +27,7 @@ export type AgentInspectState = {
21
27
  };
22
28
  prompt: string;
23
29
  tools: string[];
30
+ integrations: AgentInspectIntegration[];
24
31
  channels: AgentInspectChannel[];
25
32
  transcription: {
26
33
  enabled: boolean;
@@ -71,6 +78,8 @@ export type AgentInspectState = {
71
78
  secrets: Record<string, SecretState>;
72
79
  managedSecrets: string[];
73
80
  userSecrets: string[];
81
+ skills: AgentInspectSkills;
82
+ warnings: AgentInspectWarning[];
74
83
  };
75
84
 
76
85
  export type AgentInspectDatabase = {
@@ -97,23 +106,48 @@ export type AgentInspectChannel = {
97
106
  audio?: AgentChannel["audio"];
98
107
  };
99
108
 
109
+ export type AgentInspectSkills = {
110
+ status: "current" | "missing" | "outdated" | "not_checked";
111
+ checked: number;
112
+ missing: string[];
113
+ outdated: string[];
114
+ syncCommand: string | null;
115
+ };
116
+
117
+ export type AgentInspectWarning = {
118
+ code: string;
119
+ message: string;
120
+ command?: string;
121
+ };
122
+
123
+ export type AgentInspectIntegration = {
124
+ name: string;
125
+ provider: "composio";
126
+ mode: "managed";
127
+ toolkits: string[];
128
+ tools: string[];
129
+ userId: string;
130
+ };
131
+
100
132
  export async function inspectAgentCapsule(
101
133
  cwd = process.cwd(),
102
134
  env: Record<string, string | undefined> = process.env,
103
135
  ): Promise<AgentInspectState> {
104
136
  const capsule = await loadAgentCapsule(cwd);
105
- return buildInspectState(capsule, await loadCapsuleEnv(capsule.root, env));
137
+ return buildInspectState(capsule, await loadCapsuleEnv(capsule.root, env), await checkDefaultSkillStatus(capsule.root));
106
138
  }
107
139
 
108
140
  export function buildInspectState(
109
141
  capsule: LoadedAgentCapsule,
110
142
  env: Record<string, string | undefined> = process.env,
143
+ skillStatus: DefaultSkillStatus | null = null,
111
144
  ): AgentInspectState {
112
145
  const declaredSecrets = new Set(capsule.config.secrets);
113
146
  const knowledge = resolveKnowledgeConfig(capsule.config);
147
+ const integrationTools = createManagedComposioTools(capsule.config);
114
148
  const transcription = resolveTranscriptionConfig(capsule.config.transcription);
115
149
 
116
- for (const tool of capsule.config.tools ?? []) {
150
+ for (const tool of [...integrationTools, ...(capsule.config.tools ?? [])]) {
117
151
  for (const secret of tool.secrets ?? []) {
118
152
  declaredSecrets.add(secret);
119
153
  }
@@ -134,7 +168,10 @@ export function buildInspectState(
134
168
  }
135
169
 
136
170
  const managedSecrets = managedCloudSecrets(capsule);
137
- const userSecrets = Array.from(declaredSecrets).sort();
171
+ const managedSecretSet = new Set(managedSecrets);
172
+ const userSecrets = Array.from(declaredSecrets)
173
+ .filter((secret) => !managedSecretSet.has(secret))
174
+ .sort();
138
175
  const secrets: Record<string, SecretState> = {};
139
176
 
140
177
  for (const secret of userSecrets) {
@@ -143,6 +180,9 @@ export function buildInspectState(
143
180
 
144
181
  const database = inspectDatabase(capsule);
145
182
 
183
+ const skills = inspectSkills(skillStatus);
184
+ const warnings = inspectWarnings(capsule, transcription, skills);
185
+
146
186
  return {
147
187
  agent: capsule.config.name,
148
188
  runtime: capsule.config.runtime,
@@ -155,7 +195,15 @@ export function buildInspectState(
155
195
  }),
156
196
  },
157
197
  prompt: toCapsulePath(capsule.root, capsule.instructionsPath),
158
- tools: (capsule.config.tools ?? []).map((tool) => tool.name),
198
+ tools: [...integrationTools, ...(capsule.config.tools ?? [])].map((tool) => tool.name),
199
+ integrations: resolveManagedComposioIntegrations(capsule.config).map((integration) => ({
200
+ name: integration.name,
201
+ provider: integration.provider,
202
+ mode: integration.mode,
203
+ toolkits: integration.toolkits,
204
+ tools: integration.allowedTools,
205
+ userId: integration.userId,
206
+ })),
159
207
  channels: (capsule.config.channels ?? []).map((channel) => ({
160
208
  name: channel.name,
161
209
  type: channel.type,
@@ -208,6 +256,8 @@ export function buildInspectState(
208
256
  secrets,
209
257
  managedSecrets,
210
258
  userSecrets,
259
+ skills,
260
+ warnings,
211
261
  };
212
262
  }
213
263
 
@@ -237,6 +287,56 @@ function inspectDatabase(capsule: LoadedAgentCapsule): AgentInspectDatabase {
237
287
  };
238
288
  }
239
289
 
290
+ function inspectSkills(status: DefaultSkillStatus | null): AgentInspectSkills {
291
+ if (!status) {
292
+ return {
293
+ status: "not_checked",
294
+ checked: 0,
295
+ missing: [],
296
+ outdated: [],
297
+ syncCommand: null,
298
+ };
299
+ }
300
+
301
+ return {
302
+ status: status.status,
303
+ checked: status.checked,
304
+ missing: status.missing,
305
+ outdated: status.outdated,
306
+ syncCommand: status.status === "current" ? null : "npm run agentkit -- skills sync",
307
+ };
308
+ }
309
+
310
+ function inspectWarnings(
311
+ capsule: LoadedAgentCapsule,
312
+ transcription: ReturnType<typeof resolveTranscriptionConfig>,
313
+ skills: AgentInspectSkills,
314
+ ): AgentInspectWarning[] {
315
+ const warnings: AgentInspectWarning[] = [];
316
+
317
+ if (skills.status === "missing" || skills.status === "outdated") {
318
+ warnings.push({
319
+ code: "local_skills_outdated",
320
+ message: "Local AgentKit skills are older than or missing from the installed package docs.",
321
+ command: "npm run agentkit -- skills sync",
322
+ });
323
+ }
324
+
325
+ if (
326
+ transcription.enabled &&
327
+ transcription.provider === "openai" &&
328
+ (capsule.config.channels ?? []).some((channel) => channel.type === "telegram" && channel.audio?.mode === "transcribe")
329
+ ) {
330
+ warnings.push({
331
+ code: "telegram_audio_openai_format",
332
+ message: "Telegram voice notes arrive as OGG/Opus; use Groq for the default Telegram voice-note transcription path.",
333
+ command: "Set transcription.provider to \"groq\" and secret to \"GROQ_API_KEY\".",
334
+ });
335
+ }
336
+
337
+ return warnings;
338
+ }
339
+
240
340
  function managedCloudSecrets(capsule: LoadedAgentCapsule): string[] {
241
341
  const secrets = new Set<string>();
242
342
 
@@ -245,5 +345,9 @@ function managedCloudSecrets(capsule: LoadedAgentCapsule): string[] {
245
345
  secrets.add("TURSO_DATABASE_URL");
246
346
  }
247
347
 
348
+ for (const secret of managedComposioRequiredSecrets(capsule.config)) {
349
+ secrets.add(secret);
350
+ }
351
+
248
352
  return Array.from(secrets).sort();
249
353
  }