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

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 (39) hide show
  1. package/README.md +8 -0
  2. package/docs/guides/add-channel.md +4 -2
  3. package/docs/guides/add-managed-composio.md +161 -0
  4. package/docs/guides/channel-security.md +36 -39
  5. package/docs/guides/connect-telegram.md +3 -1
  6. package/docs/guides/debug-channel.md +141 -0
  7. package/docs/guides/prepare-deploy.md +7 -10
  8. package/docs/llms-full.txt +19 -5
  9. package/docs/llms.txt +11 -2
  10. package/package.json +1 -3
  11. package/src/cli/cloud-client.ts +12 -0
  12. package/src/cli/commands/channels.ts +119 -7
  13. package/src/cli/commands/transcribe.ts +171 -0
  14. package/src/cli/deploy-chat-ui.ts +146 -3
  15. package/src/cli/deploy-readiness.ts +112 -3
  16. package/src/cli/help.ts +19 -4
  17. package/src/cli/index.ts +198 -3
  18. package/src/create-project.ts +4 -32
  19. package/src/index.ts +149 -0
  20. package/src/runtime/chat.ts +2 -1
  21. package/src/runtime/config.ts +133 -0
  22. package/src/runtime/core/manifest.ts +31 -2
  23. package/src/runtime/deploy-readiness.ts +31 -1
  24. package/src/runtime/inspect.ts +109 -4
  25. package/src/runtime/integrations/composio.ts +423 -0
  26. package/src/runtime/skills.ts +95 -0
  27. package/src/runtime/targets/cloudflare/build.ts +395 -10
  28. package/src/runtime/tool-runner.ts +2 -1
  29. package/src/templates/skills/agentkit-capsule/SKILL.md +1 -0
  30. package/src/templates/skills/agentkit-channels/SKILL.md +2 -0
  31. package/src/templates/skills/agentkit-channels/references/channel-debugging.md +3 -1
  32. package/src/templates/skills/agentkit-channels/references/telegram.md +3 -1
  33. package/src/templates/skills/agentkit-deploy/SKILL.md +1 -1
  34. package/src/templates/skills/agentkit-integrations/SKILL.md +75 -0
  35. package/src/templates/skills/agentkit-troubleshooting/SKILL.md +3 -2
  36. package/docs/guides/agentkit-skills-architecture.md +0 -472
  37. package/docs/guides/channels-implementation-map.md +0 -243
  38. package/docs/guides/channels-production-handoff.md +0 -118
  39. package/docs/portable-deploy-release-checklist.md +0 -41
@@ -2,12 +2,14 @@ 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_API_KEY_SECRET, MANAGED_COMPOSIO_ENTITLEMENT } from "../runtime/integrations/composio";
5
6
  import {
6
7
  cloudApiRequest,
7
8
  formatCloudAuthSource,
8
9
  readCloudAuthForApiUrl,
9
10
  type CloudCapabilitiesResponse,
10
11
  type CloudLimitsResponse,
12
+ type CloudManagedComposioResponse,
11
13
  type CloudMeResponse,
12
14
  type CloudProjectResolveResponse,
13
15
  type CloudSecretsResponse,
@@ -53,6 +55,22 @@ export async function checkDeployReadiness(options: {
53
55
  });
54
56
  }
55
57
 
58
+ const nodeMajor = majorNodeVersion(process.versions.node);
59
+ if (nodeMajor !== null && nodeMajor < 24) {
60
+ checks.push({
61
+ status: "warn",
62
+ title: "Node version",
63
+ message: `@andreprado/agentkit requires Node >=24; current Node is ${process.versions.node}.`,
64
+ action: "use Node 24+ before running direct node imports or production container commands",
65
+ });
66
+ } else {
67
+ checks.push({
68
+ status: "pass",
69
+ title: "Node version",
70
+ message: `Node ${process.versions.node} satisfies @andreprado/agentkit engines.node >=24.`,
71
+ });
72
+ }
73
+
56
74
  if (!auth?.token) {
57
75
  checks.push({
58
76
  status: options.requireLogin ? "fail" : "warn",
@@ -104,6 +122,9 @@ export async function checkDeployReadiness(options: {
104
122
  const hasCloudflareAlpha = capabilities.some(
105
123
  (capability) => capability.name === "cloudflare_deploy_alpha" && capability.status === "active",
106
124
  );
125
+ const hasManagedComposio = capabilities.some(
126
+ (capability) => capability.name === MANAGED_COMPOSIO_ENTITLEMENT && capability.status === "active",
127
+ );
107
128
 
108
129
  checks.push({
109
130
  status: hasCloudflareAlpha ? "pass" : "fail",
@@ -113,6 +134,17 @@ export async function checkDeployReadiness(options: {
113
134
  : "This account is missing the cloudflare_deploy_alpha entitlement.",
114
135
  ...(hasCloudflareAlpha ? {} : { action: "log in with an invited AgentKit Cloud alpha token" }),
115
136
  });
137
+
138
+ if (context.integrations.managedComposio.enabled) {
139
+ checks.push({
140
+ status: hasManagedComposio ? "pass" : "fail",
141
+ title: "Managed Composio entitlement",
142
+ message: hasManagedComposio
143
+ ? `${MANAGED_COMPOSIO_ENTITLEMENT} is active.`
144
+ : `This capsule uses AgentKit-managed Composio, but this account is missing ${MANAGED_COMPOSIO_ENTITLEMENT}.`,
145
+ ...(hasManagedComposio ? {} : { action: "upgrade the AgentKit Cloud account or ask an operator to grant managed_composio" }),
146
+ });
147
+ }
116
148
  } catch (error) {
117
149
  checks.push({
118
150
  status: "fail",
@@ -172,6 +204,58 @@ export async function checkDeployReadiness(options: {
172
204
  message: `Could not verify online deploy capacity. ${formatReadinessError(error)}`,
173
205
  });
174
206
  }
207
+
208
+ if (context.integrations.managedComposio.enabled) {
209
+ try {
210
+ const payload = (await cloudApiRequest(
211
+ options.apiUrl,
212
+ `/v1/me/managed-composio?toolkits=${encodeURIComponent(context.integrations.managedComposio.toolkits.join(","))}`,
213
+ {
214
+ method: "GET",
215
+ },
216
+ )) as CloudManagedComposioResponse;
217
+ const managed = payload.managed_composio;
218
+ const missingToolkits = (managed?.toolkits ?? []).filter((toolkit) => toolkit.auth_config !== "set");
219
+
220
+ if (managed?.api_key !== "set") {
221
+ checks.push({
222
+ status: "fail",
223
+ title: "Managed Composio API key",
224
+ message: `AgentKit Cloud is missing AGENTKIT_SECRET_${MANAGED_COMPOSIO_API_KEY_SECRET}.`,
225
+ action: "ask the AgentKit Cloud operator to configure managed Composio",
226
+ });
227
+ } else {
228
+ checks.push({
229
+ status: "pass",
230
+ title: "Managed Composio API key",
231
+ message: "AgentKit Cloud has the managed COMPOSIO_API_KEY.",
232
+ });
233
+ }
234
+
235
+ if (missingToolkits.length > 0) {
236
+ checks.push({
237
+ status: "fail",
238
+ title: "Managed Composio auth configs",
239
+ message: `Missing ${missingToolkits
240
+ .map((toolkit) => `${toolkit.auth_config_env ?? "auth config"} for ${toolkit.toolkit ?? "unknown"}`)
241
+ .join(", ")}.`,
242
+ action: "ask the AgentKit Cloud operator to configure the missing toolkit auth config",
243
+ });
244
+ } else {
245
+ checks.push({
246
+ status: "pass",
247
+ title: "Managed Composio auth configs",
248
+ message: `Auth configs are available for ${context.integrations.managedComposio.toolkits.join(", ")}.`,
249
+ });
250
+ }
251
+ } catch (error) {
252
+ checks.push({
253
+ status: "warn",
254
+ title: "Managed Composio auth configs",
255
+ message: `Could not verify managed Composio auth configs. ${formatReadinessError(error)}`,
256
+ });
257
+ }
258
+ }
175
259
  }
176
260
 
177
261
  if (context.agentKitManagedSecrets.length > 0) {
@@ -184,6 +268,25 @@ export async function checkDeployReadiness(options: {
184
268
  });
185
269
  }
186
270
 
271
+ if (context.integrations.managedComposio.enabled) {
272
+ if (!options.requireLogin) {
273
+ checks.push({
274
+ status: "fail",
275
+ title: "Managed Composio access",
276
+ message: "AgentKit-managed Composio is paid hosted infrastructure and cannot be used by anonymous deploys.",
277
+ action: "run agentkit deploy without --anonymous after logging in with a paid AgentKit Cloud account",
278
+ });
279
+ } else {
280
+ checks.push({
281
+ status: "info",
282
+ title: "Managed Composio",
283
+ message: `Configured for ${context.integrations.managedComposio.toolkits.join(", ")} with ${
284
+ context.integrations.managedComposio.toolCount
285
+ } allowed action${context.integrations.managedComposio.toolCount === 1 ? "" : "s"}. AgentKit Cloud injects COMPOSIO_API_KEY.`,
286
+ });
287
+ }
288
+ }
289
+
187
290
  if (context.accessMode === "private") {
188
291
  checks.push({
189
292
  status: "info",
@@ -350,20 +453,26 @@ async function resolveCloudProjectId(apiUrl: string, projectName: string): Promi
350
453
 
351
454
  function secretSetCommand(secret: string, source: DeployReadinessContext["localSecrets"][string] | undefined): string {
352
455
  if (source?.dotenv) {
353
- return `agentkit secret set ${secret} --from-local-env`;
456
+ return `npm run agentkit -- secret set ${secret} --from-local-env`;
354
457
  }
355
458
 
356
459
  if (source?.environment) {
357
- return `agentkit secret set ${secret} --from-env`;
460
+ return `npm run agentkit -- secret set ${secret} --from-env`;
358
461
  }
359
462
 
360
- return `agentkit secret set ${secret} --stdin`;
463
+ return `npm run agentkit -- secret set ${secret} --stdin`;
361
464
  }
362
465
 
363
466
  function normalizeOptionalNumber(value: number | null | undefined): number | undefined {
364
467
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
365
468
  }
366
469
 
470
+ function majorNodeVersion(version: string): number | null {
471
+ const [major] = version.split(".");
472
+ const parsed = Number(major);
473
+ return Number.isInteger(parsed) ? parsed : null;
474
+ }
475
+
367
476
  function isString(value: unknown): value is string {
368
477
  return typeof value === "string";
369
478
  }
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
@@ -72,9 +73,9 @@ Usage:
72
73
  agentkit db seed [--file <path>]
73
74
  agentkit eval run
74
75
  agentkit eval from-conversation <conversation-id> [--out <path>] [--force]
75
- agentkit conversations list
76
- agentkit conversations show <conversation-id>
77
- agentkit conversations trace <conversation-id>
76
+ agentkit conversations list [--deploy]
77
+ agentkit conversations show <conversation-id> [--deploy]
78
+ agentkit conversations trace <conversation-id> [--deploy]
78
79
  agentkit channels list
79
80
  agentkit channels add <website|telegram|whatsapp> <name> [--provider zapster|meta] [--api <url>]
80
81
  agentkit channels connect <website|telegram|whatsapp> <name> [--provider zapster|meta] [--api <url>]
@@ -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 [--toolkit <slug>] [--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
@@ -172,12 +182,17 @@ export function renderCommandHelp(command: string): string {
172
182
  sync: "agentkit sync init [--force] | agentkit sync run [--file <path>]",
173
183
  eval: "agentkit eval run | agentkit eval from-conversation <conversation-id> [--out <path>] [--force]",
174
184
  conversations:
175
- "agentkit conversations list | agentkit conversations show <conversation-id> | agentkit conversations trace <conversation-id>",
185
+ "agentkit conversations list [--deploy] | agentkit conversations show <conversation-id> [--deploy] | agentkit conversations trace <conversation-id> [--deploy]",
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 [--toolkit <slug>] | 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));
@@ -336,8 +338,15 @@ async function main() {
336
338
 
337
339
  if (args.command === "conversations") {
338
340
  const [subcommand, conversationId] = args.positional;
341
+ const fromDeploy = args.flags.deploy === true;
339
342
 
340
343
  if (subcommand === "list") {
344
+ if (fromDeploy) {
345
+ const payload = await fetchDeployConversationJson(process.cwd(), "/v1/conversations");
346
+ printJson(payload);
347
+ return;
348
+ }
349
+
341
350
  const conversations = await listConversationsFromCwd(process.cwd());
342
351
 
343
352
  if (conversations.length === 0) {
@@ -363,7 +372,16 @@ async function main() {
363
372
 
364
373
  if (subcommand === "show") {
365
374
  if (!conversationId) {
366
- throw new Error("Missing conversation id. Usage: agentkit conversations show <conversation-id>");
375
+ throw new Error("Missing conversation id. Usage: agentkit conversations show <conversation-id> [--deploy]");
376
+ }
377
+
378
+ if (fromDeploy) {
379
+ const payload = await fetchDeployConversationJson(
380
+ process.cwd(),
381
+ `/v1/conversations/${encodeURIComponent(conversationId)}`,
382
+ );
383
+ printJson(payload);
384
+ return;
367
385
  }
368
386
 
369
387
  const conversation = await getConversationFromCwd(process.cwd(), conversationId);
@@ -382,7 +400,16 @@ async function main() {
382
400
 
383
401
  if (subcommand === "trace") {
384
402
  if (!conversationId) {
385
- throw new Error("Missing conversation id. Usage: agentkit conversations trace <conversation-id>");
403
+ throw new Error("Missing conversation id. Usage: agentkit conversations trace <conversation-id> [--deploy]");
404
+ }
405
+
406
+ if (fromDeploy) {
407
+ const trace = await fetchDeployConversationJson(
408
+ process.cwd(),
409
+ `/v1/conversations/${encodeURIComponent(conversationId)}/trace`,
410
+ );
411
+ printJson(trace);
412
+ return;
386
413
  }
387
414
 
388
415
  const trace = await getConversationTraceFromCwd(process.cwd(), conversationId);
@@ -390,7 +417,7 @@ async function main() {
390
417
  return;
391
418
  }
392
419
 
393
- throw new Error("Usage: agentkit conversations list | agentkit conversations show <conversation-id> | agentkit conversations trace <conversation-id>");
420
+ throw new Error("Usage: agentkit conversations list [--deploy] | agentkit conversations show <conversation-id> [--deploy] | agentkit conversations trace <conversation-id> [--deploy]");
394
421
  }
395
422
 
396
423
  if (args.command === "knowledge") {
@@ -764,6 +791,117 @@ async function main() {
764
791
  return;
765
792
  }
766
793
 
794
+ if (args.command === "transcribe") {
795
+ await handleTranscribeCommand(args);
796
+ return;
797
+ }
798
+
799
+ if (args.command === "skills") {
800
+ const [subcommand] = args.positional;
801
+ const root = await findAgentCapsuleRoot(process.cwd());
802
+
803
+ if (subcommand === "status") {
804
+ const status = await checkDefaultSkillStatus(root);
805
+ printSkillStatus(status);
806
+ if (status.status !== "current") {
807
+ process.exitCode = 1;
808
+ }
809
+ return;
810
+ }
811
+
812
+ if (subcommand === "sync") {
813
+ const result = await syncDefaultSkillFiles(root);
814
+ console.log(`Synced ${result.files.length} AgentKit skill files.`);
815
+ console.log("Status: current");
816
+ return;
817
+ }
818
+
819
+ throw new Error("Usage: agentkit skills status | agentkit skills sync");
820
+ }
821
+
822
+ if (args.command === "integrations") {
823
+ const [subcommand, provider] = args.positional;
824
+
825
+ if (provider !== undefined && provider !== "composio") {
826
+ throw new Error("Usage: agentkit integrations status | connect composio [--toolkit <slug>] [--callback-url <url>] [--api <url>]");
827
+ }
828
+
829
+ const state = await requireLocalDeployState();
830
+ const apiUrl = await resolveCloudApiUrl(args.flags.api);
831
+
832
+ if (subcommand === "status") {
833
+ const toolkit = parseOptionalStringFlag(
834
+ args.flags.toolkit,
835
+ "toolkit",
836
+ "agentkit integrations status --toolkit <slug>",
837
+ );
838
+ const payload = await cloudApiRequest(
839
+ apiUrl,
840
+ `/v1/deploys/${encodeURIComponent(state.deploy_id)}/integrations/composio/status${
841
+ toolkit ? `?toolkit=${encodeURIComponent(toolkit)}` : ""
842
+ }`,
843
+ { method: "GET" },
844
+ );
845
+ printJson(payload);
846
+ return;
847
+ }
848
+
849
+ if (subcommand === "connect" && provider === "composio") {
850
+ const toolkit = parseOptionalStringFlag(
851
+ args.flags.toolkit,
852
+ "toolkit",
853
+ "agentkit integrations connect composio --toolkit <slug>",
854
+ );
855
+ const callbackUrl = parseOptionalStringFlag(
856
+ args.flags["callback-url"],
857
+ "callback-url",
858
+ "agentkit integrations connect composio --callback-url <url>",
859
+ );
860
+ const payload = (await cloudApiRequest(
861
+ apiUrl,
862
+ `/v1/deploys/${encodeURIComponent(state.deploy_id)}/integrations/composio/connect-link`,
863
+ {
864
+ method: "POST",
865
+ body: JSON.stringify({
866
+ ...(toolkit ? { toolkit } : {}),
867
+ ...(callbackUrl ? { callback_url: callbackUrl } : {}),
868
+ }),
869
+ },
870
+ )) as {
871
+ connect_link?: {
872
+ toolkit?: string;
873
+ user_id?: string;
874
+ redirect_url?: string;
875
+ expires_at?: string;
876
+ connected_account_id?: string;
877
+ };
878
+ };
879
+
880
+ const link = payload.connect_link;
881
+
882
+ if (!link?.redirect_url) {
883
+ throw new AgentKitError("integration_response_invalid", "AgentKit Cloud returned an invalid Composio connect-link payload.");
884
+ }
885
+
886
+ console.log("Composio connect link");
887
+ console.log(`Toolkit: ${link.toolkit ?? toolkit ?? "unknown"}`);
888
+ console.log(`User: ${link.user_id ?? "unknown"}`);
889
+ console.log(`URL: ${link.redirect_url}`);
890
+
891
+ if (link.expires_at) {
892
+ console.log(`Expires: ${link.expires_at}`);
893
+ }
894
+
895
+ if (link.connected_account_id) {
896
+ console.log(`Account: ${link.connected_account_id}`);
897
+ }
898
+
899
+ return;
900
+ }
901
+
902
+ throw new Error("Usage: agentkit integrations status | connect composio [--toolkit <slug>] [--callback-url <url>] [--api <url>]");
903
+ }
904
+
767
905
  if (args.command === "env") {
768
906
  const [subcommand, name, ...valueParts] = args.positional;
769
907
  const root = await findAgentCapsuleRoot(process.cwd());
@@ -1074,10 +1212,67 @@ async function runDeploySmoke(input: { deployUrl: string; message: string; token
1074
1212
  }
1075
1213
  }
1076
1214
 
1215
+ async function fetchDeployConversationJson(cwd: string, path: string): Promise<unknown> {
1216
+ const state = await requireLocalDeployState();
1217
+ const deployUrl = requireDeployUrl(state);
1218
+ const token = await readDeployAccessTokenForSmoke(cwd, state);
1219
+ const headers = new Headers();
1220
+
1221
+ if (token) {
1222
+ headers.set("Authorization", `Bearer ${token}`);
1223
+ }
1224
+
1225
+ let response: Response;
1226
+
1227
+ try {
1228
+ response = await fetch(new URL(path, deployUrl).href, {
1229
+ method: "GET",
1230
+ headers,
1231
+ });
1232
+ } catch (error) {
1233
+ throw new AgentKitError(
1234
+ "deploy_conversation_request_failed",
1235
+ `Could not read hosted conversation data: ${error instanceof Error ? error.message : String(error)}`,
1236
+ );
1237
+ }
1238
+
1239
+ const payload = await response.json().catch(() => null);
1240
+
1241
+ if (!response.ok) {
1242
+ throw new AgentKitError("deploy_conversation_request_failed", readCloudError(payload, response.status));
1243
+ }
1244
+
1245
+ return payload;
1246
+ }
1247
+
1077
1248
  function printJson(value: unknown): void {
1078
1249
  console.log(JSON.stringify(value, null, 2));
1079
1250
  }
1080
1251
 
1252
+ function printSkillStatus(status: Awaited<ReturnType<typeof checkDefaultSkillStatus>>): void {
1253
+ console.log("AgentKit skills");
1254
+ console.log(`Status: ${status.status}`);
1255
+ console.log(`Checked: ${status.checked}`);
1256
+
1257
+ if (status.missing.length > 0) {
1258
+ console.log(`Missing: ${status.missing.length}`);
1259
+ for (const file of status.missing.slice(0, 10)) {
1260
+ console.log(` ${file}`);
1261
+ }
1262
+ }
1263
+
1264
+ if (status.outdated.length > 0) {
1265
+ console.log(`Outdated: ${status.outdated.length}`);
1266
+ for (const file of status.outdated.slice(0, 10)) {
1267
+ console.log(` ${file}`);
1268
+ }
1269
+ }
1270
+
1271
+ if (status.status !== "current") {
1272
+ console.log("Run: npm run agentkit -- skills sync");
1273
+ }
1274
+ }
1275
+
1081
1276
  async function printJsonOrWrite(value: unknown, outFlag: string | boolean | undefined, usage: string): Promise<void> {
1082
1277
  const outPath = parseOptionalStringFlag(outFlag, "out", usage);
1083
1278
 
@@ -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);