multiclaws 0.4.33 → 0.4.34
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
|
@@ -625,16 +625,25 @@ const plugin = {
|
|
|
625
625
|
api.on("gateway_stop", () => {
|
|
626
626
|
structured.logger.info("[multiclaws] gateway_stop observed");
|
|
627
627
|
});
|
|
628
|
-
// Collect notification targets from incoming messages
|
|
628
|
+
// Collect notification targets from incoming messages
|
|
629
629
|
api.on("message_received", (_event, ctx) => {
|
|
630
|
-
if (service
|
|
630
|
+
if (!service || !ctx.channelId)
|
|
631
|
+
return;
|
|
632
|
+
if (ctx.channelId === "webchat" && ctx.conversationId) {
|
|
633
|
+
// WebChat: use conversationId with the message tool
|
|
634
|
+
service.addNotificationTarget(`webchat:${ctx.conversationId}`, { type: "channel", conversationId: ctx.conversationId });
|
|
635
|
+
}
|
|
636
|
+
else if (ctx.channelId !== "webchat" && ctx.conversationId) {
|
|
637
|
+
// External channels (Telegram, etc.)
|
|
631
638
|
service.addNotificationTarget(`${ctx.channelId}:${ctx.conversationId}`, { type: "channel", conversationId: ctx.conversationId });
|
|
632
639
|
}
|
|
633
640
|
});
|
|
634
641
|
// Inject onboarding prompt when profile is pending first-run setup
|
|
635
|
-
// Also capture web session targets for notifications
|
|
642
|
+
// Also capture web session targets for notifications (skip internal sub-agent sessions)
|
|
643
|
+
const INTERNAL_SESSION_PREFIXES = ["delegate-", "a2a-"];
|
|
636
644
|
api.on("before_prompt_build", async (_event, ctx) => {
|
|
637
|
-
if (service && ctx.sessionKey
|
|
645
|
+
if (service && ctx.sessionKey &&
|
|
646
|
+
!INTERNAL_SESSION_PREFIXES.some((p) => ctx.sessionKey.startsWith(p))) {
|
|
638
647
|
service.addNotificationTarget(`web:${ctx.sessionKey}`, { type: "web", sessionKey: ctx.sessionKey });
|
|
639
648
|
}
|
|
640
649
|
if (!service)
|
|
@@ -37,33 +37,43 @@ class OpenClawAgentExecutor {
|
|
|
37
37
|
async execute(context, eventBus) {
|
|
38
38
|
const taskText = extractTextFromMessage(context.userMessage);
|
|
39
39
|
const taskId = context.taskId;
|
|
40
|
+
this.logger.info(`[a2a-adapter] ▶ execute() called — taskId=${taskId}, textLen=${taskText.length}`);
|
|
40
41
|
if (!taskText.trim()) {
|
|
42
|
+
this.logger.warn(`[a2a-adapter] ✗ empty task text, rejecting — taskId=${taskId}`);
|
|
41
43
|
this.publishMessage(eventBus, "Error: empty task received.");
|
|
42
44
|
eventBus.finished();
|
|
43
45
|
return;
|
|
44
46
|
}
|
|
45
|
-
const
|
|
47
|
+
const meta = context.userMessage.metadata ?? {};
|
|
48
|
+
const fromAgentUrl = meta.agentUrl ?? "unknown";
|
|
49
|
+
const fromAgentName = meta.agentName || fromAgentUrl;
|
|
50
|
+
this.logger.info(`[a2a-adapter] task ${taskId} from ${fromAgentName} (${fromAgentUrl}): ${taskText.slice(0, 120)}`);
|
|
46
51
|
this.taskTracker.create({
|
|
47
|
-
fromPeerId:
|
|
52
|
+
fromPeerId: fromAgentUrl,
|
|
48
53
|
toPeerId: "local",
|
|
49
54
|
task: taskText,
|
|
50
55
|
});
|
|
56
|
+
this.logger.info(`[a2a-adapter] task ${taskId} tracked`);
|
|
51
57
|
if (!this.gatewayConfig) {
|
|
52
|
-
this.logger.error(
|
|
58
|
+
this.logger.error(`[a2a-adapter] ✗ gateway config not available — taskId=${taskId}`);
|
|
53
59
|
this.taskTracker.update(taskId, { status: "failed", error: "gateway config not available" });
|
|
54
60
|
this.publishMessage(eventBus, "Error: gateway config not available, cannot execute task.");
|
|
55
61
|
eventBus.finished();
|
|
56
62
|
return;
|
|
57
63
|
}
|
|
58
64
|
// Notify local user about incoming task
|
|
59
|
-
|
|
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)}`);
|
|
60
68
|
try {
|
|
61
|
-
this.logger.info(`[a2a-adapter] executing task ${taskId}: ${taskText.slice(0, 100)}`);
|
|
62
69
|
// Create a promise that resolves when sub-agent calls multiclaws_a2a_callback
|
|
63
|
-
const
|
|
70
|
+
const timeoutMs = 180_000;
|
|
71
|
+
const resultPromise = this.createCallback(taskId, timeoutMs);
|
|
72
|
+
this.logger.info(`[a2a-adapter] task ${taskId} callback registered (timeout=${timeoutMs / 1000}s)`);
|
|
64
73
|
// Spawn the subagent with instructions to call back when done
|
|
65
74
|
const prompt = buildA2ASubagentPrompt(taskId, taskText);
|
|
66
|
-
|
|
75
|
+
this.logger.info(`[a2a-adapter] task ${taskId} spawning sub-agent via sessions_spawn (cwd=${this.cwd}, sessionKey=a2a-${taskId})`);
|
|
76
|
+
const spawnResult = await (0, gateway_client_1.invokeGatewayTool)({
|
|
67
77
|
gateway: this.gatewayConfig,
|
|
68
78
|
tool: "sessions_spawn",
|
|
69
79
|
args: {
|
|
@@ -74,20 +84,22 @@ class OpenClawAgentExecutor {
|
|
|
74
84
|
sessionKey: `a2a-${taskId}`,
|
|
75
85
|
timeoutMs: 15_000,
|
|
76
86
|
});
|
|
77
|
-
this.logger.info(`[a2a-adapter] task ${taskId} spawned
|
|
87
|
+
this.logger.info(`[a2a-adapter] task ${taskId} sub-agent spawned — result=${JSON.stringify(spawnResult).slice(0, 200)}`);
|
|
88
|
+
this.logger.info(`[a2a-adapter] task ${taskId} waiting for callback from sub-agent...`);
|
|
78
89
|
// Wait for the sub-agent to call back
|
|
79
90
|
const output = await resultPromise;
|
|
80
91
|
// Return result
|
|
81
92
|
this.taskTracker.update(taskId, { status: "completed", result: output });
|
|
82
|
-
this.logger.info(`[a2a-adapter] task ${taskId} completed
|
|
93
|
+
this.logger.info(`[a2a-adapter] ✓ task ${taskId} completed — resultLen=${output.length}, preview=${output.slice(0, 120)}`);
|
|
83
94
|
this.publishMessage(eventBus, output || "Task completed with no output.");
|
|
84
95
|
}
|
|
85
96
|
catch (err) {
|
|
86
97
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
87
|
-
this.logger.error(`[a2a-adapter] task
|
|
98
|
+
this.logger.error(`[a2a-adapter] ✗ task ${taskId} failed: ${errorMsg}`);
|
|
88
99
|
this.taskTracker.update(taskId, { status: "failed", error: errorMsg });
|
|
89
100
|
this.publishMessage(eventBus, `Error: ${errorMsg}`);
|
|
90
101
|
}
|
|
102
|
+
this.logger.info(`[a2a-adapter] task ${taskId} eventBus.finished()`);
|
|
91
103
|
eventBus.finished();
|
|
92
104
|
}
|
|
93
105
|
/**
|
|
@@ -96,10 +108,13 @@ class OpenClawAgentExecutor {
|
|
|
96
108
|
*/
|
|
97
109
|
resolveCallback(taskId, result) {
|
|
98
110
|
const pending = this.pendingCallbacks.get(taskId);
|
|
99
|
-
if (!pending)
|
|
111
|
+
if (!pending) {
|
|
112
|
+
this.logger.warn(`[a2a-adapter] resolveCallback: no pending callback for taskId=${taskId} (may have timed out)`);
|
|
100
113
|
return false;
|
|
114
|
+
}
|
|
101
115
|
clearTimeout(pending.timer);
|
|
102
116
|
this.pendingCallbacks.delete(taskId);
|
|
117
|
+
this.logger.info(`[a2a-adapter] resolveCallback: taskId=${taskId} resolved — resultLen=${result.length}`);
|
|
103
118
|
pending.resolve(result);
|
|
104
119
|
return true;
|
|
105
120
|
}
|
|
@@ -111,6 +126,7 @@ class OpenClawAgentExecutor {
|
|
|
111
126
|
clearTimeout(pending.timer);
|
|
112
127
|
this.pendingCallbacks.delete(taskId);
|
|
113
128
|
pending.reject(new Error("canceled"));
|
|
129
|
+
this.logger.info(`[a2a-adapter] cancelTask: pending callback rejected for taskId=${taskId}`);
|
|
114
130
|
}
|
|
115
131
|
this.taskTracker.update(taskId, { status: "failed", error: "canceled" });
|
|
116
132
|
this.publishMessage(eventBus, "Task was canceled.");
|
|
@@ -127,6 +143,7 @@ class OpenClawAgentExecutor {
|
|
|
127
143
|
return new Promise((resolve, reject) => {
|
|
128
144
|
const timer = setTimeout(() => {
|
|
129
145
|
this.pendingCallbacks.delete(taskId);
|
|
146
|
+
this.logger.error(`[a2a-adapter] ✗ task ${taskId} callback timed out after ${timeoutMs / 1000}s — pending callbacks remaining: ${this.pendingCallbacks.size}`);
|
|
130
147
|
reject(new Error(`task timed out after ${timeoutMs / 1000}s waiting for sub-agent callback`));
|
|
131
148
|
}, timeoutMs);
|
|
132
149
|
this.pendingCallbacks.set(taskId, { resolve, reject, timer });
|
|
@@ -135,21 +152,36 @@ class OpenClawAgentExecutor {
|
|
|
135
152
|
/** Send a notification to all known targets. Individual failures are silently ignored. */
|
|
136
153
|
async notifyUser(message) {
|
|
137
154
|
const targets = this.getNotificationTargets();
|
|
138
|
-
if (!this.gatewayConfig || targets.size === 0)
|
|
155
|
+
if (!this.gatewayConfig || targets.size === 0) {
|
|
156
|
+
this.logger.info(`[a2a-adapter] notifyUser: skipped (gateway=${!!this.gatewayConfig}, targets=${targets.size})`);
|
|
139
157
|
return;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
158
|
+
}
|
|
159
|
+
const results = await Promise.allSettled([...targets.entries()].map(async ([key, target]) => {
|
|
160
|
+
this.logger.info(`[a2a-adapter] notifyUser: sending to ${key} (type=${target.type})`);
|
|
161
|
+
try {
|
|
162
|
+
await (target.type === "channel"
|
|
163
|
+
? (0, gateway_client_1.invokeGatewayTool)({
|
|
164
|
+
gateway: this.gatewayConfig,
|
|
165
|
+
tool: "message",
|
|
166
|
+
args: { action: "send", target: target.conversationId, message },
|
|
167
|
+
timeoutMs: 5_000,
|
|
168
|
+
})
|
|
169
|
+
: (0, gateway_client_1.invokeGatewayTool)({
|
|
170
|
+
gateway: this.gatewayConfig,
|
|
171
|
+
tool: "chat.send",
|
|
172
|
+
args: { sessionKey: target.sessionKey, message },
|
|
173
|
+
timeoutMs: 5_000,
|
|
174
|
+
}));
|
|
175
|
+
this.logger.info(`[a2a-adapter] notifyUser: sent to ${key} ✓`);
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
this.logger.warn(`[a2a-adapter] notifyUser: failed to send to ${key}: ${err instanceof Error ? err.message : String(err)}`);
|
|
179
|
+
throw err;
|
|
180
|
+
}
|
|
181
|
+
}));
|
|
182
|
+
const ok = results.filter((r) => r.status === "fulfilled").length;
|
|
183
|
+
const fail = results.filter((r) => r.status === "rejected").length;
|
|
184
|
+
this.logger.info(`[a2a-adapter] notifyUser: done (${ok} ok, ${fail} failed)`);
|
|
153
185
|
}
|
|
154
186
|
publishMessage(eventBus, text) {
|
|
155
187
|
const message = {
|
|
@@ -133,7 +133,7 @@ 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
|
-
/**
|
|
136
|
+
/** Resolve a pending A2A callback from sub-agent. */
|
|
137
137
|
resolveA2ACallback(taskId: string, result: string): boolean;
|
|
138
138
|
addNotificationTarget(key: string, target: NotificationTarget): void;
|
|
139
139
|
/** Send a notification to all known targets. Individual failures are silently ignored. */
|
|
@@ -256,26 +256,32 @@ class MulticlawsService extends node_events_1.EventEmitter {
|
|
|
256
256
|
/* Task delegation */
|
|
257
257
|
/* ---------------------------------------------------------------- */
|
|
258
258
|
async delegateTask(params) {
|
|
259
|
-
this.log("info", `delegateTask(agentUrl=${params.agentUrl},
|
|
259
|
+
this.log("info", `[delegate] ▶ delegateTask(agentUrl=${params.agentUrl}, taskLen=${params.task.length})`);
|
|
260
|
+
this.log("info", `[delegate] task preview: ${params.task.slice(0, 120)}`);
|
|
260
261
|
await this.requireCompleteProfile();
|
|
261
262
|
const agentRecord = await this.agentRegistry.get(params.agentUrl);
|
|
262
263
|
if (!agentRecord) {
|
|
263
|
-
this.log("warn", `
|
|
264
|
+
this.log("warn", `[delegate] ✗ unknown agent: ${params.agentUrl}`);
|
|
264
265
|
return { status: "failed", error: `unknown agent: ${params.agentUrl}` };
|
|
265
266
|
}
|
|
267
|
+
this.log("info", `[delegate] agent found: ${agentRecord.name} (${agentRecord.url})`);
|
|
266
268
|
const track = this.taskTracker.create({
|
|
267
269
|
fromPeerId: "local",
|
|
268
270
|
toPeerId: params.agentUrl,
|
|
269
271
|
task: params.task,
|
|
270
272
|
});
|
|
271
273
|
this.taskTracker.update(track.taskId, { status: "running" });
|
|
274
|
+
this.log("info", `[delegate] task tracked: ${track.taskId}, status=running`);
|
|
272
275
|
try {
|
|
276
|
+
this.log("info", `[delegate] ${track.taskId} creating A2A client for ${agentRecord.url}`);
|
|
273
277
|
const client = await this.createA2AClient(agentRecord);
|
|
278
|
+
this.log("info", `[delegate] ${track.taskId} A2A client created, starting fire-and-forget send`);
|
|
274
279
|
// Fire-and-forget execution: keep running in the background so that
|
|
275
280
|
// the gateway call can return quickly and the task can outlive
|
|
276
281
|
// the gateway's HTTP timeout.
|
|
277
282
|
void (async () => {
|
|
278
283
|
try {
|
|
284
|
+
this.log("info", `[delegate] ${track.taskId} sending A2A message (background)...`);
|
|
279
285
|
const result = await client.sendMessage({
|
|
280
286
|
message: {
|
|
281
287
|
kind: "message",
|
|
@@ -284,22 +290,24 @@ class MulticlawsService extends node_events_1.EventEmitter {
|
|
|
284
290
|
messageId: track.taskId,
|
|
285
291
|
},
|
|
286
292
|
});
|
|
293
|
+
this.log("info", `[delegate] ${track.taskId} A2A response received (background)`);
|
|
287
294
|
this.processTaskResult(track.taskId, result);
|
|
288
295
|
}
|
|
289
296
|
catch (err) {
|
|
290
297
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
291
298
|
this.taskTracker.update(track.taskId, { status: "failed", error: errorMsg });
|
|
292
|
-
this.log("
|
|
299
|
+
this.log("error", `[delegate] ✗ ${track.taskId} background send failed: ${errorMsg}`);
|
|
293
300
|
}
|
|
294
301
|
})();
|
|
295
302
|
// Return immediately so that gateway tool invocations are fast and
|
|
296
303
|
// do not depend on the remote agent's total execution time.
|
|
304
|
+
this.log("info", `[delegate] ${track.taskId} returned immediately (fire-and-forget)`);
|
|
297
305
|
return { taskId: track.taskId, status: "running" };
|
|
298
306
|
}
|
|
299
307
|
catch (err) {
|
|
300
308
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
301
309
|
this.taskTracker.update(track.taskId, { status: "failed", error: errorMsg });
|
|
302
|
-
this.log("error", `
|
|
310
|
+
this.log("error", `[delegate] ✗ ${track.taskId} failed: ${errorMsg}`);
|
|
303
311
|
return { taskId: track.taskId, status: "failed", error: errorMsg };
|
|
304
312
|
}
|
|
305
313
|
}
|
|
@@ -308,37 +316,47 @@ class MulticlawsService extends node_events_1.EventEmitter {
|
|
|
308
316
|
* Used by sub-agents internally via the multiclaws_delegate_send tool.
|
|
309
317
|
*/
|
|
310
318
|
async delegateTaskSync(params) {
|
|
311
|
-
this.log("info", `delegateTaskSync(agentUrl=${params.agentUrl},
|
|
319
|
+
this.log("info", `[delegate-sync] ▶ delegateTaskSync(agentUrl=${params.agentUrl}, taskLen=${params.task.length})`);
|
|
320
|
+
this.log("info", `[delegate-sync] task preview: ${params.task.slice(0, 120)}`);
|
|
312
321
|
await this.requireCompleteProfile();
|
|
313
322
|
const agentRecord = await this.agentRegistry.get(params.agentUrl);
|
|
314
323
|
if (!agentRecord) {
|
|
315
|
-
this.log("warn", `
|
|
324
|
+
this.log("warn", `[delegate-sync] ✗ unknown agent: ${params.agentUrl}`);
|
|
316
325
|
return { status: "failed", error: `unknown agent: ${params.agentUrl}` };
|
|
317
326
|
}
|
|
327
|
+
this.log("info", `[delegate-sync] agent found: ${agentRecord.name} (${agentRecord.url})`);
|
|
318
328
|
const track = this.taskTracker.create({
|
|
319
329
|
fromPeerId: "local",
|
|
320
330
|
toPeerId: params.agentUrl,
|
|
321
331
|
task: params.task,
|
|
322
332
|
});
|
|
323
333
|
this.taskTracker.update(track.taskId, { status: "running" });
|
|
334
|
+
this.log("info", `[delegate-sync] task tracked: ${track.taskId}, status=running`);
|
|
324
335
|
try {
|
|
336
|
+
this.log("info", `[delegate-sync] ${track.taskId} creating A2A client for ${agentRecord.url}`);
|
|
325
337
|
const client = await this.createA2AClient(agentRecord);
|
|
338
|
+
this.log("info", `[delegate-sync] ${track.taskId} sending A2A message (sync, with metadata: selfUrl=${this.selfUrl}, selfName=${this.agentCard?.name ?? "unknown"})...`);
|
|
326
339
|
const result = await client.sendMessage({
|
|
327
340
|
message: {
|
|
328
341
|
kind: "message",
|
|
329
342
|
role: "user",
|
|
330
343
|
parts: [{ kind: "text", text: params.task }],
|
|
331
344
|
messageId: track.taskId,
|
|
345
|
+
metadata: {
|
|
346
|
+
agentUrl: this.selfUrl,
|
|
347
|
+
agentName: this.agentCard?.name ?? "unknown",
|
|
348
|
+
},
|
|
332
349
|
},
|
|
333
350
|
});
|
|
351
|
+
this.log("info", `[delegate-sync] ${track.taskId} A2A response received`);
|
|
334
352
|
const taskResult = this.processTaskResult(track.taskId, result);
|
|
335
|
-
this.log("
|
|
353
|
+
this.log("info", `[delegate-sync] ✓ ${track.taskId} completed — status=${taskResult.status}, outputLen=${taskResult.output?.length ?? 0}`);
|
|
336
354
|
return taskResult;
|
|
337
355
|
}
|
|
338
356
|
catch (err) {
|
|
339
357
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
340
358
|
this.taskTracker.update(track.taskId, { status: "failed", error: errorMsg });
|
|
341
|
-
this.log("error", `
|
|
359
|
+
this.log("error", `[delegate-sync] ✗ ${track.taskId} failed: ${errorMsg}`);
|
|
342
360
|
return { taskId: track.taskId, status: "failed", error: errorMsg };
|
|
343
361
|
}
|
|
344
362
|
}
|
|
@@ -348,26 +366,30 @@ class MulticlawsService extends node_events_1.EventEmitter {
|
|
|
348
366
|
* reports results back to the user via the message tool.
|
|
349
367
|
*/
|
|
350
368
|
async spawnDelegation(params) {
|
|
351
|
-
this.log("info", `spawnDelegation(agentUrl=${params.agentUrl},
|
|
369
|
+
this.log("info", `[spawn-delegate] ▶ spawnDelegation(agentUrl=${params.agentUrl}, taskLen=${params.task.length})`);
|
|
370
|
+
this.log("info", `[spawn-delegate] task preview: ${params.task.slice(0, 120)}`);
|
|
352
371
|
await this.requireCompleteProfile();
|
|
353
372
|
const agent = await this.agentRegistry.get(params.agentUrl);
|
|
354
373
|
if (!agent) {
|
|
355
|
-
this.log("warn", `
|
|
374
|
+
this.log("warn", `[spawn-delegate] ✗ unknown agent: ${params.agentUrl}`);
|
|
356
375
|
throw new Error(`unknown agent: ${params.agentUrl}`);
|
|
357
376
|
}
|
|
377
|
+
this.log("info", `[spawn-delegate] agent found: ${agent.name} (${agent.url})`);
|
|
358
378
|
if (!this.gatewayConfig) {
|
|
359
|
-
this.log("error",
|
|
379
|
+
this.log("error", `[spawn-delegate] ✗ gateway config not available`);
|
|
360
380
|
throw new Error("gateway config not available — cannot spawn sub-agent");
|
|
361
381
|
}
|
|
362
382
|
const prompt = buildDelegationPrompt(agent, params.task);
|
|
363
|
-
|
|
383
|
+
const sessionKey = `delegate-${Date.now()}`;
|
|
384
|
+
this.log("info", `[spawn-delegate] spawning sub-agent via sessions_spawn (cwd=${this.resolvedCwd}, sessionKey=${sessionKey}, promptLen=${prompt.length})`);
|
|
385
|
+
const spawnResult = await (0, gateway_client_1.invokeGatewayTool)({
|
|
364
386
|
gateway: this.gatewayConfig,
|
|
365
387
|
tool: "sessions_spawn",
|
|
366
388
|
args: { task: prompt, mode: "run", cwd: this.resolvedCwd },
|
|
367
|
-
sessionKey
|
|
389
|
+
sessionKey,
|
|
368
390
|
timeoutMs: 15_000,
|
|
369
391
|
});
|
|
370
|
-
this.log("info", `
|
|
392
|
+
this.log("info", `[spawn-delegate] ✓ sub-agent spawned for ${agent.name} — result=${JSON.stringify(spawnResult).slice(0, 200)}`);
|
|
371
393
|
return { message: `已启动子 agent 向 ${agent.name} 委派任务` };
|
|
372
394
|
}
|
|
373
395
|
getTaskStatus(taskId) {
|
|
@@ -858,24 +880,27 @@ class MulticlawsService extends node_events_1.EventEmitter {
|
|
|
858
880
|
* return the final Task or Message as soon as B signals completion.
|
|
859
881
|
*/
|
|
860
882
|
processTaskResult(trackId, result) {
|
|
861
|
-
this.log("
|
|
883
|
+
this.log("info", `[process-result] processing result for ${trackId}, resultType=${("status" in result && result.status) ? "Task" : "Message"}`);
|
|
862
884
|
try {
|
|
863
885
|
if ("status" in result && result.status) {
|
|
864
886
|
const task = result;
|
|
865
887
|
const state = task.status?.state ?? "unknown";
|
|
866
888
|
const output = this.extractArtifactText(task);
|
|
889
|
+
this.log("info", `[process-result] ${trackId} Task response — state=${state}, outputLen=${output.length}, preview=${output.slice(0, 120)}`);
|
|
867
890
|
if (state === "completed") {
|
|
868
891
|
this.taskTracker.update(trackId, { status: "completed", result: output });
|
|
892
|
+
this.log("info", `[process-result] ✓ ${trackId} marked completed`);
|
|
869
893
|
}
|
|
870
894
|
else if (state === "failed") {
|
|
871
895
|
this.taskTracker.update(trackId, { status: "failed", error: output || "remote task failed" });
|
|
896
|
+
this.log("warn", `[process-result] ✗ ${trackId} marked failed — error=${output || "remote task failed"}`);
|
|
872
897
|
}
|
|
873
898
|
else {
|
|
874
899
|
// For any other state (unknown, working, etc.), mark as failed to avoid
|
|
875
900
|
// tasks stuck in "running" forever until TTL prune.
|
|
876
901
|
this.taskTracker.update(trackId, { status: "failed", error: `unexpected remote state: ${state}` });
|
|
902
|
+
this.log("warn", `[process-result] ✗ ${trackId} unexpected state=${state}, marked failed`);
|
|
877
903
|
}
|
|
878
|
-
this.log("debug", `processTaskResult completed, status=${state}`);
|
|
879
904
|
return { taskId: task.id, output, status: state };
|
|
880
905
|
}
|
|
881
906
|
const msg = result;
|
|
@@ -884,11 +909,11 @@ class MulticlawsService extends node_events_1.EventEmitter {
|
|
|
884
909
|
.map((p) => p.text)
|
|
885
910
|
.join("\n") ?? "";
|
|
886
911
|
this.taskTracker.update(trackId, { status: "completed", result: text });
|
|
887
|
-
this.log("
|
|
912
|
+
this.log("info", `[process-result] ✓ ${trackId} Message response — completed, textLen=${text.length}, preview=${text.slice(0, 120)}`);
|
|
888
913
|
return { taskId: trackId, output: text, status: "completed" };
|
|
889
914
|
}
|
|
890
915
|
catch (err) {
|
|
891
|
-
this.log("error", `
|
|
916
|
+
this.log("error", `[process-result] ✗ ${trackId} processing failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
892
917
|
throw err;
|
|
893
918
|
}
|
|
894
919
|
}
|
|
@@ -920,11 +945,16 @@ class MulticlawsService extends node_events_1.EventEmitter {
|
|
|
920
945
|
}
|
|
921
946
|
throw lastError;
|
|
922
947
|
}
|
|
923
|
-
/**
|
|
948
|
+
/** Resolve a pending A2A callback from sub-agent. */
|
|
924
949
|
resolveA2ACallback(taskId, result) {
|
|
925
|
-
|
|
950
|
+
this.log("info", `[a2a-callback] resolveA2ACallback(taskId=${taskId}, resultLen=${result.length})`);
|
|
951
|
+
if (!this.agentExecutor) {
|
|
952
|
+
this.log("warn", `[a2a-callback] ✗ no agentExecutor available for taskId=${taskId}`);
|
|
926
953
|
return false;
|
|
927
|
-
|
|
954
|
+
}
|
|
955
|
+
const resolved = this.agentExecutor.resolveCallback(taskId, result);
|
|
956
|
+
this.log("info", `[a2a-callback] ${resolved ? "✓" : "✗"} taskId=${taskId} ${resolved ? "resolved" : "no pending callback found"}`);
|
|
957
|
+
return resolved;
|
|
928
958
|
}
|
|
929
959
|
addNotificationTarget(key, target) {
|
|
930
960
|
if (!this.notificationTargets.has(key)) {
|