@meetopenbot/openbot 0.2.7 → 1.0.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/auto-model.js +13 -0
- package/dist/build-tools.js +26 -0
- package/dist/history.js +9 -5
- package/dist/images.js +64 -0
- package/dist/index.js +66 -67
- package/dist/output-buffer.js +31 -0
- package/dist/runtime.js +385 -482
- package/dist/stream-error.js +6 -0
- package/dist/system-prompt.js +4 -0
- package/dist/tools/approval.js +137 -108
- package/dist/tools/ask-agent.js +39 -47
- package/dist/tools/memory.js +41 -73
- package/dist/tools/start-work.js +61 -141
- package/dist/tools/storage.js +62 -424
- package/dist/tools/thread-status.js +22 -38
- package/dist/tools/thread-title.js +57 -0
- package/dist/tools/todo.js +33 -52
- package/dist/types.js +0 -8
- package/package.json +2 -2
- package/dist/tools/bash.js +0 -432
- package/dist/tools/preview.js +0 -269
- package/dist/tools/ui.js +0 -120
- package/dist/utils/workspace-url.js +0 -6
package/dist/auto-model.js
CHANGED
|
@@ -49,6 +49,19 @@ export function expandModelString(modelString, ctx) {
|
|
|
49
49
|
return pickAutoModel(ctx);
|
|
50
50
|
return modelString.trim();
|
|
51
51
|
}
|
|
52
|
+
/** BYOK message when the resolved provider has no API key in env. */
|
|
53
|
+
export function missingByokApiKeyMessage(modelString, ctx) {
|
|
54
|
+
if (shouldUseCreditsAuth(ctx))
|
|
55
|
+
return undefined;
|
|
56
|
+
const env = ctx?.env ?? process.env;
|
|
57
|
+
const provider = providerOf(expandModelString(modelString, ctx));
|
|
58
|
+
if (providerHasByokKey(provider, env))
|
|
59
|
+
return undefined;
|
|
60
|
+
const envVar = PROVIDER_BYOK_ENV[provider];
|
|
61
|
+
if (!envVar)
|
|
62
|
+
return undefined;
|
|
63
|
+
return `${provider} API key is missing`;
|
|
64
|
+
}
|
|
52
65
|
export function isRetryableModelError(error) {
|
|
53
66
|
const message = error instanceof Error ? error.message : String(error);
|
|
54
67
|
const lower = message.toLowerCase();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { memoryTools } from "./tools/memory.js";
|
|
2
|
+
import { todoTools } from "./tools/todo.js";
|
|
3
|
+
import { storageTools } from "./tools/storage.js";
|
|
4
|
+
import { askAgentTools } from "./tools/ask-agent.js";
|
|
5
|
+
import { startWorkTools } from "./tools/start-work.js";
|
|
6
|
+
import { threadStatusTools } from "./tools/thread-status.js";
|
|
7
|
+
import { threadTitleTools } from "./tools/thread-title.js";
|
|
8
|
+
import { wrapToolsWithApproval } from "./tools/approval.js";
|
|
9
|
+
/**
|
|
10
|
+
* Merge first-party OpenBot coordinator tools and wrap configured actions
|
|
11
|
+
* with approval.
|
|
12
|
+
*/
|
|
13
|
+
export function buildOpenbotTools(args) {
|
|
14
|
+
const tools = {
|
|
15
|
+
...memoryTools,
|
|
16
|
+
...todoTools,
|
|
17
|
+
...storageTools,
|
|
18
|
+
...askAgentTools,
|
|
19
|
+
...startWorkTools,
|
|
20
|
+
...threadStatusTools,
|
|
21
|
+
...threadTitleTools,
|
|
22
|
+
...args.context.tools,
|
|
23
|
+
};
|
|
24
|
+
const approvalConfig = args.context.config?.approval ?? { actions: [] };
|
|
25
|
+
return wrapToolsWithApproval(tools, approvalConfig.actions ?? [], args.context);
|
|
26
|
+
}
|
package/dist/history.js
CHANGED
|
@@ -164,12 +164,16 @@ export function eventsToModelMessages(events, options) {
|
|
|
164
164
|
}
|
|
165
165
|
case 'agent:invoke': {
|
|
166
166
|
const invokeEvent = event;
|
|
167
|
-
if (invokeEvent.data?.
|
|
167
|
+
if (invokeEvent.data?.role) {
|
|
168
168
|
const role = invokeEvent.data.role === 'agent' ? 'user' : invokeEvent.data.role;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
const content = invokeEvent.data.content ?? '';
|
|
170
|
+
const hasFiles = Boolean(invokeEvent.data.attachments?.length);
|
|
171
|
+
if (content || hasFiles) {
|
|
172
|
+
messages.push({
|
|
173
|
+
role,
|
|
174
|
+
content,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
173
177
|
}
|
|
174
178
|
break;
|
|
175
179
|
}
|
package/dist/images.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { shouldUseCreditsAuth } from '@meetopenbot/plugin-sdk';
|
|
2
|
+
export function isImageAttachment(file) {
|
|
3
|
+
if (file.mimeType?.startsWith('image/'))
|
|
4
|
+
return true;
|
|
5
|
+
return /\.(png|jpe?g|gif|webp)$/i.test(file.name);
|
|
6
|
+
}
|
|
7
|
+
export function eventsHaveImages(events) {
|
|
8
|
+
return events.some((event) => {
|
|
9
|
+
if (event.meta?.parentToolCallId)
|
|
10
|
+
return false;
|
|
11
|
+
if (event.type !== 'agent:invoke')
|
|
12
|
+
return false;
|
|
13
|
+
return Boolean(event.data?.attachments?.some(isImageAttachment));
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/** Auto uses DeepSeek, which cannot see pictures. Pick a cheap vision model. */
|
|
17
|
+
export function pickVisionModel(authMode, env = process.env) {
|
|
18
|
+
if (shouldUseCreditsAuth({ authMode }))
|
|
19
|
+
return 'google/gemini-3.5-flash';
|
|
20
|
+
if (env.OPENAI_API_KEY?.trim())
|
|
21
|
+
return 'openai/gpt-4o-mini';
|
|
22
|
+
if (env.GOOGLE_GENERATIVE_AI_API_KEY?.trim())
|
|
23
|
+
return 'google/gemini-3.5-flash';
|
|
24
|
+
if (env.ANTHROPIC_API_KEY?.trim())
|
|
25
|
+
return 'anthropic/claude-haiku-4-5';
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
/** Put image files on matching user messages as AI SDK `file` parts. */
|
|
29
|
+
export async function addImageParts(messages, events, read) {
|
|
30
|
+
const result = [...messages];
|
|
31
|
+
let userIndex = 0;
|
|
32
|
+
for (const event of events) {
|
|
33
|
+
if (event.meta?.parentToolCallId)
|
|
34
|
+
continue;
|
|
35
|
+
if (event.type !== 'agent:invoke' || !event.data?.role)
|
|
36
|
+
continue;
|
|
37
|
+
const files = (event.data.attachments ?? []).filter(isImageAttachment);
|
|
38
|
+
const text = event.data.content ?? '';
|
|
39
|
+
if (!text && files.length === 0)
|
|
40
|
+
continue;
|
|
41
|
+
const current = result[userIndex];
|
|
42
|
+
userIndex += 1;
|
|
43
|
+
if (!current || files.length === 0)
|
|
44
|
+
continue;
|
|
45
|
+
const parts = [];
|
|
46
|
+
if (typeof current.content === 'string' && current.content) {
|
|
47
|
+
parts.push({ type: 'text', text: current.content });
|
|
48
|
+
}
|
|
49
|
+
for (const file of files) {
|
|
50
|
+
const loaded = await read(file.name);
|
|
51
|
+
if (!loaded?.data.byteLength)
|
|
52
|
+
continue;
|
|
53
|
+
parts.push({
|
|
54
|
+
type: 'file',
|
|
55
|
+
data: loaded.data,
|
|
56
|
+
mediaType: loaded.mediaType || 'image/png',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (parts.some((part) => part.type === 'file')) {
|
|
60
|
+
result[userIndex - 1] = { role: current.role, content: parts };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,90 +1,89 @@
|
|
|
1
|
-
import { defineOpenbotPlugin } from
|
|
2
|
-
import { cloudAuthModeProperties, modelConfigField, } from
|
|
3
|
-
import { AUTO_MODEL_ID, AUTO_MODEL_OPTION } from
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
import { storageToolPlugin } from "./tools/storage.js";
|
|
15
|
-
export const OPENBOT_PLUGIN_ID = "@meetopenbot/openbot";
|
|
1
|
+
import { defineOpenbotPlugin } from './types.js';
|
|
2
|
+
import { cloudAuthModeProperties, modelConfigField, } from '@meetopenbot/plugin-sdk';
|
|
3
|
+
import { AUTO_MODEL_ID, AUTO_MODEL_OPTION } from './auto-model.js';
|
|
4
|
+
import { handleOpenbotWidgetResponse, runOpenbotTurn } from './runtime.js';
|
|
5
|
+
import { memoryTools } from './tools/memory.js';
|
|
6
|
+
import { todoTools } from './tools/todo.js';
|
|
7
|
+
import { storageTools } from './tools/storage.js';
|
|
8
|
+
import { askAgentTools } from './tools/ask-agent.js';
|
|
9
|
+
import { startWorkTools } from './tools/start-work.js';
|
|
10
|
+
import { threadStatusTools } from './tools/thread-status.js';
|
|
11
|
+
import { threadTitleTools } from './tools/thread-title.js';
|
|
12
|
+
import { buildOpenbotTools } from './build-tools.js';
|
|
13
|
+
export const OPENBOT_PLUGIN_ID = '@meetopenbot/openbot';
|
|
16
14
|
const modelField = await modelConfigField({
|
|
17
|
-
providers: [
|
|
15
|
+
providers: ['openai', 'anthropic', 'google', 'deepseek'],
|
|
18
16
|
extraOptions: [AUTO_MODEL_OPTION],
|
|
19
17
|
defaultValue: AUTO_MODEL_ID,
|
|
20
|
-
description:
|
|
21
|
-
fallbackDescription:
|
|
18
|
+
description: 'Auto, or a model from the OpenBot registry.',
|
|
19
|
+
fallbackDescription: 'Auto, or a provider model in provider/model-id format (e.g. openai/gpt-4o-mini).',
|
|
22
20
|
});
|
|
23
|
-
const SPECIALIST_TOOL_NAMES = new Set([
|
|
24
|
-
...Object.keys(bashPlugin.toolDefinitions ?? {}),
|
|
25
|
-
...Object.keys(previewPlugin.toolDefinitions ?? {}),
|
|
26
|
-
]);
|
|
27
21
|
/**
|
|
28
22
|
* `@meetopenbot/openbot` — the standard, opinionated OpenBot agent runtime.
|
|
29
23
|
*
|
|
30
24
|
* The orchestrator (`system`) is a coordinator: ask agents, start work in
|
|
31
|
-
* Spaces, memory, todos, storage.
|
|
32
|
-
*
|
|
25
|
+
* Spaces, memory, todos, storage. Coding and preview work belongs to
|
|
26
|
+
* specialist harnesses, not this plugin.
|
|
33
27
|
*/
|
|
34
28
|
export const openbotPlugin = defineOpenbotPlugin({
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
description: "OpenBot coordinator runtime: ask specialists, route work into Spaces, and track todos.",
|
|
29
|
+
name: 'OpenBot Agent',
|
|
30
|
+
description: 'OpenBot coordinator runtime: ask specialists, route work into Spaces, and track todos.',
|
|
38
31
|
configSchema: {
|
|
39
|
-
type:
|
|
32
|
+
type: 'object',
|
|
40
33
|
properties: {
|
|
41
34
|
...cloudAuthModeProperties(),
|
|
42
35
|
model: modelField,
|
|
43
36
|
},
|
|
44
37
|
},
|
|
45
|
-
|
|
46
|
-
...
|
|
47
|
-
...
|
|
48
|
-
...
|
|
49
|
-
...
|
|
50
|
-
...
|
|
51
|
-
...
|
|
52
|
-
...
|
|
53
|
-
...previewPlugin.toolDefinitions,
|
|
38
|
+
tools: {
|
|
39
|
+
...memoryTools,
|
|
40
|
+
...todoTools,
|
|
41
|
+
...storageTools,
|
|
42
|
+
...askAgentTools,
|
|
43
|
+
...startWorkTools,
|
|
44
|
+
...threadStatusTools,
|
|
45
|
+
...threadTitleTools,
|
|
54
46
|
},
|
|
55
|
-
|
|
56
|
-
const {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
47
|
+
async *run(args) {
|
|
48
|
+
const { context } = args;
|
|
49
|
+
const authMode = context.host.isCloudSystemAgent(context.agentId)
|
|
50
|
+
? context.host.parseOpenbotAuthMode(context.config?.authMode)
|
|
51
|
+
: 'byok';
|
|
52
|
+
const toolDefinitions = buildOpenbotTools({ context });
|
|
53
|
+
yield* runOpenbotTurn(args, {
|
|
54
|
+
model: context.config?.model || AUTO_MODEL_ID,
|
|
55
|
+
authMode,
|
|
56
|
+
agentId: context.agentId,
|
|
57
|
+
storage: context.storage,
|
|
58
|
+
toolDefinitions,
|
|
59
|
+
abortSignal: context.abortSignal,
|
|
60
|
+
host: context.host,
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
async *onWidgetResponse(args) {
|
|
64
|
+
const { context } = args;
|
|
65
|
+
const authMode = context.host.isCloudSystemAgent(context.agentId)
|
|
66
|
+
? context.host.parseOpenbotAuthMode(context.config?.authMode)
|
|
67
|
+
: 'byok';
|
|
68
|
+
const toolDefinitions = buildOpenbotTools({ context });
|
|
69
|
+
const turnArgs = {
|
|
70
|
+
prompt: '',
|
|
71
|
+
threadId: args.state.threadId,
|
|
72
|
+
event: args.state.triggerEvent,
|
|
73
|
+
handlerCtx: args.handlerCtx,
|
|
74
|
+
context,
|
|
75
|
+
state: args.state,
|
|
76
|
+
emit: args.emit,
|
|
71
77
|
};
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
? host.parseOpenbotAuthMode(config?.authMode)
|
|
75
|
-
: "byok";
|
|
76
|
-
const toolDefinitions = isOrchestrator
|
|
77
|
-
? Object.fromEntries(Object.entries(tools).filter(([name]) => !SPECIALIST_TOOL_NAMES.has(name)))
|
|
78
|
-
: tools;
|
|
79
|
-
return openbotRuntime({
|
|
80
|
-
model: config?.model || AUTO_MODEL_ID,
|
|
78
|
+
yield* handleOpenbotWidgetResponse(turnArgs, {
|
|
79
|
+
model: context.config?.model || AUTO_MODEL_ID,
|
|
81
80
|
authMode,
|
|
82
|
-
agentId,
|
|
83
|
-
storage,
|
|
81
|
+
agentId: context.agentId,
|
|
82
|
+
storage: context.storage,
|
|
84
83
|
toolDefinitions,
|
|
85
|
-
abortSignal,
|
|
86
|
-
host,
|
|
87
|
-
})
|
|
84
|
+
abortSignal: context.abortSignal,
|
|
85
|
+
host: context.host,
|
|
86
|
+
});
|
|
88
87
|
},
|
|
89
88
|
});
|
|
90
89
|
export default openbotPlugin;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Persist one `agent:output` per model step so jsonl stays thought → tools → reply. */
|
|
2
|
+
export function createAgentOutputBuffer(meta) {
|
|
3
|
+
let text = '';
|
|
4
|
+
let reasoning = '';
|
|
5
|
+
return {
|
|
6
|
+
addText(delta) {
|
|
7
|
+
text += delta;
|
|
8
|
+
},
|
|
9
|
+
addReasoning(delta) {
|
|
10
|
+
reasoning += delta;
|
|
11
|
+
},
|
|
12
|
+
take() {
|
|
13
|
+
if (!text && !reasoning)
|
|
14
|
+
return null;
|
|
15
|
+
const event = {
|
|
16
|
+
type: 'agent:output',
|
|
17
|
+
data: {
|
|
18
|
+
content: text,
|
|
19
|
+
...(reasoning ? { reasoning } : {}),
|
|
20
|
+
},
|
|
21
|
+
meta,
|
|
22
|
+
};
|
|
23
|
+
text = '';
|
|
24
|
+
reasoning = '';
|
|
25
|
+
return event;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function isOutputBoundaryPart(type) {
|
|
30
|
+
return type === 'tool-call' || type === 'finish-step' || type === 'step-finish' || type === 'finish';
|
|
31
|
+
}
|