@meetopenbot/openbot 0.1.14 → 0.2.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 +67 -0
- package/dist/context.js +119 -37
- package/dist/history.js +2 -1
- package/dist/index.js +47 -29
- package/dist/model-registry.js +12 -11
- package/dist/model.js +14 -4
- package/dist/runtime.js +80 -45
- package/dist/space-id.js +22 -0
- package/dist/system-prompt.js +54 -55
- package/dist/tools/ask-agent.js +123 -0
- package/dist/tools/start-work.js +262 -0
- package/dist/tools/storage.js +135 -98
- package/dist/tools/thread-status.js +91 -0
- package/package.json +8 -9
- package/dist/tools/delegation.js +0 -137
package/dist/tools/storage.js
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
|
-
import z from
|
|
2
|
-
import { asActionBuilder } from
|
|
3
|
-
import { buildWorkspaceFileUrl } from
|
|
1
|
+
import z from "zod";
|
|
2
|
+
import { asActionBuilder } from "../types.js";
|
|
3
|
+
import { buildWorkspaceFileUrl } from "../utils/workspace-url.js";
|
|
4
|
+
import { isPlaceholderSpaceId, placeholderSpaceError, } from "../space-id.js";
|
|
4
5
|
function normalizeChannelId(raw) {
|
|
5
6
|
return raw
|
|
6
7
|
.trim()
|
|
7
8
|
.toLowerCase()
|
|
8
|
-
.replace(/[^a-z0-9]+/g,
|
|
9
|
-
.replace(/(^-|-$)/g,
|
|
9
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
10
|
+
.replace(/(^-|-$)/g, "");
|
|
10
11
|
}
|
|
11
12
|
function displayNameFromChannelId(channelId) {
|
|
12
13
|
return channelId
|
|
13
|
-
.split(
|
|
14
|
+
.split("-")
|
|
14
15
|
.filter(Boolean)
|
|
15
16
|
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
16
|
-
.join(
|
|
17
|
+
.join(" ");
|
|
17
18
|
}
|
|
18
19
|
function createChannelWidget(args) {
|
|
19
20
|
return {
|
|
20
|
-
type:
|
|
21
|
+
type: "client:ui:widget",
|
|
21
22
|
data: {
|
|
22
23
|
widgetId: args.widgetId,
|
|
23
|
-
kind:
|
|
24
|
-
title:
|
|
24
|
+
kind: "message",
|
|
25
|
+
title: "create_channel",
|
|
25
26
|
description: args.channelId || undefined,
|
|
26
27
|
body: args.body,
|
|
27
|
-
display:
|
|
28
|
+
display: "collapsed",
|
|
28
29
|
state: args.state,
|
|
29
30
|
},
|
|
30
31
|
meta: args.meta,
|
|
@@ -32,71 +33,79 @@ function createChannelWidget(args) {
|
|
|
32
33
|
}
|
|
33
34
|
const storageToolDefinitions = {
|
|
34
35
|
create_channel: {
|
|
35
|
-
description:
|
|
36
|
+
description: "Create a new Space after the human asked for it or agreed to your suggestion. Prefer an existing Space and start_work. Skip questions, listings, and Q&A in #general. Never use placeholder ids (noop, temp, misc).",
|
|
36
37
|
inputSchema: z.object({
|
|
37
38
|
channelId: z
|
|
38
39
|
.string()
|
|
39
|
-
.describe(
|
|
40
|
+
.describe("Unique Space id (e.g. product-launch, backend-platform)."),
|
|
40
41
|
name: z
|
|
41
42
|
.string()
|
|
42
43
|
.optional()
|
|
43
|
-
.describe(
|
|
44
|
+
.describe("Optional display name. Defaults to a title-cased Space id."),
|
|
44
45
|
spec: z
|
|
45
46
|
.string()
|
|
46
47
|
.optional()
|
|
47
|
-
.describe(
|
|
48
|
+
.describe("Optional initial markdown content for the channel spec."),
|
|
48
49
|
initialState: z
|
|
49
50
|
.record(z.string(), z.unknown())
|
|
50
51
|
.optional()
|
|
51
|
-
.describe(
|
|
52
|
+
.describe("Optional initial state object for the channel. Do not set cwd; the runtime assigns the workspace path."),
|
|
52
53
|
}),
|
|
53
54
|
},
|
|
54
55
|
patch_channel_details: {
|
|
55
|
-
description:
|
|
56
|
+
description: "Patch the current Space (state, spec, cwd).",
|
|
56
57
|
inputSchema: z
|
|
57
58
|
.object({
|
|
58
59
|
state: z
|
|
59
60
|
.record(z.string(), z.unknown())
|
|
60
61
|
.optional()
|
|
61
|
-
.describe(
|
|
62
|
+
.describe("JSON state object for the channel."),
|
|
62
63
|
spec: z
|
|
63
64
|
.string()
|
|
64
65
|
.optional()
|
|
65
|
-
.describe(
|
|
66
|
-
cwd: z
|
|
66
|
+
.describe("Markdown content for the channel specification (SPEC.md). Use for goals and rules."),
|
|
67
|
+
cwd: z
|
|
68
|
+
.string()
|
|
69
|
+
.optional()
|
|
70
|
+
.describe("Current working directory for the channel."),
|
|
67
71
|
})
|
|
68
|
-
.refine((value) => value.state !== undefined ||
|
|
72
|
+
.refine((value) => value.state !== undefined ||
|
|
73
|
+
value.spec !== undefined ||
|
|
74
|
+
value.cwd !== undefined, { message: "Provide at least one of state, spec, or cwd." }),
|
|
69
75
|
},
|
|
70
76
|
patch_thread_details: {
|
|
71
|
-
description:
|
|
77
|
+
description: "Patch current thread details (state). Use for thread metadata such as `name` or `isSmartNamed`. For job status use `set_thread_status`. For multi-step task tracking, use `todo_write`.",
|
|
72
78
|
inputSchema: z.object({
|
|
73
79
|
state: z
|
|
74
80
|
.record(z.string(), z.unknown())
|
|
75
|
-
.describe(
|
|
81
|
+
.describe("JSON state object for the thread. Merges with existing state."),
|
|
76
82
|
}),
|
|
77
83
|
},
|
|
78
84
|
create_variable: {
|
|
79
|
-
description:
|
|
85
|
+
description: "Create or update a variable in the workspace storage.",
|
|
80
86
|
inputSchema: z.object({
|
|
81
|
-
key: z.string().describe(
|
|
82
|
-
value: z.string().describe(
|
|
83
|
-
secret: z
|
|
87
|
+
key: z.string().describe("The key of the variable."),
|
|
88
|
+
value: z.string().describe("The value of the variable."),
|
|
89
|
+
secret: z
|
|
90
|
+
.boolean()
|
|
91
|
+
.optional()
|
|
92
|
+
.describe("Whether the variable is a secret."),
|
|
84
93
|
}),
|
|
85
94
|
},
|
|
86
95
|
delete_variable: {
|
|
87
|
-
description:
|
|
96
|
+
description: "Delete a variable from the workspace storage.",
|
|
88
97
|
inputSchema: z.object({
|
|
89
|
-
key: z.string().describe(
|
|
98
|
+
key: z.string().describe("The key of the variable to delete."),
|
|
90
99
|
}),
|
|
91
100
|
},
|
|
92
101
|
delete_channel: {
|
|
93
|
-
description:
|
|
102
|
+
description: "Permanently delete a Space and all its threads. Always confirm with the user before deleting.",
|
|
94
103
|
inputSchema: z.object({
|
|
95
|
-
channelId: z.string().describe(
|
|
104
|
+
channelId: z.string().describe("The channel ID to delete."),
|
|
96
105
|
}),
|
|
97
106
|
},
|
|
98
107
|
get_workspace_file_url: {
|
|
99
|
-
description:
|
|
108
|
+
description: "Get a fetchable HTTP URL for a file in the current channel workspace (images, video, audio, documents).",
|
|
100
109
|
inputSchema: z.object({
|
|
101
110
|
path: z
|
|
102
111
|
.string()
|
|
@@ -110,32 +119,54 @@ export function registerStorageTools(context) {
|
|
|
110
119
|
const resolvePublicBaseUrl = () => publicBaseUrl;
|
|
111
120
|
return (builder) => {
|
|
112
121
|
const actions = asActionBuilder(builder);
|
|
113
|
-
actions.on(
|
|
122
|
+
actions.on("create_channel", async function* (event, context) {
|
|
114
123
|
const { channelId, name, spec, initialState } = event.data;
|
|
115
|
-
const rawChannelId = normalizeChannelId(channelId ||
|
|
124
|
+
const rawChannelId = normalizeChannelId(channelId || "");
|
|
116
125
|
const resultMeta = {
|
|
117
126
|
...(event.meta || {}),
|
|
118
127
|
agentId: context.state.agentId,
|
|
119
128
|
threadId: event.meta?.threadId || context.state.threadId,
|
|
120
129
|
};
|
|
121
|
-
const widgetId = typeof event.meta?.toolCallId ===
|
|
130
|
+
const widgetId = typeof event.meta?.toolCallId === "string"
|
|
122
131
|
? event.meta.toolCallId
|
|
123
132
|
: `create_channel:${rawChannelId || Date.now()}`;
|
|
124
133
|
if (!rawChannelId) {
|
|
125
|
-
const error =
|
|
134
|
+
const error = "channelId is required";
|
|
135
|
+
yield createChannelWidget({
|
|
136
|
+
widgetId,
|
|
137
|
+
channelId: "",
|
|
138
|
+
body: error,
|
|
139
|
+
state: "error",
|
|
140
|
+
meta: resultMeta,
|
|
141
|
+
});
|
|
142
|
+
yield {
|
|
143
|
+
type: "action:create_channel:result",
|
|
144
|
+
data: {
|
|
145
|
+
success: false,
|
|
146
|
+
channelId: "",
|
|
147
|
+
channelUrl: "",
|
|
148
|
+
error,
|
|
149
|
+
output: error,
|
|
150
|
+
},
|
|
151
|
+
meta: resultMeta,
|
|
152
|
+
};
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (isPlaceholderSpaceId(rawChannelId)) {
|
|
156
|
+
const error = placeholderSpaceError(rawChannelId);
|
|
126
157
|
yield createChannelWidget({
|
|
127
158
|
widgetId,
|
|
128
|
-
channelId:
|
|
159
|
+
channelId: rawChannelId,
|
|
129
160
|
body: error,
|
|
130
|
-
state:
|
|
161
|
+
state: "error",
|
|
131
162
|
meta: resultMeta,
|
|
132
163
|
});
|
|
133
164
|
yield {
|
|
134
|
-
type:
|
|
165
|
+
type: "action:create_channel:result",
|
|
135
166
|
data: {
|
|
136
167
|
success: false,
|
|
137
|
-
channelId:
|
|
138
|
-
channelUrl:
|
|
168
|
+
channelId: rawChannelId,
|
|
169
|
+
channelUrl: "",
|
|
139
170
|
error,
|
|
140
171
|
output: error,
|
|
141
172
|
},
|
|
@@ -144,9 +175,9 @@ export function registerStorageTools(context) {
|
|
|
144
175
|
return;
|
|
145
176
|
}
|
|
146
177
|
const channelUrl = `/channels/${rawChannelId}`;
|
|
147
|
-
const displayName = typeof name ===
|
|
178
|
+
const displayName = typeof name === "string" && name.trim()
|
|
148
179
|
? name.trim()
|
|
149
|
-
: typeof initialState?.name ===
|
|
180
|
+
: typeof initialState?.name === "string" && initialState.name.trim()
|
|
150
181
|
? initialState.name.trim()
|
|
151
182
|
: displayNameFromChannelId(rawChannelId);
|
|
152
183
|
const mergedInitial = {
|
|
@@ -154,12 +185,12 @@ export function registerStorageTools(context) {
|
|
|
154
185
|
name: displayName,
|
|
155
186
|
};
|
|
156
187
|
delete mergedInitial.cwd;
|
|
157
|
-
const channelSpec = typeof spec ===
|
|
188
|
+
const channelSpec = typeof spec === "string" && spec.trim() ? spec : `# ${displayName}\n\n`;
|
|
158
189
|
yield createChannelWidget({
|
|
159
190
|
widgetId,
|
|
160
191
|
channelId: rawChannelId,
|
|
161
|
-
body:
|
|
162
|
-
state:
|
|
192
|
+
body: "Creating…",
|
|
193
|
+
state: "open",
|
|
163
194
|
meta: resultMeta,
|
|
164
195
|
});
|
|
165
196
|
try {
|
|
@@ -173,27 +204,27 @@ export function registerStorageTools(context) {
|
|
|
173
204
|
widgetId,
|
|
174
205
|
channelId: rawChannelId,
|
|
175
206
|
body: output,
|
|
176
|
-
state:
|
|
207
|
+
state: "submitted",
|
|
177
208
|
meta: resultMeta,
|
|
178
209
|
});
|
|
179
210
|
yield {
|
|
180
|
-
type:
|
|
211
|
+
type: "action:create_channel:result",
|
|
181
212
|
data: { success: true, channelId: rawChannelId, channelUrl, output },
|
|
182
213
|
meta: resultMeta,
|
|
183
214
|
};
|
|
184
215
|
}
|
|
185
216
|
catch (error) {
|
|
186
|
-
const message = error instanceof Error ? error.message :
|
|
217
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
187
218
|
const output = `Failed to create channel: ${message}`;
|
|
188
219
|
yield createChannelWidget({
|
|
189
220
|
widgetId,
|
|
190
221
|
channelId: rawChannelId,
|
|
191
222
|
body: output,
|
|
192
|
-
state:
|
|
223
|
+
state: "error",
|
|
193
224
|
meta: resultMeta,
|
|
194
225
|
});
|
|
195
226
|
yield {
|
|
196
|
-
type:
|
|
227
|
+
type: "action:create_channel:result",
|
|
197
228
|
data: {
|
|
198
229
|
success: false,
|
|
199
230
|
channelId: rawChannelId,
|
|
@@ -205,19 +236,19 @@ export function registerStorageTools(context) {
|
|
|
205
236
|
};
|
|
206
237
|
}
|
|
207
238
|
});
|
|
208
|
-
actions.on(
|
|
209
|
-
const rawChannelId = (event.data?.channelId ||
|
|
239
|
+
actions.on("delete_channel", async function* (event, context) {
|
|
240
|
+
const rawChannelId = (event.data?.channelId || "").trim();
|
|
210
241
|
const resultMeta = {
|
|
211
242
|
...(event.meta || {}),
|
|
212
243
|
agentId: context.state.agentId,
|
|
213
244
|
};
|
|
214
245
|
if (!rawChannelId) {
|
|
215
246
|
yield {
|
|
216
|
-
type:
|
|
247
|
+
type: "action:delete_channel:result",
|
|
217
248
|
data: {
|
|
218
249
|
success: false,
|
|
219
|
-
channelId:
|
|
220
|
-
error:
|
|
250
|
+
channelId: "",
|
|
251
|
+
error: "channelId is required",
|
|
221
252
|
},
|
|
222
253
|
meta: resultMeta,
|
|
223
254
|
};
|
|
@@ -226,41 +257,43 @@ export function registerStorageTools(context) {
|
|
|
226
257
|
try {
|
|
227
258
|
await storage.deleteChannel({ channelId: rawChannelId });
|
|
228
259
|
yield {
|
|
229
|
-
type:
|
|
260
|
+
type: "action:delete_channel:result",
|
|
230
261
|
data: { success: true, channelId: rawChannelId },
|
|
231
262
|
meta: resultMeta,
|
|
232
263
|
};
|
|
233
264
|
yield {
|
|
234
|
-
type:
|
|
265
|
+
type: "agent:output",
|
|
235
266
|
data: { content: `Deleted channel \`${rawChannelId}\`.` },
|
|
236
267
|
meta: resultMeta,
|
|
237
268
|
};
|
|
238
269
|
}
|
|
239
270
|
catch (error) {
|
|
240
271
|
yield {
|
|
241
|
-
type:
|
|
272
|
+
type: "action:delete_channel:result",
|
|
242
273
|
data: {
|
|
243
274
|
success: false,
|
|
244
275
|
channelId: rawChannelId,
|
|
245
|
-
error: error instanceof Error ? error.message :
|
|
276
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
246
277
|
},
|
|
247
278
|
meta: resultMeta,
|
|
248
279
|
};
|
|
249
280
|
}
|
|
250
281
|
});
|
|
251
|
-
actions.on(
|
|
282
|
+
actions.on("update_channel", async function* (event, context) {
|
|
252
283
|
const data = event.data;
|
|
253
|
-
const targetChannelId = (data.channelId ||
|
|
284
|
+
const targetChannelId = (data.channelId ||
|
|
285
|
+
context.state.channelId ||
|
|
286
|
+
"").trim();
|
|
254
287
|
const resultMeta = {
|
|
255
288
|
...(event.meta || {}),
|
|
256
289
|
agentId: context.state.agentId,
|
|
257
290
|
};
|
|
258
291
|
if (!targetChannelId) {
|
|
259
292
|
yield {
|
|
260
|
-
type:
|
|
293
|
+
type: "action:update_channel:result",
|
|
261
294
|
data: {
|
|
262
295
|
success: false,
|
|
263
|
-
channelId:
|
|
296
|
+
channelId: "",
|
|
264
297
|
updatedFields: [],
|
|
265
298
|
},
|
|
266
299
|
meta: resultMeta,
|
|
@@ -269,13 +302,13 @@ export function registerStorageTools(context) {
|
|
|
269
302
|
}
|
|
270
303
|
const patch = {};
|
|
271
304
|
const updatedFields = [];
|
|
272
|
-
if (typeof data.name ===
|
|
305
|
+
if (typeof data.name === "string" && data.name.trim()) {
|
|
273
306
|
patch.name = data.name.trim();
|
|
274
|
-
updatedFields.push(
|
|
307
|
+
updatedFields.push("name");
|
|
275
308
|
}
|
|
276
|
-
if (typeof data.cwd ===
|
|
309
|
+
if (typeof data.cwd === "string" && data.cwd.trim()) {
|
|
277
310
|
patch.cwd = data.cwd.trim();
|
|
278
|
-
updatedFields.push(
|
|
311
|
+
updatedFields.push("cwd");
|
|
279
312
|
}
|
|
280
313
|
try {
|
|
281
314
|
if (updatedFields.length > 0) {
|
|
@@ -290,20 +323,20 @@ export function registerStorageTools(context) {
|
|
|
290
323
|
});
|
|
291
324
|
}
|
|
292
325
|
yield {
|
|
293
|
-
type:
|
|
326
|
+
type: "action:update_channel:result",
|
|
294
327
|
data: { success: true, channelId: targetChannelId, updatedFields },
|
|
295
328
|
meta: resultMeta,
|
|
296
329
|
};
|
|
297
330
|
}
|
|
298
331
|
catch {
|
|
299
332
|
yield {
|
|
300
|
-
type:
|
|
333
|
+
type: "action:update_channel:result",
|
|
301
334
|
data: { success: false, channelId: targetChannelId, updatedFields },
|
|
302
335
|
meta: resultMeta,
|
|
303
336
|
};
|
|
304
337
|
}
|
|
305
338
|
});
|
|
306
|
-
actions.on(
|
|
339
|
+
actions.on("patch_channel_details", async function* (event, context) {
|
|
307
340
|
const updatedFields = [];
|
|
308
341
|
const resultMeta = {
|
|
309
342
|
...(event.meta || {}),
|
|
@@ -316,51 +349,51 @@ export function registerStorageTools(context) {
|
|
|
316
349
|
channelId: context.state.channelId,
|
|
317
350
|
state: data.state,
|
|
318
351
|
});
|
|
319
|
-
updatedFields.push(
|
|
352
|
+
updatedFields.push("state");
|
|
320
353
|
}
|
|
321
|
-
if (typeof data.spec ===
|
|
354
|
+
if (typeof data.spec === "string") {
|
|
322
355
|
await storage.patchChannelSpec({
|
|
323
356
|
channelId: context.state.channelId,
|
|
324
357
|
spec: data.spec,
|
|
325
358
|
});
|
|
326
|
-
updatedFields.push(
|
|
359
|
+
updatedFields.push("spec");
|
|
327
360
|
}
|
|
328
|
-
if (typeof data.cwd ===
|
|
361
|
+
if (typeof data.cwd === "string") {
|
|
329
362
|
await storage.patchChannelState({
|
|
330
363
|
channelId: context.state.channelId,
|
|
331
364
|
state: { cwd: data.cwd },
|
|
332
365
|
});
|
|
333
|
-
updatedFields.push(
|
|
366
|
+
updatedFields.push("cwd");
|
|
334
367
|
}
|
|
335
368
|
context.state.channelDetails = await storage.getChannelDetails({
|
|
336
369
|
channelId: context.state.channelId,
|
|
337
370
|
});
|
|
338
371
|
yield {
|
|
339
|
-
type:
|
|
372
|
+
type: "client:ui:widget",
|
|
340
373
|
data: {
|
|
341
|
-
widgetId:
|
|
342
|
-
kind:
|
|
343
|
-
title:
|
|
344
|
-
body: `The channel details have been updated. ${updatedFields.join(
|
|
345
|
-
display:
|
|
374
|
+
widgetId: "patch-channel-details-result" + Date.now(),
|
|
375
|
+
kind: "message",
|
|
376
|
+
title: "Channel details updated.",
|
|
377
|
+
body: `The channel details have been updated. ${updatedFields.join(", ")}`,
|
|
378
|
+
display: "collapsed",
|
|
346
379
|
},
|
|
347
380
|
meta: resultMeta,
|
|
348
381
|
};
|
|
349
382
|
yield {
|
|
350
|
-
type:
|
|
383
|
+
type: "action:patch_channel_details:result",
|
|
351
384
|
data: { success: true, updatedFields },
|
|
352
385
|
meta: resultMeta,
|
|
353
386
|
};
|
|
354
387
|
}
|
|
355
388
|
catch {
|
|
356
389
|
yield {
|
|
357
|
-
type:
|
|
390
|
+
type: "action:patch_channel_details:result",
|
|
358
391
|
data: { success: false, updatedFields },
|
|
359
392
|
meta: resultMeta,
|
|
360
393
|
};
|
|
361
394
|
}
|
|
362
395
|
});
|
|
363
|
-
actions.on(
|
|
396
|
+
actions.on("patch_thread_details", async function* (event, context) {
|
|
364
397
|
const updatedFields = [];
|
|
365
398
|
const resultMeta = {
|
|
366
399
|
...(event.meta || {}),
|
|
@@ -368,46 +401,50 @@ export function registerStorageTools(context) {
|
|
|
368
401
|
};
|
|
369
402
|
try {
|
|
370
403
|
if (!context.state.threadId) {
|
|
371
|
-
throw new Error(
|
|
404
|
+
throw new Error("Missing threadId in state for patch_thread_details");
|
|
372
405
|
}
|
|
373
406
|
if (event.data?.state !== undefined) {
|
|
407
|
+
const state = { ...event.data.state };
|
|
408
|
+
delete state.status;
|
|
409
|
+
delete state.statusUpdatedAt;
|
|
410
|
+
delete state.statusReason;
|
|
374
411
|
await storage.patchThreadState({
|
|
375
412
|
channelId: context.state.channelId,
|
|
376
413
|
threadId: context.state.threadId,
|
|
377
|
-
state
|
|
414
|
+
state,
|
|
378
415
|
});
|
|
379
|
-
updatedFields.push(
|
|
416
|
+
updatedFields.push("state");
|
|
380
417
|
}
|
|
381
418
|
context.state.threadDetails = await storage.getThreadDetails({
|
|
382
419
|
channelId: context.state.channelId,
|
|
383
420
|
threadId: context.state.threadId,
|
|
384
421
|
});
|
|
385
422
|
yield {
|
|
386
|
-
type:
|
|
423
|
+
type: "action:patch_thread_details:result",
|
|
387
424
|
data: { success: true, updatedFields },
|
|
388
425
|
meta: resultMeta,
|
|
389
426
|
};
|
|
390
427
|
}
|
|
391
428
|
catch {
|
|
392
429
|
yield {
|
|
393
|
-
type:
|
|
430
|
+
type: "action:patch_thread_details:result",
|
|
394
431
|
data: { success: false, updatedFields },
|
|
395
432
|
meta: resultMeta,
|
|
396
433
|
};
|
|
397
434
|
}
|
|
398
435
|
});
|
|
399
|
-
actions.on(
|
|
436
|
+
actions.on("get_workspace_file_url", async function* (event, context) {
|
|
400
437
|
const channelId = context.state.channelId;
|
|
401
438
|
const filePath = event.data?.path;
|
|
402
439
|
const toolCallId = event.meta?.toolCallId;
|
|
403
440
|
if (!filePath) {
|
|
404
441
|
yield {
|
|
405
|
-
type:
|
|
442
|
+
type: "action:get_workspace_file_url:result",
|
|
406
443
|
data: {
|
|
407
444
|
success: false,
|
|
408
|
-
path:
|
|
409
|
-
error:
|
|
410
|
-
output:
|
|
445
|
+
path: "",
|
|
446
|
+
error: "Path is required",
|
|
447
|
+
output: "Path is required",
|
|
411
448
|
},
|
|
412
449
|
meta: { ...(event.meta || {}), toolCallId },
|
|
413
450
|
};
|
|
@@ -430,7 +467,7 @@ export function registerStorageTools(context) {
|
|
|
430
467
|
size,
|
|
431
468
|
});
|
|
432
469
|
yield {
|
|
433
|
-
type:
|
|
470
|
+
type: "action:get_workspace_file_url:result",
|
|
434
471
|
data: {
|
|
435
472
|
success: true,
|
|
436
473
|
path: filePath,
|
|
@@ -443,9 +480,9 @@ export function registerStorageTools(context) {
|
|
|
443
480
|
};
|
|
444
481
|
}
|
|
445
482
|
catch (error) {
|
|
446
|
-
const message = error instanceof Error ? error.message :
|
|
483
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
447
484
|
yield {
|
|
448
|
-
type:
|
|
485
|
+
type: "action:get_workspace_file_url:result",
|
|
449
486
|
data: {
|
|
450
487
|
success: false,
|
|
451
488
|
path: filePath,
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
const AGENT_THREAD_STATUSES = [
|
|
3
|
+
"working",
|
|
4
|
+
"needs_input",
|
|
5
|
+
"ready_for_review",
|
|
6
|
+
];
|
|
7
|
+
function formatStatusOutput(args) {
|
|
8
|
+
const reason = args.reason?.trim();
|
|
9
|
+
return reason
|
|
10
|
+
? `Job status set to ${args.status}: ${reason}`
|
|
11
|
+
: `Job status set to ${args.status}.`;
|
|
12
|
+
}
|
|
13
|
+
const threadStatusToolDefinitions = {
|
|
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 while you are doing it. Never mark completed or archived — only the human can.",
|
|
16
|
+
inputSchema: z.object({
|
|
17
|
+
status: z
|
|
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."),
|
|
20
|
+
reason: z
|
|
21
|
+
.string()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("Short reason shown in the job list, e.g. what you need from the human."),
|
|
24
|
+
}),
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export const threadStatusPlugin = {
|
|
28
|
+
id: "thread-status",
|
|
29
|
+
name: "Thread status",
|
|
30
|
+
description: "Sets the durable workflow status for the current job/thread.",
|
|
31
|
+
toolDefinitions: threadStatusToolDefinitions,
|
|
32
|
+
factory: (pluginContext) => (builder) => {
|
|
33
|
+
builder.on("action:set_thread_status", async function* (event, context) {
|
|
34
|
+
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
35
|
+
try {
|
|
36
|
+
const channelId = context.state.channelId;
|
|
37
|
+
const threadId = context.state.threadId;
|
|
38
|
+
if (!channelId || !threadId) {
|
|
39
|
+
throw new Error("Missing channelId or threadId for set_thread_status");
|
|
40
|
+
}
|
|
41
|
+
const status = event.data.status;
|
|
42
|
+
if (status !== "working" &&
|
|
43
|
+
status !== "needs_input" &&
|
|
44
|
+
status !== "ready_for_review") {
|
|
45
|
+
throw new Error('Agents may only set working, needs_input, or ready_for_review. The human marks completed and archived.');
|
|
46
|
+
}
|
|
47
|
+
const reasonRaw = event.data.reason;
|
|
48
|
+
const reason = typeof reasonRaw === "string" && reasonRaw.trim()
|
|
49
|
+
? reasonRaw.trim()
|
|
50
|
+
: "";
|
|
51
|
+
await pluginContext.storage.patchThreadState({
|
|
52
|
+
channelId,
|
|
53
|
+
threadId,
|
|
54
|
+
state: {
|
|
55
|
+
status,
|
|
56
|
+
statusUpdatedAt: new Date().toISOString(),
|
|
57
|
+
statusReason: reason,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
context.state.threadDetails = await pluginContext.storage.getThreadDetails({
|
|
61
|
+
channelId,
|
|
62
|
+
threadId,
|
|
63
|
+
});
|
|
64
|
+
const output = formatStatusOutput({ status, reason });
|
|
65
|
+
yield {
|
|
66
|
+
type: "action:set_thread_status:result",
|
|
67
|
+
data: {
|
|
68
|
+
success: true,
|
|
69
|
+
status,
|
|
70
|
+
...(reason ? { reason } : {}),
|
|
71
|
+
output,
|
|
72
|
+
},
|
|
73
|
+
meta: resultMeta,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
78
|
+
yield {
|
|
79
|
+
type: "action:set_thread_status:result",
|
|
80
|
+
data: {
|
|
81
|
+
success: false,
|
|
82
|
+
error: message,
|
|
83
|
+
output: message,
|
|
84
|
+
},
|
|
85
|
+
meta: resultMeta,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
export default threadStatusPlugin;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meetopenbot/openbot",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "OpenBot coordinator runtime: ask specialists, route work into Spaces, and track todos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -18,12 +18,6 @@
|
|
|
18
18
|
"files": [
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build": "tsc && node ../../scripts/write-plugin-declaration.mjs",
|
|
23
|
-
"dev": "tsc --watch --preserveWatchOutput",
|
|
24
|
-
"typecheck": "tsc --noEmit",
|
|
25
|
-
"prepack": "pnpm build"
|
|
26
|
-
},
|
|
27
21
|
"dependencies": {
|
|
28
22
|
"@ai-sdk/anthropic": "^3.0.33",
|
|
29
23
|
"@ai-sdk/openai": "^3.0.13",
|
|
@@ -34,5 +28,10 @@
|
|
|
34
28
|
"devDependencies": {
|
|
35
29
|
"@types/node": "^20.10.1",
|
|
36
30
|
"typescript": "^5.9.3"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc && node ../../scripts/write-plugin-declaration.mjs",
|
|
34
|
+
"dev": "tsc --watch --preserveWatchOutput",
|
|
35
|
+
"typecheck": "tsc --noEmit"
|
|
37
36
|
}
|
|
38
|
-
}
|
|
37
|
+
}
|