openbot 0.4.7 → 0.5.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/.claude/agents/runtime-security-reviewer.md +32 -0
- package/.claude/hooks/format.sh +18 -0
- package/.claude/hooks/protect-secrets.sh +23 -0
- package/.claude/settings.json +26 -0
- package/.claude/skills/deploy/SKILL.md +26 -0
- package/.claude/skills/new-plugin/SKILL.md +46 -0
- package/.dockerignore +9 -0
- package/.mcp.json +8 -0
- package/AGENTS.md +2 -2
- package/CLAUDE.md +51 -0
- package/Dockerfile +41 -0
- package/deploy/README.md +59 -0
- package/deploy/entrypoint.sh +6 -0
- package/deploy/fly.toml +36 -0
- package/dist/app/bootstrap.js +29 -0
- package/dist/app/cli.js +5 -1
- package/dist/app/cloud-mode.js +15 -0
- package/dist/app/config.js +61 -16
- package/dist/app/ensure-default-stack.js +54 -0
- package/dist/app/openbot-plugin.js +4 -0
- package/dist/app/server.js +37 -6
- package/dist/harness/index.js +3 -0
- package/dist/plugins/approval/index.js +1 -1
- package/dist/plugins/bash/index.js +386 -164
- package/dist/plugins/memory/service.js +2 -5
- package/dist/plugins/openbot/context.js +17 -1
- package/dist/plugins/openbot/index.js +23 -7
- package/dist/plugins/openbot/model.js +20 -0
- package/dist/plugins/openbot/runtime.js +45 -57
- package/dist/plugins/openbot/system-prompt.js +43 -8
- package/dist/plugins/preview/index.js +267 -0
- package/dist/plugins/storage/index.js +3 -325
- package/dist/plugins/storage/service.js +61 -18
- package/dist/plugins/todo/index.js +131 -38
- package/dist/plugins/todo/service.js +93 -0
- package/dist/plugins/ui/index.js +8 -1
- package/dist/services/plugins/host.js +21 -0
- package/dist/services/plugins/model-registry.js +126 -0
- package/dist/services/plugins/registry.js +28 -42
- package/dist/services/plugins/service.js +2 -6
- package/docs/agents.md +20 -2
- package/docs/architecture.md +1 -1
- package/docs/plugins.md +13 -3
- package/docs/templates/AGENT.example.md +8 -14
- package/package.json +5 -2
- package/pnpm-workspace.yaml +2 -0
- package/src/app/bootstrap.ts +43 -0
- package/src/app/cli.ts +5 -1
- package/src/app/cloud-mode.ts +26 -0
- package/src/app/config.ts +71 -19
- package/src/app/ensure-default-stack.ts +63 -0
- package/src/app/openbot-plugin.ts +5 -0
- package/src/app/server.ts +42 -6
- package/src/app/types.ts +158 -32
- package/src/harness/index.ts +4 -0
- package/src/plugins/memory/service.ts +2 -5
- package/src/plugins/storage/index.ts +3 -371
- package/src/plugins/storage/service.ts +81 -22
- package/src/services/plugins/domain.ts +2 -0
- package/src/services/plugins/host.ts +36 -0
- package/src/services/plugins/model-registry.ts +178 -0
- package/src/services/plugins/registry.ts +30 -43
- package/src/services/plugins/service.ts +8 -17
- package/src/services/plugins/types.ts +36 -2
- package/src/services/todo/types.ts +12 -0
- package/dist/agents/openbot/index.js +0 -76
- package/dist/agents/openbot/middleware/approval.js +0 -132
- package/dist/agents/openbot/runtime.js +0 -289
- package/dist/agents/openbot/system-prompt.js +0 -32
- package/dist/agents/openbot/tools/delegation.js +0 -78
- package/dist/agents/openbot/tools/mcp.js +0 -99
- package/dist/agents/openbot/tools/shell.js +0 -91
- package/dist/agents/openbot/tools/storage.js +0 -75
- package/dist/agents/openbot/tools/ui.js +0 -176
- package/dist/agents/system.js +0 -33
- package/dist/bus/agent-package.js +0 -1
- package/dist/bus/plugin.js +0 -1
- package/dist/bus/services.js +0 -754
- package/dist/harness/agent-harness.js +0 -45
- package/dist/harness/agent-invoke-run.js +0 -44
- package/dist/harness/agent-turn.js +0 -99
- package/dist/harness/channel-participants.js +0 -40
- package/dist/harness/constants.js +0 -2
- package/dist/harness/context-meter.js +0 -97
- package/dist/harness/context.js +0 -363
- package/dist/harness/dispatch.js +0 -144
- package/dist/harness/dispatcher.js +0 -156
- package/dist/harness/event-normalizer.js +0 -59
- package/dist/harness/history.js +0 -177
- package/dist/harness/mcp.js +0 -61
- package/dist/harness/orchestration.js +0 -88
- package/dist/harness/orchestrator.js +0 -149
- package/dist/harness/participants.js +0 -22
- package/dist/harness/process.js +0 -29
- package/dist/harness/queue-processor.js +0 -187
- package/dist/harness/run-harness.js +0 -154
- package/dist/harness/run.js +0 -98
- package/dist/harness/runtime-factory.js +0 -73
- package/dist/harness/runtime.js +0 -57
- package/dist/harness/todo-advance.js +0 -93
- package/dist/harness/todo-dispatch.js +0 -51
- package/dist/harness/todos.js +0 -5
- package/dist/harness/turn.js +0 -79
- package/dist/harness/types.js +0 -1
- package/dist/plugins/ai-sdk/index.js +0 -34
- package/dist/plugins/ai-sdk/runtime.js +0 -395
- package/dist/plugins/ai-sdk/system-prompt.js +0 -18
- package/dist/plugins/ai-sdk.js +0 -331
- package/dist/plugins/approval.js +0 -163
- package/dist/plugins/delegation.js +0 -108
- package/dist/plugins/mcp/index.js +0 -108
- package/dist/plugins/mcp.js +0 -140
- package/dist/plugins/shell/index.js +0 -100
- package/dist/plugins/shell.js +0 -123
- package/dist/plugins/storage-tools/index.js +0 -76
- package/dist/plugins/storage.js +0 -737
- package/dist/plugins/thread-namer/index.js +0 -72
- package/dist/plugins/threads/index.js +0 -114
- package/dist/plugins/ui.js +0 -211
- package/dist/plugins/workflow/index.js +0 -65
- package/dist/registry/agents.js +0 -138
- package/dist/registry/plugins.js +0 -110
- package/dist/services/agent-packages.js +0 -103
- package/dist/services/memory.js +0 -152
- package/dist/services/plugins.js +0 -98
- package/dist/services/storage.js +0 -1028
- package/dist/workflow/service.js +0 -106
- package/dist/workflow/types.js +0 -3
- package/src/plugins/approval/index.ts +0 -147
- package/src/plugins/bash/index.ts +0 -232
- package/src/plugins/delegation/index.ts +0 -153
- package/src/plugins/memory/index.ts +0 -182
- package/src/plugins/openbot/context.ts +0 -121
- package/src/plugins/openbot/history.ts +0 -158
- package/src/plugins/openbot/index.ts +0 -80
- package/src/plugins/openbot/runtime.ts +0 -526
- package/src/plugins/openbot/system-prompt.ts +0 -22
- package/src/plugins/ui/index.ts +0 -123
- /package/dist/{bus → services/todo}/types.js +0 -0
|
@@ -1,109 +1,15 @@
|
|
|
1
|
-
import z from 'zod';
|
|
2
1
|
import type { Plugin } from '../../services/plugins/types.js';
|
|
3
2
|
import { OpenBotEvent } from '../../app/types.js';
|
|
4
3
|
import { buildWorkspaceFileUrl } from './files.js';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
|
-
* `storage` —
|
|
8
|
-
*
|
|
6
|
+
* `storage` — platform infra handlers for `/api/state` and deterministic reads.
|
|
7
|
+
* Agent-facing storage tools live in `@meetopenbot/openbot`.
|
|
9
8
|
*/
|
|
10
|
-
const storageToolDefinitions = {
|
|
11
|
-
create_channel: {
|
|
12
|
-
description:
|
|
13
|
-
'Create a new channel. Use when the user intent is clearly different from the current channel and should be split. Always confirm before creating. Skip for simple Q&A.',
|
|
14
|
-
inputSchema: z.object({
|
|
15
|
-
channelId: z
|
|
16
|
-
.string()
|
|
17
|
-
.describe('Unique channel ID (e.g. product-launch, backend-platform, channel_roadmap).'),
|
|
18
|
-
spec: z
|
|
19
|
-
.string()
|
|
20
|
-
.optional()
|
|
21
|
-
.describe('Optional initial markdown content for the channel spec.'),
|
|
22
|
-
initialState: z
|
|
23
|
-
.record(z.string(), z.unknown())
|
|
24
|
-
.optional()
|
|
25
|
-
.describe('Optional initial state object for the channel.'),
|
|
26
|
-
cwd: z
|
|
27
|
-
.string()
|
|
28
|
-
.optional()
|
|
29
|
-
.describe(
|
|
30
|
-
'Optional initial current working directory for the channel. Defaults to an absolute path under ~/openbot/{channelId}.',
|
|
31
|
-
),
|
|
32
|
-
}),
|
|
33
|
-
},
|
|
34
|
-
patch_channel_details: {
|
|
35
|
-
description: 'Patch current channel details (state, spec, cwd).',
|
|
36
|
-
inputSchema: z
|
|
37
|
-
.object({
|
|
38
|
-
state: z
|
|
39
|
-
.record(z.string(), z.unknown())
|
|
40
|
-
.optional()
|
|
41
|
-
.describe(
|
|
42
|
-
'JSON state object for the channel. Use for structured metadata.',
|
|
43
|
-
),
|
|
44
|
-
spec: z
|
|
45
|
-
.string()
|
|
46
|
-
.optional()
|
|
47
|
-
.describe(
|
|
48
|
-
'Markdown content for the channel specification (SPEC.md). Use for goals and rules.',
|
|
49
|
-
),
|
|
50
|
-
cwd: z.string().optional().describe('Current working directory for the channel.'),
|
|
51
|
-
})
|
|
52
|
-
.refine(
|
|
53
|
-
(value) =>
|
|
54
|
-
value.state !== undefined ||
|
|
55
|
-
value.spec !== undefined ||
|
|
56
|
-
value.cwd !== undefined,
|
|
57
|
-
{ message: 'Provide at least one of state, spec, or cwd.' },
|
|
58
|
-
),
|
|
59
|
-
},
|
|
60
|
-
patch_thread_details: {
|
|
61
|
-
description: 'Patch current thread details (state).',
|
|
62
|
-
inputSchema: z.object({
|
|
63
|
-
state: z
|
|
64
|
-
.record(z.string(), z.unknown())
|
|
65
|
-
.describe(
|
|
66
|
-
'JSON state object for the thread. Use for structured progress or metadata.',
|
|
67
|
-
),
|
|
68
|
-
}),
|
|
69
|
-
},
|
|
70
|
-
create_variable: {
|
|
71
|
-
description: 'Create or update a variable in the workspace storage.',
|
|
72
|
-
inputSchema: z.object({
|
|
73
|
-
key: z.string().describe('The key of the variable.'),
|
|
74
|
-
value: z.string().describe('The value of the variable.'),
|
|
75
|
-
secret: z.boolean().optional().describe('Whether the variable is a secret.'),
|
|
76
|
-
}),
|
|
77
|
-
},
|
|
78
|
-
delete_variable: {
|
|
79
|
-
description: 'Delete a variable from the workspace storage.',
|
|
80
|
-
inputSchema: z.object({
|
|
81
|
-
key: z.string().describe('The key of the variable to delete.'),
|
|
82
|
-
}),
|
|
83
|
-
},
|
|
84
|
-
delete_channel: {
|
|
85
|
-
description:
|
|
86
|
-
'Permanently delete a channel and all its threads and events. Always confirm with the user before deleting.',
|
|
87
|
-
inputSchema: z.object({
|
|
88
|
-
channelId: z.string().describe('The channel ID to delete.'),
|
|
89
|
-
}),
|
|
90
|
-
},
|
|
91
|
-
get_workspace_file_url: {
|
|
92
|
-
description:
|
|
93
|
-
'Get a fetchable HTTP URL for a file in the current channel workspace (images, video, audio, documents).',
|
|
94
|
-
inputSchema: z.object({
|
|
95
|
-
path: z
|
|
96
|
-
.string()
|
|
97
|
-
.describe('Path relative to the channel working directory, e.g. "uploads/clip.mp4".'),
|
|
98
|
-
}),
|
|
99
|
-
},
|
|
100
|
-
};
|
|
101
|
-
|
|
102
9
|
export const storagePlugin: Plugin = {
|
|
103
10
|
id: 'storage',
|
|
104
11
|
name: 'Storage',
|
|
105
|
-
description: '
|
|
106
|
-
toolDefinitions: storageToolDefinitions,
|
|
12
|
+
description: 'Infrastructure storage handlers for channels, threads, agents, and files.',
|
|
107
13
|
factory: ({ storage, publicBaseUrl }) => (builder) => {
|
|
108
14
|
const resolvePublicBaseUrl = () => publicBaseUrl;
|
|
109
15
|
|
|
@@ -147,243 +53,6 @@ export const storagePlugin: Plugin = {
|
|
|
147
53
|
} as OpenBotEvent;
|
|
148
54
|
});
|
|
149
55
|
|
|
150
|
-
builder.on('action:create_channel', async function* (event, context) {
|
|
151
|
-
const { channelId, spec, initialState, cwd } = (event as any).data;
|
|
152
|
-
const rawChannelId = (channelId || '').trim();
|
|
153
|
-
const channelSpec = typeof spec === 'string' ? spec : '';
|
|
154
|
-
|
|
155
|
-
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
156
|
-
|
|
157
|
-
if (!rawChannelId) {
|
|
158
|
-
yield {
|
|
159
|
-
type: 'action:create_channel:result',
|
|
160
|
-
data: { success: false, channelId: '', channelUrl: '' },
|
|
161
|
-
meta: resultMeta,
|
|
162
|
-
} as OpenBotEvent;
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const channelUrl = `/channels/${rawChannelId}`;
|
|
167
|
-
|
|
168
|
-
const mergedInitial: Record<string, unknown> = { ...(initialState || {}) };
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
await storage.createChannel({
|
|
172
|
-
channelId: rawChannelId,
|
|
173
|
-
spec: channelSpec,
|
|
174
|
-
initialState: mergedInitial,
|
|
175
|
-
cwd,
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
yield {
|
|
179
|
-
type: 'action:create_channel:result',
|
|
180
|
-
data: { success: true, channelId: rawChannelId, channelUrl },
|
|
181
|
-
meta: resultMeta,
|
|
182
|
-
} as OpenBotEvent;
|
|
183
|
-
|
|
184
|
-
yield {
|
|
185
|
-
type: 'agent:output',
|
|
186
|
-
data: { content: `Created channel \`${rawChannelId}\`.` },
|
|
187
|
-
meta: resultMeta,
|
|
188
|
-
} as OpenBotEvent;
|
|
189
|
-
} catch {
|
|
190
|
-
yield {
|
|
191
|
-
type: 'action:create_channel:result',
|
|
192
|
-
data: { success: false, channelId: rawChannelId, channelUrl },
|
|
193
|
-
meta: resultMeta,
|
|
194
|
-
} as OpenBotEvent;
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
builder.on('action:delete_channel', async function* (event, context) {
|
|
199
|
-
const rawChannelId = ((event.data as { channelId?: string })?.channelId || '').trim();
|
|
200
|
-
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
201
|
-
|
|
202
|
-
if (!rawChannelId) {
|
|
203
|
-
yield {
|
|
204
|
-
type: 'action:delete_channel:result',
|
|
205
|
-
data: { success: false, channelId: '', error: 'channelId is required' },
|
|
206
|
-
meta: resultMeta,
|
|
207
|
-
} as OpenBotEvent;
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
try {
|
|
212
|
-
await storage.deleteChannel({ channelId: rawChannelId });
|
|
213
|
-
yield {
|
|
214
|
-
type: 'action:delete_channel:result',
|
|
215
|
-
data: { success: true, channelId: rawChannelId },
|
|
216
|
-
meta: resultMeta,
|
|
217
|
-
} as OpenBotEvent;
|
|
218
|
-
yield {
|
|
219
|
-
type: 'agent:output',
|
|
220
|
-
data: { content: `Deleted channel \`${rawChannelId}\`.` },
|
|
221
|
-
meta: resultMeta,
|
|
222
|
-
} as OpenBotEvent;
|
|
223
|
-
} catch (error) {
|
|
224
|
-
yield {
|
|
225
|
-
type: 'action:delete_channel:result',
|
|
226
|
-
data: {
|
|
227
|
-
success: false,
|
|
228
|
-
channelId: rawChannelId,
|
|
229
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
230
|
-
},
|
|
231
|
-
meta: resultMeta,
|
|
232
|
-
} as OpenBotEvent;
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
builder.on('action:update_channel', async function* (event, context) {
|
|
237
|
-
const data = (event.data || {}) as {
|
|
238
|
-
channelId?: string;
|
|
239
|
-
name?: string;
|
|
240
|
-
cwd?: string;
|
|
241
|
-
};
|
|
242
|
-
const targetChannelId = (data.channelId || context.state.channelId || '').trim();
|
|
243
|
-
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
244
|
-
|
|
245
|
-
if (!targetChannelId) {
|
|
246
|
-
yield {
|
|
247
|
-
type: 'action:update_channel:result',
|
|
248
|
-
data: { success: false, channelId: '', updatedFields: [] as string[] },
|
|
249
|
-
meta: resultMeta,
|
|
250
|
-
} as OpenBotEvent;
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const patch: Record<string, unknown> = {};
|
|
255
|
-
const updatedFields: string[] = [];
|
|
256
|
-
|
|
257
|
-
if (typeof data.name === 'string' && data.name.trim()) {
|
|
258
|
-
patch.name = data.name.trim();
|
|
259
|
-
updatedFields.push('name');
|
|
260
|
-
}
|
|
261
|
-
if (typeof data.cwd === 'string' && data.cwd.trim()) {
|
|
262
|
-
patch.cwd = data.cwd.trim();
|
|
263
|
-
updatedFields.push('cwd');
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
try {
|
|
267
|
-
if (updatedFields.length > 0) {
|
|
268
|
-
await storage.patchChannelState({ channelId: targetChannelId, state: patch });
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (targetChannelId === context.state.channelId) {
|
|
272
|
-
context.state.channelDetails = await storage.getChannelDetails({
|
|
273
|
-
channelId: context.state.channelId,
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
yield {
|
|
278
|
-
type: 'action:update_channel:result',
|
|
279
|
-
data: { success: true, channelId: targetChannelId, updatedFields },
|
|
280
|
-
meta: resultMeta,
|
|
281
|
-
} as OpenBotEvent;
|
|
282
|
-
} catch {
|
|
283
|
-
yield {
|
|
284
|
-
type: 'action:update_channel:result',
|
|
285
|
-
data: { success: false, channelId: targetChannelId, updatedFields },
|
|
286
|
-
meta: resultMeta,
|
|
287
|
-
} as OpenBotEvent;
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
builder.on('action:patch_channel_details', async function* (event, context) {
|
|
292
|
-
const updatedFields: ('state' | 'spec' | 'cwd')[] = [];
|
|
293
|
-
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
294
|
-
const data = (event.data || {}) as {
|
|
295
|
-
state?: Record<string, unknown>;
|
|
296
|
-
spec?: string;
|
|
297
|
-
cwd?: string;
|
|
298
|
-
};
|
|
299
|
-
try {
|
|
300
|
-
if (data.state !== undefined) {
|
|
301
|
-
await storage.patchChannelState({
|
|
302
|
-
channelId: context.state.channelId,
|
|
303
|
-
state: data.state,
|
|
304
|
-
});
|
|
305
|
-
updatedFields.push('state');
|
|
306
|
-
}
|
|
307
|
-
if (typeof data.spec === 'string') {
|
|
308
|
-
await storage.patchChannelSpec({
|
|
309
|
-
channelId: context.state.channelId,
|
|
310
|
-
spec: data.spec,
|
|
311
|
-
});
|
|
312
|
-
updatedFields.push('spec');
|
|
313
|
-
}
|
|
314
|
-
if (typeof data.cwd === 'string') {
|
|
315
|
-
await storage.patchChannelState({
|
|
316
|
-
channelId: context.state.channelId,
|
|
317
|
-
state: { cwd: data.cwd },
|
|
318
|
-
});
|
|
319
|
-
updatedFields.push('cwd');
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
context.state.channelDetails = await storage.getChannelDetails({
|
|
323
|
-
channelId: context.state.channelId,
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
yield {
|
|
327
|
-
type: "client:ui:widget",
|
|
328
|
-
data: {
|
|
329
|
-
widgetId: "patch-channel-details-result" + Date.now(),
|
|
330
|
-
kind: "message",
|
|
331
|
-
title: "Channel details updated.",
|
|
332
|
-
body: `The channel details have been updated. ${updatedFields.join(', ')}`,
|
|
333
|
-
display: "collapsed",
|
|
334
|
-
},
|
|
335
|
-
meta: resultMeta,
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
yield {
|
|
339
|
-
type: 'action:patch_channel_details:result',
|
|
340
|
-
data: { success: true, updatedFields },
|
|
341
|
-
meta: resultMeta,
|
|
342
|
-
};
|
|
343
|
-
} catch {
|
|
344
|
-
yield {
|
|
345
|
-
type: 'action:patch_channel_details:result',
|
|
346
|
-
data: { success: false, updatedFields },
|
|
347
|
-
meta: resultMeta,
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
builder.on('action:patch_thread_details', async function* (event, context) {
|
|
353
|
-
const updatedFields: ('state')[] = [];
|
|
354
|
-
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
355
|
-
try {
|
|
356
|
-
if (!context.state.threadId) {
|
|
357
|
-
throw new Error('Missing threadId in state for patch_thread_details');
|
|
358
|
-
}
|
|
359
|
-
if ((event.data as any).state !== undefined) {
|
|
360
|
-
await storage.patchThreadState({
|
|
361
|
-
channelId: context.state.channelId,
|
|
362
|
-
threadId: context.state.threadId,
|
|
363
|
-
state: (event.data as any).state,
|
|
364
|
-
});
|
|
365
|
-
updatedFields.push('state');
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
context.state.threadDetails = await storage.getThreadDetails({
|
|
369
|
-
channelId: context.state.channelId,
|
|
370
|
-
threadId: context.state.threadId,
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
yield {
|
|
374
|
-
type: 'action:patch_thread_details:result',
|
|
375
|
-
data: { success: true, updatedFields },
|
|
376
|
-
meta: resultMeta,
|
|
377
|
-
};
|
|
378
|
-
} catch {
|
|
379
|
-
yield {
|
|
380
|
-
type: 'action:patch_thread_details:result',
|
|
381
|
-
data: { success: false, updatedFields },
|
|
382
|
-
meta: resultMeta,
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
});
|
|
386
|
-
|
|
387
56
|
builder.on('agent:usage', async function* (event, context) {
|
|
388
57
|
const { usage } = event.data;
|
|
389
58
|
if (!context.state.threadId) return;
|
|
@@ -738,43 +407,6 @@ export const storagePlugin: Plugin = {
|
|
|
738
407
|
};
|
|
739
408
|
}
|
|
740
409
|
});
|
|
741
|
-
|
|
742
|
-
builder.on('action:get_workspace_file_url', async function* (event, context) {
|
|
743
|
-
const channelId = context.state.channelId;
|
|
744
|
-
const filePath = (event.data as { path?: string })?.path;
|
|
745
|
-
const toolCallId = event.meta?.toolCallId;
|
|
746
|
-
|
|
747
|
-
if (!filePath) {
|
|
748
|
-
yield {
|
|
749
|
-
type: 'action:get_workspace_file_url:result',
|
|
750
|
-
data: { success: false, path: '', error: 'Path is required', output: 'Path is required' },
|
|
751
|
-
meta: { ...(event.meta || {}), toolCallId },
|
|
752
|
-
};
|
|
753
|
-
return;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
try {
|
|
757
|
-
const { size, mimeType } = await storage.getChannelFileStat({ channelId, path: filePath });
|
|
758
|
-
const url = buildWorkspaceFileUrl({
|
|
759
|
-
baseUrl: resolvePublicBaseUrl(),
|
|
760
|
-
channelId,
|
|
761
|
-
filePath,
|
|
762
|
-
});
|
|
763
|
-
const output = JSON.stringify({ path: filePath, url, mimeType, size });
|
|
764
|
-
yield {
|
|
765
|
-
type: 'action:get_workspace_file_url:result',
|
|
766
|
-
data: { success: true, path: filePath, url, mimeType, size, output },
|
|
767
|
-
meta: { ...(event.meta || {}), toolCallId },
|
|
768
|
-
};
|
|
769
|
-
} catch (error) {
|
|
770
|
-
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
771
|
-
yield {
|
|
772
|
-
type: 'action:get_workspace_file_url:result',
|
|
773
|
-
data: { success: false, path: filePath, error: message, output: message },
|
|
774
|
-
meta: { ...(event.meta || {}), toolCallId },
|
|
775
|
-
};
|
|
776
|
-
}
|
|
777
|
-
});
|
|
778
410
|
},
|
|
779
411
|
};
|
|
780
412
|
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { ORCHESTRATOR_AGENT_ID, STATE_AGENT_ID } from '../../app/agent-ids.js';
|
|
2
|
+
import {
|
|
3
|
+
assertCloudSystemAgentPluginsMutable,
|
|
4
|
+
DEFAULT_CLOUD_OPENBOT_AUTH_MODE,
|
|
5
|
+
isCloudMode,
|
|
6
|
+
} from '../../app/cloud-mode.js';
|
|
2
7
|
import {
|
|
3
8
|
DEFAULT_PLUGINS_DIR,
|
|
4
9
|
DEFAULT_AGENTS_DIR,
|
|
5
|
-
DEFAULT_BASE_DIR,
|
|
6
10
|
DEFAULT_CHANNELS_DIR,
|
|
11
|
+
getBaseDir,
|
|
7
12
|
getDefaultChannelCwd,
|
|
8
|
-
loadConfig,
|
|
9
13
|
resolvePath,
|
|
10
14
|
StoredVariable,
|
|
11
15
|
VARIABLES_FILE,
|
|
@@ -27,8 +31,12 @@ import {
|
|
|
27
31
|
ThreadState,
|
|
28
32
|
} from '../../services/plugins/domain.js';
|
|
29
33
|
import type { PluginRef } from '../../services/plugins/types.js';
|
|
30
|
-
import {
|
|
34
|
+
import { OPENBOT_PLUGIN_ID } from '../../app/openbot-plugin.js';
|
|
31
35
|
import { listBuiltInPlugins, parsePluginModule } from '../../services/plugins/registry.js';
|
|
36
|
+
import {
|
|
37
|
+
enrichOpenbotPluginDescriptor,
|
|
38
|
+
listRegistryModelOptions,
|
|
39
|
+
} from '../../services/plugins/model-registry.js';
|
|
32
40
|
import { OpenBotEvent, OpenBotState } from '../../app/types.js';
|
|
33
41
|
import { processService } from '../../services/process.js';
|
|
34
42
|
import { memoryService } from '../memory/service.js';
|
|
@@ -38,10 +46,7 @@ import {
|
|
|
38
46
|
statChannelFile,
|
|
39
47
|
} from './files.js';
|
|
40
48
|
|
|
41
|
-
const resolveBaseDir = () =>
|
|
42
|
-
const config = loadConfig();
|
|
43
|
-
return resolvePath(config.baseDir || DEFAULT_BASE_DIR);
|
|
44
|
-
};
|
|
49
|
+
const resolveBaseDir = () => getBaseDir();
|
|
45
50
|
|
|
46
51
|
const ENTITY_SVG_CANDIDATE_NAMES = ['avatar.svg', 'icon.svg', 'image.svg', 'logo.svg'] as const;
|
|
47
52
|
|
|
@@ -117,16 +122,46 @@ const SYSTEM_AGENT_ID = ORCHESTRATOR_AGENT_ID;
|
|
|
117
122
|
|
|
118
123
|
const SYSTEM_DEFAULT_PLUGINS: PluginRef[] = [
|
|
119
124
|
{
|
|
120
|
-
id:
|
|
125
|
+
id: OPENBOT_PLUGIN_ID,
|
|
121
126
|
config: {
|
|
122
127
|
model: 'openai/gpt-5.4-mini',
|
|
123
|
-
approval: {
|
|
124
|
-
actions: ['action:bash', 'action:create_channel', 'action:delete_channel'],
|
|
125
|
-
},
|
|
126
128
|
},
|
|
127
129
|
},
|
|
128
130
|
];
|
|
129
131
|
|
|
132
|
+
function getCloudSystemDefaultPlugins(): PluginRef[] {
|
|
133
|
+
return SYSTEM_DEFAULT_PLUGINS.map((ref) => {
|
|
134
|
+
if (ref.id !== OPENBOT_PLUGIN_ID) return ref;
|
|
135
|
+
return {
|
|
136
|
+
...ref,
|
|
137
|
+
config: {
|
|
138
|
+
...ref.config,
|
|
139
|
+
authMode: DEFAULT_CLOUD_OPENBOT_AUTH_MODE,
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function mergeCloudSystemPluginRefs(
|
|
146
|
+
defaults: PluginRef[],
|
|
147
|
+
diskRefs: PluginRef[],
|
|
148
|
+
): PluginRef[] {
|
|
149
|
+
const defaultOpenbot = defaults.find((ref) => ref.id === OPENBOT_PLUGIN_ID);
|
|
150
|
+
const diskOpenbot = diskRefs.find((ref) => ref.id === OPENBOT_PLUGIN_ID);
|
|
151
|
+
|
|
152
|
+
if (!defaultOpenbot) return defaults;
|
|
153
|
+
|
|
154
|
+
return [
|
|
155
|
+
{
|
|
156
|
+
...defaultOpenbot,
|
|
157
|
+
config: {
|
|
158
|
+
...defaultOpenbot.config,
|
|
159
|
+
...diskOpenbot?.config,
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
|
|
130
165
|
/** No `openbot` / `bash` — storage-side effects and infra plugins only. */
|
|
131
166
|
const STATE_DEFAULT_PLUGINS: PluginRef[] = [
|
|
132
167
|
{ id: 'storage' },
|
|
@@ -137,6 +172,7 @@ const STATE_AGENT_INSTRUCTIONS =
|
|
|
137
172
|
'Built-in infra agent for deterministic state reads. No conversational model is attached; handle storage, approvals, memory, and plugin marketplace events.';
|
|
138
173
|
|
|
139
174
|
function getSystemAgentDetails(overrides?: Partial<AgentDetails>): AgentDetails {
|
|
175
|
+
const defaultPlugins = isCloudMode() ? getCloudSystemDefaultPlugins() : SYSTEM_DEFAULT_PLUGINS;
|
|
140
176
|
const defaults: AgentDetails = {
|
|
141
177
|
id: SYSTEM_AGENT_ID,
|
|
142
178
|
name: 'OpenBot',
|
|
@@ -144,22 +180,41 @@ function getSystemAgentDetails(overrides?: Partial<AgentDetails>): AgentDetails
|
|
|
144
180
|
description:
|
|
145
181
|
'First-party orchestration agent for OpenBot.',
|
|
146
182
|
instructions: '',
|
|
147
|
-
plugins:
|
|
148
|
-
pluginRefs:
|
|
183
|
+
plugins: defaultPlugins.map((ref) => ref.id),
|
|
184
|
+
pluginRefs: defaultPlugins,
|
|
149
185
|
createdAt: new Date(),
|
|
150
186
|
updatedAt: new Date(),
|
|
151
187
|
};
|
|
152
188
|
|
|
153
189
|
if (!overrides) return defaults;
|
|
154
190
|
|
|
155
|
-
const refs = overrides.pluginRefs && overrides.pluginRefs.length > 0
|
|
156
|
-
? overrides.pluginRefs
|
|
157
|
-
: defaults.pluginRefs;
|
|
158
|
-
|
|
159
191
|
const diskInstructions = overrides.instructions?.trim();
|
|
160
192
|
const instructions =
|
|
161
193
|
diskInstructions && diskInstructions.length > 0 ? diskInstructions : defaults.instructions;
|
|
162
194
|
|
|
195
|
+
if (isCloudMode()) {
|
|
196
|
+
const diskRefs =
|
|
197
|
+
overrides.pluginRefs && overrides.pluginRefs.length > 0 ? overrides.pluginRefs : undefined;
|
|
198
|
+
const refs = diskRefs
|
|
199
|
+
? mergeCloudSystemPluginRefs(defaults.pluginRefs, diskRefs)
|
|
200
|
+
: defaults.pluginRefs;
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
...defaults,
|
|
204
|
+
...overrides,
|
|
205
|
+
id: SYSTEM_AGENT_ID,
|
|
206
|
+
instructions,
|
|
207
|
+
image: overrides.image || defaults.image,
|
|
208
|
+
plugins: refs.map((ref) => ref.id),
|
|
209
|
+
pluginRefs: refs,
|
|
210
|
+
updatedAt: new Date(),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const refs = overrides.pluginRefs && overrides.pluginRefs.length > 0
|
|
215
|
+
? overrides.pluginRefs
|
|
216
|
+
: defaults.pluginRefs;
|
|
217
|
+
|
|
163
218
|
return {
|
|
164
219
|
...defaults,
|
|
165
220
|
...overrides,
|
|
@@ -220,9 +275,6 @@ const agentSummaryFromDetails = (details: AgentDetails): Agent => ({
|
|
|
220
275
|
updatedAt: details.updatedAt,
|
|
221
276
|
});
|
|
222
277
|
|
|
223
|
-
// Suppress unused warning until system agent customization re-uses openbotPlugin metadata.
|
|
224
|
-
void openbotPlugin;
|
|
225
|
-
|
|
226
278
|
/** Built-in agents may persist optional `agents/<id>/AGENT.md` overlays; read path merges them with defaults. */
|
|
227
279
|
const isBuiltinOverlayAgentId = (agentId: string): boolean =>
|
|
228
280
|
agentId === SYSTEM_AGENT_ID || agentId === STATE_AGENT_ID;
|
|
@@ -992,12 +1044,17 @@ export const storageService = {
|
|
|
992
1044
|
return Array.from(deduped.values()).filter((agent) => !agent.hidden);
|
|
993
1045
|
},
|
|
994
1046
|
getPlugins: async (): Promise<PluginDescriptor[]> => {
|
|
995
|
-
const [builtIn, fromDisk] = await Promise.all([
|
|
1047
|
+
const [builtIn, fromDisk, modelOptions] = await Promise.all([
|
|
996
1048
|
listBuiltInPluginDescriptors(),
|
|
997
1049
|
listPluginsFromDisk(),
|
|
1050
|
+
listRegistryModelOptions(),
|
|
998
1051
|
]);
|
|
999
1052
|
|
|
1000
|
-
const
|
|
1053
|
+
const enrichedBuiltIn = builtIn.map((plugin) =>
|
|
1054
|
+
enrichOpenbotPluginDescriptor(plugin, modelOptions),
|
|
1055
|
+
);
|
|
1056
|
+
|
|
1057
|
+
const merged = [...enrichedBuiltIn, ...fromDisk];
|
|
1001
1058
|
const deduped = new Map<string, PluginDescriptor>();
|
|
1002
1059
|
for (const plugin of merged) {
|
|
1003
1060
|
if (!deduped.has(plugin.id)) {
|
|
@@ -1081,6 +1138,7 @@ export const storageService = {
|
|
|
1081
1138
|
plugins: PluginRef[];
|
|
1082
1139
|
}): Promise<void> => {
|
|
1083
1140
|
assertAgentIdFormat(agentId);
|
|
1141
|
+
assertCloudSystemAgentPluginsMutable(agentId, plugins);
|
|
1084
1142
|
const agentDir = resolvePath(path.join(getAgentsRootDir(), agentId));
|
|
1085
1143
|
const agentMdPath = path.join(agentDir, 'AGENT.md');
|
|
1086
1144
|
|
|
@@ -1133,6 +1191,7 @@ export const storageService = {
|
|
|
1133
1191
|
plugins?: PluginRef[];
|
|
1134
1192
|
}): Promise<void> => {
|
|
1135
1193
|
assertAgentIdFormat(agentId);
|
|
1194
|
+
assertCloudSystemAgentPluginsMutable(agentId, plugins);
|
|
1136
1195
|
const agentDir = resolvePath(path.join(getAgentsRootDir(), agentId));
|
|
1137
1196
|
const agentMdPath = path.join(agentDir, 'AGENT.md');
|
|
1138
1197
|
|
|
@@ -49,6 +49,8 @@ export type ConfigSchema = {
|
|
|
49
49
|
description?: string;
|
|
50
50
|
default?: unknown;
|
|
51
51
|
enum?: unknown[];
|
|
52
|
+
/** Labeled select options (preferred over bare `enum` when present). */
|
|
53
|
+
options?: Array<{ label: string; value: string; description?: string }>;
|
|
52
54
|
minimum?: number;
|
|
53
55
|
maximum?: number;
|
|
54
56
|
format?: 'password' | 'url' | 'email';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_CLOUD_OPENBOT_AUTH_MODE,
|
|
3
|
+
isCloudMode,
|
|
4
|
+
isCloudSystemAgent,
|
|
5
|
+
parseOpenbotAuthMode,
|
|
6
|
+
type OpenbotAuthMode,
|
|
7
|
+
} from '../../app/cloud-mode.js';
|
|
8
|
+
import { ORCHESTRATOR_AGENT_ID } from '../../app/agent-ids.js';
|
|
9
|
+
import { OPENBOT_PLUGIN_ID } from '../../app/openbot-plugin.js';
|
|
10
|
+
import { getBaseDir, resolvePath, saveConfig } from '../../app/config.js';
|
|
11
|
+
import {
|
|
12
|
+
listApiKeyProvidersFromRegistry,
|
|
13
|
+
resolveModelRegistry,
|
|
14
|
+
} from './model-registry.js';
|
|
15
|
+
import type { PluginHost, RunAgentHostOptions } from './types.js';
|
|
16
|
+
|
|
17
|
+
export function createPluginHost(
|
|
18
|
+
runAgent: (options: RunAgentHostOptions) => Promise<void>,
|
|
19
|
+
): PluginHost {
|
|
20
|
+
return {
|
|
21
|
+
runAgent,
|
|
22
|
+
isCloudSystemAgent,
|
|
23
|
+
isCloudMode,
|
|
24
|
+
parseOpenbotAuthMode,
|
|
25
|
+
resolveModelRegistry,
|
|
26
|
+
listApiKeyProvidersFromRegistry,
|
|
27
|
+
saveConfig,
|
|
28
|
+
getBaseDir,
|
|
29
|
+
resolvePath,
|
|
30
|
+
orchestratorAgentId: ORCHESTRATOR_AGENT_ID,
|
|
31
|
+
openbotPluginId: OPENBOT_PLUGIN_ID,
|
|
32
|
+
defaultCloudAuthMode: DEFAULT_CLOUD_OPENBOT_AUTH_MODE,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type { OpenbotAuthMode };
|