@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
@@ -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)();
@@ -1905,6 +1915,19 @@ class SingleAgentClient {
1905
1915
  getWireAdcpVersion() {
1906
1916
  return this.config.wireAdcpVersion ?? this.resolvedAdcpVersion;
1907
1917
  }
1918
+ /** @internal Read only current, auth-scoped actual discovery evidence; never fetch. */
1919
+ getCachedServedAdcpVersion(options) {
1920
+ this.synchronizeCapabilityEvidenceAuthorizationScope();
1921
+ if (this.usesScopedDiscoveryTransport(options)) return void 0;
1922
+ if (this.primedCapabilitiesExpiresAt !== void 0 && this.primedCapabilitiesExpiresAt <= Date.now())
1923
+ return void 0;
1924
+ if (this.config.discoveryCache && this.cachedCapabilities) {
1925
+ const key = this.sharedDiscoveryKey(options);
1926
+ if (this.sharedEvidenceKey !== key || key !== void 0 && this.sharedEvidenceEpoch !== void 0 && this.sharedEvidenceEpoch !== this.sharedInvalidationEpoch(key))
1927
+ return void 0;
1928
+ }
1929
+ return this.cachedCapabilities?._synthetic === false ? this.cachedCapabilities.servedVersion : void 0;
1930
+ }
1908
1931
  /**
1909
1932
  * Ensure MCP endpoint is discovered (lazy initialization)
1910
1933
  *
@@ -2938,7 +2961,11 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
2938
2961
  let externalStatus;
2939
2962
  let forgetPolicyState = false;
2940
2963
  try {
2941
- const canonicalResult = this.canonicalizeWebhookCreativeResult(metadata, parsed.result, canonicalCreativeTask);
2964
+ const canonicalResult = this.canonicalizeWebhookCreativeResult(
2965
+ metadata,
2966
+ this.convertWebhookResultToClientRelease(parsed),
2967
+ canonicalCreativeTask
2968
+ );
2942
2969
  const dedupResult = canonicalResult;
2943
2970
  const dedupMetadata = { ...metadata };
2944
2971
  let webhookResult = dedupResult;
@@ -3519,32 +3546,47 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
3519
3546
  };
3520
3547
  const serverVersion = await this.detectServerVersion(detectionOptions);
3521
3548
  (0, import_abort2.throwIfAborted)(options?.signal);
3549
+ const negotiatedRelease = this.negotiateCallRelease(capabilityDiscoveryContext.capabilities, options);
3550
+ this.assertToolAvailableAtRelease(taskType, negotiatedRelease, capabilityDiscoveryContext.toolSchemas);
3522
3551
  this.assertRequestSupportedByTargetVersion(
3523
3552
  taskType,
3524
3553
  normalizedParams,
3525
3554
  capabilityDiscoveryContext.capabilities,
3526
3555
  canonicalCreativeInvocation,
3527
- serverVersion
3556
+ serverVersion,
3557
+ negotiatedRelease
3528
3558
  );
3529
- const { options: effectiveOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(
3559
+ const { options: webhookOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(
3530
3560
  taskType,
3531
3561
  options,
3532
3562
  capabilityDiscoveryContext.capabilities
3533
3563
  );
3564
+ const effectiveOptions = this.withNegotiatedRelease(webhookOptions, negotiatedRelease);
3534
3565
  const inputSchemaStripLogs = [];
3535
3566
  if (options?.preserveGovernedPayload && serverVersion !== "v3") {
3536
3567
  throw new import_errors.ConfigurationError(
3537
3568
  "Governed storyboard requests require a v3 seller to preserve the approved payload."
3538
3569
  );
3539
3570
  }
3540
- const { params: adaptedParams, driftLogs: adaptDriftLogs } = options?.preserveGovernedPayload ? { params: normalizedParams, driftLogs: [] } : this.adaptRequest(
3571
+ const { params: convertedParams, driftLogs: adaptDriftLogs } = options?.preserveGovernedPayload ? { params: normalizedParams, driftLogs: [] } : this.adaptRequest(
3541
3572
  taskType,
3542
3573
  normalizedParams,
3543
3574
  serverVersion,
3544
3575
  inputSchemaStripLogs,
3545
3576
  capabilityDiscoveryContext.toolSchemas,
3546
- capabilityDiscoveryContext.capabilities
3577
+ capabilityDiscoveryContext.capabilities,
3578
+ negotiatedRelease
3579
+ );
3580
+ const adaptedParams = options?.preserveGovernedPayload ? convertedParams : await this.reconcileAccountKeyReductions(
3581
+ taskType,
3582
+ normalizedParams,
3583
+ convertedParams,
3584
+ negotiatedRelease,
3585
+ adaptDriftLogs
3547
3586
  );
3587
+ if (negotiatedRelease?.downgraded && !options?.skipIdempotencyAutoInject && !options?.skipAccountValidation && !options?.skipRequestValidation) {
3588
+ this.executor.validateRequest(taskType, adaptedParams, inputSchemaStripLogs, negotiatedRelease.version);
3589
+ }
3548
3590
  const adaptationLogs = [...adaptDriftLogs];
3549
3591
  if (webhookDriftLog) adaptationLogs.push(webhookDriftLog);
3550
3592
  const canonicalInputHandler = this.canonicalCreativeInputHandler(
@@ -3562,6 +3604,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
3562
3604
  ...capabilityDiscoveryContext.capabilities?._synthetic !== void 0 && {
3563
3605
  serverVersionSynthetic: capabilityDiscoveryContext.capabilities._synthetic
3564
3606
  },
3607
+ ...negotiatedRelease?.downgraded && { negotiatedAdcpVersion: negotiatedRelease.version },
3565
3608
  productPolicyRequest: productPolicyRequestSnapshot(normalizedParams),
3566
3609
  accountRegistry: this.accountRegistryContext(taskType, normalizedParams, effectiveOptions),
3567
3610
  ...projectionCatalogs !== void 0 && { projectionCatalogs },
@@ -3629,6 +3672,21 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
3629
3672
  }
3630
3673
  };
3631
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
+ }
3632
3690
  const completionHandlerAlreadyPublished = (0, import_TaskExecutor.hasCompletionHandlerAlreadyPublished)(result);
3633
3691
  const settlementAcknowledger = result[import_TaskExecutor.DEFERRED_SETTLEMENT_ACK];
3634
3692
  const taskType = context.taskType;
@@ -4613,6 +4671,228 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4613
4671
  suppressHandler: !policyResult.success
4614
4672
  };
4615
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
+ }
4616
4896
  /**
4617
4897
  * Adapt a request for the detected server wire version and the seller's
4618
4898
  * AdCP protocol version. Applies protocol-version adapters (e.g. stripping
@@ -4622,7 +4902,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4622
4902
  * Runs after `detectServerVersion` so capabilities are available and the
4623
4903
  * current call's tool schemas can be supplied without cross-tenant caching.
4624
4904
  */
4625
- adaptRequest(taskType, params, serverVersion, debugLogs, perCallToolSchemas, perCallCapabilities) {
4905
+ adaptRequest(taskType, params, serverVersion, debugLogs, perCallToolSchemas, perCallCapabilities, negotiated) {
4626
4906
  if (serverVersion !== "v3") {
4627
4907
  throw new import_errors.VersionUnsupportedError(taskType, "version", "v2", this.agent.agent_uri);
4628
4908
  }
@@ -4659,10 +4939,34 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4659
4939
  }
4660
4940
  adapted = filtered;
4661
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
+ }
4662
4966
  const adapterKey = (0, import_version2.resolveAdapterKey)(this.resolvedAdcpVersion, perCallCapabilities ?? this.cachedCapabilities);
4663
4967
  if (adapterKey) {
4664
4968
  const versionAdapter = (0, import_version2.getVersionAdapter)(adapterKey, taskType);
4665
- if (versionAdapter) {
4969
+ if (versionAdapter?.adaptRequest) {
4666
4970
  const result = versionAdapter.adaptRequest(adapted);
4667
4971
  adapted = result.params;
4668
4972
  if (result.drift) {
@@ -6281,32 +6585,47 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
6281
6585
  const serverVersion = await this.detectServerVersion(detectionOptions);
6282
6586
  detectedServerVersion = serverVersion;
6283
6587
  detectedServerVersionSynthetic = capabilityDiscoveryContext.capabilities?._synthetic;
6588
+ const negotiatedRelease = this.negotiateCallRelease(capabilityDiscoveryContext.capabilities, options);
6589
+ this.assertToolAvailableAtRelease(taskName, negotiatedRelease, capabilityDiscoveryContext.toolSchemas);
6284
6590
  this.assertRequestSupportedByTargetVersion(
6285
6591
  taskName,
6286
6592
  normalizedParams,
6287
6593
  capabilityDiscoveryContext.capabilities,
6288
6594
  false,
6289
- serverVersion
6595
+ serverVersion,
6596
+ negotiatedRelease
6290
6597
  );
6291
- const { options: effectiveOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(
6598
+ const { options: webhookOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(
6292
6599
  taskName,
6293
6600
  options,
6294
6601
  capabilityDiscoveryContext.capabilities
6295
6602
  );
6603
+ const effectiveOptions = this.withNegotiatedRelease(webhookOptions, negotiatedRelease);
6296
6604
  const inputSchemaStripLogs = [];
6297
6605
  if (options?.preserveGovernedPayload && serverVersion !== "v3") {
6298
6606
  throw new import_errors.ConfigurationError(
6299
6607
  "Governed storyboard requests require a v3 seller to preserve the approved payload."
6300
6608
  );
6301
6609
  }
6302
- const { params: adaptedParams, driftLogs: adaptDriftLogs } = options?.preserveGovernedPayload ? { params: normalizedParams, driftLogs: [] } : this.adaptRequest(
6610
+ const { params: convertedParams, driftLogs: adaptDriftLogs } = options?.preserveGovernedPayload ? { params: normalizedParams, driftLogs: [] } : this.adaptRequest(
6303
6611
  taskName,
6304
6612
  normalizedParams,
6305
6613
  serverVersion,
6306
6614
  inputSchemaStripLogs,
6307
6615
  capabilityDiscoveryContext.toolSchemas,
6308
- capabilityDiscoveryContext.capabilities
6616
+ capabilityDiscoveryContext.capabilities,
6617
+ negotiatedRelease
6618
+ );
6619
+ const adaptedParams = options?.preserveGovernedPayload ? convertedParams : await this.reconcileAccountKeyReductions(
6620
+ taskName,
6621
+ normalizedParams,
6622
+ convertedParams,
6623
+ negotiatedRelease,
6624
+ adaptDriftLogs
6309
6625
  );
6626
+ if (negotiatedRelease?.downgraded && !options?.skipIdempotencyAutoInject && !options?.skipAccountValidation && !options?.skipRequestValidation) {
6627
+ this.executor.validateRequest(taskName, adaptedParams, inputSchemaStripLogs, negotiatedRelease.version);
6628
+ }
6310
6629
  const adaptationLogs = [...adaptDriftLogs];
6311
6630
  if (webhookDriftLog) adaptationLogs.push(webhookDriftLog);
6312
6631
  const deferredClientContext = {
@@ -6318,6 +6637,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
6318
6637
  ...capabilityDiscoveryContext.capabilities?._synthetic !== void 0 && {
6319
6638
  serverVersionSynthetic: capabilityDiscoveryContext.capabilities._synthetic
6320
6639
  },
6640
+ ...negotiatedRelease?.downgraded && { negotiatedAdcpVersion: negotiatedRelease.version },
6321
6641
  productPolicyRequest: productPolicyRequestSnapshot(normalizedParams),
6322
6642
  accountRegistry: this.accountRegistryContext(taskName, normalizedParams, effectiveOptions),
6323
6643
  ...effectiveOptions?.taskId !== void 0 && { optionTaskId: effectiveOptions.taskId },
@@ -8012,8 +8332,9 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
8012
8332
  * first (cold-cache) call; the configured-version gate above still catches
8013
8333
  * an explicitly pre-3.1 client before validation or network I/O.
8014
8334
  */
8015
- assertRequestSupportedByTargetVersion(taskName, params, capabilities, canonicalCreativeInvocation = false, serverVersion) {
8016
- if (taskName === "sync_accounts") {
8335
+ assertRequestSupportedByTargetVersion(taskName, params, capabilities, canonicalCreativeInvocation = false, serverVersion, negotiated) {
8336
+ const reducesAccountIdentity = this.accountIdentityDowngrade === "reduce" && negotiated?.downgraded === true;
8337
+ if (taskName === "sync_accounts" && !reducesAccountIdentity) {
8017
8338
  const advertisedVersions2 = capabilities?.supportedVersions ?? [];
8018
8339
  const servedVersion = capabilities?.servedVersion ?? capabilities?._raw?.adcp_version;
8019
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")));
@@ -8183,7 +8504,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
8183
8504
  }
8184
8505
  const supportedVersions = capabilities.supportedVersions;
8185
8506
  if (supportedVersions !== void 0 && supportedVersions.length > 0) {
8186
- 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) {
8187
8508
  throw new import_errors.VersionUnsupportedError(taskType, "version", capabilities.version, this.agent.agent_uri);
8188
8509
  }
8189
8510
  } else {