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
4 changes: 4 additions & 0 deletions python/copilot/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,7 @@ async def create_session(
commands: list[CommandDefinition] | None = None,
on_elicitation_request: ElicitationHandler | None = None,
create_session_fs_handler: CreateSessionFsHandler | None = None,
persistent_memory: bool | None = None,
) -> CopilotSession:
"""
Create a new conversation session with the Copilot CLI.
Expand Down Expand Up @@ -1348,6 +1349,9 @@ async def create_session(
if working_directory:
payload["workingDirectory"] = working_directory

if persistent_memory is not None:
payload["persistentMemory"] = persistent_memory

# Add streaming option if provided
if streaming is not None:
payload["streaming"] = streaming
Expand Down
3 changes: 3 additions & 0 deletions python/copilot/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,9 @@ class SessionConfig(TypedDict, total=False):
on_permission_request: _PermissionHandlerFn
# Handler for user input requests from the agent (enables ask_user tool)
on_user_input_request: UserInputHandler
# Whether to enable persistent memory for the session.
# Persistent memory allows the agent to remember state across sessions.
persistent_memory: bool
# Hook handlers for intercepting session lifecycle events
hooks: SessionHooks
# Working directory for the session. Tool operations will be relative to this directory.
Expand Down
95 changes: 95 additions & 0 deletions python/test_persistent_memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import asyncio
import sys
import os
from unittest.mock import MagicMock, AsyncMock, patch

# Add current directory to path
sys.path.insert(0, os.path.abspath("."))

import copilot.client
from copilot.client import CopilotClient
from copilot.session import PermissionHandler

async def test_create_session_passes_persistent_memory():
"""
Verify that create_session correctly passes the persistent_memory flag to the RPC layer.
"""
# Mock the JSON-RPC client
mock_rpc = AsyncMock()
mock_rpc.request.return_value = {"sessionId": "test-session-id"}

# Initialize the client with mock init
with patch("copilot.client.CopilotClient.__init__", return_value=None):
client = CopilotClient()
client._client = mock_rpc
client._auto_start = False
client._session_fs_config = None
client._create_session_fs_handler = None
client._skill_directories = []
client._disabled_skills = []
client._mcp_servers = {}
client._custom_agents = []
client._default_agent = None
client._agent = None
client._config_dir = None
client._enable_config_discovery = False
client._infinite_sessions = None
client._on_event = None
client._on_elicitation_request = None

# Create a session with persistent_memory=True
await client.create_session(
on_permission_request=PermissionHandler.approve_all,
persistent_memory=True
)

# Verify the RPC call
mock_rpc.request.assert_called_once()
args, _ = mock_rpc.request.call_args
assert args[0] == "session.create"
assert args[1]["persistentMemory"] is True

async def test_create_session_defaults_persistent_memory_to_none():
"""
Verify that create_session does not pass persistentMemory if not specified.
"""
mock_rpc = AsyncMock()
mock_rpc.request.return_value = {"sessionId": "test-session-id"}

with patch("copilot.client.CopilotClient.__init__", return_value=None):
client = CopilotClient()
client._client = mock_rpc
client._auto_start = False
client._session_fs_config = None
client._create_session_fs_handler = None
client._skill_directories = []
client._disabled_skills = []
client._mcp_servers = {}
client._custom_agents = []
client._default_agent = None
client._agent = None
client._config_dir = None
client._enable_config_discovery = False
client._infinite_sessions = None
client._on_event = None
client._on_elicitation_request = None

await client.create_session(
on_permission_request=PermissionHandler.approve_all
)

mock_rpc.request.assert_called_once()
args, _ = mock_rpc.request.call_args
assert "persistentMemory" not in args[1]

if __name__ == "__main__":
# Manually run tests
try:
asyncio.run(test_create_session_passes_persistent_memory())
asyncio.run(test_create_session_defaults_persistent_memory_to_none())
print("Copilot SDK persistent_memory tests passed!")
except Exception as e:
print(f"Test failed: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
49 changes: 49 additions & 0 deletions python/test_persistent_memory_unit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import pytest
from unittest.mock import AsyncMock
from copilot import CopilotClient
from copilot.client import SubprocessConfig
from copilot.session import PermissionHandler

@pytest.mark.asyncio
async def test_create_session_passes_persistent_memory():
"""
Verify that create_session correctly passes the persistent_memory flag to the RPC layer.
"""
# Mock the JSON-RPC client
mock_rpc = AsyncMock()
mock_rpc.request.return_value = {"sessionId": "test-session-id"}

# Initialize the client with a dummy CLI path
client = CopilotClient(SubprocessConfig(cli_path="dummy-cli"))
client._client = mock_rpc

# Create a session with persistent_memory=True
await client.create_session(
on_permission_request=PermissionHandler.approve_all,
persistent_memory=True
)

# Verify the RPC call
mock_rpc.request.assert_called_once()
args, _ = mock_rpc.request.call_args
assert args[0] == "session.create"
assert args[1]["persistentMemory"] is True

@pytest.mark.asyncio
async def test_create_session_defaults_persistent_memory_to_none():
"""
Verify that create_session does not pass persistentMemory if not specified.
"""
mock_rpc = AsyncMock()
mock_rpc.request.return_value = {"sessionId": "test-session-id"}

client = CopilotClient(SubprocessConfig(cli_path="dummy-cli"))
client._client = mock_rpc

await client.create_session(
on_permission_request=PermissionHandler.approve_all
)

mock_rpc.request.assert_called_once()
args, _ = mock_rpc.request.call_args
assert "persistentMemory" not in args[1]