openbot 0.2.14 → 0.3.1
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/agents/openbot/index.js +76 -0
- package/dist/agents/openbot/middleware/approval.js +132 -0
- package/dist/agents/openbot/runtime.js +289 -0
- package/dist/agents/openbot/system-prompt.js +32 -0
- package/dist/agents/openbot/tools/delegation.js +78 -0
- package/dist/agents/openbot/tools/mcp.js +99 -0
- package/dist/agents/openbot/tools/shell.js +91 -0
- package/dist/agents/openbot/tools/storage.js +75 -0
- package/dist/agents/openbot/tools/ui.js +176 -0
- package/dist/agents/system.js +20 -93
- package/dist/app/cli.js +1 -1
- package/dist/app/config.js +4 -1
- package/dist/app/server.js +15 -8
- package/dist/bus/agent-package.js +1 -0
- package/dist/bus/plugin.js +1 -0
- package/dist/bus/services.js +711 -0
- package/dist/bus/types.js +1 -0
- package/dist/harness/context.js +250 -0
- package/dist/harness/event-normalizer.js +59 -0
- package/dist/harness/orchestrator.js +27 -227
- package/dist/harness/process.js +25 -3
- package/dist/harness/queue-processor.js +227 -0
- package/dist/harness/runtime-factory.js +103 -0
- package/dist/plugins/ai-sdk/index.js +37 -0
- package/dist/plugins/ai-sdk/runtime.js +402 -0
- package/dist/plugins/ai-sdk/system-prompt.js +3 -0
- package/dist/plugins/ai-sdk.js +277 -87
- package/dist/plugins/approval/index.js +159 -0
- package/dist/plugins/approval.js +163 -0
- package/dist/plugins/delegation/index.js +79 -0
- package/dist/plugins/delegation.js +67 -11
- package/dist/plugins/mcp/index.js +108 -0
- package/dist/plugins/memory/index.js +71 -0
- package/dist/plugins/shell/index.js +99 -0
- package/dist/plugins/shell.js +123 -0
- package/dist/plugins/storage-tools/index.js +85 -0
- package/dist/plugins/storage.js +240 -5
- package/dist/plugins/ui/index.js +184 -0
- package/dist/plugins/ui.js +185 -21
- package/dist/registry/agents.js +138 -0
- package/dist/registry/plugins.js +93 -50
- package/dist/services/agent-packages.js +103 -0
- package/dist/services/memory.js +152 -0
- package/dist/services/plugins.js +98 -0
- package/dist/services/storage.js +366 -94
- package/docs/agents.md +52 -65
- package/docs/architecture.md +1 -1
- package/docs/plugins.md +70 -58
- package/docs/templates/AGENT.example.md +57 -0
- package/package.json +8 -7
- package/src/app/cli.ts +1 -1
- package/src/app/config.ts +14 -4
- package/src/app/server.ts +23 -10
- package/src/app/types.ts +445 -16
- package/src/assets/icon.svg +4 -1
- package/src/bus/plugin.ts +67 -0
- package/src/bus/services.ts +786 -0
- package/src/bus/types.ts +160 -0
- package/src/harness/context.ts +293 -0
- package/src/harness/event-normalizer.ts +82 -0
- package/src/harness/orchestrator.ts +35 -273
- package/src/harness/process.ts +28 -4
- package/src/harness/queue-processor.ts +309 -0
- package/src/harness/runtime-factory.ts +125 -0
- package/src/plugins/ai-sdk/index.ts +44 -0
- package/src/plugins/ai-sdk/runtime.ts +484 -0
- package/src/plugins/ai-sdk/system-prompt.ts +4 -0
- package/src/plugins/approval/index.ts +228 -0
- package/src/plugins/delegation/index.ts +94 -0
- package/src/plugins/mcp/index.ts +128 -0
- package/src/plugins/memory/index.ts +85 -0
- package/src/plugins/shell/index.ts +123 -0
- package/src/plugins/storage-tools/index.ts +101 -0
- package/src/plugins/ui/index.ts +227 -0
- package/src/registry/plugins.ts +108 -55
- package/src/services/memory.ts +213 -0
- package/src/services/plugins.ts +133 -0
- package/src/services/storage.ts +472 -137
- package/src/agents/system.ts +0 -112
- package/src/plugins/ai-sdk.ts +0 -197
- package/src/plugins/delegation.ts +0 -60
- package/src/plugins/mcp.ts +0 -154
- package/src/plugins/storage.ts +0 -725
- package/src/plugins/ui.ts +0 -57
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
import { DEFAULT_MARKETPLACE_REGISTRY_URL, loadConfig } from '../app/config.js';
|
|
2
|
+
import { storageService } from '../services/storage.js';
|
|
3
|
+
import { pluginService } from '../services/plugins.js';
|
|
4
|
+
/**
|
|
5
|
+
* Resolve a scope alias to a concrete scope string. Aliases let tools accept
|
|
6
|
+
* `agent`/`channel`/`global` without knowing the active ids; the bus rewrites
|
|
7
|
+
* them using `context.state`.
|
|
8
|
+
*/
|
|
9
|
+
function resolveMemoryScope(alias, state) {
|
|
10
|
+
switch (alias) {
|
|
11
|
+
case 'agent':
|
|
12
|
+
return `agent:${state.agentId}`;
|
|
13
|
+
case 'channel':
|
|
14
|
+
return `channel:${state.channelId}`;
|
|
15
|
+
case 'global':
|
|
16
|
+
case undefined:
|
|
17
|
+
return 'global';
|
|
18
|
+
default:
|
|
19
|
+
return 'global';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function resolveMemoryScopeFilter(alias, state) {
|
|
23
|
+
if (alias === 'all' || alias === undefined) {
|
|
24
|
+
return ['global', `agent:${state.agentId}`, `channel:${state.channelId}`];
|
|
25
|
+
}
|
|
26
|
+
return [resolveMemoryScope(alias, state)];
|
|
27
|
+
}
|
|
28
|
+
const DEFAULT_MARKETPLACE_AGENTS = [
|
|
29
|
+
{
|
|
30
|
+
id: 'researcher',
|
|
31
|
+
name: 'Researcher',
|
|
32
|
+
description: 'Specialized in web research and information synthesis.',
|
|
33
|
+
instructions: 'You are a research assistant. Use available tools to find information.',
|
|
34
|
+
plugins: [
|
|
35
|
+
{ id: 'ai-sdk', config: { model: 'openai/gpt-4o' } },
|
|
36
|
+
{ id: 'mcp' },
|
|
37
|
+
{ id: 'shell' },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 'coder',
|
|
42
|
+
name: 'Coder',
|
|
43
|
+
description: 'Expert in multiple programming languages and software architecture.',
|
|
44
|
+
instructions: 'You are an expert software engineer. Help the user with coding tasks.',
|
|
45
|
+
plugins: [{ id: 'claude-code' }],
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
function isRecord(value) {
|
|
49
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Parses JSON from a remote registry file. Supports either
|
|
53
|
+
* `{ "agents": [ ... ] }` or a top-level array.
|
|
54
|
+
*/
|
|
55
|
+
export function parseMarketplaceRegistryJson(data) {
|
|
56
|
+
const rawAgents = Array.isArray(data) ? data : isRecord(data) && Array.isArray(data.agents) ? data.agents : null;
|
|
57
|
+
if (!Array.isArray(rawAgents)) {
|
|
58
|
+
throw new Error('Registry JSON must be an array or an object with an "agents" array');
|
|
59
|
+
}
|
|
60
|
+
return rawAgents.map((item, i) => {
|
|
61
|
+
if (!isRecord(item)) {
|
|
62
|
+
throw new Error(`agents[${i}]: expected object`);
|
|
63
|
+
}
|
|
64
|
+
const id = item.id;
|
|
65
|
+
const name = item.name;
|
|
66
|
+
const description = item.description;
|
|
67
|
+
const instructions = item.instructions;
|
|
68
|
+
const pluginsRaw = item.plugins;
|
|
69
|
+
if (typeof id !== 'string' || !id)
|
|
70
|
+
throw new Error(`agents[${i}].id must be a non-empty string`);
|
|
71
|
+
if (typeof name !== 'string')
|
|
72
|
+
throw new Error(`agents[${i}].name must be a string`);
|
|
73
|
+
if (typeof description !== 'string')
|
|
74
|
+
throw new Error(`agents[${i}].description must be a string`);
|
|
75
|
+
if (typeof instructions !== 'string') {
|
|
76
|
+
throw new Error(`agents[${i}].instructions must be a string`);
|
|
77
|
+
}
|
|
78
|
+
if (!Array.isArray(pluginsRaw))
|
|
79
|
+
throw new Error(`agents[${i}].plugins must be an array`);
|
|
80
|
+
const plugins = pluginsRaw.map((p, j) => {
|
|
81
|
+
if (!isRecord(p) || typeof p.id !== 'string' || !p.id) {
|
|
82
|
+
throw new Error(`agents[${i}].plugins[${j}]: expected { "id": string, "config"?: object }`);
|
|
83
|
+
}
|
|
84
|
+
const ref = { id: p.id };
|
|
85
|
+
if (p.config !== undefined) {
|
|
86
|
+
if (!isRecord(p.config))
|
|
87
|
+
throw new Error(`agents[${i}].plugins[${j}].config must be an object`);
|
|
88
|
+
ref.config = p.config;
|
|
89
|
+
}
|
|
90
|
+
return ref;
|
|
91
|
+
});
|
|
92
|
+
const listing = { id, name, description, instructions, plugins };
|
|
93
|
+
if (item.image !== undefined) {
|
|
94
|
+
if (typeof item.image !== 'string')
|
|
95
|
+
throw new Error(`agents[${i}].image must be a string`);
|
|
96
|
+
listing.image = item.image;
|
|
97
|
+
}
|
|
98
|
+
return listing;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
async function fetchMarketplaceAgentsFromUrl(url) {
|
|
102
|
+
const res = await fetch(url, {
|
|
103
|
+
headers: { Accept: 'application/json' },
|
|
104
|
+
signal: AbortSignal.timeout(15000),
|
|
105
|
+
});
|
|
106
|
+
if (!res.ok) {
|
|
107
|
+
throw new Error(`Registry HTTP ${res.status} ${res.statusText}`);
|
|
108
|
+
}
|
|
109
|
+
const json = await res.json();
|
|
110
|
+
return parseMarketplaceRegistryJson(json);
|
|
111
|
+
}
|
|
112
|
+
export const busServicesPlugin = (options) => (builder) => {
|
|
113
|
+
const { storage } = options;
|
|
114
|
+
builder.on('action:create_thread', async function* (event, context) {
|
|
115
|
+
const threadId = event.meta?.threadId;
|
|
116
|
+
const channelId = context.state.channelId;
|
|
117
|
+
const { threadTitle, spec, initialState } = event.data;
|
|
118
|
+
if (!threadId) {
|
|
119
|
+
console.warn('[bus] Cannot create thread: meta.threadId is missing');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
context.state.threadId = threadId;
|
|
123
|
+
if (channelId) {
|
|
124
|
+
try {
|
|
125
|
+
await storage.createThread({
|
|
126
|
+
channelId,
|
|
127
|
+
threadId,
|
|
128
|
+
threadTitle,
|
|
129
|
+
spec,
|
|
130
|
+
initialState: initialState || {},
|
|
131
|
+
});
|
|
132
|
+
context.state.threadDetails = await storage.getThreadDetails({
|
|
133
|
+
channelId,
|
|
134
|
+
threadId,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
console.warn(`[bus] Failed to initialize thread for channel ${channelId} thread ${threadId}`, error);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
yield {
|
|
142
|
+
type: 'action:create_thread:result',
|
|
143
|
+
data: { success: true, threadId, threadTitle },
|
|
144
|
+
meta: { ...(event.meta || {}), threadId, agentId: context.state.agentId },
|
|
145
|
+
};
|
|
146
|
+
});
|
|
147
|
+
builder.on('action:create_channel', async function* (event, context) {
|
|
148
|
+
const { channelId, spec, initialState, cwd } = event.data;
|
|
149
|
+
const rawChannelId = (channelId || '').trim();
|
|
150
|
+
const channelSpec = typeof spec === 'string' ? spec : '';
|
|
151
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
152
|
+
if (!rawChannelId) {
|
|
153
|
+
yield {
|
|
154
|
+
type: 'action:create_channel:result',
|
|
155
|
+
data: { success: false, channelId: '', channelUrl: '' },
|
|
156
|
+
meta: resultMeta,
|
|
157
|
+
};
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const channelUrl = `/channels/${rawChannelId}`;
|
|
161
|
+
try {
|
|
162
|
+
await storage.createChannel({
|
|
163
|
+
channelId: rawChannelId,
|
|
164
|
+
spec: channelSpec,
|
|
165
|
+
initialState: initialState,
|
|
166
|
+
cwd,
|
|
167
|
+
});
|
|
168
|
+
yield {
|
|
169
|
+
type: 'action:create_channel:result',
|
|
170
|
+
data: { success: true, channelId: rawChannelId, channelUrl },
|
|
171
|
+
meta: resultMeta,
|
|
172
|
+
};
|
|
173
|
+
yield {
|
|
174
|
+
type: 'agent:output',
|
|
175
|
+
data: { content: `Created channel \`${rawChannelId}\`.` },
|
|
176
|
+
meta: resultMeta,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
yield {
|
|
181
|
+
type: 'action:create_channel:result',
|
|
182
|
+
data: { success: false, channelId: rawChannelId, channelUrl },
|
|
183
|
+
meta: resultMeta,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
builder.on('action:update_channel', async function* (event, context) {
|
|
188
|
+
const data = (event.data || {});
|
|
189
|
+
const targetChannelId = (data.channelId || context.state.channelId || '').trim();
|
|
190
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
191
|
+
if (!targetChannelId) {
|
|
192
|
+
yield {
|
|
193
|
+
type: 'action:update_channel:result',
|
|
194
|
+
data: { success: false, channelId: '', updatedFields: [] },
|
|
195
|
+
meta: resultMeta,
|
|
196
|
+
};
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const patch = {};
|
|
200
|
+
const updatedFields = [];
|
|
201
|
+
if (typeof data.name === 'string' && data.name.trim()) {
|
|
202
|
+
patch.name = data.name.trim();
|
|
203
|
+
updatedFields.push('name');
|
|
204
|
+
}
|
|
205
|
+
if (typeof data.cwd === 'string' && data.cwd.trim()) {
|
|
206
|
+
patch.cwd = data.cwd.trim();
|
|
207
|
+
updatedFields.push('cwd');
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
if (updatedFields.length > 0) {
|
|
211
|
+
await storage.patchChannelState({ channelId: targetChannelId, state: patch });
|
|
212
|
+
}
|
|
213
|
+
if (targetChannelId === context.state.channelId) {
|
|
214
|
+
context.state.channelDetails = await storage.getChannelDetails({
|
|
215
|
+
channelId: context.state.channelId,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
yield {
|
|
219
|
+
type: 'action:update_channel:result',
|
|
220
|
+
data: { success: true, channelId: targetChannelId, updatedFields },
|
|
221
|
+
meta: resultMeta,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
yield {
|
|
226
|
+
type: 'action:update_channel:result',
|
|
227
|
+
data: { success: false, channelId: targetChannelId, updatedFields },
|
|
228
|
+
meta: resultMeta,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
builder.on('action:patch_channel_details', async function* (event, context) {
|
|
233
|
+
const updatedFields = [];
|
|
234
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
235
|
+
try {
|
|
236
|
+
if (event.data.state !== undefined) {
|
|
237
|
+
await storage.patchChannelState({
|
|
238
|
+
channelId: context.state.channelId,
|
|
239
|
+
state: event.data.state,
|
|
240
|
+
});
|
|
241
|
+
updatedFields.push('state');
|
|
242
|
+
}
|
|
243
|
+
if (typeof event.data.spec === 'string') {
|
|
244
|
+
await storage.patchChannelSpec({
|
|
245
|
+
channelId: context.state.channelId,
|
|
246
|
+
spec: event.data.spec,
|
|
247
|
+
});
|
|
248
|
+
updatedFields.push('spec');
|
|
249
|
+
}
|
|
250
|
+
if (typeof event.data.cwd === 'string') {
|
|
251
|
+
await storage.patchChannelState({
|
|
252
|
+
channelId: context.state.channelId,
|
|
253
|
+
state: { cwd: event.data.cwd },
|
|
254
|
+
});
|
|
255
|
+
updatedFields.push('cwd');
|
|
256
|
+
}
|
|
257
|
+
context.state.channelDetails = await storage.getChannelDetails({
|
|
258
|
+
channelId: context.state.channelId,
|
|
259
|
+
});
|
|
260
|
+
yield {
|
|
261
|
+
type: 'action:patch_channel_details:result',
|
|
262
|
+
data: { success: true, updatedFields },
|
|
263
|
+
meta: resultMeta,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
yield {
|
|
268
|
+
type: 'action:patch_channel_details:result',
|
|
269
|
+
data: { success: false, updatedFields },
|
|
270
|
+
meta: resultMeta,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
builder.on('action:patch_thread_details', async function* (event, context) {
|
|
275
|
+
const updatedFields = [];
|
|
276
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
277
|
+
try {
|
|
278
|
+
if (!context.state.threadId) {
|
|
279
|
+
throw new Error('Missing threadId in state for patch_thread_details');
|
|
280
|
+
}
|
|
281
|
+
if (event.data.state !== undefined) {
|
|
282
|
+
await storage.patchThreadState({
|
|
283
|
+
channelId: context.state.channelId,
|
|
284
|
+
threadId: context.state.threadId,
|
|
285
|
+
state: event.data.state,
|
|
286
|
+
});
|
|
287
|
+
updatedFields.push('state');
|
|
288
|
+
}
|
|
289
|
+
if (typeof event.data.spec === 'string') {
|
|
290
|
+
await storage.patchThreadSpec({
|
|
291
|
+
channelId: context.state.channelId,
|
|
292
|
+
threadId: context.state.threadId,
|
|
293
|
+
spec: event.data.spec,
|
|
294
|
+
});
|
|
295
|
+
updatedFields.push('spec');
|
|
296
|
+
}
|
|
297
|
+
context.state.threadDetails = await storage.getThreadDetails({
|
|
298
|
+
channelId: context.state.channelId,
|
|
299
|
+
threadId: context.state.threadId,
|
|
300
|
+
});
|
|
301
|
+
yield {
|
|
302
|
+
type: 'action:patch_thread_details:result',
|
|
303
|
+
data: { success: true, updatedFields },
|
|
304
|
+
meta: resultMeta,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
yield {
|
|
309
|
+
type: 'action:patch_thread_details:result',
|
|
310
|
+
data: { success: false, updatedFields },
|
|
311
|
+
meta: resultMeta,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
builder.on('action:storage:get-channels', async function* () {
|
|
316
|
+
const channels = await storage.getChannels();
|
|
317
|
+
yield { type: 'action:storage:get-channels-result', data: { channels } };
|
|
318
|
+
});
|
|
319
|
+
builder.on('action:storage:get-threads', async function* (event) {
|
|
320
|
+
const threads = await storage.getThreads({ channelId: event.data.channelId });
|
|
321
|
+
yield { type: 'action:storage:get-threads-result', data: { threads } };
|
|
322
|
+
});
|
|
323
|
+
builder.on('action:storage:get-channel-details', async function* (_, state) {
|
|
324
|
+
const channelDetails = await storage.getChannelDetails({
|
|
325
|
+
channelId: state.state.channelId,
|
|
326
|
+
});
|
|
327
|
+
yield { type: 'action:storage:get-channel-details-result', data: { channelDetails } };
|
|
328
|
+
});
|
|
329
|
+
builder.on('action:storage:get-thread-details', async function* (_, state) {
|
|
330
|
+
const threadId = state.state.threadId;
|
|
331
|
+
const threadDetails = threadId
|
|
332
|
+
? await storage.getThreadDetails({ channelId: state.state.channelId, threadId })
|
|
333
|
+
: null;
|
|
334
|
+
yield { type: 'action:storage:get-thread-details-result', data: { threadDetails } };
|
|
335
|
+
});
|
|
336
|
+
builder.on('action:storage:get-agents', async function* () {
|
|
337
|
+
const agents = await storage.getAgents();
|
|
338
|
+
yield { type: 'action:storage:get-agents-result', data: { agents } };
|
|
339
|
+
});
|
|
340
|
+
builder.on('action:storage:get-plugins', async function* () {
|
|
341
|
+
const plugins = await storage.getPlugins();
|
|
342
|
+
yield { type: 'action:storage:get-plugins-result', data: { plugins } };
|
|
343
|
+
});
|
|
344
|
+
builder.on('action:storage:get-agent-details', async function* (event) {
|
|
345
|
+
try {
|
|
346
|
+
const agentDetails = await storage.getAgentDetails({ agentId: event.data.agentId });
|
|
347
|
+
yield { type: 'action:storage:get-agent-details-result', data: { agentDetails } };
|
|
348
|
+
}
|
|
349
|
+
catch (error) {
|
|
350
|
+
console.error(`[bus] Failed to get agent details for ${event.data.agentId}`, error);
|
|
351
|
+
yield {
|
|
352
|
+
type: 'action:storage:get-agent-details-result',
|
|
353
|
+
data: {
|
|
354
|
+
agentDetails: null,
|
|
355
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
builder.on('action:storage:create-agent', async function* (event) {
|
|
361
|
+
try {
|
|
362
|
+
const { agentId, name, description, instructions, plugins } = event.data;
|
|
363
|
+
await storage.createAgent({ agentId, name, description, instructions, plugins });
|
|
364
|
+
yield { type: 'action:storage:create-agent-result', data: { success: true } };
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
yield {
|
|
368
|
+
type: 'action:storage:create-agent-result',
|
|
369
|
+
data: {
|
|
370
|
+
success: false,
|
|
371
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
372
|
+
},
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
builder.on('action:storage:update-agent', async function* (event) {
|
|
377
|
+
try {
|
|
378
|
+
const { agentId, name, description, instructions, plugins } = event.data;
|
|
379
|
+
await storage.updateAgent({ agentId, name, description, instructions, plugins });
|
|
380
|
+
yield { type: 'action:storage:update-agent-result', data: { success: true } };
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
yield {
|
|
384
|
+
type: 'action:storage:update-agent-result',
|
|
385
|
+
data: {
|
|
386
|
+
success: false,
|
|
387
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
388
|
+
},
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
builder.on('action:storage:delete-agent', async function* (event) {
|
|
393
|
+
try {
|
|
394
|
+
await storage.deleteAgent({ agentId: event.data.agentId });
|
|
395
|
+
yield { type: 'action:storage:delete-agent-result', data: { success: true } };
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
yield {
|
|
399
|
+
type: 'action:storage:delete-agent-result',
|
|
400
|
+
data: {
|
|
401
|
+
success: false,
|
|
402
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
builder.on('action:storage:get-events', async function* (_, state) {
|
|
408
|
+
const events = await storage.getEvents(state.state);
|
|
409
|
+
if (!state.state.threadId && events.length > 0) {
|
|
410
|
+
const lastId = events[events.length - 1]?.id;
|
|
411
|
+
if (lastId) {
|
|
412
|
+
await storageService.setLastReadForChannel({
|
|
413
|
+
channelId: state.state.channelId,
|
|
414
|
+
lastReadEventId: lastId,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
yield { type: 'action:storage:get-events-result', data: { events } };
|
|
419
|
+
});
|
|
420
|
+
builder.on('action:storage:get-variables', async function* () {
|
|
421
|
+
const variables = await storage.getVariables();
|
|
422
|
+
const masked = {};
|
|
423
|
+
for (const [key, val] of Object.entries(variables)) {
|
|
424
|
+
if (typeof val === 'object' && val !== null && val.secret) {
|
|
425
|
+
masked[key] = '********';
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
masked[key] = typeof val === 'string' ? val : val.value;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
yield {
|
|
432
|
+
type: 'action:storage:get-variables-result',
|
|
433
|
+
data: { variables: masked },
|
|
434
|
+
};
|
|
435
|
+
});
|
|
436
|
+
builder.on('action:storage:create-variable', async function* (event) {
|
|
437
|
+
try {
|
|
438
|
+
const { key, value, secret } = event.data;
|
|
439
|
+
await storage.createVariable({ key, value, secret });
|
|
440
|
+
yield { type: 'action:storage:create-variable-result', data: { success: true } };
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
yield {
|
|
444
|
+
type: 'action:storage:create-variable-result',
|
|
445
|
+
data: {
|
|
446
|
+
success: false,
|
|
447
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
448
|
+
},
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
builder.on('action:storage:delete-variable', async function* (event) {
|
|
453
|
+
try {
|
|
454
|
+
await storage.deleteVariable({ key: event.data.key });
|
|
455
|
+
yield { type: 'action:storage:delete-variable-result', data: { success: true } };
|
|
456
|
+
}
|
|
457
|
+
catch (error) {
|
|
458
|
+
yield {
|
|
459
|
+
type: 'action:storage:delete-variable-result',
|
|
460
|
+
data: {
|
|
461
|
+
success: false,
|
|
462
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
463
|
+
},
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
builder.on('action:storage:patch-channel-state', async function* (event, state) {
|
|
468
|
+
try {
|
|
469
|
+
await storage.patchChannelState({
|
|
470
|
+
channelId: state.state.channelId,
|
|
471
|
+
state: event.data.state,
|
|
472
|
+
});
|
|
473
|
+
yield { type: 'action:storage:patch-channel-state-result', data: { success: true } };
|
|
474
|
+
}
|
|
475
|
+
catch {
|
|
476
|
+
yield { type: 'action:storage:patch-channel-state-result', data: { success: false } };
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
builder.on('action:storage:patch-thread-state', async function* (event, state) {
|
|
480
|
+
try {
|
|
481
|
+
if (!state.state.threadId) {
|
|
482
|
+
throw new Error('Missing threadId in state for patch-thread-state');
|
|
483
|
+
}
|
|
484
|
+
await storage.patchThreadState({
|
|
485
|
+
channelId: state.state.channelId,
|
|
486
|
+
threadId: state.state.threadId,
|
|
487
|
+
state: event.data.state,
|
|
488
|
+
});
|
|
489
|
+
yield { type: 'action:storage:patch-thread-state-result', data: { success: true } };
|
|
490
|
+
}
|
|
491
|
+
catch {
|
|
492
|
+
yield { type: 'action:storage:patch-thread-state-result', data: { success: false } };
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
builder.on('action:storage:list-files', async function* (event, context) {
|
|
496
|
+
const channelId = context.state.channelId;
|
|
497
|
+
const subPath = event.data?.path || '';
|
|
498
|
+
try {
|
|
499
|
+
const files = await storage.listFiles({ channelId, path: subPath });
|
|
500
|
+
yield {
|
|
501
|
+
type: 'action:storage:list-files:result',
|
|
502
|
+
data: { success: true, files },
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
yield {
|
|
507
|
+
type: 'action:storage:list-files:result',
|
|
508
|
+
data: {
|
|
509
|
+
success: false,
|
|
510
|
+
files: [],
|
|
511
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
512
|
+
},
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
builder.on('action:storage:read-file', async function* (event, context) {
|
|
517
|
+
const channelId = context.state.channelId;
|
|
518
|
+
const filePath = event.data?.path;
|
|
519
|
+
if (!filePath) {
|
|
520
|
+
yield {
|
|
521
|
+
type: 'action:storage:read-file:result',
|
|
522
|
+
data: { success: false, path: '', error: 'Path is required' },
|
|
523
|
+
};
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
try {
|
|
527
|
+
const content = await storage.readFile({ channelId, path: filePath });
|
|
528
|
+
yield {
|
|
529
|
+
type: 'action:storage:read-file:result',
|
|
530
|
+
data: { success: true, content, path: filePath },
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
yield {
|
|
535
|
+
type: 'action:storage:read-file:result',
|
|
536
|
+
data: {
|
|
537
|
+
success: false,
|
|
538
|
+
path: filePath,
|
|
539
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
540
|
+
},
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
builder.on('action:plugin:install', async function* (event) {
|
|
545
|
+
try {
|
|
546
|
+
const { name, version } = event.data;
|
|
547
|
+
const result = await pluginService.install({ packageName: name, version });
|
|
548
|
+
yield {
|
|
549
|
+
type: 'action:plugin:install:result',
|
|
550
|
+
data: { success: true, plugin: result },
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
catch (error) {
|
|
554
|
+
yield {
|
|
555
|
+
type: 'action:plugin:install:result',
|
|
556
|
+
data: { success: false, error: error.message },
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
builder.on('action:plugin:uninstall', async function* (event) {
|
|
561
|
+
try {
|
|
562
|
+
await pluginService.uninstall(event.data.id);
|
|
563
|
+
yield { type: 'action:plugin:uninstall:result', data: { success: true } };
|
|
564
|
+
}
|
|
565
|
+
catch (error) {
|
|
566
|
+
yield {
|
|
567
|
+
type: 'action:plugin:uninstall:result',
|
|
568
|
+
data: { success: false, error: error.message },
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
builder.on('action:marketplace:list', async function* () {
|
|
573
|
+
const { marketplaceRegistryUrl } = loadConfig();
|
|
574
|
+
const registryUrl = marketplaceRegistryUrl?.trim() || DEFAULT_MARKETPLACE_REGISTRY_URL;
|
|
575
|
+
let agents = DEFAULT_MARKETPLACE_AGENTS;
|
|
576
|
+
try {
|
|
577
|
+
agents = await fetchMarketplaceAgentsFromUrl(registryUrl);
|
|
578
|
+
}
|
|
579
|
+
catch (err) {
|
|
580
|
+
console.warn(`[bus] marketplace registry fetch failed (${registryUrl}), using built-in list:`, err instanceof Error ? err.message : err);
|
|
581
|
+
}
|
|
582
|
+
yield {
|
|
583
|
+
type: 'action:marketplace:list:result',
|
|
584
|
+
data: { success: true, agents },
|
|
585
|
+
};
|
|
586
|
+
});
|
|
587
|
+
builder.on('action:remember', async function* (event, context) {
|
|
588
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
589
|
+
try {
|
|
590
|
+
const { content, scope, tags } = event.data;
|
|
591
|
+
const record = await storage.appendMemory({
|
|
592
|
+
scope: resolveMemoryScope(scope, context.state),
|
|
593
|
+
content,
|
|
594
|
+
tags,
|
|
595
|
+
});
|
|
596
|
+
yield {
|
|
597
|
+
type: 'action:remember:result',
|
|
598
|
+
data: { success: true, record },
|
|
599
|
+
meta: resultMeta,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
catch (error) {
|
|
603
|
+
yield {
|
|
604
|
+
type: 'action:remember:result',
|
|
605
|
+
data: {
|
|
606
|
+
success: false,
|
|
607
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
608
|
+
},
|
|
609
|
+
meta: resultMeta,
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
builder.on('action:recall', async function* (event, context) {
|
|
614
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
615
|
+
try {
|
|
616
|
+
const { query, tag, scope, limit } = event.data;
|
|
617
|
+
const records = await storage.listMemories({
|
|
618
|
+
scopes: resolveMemoryScopeFilter(scope, context.state),
|
|
619
|
+
query,
|
|
620
|
+
tag,
|
|
621
|
+
limit,
|
|
622
|
+
});
|
|
623
|
+
yield {
|
|
624
|
+
type: 'action:recall:result',
|
|
625
|
+
data: { success: true, records },
|
|
626
|
+
meta: resultMeta,
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
catch (error) {
|
|
630
|
+
yield {
|
|
631
|
+
type: 'action:recall:result',
|
|
632
|
+
data: {
|
|
633
|
+
success: false,
|
|
634
|
+
records: [],
|
|
635
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
636
|
+
},
|
|
637
|
+
meta: resultMeta,
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
builder.on('action:forget', async function* (event, context) {
|
|
642
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
643
|
+
try {
|
|
644
|
+
const deleted = await storage.deleteMemory({ id: event.data.id });
|
|
645
|
+
yield {
|
|
646
|
+
type: 'action:forget:result',
|
|
647
|
+
data: { success: true, deleted },
|
|
648
|
+
meta: resultMeta,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
catch (error) {
|
|
652
|
+
yield {
|
|
653
|
+
type: 'action:forget:result',
|
|
654
|
+
data: {
|
|
655
|
+
success: false,
|
|
656
|
+
deleted: false,
|
|
657
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
658
|
+
},
|
|
659
|
+
meta: resultMeta,
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
builder.on('action:agent:install', async function* (event) {
|
|
664
|
+
try {
|
|
665
|
+
const { agentId, name, description, instructions, plugins } = event.data;
|
|
666
|
+
// Ensure each plugin is available locally. Built-in ids resolve
|
|
667
|
+
// immediately; npm-name ids are fetched on demand.
|
|
668
|
+
for (const ref of plugins) {
|
|
669
|
+
const installed = await pluginService.isInstalled(ref.id);
|
|
670
|
+
if (!installed && ref.id.includes('/') === false && ref.id.includes('-plugin-') === false) {
|
|
671
|
+
// Treat ids without a hyphen+slash signature as built-ins; skip install.
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
if (!installed) {
|
|
675
|
+
try {
|
|
676
|
+
await pluginService.install({ packageName: ref.id });
|
|
677
|
+
}
|
|
678
|
+
catch (err) {
|
|
679
|
+
console.warn(`[bus] Failed to pre-install plugin ${ref.id}`, err);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
await storage.createAgent({
|
|
684
|
+
agentId,
|
|
685
|
+
name,
|
|
686
|
+
description,
|
|
687
|
+
instructions,
|
|
688
|
+
plugins,
|
|
689
|
+
});
|
|
690
|
+
yield {
|
|
691
|
+
type: 'action:agent:install:result',
|
|
692
|
+
data: { success: true, agentId },
|
|
693
|
+
};
|
|
694
|
+
yield {
|
|
695
|
+
type: 'agent:output',
|
|
696
|
+
data: { content: `Successfully installed agent **${name}** (${agentId}) from marketplace.` },
|
|
697
|
+
meta: { agentId: 'system' },
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
catch (error) {
|
|
701
|
+
yield {
|
|
702
|
+
type: 'action:agent:install:result',
|
|
703
|
+
data: {
|
|
704
|
+
success: false,
|
|
705
|
+
agentId: event.data.agentId,
|
|
706
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
707
|
+
},
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
});
|
|
711
|
+
};
|