@f2a/network 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/.github/workflows/ci.yml +113 -0
  2. package/.github/workflows/publish.yml +60 -0
  3. package/LICENSE +21 -0
  4. package/MONOREPO.md +58 -0
  5. package/README.md +280 -0
  6. package/SKILL.md +137 -0
  7. package/dist/adapters/openclaw.d.ts +103 -0
  8. package/dist/adapters/openclaw.d.ts.map +1 -0
  9. package/dist/adapters/openclaw.js +297 -0
  10. package/dist/adapters/openclaw.js.map +1 -0
  11. package/dist/cli/commands.d.ts +17 -0
  12. package/dist/cli/commands.d.ts.map +1 -0
  13. package/dist/cli/commands.js +107 -0
  14. package/dist/cli/commands.js.map +1 -0
  15. package/dist/cli/index.d.ts +6 -0
  16. package/dist/cli/index.d.ts.map +1 -0
  17. package/dist/cli/index.js +203 -0
  18. package/dist/cli/index.js.map +1 -0
  19. package/dist/core/autonomous-economy.d.ts +136 -0
  20. package/dist/core/autonomous-economy.d.ts.map +1 -0
  21. package/dist/core/autonomous-economy.js +255 -0
  22. package/dist/core/autonomous-economy.js.map +1 -0
  23. package/dist/core/connection-manager.d.ts +80 -0
  24. package/dist/core/connection-manager.d.ts.map +1 -0
  25. package/dist/core/connection-manager.js +235 -0
  26. package/dist/core/connection-manager.js.map +1 -0
  27. package/dist/core/connection-manager.test.d.ts +2 -0
  28. package/dist/core/connection-manager.test.d.ts.map +1 -0
  29. package/dist/core/connection-manager.test.js +52 -0
  30. package/dist/core/connection-manager.test.js.map +1 -0
  31. package/dist/core/e2ee-crypto.d.ts +90 -0
  32. package/dist/core/e2ee-crypto.d.ts.map +1 -0
  33. package/dist/core/e2ee-crypto.js +190 -0
  34. package/dist/core/e2ee-crypto.js.map +1 -0
  35. package/dist/core/f2a.d.ts +126 -0
  36. package/dist/core/f2a.d.ts.map +1 -0
  37. package/dist/core/f2a.js +425 -0
  38. package/dist/core/f2a.js.map +1 -0
  39. package/dist/core/identity.d.ts +47 -0
  40. package/dist/core/identity.d.ts.map +1 -0
  41. package/dist/core/identity.js +130 -0
  42. package/dist/core/identity.js.map +1 -0
  43. package/dist/core/identity.test.d.ts +2 -0
  44. package/dist/core/identity.test.d.ts.map +1 -0
  45. package/dist/core/identity.test.js +43 -0
  46. package/dist/core/identity.test.js.map +1 -0
  47. package/dist/core/p2p-network.d.ts +242 -0
  48. package/dist/core/p2p-network.d.ts.map +1 -0
  49. package/dist/core/p2p-network.js +1182 -0
  50. package/dist/core/p2p-network.js.map +1 -0
  51. package/dist/core/reputation-security.d.ts +168 -0
  52. package/dist/core/reputation-security.d.ts.map +1 -0
  53. package/dist/core/reputation-security.js +369 -0
  54. package/dist/core/reputation-security.js.map +1 -0
  55. package/dist/core/reputation.d.ts +179 -0
  56. package/dist/core/reputation.d.ts.map +1 -0
  57. package/dist/core/reputation.js +472 -0
  58. package/dist/core/reputation.js.map +1 -0
  59. package/dist/core/review-committee.d.ts +130 -0
  60. package/dist/core/review-committee.d.ts.map +1 -0
  61. package/dist/core/review-committee.js +251 -0
  62. package/dist/core/review-committee.js.map +1 -0
  63. package/dist/core/serverless.d.ts +155 -0
  64. package/dist/core/serverless.d.ts.map +1 -0
  65. package/dist/core/serverless.js +615 -0
  66. package/dist/core/serverless.js.map +1 -0
  67. package/dist/core/token-manager.d.ts +42 -0
  68. package/dist/core/token-manager.d.ts.map +1 -0
  69. package/dist/core/token-manager.js +122 -0
  70. package/dist/core/token-manager.js.map +1 -0
  71. package/dist/daemon/control-server.d.ts +55 -0
  72. package/dist/daemon/control-server.d.ts.map +1 -0
  73. package/dist/daemon/control-server.js +262 -0
  74. package/dist/daemon/control-server.js.map +1 -0
  75. package/dist/daemon/index.d.ts +35 -0
  76. package/dist/daemon/index.d.ts.map +1 -0
  77. package/dist/daemon/index.js +69 -0
  78. package/dist/daemon/index.js.map +1 -0
  79. package/dist/daemon/main.d.ts +6 -0
  80. package/dist/daemon/main.d.ts.map +1 -0
  81. package/dist/daemon/main.js +38 -0
  82. package/dist/daemon/main.js.map +1 -0
  83. package/dist/daemon/start.d.ts +6 -0
  84. package/dist/daemon/start.d.ts.map +1 -0
  85. package/dist/daemon/start.js +25 -0
  86. package/dist/daemon/start.js.map +1 -0
  87. package/dist/daemon/webhook.d.ts +30 -0
  88. package/dist/daemon/webhook.d.ts.map +1 -0
  89. package/dist/daemon/webhook.js +86 -0
  90. package/dist/daemon/webhook.js.map +1 -0
  91. package/dist/daemon/webhook.test.d.ts +2 -0
  92. package/dist/daemon/webhook.test.d.ts.map +1 -0
  93. package/dist/daemon/webhook.test.js +24 -0
  94. package/dist/daemon/webhook.test.js.map +1 -0
  95. package/dist/index.d.ts +24 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +25 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/protocol/messages.d.ts +739 -0
  100. package/dist/protocol/messages.d.ts.map +1 -0
  101. package/dist/protocol/messages.js +188 -0
  102. package/dist/protocol/messages.js.map +1 -0
  103. package/dist/protocol/messages.test.d.ts +2 -0
  104. package/dist/protocol/messages.test.d.ts.map +1 -0
  105. package/dist/protocol/messages.test.js +55 -0
  106. package/dist/protocol/messages.test.js.map +1 -0
  107. package/dist/types/index.d.ts +247 -0
  108. package/dist/types/index.d.ts.map +1 -0
  109. package/dist/types/index.js +10 -0
  110. package/dist/types/index.js.map +1 -0
  111. package/dist/types/result.d.ts +28 -0
  112. package/dist/types/result.d.ts.map +1 -0
  113. package/dist/types/result.js +16 -0
  114. package/dist/types/result.js.map +1 -0
  115. package/dist/utils/benchmark.d.ts +67 -0
  116. package/dist/utils/benchmark.d.ts.map +1 -0
  117. package/dist/utils/benchmark.js +179 -0
  118. package/dist/utils/benchmark.js.map +1 -0
  119. package/dist/utils/logger.d.ts +105 -0
  120. package/dist/utils/logger.d.ts.map +1 -0
  121. package/dist/utils/logger.js +275 -0
  122. package/dist/utils/logger.js.map +1 -0
  123. package/dist/utils/middleware.d.ts +85 -0
  124. package/dist/utils/middleware.d.ts.map +1 -0
  125. package/dist/utils/middleware.js +173 -0
  126. package/dist/utils/middleware.js.map +1 -0
  127. package/dist/utils/rate-limiter.d.ts +71 -0
  128. package/dist/utils/rate-limiter.d.ts.map +1 -0
  129. package/dist/utils/rate-limiter.js +160 -0
  130. package/dist/utils/rate-limiter.js.map +1 -0
  131. package/dist/utils/signature.d.ts +57 -0
  132. package/dist/utils/signature.d.ts.map +1 -0
  133. package/dist/utils/signature.js +102 -0
  134. package/dist/utils/signature.js.map +1 -0
  135. package/dist/utils/validation.d.ts +504 -0
  136. package/dist/utils/validation.d.ts.map +1 -0
  137. package/dist/utils/validation.js +159 -0
  138. package/dist/utils/validation.js.map +1 -0
  139. package/docs/F2A-PROTOCOL.md +61 -0
  140. package/docs/MOBILE_BOOTSTRAP_DESIGN.md +126 -0
  141. package/docs/a2a-lessons.md +316 -0
  142. package/docs/middleware-guide.md +448 -0
  143. package/docs/readme-update-checklist.md +90 -0
  144. package/docs/reputation-guide.md +396 -0
  145. package/docs/rfcs/001-reputation-system.md +712 -0
  146. package/docs/security-design.md +247 -0
  147. package/install.sh +231 -0
  148. package/package.json +64 -0
  149. package/packages/openclaw-adapter/README.md +510 -0
  150. package/packages/openclaw-adapter/openclaw.plugin.json +106 -0
  151. package/packages/openclaw-adapter/package.json +40 -0
  152. package/packages/openclaw-adapter/src/announcement-queue.test.ts +449 -0
  153. package/packages/openclaw-adapter/src/announcement-queue.ts +403 -0
  154. package/packages/openclaw-adapter/src/capability-detector.test.ts +99 -0
  155. package/packages/openclaw-adapter/src/capability-detector.ts +183 -0
  156. package/packages/openclaw-adapter/src/claim-handlers.test.ts +974 -0
  157. package/packages/openclaw-adapter/src/claim-handlers.ts +482 -0
  158. package/packages/openclaw-adapter/src/connector.business.test.ts +583 -0
  159. package/packages/openclaw-adapter/src/connector.ts +795 -0
  160. package/packages/openclaw-adapter/src/index.test.ts +82 -0
  161. package/packages/openclaw-adapter/src/index.ts +18 -0
  162. package/packages/openclaw-adapter/src/integration.e2e.test.ts +829 -0
  163. package/packages/openclaw-adapter/src/logger.ts +51 -0
  164. package/packages/openclaw-adapter/src/network-client.test.ts +266 -0
  165. package/packages/openclaw-adapter/src/network-client.ts +251 -0
  166. package/packages/openclaw-adapter/src/network-recovery.test.ts +465 -0
  167. package/packages/openclaw-adapter/src/node-manager.test.ts +136 -0
  168. package/packages/openclaw-adapter/src/node-manager.ts +429 -0
  169. package/packages/openclaw-adapter/src/plugin.test.ts +439 -0
  170. package/packages/openclaw-adapter/src/plugin.ts +104 -0
  171. package/packages/openclaw-adapter/src/reputation.test.ts +221 -0
  172. package/packages/openclaw-adapter/src/reputation.ts +368 -0
  173. package/packages/openclaw-adapter/src/task-guard.test.ts +502 -0
  174. package/packages/openclaw-adapter/src/task-guard.ts +860 -0
  175. package/packages/openclaw-adapter/src/task-queue.concurrency.test.ts +462 -0
  176. package/packages/openclaw-adapter/src/task-queue.edge-cases.test.ts +284 -0
  177. package/packages/openclaw-adapter/src/task-queue.persistence.test.ts +408 -0
  178. package/packages/openclaw-adapter/src/task-queue.ts +668 -0
  179. package/packages/openclaw-adapter/src/tool-handlers.test.ts +906 -0
  180. package/packages/openclaw-adapter/src/tool-handlers.ts +574 -0
  181. package/packages/openclaw-adapter/src/types.ts +361 -0
  182. package/packages/openclaw-adapter/src/webhook-pusher.test.ts +188 -0
  183. package/packages/openclaw-adapter/src/webhook-pusher.ts +220 -0
  184. package/packages/openclaw-adapter/src/webhook-server.test.ts +580 -0
  185. package/packages/openclaw-adapter/src/webhook-server.ts +202 -0
  186. package/packages/openclaw-adapter/tsconfig.json +20 -0
  187. package/src/cli/commands.test.ts +157 -0
  188. package/src/cli/commands.ts +129 -0
  189. package/src/cli/index.test.ts +77 -0
  190. package/src/cli/index.ts +234 -0
  191. package/src/core/autonomous-economy.test.ts +291 -0
  192. package/src/core/autonomous-economy.ts +428 -0
  193. package/src/core/e2ee-crypto.test.ts +125 -0
  194. package/src/core/e2ee-crypto.ts +246 -0
  195. package/src/core/f2a.test.ts +269 -0
  196. package/src/core/f2a.ts +618 -0
  197. package/src/core/p2p-network.test.ts +199 -0
  198. package/src/core/p2p-network.ts +1432 -0
  199. package/src/core/reputation-security.test.ts +403 -0
  200. package/src/core/reputation-security.ts +562 -0
  201. package/src/core/reputation.test.ts +260 -0
  202. package/src/core/reputation.ts +576 -0
  203. package/src/core/review-committee.test.ts +380 -0
  204. package/src/core/review-committee.ts +401 -0
  205. package/src/core/token-manager.test.ts +133 -0
  206. package/src/core/token-manager.ts +140 -0
  207. package/src/daemon/control-server.test.ts +216 -0
  208. package/src/daemon/control-server.ts +292 -0
  209. package/src/daemon/index.test.ts +85 -0
  210. package/src/daemon/index.ts +89 -0
  211. package/src/daemon/main.ts +44 -0
  212. package/src/daemon/start.ts +29 -0
  213. package/src/daemon/webhook.test.ts +68 -0
  214. package/src/daemon/webhook.ts +105 -0
  215. package/src/index.test.ts +436 -0
  216. package/src/index.ts +72 -0
  217. package/src/types/index.test.ts +87 -0
  218. package/src/types/index.ts +341 -0
  219. package/src/types/result.ts +68 -0
  220. package/src/utils/benchmark.ts +237 -0
  221. package/src/utils/logger.ts +331 -0
  222. package/src/utils/middleware.ts +229 -0
  223. package/src/utils/rate-limiter.ts +207 -0
  224. package/src/utils/signature.ts +136 -0
  225. package/src/utils/validation.ts +186 -0
  226. package/tests/docker/Dockerfile.node +23 -0
  227. package/tests/docker/Dockerfile.runner +18 -0
  228. package/tests/docker/docker-compose.test.yml +73 -0
  229. package/tests/integration/message-passing.test.ts +109 -0
  230. package/tests/integration/multi-node.test.ts +92 -0
  231. package/tests/integration/p2p-connection.test.ts +83 -0
  232. package/tests/integration/test-config.ts +32 -0
  233. package/tsconfig.json +21 -0
  234. package/vitest.config.ts +26 -0
@@ -0,0 +1,618 @@
1
+ /**
2
+ * F2A 主类 - P2P 版本
3
+ * 整合 P2P 网络、能力发现与任务委托
4
+ */
5
+
6
+ import { EventEmitter } from 'eventemitter3';
7
+ import { P2PNetwork } from './p2p-network.js';
8
+ import { Logger } from '../utils/logger.js';
9
+ import { Middleware } from '../utils/middleware.js';
10
+ import { validateAgentCapability, validateTaskDelegateOptions } from '../utils/validation.js';
11
+ import {
12
+ F2AOptions,
13
+ F2AEvents,
14
+ AgentInfo,
15
+ AgentCapability,
16
+ Result,
17
+ TaskDelegateOptions,
18
+ TaskDelegateResult,
19
+ TaskRequestEvent,
20
+ TaskResponseEvent,
21
+ PeerDiscoveredEvent,
22
+ PeerConnectedEvent,
23
+ PeerDisconnectedEvent,
24
+ RegisteredCapability,
25
+ NetworkStartedEvent,
26
+ success,
27
+ failureFromError
28
+ } from '../types/index.js';
29
+
30
+ // 版本号
31
+ const F2A_VERSION = '1.0.0';
32
+ const PROTOCOL_VERSION = 'f2a/1.0';
33
+
34
+ export interface F2AInstance {
35
+ readonly peerId: string;
36
+ /** 获取 Agent 信息(延迟获取,确保 peerId 在 start() 后才有效) */
37
+ readonly agentInfo: AgentInfo;
38
+ start(): Promise<Result<void>>;
39
+ stop(): Promise<void>;
40
+
41
+ // 能力管理
42
+ registerCapability(capability: AgentCapability, handler: (params: Record<string, unknown>) => Promise<unknown>): void;
43
+ getCapabilities(): AgentCapability[];
44
+
45
+ // 发现
46
+ discoverAgents(capability?: string): Promise<AgentInfo[]>;
47
+ getConnectedPeers(): AgentInfo[];
48
+ getAllPeers(): AgentInfo[];
49
+
50
+ // 任务委托
51
+ delegateTask(options: TaskDelegateOptions): Promise<Result<TaskDelegateResult>>;
52
+
53
+ // 直接通信
54
+ sendTaskTo(peerId: string, taskType: string, description: string, parameters?: Record<string, unknown>): Promise<Result<unknown>>;
55
+
56
+ // 中间件
57
+ useMiddleware(middleware: Middleware): void;
58
+ removeMiddleware(name: string): boolean;
59
+ listMiddlewares(): string[];
60
+
61
+ // DHT
62
+ findPeerViaDHT(peerId: string): Promise<Result<string[]>>;
63
+ getDHTPeerCount(): number;
64
+ isDHTEnabled(): boolean;
65
+ }
66
+
67
+ export class F2A extends EventEmitter<F2AEvents> implements F2AInstance {
68
+ private _agentInfo: AgentInfo;
69
+ private p2pNetwork: P2PNetwork;
70
+ private options: Required<F2AOptions>;
71
+ private running: boolean = false;
72
+ private registeredCapabilities: Map<string, RegisteredCapability> = new Map();
73
+ private logger: Logger;
74
+
75
+ private constructor(
76
+ agentInfo: AgentInfo,
77
+ p2pNetwork: P2PNetwork,
78
+ options: Required<F2AOptions>
79
+ ) {
80
+ super();
81
+ this._agentInfo = agentInfo;
82
+ this.p2pNetwork = p2pNetwork;
83
+ this.options = options;
84
+ this.logger = new Logger({ level: options.logLevel, component: 'F2A' });
85
+
86
+ this.bindEvents();
87
+ }
88
+
89
+ /**
90
+ * 获取 Agent 信息
91
+ * 使用 getter 延迟获取 peerId,避免在 start() 前读到空值
92
+ */
93
+ get agentInfo(): AgentInfo {
94
+ // 返回一个代理对象,确保 peerId 始终从 p2pNetwork 获取最新值
95
+ return {
96
+ ...this._agentInfo,
97
+ peerId: this.running ? this._agentInfo.peerId : ''
98
+ };
99
+ }
100
+
101
+ /**
102
+ * 工厂方法:创建 F2A 实例
103
+ */
104
+ static async create(options: F2AOptions = {}): Promise<F2A> {
105
+ // 默认配置
106
+ const mergedOptions: Required<F2AOptions> = {
107
+ displayName: options.displayName || 'F2A Agent',
108
+ agentType: options.agentType || 'openclaw',
109
+ network: {
110
+ listenPort: 0,
111
+ enableMDNS: true,
112
+ enableDHT: false,
113
+ ...options.network
114
+ },
115
+ security: {
116
+ level: 'medium',
117
+ requireConfirmation: true,
118
+ verifySignatures: true,
119
+ ...options.security
120
+ },
121
+ logLevel: options.logLevel || 'INFO',
122
+ dataDir: options.dataDir || './f2a-data'
123
+ };
124
+
125
+ // 创建 AgentInfo
126
+ const agentInfo: AgentInfo = {
127
+ peerId: '', // 启动后由 P2P 网络填充
128
+ displayName: mergedOptions.displayName,
129
+ agentType: mergedOptions.agentType as AgentInfo['agentType'],
130
+ version: F2A_VERSION,
131
+ capabilities: [],
132
+ protocolVersion: PROTOCOL_VERSION,
133
+ lastSeen: Date.now(),
134
+ multiaddrs: []
135
+ };
136
+
137
+ // 创建 P2P 网络
138
+ const p2pNetwork = new P2PNetwork(agentInfo, mergedOptions.network);
139
+
140
+ // 创建实例
141
+ const f2a = new F2A(agentInfo, p2pNetwork, mergedOptions);
142
+
143
+ return f2a;
144
+ }
145
+
146
+ /**
147
+ * 启动 F2A
148
+ */
149
+ async start(): Promise<Result<void>> {
150
+ if (this.running) {
151
+ return failureFromError('NETWORK_ALREADY_RUNNING', 'F2A already running');
152
+ }
153
+
154
+ this.logger.info('Starting agent', { displayName: this.agentInfo.displayName });
155
+
156
+ // 启动 P2P 网络
157
+ const result = await this.p2pNetwork.start();
158
+ if (!result.success) {
159
+ return result;
160
+ }
161
+
162
+ // 更新 agentInfo
163
+ this._agentInfo.peerId = result.data.peerId;
164
+ this._agentInfo.multiaddrs = result.data.addresses;
165
+
166
+ this.running = true;
167
+
168
+ this.emit('network:started', {
169
+ peerId: result.data.peerId,
170
+ listenAddresses: result.data.addresses
171
+ });
172
+
173
+ this.logger.info('Started', { peerId: result.data.peerId.slice(0, 16) });
174
+
175
+ return { success: true, data: undefined };
176
+ }
177
+
178
+ /**
179
+ * 停止 F2A
180
+ */
181
+ async stop(): Promise<void> {
182
+ if (!this.running) return;
183
+
184
+ this.logger.info('Stopping');
185
+
186
+ await this.p2pNetwork.stop();
187
+
188
+ this.running = false;
189
+ this.emit('network:stopped');
190
+
191
+ this.logger.info('Stopped');
192
+ }
193
+
194
+ /**
195
+ * 注册能力
196
+ */
197
+ registerCapability(
198
+ capability: AgentCapability,
199
+ handler: (params: Record<string, unknown>) => Promise<unknown>
200
+ ): void {
201
+ // 验证能力定义
202
+ const validation = validateAgentCapability(capability);
203
+ if (!validation.success) {
204
+ this.logger.error('Invalid capability definition', {
205
+ errors: validation.error.errors
206
+ });
207
+ throw new Error(`Invalid capability: ${validation.error.errors.map(e => e.message).join(', ')}`);
208
+ }
209
+
210
+ this.registeredCapabilities.set(capability.name, {
211
+ ...capability,
212
+ handler
213
+ });
214
+
215
+ // 更新 agentInfo
216
+ this.updateAgentCapabilities();
217
+
218
+ this.logger.info('Registered capability', { name: capability.name });
219
+ }
220
+
221
+ /**
222
+ * 获取已注册的能力
223
+ */
224
+ getCapabilities(): AgentCapability[] {
225
+ return Array.from(this.registeredCapabilities.values()).map(c => ({
226
+ name: c.name,
227
+ description: c.description,
228
+ tools: c.tools,
229
+ parameters: c.parameters
230
+ }));
231
+ }
232
+
233
+ /**
234
+ * 发现网络中的 Agents
235
+ */
236
+ async discoverAgents(capability?: string): Promise<AgentInfo[]> {
237
+ return this.p2pNetwork.discoverAgents(capability);
238
+ }
239
+
240
+ /**
241
+ * 获取已连接的 Peers
242
+ */
243
+ getConnectedPeers(): AgentInfo[] {
244
+ return this.p2pNetwork.getConnectedPeers()
245
+ .filter(p => p.agentInfo)
246
+ .map(p => p.agentInfo!);
247
+ }
248
+
249
+ /**
250
+ * 获取所有已知的 Peers(包括已断开但已发现的)
251
+ */
252
+ getAllPeers(): AgentInfo[] {
253
+ // 返回所有已知节点,包括还没有交换 agentInfo 的
254
+ // 如果 agentInfo 不存在,创建一个基本的 AgentInfo
255
+ return this.p2pNetwork.getAllPeers()
256
+ .map(p => {
257
+ if (p.agentInfo) {
258
+ return p.agentInfo;
259
+ }
260
+ // 创建基本的 AgentInfo
261
+ return {
262
+ peerId: p.peerId,
263
+ capabilities: [],
264
+ multiaddrs: p.multiaddrs.map(m => m.toString()),
265
+ lastSeen: p.lastSeen,
266
+ agentType: 'custom' as const,
267
+ version: '0.0.0',
268
+ protocolVersion: '1.0.0'
269
+ };
270
+ });
271
+ }
272
+
273
+ /**
274
+ * 委托任务给网络
275
+ */
276
+ async delegateTask(options: TaskDelegateOptions): Promise<Result<TaskDelegateResult>> {
277
+ // 验证任务委托选项
278
+ const validation = validateTaskDelegateOptions(options);
279
+ if (!validation.success) {
280
+ this.logger.error('Invalid task delegate options', {
281
+ errors: validation.error.errors
282
+ });
283
+ return failureFromError(
284
+ 'INVALID_OPTIONS',
285
+ `Invalid options: ${validation.error.errors.map(e => e.message).join(', ')}`
286
+ );
287
+ }
288
+
289
+ const taskId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
290
+
291
+ this.logger.info('Delegating task', {
292
+ taskId,
293
+ capability: options.capability,
294
+ description: options.description.slice(0, 50)
295
+ });
296
+
297
+ // 可配置的重试选项
298
+ const retryOptions = {
299
+ maxRetries: options.retryOptions?.maxRetries ?? 3,
300
+ retryDelayMs: options.retryOptions?.retryDelayMs ?? 1000,
301
+ discoverTimeoutMs: options.retryOptions?.discoverTimeoutMs ?? 5000
302
+ };
303
+
304
+ // 1. 发现有能力执行任务的 Agents(带重试)
305
+ let agents: AgentInfo[] = [];
306
+ let lastError: string | undefined;
307
+
308
+ for (let attempt = 0; attempt <= retryOptions.maxRetries; attempt++) {
309
+ agents = await this.discoverAgents(options.capability);
310
+
311
+ if (agents.length > 0) {
312
+ break;
313
+ }
314
+
315
+ if (attempt < retryOptions.maxRetries) {
316
+ this.logger.warn(`No agents found, retrying (${attempt + 1}/${retryOptions.maxRetries})`, {
317
+ capability: options.capability
318
+ });
319
+ await new Promise(resolve => setTimeout(resolve, retryOptions.retryDelayMs));
320
+ }
321
+ }
322
+
323
+ if (agents.length === 0) {
324
+ this.logger.warn('No agents found with capability after retries', {
325
+ capability: options.capability,
326
+ retries: retryOptions.maxRetries
327
+ });
328
+ return failureFromError(
329
+ 'CAPABILITY_NOT_SUPPORTED',
330
+ `No agent found with capability: ${options.capability} (after ${retryOptions.maxRetries} retries)`
331
+ );
332
+ }
333
+
334
+ this.logger.info('Found agents with capability', {
335
+ count: agents.length,
336
+ capability: options.capability
337
+ });
338
+
339
+ // 2. 发送任务请求
340
+ const timeout = options.timeout || 30000;
341
+ const results: TaskDelegateResult['results'] = [];
342
+
343
+ if (options.parallel) {
344
+ // 并行发送给多个 Agents
345
+ const minResponses = options.minResponses || 1;
346
+
347
+ const promises = agents.map(async (agent) => {
348
+ const startTime = Date.now();
349
+ const result = await this.p2pNetwork.sendTaskRequest(
350
+ agent.peerId,
351
+ options.capability,
352
+ options.description,
353
+ options.parameters,
354
+ timeout
355
+ );
356
+ const latency = Date.now() - startTime;
357
+
358
+ return {
359
+ peerId: agent.peerId,
360
+ status: result.success ? 'success' as const : 'error' as const,
361
+ result: result.success ? result.data : undefined,
362
+ error: result.success ? undefined : (result.error?.message || String(result.error)),
363
+ latency
364
+ };
365
+ });
366
+
367
+ // 等待至少 minResponses 个响应
368
+ const settled = await Promise.allSettled(promises);
369
+
370
+ for (const outcome of settled) {
371
+ if (outcome.status === 'fulfilled') {
372
+ results.push(outcome.value);
373
+ }
374
+ }
375
+
376
+ // 检查是否达到最小响应数
377
+ const successCount = results.filter(r => r.status === 'success').length;
378
+ if (successCount < minResponses) {
379
+ return failureFromError(
380
+ 'TASK_FAILED',
381
+ `Only ${successCount} successful responses, required ${minResponses}`
382
+ );
383
+ }
384
+ } else {
385
+ // 串行发送,取第一个成功的
386
+ for (const agent of agents) {
387
+ const startTime = Date.now();
388
+ const result = await this.p2pNetwork.sendTaskRequest(
389
+ agent.peerId,
390
+ options.capability,
391
+ options.description,
392
+ options.parameters,
393
+ timeout
394
+ );
395
+ const latency = Date.now() - startTime;
396
+
397
+ results.push({
398
+ peerId: agent.peerId,
399
+ status: result.success ? 'success' : 'error',
400
+ result: result.success ? result.data : undefined,
401
+ error: result.success ? undefined : (result.error?.message || String(result.error)),
402
+ latency
403
+ });
404
+
405
+ if (result.success) {
406
+ break; // 第一个成功就停止
407
+ }
408
+ }
409
+
410
+ // 检查是否有成功结果
411
+ if (!results.some(r => r.status === 'success')) {
412
+ return failureFromError('TASK_FAILED', 'All agents failed to execute the task');
413
+ }
414
+ }
415
+
416
+ return success({ taskId, results });
417
+ }
418
+
419
+ /**
420
+ * 直接发送任务给特定 Peer
421
+ */
422
+ async sendTaskTo(
423
+ peerId: string,
424
+ taskType: string,
425
+ description: string,
426
+ parameters?: Record<string, unknown>
427
+ ): Promise<Result<unknown>> {
428
+ return this.p2pNetwork.sendTaskRequest(
429
+ peerId,
430
+ taskType,
431
+ description,
432
+ parameters,
433
+ 30000
434
+ );
435
+ }
436
+
437
+ /**
438
+ * 注册中间件
439
+ */
440
+ useMiddleware(middleware: Middleware): void {
441
+ this.p2pNetwork.useMiddleware(middleware);
442
+ }
443
+
444
+ /**
445
+ * 移除中间件
446
+ */
447
+ removeMiddleware(name: string): boolean {
448
+ return this.p2pNetwork.removeMiddleware(name);
449
+ }
450
+
451
+ /**
452
+ * 获取已注册中间件列表
453
+ */
454
+ listMiddlewares(): string[] {
455
+ return this.p2pNetwork.listMiddlewares();
456
+ }
457
+
458
+ /**
459
+ * 通过 DHT 查找节点
460
+ */
461
+ async findPeerViaDHT(peerId: string): Promise<Result<string[]>> {
462
+ return this.p2pNetwork.findPeerViaDHT(peerId);
463
+ }
464
+
465
+ /**
466
+ * 获取 DHT 路由表大小
467
+ */
468
+ getDHTPeerCount(): number {
469
+ return this.p2pNetwork.getDHTPeerCount();
470
+ }
471
+
472
+ /**
473
+ * 检查 DHT 是否启用
474
+ */
475
+ isDHTEnabled(): boolean {
476
+ return this.p2pNetwork.isDHTEnabled();
477
+ }
478
+
479
+ /**
480
+ * 获取 PeerID
481
+ */
482
+ get peerId(): string {
483
+ return this.agentInfo.peerId;
484
+ }
485
+
486
+ /**
487
+ * 绑定内部事件
488
+ */
489
+ private bindEvents(): void {
490
+ // 转发 P2P 网络事件
491
+ this.p2pNetwork.on('peer:discovered', (event) => {
492
+ this.emit('peer:discovered', event);
493
+ });
494
+
495
+ this.p2pNetwork.on('peer:connected', (event) => {
496
+ this.emit('peer:connected', event);
497
+ });
498
+
499
+ this.p2pNetwork.on('peer:disconnected', (event) => {
500
+ this.emit('peer:disconnected', event);
501
+ });
502
+
503
+ // 处理收到的任务请求
504
+ this.p2pNetwork.on('message:received', async (message, peerId) => {
505
+ if (message.type === 'TASK_REQUEST') {
506
+ await this.handleTaskRequest(message.payload as TaskRequestEvent, peerId);
507
+ }
508
+ });
509
+
510
+ this.p2pNetwork.on('error', (error) => {
511
+ this.emit('error', error);
512
+ });
513
+ }
514
+
515
+ /**
516
+ * 处理收到的任务请求
517
+ */
518
+ private async handleTaskRequest(
519
+ request: TaskRequestEvent,
520
+ fromPeerId: string
521
+ ): Promise<void> {
522
+ this.logger.info('Received task request', {
523
+ fromPeerId: fromPeerId.slice(0, 16),
524
+ taskType: request.taskType,
525
+ taskId: request.taskId
526
+ });
527
+
528
+ // 查找对应的能力处理器
529
+ const capability = this.registeredCapabilities.get(request.taskType);
530
+
531
+ if (!capability) {
532
+ this.logger.warn('Capability not supported, rejecting task', {
533
+ taskType: request.taskType,
534
+ fromPeerId: fromPeerId.slice(0, 16)
535
+ });
536
+ // 拒绝任务
537
+ await this.p2pNetwork.sendTaskResponse(
538
+ fromPeerId,
539
+ request.taskId,
540
+ 'rejected',
541
+ undefined,
542
+ `Capability not supported: ${request.taskType}`
543
+ );
544
+ return;
545
+ }
546
+
547
+ // 触发事件,让上层(OpenClaw)可以拦截或监控
548
+ this.emit('task:request', request);
549
+
550
+ // 如果有注册 handler,自动执行任务并发送响应
551
+ if (capability.handler) {
552
+ try {
553
+ const result = await capability.handler(request.parameters || {});
554
+ await this.p2pNetwork.sendTaskResponse(
555
+ fromPeerId,
556
+ request.taskId,
557
+ 'success',
558
+ result
559
+ );
560
+ this.logger.info('Task executed successfully', {
561
+ taskId: request.taskId,
562
+ fromPeerId: fromPeerId.slice(0, 16)
563
+ });
564
+ } catch (error) {
565
+ this.logger.error('Task execution failed', {
566
+ taskId: request.taskId,
567
+ error: error instanceof Error ? error.message : String(error)
568
+ });
569
+ await this.p2pNetwork.sendTaskResponse(
570
+ fromPeerId,
571
+ request.taskId,
572
+ 'error',
573
+ undefined,
574
+ error instanceof Error ? error.message : String(error)
575
+ );
576
+ }
577
+ }
578
+ // 如果没有 handler,依赖上层通过 respondToTask 手动响应
579
+ }
580
+
581
+ /**
582
+ * 发送任务响应(供 OpenClaw 调用)
583
+ */
584
+ async respondToTask(
585
+ peerId: string,
586
+ taskId: string,
587
+ status: 'success' | 'error' | 'rejected',
588
+ result?: unknown,
589
+ error?: string
590
+ ): Promise<Result<void>> {
591
+ const responseResult = await this.p2pNetwork.sendTaskResponse(
592
+ peerId,
593
+ taskId,
594
+ status,
595
+ result,
596
+ error
597
+ );
598
+
599
+ if (responseResult.success) {
600
+ this.emit('task:response', {
601
+ taskId,
602
+ from: this.peerId,
603
+ status,
604
+ result,
605
+ error
606
+ });
607
+ }
608
+
609
+ return responseResult;
610
+ }
611
+
612
+ /**
613
+ * 更新 AgentInfo 中的能力列表
614
+ */
615
+ private updateAgentCapabilities(): void {
616
+ this._agentInfo.capabilities = this.getCapabilities();
617
+ }
618
+ }