@adcp/sdk 16.3.0 → 16.4.0
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/adapters/version/3.1/assignments.d.mts +12 -0
- package/dist/lib/adapters/version/3.1/assignments.d.ts +12 -0
- package/dist/lib/adapters/version/3.1/assignments.js +67 -0
- package/dist/lib/adapters/version/3.1/assignments.mjs +43 -0
- package/dist/lib/adapters/version/3.1/bidding.d.mts +3 -0
- package/dist/lib/adapters/version/3.1/bidding.d.ts +3 -0
- package/dist/lib/adapters/version/3.1/bidding.js +115 -0
- package/dist/lib/adapters/version/3.1/bidding.mjs +90 -0
- package/dist/lib/adapters/version/3.1/delivery.d.mts +7 -0
- package/dist/lib/adapters/version/3.1/delivery.d.ts +7 -0
- package/dist/lib/adapters/version/3.1/delivery.js +78 -0
- package/dist/lib/adapters/version/3.1/delivery.mjs +54 -0
- package/dist/lib/adapters/version/3.1/index.d.mts +3 -0
- package/dist/lib/adapters/version/3.1/index.d.ts +3 -0
- package/dist/lib/adapters/version/3.1/index.js +36 -0
- package/dist/lib/adapters/version/3.1/index.mjs +12 -0
- package/dist/lib/adapters/version/index.d.mts +59 -7
- package/dist/lib/adapters/version/index.d.ts +59 -7
- package/dist/lib/adapters/version/index.js +51 -2
- package/dist/lib/adapters/version/index.mjs +49 -1
- package/dist/lib/adapters/version/negotiate.d.mts +71 -0
- package/dist/lib/adapters/version/negotiate.d.ts +71 -0
- package/dist/lib/adapters/version/negotiate.js +101 -0
- package/dist/lib/adapters/version/negotiate.mjs +73 -0
- package/dist/lib/adapters/version/schema-projection.d.mts +33 -0
- package/dist/lib/adapters/version/schema-projection.d.ts +33 -0
- package/dist/lib/adapters/version/schema-projection.js +294 -0
- package/dist/lib/adapters/version/schema-projection.mjs +269 -0
- package/dist/lib/adapters/version/translate.d.mts +122 -0
- package/dist/lib/adapters/version/translate.d.ts +122 -0
- package/dist/lib/adapters/version/translate.js +169 -0
- package/dist/lib/adapters/version/translate.mjs +141 -0
- package/dist/lib/adapters/version/types.d.mts +51 -10
- package/dist/lib/adapters/version/types.d.ts +51 -10
- package/dist/lib/core/AgentClient.d.mts +1 -1
- package/dist/lib/core/AgentClient.d.ts +1 -1
- package/dist/lib/core/ConversationTypes.d.mts +7 -0
- package/dist/lib/core/ConversationTypes.d.ts +7 -0
- package/dist/lib/core/SingleAgentClient.d.mts +82 -0
- package/dist/lib/core/SingleAgentClient.d.ts +82 -0
- package/dist/lib/core/SingleAgentClient.js +323 -15
- package/dist/lib/core/SingleAgentClient.mjs +337 -17
- package/dist/lib/core/TaskExecutor.d.mts +1 -1
- package/dist/lib/core/TaskExecutor.d.ts +1 -1
- package/dist/lib/core/TaskExecutor.js +97 -21
- package/dist/lib/core/TaskExecutor.mjs +97 -21
- package/dist/lib/core/account-reduction.d.mts +36 -0
- package/dist/lib/core/account-reduction.d.ts +36 -0
- package/dist/lib/core/account-reduction.js +127 -0
- package/dist/lib/core/account-reduction.mjs +98 -0
- package/dist/lib/core/buyer-account-registry.d.mts +52 -0
- package/dist/lib/core/buyer-account-registry.d.ts +52 -0
- package/dist/lib/core/buyer-account-registry.js +142 -6
- package/dist/lib/core/buyer-account-registry.mjs +142 -6
- package/dist/lib/media-buy/action-contracts.js +4 -4
- package/dist/lib/media-buy/action-contracts.mjs +4 -4
- package/dist/lib/media-buy/action-types.d.mts +3 -2
- package/dist/lib/media-buy/action-types.d.ts +3 -2
- package/dist/lib/media-buy/update-fields.generated.d.mts +3 -2
- package/dist/lib/media-buy/update-fields.generated.d.ts +3 -2
- package/dist/lib/registry/types.generated.d.mts +1 -1
- package/dist/lib/registry/types.generated.d.ts +1 -1
- package/dist/lib/server/create-adcp-server.d.mts +21 -0
- package/dist/lib/server/create-adcp-server.d.ts +21 -0
- package/dist/lib/server/create-adcp-server.js +97 -15
- package/dist/lib/server/create-adcp-server.mjs +83 -1
- package/dist/lib/server/index.d.mts +1 -1
- package/dist/lib/server/index.d.ts +1 -1
- package/dist/lib/storage/interfaces.d.mts +2 -0
- package/dist/lib/storage/interfaces.d.ts +2 -0
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +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.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/llms.txt +3 -3
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/package.json +1 -1
|
@@ -41,6 +41,7 @@ var import_schema_loader = require('../validation/schema-loader.js');
|
|
|
41
41
|
var import_canonicalize = require('../signing/agent-resolver/canonicalize.js');
|
|
42
42
|
var import_products = require('../supply-path/products.js');
|
|
43
43
|
var import_node_crypto = require("node:crypto");
|
|
44
|
+
var import_account_reduction = require('./account-reduction.js');
|
|
44
45
|
var import_adcp = require('../types/adcp.js');
|
|
45
46
|
var import_version = require('../version.js');
|
|
46
47
|
var import_adcp_version_config = require('../utils/adcp-version-config.js');
|
|
@@ -275,8 +276,9 @@ function creativeMatchesProductOption(creative, declaration) {
|
|
|
275
276
|
function isDeferredClientFinalizationContext(value) {
|
|
276
277
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
277
278
|
const context = value;
|
|
278
|
-
return context.kind === "single-agent" && typeof context.taskType === "string" && typeof context.canonical === "boolean" && !!context.productPolicyRequest && typeof context.productPolicyRequest === "object" && !Array.isArray(context.productPolicyRequest) && (context.serverVersion === void 0 || context.serverVersion === "v2" || context.serverVersion === "v3") && (context.serverVersionSynthetic === void 0 || typeof context.serverVersionSynthetic === "boolean") && (context.projectionCatalogs === void 0 || Array.isArray(context.projectionCatalogs)) && (context.handlerName === void 0 || typeof context.handlerName === "string") && (context.skipStatusHandlers === void 0 || typeof context.skipStatusHandlers === "boolean");
|
|
279
|
+
return context.kind === "single-agent" && typeof context.taskType === "string" && typeof context.canonical === "boolean" && !!context.productPolicyRequest && typeof context.productPolicyRequest === "object" && !Array.isArray(context.productPolicyRequest) && (context.serverVersion === void 0 || context.serverVersion === "v2" || context.serverVersion === "v3") && (context.serverVersionSynthetic === void 0 || typeof context.serverVersionSynthetic === "boolean") && (context.negotiatedAdcpVersion === void 0 || typeof context.negotiatedAdcpVersion === "string") && (context.projectionCatalogs === void 0 || Array.isArray(context.projectionCatalogs)) && (context.handlerName === void 0 || typeof context.handlerName === "string") && (context.skipStatusHandlers === void 0 || typeof context.skipStatusHandlers === "boolean");
|
|
279
280
|
}
|
|
281
|
+
const MAX_REMEMBERED_NEGOTIATED_RELEASES = 1024;
|
|
280
282
|
const CANONICAL_PACKAGE_ROUTE_TASKS = /* @__PURE__ */ new Set(["create_media_buy", "update_media_buy", "get_media_buys"]);
|
|
281
283
|
const canonicalCreativeRoutingSnapshots = /* @__PURE__ */ new WeakSet();
|
|
282
284
|
function canonicalAccountRoutingSnapshot(value) {
|
|
@@ -822,6 +824,12 @@ class SingleAgentClient {
|
|
|
822
824
|
this.resolvedAdcpVersion = (0, import_adcp_version_config.resolveAdcpVersion)(
|
|
823
825
|
config.adcpVersion ?? (config.experimentalIdentity33 ? "3.3" : void 0)
|
|
824
826
|
);
|
|
827
|
+
if (config.accountIdentityDowngrade === "reduce" && typeof config.accountStorage?.compareAndSet !== "function") {
|
|
828
|
+
throw new import_errors.ConfigurationError(
|
|
829
|
+
"accountIdentityDowngrade: 'reduce' requires accountStorage with compareAndSet. Reduced account keys are claimed in that storage so that no two buyer accounts reach one seller account, across instances and restarts. Configure durable accountStorage that implements compareAndSet, or use 'refuse'.",
|
|
830
|
+
"accountIdentityDowngrade"
|
|
831
|
+
);
|
|
832
|
+
}
|
|
825
833
|
this.webhookRegistrationStore = config.webhookRegistrationStore ?? new import_webhook_registration.InMemoryWebhookRegistrationStore();
|
|
826
834
|
this.webhookReplayStore = config.webhookVerification?.replayStore ?? new import_server.InMemoryReplayStore();
|
|
827
835
|
this.webhookRevocationStore = config.webhookVerification?.revocationStore ?? new import_server.InMemoryRevocationStore();
|
|
@@ -1142,6 +1150,8 @@ class SingleAgentClient {
|
|
|
1142
1150
|
runAgentInfo;
|
|
1143
1151
|
cachedToolSchemas;
|
|
1144
1152
|
// inputSchema.properties per tool name
|
|
1153
|
+
/** Release negotiated per operation, for converting webhook completions up. */
|
|
1154
|
+
negotiatedReleaseByOperation = /* @__PURE__ */ new Map();
|
|
1145
1155
|
primedCapabilitiesExpiresAt;
|
|
1146
1156
|
// Freshness bound for application-owned evidence only
|
|
1147
1157
|
capabilityEvidenceScopeKey = (0, import_node_crypto.randomUUID)();
|
|
@@ -2951,7 +2961,11 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
2951
2961
|
let externalStatus;
|
|
2952
2962
|
let forgetPolicyState = false;
|
|
2953
2963
|
try {
|
|
2954
|
-
const canonicalResult = this.canonicalizeWebhookCreativeResult(
|
|
2964
|
+
const canonicalResult = this.canonicalizeWebhookCreativeResult(
|
|
2965
|
+
metadata,
|
|
2966
|
+
this.convertWebhookResultToClientRelease(parsed),
|
|
2967
|
+
canonicalCreativeTask
|
|
2968
|
+
);
|
|
2955
2969
|
const dedupResult = canonicalResult;
|
|
2956
2970
|
const dedupMetadata = { ...metadata };
|
|
2957
2971
|
let webhookResult = dedupResult;
|
|
@@ -3532,32 +3546,47 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
3532
3546
|
};
|
|
3533
3547
|
const serverVersion = await this.detectServerVersion(detectionOptions);
|
|
3534
3548
|
(0, import_abort2.throwIfAborted)(options?.signal);
|
|
3549
|
+
const negotiatedRelease = this.negotiateCallRelease(capabilityDiscoveryContext.capabilities, options);
|
|
3550
|
+
this.assertToolAvailableAtRelease(taskType, negotiatedRelease, capabilityDiscoveryContext.toolSchemas);
|
|
3535
3551
|
this.assertRequestSupportedByTargetVersion(
|
|
3536
3552
|
taskType,
|
|
3537
3553
|
normalizedParams,
|
|
3538
3554
|
capabilityDiscoveryContext.capabilities,
|
|
3539
3555
|
canonicalCreativeInvocation,
|
|
3540
|
-
serverVersion
|
|
3556
|
+
serverVersion,
|
|
3557
|
+
negotiatedRelease
|
|
3541
3558
|
);
|
|
3542
|
-
const { options:
|
|
3559
|
+
const { options: webhookOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(
|
|
3543
3560
|
taskType,
|
|
3544
3561
|
options,
|
|
3545
3562
|
capabilityDiscoveryContext.capabilities
|
|
3546
3563
|
);
|
|
3564
|
+
const effectiveOptions = this.withNegotiatedRelease(webhookOptions, negotiatedRelease);
|
|
3547
3565
|
const inputSchemaStripLogs = [];
|
|
3548
3566
|
if (options?.preserveGovernedPayload && serverVersion !== "v3") {
|
|
3549
3567
|
throw new import_errors.ConfigurationError(
|
|
3550
3568
|
"Governed storyboard requests require a v3 seller to preserve the approved payload."
|
|
3551
3569
|
);
|
|
3552
3570
|
}
|
|
3553
|
-
const { params:
|
|
3571
|
+
const { params: convertedParams, driftLogs: adaptDriftLogs } = options?.preserveGovernedPayload ? { params: normalizedParams, driftLogs: [] } : this.adaptRequest(
|
|
3554
3572
|
taskType,
|
|
3555
3573
|
normalizedParams,
|
|
3556
3574
|
serverVersion,
|
|
3557
3575
|
inputSchemaStripLogs,
|
|
3558
3576
|
capabilityDiscoveryContext.toolSchemas,
|
|
3559
|
-
capabilityDiscoveryContext.capabilities
|
|
3577
|
+
capabilityDiscoveryContext.capabilities,
|
|
3578
|
+
negotiatedRelease
|
|
3560
3579
|
);
|
|
3580
|
+
const adaptedParams = options?.preserveGovernedPayload ? convertedParams : await this.reconcileAccountKeyReductions(
|
|
3581
|
+
taskType,
|
|
3582
|
+
normalizedParams,
|
|
3583
|
+
convertedParams,
|
|
3584
|
+
negotiatedRelease,
|
|
3585
|
+
adaptDriftLogs
|
|
3586
|
+
);
|
|
3587
|
+
if (negotiatedRelease?.downgraded && !options?.skipIdempotencyAutoInject && !options?.skipAccountValidation && !options?.skipRequestValidation) {
|
|
3588
|
+
this.executor.validateRequest(taskType, adaptedParams, inputSchemaStripLogs, negotiatedRelease.version);
|
|
3589
|
+
}
|
|
3561
3590
|
const adaptationLogs = [...adaptDriftLogs];
|
|
3562
3591
|
if (webhookDriftLog) adaptationLogs.push(webhookDriftLog);
|
|
3563
3592
|
const canonicalInputHandler = this.canonicalCreativeInputHandler(
|
|
@@ -3575,6 +3604,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
3575
3604
|
...capabilityDiscoveryContext.capabilities?._synthetic !== void 0 && {
|
|
3576
3605
|
serverVersionSynthetic: capabilityDiscoveryContext.capabilities._synthetic
|
|
3577
3606
|
},
|
|
3607
|
+
...negotiatedRelease?.downgraded && { negotiatedAdcpVersion: negotiatedRelease.version },
|
|
3578
3608
|
productPolicyRequest: productPolicyRequestSnapshot(normalizedParams),
|
|
3579
3609
|
accountRegistry: this.accountRegistryContext(taskType, normalizedParams, effectiveOptions),
|
|
3580
3610
|
...projectionCatalogs !== void 0 && { projectionCatalogs },
|
|
@@ -3642,6 +3672,21 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
3642
3672
|
}
|
|
3643
3673
|
};
|
|
3644
3674
|
}
|
|
3675
|
+
if (context.negotiatedAdcpVersion !== void 0) {
|
|
3676
|
+
result.metadata = { ...result.metadata, negotiatedAdcpVersion: context.negotiatedAdcpVersion };
|
|
3677
|
+
if (result.metadata.taskId) this.rememberNegotiatedRelease(result.metadata.taskId, context.negotiatedAdcpVersion);
|
|
3678
|
+
if (result.success && result.data) {
|
|
3679
|
+
const converted = this.convertResponseToClientRelease(
|
|
3680
|
+
context.taskType,
|
|
3681
|
+
result.data,
|
|
3682
|
+
context.negotiatedAdcpVersion
|
|
3683
|
+
);
|
|
3684
|
+
result.data = converted.data;
|
|
3685
|
+
if (converted.driftLogs.length > 0) {
|
|
3686
|
+
result.debug_logs = [...result.debug_logs ?? [], ...converted.driftLogs];
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3645
3690
|
const completionHandlerAlreadyPublished = (0, import_TaskExecutor.hasCompletionHandlerAlreadyPublished)(result);
|
|
3646
3691
|
const settlementAcknowledger = result[import_TaskExecutor.DEFERRED_SETTLEMENT_ACK];
|
|
3647
3692
|
const taskType = context.taskType;
|
|
@@ -4626,6 +4671,228 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
4626
4671
|
suppressHandler: !policyResult.success
|
|
4627
4672
|
};
|
|
4628
4673
|
}
|
|
4674
|
+
get accountIdentityDowngrade() {
|
|
4675
|
+
return this.config.accountIdentityDowngrade ?? "refuse";
|
|
4676
|
+
}
|
|
4677
|
+
/**
|
|
4678
|
+
* Negotiate the AdCP release to speak with this seller for one call: the
|
|
4679
|
+
* highest release both sides support. Returns `undefined` when the call
|
|
4680
|
+
* keeps its configured wire version: an explicit `wireAdcpVersion`
|
|
4681
|
+
* override, a governed payload that must reach the seller byte-for-byte,
|
|
4682
|
+
* the identity 3.3 experiment, or no authoritative seller release
|
|
4683
|
+
* (synthetic capabilities, no release in common, or no release metadata at
|
|
4684
|
+
* all: AdCP 3.1 does not require `supported_versions`, so its absence does
|
|
4685
|
+
* not prove a 3.0 seller, and such sellers keep the 3.0 request adapters).
|
|
4686
|
+
*/
|
|
4687
|
+
negotiateCallRelease(capabilities, options) {
|
|
4688
|
+
if (this.config.wireAdcpVersion !== void 0 || options?.wireAdcpVersion !== void 0) return void 0;
|
|
4689
|
+
if (options?.preserveGovernedPayload || this.config.experimentalIdentity33) return void 0;
|
|
4690
|
+
const negotiated = (0, import_version2.negotiateAdcpRelease)(this.resolvedAdcpVersion, capabilities);
|
|
4691
|
+
if (!negotiated || (0, import_version2.isNegotiationFailure)(negotiated) || negotiated.source === "legacy_v3") return void 0;
|
|
4692
|
+
return negotiated;
|
|
4693
|
+
}
|
|
4694
|
+
/**
|
|
4695
|
+
* Per-call options carrying the negotiated release to the executor, which
|
|
4696
|
+
* stamps it on the version envelope and validates the response against it.
|
|
4697
|
+
*/
|
|
4698
|
+
withNegotiatedRelease(options, negotiated) {
|
|
4699
|
+
if (!negotiated?.downgraded) return options;
|
|
4700
|
+
return { ...options ?? {}, wireAdcpVersion: negotiated.version, negotiatedAdcpVersion: negotiated.version };
|
|
4701
|
+
}
|
|
4702
|
+
/**
|
|
4703
|
+
* Refuse a tool the negotiated release does not define (a 3.2-only task
|
|
4704
|
+
* sent to a 3.1 seller) with a typed error instead of a seller-side schema
|
|
4705
|
+
* or unknown-tool failure. Sellers that list the tool anyway are called.
|
|
4706
|
+
*/
|
|
4707
|
+
assertToolAvailableAtRelease(taskType, negotiated, toolSchemas) {
|
|
4708
|
+
if (!negotiated?.downgraded) return;
|
|
4709
|
+
if ((0, import_schema_loader2.isExternalSchemaRootActive)(negotiated.version)) return;
|
|
4710
|
+
if ((0, import_version2.releaseDefinesTool)(taskType, negotiated.version)) return;
|
|
4711
|
+
if (!(0, import_version2.releaseDefinesTool)(taskType, this.resolvedAdcpVersion)) return;
|
|
4712
|
+
if ((toolSchemas ?? this.cachedToolSchemas)?.has(taskType)) return;
|
|
4713
|
+
const suggestion = `Call a task AdCP ${negotiated.version} defines, or reach this seller once it supports AdCP ${(0, import_schema_loader2.toReleasePrecisionWire)(this.resolvedAdcpVersion)}.`;
|
|
4714
|
+
throw new import_errors.ProtocolFeatureUnsupportedError([taskType], [], this.agent.agent_uri, {
|
|
4715
|
+
message: `${taskType} is not defined in AdCP ${negotiated.version}, the newest release this seller supports. No request was sent. ${suggestion}`,
|
|
4716
|
+
suggestion,
|
|
4717
|
+
details: {
|
|
4718
|
+
feature: taskType,
|
|
4719
|
+
required_version: (0, import_schema_loader2.toReleasePrecisionWire)(this.resolvedAdcpVersion),
|
|
4720
|
+
capability_path: "adcp.supported_versions",
|
|
4721
|
+
current_version: negotiated.version,
|
|
4722
|
+
tool: taskType
|
|
4723
|
+
}
|
|
4724
|
+
});
|
|
4725
|
+
}
|
|
4726
|
+
/**
|
|
4727
|
+
* Account-key bookkeeping for `accountIdentityDowngrade: 'reduce'`.
|
|
4728
|
+
*
|
|
4729
|
+
* Converting down to an older seller release: claim each reduced key for
|
|
4730
|
+
* the buyer key it came from, and refuse the request when the reduced key
|
|
4731
|
+
* (or an unreduced key the request sends as is) already belongs to a
|
|
4732
|
+
* different buyer key. Without this, two buyer accounts would silently
|
|
4733
|
+
* share one seller account, with merged settings, spend and reads.
|
|
4734
|
+
*
|
|
4735
|
+
* Seller on the client's release: a buyer key that earlier reached this
|
|
4736
|
+
* seller reduced is still addressed by its reduced key, which is the key
|
|
4737
|
+
* the seller's account has, until the caller moves the account to the full
|
|
4738
|
+
* key and calls `client.accounts.clearReduction(key)`.
|
|
4739
|
+
*
|
|
4740
|
+
* Runs only once the account registry is in use or under `'reduce'`.
|
|
4741
|
+
*/
|
|
4742
|
+
async reconcileAccountKeyReductions(taskType, original, adapted, negotiated, driftLogs) {
|
|
4743
|
+
if (this.accountIdentityDowngrade !== "reduce" && !this._accounts) return adapted;
|
|
4744
|
+
const sites = (0, import_account_reduction.accountKeySites)(original);
|
|
4745
|
+
if (sites.length === 0) return adapted;
|
|
4746
|
+
const registry = this.getAccountRegistry();
|
|
4747
|
+
if (negotiated?.downgraded) {
|
|
4748
|
+
if (this.accountIdentityDowngrade !== "reduce") return adapted;
|
|
4749
|
+
const claims = sites.flatMap((site) => {
|
|
4750
|
+
const reduced = (0, import_account_reduction.naturalAccountKey)((0, import_account_reduction.valueAt)(adapted, site.path));
|
|
4751
|
+
return reduced ? [{ site, account: site.key, reduced, adcpVersion: negotiated.version }] : [];
|
|
4752
|
+
});
|
|
4753
|
+
const conflict = await registry.claimReductions(claims);
|
|
4754
|
+
if (conflict) {
|
|
4755
|
+
this.throwAccountKeyCollision(taskType, claims[conflict.index].site.path, conflict.owner, negotiated.version);
|
|
4756
|
+
}
|
|
4757
|
+
return adapted;
|
|
4758
|
+
}
|
|
4759
|
+
let rewritten = adapted;
|
|
4760
|
+
const pending = [];
|
|
4761
|
+
for (const site of sites) {
|
|
4762
|
+
const reduction = await registry.reductionFor(site.key);
|
|
4763
|
+
if (!reduction) continue;
|
|
4764
|
+
rewritten = (0, import_account_reduction.withReducedKeyAt)(rewritten, site.path, reduction.account);
|
|
4765
|
+
pending.push({
|
|
4766
|
+
path: site.path.join("."),
|
|
4767
|
+
fields: (0, import_account_reduction.reducedFields)(site.key, reduction.account),
|
|
4768
|
+
adcpVersion: reduction.adcpVersion
|
|
4769
|
+
});
|
|
4770
|
+
}
|
|
4771
|
+
if (pending.length > 0) {
|
|
4772
|
+
const inPlace = pending.every((entry) => entry.fields.every((field) => field === "operator_unit"));
|
|
4773
|
+
driftLogs.push({
|
|
4774
|
+
type: "account_identity_migration_pending",
|
|
4775
|
+
message: `${taskType}: addressed ${pending.map((entry) => entry.path).join(", ")} by the reduced account key this seller has, without ${[...new Set(pending.flatMap((entry) => entry.fields))].join(", ")}. ` + (inPlace ? "Rekey the account in place with a sync_accounts settings update (account, operator_identity, revision), " : "currency, timezone and brand.countries cannot change on an existing account, so open a new account under the full key (and decide which key keeps the existing history), ") + "then call client.accounts.clearReduction(key).",
|
|
4776
|
+
accounts: pending,
|
|
4777
|
+
taskName: taskType,
|
|
4778
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4779
|
+
});
|
|
4780
|
+
}
|
|
4781
|
+
return rewritten;
|
|
4782
|
+
}
|
|
4783
|
+
throwAccountKeyCollision(taskType, path, owner, negotiatedVersion) {
|
|
4784
|
+
const field = path.join(".");
|
|
4785
|
+
const suggestion = `Reach this seller with one of the two account keys, address the account by account_id, or wait until the seller supports the release that tells them apart.`;
|
|
4786
|
+
throw new import_errors.ProtocolFeatureUnsupportedError([`${taskType}.${field}`], [], this.agent.agent_uri, {
|
|
4787
|
+
message: `${taskType} ${field} would reach the same seller account as another of this buyer's account keys: AdCP ${negotiatedVersion} cannot tell them apart. No request was sent. ${suggestion}`,
|
|
4788
|
+
field,
|
|
4789
|
+
suggestion,
|
|
4790
|
+
details: {
|
|
4791
|
+
feature: `${taskType}.${field}`,
|
|
4792
|
+
reason: "account_identity_collision",
|
|
4793
|
+
current_version: negotiatedVersion,
|
|
4794
|
+
tool: taskType,
|
|
4795
|
+
field,
|
|
4796
|
+
conflicting_account: owner
|
|
4797
|
+
}
|
|
4798
|
+
});
|
|
4799
|
+
}
|
|
4800
|
+
throwFieldsUnsupportedAtRelease(taskType, fields, negotiatedVersion) {
|
|
4801
|
+
const [first] = fields;
|
|
4802
|
+
const paths = fields.map((field) => field.path);
|
|
4803
|
+
const accountMove = paths.some((path) => /(?:^|\.)(?:operator_identity|revision)$/.test(path));
|
|
4804
|
+
const suggestion = accountMove ? `AdCP ${negotiatedVersion} cannot rekey an account or hand it to another operator (operator_identity, revision). Make the move once the seller supports AdCP ${first.introducedIn}; until then the account keeps its current key and operator.` : `Remove ${paths.join(", ")} for this seller, or reach it once it supports AdCP ${first.introducedIn}. Sending the request without them would change what the seller does.`;
|
|
4805
|
+
throw new import_errors.ProtocolFeatureUnsupportedError(
|
|
4806
|
+
paths.map((path) => `${taskType}.${path}`),
|
|
4807
|
+
[],
|
|
4808
|
+
this.agent.agent_uri,
|
|
4809
|
+
{
|
|
4810
|
+
message: `${taskType} ${paths.join(", ")} ${paths.length === 1 ? "requires" : "require"} AdCP ${first.introducedIn}; this seller supports AdCP ${negotiatedVersion}. No request was sent. ${suggestion}`,
|
|
4811
|
+
field: first.path,
|
|
4812
|
+
suggestion,
|
|
4813
|
+
details: {
|
|
4814
|
+
feature: `${taskType}.${first.path}`,
|
|
4815
|
+
required_version: first.introducedIn,
|
|
4816
|
+
capability_path: "adcp.supported_versions",
|
|
4817
|
+
current_version: negotiatedVersion,
|
|
4818
|
+
tool: taskType,
|
|
4819
|
+
field: first.path,
|
|
4820
|
+
fields: paths
|
|
4821
|
+
}
|
|
4822
|
+
}
|
|
4823
|
+
);
|
|
4824
|
+
}
|
|
4825
|
+
throwAccountIdentityUnsupported(taskType, fields, negotiatedVersion) {
|
|
4826
|
+
const [first] = fields;
|
|
4827
|
+
const paths = fields.map((field) => field.path);
|
|
4828
|
+
const suggestion = `Set accountIdentityDowngrade: 'reduce' to send the older account key (allowed while no other buyer key reaches the same seller account without ${paths.join(", ")}), or remove those fields for this seller.`;
|
|
4829
|
+
throw new import_errors.ProtocolFeatureUnsupportedError(
|
|
4830
|
+
paths.map((path) => `${taskType}.${path}`),
|
|
4831
|
+
[],
|
|
4832
|
+
this.agent.agent_uri,
|
|
4833
|
+
{
|
|
4834
|
+
message: `${taskType} ${paths.join(", ")} ${paths.length === 1 ? "is" : "are"} part of the AdCP ${first.introducedIn} account key, which AdCP ${negotiatedVersion} cannot express. No request was sent. ${suggestion}`,
|
|
4835
|
+
field: first.path,
|
|
4836
|
+
suggestion,
|
|
4837
|
+
details: {
|
|
4838
|
+
feature: `${taskType}.${first.path}`,
|
|
4839
|
+
required_version: first.introducedIn,
|
|
4840
|
+
capability_path: "adcp.supported_versions",
|
|
4841
|
+
current_version: negotiatedVersion,
|
|
4842
|
+
tool: taskType,
|
|
4843
|
+
field: first.path,
|
|
4844
|
+
fields: paths
|
|
4845
|
+
}
|
|
4846
|
+
}
|
|
4847
|
+
);
|
|
4848
|
+
}
|
|
4849
|
+
rememberNegotiatedRelease(operationId, version) {
|
|
4850
|
+
this.negotiatedReleaseByOperation.delete(operationId);
|
|
4851
|
+
this.negotiatedReleaseByOperation.set(operationId, version);
|
|
4852
|
+
if (this.negotiatedReleaseByOperation.size > MAX_REMEMBERED_NEGOTIATED_RELEASES) {
|
|
4853
|
+
const oldest = this.negotiatedReleaseByOperation.keys().next().value;
|
|
4854
|
+
if (oldest !== void 0) this.negotiatedReleaseByOperation.delete(oldest);
|
|
4855
|
+
}
|
|
4856
|
+
}
|
|
4857
|
+
/**
|
|
4858
|
+
* Webhook completions bypass task finalization, so convert their result up
|
|
4859
|
+
* here. The seller's own `adcp_version` names the release it served; an
|
|
4860
|
+
* AdCP 3.0 seller sends none, so fall back to the release this client
|
|
4861
|
+
* negotiated for the operation.
|
|
4862
|
+
*/
|
|
4863
|
+
convertWebhookResultToClientRelease(parsed) {
|
|
4864
|
+
const served = [parsed.result, parsed.envelope].map(
|
|
4865
|
+
(value) => value && typeof value === "object" && typeof value.adcp_version === "string" ? value.adcp_version : void 0
|
|
4866
|
+
).find((version) => version !== void 0);
|
|
4867
|
+
const release = served ?? this.negotiatedReleaseByOperation.get(parsed.metadata.operationId);
|
|
4868
|
+
if (release === void 0 || parsed.result === void 0 || parsed.result === null) return parsed.result;
|
|
4869
|
+
return (0, import_version2.convertResponse)(parsed.metadata.taskType, parsed.result, {
|
|
4870
|
+
fromVersion: release,
|
|
4871
|
+
toVersion: this.resolvedAdcpVersion
|
|
4872
|
+
}).data;
|
|
4873
|
+
}
|
|
4874
|
+
/**
|
|
4875
|
+
* Convert a response served at the negotiated release up to the client's
|
|
4876
|
+
* release. Returns drift entries for each conversion applied.
|
|
4877
|
+
*/
|
|
4878
|
+
convertResponseToClientRelease(taskType, data, negotiatedVersion) {
|
|
4879
|
+
if (negotiatedVersion === void 0 || data === void 0 || data === null) return { data, driftLogs: [] };
|
|
4880
|
+
const translation = (0, import_version2.convertResponse)(taskType, data, {
|
|
4881
|
+
fromVersion: negotiatedVersion,
|
|
4882
|
+
toVersion: this.resolvedAdcpVersion
|
|
4883
|
+
});
|
|
4884
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
4885
|
+
return {
|
|
4886
|
+
data: translation.data,
|
|
4887
|
+
driftLogs: translation.drifts.map((drift) => ({
|
|
4888
|
+
...drift,
|
|
4889
|
+
taskName: taskType,
|
|
4890
|
+
clientVersion: this.resolvedAdcpVersion,
|
|
4891
|
+
targetVersion: negotiatedVersion,
|
|
4892
|
+
timestamp
|
|
4893
|
+
}))
|
|
4894
|
+
};
|
|
4895
|
+
}
|
|
4629
4896
|
/**
|
|
4630
4897
|
* Adapt a request for the detected server wire version and the seller's
|
|
4631
4898
|
* AdCP protocol version. Applies protocol-version adapters (e.g. stripping
|
|
@@ -4635,7 +4902,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
4635
4902
|
* Runs after `detectServerVersion` so capabilities are available and the
|
|
4636
4903
|
* current call's tool schemas can be supplied without cross-tenant caching.
|
|
4637
4904
|
*/
|
|
4638
|
-
adaptRequest(taskType, params, serverVersion, debugLogs, perCallToolSchemas, perCallCapabilities) {
|
|
4905
|
+
adaptRequest(taskType, params, serverVersion, debugLogs, perCallToolSchemas, perCallCapabilities, negotiated) {
|
|
4639
4906
|
if (serverVersion !== "v3") {
|
|
4640
4907
|
throw new import_errors.VersionUnsupportedError(taskType, "version", "v2", this.agent.agent_uri);
|
|
4641
4908
|
}
|
|
@@ -4672,10 +4939,34 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
4672
4939
|
}
|
|
4673
4940
|
adapted = filtered;
|
|
4674
4941
|
}
|
|
4942
|
+
if (negotiated?.downgraded) {
|
|
4943
|
+
const translation = (0, import_version2.convertRequest)(taskType, adapted, {
|
|
4944
|
+
fromVersion: this.resolvedAdcpVersion,
|
|
4945
|
+
toVersion: negotiated.version,
|
|
4946
|
+
accountIdentity: this.accountIdentityDowngrade
|
|
4947
|
+
});
|
|
4948
|
+
if (translation.identityFields.length > 0 && this.accountIdentityDowngrade === "refuse") {
|
|
4949
|
+
this.throwAccountIdentityUnsupported(taskType, translation.identityFields, negotiated.version);
|
|
4950
|
+
}
|
|
4951
|
+
if (translation.refusedFields.length > 0) {
|
|
4952
|
+
this.throwFieldsUnsupportedAtRelease(taskType, translation.refusedFields, negotiated.version);
|
|
4953
|
+
}
|
|
4954
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
4955
|
+
for (const drift of translation.drifts) {
|
|
4956
|
+
driftLogs.push({
|
|
4957
|
+
...drift,
|
|
4958
|
+
taskName: taskType,
|
|
4959
|
+
clientVersion: this.resolvedAdcpVersion,
|
|
4960
|
+
targetVersion: negotiated.version,
|
|
4961
|
+
timestamp
|
|
4962
|
+
});
|
|
4963
|
+
}
|
|
4964
|
+
return { params: translation.params, driftLogs };
|
|
4965
|
+
}
|
|
4675
4966
|
const adapterKey = (0, import_version2.resolveAdapterKey)(this.resolvedAdcpVersion, perCallCapabilities ?? this.cachedCapabilities);
|
|
4676
4967
|
if (adapterKey) {
|
|
4677
4968
|
const versionAdapter = (0, import_version2.getVersionAdapter)(adapterKey, taskType);
|
|
4678
|
-
if (versionAdapter) {
|
|
4969
|
+
if (versionAdapter?.adaptRequest) {
|
|
4679
4970
|
const result = versionAdapter.adaptRequest(adapted);
|
|
4680
4971
|
adapted = result.params;
|
|
4681
4972
|
if (result.drift) {
|
|
@@ -6294,32 +6585,47 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
6294
6585
|
const serverVersion = await this.detectServerVersion(detectionOptions);
|
|
6295
6586
|
detectedServerVersion = serverVersion;
|
|
6296
6587
|
detectedServerVersionSynthetic = capabilityDiscoveryContext.capabilities?._synthetic;
|
|
6588
|
+
const negotiatedRelease = this.negotiateCallRelease(capabilityDiscoveryContext.capabilities, options);
|
|
6589
|
+
this.assertToolAvailableAtRelease(taskName, negotiatedRelease, capabilityDiscoveryContext.toolSchemas);
|
|
6297
6590
|
this.assertRequestSupportedByTargetVersion(
|
|
6298
6591
|
taskName,
|
|
6299
6592
|
normalizedParams,
|
|
6300
6593
|
capabilityDiscoveryContext.capabilities,
|
|
6301
6594
|
false,
|
|
6302
|
-
serverVersion
|
|
6595
|
+
serverVersion,
|
|
6596
|
+
negotiatedRelease
|
|
6303
6597
|
);
|
|
6304
|
-
const { options:
|
|
6598
|
+
const { options: webhookOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(
|
|
6305
6599
|
taskName,
|
|
6306
6600
|
options,
|
|
6307
6601
|
capabilityDiscoveryContext.capabilities
|
|
6308
6602
|
);
|
|
6603
|
+
const effectiveOptions = this.withNegotiatedRelease(webhookOptions, negotiatedRelease);
|
|
6309
6604
|
const inputSchemaStripLogs = [];
|
|
6310
6605
|
if (options?.preserveGovernedPayload && serverVersion !== "v3") {
|
|
6311
6606
|
throw new import_errors.ConfigurationError(
|
|
6312
6607
|
"Governed storyboard requests require a v3 seller to preserve the approved payload."
|
|
6313
6608
|
);
|
|
6314
6609
|
}
|
|
6315
|
-
const { params:
|
|
6610
|
+
const { params: convertedParams, driftLogs: adaptDriftLogs } = options?.preserveGovernedPayload ? { params: normalizedParams, driftLogs: [] } : this.adaptRequest(
|
|
6316
6611
|
taskName,
|
|
6317
6612
|
normalizedParams,
|
|
6318
6613
|
serverVersion,
|
|
6319
6614
|
inputSchemaStripLogs,
|
|
6320
6615
|
capabilityDiscoveryContext.toolSchemas,
|
|
6321
|
-
capabilityDiscoveryContext.capabilities
|
|
6616
|
+
capabilityDiscoveryContext.capabilities,
|
|
6617
|
+
negotiatedRelease
|
|
6322
6618
|
);
|
|
6619
|
+
const adaptedParams = options?.preserveGovernedPayload ? convertedParams : await this.reconcileAccountKeyReductions(
|
|
6620
|
+
taskName,
|
|
6621
|
+
normalizedParams,
|
|
6622
|
+
convertedParams,
|
|
6623
|
+
negotiatedRelease,
|
|
6624
|
+
adaptDriftLogs
|
|
6625
|
+
);
|
|
6626
|
+
if (negotiatedRelease?.downgraded && !options?.skipIdempotencyAutoInject && !options?.skipAccountValidation && !options?.skipRequestValidation) {
|
|
6627
|
+
this.executor.validateRequest(taskName, adaptedParams, inputSchemaStripLogs, negotiatedRelease.version);
|
|
6628
|
+
}
|
|
6323
6629
|
const adaptationLogs = [...adaptDriftLogs];
|
|
6324
6630
|
if (webhookDriftLog) adaptationLogs.push(webhookDriftLog);
|
|
6325
6631
|
const deferredClientContext = {
|
|
@@ -6331,6 +6637,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
6331
6637
|
...capabilityDiscoveryContext.capabilities?._synthetic !== void 0 && {
|
|
6332
6638
|
serverVersionSynthetic: capabilityDiscoveryContext.capabilities._synthetic
|
|
6333
6639
|
},
|
|
6640
|
+
...negotiatedRelease?.downgraded && { negotiatedAdcpVersion: negotiatedRelease.version },
|
|
6334
6641
|
productPolicyRequest: productPolicyRequestSnapshot(normalizedParams),
|
|
6335
6642
|
accountRegistry: this.accountRegistryContext(taskName, normalizedParams, effectiveOptions),
|
|
6336
6643
|
...effectiveOptions?.taskId !== void 0 && { optionTaskId: effectiveOptions.taskId },
|
|
@@ -8025,8 +8332,9 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
8025
8332
|
* first (cold-cache) call; the configured-version gate above still catches
|
|
8026
8333
|
* an explicitly pre-3.1 client before validation or network I/O.
|
|
8027
8334
|
*/
|
|
8028
|
-
assertRequestSupportedByTargetVersion(taskName, params, capabilities, canonicalCreativeInvocation = false, serverVersion) {
|
|
8029
|
-
|
|
8335
|
+
assertRequestSupportedByTargetVersion(taskName, params, capabilities, canonicalCreativeInvocation = false, serverVersion, negotiated) {
|
|
8336
|
+
const reducesAccountIdentity = this.accountIdentityDowngrade === "reduce" && negotiated?.downgraded === true;
|
|
8337
|
+
if (taskName === "sync_accounts" && !reducesAccountIdentity) {
|
|
8030
8338
|
const advertisedVersions2 = capabilities?.supportedVersions ?? [];
|
|
8031
8339
|
const servedVersion = capabilities?.servedVersion ?? capabilities?._raw?.adcp_version;
|
|
8032
8340
|
const supports32 = serverVersion !== "v2" && (typeof servedVersion === "string" ? (0, import_adcp_version_config.isAdcpVersionAtLeast)(servedVersion, "3.2.0-0") : advertisedVersions2.some((version) => (0, import_adcp_version_config.isAdcpVersionAtLeast)(version, "3.2.0-0")));
|
|
@@ -8196,7 +8504,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
8196
8504
|
}
|
|
8197
8505
|
const supportedVersions = capabilities.supportedVersions;
|
|
8198
8506
|
if (supportedVersions !== void 0 && supportedVersions.length > 0) {
|
|
8199
|
-
if (!(0, import_adcp_version_config.isAdcpVersionSupported)(this.resolvedAdcpVersion, supportedVersions)) {
|
|
8507
|
+
if (!(0, import_adcp_version_config.isAdcpVersionSupported)(this.resolvedAdcpVersion, supportedVersions) && this.negotiateCallRelease(capabilities, options) === void 0) {
|
|
8200
8508
|
throw new import_errors.VersionUnsupportedError(taskType, "version", capabilities.version, this.agent.agent_uri);
|
|
8201
8509
|
}
|
|
8202
8510
|
} else {
|