@meetopenbot/openbot 1.3.2 → 1.4.0
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/build-tools.js +3 -1
- package/dist/images.js +4 -3
- package/dist/model.js +1 -1
- package/dist/system-prompt.js +2 -2
- package/dist/tools/start-thread.js +120 -0
- package/dist/tools/storage.js +1 -1
- package/package.json +2 -2
package/dist/auto-model.js
CHANGED
|
@@ -10,7 +10,7 @@ export const AUTO_MODEL_OPTION = {
|
|
|
10
10
|
* Auto currently always uses this model. Restore a cheap pool (preference
|
|
11
11
|
* order, failover, intent routing) without changing the `openbot/auto` alias.
|
|
12
12
|
*/
|
|
13
|
-
export const AUTO_MODEL_POOL = ['
|
|
13
|
+
export const AUTO_MODEL_POOL = ['anthropic/claude-haiku-5-5'];
|
|
14
14
|
function providerByokKeys(provider) {
|
|
15
15
|
return BYOK_ENV_KEYS[provider];
|
|
16
16
|
}
|
package/dist/build-tools.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { memoryTools } from "./tools/memory.js";
|
|
2
2
|
import { todoTools } from "./tools/todo.js";
|
|
3
3
|
import { storageTools } from "./tools/storage.js";
|
|
4
|
+
import { startThreadTools } from "./tools/start-thread.js";
|
|
4
5
|
import { askAgentTools } from "./tools/ask-agent.js";
|
|
5
6
|
import { installAgentTools } from "./tools/install-agent.js";
|
|
6
7
|
import { threadStatusTools } from "./tools/thread-status.js";
|
|
7
8
|
import { threadTitleTools } from "./tools/thread-title.js";
|
|
8
9
|
import { previewTools } from "./tools/preview.js";
|
|
9
10
|
import { wrapToolsWithApproval } from "./tools/approval.js";
|
|
10
|
-
export const DEFAULT_APPROVAL_ACTIONS = ["update_channel_spec"];
|
|
11
|
+
export const DEFAULT_APPROVAL_ACTIONS = ["update_channel_spec", "start_thread"];
|
|
11
12
|
/**
|
|
12
13
|
* Merge first-party OpenBot coordinator tools and wrap configured actions
|
|
13
14
|
* with approval.
|
|
@@ -17,6 +18,7 @@ export function buildOpenbotTools(args) {
|
|
|
17
18
|
...memoryTools,
|
|
18
19
|
...todoTools,
|
|
19
20
|
...storageTools,
|
|
21
|
+
...startThreadTools,
|
|
20
22
|
...askAgentTools,
|
|
21
23
|
...installAgentTools,
|
|
22
24
|
...threadStatusTools,
|
package/dist/images.js
CHANGED
|
@@ -31,7 +31,7 @@ export function modelSupportsImages(modelString, ctx) {
|
|
|
31
31
|
function hasAnyKey(env, keys) {
|
|
32
32
|
return Boolean(keys?.some((key) => Boolean(env[key]?.trim())));
|
|
33
33
|
}
|
|
34
|
-
/**
|
|
34
|
+
/** Cheap vision model for when Auto's pool model cannot see pictures. */
|
|
35
35
|
export function pickVisionModel(authMode, env = process.env) {
|
|
36
36
|
if (shouldUseCreditsAuth({ authMode }))
|
|
37
37
|
return 'google/gemini-3.8-flash';
|
|
@@ -50,12 +50,13 @@ function invokeRole(role) {
|
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
52
|
* Models to try this turn. Auto switches to a cheap vision model only when the
|
|
53
|
-
* newest user message has an image
|
|
53
|
+
* newest user message has an image, Auto's own model cannot see it, and a vision
|
|
54
|
+
* model is available. Never throws.
|
|
54
55
|
*/
|
|
55
56
|
export function chooseTurnCandidates({ model, events, authMode, env = process.env, }) {
|
|
56
57
|
if (!isAutoModel(model))
|
|
57
58
|
return [model.trim()];
|
|
58
|
-
if (latestUserMessageHasImages(events)) {
|
|
59
|
+
if (latestUserMessageHasImages(events) && !modelSupportsImages(model, { authMode, env })) {
|
|
59
60
|
const vision = pickVisionModel(authMode, env);
|
|
60
61
|
if (vision)
|
|
61
62
|
return [vision];
|
package/dist/model.js
CHANGED
|
@@ -77,7 +77,7 @@ export function resolveModel(modelString, options) {
|
|
|
77
77
|
function providerOf(modelString) {
|
|
78
78
|
return expandModelString(modelString).split('/')[0] ?? '';
|
|
79
79
|
}
|
|
80
|
-
/** Low thinking for DeepSeek
|
|
80
|
+
/** Low thinking for DeepSeek. Other providers use their SDK defaults. */
|
|
81
81
|
export function reasoningForModel(modelString) {
|
|
82
82
|
if (providerOf(modelString) === 'deepseek')
|
|
83
83
|
return 'low';
|
package/dist/system-prompt.js
CHANGED
|
@@ -15,7 +15,7 @@ export const OPENBOT_SYSTEM_PROMPT = [
|
|
|
15
15
|
"# THE AGENT LOOP",
|
|
16
16
|
"You operate in an iterative loop:",
|
|
17
17
|
"1. **Analyze Events**: Understand the human, mentioned people, the current Space, and `## SPACES`.",
|
|
18
|
-
"2. **Right Space**: Use `## SPACES` to see where work belongs. If the human's intent clearly fits a different existing Space than the one you are in — especially project work posted in `#general` — explain in text which Space it belongs in and why
|
|
18
|
+
"2. **Right Space**: Use `## SPACES` to see where work belongs. If the human's intent clearly fits a different existing Space than the one you are in — especially project work posted in `#general` — explain in text which Space it belongs in and why. Then either wait for the human to continue there, or — when the request is clear enough to act on — call `start_thread` to open a thread in that Space with their request; the human approves it before it starts. Never start a thread in the Space you are already in. In `#general`, handle questions, listings, setup, and routing only; do not run project execution there. If they are already in the right Space, continue with `ask_agent`. If no Space fits, propose a concrete name and why, then wait for the human to agree before `create_channel` — unless they already named a new Space or explicitly asked you to create it. Never invent placeholder ids (`noop`, `temp`, `misc`, `untitled`).",
|
|
19
19
|
"3. **Ask people**: Use `ask_agent` to send a message to a specialist. Write it as a request to a colleague, not as a tool invocation.",
|
|
20
20
|
"4. **Wait**: Let the asked agent run in its own harness. Read their reply, then continue or ask someone else.",
|
|
21
21
|
"5. **Close**: The human already saw the specialist speak in this thread. Do not restate, re-bullet, or paraphrase their findings. Name who you asked, then add only what is new — a decision, a conflict, a next step, or a caveat. If nothing is new, close in one or two sentences.",
|
|
@@ -42,7 +42,7 @@ export const OPENBOT_SYSTEM_PROMPT = [
|
|
|
42
42
|
"- The current status is injected each turn as `## THREAD STATUS`.",
|
|
43
43
|
"",
|
|
44
44
|
"# OPERATIONAL GUIDELINES",
|
|
45
|
-
'- **Spaces**: Work lives in Spaces (product name for channels). `#general` is dispatch — setup, questions, listings, routing, and "what should we do next." Project, coding, design, and ops work belongs in a dedicated Space. Prefer existing Spaces. When the human is in the wrong existing Space, explain the appropriate Space in text
|
|
45
|
+
'- **Spaces**: Work lives in Spaces (product name for channels). `#general` is dispatch — setup, questions, listings, routing, and "what should we do next." Project, coding, design, and ops work belongs in a dedicated Space. Prefer existing Spaces. When the human is in the wrong existing Space, explain the appropriate Space in text, then let them continue there or hand it over with `start_thread`. Never auto-move threads or messages; `start_thread` opens a new thread and leaves this one in place. Create a new Space only when the human asked for one, or they agreed after you suggested it.',
|
|
46
46
|
"- **Asking**: You ask specialists with `ask_agent`. If `## MENTIONED AGENTS` is present, you MUST ask those people. You may also ask others if needed. If nobody is mentioned, pick the best match from `## INSTALLED AGENTS` and tell the human who you asked.",
|
|
47
47
|
"- **Models**: When you `ask_agent`, pick `model` from that agent's list in `## INSTALLED AGENTS` using the descriptions — cheaper/faster for simple work, stronger for complex coding or reasoning. Omit `model` to use their default. Do not reuse another agent's ids.",
|
|
48
48
|
"- **Closer**: After `ask_agent`, you are the closer, not a second author of their work. Don't repeat. Add or close. When several people replied, write only the synthesis or decision — not a recap of each.",
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import z from 'zod';
|
|
3
|
+
import { isPlaceholderSpaceId } from '../space-id.js';
|
|
4
|
+
import { normalizeChannelId } from './storage.js';
|
|
5
|
+
async function emitStartThreadWidget(ctx, args) {
|
|
6
|
+
const actions = args.state === 'submitted' && args.threadUrl
|
|
7
|
+
? [
|
|
8
|
+
{
|
|
9
|
+
id: 'navigate',
|
|
10
|
+
label: `Open in #${args.channelId}`,
|
|
11
|
+
variant: 'primary',
|
|
12
|
+
value: { path: args.threadUrl },
|
|
13
|
+
},
|
|
14
|
+
]
|
|
15
|
+
: undefined;
|
|
16
|
+
await ctx.emit({
|
|
17
|
+
type: 'client:ui:widget',
|
|
18
|
+
data: {
|
|
19
|
+
widgetId: args.widgetId,
|
|
20
|
+
kind: 'message',
|
|
21
|
+
title: 'Thread started',
|
|
22
|
+
description: args.title ?? (args.channelId ? `#${args.channelId}` : undefined),
|
|
23
|
+
body: args.body,
|
|
24
|
+
display: args.state === 'submitted' ? 'expanded' : 'collapsed',
|
|
25
|
+
state: args.state,
|
|
26
|
+
...(actions ? { actions } : {}),
|
|
27
|
+
metadata: { type: 'thread_started', channelId: args.channelId || undefined },
|
|
28
|
+
},
|
|
29
|
+
meta: {
|
|
30
|
+
agentId: ctx.agentId,
|
|
31
|
+
threadId: ctx.threadId ?? ctx.state.threadId,
|
|
32
|
+
parentToolCallId: ctx.toolCallId,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export const startThreadTools = {
|
|
37
|
+
start_thread: {
|
|
38
|
+
description: "Start a new thread in another existing Space and hand the human's request to that Space's agent. Use when work asked here clearly belongs in a different Space from `## SPACES`. The new thread runs on its own; the human follows it there. Never use it for the Space you are already in.",
|
|
39
|
+
inputSchema: z.object({
|
|
40
|
+
channelId: z.string().describe('Existing Space id from `## SPACES` (e.g. backend-platform).'),
|
|
41
|
+
prompt: z
|
|
42
|
+
.string()
|
|
43
|
+
.describe("First message of the new thread, written as the human's request with the context the Space's agent needs. It does not see this conversation."),
|
|
44
|
+
title: z.string().optional().describe('Optional concise thread title.'),
|
|
45
|
+
}),
|
|
46
|
+
execute: async (raw, ctx) => {
|
|
47
|
+
const { channelId, prompt, title } = (raw ?? {});
|
|
48
|
+
const targetChannelId = normalizeChannelId(channelId || '');
|
|
49
|
+
const content = typeof prompt === 'string' ? prompt.trim() : '';
|
|
50
|
+
const threadTitle = typeof title === 'string' && title.trim() ? title.trim() : undefined;
|
|
51
|
+
const widgetId = ctx.toolCallId ?? `start_thread:${targetChannelId || Date.now()}`;
|
|
52
|
+
const fail = async (error) => {
|
|
53
|
+
await emitStartThreadWidget(ctx, {
|
|
54
|
+
widgetId,
|
|
55
|
+
channelId: targetChannelId,
|
|
56
|
+
title: threadTitle,
|
|
57
|
+
body: error,
|
|
58
|
+
state: 'error',
|
|
59
|
+
});
|
|
60
|
+
return { success: false, channelId: targetChannelId, error, output: error };
|
|
61
|
+
};
|
|
62
|
+
if (ctx.agentId !== ctx.host.orchestratorAgentId) {
|
|
63
|
+
return fail('Only OpenBot can start threads in other Spaces.');
|
|
64
|
+
}
|
|
65
|
+
if (!targetChannelId)
|
|
66
|
+
return fail('channelId is required');
|
|
67
|
+
if (!content)
|
|
68
|
+
return fail('prompt is required');
|
|
69
|
+
if (isPlaceholderSpaceId(targetChannelId)) {
|
|
70
|
+
return fail(`Refused to start a thread in #${targetChannelId}: that is not a real Space.`);
|
|
71
|
+
}
|
|
72
|
+
if (targetChannelId === (ctx.channelId ?? ctx.state.channelId)) {
|
|
73
|
+
return fail(`You are already in #${targetChannelId}. Continue here instead of starting a thread.`);
|
|
74
|
+
}
|
|
75
|
+
if (!ctx.host.startThreadRun) {
|
|
76
|
+
return fail('This runtime cannot start threads in other Spaces yet. Update OpenBot.');
|
|
77
|
+
}
|
|
78
|
+
const spaces = await ctx.storage.getChannels();
|
|
79
|
+
if (!spaces.some((space) => space.id === targetChannelId)) {
|
|
80
|
+
return fail(`#${targetChannelId} does not exist. Pick a Space from \`## SPACES\`, or propose a new one.`);
|
|
81
|
+
}
|
|
82
|
+
const threadId = randomUUID();
|
|
83
|
+
const threadUrl = `/channels/${targetChannelId}/threads/${threadId}`;
|
|
84
|
+
await emitStartThreadWidget(ctx, {
|
|
85
|
+
widgetId,
|
|
86
|
+
channelId: targetChannelId,
|
|
87
|
+
title: threadTitle,
|
|
88
|
+
body: 'Starting…',
|
|
89
|
+
state: 'open',
|
|
90
|
+
});
|
|
91
|
+
try {
|
|
92
|
+
await ctx.storage.createThread({
|
|
93
|
+
channelId: targetChannelId,
|
|
94
|
+
threadId,
|
|
95
|
+
...(threadTitle ? { threadTitle } : {}),
|
|
96
|
+
initialState: {},
|
|
97
|
+
});
|
|
98
|
+
const { agentId } = await ctx.host.startThreadRun({
|
|
99
|
+
channelId: targetChannelId,
|
|
100
|
+
threadId,
|
|
101
|
+
content,
|
|
102
|
+
});
|
|
103
|
+
await emitStartThreadWidget(ctx, {
|
|
104
|
+
widgetId,
|
|
105
|
+
channelId: targetChannelId,
|
|
106
|
+
title: threadTitle,
|
|
107
|
+
body: `Started a thread in #${targetChannelId}.`,
|
|
108
|
+
state: 'submitted',
|
|
109
|
+
threadUrl,
|
|
110
|
+
});
|
|
111
|
+
const output = `Started thread ${threadId} in #${targetChannelId}; ${agentId} is working on it there. The human already sees a link to it, so do not restate the request; just say the work continues in #${targetChannelId}.`;
|
|
112
|
+
return { success: true, channelId: targetChannelId, threadId, threadUrl, output };
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
116
|
+
return fail(`Failed to start thread: ${message}`);
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
};
|
package/dist/tools/storage.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meetopenbot/openbot",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
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",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@ai-sdk/openai": "^4.0.72",
|
|
26
26
|
"ai": "^7.0.111",
|
|
27
27
|
"zod": "^4.3.5",
|
|
28
|
-
"@meetopenbot/plugin-sdk": "^1.
|
|
28
|
+
"@meetopenbot/plugin-sdk": "^1.5.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^20.10.1",
|