@meetopenbot/openbot 0.2.7 → 1.0.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.
@@ -1,7 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { z } from "zod";
3
3
  import { GENERAL_SPACE_ID, isPlaceholderSpaceId, placeholderSpaceError, } from "../space-id.js";
4
- import { asActionBuilder, } from "../types.js";
5
4
  function normalizeSpaceId(raw) {
6
5
  return raw
7
6
  .trim()
@@ -24,7 +23,14 @@ function startWorkWidget(args) {
24
23
  meta: args.meta,
25
24
  };
26
25
  }
27
- const startWorkToolDefinitions = {
26
+ function resultMeta(ctx) {
27
+ return {
28
+ agentId: ctx.agentId,
29
+ threadId: ctx.threadId ?? ctx.state.threadId,
30
+ toolCallId: ctx.toolCallId,
31
+ };
32
+ }
33
+ export const toolDefinitions = {
28
34
  start_work: {
29
35
  description: "Start the human's ask in an existing Space. Use from #general for real project work only — not questions, listings, or setup. If the Space does not exist, do not invent one: propose it to the human, then create_channel after they agree. Do not call this on work that was already forwarded.",
30
36
  inputSchema: z.object({
@@ -45,149 +51,71 @@ const startWorkToolDefinitions = {
45
51
  }),
46
52
  },
47
53
  };
48
- export const startWorkPlugin = {
49
- id: "start-work",
50
- name: "Start work",
51
- description: "Forwards an ask into the right Space and starts OpenBot there.",
52
- toolDefinitions: startWorkToolDefinitions,
53
- factory: (pluginContext) => (builder) => {
54
- const actions = asActionBuilder(builder);
55
- actions.on("start_work", async function* (event, context) {
56
- const data = event.data;
57
- const toolCallId = event.meta?.toolCallId;
58
- const resultMeta = {
59
- ...(event.meta || {}),
60
- agentId: context.state.agentId,
61
- threadId: event.meta?.threadId || context.state.threadId,
62
- };
63
- const widgetId = typeof toolCallId === "string"
64
- ? toolCallId
54
+ export const tools = {
55
+ start_work: {
56
+ ...toolDefinitions.start_work,
57
+ execute: async (rawArgs, ctx) => {
58
+ const data = (rawArgs ?? {});
59
+ const meta = resultMeta(ctx);
60
+ const widgetId = typeof ctx.toolCallId === "string"
61
+ ? ctx.toolCallId
65
62
  : `start_work:${Date.now()}`;
66
- if (context.state.agentId !== pluginContext.host.orchestratorAgentId) {
67
- const error = "Only OpenBot can start work in another Space.";
68
- yield startWorkWidget({
63
+ const fail = async (error, spaceId = "") => {
64
+ await ctx.emit(startWorkWidget({
69
65
  widgetId,
70
- spaceId: "",
66
+ spaceId,
71
67
  body: error,
72
68
  state: "error",
73
- meta: resultMeta,
74
- });
75
- yield {
76
- type: "action:start_work:result",
77
- data: { success: false, error, output: error },
78
- meta: resultMeta,
79
- };
80
- return;
69
+ meta,
70
+ }));
71
+ return { success: false, spaceId, error, output: error };
72
+ };
73
+ if (ctx.agentId !== ctx.host.orchestratorAgentId) {
74
+ return fail("Only OpenBot can start work in another Space.");
81
75
  }
82
- const invokeData = context.state.triggerEvent?.type === "agent:invoke"
83
- ? context.state.triggerEvent.data
76
+ const invokeData = ctx.state.triggerEvent?.type === "agent:invoke"
77
+ ? ctx.state.triggerEvent.data
84
78
  : undefined;
85
79
  if (typeof invokeData?.forwardedFromSpaceId === "string") {
86
- const error = "This thread was already forwarded. Stay here and ask agents; do not start_work again.";
87
- yield startWorkWidget({
88
- widgetId,
89
- spaceId: "",
90
- body: error,
91
- state: "error",
92
- meta: resultMeta,
93
- });
94
- yield {
95
- type: "action:start_work:result",
96
- data: { success: false, error, output: error },
97
- meta: resultMeta,
98
- };
99
- return;
80
+ return fail("This thread was already forwarded. Stay here and ask agents; do not start_work again.");
100
81
  }
101
82
  const spaceId = normalizeSpaceId(data.spaceId || "");
102
83
  const prompt = typeof data.prompt === "string" ? data.prompt.trim() : "";
103
84
  if (spaceId && isPlaceholderSpaceId(spaceId)) {
104
- const error = placeholderSpaceError(spaceId);
105
- yield startWorkWidget({
106
- widgetId,
107
- spaceId,
108
- body: error,
109
- state: "error",
110
- meta: resultMeta,
111
- });
112
- yield {
113
- type: "action:start_work:result",
114
- data: { success: false, spaceId, error, output: error },
115
- meta: resultMeta,
116
- };
117
- return;
85
+ return fail(placeholderSpaceError(spaceId), spaceId);
118
86
  }
119
87
  if (!spaceId || !prompt) {
120
- const error = "spaceId and prompt are required";
121
- yield startWorkWidget({
122
- widgetId,
123
- spaceId,
124
- body: error,
125
- state: "error",
126
- meta: resultMeta,
127
- });
128
- yield {
129
- type: "action:start_work:result",
130
- data: { success: false, spaceId, error, output: error },
131
- meta: resultMeta,
132
- };
133
- return;
88
+ return fail("spaceId and prompt are required", spaceId);
134
89
  }
135
- if (spaceId === context.state.channelId) {
136
- const error = `Already in #${spaceId}. Ask an agent here instead of start_work.`;
137
- yield startWorkWidget({
138
- widgetId,
139
- spaceId,
140
- body: error,
141
- state: "error",
142
- meta: resultMeta,
143
- });
144
- yield {
145
- type: "action:start_work:result",
146
- data: { success: false, spaceId, error, output: error },
147
- meta: resultMeta,
148
- };
149
- return;
90
+ if (spaceId === (ctx.channelId ?? ctx.state.channelId)) {
91
+ return fail(`Already in #${spaceId}. Ask an agent here instead of start_work.`, spaceId);
150
92
  }
151
- const storage = pluginContext.storage;
152
- const existing = (await storage.getChannels?.().catch(() => []));
93
+ const existing = (await ctx.storage.getChannels?.().catch(() => []));
153
94
  const exists = Array.isArray(existing) &&
154
95
  existing.some((space) => space.id === spaceId);
155
96
  if (!exists) {
156
- const error = `Space #${spaceId} does not exist. Propose it to the human (name + why). After they agree, use create_channel, then start_work. Do not invent a placeholder Space.`;
157
- yield startWorkWidget({
158
- widgetId,
159
- spaceId,
160
- body: error,
161
- state: "error",
162
- meta: resultMeta,
163
- });
164
- yield {
165
- type: "action:start_work:result",
166
- data: { success: false, spaceId, error, output: error },
167
- meta: resultMeta,
168
- };
169
- return;
97
+ return fail(`Space #${spaceId} does not exist. Propose it to the human (name + why). After they agree, use create_channel, then start_work. Do not invent a placeholder Space.`, spaceId);
170
98
  }
171
99
  const threadId = randomUUID();
172
100
  const threadTitle = typeof data.title === "string" && data.title.trim()
173
101
  ? data.title.trim()
174
102
  : undefined;
175
103
  const threadUrl = `/channels/${encodeURIComponent(spaceId)}/threads/${encodeURIComponent(threadId)}`;
176
- const fromSpace = context.state.channelId || GENERAL_SPACE_ID;
177
- yield startWorkWidget({
104
+ const fromSpace = ctx.channelId ?? ctx.state.channelId ?? GENERAL_SPACE_ID;
105
+ await ctx.emit(startWorkWidget({
178
106
  widgetId,
179
107
  spaceId,
180
108
  body: `Starting in #${spaceId}…`,
181
109
  state: "open",
182
- meta: resultMeta,
183
- });
110
+ meta,
111
+ }));
184
112
  try {
185
- await storage.createThread({
113
+ await ctx.storage.createThread({
186
114
  channelId: spaceId,
187
115
  threadId,
188
116
  threadTitle,
189
117
  initialState: {
190
- respondingAgentId: pluginContext.host.orchestratorAgentId,
118
+ respondingAgentId: ctx.host.orchestratorAgentId,
191
119
  ...(threadTitle ? { name: threadTitle } : {}),
192
120
  },
193
121
  });
@@ -205,58 +133,50 @@ export const startWorkPlugin = {
205
133
  meta: {
206
134
  channelId: spaceId,
207
135
  threadId,
208
- userId: context.state.triggerEvent?.meta?.userId,
209
- userName: context.state.triggerEvent?.meta?.userName,
210
- userAvatarUrl: context.state.triggerEvent?.meta?.userAvatarUrl,
136
+ userId: ctx.state.triggerEvent?.meta?.userId,
137
+ userName: ctx.state.triggerEvent?.meta?.userName,
138
+ userAvatarUrl: ctx.state.triggerEvent?.meta?.userAvatarUrl,
211
139
  },
212
140
  };
213
- await pluginContext.host.runAgent({
141
+ await ctx.host.runAgent({
214
142
  runId: `sw_${randomUUID()}`,
215
- agentId: pluginContext.host.orchestratorAgentId,
143
+ agentId: ctx.host.orchestratorAgentId,
216
144
  event: invokeEvent,
217
- publicBaseUrl: pluginContext.publicBaseUrl,
145
+ publicBaseUrl: ctx.publicBaseUrl,
218
146
  persistEvents: true,
219
147
  onEvent: async () => {
220
148
  // Persist in the target Space via the inner harness. Do not echo into this thread.
221
149
  },
222
150
  });
223
151
  const output = `Started work in #${spaceId}. Open ${threadUrl}`;
224
- yield startWorkWidget({
152
+ await ctx.emit(startWorkWidget({
225
153
  widgetId,
226
154
  spaceId,
227
155
  body: output,
228
156
  state: "submitted",
229
- meta: resultMeta,
230
- });
231
- yield {
232
- type: "action:start_work:result",
233
- data: {
234
- success: true,
235
- spaceId,
236
- threadId,
237
- threadUrl,
238
- output,
239
- },
240
- meta: resultMeta,
157
+ meta,
158
+ }));
159
+ return {
160
+ success: true,
161
+ spaceId,
162
+ threadId,
163
+ threadUrl,
164
+ output,
241
165
  };
242
166
  }
243
167
  catch (error) {
244
168
  const message = error instanceof Error ? error.message : "Unknown error";
245
169
  const output = `Failed to start work in #${spaceId}: ${message}`;
246
- yield startWorkWidget({
170
+ await ctx.emit(startWorkWidget({
247
171
  widgetId,
248
172
  spaceId,
249
173
  body: output,
250
174
  state: "error",
251
- meta: resultMeta,
252
- });
253
- yield {
254
- type: "action:start_work:result",
255
- data: { success: false, spaceId, threadId, error: message, output },
256
- meta: resultMeta,
257
- };
175
+ meta,
176
+ }));
177
+ return { success: false, spaceId, threadId, error: message, output };
258
178
  }
259
- });
179
+ },
260
180
  },
261
181
  };
262
- export default startWorkPlugin;
182
+ export const startWorkTools = tools;