openbot 0.3.3 → 0.3.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/app/cli.js +1 -1
- package/dist/app/server.js +39 -21
- package/dist/assets/icon.svg +4 -0
- package/dist/bus/services.js +6 -5
- package/dist/harness/dispatcher.js +267 -0
- package/dist/harness/orchestrator.js +76 -0
- package/dist/harness/queue-processor.js +26 -0
- package/dist/services/storage.js +39 -5
- package/docs/architecture.md +5 -8
- package/package.json +7 -7
- package/src/app/cli.ts +1 -1
- package/src/app/server.ts +49 -27
- package/src/app/types.ts +37 -0
- package/src/bus/services.ts +6 -5
- package/src/bus/types.ts +4 -0
- package/src/harness/dispatcher.ts +379 -0
- package/src/services/storage.ts +41 -4
- package/src/harness/agent-harness.ts +0 -58
- package/src/harness/event-normalizer.ts +0 -82
- package/src/harness/orchestrator.ts +0 -104
- package/src/harness/queue-processor.ts +0 -220
- package/src/harness/types.ts +0 -34
package/dist/services/storage.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { DEFAULT_PLUGINS_DIR, DEFAULT_AGENTS_DIR, DEFAULT_BASE_DIR, DEFAULT_CHANNELS_DIR, loadConfig, resolvePath, VARIABLES_FILE, } from '../app/config.js';
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
3
4
|
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
6
|
import crypto from 'node:crypto';
|
|
5
7
|
import matter from 'gray-matter';
|
|
6
8
|
import { aiSdkPlugin } from '../plugins/ai-sdk/index.js';
|
|
@@ -8,13 +10,31 @@ import { AI_SDK_SYSTEM_PROMPT } from '../plugins/ai-sdk/system-prompt.js';
|
|
|
8
10
|
import { listBuiltInPlugins, parsePluginModule } from '../registry/plugins.js';
|
|
9
11
|
import { processService } from '../harness/process.js';
|
|
10
12
|
import { memoryService } from './memory.js';
|
|
11
|
-
import { pathToFileURL } from 'node:url';
|
|
12
13
|
const resolveBaseDir = () => {
|
|
13
14
|
const config = loadConfig();
|
|
14
15
|
return resolvePath(config.baseDir || DEFAULT_BASE_DIR);
|
|
15
16
|
};
|
|
16
17
|
const ENTITY_SVG_CANDIDATE_NAMES = ['avatar.svg', 'icon.svg', 'image.svg', 'logo.svg'];
|
|
17
18
|
const toSvgDataUrl = (svg) => `data:image/svg+xml;base64,${Buffer.from(svg, 'utf-8').toString('base64')}`;
|
|
19
|
+
let bundledSystemAgentImage;
|
|
20
|
+
let bundledSystemAgentImageLoaded = false;
|
|
21
|
+
/** OpenBot mark from `src/assets/icon.svg` (also copied to `dist/assets` at build). */
|
|
22
|
+
function getBundledSystemAgentImage() {
|
|
23
|
+
if (bundledSystemAgentImageLoaded)
|
|
24
|
+
return bundledSystemAgentImage;
|
|
25
|
+
bundledSystemAgentImageLoaded = true;
|
|
26
|
+
try {
|
|
27
|
+
const iconPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '../assets/icon.svg');
|
|
28
|
+
const trimmed = readFileSync(iconPath, 'utf-8').trim();
|
|
29
|
+
if (!trimmed.startsWith('<svg'))
|
|
30
|
+
return undefined;
|
|
31
|
+
bundledSystemAgentImage = toSvgDataUrl(trimmed);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
bundledSystemAgentImage = undefined;
|
|
35
|
+
}
|
|
36
|
+
return bundledSystemAgentImage;
|
|
37
|
+
}
|
|
18
38
|
const tryReadSvgDataUrl = async (filePath) => {
|
|
19
39
|
try {
|
|
20
40
|
const svg = await fs.readFile(filePath, 'utf-8');
|
|
@@ -72,7 +92,7 @@ function getSystemAgentDetails(overrides) {
|
|
|
72
92
|
const defaults = {
|
|
73
93
|
id: SYSTEM_AGENT_ID,
|
|
74
94
|
name: 'OpenBot',
|
|
75
|
-
image:
|
|
95
|
+
image: getBundledSystemAgentImage(),
|
|
76
96
|
description: 'First-party orchestration agent for OpenBot. Coordinates other agents via handoff.',
|
|
77
97
|
instructions: AI_SDK_SYSTEM_PROMPT,
|
|
78
98
|
plugins: SYSTEM_DEFAULT_PLUGINS.map((ref) => ref.id),
|
|
@@ -599,6 +619,9 @@ export const storageService = {
|
|
|
599
619
|
const discoveredImage = await resolveEntityImageDataUrl(agentDir);
|
|
600
620
|
const stats = await fs.stat(agentMdPath);
|
|
601
621
|
const pluginRefs = parsePluginRefs(data.plugins);
|
|
622
|
+
const frontmatterImage = typeof data.image === 'string' && data.image.trim() !== ''
|
|
623
|
+
? data.image.trim()
|
|
624
|
+
: undefined;
|
|
602
625
|
diskDetails = {
|
|
603
626
|
id: agentId,
|
|
604
627
|
name: typeof data.name === 'string' ? data.name : agentId,
|
|
@@ -606,7 +629,7 @@ export const storageService = {
|
|
|
606
629
|
plugins: pluginRefs.map((ref) => ref.id),
|
|
607
630
|
pluginRefs,
|
|
608
631
|
description: typeof data.description === 'string' ? data.description : '',
|
|
609
|
-
image: discoveredImage || undefined,
|
|
632
|
+
image: frontmatterImage || discoveredImage || undefined,
|
|
610
633
|
createdAt: stats.birthtime,
|
|
611
634
|
updatedAt: stats.mtime,
|
|
612
635
|
};
|
|
@@ -630,7 +653,7 @@ export const storageService = {
|
|
|
630
653
|
}
|
|
631
654
|
return diskDetails;
|
|
632
655
|
},
|
|
633
|
-
createAgent: async ({ agentId, name, description = '', instructions, plugins, }) => {
|
|
656
|
+
createAgent: async ({ agentId, name, description = '', image, instructions, plugins, }) => {
|
|
634
657
|
assertValidDiskAgentId(agentId);
|
|
635
658
|
const agentDir = resolvePath(path.join(getAgentsRootDir(), agentId));
|
|
636
659
|
const agentMdPath = path.join(agentDir, 'AGENT.md');
|
|
@@ -656,10 +679,13 @@ export const storageService = {
|
|
|
656
679
|
description,
|
|
657
680
|
plugins: serializePluginRefs(plugins),
|
|
658
681
|
};
|
|
682
|
+
if (typeof image === 'string' && image.trim() !== '') {
|
|
683
|
+
data.image = image.trim();
|
|
684
|
+
}
|
|
659
685
|
const body = matter.stringify(`${instructions.trim()}\n`, data);
|
|
660
686
|
await fs.writeFile(agentMdPath, body, 'utf-8');
|
|
661
687
|
},
|
|
662
|
-
updateAgent: async ({ agentId, name, description, instructions, plugins, }) => {
|
|
688
|
+
updateAgent: async ({ agentId, name, description, image, instructions, plugins, }) => {
|
|
663
689
|
assertValidDiskAgentId(agentId);
|
|
664
690
|
const agentDir = resolvePath(path.join(getAgentsRootDir(), agentId));
|
|
665
691
|
const agentMdPath = path.join(agentDir, 'AGENT.md');
|
|
@@ -683,6 +709,14 @@ export const storageService = {
|
|
|
683
709
|
nextData.description = description;
|
|
684
710
|
if (plugins !== undefined)
|
|
685
711
|
nextData.plugins = serializePluginRefs(plugins);
|
|
712
|
+
if (image !== undefined) {
|
|
713
|
+
if (typeof image === 'string' && image.trim() !== '') {
|
|
714
|
+
nextData.image = image.trim();
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
delete nextData.image;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
686
720
|
const nextContent = instructions !== undefined ? instructions : parsed.content;
|
|
687
721
|
const body = matter.stringify(`${String(nextContent).trim()}\n`, nextData);
|
|
688
722
|
await fs.writeFile(agentMdPath, body, 'utf-8');
|
package/docs/architecture.md
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
# Architecture
|
|
2
2
|
|
|
3
|
-
OpenBot is an orchestration platform built on a modular, event-driven architecture. It leverages the `melony` framework to coordinate interactions between multiple specialized agents
|
|
3
|
+
OpenBot is an orchestration platform built on a modular, event-driven architecture. It leverages the `melony` framework to coordinate interactions between multiple specialized agents through a central **orchestrator** (HTTP handlers call it directly).
|
|
4
4
|
|
|
5
5
|
## Core Components
|
|
6
6
|
|
|
7
|
-
### 1.
|
|
8
|
-
The
|
|
9
|
-
|
|
10
|
-
### 2. Agent Orchestration & Routing
|
|
11
|
-
The orchestrator is the central dispatcher within the harness. It receives user input and determines how to delegate tasks across the agent network using the following logic:
|
|
7
|
+
### 1. Orchestrator & routing
|
|
8
|
+
The orchestrator is the execution entry point for agent work: it normalizes incoming events, runs the queue processor (handoffs and todo-driven assignees), builds per-agent Melony runtimes, and streams emitted events back to callers (for example storage and SSE). Routing across the agent network uses:
|
|
12
9
|
|
|
13
10
|
1. **Command Prefix** — Explicit delegation to a specific agent (e.g., `/os list files`).
|
|
14
11
|
2. **DM context** — Direct communication with a specific agent.
|
|
@@ -20,10 +17,10 @@ A dynamic registry that manages all available agents. Agents can be:
|
|
|
20
17
|
- **YAML-based**: Rapidly defined agents in `~/.openbot/agents/*/AGENT.md`.
|
|
21
18
|
- **TS Packages**: Advanced agents with custom logic in `~/.openbot/agents/*/index.ts`.
|
|
22
19
|
|
|
23
|
-
### 3. Plugin
|
|
20
|
+
### 3. Plugin registry
|
|
24
21
|
The "capability layer" that provides tools and logic shared across the platform. Plugins (like `shell`, `file-system`, or `mcp`) define the actions agents can perform.
|
|
25
22
|
|
|
26
|
-
### 4. Orchestration
|
|
23
|
+
### 4. Orchestration layer (Melony)
|
|
27
24
|
The underlying event bus that handles all communication. It ensures that agents can collaborate asynchronously, share context, and emit real-time updates to the UI.
|
|
28
25
|
|
|
29
26
|
## Multi-Agent Workflow
|
package/package.json
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openbot",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.12.0"
|
|
8
8
|
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"dev": "tsx watch src/app/cli.ts start",
|
|
11
|
-
"build": "tsc",
|
|
12
|
-
"start": "node dist/app/cli.js start"
|
|
13
|
-
},
|
|
14
9
|
"bin": {
|
|
15
10
|
"openbot": "./dist/app/cli.js"
|
|
16
11
|
},
|
|
@@ -36,5 +31,10 @@
|
|
|
36
31
|
"@types/node": "^20.10.1",
|
|
37
32
|
"tsx": "^4.21.0",
|
|
38
33
|
"typescript": "^5.9.3"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"dev": "tsx watch src/app/cli.ts start",
|
|
37
|
+
"build": "tsc && mkdir -p dist/assets && cp src/assets/icon.svg dist/assets/icon.svg",
|
|
38
|
+
"start": "node dist/app/cli.js start"
|
|
39
39
|
}
|
|
40
|
-
}
|
|
40
|
+
}
|
package/src/app/cli.ts
CHANGED
package/src/app/server.ts
CHANGED
|
@@ -12,10 +12,12 @@ import { DEFAULT_BASE_DIR, loadConfig, resolvePath } from '../app/config.js';
|
|
|
12
12
|
import { ActiveRunsSnapshotEvent, OpenBotEvent, OpenBotState } from './types.js';
|
|
13
13
|
import { processService } from '../harness/process.js';
|
|
14
14
|
import { storageService } from '../services/storage.js';
|
|
15
|
-
import {
|
|
15
|
+
import { dispatch } from '../harness/dispatcher.js';
|
|
16
16
|
import { initPlugins } from '../registry/plugins.js';
|
|
17
17
|
import { ensureEventId, openBotEventFromQuery } from './utils.js';
|
|
18
18
|
|
|
19
|
+
type Bucket = { channelId: string; threadId?: string; activeCount: number; agentIds: Set<string> };
|
|
20
|
+
|
|
19
21
|
export interface ServerOptions {
|
|
20
22
|
port?: number;
|
|
21
23
|
}
|
|
@@ -94,25 +96,38 @@ export async function startServer(options: ServerOptions = {}) {
|
|
|
94
96
|
};
|
|
95
97
|
|
|
96
98
|
const buildActiveRunsSnapshot = (): ActiveRunsSnapshotEvent => {
|
|
97
|
-
const
|
|
99
|
+
const byBucket = new Map<string, Bucket>();
|
|
98
100
|
for (const run of activeRuns.values()) {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
const threadId = run.threadId || undefined;
|
|
102
|
+
const key = JSON.stringify([run.channelId, threadId ?? null]);
|
|
103
|
+
let bucket = byBucket.get(key);
|
|
104
|
+
if (!bucket) {
|
|
105
|
+
bucket = { channelId: run.channelId, threadId, activeCount: 0, agentIds: new Set<string>() };
|
|
106
|
+
byBucket.set(key, bucket);
|
|
107
|
+
}
|
|
108
|
+
bucket.activeCount += 1;
|
|
109
|
+
bucket.agentIds.add(run.agentId);
|
|
106
110
|
}
|
|
111
|
+
const channels = Array.from(byBucket.values())
|
|
112
|
+
.sort((a, b) => {
|
|
113
|
+
const c = a.channelId.localeCompare(b.channelId);
|
|
114
|
+
if (c !== 0) return c;
|
|
115
|
+
return (a.threadId ?? '').localeCompare(b.threadId ?? '');
|
|
116
|
+
})
|
|
117
|
+
.map(({ channelId, threadId, activeCount, agentIds }) => {
|
|
118
|
+
const row: ActiveRunsSnapshotEvent['data']['channels'][number] = {
|
|
119
|
+
channelId,
|
|
120
|
+
activeCount,
|
|
121
|
+
agentIds: Array.from(agentIds),
|
|
122
|
+
};
|
|
123
|
+
if (threadId !== undefined) {
|
|
124
|
+
row.threadId = threadId;
|
|
125
|
+
}
|
|
126
|
+
return row;
|
|
127
|
+
});
|
|
107
128
|
return {
|
|
108
129
|
type: 'agent:active-runs:snapshot',
|
|
109
|
-
data: {
|
|
110
|
-
channels: Array.from(byChannel.entries()).map(([channelId, value]) => ({
|
|
111
|
-
channelId,
|
|
112
|
-
activeCount: value.activeCount,
|
|
113
|
-
agentIds: Array.from(value.agentIds),
|
|
114
|
-
})),
|
|
115
|
-
},
|
|
130
|
+
data: { channels },
|
|
116
131
|
};
|
|
117
132
|
};
|
|
118
133
|
|
|
@@ -149,6 +164,7 @@ export async function startServer(options: ServerOptions = {}) {
|
|
|
149
164
|
|
|
150
165
|
if (channelId === GLOBAL_CHANNEL_ID) {
|
|
151
166
|
const snapshot = buildActiveRunsSnapshot();
|
|
167
|
+
|
|
152
168
|
ensureEventId(snapshot);
|
|
153
169
|
res.write(`data: ${JSON.stringify(snapshot)}\n\n`);
|
|
154
170
|
}
|
|
@@ -206,10 +222,10 @@ export async function startServer(options: ServerOptions = {}) {
|
|
|
206
222
|
activeRuns.set(
|
|
207
223
|
getRunKey(chunk.data.runId, chunk.data.agentId, chunk.data.channelId, chunk.data.threadId),
|
|
208
224
|
{
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
225
|
+
runId: chunk.data.runId,
|
|
226
|
+
channelId: chunk.data.channelId,
|
|
227
|
+
threadId: chunk.data.threadId,
|
|
228
|
+
agentId: chunk.data.agentId,
|
|
213
229
|
},
|
|
214
230
|
);
|
|
215
231
|
} else if (chunk.type === 'agent:run:end') {
|
|
@@ -226,21 +242,26 @@ export async function startServer(options: ServerOptions = {}) {
|
|
|
226
242
|
|
|
227
243
|
sendToClientKey(targetClientKey, chunk);
|
|
228
244
|
|
|
229
|
-
if (
|
|
245
|
+
if (
|
|
246
|
+
chunk.type === 'agent:run:start' ||
|
|
247
|
+
chunk.type === 'agent:run:end' ||
|
|
248
|
+
chunk.type === 'agent:run:stopped'
|
|
249
|
+
) {
|
|
230
250
|
sendToClientKey(GLOBAL_CHANNEL_ID, chunk);
|
|
231
251
|
}
|
|
232
252
|
};
|
|
233
253
|
|
|
234
254
|
try {
|
|
235
|
-
|
|
255
|
+
ensureEventId(event);
|
|
256
|
+
|
|
257
|
+
await dispatch({
|
|
236
258
|
runId,
|
|
237
259
|
agentId: agentId || 'system',
|
|
260
|
+
event,
|
|
238
261
|
channelId,
|
|
239
262
|
threadId,
|
|
240
263
|
onEvent,
|
|
241
264
|
});
|
|
242
|
-
|
|
243
|
-
await harness.dispatch(event);
|
|
244
265
|
res.sendStatus(200);
|
|
245
266
|
} catch (error) {
|
|
246
267
|
console.error('[publish] Failed to dispatch event', {
|
|
@@ -272,15 +293,16 @@ export async function startServer(options: ServerOptions = {}) {
|
|
|
272
293
|
};
|
|
273
294
|
|
|
274
295
|
try {
|
|
275
|
-
|
|
296
|
+
ensureEventId(event);
|
|
297
|
+
|
|
298
|
+
await dispatch({
|
|
276
299
|
runId,
|
|
277
300
|
agentId: agentId || 'system',
|
|
301
|
+
event,
|
|
278
302
|
channelId,
|
|
279
303
|
threadId,
|
|
280
304
|
onEvent,
|
|
281
305
|
});
|
|
282
|
-
|
|
283
|
-
await harness.dispatch(event);
|
|
284
306
|
res.json({ events });
|
|
285
307
|
} catch (error) {
|
|
286
308
|
res.status(500).json({ error: 'Failed to process state request' });
|
package/src/app/types.ts
CHANGED
|
@@ -143,6 +143,7 @@ export type CreateAgentEvent = BaseEvent & {
|
|
|
143
143
|
agentId: string;
|
|
144
144
|
name: string;
|
|
145
145
|
description?: string;
|
|
146
|
+
image?: string;
|
|
146
147
|
instructions: string;
|
|
147
148
|
plugins: PluginRef[];
|
|
148
149
|
};
|
|
@@ -162,6 +163,7 @@ export type UpdateAgentEvent = BaseEvent & {
|
|
|
162
163
|
agentId: string;
|
|
163
164
|
name?: string;
|
|
164
165
|
description?: string;
|
|
166
|
+
image?: string;
|
|
165
167
|
instructions?: string;
|
|
166
168
|
plugins?: PluginRef[];
|
|
167
169
|
};
|
|
@@ -365,17 +367,48 @@ export type AgentRunEndEvent = BaseEvent & {
|
|
|
365
367
|
};
|
|
366
368
|
};
|
|
367
369
|
|
|
370
|
+
export type AgentRunStoppedEvent = BaseEvent & {
|
|
371
|
+
type: 'agent:run:stopped';
|
|
372
|
+
data: {
|
|
373
|
+
runId: string;
|
|
374
|
+
agentId: string;
|
|
375
|
+
channelId: string;
|
|
376
|
+
threadId?: string;
|
|
377
|
+
reason?: string;
|
|
378
|
+
};
|
|
379
|
+
};
|
|
380
|
+
|
|
368
381
|
export type ActiveRunsSnapshotEvent = BaseEvent & {
|
|
369
382
|
type: 'agent:active-runs:snapshot';
|
|
370
383
|
data: {
|
|
371
384
|
channels: Array<{
|
|
372
385
|
channelId: string;
|
|
386
|
+
threadId?: string;
|
|
373
387
|
activeCount: number;
|
|
374
388
|
agentIds: string[];
|
|
375
389
|
}>;
|
|
376
390
|
};
|
|
377
391
|
};
|
|
378
392
|
|
|
393
|
+
export type StopAgentRunEvent = BaseEvent & {
|
|
394
|
+
type: 'action:agent_run_stop';
|
|
395
|
+
data: {
|
|
396
|
+
runId: string;
|
|
397
|
+
agentId?: string;
|
|
398
|
+
channelId?: string;
|
|
399
|
+
threadId?: string;
|
|
400
|
+
reason?: string;
|
|
401
|
+
};
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
export type StopAgentRunResultEvent = BaseEvent & {
|
|
405
|
+
type: 'action:agent_run_stop:result';
|
|
406
|
+
data: {
|
|
407
|
+
success: boolean;
|
|
408
|
+
message?: string;
|
|
409
|
+
};
|
|
410
|
+
};
|
|
411
|
+
|
|
379
412
|
export type CreateThreadEvent = BaseEvent & {
|
|
380
413
|
type: 'action:create_thread';
|
|
381
414
|
data: {
|
|
@@ -714,6 +747,7 @@ export type InstallAgentEvent = BaseEvent & {
|
|
|
714
747
|
agentId: string;
|
|
715
748
|
name: string;
|
|
716
749
|
description?: string;
|
|
750
|
+
image?: string;
|
|
717
751
|
instructions: string;
|
|
718
752
|
plugins: PluginRef[];
|
|
719
753
|
};
|
|
@@ -860,7 +894,10 @@ export type OpenBotEvent =
|
|
|
860
894
|
| AgentOutputEvent
|
|
861
895
|
| AgentRunStartEvent
|
|
862
896
|
| AgentRunEndEvent
|
|
897
|
+
| AgentRunStoppedEvent
|
|
863
898
|
| ActiveRunsSnapshotEvent
|
|
899
|
+
| StopAgentRunEvent
|
|
900
|
+
| StopAgentRunResultEvent
|
|
864
901
|
| GetChannelsEvent
|
|
865
902
|
| GetChannelsResultEvent
|
|
866
903
|
| GetThreadsEvent
|
package/src/bus/services.ts
CHANGED
|
@@ -550,8 +550,8 @@ export const busServicesPlugin =
|
|
|
550
550
|
|
|
551
551
|
builder.on('action:storage:create-agent', async function* (event) {
|
|
552
552
|
try {
|
|
553
|
-
const { agentId, name, description, instructions, plugins } = event.data;
|
|
554
|
-
await storage.createAgent({ agentId, name, description, instructions, plugins });
|
|
553
|
+
const { agentId, name, description, image, instructions, plugins } = event.data;
|
|
554
|
+
await storage.createAgent({ agentId, name, description, image, instructions, plugins });
|
|
555
555
|
yield { type: 'action:storage:create-agent-result', data: { success: true } };
|
|
556
556
|
} catch (error) {
|
|
557
557
|
yield {
|
|
@@ -566,8 +566,8 @@ export const busServicesPlugin =
|
|
|
566
566
|
|
|
567
567
|
builder.on('action:storage:update-agent', async function* (event) {
|
|
568
568
|
try {
|
|
569
|
-
const { agentId, name, description, instructions, plugins } = event.data;
|
|
570
|
-
await storage.updateAgent({ agentId, name, description, instructions, plugins });
|
|
569
|
+
const { agentId, name, description, image, instructions, plugins } = event.data;
|
|
570
|
+
await storage.updateAgent({ agentId, name, description, image, instructions, plugins });
|
|
571
571
|
yield { type: 'action:storage:update-agent-result', data: { success: true } };
|
|
572
572
|
} catch (error) {
|
|
573
573
|
yield {
|
|
@@ -858,7 +858,7 @@ export const busServicesPlugin =
|
|
|
858
858
|
|
|
859
859
|
builder.on('action:agent:install', async function* (event) {
|
|
860
860
|
try {
|
|
861
|
-
const { agentId, name, description, instructions, plugins } = event.data;
|
|
861
|
+
const { agentId, name, description, image, instructions, plugins } = event.data;
|
|
862
862
|
|
|
863
863
|
// Ensure each plugin is available locally. Built-in ids resolve
|
|
864
864
|
// immediately; npm-name ids are fetched on demand.
|
|
@@ -881,6 +881,7 @@ export const busServicesPlugin =
|
|
|
881
881
|
agentId,
|
|
882
882
|
name,
|
|
883
883
|
description,
|
|
884
|
+
image,
|
|
884
885
|
instructions,
|
|
885
886
|
plugins,
|
|
886
887
|
});
|
package/src/bus/types.ts
CHANGED
|
@@ -113,6 +113,8 @@ export interface Storage {
|
|
|
113
113
|
agentId: string;
|
|
114
114
|
name: string;
|
|
115
115
|
description?: string;
|
|
116
|
+
/** Avatar/logo URL or data URI; persisted in AGENT.md frontmatter. */
|
|
117
|
+
image?: string;
|
|
116
118
|
instructions: string;
|
|
117
119
|
plugins: PluginRef[];
|
|
118
120
|
}) => Promise<void>;
|
|
@@ -120,6 +122,8 @@ export interface Storage {
|
|
|
120
122
|
agentId: string;
|
|
121
123
|
name?: string;
|
|
122
124
|
description?: string;
|
|
125
|
+
/** Omit to leave unchanged; empty string removes stored image. */
|
|
126
|
+
image?: string;
|
|
123
127
|
instructions?: string;
|
|
124
128
|
plugins?: PluginRef[];
|
|
125
129
|
}) => Promise<void>;
|