openbot 0.2.3 → 0.2.6
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 +1 -1
- package/dist/agents/agent-creator.js +58 -19
- package/dist/agents/os-agent.js +1 -4
- package/dist/agents/planner-agent.js +32 -0
- package/dist/agents/topic-agent.js +1 -1
- package/dist/architecture/contracts.js +1 -0
- package/dist/architecture/execution-engine.js +151 -0
- package/dist/architecture/intent-classifier.js +26 -0
- package/dist/architecture/planner.js +106 -0
- package/dist/automation-worker.js +121 -0
- package/dist/automations.js +52 -0
- package/dist/cli.js +116 -146
- package/dist/config.js +20 -0
- package/dist/core/agents.js +41 -0
- package/dist/core/delegation.js +124 -0
- package/dist/core/manager.js +73 -0
- package/dist/core/plugins.js +77 -0
- package/dist/core/router.js +40 -0
- package/dist/installers.js +156 -0
- package/dist/marketplace.js +80 -0
- package/dist/open-bot.js +34 -157
- package/dist/orchestrator.js +247 -51
- package/dist/plugins/approval/index.js +107 -3
- package/dist/plugins/brain/index.js +17 -86
- package/dist/plugins/brain/memory.js +1 -1
- package/dist/plugins/brain/prompt.js +8 -13
- package/dist/plugins/brain/types.js +0 -15
- package/dist/plugins/file-system/index.js +8 -8
- package/dist/plugins/llm/context-shaping.js +177 -0
- package/dist/plugins/llm/index.js +223 -49
- package/dist/plugins/memory/index.js +220 -0
- package/dist/plugins/memory/memory.js +122 -0
- package/dist/plugins/memory/prompt.js +55 -0
- package/dist/plugins/memory/types.js +45 -0
- package/dist/plugins/shell/index.js +3 -3
- package/dist/plugins/skills/index.js +9 -9
- package/dist/registry/index.js +1 -4
- package/dist/registry/plugin-loader.js +361 -56
- package/dist/registry/plugin-registry.js +21 -4
- package/dist/registry/ts-agent-loader.js +4 -4
- package/dist/registry/yaml-agent-loader.js +78 -20
- package/dist/runtime/execution-trace.js +41 -0
- package/dist/runtime/intent-routing.js +26 -0
- package/dist/runtime/openbot-runtime.js +354 -0
- package/dist/server.js +513 -41
- package/dist/ui/widgets/approval-card.js +22 -2
- package/dist/ui/widgets/delegation.js +29 -0
- package/dist/version.js +62 -0
- package/package.json +4 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export async function* runOpenBot(event, context, managerRuntime, agentRuntimes) {
|
|
2
|
+
const { state } = context;
|
|
3
|
+
// Initialize state
|
|
4
|
+
if (!state.messages)
|
|
5
|
+
state.messages = [];
|
|
6
|
+
if (!state.agentStates)
|
|
7
|
+
state.agentStates = {};
|
|
8
|
+
// 1. Direct agent routing (e.g. "@os list files")
|
|
9
|
+
if (event.type === "agent:input") {
|
|
10
|
+
const content = event.data.content;
|
|
11
|
+
if (content?.startsWith("@")) {
|
|
12
|
+
const match = content.match(/^@([a-zA-Z0-9_-]+)\s*(.*)$/);
|
|
13
|
+
if (match) {
|
|
14
|
+
const [, agentName, remaining] = match;
|
|
15
|
+
const runtime = agentRuntimes.get(agentName);
|
|
16
|
+
if (runtime) {
|
|
17
|
+
if (!state.agentStates[agentName])
|
|
18
|
+
state.agentStates[agentName] = {};
|
|
19
|
+
const agentEvent = {
|
|
20
|
+
...event,
|
|
21
|
+
data: {
|
|
22
|
+
content: remaining || "Hello",
|
|
23
|
+
attachments: event.data.attachments,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
yield* runtime.run(agentEvent, {
|
|
27
|
+
runId: context.runId,
|
|
28
|
+
state: state.agentStates[agentName],
|
|
29
|
+
});
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// 2. Default routing: translate user event to manager input
|
|
36
|
+
yield* managerRuntime.run({ ...event, type: "agent:input" }, {
|
|
37
|
+
runId: context.runId,
|
|
38
|
+
state: state,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { resolvePath, DEFAULT_BASE_DIR, loadConfig } from "./config.js";
|
|
6
|
+
import { readAgentConfig, ensurePluginReady } from "./registry/plugin-loader.js";
|
|
7
|
+
const BUILT_IN_PLUGIN_NAMES = new Set(["shell", "file-system", "approval"]);
|
|
8
|
+
function run(command, args, options) {
|
|
9
|
+
execFileSync(command, args, {
|
|
10
|
+
cwd: options?.cwd,
|
|
11
|
+
stdio: options?.quiet ? "ignore" : "inherit",
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function log(message, quiet) {
|
|
15
|
+
if (!quiet)
|
|
16
|
+
console.log(message);
|
|
17
|
+
}
|
|
18
|
+
function getBaseDir() {
|
|
19
|
+
const cfg = loadConfig();
|
|
20
|
+
const baseDir = cfg.baseDir || DEFAULT_BASE_DIR;
|
|
21
|
+
return resolvePath(baseDir);
|
|
22
|
+
}
|
|
23
|
+
async function directoryExists(targetPath) {
|
|
24
|
+
return fs.access(targetPath).then(() => true).catch(() => false);
|
|
25
|
+
}
|
|
26
|
+
export function checkGitHubRepo(repo) {
|
|
27
|
+
try {
|
|
28
|
+
const url = `https://github.com/${repo}.git`;
|
|
29
|
+
run("git", ["ls-remote", url], { quiet: true });
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function checkNpmPackage(pkg) {
|
|
37
|
+
try {
|
|
38
|
+
run("npm", ["show", pkg, "version"], { quiet: true });
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function parseSource(source) {
|
|
46
|
+
const normalized = source.trim();
|
|
47
|
+
const isGithub = (normalized.includes("/") || normalized.startsWith("github:"))
|
|
48
|
+
&& !normalized.startsWith("/")
|
|
49
|
+
&& !normalized.startsWith(".");
|
|
50
|
+
const isNpm = normalized.startsWith("@") || normalized.startsWith("npm:");
|
|
51
|
+
if (isGithub) {
|
|
52
|
+
return {
|
|
53
|
+
type: "github",
|
|
54
|
+
value: normalized.startsWith("github:") ? normalized.slice(7) : normalized,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (isNpm) {
|
|
58
|
+
return {
|
|
59
|
+
type: "npm",
|
|
60
|
+
value: normalized.startsWith("npm:") ? normalized.slice(4) : normalized,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return { type: "local", value: path.resolve(normalized) };
|
|
64
|
+
}
|
|
65
|
+
export async function installExtension(type, source, options = {}) {
|
|
66
|
+
const quiet = !!options.quiet;
|
|
67
|
+
const baseDir = getBaseDir();
|
|
68
|
+
const targetRoot = path.join(baseDir, type === "agent" ? "agents" : "plugins");
|
|
69
|
+
await fs.mkdir(targetRoot, { recursive: true });
|
|
70
|
+
// 1. Determine the folder name (the "id") - ALWAYS based on source or explicit id
|
|
71
|
+
let id = options.id;
|
|
72
|
+
if (!id) {
|
|
73
|
+
if (source.type === "github") {
|
|
74
|
+
id = path.basename(source.value); // e.g. "agent-browser"
|
|
75
|
+
}
|
|
76
|
+
else if (source.type === "npm") {
|
|
77
|
+
id = source.value.split("/").pop(); // e.g. "@melony/plugin-test" -> "plugin-test"
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
id = path.basename(source.value);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const targetDir = path.join(targetRoot, id);
|
|
84
|
+
const tempDir = path.join(tmpdir(), `openbot-install-${Date.now()}-${id}`);
|
|
85
|
+
try {
|
|
86
|
+
log(`📦 Installing ${type} "${id}" from ${source.type}...`, quiet);
|
|
87
|
+
if (source.type === "github") {
|
|
88
|
+
const url = `https://github.com/${source.value}.git`;
|
|
89
|
+
run("git", ["clone", "--depth", "1", url, tempDir], { quiet });
|
|
90
|
+
}
|
|
91
|
+
else if (source.type === "npm") {
|
|
92
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
93
|
+
run("npm", ["install", source.value, "--prefix", tempDir], { quiet });
|
|
94
|
+
const pkgFolder = path.join(tempDir, "node_modules", source.value);
|
|
95
|
+
const moveTemp = path.join(tmpdir(), `openbot-npm-move-${Date.now()}`);
|
|
96
|
+
await fs.rename(pkgFolder, moveTemp);
|
|
97
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
98
|
+
await fs.rename(moveTemp, tempDir);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
102
|
+
await fs.cp(source.value, tempDir, { recursive: true });
|
|
103
|
+
}
|
|
104
|
+
if (await directoryExists(targetDir)) {
|
|
105
|
+
log(`⚠️ Removing existing folder: ${targetDir}`, quiet);
|
|
106
|
+
await fs.rm(targetDir, { recursive: true, force: true });
|
|
107
|
+
}
|
|
108
|
+
await fs.rename(tempDir, targetDir);
|
|
109
|
+
log(`✅ Installed to: ${targetDir}`, quiet);
|
|
110
|
+
// Prepare dependencies and build
|
|
111
|
+
await ensurePluginReady(targetDir);
|
|
112
|
+
// If it's an agent, check for missing plugins
|
|
113
|
+
if (type === "agent") {
|
|
114
|
+
await installMissingPluginsFromAgent(targetDir, { quiet });
|
|
115
|
+
}
|
|
116
|
+
log(`🎉 Successfully installed ${type}: ${id}`, quiet);
|
|
117
|
+
return id;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => undefined);
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Backward compatibility aliases
|
|
125
|
+
export const installPluginFromSource = (source, opts) => installExtension("plugin", source, opts);
|
|
126
|
+
export const installAgentFromSource = (source, opts) => installExtension("agent", source, opts);
|
|
127
|
+
export const parsePluginInstallSource = parseSource;
|
|
128
|
+
export const parseAgentInstallSource = parseSource;
|
|
129
|
+
export async function installMissingPluginsFromAgent(agentFolder, options = {}) {
|
|
130
|
+
const quiet = !!options.quiet;
|
|
131
|
+
const config = await readAgentConfig(agentFolder);
|
|
132
|
+
const baseDir = getBaseDir();
|
|
133
|
+
for (const pluginItem of config.plugins || []) {
|
|
134
|
+
const pluginName = typeof pluginItem === "string" ? pluginItem : pluginItem.name;
|
|
135
|
+
if (!pluginName || BUILT_IN_PLUGIN_NAMES.has(pluginName))
|
|
136
|
+
continue;
|
|
137
|
+
// Check if it already exists as a folder in plugins/
|
|
138
|
+
const pluginPath = path.join(baseDir, "plugins", pluginName);
|
|
139
|
+
const prefixedPluginPath = path.join(baseDir, "plugins", `plugin-${pluginName}`);
|
|
140
|
+
if (await (directoryExists(pluginPath)) || await (directoryExists(prefixedPluginPath))) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
log(`🔍 Agent needs plugin "${pluginName}". Searching...`, quiet);
|
|
144
|
+
const ghRepo = `meetopenbot/plugin-${pluginName}`;
|
|
145
|
+
if (checkGitHubRepo(ghRepo)) {
|
|
146
|
+
await installExtension("plugin", { type: "github", value: ghRepo }, { quiet });
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const npmPkg = `@melony/plugin-${pluginName}`;
|
|
150
|
+
if (checkNpmPackage(npmPkg)) {
|
|
151
|
+
await installExtension("plugin", { type: "npm", value: npmPkg }, { quiet });
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
log(`⚠️ Could not find plugin "${pluginName}" for this agent.`, quiet);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { installAgentFromSource, installPluginFromSource } from "./installers.js";
|
|
2
|
+
const DEFAULT_MARKETPLACE_REGISTRY_URL = "https://raw.githubusercontent.com/meetopenbot/openbot-registry/main/registry.json";
|
|
3
|
+
const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
4
|
+
let cachedRegistry = null;
|
|
5
|
+
let cacheExpiresAt = 0;
|
|
6
|
+
function normalizeSource(source) {
|
|
7
|
+
if (!source || typeof source !== "object") {
|
|
8
|
+
throw new Error("invalid source");
|
|
9
|
+
}
|
|
10
|
+
const value = source;
|
|
11
|
+
if ((value.type !== "github" && value.type !== "npm") || typeof value.value !== "string" || !value.value.trim()) {
|
|
12
|
+
throw new Error("invalid source");
|
|
13
|
+
}
|
|
14
|
+
return { type: value.type, value: value.value.trim() };
|
|
15
|
+
}
|
|
16
|
+
function normalizeItem(item) {
|
|
17
|
+
if (!item || typeof item !== "object")
|
|
18
|
+
throw new Error("invalid item");
|
|
19
|
+
const value = item;
|
|
20
|
+
if (typeof value.id !== "string" || !value.id.trim())
|
|
21
|
+
throw new Error("invalid item id");
|
|
22
|
+
if (typeof value.name !== "string" || !value.name.trim())
|
|
23
|
+
throw new Error("invalid item name");
|
|
24
|
+
if (typeof value.description !== "string")
|
|
25
|
+
throw new Error("invalid item description");
|
|
26
|
+
const tags = Array.isArray(value.tags) ? value.tags.filter((tag) => typeof tag === "string") : undefined;
|
|
27
|
+
return {
|
|
28
|
+
id: value.id.trim(),
|
|
29
|
+
name: value.name.trim(),
|
|
30
|
+
description: value.description,
|
|
31
|
+
source: normalizeSource(value.source),
|
|
32
|
+
tags,
|
|
33
|
+
image: typeof value.image === "string" ? value.image.trim() : undefined,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function normalizeRegistry(value) {
|
|
37
|
+
if (!value || typeof value !== "object")
|
|
38
|
+
throw new Error("invalid registry");
|
|
39
|
+
const raw = value;
|
|
40
|
+
if (typeof raw.version !== "number")
|
|
41
|
+
throw new Error("invalid version");
|
|
42
|
+
const agents = Array.isArray(raw.agents) ? raw.agents.map(normalizeItem) : [];
|
|
43
|
+
const plugins = Array.isArray(raw.plugins) ? raw.plugins.map(normalizeItem) : [];
|
|
44
|
+
return { version: raw.version, agents, plugins };
|
|
45
|
+
}
|
|
46
|
+
export async function getMarketplaceRegistry(forceRefresh = false) {
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
if (!forceRefresh && cachedRegistry && now < cacheExpiresAt) {
|
|
49
|
+
return cachedRegistry;
|
|
50
|
+
}
|
|
51
|
+
const registryUrl = process.env.OPENBOT_MARKETPLACE_REGISTRY_URL || DEFAULT_MARKETPLACE_REGISTRY_URL;
|
|
52
|
+
const response = await fetch(registryUrl);
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
throw new Error(`Failed to fetch marketplace registry (${response.status})`);
|
|
55
|
+
}
|
|
56
|
+
const json = await response.json();
|
|
57
|
+
const normalized = normalizeRegistry(json);
|
|
58
|
+
cachedRegistry = normalized;
|
|
59
|
+
cacheExpiresAt = now + CACHE_TTL_MS;
|
|
60
|
+
return normalized;
|
|
61
|
+
}
|
|
62
|
+
export async function installMarketplaceAgent(agentId) {
|
|
63
|
+
const registry = await getMarketplaceRegistry(true);
|
|
64
|
+
const agent = registry.agents.find((entry) => entry.id === agentId);
|
|
65
|
+
if (!agent)
|
|
66
|
+
throw new Error(`Agent "${agentId}" was not found in marketplace`);
|
|
67
|
+
if (agent.source.type !== "github") {
|
|
68
|
+
throw new Error(`Marketplace agent "${agentId}" has unsupported source type "${agent.source.type}"`);
|
|
69
|
+
}
|
|
70
|
+
const installedName = await installAgentFromSource({ type: "github", value: agent.source.value }, { id: agent.id });
|
|
71
|
+
return { installedName, agent };
|
|
72
|
+
}
|
|
73
|
+
export async function installMarketplacePlugin(pluginId) {
|
|
74
|
+
const registry = await getMarketplaceRegistry(true);
|
|
75
|
+
const plugin = registry.plugins.find((entry) => entry.id === pluginId);
|
|
76
|
+
if (!plugin)
|
|
77
|
+
throw new Error(`Plugin "${pluginId}" was not found in marketplace`);
|
|
78
|
+
const installedName = await installPluginFromSource(plugin.source, { id: plugin.id });
|
|
79
|
+
return { installedName, plugin };
|
|
80
|
+
}
|
package/dist/open-bot.js
CHANGED
|
@@ -1,26 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { topicAgent } from "./agents/topic-agent.js";
|
|
3
|
-
import { agentCreatorAgent } from "./agents/agent-creator.js";
|
|
4
|
-
import { brainPlugin, brainToolDefinitions, createBrainPromptBuilder } from "./plugins/brain/index.js";
|
|
5
|
-
import { llmPlugin } from "./plugins/llm/index.js";
|
|
1
|
+
import { melony } from "melony";
|
|
6
2
|
import { loadConfig, resolvePath, DEFAULT_BASE_DIR } from "./config.js";
|
|
7
3
|
import { createModel, parseModelString } from "./models.js";
|
|
8
4
|
import { DEFAULT_MODEL_ID } from "./model-defaults.js";
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { approvalPlugin } from "./plugins/approval/index.js";
|
|
14
|
-
import { PluginRegistry, AgentRegistry, discoverYamlAgents, discoverTsAgents, loadPluginsFromDir } from "./registry/index.js";
|
|
15
|
-
import { Orchestrator } from "./orchestrator.js";
|
|
5
|
+
import { setupPluginRegistry } from "./core/plugins.js";
|
|
6
|
+
import { createManagerPlugin } from "./core/manager.js";
|
|
7
|
+
import { setupDelegation } from "./core/delegation.js";
|
|
8
|
+
import { runOpenBot } from "./core/router.js";
|
|
16
9
|
/**
|
|
17
|
-
* Create the OpenBot
|
|
18
|
-
*
|
|
19
|
-
* Architecture:
|
|
20
|
-
* 1. Each agent gets its own isolated melony runtime (no shared handlers or state).
|
|
21
|
-
* 2. The manager has its own runtime with brain tools and a delegateTask tool.
|
|
22
|
-
* 3. The Orchestrator routes events between manager and agent runtimes.
|
|
23
|
-
* 4. Plugins are only instantiated within the runtime that needs them — no duplication.
|
|
10
|
+
* Create the OpenBot runtime.
|
|
24
11
|
*/
|
|
25
12
|
export async function createOpenBot(options) {
|
|
26
13
|
const config = loadConfig();
|
|
@@ -30,145 +17,35 @@ export async function createOpenBot(options) {
|
|
|
30
17
|
const { provider, modelId } = parseModelString(configuredModel);
|
|
31
18
|
const resolvedModelId = `${provider}/${modelId}`;
|
|
32
19
|
const model = createModel(options);
|
|
33
|
-
//
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
pluginRegistry.register({
|
|
42
|
-
name: "file-system",
|
|
43
|
-
description: "Read, write, list, and delete files",
|
|
44
|
-
toolDefinitions: fileSystemToolDefinitions,
|
|
45
|
-
factory: () => fileSystemPlugin({ baseDir: "/" }),
|
|
46
|
-
});
|
|
47
|
-
pluginRegistry.register({
|
|
48
|
-
name: "approval",
|
|
49
|
-
description: "Require user approval for specific actions",
|
|
50
|
-
toolDefinitions: {},
|
|
51
|
-
factory: (options) => approvalPlugin(options),
|
|
52
|
-
});
|
|
53
|
-
// ─── Shared Plugins ──────────────────────────────────────────────
|
|
54
|
-
const sharedPlugins = await loadPluginsFromDir(path.join(resolvedBaseDir, "plugins"));
|
|
55
|
-
for (const p of sharedPlugins) {
|
|
56
|
-
pluginRegistry.register(p);
|
|
57
|
-
console.log(`[plugins] Loaded shared plugin: ${p.name}`);
|
|
20
|
+
// 1. Setup unified registry (built-in tools + agents + community plugins)
|
|
21
|
+
const registry = await setupPluginRegistry(resolvedBaseDir, model, options);
|
|
22
|
+
// 2. Initialize agent runtimes
|
|
23
|
+
const agentRuntimes = new Map();
|
|
24
|
+
for (const agent of registry.getAgents()) {
|
|
25
|
+
const builder = melony();
|
|
26
|
+
builder.use(agent.plugin);
|
|
27
|
+
agentRuntimes.set(agent.name, builder.build());
|
|
58
28
|
}
|
|
59
|
-
//
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
capabilities: {
|
|
74
|
-
...Object.fromEntries(Object.entries(fileSystemToolDefinitions).map(([k, v]) => [k, v.description])),
|
|
75
|
-
},
|
|
76
|
-
plugin: agentCreatorAgent({ model: model }),
|
|
77
|
-
});
|
|
78
|
-
// Discover community / user agents from ~/.openbot/agents/
|
|
79
|
-
const agentsDir = path.join(resolvedBaseDir, "agents");
|
|
80
|
-
const [yamlAgents, tsAgents] = await Promise.all([
|
|
81
|
-
discoverYamlAgents(agentsDir, pluginRegistry, model, options),
|
|
82
|
-
discoverTsAgents(agentsDir, model, options),
|
|
83
|
-
]);
|
|
84
|
-
for (const agent of [...yamlAgents, ...tsAgents]) {
|
|
85
|
-
agentRegistry.register(agent);
|
|
29
|
+
// 3. Initialize manager runtime
|
|
30
|
+
const managerBuilder = melony();
|
|
31
|
+
managerBuilder.use(createManagerPlugin(model, resolvedModelId, resolvedBaseDir, registry));
|
|
32
|
+
// 4. Setup delegation
|
|
33
|
+
setupDelegation(managerBuilder, agentRuntimes);
|
|
34
|
+
const managerRuntime = managerBuilder.build();
|
|
35
|
+
// 5. Trigger initialization for all runtimes
|
|
36
|
+
const initPromises = [];
|
|
37
|
+
const exhaust = async (runtime) => {
|
|
38
|
+
const iterator = runtime.run({ type: "init" }, { runId: "init", state: {} });
|
|
39
|
+
for await (const _ of iterator) { /* side-effects only */ }
|
|
40
|
+
};
|
|
41
|
+
for (const agentRuntime of agentRuntimes.values()) {
|
|
42
|
+
initPromises.push(exhaust(agentRuntime));
|
|
86
43
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const tools = a.capabilities
|
|
94
|
-
? Object.entries(a.capabilities)
|
|
95
|
-
.map(([name, desc]) => ` - ${name}: ${desc}`)
|
|
96
|
-
.join("\n")
|
|
97
|
-
: "";
|
|
98
|
-
return `<agent name="${a.name}">
|
|
99
|
-
<description>${a.description}</description>
|
|
100
|
-
${tools ? ` <capabilities>\n${tools}\n </capabilities>` : ""}
|
|
101
|
-
</agent>`;
|
|
102
|
-
})
|
|
103
|
-
.join("\n\n");
|
|
104
|
-
// The manager plugin composes brain, topic, and the manager LLM
|
|
105
|
-
const managerPlugin = (builder) => {
|
|
106
|
-
builder
|
|
107
|
-
.use(brainPlugin({
|
|
108
|
-
baseDir: resolvedBaseDir,
|
|
109
|
-
allowSoulModification: false,
|
|
110
|
-
}))
|
|
111
|
-
.use(topicAgent({ model: model }))
|
|
112
|
-
.use(llmPlugin({
|
|
113
|
-
model: model,
|
|
114
|
-
modelId: resolvedModelId,
|
|
115
|
-
usageScope: "manager",
|
|
116
|
-
system: async (context) => {
|
|
117
|
-
const brainPrompt = await buildBrainPrompt(context);
|
|
118
|
-
return `${brainPrompt}
|
|
119
|
-
|
|
120
|
-
<manager_core>
|
|
121
|
-
<role>
|
|
122
|
-
Your role is to be the central orchestrator of this system. Your primary goal is to solve user requests by managing your persistent memory and delegating tasks to expert sub-agents.
|
|
123
|
-
</role>
|
|
124
|
-
|
|
125
|
-
<operating_principles>
|
|
126
|
-
1. **Delegate by Default**: If a task requires specialized expertise (shell, files, browser, etc.), you **must** delegate to an expert agent via \`delegateTask\`.
|
|
127
|
-
2. **Context-Rich Delegation**: When calling \`delegateTask\`, provide a thorough, context-rich task description so the sub-agent can work independently. If the user included attachments, pass them through \`attachments\`.
|
|
128
|
-
3. **Concise Reporting**: After a sub-agent finishes, provide a high-level, concise summary to the user. Do not repeat technical details unless requested.
|
|
129
|
-
4. **Memory Management**: Use your brain tools (\`remember\`, \`recall\`, \`journal\`, etc.) to maintain continuity and preferences across sessions.
|
|
130
|
-
</operating_principles>
|
|
131
|
-
</manager_core>
|
|
132
|
-
|
|
133
|
-
<specialized_agents>
|
|
134
|
-
${agentDescriptions}
|
|
135
|
-
</specialized_agents>
|
|
136
|
-
|
|
137
|
-
<final_guidance>
|
|
138
|
-
Always remain professional and efficient. You manage the big picture; let the agents do the work.
|
|
139
|
-
</final_guidance>`;
|
|
140
|
-
},
|
|
141
|
-
promptInputType: "manager:input",
|
|
142
|
-
actionResultInputType: "manager:result",
|
|
143
|
-
completionEventType: "manager:completion",
|
|
144
|
-
toolDefinitions: {
|
|
145
|
-
...brainToolDefinitions,
|
|
146
|
-
delegateTask: {
|
|
147
|
-
description: `Delegate a specialized task to another agent. Use this whenever a task matches the capabilities of one of the available agents.`,
|
|
148
|
-
inputSchema: z.object({
|
|
149
|
-
agent: z.enum(agentNames).describe("The specialized agent to use"),
|
|
150
|
-
task: z.string().describe("The detailed task description for the agent"),
|
|
151
|
-
attachments: z.array(z.object({
|
|
152
|
-
id: z.string(),
|
|
153
|
-
name: z.string(),
|
|
154
|
-
mimeType: z.string(),
|
|
155
|
-
size: z.number(),
|
|
156
|
-
url: z.string(),
|
|
157
|
-
})).optional().describe("Image/file attachments from the user message, if present."),
|
|
158
|
-
}),
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
}));
|
|
44
|
+
initPromises.push(exhaust(managerRuntime));
|
|
45
|
+
await Promise.all(initPromises);
|
|
46
|
+
// 6. Return the runtime
|
|
47
|
+
return {
|
|
48
|
+
registry,
|
|
49
|
+
run: (event, context) => runOpenBot(event, context, managerRuntime, agentRuntimes),
|
|
162
50
|
};
|
|
163
|
-
// Collect agents for the orchestrator (excludes topic since it's a manager plugin now)
|
|
164
|
-
const orchestratorAgents = allAgents.map((a) => ({
|
|
165
|
-
name: a.name,
|
|
166
|
-
description: a.description,
|
|
167
|
-
plugin: a.plugin,
|
|
168
|
-
capabilities: a.capabilities,
|
|
169
|
-
}));
|
|
170
|
-
return new Orchestrator({
|
|
171
|
-
managerPlugin,
|
|
172
|
-
agents: orchestratorAgents,
|
|
173
|
-
});
|
|
174
51
|
}
|