@adcp/sdk 9.0.0-beta.30 → 9.0.0-beta.31

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 (86) hide show
  1. package/dist/lib/adapters/version/3.0/brand-fields.d.ts +4 -0
  2. package/dist/lib/adapters/version/3.0/brand-fields.d.ts.map +1 -0
  3. package/dist/lib/adapters/version/3.0/brand-fields.js +30 -0
  4. package/dist/lib/adapters/version/3.0/brand-fields.js.map +1 -0
  5. package/dist/lib/adapters/version/3.0/index.d.ts +3 -0
  6. package/dist/lib/adapters/version/3.0/index.d.ts.map +1 -0
  7. package/dist/lib/adapters/version/3.0/index.js +11 -0
  8. package/dist/lib/adapters/version/3.0/index.js.map +1 -0
  9. package/dist/lib/adapters/version/3.0/sync-accounts.d.ts +3 -0
  10. package/dist/lib/adapters/version/3.0/sync-accounts.d.ts.map +1 -0
  11. package/dist/lib/adapters/version/3.0/sync-accounts.js +34 -0
  12. package/dist/lib/adapters/version/3.0/sync-accounts.js.map +1 -0
  13. package/dist/lib/adapters/version/index.d.ts +38 -0
  14. package/dist/lib/adapters/version/index.d.ts.map +1 -0
  15. package/dist/lib/adapters/version/index.js +53 -0
  16. package/dist/lib/adapters/version/index.js.map +1 -0
  17. package/dist/lib/adapters/version/types.d.ts +34 -0
  18. package/dist/lib/adapters/version/types.d.ts.map +1 -0
  19. package/dist/lib/adapters/version/types.js +15 -0
  20. package/dist/lib/adapters/version/types.js.map +1 -0
  21. package/dist/lib/core/AgentClient.d.ts.map +1 -1
  22. package/dist/lib/core/SingleAgentClient.d.ts +25 -4
  23. package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
  24. package/dist/lib/core/SingleAgentClient.js +161 -67
  25. package/dist/lib/core/SingleAgentClient.js.map +1 -1
  26. package/dist/lib/index.d.ts +1 -1
  27. package/dist/lib/index.d.ts.map +1 -1
  28. package/dist/lib/index.js.map +1 -1
  29. package/dist/lib/registry/index.d.ts +13 -4
  30. package/dist/lib/registry/index.d.ts.map +1 -1
  31. package/dist/lib/registry/index.js +33 -4
  32. package/dist/lib/registry/index.js.map +1 -1
  33. package/dist/lib/registry/types.d.ts +11 -4
  34. package/dist/lib/registry/types.d.ts.map +1 -1
  35. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  36. package/dist/lib/server/create-adcp-server.d.ts +20 -5
  37. package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
  38. package/dist/lib/server/create-adcp-server.js +29 -16
  39. package/dist/lib/server/create-adcp-server.js.map +1 -1
  40. package/dist/lib/server/decisioning/tenant-registry.d.ts.map +1 -1
  41. package/dist/lib/server/decisioning/tenant-registry.js +14 -12
  42. package/dist/lib/server/decisioning/tenant-registry.js.map +1 -1
  43. package/dist/lib/server/webhook-emitter.d.ts +14 -14
  44. package/dist/lib/server/webhook-emitter.d.ts.map +1 -1
  45. package/dist/lib/signing/errors.d.ts.map +1 -1
  46. package/dist/lib/signing/errors.js.map +1 -1
  47. package/dist/lib/signing/jwks-helpers.d.ts +16 -2
  48. package/dist/lib/signing/jwks-helpers.d.ts.map +1 -1
  49. package/dist/lib/signing/jwks-helpers.js.map +1 -1
  50. package/dist/lib/signing/signer-async.d.ts.map +1 -1
  51. package/dist/lib/signing/signer-async.js +1 -1
  52. package/dist/lib/signing/signer-async.js.map +1 -1
  53. package/dist/lib/signing/signer.d.ts +9 -2
  54. package/dist/lib/signing/signer.d.ts.map +1 -1
  55. package/dist/lib/signing/signer.js +28 -1
  56. package/dist/lib/signing/signer.js.map +1 -1
  57. package/dist/lib/signing/webhook-verifier.d.ts +2 -1
  58. package/dist/lib/signing/webhook-verifier.d.ts.map +1 -1
  59. package/dist/lib/signing/webhook-verifier.js +25 -14
  60. package/dist/lib/signing/webhook-verifier.js.map +1 -1
  61. package/dist/lib/testing/storyboard/compliance.d.ts +18 -0
  62. package/dist/lib/testing/storyboard/compliance.d.ts.map +1 -1
  63. package/dist/lib/testing/storyboard/compliance.js +30 -1
  64. package/dist/lib/testing/storyboard/compliance.js.map +1 -1
  65. package/dist/lib/testing/storyboard/runner.d.ts +6 -8
  66. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
  67. package/dist/lib/testing/storyboard/runner.js +99 -45
  68. package/dist/lib/testing/storyboard/runner.js.map +1 -1
  69. package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
  70. package/dist/lib/testing/storyboard/task-map.js +6 -0
  71. package/dist/lib/testing/storyboard/task-map.js.map +1 -1
  72. package/dist/lib/testing/storyboard/types.d.ts +18 -12
  73. package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
  74. package/dist/lib/testing/storyboard/types.js.map +1 -1
  75. package/dist/lib/types/adcp.d.ts +13 -5
  76. package/dist/lib/types/adcp.d.ts.map +1 -1
  77. package/dist/lib/utils/adcp-version-config.d.ts +22 -0
  78. package/dist/lib/utils/adcp-version-config.d.ts.map +1 -1
  79. package/dist/lib/utils/adcp-version-config.js +37 -0
  80. package/dist/lib/utils/adcp-version-config.js.map +1 -1
  81. package/dist/lib/utils/response-unwrapper.d.ts.map +1 -1
  82. package/dist/lib/utils/response-unwrapper.js +3 -2
  83. package/dist/lib/utils/response-unwrapper.js.map +1 -1
  84. package/dist/lib/version.d.ts +3 -3
  85. package/dist/lib/version.js +3 -3
  86. package/package.json +2 -2
@@ -41,6 +41,7 @@ const schemas = __importStar(require("../types/schemas.generated"));
41
41
  const adcp_1 = require("../types/adcp");
42
42
  const version_1 = require("../version");
43
43
  const adcp_version_config_1 = require("../utils/adcp-version-config");
44
+ const version_2 = require("../adapters/version");
44
45
  const idempotency_1 = require("../utils/idempotency");
45
46
  const TaskExecutor_1 = require("./TaskExecutor");
46
47
  const match_1 = require("./match");
@@ -1045,6 +1046,10 @@ class SingleAgentClient {
1045
1046
  skipAccountValidation: options?.skipAccountValidation,
1046
1047
  });
1047
1048
  this.assertRequestSupportedByConfiguredVersion(taskType, normalizedParams, options);
1049
+ // Degrade an auto-injected discovery webhook to polling for pre-3.1 pins
1050
+ // (get_products / get_signals). `effectiveOptions` carries disableWebhook
1051
+ // so no push_notification_config reaches a seller that can't accept it.
1052
+ const { options: effectiveOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(taskType, options);
1048
1053
  // Inject an idempotency_key for mutating tools before schema validation
1049
1054
  // so callers don't have to supply one. TaskExecutor also guards against
1050
1055
  // missing keys, but validation happens here first — do the injection up
@@ -1088,27 +1093,32 @@ class SingleAgentClient {
1088
1093
  if (!options?.skipIdempotencyAutoInject && !options?.skipAccountValidation) {
1089
1094
  this.executor.validateRequest(taskType, normalizedParams);
1090
1095
  }
1091
- // Adapt request for v2 servers if needed
1096
+ // Adapt request for the detected server and AdCP protocol versions.
1092
1097
  const serverVersion = await this.detectServerVersion();
1093
- const adaptedParams = await this.adaptRequestForServerVersion(taskType, normalizedParams);
1098
+ const inputSchemaStripLogs = [];
1099
+ const { params: adaptedParams, driftLogs: adaptDriftLogs } = this.adaptRequest(taskType, normalizedParams, serverVersion, inputSchemaStripLogs);
1094
1100
  // Symmetric to the pre-adapter v3 pass above: when the adapter
1095
1101
  // rewrote the request for a v2 server, warn-validate the adapted
1096
1102
  // shape against the cached v2.5 schema bundle. Drift gets collected
1097
1103
  // here and merged into result.metadata.debug_logs after executeTask
1098
1104
  // returns — without that merge the warning would silently drop on
1099
1105
  // the floor and adapter drift would land in production unnoticed.
1100
- const v25DriftLogs = [];
1106
+ const v25DriftLogs = [...adaptDriftLogs];
1107
+ if (webhookDriftLog)
1108
+ v25DriftLogs.push(webhookDriftLog);
1101
1109
  if (serverVersion === 'v2') {
1102
1110
  this.executor.validateAdaptedRequestAgainstV2(taskType, adaptedParams, v25DriftLogs);
1103
1111
  }
1104
- let result = await this.executor.executeTask(agent, taskType, adaptedParams, inputHandler, options, serverVersion);
1112
+ let result = await this.executor.executeTask(agent, taskType, adaptedParams, inputHandler, effectiveOptions, serverVersion);
1105
1113
  // Merge collected drift into the executor's debug_logs so adopters
1106
- // reading result.debug_logs see post-adapter v2.5 warnings alongside
1114
+ // reading result.debug_logs see input-schema stripping, post-adapter
1115
+ // v2.5 warnings, and any pre-3.1 webhook-degradation notice alongside
1107
1116
  // the executor's own logs. On error paths the executor may not surface
1108
- // result.debug_logs at all drift collected before the failure is
1117
+ // result.debug_logs at all; logs collected before the failure are
1109
1118
  // dropped, matching the executor's own debug-log behavior.
1110
- if (v25DriftLogs.length > 0) {
1111
- result.debug_logs = [...(result.debug_logs ?? []), ...v25DriftLogs];
1119
+ const postAdapterLogs = [...inputSchemaStripLogs, ...v25DriftLogs];
1120
+ if (postAdapterLogs.length > 0) {
1121
+ result.debug_logs = [...(result.debug_logs ?? []), ...postAdapterLogs];
1112
1122
  }
1113
1123
  // Normalize response to v3 format
1114
1124
  if (result.success && result.data) {
@@ -1428,15 +1438,19 @@ class SingleAgentClient {
1428
1438
  };
1429
1439
  }
1430
1440
  /**
1431
- * Adapt request parameters for the detected server version
1441
+ * Adapt a request for the detected server wire version and the seller's
1442
+ * AdCP protocol version. Applies wire-format adapters (v2.5) when talking
1443
+ * to a v2 server, then applies protocol-version adapters (e.g. stripping
1444
+ * 3.1-only fields for a 3.0 seller). Returns the adapted params and any
1445
+ * drift log entries describing what was changed.
1432
1446
  *
1433
- * Converts v3-style requests to v2 format when talking to v2 servers.
1447
+ * Runs after `detectServerVersion` so `cachedCapabilities` is populated
1448
+ * and the protocol-version adapters see the seller's declared caps.
1434
1449
  */
1435
- async adaptRequestForServerVersion(taskType, params) {
1436
- // Get server version (cached after first call)
1437
- const version = await this.detectServerVersion();
1450
+ adaptRequest(taskType, params, serverVersion, debugLogs) {
1451
+ const driftLogs = [];
1438
1452
  let adapted = params;
1439
- if (version !== 'v3') {
1453
+ if (serverVersion !== 'v3') {
1440
1454
  // Dispatch through the legacy v2.5 adapter registry. Per-tool pairs
1441
1455
  // live in `src/lib/adapters/legacy/v2-5/<tool>.ts`. Tools without a
1442
1456
  // registered pair (or pairs whose request side is pass-through)
@@ -1445,7 +1459,7 @@ class SingleAgentClient {
1445
1459
  // this dispatch.
1446
1460
  const pair = (0, v2_5_1.getV25Adapter)(taskType);
1447
1461
  if (pair)
1448
- adapted = pair.adaptRequest(params);
1462
+ adapted = pair.adaptRequest(adapted);
1449
1463
  }
1450
1464
  // Strip any top-level fields not declared in the agent's tool schema.
1451
1465
  // This handles partial implementations (agents that omit some fields)
@@ -1469,49 +1483,82 @@ class SingleAgentClient {
1469
1483
  // (e.g. `applyBrandInvariant` in the storyboard runner — see #940),
1470
1484
  // not to lean on this strip path as a backstop.
1471
1485
  const toolSchema = this.cachedToolSchemas?.get(taskType);
1472
- if (!toolSchema || Object.keys(toolSchema).length === 0)
1473
- return adapted;
1474
- const declaredFields = new Set(Object.keys(toolSchema));
1475
- // The v2 adapter may rename fields (e.g. brand brand_manifest) that a
1476
- // v3 server misdetected as v2 — doesn't declare. Reconcile known
1477
- // adapter mappings so the value isn't silently dropped.
1478
- //
1479
- // CRITICAL: only alias when the JS type of the moved value is
1480
- // compatible with the destination field's declared shape. v2.5 sellers
1481
- // (e.g. Wonderstruck) declare `brand` in their tool schema as a
1482
- // BrandReference object v2 adapter produces a `brand_manifest` URL
1483
- // string, and blindly aliasing the string into the object slot causes
1484
- // the seller to reject with `Input should be a valid dictionary or
1485
- // instance of BrandReference`. Skip the alias when shapes don't match
1486
- // and let the field-stripping path drop the v2-shaped value cleanly.
1487
- const adapterAliases = [['brand_manifest', 'brand']];
1488
- for (const [adapterField, schemaField] of adapterAliases) {
1489
- if (adapted[adapterField] !== undefined &&
1490
- !declaredFields.has(adapterField) &&
1491
- declaredFields.has(schemaField) &&
1492
- adapted[schemaField] === undefined &&
1493
- valueMatchesSchemaType(adapted[adapterField], toolSchema[schemaField])) {
1494
- adapted[schemaField] = adapted[adapterField];
1495
- delete adapted[adapterField];
1486
+ if (toolSchema && Object.keys(toolSchema).length > 0) {
1487
+ const declaredFields = new Set(Object.keys(toolSchema));
1488
+ // The v2 adapter may rename fields (e.g. brand → brand_manifest) that a
1489
+ // v3 server misdetected as v2 doesn't declare. Reconcile known
1490
+ // adapter mappings so the value isn't silently dropped.
1491
+ //
1492
+ // CRITICAL: only alias when the JS type of the moved value is
1493
+ // compatible with the destination field's declared shape. v2.5 sellers
1494
+ // (e.g. Wonderstruck) declare `brand` in their tool schema as a
1495
+ // BrandReference object v2 adapter produces a `brand_manifest` URL
1496
+ // string, and blindly aliasing the string into the object slot causes
1497
+ // the seller to reject with `Input should be a valid dictionary or
1498
+ // instance of BrandReference`. Skip the alias when shapes don't match
1499
+ // and let the field-stripping path drop the v2-shaped value cleanly.
1500
+ const adapterAliases = [['brand_manifest', 'brand']];
1501
+ for (const [adapterField, schemaField] of adapterAliases) {
1502
+ if (adapted[adapterField] !== undefined &&
1503
+ !declaredFields.has(adapterField) &&
1504
+ declaredFields.has(schemaField) &&
1505
+ adapted[schemaField] === undefined &&
1506
+ valueMatchesSchemaType(adapted[adapterField], toolSchema[schemaField])) {
1507
+ adapted[schemaField] = adapted[adapterField];
1508
+ delete adapted[adapterField];
1509
+ }
1496
1510
  }
1497
- }
1498
- // Protocol envelope fields are always preserved — they live at the
1499
- // protocol layer, not in individual tool schemas.
1500
- const envelopeFields = adcp_1.ADCP_ENVELOPE_FIELDS;
1501
- const filtered = {};
1502
- const stripped = [];
1503
- for (const [key, value] of Object.entries(adapted)) {
1504
- if (declaredFields.has(key) || envelopeFields.has(key)) {
1505
- filtered[key] = value;
1511
+ // Protocol envelope fields are always preserved — they live at the
1512
+ // protocol layer, not in individual tool schemas.
1513
+ const envelopeFields = adcp_1.ADCP_ENVELOPE_FIELDS;
1514
+ const filtered = {};
1515
+ const schemaStripped = [];
1516
+ for (const [key, value] of Object.entries(adapted)) {
1517
+ if (declaredFields.has(key) || envelopeFields.has(key)) {
1518
+ filtered[key] = value;
1519
+ }
1520
+ else {
1521
+ schemaStripped.push(key);
1522
+ }
1506
1523
  }
1507
- else {
1508
- stripped.push(key);
1524
+ if (schemaStripped.length > 0) {
1525
+ console.warn(`[AdCP] Stripping fields not declared in agent "${this.agent.id}" schema for ${taskType}: ${schemaStripped.join(', ')}`);
1526
+ debugLogs?.push({
1527
+ type: 'warning',
1528
+ message: `Stripped fields not declared in agent tool input schema for ${taskType}: ${schemaStripped.join(', ')}`,
1529
+ timestamp: new Date().toISOString(),
1530
+ details: {
1531
+ code: 'input_schema_field_stripped',
1532
+ task: taskType,
1533
+ fields: schemaStripped,
1534
+ agent_id: this.agent.id,
1535
+ },
1536
+ });
1537
+ }
1538
+ adapted = filtered;
1539
+ }
1540
+ // Protocol version adaptation: strip fields not accepted by the target
1541
+ // AdCP version. `resolveAdapterKey` returns the effective target version
1542
+ // based on the client pin and the seller's advertised caps; adapters live
1543
+ // in `src/lib/adapters/version/<target>/`.
1544
+ const adapterKey = (0, version_2.resolveAdapterKey)(this.resolvedAdcpVersion, this.cachedCapabilities);
1545
+ if (adapterKey) {
1546
+ const versionAdapter = (0, version_2.getVersionAdapter)(adapterKey, taskType);
1547
+ if (versionAdapter) {
1548
+ const result = versionAdapter.adaptRequest(adapted);
1549
+ adapted = result.params;
1550
+ if (result.drift) {
1551
+ driftLogs.push({
1552
+ ...result.drift,
1553
+ taskName: taskType,
1554
+ clientVersion: this.resolvedAdcpVersion,
1555
+ targetVersion: adapterKey,
1556
+ timestamp: new Date().toISOString(),
1557
+ });
1558
+ }
1509
1559
  }
1510
1560
  }
1511
- if (stripped.length > 0) {
1512
- console.warn(`[AdCP] Stripping fields not declared in agent "${this.agent.id}" schema for ${taskType}: ${stripped.join(', ')}`);
1513
- }
1514
- return filtered;
1561
+ return { params: adapted, driftLogs };
1515
1562
  }
1516
1563
  /**
1517
1564
  * Normalize response to v3 format
@@ -1955,6 +2002,10 @@ class SingleAgentClient {
1955
2002
  skipAccountValidation: options?.skipAccountValidation,
1956
2003
  });
1957
2004
  this.assertRequestSupportedByConfiguredVersion(taskName, normalizedParams, options);
2005
+ // Degrade an auto-injected discovery webhook to polling for pre-3.1 pins
2006
+ // (get_products / get_signals). `effectiveOptions` carries disableWebhook
2007
+ // so no push_notification_config reaches a seller that can't accept it.
2008
+ const { options: effectiveOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(taskName, options);
1958
2009
  await this.validateTaskFeatures(taskName);
1959
2010
  if (this.config.requireV3ForMutations && (0, idempotency_1.isMutatingTask)(taskName)) {
1960
2011
  await this.requireSupportedMajor(taskName);
@@ -1968,20 +2019,23 @@ class SingleAgentClient {
1968
2019
  if (!options?.skipIdempotencyAutoInject && !options?.skipAccountValidation) {
1969
2020
  this.executor.validateRequest(taskName, normalizedParams);
1970
2021
  }
1971
- // Adapt request for the server's protocol version (e.g. strip v3-only
1972
- // fields like buying_mode when talking to v2 agents).
2022
+ // Adapt request for the detected server and AdCP protocol versions.
1973
2023
  const serverVersion = await this.detectServerVersion();
1974
- const adaptedParams = await this.adaptRequestForServerVersion(taskName, normalizedParams);
2024
+ const inputSchemaStripLogs = [];
2025
+ const { params: adaptedParams, driftLogs: adaptDriftLogs } = this.adaptRequest(taskName, normalizedParams, serverVersion, inputSchemaStripLogs);
1975
2026
  // Symmetric warn-only post-adapter pass against the v2.5 schema bundle.
1976
2027
  // Drift gets surfaced via result.metadata.debug_logs so adapter
1977
2028
  // regressions in production aren't silently swallowed.
1978
- const v25DriftLogs = [];
2029
+ const v25DriftLogs = [...adaptDriftLogs];
2030
+ if (webhookDriftLog)
2031
+ v25DriftLogs.push(webhookDriftLog);
1979
2032
  if (serverVersion === 'v2') {
1980
2033
  this.executor.validateAdaptedRequestAgainstV2(taskName, adaptedParams, v25DriftLogs);
1981
2034
  }
1982
- let result = await this.executor.executeTask(agent, taskName, adaptedParams, inputHandler, options, serverVersion);
1983
- if (v25DriftLogs.length > 0) {
1984
- result.debug_logs = [...(result.debug_logs ?? []), ...v25DriftLogs];
2035
+ let result = await this.executor.executeTask(agent, taskName, adaptedParams, inputHandler, effectiveOptions, serverVersion);
2036
+ const postAdapterLogs = [...inputSchemaStripLogs, ...v25DriftLogs];
2037
+ if (postAdapterLogs.length > 0) {
2038
+ result.debug_logs = [...(result.debug_logs ?? []), ...postAdapterLogs];
1985
2039
  }
1986
2040
  // Normalize response to v3 format for consistent API surface
1987
2041
  if (result.success && result.data) {
@@ -2518,7 +2572,7 @@ class SingleAgentClient {
2518
2572
  }));
2519
2573
  // Cache raw tool schemas for field-level compatibility checks (e.g. buying_mode on get_products).
2520
2574
  // INVARIANT: must be assigned before cachedCapabilities below so that any code path
2521
- // reaching adaptRequestForServerVersion always finds the schemas populated.
2575
+ // reaching adaptRequest always finds the schemas populated.
2522
2576
  this.cachedToolSchemas = new Map(agentInfo.tools
2523
2577
  .filter(t => t.inputSchema?.properties)
2524
2578
  .map(t => [t.name, t.inputSchema.properties]));
@@ -2808,19 +2862,22 @@ class SingleAgentClient {
2808
2862
  * result set. A pre-3.1 client pin should not silently drop those controls
2809
2863
  * or let a generic schema error hide the recovery path.
2810
2864
  */
2811
- assertRequestSupportedByConfiguredVersion(taskName, params, options) {
2865
+ assertRequestSupportedByConfiguredVersion(taskName, params, _options) {
2812
2866
  if (!(0, adcp_version_config_1.isPre31AdcpVersion)(this.resolvedAdcpVersion))
2813
2867
  return;
2814
2868
  const request = params && typeof params === 'object' && !Array.isArray(params) ? params : {};
2815
- const willInjectDiscoveryWebhook = !options?.disableWebhook && (0, webhook_url_1.selectWebhookTemplate)(this.config.webhookUrlTemplate, taskName) !== undefined;
2816
2869
  if (taskName === 'get_signals' && request.discovery_mode === 'wholesale') {
2817
2870
  this.throwPre31UnsupportedFeature(taskName, 'discovery_mode', 'get_signals.discovery_mode=wholesale', {
2818
2871
  capabilityPath: 'signals.discovery_modes',
2819
2872
  suffix: 'Probe get_adcp_capabilities at signals.discovery_modes before issuing wholesale calls.',
2820
2873
  });
2821
2874
  }
2822
- if ((taskName === 'get_products' || taskName === 'get_signals') &&
2823
- (request.push_notification_config !== undefined || willInjectDiscoveryWebhook)) {
2875
+ // An EXPLICIT push_notification_config on a discovery task is caller misuse
2876
+ // while hard-pinned <3.1: surface it rather than silently dropping the
2877
+ // caller's webhook. An AUTO-injected discovery webhook (from
2878
+ // `webhookUrlTemplate`) is degraded to polling instead; see
2879
+ // `suppressPre31DiscoveryWebhook`.
2880
+ if ((taskName === 'get_products' || taskName === 'get_signals') && request.push_notification_config !== undefined) {
2824
2881
  this.throwPre31UnsupportedFeature(taskName, 'push_notification_config', `${taskName}.push_notification_config`, {
2825
2882
  capabilityPath: 'adcp.supported_versions',
2826
2883
  suffix: 'Probe get_adcp_capabilities at adcp.supported_versions before relying on discovery task webhooks.',
@@ -2831,6 +2888,43 @@ class SingleAgentClient {
2831
2888
  // probes, and pre-3.1 sellers may safely ignore them and return the full
2832
2889
  // payload.
2833
2890
  }
2891
+ /**
2892
+ * Degrade the auto-injected get_products / get_signals discovery webhook to
2893
+ * polling when the client is pinned below 3.1. Discovery-task
2894
+ * `push_notification_config` is an AdCP 3.1 feature; a pre-3.1 seller would
2895
+ * reject it. Rather than throwing on the library's own auto-injected webhook
2896
+ * (which the caller never asked for), suppress it via `disableWebhook` and
2897
+ * record a `pre31_webhook_degraded` drift entry so the loss of push is
2898
+ * visible in `debug_logs`.
2899
+ *
2900
+ * Returns the effective options (a `disableWebhook` copy when suppressing,
2901
+ * otherwise the caller's unchanged) and an optional drift log to merge into
2902
+ * the result. Explicit caller-supplied `push_notification_config` is handled
2903
+ * by `assertRequestSupportedByConfiguredVersion` (it throws) and never
2904
+ * reaches here.
2905
+ */
2906
+ suppressPre31DiscoveryWebhook(taskName, options) {
2907
+ if (!(0, adcp_version_config_1.isPre31AdcpVersion)(this.resolvedAdcpVersion))
2908
+ return { options };
2909
+ if (taskName !== 'get_products' && taskName !== 'get_signals')
2910
+ return { options };
2911
+ if (options?.disableWebhook)
2912
+ return { options };
2913
+ if ((0, webhook_url_1.selectWebhookTemplate)(this.config.webhookUrlTemplate, taskName) === undefined)
2914
+ return { options };
2915
+ return {
2916
+ options: { ...options, disableWebhook: true },
2917
+ driftLog: {
2918
+ type: 'pre31_webhook_degraded',
2919
+ message: `${taskName} discovery webhook degraded to polling: discovery-task push_notification_config ` +
2920
+ `requires AdCP 3.1, but this client is pinned to ${this.resolvedAdcpVersion}. ` +
2921
+ 'The seller will not receive a push webhook; poll for the result instead.',
2922
+ timestamp: new Date().toISOString(),
2923
+ taskName,
2924
+ clientVersion: this.resolvedAdcpVersion,
2925
+ },
2926
+ };
2927
+ }
2834
2928
  throwPre31UnsupportedFeature(taskName, field, feature, opts) {
2835
2929
  throw new errors_1.ProtocolFeatureUnsupportedError([feature], [], this.agent.agent_uri, {
2836
2930
  message: `${taskName} ${field} requires AdCP 3.1 or later; ` +
@@ -2986,7 +3080,7 @@ class SingleAgentClient {
2986
3080
  * but unknown top-level keys pass through. This matters because callers —
2987
3081
  * including the storyboard runner's `applyBrandInvariant` — inject
2988
3082
  * scoping fields (`brand`, `account`) onto every outgoing request, and
2989
- * `adaptRequestForServerVersion` strips those fields downstream for tools
3083
+ * `adaptRequest` strips those fields downstream for tools
2990
3084
  * whose schema doesn't declare them. A strict parse here rejects the
2991
3085
  * injected fields before the adapter gets a chance to clean them up, so
2992
3086
  * the two passes have to agree on "extra keys are fine."