Compare commits

...

1 Commits

Author SHA1 Message Date
openhands
74e0c77b4c Fix working_dir in workspace context recall for local runtime
- Fix CLI main.py to use runtime.workspace_root instead of os.getcwd()
- Fix agent_session.py to use runtime.workspace_root when available
- Add comprehensive unit tests to reproduce bug and verify fix
- Ensure working_dir reflects actual workspace path when using SANDBOX_VOLUMES

Fixes issue where workspace context recall showed /workspace instead of
the actual working directory when using SANDBOX_VOLUMES={other_folder}:/workspace:rw

Co-authored-by: openhands <openhands@all-hands.dev>
2025-09-12 07:37:57 +00:00
3 changed files with 104 additions and 2 deletions

View File

@@ -329,7 +329,7 @@ async def run_session(
selected_repository=config.sandbox.selected_repo,
repo_directory=repo_directory,
conversation_instructions=conversation_instructions,
working_dir=os.getcwd(),
working_dir=str(runtime.workspace_root),
)
# Add MCP tools to the agent

View File

@@ -153,13 +153,18 @@ class AgentSession:
if custom_secrets:
custom_secrets_handler.set_event_stream_secrets(self.event_stream)
# Determine working directory - use runtime's workspace_root if available, otherwise fallback to config
working_dir = config.workspace_mount_path_in_sandbox
if self.runtime is not None:
working_dir = str(self.runtime.workspace_root)
self.memory = await self._create_memory(
selected_repository=selected_repository,
repo_directory=repo_directory,
selected_branch=selected_branch,
conversation_instructions=conversation_instructions,
custom_secrets_descriptions=custom_secrets_handler.get_custom_secrets_descriptions(),
working_dir=config.workspace_mount_path_in_sandbox,
working_dir=working_dir,
)
# NOTE: this needs to happen before controller is created

View File

@@ -0,0 +1,97 @@
"""
Test for working_dir bug fix in workspace context recall.
This test verifies that the working_dir in workspace context recall
correctly reflects the runtime's actual workspace root instead of
the hardcoded config value.
"""
import tempfile
from pathlib import Path
from unittest.mock import MagicMock
import pytest
from openhands.core.config import OpenHandsConfig
from openhands.events.action.agent import RecallAction
from openhands.events.observation.agent import RecallType
from openhands.events.stream import EventStream
from openhands.memory.memory import Memory
from openhands.runtime.base import Runtime
from openhands.storage.memory import InMemoryFileStore
@pytest.fixture
def file_store():
"""Create a temporary file store for testing."""
return InMemoryFileStore({})
@pytest.fixture
def mock_runtime():
"""Create a mock runtime for testing."""
runtime = MagicMock(spec=Runtime)
runtime.web_hosts = {}
runtime.additional_agent_instructions = ''
runtime.config = OpenHandsConfig()
return runtime
def test_working_dir_uses_runtime_workspace_root(file_store, mock_runtime):
"""Test that working_dir correctly uses runtime.workspace_root."""
with tempfile.TemporaryDirectory() as temp_dir:
# Set up mock runtime with actual working directory
actual_working_dir = temp_dir
mock_runtime.workspace_root = Path(actual_working_dir)
# Create event stream and memory
event_stream = EventStream('test_sid', file_store)
memory = Memory(event_stream, 'test_sid')
# Set runtime info using the runtime workspace_root (the fix)
memory.set_runtime_info(mock_runtime, {}, str(mock_runtime.workspace_root))
# Trigger workspace context recall
recall_action = RecallAction(
recall_type=RecallType.WORKSPACE_CONTEXT, query='test query'
)
# Get the workspace context observation
workspace_obs = memory._on_workspace_context_recall(recall_action)
# Verify that working_dir matches the runtime's workspace_root
assert workspace_obs.working_dir == str(actual_working_dir)
assert workspace_obs.working_dir == str(mock_runtime.workspace_root)
def test_working_dir_bug_reproduction(file_store, mock_runtime):
"""Test that demonstrates the original bug with hardcoded config value."""
with tempfile.TemporaryDirectory() as temp_dir:
# Set up mock runtime with actual working directory
actual_working_dir = temp_dir
mock_runtime.workspace_root = Path(actual_working_dir)
# Create event stream and memory
event_stream = EventStream('test_sid', file_store)
memory = Memory(event_stream, 'test_sid')
# Set runtime info using the hardcoded config value (the bug)
config = mock_runtime.config
memory.set_runtime_info(
mock_runtime, {}, config.workspace_mount_path_in_sandbox
)
# Trigger workspace context recall
recall_action = RecallAction(
recall_type=RecallType.WORKSPACE_CONTEXT, query='test query'
)
# Get the workspace context observation
workspace_obs = memory._on_workspace_context_recall(recall_action)
# Verify that working_dir incorrectly uses the config value instead of actual directory
assert workspace_obs.working_dir == config.workspace_mount_path_in_sandbox
assert workspace_obs.working_dir != str(actual_working_dir)
assert workspace_obs.working_dir != str(mock_runtime.workspace_root)