@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
@@ -154,6 +154,7 @@ const CONTROL_MEDIA_BUY_FIELDS = /* @__PURE__ */ new Set([
154
154
  "governance_context",
155
155
  "idempotency_key",
156
156
  "media_buy_id",
157
+ "name",
157
158
  "pacing",
158
159
  "packages",
159
160
  "paused",
@@ -468,18 +469,14 @@ function containsCredentialShapedKey(value, seen = /* @__PURE__ */ new WeakSet()
468
469
  function containsPresignedUrl(value, seen = /* @__PURE__ */ new WeakSet()) {
469
470
  if (typeof value === "string") {
470
471
  try {
471
- const url = new URL(value);
472
- const sensitiveQueryKeys = /* @__PURE__ */ new Set([
473
- "x-amz-credential",
474
- "x-amz-signature",
475
- "x-goog-credential",
476
- "x-goog-signature",
477
- "signature",
478
- "sig",
479
- "token",
480
- "access_token"
481
- ]);
482
- return [...url.searchParams.keys()].some((key) => sensitiveQueryKeys.has(key.toLowerCase()));
472
+ const url = new URL(value, "https://adcp-relative.invalid");
473
+ const signedUrlAliases = /* @__PURE__ */ new Set(["xamzcredential", "xamzsignature", "xgoogcredential", "xgoogsignature", "sig"]);
474
+ const isSensitiveUrlKey = (key) => {
475
+ const normalized = key.replace(/[-_]/g, "").toLowerCase();
476
+ return isCredentialShapedKey(key) || signedUrlAliases.has(normalized);
477
+ };
478
+ const fragmentParams = new URLSearchParams(url.hash.startsWith("#") ? url.hash.slice(1) : url.hash);
479
+ return url.username.length > 0 || url.password.length > 0 || [...url.searchParams.keys()].some(isSensitiveUrlKey) || [...fragmentParams.keys()].some(isSensitiveUrlKey);
483
480
  } catch {
484
481
  return false;
485
482
  }
@@ -557,7 +554,6 @@ function negotiatedVersion(capabilities, clientVersion) {
557
554
  }
558
555
  const MAX_PROPOSAL_TRAVERSAL_NODES = 4096;
559
556
  const LEGACY_PROPOSAL_RAW = /* @__PURE__ */ Symbol("legacyProposalRaw");
560
- const LEGACY_PURCHASE_CLIENT_SCOPES = /* @__PURE__ */ new WeakMap();
561
557
  const COMPACT_REQUEST_PROPOSALS_RESPONSE_FIELDS = /* @__PURE__ */ new Set([
562
558
  "adcp_version",
563
559
  "outcome",
@@ -1651,7 +1647,7 @@ class MediaBuyLifecycleCoordinator {
1651
1647
  return [
1652
1648
  "feed_version_not_atomic",
1653
1649
  "pricing_version_not_atomic",
1654
- ...sourceVersion === "2.5" ? ["mutation_idempotency_not_guaranteed"] : []
1650
+ ...sourceVersion === "2.5" || this.idempotencyReplayTtlMs === void 0 ? ["mutation_idempotency_not_guaranteed"] : []
1655
1651
  ];
1656
1652
  }
1657
1653
  legacyPurchaseBinding(accountScope) {
@@ -1659,18 +1655,11 @@ class MediaBuyLifecycleCoordinator {
1659
1655
  const contextId = this.agent.getContextId();
1660
1656
  if (this.resolvedLegacyPurchaseSellerSessionScope === void 0) {
1661
1657
  let sessionScope = this.configuredLegacyPurchaseSellerSessionScope ?? contextId;
1662
- if (sessionScope === void 0 && this.legacyPurchaseSourceVersion() === "2.5") {
1658
+ if (sessionScope === void 0) {
1663
1659
  throw new import_errors.ConfigurationError(
1664
- "Products-only AdCP 2.5 purchase continuations require mediaBuy.legacyPurchaseSellerSessionScope when the seller provides no context ID."
1660
+ "Products-only legacy purchase continuations require legacyPurchaseSellerSessionScope in negotiateMediaBuyLifecycle() when the seller provides no context ID."
1665
1661
  );
1666
1662
  }
1667
- if (sessionScope === void 0) {
1668
- sessionScope = LEGACY_PURCHASE_CLIENT_SCOPES.get(this.agent);
1669
- if (sessionScope === void 0) {
1670
- sessionScope = (0, import_node_crypto.randomBytes)(32).toString("base64url");
1671
- LEGACY_PURCHASE_CLIENT_SCOPES.set(this.agent, sessionScope);
1672
- }
1673
- }
1674
1663
  this.resolvedLegacyPurchaseSellerSessionScope = sessionScope;
1675
1664
  }
1676
1665
  return {
@@ -3171,10 +3160,11 @@ class MediaBuyLifecycleCoordinator {
3171
3160
  }
3172
3161
  const packages = array(legacyRequest.packages);
3173
3162
  const packageProductIds = packages.map((pkg) => optionalString(record(pkg).product_id));
3174
- if (packages.length === 0 || packageProductIds.some((productId) => productId === void 0) || new Set(packageProductIds).size !== packageProductIds.length || !exactSet(packageProductIds, selectedProductIds)) {
3163
+ const distinctPackageProductIds = [...new Set(packageProductIds)];
3164
+ if (packages.length === 0 || packageProductIds.some((productId) => productId === void 0) || !exactSet(distinctPackageProductIds, selectedProductIds)) {
3175
3165
  throw new LegacyPurchaseContinuationError(
3176
3166
  "selection_mismatch",
3177
- "The distinct legacy_create_request package product IDs must exactly match selected_product_ids."
3167
+ "The legacy_create_request package product ID set must exactly match selected_product_ids."
3178
3168
  );
3179
3169
  }
3180
3170
  const observedPricingOptions = /* @__PURE__ */ new Map();
@@ -3217,7 +3207,7 @@ class MediaBuyLifecycleCoordinator {
3217
3207
  `legacy_create_request is invalid for AdCP ${continuation.sourceAdcpVersion}: ${(0, import_schema_validator.formatIssues)(validation.issues)}`
3218
3208
  );
3219
3209
  }
3220
- const claim = {
3210
+ let claim = {
3221
3211
  idempotencyKey,
3222
3212
  inputFingerprint: requestFingerprint(input),
3223
3213
  operationKey: requestFingerprint({
@@ -3246,7 +3236,8 @@ class MediaBuyLifecycleCoordinator {
3246
3236
  const finalRequest = record(effectiveParams);
3247
3237
  const finalPackages = array(finalRequest.packages);
3248
3238
  const finalProductIds = finalPackages.map((pkg) => optionalString(record(pkg).product_id));
3249
- if (finalPackages.length === 0 || finalProductIds.some((productId) => productId === void 0) || new Set(finalProductIds).size !== finalProductIds.length || !exactSet(finalProductIds, selectedProductIds)) {
3239
+ const distinctFinalProductIds = [...new Set(finalProductIds)];
3240
+ if (finalPackages.length === 0 || finalProductIds.some((productId) => productId === void 0) || !exactSet(distinctFinalProductIds, selectedProductIds)) {
3250
3241
  throw new LegacyPurchaseContinuationError(
3251
3242
  "selection_mismatch",
3252
3243
  "The final seller payload product IDs must exactly match the continuation selection."
@@ -3276,6 +3267,11 @@ class MediaBuyLifecycleCoordinator {
3276
3267
  "The final seller payload mutation key must match the continuation request."
3277
3268
  );
3278
3269
  }
3270
+ claim = {
3271
+ ...claim,
3272
+ claimedAt: (/* @__PURE__ */ new Date()).toISOString(),
3273
+ replayExpiresAt: new Date(Date.now() + this.legacyPurchaseOperationTtlMs).toISOString()
3274
+ };
3279
3275
  let claimed;
3280
3276
  try {
3281
3277
  claimed = await this.legacyPurchaseContinuationStore.claim(token, {
@@ -3347,8 +3343,37 @@ class MediaBuyLifecycleCoordinator {
3347
3343
  );
3348
3344
  }
3349
3345
  claimedForDispatch = true;
3350
- return { action: "dispatch_committed" };
3346
+ return {
3347
+ action: "dispatch_committed",
3348
+ onResult: async (result) => {
3349
+ const settled = await this.trackLegacyPurchaseResult(
3350
+ token,
3351
+ claim,
3352
+ result,
3353
+ context.publishSettledTaskStatus,
3354
+ context.registerExternalTaskSettlement,
3355
+ options?.transport
3356
+ );
3357
+ settledInsideExecutor = true;
3358
+ return settled;
3359
+ },
3360
+ onError: async (error) => {
3361
+ settledInsideExecutor = true;
3362
+ if (error instanceof LegacyPurchaseContinuationError) {
3363
+ if (error.code === "ambiguous") {
3364
+ await this.markLegacyPurchaseAmbiguous(token, claim, "create_media_buy_result_invalid");
3365
+ }
3366
+ throw error;
3367
+ }
3368
+ await this.markLegacyPurchaseAmbiguous(token, claim, "create_media_buy_transport_uncertain");
3369
+ throw this.legacyPurchaseAmbiguousError(
3370
+ "The legacy create transport failed after the token was claimed.",
3371
+ error
3372
+ );
3373
+ }
3374
+ };
3351
3375
  };
3376
+ let settledInsideExecutor = false;
3352
3377
  try {
3353
3378
  const result = await this.agent.createMediaBuyLegacyWithPreDispatch(
3354
3379
  legacyRequest,
@@ -3361,9 +3386,11 @@ class MediaBuyLifecycleCoordinator {
3361
3386
  }
3362
3387
  );
3363
3388
  if (!claimedForDispatch) return result;
3364
- return this.trackLegacyPurchaseResult(token, claim, result);
3389
+ if (settledInsideExecutor) return result;
3390
+ return this.trackLegacyPurchaseResult(token, claim, result, void 0, void 0, options?.transport);
3365
3391
  } catch (error) {
3366
3392
  if (!claimedForDispatch) throw error;
3393
+ if (settledInsideExecutor) throw error;
3367
3394
  if (error instanceof LegacyPurchaseContinuationError) {
3368
3395
  if (error.code === "ambiguous") {
3369
3396
  await this.markLegacyPurchaseAmbiguous(token, claim, "create_media_buy_result_invalid");
@@ -3457,21 +3484,96 @@ class MediaBuyLifecycleCoordinator {
3457
3484
  } catch {
3458
3485
  }
3459
3486
  }
3460
- async trackLegacyPurchaseResult(token, claim, result) {
3487
+ async trackLegacyPurchaseResult(token, claim, result, publishSettledTaskStatus, registerExternalTaskSettlement, settlementTransport) {
3461
3488
  if (result.status === "completed" || result.status === "failed" || result.status === "governance-denied") {
3462
- if (result.status === "failed" && result.adcpError === void 0) {
3489
+ if (result.status === "failed" && (result.adcpError === void 0 || result.adcpError.synthetic === true)) {
3463
3490
  await this.markLegacyPurchaseAmbiguous(token, claim, "create_media_buy_failure_not_authoritative");
3464
3491
  throw this.legacyPurchaseAmbiguousError(
3465
3492
  "The legacy create failure was not an authoritative structured AdCP error."
3466
3493
  );
3467
3494
  }
3468
- const terminal = this.assertLegacyPurchaseTerminalResult(result, this.legacyPurchaseSourceVersion());
3495
+ let terminal;
3496
+ try {
3497
+ terminal = this.assertLegacyPurchaseTerminalResult(result, this.legacyPurchaseSourceVersion());
3498
+ } catch (error) {
3499
+ if (error instanceof LegacyPurchaseContinuationError && error.code === "ambiguous") {
3500
+ await this.markLegacyPurchaseAmbiguous(token, claim, "create_media_buy_result_invalid");
3501
+ }
3502
+ throw error;
3503
+ }
3469
3504
  return (0, import_match.attachMatch)(await this.completeLegacyPurchase(token, claim, terminal));
3470
3505
  }
3471
- if (result.submitted) {
3472
- const submitted = result.submitted;
3506
+ const settlementMetadata = {
3507
+ taskId: result.metadata.taskId,
3508
+ taskName: result.metadata.taskName,
3509
+ agent: { ...result.metadata.agent },
3510
+ responseTimeMs: result.metadata.responseTimeMs,
3511
+ timestamp: result.metadata.timestamp,
3512
+ clarificationRounds: result.metadata.clarificationRounds,
3513
+ status: result.metadata.status,
3514
+ ...result.metadata.contextId !== void 0 && { contextId: result.metadata.contextId },
3515
+ ...(result.submitted?.taskId ?? result.metadata.serverTaskId ?? claim.sellerTaskId) !== void 0 && {
3516
+ serverTaskId: result.submitted?.taskId ?? result.metadata.serverTaskId ?? claim.sellerTaskId
3517
+ },
3518
+ ...result.metadata.idempotency_key !== void 0 && { idempotency_key: result.metadata.idempotency_key },
3519
+ ...result.metadata.replayed !== void 0 && { replayed: result.metadata.replayed },
3520
+ ...result.metadata.adcpVersion !== void 0 && { adcpVersion: result.metadata.adcpVersion }
3521
+ };
3522
+ const failureFieldsFromTask = (task) => {
3523
+ const extracted = (0, import_error_extraction.extractAdcpErrorInfo)(task.result);
3524
+ const adcpError = extracted?.synthetic === true ? void 0 : extracted;
3525
+ const correlationId = (0, import_error_extraction.extractCorrelationId)(task.result);
3526
+ return {
3527
+ // tasks/get and webhook free text are not durable-safe boundaries.
3528
+ // Preserve a structured AdCP message when present; otherwise replay a
3529
+ // generic error while retaining the seller payload for inspection.
3530
+ error: adcpError?.message ?? "Legacy create failed.",
3531
+ ...adcpError !== void 0 && { adcpError },
3532
+ ...correlationId !== void 0 && { correlationId }
3533
+ };
3534
+ };
3535
+ const resultFromTask = (task) => {
3536
+ const metadata = {
3537
+ ...settlementMetadata,
3538
+ taskName: task.taskType,
3539
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3540
+ };
3541
+ if (task.status === "completed" && task.result !== void 0 && (0, import_response_unwrapper.isAdcpOperationSuccess)(task.result, task.taskType)) {
3542
+ return (0, import_match.attachMatch)({
3543
+ success: true,
3544
+ status: "completed",
3545
+ data: task.result,
3546
+ metadata: { ...metadata, status: "completed" }
3547
+ });
3548
+ }
3549
+ if (task.status === "unknown") {
3550
+ throw this.legacyPurchaseAmbiguousError(
3551
+ "tasks/get returned an unrecognizable response for the submitted legacy purchase."
3552
+ );
3553
+ }
3554
+ if (["completed", "failed", "rejected", "canceled", "governance-denied"].includes(task.status)) {
3555
+ const status = task.status === "governance-denied" ? "governance-denied" : "failed";
3556
+ return (0, import_match.attachMatch)({
3557
+ success: false,
3558
+ status,
3559
+ ...failureFieldsFromTask(task),
3560
+ ...task.result !== void 0 && { data: task.result },
3561
+ metadata: { ...metadata, status }
3562
+ });
3563
+ }
3564
+ if (task.status === "input-required" || task.status === "auth-required") {
3565
+ return (0, import_match.attachMatch)({
3566
+ success: true,
3567
+ status: task.status,
3568
+ ...task.result !== void 0 && { data: task.result },
3569
+ metadata: { ...metadata, status: task.status }
3570
+ });
3571
+ }
3572
+ return void 0;
3573
+ };
3574
+ const bindSellerTask = async (sellerTaskId) => {
3473
3575
  try {
3474
- const recorded = await this.legacyPurchaseContinuationStore.recordSubmittedTask(token, claim, submitted.taskId);
3576
+ const recorded = await this.legacyPurchaseContinuationStore.recordSubmittedTask(token, claim, sellerTaskId);
3475
3577
  if (!recorded) {
3476
3578
  await this.markLegacyPurchaseAmbiguous(token, claim, "submitted_task_persistence_failed");
3477
3579
  throw this.legacyPurchaseAmbiguousError("Could not durably bind the submitted seller task.");
@@ -3481,32 +3583,128 @@ class MediaBuyLifecycleCoordinator {
3481
3583
  await this.markLegacyPurchaseAmbiguous(token, claim, "submitted_task_persistence_failed");
3482
3584
  throw this.legacyPurchaseAmbiguousError("Could not durably bind the submitted seller task.", error);
3483
3585
  }
3484
- const failureFieldsFromTask = (task) => {
3485
- const adcpError = (0, import_error_extraction.extractAdcpErrorInfo)(task.result);
3486
- const correlationId = (0, import_error_extraction.extractCorrelationId)(task.result);
3487
- return {
3488
- // tasks/get free text is not a durable-safe boundary. Preserve a
3489
- // structured AdCP message when present; otherwise replay a generic
3490
- // error while retaining the seller payload in data for inspection.
3491
- error: adcpError?.message ?? "Legacy create failed.",
3492
- ...adcpError !== void 0 && { adcpError },
3493
- ...correlationId !== void 0 && { correlationId }
3494
- };
3495
- };
3586
+ registerExternalTaskSettlement?.(async (observation) => {
3587
+ try {
3588
+ if (observation.serverTaskId !== sellerTaskId) {
3589
+ throw this.legacyPurchaseAmbiguousError(
3590
+ "The pushed seller task ID does not match the durably recorded submitted task."
3591
+ );
3592
+ }
3593
+ if (observation.taskType !== "create_media_buy") {
3594
+ throw this.legacyPurchaseAmbiguousError("The pushed seller task has the wrong task identity.");
3595
+ }
3596
+ const task = {
3597
+ taskId: sellerTaskId,
3598
+ taskType: observation.taskType,
3599
+ status: observation.status,
3600
+ createdAt: Date.now(),
3601
+ updatedAt: Date.now(),
3602
+ ...observation.result !== void 0 && { result: observation.result }
3603
+ };
3604
+ const observed = resultFromTask(task);
3605
+ if (!observed || !["completed", "failed", "governance-denied"].includes(observed.status)) {
3606
+ throw this.legacyPurchaseAmbiguousError(
3607
+ "The pushed legacy create status was not an authoritative terminal result."
3608
+ );
3609
+ }
3610
+ return await this.trackLegacyPurchaseResult(
3611
+ token,
3612
+ claim,
3613
+ observed,
3614
+ publishSettledTaskStatus,
3615
+ registerExternalTaskSettlement,
3616
+ settlementTransport
3617
+ );
3618
+ } catch (error) {
3619
+ if (error instanceof LegacyPurchaseContinuationError && error.code === "ambiguous") {
3620
+ await this.markLegacyPurchaseAmbiguous(token, claim, "pushed_task_result_invalid");
3621
+ }
3622
+ throw error;
3623
+ }
3624
+ });
3625
+ };
3626
+ if (result.status === "working" || result.status === "input-required" || result.status === "auth-required") {
3627
+ const sellerTaskId = result.metadata.serverTaskId;
3628
+ if (!sellerTaskId) {
3629
+ await this.markLegacyPurchaseAmbiguous(token, claim, "paused_task_identity_missing");
3630
+ throw this.legacyPurchaseAmbiguousError(
3631
+ "The non-terminal legacy create response did not provide a durable seller task identity."
3632
+ );
3633
+ }
3634
+ await bindSellerTask(sellerTaskId);
3635
+ if (result.status !== "working") return (0, import_match.attachMatch)(result);
3636
+ const watchSignal = AbortSignal.timeout(Math.min(this.legacyPurchaseOperationTtlMs, 5 * 60 * 1e3));
3637
+ const waitForWorkingPoll = () => new Promise((resolve, reject) => {
3638
+ if (watchSignal.aborted) {
3639
+ reject((0, import_abort.createAbortError)(watchSignal.reason));
3640
+ return;
3641
+ }
3642
+ const timer = setTimeout(finish, 6e4);
3643
+ const abort = () => finish((0, import_abort.createAbortError)(watchSignal.reason));
3644
+ watchSignal.addEventListener("abort", abort, { once: true });
3645
+ function finish(error) {
3646
+ clearTimeout(timer);
3647
+ watchSignal.removeEventListener("abort", abort);
3648
+ if (error) reject(error);
3649
+ else resolve();
3650
+ }
3651
+ });
3652
+ void new Promise((resolve) => setTimeout(resolve, 0)).then(async () => {
3653
+ while (!watchSignal.aborted) {
3654
+ const task = await this.agent.getTaskStatus(sellerTaskId, settlementTransport, watchSignal);
3655
+ if (task.taskId !== sellerTaskId || task.taskType !== "create_media_buy") {
3656
+ throw this.legacyPurchaseAmbiguousError(
3657
+ "The polled working seller task does not match the durably recorded create_media_buy task."
3658
+ );
3659
+ }
3660
+ const observed = resultFromTask(task);
3661
+ if (observed) {
3662
+ return this.trackLegacyPurchaseResult(
3663
+ token,
3664
+ claim,
3665
+ observed,
3666
+ publishSettledTaskStatus,
3667
+ registerExternalTaskSettlement,
3668
+ settlementTransport
3669
+ );
3670
+ }
3671
+ await waitForWorkingPoll();
3672
+ }
3673
+ throw (0, import_abort.createAbortError)(watchSignal.reason);
3674
+ }).then((completion) => {
3675
+ publishSettledTaskStatus?.(completion.status, completion.data, completion.error);
3676
+ }).catch(async (error) => {
3677
+ if (watchSignal.aborted || (0, import_abort.isAbortOrTimeoutError)(error)) return;
3678
+ await this.markLegacyPurchaseAmbiguous(token, claim, "non_terminal_completion_watch_uncertain");
3679
+ void error;
3680
+ });
3681
+ return (0, import_match.attachMatch)(result);
3682
+ }
3683
+ if (result.submitted) {
3684
+ const submitted = result.submitted;
3685
+ await bindSellerTask(submitted.taskId);
3496
3686
  const observeTask = async (transport, observationSignal) => {
3497
3687
  try {
3498
3688
  const task = observationSignal ? await (0, import_abort.withAbortSignal)([observationSignal], void 0, () => submitted.track(transport)) : await submitted.track(transport);
3689
+ if (task.taskId !== submitted.taskId) {
3690
+ throw this.legacyPurchaseAmbiguousError(
3691
+ "The tracked seller task ID does not match the durably recorded submitted task."
3692
+ );
3693
+ }
3694
+ if (task.taskType !== "create_media_buy") {
3695
+ throw this.legacyPurchaseAmbiguousError("The tracked seller task has the wrong task identity.");
3696
+ }
3499
3697
  if (["completed", "failed", "rejected", "canceled", "governance-denied"].includes(task.status)) {
3500
- if (task.taskType !== "create_media_buy") {
3501
- throw this.legacyPurchaseAmbiguousError("The tracked seller task has the wrong task identity.");
3502
- }
3503
3698
  if (task.status === "completed" && task.result === void 0) {
3504
3699
  throw this.legacyPurchaseAmbiguousError(
3505
3700
  "The tracked seller task reported completion without a create_media_buy result."
3506
3701
  );
3507
3702
  }
3508
3703
  const completedSuccessfully = task.status === "completed" && (0, import_response_unwrapper.isAdcpOperationSuccess)(task.result, task.taskType);
3509
- if (!completedSuccessfully && task.status !== "governance-denied" && (0, import_error_extraction.extractAdcpErrorInfo)(task.result) === void 0) {
3704
+ if (!completedSuccessfully && task.status !== "governance-denied" && (() => {
3705
+ const adcpError = (0, import_error_extraction.extractAdcpErrorInfo)(task.result);
3706
+ return adcpError === void 0 || adcpError.synthetic === true;
3707
+ })()) {
3510
3708
  throw this.legacyPurchaseAmbiguousError(
3511
3709
  "The tracked legacy create failure was not an authoritative structured AdCP error."
3512
3710
  );
@@ -3520,7 +3718,7 @@ class MediaBuyLifecycleCoordinator {
3520
3718
  ...task.result !== void 0 && { data: task.result }
3521
3719
  },
3522
3720
  metadata: {
3523
- ...result.metadata,
3721
+ ...settlementMetadata,
3524
3722
  status: completedSuccessfully ? "completed" : task.status === "governance-denied" ? "governance-denied" : "failed",
3525
3723
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
3526
3724
  }
@@ -3550,45 +3748,6 @@ class MediaBuyLifecycleCoordinator {
3550
3748
  throw this.legacyPurchaseAmbiguousError("Legacy create task tracking failed after claim.", error);
3551
3749
  }
3552
3750
  };
3553
- const resultFromTask = (task) => {
3554
- const metadata = {
3555
- ...result.metadata,
3556
- taskName: task.taskType,
3557
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
3558
- };
3559
- if (task.status === "completed" && task.result !== void 0 && (0, import_response_unwrapper.isAdcpOperationSuccess)(task.result, task.taskType)) {
3560
- return (0, import_match.attachMatch)({
3561
- success: true,
3562
- status: "completed",
3563
- data: task.result,
3564
- metadata: { ...metadata, status: "completed" }
3565
- });
3566
- }
3567
- if (task.status === "unknown") {
3568
- throw this.legacyPurchaseAmbiguousError(
3569
- "tasks/get returned an unrecognizable response for the submitted legacy purchase."
3570
- );
3571
- }
3572
- if (["completed", "failed", "rejected", "canceled", "governance-denied"].includes(task.status)) {
3573
- const status = task.status === "governance-denied" ? "governance-denied" : "failed";
3574
- return (0, import_match.attachMatch)({
3575
- success: false,
3576
- status,
3577
- ...failureFieldsFromTask(task),
3578
- ...task.result !== void 0 && { data: task.result },
3579
- metadata: { ...metadata, status }
3580
- });
3581
- }
3582
- if (task.status === "input-required" || task.status === "auth-required") {
3583
- return (0, import_match.attachMatch)({
3584
- success: true,
3585
- status: task.status,
3586
- ...task.result !== void 0 && { data: task.result },
3587
- metadata: { ...metadata, status: task.status }
3588
- });
3589
- }
3590
- return void 0;
3591
- };
3592
3751
  const waitForNextPoll = (state, signal) => new Promise((resolve, reject) => {
3593
3752
  if (signal.aborted) {
3594
3753
  reject((0, import_abort.createAbortError)(signal.reason));
@@ -3610,9 +3769,18 @@ class MediaBuyLifecycleCoordinator {
3610
3769
  const pollTrackedCompletion = async (state, signal) => {
3611
3770
  while (true) {
3612
3771
  if (signal.aborted) throw (0, import_abort.createAbortError)(signal.reason);
3613
- const task = await observeTask(void 0, signal);
3772
+ const task = await observeTask(settlementTransport, signal);
3614
3773
  const observed = resultFromTask(task);
3615
- if (observed) return this.trackLegacyPurchaseResult(token, claim, observed);
3774
+ if (observed) {
3775
+ return this.trackLegacyPurchaseResult(
3776
+ token,
3777
+ claim,
3778
+ observed,
3779
+ publishSettledTaskStatus,
3780
+ registerExternalTaskSettlement,
3781
+ settlementTransport
3782
+ );
3783
+ }
3616
3784
  await waitForNextPoll(state, signal);
3617
3785
  }
3618
3786
  };
@@ -3688,7 +3856,9 @@ class MediaBuyLifecycleCoordinator {
3688
3856
  }
3689
3857
  };
3690
3858
  const watchSignal = AbortSignal.timeout(Math.min(this.legacyPurchaseOperationTtlMs, 5 * 60 * 1e3));
3691
- void new Promise((resolve) => setTimeout(resolve, 0)).then(() => waitForSharedCompletion(void 0, watchSignal)).catch(async (error) => {
3859
+ void new Promise((resolve) => setTimeout(resolve, 0)).then(() => waitForSharedCompletion(void 0, watchSignal)).then((completion) => {
3860
+ publishSettledTaskStatus?.(completion.status, completion.data, completion.error);
3861
+ }).catch(async (error) => {
3692
3862
  if (watchSignal.aborted || (0, import_abort.isAbortOrTimeoutError)(error)) return;
3693
3863
  await this.markLegacyPurchaseAmbiguous(token, claim, "background_completion_watch_uncertain");
3694
3864
  void error;
@@ -3700,7 +3870,14 @@ class MediaBuyLifecycleCoordinator {
3700
3870
  ...deferred,
3701
3871
  resume: async (resumeInput) => {
3702
3872
  try {
3703
- return await this.trackLegacyPurchaseResult(token, claim, await deferred.resume(resumeInput));
3873
+ return await this.trackLegacyPurchaseResult(
3874
+ token,
3875
+ claim,
3876
+ await deferred.resume(resumeInput),
3877
+ publishSettledTaskStatus,
3878
+ registerExternalTaskSettlement,
3879
+ settlementTransport
3880
+ );
3704
3881
  } catch (error) {
3705
3882
  if (error instanceof LegacyPurchaseContinuationError) {
3706
3883
  if (error.code === "ambiguous") {
@@ -4658,6 +4835,25 @@ class MediaBuyLifecycleCoordinator {
4658
4835
  const input = record(params);
4659
4836
  const lifecycle = this.selectLifecycle("control_media_buy");
4660
4837
  this.assertLegacyReferenceShapes("controlMediaBuy", input);
4838
+ const canceledControlConflicts = [
4839
+ "name",
4840
+ "paused",
4841
+ "total_budget",
4842
+ "daily_budget_cap",
4843
+ "budget_cap_timezone",
4844
+ "budget_allocation",
4845
+ "pacing",
4846
+ "bidding",
4847
+ "packages",
4848
+ "reporting_webhook"
4849
+ ];
4850
+ if (Object.hasOwn(input, "canceled") && canceledControlConflicts.some((field) => Object.hasOwn(input, field))) {
4851
+ throw this.unsupported(
4852
+ "controlMediaBuy",
4853
+ "canceled",
4854
+ "Compact media-buy cancellation cannot be combined with other operational controls."
4855
+ );
4856
+ }
4661
4857
  if (lifecycle === "compact") {
4662
4858
  this.assertValidCompactRequest("control_media_buy", params, lifecycle, true);
4663
4859
  const result2 = await this.agent.controlMediaBuy(params, inputHandler, options);
@@ -4675,6 +4871,9 @@ class MediaBuyLifecycleCoordinator {
4675
4871
  "bidding",
4676
4872
  "governance_context"
4677
4873
  ]);
4874
+ if (!isCompactRelease(this.negotiated_version)) {
4875
+ this.assertCompactWireFieldsAbsent("controlMediaBuy", input, ["name"]);
4876
+ }
4678
4877
  if (compareRelease(this.negotiated_version, "3.0") < 0) {
4679
4878
  this.assertCompactWireFieldsAbsent("controlMediaBuy", input, ["reporting_webhook"]);
4680
4879
  }
@@ -4870,29 +5069,12 @@ class MediaBuyLifecycleCoordinator {
4870
5069
  "Compact media-buy cancellation_reason requires canceled=true."
4871
5070
  );
4872
5071
  }
4873
- const canceledControlConflicts = [
4874
- "paused",
4875
- "total_budget",
4876
- "daily_budget_cap",
4877
- "budget_cap_timezone",
4878
- "budget_allocation",
4879
- "pacing",
4880
- "bidding",
4881
- "packages",
4882
- "reporting_webhook"
4883
- ];
4884
- if (Object.hasOwn(input, "canceled") && canceledControlConflicts.some((field) => Object.hasOwn(input, field))) {
4885
- throw this.unsupported(
4886
- "controlMediaBuy",
4887
- "canceled",
4888
- "Compact media-buy cancellation cannot be combined with other operational controls."
4889
- );
4890
- }
4891
5072
  const request = {
4892
5073
  account: input.account,
4893
5074
  media_buy_id: input.media_buy_id,
4894
5075
  ...input.idempotency_key !== void 0 && { idempotency_key: input.idempotency_key },
4895
5076
  revision: input.revision,
5077
+ ...input.name !== void 0 && { name: input.name },
4896
5078
  ...input.paused !== void 0 && { paused: input.paused },
4897
5079
  ...input.canceled !== void 0 && { canceled: input.canceled },
4898
5080
  ...input.cancellation_reason !== void 0 && { cancellation_reason: input.cancellation_reason },