@meetopenbot/openbot 0.2.1 → 0.2.3
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 +1 -1
- package/dist/context.js +1 -1
- package/dist/index.js +11 -14
- package/dist/model.js +15 -11
- package/dist/runtime.js +2 -2
- package/dist/system-prompt.js +2 -1
- package/dist/tools/thread-status.js +2 -2
- package/package.json +2 -2
- package/dist/cloud-mode.js +0 -4
- package/dist/credits-auth.js +0 -41
- package/dist/model-registry.js +0 -94
package/dist/auto-model.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { shouldUseCreditsAuth } from '
|
|
1
|
+
import { shouldUseCreditsAuth } from '@meetopenbot/plugin-sdk';
|
|
2
2
|
/** Product alias shown in the picker. Never sent to a provider or the credits proxy. */
|
|
3
3
|
export const AUTO_MODEL_ID = 'openbot/auto';
|
|
4
4
|
export const AUTO_MODEL_OPTION = {
|
package/dist/context.js
CHANGED
|
@@ -66,7 +66,7 @@ export async function buildContext(state, storage) {
|
|
|
66
66
|
statusSection += `\n- Reason: ${threadStatusReason}`;
|
|
67
67
|
}
|
|
68
68
|
statusSection +=
|
|
69
|
-
"\nUse `set_thread_status` to update this. needs_input when blocked on the human; ready_for_review when you believe the job is done. Never set completed or archived.";
|
|
69
|
+
"\nUse `set_thread_status` to update this. needs_input when blocked on the human; ready_for_review when you believe the job is done; working when the job is still in progress. Never set completed or archived.";
|
|
70
70
|
sections.push(statusSection);
|
|
71
71
|
}
|
|
72
72
|
const invokeData = state.triggerEvent?.type === "agent:invoke"
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { defineOpenbotPlugin } from "./types.js";
|
|
2
|
-
import {
|
|
3
|
-
import { AUTO_MODEL_ID } from "./auto-model.js";
|
|
4
|
-
import { resolveModelConfigField } from "./model-registry.js";
|
|
2
|
+
import { cloudAuthModeProperties, modelConfigField, } from "@meetopenbot/plugin-sdk";
|
|
3
|
+
import { AUTO_MODEL_ID, AUTO_MODEL_OPTION } from "./auto-model.js";
|
|
5
4
|
import { openbotRuntime } from "./runtime.js";
|
|
6
5
|
import { bashPlugin } from "./tools/bash.js";
|
|
7
6
|
import { memoryPlugin } from "./tools/memory.js";
|
|
@@ -14,7 +13,13 @@ import { uiPlugin } from "./tools/ui.js";
|
|
|
14
13
|
import { previewPlugin } from "./tools/preview.js";
|
|
15
14
|
import { storageToolPlugin } from "./tools/storage.js";
|
|
16
15
|
export const OPENBOT_PLUGIN_ID = "@meetopenbot/openbot";
|
|
17
|
-
const modelField = await
|
|
16
|
+
const modelField = await modelConfigField({
|
|
17
|
+
providers: ["openai", "anthropic", "google", "deepseek"],
|
|
18
|
+
extraOptions: [AUTO_MODEL_OPTION],
|
|
19
|
+
defaultValue: AUTO_MODEL_ID,
|
|
20
|
+
description: "Auto, or a model from the OpenBot registry.",
|
|
21
|
+
fallbackDescription: "Auto, or a provider model in provider/model-id format (e.g. openai/gpt-4o-mini).",
|
|
22
|
+
});
|
|
18
23
|
const SPECIALIST_TOOL_NAMES = new Set([
|
|
19
24
|
...Object.keys(bashPlugin.toolDefinitions ?? {}),
|
|
20
25
|
...Object.keys(previewPlugin.toolDefinitions ?? {}),
|
|
@@ -33,16 +38,7 @@ export const openbotPlugin = defineOpenbotPlugin({
|
|
|
33
38
|
configSchema: {
|
|
34
39
|
type: "object",
|
|
35
40
|
properties: {
|
|
36
|
-
...(
|
|
37
|
-
? {
|
|
38
|
-
authMode: {
|
|
39
|
-
type: "string",
|
|
40
|
-
description: "Credits — use your workspace credit balance via OpenBot. BYOK — bring your own API key.",
|
|
41
|
-
enum: ["credits", "byok"],
|
|
42
|
-
default: "credits",
|
|
43
|
-
},
|
|
44
|
-
}
|
|
45
|
-
: {}),
|
|
41
|
+
...cloudAuthModeProperties(),
|
|
46
42
|
model: modelField,
|
|
47
43
|
},
|
|
48
44
|
},
|
|
@@ -92,3 +88,4 @@ export const openbotPlugin = defineOpenbotPlugin({
|
|
|
92
88
|
},
|
|
93
89
|
});
|
|
94
90
|
export default openbotPlugin;
|
|
91
|
+
export { openbotPlugin as plugin };
|
package/dist/model.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { createOpenAI, openai as defaultOpenai } from '@ai-sdk/openai';
|
|
2
2
|
import { createAnthropic, anthropic } from '@ai-sdk/anthropic';
|
|
3
3
|
import { expandModelString } from './auto-model.js';
|
|
4
|
-
import { CREDITS_API_KEY_PLACEHOLDER, INTEGRATIONS_TOKEN_HEADER, creditsProviderBaseUrl, resolveCreditsAuthConfig, shouldUseCreditsAuth, } from '
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
import { CREDITS_API_KEY_PLACEHOLDER, INTEGRATIONS_TOKEN_HEADER, creditsProviderBaseUrl, resolveCreditsAuthConfig, shouldUseCreditsAuth, } from '@meetopenbot/plugin-sdk';
|
|
5
|
+
function deepseekChat(modelId) {
|
|
6
|
+
return createOpenAI({
|
|
7
|
+
baseURL: 'https://api.deepseek.com',
|
|
8
|
+
apiKey: process.env.DEEPSEEK_API_KEY,
|
|
9
|
+
}).chat(modelId);
|
|
10
|
+
}
|
|
11
|
+
function googleChat(modelId) {
|
|
12
|
+
return createOpenAI({
|
|
13
|
+
baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai',
|
|
14
|
+
apiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY,
|
|
15
|
+
}).chat(modelId);
|
|
16
|
+
}
|
|
13
17
|
/**
|
|
14
18
|
* OpenAI-compatible Chat Completions. Gemini/DeepSeek gateways only speak this.
|
|
15
19
|
* Do not use for OpenAI itself — gpt-5.6-luna rejects function tools on
|
|
@@ -56,9 +60,9 @@ export function resolveModel(modelString, options) {
|
|
|
56
60
|
case 'anthropic':
|
|
57
61
|
return useCredits ? resolveCreditsProvider('anthropic', modelId) : anthropic(modelId);
|
|
58
62
|
case 'google':
|
|
59
|
-
return useCredits ? resolveCreditsProvider('google', modelId) :
|
|
63
|
+
return useCredits ? resolveCreditsProvider('google', modelId) : googleChat(modelId);
|
|
60
64
|
case 'deepseek':
|
|
61
|
-
return useCredits ? resolveCreditsProvider('deepseek', modelId) :
|
|
65
|
+
return useCredits ? resolveCreditsProvider('deepseek', modelId) : deepseekChat(modelId);
|
|
62
66
|
default:
|
|
63
67
|
throw new Error(`Unsupported AI provider: "${provider}"`);
|
|
64
68
|
}
|
package/dist/runtime.js
CHANGED
|
@@ -2,10 +2,10 @@ import { generateText } from 'ai';
|
|
|
2
2
|
import { eventsToModelMessages } from './history.js';
|
|
3
3
|
import { buildContext } from './context.js';
|
|
4
4
|
import { OPENBOT_SYSTEM_PROMPT } from './system-prompt.js';
|
|
5
|
-
import { isAuthErrorMessage, isCreditsErrorMessage } from '
|
|
5
|
+
import { isAuthErrorMessage, isCreditsErrorMessage } from '@meetopenbot/plugin-sdk';
|
|
6
6
|
import { AUTO_MODEL_ID, isAutoModel, isRetryableModelError, listAutoModelCandidates, } from './auto-model.js';
|
|
7
7
|
import { resolveModel } from './model.js';
|
|
8
|
-
import { fetchModelRegistry, getProviderModelOptions, listApiKeyProviders, PROVIDER_API_KEY_LINKS, } from '
|
|
8
|
+
import { fetchModelRegistry, getProviderModelOptions, listApiKeyProviders, PROVIDER_API_KEY_LINKS, } from '@meetopenbot/plugin-sdk';
|
|
9
9
|
async function buildSystemPrompt(state, storage) {
|
|
10
10
|
const context = await buildContext(state, storage);
|
|
11
11
|
const sections = [OPENBOT_SYSTEM_PROMPT, '', context];
|
package/dist/system-prompt.js
CHANGED
|
@@ -29,7 +29,8 @@ export const OPENBOT_SYSTEM_PROMPT = [
|
|
|
29
29
|
"- Do not stop with open `pending` or `in_progress` items unless you are blocked and have told the user why.",
|
|
30
30
|
"",
|
|
31
31
|
"# JOB STATUS",
|
|
32
|
-
"- Every thread is a job. Status is `working
|
|
32
|
+
"- Every thread is a job. Status is `working` (in progress), `needs_input`, `ready_for_review`, `completed`, or `archived`.",
|
|
33
|
+
"- Status is the job's workflow state, not whether an agent is currently executing.",
|
|
33
34
|
"- When you need a blocking answer from the human, call `set_thread_status` with `needs_input` and a short `reason`.",
|
|
34
35
|
"- When you believe the job is done, set `ready_for_review`. Never set `completed` or `archived` — only the human can.",
|
|
35
36
|
"- The current status is injected each turn as `## THREAD STATUS`.",
|
|
@@ -12,11 +12,11 @@ function formatStatusOutput(args) {
|
|
|
12
12
|
}
|
|
13
13
|
const threadStatusToolDefinitions = {
|
|
14
14
|
set_thread_status: {
|
|
15
|
-
description: "Set this job's workflow status. Use needs_input when blocked on the human, ready_for_review when you believe the work is done, working
|
|
15
|
+
description: "Set this job's workflow status. Use needs_input when blocked on the human, ready_for_review when you believe the work is done, working when the job is still in progress. Do not use working to mean you are currently executing — that is tracked automatically. Never mark completed or archived — only the human can.",
|
|
16
16
|
inputSchema: z.object({
|
|
17
17
|
status: z
|
|
18
18
|
.enum(AGENT_THREAD_STATUSES)
|
|
19
|
-
.describe("working — in progress; needs_input — blocked on the human; ready_for_review — you believe the job is done."),
|
|
19
|
+
.describe("working — job is in progress; needs_input — blocked on the human; ready_for_review — you believe the job is done."),
|
|
20
20
|
reason: z
|
|
21
21
|
.string()
|
|
22
22
|
.optional()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meetopenbot/openbot",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "OpenBot coordinator runtime: ask specialists, route work into Spaces, and track todos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"@ai-sdk/openai": "^3.0.13",
|
|
24
24
|
"ai": "^6.0.42",
|
|
25
25
|
"zod": "^4.3.5",
|
|
26
|
-
"@meetopenbot/plugin-sdk": "^0.
|
|
26
|
+
"@meetopenbot/plugin-sdk": "^0.3.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^20.10.1",
|
package/dist/cloud-mode.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/** True when this runtime is a platform-managed cloud deployment. */
|
|
2
|
-
export const isCloudMode = () => process.env.OPENBOT_CLOUD_MODE === '1';
|
|
3
|
-
/** Default auth mode: Credits on cloud, BYOK locally. */
|
|
4
|
-
export const defaultAuthMode = () => (isCloudMode() ? 'credits' : 'byok');
|
package/dist/credits-auth.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { isCloudMode } from './cloud-mode.js';
|
|
2
|
-
export const INTEGRATIONS_TOKEN_HEADER = 'x-openbot-integrations-token';
|
|
3
|
-
export const CREDITS_API_KEY_PLACEHOLDER = 'openbot-credits';
|
|
4
|
-
const CREDITS_PROVIDER_PATHS = {
|
|
5
|
-
openai: 'openai/v1',
|
|
6
|
-
anthropic: 'anthropic/v1',
|
|
7
|
-
google: 'google/v1',
|
|
8
|
-
deepseek: 'deepseek/v1',
|
|
9
|
-
};
|
|
10
|
-
/** Cloud host injects these when routing LLM calls through OpenBot Credits. */
|
|
11
|
-
export function resolveCreditsAuthConfig() {
|
|
12
|
-
const baseUrl = process.env.OPENBOT_INTEGRATIONS_BASE_URL?.trim();
|
|
13
|
-
const token = process.env.OPENBOT_INTEGRATIONS_TOKEN?.trim();
|
|
14
|
-
if (!baseUrl || !token)
|
|
15
|
-
return undefined;
|
|
16
|
-
return { baseUrl: baseUrl.replace(/\/$/, ''), token };
|
|
17
|
-
}
|
|
18
|
-
export function creditsProviderBaseUrl(config, provider) {
|
|
19
|
-
return `${config.baseUrl}/${CREDITS_PROVIDER_PATHS[provider]}`;
|
|
20
|
-
}
|
|
21
|
-
export function shouldUseCreditsAuth(options) {
|
|
22
|
-
if (options?.authMode === 'byok')
|
|
23
|
-
return false;
|
|
24
|
-
if (options?.authMode === 'credits')
|
|
25
|
-
return true;
|
|
26
|
-
// Cloud host injected integrations credentials — route through proxy even if authMode unset.
|
|
27
|
-
return isCloudMode() && resolveCreditsAuthConfig() !== undefined;
|
|
28
|
-
}
|
|
29
|
-
export function isCreditsErrorMessage(message) {
|
|
30
|
-
const lower = message.toLowerCase();
|
|
31
|
-
return (lower.includes('insufficient_credits') ||
|
|
32
|
-
lower.includes('insufficient credits') ||
|
|
33
|
-
lower.includes('402'));
|
|
34
|
-
}
|
|
35
|
-
export function isAuthErrorMessage(message) {
|
|
36
|
-
const lower = message.toLowerCase();
|
|
37
|
-
return (lower.includes('api key') ||
|
|
38
|
-
lower.includes('401') ||
|
|
39
|
-
lower.includes('unauthorized') ||
|
|
40
|
-
lower.includes('authentication'));
|
|
41
|
-
}
|
package/dist/model-registry.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { AUTO_MODEL_ID, AUTO_MODEL_OPTION } from './auto-model.js';
|
|
2
|
-
const DEFAULT_REGISTRY_URL = 'https://raw.githubusercontent.com/meetopenbot/openbot-registry/main/registry.json';
|
|
3
|
-
const API_KEY_PROVIDER_IDS = ['openai', 'anthropic', 'google', 'deepseek'];
|
|
4
|
-
export const PROVIDER_API_KEY_LINKS = {
|
|
5
|
-
openai: 'https://platform.openai.com/api-keys',
|
|
6
|
-
anthropic: 'https://console.anthropic.com/settings/keys',
|
|
7
|
-
google: 'https://aistudio.google.com/app/apikey',
|
|
8
|
-
deepseek: 'https://platform.deepseek.com/api_keys',
|
|
9
|
-
};
|
|
10
|
-
const FALLBACK_PROVIDERS = [
|
|
11
|
-
{ id: 'openai', label: 'OpenAI' },
|
|
12
|
-
{ id: 'anthropic', label: 'Anthropic' },
|
|
13
|
-
{ id: 'google', label: 'Google' },
|
|
14
|
-
{ id: 'deepseek', label: 'DeepSeek' },
|
|
15
|
-
];
|
|
16
|
-
let cachedRegistry;
|
|
17
|
-
export async function fetchModelRegistry() {
|
|
18
|
-
if (cachedRegistry !== undefined)
|
|
19
|
-
return cachedRegistry;
|
|
20
|
-
const url = process.env.OPENBOT_MODEL_REGISTRY_URL?.trim() || DEFAULT_REGISTRY_URL;
|
|
21
|
-
try {
|
|
22
|
-
const response = await fetch(url, {
|
|
23
|
-
headers: { Accept: 'application/json' },
|
|
24
|
-
signal: AbortSignal.timeout(15000),
|
|
25
|
-
});
|
|
26
|
-
if (!response.ok) {
|
|
27
|
-
cachedRegistry = null;
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
cachedRegistry = (await response.json());
|
|
31
|
-
return cachedRegistry;
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
cachedRegistry = null;
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
export function listApiKeyProviders(registry) {
|
|
39
|
-
if (!registry?.providers)
|
|
40
|
-
return FALLBACK_PROVIDERS;
|
|
41
|
-
const providers = API_KEY_PROVIDER_IDS.flatMap((id) => {
|
|
42
|
-
const provider = registry.providers?.[id];
|
|
43
|
-
return provider ? [{ id, label: provider.label }] : [];
|
|
44
|
-
});
|
|
45
|
-
return providers.length > 0 ? providers : FALLBACK_PROVIDERS;
|
|
46
|
-
}
|
|
47
|
-
export function getProviderModelOptions(registry, provider) {
|
|
48
|
-
const models = registry?.providers?.[provider]?.models;
|
|
49
|
-
if (!models?.length)
|
|
50
|
-
return undefined;
|
|
51
|
-
return models.map((m) => ({ label: m.label, value: m.id }));
|
|
52
|
-
}
|
|
53
|
-
function listAllModelOptions(registry) {
|
|
54
|
-
if (!registry?.providers)
|
|
55
|
-
return [];
|
|
56
|
-
const options = [];
|
|
57
|
-
for (const providerId of API_KEY_PROVIDER_IDS) {
|
|
58
|
-
const provider = registry.providers[providerId];
|
|
59
|
-
if (!provider)
|
|
60
|
-
continue;
|
|
61
|
-
for (const model of provider.models ?? []) {
|
|
62
|
-
options.push({
|
|
63
|
-
value: `${providerId}/${model.id}`,
|
|
64
|
-
label: `${provider.label} — ${model.label}`,
|
|
65
|
-
description: model.description,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return options;
|
|
70
|
-
}
|
|
71
|
-
function withAutoOption(options) {
|
|
72
|
-
if (options.some((option) => option.value === AUTO_MODEL_ID))
|
|
73
|
-
return options;
|
|
74
|
-
return [AUTO_MODEL_OPTION, ...options];
|
|
75
|
-
}
|
|
76
|
-
const freeInputModelField = () => ({
|
|
77
|
-
type: 'string',
|
|
78
|
-
description: 'Auto, or a provider model in provider/model-id format (e.g. openai/gpt-4o-mini).',
|
|
79
|
-
default: AUTO_MODEL_ID,
|
|
80
|
-
});
|
|
81
|
-
/** Registry-backed model field for plugin configSchema (`enum` + labeled `options`). */
|
|
82
|
-
export async function resolveModelConfigField() {
|
|
83
|
-
const registry = await fetchModelRegistry();
|
|
84
|
-
const options = withAutoOption(listAllModelOptions(registry));
|
|
85
|
-
if (options.length === 1)
|
|
86
|
-
return freeInputModelField();
|
|
87
|
-
return {
|
|
88
|
-
type: 'string',
|
|
89
|
-
description: 'Auto, or a model from the OpenBot registry.',
|
|
90
|
-
default: AUTO_MODEL_ID,
|
|
91
|
-
enum: options.map((option) => option.value),
|
|
92
|
-
options,
|
|
93
|
-
};
|
|
94
|
-
}
|