@meetopenbot/openbot 1.2.0 → 1.2.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/history.js +62 -38
- package/dist/runtime.js +6 -2
- package/dist/system-prompt.js +27 -0
- package/package.json +2 -2
package/dist/history.js
CHANGED
|
@@ -71,51 +71,75 @@ function appendAssistantOutput(message, text, reasoningParts) {
|
|
|
71
71
|
insertBeforeToolCalls(parts, [{ type: 'text', text }]);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
74
|
+
function isToolResultPart(part) {
|
|
75
|
+
return Boolean(part && typeof part === 'object' && part.type === 'tool-result');
|
|
76
|
+
}
|
|
77
|
+
function assistantToolCalls(message) {
|
|
78
|
+
if (message.role !== 'assistant' || !Array.isArray(message.content))
|
|
79
|
+
return [];
|
|
80
|
+
const calls = [];
|
|
81
|
+
for (const part of message.content) {
|
|
82
|
+
if (part.type !== 'tool-call')
|
|
83
|
+
continue;
|
|
84
|
+
const toolCall = part;
|
|
85
|
+
if (typeof toolCall.toolCallId !== 'string')
|
|
86
|
+
continue;
|
|
87
|
+
calls.push({
|
|
88
|
+
toolCallId: toolCall.toolCallId,
|
|
89
|
+
toolName: typeof toolCall.toolName === 'string' ? toolCall.toolName : 'tool',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return calls;
|
|
93
|
+
}
|
|
94
|
+
function collectToolResults(messages) {
|
|
95
|
+
const results = new Map();
|
|
96
|
+
for (const message of messages) {
|
|
97
|
+
if (message.role !== 'tool' || !Array.isArray(message.content))
|
|
98
|
+
continue;
|
|
99
|
+
for (const part of message.content) {
|
|
100
|
+
if (!isToolResultPart(part) || results.has(part.toolCallId))
|
|
101
|
+
continue;
|
|
102
|
+
results.set(part.toolCallId, part);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return results;
|
|
106
|
+
}
|
|
107
|
+
function toolMessageForCalls(calls, results) {
|
|
108
|
+
return {
|
|
109
|
+
role: 'tool',
|
|
110
|
+
content: calls.map(({ toolCallId, toolName }) => {
|
|
111
|
+
const existing = results.get(toolCallId);
|
|
112
|
+
if (existing) {
|
|
113
|
+
results.delete(toolCallId);
|
|
114
|
+
return existing;
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
88
117
|
type: 'tool-result',
|
|
89
118
|
toolCallId,
|
|
90
119
|
toolName,
|
|
91
120
|
output: { type: 'text', value: '' },
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
pending.clear();
|
|
121
|
+
};
|
|
122
|
+
}),
|
|
95
123
|
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Rebuild tool messages so every `tool-call` is followed immediately by matching
|
|
127
|
+
* results. Late results (user invoke between call and `:result`) are moved next
|
|
128
|
+
* to their call; unmatched results are dropped; interrupted calls get an empty
|
|
129
|
+
* result so the next LLM turn does not fail provider validation.
|
|
130
|
+
*/
|
|
131
|
+
function sanitizeToolPairing(messages) {
|
|
132
|
+
const results = collectToolResults(messages);
|
|
133
|
+
const sanitized = [];
|
|
96
134
|
for (const message of messages) {
|
|
97
|
-
if (message.role === 'tool'
|
|
98
|
-
filled.push(message);
|
|
99
|
-
for (const part of message.content) {
|
|
100
|
-
if (part.type === 'tool-result') {
|
|
101
|
-
pending.delete(part.toolCallId);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
135
|
+
if (message.role === 'tool')
|
|
104
136
|
continue;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
for (const part of message.content) {
|
|
110
|
-
if (part.type === 'tool-call') {
|
|
111
|
-
const toolCall = part;
|
|
112
|
-
pending.set(toolCall.toolCallId, toolCall.toolName);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
137
|
+
sanitized.push(message);
|
|
138
|
+
const calls = assistantToolCalls(message);
|
|
139
|
+
if (calls.length > 0)
|
|
140
|
+
sanitized.push(toolMessageForCalls(calls, results));
|
|
116
141
|
}
|
|
117
|
-
|
|
118
|
-
return filled;
|
|
142
|
+
return sanitized;
|
|
119
143
|
}
|
|
120
144
|
function toolResultText(data) {
|
|
121
145
|
if (data && typeof data === 'object') {
|
|
@@ -235,5 +259,5 @@ export function eventsToModelMessages(events, options) {
|
|
|
235
259
|
break;
|
|
236
260
|
}
|
|
237
261
|
}
|
|
238
|
-
return
|
|
262
|
+
return sanitizeToolPairing(messages);
|
|
239
263
|
}
|
package/dist/runtime.js
CHANGED
|
@@ -3,14 +3,18 @@ import { BYOK_ENV_KEYS, isAuthErrorMessage, isCreditsErrorMessage, fetchModelReg
|
|
|
3
3
|
import { eventsToModelMessages } from './history.js';
|
|
4
4
|
import { addImageParts, eventsHaveImages, pickVisionModel } from './images.js';
|
|
5
5
|
import { buildContext } from './context.js';
|
|
6
|
-
import { OPENBOT_SYSTEM_PROMPT, withUserInstructions } from './system-prompt.js';
|
|
6
|
+
import { OPENBOT_SYSTEM_PROMPT, withGithubGitInstructions, withUserInstructions, } from './system-prompt.js';
|
|
7
7
|
import { AUTO_MODEL_ID, isAutoModel, isRetryableModelError, listAutoModelCandidates, missingByokApiKeyMessage, } from './auto-model.js';
|
|
8
8
|
import { reasoningForModel, resolveModel } from './model.js';
|
|
9
9
|
import { errorFromStreamPart } from './stream-error.js';
|
|
10
10
|
import { createAgentOutputBuffer, isOutputBoundaryPart } from './output-buffer.js';
|
|
11
11
|
async function buildSystemPrompt(state, storage, instructions, host) {
|
|
12
12
|
const context = await buildContext(state, storage, host);
|
|
13
|
-
const sections = [
|
|
13
|
+
const sections = [
|
|
14
|
+
withGithubGitInstructions(withUserInstructions(OPENBOT_SYSTEM_PROMPT, instructions)),
|
|
15
|
+
'',
|
|
16
|
+
context,
|
|
17
|
+
];
|
|
14
18
|
const threadState = state.threadDetails?.state;
|
|
15
19
|
if (!threadState?.isSmartNamed) {
|
|
16
20
|
sections.push('', '## SYSTEM HINT', 'This thread is unnamed. Please use the `set_thread_title` tool to set a concise, descriptive title (e.g., "Project Brainstorming" instead of "project-brainstorm"). Only do this once.');
|
package/dist/system-prompt.js
CHANGED
|
@@ -49,6 +49,12 @@ export const OPENBOT_SYSTEM_PROMPT = [
|
|
|
49
49
|
"- **Hub-and-spoke**: Specialists cannot talk to each other. You pass context from one to the next.",
|
|
50
50
|
"- **Durable Memory**: Use the `remember` tool to store important facts, preferences, or project details that should persist across sessions.",
|
|
51
51
|
"",
|
|
52
|
+
"# CLOUD COMPUTER",
|
|
53
|
+
"This runtime is a remote cloud computer. The human is in the browser at openbot.one and cannot open localhost, 127.0.0.1, or any port on this machine.",
|
|
54
|
+
"- Never tell them to visit localhost. They need a public HTTPS URL.",
|
|
55
|
+
"- When you ask a coding agent to run a dev server, preview, or any local process the human should open, tell them to expose it with `cloudflared tunnel --url http://127.0.0.1:<port>` and to add `cloudflared` as a **project devDependency** (not a global install — this image is ephemeral and only workspace files persist).",
|
|
56
|
+
"- Share the `https://*.trycloudflare.com` URL they print. Quick tunnels change on restart — pass the new one through. If they report a localhost URL, ask them to tunnel it.",
|
|
57
|
+
"",
|
|
52
58
|
"# COMMUNICATION & WRITING STYLE",
|
|
53
59
|
"- Be concise, professional, proactive, and polite.",
|
|
54
60
|
"- Confirm receipt quickly. For longer work, say where you are taking it and who you will ask.",
|
|
@@ -61,6 +67,27 @@ export const OPENBOT_SYSTEM_PROMPT = [
|
|
|
61
67
|
"- If an asked agent fails, try a different agent or tell the human what blocked them.",
|
|
62
68
|
"- Only ask the human for help after you cannot route or ask anyone who can do the work.",
|
|
63
69
|
].join("\n");
|
|
70
|
+
/**
|
|
71
|
+
* Appended when `GITHUB_TOKEN` is in the runtime env (workspace variables are
|
|
72
|
+
* merged in). Git does not read that variable; coding agents need an inline hint.
|
|
73
|
+
*/
|
|
74
|
+
export const GITHUB_GIT_INSTRUCTIONS = [
|
|
75
|
+
"# GITHUB GIT",
|
|
76
|
+
"`GITHUB_TOKEN` is set in this runtime's environment. Git does not read it automatically.",
|
|
77
|
+
"When you `ask_agent` a coding agent (Claude Code, Codex, Cursor, OpenCode, Pi) to clone, fetch, pull, or push GitHub, tell them in that prompt:",
|
|
78
|
+
"- `GITHUB_TOKEN` is already in their environment. Never paste the token value into the prompt, chat, or a command you write out.",
|
|
79
|
+
"- Use it inline and keep `$GITHUB_TOKEN` unexpanded, e.g. `git push https://x-access-token:$GITHUB_TOKEN@github.com/<owner>/<repo>.git <branch>`.",
|
|
80
|
+
"- Do not write the token into a git remote (`.git/config`) or print `git remote -v` after embedding it.",
|
|
81
|
+
].join("\n");
|
|
82
|
+
export function githubTokenIsSet(env = process.env) {
|
|
83
|
+
return Boolean(env.GITHUB_TOKEN?.trim());
|
|
84
|
+
}
|
|
85
|
+
/** Append GitHub git guidance when `GITHUB_TOKEN` is present. */
|
|
86
|
+
export function withGithubGitInstructions(basePrompt, env = process.env) {
|
|
87
|
+
if (!githubTokenIsSet(env))
|
|
88
|
+
return basePrompt;
|
|
89
|
+
return `${basePrompt}\n\n${GITHUB_GIT_INSTRUCTIONS}`;
|
|
90
|
+
}
|
|
64
91
|
/** Max characters accepted from the OpenBot plugin `instructions` config field. */
|
|
65
92
|
export const MAX_USER_INSTRUCTIONS_LENGTH = 4000;
|
|
66
93
|
/** Trim and cap user-provided coordinator instructions. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meetopenbot/openbot",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
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.46",
|
|
26
26
|
"ai": "^7.0.77",
|
|
27
27
|
"zod": "^4.3.5",
|
|
28
|
-
"@meetopenbot/plugin-sdk": "^1.1.
|
|
28
|
+
"@meetopenbot/plugin-sdk": "^1.1.3"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^20.10.1",
|