openbot 0.3.6 → 0.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.
Files changed (96) hide show
  1. package/README.md +15 -16
  2. package/dist/app/agent-ids.js +4 -0
  3. package/dist/app/cli.js +1 -1
  4. package/dist/app/config.js +0 -19
  5. package/dist/app/server.js +8 -14
  6. package/dist/bus/services.js +34 -124
  7. package/dist/harness/agent-invoke-run.js +44 -0
  8. package/dist/harness/agent-turn.js +99 -0
  9. package/dist/harness/channel-participants.js +40 -0
  10. package/dist/harness/constants.js +2 -0
  11. package/dist/harness/context-meter.js +97 -0
  12. package/dist/harness/context.js +95 -47
  13. package/dist/harness/dispatch.js +144 -0
  14. package/dist/harness/dispatcher.js +45 -156
  15. package/dist/harness/history.js +177 -0
  16. package/dist/harness/index.js +91 -0
  17. package/dist/harness/orchestration.js +88 -0
  18. package/dist/harness/participants.js +22 -0
  19. package/dist/harness/run-harness.js +154 -0
  20. package/dist/harness/run.js +98 -0
  21. package/dist/harness/runtime-factory.js +0 -34
  22. package/dist/harness/runtime.js +57 -0
  23. package/dist/harness/todo-dispatch.js +51 -0
  24. package/dist/harness/todos.js +5 -0
  25. package/dist/harness/turn.js +79 -0
  26. package/dist/plugins/approval/index.js +105 -149
  27. package/dist/plugins/delegation/index.js +119 -32
  28. package/dist/plugins/memory/index.js +103 -14
  29. package/dist/plugins/memory/service.js +152 -0
  30. package/dist/plugins/openbot/context.js +80 -0
  31. package/dist/plugins/openbot/history.js +98 -0
  32. package/dist/plugins/openbot/index.js +31 -0
  33. package/dist/plugins/openbot/runtime.js +317 -0
  34. package/dist/plugins/openbot/system-prompt.js +5 -0
  35. package/dist/plugins/plugin-manager/index.js +105 -0
  36. package/dist/plugins/storage/index.js +573 -0
  37. package/dist/plugins/storage/service.js +1159 -0
  38. package/dist/plugins/storage-tools/index.js +2 -2
  39. package/dist/plugins/thread-namer/index.js +72 -0
  40. package/dist/plugins/thread-naming/generate-title.js +44 -0
  41. package/dist/plugins/thread-naming/index.js +103 -0
  42. package/dist/plugins/threads/index.js +114 -0
  43. package/dist/plugins/todo/index.js +24 -25
  44. package/dist/plugins/ui/index.js +2 -32
  45. package/dist/registry/plugins.js +3 -9
  46. package/dist/services/plugins/domain.js +1 -0
  47. package/dist/services/plugins/plugin-cache.js +9 -0
  48. package/dist/services/plugins/registry.js +110 -0
  49. package/dist/services/plugins/service.js +177 -0
  50. package/dist/services/plugins/types.js +1 -0
  51. package/dist/services/process.js +29 -0
  52. package/dist/services/storage.js +11 -10
  53. package/dist/services/thread-naming.js +81 -0
  54. package/docs/agents.md +16 -10
  55. package/docs/architecture.md +2 -2
  56. package/docs/plugins.md +6 -15
  57. package/docs/templates/AGENT.example.md +7 -13
  58. package/package.json +1 -2
  59. package/src/app/agent-ids.ts +5 -0
  60. package/src/app/cli.ts +1 -1
  61. package/src/app/config.ts +1 -31
  62. package/src/app/server.ts +8 -16
  63. package/src/app/types.ts +63 -189
  64. package/src/harness/index.ts +145 -0
  65. package/src/plugins/approval/index.ts +91 -189
  66. package/src/plugins/delegation/index.ts +136 -39
  67. package/src/plugins/memory/index.ts +112 -15
  68. package/src/{services/memory.ts → plugins/memory/service.ts} +1 -1
  69. package/src/plugins/openbot/context.ts +91 -0
  70. package/src/plugins/openbot/history.ts +107 -0
  71. package/src/plugins/openbot/index.ts +37 -0
  72. package/src/plugins/openbot/runtime.ts +384 -0
  73. package/src/plugins/openbot/system-prompt.ts +7 -0
  74. package/src/plugins/plugin-manager/index.ts +122 -0
  75. package/src/plugins/shell/index.ts +1 -1
  76. package/src/plugins/storage/index.ts +633 -0
  77. package/src/{services/storage.ts → plugins/storage/service.ts} +224 -67
  78. package/src/{bus/types.ts → services/plugins/domain.ts} +16 -7
  79. package/src/services/plugins/plugin-cache.ts +13 -0
  80. package/src/{registry/plugins.ts → services/plugins/registry.ts} +25 -27
  81. package/src/services/{plugins.ts → plugins/service.ts} +96 -2
  82. package/src/{bus/plugin.ts → services/plugins/types.ts} +3 -3
  83. package/src/bus/services.ts +0 -954
  84. package/src/harness/context.ts +0 -365
  85. package/src/harness/dispatcher.ts +0 -379
  86. package/src/harness/mcp.ts +0 -78
  87. package/src/harness/runtime-factory.ts +0 -129
  88. package/src/harness/todo-advance.ts +0 -128
  89. package/src/plugins/ai-sdk/index.ts +0 -41
  90. package/src/plugins/ai-sdk/runtime.ts +0 -468
  91. package/src/plugins/ai-sdk/system-prompt.ts +0 -18
  92. package/src/plugins/mcp/index.ts +0 -128
  93. package/src/plugins/storage-tools/index.ts +0 -90
  94. package/src/plugins/todo/index.ts +0 -64
  95. package/src/plugins/ui/index.ts +0 -227
  96. /package/src/{harness → services}/process.ts +0 -0
@@ -0,0 +1,573 @@
1
+ import z from 'zod';
2
+ /**
3
+ * `storage` — exposes channel/thread/variable mutation tools and provides
4
+ * platform-level storage handlers.
5
+ */
6
+ const storageToolDefinitions = {
7
+ create_channel: {
8
+ 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.',
9
+ inputSchema: z.object({
10
+ channelId: z
11
+ .string()
12
+ .describe('Unique channel ID (e.g. product-launch, backend-platform, channel_roadmap).'),
13
+ spec: z
14
+ .string()
15
+ .optional()
16
+ .describe('Optional initial markdown content for the channel spec.'),
17
+ initialState: z
18
+ .record(z.string(), z.unknown())
19
+ .optional()
20
+ .describe('Optional initial state object for the channel.'),
21
+ cwd: z
22
+ .string()
23
+ .optional()
24
+ .describe('Optional initial current working directory for the channel.'),
25
+ }),
26
+ },
27
+ patch_channel_details: {
28
+ description: 'Patch current channel details (state, spec, cwd).',
29
+ inputSchema: z
30
+ .object({
31
+ state: z
32
+ .record(z.string(), z.unknown())
33
+ .optional()
34
+ .describe('JSON state object for the channel. Use for structured metadata.'),
35
+ spec: z
36
+ .string()
37
+ .optional()
38
+ .describe('Markdown content for the channel specification (SPEC.md). Use for goals and rules.'),
39
+ cwd: z.string().optional().describe('Current working directory for the channel.'),
40
+ })
41
+ .refine((value) => value.state !== undefined || value.spec !== undefined || value.cwd !== undefined, { message: 'Provide at least one of state, spec, or cwd.' }),
42
+ },
43
+ patch_thread_details: {
44
+ description: 'Patch current thread details (state).',
45
+ inputSchema: z.object({
46
+ state: z
47
+ .record(z.string(), z.unknown())
48
+ .describe('JSON state object for the thread. Use for structured progress or metadata.'),
49
+ }),
50
+ },
51
+ create_variable: {
52
+ description: 'Create or update a variable in the workspace storage.',
53
+ inputSchema: z.object({
54
+ key: z.string().describe('The key of the variable.'),
55
+ value: z.string().describe('The value of the variable.'),
56
+ secret: z.boolean().optional().describe('Whether the variable is a secret.'),
57
+ }),
58
+ },
59
+ delete_variable: {
60
+ description: 'Delete a variable from the workspace storage.',
61
+ inputSchema: z.object({
62
+ key: z.string().describe('The key of the variable to delete.'),
63
+ }),
64
+ },
65
+ };
66
+ export const storagePlugin = {
67
+ id: 'storage',
68
+ name: 'Storage',
69
+ description: 'Tools for creating channels, patching state, and managing workspace variables.',
70
+ toolDefinitions: storageToolDefinitions,
71
+ factory: ({ storage }) => (builder) => {
72
+ builder.on('action:create_thread', async function* (event, context) {
73
+ const threadId = event.meta?.threadId;
74
+ const channelId = context.state.channelId;
75
+ const { threadTitle, initialState } = event.data;
76
+ if (!threadId) {
77
+ console.warn('[storage] Cannot create thread: meta.threadId is missing');
78
+ return;
79
+ }
80
+ context.state.threadId = threadId;
81
+ if (channelId) {
82
+ try {
83
+ await storage.createThread({
84
+ channelId,
85
+ threadId,
86
+ threadTitle,
87
+ initialState: initialState || {},
88
+ });
89
+ context.state.threadDetails = await storage.getThreadDetails({
90
+ channelId,
91
+ threadId,
92
+ });
93
+ }
94
+ catch (error) {
95
+ console.warn(`[storage] Failed to initialize thread for channel ${channelId} thread ${threadId}`, error);
96
+ }
97
+ }
98
+ yield {
99
+ type: 'action:create_thread:result',
100
+ data: { success: true, threadId, threadTitle },
101
+ meta: { ...(event.meta || {}), threadId, agentId: context.state.agentId },
102
+ };
103
+ });
104
+ builder.on('action:create_channel', async function* (event, context) {
105
+ const { channelId, spec, initialState, cwd, participants } = event.data;
106
+ const rawChannelId = (channelId || '').trim();
107
+ const channelSpec = typeof spec === 'string' ? spec : '';
108
+ const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
109
+ if (!rawChannelId) {
110
+ yield {
111
+ type: 'action:create_channel:result',
112
+ data: { success: false, channelId: '', channelUrl: '' },
113
+ meta: resultMeta,
114
+ };
115
+ return;
116
+ }
117
+ const channelUrl = `/channels/${rawChannelId}`;
118
+ const mergedInitial = { ...(initialState || {}) };
119
+ if (participants !== undefined) {
120
+ const normalized = Array.isArray(participants)
121
+ ? participants
122
+ .filter((x) => typeof x === 'string')
123
+ .map((s) => s.trim())
124
+ .filter(Boolean)
125
+ : [];
126
+ mergedInitial.participants = normalized;
127
+ }
128
+ try {
129
+ await storage.createChannel({
130
+ channelId: rawChannelId,
131
+ spec: channelSpec,
132
+ initialState: mergedInitial,
133
+ cwd,
134
+ });
135
+ yield {
136
+ type: 'action:create_channel:result',
137
+ data: { success: true, channelId: rawChannelId, channelUrl },
138
+ meta: resultMeta,
139
+ };
140
+ yield {
141
+ type: 'agent:output',
142
+ data: { content: `Created channel \`${rawChannelId}\`.` },
143
+ meta: resultMeta,
144
+ };
145
+ }
146
+ catch {
147
+ yield {
148
+ type: 'action:create_channel:result',
149
+ data: { success: false, channelId: rawChannelId, channelUrl },
150
+ meta: resultMeta,
151
+ };
152
+ }
153
+ });
154
+ builder.on('action:update_channel', async function* (event, context) {
155
+ const data = (event.data || {});
156
+ const targetChannelId = (data.channelId || context.state.channelId || '').trim();
157
+ const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
158
+ if (!targetChannelId) {
159
+ yield {
160
+ type: 'action:update_channel:result',
161
+ data: { success: false, channelId: '', updatedFields: [] },
162
+ meta: resultMeta,
163
+ };
164
+ return;
165
+ }
166
+ const patch = {};
167
+ const updatedFields = [];
168
+ if (typeof data.name === 'string' && data.name.trim()) {
169
+ patch.name = data.name.trim();
170
+ updatedFields.push('name');
171
+ }
172
+ if (typeof data.cwd === 'string' && data.cwd.trim()) {
173
+ patch.cwd = data.cwd.trim();
174
+ updatedFields.push('cwd');
175
+ }
176
+ if (data.participants !== undefined) {
177
+ if (Array.isArray(data.participants)) {
178
+ patch.participants = data.participants
179
+ .filter((x) => typeof x === 'string')
180
+ .map((s) => s.trim())
181
+ .filter(Boolean);
182
+ }
183
+ else {
184
+ patch.participants = [];
185
+ }
186
+ updatedFields.push('participants');
187
+ }
188
+ try {
189
+ if (updatedFields.length > 0) {
190
+ await storage.patchChannelState({ channelId: targetChannelId, state: patch });
191
+ }
192
+ if (targetChannelId === context.state.channelId) {
193
+ context.state.channelDetails = await storage.getChannelDetails({
194
+ channelId: context.state.channelId,
195
+ });
196
+ }
197
+ yield {
198
+ type: 'action:update_channel:result',
199
+ data: { success: true, channelId: targetChannelId, updatedFields },
200
+ meta: resultMeta,
201
+ };
202
+ }
203
+ catch {
204
+ yield {
205
+ type: 'action:update_channel:result',
206
+ data: { success: false, channelId: targetChannelId, updatedFields },
207
+ meta: resultMeta,
208
+ };
209
+ }
210
+ });
211
+ builder.on('action:patch_channel_details', async function* (event, context) {
212
+ const updatedFields = [];
213
+ const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
214
+ const data = (event.data || {});
215
+ try {
216
+ if (data.state !== undefined) {
217
+ await storage.patchChannelState({
218
+ channelId: context.state.channelId,
219
+ state: data.state,
220
+ });
221
+ updatedFields.push('state');
222
+ }
223
+ if (typeof data.spec === 'string') {
224
+ await storage.patchChannelSpec({
225
+ channelId: context.state.channelId,
226
+ spec: data.spec,
227
+ });
228
+ updatedFields.push('spec');
229
+ }
230
+ if (typeof data.cwd === 'string') {
231
+ await storage.patchChannelState({
232
+ channelId: context.state.channelId,
233
+ state: { cwd: data.cwd },
234
+ });
235
+ updatedFields.push('cwd');
236
+ }
237
+ if (data.participants !== undefined) {
238
+ const normalized = Array.isArray(data.participants)
239
+ ? data.participants
240
+ .filter((x) => typeof x === 'string')
241
+ .map((s) => s.trim())
242
+ .filter(Boolean)
243
+ : [];
244
+ await storage.patchChannelState({
245
+ channelId: context.state.channelId,
246
+ state: { participants: normalized },
247
+ });
248
+ updatedFields.push('participants');
249
+ }
250
+ context.state.channelDetails = await storage.getChannelDetails({
251
+ channelId: context.state.channelId,
252
+ });
253
+ yield {
254
+ type: "client:ui:widget",
255
+ data: {
256
+ widgetId: "patch-channel-details-result" + Date.now(),
257
+ kind: "message",
258
+ title: "Channel details updated.",
259
+ body: "The channel details have been updated.",
260
+ },
261
+ meta: resultMeta,
262
+ };
263
+ yield {
264
+ type: 'action:patch_channel_details:result',
265
+ data: { success: true, updatedFields },
266
+ meta: resultMeta,
267
+ };
268
+ }
269
+ catch {
270
+ yield {
271
+ type: 'action:patch_channel_details:result',
272
+ data: { success: false, updatedFields },
273
+ meta: resultMeta,
274
+ };
275
+ }
276
+ });
277
+ builder.on('action:patch_thread_details', async function* (event, context) {
278
+ const updatedFields = [];
279
+ const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
280
+ try {
281
+ if (!context.state.threadId) {
282
+ throw new Error('Missing threadId in state for patch_thread_details');
283
+ }
284
+ if (event.data.state !== undefined) {
285
+ await storage.patchThreadState({
286
+ channelId: context.state.channelId,
287
+ threadId: context.state.threadId,
288
+ state: event.data.state,
289
+ });
290
+ updatedFields.push('state');
291
+ }
292
+ context.state.threadDetails = await storage.getThreadDetails({
293
+ channelId: context.state.channelId,
294
+ threadId: context.state.threadId,
295
+ });
296
+ yield {
297
+ type: 'action:patch_thread_details:result',
298
+ data: { success: true, updatedFields },
299
+ meta: resultMeta,
300
+ };
301
+ }
302
+ catch {
303
+ yield {
304
+ type: 'action:patch_thread_details:result',
305
+ data: { success: false, updatedFields },
306
+ meta: resultMeta,
307
+ };
308
+ }
309
+ });
310
+ builder.on('agent:usage', async function* (event, context) {
311
+ const { usage } = event.data;
312
+ if (!context.state.threadId)
313
+ return;
314
+ const currentState = context.state.threadDetails?.state || {};
315
+ const currentUsage = currentState.usage || {
316
+ promptTokens: 0,
317
+ completionTokens: 0,
318
+ totalTokens: 0,
319
+ };
320
+ const nextUsage = {
321
+ promptTokens: (currentUsage.promptTokens || 0) + usage.promptTokens,
322
+ completionTokens: (currentUsage.completionTokens || 0) + usage.completionTokens,
323
+ totalTokens: (currentUsage.totalTokens || 0) + usage.totalTokens,
324
+ };
325
+ await storage.patchThreadState({
326
+ channelId: context.state.channelId,
327
+ threadId: context.state.threadId,
328
+ state: { usage: nextUsage },
329
+ });
330
+ context.state.threadDetails = await storage.getThreadDetails({
331
+ channelId: context.state.channelId,
332
+ threadId: context.state.threadId,
333
+ });
334
+ });
335
+ builder.on('action:storage:get-channels', async function* () {
336
+ const channels = await storage.getChannels();
337
+ yield { type: 'action:storage:get-channels-result', data: { channels } };
338
+ });
339
+ builder.on('action:storage:get-threads', async function* (event) {
340
+ const threads = await storage.getThreads({ channelId: event.data.channelId });
341
+ yield { type: 'action:storage:get-threads-result', data: { threads } };
342
+ });
343
+ builder.on('action:storage:get-channel-details', async function* (_, state) {
344
+ const channelDetails = await storage.getChannelDetails({
345
+ channelId: state.state.channelId,
346
+ });
347
+ yield { type: 'action:storage:get-channel-details-result', data: { channelDetails } };
348
+ });
349
+ builder.on('action:storage:get-thread-details', async function* (_, state) {
350
+ const threadId = state.state.threadId;
351
+ const threadDetails = threadId
352
+ ? await storage.getThreadDetails({ channelId: state.state.channelId, threadId })
353
+ : null;
354
+ yield { type: 'action:storage:get-thread-details-result', data: { threadDetails } };
355
+ });
356
+ builder.on('action:storage:get-agents', async function* () {
357
+ const agents = await storage.getAgents();
358
+ yield { type: 'action:storage:get-agents-result', data: { agents } };
359
+ });
360
+ builder.on('action:storage:get-plugins', async function* () {
361
+ const plugins = await storage.getPlugins();
362
+ yield { type: 'action:storage:get-plugins-result', data: { plugins } };
363
+ });
364
+ builder.on('action:storage:get-agent-details', async function* (event) {
365
+ try {
366
+ const agentDetails = await storage.getAgentDetails({ agentId: event.data.agentId });
367
+ yield { type: 'action:storage:get-agent-details-result', data: { agentDetails } };
368
+ }
369
+ catch (error) {
370
+ console.error(`[storage] Failed to get agent details for ${event.data.agentId}`, error);
371
+ yield {
372
+ type: 'action:storage:get-agent-details-result',
373
+ data: {
374
+ agentDetails: null,
375
+ error: error instanceof Error ? error.message : 'Unknown error',
376
+ },
377
+ };
378
+ }
379
+ });
380
+ builder.on('action:storage:create-agent', async function* (event) {
381
+ try {
382
+ const { agentId, name, description, image, hidden, instructions, plugins } = event.data;
383
+ await storage.createAgent({
384
+ agentId,
385
+ name,
386
+ description,
387
+ image,
388
+ hidden,
389
+ instructions,
390
+ plugins,
391
+ });
392
+ yield { type: 'action:storage:create-agent-result', data: { success: true } };
393
+ }
394
+ catch (error) {
395
+ yield {
396
+ type: 'action:storage:create-agent-result',
397
+ data: {
398
+ success: false,
399
+ error: error instanceof Error ? error.message : 'Unknown error',
400
+ },
401
+ };
402
+ }
403
+ });
404
+ builder.on('action:storage:update-agent', async function* (event) {
405
+ try {
406
+ const { agentId, name, description, image, hidden, instructions, plugins } = event.data;
407
+ await storage.updateAgent({
408
+ agentId,
409
+ name,
410
+ description,
411
+ image,
412
+ hidden,
413
+ instructions,
414
+ plugins,
415
+ });
416
+ yield { type: 'action:storage:update-agent-result', data: { success: true } };
417
+ }
418
+ catch (error) {
419
+ yield {
420
+ type: 'action:storage:update-agent-result',
421
+ data: {
422
+ success: false,
423
+ error: error instanceof Error ? error.message : 'Unknown error',
424
+ },
425
+ };
426
+ }
427
+ });
428
+ builder.on('action:storage:delete-agent', async function* (event) {
429
+ try {
430
+ await storage.deleteAgent({ agentId: event.data.agentId });
431
+ yield { type: 'action:storage:delete-agent-result', data: { success: true } };
432
+ }
433
+ catch (error) {
434
+ yield {
435
+ type: 'action:storage:delete-agent-result',
436
+ data: {
437
+ success: false,
438
+ error: error instanceof Error ? error.message : 'Unknown error',
439
+ },
440
+ };
441
+ }
442
+ });
443
+ builder.on('action:storage:get-events', async function* (_, state) {
444
+ const events = await storage.getEvents(state.state);
445
+ yield { type: 'action:storage:get-events-result', data: { events } };
446
+ });
447
+ builder.on('action:storage:get-variables', async function* () {
448
+ const variables = await storage.getVariables();
449
+ const masked = {};
450
+ for (const [key, val] of Object.entries(variables)) {
451
+ if (typeof val === 'object' && val !== null && val.secret) {
452
+ masked[key] = '********';
453
+ }
454
+ else {
455
+ masked[key] = typeof val === 'string' ? val : val.value;
456
+ }
457
+ }
458
+ yield {
459
+ type: 'action:storage:get-variables-result',
460
+ data: { variables: masked },
461
+ };
462
+ });
463
+ builder.on('action:storage:create-variable', async function* (event) {
464
+ try {
465
+ const { key, value, secret } = event.data;
466
+ await storage.createVariable({ key, value, secret });
467
+ yield { type: 'action:storage:create-variable-result', data: { success: true } };
468
+ }
469
+ catch (error) {
470
+ yield {
471
+ type: 'action:storage:create-variable-result',
472
+ data: {
473
+ success: false,
474
+ error: error instanceof Error ? error.message : 'Unknown error',
475
+ },
476
+ };
477
+ }
478
+ });
479
+ builder.on('action:storage:delete-variable', async function* (event) {
480
+ try {
481
+ await storage.deleteVariable({ key: event.data.key });
482
+ yield { type: 'action:storage:delete-variable-result', data: { success: true } };
483
+ }
484
+ catch (error) {
485
+ yield {
486
+ type: 'action:storage:delete-variable-result',
487
+ data: {
488
+ success: false,
489
+ error: error instanceof Error ? error.message : 'Unknown error',
490
+ },
491
+ };
492
+ }
493
+ });
494
+ builder.on('action:storage:patch-channel-state', async function* (event, state) {
495
+ try {
496
+ await storage.patchChannelState({
497
+ channelId: state.state.channelId,
498
+ state: event.data.state,
499
+ });
500
+ yield { type: 'action:storage:patch-channel-state-result', data: { success: true } };
501
+ }
502
+ catch {
503
+ yield { type: 'action:storage:patch-channel-state-result', data: { success: false } };
504
+ }
505
+ });
506
+ builder.on('action:storage:patch-thread-state', async function* (event, state) {
507
+ try {
508
+ if (!state.state.threadId) {
509
+ throw new Error('Missing threadId in state for patch-thread-state');
510
+ }
511
+ await storage.patchThreadState({
512
+ channelId: state.state.channelId,
513
+ threadId: state.state.threadId,
514
+ state: event.data.state,
515
+ });
516
+ yield { type: 'action:storage:patch-thread-state-result', data: { success: true } };
517
+ }
518
+ catch {
519
+ yield { type: 'action:storage:patch-thread-state-result', data: { success: false } };
520
+ }
521
+ });
522
+ builder.on('action:storage:list-files', async function* (event, context) {
523
+ const channelId = context.state.channelId;
524
+ const subPath = event.data?.path || '';
525
+ try {
526
+ const files = await storage.listFiles({ channelId, path: subPath });
527
+ yield {
528
+ type: 'action:storage:list-files:result',
529
+ data: { success: true, files },
530
+ };
531
+ }
532
+ catch (error) {
533
+ yield {
534
+ type: 'action:storage:list-files:result',
535
+ data: {
536
+ success: false,
537
+ files: [],
538
+ error: error instanceof Error ? error.message : 'Unknown error',
539
+ },
540
+ };
541
+ }
542
+ });
543
+ builder.on('action:storage:read-file', async function* (event, context) {
544
+ const channelId = context.state.channelId;
545
+ const filePath = event.data?.path;
546
+ if (!filePath) {
547
+ yield {
548
+ type: 'action:storage:read-file:result',
549
+ data: { success: false, path: '', error: 'Path is required' },
550
+ };
551
+ return;
552
+ }
553
+ try {
554
+ const content = await storage.readFile({ channelId, path: filePath });
555
+ yield {
556
+ type: 'action:storage:read-file:result',
557
+ data: { success: true, content, path: filePath },
558
+ };
559
+ }
560
+ catch (error) {
561
+ yield {
562
+ type: 'action:storage:read-file:result',
563
+ data: {
564
+ success: false,
565
+ path: filePath,
566
+ error: error instanceof Error ? error.message : 'Unknown error',
567
+ },
568
+ };
569
+ }
570
+ });
571
+ },
572
+ };
573
+ export default storagePlugin;