From bef5c8b3bcca0811c6ec1c628986578477c0232e Mon Sep 17 00:00:00 2001 From: Erick Aleman Date: Tue, 21 Apr 2026 21:43:29 -0400 Subject: [PATCH] feat(python): add support for persistent_memory in SessionConfig --- python/copilot/client.py | 4 ++ python/copilot/session.py | 3 + python/test_persistent_memory.py | 95 +++++++++++++++++++++++++++ python/test_persistent_memory_unit.py | 49 ++++++++++++++ 4 files changed, 151 insertions(+) create mode 100644 python/test_persistent_memory.py create mode 100644 python/test_persistent_memory_unit.py diff --git a/python/copilot/client.py b/python/copilot/client.py index a51940a96..5f085886b 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -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. @@ -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 diff --git a/python/copilot/session.py b/python/copilot/session.py index 88f742afb..5fb200cd8 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -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. diff --git a/python/test_persistent_memory.py b/python/test_persistent_memory.py new file mode 100644 index 000000000..8a69ec67a --- /dev/null +++ b/python/test_persistent_memory.py @@ -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) diff --git a/python/test_persistent_memory_unit.py b/python/test_persistent_memory_unit.py new file mode 100644 index 000000000..e2114b24e --- /dev/null +++ b/python/test_persistent_memory_unit.py @@ -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]