@meetopenbot/openbot 0.1.14 → 0.1.15

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.
@@ -1,30 +1,31 @@
1
- import z from 'zod';
2
- import { asActionBuilder } from '../types.js';
3
- import { buildWorkspaceFileUrl } from '../utils/workspace-url.js';
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: 'client:ui:widget',
21
+ type: "client:ui:widget",
21
22
  data: {
22
23
  widgetId: args.widgetId,
23
- kind: 'message',
24
- title: 'create_channel',
24
+ kind: "message",
25
+ title: "create_channel",
25
26
  description: args.channelId || undefined,
26
27
  body: args.body,
27
- display: 'collapsed',
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: 'Create a new channel. Use when the user intent is clearly different from the current channel and should be split. Always confirm before creating. Skip for simple Q&A.',
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('Unique channel ID (e.g. product-launch, backend-platform, channel_roadmap).'),
40
+ .describe("Unique Space id (e.g. product-launch, backend-platform)."),
40
41
  name: z
41
42
  .string()
42
43
  .optional()
43
- .describe('Optional display name. Defaults to a title-cased channel ID.'),
44
+ .describe("Optional display name. Defaults to a title-cased Space id."),
44
45
  spec: z
45
46
  .string()
46
47
  .optional()
47
- .describe('Optional initial markdown content for the channel spec.'),
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('Optional initial state object for the channel. Do not set cwd; the runtime assigns the workspace path.'),
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: 'Patch current channel details (state, spec, cwd).',
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('JSON state object for the channel.'),
62
+ .describe("JSON state object for the channel."),
62
63
  spec: z
63
64
  .string()
64
65
  .optional()
65
- .describe('Markdown content for the channel specification (SPEC.md). Use for goals and rules.'),
66
- cwd: z.string().optional().describe('Current working directory for the channel.'),
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 || value.spec !== undefined || value.cwd !== undefined, { message: 'Provide at least one of state, spec, or cwd.' }),
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: 'Patch current thread details (state). Use for thread metadata such as `name` or `isSmartNamed`. For multi-step task tracking, use `todo_write` instead.',
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('JSON state object for the thread. Merges with existing state.'),
81
+ .describe("JSON state object for the thread. Merges with existing state."),
76
82
  }),
77
83
  },
78
84
  create_variable: {
79
- description: 'Create or update a variable in the workspace storage.',
85
+ description: "Create or update a variable in the workspace storage.",
80
86
  inputSchema: z.object({
81
- key: z.string().describe('The key of the variable.'),
82
- value: z.string().describe('The value of the variable.'),
83
- secret: z.boolean().optional().describe('Whether the variable is a secret.'),
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: 'Delete a variable from the workspace storage.',
96
+ description: "Delete a variable from the workspace storage.",
88
97
  inputSchema: z.object({
89
- key: z.string().describe('The key of the variable to delete.'),
98
+ key: z.string().describe("The key of the variable to delete."),
90
99
  }),
91
100
  },
92
101
  delete_channel: {
93
- description: 'Permanently delete a channel and all its threads and events. Always confirm with the user before deleting.',
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('The channel ID to delete.'),
104
+ channelId: z.string().describe("The channel ID to delete."),
96
105
  }),
97
106
  },
98
107
  get_workspace_file_url: {
99
- description: 'Get a fetchable HTTP URL for a file in the current channel workspace (images, video, audio, documents).',
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('create_channel', async function* (event, context) {
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 === 'string'
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 = 'channelId is required';
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: 'error',
161
+ state: "error",
131
162
  meta: resultMeta,
132
163
  });
133
164
  yield {
134
- type: 'action:create_channel:result',
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 === 'string' && name.trim()
178
+ const displayName = typeof name === "string" && name.trim()
148
179
  ? name.trim()
149
- : typeof initialState?.name === 'string' && initialState.name.trim()
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 === 'string' && spec.trim() ? spec : `# ${displayName}\n\n`;
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: 'Creating…',
162
- state: 'open',
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: 'submitted',
207
+ state: "submitted",
177
208
  meta: resultMeta,
178
209
  });
179
210
  yield {
180
- type: 'action:create_channel:result',
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 : 'Unknown error';
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: 'error',
223
+ state: "error",
193
224
  meta: resultMeta,
194
225
  });
195
226
  yield {
196
- type: 'action:create_channel:result',
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('delete_channel', async function* (event, context) {
209
- const rawChannelId = (event.data?.channelId || '').trim();
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: 'action:delete_channel:result',
247
+ type: "action:delete_channel:result",
217
248
  data: {
218
249
  success: false,
219
- channelId: '',
220
- error: 'channelId is required',
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: 'action:delete_channel:result',
260
+ type: "action:delete_channel:result",
230
261
  data: { success: true, channelId: rawChannelId },
231
262
  meta: resultMeta,
232
263
  };
233
264
  yield {
234
- type: 'agent:output',
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: 'action:delete_channel:result',
272
+ type: "action:delete_channel:result",
242
273
  data: {
243
274
  success: false,
244
275
  channelId: rawChannelId,
245
- error: error instanceof Error ? error.message : 'Unknown error',
276
+ error: error instanceof Error ? error.message : "Unknown error",
246
277
  },
247
278
  meta: resultMeta,
248
279
  };
249
280
  }
250
281
  });
251
- actions.on('update_channel', async function* (event, context) {
282
+ actions.on("update_channel", async function* (event, context) {
252
283
  const data = event.data;
253
- const targetChannelId = (data.channelId || context.state.channelId || '').trim();
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: 'action:update_channel:result',
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 === 'string' && data.name.trim()) {
305
+ if (typeof data.name === "string" && data.name.trim()) {
273
306
  patch.name = data.name.trim();
274
- updatedFields.push('name');
307
+ updatedFields.push("name");
275
308
  }
276
- if (typeof data.cwd === 'string' && data.cwd.trim()) {
309
+ if (typeof data.cwd === "string" && data.cwd.trim()) {
277
310
  patch.cwd = data.cwd.trim();
278
- updatedFields.push('cwd');
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: 'action:update_channel:result',
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: 'action:update_channel:result',
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('patch_channel_details', async function* (event, context) {
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('state');
352
+ updatedFields.push("state");
320
353
  }
321
- if (typeof data.spec === 'string') {
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('spec');
359
+ updatedFields.push("spec");
327
360
  }
328
- if (typeof data.cwd === 'string') {
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('cwd');
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: 'client:ui:widget',
372
+ type: "client:ui:widget",
340
373
  data: {
341
- widgetId: 'patch-channel-details-result' + Date.now(),
342
- kind: 'message',
343
- title: 'Channel details updated.',
344
- body: `The channel details have been updated. ${updatedFields.join(', ')}`,
345
- display: 'collapsed',
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: 'action:patch_channel_details:result',
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: 'action:patch_channel_details:result',
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('patch_thread_details', async function* (event, context) {
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('Missing threadId in state for patch_thread_details');
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: event.data.state,
414
+ state,
378
415
  });
379
- updatedFields.push('state');
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: 'action:patch_thread_details:result',
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: 'action:patch_thread_details:result',
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('get_workspace_file_url', async function* (event, context) {
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: 'action:get_workspace_file_url:result',
442
+ type: "action:get_workspace_file_url:result",
406
443
  data: {
407
444
  success: false,
408
- path: '',
409
- error: 'Path is required',
410
- output: 'Path is required',
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: 'action:get_workspace_file_url:result',
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 : 'Unknown error';
483
+ const message = error instanceof Error ? error.message : "Unknown error";
447
484
  yield {
448
- type: 'action:get_workspace_file_url:result',
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.1.14",
4
- "description": "Monolithic OpenBot agent runtime with batteries-included tools.",
3
+ "version": "0.1.15",
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",