11
11
from openhands .controller import AgentController
12
12
from openhands .controller .agent import Agent
13
13
from openhands .core .config import (
14
+ AppConfig ,
14
15
get_parser ,
15
16
load_app_config ,
16
17
)
20
21
from openhands .events import EventSource , EventStream , EventStreamSubscriber
21
22
from openhands .events .action import (
22
23
Action ,
24
+ ActionConfirmationStatus ,
23
25
ChangeAgentStateAction ,
24
26
CmdRunAction ,
25
27
FileEditAction ,
30
32
AgentStateChangedObservation ,
31
33
CmdOutputObservation ,
32
34
FileEditObservation ,
35
+ NullObservation ,
33
36
)
34
37
from openhands .llm .llm import LLM
35
38
from openhands .runtime import get_runtime_cls
36
39
from openhands .runtime .base import Runtime
40
+ from openhands .security import SecurityAnalyzer , options
37
41
from openhands .storage import get_file_store
38
42
39
43
@@ -45,6 +49,15 @@ def display_command(command: str):
45
49
print ('❯ ' + colored (command + '\n ' , 'green' ))
46
50
47
51
52
+ def display_confirmation (confirmation_state : ActionConfirmationStatus ):
53
+ if confirmation_state == ActionConfirmationStatus .CONFIRMED :
54
+ print (colored ('✅ ' + confirmation_state + '\n ' , 'green' ))
55
+ elif confirmation_state == ActionConfirmationStatus .REJECTED :
56
+ print (colored ('❌ ' + confirmation_state + '\n ' , 'red' ))
57
+ else :
58
+ print (colored ('⏳ ' + confirmation_state + '\n ' , 'yellow' ))
59
+
60
+
48
61
def display_command_output (output : str ):
49
62
lines = output .split ('\n ' )
50
63
for line in lines :
@@ -59,7 +72,7 @@ def display_file_edit(event: FileEditAction | FileEditObservation):
59
72
print (colored (str (event ), 'green' ))
60
73
61
74
62
- def display_event (event : Event ):
75
+ def display_event (event : Event , config : AppConfig ):
63
76
if isinstance (event , Action ):
64
77
if hasattr (event , 'thought' ):
65
78
display_message (event .thought )
@@ -74,6 +87,8 @@ def display_event(event: Event):
74
87
display_file_edit (event )
75
88
if isinstance (event , FileEditObservation ):
76
89
display_file_edit (event )
90
+ if hasattr (event , 'confirmation_state' ) and config .security .confirmation_mode :
91
+ display_confirmation (event .confirmation_state )
77
92
78
93
79
94
async def main ():
@@ -119,12 +134,18 @@ async def main():
119
134
headless_mode = True ,
120
135
)
121
136
137
+ if config .security .security_analyzer :
138
+ options .SecurityAnalyzers .get (
139
+ config .security .security_analyzer , SecurityAnalyzer
140
+ )(event_stream )
141
+
122
142
controller = AgentController (
123
143
agent = agent ,
124
144
max_iterations = config .max_iterations ,
125
145
max_budget_per_task = config .max_budget_per_task ,
126
146
agent_to_llm_config = config .get_agent_to_llm_config_map (),
127
147
event_stream = event_stream ,
148
+ confirmation_mode = config .security .confirmation_mode ,
128
149
)
129
150
130
151
async def prompt_for_next_task ():
@@ -143,14 +164,34 @@ async def prompt_for_next_task():
143
164
action = MessageAction (content = next_message )
144
165
event_stream .add_event (action , EventSource .USER )
145
166
167
+ async def prompt_for_user_confirmation ():
168
+ loop = asyncio .get_event_loop ()
169
+ user_confirmation = await loop .run_in_executor (
170
+ None , lambda : input ('Confirm action (possible security risk)? (y/n) >> ' )
171
+ )
172
+ return user_confirmation .lower () == 'y'
173
+
146
174
async def on_event (event : Event ):
147
- display_event (event )
175
+ display_event (event , config )
148
176
if isinstance (event , AgentStateChangedObservation ):
149
177
if event .agent_state in [
150
178
AgentState .AWAITING_USER_INPUT ,
151
179
AgentState .FINISHED ,
152
180
]:
153
181
await prompt_for_next_task ()
182
+ if (
183
+ isinstance (event , NullObservation )
184
+ and controller .state .agent_state == AgentState .AWAITING_USER_CONFIRMATION
185
+ ):
186
+ user_confirmed = await prompt_for_user_confirmation ()
187
+ if user_confirmed :
188
+ event_stream .add_event (
189
+ ChangeAgentStateAction (AgentState .USER_CONFIRMED ), EventSource .USER
190
+ )
191
+ else :
192
+ event_stream .add_event (
193
+ ChangeAgentStateAction (AgentState .USER_REJECTED ), EventSource .USER
194
+ )
154
195
155
196
event_stream .subscribe (EventStreamSubscriber .MAIN , on_event , str (uuid4 ()))
156
197
0 commit comments