15 KiB
Cross-Repo Call Relation Analysis
Date: 2026-06-05 Scope: xworkmate-app, xworkmate-bridge, openclaw-multi-session-plugins, openclaw.svc.plus (ref), playbooks (deploy)
1. 仓库角色与依赖方向
┌──────────────────────────────┐
│ xworkmate-app (Flutter) │ Desktop UI + 本地状态
│ 版本: 1.1.4+1 │ 用户线程、任务对话框、artifact 面板
└──────────────┬───────────────┘
│ JSON-RPC over WebSocket/HTTP
│ Path: /acp, /acp/rpc
│ Auth: Bearer <BRIDGE_AUTH_TOKEN>
▼
┌──────────────────────────────┐
│ xworkmate-bridge (Go) │ ACP 控制面 + 路由引擎
│ 版本: v1.1.0 │ 任务编排、concurrency gate
│ Port: 8787 │ artifact download proxy
└──────────────┬───────────────┘
│ Custom JSON-RPC over WebSocket v4
│ Auth: Ed25519 device identity + shared token
▼
┌──────────────────────────────┐
│ openclaw.svc.plus (Node/TS)│ Agent 运行时 + 工具执行
│ Port: 18789 │ browser、image-gen、video-gen 等工具
│ State: ~/.openclaw/ │ 全局 media cache: media/browser/ 等
└──────────────┬───────────────┘
│ Plugin SDK (Gateway RPC)
▼
┌──────────────────────────────┐
│ openclaw-multi-session- │ 多会话 artifact 管理
│ plugins (TS) │ 文件输出: tasks/<session>/<run>/
└──────────────────────────────┘
仓库 playbooks 是 Ansible 部署基础设施,管理 xworkmate-bridge 的 systemd、Caddy、VPN 拓扑。它不是运行时调用链的一部分,但控制 bridge 的 config.yaml 注入和环境变量。
2. 三大调用链
链路 A: 任务执行链(主流程)
1. xworkmate-app
AssistantPage.sendChatMessage()
→ AppController.sendChatMessage()
→ ExternalCodeAgentAcpDesktopTransport.executeTask()
→ GatewayAcpClient.request('session.start' | 'session.message')
→ WebSocket POST /acp with routing params
2. xworkmate-bridge
http_handler.Handler() → handleRequest()
→ orchestrator.Process()
→ routing engine: Resolve(params, prompt, memory)
→ openClawGatewayAdmissionGate.acquire() // max 5 concurrent, 20 queued
→ startOpenClawGatewayTask()
→ openClawArtifactPrepare() // calls xworkmate.artifacts.prepare
→ gatewayruntime.send('chat.send')
→ returns running handle; Bridge does not own terminal state
3. openclaw.svc.plus
Gateway receives 'chat.send'
→ Agent runner processes turn
→ Tools (browser, image-gen, etc.) execute
→ Tool outputs go to ~/.openclaw/media/* or /tmp/openclaw/* ← CRITICAL
4. openclaw-multi-session-plugins
prepareXWorkmateArtifacts() → creates tasks/<session>/<run>/
exportXWorkmateArtifacts() → scans ONLY tasks/<session>/<run>/
→ returns manifest + base64 files to bridge
5. xworkmate-bridge
forwards native transcript/task events to app
→ xworkmate.tasks.get is forwarded to Plugin/OpenClaw native task-registry
→ artifact collect/export remains an explicit proxy operation
6. xworkmate-app
ExternalCodeAgentAcpDesktopTransport receives SSE
→ applies gateway chat result
→ syncArtifactsFromBridge() → writes to local ~/.xworkmate/threads/<session>/
链路 B: Artifact 回传链
openclaw.svc.plus (tool saves file to media/browser/)
→ Agent declares output file path
→ Multi-session plugin: detectPathIsOutsideTaskScope(path)
→ FILE IS SKIPPED — not in tasks/<session>/<run>/
→ exportXWorkmateArtifacts returns incomplete manifest
→ xworkmate-bridge: terminal snapshot has no artifacts
→ xworkmate-app: lastArtifactSyncStatus=no-exported-artifacts
链路 C: Bridge Distributed Forwarding 链
xworkmate-app → xworkmate-bridge (primary, 172.29.10.1:8787)
→ distributedTaskRouter selects node
→ HTTP POST → cn-xworkmate-bridge (edge, 172.29.10.2:8787)
over WireGuard VPN tunnel
→ Forward headers:
X-XWorkmate-Forward-Source: <nodeId>
X-XWorkmate-Forward-Target: <nodeId>
X-XWorkmate-Forward-Hop: <N>
3. 协议边界
ACP 协议(App ↔ Bridge)
- Transport: WebSocket GET
/acp或 HTTP POST/acp/rpc - Format: JSON-RPC 2.0
- Auth:
Authorization: Bearer <BRIDGE_AUTH_TOKEN> - Methods:
Method 方向 说明 acp.capabilities→ 获取 provider/gateway catalog session.start→ 开始会话 session.message→ 续写会话 session.cancel/session.close→ 终止会话 xworkmate.routing.resolve→ 路由预查询 xworkmate.gateway.connect→ 建立 gateway 连接 xworkmate.gateway.request→ gateway 代理请求 xworkmate.tasks.get→ 查询异步任务快照 xworkmate.tasks.cancel→ 取消异步任务 xworkmate.tools.invoke→ 工具调用 xworkmate.desktop.*↔ WebRTC 桌面流 session.update← SSE 推送 (bridge→app)
Gateway 协议(Bridge ↔ OpenClaw)
- Transport: WebSocket to gateway endpoint
- Format: Custom JSON-RPC v4
- Auth: Ed25519 device identity signing + shared token
- Methods:
Method 方向 说明 connect→ 建立 gateway 会话 chat.send→ 提交 agent 任务 agent.wait→ 轮询任务状态 (1s timeout) agent.cancel→ 取消任务 xworkmate.artifacts.prepare→ 分配 artifact 目录 xworkmate.artifacts.export→ 导出 artifact xworkmate.artifacts.read→ 读取单个 artifact tools.invoke→ 调用 OpenClaw 工具
Artifact 协议(Plugin ↔ File System)
- Scope creation:
tasks/<safeSessionKey>/<safeRunId>/under workspace root - Scope validation:
isWithinRoot(workspaceRoot, scopeRoot)— 防止路径穿越 - Security: HMAC-SHA256 签名 artifact ref,24h TTL
- Bridge download proxy:
/artifacts/openclaw/download?ref=<signed>&t=<expiry>
4. 关键数据结构
OpenClawTaskRecord(xworkmate-bridge)
type OpenClawTaskRecord struct {
SessionID, ThreadID, TurnID, RunID string // 四元组标识
SessionKey string // 传递给 plugin 做 scope
GatewayProviderID string // "openclaw"
TaskLoadClass string // short_task/long_task/complex_chain_task
RuntimeBudgetMinutes int // 10/30/60
StartedAt, DeadlineAt time.Time
ProgressStage, ProgressMessage string
PreparedArtifact *openClawPreparedArtifactScope
}
Session Key 格式
agent:<agentId>:<sessionId>
由 agentIdFromSessionKey() 解析(exportArtifacts.ts:730-736),用于查找 agent config 中的 workspace 路径。
Artifact Ref
<sessionKey>::<runId>::<relativePath>::<hmac>
- HMAC-SHA256 with plugin-configured signing secret
- 24h expiry
- Bridge 验证后代理下载
ArtifactScope(plugin)
tasks/<safeSessionKey>/<safeRunId>/
safeSessionKey= sanitize(sessionKey): replace [/\:*?"<>|] with -, truncate 96 charssafeRunId= sanitize(runId): same rules
5. 调用链入口(xworkmate-app 侧)
主入口:AssistantPage.sendChatMessage()
lib/features/assistant/assistant_page.dart
→ assistant_page_state_actions.dart: sendChatMessage()
→ app_controller_openclaw_task_queue.dart: queueOpenClawGatewayWork()
→ go_task_service_desktop_service.dart: DesktopGoTaskService.startSession()
→ external_code_agent_acp_desktop_transport.dart: executeTask()
→ GatewayAcpClient.request('session.start') via WebSocket /acp
→ GatewayAcpClient.request('session.message') via WebSocket /acp
→ pollBridgeTaskSnapshot() → 'xworkmate.tasks.get'
恢复入口:AppController.resumeOpenClawRunningTask()
app_controller_desktop_thread_sessions.dart: resolveGatewayThreadConnectionState()
→ 检查 localLastRunId 是否对应一个未完成的 OpenClaw 任务
→ external_code_agent_acp_desktop_transport.dart: pollBridgeTaskSnapshot()
→ 'xworkmate.tasks.get' → 获取 terminal snapshot
取消入口:AppController.cancelCurrentTask()
assistant_page_state_actions.dart: onCancelTask()
→ external_code_agent_acp_desktop_transport.dart: cancelAndCloseTask()
→ 'session.cancel' 或 'xworkmate.tasks.cancel'
6. 最容易改坏的 10 个地方
🔴 CRITICAL: 路径断裂区
6.1 OpenClaw 工具输出路径 ≠ plugin artifact scope
OpenClaw 工具(browser screenshot、image generation、video render、file write)将输出写到:
~/.openclaw/media/browser/<uuid>.png—saveMediaBuffer(buffer, contentType, "browser")~/.openclaw/media/inbound/<uuid>.<ext>— 上传/附件/tmp/openclaw/downloads/— 浏览器下载/tmp/openclaw/traces/— 工具 trace
但 exportXWorkmateArtifacts() 只扫描 tasks/<session>/<run>/。
后果: 工具生成的图片、文档、视频在 artifact 回传时被静默丢弃。用户收到 "no-exported-artifacts"。
触发条件: 任何使用 OpenClaw 内置工具(browser、image-gen、video-gen)的任务,如果工具输出没有显式复制到 tasks/<session>/<run>/ 下。
修复方向:
- 方案 A: 在 plugin 的
exportXWorkmateArtifacts增加对~/.openclaw/media/browser/的扫描(按 session 过滤) - 方案 B: 修改 OpenClaw 工具,使其在 gateway session 上下文中使用 session-scoped 输出路径
- 方案 C: 在 bridge 的
openClawArtifactExport()中,额外从 gateway 查询 media cache 中的当前 session 产物
6.2 Session Key 派生不一致
Session key 由多个来源派生:
- App:
ExternalCodeAgentAcpDesktopTransport._sessionKey— 从 response params 获取 - Bridge:
OpenClawTaskRecord.SessionKey— 从 gateway response 获取 - Plugin:
agentIdFromSessionKey(sessionKey)— 解析agent:<id>:<session>格式
如果任一端对 session key 的生成或传递逻辑发生变化,artifact scope 将不匹配。
验证方法: 在 artifact prepare/export 两端打印 sessionKey,确保完全一致。
6.3 Bridge 的 artifact prepare 时序
openClawArtifactPrepare() 在 bridge 侧通过 gateway 调用 xworkmate.artifacts.prepare,但该调用发生在 chat.send 之前还是之后决定了 scope 目录是否存在。
当前代码: startOpenClawGatewayTask() 先 prepare 再 send — 正确。但如果在 send 之前 prepare 失败(超时、gateway 不可达),任务仍会提交但无 scope 目录。
6.4 Artifact ref 签名密钥轮换
HMAC-SHA256 签名密钥在 plugin config 中的 artifactRefSigningSecret。如果密钥变更,旧的 artifact ref 全部失效。Bridge 的 download proxy 会返回 403。
6.5 Bridge admission gate 满时静默丢失
openClawGatewayAdmissionGate.acquire():
- maxActive: 5
- maxQueued: 20
- queueTimeout: 10 min
当队列满且超时,返回 OPENCLAW_GATEWAY_BUSY。App 端是否有正确处理?检查 app_controller_openclaw_task_queue.dart 中的 drainOpenClawGatewayQueue 逻辑。
6.6 Bridge 会话纯内存存储
xworkmate-bridge 的 session 存储在 Server.sessions map[string]*session,不持久化。Bridge 重启 → 所有运行中的任务丢失 → App 端 xworkmate.tasks.get 返回 "session not found" → 任务静默失败。
🟠 HIGH: 状态同步区
6.7 TaskSnapshot 字段不完整
Bridge 不再组装 terminal snapshot。Terminal success/failure 必须来自 OpenClaw native task-registry;artifact manifest 只由 Plugin 的 collect/export/read 路径提供。如果 plugin 返回的 artifact manifest 中有文件但内容为空、或相对路径超出 scope,App 侧 artifact 栏可能缺少条目,但任务终态不应因此由 Bridge 推断。
6.8 SSE 流中断后的轮询策略
当 SSE stream 关闭(网络断开、bridge 重启),app 的 ExternalCodeAgentAcpDesktopTransport 进入 polling 模式调用 xworkmate.tasks.get。如果轮询间隔、重试次数、超时策略设置不当,可能导致:
- 轮询过早结束(任务实际还在跑)
- 轮询永不结束(bridge 已重启 session 丢失)
6.9 Plugin 的 workspace 解析回退链过长
resolveWorkspaceDir() 的回退链:
- 显式 params.workspaceDir
- pluginConfig.workspaceDir
- agent config (按 agentId 匹配 → default agent → 第一个 agent)
- OPENCLAW_PROFILE env →
~/.openclaw/workspace-<profile> ~/.openclaw/workspace
如果 agent config 变更(增删 agent、修改 default、调整 workspace),可能影响所有运行中 session 的 artifact 路径。
App/owner-scoped remoteWorkingDirectoryHint(例如 /owners/...)不是
OpenClaw 文件系统路径,Bridge 不得把它透传为 plugin workspaceDir。否则
Plugin 会在创建 tasks/<session>/<run>/ 前先 realpath(workspaceDir),并以
ENOENT 失败。
🟡 MEDIUM: 配置与部署区
6.10 Distributed bridge forwarding topology 变更
Bridge 的双节点拓扑(primary + edge)定义在 config.yaml 的 distributed.nodes。拓扑变更(增删节点、修改 endpoint、更新转发规则)会影响跨区域任务路由。session stickiness(24h TTL)意味着变更后已有 session 可能被路由到错误的节点。
7. tasks// 状态矩阵
| 阶段 | 状态 | 文件位置 | 谁负责 |
|---|---|---|---|
| prepare | xworkmate.artifacts.prepare 调用 |
plugin 创建空目录 | multi-session-plugins |
| execute | Agent 工具运行 | 工具输出到 ~/.openclaw/media/ 或 /tmp/ |
openclaw.svc.plus |
| — | GAP: 工具输出不在 scope 内 | files in media/, NOT in tasks/ | 无人负责 |
| export | xworkmate.artifacts.export 扫描 |
扫描 tasks/<session>/<run>/ |
multi-session-plugins |
| snapshot | bridge 组装 terminal snapshot | 内存中, 通过 xworkmate.tasks.get 查询 |
xworkmate-bridge |
| sync | app 下载 artifact 到本地 | ~/.xworkmate/threads/<session>/ |
xworkmate-app |
核心 gap: execute 阶段工具写文件到全局路径 → export 阶段只扫描 task scope → 文件不可见。
8. 建议沉淀的 chain-map.md
| 链路 | 文件名 | 覆盖范围 |
|---|---|---|
| A | chain-map-task-execution.md |
App→Bridge→OpenClaw→Plugin 完整任务执行 |
| B | chain-map-artifact-lifecycle.md |
Artifact prepare/export/read/download 全周期 |
| C | chain-map-bridge-distributed.md |
Bridge 分布式转发拓扑 |
| D | chain-map-session-recovery.md |
App 会话恢复、task.get 轮询、bridge 重启恢复 |