@andreprado/agentkit 0.1.0-alpha.9 → 0.1.1

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 (122) hide show
  1. package/README.md +18 -1
  2. package/docs/guides/add-channel.md +251 -7
  3. package/docs/guides/add-knowledge.md +10 -0
  4. package/docs/guides/add-managed-composio.md +165 -0
  5. package/docs/guides/add-tool.md +10 -3
  6. package/docs/guides/channel-security.md +162 -32
  7. package/docs/guides/connect-discord.md +178 -0
  8. package/docs/guides/connect-slack.md +126 -0
  9. package/docs/guides/connect-telegram.md +61 -1
  10. package/docs/guides/connect-whatsapp-evolution.md +121 -0
  11. package/docs/guides/connect-whatsapp-uazapi.md +139 -0
  12. package/docs/guides/connect-whatsapp-zapster.md +119 -16
  13. package/docs/guides/create-agent.md +31 -4
  14. package/docs/guides/debug-channel.md +159 -0
  15. package/docs/guides/improve-from-production.md +151 -0
  16. package/docs/guides/prepare-deploy.md +32 -14
  17. package/docs/guides/replay-production-traces.md +72 -0
  18. package/docs/guides/run-evals.md +95 -25
  19. package/docs/guides/security-rules.md +9 -5
  20. package/docs/guides/send-feedback.md +135 -0
  21. package/docs/guides/use-jev.md +67 -0
  22. package/docs/guides/use-provider.md +70 -3
  23. package/docs/llms-full.txt +295 -25
  24. package/docs/llms.txt +54 -7
  25. package/package.json +3 -7
  26. package/src/cli/args.ts +23 -2
  27. package/src/cli/cloud-client.ts +121 -9
  28. package/src/cli/commands/channels.ts +856 -36
  29. package/src/cli/commands/feedback.ts +438 -0
  30. package/src/cli/commands/provider.ts +47 -0
  31. package/src/cli/commands/transcribe.ts +171 -0
  32. package/src/cli/deploy-chat-ui.ts +232 -18
  33. package/src/cli/deploy-readiness.ts +227 -14
  34. package/src/cli/help.ts +67 -9
  35. package/src/cli/index.ts +740 -35
  36. package/src/cli/new-command.ts +41 -0
  37. package/src/cloud/client.ts +4 -3
  38. package/src/cloud/contracts.ts +1 -1
  39. package/src/create-project.ts +18 -35
  40. package/src/index.ts +565 -11
  41. package/src/providers/codex-auth.ts +111 -0
  42. package/src/providers/pi.ts +88 -19
  43. package/src/providers/test.ts +36 -0
  44. package/src/providers/types.ts +8 -0
  45. package/src/runtime/channel-test-harness.ts +21 -1
  46. package/src/runtime/channels/discord.ts +904 -0
  47. package/src/runtime/channels/generic-webhook.ts +682 -0
  48. package/src/runtime/channels/net-guard.ts +480 -0
  49. package/src/runtime/channels/provider-fetch.ts +54 -0
  50. package/src/runtime/channels/slack.ts +652 -0
  51. package/src/runtime/channels/telegram.ts +379 -15
  52. package/src/runtime/channels/whatsapp-evolution.ts +1330 -0
  53. package/src/runtime/channels/whatsapp-meta.ts +9 -0
  54. package/src/runtime/channels/whatsapp-uazapi.ts +1192 -0
  55. package/src/runtime/channels/whatsapp-zapster.ts +702 -40
  56. package/src/runtime/channels.ts +83 -3
  57. package/src/runtime/chat.ts +70 -44
  58. package/src/runtime/config.ts +512 -20
  59. package/src/runtime/core/manifest.ts +75 -5
  60. package/src/runtime/core/targets.ts +5 -5
  61. package/src/runtime/deploy-readiness.ts +34 -4
  62. package/src/runtime/dev-server.ts +639 -39
  63. package/src/runtime/env.ts +8 -3
  64. package/src/runtime/evals.ts +445 -74
  65. package/src/runtime/improve.ts +868 -0
  66. package/src/runtime/inspect.ts +173 -4
  67. package/src/runtime/integrations/composio.ts +425 -0
  68. package/src/runtime/knowledge/embeddings.ts +45 -7
  69. package/src/runtime/knowledge/ingest.ts +69 -6
  70. package/src/runtime/knowledge/retrieve.ts +25 -5
  71. package/src/runtime/knowledge/schema.ts +45 -1
  72. package/src/runtime/knowledge/vector.ts +30 -30
  73. package/src/runtime/prompt-context.ts +141 -0
  74. package/src/runtime/runtime-contract.ts +71 -7
  75. package/src/runtime/skills.ts +95 -0
  76. package/src/runtime/targets/cloudflare/build.ts +1010 -208
  77. package/src/runtime/targets/container/server.ts +1 -1
  78. package/src/runtime/targets/vps/deploy.ts +26 -9
  79. package/src/runtime/tool-runner.ts +9 -1
  80. package/src/runtime/tools.ts +26 -2
  81. package/src/runtime/transcription.ts +483 -0
  82. package/src/storage/sqlite.ts +7 -2
  83. package/src/templates/blank.ts +37 -9
  84. package/src/templates/dentista.ts +40 -14
  85. package/src/templates/skills/agentkit-build-agent/SKILL.md +34 -5
  86. package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +2 -1
  87. package/src/templates/skills/agentkit-capsule/SKILL.md +32 -3
  88. package/src/templates/skills/agentkit-capsule/references/docs-router.md +2 -2
  89. package/src/templates/skills/agentkit-channels/SKILL.md +66 -1
  90. package/src/templates/skills/agentkit-channels/references/channel-buffering.md +8 -1
  91. package/src/templates/skills/agentkit-channels/references/channel-debugging.md +28 -3
  92. package/src/templates/skills/agentkit-channels/references/discord.md +93 -0
  93. package/src/templates/skills/agentkit-channels/references/slack.md +56 -0
  94. package/src/templates/skills/agentkit-channels/references/telegram.md +34 -0
  95. package/src/templates/skills/agentkit-channels/references/whatsapp-evolution.md +57 -0
  96. package/src/templates/skills/agentkit-channels/references/whatsapp-uazapi.md +54 -0
  97. package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +42 -8
  98. package/src/templates/skills/agentkit-database/SKILL.md +11 -0
  99. package/src/templates/skills/agentkit-deploy/SKILL.md +9 -1
  100. package/src/templates/skills/agentkit-evals/SKILL.md +77 -13
  101. package/src/templates/skills/agentkit-evals/templates/multi-turn.eval.md +13 -6
  102. package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +8 -4
  103. package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +8 -4
  104. package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +16 -7
  105. package/src/templates/skills/agentkit-improve/SKILL.md +96 -0
  106. package/src/templates/skills/agentkit-improve/references/replay-side-effects.md +18 -0
  107. package/src/templates/skills/agentkit-improve/references/trace-packets.md +22 -0
  108. package/src/templates/skills/agentkit-improve/templates/regression.eval.md +18 -0
  109. package/src/templates/skills/agentkit-integrations/SKILL.md +98 -0
  110. package/src/templates/skills/agentkit-knowledge/SKILL.md +4 -1
  111. package/src/templates/skills/agentkit-prompts/SKILL.md +3 -1
  112. package/src/templates/skills/agentkit-provider/SKILL.md +29 -4
  113. package/src/templates/skills/agentkit-security/SKILL.md +5 -2
  114. package/src/templates/skills/agentkit-tools/SKILL.md +8 -1
  115. package/src/templates/skills/agentkit-tools/examples/eval-safe-external-action.tool.md +8 -8
  116. package/src/templates/skills/agentkit-tools/examples/jev-service-fit.tool.md +110 -0
  117. package/src/templates/skills/agentkit-troubleshooting/SKILL.md +25 -1
  118. package/src/templates/support.ts +42 -12
  119. package/docs/guides/agentkit-skills-architecture.md +0 -471
  120. package/docs/guides/channels-implementation-map.md +0 -243
  121. package/docs/guides/channels-production-handoff.md +0 -101
  122. package/docs/portable-deploy-release-checklist.md +0 -41
package/src/cli/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { existsSync } from "node:fs";
3
- import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
4
4
  import { dirname, join, relative, resolve } from "node:path";
5
5
 
6
6
  import { createProject } from "../create-project";
@@ -21,7 +21,9 @@ import {
21
21
  import { runEvalsFromCwd, writeEvalFromConversation } from "../runtime/evals";
22
22
  import { findAgentKitDocs } from "../runtime/docs";
23
23
  import { listCapsuleEnv, setCapsuleEnv, unsetCapsuleEnv } from "../runtime/env";
24
+ import { collectDeployImproveBundle, collectLocalImproveBundle, generateImproveEvals, replayImproveBundle } from "../runtime/improve";
24
25
  import { checkAgentSpec, initializeAgentSpec } from "../runtime/spec";
26
+ import { checkDefaultSkillStatus, syncDefaultSkillFiles } from "../runtime/skills";
25
27
  import { initializeSyncScaffold, runCapsuleSync } from "../runtime/sync";
26
28
  import { runToolFromCwd } from "../runtime/tool-runner";
27
29
  import { startAgentDevServer } from "../runtime/dev-server";
@@ -29,6 +31,7 @@ import { inspectAgentCapsule } from "../runtime/inspect";
29
31
  import { AgentKitError, isAgentKitError } from "../runtime/errors";
30
32
  import { prepareVpsDeploy } from "../runtime/targets/vps/deploy";
31
33
  import { parseArgs, isHelpRequested, type ParsedArgs } from "./args";
34
+ import { resolveNewProjectName } from "./new-command";
32
35
  import {
33
36
  defaultAgentKitDevPort,
34
37
  defaultDeployChatAccessTokenName,
@@ -38,16 +41,25 @@ import {
38
41
  clearCloudAuth,
39
42
  cloudApiRequest,
40
43
  readCloudAuth,
44
+ readCloudAuthForApiUrl,
41
45
  readCloudError,
42
46
  resolveCloudApiUrl,
43
47
  verifyCloudLogin,
44
48
  writeCloudAuth,
49
+ type AccountApiTokenCreateResponse,
50
+ type AccountApiTokenListResponse,
51
+ type BillingCheckoutIntentResponse,
52
+ type BillingCheckoutResponse,
53
+ type BillingClaimResponse,
54
+ type BillingPortalResponse,
45
55
  type CloudMeResponse,
46
56
  type DeployAccessTokenCreateResponse,
47
57
  type DeployAccessTokenListResponse,
48
58
  } from "./cloud-client";
49
59
  import {
50
60
  checkDeployReadiness,
61
+ formatDeployCloudAuthSummary,
62
+ formatDeployOnlineCapacity,
51
63
  formatReadinessError,
52
64
  printDeployReadinessReport,
53
65
  resolveProjectIdForSecretCommand,
@@ -64,8 +76,13 @@ import {
64
76
  } from "./flags";
65
77
  import { renderCommandHelp, renderCommandReference, renderHelp } from "./help";
66
78
  import { runInheritedCommand, waitForShutdown } from "./process";
79
+ import { handleProviderCommand } from "./commands/provider";
67
80
  import { handleChannelsCommand } from "./commands/channels";
81
+ import { handleFeedbackCommand } from "./commands/feedback";
68
82
  import { handleKnowledgeCommand } from "./commands/knowledge";
83
+ import { handleTranscribeCommand } from "./commands/transcribe";
84
+
85
+ const hostedDeployEntitlement = "agentkit_cloud_deploy";
69
86
 
70
87
  async function main() {
71
88
  const args = parseArgs(process.argv.slice(2));
@@ -96,15 +113,18 @@ async function main() {
96
113
  throw new Error("Usage: agentkit help [commands]");
97
114
  }
98
115
 
116
+ warnIfUnsupportedNode();
117
+
118
+ if (args.command === "provider") {
119
+ await handleProviderCommand(args);
120
+ return;
121
+ }
122
+
99
123
  if (args.command === "new") {
100
- const [name] = args.positional;
124
+ const name = await resolveNewProjectName(args.positional);
101
125
  const template = String(args.flags.template ?? "blank");
102
126
  const shouldInstall = args.flags["no-install"] !== true;
103
127
 
104
- if (!name) {
105
- throw new Error("Missing project name. Usage: agentkit new <name> [--template blank|support|dentista] [--no-install]");
106
- }
107
-
108
128
  const result = await createProject({ name, template, cwd: process.cwd() });
109
129
  console.log(`Created ${result.name} at ${result.path}`);
110
130
 
@@ -123,8 +143,8 @@ async function main() {
123
143
  console.log(" npm install");
124
144
  }
125
145
  console.log(" open this folder in Codex or Claude Code");
126
- console.log(" coding agents start at skills/agentkit-capsule/SKILL.md");
127
- console.log(' ask: "Develop an appointment and intake agent for an ophthalmology office"');
146
+ console.log(' ask Codex: "Develop an appointment and intake agent for an ophthalmology office"');
147
+ console.log(" Codex starts at skills/agentkit-capsule/SKILL.md and treats your request as the brief");
128
148
  console.log(' npm run chat -- --message "hello"');
129
149
  console.log(" npm run dev");
130
150
  console.log(" npm run agentkit -- deploy");
@@ -174,16 +194,25 @@ async function main() {
174
194
  throw new Error("Usage: agentkit chat-ui --deploy [--port <number>] [--token-file <path>]");
175
195
  }
176
196
 
177
- const port = parsePort(args.flags.port, "agentkit chat-ui --deploy --port <number>") ?? 4124;
197
+ const requestedPort = parsePort(args.flags.port, "agentkit chat-ui --deploy --port <number>");
198
+ const port = requestedPort ?? 4124;
178
199
  const tokenFile = parseOptionalStringFlag(
179
200
  args.flags["token-file"],
180
201
  "token-file",
181
202
  "agentkit chat-ui --deploy --token-file <path>",
182
203
  );
183
- const server = await startDeployChatUiServer(process.cwd(), { port, tokenFile });
204
+ const server = await startDeployChatUiServer(process.cwd(), {
205
+ port,
206
+ tokenFile,
207
+ autoPort: requestedPort === undefined,
208
+ });
184
209
 
185
210
  console.log("AgentKit hosted chat UI running");
186
211
  console.log("");
212
+ if (server.portConflict) {
213
+ console.log(`Port ${server.portConflict.port} is already used. Started hosted chat UI at ${server.url}.`);
214
+ console.log("");
215
+ }
187
216
  console.log(`Chat: ${server.url}`);
188
217
  console.log(`Production: ${server.deployUrl}`);
189
218
  console.log(`Token: ${server.tokenSource}`);
@@ -333,10 +362,132 @@ async function main() {
333
362
  return;
334
363
  }
335
364
 
365
+ if (args.command === "improve") {
366
+ const [subcommand, bundlePath] = args.positional;
367
+
368
+ if (subcommand === "collect") {
369
+ const since = parseOptionalStringFlag(args.flags.since, "since", 'agentkit improve collect --since "24h"');
370
+ const conversationId = parseOptionalStringFlag(
371
+ args.flags["conversation-id"] ?? args.flags.conversation,
372
+ "conversation id",
373
+ "agentkit improve collect --conversation-id <id>",
374
+ );
375
+ const out = parseOptionalStringFlag(args.flags.out, "out", "agentkit improve collect --out <directory>");
376
+ const deployState = args.flags.deploy === true ? await requireLocalDeployState() : null;
377
+ const apiUrl = args.flags.deploy === true ? await resolveCloudApiUrl(args.flags.api) : null;
378
+ const cloudAuth = apiUrl ? await readCloudAuthForApiUrl(apiUrl) : null;
379
+ const result = args.flags.deploy === true
380
+ ? await collectDeployImproveBundle(process.cwd(), {
381
+ deployId: deployState?.deploy_id ?? "",
382
+ since,
383
+ conversationId,
384
+ out,
385
+ collector: {
386
+ ...(cloudAuth && apiUrl && deployState
387
+ ? {
388
+ exportEvidence: (input: { deployId: string; since?: string; conversationId?: string }) =>
389
+ fetchDeployImproveEvidence(apiUrl, input),
390
+ }
391
+ : {}),
392
+ listConversations: () => fetchDeployConversationJson(process.cwd(), "/v1/conversations"),
393
+ getTrace: (id: string) =>
394
+ fetchDeployConversationJson(process.cwd(), `/v1/conversations/${encodeURIComponent(id)}/trace`),
395
+ },
396
+ })
397
+ : await collectLocalImproveBundle(process.cwd(), {
398
+ since,
399
+ conversationId,
400
+ out,
401
+ });
402
+
403
+ console.log("Improve bundle collected");
404
+ console.log(`Source: ${result.source}`);
405
+ console.log(`Directory: ${relativePath(result.root, result.directory)}`);
406
+ console.log(`Bundle: ${relativePath(result.root, result.bundlePath)}`);
407
+ console.log(`Report: ${relativePath(result.root, result.reportPath)}`);
408
+ console.log(`Traces: ${result.traceCount}`);
409
+ console.log("");
410
+ console.log(`Next: agentkit improve evals ${relativePath(result.root, result.directory)}`);
411
+ console.log(`Replay: agentkit replay ${relativePath(result.root, result.directory)} --against local`);
412
+ return;
413
+ }
414
+
415
+ if (subcommand === "evals") {
416
+ if (!bundlePath) {
417
+ throw new Error("Missing bundle path. Usage: agentkit improve evals <bundle-dir-or-json> [--force]");
418
+ }
419
+
420
+ const result = await generateImproveEvals(process.cwd(), {
421
+ bundle: bundlePath,
422
+ force: args.flags.force === true,
423
+ });
424
+
425
+ console.log("Improve evals generated");
426
+ console.log(`Bundle: ${relativePath(result.root, result.bundlePath)}`);
427
+ console.log(`Files: ${result.files.length}`);
428
+ for (const file of result.files) {
429
+ console.log(` ${relativePath(result.root, file)}`);
430
+ }
431
+ if (result.skipped.length > 0) {
432
+ console.log(`Skipped: ${result.skipped.length}`);
433
+ for (const skipped of result.skipped) {
434
+ console.log(` ${skipped.trace_id}: ${skipped.reason}`);
435
+ }
436
+ }
437
+ return;
438
+ }
439
+
440
+ throw new Error("Usage: agentkit improve collect [--deploy] [--since <duration|iso>] [--conversation-id <id>] [--out <directory>] | agentkit improve evals <bundle-dir-or-json> [--force]");
441
+ }
442
+
443
+ if (args.command === "replay") {
444
+ const [bundlePath] = args.positional;
445
+
446
+ if (!bundlePath) {
447
+ throw new Error("Missing bundle path. Usage: agentkit replay <bundle-dir-or-json> --against local");
448
+ }
449
+
450
+ const against = parseOptionalStringFlag(args.flags.against, "against", "agentkit replay <bundle> --against local") ?? "local";
451
+
452
+ if (against !== "local") {
453
+ throw new Error("Usage: agentkit replay <bundle-dir-or-json> --against local");
454
+ }
455
+
456
+ const result = await replayImproveBundle(process.cwd(), {
457
+ bundle: bundlePath,
458
+ against,
459
+ });
460
+
461
+ for (const item of result.results) {
462
+ console.log(`${item.status.toUpperCase()} ${item.conversation_id} (${item.turns} turns)`);
463
+ if (item.error) {
464
+ console.log(` - ${item.error}`);
465
+ }
466
+ }
467
+
468
+ console.log("");
469
+ console.log(`Replay passed: ${result.passed}`);
470
+ console.log(`Replay failed: ${result.failed}`);
471
+ console.log(`Replay skipped: ${result.skipped}`);
472
+
473
+ if (result.failed > 0) {
474
+ process.exitCode = 1;
475
+ }
476
+
477
+ return;
478
+ }
479
+
336
480
  if (args.command === "conversations") {
337
481
  const [subcommand, conversationId] = args.positional;
482
+ const fromDeploy = args.flags.deploy === true;
338
483
 
339
484
  if (subcommand === "list") {
485
+ if (fromDeploy) {
486
+ const payload = await fetchDeployConversationJson(process.cwd(), "/v1/conversations");
487
+ printJson(payload);
488
+ return;
489
+ }
490
+
340
491
  const conversations = await listConversationsFromCwd(process.cwd());
341
492
 
342
493
  if (conversations.length === 0) {
@@ -362,7 +513,16 @@ async function main() {
362
513
 
363
514
  if (subcommand === "show") {
364
515
  if (!conversationId) {
365
- throw new Error("Missing conversation id. Usage: agentkit conversations show <conversation-id>");
516
+ throw new Error("Missing conversation id. Usage: agentkit conversations show <conversation-id> [--deploy]");
517
+ }
518
+
519
+ if (fromDeploy) {
520
+ const payload = await fetchDeployConversationJson(
521
+ process.cwd(),
522
+ `/v1/conversations/${encodeURIComponent(conversationId)}`,
523
+ );
524
+ printJson(payload);
525
+ return;
366
526
  }
367
527
 
368
528
  const conversation = await getConversationFromCwd(process.cwd(), conversationId);
@@ -381,7 +541,16 @@ async function main() {
381
541
 
382
542
  if (subcommand === "trace") {
383
543
  if (!conversationId) {
384
- throw new Error("Missing conversation id. Usage: agentkit conversations trace <conversation-id>");
544
+ throw new Error("Missing conversation id. Usage: agentkit conversations trace <conversation-id> [--deploy]");
545
+ }
546
+
547
+ if (fromDeploy) {
548
+ const trace = await fetchDeployConversationJson(
549
+ process.cwd(),
550
+ `/v1/conversations/${encodeURIComponent(conversationId)}/trace`,
551
+ );
552
+ printJson(trace);
553
+ return;
385
554
  }
386
555
 
387
556
  const trace = await getConversationTraceFromCwd(process.cwd(), conversationId);
@@ -389,7 +558,7 @@ async function main() {
389
558
  return;
390
559
  }
391
560
 
392
- throw new Error("Usage: agentkit conversations list | agentkit conversations show <conversation-id> | agentkit conversations trace <conversation-id>");
561
+ throw new Error("Usage: agentkit conversations list [--deploy] | agentkit conversations show <conversation-id> [--deploy] | agentkit conversations trace <conversation-id> [--deploy]");
393
562
  }
394
563
 
395
564
  if (args.command === "knowledge") {
@@ -397,6 +566,11 @@ async function main() {
397
566
  return;
398
567
  }
399
568
 
569
+ if (args.command === "feedback") {
570
+ await handleFeedbackCommand(args);
571
+ return;
572
+ }
573
+
400
574
  if (args.command === "sync") {
401
575
  const [subcommand] = args.positional;
402
576
 
@@ -489,13 +663,15 @@ async function main() {
489
663
  const email = verified.me.account?.email;
490
664
  console.log(email ? `Logged in to AgentKit Cloud as ${email}.` : "AgentKit Cloud token verified and stored.");
491
665
 
492
- const hasCloudflareAlpha = (verified.capabilities.capabilities ?? []).some(
493
- (capability) => capability.name === "cloudflare_deploy_alpha" && capability.status === "active",
666
+ const hasHostedDeploy = (verified.capabilities.capabilities ?? []).some(
667
+ (capability) =>
668
+ (capability.name === "cloudflare_deploy_alpha" || capability.name === hostedDeployEntitlement) &&
669
+ capability.status === "active",
494
670
  );
495
671
  console.log(
496
- hasCloudflareAlpha
497
- ? "Entitlement: cloudflare_deploy_alpha active."
498
- : "Warning: cloudflare_deploy_alpha entitlement was not found for this account.",
672
+ hasHostedDeploy
673
+ ? "Entitlement: hosted deploy active."
674
+ : `Warning: cloudflare_deploy_alpha or ${hostedDeployEntitlement} entitlement was not found for this account.`,
499
675
  );
500
676
 
501
677
  if (process.env.AGENTKIT_CLOUD_API_TOKEN && process.env.AGENTKIT_CLOUD_API_TOKEN !== token) {
@@ -511,6 +687,208 @@ async function main() {
511
687
  return;
512
688
  }
513
689
 
690
+ if (args.command === "account") {
691
+ const [noun, verb, value] = args.positional;
692
+
693
+ if (noun !== "token") {
694
+ throw new Error("Usage: agentkit account token create <name> [--use] [--out <path>] | list | revoke <token-id> [--api <url>]");
695
+ }
696
+
697
+ const apiUrl = await resolveCloudApiUrl(args.flags.api);
698
+
699
+ if (verb === "create" && value) {
700
+ const payload = (await cloudApiRequest(apiUrl, "/v1/account/tokens", {
701
+ method: "POST",
702
+ body: JSON.stringify({ name: value }),
703
+ })) as AccountApiTokenCreateResponse;
704
+ const token = payload.token;
705
+
706
+ if (!token?.token || !token.id) {
707
+ throw new AgentKitError("account_token_invalid", "AgentKit Cloud returned an invalid account token payload.");
708
+ }
709
+
710
+ const outPath = parseOptionalStringFlag(
711
+ args.flags.out,
712
+ "out",
713
+ "agentkit account token create <name> [--api <url>] [--use] [--out <path>]",
714
+ );
715
+
716
+ if (outPath) {
717
+ const absolutePath = resolve(process.cwd(), outPath);
718
+ await writeSecretJson(absolutePath, payload);
719
+ console.log(`Account token written to ${absolutePath}`);
720
+ } else {
721
+ printJson(payload);
722
+ }
723
+
724
+ if (args.flags.use === true) {
725
+ await writeCloudAuth({ apiUrl, token: token.token });
726
+ console.log(`Saved ${token.id} as the current AgentKit Cloud login.`);
727
+ }
728
+
729
+ return;
730
+ }
731
+
732
+ if (verb === "list" && !value) {
733
+ const payload = (await cloudApiRequest(apiUrl, "/v1/account/tokens", {
734
+ method: "GET",
735
+ })) as AccountApiTokenListResponse;
736
+ printJson(payload);
737
+ return;
738
+ }
739
+
740
+ if (verb === "revoke" && value) {
741
+ const payload = await cloudApiRequest(apiUrl, `/v1/account/tokens/${encodeURIComponent(value)}/revoke`, {
742
+ method: "POST",
743
+ });
744
+ printJson(payload);
745
+ return;
746
+ }
747
+
748
+ throw new Error("Usage: agentkit account token create <name> [--use] [--out <path>] | list | revoke <token-id> [--api <url>]");
749
+ }
750
+
751
+ if (args.command === "billing") {
752
+ const [subcommand, intentId] = args.positional;
753
+ const apiUrl = await resolveCloudApiUrl(args.flags.api);
754
+
755
+ if (subcommand === "checkout") {
756
+ const slots = parsePositiveIntegerFlag(
757
+ args.flags.slots,
758
+ "slots",
759
+ "agentkit billing checkout --slots <count> [--email <email>] [--api <url>]",
760
+ );
761
+ const email = parseOptionalStringFlag(
762
+ args.flags.email,
763
+ "email",
764
+ "agentkit billing checkout --slots <count> --email <email>",
765
+ );
766
+ const payload = (await cloudApiRequest(apiUrl, "/v1/billing/checkout", {
767
+ method: "POST",
768
+ body: JSON.stringify({
769
+ slots,
770
+ ...(email ? { email } : {}),
771
+ }),
772
+ })) as BillingCheckoutResponse;
773
+ const checkoutUrl = payload.checkout?.url;
774
+ const billingIntentId = payload.billing_intent?.id;
775
+ const secret = payload.billing_intent?.secret;
776
+
777
+ if (!checkoutUrl || !billingIntentId || !secret) {
778
+ throw new AgentKitError("billing_checkout_invalid", "AgentKit Cloud returned an invalid billing checkout payload.");
779
+ }
780
+
781
+ if (args.flags.json === true) {
782
+ printJson(payload);
783
+ } else {
784
+ console.log("AgentKit Cloud checkout");
785
+ console.log("");
786
+ console.log(`URL: ${checkoutUrl}`);
787
+ console.log(`Intent: ${billingIntentId}`);
788
+ console.log(`Secret: ${secret}`);
789
+ console.log("");
790
+ console.log(`After payment: agentkit billing claim ${billingIntentId} --secret ${secret} --api ${apiUrl}`);
791
+ }
792
+
793
+ if (args.flags.open === true) {
794
+ await openLocalUrl(checkoutUrl);
795
+ }
796
+
797
+ return;
798
+ }
799
+
800
+ if (subcommand === "status" && intentId) {
801
+ const secret = parseRequiredStringFlag(
802
+ args.flags.secret,
803
+ "secret",
804
+ "agentkit billing status <billing-intent-id> --secret <secret> [--api <url>]",
805
+ );
806
+ const payload = (await cloudApiRequest(
807
+ apiUrl,
808
+ `/v1/billing/checkout-intents/${encodeURIComponent(intentId)}?secret=${encodeURIComponent(secret)}`,
809
+ { method: "GET" },
810
+ )) as BillingCheckoutIntentResponse;
811
+ printJson(payload);
812
+ return;
813
+ }
814
+
815
+ if (subcommand === "claim" && intentId) {
816
+ const secret = parseRequiredStringFlag(
817
+ args.flags.secret,
818
+ "secret",
819
+ "agentkit billing claim <billing-intent-id> --secret <secret> [--token-name <name>] [--api <url>]",
820
+ );
821
+ const tokenName = parseOptionalStringFlag(
822
+ args.flags["token-name"],
823
+ "token-name",
824
+ "agentkit billing claim <billing-intent-id> --secret <secret> --token-name <name>",
825
+ );
826
+ const payload = (await cloudApiRequest(apiUrl, `/v1/billing/checkout-intents/${encodeURIComponent(intentId)}/claim`, {
827
+ method: "POST",
828
+ body: JSON.stringify({
829
+ secret,
830
+ ...(tokenName ? { token_name: tokenName } : {}),
831
+ }),
832
+ })) as BillingClaimResponse;
833
+ const token = payload.token;
834
+
835
+ if (!token?.token || !token.id) {
836
+ throw new AgentKitError("billing_claim_invalid", "AgentKit Cloud returned an invalid billing claim payload.");
837
+ }
838
+
839
+ await writeCloudAuth({ apiUrl, token: token.token });
840
+
841
+ const outPath = parseOptionalStringFlag(
842
+ args.flags.out,
843
+ "out",
844
+ "agentkit billing claim <billing-intent-id> --secret <secret> [--out <path>]",
845
+ );
846
+
847
+ if (outPath) {
848
+ const absolutePath = resolve(process.cwd(), outPath);
849
+ await writeSecretJson(absolutePath, payload);
850
+ console.log(`Account token written to ${absolutePath}`);
851
+ } else if (args.flags.json === true) {
852
+ printJson(payload);
853
+ } else {
854
+ console.log("AgentKit Cloud account token claimed and saved.");
855
+ console.log(`Token: ${token.id}`);
856
+ console.log(`API: ${apiUrl}`);
857
+ }
858
+
859
+ return;
860
+ }
861
+
862
+ if (subcommand === "portal") {
863
+ const payload = (await cloudApiRequest(apiUrl, "/v1/billing/portal", {
864
+ method: "POST",
865
+ body: JSON.stringify({}),
866
+ })) as BillingPortalResponse;
867
+ const portalUrl = payload.portal?.url;
868
+
869
+ if (!portalUrl) {
870
+ throw new AgentKitError("billing_portal_invalid", "AgentKit Cloud returned an invalid billing portal payload.");
871
+ }
872
+
873
+ if (args.flags.json === true) {
874
+ printJson(payload);
875
+ } else {
876
+ console.log("AgentKit Cloud billing portal");
877
+ console.log(`URL: ${portalUrl}`);
878
+ }
879
+
880
+ if (args.flags.open === true) {
881
+ await openLocalUrl(portalUrl);
882
+ }
883
+
884
+ return;
885
+ }
886
+
887
+ throw new Error(
888
+ "Usage: agentkit billing checkout --slots <count> [--email <email>] | status <intent-id> --secret <secret> | claim <intent-id> --secret <secret> | portal [--api <url>]",
889
+ );
890
+ }
891
+
514
892
  if (args.command === "deploy") {
515
893
  const [deploySubcommand] = args.positional;
516
894
 
@@ -519,7 +897,7 @@ async function main() {
519
897
  parseOptionalStringFlag(args.flags.message ?? args.flags.smoke, "message", 'agentkit deploy smoke --message "hello"') ??
520
898
  "hello";
521
899
  const state = await requireLocalDeployState();
522
- const token = await readDeployAccessTokenForSmoke(process.cwd(), state);
900
+ const token = await readDeployAccessTokenForHostedRequest(process.cwd());
523
901
  await runDeploySmoke({
524
902
  deployUrl: requireDeployUrl(state),
525
903
  message,
@@ -574,14 +952,16 @@ async function main() {
574
952
  return;
575
953
  }
576
954
 
955
+ let readinessReport: Awaited<ReturnType<typeof checkDeployReadiness>> | null = null;
956
+
577
957
  if (!args.flags["dry-run"] && !args.flags["local-wrangler"]) {
578
- const report = await checkDeployReadiness({
958
+ readinessReport = await checkDeployReadiness({
579
959
  apiUrl: await resolveCloudApiUrl(args.flags.api),
580
960
  requireLogin: !anonymous,
581
961
  });
582
962
 
583
- if (!report.ok) {
584
- printDeployReadinessReport(report);
963
+ if (!readinessReport.ok) {
964
+ printDeployReadinessReport(readinessReport);
585
965
  throw new AgentKitError(
586
966
  "deploy_readiness_failed",
587
967
  "Fix deploy readiness failures before running agentkit deploy.",
@@ -607,10 +987,10 @@ async function main() {
607
987
  }
608
988
 
609
989
  const apiUrl = await resolveCloudApiUrl(args.flags.api);
610
- const auth = await readCloudAuth();
990
+ const auth = anonymous ? null : await readCloudAuthForApiUrl(apiUrl);
611
991
  const deploy = await deployBuildToAgentKitCloud(result, {
612
992
  apiUrl,
613
- apiToken: auth?.token,
993
+ apiToken: anonymous ? null : auth?.token,
614
994
  anonymous: anonymous || !auth?.token,
615
995
  });
616
996
  const statePath = await writeLocalDeployState(result.root, deploy);
@@ -618,7 +998,7 @@ async function main() {
618
998
  apiUrl,
619
999
  root: result.root,
620
1000
  deployId: deploy.deploy.id,
621
- skip: anonymous || !auth?.token || deploy.deploy.access.mode === "public",
1001
+ skip: anonymous || !auth?.token,
622
1002
  });
623
1003
 
624
1004
  console.log("");
@@ -627,11 +1007,12 @@ async function main() {
627
1007
  console.log(`Project: ${deploy.project.id}`);
628
1008
  console.log(`Deploy: ${deploy.deploy.id}`);
629
1009
  console.log(`State: ${statePath}`);
1010
+ printDeployCloudSummary(readinessReport);
630
1011
 
631
1012
  if (chatAccess?.status === "ready") {
632
1013
  console.log(`Chat auth: ${relativePath(process.cwd(), chatAccess.path)}`);
633
1014
  } else if (chatAccess?.status === "skipped") {
634
- console.log("Chat auth: public or anonymous deploy; no local chat token was created.");
1015
+ console.log("Chat auth: anonymous deploy; no local chat token was created.");
635
1016
  } else if (chatAccess?.status === "failed") {
636
1017
  console.log(`Chat auth: could not create local chat token. ${formatReadinessError(chatAccess.error)}`);
637
1018
  console.log(`Run: agentkit access token create ${defaultDeployChatAccessTokenName} --out ${defaultDeployChatAccessTokenPath}`);
@@ -654,13 +1035,21 @@ async function main() {
654
1035
  : undefined,
655
1036
  });
656
1037
  }
1038
+ printDeployProductionHandoff({
1039
+ deployUrl: deploy.deploy.url,
1040
+ statePath,
1041
+ chatAccess,
1042
+ databaseSchemaPath: result.tursoSchemaPath,
1043
+ readinessReport,
1044
+ smokeMessage,
1045
+ });
657
1046
  return;
658
1047
  }
659
1048
 
660
1049
  if (args.command === "secret") {
661
1050
  const [subcommand, name, ...valueParts] = args.positional;
662
- const projectId = await resolveProjectIdForSecretCommand(process.cwd());
663
1051
  const apiUrl = await resolveCloudApiUrl(args.flags.api);
1052
+ const projectId = await resolveProjectIdForSecretCommand(process.cwd(), apiUrl);
664
1053
 
665
1054
  if (subcommand === "list") {
666
1055
  const payload = await cloudApiRequest(apiUrl, `/v1/projects/${encodeURIComponent(projectId)}/secrets`, {
@@ -763,6 +1152,121 @@ async function main() {
763
1152
  return;
764
1153
  }
765
1154
 
1155
+ if (args.command === "transcribe") {
1156
+ await handleTranscribeCommand(args);
1157
+ return;
1158
+ }
1159
+
1160
+ if (args.command === "skills") {
1161
+ const [subcommand] = args.positional;
1162
+ const root = await findAgentCapsuleRoot(process.cwd());
1163
+
1164
+ if (subcommand === "status") {
1165
+ const status = await checkDefaultSkillStatus(root);
1166
+ printSkillStatus(status);
1167
+ if (status.status !== "current") {
1168
+ process.exitCode = 1;
1169
+ }
1170
+ return;
1171
+ }
1172
+
1173
+ if (subcommand === "sync") {
1174
+ const result = await syncDefaultSkillFiles(root);
1175
+ console.log(`Synced ${result.files.length} AgentKit skill files.`);
1176
+ console.log("Status: current");
1177
+ return;
1178
+ }
1179
+
1180
+ throw new Error("Usage: agentkit skills status | agentkit skills sync");
1181
+ }
1182
+
1183
+ if (args.command === "integrations") {
1184
+ const [subcommand, provider] = args.positional;
1185
+
1186
+ if (provider !== undefined && provider !== "composio") {
1187
+ throw new Error("Usage: agentkit integrations status | connect composio [--toolkit <slug>] [--callback-url <url>] [--api <url>]");
1188
+ }
1189
+
1190
+ const state = await requireLocalDeployState();
1191
+ const apiUrl = await resolveCloudApiUrl(args.flags.api);
1192
+
1193
+ if (subcommand === "status") {
1194
+ const toolkit = parseOptionalStringFlag(
1195
+ args.flags.toolkit,
1196
+ "toolkit",
1197
+ "agentkit integrations status --toolkit <slug>",
1198
+ );
1199
+ const payload = await cloudApiRequest(
1200
+ apiUrl,
1201
+ `/v1/deploys/${encodeURIComponent(state.deploy_id)}/integrations/composio/status${
1202
+ toolkit ? `?toolkit=${encodeURIComponent(toolkit)}` : ""
1203
+ }`,
1204
+ { method: "GET" },
1205
+ );
1206
+ printJson(payload);
1207
+ return;
1208
+ }
1209
+
1210
+ if (subcommand === "connect" && provider === "composio") {
1211
+ const toolkit = parseOptionalStringFlag(
1212
+ args.flags.toolkit,
1213
+ "toolkit",
1214
+ "agentkit integrations connect composio --toolkit <slug>",
1215
+ );
1216
+ const callbackUrl = parseOptionalStringFlag(
1217
+ args.flags["callback-url"],
1218
+ "callback-url",
1219
+ "agentkit integrations connect composio --callback-url <url>",
1220
+ );
1221
+ const payload = (await cloudApiRequest(
1222
+ apiUrl,
1223
+ `/v1/deploys/${encodeURIComponent(state.deploy_id)}/integrations/composio/connect-link`,
1224
+ {
1225
+ method: "POST",
1226
+ body: JSON.stringify({
1227
+ ...(toolkit ? { toolkit } : {}),
1228
+ ...(callbackUrl ? { callback_url: callbackUrl } : {}),
1229
+ }),
1230
+ },
1231
+ )) as {
1232
+ connect_link?: {
1233
+ toolkit?: string;
1234
+ user_id?: string;
1235
+ redirect_url?: string;
1236
+ expires_at?: string;
1237
+ connected_account_id?: string;
1238
+ auth_config_source?: string;
1239
+ };
1240
+ };
1241
+
1242
+ const link = payload.connect_link;
1243
+
1244
+ if (!link?.redirect_url) {
1245
+ throw new AgentKitError("integration_response_invalid", "AgentKit Cloud returned an invalid Composio connect-link payload.");
1246
+ }
1247
+
1248
+ console.log("Composio connect link");
1249
+ console.log(`Toolkit: ${link.toolkit ?? toolkit ?? "unknown"}`);
1250
+ console.log(`User: ${link.user_id ?? "unknown"}`);
1251
+ if (link.auth_config_source) {
1252
+ console.log(`Auth: ${link.auth_config_source}`);
1253
+ }
1254
+ console.log(`URL: ${link.redirect_url}`);
1255
+
1256
+ if (link.expires_at) {
1257
+ console.log(`Expires: ${link.expires_at}`);
1258
+ }
1259
+
1260
+ if (link.connected_account_id) {
1261
+ console.log(`Account: ${link.connected_account_id}`);
1262
+ }
1263
+
1264
+ return;
1265
+ }
1266
+
1267
+ throw new Error("Usage: agentkit integrations status | connect composio [--toolkit <slug>] [--callback-url <url>] [--api <url>]");
1268
+ }
1269
+
766
1270
  if (args.command === "env") {
767
1271
  const [subcommand, name, ...valueParts] = args.positional;
768
1272
  const root = await findAgentCapsuleRoot(process.cwd());
@@ -885,7 +1389,7 @@ Rules:
885
1389
  - Edit the agent contract in agentkit.config.ts.
886
1390
  - Add TypeScript tools under tools/.
887
1391
  - Keep the first useful version runnable with test/fake unless the owner has already chosen a real provider.
888
- - Before testing real conversation behavior, ask the owner to choose OpenRouter, OpenAI, Anthropic, or another supported provider. Do not choose for them.
1392
+ - Before testing real conversation behavior, ask the owner to choose OpenRouter, OpenAI, Anthropic, OpenCode Zen, OpenCode Go, or another supported provider. Do not choose for them.
889
1393
  - Keep local secrets in .env only.
890
1394
  - Do not commit or print secret values.
891
1395
  - Do not edit .agentkit/ directly.
@@ -925,6 +1429,103 @@ function isRecord(value: unknown): value is Record<string, unknown> {
925
1429
  return typeof value === "object" && value !== null && !Array.isArray(value);
926
1430
  }
927
1431
 
1432
+ function warnIfUnsupportedNode(): void {
1433
+ const major = majorNodeVersion(process.versions.node);
1434
+
1435
+ if (major !== null && major < 24) {
1436
+ console.warn(
1437
+ `Warning: @andreprado/agentkit requires Node >=24; current Node is ${process.versions.node}. Use Node 24+ before relying on local runtime or deploy commands.`,
1438
+ );
1439
+ }
1440
+ }
1441
+
1442
+ function majorNodeVersion(version: string): number | null {
1443
+ const [major] = version.split(".");
1444
+ const parsed = Number(major);
1445
+ return Number.isInteger(parsed) ? parsed : null;
1446
+ }
1447
+
1448
+ function printDeployProductionHandoff(input: {
1449
+ deployUrl: string;
1450
+ statePath: string;
1451
+ chatAccess: Awaited<ReturnType<typeof ensureDeployChatAccessToken>>;
1452
+ databaseSchemaPath: string | null;
1453
+ readinessReport: Awaited<ReturnType<typeof checkDeployReadiness>> | null;
1454
+ smokeMessage?: string;
1455
+ }): void {
1456
+ const context = input.readinessReport?.context;
1457
+ const userSecrets = context?.userManagedSecrets ?? [];
1458
+ const managedSecrets = context?.agentKitManagedSecrets ?? [];
1459
+ const composio = context?.integrations.managedComposio;
1460
+
1461
+ console.log("");
1462
+ console.log("Production handoff");
1463
+ console.log(`URL: ${input.deployUrl}`);
1464
+ console.log(`UI: npm run agentkit -- chat-ui --deploy`);
1465
+ console.log(`State: ${input.statePath}`);
1466
+ console.log(
1467
+ `Secrets: ${
1468
+ userSecrets.length > 0
1469
+ ? `user-managed set (${userSecrets.join(", ")})`
1470
+ : "no user-managed hosted secrets required"
1471
+ }${managedSecrets.length > 0 ? `; AgentKit-managed: ${managedSecrets.join(", ")}` : ""}`,
1472
+ );
1473
+ console.log(
1474
+ `Database: ${
1475
+ input.databaseSchemaPath
1476
+ ? `AgentKit-managed; schema artifact ${input.databaseSchemaPath}`
1477
+ : "AgentKit-managed; no application schema artifact"
1478
+ }`,
1479
+ );
1480
+
1481
+ if (composio?.enabled) {
1482
+ console.log(`Integrations: managed Composio configured for ${composio.toolkits.join(", ")}`);
1483
+ for (const toolkit of composio.toolkits) {
1484
+ console.log(`Connect: npm run agentkit -- integrations connect composio --toolkit ${toolkit}`);
1485
+ }
1486
+ } else {
1487
+ console.log("Integrations: none configured");
1488
+ }
1489
+
1490
+ if (input.smokeMessage) {
1491
+ console.log(`Smoke: verified with ${JSON.stringify(input.smokeMessage)}`);
1492
+ } else {
1493
+ console.log('Smoke: not run; next check: npm run agentkit -- deploy smoke --message "hello"');
1494
+ }
1495
+
1496
+ if (input.chatAccess.status === "ready") {
1497
+ console.log(`Chat token: ${relativePath(process.cwd(), input.chatAccess.path)}`);
1498
+ } else if (input.chatAccess.status === "skipped") {
1499
+ console.log("Chat token: not created for anonymous deploy");
1500
+ } else {
1501
+ console.log(`Chat token: failed; run npm run agentkit -- access token create ${defaultDeployChatAccessTokenName} --out ${defaultDeployChatAccessTokenPath}`);
1502
+ }
1503
+
1504
+ const nextCommand = composio?.enabled && composio.toolkits.length > 0
1505
+ ? `npm run agentkit -- integrations connect composio --toolkit ${composio.toolkits[0]}`
1506
+ : "npm run agentkit -- chat-ui --deploy";
1507
+ console.log(`Next: ${nextCommand}`);
1508
+ }
1509
+
1510
+ function printDeployCloudSummary(report: Awaited<ReturnType<typeof checkDeployReadiness>> | null): void {
1511
+ if (!report) {
1512
+ return;
1513
+ }
1514
+
1515
+ const authSummary = formatDeployCloudAuthSummary(report);
1516
+
1517
+ if (authSummary) {
1518
+ console.log(`Account: ${authSummary.account}`);
1519
+ console.log(`Auth: ${authSummary.source}`);
1520
+ }
1521
+
1522
+ const capacitySummary = formatDeployOnlineCapacity(report, "before deploy");
1523
+
1524
+ if (capacitySummary) {
1525
+ console.log(`Capacity: ${capacitySummary}`);
1526
+ }
1527
+ }
1528
+
928
1529
  async function ensureDeployChatAccessToken(options: {
929
1530
  apiUrl: string;
930
1531
  root: string;
@@ -949,9 +1550,12 @@ async function ensureDeployChatAccessToken(options: {
949
1550
  `/v1/deploys/${encodeURIComponent(options.deployId)}/access-tokens`,
950
1551
  { method: "GET" },
951
1552
  )) as DeployAccessTokenListResponse;
952
- const stillExists = (listed.access_tokens ?? []).some((token) => token.id === existing.id);
1553
+ const stillUsable = (listed.access_tokens ?? []).some(
1554
+ (token) => token.id === existing.id && deployAccessTokenMetadataIsUsable(token),
1555
+ );
953
1556
 
954
- if (stillExists) {
1557
+ if (stillUsable) {
1558
+ await chmod(path, 0o600);
955
1559
  return { status: "ready", path, tokenId: existing.id, created: false };
956
1560
  }
957
1561
  }
@@ -977,6 +1581,15 @@ async function ensureDeployChatAccessToken(options: {
977
1581
  }
978
1582
  }
979
1583
 
1584
+ function deployAccessTokenMetadataIsUsable(token: { expires_at?: string }): boolean {
1585
+ if (!token.expires_at) {
1586
+ return true;
1587
+ }
1588
+
1589
+ const expiresAtMs = Date.parse(token.expires_at);
1590
+ return Number.isFinite(expiresAtMs) && expiresAtMs > Date.now();
1591
+ }
1592
+
980
1593
  async function readDeployAccessTokenFile(path: string): Promise<{ id?: string; deploy_id?: string } | null> {
981
1594
  try {
982
1595
  const parsed = JSON.parse(await readFile(path, "utf8")) as DeployAccessTokenCreateResponse;
@@ -989,6 +1602,7 @@ async function readDeployAccessTokenFile(path: string): Promise<{ id?: string; d
989
1602
  async function writeSecretJson(path: string, value: unknown): Promise<void> {
990
1603
  await mkdir(dirname(path), { recursive: true });
991
1604
  await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
1605
+ await chmod(path, 0o600);
992
1606
  }
993
1607
 
994
1608
  async function requireLocalDeployState(): Promise<LocalDeployState> {
@@ -1009,11 +1623,7 @@ function requireDeployUrl(state: Pick<LocalDeployState, "deploy_id" | "url">): s
1009
1623
  return state.url;
1010
1624
  }
1011
1625
 
1012
- async function readDeployAccessTokenForSmoke(cwd: string, state: LocalDeployState): Promise<string | undefined> {
1013
- if (state.access?.mode === "public") {
1014
- return undefined;
1015
- }
1016
-
1626
+ async function readDeployAccessTokenForHostedRequest(cwd: string): Promise<string> {
1017
1627
  const root = await findAgentCapsuleRoot(cwd);
1018
1628
  return readDeployAccessTokenForUi(join(root, defaultDeployChatAccessTokenPath));
1019
1629
  }
@@ -1060,10 +1670,90 @@ async function runDeploySmoke(input: { deployUrl: string; message: string; token
1060
1670
  }
1061
1671
  }
1062
1672
 
1673
+ async function fetchDeployConversationJson(cwd: string, path: string): Promise<unknown> {
1674
+ const state = await requireLocalDeployState();
1675
+ const deployUrl = requireDeployUrl(state);
1676
+ const token = await readDeployAccessTokenForHostedRequest(cwd);
1677
+ const headers = new Headers();
1678
+
1679
+ headers.set("Authorization", `Bearer ${token}`);
1680
+
1681
+ let response: Response;
1682
+
1683
+ try {
1684
+ response = await fetch(new URL(path, deployUrl).href, {
1685
+ method: "GET",
1686
+ headers,
1687
+ });
1688
+ } catch (error) {
1689
+ throw new AgentKitError(
1690
+ "deploy_conversation_request_failed",
1691
+ `Could not read hosted conversation data: ${error instanceof Error ? error.message : String(error)}`,
1692
+ );
1693
+ }
1694
+
1695
+ const payload = await response.json().catch(() => null);
1696
+
1697
+ if (!response.ok) {
1698
+ throw new AgentKitError("deploy_conversation_request_failed", readCloudError(payload, response.status));
1699
+ }
1700
+
1701
+ return payload;
1702
+ }
1703
+
1704
+ async function fetchDeployImproveEvidence(
1705
+ apiUrl: string,
1706
+ input: { deployId: string; since?: string; conversationId?: string },
1707
+ ): Promise<unknown | null> {
1708
+ try {
1709
+ return await cloudApiRequest(apiUrl, `/v1/deploys/${encodeURIComponent(input.deployId)}/improve/export`, {
1710
+ method: "POST",
1711
+ body: JSON.stringify({
1712
+ status: "all",
1713
+ ...(input.since ? { since: input.since } : {}),
1714
+ ...(input.conversationId ? { conversation_id: input.conversationId } : {}),
1715
+ }),
1716
+ });
1717
+ } catch (error) {
1718
+ if (
1719
+ isAgentKitError(error) &&
1720
+ (error.code === "improve_evidence_store_not_configured" || error.code === "not_found")
1721
+ ) {
1722
+ return null;
1723
+ }
1724
+
1725
+ throw error;
1726
+ }
1727
+ }
1728
+
1063
1729
  function printJson(value: unknown): void {
1064
1730
  console.log(JSON.stringify(value, null, 2));
1065
1731
  }
1066
1732
 
1733
+ function printSkillStatus(status: Awaited<ReturnType<typeof checkDefaultSkillStatus>>): void {
1734
+ console.log("AgentKit skills");
1735
+ console.log(`Status: ${status.status}`);
1736
+ console.log(`Checked: ${status.checked}`);
1737
+
1738
+ if (status.missing.length > 0) {
1739
+ console.log(`Missing: ${status.missing.length}`);
1740
+ for (const file of status.missing.slice(0, 10)) {
1741
+ console.log(` ${file}`);
1742
+ }
1743
+ }
1744
+
1745
+ if (status.outdated.length > 0) {
1746
+ console.log(`Outdated: ${status.outdated.length}`);
1747
+ for (const file of status.outdated.slice(0, 10)) {
1748
+ console.log(` ${file}`);
1749
+ }
1750
+ }
1751
+
1752
+ if (status.status !== "current") {
1753
+ console.log("Run: npm run agentkit -- skills sync");
1754
+ }
1755
+ }
1756
+
1067
1757
  async function printJsonOrWrite(value: unknown, outFlag: string | boolean | undefined, usage: string): Promise<void> {
1068
1758
  const outPath = parseOptionalStringFlag(outFlag, "out", usage);
1069
1759
 
@@ -1077,6 +1767,20 @@ async function printJsonOrWrite(value: unknown, outFlag: string | boolean | unde
1077
1767
  console.log(`Deploy access token written to ${absolutePath}`);
1078
1768
  }
1079
1769
 
1770
+ function parsePositiveIntegerFlag(value: string | boolean | undefined, name: string, usage: string): number {
1771
+ if (typeof value !== "string") {
1772
+ throw new Error(`Missing ${name}. Usage: ${usage}`);
1773
+ }
1774
+
1775
+ const parsed = Number(value);
1776
+
1777
+ if (!Number.isInteger(parsed) || parsed <= 0) {
1778
+ throw new Error(`Invalid ${name}. Usage: ${usage}`);
1779
+ }
1780
+
1781
+ return parsed;
1782
+ }
1783
+
1080
1784
  function relativePath(root: string, path: string): string {
1081
1785
  const output = relative(root, path);
1082
1786
  return output.startsWith("..") ? path : output || ".";
@@ -1272,5 +1976,6 @@ main().catch((error: unknown) => {
1272
1976
  const message = error instanceof Error ? error.message : String(error);
1273
1977
  const prefix = isAgentKitError(error) ? `agentkit ${error.code}` : "agentkit";
1274
1978
  console.error(`${prefix}: ${message}`);
1979
+ console.error("Feedback draft: agentkit feedback create --about last-run --kind bug --summary <text>");
1275
1980
  process.exitCode = 1;
1276
1981
  });