openbot 0.2.11 → 0.2.13
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/.prettierrc +8 -0
- package/AGENTS.md +68 -0
- package/CONTRIBUTING.md +74 -0
- package/LICENSE +21 -0
- package/README.md +117 -14
- package/dist/agents/system.js +106 -0
- package/dist/app/cli.js +27 -0
- package/dist/app/config.js +64 -0
- package/dist/app/server.js +237 -0
- package/dist/app/utils.js +35 -0
- package/dist/harness/agent-harness.js +45 -0
- package/dist/harness/mcp.js +61 -0
- package/dist/harness/orchestrator.js +273 -0
- package/dist/harness/process.js +7 -0
- package/dist/plugins/ai-sdk.js +141 -0
- package/dist/plugins/delegation.js +52 -0
- package/dist/plugins/mcp.js +140 -0
- package/dist/plugins/storage.js +502 -0
- package/dist/plugins/ui.js +47 -0
- package/dist/registry/plugins.js +73 -0
- package/dist/services/storage.js +724 -0
- package/docs/README.md +7 -0
- package/docs/agents.md +83 -0
- package/docs/architecture.md +34 -0
- package/docs/plugins.md +77 -0
- package/logo-black.png +0 -0
- package/{dist/assets/logo.js → logo-black.svg} +24 -24
- package/{dist/ui/sidebar.js → logo-white.svg} +23 -88
- package/package.json +10 -9
- package/src/agents/system.ts +112 -0
- package/src/app/cli.ts +38 -0
- package/src/app/config.ts +104 -0
- package/src/app/server.ts +284 -0
- package/src/app/types.ts +476 -0
- package/src/app/utils.ts +43 -0
- package/src/assets/icon.svg +1 -0
- package/src/harness/agent-harness.ts +58 -0
- package/src/harness/mcp.ts +78 -0
- package/src/harness/orchestrator.ts +342 -0
- package/src/harness/process.ts +9 -0
- package/src/harness/types.ts +34 -0
- package/src/plugins/ai-sdk.ts +197 -0
- package/src/plugins/delegation.ts +60 -0
- package/src/plugins/mcp.ts +154 -0
- package/src/plugins/storage.ts +725 -0
- package/src/plugins/ui.ts +57 -0
- package/src/registry/plugins.ts +85 -0
- package/src/services/storage.ts +957 -0
- package/tsconfig.json +18 -0
- package/dist/agents/agent-creator.js +0 -74
- package/dist/agents/browser-agent.js +0 -31
- package/dist/agents/os-agent.js +0 -32
- package/dist/agents/planner-agent.js +0 -32
- package/dist/agents/topic-agent.js +0 -46
- package/dist/architecture/execution-engine.js +0 -151
- package/dist/architecture/intent-classifier.js +0 -26
- package/dist/architecture/planner.js +0 -106
- package/dist/automation-worker.js +0 -121
- package/dist/automations.js +0 -52
- package/dist/cli.js +0 -275
- package/dist/config.js +0 -53
- package/dist/core/agents.js +0 -41
- package/dist/core/delegation.js +0 -230
- package/dist/core/manager.js +0 -96
- package/dist/core/plugins.js +0 -74
- package/dist/core/router.js +0 -191
- package/dist/handlers/init.js +0 -29
- package/dist/handlers/session-change.js +0 -21
- package/dist/handlers/settings.js +0 -47
- package/dist/handlers/tab-change.js +0 -14
- package/dist/installers.js +0 -156
- package/dist/marketplace.js +0 -80
- package/dist/model-catalog.js +0 -132
- package/dist/model-defaults.js +0 -25
- package/dist/models.js +0 -47
- package/dist/open-bot.js +0 -51
- package/dist/orchestrator/direct-invocation.js +0 -13
- package/dist/orchestrator/events.js +0 -36
- package/dist/orchestrator/state.js +0 -54
- package/dist/orchestrator.js +0 -422
- package/dist/plugins/agent/index.js +0 -81
- package/dist/plugins/approval/index.js +0 -100
- package/dist/plugins/brain/identity.js +0 -77
- package/dist/plugins/brain/index.js +0 -204
- package/dist/plugins/brain/memory.js +0 -120
- package/dist/plugins/brain/prompt.js +0 -46
- package/dist/plugins/brain/types.js +0 -45
- package/dist/plugins/brain/ui.js +0 -7
- package/dist/plugins/browser/index.js +0 -629
- package/dist/plugins/browser/ui.js +0 -13
- package/dist/plugins/file-system/index.js +0 -171
- package/dist/plugins/file-system/ui.js +0 -6
- package/dist/plugins/llm/context-budget.js +0 -139
- package/dist/plugins/llm/context-shaping.js +0 -177
- package/dist/plugins/llm/index.js +0 -380
- package/dist/plugins/memory/index.js +0 -220
- package/dist/plugins/memory/memory.js +0 -122
- package/dist/plugins/memory/prompt.js +0 -55
- package/dist/plugins/memory/types.js +0 -45
- package/dist/plugins/meta-agent/index.js +0 -570
- package/dist/plugins/meta-agent/ui.js +0 -11
- package/dist/plugins/shell/index.js +0 -100
- package/dist/plugins/shell/ui.js +0 -6
- package/dist/plugins/skills/index.js +0 -286
- package/dist/plugins/skills/types.js +0 -50
- package/dist/plugins/skills/ui.js +0 -12
- package/dist/registry/agent-registry.js +0 -35
- package/dist/registry/index.js +0 -2
- package/dist/registry/plugin-loader.js +0 -499
- package/dist/registry/plugin-registry.js +0 -44
- package/dist/registry/ts-agent-loader.js +0 -82
- package/dist/registry/yaml-agent-loader.js +0 -246
- package/dist/runtime/execution-trace.js +0 -41
- package/dist/runtime/intent-routing.js +0 -26
- package/dist/runtime/openbot-runtime.js +0 -354
- package/dist/server.js +0 -890
- package/dist/session.js +0 -179
- package/dist/ui/block.js +0 -12
- package/dist/ui/header.js +0 -52
- package/dist/ui/layout.js +0 -26
- package/dist/ui/navigation.js +0 -15
- package/dist/ui/settings.js +0 -106
- package/dist/ui/skills.js +0 -7
- package/dist/ui/thread.js +0 -16
- package/dist/ui/widgets/action-list.js +0 -2
- package/dist/ui/widgets/approval-card.js +0 -9
- package/dist/ui/widgets/code-snippet.js +0 -2
- package/dist/ui/widgets/data-block.js +0 -2
- package/dist/ui/widgets/data-table.js +0 -2
- package/dist/ui/widgets/delegation.js +0 -29
- package/dist/ui/widgets/empty-state.js +0 -2
- package/dist/ui/widgets/index.js +0 -23
- package/dist/ui/widgets/inquiry.js +0 -7
- package/dist/ui/widgets/key-value.js +0 -2
- package/dist/ui/widgets/progress-step.js +0 -2
- package/dist/ui/widgets/resource-card.js +0 -2
- package/dist/ui/widgets/status.js +0 -2
- package/dist/ui/widgets/todo-list.js +0 -2
- package/dist/version.js +0 -62
- /package/dist/{types.js → app/types.js} +0 -0
- /package/dist/{architecture/contracts.js → harness/types.js} +0 -0
|
@@ -1,570 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import * as fs from "node:fs/promises";
|
|
3
|
-
import * as path from "node:path";
|
|
4
|
-
import matter from "gray-matter";
|
|
5
|
-
// Tool definitions for the meta-agent capabilities
|
|
6
|
-
export const metaAgentToolDefinitions = {
|
|
7
|
-
loadSkill: {
|
|
8
|
-
description: "Load a skill's full instructions when you need to use it. Call this before executing a skill.",
|
|
9
|
-
inputSchema: z.object({
|
|
10
|
-
skillId: z.string().describe("The skill folder name (e.g., 'code-review')"),
|
|
11
|
-
}),
|
|
12
|
-
},
|
|
13
|
-
createSkill: {
|
|
14
|
-
description: "Create a new skill from learned knowledge. Use when you discover a reusable pattern.",
|
|
15
|
-
inputSchema: z.object({
|
|
16
|
-
id: z.string().describe("Skill folder name in kebab-case (e.g., 'web-search')"),
|
|
17
|
-
title: z.string().describe("Human-readable skill title"),
|
|
18
|
-
description: z.string().describe("Brief description of what the skill does"),
|
|
19
|
-
content: z.string().describe("Full skill instructions in markdown"),
|
|
20
|
-
}),
|
|
21
|
-
},
|
|
22
|
-
updateSkill: {
|
|
23
|
-
description: "Update an existing skill with new knowledge or improvements.",
|
|
24
|
-
inputSchema: z.object({
|
|
25
|
-
id: z.string().describe("The skill folder name (e.g., 'code-review')"),
|
|
26
|
-
title: z.string().optional().describe("New title for the skill"),
|
|
27
|
-
description: z.string().optional().describe("New description for the skill"),
|
|
28
|
-
content: z.string().describe("Updated full skill instructions in markdown"),
|
|
29
|
-
}),
|
|
30
|
-
},
|
|
31
|
-
listSkills: {
|
|
32
|
-
description: "List all available skills with their metadata",
|
|
33
|
-
inputSchema: z.object({}),
|
|
34
|
-
},
|
|
35
|
-
updateIdentity: {
|
|
36
|
-
description: "Update your identity file to refine your personality and traits",
|
|
37
|
-
inputSchema: z.object({
|
|
38
|
-
content: z.string().describe("New content for IDENTITY.md"),
|
|
39
|
-
}),
|
|
40
|
-
},
|
|
41
|
-
readIdentity: {
|
|
42
|
-
description: "Read your current identity or soul configuration",
|
|
43
|
-
inputSchema: z.object({
|
|
44
|
-
file: z.enum(["IDENTITY.md", "SOUL.md"]).describe("Which identity file to read"),
|
|
45
|
-
}),
|
|
46
|
-
},
|
|
47
|
-
appendToMemory: {
|
|
48
|
-
description: "Add a fact or note to your long-term memory",
|
|
49
|
-
inputSchema: z.object({
|
|
50
|
-
category: z.enum(["facts", "journal"]).describe("Memory category"),
|
|
51
|
-
content: z.string().describe("Content to append"),
|
|
52
|
-
}),
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
// Default content for identity files
|
|
56
|
-
const DEFAULT_SOUL = `# Soul
|
|
57
|
-
|
|
58
|
-
## Core Values
|
|
59
|
-
- Be helpful, honest, and harmless
|
|
60
|
-
- Respect user privacy and data
|
|
61
|
-
- Learn and improve continuously
|
|
62
|
-
- Be transparent about capabilities and limitations
|
|
63
|
-
|
|
64
|
-
## Ethical Guidelines
|
|
65
|
-
- Never assist with harmful or illegal activities
|
|
66
|
-
- Protect sensitive information
|
|
67
|
-
- Acknowledge uncertainty when unsure
|
|
68
|
-
- Prioritize user well-being
|
|
69
|
-
`;
|
|
70
|
-
const DEFAULT_IDENTITY = `# Identity
|
|
71
|
-
|
|
72
|
-
I am OpenBot, a self-evolving AI assistant built on the OpenBot framework.
|
|
73
|
-
|
|
74
|
-
## Personality
|
|
75
|
-
- Friendly and approachable
|
|
76
|
-
- Technically competent
|
|
77
|
-
- Eager to learn and adapt
|
|
78
|
-
|
|
79
|
-
## Capabilities
|
|
80
|
-
- Shell command execution
|
|
81
|
-
- File system operations
|
|
82
|
-
- Skill-based task execution
|
|
83
|
-
- Self-modification and learning
|
|
84
|
-
`;
|
|
85
|
-
/**
|
|
86
|
-
* Expand ~ to home directory
|
|
87
|
-
*/
|
|
88
|
-
function expandPath(p) {
|
|
89
|
-
if (p.startsWith("~/")) {
|
|
90
|
-
return path.join(process.env.HOME || "", p.slice(2));
|
|
91
|
-
}
|
|
92
|
-
return p;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Truncate a markdown file by keeping the header and the last N entries (separated by ##)
|
|
96
|
-
*/
|
|
97
|
-
function truncateToRecent(content, limit = 10) {
|
|
98
|
-
const parts = content.split(/\n(?=## )/);
|
|
99
|
-
if (parts.length <= limit + 1)
|
|
100
|
-
return content.trim();
|
|
101
|
-
const header = parts[0].trim();
|
|
102
|
-
const recent = parts.slice(-limit).map((p) => p.trim());
|
|
103
|
-
return `${header}\n\n*... (older entries truncated) ...*\n\n${recent.join("\n\n")}`;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Build the dynamic system prompt from identity files and skills
|
|
107
|
-
*/
|
|
108
|
-
export async function buildSystemPrompt(baseDir, context) {
|
|
109
|
-
const expandedBase = expandPath(baseDir);
|
|
110
|
-
const parts = [];
|
|
111
|
-
const state = context?.state;
|
|
112
|
-
const currentCwd = state?.cwd || process.cwd();
|
|
113
|
-
// Add environment context
|
|
114
|
-
const now = new Date();
|
|
115
|
-
parts.push(`## Environment
|
|
116
|
-
You are running as a global system agent.
|
|
117
|
-
- **Current Time**: ${now.toLocaleString()} (${Intl.DateTimeFormat().resolvedOptions().timeZone})
|
|
118
|
-
- **Current Working Directory (CWD)**: ${currentCwd}
|
|
119
|
-
- **System Access**: You have access to the entire file system (root: /).
|
|
120
|
-
- **Bot Home (Internal State)**: ${expandedBase}
|
|
121
|
-
|
|
122
|
-
### Path Rules:
|
|
123
|
-
1. **Shell Commands**: All commands (executeCommand) run in the CWD: ${currentCwd}.
|
|
124
|
-
2. **File Operations**: Relative paths in readFile, writeFile, listFiles, etc. resolve against the CWD.
|
|
125
|
-
3. **Changing Directory**: Use \`cd <path>\` in executeCommand to move. Your CWD is persisted across turns.
|
|
126
|
-
4. **Skills/Memory**: To access your own skills and memory, use absolute paths starting with "${expandedBase}/".
|
|
127
|
-
|
|
128
|
-
When you want to execute skill scripts, always use the full path to the skill directory. For example, if the skill is at "${expandedBase}/skills/my-skill", the full path to the script is "${expandedBase}/skills/my-skill/scripts/script.sh".`);
|
|
129
|
-
// Load SOUL.md
|
|
130
|
-
try {
|
|
131
|
-
const soul = await fs.readFile(path.join(expandedBase, "SOUL.md"), "utf-8");
|
|
132
|
-
parts.push(soul.trim());
|
|
133
|
-
}
|
|
134
|
-
catch {
|
|
135
|
-
// File doesn't exist yet
|
|
136
|
-
}
|
|
137
|
-
// Load IDENTITY.md
|
|
138
|
-
try {
|
|
139
|
-
const identity = await fs.readFile(path.join(expandedBase, "IDENTITY.md"), "utf-8");
|
|
140
|
-
parts.push(identity.trim());
|
|
141
|
-
}
|
|
142
|
-
catch {
|
|
143
|
-
// File doesn't exist yet
|
|
144
|
-
}
|
|
145
|
-
// Load Memory (Facts and Journal)
|
|
146
|
-
const memoryParts = [];
|
|
147
|
-
try {
|
|
148
|
-
const facts = await fs.readFile(path.join(expandedBase, "memory/facts.md"), "utf-8");
|
|
149
|
-
if (facts.trim().length > 0) {
|
|
150
|
-
memoryParts.push(`### Facts\n${truncateToRecent(facts, 10)}`);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
catch { }
|
|
154
|
-
try {
|
|
155
|
-
const journal = await fs.readFile(path.join(expandedBase, "memory/journal.md"), "utf-8");
|
|
156
|
-
if (journal.trim().length > 0) {
|
|
157
|
-
memoryParts.push(`### Journal\n${truncateToRecent(journal, 10)}`);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
catch { }
|
|
161
|
-
if (memoryParts.length > 0) {
|
|
162
|
-
parts.push(`## Memory\n\nRefer to these facts and notes to provide personalized and context-aware responses.\n\n${memoryParts.join("\n\n")}`);
|
|
163
|
-
}
|
|
164
|
-
// Scan skills and build index
|
|
165
|
-
const skillsDir = path.join(expandedBase, "skills");
|
|
166
|
-
const skillsIndex = [];
|
|
167
|
-
try {
|
|
168
|
-
const skillFolders = await fs.readdir(skillsDir);
|
|
169
|
-
for (const folder of skillFolders) {
|
|
170
|
-
if (folder.startsWith("_") || folder.startsWith("."))
|
|
171
|
-
continue;
|
|
172
|
-
const skillPath = path.join(skillsDir, folder, "SKILL.md");
|
|
173
|
-
try {
|
|
174
|
-
const content = await fs.readFile(skillPath, "utf-8");
|
|
175
|
-
const { data } = matter(content);
|
|
176
|
-
skillsIndex.push({
|
|
177
|
-
id: folder,
|
|
178
|
-
title: data.title || folder,
|
|
179
|
-
description: data.description || "No description",
|
|
180
|
-
version: data.version,
|
|
181
|
-
tools: data.tools,
|
|
182
|
-
triggers: data.triggers,
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
catch {
|
|
186
|
-
// Skill doesn't have a valid SKILL.md
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
catch {
|
|
191
|
-
// Skills directory doesn't exist yet
|
|
192
|
-
}
|
|
193
|
-
// Add skills section
|
|
194
|
-
if (skillsIndex.length > 0) {
|
|
195
|
-
parts.push(`## Available Skills
|
|
196
|
-
|
|
197
|
-
You have the following skills available. Use \`loadSkill\` with the skill id to get full instructions before executing.
|
|
198
|
-
|
|
199
|
-
${skillsIndex.map((s) => `- **${s.title}** (\`${s.id}\`): ${s.description}`).join("\n")}`);
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
parts.push(`## Skills
|
|
203
|
-
|
|
204
|
-
You have no skills yet. When you learn reusable patterns, create skills using \`createSkill\` so you can use them again later.`);
|
|
205
|
-
}
|
|
206
|
-
// Add self-modification instructions
|
|
207
|
-
parts.push(`## Self-Modification
|
|
208
|
-
|
|
209
|
-
You can evolve and improve yourself:
|
|
210
|
-
- Use \`updateIdentity\` to refine your personality in IDENTITY.md
|
|
211
|
-
- Use \`createSkill\` to save reusable patterns as skills
|
|
212
|
-
- Use \`updateSkill\` to improve or expand existing skills with new knowledge
|
|
213
|
-
- Use \`appendToMemory\` to remember important facts or journal your learnings
|
|
214
|
-
- SOUL.md contains your core values and is protected from modification`);
|
|
215
|
-
return parts.join("\n\n");
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* Meta-Agent Plugin for Melony
|
|
219
|
-
* Provides self-modification, skill management, and identity persistence
|
|
220
|
-
*/
|
|
221
|
-
export const metaAgentPlugin = (options) => (builder) => {
|
|
222
|
-
const { baseDir, allowSoulModification = false } = options;
|
|
223
|
-
const expandedBase = expandPath(baseDir);
|
|
224
|
-
const resolvePath = (p) => path.join(expandedBase, p);
|
|
225
|
-
// Ensure directory structure exists
|
|
226
|
-
const ensureStructure = async () => {
|
|
227
|
-
// Ensure base directory exists with restricted permissions (0o700)
|
|
228
|
-
await fs.mkdir(expandedBase, { recursive: true, mode: 0o700 });
|
|
229
|
-
await fs.mkdir(resolvePath("skills"), { recursive: true });
|
|
230
|
-
await fs.mkdir(resolvePath("memory"), { recursive: true });
|
|
231
|
-
// Create default files if they don't exist
|
|
232
|
-
const defaults = {
|
|
233
|
-
"SOUL.md": DEFAULT_SOUL,
|
|
234
|
-
"IDENTITY.md": DEFAULT_IDENTITY,
|
|
235
|
-
"memory/facts.md": "# Facts\n\nLearned facts about the user and environment:\n",
|
|
236
|
-
"memory/journal.md": "# Journal\n\nSession notes and learnings:\n",
|
|
237
|
-
};
|
|
238
|
-
for (const [file, content] of Object.entries(defaults)) {
|
|
239
|
-
const filePath = resolvePath(file);
|
|
240
|
-
try {
|
|
241
|
-
await fs.access(filePath);
|
|
242
|
-
}
|
|
243
|
-
catch {
|
|
244
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
245
|
-
await fs.writeFile(filePath, content, "utf-8");
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
// Initialize on the "init" event
|
|
250
|
-
builder.on("init", async function* (event, context) {
|
|
251
|
-
yield {
|
|
252
|
-
type: "meta:status",
|
|
253
|
-
data: { message: "Initializing meta-agent structure..." },
|
|
254
|
-
};
|
|
255
|
-
await ensureStructure();
|
|
256
|
-
const systemPrompt = await buildSystemPrompt(baseDir, context);
|
|
257
|
-
yield {
|
|
258
|
-
type: "meta:initialized",
|
|
259
|
-
data: {
|
|
260
|
-
baseDir: expandedBase,
|
|
261
|
-
systemPrompt,
|
|
262
|
-
},
|
|
263
|
-
};
|
|
264
|
-
yield {
|
|
265
|
-
type: "meta:status",
|
|
266
|
-
data: { message: "Meta-agent initialized", severity: "success" },
|
|
267
|
-
};
|
|
268
|
-
});
|
|
269
|
-
// Load a skill's full content
|
|
270
|
-
builder.on("action:loadSkill", async function* (event) {
|
|
271
|
-
const { skillId, toolCallId } = event.data;
|
|
272
|
-
const skillPath = resolvePath(`skills/${skillId}/SKILL.md`);
|
|
273
|
-
try {
|
|
274
|
-
const content = await fs.readFile(skillPath, "utf-8");
|
|
275
|
-
const { data, content: body } = matter(content);
|
|
276
|
-
yield {
|
|
277
|
-
type: "meta:skill-loaded",
|
|
278
|
-
data: { skillId, title: data.title || skillId, instructions: data.description || "No description" },
|
|
279
|
-
};
|
|
280
|
-
yield {
|
|
281
|
-
type: "action:result",
|
|
282
|
-
data: {
|
|
283
|
-
action: "loadSkill",
|
|
284
|
-
toolCallId,
|
|
285
|
-
result: {
|
|
286
|
-
id: skillId,
|
|
287
|
-
meta: data,
|
|
288
|
-
instructions: body.trim(),
|
|
289
|
-
},
|
|
290
|
-
},
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
catch (error) {
|
|
294
|
-
yield {
|
|
295
|
-
type: "action:result",
|
|
296
|
-
data: {
|
|
297
|
-
action: "loadSkill",
|
|
298
|
-
toolCallId,
|
|
299
|
-
result: { error: `Skill "${skillId}" not found` },
|
|
300
|
-
},
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
// List all available skills
|
|
305
|
-
builder.on("action:listSkills", async function* (event) {
|
|
306
|
-
const { toolCallId } = event.data;
|
|
307
|
-
const skillsDir = resolvePath("skills");
|
|
308
|
-
const skills = [];
|
|
309
|
-
try {
|
|
310
|
-
const folders = await fs.readdir(skillsDir);
|
|
311
|
-
for (const folder of folders) {
|
|
312
|
-
if (folder.startsWith("_") || folder.startsWith("."))
|
|
313
|
-
continue;
|
|
314
|
-
const skillPath = path.join(skillsDir, folder, "SKILL.md");
|
|
315
|
-
try {
|
|
316
|
-
const content = await fs.readFile(skillPath, "utf-8");
|
|
317
|
-
const { data } = matter(content);
|
|
318
|
-
skills.push({
|
|
319
|
-
id: folder,
|
|
320
|
-
title: data.title || folder,
|
|
321
|
-
description: data.description || "No description",
|
|
322
|
-
version: data.version,
|
|
323
|
-
tools: data.tools,
|
|
324
|
-
triggers: data.triggers,
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
catch {
|
|
328
|
-
// Invalid skill, skip
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
catch {
|
|
333
|
-
// Skills directory doesn't exist
|
|
334
|
-
}
|
|
335
|
-
yield {
|
|
336
|
-
type: "action:result",
|
|
337
|
-
data: {
|
|
338
|
-
action: "listSkills",
|
|
339
|
-
toolCallId,
|
|
340
|
-
result: { skills },
|
|
341
|
-
},
|
|
342
|
-
};
|
|
343
|
-
});
|
|
344
|
-
// Create a new skill
|
|
345
|
-
builder.on("action:createSkill", async function* (event) {
|
|
346
|
-
const { id, title, description, content, toolCallId } = event.data;
|
|
347
|
-
// Validate skill id (kebab-case)
|
|
348
|
-
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(id)) {
|
|
349
|
-
yield {
|
|
350
|
-
type: "action:result",
|
|
351
|
-
data: {
|
|
352
|
-
action: "createSkill",
|
|
353
|
-
toolCallId,
|
|
354
|
-
result: { error: "Skill id must be kebab-case (e.g., 'my-skill')" },
|
|
355
|
-
},
|
|
356
|
-
};
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
try {
|
|
360
|
-
const skillDir = resolvePath(`skills/${id}`);
|
|
361
|
-
await fs.mkdir(skillDir, { recursive: true });
|
|
362
|
-
// Build SKILL.md with frontmatter
|
|
363
|
-
const skillContent = `---
|
|
364
|
-
title: ${title}
|
|
365
|
-
description: ${description}
|
|
366
|
-
version: 1.0.0
|
|
367
|
-
createdAt: ${new Date().toISOString()}
|
|
368
|
-
---
|
|
369
|
-
|
|
370
|
-
${content}`;
|
|
371
|
-
await fs.writeFile(path.join(skillDir, "SKILL.md"), skillContent, "utf-8");
|
|
372
|
-
yield {
|
|
373
|
-
type: "meta:status",
|
|
374
|
-
data: { message: `Skill "${title}" created`, severity: "success" },
|
|
375
|
-
};
|
|
376
|
-
yield {
|
|
377
|
-
type: "action:result",
|
|
378
|
-
data: {
|
|
379
|
-
action: "createSkill",
|
|
380
|
-
toolCallId,
|
|
381
|
-
result: {
|
|
382
|
-
success: true,
|
|
383
|
-
path: `skills/${id}/SKILL.md`,
|
|
384
|
-
message: `Skill "${title}" created successfully`,
|
|
385
|
-
},
|
|
386
|
-
},
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
catch (error) {
|
|
390
|
-
yield {
|
|
391
|
-
type: "meta:status",
|
|
392
|
-
data: { message: `Failed to create skill: ${error.message}`, severity: "error" },
|
|
393
|
-
};
|
|
394
|
-
yield {
|
|
395
|
-
type: "action:result",
|
|
396
|
-
data: {
|
|
397
|
-
action: "createSkill",
|
|
398
|
-
toolCallId,
|
|
399
|
-
result: { error: error.message },
|
|
400
|
-
},
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
// Update an existing skill
|
|
405
|
-
builder.on("action:updateSkill", async function* (event) {
|
|
406
|
-
const { id, title, description, content, toolCallId } = event.data;
|
|
407
|
-
const skillDir = resolvePath(`skills/${id}`);
|
|
408
|
-
const skillPath = path.join(skillDir, "SKILL.md");
|
|
409
|
-
try {
|
|
410
|
-
// Check if skill exists
|
|
411
|
-
await fs.access(skillPath);
|
|
412
|
-
// Read existing skill to get metadata if not provided
|
|
413
|
-
const existingContent = await fs.readFile(skillPath, "utf-8");
|
|
414
|
-
const { data: existingData } = matter(existingContent);
|
|
415
|
-
const newTitle = title || existingData.title || id;
|
|
416
|
-
const newDescription = description || existingData.description || "No description";
|
|
417
|
-
// Basic versioning: bump patch version
|
|
418
|
-
let version = existingData.version || "1.0.0";
|
|
419
|
-
const parts = version.split(".");
|
|
420
|
-
if (parts.length === 3) {
|
|
421
|
-
parts[2] = (parseInt(parts[2]) + 1).toString();
|
|
422
|
-
version = parts.join(".");
|
|
423
|
-
}
|
|
424
|
-
// Build updated SKILL.md
|
|
425
|
-
const skillContent = `---
|
|
426
|
-
title: ${newTitle}
|
|
427
|
-
description: ${newDescription}
|
|
428
|
-
version: ${version}
|
|
429
|
-
updatedAt: ${new Date().toISOString()}
|
|
430
|
-
createdAt: ${existingData.createdAt || new Date().toISOString()}
|
|
431
|
-
---
|
|
432
|
-
|
|
433
|
-
${content}`;
|
|
434
|
-
await fs.writeFile(skillPath, skillContent, "utf-8");
|
|
435
|
-
yield {
|
|
436
|
-
type: "meta:status",
|
|
437
|
-
data: { message: `Skill "${newTitle}" updated to v${version}`, severity: "success" },
|
|
438
|
-
};
|
|
439
|
-
yield {
|
|
440
|
-
type: "action:result",
|
|
441
|
-
data: {
|
|
442
|
-
action: "updateSkill",
|
|
443
|
-
toolCallId,
|
|
444
|
-
result: {
|
|
445
|
-
success: true,
|
|
446
|
-
path: `skills/${id}/SKILL.md`,
|
|
447
|
-
version,
|
|
448
|
-
message: `Skill "${newTitle}" updated to version ${version}`,
|
|
449
|
-
},
|
|
450
|
-
},
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
catch (error) {
|
|
454
|
-
const errorMsg = error.code === "ENOENT" ? `Skill "${id}" does not exist` : error.message;
|
|
455
|
-
yield {
|
|
456
|
-
type: "meta:status",
|
|
457
|
-
data: { message: `Failed to update skill: ${errorMsg}`, severity: "error" },
|
|
458
|
-
};
|
|
459
|
-
yield {
|
|
460
|
-
type: "action:result",
|
|
461
|
-
data: {
|
|
462
|
-
action: "updateSkill",
|
|
463
|
-
toolCallId,
|
|
464
|
-
result: { error: errorMsg },
|
|
465
|
-
},
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
});
|
|
469
|
-
// Update identity
|
|
470
|
-
builder.on("action:updateIdentity", async function* (event) {
|
|
471
|
-
const { content, toolCallId } = event.data;
|
|
472
|
-
try {
|
|
473
|
-
await fs.writeFile(resolvePath("IDENTITY.md"), content, "utf-8");
|
|
474
|
-
yield {
|
|
475
|
-
type: "meta:status",
|
|
476
|
-
data: { message: "Identity updated", severity: "success" },
|
|
477
|
-
};
|
|
478
|
-
yield {
|
|
479
|
-
type: "action:result",
|
|
480
|
-
data: {
|
|
481
|
-
action: "updateIdentity",
|
|
482
|
-
toolCallId,
|
|
483
|
-
result: {
|
|
484
|
-
success: true,
|
|
485
|
-
message: "Identity updated. Changes will take effect on next initialization.",
|
|
486
|
-
},
|
|
487
|
-
},
|
|
488
|
-
};
|
|
489
|
-
}
|
|
490
|
-
catch (error) {
|
|
491
|
-
yield {
|
|
492
|
-
type: "meta:status",
|
|
493
|
-
data: { message: `Failed to update identity: ${error.message}`, severity: "error" },
|
|
494
|
-
};
|
|
495
|
-
yield {
|
|
496
|
-
type: "action:result",
|
|
497
|
-
data: {
|
|
498
|
-
action: "updateIdentity",
|
|
499
|
-
toolCallId,
|
|
500
|
-
result: { error: error.message },
|
|
501
|
-
},
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
});
|
|
505
|
-
// Read identity files
|
|
506
|
-
builder.on("action:readIdentity", async function* (event) {
|
|
507
|
-
const { file, toolCallId } = event.data;
|
|
508
|
-
try {
|
|
509
|
-
const content = await fs.readFile(resolvePath(file), "utf-8");
|
|
510
|
-
yield {
|
|
511
|
-
type: "action:result",
|
|
512
|
-
data: {
|
|
513
|
-
action: "readIdentity",
|
|
514
|
-
toolCallId,
|
|
515
|
-
result: { file, content },
|
|
516
|
-
},
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
catch (error) {
|
|
520
|
-
yield {
|
|
521
|
-
type: "action:result",
|
|
522
|
-
data: {
|
|
523
|
-
action: "readIdentity",
|
|
524
|
-
toolCallId,
|
|
525
|
-
result: { error: `Could not read ${file}: ${error.message}` },
|
|
526
|
-
},
|
|
527
|
-
};
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
// Append to memory
|
|
531
|
-
builder.on("action:appendToMemory", async function* (event) {
|
|
532
|
-
const { category, content, toolCallId } = event.data;
|
|
533
|
-
const memoryFile = resolvePath(`memory/${category}.md`);
|
|
534
|
-
try {
|
|
535
|
-
const timestamp = new Date().toISOString();
|
|
536
|
-
const entry = `\n## ${timestamp}\n${content}\n`;
|
|
537
|
-
await fs.appendFile(memoryFile, entry, "utf-8");
|
|
538
|
-
yield {
|
|
539
|
-
type: "meta:status",
|
|
540
|
-
data: { message: `Added to ${category} memory`, severity: "success" },
|
|
541
|
-
};
|
|
542
|
-
yield {
|
|
543
|
-
type: "action:result",
|
|
544
|
-
data: {
|
|
545
|
-
action: "appendToMemory",
|
|
546
|
-
toolCallId,
|
|
547
|
-
result: {
|
|
548
|
-
success: true,
|
|
549
|
-
message: `Added to ${category} memory`,
|
|
550
|
-
},
|
|
551
|
-
},
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
catch (error) {
|
|
555
|
-
yield {
|
|
556
|
-
type: "meta:status",
|
|
557
|
-
data: { message: `Failed to append to memory: ${error.message}`, severity: "error" },
|
|
558
|
-
};
|
|
559
|
-
yield {
|
|
560
|
-
type: "action:result",
|
|
561
|
-
data: {
|
|
562
|
-
action: "appendToMemory",
|
|
563
|
-
toolCallId,
|
|
564
|
-
result: { error: error.message },
|
|
565
|
-
},
|
|
566
|
-
};
|
|
567
|
-
}
|
|
568
|
-
});
|
|
569
|
-
};
|
|
570
|
-
export default metaAgentPlugin;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { ui } from "@melony/ui-kit/server";
|
|
2
|
-
export const metaAgentUIPlugin = () => (builder) => {
|
|
3
|
-
builder.on("meta:status", async function* (event) {
|
|
4
|
-
yield ui.event(ui.status(event.data.message, event.data.severity));
|
|
5
|
-
});
|
|
6
|
-
builder.on("meta:skill-loaded", async function* (event) {
|
|
7
|
-
yield ui.event(ui.resourceCard(event.data.title, '', [
|
|
8
|
-
ui.text(event.data.instructions),
|
|
9
|
-
]));
|
|
10
|
-
});
|
|
11
|
-
};
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { uiEvent } from "../../ui/block.js";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { exec } from "node:child_process";
|
|
4
|
-
import { promisify } from "node:util";
|
|
5
|
-
import * as path from "node:path";
|
|
6
|
-
import { statusWidget } from "../../ui/widgets/status.js";
|
|
7
|
-
const execAsync = promisify(exec);
|
|
8
|
-
export const shellToolDefinitions = {
|
|
9
|
-
executeCommand: {
|
|
10
|
-
description: "Execute a shell command. Use 'cd' to change the current working directory for subsequent commands.",
|
|
11
|
-
inputSchema: z.object({
|
|
12
|
-
command: z.string().describe("The shell command to execute"),
|
|
13
|
-
}),
|
|
14
|
-
},
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Truncates a string by keeping the first and last N characters.
|
|
18
|
-
*/
|
|
19
|
-
function truncate(str, maxChars) {
|
|
20
|
-
if (!str || str.length <= maxChars)
|
|
21
|
-
return str;
|
|
22
|
-
const half = Math.floor(maxChars / 2);
|
|
23
|
-
const truncatedCount = str.length - maxChars;
|
|
24
|
-
return `${str.slice(0, half)}\n\n[... ${truncatedCount} characters truncated ...]\n\n${str.slice(-half)}`;
|
|
25
|
-
}
|
|
26
|
-
export const shellPlugin = (options = {}) => (builder) => {
|
|
27
|
-
const { cwd = process.cwd(), env = process.env, maxOutputLength = 10000 } = options;
|
|
28
|
-
builder.on("action:executeCommand", async function* (event, { state }) {
|
|
29
|
-
const { command, toolCallId } = event.data;
|
|
30
|
-
const currentCwd = state.cwd || cwd;
|
|
31
|
-
yield {
|
|
32
|
-
type: "shell:status",
|
|
33
|
-
data: { message: `Executing command: ${command} in ${currentCwd}` }
|
|
34
|
-
};
|
|
35
|
-
// Basic 'cd' detection and state update
|
|
36
|
-
if (command.trim().startsWith("cd ")) {
|
|
37
|
-
const targetDir = command.trim().slice(3).trim();
|
|
38
|
-
const newCwd = path.resolve(currentCwd, targetDir);
|
|
39
|
-
state.cwd = newCwd;
|
|
40
|
-
yield {
|
|
41
|
-
type: "shell:status",
|
|
42
|
-
data: { message: `Directory changed to ${newCwd}`, severity: "success" }
|
|
43
|
-
};
|
|
44
|
-
yield {
|
|
45
|
-
type: "action:result",
|
|
46
|
-
data: {
|
|
47
|
-
action: "executeCommand",
|
|
48
|
-
toolCallId,
|
|
49
|
-
result: {
|
|
50
|
-
stdout: `Changed directory to ${newCwd}`,
|
|
51
|
-
stderr: "",
|
|
52
|
-
success: true
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
try {
|
|
59
|
-
const { stdout, stderr } = await execAsync(command, { cwd: currentCwd, env });
|
|
60
|
-
yield {
|
|
61
|
-
type: "shell:status",
|
|
62
|
-
data: { message: `Command executed successfully`, severity: "success" }
|
|
63
|
-
};
|
|
64
|
-
yield {
|
|
65
|
-
type: "action:result",
|
|
66
|
-
data: {
|
|
67
|
-
action: "executeCommand",
|
|
68
|
-
toolCallId,
|
|
69
|
-
result: {
|
|
70
|
-
stdout: truncate(stdout, maxOutputLength),
|
|
71
|
-
stderr: truncate(stderr, maxOutputLength),
|
|
72
|
-
success: true
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
yield {
|
|
79
|
-
type: "action:result",
|
|
80
|
-
data: {
|
|
81
|
-
action: "executeCommand",
|
|
82
|
-
toolCallId,
|
|
83
|
-
result: {
|
|
84
|
-
error: error.message,
|
|
85
|
-
stdout: truncate(error.stdout, maxOutputLength),
|
|
86
|
-
stderr: truncate(error.stderr, maxOutputLength),
|
|
87
|
-
success: false,
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
yield {
|
|
92
|
-
type: "shell:status",
|
|
93
|
-
data: { message: `Command failed: ${error.message}`, severity: "error" }
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
builder.on("shell:status", async function* (event) {
|
|
98
|
-
yield uiEvent(statusWidget(event.data.message, event.data.severity));
|
|
99
|
-
});
|
|
100
|
-
};
|
package/dist/plugins/shell/ui.js
DELETED