openbot 0.3.0 → 0.3.2
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/app/cli.js +1 -1
- package/dist/app/server.js +1 -4
- package/dist/bus/services.js +222 -15
- package/dist/harness/context.js +205 -26
- package/dist/harness/queue-processor.js +44 -110
- package/dist/harness/runtime-factory.js +11 -7
- package/dist/harness/todo-advance.js +93 -0
- package/dist/plugins/ai-sdk/index.js +0 -3
- package/dist/plugins/ai-sdk/runtime.js +78 -13
- package/dist/plugins/ai-sdk/system-prompt.js +18 -3
- package/dist/plugins/delegation/index.js +7 -46
- package/dist/plugins/memory/index.js +71 -0
- package/dist/plugins/storage-tools/index.js +2 -11
- package/dist/plugins/todo/index.js +54 -0
- package/dist/plugins/workflow/index.js +65 -0
- package/dist/registry/plugins.js +4 -2
- package/dist/services/memory.js +152 -0
- package/dist/services/storage.js +9 -31
- package/dist/workflow/service.js +106 -0
- package/dist/workflow/types.js +3 -0
- package/docs/agents.md +15 -1
- package/docs/plugins.md +0 -1
- package/package.json +1 -1
- package/src/app/cli.ts +1 -1
- package/src/app/server.ts +3 -4
- package/src/app/types.ts +140 -45
- package/src/bus/plugin.ts +0 -2
- package/src/bus/services.ts +258 -17
- package/src/bus/types.ts +13 -4
- package/src/harness/context.ts +233 -37
- package/src/harness/queue-processor.ts +54 -143
- package/src/harness/runtime-factory.ts +11 -7
- package/src/harness/todo-advance.ts +128 -0
- package/src/plugins/ai-sdk/index.ts +0 -3
- package/src/plugins/ai-sdk/runtime.ts +356 -298
- package/src/plugins/ai-sdk/system-prompt.ts +18 -4
- package/src/plugins/delegation/index.ts +7 -50
- package/src/plugins/memory/index.ts +85 -0
- package/src/plugins/storage-tools/index.ts +8 -19
- package/src/plugins/todo/index.ts +64 -0
- package/src/registry/plugins.ts +4 -3
- package/src/services/memory.ts +213 -0
- package/src/services/storage.ts +9 -49
package/dist/app/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ function checkNodeVersion() {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
checkNodeVersion();
|
|
19
|
-
program.name('openbot').description('OpenBot CLI').version('0.
|
|
19
|
+
program.name('openbot').description('OpenBot CLI').version('0.3.1');
|
|
20
20
|
program
|
|
21
21
|
.command('start')
|
|
22
22
|
.description('Start the OpenBot harness')
|
package/dist/app/server.js
CHANGED
|
@@ -236,9 +236,6 @@ export async function startServer(options = {}) {
|
|
|
236
236
|
});
|
|
237
237
|
app.listen(PORT, () => {
|
|
238
238
|
console.log(`\x1b[32mOpenBot server listening at http://localhost:${PORT}\x1b[0m`);
|
|
239
|
-
console.log(
|
|
240
|
-
console.log(` - Events endpoint: GET /api/events (SSE)`);
|
|
241
|
-
console.log(` - Publish endpoint: POST /api/publish`);
|
|
242
|
-
console.log(` - State endpoint: GET /api/state`);
|
|
239
|
+
console.log(`🌐 Visit \x1b[96m\x1b[1mhttps://openbot.one\x1b[0m to connect to this runtime and manage everything from there. ✨`);
|
|
243
240
|
});
|
|
244
241
|
}
|
package/dist/bus/services.js
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
import { DEFAULT_MARKETPLACE_REGISTRY_URL, loadConfig } from '../app/config.js';
|
|
2
2
|
import { storageService } from '../services/storage.js';
|
|
3
3
|
import { pluginService } from '../services/plugins.js';
|
|
4
|
+
const readTodos = (state) => {
|
|
5
|
+
const raw = state.threadDetails?.state?.todos;
|
|
6
|
+
return Array.isArray(raw) ? raw : [];
|
|
7
|
+
};
|
|
8
|
+
let todoCounter = 0;
|
|
9
|
+
const newTodoId = (now, idx) => `todo_${now.toString(36)}_${(todoCounter++).toString(36)}_${idx}`;
|
|
10
|
+
async function persistTodos(storage, state, todos) {
|
|
11
|
+
if (!state.threadId)
|
|
12
|
+
throw new Error('No active thread');
|
|
13
|
+
await storage.patchThreadState({
|
|
14
|
+
channelId: state.channelId,
|
|
15
|
+
threadId: state.threadId,
|
|
16
|
+
state: { todos },
|
|
17
|
+
});
|
|
18
|
+
state.threadDetails = await storage.getThreadDetails({
|
|
19
|
+
channelId: state.channelId,
|
|
20
|
+
threadId: state.threadId,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolve a scope alias to a concrete scope string. Aliases let tools accept
|
|
25
|
+
* `agent`/`channel`/`global` without knowing the active ids; the bus rewrites
|
|
26
|
+
* them using `context.state`.
|
|
27
|
+
*/
|
|
28
|
+
function resolveMemoryScope(alias, state) {
|
|
29
|
+
switch (alias) {
|
|
30
|
+
case 'agent':
|
|
31
|
+
return `agent:${state.agentId}`;
|
|
32
|
+
case 'channel':
|
|
33
|
+
return `channel:${state.channelId}`;
|
|
34
|
+
case 'global':
|
|
35
|
+
case undefined:
|
|
36
|
+
return 'global';
|
|
37
|
+
default:
|
|
38
|
+
return 'global';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function resolveMemoryScopeFilter(alias, state) {
|
|
42
|
+
if (alias === 'all' || alias === undefined) {
|
|
43
|
+
return ['global', `agent:${state.agentId}`, `channel:${state.channelId}`];
|
|
44
|
+
}
|
|
45
|
+
return [resolveMemoryScope(alias, state)];
|
|
46
|
+
}
|
|
4
47
|
const DEFAULT_MARKETPLACE_AGENTS = [
|
|
5
48
|
{
|
|
6
49
|
id: 'researcher',
|
|
@@ -90,7 +133,7 @@ export const busServicesPlugin = (options) => (builder) => {
|
|
|
90
133
|
builder.on('action:create_thread', async function* (event, context) {
|
|
91
134
|
const threadId = event.meta?.threadId;
|
|
92
135
|
const channelId = context.state.channelId;
|
|
93
|
-
const { threadTitle,
|
|
136
|
+
const { threadTitle, initialState } = event.data;
|
|
94
137
|
if (!threadId) {
|
|
95
138
|
console.warn('[bus] Cannot create thread: meta.threadId is missing');
|
|
96
139
|
return;
|
|
@@ -102,7 +145,6 @@ export const busServicesPlugin = (options) => (builder) => {
|
|
|
102
145
|
channelId,
|
|
103
146
|
threadId,
|
|
104
147
|
threadTitle,
|
|
105
|
-
spec,
|
|
106
148
|
initialState: initialState || {},
|
|
107
149
|
});
|
|
108
150
|
context.state.threadDetails = await storage.getThreadDetails({
|
|
@@ -117,17 +159,19 @@ export const busServicesPlugin = (options) => (builder) => {
|
|
|
117
159
|
yield {
|
|
118
160
|
type: 'action:create_thread:result',
|
|
119
161
|
data: { success: true, threadId, threadTitle },
|
|
120
|
-
meta: { threadId },
|
|
162
|
+
meta: { ...(event.meta || {}), threadId, agentId: context.state.agentId },
|
|
121
163
|
};
|
|
122
164
|
});
|
|
123
165
|
builder.on('action:create_channel', async function* (event, context) {
|
|
124
166
|
const { channelId, spec, initialState, cwd } = event.data;
|
|
125
167
|
const rawChannelId = (channelId || '').trim();
|
|
126
168
|
const channelSpec = typeof spec === 'string' ? spec : '';
|
|
169
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
127
170
|
if (!rawChannelId) {
|
|
128
171
|
yield {
|
|
129
172
|
type: 'action:create_channel:result',
|
|
130
173
|
data: { success: false, channelId: '', channelUrl: '' },
|
|
174
|
+
meta: resultMeta,
|
|
131
175
|
};
|
|
132
176
|
return;
|
|
133
177
|
}
|
|
@@ -142,30 +186,31 @@ export const busServicesPlugin = (options) => (builder) => {
|
|
|
142
186
|
yield {
|
|
143
187
|
type: 'action:create_channel:result',
|
|
144
188
|
data: { success: true, channelId: rawChannelId, channelUrl },
|
|
189
|
+
meta: resultMeta,
|
|
145
190
|
};
|
|
146
191
|
yield {
|
|
147
192
|
type: 'agent:output',
|
|
148
193
|
data: { content: `Created channel \`${rawChannelId}\`.` },
|
|
149
|
-
meta:
|
|
150
|
-
...(event.meta || {}),
|
|
151
|
-
agentId: context.state.agentId,
|
|
152
|
-
},
|
|
194
|
+
meta: resultMeta,
|
|
153
195
|
};
|
|
154
196
|
}
|
|
155
197
|
catch {
|
|
156
198
|
yield {
|
|
157
199
|
type: 'action:create_channel:result',
|
|
158
200
|
data: { success: false, channelId: rawChannelId, channelUrl },
|
|
201
|
+
meta: resultMeta,
|
|
159
202
|
};
|
|
160
203
|
}
|
|
161
204
|
});
|
|
162
205
|
builder.on('action:update_channel', async function* (event, context) {
|
|
163
206
|
const data = (event.data || {});
|
|
164
207
|
const targetChannelId = (data.channelId || context.state.channelId || '').trim();
|
|
208
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
165
209
|
if (!targetChannelId) {
|
|
166
210
|
yield {
|
|
167
211
|
type: 'action:update_channel:result',
|
|
168
212
|
data: { success: false, channelId: '', updatedFields: [] },
|
|
213
|
+
meta: resultMeta,
|
|
169
214
|
};
|
|
170
215
|
return;
|
|
171
216
|
}
|
|
@@ -191,17 +236,20 @@ export const busServicesPlugin = (options) => (builder) => {
|
|
|
191
236
|
yield {
|
|
192
237
|
type: 'action:update_channel:result',
|
|
193
238
|
data: { success: true, channelId: targetChannelId, updatedFields },
|
|
239
|
+
meta: resultMeta,
|
|
194
240
|
};
|
|
195
241
|
}
|
|
196
242
|
catch {
|
|
197
243
|
yield {
|
|
198
244
|
type: 'action:update_channel:result',
|
|
199
245
|
data: { success: false, channelId: targetChannelId, updatedFields },
|
|
246
|
+
meta: resultMeta,
|
|
200
247
|
};
|
|
201
248
|
}
|
|
202
249
|
});
|
|
203
250
|
builder.on('action:patch_channel_details', async function* (event, context) {
|
|
204
251
|
const updatedFields = [];
|
|
252
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
205
253
|
try {
|
|
206
254
|
if (event.data.state !== undefined) {
|
|
207
255
|
await storage.patchChannelState({
|
|
@@ -230,17 +278,20 @@ export const busServicesPlugin = (options) => (builder) => {
|
|
|
230
278
|
yield {
|
|
231
279
|
type: 'action:patch_channel_details:result',
|
|
232
280
|
data: { success: true, updatedFields },
|
|
281
|
+
meta: resultMeta,
|
|
233
282
|
};
|
|
234
283
|
}
|
|
235
284
|
catch {
|
|
236
285
|
yield {
|
|
237
286
|
type: 'action:patch_channel_details:result',
|
|
238
287
|
data: { success: false, updatedFields },
|
|
288
|
+
meta: resultMeta,
|
|
239
289
|
};
|
|
240
290
|
}
|
|
241
291
|
});
|
|
242
292
|
builder.on('action:patch_thread_details', async function* (event, context) {
|
|
243
293
|
const updatedFields = [];
|
|
294
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
244
295
|
try {
|
|
245
296
|
if (!context.state.threadId) {
|
|
246
297
|
throw new Error('Missing threadId in state for patch_thread_details');
|
|
@@ -253,14 +304,6 @@ export const busServicesPlugin = (options) => (builder) => {
|
|
|
253
304
|
});
|
|
254
305
|
updatedFields.push('state');
|
|
255
306
|
}
|
|
256
|
-
if (typeof event.data.spec === 'string') {
|
|
257
|
-
await storage.patchThreadSpec({
|
|
258
|
-
channelId: context.state.channelId,
|
|
259
|
-
threadId: context.state.threadId,
|
|
260
|
-
spec: event.data.spec,
|
|
261
|
-
});
|
|
262
|
-
updatedFields.push('spec');
|
|
263
|
-
}
|
|
264
307
|
context.state.threadDetails = await storage.getThreadDetails({
|
|
265
308
|
channelId: context.state.channelId,
|
|
266
309
|
threadId: context.state.threadId,
|
|
@@ -268,12 +311,100 @@ export const busServicesPlugin = (options) => (builder) => {
|
|
|
268
311
|
yield {
|
|
269
312
|
type: 'action:patch_thread_details:result',
|
|
270
313
|
data: { success: true, updatedFields },
|
|
314
|
+
meta: resultMeta,
|
|
271
315
|
};
|
|
272
316
|
}
|
|
273
317
|
catch {
|
|
274
318
|
yield {
|
|
275
319
|
type: 'action:patch_thread_details:result',
|
|
276
320
|
data: { success: false, updatedFields },
|
|
321
|
+
meta: resultMeta,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
builder.on('action:todo_write', async function* (event, context) {
|
|
326
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
327
|
+
try {
|
|
328
|
+
if (!context.state.threadId) {
|
|
329
|
+
throw new Error('todo_write requires an active thread');
|
|
330
|
+
}
|
|
331
|
+
const existing = readTodos(context.state);
|
|
332
|
+
const byId = new Map(existing.map((t) => [t.id, t]));
|
|
333
|
+
const now = Date.now();
|
|
334
|
+
const author = context.state.agentId || 'system';
|
|
335
|
+
const inputs = event.data.todos || [];
|
|
336
|
+
const next = inputs.map((raw, idx) => {
|
|
337
|
+
const prior = raw.id ? byId.get(raw.id) : undefined;
|
|
338
|
+
return {
|
|
339
|
+
id: prior?.id || raw.id || newTodoId(now, idx),
|
|
340
|
+
content: raw.content,
|
|
341
|
+
status: raw.status || prior?.status || 'pending',
|
|
342
|
+
assignee: raw.assignee ?? prior?.assignee,
|
|
343
|
+
createdBy: prior?.createdBy || author,
|
|
344
|
+
createdAt: prior?.createdAt || now,
|
|
345
|
+
updatedAt: now,
|
|
346
|
+
...(prior?.result !== undefined ? { result: prior.result } : {}),
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
await persistTodos(storage, context.state, next);
|
|
350
|
+
yield {
|
|
351
|
+
type: 'action:todo_write:result',
|
|
352
|
+
data: { success: true, todos: next },
|
|
353
|
+
meta: resultMeta,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
yield {
|
|
358
|
+
type: 'action:todo_write:result',
|
|
359
|
+
data: {
|
|
360
|
+
success: false,
|
|
361
|
+
todos: readTodos(context.state),
|
|
362
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
363
|
+
},
|
|
364
|
+
meta: resultMeta,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
builder.on('action:todo_update', async function* (event, context) {
|
|
369
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
370
|
+
const patch = event.data;
|
|
371
|
+
try {
|
|
372
|
+
if (!context.state.threadId) {
|
|
373
|
+
throw new Error('todo_update requires an active thread');
|
|
374
|
+
}
|
|
375
|
+
const existing = readTodos(context.state);
|
|
376
|
+
const idx = existing.findIndex((t) => t.id === patch.id);
|
|
377
|
+
if (idx === -1) {
|
|
378
|
+
throw new Error(`Todo "${patch.id}" not found`);
|
|
379
|
+
}
|
|
380
|
+
const now = Date.now();
|
|
381
|
+
const updated = {
|
|
382
|
+
...existing[idx],
|
|
383
|
+
...(patch.content !== undefined ? { content: patch.content } : {}),
|
|
384
|
+
...(patch.status !== undefined ? { status: patch.status } : {}),
|
|
385
|
+
...(patch.assignee !== undefined
|
|
386
|
+
? { assignee: patch.assignee === '' ? undefined : patch.assignee }
|
|
387
|
+
: {}),
|
|
388
|
+
updatedAt: now,
|
|
389
|
+
};
|
|
390
|
+
const next = [...existing];
|
|
391
|
+
next[idx] = updated;
|
|
392
|
+
await persistTodos(storage, context.state, next);
|
|
393
|
+
yield {
|
|
394
|
+
type: 'action:todo_update:result',
|
|
395
|
+
data: { success: true, todo: updated, todos: next },
|
|
396
|
+
meta: resultMeta,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
yield {
|
|
401
|
+
type: 'action:todo_update:result',
|
|
402
|
+
data: {
|
|
403
|
+
success: false,
|
|
404
|
+
todos: readTodos(context.state),
|
|
405
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
406
|
+
},
|
|
407
|
+
meta: resultMeta,
|
|
277
408
|
};
|
|
278
409
|
}
|
|
279
410
|
});
|
|
@@ -549,6 +680,82 @@ export const busServicesPlugin = (options) => (builder) => {
|
|
|
549
680
|
data: { success: true, agents },
|
|
550
681
|
};
|
|
551
682
|
});
|
|
683
|
+
builder.on('action:remember', async function* (event, context) {
|
|
684
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
685
|
+
try {
|
|
686
|
+
const { content, scope, tags } = event.data;
|
|
687
|
+
const record = await storage.appendMemory({
|
|
688
|
+
scope: resolveMemoryScope(scope, context.state),
|
|
689
|
+
content,
|
|
690
|
+
tags,
|
|
691
|
+
});
|
|
692
|
+
yield {
|
|
693
|
+
type: 'action:remember:result',
|
|
694
|
+
data: { success: true, record },
|
|
695
|
+
meta: resultMeta,
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
catch (error) {
|
|
699
|
+
yield {
|
|
700
|
+
type: 'action:remember:result',
|
|
701
|
+
data: {
|
|
702
|
+
success: false,
|
|
703
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
704
|
+
},
|
|
705
|
+
meta: resultMeta,
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
builder.on('action:recall', async function* (event, context) {
|
|
710
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
711
|
+
try {
|
|
712
|
+
const { query, tag, scope, limit } = event.data;
|
|
713
|
+
const records = await storage.listMemories({
|
|
714
|
+
scopes: resolveMemoryScopeFilter(scope, context.state),
|
|
715
|
+
query,
|
|
716
|
+
tag,
|
|
717
|
+
limit,
|
|
718
|
+
});
|
|
719
|
+
yield {
|
|
720
|
+
type: 'action:recall:result',
|
|
721
|
+
data: { success: true, records },
|
|
722
|
+
meta: resultMeta,
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
catch (error) {
|
|
726
|
+
yield {
|
|
727
|
+
type: 'action:recall:result',
|
|
728
|
+
data: {
|
|
729
|
+
success: false,
|
|
730
|
+
records: [],
|
|
731
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
732
|
+
},
|
|
733
|
+
meta: resultMeta,
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
builder.on('action:forget', async function* (event, context) {
|
|
738
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
739
|
+
try {
|
|
740
|
+
const deleted = await storage.deleteMemory({ id: event.data.id });
|
|
741
|
+
yield {
|
|
742
|
+
type: 'action:forget:result',
|
|
743
|
+
data: { success: true, deleted },
|
|
744
|
+
meta: resultMeta,
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
catch (error) {
|
|
748
|
+
yield {
|
|
749
|
+
type: 'action:forget:result',
|
|
750
|
+
data: {
|
|
751
|
+
success: false,
|
|
752
|
+
deleted: false,
|
|
753
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
754
|
+
},
|
|
755
|
+
meta: resultMeta,
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
});
|
|
552
759
|
builder.on('action:agent:install', async function* (event) {
|
|
553
760
|
try {
|
|
554
761
|
const { agentId, name, description, instructions, plugins } = event.data;
|