@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.
- package/README.md +8 -0
- package/docs/guides/add-channel.md +4 -2
- package/docs/guides/add-managed-composio.md +161 -0
- package/docs/guides/channel-security.md +36 -39
- package/docs/guides/connect-telegram.md +3 -1
- package/docs/guides/debug-channel.md +141 -0
- package/docs/guides/prepare-deploy.md +7 -10
- package/docs/llms-full.txt +19 -5
- package/docs/llms.txt +11 -2
- package/package.json +1 -3
- package/src/cli/cloud-client.ts +12 -0
- package/src/cli/commands/channels.ts +119 -7
- package/src/cli/commands/transcribe.ts +171 -0
- package/src/cli/deploy-chat-ui.ts +146 -3
- package/src/cli/deploy-readiness.ts +112 -3
- package/src/cli/help.ts +19 -4
- package/src/cli/index.ts +198 -3
- package/src/create-project.ts +4 -32
- package/src/index.ts +149 -0
- package/src/runtime/chat.ts +2 -1
- package/src/runtime/config.ts +133 -0
- package/src/runtime/core/manifest.ts +31 -2
- package/src/runtime/deploy-readiness.ts +31 -1
- package/src/runtime/inspect.ts +109 -4
- package/src/runtime/integrations/composio.ts +423 -0
- package/src/runtime/skills.ts +95 -0
- package/src/runtime/targets/cloudflare/build.ts +395 -10
- package/src/runtime/tool-runner.ts +2 -1
- package/src/templates/skills/agentkit-capsule/SKILL.md +1 -0
- package/src/templates/skills/agentkit-channels/SKILL.md +2 -0
- package/src/templates/skills/agentkit-channels/references/channel-debugging.md +3 -1
- package/src/templates/skills/agentkit-channels/references/telegram.md +3 -1
- package/src/templates/skills/agentkit-deploy/SKILL.md +1 -1
- package/src/templates/skills/agentkit-integrations/SKILL.md +75 -0
- package/src/templates/skills/agentkit-troubleshooting/SKILL.md +3 -2
- package/docs/guides/agentkit-skills-architecture.md +0 -472
- package/docs/guides/channels-implementation-map.md +0 -243
- package/docs/guides/channels-production-handoff.md +0 -118
- 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:
|
|
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
|
-
|
|
1630
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|