@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
@@ -135,6 +135,7 @@ const CONTROL_MEDIA_BUY_FIELDS = /* @__PURE__ */ new Set([
135
135
  "governance_context",
136
136
  "idempotency_key",
137
137
  "media_buy_id",
138
+ "name",
138
139
  "pacing",
139
140
  "packages",
140
141
  "paused",
@@ -449,18 +450,14 @@ function containsCredentialShapedKey(value, seen = /* @__PURE__ */ new WeakSet()
449
450
  function containsPresignedUrl(value, seen = /* @__PURE__ */ new WeakSet()) {
450
451
  if (typeof value === "string") {
451
452
  try {
452
- const url = new URL(value);
453
- const sensitiveQueryKeys = /* @__PURE__ */ new Set([
454
- "x-amz-credential",
455
- "x-amz-signature",
456
- "x-goog-credential",
457
- "x-goog-signature",
458
- "signature",
459
- "sig",
460
- "token",
461
- "access_token"
462
- ]);
463
- return [...url.searchParams.keys()].some((key) => sensitiveQueryKeys.has(key.toLowerCase()));
453
+ const url = new URL(value, "https://adcp-relative.invalid");
454
+ const signedUrlAliases = /* @__PURE__ */ new Set(["xamzcredential", "xamzsignature", "xgoogcredential", "xgoogsignature", "sig"]);
455
+ const isSensitiveUrlKey = (key) => {
456
+ const normalized = key.replace(/[-_]/g, "").toLowerCase();
457
+ return isCredentialShapedKey(key) || signedUrlAliases.has(normalized);
458
+ };
459
+ const fragmentParams = new URLSearchParams(url.hash.startsWith("#") ? url.hash.slice(1) : url.hash);
460
+ return url.username.length > 0 || url.password.length > 0 || [...url.searchParams.keys()].some(isSensitiveUrlKey) || [...fragmentParams.keys()].some(isSensitiveUrlKey);
464
461
  } catch {
465
462
  return false;
466
463
  }
@@ -538,7 +535,6 @@ function negotiatedVersion(capabilities, clientVersion) {
538
535
  }
539
536
  const MAX_PROPOSAL_TRAVERSAL_NODES = 4096;
540
537
  const LEGACY_PROPOSAL_RAW = /* @__PURE__ */ Symbol("legacyProposalRaw");
541
- const LEGACY_PURCHASE_CLIENT_SCOPES = /* @__PURE__ */ new WeakMap();
542
538
  const COMPACT_REQUEST_PROPOSALS_RESPONSE_FIELDS = /* @__PURE__ */ new Set([
543
539
  "adcp_version",
544
540
  "outcome",
@@ -1632,7 +1628,7 @@ class MediaBuyLifecycleCoordinator {
1632
1628
  return [
1633
1629
  "feed_version_not_atomic",
1634
1630
  "pricing_version_not_atomic",
1635
- ...sourceVersion === "2.5" ? ["mutation_idempotency_not_guaranteed"] : []
1631
+ ...sourceVersion === "2.5" || this.idempotencyReplayTtlMs === void 0 ? ["mutation_idempotency_not_guaranteed"] : []
1636
1632
  ];
1637
1633
  }
1638
1634
  legacyPurchaseBinding(accountScope) {
@@ -1640,18 +1636,11 @@ class MediaBuyLifecycleCoordinator {
1640
1636
  const contextId = this.agent.getContextId();
1641
1637
  if (this.resolvedLegacyPurchaseSellerSessionScope === void 0) {
1642
1638
  let sessionScope = this.configuredLegacyPurchaseSellerSessionScope ?? contextId;
1643
- if (sessionScope === void 0 && this.legacyPurchaseSourceVersion() === "2.5") {
1639
+ if (sessionScope === void 0) {
1644
1640
  throw new ConfigurationError(
1645
- "Products-only AdCP 2.5 purchase continuations require mediaBuy.legacyPurchaseSellerSessionScope when the seller provides no context ID."
1641
+ "Products-only legacy purchase continuations require legacyPurchaseSellerSessionScope in negotiateMediaBuyLifecycle() when the seller provides no context ID."
1646
1642
  );
1647
1643
  }
1648
- if (sessionScope === void 0) {
1649
- sessionScope = LEGACY_PURCHASE_CLIENT_SCOPES.get(this.agent);
1650
- if (sessionScope === void 0) {
1651
- sessionScope = randomBytes(32).toString("base64url");
1652
- LEGACY_PURCHASE_CLIENT_SCOPES.set(this.agent, sessionScope);
1653
- }
1654
- }
1655
1644
  this.resolvedLegacyPurchaseSellerSessionScope = sessionScope;
1656
1645
  }
1657
1646
  return {
@@ -3152,10 +3141,11 @@ class MediaBuyLifecycleCoordinator {
3152
3141
  }
3153
3142
  const packages = array(legacyRequest.packages);
3154
3143
  const packageProductIds = packages.map((pkg) => optionalString(record(pkg).product_id));
3155
- if (packages.length === 0 || packageProductIds.some((productId) => productId === void 0) || new Set(packageProductIds).size !== packageProductIds.length || !exactSet(packageProductIds, selectedProductIds)) {
3144
+ const distinctPackageProductIds = [...new Set(packageProductIds)];
3145
+ if (packages.length === 0 || packageProductIds.some((productId) => productId === void 0) || !exactSet(distinctPackageProductIds, selectedProductIds)) {
3156
3146
  throw new LegacyPurchaseContinuationError(
3157
3147
  "selection_mismatch",
3158
- "The distinct legacy_create_request package product IDs must exactly match selected_product_ids."
3148
+ "The legacy_create_request package product ID set must exactly match selected_product_ids."
3159
3149
  );
3160
3150
  }
3161
3151
  const observedPricingOptions = /* @__PURE__ */ new Map();
@@ -3198,7 +3188,7 @@ class MediaBuyLifecycleCoordinator {
3198
3188
  `legacy_create_request is invalid for AdCP ${continuation.sourceAdcpVersion}: ${formatIssues(validation.issues)}`
3199
3189
  );
3200
3190
  }
3201
- const claim = {
3191
+ let claim = {
3202
3192
  idempotencyKey,
3203
3193
  inputFingerprint: requestFingerprint(input),
3204
3194
  operationKey: requestFingerprint({
@@ -3227,7 +3217,8 @@ class MediaBuyLifecycleCoordinator {
3227
3217
  const finalRequest = record(effectiveParams);
3228
3218
  const finalPackages = array(finalRequest.packages);
3229
3219
  const finalProductIds = finalPackages.map((pkg) => optionalString(record(pkg).product_id));
3230
- if (finalPackages.length === 0 || finalProductIds.some((productId) => productId === void 0) || new Set(finalProductIds).size !== finalProductIds.length || !exactSet(finalProductIds, selectedProductIds)) {
3220
+ const distinctFinalProductIds = [...new Set(finalProductIds)];
3221
+ if (finalPackages.length === 0 || finalProductIds.some((productId) => productId === void 0) || !exactSet(distinctFinalProductIds, selectedProductIds)) {
3231
3222
  throw new LegacyPurchaseContinuationError(
3232
3223
  "selection_mismatch",
3233
3224
  "The final seller payload product IDs must exactly match the continuation selection."
@@ -3257,6 +3248,11 @@ class MediaBuyLifecycleCoordinator {
3257
3248
  "The final seller payload mutation key must match the continuation request."
3258
3249
  );
3259
3250
  }
3251
+ claim = {
3252
+ ...claim,
3253
+ claimedAt: (/* @__PURE__ */ new Date()).toISOString(),
3254
+ replayExpiresAt: new Date(Date.now() + this.legacyPurchaseOperationTtlMs).toISOString()
3255
+ };
3260
3256
  let claimed;
3261
3257
  try {
3262
3258
  claimed = await this.legacyPurchaseContinuationStore.claim(token, {
@@ -3328,8 +3324,37 @@ class MediaBuyLifecycleCoordinator {
3328
3324
  );
3329
3325
  }
3330
3326
  claimedForDispatch = true;
3331
- return { action: "dispatch_committed" };
3327
+ return {
3328
+ action: "dispatch_committed",
3329
+ onResult: async (result) => {
3330
+ const settled = await this.trackLegacyPurchaseResult(
3331
+ token,
3332
+ claim,
3333
+ result,
3334
+ context.publishSettledTaskStatus,
3335
+ context.registerExternalTaskSettlement,
3336
+ options?.transport
3337
+ );
3338
+ settledInsideExecutor = true;
3339
+ return settled;
3340
+ },
3341
+ onError: async (error) => {
3342
+ settledInsideExecutor = true;
3343
+ if (error instanceof LegacyPurchaseContinuationError) {
3344
+ if (error.code === "ambiguous") {
3345
+ await this.markLegacyPurchaseAmbiguous(token, claim, "create_media_buy_result_invalid");
3346
+ }
3347
+ throw error;
3348
+ }
3349
+ await this.markLegacyPurchaseAmbiguous(token, claim, "create_media_buy_transport_uncertain");
3350
+ throw this.legacyPurchaseAmbiguousError(
3351
+ "The legacy create transport failed after the token was claimed.",
3352
+ error
3353
+ );
3354
+ }
3355
+ };
3332
3356
  };
3357
+ let settledInsideExecutor = false;
3333
3358
  try {
3334
3359
  const result = await this.agent.createMediaBuyLegacyWithPreDispatch(
3335
3360
  legacyRequest,
@@ -3342,9 +3367,11 @@ class MediaBuyLifecycleCoordinator {
3342
3367
  }
3343
3368
  );
3344
3369
  if (!claimedForDispatch) return result;
3345
- return this.trackLegacyPurchaseResult(token, claim, result);
3370
+ if (settledInsideExecutor) return result;
3371
+ return this.trackLegacyPurchaseResult(token, claim, result, void 0, void 0, options?.transport);
3346
3372
  } catch (error) {
3347
3373
  if (!claimedForDispatch) throw error;
3374
+ if (settledInsideExecutor) throw error;
3348
3375
  if (error instanceof LegacyPurchaseContinuationError) {
3349
3376
  if (error.code === "ambiguous") {
3350
3377
  await this.markLegacyPurchaseAmbiguous(token, claim, "create_media_buy_result_invalid");
@@ -3438,21 +3465,96 @@ class MediaBuyLifecycleCoordinator {
3438
3465
  } catch {
3439
3466
  }
3440
3467
  }
3441
- async trackLegacyPurchaseResult(token, claim, result) {
3468
+ async trackLegacyPurchaseResult(token, claim, result, publishSettledTaskStatus, registerExternalTaskSettlement, settlementTransport) {
3442
3469
  if (result.status === "completed" || result.status === "failed" || result.status === "governance-denied") {
3443
- if (result.status === "failed" && result.adcpError === void 0) {
3470
+ if (result.status === "failed" && (result.adcpError === void 0 || result.adcpError.synthetic === true)) {
3444
3471
  await this.markLegacyPurchaseAmbiguous(token, claim, "create_media_buy_failure_not_authoritative");
3445
3472
  throw this.legacyPurchaseAmbiguousError(
3446
3473
  "The legacy create failure was not an authoritative structured AdCP error."
3447
3474
  );
3448
3475
  }
3449
- const terminal = this.assertLegacyPurchaseTerminalResult(result, this.legacyPurchaseSourceVersion());
3476
+ let terminal;
3477
+ try {
3478
+ terminal = this.assertLegacyPurchaseTerminalResult(result, this.legacyPurchaseSourceVersion());
3479
+ } catch (error) {
3480
+ if (error instanceof LegacyPurchaseContinuationError && error.code === "ambiguous") {
3481
+ await this.markLegacyPurchaseAmbiguous(token, claim, "create_media_buy_result_invalid");
3482
+ }
3483
+ throw error;
3484
+ }
3450
3485
  return attachMatch(await this.completeLegacyPurchase(token, claim, terminal));
3451
3486
  }
3452
- if (result.submitted) {
3453
- const submitted = result.submitted;
3487
+ const settlementMetadata = {
3488
+ taskId: result.metadata.taskId,
3489
+ taskName: result.metadata.taskName,
3490
+ agent: { ...result.metadata.agent },
3491
+ responseTimeMs: result.metadata.responseTimeMs,
3492
+ timestamp: result.metadata.timestamp,
3493
+ clarificationRounds: result.metadata.clarificationRounds,
3494
+ status: result.metadata.status,
3495
+ ...result.metadata.contextId !== void 0 && { contextId: result.metadata.contextId },
3496
+ ...(result.submitted?.taskId ?? result.metadata.serverTaskId ?? claim.sellerTaskId) !== void 0 && {
3497
+ serverTaskId: result.submitted?.taskId ?? result.metadata.serverTaskId ?? claim.sellerTaskId
3498
+ },
3499
+ ...result.metadata.idempotency_key !== void 0 && { idempotency_key: result.metadata.idempotency_key },
3500
+ ...result.metadata.replayed !== void 0 && { replayed: result.metadata.replayed },
3501
+ ...result.metadata.adcpVersion !== void 0 && { adcpVersion: result.metadata.adcpVersion }
3502
+ };
3503
+ const failureFieldsFromTask = (task) => {
3504
+ const extracted = extractAdcpErrorInfo(task.result);
3505
+ const adcpError = extracted?.synthetic === true ? void 0 : extracted;
3506
+ const correlationId = extractCorrelationId(task.result);
3507
+ return {
3508
+ // tasks/get and webhook free text are not durable-safe boundaries.
3509
+ // Preserve a structured AdCP message when present; otherwise replay a
3510
+ // generic error while retaining the seller payload for inspection.
3511
+ error: adcpError?.message ?? "Legacy create failed.",
3512
+ ...adcpError !== void 0 && { adcpError },
3513
+ ...correlationId !== void 0 && { correlationId }
3514
+ };
3515
+ };
3516
+ const resultFromTask = (task) => {
3517
+ const metadata = {
3518
+ ...settlementMetadata,
3519
+ taskName: task.taskType,
3520
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3521
+ };
3522
+ if (task.status === "completed" && task.result !== void 0 && isAdcpOperationSuccess(task.result, task.taskType)) {
3523
+ return attachMatch({
3524
+ success: true,
3525
+ status: "completed",
3526
+ data: task.result,
3527
+ metadata: { ...metadata, status: "completed" }
3528
+ });
3529
+ }
3530
+ if (task.status === "unknown") {
3531
+ throw this.legacyPurchaseAmbiguousError(
3532
+ "tasks/get returned an unrecognizable response for the submitted legacy purchase."
3533
+ );
3534
+ }
3535
+ if (["completed", "failed", "rejected", "canceled", "governance-denied"].includes(task.status)) {
3536
+ const status = task.status === "governance-denied" ? "governance-denied" : "failed";
3537
+ return attachMatch({
3538
+ success: false,
3539
+ status,
3540
+ ...failureFieldsFromTask(task),
3541
+ ...task.result !== void 0 && { data: task.result },
3542
+ metadata: { ...metadata, status }
3543
+ });
3544
+ }
3545
+ if (task.status === "input-required" || task.status === "auth-required") {
3546
+ return attachMatch({
3547
+ success: true,
3548
+ status: task.status,
3549
+ ...task.result !== void 0 && { data: task.result },
3550
+ metadata: { ...metadata, status: task.status }
3551
+ });
3552
+ }
3553
+ return void 0;
3554
+ };
3555
+ const bindSellerTask = async (sellerTaskId) => {
3454
3556
  try {
3455
- const recorded = await this.legacyPurchaseContinuationStore.recordSubmittedTask(token, claim, submitted.taskId);
3557
+ const recorded = await this.legacyPurchaseContinuationStore.recordSubmittedTask(token, claim, sellerTaskId);
3456
3558
  if (!recorded) {
3457
3559
  await this.markLegacyPurchaseAmbiguous(token, claim, "submitted_task_persistence_failed");
3458
3560
  throw this.legacyPurchaseAmbiguousError("Could not durably bind the submitted seller task.");
@@ -3462,32 +3564,128 @@ class MediaBuyLifecycleCoordinator {
3462
3564
  await this.markLegacyPurchaseAmbiguous(token, claim, "submitted_task_persistence_failed");
3463
3565
  throw this.legacyPurchaseAmbiguousError("Could not durably bind the submitted seller task.", error);
3464
3566
  }
3465
- const failureFieldsFromTask = (task) => {
3466
- const adcpError = extractAdcpErrorInfo(task.result);
3467
- const correlationId = extractCorrelationId(task.result);
3468
- return {
3469
- // tasks/get free text is not a durable-safe boundary. Preserve a
3470
- // structured AdCP message when present; otherwise replay a generic
3471
- // error while retaining the seller payload in data for inspection.
3472
- error: adcpError?.message ?? "Legacy create failed.",
3473
- ...adcpError !== void 0 && { adcpError },
3474
- ...correlationId !== void 0 && { correlationId }
3475
- };
3476
- };
3567
+ registerExternalTaskSettlement?.(async (observation) => {
3568
+ try {
3569
+ if (observation.serverTaskId !== sellerTaskId) {
3570
+ throw this.legacyPurchaseAmbiguousError(
3571
+ "The pushed seller task ID does not match the durably recorded submitted task."
3572
+ );
3573
+ }
3574
+ if (observation.taskType !== "create_media_buy") {
3575
+ throw this.legacyPurchaseAmbiguousError("The pushed seller task has the wrong task identity.");
3576
+ }
3577
+ const task = {
3578
+ taskId: sellerTaskId,
3579
+ taskType: observation.taskType,
3580
+ status: observation.status,
3581
+ createdAt: Date.now(),
3582
+ updatedAt: Date.now(),
3583
+ ...observation.result !== void 0 && { result: observation.result }
3584
+ };
3585
+ const observed = resultFromTask(task);
3586
+ if (!observed || !["completed", "failed", "governance-denied"].includes(observed.status)) {
3587
+ throw this.legacyPurchaseAmbiguousError(
3588
+ "The pushed legacy create status was not an authoritative terminal result."
3589
+ );
3590
+ }
3591
+ return await this.trackLegacyPurchaseResult(
3592
+ token,
3593
+ claim,
3594
+ observed,
3595
+ publishSettledTaskStatus,
3596
+ registerExternalTaskSettlement,
3597
+ settlementTransport
3598
+ );
3599
+ } catch (error) {
3600
+ if (error instanceof LegacyPurchaseContinuationError && error.code === "ambiguous") {
3601
+ await this.markLegacyPurchaseAmbiguous(token, claim, "pushed_task_result_invalid");
3602
+ }
3603
+ throw error;
3604
+ }
3605
+ });
3606
+ };
3607
+ if (result.status === "working" || result.status === "input-required" || result.status === "auth-required") {
3608
+ const sellerTaskId = result.metadata.serverTaskId;
3609
+ if (!sellerTaskId) {
3610
+ await this.markLegacyPurchaseAmbiguous(token, claim, "paused_task_identity_missing");
3611
+ throw this.legacyPurchaseAmbiguousError(
3612
+ "The non-terminal legacy create response did not provide a durable seller task identity."
3613
+ );
3614
+ }
3615
+ await bindSellerTask(sellerTaskId);
3616
+ if (result.status !== "working") return attachMatch(result);
3617
+ const watchSignal = AbortSignal.timeout(Math.min(this.legacyPurchaseOperationTtlMs, 5 * 60 * 1e3));
3618
+ const waitForWorkingPoll = () => new Promise((resolve, reject) => {
3619
+ if (watchSignal.aborted) {
3620
+ reject(createAbortError(watchSignal.reason));
3621
+ return;
3622
+ }
3623
+ const timer = setTimeout(finish, 6e4);
3624
+ const abort = () => finish(createAbortError(watchSignal.reason));
3625
+ watchSignal.addEventListener("abort", abort, { once: true });
3626
+ function finish(error) {
3627
+ clearTimeout(timer);
3628
+ watchSignal.removeEventListener("abort", abort);
3629
+ if (error) reject(error);
3630
+ else resolve();
3631
+ }
3632
+ });
3633
+ void new Promise((resolve) => setTimeout(resolve, 0)).then(async () => {
3634
+ while (!watchSignal.aborted) {
3635
+ const task = await this.agent.getTaskStatus(sellerTaskId, settlementTransport, watchSignal);
3636
+ if (task.taskId !== sellerTaskId || task.taskType !== "create_media_buy") {
3637
+ throw this.legacyPurchaseAmbiguousError(
3638
+ "The polled working seller task does not match the durably recorded create_media_buy task."
3639
+ );
3640
+ }
3641
+ const observed = resultFromTask(task);
3642
+ if (observed) {
3643
+ return this.trackLegacyPurchaseResult(
3644
+ token,
3645
+ claim,
3646
+ observed,
3647
+ publishSettledTaskStatus,
3648
+ registerExternalTaskSettlement,
3649
+ settlementTransport
3650
+ );
3651
+ }
3652
+ await waitForWorkingPoll();
3653
+ }
3654
+ throw createAbortError(watchSignal.reason);
3655
+ }).then((completion) => {
3656
+ publishSettledTaskStatus?.(completion.status, completion.data, completion.error);
3657
+ }).catch(async (error) => {
3658
+ if (watchSignal.aborted || isAbortOrTimeoutError(error)) return;
3659
+ await this.markLegacyPurchaseAmbiguous(token, claim, "non_terminal_completion_watch_uncertain");
3660
+ void error;
3661
+ });
3662
+ return attachMatch(result);
3663
+ }
3664
+ if (result.submitted) {
3665
+ const submitted = result.submitted;
3666
+ await bindSellerTask(submitted.taskId);
3477
3667
  const observeTask = async (transport, observationSignal) => {
3478
3668
  try {
3479
3669
  const task = observationSignal ? await withAbortSignal([observationSignal], void 0, () => submitted.track(transport)) : await submitted.track(transport);
3670
+ if (task.taskId !== submitted.taskId) {
3671
+ throw this.legacyPurchaseAmbiguousError(
3672
+ "The tracked seller task ID does not match the durably recorded submitted task."
3673
+ );
3674
+ }
3675
+ if (task.taskType !== "create_media_buy") {
3676
+ throw this.legacyPurchaseAmbiguousError("The tracked seller task has the wrong task identity.");
3677
+ }
3480
3678
  if (["completed", "failed", "rejected", "canceled", "governance-denied"].includes(task.status)) {
3481
- if (task.taskType !== "create_media_buy") {
3482
- throw this.legacyPurchaseAmbiguousError("The tracked seller task has the wrong task identity.");
3483
- }
3484
3679
  if (task.status === "completed" && task.result === void 0) {
3485
3680
  throw this.legacyPurchaseAmbiguousError(
3486
3681
  "The tracked seller task reported completion without a create_media_buy result."
3487
3682
  );
3488
3683
  }
3489
3684
  const completedSuccessfully = task.status === "completed" && isAdcpOperationSuccess(task.result, task.taskType);
3490
- if (!completedSuccessfully && task.status !== "governance-denied" && extractAdcpErrorInfo(task.result) === void 0) {
3685
+ if (!completedSuccessfully && task.status !== "governance-denied" && (() => {
3686
+ const adcpError = extractAdcpErrorInfo(task.result);
3687
+ return adcpError === void 0 || adcpError.synthetic === true;
3688
+ })()) {
3491
3689
  throw this.legacyPurchaseAmbiguousError(
3492
3690
  "The tracked legacy create failure was not an authoritative structured AdCP error."
3493
3691
  );
@@ -3501,7 +3699,7 @@ class MediaBuyLifecycleCoordinator {
3501
3699
  ...task.result !== void 0 && { data: task.result }
3502
3700
  },
3503
3701
  metadata: {
3504
- ...result.metadata,
3702
+ ...settlementMetadata,
3505
3703
  status: completedSuccessfully ? "completed" : task.status === "governance-denied" ? "governance-denied" : "failed",
3506
3704
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
3507
3705
  }
@@ -3531,45 +3729,6 @@ class MediaBuyLifecycleCoordinator {
3531
3729
  throw this.legacyPurchaseAmbiguousError("Legacy create task tracking failed after claim.", error);
3532
3730
  }
3533
3731
  };
3534
- const resultFromTask = (task) => {
3535
- const metadata = {
3536
- ...result.metadata,
3537
- taskName: task.taskType,
3538
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
3539
- };
3540
- if (task.status === "completed" && task.result !== void 0 && isAdcpOperationSuccess(task.result, task.taskType)) {
3541
- return attachMatch({
3542
- success: true,
3543
- status: "completed",
3544
- data: task.result,
3545
- metadata: { ...metadata, status: "completed" }
3546
- });
3547
- }
3548
- if (task.status === "unknown") {
3549
- throw this.legacyPurchaseAmbiguousError(
3550
- "tasks/get returned an unrecognizable response for the submitted legacy purchase."
3551
- );
3552
- }
3553
- if (["completed", "failed", "rejected", "canceled", "governance-denied"].includes(task.status)) {
3554
- const status = task.status === "governance-denied" ? "governance-denied" : "failed";
3555
- return attachMatch({
3556
- success: false,
3557
- status,
3558
- ...failureFieldsFromTask(task),
3559
- ...task.result !== void 0 && { data: task.result },
3560
- metadata: { ...metadata, status }
3561
- });
3562
- }
3563
- if (task.status === "input-required" || task.status === "auth-required") {
3564
- return attachMatch({
3565
- success: true,
3566
- status: task.status,
3567
- ...task.result !== void 0 && { data: task.result },
3568
- metadata: { ...metadata, status: task.status }
3569
- });
3570
- }
3571
- return void 0;
3572
- };
3573
3732
  const waitForNextPoll = (state, signal) => new Promise((resolve, reject) => {
3574
3733
  if (signal.aborted) {
3575
3734
  reject(createAbortError(signal.reason));
@@ -3591,9 +3750,18 @@ class MediaBuyLifecycleCoordinator {
3591
3750
  const pollTrackedCompletion = async (state, signal) => {
3592
3751
  while (true) {
3593
3752
  if (signal.aborted) throw createAbortError(signal.reason);
3594
- const task = await observeTask(void 0, signal);
3753
+ const task = await observeTask(settlementTransport, signal);
3595
3754
  const observed = resultFromTask(task);
3596
- if (observed) return this.trackLegacyPurchaseResult(token, claim, observed);
3755
+ if (observed) {
3756
+ return this.trackLegacyPurchaseResult(
3757
+ token,
3758
+ claim,
3759
+ observed,
3760
+ publishSettledTaskStatus,
3761
+ registerExternalTaskSettlement,
3762
+ settlementTransport
3763
+ );
3764
+ }
3597
3765
  await waitForNextPoll(state, signal);
3598
3766
  }
3599
3767
  };
@@ -3669,7 +3837,9 @@ class MediaBuyLifecycleCoordinator {
3669
3837
  }
3670
3838
  };
3671
3839
  const watchSignal = AbortSignal.timeout(Math.min(this.legacyPurchaseOperationTtlMs, 5 * 60 * 1e3));
3672
- void new Promise((resolve) => setTimeout(resolve, 0)).then(() => waitForSharedCompletion(void 0, watchSignal)).catch(async (error) => {
3840
+ void new Promise((resolve) => setTimeout(resolve, 0)).then(() => waitForSharedCompletion(void 0, watchSignal)).then((completion) => {
3841
+ publishSettledTaskStatus?.(completion.status, completion.data, completion.error);
3842
+ }).catch(async (error) => {
3673
3843
  if (watchSignal.aborted || isAbortOrTimeoutError(error)) return;
3674
3844
  await this.markLegacyPurchaseAmbiguous(token, claim, "background_completion_watch_uncertain");
3675
3845
  void error;
@@ -3681,7 +3851,14 @@ class MediaBuyLifecycleCoordinator {
3681
3851
  ...deferred,
3682
3852
  resume: async (resumeInput) => {
3683
3853
  try {
3684
- return await this.trackLegacyPurchaseResult(token, claim, await deferred.resume(resumeInput));
3854
+ return await this.trackLegacyPurchaseResult(
3855
+ token,
3856
+ claim,
3857
+ await deferred.resume(resumeInput),
3858
+ publishSettledTaskStatus,
3859
+ registerExternalTaskSettlement,
3860
+ settlementTransport
3861
+ );
3685
3862
  } catch (error) {
3686
3863
  if (error instanceof LegacyPurchaseContinuationError) {
3687
3864
  if (error.code === "ambiguous") {
@@ -4639,6 +4816,25 @@ class MediaBuyLifecycleCoordinator {
4639
4816
  const input = record(params);
4640
4817
  const lifecycle = this.selectLifecycle("control_media_buy");
4641
4818
  this.assertLegacyReferenceShapes("controlMediaBuy", input);
4819
+ const canceledControlConflicts = [
4820
+ "name",
4821
+ "paused",
4822
+ "total_budget",
4823
+ "daily_budget_cap",
4824
+ "budget_cap_timezone",
4825
+ "budget_allocation",
4826
+ "pacing",
4827
+ "bidding",
4828
+ "packages",
4829
+ "reporting_webhook"
4830
+ ];
4831
+ if (Object.hasOwn(input, "canceled") && canceledControlConflicts.some((field) => Object.hasOwn(input, field))) {
4832
+ throw this.unsupported(
4833
+ "controlMediaBuy",
4834
+ "canceled",
4835
+ "Compact media-buy cancellation cannot be combined with other operational controls."
4836
+ );
4837
+ }
4642
4838
  if (lifecycle === "compact") {
4643
4839
  this.assertValidCompactRequest("control_media_buy", params, lifecycle, true);
4644
4840
  const result2 = await this.agent.controlMediaBuy(params, inputHandler, options);
@@ -4656,6 +4852,9 @@ class MediaBuyLifecycleCoordinator {
4656
4852
  "bidding",
4657
4853
  "governance_context"
4658
4854
  ]);
4855
+ if (!isCompactRelease(this.negotiated_version)) {
4856
+ this.assertCompactWireFieldsAbsent("controlMediaBuy", input, ["name"]);
4857
+ }
4659
4858
  if (compareRelease(this.negotiated_version, "3.0") < 0) {
4660
4859
  this.assertCompactWireFieldsAbsent("controlMediaBuy", input, ["reporting_webhook"]);
4661
4860
  }
@@ -4851,29 +5050,12 @@ class MediaBuyLifecycleCoordinator {
4851
5050
  "Compact media-buy cancellation_reason requires canceled=true."
4852
5051
  );
4853
5052
  }
4854
- const canceledControlConflicts = [
4855
- "paused",
4856
- "total_budget",
4857
- "daily_budget_cap",
4858
- "budget_cap_timezone",
4859
- "budget_allocation",
4860
- "pacing",
4861
- "bidding",
4862
- "packages",
4863
- "reporting_webhook"
4864
- ];
4865
- if (Object.hasOwn(input, "canceled") && canceledControlConflicts.some((field) => Object.hasOwn(input, field))) {
4866
- throw this.unsupported(
4867
- "controlMediaBuy",
4868
- "canceled",
4869
- "Compact media-buy cancellation cannot be combined with other operational controls."
4870
- );
4871
- }
4872
5053
  const request = {
4873
5054
  account: input.account,
4874
5055
  media_buy_id: input.media_buy_id,
4875
5056
  ...input.idempotency_key !== void 0 && { idempotency_key: input.idempotency_key },
4876
5057
  revision: input.revision,
5058
+ ...input.name !== void 0 && { name: input.name },
4877
5059
  ...input.paused !== void 0 && { paused: input.paused },
4878
5060
  ...input.canceled !== void 0 && { canceled: input.canceled },
4879
5061
  ...input.cancellation_reason !== void 0 && { cancellation_reason: input.cancellation_reason },