openbot 0.1.28 → 0.1.29
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/dist/cli.js +1 -1
- package/dist/open-bot.js +84 -14
- package/dist/plugins/llm/index.js +2 -1
- package/dist/registry/index.js +1 -0
- package/dist/registry/ts-agent-loader.js +82 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
package/dist/open-bot.js
CHANGED
|
@@ -16,7 +16,7 @@ import { shellPlugin, shellToolDefinitions } from "./plugins/shell/index.js";
|
|
|
16
16
|
import { fileSystemPlugin, fileSystemToolDefinitions } from "./plugins/file-system/index.js";
|
|
17
17
|
import { approvalPlugin } from "./plugins/approval/index.js";
|
|
18
18
|
// Registry
|
|
19
|
-
import { PluginRegistry, AgentRegistry, discoverYamlAgents, loadPluginsFromDir } from "./registry/index.js";
|
|
19
|
+
import { PluginRegistry, AgentRegistry, discoverYamlAgents, discoverTsAgents, loadPluginsFromDir } from "./registry/index.js";
|
|
20
20
|
/**
|
|
21
21
|
* Create the OpenBot manager agent.
|
|
22
22
|
*
|
|
@@ -81,8 +81,12 @@ export async function createOpenBot(options) {
|
|
|
81
81
|
subscribe: ["manager:completion"],
|
|
82
82
|
});
|
|
83
83
|
// Discover community / user agents from ~/.openbot/agents/
|
|
84
|
-
const
|
|
85
|
-
|
|
84
|
+
const agentsDir = path.join(resolvedBaseDir, "agents");
|
|
85
|
+
const [yamlAgents, tsAgents] = await Promise.all([
|
|
86
|
+
discoverYamlAgents(agentsDir, pluginRegistry, model, options),
|
|
87
|
+
discoverTsAgents(agentsDir, model, options),
|
|
88
|
+
]);
|
|
89
|
+
for (const agent of [...yamlAgents, ...tsAgents]) {
|
|
86
90
|
agentRegistry.register(agent);
|
|
87
91
|
}
|
|
88
92
|
// ─── Compose the Melony App ──────────────────────────────────────
|
|
@@ -131,6 +135,61 @@ export async function createOpenBot(options) {
|
|
|
131
135
|
return `- **${a.name}**: ${a.description}${tools ? `\n Capabilities:\n${tools}` : ""}`;
|
|
132
136
|
})
|
|
133
137
|
.join("\n\n");
|
|
138
|
+
// 2.5 Command Prefix Router
|
|
139
|
+
// Allows bypassing the manager using "/agent task" (e.g. "/os list files")
|
|
140
|
+
app.on("user:text", async function* (event, { state }) {
|
|
141
|
+
const content = event.data.content.trim();
|
|
142
|
+
if (content.startsWith("/")) {
|
|
143
|
+
const firstSpace = content.indexOf(" ");
|
|
144
|
+
const prefix = firstSpace === -1 ? content.slice(1) : content.slice(1, firstSpace);
|
|
145
|
+
const task = firstSpace === -1 ? "" : content.slice(firstSpace + 1).trim();
|
|
146
|
+
if (agentNames.includes(prefix)) {
|
|
147
|
+
// Direct route to specialized agent
|
|
148
|
+
state.lastDirectAgent = prefix;
|
|
149
|
+
yield {
|
|
150
|
+
type: `agent:${prefix}:input`,
|
|
151
|
+
data: { content: task },
|
|
152
|
+
};
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Default: send to manager for reasoning/delegation
|
|
157
|
+
state.lastDirectAgent = undefined;
|
|
158
|
+
yield { type: "manager:input", data: event.data };
|
|
159
|
+
});
|
|
160
|
+
// 2.6 Global Tool Result Dispatcher
|
|
161
|
+
// Routes tool results to the appropriate agent's internal loop.
|
|
162
|
+
// taskResult means that the loop is complete.
|
|
163
|
+
app.on("action:taskResult", async function* (event, { state, suspend }) {
|
|
164
|
+
const s = state;
|
|
165
|
+
// 1. Direct route (prefix command)
|
|
166
|
+
if (s.lastDirectAgent) {
|
|
167
|
+
// suspend not to go to infinite loop
|
|
168
|
+
// suspend({
|
|
169
|
+
// type: `agent:${s.lastDirectAgent}:result`,
|
|
170
|
+
// data: event.data,
|
|
171
|
+
// } as ChatEvent);
|
|
172
|
+
yield {
|
|
173
|
+
type: `agent:${s.lastDirectAgent}:result`,
|
|
174
|
+
data: event.data,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
// 2. Delegation route (manager-led)
|
|
178
|
+
// Find which agent has a pending task
|
|
179
|
+
const pendingAgentName = Object.keys(s.pendingAgentTasks || {}).find((name) => s.pendingAgentTasks[name]);
|
|
180
|
+
if (pendingAgentName) {
|
|
181
|
+
yield {
|
|
182
|
+
type: `agent:${pendingAgentName}:result`,
|
|
183
|
+
data: event.data,
|
|
184
|
+
};
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
// 3. Manager route (brain tools, etc.)
|
|
188
|
+
yield {
|
|
189
|
+
type: "manager:result",
|
|
190
|
+
data: event.data,
|
|
191
|
+
};
|
|
192
|
+
});
|
|
134
193
|
app.use(llmPlugin({
|
|
135
194
|
model: model,
|
|
136
195
|
system: async (context) => {
|
|
@@ -139,21 +198,23 @@ export async function createOpenBot(options) {
|
|
|
139
198
|
]);
|
|
140
199
|
return `${brainPrompt}
|
|
141
200
|
|
|
142
|
-
##
|
|
143
|
-
You are the **Manager Agent**.
|
|
144
|
-
|
|
201
|
+
## Core Role: Manager Agent
|
|
202
|
+
You are the **Manager Agent**. You have exactly two jobs:
|
|
203
|
+
1. **Task Delegation**: Orchestrate tasks by delegating them to specialized agents.
|
|
204
|
+
2. **Memory & Identity**: Manage your long-term memory and identity using your core brain tools (\`remember\`, \`recall\`, \`updateIdentity\`, etc.).
|
|
205
|
+
|
|
206
|
+
### Delegation & Reporting Guidelines:
|
|
207
|
+
- **Delegate by Default**: If a task requires capabilities outside of memory/identity management (like shell access, file operations, web browsing), you **MUST** delegate to the appropriate agent.
|
|
208
|
+
- **Be Concise**: When a sub-agent completes a task, provide a **nice, concise summary**. Sub-agents provide detailed outputs in the background; your response should be a brief, high-level answer to the user. Do not repeat details unless necessary.
|
|
209
|
+
- **Detailed Delegation**: When calling \`delegateTask\`, provide a thorough description of the task so the sub-agent has full context.
|
|
145
210
|
|
|
146
211
|
### Available Agents:
|
|
147
212
|
${agentDescriptions}
|
|
148
213
|
|
|
149
|
-
|
|
150
|
-
1. **Choose the Best Expert**: Analyze the user's request and select the agent whose description most closely matches the required expertise.
|
|
151
|
-
2. **Task Description**: When delegating, provide a clear and detailed task description. Include any context the agent might need to succeed.
|
|
152
|
-
3. **No "I Can't"**: If an agent is available that can handle a request, do not tell the user you cannot do it. Simply delegate.
|
|
153
|
-
4. **Summary**: Once an agent returns a result, summarize the findings or actions for the user.
|
|
154
|
-
|
|
155
|
-
Example: If the user asks to "check the weather", and you see a 'browser' agent, delegate the task to it.`;
|
|
214
|
+
Remember: You are the orchestrator. Let the specialized agents do the work, and you provide the concise final answer.`;
|
|
156
215
|
},
|
|
216
|
+
promptInputType: "manager:input",
|
|
217
|
+
actionResultInputType: "manager:result",
|
|
157
218
|
completionEventType: "manager:completion",
|
|
158
219
|
toolDefinitions: {
|
|
159
220
|
...brainToolDefinitions,
|
|
@@ -184,9 +245,10 @@ Example: If the user asks to "check the weather", and you see a 'browser' agent,
|
|
|
184
245
|
const s = state;
|
|
185
246
|
const pending = s.pendingAgentTasks?.[agent.name];
|
|
186
247
|
if (pending) {
|
|
248
|
+
// This was a delegated task — bridge back to the manager's tool call
|
|
187
249
|
delete s.pendingAgentTasks[agent.name];
|
|
188
250
|
yield {
|
|
189
|
-
type: "
|
|
251
|
+
type: "manager:result",
|
|
190
252
|
data: {
|
|
191
253
|
action: "delegateTask",
|
|
192
254
|
toolCallId: pending.toolCallId,
|
|
@@ -194,6 +256,14 @@ Example: If the user asks to "check the weather", and you see a 'browser' agent,
|
|
|
194
256
|
},
|
|
195
257
|
};
|
|
196
258
|
}
|
|
259
|
+
else {
|
|
260
|
+
// This was a direct command (prefix) — show output directly as assistant text
|
|
261
|
+
yield {
|
|
262
|
+
type: "assistant:text",
|
|
263
|
+
data: { content: event.data.content },
|
|
264
|
+
meta: { agent: agent.name }
|
|
265
|
+
};
|
|
266
|
+
}
|
|
197
267
|
});
|
|
198
268
|
}
|
|
199
269
|
// 6. Init handlers
|
|
@@ -22,10 +22,11 @@ export const llmPlugin = (options) => (builder) => {
|
|
|
22
22
|
state.messages.push(newMessage);
|
|
23
23
|
// Evaluate dynamic system prompt if it's a function
|
|
24
24
|
const systemPrompt = typeof system === "function" ? await system(context) : system;
|
|
25
|
+
const recentMessages = getRecentHistory(state.messages, 20);
|
|
25
26
|
const result = streamText({
|
|
26
27
|
model,
|
|
27
28
|
system: systemPrompt,
|
|
28
|
-
messages:
|
|
29
|
+
messages: recentMessages,
|
|
29
30
|
tools: toolDefinitions,
|
|
30
31
|
});
|
|
31
32
|
let assistantText = "";
|
package/dist/registry/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { PluginRegistry } from "./plugin-registry.js";
|
|
2
2
|
export { AgentRegistry } from "./agent-registry.js";
|
|
3
3
|
export { discoverYamlAgents, listYamlAgents } from "./yaml-agent-loader.js";
|
|
4
|
+
export { discoverTsAgents } from "./ts-agent-loader.js";
|
|
4
5
|
export { loadPluginsFromDir } from "./plugin-loader.js";
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { ensurePluginReady } from "./plugin-loader.js";
|
|
5
|
+
/**
|
|
6
|
+
* Discover and load TS-defined agents from a directory.
|
|
7
|
+
*
|
|
8
|
+
* Scans each subdirectory for a package.json and an index file.
|
|
9
|
+
*
|
|
10
|
+
* @param agentsDir Absolute path to the agents directory (e.g. ~/.openbot/agents)
|
|
11
|
+
* @param defaultModel Language model to use for agent LLMs
|
|
12
|
+
* @param options Optional API keys for creating specific models
|
|
13
|
+
* @returns Array of discovered agent entries ready for registration
|
|
14
|
+
*/
|
|
15
|
+
export async function discoverTsAgents(agentsDir, defaultModel, options) {
|
|
16
|
+
const agents = [];
|
|
17
|
+
try {
|
|
18
|
+
const entries = await fs.readdir(agentsDir, { withFileTypes: true });
|
|
19
|
+
for (const entry of entries) {
|
|
20
|
+
if (!entry.isDirectory())
|
|
21
|
+
continue;
|
|
22
|
+
if (entry.name.startsWith(".") || entry.name.startsWith("_"))
|
|
23
|
+
continue;
|
|
24
|
+
const agentDir = path.join(agentsDir, entry.name);
|
|
25
|
+
// We only consider it a TS agent if it doesn't have an agent.yaml
|
|
26
|
+
// (This avoids double-loading if someone has both for some reason)
|
|
27
|
+
const yamlPath = path.join(agentDir, "agent.yaml");
|
|
28
|
+
const hasYaml = await fs.access(yamlPath).then(() => true).catch(() => false);
|
|
29
|
+
if (hasYaml)
|
|
30
|
+
continue;
|
|
31
|
+
// Check for package.json to see if it's a package
|
|
32
|
+
const pkgPath = path.join(agentDir, "package.json");
|
|
33
|
+
const hasPackageJson = await fs.access(pkgPath).then(() => true).catch(() => false);
|
|
34
|
+
if (!hasPackageJson)
|
|
35
|
+
continue;
|
|
36
|
+
try {
|
|
37
|
+
// 1. Ensure dependencies and build are ready
|
|
38
|
+
await ensurePluginReady(agentDir);
|
|
39
|
+
// 2. Find index file
|
|
40
|
+
let indexPath;
|
|
41
|
+
const possibleIndices = ["dist/index.js", "index.js", "index.ts"];
|
|
42
|
+
for (const file of possibleIndices) {
|
|
43
|
+
try {
|
|
44
|
+
const fullPath = path.join(agentDir, file);
|
|
45
|
+
await fs.access(fullPath);
|
|
46
|
+
indexPath = fullPath;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!indexPath)
|
|
54
|
+
continue;
|
|
55
|
+
// 3. Import and instantiate
|
|
56
|
+
const moduleUrl = pathToFileURL(indexPath).href;
|
|
57
|
+
const module = await import(moduleUrl);
|
|
58
|
+
// Support 'agent', 'plugin', 'default', or 'entry'
|
|
59
|
+
const definition = module.agent || module.plugin || module.default || module.entry;
|
|
60
|
+
if (definition && typeof definition.factory === "function") {
|
|
61
|
+
const name = definition.name || entry.name;
|
|
62
|
+
const description = definition.description || "TS Agent";
|
|
63
|
+
agents.push({
|
|
64
|
+
name,
|
|
65
|
+
description,
|
|
66
|
+
plugin: definition.factory({ ...options, model: defaultModel }),
|
|
67
|
+
capabilities: definition.capabilities,
|
|
68
|
+
subscribe: definition.subscribe,
|
|
69
|
+
});
|
|
70
|
+
console.log(`[agents] Loaded TS agent: ${name} — ${description}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
console.warn(`[agents] Failed to load TS agent package "${entry.name}":`, err);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Agents directory doesn't exist
|
|
80
|
+
}
|
|
81
|
+
return agents;
|
|
82
|
+
}
|