openbot 0.2.2 → 0.2.5
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 +54 -141
- 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 +170 -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 +339 -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 +549 -31
- 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 +8 -7
|
@@ -0,0 +1,170 @@
|
|
|
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 { getPluginMetadata, 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 githubRepoToCloneUrl(repo) {
|
|
19
|
+
return `https://github.com/${repo}.git`;
|
|
20
|
+
}
|
|
21
|
+
function getBaseDir() {
|
|
22
|
+
const cfg = loadConfig();
|
|
23
|
+
const baseDir = cfg.baseDir || DEFAULT_BASE_DIR;
|
|
24
|
+
return resolvePath(baseDir);
|
|
25
|
+
}
|
|
26
|
+
async function directoryExists(targetPath) {
|
|
27
|
+
return fs.access(targetPath).then(() => true).catch(() => false);
|
|
28
|
+
}
|
|
29
|
+
export function checkGitHubRepo(repo) {
|
|
30
|
+
try {
|
|
31
|
+
run("git", ["ls-remote", githubRepoToCloneUrl(repo)], { quiet: true });
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function checkNpmPackage(pkg) {
|
|
39
|
+
try {
|
|
40
|
+
run("npm", ["show", pkg, "version"], { quiet: true });
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function parsePluginInstallSource(source) {
|
|
48
|
+
const normalized = source.trim();
|
|
49
|
+
const isGithub = (normalized.includes("/") || normalized.startsWith("github:"))
|
|
50
|
+
&& !normalized.startsWith("/")
|
|
51
|
+
&& !normalized.startsWith(".");
|
|
52
|
+
const isNpm = normalized.startsWith("@") || normalized.startsWith("npm:");
|
|
53
|
+
if (isGithub) {
|
|
54
|
+
return {
|
|
55
|
+
type: "github",
|
|
56
|
+
value: normalized.startsWith("github:") ? normalized.slice(7) : normalized,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (isNpm) {
|
|
60
|
+
return {
|
|
61
|
+
type: "npm",
|
|
62
|
+
value: normalized.startsWith("npm:") ? normalized.slice(4) : normalized,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return { type: "local", value: path.resolve(normalized) };
|
|
66
|
+
}
|
|
67
|
+
export function parseAgentInstallSource(source) {
|
|
68
|
+
const normalized = source.trim();
|
|
69
|
+
if (normalized.startsWith("github:")) {
|
|
70
|
+
return { type: "github", value: normalized.slice(7) };
|
|
71
|
+
}
|
|
72
|
+
return { type: "github", value: normalized };
|
|
73
|
+
}
|
|
74
|
+
export async function installPluginFromSource(source, options = {}) {
|
|
75
|
+
const quiet = !!options.quiet;
|
|
76
|
+
const tempDir = path.join(tmpdir(), `openbot-plugin-install-${Date.now()}`);
|
|
77
|
+
const baseDir = getBaseDir();
|
|
78
|
+
const pluginRoot = path.join(baseDir, "plugins");
|
|
79
|
+
await fs.mkdir(pluginRoot, { recursive: true });
|
|
80
|
+
try {
|
|
81
|
+
if (source.type === "github") {
|
|
82
|
+
log(`📦 Installing plugin from: ${githubRepoToCloneUrl(source.value)}`, quiet);
|
|
83
|
+
run("git", ["clone", "--depth", "1", githubRepoToCloneUrl(source.value), tempDir], { quiet });
|
|
84
|
+
}
|
|
85
|
+
else if (source.type === "npm") {
|
|
86
|
+
log(`📦 Installing plugin from: ${source.value}`, quiet);
|
|
87
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
88
|
+
run("npm", ["install", source.value, "--prefix", tempDir], { quiet });
|
|
89
|
+
const pkgFolder = path.join(tempDir, "node_modules", source.value);
|
|
90
|
+
const moveTemp = path.join(tmpdir(), `openbot-npm-move-${Date.now()}`);
|
|
91
|
+
await fs.rename(pkgFolder, moveTemp);
|
|
92
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
93
|
+
await fs.rename(moveTemp, tempDir);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
log(`📦 Installing plugin from: ${source.value}`, quiet);
|
|
97
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
98
|
+
await fs.cp(source.value, tempDir, { recursive: true });
|
|
99
|
+
}
|
|
100
|
+
const { name } = await getPluginMetadata(tempDir);
|
|
101
|
+
const targetDir = path.join(pluginRoot, name);
|
|
102
|
+
if (await directoryExists(targetDir)) {
|
|
103
|
+
log(`⚠️ Plugin "${name}" already exists. Overwriting...`, quiet);
|
|
104
|
+
await fs.rm(targetDir, { recursive: true, force: true });
|
|
105
|
+
}
|
|
106
|
+
await fs.rename(tempDir, targetDir);
|
|
107
|
+
log(`✅ Moved to: ${targetDir}`, quiet);
|
|
108
|
+
log(`⚙️ Preparing plugin "${name}"...`, quiet);
|
|
109
|
+
await ensurePluginReady(targetDir);
|
|
110
|
+
log(`\n🎉 Successfully installed plugin: ${name}`, quiet);
|
|
111
|
+
return name;
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => undefined);
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export async function installAgentFromSource(source, options = {}) {
|
|
119
|
+
const quiet = !!options.quiet;
|
|
120
|
+
const tempDir = path.join(tmpdir(), `openbot-agent-install-${Date.now()}`);
|
|
121
|
+
const baseDir = getBaseDir();
|
|
122
|
+
const agentRoot = path.join(baseDir, "agents");
|
|
123
|
+
await fs.mkdir(agentRoot, { recursive: true });
|
|
124
|
+
try {
|
|
125
|
+
log(`🤖 Installing agent from: ${githubRepoToCloneUrl(source.value)}`, quiet);
|
|
126
|
+
run("git", ["clone", "--depth", "1", githubRepoToCloneUrl(source.value), tempDir], { quiet });
|
|
127
|
+
const config = await readAgentConfig(tempDir);
|
|
128
|
+
const name = config.name || path.basename(source.value).replace(/^agent-/, "");
|
|
129
|
+
const targetDir = path.join(agentRoot, name);
|
|
130
|
+
if (await directoryExists(targetDir)) {
|
|
131
|
+
log(`⚠️ Agent "${name}" already exists. Overwriting...`, quiet);
|
|
132
|
+
await fs.rm(targetDir, { recursive: true, force: true });
|
|
133
|
+
}
|
|
134
|
+
await fs.rename(tempDir, targetDir);
|
|
135
|
+
log(`✅ Moved to: ${targetDir}`, quiet);
|
|
136
|
+
await installMissingPluginsFromAgent(targetDir, { quiet });
|
|
137
|
+
log(`\n🎉 Successfully installed agent: ${name}`, quiet);
|
|
138
|
+
return name;
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => undefined);
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
export async function installMissingPluginsFromAgent(agentFolder, options = {}) {
|
|
146
|
+
const quiet = !!options.quiet;
|
|
147
|
+
const config = await readAgentConfig(agentFolder);
|
|
148
|
+
const baseDir = getBaseDir();
|
|
149
|
+
for (const pluginItem of config.plugins || []) {
|
|
150
|
+
const pluginName = typeof pluginItem === "string" ? pluginItem : pluginItem.name;
|
|
151
|
+
if (!pluginName || BUILT_IN_PLUGIN_NAMES.has(pluginName))
|
|
152
|
+
continue;
|
|
153
|
+
const pluginPath = path.join(baseDir, "plugins", pluginName);
|
|
154
|
+
const existsLocally = await directoryExists(pluginPath);
|
|
155
|
+
if (existsLocally)
|
|
156
|
+
continue;
|
|
157
|
+
log(`🔍 Agent needs plugin "${pluginName}". Searching...`, quiet);
|
|
158
|
+
const ghRepo = `meetopenbot/plugin-${pluginName}`;
|
|
159
|
+
if (checkGitHubRepo(ghRepo)) {
|
|
160
|
+
await installPluginFromSource({ type: "github", value: ghRepo }, { quiet });
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const npmPkg = `@melony/plugin-${pluginName}`;
|
|
164
|
+
if (checkNpmPackage(npmPkg)) {
|
|
165
|
+
await installPluginFromSource({ type: "npm", value: npmPkg }, { quiet });
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
log(`⚠️ Could not find plugin "${pluginName}" for this agent. You may need to install it manually.`, quiet);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -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 });
|
|
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);
|
|
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
|
}
|