|
|
@@ -11,6 +11,7 @@ from openhands import __version__
|
|
|
from openhands.controller import AgentController
|
|
|
from openhands.controller.agent import Agent
|
|
|
from openhands.core.config import (
|
|
|
+ AppConfig,
|
|
|
get_parser,
|
|
|
load_app_config,
|
|
|
)
|
|
|
@@ -20,6 +21,7 @@ from openhands.core.schema import AgentState
|
|
|
from openhands.events import EventSource, EventStream, EventStreamSubscriber
|
|
|
from openhands.events.action import (
|
|
|
Action,
|
|
|
+ ActionConfirmationStatus,
|
|
|
ChangeAgentStateAction,
|
|
|
CmdRunAction,
|
|
|
FileEditAction,
|
|
|
@@ -30,10 +32,12 @@ from openhands.events.observation import (
|
|
|
AgentStateChangedObservation,
|
|
|
CmdOutputObservation,
|
|
|
FileEditObservation,
|
|
|
+ NullObservation,
|
|
|
)
|
|
|
from openhands.llm.llm import LLM
|
|
|
from openhands.runtime import get_runtime_cls
|
|
|
from openhands.runtime.base import Runtime
|
|
|
+from openhands.security import SecurityAnalyzer, options
|
|
|
from openhands.storage import get_file_store
|
|
|
|
|
|
|
|
|
@@ -45,6 +49,15 @@ def display_command(command: str):
|
|
|
print('❯ ' + colored(command + '\n', 'green'))
|
|
|
|
|
|
|
|
|
+def display_confirmation(confirmation_state: ActionConfirmationStatus):
|
|
|
+ if confirmation_state == ActionConfirmationStatus.CONFIRMED:
|
|
|
+ print(colored('✅ ' + confirmation_state + '\n', 'green'))
|
|
|
+ elif confirmation_state == ActionConfirmationStatus.REJECTED:
|
|
|
+ print(colored('❌ ' + confirmation_state + '\n', 'red'))
|
|
|
+ else:
|
|
|
+ print(colored('⏳ ' + confirmation_state + '\n', 'yellow'))
|
|
|
+
|
|
|
+
|
|
|
def display_command_output(output: str):
|
|
|
lines = output.split('\n')
|
|
|
for line in lines:
|
|
|
@@ -59,7 +72,7 @@ def display_file_edit(event: FileEditAction | FileEditObservation):
|
|
|
print(colored(str(event), 'green'))
|
|
|
|
|
|
|
|
|
-def display_event(event: Event):
|
|
|
+def display_event(event: Event, config: AppConfig):
|
|
|
if isinstance(event, Action):
|
|
|
if hasattr(event, 'thought'):
|
|
|
display_message(event.thought)
|
|
|
@@ -74,6 +87,8 @@ def display_event(event: Event):
|
|
|
display_file_edit(event)
|
|
|
if isinstance(event, FileEditObservation):
|
|
|
display_file_edit(event)
|
|
|
+ if hasattr(event, 'confirmation_state') and config.security.confirmation_mode:
|
|
|
+ display_confirmation(event.confirmation_state)
|
|
|
|
|
|
|
|
|
async def main():
|
|
|
@@ -119,12 +134,18 @@ async def main():
|
|
|
headless_mode=True,
|
|
|
)
|
|
|
|
|
|
+ if config.security.security_analyzer:
|
|
|
+ options.SecurityAnalyzers.get(
|
|
|
+ config.security.security_analyzer, SecurityAnalyzer
|
|
|
+ )(event_stream)
|
|
|
+
|
|
|
controller = AgentController(
|
|
|
agent=agent,
|
|
|
max_iterations=config.max_iterations,
|
|
|
max_budget_per_task=config.max_budget_per_task,
|
|
|
agent_to_llm_config=config.get_agent_to_llm_config_map(),
|
|
|
event_stream=event_stream,
|
|
|
+ confirmation_mode=config.security.confirmation_mode,
|
|
|
)
|
|
|
|
|
|
async def prompt_for_next_task():
|
|
|
@@ -143,14 +164,34 @@ async def main():
|
|
|
action = MessageAction(content=next_message)
|
|
|
event_stream.add_event(action, EventSource.USER)
|
|
|
|
|
|
+ async def prompt_for_user_confirmation():
|
|
|
+ loop = asyncio.get_event_loop()
|
|
|
+ user_confirmation = await loop.run_in_executor(
|
|
|
+ None, lambda: input('Confirm action (possible security risk)? (y/n) >> ')
|
|
|
+ )
|
|
|
+ return user_confirmation.lower() == 'y'
|
|
|
+
|
|
|
async def on_event(event: Event):
|
|
|
- display_event(event)
|
|
|
+ display_event(event, config)
|
|
|
if isinstance(event, AgentStateChangedObservation):
|
|
|
if event.agent_state in [
|
|
|
AgentState.AWAITING_USER_INPUT,
|
|
|
AgentState.FINISHED,
|
|
|
]:
|
|
|
await prompt_for_next_task()
|
|
|
+ if (
|
|
|
+ isinstance(event, NullObservation)
|
|
|
+ and controller.state.agent_state == AgentState.AWAITING_USER_CONFIRMATION
|
|
|
+ ):
|
|
|
+ user_confirmed = await prompt_for_user_confirmation()
|
|
|
+ if user_confirmed:
|
|
|
+ event_stream.add_event(
|
|
|
+ ChangeAgentStateAction(AgentState.USER_CONFIRMED), EventSource.USER
|
|
|
+ )
|
|
|
+ else:
|
|
|
+ event_stream.add_event(
|
|
|
+ ChangeAgentStateAction(AgentState.USER_REJECTED), EventSource.USER
|
|
|
+ )
|
|
|
|
|
|
event_stream.subscribe(EventStreamSubscriber.MAIN, on_event, str(uuid4()))
|
|
|
|