Skip to content

Commit

Permalink
fix: properly handle PS1 metadata JSON escaping
Browse files Browse the repository at this point in the history
- Use raw strings for backslash sequences in PS1 metadata
- Remove unnecessary quote escaping since json.dumps already handles it
- Add tests to verify PS1 metadata parsing works correctly
- Fixes #6826
  • Loading branch information
openhands-agent committed Feb 19, 2025
1 parent 49de927 commit 8c60e51
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 349 deletions.
19 changes: 8 additions & 11 deletions openhands/events/observation/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,19 @@ class CmdOutputMetadata(BaseModel):
def to_ps1_prompt(cls) -> str:
"""Convert the required metadata into a PS1 prompt."""
prompt = CMD_OUTPUT_PS1_BEGIN
# First, escape backslashes in the values
# Create the JSON string with proper escaping
json_str = json.dumps(
{
'pid': '$!',
'exit_code': '$?',
'username': '\\u', # Double backslash to escape it in JSON
'hostname': '\\h', # Double backslash to escape it in JSON
'username': r'\u', # Raw string to avoid double escaping
'hostname': r'\h', # Raw string to avoid double escaping
'working_dir': '$(pwd)',
'py_interpreter_path': '$(which python 2>/dev/null || echo "")',
},
indent=2,
)
# Then escape double quotes for PS1
prompt += json_str.replace('"', r'\"')
prompt += json_str
prompt += CMD_OUTPUT_PS1_END + '\n' # Ensure there's a newline at the end
return prompt

Expand All @@ -56,9 +55,8 @@ def matches_ps1_metadata(cls, string: str) -> list[re.Match[str]]:
matches = []
for match in CMD_OUTPUT_METADATA_PS1_REGEX.finditer(string):
try:
# Unescape the quotes before parsing
json_str = match.group(1).strip().replace(r'\"', '"')
json.loads(json_str) # Try to parse as JSON
# Parse the JSON as is
json.loads(match.group(1).strip())
matches.append(match)
except json.JSONDecodeError:
logger.warning(
Expand All @@ -71,9 +69,8 @@ def matches_ps1_metadata(cls, string: str) -> list[re.Match[str]]:
@classmethod
def from_ps1_match(cls, match: re.Match[str]) -> Self:
"""Extract the required metadata from a PS1 prompt."""
# Unescape the quotes before parsing
json_str = match.group(1).strip().replace(r'\"', '"')
metadata = json.loads(json_str)
# Parse the JSON as is
metadata = json.loads(match.group(1).strip())
# Create a copy of metadata to avoid modifying the original
processed = metadata.copy()
# Convert numeric fields
Expand Down
Loading

0 comments on commit 8c60e51

Please sign in to comment.