diff --git a/EvoScientist/EvoScientist.py b/EvoScientist/EvoScientist.py index f52e8b8..9fbf2c9 100644 --- a/EvoScientist/EvoScientist.py +++ b/EvoScientist/EvoScientist.py @@ -204,12 +204,15 @@ def __getattr__(name: str): def create_cli_agent(workspace_dir: str | None = None, checkpointer=None): """Create agent with checkpointer for CLI multi-turn support. + A fresh backend is constructed on every call using the current + ``paths.WORKSPACE_ROOT`` (or the explicit *workspace_dir*), so + runtime ``set_workspace_root()`` changes are always respected. + Args: - workspace_dir: Optional per-session workspace directory. If provided, - creates a fresh backend rooted at this path. If None, uses the - module-level default backend. - checkpointer: Optional LangGraph checkpointer. If None, falls back - to ``InMemorySaver`` (non-persistent). + workspace_dir: Per-session workspace directory. If ``None``, + defaults to the current ``paths.WORKSPACE_ROOT``. + checkpointer: Optional LangGraph checkpointer. If ``None``, + falls back to ``InMemorySaver`` (non-persistent). """ from . import paths as _paths @@ -221,31 +224,34 @@ def create_cli_agent(workspace_dir: str | None = None, checkpointer=None): _mem_dir = str(_paths.MEMORY_DIR) _usr_skills_dir = str(_paths.USER_SKILLS_DIR) - if workspace_dir: - set_active_workspace(workspace_dir) - ws_backend = CustomSandboxBackend( - root_dir=workspace_dir, - virtual_mode=True, - timeout=300, - ) - sk_backend = MergedReadOnlyBackend( - primary_dir=_usr_skills_dir, - secondary_dir=SKILLS_DIR, - ) - # Memory always uses SHARED directory (not per-session) for cross-session persistence - mem_backend = FilesystemBackend( - root_dir=_mem_dir, - virtual_mode=True, - ) - be = CompositeBackend( - default=ws_backend, - routes={ - "/skills/": sk_backend, - "/memory/": mem_backend, - }, - ) - else: - be = backend + # Default to current WORKSPACE_ROOT when no explicit dir is provided + if workspace_dir is None: + workspace_dir = str(_paths.WORKSPACE_ROOT) + + # Always construct fresh backends from current paths (avoids stale + # module-level backend when workspace root changed at runtime). + set_active_workspace(workspace_dir) + ws_backend = CustomSandboxBackend( + root_dir=workspace_dir, + virtual_mode=True, + timeout=300, + ) + sk_backend = MergedReadOnlyBackend( + primary_dir=_usr_skills_dir, + secondary_dir=SKILLS_DIR, + ) + # Memory always uses SHARED directory (not per-session) for cross-session persistence + mem_backend = FilesystemBackend( + root_dir=_mem_dir, + virtual_mode=True, + ) + be = CompositeBackend( + default=ws_backend, + routes={ + "/skills/": sk_backend, + "/memory/": mem_backend, + }, + ) mw = [ create_memory_middleware(_mem_dir, extraction_model=chat_model), diff --git a/EvoScientist/channels/imessage/serve.py b/EvoScientist/channels/imessage/serve.py index d478b6b..ff7c9b3 100644 --- a/EvoScientist/channels/imessage/serve.py +++ b/EvoScientist/channels/imessage/serve.py @@ -50,10 +50,21 @@ def create_agent_handler( on_todo: Optional async callback for todo list updates. Signature: async def on_todo(sender: str, content: str, metadata: dict) -> None """ + import os from langchain_core.messages import HumanMessage + from ...config import get_effective_config, apply_config_to_env + from ...paths import set_workspace_root, ensure_dirs from ...EvoScientist import create_cli_agent from ...stream.events import stream_agent_events + # Apply config so default_workdir is respected in non-CLI entry points + config = get_effective_config() + apply_config_to_env(config) + if config.default_workdir: + workdir = os.path.abspath(os.path.expanduser(config.default_workdir)) + set_workspace_root(workdir) + ensure_dirs() + agent = create_cli_agent() sessions: dict[str, str] = {} # sender -> thread_id