GitHub Issue #976 · 标签:路径映射

文件预览 404:会话读写路径漂移分析

分析方法:人工排查 + Codex 交叉验证,两者结论一致。结论给出根因、扩散隐患点、可减免的 if/else 与确定性修复方案。

一、根因(一句话)

SessionManager.sandbox_workspace_for_session() 的返回值依赖进程内、不落库、注册时机不确定的内存字典 self._user_home_pathsmanager.py:72)。同一个持久会话在「未注册 / 注册 local fallback / 注册 Blade OS home」三种状态间切换,于是对同一会话给出不同目录,读写路径天然会漂。

sandbox_workspace_for_session() 本应是纯确定性函数(同样的 session metadata 永远返回同一目录),现在却成了依赖可变全局状态的函数。这是问题的本质。

二、漂移链路

正向("上午能下午不能")

  1. create_session 只试 _resolve_user_home_host_path(),Blade OS 失败就不注册 → 初始化文件写进 fallback 目录(_engine_session_create.py:95-99)。
  2. 后续预览走 ensure_session_user_home(),若此刻 Blade OS 可解析就注册真实 user_home(sessions_helpers.py:303)。
  3. 再算 workspace → 目录切到 Blade OS home 下的空目录 → 404。

反向(重启触发)

_create_sandbox_provider() 解析 Blade OS 失败时会注册 local_user_home()_engine_runtime.py:136-140)——同进程内把 local fallback 钉住;但后端重启后 dict 清空,文件 API 又可能重新解析到 Blade OS home,目录再次漂移

三、其它类似隐患点(同根扩散)

位置隐患
execution/session_runtime.py:95runtime 按 session_id 缓存,root 固定在创建那一刻。之后 dict 变了,文件 API 读新目录、已起容器仍写旧目录。
sessions_helpers.py:331-334/root fallback 用 sandbox/.root;但 sandbox provider 把 local_user_home 挂到容器 /root。"文件 API 先访问" vs "容器先启动" 得到两个不同 /root
services/app_publish.py:438直接调 workspace,无 ensure。重启后容易发布空目录/旧 fallback 目录。
routes/software_factory.py
:409/462/496/660/841
多处直接读写 workspace,无 ensure,时机不一致。
routes/factory_v2.py:40自己实现第三套 _resolve_user_home():Blade OS 失败 fallback 到 local,但不注册回 SessionManager。注释写着"和 _engine_runtime.py 一样"——典型复制粘贴。
routes/prod_agents.py:504/540仍写 user_workspace() 而非 sandbox workspace,未来持久化会和容器可见 workspace 分叉。
_session_file_helpers.py:112sandbox.parent.name 反推 workspace_root。持久会话 parent 是「智能助手工作空间」而非 session_id,路径拼错
sandbox/docker_container_create.py:109持久容器存在第三种 /root 存储(named volume fallback)。产品目标是"用户文件可见",静默落到不可见 named volume 是错误兜底。

四、可减免 / 合并的 if/else

最该删sandbox_workspace_for_session() 的"registered vs local fallback"动态分支(manager.py:131-141)。两个内层分支只差 user_home 来源,函数却因此变成非确定性。正确做法:让"有效 user_home"在建会话时确定一次并持久化,之后只读结果。分支消失,确定性恢复。

合并四处 resolver 合并成一条路径。 _resolve_user_home_host_path()ensure_session_user_home()factory_v2._resolve_user_home()_create_sandbox_provider() 各有"只查 Blade OS / 查不到 local fallback / 查到才注册 / 不注册"差异,是重复且矛盾的 4 套语义。

收敛register_user_home() 不该是任意时刻可改路径的公共 cache。收敛成内部 ensure_effective_user_home(session)只初始化一次,已有路径不允许悄悄切换。

降级报错多余 fallback 直接报错。Blade OS 已配置(BLADE_OS_BASE_URL 非空)但解析失败 → 直接报错,不静默落 local;只有明确本地开发无 Blade OS 时才用 local_user_home()。少一层 fallback 就少一条漂移路径。

五、建议方案(确定性目录契约)

  • ·临时会话workspace/{session_id}/workspace/rootsandbox/.root
  • ·持久会话effective_user_home/智能助手工作空间/{session_id}/root → 同一个 effective_user_home
  • ·effective_user_home 一旦为某 session/user 确定,不因后续注册时机变化而改变

分两步落地

  1. 引入单一 resolver,让 create_session / _create_sandbox_provider / ensure_session_user_home / app_publish / software_factory 全部走它。
  2. 移除多余 fallback(Blade OS 配置了但解析失败即报错)。
关键测试:同一持久 session 在 create → file API → socket subscribe → runtime 创建 → 模拟进程重启 后,sandbox_workspace_for_session() 始终返回同一目录。这比"注册后路径正确"更能覆盖 #976。