multiclaws 0.4.0 → 0.4.1

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.
@@ -18,6 +18,10 @@ const sessionReplySchema = zod_1.z.object({
18
18
  });
19
19
  const sessionStatusSchema = zod_1.z.object({ sessionId: zod_1.z.string().trim().min(1).optional() });
20
20
  const sessionEndSchema = zod_1.z.object({ sessionId: nonEmptyString });
21
+ const sessionWaitAllSchema = zod_1.z.object({
22
+ sessionIds: zod_1.z.array(nonEmptyString).min(1),
23
+ timeoutMs: zod_1.z.number().positive().optional(),
24
+ });
21
25
  const profileSetSchema = zod_1.z.object({
22
26
  ownerName: zod_1.z.string().trim().optional(),
23
27
  bio: zod_1.z.string().optional(),
@@ -106,6 +110,17 @@ function createGatewayHandlers(getService) {
106
110
  safeHandle(respond, "session_status_failed", error);
107
111
  }
108
112
  },
113
+ "multiclaws.session.wait_all": async ({ params, respond }) => {
114
+ try {
115
+ const parsed = sessionWaitAllSchema.parse(params);
116
+ const service = getService();
117
+ const result = await service.waitForSessions(parsed);
118
+ respond(true, result);
119
+ }
120
+ catch (error) {
121
+ safeHandle(respond, "session_wait_all_failed", error);
122
+ }
123
+ },
109
124
  "multiclaws.session.end": async ({ params, respond }) => {
110
125
  try {
111
126
  const parsed = sessionEndSchema.parse(params);
package/dist/index.js CHANGED
@@ -155,6 +155,30 @@ function createTools(getService) {
155
155
  return textResult(JSON.stringify({ sessions }, null, 2), { sessions });
156
156
  },
157
157
  };
158
+ const multiclawsSessionWaitAll = {
159
+ name: "multiclaws_session_wait_all",
160
+ description: "Wait for multiple sessions to complete, then return all results at once. Use this when you have started multiple sessions concurrently and need all results before synthesizing an answer. Returns early if any session needs input (input-required). Default timeout: 5 minutes.",
161
+ parameters: {
162
+ type: "object",
163
+ additionalProperties: false,
164
+ properties: {
165
+ sessionIds: { type: "array", items: { type: "string" } },
166
+ timeoutMs: { type: "number" },
167
+ },
168
+ required: ["sessionIds"],
169
+ },
170
+ execute: async (_toolCallId, args) => {
171
+ const service = requireService(getService());
172
+ const sessionIds = Array.isArray(args.sessionIds)
173
+ ? args.sessionIds.map((s) => String(s).trim()).filter(Boolean)
174
+ : [];
175
+ if (!sessionIds.length)
176
+ throw new Error("sessionIds must be a non-empty array");
177
+ const timeoutMs = typeof args.timeoutMs === "number" ? args.timeoutMs : undefined;
178
+ const result = await service.waitForSessions({ sessionIds, timeoutMs });
179
+ return textResult(JSON.stringify(result, null, 2), result);
180
+ },
181
+ };
158
182
  const multiclawsSessionEnd = {
159
183
  name: "multiclaws_session_end",
160
184
  description: "Cancel and close a collaboration session.",
@@ -327,6 +351,7 @@ function createTools(getService) {
327
351
  multiclawsSessionStart,
328
352
  multiclawsSessionReply,
329
353
  multiclawsSessionStatus,
354
+ multiclawsSessionWaitAll,
330
355
  multiclawsSessionEnd,
331
356
  multiclawsTeamCreate,
332
357
  multiclawsTeamJoin,
@@ -63,6 +63,19 @@ export declare class MulticlawsService extends EventEmitter {
63
63
  }): Promise<SessionReplyResult>;
64
64
  getSession(sessionId: string): ConversationSession | null;
65
65
  listSessions(): ConversationSession[];
66
+ waitForSessions(params: {
67
+ sessionIds: string[];
68
+ timeoutMs?: number;
69
+ }): Promise<{
70
+ results: Array<{
71
+ sessionId: string;
72
+ status: string;
73
+ agentName: string;
74
+ lastMessage?: string;
75
+ error?: string;
76
+ }>;
77
+ timedOut: boolean;
78
+ }>;
66
79
  endSession(sessionId: string): boolean;
67
80
  private runSession;
68
81
  private handleSessionResult;
@@ -262,6 +262,36 @@ class MulticlawsService extends node_events_1.EventEmitter {
262
262
  listSessions() {
263
263
  return this.sessionStore.list();
264
264
  }
265
+ async waitForSessions(params) {
266
+ const timeout = params.timeoutMs ?? 5 * 60 * 1000;
267
+ const deadline = Date.now() + timeout;
268
+ const terminalStates = new Set(["completed", "failed", "canceled"]);
269
+ const getResults = () => params.sessionIds.map((id) => {
270
+ const session = this.sessionStore.get(id);
271
+ if (!session)
272
+ return { sessionId: id, agentName: "unknown", status: "not_found" };
273
+ const lastAgent = [...session.messages].reverse().find((m) => m.role === "agent");
274
+ return {
275
+ sessionId: id,
276
+ agentName: session.agentName,
277
+ status: session.status,
278
+ lastMessage: lastAgent?.content,
279
+ error: session.error,
280
+ };
281
+ });
282
+ while (Date.now() < deadline) {
283
+ const results = getResults();
284
+ const allSettled = results.every((r) => terminalStates.has(r.status) || r.status === "not_found");
285
+ if (allSettled)
286
+ return { results, timedOut: false };
287
+ // Return early if any session needs input — AI must handle it before continuing
288
+ const needsInput = results.some((r) => r.status === "input-required");
289
+ if (needsInput)
290
+ return { results, timedOut: false };
291
+ await new Promise((r) => setTimeout(r, 1_000));
292
+ }
293
+ return { results: getResults(), timedOut: true };
294
+ }
265
295
  endSession(sessionId) {
266
296
  const session = this.sessionStore.get(sessionId);
267
297
  if (!session)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multiclaws",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "MultiClaws plugin for OpenClaw collaboration via A2A protocol",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -49,13 +49,19 @@ multiclaws_session_status(sessionId="...") → 查看单个会话及消息历史
49
49
  multiclaws_session_end(sessionId="...") → 取消并关闭会话
50
50
  ```
51
51
 
52
- ### 并发协作
53
- 可同时开启多个 session,各自独立运行:
52
+ ### 并发协作(自动汇总)
53
+ 同时开启多个 session,等所有结果后汇总:
54
54
  ```
55
- multiclaws_session_start(agentUrl=B, message="任务1") → sessionId_1
56
- multiclaws_session_start(agentUrl=C, message="任务2") → sessionId_2
55
+ id1 = multiclaws_session_start(agentUrl=B, message="子任务1")
56
+ id2 = multiclaws_session_start(agentUrl=C, message="子任务2")
57
+ results = multiclaws_session_wait_all(sessionIds=[id1, id2])
58
+ → 阻塞直到全部完成,返回所有结果
59
+ → AI 汇总后回复用户
57
60
  ```
58
61
 
62
+ **注意**:若任何 session 变为 `input-required`,`wait_all` 会提前返回,
63
+ AI 应先用 `session_reply` 处理,再继续等待剩余 session。
64
+
59
65
  ### 链式协作(A→B→C)
60
66
  B 内部可以自己调用 `multiclaws_session_start` 委派给 C,结果自然冒泡回 A。
61
67
 
@@ -99,6 +105,7 @@ multiclaws_profile_show()
99
105
  | `multiclaws_session_start` | 开始协作会话(替代旧 delegate) | `agentUrl`, `message` |
100
106
  | `multiclaws_session_reply` | 在会话中发送后续消息 | `sessionId`, `message` |
101
107
  | `multiclaws_session_status` | 查看会话状态和消息历史 | `sessionId`(可选,不传返回全部) |
108
+ | `multiclaws_session_wait_all` | 等待多个会话全部完成,返回所有结果 | `sessionIds[]`, `timeoutMs`(可选) |
102
109
  | `multiclaws_session_end` | 取消/关闭会话 | `sessionId` |
103
110
 
104
111
  ### 档案