@adcp/sdk 15.2.2 → 15.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 (48) hide show
  1. package/dist/lib/core/ConversationTypes.d.mts +5 -0
  2. package/dist/lib/core/ConversationTypes.d.ts +5 -0
  3. package/dist/lib/core/SingleAgentClient.d.mts +19 -1
  4. package/dist/lib/core/SingleAgentClient.d.ts +19 -1
  5. package/dist/lib/core/SingleAgentClient.js +38 -7
  6. package/dist/lib/core/SingleAgentClient.mjs +38 -7
  7. package/dist/lib/index.d.mts +1 -1
  8. package/dist/lib/index.d.ts +1 -1
  9. package/dist/lib/reporting/ledger/handler.d.mts +6 -0
  10. package/dist/lib/reporting/ledger/handler.d.ts +6 -0
  11. package/dist/lib/reporting/ledger/handler.js +36 -7
  12. package/dist/lib/reporting/ledger/handler.mjs +35 -7
  13. package/dist/lib/reporting/ledger/index.d.mts +1 -0
  14. package/dist/lib/reporting/ledger/index.d.ts +1 -0
  15. package/dist/lib/reporting/ledger/index.js +3 -1
  16. package/dist/lib/reporting/ledger/index.mjs +1 -0
  17. package/dist/lib/reporting/ledger/lifecycle.js +3 -2
  18. package/dist/lib/reporting/ledger/lifecycle.mjs +3 -2
  19. package/dist/lib/reporting/ledger/managed-postgres.js +5 -5
  20. package/dist/lib/reporting/ledger/managed-postgres.mjs +5 -5
  21. package/dist/lib/reporting/ledger/postgres.d.mts +4 -1
  22. package/dist/lib/reporting/ledger/postgres.d.ts +4 -1
  23. package/dist/lib/reporting/ledger/postgres.js +38 -0
  24. package/dist/lib/reporting/ledger/postgres.mjs +38 -0
  25. package/dist/lib/reporting/ledger/producer.js +3 -2
  26. package/dist/lib/reporting/ledger/producer.mjs +3 -2
  27. package/dist/lib/reporting/ledger/revision-metadata.d.mts +17 -0
  28. package/dist/lib/reporting/ledger/revision-metadata.d.ts +17 -0
  29. package/dist/lib/reporting/ledger/revision-metadata.js +71 -0
  30. package/dist/lib/reporting/ledger/revision-metadata.mjs +44 -0
  31. package/dist/lib/reporting/ledger/row-encoding.d.mts +128 -0
  32. package/dist/lib/reporting/ledger/row-encoding.d.ts +128 -0
  33. package/dist/lib/reporting/ledger/row-encoding.js +355 -0
  34. package/dist/lib/reporting/ledger/row-encoding.mjs +315 -0
  35. package/dist/lib/reporting/ledger/types.d.mts +37 -0
  36. package/dist/lib/reporting/ledger/types.d.ts +37 -0
  37. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  38. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  39. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  40. package/dist/lib/version.d.mts +3 -3
  41. package/dist/lib/version.d.ts +3 -3
  42. package/dist/lib/version.js +3 -3
  43. package/dist/lib/version.mjs +3 -3
  44. package/docs/TYPE-SUMMARY.md +2 -1
  45. package/docs/guides/PUSH-NOTIFICATION-CONFIG.md +39 -0
  46. package/docs/llms.txt +1 -1
  47. package/docs/migration-14.x-rc-worksheet.md +4 -4
  48. package/package.json +1 -1
@@ -0,0 +1,315 @@
1
+ import { createHash } from "crypto";
2
+ import { canonicalize } from "../../utils/jcs.mjs";
3
+ const REPORTING_ROW_ENCODING_V1 = "adcp_canonical_jsonl_v1";
4
+ const REPORTING_ROW_SEGMENT_MAX_ROWS = 500;
5
+ const REPORTING_ROW_CHUNK_MAX_SEGMENTS = 20;
6
+ const REPORTING_ROW_CHUNK_MAX_ROWS = REPORTING_ROW_SEGMENT_MAX_ROWS * REPORTING_ROW_CHUNK_MAX_SEGMENTS;
7
+ const REPORTING_ROW_CHUNK_MAX_BYTES = 8 * 1024 * 1024;
8
+ const NEWLINE = 10;
9
+ const COMMA = Buffer.from(",", "utf8");
10
+ const LONE_SURROGATE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
11
+ class ReportingRowEncodingError extends Error {
12
+ constructor(code, detail) {
13
+ super(`Reporting row encoding: ${code}: ${detail}`);
14
+ this.code = code;
15
+ this.name = "ReportingRowEncodingError";
16
+ Object.defineProperty(this, /* @__PURE__ */ Symbol.for("adcp.reportingRowEncodingError"), { value: true });
17
+ }
18
+ code;
19
+ }
20
+ function isReportingRowEncodingError(error) {
21
+ return !!error && typeof error === "object" && error[/* @__PURE__ */ Symbol.for("adcp.reportingRowEncodingError")] === true;
22
+ }
23
+ function canonicalReportingRowV1(row, ordinal = 0) {
24
+ if (!row || typeof row !== "object" || Array.isArray(row)) {
25
+ throw new ReportingRowEncodingError("INVALID_ROW", `row ${ordinal} is not a JSON object`);
26
+ }
27
+ assertPortableNumbers(row, ordinal);
28
+ let canonical;
29
+ try {
30
+ canonical = canonicalize(row);
31
+ } catch (error) {
32
+ throw new ReportingRowEncodingError(
33
+ "INVALID_ROW",
34
+ `row ${ordinal} is not canonical JSON: ${error instanceof Error ? error.message : String(error)}`
35
+ );
36
+ }
37
+ if (LONE_SURROGATE.test(canonical)) {
38
+ throw new ReportingRowEncodingError("INVALID_ROW", `row ${ordinal} contains a lone surrogate`);
39
+ }
40
+ return canonical;
41
+ }
42
+ function encodeReportingRowsV1(rows) {
43
+ const chunks = [];
44
+ let chunkLines = [];
45
+ let chunkBytes = 0;
46
+ let chunkFirstOrdinal = 0;
47
+ const closeChunk = () => {
48
+ if (chunkLines.length === 0) return;
49
+ chunks.push(buildChunk(chunks.length, chunkFirstOrdinal, chunkLines));
50
+ chunkLines = [];
51
+ chunkBytes = 0;
52
+ };
53
+ rows.forEach((row, ordinal) => {
54
+ const line = Buffer.from(canonicalReportingRowV1(row, ordinal) + "\n", "utf8");
55
+ if (chunkLines.length === REPORTING_ROW_CHUNK_MAX_ROWS || chunkLines.length > 0 && chunkBytes + line.byteLength > REPORTING_ROW_CHUNK_MAX_BYTES) {
56
+ closeChunk();
57
+ }
58
+ if (chunkLines.length === 0) chunkFirstOrdinal = ordinal;
59
+ chunkLines.push(line);
60
+ chunkBytes += line.byteLength;
61
+ });
62
+ closeChunk();
63
+ const manifests = chunks.map((chunk) => chunk.manifest);
64
+ return {
65
+ encoding: REPORTING_ROW_ENCODING_V1,
66
+ rowCount: rows.length,
67
+ byteCount: chunks.reduce((total, chunk) => total + chunk.manifest.byte_count, 0),
68
+ chunks,
69
+ manifests,
70
+ rowManifestSha256: reportingRowManifestSha256V1(manifests)
71
+ };
72
+ }
73
+ function reportingRowManifestSha256V1(manifests) {
74
+ return sha256Hex(
75
+ canonicalize(
76
+ manifests.map((manifest) => ({
77
+ chunk_index: manifest.chunk_index,
78
+ first_ordinal: manifest.first_ordinal,
79
+ row_count: manifest.row_count,
80
+ byte_count: manifest.byte_count,
81
+ sha256: manifest.sha256,
82
+ segments: manifest.segments.map((segment) => ({
83
+ first_ordinal: segment.first_ordinal,
84
+ row_count: segment.row_count,
85
+ byte_offset: segment.byte_offset,
86
+ byte_count: segment.byte_count,
87
+ sha256: segment.sha256
88
+ }))
89
+ }))
90
+ )
91
+ );
92
+ }
93
+ function verifyReportingRowManifestsV1(manifests, expected) {
94
+ if (reportingRowManifestSha256V1(manifests) !== expected.rowManifestSha256.toLowerCase()) {
95
+ throw new ReportingRowEncodingError("MANIFEST_MISMATCH", "chunk manifest digest does not match the header");
96
+ }
97
+ let nextOrdinal = 0;
98
+ manifests.forEach((manifest, index) => {
99
+ if (manifest.chunk_index !== index || manifest.first_ordinal !== nextOrdinal) {
100
+ throw new ReportingRowEncodingError("MANIFEST_MISMATCH", `chunk ${index} is out of order`);
101
+ }
102
+ if (manifest.row_count < 1 || manifest.row_count > REPORTING_ROW_CHUNK_MAX_ROWS || manifest.segments.length < 1 || manifest.segments.length > REPORTING_ROW_CHUNK_MAX_SEGMENTS) {
103
+ throw new ReportingRowEncodingError("MANIFEST_MISMATCH", `chunk ${index} exceeds contract limits`);
104
+ }
105
+ let segmentOrdinal = manifest.first_ordinal;
106
+ let segmentOffset = 0;
107
+ manifest.segments.forEach((segment, segmentIndex) => {
108
+ const last = segmentIndex === manifest.segments.length - 1;
109
+ if (segment.first_ordinal !== segmentOrdinal || segment.byte_offset !== segmentOffset || segment.row_count < 1 || segment.byte_count < 1 || segment.row_count > REPORTING_ROW_SEGMENT_MAX_ROWS || !last && segment.row_count !== REPORTING_ROW_SEGMENT_MAX_ROWS) {
110
+ throw new ReportingRowEncodingError("MANIFEST_MISMATCH", `chunk ${index} segment ${segmentIndex} is invalid`);
111
+ }
112
+ segmentOrdinal += segment.row_count;
113
+ segmentOffset += segment.byte_count;
114
+ });
115
+ if (segmentOrdinal !== manifest.first_ordinal + manifest.row_count || segmentOffset !== manifest.byte_count) {
116
+ throw new ReportingRowEncodingError("MANIFEST_MISMATCH", `chunk ${index} segments do not tile the chunk`);
117
+ }
118
+ if (manifest.row_count > 1 && manifest.byte_count > REPORTING_ROW_CHUNK_MAX_BYTES) {
119
+ throw new ReportingRowEncodingError("MANIFEST_MISMATCH", `chunk ${index} exceeds the byte limit`);
120
+ }
121
+ nextOrdinal += manifest.row_count;
122
+ });
123
+ if (nextOrdinal !== expected.rowCount) {
124
+ throw new ReportingRowEncodingError("MANIFEST_MISMATCH", "chunk manifests do not cover row_count");
125
+ }
126
+ }
127
+ function verifyReportingRowChunkV1(manifest, bytes) {
128
+ const buffer = toBuffer(bytes);
129
+ if (buffer.byteLength !== manifest.byte_count || sha256Hex(buffer) !== manifest.sha256) {
130
+ throw new ReportingRowEncodingError("CHUNK_INTEGRITY_FAILED", `chunk ${manifest.chunk_index} digest mismatch`);
131
+ }
132
+ manifest.segments.forEach((segment, index) => {
133
+ verifySegment(manifest, index, buffer.subarray(segment.byte_offset, segment.byte_offset + segment.byte_count));
134
+ });
135
+ }
136
+ function decodeVerifiedReportingRowSegmentsV1(manifest, segmentRange, bytes) {
137
+ const [from, to] = segmentRange;
138
+ if (!Number.isInteger(from) || !Number.isInteger(to) || from < 0 || to < from || to >= manifest.segments.length) {
139
+ throw new ReportingRowEncodingError("MANIFEST_MISMATCH", "segment range is outside the chunk");
140
+ }
141
+ const buffer = toBuffer(bytes);
142
+ const base = manifest.segments[from].byte_offset;
143
+ const end = manifest.segments[to].byte_offset + manifest.segments[to].byte_count;
144
+ if (buffer.byteLength !== end - base) {
145
+ throw new ReportingRowEncodingError("CHUNK_INTEGRITY_FAILED", "segment range byte length mismatch");
146
+ }
147
+ for (let index = from; index <= to; index += 1) {
148
+ const segment = manifest.segments[index];
149
+ verifySegment(
150
+ manifest,
151
+ index,
152
+ buffer.subarray(segment.byte_offset - base, segment.byte_offset - base + segment.byte_count)
153
+ );
154
+ }
155
+ return splitLines(buffer).map((line) => JSON.parse(line.toString("utf8")));
156
+ }
157
+ function createReportingRevisionEnvelopeHasherV1(envelope) {
158
+ const prefix = Buffer.from(
159
+ `{"control_totals":${canonicalize(envelope.control_totals)},"reporting_revision_id":${canonicalize(
160
+ envelope.reporting_revision_id
161
+ )},"reporting_rows":[`,
162
+ "utf8"
163
+ );
164
+ const suffix = Buffer.from(`],"row_count":${canonicalize(envelope.row_count)}}`, "utf8");
165
+ return createJoinedRowsHasher(prefix, suffix, envelope.row_count);
166
+ }
167
+ function reportingRevisionEnvelopeDigestV1(envelope, chunks) {
168
+ const hasher = createReportingRevisionEnvelopeHasherV1(envelope);
169
+ for (const chunk of chunks) hasher.update(chunk);
170
+ return hasher.digest();
171
+ }
172
+ function createReportingRowsOnlyHasherV1(rowCount) {
173
+ return createJoinedRowsHasher(Buffer.from("[", "utf8"), Buffer.from("]", "utf8"), rowCount);
174
+ }
175
+ function assertReportingRevisionEnvelopeV1(envelope, chunks, expected) {
176
+ const actual = reportingRevisionEnvelopeDigestV1(envelope, chunks);
177
+ if (actual.sha256 !== expected.sha256.toLowerCase() || expected.byteCount !== void 0 && actual.byteCount !== expected.byteCount) {
178
+ throw new ReportingRowEncodingError("ENVELOPE_INTEGRITY_FAILED", "revision content binding mismatch");
179
+ }
180
+ }
181
+ function buildChunk(chunkIndex, firstOrdinal, lines) {
182
+ const segments = [];
183
+ let offset = 0;
184
+ for (let start = 0; start < lines.length; start += REPORTING_ROW_SEGMENT_MAX_ROWS) {
185
+ const segmentLines = lines.slice(start, start + REPORTING_ROW_SEGMENT_MAX_ROWS);
186
+ const segmentBytes = Buffer.concat(segmentLines);
187
+ segments.push({
188
+ first_ordinal: firstOrdinal + start,
189
+ row_count: segmentLines.length,
190
+ byte_offset: offset,
191
+ byte_count: segmentBytes.byteLength,
192
+ sha256: sha256Hex(segmentBytes)
193
+ });
194
+ offset += segmentBytes.byteLength;
195
+ }
196
+ const bytes = Buffer.concat(lines);
197
+ return {
198
+ manifest: {
199
+ chunk_index: chunkIndex,
200
+ first_ordinal: firstOrdinal,
201
+ row_count: lines.length,
202
+ byte_count: bytes.byteLength,
203
+ sha256: sha256Hex(bytes),
204
+ segments
205
+ },
206
+ bytes
207
+ };
208
+ }
209
+ function verifySegment(manifest, index, bytes) {
210
+ const segment = manifest.segments[index];
211
+ if (bytes.byteLength !== segment.byte_count || sha256Hex(bytes) !== segment.sha256) {
212
+ throw new ReportingRowEncodingError(
213
+ "CHUNK_INTEGRITY_FAILED",
214
+ `chunk ${manifest.chunk_index} segment ${index} digest mismatch`
215
+ );
216
+ }
217
+ let lines = 0;
218
+ for (const byte of bytes) if (byte === NEWLINE) lines += 1;
219
+ if (lines !== segment.row_count || bytes[bytes.byteLength - 1] !== NEWLINE) {
220
+ throw new ReportingRowEncodingError(
221
+ "CHUNK_INTEGRITY_FAILED",
222
+ `chunk ${manifest.chunk_index} segment ${index} line count mismatch`
223
+ );
224
+ }
225
+ }
226
+ function createJoinedRowsHasher(prefix, suffix, expectedRows) {
227
+ const hash = createHash("sha256").update(prefix);
228
+ let byteCount = prefix.byteLength;
229
+ let rows = 0;
230
+ let finished = false;
231
+ return {
232
+ update(chunkBytes) {
233
+ if (finished) throw new Error("Reporting row hasher already finished");
234
+ const buffer = toBuffer(chunkBytes);
235
+ if (buffer.byteLength > 0 && buffer[buffer.byteLength - 1] !== NEWLINE) {
236
+ throw new ReportingRowEncodingError("CHUNK_INTEGRITY_FAILED", "chunk does not end at a row boundary");
237
+ }
238
+ for (const line of splitLines(buffer)) {
239
+ if (rows > 0) {
240
+ hash.update(COMMA);
241
+ byteCount += 1;
242
+ }
243
+ hash.update(line);
244
+ byteCount += line.byteLength;
245
+ rows += 1;
246
+ }
247
+ },
248
+ digest() {
249
+ if (finished) throw new Error("Reporting row hasher already finished");
250
+ finished = true;
251
+ if (rows !== expectedRows) {
252
+ throw new ReportingRowEncodingError("ENVELOPE_INTEGRITY_FAILED", "row count does not match the header");
253
+ }
254
+ hash.update(suffix);
255
+ return { sha256: hash.digest("hex"), byteCount: byteCount + suffix.byteLength };
256
+ }
257
+ };
258
+ }
259
+ function splitLines(buffer) {
260
+ const lines = [];
261
+ let start = 0;
262
+ for (let index = 0; index < buffer.byteLength; index += 1) {
263
+ if (buffer[index] === NEWLINE) {
264
+ lines.push(buffer.subarray(start, index));
265
+ start = index + 1;
266
+ }
267
+ }
268
+ if (start !== buffer.byteLength) {
269
+ throw new ReportingRowEncodingError("CHUNK_INTEGRITY_FAILED", "trailing bytes after the last row");
270
+ }
271
+ return lines;
272
+ }
273
+ function assertPortableNumbers(value, ordinal) {
274
+ if (typeof value === "number") {
275
+ if (!Number.isFinite(value) || Number.isInteger(value) && !Number.isSafeInteger(value)) {
276
+ throw new ReportingRowEncodingError(
277
+ "INVALID_ROW",
278
+ `row ${ordinal} contains a number outside the portable range; encode it as a string`
279
+ );
280
+ }
281
+ return;
282
+ }
283
+ if (Array.isArray(value)) {
284
+ for (const item of value) assertPortableNumbers(item, ordinal);
285
+ return;
286
+ }
287
+ if (value && typeof value === "object") {
288
+ for (const item of Object.values(value)) assertPortableNumbers(item, ordinal);
289
+ }
290
+ }
291
+ function toBuffer(bytes) {
292
+ return Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
293
+ }
294
+ function sha256Hex(value) {
295
+ return createHash("sha256").update(value).digest("hex");
296
+ }
297
+ export {
298
+ REPORTING_ROW_CHUNK_MAX_BYTES,
299
+ REPORTING_ROW_CHUNK_MAX_ROWS,
300
+ REPORTING_ROW_CHUNK_MAX_SEGMENTS,
301
+ REPORTING_ROW_ENCODING_V1,
302
+ REPORTING_ROW_SEGMENT_MAX_ROWS,
303
+ ReportingRowEncodingError,
304
+ assertReportingRevisionEnvelopeV1,
305
+ canonicalReportingRowV1,
306
+ createReportingRevisionEnvelopeHasherV1,
307
+ createReportingRowsOnlyHasherV1,
308
+ decodeVerifiedReportingRowSegmentsV1,
309
+ encodeReportingRowsV1,
310
+ isReportingRowEncodingError,
311
+ reportingRevisionEnvelopeDigestV1,
312
+ reportingRowManifestSha256V1,
313
+ verifyReportingRowChunkV1,
314
+ verifyReportingRowManifestsV1
315
+ };
@@ -198,6 +198,21 @@ export interface ReportingLedgerAdjustmentV1 {
198
198
  }
199
199
  export type ReportingLedgerRevisionSnapshotV1 = Omit<ReportingLedgerRevisionV1, 'rows'>;
200
200
  export type ReportingLedgerRevisionMetadataV1 = Omit<ReportingLedgerRevisionV1, 'rows'>;
201
+ export type ReportingLedgerAdjustmentMetadataV1 = Omit<ReportingLedgerAdjustmentV1, 'rows'>;
202
+ export interface ReportingRevisionRowsReadV1 {
203
+ reporting_revision_id: string;
204
+ account_id: string;
205
+ /** Zero-based ordinal of the first row to return. */
206
+ offset: number;
207
+ /** Maximum rows to return; at least 1. */
208
+ limit: number;
209
+ signal?: AbortSignal;
210
+ }
211
+ export interface ReportingRevisionRowsPageV1 {
212
+ rows: Record<string, unknown>[];
213
+ /** Total rows in the revision. */
214
+ total: number;
215
+ }
201
216
  export type ReportingLedgerAdjustmentSnapshotV1 = Omit<ReportingLedgerAdjustmentV1, 'rows'>;
202
217
  export interface ReportingLedgerConsumerStatusV1 {
203
218
  consumerStatusId: string;
@@ -761,12 +776,34 @@ export interface ReportingLedgerStore {
761
776
  }>;
762
777
  /** Returns an exact row-bearing revision only when it belongs to the resolved account. */
763
778
  getRevision(reporting_revision_id: string, account_id: string): Promise<ReportingLedgerRevisionV1 | null>;
779
+ /**
780
+ * Optional row-free counterpart of `getRevision`, scoped to the resolved
781
+ * account. Exact-read handlers prefer it so they never load every row to
782
+ * serve one page.
783
+ */
784
+ getRevisionMetadata?(reporting_revision_id: string, account_id: string): Promise<ReportingLedgerRevisionMetadataV1 | null>;
785
+ /**
786
+ * Optional: one page of a revision's rows in ordinal order, only when the
787
+ * revision belongs to the resolved account. `total` is the revision's row
788
+ * count. Stores that keep rows outside the revision document verify every
789
+ * row they return against the committed binding before returning it.
790
+ */
791
+ readRevisionRows?(input: ReportingRevisionRowsReadV1): Promise<ReportingRevisionRowsPageV1 | null>;
764
792
  listRevisions(reporting_obligation_id: string): Promise<ReportingLedgerRevisionV1[]>;
793
+ /**
794
+ * Optional: every retained revision for one obligation without
795
+ * materializing rows. The producer, lifecycle reconciler and status ingest
796
+ * prefer it and otherwise fall back to `listRevisions` with rows dropped.
797
+ * When `account_id` is supplied, only that account's revisions are returned.
798
+ */
799
+ listRevisionMetadata?(reporting_obligation_id: string, account_id?: string): Promise<ReportingLedgerRevisionMetadataV1[]>;
765
800
  commitAdjustment(adjustment: ReportingLedgerAdjustmentV1, lease: ReportingLedgerLeaseV1): Promise<{
766
801
  inserted: boolean;
767
802
  value: ReportingLedgerAdjustmentV1;
768
803
  }>;
769
804
  listAdjustments(reporting_obligation_id: string): Promise<ReportingLedgerAdjustmentV1[]>;
805
+ /** Optional row-free counterpart of `listAdjustments`, preferred when present. */
806
+ listAdjustmentMetadata?(reporting_obligation_id: string): Promise<ReportingLedgerAdjustmentMetadataV1[]>;
770
807
  putConsumerStatus(status: ReportingLedgerConsumerStatusV1): Promise<{
771
808
  inserted: boolean;
772
809
  value: ReportingLedgerConsumerStatusV1;
@@ -198,6 +198,21 @@ export interface ReportingLedgerAdjustmentV1 {
198
198
  }
199
199
  export type ReportingLedgerRevisionSnapshotV1 = Omit<ReportingLedgerRevisionV1, 'rows'>;
200
200
  export type ReportingLedgerRevisionMetadataV1 = Omit<ReportingLedgerRevisionV1, 'rows'>;
201
+ export type ReportingLedgerAdjustmentMetadataV1 = Omit<ReportingLedgerAdjustmentV1, 'rows'>;
202
+ export interface ReportingRevisionRowsReadV1 {
203
+ reporting_revision_id: string;
204
+ account_id: string;
205
+ /** Zero-based ordinal of the first row to return. */
206
+ offset: number;
207
+ /** Maximum rows to return; at least 1. */
208
+ limit: number;
209
+ signal?: AbortSignal;
210
+ }
211
+ export interface ReportingRevisionRowsPageV1 {
212
+ rows: Record<string, unknown>[];
213
+ /** Total rows in the revision. */
214
+ total: number;
215
+ }
201
216
  export type ReportingLedgerAdjustmentSnapshotV1 = Omit<ReportingLedgerAdjustmentV1, 'rows'>;
202
217
  export interface ReportingLedgerConsumerStatusV1 {
203
218
  consumerStatusId: string;
@@ -761,12 +776,34 @@ export interface ReportingLedgerStore {
761
776
  }>;
762
777
  /** Returns an exact row-bearing revision only when it belongs to the resolved account. */
763
778
  getRevision(reporting_revision_id: string, account_id: string): Promise<ReportingLedgerRevisionV1 | null>;
779
+ /**
780
+ * Optional row-free counterpart of `getRevision`, scoped to the resolved
781
+ * account. Exact-read handlers prefer it so they never load every row to
782
+ * serve one page.
783
+ */
784
+ getRevisionMetadata?(reporting_revision_id: string, account_id: string): Promise<ReportingLedgerRevisionMetadataV1 | null>;
785
+ /**
786
+ * Optional: one page of a revision's rows in ordinal order, only when the
787
+ * revision belongs to the resolved account. `total` is the revision's row
788
+ * count. Stores that keep rows outside the revision document verify every
789
+ * row they return against the committed binding before returning it.
790
+ */
791
+ readRevisionRows?(input: ReportingRevisionRowsReadV1): Promise<ReportingRevisionRowsPageV1 | null>;
764
792
  listRevisions(reporting_obligation_id: string): Promise<ReportingLedgerRevisionV1[]>;
793
+ /**
794
+ * Optional: every retained revision for one obligation without
795
+ * materializing rows. The producer, lifecycle reconciler and status ingest
796
+ * prefer it and otherwise fall back to `listRevisions` with rows dropped.
797
+ * When `account_id` is supplied, only that account's revisions are returned.
798
+ */
799
+ listRevisionMetadata?(reporting_obligation_id: string, account_id?: string): Promise<ReportingLedgerRevisionMetadataV1[]>;
765
800
  commitAdjustment(adjustment: ReportingLedgerAdjustmentV1, lease: ReportingLedgerLeaseV1): Promise<{
766
801
  inserted: boolean;
767
802
  value: ReportingLedgerAdjustmentV1;
768
803
  }>;
769
804
  listAdjustments(reporting_obligation_id: string): Promise<ReportingLedgerAdjustmentV1[]>;
805
+ /** Optional row-free counterpart of `listAdjustments`, preferred when present. */
806
+ listAdjustmentMetadata?(reporting_obligation_id: string): Promise<ReportingLedgerAdjustmentMetadataV1[]>;
770
807
  putConsumerStatus(status: ReportingLedgerConsumerStatusV1): Promise<{
771
808
  inserted: boolean;
772
809
  value: ReportingLedgerConsumerStatusV1;
@@ -4,5 +4,5 @@
4
4
  "source_sha": "4e553ad955f83b49c7d221ab5c3ff78237ad02e3",
5
5
  "source_tarball_sha256": "580656d6466ef9f0d1119985e6726c2efea718dc671e2ad30957fcb2fd54af0f",
6
6
  "upstream_adcp_version": "2.5.3",
7
- "synced_at": "2026-10-08T17:15:08.701Z"
7
+ "synced_at": "2026-10-08T19:53:54.089Z"
8
8
  }
@@ -197,7 +197,7 @@ export interface UpstreamTrafficQueryResult {
197
197
  */
198
198
  export declare function runValidations(validations: StoryboardValidation[], context: ValidationContext): ValidationResult[];
199
199
  /** Capability semver emitted in run summaries and used for advisory expiry. */
200
- export declare const RUNNER_CAPABILITY_VERSION = "15.2.2";
200
+ export declare const RUNNER_CAPABILITY_VERSION = "15.4.0";
201
201
  /** True when a failed validation contributes to the owning step's grade. */
202
202
  export declare function validationFailsStep(result: ValidationResult): boolean;
203
203
  /**
@@ -197,7 +197,7 @@ export interface UpstreamTrafficQueryResult {
197
197
  */
198
198
  export declare function runValidations(validations: StoryboardValidation[], context: ValidationContext): ValidationResult[];
199
199
  /** Capability semver emitted in run summaries and used for advisory expiry. */
200
- export declare const RUNNER_CAPABILITY_VERSION = "15.2.2";
200
+ export declare const RUNNER_CAPABILITY_VERSION = "15.4.0";
201
201
  /** True when a failed validation contributes to the owning step's grade. */
202
202
  export declare function validationFailsStep(result: ValidationResult): boolean;
203
203
  /**
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * AdCP SDK library version
3
3
  */
4
- export declare const LIBRARY_VERSION = "15.2.2";
4
+ export declare const LIBRARY_VERSION = "15.4.0";
5
5
  /**
6
6
  * AdCP specification version this library is built for
7
7
  */
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
33
33
  * Full version information
34
34
  */
35
35
  export declare const VERSION_INFO: {
36
- readonly library: "15.2.2";
36
+ readonly library: "15.4.0";
37
37
  readonly adcp: "3.2.3";
38
38
  readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.0.13", "3.0.14", "3.0.15", "3.0.16", "3.0.17", "3.0.18", "3.0.19", "3.0.20", "3.0.21", "3.0.22", "3.0.23", "3.0.24", "3.0.25", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8", "3.1.9", "3.1.10", "3.1.11", "3.1.12", "3.1.13", "3.1.14", "3.1.15", "3.1.16", "3.1.17", "3.1.18", "3.1.19", "3.1.20", "3.1.21", "3.1.22", "3.1.23", "3.1.24", "3.2.1", "3.2", "3.2.2", "3.2.3"];
39
- readonly generatedAt: "2026-10-08T16:05:52.268Z";
39
+ readonly generatedAt: "2026-10-08T17:53:59.048Z";
40
40
  };
41
41
  /**
42
42
  * Get the AdCP specification version this library is built for
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * AdCP SDK library version
3
3
  */
4
- export declare const LIBRARY_VERSION = "15.2.2";
4
+ export declare const LIBRARY_VERSION = "15.4.0";
5
5
  /**
6
6
  * AdCP specification version this library is built for
7
7
  */
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
33
33
  * Full version information
34
34
  */
35
35
  export declare const VERSION_INFO: {
36
- readonly library: "15.2.2";
36
+ readonly library: "15.4.0";
37
37
  readonly adcp: "3.2.3";
38
38
  readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.0.13", "3.0.14", "3.0.15", "3.0.16", "3.0.17", "3.0.18", "3.0.19", "3.0.20", "3.0.21", "3.0.22", "3.0.23", "3.0.24", "3.0.25", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8", "3.1.9", "3.1.10", "3.1.11", "3.1.12", "3.1.13", "3.1.14", "3.1.15", "3.1.16", "3.1.17", "3.1.18", "3.1.19", "3.1.20", "3.1.21", "3.1.22", "3.1.23", "3.1.24", "3.2.1", "3.2", "3.2.2", "3.2.3"];
39
- readonly generatedAt: "2026-10-08T16:05:52.268Z";
39
+ readonly generatedAt: "2026-10-08T17:53:59.048Z";
40
40
  };
41
41
  /**
42
42
  * Get the AdCP specification version this library is built for
@@ -31,7 +31,7 @@ __export(version_exports, {
31
31
  toReleasePrecisionVersion: () => toReleasePrecisionVersion
32
32
  });
33
33
  module.exports = __toCommonJS(version_exports);
34
- const LIBRARY_VERSION = "15.2.2";
34
+ const LIBRARY_VERSION = "15.4.0";
35
35
  const ADCP_VERSION = "3.2.3";
36
36
  const ADCP_MAJOR_VERSION = 3;
37
37
  const COMPATIBLE_ADCP_VERSIONS = [
@@ -97,10 +97,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
97
97
  "3.2.3"
98
98
  ];
99
99
  const VERSION_INFO = {
100
- library: "15.2.2",
100
+ library: "15.4.0",
101
101
  adcp: "3.2.3",
102
102
  compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
103
- generatedAt: "2026-10-08T16:05:52.268Z"
103
+ generatedAt: "2026-10-08T17:53:59.048Z"
104
104
  };
105
105
  function getAdcpVersion() {
106
106
  return ADCP_VERSION;
@@ -1,4 +1,4 @@
1
- const LIBRARY_VERSION = "15.2.2";
1
+ const LIBRARY_VERSION = "15.4.0";
2
2
  const ADCP_VERSION = "3.2.3";
3
3
  const ADCP_MAJOR_VERSION = 3;
4
4
  const COMPATIBLE_ADCP_VERSIONS = [
@@ -64,10 +64,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
64
64
  "3.2.3"
65
65
  ];
66
66
  const VERSION_INFO = {
67
- library: "15.2.2",
67
+ library: "15.4.0",
68
68
  adcp: "3.2.3",
69
69
  compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
70
- generatedAt: "2026-10-08T16:05:52.268Z"
70
+ generatedAt: "2026-10-08T17:53:59.048Z"
71
71
  };
72
72
  function getAdcpVersion() {
73
73
  return ADCP_VERSION;
@@ -1,7 +1,7 @@
1
1
  # AdCP Type Summary
2
2
 
3
3
  > Generated at: 2026-10-08
4
- > @adcp/sdk v15.2.2
4
+ > @adcp/sdk v15.4.0
5
5
 
6
6
  Curated reference of the types that matter for using the AdCP client. For full generated types see `src/lib/types/tools.generated.ts` and `src/lib/types/core.generated.ts`.
7
7
 
@@ -57,6 +57,7 @@ interface TransportOptions {
57
57
  }
58
58
 
59
59
  interface TaskOptions {
60
+ skipStatusHandlers?: boolean; // Skip inline completion handlers, including continuations
60
61
  timeout?: number; // Absolute whole-task deadline
61
62
  signal?: AbortSignal; // Caller cancellation
62
63
  // Direct A2A mutation route, bound to authenticated principal + account scope.
@@ -39,6 +39,45 @@ the reporting webhook schema. A caller-provided reporting URL must always bring
39
39
  its own complete authentication block; the client never pairs `webhookSecret`
40
40
  with a caller-controlled endpoint.
41
41
 
42
+ ## Inline completion handlers
43
+
44
+ Configured status handlers also run inline when a task call returns a successful
45
+ completed result. By default, an inline handler error rejects that call. For a
46
+ forwarding call whose caller will process the returned result, skip the inline
47
+ handler for that call:
48
+
49
+ ```typescript
50
+ const result = await agent.syncCreatives(params, undefined, { skipStatusHandlers: true });
51
+ ```
52
+
53
+ The option follows submitted waits and deferred resumes, including stored
54
+ continuations resumed after restart. Independently delivered webhook handlers
55
+ still run; `skipStatusHandlers` does not disable webhook registration or delivery.
56
+
57
+ To keep successful results when inline post-processing fails, opt in at client
58
+ construction:
59
+
60
+ ```typescript
61
+ const agent = new SingleAgentClient(sellerConfig, {
62
+ handlers: {
63
+ onSyncCreativesStatusChange: recordCreativeResults,
64
+ },
65
+ isolateStatusHandlerErrors: true,
66
+ onStatusHandlerError: async (error, { handlerName, metadata }) => {
67
+ await recordLocalFailure(error, handlerName, metadata.task_id);
68
+ },
69
+ });
70
+ ```
71
+
72
+ Isolation returns the completed `TaskResult` and allows settlement acknowledgement.
73
+ The SDK adds a warning to `debug_logs` without including the handler's error text.
74
+ The observer receives the original error and completion metadata, so keep secrets
75
+ out of application logs. Observer errors add a warning and do not reject the task.
76
+ The observer is awaited; keep its work bounded so it cannot stall local completion.
77
+ Cancellation, response validation, settlement acknowledgement errors, and webhook
78
+ error handling retain their existing behavior. With isolation disabled, durable
79
+ handler failures remain retryable without redispatching the seller operation.
80
+
42
81
  ## Wire Payload
43
82
 
44
83
  The default RFC 9421 registration has no `authentication` block:
package/docs/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  # Ad Context Protocol (AdCP)
2
2
 
3
3
  > Generated at: 2026-10-08
4
- > Library: @adcp/sdk v15.2.2
4
+ > Library: @adcp/sdk v15.4.0
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.
@@ -11,8 +11,8 @@ below as the publication/deployment gate.
11
11
 
12
12
  | Fact | Value |
13
13
  | --- | --- |
14
- | Exact npm package | `@adcp/sdk@15.2.2` |
15
- | npm integrity | registry-derived after publication; run `npm view @adcp/sdk@15.2.2 dist.integrity` |
14
+ | Exact npm package | `@adcp/sdk@15.4.0` |
15
+ | npm integrity | registry-derived after publication; run `npm view @adcp/sdk@15.4.0 dist.integrity` |
16
16
  | Node.js runtime | `^20.19.0 || >=22.12.0` |
17
17
  | Default AdCP wire release | `3.2.3` |
18
18
  | Maintained wire releases | `v2.5`, `v2.6`, `v3`, `3.0.0`, `3.0`, `3.0.1`, `3.0.2`, `3.0.3`, `3.0.4`, `3.0.5`, `3.0.6`, `3.0.7`, `3.0.8`, `3.0.9`, `3.0.10`, `3.0.11`, `3.0.12`, `3.0.13`, `3.0.14`, `3.0.15`, `3.0.16`, `3.0.17`, `3.0.18`, `3.0.19`, `3.0.20`, `3.0.21`, `3.0.22`, `3.0.23`, `3.0.24`, `3.0.25`, `3.1.0`, `3.1`, `3.1.1`, `3.1.2`, `3.1.3`, `3.1.4`, `3.1.5`, `3.1.6`, `3.1.7`, `3.1.8`, `3.1.9`, `3.1.10`, `3.1.11`, `3.1.12`, `3.1.13`, `3.1.14`, `3.1.15`, `3.1.16`, `3.1.17`, `3.1.18`, `3.1.19`, `3.1.20`, `3.1.21`, `3.1.22`, `3.1.23`, `3.1.24`, `3.2.1`, `3.2`, `3.2.2`, `3.2.3` |
@@ -21,8 +21,8 @@ below as the publication/deployment gate.
21
21
  Install exact production inputs rather than a moving range or dist-tag:
22
22
 
23
23
  ```bash
24
- npm install --save-exact '@adcp/sdk@15.2.2'
25
- npm view '@adcp/sdk@15.2.2' dist.integrity
24
+ npm install --save-exact '@adcp/sdk@15.4.0'
25
+ npm view '@adcp/sdk@15.4.0' dist.integrity
26
26
  ```
27
27
 
28
28
  ### Required and optional peers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adcp/sdk",
3
- "version": "15.2.2",
3
+ "version": "15.4.0",
4
4
  "description": "AdCP SDK — client, server, and compliance harnesses for the AdContext Protocol (MCP + A2A)",
5
5
  "workspaces": [
6
6
  ".",