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: 2 additions & 2 deletions python/copilot/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1252,7 +1252,7 @@ async def create_session(

# Create and register the session before issuing the RPC so that
# events emitted by the CLI (e.g. session.start) are not dropped.
session = CopilotSession(actual_session_id, self._client, None)
session = CopilotSession(actual_session_id, self._client, workspace_path=None)
session._register_tools(tools)
Comment on lines 1253 to 1256
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CopilotSession.workspace_path is now a cached_property, but this method sets session._workspace_path only after the session.create RPC returns. If any early event handler accesses session.workspace_path before _workspace_path is populated, it will cache None permanently unless the cache is invalidated after assigning _workspace_path. Consider explicitly clearing the cached attribute after setting _workspace_path (or switching workspace_path back to a non-cached @property).

Copilot uses AI. Check for mistakes.
session._register_permission_handler(on_permission_request)
if on_user_input_request:
Expand Down Expand Up @@ -1456,7 +1456,7 @@ async def resume_session(

# Create and register the session before issuing the RPC so that
# events emitted by the CLI (e.g. session.start) are not dropped.
session = CopilotSession(session_id, self._client, None)
session = CopilotSession(session_id, self._client, workspace_path=None)
session._register_tools(tools)
Comment on lines 1457 to 1460
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as create_session: CopilotSession.workspace_path is a cached_property while _workspace_path is populated only after the session.resume RPC completes. If any early event handler reads session.workspace_path before _workspace_path is set, the cached value may remain None unless the cache is invalidated after assigning _workspace_path.

Copilot uses AI. Check for mistakes.
session._register_permission_handler(on_permission_request)
if on_user_input_request:
Expand Down
19 changes: 14 additions & 5 deletions python/copilot/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
from __future__ import annotations

import asyncio
import functools
import inspect
import os
import pathlib
import threading
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
Expand Down Expand Up @@ -639,7 +642,9 @@ class CopilotSession:
... unsubscribe()
"""

def __init__(self, session_id: str, client: Any, workspace_path: str | None = None):
def __init__(
self, session_id: str, client: Any, workspace_path: os.PathLike[str] | str | None = None
):
"""
Initialize a new CopilotSession.

Expand All @@ -655,7 +660,7 @@ def __init__(self, session_id: str, client: Any, workspace_path: str | None = No
"""
self.session_id = session_id
self._client = client
self._workspace_path = workspace_path
self._workspace_path = os.fsdecode(workspace_path) if workspace_path is not None else None
self._event_handlers: set[Callable[[SessionEvent], None]] = set()
self._event_handlers_lock = threading.Lock()
self._tool_handlers: dict[str, ToolHandler] = {}
Expand All @@ -677,15 +682,19 @@ def rpc(self) -> SessionRpc:
self._rpc = SessionRpc(self._client, self.session_id)
return self._rpc

@property
def workspace_path(self) -> str | None:
@functools.cached_property
def workspace_path(self) -> pathlib.Path | None:
"""
Path to the session workspace directory when infinite sessions are enabled.

Contains checkpoints/, plan.md, and files/ subdirectories.
None if infinite sessions are disabled.
"""
return self._workspace_path
# Done as a property as self._workspace_path is directly set from a server
# response post-init. So it was either make sure all places directly setting
# the attribute handle the None case appropriately, use a setter for the
# attribute to do the conversion, or just do the conversion lazily via a getter.
return pathlib.Path(self._workspace_path) if self._workspace_path else None
Comment on lines +685 to +697
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

workspace_path is a cached_property, but _workspace_path is set after session construction (e.g. in CopilotClient.create_session/resume_session). If workspace_path is accessed before the RPC response populates _workspace_path (for example inside an early on_event callback), the cached value will be None and will never update even after _workspace_path is later set. Consider using a regular @property (compute Path on each access) or implementing explicit cache invalidation whenever _workspace_path changes (e.g., a setter/helper that deletes the cached attribute from __dict__).

Copilot uses AI. Check for mistakes.

async def send(
self,
Expand Down
Loading