multiclaws 0.4.20 → 0.4.21
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/gateway/handlers.d.ts +2 -1
- package/dist/gateway/handlers.js +63 -13
- package/dist/index.js +227 -83
- package/dist/infra/logger.d.ts +1 -0
- package/dist/infra/logger.js +7 -0
- package/dist/service/a2a-adapter.js +2 -0
- package/dist/service/agent-profile.d.ts +4 -1
- package/dist/service/agent-profile.js +30 -9
- package/dist/service/agent-registry.d.ts +4 -1
- package/dist/service/agent-registry.js +76 -45
- package/dist/service/multiclaws-service.d.ts +20 -0
- package/dist/service/multiclaws-service.js +487 -260
- package/dist/service/session-store.d.ts +3 -0
- package/dist/service/session-store.js +4 -1
- package/dist/task/tracker.d.ts +3 -0
- package/dist/task/tracker.js +6 -1
- package/dist/team/team-store.d.ts +4 -1
- package/dist/team/team-store.js +116 -67
- package/package.json +8 -8
- package/skills/multiclaws/SKILL.md +16 -80
|
@@ -112,6 +112,7 @@ class OpenClawAgentExecutor {
|
|
|
112
112
|
* Collects ALL assistant text messages and returns them joined.
|
|
113
113
|
*/
|
|
114
114
|
async waitForCompletion(sessionKey, timeoutMs, gatewaySessionKey) {
|
|
115
|
+
this.logger.info(`[a2a-adapter] waitForCompletion(sessionKey=${sessionKey}, timeoutMs=${timeoutMs})`);
|
|
115
116
|
const gateway = this.gatewayConfig;
|
|
116
117
|
const startTime = Date.now();
|
|
117
118
|
let attempt = 0;
|
|
@@ -226,6 +227,7 @@ class OpenClawAgentExecutor {
|
|
|
226
227
|
return null;
|
|
227
228
|
}
|
|
228
229
|
async cancelTask(taskId, eventBus) {
|
|
230
|
+
this.logger.info(`[a2a-adapter] cancelTask(taskId=${taskId})`);
|
|
229
231
|
this.taskTracker.update(taskId, { status: "failed", error: "canceled" });
|
|
230
232
|
this.publishMessage(eventBus, "Task was canceled.");
|
|
231
233
|
eventBus.finished();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BasicLogger } from "../infra/logger";
|
|
1
2
|
export type AgentProfile = {
|
|
2
3
|
ownerName: string;
|
|
3
4
|
/** Free-form markdown describing this agent: role, capabilities, data sources, etc. */
|
|
@@ -6,7 +7,9 @@ export type AgentProfile = {
|
|
|
6
7
|
export declare function renderProfileDescription(profile: AgentProfile): string;
|
|
7
8
|
export declare class ProfileStore {
|
|
8
9
|
private readonly filePath;
|
|
9
|
-
|
|
10
|
+
private readonly logger?;
|
|
11
|
+
constructor(filePath: string, logger?: BasicLogger | undefined);
|
|
12
|
+
private log;
|
|
10
13
|
load(): Promise<AgentProfile>;
|
|
11
14
|
save(profile: AgentProfile): Promise<void>;
|
|
12
15
|
update(patch: Partial<AgentProfile>): Promise<AgentProfile>;
|
|
@@ -16,23 +16,44 @@ function renderProfileDescription(profile) {
|
|
|
16
16
|
}
|
|
17
17
|
class ProfileStore {
|
|
18
18
|
filePath;
|
|
19
|
-
|
|
19
|
+
logger;
|
|
20
|
+
constructor(filePath, logger) {
|
|
20
21
|
this.filePath = filePath;
|
|
22
|
+
this.logger = logger;
|
|
23
|
+
}
|
|
24
|
+
log(level, message) {
|
|
25
|
+
const fn = level === "debug" ? this.logger?.debug : this.logger?.[level];
|
|
26
|
+
fn?.(`[profile-store] ${message}`);
|
|
21
27
|
}
|
|
22
28
|
async load() {
|
|
23
29
|
return await (0, json_store_1.readJsonWithFallback)(this.filePath, emptyProfile());
|
|
24
30
|
}
|
|
25
31
|
async save(profile) {
|
|
26
|
-
|
|
32
|
+
this.log("debug", `save(ownerName=${profile.ownerName})`);
|
|
33
|
+
try {
|
|
34
|
+
await (0, json_store_1.writeJsonAtomically)(this.filePath, profile);
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
this.log("error", `save failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
27
40
|
}
|
|
28
41
|
async update(patch) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
profile
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
this.log("debug", `update(keys=${Object.keys(patch).join(",")})`);
|
|
43
|
+
try {
|
|
44
|
+
const profile = await this.load();
|
|
45
|
+
if (patch.ownerName !== undefined)
|
|
46
|
+
profile.ownerName = patch.ownerName;
|
|
47
|
+
if (patch.bio !== undefined)
|
|
48
|
+
profile.bio = patch.bio;
|
|
49
|
+
await this.save(profile);
|
|
50
|
+
this.log("debug", `update completed`);
|
|
51
|
+
return profile;
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
this.log("error", `update failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
36
57
|
}
|
|
37
58
|
}
|
|
38
59
|
exports.ProfileStore = ProfileStore;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BasicLogger } from "../infra/logger";
|
|
1
2
|
export type AgentRecord = {
|
|
2
3
|
url: string;
|
|
3
4
|
name: string;
|
|
@@ -9,7 +10,9 @@ export type AgentRecord = {
|
|
|
9
10
|
};
|
|
10
11
|
export declare class AgentRegistry {
|
|
11
12
|
private readonly filePath;
|
|
12
|
-
|
|
13
|
+
private readonly logger?;
|
|
14
|
+
constructor(filePath: string, logger?: BasicLogger | undefined);
|
|
15
|
+
private log;
|
|
13
16
|
private readStore;
|
|
14
17
|
add(params: {
|
|
15
18
|
url: string;
|
|
@@ -19,50 +19,74 @@ function normalizeStore(raw) {
|
|
|
19
19
|
}
|
|
20
20
|
class AgentRegistry {
|
|
21
21
|
filePath;
|
|
22
|
-
|
|
22
|
+
logger;
|
|
23
|
+
constructor(filePath, logger) {
|
|
23
24
|
this.filePath = filePath;
|
|
25
|
+
this.logger = logger;
|
|
26
|
+
}
|
|
27
|
+
log(level, message) {
|
|
28
|
+
const fn = level === "debug" ? this.logger?.debug : this.logger?.[level];
|
|
29
|
+
fn?.(`[agent-registry] ${message}`);
|
|
24
30
|
}
|
|
25
31
|
async readStore() {
|
|
26
32
|
const store = await (0, json_store_1.readJsonWithFallback)(this.filePath, emptyStore());
|
|
27
33
|
return normalizeStore(store);
|
|
28
34
|
}
|
|
29
35
|
async add(params) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
36
|
+
const normalizedUrl = params.url.replace(/\/+$/, "");
|
|
37
|
+
this.log("debug", `add(url=${normalizedUrl}, name=${params.name})`);
|
|
38
|
+
try {
|
|
39
|
+
const result = await (0, json_store_1.withJsonLock)(this.filePath, emptyStore(), async () => {
|
|
40
|
+
const store = await this.readStore();
|
|
41
|
+
const existing = store.agents.findIndex((a) => a.url === normalizedUrl);
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
const record = {
|
|
44
|
+
url: normalizedUrl,
|
|
45
|
+
name: params.name,
|
|
46
|
+
description: params.description ?? "",
|
|
47
|
+
skills: params.skills ?? [],
|
|
48
|
+
apiKey: params.apiKey,
|
|
49
|
+
addedAtMs: existing >= 0 ? store.agents[existing].addedAtMs : now,
|
|
50
|
+
lastSeenAtMs: now,
|
|
51
|
+
};
|
|
52
|
+
if (existing >= 0) {
|
|
53
|
+
store.agents[existing] = record;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
store.agents.push(record);
|
|
57
|
+
}
|
|
58
|
+
await (0, json_store_1.writeJsonAtomically)(this.filePath, store);
|
|
59
|
+
return record;
|
|
60
|
+
});
|
|
61
|
+
this.log("debug", `add completed, agent=${result.name}`);
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
this.log("error", `add failed for url=${normalizedUrl}: ${err instanceof Error ? err.message : String(err)}`);
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
53
68
|
}
|
|
54
69
|
async remove(url) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
const normalizedUrl = url.replace(/\/+$/, "");
|
|
71
|
+
this.log("debug", `remove(url=${normalizedUrl})`);
|
|
72
|
+
try {
|
|
73
|
+
const result = await (0, json_store_1.withJsonLock)(this.filePath, emptyStore(), async () => {
|
|
74
|
+
const store = await this.readStore();
|
|
75
|
+
const before = store.agents.length;
|
|
76
|
+
store.agents = store.agents.filter((a) => a.url !== normalizedUrl);
|
|
77
|
+
if (store.agents.length === before) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
await (0, json_store_1.writeJsonAtomically)(this.filePath, store);
|
|
81
|
+
return true;
|
|
82
|
+
});
|
|
83
|
+
this.log("debug", `remove completed, found=${result}`);
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
this.log("error", `remove failed for url=${normalizedUrl}: ${err instanceof Error ? err.message : String(err)}`);
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
66
90
|
}
|
|
67
91
|
async list() {
|
|
68
92
|
const store = await this.readStore();
|
|
@@ -74,16 +98,23 @@ class AgentRegistry {
|
|
|
74
98
|
return store.agents.find((a) => a.url === normalizedUrl) ?? null;
|
|
75
99
|
}
|
|
76
100
|
async updateDescription(url, description) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
agent
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
101
|
+
const normalizedUrl = url.replace(/\/+$/, "");
|
|
102
|
+
this.log("debug", `updateDescription(url=${normalizedUrl})`);
|
|
103
|
+
try {
|
|
104
|
+
await (0, json_store_1.withJsonLock)(this.filePath, emptyStore(), async () => {
|
|
105
|
+
const store = await this.readStore();
|
|
106
|
+
const agent = store.agents.find((a) => a.url === normalizedUrl);
|
|
107
|
+
if (agent) {
|
|
108
|
+
agent.description = description;
|
|
109
|
+
agent.lastSeenAtMs = Date.now();
|
|
110
|
+
await (0, json_store_1.writeJsonAtomically)(this.filePath, store);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
this.log("error", `updateDescription failed for url=${normalizedUrl}: ${err instanceof Error ? err.message : String(err)}`);
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
87
118
|
}
|
|
88
119
|
async updateLastSeen(url) {
|
|
89
120
|
await (0, json_store_1.withJsonLock)(this.filePath, emptyStore(), async () => {
|
|
@@ -42,6 +42,7 @@ export declare class MulticlawsService extends EventEmitter {
|
|
|
42
42
|
private frpTunnel;
|
|
43
43
|
private selfUrl;
|
|
44
44
|
private profileDescription;
|
|
45
|
+
private readonly gatewayConfig;
|
|
45
46
|
constructor(options: MulticlawsServiceOptions);
|
|
46
47
|
start(): Promise<void>;
|
|
47
48
|
stop(): Promise<void>;
|
|
@@ -56,6 +57,25 @@ export declare class MulticlawsService extends EventEmitter {
|
|
|
56
57
|
agentUrl: string;
|
|
57
58
|
task: string;
|
|
58
59
|
}): Promise<DelegateTaskResult>;
|
|
60
|
+
/**
|
|
61
|
+
* Synchronous delegation: sends A2A task and waits for the result.
|
|
62
|
+
* Used by sub-agents internally via the multiclaws_delegate_send tool.
|
|
63
|
+
*/
|
|
64
|
+
delegateTaskSync(params: {
|
|
65
|
+
agentUrl: string;
|
|
66
|
+
task: string;
|
|
67
|
+
}): Promise<DelegateTaskResult>;
|
|
68
|
+
/**
|
|
69
|
+
* Spawn a sub-agent to handle delegation asynchronously.
|
|
70
|
+
* The sub-agent uses multiclaws_delegate_send internally and
|
|
71
|
+
* reports results back to the user via the message tool.
|
|
72
|
+
*/
|
|
73
|
+
spawnDelegation(params: {
|
|
74
|
+
agentUrl: string;
|
|
75
|
+
task: string;
|
|
76
|
+
}): Promise<{
|
|
77
|
+
message: string;
|
|
78
|
+
}>;
|
|
59
79
|
getTaskStatus(taskId: string): import("../task/tracker").TaskRecord | null;
|
|
60
80
|
getProfile(): Promise<AgentProfile>;
|
|
61
81
|
/**
|