@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
@@ -0,0 +1,95 @@
1
+ import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
2
+ import { dirname, join, relative } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ export type DefaultSkillFile = {
6
+ path: string;
7
+ contents: string;
8
+ };
9
+
10
+ export type DefaultSkillStatus = {
11
+ status: "current" | "missing" | "outdated";
12
+ checked: number;
13
+ missing: string[];
14
+ outdated: string[];
15
+ };
16
+
17
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
18
+
19
+ export async function syncDefaultSkillFiles(projectPath: string): Promise<{ files: string[] }> {
20
+ const files = await loadDefaultSkillFiles();
21
+
22
+ for (const file of files) {
23
+ const absolutePath = join(projectPath, file.path);
24
+
25
+ await mkdir(dirname(absolutePath), { recursive: true });
26
+ await writeFile(absolutePath, file.contents);
27
+ }
28
+
29
+ return { files: files.map((file) => file.path) };
30
+ }
31
+
32
+ export async function checkDefaultSkillStatus(projectPath: string): Promise<DefaultSkillStatus> {
33
+ const files = await loadDefaultSkillFiles();
34
+ const missing: string[] = [];
35
+ const outdated: string[] = [];
36
+
37
+ for (const file of files) {
38
+ const absolutePath = join(projectPath, file.path);
39
+
40
+ if (!(await pathExists(absolutePath))) {
41
+ missing.push(file.path);
42
+ continue;
43
+ }
44
+
45
+ const current = await readFile(absolutePath, "utf8");
46
+ if (current !== file.contents) {
47
+ outdated.push(file.path);
48
+ }
49
+ }
50
+
51
+ return {
52
+ status: missing.length > 0 ? "missing" : outdated.length > 0 ? "outdated" : "current",
53
+ checked: files.length,
54
+ missing,
55
+ outdated,
56
+ };
57
+ }
58
+
59
+ export async function loadDefaultSkillFiles(): Promise<DefaultSkillFile[]> {
60
+ const sourceRoot = join(moduleDir, "..", "templates", "skills");
61
+ const sourceFiles = await listFiles(sourceRoot);
62
+
63
+ return Promise.all(
64
+ sourceFiles.map(async (sourcePath) => ({
65
+ path: join("skills", relative(sourceRoot, sourcePath)).replaceAll("\\", "/"),
66
+ contents: await readFile(sourcePath, "utf8"),
67
+ })),
68
+ );
69
+ }
70
+
71
+ async function listFiles(directory: string): Promise<string[]> {
72
+ const entries = await readdir(directory, { withFileTypes: true });
73
+ const files = await Promise.all(
74
+ entries.map(async (entry) => {
75
+ const path = join(directory, entry.name);
76
+
77
+ if (entry.isDirectory()) {
78
+ return listFiles(path);
79
+ }
80
+
81
+ return entry.isFile() ? [path] : [];
82
+ }),
83
+ );
84
+
85
+ return files.flat().sort();
86
+ }
87
+
88
+ async function pathExists(path: string): Promise<boolean> {
89
+ try {
90
+ await stat(path);
91
+ return true;
92
+ } catch {
93
+ return false;
94
+ }
95
+ }
@@ -36,6 +36,7 @@ export type AgentManifest = {
36
36
  prompt: SharedAgentManifest["prompt"];
37
37
  secrets: SharedAgentManifest["secrets"];
38
38
  tools: SharedAgentManifest["tools"];
39
+ integrations: SharedAgentManifest["integrations"];
39
40
  channels: SharedAgentManifest["channels"];
40
41
  transcription: SharedAgentManifest["transcription"];
41
42
  knowledge: SharedAgentManifest["knowledge"];
@@ -198,6 +199,7 @@ function buildManifest(
198
199
  prompt: shared.prompt,
199
200
  secrets: shared.secrets,
200
201
  tools: shared.tools,
202
+ integrations: shared.integrations,
201
203
  channels: shared.channels,
202
204
  transcription: shared.transcription,
203
205
  knowledge: shared.knowledge,
@@ -383,6 +385,23 @@ export function defineTool(tool) {
383
385
  return tool;
384
386
  }
385
387
 
388
+ export function composioManaged(input) {
389
+ const name = normalizeIntegrationName(input.name ?? "composio", "composio integration name");
390
+ const toolkits = normalizeToolkitList(input.toolkits, "composioManaged.toolkits");
391
+ const tools = input.tools ? normalizeComposioToolMap(input.tools, toolkits) : undefined;
392
+ const tags = input.tags ? normalizeIntegrationTags(input.tags) : undefined;
393
+
394
+ return {
395
+ type: "composio",
396
+ provider: "composio",
397
+ mode: "managed",
398
+ name,
399
+ toolkits,
400
+ ...(tools ? { tools } : {}),
401
+ ...(tags ? { tags } : {}),
402
+ };
403
+ }
404
+
386
405
  export function websiteChannel(input) {
387
406
  return {
388
407
  type: "website",
@@ -440,6 +459,105 @@ function normalizeChannelSecrets(secrets, context) {
440
459
  return Array.from(uniqueSecrets).sort();
441
460
  }
442
461
 
462
+ function normalizeIntegrationName(value, context) {
463
+ const name = value.trim();
464
+
465
+ if (!/^[a-z][a-z0-9-]*$/.test(name)) {
466
+ throw new Error(\`\${context} must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens.\`);
467
+ }
468
+
469
+ return name;
470
+ }
471
+
472
+ function normalizeToolkitList(values, context) {
473
+ if (!Array.isArray(values) || values.length === 0) {
474
+ throw new Error(\`\${context} must include at least one toolkit slug.\`);
475
+ }
476
+
477
+ const toolkits = new Set();
478
+
479
+ for (const value of values) {
480
+ if (typeof value !== "string") {
481
+ throw new Error(\`\${context} entries must be toolkit slug strings.\`);
482
+ }
483
+
484
+ const toolkit = value.trim().toLowerCase();
485
+
486
+ if (!/^[a-z][a-z0-9_-]*$/.test(toolkit)) {
487
+ throw new Error(\`\${context} entries must start with a letter and contain only letters, numbers, underscores, and hyphens.\`);
488
+ }
489
+
490
+ toolkits.add(toolkit);
491
+ }
492
+
493
+ return Array.from(toolkits).sort();
494
+ }
495
+
496
+ function normalizeComposioToolMap(values, toolkits) {
497
+ const toolkitSet = new Set(toolkits);
498
+ const normalized = {};
499
+
500
+ for (const [rawToolkit, rawTools] of Object.entries(values)) {
501
+ const toolkit = rawToolkit.trim().toLowerCase();
502
+
503
+ if (!toolkitSet.has(toolkit)) {
504
+ throw new Error(\`composioManaged.tools.\${rawToolkit} must reference a toolkit listed in composioManaged.toolkits.\`);
505
+ }
506
+
507
+ if (!Array.isArray(rawTools) || rawTools.length === 0) {
508
+ throw new Error(\`composioManaged.tools.\${rawToolkit} must include at least one Composio action slug.\`);
509
+ }
510
+
511
+ const tools = new Set();
512
+
513
+ for (const rawTool of rawTools) {
514
+ if (typeof rawTool !== "string") {
515
+ throw new Error(\`composioManaged.tools.\${rawToolkit} entries must be Composio action slug strings.\`);
516
+ }
517
+
518
+ const tool = rawTool.trim().toUpperCase();
519
+
520
+ if (!/^[A-Z][A-Z0-9_]*$/.test(tool)) {
521
+ throw new Error(\`composioManaged.tools.\${rawToolkit} entries must be uppercase Composio action slugs.\`);
522
+ }
523
+
524
+ tools.add(tool);
525
+ }
526
+
527
+ normalized[toolkit] = Array.from(tools).sort();
528
+ }
529
+
530
+ return Object.keys(normalized).length > 0 ? normalized : undefined;
531
+ }
532
+
533
+ function normalizeIntegrationTags(tags) {
534
+ const normalized = {};
535
+
536
+ if (tags.enable !== undefined) {
537
+ normalized.enable = normalizeTagList(tags.enable, "composioManaged.tags.enable");
538
+ }
539
+
540
+ if (tags.disable !== undefined) {
541
+ normalized.disable = normalizeTagList(tags.disable, "composioManaged.tags.disable");
542
+ }
543
+
544
+ return normalized;
545
+ }
546
+
547
+ function normalizeTagList(values, context) {
548
+ if (!Array.isArray(values)) {
549
+ throw new Error(\`\${context} must be an array of tag strings.\`);
550
+ }
551
+
552
+ return Array.from(new Set(values.map((value) => {
553
+ if (typeof value !== "string" || value.trim().length === 0) {
554
+ throw new Error(\`\${context} entries must be non-empty strings.\`);
555
+ }
556
+
557
+ return value.trim();
558
+ }))).sort();
559
+ }
560
+
443
561
  function normalizeChannelBuffer(input, context) {
444
562
  if (input.mode === "off") {
445
563
  return { mode: "off" };
@@ -542,17 +660,18 @@ function renderCloudflareWorker(input: { manifest: AgentManifest; instructions:
542
660
  const instructionsJson = JSON.stringify(input.instructions);
543
661
  const configImportPath = JSON.stringify(input.configPath);
544
662
  const providerImportPath = JSON.stringify(resolve(moduleDir, "../../../providers/index.ts"));
663
+ const composioIntegrationImportPath = JSON.stringify(resolve(moduleDir, "../../integrations/composio.ts"));
545
664
  const promptContextImportPath = JSON.stringify(resolve(moduleDir, "../../prompt-context.ts"));
546
665
 
547
666
  return `import { DurableObject } from "cloudflare:workers";
548
667
  import agentConfig from ${configImportPath};
549
668
  import { getProviderAdapter } from ${providerImportPath};
669
+ import { MANAGED_COMPOSIO_USER_ID_BINDING, createManagedComposioTools } from ${composioIntegrationImportPath};
550
670
  import { appendRuntimePromptContext, buildRuntimeClockContext } from ${promptContextImportPath};
551
671
 
552
672
  const manifest = ${manifestJson};
553
673
  const instructions = ${instructionsJson};
554
674
  const hostedKnowledgeTool = manifest.knowledge.enabled ? createHostedKnowledgeSearchTool() : null;
555
- const hostedTools = hostedKnowledgeTool ? [hostedKnowledgeTool, ...(agentConfig.tools ?? [])] : (agentConfig.tools ?? []);
556
675
  const DEFAULT_TOOL_TIMEOUT_MS = 30000;
557
676
 
558
677
  export class AgentKitConversationStore extends DurableObject {
@@ -612,6 +731,22 @@ export class AgentKitConversationStore extends DurableObject {
612
731
  return this.getMessages(url.searchParams.get("conversationId"));
613
732
  }
614
733
 
734
+ if (request.method === "GET" && url.pathname === "/conversations") {
735
+ return this.listConversations();
736
+ }
737
+
738
+ const conversationTraceMatch = url.pathname.match(/^\\/conversations\\/([^/]+)\\/trace$/);
739
+
740
+ if (request.method === "GET" && conversationTraceMatch) {
741
+ return this.getConversationTrace(decodeURIComponent(conversationTraceMatch[1] || ""));
742
+ }
743
+
744
+ const conversationMatch = url.pathname.match(/^\\/conversations\\/([^/]+)$/);
745
+
746
+ if (request.method === "GET" && conversationMatch) {
747
+ return this.getConversation(decodeURIComponent(conversationMatch[1] || ""));
748
+ }
749
+
615
750
  return Response.json({ error: { code: "not_found", message: "Unknown Durable Object route." } }, { status: 404 });
616
751
  }
617
752
 
@@ -635,6 +770,128 @@ export class AgentKitConversationStore extends DurableObject {
635
770
  });
636
771
  }
637
772
 
773
+ listConversations() {
774
+ const rows = this.sql.exec(\`
775
+ SELECT conversations.id, conversations.agent_name, conversations.title, conversations.created_at, conversations.updated_at, COUNT(messages.id) AS message_count
776
+ FROM conversations
777
+ LEFT JOIN messages ON messages.conversation_id = conversations.id
778
+ GROUP BY conversations.id, conversations.agent_name, conversations.title, conversations.created_at, conversations.updated_at
779
+ ORDER BY conversations.updated_at DESC
780
+ \`).toArray();
781
+
782
+ return Response.json({
783
+ conversations: rows.map((row) => ({
784
+ id: String(row.id),
785
+ agentName: String(row.agent_name),
786
+ title: row.title === null || row.title === undefined ? null : String(row.title),
787
+ createdAt: String(row.created_at),
788
+ updatedAt: String(row.updated_at),
789
+ messageCount: Number(row.message_count || 0),
790
+ })),
791
+ });
792
+ }
793
+
794
+ getConversation(conversationId) {
795
+ const conversation = this.readConversation(conversationId);
796
+
797
+ if (!conversation) {
798
+ return Response.json(
799
+ { error: { code: "conversation_not_found", message: \`Conversation "\${conversationId}" was not found.\` } },
800
+ { status: 404 },
801
+ );
802
+ }
803
+
804
+ return Response.json(conversation);
805
+ }
806
+
807
+ getConversationTrace(conversationId) {
808
+ const conversation = this.readConversation(conversationId);
809
+
810
+ if (!conversation) {
811
+ return Response.json(
812
+ { error: { code: "conversation_not_found", message: \`Conversation "\${conversationId}" was not found.\` } },
813
+ { status: 404 },
814
+ );
815
+ }
816
+
817
+ const runs = this.sql.exec(
818
+ "SELECT id, conversation_id, provider, model, status, error_code, error_message, created_at, completed_at FROM runs WHERE conversation_id = ? ORDER BY rowid ASC",
819
+ conversationId,
820
+ ).toArray();
821
+
822
+ return Response.json({
823
+ ...conversation,
824
+ runs: runs.map((run) => ({
825
+ id: String(run.id),
826
+ conversationId: String(run.conversation_id),
827
+ provider: String(run.provider),
828
+ model: String(run.model),
829
+ status: String(run.status),
830
+ errorCode: run.error_code === null || run.error_code === undefined ? null : String(run.error_code),
831
+ errorMessage: run.error_message === null || run.error_message === undefined ? null : String(run.error_message),
832
+ startedAt: String(run.created_at),
833
+ completedAt: run.completed_at === null || run.completed_at === undefined ? null : String(run.completed_at),
834
+ toolCalls: this.readToolCalls(String(run.id)),
835
+ })),
836
+ });
837
+ }
838
+
839
+ readConversation(conversationId) {
840
+ if (!conversationId) {
841
+ return null;
842
+ }
843
+
844
+ const row = this.sql.exec(
845
+ "SELECT id, agent_name, title, created_at, updated_at FROM conversations WHERE id = ?",
846
+ conversationId,
847
+ ).one();
848
+
849
+ if (!row) {
850
+ return null;
851
+ }
852
+
853
+ const messages = this.sql.exec(
854
+ "SELECT id, conversation_id, role, content, created_at FROM messages WHERE conversation_id = ? ORDER BY rowid ASC",
855
+ conversationId,
856
+ ).toArray();
857
+
858
+ return {
859
+ id: String(row.id),
860
+ agentName: String(row.agent_name),
861
+ title: row.title === null || row.title === undefined ? null : String(row.title),
862
+ createdAt: String(row.created_at),
863
+ updatedAt: String(row.updated_at),
864
+ messages: messages.map((message) => ({
865
+ id: String(message.id),
866
+ conversationId: String(message.conversation_id),
867
+ role: String(message.role),
868
+ content: String(message.content),
869
+ createdAt: String(message.created_at),
870
+ })),
871
+ };
872
+ }
873
+
874
+ readToolCalls(runId) {
875
+ const rows = this.sql.exec(
876
+ "SELECT id, run_id, conversation_id, tool_name, visibility, status, input_json, output_json, error_message, created_at, completed_at FROM tool_calls WHERE run_id = ? ORDER BY rowid ASC",
877
+ runId,
878
+ ).toArray();
879
+
880
+ return rows.map((row) => ({
881
+ id: String(row.id),
882
+ runId: String(row.run_id),
883
+ conversationId: String(row.conversation_id),
884
+ name: String(row.tool_name),
885
+ visibility: String(row.visibility),
886
+ status: String(row.status),
887
+ input: parseStoredJson(row.input_json, {}),
888
+ output: row.output_json === null || row.output_json === undefined ? undefined : parseStoredJson(row.output_json, null),
889
+ errorMessage: row.error_message === null || row.error_message === undefined ? null : String(row.error_message),
890
+ createdAt: String(row.created_at),
891
+ completedAt: row.completed_at === null || row.completed_at === undefined ? null : String(row.completed_at),
892
+ }));
893
+ }
894
+
638
895
  persistMessages(input) {
639
896
  const now = new Date().toISOString();
640
897
  const conversationId = String(input.conversationId || crypto.randomUUID());
@@ -721,13 +978,16 @@ export default {
721
978
  return auth;
722
979
  }
723
980
 
981
+ const hostedTools = createHostedTools(env);
982
+
724
983
  return Response.json({
725
984
  agent: manifest.name,
726
985
  runtime: manifest.runtime,
727
986
  target: manifest.target,
728
987
  provider: manifest.provider,
729
988
  prompt: manifest.prompt,
730
- tools: manifest.tools.map((tool) => tool.name),
989
+ tools: hostedTools.map((tool) => tool.name),
990
+ integrations: hostedIntegrations(env),
731
991
  channels: manifest.channels.map((channel) => ({
732
992
  name: channel.name,
733
993
  type: channel.type,
@@ -765,6 +1025,22 @@ export default {
765
1025
  return handleChat(request, env);
766
1026
  }
767
1027
 
1028
+ if (request.method === "GET" && (url.pathname === "/conversations" || url.pathname === "/v1/conversations")) {
1029
+ return handleConversationRead(request, env, "list");
1030
+ }
1031
+
1032
+ const conversationTraceMatch = url.pathname.match(/^\\/(?:v1\\/)?conversations\\/([^/]+)\\/trace$/);
1033
+
1034
+ if (request.method === "GET" && conversationTraceMatch) {
1035
+ return handleConversationRead(request, env, "trace", decodeURIComponent(conversationTraceMatch[1] || ""));
1036
+ }
1037
+
1038
+ const conversationMatch = url.pathname.match(/^\\/(?:v1\\/)?conversations\\/([^/]+)$/);
1039
+
1040
+ if (request.method === "GET" && conversationMatch) {
1041
+ return handleConversationRead(request, env, "show", decodeURIComponent(conversationMatch[1] || ""));
1042
+ }
1043
+
768
1044
  if (request.method === "POST" && url.pathname.startsWith("/tools/")) {
769
1045
  return handleTool(request, env, decodeURIComponent(url.pathname.slice("/tools/".length)));
770
1046
  }
@@ -1111,6 +1387,39 @@ async function handleChat(request, env) {
1111
1387
  });
1112
1388
  }
1113
1389
 
1390
+ async function handleConversationRead(request, env, action, conversationId) {
1391
+ const auth = await authorizeChat(request, env, {});
1392
+
1393
+ if (auth) {
1394
+ return auth;
1395
+ }
1396
+
1397
+ if (!env.AGENTKIT_CONVERSATIONS) {
1398
+ return Response.json({ error: { code: "conversations_not_configured", message: "Hosted conversation storage is not configured." } }, { status: 503 });
1399
+ }
1400
+
1401
+ if (action === "list") {
1402
+ const stub = hostedConversationStore(env);
1403
+ return stub.fetch("https://agentkit.internal/conversations");
1404
+ }
1405
+
1406
+ if (!conversationId) {
1407
+ return Response.json({ error: { code: "validation_error", message: "Missing conversation id." } }, { status: 400 });
1408
+ }
1409
+
1410
+ const stub = hostedConversationStore(env);
1411
+ const suffix = action === "trace" ? "/trace" : "";
1412
+ const path = \`/conversations/\${encodeURIComponent(conversationId)}\${suffix}\`;
1413
+ const response = await stub.fetch(\`https://agentkit.internal\${path}\`);
1414
+
1415
+ if (response.status !== 404) {
1416
+ return response;
1417
+ }
1418
+
1419
+ const legacyStub = legacyConversationStore(env, conversationId);
1420
+ return legacyStub.fetch(\`https://agentkit.internal\${path}\`);
1421
+ }
1422
+
1114
1423
  function readChatMessage(body) {
1115
1424
  if (typeof body.message === "string") {
1116
1425
  return body.message.trim();
@@ -1129,6 +1438,7 @@ function isRecord(value) {
1129
1438
 
1130
1439
  async function callHostedProvider(env, messages, signal) {
1131
1440
  const adapter = getProviderAdapter(manifest.provider);
1441
+ const hostedTools = createHostedTools(env);
1132
1442
  const clock = buildRuntimeClockContext({
1133
1443
  timeZone: manifest.timeZone,
1134
1444
  env,
@@ -1626,18 +1936,49 @@ function inspectHostedDatabase() {
1626
1936
  }
1627
1937
 
1628
1938
  function hostedManagedSecrets() {
1629
- if (manifest.storage.database.driver !== "turso") {
1630
- return [];
1939
+ const secrets = [];
1940
+
1941
+ if (manifest.storage.database.driver === "turso") {
1942
+ secrets.push("TURSO_AUTH_TOKEN", "TURSO_DATABASE_URL");
1943
+ }
1944
+
1945
+ if (manifest.integrations.some((integration) => integration.provider === "composio" && integration.mode === "managed")) {
1946
+ secrets.push("COMPOSIO_API_KEY");
1947
+ }
1948
+
1949
+ return secrets.sort();
1950
+ }
1951
+
1952
+ function createHostedTools(env) {
1953
+ return [
1954
+ ...(hostedKnowledgeTool ? [hostedKnowledgeTool] : []),
1955
+ ...createManagedComposioTools(agentConfig, { userId: env[MANAGED_COMPOSIO_USER_ID_BINDING] }),
1956
+ ...(agentConfig.tools ?? []),
1957
+ ];
1958
+ }
1959
+
1960
+ function hostedIntegrations(env) {
1961
+ const userId = env[MANAGED_COMPOSIO_USER_ID_BINDING];
1962
+
1963
+ if (!userId) {
1964
+ return manifest.integrations;
1631
1965
  }
1632
1966
 
1633
- return ["TURSO_AUTH_TOKEN", "TURSO_DATABASE_URL"];
1967
+ return manifest.integrations.map((integration) =>
1968
+ integration.provider === "composio" && integration.mode === "managed"
1969
+ ? { ...integration, userId }
1970
+ : integration,
1971
+ );
1634
1972
  }
1635
1973
 
1636
1974
  function createHostedProviderToolRuntime(env, signal, clock) {
1975
+ const hostedTools = createHostedTools(env);
1976
+
1637
1977
  return {
1638
1978
  definitions: hostedTools,
1639
1979
  execute(request) {
1640
1980
  return executeHostedTool({
1981
+ tools: hostedTools,
1641
1982
  name: request.name,
1642
1983
  input: request.input,
1643
1984
  env,
@@ -1650,6 +1991,7 @@ function createHostedProviderToolRuntime(env, signal, clock) {
1650
1991
  }
1651
1992
 
1652
1993
  async function executeHostedTool(request) {
1994
+ const hostedTools = request.tools ?? createHostedTools(request.env);
1653
1995
  const tool = hostedTools.find((candidate) => candidate.name === request.name);
1654
1996
  const id = crypto.randomUUID();
1655
1997
 
@@ -2121,8 +2463,7 @@ async function persist(env, input) {
2121
2463
  return {};
2122
2464
  }
2123
2465
 
2124
- const id = env.AGENTKIT_CONVERSATIONS.idFromName(input.conversationId);
2125
- const stub = env.AGENTKIT_CONVERSATIONS.get(id);
2466
+ const stub = hostedConversationStore(env);
2126
2467
  const response = await stub.fetch("https://agentkit.internal/messages", {
2127
2468
  method: "POST",
2128
2469
  body: JSON.stringify(input),
@@ -2140,8 +2481,7 @@ async function loadConversationMessages(env, conversationId) {
2140
2481
  return [];
2141
2482
  }
2142
2483
 
2143
- const id = env.AGENTKIT_CONVERSATIONS.idFromName(conversationId);
2144
- const stub = env.AGENTKIT_CONVERSATIONS.get(id);
2484
+ const stub = hostedConversationStore(env);
2145
2485
  const response = await stub.fetch(
2146
2486
  \`https://agentkit.internal/messages?conversationId=\${encodeURIComponent(conversationId)}\`,
2147
2487
  );
@@ -2151,7 +2491,7 @@ async function loadConversationMessages(env, conversationId) {
2151
2491
  }
2152
2492
 
2153
2493
  const payload = await response.json();
2154
- return Array.isArray(payload.messages)
2494
+ const messages = Array.isArray(payload.messages)
2155
2495
  ? payload.messages.filter((message) =>
2156
2496
  message &&
2157
2497
  typeof message === "object" &&
@@ -2159,6 +2499,39 @@ async function loadConversationMessages(env, conversationId) {
2159
2499
  typeof message.content === "string"
2160
2500
  )
2161
2501
  : [];
2502
+
2503
+ if (messages.length > 0) {
2504
+ return messages;
2505
+ }
2506
+
2507
+ const legacyStub = legacyConversationStore(env, conversationId);
2508
+ const legacyResponse = await legacyStub.fetch(
2509
+ \`https://agentkit.internal/messages?conversationId=\${encodeURIComponent(conversationId)}\`,
2510
+ );
2511
+
2512
+ if (!legacyResponse.ok) {
2513
+ return [];
2514
+ }
2515
+
2516
+ const legacyPayload = await legacyResponse.json();
2517
+ return Array.isArray(legacyPayload.messages)
2518
+ ? legacyPayload.messages.filter((message) =>
2519
+ message &&
2520
+ typeof message === "object" &&
2521
+ (message.role === "user" || message.role === "assistant") &&
2522
+ typeof message.content === "string"
2523
+ )
2524
+ : [];
2525
+ }
2526
+
2527
+ function hostedConversationStore(env) {
2528
+ const id = env.AGENTKIT_CONVERSATIONS.idFromName("agentkit-conversations");
2529
+ return env.AGENTKIT_CONVERSATIONS.get(id);
2530
+ }
2531
+
2532
+ function legacyConversationStore(env, conversationId) {
2533
+ const id = env.AGENTKIT_CONVERSATIONS.idFromName(conversationId);
2534
+ return env.AGENTKIT_CONVERSATIONS.get(id);
2162
2535
  }
2163
2536
 
2164
2537
  function normalizeConversationId(value) {
@@ -2435,6 +2808,18 @@ function titleFromMessage(content) {
2435
2808
  const compact = content.split(/\\r?\\n/, 1)[0].replace(/\\s+/g, " ").trim();
2436
2809
  return compact.length <= 80 ? compact : \`\${compact.slice(0, 77)}...\`;
2437
2810
  }
2811
+
2812
+ function parseStoredJson(value, fallback) {
2813
+ if (typeof value !== "string") {
2814
+ return fallback;
2815
+ }
2816
+
2817
+ try {
2818
+ return JSON.parse(value);
2819
+ } catch {
2820
+ return fallback;
2821
+ }
2822
+ }
2438
2823
  `;
2439
2824
  }
2440
2825
 
@@ -2,6 +2,7 @@ import { loadAgentCapsule } from "./config";
2
2
  import { openPreparedCapsuleStore } from "./database";
3
3
  import { loadCapsuleEnv } from "./env";
4
4
  import { isAgentKitError } from "./errors";
5
+ import { createManagedComposioTools } from "./integrations/composio";
5
6
  import { buildRuntimeClockContext } from "./prompt-context";
6
7
  import { createToolRuntime, type ToolCallResult } from "./tools";
7
8
 
@@ -26,7 +27,7 @@ export async function runToolFromCwd(cwd: string, options: RunToolOptions): Prom
26
27
  model: capsule.config.provider.model,
27
28
  });
28
29
  const toolRuntime = createToolRuntime({
29
- tools: capsule.config.tools ?? [],
30
+ tools: [...createManagedComposioTools(capsule.config), ...(capsule.config.tools ?? [])],
30
31
  store,
31
32
  runId: run.id,
32
33
  conversationId: conversation.id,
@@ -19,6 +19,7 @@ Use this first inside an AgentKit Agent Capsule.
19
19
  - Build or reshape the agent from the owner's brief: `skills/agentkit-build-agent/SKILL.md`
20
20
  - Edit prompts: `skills/agentkit-prompts/SKILL.md`
21
21
  - Add actions or external data: `skills/agentkit-tools/SKILL.md`
22
+ - Add AgentKit-managed integrations such as managed Composio: `skills/agentkit-integrations/SKILL.md`
22
23
  - Add database tables or database-backed tools: `skills/agentkit-database/SKILL.md`
23
24
  - Add docs, FAQs, prices, policies, or CSV facts: `skills/agentkit-knowledge/SKILL.md`
24
25
  - Switch from `test/fake` to a real model provider: `skills/agentkit-provider/SKILL.md`
@@ -80,6 +80,8 @@ npm run agentkit -- channels connect telegram support-telegram
80
80
  npm run agentkit -- channels add whatsapp support-whatsapp --provider zapster
81
81
  npm run agentkit -- channels doctor support-telegram
82
82
  npm run agentkit -- channels test support-telegram --message "hello"
83
+ npm run agentkit -- channels test-audio support-telegram --fixture voice-note
84
+ npm run agentkit -- transcribe smoke --provider groq
83
85
  npm run agentkit -- channels deliveries list support-telegram
84
86
  ```
85
87
 
@@ -7,6 +7,8 @@ agentkit channels list
7
7
  agentkit channels status <name>
8
8
  agentkit channels doctor <name>
9
9
  agentkit channels test <name> --message "hello"
10
+ agentkit channels test-audio <name> --fixture voice-note
11
+ agentkit transcribe smoke --provider groq
10
12
  agentkit channels deliveries list <name> --since 24h
11
13
  agentkit channels deliveries show <delivery-id>
12
14
  agentkit channels buffers list <name>
@@ -42,7 +44,7 @@ skipped
42
44
 
43
45
  Common errors:
44
46
 
45
- - `channel_not_found`: webhook URL points to an unknown channel.
47
+ - `channel_not_found`: webhook URL points to an unknown channel. `channels test` is the official synthetic smoke and should resolve the current `.agentkit/deploy.json` channel.
46
48
  - `channel_secret_missing`: required hosted secret is not set.
47
49
  - `channel_signature_invalid`: webhook secret, token, or origin header mismatch.
48
50
  - `channel_payload_invalid`: malformed or unsupported provider payload.