@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
|
@@ -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
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
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
|
|
1658
|
+
if (sessionScope === void 0) {
|
|
1663
1659
|
throw new import_errors.ConfigurationError(
|
|
1664
|
-
"Products-only
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3472
|
-
|
|
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,
|
|
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
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
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" && (
|
|
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
|
-
...
|
|
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(
|
|
3772
|
+
const task = await observeTask(settlementTransport, signal);
|
|
3614
3773
|
const observed = resultFromTask(task);
|
|
3615
|
-
if (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)).
|
|
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(
|
|
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 },
|