diff --git a/src/claude_agent_sdk/_internal/transport/subprocess_cli.py b/src/claude_agent_sdk/_internal/transport/subprocess_cli.py index 4b47f115..3d7dd3d1 100644 --- a/src/claude_agent_sdk/_internal/transport/subprocess_cli.py +++ b/src/claude_agent_sdk/_internal/transport/subprocess_cli.py @@ -20,6 +20,7 @@ from ..._errors import CLIConnectionError, CLINotFoundError, ProcessError from ..._errors import CLIJSONDecodeError as SDKJSONDecodeError from ..._version import __version__ +from ..sessions import list_sessions from ...types import ClaudeAgentOptions, SystemPromptFile, SystemPromptPreset from . import Transport @@ -108,6 +109,33 @@ def _find_bundled_cli(self) -> str | None: return None + def _get_resume_session_id(self) -> str | None: + """Resolve the session to resume when continuation is requested. + + Explicit ``resume`` and ``session_id`` options take precedence. When + ``continue_conversation`` is enabled on a fresh process, fall back to + the most recent session for the current cwd so the CLI can resume the + prior conversation instead of starting a new one. + """ + if self._options.resume: + return self._options.resume + + if self._options.session_id or not self._options.continue_conversation: + return None + + if not self._cwd: + return None + + try: + sessions = list_sessions(directory=self._cwd, limit=1) + except OSError: + return None + + if not sessions: + return None + + return sessions[0].session_id + def _build_settings_value(self) -> str | None: """Build settings value, merging sandbox settings if provided. @@ -225,11 +253,13 @@ def _build_command(self) -> list[str]: if self._options.permission_mode: cmd.extend(["--permission-mode", self._options.permission_mode]) + resume_session_id = self._get_resume_session_id() + if self._options.continue_conversation: cmd.append("--continue") - if self._options.resume: - cmd.extend(["--resume", self._options.resume]) + if resume_session_id: + cmd.extend(["--resume", resume_session_id]) if self._options.session_id: cmd.extend(["--session-id", self._options.session_id]) diff --git a/tests/test_transport.py b/tests/test_transport.py index b2c40923..5fe50f51 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -306,6 +306,50 @@ def test_session_continuation(self): assert "--resume" in cmd assert "session-123" in cmd + def test_session_continuation_uses_latest_project_session(self): + """Test continuation resolves the latest session for the current cwd.""" + transport = SubprocessCLITransport( + prompt="Continue from before", + options=make_options( + continue_conversation=True, + cwd="/tmp/project", + ), + ) + + with patch( + "claude_agent_sdk._internal.transport.subprocess_cli.list_sessions" + ) as mock_list_sessions: + mock_session = Mock(session_id="550e8400-e29b-41d4-a716-446655440000") + mock_list_sessions.return_value = [mock_session] + + cmd = transport._build_command() + + mock_list_sessions.assert_called_once_with(directory="/tmp/project", limit=1) + assert "--continue" in cmd + assert "--resume" in cmd + idx = cmd.index("--resume") + assert cmd[idx + 1] == "550e8400-e29b-41d4-a716-446655440000" + + def test_session_continuation_skips_resume_when_no_prior_session(self): + """Test continuation without prior sessions does not add --resume.""" + transport = SubprocessCLITransport( + prompt="Continue from before", + options=make_options( + continue_conversation=True, + cwd="/tmp/project", + ), + ) + + with patch( + "claude_agent_sdk._internal.transport.subprocess_cli.list_sessions", + return_value=[], + ) as mock_list_sessions: + cmd = transport._build_command() + + mock_list_sessions.assert_called_once_with(directory="/tmp/project", limit=1) + assert "--continue" in cmd + assert "--resume" not in cmd + def test_session_id(self): """Test custom session ID option.""" transport = SubprocessCLITransport(