openbot 0.4.0 → 0.4.3

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 (50) hide show
  1. package/dist/app/cli.js +1 -1
  2. package/dist/app/config.js +10 -0
  3. package/dist/app/server.js +200 -3
  4. package/dist/harness/index.js +18 -0
  5. package/dist/plugins/approval/index.js +35 -20
  6. package/dist/plugins/bash/index.js +195 -0
  7. package/dist/plugins/delegation/index.js +6 -2
  8. package/dist/plugins/openbot/context.js +54 -9
  9. package/dist/plugins/openbot/history.js +47 -1
  10. package/dist/plugins/openbot/index.js +43 -3
  11. package/dist/plugins/openbot/runtime.js +91 -27
  12. package/dist/plugins/openbot/system-prompt.js +21 -1
  13. package/dist/plugins/plugin-manager/index.js +87 -3
  14. package/dist/plugins/shell/index.js +2 -1
  15. package/dist/plugins/storage/files.js +67 -0
  16. package/dist/plugins/storage/index.js +184 -7
  17. package/dist/plugins/storage/service.js +215 -59
  18. package/dist/plugins/ui/index.js +109 -150
  19. package/dist/services/abort.js +43 -0
  20. package/dist/services/plugins/registry.js +5 -3
  21. package/dist/services/plugins/service.js +66 -11
  22. package/docs/agents.md +5 -8
  23. package/docs/architecture.md +1 -1
  24. package/docs/plugins.md +28 -7
  25. package/docs/templates/AGENT.example.md +4 -4
  26. package/package.json +7 -7
  27. package/src/app/cli.ts +1 -1
  28. package/src/app/config.ts +13 -0
  29. package/src/app/server.ts +235 -3
  30. package/src/app/types.ts +284 -14
  31. package/src/harness/index.ts +21 -0
  32. package/src/plugins/approval/index.ts +37 -20
  33. package/src/plugins/bash/index.ts +232 -0
  34. package/src/plugins/delegation/index.ts +7 -2
  35. package/src/plugins/openbot/context.ts +58 -9
  36. package/src/plugins/openbot/history.ts +52 -1
  37. package/src/plugins/openbot/index.ts +45 -3
  38. package/src/plugins/openbot/runtime.ts +121 -27
  39. package/src/plugins/openbot/system-prompt.ts +21 -1
  40. package/src/plugins/plugin-manager/index.ts +105 -3
  41. package/src/plugins/storage/files.ts +81 -0
  42. package/src/plugins/storage/index.ts +198 -8
  43. package/src/plugins/storage/service.ts +282 -59
  44. package/src/plugins/ui/index.ts +123 -0
  45. package/src/services/abort.ts +46 -0
  46. package/src/services/plugins/domain.ts +34 -1
  47. package/src/services/plugins/registry.ts +5 -3
  48. package/src/services/plugins/service.ts +136 -45
  49. package/src/services/plugins/types.ts +5 -1
  50. package/src/plugins/shell/index.ts +0 -123
package/src/app/types.ts CHANGED
@@ -21,6 +21,12 @@ export interface OpenBotState {
21
21
  triggerEvent?: OpenBotEvent;
22
22
  /** Active model string (e.g. `openai/gpt-4o-mini`). */
23
23
  model?: string;
24
+ /** Persisted tool call IDs waiting for results. */
25
+ pendingToolCallIds?: string[];
26
+ /** Current user information if available. */
27
+ currentUser?: {
28
+ userName?: string;
29
+ };
24
30
  }
25
31
 
26
32
  export type BaseEvent = {
@@ -63,6 +69,23 @@ export type GetThreadsResultEvent = BaseEvent & {
63
69
  };
64
70
  };
65
71
 
72
+ export type SetLastReadEvent = BaseEvent & {
73
+ type: 'action:storage:set-last-read';
74
+ data: {
75
+ channelId?: string;
76
+ threadId?: string;
77
+ lastReadEventId: string;
78
+ };
79
+ };
80
+
81
+ export type SetLastReadResultEvent = BaseEvent & {
82
+ type: 'action:storage:set-last-read-result';
83
+ data: {
84
+ success: boolean;
85
+ error?: string;
86
+ };
87
+ };
88
+
66
89
  export type GetChannelDetailsEvent = BaseEvent & {
67
90
  type: 'action:storage:get-channel-details';
68
91
  };
@@ -322,6 +345,7 @@ export type ReadFileEvent = BaseEvent & {
322
345
  type: 'action:storage:read-file';
323
346
  data: {
324
347
  path: string;
348
+ encoding?: 'utf8' | 'base64';
325
349
  };
326
350
  };
327
351
 
@@ -331,6 +355,107 @@ export type ReadFileResultEvent = BaseEvent & {
331
355
  success: boolean;
332
356
  content?: string;
333
357
  path: string;
358
+ encoding?: 'utf8' | 'base64';
359
+ mimeType?: string;
360
+ size?: number;
361
+ error?: string;
362
+ };
363
+ };
364
+
365
+ export type ServeFileEvent = BaseEvent & {
366
+ type: 'action:storage:serve-file';
367
+ data: {
368
+ path: string;
369
+ };
370
+ };
371
+
372
+ export type WriteFileEvent = BaseEvent & {
373
+ type: 'action:storage:write-file';
374
+ data: {
375
+ path: string;
376
+ content: string;
377
+ encoding?: 'utf8' | 'base64';
378
+ overwrite?: boolean;
379
+ };
380
+ };
381
+
382
+ export type WriteFileResultEvent = BaseEvent & {
383
+ type: 'action:storage:write-file:result';
384
+ data: {
385
+ success: boolean;
386
+ path: string;
387
+ url?: string;
388
+ mimeType?: string;
389
+ size?: number;
390
+ error?: string;
391
+ };
392
+ };
393
+
394
+ export type GetFileUrlEvent = BaseEvent & {
395
+ type: 'action:storage:get-file-url';
396
+ data: {
397
+ path: string;
398
+ };
399
+ };
400
+
401
+ export type GetFileUrlResultEvent = BaseEvent & {
402
+ type: 'action:storage:get-file-url:result';
403
+ data: {
404
+ success: boolean;
405
+ path: string;
406
+ url?: string;
407
+ mimeType?: string;
408
+ size?: number;
409
+ error?: string;
410
+ };
411
+ };
412
+
413
+ export type GetWorkspaceFileUrlEvent = BaseEvent & {
414
+ type: 'action:get_workspace_file_url';
415
+ data: {
416
+ path: string;
417
+ };
418
+ meta?: {
419
+ toolCallId?: string;
420
+ agentId?: string;
421
+ threadId?: string;
422
+ };
423
+ };
424
+
425
+ export type GetWorkspaceFileUrlResultEvent = BaseEvent & {
426
+ type: 'action:get_workspace_file_url:result';
427
+ data: {
428
+ success: boolean;
429
+ path: string;
430
+ url?: string;
431
+ mimeType?: string;
432
+ size?: number;
433
+ output?: string;
434
+ error?: string;
435
+ };
436
+ meta?: {
437
+ toolCallId?: string;
438
+ agentId?: string;
439
+ threadId?: string;
440
+ };
441
+ };
442
+
443
+ export type UploadFileEvent = BaseEvent & {
444
+ type: 'action:storage:upload-file';
445
+ data: {
446
+ path: string;
447
+ overwrite?: boolean;
448
+ };
449
+ };
450
+
451
+ export type UploadFileResultEvent = BaseEvent & {
452
+ type: 'action:storage:upload-file:result';
453
+ data: {
454
+ success: boolean;
455
+ path: string;
456
+ url?: string;
457
+ mimeType?: string;
458
+ size?: number;
334
459
  error?: string;
335
460
  };
336
461
  };
@@ -479,6 +604,42 @@ export type UpdateChannelResultEvent = BaseEvent & {
479
604
  };
480
605
  };
481
606
 
607
+ export type DeleteChannelEvent = BaseEvent & {
608
+ type: 'action:storage:delete-channel';
609
+ data: {
610
+ channelId: string;
611
+ };
612
+ };
613
+
614
+ export type DeleteChannelResultEvent = BaseEvent & {
615
+ type: 'action:storage:delete-channel-result';
616
+ data: {
617
+ success: boolean;
618
+ error?: string;
619
+ };
620
+ };
621
+
622
+ export type DeleteChannelToolEvent = BaseEvent & {
623
+ type: 'action:delete_channel';
624
+ data: {
625
+ channelId: string;
626
+ };
627
+ meta?: {
628
+ toolCallId?: string;
629
+ agentId?: string;
630
+ threadId?: string;
631
+ };
632
+ };
633
+
634
+ export type DeleteChannelToolResultEvent = BaseEvent & {
635
+ type: 'action:delete_channel:result';
636
+ data: {
637
+ success: boolean;
638
+ channelId: string;
639
+ error?: string;
640
+ };
641
+ };
642
+
482
643
  export type UIWidgetAction = {
483
644
  id: string;
484
645
  label: string;
@@ -573,13 +734,11 @@ export type UIWidgetResponseEvent = BaseEvent & {
573
734
  };
574
735
  };
575
736
 
576
- export type ShellExecEvent = BaseEvent & {
577
- type: 'action:shell_exec';
737
+ export type BashEvent = BaseEvent & {
738
+ type: 'action:bash';
578
739
  data: {
579
740
  command: string;
580
- cwd?: string;
581
- shell?: string;
582
- timeoutMs?: number;
741
+ restart?: boolean;
583
742
  };
584
743
  meta?: {
585
744
  toolCallId?: string;
@@ -589,8 +748,8 @@ export type ShellExecEvent = BaseEvent & {
589
748
  };
590
749
  };
591
750
 
592
- export type ShellExecResultEvent = BaseEvent & {
593
- type: 'action:shell_exec:result';
751
+ export type BashResultEvent = BaseEvent & {
752
+ type: 'action:bash:result';
594
753
  data: {
595
754
  success: boolean;
596
755
  approved?: boolean;
@@ -602,6 +761,37 @@ export type ShellExecResultEvent = BaseEvent & {
602
761
  };
603
762
  };
604
763
 
764
+ export type BashStopEvent = BaseEvent & {
765
+ type: 'action:bash_stop';
766
+ data: {
767
+ channelId?: string;
768
+ };
769
+ };
770
+
771
+ export type BashStopResultEvent = BaseEvent & {
772
+ type: 'action:bash_stop:result';
773
+ data: {
774
+ success: boolean;
775
+ };
776
+ };
777
+
778
+ export type BashListSessionsEvent = BaseEvent & {
779
+ type: 'action:bash_list_sessions';
780
+ data: {};
781
+ };
782
+
783
+ export type BashListSessionsResultEvent = BaseEvent & {
784
+ type: 'action:bash_list_sessions:result';
785
+ data: {
786
+ success: boolean;
787
+ sessions: Array<{
788
+ channelId: string;
789
+ cwd: string;
790
+ lastActivity: number;
791
+ }>;
792
+ };
793
+ };
794
+
605
795
  export type InstallPluginEvent = BaseEvent & {
606
796
  type: 'action:plugin:install';
607
797
  data: {
@@ -634,11 +824,11 @@ export type UninstallPluginResultEvent = BaseEvent & {
634
824
  };
635
825
  };
636
826
 
637
- export type ListMarketplaceAgentsEvent = BaseEvent & {
827
+ export type ListMarketplaceRegistryEvent = BaseEvent & {
638
828
  type: 'action:marketplace:list';
639
829
  };
640
830
 
641
- export type ListMarketplaceAgentsResultEvent = BaseEvent & {
831
+ export type ListMarketplaceRegistryResultEvent = BaseEvent & {
642
832
  type: 'action:marketplace:list:result';
643
833
  data: {
644
834
  success: boolean;
@@ -650,6 +840,36 @@ export type ListMarketplaceAgentsResultEvent = BaseEvent & {
650
840
  instructions: string;
651
841
  plugins: PluginRef[];
652
842
  }>;
843
+ channels: Array<{
844
+ id: string;
845
+ name: string;
846
+ description: string;
847
+ image?: string;
848
+ spec?: string;
849
+ initialState?: Record<string, unknown>;
850
+ participants: string[];
851
+ starterPrompts?: Array<{ label: string; prompt: string }>;
852
+ }>;
853
+ error?: string;
854
+ };
855
+ };
856
+
857
+ export type InstallChannelEvent = BaseEvent & {
858
+ type: 'action:channel:install';
859
+ data: {
860
+ channelId: string;
861
+ name?: string;
862
+ participants?: string[];
863
+ initialState?: Record<string, unknown>;
864
+ };
865
+ };
866
+
867
+ export type InstallChannelResultEvent = BaseEvent & {
868
+ type: 'action:channel:install:result';
869
+ data: {
870
+ success: boolean;
871
+ channelId?: string;
872
+ channelUrl?: string;
653
873
  error?: string;
654
874
  };
655
875
  };
@@ -764,6 +984,33 @@ export type DelegateTaskResultEvent = BaseEvent & {
764
984
  };
765
985
  };
766
986
 
987
+ export type RenderWidgetEvent = BaseEvent & {
988
+ type: 'action:render_widget';
989
+ data: {
990
+ kind: 'message' | 'choice' | 'form' | 'list';
991
+ title: string;
992
+ description?: string;
993
+ fields?: UIWidgetField[];
994
+ actions?: UIWidgetAction[];
995
+ items?: UIWidgetListItem[];
996
+ submitLabel?: string;
997
+ };
998
+ meta?: {
999
+ toolCallId?: string;
1000
+ threadId?: string;
1001
+ [key: string]: any;
1002
+ };
1003
+ };
1004
+
1005
+ export type RenderWidgetResultEvent = BaseEvent & {
1006
+ type: 'action:render_widget:result';
1007
+ data: {
1008
+ success: boolean;
1009
+ actionId: string;
1010
+ values?: Record<string, unknown>;
1011
+ };
1012
+ };
1013
+
767
1014
  /**
768
1015
  * Internal message representation to decouple harness history from specific AI SDKs.
769
1016
  */
@@ -792,6 +1039,8 @@ export type OpenBotEvent =
792
1039
  | GetChannelsResultEvent
793
1040
  | GetThreadsEvent
794
1041
  | GetThreadsResultEvent
1042
+ | SetLastReadEvent
1043
+ | SetLastReadResultEvent
795
1044
  | GetChannelDetailsEvent
796
1045
  | GetChannelDetailsResultEvent
797
1046
  | GetThreadDetailsEvent
@@ -829,24 +1078,43 @@ export type OpenBotEvent =
829
1078
  | ListFilesResultEvent
830
1079
  | ReadFileEvent
831
1080
  | ReadFileResultEvent
1081
+ | ServeFileEvent
1082
+ | WriteFileEvent
1083
+ | WriteFileResultEvent
1084
+ | GetFileUrlEvent
1085
+ | GetFileUrlResultEvent
1086
+ | GetWorkspaceFileUrlEvent
1087
+ | GetWorkspaceFileUrlResultEvent
1088
+ | UploadFileEvent
1089
+ | UploadFileResultEvent
832
1090
  | CreateThreadEvent
833
1091
  | CreateThreadResultEvent
834
1092
  | CreateChannelEvent
835
1093
  | CreateChannelResultEvent
836
1094
  | UpdateChannelEvent
837
1095
  | UpdateChannelResultEvent
1096
+ | DeleteChannelEvent
1097
+ | DeleteChannelResultEvent
1098
+ | DeleteChannelToolEvent
1099
+ | DeleteChannelToolResultEvent
838
1100
  | UIWidgetEvent
839
1101
  | UIWidgetResponseEvent
840
- | ShellExecEvent
841
- | ShellExecResultEvent
1102
+ | BashEvent
1103
+ | BashResultEvent
1104
+ | BashStopEvent
1105
+ | BashStopResultEvent
1106
+ | BashListSessionsEvent
1107
+ | BashListSessionsResultEvent
842
1108
  | InstallPluginEvent
843
1109
  | InstallPluginResultEvent
844
1110
  | UninstallPluginEvent
845
1111
  | UninstallPluginResultEvent
846
- | ListMarketplaceAgentsEvent
847
- | ListMarketplaceAgentsResultEvent
1112
+ | ListMarketplaceRegistryEvent
1113
+ | ListMarketplaceRegistryResultEvent
848
1114
  | InstallAgentEvent
849
1115
  | InstallAgentResultEvent
1116
+ | InstallChannelEvent
1117
+ | InstallChannelResultEvent
850
1118
  | RememberEvent
851
1119
  | RememberResultEvent
852
1120
  | RecallEvent
@@ -854,4 +1122,6 @@ export type OpenBotEvent =
854
1122
  | ForgetEvent
855
1123
  | ForgetResultEvent
856
1124
  | DelegateTaskEvent
857
- | DelegateTaskResultEvent;
1125
+ | DelegateTaskResultEvent
1126
+ | RenderWidgetEvent
1127
+ | RenderWidgetResultEvent;
@@ -5,6 +5,9 @@ import { storageService } from '../plugins/storage/service.js';
5
5
  import { STATE_AGENT_ID, ORCHESTRATOR_AGENT_ID } from '../app/agent-ids.js';
6
6
  import { resolvePlugin } from '../services/plugins/registry.js';
7
7
  import { ToolDefinition } from '../services/plugins/types.js';
8
+ import { abortRegistry, abortKey } from '../services/abort.js';
9
+ import { loadConfig } from '../app/config.js';
10
+ import { getPublicBaseUrl } from '../plugins/storage/files.js';
8
11
 
9
12
  export { STATE_AGENT_ID, ORCHESTRATOR_AGENT_ID };
10
13
 
@@ -15,6 +18,8 @@ export interface RunAgentOptions {
15
18
  channelId: string;
16
19
  threadId?: string;
17
20
  persistEvents?: boolean;
21
+ /** Resolved public base URL for the server. */
22
+ publicBaseUrl?: string;
18
23
  onEvent: (event: OpenBotEvent, state?: OpenBotState) => Promise<void>;
19
24
  }
20
25
 
@@ -67,6 +72,13 @@ export async function runAgent(options: RunAgentOptions): Promise<void> {
67
72
  const { runId, agentId, event, channelId, threadId, onEvent } = options;
68
73
  const persistEvents = options.persistEvents !== false;
69
74
 
75
+ let publicBaseUrl = options.publicBaseUrl;
76
+ if (!publicBaseUrl) {
77
+ const config = loadConfig();
78
+ const port = Number(config.port ?? process.env.PORT ?? 4132);
79
+ publicBaseUrl = getPublicBaseUrl(port, config.publicUrl);
80
+ }
81
+
70
82
  const parentAgentId = event.meta?.parentAgentId;
71
83
  const parentToolCallId = event.meta?.parentToolCallId;
72
84
 
@@ -80,6 +92,11 @@ export async function runAgent(options: RunAgentOptions): Promise<void> {
80
92
  event,
81
93
  });
82
94
 
95
+ // Shared per-thread abort signal so a stop request cancels this run and any
96
+ // delegated sub-agent runs (which execute in the same channel/thread).
97
+ const runKey = abortKey(channelId, threadId);
98
+ const abortSignal = abortRegistry.acquire(runKey);
99
+
83
100
  await emitEvent(
84
101
  {
85
102
  type: 'agent:run:start',
@@ -113,6 +130,8 @@ export async function runAgent(options: RunAgentOptions): Promise<void> {
113
130
  config: ref.config ?? {},
114
131
  storage: storageService,
115
132
  tools,
133
+ publicBaseUrl,
134
+ abortSignal,
116
135
  }),
117
136
  );
118
137
  }
@@ -121,6 +140,7 @@ export async function runAgent(options: RunAgentOptions): Promise<void> {
121
140
  const generator = runtime.run(event, { runId, state });
122
141
 
123
142
  for await (const outputEvent of generator) {
143
+ if (abortSignal.aborted) break;
124
144
  await emitEvent(outputEvent, state, {
125
145
  persistEvents,
126
146
  channelId,
@@ -133,6 +153,7 @@ export async function runAgent(options: RunAgentOptions): Promise<void> {
133
153
  } catch (error) {
134
154
  console.error(`[harness] Error running agent ${agentId}:`, error);
135
155
  } finally {
156
+ abortRegistry.release(runKey);
136
157
  await emitEvent(
137
158
  {
138
159
  type: 'agent:run:end',
@@ -5,7 +5,7 @@ import { OpenBotEvent } from '../../app/types.js';
5
5
  /**
6
6
  * `approval` — gates protected tool calls behind a UI confirmation widget.
7
7
  *
8
- * This is a simplified version that intercepts specified actions (default: shell_exec)
8
+ * This is a simplified version that intercepts specified actions (default: bash)
9
9
  * and requires user approval before they are allowed to proceed.
10
10
  */
11
11
 
@@ -17,9 +17,9 @@ export const approvalPlugin: Plugin = {
17
17
  id: 'approval',
18
18
  name: 'Approval',
19
19
  description: 'Gate protected tool calls behind a UI confirmation widget.',
20
- factory: ({ config }) => (builder) => {
21
- // Actions that require approval. Defaults to shell_exec.
22
- const actionsToApprove = (config.actions as string[]) || ['action:shell_exec'];
20
+ factory: ({ config, storage }) => (builder) => {
21
+ // Actions that require approval. Defaults to bash.
22
+ const actionsToApprove = (config.actions as string[]) || ['action:bash'];
23
23
 
24
24
  for (const action of actionsToApprove) {
25
25
  builder.intercept(action as OpenBotEvent['type'], (event, context) => {
@@ -27,14 +27,12 @@ export const approvalPlugin: Plugin = {
27
27
  if (event.meta?.approvalStatus === 'approved') return event;
28
28
 
29
29
  // Otherwise, intercept and ask for approval via a UI widget
30
- const displayData = action === 'action:shell_exec'
31
- ? `\`\`\`bash\n${(event as any).data.command}\n\`\`\``
32
- : `\`\`\`json\n${JSON.stringify((event as any).data, null, 2)}\n\`\`\``;
30
+ const displayData = JSON.stringify((event as any)?.data) || '';
33
31
 
34
32
  const widgetId = randomUUID();
35
33
  pendingApprovals.set(widgetId, Date.now());
36
34
 
37
- context.suspend({
35
+ return {
38
36
  type: 'client:ui:widget',
39
37
  data: {
40
38
  widgetId,
@@ -51,7 +49,7 @@ export const approvalPlugin: Plugin = {
51
49
  ],
52
50
  },
53
51
  meta: { agentId: context.state.agentId, threadId: context.state.threadId },
54
- } as OpenBotEvent);
52
+ } as OpenBotEvent;
55
53
  });
56
54
  }
57
55
 
@@ -80,6 +78,8 @@ export const approvalPlugin: Plugin = {
80
78
  const originalEvent = metadata.originalEvent as OpenBotEvent;
81
79
  const approved = actionId === 'approve';
82
80
 
81
+ const displayData = JSON.stringify((event as any)?.data) || '';
82
+
83
83
  // Yield a "responded" widget update to the UI
84
84
  yield {
85
85
  type: 'client:ui:widget',
@@ -87,7 +87,7 @@ export const approvalPlugin: Plugin = {
87
87
  widgetId,
88
88
  kind: 'message',
89
89
  title: `Action ${approved ? 'Approved' : 'Denied'}`,
90
- body: `The request for \`${originalEvent.type}\` was ${approved ? 'approved' : 'denied'}.`,
90
+ body: displayData,
91
91
  state: approved ? 'submitted' : 'cancelled',
92
92
  display: 'collapsed',
93
93
  disabled: true,
@@ -106,16 +106,33 @@ export const approvalPlugin: Plugin = {
106
106
  },
107
107
  };
108
108
  } else {
109
- // Emit a failure result event for the denied action
110
- // yield {
111
- // type: `${originalEvent.type}:result` as OpenBotEvent['type'],
112
- // data: {
113
- // success: false,
114
- // error: 'Action denied by user.',
115
- // stderr: 'Action denied by user.',
116
- // },
117
- // meta: originalEvent.meta,
118
- // } as OpenBotEvent;
109
+ // Manually store the original event with denied status so it's recorded in history
110
+ // but NOT re-emitted to the pipeline (to avoid actual execution).
111
+ if (storage) {
112
+ await storage.storeEvent({
113
+ channelId: context.state.channelId,
114
+ threadId: context.state.threadId,
115
+ event: {
116
+ ...originalEvent,
117
+ meta: {
118
+ ...(originalEvent.meta || {}),
119
+ approvalStatus: 'denied',
120
+ },
121
+ },
122
+ });
123
+ }
124
+
125
+ // Emit a failure result event for the denied action to clear the pending tool batch
126
+ yield {
127
+ type: `${originalEvent.type}:result` as OpenBotEvent['type'],
128
+ data: {
129
+ success: false,
130
+ error: 'Action denied by user.',
131
+ stderr: 'Action denied by user.',
132
+ output: 'Action denied by user.',
133
+ },
134
+ meta: originalEvent.meta,
135
+ } as OpenBotEvent;
119
136
 
120
137
  yield {
121
138
  type: 'agent:output',