diff --git a/openhands/events/action/commands.py b/openhands/events/action/commands.py index df67d8b9ef..44293e4f4d 100644 --- a/openhands/events/action/commands.py +++ b/openhands/events/action/commands.py @@ -19,7 +19,9 @@ class CmdRunAction(Action): blocking: bool = False # if True, the command will be run in a blocking manner, but a timeout must be set through _set_hard_timeout is_static: bool = False # if True, runs the command in a separate process cwd: str | None = None # current working directory, only used if is_static is True - hidden: bool = False + hidden: bool = ( + False # if True, this command does not go through the LLM or event stream + ) action: str = ActionType.RUN runnable: ClassVar[bool] = True confirmation_state: ActionConfirmationStatus = ActionConfirmationStatus.CONFIRMED diff --git a/openhands/events/observation/commands.py b/openhands/events/observation/commands.py index bcc1bcd4a0..7c532fbe0e 100644 --- a/openhands/events/observation/commands.py +++ b/openhands/events/observation/commands.py @@ -115,9 +115,12 @@ class CmdOutputObservation(Observation): **kwargs: Any, ) -> None: # Truncate content before passing it to parent - truncated_content = self._maybe_truncate(content) + # Hidden commands don't go through LLM/event stream, so no need to truncate + truncate = not hidden + if truncate: + content = self._maybe_truncate(content) - super().__init__(truncated_content) + super().__init__(content) self.command = command self.observation = observation diff --git a/openhands/runtime/base.py b/openhands/runtime/base.py index 3094f030d0..19038fbf15 100644 --- a/openhands/runtime/base.py +++ b/openhands/runtime/base.py @@ -1038,7 +1038,9 @@ fi self, command: str, cwd: str | None ) -> CommandResult: """This function is used by the GitHandler to execute shell commands.""" - obs = self.run(CmdRunAction(command=command, is_static=True, cwd=cwd)) + obs = self.run( + CmdRunAction(command=command, is_static=True, hidden=True, cwd=cwd) + ) exit_code = 0 content = '' diff --git a/openhands/runtime/impl/action_execution/action_execution_client.py b/openhands/runtime/impl/action_execution/action_execution_client.py index b7beec5700..bf3515b355 100644 --- a/openhands/runtime/impl/action_execution/action_execution_client.py +++ b/openhands/runtime/impl/action_execution/action_execution_client.py @@ -322,6 +322,8 @@ class ActionExecutionClient(Runtime): ) assert response.is_closed output = response.json() + if getattr(action, 'hidden', False): + output.get('extras')['hidden'] = True obs = observation_from_dict(output) obs._cause = action.id # type: ignore[attr-defined] except httpx.TimeoutException: diff --git a/openhands/runtime/utils/bash.py b/openhands/runtime/utils/bash.py index 68064f842d..c5eea320d7 100644 --- a/openhands/runtime/utils/bash.py +++ b/openhands/runtime/utils/bash.py @@ -312,7 +312,11 @@ class BashSession: return command_output.rstrip() def _handle_completed_command( - self, command: str, pane_content: str, ps1_matches: list[re.Match] + self, + command: str, + pane_content: str, + ps1_matches: list[re.Match], + hidden: bool, ) -> CmdOutputObservation: is_special_key = self._is_special_key(command) assert len(ps1_matches) >= 1, ( @@ -359,6 +363,7 @@ class BashSession: content=command_output, command=command, metadata=metadata, + hidden=hidden, ) def _handle_nochange_timeout_command( @@ -566,6 +571,7 @@ class BashSession: command=command, content=command_output, metadata=metadata, + hidden=getattr(action, 'hidden', False), ) # Send actual command/inputs to the pane @@ -616,6 +622,7 @@ class BashSession: command, pane_content=cur_pane_output, ps1_matches=ps1_matches, + hidden=getattr(action, 'hidden', False), ) # Timeout checks should only trigger if a new prompt hasn't appeared yet.