@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
@@ -0,0 +1,171 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { basename, extname, resolve } from "node:path";
3
+
4
+ import type { TranscriptionProviderName } from "../../index";
5
+ import { findAgentCapsuleRoot, loadAgentCapsule } from "../../runtime/config";
6
+ import { loadCapsuleEnv } from "../../runtime/env";
7
+ import {
8
+ defaultTranscriptionSecret,
9
+ resolveTranscriptionConfig,
10
+ transcribeAudio,
11
+ type ResolvedTranscriptionConfig,
12
+ } from "../../runtime/transcription";
13
+ import type { ParsedArgs } from "../args";
14
+
15
+ type SmokeFixture = {
16
+ audio: Uint8Array;
17
+ filename: string;
18
+ mimeType: string;
19
+ source: string;
20
+ };
21
+
22
+ const defaultModels: Record<TranscriptionProviderName, string> = {
23
+ test: "fake",
24
+ openai: "gpt-4o-mini-transcribe",
25
+ groq: "whisper-large-v3-turbo",
26
+ };
27
+
28
+ // Short macOS-generated spoken "hello" sample encoded as M4A. M4A is supported by OpenAI and Groq.
29
+ const builtinSmokeM4aBase64 =
30
+ "AAAAHGZ0eXBNNEEgAAAAAE00QSBtcDQyaXNvbQAAA3ptb292AAAAbG12aGQAAAAA5kTZseZE2bEAAFYiAAA4AAABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACDHRyYWsAAABcdGtoZAAAAAfmRNmx5kTZsQAAAAEAAAAAAAA4AAAAAAAAAAAAAAAAAAEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAahtZGlhAAAAIG1kaGQAAAAA5kTZseZE2bEAAFYiAAA4AAAAAAAAAAAiaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAAAAAAABXm1pbmYAAAAQc21oZAAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAABInN0YmwAAAB2c3RzZAAAAAAAAAABAAAAZm1wNGEAAAAAAAAAAQAAAAAAAAAAAAIAEAAAAABWIgAAAAAAM2VzZHMAAAAAA4CAgCIAAAAEgICAFEAUABgAAAAU+AAAfQAFgICAAhOIBoCAgAECAAAAD3NidGQAAAAASTE2AAAAGHN0dHMAAAAAAAAAAQAAAA4AAAQAAAAAKHN0c2MAAAAAAAAAAgAAAAEAAAALAAAAAQAAAAIAAAADAAAAAQAAAExzdHN6AAAAAAAAAAAAAAAOAAAABAAAAbkAAADiAAAA0wAAAL8AAADLAAAAzAAAAJMAAACmAAAA5wAAALcAAACnAAAAtgAAADoAAAAYc3RjbwAAAAAAAAACAAAQAAAAGJ8AAAD6dWR0YQAAAPJtZXRhAAAAAAAAACJoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAAADEaWxzdAAAALwtLS0tAAAAHG1lYW4AAAAAY29tLmFwcGxlLmlUdW5lcwAAABRuYW1lAAAAAGlUdW5TTVBCAAAAhGRhdGEAAAABAAAAACAwMDAwMDAwMCAwMDAwMDg0MCAwMDAwMDEwOCAwMDAwMDAwMDAwMDAyRUI4IDAwMDAwMDAwIDAwMDAwMDAwIDAwMDAwMDAwIDAwMDAwMDAwIDAwMDAwMDAwIDAwMDAwMDAwIDAwMDAwMDAwIDAwMDAwMDAwAAAMYmZyZWUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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";
31
+
32
+ export async function handleTranscribeCommand(args: ParsedArgs): Promise<void> {
33
+ const [subcommand] = args.positional;
34
+
35
+ if (subcommand !== "smoke") {
36
+ throw new Error("Usage: agentkit transcribe smoke [--provider test|openai|groq] [--model <model>] [--fixture <audio-file>]");
37
+ }
38
+
39
+ const root = await findAgentCapsuleRoot(process.cwd());
40
+ const capsule = await loadAgentCapsule(root);
41
+ const env = compactEnv(await loadCapsuleEnv(root, process.env));
42
+ const configured = resolveTranscriptionConfig(capsule.config.transcription);
43
+ const provider = parseProviderFlag(args.flags.provider, configured);
44
+ const model = typeof args.flags.model === "string" ? args.flags.model : configured.enabled && configured.provider === provider ? configured.model : defaultModels[provider];
45
+ const secret = typeof args.flags.secret === "string"
46
+ ? args.flags.secret
47
+ : configured.enabled && configured.provider === provider
48
+ ? configured.secret
49
+ : defaultTranscriptionSecret(provider);
50
+ const fixture = await loadSmokeFixture(provider, args.flags.fixture);
51
+
52
+ const config: ResolvedTranscriptionConfig = {
53
+ enabled: true,
54
+ provider,
55
+ model,
56
+ secret,
57
+ language: typeof args.flags.language === "string" ? args.flags.language : configured.enabled ? configured.language : null,
58
+ prompt: typeof args.flags.prompt === "string" ? args.flags.prompt : configured.enabled ? configured.prompt : null,
59
+ limits: configured.enabled ? configured.limits : {},
60
+ rawAudioTtlSeconds: configured.enabled ? configured.rawAudioTtlSeconds : 3600,
61
+ };
62
+ const result = await transcribeAudio({
63
+ config,
64
+ secrets: env,
65
+ audio: fixture.audio,
66
+ filename: fixture.filename,
67
+ mimeType: fixture.mimeType,
68
+ });
69
+
70
+ console.log("Transcription smoke");
71
+ console.log(`Provider: ${provider}`);
72
+ console.log(`Model: ${model}`);
73
+ console.log(`Fixture: ${fixture.source} (${fixture.mimeType})`);
74
+
75
+ if (result.ok) {
76
+ console.log("Status: ok");
77
+ console.log(`Transcript: ${JSON.stringify(result.text)}`);
78
+ return;
79
+ }
80
+
81
+ console.log("Status: failed");
82
+ console.log(`Code: ${result.code}`);
83
+ console.log(`Message: ${result.message}`);
84
+ process.exitCode = 1;
85
+ }
86
+
87
+ function parseProviderFlag(
88
+ value: string | boolean | undefined,
89
+ configured: ResolvedTranscriptionConfig,
90
+ ): TranscriptionProviderName {
91
+ if (value === undefined || value === false) {
92
+ return configured.enabled ? configured.provider : "groq";
93
+ }
94
+
95
+ if (value === "test" || value === "openai" || value === "groq") {
96
+ return value;
97
+ }
98
+
99
+ throw new Error("Usage: agentkit transcribe smoke [--provider test|openai|groq] [--model <model>] [--fixture <audio-file>]");
100
+ }
101
+
102
+ function compactEnv(env: Record<string, string | undefined>): Record<string, string> {
103
+ const compact: Record<string, string> = {};
104
+
105
+ for (const [name, value] of Object.entries(env)) {
106
+ if (value !== undefined) {
107
+ compact[name] = value;
108
+ }
109
+ }
110
+
111
+ return compact;
112
+ }
113
+
114
+ async function loadSmokeFixture(
115
+ provider: TranscriptionProviderName,
116
+ fixtureFlag: string | boolean | undefined,
117
+ ): Promise<SmokeFixture> {
118
+ if (typeof fixtureFlag === "string") {
119
+ const filename = resolve(process.cwd(), fixtureFlag);
120
+ return {
121
+ audio: new Uint8Array(await readFile(filename)),
122
+ filename: basename(filename),
123
+ mimeType: mimeTypeForFilename(filename),
124
+ source: fixtureFlag,
125
+ };
126
+ }
127
+
128
+ if (provider === "test") {
129
+ return {
130
+ audio: new TextEncoder().encode("hello"),
131
+ filename: "agentkit-smoke.ogg",
132
+ mimeType: "audio/ogg",
133
+ source: "built-in text fixture",
134
+ };
135
+ }
136
+
137
+ return {
138
+ audio: new Uint8Array(Buffer.from(builtinSmokeM4aBase64, "base64")),
139
+ filename: "agentkit-transcribe-smoke.m4a",
140
+ mimeType: "audio/m4a",
141
+ source: "built-in spoken hello",
142
+ };
143
+ }
144
+
145
+ function mimeTypeForFilename(filename: string): string {
146
+ const extension = extname(filename).toLowerCase();
147
+
148
+ if (extension === ".ogg" || extension === ".opus") {
149
+ return "audio/ogg";
150
+ }
151
+ if (extension === ".mp3" || extension === ".mpeg" || extension === ".mpga") {
152
+ return "audio/mpeg";
153
+ }
154
+ if (extension === ".m4a") {
155
+ return "audio/m4a";
156
+ }
157
+ if (extension === ".mp4") {
158
+ return "audio/mp4";
159
+ }
160
+ if (extension === ".wav") {
161
+ return "audio/wav";
162
+ }
163
+ if (extension === ".webm") {
164
+ return "audio/webm";
165
+ }
166
+ if (extension === ".flac") {
167
+ return "audio/flac";
168
+ }
169
+
170
+ return "application/octet-stream";
171
+ }
@@ -2,6 +2,7 @@ import { findAgentCapsuleRoot, loadAgentCapsule } from "../runtime/config";
2
2
  import { loadDeployReadinessContext, type DeployReadinessContext } from "../runtime/deploy-readiness";
3
3
  import { loadCapsuleEnv } from "../runtime/env";
4
4
  import { isAgentKitError } from "../runtime/errors";
5
+ import { MANAGED_COMPOSIO_ENTITLEMENT } from "../runtime/integrations/composio";
5
6
  import {
6
7
  cloudApiRequest,
7
8
  formatCloudAuthSource,
@@ -53,6 +54,22 @@ export async function checkDeployReadiness(options: {
53
54
  });
54
55
  }
55
56
 
57
+ const nodeMajor = majorNodeVersion(process.versions.node);
58
+ if (nodeMajor !== null && nodeMajor < 24) {
59
+ checks.push({
60
+ status: "warn",
61
+ title: "Node version",
62
+ message: `@andreprado/agentkit requires Node >=24; current Node is ${process.versions.node}.`,
63
+ action: "use Node 24+ before running direct node imports or production container commands",
64
+ });
65
+ } else {
66
+ checks.push({
67
+ status: "pass",
68
+ title: "Node version",
69
+ message: `Node ${process.versions.node} satisfies @andreprado/agentkit engines.node >=24.`,
70
+ });
71
+ }
72
+
56
73
  if (!auth?.token) {
57
74
  checks.push({
58
75
  status: options.requireLogin ? "fail" : "warn",
@@ -104,6 +121,9 @@ export async function checkDeployReadiness(options: {
104
121
  const hasCloudflareAlpha = capabilities.some(
105
122
  (capability) => capability.name === "cloudflare_deploy_alpha" && capability.status === "active",
106
123
  );
124
+ const hasManagedComposio = capabilities.some(
125
+ (capability) => capability.name === MANAGED_COMPOSIO_ENTITLEMENT && capability.status === "active",
126
+ );
107
127
 
108
128
  checks.push({
109
129
  status: hasCloudflareAlpha ? "pass" : "fail",
@@ -113,6 +133,17 @@ export async function checkDeployReadiness(options: {
113
133
  : "This account is missing the cloudflare_deploy_alpha entitlement.",
114
134
  ...(hasCloudflareAlpha ? {} : { action: "log in with an invited AgentKit Cloud alpha token" }),
115
135
  });
136
+
137
+ if (context.integrations.managedComposio.enabled) {
138
+ checks.push({
139
+ status: hasManagedComposio ? "pass" : "fail",
140
+ title: "Managed Composio entitlement",
141
+ message: hasManagedComposio
142
+ ? `${MANAGED_COMPOSIO_ENTITLEMENT} is active.`
143
+ : `This capsule uses AgentKit-managed Composio, but this account is missing ${MANAGED_COMPOSIO_ENTITLEMENT}.`,
144
+ ...(hasManagedComposio ? {} : { action: "upgrade the AgentKit Cloud account or ask an operator to grant managed_composio" }),
145
+ });
146
+ }
116
147
  } catch (error) {
117
148
  checks.push({
118
149
  status: "fail",
@@ -184,6 +215,25 @@ export async function checkDeployReadiness(options: {
184
215
  });
185
216
  }
186
217
 
218
+ if (context.integrations.managedComposio.enabled) {
219
+ if (!options.requireLogin) {
220
+ checks.push({
221
+ status: "fail",
222
+ title: "Managed Composio access",
223
+ message: "AgentKit-managed Composio is paid hosted infrastructure and cannot be used by anonymous deploys.",
224
+ action: "run agentkit deploy without --anonymous after logging in with a paid AgentKit Cloud account",
225
+ });
226
+ } else {
227
+ checks.push({
228
+ status: "info",
229
+ title: "Managed Composio",
230
+ message: `Configured for ${context.integrations.managedComposio.toolkits.join(", ")} with ${
231
+ context.integrations.managedComposio.toolCount
232
+ } allowed action${context.integrations.managedComposio.toolCount === 1 ? "" : "s"}. AgentKit Cloud injects COMPOSIO_API_KEY.`,
233
+ });
234
+ }
235
+ }
236
+
187
237
  if (context.accessMode === "private") {
188
238
  checks.push({
189
239
  status: "info",
@@ -350,20 +400,26 @@ async function resolveCloudProjectId(apiUrl: string, projectName: string): Promi
350
400
 
351
401
  function secretSetCommand(secret: string, source: DeployReadinessContext["localSecrets"][string] | undefined): string {
352
402
  if (source?.dotenv) {
353
- return `agentkit secret set ${secret} --from-local-env`;
403
+ return `npm run agentkit -- secret set ${secret} --from-local-env`;
354
404
  }
355
405
 
356
406
  if (source?.environment) {
357
- return `agentkit secret set ${secret} --from-env`;
407
+ return `npm run agentkit -- secret set ${secret} --from-env`;
358
408
  }
359
409
 
360
- return `agentkit secret set ${secret} --stdin`;
410
+ return `npm run agentkit -- secret set ${secret} --stdin`;
361
411
  }
362
412
 
363
413
  function normalizeOptionalNumber(value: number | null | undefined): number | undefined {
364
414
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
365
415
  }
366
416
 
417
+ function majorNodeVersion(version: string): number | null {
418
+ const [major] = version.split(".");
419
+ const parsed = Number(major);
420
+ return Number.isInteger(parsed) ? parsed : null;
421
+ }
422
+
367
423
  function isString(value: unknown): value is string {
368
424
  return typeof value === "string";
369
425
  }
package/src/cli/help.ts CHANGED
@@ -41,6 +41,7 @@ Deploy:
41
41
 
42
42
  More:
43
43
  agentkit channels ...
44
+ agentkit integrations status
44
45
  agentkit access token ...
45
46
  agentkit env ...
46
47
  agentkit docs full
@@ -82,6 +83,7 @@ Usage:
82
83
  agentkit channels status <name> [--api <url>]
83
84
  agentkit channels doctor <name> [--api <url>]
84
85
  agentkit channels test <name> [--message <text>] [--fixture <path>] [--api <url>]
86
+ agentkit channels test-audio <name> [--fixture voice-note|audio-file|<path>] [--api <url>]
85
87
  agentkit channels deliveries list <name> [--api <url>]
86
88
  agentkit channels deliveries show <delivery-id> [--api <url>]
87
89
  agentkit channels buffers list <name> [--api <url>]
@@ -89,6 +91,10 @@ Usage:
89
91
  agentkit channels buffers flush [<name>] <conversation-id> [--api <url>]
90
92
  agentkit channels buffers clear [<name>] <conversation-id> [--api <url>]
91
93
  agentkit channels buffers retry [<name>] <conversation-id> [--api <url>]
94
+ agentkit integrations status [--api <url>]
95
+ agentkit integrations connect composio [--toolkit <slug>] [--callback-url <url>] [--api <url>]
96
+ agentkit skills status
97
+ agentkit skills sync
92
98
  agentkit inspect
93
99
  agentkit build [--target cloudflare|container]
94
100
  agentkit login --token <token> [--api <url>]
@@ -99,6 +105,7 @@ Usage:
99
105
  agentkit deploy status [--api <url>]
100
106
  agentkit deploy pause [--api <url>]
101
107
  agentkit deploy resume [--api <url>]
108
+ agentkit transcribe smoke [--provider test|openai|groq] [--model <model>] [--fixture <audio-file>]
102
109
  agentkit secret set <NAME> <VALUE> [--api <url>]
103
110
  agentkit secret set <NAME> --stdin [--api <url>]
104
111
  agentkit secret set <NAME> --from-env [ENV_NAME] [--api <url>]
@@ -135,6 +142,9 @@ Commands:
135
142
  eval Run local eval files or create one from a conversation
136
143
  conversations List, inspect, and trace stored local conversations
137
144
  channels Manage hosted channel resources
145
+ integrations Manage hosted AgentKit-managed integrations
146
+ skills Check or refresh local AgentKit skill files
147
+ transcribe Smoke-test audio transcription providers
138
148
  inspect Print machine-readable Agent Capsule state
139
149
  build Build a hosted deploy artifact
140
150
  login Store an AgentKit Cloud alpha token outside the capsule
@@ -173,11 +183,16 @@ export function renderCommandHelp(command: string): string {
173
183
  eval: "agentkit eval run | agentkit eval from-conversation <conversation-id> [--out <path>] [--force]",
174
184
  conversations:
175
185
  "agentkit conversations list | agentkit conversations show <conversation-id> | agentkit conversations trace <conversation-id>",
186
+ channels:
187
+ "agentkit channels list | add | connect | setup | status | doctor | test | test-audio | deliveries | buffers",
188
+ skills: "agentkit skills status | agentkit skills sync",
189
+ transcribe: "agentkit transcribe smoke [--provider test|openai|groq] [--model <model>] [--fixture <audio-file>]",
176
190
  deploy:
177
191
  'agentkit deploy [--smoke "hello"] | agentkit deploy smoke [--message "hello"] | agentkit deploy doctor | status | pause | resume',
178
192
  secret:
179
193
  "agentkit secret set <NAME> --stdin | --from-env [ENV_NAME] | --from-local-env | agentkit secret sync --from-local | list | unset <NAME>",
180
194
  access: "agentkit access token create <name> [--out <path>] | list | revoke <token-id>",
195
+ integrations: "agentkit integrations status | agentkit integrations connect composio [--toolkit <slug>]",
181
196
  env: "agentkit env set <NAME> --stdin | --from-env [ENV_NAME] | list | unset <NAME>",
182
197
  docs: "agentkit docs path | llms | full",
183
198
  handoff: "agentkit handoff codex [goal] | agentkit handoff claude [goal]",
package/src/cli/index.ts CHANGED
@@ -22,6 +22,7 @@ import { runEvalsFromCwd, writeEvalFromConversation } from "../runtime/evals";
22
22
  import { findAgentKitDocs } from "../runtime/docs";
23
23
  import { listCapsuleEnv, setCapsuleEnv, unsetCapsuleEnv } from "../runtime/env";
24
24
  import { checkAgentSpec, initializeAgentSpec } from "../runtime/spec";
25
+ import { checkDefaultSkillStatus, syncDefaultSkillFiles } from "../runtime/skills";
25
26
  import { initializeSyncScaffold, runCapsuleSync } from "../runtime/sync";
26
27
  import { runToolFromCwd } from "../runtime/tool-runner";
27
28
  import { startAgentDevServer } from "../runtime/dev-server";
@@ -67,6 +68,7 @@ import { renderCommandHelp, renderCommandReference, renderHelp } from "./help";
67
68
  import { runInheritedCommand, waitForShutdown } from "./process";
68
69
  import { handleChannelsCommand } from "./commands/channels";
69
70
  import { handleKnowledgeCommand } from "./commands/knowledge";
71
+ import { handleTranscribeCommand } from "./commands/transcribe";
70
72
 
71
73
  async function main() {
72
74
  const args = parseArgs(process.argv.slice(2));
@@ -764,6 +766,110 @@ async function main() {
764
766
  return;
765
767
  }
766
768
 
769
+ if (args.command === "transcribe") {
770
+ await handleTranscribeCommand(args);
771
+ return;
772
+ }
773
+
774
+ if (args.command === "skills") {
775
+ const [subcommand] = args.positional;
776
+ const root = await findAgentCapsuleRoot(process.cwd());
777
+
778
+ if (subcommand === "status") {
779
+ const status = await checkDefaultSkillStatus(root);
780
+ printSkillStatus(status);
781
+ if (status.status !== "current") {
782
+ process.exitCode = 1;
783
+ }
784
+ return;
785
+ }
786
+
787
+ if (subcommand === "sync") {
788
+ const result = await syncDefaultSkillFiles(root);
789
+ console.log(`Synced ${result.files.length} AgentKit skill files.`);
790
+ console.log("Status: current");
791
+ return;
792
+ }
793
+
794
+ throw new Error("Usage: agentkit skills status | agentkit skills sync");
795
+ }
796
+
797
+ if (args.command === "integrations") {
798
+ const [subcommand, provider] = args.positional;
799
+
800
+ if (provider !== undefined && provider !== "composio") {
801
+ throw new Error("Usage: agentkit integrations status | connect composio [--toolkit <slug>] [--callback-url <url>] [--api <url>]");
802
+ }
803
+
804
+ const state = await requireLocalDeployState();
805
+ const apiUrl = await resolveCloudApiUrl(args.flags.api);
806
+
807
+ if (subcommand === "status") {
808
+ const payload = await cloudApiRequest(
809
+ apiUrl,
810
+ `/v1/deploys/${encodeURIComponent(state.deploy_id)}/integrations/composio/status`,
811
+ { method: "GET" },
812
+ );
813
+ printJson(payload);
814
+ return;
815
+ }
816
+
817
+ if (subcommand === "connect" && provider === "composio") {
818
+ const toolkit = parseOptionalStringFlag(
819
+ args.flags.toolkit,
820
+ "toolkit",
821
+ "agentkit integrations connect composio --toolkit <slug>",
822
+ );
823
+ const callbackUrl = parseOptionalStringFlag(
824
+ args.flags["callback-url"],
825
+ "callback-url",
826
+ "agentkit integrations connect composio --callback-url <url>",
827
+ );
828
+ const payload = (await cloudApiRequest(
829
+ apiUrl,
830
+ `/v1/deploys/${encodeURIComponent(state.deploy_id)}/integrations/composio/connect-link`,
831
+ {
832
+ method: "POST",
833
+ body: JSON.stringify({
834
+ ...(toolkit ? { toolkit } : {}),
835
+ ...(callbackUrl ? { callback_url: callbackUrl } : {}),
836
+ }),
837
+ },
838
+ )) as {
839
+ connect_link?: {
840
+ toolkit?: string;
841
+ user_id?: string;
842
+ redirect_url?: string;
843
+ expires_at?: string;
844
+ connected_account_id?: string;
845
+ };
846
+ };
847
+
848
+ const link = payload.connect_link;
849
+
850
+ if (!link?.redirect_url) {
851
+ throw new AgentKitError("integration_response_invalid", "AgentKit Cloud returned an invalid Composio connect-link payload.");
852
+ }
853
+
854
+ console.log("Composio connect link");
855
+ console.log(`Toolkit: ${link.toolkit ?? toolkit ?? "unknown"}`);
856
+ console.log(`User: ${link.user_id ?? "unknown"}`);
857
+ console.log(`URL: ${link.redirect_url}`);
858
+
859
+ if (link.expires_at) {
860
+ console.log(`Expires: ${link.expires_at}`);
861
+ }
862
+
863
+ if (link.connected_account_id) {
864
+ console.log(`Account: ${link.connected_account_id}`);
865
+ }
866
+
867
+ return;
868
+ }
869
+
870
+ throw new Error("Usage: agentkit integrations status | connect composio [--toolkit <slug>] [--callback-url <url>] [--api <url>]");
871
+ }
872
+
767
873
  if (args.command === "env") {
768
874
  const [subcommand, name, ...valueParts] = args.positional;
769
875
  const root = await findAgentCapsuleRoot(process.cwd());
@@ -1078,6 +1184,30 @@ function printJson(value: unknown): void {
1078
1184
  console.log(JSON.stringify(value, null, 2));
1079
1185
  }
1080
1186
 
1187
+ function printSkillStatus(status: Awaited<ReturnType<typeof checkDefaultSkillStatus>>): void {
1188
+ console.log("AgentKit skills");
1189
+ console.log(`Status: ${status.status}`);
1190
+ console.log(`Checked: ${status.checked}`);
1191
+
1192
+ if (status.missing.length > 0) {
1193
+ console.log(`Missing: ${status.missing.length}`);
1194
+ for (const file of status.missing.slice(0, 10)) {
1195
+ console.log(` ${file}`);
1196
+ }
1197
+ }
1198
+
1199
+ if (status.outdated.length > 0) {
1200
+ console.log(`Outdated: ${status.outdated.length}`);
1201
+ for (const file of status.outdated.slice(0, 10)) {
1202
+ console.log(` ${file}`);
1203
+ }
1204
+ }
1205
+
1206
+ if (status.status !== "current") {
1207
+ console.log("Run: npm run agentkit -- skills sync");
1208
+ }
1209
+ }
1210
+
1081
1211
  async function printJsonOrWrite(value: unknown, outFlag: string | boolean | undefined, usage: string): Promise<void> {
1082
1212
  const outPath = parseOptionalStringFlag(outFlag, "out", usage);
1083
1213
 
@@ -1,10 +1,11 @@
1
1
  import { execFile } from "node:child_process";
2
- import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
3
- import { basename, dirname, join, relative, resolve } from "node:path";
2
+ import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
3
+ import { basename, dirname, join, resolve } from "node:path";
4
4
  import { promisify } from "node:util";
5
5
  import { fileURLToPath } from "node:url";
6
6
 
7
- import { getTemplate, type TemplateFile } from "./templates";
7
+ import { loadDefaultSkillFiles } from "./runtime/skills";
8
+ import { getTemplate } from "./templates";
8
9
 
9
10
  const execFileAsync = promisify(execFile);
10
11
  const moduleDir = dirname(fileURLToPath(import.meta.url));
@@ -129,35 +130,6 @@ async function resolveAgentKitDependency(): Promise<string> {
129
130
  return packageJson.version;
130
131
  }
131
132
 
132
- async function loadDefaultSkillFiles(): Promise<TemplateFile[]> {
133
- const sourceRoot = join(moduleDir, "templates", "skills");
134
- const sourceFiles = await listFiles(sourceRoot);
135
-
136
- return Promise.all(
137
- sourceFiles.map(async (sourcePath) => ({
138
- path: join("skills", relative(sourceRoot, sourcePath)).replaceAll("\\", "/"),
139
- contents: await readFile(sourcePath, "utf8"),
140
- })),
141
- );
142
- }
143
-
144
- async function listFiles(directory: string): Promise<string[]> {
145
- const entries = await readdir(directory, { withFileTypes: true });
146
- const files = await Promise.all(
147
- entries.map(async (entry) => {
148
- const path = join(directory, entry.name);
149
-
150
- if (entry.isDirectory()) {
151
- return listFiles(path);
152
- }
153
-
154
- return entry.isFile() ? [path] : [];
155
- }),
156
- );
157
-
158
- return files.flat().sort();
159
- }
160
-
161
133
  async function directoryExists(path: string): Promise<boolean> {
162
134
  try {
163
135
  const result = await stat(path);