@adcp/sdk 8.1.0-beta.11 → 8.1.0-beta.12

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 (76) hide show
  1. package/dist/lib/index.d.ts +4 -0
  2. package/dist/lib/index.d.ts.map +1 -1
  3. package/dist/lib/index.js +20 -10
  4. package/dist/lib/index.js.map +1 -1
  5. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  6. package/dist/lib/server/create-adcp-server.d.ts +2 -2
  7. package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
  8. package/dist/lib/server/create-adcp-server.js +46 -15
  9. package/dist/lib/server/create-adcp-server.js.map +1 -1
  10. package/dist/lib/server/decisioning/async-outcome.d.ts +4 -8
  11. package/dist/lib/server/decisioning/async-outcome.d.ts.map +1 -1
  12. package/dist/lib/server/decisioning/async-outcome.js +6 -15
  13. package/dist/lib/server/decisioning/async-outcome.js.map +1 -1
  14. package/dist/lib/server/decisioning/proposal/dispatch.d.ts +4 -5
  15. package/dist/lib/server/decisioning/proposal/dispatch.d.ts.map +1 -1
  16. package/dist/lib/server/decisioning/proposal/dispatch.js.map +1 -1
  17. package/dist/lib/server/decisioning/proposal/mock-manager.d.ts +4 -5
  18. package/dist/lib/server/decisioning/proposal/mock-manager.d.ts.map +1 -1
  19. package/dist/lib/server/decisioning/proposal/mock-manager.js.map +1 -1
  20. package/dist/lib/server/decisioning/proposal/types.d.ts +4 -3
  21. package/dist/lib/server/decisioning/proposal/types.d.ts.map +1 -1
  22. package/dist/lib/server/decisioning/proposal/types.js.map +1 -1
  23. package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
  24. package/dist/lib/server/decisioning/specialisms/sales.d.ts +2 -2
  25. package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -1
  26. package/dist/lib/server/operational-platform.d.ts +2 -2
  27. package/dist/lib/server/operational-platform.d.ts.map +1 -1
  28. package/dist/lib/server/responses.d.ts +2 -2
  29. package/dist/lib/server/responses.d.ts.map +1 -1
  30. package/dist/lib/server/responses.js +2 -1
  31. package/dist/lib/server/responses.js.map +1 -1
  32. package/dist/lib/types/server-payload-aliases.d.ts +2 -2
  33. package/dist/lib/types/server-payload-aliases.d.ts.map +1 -1
  34. package/dist/lib/types/server-payload.d.ts +32 -0
  35. package/dist/lib/types/server-payload.d.ts.map +1 -1
  36. package/dist/lib/utils/envelope-status-compat.d.ts +12 -0
  37. package/dist/lib/utils/envelope-status-compat.d.ts.map +1 -1
  38. package/dist/lib/utils/envelope-status-compat.js +1 -0
  39. package/dist/lib/utils/envelope-status-compat.js.map +1 -1
  40. package/dist/lib/utils/response-schemas.d.ts.map +1 -1
  41. package/dist/lib/utils/response-schemas.js +22 -1
  42. package/dist/lib/utils/response-schemas.js.map +1 -1
  43. package/dist/lib/utils/signal-discovery-helpers.d.ts +46 -0
  44. package/dist/lib/utils/signal-discovery-helpers.d.ts.map +1 -0
  45. package/dist/lib/utils/signal-discovery-helpers.js +94 -0
  46. package/dist/lib/utils/signal-discovery-helpers.js.map +1 -0
  47. package/dist/lib/validation/hints.d.ts +3 -0
  48. package/dist/lib/validation/hints.d.ts.map +1 -1
  49. package/dist/lib/validation/hints.js +11 -0
  50. package/dist/lib/validation/hints.js.map +1 -1
  51. package/dist/lib/version.d.ts +3 -3
  52. package/dist/lib/version.js +3 -3
  53. package/dist/lib/webhooks/index.d.ts +2 -0
  54. package/dist/lib/webhooks/index.d.ts.map +1 -1
  55. package/dist/lib/webhooks/index.js +5 -0
  56. package/dist/lib/webhooks/index.js.map +1 -1
  57. package/dist/lib/wholesale-feed-sync/index.d.ts +2 -0
  58. package/dist/lib/wholesale-feed-sync/index.d.ts.map +1 -1
  59. package/dist/lib/wholesale-feed-sync/index.js +5 -1
  60. package/dist/lib/wholesale-feed-sync/index.js.map +1 -1
  61. package/dist/lib/wholesale-feed-sync/sync.js +1 -1
  62. package/dist/lib/wholesale-feed-sync/sync.js.map +1 -1
  63. package/dist/lib/wholesale-feed-sync/webhook-notification.d.ts +54 -0
  64. package/dist/lib/wholesale-feed-sync/webhook-notification.d.ts.map +1 -0
  65. package/dist/lib/wholesale-feed-sync/webhook-notification.js +232 -0
  66. package/dist/lib/wholesale-feed-sync/webhook-notification.js.map +1 -0
  67. package/docs/guides/BUILD-AN-AGENT.md +33 -17
  68. package/docs/llms.txt +13 -3
  69. package/examples/decisioning-platform-broadcast-tv.ts +3 -3
  70. package/examples/decisioning-platform-mock-seller.ts +3 -3
  71. package/examples/decisioning-platform-programmatic.ts +3 -3
  72. package/examples/hello_seller_adapter_proposal_mode.ts +6 -7
  73. package/package.json +1 -1
  74. package/skills/build-decisioning-platform/SKILL.md +1 -0
  75. package/skills/build-decisioning-platform/advanced/REFERENCE.md +2 -1
  76. package/skills/call-adcp-agent/SKILL.md +1 -1
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeWholesaleFeedWebhookNotification = exports.WholesaleFeedWebhookNotificationError = void 0;
4
+ exports.parseWholesaleFeedWebhookNotification = parseWholesaleFeedWebhookNotification;
5
+ const errors_1 = require("../errors");
6
+ class WholesaleFeedWebhookNotificationError extends errors_1.ADCPError {
7
+ code;
8
+ field;
9
+ constructor(code, message, details = {}) {
10
+ super(message, details);
11
+ this.code = code;
12
+ this.field = details.field;
13
+ }
14
+ }
15
+ exports.WholesaleFeedWebhookNotificationError = WholesaleFeedWebhookNotificationError;
16
+ const WHOLESALE_FEED_NOTIFICATION_TYPES = new Set([
17
+ 'product.created',
18
+ 'product.updated',
19
+ 'product.priced',
20
+ 'product.removed',
21
+ 'signal.created',
22
+ 'signal.updated',
23
+ 'signal.priced',
24
+ 'signal.removed',
25
+ 'wholesale_feed.bulk_change',
26
+ ]);
27
+ /**
28
+ * Parse and normalize a canonical wholesale-feed webhook notification.
29
+ *
30
+ * The normalized shape intentionally separates `idempotencyKey` (delivery
31
+ * replay/dedupe) from `eventId` / `notificationId` (domain event identity).
32
+ * It validates envelope-level invariants, including the canonical
33
+ * `notification_id === event.event_id` match.
34
+ */
35
+ function parseWholesaleFeedWebhookNotification(input) {
36
+ const parsed = parseInput(input);
37
+ const webhook = readRecord(parsed, '$');
38
+ const event = readRecord(webhook.event, 'event');
39
+ const idempotencyKey = readRequiredString(webhook, 'idempotency_key');
40
+ const notificationId = readRequiredString(webhook, 'notification_id');
41
+ const notificationType = readNotificationType(readRequiredString(webhook, 'notification_type'), 'notification_type');
42
+ const firedAt = readRequiredString(webhook, 'fired_at');
43
+ const subscriberId = readRequiredString(webhook, 'subscriber_id');
44
+ const accountId = readRequiredString(webhook, 'account_id');
45
+ const wholesaleFeedVersion = readRequiredString(webhook, 'wholesale_feed_version');
46
+ const previousWholesaleFeedVersion = readOptionalString(webhook, 'previous_wholesale_feed_version');
47
+ const cacheScope = readCacheScope(readRequiredString(webhook, 'cache_scope'), 'cache_scope');
48
+ const eventId = readRequiredString(event, 'event.event_id');
49
+ if (notificationId !== eventId) {
50
+ throw new WholesaleFeedWebhookNotificationError('wholesale_feed_webhook_field_invalid', 'wholesale-feed webhook notification_id must match event.event_id.', { field: 'notification_id', expected: eventId, actual: notificationId });
51
+ }
52
+ const eventType = readNotificationType(readRequiredString(event, 'event.event_type'), 'event.event_type');
53
+ if (notificationType !== eventType) {
54
+ throw new WholesaleFeedWebhookNotificationError('wholesale_feed_webhook_notification_type_mismatch', 'wholesale-feed webhook notification_type must match event.event_type.', { field: 'notification_type', expected: eventType, actual: notificationType });
55
+ }
56
+ const entityType = readEntityType(readRequiredString(event, 'event.entity_type'), 'event.entity_type');
57
+ assertEntityTypeMatchesNotification(notificationType, entityType);
58
+ const entityId = readRequiredString(event, 'event.entity_id');
59
+ const eventCreatedAt = readRequiredString(event, 'event.created_at');
60
+ const payload = readRecord(event.payload, 'event.payload');
61
+ const payloadScope = readRequiredPayloadScope(payload);
62
+ if (payloadScope !== cacheScope) {
63
+ throw new WholesaleFeedWebhookNotificationError('wholesale_feed_webhook_cache_scope_mismatch', 'wholesale-feed webhook cache_scope must match event.payload.applies_to.scope.', { field: 'cache_scope', expected: payloadScope, actual: cacheScope });
64
+ }
65
+ const affectedEntityType = validatePayloadForEvent(notificationType, entityId, payload);
66
+ return {
67
+ idempotencyKey,
68
+ notificationId,
69
+ notificationType,
70
+ accountId,
71
+ subscriberId,
72
+ firedAt,
73
+ wholesaleFeedVersion,
74
+ ...(previousWholesaleFeedVersion !== undefined && { previousWholesaleFeedVersion }),
75
+ cacheScope,
76
+ eventId,
77
+ eventType,
78
+ entityType,
79
+ entityId,
80
+ eventCreatedAt,
81
+ ...(affectedEntityType !== undefined && { affectedEntityType }),
82
+ event: event,
83
+ webhook: webhook,
84
+ };
85
+ }
86
+ exports.normalizeWholesaleFeedWebhookNotification = parseWholesaleFeedWebhookNotification;
87
+ function parseInput(input) {
88
+ if (typeof input === 'string')
89
+ return parseJson(input);
90
+ if (input instanceof Uint8Array)
91
+ return parseJson(new TextDecoder().decode(input));
92
+ return input;
93
+ }
94
+ function parseJson(raw) {
95
+ try {
96
+ return JSON.parse(raw);
97
+ }
98
+ catch (err) {
99
+ throw new WholesaleFeedWebhookNotificationError('wholesale_feed_webhook_body_malformed', 'wholesale-feed webhook body must be valid JSON.', { field: '$', actual: err instanceof Error ? err.message : String(err) });
100
+ }
101
+ }
102
+ function readRecord(value, field) {
103
+ if (value && typeof value === 'object' && !Array.isArray(value))
104
+ return value;
105
+ throw new WholesaleFeedWebhookNotificationError(field === '$' ? 'wholesale_feed_webhook_body_malformed' : 'wholesale_feed_webhook_field_invalid', field === '$'
106
+ ? 'wholesale-feed webhook body must be a JSON object.'
107
+ : `wholesale-feed webhook field ${field} must be an object.`, { field, expected: 'object', actual: describeValue(value) });
108
+ }
109
+ function readRequiredRecord(obj, field) {
110
+ const key = field.includes('.') ? field.slice(field.lastIndexOf('.') + 1) : field;
111
+ const value = obj[key];
112
+ if (value === undefined) {
113
+ throw new WholesaleFeedWebhookNotificationError('wholesale_feed_webhook_field_missing', `wholesale-feed webhook field ${field} must be an object.`, { field, expected: 'object', actual: describeValue(value) });
114
+ }
115
+ return readRecord(value, field);
116
+ }
117
+ function readRequiredString(obj, field) {
118
+ const key = field.includes('.') ? field.slice(field.lastIndexOf('.') + 1) : field;
119
+ const value = obj[key];
120
+ if (typeof value === 'string' && value.length > 0)
121
+ return value;
122
+ throw new WholesaleFeedWebhookNotificationError(value === undefined ? 'wholesale_feed_webhook_field_missing' : 'wholesale_feed_webhook_field_invalid', `wholesale-feed webhook field ${field} must be a non-empty string.`, { field, expected: 'non-empty string', actual: describeValue(value) });
123
+ }
124
+ function readRequiredNonEmptyArray(obj, field) {
125
+ const key = field.includes('.') ? field.slice(field.lastIndexOf('.') + 1) : field;
126
+ const value = obj[key];
127
+ if (Array.isArray(value) && value.length > 0)
128
+ return value;
129
+ throw new WholesaleFeedWebhookNotificationError(value === undefined ? 'wholesale_feed_webhook_field_missing' : 'wholesale_feed_webhook_field_invalid', `wholesale-feed webhook field ${field} must be a non-empty array.`, { field, expected: 'non-empty array', actual: describeValue(value) });
130
+ }
131
+ function readRequiredPositiveInteger(obj, field) {
132
+ const key = field.includes('.') ? field.slice(field.lastIndexOf('.') + 1) : field;
133
+ const value = obj[key];
134
+ if (Number.isInteger(value) && value > 0)
135
+ return value;
136
+ throw new WholesaleFeedWebhookNotificationError(value === undefined ? 'wholesale_feed_webhook_field_missing' : 'wholesale_feed_webhook_field_invalid', `wholesale-feed webhook field ${field} must be a positive integer.`, { field, expected: 'positive integer', actual: describeValue(value) });
137
+ }
138
+ function readOptionalString(obj, field) {
139
+ const value = obj[field];
140
+ if (value === undefined)
141
+ return undefined;
142
+ if (typeof value === 'string' && value.length > 0)
143
+ return value;
144
+ throw new WholesaleFeedWebhookNotificationError('wholesale_feed_webhook_field_invalid', `wholesale-feed webhook field ${field} must be a non-empty string when present.`, { field, expected: 'non-empty string', actual: describeValue(value) });
145
+ }
146
+ function readNotificationType(value, field) {
147
+ if (WHOLESALE_FEED_NOTIFICATION_TYPES.has(value)) {
148
+ return value;
149
+ }
150
+ throw new WholesaleFeedWebhookNotificationError('wholesale_feed_webhook_field_invalid', `wholesale-feed webhook field ${field} is not a supported wholesale-feed notification type.`, { field, expected: [...WHOLESALE_FEED_NOTIFICATION_TYPES], actual: value });
151
+ }
152
+ function readCacheScope(value, field) {
153
+ if (value === 'public' || value === 'account')
154
+ return value;
155
+ throw new WholesaleFeedWebhookNotificationError('wholesale_feed_webhook_field_invalid', `wholesale-feed webhook field ${field} must be "public" or "account".`, { field, expected: ['public', 'account'], actual: value });
156
+ }
157
+ function readEntityType(value, field) {
158
+ if (value === 'product' || value === 'signal' || value === 'feed')
159
+ return value;
160
+ throw new WholesaleFeedWebhookNotificationError('wholesale_feed_webhook_field_invalid', `wholesale-feed webhook field ${field} must be "product", "signal", or "feed".`, { field, expected: ['product', 'signal', 'feed'], actual: value });
161
+ }
162
+ function assertEntityTypeMatchesNotification(notificationType, entityType) {
163
+ const expected = notificationType.startsWith('product.')
164
+ ? 'product'
165
+ : notificationType.startsWith('signal.')
166
+ ? 'signal'
167
+ : 'feed';
168
+ if (entityType !== expected) {
169
+ throw new WholesaleFeedWebhookNotificationError('wholesale_feed_webhook_entity_type_mismatch', 'wholesale-feed webhook event.entity_type does not match event.event_type.', { field: 'event.entity_type', expected, actual: entityType });
170
+ }
171
+ }
172
+ function validatePayloadForEvent(notificationType, entityId, payload) {
173
+ switch (notificationType) {
174
+ case 'product.created':
175
+ case 'product.updated': {
176
+ assertEntityIdMatchesPayload(entityId, readRequiredString(payload, 'event.payload.product_id'), 'product_id');
177
+ readRequiredRecord(payload, 'event.payload.product');
178
+ return undefined;
179
+ }
180
+ case 'product.priced': {
181
+ assertEntityIdMatchesPayload(entityId, readRequiredString(payload, 'event.payload.product_id'), 'product_id');
182
+ readRequiredNonEmptyArray(payload, 'event.payload.pricing_options');
183
+ return undefined;
184
+ }
185
+ case 'product.removed': {
186
+ assertEntityIdMatchesPayload(entityId, readRequiredString(payload, 'event.payload.product_id'), 'product_id');
187
+ return undefined;
188
+ }
189
+ case 'signal.created':
190
+ case 'signal.updated': {
191
+ assertEntityIdMatchesPayload(entityId, readRequiredString(payload, 'event.payload.signal_agent_segment_id'), 'signal_agent_segment_id');
192
+ readRequiredRecord(payload, 'event.payload.signal');
193
+ return undefined;
194
+ }
195
+ case 'signal.priced': {
196
+ assertEntityIdMatchesPayload(entityId, readRequiredString(payload, 'event.payload.signal_agent_segment_id'), 'signal_agent_segment_id');
197
+ readRequiredNonEmptyArray(payload, 'event.payload.pricing_options');
198
+ return undefined;
199
+ }
200
+ case 'signal.removed': {
201
+ assertEntityIdMatchesPayload(entityId, readRequiredString(payload, 'event.payload.signal_agent_segment_id'), 'signal_agent_segment_id');
202
+ return undefined;
203
+ }
204
+ case 'wholesale_feed.bulk_change':
205
+ readRequiredString(payload, 'event.payload.summary');
206
+ readRequiredPositiveInteger(payload, 'event.payload.affected_count');
207
+ return readBulkChangeAffectedEntityType(payload);
208
+ }
209
+ }
210
+ function assertEntityIdMatchesPayload(entityId, payloadEntityId, payloadField) {
211
+ if (entityId === payloadEntityId)
212
+ return;
213
+ throw new WholesaleFeedWebhookNotificationError('wholesale_feed_webhook_field_invalid', `wholesale-feed webhook event.entity_id must match event.payload.${payloadField}.`, { field: 'event.entity_id', expected: payloadEntityId, actual: entityId });
214
+ }
215
+ function readRequiredPayloadScope(payload) {
216
+ const appliesToRecord = readRequiredRecord(payload, 'event.payload.applies_to');
217
+ return readCacheScope(readRequiredString(appliesToRecord, 'event.payload.applies_to.scope'), 'event.payload.applies_to.scope');
218
+ }
219
+ function readBulkChangeAffectedEntityType(payload) {
220
+ const affected = payload.affected_entity_type;
221
+ if (affected === 'product' || affected === 'signal')
222
+ return affected;
223
+ throw new WholesaleFeedWebhookNotificationError('wholesale_feed_webhook_bulk_change_invalid', 'wholesale_feed.bulk_change payload requires affected_entity_type of "product" or "signal".', { field: 'event.payload.affected_entity_type', expected: ['product', 'signal'], actual: describeValue(affected) });
224
+ }
225
+ function describeValue(value) {
226
+ if (value === null)
227
+ return 'null';
228
+ if (Array.isArray(value))
229
+ return 'array';
230
+ return typeof value;
231
+ }
232
+ //# sourceMappingURL=webhook-notification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-notification.js","sourceRoot":"","sources":["../../../src/lib/wholesale-feed-sync/webhook-notification.ts"],"names":[],"mappings":";;;AAqFA,sFAqEC;AA1JD,sCAAsC;AAsBtC,MAAa,qCAAsC,SAAQ,kBAAS;IACzD,IAAI,CAA4C;IAChD,KAAK,CAAqB;IAEnC,YACE,IAA+C,EAC/C,OAAe,EACf,UAAwD,EAAE;QAE1D,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC7B,CAAC;CACF;AAbD,sFAaC;AA8BD,MAAM,iCAAiC,GAAG,IAAI,GAAG,CAAuC;IACtF,iBAAiB;IACjB,iBAAiB;IACjB,gBAAgB;IAChB,iBAAiB;IACjB,gBAAgB;IAChB,gBAAgB;IAChB,eAAe;IACf,gBAAgB;IAChB,4BAA4B;CAC7B,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,SAAgB,qCAAqC,CAAC,KAAc;IAClE,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IACjC,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAEjD,MAAM,cAAc,GAAG,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;IACtE,MAAM,cAAc,GAAG,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;IACtE,MAAM,gBAAgB,GAAG,oBAAoB,CAAC,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,CAAC,EAAE,mBAAmB,CAAC,CAAC;IACrH,MAAM,OAAO,GAAG,kBAAkB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACxD,MAAM,YAAY,GAAG,kBAAkB,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;IAClE,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC5D,MAAM,oBAAoB,GAAG,kBAAkB,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;IACnF,MAAM,4BAA4B,GAAG,kBAAkB,CAAC,OAAO,EAAE,iCAAiC,CAAC,CAAC;IACpG,MAAM,UAAU,GAAG,cAAc,CAAC,kBAAkB,CAAC,OAAO,EAAE,aAAa,CAAC,EAAE,aAAa,CAAC,CAAC;IAE7F,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;IAC5D,IAAI,cAAc,KAAK,OAAO,EAAE,CAAC;QAC/B,MAAM,IAAI,qCAAqC,CAC7C,sCAAsC,EACtC,mEAAmE,EACnE,EAAE,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CACxE,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,CAAC,EAAE,kBAAkB,CAAC,CAAC;IAC1G,IAAI,gBAAgB,KAAK,SAAS,EAAE,CAAC;QACnC,MAAM,IAAI,qCAAqC,CAC7C,mDAAmD,EACnD,uEAAuE,EACvE,EAAE,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAC9E,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,cAAc,CAAC,kBAAkB,CAAC,KAAK,EAAE,mBAAmB,CAAC,EAAE,mBAAmB,CAAC,CAAC;IACvG,mCAAmC,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;IAC9D,MAAM,cAAc,GAAG,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;IAE3D,MAAM,YAAY,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAC;IACvD,IAAI,YAAY,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,IAAI,qCAAqC,CAC7C,6CAA6C,EAC7C,+EAA+E,EAC/E,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,CACrE,CAAC;IACJ,CAAC;IAED,MAAM,kBAAkB,GAAG,uBAAuB,CAAC,gBAAgB,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAExF,OAAO;QACL,cAAc;QACd,cAAc;QACd,gBAAgB;QAChB,SAAS;QACT,YAAY;QACZ,OAAO;QACP,oBAAoB;QACpB,GAAG,CAAC,4BAA4B,KAAK,SAAS,IAAI,EAAE,4BAA4B,EAAE,CAAC;QACnF,UAAU;QACV,OAAO;QACP,SAAS;QACT,UAAU;QACV,QAAQ;QACR,cAAc;QACd,GAAG,CAAC,kBAAkB,KAAK,SAAS,IAAI,EAAE,kBAAkB,EAAE,CAAC;QAC/D,KAAK,EAAE,KAAmC;QAC1C,OAAO,EAAE,OAAuC;KACjD,CAAC;AACJ,CAAC;AAEY,QAAA,yCAAyC,GAAG,qCAAqC,CAAC;AAE/F,SAAS,UAAU,CAAC,KAAc;IAChC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;IACvD,IAAI,KAAK,YAAY,UAAU;QAAE,OAAO,SAAS,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACnF,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,qCAAqC,CAC7C,uCAAuC,EACvC,iDAAiD,EACjD,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACzE,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,KAAc,EAAE,KAAa;IAC/C,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAgC,CAAC;IACzG,MAAM,IAAI,qCAAqC,CAC7C,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,uCAAuC,CAAC,CAAC,CAAC,sCAAsC,EAChG,KAAK,KAAK,GAAG;QACX,CAAC,CAAC,oDAAoD;QACtD,CAAC,CAAC,gCAAgC,KAAK,qBAAqB,EAC9D,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,CAC5D,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,GAA4B,EAAE,KAAa;IACrE,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAClF,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,qCAAqC,CAC7C,sCAAsC,EACtC,gCAAgC,KAAK,qBAAqB,EAC1D,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,CAC5D,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,kBAAkB,CAAC,GAA4B,EAAE,KAAa;IACrE,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAClF,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAChE,MAAM,IAAI,qCAAqC,CAC7C,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,CAAC,sCAAsC,EACrG,gCAAgC,KAAK,8BAA8B,EACnE,EAAE,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,CACtE,CAAC;AACJ,CAAC;AAED,SAAS,yBAAyB,CAAC,GAA4B,EAAE,KAAa;IAC5E,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAClF,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3D,MAAM,IAAI,qCAAqC,CAC7C,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,CAAC,sCAAsC,EACrG,gCAAgC,KAAK,6BAA6B,EAClE,EAAE,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,CACrE,CAAC;AACJ,CAAC;AAED,SAAS,2BAA2B,CAAC,GAA4B,EAAE,KAAa;IAC9E,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAClF,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAK,KAAgB,GAAG,CAAC;QAAE,OAAO,KAAe,CAAC;IAC7E,MAAM,IAAI,qCAAqC,CAC7C,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,CAAC,sCAAsC,EACrG,gCAAgC,KAAK,8BAA8B,EACnE,EAAE,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,CACtE,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,GAA4B,EAAE,KAAa;IACrE,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;IACzB,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAChE,MAAM,IAAI,qCAAqC,CAC7C,sCAAsC,EACtC,gCAAgC,KAAK,2CAA2C,EAChF,EAAE,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,CACtE,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAa,EAAE,KAAa;IACxD,IAAI,iCAAiC,CAAC,GAAG,CAAC,KAA6C,CAAC,EAAE,CAAC;QACzF,OAAO,KAA6C,CAAC;IACvD,CAAC;IACD,MAAM,IAAI,qCAAqC,CAC7C,sCAAsC,EACtC,gCAAgC,KAAK,uDAAuD,EAC5F,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,GAAG,iCAAiC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAC3E,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,KAAa,EAAE,KAAa;IAClD,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC5D,MAAM,IAAI,qCAAqC,CAC7C,sCAAsC,EACtC,gCAAgC,KAAK,iCAAiC,EACtE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAC1D,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,KAAa,EAAE,KAAa;IAClD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IAChF,MAAM,IAAI,qCAAqC,CAC7C,sCAAsC,EACtC,gCAAgC,KAAK,0CAA0C,EAC/E,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAClE,CAAC;AACJ,CAAC;AAED,SAAS,mCAAmC,CAC1C,gBAAsD,EACtD,UAAoE;IAEpE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,UAAU,CAAC,UAAU,CAAC;QACtD,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,gBAAgB,CAAC,UAAU,CAAC,SAAS,CAAC;YACtC,CAAC,CAAC,QAAQ;YACV,CAAC,CAAC,MAAM,CAAC;IAEb,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,qCAAqC,CAC7C,6CAA6C,EAC7C,2EAA2E,EAC3E,EAAE,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAC7D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,uBAAuB,CAC9B,gBAAsD,EACtD,QAAgB,EAChB,OAAgC;IAEhC,QAAQ,gBAAgB,EAAE,CAAC;QACzB,KAAK,iBAAiB,CAAC;QACvB,KAAK,iBAAiB,CAAC,CAAC,CAAC;YACvB,4BAA4B,CAAC,QAAQ,EAAE,kBAAkB,CAAC,OAAO,EAAE,0BAA0B,CAAC,EAAE,YAAY,CAAC,CAAC;YAC9G,kBAAkB,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAC;YACrD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,KAAK,gBAAgB,CAAC,CAAC,CAAC;YACtB,4BAA4B,CAAC,QAAQ,EAAE,kBAAkB,CAAC,OAAO,EAAE,0BAA0B,CAAC,EAAE,YAAY,CAAC,CAAC;YAC9G,yBAAyB,CAAC,OAAO,EAAE,+BAA+B,CAAC,CAAC;YACpE,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,KAAK,iBAAiB,CAAC,CAAC,CAAC;YACvB,4BAA4B,CAAC,QAAQ,EAAE,kBAAkB,CAAC,OAAO,EAAE,0BAA0B,CAAC,EAAE,YAAY,CAAC,CAAC;YAC9G,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,KAAK,gBAAgB,CAAC;QACtB,KAAK,gBAAgB,CAAC,CAAC,CAAC;YACtB,4BAA4B,CAC1B,QAAQ,EACR,kBAAkB,CAAC,OAAO,EAAE,uCAAuC,CAAC,EACpE,yBAAyB,CAC1B,CAAC;YACF,kBAAkB,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC;YACpD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,4BAA4B,CAC1B,QAAQ,EACR,kBAAkB,CAAC,OAAO,EAAE,uCAAuC,CAAC,EACpE,yBAAyB,CAC1B,CAAC;YACF,yBAAyB,CAAC,OAAO,EAAE,+BAA+B,CAAC,CAAC;YACpE,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,KAAK,gBAAgB,CAAC,CAAC,CAAC;YACtB,4BAA4B,CAC1B,QAAQ,EACR,kBAAkB,CAAC,OAAO,EAAE,uCAAuC,CAAC,EACpE,yBAAyB,CAC1B,CAAC;YACF,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,KAAK,4BAA4B;YAC/B,kBAAkB,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAC;YACrD,2BAA2B,CAAC,OAAO,EAAE,8BAA8B,CAAC,CAAC;YACrE,OAAO,gCAAgC,CAAC,OAAO,CAAC,CAAC;IACrD,CAAC;AACH,CAAC;AAED,SAAS,4BAA4B,CAAC,QAAgB,EAAE,eAAuB,EAAE,YAAoB;IACnG,IAAI,QAAQ,KAAK,eAAe;QAAE,OAAO;IACzC,MAAM,IAAI,qCAAqC,CAC7C,sCAAsC,EACtC,mEAAmE,YAAY,GAAG,EAClF,EAAE,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,CAC1E,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAAC,OAAgC;IAChE,MAAM,eAAe,GAAG,kBAAkB,CAAC,OAAO,EAAE,0BAA0B,CAAC,CAAC;IAChF,OAAO,cAAc,CACnB,kBAAkB,CAAC,eAAe,EAAE,gCAAgC,CAAC,EACrE,gCAAgC,CACjC,CAAC;AACJ,CAAC;AAED,SAAS,gCAAgC,CAAC,OAAgC;IACxE,MAAM,QAAQ,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAC9C,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IACrE,MAAM,IAAI,qCAAqC,CAC7C,4CAA4C,EAC5C,4FAA4F,EAC5F,EAAE,KAAK,EAAE,oCAAoC,EAAE,QAAQ,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,EAAE,CAClH,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IACzC,OAAO,OAAO,KAAK,CAAC;AACtB,CAAC"}
@@ -9,7 +9,7 @@ We'll build a **signals agent** that serves audience segments via the `get_signa
9
9
  ## Prerequisites
10
10
 
11
11
  - Node.js 18+
12
- - `@adcp/sdk` installed (`npm install @adcp/sdk`)
12
+ - `@adcp/sdk` installed from the beta line while validating AdCP 3.1 (`npm install @adcp/sdk@beta`)
13
13
  - `@modelcontextprotocol/sdk` (installed as a dependency of `@adcp/sdk`)
14
14
 
15
15
  ## The server entry point
@@ -86,8 +86,8 @@ Start it and test immediately:
86
86
 
87
87
  ```bash
88
88
  npx tsx agent.ts
89
- npx @adcp/sdk@latest http://localhost:3001/mcp # discover tools
90
- npx @adcp/sdk@latest http://localhost:3001/mcp get_signals '{}' # call get_signals
89
+ npx @adcp/sdk@beta http://localhost:3001/mcp # discover tools
90
+ npx @adcp/sdk@beta http://localhost:3001/mcp get_signals '{}' # call get_signals
91
91
  ```
92
92
 
93
93
  `definePlatform` / `defineSignalsPlatform` (and the sibling
@@ -133,14 +133,20 @@ const platform = definePlatform({
133
133
  list: async (filter, ctx) => db.listAccounts(filter, { agentUrl: ctx?.agent?.agent_url }),
134
134
  },
135
135
  sales: defineSalesCorePlatform({
136
- getProducts: async (req, ctx) => ({ products: catalog.search(req) }), // req typed ✓
136
+ getProducts: async (req, ctx) => {
137
+ const result = catalog.search(req);
138
+ return {
139
+ products: result.products,
140
+ cache_scope: result.usesAccountSpecificPricing ? 'account' : 'public',
141
+ };
142
+ }, // req typed ✓
137
143
  createMediaBuy: async (req, ctx) => ({
138
144
  media_buy_id: `mb_${Date.now()}`,
139
- status: 'pending_creatives',
145
+ media_buy_status: 'pending_creatives',
140
146
  confirmed_at: new Date().toISOString(),
141
147
  packages: [],
142
148
  }),
143
- updateMediaBuy: async (id, patch, ctx) => ({ media_buy_id: id, status: 'active' }),
149
+ updateMediaBuy: async (id, patch, ctx) => ({ media_buy_id: id, media_buy_status: 'active' }),
144
150
  getMediaBuyDelivery: async (req, ctx) => ({ media_buys: [] }),
145
151
  getMediaBuys: async (req, ctx) => ({ media_buys: [] }),
146
152
  }),
@@ -299,9 +305,13 @@ Every handler receives `ctx.store` — a key-value store for persisting domain o
299
305
  ```typescript
300
306
  mediaBuy: {
301
307
  createMediaBuy: async (params, ctx) => {
302
- const mediaBuy = { media_buy_id: `mb_${Date.now()}`, status: 'pending', packages: [] };
308
+ const mediaBuy = { media_buy_id: `mb_${Date.now()}`, status: 'pending_creatives', packages: [] };
303
309
  await ctx.store.put('media_buys', mediaBuy.media_buy_id, mediaBuy);
304
- return mediaBuy;
310
+ return {
311
+ media_buy_id: mediaBuy.media_buy_id,
312
+ media_buy_status: mediaBuy.status,
313
+ packages: mediaBuy.packages,
314
+ };
305
315
  },
306
316
  getMediaBuys: async (params, ctx) => {
307
317
  if (params.media_buy_ids?.length) {
@@ -346,7 +356,7 @@ const platform = definePlatform({
346
356
  getProducts: async (req, ctx) => {
347
357
  // ctx.account is the resolved account
348
358
  const products = await catalog.search(req, ctx.account.id);
349
- return { products };
359
+ return { products, cache_scope: 'account' };
350
360
  },
351
361
  /* ... */
352
362
  }),
@@ -575,12 +585,18 @@ return wrapEnvelope(
575
585
 
576
586
  ### Response Builders
577
587
 
578
- With `createAdcpServerFromPlatform`, response builders are applied automatically return raw data and the framework wraps it. If you need manual control (e.g., with `createTaskCapableServer`), builders are available:
588
+ With `createAdcpServerFromPlatform`, return handler payloads, not full wire envelopes. The framework owns protocol fields such as task `status`, `task_id`, `adcp_version`, context echoing, and response validation. Domain lifecycle fields remain part of your payload: for media buys use `media_buy_status`, while nested resources such as accounts, creatives, audiences, and `media_buys[]` keep their resource `status` fields.
589
+
590
+ For `getProducts`, any response that includes `products` or `unchanged: true` must also include `cache_scope`. Use `public` for a universal rate card and `account` when the response includes account-specific rate cards or pricing overlays. The framework can safely infer `public` only when there is no inline account and no auth-derived/resolved account; account-scoped product responses should set the field explicitly.
591
+
592
+ If you need manual control (e.g., with `createTaskCapableServer`), builders are available:
579
593
 
580
594
  ```typescript
581
595
  import { productsResponse, mediaBuyResponse, deliveryResponse, taskToolResponse } from '@adcp/sdk';
582
596
  // For error envelopes, throw a typed error class — `AuthRequiredError`,
583
597
  // `PermissionDeniedError`, `RateLimitedError`, etc. — from `@adcp/sdk/server`.
598
+
599
+ const response = productsResponse({ products, cache_scope: 'public' });
584
600
  ```
585
601
 
586
602
  ### Task Statuses (Server-Side Contract)
@@ -708,7 +724,7 @@ const httpServer = createServer(async (req, res) => {
708
724
  ### Tool Discovery
709
725
 
710
726
  ```bash
711
- npx @adcp/sdk@latest http://localhost:3001/mcp
727
+ npx @adcp/sdk@beta http://localhost:3001/mcp
712
728
  ```
713
729
 
714
730
  This lists all tools your agent exposes, their descriptions, and parameters. If `get_signals` appears with the correct schema, your agent is wired up correctly.
@@ -717,16 +733,16 @@ This lists all tools your agent exposes, their descriptions, and parameters. If
717
733
 
718
734
  ```bash
719
735
  # All segments
720
- npx @adcp/sdk@latest http://localhost:3001/mcp get_signals '{"signal_spec":"audience segments"}'
736
+ npx @adcp/sdk@beta http://localhost:3001/mcp get_signals '{"signal_spec":"audience segments"}'
721
737
 
722
738
  # Filtered by text
723
- npx @adcp/sdk@latest http://localhost:3001/mcp get_signals '{"signal_spec":"shoppers"}'
739
+ npx @adcp/sdk@beta http://localhost:3001/mcp get_signals '{"signal_spec":"shoppers"}'
724
740
 
725
741
  # Filtered by catalog type
726
- npx @adcp/sdk@latest http://localhost:3001/mcp get_signals '{"filters":{"catalog_types":["marketplace"]}}'
742
+ npx @adcp/sdk@beta http://localhost:3001/mcp get_signals '{"filters":{"catalog_types":["marketplace"]}}'
727
743
 
728
744
  # JSON output for scripting
729
- npx @adcp/sdk@latest http://localhost:3001/mcp get_signals '{}' --json
745
+ npx @adcp/sdk@beta http://localhost:3001/mcp get_signals '{}' --json
730
746
  ```
731
747
 
732
748
  ```bash
@@ -734,7 +750,7 @@ npx @adcp/sdk@latest http://localhost:3001/mcp get_signals '{}' --json
734
750
  # Schema traps: idempotency_key must be 16-255 chars (UUID v4 recommended);
735
751
  # package-level budget is a plain number (not {amount,currency}); brand uses {domain} (not {brand_id});
736
752
  # packages require product_id, budget, and pricing_option_id
737
- npx @adcp/sdk@latest http://localhost:3001/mcp create_media_buy '{
753
+ npx @adcp/sdk@beta http://localhost:3001/mcp create_media_buy '{
738
754
  "idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
739
755
  "account": { "account_id": "acct_123" },
740
756
  "brand": { "domain": "acme.example" },
@@ -749,7 +765,7 @@ npx @adcp/sdk@latest http://localhost:3001/mcp create_media_buy '{
749
765
  ### Compliance Check
750
766
 
751
767
  ```bash
752
- npx @adcp/sdk@latest storyboard run http://localhost:3001/mcp
768
+ npx @adcp/sdk@beta storyboard run http://localhost:3001/mcp
753
769
  ```
754
770
 
755
771
  This runs a standard validation suite against your agent to check AdCP compliance. For the full validation picture — storyboard runner, property-based fuzzing (`adcp fuzz`), multi-instance testing, webhook conformance, request-signing, schema-driven validation, and the skill→agent→grader dogfood harness — see [**VALIDATE-YOUR-AGENT.md**](./VALIDATE-YOUR-AGENT.md).
package/docs/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  # Ad Context Protocol (AdCP)
2
2
 
3
- > Generated at: 2026-05-22
4
- > Library: @adcp/sdk v8.1.0-beta.0
3
+ > Generated at: 2026-05-26
4
+ > Library: @adcp/sdk v8.1.0-beta.11
5
5
  > AdCP major version: 3
6
6
  > Canonical URL: https://adcontextprotocol.github.io/adcp-client/llms.txt
7
7
  > Note: the `Library` stamp reflects the package.json version at doc-generation time. The narrative below describes the surface that lands on the next-published minor — including any 6.7 helpers documented here ahead of the release tag.
@@ -312,6 +312,10 @@ Request parameters for discovering available advertising products.
312
312
  **Response (success branch):**
313
313
  - Optional: `products: object[]`, `extensions: object`, `proposals: object[]`, `errors: object[]`, `property_list_applied: boolean`, `catalog_applied: boolean`, `refinement_applied: object[]`, `incomplete: object[]`, +8 more
314
314
 
315
+ **Watch out:**
316
+ - `cache_scope` is required whenever the response includes `products` or `unchanged: true`. Use `public` for the universal rate card and `account` for account-specific rate cards or pricing overlays.
317
+ - SDK server handlers may omit `cache_scope` only for no-account product feeds; the framework can safely infer `public` only when there is no inline account and no auth-derived/resolved account.
318
+
315
319
  #### `list_creative_formats`
316
320
 
317
321
  Request parameters for discovering format IDs and creative agents supported by this sales agent.
@@ -340,6 +344,9 @@ Request parameters for creating a media buy.
340
344
  - Required: `media_buy_id: string`, `packages: object[]`
341
345
  - Optional: `account: Account`, `invoice_recipient: Business Entity`, `media_buy_status: Media Buy Status`, `status: Media Buy Status`, `confirmed_at: string`, `creative_deadline: string`, `revision: integer`, `currency: string`, +6 more
342
346
 
347
+ **Watch out:**
348
+ - Server handlers should return business lifecycle state as `media_buy_status`. The framework owns the task envelope `status`; do not return top-level `status` as the media-buy state.
349
+
343
350
  #### `update_media_buy`
344
351
 
345
352
  Request parameters for updating campaign and package settings.
@@ -352,6 +359,9 @@ Request parameters for updating campaign and package settings.
352
359
  - Required: `media_buy_id: string`
353
360
  - Optional: `media_buy_status: Media Buy Status`, `status: Media Buy Status`, `revision: integer`, `currency: string`, `total_budget: number`, `implementation_date: string,null`, `invoice_recipient: Business Entity`, `affected_packages: object[]`, +4 more
354
361
 
362
+ **Watch out:**
363
+ - Server handlers should return business lifecycle state as `media_buy_status`. The framework owns the task envelope `status`; do not return top-level `status` as the media-buy state.
364
+
355
365
  #### `get_media_buys`
356
366
 
357
367
  Request parameters for retrieving media buy status, creative approvals, and delivery snapshots.
@@ -1416,4 +1426,4 @@ JSON schemas (source of truth): `schemas/cache/latest/index.json` (local only)
1416
1426
  - Documentation: https://adcontextprotocol.github.io/adcp-client/
1417
1427
  - npm: https://www.npmjs.com/package/@adcp/sdk
1418
1428
  - Spec: https://adcontextprotocol.org
1419
- - CLI: `npx @adcp/sdk@latest`
1429
+ - CLI: `npx @adcp/sdk@beta` for the 8.1 / AdCP 3.1 beta line
@@ -34,11 +34,11 @@ import {
34
34
  type SalesCorePlatform,
35
35
  type SalesIngestionPlatform,
36
36
  type AccountStore,
37
+ type GetProductsPayload,
37
38
  type SyncCreativesRow,
38
39
  } from '@adcp/sdk/server';
39
40
  import type {
40
41
  GetProductsRequest,
41
- GetProductsResponse,
42
42
  CreateMediaBuyRequest,
43
43
  CreateMediaBuySuccess,
44
44
  UpdateMediaBuyRequest,
@@ -117,7 +117,7 @@ export class BroadcastTvSeller implements DecisioningPlatform<BroadcastTvConfig,
117
117
  * the eventual products via `publishStatusChange` on
118
118
  * `resource_type: 'proposal'`.
119
119
  */
120
- getProducts: async (req: GetProductsRequest): Promise<GetProductsResponse> => {
120
+ getProducts: async (req: GetProductsRequest): Promise<GetProductsPayload> => {
121
121
  const promotedOffering = (req as { promoted_offering?: string }).promoted_offering ?? '';
122
122
  if (/political|cannabis|gambling/i.test(promotedOffering)) {
123
123
  throw new AdcpError('POLICY_VIOLATION', {
@@ -129,7 +129,7 @@ export class BroadcastTvSeller implements DecisioningPlatform<BroadcastTvConfig,
129
129
  }
130
130
 
131
131
  return {
132
- status: 'completed' as const,
132
+ cache_scope: 'account' as const,
133
133
  products: [
134
134
  {
135
135
  product_id: 'prod_primetime_30s',
@@ -47,11 +47,11 @@ import {
47
47
  type SalesIngestionPlatform,
48
48
  type AccountStore,
49
49
  type AdcpStructuredError,
50
+ type GetProductsPayload,
50
51
  type SyncCreativesRow,
51
52
  } from '@adcp/sdk/server';
52
53
  import type {
53
54
  GetProductsRequest,
54
- GetProductsResponse,
55
55
  CreateMediaBuyRequest,
56
56
  CreateMediaBuySuccess,
57
57
  UpdateMediaBuyRequest,
@@ -146,8 +146,8 @@ function rejectPreflight(errors: AdcpStructuredError[]): never {
146
146
  });
147
147
  }
148
148
 
149
- const SHARED_GET_PRODUCTS = async (_req: GetProductsRequest): Promise<GetProductsResponse> => ({
150
- status: 'completed' as const,
149
+ const SHARED_GET_PRODUCTS = async (_req: GetProductsRequest): Promise<GetProductsPayload> => ({
150
+ cache_scope: 'account' as const,
151
151
  products: [
152
152
  {
153
153
  product_id: 'prod_premium_video',
@@ -28,11 +28,11 @@ import {
28
28
  type SalesCorePlatform,
29
29
  type SalesIngestionPlatform,
30
30
  type AccountStore,
31
+ type GetProductsPayload,
31
32
  type SyncCreativesRow,
32
33
  } from '@adcp/sdk/server';
33
34
  import type {
34
35
  GetProductsRequest,
35
- GetProductsResponse,
36
36
  CreateMediaBuyRequest,
37
37
  CreateMediaBuySuccess,
38
38
  UpdateMediaBuyRequest,
@@ -97,8 +97,8 @@ export class ProgrammaticSeller implements DecisioningPlatform<ProgrammaticConfi
97
97
 
98
98
  sales: SalesCorePlatform<ProgrammaticMeta> & SalesIngestionPlatform<ProgrammaticMeta> = {
99
99
  /** Sync discovery: catalog read; no async ceremony. */
100
- getProducts: async (_req: GetProductsRequest): Promise<GetProductsResponse> => ({
101
- status: 'completed' as const,
100
+ getProducts: async (_req: GetProductsRequest): Promise<GetProductsPayload> => ({
101
+ cache_scope: 'account' as const,
102
102
  products: [
103
103
  {
104
104
  product_id: 'prod_run_of_network_display',
@@ -47,6 +47,7 @@ import {
47
47
  type DecisioningPlatform,
48
48
  type FinalizeProposalRequest,
49
49
  type FinalizeProposalSuccess,
50
+ type GetProductsPayload,
50
51
  type ProposalManager,
51
52
  type SalesCorePlatform,
52
53
  } from '@adcp/sdk/server';
@@ -349,11 +350,11 @@ const proposalManager: ProposalManager<GAMLikeRecipe, NetworkMeta> = {
349
350
  availabilityReservations: true,
350
351
  },
351
352
 
352
- async getProducts(req: GetProductsRequest, ctx): Promise<GetProductsResponse> {
353
+ async getProducts(req: GetProductsRequest, ctx): Promise<GetProductsPayload> {
353
354
  const networkCode = ctx.account.ctx_metadata.network_code;
354
355
  const publisherDomain = ctx.account.ctx_metadata.publisher_domain;
355
356
  const products = await upstream.listProducts(networkCode);
356
- if (products.length === 0) return { status: 'completed', products: [], cache_scope: 'account' };
357
+ if (products.length === 0) return { products: [], cache_scope: 'account' };
357
358
 
358
359
  // brief + total_budget signals → curated proposal. Without a brief
359
360
  // the buyer is browsing the catalog; skip proposal generation.
@@ -362,7 +363,7 @@ const proposalManager: ProposalManager<GAMLikeRecipe, NetworkMeta> = {
362
363
  if (!brief) {
363
364
  // Catalog mode — return products with recipes, no proposals.
364
365
  const productsOut = products.map(p => projectProduct(p, publisherDomain, buildGAMLikeRecipe(p)));
365
- return { status: 'completed', products: productsOut, cache_scope: 'account' };
366
+ return { products: productsOut, cache_scope: 'account' };
366
367
  }
367
368
 
368
369
  const draft = await upstream.createProposal(networkCode, {
@@ -374,14 +375,13 @@ const proposalManager: ProposalManager<GAMLikeRecipe, NetworkMeta> = {
374
375
  .filter(p => referencedIds.has(p.product_id))
375
376
  .map(p => projectProduct(p, publisherDomain, buildGAMLikeRecipe(p)));
376
377
  return {
377
- status: 'completed',
378
378
  products: productsOut,
379
379
  proposals: [projectProposal(draft, totalBudget)],
380
380
  cache_scope: 'account',
381
381
  };
382
382
  },
383
383
 
384
- async refineProducts(req: GetProductsRequest, ctx): Promise<GetProductsResponse> {
384
+ async refineProducts(req: GetProductsRequest, ctx): Promise<GetProductsPayload> {
385
385
  const networkCode = ctx.account.ctx_metadata.network_code;
386
386
  const publisherDomain = ctx.account.ctx_metadata.publisher_domain;
387
387
  const refine =
@@ -402,7 +402,6 @@ const proposalManager: ProposalManager<GAMLikeRecipe, NetworkMeta> = {
402
402
  .filter(p => referencedIds.has(p.product_id))
403
403
  .map(p => projectProduct(p, publisherDomain, buildGAMLikeRecipe(p)));
404
404
  return {
405
- status: 'completed',
406
405
  products: productsOut,
407
406
  proposals: [projectProposal(refined)],
408
407
  cache_scope: 'account',
@@ -463,7 +462,7 @@ const sales: SalesCorePlatform<NetworkMeta> = {
463
462
  // getProducts is owned by proposalManager when wired; the framework
464
463
  // routes there. We keep this empty at the type level — the framework
465
464
  // never reaches it.
466
- getProducts: async () => ({ status: 'completed', products: [], cache_scope: 'account' }),
465
+ getProducts: async () => ({ products: [], cache_scope: 'account' }),
467
466
 
468
467
  async createMediaBuy(req: CreateMediaBuyRequest, ctx): Promise<CreateMediaBuySuccess> {
469
468
  const networkCode = ctx.account.ctx_metadata.network_code;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adcp/sdk",
3
- "version": "8.1.0-beta.11",
3
+ "version": "8.1.0-beta.12",
4
4
  "description": "AdCP SDK — client, server, and compliance harnesses for the AdContext Protocol (MCP + A2A)",
5
5
  "workspaces": [
6
6
  ".",
@@ -104,6 +104,7 @@ class MyPlatform implements DecisioningPlatform {
104
104
  getProducts: async (req, ctx) => {
105
105
  const products = await this.platform.searchInventory(req.brief, req.promoted_offering);
106
106
  return {
107
+ cache_scope: 'account',
107
108
  products: products.map(p => ({
108
109
  product_id: p.id,
109
110
  name: p.name,
@@ -57,6 +57,7 @@ const platform = {
57
57
  sales: {
58
58
  getProducts: async (req, ctx) => ({
59
59
  status: 'completed',
60
+ cache_scope: 'account',
60
61
  products: [
61
62
  {
62
63
  product_id: 'p_homepage',
@@ -139,7 +140,7 @@ getProducts: async (req, ctx) => {
139
140
  for (const p of products) {
140
141
  await ctx.ctxMetadata?.set('product', p.id, { gam: { ad_unit_ids: p.adUnitIds } });
141
142
  }
142
- return { products: products.map(toAdcpProduct) };
143
+ return { products: products.map(toAdcpProduct), cache_scope: 'account' };
143
144
  },
144
145
 
145
146
  // Read on the way in (subsequent call referencing the same ID):
@@ -136,7 +136,7 @@ Every validation failure produces:
136
136
  }
137
137
  ```
138
138
 
139
- Returns `{ products: [{ product_id, name, description, delivery_type, pricing_options, ... }] }`.
139
+ Returns `{ cache_scope: "public" | "account", products: [{ product_id, name, description, delivery_type, pricing_options, ... }] }`.
140
140
 
141
141
  ### create_media_buy
142
142