@adcp/sdk 14.0.0-beta.17 → 14.0.0-beta.19

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 (35) hide show
  1. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  2. package/dist/lib/server/decisioning/context.d.mts +4 -0
  3. package/dist/lib/server/decisioning/context.d.ts +4 -0
  4. package/dist/lib/server/decisioning/index.d.mts +1 -0
  5. package/dist/lib/server/decisioning/index.d.ts +1 -0
  6. package/dist/lib/server/decisioning/index.js +11 -0
  7. package/dist/lib/server/decisioning/index.mjs +12 -0
  8. package/dist/lib/server/decisioning/runtime/postgres-task-registry.js +5 -3
  9. package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs +5 -3
  10. package/dist/lib/server/decisioning/runtime/postgres-task-settlement-intents.d.mts +136 -0
  11. package/dist/lib/server/decisioning/runtime/postgres-task-settlement-intents.d.ts +136 -0
  12. package/dist/lib/server/decisioning/runtime/postgres-task-settlement-intents.js +745 -0
  13. package/dist/lib/server/decisioning/runtime/postgres-task-settlement-intents.mjs +717 -0
  14. package/dist/lib/server/decisioning/runtime/postgres-task-settlement.js +108 -36
  15. package/dist/lib/server/decisioning/runtime/postgres-task-settlement.mjs +108 -36
  16. package/dist/lib/server/decisioning/runtime/to-context.js +38 -9
  17. package/dist/lib/server/decisioning/runtime/to-context.mjs +38 -9
  18. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  19. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  20. package/dist/lib/types/schemas.generated.js +0 -63
  21. package/dist/lib/types/schemas.generated.mjs +0 -63
  22. package/dist/lib/utils/well-formed-unicode.d.mts +2 -0
  23. package/dist/lib/utils/well-formed-unicode.d.ts +2 -0
  24. package/dist/lib/utils/well-formed-unicode.js +51 -0
  25. package/dist/lib/utils/well-formed-unicode.mjs +27 -0
  26. package/dist/lib/version.d.mts +3 -3
  27. package/dist/lib/version.d.ts +3 -3
  28. package/dist/lib/version.js +3 -3
  29. package/dist/lib/version.mjs +3 -3
  30. package/docs/guides/BUILD-AN-AGENT.md +7 -0
  31. package/docs/guides/DURABLE-TASK-SETTLEMENT.md +468 -0
  32. package/docs/llms.txt +5 -2
  33. package/docs/migration-13-to-14.md +1 -1
  34. package/docs/migration-task-registry-scoping.md +45 -14
  35. package/package.json +2 -1
@@ -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-08-29T18:18:11.038Z"
7
+ "synced_at": "2026-08-30T04:26:50.358Z"
8
8
  }
@@ -43,6 +43,10 @@ export interface RequestContext<TAccount = Account> {
43
43
  * `ctx_metadata`, account objects, task results, logs, or durable HITL
44
44
  * state. Background workers must re-resolve credentials; if durable work
45
45
  * only needs identity, snapshot a stable non-secret principal identifier.
46
+ * A native `AbortSignal` stored as a data property at
47
+ * `authInfo.extra.signal` retains its identity so later host cancellation
48
+ * remains observable. Other auth values retain the framework's existing
49
+ * clone-and-freeze behavior.
46
50
  */
47
51
  authInfo?: Readonly<ResolvedAuthInfo>;
48
52
  /** Framework-derived caller namespace for authenticated mutations. */
@@ -43,6 +43,10 @@ export interface RequestContext<TAccount = Account> {
43
43
  * `ctx_metadata`, account objects, task results, logs, or durable HITL
44
44
  * state. Background workers must re-resolve credentials; if durable work
45
45
  * only needs identity, snapshot a stable non-secret principal identifier.
46
+ * A native `AbortSignal` stored as a data property at
47
+ * `authInfo.extra.signal` retains its identity so later host cancellation
48
+ * remains observable. Other auth values retain the framework's existing
49
+ * clone-and-freeze behavior.
46
50
  */
47
51
  authInfo?: Readonly<ResolvedAuthInfo>;
48
52
  /** Framework-derived caller namespace for authenticated mutations. */
@@ -49,6 +49,7 @@ export { PlatformConfigError, validatePlatform } from './runtime/validate-platfo
49
49
  export { completeScopedTask, createInMemoryTaskRegistry, failScopedTask, updateScopedTaskProgress, type ScopedTaskRef, type TaskMutationOutcome, type TaskRegistry, type TaskRegistryMutationResult, type TaskRegistryScope, type TaskRecord, type TaskStatus, } from './runtime/task-registry.mjs';
50
50
  export { createPostgresTaskRegistry, getDecisioningTaskRegistryBootstrap, getDecisioningTaskRegistryMigration, getDecisioningTaskRegistryScopeV1Upgrade, type CreatePostgresTaskRegistryOptions, type DecisioningTaskRegistryScopeV1Upgrade, type DecisioningTaskRegistryScopeV1UpgradeOptions, type PgQueryable, type PgTransactionClient, type PgTransactionalPool, } from './runtime/postgres-task-registry.mjs';
51
51
  export { completeScopedPushTask, createPostgresTaskSettlementCoordinator, failScopedPushTask, TaskPushSettlementConfigurationError, type PostgresTaskSettlementCoordinator, type PostgresTaskSettlementCoordinatorOptions, type TaskPushDeliveryState, type TaskPushSettlementConfig, type TaskPushSettlementOutcome, } from './runtime/postgres-task-settlement.mjs';
52
+ export { canonicalizeTaskSettlementIntent, createPostgresTaskSettlementIntentQueue, getTaskSettlementIntentMigration, TASK_SETTLEMENT_INTENT_IDEMPOTENCY_HORIZON_MS, TaskSettlementIntentConflictError, type CreatePostgresTaskSettlementIntentQueueOptions, type DurableTaskSettlementRef, type PostgresTaskSettlementIntentQueue, type PruneTaskSettlementIntentAcknowledgementsOptions, type RecoverTaskSettlementIntentsOptions, type TaskSettlementIntent, type TaskSettlementIntentCheckpoint, type TaskSettlementIntentRecoveryContext, type TaskSettlementIntentRecoveryErrorContext, type TaskSettlementIntentRecoveryMetrics, type TaskSettlementIntentWriteOptions, } from './runtime/postgres-task-settlement-intents.mjs';
52
53
  export { createTenantRegistry, createDefaultJwksValidator, createSelfSignedTenantKey, createNoopJwksValidator, type TenantRegistry, type TenantConfig, type TenantSigningKey, type TenantStatus, type TenantHealth, type TenantRegistryOptions, type JwksValidator, type JwksValidationResult, } from './tenant-registry.mjs';
53
54
  export { getAsset, getAssetSlot, requireAsset } from './manifest-helpers.mjs';
54
55
  export type { CreativeAssetsContainer } from './manifest-helpers.mjs';
@@ -49,6 +49,7 @@ export { PlatformConfigError, validatePlatform } from './runtime/validate-platfo
49
49
  export { completeScopedTask, createInMemoryTaskRegistry, failScopedTask, updateScopedTaskProgress, type ScopedTaskRef, type TaskMutationOutcome, type TaskRegistry, type TaskRegistryMutationResult, type TaskRegistryScope, type TaskRecord, type TaskStatus, } from './runtime/task-registry';
50
50
  export { createPostgresTaskRegistry, getDecisioningTaskRegistryBootstrap, getDecisioningTaskRegistryMigration, getDecisioningTaskRegistryScopeV1Upgrade, type CreatePostgresTaskRegistryOptions, type DecisioningTaskRegistryScopeV1Upgrade, type DecisioningTaskRegistryScopeV1UpgradeOptions, type PgQueryable, type PgTransactionClient, type PgTransactionalPool, } from './runtime/postgres-task-registry';
51
51
  export { completeScopedPushTask, createPostgresTaskSettlementCoordinator, failScopedPushTask, TaskPushSettlementConfigurationError, type PostgresTaskSettlementCoordinator, type PostgresTaskSettlementCoordinatorOptions, type TaskPushDeliveryState, type TaskPushSettlementConfig, type TaskPushSettlementOutcome, } from './runtime/postgres-task-settlement';
52
+ export { canonicalizeTaskSettlementIntent, createPostgresTaskSettlementIntentQueue, getTaskSettlementIntentMigration, TASK_SETTLEMENT_INTENT_IDEMPOTENCY_HORIZON_MS, TaskSettlementIntentConflictError, type CreatePostgresTaskSettlementIntentQueueOptions, type DurableTaskSettlementRef, type PostgresTaskSettlementIntentQueue, type PruneTaskSettlementIntentAcknowledgementsOptions, type RecoverTaskSettlementIntentsOptions, type TaskSettlementIntent, type TaskSettlementIntentCheckpoint, type TaskSettlementIntentRecoveryContext, type TaskSettlementIntentRecoveryErrorContext, type TaskSettlementIntentRecoveryMetrics, type TaskSettlementIntentWriteOptions, } from './runtime/postgres-task-settlement-intents';
52
53
  export { createTenantRegistry, createDefaultJwksValidator, createSelfSignedTenantKey, createNoopJwksValidator, type TenantRegistry, type TenantConfig, type TenantSigningKey, type TenantStatus, type TenantHealth, type TenantRegistryOptions, type JwksValidator, type JwksValidationResult, } from './tenant-registry';
53
54
  export { getAsset, getAssetSlot, requireAsset } from './manifest-helpers';
54
55
  export type { CreativeAssetsContainer } from './manifest-helpers';
@@ -46,7 +46,9 @@ __export(decisioning_exports, {
46
46
  ProductUnavailableError: () => import_errors_typed.ProductUnavailableError,
47
47
  RateLimitedError: () => import_errors_typed.RateLimitedError,
48
48
  ServiceUnavailableError: () => import_errors_typed.ServiceUnavailableError,
49
+ TASK_SETTLEMENT_INTENT_IDEMPOTENCY_HORIZON_MS: () => import_postgres_task_settlement_intents.TASK_SETTLEMENT_INTENT_IDEMPOTENCY_HORIZON_MS,
49
50
  TaskPushSettlementConfigurationError: () => import_postgres_task_settlement.TaskPushSettlementConfigurationError,
51
+ TaskSettlementIntentConflictError: () => import_postgres_task_settlement_intents.TaskSettlementIntentConflictError,
50
52
  UnsupportedFeatureError: () => import_errors_typed.UnsupportedFeatureError,
51
53
  batchPoll: () => import_helpers.batchPoll,
52
54
  buildListCreativesResponse: () => import_list_helpers.buildListCreativesResponse,
@@ -55,6 +57,7 @@ __export(decisioning_exports, {
55
57
  buildPricingOption: () => import_assembly_helpers.buildPricingOption,
56
58
  buildProduct: () => import_assembly_helpers.buildProduct,
57
59
  buildProductLegacy: () => import_assembly_helpers.buildProductLegacy,
60
+ canonicalizeTaskSettlementIntent: () => import_postgres_task_settlement_intents.canonicalizeTaskSettlementIntent,
58
61
  completeScopedPushTask: () => import_postgres_task_settlement.completeScopedPushTask,
59
62
  completeScopedTask: () => import_task_registry.completeScopedTask,
60
63
  composeMethod: () => import_compose.composeMethod,
@@ -66,6 +69,7 @@ __export(decisioning_exports, {
66
69
  createPostgresProposalStore: () => import_proposal.createPostgresProposalStore,
67
70
  createPostgresTaskRegistry: () => import_postgres_task_registry.createPostgresTaskRegistry,
68
71
  createPostgresTaskSettlementCoordinator: () => import_postgres_task_settlement.createPostgresTaskSettlementCoordinator,
72
+ createPostgresTaskSettlementIntentQueue: () => import_postgres_task_settlement_intents.createPostgresTaskSettlementIntentQueue,
69
73
  createSelfSignedTenantKey: () => import_tenant_registry.createSelfSignedTenantKey,
70
74
  createTenantAdminHandlers: () => import_admin_router.createTenantAdminHandlers,
71
75
  createTenantAdminRouter: () => import_admin_router.createTenantAdminRouter,
@@ -100,6 +104,7 @@ __export(decisioning_exports, {
100
104
  getHydratedLegacyFormatIds: () => import_from_platform.getHydratedLegacyFormatIds,
101
105
  getProposalStoreMigration: () => import_proposal.getProposalStoreMigration,
102
106
  getStatusChangeBus: () => import_status_changes.getStatusChangeBus,
107
+ getTaskSettlementIntentMigration: () => import_postgres_task_settlement_intents.getTaskSettlementIntentMigration,
103
108
  identityStatusMappers: () => import_status_mappers.identityStatusMappers,
104
109
  maybeHydrateRecipesForMediaBuyId: () => import_proposal.maybeHydrateRecipesForMediaBuyId,
105
110
  maybeInterceptFinalize: () => import_proposal.maybeInterceptFinalize,
@@ -145,6 +150,7 @@ var import_validate_platform = require('./runtime/validate-platform.js');
145
150
  var import_task_registry = require('./runtime/task-registry.js');
146
151
  var import_postgres_task_registry = require('./runtime/postgres-task-registry.js');
147
152
  var import_postgres_task_settlement = require('./runtime/postgres-task-settlement.js');
153
+ var import_postgres_task_settlement_intents = require('./runtime/postgres-task-settlement-intents.js');
148
154
  var import_tenant_registry = require('./tenant-registry.js');
149
155
  var import_manifest_helpers = require('./manifest-helpers.js');
150
156
  var import_list_helpers = require('./list-helpers.js');
@@ -184,7 +190,9 @@ var import_assembly_helpers = require('./assembly-helpers.js');
184
190
  ProductUnavailableError,
185
191
  RateLimitedError,
186
192
  ServiceUnavailableError,
193
+ TASK_SETTLEMENT_INTENT_IDEMPOTENCY_HORIZON_MS,
187
194
  TaskPushSettlementConfigurationError,
195
+ TaskSettlementIntentConflictError,
188
196
  UnsupportedFeatureError,
189
197
  batchPoll,
190
198
  buildListCreativesResponse,
@@ -193,6 +201,7 @@ var import_assembly_helpers = require('./assembly-helpers.js');
193
201
  buildPricingOption,
194
202
  buildProduct,
195
203
  buildProductLegacy,
204
+ canonicalizeTaskSettlementIntent,
196
205
  completeScopedPushTask,
197
206
  completeScopedTask,
198
207
  composeMethod,
@@ -204,6 +213,7 @@ var import_assembly_helpers = require('./assembly-helpers.js');
204
213
  createPostgresProposalStore,
205
214
  createPostgresTaskRegistry,
206
215
  createPostgresTaskSettlementCoordinator,
216
+ createPostgresTaskSettlementIntentQueue,
207
217
  createSelfSignedTenantKey,
208
218
  createTenantAdminHandlers,
209
219
  createTenantAdminRouter,
@@ -238,6 +248,7 @@ var import_assembly_helpers = require('./assembly-helpers.js');
238
248
  getHydratedLegacyFormatIds,
239
249
  getProposalStoreMigration,
240
250
  getStatusChangeBus,
251
+ getTaskSettlementIntentMigration,
241
252
  identityStatusMappers,
242
253
  maybeHydrateRecipesForMediaBuyId,
243
254
  maybeInterceptFinalize,
@@ -61,6 +61,13 @@ import {
61
61
  failScopedPushTask,
62
62
  TaskPushSettlementConfigurationError
63
63
  } from "./runtime/postgres-task-settlement.mjs";
64
+ import {
65
+ canonicalizeTaskSettlementIntent,
66
+ createPostgresTaskSettlementIntentQueue,
67
+ getTaskSettlementIntentMigration,
68
+ TASK_SETTLEMENT_INTENT_IDEMPOTENCY_HORIZON_MS,
69
+ TaskSettlementIntentConflictError
70
+ } from "./runtime/postgres-task-settlement-intents.mjs";
64
71
  import {
65
72
  createTenantRegistry,
66
73
  createDefaultJwksValidator,
@@ -145,7 +152,9 @@ export {
145
152
  ProductUnavailableError,
146
153
  RateLimitedError,
147
154
  ServiceUnavailableError,
155
+ TASK_SETTLEMENT_INTENT_IDEMPOTENCY_HORIZON_MS,
148
156
  TaskPushSettlementConfigurationError,
157
+ TaskSettlementIntentConflictError,
149
158
  UnsupportedFeatureError,
150
159
  batchPoll,
151
160
  buildListCreativesResponse,
@@ -154,6 +163,7 @@ export {
154
163
  buildPricingOption,
155
164
  buildProduct,
156
165
  buildProductLegacy,
166
+ canonicalizeTaskSettlementIntent,
157
167
  completeScopedPushTask,
158
168
  completeScopedTask,
159
169
  composeMethod,
@@ -165,6 +175,7 @@ export {
165
175
  createPostgresProposalStore,
166
176
  createPostgresTaskRegistry,
167
177
  createPostgresTaskSettlementCoordinator,
178
+ createPostgresTaskSettlementIntentQueue,
168
179
  createSelfSignedTenantKey,
169
180
  createTenantAdminHandlers,
170
181
  createTenantAdminRouter,
@@ -199,6 +210,7 @@ export {
199
210
  getHydratedLegacyFormatIds,
200
211
  getProposalStoreMigration,
201
212
  getStatusChangeBus,
213
+ getTaskSettlementIntentMigration,
202
214
  identityStatusMappers,
203
215
  maybeHydrateRecipesForMediaBuyId,
204
216
  maybeInterceptFinalize,
@@ -382,7 +382,7 @@ function rowToRecord(row) {
382
382
  ...row.owner_scope ? { ownerScope: row.owner_scope } : {},
383
383
  status: row.status,
384
384
  ...row.status_message !== null && { statusMessage: row.status_message },
385
- ...row.result !== null && row.result !== void 0 && { result: row.result },
385
+ ...(row.has_result ?? (row.result !== null && row.result !== void 0)) && { result: row.result },
386
386
  ...row.error !== null && row.error !== void 0 && { error: row.error },
387
387
  ...row.progress !== null && row.progress !== void 0 && { progress: row.progress },
388
388
  ...row.has_webhook && { hasWebhook: true },
@@ -447,7 +447,8 @@ function createPostgresTaskRegistry(opts) {
447
447
  if (scope.registryId !== void 0 && scope.registryId !== registryId) return null;
448
448
  const { rows } = await query(
449
449
  "getTask",
450
- `SELECT task_id, tool, account_id, owner_scope, status, status_message, result, error, progress, has_webhook, created_at, updated_at
450
+ `SELECT task_id, tool, account_id, owner_scope, status, status_message, result,
451
+ result IS NOT NULL AS has_result, error, progress, has_webhook, created_at, updated_at
451
452
  FROM ${table} WHERE task_id = $1 AND registry_namespace = $2 AND account_id = $3 AND owner_scope = $4`,
452
453
  [taskId, namespace, scope.accountId, scope.ownerScope]
453
454
  );
@@ -457,7 +458,8 @@ function createPostgresTaskRegistry(opts) {
457
458
  async list(listOpts) {
458
459
  const { rows } = await query(
459
460
  "list",
460
- `SELECT task_id, tool, account_id, owner_scope, status, status_message, result, error, progress, has_webhook, created_at, updated_at
461
+ `SELECT task_id, tool, account_id, owner_scope, status, status_message, result,
462
+ result IS NOT NULL AS has_result, error, progress, has_webhook, created_at, updated_at
461
463
  FROM ${table}
462
464
  WHERE registry_namespace = $1 AND account_id = $2 AND owner_scope = $3
463
465
  ORDER BY created_at DESC, task_id DESC`,
@@ -355,7 +355,7 @@ function rowToRecord(row) {
355
355
  ...row.owner_scope ? { ownerScope: row.owner_scope } : {},
356
356
  status: row.status,
357
357
  ...row.status_message !== null && { statusMessage: row.status_message },
358
- ...row.result !== null && row.result !== void 0 && { result: row.result },
358
+ ...(row.has_result ?? (row.result !== null && row.result !== void 0)) && { result: row.result },
359
359
  ...row.error !== null && row.error !== void 0 && { error: row.error },
360
360
  ...row.progress !== null && row.progress !== void 0 && { progress: row.progress },
361
361
  ...row.has_webhook && { hasWebhook: true },
@@ -420,7 +420,8 @@ function createPostgresTaskRegistry(opts) {
420
420
  if (scope.registryId !== void 0 && scope.registryId !== registryId) return null;
421
421
  const { rows } = await query(
422
422
  "getTask",
423
- `SELECT task_id, tool, account_id, owner_scope, status, status_message, result, error, progress, has_webhook, created_at, updated_at
423
+ `SELECT task_id, tool, account_id, owner_scope, status, status_message, result,
424
+ result IS NOT NULL AS has_result, error, progress, has_webhook, created_at, updated_at
424
425
  FROM ${table} WHERE task_id = $1 AND registry_namespace = $2 AND account_id = $3 AND owner_scope = $4`,
425
426
  [taskId, namespace, scope.accountId, scope.ownerScope]
426
427
  );
@@ -430,7 +431,8 @@ function createPostgresTaskRegistry(opts) {
430
431
  async list(listOpts) {
431
432
  const { rows } = await query(
432
433
  "list",
433
- `SELECT task_id, tool, account_id, owner_scope, status, status_message, result, error, progress, has_webhook, created_at, updated_at
434
+ `SELECT task_id, tool, account_id, owner_scope, status, status_message, result,
435
+ result IS NOT NULL AS has_result, error, progress, has_webhook, created_at, updated_at
434
436
  FROM ${table}
435
437
  WHERE registry_namespace = $1 AND account_id = $2 AND owner_scope = $3
436
438
  ORDER BY created_at DESC, task_id DESC`,
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Durable PostgreSQL queue for application-owned task settlement intents.
3
+ *
4
+ * `createPostgresTaskSettlementCoordinator()` makes the task transition and
5
+ * terminal webhook checkpoint atomic. This queue protects the boundary before
6
+ * that call: an application may commit a human approval or provider outcome
7
+ * and then die before it asks the SDK to settle the AdCP task.
8
+ */
9
+ import type { AdcpStructuredError } from '../async-outcome.mjs';
10
+ import type { PgQueryable } from './postgres-task-registry.mjs';
11
+ import { type ScopedTaskRef } from './task-registry.mjs';
12
+ /** Default conflict-retention window after an intent is acknowledged. */
13
+ export declare const TASK_SETTLEMENT_INTENT_IDEMPOTENCY_HORIZON_MS: number;
14
+ /** Complete, serializable task handle required by the durable intent queue. */
15
+ export interface DurableTaskSettlementRef extends ScopedTaskRef {
16
+ registryId: string;
17
+ ownerScope: string;
18
+ }
19
+ export type TaskSettlementIntent = {
20
+ taskRef: DurableTaskSettlementRef;
21
+ action: 'complete';
22
+ result: unknown;
23
+ } | {
24
+ taskRef: DurableTaskSettlementRef;
25
+ action: 'fail';
26
+ error: AdcpStructuredError;
27
+ result?: unknown;
28
+ };
29
+ export interface TaskSettlementIntentCheckpoint extends DurableTaskSettlementRef {
30
+ queueNamespace: string;
31
+ intentFingerprint: string;
32
+ }
33
+ export interface TaskSettlementIntentRecoveryContext {
34
+ attemptCount: number;
35
+ /** Extend this claim by the configured lease duration. Returns false if fencing ownership was lost. */
36
+ extendLease(): Promise<boolean>;
37
+ }
38
+ export interface TaskSettlementIntentRecoveryMetrics {
39
+ claimed: number;
40
+ settled: number;
41
+ retried: number;
42
+ deadLettered: number;
43
+ leaseLost: number;
44
+ }
45
+ export interface TaskSettlementIntentRecoveryErrorContext {
46
+ attemptCount: number;
47
+ taskRef: DurableTaskSettlementRef;
48
+ action: TaskSettlementIntent['action'];
49
+ disposition: 'retry' | 'dead_letter' | 'lease_lost';
50
+ }
51
+ export interface RecoverTaskSettlementIntentsOptions {
52
+ /**
53
+ * Apply the exact terminal intent and return the literal `settled` only
54
+ * after the intended state is proven. The implementation MUST be
55
+ * idempotent because a worker can die after settlement and before ack.
56
+ */
57
+ settle(intent: TaskSettlementIntent, context: TaskSettlementIntentRecoveryContext): Promise<'settled'>;
58
+ batchSize?: number;
59
+ leaseMs?: number;
60
+ retryAfterMs?: number;
61
+ maxRetryAfterMs?: number;
62
+ maxAttempts?: number;
63
+ workerId?: string;
64
+ /** Observability hook. Error messages are never persisted by the queue. */
65
+ onError?(error: unknown, context: TaskSettlementIntentRecoveryErrorContext): void | Promise<void>;
66
+ }
67
+ export interface TaskSettlementIntentWriteOptions {
68
+ /**
69
+ * Optional active transaction client. Use this to commit the domain outcome
70
+ * and settlement intent atomically in the same application transaction.
71
+ */
72
+ db?: PgQueryable;
73
+ }
74
+ export interface PruneTaskSettlementIntentAcknowledgementsOptions extends TaskSettlementIntentWriteOptions {
75
+ /** Maximum tombstones removed by this call. Defaults to 1000; maximum 10000. */
76
+ limit?: number;
77
+ }
78
+ export interface PostgresTaskSettlementIntentQueue {
79
+ readonly durability: 'durable';
80
+ enqueue(intent: TaskSettlementIntent, options?: TaskSettlementIntentWriteOptions): Promise<TaskSettlementIntentCheckpoint>;
81
+ /** Returns false when the exact checkpoint was already absent. */
82
+ acknowledge(checkpoint: TaskSettlementIntentCheckpoint, options?: TaskSettlementIntentWriteOptions): Promise<boolean>;
83
+ /** Remove a bounded batch of acknowledgement tombstones whose idempotency horizon elapsed. */
84
+ pruneAcknowledged(options?: PruneTaskSettlementIntentAcknowledgementsOptions): Promise<number>;
85
+ recover(options: RecoverTaskSettlementIntentsOptions): Promise<TaskSettlementIntentRecoveryMetrics>;
86
+ probe(): Promise<void>;
87
+ }
88
+ export interface CreatePostgresTaskSettlementIntentQueueOptions {
89
+ /** Pool used by recovery and by writes that do not supply a transaction. */
90
+ db: PgQueryable;
91
+ /** Trusted deployment or tenant namespace. */
92
+ namespace: string;
93
+ /** Defaults to `adcp_task_settlement_intents`. */
94
+ tableName?: string;
95
+ /**
96
+ * Retain acknowledged fingerprints for this long so a conflicting terminal
97
+ * artifact cannot rebind the same scoped task. Defaults to seven days.
98
+ */
99
+ idempotencyHorizonMs?: number;
100
+ }
101
+ /** An existing scoped task is already bound to a different terminal intent. */
102
+ export declare class TaskSettlementIntentConflictError extends Error {
103
+ readonly name = "TaskSettlementIntentConflictError";
104
+ }
105
+ /** Bootstrap DDL for a new durable settlement-intent queue. */
106
+ export declare function getTaskSettlementIntentMigration(options?: {
107
+ tableName?: string;
108
+ }): string;
109
+ /**
110
+ * Create a durable task-settlement intent queue.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * const queue = createPostgresTaskSettlementIntentQueue({
115
+ * db: pool,
116
+ * namespace: 'seller-prod',
117
+ * });
118
+ *
119
+ * await withTransaction(async tx => {
120
+ * await saveApproval(tx, approval);
121
+ * await queue.enqueue({ taskRef, action: 'complete', result }, { db: tx });
122
+ * });
123
+ *
124
+ * await queue.recover({
125
+ * settle: async intent => {
126
+ * await settleThroughRegistryOrPushCoordinator(intent);
127
+ * return 'settled';
128
+ * },
129
+ * });
130
+ * ```
131
+ */
132
+ export declare function createPostgresTaskSettlementIntentQueue(options: CreatePostgresTaskSettlementIntentQueueOptions): PostgresTaskSettlementIntentQueue;
133
+ /** Clone, validate, and reduce an intent to the exact artifact persisted by the task registry. */
134
+ export declare function canonicalizeTaskSettlementIntent(intent: TaskSettlementIntent): TaskSettlementIntent & {
135
+ taskRef: DurableTaskSettlementRef;
136
+ };
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Durable PostgreSQL queue for application-owned task settlement intents.
3
+ *
4
+ * `createPostgresTaskSettlementCoordinator()` makes the task transition and
5
+ * terminal webhook checkpoint atomic. This queue protects the boundary before
6
+ * that call: an application may commit a human approval or provider outcome
7
+ * and then die before it asks the SDK to settle the AdCP task.
8
+ */
9
+ import type { AdcpStructuredError } from '../async-outcome';
10
+ import type { PgQueryable } from './postgres-task-registry';
11
+ import { type ScopedTaskRef } from './task-registry';
12
+ /** Default conflict-retention window after an intent is acknowledged. */
13
+ export declare const TASK_SETTLEMENT_INTENT_IDEMPOTENCY_HORIZON_MS: number;
14
+ /** Complete, serializable task handle required by the durable intent queue. */
15
+ export interface DurableTaskSettlementRef extends ScopedTaskRef {
16
+ registryId: string;
17
+ ownerScope: string;
18
+ }
19
+ export type TaskSettlementIntent = {
20
+ taskRef: DurableTaskSettlementRef;
21
+ action: 'complete';
22
+ result: unknown;
23
+ } | {
24
+ taskRef: DurableTaskSettlementRef;
25
+ action: 'fail';
26
+ error: AdcpStructuredError;
27
+ result?: unknown;
28
+ };
29
+ export interface TaskSettlementIntentCheckpoint extends DurableTaskSettlementRef {
30
+ queueNamespace: string;
31
+ intentFingerprint: string;
32
+ }
33
+ export interface TaskSettlementIntentRecoveryContext {
34
+ attemptCount: number;
35
+ /** Extend this claim by the configured lease duration. Returns false if fencing ownership was lost. */
36
+ extendLease(): Promise<boolean>;
37
+ }
38
+ export interface TaskSettlementIntentRecoveryMetrics {
39
+ claimed: number;
40
+ settled: number;
41
+ retried: number;
42
+ deadLettered: number;
43
+ leaseLost: number;
44
+ }
45
+ export interface TaskSettlementIntentRecoveryErrorContext {
46
+ attemptCount: number;
47
+ taskRef: DurableTaskSettlementRef;
48
+ action: TaskSettlementIntent['action'];
49
+ disposition: 'retry' | 'dead_letter' | 'lease_lost';
50
+ }
51
+ export interface RecoverTaskSettlementIntentsOptions {
52
+ /**
53
+ * Apply the exact terminal intent and return the literal `settled` only
54
+ * after the intended state is proven. The implementation MUST be
55
+ * idempotent because a worker can die after settlement and before ack.
56
+ */
57
+ settle(intent: TaskSettlementIntent, context: TaskSettlementIntentRecoveryContext): Promise<'settled'>;
58
+ batchSize?: number;
59
+ leaseMs?: number;
60
+ retryAfterMs?: number;
61
+ maxRetryAfterMs?: number;
62
+ maxAttempts?: number;
63
+ workerId?: string;
64
+ /** Observability hook. Error messages are never persisted by the queue. */
65
+ onError?(error: unknown, context: TaskSettlementIntentRecoveryErrorContext): void | Promise<void>;
66
+ }
67
+ export interface TaskSettlementIntentWriteOptions {
68
+ /**
69
+ * Optional active transaction client. Use this to commit the domain outcome
70
+ * and settlement intent atomically in the same application transaction.
71
+ */
72
+ db?: PgQueryable;
73
+ }
74
+ export interface PruneTaskSettlementIntentAcknowledgementsOptions extends TaskSettlementIntentWriteOptions {
75
+ /** Maximum tombstones removed by this call. Defaults to 1000; maximum 10000. */
76
+ limit?: number;
77
+ }
78
+ export interface PostgresTaskSettlementIntentQueue {
79
+ readonly durability: 'durable';
80
+ enqueue(intent: TaskSettlementIntent, options?: TaskSettlementIntentWriteOptions): Promise<TaskSettlementIntentCheckpoint>;
81
+ /** Returns false when the exact checkpoint was already absent. */
82
+ acknowledge(checkpoint: TaskSettlementIntentCheckpoint, options?: TaskSettlementIntentWriteOptions): Promise<boolean>;
83
+ /** Remove a bounded batch of acknowledgement tombstones whose idempotency horizon elapsed. */
84
+ pruneAcknowledged(options?: PruneTaskSettlementIntentAcknowledgementsOptions): Promise<number>;
85
+ recover(options: RecoverTaskSettlementIntentsOptions): Promise<TaskSettlementIntentRecoveryMetrics>;
86
+ probe(): Promise<void>;
87
+ }
88
+ export interface CreatePostgresTaskSettlementIntentQueueOptions {
89
+ /** Pool used by recovery and by writes that do not supply a transaction. */
90
+ db: PgQueryable;
91
+ /** Trusted deployment or tenant namespace. */
92
+ namespace: string;
93
+ /** Defaults to `adcp_task_settlement_intents`. */
94
+ tableName?: string;
95
+ /**
96
+ * Retain acknowledged fingerprints for this long so a conflicting terminal
97
+ * artifact cannot rebind the same scoped task. Defaults to seven days.
98
+ */
99
+ idempotencyHorizonMs?: number;
100
+ }
101
+ /** An existing scoped task is already bound to a different terminal intent. */
102
+ export declare class TaskSettlementIntentConflictError extends Error {
103
+ readonly name = "TaskSettlementIntentConflictError";
104
+ }
105
+ /** Bootstrap DDL for a new durable settlement-intent queue. */
106
+ export declare function getTaskSettlementIntentMigration(options?: {
107
+ tableName?: string;
108
+ }): string;
109
+ /**
110
+ * Create a durable task-settlement intent queue.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * const queue = createPostgresTaskSettlementIntentQueue({
115
+ * db: pool,
116
+ * namespace: 'seller-prod',
117
+ * });
118
+ *
119
+ * await withTransaction(async tx => {
120
+ * await saveApproval(tx, approval);
121
+ * await queue.enqueue({ taskRef, action: 'complete', result }, { db: tx });
122
+ * });
123
+ *
124
+ * await queue.recover({
125
+ * settle: async intent => {
126
+ * await settleThroughRegistryOrPushCoordinator(intent);
127
+ * return 'settled';
128
+ * },
129
+ * });
130
+ * ```
131
+ */
132
+ export declare function createPostgresTaskSettlementIntentQueue(options: CreatePostgresTaskSettlementIntentQueueOptions): PostgresTaskSettlementIntentQueue;
133
+ /** Clone, validate, and reduce an intent to the exact artifact persisted by the task registry. */
134
+ export declare function canonicalizeTaskSettlementIntent(intent: TaskSettlementIntent): TaskSettlementIntent & {
135
+ taskRef: DurableTaskSettlementRef;
136
+ };