@mastra/server 1.58.0-alpha.0 → 1.58.0-alpha.3
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/CHANGELOG.md +119 -0
- package/dist/_types/@mastra_schema-compat/dist/provider-compats/anthropic.d.ts +1 -1
- package/dist/{a2a-CnU9zaGR.js → a2a-8C4O2dVJ.js} +41 -5
- package/dist/a2a-8C4O2dVJ.js.map +1 -0
- package/dist/{a2a-DKnwIgiw.cjs → a2a-B6YkZ2i9.cjs} +872 -227
- package/dist/a2a-B6YkZ2i9.cjs.map +1 -0
- package/dist/{a2a-oM7aqLz4.cjs → a2a-MPoM5lzS.cjs} +41 -5
- package/dist/a2a-MPoM5lzS.cjs.map +1 -0
- package/dist/{a2a-uPi5sPwb.js → a2a-_D2EA4oa.js} +861 -228
- package/dist/a2a-_D2EA4oa.js.map +1 -0
- package/dist/{agent-builder-BDYN3r89.cjs → agent-builder-DAiJH1u2.cjs} +2 -2
- package/dist/{agent-builder-BDYN3r89.cjs.map → agent-builder-DAiJH1u2.cjs.map} +1 -1
- package/dist/{agent-builder-Dqy_qumV.js → agent-builder-gGFywDVq.js} +2 -2
- package/dist/{agent-builder-Dqy_qumV.js.map → agent-builder-gGFywDVq.js.map} +1 -1
- package/dist/{api-schema-manifest-7-NC9Dyc.cjs → api-schema-manifest-B3bC-D98.cjs} +2 -2
- package/dist/{api-schema-manifest-7-NC9Dyc.cjs.map → api-schema-manifest-B3bC-D98.cjs.map} +1 -1
- package/dist/{api-schema-manifest-C5hpdIE_.js → api-schema-manifest-B4J_QYOr.js} +2 -2
- package/dist/{api-schema-manifest-C5hpdIE_.js.map → api-schema-manifest-B4J_QYOr.js.map} +1 -1
- package/dist/{datasets-Cvix7Rms.cjs → datasets-BMeFvFwT.cjs} +43 -1
- package/dist/datasets-BMeFvFwT.cjs.map +1 -0
- package/dist/{datasets-dhXnHLAL.js → datasets-BeBH9Seg.js} +38 -2
- package/dist/datasets-BeBH9Seg.js.map +1 -0
- package/dist/{dist-DGqlUp0b.js → dist-BKRMiJcn.js} +215 -74
- package/dist/dist-BKRMiJcn.js.map +1 -0
- package/dist/{dist-COHIL6fR.cjs → dist-C2-SedDQ.cjs} +215 -74
- package/dist/dist-C2-SedDQ.cjs.map +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-agents-a2a.md +75 -2
- package/dist/docs/references/reference-server-routes.md +25 -11
- package/dist/route-builder-CIk5k35M.js.map +1 -1
- package/dist/route-builder-CgLVYL1c.cjs.map +1 -1
- package/dist/{routes-hSY4wTeS.cjs → routes-CZwPlO3-.cjs} +3 -2
- package/dist/routes-CZwPlO3-.cjs.map +1 -0
- package/dist/{routes-CRoTKPvQ.js → routes-HUaCyUb7.js} +4 -3
- package/dist/{routes-CRoTKPvQ.js.map → routes-HUaCyUb7.js.map} +1 -1
- package/dist/server/a2a/store.cjs +30 -2
- package/dist/server/a2a/store.cjs.map +1 -1
- package/dist/server/a2a/store.d.ts +21 -2
- package/dist/server/a2a/store.d.ts.map +1 -1
- package/dist/server/a2a/store.js +30 -3
- package/dist/server/a2a/store.js.map +1 -1
- package/dist/server/a2a/task-state.d.ts +4 -0
- package/dist/server/a2a/task-state.d.ts.map +1 -0
- package/dist/server/a2a/tasks.d.ts +1 -1
- package/dist/server/a2a/tasks.d.ts.map +1 -1
- package/dist/server/handlers/a2a.cjs +3 -1
- package/dist/server/handlers/a2a.d.ts +155 -12
- package/dist/server/handlers/a2a.d.ts.map +1 -1
- package/dist/server/handlers/a2a.js +2 -2
- package/dist/server/handlers/agent-builder.cjs +3 -3
- package/dist/server/handlers/agent-builder.js +3 -3
- package/dist/server/handlers/agent-controller.cjs +25 -6
- package/dist/server/handlers/agent-controller.cjs.map +1 -1
- package/dist/server/handlers/agent-controller.d.ts +4 -3
- package/dist/server/handlers/agent-controller.d.ts.map +1 -1
- package/dist/server/handlers/agent-controller.js +25 -6
- package/dist/server/handlers/agent-controller.js.map +1 -1
- package/dist/server/handlers/agent-versions.cjs +6 -0
- package/dist/server/handlers/agent-versions.cjs.map +1 -1
- package/dist/server/handlers/agent-versions.d.ts.map +1 -1
- package/dist/server/handlers/agent-versions.js +6 -0
- package/dist/server/handlers/agent-versions.js.map +1 -1
- package/dist/server/handlers/datasets.cjs +26 -11
- package/dist/server/handlers/datasets.cjs.map +1 -1
- package/dist/server/handlers/datasets.d.ts +136 -0
- package/dist/server/handlers/datasets.d.ts.map +1 -1
- package/dist/server/handlers/datasets.js +26 -11
- package/dist/server/handlers/datasets.js.map +1 -1
- package/dist/server/handlers/observability.cjs +20 -9
- package/dist/server/handlers/observability.cjs.map +1 -1
- package/dist/server/handlers/observability.d.ts.map +1 -1
- package/dist/server/handlers/observability.js +20 -9
- package/dist/server/handlers/observability.js.map +1 -1
- package/dist/server/handlers/stored-agents.cjs +16 -1
- package/dist/server/handlers/stored-agents.cjs.map +1 -1
- package/dist/server/handlers/stored-agents.d.ts.map +1 -1
- package/dist/server/handlers/stored-agents.js +16 -1
- package/dist/server/handlers/stored-agents.js.map +1 -1
- package/dist/server/handlers/system.cjs +1 -1
- package/dist/server/handlers/system.js +1 -1
- package/dist/server/handlers/validate-agent-instructions.cjs +52 -0
- package/dist/server/handlers/validate-agent-instructions.cjs.map +1 -0
- package/dist/server/handlers/validate-agent-instructions.d.ts +13 -0
- package/dist/server/handlers/validate-agent-instructions.d.ts.map +1 -0
- package/dist/server/handlers/validate-agent-instructions.js +51 -0
- package/dist/server/handlers/validate-agent-instructions.js.map +1 -0
- package/dist/server/handlers/workflows.cjs +105 -2
- package/dist/server/handlers/workflows.cjs.map +1 -1
- package/dist/server/handlers/workflows.d.ts +9 -0
- package/dist/server/handlers/workflows.d.ts.map +1 -1
- package/dist/server/handlers/workflows.js +104 -3
- package/dist/server/handlers/workflows.js.map +1 -1
- package/dist/server/handlers.cjs +1 -1
- package/dist/server/handlers.js +1 -1
- package/dist/server/schemas/a2a.d.ts +70 -6
- package/dist/server/schemas/a2a.d.ts.map +1 -1
- package/dist/server/schemas/datasets.d.ts +53 -0
- package/dist/server/schemas/datasets.d.ts.map +1 -1
- package/dist/server/schemas/index.cjs +5 -4
- package/dist/server/schemas/index.js +5 -5
- package/dist/server/schemas/workflows.d.ts +8 -0
- package/dist/server/schemas/workflows.d.ts.map +1 -1
- package/dist/server/server-adapter/index.cjs +1 -1
- package/dist/server/server-adapter/index.js +1 -1
- package/dist/server/server-adapter/routes/a2a.d.ts +116 -8
- package/dist/server/server-adapter/routes/a2a.d.ts.map +1 -1
- package/dist/server/server-adapter/routes/datasets.d.ts +136 -0
- package/dist/server/server-adapter/routes/datasets.d.ts.map +1 -1
- package/dist/server/server-adapter/routes/workflows.d.ts +7 -1
- package/dist/server/server-adapter/routes/workflows.d.ts.map +1 -1
- package/dist/{workflows-DPW6FrY0.js → workflows-CMU5dz_4.js} +7 -2
- package/dist/workflows-CMU5dz_4.js.map +1 -0
- package/dist/{workflows-XfiENvkF.cjs → workflows-Dzxr6cmf.cjs} +12 -1
- package/dist/workflows-Dzxr6cmf.cjs.map +1 -0
- package/package.json +8 -8
- package/dist/a2a-CnU9zaGR.js.map +0 -1
- package/dist/a2a-DKnwIgiw.cjs.map +0 -1
- package/dist/a2a-oM7aqLz4.cjs.map +0 -1
- package/dist/a2a-uPi5sPwb.js.map +0 -1
- package/dist/datasets-Cvix7Rms.cjs.map +0 -1
- package/dist/datasets-dhXnHLAL.js.map +0 -1
- package/dist/dist-COHIL6fR.cjs.map +0 -1
- package/dist/dist-DGqlUp0b.js.map +0 -1
- package/dist/routes-hSY4wTeS.cjs.map +0 -1
- package/dist/workflows-DPW6FrY0.js.map +0 -1
- package/dist/workflows-XfiENvkF.cjs.map +0 -1
|
@@ -2,7 +2,8 @@ const require_rolldown_runtime = require("./rolldown-runtime-CVvi-lCc.cjs");
|
|
|
2
2
|
const require_route_builder = require("./route-builder-CgLVYL1c.cjs");
|
|
3
3
|
const require_utils = require("./utils-wESuBh27.cjs");
|
|
4
4
|
const require_server_handlers_agents = require("./server/handlers/agents.cjs");
|
|
5
|
-
const
|
|
5
|
+
const require_server_a2a_store = require("./server/a2a/store.cjs");
|
|
6
|
+
const require_a2a = require("./a2a-MPoM5lzS.cjs");
|
|
6
7
|
const require_server_handlers_auth = require("./server/handlers/auth.cjs");
|
|
7
8
|
let zod_v4 = require("zod/v4");
|
|
8
9
|
let util = require("util");
|
|
@@ -118,7 +119,7 @@ function createErrorResponse(id, error) {
|
|
|
118
119
|
};
|
|
119
120
|
}
|
|
120
121
|
function createSuccessResponse(id, result) {
|
|
121
|
-
if (
|
|
122
|
+
if (id === null) throw _mastra_core_a2a.MastraA2AError.internalError("Cannot create success response for null ID.");
|
|
122
123
|
return {
|
|
123
124
|
jsonrpc: "2.0",
|
|
124
125
|
id,
|
|
@@ -306,6 +307,14 @@ var InMemoryPushNotificationStore = class {
|
|
|
306
307
|
}
|
|
307
308
|
};
|
|
308
309
|
//#endregion
|
|
310
|
+
//#region src/server/a2a/task-state.ts
|
|
311
|
+
function isTerminalTaskState(state) {
|
|
312
|
+
return state === "completed" || state === "failed" || state === "canceled" || state === "rejected";
|
|
313
|
+
}
|
|
314
|
+
function isInterruptedTaskState(state) {
|
|
315
|
+
return state === "input-required" || state === "auth-required";
|
|
316
|
+
}
|
|
317
|
+
//#endregion
|
|
309
318
|
//#region src/server/a2a/tasks.ts
|
|
310
319
|
function isTaskStatusUpdate(update) {
|
|
311
320
|
return "state" in update && !("parts" in update);
|
|
@@ -341,54 +350,63 @@ function applyUpdateToTask(current, update) {
|
|
|
341
350
|
return newTask;
|
|
342
351
|
}
|
|
343
352
|
async function loadOrCreateTask({ agentId, taskId, taskStore, message, contextId, metadata, logger }) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
taskId
|
|
347
|
-
});
|
|
348
|
-
if (!data) {
|
|
349
|
-
const initialTask = {
|
|
350
|
-
id: taskId,
|
|
351
|
-
contextId: contextId || crypto.randomUUID(),
|
|
352
|
-
status: {
|
|
353
|
-
state: "submitted",
|
|
354
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
355
|
-
message: void 0
|
|
356
|
-
},
|
|
357
|
-
artifacts: [],
|
|
358
|
-
history: [message],
|
|
359
|
-
metadata,
|
|
360
|
-
kind: "task"
|
|
361
|
-
};
|
|
362
|
-
logger?.info(`[Task ${taskId}] Created new task.`);
|
|
363
|
-
await taskStore.save({
|
|
353
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
354
|
+
const snapshot = taskStore.loadWithVersion({
|
|
364
355
|
agentId,
|
|
365
|
-
|
|
356
|
+
taskId
|
|
366
357
|
});
|
|
367
|
-
|
|
358
|
+
const data = snapshot?.task;
|
|
359
|
+
if (!data) {
|
|
360
|
+
const initialTask = {
|
|
361
|
+
id: taskId,
|
|
362
|
+
contextId: contextId || crypto.randomUUID(),
|
|
363
|
+
status: {
|
|
364
|
+
state: "submitted",
|
|
365
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
366
|
+
message: void 0
|
|
367
|
+
},
|
|
368
|
+
artifacts: [],
|
|
369
|
+
history: [message],
|
|
370
|
+
metadata,
|
|
371
|
+
kind: "task"
|
|
372
|
+
};
|
|
373
|
+
logger?.info(`[Task ${taskId}] Created new task.`);
|
|
374
|
+
try {
|
|
375
|
+
await taskStore.save({
|
|
376
|
+
agentId,
|
|
377
|
+
data: initialTask,
|
|
378
|
+
expectedVersion: 0
|
|
379
|
+
});
|
|
380
|
+
return initialTask;
|
|
381
|
+
} catch (error) {
|
|
382
|
+
if (error instanceof require_server_a2a_store.TaskStoreVersionConflictError) continue;
|
|
383
|
+
throw error;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
logger?.info(`[Task ${taskId}] Loaded existing task.`);
|
|
387
|
+
const { status } = data;
|
|
388
|
+
if (isTerminalTaskState(status.state)) throw _mastra_core_a2a.MastraA2AError.invalidRequest(`Task ${taskId} is in terminal state ${status.state} and cannot be restarted.`);
|
|
389
|
+
let updatedData = {
|
|
390
|
+
...data,
|
|
391
|
+
history: [...data.history || [], message]
|
|
392
|
+
};
|
|
393
|
+
if (status.state === "input-required" || status.state === "auth-required") {
|
|
394
|
+
logger?.info(`[Task ${taskId}] Changing state from '${status.state}' to 'working'.`);
|
|
395
|
+
updatedData = applyUpdateToTask(updatedData, { state: "working" });
|
|
396
|
+
} else if (status.state === "working") logger?.warn(`[Task ${taskId}] Received message while already 'working'. Proceeding.`);
|
|
397
|
+
try {
|
|
398
|
+
await taskStore.save({
|
|
399
|
+
agentId,
|
|
400
|
+
data: updatedData,
|
|
401
|
+
expectedVersion: snapshot.version
|
|
402
|
+
});
|
|
403
|
+
return updatedData;
|
|
404
|
+
} catch (error) {
|
|
405
|
+
if (error instanceof require_server_a2a_store.TaskStoreVersionConflictError) continue;
|
|
406
|
+
throw error;
|
|
407
|
+
}
|
|
368
408
|
}
|
|
369
|
-
|
|
370
|
-
let updatedData = data;
|
|
371
|
-
updatedData.history = [...data.history || [], message];
|
|
372
|
-
const { status } = data;
|
|
373
|
-
if ([
|
|
374
|
-
"completed",
|
|
375
|
-
"failed",
|
|
376
|
-
"canceled"
|
|
377
|
-
].includes(status.state)) {
|
|
378
|
-
logger?.warn(`[Task ${taskId}] Received message for task in final state ${status.state}. Restarting.`);
|
|
379
|
-
updatedData = applyUpdateToTask(updatedData, {
|
|
380
|
-
state: "submitted",
|
|
381
|
-
message: void 0
|
|
382
|
-
});
|
|
383
|
-
} else if (status.state === "input-required") {
|
|
384
|
-
logger?.info(`[Task ${taskId}] Changing state from 'input-required' to 'working'.`);
|
|
385
|
-
updatedData = applyUpdateToTask(updatedData, { state: "working" });
|
|
386
|
-
} else if (status.state === "working") logger?.warn(`[Task ${taskId}] Received message while already 'working'. Proceeding.`);
|
|
387
|
-
await taskStore.save({
|
|
388
|
-
agentId,
|
|
389
|
-
data: updatedData
|
|
390
|
-
});
|
|
391
|
-
return updatedData;
|
|
409
|
+
throw _mastra_core_a2a.MastraA2AError.invalidRequest(`Task ${taskId} was updated concurrently. Retry the request.`);
|
|
392
410
|
}
|
|
393
411
|
//#endregion
|
|
394
412
|
//#region src/server/handlers/a2a.ts
|
|
@@ -405,7 +423,9 @@ var a2a_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
|
|
|
405
423
|
handleSetTaskPushNotificationConfig: () => handleSetTaskPushNotificationConfig,
|
|
406
424
|
handleTaskCancel: () => handleTaskCancel,
|
|
407
425
|
handleTaskGet: () => handleTaskGet,
|
|
408
|
-
|
|
426
|
+
handleTaskList: () => handleTaskList,
|
|
427
|
+
handleTaskResubscribe: () => handleTaskResubscribe,
|
|
428
|
+
resolveA2AProtocolVersion: () => resolveA2AProtocolVersion
|
|
409
429
|
});
|
|
410
430
|
const messagePartSchema = zod_v4.z.discriminatedUnion("kind", [
|
|
411
431
|
zod_v4.z.object({
|
|
@@ -447,6 +467,7 @@ const messageSendParamsSchema = zod_v4.z.object({
|
|
|
447
467
|
configuration: zod_v4.z.object({
|
|
448
468
|
acceptedOutputModes: zod_v4.z.array(zod_v4.z.string()).optional(),
|
|
449
469
|
blocking: zod_v4.z.boolean().optional(),
|
|
470
|
+
returnImmediately: zod_v4.z.boolean().optional(),
|
|
450
471
|
historyLength: zod_v4.z.number().optional(),
|
|
451
472
|
pushNotificationConfig: zod_v4.z.object({
|
|
452
473
|
url: zod_v4.z.string(),
|
|
@@ -461,6 +482,159 @@ const messageSendParamsSchema = zod_v4.z.object({
|
|
|
461
482
|
});
|
|
462
483
|
const defaultPushNotificationStore = new InMemoryPushNotificationStore();
|
|
463
484
|
const defaultPushNotificationSender = new DefaultPushNotificationSender(defaultPushNotificationStore);
|
|
485
|
+
const V1_TASK_STATES = {
|
|
486
|
+
submitted: "TASK_STATE_SUBMITTED",
|
|
487
|
+
working: "TASK_STATE_WORKING",
|
|
488
|
+
completed: "TASK_STATE_COMPLETED",
|
|
489
|
+
failed: "TASK_STATE_FAILED",
|
|
490
|
+
canceled: "TASK_STATE_CANCELED",
|
|
491
|
+
"input-required": "TASK_STATE_INPUT_REQUIRED",
|
|
492
|
+
rejected: "TASK_STATE_REJECTED",
|
|
493
|
+
"auth-required": "TASK_STATE_AUTH_REQUIRED"
|
|
494
|
+
};
|
|
495
|
+
function normalizeV1Part(part) {
|
|
496
|
+
if ("text" in part) return {
|
|
497
|
+
kind: "text",
|
|
498
|
+
text: part.text,
|
|
499
|
+
metadata: part.metadata
|
|
500
|
+
};
|
|
501
|
+
if ("raw" in part) return {
|
|
502
|
+
kind: "file",
|
|
503
|
+
file: {
|
|
504
|
+
bytes: part.raw,
|
|
505
|
+
mimeType: part.mediaType,
|
|
506
|
+
name: part.filename
|
|
507
|
+
},
|
|
508
|
+
metadata: part.metadata
|
|
509
|
+
};
|
|
510
|
+
if ("url" in part) return {
|
|
511
|
+
kind: "file",
|
|
512
|
+
file: {
|
|
513
|
+
uri: part.url,
|
|
514
|
+
mimeType: part.mediaType,
|
|
515
|
+
name: part.filename
|
|
516
|
+
},
|
|
517
|
+
metadata: part.metadata
|
|
518
|
+
};
|
|
519
|
+
return {
|
|
520
|
+
kind: "data",
|
|
521
|
+
data: part.data,
|
|
522
|
+
metadata: part.metadata
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
function normalizeV1Params(params) {
|
|
526
|
+
if (!params?.message) return params;
|
|
527
|
+
const configuration = params.configuration;
|
|
528
|
+
return {
|
|
529
|
+
...params,
|
|
530
|
+
message: {
|
|
531
|
+
...params.message,
|
|
532
|
+
kind: "message",
|
|
533
|
+
role: params.message.role === "ROLE_AGENT" ? "agent" : "user",
|
|
534
|
+
parts: params.message.parts.map((part) => normalizeV1Part(part))
|
|
535
|
+
},
|
|
536
|
+
configuration: configuration ? {
|
|
537
|
+
...configuration,
|
|
538
|
+
blocking: configuration.returnImmediately === void 0 ? void 0 : !configuration.returnImmediately,
|
|
539
|
+
pushNotificationConfig: configuration.taskPushNotificationConfig
|
|
540
|
+
} : void 0
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
function toV1Part(part) {
|
|
544
|
+
if (part.kind === "text") return {
|
|
545
|
+
text: part.text,
|
|
546
|
+
metadata: part.metadata
|
|
547
|
+
};
|
|
548
|
+
if (part.kind === "file") return "uri" in part.file ? {
|
|
549
|
+
url: part.file.uri,
|
|
550
|
+
filename: part.file.name,
|
|
551
|
+
mediaType: part.file.mimeType,
|
|
552
|
+
metadata: part.metadata
|
|
553
|
+
} : {
|
|
554
|
+
raw: part.file.bytes,
|
|
555
|
+
filename: part.file.name,
|
|
556
|
+
mediaType: part.file.mimeType,
|
|
557
|
+
metadata: part.metadata
|
|
558
|
+
};
|
|
559
|
+
return {
|
|
560
|
+
data: part.data,
|
|
561
|
+
metadata: part.metadata
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
function toV1Message(message) {
|
|
565
|
+
if (!message) return void 0;
|
|
566
|
+
return {
|
|
567
|
+
messageId: message.messageId,
|
|
568
|
+
contextId: message.contextId,
|
|
569
|
+
taskId: message.taskId,
|
|
570
|
+
role: message.role === "agent" ? "ROLE_AGENT" : "ROLE_USER",
|
|
571
|
+
parts: message.parts?.map(toV1Part),
|
|
572
|
+
metadata: message.metadata,
|
|
573
|
+
extensions: message.extensions,
|
|
574
|
+
referenceTaskIds: message.referenceTaskIds
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
function toV1Task(task, { includeArtifacts = true, historyLength } = {}) {
|
|
578
|
+
return {
|
|
579
|
+
id: task.id,
|
|
580
|
+
contextId: task.contextId,
|
|
581
|
+
status: {
|
|
582
|
+
state: V1_TASK_STATES[task.status.state] ?? "TASK_STATE_UNSPECIFIED",
|
|
583
|
+
message: toV1Message(task.status.message),
|
|
584
|
+
timestamp: task.status.timestamp
|
|
585
|
+
},
|
|
586
|
+
artifacts: includeArtifacts ? task.artifacts?.map((artifact) => ({
|
|
587
|
+
artifactId: artifact.artifactId,
|
|
588
|
+
name: artifact.name,
|
|
589
|
+
description: artifact.description,
|
|
590
|
+
parts: artifact.parts?.map(toV1Part),
|
|
591
|
+
metadata: artifact.metadata,
|
|
592
|
+
extensions: artifact.extensions
|
|
593
|
+
})) : void 0,
|
|
594
|
+
history: task.history?.slice(historyLength === void 0 ? 0 : -historyLength).map(toV1Message),
|
|
595
|
+
metadata: task.metadata
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function toV1Result(result, method) {
|
|
599
|
+
if (method === "message/send") return result?.kind === "message" ? { message: toV1Message(result) } : { task: toV1Task(result) };
|
|
600
|
+
if (method === "message/stream" || method === "tasks/resubscribe") {
|
|
601
|
+
if (result?.kind === "status-update") return { statusUpdate: {
|
|
602
|
+
taskId: result.taskId,
|
|
603
|
+
contextId: result.contextId,
|
|
604
|
+
status: {
|
|
605
|
+
state: V1_TASK_STATES[result.status.state] ?? "TASK_STATE_UNSPECIFIED",
|
|
606
|
+
message: toV1Message(result.status.message),
|
|
607
|
+
timestamp: result.status.timestamp
|
|
608
|
+
},
|
|
609
|
+
final: result.final,
|
|
610
|
+
metadata: result.metadata
|
|
611
|
+
} };
|
|
612
|
+
if (result?.kind === "artifact-update") return { artifactUpdate: {
|
|
613
|
+
taskId: result.taskId,
|
|
614
|
+
contextId: result.contextId,
|
|
615
|
+
artifact: {
|
|
616
|
+
...result.artifact,
|
|
617
|
+
parts: result.artifact.parts?.map(toV1Part)
|
|
618
|
+
},
|
|
619
|
+
append: result.append,
|
|
620
|
+
lastChunk: result.lastChunk,
|
|
621
|
+
metadata: result.metadata
|
|
622
|
+
} };
|
|
623
|
+
return result?.kind === "message" ? { message: toV1Message(result) } : { task: toV1Task(result) };
|
|
624
|
+
}
|
|
625
|
+
if (method === "tasks/get" || method === "tasks/cancel") return toV1Task(result);
|
|
626
|
+
return result;
|
|
627
|
+
}
|
|
628
|
+
function convertV1Response(response, method) {
|
|
629
|
+
if (!response || !("result" in response)) return response;
|
|
630
|
+
return {
|
|
631
|
+
...response,
|
|
632
|
+
result: toV1Result(response.result, method)
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
async function* convertV1Stream(stream, method) {
|
|
636
|
+
for await (const response of stream) yield convertV1Response(response, method);
|
|
637
|
+
}
|
|
464
638
|
function createAgentCardDefaults({ pushNotifications = false } = {}) {
|
|
465
639
|
return {
|
|
466
640
|
protocolVersion: "0.3.0",
|
|
@@ -575,6 +749,176 @@ function createDataArtifactUpdate({ taskId, contextId, data, lastChunk }) {
|
|
|
575
749
|
}
|
|
576
750
|
};
|
|
577
751
|
}
|
|
752
|
+
/**
|
|
753
|
+
* Task metadata keys that store the resume bookkeeping for a suspended agent
|
|
754
|
+
* run so a follow-up message for an `input-required` task can resume it.
|
|
755
|
+
*/
|
|
756
|
+
const SUSPENDED_RUN_ID_METADATA_KEY = "suspendedRunId";
|
|
757
|
+
const SUSPENDED_TOOL_CALL_ID_METADATA_KEY = "suspendedToolCallId";
|
|
758
|
+
const SUSPENDED_REQUIRES_APPROVAL_METADATA_KEY = "suspendedRequiresApproval";
|
|
759
|
+
function toJsonSafe(value) {
|
|
760
|
+
if (value === void 0) return;
|
|
761
|
+
try {
|
|
762
|
+
return JSON.parse(JSON.stringify(value));
|
|
763
|
+
} catch {
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Builds the `input-required` status update for a suspended agent run.
|
|
769
|
+
* The status message carries a human-readable prompt plus a data part with
|
|
770
|
+
* the structured suspend payload and resume schema so A2A clients can
|
|
771
|
+
* render/collect the required input (HITL per the A2A spec).
|
|
772
|
+
*/
|
|
773
|
+
function createInputRequiredStatusUpdate({ taskId, contextId, suspendPayload, resumeSchema, logger }) {
|
|
774
|
+
const extractMessage = (value) => {
|
|
775
|
+
if (!value || typeof value !== "object") return;
|
|
776
|
+
const messageValue = value.message;
|
|
777
|
+
return typeof messageValue === "string" && messageValue.length > 0 ? messageValue : void 0;
|
|
778
|
+
};
|
|
779
|
+
const promptText = extractMessage(suspendPayload) ?? extractMessage(suspendPayload?.suspendPayload) ?? "Additional input is required to continue this task.";
|
|
780
|
+
const safeSuspendPayload = toJsonSafe(suspendPayload);
|
|
781
|
+
const safeResumeSchema = toJsonSafe(resumeSchema);
|
|
782
|
+
if (suspendPayload !== void 0 && safeSuspendPayload === void 0) logger?.warn(`[Task ${taskId}] Suspend payload is not JSON-serializable and was omitted from the status message.`);
|
|
783
|
+
if (resumeSchema !== void 0 && safeResumeSchema === void 0) logger?.warn(`[Task ${taskId}] Resume schema is not JSON-serializable and was omitted from the status message.`);
|
|
784
|
+
const data = {
|
|
785
|
+
...safeSuspendPayload !== void 0 ? { suspendPayload: safeSuspendPayload } : {},
|
|
786
|
+
...safeResumeSchema !== void 0 ? { resumeSchema: safeResumeSchema } : {}
|
|
787
|
+
};
|
|
788
|
+
return {
|
|
789
|
+
state: "input-required",
|
|
790
|
+
message: {
|
|
791
|
+
messageId: crypto.randomUUID(),
|
|
792
|
+
kind: "message",
|
|
793
|
+
role: "agent",
|
|
794
|
+
taskId,
|
|
795
|
+
contextId,
|
|
796
|
+
parts: [{
|
|
797
|
+
kind: "text",
|
|
798
|
+
text: promptText
|
|
799
|
+
}, ...Object.keys(data).length > 0 ? [{
|
|
800
|
+
kind: "data",
|
|
801
|
+
data
|
|
802
|
+
}] : []]
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Extracts resume data from a follow-up message for an `input-required` task.
|
|
808
|
+
* Prefers a structured data part; falls back to parsing the text as JSON
|
|
809
|
+
* (the Mastra A2A client serializes structured resume data as JSON text),
|
|
810
|
+
* and finally to the raw text.
|
|
811
|
+
*/
|
|
812
|
+
function extractResumeData(message) {
|
|
813
|
+
const dataPart = message.parts.find((part) => part.kind === "data");
|
|
814
|
+
if (dataPart && "data" in dataPart) return dataPart.data;
|
|
815
|
+
const text = message.parts.filter((part) => part.kind === "text").map((part) => part.text).join("\n").trim();
|
|
816
|
+
if (!text) return;
|
|
817
|
+
try {
|
|
818
|
+
return JSON.parse(text);
|
|
819
|
+
} catch {
|
|
820
|
+
return text;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
function getSuspendedRunId(task) {
|
|
824
|
+
const value = task?.metadata?.[SUSPENDED_RUN_ID_METADATA_KEY];
|
|
825
|
+
return typeof value === "string" ? value : void 0;
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Approval suspensions (`requireApproval`) carry `{ toolCallId, toolName, args, resumeSchema }`
|
|
829
|
+
* without a nested `suspendPayload`, while `suspend()` suspensions include one
|
|
830
|
+
* (see `ToolCallApprovalPayload` / `ToolCallSuspendedPayload` in @mastra/core).
|
|
831
|
+
*/
|
|
832
|
+
function isApprovalSuspension(suspendPayload) {
|
|
833
|
+
if (!suspendPayload || typeof suspendPayload !== "object") return false;
|
|
834
|
+
const payload = suspendPayload;
|
|
835
|
+
return typeof payload.toolCallId === "string" && payload.suspendPayload === void 0;
|
|
836
|
+
}
|
|
837
|
+
const APPROVAL_AFFIRMATIVE_PATTERN = /^(y|yes|approve|approved|ok|okay|confirm|confirmed|true)[.!]?$/i;
|
|
838
|
+
const APPROVAL_NEGATIVE_PATTERN = /^(n|no|decline|declined|deny|denied|reject|rejected|false)[.!]?$/i;
|
|
839
|
+
/**
|
|
840
|
+
* Approval resumes are driven by `resumeData.approved` in the agentic loop, so
|
|
841
|
+
* plain-text replies from A2A clients ("yes", "no") are coerced to the
|
|
842
|
+
* `{ approved }` shape. Unrecognized values pass through unchanged.
|
|
843
|
+
*/
|
|
844
|
+
function normalizeResumeData(resumeData, requiresApproval) {
|
|
845
|
+
if (!requiresApproval || typeof resumeData !== "string") return resumeData;
|
|
846
|
+
const text = resumeData.trim();
|
|
847
|
+
if (APPROVAL_AFFIRMATIVE_PATTERN.test(text)) return { approved: true };
|
|
848
|
+
if (APPROVAL_NEGATIVE_PATTERN.test(text)) return { approved: false };
|
|
849
|
+
return resumeData;
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Marks a task `input-required` for a suspended agent run and records the
|
|
853
|
+
* resume bookkeeping (runId, toolCallId, approval flag) in task metadata.
|
|
854
|
+
* Shared by the send and stream paths so both report identical suspensions.
|
|
855
|
+
*/
|
|
856
|
+
function applySuspensionToTask({ task, suspendPayload, resumeSchema, runId, logger }) {
|
|
857
|
+
const nextTask = applyUpdateToTask(task, createInputRequiredStatusUpdate({
|
|
858
|
+
taskId: task.id,
|
|
859
|
+
contextId: task.contextId,
|
|
860
|
+
suspendPayload,
|
|
861
|
+
resumeSchema,
|
|
862
|
+
logger
|
|
863
|
+
}));
|
|
864
|
+
const payload = suspendPayload;
|
|
865
|
+
nextTask.metadata = {
|
|
866
|
+
...clearSuspensionMetadata(nextTask.metadata),
|
|
867
|
+
[SUSPENDED_RUN_ID_METADATA_KEY]: runId,
|
|
868
|
+
...typeof payload?.toolCallId === "string" ? { [SUSPENDED_TOOL_CALL_ID_METADATA_KEY]: payload.toolCallId } : {},
|
|
869
|
+
...isApprovalSuspension(suspendPayload) ? { [SUSPENDED_REQUIRES_APPROVAL_METADATA_KEY]: true } : {}
|
|
870
|
+
};
|
|
871
|
+
return nextTask;
|
|
872
|
+
}
|
|
873
|
+
/** Removes the suspension bookkeeping from task metadata once the run completes. */
|
|
874
|
+
function clearSuspensionMetadata(metadata) {
|
|
875
|
+
const { [SUSPENDED_RUN_ID_METADATA_KEY]: _runId, [SUSPENDED_TOOL_CALL_ID_METADATA_KEY]: _toolCallId, [SUSPENDED_REQUIRES_APPROVAL_METADATA_KEY]: _requiresApproval, ...rest } = metadata ?? {};
|
|
876
|
+
return rest;
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Claims an interrupted task for resume by transitioning it to `working`.
|
|
880
|
+
* `loadWithVersion` and the body of `InMemoryTaskStore.save` both execute
|
|
881
|
+
* synchronously, so two concurrent follow-up messages cannot both claim (and
|
|
882
|
+
* double-resume) the same suspended run.
|
|
883
|
+
*/
|
|
884
|
+
async function claimInterruptedTaskResume({ taskStore, agentId, taskId }) {
|
|
885
|
+
const snapshot = taskStore.loadWithVersion({
|
|
886
|
+
agentId,
|
|
887
|
+
taskId
|
|
888
|
+
});
|
|
889
|
+
if (snapshot?.task.status.state !== "input-required" && snapshot?.task.status.state !== "auth-required") return;
|
|
890
|
+
const task = snapshot.task;
|
|
891
|
+
const toolCallId = task.metadata?.[SUSPENDED_TOOL_CALL_ID_METADATA_KEY];
|
|
892
|
+
const claim = {
|
|
893
|
+
runId: getSuspendedRunId(task) ?? taskId,
|
|
894
|
+
...typeof toolCallId === "string" ? { toolCallId } : {},
|
|
895
|
+
requiresApproval: task.metadata?.[SUSPENDED_REQUIRES_APPROVAL_METADATA_KEY] === true
|
|
896
|
+
};
|
|
897
|
+
try {
|
|
898
|
+
await taskStore.save({
|
|
899
|
+
agentId,
|
|
900
|
+
data: applyUpdateToTask(task, { state: "working" }),
|
|
901
|
+
expectedVersion: snapshot.version
|
|
902
|
+
});
|
|
903
|
+
} catch (error) {
|
|
904
|
+
if (error instanceof require_server_a2a_store.TaskStoreVersionConflictError) return;
|
|
905
|
+
throw error;
|
|
906
|
+
}
|
|
907
|
+
return claim;
|
|
908
|
+
}
|
|
909
|
+
async function waitForClaimedResume({ taskStore, agentId, taskId }) {
|
|
910
|
+
let snapshot = taskStore.loadWithVersion({
|
|
911
|
+
agentId,
|
|
912
|
+
taskId
|
|
913
|
+
});
|
|
914
|
+
if (!snapshot) throw _mastra_core_a2a.MastraA2AError.taskNotFound(taskId);
|
|
915
|
+
while (snapshot.task.status.state === "working") snapshot = await taskStore.waitForNextUpdate({
|
|
916
|
+
agentId,
|
|
917
|
+
taskId,
|
|
918
|
+
afterVersion: snapshot.version
|
|
919
|
+
});
|
|
920
|
+
return snapshot.task;
|
|
921
|
+
}
|
|
578
922
|
function resolvePushNotificationPair({ pushNotificationStore, pushNotificationSender }) {
|
|
579
923
|
if (pushNotificationSender) return {
|
|
580
924
|
pushNotificationStore: pushNotificationSender.getStore(),
|
|
@@ -603,23 +947,45 @@ function shouldSendPushNotification(previousTask, nextTask) {
|
|
|
603
947
|
"completed",
|
|
604
948
|
"failed",
|
|
605
949
|
"canceled",
|
|
606
|
-
"
|
|
950
|
+
"rejected",
|
|
951
|
+
"input-required",
|
|
952
|
+
"auth-required"
|
|
607
953
|
].includes(nextTask.status.state)) return false;
|
|
608
954
|
return previousTask?.status.state !== nextTask.status.state;
|
|
609
955
|
}
|
|
610
|
-
|
|
611
|
-
|
|
956
|
+
function createLinkedAbortController(abortSignal) {
|
|
957
|
+
const controller = new AbortController();
|
|
958
|
+
if (!abortSignal) return {
|
|
959
|
+
controller,
|
|
960
|
+
cleanup: () => {}
|
|
961
|
+
};
|
|
962
|
+
const abortFromSignal = () => {
|
|
963
|
+
if (!controller.signal.aborted) controller.abort(abortSignal.reason);
|
|
964
|
+
};
|
|
965
|
+
if (abortSignal.aborted) abortFromSignal();
|
|
966
|
+
else abortSignal.addEventListener("abort", abortFromSignal, { once: true });
|
|
967
|
+
return {
|
|
968
|
+
controller,
|
|
969
|
+
cleanup: () => abortSignal.removeEventListener("abort", abortFromSignal)
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
async function saveTaskAndMaybeSendPushNotification({ taskStore, pushNotificationSender, previousTask, nextTask, agentId, expectedVersion, logger }) {
|
|
973
|
+
const storedTask = await taskStore.save({
|
|
612
974
|
agentId,
|
|
613
|
-
data: nextTask
|
|
975
|
+
data: nextTask,
|
|
976
|
+
expectedVersion,
|
|
977
|
+
skipIfCanceled: true
|
|
614
978
|
});
|
|
615
|
-
if (
|
|
979
|
+
if (storedTask.status.state === "canceled" && nextTask.status.state !== "canceled") return storedTask;
|
|
980
|
+
if (!shouldSendPushNotification(previousTask, storedTask)) return storedTask;
|
|
616
981
|
pushNotificationSender.sendNotifications({
|
|
617
982
|
agentId,
|
|
618
|
-
task:
|
|
983
|
+
task: storedTask,
|
|
619
984
|
logger
|
|
620
985
|
}).catch((error) => {
|
|
621
986
|
logger?.error("Failed to schedule A2A push notification", error);
|
|
622
987
|
});
|
|
988
|
+
return storedTask;
|
|
623
989
|
}
|
|
624
990
|
function extractFullStreamTextDelta(value) {
|
|
625
991
|
if (typeof value !== "object" || value === null || !("type" in value)) return null;
|
|
@@ -642,12 +1008,10 @@ function extractFinalStructuredObject(value) {
|
|
|
642
1008
|
const objectValue = chunk.payload?.object ?? chunk.object;
|
|
643
1009
|
return objectValue && typeof objectValue === "object" ? objectValue : void 0;
|
|
644
1010
|
}
|
|
645
|
-
function
|
|
646
|
-
return
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
"canceled"
|
|
650
|
-
].includes(state);
|
|
1011
|
+
function isSuspensionChunk(value) {
|
|
1012
|
+
if (typeof value !== "object" || value === null || !("type" in value)) return false;
|
|
1013
|
+
const type = value.type;
|
|
1014
|
+
return type === "tool-call-suspended" || type === "tool-call-approval";
|
|
651
1015
|
}
|
|
652
1016
|
function artifactIdentity(artifact) {
|
|
653
1017
|
return artifact.artifactId || artifact.name;
|
|
@@ -692,11 +1056,15 @@ function getTaskArtifactUpdates({ previous, next }) {
|
|
|
692
1056
|
artifact: structuredClone(artifact)
|
|
693
1057
|
}));
|
|
694
1058
|
}
|
|
695
|
-
async function executeMessageSend({ requestId, message, metadata, currentData, taskStore, pushNotificationSender, agent, agentId, logger, requestContext }) {
|
|
1059
|
+
async function executeMessageSend({ requestId, message, metadata, currentData, taskStore, pushNotificationSender, agent, agentId, logger, requestContext, resume }) {
|
|
696
1060
|
const { contextId } = message;
|
|
697
1061
|
try {
|
|
698
1062
|
const resourceId = metadata?.resourceId ?? message.metadata?.resourceId ?? agentId;
|
|
699
|
-
const result = await agent.
|
|
1063
|
+
const result = resume ? await agent.resumeGenerate(normalizeResumeData(extractResumeData(message), resume.requiresApproval), {
|
|
1064
|
+
runId: resume.runId,
|
|
1065
|
+
...resume.toolCallId ? { toolCallId: resume.toolCallId } : {},
|
|
1066
|
+
requestContext
|
|
1067
|
+
}) : await agent.generate([convertToCoreMessage(message)], {
|
|
700
1068
|
runId: currentData.id,
|
|
701
1069
|
requestContext,
|
|
702
1070
|
...contextId ? {
|
|
@@ -717,13 +1085,32 @@ async function executeMessageSend({ requestId, message, metadata, currentData, t
|
|
|
717
1085
|
data: result.object
|
|
718
1086
|
});
|
|
719
1087
|
if (artifactUpdate) currentData = applyUpdateToTask(currentData, artifactUpdate);
|
|
1088
|
+
if (result.finishReason === "suspended") {
|
|
1089
|
+
const previousTask = currentData;
|
|
1090
|
+
currentData = applySuspensionToTask({
|
|
1091
|
+
task: currentData,
|
|
1092
|
+
suspendPayload: result.suspendPayload,
|
|
1093
|
+
resumeSchema: result.resumeSchema,
|
|
1094
|
+
runId: result.runId ?? currentData.id,
|
|
1095
|
+
logger
|
|
1096
|
+
});
|
|
1097
|
+
await saveTaskAndMaybeSendPushNotification({
|
|
1098
|
+
taskStore,
|
|
1099
|
+
pushNotificationSender,
|
|
1100
|
+
previousTask,
|
|
1101
|
+
nextTask: currentData,
|
|
1102
|
+
agentId,
|
|
1103
|
+
logger
|
|
1104
|
+
});
|
|
1105
|
+
return createSuccessResponse(requestId, currentData);
|
|
1106
|
+
}
|
|
720
1107
|
const previousTask = currentData;
|
|
721
1108
|
currentData = applyUpdateToTask(currentData, {
|
|
722
1109
|
state: "completed",
|
|
723
1110
|
message: void 0
|
|
724
1111
|
});
|
|
725
1112
|
currentData.metadata = {
|
|
726
|
-
...currentData.metadata,
|
|
1113
|
+
...clearSuspensionMetadata(currentData.metadata),
|
|
727
1114
|
execution: {
|
|
728
1115
|
toolCalls: result.toolCalls,
|
|
729
1116
|
toolResults: result.toolResults,
|
|
@@ -781,11 +1168,28 @@ async function handleMessageSend({ requestId, params, taskStore, pushNotificatio
|
|
|
781
1168
|
const { message, metadata } = params;
|
|
782
1169
|
const { contextId } = message;
|
|
783
1170
|
const taskId = message.taskId || crypto.randomUUID();
|
|
784
|
-
const existingTask =
|
|
1171
|
+
const existingTask = taskStore.loadWithVersion({
|
|
785
1172
|
agentId,
|
|
786
1173
|
taskId
|
|
787
|
-
});
|
|
1174
|
+
})?.task;
|
|
1175
|
+
if (message.taskId && !existingTask) throw _mastra_core_a2a.MastraA2AError.taskNotFound(message.taskId);
|
|
788
1176
|
if (params.configuration?.blocking === false && existingTask?.status.state === "working") return createSuccessResponse(requestId, existingTask);
|
|
1177
|
+
if (existingTask?.status.state === "working" && getSuspendedRunId(existingTask)) return createSuccessResponse(requestId, await waitForClaimedResume({
|
|
1178
|
+
taskStore,
|
|
1179
|
+
agentId,
|
|
1180
|
+
taskId
|
|
1181
|
+
}));
|
|
1182
|
+
const wasInterrupted = isInterruptedTaskState(existingTask?.status.state);
|
|
1183
|
+
const resume = await claimInterruptedTaskResume({
|
|
1184
|
+
taskStore,
|
|
1185
|
+
agentId,
|
|
1186
|
+
taskId
|
|
1187
|
+
});
|
|
1188
|
+
if (wasInterrupted && !resume) return createSuccessResponse(requestId, await waitForClaimedResume({
|
|
1189
|
+
taskStore,
|
|
1190
|
+
agentId,
|
|
1191
|
+
taskId
|
|
1192
|
+
}));
|
|
789
1193
|
const { pushNotificationStore: resolvedPushNotificationStore, pushNotificationSender: resolvedPushNotificationSender } = resolvePushNotificationPair({
|
|
790
1194
|
pushNotificationStore,
|
|
791
1195
|
pushNotificationSender
|
|
@@ -831,7 +1235,8 @@ async function handleMessageSend({ requestId, params, taskStore, pushNotificatio
|
|
|
831
1235
|
agent,
|
|
832
1236
|
agentId,
|
|
833
1237
|
logger,
|
|
834
|
-
requestContext
|
|
1238
|
+
requestContext,
|
|
1239
|
+
resume
|
|
835
1240
|
});
|
|
836
1241
|
if (params.configuration?.blocking === false) {
|
|
837
1242
|
execution.catch((error) => {
|
|
@@ -849,6 +1254,25 @@ async function handleTaskGet({ requestId, taskStore, agentId, taskId }) {
|
|
|
849
1254
|
if (!task) throw _mastra_core_a2a.MastraA2AError.taskNotFound(taskId);
|
|
850
1255
|
return createSuccessResponse(requestId, task);
|
|
851
1256
|
}
|
|
1257
|
+
function handleTaskList({ requestId, taskStore, agentId, params }) {
|
|
1258
|
+
const requestedPageSize = params.pageSize ?? 50;
|
|
1259
|
+
const offset = params.pageToken ? Number.parseInt(params.pageToken, 10) : 0;
|
|
1260
|
+
const start = Number.isFinite(offset) && offset >= 0 ? offset : 0;
|
|
1261
|
+
const status = typeof params.status === "string" ? params.status.replace(/^TASK_STATE_/, "").toLowerCase().replaceAll("_", "-") : void 0;
|
|
1262
|
+
const timestampAfter = params.statusTimestampAfter ? Date.parse(params.statusTimestampAfter) : void 0;
|
|
1263
|
+
const matchingTasks = taskStore.list({ agentId }).filter((task) => !params.contextId || task.contextId === params.contextId).filter((task) => !status || task.status.state === status).filter((task) => !timestampAfter || task.status.timestamp !== void 0 && Date.parse(task.status.timestamp) >= timestampAfter);
|
|
1264
|
+
const tasks = matchingTasks.slice(start, start + requestedPageSize).map((task) => toV1Task(task, {
|
|
1265
|
+
includeArtifacts: params.includeArtifacts ?? false,
|
|
1266
|
+
historyLength: params.historyLength
|
|
1267
|
+
}));
|
|
1268
|
+
const nextOffset = start + tasks.length;
|
|
1269
|
+
return createSuccessResponse(requestId, {
|
|
1270
|
+
tasks,
|
|
1271
|
+
nextPageToken: nextOffset < matchingTasks.length ? String(nextOffset) : "",
|
|
1272
|
+
pageSize: requestedPageSize,
|
|
1273
|
+
totalSize: matchingTasks.length
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
852
1276
|
async function loadTaskOrThrow({ taskStore, agentId, taskId }) {
|
|
853
1277
|
const task = await taskStore.load({
|
|
854
1278
|
agentId,
|
|
@@ -908,11 +1332,20 @@ async function handleDeleteTaskPushNotificationConfig({ requestId, taskStore, pu
|
|
|
908
1332
|
})) throw _mastra_core_a2a.MastraA2AError.invalidParams(`Push notification config not found: ${params.pushNotificationConfigId}`);
|
|
909
1333
|
return createSuccessResponse(requestId, null);
|
|
910
1334
|
}
|
|
911
|
-
async function* handleMessageStream({ requestId, params, taskStore, pushNotificationStore, pushNotificationSender, agent, agentId, logger, requestContext }) {
|
|
1335
|
+
async function* handleMessageStream({ requestId, params, taskStore, pushNotificationStore, pushNotificationSender, agent, agentId, logger, requestContext, abortSignal }) {
|
|
912
1336
|
validateMessageSendParams(params);
|
|
913
1337
|
const { message, metadata } = params;
|
|
914
1338
|
const { contextId } = message;
|
|
915
1339
|
const taskId = message.taskId || crypto.randomUUID();
|
|
1340
|
+
if (message.taskId && !taskStore.loadWithVersion({
|
|
1341
|
+
agentId,
|
|
1342
|
+
taskId
|
|
1343
|
+
})) throw _mastra_core_a2a.MastraA2AError.taskNotFound(message.taskId);
|
|
1344
|
+
const resume = await claimInterruptedTaskResume({
|
|
1345
|
+
taskStore,
|
|
1346
|
+
agentId,
|
|
1347
|
+
taskId
|
|
1348
|
+
});
|
|
916
1349
|
const { pushNotificationStore: resolvedPushNotificationStore, pushNotificationSender: resolvedPushNotificationSender } = resolvePushNotificationPair({
|
|
917
1350
|
pushNotificationStore,
|
|
918
1351
|
pushNotificationSender
|
|
@@ -948,12 +1381,24 @@ async function* handleMessageStream({ requestId, params, taskStore, pushNotifica
|
|
|
948
1381
|
agentId,
|
|
949
1382
|
logger
|
|
950
1383
|
});
|
|
951
|
-
|
|
1384
|
+
const { controller: taskAbortController, cleanup: cleanupLinkedAbortController } = createLinkedAbortController(abortSignal);
|
|
1385
|
+
const unregisterTaskAbortController = taskStore.registerAbortController({
|
|
1386
|
+
agentId,
|
|
1387
|
+
taskId,
|
|
1388
|
+
controller: taskAbortController
|
|
1389
|
+
});
|
|
952
1390
|
try {
|
|
1391
|
+
yield createSuccessResponse(requestId, currentData);
|
|
953
1392
|
const resourceId = metadata?.resourceId ?? message.metadata?.resourceId ?? agentId;
|
|
954
|
-
const result = await agent.
|
|
1393
|
+
const result = resume ? await agent.resumeStream(normalizeResumeData(extractResumeData(message), resume.requiresApproval), {
|
|
1394
|
+
runId: resume.runId,
|
|
1395
|
+
...resume.toolCallId ? { toolCallId: resume.toolCallId } : {},
|
|
1396
|
+
requestContext,
|
|
1397
|
+
abortSignal: taskAbortController.signal
|
|
1398
|
+
}) : await agent.stream([convertToCoreMessage(message)], {
|
|
955
1399
|
runId: taskId,
|
|
956
1400
|
requestContext,
|
|
1401
|
+
abortSignal: taskAbortController.signal,
|
|
957
1402
|
...contextId ? {
|
|
958
1403
|
threadId: contextId,
|
|
959
1404
|
resourceId
|
|
@@ -962,7 +1407,22 @@ async function* handleMessageStream({ requestId, params, taskStore, pushNotifica
|
|
|
962
1407
|
let sawTextArtifact = false;
|
|
963
1408
|
let pendingTextChunk;
|
|
964
1409
|
let structuredData;
|
|
1410
|
+
let suspended = false;
|
|
1411
|
+
let streamCanceled = false;
|
|
965
1412
|
for await (const chunk of result.fullStream) {
|
|
1413
|
+
if (taskAbortController.signal.aborted) {
|
|
1414
|
+
const latestTask = await taskStore.load({
|
|
1415
|
+
agentId,
|
|
1416
|
+
taskId: currentData.id
|
|
1417
|
+
});
|
|
1418
|
+
if (latestTask) currentData = latestTask;
|
|
1419
|
+
streamCanceled = true;
|
|
1420
|
+
break;
|
|
1421
|
+
}
|
|
1422
|
+
if (isSuspensionChunk(chunk)) {
|
|
1423
|
+
suspended = true;
|
|
1424
|
+
continue;
|
|
1425
|
+
}
|
|
966
1426
|
const textDelta = extractFullStreamTextDelta(chunk);
|
|
967
1427
|
if (textDelta !== null) {
|
|
968
1428
|
if (!pendingTextChunk) {
|
|
@@ -977,13 +1437,17 @@ async function* handleMessageStream({ requestId, params, taskStore, pushNotifica
|
|
|
977
1437
|
lastChunk: false
|
|
978
1438
|
});
|
|
979
1439
|
currentData = applyUpdateToTask(currentData, textUpdate);
|
|
980
|
-
await saveTaskAndMaybeSendPushNotification({
|
|
1440
|
+
currentData = await saveTaskAndMaybeSendPushNotification({
|
|
981
1441
|
taskStore,
|
|
982
1442
|
pushNotificationSender: resolvedPushNotificationSender,
|
|
983
1443
|
nextTask: currentData,
|
|
984
1444
|
agentId,
|
|
985
1445
|
logger
|
|
986
1446
|
});
|
|
1447
|
+
if (currentData.status.state === "canceled") {
|
|
1448
|
+
streamCanceled = true;
|
|
1449
|
+
break;
|
|
1450
|
+
}
|
|
987
1451
|
yield createSuccessResponse(requestId, textUpdate);
|
|
988
1452
|
sawTextArtifact = true;
|
|
989
1453
|
pendingTextChunk = textDelta;
|
|
@@ -992,98 +1456,217 @@ async function* handleMessageStream({ requestId, params, taskStore, pushNotifica
|
|
|
992
1456
|
const finalStructuredObject = extractFinalStructuredObject(chunk);
|
|
993
1457
|
if (finalStructuredObject) structuredData = finalStructuredObject;
|
|
994
1458
|
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1459
|
+
if (!streamCanceled && taskAbortController.signal.aborted) {
|
|
1460
|
+
const latestTask = await taskStore.load({
|
|
1461
|
+
agentId,
|
|
1462
|
+
taskId: currentData.id
|
|
1463
|
+
});
|
|
1464
|
+
if (latestTask) currentData = latestTask;
|
|
1465
|
+
streamCanceled = true;
|
|
999
1466
|
}
|
|
1000
|
-
if (
|
|
1001
|
-
const
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1467
|
+
if (streamCanceled && abortSignal?.aborted && currentData.status.state !== "canceled") {
|
|
1468
|
+
const previousTask = currentData;
|
|
1469
|
+
currentData = applyUpdateToTask(currentData, {
|
|
1470
|
+
state: "canceled",
|
|
1471
|
+
message: {
|
|
1472
|
+
messageId: crypto.randomUUID(),
|
|
1473
|
+
role: "agent",
|
|
1474
|
+
parts: [{
|
|
1475
|
+
kind: "text",
|
|
1476
|
+
text: "Task canceled because the request was aborted."
|
|
1477
|
+
}],
|
|
1478
|
+
kind: "message"
|
|
1479
|
+
}
|
|
1007
1480
|
});
|
|
1008
|
-
currentData =
|
|
1009
|
-
await saveTaskAndMaybeSendPushNotification({
|
|
1481
|
+
currentData = await saveTaskAndMaybeSendPushNotification({
|
|
1010
1482
|
taskStore,
|
|
1011
1483
|
pushNotificationSender: resolvedPushNotificationSender,
|
|
1484
|
+
previousTask,
|
|
1012
1485
|
nextTask: currentData,
|
|
1013
1486
|
agentId,
|
|
1014
1487
|
logger
|
|
1015
1488
|
});
|
|
1016
|
-
yield createSuccessResponse(requestId, textUpdate);
|
|
1017
|
-
sawTextArtifact = true;
|
|
1018
|
-
pendingTextChunk = void 0;
|
|
1019
1489
|
}
|
|
1020
|
-
if (
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1490
|
+
if (!streamCanceled && suspended) {
|
|
1491
|
+
if (pendingTextChunk) {
|
|
1492
|
+
const textUpdate = createTextChunkArtifactUpdate({
|
|
1493
|
+
taskId: currentData.id,
|
|
1494
|
+
contextId: currentData.contextId,
|
|
1495
|
+
text: pendingTextChunk,
|
|
1496
|
+
append: sawTextArtifact,
|
|
1497
|
+
lastChunk: true
|
|
1498
|
+
});
|
|
1499
|
+
currentData = applyUpdateToTask(currentData, textUpdate);
|
|
1500
|
+
currentData = await saveTaskAndMaybeSendPushNotification({
|
|
1501
|
+
taskStore,
|
|
1502
|
+
pushNotificationSender: resolvedPushNotificationSender,
|
|
1503
|
+
nextTask: currentData,
|
|
1504
|
+
agentId,
|
|
1505
|
+
logger
|
|
1506
|
+
});
|
|
1507
|
+
if (currentData.status.state === "canceled") streamCanceled = true;
|
|
1508
|
+
else yield createSuccessResponse(requestId, textUpdate);
|
|
1509
|
+
}
|
|
1510
|
+
const suspensionTask = currentData;
|
|
1511
|
+
currentData = applySuspensionToTask({
|
|
1512
|
+
task: currentData,
|
|
1513
|
+
suspendPayload: await result.suspendPayload,
|
|
1514
|
+
resumeSchema: await result.resumeSchema,
|
|
1515
|
+
runId: result.runId ?? currentData.id,
|
|
1516
|
+
logger
|
|
1026
1517
|
});
|
|
1027
|
-
currentData =
|
|
1028
|
-
await saveTaskAndMaybeSendPushNotification({
|
|
1518
|
+
currentData = await saveTaskAndMaybeSendPushNotification({
|
|
1029
1519
|
taskStore,
|
|
1030
1520
|
pushNotificationSender: resolvedPushNotificationSender,
|
|
1521
|
+
previousTask: suspensionTask,
|
|
1031
1522
|
nextTask: currentData,
|
|
1032
1523
|
agentId,
|
|
1033
1524
|
logger
|
|
1034
1525
|
});
|
|
1035
|
-
|
|
1526
|
+
if (currentData.status.state === "canceled") streamCanceled = true;
|
|
1036
1527
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
completedTask.metadata = {
|
|
1043
|
-
...completedTask.metadata,
|
|
1044
|
-
execution: {
|
|
1045
|
-
toolCalls: await result.toolCalls,
|
|
1046
|
-
toolResults: await result.toolResults,
|
|
1047
|
-
usage: await result.usage,
|
|
1048
|
-
finishReason: await result.finishReason
|
|
1528
|
+
if (!streamCanceled && !suspended) {
|
|
1529
|
+
structuredData ??= await result.object;
|
|
1530
|
+
if (!pendingTextChunk && !sawTextArtifact) {
|
|
1531
|
+
const finalText = await result.text;
|
|
1532
|
+
if (finalText) pendingTextChunk = finalText;
|
|
1049
1533
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1534
|
+
if (pendingTextChunk) {
|
|
1535
|
+
const textUpdate = createTextChunkArtifactUpdate({
|
|
1536
|
+
taskId: currentData.id,
|
|
1537
|
+
contextId: currentData.contextId,
|
|
1538
|
+
text: pendingTextChunk,
|
|
1539
|
+
append: sawTextArtifact,
|
|
1540
|
+
lastChunk: !structuredData
|
|
1541
|
+
});
|
|
1542
|
+
currentData = applyUpdateToTask(currentData, textUpdate);
|
|
1543
|
+
currentData = await saveTaskAndMaybeSendPushNotification({
|
|
1544
|
+
taskStore,
|
|
1545
|
+
pushNotificationSender: resolvedPushNotificationSender,
|
|
1546
|
+
nextTask: currentData,
|
|
1547
|
+
agentId,
|
|
1548
|
+
logger
|
|
1549
|
+
});
|
|
1550
|
+
if (currentData.status.state === "canceled") streamCanceled = true;
|
|
1551
|
+
else yield createSuccessResponse(requestId, textUpdate);
|
|
1552
|
+
sawTextArtifact = true;
|
|
1553
|
+
pendingTextChunk = void 0;
|
|
1554
|
+
}
|
|
1555
|
+
if (!streamCanceled && structuredData) {
|
|
1556
|
+
const dataUpdate = createDataArtifactUpdate({
|
|
1557
|
+
taskId: currentData.id,
|
|
1558
|
+
contextId: currentData.contextId,
|
|
1559
|
+
data: structuredData,
|
|
1560
|
+
lastChunk: true
|
|
1561
|
+
});
|
|
1562
|
+
currentData = applyUpdateToTask(currentData, dataUpdate);
|
|
1563
|
+
currentData = await saveTaskAndMaybeSendPushNotification({
|
|
1564
|
+
taskStore,
|
|
1565
|
+
pushNotificationSender: resolvedPushNotificationSender,
|
|
1566
|
+
nextTask: currentData,
|
|
1567
|
+
agentId,
|
|
1568
|
+
logger
|
|
1569
|
+
});
|
|
1570
|
+
if (currentData.status.state === "canceled") streamCanceled = true;
|
|
1571
|
+
else yield createSuccessResponse(requestId, dataUpdate);
|
|
1572
|
+
}
|
|
1573
|
+
if (!streamCanceled) {
|
|
1574
|
+
const previousTask = currentData;
|
|
1575
|
+
const completedTask = applyUpdateToTask(currentData, {
|
|
1576
|
+
state: "completed",
|
|
1577
|
+
message: void 0
|
|
1578
|
+
});
|
|
1579
|
+
completedTask.metadata = {
|
|
1580
|
+
...clearSuspensionMetadata(completedTask.metadata),
|
|
1581
|
+
execution: {
|
|
1582
|
+
toolCalls: await result.toolCalls,
|
|
1583
|
+
toolResults: await result.toolResults,
|
|
1584
|
+
usage: await result.usage,
|
|
1585
|
+
finishReason: await result.finishReason
|
|
1586
|
+
}
|
|
1587
|
+
};
|
|
1588
|
+
currentData = await saveTaskAndMaybeSendPushNotification({
|
|
1589
|
+
taskStore,
|
|
1590
|
+
pushNotificationSender: resolvedPushNotificationSender,
|
|
1591
|
+
previousTask,
|
|
1592
|
+
nextTask: completedTask,
|
|
1593
|
+
agentId,
|
|
1594
|
+
logger
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
} catch (handlerError) {
|
|
1599
|
+
const latestTask = await taskStore.load({
|
|
1057
1600
|
agentId,
|
|
1058
|
-
|
|
1601
|
+
taskId: currentData.id
|
|
1059
1602
|
});
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1603
|
+
if (latestTask?.status.state === "canceled") currentData = latestTask;
|
|
1604
|
+
else if (taskAbortController.signal.aborted) {
|
|
1605
|
+
currentData = latestTask ?? currentData;
|
|
1606
|
+
if (abortSignal?.aborted) {
|
|
1607
|
+
const previousTask = currentData;
|
|
1608
|
+
currentData = applyUpdateToTask(currentData, {
|
|
1609
|
+
state: "canceled",
|
|
1610
|
+
message: {
|
|
1611
|
+
messageId: crypto.randomUUID(),
|
|
1612
|
+
role: "agent",
|
|
1613
|
+
parts: [{
|
|
1614
|
+
kind: "text",
|
|
1615
|
+
text: "Task canceled because the request was aborted."
|
|
1616
|
+
}],
|
|
1617
|
+
kind: "message"
|
|
1618
|
+
}
|
|
1619
|
+
});
|
|
1620
|
+
try {
|
|
1621
|
+
currentData = await saveTaskAndMaybeSendPushNotification({
|
|
1622
|
+
taskStore,
|
|
1623
|
+
pushNotificationSender: resolvedPushNotificationSender,
|
|
1624
|
+
previousTask,
|
|
1625
|
+
nextTask: currentData,
|
|
1626
|
+
agentId,
|
|
1627
|
+
logger
|
|
1628
|
+
});
|
|
1629
|
+
} catch (saveError) {
|
|
1630
|
+
logger?.error(`Failed to save task ${currentData.id} after request abort:`, saveError?.message);
|
|
1631
|
+
}
|
|
1072
1632
|
}
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1633
|
+
} else {
|
|
1634
|
+
currentData = latestTask ?? currentData;
|
|
1635
|
+
const previousTask = currentData;
|
|
1636
|
+
currentData = applyUpdateToTask(currentData, {
|
|
1637
|
+
state: "failed",
|
|
1638
|
+
message: {
|
|
1639
|
+
messageId: crypto.randomUUID(),
|
|
1640
|
+
role: "agent",
|
|
1641
|
+
parts: [{
|
|
1642
|
+
kind: "text",
|
|
1643
|
+
text: `Handler failed: ${handlerError instanceof Error ? handlerError.message : String(handlerError)}`
|
|
1644
|
+
}],
|
|
1645
|
+
kind: "message"
|
|
1646
|
+
}
|
|
1082
1647
|
});
|
|
1083
|
-
|
|
1084
|
-
|
|
1648
|
+
try {
|
|
1649
|
+
currentData = await saveTaskAndMaybeSendPushNotification({
|
|
1650
|
+
taskStore,
|
|
1651
|
+
pushNotificationSender: resolvedPushNotificationSender,
|
|
1652
|
+
previousTask,
|
|
1653
|
+
nextTask: currentData,
|
|
1654
|
+
agentId,
|
|
1655
|
+
logger
|
|
1656
|
+
});
|
|
1657
|
+
} catch (saveError) {
|
|
1658
|
+
logger?.error(`Failed to save task ${currentData.id} after handler error:`, saveError?.message);
|
|
1659
|
+
}
|
|
1085
1660
|
}
|
|
1661
|
+
} finally {
|
|
1662
|
+
unregisterTaskAbortController();
|
|
1663
|
+
cleanupLinkedAbortController();
|
|
1086
1664
|
}
|
|
1665
|
+
const latestTask = await taskStore.load({
|
|
1666
|
+
agentId,
|
|
1667
|
+
taskId: currentData.id
|
|
1668
|
+
});
|
|
1669
|
+
if (latestTask?.status.state === "canceled") currentData = latestTask;
|
|
1087
1670
|
yield createSuccessResponse(requestId, {
|
|
1088
1671
|
kind: "status-update",
|
|
1089
1672
|
taskId: currentData.id,
|
|
@@ -1099,7 +1682,7 @@ async function* handleTaskResubscribe({ requestId, taskStore, agentId, taskId, a
|
|
|
1099
1682
|
});
|
|
1100
1683
|
if (!snapshot) throw _mastra_core_a2a.MastraA2AError.taskNotFound(taskId);
|
|
1101
1684
|
yield createSuccessResponse(requestId, snapshot.task);
|
|
1102
|
-
if (isTerminalTaskState(snapshot.task.status.state)) return;
|
|
1685
|
+
if (isTerminalTaskState(snapshot.task.status.state) || isInterruptedTaskState(snapshot.task.status.state)) return;
|
|
1103
1686
|
while (true) {
|
|
1104
1687
|
const { task, version } = snapshot;
|
|
1105
1688
|
const nextUpdate = await taskStore.waitForNextUpdate({
|
|
@@ -1112,14 +1695,16 @@ async function* handleTaskResubscribe({ requestId, taskStore, agentId, taskId, a
|
|
|
1112
1695
|
previous: task,
|
|
1113
1696
|
next: nextUpdate.task
|
|
1114
1697
|
})) yield createSuccessResponse(requestId, artifactUpdate);
|
|
1698
|
+
const nextState = nextUpdate.task.status.state;
|
|
1699
|
+
const streamEnded = isTerminalTaskState(nextState) || isInterruptedTaskState(nextState);
|
|
1115
1700
|
if (didTaskStatusChange(task, nextUpdate.task)) yield createSuccessResponse(requestId, {
|
|
1116
1701
|
kind: "status-update",
|
|
1117
1702
|
taskId: nextUpdate.task.id,
|
|
1118
1703
|
contextId: nextUpdate.task.contextId,
|
|
1119
1704
|
status: nextUpdate.task.status,
|
|
1120
|
-
final:
|
|
1705
|
+
final: streamEnded
|
|
1121
1706
|
});
|
|
1122
|
-
if (
|
|
1707
|
+
if (streamEnded) return;
|
|
1123
1708
|
snapshot = nextUpdate;
|
|
1124
1709
|
}
|
|
1125
1710
|
}
|
|
@@ -1157,101 +1742,135 @@ function createA2ASSEResponse(payload) {
|
|
|
1157
1742
|
} });
|
|
1158
1743
|
}
|
|
1159
1744
|
async function handleTaskCancel({ requestId, taskStore, pushNotificationSender, agentId, taskId, logger }) {
|
|
1160
|
-
let
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
logger?.info(`Task ${taskId} already in final state ${data.status.state}, cannot cancel.`);
|
|
1171
|
-
return createSuccessResponse(requestId, data);
|
|
1172
|
-
}
|
|
1173
|
-
taskStore.activeCancellations.add(taskId);
|
|
1174
|
-
const cancelUpdate = {
|
|
1175
|
-
state: "canceled",
|
|
1176
|
-
message: {
|
|
1177
|
-
role: "agent",
|
|
1178
|
-
parts: [{
|
|
1179
|
-
kind: "text",
|
|
1180
|
-
text: "Task cancelled by request."
|
|
1181
|
-
}],
|
|
1182
|
-
kind: "message",
|
|
1183
|
-
messageId: crypto.randomUUID()
|
|
1745
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1746
|
+
const snapshot = taskStore.loadWithVersion({
|
|
1747
|
+
agentId,
|
|
1748
|
+
taskId
|
|
1749
|
+
});
|
|
1750
|
+
const data = snapshot?.task;
|
|
1751
|
+
if (!data) throw _mastra_core_a2a.MastraA2AError.taskNotFound(taskId);
|
|
1752
|
+
if (isTerminalTaskState(data.status.state)) {
|
|
1753
|
+
logger?.info(`Task ${taskId} already in final state ${data.status.state}, cannot cancel.`);
|
|
1754
|
+
throw _mastra_core_a2a.MastraA2AError.taskNotCancelable(taskId);
|
|
1184
1755
|
}
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1756
|
+
taskStore.activeCancellations.add(taskId);
|
|
1757
|
+
taskStore.abortTask({
|
|
1758
|
+
agentId,
|
|
1759
|
+
taskId,
|
|
1760
|
+
reason: new DOMException("Task cancelled by request.", "AbortError")
|
|
1761
|
+
});
|
|
1762
|
+
const canceledTask = applyUpdateToTask(data, {
|
|
1763
|
+
state: "canceled",
|
|
1764
|
+
message: {
|
|
1765
|
+
role: "agent",
|
|
1766
|
+
parts: [{
|
|
1767
|
+
kind: "text",
|
|
1768
|
+
text: "Task cancelled by request."
|
|
1769
|
+
}],
|
|
1770
|
+
kind: "message",
|
|
1771
|
+
messageId: crypto.randomUUID()
|
|
1772
|
+
}
|
|
1773
|
+
});
|
|
1774
|
+
try {
|
|
1775
|
+
await saveTaskAndMaybeSendPushNotification({
|
|
1776
|
+
taskStore,
|
|
1777
|
+
pushNotificationSender: resolvePushNotificationPair({ pushNotificationSender }).pushNotificationSender,
|
|
1778
|
+
previousTask: data,
|
|
1779
|
+
nextTask: canceledTask,
|
|
1780
|
+
agentId,
|
|
1781
|
+
expectedVersion: snapshot.version,
|
|
1782
|
+
logger
|
|
1783
|
+
});
|
|
1784
|
+
return createSuccessResponse(requestId, canceledTask);
|
|
1785
|
+
} catch (error) {
|
|
1786
|
+
if (!(error instanceof require_server_a2a_store.TaskStoreVersionConflictError)) throw error;
|
|
1787
|
+
} finally {
|
|
1788
|
+
taskStore.activeCancellations.delete(taskId);
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
throw _mastra_core_a2a.MastraA2AError.invalidRequest(`Task ${taskId} was updated concurrently. Retry the request.`);
|
|
1198
1792
|
}
|
|
1199
|
-
async function getAgentExecutionHandler({ requestId, mastra, agentId, requestContext, method, params, taskStore, pushNotificationStore, pushNotificationSender, logger, abortSignal }) {
|
|
1793
|
+
async function getAgentExecutionHandler({ requestId, mastra, agentId, requestContext, method, params, taskStore, pushNotificationStore, pushNotificationSender, logger, abortSignal, protocolVersion = "0.3" }) {
|
|
1200
1794
|
const agent = await require_server_handlers_agents.getAgentFromSystem({
|
|
1201
1795
|
mastra,
|
|
1202
1796
|
agentId
|
|
1203
1797
|
});
|
|
1798
|
+
const protocolParams = protocolVersion === "1.0" ? normalizeV1Params(params) : params;
|
|
1204
1799
|
const { pushNotificationStore: resolvedPushNotificationStore, pushNotificationSender: resolvedPushNotificationSender } = resolvePushNotificationPair({
|
|
1205
1800
|
pushNotificationStore,
|
|
1206
1801
|
pushNotificationSender
|
|
1207
1802
|
});
|
|
1208
1803
|
let taskId;
|
|
1209
1804
|
try {
|
|
1210
|
-
taskId = getTaskIdFromParams(
|
|
1805
|
+
taskId = getTaskIdFromParams(protocolParams);
|
|
1211
1806
|
switch (method) {
|
|
1212
|
-
case "message/send":
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1807
|
+
case "message/send": {
|
|
1808
|
+
const result = await handleMessageSend({
|
|
1809
|
+
requestId,
|
|
1810
|
+
params: protocolParams,
|
|
1811
|
+
taskStore,
|
|
1812
|
+
pushNotificationStore: resolvedPushNotificationStore,
|
|
1813
|
+
pushNotificationSender: resolvedPushNotificationSender,
|
|
1814
|
+
agent,
|
|
1815
|
+
agentId,
|
|
1816
|
+
logger,
|
|
1817
|
+
requestContext
|
|
1818
|
+
});
|
|
1819
|
+
return protocolVersion === "1.0" ? convertV1Response(result, method) : result;
|
|
1820
|
+
}
|
|
1821
|
+
case "message/stream": {
|
|
1822
|
+
const result = await handleMessageStream({
|
|
1823
|
+
requestId,
|
|
1824
|
+
taskStore,
|
|
1825
|
+
params: protocolParams,
|
|
1826
|
+
pushNotificationStore: resolvedPushNotificationStore,
|
|
1827
|
+
pushNotificationSender: resolvedPushNotificationSender,
|
|
1828
|
+
agent,
|
|
1829
|
+
agentId,
|
|
1830
|
+
logger,
|
|
1831
|
+
requestContext,
|
|
1832
|
+
abortSignal
|
|
1833
|
+
});
|
|
1834
|
+
return protocolVersion === "1.0" ? convertV1Stream(result, method) : result;
|
|
1835
|
+
}
|
|
1836
|
+
case "tasks/get": {
|
|
1837
|
+
const result = await handleTaskGet({
|
|
1838
|
+
requestId,
|
|
1839
|
+
taskStore,
|
|
1840
|
+
agentId,
|
|
1841
|
+
taskId: taskId || "No task ID provided"
|
|
1842
|
+
});
|
|
1843
|
+
return protocolVersion === "1.0" ? convertV1Response(result, method) : result;
|
|
1844
|
+
}
|
|
1845
|
+
case "tasks/list":
|
|
1846
|
+
if (protocolVersion !== "1.0") throw _mastra_core_a2a.MastraA2AError.methodNotFound(method);
|
|
1847
|
+
return handleTaskList({
|
|
1848
|
+
requestId,
|
|
1849
|
+
taskStore,
|
|
1850
|
+
agentId,
|
|
1851
|
+
params: protocolParams ?? {}
|
|
1852
|
+
});
|
|
1853
|
+
case "tasks/cancel": {
|
|
1854
|
+
const result = await handleTaskCancel({
|
|
1855
|
+
requestId,
|
|
1856
|
+
taskStore,
|
|
1857
|
+
pushNotificationSender: resolvedPushNotificationSender,
|
|
1858
|
+
agentId,
|
|
1859
|
+
taskId: taskId || "No task ID provided",
|
|
1860
|
+
logger
|
|
1861
|
+
});
|
|
1862
|
+
return protocolVersion === "1.0" ? convertV1Response(result, method) : result;
|
|
1863
|
+
}
|
|
1864
|
+
case "tasks/resubscribe": {
|
|
1865
|
+
const result = handleTaskResubscribe({
|
|
1866
|
+
requestId,
|
|
1867
|
+
taskStore,
|
|
1868
|
+
agentId,
|
|
1869
|
+
taskId: taskId || "No task ID provided",
|
|
1870
|
+
abortSignal
|
|
1871
|
+
});
|
|
1872
|
+
return protocolVersion === "1.0" ? convertV1Stream(result, method) : result;
|
|
1873
|
+
}
|
|
1255
1874
|
case "tasks/pushNotificationConfig/set": return await handleSetTaskPushNotificationConfig({
|
|
1256
1875
|
requestId,
|
|
1257
1876
|
taskStore,
|
|
@@ -1288,6 +1907,12 @@ async function getAgentExecutionHandler({ requestId, mastra, agentId, requestCon
|
|
|
1288
1907
|
return normalizeError(error, requestId, taskId, logger);
|
|
1289
1908
|
}
|
|
1290
1909
|
}
|
|
1910
|
+
function resolveA2AProtocolVersion(request) {
|
|
1911
|
+
const version = request?.headers.get("A2A-Version")?.trim();
|
|
1912
|
+
if (!version || version === "0.3") return "0.3";
|
|
1913
|
+
if (version === "1.0") return "1.0";
|
|
1914
|
+
throw _mastra_core_a2a.MastraA2AError.versionNotSupported(version);
|
|
1915
|
+
}
|
|
1291
1916
|
const GET_AGENT_CARD_ROUTE = require_route_builder.createRoute({
|
|
1292
1917
|
method: "GET",
|
|
1293
1918
|
path: "/.well-known/:agentId/agent-card.json",
|
|
@@ -1324,17 +1949,25 @@ const AGENT_EXECUTION_ROUTE = require_route_builder.createRoute({
|
|
|
1324
1949
|
description: "Executes an agent action via JSON-RPC 2.0 over A2A protocol",
|
|
1325
1950
|
tags: ["Agent-to-Agent"],
|
|
1326
1951
|
requiresAuth: true,
|
|
1327
|
-
handler: async ({ mastra, agentId, requestContext, taskStore, abortSignal, ...bodyParams }) => {
|
|
1952
|
+
handler: async ({ mastra, agentId, requestContext, taskStore, abortSignal, request, ...bodyParams }) => {
|
|
1328
1953
|
const { id: requestId, method } = bodyParams;
|
|
1954
|
+
const params = "params" in bodyParams ? bodyParams.params : void 0;
|
|
1955
|
+
let protocolVersion;
|
|
1956
|
+
try {
|
|
1957
|
+
protocolVersion = resolveA2AProtocolVersion(request);
|
|
1958
|
+
} catch (error) {
|
|
1959
|
+
return createA2AJsonResponse(normalizeError(error, requestId));
|
|
1960
|
+
}
|
|
1329
1961
|
const result = await getAgentExecutionHandler({
|
|
1330
1962
|
requestId,
|
|
1331
1963
|
mastra,
|
|
1332
1964
|
agentId,
|
|
1333
1965
|
requestContext,
|
|
1334
1966
|
method,
|
|
1335
|
-
params
|
|
1967
|
+
params,
|
|
1336
1968
|
taskStore,
|
|
1337
|
-
abortSignal
|
|
1969
|
+
abortSignal,
|
|
1970
|
+
protocolVersion
|
|
1338
1971
|
});
|
|
1339
1972
|
if (method === "message/stream" || method === "tasks/resubscribe") return createA2ASSEResponse(result);
|
|
1340
1973
|
return createA2AJsonResponse(result);
|
|
@@ -1419,11 +2052,23 @@ Object.defineProperty(exports, "handleTaskGet", {
|
|
|
1419
2052
|
return handleTaskGet;
|
|
1420
2053
|
}
|
|
1421
2054
|
});
|
|
2055
|
+
Object.defineProperty(exports, "handleTaskList", {
|
|
2056
|
+
enumerable: true,
|
|
2057
|
+
get: function() {
|
|
2058
|
+
return handleTaskList;
|
|
2059
|
+
}
|
|
2060
|
+
});
|
|
1422
2061
|
Object.defineProperty(exports, "handleTaskResubscribe", {
|
|
1423
2062
|
enumerable: true,
|
|
1424
2063
|
get: function() {
|
|
1425
2064
|
return handleTaskResubscribe;
|
|
1426
2065
|
}
|
|
1427
2066
|
});
|
|
2067
|
+
Object.defineProperty(exports, "resolveA2AProtocolVersion", {
|
|
2068
|
+
enumerable: true,
|
|
2069
|
+
get: function() {
|
|
2070
|
+
return resolveA2AProtocolVersion;
|
|
2071
|
+
}
|
|
2072
|
+
});
|
|
1428
2073
|
|
|
1429
|
-
//# sourceMappingURL=a2a-
|
|
2074
|
+
//# sourceMappingURL=a2a-B6YkZ2i9.cjs.map
|