openbot 0.2.12 → 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 -279
- 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
package/dist/core/manager.js
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { memoryPlugin, memoryToolDefinitions, createMemoryPromptBuilder } from "../plugins/memory/index.js";
|
|
3
|
-
import { topicAgent } from "../agents/topic-agent.js";
|
|
4
|
-
import { llmPlugin } from "../plugins/llm/index.js";
|
|
5
|
-
export function createManagerPlugin(model, resolvedModelId, resolvedBaseDir, registry) {
|
|
6
|
-
const agentIds = registry.getAgentIds();
|
|
7
|
-
const allAgents = registry.getAgents();
|
|
8
|
-
const buildMemoryPrompt = createMemoryPromptBuilder(resolvedBaseDir);
|
|
9
|
-
const agentDescriptions = allAgents
|
|
10
|
-
.map((a) => {
|
|
11
|
-
const tools = a.capabilities
|
|
12
|
-
? Object.entries(a.capabilities)
|
|
13
|
-
.map(([name, desc]) => ` - ${name}: ${desc}`)
|
|
14
|
-
.join("\n")
|
|
15
|
-
: "";
|
|
16
|
-
return `<agent id="${a.id}" name="${a.name}">
|
|
17
|
-
<description>${a.description}</description>
|
|
18
|
-
${tools ? ` <capabilities>\n${tools}\n </capabilities>` : ""}
|
|
19
|
-
</agent>`;
|
|
20
|
-
})
|
|
21
|
-
.join("\n\n");
|
|
22
|
-
console.log("agentIds", agentIds);
|
|
23
|
-
console.log("agentDescriptions", agentDescriptions);
|
|
24
|
-
return (builder) => {
|
|
25
|
-
builder
|
|
26
|
-
.use(memoryPlugin({
|
|
27
|
-
baseDir: resolvedBaseDir,
|
|
28
|
-
}))
|
|
29
|
-
.use(topicAgent({ model: model }))
|
|
30
|
-
.use(llmPlugin({
|
|
31
|
-
model: model,
|
|
32
|
-
modelId: resolvedModelId,
|
|
33
|
-
usageScope: "manager",
|
|
34
|
-
system: async (context) => {
|
|
35
|
-
const memoryPrompt = await buildMemoryPrompt(context);
|
|
36
|
-
const state = context.state;
|
|
37
|
-
// Deterministically inject any custom state keys into the prompt
|
|
38
|
-
const standardKeys = ["messages", "agentStates", "usage", "cwd", "workspaceRoot", "title", "sessionId", "pendingAgentTasks"];
|
|
39
|
-
const customState = {};
|
|
40
|
-
for (const key of Object.keys(state)) {
|
|
41
|
-
if (!standardKeys.includes(key)) {
|
|
42
|
-
customState[key] = state[key];
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
const statePrompt = Object.keys(customState).length > 0
|
|
46
|
-
? `\n\n<session_state>\n${JSON.stringify(customState, null, 2)}\n</session_state>`
|
|
47
|
-
: "";
|
|
48
|
-
const finalSystemPrompt = `
|
|
49
|
-
|
|
50
|
-
<orchestrator>
|
|
51
|
-
Your goal is to solve user requests by delegating tasks to expert sub-agents.
|
|
52
|
-
|
|
53
|
-
**Directives**:
|
|
54
|
-
1. **Delegate**: Use \`delegateTask\` for any task matching an agent's description.
|
|
55
|
-
3. **Context**: Provide a clear, detailed task for the sub-agent. Pass any relevant user attachments.
|
|
56
|
-
4. **Report**: Summarize the sub-agent's work concisely for the user.
|
|
57
|
-
5. **Memory**: Use your memory tools (\`remember\`, \`recall\`) to maintain context across sessions.
|
|
58
|
-
6. **State**: Use \`updateSessionState\` to modify any value in the <session_state>.
|
|
59
|
-
</orchestrator>
|
|
60
|
-
|
|
61
|
-
${statePrompt}
|
|
62
|
-
|
|
63
|
-
<agents>
|
|
64
|
-
${agentDescriptions}
|
|
65
|
-
</agents>${memoryPrompt}`;
|
|
66
|
-
// console.log("finalSystemPrompt:::::", finalSystemPrompt);
|
|
67
|
-
return finalSystemPrompt;
|
|
68
|
-
},
|
|
69
|
-
toolDefinitions: {
|
|
70
|
-
...memoryToolDefinitions,
|
|
71
|
-
delegateTask: {
|
|
72
|
-
description: `Delegate a task to a specialized expert agent.`,
|
|
73
|
-
inputSchema: z.object({
|
|
74
|
-
agentId: z.enum(agentIds).describe("The ID of the agent to use"),
|
|
75
|
-
task: z.string().describe("The task for the agent to perform"),
|
|
76
|
-
stateKey: z.string().optional().describe("Optional key to store structured JSON result in the session state"),
|
|
77
|
-
attachments: z.array(z.object({
|
|
78
|
-
id: z.string(),
|
|
79
|
-
name: z.string(),
|
|
80
|
-
mimeType: z.string(),
|
|
81
|
-
size: z.number(),
|
|
82
|
-
url: z.string(),
|
|
83
|
-
})).optional().describe("Attachments to pass through to the agent"),
|
|
84
|
-
}),
|
|
85
|
-
},
|
|
86
|
-
updateSessionState: {
|
|
87
|
-
description: "Update a value in the session state using a JSON path.",
|
|
88
|
-
inputSchema: z.object({
|
|
89
|
-
path: z.string().describe("The JSON path to the value (e.g. 'project_plan.todos.0.status')"),
|
|
90
|
-
value: z.any().describe("The new value to set"),
|
|
91
|
-
}),
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
}));
|
|
95
|
-
};
|
|
96
|
-
}
|
package/dist/core/plugins.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { shellPlugin, shellToolDefinitions } from "../plugins/shell/index.js";
|
|
2
|
-
import { fileSystemPlugin, fileSystemToolDefinitions } from "../plugins/file-system/index.js";
|
|
3
|
-
import { approvalPlugin } from "../plugins/approval/index.js";
|
|
4
|
-
import { osAgent } from "../agents/os-agent.js";
|
|
5
|
-
import { agentCreatorAgent } from "../agents/agent-creator.js";
|
|
6
|
-
import { PluginRegistry, discoverPlugins } from "../registry/index.js";
|
|
7
|
-
import path from "node:path";
|
|
8
|
-
/**
|
|
9
|
-
* Build the unified plugin registry.
|
|
10
|
-
*
|
|
11
|
-
* Registers built-in tools and agents, then discovers community
|
|
12
|
-
* plugins (tools + agents) from ~/.openbot/plugins/.
|
|
13
|
-
*/
|
|
14
|
-
export async function setupPluginRegistry(resolvedBaseDir, model, options) {
|
|
15
|
-
const registry = new PluginRegistry();
|
|
16
|
-
// ── Built-in tools ───────────────────────────────────────────────
|
|
17
|
-
registry.register({
|
|
18
|
-
id: "shell",
|
|
19
|
-
name: "shell",
|
|
20
|
-
description: "Execute shell commands",
|
|
21
|
-
type: "tool",
|
|
22
|
-
toolDefinitions: shellToolDefinitions,
|
|
23
|
-
plugin: () => shellPlugin({ cwd: process.cwd() }),
|
|
24
|
-
isBuiltIn: true,
|
|
25
|
-
});
|
|
26
|
-
registry.register({
|
|
27
|
-
id: "file-system",
|
|
28
|
-
name: "file-system",
|
|
29
|
-
description: "Read, write, list, and delete files",
|
|
30
|
-
type: "tool",
|
|
31
|
-
toolDefinitions: fileSystemToolDefinitions,
|
|
32
|
-
plugin: () => fileSystemPlugin({ baseDir: "/" }),
|
|
33
|
-
isBuiltIn: true,
|
|
34
|
-
});
|
|
35
|
-
registry.register({
|
|
36
|
-
id: "approval",
|
|
37
|
-
name: "approval",
|
|
38
|
-
description: "Require user approval for specific actions",
|
|
39
|
-
type: "tool",
|
|
40
|
-
toolDefinitions: {},
|
|
41
|
-
plugin: (opts) => approvalPlugin(opts),
|
|
42
|
-
isBuiltIn: true,
|
|
43
|
-
});
|
|
44
|
-
// ── Built-in agents ──────────────────────────────────────────────
|
|
45
|
-
registry.register({
|
|
46
|
-
id: "os",
|
|
47
|
-
name: "os",
|
|
48
|
-
description: "Handles shell commands and file system operations",
|
|
49
|
-
type: "agent",
|
|
50
|
-
capabilities: {
|
|
51
|
-
...Object.fromEntries(Object.entries(shellToolDefinitions).map(([k, v]) => [k, v.description])),
|
|
52
|
-
...Object.fromEntries(Object.entries(fileSystemToolDefinitions).map(([k, v]) => [k, v.description])),
|
|
53
|
-
},
|
|
54
|
-
plugin: osAgent({ model }),
|
|
55
|
-
isBuiltIn: true,
|
|
56
|
-
});
|
|
57
|
-
registry.register({
|
|
58
|
-
id: "agent-creator",
|
|
59
|
-
name: "agent-creator",
|
|
60
|
-
description: "Helps the user create and update custom OpenBot agents via natural language.",
|
|
61
|
-
type: "agent",
|
|
62
|
-
capabilities: {
|
|
63
|
-
...Object.fromEntries(Object.entries(fileSystemToolDefinitions).map(([k, v]) => [k, v.description])),
|
|
64
|
-
},
|
|
65
|
-
plugin: agentCreatorAgent({ model }),
|
|
66
|
-
isBuiltIn: true,
|
|
67
|
-
});
|
|
68
|
-
// ── Custom agents and plugins ────────────────────────────────────
|
|
69
|
-
const agentsDir = path.join(resolvedBaseDir, "agents");
|
|
70
|
-
const pluginsDir = path.join(resolvedBaseDir, "plugins");
|
|
71
|
-
await discoverPlugins(pluginsDir, registry, model, options);
|
|
72
|
-
await discoverPlugins(agentsDir, registry, model, options);
|
|
73
|
-
return registry;
|
|
74
|
-
}
|
package/dist/core/router.js
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
function summarizeAgentEventValue(event) {
|
|
2
|
-
if (!event)
|
|
3
|
-
return undefined;
|
|
4
|
-
const value = event?.data?.result ?? event?.data?.content ?? event?.data?.message ?? event?.data;
|
|
5
|
-
if (typeof value === "string")
|
|
6
|
-
return value;
|
|
7
|
-
if (typeof value === "object" && value !== null)
|
|
8
|
-
return JSON.stringify(value, null, 2);
|
|
9
|
-
return undefined;
|
|
10
|
-
}
|
|
11
|
-
export async function* runOpenBot(event, context, managerRuntime, agentRuntimes, registry) {
|
|
12
|
-
const { state } = context;
|
|
13
|
-
const allAgents = registry.getAgents();
|
|
14
|
-
// Initialize state
|
|
15
|
-
if (!state.messages)
|
|
16
|
-
state.messages = [];
|
|
17
|
-
if (!state.agentStates)
|
|
18
|
-
state.agentStates = {};
|
|
19
|
-
// 1. Route non-user events directly (pluggable).
|
|
20
|
-
// If an event is tagged with meta.agentName, send it to that agent runtime.
|
|
21
|
-
// Otherwise, pass it to manager runtime unchanged.
|
|
22
|
-
if (event.type !== "agent:input") {
|
|
23
|
-
const targetAgent = event.meta?.agentName;
|
|
24
|
-
if (targetAgent) {
|
|
25
|
-
const runtime = agentRuntimes.get(targetAgent);
|
|
26
|
-
if (runtime) {
|
|
27
|
-
if (!state.agentStates[targetAgent])
|
|
28
|
-
state.agentStates[targetAgent] = {};
|
|
29
|
-
let resumedOutput = "";
|
|
30
|
-
for await (const agentChunk of runtime.run(event, {
|
|
31
|
-
runId: context.runId,
|
|
32
|
-
state: state.agentStates[targetAgent],
|
|
33
|
-
})) {
|
|
34
|
-
// Preserve sub-agent attribution when resuming after approval/deny
|
|
35
|
-
// so UI does not fall back to manager identity.
|
|
36
|
-
yield {
|
|
37
|
-
...agentChunk,
|
|
38
|
-
meta: {
|
|
39
|
-
...agentChunk?.meta,
|
|
40
|
-
...(event.meta?.delegationId ? { delegationId: event.meta.delegationId } : {}),
|
|
41
|
-
agentName: targetAgent,
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
if (agentChunk.type === "agent:output" || agentChunk.type === "action:result") {
|
|
45
|
-
const summary = summarizeAgentEventValue(agentChunk);
|
|
46
|
-
if (summary) {
|
|
47
|
-
if (resumedOutput)
|
|
48
|
-
resumedOutput += "\n\n";
|
|
49
|
-
resumedOutput += summary;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// Resolve pending delegated tool call after approval follow-up.
|
|
54
|
-
const maybeApprovalId = event?.data?.id;
|
|
55
|
-
const shouldResolvePending = (event.type === "action:approve" || event.type === "action:deny")
|
|
56
|
-
&& typeof maybeApprovalId === "string";
|
|
57
|
-
if (shouldResolvePending) {
|
|
58
|
-
const pending = state.pendingAgentTasks?.[maybeApprovalId];
|
|
59
|
-
if (pending) {
|
|
60
|
-
const wasDenied = event.type === "action:deny";
|
|
61
|
-
const delegateResult = wasDenied
|
|
62
|
-
? { error: "Action denied by user", denied: true }
|
|
63
|
-
: (resumedOutput || "Task completed with no output.");
|
|
64
|
-
delete state.pendingAgentTasks[maybeApprovalId];
|
|
65
|
-
yield {
|
|
66
|
-
type: "delegation:end",
|
|
67
|
-
meta: { delegationId: pending.delegationId, agentName: pending.agentName },
|
|
68
|
-
data: {
|
|
69
|
-
agent: pending.agentName,
|
|
70
|
-
result: wasDenied ? "Action denied by user." : (resumedOutput || "Task completed."),
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
yield* managerRuntime.run({
|
|
74
|
-
type: "action:result",
|
|
75
|
-
data: {
|
|
76
|
-
action: "delegateTask",
|
|
77
|
-
result: delegateResult,
|
|
78
|
-
toolCallId: pending.toolCallId,
|
|
79
|
-
success: !wasDenied,
|
|
80
|
-
halt: wasDenied,
|
|
81
|
-
},
|
|
82
|
-
}, {
|
|
83
|
-
runId: context.runId,
|
|
84
|
-
state: state,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
yield* managerRuntime.run(event, {
|
|
92
|
-
runId: context.runId,
|
|
93
|
-
state: state,
|
|
94
|
-
});
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
// 2. Direct agent routing for user input (e.g. "@os list files" or "@Codex Agent list files")
|
|
98
|
-
if (event.type === "agent:input") {
|
|
99
|
-
const content = event.data.content;
|
|
100
|
-
if (content?.trim().startsWith("@")) {
|
|
101
|
-
const trimmedContent = content.trim();
|
|
102
|
-
const afterAt = trimmedContent.slice(1);
|
|
103
|
-
// Find the longest matching agent (by ID or Name) at the start of the message
|
|
104
|
-
// This handles agent names with spaces like "Codex Agent"
|
|
105
|
-
let bestMatch;
|
|
106
|
-
for (const agent of allAgents) {
|
|
107
|
-
const idMatches = afterAt.toLowerCase().startsWith(agent.id.toLowerCase());
|
|
108
|
-
const nameMatches = afterAt.toLowerCase().startsWith(agent.name.toLowerCase());
|
|
109
|
-
if (idMatches || nameMatches) {
|
|
110
|
-
const matchPrefix = idMatches ? agent.id : agent.name;
|
|
111
|
-
const prefixLength = matchPrefix.length;
|
|
112
|
-
// Next char must be space, end of string, or the match length must be at least
|
|
113
|
-
// the current best match length (prefer longer names like "Codex Agent" over "Codex")
|
|
114
|
-
const nextChar = afterAt[prefixLength];
|
|
115
|
-
if (!nextChar || nextChar === " ") {
|
|
116
|
-
if (!bestMatch || prefixLength > bestMatch.prefixLength) {
|
|
117
|
-
bestMatch = { id: agent.id, name: agent.name, prefixLength };
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (bestMatch) {
|
|
123
|
-
const targetAgent = bestMatch.id;
|
|
124
|
-
const remaining = afterAt.slice(bestMatch.prefixLength).trim();
|
|
125
|
-
const runtime = agentRuntimes.get(targetAgent);
|
|
126
|
-
if (runtime) {
|
|
127
|
-
if (!state.agentStates[targetAgent])
|
|
128
|
-
state.agentStates[targetAgent] = {};
|
|
129
|
-
const agentEvent = {
|
|
130
|
-
...event,
|
|
131
|
-
data: {
|
|
132
|
-
content: remaining || "Hello",
|
|
133
|
-
attachments: event.data.attachments,
|
|
134
|
-
},
|
|
135
|
-
};
|
|
136
|
-
let lastAgentOutput = "";
|
|
137
|
-
for await (const agentChunk of runtime.run(agentEvent, {
|
|
138
|
-
runId: context.runId,
|
|
139
|
-
state: state.agentStates[targetAgent],
|
|
140
|
-
})) {
|
|
141
|
-
if (agentChunk.type === "agent:output" || agentChunk.type === "agent:output-delta") {
|
|
142
|
-
const summary = summarizeAgentEventValue(agentChunk);
|
|
143
|
-
if (summary)
|
|
144
|
-
lastAgentOutput = summary;
|
|
145
|
-
}
|
|
146
|
-
yield {
|
|
147
|
-
...agentChunk,
|
|
148
|
-
meta: {
|
|
149
|
-
...agentChunk?.meta,
|
|
150
|
-
agentName: targetAgent,
|
|
151
|
-
},
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
// Direct "@agent" routing bypasses manager handlers entirely.
|
|
155
|
-
// Trigger manager-side post-processing (e.g. topic/title generation)
|
|
156
|
-
// without producing a manager reply.
|
|
157
|
-
if (!state.title) {
|
|
158
|
-
for await (const _ of managerRuntime.run({
|
|
159
|
-
type: "agent:output",
|
|
160
|
-
meta: { agentName: targetAgent },
|
|
161
|
-
data: { content: lastAgentOutput || "" },
|
|
162
|
-
}, {
|
|
163
|
-
runId: context.runId,
|
|
164
|
-
state: state,
|
|
165
|
-
})) {
|
|
166
|
-
// side-effects only
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
// If the user used @ but the agent wasn't found, stop here to avoid
|
|
174
|
-
// falling back to the manager and burning tokens for a failed routing attempt.
|
|
175
|
-
const agentPrefixMatch = afterAt.split(" ")[0];
|
|
176
|
-
yield {
|
|
177
|
-
type: "agent:output",
|
|
178
|
-
data: {
|
|
179
|
-
content: `Agent "@${agentPrefixMatch}" not found. Available agents:\n${allAgents.map(a => `- ${a.name} (@${a.id})`).join("\n")}`
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
// 3. Default routing: translate user input to manager input
|
|
187
|
-
yield* managerRuntime.run({ ...event, type: "agent:input" }, {
|
|
188
|
-
runId: context.runId,
|
|
189
|
-
state: state,
|
|
190
|
-
});
|
|
191
|
-
}
|
package/dist/handlers/init.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { sidebarOnlyUI, tabOnlyUI } from "../ui/layout.js";
|
|
2
|
-
/**
|
|
3
|
-
* Initial application layout handler
|
|
4
|
-
*/
|
|
5
|
-
export async function* initHandler(event, { state }) {
|
|
6
|
-
// Initialize path state if not already present
|
|
7
|
-
if (!state.cwd) {
|
|
8
|
-
state.cwd = process.cwd();
|
|
9
|
-
}
|
|
10
|
-
if (!state.workspaceRoot) {
|
|
11
|
-
state.workspaceRoot = process.cwd();
|
|
12
|
-
}
|
|
13
|
-
const tab = event.data?.tab || "chat";
|
|
14
|
-
// For init, return both sidebar and content
|
|
15
|
-
yield {
|
|
16
|
-
type: "ui",
|
|
17
|
-
meta: {
|
|
18
|
-
type: "sidebar",
|
|
19
|
-
},
|
|
20
|
-
data: await sidebarOnlyUI({ sessionId: state.sessionId })
|
|
21
|
-
};
|
|
22
|
-
yield {
|
|
23
|
-
type: "ui",
|
|
24
|
-
meta: {
|
|
25
|
-
type: "content",
|
|
26
|
-
},
|
|
27
|
-
data: await tabOnlyUI({ tab })
|
|
28
|
-
};
|
|
29
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { sidebarOnlyUI, tabOnlyUI } from "../ui/layout.js";
|
|
2
|
-
/**
|
|
3
|
-
* Session change handler
|
|
4
|
-
*/
|
|
5
|
-
export async function* sessionChangeHandler(event, { state }) {
|
|
6
|
-
const tab = event.data?.tab || "chat";
|
|
7
|
-
yield {
|
|
8
|
-
type: "ui",
|
|
9
|
-
meta: {
|
|
10
|
-
type: "sidebar",
|
|
11
|
-
},
|
|
12
|
-
data: await sidebarOnlyUI({ sessionId: state.sessionId })
|
|
13
|
-
};
|
|
14
|
-
yield {
|
|
15
|
-
type: "ui",
|
|
16
|
-
meta: {
|
|
17
|
-
type: "content",
|
|
18
|
-
},
|
|
19
|
-
data: await tabOnlyUI({ tab })
|
|
20
|
-
};
|
|
21
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { saveConfig } from "../config.js";
|
|
2
|
-
import { tabOnlyUI } from "../ui/layout.js";
|
|
3
|
-
import { exec } from "node:child_process";
|
|
4
|
-
import os from "node:os";
|
|
5
|
-
/**
|
|
6
|
-
* Handle settings updates (like API keys)
|
|
7
|
-
*/
|
|
8
|
-
export async function* updateSettingsHandler(event, { state }) {
|
|
9
|
-
const { openai_api_key, anthropic_api_key, model } = event.data;
|
|
10
|
-
const updates = {};
|
|
11
|
-
if (model) {
|
|
12
|
-
updates.model = model.trim();
|
|
13
|
-
}
|
|
14
|
-
if (openai_api_key && openai_api_key !== "••••••••••••••••") {
|
|
15
|
-
updates.openaiApiKey = openai_api_key.trim();
|
|
16
|
-
}
|
|
17
|
-
if (anthropic_api_key && anthropic_api_key !== "••••••••••••••••") {
|
|
18
|
-
updates.anthropicApiKey = anthropic_api_key.trim();
|
|
19
|
-
}
|
|
20
|
-
if (Object.keys(updates).length > 0) {
|
|
21
|
-
saveConfig(updates);
|
|
22
|
-
// Refresh the settings UI to show the "saved" state (masking the keys)
|
|
23
|
-
yield {
|
|
24
|
-
type: "ui",
|
|
25
|
-
meta: {
|
|
26
|
-
type: "content",
|
|
27
|
-
},
|
|
28
|
-
data: await tabOnlyUI({ tab: "settings" })
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Handle opening an agent's folder in the native file explorer
|
|
34
|
-
*/
|
|
35
|
-
export async function* openAgentFolderHandler(event) {
|
|
36
|
-
const { folder } = event.data;
|
|
37
|
-
if (folder) {
|
|
38
|
-
const command = os.platform() === "win32" ? `explorer "${folder}"` :
|
|
39
|
-
os.platform() === "darwin" ? `open "${folder}"` :
|
|
40
|
-
`xdg-open "${folder}"`;
|
|
41
|
-
exec(command, (error) => {
|
|
42
|
-
if (error) {
|
|
43
|
-
console.error(`Failed to open folder: ${error.message}`);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { tabOnlyUI } from "../ui/layout.js";
|
|
2
|
-
/**
|
|
3
|
-
* Tab change handler
|
|
4
|
-
*/
|
|
5
|
-
export async function* tabChangeHandler(event, { state }) {
|
|
6
|
-
const tab = event.data?.tab || "chat";
|
|
7
|
-
yield {
|
|
8
|
-
type: "ui",
|
|
9
|
-
meta: {
|
|
10
|
-
type: "content",
|
|
11
|
-
},
|
|
12
|
-
data: await tabOnlyUI({ tab })
|
|
13
|
-
};
|
|
14
|
-
}
|
package/dist/installers.js
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
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
|
-
}
|