multiclaws 0.4.31 → 0.4.33
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.
package/dist/index.js
CHANGED
|
@@ -234,6 +234,35 @@ function createTools(getService, logger) {
|
|
|
234
234
|
}
|
|
235
235
|
},
|
|
236
236
|
};
|
|
237
|
+
const multiclawsNotify = {
|
|
238
|
+
name: "multiclaws_notify",
|
|
239
|
+
description: "Send a notification message to the local user's WebUI. " +
|
|
240
|
+
"Used by sub-agents to deliver delegation results back to the user. " +
|
|
241
|
+
"Broadcasts to all known channels so the user sees the message regardless of which channel they are on.",
|
|
242
|
+
parameters: {
|
|
243
|
+
type: "object",
|
|
244
|
+
additionalProperties: false,
|
|
245
|
+
properties: {
|
|
246
|
+
message: { type: "string", description: "The message to send to the user." },
|
|
247
|
+
},
|
|
248
|
+
required: ["message"],
|
|
249
|
+
},
|
|
250
|
+
execute: async (_toolCallId, args) => {
|
|
251
|
+
const msg = typeof args.message === "string" ? args.message.trim() : "";
|
|
252
|
+
log("info", `tool:multiclaws_notify(len=${msg.length})`);
|
|
253
|
+
try {
|
|
254
|
+
const service = requireService(getService());
|
|
255
|
+
if (!msg)
|
|
256
|
+
throw new Error("message is required");
|
|
257
|
+
await service.notifyUser(msg);
|
|
258
|
+
return textResult("Notification sent.");
|
|
259
|
+
}
|
|
260
|
+
catch (err) {
|
|
261
|
+
log("error", `tool:multiclaws_notify failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
262
|
+
throw err;
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
};
|
|
237
266
|
const multiclawsTaskStatus = {
|
|
238
267
|
name: "multiclaws_task_status",
|
|
239
268
|
description: "Check the status of a delegated task.",
|
|
@@ -471,6 +500,7 @@ function createTools(getService, logger) {
|
|
|
471
500
|
multiclawsDelegate,
|
|
472
501
|
multiclawsDelegateSend,
|
|
473
502
|
multiclawsA2ACallback,
|
|
503
|
+
multiclawsNotify,
|
|
474
504
|
multiclawsTaskStatus,
|
|
475
505
|
multiclawsTeamCreate,
|
|
476
506
|
multiclawsTeamJoin,
|
|
@@ -509,7 +539,7 @@ const plugin = {
|
|
|
509
539
|
if (gw) {
|
|
510
540
|
const tools = (gw.tools ?? {});
|
|
511
541
|
const allow = Array.isArray(tools.allow) ? tools.allow : [];
|
|
512
|
-
const adapterRequired = ["sessions_spawn", "sessions_history", "message"];
|
|
542
|
+
const adapterRequired = ["sessions_spawn", "sessions_history", "message", "chat.send"];
|
|
513
543
|
const defaultA2AExecutionTools = ["exec", "read", "write", "edit", "process"];
|
|
514
544
|
const pluginConf = api.pluginConfig ?? {};
|
|
515
545
|
const a2aExecTools = Array.isArray(pluginConf.a2aAllowedTools)
|
|
@@ -595,14 +625,18 @@ const plugin = {
|
|
|
595
625
|
api.on("gateway_stop", () => {
|
|
596
626
|
structured.logger.info("[multiclaws] gateway_stop observed");
|
|
597
627
|
});
|
|
598
|
-
// Collect
|
|
628
|
+
// Collect notification targets from incoming messages (external channels)
|
|
599
629
|
api.on("message_received", (_event, ctx) => {
|
|
600
|
-
if (service && ctx.channelId) {
|
|
601
|
-
service.
|
|
630
|
+
if (service && ctx.channelId && ctx.channelId !== "webchat" && ctx.conversationId) {
|
|
631
|
+
service.addNotificationTarget(`${ctx.channelId}:${ctx.conversationId}`, { type: "channel", conversationId: ctx.conversationId });
|
|
602
632
|
}
|
|
603
633
|
});
|
|
604
634
|
// Inject onboarding prompt when profile is pending first-run setup
|
|
605
|
-
|
|
635
|
+
// Also capture web session targets for notifications
|
|
636
|
+
api.on("before_prompt_build", async (_event, ctx) => {
|
|
637
|
+
if (service && ctx.sessionKey) {
|
|
638
|
+
service.addNotificationTarget(`web:${ctx.sessionKey}`, { type: "web", sessionKey: ctx.sessionKey });
|
|
639
|
+
}
|
|
606
640
|
if (!service)
|
|
607
641
|
return;
|
|
608
642
|
try {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { AgentExecutor, ExecutionEventBus, RequestContext } from "@a2a-js/sdk/server";
|
|
2
2
|
import { type GatewayConfig } from "../infra/gateway-client";
|
|
3
3
|
import type { TaskTracker } from "../task/tracker";
|
|
4
|
+
import type { NotificationTarget } from "./multiclaws-service";
|
|
4
5
|
export type A2AAdapterOptions = {
|
|
5
6
|
gatewayConfig: GatewayConfig | null;
|
|
6
7
|
taskTracker: TaskTracker;
|
|
7
8
|
cwd?: string;
|
|
8
|
-
|
|
9
|
+
getNotificationTargets?: () => ReadonlyMap<string, NotificationTarget>;
|
|
9
10
|
logger: {
|
|
10
11
|
info: (msg: string) => void;
|
|
11
12
|
warn: (msg: string) => void;
|
|
@@ -25,7 +26,7 @@ export type A2AAdapterOptions = {
|
|
|
25
26
|
export declare class OpenClawAgentExecutor implements AgentExecutor {
|
|
26
27
|
private gatewayConfig;
|
|
27
28
|
private readonly taskTracker;
|
|
28
|
-
private readonly
|
|
29
|
+
private readonly getNotificationTargets;
|
|
29
30
|
private readonly logger;
|
|
30
31
|
private readonly cwd;
|
|
31
32
|
private readonly pendingCallbacks;
|
|
@@ -43,7 +44,7 @@ export declare class OpenClawAgentExecutor implements AgentExecutor {
|
|
|
43
44
|
* or rejects on timeout.
|
|
44
45
|
*/
|
|
45
46
|
private createCallback;
|
|
46
|
-
/** Send a notification to all known
|
|
47
|
+
/** Send a notification to all known targets. Individual failures are silently ignored. */
|
|
47
48
|
private notifyUser;
|
|
48
49
|
private publishMessage;
|
|
49
50
|
}
|
|
@@ -23,14 +23,14 @@ function extractTextFromMessage(message) {
|
|
|
23
23
|
class OpenClawAgentExecutor {
|
|
24
24
|
gatewayConfig;
|
|
25
25
|
taskTracker;
|
|
26
|
-
|
|
26
|
+
getNotificationTargets;
|
|
27
27
|
logger;
|
|
28
28
|
cwd;
|
|
29
29
|
pendingCallbacks = new Map();
|
|
30
30
|
constructor(options) {
|
|
31
31
|
this.gatewayConfig = options.gatewayConfig;
|
|
32
32
|
this.taskTracker = options.taskTracker;
|
|
33
|
-
this.
|
|
33
|
+
this.getNotificationTargets = options.getNotificationTargets ?? (() => new Map());
|
|
34
34
|
this.logger = options.logger;
|
|
35
35
|
this.cwd = options.cwd || process.cwd();
|
|
36
36
|
}
|
|
@@ -132,17 +132,24 @@ class OpenClawAgentExecutor {
|
|
|
132
132
|
this.pendingCallbacks.set(taskId, { resolve, reject, timer });
|
|
133
133
|
});
|
|
134
134
|
}
|
|
135
|
-
/** Send a notification to all known
|
|
135
|
+
/** Send a notification to all known targets. Individual failures are silently ignored. */
|
|
136
136
|
async notifyUser(message) {
|
|
137
|
-
const
|
|
138
|
-
if (!this.gatewayConfig ||
|
|
137
|
+
const targets = this.getNotificationTargets();
|
|
138
|
+
if (!this.gatewayConfig || targets.size === 0)
|
|
139
139
|
return;
|
|
140
|
-
await Promise.allSettled([...
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
140
|
+
await Promise.allSettled([...targets.values()].map((target) => target.type === "channel"
|
|
141
|
+
? (0, gateway_client_1.invokeGatewayTool)({
|
|
142
|
+
gateway: this.gatewayConfig,
|
|
143
|
+
tool: "message",
|
|
144
|
+
args: { action: "send", target: target.conversationId, message },
|
|
145
|
+
timeoutMs: 5_000,
|
|
146
|
+
})
|
|
147
|
+
: (0, gateway_client_1.invokeGatewayTool)({
|
|
148
|
+
gateway: this.gatewayConfig,
|
|
149
|
+
tool: "chat.send",
|
|
150
|
+
args: { sessionKey: target.sessionKey, message },
|
|
151
|
+
timeoutMs: 5_000,
|
|
152
|
+
})));
|
|
146
153
|
}
|
|
147
154
|
publishMessage(eventBus, text) {
|
|
148
155
|
const message = {
|
|
@@ -27,6 +27,13 @@ export type DelegateTaskResult = {
|
|
|
27
27
|
status: string;
|
|
28
28
|
error?: string;
|
|
29
29
|
};
|
|
30
|
+
export type NotificationTarget = {
|
|
31
|
+
type: "channel";
|
|
32
|
+
conversationId: string;
|
|
33
|
+
} | {
|
|
34
|
+
type: "web";
|
|
35
|
+
sessionKey: string;
|
|
36
|
+
};
|
|
30
37
|
export declare class MulticlawsService extends EventEmitter {
|
|
31
38
|
private readonly options;
|
|
32
39
|
private started;
|
|
@@ -45,7 +52,7 @@ export declare class MulticlawsService extends EventEmitter {
|
|
|
45
52
|
private profileDescription;
|
|
46
53
|
private readonly gatewayConfig;
|
|
47
54
|
private readonly resolvedCwd;
|
|
48
|
-
private readonly
|
|
55
|
+
private readonly notificationTargets;
|
|
49
56
|
constructor(options: MulticlawsServiceOptions);
|
|
50
57
|
start(): Promise<void>;
|
|
51
58
|
stop(): Promise<void>;
|
|
@@ -128,8 +135,8 @@ export declare class MulticlawsService extends EventEmitter {
|
|
|
128
135
|
private fetchWithRetry;
|
|
129
136
|
/** Register a channel ID for notifications. */
|
|
130
137
|
resolveA2ACallback(taskId: string, result: string): boolean;
|
|
131
|
-
|
|
132
|
-
/** Send a notification to all known
|
|
133
|
-
|
|
138
|
+
addNotificationTarget(key: string, target: NotificationTarget): void;
|
|
139
|
+
/** Send a notification to all known targets. Individual failures are silently ignored. */
|
|
140
|
+
notifyUser(message: string): Promise<void>;
|
|
134
141
|
private log;
|
|
135
142
|
}
|
|
@@ -38,14 +38,15 @@ function buildDelegationPrompt(agent, task) {
|
|
|
38
38
|
|
|
39
39
|
## 执行步骤
|
|
40
40
|
1. 调用 multiclaws_delegate_send(agentUrl="${agent.url}", task="${task.replace(/"/g, '\\"')}") 发送任务
|
|
41
|
-
2.
|
|
41
|
+
2. 收到回复后,调用 multiclaws_notify(message="结果内容") 将结果推送给用户
|
|
42
42
|
3. 如果需要进一步沟通,可再次调用 multiclaws_delegate_send(最多 5 轮)
|
|
43
|
-
4.
|
|
43
|
+
4. 每次收到回复后立即调用 multiclaws_notify 推送进展
|
|
44
44
|
|
|
45
45
|
## 规则
|
|
46
46
|
- 使用 multiclaws_delegate_send(不是 multiclaws_delegate)发送任务
|
|
47
|
+
- 使用 multiclaws_notify(不是 message)将结果推送给用户
|
|
47
48
|
- 最多 5 轮沟通
|
|
48
|
-
-
|
|
49
|
+
- 遇到错误时在 multiclaws_notify 中说明失败原因`;
|
|
49
50
|
}
|
|
50
51
|
/* ------------------------------------------------------------------ */
|
|
51
52
|
/* Service */
|
|
@@ -68,7 +69,7 @@ class MulticlawsService extends node_events_1.EventEmitter {
|
|
|
68
69
|
profileDescription = "OpenClaw agent";
|
|
69
70
|
gatewayConfig;
|
|
70
71
|
resolvedCwd;
|
|
71
|
-
|
|
72
|
+
notificationTargets = new Map();
|
|
72
73
|
constructor(options) {
|
|
73
74
|
super();
|
|
74
75
|
this.options = options;
|
|
@@ -123,7 +124,7 @@ class MulticlawsService extends node_events_1.EventEmitter {
|
|
|
123
124
|
gatewayConfig: this.options.gatewayConfig ?? null,
|
|
124
125
|
taskTracker: this.taskTracker,
|
|
125
126
|
cwd: this.resolvedCwd,
|
|
126
|
-
|
|
127
|
+
getNotificationTargets: () => this.notificationTargets,
|
|
127
128
|
logger,
|
|
128
129
|
});
|
|
129
130
|
this.agentCard = {
|
|
@@ -925,22 +926,29 @@ class MulticlawsService extends node_events_1.EventEmitter {
|
|
|
925
926
|
return false;
|
|
926
927
|
return this.agentExecutor.resolveCallback(taskId, result);
|
|
927
928
|
}
|
|
928
|
-
|
|
929
|
-
if (!this.
|
|
930
|
-
this.
|
|
931
|
-
this.log("debug", `
|
|
929
|
+
addNotificationTarget(key, target) {
|
|
930
|
+
if (!this.notificationTargets.has(key)) {
|
|
931
|
+
this.notificationTargets.set(key, target);
|
|
932
|
+
this.log("debug", `notification target registered: ${key} (total: ${this.notificationTargets.size})`);
|
|
932
933
|
}
|
|
933
934
|
}
|
|
934
|
-
/** Send a notification to all known
|
|
935
|
+
/** Send a notification to all known targets. Individual failures are silently ignored. */
|
|
935
936
|
async notifyUser(message) {
|
|
936
|
-
if (!this.gatewayConfig || this.
|
|
937
|
+
if (!this.gatewayConfig || this.notificationTargets.size === 0)
|
|
937
938
|
return;
|
|
938
|
-
await Promise.allSettled([...this.
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
939
|
+
await Promise.allSettled([...this.notificationTargets.values()].map((target) => target.type === "channel"
|
|
940
|
+
? (0, gateway_client_1.invokeGatewayTool)({
|
|
941
|
+
gateway: this.gatewayConfig,
|
|
942
|
+
tool: "message",
|
|
943
|
+
args: { action: "send", target: target.conversationId, message },
|
|
944
|
+
timeoutMs: 5_000,
|
|
945
|
+
})
|
|
946
|
+
: (0, gateway_client_1.invokeGatewayTool)({
|
|
947
|
+
gateway: this.gatewayConfig,
|
|
948
|
+
tool: "chat.send",
|
|
949
|
+
args: { sessionKey: target.sessionKey, message },
|
|
950
|
+
timeoutMs: 5_000,
|
|
951
|
+
})));
|
|
944
952
|
}
|
|
945
953
|
log(level, message) {
|
|
946
954
|
this.options.logger?.[level]?.(`[multiclaws] ${message}`);
|