Document OpenClaw artifact dirs protocol boundary

This commit is contained in:
Haitao Pan 2026-06-05 21:23:56 +08:00
parent ea67604986
commit 5f3dc974aa
5 changed files with 83 additions and 115 deletions

View File

@ -60,11 +60,12 @@ xworkmate-app xworkmate-bridge
───────────── ────────────────
lib/runtime/external_code_agent_acp_desktop_transport.dart
ExternalCodeAgentAcpTransport
→ session.start(provider="gateway")
→ session.start(routing: gateway/openclaw,
metadata.xworkmateTaskArtifactContract.expectedArtifactDirs)
└─ lib/runtime/gateway_acp_client.dart
POST /gateway/openclaw ────────────────► internal/acp/http_handler.go
│ HandleOpenClawRPC()
│ └─ internal/acp/gateway.go
WebSocket /acp or POST /acp/rpc ───────► internal/acp/http_handler.go
│ HandleWebSocket()/HandleRPC()
│ └─ internal/acp/rpc_handler.go
│ Orchestrator.Process()
│ │
│ ├─ 本地 gateway
@ -99,6 +100,12 @@ lib/app/app_controller_openclaw_task_queue.dart
→ 持久化 & 恢复 (pollOpenClawTaskAssociationInternal)
```
Protocol boundary:
- `expectedArtifactDirs` is not a `chat.send` root parameter.
- App sends it only as `metadata.xworkmateTaskArtifactContract.expectedArtifactDirs` on `session.start`.
- Bridge maps it only into `xworkmate.artifacts.export` and `xworkmate.artifacts.collect-and-snapshot`.
- Bridge must reject old root/metadata compatibility paths instead of probing fallback keys.
### 涉及的 key files:
| 层 | 文件 | 作用 |
|----|------|------|
@ -107,7 +114,7 @@ lib/app/app_controller_openclaw_task_queue.dart
| app | `lib/runtime/go_task_service_desktop_service.dart` | 桌面任务服务 |
| app | `lib/app/app_controller_openclaw_task_queue.dart` | 客户端任务队列 |
| bridge | `internal/acp/gateway.go` | OpenClaw 集成 |
| bridge | `internal/acp/http_handler.go` | `/gateway/openclaw` 端点 |
| bridge | `internal/acp/http_handler.go` | `/acp` / `/acp/rpc` 端点 |
| bridge | `internal/gatewayruntime/runtime.go` | 网关 WS 客户端 |
| bridge | `internal/acp/distributed_forwarder.go` | 分布式转发 |
| plugins | `index.ts` | 插件入口 |
@ -198,49 +205,28 @@ lib/runtime/opencode_config_bridge.dart
---
## Chain 5: 多 Agent 编排 (Plugin → Bridge 反向调用)
## Chain 5: 已移除的 Plugin → Bridge 反向调用
```
openclaw-multi-session-plugins xworkmate-bridge
───────────────────────────── ────────────────
src/bridgeAgents.ts
run(input)
→ fetch(bridgeUrl, {
method: "POST",
body: { jsonrpc: "2.0",
method: "session.start",
params: {
sessionId: "openclaw:<sessionKey>",
multiAgent: true,
mode: "multi-agent",
routing: { orchestrationMode, steps, ... }
}
}
})
→ HTTP POST /acp/rpc ──────────────────────► internal/acp/rpc_handler.go
handleRequest("session.start", ...)
└─ internal/router/
→ Orchestrator.Process()
→ 多 agent 协作
→ 结果返回插件
旧链路:
openclaw-multi-session-plugins/src/bridgeAgents.ts
→ HTTP POST xworkmate-bridge /acp/rpc
→ session.start(multiAgent=true)
src/exportArtifacts.ts
→ 结果写入 artifactDirectory
→ multi-agent-result.json
→ multi-agent-result.md
当前链路:
xworkmate-bridge → OpenClaw Gateway → openclaw-multi-session-plugins
→ xworkmate.artifacts.*
```
### 涉及的 key files:
| 层 | 文件 | 作用 |
|----|------|------|
| plugins | `src/bridgeAgents.ts` | Bridge HTTP 调用 |
| bridge | `internal/acp/rpc_handler.go` | RPC 分发 |
| bridge | `internal/acp/orchestrator.go` | 多 agent 编排 |
| plugins | `index.ts` / `src/exportArtifacts.ts` | artifact scope adapter |
| bridge | `internal/acp/orchestrator.go` | OpenClaw gateway orchestration |
### 协议: HTTP JSON-RPC (插件 → Bridge)
### 协议: 无 Plugin→Bridge 运行时协议
### 断点风险:
- 插件配置中 bridgeUrl 指向错误 → 调用失败
- 双向循环依赖: plugins 调 bridge, bridge 调 plugins
- 旧版本插件若仍暴露 bridge agents 工具,会恢复循环依赖,应从 manifest 和 dist 中删除
---
@ -251,12 +237,11 @@ src/exportArtifacts.ts
app bridge plugins
┌───────┬───────┬───────┐
app │ - │ ACP │ - │
被调 bridge │ - │ - │ HTTP
被调 bridge │ - │ - │ -
方 plugins │ - │ GW │ - │
└───────┴───────┴───────┘
ACP = JSON-RPC 2.0 over WebSocket/HTTP SSE
HTTP = JSON-RPC 2.0 over HTTP POST
GW = OpenClaw Gateway RPC over WebSocket (Ed25519)
```

View File

@ -162,7 +162,7 @@ App → Bridge → Gateway: device.pair.approve / device.pair.reject
| 方法 | 方向 | 描述 |
|------|------|------|
| `connect` | Bridge→Gateway | 认证连接 |
| `chat.send` | Bridge→Gateway | 发送 agent 执行请求 |
| `chat.send` | Bridge→Gateway | 发送 agent 执行请求;不得携带 `expectedArtifactDirs` |
| `agent.wait` | Bridge→Gateway | 等待 agent 完成 |
| `health` | Bridge→Gateway | 健康检查 |
| `skills.status` | Bridge→Gateway | 技能状态 |
@ -199,24 +199,21 @@ App → Bridge → Gateway: device.pair.approve / device.pair.reject
"extensions": ["./dist/index.js"]
},
"gatewayMethods": [
"xworkmate.tasks.get",
"xworkmate.artifacts.prepare",
"xworkmate.artifacts.export",
"xworkmate.artifacts.collect-and-snapshot",
"xworkmate.artifacts.list",
"xworkmate.artifacts.read",
"xworkmate.agents.run"
"xworkmate.artifacts.read"
],
"tools": {
"openclaw_multi_session_artifacts": { "sessionScoped": true },
"openclaw_multi_session_agents": { "sessionScoped": true }
"openclaw_multi_session_artifacts": { "sessionScoped": true }
},
"config": {
"workspaceDir": "~/.openclaw/workspace",
"maxFiles": 1000,
"maxInlineBytes": 1048576,
"artifactRefSigningSecret": "",
"bridgeUrl": "",
"bridgeToken": "",
"bridgeTimeoutMs": 120000
"artifactRefSigningSecret": ""
}
}
```
@ -226,10 +223,16 @@ App → Bridge → Gateway: device.pair.approve / device.pair.reject
| 方法 | 输入 | 输出 |
|------|------|------|
| `xworkmate.artifacts.prepare` | `{sessionKey, runId, workspaceDir?}` | `{artifactScope, artifactDirectory, scopeKind}` |
| `xworkmate.artifacts.export` | `{sessionKey, runId, artifactScope?, sinceUnixMs?, ...}` | `{artifacts[], manifestMarkdown, warnings[]}` |
| `xworkmate.artifacts.export` | `{sessionKey, runId, artifactScope?, sinceUnixMs?, expectedArtifactDirs?}` | `{artifacts[], manifestMarkdown, warnings[]}` |
| `xworkmate.artifacts.collect-and-snapshot` | `{sessionKey, runId, artifactScope?, sinceUnixMs?, expectedArtifactDirs?}` | `{artifacts[], warnings[]}` |
| `xworkmate.artifacts.list` | `{sessionKey, runId, ...}` | `{artifacts[] (不含内容), manifestMarkdown}` |
| `xworkmate.artifacts.read` | `{sessionKey, runId, artifactScope?, relativePath?, artifactRef?}` | `{artifacts[0], manifestMarkdown}` |
| `xworkmate.agents.run` | `{sessionKey, runId, taskPrompt, mode, steps?, participants?, ...}` | `{bridgeResult, artifacts[]}` |
| `xworkmate.tasks.get` | `{sessionKey, runId}` | `{taskStatus, status, artifacts[], warnings[]}` |
`expectedArtifactDirs` has a single upstream source:
`session.start.metadata.xworkmateTaskArtifactContract.expectedArtifactDirs`.
Bridge may pass it to artifact export/snapshot RPCs only. It is not accepted at
`session.start` root, metadata root, `chat.send`, or `xworkmate.tasks.get`.
### 安全边界
@ -255,57 +258,16 @@ artifactRef = HMAC-SHA256(workspaceRoot, sessionKey, runId, relativePath, size,
输出: { artifacts[]|manifestMarkdown }
安全: sessionKey/runId 由 OpenClaw 运行时注入,
Agent 无法覆盖 (在 tool factory 中解构排除)
工具: openclaw_multi_session_agents
输入: { taskPrompt, mode, steps?, participants?, maxTurns? }
输出: { result, artifacts[] }
安全: sessionKey/runId 同样由运行时注入
```
---
## 5. Plugin → Bridge 反向调用边界
### HTTP JSON-RPC 调用
```
Plugin (bridgeAgents.ts)
→ POST {bridgeUrl}/acp/rpc
Headers:
Authorization: Bearer <bridgeToken>
Content-Type: application/json
Body:
{
"jsonrpc": "2.0",
"id": "openclaw-<timestamp>",
"method": "session.start",
"params": {
"sessionId": "openclaw:<sessionKey>",
"threadId": "<sessionKey>",
"taskPrompt": "...",
"workingDirectory": "<artifactDirectory>",
"multiAgent": true,
"mode": "multi-agent",
"routing": {
"orchestrationMode": "...",
"steps": [...]
}
}
}
```
### 配置
```
环境变量:
XWORKMATE_BRIDGE_URL — bridge 基础 URL (自动追加 /acp/rpc)
XWORKMATE_BRIDGE_TOKEN — bridge 认证 token
插件配置 (openclaw.plugin.json):
bridgeUrl: "https://xworkmate-bridge.svc.plus"
bridgeToken: "..."
bridgeTimeoutMs: 120000
```
该边界已移除。插件不得通过 HTTP 调用 xworkmate-bridge也不再暴露
`xworkmate.agents.run``openclaw_multi_session_agents`。多 agent 编排由
OpenClaw 原生 runtime 或 xworkmate-bridge 自身拥有,插件只保留 artifact
scope、artifact manifest/read、task snapshot adapter 和 session key mapping。
---
@ -322,6 +284,5 @@ Plugin (bridgeAgents.ts)
| Bridge↔Gateway | Ed25519 密钥轮换无自动化 | **低** |
| Gateway↔Plugin | artifactRef 签名密钥不一致 | **高** |
| Gateway↔Plugin | 24h 签名过期 → 历史工件不可读 | **中** |
| Plugin→Bridge | bridgeUrl/bridgeToken 配置错误 | **高** |
| Plugin→Bridge | 循环依赖 (plugin 调 bridge, bridge 调 plugin) | **高** |
| Plugin→Bridge | 已移除;旧插件版本若残留会恢复循环依赖 | **高** |
| 全部 | 多仓库版本耦合 (app 1.1.4 + bridge latest + plugins 0.1.15) | **中** |

View File

@ -101,11 +101,14 @@ xworkmate-app
→ openclaw: WebSocket → OpenClaw gateway (ws://127.0.0.1:18789)
xworkmate-app
→ POST /gateway/openclaw (session.start, provider="gateway")
→ acp/gateway.go → gatewayruntime/runtime.go
→ /acp or /acp/rpc session.start (routing: gateway/openclaw)
metadata.xworkmateTaskArtifactContract.expectedArtifactDirs
→ acp/rpc_handler.go → orchestrator.go → gatewayruntime/runtime.go
→ WebSocket → OpenClaw gateway
→ chat.send → agent 执行
→ xworkmate.artifacts.* → openclaw-multi-session-plugins
→ chat.send → agent 执行 (no expectedArtifactDirs root field)
→ xworkmate.artifacts.export/collect-and-snapshot
with expectedArtifactDirs from the artifact contract
→ openclaw-multi-session-plugins
```
### 与其他仓库的关系
@ -121,21 +124,20 @@ Go 1.25, gorilla/websocket, pion/webrtc v4, YAML 配置。无 gRPC。Docker 部
## 3. openclaw-multi-session-plugins (TypeScript)
### 核心职责
OpenClaw 插件 — 工件管理平面。为每个 session/run 对创建隔离工件目录,扫描文件,生成 HMAC 签名引用,提供多 agent 编排能力
OpenClaw 插件 — XWorkmate artifact 作用域适配层。为每个 session/run 对创建隔离工件目录,扫描文件,生成 HMAC 签名引用,并把 task snapshot 与 OpenClaw 原生 task/session 状态对齐
### 入口模块
| 入口 | 路径 | 作用 |
|------|------|------|
| plugin entry | `index.ts` | register() — 注册 5 个网关方法 + 2 个 agent 工具 |
| plugin entry | `index.ts` | register() — 注册 task/artifact 网关方法 + artifact agent 工具 |
| artifacts | `src/exportArtifacts.ts` | 工件准备/导出/读取逻辑 |
| bridge agents | `src/bridgeAgents.ts` | 通过 HTTP 调用 bridge 的 session.start |
| manifest | `openclaw.plugin.json` | 声明网关方法和工具配置 |
### 关键目录
```
src/
exportArtifacts.ts — 核心: prepare/export/list/read
bridgeAgents.ts — bridge 调用: session.start (multi-agent)
taskState.ts — task snapshot + session key mapping adapter
index.ts — 插件入口: register()
openclaw.plugin.json — 插件清单: 网关方法 + 工具声明
```
@ -143,19 +145,16 @@ openclaw.plugin.json — 插件清单: 网关方法 + 工具声明
### 主要调用链
```
OpenClaw 网关 (WebSocket RPC)
→ xworkmate.tasks.get → 原生 task registry 状态 + artifact manifest
→ xworkmate.artifacts.prepare → 创建 tasks/<session>/<run>/ 目录
→ xworkmate.artifacts.export → 扫描文件, 计算 HMAC 签名, 输出清单
→ xworkmate.artifacts.collect-and-snapshot → 收集 tool 输出并绑定到当前 artifact scope
→ xworkmate.artifacts.read → 返回单个工件内容
Agent Tool Call (内部)
→ openclaw_multi_session_agents
→ bridgeAgents.run() → HTTP POST → bridge /acp/rpc (session.start, multiAgent: true)
→ 结果写入 artifactDirectory → multi-agent-result.json
```
### 与其他仓库的关系
- **xworkmate-bridge**: 被 bridge 通过网关 RPC 调用(`xworkmate.artifacts.*`)。也主动调用 bridge 的 `/acp/rpc`multi-agent 模式)。
- **xworkmate-bridge**: 被 bridge 通过 OpenClaw 网关 RPC 间接调用(`xworkmate.tasks.get`, `xworkmate.artifacts.*`)。
- **xworkmate-app**: 不直接通信。app 通过 bridge 的 `/artifacts/openclaw/download` 获取工件。
### 技术栈
TypeScript (ESM), Node.js, OpenClaw Plugin SDK (2026.5.28)。无外部 HTTP 依赖。`bridgeAgents.ts` 中使用原生 `fetch`
TypeScript (ESM), Node.js, OpenClaw Plugin SDK。无外部 HTTP 依赖。

View File

@ -131,6 +131,7 @@ Inputs:
artifactRef?: string // alternative: read single artifact
maxFiles?: number // default: 200
maxInlineBytes?: number // default: 512KB, files larger are omitted
expectedArtifactDirs?: string[] // from session.start metadata.xworkmateTaskArtifactContract only
Process:
1. resolveScopeRoot(workspaceRoot, artifactScope)
@ -144,6 +145,16 @@ Process:
→ Skip: symlinks (security measure)
→ Apply: artifact-ignore.md rules
2b. If the task scope has no candidates and `expectedArtifactDirs` is present:
→ Scan only those explicit workspace-root subdirectories
→ Keep exported entries bound to the current task artifactScope
→ Do not scan the workspace root broadly and do not borrow older task scopes
Protocol boundary:
`expectedArtifactDirs` is bridge artifact-contract data, not agent execution
data. Bridge must not put it in `chat.send` params. Bridge must not probe old
root-level or metadata-root compatibility keys.
3. For each file under maxFiles limit:
→ Read content (up to maxInlineBytes)
→ Compute SHA-256 hash

View File

@ -76,7 +76,8 @@ xworkmate-bridge
│ └─ scope: tasks/<sessionKey>/<runId>/
├─ gateway.request('chat.send')
│ └─ payload: message, attachments, artifactSince, workingDirectory
│ └─ payload: sessionKey, message, attachments, idempotencyKey
│ (no expectedArtifactDirs root field)
├─ Create OpenClawTaskRecord
│ ├─ SessionID, ThreadID, TurnID, RunID
@ -135,6 +136,17 @@ openclaw-multi-session-plugins
├─ signArtifactRef(sessionKey, runId, relativePath)
└─ Return manifest + base64 file contents
`expectedArtifactDirs` source:
session.start.metadata.xworkmateTaskArtifactContract.expectedArtifactDirs
→ bridge artifact contract
→ xworkmate.artifacts.collect-and-snapshot / export only
Forbidden compatibility paths:
session.start.expectedArtifactDirs
session.start.metadata.expectedArtifactDirs
chat.send.expectedArtifactDirs
xworkmate.tasks.get.expectedArtifactDirs
Receives gateway RPC: xworkmate.artifacts.collect-and-snapshot
collectAndSnapshotXWorkmateArtifacts()
├─ Scan ~/.openclaw/media/ and /tmp/openclaw/ for files changed since task start
@ -207,8 +219,8 @@ etc.), not by extending the task execution lifecycle.
### openclaw-multi-session-plugins
- `src/exportArtifacts.ts` — artifact prepare/export/read (963 lines)
- `src/taskState.ts` — task snapshot adapter + SessionEntry.pluginExtensions mapping
- `index.ts` — plugin entry + gateway method registration
- `src/bridgeAgents.ts` — bridge agent orchestration
### openclaw.svc.plus (reference)
- `src/config/paths.ts` — ~/.openclaw/ state directory