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.
- package/dist/app/cli.js +1 -1
- package/dist/app/config.js +10 -0
- package/dist/app/server.js +200 -3
- package/dist/harness/index.js +18 -0
- package/dist/plugins/approval/index.js +35 -20
- package/dist/plugins/bash/index.js +195 -0
- package/dist/plugins/delegation/index.js +6 -2
- package/dist/plugins/openbot/context.js +54 -9
- package/dist/plugins/openbot/history.js +47 -1
- package/dist/plugins/openbot/index.js +43 -3
- package/dist/plugins/openbot/runtime.js +91 -27
- package/dist/plugins/openbot/system-prompt.js +21 -1
- package/dist/plugins/plugin-manager/index.js +87 -3
- package/dist/plugins/shell/index.js +2 -1
- package/dist/plugins/storage/files.js +67 -0
- package/dist/plugins/storage/index.js +184 -7
- package/dist/plugins/storage/service.js +215 -59
- package/dist/plugins/ui/index.js +109 -150
- package/dist/services/abort.js +43 -0
- package/dist/services/plugins/registry.js +5 -3
- package/dist/services/plugins/service.js +66 -11
- package/docs/agents.md +5 -8
- package/docs/architecture.md +1 -1
- package/docs/plugins.md +28 -7
- package/docs/templates/AGENT.example.md +4 -4
- package/package.json +7 -7
- package/src/app/cli.ts +1 -1
- package/src/app/config.ts +13 -0
- package/src/app/server.ts +235 -3
- package/src/app/types.ts +284 -14
- package/src/harness/index.ts +21 -0
- package/src/plugins/approval/index.ts +37 -20
- package/src/plugins/bash/index.ts +232 -0
- package/src/plugins/delegation/index.ts +7 -2
- package/src/plugins/openbot/context.ts +58 -9
- package/src/plugins/openbot/history.ts +52 -1
- package/src/plugins/openbot/index.ts +45 -3
- package/src/plugins/openbot/runtime.ts +121 -27
- package/src/plugins/openbot/system-prompt.ts +21 -1
- package/src/plugins/plugin-manager/index.ts +105 -3
- package/src/plugins/storage/files.ts +81 -0
- package/src/plugins/storage/index.ts +198 -8
- package/src/plugins/storage/service.ts +282 -59
- package/src/plugins/ui/index.ts +123 -0
- package/src/services/abort.ts +46 -0
- package/src/services/plugins/domain.ts +34 -1
- package/src/services/plugins/registry.ts +5 -3
- package/src/services/plugins/service.ts +136 -45
- package/src/services/plugins/types.ts +5 -1
- 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
|
|
577
|
-
type: 'action:
|
|
737
|
+
export type BashEvent = BaseEvent & {
|
|
738
|
+
type: 'action:bash';
|
|
578
739
|
data: {
|
|
579
740
|
command: string;
|
|
580
|
-
|
|
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
|
|
593
|
-
type: 'action:
|
|
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
|
|
827
|
+
export type ListMarketplaceRegistryEvent = BaseEvent & {
|
|
638
828
|
type: 'action:marketplace:list';
|
|
639
829
|
};
|
|
640
830
|
|
|
641
|
-
export type
|
|
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
|
-
|
|
|
841
|
-
|
|
|
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
|
-
|
|
|
847
|
-
|
|
|
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;
|
package/src/harness/index.ts
CHANGED
|
@@ -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:
|
|
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
|
|
22
|
-
const actionsToApprove = (config.actions as string[]) || ['action:
|
|
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 =
|
|
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
|
-
|
|
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:
|
|
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
|
-
//
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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',
|