multiclaws 0.4.20 → 0.4.22

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.
@@ -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
- constructor(filePath: string);
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
- constructor(filePath) {
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
- await (0, json_store_1.writeJsonAtomically)(this.filePath, profile);
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
- const profile = await this.load();
30
- if (patch.ownerName !== undefined)
31
- profile.ownerName = patch.ownerName;
32
- if (patch.bio !== undefined)
33
- profile.bio = patch.bio;
34
- await this.save(profile);
35
- return profile;
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
- constructor(filePath: string);
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
- constructor(filePath) {
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
- return await (0, json_store_1.withJsonLock)(this.filePath, emptyStore(), async () => {
31
- const store = await this.readStore();
32
- const normalizedUrl = params.url.replace(/\/+$/, "");
33
- const existing = store.agents.findIndex((a) => a.url === normalizedUrl);
34
- const now = Date.now();
35
- const record = {
36
- url: normalizedUrl,
37
- name: params.name,
38
- description: params.description ?? "",
39
- skills: params.skills ?? [],
40
- apiKey: params.apiKey,
41
- addedAtMs: existing >= 0 ? store.agents[existing].addedAtMs : now,
42
- lastSeenAtMs: now,
43
- };
44
- if (existing >= 0) {
45
- store.agents[existing] = record;
46
- }
47
- else {
48
- store.agents.push(record);
49
- }
50
- await (0, json_store_1.writeJsonAtomically)(this.filePath, store);
51
- return record;
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
- return await (0, json_store_1.withJsonLock)(this.filePath, emptyStore(), async () => {
56
- const store = await this.readStore();
57
- const normalizedUrl = url.replace(/\/+$/, "");
58
- const before = store.agents.length;
59
- store.agents = store.agents.filter((a) => a.url !== normalizedUrl);
60
- if (store.agents.length === before) {
61
- return false;
62
- }
63
- await (0, json_store_1.writeJsonAtomically)(this.filePath, store);
64
- return true;
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
- await (0, json_store_1.withJsonLock)(this.filePath, emptyStore(), async () => {
78
- const store = await this.readStore();
79
- const normalizedUrl = url.replace(/\/+$/, "");
80
- const agent = store.agents.find((a) => a.url === normalizedUrl);
81
- if (agent) {
82
- agent.description = description;
83
- agent.lastSeenAtMs = Date.now();
84
- await (0, json_store_1.writeJsonAtomically)(this.filePath, store);
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
  /**
@@ -103,5 +123,7 @@ export declare class MulticlawsService extends EventEmitter {
103
123
  private extractArtifactText;
104
124
  /** Fetch with up to 2 retries and exponential backoff. */
105
125
  private fetchWithRetry;
126
+ /** Send a notification message to the local user via the gateway message tool. */
127
+ private notifyUser;
106
128
  private log;
107
129
  }