@adcp/sdk 16.2.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.
Files changed (161) hide show
  1. package/AGENTS.md +2 -0
  2. package/dist/lib/adapters/version/3.1/assignments.d.mts +12 -0
  3. package/dist/lib/adapters/version/3.1/assignments.d.ts +12 -0
  4. package/dist/lib/adapters/version/3.1/assignments.js +67 -0
  5. package/dist/lib/adapters/version/3.1/assignments.mjs +43 -0
  6. package/dist/lib/adapters/version/3.1/bidding.d.mts +3 -0
  7. package/dist/lib/adapters/version/3.1/bidding.d.ts +3 -0
  8. package/dist/lib/adapters/version/3.1/bidding.js +115 -0
  9. package/dist/lib/adapters/version/3.1/bidding.mjs +90 -0
  10. package/dist/lib/adapters/version/3.1/delivery.d.mts +7 -0
  11. package/dist/lib/adapters/version/3.1/delivery.d.ts +7 -0
  12. package/dist/lib/adapters/version/3.1/delivery.js +78 -0
  13. package/dist/lib/adapters/version/3.1/delivery.mjs +54 -0
  14. package/dist/lib/adapters/version/3.1/index.d.mts +3 -0
  15. package/dist/lib/adapters/version/3.1/index.d.ts +3 -0
  16. package/dist/lib/adapters/version/3.1/index.js +36 -0
  17. package/dist/lib/adapters/version/3.1/index.mjs +12 -0
  18. package/dist/lib/adapters/version/index.d.mts +59 -7
  19. package/dist/lib/adapters/version/index.d.ts +59 -7
  20. package/dist/lib/adapters/version/index.js +51 -2
  21. package/dist/lib/adapters/version/index.mjs +49 -1
  22. package/dist/lib/adapters/version/negotiate.d.mts +71 -0
  23. package/dist/lib/adapters/version/negotiate.d.ts +71 -0
  24. package/dist/lib/adapters/version/negotiate.js +101 -0
  25. package/dist/lib/adapters/version/negotiate.mjs +73 -0
  26. package/dist/lib/adapters/version/schema-projection.d.mts +33 -0
  27. package/dist/lib/adapters/version/schema-projection.d.ts +33 -0
  28. package/dist/lib/adapters/version/schema-projection.js +294 -0
  29. package/dist/lib/adapters/version/schema-projection.mjs +269 -0
  30. package/dist/lib/adapters/version/translate.d.mts +122 -0
  31. package/dist/lib/adapters/version/translate.d.ts +122 -0
  32. package/dist/lib/adapters/version/translate.js +169 -0
  33. package/dist/lib/adapters/version/translate.mjs +141 -0
  34. package/dist/lib/adapters/version/types.d.mts +51 -10
  35. package/dist/lib/adapters/version/types.d.ts +51 -10
  36. package/dist/lib/core/AgentClient.d.mts +1 -1
  37. package/dist/lib/core/AgentClient.d.ts +1 -1
  38. package/dist/lib/core/AgentClient.js +17 -1
  39. package/dist/lib/core/AgentClient.mjs +17 -1
  40. package/dist/lib/core/ConversationTypes.d.mts +7 -0
  41. package/dist/lib/core/ConversationTypes.d.ts +7 -0
  42. package/dist/lib/core/SingleAgentClient.d.mts +82 -0
  43. package/dist/lib/core/SingleAgentClient.d.ts +82 -0
  44. package/dist/lib/core/SingleAgentClient.js +336 -15
  45. package/dist/lib/core/SingleAgentClient.mjs +350 -17
  46. package/dist/lib/core/TaskExecutor.d.mts +1 -1
  47. package/dist/lib/core/TaskExecutor.d.ts +1 -1
  48. package/dist/lib/core/TaskExecutor.js +182 -31
  49. package/dist/lib/core/TaskExecutor.mjs +183 -32
  50. package/dist/lib/core/account-reduction.d.mts +36 -0
  51. package/dist/lib/core/account-reduction.d.ts +36 -0
  52. package/dist/lib/core/account-reduction.js +127 -0
  53. package/dist/lib/core/account-reduction.mjs +98 -0
  54. package/dist/lib/core/buyer-account-registry.d.mts +52 -0
  55. package/dist/lib/core/buyer-account-registry.d.ts +52 -0
  56. package/dist/lib/core/buyer-account-registry.js +142 -6
  57. package/dist/lib/core/buyer-account-registry.mjs +142 -6
  58. package/dist/lib/errors/index.d.mts +34 -0
  59. package/dist/lib/errors/index.d.ts +34 -0
  60. package/dist/lib/errors/index.js +11 -0
  61. package/dist/lib/errors/index.mjs +10 -0
  62. package/dist/lib/index.d.mts +1 -1
  63. package/dist/lib/index.d.ts +1 -1
  64. package/dist/lib/index.js +2 -0
  65. package/dist/lib/index.mjs +2 -0
  66. package/dist/lib/media-buy/action-contracts.js +4 -4
  67. package/dist/lib/media-buy/action-contracts.mjs +4 -4
  68. package/dist/lib/media-buy/action-types.d.mts +3 -2
  69. package/dist/lib/media-buy/action-types.d.ts +3 -2
  70. package/dist/lib/media-buy/compatibility.d.mts +1 -0
  71. package/dist/lib/media-buy/compatibility.d.ts +1 -0
  72. package/dist/lib/media-buy/compatibility.js +33 -10
  73. package/dist/lib/media-buy/compatibility.mjs +32 -14
  74. package/dist/lib/media-buy/update-fields.generated.d.mts +3 -2
  75. package/dist/lib/media-buy/update-fields.generated.d.ts +3 -2
  76. package/dist/lib/negotiation/buyer.js +11 -5
  77. package/dist/lib/negotiation/buyer.mjs +11 -5
  78. package/dist/lib/negotiation/types.d.mts +4 -2
  79. package/dist/lib/negotiation/types.d.ts +4 -2
  80. package/dist/lib/negotiation/verification.d.mts +15 -6
  81. package/dist/lib/negotiation/verification.d.ts +15 -6
  82. package/dist/lib/negotiation/verification.js +146 -27
  83. package/dist/lib/negotiation/verification.mjs +146 -28
  84. package/dist/lib/negotiation/version.d.mts +6 -0
  85. package/dist/lib/negotiation/version.d.ts +6 -0
  86. package/dist/lib/negotiation/version.js +60 -0
  87. package/dist/lib/negotiation/version.mjs +33 -0
  88. package/dist/lib/registry/types.generated.d.mts +1 -1
  89. package/dist/lib/registry/types.generated.d.ts +1 -1
  90. package/dist/lib/server/create-adcp-server.d.mts +21 -0
  91. package/dist/lib/server/create-adcp-server.d.ts +21 -0
  92. package/dist/lib/server/create-adcp-server.js +98 -16
  93. package/dist/lib/server/create-adcp-server.mjs +84 -2
  94. package/dist/lib/server/error-arm-tools.d.mts +10 -10
  95. package/dist/lib/server/error-arm-tools.d.ts +10 -10
  96. package/dist/lib/server/error-arm-tools.js +22 -58
  97. package/dist/lib/server/error-arm-tools.mjs +30 -65
  98. package/dist/lib/server/index.d.mts +1 -1
  99. package/dist/lib/server/index.d.ts +1 -1
  100. package/dist/lib/storage/interfaces.d.mts +2 -0
  101. package/dist/lib/storage/interfaces.d.ts +2 -0
  102. package/dist/lib/testing/compliance/comply.js +17 -0
  103. package/dist/lib/testing/compliance/comply.mjs +17 -0
  104. package/dist/lib/testing/index.d.mts +3 -0
  105. package/dist/lib/testing/index.d.ts +3 -0
  106. package/dist/lib/testing/index.js +44 -2
  107. package/dist/lib/testing/index.mjs +42 -0
  108. package/dist/lib/testing/storyboard/collection-selection.d.mts +259 -0
  109. package/dist/lib/testing/storyboard/collection-selection.d.ts +259 -0
  110. package/dist/lib/testing/storyboard/collection-selection.js +384 -0
  111. package/dist/lib/testing/storyboard/collection-selection.mjs +348 -0
  112. package/dist/lib/testing/storyboard/context.js +12 -6
  113. package/dist/lib/testing/storyboard/context.mjs +12 -6
  114. package/dist/lib/testing/storyboard/fixture-resolution.d.mts +39 -1
  115. package/dist/lib/testing/storyboard/fixture-resolution.d.ts +39 -1
  116. package/dist/lib/testing/storyboard/fixture-resolution.js +216 -12
  117. package/dist/lib/testing/storyboard/fixture-resolution.mjs +218 -12
  118. package/dist/lib/testing/storyboard/index.d.mts +9 -2
  119. package/dist/lib/testing/storyboard/index.d.ts +9 -2
  120. package/dist/lib/testing/storyboard/index.js +51 -2
  121. package/dist/lib/testing/storyboard/index.mjs +49 -1
  122. package/dist/lib/testing/storyboard/prepared-fixtures.d.mts +471 -0
  123. package/dist/lib/testing/storyboard/prepared-fixtures.d.ts +471 -0
  124. package/dist/lib/testing/storyboard/prepared-fixtures.js +434 -0
  125. package/dist/lib/testing/storyboard/prepared-fixtures.mjs +389 -0
  126. package/dist/lib/testing/storyboard/protected-context.d.mts +127 -0
  127. package/dist/lib/testing/storyboard/protected-context.d.ts +127 -0
  128. package/dist/lib/testing/storyboard/protected-context.js +301 -0
  129. package/dist/lib/testing/storyboard/protected-context.mjs +267 -0
  130. package/dist/lib/testing/storyboard/rejection-hints.js +13 -4
  131. package/dist/lib/testing/storyboard/rejection-hints.mjs +13 -4
  132. package/dist/lib/testing/storyboard/runner.d.mts +24 -0
  133. package/dist/lib/testing/storyboard/runner.d.ts +24 -0
  134. package/dist/lib/testing/storyboard/runner.js +1061 -105
  135. package/dist/lib/testing/storyboard/runner.mjs +1075 -106
  136. package/dist/lib/testing/storyboard/seeding.d.mts +15 -1
  137. package/dist/lib/testing/storyboard/seeding.d.ts +15 -1
  138. package/dist/lib/testing/storyboard/seeding.js +272 -12
  139. package/dist/lib/testing/storyboard/seeding.mjs +274 -13
  140. package/dist/lib/testing/storyboard/types.d.mts +207 -10
  141. package/dist/lib/testing/storyboard/types.d.ts +207 -10
  142. package/dist/lib/testing/storyboard/types.js +1 -1
  143. package/dist/lib/testing/storyboard/types.mjs +1 -1
  144. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  145. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  146. package/dist/lib/testing/storyboard/validations.js +95 -0
  147. package/dist/lib/testing/storyboard/validations.mjs +95 -0
  148. package/dist/lib/validation/bundled-schema-store.js +5 -0
  149. package/dist/lib/validation/bundled-schema-store.mjs +4 -0
  150. package/dist/lib/validation/schema-loader.js +19 -2
  151. package/dist/lib/validation/schema-loader.mjs +22 -3
  152. package/dist/lib/version.d.mts +3 -3
  153. package/dist/lib/version.d.ts +3 -3
  154. package/dist/lib/version.js +3 -3
  155. package/dist/lib/version.mjs +3 -3
  156. package/docs/TYPE-SUMMARY.md +2 -2
  157. package/docs/guides/COLLECTION-FIXTURE-FOUNDATION.md +850 -0
  158. package/docs/guides/PROPOSAL-TERMS-VERIFICATION.md +66 -0
  159. package/docs/llms.txt +4 -3
  160. package/docs/migration-14.x-rc-worksheet.md +4 -4
  161. 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 { getVersionAdapter, resolveAdapterKey } from "../adapters/version/index.mjs";
35
- import { isExternalSchemaRootActive, schemaAllowsTopLevelField } from "../validation/schema-loader.mjs";
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();
@@ -2002,6 +2024,19 @@ class SingleAgentClient {
2002
2024
  getWireAdcpVersion() {
2003
2025
  return this.config.wireAdcpVersion ?? this.resolvedAdcpVersion;
2004
2026
  }
2027
+ /** @internal Read only current, auth-scoped actual discovery evidence; never fetch. */
2028
+ getCachedServedAdcpVersion(options) {
2029
+ this.synchronizeCapabilityEvidenceAuthorizationScope();
2030
+ if (this.usesScopedDiscoveryTransport(options)) return void 0;
2031
+ if (this.primedCapabilitiesExpiresAt !== void 0 && this.primedCapabilitiesExpiresAt <= Date.now())
2032
+ return void 0;
2033
+ if (this.config.discoveryCache && this.cachedCapabilities) {
2034
+ const key = this.sharedDiscoveryKey(options);
2035
+ if (this.sharedEvidenceKey !== key || key !== void 0 && this.sharedEvidenceEpoch !== void 0 && this.sharedEvidenceEpoch !== this.sharedInvalidationEpoch(key))
2036
+ return void 0;
2037
+ }
2038
+ return this.cachedCapabilities?._synthetic === false ? this.cachedCapabilities.servedVersion : void 0;
2039
+ }
2005
2040
  /**
2006
2041
  * Ensure MCP endpoint is discovered (lazy initialization)
2007
2042
  *
@@ -3035,7 +3070,11 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
3035
3070
  let externalStatus;
3036
3071
  let forgetPolicyState = false;
3037
3072
  try {
3038
- const canonicalResult = this.canonicalizeWebhookCreativeResult(metadata, parsed.result, canonicalCreativeTask);
3073
+ const canonicalResult = this.canonicalizeWebhookCreativeResult(
3074
+ metadata,
3075
+ this.convertWebhookResultToClientRelease(parsed),
3076
+ canonicalCreativeTask
3077
+ );
3039
3078
  const dedupResult = canonicalResult;
3040
3079
  const dedupMetadata = { ...metadata };
3041
3080
  let webhookResult = dedupResult;
@@ -3616,32 +3655,47 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
3616
3655
  };
3617
3656
  const serverVersion = await this.detectServerVersion(detectionOptions);
3618
3657
  throwIfAborted(options?.signal);
3658
+ const negotiatedRelease = this.negotiateCallRelease(capabilityDiscoveryContext.capabilities, options);
3659
+ this.assertToolAvailableAtRelease(taskType, negotiatedRelease, capabilityDiscoveryContext.toolSchemas);
3619
3660
  this.assertRequestSupportedByTargetVersion(
3620
3661
  taskType,
3621
3662
  normalizedParams,
3622
3663
  capabilityDiscoveryContext.capabilities,
3623
3664
  canonicalCreativeInvocation,
3624
- serverVersion
3665
+ serverVersion,
3666
+ negotiatedRelease
3625
3667
  );
3626
- const { options: effectiveOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(
3668
+ const { options: webhookOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(
3627
3669
  taskType,
3628
3670
  options,
3629
3671
  capabilityDiscoveryContext.capabilities
3630
3672
  );
3673
+ const effectiveOptions = this.withNegotiatedRelease(webhookOptions, negotiatedRelease);
3631
3674
  const inputSchemaStripLogs = [];
3632
3675
  if (options?.preserveGovernedPayload && serverVersion !== "v3") {
3633
3676
  throw new ConfigurationError(
3634
3677
  "Governed storyboard requests require a v3 seller to preserve the approved payload."
3635
3678
  );
3636
3679
  }
3637
- const { params: adaptedParams, driftLogs: adaptDriftLogs } = options?.preserveGovernedPayload ? { params: normalizedParams, driftLogs: [] } : this.adaptRequest(
3680
+ const { params: convertedParams, driftLogs: adaptDriftLogs } = options?.preserveGovernedPayload ? { params: normalizedParams, driftLogs: [] } : this.adaptRequest(
3638
3681
  taskType,
3639
3682
  normalizedParams,
3640
3683
  serverVersion,
3641
3684
  inputSchemaStripLogs,
3642
3685
  capabilityDiscoveryContext.toolSchemas,
3643
- 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
3644
3695
  );
3696
+ if (negotiatedRelease?.downgraded && !options?.skipIdempotencyAutoInject && !options?.skipAccountValidation && !options?.skipRequestValidation) {
3697
+ this.executor.validateRequest(taskType, adaptedParams, inputSchemaStripLogs, negotiatedRelease.version);
3698
+ }
3645
3699
  const adaptationLogs = [...adaptDriftLogs];
3646
3700
  if (webhookDriftLog) adaptationLogs.push(webhookDriftLog);
3647
3701
  const canonicalInputHandler = this.canonicalCreativeInputHandler(
@@ -3659,6 +3713,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
3659
3713
  ...capabilityDiscoveryContext.capabilities?._synthetic !== void 0 && {
3660
3714
  serverVersionSynthetic: capabilityDiscoveryContext.capabilities._synthetic
3661
3715
  },
3716
+ ...negotiatedRelease?.downgraded && { negotiatedAdcpVersion: negotiatedRelease.version },
3662
3717
  productPolicyRequest: productPolicyRequestSnapshot(normalizedParams),
3663
3718
  accountRegistry: this.accountRegistryContext(taskType, normalizedParams, effectiveOptions),
3664
3719
  ...projectionCatalogs !== void 0 && { projectionCatalogs },
@@ -3726,6 +3781,21 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
3726
3781
  }
3727
3782
  };
3728
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
+ }
3729
3799
  const completionHandlerAlreadyPublished = hasCompletionHandlerAlreadyPublished(result);
3730
3800
  const settlementAcknowledger = result[DEFERRED_SETTLEMENT_ACK];
3731
3801
  const taskType = context.taskType;
@@ -4710,6 +4780,228 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4710
4780
  suppressHandler: !policyResult.success
4711
4781
  };
4712
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
+ }
4713
5005
  /**
4714
5006
  * Adapt a request for the detected server wire version and the seller's
4715
5007
  * AdCP protocol version. Applies protocol-version adapters (e.g. stripping
@@ -4719,7 +5011,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4719
5011
  * Runs after `detectServerVersion` so capabilities are available and the
4720
5012
  * current call's tool schemas can be supplied without cross-tenant caching.
4721
5013
  */
4722
- adaptRequest(taskType, params, serverVersion, debugLogs, perCallToolSchemas, perCallCapabilities) {
5014
+ adaptRequest(taskType, params, serverVersion, debugLogs, perCallToolSchemas, perCallCapabilities, negotiated) {
4723
5015
  if (serverVersion !== "v3") {
4724
5016
  throw new VersionUnsupportedError(taskType, "version", "v2", this.agent.agent_uri);
4725
5017
  }
@@ -4756,10 +5048,34 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4756
5048
  }
4757
5049
  adapted = filtered;
4758
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
+ }
4759
5075
  const adapterKey = resolveAdapterKey(this.resolvedAdcpVersion, perCallCapabilities ?? this.cachedCapabilities);
4760
5076
  if (adapterKey) {
4761
5077
  const versionAdapter = getVersionAdapter(adapterKey, taskType);
4762
- if (versionAdapter) {
5078
+ if (versionAdapter?.adaptRequest) {
4763
5079
  const result = versionAdapter.adaptRequest(adapted);
4764
5080
  adapted = result.params;
4765
5081
  if (result.drift) {
@@ -6378,32 +6694,47 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
6378
6694
  const serverVersion = await this.detectServerVersion(detectionOptions);
6379
6695
  detectedServerVersion = serverVersion;
6380
6696
  detectedServerVersionSynthetic = capabilityDiscoveryContext.capabilities?._synthetic;
6697
+ const negotiatedRelease = this.negotiateCallRelease(capabilityDiscoveryContext.capabilities, options);
6698
+ this.assertToolAvailableAtRelease(taskName, negotiatedRelease, capabilityDiscoveryContext.toolSchemas);
6381
6699
  this.assertRequestSupportedByTargetVersion(
6382
6700
  taskName,
6383
6701
  normalizedParams,
6384
6702
  capabilityDiscoveryContext.capabilities,
6385
6703
  false,
6386
- serverVersion
6704
+ serverVersion,
6705
+ negotiatedRelease
6387
6706
  );
6388
- const { options: effectiveOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(
6707
+ const { options: webhookOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(
6389
6708
  taskName,
6390
6709
  options,
6391
6710
  capabilityDiscoveryContext.capabilities
6392
6711
  );
6712
+ const effectiveOptions = this.withNegotiatedRelease(webhookOptions, negotiatedRelease);
6393
6713
  const inputSchemaStripLogs = [];
6394
6714
  if (options?.preserveGovernedPayload && serverVersion !== "v3") {
6395
6715
  throw new ConfigurationError(
6396
6716
  "Governed storyboard requests require a v3 seller to preserve the approved payload."
6397
6717
  );
6398
6718
  }
6399
- const { params: adaptedParams, driftLogs: adaptDriftLogs } = options?.preserveGovernedPayload ? { params: normalizedParams, driftLogs: [] } : this.adaptRequest(
6719
+ const { params: convertedParams, driftLogs: adaptDriftLogs } = options?.preserveGovernedPayload ? { params: normalizedParams, driftLogs: [] } : this.adaptRequest(
6400
6720
  taskName,
6401
6721
  normalizedParams,
6402
6722
  serverVersion,
6403
6723
  inputSchemaStripLogs,
6404
6724
  capabilityDiscoveryContext.toolSchemas,
6405
- capabilityDiscoveryContext.capabilities
6725
+ capabilityDiscoveryContext.capabilities,
6726
+ negotiatedRelease
6727
+ );
6728
+ const adaptedParams = options?.preserveGovernedPayload ? convertedParams : await this.reconcileAccountKeyReductions(
6729
+ taskName,
6730
+ normalizedParams,
6731
+ convertedParams,
6732
+ negotiatedRelease,
6733
+ adaptDriftLogs
6406
6734
  );
6735
+ if (negotiatedRelease?.downgraded && !options?.skipIdempotencyAutoInject && !options?.skipAccountValidation && !options?.skipRequestValidation) {
6736
+ this.executor.validateRequest(taskName, adaptedParams, inputSchemaStripLogs, negotiatedRelease.version);
6737
+ }
6407
6738
  const adaptationLogs = [...adaptDriftLogs];
6408
6739
  if (webhookDriftLog) adaptationLogs.push(webhookDriftLog);
6409
6740
  const deferredClientContext = {
@@ -6415,6 +6746,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
6415
6746
  ...capabilityDiscoveryContext.capabilities?._synthetic !== void 0 && {
6416
6747
  serverVersionSynthetic: capabilityDiscoveryContext.capabilities._synthetic
6417
6748
  },
6749
+ ...negotiatedRelease?.downgraded && { negotiatedAdcpVersion: negotiatedRelease.version },
6418
6750
  productPolicyRequest: productPolicyRequestSnapshot(normalizedParams),
6419
6751
  accountRegistry: this.accountRegistryContext(taskName, normalizedParams, effectiveOptions),
6420
6752
  ...effectiveOptions?.taskId !== void 0 && { optionTaskId: effectiveOptions.taskId },
@@ -8109,8 +8441,9 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
8109
8441
  * first (cold-cache) call; the configured-version gate above still catches
8110
8442
  * an explicitly pre-3.1 client before validation or network I/O.
8111
8443
  */
8112
- assertRequestSupportedByTargetVersion(taskName, params, capabilities, canonicalCreativeInvocation = false, serverVersion) {
8113
- if (taskName === "sync_accounts") {
8444
+ assertRequestSupportedByTargetVersion(taskName, params, capabilities, canonicalCreativeInvocation = false, serverVersion, negotiated) {
8445
+ const reducesAccountIdentity = this.accountIdentityDowngrade === "reduce" && negotiated?.downgraded === true;
8446
+ if (taskName === "sync_accounts" && !reducesAccountIdentity) {
8114
8447
  const advertisedVersions2 = capabilities?.supportedVersions ?? [];
8115
8448
  const servedVersion = capabilities?.servedVersion ?? capabilities?._raw?.adcp_version;
8116
8449
  const supports32 = serverVersion !== "v2" && (typeof servedVersion === "string" ? isAdcpVersionAtLeast(servedVersion, "3.2.0-0") : advertisedVersions2.some((version) => isAdcpVersionAtLeast(version, "3.2.0-0")));
@@ -8280,7 +8613,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
8280
8613
  }
8281
8614
  const supportedVersions = capabilities.supportedVersions;
8282
8615
  if (supportedVersions !== void 0 && supportedVersions.length > 0) {
8283
- if (!isAdcpVersionSupported(this.resolvedAdcpVersion, supportedVersions)) {
8616
+ if (!isAdcpVersionSupported(this.resolvedAdcpVersion, supportedVersions) && this.negotiateCallRelease(capabilities, options) === void 0) {
8284
8617
  throw new VersionUnsupportedError(taskType, "version", capabilities.version, this.agent.agent_uri);
8285
8618
  }
8286
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
  */