Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions src/claude_agent_sdk/_internal/transport/subprocess_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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])
Expand Down
44 changes: 44 additions & 0 deletions tests/test_transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down