@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.
Files changed (127) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/dist/_types/@mastra_schema-compat/dist/provider-compats/anthropic.d.ts +1 -1
  3. package/dist/{a2a-CnU9zaGR.js → a2a-8C4O2dVJ.js} +41 -5
  4. package/dist/a2a-8C4O2dVJ.js.map +1 -0
  5. package/dist/{a2a-DKnwIgiw.cjs → a2a-B6YkZ2i9.cjs} +872 -227
  6. package/dist/a2a-B6YkZ2i9.cjs.map +1 -0
  7. package/dist/{a2a-oM7aqLz4.cjs → a2a-MPoM5lzS.cjs} +41 -5
  8. package/dist/a2a-MPoM5lzS.cjs.map +1 -0
  9. package/dist/{a2a-uPi5sPwb.js → a2a-_D2EA4oa.js} +861 -228
  10. package/dist/a2a-_D2EA4oa.js.map +1 -0
  11. package/dist/{agent-builder-BDYN3r89.cjs → agent-builder-DAiJH1u2.cjs} +2 -2
  12. package/dist/{agent-builder-BDYN3r89.cjs.map → agent-builder-DAiJH1u2.cjs.map} +1 -1
  13. package/dist/{agent-builder-Dqy_qumV.js → agent-builder-gGFywDVq.js} +2 -2
  14. package/dist/{agent-builder-Dqy_qumV.js.map → agent-builder-gGFywDVq.js.map} +1 -1
  15. package/dist/{api-schema-manifest-7-NC9Dyc.cjs → api-schema-manifest-B3bC-D98.cjs} +2 -2
  16. package/dist/{api-schema-manifest-7-NC9Dyc.cjs.map → api-schema-manifest-B3bC-D98.cjs.map} +1 -1
  17. package/dist/{api-schema-manifest-C5hpdIE_.js → api-schema-manifest-B4J_QYOr.js} +2 -2
  18. package/dist/{api-schema-manifest-C5hpdIE_.js.map → api-schema-manifest-B4J_QYOr.js.map} +1 -1
  19. package/dist/{datasets-Cvix7Rms.cjs → datasets-BMeFvFwT.cjs} +43 -1
  20. package/dist/datasets-BMeFvFwT.cjs.map +1 -0
  21. package/dist/{datasets-dhXnHLAL.js → datasets-BeBH9Seg.js} +38 -2
  22. package/dist/datasets-BeBH9Seg.js.map +1 -0
  23. package/dist/{dist-DGqlUp0b.js → dist-BKRMiJcn.js} +215 -74
  24. package/dist/dist-BKRMiJcn.js.map +1 -0
  25. package/dist/{dist-COHIL6fR.cjs → dist-C2-SedDQ.cjs} +215 -74
  26. package/dist/dist-C2-SedDQ.cjs.map +1 -0
  27. package/dist/docs/SKILL.md +1 -1
  28. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  29. package/dist/docs/references/docs-agents-a2a.md +75 -2
  30. package/dist/docs/references/reference-server-routes.md +25 -11
  31. package/dist/route-builder-CIk5k35M.js.map +1 -1
  32. package/dist/route-builder-CgLVYL1c.cjs.map +1 -1
  33. package/dist/{routes-hSY4wTeS.cjs → routes-CZwPlO3-.cjs} +3 -2
  34. package/dist/routes-CZwPlO3-.cjs.map +1 -0
  35. package/dist/{routes-CRoTKPvQ.js → routes-HUaCyUb7.js} +4 -3
  36. package/dist/{routes-CRoTKPvQ.js.map → routes-HUaCyUb7.js.map} +1 -1
  37. package/dist/server/a2a/store.cjs +30 -2
  38. package/dist/server/a2a/store.cjs.map +1 -1
  39. package/dist/server/a2a/store.d.ts +21 -2
  40. package/dist/server/a2a/store.d.ts.map +1 -1
  41. package/dist/server/a2a/store.js +30 -3
  42. package/dist/server/a2a/store.js.map +1 -1
  43. package/dist/server/a2a/task-state.d.ts +4 -0
  44. package/dist/server/a2a/task-state.d.ts.map +1 -0
  45. package/dist/server/a2a/tasks.d.ts +1 -1
  46. package/dist/server/a2a/tasks.d.ts.map +1 -1
  47. package/dist/server/handlers/a2a.cjs +3 -1
  48. package/dist/server/handlers/a2a.d.ts +155 -12
  49. package/dist/server/handlers/a2a.d.ts.map +1 -1
  50. package/dist/server/handlers/a2a.js +2 -2
  51. package/dist/server/handlers/agent-builder.cjs +3 -3
  52. package/dist/server/handlers/agent-builder.js +3 -3
  53. package/dist/server/handlers/agent-controller.cjs +25 -6
  54. package/dist/server/handlers/agent-controller.cjs.map +1 -1
  55. package/dist/server/handlers/agent-controller.d.ts +4 -3
  56. package/dist/server/handlers/agent-controller.d.ts.map +1 -1
  57. package/dist/server/handlers/agent-controller.js +25 -6
  58. package/dist/server/handlers/agent-controller.js.map +1 -1
  59. package/dist/server/handlers/agent-versions.cjs +6 -0
  60. package/dist/server/handlers/agent-versions.cjs.map +1 -1
  61. package/dist/server/handlers/agent-versions.d.ts.map +1 -1
  62. package/dist/server/handlers/agent-versions.js +6 -0
  63. package/dist/server/handlers/agent-versions.js.map +1 -1
  64. package/dist/server/handlers/datasets.cjs +26 -11
  65. package/dist/server/handlers/datasets.cjs.map +1 -1
  66. package/dist/server/handlers/datasets.d.ts +136 -0
  67. package/dist/server/handlers/datasets.d.ts.map +1 -1
  68. package/dist/server/handlers/datasets.js +26 -11
  69. package/dist/server/handlers/datasets.js.map +1 -1
  70. package/dist/server/handlers/observability.cjs +20 -9
  71. package/dist/server/handlers/observability.cjs.map +1 -1
  72. package/dist/server/handlers/observability.d.ts.map +1 -1
  73. package/dist/server/handlers/observability.js +20 -9
  74. package/dist/server/handlers/observability.js.map +1 -1
  75. package/dist/server/handlers/stored-agents.cjs +16 -1
  76. package/dist/server/handlers/stored-agents.cjs.map +1 -1
  77. package/dist/server/handlers/stored-agents.d.ts.map +1 -1
  78. package/dist/server/handlers/stored-agents.js +16 -1
  79. package/dist/server/handlers/stored-agents.js.map +1 -1
  80. package/dist/server/handlers/system.cjs +1 -1
  81. package/dist/server/handlers/system.js +1 -1
  82. package/dist/server/handlers/validate-agent-instructions.cjs +52 -0
  83. package/dist/server/handlers/validate-agent-instructions.cjs.map +1 -0
  84. package/dist/server/handlers/validate-agent-instructions.d.ts +13 -0
  85. package/dist/server/handlers/validate-agent-instructions.d.ts.map +1 -0
  86. package/dist/server/handlers/validate-agent-instructions.js +51 -0
  87. package/dist/server/handlers/validate-agent-instructions.js.map +1 -0
  88. package/dist/server/handlers/workflows.cjs +105 -2
  89. package/dist/server/handlers/workflows.cjs.map +1 -1
  90. package/dist/server/handlers/workflows.d.ts +9 -0
  91. package/dist/server/handlers/workflows.d.ts.map +1 -1
  92. package/dist/server/handlers/workflows.js +104 -3
  93. package/dist/server/handlers/workflows.js.map +1 -1
  94. package/dist/server/handlers.cjs +1 -1
  95. package/dist/server/handlers.js +1 -1
  96. package/dist/server/schemas/a2a.d.ts +70 -6
  97. package/dist/server/schemas/a2a.d.ts.map +1 -1
  98. package/dist/server/schemas/datasets.d.ts +53 -0
  99. package/dist/server/schemas/datasets.d.ts.map +1 -1
  100. package/dist/server/schemas/index.cjs +5 -4
  101. package/dist/server/schemas/index.js +5 -5
  102. package/dist/server/schemas/workflows.d.ts +8 -0
  103. package/dist/server/schemas/workflows.d.ts.map +1 -1
  104. package/dist/server/server-adapter/index.cjs +1 -1
  105. package/dist/server/server-adapter/index.js +1 -1
  106. package/dist/server/server-adapter/routes/a2a.d.ts +116 -8
  107. package/dist/server/server-adapter/routes/a2a.d.ts.map +1 -1
  108. package/dist/server/server-adapter/routes/datasets.d.ts +136 -0
  109. package/dist/server/server-adapter/routes/datasets.d.ts.map +1 -1
  110. package/dist/server/server-adapter/routes/workflows.d.ts +7 -1
  111. package/dist/server/server-adapter/routes/workflows.d.ts.map +1 -1
  112. package/dist/{workflows-DPW6FrY0.js → workflows-CMU5dz_4.js} +7 -2
  113. package/dist/workflows-CMU5dz_4.js.map +1 -0
  114. package/dist/{workflows-XfiENvkF.cjs → workflows-Dzxr6cmf.cjs} +12 -1
  115. package/dist/workflows-Dzxr6cmf.cjs.map +1 -0
  116. package/package.json +8 -8
  117. package/dist/a2a-CnU9zaGR.js.map +0 -1
  118. package/dist/a2a-DKnwIgiw.cjs.map +0 -1
  119. package/dist/a2a-oM7aqLz4.cjs.map +0 -1
  120. package/dist/a2a-uPi5sPwb.js.map +0 -1
  121. package/dist/datasets-Cvix7Rms.cjs.map +0 -1
  122. package/dist/datasets-dhXnHLAL.js.map +0 -1
  123. package/dist/dist-COHIL6fR.cjs.map +0 -1
  124. package/dist/dist-DGqlUp0b.js.map +0 -1
  125. package/dist/routes-hSY4wTeS.cjs.map +0 -1
  126. package/dist/workflows-DPW6FrY0.js.map +0 -1
  127. 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 require_a2a = require("./a2a-oM7aqLz4.cjs");
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 (!id) throw _mastra_core_a2a.MastraA2AError.internalError("Cannot create success response for null ID.");
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
- const data = await taskStore.load({
345
- agentId,
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
- data: initialTask
356
+ taskId
366
357
  });
367
- return initialTask;
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
- logger?.info(`[Task ${taskId}] Loaded existing task.`);
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
- handleTaskResubscribe: () => handleTaskResubscribe
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
- "input-required"
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
- async function saveTaskAndMaybeSendPushNotification({ taskStore, pushNotificationSender, previousTask, nextTask, agentId, logger }) {
611
- await taskStore.save({
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 (!shouldSendPushNotification(previousTask, nextTask)) return;
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: nextTask,
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 isTerminalTaskState(state) {
646
- return [
647
- "completed",
648
- "failed",
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.generate([convertToCoreMessage(message)], {
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 = await taskStore.load({
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
- yield createSuccessResponse(requestId, currentData);
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.stream([convertToCoreMessage(message)], {
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
- structuredData ??= await result.object;
996
- if (!pendingTextChunk && !sawTextArtifact) {
997
- const finalText = await result.text;
998
- if (finalText) pendingTextChunk = finalText;
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 (pendingTextChunk) {
1001
- const textUpdate = createTextChunkArtifactUpdate({
1002
- taskId: currentData.id,
1003
- contextId: currentData.contextId,
1004
- text: pendingTextChunk,
1005
- append: sawTextArtifact,
1006
- lastChunk: !structuredData
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 = applyUpdateToTask(currentData, textUpdate);
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 (structuredData) {
1021
- const dataUpdate = createDataArtifactUpdate({
1022
- taskId: currentData.id,
1023
- contextId: currentData.contextId,
1024
- data: structuredData,
1025
- lastChunk: true
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 = applyUpdateToTask(currentData, dataUpdate);
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
- yield createSuccessResponse(requestId, dataUpdate);
1526
+ if (currentData.status.state === "canceled") streamCanceled = true;
1036
1527
  }
1037
- const previousTask = currentData;
1038
- const completedTask = applyUpdateToTask(currentData, {
1039
- state: "completed",
1040
- message: void 0
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
- currentData = completedTask;
1052
- await saveTaskAndMaybeSendPushNotification({
1053
- taskStore,
1054
- pushNotificationSender: resolvedPushNotificationSender,
1055
- previousTask,
1056
- nextTask: currentData,
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
- logger
1601
+ taskId: currentData.id
1059
1602
  });
1060
- } catch (handlerError) {
1061
- const previousTask = currentData;
1062
- currentData = applyUpdateToTask(currentData, {
1063
- state: "failed",
1064
- message: {
1065
- messageId: crypto.randomUUID(),
1066
- role: "agent",
1067
- parts: [{
1068
- kind: "text",
1069
- text: `Handler failed: ${handlerError instanceof Error ? handlerError.message : String(handlerError)}`
1070
- }],
1071
- kind: "message"
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
- try {
1075
- await saveTaskAndMaybeSendPushNotification({
1076
- taskStore,
1077
- pushNotificationSender: resolvedPushNotificationSender,
1078
- previousTask,
1079
- nextTask: currentData,
1080
- agentId,
1081
- logger
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
- } catch (saveError) {
1084
- logger?.error(`Failed to save task ${currentData.id} after handler error:`, saveError?.message);
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: isTerminalTaskState(nextUpdate.task.status.state)
1705
+ final: streamEnded
1121
1706
  });
1122
- if (isTerminalTaskState(nextUpdate.task.status.state)) return;
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 data = await taskStore.load({
1161
- agentId,
1162
- taskId
1163
- });
1164
- if (!data) throw _mastra_core_a2a.MastraA2AError.taskNotFound(taskId);
1165
- if ([
1166
- "completed",
1167
- "failed",
1168
- "canceled"
1169
- ].includes(data.status.state)) {
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
- const previousTask = data;
1187
- data = applyUpdateToTask(data, cancelUpdate);
1188
- await saveTaskAndMaybeSendPushNotification({
1189
- taskStore,
1190
- pushNotificationSender: resolvePushNotificationPair({ pushNotificationSender }).pushNotificationSender,
1191
- previousTask,
1192
- nextTask: data,
1193
- agentId,
1194
- logger
1195
- });
1196
- taskStore.activeCancellations.delete(taskId);
1197
- return createSuccessResponse(requestId, data);
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(params);
1805
+ taskId = getTaskIdFromParams(protocolParams);
1211
1806
  switch (method) {
1212
- case "message/send": return await handleMessageSend({
1213
- requestId,
1214
- params,
1215
- taskStore,
1216
- pushNotificationStore: resolvedPushNotificationStore,
1217
- pushNotificationSender: resolvedPushNotificationSender,
1218
- agent,
1219
- agentId,
1220
- logger,
1221
- requestContext
1222
- });
1223
- case "message/stream": return await handleMessageStream({
1224
- requestId,
1225
- taskStore,
1226
- params,
1227
- pushNotificationStore: resolvedPushNotificationStore,
1228
- pushNotificationSender: resolvedPushNotificationSender,
1229
- agent,
1230
- agentId,
1231
- logger,
1232
- requestContext
1233
- });
1234
- case "tasks/get": return await handleTaskGet({
1235
- requestId,
1236
- taskStore,
1237
- agentId,
1238
- taskId: taskId || "No task ID provided"
1239
- });
1240
- case "tasks/cancel": return await handleTaskCancel({
1241
- requestId,
1242
- taskStore,
1243
- pushNotificationSender: resolvedPushNotificationSender,
1244
- agentId,
1245
- taskId: taskId || "No task ID provided",
1246
- logger
1247
- });
1248
- case "tasks/resubscribe": return await handleTaskResubscribe({
1249
- requestId,
1250
- taskStore,
1251
- agentId,
1252
- taskId: taskId || "No task ID provided",
1253
- abortSignal
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: "params" in bodyParams ? bodyParams.params : void 0,
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-DKnwIgiw.cjs.map
2074
+ //# sourceMappingURL=a2a-B6YkZ2i9.cjs.map