xworkmate-app/docs/architecture/archive/assistant-thread-working-directory-flow.md
Cowork 3P 7573780956 docs(architecture): fix critical accuracy errors, stale refs, paths
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)
2026-06-04 06:53:38 +00:00

11 KiB
Raw Permalink Blame History

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 没切对,后续 workspaceRefcwd、右栏路径都会跟着取错。

2. 全局基础工作目录

默认线程目录由它派生:

<settings.workspacePath>/.xworkmate/threads/<sanitized-sessionKey>

关键位置:

3. 线程自己的目录绑定

读取优先级:

  1. 线程记录里的 workspaceRef
  2. 默认派生目录 defaultWorkspaceRefForSessionInternal(sessionKey)

4. 线程目录类型

它决定 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 -> 返回 null
  • remotePath -> 直接返回 workspaceRef
  • localPath
    • 目录存在:返回本地目录
    • 目录不存在且要求必须本地存在:返回 null
    • 目录不存在但不强制:返回字符串本身

关键位置:

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.resolvedWorkingDirectory
  • result.resolvedWorkspaceRefKind

历史关键位置:

  • 历史 Single Agent 桥接文件,已在 2026-05 清理中移除

当前逻辑中:

  • 当返回了非空 resolvedWorkingDirectory
  • resolvedWorkspaceRefKind == remotePath
  • 会回写到当前线程的 assistantThreadRecord

这样第二次开始,线程就能稳定复用 provider 返回的真实目录。

线程目录同步逻辑

1. 同步入口

  • syncAssistantWorkspaceRefForSessionInternal(sessionKey)

关键位置:

2. 它什么时候会改目录

会基于:

  • defaultWorkspaceRefForSessionInternal(sessionKey)
  • defaultWorkspaceRefKindForTargetInternal(target)
  • shouldMigrateWorkspaceRefInternal(...)

来判断是否要把线程目录重新同步成默认派生值。

关键位置:

3. 典型迁移触发

  • workspaceRef 为空
  • 指向旧共享根目录
  • 指向另一个 root 下的旧默认线程目录
  • 本地目录不存在

右侧边栏显示

右侧边栏不决定 cwd只负责展示

  • workspaceRef
  • workspaceRefKind

传入位置:

渲染位置:

因此右栏显示“未设置”或显示某个路径,本质上反映的是当前线程记录里到底有没有有效 workspaceRef

最关键的四个变量

如果只看“谁最直接影响任务线程独立工作目录”,优先级最高的是这四个:

  1. currentSessionKey
  2. assistantThreadRecordsInternal[sessionKey].workspaceRef
  3. assistantThreadRecordsInternal[sessionKey].workspaceRefKind
  4. settings.workspacePath

最关键的两个回退口

如果目标是“任务线程必须严格使用独立目录,不允许悄悄落回全局目录”,最值得做取舍的是这两个点:

  1. defaultWorkspaceRefForSessionInternal(...)
    • 线程没绑定目录时,是否允许自动派生一个默认目录
  2. Directory.current.path
    • cwd 解析失败时,是否允许最后再退到进程当前目录

这两个点决定了系统是:

  • “尽量可运行,但可能回退到全局目录”
  • 还是
  • “没有线程目录就禁止运行,必须先绑定正确目录”