"""Shared path resolution for thread virtual paths (e.g. mnt/user-data/outputs/...).""" from pathlib import Path from fastapi import HTTPException from deerflow.config.paths import get_paths from deerflow.runtime.actor_context import get_effective_user_id def resolve_thread_virtual_path(thread_id: str, virtual_path: str, *, user_id: str | None = None) -> Path: """Resolve a virtual path to the actual filesystem path under thread user-data. Args: thread_id: The thread ID. virtual_path: The virtual path as seen inside the sandbox (e.g., /mnt/user-data/outputs/file.txt). user_id: Explicit user id override. Falls back to the current actor context. Returns: The resolved filesystem path. Raises: HTTPException: If the path is invalid or outside allowed directories. """ try: resolved_user_id = get_effective_user_id() if user_id is None else user_id return get_paths().resolve_virtual_path(thread_id, virtual_path, user_id=resolved_user_id) except ValueError as e: status = 403 if "traversal" in str(e) else 400 raise HTTPException(status_code=status, detail=str(e))