multiclaws 0.4.36 → 0.4.37

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.
@@ -2,6 +2,48 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OpenClawAgentExecutor = void 0;
4
4
  const gateway_client_1 = require("../infra/gateway-client");
5
+ /* ------------------------------------------------------------------ */
6
+ /* Risk classification */
7
+ /* ------------------------------------------------------------------ */
8
+ /**
9
+ * Heuristic risk classifier. Returns "safe" only when the task is
10
+ * clearly a read-only query; defaults to "risky" for anything ambiguous.
11
+ *
12
+ * This drives the permission gate: risky tasks require explicit human
13
+ * approval before a sub-agent is spawned to execute them.
14
+ */
15
+ function classifyTaskRisk(taskText) {
16
+ const text = taskText.toLowerCase();
17
+ // Explicit risky patterns (write / modify / execute / send)
18
+ const riskyPatterns = [
19
+ // English
20
+ /\b(write|creat|delet|remov|modif|edit|updat|install|execut|deploy|push|commit|send|post|drop|format|rename|overwrite|reset|wipe|destroy|kill|terminat|rm\b|mkdir|touch\b|mv\b)\b/i,
21
+ // Chinese write-oriented verbs
22
+ /[写创建删除修改编辑更新安装执行运行发送提交部署重命名覆盖重置清空销毁终止]/,
23
+ ];
24
+ if (riskyPatterns.some((p) => p.test(text))) {
25
+ return "risky";
26
+ }
27
+ // Explicitly safe read-only patterns
28
+ const safePatterns = [
29
+ /\b(list|show|get|check|view|read|query|find|search|display|fetch|retriev|look|what|which|count|how many|summariz|describ|explain|analyz|report)\b/i,
30
+ /[查看获取搜索显示检查列出查询统计描述分析报告]/,
31
+ // Calendar / schedule queries
32
+ /\b(calendar|schedule|event|meeting|free|busy|availab)\b/i,
33
+ /[日历日程会议空闲忙碌可用时间]/,
34
+ // Process / system info
35
+ /\b(process|pid|cpu|memory|disk|uptime|version|status|running|service)\b/i,
36
+ /[进程内存磁盘状态运行版本服务]/,
37
+ ];
38
+ if (safePatterns.some((p) => p.test(text))) {
39
+ return "safe";
40
+ }
41
+ // Default: treat as risky if uncertain
42
+ return "risky";
43
+ }
44
+ /* ------------------------------------------------------------------ */
45
+ /* Helpers */
46
+ /* ------------------------------------------------------------------ */
5
47
  function extractTextFromMessage(message) {
6
48
  if (!message.parts)
7
49
  return "";
@@ -15,10 +57,13 @@ function extractTextFromMessage(message) {
15
57
  *
16
58
  * When a remote agent sends a task via A2A `message/send`,
17
59
  * this executor:
18
- * 1. Records the task via TaskTracker
19
- * 2. Calls OpenClaw's `sessions_spawn` (run mode) to start execution
20
- * 3. Waits for the sub-agent to call back via `multiclaws_a2a_callback`
21
- * 4. Returns the final result as a Message
60
+ * 1. Classifies the task risk (safe vs risky)
61
+ * 2. Notifies the local human owner
62
+ * 3. For risky tasks: waits for explicit human approval
63
+ * For safe tasks: executes immediately
64
+ * 4. Calls OpenClaw's `sessions_spawn` (run mode) to start execution
65
+ * 5. Waits for the sub-agent to call back via `multiclaws_a2a_callback`
66
+ * 6. Returns the final result as a Message
22
67
  */
23
68
  class OpenClawAgentExecutor {
24
69
  gatewayConfig;
@@ -27,6 +72,7 @@ class OpenClawAgentExecutor {
27
72
  logger;
28
73
  cwd;
29
74
  pendingCallbacks = new Map();
75
+ pendingApprovals = new Map();
30
76
  constructor(options) {
31
77
  this.gatewayConfig = options.gatewayConfig;
32
78
  this.taskTracker = options.taskTracker;
@@ -61,10 +107,49 @@ class OpenClawAgentExecutor {
61
107
  eventBus.finished();
62
108
  return;
63
109
  }
64
- // Notify local user about incoming task
65
- const notifyTargets = this.getNotificationTargets();
66
- this.logger.info(`[a2a-adapter] task ${taskId} notifying user (${notifyTargets.size} targets)`);
67
- void this.notifyUser(`📨 收到来自 **${fromAgentName}** 的委派任务:${taskText.slice(0, 200)}`);
110
+ // Classify risk and gate accordingly
111
+ const risk = classifyTaskRisk(taskText);
112
+ this.logger.info(`[a2a-adapter] task ${taskId} risk=${risk}`);
113
+ if (risk === "risky") {
114
+ // Notify with approval request and wait
115
+ const approvalTimeoutMs = 5 * 60 * 1000; // 5 minutes
116
+ const approvalPromise = this.createApprovalCallback(taskId, approvalTimeoutMs);
117
+ this.logger.info(`[a2a-adapter] task ${taskId} requesting human approval (timeout=${approvalTimeoutMs / 1000}s)`);
118
+ void this.notifyUser(buildApprovalRequest(taskId, fromAgentName, taskText));
119
+ let approved;
120
+ try {
121
+ approved = await approvalPromise;
122
+ }
123
+ catch (err) {
124
+ const isCanceled = err instanceof Error && err.message === "canceled";
125
+ if (isCanceled) {
126
+ // Task was explicitly canceled — use the canonical "canceled" message
127
+ this.logger.info(`[a2a-adapter] task ${taskId} canceled during approval wait`);
128
+ this.taskTracker.update(taskId, { status: "failed", error: "canceled" });
129
+ this.publishMessage(eventBus, "Task was canceled.");
130
+ eventBus.finished();
131
+ return;
132
+ }
133
+ // Approval timed out → auto-reject
134
+ approved = false;
135
+ this.logger.warn(`[a2a-adapter] task ${taskId} approval timed out — auto-rejected`);
136
+ }
137
+ if (!approved) {
138
+ const reason = "用户拒绝或未在超时时间内授权。";
139
+ this.logger.info(`[a2a-adapter] task ${taskId} rejected`);
140
+ this.taskTracker.update(taskId, { status: "failed", error: reason });
141
+ this.publishMessage(eventBus, `任务已被拒绝:${reason}`);
142
+ eventBus.finished();
143
+ return;
144
+ }
145
+ this.logger.info(`[a2a-adapter] task ${taskId} approved by user`);
146
+ void this.notifyUser(`✅ 已授权,开始执行来自 **${fromAgentName}** 的任务…`);
147
+ }
148
+ else {
149
+ // Safe task: notify but auto-execute
150
+ this.logger.info(`[a2a-adapter] task ${taskId} safe query — auto-executing`);
151
+ void this.notifyUser(`📨 收到来自 **${fromAgentName}** 的查询任务(安全,自动执行):\n\n${taskText.slice(0, 300)}`);
152
+ }
68
153
  try {
69
154
  // Create a promise that resolves when sub-agent calls multiclaws_a2a_callback
70
155
  const timeoutMs = 180_000;
@@ -88,15 +173,17 @@ class OpenClawAgentExecutor {
88
173
  this.logger.info(`[a2a-adapter] task ${taskId} waiting for callback from sub-agent...`);
89
174
  // Wait for the sub-agent to call back
90
175
  const output = await resultPromise;
91
- // Return result
176
+ // Return result and notify user
92
177
  this.taskTracker.update(taskId, { status: "completed", result: output });
93
178
  this.logger.info(`[a2a-adapter] ✓ task ${taskId} completed — resultLen=${output.length}, preview=${output.slice(0, 120)}`);
179
+ void this.notifyUser(`✅ **来自 ${fromAgentName} 的任务已完成**\n\n${output.slice(0, 800)}`);
94
180
  this.publishMessage(eventBus, output || "Task completed with no output.");
95
181
  }
96
182
  catch (err) {
97
183
  const errorMsg = err instanceof Error ? err.message : String(err);
98
184
  this.logger.error(`[a2a-adapter] ✗ task ${taskId} failed: ${errorMsg}`);
99
185
  this.taskTracker.update(taskId, { status: "failed", error: errorMsg });
186
+ void this.notifyUser(`❌ 来自 **${fromAgentName}** 的任务执行失败:${errorMsg}`);
100
187
  this.publishMessage(eventBus, `Error: ${errorMsg}`);
101
188
  }
102
189
  this.logger.info(`[a2a-adapter] task ${taskId} eventBus.finished()`);
@@ -118,8 +205,32 @@ class OpenClawAgentExecutor {
118
205
  pending.resolve(result);
119
206
  return true;
120
207
  }
208
+ /**
209
+ * Called when the local human owner approves or rejects a pending risky task.
210
+ * Returns true if a pending approval was found.
211
+ */
212
+ resolveApproval(taskId, approved) {
213
+ const pending = this.pendingApprovals.get(taskId);
214
+ if (!pending) {
215
+ this.logger.warn(`[a2a-adapter] resolveApproval: no pending approval for taskId=${taskId}`);
216
+ return false;
217
+ }
218
+ clearTimeout(pending.timer);
219
+ this.pendingApprovals.delete(taskId);
220
+ this.logger.info(`[a2a-adapter] resolveApproval: taskId=${taskId} approved=${approved}`);
221
+ pending.resolve(approved);
222
+ return true;
223
+ }
121
224
  async cancelTask(taskId, eventBus) {
122
225
  this.logger.info(`[a2a-adapter] cancelTask(taskId=${taskId})`);
226
+ // Reject pending approval if any — distinct from user-rejection, uses Error("canceled")
227
+ const approval = this.pendingApprovals.get(taskId);
228
+ if (approval) {
229
+ clearTimeout(approval.timer);
230
+ this.pendingApprovals.delete(taskId);
231
+ approval.reject(new Error("canceled"));
232
+ this.logger.info(`[a2a-adapter] cancelTask: pending approval canceled for taskId=${taskId}`);
233
+ }
123
234
  // Reject pending callback if any
124
235
  const pending = this.pendingCallbacks.get(taskId);
125
236
  if (pending) {
@@ -149,6 +260,20 @@ class OpenClawAgentExecutor {
149
260
  this.pendingCallbacks.set(taskId, { resolve, reject, timer });
150
261
  });
151
262
  }
263
+ /**
264
+ * Create a pending approval that resolves when the human owner responds,
265
+ * or rejects on timeout or cancellation.
266
+ */
267
+ createApprovalCallback(taskId, timeoutMs) {
268
+ return new Promise((resolve, reject) => {
269
+ const timer = setTimeout(() => {
270
+ this.pendingApprovals.delete(taskId);
271
+ this.logger.warn(`[a2a-adapter] task ${taskId} approval timed out after ${timeoutMs / 1000}s`);
272
+ reject(new Error(`approval timed out after ${timeoutMs / 1000}s`));
273
+ }, timeoutMs);
274
+ this.pendingApprovals.set(taskId, { resolve, reject, timer });
275
+ });
276
+ }
152
277
  /** Send a notification to all known targets. Individual failures are silently ignored. */
153
278
  async notifyUser(message) {
154
279
  const targets = this.getNotificationTargets();
@@ -167,8 +292,10 @@ class OpenClawAgentExecutor {
167
292
  timeoutMs: 5_000,
168
293
  })
169
294
  : (0, gateway_client_1.invokeGatewayTool)({
295
+ // sessions_send injects a message into the session so the AI
296
+ // can relay it to the human (correct tool; was "chat.send" before)
170
297
  gateway: this.gatewayConfig,
171
- tool: "chat.send",
298
+ tool: "sessions_send",
172
299
  args: { sessionKey: target.sessionKey, message },
173
300
  timeoutMs: 5_000,
174
301
  }));
@@ -194,28 +321,50 @@ class OpenClawAgentExecutor {
194
321
  }
195
322
  }
196
323
  exports.OpenClawAgentExecutor = OpenClawAgentExecutor;
324
+ /* ------------------------------------------------------------------ */
325
+ /* Prompt builders */
326
+ /* ------------------------------------------------------------------ */
327
+ /**
328
+ * Build the approval request message injected into the human's active session.
329
+ * The AI in that session will relay it and handle the human's approve/reject response.
330
+ */
331
+ function buildApprovalRequest(taskId, fromAgentName, taskText) {
332
+ const preview = taskText.length > 600 ? taskText.slice(0, 600) + "…" : taskText;
333
+ return `[MultiClaws] 收到来自 **${fromAgentName}** 的委派任务,需要授权
334
+
335
+ **任务内容:**
336
+ ${preview}
337
+
338
+ ⚠️ 该任务涉及写操作或高风险操作,需要您授权才能执行。
339
+
340
+ 请询问用户是否同意执行,并根据回复调用对应工具:
341
+ - 同意:\`multiclaws_task_respond(taskId="${taskId}", approved=true)\`
342
+ - 拒绝:\`multiclaws_task_respond(taskId="${taskId}", approved=false)\`
343
+
344
+ 授权等待时间:5 分钟,超时自动拒绝。`;
345
+ }
197
346
  /**
198
347
  * Build the prompt for the sub-agent that handles an incoming A2A task.
199
348
  * The sub-agent must call `multiclaws_a2a_callback` to report its result.
200
349
  */
201
350
  function buildA2ASubagentPrompt(taskId, taskText) {
202
- return `你收到了一个来自远端智能体的委派任务。请完成任务并汇报结果。
203
-
204
- ## 任务内容
205
-
206
- ${taskText}
207
-
208
- ## 完成后必做
209
-
210
- 完成任务后,你**必须**调用 \`multiclaws_a2a_callback\` 工具汇报结果:
211
-
212
- \`\`\`
213
- multiclaws_a2a_callback(taskId="${taskId}", result="你的完整回复内容")
214
- \`\`\`
215
-
216
- **重要**:
217
- - 无论任务成功还是失败,都必须调用 \`multiclaws_a2a_callback\`
218
- - result 参数填写你的完整回复文本
219
- - 如果任务失败,在 result 中说明失败原因
351
+ return `你收到了一个来自远端智能体的委派任务。请完成任务并汇报结果。
352
+
353
+ ## 任务内容
354
+
355
+ ${taskText}
356
+
357
+ ## 完成后必做
358
+
359
+ 完成任务后,你**必须**调用 \`multiclaws_a2a_callback\` 工具汇报结果:
360
+
361
+ \`\`\`
362
+ multiclaws_a2a_callback(taskId="${taskId}", result="你的完整回复内容")
363
+ \`\`\`
364
+
365
+ **重要**:
366
+ - 无论任务成功还是失败,都必须调用 \`multiclaws_a2a_callback\`
367
+ - result 参数填写你的完整回复文本
368
+ - 如果任务失败,在 result 中说明失败原因
220
369
  - 这是唯一的结果回传方式,不调用则结果会丢失`;
221
370
  }
@@ -133,6 +133,11 @@ export declare class MulticlawsService extends EventEmitter {
133
133
  private extractArtifactText;
134
134
  /** Fetch with up to 2 retries and exponential backoff. */
135
135
  private fetchWithRetry;
136
+ /**
137
+ * Called by the `multiclaws_task_respond` tool when the local human
138
+ * approves or rejects a pending risky incoming task.
139
+ */
140
+ respondToTask(taskId: string, approved: boolean): boolean;
136
141
  /** Resolve a pending A2A callback from sub-agent. */
137
142
  resolveA2ACallback(taskId: string, result: string): boolean;
138
143
  addNotificationTarget(key: string, target: NotificationTarget): void;
@@ -30,22 +30,22 @@ function buildDelegationPrompt(agent, task) {
30
30
  const bioSnippet = agent.description
31
31
  ? `\n**智能体能力**: ${agent.description.slice(0, 500)}`
32
32
  : "";
33
- return `## 委派任务
34
- 向远端智能体发送任务并汇报结果。
35
-
36
- **目标智能体**: ${agent.name} (${agent.url})${bioSnippet}
37
- **任务内容**: ${task}
38
-
39
- ## 执行步骤
40
- 1. 调用 multiclaws_delegate_send(agentUrl="${agent.url}", task="${task.replace(/"/g, '\\"')}") 发送任务
41
- 2. 收到回复后,调用 multiclaws_notify(message="结果内容") 将结果推送给用户
42
- 3. 如果需要进一步沟通,可再次调用 multiclaws_delegate_send(最多 5 轮)
43
- 4. 每次收到回复后立即调用 multiclaws_notify 推送进展
44
-
45
- ## 规则
46
- - 使用 multiclaws_delegate_send(不是 multiclaws_delegate)发送任务
47
- - 使用 multiclaws_notify(不是 message)将结果推送给用户
48
- - 最多 5 轮沟通
33
+ return `## 委派任务
34
+ 向远端智能体发送任务并汇报结果。
35
+
36
+ **目标智能体**: ${agent.name} (${agent.url})${bioSnippet}
37
+ **任务内容**: ${task}
38
+
39
+ ## 执行步骤
40
+ 1. 调用 multiclaws_delegate_send(agentUrl="${agent.url}", task="${task.replace(/"/g, '\\"')}") 发送任务
41
+ 2. 收到回复后,调用 multiclaws_notify(message="结果内容") 将结果推送给用户
42
+ 3. 如果需要进一步沟通,可再次调用 multiclaws_delegate_send(最多 5 轮)
43
+ 4. 每次收到回复后立即调用 multiclaws_notify 推送进展
44
+
45
+ ## 规则
46
+ - 使用 multiclaws_delegate_send(不是 multiclaws_delegate)发送任务
47
+ - 使用 multiclaws_notify(不是 message)将结果推送给用户
48
+ - 最多 5 轮沟通
49
49
  - 遇到错误时在 multiclaws_notify 中说明失败原因`;
50
50
  }
51
51
  /* ------------------------------------------------------------------ */
@@ -940,6 +940,20 @@ class MulticlawsService extends node_events_1.EventEmitter {
940
940
  }
941
941
  throw lastError;
942
942
  }
943
+ /**
944
+ * Called by the `multiclaws_task_respond` tool when the local human
945
+ * approves or rejects a pending risky incoming task.
946
+ */
947
+ respondToTask(taskId, approved) {
948
+ this.log("info", `respondToTask(taskId=${taskId}, approved=${approved})`);
949
+ if (!this.agentExecutor) {
950
+ this.log("warn", `respondToTask: no agentExecutor available for taskId=${taskId}`);
951
+ return false;
952
+ }
953
+ const resolved = this.agentExecutor.resolveApproval(taskId, approved);
954
+ this.log("info", `respondToTask: ${resolved ? "✓" : "✗"} taskId=${taskId} ${resolved ? "resolved" : "no pending approval found"}`);
955
+ return resolved;
956
+ }
943
957
  /** Resolve a pending A2A callback from sub-agent. */
944
958
  resolveA2ACallback(taskId, result) {
945
959
  this.log("info", `[a2a-callback] resolveA2ACallback(taskId=${taskId}, resultLen=${result.length})`);
@@ -980,8 +994,10 @@ class MulticlawsService extends node_events_1.EventEmitter {
980
994
  timeoutMs: 5_000,
981
995
  })
982
996
  : (0, gateway_client_1.invokeGatewayTool)({
997
+ // sessions_send injects a message into the session so the AI
998
+ // can relay it to the human (correct tool; was "chat.send" before)
983
999
  gateway: this.gatewayConfig,
984
- tool: "chat.send",
1000
+ tool: "sessions_send",
985
1001
  args: { sessionKey: target.sessionKey, message },
986
1002
  timeoutMs: 5_000,
987
1003
  }));
@@ -1,47 +1,47 @@
1
- {
2
- "id": "multiclaws",
3
- "name": "MultiClaws",
4
- "description": "MultiClaws plugin for multi-instance collaboration using A2A protocol",
5
- "version": "0.3.0",
6
- "skills": ["./skills/multiclaws"],
7
- "configSchema": {
8
- "type": "object",
9
- "additionalProperties": false,
10
- "properties": {
11
- "port": {
12
- "type": "integer",
13
- "minimum": 1,
14
- "maximum": 65535,
15
- "default": 3100
16
- },
17
- "displayName": {
18
- "type": "string",
19
- "minLength": 1
20
- },
21
- "selfUrl": {
22
- "type": "string",
23
- "description": "Publicly reachable URL for this agent. If not set, tunnel configuration is required."
24
- },
25
- "tunnel": {
26
- "type": "object",
27
- "description": "FRP tunnel configuration for cross-network connectivity. Required if selfUrl is not set.",
28
- "properties": {
29
- "type": { "type": "string", "enum": ["frp"] },
30
- "serverAddr": { "type": "string", "description": "FRP server address (IP or domain)" },
31
- "serverPort": { "type": "integer", "default": 7000, "description": "FRP server bind port" },
32
- "token": { "type": "string", "description": "FRP authentication token" },
33
- "portRangeStart": { "type": "integer", "description": "Start of available remote port range" },
34
- "portRangeEnd": { "type": "integer", "description": "End of available remote port range (inclusive)" }
35
- },
36
- "required": ["type", "serverAddr", "serverPort", "token", "portRangeStart", "portRangeEnd"]
37
- },
38
- "telemetry": {
39
- "type": "object",
40
- "additionalProperties": false,
41
- "properties": {
42
- "consoleExporter": { "type": "boolean", "default": false }
43
- }
44
- }
45
- }
46
- }
47
- }
1
+ {
2
+ "id": "multiclaws",
3
+ "name": "MultiClaws",
4
+ "description": "MultiClaws plugin for multi-instance collaboration using A2A protocol",
5
+ "version": "0.3.0",
6
+ "skills": ["./skills/multiclaws", "./skills/meeting-scheduler"],
7
+ "configSchema": {
8
+ "type": "object",
9
+ "additionalProperties": false,
10
+ "properties": {
11
+ "port": {
12
+ "type": "integer",
13
+ "minimum": 1,
14
+ "maximum": 65535,
15
+ "default": 3100
16
+ },
17
+ "displayName": {
18
+ "type": "string",
19
+ "minLength": 1
20
+ },
21
+ "selfUrl": {
22
+ "type": "string",
23
+ "description": "Publicly reachable URL for this agent. If not set, tunnel configuration is required."
24
+ },
25
+ "tunnel": {
26
+ "type": "object",
27
+ "description": "FRP tunnel configuration for cross-network connectivity. Required if selfUrl is not set.",
28
+ "properties": {
29
+ "type": { "type": "string", "enum": ["frp"] },
30
+ "serverAddr": { "type": "string", "description": "FRP server address (IP or domain)" },
31
+ "serverPort": { "type": "integer", "default": 7000, "description": "FRP server bind port" },
32
+ "token": { "type": "string", "description": "FRP authentication token" },
33
+ "portRangeStart": { "type": "integer", "description": "Start of available remote port range" },
34
+ "portRangeEnd": { "type": "integer", "description": "End of available remote port range (inclusive)" }
35
+ },
36
+ "required": ["type", "serverAddr", "serverPort", "token", "portRangeStart", "portRangeEnd"]
37
+ },
38
+ "telemetry": {
39
+ "type": "object",
40
+ "additionalProperties": false,
41
+ "properties": {
42
+ "consoleExporter": { "type": "boolean", "default": false }
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
package/package.json CHANGED
@@ -1,56 +1,56 @@
1
- {
2
- "name": "multiclaws",
3
- "version": "0.4.36",
4
- "description": "MultiClaws plugin for OpenClaw collaboration via A2A protocol",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "openclaw": {
8
- "extensions": [
9
- "./dist/index.js"
10
- ]
11
- },
12
- "files": [
13
- "dist",
14
- "skills",
15
- "openclaw.plugin.json",
16
- "README.md"
17
- ],
18
- "scripts": {
19
- "build": "tsc -p tsconfig.json",
20
- "test": "vitest run",
21
- "test:watch": "vitest",
22
- "clean": "rm -rf dist"
23
- },
24
- "keywords": [
25
- "openclaw",
26
- "plugin",
27
- "multiclaws",
28
- "a2a",
29
- "collaboration"
30
- ],
31
- "author": "Eric Sun",
32
- "license": "MIT",
33
- "repository": {
34
- "type": "git",
35
- "url": "https://github.com/EricSun0218/multiclaws.git"
36
- },
37
- "dependencies": {
38
- "@a2a-js/sdk": "^0.3.10",
39
- "@opentelemetry/api": "^1.9.0",
40
- "@opentelemetry/sdk-trace-base": "^2.6.0",
41
- "@opentelemetry/sdk-trace-node": "^2.6.0",
42
- "express": "^5.2.1",
43
- "opossum": "^9.0.0",
44
- "p-retry": "^7.1.1",
45
- "proper-lockfile": "^4.1.2",
46
- "zod": "^4.3.6"
47
- },
48
- "devDependencies": {
49
- "@types/express": "^5.0.6",
50
- "@types/node": "^24.3.0",
51
- "@types/opossum": "^8.1.9",
52
- "@types/proper-lockfile": "^4.1.4",
53
- "typescript": "^5.9.2",
54
- "vitest": "^3.2.4"
55
- }
56
- }
1
+ {
2
+ "name": "multiclaws",
3
+ "version": "0.4.37",
4
+ "description": "MultiClaws plugin for OpenClaw collaboration via A2A protocol",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "openclaw": {
8
+ "extensions": [
9
+ "./dist/index.js"
10
+ ]
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "skills",
15
+ "openclaw.plugin.json",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.json",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "clean": "rm -rf dist"
23
+ },
24
+ "keywords": [
25
+ "openclaw",
26
+ "plugin",
27
+ "multiclaws",
28
+ "a2a",
29
+ "collaboration"
30
+ ],
31
+ "author": "Eric Sun",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/EricSun0218/multiclaws.git"
36
+ },
37
+ "dependencies": {
38
+ "@a2a-js/sdk": "^0.3.10",
39
+ "@opentelemetry/api": "^1.9.0",
40
+ "@opentelemetry/sdk-trace-base": "^2.6.0",
41
+ "@opentelemetry/sdk-trace-node": "^2.6.0",
42
+ "express": "^5.2.1",
43
+ "opossum": "^9.0.0",
44
+ "p-retry": "^7.1.1",
45
+ "proper-lockfile": "^4.1.2",
46
+ "zod": "^4.3.6"
47
+ },
48
+ "devDependencies": {
49
+ "@types/express": "^5.0.6",
50
+ "@types/node": "^24.3.0",
51
+ "@types/opossum": "^8.1.9",
52
+ "@types/proper-lockfile": "^4.1.4",
53
+ "typescript": "^5.9.2",
54
+ "vitest": "^3.2.4"
55
+ }
56
+ }