@adcp/sdk 16.3.0 → 16.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/lib/adapters/version/3.1/assignments.d.mts +12 -0
  2. package/dist/lib/adapters/version/3.1/assignments.d.ts +12 -0
  3. package/dist/lib/adapters/version/3.1/assignments.js +67 -0
  4. package/dist/lib/adapters/version/3.1/assignments.mjs +43 -0
  5. package/dist/lib/adapters/version/3.1/bidding.d.mts +3 -0
  6. package/dist/lib/adapters/version/3.1/bidding.d.ts +3 -0
  7. package/dist/lib/adapters/version/3.1/bidding.js +115 -0
  8. package/dist/lib/adapters/version/3.1/bidding.mjs +90 -0
  9. package/dist/lib/adapters/version/3.1/delivery.d.mts +7 -0
  10. package/dist/lib/adapters/version/3.1/delivery.d.ts +7 -0
  11. package/dist/lib/adapters/version/3.1/delivery.js +78 -0
  12. package/dist/lib/adapters/version/3.1/delivery.mjs +54 -0
  13. package/dist/lib/adapters/version/3.1/index.d.mts +3 -0
  14. package/dist/lib/adapters/version/3.1/index.d.ts +3 -0
  15. package/dist/lib/adapters/version/3.1/index.js +36 -0
  16. package/dist/lib/adapters/version/3.1/index.mjs +12 -0
  17. package/dist/lib/adapters/version/index.d.mts +59 -7
  18. package/dist/lib/adapters/version/index.d.ts +59 -7
  19. package/dist/lib/adapters/version/index.js +51 -2
  20. package/dist/lib/adapters/version/index.mjs +49 -1
  21. package/dist/lib/adapters/version/negotiate.d.mts +71 -0
  22. package/dist/lib/adapters/version/negotiate.d.ts +71 -0
  23. package/dist/lib/adapters/version/negotiate.js +101 -0
  24. package/dist/lib/adapters/version/negotiate.mjs +73 -0
  25. package/dist/lib/adapters/version/schema-projection.d.mts +33 -0
  26. package/dist/lib/adapters/version/schema-projection.d.ts +33 -0
  27. package/dist/lib/adapters/version/schema-projection.js +294 -0
  28. package/dist/lib/adapters/version/schema-projection.mjs +269 -0
  29. package/dist/lib/adapters/version/translate.d.mts +122 -0
  30. package/dist/lib/adapters/version/translate.d.ts +122 -0
  31. package/dist/lib/adapters/version/translate.js +169 -0
  32. package/dist/lib/adapters/version/translate.mjs +141 -0
  33. package/dist/lib/adapters/version/types.d.mts +51 -10
  34. package/dist/lib/adapters/version/types.d.ts +51 -10
  35. package/dist/lib/core/AgentClient.d.mts +1 -1
  36. package/dist/lib/core/AgentClient.d.ts +1 -1
  37. package/dist/lib/core/ConversationTypes.d.mts +7 -0
  38. package/dist/lib/core/ConversationTypes.d.ts +7 -0
  39. package/dist/lib/core/SingleAgentClient.d.mts +82 -0
  40. package/dist/lib/core/SingleAgentClient.d.ts +82 -0
  41. package/dist/lib/core/SingleAgentClient.js +323 -15
  42. package/dist/lib/core/SingleAgentClient.mjs +337 -17
  43. package/dist/lib/core/TaskExecutor.d.mts +1 -1
  44. package/dist/lib/core/TaskExecutor.d.ts +1 -1
  45. package/dist/lib/core/TaskExecutor.js +97 -21
  46. package/dist/lib/core/TaskExecutor.mjs +97 -21
  47. package/dist/lib/core/account-reduction.d.mts +36 -0
  48. package/dist/lib/core/account-reduction.d.ts +36 -0
  49. package/dist/lib/core/account-reduction.js +127 -0
  50. package/dist/lib/core/account-reduction.mjs +98 -0
  51. package/dist/lib/core/buyer-account-registry.d.mts +52 -0
  52. package/dist/lib/core/buyer-account-registry.d.ts +52 -0
  53. package/dist/lib/core/buyer-account-registry.js +142 -6
  54. package/dist/lib/core/buyer-account-registry.mjs +142 -6
  55. package/dist/lib/media-buy/action-contracts.js +4 -4
  56. package/dist/lib/media-buy/action-contracts.mjs +4 -4
  57. package/dist/lib/media-buy/action-types.d.mts +3 -2
  58. package/dist/lib/media-buy/action-types.d.ts +3 -2
  59. package/dist/lib/media-buy/update-fields.generated.d.mts +3 -2
  60. package/dist/lib/media-buy/update-fields.generated.d.ts +3 -2
  61. package/dist/lib/registry/types.generated.d.mts +1 -1
  62. package/dist/lib/registry/types.generated.d.ts +1 -1
  63. package/dist/lib/server/create-adcp-server.d.mts +21 -0
  64. package/dist/lib/server/create-adcp-server.d.ts +21 -0
  65. package/dist/lib/server/create-adcp-server.js +97 -15
  66. package/dist/lib/server/create-adcp-server.mjs +83 -1
  67. package/dist/lib/server/index.d.mts +1 -1
  68. package/dist/lib/server/index.d.ts +1 -1
  69. package/dist/lib/storage/interfaces.d.mts +2 -0
  70. package/dist/lib/storage/interfaces.d.ts +2 -0
  71. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  72. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  73. package/dist/lib/version.d.mts +3 -3
  74. package/dist/lib/version.d.ts +3 -3
  75. package/dist/lib/version.js +3 -3
  76. package/dist/lib/version.mjs +3 -3
  77. package/docs/TYPE-SUMMARY.md +2 -2
  78. package/docs/llms.txt +3 -3
  79. package/docs/migration-14.x-rc-worksheet.md +4 -4
  80. package/package.json +1 -1
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Chained conversion between AdCP minor releases.
3
+ *
4
+ * Application code builds requests in the shape of the client's own release
5
+ * and reads responses in that shape. When the negotiated wire release is
6
+ * older, requests are converted down one minor at a time (3.2 → 3.1 → 3.0)
7
+ * and responses are converted back up the same steps in reverse.
8
+ *
9
+ * Each downward step runs the step's hand-written adapter (renames and moves)
10
+ * and then the schema projection, which removes whatever the older release
11
+ * cannot express and reports it. Upward steps run hand-written adapters only:
12
+ * a field an older seller sends that the newer release dropped is left for
13
+ * the caller rather than silently discarded.
14
+ */
15
+ import type { VersionAdapter, VersionDrift } from './types';
16
+ /**
17
+ * How to convert a request whose account key uses fields the target release
18
+ * cannot express (AdCP 3.2 added `operator_unit`, `currency` and `timezone`
19
+ * to the account natural key, and `countries` to brand references).
20
+ *
21
+ * - `'refuse'`: fail the call before it is sent.
22
+ * - `'reduce'`: send the older key when that is unambiguous, and refuse when
23
+ * it would merge accounts. The client claims each reduced key for the one
24
+ * buyer key it came from; a second buyer key that would reach the same
25
+ * seller account is refused with `account_identity_collision`, so no two
26
+ * buyer accounts ever share one seller account. Every reduction is reported
27
+ * as an `account_identity_reduced` drift entry.
28
+ */
29
+ export type AccountIdentityDowngrade = 'refuse' | 'reduce';
30
+ export interface TranslationStep {
31
+ /** Release the step converts from (downward) / to (upward). */
32
+ upper: string;
33
+ /** Release the step converts to (downward) / from (upward). Also the adapter key. */
34
+ lower: string;
35
+ /** Registry key for the step's hand-written adapters, e.g. `'3.1'`. */
36
+ key: string;
37
+ }
38
+ export interface RequestTranslation<T = unknown> {
39
+ params: T;
40
+ drifts: VersionDrift[];
41
+ /**
42
+ * Account-identity fields the target release cannot express, with the
43
+ * release line that introduced each. Under `'refuse'` the caller must not
44
+ * send the request when this is non-empty.
45
+ */
46
+ identityFields: Array<{
47
+ path: string;
48
+ introducedIn: string;
49
+ }>;
50
+ /**
51
+ * Fields of a mutating request the target release cannot express and whose
52
+ * loss would change what the seller does. The caller must not send the
53
+ * request when this is non-empty.
54
+ */
55
+ refusedFields: Array<{
56
+ path: string;
57
+ introducedIn: string;
58
+ }>;
59
+ }
60
+ export interface ResponseTranslation<T = unknown> {
61
+ data: T;
62
+ drifts: VersionDrift[];
63
+ }
64
+ type AdapterLookup = (key: string, toolName: string) => VersionAdapter | undefined;
65
+ /**
66
+ * Steps between two releases, newest first. Empty when both are on the same
67
+ * minor line or `lower` is not older than `upper`.
68
+ */
69
+ export declare function translationSteps(upperVersion: string, lowerVersion: string): TranslationStep[];
70
+ /**
71
+ * Convert a request from `fromVersion` down to `toVersion`.
72
+ *
73
+ * Returns the original params reference when nothing changes. Identity
74
+ * fields are always reported in `identityFields`; they are removed only under
75
+ * `'reduce'`, which is also the only policy that emits
76
+ * `account_identity_reduced` drift.
77
+ */
78
+ export declare function convertRequestToRelease<T>(toolName: string, params: T, options: {
79
+ fromVersion: string;
80
+ toVersion: string;
81
+ accountIdentity: AccountIdentityDowngrade;
82
+ adapters: AdapterLookup;
83
+ /** Whether the request changes seller state (`isMutatingTask`). */
84
+ mutating: boolean;
85
+ }): RequestTranslation<T>;
86
+ /**
87
+ * Convert a response from the wire release `fromVersion` up to the client's
88
+ * `toVersion` through each step's hand-written `adaptResponse`.
89
+ */
90
+ export declare function convertResponseFromRelease<T>(toolName: string, data: T, options: {
91
+ fromVersion: string;
92
+ toVersion: string;
93
+ adapters: AdapterLookup;
94
+ }): ResponseTranslation<T>;
95
+ /**
96
+ * Server side: convert an older buyer's request from the served release
97
+ * `fromVersion` up to the server's release `toVersion` through each step's
98
+ * `upgradeRequest`. Upward steps never remove fields.
99
+ */
100
+ export declare function convertRequestFromRelease<T>(toolName: string, params: T, options: {
101
+ fromVersion: string;
102
+ toVersion: string;
103
+ adapters: AdapterLookup;
104
+ }): {
105
+ params: T;
106
+ drifts: VersionDrift[];
107
+ };
108
+ /**
109
+ * Server side: convert a handler's response from the server's release
110
+ * `fromVersion` down to the served release `toVersion`. Each step runs the
111
+ * hand-written `downgradeResponse`, then removes and reports what the older
112
+ * release's response schema cannot express.
113
+ */
114
+ export declare function convertResponseToRelease<T>(toolName: string, data: T, options: {
115
+ fromVersion: string;
116
+ toVersion: string;
117
+ adapters: AdapterLookup;
118
+ }): {
119
+ data: T;
120
+ drifts: VersionDrift[];
121
+ };
122
+ export {};
@@ -0,0 +1,169 @@
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 translate_exports = {};
20
+ __export(translate_exports, {
21
+ convertRequestFromRelease: () => convertRequestFromRelease,
22
+ convertRequestToRelease: () => convertRequestToRelease,
23
+ convertResponseFromRelease: () => convertResponseFromRelease,
24
+ convertResponseToRelease: () => convertResponseToRelease,
25
+ translationSteps: () => translationSteps
26
+ });
27
+ module.exports = __toCommonJS(translate_exports);
28
+ var import_negotiate = require('./negotiate.js');
29
+ var import_schema_projection = require('./schema-projection.js');
30
+ function translationSteps(upperVersion, lowerVersion) {
31
+ const upper = (0, import_negotiate.parseAdcpRelease)(upperVersion);
32
+ const lower = (0, import_negotiate.parseAdcpRelease)(lowerVersion);
33
+ if (!upper || !lower || upper.major !== lower.major || upper.minor <= lower.minor) return [];
34
+ const steps = [];
35
+ for (let minor = upper.minor; minor > lower.minor; minor--) {
36
+ const key = `${upper.major}.${minor - 1}`;
37
+ steps.push({
38
+ upper: minor === upper.minor ? upperVersion : `${upper.major}.${minor}`,
39
+ lower: minor - 1 === lower.minor ? lowerVersion : key,
40
+ key
41
+ });
42
+ }
43
+ return steps;
44
+ }
45
+ const IDENTITY_FIELD_PATTERNS = [
46
+ // Account references anywhere in a request (`account`, `accounts[n]`,
47
+ // `filters.accounts[n]`), including sync_accounts rows. AdCP 3.2 brand
48
+ // country sets are part of the key inside an account reference.
49
+ /(?:^|[.\]])accounts?(?:\[\d+\])?\.(?:operator_unit|currency|timezone|brand\.countries)$/,
50
+ // ...and scope a rights licensee.
51
+ /^(?:buyer|buyer_brand)\.countries$/
52
+ ];
53
+ function isIdentityDrop(drop) {
54
+ return drop.kind === "field" && IDENTITY_FIELD_PATTERNS.some((pattern) => pattern.test(drop.path));
55
+ }
56
+ const DESCRIPTIVE_FIELD_PATTERNS = [
57
+ /^(?:name|opportunity|governance_context)$/,
58
+ /^brand\.countries$/,
59
+ /(?:^|\.)pacing$/,
60
+ /\.creative_assignments\[\d+\]\.(?:rotation_mode|group_id|sequence_position)$/
61
+ ];
62
+ const UPDATE_TOOLS = /* @__PURE__ */ new Set(["update_media_buy"]);
63
+ function isDescriptiveDrop(toolName, drop) {
64
+ if (UPDATE_TOOLS.has(toolName)) return false;
65
+ return drop.kind === "field" && DESCRIPTIVE_FIELD_PATTERNS.some((pattern) => pattern.test(drop.path));
66
+ }
67
+ function droppedDrift(toolName, drops, step) {
68
+ const paths = drops.map((drop) => drop.path);
69
+ return {
70
+ type: "version_downgrade_fields_dropped",
71
+ message: `${toolName}: removed ${paths.length} field(s) AdCP ${(0, import_negotiate.adcpMinorLine)(step.lower) ?? step.lower} cannot express before sending to the seller: ${paths.join(", ")}.`,
72
+ strippedFields: paths,
73
+ fromVersion: step.upper,
74
+ toVersion: step.lower
75
+ };
76
+ }
77
+ function identityDrift(toolName, drops, step) {
78
+ const paths = drops.map((drop) => drop.path);
79
+ return {
80
+ type: "account_identity_reduced",
81
+ message: `${toolName}: account key reduced to AdCP ${(0, import_negotiate.adcpMinorLine)(step.lower) ?? step.lower} by removing ${paths.join(", ")}. The seller identifies the account by the reduced key; this buyer key is the only one allowed to use it.`,
82
+ strippedFields: paths,
83
+ fromVersion: step.upper,
84
+ toVersion: step.lower
85
+ };
86
+ }
87
+ function convertRequestToRelease(toolName, params, options) {
88
+ const drifts = [];
89
+ const identityFields = [];
90
+ const refusedFields = [];
91
+ let current = params;
92
+ for (const step of translationSteps(options.fromVersion, options.toVersion)) {
93
+ const adapter = options.adapters(step.key, toolName);
94
+ if (adapter?.adaptRequest) {
95
+ const adapted = adapter.adaptRequest(current);
96
+ current = adapted.params;
97
+ if (adapted.drift) drifts.push({ ...adapted.drift, fromVersion: step.upper, toVersion: step.lower });
98
+ }
99
+ const projected = (0, import_schema_projection.projectToolPayload)(toolName, "request", current, step.upper, step.lower);
100
+ if (projected.dropped.length === 0) continue;
101
+ const identity = projected.dropped.filter(isIdentityDrop);
102
+ const other = projected.dropped.filter((drop) => !isIdentityDrop(drop));
103
+ const introducedIn = (0, import_negotiate.adcpMinorLine)(step.upper) ?? step.upper;
104
+ identityFields.push(...identity.map((drop) => ({ path: drop.path, introducedIn })));
105
+ if (options.mutating) {
106
+ for (const drop of other) {
107
+ if (!isDescriptiveDrop(toolName, drop)) refusedFields.push({ path: drop.path, introducedIn });
108
+ }
109
+ }
110
+ current = projected.value;
111
+ if (other.length > 0) drifts.push(droppedDrift(toolName, other, step));
112
+ if (identity.length > 0 && options.accountIdentity === "reduce") {
113
+ drifts.push(identityDrift(toolName, identity, step));
114
+ }
115
+ }
116
+ return { params: current, drifts, identityFields, refusedFields };
117
+ }
118
+ function convertResponseFromRelease(toolName, data, options) {
119
+ const drifts = [];
120
+ let current = data;
121
+ for (const step of [...translationSteps(options.toVersion, options.fromVersion)].reverse()) {
122
+ const adapter = options.adapters(step.key, toolName);
123
+ if (!adapter?.adaptResponse) continue;
124
+ const adapted = adapter.adaptResponse(current);
125
+ current = adapted.data;
126
+ if (adapted.drift) drifts.push({ ...adapted.drift, fromVersion: step.lower, toVersion: step.upper });
127
+ }
128
+ return { data: current, drifts };
129
+ }
130
+ function convertRequestFromRelease(toolName, params, options) {
131
+ const drifts = [];
132
+ let current = params;
133
+ for (const step of [...translationSteps(options.toVersion, options.fromVersion)].reverse()) {
134
+ const adapter = options.adapters(step.key, toolName);
135
+ if (!adapter?.upgradeRequest) continue;
136
+ const adapted = adapter.upgradeRequest(current);
137
+ current = adapted.params;
138
+ if (adapted.drift) drifts.push({ ...adapted.drift, fromVersion: step.lower, toVersion: step.upper });
139
+ }
140
+ return { params: current, drifts };
141
+ }
142
+ function convertResponseToRelease(toolName, data, options) {
143
+ const drifts = [];
144
+ let current = data;
145
+ for (const step of translationSteps(options.fromVersion, options.toVersion)) {
146
+ const adapter = options.adapters(step.key, toolName);
147
+ if (adapter?.downgradeResponse) {
148
+ const adapted = adapter.downgradeResponse(current);
149
+ current = adapted.data;
150
+ if (adapted.drift) drifts.push({ ...adapted.drift, fromVersion: step.upper, toVersion: step.lower });
151
+ }
152
+ const projected = (0, import_schema_projection.projectToolPayload)(toolName, "sync", current, step.upper, step.lower);
153
+ if (projected.dropped.length === 0) continue;
154
+ current = projected.value;
155
+ drifts.push({
156
+ ...droppedDrift(toolName, projected.dropped, step),
157
+ message: `${toolName}: removed ${projected.dropped.length} response field(s) AdCP ${(0, import_negotiate.adcpMinorLine)(step.lower) ?? step.lower} cannot express: ${projected.dropped.map((d) => d.path).join(", ")}.`
158
+ });
159
+ }
160
+ return { data: current, drifts };
161
+ }
162
+ // Annotate the CommonJS export names for ESM import in node:
163
+ 0 && (module.exports = {
164
+ convertRequestFromRelease,
165
+ convertRequestToRelease,
166
+ convertResponseFromRelease,
167
+ convertResponseToRelease,
168
+ translationSteps
169
+ });
@@ -0,0 +1,141 @@
1
+ import { adcpMinorLine, parseAdcpRelease } from "./negotiate.mjs";
2
+ import { projectToolPayload } from "./schema-projection.mjs";
3
+ function translationSteps(upperVersion, lowerVersion) {
4
+ const upper = parseAdcpRelease(upperVersion);
5
+ const lower = parseAdcpRelease(lowerVersion);
6
+ if (!upper || !lower || upper.major !== lower.major || upper.minor <= lower.minor) return [];
7
+ const steps = [];
8
+ for (let minor = upper.minor; minor > lower.minor; minor--) {
9
+ const key = `${upper.major}.${minor - 1}`;
10
+ steps.push({
11
+ upper: minor === upper.minor ? upperVersion : `${upper.major}.${minor}`,
12
+ lower: minor - 1 === lower.minor ? lowerVersion : key,
13
+ key
14
+ });
15
+ }
16
+ return steps;
17
+ }
18
+ const IDENTITY_FIELD_PATTERNS = [
19
+ // Account references anywhere in a request (`account`, `accounts[n]`,
20
+ // `filters.accounts[n]`), including sync_accounts rows. AdCP 3.2 brand
21
+ // country sets are part of the key inside an account reference.
22
+ /(?:^|[.\]])accounts?(?:\[\d+\])?\.(?:operator_unit|currency|timezone|brand\.countries)$/,
23
+ // ...and scope a rights licensee.
24
+ /^(?:buyer|buyer_brand)\.countries$/
25
+ ];
26
+ function isIdentityDrop(drop) {
27
+ return drop.kind === "field" && IDENTITY_FIELD_PATTERNS.some((pattern) => pattern.test(drop.path));
28
+ }
29
+ const DESCRIPTIVE_FIELD_PATTERNS = [
30
+ /^(?:name|opportunity|governance_context)$/,
31
+ /^brand\.countries$/,
32
+ /(?:^|\.)pacing$/,
33
+ /\.creative_assignments\[\d+\]\.(?:rotation_mode|group_id|sequence_position)$/
34
+ ];
35
+ const UPDATE_TOOLS = /* @__PURE__ */ new Set(["update_media_buy"]);
36
+ function isDescriptiveDrop(toolName, drop) {
37
+ if (UPDATE_TOOLS.has(toolName)) return false;
38
+ return drop.kind === "field" && DESCRIPTIVE_FIELD_PATTERNS.some((pattern) => pattern.test(drop.path));
39
+ }
40
+ function droppedDrift(toolName, drops, step) {
41
+ const paths = drops.map((drop) => drop.path);
42
+ return {
43
+ type: "version_downgrade_fields_dropped",
44
+ message: `${toolName}: removed ${paths.length} field(s) AdCP ${adcpMinorLine(step.lower) ?? step.lower} cannot express before sending to the seller: ${paths.join(", ")}.`,
45
+ strippedFields: paths,
46
+ fromVersion: step.upper,
47
+ toVersion: step.lower
48
+ };
49
+ }
50
+ function identityDrift(toolName, drops, step) {
51
+ const paths = drops.map((drop) => drop.path);
52
+ return {
53
+ type: "account_identity_reduced",
54
+ message: `${toolName}: account key reduced to AdCP ${adcpMinorLine(step.lower) ?? step.lower} by removing ${paths.join(", ")}. The seller identifies the account by the reduced key; this buyer key is the only one allowed to use it.`,
55
+ strippedFields: paths,
56
+ fromVersion: step.upper,
57
+ toVersion: step.lower
58
+ };
59
+ }
60
+ function convertRequestToRelease(toolName, params, options) {
61
+ const drifts = [];
62
+ const identityFields = [];
63
+ const refusedFields = [];
64
+ let current = params;
65
+ for (const step of translationSteps(options.fromVersion, options.toVersion)) {
66
+ const adapter = options.adapters(step.key, toolName);
67
+ if (adapter?.adaptRequest) {
68
+ const adapted = adapter.adaptRequest(current);
69
+ current = adapted.params;
70
+ if (adapted.drift) drifts.push({ ...adapted.drift, fromVersion: step.upper, toVersion: step.lower });
71
+ }
72
+ const projected = projectToolPayload(toolName, "request", current, step.upper, step.lower);
73
+ if (projected.dropped.length === 0) continue;
74
+ const identity = projected.dropped.filter(isIdentityDrop);
75
+ const other = projected.dropped.filter((drop) => !isIdentityDrop(drop));
76
+ const introducedIn = adcpMinorLine(step.upper) ?? step.upper;
77
+ identityFields.push(...identity.map((drop) => ({ path: drop.path, introducedIn })));
78
+ if (options.mutating) {
79
+ for (const drop of other) {
80
+ if (!isDescriptiveDrop(toolName, drop)) refusedFields.push({ path: drop.path, introducedIn });
81
+ }
82
+ }
83
+ current = projected.value;
84
+ if (other.length > 0) drifts.push(droppedDrift(toolName, other, step));
85
+ if (identity.length > 0 && options.accountIdentity === "reduce") {
86
+ drifts.push(identityDrift(toolName, identity, step));
87
+ }
88
+ }
89
+ return { params: current, drifts, identityFields, refusedFields };
90
+ }
91
+ function convertResponseFromRelease(toolName, data, options) {
92
+ const drifts = [];
93
+ let current = data;
94
+ for (const step of [...translationSteps(options.toVersion, options.fromVersion)].reverse()) {
95
+ const adapter = options.adapters(step.key, toolName);
96
+ if (!adapter?.adaptResponse) continue;
97
+ const adapted = adapter.adaptResponse(current);
98
+ current = adapted.data;
99
+ if (adapted.drift) drifts.push({ ...adapted.drift, fromVersion: step.lower, toVersion: step.upper });
100
+ }
101
+ return { data: current, drifts };
102
+ }
103
+ function convertRequestFromRelease(toolName, params, options) {
104
+ const drifts = [];
105
+ let current = params;
106
+ for (const step of [...translationSteps(options.toVersion, options.fromVersion)].reverse()) {
107
+ const adapter = options.adapters(step.key, toolName);
108
+ if (!adapter?.upgradeRequest) continue;
109
+ const adapted = adapter.upgradeRequest(current);
110
+ current = adapted.params;
111
+ if (adapted.drift) drifts.push({ ...adapted.drift, fromVersion: step.lower, toVersion: step.upper });
112
+ }
113
+ return { params: current, drifts };
114
+ }
115
+ function convertResponseToRelease(toolName, data, options) {
116
+ const drifts = [];
117
+ let current = data;
118
+ for (const step of translationSteps(options.fromVersion, options.toVersion)) {
119
+ const adapter = options.adapters(step.key, toolName);
120
+ if (adapter?.downgradeResponse) {
121
+ const adapted = adapter.downgradeResponse(current);
122
+ current = adapted.data;
123
+ if (adapted.drift) drifts.push({ ...adapted.drift, fromVersion: step.upper, toVersion: step.lower });
124
+ }
125
+ const projected = projectToolPayload(toolName, "sync", current, step.upper, step.lower);
126
+ if (projected.dropped.length === 0) continue;
127
+ current = projected.value;
128
+ drifts.push({
129
+ ...droppedDrift(toolName, projected.dropped, step),
130
+ message: `${toolName}: removed ${projected.dropped.length} response field(s) AdCP ${adcpMinorLine(step.lower) ?? step.lower} cannot express: ${projected.dropped.map((d) => d.path).join(", ")}.`
131
+ });
132
+ }
133
+ return { data: current, drifts };
134
+ }
135
+ export {
136
+ convertRequestFromRelease,
137
+ convertRequestToRelease,
138
+ convertResponseFromRelease,
139
+ convertResponseToRelease,
140
+ translationSteps
141
+ };
@@ -1,10 +1,16 @@
1
1
  /**
2
- * Registry pattern for AdCP protocol-version request adapters.
2
+ * Registry pattern for AdCP protocol-version adapters.
3
3
  *
4
- * Each AdCP tool that needs adaptation when targeting a seller on an older
5
- * protocol version registers a `VersionAdapter`. The adapter's `adaptRequest`
6
- * strips or rewrites fields the target version does not accept, returning the
7
- * adapted params and an optional `VersionDrift` describing what changed.
4
+ * Each AdCP tool that needs hand-written adaptation between two consecutive
5
+ * minor releases registers a `VersionAdapter` under the older release's key.
6
+ * `adaptRequest` converts a request from the next minor down to the key's
7
+ * release, and `adaptResponse` converts a response from the key's release up
8
+ * to the next minor. Both return the (possibly modified) payload and an
9
+ * optional `VersionDrift` describing what changed.
10
+ *
11
+ * Hand-written adapters cover renames and moves. Fields the older release
12
+ * cannot express at all are removed by the schema-driven projection in
13
+ * `schema-projection.ts`, which runs after the adapter on each step.
8
14
  *
9
15
  * Adapters live in per-version directories (`version/<target>/`) and are
10
16
  * collected by the registry in `version/index.ts`. Adding support for a new
@@ -15,21 +21,56 @@ export interface VersionDrift {
15
21
  type: string;
16
22
  /** Human-readable description of what was stripped and why. */
17
23
  message: string;
18
- /** Names of the fields removed from the request. */
24
+ /** Names (or JSON paths) of the fields removed from the payload. */
19
25
  strippedFields?: string[];
26
+ /** Fields whose values were moved or rewritten rather than removed. */
27
+ movedFields?: Array<{
28
+ from: string;
29
+ to: string;
30
+ }>;
20
31
  /** Individual drift types when one adaptation aggregates multiple changes. */
21
32
  constituentTypes?: string[];
33
+ /** Release the payload was converted from. Set by the translation chain. */
34
+ fromVersion?: string;
35
+ /** Release the payload was converted to. Set by the translation chain. */
36
+ toVersion?: string;
22
37
  }
23
38
  export interface VersionAdapter {
24
39
  /** AdCP tool name this adapter handles (snake_case, e.g. `create_media_buy`). */
25
40
  readonly toolName: string;
26
41
  /**
27
- * Adapt a request for the target protocol version. Returns the (possibly
28
- * modified) params and an optional drift description. When no adaptation is
29
- * needed the original params reference is returned unchanged.
42
+ * Adapt a request from the next minor release down to this adapter's
43
+ * release. Returns the (possibly modified) params and an optional drift
44
+ * description. When no adaptation is needed the original params reference
45
+ * is returned unchanged.
46
+ */
47
+ adaptRequest?(params: unknown): {
48
+ params: unknown;
49
+ drift?: VersionDrift;
50
+ };
51
+ /**
52
+ * Adapt a response from this adapter's release up to the next minor
53
+ * release. Returns the original reference when nothing changes.
54
+ */
55
+ adaptResponse?(data: unknown): {
56
+ data: unknown;
57
+ drift?: VersionDrift;
58
+ };
59
+ /**
60
+ * Server side: adapt an older buyer's request from this adapter's release
61
+ * up to the next minor release, so handlers see one shape.
30
62
  */
31
- adaptRequest(params: unknown): {
63
+ upgradeRequest?(params: unknown): {
32
64
  params: unknown;
33
65
  drift?: VersionDrift;
34
66
  };
67
+ /**
68
+ * Server side: adapt a handler's response from the next minor release down
69
+ * to this adapter's release, before the schema projection removes what
70
+ * this release cannot express.
71
+ */
72
+ downgradeResponse?(data: unknown): {
73
+ data: unknown;
74
+ drift?: VersionDrift;
75
+ };
35
76
  }
@@ -1,10 +1,16 @@
1
1
  /**
2
- * Registry pattern for AdCP protocol-version request adapters.
2
+ * Registry pattern for AdCP protocol-version adapters.
3
3
  *
4
- * Each AdCP tool that needs adaptation when targeting a seller on an older
5
- * protocol version registers a `VersionAdapter`. The adapter's `adaptRequest`
6
- * strips or rewrites fields the target version does not accept, returning the
7
- * adapted params and an optional `VersionDrift` describing what changed.
4
+ * Each AdCP tool that needs hand-written adaptation between two consecutive
5
+ * minor releases registers a `VersionAdapter` under the older release's key.
6
+ * `adaptRequest` converts a request from the next minor down to the key's
7
+ * release, and `adaptResponse` converts a response from the key's release up
8
+ * to the next minor. Both return the (possibly modified) payload and an
9
+ * optional `VersionDrift` describing what changed.
10
+ *
11
+ * Hand-written adapters cover renames and moves. Fields the older release
12
+ * cannot express at all are removed by the schema-driven projection in
13
+ * `schema-projection.ts`, which runs after the adapter on each step.
8
14
  *
9
15
  * Adapters live in per-version directories (`version/<target>/`) and are
10
16
  * collected by the registry in `version/index.ts`. Adding support for a new
@@ -15,21 +21,56 @@ export interface VersionDrift {
15
21
  type: string;
16
22
  /** Human-readable description of what was stripped and why. */
17
23
  message: string;
18
- /** Names of the fields removed from the request. */
24
+ /** Names (or JSON paths) of the fields removed from the payload. */
19
25
  strippedFields?: string[];
26
+ /** Fields whose values were moved or rewritten rather than removed. */
27
+ movedFields?: Array<{
28
+ from: string;
29
+ to: string;
30
+ }>;
20
31
  /** Individual drift types when one adaptation aggregates multiple changes. */
21
32
  constituentTypes?: string[];
33
+ /** Release the payload was converted from. Set by the translation chain. */
34
+ fromVersion?: string;
35
+ /** Release the payload was converted to. Set by the translation chain. */
36
+ toVersion?: string;
22
37
  }
23
38
  export interface VersionAdapter {
24
39
  /** AdCP tool name this adapter handles (snake_case, e.g. `create_media_buy`). */
25
40
  readonly toolName: string;
26
41
  /**
27
- * Adapt a request for the target protocol version. Returns the (possibly
28
- * modified) params and an optional drift description. When no adaptation is
29
- * needed the original params reference is returned unchanged.
42
+ * Adapt a request from the next minor release down to this adapter's
43
+ * release. Returns the (possibly modified) params and an optional drift
44
+ * description. When no adaptation is needed the original params reference
45
+ * is returned unchanged.
46
+ */
47
+ adaptRequest?(params: unknown): {
48
+ params: unknown;
49
+ drift?: VersionDrift;
50
+ };
51
+ /**
52
+ * Adapt a response from this adapter's release up to the next minor
53
+ * release. Returns the original reference when nothing changes.
54
+ */
55
+ adaptResponse?(data: unknown): {
56
+ data: unknown;
57
+ drift?: VersionDrift;
58
+ };
59
+ /**
60
+ * Server side: adapt an older buyer's request from this adapter's release
61
+ * up to the next minor release, so handlers see one shape.
30
62
  */
31
- adaptRequest(params: unknown): {
63
+ upgradeRequest?(params: unknown): {
32
64
  params: unknown;
33
65
  drift?: VersionDrift;
34
66
  };
67
+ /**
68
+ * Server side: adapt a handler's response from the next minor release down
69
+ * to this adapter's release, before the schema projection removes what
70
+ * this release cannot express.
71
+ */
72
+ downgradeResponse?(data: unknown): {
73
+ data: unknown;
74
+ drift?: VersionDrift;
75
+ };
35
76
  }
@@ -160,7 +160,7 @@ export type TaskRequestFor<K extends AdcpTaskName> = TaskRequestTypeMap[K];
160
160
  * - `governance` — buyer-side governance config
161
161
  * - `requireV3ForMutations` — enforce AdCP v3 before dispatching mutating tools
162
162
  */
163
- export type InProcessAgentClientConfig = Pick<SingleAgentClientConfig, 'adcpVersion' | 'wireAdcpVersion' | 'versionEnvelope' | 'debug' | 'validation' | 'governance' | 'onActivity' | 'legacyFormatConverter' | 'projectionCatalogs' | 'canonicalFormatLegacyResolver' | 'validateFeatures' | 'requireV3ForMutations' | 'workingTimeout' | 'defaultMaxClarifications' | 'persistConversations' | 'deferredStorage' | 'resolveDeferredAgent' | 'deferredTaskTtlSeconds'> & {
163
+ export type InProcessAgentClientConfig = Pick<SingleAgentClientConfig, 'adcpVersion' | 'wireAdcpVersion' | 'versionEnvelope' | 'debug' | 'validation' | 'governance' | 'onActivity' | 'legacyFormatConverter' | 'projectionCatalogs' | 'canonicalFormatLegacyResolver' | 'validateFeatures' | 'requireV3ForMutations' | 'accountIdentityDowngrade' | 'workingTimeout' | 'defaultMaxClarifications' | 'persistConversations' | 'deferredStorage' | 'resolveDeferredAgent' | 'deferredTaskTtlSeconds'> & {
164
164
  /**
165
165
  * Human-readable name for this agent, used in debug logs and
166
166
  * `getAgentName()`. Defaults to `'in-process'`.
@@ -160,7 +160,7 @@ export type TaskRequestFor<K extends AdcpTaskName> = TaskRequestTypeMap[K];
160
160
  * - `governance` — buyer-side governance config
161
161
  * - `requireV3ForMutations` — enforce AdCP v3 before dispatching mutating tools
162
162
  */
163
- export type InProcessAgentClientConfig = Pick<SingleAgentClientConfig, 'adcpVersion' | 'wireAdcpVersion' | 'versionEnvelope' | 'debug' | 'validation' | 'governance' | 'onActivity' | 'legacyFormatConverter' | 'projectionCatalogs' | 'canonicalFormatLegacyResolver' | 'validateFeatures' | 'requireV3ForMutations' | 'workingTimeout' | 'defaultMaxClarifications' | 'persistConversations' | 'deferredStorage' | 'resolveDeferredAgent' | 'deferredTaskTtlSeconds'> & {
163
+ export type InProcessAgentClientConfig = Pick<SingleAgentClientConfig, 'adcpVersion' | 'wireAdcpVersion' | 'versionEnvelope' | 'debug' | 'validation' | 'governance' | 'onActivity' | 'legacyFormatConverter' | 'projectionCatalogs' | 'canonicalFormatLegacyResolver' | 'validateFeatures' | 'requireV3ForMutations' | 'accountIdentityDowngrade' | 'workingTimeout' | 'defaultMaxClarifications' | 'persistConversations' | 'deferredStorage' | 'resolveDeferredAgent' | 'deferredTaskTtlSeconds'> & {
164
164
  /**
165
165
  * Human-readable name for this agent, used in debug logs and
166
166
  * `getAgentName()`. Defaults to `'in-process'`.
@@ -503,6 +503,13 @@ export interface TaskResultMetadata {
503
503
  * configuration pin, when detecting same-major downshift.
504
504
  */
505
505
  adcpVersion?: string;
506
+ /**
507
+ * Older AdCP release negotiated with the seller for this call. Present only
508
+ * when it differs from the client's release: the request was converted down
509
+ * to it and the response converted back up (see `debug_logs` for every
510
+ * field the conversion removed or moved).
511
+ */
512
+ negotiatedAdcpVersion?: string;
506
513
  /** Seller wire generation used for this request after capability discovery. */
507
514
  serverVersion?: 'v2' | 'v3';
508
515
  /** True when {@link serverVersion} came from the SDK's synthetic fallback. */
@@ -503,6 +503,13 @@ export interface TaskResultMetadata {
503
503
  * configuration pin, when detecting same-major downshift.
504
504
  */
505
505
  adcpVersion?: string;
506
+ /**
507
+ * Older AdCP release negotiated with the seller for this call. Present only
508
+ * when it differs from the client's release: the request was converted down
509
+ * to it and the response converted back up (see `debug_logs` for every
510
+ * field the conversion removed or moved).
511
+ */
512
+ negotiatedAdcpVersion?: string;
506
513
  /** Seller wire generation used for this request after capability discovery. */
507
514
  serverVersion?: 'v2' | 'v3';
508
515
  /** True when {@link serverVersion} came from the SDK's synthetic fallback. */