diff --git a/autogpt_platform/backend/backend/api/features/chat/sdk/security_hooks.py b/autogpt_platform/backend/backend/api/features/chat/sdk/security_hooks.py index 82e76924a0..22c6cd5670 100644 --- a/autogpt_platform/backend/backend/api/features/chat/sdk/security_hooks.py +++ b/autogpt_platform/backend/backend/api/features/chat/sdk/security_hooks.py @@ -75,7 +75,15 @@ def _validate_workspace_path( # Glob/Grep without a path default to cwd which is already sandboxed return {} - resolved = os.path.normpath(os.path.expanduser(path)) + # Resolve relative paths against sdk_cwd (the SDK sets cwd so the LLM + # naturally uses relative paths like "test.txt" instead of absolute ones). + # Tilde paths (~/) are home-dir references, not relative — expand first. + if path.startswith("~"): + resolved = os.path.normpath(os.path.expanduser(path)) + elif not os.path.isabs(path) and sdk_cwd: + resolved = os.path.normpath(os.path.join(sdk_cwd, path)) + else: + resolved = os.path.normpath(path) # Allow access within the SDK working directory if sdk_cwd: @@ -91,9 +99,11 @@ def _validate_workspace_path( logger.warning( f"Blocked {tool_name} outside workspace: {path} (resolved={resolved})" ) + workspace_hint = f" Allowed workspace: {sdk_cwd}" if sdk_cwd else "" return _deny( f"[SECURITY] Tool '{tool_name}' can only access files within the workspace " - "directory. This is enforced by the platform and cannot be bypassed." + f"directory.{workspace_hint} " + "This is enforced by the platform and cannot be bypassed." ) diff --git a/autogpt_platform/backend/backend/api/features/chat/sdk/service.py b/autogpt_platform/backend/backend/api/features/chat/sdk/service.py index 9eb17e4c3b..d760d8562a 100644 --- a/autogpt_platform/backend/backend/api/features/chat/sdk/service.py +++ b/autogpt_platform/backend/backend/api/features/chat/sdk/service.py @@ -63,6 +63,11 @@ _SDK_TOOL_SUPPLEMENT = """ - The SDK built-in Bash tool is NOT available. Use the `bash_exec` MCP tool for shell commands — it runs in a network-isolated sandbox. +- **Shared workspace**: The SDK Read/Write tools and `bash_exec` share the + same working directory. Files created by one are readable by the other. + These files are **ephemeral** — they exist only for the current session. +- **Persistent storage**: Use `write_workspace_file` / `read_workspace_file` + for files that should persist across sessions (stored in cloud storage). - Long-running tools (create_agent, edit_agent, etc.) are handled asynchronously. You will receive an immediate response; the actual result is delivered to the user via a background stream. diff --git a/autogpt_platform/backend/backend/api/features/chat/tools/__init__.py b/autogpt_platform/backend/backend/api/features/chat/tools/__init__.py index 9296dab6af..250f2aa117 100644 --- a/autogpt_platform/backend/backend/api/features/chat/tools/__init__.py +++ b/autogpt_platform/backend/backend/api/features/chat/tools/__init__.py @@ -53,7 +53,7 @@ TOOL_REGISTRY: dict[str, BaseTool] = { "web_fetch": WebFetchTool(), # Sandboxed code execution (bubblewrap) "bash_exec": BashExecTool(), - # Workspace tools for CoPilot file operations + # Persistent workspace tools (cloud storage, survives across sessions) "list_workspace_files": ListWorkspaceFilesTool(), "read_workspace_file": ReadWorkspaceFileTool(), "write_workspace_file": WriteWorkspaceFileTool(), diff --git a/autogpt_platform/backend/backend/api/features/chat/tools/bash_exec.py b/autogpt_platform/backend/backend/api/features/chat/tools/bash_exec.py index ae3a81b7e1..da9d8bf3fa 100644 --- a/autogpt_platform/backend/backend/api/features/chat/tools/bash_exec.py +++ b/autogpt_platform/backend/backend/api/features/chat/tools/bash_exec.py @@ -45,6 +45,8 @@ class BashExecTool(BaseTool): "Execute a Bash command or script in a bubblewrap sandbox. " "Full Bash scripting is supported (loops, conditionals, pipes, " "functions, etc.). " + "The sandbox shares the same working directory as the SDK Read/Write " + "tools — files created by either are accessible to both. " "SECURITY: Only system directories (/usr, /bin, /lib, /etc) are " "visible read-only, the per-session workspace is the only writable " "path, environment variables are wiped (no secrets), all network " diff --git a/autogpt_platform/backend/backend/api/features/chat/tools/workspace_files.py b/autogpt_platform/backend/backend/api/features/chat/tools/workspace_files.py index 03532c8fee..f37d2c80e0 100644 --- a/autogpt_platform/backend/backend/api/features/chat/tools/workspace_files.py +++ b/autogpt_platform/backend/backend/api/features/chat/tools/workspace_files.py @@ -88,7 +88,9 @@ class ListWorkspaceFilesTool(BaseTool): @property def description(self) -> str: return ( - "List files in the user's workspace. " + "List files in the user's persistent workspace (cloud storage). " + "These files survive across sessions. " + "For ephemeral session files, use the SDK Read/Glob tools instead. " "Returns file names, paths, sizes, and metadata. " "Optionally filter by path prefix." ) @@ -204,7 +206,9 @@ class ReadWorkspaceFileTool(BaseTool): @property def description(self) -> str: return ( - "Read a file from the user's workspace. " + "Read a file from the user's persistent workspace (cloud storage). " + "These files survive across sessions. " + "For ephemeral session files, use the SDK Read tool instead. " "Specify either file_id or path to identify the file. " "For small text files, returns content directly. " "For large or binary files, returns metadata and a download URL. " @@ -378,7 +382,9 @@ class WriteWorkspaceFileTool(BaseTool): @property def description(self) -> str: return ( - "Write or create a file in the user's workspace. " + "Write or create a file in the user's persistent workspace (cloud storage). " + "These files survive across sessions. " + "For ephemeral session files, use the SDK Write tool instead. " "Provide the content as a base64-encoded string. " f"Maximum file size is {Config().max_file_size_mb}MB. " "Files are saved to the current session's folder by default. " @@ -523,7 +529,7 @@ class DeleteWorkspaceFileTool(BaseTool): @property def description(self) -> str: return ( - "Delete a file from the user's workspace. " + "Delete a file from the user's persistent workspace (cloud storage). " "Specify either file_id or path to identify the file. " "Paths are scoped to the current session by default. " "Use /sessions//... for cross-session access."