@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
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
import { randomUUID, scryptSync } from "node:crypto";
|
|
22
22
|
const SKIP_PRODUCT_CACHE = /* @__PURE__ */ Symbol("adcp.skipProductCache");
|
|
23
23
|
const SKIP_ACCOUNT_REGISTRY_OBSERVATION = /* @__PURE__ */ Symbol("adcp.skipAccountRegistryObservation");
|
|
24
|
+
import { accountKeySites, naturalAccountKey, reducedFields, valueAt, withReducedKeyAt } from "./account-reduction.mjs";
|
|
24
25
|
import { ADCP_ENVELOPE_FIELDS } from "../types/adcp.mjs";
|
|
25
26
|
import { parseAdcpMajorVersion, toReleasePrecisionVersion } from "../version.mjs";
|
|
26
27
|
import {
|
|
@@ -31,8 +32,20 @@ import {
|
|
|
31
32
|
resolveAdcpVersion
|
|
32
33
|
} from "../utils/adcp-version-config.mjs";
|
|
33
34
|
import { beta6ReportingRequestIssue } from "../media-buy/reporting-version.mjs";
|
|
34
|
-
import {
|
|
35
|
-
|
|
35
|
+
import {
|
|
36
|
+
convertRequest,
|
|
37
|
+
convertResponse,
|
|
38
|
+
getVersionAdapter,
|
|
39
|
+
isNegotiationFailure,
|
|
40
|
+
negotiateAdcpRelease,
|
|
41
|
+
releaseDefinesTool,
|
|
42
|
+
resolveAdapterKey
|
|
43
|
+
} from "../adapters/version/index.mjs";
|
|
44
|
+
import {
|
|
45
|
+
isExternalSchemaRootActive,
|
|
46
|
+
schemaAllowsTopLevelField,
|
|
47
|
+
toReleasePrecisionWire
|
|
48
|
+
} from "../validation/schema-loader.mjs";
|
|
36
49
|
import { generateIdempotencyKey, requestUsesIdempotency } from "../utils/idempotency.mjs";
|
|
37
50
|
import { createA2AClientFromCardUrl } from "../protocols/a2a.mjs";
|
|
38
51
|
import {
|
|
@@ -372,8 +385,9 @@ function creativeMatchesProductOption(creative, declaration) {
|
|
|
372
385
|
function isDeferredClientFinalizationContext(value) {
|
|
373
386
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
374
387
|
const context = value;
|
|
375
|
-
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");
|
|
388
|
+
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");
|
|
376
389
|
}
|
|
390
|
+
const MAX_REMEMBERED_NEGOTIATED_RELEASES = 1024;
|
|
377
391
|
const CANONICAL_PACKAGE_ROUTE_TASKS = /* @__PURE__ */ new Set(["create_media_buy", "update_media_buy", "get_media_buys"]);
|
|
378
392
|
const canonicalCreativeRoutingSnapshots = /* @__PURE__ */ new WeakSet();
|
|
379
393
|
function canonicalAccountRoutingSnapshot(value) {
|
|
@@ -919,6 +933,12 @@ class SingleAgentClient {
|
|
|
919
933
|
this.resolvedAdcpVersion = resolveAdcpVersion(
|
|
920
934
|
config.adcpVersion ?? (config.experimentalIdentity33 ? "3.3" : void 0)
|
|
921
935
|
);
|
|
936
|
+
if (config.accountIdentityDowngrade === "reduce" && typeof config.accountStorage?.compareAndSet !== "function") {
|
|
937
|
+
throw new ConfigurationError(
|
|
938
|
+
"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'.",
|
|
939
|
+
"accountIdentityDowngrade"
|
|
940
|
+
);
|
|
941
|
+
}
|
|
922
942
|
this.webhookRegistrationStore = config.webhookRegistrationStore ?? new InMemoryWebhookRegistrationStore();
|
|
923
943
|
this.webhookReplayStore = config.webhookVerification?.replayStore ?? new InMemoryReplayStore();
|
|
924
944
|
this.webhookRevocationStore = config.webhookVerification?.revocationStore ?? new InMemoryRevocationStore();
|
|
@@ -1239,6 +1259,8 @@ class SingleAgentClient {
|
|
|
1239
1259
|
runAgentInfo;
|
|
1240
1260
|
cachedToolSchemas;
|
|
1241
1261
|
// inputSchema.properties per tool name
|
|
1262
|
+
/** Release negotiated per operation, for converting webhook completions up. */
|
|
1263
|
+
negotiatedReleaseByOperation = /* @__PURE__ */ new Map();
|
|
1242
1264
|
primedCapabilitiesExpiresAt;
|
|
1243
1265
|
// Freshness bound for application-owned evidence only
|
|
1244
1266
|
capabilityEvidenceScopeKey = randomUUID();
|
|
@@ -3048,7 +3070,11 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
3048
3070
|
let externalStatus;
|
|
3049
3071
|
let forgetPolicyState = false;
|
|
3050
3072
|
try {
|
|
3051
|
-
const canonicalResult = this.canonicalizeWebhookCreativeResult(
|
|
3073
|
+
const canonicalResult = this.canonicalizeWebhookCreativeResult(
|
|
3074
|
+
metadata,
|
|
3075
|
+
this.convertWebhookResultToClientRelease(parsed),
|
|
3076
|
+
canonicalCreativeTask
|
|
3077
|
+
);
|
|
3052
3078
|
const dedupResult = canonicalResult;
|
|
3053
3079
|
const dedupMetadata = { ...metadata };
|
|
3054
3080
|
let webhookResult = dedupResult;
|
|
@@ -3629,32 +3655,47 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
3629
3655
|
};
|
|
3630
3656
|
const serverVersion = await this.detectServerVersion(detectionOptions);
|
|
3631
3657
|
throwIfAborted(options?.signal);
|
|
3658
|
+
const negotiatedRelease = this.negotiateCallRelease(capabilityDiscoveryContext.capabilities, options);
|
|
3659
|
+
this.assertToolAvailableAtRelease(taskType, negotiatedRelease, capabilityDiscoveryContext.toolSchemas);
|
|
3632
3660
|
this.assertRequestSupportedByTargetVersion(
|
|
3633
3661
|
taskType,
|
|
3634
3662
|
normalizedParams,
|
|
3635
3663
|
capabilityDiscoveryContext.capabilities,
|
|
3636
3664
|
canonicalCreativeInvocation,
|
|
3637
|
-
serverVersion
|
|
3665
|
+
serverVersion,
|
|
3666
|
+
negotiatedRelease
|
|
3638
3667
|
);
|
|
3639
|
-
const { options:
|
|
3668
|
+
const { options: webhookOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(
|
|
3640
3669
|
taskType,
|
|
3641
3670
|
options,
|
|
3642
3671
|
capabilityDiscoveryContext.capabilities
|
|
3643
3672
|
);
|
|
3673
|
+
const effectiveOptions = this.withNegotiatedRelease(webhookOptions, negotiatedRelease);
|
|
3644
3674
|
const inputSchemaStripLogs = [];
|
|
3645
3675
|
if (options?.preserveGovernedPayload && serverVersion !== "v3") {
|
|
3646
3676
|
throw new ConfigurationError(
|
|
3647
3677
|
"Governed storyboard requests require a v3 seller to preserve the approved payload."
|
|
3648
3678
|
);
|
|
3649
3679
|
}
|
|
3650
|
-
const { params:
|
|
3680
|
+
const { params: convertedParams, driftLogs: adaptDriftLogs } = options?.preserveGovernedPayload ? { params: normalizedParams, driftLogs: [] } : this.adaptRequest(
|
|
3651
3681
|
taskType,
|
|
3652
3682
|
normalizedParams,
|
|
3653
3683
|
serverVersion,
|
|
3654
3684
|
inputSchemaStripLogs,
|
|
3655
3685
|
capabilityDiscoveryContext.toolSchemas,
|
|
3656
|
-
capabilityDiscoveryContext.capabilities
|
|
3686
|
+
capabilityDiscoveryContext.capabilities,
|
|
3687
|
+
negotiatedRelease
|
|
3688
|
+
);
|
|
3689
|
+
const adaptedParams = options?.preserveGovernedPayload ? convertedParams : await this.reconcileAccountKeyReductions(
|
|
3690
|
+
taskType,
|
|
3691
|
+
normalizedParams,
|
|
3692
|
+
convertedParams,
|
|
3693
|
+
negotiatedRelease,
|
|
3694
|
+
adaptDriftLogs
|
|
3657
3695
|
);
|
|
3696
|
+
if (negotiatedRelease?.downgraded && !options?.skipIdempotencyAutoInject && !options?.skipAccountValidation && !options?.skipRequestValidation) {
|
|
3697
|
+
this.executor.validateRequest(taskType, adaptedParams, inputSchemaStripLogs, negotiatedRelease.version);
|
|
3698
|
+
}
|
|
3658
3699
|
const adaptationLogs = [...adaptDriftLogs];
|
|
3659
3700
|
if (webhookDriftLog) adaptationLogs.push(webhookDriftLog);
|
|
3660
3701
|
const canonicalInputHandler = this.canonicalCreativeInputHandler(
|
|
@@ -3672,6 +3713,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
3672
3713
|
...capabilityDiscoveryContext.capabilities?._synthetic !== void 0 && {
|
|
3673
3714
|
serverVersionSynthetic: capabilityDiscoveryContext.capabilities._synthetic
|
|
3674
3715
|
},
|
|
3716
|
+
...negotiatedRelease?.downgraded && { negotiatedAdcpVersion: negotiatedRelease.version },
|
|
3675
3717
|
productPolicyRequest: productPolicyRequestSnapshot(normalizedParams),
|
|
3676
3718
|
accountRegistry: this.accountRegistryContext(taskType, normalizedParams, effectiveOptions),
|
|
3677
3719
|
...projectionCatalogs !== void 0 && { projectionCatalogs },
|
|
@@ -3739,6 +3781,21 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
3739
3781
|
}
|
|
3740
3782
|
};
|
|
3741
3783
|
}
|
|
3784
|
+
if (context.negotiatedAdcpVersion !== void 0) {
|
|
3785
|
+
result.metadata = { ...result.metadata, negotiatedAdcpVersion: context.negotiatedAdcpVersion };
|
|
3786
|
+
if (result.metadata.taskId) this.rememberNegotiatedRelease(result.metadata.taskId, context.negotiatedAdcpVersion);
|
|
3787
|
+
if (result.success && result.data) {
|
|
3788
|
+
const converted = this.convertResponseToClientRelease(
|
|
3789
|
+
context.taskType,
|
|
3790
|
+
result.data,
|
|
3791
|
+
context.negotiatedAdcpVersion
|
|
3792
|
+
);
|
|
3793
|
+
result.data = converted.data;
|
|
3794
|
+
if (converted.driftLogs.length > 0) {
|
|
3795
|
+
result.debug_logs = [...result.debug_logs ?? [], ...converted.driftLogs];
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3742
3799
|
const completionHandlerAlreadyPublished = hasCompletionHandlerAlreadyPublished(result);
|
|
3743
3800
|
const settlementAcknowledger = result[DEFERRED_SETTLEMENT_ACK];
|
|
3744
3801
|
const taskType = context.taskType;
|
|
@@ -4723,6 +4780,228 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
4723
4780
|
suppressHandler: !policyResult.success
|
|
4724
4781
|
};
|
|
4725
4782
|
}
|
|
4783
|
+
get accountIdentityDowngrade() {
|
|
4784
|
+
return this.config.accountIdentityDowngrade ?? "refuse";
|
|
4785
|
+
}
|
|
4786
|
+
/**
|
|
4787
|
+
* Negotiate the AdCP release to speak with this seller for one call: the
|
|
4788
|
+
* highest release both sides support. Returns `undefined` when the call
|
|
4789
|
+
* keeps its configured wire version: an explicit `wireAdcpVersion`
|
|
4790
|
+
* override, a governed payload that must reach the seller byte-for-byte,
|
|
4791
|
+
* the identity 3.3 experiment, or no authoritative seller release
|
|
4792
|
+
* (synthetic capabilities, no release in common, or no release metadata at
|
|
4793
|
+
* all: AdCP 3.1 does not require `supported_versions`, so its absence does
|
|
4794
|
+
* not prove a 3.0 seller, and such sellers keep the 3.0 request adapters).
|
|
4795
|
+
*/
|
|
4796
|
+
negotiateCallRelease(capabilities, options) {
|
|
4797
|
+
if (this.config.wireAdcpVersion !== void 0 || options?.wireAdcpVersion !== void 0) return void 0;
|
|
4798
|
+
if (options?.preserveGovernedPayload || this.config.experimentalIdentity33) return void 0;
|
|
4799
|
+
const negotiated = negotiateAdcpRelease(this.resolvedAdcpVersion, capabilities);
|
|
4800
|
+
if (!negotiated || isNegotiationFailure(negotiated) || negotiated.source === "legacy_v3") return void 0;
|
|
4801
|
+
return negotiated;
|
|
4802
|
+
}
|
|
4803
|
+
/**
|
|
4804
|
+
* Per-call options carrying the negotiated release to the executor, which
|
|
4805
|
+
* stamps it on the version envelope and validates the response against it.
|
|
4806
|
+
*/
|
|
4807
|
+
withNegotiatedRelease(options, negotiated) {
|
|
4808
|
+
if (!negotiated?.downgraded) return options;
|
|
4809
|
+
return { ...options ?? {}, wireAdcpVersion: negotiated.version, negotiatedAdcpVersion: negotiated.version };
|
|
4810
|
+
}
|
|
4811
|
+
/**
|
|
4812
|
+
* Refuse a tool the negotiated release does not define (a 3.2-only task
|
|
4813
|
+
* sent to a 3.1 seller) with a typed error instead of a seller-side schema
|
|
4814
|
+
* or unknown-tool failure. Sellers that list the tool anyway are called.
|
|
4815
|
+
*/
|
|
4816
|
+
assertToolAvailableAtRelease(taskType, negotiated, toolSchemas) {
|
|
4817
|
+
if (!negotiated?.downgraded) return;
|
|
4818
|
+
if (isExternalSchemaRootActive(negotiated.version)) return;
|
|
4819
|
+
if (releaseDefinesTool(taskType, negotiated.version)) return;
|
|
4820
|
+
if (!releaseDefinesTool(taskType, this.resolvedAdcpVersion)) return;
|
|
4821
|
+
if ((toolSchemas ?? this.cachedToolSchemas)?.has(taskType)) return;
|
|
4822
|
+
const suggestion = `Call a task AdCP ${negotiated.version} defines, or reach this seller once it supports AdCP ${toReleasePrecisionWire(this.resolvedAdcpVersion)}.`;
|
|
4823
|
+
throw new ProtocolFeatureUnsupportedError([taskType], [], this.agent.agent_uri, {
|
|
4824
|
+
message: `${taskType} is not defined in AdCP ${negotiated.version}, the newest release this seller supports. No request was sent. ${suggestion}`,
|
|
4825
|
+
suggestion,
|
|
4826
|
+
details: {
|
|
4827
|
+
feature: taskType,
|
|
4828
|
+
required_version: toReleasePrecisionWire(this.resolvedAdcpVersion),
|
|
4829
|
+
capability_path: "adcp.supported_versions",
|
|
4830
|
+
current_version: negotiated.version,
|
|
4831
|
+
tool: taskType
|
|
4832
|
+
}
|
|
4833
|
+
});
|
|
4834
|
+
}
|
|
4835
|
+
/**
|
|
4836
|
+
* Account-key bookkeeping for `accountIdentityDowngrade: 'reduce'`.
|
|
4837
|
+
*
|
|
4838
|
+
* Converting down to an older seller release: claim each reduced key for
|
|
4839
|
+
* the buyer key it came from, and refuse the request when the reduced key
|
|
4840
|
+
* (or an unreduced key the request sends as is) already belongs to a
|
|
4841
|
+
* different buyer key. Without this, two buyer accounts would silently
|
|
4842
|
+
* share one seller account, with merged settings, spend and reads.
|
|
4843
|
+
*
|
|
4844
|
+
* Seller on the client's release: a buyer key that earlier reached this
|
|
4845
|
+
* seller reduced is still addressed by its reduced key, which is the key
|
|
4846
|
+
* the seller's account has, until the caller moves the account to the full
|
|
4847
|
+
* key and calls `client.accounts.clearReduction(key)`.
|
|
4848
|
+
*
|
|
4849
|
+
* Runs only once the account registry is in use or under `'reduce'`.
|
|
4850
|
+
*/
|
|
4851
|
+
async reconcileAccountKeyReductions(taskType, original, adapted, negotiated, driftLogs) {
|
|
4852
|
+
if (this.accountIdentityDowngrade !== "reduce" && !this._accounts) return adapted;
|
|
4853
|
+
const sites = accountKeySites(original);
|
|
4854
|
+
if (sites.length === 0) return adapted;
|
|
4855
|
+
const registry = this.getAccountRegistry();
|
|
4856
|
+
if (negotiated?.downgraded) {
|
|
4857
|
+
if (this.accountIdentityDowngrade !== "reduce") return adapted;
|
|
4858
|
+
const claims = sites.flatMap((site) => {
|
|
4859
|
+
const reduced = naturalAccountKey(valueAt(adapted, site.path));
|
|
4860
|
+
return reduced ? [{ site, account: site.key, reduced, adcpVersion: negotiated.version }] : [];
|
|
4861
|
+
});
|
|
4862
|
+
const conflict = await registry.claimReductions(claims);
|
|
4863
|
+
if (conflict) {
|
|
4864
|
+
this.throwAccountKeyCollision(taskType, claims[conflict.index].site.path, conflict.owner, negotiated.version);
|
|
4865
|
+
}
|
|
4866
|
+
return adapted;
|
|
4867
|
+
}
|
|
4868
|
+
let rewritten = adapted;
|
|
4869
|
+
const pending = [];
|
|
4870
|
+
for (const site of sites) {
|
|
4871
|
+
const reduction = await registry.reductionFor(site.key);
|
|
4872
|
+
if (!reduction) continue;
|
|
4873
|
+
rewritten = withReducedKeyAt(rewritten, site.path, reduction.account);
|
|
4874
|
+
pending.push({
|
|
4875
|
+
path: site.path.join("."),
|
|
4876
|
+
fields: reducedFields(site.key, reduction.account),
|
|
4877
|
+
adcpVersion: reduction.adcpVersion
|
|
4878
|
+
});
|
|
4879
|
+
}
|
|
4880
|
+
if (pending.length > 0) {
|
|
4881
|
+
const inPlace = pending.every((entry) => entry.fields.every((field) => field === "operator_unit"));
|
|
4882
|
+
driftLogs.push({
|
|
4883
|
+
type: "account_identity_migration_pending",
|
|
4884
|
+
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).",
|
|
4885
|
+
accounts: pending,
|
|
4886
|
+
taskName: taskType,
|
|
4887
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4888
|
+
});
|
|
4889
|
+
}
|
|
4890
|
+
return rewritten;
|
|
4891
|
+
}
|
|
4892
|
+
throwAccountKeyCollision(taskType, path, owner, negotiatedVersion) {
|
|
4893
|
+
const field = path.join(".");
|
|
4894
|
+
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.`;
|
|
4895
|
+
throw new ProtocolFeatureUnsupportedError([`${taskType}.${field}`], [], this.agent.agent_uri, {
|
|
4896
|
+
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}`,
|
|
4897
|
+
field,
|
|
4898
|
+
suggestion,
|
|
4899
|
+
details: {
|
|
4900
|
+
feature: `${taskType}.${field}`,
|
|
4901
|
+
reason: "account_identity_collision",
|
|
4902
|
+
current_version: negotiatedVersion,
|
|
4903
|
+
tool: taskType,
|
|
4904
|
+
field,
|
|
4905
|
+
conflicting_account: owner
|
|
4906
|
+
}
|
|
4907
|
+
});
|
|
4908
|
+
}
|
|
4909
|
+
throwFieldsUnsupportedAtRelease(taskType, fields, negotiatedVersion) {
|
|
4910
|
+
const [first] = fields;
|
|
4911
|
+
const paths = fields.map((field) => field.path);
|
|
4912
|
+
const accountMove = paths.some((path) => /(?:^|\.)(?:operator_identity|revision)$/.test(path));
|
|
4913
|
+
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.`;
|
|
4914
|
+
throw new ProtocolFeatureUnsupportedError(
|
|
4915
|
+
paths.map((path) => `${taskType}.${path}`),
|
|
4916
|
+
[],
|
|
4917
|
+
this.agent.agent_uri,
|
|
4918
|
+
{
|
|
4919
|
+
message: `${taskType} ${paths.join(", ")} ${paths.length === 1 ? "requires" : "require"} AdCP ${first.introducedIn}; this seller supports AdCP ${negotiatedVersion}. No request was sent. ${suggestion}`,
|
|
4920
|
+
field: first.path,
|
|
4921
|
+
suggestion,
|
|
4922
|
+
details: {
|
|
4923
|
+
feature: `${taskType}.${first.path}`,
|
|
4924
|
+
required_version: first.introducedIn,
|
|
4925
|
+
capability_path: "adcp.supported_versions",
|
|
4926
|
+
current_version: negotiatedVersion,
|
|
4927
|
+
tool: taskType,
|
|
4928
|
+
field: first.path,
|
|
4929
|
+
fields: paths
|
|
4930
|
+
}
|
|
4931
|
+
}
|
|
4932
|
+
);
|
|
4933
|
+
}
|
|
4934
|
+
throwAccountIdentityUnsupported(taskType, fields, negotiatedVersion) {
|
|
4935
|
+
const [first] = fields;
|
|
4936
|
+
const paths = fields.map((field) => field.path);
|
|
4937
|
+
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.`;
|
|
4938
|
+
throw new ProtocolFeatureUnsupportedError(
|
|
4939
|
+
paths.map((path) => `${taskType}.${path}`),
|
|
4940
|
+
[],
|
|
4941
|
+
this.agent.agent_uri,
|
|
4942
|
+
{
|
|
4943
|
+
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}`,
|
|
4944
|
+
field: first.path,
|
|
4945
|
+
suggestion,
|
|
4946
|
+
details: {
|
|
4947
|
+
feature: `${taskType}.${first.path}`,
|
|
4948
|
+
required_version: first.introducedIn,
|
|
4949
|
+
capability_path: "adcp.supported_versions",
|
|
4950
|
+
current_version: negotiatedVersion,
|
|
4951
|
+
tool: taskType,
|
|
4952
|
+
field: first.path,
|
|
4953
|
+
fields: paths
|
|
4954
|
+
}
|
|
4955
|
+
}
|
|
4956
|
+
);
|
|
4957
|
+
}
|
|
4958
|
+
rememberNegotiatedRelease(operationId, version) {
|
|
4959
|
+
this.negotiatedReleaseByOperation.delete(operationId);
|
|
4960
|
+
this.negotiatedReleaseByOperation.set(operationId, version);
|
|
4961
|
+
if (this.negotiatedReleaseByOperation.size > MAX_REMEMBERED_NEGOTIATED_RELEASES) {
|
|
4962
|
+
const oldest = this.negotiatedReleaseByOperation.keys().next().value;
|
|
4963
|
+
if (oldest !== void 0) this.negotiatedReleaseByOperation.delete(oldest);
|
|
4964
|
+
}
|
|
4965
|
+
}
|
|
4966
|
+
/**
|
|
4967
|
+
* Webhook completions bypass task finalization, so convert their result up
|
|
4968
|
+
* here. The seller's own `adcp_version` names the release it served; an
|
|
4969
|
+
* AdCP 3.0 seller sends none, so fall back to the release this client
|
|
4970
|
+
* negotiated for the operation.
|
|
4971
|
+
*/
|
|
4972
|
+
convertWebhookResultToClientRelease(parsed) {
|
|
4973
|
+
const served = [parsed.result, parsed.envelope].map(
|
|
4974
|
+
(value) => value && typeof value === "object" && typeof value.adcp_version === "string" ? value.adcp_version : void 0
|
|
4975
|
+
).find((version) => version !== void 0);
|
|
4976
|
+
const release = served ?? this.negotiatedReleaseByOperation.get(parsed.metadata.operationId);
|
|
4977
|
+
if (release === void 0 || parsed.result === void 0 || parsed.result === null) return parsed.result;
|
|
4978
|
+
return convertResponse(parsed.metadata.taskType, parsed.result, {
|
|
4979
|
+
fromVersion: release,
|
|
4980
|
+
toVersion: this.resolvedAdcpVersion
|
|
4981
|
+
}).data;
|
|
4982
|
+
}
|
|
4983
|
+
/**
|
|
4984
|
+
* Convert a response served at the negotiated release up to the client's
|
|
4985
|
+
* release. Returns drift entries for each conversion applied.
|
|
4986
|
+
*/
|
|
4987
|
+
convertResponseToClientRelease(taskType, data, negotiatedVersion) {
|
|
4988
|
+
if (negotiatedVersion === void 0 || data === void 0 || data === null) return { data, driftLogs: [] };
|
|
4989
|
+
const translation = convertResponse(taskType, data, {
|
|
4990
|
+
fromVersion: negotiatedVersion,
|
|
4991
|
+
toVersion: this.resolvedAdcpVersion
|
|
4992
|
+
});
|
|
4993
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
4994
|
+
return {
|
|
4995
|
+
data: translation.data,
|
|
4996
|
+
driftLogs: translation.drifts.map((drift) => ({
|
|
4997
|
+
...drift,
|
|
4998
|
+
taskName: taskType,
|
|
4999
|
+
clientVersion: this.resolvedAdcpVersion,
|
|
5000
|
+
targetVersion: negotiatedVersion,
|
|
5001
|
+
timestamp
|
|
5002
|
+
}))
|
|
5003
|
+
};
|
|
5004
|
+
}
|
|
4726
5005
|
/**
|
|
4727
5006
|
* Adapt a request for the detected server wire version and the seller's
|
|
4728
5007
|
* AdCP protocol version. Applies protocol-version adapters (e.g. stripping
|
|
@@ -4732,7 +5011,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
4732
5011
|
* Runs after `detectServerVersion` so capabilities are available and the
|
|
4733
5012
|
* current call's tool schemas can be supplied without cross-tenant caching.
|
|
4734
5013
|
*/
|
|
4735
|
-
adaptRequest(taskType, params, serverVersion, debugLogs, perCallToolSchemas, perCallCapabilities) {
|
|
5014
|
+
adaptRequest(taskType, params, serverVersion, debugLogs, perCallToolSchemas, perCallCapabilities, negotiated) {
|
|
4736
5015
|
if (serverVersion !== "v3") {
|
|
4737
5016
|
throw new VersionUnsupportedError(taskType, "version", "v2", this.agent.agent_uri);
|
|
4738
5017
|
}
|
|
@@ -4769,10 +5048,34 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
4769
5048
|
}
|
|
4770
5049
|
adapted = filtered;
|
|
4771
5050
|
}
|
|
5051
|
+
if (negotiated?.downgraded) {
|
|
5052
|
+
const translation = convertRequest(taskType, adapted, {
|
|
5053
|
+
fromVersion: this.resolvedAdcpVersion,
|
|
5054
|
+
toVersion: negotiated.version,
|
|
5055
|
+
accountIdentity: this.accountIdentityDowngrade
|
|
5056
|
+
});
|
|
5057
|
+
if (translation.identityFields.length > 0 && this.accountIdentityDowngrade === "refuse") {
|
|
5058
|
+
this.throwAccountIdentityUnsupported(taskType, translation.identityFields, negotiated.version);
|
|
5059
|
+
}
|
|
5060
|
+
if (translation.refusedFields.length > 0) {
|
|
5061
|
+
this.throwFieldsUnsupportedAtRelease(taskType, translation.refusedFields, negotiated.version);
|
|
5062
|
+
}
|
|
5063
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
5064
|
+
for (const drift of translation.drifts) {
|
|
5065
|
+
driftLogs.push({
|
|
5066
|
+
...drift,
|
|
5067
|
+
taskName: taskType,
|
|
5068
|
+
clientVersion: this.resolvedAdcpVersion,
|
|
5069
|
+
targetVersion: negotiated.version,
|
|
5070
|
+
timestamp
|
|
5071
|
+
});
|
|
5072
|
+
}
|
|
5073
|
+
return { params: translation.params, driftLogs };
|
|
5074
|
+
}
|
|
4772
5075
|
const adapterKey = resolveAdapterKey(this.resolvedAdcpVersion, perCallCapabilities ?? this.cachedCapabilities);
|
|
4773
5076
|
if (adapterKey) {
|
|
4774
5077
|
const versionAdapter = getVersionAdapter(adapterKey, taskType);
|
|
4775
|
-
if (versionAdapter) {
|
|
5078
|
+
if (versionAdapter?.adaptRequest) {
|
|
4776
5079
|
const result = versionAdapter.adaptRequest(adapted);
|
|
4777
5080
|
adapted = result.params;
|
|
4778
5081
|
if (result.drift) {
|
|
@@ -6391,32 +6694,47 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
6391
6694
|
const serverVersion = await this.detectServerVersion(detectionOptions);
|
|
6392
6695
|
detectedServerVersion = serverVersion;
|
|
6393
6696
|
detectedServerVersionSynthetic = capabilityDiscoveryContext.capabilities?._synthetic;
|
|
6697
|
+
const negotiatedRelease = this.negotiateCallRelease(capabilityDiscoveryContext.capabilities, options);
|
|
6698
|
+
this.assertToolAvailableAtRelease(taskName, negotiatedRelease, capabilityDiscoveryContext.toolSchemas);
|
|
6394
6699
|
this.assertRequestSupportedByTargetVersion(
|
|
6395
6700
|
taskName,
|
|
6396
6701
|
normalizedParams,
|
|
6397
6702
|
capabilityDiscoveryContext.capabilities,
|
|
6398
6703
|
false,
|
|
6399
|
-
serverVersion
|
|
6704
|
+
serverVersion,
|
|
6705
|
+
negotiatedRelease
|
|
6400
6706
|
);
|
|
6401
|
-
const { options:
|
|
6707
|
+
const { options: webhookOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(
|
|
6402
6708
|
taskName,
|
|
6403
6709
|
options,
|
|
6404
6710
|
capabilityDiscoveryContext.capabilities
|
|
6405
6711
|
);
|
|
6712
|
+
const effectiveOptions = this.withNegotiatedRelease(webhookOptions, negotiatedRelease);
|
|
6406
6713
|
const inputSchemaStripLogs = [];
|
|
6407
6714
|
if (options?.preserveGovernedPayload && serverVersion !== "v3") {
|
|
6408
6715
|
throw new ConfigurationError(
|
|
6409
6716
|
"Governed storyboard requests require a v3 seller to preserve the approved payload."
|
|
6410
6717
|
);
|
|
6411
6718
|
}
|
|
6412
|
-
const { params:
|
|
6719
|
+
const { params: convertedParams, driftLogs: adaptDriftLogs } = options?.preserveGovernedPayload ? { params: normalizedParams, driftLogs: [] } : this.adaptRequest(
|
|
6413
6720
|
taskName,
|
|
6414
6721
|
normalizedParams,
|
|
6415
6722
|
serverVersion,
|
|
6416
6723
|
inputSchemaStripLogs,
|
|
6417
6724
|
capabilityDiscoveryContext.toolSchemas,
|
|
6418
|
-
capabilityDiscoveryContext.capabilities
|
|
6725
|
+
capabilityDiscoveryContext.capabilities,
|
|
6726
|
+
negotiatedRelease
|
|
6419
6727
|
);
|
|
6728
|
+
const adaptedParams = options?.preserveGovernedPayload ? convertedParams : await this.reconcileAccountKeyReductions(
|
|
6729
|
+
taskName,
|
|
6730
|
+
normalizedParams,
|
|
6731
|
+
convertedParams,
|
|
6732
|
+
negotiatedRelease,
|
|
6733
|
+
adaptDriftLogs
|
|
6734
|
+
);
|
|
6735
|
+
if (negotiatedRelease?.downgraded && !options?.skipIdempotencyAutoInject && !options?.skipAccountValidation && !options?.skipRequestValidation) {
|
|
6736
|
+
this.executor.validateRequest(taskName, adaptedParams, inputSchemaStripLogs, negotiatedRelease.version);
|
|
6737
|
+
}
|
|
6420
6738
|
const adaptationLogs = [...adaptDriftLogs];
|
|
6421
6739
|
if (webhookDriftLog) adaptationLogs.push(webhookDriftLog);
|
|
6422
6740
|
const deferredClientContext = {
|
|
@@ -6428,6 +6746,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
6428
6746
|
...capabilityDiscoveryContext.capabilities?._synthetic !== void 0 && {
|
|
6429
6747
|
serverVersionSynthetic: capabilityDiscoveryContext.capabilities._synthetic
|
|
6430
6748
|
},
|
|
6749
|
+
...negotiatedRelease?.downgraded && { negotiatedAdcpVersion: negotiatedRelease.version },
|
|
6431
6750
|
productPolicyRequest: productPolicyRequestSnapshot(normalizedParams),
|
|
6432
6751
|
accountRegistry: this.accountRegistryContext(taskName, normalizedParams, effectiveOptions),
|
|
6433
6752
|
...effectiveOptions?.taskId !== void 0 && { optionTaskId: effectiveOptions.taskId },
|
|
@@ -8122,8 +8441,9 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
8122
8441
|
* first (cold-cache) call; the configured-version gate above still catches
|
|
8123
8442
|
* an explicitly pre-3.1 client before validation or network I/O.
|
|
8124
8443
|
*/
|
|
8125
|
-
assertRequestSupportedByTargetVersion(taskName, params, capabilities, canonicalCreativeInvocation = false, serverVersion) {
|
|
8126
|
-
|
|
8444
|
+
assertRequestSupportedByTargetVersion(taskName, params, capabilities, canonicalCreativeInvocation = false, serverVersion, negotiated) {
|
|
8445
|
+
const reducesAccountIdentity = this.accountIdentityDowngrade === "reduce" && negotiated?.downgraded === true;
|
|
8446
|
+
if (taskName === "sync_accounts" && !reducesAccountIdentity) {
|
|
8127
8447
|
const advertisedVersions2 = capabilities?.supportedVersions ?? [];
|
|
8128
8448
|
const servedVersion = capabilities?.servedVersion ?? capabilities?._raw?.adcp_version;
|
|
8129
8449
|
const supports32 = serverVersion !== "v2" && (typeof servedVersion === "string" ? isAdcpVersionAtLeast(servedVersion, "3.2.0-0") : advertisedVersions2.some((version) => isAdcpVersionAtLeast(version, "3.2.0-0")));
|
|
@@ -8293,7 +8613,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
|
|
|
8293
8613
|
}
|
|
8294
8614
|
const supportedVersions = capabilities.supportedVersions;
|
|
8295
8615
|
if (supportedVersions !== void 0 && supportedVersions.length > 0) {
|
|
8296
|
-
if (!isAdcpVersionSupported(this.resolvedAdcpVersion, supportedVersions)) {
|
|
8616
|
+
if (!isAdcpVersionSupported(this.resolvedAdcpVersion, supportedVersions) && this.negotiateCallRelease(capabilities, options) === void 0) {
|
|
8297
8617
|
throw new VersionUnsupportedError(taskType, "version", capabilities.version, this.agent.agent_uri);
|
|
8298
8618
|
}
|
|
8299
8619
|
} else {
|
|
@@ -226,7 +226,7 @@ export declare class TaskExecutor {
|
|
|
226
226
|
* adaptation runs, so the validator sees the same object the caller wrote.
|
|
227
227
|
* Throws in strict, logs to `debugLogs` in warn, no-ops in off.
|
|
228
228
|
*/
|
|
229
|
-
validateRequest(taskName: string, params: unknown, debugLogs?: any[]): void;
|
|
229
|
+
validateRequest(taskName: string, params: unknown, debugLogs?: any[], adcpVersion?: string): void;
|
|
230
230
|
/**
|
|
231
231
|
* Generate webhook URL for protocol-level webhook support
|
|
232
232
|
*/
|
|
@@ -226,7 +226,7 @@ export declare class TaskExecutor {
|
|
|
226
226
|
* adaptation runs, so the validator sees the same object the caller wrote.
|
|
227
227
|
* Throws in strict, logs to `debugLogs` in warn, no-ops in off.
|
|
228
228
|
*/
|
|
229
|
-
validateRequest(taskName: string, params: unknown, debugLogs?: any[]): void;
|
|
229
|
+
validateRequest(taskName: string, params: unknown, debugLogs?: any[], adcpVersion?: string): void;
|
|
230
230
|
/**
|
|
231
231
|
* Generate webhook URL for protocol-level webhook support
|
|
232
232
|
*/
|