@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.
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/AgentClient.js +26 -10
- package/dist/lib/core/AgentClient.js.map +1 -1
- package/dist/lib/core/AgentClient.mjs +26 -10
- package/dist/lib/core/AgentClient.mjs.map +1 -1
- package/dist/lib/core/AsyncHandler.d.mts +4 -2
- package/dist/lib/core/AsyncHandler.d.ts +4 -2
- package/dist/lib/core/AsyncHandler.d.ts.map +1 -1
- package/dist/lib/core/AsyncHandler.js +15 -1
- package/dist/lib/core/AsyncHandler.js.map +1 -1
- package/dist/lib/core/AsyncHandler.mjs +15 -1
- package/dist/lib/core/AsyncHandler.mjs.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.mts +3 -1
- package/dist/lib/core/SingleAgentClient.d.ts +3 -1
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +158 -39
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +167 -40
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/core/TaskExecutor.d.mts +57 -2
- package/dist/lib/core/TaskExecutor.d.ts +57 -2
- package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
- package/dist/lib/core/TaskExecutor.js +382 -44
- package/dist/lib/core/TaskExecutor.js.map +1 -1
- package/dist/lib/core/TaskExecutor.mjs +382 -45
- package/dist/lib/core/TaskExecutor.mjs.map +1 -1
- package/dist/lib/core/request-proposals-guard.d.mts +11 -0
- package/dist/lib/core/request-proposals-guard.d.ts +12 -0
- package/dist/lib/core/request-proposals-guard.d.ts.map +1 -0
- package/dist/lib/core/request-proposals-guard.js +65 -0
- package/dist/lib/core/request-proposals-guard.js.map +1 -0
- package/dist/lib/core/request-proposals-guard.mjs +39 -0
- package/dist/lib/core/request-proposals-guard.mjs.map +1 -0
- package/dist/lib/core/webhook-registration.d.mts +5 -0
- package/dist/lib/core/webhook-registration.d.ts +5 -0
- package/dist/lib/core/webhook-registration.d.ts.map +1 -1
- package/dist/lib/core/webhook-registration.js +13 -0
- package/dist/lib/core/webhook-registration.js.map +1 -1
- package/dist/lib/core/webhook-registration.mjs +13 -0
- package/dist/lib/core/webhook-registration.mjs.map +1 -1
- package/dist/lib/media-buy/compatibility.d.mts +45 -10
- package/dist/lib/media-buy/compatibility.d.ts +45 -10
- package/dist/lib/media-buy/compatibility.d.ts.map +1 -1
- package/dist/lib/media-buy/compatibility.js +295 -113
- package/dist/lib/media-buy/compatibility.js.map +1 -1
- package/dist/lib/media-buy/compatibility.mjs +295 -113
- package/dist/lib/media-buy/compatibility.mjs.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/types/core.generated.d.mts +334 -85
- package/dist/lib/types/core.generated.d.ts +334 -85
- package/dist/lib/types/core.generated.d.ts.map +1 -1
- package/dist/lib/types/core.generated.js.map +1 -1
- package/dist/lib/types/decline-proposals.d.ts +0 -5
- package/dist/lib/types/index.d.mts +2 -0
- package/dist/lib/types/index.d.ts +2 -0
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/types/index.mjs.map +1 -1
- package/dist/lib/types/request-proposals.d.ts +254 -36
- package/dist/lib/types/schemas.generated.d.ts +3123 -835
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +192 -85
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/schemas.generated.mjs +192 -85
- package/dist/lib/types/schemas.generated.mjs.map +1 -1
- package/dist/lib/types/tools.generated.d.mts +1 -121
- package/dist/lib/types/tools.generated.d.ts +1 -121
- package/dist/lib/types/tools.generated.d.ts.map +1 -1
- package/dist/lib/types/tools.generated.js.map +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.js.map +1 -1
- package/dist/lib/version.mjs +3 -3
- package/dist/lib/version.mjs.map +1 -1
- package/docs/guides/MEDIA-BUY-3.2-COMPATIBILITY.md +28 -14
- package/docs/llms.txt +1 -1
- 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
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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
|
|
1639
|
+
if (sessionScope === void 0) {
|
|
1644
1640
|
throw new ConfigurationError(
|
|
1645
|
-
"Products-only
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3453
|
-
|
|
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,
|
|
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
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
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" &&
|
|
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
|
-
...
|
|
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(
|
|
3753
|
+
const task = await observeTask(settlementTransport, signal);
|
|
3595
3754
|
const observed = resultFromTask(task);
|
|
3596
|
-
if (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)).
|
|
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(
|
|
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 },
|