|
| 1 | +"""Execute bash tool implementation.""" |
| 2 | + |
| 3 | +from pydantic import Field |
| 4 | + |
| 5 | +from openhands.core.runtime.tool import Tool, ToolAnnotations |
| 6 | +from openhands.core.runtime.schema import ActionBase, ObservationBase |
| 7 | +from openhands.core.runtime.security import SECURITY_RISK_DESC, SECURITY_RISK_LITERAL |
| 8 | + |
| 9 | + |
| 10 | +class ExecuteBashAction(ActionBase): |
| 11 | + """Schema for bash command execution.""" |
| 12 | + |
| 13 | + command: str = Field( |
| 14 | + description="The bash command to execute. Can be empty string to view additional logs when previous exit code is `-1`. Can be `C-c` (Ctrl+C) to interrupt the currently running process. Note: You can only execute one bash command at a time. If you need to run multiple commands sequentially, you can use `&&` or `;` to chain them together." |
| 15 | + ) |
| 16 | + is_input: bool = Field( |
| 17 | + default=False, |
| 18 | + description="If True, the command is an input to the running process. If False, the command is a bash command to be executed in the terminal. Default is False.", |
| 19 | + ) |
| 20 | + timeout: float | None = Field( |
| 21 | + default=None, |
| 22 | + description="Optional. Sets a hard timeout in seconds for the command execution. If not provided, the command will use the default soft timeout behavior.", |
| 23 | + ) |
| 24 | + security_risk: SECURITY_RISK_LITERAL = Field(description=SECURITY_RISK_DESC) |
| 25 | + |
| 26 | + |
| 27 | +class ExecuteBashObservation(ObservationBase): |
| 28 | + """A ToolResult that can be rendered as a CLI output.""" |
| 29 | + |
| 30 | + output: str = Field( |
| 31 | + default="", description="The output from the command execution (stdout)." |
| 32 | + ) |
| 33 | + exit_code: int = Field( |
| 34 | + default=0, |
| 35 | + description="The exit code of the command. -1 indicates the process hit the soft timeout and is not yet finished.", |
| 36 | + ) |
| 37 | + error: str = Field( |
| 38 | + default="", description="Any error output from the command execution (stderr)." |
| 39 | + ) |
| 40 | + timeout: bool = Field( |
| 41 | + default=False, description="Whether the command execution timed out." |
| 42 | + ) |
| 43 | + |
| 44 | + |
| 45 | +TOOL_DESCRIPTION = """Execute a bash command in the terminal within a persistent shell session. |
| 46 | +
|
| 47 | +
|
| 48 | +### Command Execution |
| 49 | +* One command at a time: You can only execute one bash command at a time. If you need to run multiple commands sequentially, use `&&` or `;` to chain them together. |
| 50 | +* Persistent session: Commands execute in a persistent shell session where environment variables, virtual environments, and working directory persist between commands. |
| 51 | +* Soft timeout: Commands have a soft timeout of 10 seconds, once that's reached, you have the option to continue or interrupt the command (see section below for details) |
| 52 | +* Shell options: Do NOT use `set -e`, `set -eu`, or `set -euo pipefail` in shell scripts or commands in this environment. The runtime may not support them and can cause unusable shell sessions. If you want to run multi-line bash commands, write the commands to a file and then run it, instead. |
| 53 | +
|
| 54 | +### Long-running Commands |
| 55 | +* For commands that may run indefinitely, run them in the background and redirect output to a file, e.g. `python3 app.py > server.log 2>&1 &`. |
| 56 | +* For commands that may run for a long time (e.g. installation or testing commands), or commands that run for a fixed amount of time (e.g. sleep), you should set the "timeout" parameter of your function call to an appropriate value. |
| 57 | +* If a bash command returns exit code `-1`, this means the process hit the soft timeout and is not yet finished. By setting `is_input` to `true`, you can: |
| 58 | + - Send empty `command` to retrieve additional logs |
| 59 | + - Send text (set `command` to the text) to STDIN of the running process |
| 60 | + - Send control commands like `C-c` (Ctrl+C), `C-d` (Ctrl+D), or `C-z` (Ctrl+Z) to interrupt the process |
| 61 | + - If you do C-c, you can re-start the process with a longer "timeout" parameter to let it run to completion |
| 62 | +
|
| 63 | +### Best Practices |
| 64 | +* Directory verification: Before creating new directories or files, first verify the parent directory exists and is the correct location. |
| 65 | +* Directory management: Try to maintain working directory by using absolute paths and avoiding excessive use of `cd`. |
| 66 | +
|
| 67 | +### Output Handling |
| 68 | +* Output truncation: If the output exceeds a maximum length, it will be truncated before being returned. |
| 69 | +""" |
| 70 | + |
| 71 | + |
| 72 | +execute_bash_tool = Tool( |
| 73 | + name="execute_bash", |
| 74 | + input_schema=ExecuteBashAction, |
| 75 | + output_schema=ExecuteBashObservation, |
| 76 | + description=TOOL_DESCRIPTION, |
| 77 | + annotations=ToolAnnotations( |
| 78 | + title="execute_bash", |
| 79 | + readOnlyHint=False, |
| 80 | + destructiveHint=True, |
| 81 | + idempotentHint=False, |
| 82 | + openWorldHint=True, |
| 83 | + ), |
| 84 | +) |
0 commit comments