@adcp/sdk 14.1.0 → 14.2.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 (106) hide show
  1. package/dist/lib/core/SingleAgentClient.d.mts +4 -1
  2. package/dist/lib/core/SingleAgentClient.d.ts +4 -1
  3. package/dist/lib/core/SingleAgentClient.js +25 -4
  4. package/dist/lib/core/SingleAgentClient.mjs +25 -4
  5. package/dist/lib/core/TaskExecutor.d.mts +3 -1
  6. package/dist/lib/core/TaskExecutor.d.ts +3 -1
  7. package/dist/lib/core/TaskExecutor.js +15 -11
  8. package/dist/lib/core/TaskExecutor.mjs +15 -11
  9. package/dist/lib/core/buyer-account-registry.d.mts +30 -2
  10. package/dist/lib/core/buyer-account-registry.d.ts +30 -2
  11. package/dist/lib/core/buyer-account-registry.js +126 -42
  12. package/dist/lib/core/buyer-account-registry.mjs +127 -43
  13. package/dist/lib/index.d.mts +3 -2
  14. package/dist/lib/index.d.ts +3 -2
  15. package/dist/lib/index.js +9 -0
  16. package/dist/lib/index.mjs +12 -1
  17. package/dist/lib/net/agent-transport-fetch.d.mts +4 -0
  18. package/dist/lib/net/agent-transport-fetch.d.ts +4 -0
  19. package/dist/lib/net/agent-transport-fetch.js +18 -5
  20. package/dist/lib/net/agent-transport-fetch.mjs +17 -5
  21. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  22. package/dist/lib/signing/agent-resolver/errors.d.mts +2 -0
  23. package/dist/lib/signing/agent-resolver/errors.d.ts +2 -0
  24. package/dist/lib/signing/agent-resolver/errors.js +6 -0
  25. package/dist/lib/signing/agent-resolver/errors.mjs +6 -0
  26. package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +9 -0
  27. package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +9 -0
  28. package/dist/lib/signing/agent-resolver/fetch-helpers.js +35 -1
  29. package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +38 -2
  30. package/dist/lib/signing/agent-resolver/legacy-brand.js +1 -1
  31. package/dist/lib/signing/agent-resolver/legacy-brand.mjs +2 -2
  32. package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +1 -1
  33. package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +1 -1
  34. package/dist/lib/signing/agent-resolver/resolve-agent.js +47 -5
  35. package/dist/lib/signing/agent-resolver/resolve-agent.mjs +55 -6
  36. package/dist/lib/signing/brand-jwks.d.mts +2 -0
  37. package/dist/lib/signing/brand-jwks.d.ts +2 -0
  38. package/dist/lib/signing/brand-jwks.js +10 -2
  39. package/dist/lib/signing/brand-jwks.mjs +10 -2
  40. package/dist/lib/signing/errors.d.mts +4 -2
  41. package/dist/lib/signing/errors.d.ts +4 -2
  42. package/dist/lib/signing/errors.js +3 -1
  43. package/dist/lib/signing/errors.mjs +3 -1
  44. package/dist/lib/signing/verifier.js +1 -1
  45. package/dist/lib/signing/verifier.mjs +1 -1
  46. package/dist/lib/signing/webhook-verifier.js +2 -2
  47. package/dist/lib/signing/webhook-verifier.mjs +3 -3
  48. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  49. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  50. package/dist/lib/types/accept-proposal.d.ts +19 -1
  51. package/dist/lib/types/buy-products.d.ts +19 -1
  52. package/dist/lib/types/check-governance.d.ts +19 -1
  53. package/dist/lib/types/comply-test-controller.d.ts +19 -1
  54. package/dist/lib/types/control-media-buy.d.ts +19 -1
  55. package/dist/lib/types/core.generated.d.mts +14 -1
  56. package/dist/lib/types/core.generated.d.ts +14 -1
  57. package/dist/lib/types/create-media-buy.d.ts +14 -1
  58. package/dist/lib/types/get-media-buys.d.ts +14 -1
  59. package/dist/lib/types/get-products.d.ts +19 -1
  60. package/dist/lib/types/list-products.d.ts +19 -1
  61. package/dist/lib/types/refine-proposals.d.ts +19 -1
  62. package/dist/lib/types/request-proposals.d.ts +19 -1
  63. package/dist/lib/types/schemas.generated.d.ts +12 -3
  64. package/dist/lib/types/schemas.generated.js +4 -1
  65. package/dist/lib/types/schemas.generated.mjs +4 -1
  66. package/dist/lib/types/tools.generated.d.mts +14 -1
  67. package/dist/lib/types/tools.generated.d.ts +14 -1
  68. package/dist/lib/types/update-media-buy.d.ts +14 -1
  69. package/dist/lib/version.d.mts +3 -3
  70. package/dist/lib/version.d.ts +3 -3
  71. package/dist/lib/version.js +3 -3
  72. package/dist/lib/version.mjs +3 -3
  73. package/dist/lib/webhooks/index.d.mts +24 -0
  74. package/dist/lib/webhooks/index.d.ts +24 -0
  75. package/dist/lib/webhooks/index.js +50 -24
  76. package/dist/lib/webhooks/index.mjs +49 -24
  77. package/dist/lib/wholesale-feed-sync/index.d.mts +2 -0
  78. package/dist/lib/wholesale-feed-sync/index.d.ts +2 -0
  79. package/dist/lib/wholesale-feed-sync/index.js +7 -0
  80. package/dist/lib/wholesale-feed-sync/index.mjs +4 -0
  81. package/dist/lib/wholesale-feed-sync/mirror.d.mts +97 -0
  82. package/dist/lib/wholesale-feed-sync/mirror.d.ts +97 -0
  83. package/dist/lib/wholesale-feed-sync/mirror.js +350 -0
  84. package/dist/lib/wholesale-feed-sync/mirror.mjs +322 -0
  85. package/dist/lib/wholesale-feed-sync/sync.d.mts +12 -29
  86. package/dist/lib/wholesale-feed-sync/sync.d.ts +12 -29
  87. package/dist/lib/wholesale-feed-sync/sync.js +153 -297
  88. package/dist/lib/wholesale-feed-sync/sync.mjs +158 -297
  89. package/docs/README.md +6 -0
  90. package/docs/TYPE-SUMMARY.md +2 -2
  91. package/docs/guides/BUYER-STORAGE.md +3 -0
  92. package/docs/guides/FIRST-CALL-TO-A-SELLER.md +3 -1
  93. package/docs/guides/account-resolution.md +46 -10
  94. package/docs/llms.txt +2 -2
  95. package/docs/migration-14.0-to-14.1.md +85 -0
  96. package/docs/migration-14.x-rc-worksheet.md +4 -4
  97. package/docs/migration-agent-resolution-3.3.md +5 -3
  98. package/docs/recipes/verifying-inbound-webhooks.md +4 -0
  99. package/package.json +2 -1
  100. package/skills/adcp-brand.previous/SKILL.md +0 -200
  101. package/skills/adcp-creative.previous/SKILL.md +0 -305
  102. package/skills/adcp-governance.previous/SKILL.md +0 -566
  103. package/skills/adcp-measurement.previous/SKILL.md +0 -136
  104. package/skills/adcp-media-buy.previous/SKILL.md +0 -556
  105. package/skills/adcp-si.previous/SKILL.md +0 -206
  106. package/skills/adcp-signals.previous/SKILL.md +0 -204
@@ -199,6 +199,7 @@ export interface SingleAgentClientConfig extends ConversationConfig {
199
199
  accountPolicy?: AccountPolicy;
200
200
  accountStorage?: BuyerAccountStorage;
201
201
  accountRegistryMaxEntries?: number;
202
+ accountRegistryOptions?: Pick<import('./buyer-account-registry.mjs').BuyerAccountRegistryOptions, 'onDispatchStart' | 'onResult'>;
202
203
  /** Trusted stable caller identity for durable registry partitioning. Default is credential fingerprint. */
203
204
  accountRegistryScope?: string;
204
205
  productCache?: ProductCache;
@@ -1061,7 +1062,9 @@ export declare class SingleAgentClient {
1061
1062
  /**
1062
1063
  * Sync accounts
1063
1064
  */
1064
- syncAccounts(params: MutatingRequestInput<SyncAccountsRequest>, inputHandler?: InputHandler, options?: TaskOptions): Promise<TaskResult<SyncAccountsResponse>>;
1065
+ syncAccounts(params: MutatingRequestInput<SyncAccountsRequest>, inputHandler?: InputHandler, options?: TaskOptions,
1066
+ /** @internal Registry-owned final dispatch boundary. */
1067
+ dispatch?: import('./buyer-account-registry.mjs').BuyerAccountProvisioningDispatch): Promise<TaskResult<SyncAccountsResponse>>;
1065
1068
  /**
1066
1069
  * Sync audiences
1067
1070
  */
@@ -199,6 +199,7 @@ export interface SingleAgentClientConfig extends ConversationConfig {
199
199
  accountPolicy?: AccountPolicy;
200
200
  accountStorage?: BuyerAccountStorage;
201
201
  accountRegistryMaxEntries?: number;
202
+ accountRegistryOptions?: Pick<import('./buyer-account-registry').BuyerAccountRegistryOptions, 'onDispatchStart' | 'onResult'>;
202
203
  /** Trusted stable caller identity for durable registry partitioning. Default is credential fingerprint. */
203
204
  accountRegistryScope?: string;
204
205
  productCache?: ProductCache;
@@ -1061,7 +1062,9 @@ export declare class SingleAgentClient {
1061
1062
  /**
1062
1063
  * Sync accounts
1063
1064
  */
1064
- syncAccounts(params: MutatingRequestInput<SyncAccountsRequest>, inputHandler?: InputHandler, options?: TaskOptions): Promise<TaskResult<SyncAccountsResponse>>;
1065
+ syncAccounts(params: MutatingRequestInput<SyncAccountsRequest>, inputHandler?: InputHandler, options?: TaskOptions,
1066
+ /** @internal Registry-owned final dispatch boundary. */
1067
+ dispatch?: import('./buyer-account-registry').BuyerAccountProvisioningDispatch): Promise<TaskResult<SyncAccountsResponse>>;
1065
1068
  /**
1066
1069
  * Sync audiences
1067
1070
  */
@@ -883,7 +883,7 @@ class SingleAgentClient {
883
883
  getAccountRegistry() {
884
884
  return this._accounts ??= new import_buyer_account_registry.BuyerAccountRegistry(
885
885
  () => this.accountScope(),
886
- async (account, options, taskOptions) => {
886
+ async (account, options, taskOptions, dispatch) => {
887
887
  const caps = await this.getCapabilities(taskOptions);
888
888
  const supported = caps.account?.supportedBilling ?? [];
889
889
  const billing = options.billing ?? caps.account?.defaultBilling ?? (supported.length === 1 ? supported[0] : void 0);
@@ -896,6 +896,7 @@ class SingleAgentClient {
896
896
  }
897
897
  return this.syncAccounts(
898
898
  {
899
+ ...dispatch && { idempotency_key: dispatch.idempotencyKey },
899
900
  accounts: [
900
901
  {
901
902
  ...account,
@@ -906,7 +907,8 @@ class SingleAgentClient {
906
907
  ]
907
908
  },
908
909
  void 0,
909
- taskOptions
910
+ { ...taskOptions, [SKIP_ACCOUNT_REGISTRY_OBSERVATION]: true },
911
+ dispatch
910
912
  );
911
913
  },
912
914
  this.config.accountStorage,
@@ -933,6 +935,8 @@ class SingleAgentClient {
933
935
  throw new Error("Account status reconciliation could not complete pagination.");
934
936
  },
935
937
  {
938
+ ...this.config.accountRegistryOptions,
939
+ provisionOwnsDispatchBoundary: true,
936
940
  maxEntries: this.config.accountRegistryMaxEntries,
937
941
  normalizeOptions: async (options, taskOptions) => {
938
942
  if (options.billing) return options;
@@ -950,7 +954,7 @@ class SingleAgentClient {
950
954
  * `resolveAccount()` keeps the SDK 14.0 sync-on-every-call behavior.
951
955
  */
952
956
  accountRegistryOptedIn() {
953
- return (this.config.accountPolicy ?? "off") !== "off" || this.config.accountStorage !== void 0 || this.config.accountRegistryScope !== void 0 || this.config.accountRegistryMaxEntries !== void 0;
957
+ return (this.config.accountPolicy ?? "off") !== "off" || this.config.accountStorage !== void 0 || this.config.accountRegistryScope !== void 0 || this.config.accountRegistryMaxEntries !== void 0 || this.config.accountRegistryOptions !== void 0;
954
958
  }
955
959
  accountRegistryContext(task, params, options) {
956
960
  const knownOnly = !this.accountRegistryExplicitlyAccessed && (this.config.accountPolicy ?? "off") === "off" && !this.config.accountStorage;
@@ -5307,7 +5311,24 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
5307
5311
  /**
5308
5312
  * Sync accounts
5309
5313
  */
5310
- async syncAccounts(params, inputHandler, options) {
5314
+ async syncAccounts(params, inputHandler, options, dispatch) {
5315
+ if (dispatch)
5316
+ return this.executeTaskUnprojected(
5317
+ "sync_accounts",
5318
+ params,
5319
+ inputHandler,
5320
+ options,
5321
+ "onSyncAccountsStatusChange",
5322
+ Object.assign(
5323
+ async (effectiveParams) => {
5324
+ if (dispatch.requireIdempotencyKey !== false && effectiveParams.idempotency_key !== dispatch.idempotencyKey)
5325
+ throw new import_errors.ConfigurationError("Account provisioning idempotency key changed before dispatch.");
5326
+ await dispatch.beforeDispatch();
5327
+ return { action: "dispatch_ready" };
5328
+ },
5329
+ { terminalSettlement: "executor" }
5330
+ )
5331
+ );
5311
5332
  return this.executeAndHandle(
5312
5333
  "sync_accounts",
5313
5334
  "onSyncAccountsStatusChange",
@@ -953,7 +953,7 @@ class SingleAgentClient {
953
953
  getAccountRegistry() {
954
954
  return this._accounts ??= new BuyerAccountRegistry(
955
955
  () => this.accountScope(),
956
- async (account, options, taskOptions) => {
956
+ async (account, options, taskOptions, dispatch) => {
957
957
  const caps = await this.getCapabilities(taskOptions);
958
958
  const supported = caps.account?.supportedBilling ?? [];
959
959
  const billing = options.billing ?? caps.account?.defaultBilling ?? (supported.length === 1 ? supported[0] : void 0);
@@ -966,6 +966,7 @@ class SingleAgentClient {
966
966
  }
967
967
  return this.syncAccounts(
968
968
  {
969
+ ...dispatch && { idempotency_key: dispatch.idempotencyKey },
969
970
  accounts: [
970
971
  {
971
972
  ...account,
@@ -976,7 +977,8 @@ class SingleAgentClient {
976
977
  ]
977
978
  },
978
979
  void 0,
979
- taskOptions
980
+ { ...taskOptions, [SKIP_ACCOUNT_REGISTRY_OBSERVATION]: true },
981
+ dispatch
980
982
  );
981
983
  },
982
984
  this.config.accountStorage,
@@ -1003,6 +1005,8 @@ class SingleAgentClient {
1003
1005
  throw new Error("Account status reconciliation could not complete pagination.");
1004
1006
  },
1005
1007
  {
1008
+ ...this.config.accountRegistryOptions,
1009
+ provisionOwnsDispatchBoundary: true,
1006
1010
  maxEntries: this.config.accountRegistryMaxEntries,
1007
1011
  normalizeOptions: async (options, taskOptions) => {
1008
1012
  if (options.billing) return options;
@@ -1020,7 +1024,7 @@ class SingleAgentClient {
1020
1024
  * `resolveAccount()` keeps the SDK 14.0 sync-on-every-call behavior.
1021
1025
  */
1022
1026
  accountRegistryOptedIn() {
1023
- return (this.config.accountPolicy ?? "off") !== "off" || this.config.accountStorage !== void 0 || this.config.accountRegistryScope !== void 0 || this.config.accountRegistryMaxEntries !== void 0;
1027
+ return (this.config.accountPolicy ?? "off") !== "off" || this.config.accountStorage !== void 0 || this.config.accountRegistryScope !== void 0 || this.config.accountRegistryMaxEntries !== void 0 || this.config.accountRegistryOptions !== void 0;
1024
1028
  }
1025
1029
  accountRegistryContext(task, params, options) {
1026
1030
  const knownOnly = !this.accountRegistryExplicitlyAccessed && (this.config.accountPolicy ?? "off") === "off" && !this.config.accountStorage;
@@ -5377,7 +5381,24 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
5377
5381
  /**
5378
5382
  * Sync accounts
5379
5383
  */
5380
- async syncAccounts(params, inputHandler, options) {
5384
+ async syncAccounts(params, inputHandler, options, dispatch) {
5385
+ if (dispatch)
5386
+ return this.executeTaskUnprojected(
5387
+ "sync_accounts",
5388
+ params,
5389
+ inputHandler,
5390
+ options,
5391
+ "onSyncAccountsStatusChange",
5392
+ Object.assign(
5393
+ async (effectiveParams) => {
5394
+ if (dispatch.requireIdempotencyKey !== false && effectiveParams.idempotency_key !== dispatch.idempotencyKey)
5395
+ throw new ConfigurationError("Account provisioning idempotency key changed before dispatch.");
5396
+ await dispatch.beforeDispatch();
5397
+ return { action: "dispatch_ready" };
5398
+ },
5399
+ { terminalSettlement: "executor" }
5400
+ )
5401
+ );
5381
5402
  return this.executeAndHandle(
5382
5403
  "sync_accounts",
5383
5404
  "onSyncAccountsStatusChange",
@@ -41,6 +41,8 @@ export declare function acknowledgeDeferredSettlement(result: TaskResult<any>):
41
41
  export declare function rejectDeferredSettlement(result: TaskResult<any>): Promise<void>;
42
42
  /** Supporting contract for the internal pre-dispatch boundary. */
43
43
  export type BeforeProtocolDispatchHookResult<T> = {
44
+ action: 'dispatch_ready';
45
+ } | {
44
46
  action: 'dispatch_committed';
45
47
  /** Require the owning durable coordinator to authorize every seller-input continuation. */
46
48
  requireDeferredSettlementResumeAuthorization?: boolean;
@@ -84,7 +86,7 @@ export interface ExternalTaskStatusResult {
84
86
  error?: string;
85
87
  }
86
88
  /** Hook used by higher-level SDK coordinators at the final dispatch boundary. */
87
- export type BeforeProtocolDispatchHook<T> = (effectiveParams: any, context: BeforeProtocolDispatchContext) => Promise<BeforeProtocolDispatchHookResult<T>>;
89
+ export type BeforeProtocolDispatchHook<T> = ((effectiveParams: any, context: BeforeProtocolDispatchContext) => Promise<BeforeProtocolDispatchHookResult<T>>) & {};
88
90
  /**
89
91
  * Webhook manager for submitted tasks
90
92
  */
@@ -41,6 +41,8 @@ export declare function acknowledgeDeferredSettlement(result: TaskResult<any>):
41
41
  export declare function rejectDeferredSettlement(result: TaskResult<any>): Promise<void>;
42
42
  /** Supporting contract for the internal pre-dispatch boundary. */
43
43
  export type BeforeProtocolDispatchHookResult<T> = {
44
+ action: 'dispatch_ready';
45
+ } | {
44
46
  action: 'dispatch_committed';
45
47
  /** Require the owning durable coordinator to authorize every seller-input continuation. */
46
48
  requireDeferredSettlementResumeAuthorization?: boolean;
@@ -84,7 +86,7 @@ export interface ExternalTaskStatusResult {
84
86
  error?: string;
85
87
  }
86
88
  /** Hook used by higher-level SDK coordinators at the final dispatch boundary. */
87
- export type BeforeProtocolDispatchHook<T> = (effectiveParams: any, context: BeforeProtocolDispatchContext) => Promise<BeforeProtocolDispatchHookResult<T>>;
89
+ export type BeforeProtocolDispatchHook<T> = ((effectiveParams: any, context: BeforeProtocolDispatchContext) => Promise<BeforeProtocolDispatchHookResult<T>>) & {};
88
90
  /**
89
91
  * Webhook manager for submitted tasks
90
92
  */
@@ -944,16 +944,19 @@ class TaskExecutor {
944
944
  (0, import_abort.throwIfAborted)(options.signal);
945
945
  if (beforeProtocolDispatch) {
946
946
  let decision;
947
- dispatchBoundaryOwnsTerminalState = true;
947
+ const ownsSettlement = beforeProtocolDispatch.terminalSettlement !== "executor";
948
+ dispatchBoundaryOwnsTerminalState = ownsSettlement;
948
949
  try {
949
- this.compactClosedExternalTaskSettlementFences();
950
- if (this.liveExternalTaskSettlementCount() + this.settlementCapacityReservations.size >= COMPACTED_TASK_STATE_LIMIT) {
951
- throw new Error("The durable task-settlement capacity is exhausted; no mutation claim was attempted.");
952
- }
953
- this.settlementCapacityReservations.add(taskId);
954
- if (webhookRegistrationPersisted) {
955
- await this.config.onDurableSettlementRequired?.(taskId);
956
- (0, import_abort.throwIfAborted)(options.signal);
950
+ if (ownsSettlement) {
951
+ this.compactClosedExternalTaskSettlementFences();
952
+ if (this.liveExternalTaskSettlementCount() + this.settlementCapacityReservations.size >= COMPACTED_TASK_STATE_LIMIT) {
953
+ throw new Error("The durable task-settlement capacity is exhausted; no mutation claim was attempted.");
954
+ }
955
+ this.settlementCapacityReservations.add(taskId);
956
+ if (webhookRegistrationPersisted) {
957
+ await this.config.onDurableSettlementRequired?.(taskId);
958
+ (0, import_abort.throwIfAborted)(options.signal);
959
+ }
957
960
  }
958
961
  decision = await beforeProtocolDispatch(preparedCall.args, {
959
962
  operationId: taskId,
@@ -962,6 +965,7 @@ class TaskExecutor {
962
965
  registerExternalTaskSettlement: (handler) => this.registerExternalTaskSettlement(taskId, handler)
963
966
  });
964
967
  if (decision.action === "dispatch_committed") {
968
+ if (!ownsSettlement) throw new Error("An executor-settled dispatch hook cannot claim durable settlement.");
965
969
  if (decision.requireDeferredSettlementResumeAuthorization === true && this.config.deferredStorage !== void 0) {
966
970
  this.requireDeferredSettlementOperationRouting(this.config.deferredStorage);
967
971
  }
@@ -985,8 +989,8 @@ class TaskExecutor {
985
989
  }
986
990
  return (0, import_match.attachMatch)(earlyResult);
987
991
  }
988
- dispatchCommitted = true;
989
- dispatchSettlement = decision;
992
+ dispatchCommitted = decision.action === "dispatch_committed";
993
+ dispatchSettlement = decision.action === "dispatch_committed" ? decision : void 0;
990
994
  }
991
995
  (0, import_abort.throwIfAborted)(options.signal);
992
996
  const postDispatchOptions = dispatchCommitted ? { ...options, signal: void 0 } : options;
@@ -917,16 +917,19 @@ class TaskExecutor {
917
917
  throwIfAborted(options.signal);
918
918
  if (beforeProtocolDispatch) {
919
919
  let decision;
920
- dispatchBoundaryOwnsTerminalState = true;
920
+ const ownsSettlement = beforeProtocolDispatch.terminalSettlement !== "executor";
921
+ dispatchBoundaryOwnsTerminalState = ownsSettlement;
921
922
  try {
922
- this.compactClosedExternalTaskSettlementFences();
923
- if (this.liveExternalTaskSettlementCount() + this.settlementCapacityReservations.size >= COMPACTED_TASK_STATE_LIMIT) {
924
- throw new Error("The durable task-settlement capacity is exhausted; no mutation claim was attempted.");
925
- }
926
- this.settlementCapacityReservations.add(taskId);
927
- if (webhookRegistrationPersisted) {
928
- await this.config.onDurableSettlementRequired?.(taskId);
929
- throwIfAborted(options.signal);
923
+ if (ownsSettlement) {
924
+ this.compactClosedExternalTaskSettlementFences();
925
+ if (this.liveExternalTaskSettlementCount() + this.settlementCapacityReservations.size >= COMPACTED_TASK_STATE_LIMIT) {
926
+ throw new Error("The durable task-settlement capacity is exhausted; no mutation claim was attempted.");
927
+ }
928
+ this.settlementCapacityReservations.add(taskId);
929
+ if (webhookRegistrationPersisted) {
930
+ await this.config.onDurableSettlementRequired?.(taskId);
931
+ throwIfAborted(options.signal);
932
+ }
930
933
  }
931
934
  decision = await beforeProtocolDispatch(preparedCall.args, {
932
935
  operationId: taskId,
@@ -935,6 +938,7 @@ class TaskExecutor {
935
938
  registerExternalTaskSettlement: (handler) => this.registerExternalTaskSettlement(taskId, handler)
936
939
  });
937
940
  if (decision.action === "dispatch_committed") {
941
+ if (!ownsSettlement) throw new Error("An executor-settled dispatch hook cannot claim durable settlement.");
938
942
  if (decision.requireDeferredSettlementResumeAuthorization === true && this.config.deferredStorage !== void 0) {
939
943
  this.requireDeferredSettlementOperationRouting(this.config.deferredStorage);
940
944
  }
@@ -958,8 +962,8 @@ class TaskExecutor {
958
962
  }
959
963
  return attachMatch(earlyResult);
960
964
  }
961
- dispatchCommitted = true;
962
- dispatchSettlement = decision;
965
+ dispatchCommitted = decision.action === "dispatch_committed";
966
+ dispatchSettlement = decision.action === "dispatch_committed" ? decision : void 0;
963
967
  }
964
968
  throwIfAborted(options.signal);
965
969
  const postDispatchOptions = dispatchCommitted ? { ...options, signal: void 0 } : options;
@@ -5,6 +5,8 @@ export interface EnsureAccountOptions {
5
5
  billing?: 'operator' | 'agent' | 'advertiser';
6
6
  paymentTerms?: PaymentTerms;
7
7
  billingEntity?: BusinessEntity;
8
+ /** Caller-owned retry identity. Settle ambiguous dispatches explicitly with this key; the registry never rotates it. */
9
+ idempotencyKey?: string;
8
10
  }
9
11
  export interface ProvisionedAccount {
10
12
  account: AccountReference;
@@ -15,13 +17,38 @@ export interface ProvisionedAccount {
15
17
  setupTerms?: Partial<Record<keyof EnsureAccountOptions, string>>;
16
18
  /** Stored reference aliases for authoritative repair after restart. */
17
19
  aliases?: AccountReference[];
20
+ /** Monotonically increasing row revision when compareAndSet is implemented. */
21
+ revision?: number;
22
+ /** Durable claim. Reconcile an uncertain dispatch explicitly before provisioning again. */
23
+ dispatch?: {
24
+ idempotencyKey: string;
25
+ startedAt: string;
26
+ };
18
27
  }
19
28
  /** Partitioned by seller and caller scope. Implementations must provide read-your-writes. */
20
29
  export interface BuyerAccountStorage {
21
30
  get(key: string): Promise<ProvisionedAccount | undefined>;
22
31
  set(key: string, account: ProvisionedAccount): Promise<void>;
32
+ /** Atomically insert (undefined revision) or replace. Store the supplied new revision. Required for multi-process safety. */
33
+ compareAndSet?(key: string, expectedRevision: number | undefined, account: ProvisionedAccount): Promise<boolean>;
34
+ }
35
+ export interface BuyerAccountDispatch {
36
+ storageKey: string;
37
+ account: AccountReference;
38
+ idempotencyKey: string;
39
+ }
40
+ export interface BuyerAccountProvisioningDispatch {
41
+ idempotencyKey: string;
42
+ /** Invoke immediately before the transport receives the mutation. */
43
+ beforeDispatch(): Promise<void>;
23
44
  }
24
45
  export interface BuyerAccountRegistryOptions {
46
+ /** Awaited before dispatch; rejection prevents the mutation. */
47
+ onDispatchStart?: (dispatch: BuyerAccountDispatch) => Promise<void>;
48
+ /** Awaited for each seller result before account writes. Rejection preserves the claim for caller reconciliation. */
49
+ onResult?: (dispatch: BuyerAccountDispatch & {
50
+ result: TaskResult<unknown>;
51
+ }) => Promise<void>;
25
52
  maxEntries?: number;
26
53
  normalizeOptions?: (options: EnsureAccountOptions, taskOptions?: TaskOptions) => Promise<EnsureAccountOptions>;
27
54
  }
@@ -30,6 +57,7 @@ export declare class BuyerAccountRegistry {
30
57
  private provision;
31
58
  private storage?;
32
59
  private repair?;
60
+ private readonly registryOptions;
33
61
  private entries;
34
62
  private pending;
35
63
  private writes;
@@ -41,7 +69,7 @@ export declare class BuyerAccountRegistry {
41
69
  private statusEpochs;
42
70
  private readonly maxEntries;
43
71
  private readonly normalizeOptions?;
44
- constructor(scope: () => string, provision: (key: AccountReference, options: EnsureAccountOptions, taskOptions?: TaskOptions) => Promise<TaskResult<SyncAccountsResponse>>, storage?: BuyerAccountStorage | undefined, repair?: ((accountId: string, taskOptions?: TaskOptions) => Promise<readonly unknown[]>) | undefined, options?: BuyerAccountRegistryOptions);
72
+ constructor(scope: () => string, provision: (key: AccountReference, options: EnsureAccountOptions, taskOptions?: TaskOptions, dispatch?: BuyerAccountProvisioningDispatch) => Promise<TaskResult<SyncAccountsResponse>>, storage?: BuyerAccountStorage | undefined, repair?: ((accountId: string, taskOptions?: TaskOptions) => Promise<readonly unknown[]>) | undefined, registryOptions?: BuyerAccountRegistryOptions);
45
73
  private key;
46
74
  /** Capture before a seller request; observations older than a status repair are ignored. */
47
75
  captureObservation(): number;
@@ -52,7 +80,7 @@ export declare class BuyerAccountRegistry {
52
80
  private write;
53
81
  private record;
54
82
  private enqueue;
55
- observeSync(refs: readonly AccountReference[], rows: readonly unknown[], dryRun?: boolean, observationEpoch?: number): Promise<void>;
83
+ observeSync(refs: readonly AccountReference[], rows: readonly unknown[], dryRun?: boolean, observationEpoch?: number, expectedRevision?: number): Promise<void>;
56
84
  private reconcileId;
57
85
  observeList(rows: readonly unknown[], queriedAccountId?: string, observationEpoch?: number): Promise<void>;
58
86
  private observeListAtScope;
@@ -5,6 +5,8 @@ export interface EnsureAccountOptions {
5
5
  billing?: 'operator' | 'agent' | 'advertiser';
6
6
  paymentTerms?: PaymentTerms;
7
7
  billingEntity?: BusinessEntity;
8
+ /** Caller-owned retry identity. Settle ambiguous dispatches explicitly with this key; the registry never rotates it. */
9
+ idempotencyKey?: string;
8
10
  }
9
11
  export interface ProvisionedAccount {
10
12
  account: AccountReference;
@@ -15,13 +17,38 @@ export interface ProvisionedAccount {
15
17
  setupTerms?: Partial<Record<keyof EnsureAccountOptions, string>>;
16
18
  /** Stored reference aliases for authoritative repair after restart. */
17
19
  aliases?: AccountReference[];
20
+ /** Monotonically increasing row revision when compareAndSet is implemented. */
21
+ revision?: number;
22
+ /** Durable claim. Reconcile an uncertain dispatch explicitly before provisioning again. */
23
+ dispatch?: {
24
+ idempotencyKey: string;
25
+ startedAt: string;
26
+ };
18
27
  }
19
28
  /** Partitioned by seller and caller scope. Implementations must provide read-your-writes. */
20
29
  export interface BuyerAccountStorage {
21
30
  get(key: string): Promise<ProvisionedAccount | undefined>;
22
31
  set(key: string, account: ProvisionedAccount): Promise<void>;
32
+ /** Atomically insert (undefined revision) or replace. Store the supplied new revision. Required for multi-process safety. */
33
+ compareAndSet?(key: string, expectedRevision: number | undefined, account: ProvisionedAccount): Promise<boolean>;
34
+ }
35
+ export interface BuyerAccountDispatch {
36
+ storageKey: string;
37
+ account: AccountReference;
38
+ idempotencyKey: string;
39
+ }
40
+ export interface BuyerAccountProvisioningDispatch {
41
+ idempotencyKey: string;
42
+ /** Invoke immediately before the transport receives the mutation. */
43
+ beforeDispatch(): Promise<void>;
23
44
  }
24
45
  export interface BuyerAccountRegistryOptions {
46
+ /** Awaited before dispatch; rejection prevents the mutation. */
47
+ onDispatchStart?: (dispatch: BuyerAccountDispatch) => Promise<void>;
48
+ /** Awaited for each seller result before account writes. Rejection preserves the claim for caller reconciliation. */
49
+ onResult?: (dispatch: BuyerAccountDispatch & {
50
+ result: TaskResult<unknown>;
51
+ }) => Promise<void>;
25
52
  maxEntries?: number;
26
53
  normalizeOptions?: (options: EnsureAccountOptions, taskOptions?: TaskOptions) => Promise<EnsureAccountOptions>;
27
54
  }
@@ -30,6 +57,7 @@ export declare class BuyerAccountRegistry {
30
57
  private provision;
31
58
  private storage?;
32
59
  private repair?;
60
+ private readonly registryOptions;
33
61
  private entries;
34
62
  private pending;
35
63
  private writes;
@@ -41,7 +69,7 @@ export declare class BuyerAccountRegistry {
41
69
  private statusEpochs;
42
70
  private readonly maxEntries;
43
71
  private readonly normalizeOptions?;
44
- constructor(scope: () => string, provision: (key: AccountReference, options: EnsureAccountOptions, taskOptions?: TaskOptions) => Promise<TaskResult<SyncAccountsResponse>>, storage?: BuyerAccountStorage | undefined, repair?: ((accountId: string, taskOptions?: TaskOptions) => Promise<readonly unknown[]>) | undefined, options?: BuyerAccountRegistryOptions);
72
+ constructor(scope: () => string, provision: (key: AccountReference, options: EnsureAccountOptions, taskOptions?: TaskOptions, dispatch?: BuyerAccountProvisioningDispatch) => Promise<TaskResult<SyncAccountsResponse>>, storage?: BuyerAccountStorage | undefined, repair?: ((accountId: string, taskOptions?: TaskOptions) => Promise<readonly unknown[]>) | undefined, registryOptions?: BuyerAccountRegistryOptions);
45
73
  private key;
46
74
  /** Capture before a seller request; observations older than a status repair are ignored. */
47
75
  captureObservation(): number;
@@ -52,7 +80,7 @@ export declare class BuyerAccountRegistry {
52
80
  private write;
53
81
  private record;
54
82
  private enqueue;
55
- observeSync(refs: readonly AccountReference[], rows: readonly unknown[], dryRun?: boolean, observationEpoch?: number): Promise<void>;
83
+ observeSync(refs: readonly AccountReference[], rows: readonly unknown[], dryRun?: boolean, observationEpoch?: number, expectedRevision?: number): Promise<void>;
56
84
  private reconcileId;
57
85
  observeList(rows: readonly unknown[], queriedAccountId?: string, observationEpoch?: number): Promise<void>;
58
86
  private observeListAtScope;