@adcp/sdk 17.0.0-beta.0 → 17.0.0-beta.2

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 (277) hide show
  1. package/dist/lib/adapters/keyed-account-store.js +1 -1
  2. package/dist/lib/adapters/keyed-account-store.mjs +1 -1
  3. package/dist/lib/adapters/version/3.0/brand-fields.d.mts +7 -1
  4. package/dist/lib/adapters/version/3.0/brand-fields.d.ts +7 -1
  5. package/dist/lib/adapters/version/3.0/brand-fields.js +139 -3
  6. package/dist/lib/adapters/version/3.0/brand-fields.mjs +136 -2
  7. package/dist/lib/adapters/version/3.0/index.js +6 -0
  8. package/dist/lib/adapters/version/3.0/index.mjs +7 -1
  9. package/dist/lib/adapters/version/3.1/bidding.js +1 -1
  10. package/dist/lib/adapters/version/3.1/bidding.mjs +1 -1
  11. package/dist/lib/adapters/version/types.d.mts +5 -0
  12. package/dist/lib/adapters/version/types.d.ts +5 -0
  13. package/dist/lib/canonical-references/index.js +2 -0
  14. package/dist/lib/canonical-references/index.mjs +2 -0
  15. package/dist/lib/client/account-changes.d.mts +3 -1
  16. package/dist/lib/client/account-changes.d.ts +3 -1
  17. package/dist/lib/client/account-changes.js +22 -2
  18. package/dist/lib/client/account-changes.mjs +22 -2
  19. package/dist/lib/conformance/oracle.js +3 -2
  20. package/dist/lib/conformance/oracle.mjs +3 -2
  21. package/dist/lib/core/AgentClient.d.mts +165 -1
  22. package/dist/lib/core/AgentClient.d.ts +165 -1
  23. package/dist/lib/core/AgentClient.js +446 -8
  24. package/dist/lib/core/AgentClient.mjs +446 -8
  25. package/dist/lib/core/ConversationTypes.d.mts +8 -0
  26. package/dist/lib/core/ConversationTypes.d.ts +8 -0
  27. package/dist/lib/core/ProtocolResponseParser.d.mts +1 -1
  28. package/dist/lib/core/ProtocolResponseParser.d.ts +1 -1
  29. package/dist/lib/core/ProtocolResponseParser.js +28 -18
  30. package/dist/lib/core/ProtocolResponseParser.mjs +11 -1
  31. package/dist/lib/core/ResponseValidator.js +1 -1
  32. package/dist/lib/core/ResponseValidator.mjs +1 -1
  33. package/dist/lib/core/SingleAgentClient.d.mts +122 -7
  34. package/dist/lib/core/SingleAgentClient.d.ts +122 -7
  35. package/dist/lib/core/SingleAgentClient.js +601 -96
  36. package/dist/lib/core/SingleAgentClient.mjs +590 -84
  37. package/dist/lib/core/TaskExecutor.d.mts +7 -0
  38. package/dist/lib/core/TaskExecutor.d.ts +7 -0
  39. package/dist/lib/core/TaskExecutor.js +23 -8
  40. package/dist/lib/core/TaskExecutor.mjs +23 -8
  41. package/dist/lib/core/task-status.d.mts +2 -0
  42. package/dist/lib/core/task-status.d.ts +2 -0
  43. package/dist/lib/core/task-status.js +11 -2
  44. package/dist/lib/core/task-status.mjs +9 -1
  45. package/dist/lib/errors/index.d.mts +22 -0
  46. package/dist/lib/errors/index.d.ts +22 -0
  47. package/dist/lib/errors/index.js +43 -0
  48. package/dist/lib/errors/index.mjs +42 -0
  49. package/dist/lib/identity-3.3/trust.d.mts +1 -4
  50. package/dist/lib/identity-3.3/trust.d.ts +1 -4
  51. package/dist/lib/identity-3.3/trust.js +34 -21
  52. package/dist/lib/identity-3.3/trust.mjs +34 -21
  53. package/dist/lib/identity-3.3/validation.js +5 -3
  54. package/dist/lib/identity-3.3/validation.mjs +6 -4
  55. package/dist/lib/index.d.mts +7 -53
  56. package/dist/lib/index.d.ts +7 -53
  57. package/dist/lib/index.js +13 -0
  58. package/dist/lib/index.mjs +9 -0
  59. package/dist/lib/media-buy/compatibility.js +6 -1
  60. package/dist/lib/media-buy/compatibility.mjs +6 -1
  61. package/dist/lib/protocols/mcp-modern.d.mts +2 -0
  62. package/dist/lib/protocols/mcp-modern.d.ts +2 -0
  63. package/dist/lib/protocols/mcp-modern.js +31 -5
  64. package/dist/lib/protocols/mcp-modern.mjs +31 -5
  65. package/dist/lib/protocols/mcp-scope.d.mts +6 -1
  66. package/dist/lib/protocols/mcp-scope.d.ts +6 -1
  67. package/dist/lib/protocols/mcp-scope.js +25 -3
  68. package/dist/lib/protocols/mcp-scope.mjs +24 -3
  69. package/dist/lib/protocols/mcp.js +12 -6
  70. package/dist/lib/protocols/mcp.mjs +13 -6
  71. package/dist/lib/reporting/inspection.js +1 -0
  72. package/dist/lib/reporting/inspection.mjs +1 -0
  73. package/dist/lib/reporting/ledger/coverage.d.mts +5 -0
  74. package/dist/lib/reporting/ledger/coverage.d.ts +5 -0
  75. package/dist/lib/reporting/ledger/coverage.js +37 -3
  76. package/dist/lib/reporting/ledger/coverage.mjs +36 -3
  77. package/dist/lib/reporting/ledger/handler.js +7 -4
  78. package/dist/lib/reporting/ledger/handler.mjs +7 -4
  79. package/dist/lib/reporting/ledger/producer.js +215 -69
  80. package/dist/lib/reporting/ledger/producer.mjs +217 -70
  81. package/dist/lib/reporting/ledger/schedule.d.mts +2 -2
  82. package/dist/lib/reporting/ledger/schedule.d.ts +2 -2
  83. package/dist/lib/reporting/ledger/schedule.js +8 -5
  84. package/dist/lib/reporting/ledger/schedule.mjs +8 -5
  85. package/dist/lib/reporting/ledger/status-ingest.d.mts +1 -1
  86. package/dist/lib/reporting/ledger/status-ingest.d.ts +1 -1
  87. package/dist/lib/reporting/ledger/status-ingest.js +4 -3
  88. package/dist/lib/reporting/ledger/status-ingest.mjs +4 -3
  89. package/dist/lib/reporting/ledger/types.d.mts +36 -1
  90. package/dist/lib/reporting/ledger/types.d.ts +36 -1
  91. package/dist/lib/reporting/service/index.d.mts +14 -1
  92. package/dist/lib/reporting/service/index.d.ts +14 -1
  93. package/dist/lib/reporting/service/index.js +24 -12
  94. package/dist/lib/reporting/service/index.mjs +24 -12
  95. package/dist/lib/schemas/browser.d.mts +2 -0
  96. package/dist/lib/schemas/browser.d.ts +2 -0
  97. package/dist/lib/schemas/browser.js +3 -0
  98. package/dist/lib/schemas/browser.mjs +2 -0
  99. package/dist/lib/schemas/index.d.mts +5 -3
  100. package/dist/lib/schemas/index.d.ts +5 -3
  101. package/dist/lib/schemas/index.js +5 -0
  102. package/dist/lib/schemas/index.mjs +4 -0
  103. package/dist/lib/server/account-response-identity.d.mts +17 -0
  104. package/dist/lib/server/account-response-identity.d.ts +17 -0
  105. package/dist/lib/server/account-response-identity.js +52 -0
  106. package/dist/lib/server/account-response-identity.mjs +28 -0
  107. package/dist/lib/server/account-sync-contract.js +2 -2
  108. package/dist/lib/server/account-sync-contract.mjs +2 -2
  109. package/dist/lib/server/create-adcp-server.js +33 -20
  110. package/dist/lib/server/create-adcp-server.mjs +36 -12
  111. package/dist/lib/server/decisioning/account.d.mts +36 -10
  112. package/dist/lib/server/decisioning/account.d.ts +36 -10
  113. package/dist/lib/server/decisioning/account.js +33 -14
  114. package/dist/lib/server/decisioning/account.mjs +33 -14
  115. package/dist/lib/server/decisioning/runtime/from-platform.d.mts +5 -5
  116. package/dist/lib/server/decisioning/runtime/from-platform.d.ts +5 -5
  117. package/dist/lib/server/decisioning/runtime/from-platform.js +4 -4
  118. package/dist/lib/server/decisioning/runtime/from-platform.mjs +4 -4
  119. package/dist/lib/server/media-buy-store.d.mts +26 -12
  120. package/dist/lib/server/media-buy-store.d.ts +26 -12
  121. package/dist/lib/server/media-buy-store.js +69 -21
  122. package/dist/lib/server/media-buy-store.mjs +69 -21
  123. package/dist/lib/server/notification-subscriptions/runtime.d.mts +1 -1
  124. package/dist/lib/server/notification-subscriptions/runtime.d.ts +1 -1
  125. package/dist/lib/server/notification-subscriptions/runtime.js +4 -0
  126. package/dist/lib/server/notification-subscriptions/runtime.mjs +4 -0
  127. package/dist/lib/server/responses.js +9 -1
  128. package/dist/lib/server/responses.mjs +9 -1
  129. package/dist/lib/signing/agent-fetch.d.mts +1 -1
  130. package/dist/lib/signing/agent-fetch.d.ts +1 -1
  131. package/dist/lib/signing/agent-fetch.js +3 -3
  132. package/dist/lib/signing/agent-fetch.mjs +3 -3
  133. package/dist/lib/testing/client.d.mts +1 -0
  134. package/dist/lib/testing/client.d.ts +1 -0
  135. package/dist/lib/testing/client.js +6 -5
  136. package/dist/lib/testing/client.mjs +6 -5
  137. package/dist/lib/testing/storyboard/agent-routing.d.mts +5 -2
  138. package/dist/lib/testing/storyboard/agent-routing.d.ts +5 -2
  139. package/dist/lib/testing/storyboard/agent-routing.js +11 -4
  140. package/dist/lib/testing/storyboard/agent-routing.mjs +11 -4
  141. package/dist/lib/testing/storyboard/canonical-format-satisfaction.js +60 -14
  142. package/dist/lib/testing/storyboard/canonical-format-satisfaction.mjs +60 -14
  143. package/dist/lib/testing/storyboard/compliance.d.mts +2 -5
  144. package/dist/lib/testing/storyboard/compliance.d.ts +2 -5
  145. package/dist/lib/testing/storyboard/compliance.js +62 -13
  146. package/dist/lib/testing/storyboard/compliance.mjs +63 -14
  147. package/dist/lib/testing/storyboard/context.d.mts +13 -3
  148. package/dist/lib/testing/storyboard/context.d.ts +13 -3
  149. package/dist/lib/testing/storyboard/context.js +15 -10
  150. package/dist/lib/testing/storyboard/context.mjs +15 -10
  151. package/dist/lib/testing/storyboard/request-signing/vector-loader.d.mts +2 -0
  152. package/dist/lib/testing/storyboard/request-signing/vector-loader.d.ts +2 -0
  153. package/dist/lib/testing/storyboard/request-signing/vector-loader.js +11 -0
  154. package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +10 -0
  155. package/dist/lib/testing/storyboard/runner.js +141 -13
  156. package/dist/lib/testing/storyboard/runner.mjs +141 -13
  157. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  158. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  159. package/dist/lib/testing/storyboard/validations.js +4 -1
  160. package/dist/lib/testing/storyboard/validations.mjs +4 -1
  161. package/dist/lib/testing/test-controller.js +3 -0
  162. package/dist/lib/testing/test-controller.mjs +3 -0
  163. package/dist/lib/types/accept-proposal.d.ts +20 -28
  164. package/dist/lib/types/asset-instances.d.mts +1 -1
  165. package/dist/lib/types/asset-instances.d.ts +1 -1
  166. package/dist/lib/types/buy-products.d.ts +20 -28
  167. package/dist/lib/types/check-governance.d.ts +20 -28
  168. package/dist/lib/types/comply-test-controller.d.ts +20 -28
  169. package/dist/lib/types/control-media-buy.d.ts +20 -28
  170. package/dist/lib/types/core.generated.d.mts +273 -184
  171. package/dist/lib/types/core.generated.d.ts +273 -184
  172. package/dist/lib/types/create-media-buy.d.ts +20 -28
  173. package/dist/lib/types/get-media-buys.d.ts +20 -28
  174. package/dist/lib/types/get-products.d.ts +20 -28
  175. package/dist/lib/types/index.d.mts +6 -1
  176. package/dist/lib/types/index.d.ts +6 -1
  177. package/dist/lib/types/index.js +6 -1
  178. package/dist/lib/types/index.mjs +4 -1
  179. package/dist/lib/types/inline-enums.generated.d.mts +30 -4
  180. package/dist/lib/types/inline-enums.generated.d.ts +30 -4
  181. package/dist/lib/types/inline-enums.generated.js +41 -2
  182. package/dist/lib/types/inline-enums.generated.mjs +28 -2
  183. package/dist/lib/types/list-products.d.ts +20 -28
  184. package/dist/lib/types/list-proposals.d.ts +20 -28
  185. package/dist/lib/types/media-buy-delivery-report.d.mts +6 -0
  186. package/dist/lib/types/media-buy-delivery-report.d.ts +6 -0
  187. package/dist/lib/types/media-buy-delivery-report.js +16 -0
  188. package/dist/lib/types/media-buy-delivery-report.mjs +0 -0
  189. package/dist/lib/types/refine-proposals.d.ts +20 -28
  190. package/dist/lib/types/request-proposals.d.ts +20 -28
  191. package/dist/lib/types/schemas.generated.d.ts +63113 -127273
  192. package/dist/lib/types/schemas.generated.js +759 -770
  193. package/dist/lib/types/schemas.generated.mjs +759 -770
  194. package/dist/lib/types/targeting-keys.generated.d.mts +4 -0
  195. package/dist/lib/types/targeting-keys.generated.d.ts +4 -0
  196. package/dist/lib/types/targeting-keys.generated.js +109 -0
  197. package/dist/lib/types/targeting-keys.generated.mjs +84 -0
  198. package/dist/lib/types/tools.generated.d.mts +2 -68
  199. package/dist/lib/types/tools.generated.d.ts +2 -68
  200. package/dist/lib/types/update-media-buy.d.ts +20 -28
  201. package/dist/lib/utils/adcp-version-config.js +5 -2
  202. package/dist/lib/utils/adcp-version-config.mjs +5 -2
  203. package/dist/lib/utils/envelope-status-compat.d.mts +18 -1
  204. package/dist/lib/utils/envelope-status-compat.d.ts +18 -1
  205. package/dist/lib/utils/envelope-status-compat.js +11 -5
  206. package/dist/lib/utils/envelope-status-compat.mjs +11 -5
  207. package/dist/lib/utils/response-unwrapper.js +18 -10
  208. package/dist/lib/utils/response-unwrapper.mjs +18 -10
  209. package/dist/lib/v2/format-schema/resolver.js +1 -1
  210. package/dist/lib/v2/format-schema/resolver.mjs +1 -1
  211. package/dist/lib/v2/projection/creative-delivery.js +159 -39
  212. package/dist/lib/v2/projection/creative-delivery.mjs +163 -39
  213. package/dist/lib/v2/projection/index.d.mts +1 -0
  214. package/dist/lib/v2/projection/index.d.ts +1 -0
  215. package/dist/lib/v2/projection/index.js +3 -0
  216. package/dist/lib/v2/projection/index.mjs +2 -0
  217. package/dist/lib/v2/projection/legacy-metadata.d.mts +19 -0
  218. package/dist/lib/v2/projection/legacy-metadata.d.ts +19 -0
  219. package/dist/lib/v2/projection/legacy-metadata.js +91 -0
  220. package/dist/lib/v2/projection/legacy-metadata.mjs +86 -0
  221. package/dist/lib/v2/projection/v1-to-v2.d.mts +1 -1
  222. package/dist/lib/v2/projection/v1-to-v2.d.ts +1 -1
  223. package/dist/lib/v2/projection/v1-to-v2.js +12 -9
  224. package/dist/lib/v2/projection/v1-to-v2.mjs +12 -9
  225. package/dist/lib/validation/budget-allocation.d.mts +3 -3
  226. package/dist/lib/validation/budget-allocation.d.ts +3 -3
  227. package/dist/lib/validation/compact-schema.d.mts +27 -0
  228. package/dist/lib/validation/compact-schema.d.ts +27 -0
  229. package/dist/lib/validation/compact-schema.js +276 -0
  230. package/dist/lib/validation/compact-schema.mjs +250 -0
  231. package/dist/lib/validation/media-buy-delivery-report.d.mts +8 -0
  232. package/dist/lib/validation/media-buy-delivery-report.d.ts +8 -0
  233. package/dist/lib/validation/media-buy-delivery-report.js +31 -0
  234. package/dist/lib/validation/media-buy-delivery-report.mjs +7 -0
  235. package/dist/lib/validation/nonempty-array.d.mts +15 -0
  236. package/dist/lib/validation/nonempty-array.d.ts +15 -0
  237. package/dist/lib/validation/nonempty-array.js +41 -0
  238. package/dist/lib/validation/nonempty-array.mjs +7 -0
  239. package/dist/lib/validation/schema-diagnostic-origins.d.mts +21 -0
  240. package/dist/lib/validation/schema-diagnostic-origins.d.ts +21 -0
  241. package/dist/lib/validation/schema-diagnostic-origins.js +193 -0
  242. package/dist/lib/validation/schema-diagnostic-origins.mjs +169 -0
  243. package/dist/lib/validation/schema-loader.js +86 -16
  244. package/dist/lib/validation/schema-loader.mjs +82 -16
  245. package/dist/lib/validation/schema-validator.d.mts +10 -19
  246. package/dist/lib/validation/schema-validator.d.ts +10 -19
  247. package/dist/lib/validation/schema-validator.js +136 -43
  248. package/dist/lib/validation/schema-validator.mjs +140 -44
  249. package/dist/lib/validation/sync-creatives.d.mts +22 -22
  250. package/dist/lib/validation/sync-creatives.d.ts +22 -22
  251. package/dist/lib/version.d.mts +3 -3
  252. package/dist/lib/version.d.ts +3 -3
  253. package/dist/lib/version.js +3 -3
  254. package/dist/lib/version.mjs +3 -3
  255. package/dist/lib/webhooks/index.d.mts +3 -0
  256. package/dist/lib/webhooks/index.d.ts +3 -0
  257. package/dist/lib/webhooks/index.js +3 -0
  258. package/dist/lib/webhooks/index.mjs +2 -0
  259. package/dist/lib/webhooks/media-buy-delivery.d.mts +43 -0
  260. package/dist/lib/webhooks/media-buy-delivery.d.ts +43 -0
  261. package/dist/lib/webhooks/media-buy-delivery.js +119 -0
  262. package/dist/lib/webhooks/media-buy-delivery.mjs +94 -0
  263. package/docs/TYPE-SUMMARY.md +2 -2
  264. package/docs/guides/COLLECTION-FIXTURE-FOUNDATION.md +11 -0
  265. package/docs/guides/DELIVERY-WEBHOOK-PARSING.md +142 -0
  266. package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +27 -1
  267. package/docs/guides/REPORTING-LEDGER.md +93 -0
  268. package/docs/guides/REPORTING-STABILITY.md +1 -1
  269. package/docs/guides/TYPED-CLIENT-TOOLS.md +49 -0
  270. package/docs/llms.txt +2 -2
  271. package/docs/migration-14.x-rc-worksheet.md +4 -4
  272. package/docs/migration-16-to-17.md +202 -2
  273. package/docs/migration-6.6-to-6.7.md +14 -0
  274. package/docs/migration-account-change-feed.md +15 -0
  275. package/docs/migration-reporting-storage-package.md +1 -1
  276. package/docs/recipes/verifying-inbound-webhooks.md +18 -2
  277. package/package.json +2 -2
@@ -16,6 +16,7 @@ import { is401Error } from "../errors/index.mjs";
16
16
  import { withSpan, injectTraceHeaders } from "../observability/tracing.mjs";
17
17
  import {
18
18
  currentMCPConnectionScopeKey,
19
+ markMCPConnectionScopeInterrupted,
19
20
  isMCPConnectionScopeCacheKeyActive,
20
21
  registerMCPConnectionScopeCleanup,
21
22
  registerMCPConnectionScopePending
@@ -255,9 +256,15 @@ async function createNegotiatedClient(cacheKey, options, authHeaders, useCachedD
255
256
  getCapability: options.signingContext.getCapability,
256
257
  adcpVersion: options.signingContext.adcpVersion
257
258
  }) : diagnosticFetch;
259
+ const transportFetch = wrapFetchWithCapture(withPerRequestTraceHeaders(signedFetch));
260
+ let throttleRetryAfter;
258
261
  const transport = new StreamableHTTPClientTransport(new URL(options.agentUrl), {
259
262
  requestInit: { headers: splitConnectionHeaders(authHeaders).identity, redirect: "manual" },
260
- fetch: wrapFetchWithCapture(withPerRequestTraceHeaders(signedFetch)),
263
+ fetch: async (input, init) => {
264
+ const response = await transportFetch(input, init);
265
+ throttleRetryAfter = response.status === 429 || response.status === 503 ? response.headers.get("retry-after") ?? void 0 : void 0;
266
+ return response;
267
+ },
261
268
  ...options.authProvider && {
262
269
  authProvider: options.authProvider
263
270
  }
@@ -300,8 +307,22 @@ async function createNegotiatedClient(cacheKey, options, authHeaders, useCachedD
300
307
  if (isNegotiationAuthFailure(error) && !skipProbe && presentedStaticCredential(authHeaders, options.authProvider)) {
301
308
  return createNegotiatedClient(cacheKey, options, authHeaders, useCachedDiscovery, true);
302
309
  }
303
- throw error;
310
+ throw withRetryAfter(error, throttleRetryAfter);
311
+ }
312
+ }
313
+ function withRetryAfter(error, retryAfter) {
314
+ if (retryAfter === void 0 || !error || typeof error !== "object") return error;
315
+ const status = httpStatusOf(error);
316
+ if (status !== 429 && status !== 503) return error;
317
+ try {
318
+ Object.defineProperty(error, "retryAfter", { value: retryAfter, enumerable: false, configurable: true });
319
+ } catch {
304
320
  }
321
+ return error;
322
+ }
323
+ function retryAfterOf(error) {
324
+ const value = error && typeof error === "object" ? error.retryAfter : void 0;
325
+ return typeof value === "string" ? value : void 0;
305
326
  }
306
327
  async function getOrCreateModernConnection(cacheKey, options, authHeaders) {
307
328
  for (let attempt = 0; ; attempt++) {
@@ -321,12 +342,12 @@ function createSharedModernConnection(cacheKey, options, authHeaders) {
321
342
  const promise = createNegotiatedClient(cacheKey, options, authHeaders).then(async (client) => {
322
343
  if ((client.getProtocolEra() === "modern" || options.handleLegacy === true) && generation === connectionGeneration) {
323
344
  modernConnections.set(cacheKey, client);
324
- registerMCPConnectionScopeCleanup("modern", cacheKey, async () => {
345
+ registerMCPConnectionScopeCleanup("modern", cacheKey, async (terminateSession) => {
325
346
  if (modernConnections.get(cacheKey) !== client) return;
326
347
  modernConnections.delete(cacheKey);
327
348
  legacyConnectionExpiresAt.delete(cacheKey);
328
349
  modernDiscoveries.delete(cacheKey);
329
- await closeModernClient(client);
350
+ await closeModernClient(client, terminateSession);
330
351
  });
331
352
  if (client.getProtocolEra() === "legacy") {
332
353
  legacyConnectionExpiresAt.set(cacheKey, Date.now() + LEGACY_CLASSIFICATION_TTL_MS);
@@ -348,7 +369,10 @@ function createSharedModernConnection(cacheKey, options, authHeaders) {
348
369
  return promise;
349
370
  }
350
371
  async function retireFailedConnection(cacheKey, client, error) {
351
- if (isAbortOrTimeoutError(error)) return;
372
+ if (isAbortOrTimeoutError(error)) {
373
+ markMCPConnectionScopeInterrupted();
374
+ return;
375
+ }
352
376
  if (modernConnections.get(cacheKey) === client) {
353
377
  modernConnections.delete(cacheKey);
354
378
  legacyConnectionExpiresAt.delete(cacheKey);
@@ -517,6 +541,7 @@ async function probeModernMCPConnection(agentUrl, authToken, customHeaders, opti
517
541
  options.allowPrivateIp
518
542
  );
519
543
  const scoped = shouldReuseMCPConnection(options.fetchFn !== void 0);
544
+ if (isKnownLegacy(cacheKey)) return { connected: false };
520
545
  let client;
521
546
  return runWithMCPCallContext(
522
547
  callContextFor({ customHeaders, requestTimeoutMs: options.requestTimeoutMs }),
@@ -656,6 +681,7 @@ async function closeModernMCPConnections() {
656
681
  export {
657
682
  closeModernMCPConnections,
658
683
  probeModernMCPConnection,
684
+ retryAfterOf,
659
685
  tryCallModernMCPTool,
660
686
  tryListModernMCPTools
661
687
  };
@@ -1,6 +1,8 @@
1
- type ScopeCleanup = () => Promise<void>;
1
+ type ScopeCleanup = (terminateSession: boolean) => Promise<void>;
2
2
  /** Cache-key suffix for the current caller-owned MCP workflow. */
3
3
  export declare function currentMCPConnectionScopeKey(): string | undefined;
4
+ /** Preserve seller work even when a higher layer handles a request timeout. */
5
+ export declare function markMCPConnectionScopeInterrupted(): void;
4
6
  /** Whether an LRU entry belongs to a workflow that has not finished yet. */
5
7
  export declare function isMCPConnectionScopeCacheKeyActive(cacheKey: string): boolean;
6
8
  /** Register one idempotent cache-entry cleanup with the current workflow. */
@@ -20,6 +22,9 @@ export declare function closeCurrentMCPConnectionScope(): Promise<boolean>;
20
22
  * Per-call correlation headers, `AbortSignal`s and timeouts do not choose the
21
23
  * session. The scope terminates its sessions once, when `fn` settles; pass
22
24
  * `{ isolate: true }` to force a fresh scope even when called from inside one.
25
+ * Explicit workflows terminate sessions at exit even if an inner call returned
26
+ * submitted/working; the caller owns that lifetime. Implicit task scopes may
27
+ * close only their local clients when their final result remains nonterminal.
23
28
  *
24
29
  * @example
25
30
  * ```ts
@@ -1,6 +1,8 @@
1
- type ScopeCleanup = () => Promise<void>;
1
+ type ScopeCleanup = (terminateSession: boolean) => Promise<void>;
2
2
  /** Cache-key suffix for the current caller-owned MCP workflow. */
3
3
  export declare function currentMCPConnectionScopeKey(): string | undefined;
4
+ /** Preserve seller work even when a higher layer handles a request timeout. */
5
+ export declare function markMCPConnectionScopeInterrupted(): void;
4
6
  /** Whether an LRU entry belongs to a workflow that has not finished yet. */
5
7
  export declare function isMCPConnectionScopeCacheKeyActive(cacheKey: string): boolean;
6
8
  /** Register one idempotent cache-entry cleanup with the current workflow. */
@@ -20,6 +22,9 @@ export declare function closeCurrentMCPConnectionScope(): Promise<boolean>;
20
22
  * Per-call correlation headers, `AbortSignal`s and timeouts do not choose the
21
23
  * session. The scope terminates its sessions once, when `fn` settles; pass
22
24
  * `{ isolate: true }` to force a fresh scope even when called from inside one.
25
+ * Explicit workflows terminate sessions at exit even if an inner call returned
26
+ * submitted/working; the caller owns that lifetime. Implicit task scopes may
27
+ * close only their local clients when their final result remains nonterminal.
23
28
  *
24
29
  * @example
25
30
  * ```ts
@@ -21,18 +21,24 @@ __export(mcp_scope_exports, {
21
21
  closeCurrentMCPConnectionScope: () => closeCurrentMCPConnectionScope,
22
22
  currentMCPConnectionScopeKey: () => currentMCPConnectionScopeKey,
23
23
  isMCPConnectionScopeCacheKeyActive: () => isMCPConnectionScopeCacheKeyActive,
24
+ markMCPConnectionScopeInterrupted: () => markMCPConnectionScopeInterrupted,
24
25
  registerMCPConnectionScopeCleanup: () => registerMCPConnectionScopeCleanup,
25
26
  registerMCPConnectionScopePending: () => registerMCPConnectionScopePending,
26
27
  withMCPConnectionScope: () => withMCPConnectionScope
27
28
  });
28
29
  module.exports = __toCommonJS(mcp_scope_exports);
29
30
  var import_node_async_hooks = require("node:async_hooks");
31
+ var import_abort = require('./abort.js');
30
32
  const scopeStorage = new import_node_async_hooks.AsyncLocalStorage();
31
33
  const activeScopeKeys = /* @__PURE__ */ new Set();
32
34
  let nextScopeId = 0;
33
35
  function currentMCPConnectionScopeKey() {
34
36
  return scopeStorage.getStore()?.cacheKey;
35
37
  }
38
+ function markMCPConnectionScopeInterrupted() {
39
+ const scope = scopeStorage.getStore();
40
+ if (scope?.preserveOnInterruption) scope.interrupted = true;
41
+ }
36
42
  function isMCPConnectionScopeCacheKeyActive(cacheKey) {
37
43
  const marker = cacheKey.lastIndexOf("::scope:");
38
44
  return marker >= 0 && activeScopeKeys.has(cacheKey.slice(marker + 2));
@@ -55,17 +61,32 @@ async function closeCurrentMCPConnectionScope() {
55
61
  }
56
62
  const cleanups = [...scope.cleanups.values()];
57
63
  scope.cleanups.clear();
58
- await Promise.allSettled(cleanups.map((cleanup) => cleanup()));
64
+ await Promise.allSettled(
65
+ cleanups.map((cleanup) => cleanup(!scope.signal?.aborted && !scope.interrupted && !scope.preserveSession))
66
+ );
59
67
  return true;
60
68
  }
61
69
  async function withMCPConnectionScope(fn, options = {}) {
62
70
  if (scopeStorage.getStore() && options.isolate !== true) return fn();
63
71
  const id = ++nextScopeId;
64
- const scope = { id, cacheKey: `scope:${id}`, pending: /* @__PURE__ */ new Set(), cleanups: /* @__PURE__ */ new Map() };
72
+ const scope = {
73
+ id,
74
+ cacheKey: `scope:${id}`,
75
+ pending: /* @__PURE__ */ new Set(),
76
+ cleanups: /* @__PURE__ */ new Map(),
77
+ signal: options.signal,
78
+ preserveOnInterruption: options.preserveOnInterruption,
79
+ preserveSessionOnResult: options.preserveSessionOnResult
80
+ };
65
81
  activeScopeKeys.add(scope.cacheKey);
66
82
  return scopeStorage.run(scope, async () => {
67
83
  try {
68
- return await fn();
84
+ const result = await fn();
85
+ scope.preserveSession = scope.preserveSessionOnResult?.(result) === true;
86
+ return result;
87
+ } catch (error) {
88
+ if ((0, import_abort.isAbortOrTimeoutError)(error)) markMCPConnectionScopeInterrupted();
89
+ throw error;
69
90
  } finally {
70
91
  try {
71
92
  await closeCurrentMCPConnectionScope();
@@ -80,6 +101,7 @@ async function withMCPConnectionScope(fn, options = {}) {
80
101
  closeCurrentMCPConnectionScope,
81
102
  currentMCPConnectionScopeKey,
82
103
  isMCPConnectionScopeCacheKeyActive,
104
+ markMCPConnectionScopeInterrupted,
83
105
  registerMCPConnectionScopeCleanup,
84
106
  registerMCPConnectionScopePending,
85
107
  withMCPConnectionScope
@@ -4,12 +4,17 @@ import { createRequire as __adcpCreateRequire } from "node:module";
4
4
  const __dirname = __adcpDirname(__adcpFileURLToPath(import.meta.url));
5
5
  const require2 = __adcpCreateRequire(import.meta.url);
6
6
  import { AsyncLocalStorage } from "node:async_hooks";
7
+ import { isAbortOrTimeoutError } from "./abort.mjs";
7
8
  const scopeStorage = new AsyncLocalStorage();
8
9
  const activeScopeKeys = /* @__PURE__ */ new Set();
9
10
  let nextScopeId = 0;
10
11
  function currentMCPConnectionScopeKey() {
11
12
  return scopeStorage.getStore()?.cacheKey;
12
13
  }
14
+ function markMCPConnectionScopeInterrupted() {
15
+ const scope = scopeStorage.getStore();
16
+ if (scope?.preserveOnInterruption) scope.interrupted = true;
17
+ }
13
18
  function isMCPConnectionScopeCacheKeyActive(cacheKey) {
14
19
  const marker = cacheKey.lastIndexOf("::scope:");
15
20
  return marker >= 0 && activeScopeKeys.has(cacheKey.slice(marker + 2));
@@ -32,17 +37,32 @@ async function closeCurrentMCPConnectionScope() {
32
37
  }
33
38
  const cleanups = [...scope.cleanups.values()];
34
39
  scope.cleanups.clear();
35
- await Promise.allSettled(cleanups.map((cleanup) => cleanup()));
40
+ await Promise.allSettled(
41
+ cleanups.map((cleanup) => cleanup(!scope.signal?.aborted && !scope.interrupted && !scope.preserveSession))
42
+ );
36
43
  return true;
37
44
  }
38
45
  async function withMCPConnectionScope(fn, options = {}) {
39
46
  if (scopeStorage.getStore() && options.isolate !== true) return fn();
40
47
  const id = ++nextScopeId;
41
- const scope = { id, cacheKey: `scope:${id}`, pending: /* @__PURE__ */ new Set(), cleanups: /* @__PURE__ */ new Map() };
48
+ const scope = {
49
+ id,
50
+ cacheKey: `scope:${id}`,
51
+ pending: /* @__PURE__ */ new Set(),
52
+ cleanups: /* @__PURE__ */ new Map(),
53
+ signal: options.signal,
54
+ preserveOnInterruption: options.preserveOnInterruption,
55
+ preserveSessionOnResult: options.preserveSessionOnResult
56
+ };
42
57
  activeScopeKeys.add(scope.cacheKey);
43
58
  return scopeStorage.run(scope, async () => {
44
59
  try {
45
- return await fn();
60
+ const result = await fn();
61
+ scope.preserveSession = scope.preserveSessionOnResult?.(result) === true;
62
+ return result;
63
+ } catch (error) {
64
+ if (isAbortOrTimeoutError(error)) markMCPConnectionScopeInterrupted();
65
+ throw error;
46
66
  } finally {
47
67
  try {
48
68
  await closeCurrentMCPConnectionScope();
@@ -56,6 +76,7 @@ export {
56
76
  closeCurrentMCPConnectionScope,
57
77
  currentMCPConnectionScopeKey,
58
78
  isMCPConnectionScopeCacheKeyActive,
79
+ markMCPConnectionScopeInterrupted,
59
80
  registerMCPConnectionScopeCleanup,
60
81
  registerMCPConnectionScopePending,
61
82
  withMCPConnectionScope
@@ -207,10 +207,10 @@ async function getOrCreateConnection(cacheKey, baseUrl, authHeaders, debugLogs,
207
207
  throw new Error(`MCP ${label} completed after connection teardown`);
208
208
  }
209
209
  connectionCache.set(cacheKey, client);
210
- (0, import_mcp_scope.registerMCPConnectionScopeCleanup)("legacy", cacheKey, async () => {
210
+ (0, import_mcp_scope.registerMCPConnectionScopeCleanup)("legacy", cacheKey, async (terminateSession) => {
211
211
  if (connectionCache.get(cacheKey) !== client) return;
212
212
  connectionCache.delete(cacheKey);
213
- await closeMCPClient(client);
213
+ await closeMCPClient(client, terminateSession);
214
214
  });
215
215
  evictLeastRecentlyUsed();
216
216
  return client;
@@ -280,10 +280,10 @@ async function getOrCreateOAuthConnection(cacheKey, options) {
280
280
  throw new Error("OAuth MCP connection completed after connection teardown");
281
281
  }
282
282
  oauthConnectionCache.set(cacheKey, client);
283
- (0, import_mcp_scope.registerMCPConnectionScopeCleanup)("oauth", cacheKey, async () => {
283
+ (0, import_mcp_scope.registerMCPConnectionScopeCleanup)("oauth", cacheKey, async (terminateSession) => {
284
284
  if (oauthConnectionCache.get(cacheKey) !== client) return;
285
285
  oauthConnectionCache.delete(cacheKey);
286
- await closeMCPClient(client);
286
+ await closeMCPClient(client, terminateSession);
287
287
  });
288
288
  evictLeastRecentlyUsedOAuth();
289
289
  return client;
@@ -337,7 +337,10 @@ async function withCachedOAuthConnectionImpl(options, label, fn) {
337
337
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
338
338
  error
339
339
  });
340
- if ((0, import_abort.isAbortOrTimeoutError)(error)) throw error;
340
+ if ((0, import_abort.isAbortOrTimeoutError)(error)) {
341
+ (0, import_mcp_scope.markMCPConnectionScopeInterrupted)();
342
+ throw error;
343
+ }
341
344
  if ((0, import_errors.is401Error)(error)) {
342
345
  options.debugLogs.push({
343
346
  type: "warning",
@@ -405,7 +408,10 @@ async function withCachedConnectionImpl(agentUrl, authToken, authHeaders, debugL
405
408
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
406
409
  error
407
410
  });
408
- if ((0, import_abort.isAbortOrTimeoutError)(error)) throw error;
411
+ if ((0, import_abort.isAbortOrTimeoutError)(error)) {
412
+ (0, import_mcp_scope.markMCPConnectionScopeInterrupted)();
413
+ throw error;
414
+ }
409
415
  if ((0, import_errors.is401Error)(error)) {
410
416
  debugLogs.push({
411
417
  type: "warning",
@@ -35,6 +35,7 @@ import {
35
35
  import { closeModernMCPConnections, tryCallModernMCPTool } from "./mcp-modern.mjs";
36
36
  import {
37
37
  currentMCPConnectionScopeKey,
38
+ markMCPConnectionScopeInterrupted,
38
39
  isMCPConnectionScopeCacheKeyActive,
39
40
  registerMCPConnectionScopeCleanup,
40
41
  registerMCPConnectionScopePending
@@ -208,10 +209,10 @@ async function getOrCreateConnection(cacheKey, baseUrl, authHeaders, debugLogs,
208
209
  throw new Error(`MCP ${label} completed after connection teardown`);
209
210
  }
210
211
  connectionCache.set(cacheKey, client);
211
- registerMCPConnectionScopeCleanup("legacy", cacheKey, async () => {
212
+ registerMCPConnectionScopeCleanup("legacy", cacheKey, async (terminateSession) => {
212
213
  if (connectionCache.get(cacheKey) !== client) return;
213
214
  connectionCache.delete(cacheKey);
214
- await closeMCPClient(client);
215
+ await closeMCPClient(client, terminateSession);
215
216
  });
216
217
  evictLeastRecentlyUsed();
217
218
  return client;
@@ -281,10 +282,10 @@ async function getOrCreateOAuthConnection(cacheKey, options) {
281
282
  throw new Error("OAuth MCP connection completed after connection teardown");
282
283
  }
283
284
  oauthConnectionCache.set(cacheKey, client);
284
- registerMCPConnectionScopeCleanup("oauth", cacheKey, async () => {
285
+ registerMCPConnectionScopeCleanup("oauth", cacheKey, async (terminateSession) => {
285
286
  if (oauthConnectionCache.get(cacheKey) !== client) return;
286
287
  oauthConnectionCache.delete(cacheKey);
287
- await closeMCPClient(client);
288
+ await closeMCPClient(client, terminateSession);
288
289
  });
289
290
  evictLeastRecentlyUsedOAuth();
290
291
  return client;
@@ -338,7 +339,10 @@ async function withCachedOAuthConnectionImpl(options, label, fn) {
338
339
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
339
340
  error
340
341
  });
341
- if (isAbortOrTimeoutError(error)) throw error;
342
+ if (isAbortOrTimeoutError(error)) {
343
+ markMCPConnectionScopeInterrupted();
344
+ throw error;
345
+ }
342
346
  if (is401Error(error)) {
343
347
  options.debugLogs.push({
344
348
  type: "warning",
@@ -406,7 +410,10 @@ async function withCachedConnectionImpl(agentUrl, authToken, authHeaders, debugL
406
410
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
407
411
  error
408
412
  });
409
- if (isAbortOrTimeoutError(error)) throw error;
413
+ if (isAbortOrTimeoutError(error)) {
414
+ markMCPConnectionScopeInterrupted();
415
+ throw error;
416
+ }
410
417
  if (is401Error(error)) {
411
418
  debugLogs.push({
412
419
  type: "warning",
@@ -502,6 +502,7 @@ function compileRowSchema(schema, maxCompileMs, deadline) {
502
502
  assertInspectionDeadline(deadline);
503
503
  const startedAt = Date.now();
504
504
  const ajv = new Ajv2020({
505
+ inlineRefs: false,
505
506
  strictSchema: true,
506
507
  strictTypes: false,
507
508
  allErrors: true,
@@ -472,6 +472,7 @@ function compileRowSchema(schema, maxCompileMs, deadline) {
472
472
  assertInspectionDeadline(deadline);
473
473
  const startedAt = Date.now();
474
474
  const ajv = new Ajv2020({
475
+ inlineRefs: false,
475
476
  strictSchema: true,
476
477
  strictTypes: false,
477
478
  allErrors: true,
@@ -19,3 +19,8 @@ export declare function evaluateReportingLedgerCoverageV1(query: ReportingLedger
19
19
  complete: boolean;
20
20
  retainedFrom: string;
21
21
  };
22
+ /**
23
+ * Store-side guard, evaluated under the same account lock as generation writes.
24
+ * @experimental Storage-facing building block; see docs/guides/REPORTING-STABILITY.md.
25
+ */
26
+ export declare function assertReportingLedgerHistoryDoesNotOverlap(configuration: ReportingLedgerConfigurationV1, configurations: readonly ReportingLedgerConfigurationV1[]): void;
@@ -19,3 +19,8 @@ export declare function evaluateReportingLedgerCoverageV1(query: ReportingLedger
19
19
  complete: boolean;
20
20
  retainedFrom: string;
21
21
  };
22
+ /**
23
+ * Store-side guard, evaluated under the same account lock as generation writes.
24
+ * @experimental Storage-facing building block; see docs/guides/REPORTING-STABILITY.md.
25
+ */
26
+ export declare function assertReportingLedgerHistoryDoesNotOverlap(configuration: ReportingLedgerConfigurationV1, configurations: readonly ReportingLedgerConfigurationV1[]): void;
@@ -18,6 +18,7 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var coverage_exports = {};
20
20
  __export(coverage_exports, {
21
+ assertReportingLedgerHistoryDoesNotOverlap: () => assertReportingLedgerHistoryDoesNotOverlap,
21
22
  evaluateReportingLedgerCoverageV1: () => evaluateReportingLedgerCoverageV1,
22
23
  matchingReportingLedgerConfigurations: () => matchingReportingLedgerConfigurations,
23
24
  relevantReportingLedgerConfigurations: () => relevantReportingLedgerConfigurations,
@@ -56,7 +57,7 @@ function relevantReportingLedgerConfigurations(configurations, periodStart, peri
56
57
  const start = Date.parse(periodStart);
57
58
  const end = Date.parse(periodEnd);
58
59
  return configurations.filter((configuration) => {
59
- const installed = Date.parse(configuration.installedAt);
60
+ const installed = Date.parse(configuration.historyStart ?? configuration.installedAt);
60
61
  const successor = reportingLedgerSuccessor(configuration, configurations);
61
62
  const superseded = Math.min(
62
63
  successor ? Date.parse(successor.installedAt) : Number.POSITIVE_INFINITY,
@@ -83,7 +84,7 @@ function evaluateReportingLedgerCoverageV1(query, configurations, obligations, l
83
84
  let complete = true;
84
85
  let retainedFrom;
85
86
  for (const configuration of configurations.filter((value) => reportingLedgerConfigurationMatchesScope(query, value))) {
86
- const installedAt = Date.parse(configuration.installedAt);
87
+ const installedAt = Date.parse(configuration.historyStart ?? configuration.installedAt);
87
88
  const successor = reportingLedgerSuccessor(configuration, configurations);
88
89
  const ownershipEnd = Math.min(
89
90
  successor ? Date.parse(successor.installedAt) : Number.POSITIVE_INFINITY,
@@ -108,7 +109,7 @@ function evaluateReportingLedgerCoverageV1(query, configurations, obligations, l
108
109
  }
109
110
  continue;
110
111
  }
111
- const firstOwned = Math.max(0, schedule.ceil(installedAt));
112
+ const firstOwned = configuration.historyStartOrdinal ?? Math.max(0, schedule.ceil(installedAt));
112
113
  const ownershipLast = Number.isFinite(ownershipEnd) ? schedule.ceil(ownershipEnd) - 1 : Number.POSITIVE_INFINITY;
113
114
  if (ownershipLast < firstOwned) continue;
114
115
  const end = Number.isFinite(ownershipLast) ? schedule.boundary(ownershipLast + 1) : Number.POSITIVE_INFINITY;
@@ -135,8 +136,41 @@ function evaluateReportingLedgerCoverageV1(query, configurations, obligations, l
135
136
  }
136
137
  return { complete, retainedFrom: retainedFrom ?? period.start };
137
138
  }
139
+ function assertReportingLedgerHistoryDoesNotOverlap(configuration, configurations) {
140
+ if (configuration.historyStart === void 0) return;
141
+ const all = [
142
+ ...configurations.filter((value) => value.configurationId !== configuration.configurationId),
143
+ configuration
144
+ ];
145
+ const range = (value) => {
146
+ const schedule2 = (0, import_schedule.reportingPeriodSchedule)(value);
147
+ const first = value.historyStartOrdinal ?? Math.max(0, schedule2.ceil(Date.parse(value.installedAt)));
148
+ const successor = reportingLedgerSuccessor(value, all);
149
+ const end2 = Math.min(
150
+ successor ? Date.parse(successor.installedAt) : Infinity,
151
+ value.supersededAt ? Date.parse(value.supersededAt) : Infinity
152
+ );
153
+ return [schedule2.boundary(first), Number.isFinite(end2) ? schedule2.boundary(schedule2.ceil(end2)) : Infinity];
154
+ };
155
+ const [start, ownershipEnd] = range(configuration);
156
+ const previous = configurations.find((value) => value.configurationId === configuration.configurationId);
157
+ const schedule = (0, import_schedule.reportingPeriodSchedule)(configuration);
158
+ const previousStart = previous?.historyStart !== void 0 ? Date.parse(previous.historyStart) : schedule.boundary(Math.max(0, schedule.ceil(Date.parse(configuration.installedAt))));
159
+ const end = Math.min(ownershipEnd, previousStart);
160
+ if (start >= end) return;
161
+ for (const other of all) {
162
+ if (other.configurationId === configuration.configurationId || other.account.account_id !== configuration.account.account_id || other.delivery_config_id !== configuration.delivery_config_id)
163
+ continue;
164
+ if (Date.parse(other.historyStart ?? other.installedAt) >= end) continue;
165
+ const [otherStart, otherEnd] = range(other);
166
+ if (start < end && otherStart < otherEnd && start < otherEnd && otherStart < end) {
167
+ throw new RangeError("Reporting history overlaps another configuration generation");
168
+ }
169
+ }
170
+ }
138
171
  // Annotate the CommonJS export names for ESM import in node:
139
172
  0 && (module.exports = {
173
+ assertReportingLedgerHistoryDoesNotOverlap,
140
174
  evaluateReportingLedgerCoverageV1,
141
175
  matchingReportingLedgerConfigurations,
142
176
  relevantReportingLedgerConfigurations,
@@ -31,7 +31,7 @@ function relevantReportingLedgerConfigurations(configurations, periodStart, peri
31
31
  const start = Date.parse(periodStart);
32
32
  const end = Date.parse(periodEnd);
33
33
  return configurations.filter((configuration) => {
34
- const installed = Date.parse(configuration.installedAt);
34
+ const installed = Date.parse(configuration.historyStart ?? configuration.installedAt);
35
35
  const successor = reportingLedgerSuccessor(configuration, configurations);
36
36
  const superseded = Math.min(
37
37
  successor ? Date.parse(successor.installedAt) : Number.POSITIVE_INFINITY,
@@ -58,7 +58,7 @@ function evaluateReportingLedgerCoverageV1(query, configurations, obligations, l
58
58
  let complete = true;
59
59
  let retainedFrom;
60
60
  for (const configuration of configurations.filter((value) => reportingLedgerConfigurationMatchesScope(query, value))) {
61
- const installedAt = Date.parse(configuration.installedAt);
61
+ const installedAt = Date.parse(configuration.historyStart ?? configuration.installedAt);
62
62
  const successor = reportingLedgerSuccessor(configuration, configurations);
63
63
  const ownershipEnd = Math.min(
64
64
  successor ? Date.parse(successor.installedAt) : Number.POSITIVE_INFINITY,
@@ -83,7 +83,7 @@ function evaluateReportingLedgerCoverageV1(query, configurations, obligations, l
83
83
  }
84
84
  continue;
85
85
  }
86
- const firstOwned = Math.max(0, schedule.ceil(installedAt));
86
+ const firstOwned = configuration.historyStartOrdinal ?? Math.max(0, schedule.ceil(installedAt));
87
87
  const ownershipLast = Number.isFinite(ownershipEnd) ? schedule.ceil(ownershipEnd) - 1 : Number.POSITIVE_INFINITY;
88
88
  if (ownershipLast < firstOwned) continue;
89
89
  const end = Number.isFinite(ownershipLast) ? schedule.boundary(ownershipLast + 1) : Number.POSITIVE_INFINITY;
@@ -110,7 +110,40 @@ function evaluateReportingLedgerCoverageV1(query, configurations, obligations, l
110
110
  }
111
111
  return { complete, retainedFrom: retainedFrom ?? period.start };
112
112
  }
113
+ function assertReportingLedgerHistoryDoesNotOverlap(configuration, configurations) {
114
+ if (configuration.historyStart === void 0) return;
115
+ const all = [
116
+ ...configurations.filter((value) => value.configurationId !== configuration.configurationId),
117
+ configuration
118
+ ];
119
+ const range = (value) => {
120
+ const schedule2 = reportingPeriodSchedule(value);
121
+ const first = value.historyStartOrdinal ?? Math.max(0, schedule2.ceil(Date.parse(value.installedAt)));
122
+ const successor = reportingLedgerSuccessor(value, all);
123
+ const end2 = Math.min(
124
+ successor ? Date.parse(successor.installedAt) : Infinity,
125
+ value.supersededAt ? Date.parse(value.supersededAt) : Infinity
126
+ );
127
+ return [schedule2.boundary(first), Number.isFinite(end2) ? schedule2.boundary(schedule2.ceil(end2)) : Infinity];
128
+ };
129
+ const [start, ownershipEnd] = range(configuration);
130
+ const previous = configurations.find((value) => value.configurationId === configuration.configurationId);
131
+ const schedule = reportingPeriodSchedule(configuration);
132
+ const previousStart = previous?.historyStart !== void 0 ? Date.parse(previous.historyStart) : schedule.boundary(Math.max(0, schedule.ceil(Date.parse(configuration.installedAt))));
133
+ const end = Math.min(ownershipEnd, previousStart);
134
+ if (start >= end) return;
135
+ for (const other of all) {
136
+ if (other.configurationId === configuration.configurationId || other.account.account_id !== configuration.account.account_id || other.delivery_config_id !== configuration.delivery_config_id)
137
+ continue;
138
+ if (Date.parse(other.historyStart ?? other.installedAt) >= end) continue;
139
+ const [otherStart, otherEnd] = range(other);
140
+ if (start < end && otherStart < otherEnd && start < otherEnd && otherStart < end) {
141
+ throw new RangeError("Reporting history overlaps another configuration generation");
142
+ }
143
+ }
144
+ }
113
145
  export {
146
+ assertReportingLedgerHistoryDoesNotOverlap,
114
147
  evaluateReportingLedgerCoverageV1,
115
148
  matchingReportingLedgerConfigurations,
116
149
  relevantReportingLedgerConfigurations,
@@ -922,11 +922,14 @@ function nextExpectedAt(health, selected, query, configurations, ledgerAsOf) {
922
922
  }
923
923
  function nextActivePeriodStart(configuration, configurations, ledgerAsOf) {
924
924
  const asOf = Date.parse(ledgerAsOf);
925
- const installed = Date.parse(configuration.installedAt);
925
+ const installed = Date.parse(configuration.historyStart ?? configuration.installedAt);
926
926
  const ownershipEnd = reportingLedgerConfigurationOwnershipEnd(configuration, configurations);
927
927
  if (installed > asOf || ownershipEnd <= asOf) return [];
928
928
  const schedule = (0, import_schedule.reportingPeriodSchedule)(configuration);
929
- const ordinal = Math.max(0, schedule.ceil(installed), schedule.floor(asOf) + 1);
929
+ const ordinal = Math.max(
930
+ configuration.historyStartOrdinal ?? Math.max(0, schedule.ceil(installed)),
931
+ schedule.floor(asOf) + 1
932
+ );
930
933
  const start = schedule.boundary(ordinal);
931
934
  if (start >= ownershipEnd) return [];
932
935
  schedule.period(ordinal);
@@ -934,13 +937,13 @@ function nextActivePeriodStart(configuration, configurations, ledgerAsOf) {
934
937
  }
935
938
  function nextObligationDue(configuration, configurations, query, ledgerAsOf) {
936
939
  const schedule = (0, import_schedule.reportingPeriodSchedule)(configuration);
937
- const installed = Date.parse(configuration.installedAt);
940
+ const installed = Date.parse(configuration.historyStart ?? configuration.installedAt);
938
941
  const ownershipEnd = reportingLedgerConfigurationOwnershipEnd(configuration, configurations);
939
942
  const period = (0, import_coverage.reportingLedgerEffectivePeriod)(query, ledgerAsOf);
940
943
  const periodStart = Date.parse(period.start);
941
944
  const periodEnd = Date.parse(period.end);
942
945
  const expectedOffset = configuration.schedule.deliverySlaMilliseconds;
943
- const firstOwned = Math.max(0, schedule.ceil(installed));
946
+ const firstOwned = configuration.historyStartOrdinal ?? Math.max(0, schedule.ceil(installed));
944
947
  const lastOwned = schedule.ceil(ownershipEnd) - 1;
945
948
  if (lastOwned < firstOwned) return [];
946
949
  const end = Number.isFinite(lastOwned) ? schedule.boundary(lastOwned + 1) : Number.POSITIVE_INFINITY;
@@ -906,11 +906,14 @@ function nextExpectedAt(health, selected, query, configurations, ledgerAsOf) {
906
906
  }
907
907
  function nextActivePeriodStart(configuration, configurations, ledgerAsOf) {
908
908
  const asOf = Date.parse(ledgerAsOf);
909
- const installed = Date.parse(configuration.installedAt);
909
+ const installed = Date.parse(configuration.historyStart ?? configuration.installedAt);
910
910
  const ownershipEnd = reportingLedgerConfigurationOwnershipEnd(configuration, configurations);
911
911
  if (installed > asOf || ownershipEnd <= asOf) return [];
912
912
  const schedule = reportingPeriodSchedule(configuration);
913
- const ordinal = Math.max(0, schedule.ceil(installed), schedule.floor(asOf) + 1);
913
+ const ordinal = Math.max(
914
+ configuration.historyStartOrdinal ?? Math.max(0, schedule.ceil(installed)),
915
+ schedule.floor(asOf) + 1
916
+ );
914
917
  const start = schedule.boundary(ordinal);
915
918
  if (start >= ownershipEnd) return [];
916
919
  schedule.period(ordinal);
@@ -918,13 +921,13 @@ function nextActivePeriodStart(configuration, configurations, ledgerAsOf) {
918
921
  }
919
922
  function nextObligationDue(configuration, configurations, query, ledgerAsOf) {
920
923
  const schedule = reportingPeriodSchedule(configuration);
921
- const installed = Date.parse(configuration.installedAt);
924
+ const installed = Date.parse(configuration.historyStart ?? configuration.installedAt);
922
925
  const ownershipEnd = reportingLedgerConfigurationOwnershipEnd(configuration, configurations);
923
926
  const period = reportingLedgerEffectivePeriod(query, ledgerAsOf);
924
927
  const periodStart = Date.parse(period.start);
925
928
  const periodEnd = Date.parse(period.end);
926
929
  const expectedOffset = configuration.schedule.deliverySlaMilliseconds;
927
- const firstOwned = Math.max(0, schedule.ceil(installed));
930
+ const firstOwned = configuration.historyStartOrdinal ?? Math.max(0, schedule.ceil(installed));
928
931
  const lastOwned = schedule.ceil(ownershipEnd) - 1;
929
932
  if (lastOwned < firstOwned) return [];
930
933
  const end = Number.isFinite(lastOwned) ? schedule.boundary(lastOwned + 1) : Number.POSITIVE_INFINITY;