@tellet/create 0.8.0
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 +195 -0
- package/dist/ai/generate.d.ts +33 -0
- package/dist/ai/generate.js +108 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +337 -0
- package/dist/scaffold/project.d.ts +44 -0
- package/dist/scaffold/project.js +318 -0
- package/package.json +48 -0
- package/template/Dockerfile +35 -0
- package/template/app/(dashboard)/agents/page.tsx +14 -0
- package/template/app/(dashboard)/conversations/[id]/page.tsx +103 -0
- package/template/app/(dashboard)/conversations/page.tsx +50 -0
- package/template/app/(dashboard)/dashboard/page.tsx +102 -0
- package/template/app/(dashboard)/layout.tsx +15 -0
- package/template/app/(dashboard)/settings/page.tsx +46 -0
- package/template/app/(site)/layout.tsx +3 -0
- package/template/app/(site)/page.tsx +25 -0
- package/template/app/api/chat/route.ts +129 -0
- package/template/app/api/cron/route.ts +29 -0
- package/template/app/api/orchestrator/route.ts +139 -0
- package/template/app/globals.css +30 -0
- package/template/app/layout.tsx +18 -0
- package/template/components/chat/ChatWidget.tsx +109 -0
- package/template/components/chat/Markdown.tsx +136 -0
- package/template/components/dashboard/AgentChat.tsx +192 -0
- package/template/components/dashboard/AgentsListClient.tsx +86 -0
- package/template/components/dashboard/DashboardAgentGrid.tsx +73 -0
- package/template/components/dashboard/OrchestratorChat.tsx +251 -0
- package/template/components/dashboard/Sidebar.tsx +44 -0
- package/template/components/dashboard/StatsCards.tsx +40 -0
- package/template/components/dashboard/Welcome.tsx +139 -0
- package/template/components/sections/Agents.tsx +67 -0
- package/template/components/sections/CTA.tsx +46 -0
- package/template/components/sections/FAQ.tsx +81 -0
- package/template/components/sections/Features.tsx +51 -0
- package/template/components/sections/Footer.tsx +22 -0
- package/template/components/sections/Hero.tsx +86 -0
- package/template/components/sections/Icons.tsx +29 -0
- package/template/components/ui/Button.tsx +26 -0
- package/template/docker-compose.yml +32 -0
- package/template/infra/bin/app.ts +16 -0
- package/template/infra/cdk.json +6 -0
- package/template/infra/lib/tellet-stack.ts +216 -0
- package/template/infra/package.json +20 -0
- package/template/infra/tsconfig.json +16 -0
- package/template/lib/db.ts +37 -0
- package/template/lib/engine/default.ts +227 -0
- package/template/lib/engine/index.ts +17 -0
- package/template/lib/mcp/client.ts +97 -0
- package/template/lib/mcp/knowledge.ts +84 -0
- package/template/lib/mcp/registry.ts +106 -0
- package/template/lib/orchestrator/executor.ts +202 -0
- package/template/lib/orchestrator/tools.ts +245 -0
- package/template/lib/providers/anthropic.ts +41 -0
- package/template/lib/providers/index.ts +36 -0
- package/template/lib/providers/openai.ts +46 -0
- package/template/lib/scheduler.ts +115 -0
- package/template/lib/supabase.ts +30 -0
- package/template/lib/tellet.ts +45 -0
- package/template/lib/utils.ts +6 -0
- package/template/next.config.ts +7 -0
- package/template/public/widget.js +172 -0
- package/template/railway.toml +9 -0
- package/template/tsconfig.json +21 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { createServerSupabase } from "@/lib/supabase";
|
|
2
|
+
import { addDocument, searchKnowledge, listDocuments, deleteDocument } from "@/lib/mcp/knowledge";
|
|
3
|
+
import { runScheduledAgent } from "@/lib/scheduler";
|
|
4
|
+
import { TOOL_REGISTRY, getToolById } from "@/lib/mcp/registry";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
|
|
8
|
+
export async function executeTool(
|
|
9
|
+
name: string,
|
|
10
|
+
input: Record<string, unknown>
|
|
11
|
+
): Promise<string> {
|
|
12
|
+
const supabase = await createServerSupabase();
|
|
13
|
+
|
|
14
|
+
switch (name) {
|
|
15
|
+
case "list_agents": {
|
|
16
|
+
const { data } = await supabase
|
|
17
|
+
.from("agents")
|
|
18
|
+
.select("id, name, role, status, model")
|
|
19
|
+
.order("created_at");
|
|
20
|
+
return JSON.stringify(data || []);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
case "get_stats": {
|
|
24
|
+
const [
|
|
25
|
+
{ count: conversations },
|
|
26
|
+
{ count: messages },
|
|
27
|
+
{ data: agents },
|
|
28
|
+
{ data: costData },
|
|
29
|
+
] = await Promise.all([
|
|
30
|
+
supabase.from("conversations").select("*", { count: "exact", head: true }),
|
|
31
|
+
supabase.from("messages").select("*", { count: "exact", head: true }),
|
|
32
|
+
supabase.from("agents").select("id, status"),
|
|
33
|
+
supabase.from("activity_log").select("cost_usd"),
|
|
34
|
+
]);
|
|
35
|
+
const activeAgents = (agents || []).filter((a) => a.status === "active").length;
|
|
36
|
+
const totalCost = (costData || []).reduce(
|
|
37
|
+
(sum, r) => sum + Number(r.cost_usd || 0),
|
|
38
|
+
0
|
|
39
|
+
);
|
|
40
|
+
return JSON.stringify({
|
|
41
|
+
conversations: conversations || 0,
|
|
42
|
+
messages: messages || 0,
|
|
43
|
+
activeAgents,
|
|
44
|
+
totalAgents: agents?.length || 0,
|
|
45
|
+
estimatedCost: `$${totalCost.toFixed(2)}`,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
case "update_agent_prompt": {
|
|
50
|
+
const { agent_id, system_prompt } = input as {
|
|
51
|
+
agent_id: string;
|
|
52
|
+
system_prompt: string;
|
|
53
|
+
};
|
|
54
|
+
const { error } = await supabase
|
|
55
|
+
.from("agents")
|
|
56
|
+
.update({ system_prompt })
|
|
57
|
+
.eq("id", agent_id);
|
|
58
|
+
if (error) return JSON.stringify({ error: error.message });
|
|
59
|
+
return JSON.stringify({ success: true, agent_id });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
case "update_site_content": {
|
|
63
|
+
const configPath = path.join(process.cwd(), "tellet.json");
|
|
64
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
65
|
+
|
|
66
|
+
const updates = input as Record<string, unknown>;
|
|
67
|
+
if (updates.tagline) config.site.tagline = updates.tagline;
|
|
68
|
+
if (updates.subtitle) config.site.subtitle = updates.subtitle;
|
|
69
|
+
if (updates.cta) config.site.cta = updates.cta;
|
|
70
|
+
if (updates.features) config.site.features = updates.features;
|
|
71
|
+
if (updates.faq) config.site.faq = updates.faq;
|
|
72
|
+
|
|
73
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
74
|
+
return JSON.stringify({
|
|
75
|
+
success: true,
|
|
76
|
+
message: "Site content updated. Rebuild to see changes.",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
case "get_recent_conversations": {
|
|
81
|
+
const limit = (input.limit as number) || 10;
|
|
82
|
+
const { data } = await supabase
|
|
83
|
+
.from("conversations")
|
|
84
|
+
.select("id, channel, created_at, agents(name, role), messages(count)")
|
|
85
|
+
.order("created_at", { ascending: false })
|
|
86
|
+
.limit(limit);
|
|
87
|
+
return JSON.stringify(data || []);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
case "add_knowledge": {
|
|
91
|
+
const { title, content, category } = input as {
|
|
92
|
+
title: string;
|
|
93
|
+
content: string;
|
|
94
|
+
category?: string;
|
|
95
|
+
};
|
|
96
|
+
return addDocument(title, content, category);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
case "search_knowledge": {
|
|
100
|
+
return searchKnowledge(input.query as string);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
case "list_knowledge": {
|
|
104
|
+
return listDocuments();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
case "delete_knowledge": {
|
|
108
|
+
return deleteDocument(input.document_id as string);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
case "run_agent_task": {
|
|
112
|
+
const result = await runScheduledAgent(
|
|
113
|
+
input.agent_id as string,
|
|
114
|
+
input.task as string
|
|
115
|
+
);
|
|
116
|
+
return JSON.stringify(result);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
case "manage_schedule": {
|
|
120
|
+
const configPath = path.join(process.cwd(), "tellet.json");
|
|
121
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
122
|
+
const agentIdx = config.agents.findIndex(
|
|
123
|
+
(a: { id: string }) => a.id === input.agent_id
|
|
124
|
+
);
|
|
125
|
+
if (agentIdx === -1) return JSON.stringify({ error: "Agent not found" });
|
|
126
|
+
|
|
127
|
+
config.agents[agentIdx].schedule = {
|
|
128
|
+
enabled: input.enabled as boolean,
|
|
129
|
+
cron: (input.cron as string) || config.agents[agentIdx].schedule?.cron || "0 9 * * *",
|
|
130
|
+
task: (input.task as string) || config.agents[agentIdx].schedule?.task || "",
|
|
131
|
+
};
|
|
132
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
133
|
+
return JSON.stringify({ success: true, schedule: config.agents[agentIdx].schedule });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
case "list_available_tools": {
|
|
137
|
+
return JSON.stringify(
|
|
138
|
+
TOOL_REGISTRY.map((t) => ({
|
|
139
|
+
id: t.id,
|
|
140
|
+
name: t.name,
|
|
141
|
+
description: t.description,
|
|
142
|
+
category: t.category,
|
|
143
|
+
requiredKeys: t.envKeys.map((k) => k.key),
|
|
144
|
+
compatibleRoles: t.compatibleRoles,
|
|
145
|
+
}))
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
case "install_tool": {
|
|
150
|
+
const toolId = input.tool_id as string;
|
|
151
|
+
const agentIds = (input.agent_ids as string[]) || [];
|
|
152
|
+
const tool = getToolById(toolId);
|
|
153
|
+
if (!tool) return JSON.stringify({ error: `Tool "${toolId}" not found in marketplace` });
|
|
154
|
+
|
|
155
|
+
const configPath = path.join(process.cwd(), "tellet.json");
|
|
156
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
157
|
+
|
|
158
|
+
// Add tool to tools section
|
|
159
|
+
if (!config.tools) config.tools = {};
|
|
160
|
+
config.tools[toolId] = {
|
|
161
|
+
type: "mcp",
|
|
162
|
+
package: tool.package,
|
|
163
|
+
description: tool.description,
|
|
164
|
+
env: Object.fromEntries(tool.envKeys.map((k) => [k.key, `\${${k.key}}`])),
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// Assign to agents
|
|
168
|
+
if (agentIds.length > 0) {
|
|
169
|
+
for (const aid of agentIds) {
|
|
170
|
+
const agent = config.agents.find((a: { id: string }) => a.id === aid);
|
|
171
|
+
if (agent) {
|
|
172
|
+
if (!agent.tools) agent.tools = [];
|
|
173
|
+
if (!agent.tools.includes(toolId)) agent.tools.push(toolId);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
// Auto-assign to compatible agents
|
|
178
|
+
for (const agent of config.agents) {
|
|
179
|
+
if (tool.compatibleRoles.includes(agent.role)) {
|
|
180
|
+
if (!agent.tools) agent.tools = [];
|
|
181
|
+
if (!agent.tools.includes(toolId)) agent.tools.push(toolId);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
187
|
+
|
|
188
|
+
const envInstructions = tool.envKeys
|
|
189
|
+
.map((k) => ` ${k.key}=${k.placeholder}`)
|
|
190
|
+
.join("\n");
|
|
191
|
+
|
|
192
|
+
return JSON.stringify({
|
|
193
|
+
success: true,
|
|
194
|
+
tool: tool.name,
|
|
195
|
+
message: `Installed ${tool.name}. Add these to your .env:\n${envInstructions}`,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
default:
|
|
200
|
+
return JSON.stringify({ error: `Unknown tool: ${name}` });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import type Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
|
|
3
|
+
export const orchestratorTools: Anthropic.Tool[] = [
|
|
4
|
+
{
|
|
5
|
+
name: "list_agents",
|
|
6
|
+
description:
|
|
7
|
+
"List all AI agents in the company with their name, role, status, and model.",
|
|
8
|
+
input_schema: {
|
|
9
|
+
type: "object" as const,
|
|
10
|
+
properties: {},
|
|
11
|
+
required: [],
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: "get_stats",
|
|
16
|
+
description:
|
|
17
|
+
"Get company statistics: total conversations, messages, active agents, and estimated cost.",
|
|
18
|
+
input_schema: {
|
|
19
|
+
type: "object" as const,
|
|
20
|
+
properties: {},
|
|
21
|
+
required: [],
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "update_agent_prompt",
|
|
26
|
+
description:
|
|
27
|
+
"Update an agent's system prompt. Use this when the owner wants to change how an agent behaves.",
|
|
28
|
+
input_schema: {
|
|
29
|
+
type: "object" as const,
|
|
30
|
+
properties: {
|
|
31
|
+
agent_id: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "The agent ID to update",
|
|
34
|
+
},
|
|
35
|
+
system_prompt: {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "The new system prompt for the agent",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: ["agent_id", "system_prompt"],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "update_site_content",
|
|
45
|
+
description:
|
|
46
|
+
"Update the website content. Can update tagline, subtitle, features, FAQ, or CTA. Only include fields that need to change.",
|
|
47
|
+
input_schema: {
|
|
48
|
+
type: "object" as const,
|
|
49
|
+
properties: {
|
|
50
|
+
tagline: {
|
|
51
|
+
type: "string",
|
|
52
|
+
description: "New tagline for the website",
|
|
53
|
+
},
|
|
54
|
+
subtitle: {
|
|
55
|
+
type: "string",
|
|
56
|
+
description: "New subtitle for the website",
|
|
57
|
+
},
|
|
58
|
+
cta: {
|
|
59
|
+
type: "string",
|
|
60
|
+
description: "New call-to-action text",
|
|
61
|
+
},
|
|
62
|
+
features: {
|
|
63
|
+
type: "array",
|
|
64
|
+
description: "New features list",
|
|
65
|
+
items: {
|
|
66
|
+
type: "object",
|
|
67
|
+
properties: {
|
|
68
|
+
title: { type: "string" },
|
|
69
|
+
description: { type: "string" },
|
|
70
|
+
icon: { type: "string" },
|
|
71
|
+
},
|
|
72
|
+
required: ["title", "description", "icon"],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
faq: {
|
|
76
|
+
type: "array",
|
|
77
|
+
description: "New FAQ list",
|
|
78
|
+
items: {
|
|
79
|
+
type: "object",
|
|
80
|
+
properties: {
|
|
81
|
+
question: { type: "string" },
|
|
82
|
+
answer: { type: "string" },
|
|
83
|
+
},
|
|
84
|
+
required: ["question", "answer"],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
required: [],
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "get_recent_conversations",
|
|
93
|
+
description:
|
|
94
|
+
"Get recent conversations with message counts and agent info.",
|
|
95
|
+
input_schema: {
|
|
96
|
+
type: "object" as const,
|
|
97
|
+
properties: {
|
|
98
|
+
limit: {
|
|
99
|
+
type: "number",
|
|
100
|
+
description: "Number of conversations to return (default 10)",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
required: [],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "add_knowledge",
|
|
108
|
+
description:
|
|
109
|
+
"Add a document to the company knowledge base. Agents use this to answer customer questions accurately.",
|
|
110
|
+
input_schema: {
|
|
111
|
+
type: "object" as const,
|
|
112
|
+
properties: {
|
|
113
|
+
title: {
|
|
114
|
+
type: "string",
|
|
115
|
+
description: "Document title (e.g. 'Refund Policy')",
|
|
116
|
+
},
|
|
117
|
+
content: {
|
|
118
|
+
type: "string",
|
|
119
|
+
description: "Full document content",
|
|
120
|
+
},
|
|
121
|
+
category: {
|
|
122
|
+
type: "string",
|
|
123
|
+
description: "Category: policy, product, faq, or general",
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
required: ["title", "content"],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "search_knowledge",
|
|
131
|
+
description:
|
|
132
|
+
"Search the knowledge base to check what information agents have access to.",
|
|
133
|
+
input_schema: {
|
|
134
|
+
type: "object" as const,
|
|
135
|
+
properties: {
|
|
136
|
+
query: {
|
|
137
|
+
type: "string",
|
|
138
|
+
description: "Search query",
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
required: ["query"],
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: "list_knowledge",
|
|
146
|
+
description:
|
|
147
|
+
"List all documents in the knowledge base.",
|
|
148
|
+
input_schema: {
|
|
149
|
+
type: "object" as const,
|
|
150
|
+
properties: {},
|
|
151
|
+
required: [],
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: "delete_knowledge",
|
|
156
|
+
description:
|
|
157
|
+
"Delete a document from the knowledge base by ID.",
|
|
158
|
+
input_schema: {
|
|
159
|
+
type: "object" as const,
|
|
160
|
+
properties: {
|
|
161
|
+
document_id: {
|
|
162
|
+
type: "string",
|
|
163
|
+
description: "The document ID to delete",
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
required: ["document_id"],
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: "run_agent_task",
|
|
171
|
+
description:
|
|
172
|
+
"Run a specific agent with a task immediately. Use this when the owner asks to have an agent do something right now.",
|
|
173
|
+
input_schema: {
|
|
174
|
+
type: "object" as const,
|
|
175
|
+
properties: {
|
|
176
|
+
agent_id: {
|
|
177
|
+
type: "string",
|
|
178
|
+
description: "The agent ID to run",
|
|
179
|
+
},
|
|
180
|
+
task: {
|
|
181
|
+
type: "string",
|
|
182
|
+
description: "The task description for the agent",
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
required: ["agent_id", "task"],
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: "manage_schedule",
|
|
190
|
+
description:
|
|
191
|
+
"Enable or disable scheduled tasks for an agent. Updates tellet.json.",
|
|
192
|
+
input_schema: {
|
|
193
|
+
type: "object" as const,
|
|
194
|
+
properties: {
|
|
195
|
+
agent_id: {
|
|
196
|
+
type: "string",
|
|
197
|
+
description: "The agent ID",
|
|
198
|
+
},
|
|
199
|
+
enabled: {
|
|
200
|
+
type: "boolean",
|
|
201
|
+
description: "Enable or disable the schedule",
|
|
202
|
+
},
|
|
203
|
+
cron: {
|
|
204
|
+
type: "string",
|
|
205
|
+
description: "Cron expression (e.g. '0 9 * * *' for daily at 9am)",
|
|
206
|
+
},
|
|
207
|
+
task: {
|
|
208
|
+
type: "string",
|
|
209
|
+
description: "The recurring task description",
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
required: ["agent_id", "enabled"],
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: "list_available_tools",
|
|
217
|
+
description:
|
|
218
|
+
"List all tools available in the tellet marketplace that can be installed.",
|
|
219
|
+
input_schema: {
|
|
220
|
+
type: "object" as const,
|
|
221
|
+
properties: {},
|
|
222
|
+
required: [],
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: "install_tool",
|
|
227
|
+
description:
|
|
228
|
+
"Install a tool from the marketplace and assign it to agents. Updates tellet.json. The owner will need to set the required API key in .env.",
|
|
229
|
+
input_schema: {
|
|
230
|
+
type: "object" as const,
|
|
231
|
+
properties: {
|
|
232
|
+
tool_id: {
|
|
233
|
+
type: "string",
|
|
234
|
+
description: "Tool ID from the marketplace (e.g. 'stripe', 'email', 'github')",
|
|
235
|
+
},
|
|
236
|
+
agent_ids: {
|
|
237
|
+
type: "array",
|
|
238
|
+
items: { type: "string" },
|
|
239
|
+
description: "Agent IDs to assign this tool to",
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
required: ["tool_id"],
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import type { LLMProvider, StreamParams, StreamChunk } from "./index";
|
|
3
|
+
|
|
4
|
+
let _client: Anthropic | null = null;
|
|
5
|
+
function getClient() {
|
|
6
|
+
if (!_client) _client = new Anthropic();
|
|
7
|
+
return _client;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const anthropicProvider: LLMProvider = {
|
|
11
|
+
id: "anthropic",
|
|
12
|
+
|
|
13
|
+
async stream(params: StreamParams): Promise<ReadableStream<StreamChunk>> {
|
|
14
|
+
const stream = getClient().messages.stream({
|
|
15
|
+
model: params.model,
|
|
16
|
+
max_tokens: params.maxTokens || 2048,
|
|
17
|
+
system: params.system,
|
|
18
|
+
messages: params.messages,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return new ReadableStream({
|
|
22
|
+
async start(controller) {
|
|
23
|
+
stream.on("text", (text) => {
|
|
24
|
+
controller.enqueue({ text });
|
|
25
|
+
});
|
|
26
|
+
await stream.finalMessage();
|
|
27
|
+
controller.close();
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
estimateCost(inputTokens: number, outputTokens: number, model: string): number {
|
|
33
|
+
const rates: Record<string, { input: number; output: number }> = {
|
|
34
|
+
"claude-sonnet-4-6": { input: 3, output: 15 },
|
|
35
|
+
"claude-haiku-4-5": { input: 0.25, output: 1.25 },
|
|
36
|
+
"claude-opus-4-6": { input: 15, output: 75 },
|
|
37
|
+
};
|
|
38
|
+
const r = rates[model] || rates["claude-sonnet-4-6"];
|
|
39
|
+
return (inputTokens * r.input + outputTokens * r.output) / 1_000_000;
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface LLMMessage {
|
|
2
|
+
role: "user" | "assistant";
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface StreamParams {
|
|
7
|
+
model: string;
|
|
8
|
+
system: string;
|
|
9
|
+
messages: LLMMessage[];
|
|
10
|
+
maxTokens?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface StreamChunk {
|
|
14
|
+
text: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface LLMProvider {
|
|
18
|
+
id: string;
|
|
19
|
+
stream(params: StreamParams): Promise<ReadableStream<StreamChunk>>;
|
|
20
|
+
estimateCost(inputTokens: number, outputTokens: number, model: string): number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
import { anthropicProvider } from "./anthropic";
|
|
24
|
+
import { openaiProvider } from "./openai";
|
|
25
|
+
|
|
26
|
+
const providers: Record<string, LLMProvider> = {
|
|
27
|
+
anthropic: anthropicProvider,
|
|
28
|
+
openai: openaiProvider,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function getProvider(id?: string): LLMProvider {
|
|
32
|
+
const providerId = id || "anthropic";
|
|
33
|
+
const provider = providers[providerId];
|
|
34
|
+
if (!provider) throw new Error(`Unknown LLM provider: ${providerId}`);
|
|
35
|
+
return provider;
|
|
36
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
import type { LLMProvider, StreamParams, StreamChunk } from "./index";
|
|
3
|
+
|
|
4
|
+
let _client: OpenAI | null = null;
|
|
5
|
+
function getClient() {
|
|
6
|
+
if (!_client) _client = new OpenAI();
|
|
7
|
+
return _client;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const openaiProvider: LLMProvider = {
|
|
11
|
+
id: "openai",
|
|
12
|
+
|
|
13
|
+
async stream(params: StreamParams): Promise<ReadableStream<StreamChunk>> {
|
|
14
|
+
const stream = await getClient().chat.completions.create({
|
|
15
|
+
model: params.model,
|
|
16
|
+
max_tokens: params.maxTokens || 2048,
|
|
17
|
+
messages: [
|
|
18
|
+
{ role: "system", content: params.system },
|
|
19
|
+
...params.messages,
|
|
20
|
+
],
|
|
21
|
+
stream: true,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return new ReadableStream({
|
|
25
|
+
async start(controller) {
|
|
26
|
+
for await (const chunk of stream) {
|
|
27
|
+
const text = chunk.choices[0]?.delta?.content;
|
|
28
|
+
if (text) {
|
|
29
|
+
controller.enqueue({ text });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
controller.close();
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
estimateCost(inputTokens: number, outputTokens: number, model: string): number {
|
|
38
|
+
const rates: Record<string, { input: number; output: number }> = {
|
|
39
|
+
"gpt-4.1": { input: 2, output: 8 },
|
|
40
|
+
"gpt-4.1-mini": { input: 0.4, output: 1.6 },
|
|
41
|
+
"gpt-4.1-nano": { input: 0.1, output: 0.4 },
|
|
42
|
+
};
|
|
43
|
+
const r = rates[model] || rates["gpt-4.1"];
|
|
44
|
+
return (inputTokens * r.input + outputTokens * r.output) / 1_000_000;
|
|
45
|
+
},
|
|
46
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { getConfig } from "./tellet";
|
|
2
|
+
import { streamAgentWithTools } from "./engine";
|
|
3
|
+
import { searchKnowledge } from "./mcp/knowledge";
|
|
4
|
+
import { createServerSupabase } from "./supabase";
|
|
5
|
+
import type Anthropic from "@anthropic-ai/sdk";
|
|
6
|
+
|
|
7
|
+
interface ScheduleResult {
|
|
8
|
+
agentId: string;
|
|
9
|
+
agentName: string;
|
|
10
|
+
task: string;
|
|
11
|
+
response: string;
|
|
12
|
+
timestamp: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const builtinTools = [
|
|
16
|
+
{
|
|
17
|
+
name: "search_knowledge",
|
|
18
|
+
description: "Search the company knowledge base",
|
|
19
|
+
input_schema: {
|
|
20
|
+
type: "object" as const,
|
|
21
|
+
properties: {
|
|
22
|
+
query: { type: "string", description: "Search query" },
|
|
23
|
+
},
|
|
24
|
+
required: ["query"],
|
|
25
|
+
} as Anthropic.Tool.InputSchema,
|
|
26
|
+
execute: async (input: Record<string, unknown>) => {
|
|
27
|
+
return searchKnowledge(input.query as string);
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
export async function runScheduledAgent(
|
|
33
|
+
agentId: string,
|
|
34
|
+
task?: string
|
|
35
|
+
): Promise<ScheduleResult> {
|
|
36
|
+
const config = getConfig();
|
|
37
|
+
const supabase = await createServerSupabase();
|
|
38
|
+
|
|
39
|
+
const { data: agent } = await supabase
|
|
40
|
+
.from("agents")
|
|
41
|
+
.select("*")
|
|
42
|
+
.eq("id", agentId)
|
|
43
|
+
.single();
|
|
44
|
+
|
|
45
|
+
if (!agent) throw new Error(`Agent "${agentId}" not found`);
|
|
46
|
+
|
|
47
|
+
const agentConfig = config.agents.find((a) => a.id === agentId);
|
|
48
|
+
const prompt =
|
|
49
|
+
task ||
|
|
50
|
+
(agentConfig as { schedule?: { task?: string } })?.schedule?.task ||
|
|
51
|
+
`Perform your regular scheduled duties as ${agent.role}.`;
|
|
52
|
+
|
|
53
|
+
const stream = await streamAgentWithTools({
|
|
54
|
+
agent: {
|
|
55
|
+
id: agent.id,
|
|
56
|
+
name: agent.name,
|
|
57
|
+
role: agent.role,
|
|
58
|
+
model: agent.model,
|
|
59
|
+
provider: agent.config?.provider as string | undefined,
|
|
60
|
+
systemPrompt: agent.system_prompt,
|
|
61
|
+
channels: [],
|
|
62
|
+
tools: agent.config?.tools || [],
|
|
63
|
+
},
|
|
64
|
+
messages: [{ role: "user", content: prompt }],
|
|
65
|
+
builtinTools,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
let response = "";
|
|
69
|
+
const reader = stream.getReader();
|
|
70
|
+
while (true) {
|
|
71
|
+
const { done, value } = await reader.read();
|
|
72
|
+
if (done) break;
|
|
73
|
+
response += value.text;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Log activity
|
|
77
|
+
await supabase.from("activity_log").insert({
|
|
78
|
+
agent_id: agentId,
|
|
79
|
+
action: "scheduled_task",
|
|
80
|
+
summary: `[Scheduled] ${response.slice(0, 120)}${response.length > 120 ? "..." : ""}`,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
agentId,
|
|
85
|
+
agentName: agent.name,
|
|
86
|
+
task: prompt,
|
|
87
|
+
response,
|
|
88
|
+
timestamp: new Date().toISOString(),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function runAllScheduledAgents(): Promise<ScheduleResult[]> {
|
|
93
|
+
const config = getConfig();
|
|
94
|
+
const results: ScheduleResult[] = [];
|
|
95
|
+
|
|
96
|
+
for (const agent of config.agents) {
|
|
97
|
+
const schedule = (agent as { schedule?: { enabled?: boolean } }).schedule;
|
|
98
|
+
if (!schedule?.enabled) continue;
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const result = await runScheduledAgent(agent.id);
|
|
102
|
+
results.push(result);
|
|
103
|
+
} catch (err) {
|
|
104
|
+
results.push({
|
|
105
|
+
agentId: agent.id,
|
|
106
|
+
agentName: agent.name,
|
|
107
|
+
task: "scheduled",
|
|
108
|
+
response: `Error: ${err instanceof Error ? err.message : "Failed"}`,
|
|
109
|
+
timestamp: new Date().toISOString(),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return results;
|
|
115
|
+
}
|