agent-dj 0.1.1 → 0.1.3
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 +36 -7
- package/dist/cli.js +184 -8
- package/dist/server.js +145 -6
- package/package.json +1 -1
- package/web/dist/assets/index-BprVp71W.js +348 -0
- package/web/dist/assets/index-tOqJ_oLn.css +1 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-D2I1F5Wj.js +0 -348
- package/web/dist/assets/index-MHuCb1sM.css +0 -1
package/README.md
CHANGED
|
@@ -1,23 +1,49 @@
|
|
|
1
1
|
# Agent DJ
|
|
2
2
|
|
|
3
|
-
Self-hosted AI agent platform
|
|
3
|
+
**Self-hosted AI agent platform. One command, beautiful dashboard, 8 providers, multi-agent orchestration.**
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx agent-dj@latest
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Bring your own API key. Supports Claude, GPT, Gemini, Grok, Mistral, DeepSeek, Groq, and Ollama (local/free). No cloud accounts, no sign-ups — runs entirely on your machine. Apache-2.0.
|
|
10
|
+
|
|
11
|
+
**What you get:**
|
|
12
|
+
|
|
13
|
+
- Beautiful web dashboard at localhost
|
|
14
|
+
- 8 LLM providers — Claude, GPT-4o, Gemini, Grok, Mistral, DeepSeek, Groq, Ollama
|
|
15
|
+
- Multi-agent system — orchestrator auto-routes to specialist agents (Coder, Researcher, Writer, System, RAG)
|
|
16
|
+
- Knowledge base with RAG — upload documents, agents search them automatically
|
|
17
|
+
- MCP support — connect external tools (GitHub, Slack, databases, etc.)
|
|
18
|
+
- Skills — create custom instructions that shape how all agents behave
|
|
19
|
+
- Memory — agents remember context across conversations
|
|
20
|
+
- Full audit log of every action
|
|
21
|
+
- Permission system — control what agents can do
|
|
22
|
+
|
|
23
|
+
**BYOK (Bring Your Own Key).** Your keys, your data, your machine. Nothing leaves localhost unless you tell it to.
|
|
24
|
+
|
|
25
|
+
---
|
|
4
26
|
|
|
5
27
|
## Quick Start
|
|
6
28
|
|
|
7
29
|
```bash
|
|
8
|
-
|
|
9
|
-
npm install -g agent-dj
|
|
10
|
-
|
|
11
|
-
# Or run directly with npx
|
|
12
|
-
npx agent-dj
|
|
30
|
+
npx agent-dj@latest
|
|
13
31
|
```
|
|
14
32
|
|
|
15
|
-
On first run, the setup wizard walks you through choosing a provider and entering your API key. Then the dashboard opens automatically
|
|
33
|
+
On first run, the setup wizard walks you through choosing a provider and entering your API key. Then the dashboard opens automatically at **http://localhost:3456**.
|
|
34
|
+
|
|
35
|
+
You can also install globally:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install -g agent-dj
|
|
39
|
+
agent-dj
|
|
40
|
+
```
|
|
16
41
|
|
|
17
42
|
## Features
|
|
18
43
|
|
|
19
44
|
- Multi-agent orchestration (Coder, Researcher, Writer, System, RAG)
|
|
20
45
|
- Chat interface with conversation history
|
|
46
|
+
- Skills system (custom instructions for all agents, works across all providers)
|
|
21
47
|
- Memory system (user context, feedback, projects)
|
|
22
48
|
- Knowledge base with RAG (document upload, hybrid search)
|
|
23
49
|
- MCP (Model Context Protocol) server support
|
|
@@ -32,6 +58,9 @@ On first run, the setup wizard walks you through choosing a provider and enterin
|
|
|
32
58
|
| **Anthropic** | API key or OAuth token | Claude Opus, Sonnet, Haiku |
|
|
33
59
|
| **OpenAI** | API key | GPT-4o, o1, o3-mini |
|
|
34
60
|
| **Google** | API key | Gemini 2.0 Flash/Pro |
|
|
61
|
+
| **xAI** | API key | Grok-3 |
|
|
62
|
+
| **Mistral** | API key | Mistral Large |
|
|
63
|
+
| **DeepSeek** | API key | DeepSeek Chat |
|
|
35
64
|
| **Groq** | API key | Llama 3.3 70B (fast, free tier) |
|
|
36
65
|
| **Ollama** | None | Local models, free |
|
|
37
66
|
|
package/dist/cli.js
CHANGED
|
@@ -124,6 +124,18 @@ var init_defaults = __esm({
|
|
|
124
124
|
ollama: {
|
|
125
125
|
default: "llama3.2",
|
|
126
126
|
options: ["llama3.2", "mistral", "codellama", "phi3"]
|
|
127
|
+
},
|
|
128
|
+
deepseek: {
|
|
129
|
+
default: "deepseek-chat",
|
|
130
|
+
options: ["deepseek-chat", "deepseek-reasoner"]
|
|
131
|
+
},
|
|
132
|
+
xai: {
|
|
133
|
+
default: "grok-3",
|
|
134
|
+
options: ["grok-3", "grok-3-mini", "grok-2", "grok-2-mini"]
|
|
135
|
+
},
|
|
136
|
+
mistral: {
|
|
137
|
+
default: "mistral-large-latest",
|
|
138
|
+
options: ["mistral-large-latest", "codestral-latest", "mistral-small-latest"]
|
|
127
139
|
}
|
|
128
140
|
};
|
|
129
141
|
}
|
|
@@ -519,14 +531,20 @@ var init_setup = __esm({
|
|
|
519
531
|
openai: "OpenAI (GPT)",
|
|
520
532
|
google: "Google (Gemini)",
|
|
521
533
|
groq: "Groq (fast inference)",
|
|
522
|
-
ollama: "Ollama (local, free)"
|
|
534
|
+
ollama: "Ollama (local, free)",
|
|
535
|
+
deepseek: "DeepSeek (cheap & strong)",
|
|
536
|
+
xai: "xAI (Grok)",
|
|
537
|
+
mistral: "Mistral AI"
|
|
523
538
|
};
|
|
524
539
|
API_KEY_HINTS = {
|
|
525
540
|
anthropic: "Get key at console.anthropic.com",
|
|
526
541
|
openai: "Get key at platform.openai.com",
|
|
527
542
|
google: "Get key at aistudio.google.com",
|
|
528
543
|
groq: "Get key at console.groq.com (free tier available)",
|
|
529
|
-
ollama: "No key needed \u2014 runs locally"
|
|
544
|
+
ollama: "No key needed \u2014 runs locally",
|
|
545
|
+
deepseek: "Get key at platform.deepseek.com",
|
|
546
|
+
xai: "Get key at console.x.ai",
|
|
547
|
+
mistral: "Get key at console.mistral.ai"
|
|
530
548
|
};
|
|
531
549
|
}
|
|
532
550
|
});
|
|
@@ -652,6 +670,17 @@ CREATE TABLE IF NOT EXISTS permission_cache (
|
|
|
652
670
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
|
|
653
671
|
);
|
|
654
672
|
CREATE INDEX IF NOT EXISTS idx_perm_session ON permission_cache(session_id, resource_type, resource_path);
|
|
673
|
+
|
|
674
|
+
-- Skills
|
|
675
|
+
CREATE TABLE IF NOT EXISTS skills (
|
|
676
|
+
id TEXT PRIMARY KEY,
|
|
677
|
+
name TEXT NOT NULL,
|
|
678
|
+
description TEXT NOT NULL DEFAULT '',
|
|
679
|
+
content TEXT NOT NULL,
|
|
680
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
681
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
|
|
682
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
|
|
683
|
+
);
|
|
655
684
|
`;
|
|
656
685
|
}
|
|
657
686
|
});
|
|
@@ -762,6 +791,37 @@ function getAuditLog(limit = 100, offset = 0) {
|
|
|
762
791
|
function clearAuditLog() {
|
|
763
792
|
getDb().prepare("DELETE FROM audit_log").run();
|
|
764
793
|
}
|
|
794
|
+
function createSkill(params) {
|
|
795
|
+
const id = randomUUID();
|
|
796
|
+
getDb().prepare("INSERT INTO skills (id, name, description, content) VALUES (?, ?, ?, ?)").run(id, params.name, params.description, params.content);
|
|
797
|
+
return id;
|
|
798
|
+
}
|
|
799
|
+
function listSkills() {
|
|
800
|
+
return getDb().prepare("SELECT * FROM skills ORDER BY created_at DESC").all();
|
|
801
|
+
}
|
|
802
|
+
function getSkill(id) {
|
|
803
|
+
return getDb().prepare("SELECT * FROM skills WHERE id = ?").get(id);
|
|
804
|
+
}
|
|
805
|
+
function updateSkill(id, updates) {
|
|
806
|
+
const sets = [];
|
|
807
|
+
const values2 = [];
|
|
808
|
+
for (const [key, val] of Object.entries(updates)) {
|
|
809
|
+
if (val !== void 0) {
|
|
810
|
+
sets.push(`${key} = ?`);
|
|
811
|
+
values2.push(val);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
if (sets.length === 0) return;
|
|
815
|
+
sets.push("updated_at = datetime('now')");
|
|
816
|
+
values2.push(id);
|
|
817
|
+
getDb().prepare(`UPDATE skills SET ${sets.join(", ")} WHERE id = ?`).run(...values2);
|
|
818
|
+
}
|
|
819
|
+
function deleteSkill(id) {
|
|
820
|
+
getDb().prepare("DELETE FROM skills WHERE id = ?").run(id);
|
|
821
|
+
}
|
|
822
|
+
function getEnabledSkills() {
|
|
823
|
+
return getDb().prepare("SELECT * FROM skills WHERE enabled = 1 ORDER BY created_at ASC").all();
|
|
824
|
+
}
|
|
765
825
|
var db;
|
|
766
826
|
var init_db = __esm({
|
|
767
827
|
"src/db/index.ts"() {
|
|
@@ -272727,6 +272787,24 @@ async function getOrCreateProvider(config) {
|
|
|
272727
272787
|
baseURL: config.baseUrl ?? "http://localhost:11434/v1"
|
|
272728
272788
|
});
|
|
272729
272789
|
break;
|
|
272790
|
+
case "deepseek":
|
|
272791
|
+
instance = createOpenAI({
|
|
272792
|
+
apiKey: config.apiKey,
|
|
272793
|
+
baseURL: "https://api.deepseek.com/v1"
|
|
272794
|
+
});
|
|
272795
|
+
break;
|
|
272796
|
+
case "xai":
|
|
272797
|
+
instance = createOpenAI({
|
|
272798
|
+
apiKey: config.apiKey,
|
|
272799
|
+
baseURL: "https://api.x.ai/v1"
|
|
272800
|
+
});
|
|
272801
|
+
break;
|
|
272802
|
+
case "mistral":
|
|
272803
|
+
instance = createOpenAI({
|
|
272804
|
+
apiKey: config.apiKey,
|
|
272805
|
+
baseURL: "https://api.mistral.ai/v1"
|
|
272806
|
+
});
|
|
272807
|
+
break;
|
|
272730
272808
|
default:
|
|
272731
272809
|
throw new Error(`Unsupported provider: ${config.provider}`);
|
|
272732
272810
|
}
|
|
@@ -272734,7 +272812,7 @@ async function getOrCreateProvider(config) {
|
|
|
272734
272812
|
return instance;
|
|
272735
272813
|
}
|
|
272736
272814
|
function getLanguageModel(config, modelId) {
|
|
272737
|
-
const model = modelId
|
|
272815
|
+
const model = modelId || config.model || getDefaultModel(config.provider);
|
|
272738
272816
|
if (config.provider === "anthropic" && config.authType === "session_token") {
|
|
272739
272817
|
_lastSessionTokenSupportsTools = false;
|
|
272740
272818
|
if (isClaudeCliAvailable()) {
|
|
@@ -272770,7 +272848,10 @@ function getDefaultModel(provider) {
|
|
|
272770
272848
|
openai: "gpt-4o",
|
|
272771
272849
|
google: "gemini-2.0-flash",
|
|
272772
272850
|
groq: "llama-3.3-70b-versatile",
|
|
272773
|
-
ollama: "llama3.2"
|
|
272851
|
+
ollama: "llama3.2",
|
|
272852
|
+
deepseek: "deepseek-chat",
|
|
272853
|
+
xai: "grok-3",
|
|
272854
|
+
mistral: "mistral-large-latest"
|
|
272774
272855
|
};
|
|
272775
272856
|
return defaults[provider];
|
|
272776
272857
|
}
|
|
@@ -273275,7 +273356,19 @@ var init_models = __esm({
|
|
|
273275
273356
|
{ id: "llama-3.1-8b-instant", name: "Llama 3.1 8B", provider: "groq", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: false },
|
|
273276
273357
|
// Ollama
|
|
273277
273358
|
{ id: "llama3.2", name: "Llama 3.2", provider: "ollama", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: false },
|
|
273278
|
-
{ id: "mistral", name: "Mistral", provider: "ollama", contextWindow: 32e3, costTier: "cheap", supportsTools: true, supportsVision: false }
|
|
273359
|
+
{ id: "mistral", name: "Mistral", provider: "ollama", contextWindow: 32e3, costTier: "cheap", supportsTools: true, supportsVision: false },
|
|
273360
|
+
// DeepSeek
|
|
273361
|
+
{ id: "deepseek-chat", name: "DeepSeek Chat", provider: "deepseek", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: true },
|
|
273362
|
+
{ id: "deepseek-reasoner", name: "DeepSeek Reasoner", provider: "deepseek", contextWindow: 128e3, costTier: "standard", supportsTools: false, supportsVision: false },
|
|
273363
|
+
// xAI
|
|
273364
|
+
{ id: "grok-3", name: "Grok 3", provider: "xai", contextWindow: 131072, costTier: "premium", supportsTools: true, supportsVision: true },
|
|
273365
|
+
{ id: "grok-3-mini", name: "Grok 3 Mini", provider: "xai", contextWindow: 131072, costTier: "standard", supportsTools: true, supportsVision: true },
|
|
273366
|
+
{ id: "grok-2", name: "Grok 2", provider: "xai", contextWindow: 131072, costTier: "standard", supportsTools: true, supportsVision: true },
|
|
273367
|
+
{ id: "grok-2-mini", name: "Grok 2 Mini", provider: "xai", contextWindow: 131072, costTier: "cheap", supportsTools: true, supportsVision: false },
|
|
273368
|
+
// Mistral
|
|
273369
|
+
{ id: "mistral-large-latest", name: "Mistral Large", provider: "mistral", contextWindow: 128e3, costTier: "standard", supportsTools: true, supportsVision: false },
|
|
273370
|
+
{ id: "codestral-latest", name: "Codestral", provider: "mistral", contextWindow: 256e3, costTier: "cheap", supportsTools: true, supportsVision: false },
|
|
273371
|
+
{ id: "mistral-small-latest", name: "Mistral Small", provider: "mistral", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: false }
|
|
273279
273372
|
];
|
|
273280
273373
|
}
|
|
273281
273374
|
});
|
|
@@ -273357,8 +273450,21 @@ function routeMessage(message) {
|
|
|
273357
273450
|
}
|
|
273358
273451
|
return "orchestrator";
|
|
273359
273452
|
}
|
|
273453
|
+
function buildSkillsSection() {
|
|
273454
|
+
const skills = getEnabledSkills();
|
|
273455
|
+
if (skills.length === 0) return "";
|
|
273456
|
+
const skillsText = skills.map((s) => `### ${s.name}
|
|
273457
|
+
${s.content}`).join("\n\n");
|
|
273458
|
+
return `
|
|
273459
|
+
|
|
273460
|
+
## Custom Skills & Instructions
|
|
273461
|
+
Follow these additional instructions:
|
|
273462
|
+
|
|
273463
|
+
${skillsText}`;
|
|
273464
|
+
}
|
|
273360
273465
|
function buildSystemPrompt() {
|
|
273361
273466
|
let prompt = BASE_SYSTEM_PROMPT;
|
|
273467
|
+
prompt += buildSkillsSection();
|
|
273362
273468
|
const mcpSummary = getMcpToolSummary();
|
|
273363
273469
|
if (mcpSummary) {
|
|
273364
273470
|
prompt += `
|
|
@@ -273587,6 +273693,7 @@ async function delegateToAgent(targetAgent, messages, lastMessage, model, provid
|
|
|
273587
273693
|
const memoryCtx = memory.buildContext(lastMessage);
|
|
273588
273694
|
const knowledgeCtx = buildKnowledgeContext(lastMessage);
|
|
273589
273695
|
let augmentedSystem = agentDef.systemPrompt;
|
|
273696
|
+
augmentedSystem += buildSkillsSection();
|
|
273590
273697
|
const mcpSummary = getMcpToolSummary();
|
|
273591
273698
|
if (mcpSummary) augmentedSystem += `
|
|
273592
273699
|
|
|
@@ -273677,6 +273784,7 @@ async function delegateToAgentStreaming(targetAgent, messages, lastMessage, mode
|
|
|
273677
273784
|
const memoryCtx = memory.buildContext(lastMessage);
|
|
273678
273785
|
const knowledgeCtx = buildKnowledgeContext(lastMessage);
|
|
273679
273786
|
let augmentedSystem = agentDef.systemPrompt;
|
|
273787
|
+
augmentedSystem += buildSkillsSection();
|
|
273680
273788
|
const mcpSummary = getMcpToolSummary();
|
|
273681
273789
|
if (mcpSummary) augmentedSystem += `
|
|
273682
273790
|
|
|
@@ -274389,6 +274497,18 @@ var init_settings = __esm({
|
|
|
274389
274497
|
saveConfig(updated);
|
|
274390
274498
|
return c.json({ ok: true });
|
|
274391
274499
|
});
|
|
274500
|
+
settingsRoutes.delete("/providers/:name", (c) => {
|
|
274501
|
+
const name = c.req.param("name");
|
|
274502
|
+
const config = loadConfig();
|
|
274503
|
+
const idx = config.providers.findIndex((p2) => p2.provider === name);
|
|
274504
|
+
if (idx < 0) return c.json({ error: "Provider not found" }, 404);
|
|
274505
|
+
if (config.defaultProvider === name) {
|
|
274506
|
+
return c.json({ error: "Cannot delete the default provider. Set another as default first." }, 400);
|
|
274507
|
+
}
|
|
274508
|
+
config.providers.splice(idx, 1);
|
|
274509
|
+
saveConfig(config);
|
|
274510
|
+
return c.json({ ok: true });
|
|
274511
|
+
});
|
|
274392
274512
|
settingsRoutes.get("/system", (c) => {
|
|
274393
274513
|
const stats = getSystemStats();
|
|
274394
274514
|
return c.json(stats);
|
|
@@ -274661,12 +274781,66 @@ var init_apps = __esm({
|
|
|
274661
274781
|
}
|
|
274662
274782
|
});
|
|
274663
274783
|
|
|
274784
|
+
// src/server/routes/skills.ts
|
|
274785
|
+
import { Hono as Hono9 } from "hono";
|
|
274786
|
+
var skillsRoutes;
|
|
274787
|
+
var init_skills = __esm({
|
|
274788
|
+
"src/server/routes/skills.ts"() {
|
|
274789
|
+
"use strict";
|
|
274790
|
+
init_db();
|
|
274791
|
+
skillsRoutes = new Hono9();
|
|
274792
|
+
skillsRoutes.get("/", (c) => {
|
|
274793
|
+
return c.json(listSkills());
|
|
274794
|
+
});
|
|
274795
|
+
skillsRoutes.get("/:id", (c) => {
|
|
274796
|
+
const skill = getSkill(c.req.param("id"));
|
|
274797
|
+
if (!skill) return c.json({ error: "Skill not found" }, 404);
|
|
274798
|
+
return c.json(skill);
|
|
274799
|
+
});
|
|
274800
|
+
skillsRoutes.post("/", async (c) => {
|
|
274801
|
+
const body = await c.req.json();
|
|
274802
|
+
const { name, description, content } = body;
|
|
274803
|
+
if (!name || !content) {
|
|
274804
|
+
return c.json({ error: "name and content are required" }, 400);
|
|
274805
|
+
}
|
|
274806
|
+
const id = createSkill({ name, description: description || "", content });
|
|
274807
|
+
return c.json({ id }, 201);
|
|
274808
|
+
});
|
|
274809
|
+
skillsRoutes.put("/:id", async (c) => {
|
|
274810
|
+
const id = c.req.param("id");
|
|
274811
|
+
const skill = getSkill(id);
|
|
274812
|
+
if (!skill) return c.json({ error: "Skill not found" }, 404);
|
|
274813
|
+
const body = await c.req.json();
|
|
274814
|
+
const updates = {};
|
|
274815
|
+
if (body.name !== void 0) updates.name = body.name;
|
|
274816
|
+
if (body.description !== void 0) updates.description = body.description;
|
|
274817
|
+
if (body.content !== void 0) updates.content = body.content;
|
|
274818
|
+
if (body.enabled !== void 0) updates.enabled = body.enabled;
|
|
274819
|
+
updateSkill(id, updates);
|
|
274820
|
+
return c.json({ ok: true });
|
|
274821
|
+
});
|
|
274822
|
+
skillsRoutes.delete("/:id", (c) => {
|
|
274823
|
+
const id = c.req.param("id");
|
|
274824
|
+
deleteSkill(id);
|
|
274825
|
+
return c.json({ ok: true });
|
|
274826
|
+
});
|
|
274827
|
+
skillsRoutes.patch("/:id/toggle", (c) => {
|
|
274828
|
+
const id = c.req.param("id");
|
|
274829
|
+
const skill = getSkill(id);
|
|
274830
|
+
if (!skill) return c.json({ error: "Skill not found" }, 404);
|
|
274831
|
+
updateSkill(id, { enabled: skill.enabled ? 0 : 1 });
|
|
274832
|
+
const updated = getSkill(id);
|
|
274833
|
+
return c.json(updated);
|
|
274834
|
+
});
|
|
274835
|
+
}
|
|
274836
|
+
});
|
|
274837
|
+
|
|
274664
274838
|
// src/server/index.ts
|
|
274665
274839
|
var server_exports = {};
|
|
274666
274840
|
__export(server_exports, {
|
|
274667
274841
|
createApp: () => createApp
|
|
274668
274842
|
});
|
|
274669
|
-
import { Hono as
|
|
274843
|
+
import { Hono as Hono10 } from "hono";
|
|
274670
274844
|
import { cors } from "hono/cors";
|
|
274671
274845
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
274672
274846
|
import { fileURLToPath } from "url";
|
|
@@ -274687,9 +274861,9 @@ function findWebDist() {
|
|
|
274687
274861
|
return candidates[0];
|
|
274688
274862
|
}
|
|
274689
274863
|
function createApp() {
|
|
274690
|
-
const app = new
|
|
274864
|
+
const app = new Hono10();
|
|
274691
274865
|
app.use("*", cors());
|
|
274692
|
-
const SILENT_ROUTES = ["/api/tasks", "/api/settings", "/api/conversations", "/api/health", "/api/memory", "/api/mcp", "/api/agents", "/api/knowledge", "/api/apps"];
|
|
274866
|
+
const SILENT_ROUTES = ["/api/tasks", "/api/settings", "/api/conversations", "/api/health", "/api/memory", "/api/mcp", "/api/agents", "/api/knowledge", "/api/apps", "/api/skills"];
|
|
274693
274867
|
app.use("/api/*", async (c, next) => {
|
|
274694
274868
|
const path = c.req.path;
|
|
274695
274869
|
const method = c.req.method;
|
|
@@ -274709,6 +274883,7 @@ function createApp() {
|
|
|
274709
274883
|
app.route("/api/settings", settingsRoutes);
|
|
274710
274884
|
app.route("/api/memory", memoryRoutes);
|
|
274711
274885
|
app.route("/api/apps", appsRoutes);
|
|
274886
|
+
app.route("/api/skills", skillsRoutes);
|
|
274712
274887
|
app.get("/api/health", (c) => {
|
|
274713
274888
|
return c.json({
|
|
274714
274889
|
status: "ok",
|
|
@@ -274732,6 +274907,7 @@ var init_server = __esm({
|
|
|
274732
274907
|
init_settings();
|
|
274733
274908
|
init_memory2();
|
|
274734
274909
|
init_apps();
|
|
274910
|
+
init_skills();
|
|
274735
274911
|
__filename = fileURLToPath(import.meta.url);
|
|
274736
274912
|
__dirname = dirname(__filename);
|
|
274737
274913
|
WEB_DIST = findWebDist();
|
package/dist/server.js
CHANGED
|
@@ -270567,7 +270567,7 @@ var require_lib6 = __commonJS({
|
|
|
270567
270567
|
});
|
|
270568
270568
|
|
|
270569
270569
|
// src/server/index.ts
|
|
270570
|
-
import { Hono as
|
|
270570
|
+
import { Hono as Hono10 } from "hono";
|
|
270571
270571
|
import { cors } from "hono/cors";
|
|
270572
270572
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
270573
270573
|
import { fileURLToPath } from "url";
|
|
@@ -270729,6 +270729,37 @@ function getAuditLog(limit = 100, offset = 0) {
|
|
|
270729
270729
|
function clearAuditLog() {
|
|
270730
270730
|
getDb().prepare("DELETE FROM audit_log").run();
|
|
270731
270731
|
}
|
|
270732
|
+
function createSkill(params) {
|
|
270733
|
+
const id = randomUUID();
|
|
270734
|
+
getDb().prepare("INSERT INTO skills (id, name, description, content) VALUES (?, ?, ?, ?)").run(id, params.name, params.description, params.content);
|
|
270735
|
+
return id;
|
|
270736
|
+
}
|
|
270737
|
+
function listSkills() {
|
|
270738
|
+
return getDb().prepare("SELECT * FROM skills ORDER BY created_at DESC").all();
|
|
270739
|
+
}
|
|
270740
|
+
function getSkill(id) {
|
|
270741
|
+
return getDb().prepare("SELECT * FROM skills WHERE id = ?").get(id);
|
|
270742
|
+
}
|
|
270743
|
+
function updateSkill(id, updates) {
|
|
270744
|
+
const sets = [];
|
|
270745
|
+
const values2 = [];
|
|
270746
|
+
for (const [key, val] of Object.entries(updates)) {
|
|
270747
|
+
if (val !== void 0) {
|
|
270748
|
+
sets.push(`${key} = ?`);
|
|
270749
|
+
values2.push(val);
|
|
270750
|
+
}
|
|
270751
|
+
}
|
|
270752
|
+
if (sets.length === 0) return;
|
|
270753
|
+
sets.push("updated_at = datetime('now')");
|
|
270754
|
+
values2.push(id);
|
|
270755
|
+
getDb().prepare(`UPDATE skills SET ${sets.join(", ")} WHERE id = ?`).run(...values2);
|
|
270756
|
+
}
|
|
270757
|
+
function deleteSkill(id) {
|
|
270758
|
+
getDb().prepare("DELETE FROM skills WHERE id = ?").run(id);
|
|
270759
|
+
}
|
|
270760
|
+
function getEnabledSkills() {
|
|
270761
|
+
return getDb().prepare("SELECT * FROM skills WHERE enabled = 1 ORDER BY created_at ASC").all();
|
|
270762
|
+
}
|
|
270732
270763
|
|
|
270733
270764
|
// src/config/index.ts
|
|
270734
270765
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, cpSync } from "fs";
|
|
@@ -270980,6 +271011,24 @@ async function getOrCreateProvider(config) {
|
|
|
270980
271011
|
baseURL: config.baseUrl ?? "http://localhost:11434/v1"
|
|
270981
271012
|
});
|
|
270982
271013
|
break;
|
|
271014
|
+
case "deepseek":
|
|
271015
|
+
instance = createOpenAI({
|
|
271016
|
+
apiKey: config.apiKey,
|
|
271017
|
+
baseURL: "https://api.deepseek.com/v1"
|
|
271018
|
+
});
|
|
271019
|
+
break;
|
|
271020
|
+
case "xai":
|
|
271021
|
+
instance = createOpenAI({
|
|
271022
|
+
apiKey: config.apiKey,
|
|
271023
|
+
baseURL: "https://api.x.ai/v1"
|
|
271024
|
+
});
|
|
271025
|
+
break;
|
|
271026
|
+
case "mistral":
|
|
271027
|
+
instance = createOpenAI({
|
|
271028
|
+
apiKey: config.apiKey,
|
|
271029
|
+
baseURL: "https://api.mistral.ai/v1"
|
|
271030
|
+
});
|
|
271031
|
+
break;
|
|
270983
271032
|
default:
|
|
270984
271033
|
throw new Error(`Unsupported provider: ${config.provider}`);
|
|
270985
271034
|
}
|
|
@@ -270987,7 +271036,7 @@ async function getOrCreateProvider(config) {
|
|
|
270987
271036
|
return instance;
|
|
270988
271037
|
}
|
|
270989
271038
|
function getLanguageModel(config, modelId) {
|
|
270990
|
-
const model = modelId
|
|
271039
|
+
const model = modelId || config.model || getDefaultModel(config.provider);
|
|
270991
271040
|
if (config.provider === "anthropic" && config.authType === "session_token") {
|
|
270992
271041
|
_lastSessionTokenSupportsTools = false;
|
|
270993
271042
|
if (isClaudeCliAvailable()) {
|
|
@@ -271023,7 +271072,10 @@ function getDefaultModel(provider) {
|
|
|
271023
271072
|
openai: "gpt-4o",
|
|
271024
271073
|
google: "gemini-2.0-flash",
|
|
271025
271074
|
groq: "llama-3.3-70b-versatile",
|
|
271026
|
-
ollama: "llama3.2"
|
|
271075
|
+
ollama: "llama3.2",
|
|
271076
|
+
deepseek: "deepseek-chat",
|
|
271077
|
+
xai: "grok-3",
|
|
271078
|
+
mistral: "mistral-large-latest"
|
|
271027
271079
|
};
|
|
271028
271080
|
return defaults[provider];
|
|
271029
271081
|
}
|
|
@@ -272494,7 +272546,19 @@ var MODEL_CATALOG = [
|
|
|
272494
272546
|
{ id: "llama-3.1-8b-instant", name: "Llama 3.1 8B", provider: "groq", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: false },
|
|
272495
272547
|
// Ollama
|
|
272496
272548
|
{ id: "llama3.2", name: "Llama 3.2", provider: "ollama", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: false },
|
|
272497
|
-
{ id: "mistral", name: "Mistral", provider: "ollama", contextWindow: 32e3, costTier: "cheap", supportsTools: true, supportsVision: false }
|
|
272549
|
+
{ id: "mistral", name: "Mistral", provider: "ollama", contextWindow: 32e3, costTier: "cheap", supportsTools: true, supportsVision: false },
|
|
272550
|
+
// DeepSeek
|
|
272551
|
+
{ id: "deepseek-chat", name: "DeepSeek Chat", provider: "deepseek", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: true },
|
|
272552
|
+
{ id: "deepseek-reasoner", name: "DeepSeek Reasoner", provider: "deepseek", contextWindow: 128e3, costTier: "standard", supportsTools: false, supportsVision: false },
|
|
272553
|
+
// xAI
|
|
272554
|
+
{ id: "grok-3", name: "Grok 3", provider: "xai", contextWindow: 131072, costTier: "premium", supportsTools: true, supportsVision: true },
|
|
272555
|
+
{ id: "grok-3-mini", name: "Grok 3 Mini", provider: "xai", contextWindow: 131072, costTier: "standard", supportsTools: true, supportsVision: true },
|
|
272556
|
+
{ id: "grok-2", name: "Grok 2", provider: "xai", contextWindow: 131072, costTier: "standard", supportsTools: true, supportsVision: true },
|
|
272557
|
+
{ id: "grok-2-mini", name: "Grok 2 Mini", provider: "xai", contextWindow: 131072, costTier: "cheap", supportsTools: true, supportsVision: false },
|
|
272558
|
+
// Mistral
|
|
272559
|
+
{ id: "mistral-large-latest", name: "Mistral Large", provider: "mistral", contextWindow: 128e3, costTier: "standard", supportsTools: true, supportsVision: false },
|
|
272560
|
+
{ id: "codestral-latest", name: "Codestral", provider: "mistral", contextWindow: 256e3, costTier: "cheap", supportsTools: true, supportsVision: false },
|
|
272561
|
+
{ id: "mistral-small-latest", name: "Mistral Small", provider: "mistral", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: false }
|
|
272498
272562
|
];
|
|
272499
272563
|
function getModelInfo(modelId) {
|
|
272500
272564
|
return MODEL_CATALOG.find((m) => m.id === modelId);
|
|
@@ -272628,8 +272692,21 @@ You have direct access to tools including web search, file operations, knowledge
|
|
|
272628
272692
|
- When listing options, briefly note pros/cons
|
|
272629
272693
|
- End with a clear next step or action item when relevant
|
|
272630
272694
|
- ALWAYS try to use your tools to answer questions \u2014 don't just give generic advice`;
|
|
272695
|
+
function buildSkillsSection() {
|
|
272696
|
+
const skills = getEnabledSkills();
|
|
272697
|
+
if (skills.length === 0) return "";
|
|
272698
|
+
const skillsText = skills.map((s) => `### ${s.name}
|
|
272699
|
+
${s.content}`).join("\n\n");
|
|
272700
|
+
return `
|
|
272701
|
+
|
|
272702
|
+
## Custom Skills & Instructions
|
|
272703
|
+
Follow these additional instructions:
|
|
272704
|
+
|
|
272705
|
+
${skillsText}`;
|
|
272706
|
+
}
|
|
272631
272707
|
function buildSystemPrompt() {
|
|
272632
272708
|
let prompt = BASE_SYSTEM_PROMPT;
|
|
272709
|
+
prompt += buildSkillsSection();
|
|
272633
272710
|
const mcpSummary = getMcpToolSummary();
|
|
272634
272711
|
if (mcpSummary) {
|
|
272635
272712
|
prompt += `
|
|
@@ -272866,6 +272943,7 @@ async function delegateToAgent(targetAgent, messages, lastMessage, model, provid
|
|
|
272866
272943
|
const memoryCtx = memory.buildContext(lastMessage);
|
|
272867
272944
|
const knowledgeCtx = buildKnowledgeContext(lastMessage);
|
|
272868
272945
|
let augmentedSystem = agentDef.systemPrompt;
|
|
272946
|
+
augmentedSystem += buildSkillsSection();
|
|
272869
272947
|
const mcpSummary = getMcpToolSummary();
|
|
272870
272948
|
if (mcpSummary) augmentedSystem += `
|
|
272871
272949
|
|
|
@@ -272956,6 +273034,7 @@ async function delegateToAgentStreaming(targetAgent, messages, lastMessage, mode
|
|
|
272956
273034
|
const memoryCtx = memory.buildContext(lastMessage);
|
|
272957
273035
|
const knowledgeCtx = buildKnowledgeContext(lastMessage);
|
|
272958
273036
|
let augmentedSystem = agentDef.systemPrompt;
|
|
273037
|
+
augmentedSystem += buildSkillsSection();
|
|
272959
273038
|
const mcpSummary = getMcpToolSummary();
|
|
272960
273039
|
if (mcpSummary) augmentedSystem += `
|
|
272961
273040
|
|
|
@@ -273669,6 +273748,18 @@ settingsRoutes.post("/providers", async (c) => {
|
|
|
273669
273748
|
saveConfig(updated);
|
|
273670
273749
|
return c.json({ ok: true });
|
|
273671
273750
|
});
|
|
273751
|
+
settingsRoutes.delete("/providers/:name", (c) => {
|
|
273752
|
+
const name = c.req.param("name");
|
|
273753
|
+
const config = loadConfig();
|
|
273754
|
+
const idx = config.providers.findIndex((p) => p.provider === name);
|
|
273755
|
+
if (idx < 0) return c.json({ error: "Provider not found" }, 404);
|
|
273756
|
+
if (config.defaultProvider === name) {
|
|
273757
|
+
return c.json({ error: "Cannot delete the default provider. Set another as default first." }, 400);
|
|
273758
|
+
}
|
|
273759
|
+
config.providers.splice(idx, 1);
|
|
273760
|
+
saveConfig(config);
|
|
273761
|
+
return c.json({ ok: true });
|
|
273762
|
+
});
|
|
273672
273763
|
settingsRoutes.get("/system", (c) => {
|
|
273673
273764
|
const stats = getSystemStats();
|
|
273674
273765
|
return c.json(stats);
|
|
@@ -273921,6 +274012,53 @@ appsRoutes.post("/refresh", async (c) => {
|
|
|
273921
274012
|
return c.json({ status: "ok", count: cachedCatalog.apps.length });
|
|
273922
274013
|
});
|
|
273923
274014
|
|
|
274015
|
+
// src/server/routes/skills.ts
|
|
274016
|
+
import { Hono as Hono9 } from "hono";
|
|
274017
|
+
var skillsRoutes = new Hono9();
|
|
274018
|
+
skillsRoutes.get("/", (c) => {
|
|
274019
|
+
return c.json(listSkills());
|
|
274020
|
+
});
|
|
274021
|
+
skillsRoutes.get("/:id", (c) => {
|
|
274022
|
+
const skill = getSkill(c.req.param("id"));
|
|
274023
|
+
if (!skill) return c.json({ error: "Skill not found" }, 404);
|
|
274024
|
+
return c.json(skill);
|
|
274025
|
+
});
|
|
274026
|
+
skillsRoutes.post("/", async (c) => {
|
|
274027
|
+
const body = await c.req.json();
|
|
274028
|
+
const { name, description, content } = body;
|
|
274029
|
+
if (!name || !content) {
|
|
274030
|
+
return c.json({ error: "name and content are required" }, 400);
|
|
274031
|
+
}
|
|
274032
|
+
const id = createSkill({ name, description: description || "", content });
|
|
274033
|
+
return c.json({ id }, 201);
|
|
274034
|
+
});
|
|
274035
|
+
skillsRoutes.put("/:id", async (c) => {
|
|
274036
|
+
const id = c.req.param("id");
|
|
274037
|
+
const skill = getSkill(id);
|
|
274038
|
+
if (!skill) return c.json({ error: "Skill not found" }, 404);
|
|
274039
|
+
const body = await c.req.json();
|
|
274040
|
+
const updates = {};
|
|
274041
|
+
if (body.name !== void 0) updates.name = body.name;
|
|
274042
|
+
if (body.description !== void 0) updates.description = body.description;
|
|
274043
|
+
if (body.content !== void 0) updates.content = body.content;
|
|
274044
|
+
if (body.enabled !== void 0) updates.enabled = body.enabled;
|
|
274045
|
+
updateSkill(id, updates);
|
|
274046
|
+
return c.json({ ok: true });
|
|
274047
|
+
});
|
|
274048
|
+
skillsRoutes.delete("/:id", (c) => {
|
|
274049
|
+
const id = c.req.param("id");
|
|
274050
|
+
deleteSkill(id);
|
|
274051
|
+
return c.json({ ok: true });
|
|
274052
|
+
});
|
|
274053
|
+
skillsRoutes.patch("/:id/toggle", (c) => {
|
|
274054
|
+
const id = c.req.param("id");
|
|
274055
|
+
const skill = getSkill(id);
|
|
274056
|
+
if (!skill) return c.json({ error: "Skill not found" }, 404);
|
|
274057
|
+
updateSkill(id, { enabled: skill.enabled ? 0 : 1 });
|
|
274058
|
+
const updated = getSkill(id);
|
|
274059
|
+
return c.json(updated);
|
|
274060
|
+
});
|
|
274061
|
+
|
|
273924
274062
|
// src/server/index.ts
|
|
273925
274063
|
var __filename = fileURLToPath(import.meta.url);
|
|
273926
274064
|
var __dirname = dirname(__filename);
|
|
@@ -273940,9 +274078,9 @@ function findWebDist() {
|
|
|
273940
274078
|
}
|
|
273941
274079
|
var WEB_DIST = findWebDist();
|
|
273942
274080
|
function createApp() {
|
|
273943
|
-
const app = new
|
|
274081
|
+
const app = new Hono10();
|
|
273944
274082
|
app.use("*", cors());
|
|
273945
|
-
const SILENT_ROUTES = ["/api/tasks", "/api/settings", "/api/conversations", "/api/health", "/api/memory", "/api/mcp", "/api/agents", "/api/knowledge", "/api/apps"];
|
|
274083
|
+
const SILENT_ROUTES = ["/api/tasks", "/api/settings", "/api/conversations", "/api/health", "/api/memory", "/api/mcp", "/api/agents", "/api/knowledge", "/api/apps", "/api/skills"];
|
|
273946
274084
|
app.use("/api/*", async (c, next) => {
|
|
273947
274085
|
const path = c.req.path;
|
|
273948
274086
|
const method = c.req.method;
|
|
@@ -273962,6 +274100,7 @@ function createApp() {
|
|
|
273962
274100
|
app.route("/api/settings", settingsRoutes);
|
|
273963
274101
|
app.route("/api/memory", memoryRoutes);
|
|
273964
274102
|
app.route("/api/apps", appsRoutes);
|
|
274103
|
+
app.route("/api/skills", skillsRoutes);
|
|
273965
274104
|
app.get("/api/health", (c) => {
|
|
273966
274105
|
return c.json({
|
|
273967
274106
|
status: "ok",
|
package/package.json
CHANGED