@adcp/sdk 14.0.0-rc.50 → 14.0.0-rc.52

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 (57) hide show
  1. package/README.md +1 -1
  2. package/dist/lib/client/core.d.mts +1 -0
  3. package/dist/lib/client/core.d.ts +1 -0
  4. package/dist/lib/core/AgentClient.d.mts +4 -1
  5. package/dist/lib/core/AgentClient.d.ts +4 -1
  6. package/dist/lib/core/AgentClient.js +8 -0
  7. package/dist/lib/core/AgentClient.mjs +8 -0
  8. package/dist/lib/core/SingleAgentClient.d.mts +10 -1
  9. package/dist/lib/core/SingleAgentClient.d.ts +10 -1
  10. package/dist/lib/core/SingleAgentClient.js +238 -34
  11. package/dist/lib/core/SingleAgentClient.mjs +240 -35
  12. package/dist/lib/core/TaskExecutor.js +1 -1
  13. package/dist/lib/core/TaskExecutor.mjs +1 -1
  14. package/dist/lib/core/account-resolution.d.mts +24 -0
  15. package/dist/lib/core/account-resolution.d.ts +24 -0
  16. package/dist/lib/core/account-resolution.js +79 -0
  17. package/dist/lib/core/account-resolution.mjs +54 -0
  18. package/dist/lib/errors/index.d.mts +27 -0
  19. package/dist/lib/errors/index.d.ts +27 -0
  20. package/dist/lib/errors/index.js +56 -0
  21. package/dist/lib/errors/index.mjs +52 -0
  22. package/dist/lib/index.d.mts +4 -3
  23. package/dist/lib/index.d.ts +4 -3
  24. package/dist/lib/index.js +10 -0
  25. package/dist/lib/index.mjs +10 -0
  26. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  27. package/dist/lib/testing/scenarios/media-buy.d.mts +2 -1
  28. package/dist/lib/testing/scenarios/media-buy.d.ts +2 -1
  29. package/dist/lib/testing/scenarios/media-buy.js +31 -3
  30. package/dist/lib/testing/scenarios/media-buy.mjs +31 -3
  31. package/dist/lib/testing/storyboard/runner.d.mts +2 -1
  32. package/dist/lib/testing/storyboard/runner.d.ts +2 -1
  33. package/dist/lib/testing/storyboard/runner.js +144 -15
  34. package/dist/lib/testing/storyboard/runner.mjs +146 -16
  35. package/dist/lib/testing/storyboard/task-map.d.mts +1 -0
  36. package/dist/lib/testing/storyboard/task-map.d.ts +1 -0
  37. package/dist/lib/testing/storyboard/task-map.js +8 -2
  38. package/dist/lib/testing/storyboard/task-map.mjs +8 -2
  39. package/dist/lib/testing/storyboard/types.d.mts +10 -3
  40. package/dist/lib/testing/storyboard/types.d.ts +10 -3
  41. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  42. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  43. package/dist/lib/utils/capabilities.d.mts +13 -0
  44. package/dist/lib/utils/capabilities.d.ts +13 -0
  45. package/dist/lib/utils/capabilities.js +17 -0
  46. package/dist/lib/utils/capabilities.mjs +16 -0
  47. package/dist/lib/utils/request-normalizer.js +1 -1
  48. package/dist/lib/utils/request-normalizer.mjs +1 -1
  49. package/dist/lib/version.d.mts +3 -3
  50. package/dist/lib/version.d.ts +3 -3
  51. package/dist/lib/version.js +3 -3
  52. package/dist/lib/version.mjs +3 -3
  53. package/docs/TYPE-SUMMARY.md +2 -2
  54. package/docs/guides/BUYER-QUICKSTART-3.2.md +28 -1
  55. package/docs/llms.txt +2 -2
  56. package/docs/migration-14.x-rc-worksheet.md +4 -4
  57. package/package.json +1 -1
@@ -119,8 +119,11 @@ import {
119
119
  resolveFeature,
120
120
  listDeclaredFeatures,
121
121
  TASK_FEATURE_MAP,
122
- assertValidIdempotencyReplayTtlSeconds
122
+ assertValidIdempotencyReplayTtlSeconds,
123
+ supportsBuyingMode
123
124
  } from "../utils/capabilities.mjs";
125
+ import { AccountPendingApprovalError, AccountRequiredError, UnsupportedBuyingModeError } from "../errors/index.mjs";
126
+ import { sameCountrySet, selectListedAccount } from "./account-resolution.mjs";
124
127
  import { normalizeRequestParams } from "../utils/request-normalizer.mjs";
125
128
  import { globalAsyncLocalStorage } from "../utils/global-async-local-storage.mjs";
126
129
  import { validateUserAgent } from "../utils/validate-user-agent.mjs";
@@ -310,12 +313,18 @@ function canonicalAccountRoutingSnapshot(value) {
310
313
  if (typeof sourceBrand.domain !== "string") return void 0;
311
314
  const brand = Object.freeze({
312
315
  domain: sourceBrand.domain,
313
- ...typeof sourceBrand.brand_id === "string" ? { brand_id: sourceBrand.brand_id } : {}
316
+ ...typeof sourceBrand.brand_id === "string" ? { brand_id: sourceBrand.brand_id } : {},
317
+ ...Array.isArray(sourceBrand.countries) && sourceBrand.countries.every((country) => typeof country === "string") ? { countries: Object.freeze([...sourceBrand.countries].sort()) } : {}
314
318
  });
319
+ const operatorUnit = account.operator_unit;
320
+ const unitId = operatorUnit && typeof operatorUnit === "object" && !Array.isArray(operatorUnit) ? operatorUnit.id : void 0;
315
321
  return Object.freeze({
316
322
  brand,
317
323
  operator: account.operator,
318
- ...typeof account.sandbox === "boolean" ? { sandbox: account.sandbox } : {}
324
+ ...typeof unitId === "string" ? { operator_unit: Object.freeze({ id: unitId }) } : {},
325
+ ...typeof account.currency === "string" ? { currency: account.currency } : {},
326
+ ...typeof account.timezone === "string" ? { timezone: account.timezone } : {},
327
+ sandbox: account.sandbox === true
319
328
  });
320
329
  }
321
330
  function canonicalOptionRefRoutingSnapshot(value) {
@@ -575,6 +584,13 @@ function snapshotTaskOptions(options) {
575
584
  }
576
585
  };
577
586
  }
587
+ function assertRawMethodForGovernedPayload(taskType, options) {
588
+ if (options?.preserveGovernedPayload) {
589
+ throw new ConfigurationError(
590
+ `Governed ${taskType} requires the raw legacy task method; canonical creative projection can reshape approved arguments.`
591
+ );
592
+ }
593
+ }
578
594
  const PRIMARY_ADCP_TASK_NAMES = {
579
595
  get_products: true,
580
596
  list_products: true,
@@ -2956,17 +2972,17 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
2956
2972
  async executeAndHandleWithinDeadline(taskType, handlerName, params, inputHandler, options, transformCompletedResponse, legacyFormatConverter, canonicalRequest, projectionCatalogs) {
2957
2973
  throwIfAborted(options?.signal);
2958
2974
  const canonicalCreativeInvocation = CANONICAL_CREATIVE_ACTIVITY_TASKS.has(taskType) || canonicalRequest !== void 0;
2959
- let normalizedParams = normalizeRequestParams(taskType, params, {
2975
+ let normalizedParams = options?.preserveGovernedPayload ? params : normalizeRequestParams(taskType, params, {
2960
2976
  skipIdempotencyAutoInject: options?.skipIdempotencyAutoInject,
2961
2977
  skipAccountValidation: options?.skipAccountValidation
2962
2978
  });
2963
2979
  if (!options?.skipRequestValidation) assertNoPrincipalIdentityInput(taskType, normalizedParams);
2964
2980
  this.assertRequestSupportedByConfiguredVersion(taskType, normalizedParams, options, canonicalCreativeInvocation);
2965
2981
  this.assertDurablePropertyListCredentialSupported(taskType, normalizedParams);
2966
- if (!options?.skipIdempotencyAutoInject && requestUsesIdempotency(taskType, normalizedParams) && normalizedParams && typeof normalizedParams === "object" && !normalizedParams.idempotency_key) {
2982
+ if (!options?.skipIdempotencyAutoInject && !options?.preserveGovernedPayload && requestUsesIdempotency(taskType, normalizedParams) && normalizedParams && typeof normalizedParams === "object" && !normalizedParams.idempotency_key) {
2967
2983
  normalizedParams = { ...normalizedParams, idempotency_key: generateIdempotencyKey() };
2968
2984
  }
2969
- if (!options?.skipIdempotencyAutoInject && !options?.skipAccountValidation && !options?.skipRequestValidation && !isExternalSchemaRootActive(this.resolvedAdcpVersion)) {
2985
+ if (!options?.skipIdempotencyAutoInject && !options?.skipAccountValidation && !options?.skipRequestValidation && !options?.preserveGovernedPayload && !isExternalSchemaRootActive(this.resolvedAdcpVersion)) {
2970
2986
  this.validateRequest(taskType, normalizedParams);
2971
2987
  }
2972
2988
  await this.validateTaskFeatures(taskType, options);
@@ -2982,7 +2998,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
2982
2998
  }
2983
2999
  const agent = await this.ensureEndpointDiscovered(options);
2984
3000
  throwIfAborted(options?.signal);
2985
- if (!options?.skipIdempotencyAutoInject && !options?.skipAccountValidation && !options?.skipRequestValidation) {
3001
+ if (!options?.skipIdempotencyAutoInject && !options?.skipAccountValidation && !options?.skipRequestValidation && !options?.preserveGovernedPayload) {
2986
3002
  this.executor.validateRequest(taskType, normalizedParams);
2987
3003
  }
2988
3004
  const capabilityDiscoveryContext = {};
@@ -3005,7 +3021,12 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
3005
3021
  capabilityDiscoveryContext.capabilities
3006
3022
  );
3007
3023
  const inputSchemaStripLogs = [];
3008
- const { params: adaptedParams, driftLogs: adaptDriftLogs } = this.adaptRequest(
3024
+ if (options?.preserveGovernedPayload && serverVersion !== "v3") {
3025
+ throw new ConfigurationError(
3026
+ "Governed storyboard requests require a v3 seller to preserve the approved payload."
3027
+ );
3028
+ }
3029
+ const { params: adaptedParams, driftLogs: adaptDriftLogs } = options?.preserveGovernedPayload ? { params: normalizedParams, driftLogs: [] } : this.adaptRequest(
3009
3030
  taskType,
3010
3031
  normalizedParams,
3011
3032
  serverVersion,
@@ -4115,28 +4136,67 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4115
4136
  legacyFormatConverter,
4116
4137
  projectionCatalogs ?? this.config.projectionCatalogs
4117
4138
  );
4118
- const account = canonicalAccountRoutingSnapshot(params.account);
4119
- return this.executeAndHandle(
4120
- "get_products",
4121
- "onGetProductsStatusChange",
4122
- params,
4123
- inputHandler,
4124
- taskOptions,
4125
- (data) => {
4126
- const { response, diagnostics } = toCanonicalOnlyResponse(data, {
4127
- legacyFormatConverter: effectiveLegacyFormatConverter,
4128
- projectionCatalogs: projectionCatalogs ?? this.config.projectionCatalogs
4129
- });
4130
- const authoritativeProducts = Array.isArray(data.products) ? data.products : response.products;
4131
- this.rememberCanonicalProductRoutes(response.products, account, authoritativeProducts);
4132
- const { _message: _dropLegacyMessage, ...canonical } = response;
4133
- void _dropLegacyMessage;
4134
- return { ...canonical, projection: { diagnostics } };
4135
- },
4136
- effectiveLegacyFormatConverter,
4137
- void 0,
4138
- projectionCatalogs
4139
- );
4139
+ return withTaskDeadline(snapshotTaskOptions(taskOptions), async (effectiveOptions) => {
4140
+ this.assertRequestSupportedByConfiguredVersion("get_products", params, effectiveOptions);
4141
+ let request = params;
4142
+ const legacyAccountId = request.account_id;
4143
+ const accountReference = request.account ?? (typeof legacyAccountId === "string" && legacyAccountId ? { account_id: legacyAccountId } : void 0);
4144
+ if (!request.account && typeof legacyAccountId === "string" && legacyAccountId) {
4145
+ const { account_id: _legacyAccountId, ...rest } = request;
4146
+ void _legacyAccountId;
4147
+ request = { ...rest, account: { account_id: legacyAccountId } };
4148
+ }
4149
+ if (!effectiveOptions.skipRequestValidation && this.config.validateFeatures !== false) {
4150
+ const capabilities = await this.getCapabilities(effectiveOptions);
4151
+ const sellerDeclares31 = capabilities.servedVersion !== void 0 ? !isPre31AdcpVersion(capabilities.servedVersion) : capabilities.supportedVersions?.some((version) => !isPre31AdcpVersion(version)) === true;
4152
+ const sellerDeclaresBuyingModes = Array.isArray(
4153
+ capabilities._raw?.media_buy?.buying_modes
4154
+ );
4155
+ if (!capabilities._synthetic) {
4156
+ const effectiveMode = request.buying_mode ?? (request.brief ? "brief" : void 0);
4157
+ if (effectiveMode && (sellerDeclares31 || sellerDeclaresBuyingModes) && !supportsBuyingMode(capabilities, effectiveMode)) {
4158
+ throw new UnsupportedBuyingModeError(effectiveMode, capabilities.buyingModes ?? ["brief"]);
4159
+ }
4160
+ if (!effectiveOptions.skipAccountValidation && !accountReference && capabilities.account?.requiredForProducts) {
4161
+ throw new AccountRequiredError(
4162
+ capabilities.account.requireOperatorAuth ? "explicit" : "implicit",
4163
+ "get_products"
4164
+ );
4165
+ }
4166
+ }
4167
+ if (!request.buying_mode && !request.brief) {
4168
+ const legacySeller = capabilities._synthetic || !sellerDeclares31 && !sellerDeclaresBuyingModes;
4169
+ if (!legacySeller && !supportsBuyingMode(capabilities, "wholesale")) {
4170
+ throw new UnsupportedBuyingModeError(void 0, capabilities.buyingModes ?? ["brief"]);
4171
+ }
4172
+ }
4173
+ }
4174
+ if (!request.buying_mode && !request.brief) {
4175
+ request = { ...request, buying_mode: "wholesale" };
4176
+ }
4177
+ const account = canonicalAccountRoutingSnapshot(accountReference);
4178
+ return this.executeAndHandle(
4179
+ "get_products",
4180
+ "onGetProductsStatusChange",
4181
+ request,
4182
+ inputHandler,
4183
+ effectiveOptions,
4184
+ (data) => {
4185
+ const { response, diagnostics } = toCanonicalOnlyResponse(data, {
4186
+ legacyFormatConverter: effectiveLegacyFormatConverter,
4187
+ projectionCatalogs: projectionCatalogs ?? this.config.projectionCatalogs
4188
+ });
4189
+ const authoritativeProducts = Array.isArray(data.products) ? data.products : response.products;
4190
+ this.rememberCanonicalProductRoutes(response.products, account, authoritativeProducts);
4191
+ const { _message: _dropLegacyMessage, ...canonical } = response;
4192
+ void _dropLegacyMessage;
4193
+ return { ...canonical, projection: { diagnostics } };
4194
+ },
4195
+ effectiveLegacyFormatConverter,
4196
+ void 0,
4197
+ projectionCatalogs
4198
+ );
4199
+ });
4140
4200
  }
4141
4201
  /** Discover products through the compact AdCP 3.2 catalog task. */
4142
4202
  async listProducts(params, inputHandler, options) {
@@ -4150,11 +4210,14 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4150
4210
  }
4151
4211
  /** @deprecated Explicit raw-wire escape hatch for migration tooling. */
4152
4212
  async getProductsLegacy(params, inputHandler, options) {
4153
- return this.executeTaskUnprojected("get_products", params, inputHandler, options);
4213
+ const request = params.buying_mode || params.brief ? params : { ...params, buying_mode: "wholesale" };
4214
+ return this.executeTaskUnprojected("get_products", request, inputHandler, options);
4154
4215
  }
4155
4216
  /** @internal Run final legacy get_products adaptation before an application-owned atomic mutation claim. */
4156
4217
  async getProductsLegacyWithPreDispatch(params, beforeDispatch, inputHandler, options) {
4157
- const requestSnapshot = structuredClone(params);
4218
+ const requestSnapshot = structuredClone(
4219
+ params.buying_mode || params.brief ? params : { ...params, buying_mode: "wholesale" }
4220
+ );
4158
4221
  return this.executeTaskUnprojected(
4159
4222
  "get_products",
4160
4223
  requestSnapshot,
@@ -4193,6 +4256,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4193
4256
  * @param options - Task execution options
4194
4257
  */
4195
4258
  async createMediaBuy(params, inputHandler, options) {
4259
+ assertRawMethodForGovernedPayload("create_media_buy", options);
4196
4260
  this.assertDurableProjectionOverrideSupported(options?.legacyFormatConverter);
4197
4261
  const requestSnapshot = structuredCloneWithLegacyCreativeMetadata(params);
4198
4262
  const projectionCatalogs = options?.projectionCatalogs ? structuredClone(options.projectionCatalogs) : void 0;
@@ -4322,6 +4386,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4322
4386
  * @param options - Task execution options
4323
4387
  */
4324
4388
  async updateMediaBuy(params, inputHandler, options) {
4389
+ assertRawMethodForGovernedPayload("update_media_buy", options);
4325
4390
  this.assertDurableProjectionOverrideSupported(options?.legacyFormatConverter);
4326
4391
  const requestSnapshot = structuredCloneWithLegacyCreativeMetadata(params);
4327
4392
  const projectionCatalogs = options?.projectionCatalogs ? structuredClone(options.projectionCatalogs) : void 0;
@@ -4390,6 +4455,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4390
4455
  * @param options - Task execution options
4391
4456
  */
4392
4457
  async syncCreatives(params, inputHandler, options) {
4458
+ assertRawMethodForGovernedPayload("sync_creatives", options);
4393
4459
  this.assertDurableProjectionOverrideSupported(
4394
4460
  options?.creativeFormatProjection?.legacyFormatConverter ?? options?.legacyFormatConverter
4395
4461
  );
@@ -4848,6 +4914,140 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4848
4914
  options
4849
4915
  );
4850
4916
  }
4917
+ /**
4918
+ * Resolve an account for the authenticated caller. Explicit-account sellers
4919
+ * are discovered with list_accounts; implicit-account sellers are registered
4920
+ * with sync_accounts and addressed by their natural key.
4921
+ */
4922
+ async resolveAccount(hints = {}, options, onTaskResult) {
4923
+ return withTaskDeadline(snapshotTaskOptions(options), async (effectiveOptions) => {
4924
+ const capabilities = await this.getCapabilities(effectiveOptions);
4925
+ if (capabilities._synthetic || !capabilities.account) {
4926
+ throw new AccountRequiredError(
4927
+ "explicit",
4928
+ "resolveAccount",
4929
+ "The seller did not provide an account contract. Probe get_adcp_capabilities or pass an account reference explicitly."
4930
+ );
4931
+ }
4932
+ if (capabilities.account.requireOperatorAuth) {
4933
+ const accounts = [];
4934
+ const seenCursors = /* @__PURE__ */ new Set();
4935
+ let cursor;
4936
+ for (let page = 0; page < 100; page++) {
4937
+ const result2 = await this.listAccounts(
4938
+ {
4939
+ status: "active",
4940
+ sandbox: hints.sandbox ?? false,
4941
+ ...cursor !== void 0 && { pagination: { cursor } }
4942
+ },
4943
+ void 0,
4944
+ effectiveOptions
4945
+ );
4946
+ onTaskResult?.(result2);
4947
+ if (!result2.success || result2.status !== "completed" || !result2.data || !("accounts" in result2.data) || !Array.isArray(result2.data.accounts)) {
4948
+ throw new Error("list_accounts did not return a completed account list.");
4949
+ }
4950
+ accounts.push(...result2.data.accounts);
4951
+ if (!result2.data.pagination?.has_more) return selectListedAccount(accounts, hints);
4952
+ const nextCursor = result2.data.pagination.cursor;
4953
+ if (!nextCursor || seenCursors.has(nextCursor)) break;
4954
+ seenCursors.add(nextCursor);
4955
+ cursor = nextCursor;
4956
+ }
4957
+ throw new AccountRequiredError(
4958
+ "explicit",
4959
+ "resolveAccount",
4960
+ "Could not complete list_accounts pagination. Select an account_id explicitly after listing accounts."
4961
+ );
4962
+ }
4963
+ if (!hints.brand?.domain || !hints.operator) {
4964
+ throw new AccountRequiredError(
4965
+ "implicit",
4966
+ "resolveAccount",
4967
+ "Implicit-account sellers require both brand and operator to sync an account."
4968
+ );
4969
+ }
4970
+ const uses32NaturalKey = hints.operatorUnit !== void 0 || hints.currency !== void 0 || hints.timezone !== void 0;
4971
+ const sellerServes32 = capabilities.servedVersion ? !isPre32AdcpVersion(capabilities.servedVersion) : capabilities.supportedVersions?.some((version) => !isPre32AdcpVersion(version)) === true;
4972
+ if (uses32NaturalKey && (isPre32AdcpVersion(this.resolvedAdcpVersion) || !sellerServes32)) {
4973
+ throw new AccountRequiredError(
4974
+ "implicit",
4975
+ "resolveAccount",
4976
+ "Operator unit, account currency, and account timezone require AdCP 3.2 on both buyer and seller."
4977
+ );
4978
+ }
4979
+ const timezone = capabilities.account.timezone;
4980
+ if (timezone?.accountSelection === "buyer_selected") {
4981
+ if (!hints.timezone || !timezone.supportedTimezones?.includes(hints.timezone)) {
4982
+ throw new AccountRequiredError(
4983
+ "implicit",
4984
+ "resolveAccount",
4985
+ "Choose a supported account timezone before sync_accounts."
4986
+ );
4987
+ }
4988
+ } else if (hints.timezone && timezone) {
4989
+ throw new AccountRequiredError(
4990
+ "implicit",
4991
+ "resolveAccount",
4992
+ "This seller does not accept a buyer-selected account timezone."
4993
+ );
4994
+ }
4995
+ const currencyModes = capabilities.account.supportedAccountCurrencyModes;
4996
+ if (currencyModes?.length === 1 && currencyModes[0] === "fixed" && !hints.currency) {
4997
+ throw new AccountRequiredError("implicit", "resolveAccount", "This seller requires a fixed account currency.");
4998
+ }
4999
+ if (hints.currency && currencyModes?.length === 1 && currencyModes[0] === "per_media_buy") {
5000
+ throw new AccountRequiredError("implicit", "resolveAccount", "This seller selects currency per media buy.");
5001
+ }
5002
+ const account = {
5003
+ brand: hints.brand,
5004
+ operator: hints.operator,
5005
+ ...hints.operatorUnit !== void 0 && { operator_unit: hints.operatorUnit },
5006
+ ...hints.currency !== void 0 && { currency: hints.currency },
5007
+ ...hints.timezone !== void 0 && { timezone: hints.timezone },
5008
+ ...hints.sandbox !== void 0 && { sandbox: hints.sandbox }
5009
+ };
5010
+ const supportedBilling = capabilities.account.supportedBilling;
5011
+ const billing = hints.billing ?? capabilities.account.defaultBilling ?? (supportedBilling.length === 1 ? supportedBilling[0] : void 0);
5012
+ if (!billing || supportedBilling.length > 0 && !supportedBilling.includes(billing)) {
5013
+ throw new AccountRequiredError(
5014
+ "implicit",
5015
+ "resolveAccount",
5016
+ `Choose a supported billing party for sync_accounts (seller supports: ${supportedBilling.join(", ") || "(none)"}).`
5017
+ );
5018
+ }
5019
+ const result = await this.syncAccounts(
5020
+ {
5021
+ accounts: [
5022
+ {
5023
+ ...account,
5024
+ billing
5025
+ }
5026
+ ]
5027
+ },
5028
+ void 0,
5029
+ effectiveOptions
5030
+ );
5031
+ onTaskResult?.(result);
5032
+ if (!result.success || result.status !== "completed" || !result.data || !("accounts" in result.data) || !Array.isArray(result.data.accounts)) {
5033
+ throw new Error("sync_accounts did not return a completed account result.");
5034
+ }
5035
+ const synced = result.data.accounts.find(
5036
+ (row) => row.brand?.domain === hints.brand.domain && row.operator === hints.operator && (!hints.brand.brand_id || row.brand.brand_id === hints.brand.brand_id) && sameCountrySet(hints.brand.countries, row.brand.countries) && (!hints.operatorUnit || row.operator_unit?.id === hints.operatorUnit.id) && (!hints.currency || row.currency === hints.currency) && (!hints.timezone || row.timezone === hints.timezone) && (row.sandbox === void 0 || row.sandbox === true === (hints.sandbox === true))
5037
+ );
5038
+ if (synced?.status === "pending_approval") {
5039
+ throw new AccountPendingApprovalError(account, synced.account_id);
5040
+ }
5041
+ if (!synced || synced.action === "failed" || synced.status !== "active") {
5042
+ throw new AccountRequiredError(
5043
+ "implicit",
5044
+ "resolveAccount",
5045
+ "sync_accounts did not establish an active account for the requested brand and operator."
5046
+ );
5047
+ }
5048
+ return account;
5049
+ });
5050
+ }
4851
5051
  /**
4852
5052
  * Sync accounts
4853
5053
  */
@@ -5116,7 +5316,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
5116
5316
  let detectedServerVersion;
5117
5317
  let detectedServerVersionSynthetic;
5118
5318
  try {
5119
- const normalizedParams = normalizeRequestParams(taskName, params, {
5319
+ const normalizedParams = options?.preserveGovernedPayload ? params : normalizeRequestParams(taskName, params, {
5120
5320
  skipIdempotencyAutoInject: options?.skipIdempotencyAutoInject,
5121
5321
  skipAccountValidation: options?.skipAccountValidation
5122
5322
  });
@@ -5128,7 +5328,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
5128
5328
  await this.requireSupportedMajor(taskName, options);
5129
5329
  }
5130
5330
  const agent = await this.ensureEndpointDiscovered(options);
5131
- if (!options?.skipIdempotencyAutoInject && !options?.skipAccountValidation && !options?.skipRequestValidation) {
5331
+ if (!options?.skipIdempotencyAutoInject && !options?.skipAccountValidation && !options?.skipRequestValidation && !options?.preserveGovernedPayload) {
5132
5332
  this.executor.validateRequest(taskName, normalizedParams);
5133
5333
  }
5134
5334
  const capabilityDiscoveryContext = {};
@@ -5152,7 +5352,12 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
5152
5352
  capabilityDiscoveryContext.capabilities
5153
5353
  );
5154
5354
  const inputSchemaStripLogs = [];
5155
- const { params: adaptedParams, driftLogs: adaptDriftLogs } = this.adaptRequest(
5355
+ if (options?.preserveGovernedPayload && serverVersion !== "v3") {
5356
+ throw new ConfigurationError(
5357
+ "Governed storyboard requests require a v3 seller to preserve the approved payload."
5358
+ );
5359
+ }
5360
+ const { params: adaptedParams, driftLogs: adaptDriftLogs } = options?.preserveGovernedPayload ? { params: normalizedParams, driftLogs: [] } : this.adaptRequest(
5156
5361
  taskName,
5157
5362
  normalizedParams,
5158
5363
  serverVersion,
@@ -789,7 +789,7 @@ class TaskExecutor {
789
789
  const workingTimeout = this.config.workingTimeout || 12e4;
790
790
  const wireAdcpVersion = options.wireAdcpVersion ?? this.config.wireAdcpVersion;
791
791
  const versionEnvelope = options.versionEnvelope ?? this.config.versionEnvelope;
792
- const idempotencyKey = options.skipIdempotencyAutoInject ? void 0 : resolveIdempotencyKey(taskName, params, serverVersion);
792
+ const idempotencyKey = options.skipIdempotencyAutoInject || options.preserveGovernedPayload ? void 0 : resolveIdempotencyKey(taskName, params, serverVersion);
793
793
  if (idempotencyKey) (0, import_task_deadline.attachTaskDeadlineIdempotencyKey)(options, idempotencyKey);
794
794
  if (serverVersion !== "v2" && idempotencyKey && params && typeof params === "object" && !params.idempotency_key) {
795
795
  params = { ...params, idempotency_key: idempotencyKey };
@@ -762,7 +762,7 @@ class TaskExecutor {
762
762
  const workingTimeout = this.config.workingTimeout || 12e4;
763
763
  const wireAdcpVersion = options.wireAdcpVersion ?? this.config.wireAdcpVersion;
764
764
  const versionEnvelope = options.versionEnvelope ?? this.config.versionEnvelope;
765
- const idempotencyKey = options.skipIdempotencyAutoInject ? void 0 : resolveIdempotencyKey(taskName, params, serverVersion);
765
+ const idempotencyKey = options.skipIdempotencyAutoInject || options.preserveGovernedPayload ? void 0 : resolveIdempotencyKey(taskName, params, serverVersion);
766
766
  if (idempotencyKey) attachTaskDeadlineIdempotencyKey(options, idempotencyKey);
767
767
  if (serverVersion !== "v2" && idempotencyKey && params && typeof params === "object" && !params.idempotency_key) {
768
768
  params = { ...params, idempotency_key: idempotencyKey };
@@ -0,0 +1,24 @@
1
+ import type { AccountReference, ListAccountsResponse } from '../types/index.mjs';
2
+ export type ListedAccount = ListAccountsResponse['accounts'][number];
3
+ type NaturalAccountReference = Extract<AccountReference, {
4
+ brand: unknown;
5
+ }>;
6
+ export declare function sameCountrySet(expected?: readonly string[], actual?: readonly string[]): boolean;
7
+ /** Hints for resolving a seller account against the caller's own credentials. */
8
+ export interface ResolveAccountOptions {
9
+ brand?: NaturalAccountReference['brand'];
10
+ operator?: string;
11
+ operatorUnit?: NaturalAccountReference['operator_unit'];
12
+ currency?: NaturalAccountReference['currency'];
13
+ timezone?: NaturalAccountReference['timezone'];
14
+ sandbox?: boolean;
15
+ /** Require introspected access to this task when the seller publishes authorization. */
16
+ forTask?: string;
17
+ /** Billing party for buyer-declared accounts when the seller has no default. */
18
+ billing?: 'operator' | 'agent' | 'advertiser';
19
+ /** An account_id or a callback choosing one of the eligible list_accounts rows. */
20
+ select?: string | ((accounts: readonly ListedAccount[]) => string);
21
+ }
22
+ /** Select only from active rows returned for the authenticated caller. */
23
+ export declare function selectListedAccount(accounts: readonly ListedAccount[], hints: ResolveAccountOptions): AccountReference;
24
+ export {};
@@ -0,0 +1,24 @@
1
+ import type { AccountReference, ListAccountsResponse } from '../types';
2
+ export type ListedAccount = ListAccountsResponse['accounts'][number];
3
+ type NaturalAccountReference = Extract<AccountReference, {
4
+ brand: unknown;
5
+ }>;
6
+ export declare function sameCountrySet(expected?: readonly string[], actual?: readonly string[]): boolean;
7
+ /** Hints for resolving a seller account against the caller's own credentials. */
8
+ export interface ResolveAccountOptions {
9
+ brand?: NaturalAccountReference['brand'];
10
+ operator?: string;
11
+ operatorUnit?: NaturalAccountReference['operator_unit'];
12
+ currency?: NaturalAccountReference['currency'];
13
+ timezone?: NaturalAccountReference['timezone'];
14
+ sandbox?: boolean;
15
+ /** Require introspected access to this task when the seller publishes authorization. */
16
+ forTask?: string;
17
+ /** Billing party for buyer-declared accounts when the seller has no default. */
18
+ billing?: 'operator' | 'agent' | 'advertiser';
19
+ /** An account_id or a callback choosing one of the eligible list_accounts rows. */
20
+ select?: string | ((accounts: readonly ListedAccount[]) => string);
21
+ }
22
+ /** Select only from active rows returned for the authenticated caller. */
23
+ export declare function selectListedAccount(accounts: readonly ListedAccount[], hints: ResolveAccountOptions): AccountReference;
24
+ export {};
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var account_resolution_exports = {};
20
+ __export(account_resolution_exports, {
21
+ sameCountrySet: () => sameCountrySet,
22
+ selectListedAccount: () => selectListedAccount
23
+ });
24
+ module.exports = __toCommonJS(account_resolution_exports);
25
+ var import_errors = require('../errors/index.js');
26
+ function sameCountrySet(expected, actual) {
27
+ if (expected === void 0) return true;
28
+ if (!actual || expected.length !== actual.length) return false;
29
+ const sortedExpected = [...expected].sort();
30
+ const sortedActual = [...actual].sort();
31
+ return sortedExpected.every((country, index) => country === sortedActual[index]);
32
+ }
33
+ function selectListedAccount(accounts, hints) {
34
+ const candidates = accounts.filter((account) => {
35
+ if (typeof account.account_id !== "string" || !account.account_id) return false;
36
+ if (account.status !== "active") return false;
37
+ if (hints.forTask && account.authorization && !account.authorization.allowed_tasks.includes(hints.forTask)) {
38
+ return false;
39
+ }
40
+ if (hints.brand) {
41
+ if (account.brand?.domain !== hints.brand.domain) return false;
42
+ if (hints.brand.brand_id && account.brand.brand_id !== hints.brand.brand_id) return false;
43
+ if (!sameCountrySet(hints.brand.countries, account.brand.countries)) return false;
44
+ }
45
+ if (hints.operator && account.operator !== hints.operator) return false;
46
+ if (hints.operatorUnit && account.operator_unit?.id !== hints.operatorUnit.id) return false;
47
+ if (hints.currency && account.currency !== hints.currency) return false;
48
+ if (hints.timezone && account.timezone !== hints.timezone) return false;
49
+ if (account.sandbox === true !== (hints.sandbox === true)) return false;
50
+ return true;
51
+ });
52
+ if (candidates.length === 0) {
53
+ throw new import_errors.AccountRequiredError(
54
+ "explicit",
55
+ "resolveAccount",
56
+ "list_accounts returned no active account matching the supplied brand, operator, and sandbox hints."
57
+ );
58
+ }
59
+ const selectedId = typeof hints.select === "function" ? hints.select(candidates) : hints.select;
60
+ if (selectedId !== void 0) {
61
+ if (!candidates.some((account) => account.account_id === selectedId)) {
62
+ throw new import_errors.AccountRequiredError(
63
+ "explicit",
64
+ "resolveAccount",
65
+ "The selected account_id is not among the active accounts returned for this caller."
66
+ );
67
+ }
68
+ return { account_id: selectedId };
69
+ }
70
+ if (candidates.length > 1) {
71
+ throw new import_errors.AccountAmbiguousError(candidates.map((account) => account.account_id));
72
+ }
73
+ return { account_id: candidates[0].account_id };
74
+ }
75
+ // Annotate the CommonJS export names for ESM import in node:
76
+ 0 && (module.exports = {
77
+ sameCountrySet,
78
+ selectListedAccount
79
+ });
@@ -0,0 +1,54 @@
1
+ import { AccountAmbiguousError, AccountRequiredError } from "../errors/index.mjs";
2
+ function sameCountrySet(expected, actual) {
3
+ if (expected === void 0) return true;
4
+ if (!actual || expected.length !== actual.length) return false;
5
+ const sortedExpected = [...expected].sort();
6
+ const sortedActual = [...actual].sort();
7
+ return sortedExpected.every((country, index) => country === sortedActual[index]);
8
+ }
9
+ function selectListedAccount(accounts, hints) {
10
+ const candidates = accounts.filter((account) => {
11
+ if (typeof account.account_id !== "string" || !account.account_id) return false;
12
+ if (account.status !== "active") return false;
13
+ if (hints.forTask && account.authorization && !account.authorization.allowed_tasks.includes(hints.forTask)) {
14
+ return false;
15
+ }
16
+ if (hints.brand) {
17
+ if (account.brand?.domain !== hints.brand.domain) return false;
18
+ if (hints.brand.brand_id && account.brand.brand_id !== hints.brand.brand_id) return false;
19
+ if (!sameCountrySet(hints.brand.countries, account.brand.countries)) return false;
20
+ }
21
+ if (hints.operator && account.operator !== hints.operator) return false;
22
+ if (hints.operatorUnit && account.operator_unit?.id !== hints.operatorUnit.id) return false;
23
+ if (hints.currency && account.currency !== hints.currency) return false;
24
+ if (hints.timezone && account.timezone !== hints.timezone) return false;
25
+ if (account.sandbox === true !== (hints.sandbox === true)) return false;
26
+ return true;
27
+ });
28
+ if (candidates.length === 0) {
29
+ throw new AccountRequiredError(
30
+ "explicit",
31
+ "resolveAccount",
32
+ "list_accounts returned no active account matching the supplied brand, operator, and sandbox hints."
33
+ );
34
+ }
35
+ const selectedId = typeof hints.select === "function" ? hints.select(candidates) : hints.select;
36
+ if (selectedId !== void 0) {
37
+ if (!candidates.some((account) => account.account_id === selectedId)) {
38
+ throw new AccountRequiredError(
39
+ "explicit",
40
+ "resolveAccount",
41
+ "The selected account_id is not among the active accounts returned for this caller."
42
+ );
43
+ }
44
+ return { account_id: selectedId };
45
+ }
46
+ if (candidates.length > 1) {
47
+ throw new AccountAmbiguousError(candidates.map((account) => account.account_id));
48
+ }
49
+ return { account_id: candidates[0].account_id };
50
+ }
51
+ export {
52
+ sameCountrySet,
53
+ selectListedAccount
54
+ };
@@ -72,6 +72,33 @@ export declare class UnsupportedTaskError extends ADCPError {
72
72
  readonly code = "UNSUPPORTED_TASK";
73
73
  constructor(agentId: string, taskName: string, supportedTasks?: string[] | undefined);
74
74
  }
75
+ /** Seller did not declare the requested get_products buying mode. */
76
+ export declare class UnsupportedBuyingModeError extends ADCPError {
77
+ readonly requestedMode: string | undefined;
78
+ readonly declaredModes: readonly string[];
79
+ readonly code = "UNSUPPORTED_BUYING_MODE";
80
+ constructor(requestedMode: string | undefined, declaredModes: readonly string[]);
81
+ }
82
+ /** A seller requires an account reference before an account-scoped request. */
83
+ export declare class AccountRequiredError extends ADCPError {
84
+ readonly accountModel: 'explicit' | 'implicit';
85
+ readonly taskName: string;
86
+ readonly code = "ACCOUNT_REQUIRED";
87
+ constructor(accountModel: 'explicit' | 'implicit', taskName: string, reason?: string);
88
+ }
89
+ /** Account provisioning succeeded but the seller has not approved its use. */
90
+ export declare class AccountPendingApprovalError extends ADCPError {
91
+ readonly account: import('../types/index.mjs').AccountReference;
92
+ readonly accountId?: string | undefined;
93
+ readonly code = "ACCOUNT_PENDING_APPROVAL";
94
+ constructor(account: import('../types/index.mjs').AccountReference, accountId?: string | undefined);
95
+ }
96
+ /** More than one active account matches the caller's selection hints. */
97
+ export declare class AccountAmbiguousError extends ADCPError {
98
+ readonly candidates: readonly string[];
99
+ readonly code = "ACCOUNT_AMBIGUOUS";
100
+ constructor(candidates: readonly string[]);
101
+ }
75
102
  /**
76
103
  * Error thrown when protocol communication fails
77
104
  */