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 940172a0d6..b32bd0992f 100644 --- a/autogpt_platform/backend/backend/api/features/chat/sdk/service.py +++ b/autogpt_platform/backend/backend/api/features/chat/sdk/service.py @@ -287,6 +287,7 @@ async def stream_chat_completion_sdk( mcp_servers={"copilot": mcp_server}, # type: ignore[arg-type] allowed_tools=COPILOT_TOOL_NAMES, hooks=create_security_hooks(user_id), # type: ignore[arg-type] + cwd="/tmp", resume=resume_id, ) @@ -326,6 +327,9 @@ async def stream_chat_completion_sdk( # Receive messages from the SDK async for sdk_msg in client.receive_messages(): + logger.debug( + f"[SDK] Received: {type(sdk_msg).__name__} {getattr(sdk_msg, 'subtype', '')}" + ) for response in adapter.convert_message(sdk_msg): if isinstance(response, StreamStart): continue diff --git a/autogpt_platform/backend/backend/api/features/chat/sdk/session_file.py b/autogpt_platform/backend/backend/api/features/chat/sdk/session_file.py index 77a4cf874f..ad88e35358 100644 --- a/autogpt_platform/backend/backend/api/features/chat/sdk/session_file.py +++ b/autogpt_platform/backend/backend/api/features/chat/sdk/session_file.py @@ -26,8 +26,13 @@ def _encode_cwd(cwd: str) -> str: def _get_project_dir(cwd: str) -> Path: - """Get the CLI project directory for a given working directory.""" - return _CLAUDE_PROJECTS_DIR / _encode_cwd(cwd) + """Get the CLI project directory for a given working directory. + + Resolves symlinks to match the CLI's behavior (e.g. /tmp -> /private/tmp + on macOS). + """ + resolved = str(Path(cwd).resolve()) + return _CLAUDE_PROJECTS_DIR / _encode_cwd(resolved) def write_session_file( @@ -45,6 +50,7 @@ def write_session_file( return None session_id = session.session_id + resolved_cwd = str(Path(cwd).resolve()) project_dir = _get_project_dir(cwd) project_dir.mkdir(parents=True, exist_ok=True) @@ -62,7 +68,7 @@ def write_session_file( "parentUuid": prev_uuid, "isSidechain": False, "userType": "external", - "cwd": cwd, + "cwd": resolved_cwd, "sessionId": session_id, "type": "user", "message": {"role": "user", "content": msg.content}, @@ -77,7 +83,7 @@ def write_session_file( "parentUuid": prev_uuid, "isSidechain": False, "userType": "external", - "cwd": cwd, + "cwd": resolved_cwd, "sessionId": session_id, "type": "assistant", "message": { diff --git a/autogpt_platform/backend/backend/api/features/chat/sdk/session_file_test.py b/autogpt_platform/backend/backend/api/features/chat/sdk/session_file_test.py index b62f4e1df7..5c0eef1fb2 100644 --- a/autogpt_platform/backend/backend/api/features/chat/sdk/session_file_test.py +++ b/autogpt_platform/backend/backend/api/features/chat/sdk/session_file_test.py @@ -6,7 +6,7 @@ from pathlib import Path from unittest.mock import patch from ..model import ChatMessage, ChatSession -from .session_file import cleanup_session_file, write_session_file +from .session_file import _get_project_dir, cleanup_session_file, write_session_file _NOW = datetime.now(UTC) @@ -172,3 +172,51 @@ def test_cleanup_no_error_if_missing(tmp_path: Path): return_value=tmp_path, ): cleanup_session_file("nonexistent") # Should not raise + + +# -- _get_project_dir -------------------------------------------------------- + + +def test_get_project_dir_resolves_symlinks(tmp_path: Path): + """_get_project_dir should resolve symlinks so the path matches the CLI.""" + # Create a symlink: tmp_path/link -> tmp_path/real + real_dir = tmp_path / "real" + real_dir.mkdir() + link = tmp_path / "link" + link.symlink_to(real_dir) + + with patch( + "backend.api.features.chat.sdk.session_file._CLAUDE_PROJECTS_DIR", + tmp_path / "projects", + ): + result = _get_project_dir(str(link)) + + # Should resolve the symlink and encode the real path + expected_encoded = "-" + str(real_dir).lstrip("/").replace("/", "-") + assert result.name == expected_encoded + + +def test_write_uses_resolved_cwd_in_messages(tmp_path: Path): + """The cwd field in JSONL messages should use the resolved path.""" + session = _make_session( + [ + ChatMessage(role="user", content="hello"), + ChatMessage(role="assistant", content="Hi!"), + ChatMessage(role="user", content="current"), + ], + session_id="sess-cwd", + ) + + with patch( + "backend.api.features.chat.sdk.session_file._get_project_dir", + return_value=tmp_path, + ): + write_session_file(session, cwd="/tmp") + + file_path = tmp_path / "sess-cwd.jsonl" + lines = file_path.read_text().strip().split("\n") + for line in lines: + obj = json.loads(line) + # On macOS /tmp resolves to /private/tmp; on Linux stays /tmp + resolved = str(Path("/tmp").resolve()) + assert obj["cwd"] == resolved