This commit is contained in:
@@ -31,7 +31,9 @@ services:
|
||||
environment:
|
||||
- GEMINI_API_KEY=${GEMINI_API_KEY}
|
||||
- REDIS_URL=redis://redis:6379/0
|
||||
- WORKSPACE_ROOT=${WORKSPACE_ROOT:-/workspace}
|
||||
# Use HOME not PWD: Claude needs access to any absolute file path, not just current project,
|
||||
# and Claude Code could be running from multiple locations at the same time
|
||||
- WORKSPACE_ROOT=${WORKSPACE_ROOT:-${HOME}}
|
||||
volumes:
|
||||
- ${HOME:-/tmp}:/workspace:ro
|
||||
stdin_open: true
|
||||
|
||||
@@ -37,8 +37,12 @@ GEMINI_API_KEY=$API_KEY_VALUE
|
||||
# Redis configuration (automatically set for Docker Compose)
|
||||
REDIS_URL=redis://redis:6379/0
|
||||
|
||||
# Workspace root (automatically set for Docker Compose)
|
||||
WORKSPACE_ROOT=/workspace
|
||||
# Workspace root - host path that maps to /workspace in container
|
||||
# This should be the host directory path that contains all files Claude might reference
|
||||
# We use $HOME (not $PWD) because Claude needs access to ANY absolute file path,
|
||||
# not just files within the current project directory. Additionally, Claude Code
|
||||
# could be running from multiple locations at the same time.
|
||||
WORKSPACE_ROOT=$HOME
|
||||
EOF
|
||||
echo "✅ Created .env file with Redis configuration"
|
||||
echo ""
|
||||
|
||||
@@ -235,9 +235,9 @@ class TestCollaborationWorkflow:
|
||||
)
|
||||
|
||||
response = json.loads(result[0].text)
|
||||
assert (
|
||||
response["status"] == "requires_clarification"
|
||||
), "Should request clarification when asked about dependencies without package files"
|
||||
assert response["status"] == "requires_clarification", (
|
||||
"Should request clarification when asked about dependencies without package files"
|
||||
)
|
||||
|
||||
clarification = json.loads(response["content"])
|
||||
assert "package.json" in str(clarification["files_needed"]), "Should specifically request package.json"
|
||||
|
||||
@@ -300,7 +300,7 @@ class TestConversationFlow:
|
||||
# REQUEST 6: Try to exceed MAX_CONVERSATION_TURNS limit - should fail
|
||||
turns_at_limit = [
|
||||
ConversationTurn(
|
||||
role="assistant" if i % 2 == 0 else "user", content=f"Turn {i+1}", timestamp="2023-01-01T00:00:30Z"
|
||||
role="assistant" if i % 2 == 0 else "user", content=f"Turn {i + 1}", timestamp="2023-01-01T00:00:30Z"
|
||||
)
|
||||
for i in range(MAX_CONVERSATION_TURNS)
|
||||
]
|
||||
@@ -423,7 +423,9 @@ class TestConversationFlow:
|
||||
# Mock context with current turns
|
||||
turns = [
|
||||
ConversationTurn(
|
||||
role="user" if i % 2 == 0 else "assistant", content=f"Turn {i+1}", timestamp="2023-01-01T00:00:00Z"
|
||||
role="user" if i % 2 == 0 else "assistant",
|
||||
content=f"Turn {i + 1}",
|
||||
timestamp="2023-01-01T00:00:00Z",
|
||||
)
|
||||
for i in range(turn_num)
|
||||
]
|
||||
@@ -445,7 +447,7 @@ class TestConversationFlow:
|
||||
# Now we should be at the limit - create final context
|
||||
final_turns = [
|
||||
ConversationTurn(
|
||||
role="user" if i % 2 == 0 else "assistant", content=f"Turn {i+1}", timestamp="2023-01-01T00:00:00Z"
|
||||
role="user" if i % 2 == 0 else "assistant", content=f"Turn {i + 1}", timestamp="2023-01-01T00:00:00Z"
|
||||
)
|
||||
for i in range(MAX_CONVERSATION_TURNS)
|
||||
]
|
||||
|
||||
@@ -32,9 +32,9 @@ class TestThinkingModes:
|
||||
]
|
||||
|
||||
for tool, expected_default in tools:
|
||||
assert (
|
||||
tool.get_default_thinking_mode() == expected_default
|
||||
), f"{tool.__class__.__name__} should default to {expected_default}"
|
||||
assert tool.get_default_thinking_mode() == expected_default, (
|
||||
f"{tool.__class__.__name__} should default to {expected_default}"
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("tools.base.BaseTool.create_model")
|
||||
|
||||
@@ -28,8 +28,9 @@ from .token_utils import MAX_CONTEXT_TOKENS, estimate_tokens
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Get workspace root for Docker path translation
|
||||
# When running in Docker with a mounted workspace, WORKSPACE_ROOT contains
|
||||
# the host path that corresponds to /workspace in the container
|
||||
# IMPORTANT: WORKSPACE_ROOT should contain the HOST path (e.g., /Users/john/project)
|
||||
# that gets mounted to /workspace in the Docker container. This enables proper
|
||||
# path translation between host absolute paths and container workspace paths.
|
||||
WORKSPACE_ROOT = os.environ.get("WORKSPACE_ROOT")
|
||||
CONTAINER_WORKSPACE = Path("/workspace")
|
||||
|
||||
@@ -43,6 +44,7 @@ DANGEROUS_WORKSPACE_PATHS = {
|
||||
"/var",
|
||||
"/root",
|
||||
"/home",
|
||||
"/workspace", # Container path - WORKSPACE_ROOT should be host path
|
||||
"C:\\",
|
||||
"C:\\Windows",
|
||||
"C:\\Program Files",
|
||||
@@ -54,7 +56,17 @@ if WORKSPACE_ROOT:
|
||||
# Resolve to canonical path for comparison
|
||||
resolved_workspace = Path(WORKSPACE_ROOT).resolve()
|
||||
|
||||
# Check against dangerous paths
|
||||
# Special check for /workspace - common configuration mistake
|
||||
if str(resolved_workspace) == "/workspace":
|
||||
raise RuntimeError(
|
||||
f"Configuration Error: WORKSPACE_ROOT should be set to the HOST path, not the container path. "
|
||||
f"Found: WORKSPACE_ROOT={WORKSPACE_ROOT} "
|
||||
f"Expected: WORKSPACE_ROOT should be set to your host directory path (e.g., $HOME) "
|
||||
f"that contains all files Claude might reference. "
|
||||
f"This path gets mounted to /workspace inside the Docker container."
|
||||
)
|
||||
|
||||
# Check against other dangerous paths
|
||||
if str(resolved_workspace) in DANGEROUS_WORKSPACE_PATHS:
|
||||
raise RuntimeError(
|
||||
f"Security Error: WORKSPACE_ROOT '{WORKSPACE_ROOT}' is set to a dangerous system directory. "
|
||||
@@ -181,12 +193,17 @@ def translate_path_for_environment(path_str: str) -> str:
|
||||
|
||||
This is the unified path translation function that should be used by all
|
||||
tools and utilities throughout the codebase. It handles:
|
||||
1. Docker host-to-container path translation
|
||||
1. Docker host-to-container path translation (host paths -> /workspace/...)
|
||||
2. Direct mode (no translation needed)
|
||||
3. Security validation and error handling
|
||||
|
||||
Docker Path Translation Logic:
|
||||
- Input: /Users/john/project/src/file.py (host path from Claude)
|
||||
- WORKSPACE_ROOT: /Users/john/project (host path in env var)
|
||||
- Output: /workspace/src/file.py (container path for file operations)
|
||||
|
||||
Args:
|
||||
path_str: Original path string from the client
|
||||
path_str: Original path string from the client (absolute host path)
|
||||
|
||||
Returns:
|
||||
Translated path appropriate for the current environment
|
||||
|
||||
Reference in New Issue
Block a user