Accuracy fixes: - app-orchestration.md: remove non-existent constructor params - models-and-config.md: remove wrong multiAgent field from SettingsSnapshot - runtime-contracts.md: add missing multiAgent/collaborationMode/routingHint fields Stale multi-agent refs: - unified-routing-architecture.md: agent/multi-agent → agent (含 bridge 转发) - bridge-runtime-routing-map.md: multi-agent tasks → multi-agent forwarding tasks - cross-repo-task-state-workflow.md: remove multi-agent orchestration from mermaid - runtime-contracts.md, feature-surfaces.md: 多 agent → agent Organization: - Move cloud-session-service and stage4-helper to archive/ - Fix 22 xworkmate/ → xworkmate-app/ paths in archive doc - Fix XWorkmate.svc.plus repo name in simple-theme-default.md - Update README.md index and public-api/README.md coverage stats (132/590)
11 KiB
Assistant Thread Working Directory Flow
更新时间:2026-03-28
已过时:本文记录的是
workspaceRef / workspaceRefKind / cwd fallback主导时期的线程目录流转。当前实现请优先参考: docs/architecture/task-control-plane-unification.md
新文档已经把 TaskThread 的主流程图和状态图重画为基于
workspaceBinding / executionBinding / lifecycleState的 Mermaid 版本。
本文记录 XWorkmate 中“任务线程独立工作目录”的变量流转关系,重点覆盖:
- UI 选中线程后,当前线程是谁
- 线程记录里哪些字段决定工作目录
- cwd 如何被解析并传给 Single Agent runner
- provider 返回真实线程目录后如何回写
- 右侧边栏如何展示当前任务工作路径
流转图
flowchart LR
A["UI选中线程<br/>controller.switchSession(sessionKey)"] --> B["currentSessionKey"]
B --> C["按 sessionKey 读取线程记录<br/>assistantThreadRecordsInternal[sessionKey]"]
C --> D1["workspaceRef"]
C --> D2["workspaceRefKind"]
C --> D3["executionTarget"]
C -->|没有 workspaceRef| E["defaultWorkspaceRefForSessionInternal(sessionKey)"]
E --> E2["settings.workspacePath + /.xworkmate/threads/<session>"]
D1 --> F["assistantWorkspaceRefForSession(sessionKey)"]
E --> F
D2 --> G["assistantWorkspaceRefKindForSession(sessionKey)"]
D3 --> G2["defaultWorkspaceRefKindForTargetInternal(target)"]
F --> H["resolveSingleAgentWorkingDirectoryForSessionInternal(sessionKey, provider)"]
G --> H
G2 --> H
I["provider endpoint<br/>https/wss or local"] --> H
H -->|localPath 且存在| J["workingDirectory = 本地线程目录"]
H -->|remotePath| K["workingDirectory = 远端线程目录字符串"]
H -->|objectStore / 解析失败| L["fallback = Directory.current.path"]
J --> M["SingleAgentRunRequest.workingDirectory"]
K --> M
L --> M
M --> N["singleAgentRunner.run(...)"]
N --> O["Direct app-server / CLI / provider"]
O --> P1["result.resolvedWorkingDirectory"]
O --> P2["result.resolvedWorkspaceRefKind"]
P1 --> Q["回写线程记录<br/>upsertAssistantThreadRecordInternal(...)"]
P2 --> Q
Q --> R["assistantThreadRecordsInternal[sessionKey] 更新"]
R --> S["右栏显示<br/>AssistantArtifactSidebar(workspaceRef, workspaceRefKind)"]
B --> S
核心变量
1. 当前线程
- 变量:
currentSessionKey - 作用:决定当前操作命中了哪个任务线程
- 关键位置:
如果 currentSessionKey 没切对,后续 workspaceRef、cwd、右栏路径都会跟着取错。
2. 全局基础工作目录
- 变量:
settings.workspacePath - 作用:线程默认目录的根目录
- 关键位置:
默认线程目录由它派生:
<settings.workspacePath>/.xworkmate/threads/<sanitized-sessionKey>
关键位置:
3. 线程自己的目录绑定
- 变量:
assistantThreadRecordsInternal[sessionKey].workspaceRef - 作用:线程级工作目录的真实绑定值
- 关键位置:
读取优先级:
- 线程记录里的
workspaceRef - 默认派生目录
defaultWorkspaceRefForSessionInternal(sessionKey)
4. 线程目录类型
- 变量:
assistantThreadRecordsInternal[sessionKey].workspaceRefKind - 类型:
localPathremotePathobjectStore
- 关键位置:
它决定 workspaceRef 后续如何参与 cwd 解析。
默认目录生成
1. 默认目录生成函数
defaultWorkspaceRefForSessionInternal(sessionKey)defaultLocalWorkspaceRefForSessionInternal(sessionKey)
关键位置:
2. 线程目录名
threadWorkspaceDirectoryNameInternal(sessionKey)- 作用:把
sessionKey变成稳定、可落盘的目录名
关键位置:
线程初始化时的写入
新任务创建时,线程上下文初始化会直接写入默认目录:
workspaceRef: defaultWorkspaceRefForSessionInternal(normalizedSessionKey)workspaceRefKind: defaultWorkspaceRefKindForTargetInternal(resolvedTarget)
关键位置:
这意味着:
- 新线程刚创建时,通常先拿到“默认派生目录”
- 后续如果 provider 返回真实线程目录,才可能再回写成远端目录
cwd 解析
1. 入口
resolveSingleAgentWorkingDirectoryForSessionInternal(sessionKey, provider)
关键位置:
2. 参与变量
assistantWorkspaceRefForSession(sessionKey)assistantWorkspaceRefKindForSession(sessionKey)provider- provider endpoint 的 scheme / host
3. 解析规则
objectStore-> 返回nullremotePath-> 直接返回workspaceReflocalPath- 目录存在:返回本地目录
- 目录不存在且要求必须本地存在:返回
null - 目录不存在但不强制:返回字符串本身
关键位置:
- app_controller_desktop_runtime_coordination_impl.dart
- app_controller_desktop_runtime_coordination_impl.dart
- app_controller_desktop_runtime_coordination_impl.dart
provider 对 cwd 的影响
1. 是否必须是本地目录
singleAgentProviderRequiresLocalPathRuntimeInternal(provider)
关键位置:
规则简述:
https/wss远端 provider:不强制本地路径存在- loopback / 本地 provider:通常要求本地目录
runner 前的最后兜底
如果上面的解析结果是 null,发送给 Single Agent 时会退回:
Directory.current.path
历史关键位置:
- 历史 Single Agent 桥接文件,已在 2026-05 清理中移除
这就是“任务线程没有有效目录时,命令最后跑到全局/容器 cwd”的直接原因。
provider 结果回写
Single Agent 运行后可能返回:
result.resolvedWorkingDirectoryresult.resolvedWorkspaceRefKind
历史关键位置:
- 历史 Single Agent 桥接文件,已在 2026-05 清理中移除
当前逻辑中:
- 当返回了非空
resolvedWorkingDirectory - 且
resolvedWorkspaceRefKind == remotePath - 会回写到当前线程的
assistantThreadRecord
这样第二次开始,线程就能稳定复用 provider 返回的真实目录。
线程目录同步逻辑
1. 同步入口
syncAssistantWorkspaceRefForSessionInternal(sessionKey)
关键位置:
2. 它什么时候会改目录
会基于:
defaultWorkspaceRefForSessionInternal(sessionKey)defaultWorkspaceRefKindForTargetInternal(target)shouldMigrateWorkspaceRefInternal(...)
来判断是否要把线程目录重新同步成默认派生值。
关键位置:
3. 典型迁移触发
workspaceRef为空- 指向旧共享根目录
- 指向另一个 root 下的旧默认线程目录
- 本地目录不存在
右侧边栏显示
右侧边栏不决定 cwd,只负责展示:
workspaceRefworkspaceRefKind
传入位置:
渲染位置:
因此右栏显示“未设置”或显示某个路径,本质上反映的是当前线程记录里到底有没有有效 workspaceRef。
最关键的四个变量
如果只看“谁最直接影响任务线程独立工作目录”,优先级最高的是这四个:
currentSessionKeyassistantThreadRecordsInternal[sessionKey].workspaceRefassistantThreadRecordsInternal[sessionKey].workspaceRefKindsettings.workspacePath
最关键的两个回退口
如果目标是“任务线程必须严格使用独立目录,不允许悄悄落回全局目录”,最值得做取舍的是这两个点:
defaultWorkspaceRefForSessionInternal(...)- 线程没绑定目录时,是否允许自动派生一个默认目录
Directory.current.path- cwd 解析失败时,是否允许最后再退到进程当前目录
这两个点决定了系统是:
- “尽量可运行,但可能回退到全局目录”
- 还是
- “没有线程目录就禁止运行,必须先绑定正确目录”