fix: clear chat sessions on cwd change (#172)
This commit is contained in:
@@ -57,6 +57,10 @@ If you prefer a cleaner chat, hide resume lines:
|
||||
|
||||
In group chats, Takopi stores a session per sender, so different people can work independently in the same chat.
|
||||
|
||||
## Working directory changes
|
||||
|
||||
When `session_mode = "chat"` is enabled, Takopi clears stored chat sessions on startup if the current working directory differs from the one recorded in `telegram_chat_sessions_state.json`. This avoids resuming directory-bound sessions from a different project.
|
||||
|
||||
## Related
|
||||
|
||||
- [Conversation modes](../tutorials/conversation-modes.md)
|
||||
|
||||
@@ -24,6 +24,7 @@ class _ChatState(msgspec.Struct, forbid_unknown_fields=False):
|
||||
|
||||
class _ChatSessionsState(msgspec.Struct, forbid_unknown_fields=False):
|
||||
version: int
|
||||
cwd: str | None = None
|
||||
chats: dict[str, _ChatState] = msgspec.field(default_factory=dict)
|
||||
|
||||
|
||||
@@ -64,11 +65,27 @@ class ChatSessionStore(JsonStateStore[_ChatSessionsState]):
|
||||
return None
|
||||
return ResumeToken(engine=engine, value=entry.resume)
|
||||
|
||||
async def sync_startup_cwd(self, cwd: Path) -> bool:
|
||||
normalized = str(cwd.expanduser().resolve())
|
||||
async with self._lock:
|
||||
self._reload_locked_if_needed()
|
||||
previous = self._state.cwd
|
||||
cleared = False
|
||||
if previous is not None and previous != normalized:
|
||||
self._state.chats = {}
|
||||
cleared = True
|
||||
if previous != normalized:
|
||||
self._state.cwd = normalized
|
||||
self._save_locked()
|
||||
return cleared
|
||||
|
||||
async def set_session_resume(
|
||||
self, chat_id: int, owner_id: int | None, token: ResumeToken
|
||||
) -> None:
|
||||
async with self._lock:
|
||||
self._reload_locked_if_needed()
|
||||
if self._state.cwd is None:
|
||||
self._state.cwd = str(Path.cwd().expanduser().resolve())
|
||||
chat = self._ensure_chat_locked(chat_id, owner_id)
|
||||
chat.sessions[token.engine] = _SessionState(resume=token.value)
|
||||
self._save_locked()
|
||||
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
from collections.abc import AsyncIterator, Awaitable, Callable, Mapping
|
||||
from dataclasses import dataclass
|
||||
from functools import partial
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
import anyio
|
||||
@@ -964,6 +965,14 @@ async def run_main_loop(
|
||||
state.chat_session_store = ChatSessionStore(
|
||||
resolve_sessions_path(config_path)
|
||||
)
|
||||
cleared = await state.chat_session_store.sync_startup_cwd(Path.cwd())
|
||||
if cleared:
|
||||
logger.info(
|
||||
"chat_sessions.cleared",
|
||||
reason="startup_cwd_changed",
|
||||
cwd=str(Path.cwd()),
|
||||
state_path=str(resolve_sessions_path(config_path)),
|
||||
)
|
||||
logger.info(
|
||||
"chat_sessions.enabled",
|
||||
state_path=str(resolve_sessions_path(config_path)),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from takopi.model import ResumeToken
|
||||
@@ -36,3 +38,32 @@ async def test_chat_sessions_store_clear(tmp_path) -> None:
|
||||
engine="codex",
|
||||
value="two",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_chat_sessions_store_drops_sessions_on_cwd_change(
|
||||
tmp_path, monkeypatch
|
||||
) -> None:
|
||||
path = tmp_path / "telegram_chat_sessions_state.json"
|
||||
dir1 = tmp_path / "dir1"
|
||||
dir2 = tmp_path / "dir2"
|
||||
dir1.mkdir()
|
||||
dir2.mkdir()
|
||||
|
||||
monkeypatch.chdir(dir1)
|
||||
store = ChatSessionStore(path)
|
||||
await store.set_session_resume(1, None, ResumeToken(engine="codex", value="abc123"))
|
||||
assert await store.get_session_resume(1, None, "codex") == ResumeToken(
|
||||
engine="codex", value="abc123"
|
||||
)
|
||||
|
||||
store2 = ChatSessionStore(path)
|
||||
assert await store2.sync_startup_cwd(Path.cwd()) is False
|
||||
assert await store2.get_session_resume(1, None, "codex") == ResumeToken(
|
||||
engine="codex", value="abc123"
|
||||
)
|
||||
|
||||
monkeypatch.chdir(dir2)
|
||||
store3 = ChatSessionStore(path)
|
||||
assert await store3.sync_startup_cwd(Path.cwd()) is True
|
||||
assert await store3.get_session_resume(1, None, "codex") is None
|
||||
|
||||
Reference in New Issue
Block a user