@adcp/sdk 14.0.0-beta.5 → 14.0.0-beta.6

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 (80) hide show
  1. package/dist/lib/core/AgentClient.d.ts.map +1 -1
  2. package/dist/lib/core/AgentClient.js +26 -10
  3. package/dist/lib/core/AgentClient.js.map +1 -1
  4. package/dist/lib/core/AgentClient.mjs +26 -10
  5. package/dist/lib/core/AgentClient.mjs.map +1 -1
  6. package/dist/lib/core/AsyncHandler.d.mts +4 -2
  7. package/dist/lib/core/AsyncHandler.d.ts +4 -2
  8. package/dist/lib/core/AsyncHandler.d.ts.map +1 -1
  9. package/dist/lib/core/AsyncHandler.js +15 -1
  10. package/dist/lib/core/AsyncHandler.js.map +1 -1
  11. package/dist/lib/core/AsyncHandler.mjs +15 -1
  12. package/dist/lib/core/AsyncHandler.mjs.map +1 -1
  13. package/dist/lib/core/SingleAgentClient.d.mts +3 -1
  14. package/dist/lib/core/SingleAgentClient.d.ts +3 -1
  15. package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
  16. package/dist/lib/core/SingleAgentClient.js +158 -39
  17. package/dist/lib/core/SingleAgentClient.js.map +1 -1
  18. package/dist/lib/core/SingleAgentClient.mjs +167 -40
  19. package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
  20. package/dist/lib/core/TaskExecutor.d.mts +57 -2
  21. package/dist/lib/core/TaskExecutor.d.ts +57 -2
  22. package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
  23. package/dist/lib/core/TaskExecutor.js +382 -44
  24. package/dist/lib/core/TaskExecutor.js.map +1 -1
  25. package/dist/lib/core/TaskExecutor.mjs +382 -45
  26. package/dist/lib/core/TaskExecutor.mjs.map +1 -1
  27. package/dist/lib/core/request-proposals-guard.d.mts +11 -0
  28. package/dist/lib/core/request-proposals-guard.d.ts +12 -0
  29. package/dist/lib/core/request-proposals-guard.d.ts.map +1 -0
  30. package/dist/lib/core/request-proposals-guard.js +65 -0
  31. package/dist/lib/core/request-proposals-guard.js.map +1 -0
  32. package/dist/lib/core/request-proposals-guard.mjs +39 -0
  33. package/dist/lib/core/request-proposals-guard.mjs.map +1 -0
  34. package/dist/lib/core/webhook-registration.d.mts +5 -0
  35. package/dist/lib/core/webhook-registration.d.ts +5 -0
  36. package/dist/lib/core/webhook-registration.d.ts.map +1 -1
  37. package/dist/lib/core/webhook-registration.js +13 -0
  38. package/dist/lib/core/webhook-registration.js.map +1 -1
  39. package/dist/lib/core/webhook-registration.mjs +13 -0
  40. package/dist/lib/core/webhook-registration.mjs.map +1 -1
  41. package/dist/lib/media-buy/compatibility.d.mts +45 -10
  42. package/dist/lib/media-buy/compatibility.d.ts +45 -10
  43. package/dist/lib/media-buy/compatibility.d.ts.map +1 -1
  44. package/dist/lib/media-buy/compatibility.js +295 -113
  45. package/dist/lib/media-buy/compatibility.js.map +1 -1
  46. package/dist/lib/media-buy/compatibility.mjs +295 -113
  47. package/dist/lib/media-buy/compatibility.mjs.map +1 -1
  48. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  49. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  50. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  51. package/dist/lib/types/core.generated.d.mts +334 -85
  52. package/dist/lib/types/core.generated.d.ts +334 -85
  53. package/dist/lib/types/core.generated.d.ts.map +1 -1
  54. package/dist/lib/types/core.generated.js.map +1 -1
  55. package/dist/lib/types/decline-proposals.d.ts +0 -5
  56. package/dist/lib/types/index.d.mts +2 -0
  57. package/dist/lib/types/index.d.ts +2 -0
  58. package/dist/lib/types/index.d.ts.map +1 -1
  59. package/dist/lib/types/index.js.map +1 -1
  60. package/dist/lib/types/index.mjs.map +1 -1
  61. package/dist/lib/types/request-proposals.d.ts +254 -36
  62. package/dist/lib/types/schemas.generated.d.ts +3123 -835
  63. package/dist/lib/types/schemas.generated.d.ts.map +1 -1
  64. package/dist/lib/types/schemas.generated.js +192 -85
  65. package/dist/lib/types/schemas.generated.js.map +1 -1
  66. package/dist/lib/types/schemas.generated.mjs +192 -85
  67. package/dist/lib/types/schemas.generated.mjs.map +1 -1
  68. package/dist/lib/types/tools.generated.d.mts +1 -121
  69. package/dist/lib/types/tools.generated.d.ts +1 -121
  70. package/dist/lib/types/tools.generated.d.ts.map +1 -1
  71. package/dist/lib/types/tools.generated.js.map +1 -1
  72. package/dist/lib/version.d.mts +3 -3
  73. package/dist/lib/version.d.ts +3 -3
  74. package/dist/lib/version.js +3 -3
  75. package/dist/lib/version.js.map +1 -1
  76. package/dist/lib/version.mjs +3 -3
  77. package/dist/lib/version.mjs.map +1 -1
  78. package/docs/guides/MEDIA-BUY-3.2-COMPATIBILITY.md +28 -14
  79. package/docs/llms.txt +1 -1
  80. package/package.json +1 -1
@@ -45,6 +45,7 @@ var import_idempotency = require('../utils/idempotency.js');
45
45
  var import_client = require("@a2a-js/sdk/client");
46
46
  var import_TaskExecutor = require('./TaskExecutor.js');
47
47
  var import_match = require('./match.js');
48
+ var import_request_proposals_guard = require('./request-proposals-guard.js');
48
49
  var import_task_deadline = require('./task-deadline.js');
49
50
  var import_auth = require('../auth/index.js');
50
51
  var import_abort = require('../protocols/abort.js');
@@ -145,6 +146,26 @@ const CANONICAL_CREATIVE_ACTIVITY_TASKS = /* @__PURE__ */ new Set([
145
146
  "get_media_buy_delivery",
146
147
  "get_creative_delivery"
147
148
  ]);
149
+ const RECORDLESS_HMAC_READ_ONLY_TASKS = /* @__PURE__ */ new Set([
150
+ "get_adcp_capabilities",
151
+ "list_products",
152
+ "list_creative_formats",
153
+ "preview_creative",
154
+ "list_transformers",
155
+ "list_creatives",
156
+ "get_media_buys",
157
+ "get_media_buy_delivery",
158
+ "media_buy_delivery",
159
+ "get_creative_delivery",
160
+ "get_signals",
161
+ "list_accounts",
162
+ "get_property_list",
163
+ "list_property_lists",
164
+ "list_content_standards",
165
+ "get_content_standards",
166
+ "si_get_offering",
167
+ "get_plan_audit_logs"
168
+ ]);
148
169
  const TASK_SCOPED_STATE_LIMIT = 1e4;
149
170
  const CANONICAL_PACKAGE_ROUTE_TASKS = /* @__PURE__ */ new Set(["create_media_buy", "update_media_buy", "get_media_buys"]);
150
171
  const canonicalCreativeRoutingSnapshots = /* @__PURE__ */ new WeakSet();
@@ -612,6 +633,8 @@ class SingleAgentClient {
612
633
  agentId: agent.id,
613
634
  webhookSecret: config.webhookSecret,
614
635
  onWebhookRegistration: (registration) => this.persistWebhookRegistration(registration),
636
+ onDurableSettlementRequired: (operationId) => this.markWebhookDurableSettlementRequired(operationId),
637
+ externalTaskSettlementRetentionMs: registrationTtl * 1e3,
615
638
  strictSchemaValidation: config.validation?.strictSchemaValidation !== false,
616
639
  // Default: true
617
640
  logSchemaViolations: config.validation?.logSchemaViolations !== false,
@@ -679,10 +702,14 @@ class SingleAgentClient {
679
702
  this.canonicalCreativeTaskAssociations.delete(args.operationId);
680
703
  }
681
704
  }
705
+ const persistedAgentUrl = new URL(args.agent.agent_uri);
706
+ persistedAgentUrl.username = "";
707
+ persistedAgentUrl.password = "";
708
+ persistedAgentUrl.hash = "";
682
709
  try {
683
710
  await this.webhookRegistrationStore.putIfAbsent({
684
711
  agentId: args.agent.id,
685
- agentUrl: args.agent.agent_uri,
712
+ agentUrl: persistedAgentUrl.toString(),
686
713
  protocol: args.agent.protocol,
687
714
  operationId: args.operationId,
688
715
  taskType: args.taskType,
@@ -697,6 +724,15 @@ class SingleAgentClient {
697
724
  if (args.mode === "rfc9421") throw cause;
698
725
  }
699
726
  }
727
+ async markWebhookDurableSettlementRequired(operationId) {
728
+ const mark = this.webhookRegistrationStore.markRequiresDurableSettlement;
729
+ if (!mark) {
730
+ throw new import_errors.ConfigurationError(
731
+ "A custom webhookRegistrationStore must implement markRequiresDurableSettlement for durable mutation callbacks."
732
+ );
733
+ }
734
+ await mark.call(this.webhookRegistrationStore, this.agent.id, operationId);
735
+ }
700
736
  webhookJwksFor(registration) {
701
737
  const configured = this.config.webhookVerification?.jwks;
702
738
  if (configured) return configured;
@@ -1421,7 +1457,8 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
1421
1457
  try {
1422
1458
  registration = await this.webhookRegistrationStore.get(this.agent.id, trustedOperationId);
1423
1459
  } catch (cause) {
1424
- if (!this.config.webhookSecret) {
1460
+ const routedTaskType2 = options.taskType && options.taskType !== "unknown" ? options.taskType : void 0;
1461
+ if (!this.config.webhookSecret || routedTaskType2 === void 0 || !RECORDLESS_HMAC_READ_ONLY_TASKS.has(routedTaskType2)) {
1425
1462
  return {
1426
1463
  ok: false,
1427
1464
  code: "webhook_registration_store_unavailable",
@@ -1447,8 +1484,29 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
1447
1484
  message: "Webhook registration state contains invalid timestamps."
1448
1485
  };
1449
1486
  }
1487
+ try {
1488
+ const registeredAgentUrl = new URL(registration.agentUrl);
1489
+ if (registeredAgentUrl.username || registeredAgentUrl.password) {
1490
+ throw new TypeError("Webhook registration seller URL contains userinfo.");
1491
+ }
1492
+ } catch (cause) {
1493
+ return {
1494
+ ok: false,
1495
+ code: "webhook_registration_store_unavailable",
1496
+ message: "Webhook registration state contains an invalid seller URL.",
1497
+ cause
1498
+ };
1499
+ }
1450
1500
  if (registration.expiresAt <= nowMs) registration = void 0;
1451
1501
  }
1502
+ const trustedRouteTaskType = options.taskType && options.taskType !== "unknown" ? options.taskType : void 0;
1503
+ if (!registration && this.config.webhookSecret && (trustedRouteTaskType === void 0 || !RECORDLESS_HMAC_READ_ONLY_TASKS.has(trustedRouteTaskType))) {
1504
+ return {
1505
+ ok: false,
1506
+ code: "webhook_registration_store_unavailable",
1507
+ message: "A trusted webhook registration is required for this task type."
1508
+ };
1509
+ }
1452
1510
  if (authHeaders.hasRfc9421 && !trustedOperationId) {
1453
1511
  return {
1454
1512
  ok: false,
@@ -1703,7 +1761,8 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
1703
1761
  message: canonicalMessage,
1704
1762
  previewHandler,
1705
1763
  timestamp: normalizedPayload.timestamp,
1706
- idempotencyKey: normalizedPayload.idempotency_key
1764
+ idempotencyKey: normalizedPayload.idempotency_key,
1765
+ requiresDurableSettlement: registration?.requiresDurableSettlement
1707
1766
  }
1708
1767
  };
1709
1768
  } catch (error) {
@@ -1768,8 +1827,52 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
1768
1827
  canonicalResult,
1769
1828
  metadata
1770
1829
  );
1771
- const webhookResult = policyDispatch.result;
1830
+ let webhookResult = policyDispatch.result;
1772
1831
  metadata = policyDispatch.metadata;
1832
+ if (metadata.task_type === "request_proposals") {
1833
+ (0, import_request_proposals_guard.assertNativeRequestProposalsResult)(webhookResult);
1834
+ }
1835
+ if (parsed.metadata.requiresDurableSettlement === true && !this.executor.hasExternalTaskSettlementRoute(metadata.operation_id)) {
1836
+ throw new WebhookDispatchError(
1837
+ "webhook_durable_settlement_unavailable",
1838
+ "The callback requires durable mutation settlement, but no recoverable settlement route is available."
1839
+ );
1840
+ }
1841
+ const externalStatus = await this.executor.observeExternalTaskStatus(
1842
+ metadata.operation_id,
1843
+ metadata.status,
1844
+ webhookResult,
1845
+ { serverTaskId: metadata.task_id, taskType: metadata.task_type }
1846
+ );
1847
+ if (externalStatus.duplicate) {
1848
+ metadata = {
1849
+ ...metadata,
1850
+ status: externalStatus.status ?? metadata.status,
1851
+ ...externalStatus.error !== void 0 && { message: externalStatus.error }
1852
+ };
1853
+ const duplicateActivity = {
1854
+ type: "webhook_duplicate",
1855
+ operation_id: metadata.operation_id,
1856
+ agent_id: metadata.agent_id,
1857
+ context_id: metadata.context_id,
1858
+ task_id: metadata.task_id,
1859
+ task_type: metadata.task_type,
1860
+ status: metadata.status,
1861
+ idempotency_key: metadata.idempotency_key,
1862
+ timestamp: metadata.timestamp
1863
+ };
1864
+ await this.config.onActivity?.(canonicalCreativeActivity(duplicateActivity));
1865
+ await this.asyncHandler?.handleDurableSettlementDuplicate(metadata);
1866
+ return true;
1867
+ }
1868
+ if (externalStatus.settled) {
1869
+ webhookResult = externalStatus.result;
1870
+ metadata = {
1871
+ ...metadata,
1872
+ status: externalStatus.status ?? metadata.status,
1873
+ ...externalStatus.error !== void 0 && { message: externalStatus.error }
1874
+ };
1875
+ }
1773
1876
  await this.config.onActivity?.(
1774
1877
  canonicalCreativeActivity({
1775
1878
  type: "webhook_received",
@@ -1779,7 +1882,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
1779
1882
  task_id: metadata.task_id,
1780
1883
  task_type: metadata.task_type,
1781
1884
  status: metadata.status,
1782
- payload: canonicalResult,
1885
+ payload: webhookResult,
1783
1886
  timestamp: metadata.timestamp
1784
1887
  })
1785
1888
  );
@@ -1794,11 +1897,6 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
1794
1897
  }
1795
1898
  return false;
1796
1899
  } finally {
1797
- this.executor.observeExternalTaskStatus(
1798
- metadata.operation_id,
1799
- metadata.status,
1800
- metadata.rawHTTPPayload
1801
- );
1802
1900
  this.forgetProductPolicyRequestParams(metadata);
1803
1901
  }
1804
1902
  }
@@ -2055,7 +2153,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
2055
2153
  res.end(JSON.stringify({ status: "accepted", received: handled }));
2056
2154
  }
2057
2155
  } catch (error) {
2058
- const errorMessage = error instanceof Error ? error.message : String(error);
2156
+ const errorMessage = error instanceof WebhookDispatchError ? error.message : "Webhook could not be processed.";
2059
2157
  const statusCode = webhookErrorHttpStatus(error);
2060
2158
  if (res.json) {
2061
2159
  res.status(statusCode).json({ error: errorMessage });
@@ -3274,9 +3372,10 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
3274
3372
  }
3275
3373
  /** @internal Run deterministic legacy-create preflight before the caller atomically claims its mutation. */
3276
3374
  async createMediaBuyLegacyWithPreDispatch(params, beforeDispatch, inputHandler, options) {
3375
+ const requestSnapshot = structuredClone(params);
3277
3376
  return this.executeTaskUnprojected(
3278
3377
  "create_media_buy",
3279
- params,
3378
+ requestSnapshot,
3280
3379
  inputHandler,
3281
3380
  options,
3282
3381
  "onCreateMediaBuyStatusChange",
@@ -3966,6 +4065,10 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
3966
4065
  return this.getMediaBuyDelivery(params, inputHandler, options);
3967
4066
  case "get_creative_delivery":
3968
4067
  return this.getCreativeDelivery(params, inputHandler, options);
4068
+ case "request_proposals":
4069
+ return (0, import_request_proposals_guard.guardNativeRequestProposalsCompletion)(
4070
+ await this.executeTaskUnprojected(taskName, params, inputHandler, options)
4071
+ );
3969
4072
  }
3970
4073
  return this.executeTaskUnprojected(taskName, params, inputHandler, options);
3971
4074
  }
@@ -4071,7 +4174,9 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4071
4174
  result = await this.applyProductPropertyPolicy(result, taskName, normalizedParams);
4072
4175
  return result;
4073
4176
  } catch (error) {
4074
- if (error instanceof import_TaskExecutor.BeforeProtocolDispatchHookError) throw error.original;
4177
+ if (error instanceof import_TaskExecutor.BeforeProtocolDispatchHookError || error instanceof import_TaskExecutor.AfterProtocolDispatchHookError) {
4178
+ throw error.original;
4179
+ }
4075
4180
  if (error instanceof import_errors.AuthenticationRequiredError || error instanceof import_errors.TaskTimeoutError || error instanceof import_errors.VersionUnsupportedError || error instanceof import_errors.FeatureUnsupportedError || (0, import_abort.isAbortOrTimeoutError)(error)) {
4076
4181
  throw error;
4077
4182
  }
@@ -4339,6 +4444,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4339
4444
  */
4340
4445
  getActiveTasks() {
4341
4446
  return this.executor.getActiveTasks().filter((task) => task.agent.id === this.agent.id).map((task) => {
4447
+ (0, import_request_proposals_guard.assertNativeRequestProposalsTask)(task);
4342
4448
  if (!CANONICAL_CREATIVE_ACTIVITY_TASKS.has(task.taskName)) return task;
4343
4449
  const converter = this.resolveLegacyFormatConverter(
4344
4450
  this.canonicalCreativeTaskAssociation(task.taskId)?.legacyFormatConverter
@@ -4373,12 +4479,16 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4373
4479
  async listTasks() {
4374
4480
  const tasks = await this.executor.getTaskList(this.agent.id);
4375
4481
  return tasks.map(
4376
- (task) => CANONICAL_CREATIVE_ACTIVITY_TASKS.has(task.taskType) ? this.canonicalizeCreativeTaskInfo(
4377
- task,
4378
- task.taskType,
4379
- void 0,
4380
- this.resolveLegacyFormatConverter(this.canonicalCreativeTaskAssociation(task.taskId)?.legacyFormatConverter)
4381
- ) : task
4482
+ (task) => (0, import_request_proposals_guard.assertNativeRequestProposalsTask)(
4483
+ CANONICAL_CREATIVE_ACTIVITY_TASKS.has(task.taskType) ? this.canonicalizeCreativeTaskInfo(
4484
+ task,
4485
+ task.taskType,
4486
+ void 0,
4487
+ this.resolveLegacyFormatConverter(
4488
+ this.canonicalCreativeTaskAssociation(task.taskId)?.legacyFormatConverter
4489
+ )
4490
+ ) : task
4491
+ )
4382
4492
  );
4383
4493
  }
4384
4494
  /**
@@ -4389,12 +4499,15 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4389
4499
  */
4390
4500
  async getTaskInfo(taskId) {
4391
4501
  const task = await this.executor.getTaskInfo(taskId);
4392
- return task && CANONICAL_CREATIVE_ACTIVITY_TASKS.has(task.taskType) ? this.canonicalizeCreativeTaskInfo(
4393
- task,
4394
- task.taskType,
4395
- void 0,
4396
- this.resolveLegacyFormatConverter(this.canonicalCreativeTaskAssociation(task.taskId)?.legacyFormatConverter)
4397
- ) : task;
4502
+ if (!task) return null;
4503
+ return (0, import_request_proposals_guard.assertNativeRequestProposalsTask)(
4504
+ CANONICAL_CREATIVE_ACTIVITY_TASKS.has(task.taskType) ? this.canonicalizeCreativeTaskInfo(
4505
+ task,
4506
+ task.taskType,
4507
+ void 0,
4508
+ this.resolveLegacyFormatConverter(this.canonicalCreativeTaskAssociation(task.taskId)?.legacyFormatConverter)
4509
+ ) : task
4510
+ );
4398
4511
  }
4399
4512
  /**
4400
4513
  * Subscribe to task notifications for this agent
@@ -4419,14 +4532,16 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4419
4532
  return this.executor.onTaskUpdate(
4420
4533
  this.agent.id,
4421
4534
  (task) => callback(
4422
- CANONICAL_CREATIVE_ACTIVITY_TASKS.has(task.taskType) ? this.canonicalizeCreativeTaskInfo(
4423
- task,
4424
- task.taskType,
4425
- void 0,
4426
- this.resolveLegacyFormatConverter(
4427
- this.canonicalCreativeTaskAssociation(task.taskId)?.legacyFormatConverter
4428
- )
4429
- ) : task
4535
+ (0, import_request_proposals_guard.assertNativeRequestProposalsTask)(
4536
+ CANONICAL_CREATIVE_ACTIVITY_TASKS.has(task.taskType) ? this.canonicalizeCreativeTaskInfo(
4537
+ task,
4538
+ task.taskType,
4539
+ void 0,
4540
+ this.resolveLegacyFormatConverter(
4541
+ this.canonicalCreativeTaskAssociation(task.taskId)?.legacyFormatConverter
4542
+ )
4543
+ ) : task
4544
+ )
4430
4545
  )
4431
4546
  );
4432
4547
  }
@@ -4437,12 +4552,16 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4437
4552
  * @returns Unsubscribe function
4438
4553
  */
4439
4554
  onTaskEvents(callbacks) {
4440
- const safe = (task) => CANONICAL_CREATIVE_ACTIVITY_TASKS.has(task.taskType) ? this.canonicalizeCreativeTaskInfo(
4441
- task,
4442
- task.taskType,
4443
- void 0,
4444
- this.resolveLegacyFormatConverter(this.canonicalCreativeTaskAssociation(task.taskId)?.legacyFormatConverter)
4445
- ) : task;
4555
+ const safe = (task) => (0, import_request_proposals_guard.assertNativeRequestProposalsTask)(
4556
+ CANONICAL_CREATIVE_ACTIVITY_TASKS.has(task.taskType) ? this.canonicalizeCreativeTaskInfo(
4557
+ task,
4558
+ task.taskType,
4559
+ void 0,
4560
+ this.resolveLegacyFormatConverter(
4561
+ this.canonicalCreativeTaskAssociation(task.taskId)?.legacyFormatConverter
4562
+ )
4563
+ ) : task
4564
+ );
4446
4565
  return this.executor.onTaskEvents(this.agent.id, {
4447
4566
  ...callbacks.onTaskCreated && { onTaskCreated: (task) => callbacks.onTaskCreated(safe(task)) },
4448
4567
  ...callbacks.onTaskUpdated && { onTaskUpdated: (task) => callbacks.onTaskUpdated(safe(task)) },