@tangle-network/agent-gateway 0.7.0 → 0.8.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 (65) hide show
  1. package/README.md +108 -6
  2. package/dist/chunk-GITV7CPT.js +84 -0
  3. package/dist/chunk-GITV7CPT.js.map +1 -0
  4. package/dist/chunk-J5SDVHOL.js +104 -0
  5. package/dist/chunk-J5SDVHOL.js.map +1 -0
  6. package/dist/chunk-MP6IIAIA.js +5651 -0
  7. package/dist/chunk-MP6IIAIA.js.map +1 -0
  8. package/dist/index.d.ts +76 -12
  9. package/dist/index.js +307 -21
  10. package/dist/index.js.map +1 -1
  11. package/dist/middleware.d.ts +7 -2
  12. package/dist/middleware.js +3 -2
  13. package/dist/nonce-store.d.ts +47 -11
  14. package/dist/nonce-store.js +9 -3
  15. package/dist/observer-types-A0RtA8uL.d.ts +95 -0
  16. package/dist/observer.d.ts +79 -0
  17. package/dist/observer.js +11 -0
  18. package/dist/observer.js.map +1 -0
  19. package/dist/{types-CX2V06cN.d.ts → types-BHISsm7D.d.ts} +423 -166
  20. package/dist/types.d.ts +2 -1
  21. package/package.json +1 -1
  22. package/src/a2a/agent-card.ts +4 -3
  23. package/src/a2a/execution-fence.ts +162 -0
  24. package/src/a2a/handler.ts +507 -562
  25. package/src/a2a/message-send-execution.ts +241 -0
  26. package/src/a2a/message-stream-execution.ts +392 -0
  27. package/src/a2a/payment-recovery.ts +431 -0
  28. package/src/a2a/push-config-methods.ts +158 -0
  29. package/src/a2a/push-notifications.ts +172 -22
  30. package/src/a2a/task-cancellation.ts +50 -0
  31. package/src/a2a/task-finalization.ts +451 -0
  32. package/src/a2a/task-lifecycle.ts +54 -0
  33. package/src/a2a/task-methods.ts +163 -0
  34. package/src/a2a/task-push-delivery.ts +119 -0
  35. package/src/a2a/task-recovery.ts +11 -0
  36. package/src/a2a/task-state.ts +99 -0
  37. package/src/a2a/task-store-sql.ts +222 -24
  38. package/src/a2a/task-store.ts +58 -1
  39. package/src/a2a/task-submission-recovery.ts +178 -0
  40. package/src/a2a/types.ts +1 -0
  41. package/src/dispatch-authorization.ts +437 -0
  42. package/src/dispatch-payment-recovery.ts +248 -0
  43. package/src/dispatch-payment.ts +425 -0
  44. package/src/dispatch-pricing.ts +108 -0
  45. package/src/dispatch-sandbox.ts +422 -0
  46. package/src/dispatch-settlement.ts +139 -0
  47. package/src/dispatch-types.ts +81 -0
  48. package/src/dispatch.ts +35 -462
  49. package/src/index.ts +64 -2
  50. package/src/middleware.ts +313 -32
  51. package/src/mpp-payment.ts +117 -0
  52. package/src/nonce-store.ts +122 -20
  53. package/src/observer-types.ts +63 -0
  54. package/src/observer.ts +3 -63
  55. package/src/payment-operations.ts +485 -0
  56. package/src/payment-recovery-sql.ts +108 -0
  57. package/src/payment-recovery-worker.ts +488 -0
  58. package/src/payment-recovery.ts +331 -0
  59. package/src/payment-types.ts +48 -0
  60. package/src/types.ts +153 -42
  61. package/src/verify.ts +265 -36
  62. package/dist/chunk-3IKQWFKX.js +0 -1703
  63. package/dist/chunk-3IKQWFKX.js.map +0 -1
  64. package/dist/chunk-M7ZJAK4K.js +0 -53
  65. package/dist/chunk-M7ZJAK4K.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { NonceStore } from './nonce-store.js';
2
2
  import { RateLimitStore } from './rate-limit.js';
3
+ import { S as SandboxUsageReceipt, b as PaymentSettlementBasis, P as PaymentMethod, c as SandboxExecutionBudget, a as GatewayUsageEvent, G as GatewayObserver } from './observer-types-A0RtA8uL.js';
3
4
 
4
5
  /**
5
6
  * A2A protocol types (Google Agent-to-Agent, April 2025).
@@ -51,6 +52,7 @@ declare const A2A_ERROR_CODES: {
51
52
  readonly CONTENT_TYPE_NOT_SUPPORTED: -32005;
52
53
  readonly INVALID_AGENT_RESPONSE: -32006;
53
54
  readonly AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED: -32007;
55
+ readonly TASK_ACCESS_DENIED: -32008;
54
56
  };
55
57
  interface TextPart {
56
58
  kind: 'text';
@@ -193,6 +195,12 @@ interface AgentCard {
193
195
  interface TaskStore {
194
196
  get(id: string): Promise<Task | undefined>;
195
197
  put(task: Task): Promise<void>;
198
+ /** Insert only when the task id is absent. Required outside explicit demo mode. */
199
+ createIfAbsent?(task: Task): Promise<boolean>;
200
+ /** Replace only when the stored task still equals `expected`. Required for production races. */
201
+ compareAndSet?(expected: Task, next: Task): Promise<boolean>;
202
+ /** Replace an execution marker only while its owner lease is still live. */
203
+ compareAndSetExecution?(expected: Task, next: Task, requestId: string, now: number): Promise<boolean>;
196
204
  delete(id: string): Promise<void>;
197
205
  }
198
206
  declare class InMemoryTaskStore implements TaskStore {
@@ -201,6 +209,9 @@ declare class InMemoryTaskStore implements TaskStore {
201
209
  constructor(ttlMs?: number);
202
210
  get(id: string): Promise<Task | undefined>;
203
211
  put(task: Task): Promise<void>;
212
+ createIfAbsent(task: Task): Promise<boolean>;
213
+ compareAndSet(expected: Task, next: Task): Promise<boolean>;
214
+ compareAndSetExecution(expected: Task, next: Task, requestId: string, now: number): Promise<boolean>;
204
215
  delete(id: string): Promise<void>;
205
216
  /**
206
217
  * Sweep expired tasks. Called inline on every read/write — cheap for the
@@ -220,9 +231,8 @@ declare class InMemoryTaskStore implements TaskStore {
220
231
  * Schema is one table: tasks keyed by id with the full JSON payload, plus a
221
232
  * secondary index on `context_id` so `tasks/resubscribe` and conversational
222
233
  * lookups by context are O(log n). TTL is enforced at read time the same way
223
- * `InMemoryTaskStore` does — the gateway is single-writer per task id so a
224
- * stale row is invisible to callers regardless of when the row is physically
225
- * deleted.
234
+ * `InMemoryTaskStore` does — `createIfAbsent` and `compareAndSet` make task
235
+ * ownership safe when multiple gateway workers share the database.
226
236
  *
227
237
  * Why not bake in a specific driver? Hono workers run on Cloudflare (D1),
228
238
  * Node (pg / sqlite), Bun, Deno. Burning a hard dependency on one client
@@ -295,10 +305,17 @@ declare class SqlTaskStore implements TaskStore {
295
305
  });
296
306
  private get ttlMs();
297
307
  private get table();
308
+ private readRow;
309
+ private isExpired;
310
+ private deleteObservedRow;
298
311
  /** Idempotent. Call once at deploy. */
299
312
  migrate(): Promise<void>;
300
313
  get(id: string): Promise<Task | undefined>;
314
+ private insert;
301
315
  put(task: Task): Promise<void>;
316
+ createIfAbsent(task: Task): Promise<boolean>;
317
+ compareAndSet(expected: Task, next: Task): Promise<boolean>;
318
+ compareAndSetExecution(expected: Task, next: Task, requestId: string, now: number): Promise<boolean>;
302
319
  delete(id: string): Promise<void>;
303
320
  /**
304
321
  * Lookup tasks by contextId — used by `tasks/resubscribe` and the multi-turn
@@ -394,6 +411,14 @@ interface PushNotificationStore {
394
411
  list(taskId: string): Promise<PushNotificationConfig[]>;
395
412
  delete(taskId: string, configId: string): Promise<void>;
396
413
  }
414
+ /**
415
+ * Validate a push destination before the gateway sends task data to it.
416
+ *
417
+ * The default policy rejects URL credentials, non-HTTPS schemes, IP literals
418
+ * in reserved ranges, and common private hostnames. Production deployments
419
+ * should also provide `GatewayConfig.a2a.pushUrlValidator` for DNS policy.
420
+ */
421
+ declare function validatePushNotificationUrl(value: string): URL | undefined;
397
422
  declare class InMemoryPushNotificationStore implements PushNotificationStore {
398
423
  private readonly byTask;
399
424
  set(taskId: string, config: PushNotificationConfig): Promise<void>;
@@ -412,24 +437,37 @@ declare class SqlPushNotificationStore implements PushNotificationStore {
412
437
  list(taskId: string): Promise<PushNotificationConfig[]>;
413
438
  delete(taskId: string, configId: string): Promise<void>;
414
439
  }
415
- /**
416
- * Send the webhook for each registered config on a task. Signs the body with
417
- * HMAC-SHA256 against `webhookSecret` so the consumer can verify authenticity.
418
- * Fire-and-forget per the design note above — the function awaits delivery
419
- * (so observability hooks see the result) but does not retry on failure.
420
- *
421
- * The caller decides *when* to deliver — typically on terminal-state
422
- * transitions emitted from `message/send` and `message/stream`.
423
- */
424
- declare function deliverPushNotifications(args: {
440
+ interface PushDeliveryOptions {
425
441
  task: Task;
426
442
  store: PushNotificationStore;
427
- webhookSecret: string | undefined;
443
+ webhookSecret?: string;
444
+ /** Atomically claim one terminal delivery before its external side effect. */
445
+ claimDelivery?: (taskId: string, configId: string, terminalState: Task['status']['state']) => Promise<boolean>;
428
446
  /** Inject for tests. Defaults to global `fetch`. */
429
447
  fetcher?: typeof fetch;
448
+ /** Optional DNS-aware host policy for production deployments. */
449
+ urlValidator?: (url: URL) => boolean | Promise<boolean>;
450
+ /** Require `urlValidator` before sending from a production gateway. */
451
+ requireUrlValidator?: boolean;
430
452
  /** Optional callback so the gateway's observer can log delivery outcomes. */
431
453
  onDelivery?: (result: PushDeliveryResult) => void;
432
- }): Promise<PushDeliveryResult[]>;
454
+ }
455
+ type PushNotificationDeliveryOptions = Omit<PushDeliveryOptions, 'webhookSecret'> & {
456
+ webhookSecret: string;
457
+ };
458
+ /**
459
+ * Send signed webhooks for a terminal task.
460
+ *
461
+ * A non-empty HMAC secret is mandatory. This public production path cannot
462
+ * send an unsigned request.
463
+ */
464
+ declare function deliverPushNotifications(args: PushNotificationDeliveryOptions): Promise<PushDeliveryResult[]>;
465
+ /**
466
+ * Deliver unsigned webhooks only for explicit local demo mode.
467
+ *
468
+ * Production callers must use `deliverPushNotifications`.
469
+ */
470
+ declare function deliverDemoPushNotifications(args: Omit<PushNotificationDeliveryOptions, 'webhookSecret'>): Promise<PushDeliveryResult[]>;
433
471
  interface PushDeliveryResult {
434
472
  taskId: string;
435
473
  configId: string;
@@ -439,133 +477,270 @@ interface PushDeliveryResult {
439
477
  error?: string;
440
478
  }
441
479
 
480
+ /** Version negotiated by gateways that use durable payment operations. */
481
+ declare const PAYMENT_PROTOCOL_VERSION: 2;
482
+ type PaymentOperationState = 'claiming' | 'claimed' | 'executing' | 'retained' | 'settling' | 'settled' | 'releasing' | 'released' | 'reclaimable' | 'reclaimed';
483
+ /** Fenced result for a recovery lookup that found no provider operation. */
484
+ interface PaymentOperationNotFound {
485
+ protocolVersion: typeof PAYMENT_PROTOCOL_VERSION;
486
+ operationId: string;
487
+ state: 'not-found';
488
+ }
489
+ type PaymentOperationRecoveryResult = PaymentOperation | PaymentOperationNotFound;
490
+ /** Durable ownership of one signed payment authorization. */
491
+ interface PaymentOperation {
492
+ protocolVersion: typeof PAYMENT_PROTOCOL_VERSION;
493
+ operationId: string;
494
+ /** Request that atomically created this operation. Idempotent reads retain the original value. */
495
+ acquiredByRequestId: string;
496
+ executionStartedAt?: number;
497
+ retentionReason?: string;
498
+ nonceKey: string;
499
+ authorizationId: string;
500
+ reservedAmount: bigint;
501
+ settledAmount: bigint;
502
+ refundAmount: bigint;
503
+ expiresAt: number;
504
+ state: PaymentOperationState;
505
+ }
506
+ interface PaymentAuthorizationContext {
507
+ requestId: string;
508
+ agentId: string;
509
+ requiredAmount: bigint;
510
+ maxOutputTokens: number;
511
+ executionBudget: {
512
+ maxInputTokens: number;
513
+ maxOutputTokens: number;
514
+ maxReasoningTokens: number;
515
+ maxToolTokens: number;
516
+ maxToolCalls: number;
517
+ maxProviderCostUsd: number;
518
+ };
519
+ }
520
+ interface PaymentSettlementInput {
521
+ amount: bigint;
522
+ totalCostUsd: number;
523
+ usage: SandboxUsageReceipt;
524
+ /** Distinguishes a provider receipt from the bounded missing-receipt fallback. */
525
+ basis: PaymentSettlementBasis;
526
+ }
442
527
  /**
443
- * Observability hook surface.
444
- *
445
- * Consumers implement GatewayObserver to wire the gateway into their existing
446
- * telemetry stack (Langfuse, OTEL, structured logs, Prometheus, etc.) without
447
- * the gateway itself depending on any of those libraries.
448
- *
449
- * Every event carries a requestId so downstream metrics can correlate the
450
- * payment verification, sandbox execution, and settlement for one request.
451
- * When no observer is configured, the gateway stays silent.
528
+ * One payment lifecycle shared by every payment-backed gateway surface.
529
+ * Implementations must persist the operation before external side effects.
452
530
  */
531
+ interface PaymentOperations {
532
+ readonly protocolVersion: typeof PAYMENT_PROTOCOL_VERSION;
533
+ claimPayment(payload: Record<string, unknown>, context: PaymentAuthorizationContext): Promise<PaymentOperation>;
534
+ /** Prevent expiry reclaim while the sandbox can consume provider resources. */
535
+ beginPaymentExecution(operation: PaymentOperation): Promise<PaymentOperation>;
536
+ /** Preserve funds after work when the final usage receipt is still missing. */
537
+ retainPayment(operation: PaymentOperation, reason: string): Promise<PaymentOperation>;
538
+ settlePayment(operation: PaymentOperation, input: PaymentSettlementInput): Promise<PaymentOperation>;
539
+ /**
540
+ * Read the authoritative durable operation without changing its state.
541
+ * Recovery uses this to avoid repeating a provider settlement after the
542
+ * provider committed but the task finalization write lost its acknowledgement.
543
+ */
544
+ getPaymentOperation(operationId: string): Promise<PaymentOperationRecoveryResult>;
545
+ /**
546
+ * Release an unused authorization.
547
+ * Repeated calls must recover an ambiguous acknowledgement by operationId.
548
+ */
549
+ releasePayment(operation: PaymentOperation, reason: string): Promise<PaymentOperation>;
550
+ reclaimPayment(operationId: string): Promise<PaymentOperationRecoveryResult>;
551
+ }
552
+ interface MemoryPaymentOperationsOptions {
553
+ now?: () => number;
554
+ onClaim?: (operation: PaymentOperation) => Promise<void>;
555
+ onSettle?: (operation: PaymentOperation, input: PaymentSettlementInput) => Promise<void>;
556
+ onRelease?: (operation: PaymentOperation, reason: string) => Promise<void>;
557
+ onReclaim?: (operation: PaymentOperation) => Promise<void>;
558
+ }
559
+ /** Small atomic implementation used by single-process deployments and tests. */
560
+ declare class MemoryPaymentOperations implements PaymentOperations {
561
+ private readonly options;
562
+ readonly protocolVersion: 2;
563
+ private readonly operations;
564
+ private readonly claimFlights;
565
+ private readonly claimTokens;
566
+ private readonly settleFlights;
567
+ private readonly releaseFlights;
568
+ private readonly reclaimFlights;
569
+ private readonly now;
570
+ constructor(options?: MemoryPaymentOperationsOptions);
571
+ claimPayment(payload: Record<string, unknown>, context: PaymentAuthorizationContext): Promise<PaymentOperation>;
572
+ settlePayment(operation: PaymentOperation, input: PaymentSettlementInput): Promise<PaymentOperation>;
573
+ beginPaymentExecution(operation: PaymentOperation): Promise<PaymentOperation>;
574
+ retainPayment(operation: PaymentOperation, reason: string): Promise<PaymentOperation>;
575
+ releasePayment(operation: PaymentOperation, reason: string): Promise<PaymentOperation>;
576
+ reclaimPayment(operationId: string): Promise<PaymentOperationRecoveryResult>;
577
+ private runSettlement;
578
+ private recoverSettlement;
579
+ private runReclaim;
580
+ private runRelease;
581
+ get(operationId: string): PaymentOperation | undefined;
582
+ getPaymentOperation(operationId: string): Promise<PaymentOperationRecoveryResult>;
583
+ private requireCurrent;
584
+ }
453
585
 
454
- interface RequestContext {
455
- requestId: string;
456
- agentSlug: string;
457
- startMs: number;
586
+ /** Version of the gateway's method-specific MPP charge contract. */
587
+ declare const MPP_CHARGE_PROTOCOL_VERSION: 1;
588
+ type MppChargeOperationState = 'confirmed' | 'releasing' | 'released';
589
+ /** Pure authentication result for one method credential. */
590
+ interface MppAuthenticatedCredential {
591
+ consumerId: string;
592
+ /**
593
+ * Stable, non-secret processor identity for this payment credential.
594
+ * Return the same value for equivalent encodings of one credential.
595
+ * The gateway hashes this value before it persists or claims it.
596
+ */
597
+ paymentIdentity: string;
458
598
  }
459
- interface AuthFailureReason {
460
- method: 'x402' | 'mpp' | 'apikey' | 'none';
461
- code: string;
462
- httpStatus: number;
599
+ /** Durable result of one immediate MPP charge. */
600
+ interface MppChargeOperation {
601
+ protocolVersion: typeof MPP_CHARGE_PROTOCOL_VERSION;
602
+ operationId: string;
603
+ acquiredByRequestId: string;
604
+ method: string;
605
+ /** A complete Payment-Receipt header value. */
606
+ receipt: string;
607
+ state: MppChargeOperationState;
463
608
  }
464
- interface GatewayObserver {
465
- /** Called at the start of every chat completions POST. */
466
- onRequestStart?: (ctx: RequestContext) => void | Promise<void>;
467
- /** Called when a payment method has been successfully verified. */
468
- onPaymentVerified?: (ctx: RequestContext, info: {
469
- method: PaymentMethod;
470
- consumerId: string;
471
- keyId?: string;
472
- }) => void | Promise<void>;
473
- /** Called when auth fails — every branch. */
474
- onAuthFailure?: (ctx: RequestContext, reason: AuthFailureReason) => void | Promise<void>;
475
- /** Called when a consumer hits the rate limit. */
476
- onRateLimited?: (ctx: RequestContext, info: {
477
- consumerId: string;
478
- retryAfterSeconds: number;
479
- }) => void | Promise<void>;
480
- /** Called when the request body exceeds the 64KB limit. */
481
- onBodyTooLarge?: (ctx: RequestContext, contentLength: number) => void | Promise<void>;
609
+ interface MppChargeRequest {
482
610
  /**
483
- * Called when prompt-injection patterns are detected.
484
- * `blocked` is true when blockInjection config is on and the request was
485
- * rejected; false when the patterns were logged but the request proceeded.
611
+ * Stable provider idempotency key. The adapter must bind every processor
612
+ * operation to this value before it attempts confirmation.
486
613
  */
487
- onInjectionDetected?: (ctx: RequestContext, info: {
488
- consumerId: string;
489
- patterns: string[];
490
- blocked: boolean;
491
- }) => void | Promise<void>;
492
- /** Called after a successful stream completes and recordUsage has fired. */
493
- onRequestComplete?: (ctx: RequestContext, usage: GatewayUsageEvent) => void | Promise<void>;
494
- /** Called when the sandbox throws. The error message is pre-scrubbed. */
495
- onStreamError?: (ctx: RequestContext, info: {
496
- consumerId: string;
497
- errorMessage: string;
498
- }) => void | Promise<void>;
499
- /** Called when settlement fails. Payment already occurred; this is async bookkeeping. */
500
- onSettlementError?: (ctx: RequestContext, info: {
501
- consumerId: string;
502
- method: PaymentMethod;
503
- errorMessage: string;
504
- }) => void | Promise<void>;
614
+ operationId: string;
615
+ requestId: string;
616
+ agentId: string;
617
+ consumerId: string;
618
+ method: string;
619
+ /** Original decoded credential. It is available only on the live request. */
620
+ credential: string;
621
+ amount: bigint;
622
+ currencyDecimals: number;
505
623
  }
624
+ type MppChargeRecoveryResult = MppChargeOperation
625
+ /** `not-found` is final and must fence this operation ID against a later charge. */
626
+ | {
627
+ operationId: string;
628
+ state: 'not-found' | 'pending';
629
+ };
506
630
  /**
507
- * Structured-log observer. Emits one JSON line per event on the `log` function.
508
- * Default sink: console.log. Production consumers usually pipe their own
509
- * structured logger (pino, winston, the cf Logs binding).
631
+ * Immediate-charge lifecycle for a non-BlueprinTEVM MPP method.
510
632
  *
511
- * Usage:
512
- * new ConsoleObserver(({ level, event, ...rest }) => logger.info({ event, ...rest }))
633
+ * `confirmPayment` runs after every request denial and before a response or
634
+ * sandbox call. It must use `operationId` as its processor idempotency key,
635
+ * confirm payment, verify final success, and only then return `confirmed`.
636
+ *
637
+ * Every method must also support id-only recovery and an idempotent release.
638
+ * Recovery must inspect the existing processor operation. It must never
639
+ * create a second charge when an acknowledgement is ambiguous.
513
640
  */
514
- declare class ConsoleObserver implements GatewayObserver {
515
- private readonly log;
516
- constructor(log?: (entry: Record<string, unknown>) => void);
517
- private emit;
518
- onRequestStart(ctx: RequestContext): void;
519
- onPaymentVerified(ctx: RequestContext, info: {
520
- method: PaymentMethod;
521
- consumerId: string;
522
- keyId?: string;
523
- }): void;
524
- onAuthFailure(ctx: RequestContext, reason: AuthFailureReason): void;
525
- onRateLimited(ctx: RequestContext, info: {
526
- consumerId: string;
527
- retryAfterSeconds: number;
528
- }): void;
529
- onBodyTooLarge(ctx: RequestContext, contentLength: number): void;
530
- onInjectionDetected(ctx: RequestContext, info: {
531
- consumerId: string;
532
- patterns: string[];
533
- blocked: boolean;
534
- }): void;
535
- onRequestComplete(ctx: RequestContext, usage: GatewayUsageEvent): void;
536
- onStreamError(ctx: RequestContext, info: {
537
- consumerId: string;
538
- errorMessage: string;
539
- }): void;
540
- onSettlementError(ctx: RequestContext, info: {
541
- consumerId: string;
542
- method: PaymentMethod;
543
- errorMessage: string;
544
- }): void;
641
+ interface MppChargeLifecycle {
642
+ readonly protocolVersion: typeof MPP_CHARGE_PROTOCOL_VERSION;
643
+ confirmPayment(request: MppChargeRequest): Promise<MppChargeOperation>;
644
+ releasePayment(operation: MppChargeOperation, reason: string): Promise<MppChargeOperation>;
645
+ recoverPayment(operationId: string): Promise<MppChargeRecoveryResult>;
545
646
  }
546
- /**
547
- * Compose multiple observers into one. Errors in any individual observer
548
- * don't break the others (fire-and-forget telemetry).
549
- */
550
- declare class CompositeObserver implements GatewayObserver {
551
- private readonly observers;
552
- constructor(observers: GatewayObserver[]);
553
- private fanOut;
554
- onRequestStart: (ctx: RequestContext) => Promise<void>;
555
- onPaymentVerified: (ctx: RequestContext, info: Parameters<Required<GatewayObserver>["onPaymentVerified"]>[1]) => Promise<void>;
556
- onAuthFailure: (ctx: RequestContext, reason: AuthFailureReason) => Promise<void>;
557
- onRateLimited: (ctx: RequestContext, info: Parameters<Required<GatewayObserver>["onRateLimited"]>[1]) => Promise<void>;
558
- onBodyTooLarge: (ctx: RequestContext, contentLength: number) => Promise<void>;
559
- onInjectionDetected: (ctx: RequestContext, info: Parameters<Required<GatewayObserver>["onInjectionDetected"]>[1]) => Promise<void>;
560
- onRequestComplete: (ctx: RequestContext, usage: GatewayUsageEvent) => Promise<void>;
561
- onStreamError: (ctx: RequestContext, info: Parameters<Required<GatewayObserver>["onStreamError"]>[1]) => Promise<void>;
562
- onSettlementError: (ctx: RequestContext, info: Parameters<Required<GatewayObserver>["onSettlementError"]>[1]) => Promise<void>;
647
+ /** Stable gateway identity. Neither the credential nor adapter identity is persisted. */
648
+ declare function mppPaymentOperationId(method: string, paymentIdentity: string): Promise<string>;
649
+
650
+ declare const PAYMENT_RECOVERY_VERSION: 1;
651
+ type PaymentRecoveryState = 'claiming' | 'claimed' | 'executing' | 'retained' | 'settling' | 'releasing' | 'reconciled';
652
+ interface SerializedPaymentOperation {
653
+ protocolVersion: 2;
654
+ operationId: string;
655
+ acquiredByRequestId: string;
656
+ executionStartedAt?: number;
657
+ retentionReason?: string;
658
+ nonceKey: string;
659
+ authorizationId: string;
660
+ reservedAmount: string;
661
+ settledAmount: string;
662
+ refundAmount: string;
663
+ expiresAt: number;
664
+ state: PaymentOperation['state'];
665
+ }
666
+ interface PaymentRecoveryAttribution {
667
+ requestId: string;
668
+ agentId: string;
669
+ agentSlug: string;
670
+ consumerId: string;
671
+ paymentMethod: PaymentMethod;
672
+ startMs: number;
673
+ pricePerTokenUsd: number;
674
+ platformFeePercent: number;
675
+ requiredAmount: string;
676
+ currencyDecimals: number;
677
+ maxOutputTokens: number;
678
+ executionBudget: SandboxExecutionBudget;
679
+ }
680
+ type PaymentRecoveryTarget = {
681
+ kind: 'x402';
682
+ operationId: string;
683
+ operation?: SerializedPaymentOperation;
684
+ } | {
685
+ kind: 'mpp-charge';
686
+ method: string;
687
+ operationId: string;
688
+ operation?: MppChargeOperation;
689
+ };
690
+ /** Durable outbox row for one payment identity. Rows are never deleted here. */
691
+ interface PaymentRecoveryRecord {
692
+ version: typeof PAYMENT_RECOVERY_VERSION;
693
+ id: string;
694
+ revision: number;
695
+ state: PaymentRecoveryState;
696
+ payment: PaymentRecoveryTarget;
697
+ attribution: PaymentRecoveryAttribution;
698
+ workStarted: boolean;
699
+ /** Earliest time a missing receipt may settle at the quoted ceiling. */
700
+ fallbackAt?: number;
701
+ usage?: SandboxUsageReceipt;
702
+ usageRecorded: boolean;
703
+ settlementBasis?: PaymentSettlementBasis;
704
+ reason?: string;
705
+ attempts: number;
706
+ lastError?: string;
707
+ nextAttemptAt: number;
708
+ lease?: {
709
+ id: string;
710
+ expiresAt: number;
711
+ };
712
+ createdAt: number;
713
+ updatedAt: number;
714
+ reconciledAt?: number;
715
+ }
716
+ interface PaymentRecoveryStore {
717
+ createIfAbsent(record: PaymentRecoveryRecord): Promise<boolean>;
718
+ get(id: string): Promise<PaymentRecoveryRecord | undefined>;
719
+ compareAndSet(expected: PaymentRecoveryRecord, next: PaymentRecoveryRecord): Promise<boolean>;
720
+ listDue(now: number, limit: number): Promise<PaymentRecoveryRecord[]>;
721
+ }
722
+ interface PaymentRecoveryConfig {
723
+ store: PaymentRecoveryStore;
724
+ /** Claimed payment with no execution becomes recoverable after this delay. */
725
+ staleRequestMs?: number;
726
+ /** Work without a final receipt settles at the quoted ceiling after this delay. */
727
+ receiptTimeoutMs?: number;
728
+ /** Failed provider recovery waits this long before its next attempt. */
729
+ retryDelayMs?: number;
730
+ /** One recovery worker owns a row for this duration. */
731
+ leaseMs?: number;
732
+ }
733
+ /** Atomic single-process store for tests and explicit local demo mode. */
734
+ declare class MemoryPaymentRecoveryStore implements PaymentRecoveryStore {
735
+ private readonly records;
736
+ createIfAbsent(record: PaymentRecoveryRecord): Promise<boolean>;
737
+ get(id: string): Promise<PaymentRecoveryRecord | undefined>;
738
+ compareAndSet(expected: PaymentRecoveryRecord, next: PaymentRecoveryRecord): Promise<boolean>;
739
+ listDue(now: number, limit: number): Promise<PaymentRecoveryRecord[]>;
740
+ }
741
+ declare class PaymentRecoveryFenceError extends Error {
742
+ constructor(id: string);
563
743
  }
564
- /**
565
- * Generate a request-id. Crypto-random 16 bytes, hex-encoded with an `req_` prefix.
566
- * Works in Workers, Node, and browsers — all have globalThis.crypto.
567
- */
568
- declare function generateRequestId(): string;
569
744
 
570
745
  interface AgentMeta {
571
746
  /** Unique agent identifier (workspace ID, session ID, etc.) */
@@ -635,7 +810,6 @@ interface AgentMeta {
635
810
  */
636
811
  skills?: AgentSkill[];
637
812
  }
638
- type PaymentMethod = 'x402' | 'mpp' | 'apikey' | 'none';
639
813
  interface X402Config {
640
814
  /** Ethereum operator address for SpendAuth verification */
641
815
  operatorAddress: string;
@@ -647,14 +821,57 @@ interface X402Config {
647
821
  rpcUrl?: string;
648
822
  /** Demo mode: skip signature verification (default: false). NEVER enable in production. */
649
823
  demoMode?: boolean;
650
- /** Production signer verification. Called with the raw SpendAuth payload. Return true if signature is valid. */
651
- verifySigner?: (payload: Record<string, unknown>) => Promise<boolean>;
824
+ /** Protocol version for new durable payment operations. Production version 1 is read-only. */
825
+ paymentProtocolVersion?: 1 | 2;
826
+ /**
827
+ * Production signature verification. This callback must not reserve, claim,
828
+ * or mutate payment state.
829
+ */
830
+ verifySigner?: (payload: Record<string, unknown>, context?: {
831
+ protocolVersion: 1 | 2;
832
+ requestId?: string;
833
+ }) => Promise<boolean>;
834
+ /**
835
+ * Claim the verified payment after all request checks pass and immediately
836
+ * before sandbox work starts. Version 2 returns durable operation ownership.
837
+ * A boolean return is the version 1 demo-only compatibility path.
838
+ * Production version 1 must omit this callback because it has no durable
839
+ * provider operation or recovery identity.
840
+ */
841
+ authorizePayment?: (payload: Record<string, unknown>, context: PaymentAuthorizationContext) => Promise<boolean | PaymentOperation>;
842
+ /** Version 2 operation store. It owns claim, settle, release, and reclaim. */
843
+ paymentOperations?: PaymentOperations;
844
+ /**
845
+ * Number of base-unit decimals used by the payment token. Defaults to 6.
846
+ * The gateway uses this value to reject a payment that cannot cover the
847
+ * request's maximum token charge before it calls `verifySigner`.
848
+ */
849
+ currencyDecimals?: number;
652
850
  }
653
851
  interface MppConfig {
654
852
  /** MPP realm (e.g. "agents.tangle.tools") */
655
853
  realm: string;
656
854
  /** MPP method name (default: "blueprintevm") */
657
855
  method?: string;
856
+ /**
857
+ * Pure credential authentication. Return stable method-owned identity, or null.
858
+ * This callback must not consume a credential, create a processor object,
859
+ * reserve funds, confirm payment, or perform any other financial mutation.
860
+ */
861
+ authenticateCredential?: (payload: Record<string, unknown>, context: {
862
+ method: string;
863
+ credential: string;
864
+ }) => Promise<MppAuthenticatedCredential | null>;
865
+ /**
866
+ * @deprecated Use authenticateCredential and return a stable payment identity.
867
+ * This 0.7.1 callback remains supported through an explicit compatibility adapter.
868
+ */
869
+ verifySigner?: (payload: Record<string, unknown>, context: {
870
+ method: string;
871
+ credential: string;
872
+ }) => Promise<string | null>;
873
+ /** Required immediate-charge lifecycle for every non-BlueprinTEVM method. */
874
+ charge?: MppChargeLifecycle;
658
875
  }
659
876
  interface PaymentResult {
660
877
  method: PaymentMethod;
@@ -680,27 +897,6 @@ interface ApiKeyInfo {
680
897
  /** Per-key daily limit override. */
681
898
  dailyLimit?: number;
682
899
  }
683
- interface GatewayUsageEvent {
684
- /**
685
- * Per-request id (matches `RequestContext.requestId`). Lets
686
- * `recordUsage` correlate the usage row to the same request that
687
- * `settlePayment` settles, observability hooks observe, and
688
- * `onRequestComplete` reports — without re-deriving from a
689
- * synthetic key. Required field as of 0.4.0; the gateway always has
690
- * it in scope at the recordUsage call site.
691
- */
692
- requestId: string;
693
- agentId: string;
694
- agentSlug: string;
695
- consumerId: string;
696
- paymentMethod: PaymentMethod;
697
- inputTokens: number;
698
- outputTokens: number;
699
- totalCostUsd: number;
700
- ownerEarnedUsd: number;
701
- platformFeeUsd: number;
702
- durationMs: number;
703
- }
704
900
  interface SandboxStreamEvent {
705
901
  type?: string;
706
902
  data?: {
@@ -721,12 +917,26 @@ interface SandboxStreamEvent {
721
917
  inputRequired?: {
722
918
  prompt?: string;
723
919
  };
920
+ /** Provider receipt fields. Version 2 operations require every field. */
921
+ usage?: Partial<SandboxUsageReceipt>;
922
+ /** Tool or reasoning events may carry hidden usage without visible text. */
923
+ tool?: {
924
+ name?: string;
925
+ inputTokens?: number;
926
+ outputTokens?: number;
927
+ };
928
+ reasoning?: {
929
+ tokens?: number;
930
+ };
724
931
  };
725
932
  }
726
933
  interface SandboxBox {
727
934
  streamPrompt(message: string, opts?: {
728
935
  sessionId?: string;
729
936
  systemPrompt?: string;
937
+ maxOutputTokens?: number;
938
+ executionBudget?: SandboxExecutionBudget;
939
+ signal?: AbortSignal;
730
940
  }): AsyncIterable<SandboxStreamEvent>;
731
941
  }
732
942
  interface GatewayConfig {
@@ -751,28 +961,59 @@ interface GatewayConfig {
751
961
  reason: string;
752
962
  code: string;
753
963
  }>;
754
- /** Record a usage event after request completes. */
964
+ /**
965
+ * Record a usage event after request completes.
966
+ * The implementation must atomically upsert by requestId and return
967
+ * success when the row already exists. Recovery may retry after an
968
+ * acknowledgement is lost, so one request ID must produce one usage row.
969
+ */
755
970
  recordUsage: (event: GatewayUsageEvent) => Promise<void>;
756
971
  /** x402 payment configuration */
757
972
  x402: X402Config;
758
- /** MPP (Machine Payments Protocol) configuration. If provided, gateway accepts Authorization: Payment headers. */
973
+ /** MPP (Machine Payments Protocol) configuration. It is advertised only when a production verifier or explicit demo mode is available. */
759
974
  mpp?: MppConfig;
975
+ /**
976
+ * Durable payment recovery outbox. Production payment protocol version 2
977
+ * and generic MPP charge methods require this configuration.
978
+ */
979
+ paymentRecovery?: PaymentRecoveryConfig;
760
980
  /**
761
981
  * Verify an API key. Return key info if valid, null if invalid.
762
- * Default: accepts any `sk_agent_*` key (demo mode).
982
+ * In explicit x402 demo mode, the built-in verifier accepts `sk_agent_*` keys.
983
+ * Production gateways must provide this callback.
763
984
  */
764
985
  verifyApiKey?: (authHeader: string) => Promise<ApiKeyInfo | null>;
765
986
  /**
766
- * Settle payment after successful response.
767
- * For x402: call ShieldedCredits.claimPayment()
768
- * For API key: deduct from spending limit
769
- * Default: no-op (demo mode).
987
+ * Settle a legacy payment after usage attribution is recorded.
988
+ * Version 2 x402 operations use `x402.paymentOperations` instead.
989
+ * Production x402 version 1 rejects this callback before nonce claim.
990
+ * For API keys, deduct from the spending limit.
991
+ * Default: no-op in explicit demo mode.
770
992
  */
771
993
  settlePayment?: (payment: PaymentResult, cost: number) => Promise<void>;
772
994
  /** Base URL for API key purchase links (e.g. "https://film.tangle.tools") */
773
995
  baseUrl?: string;
774
996
  /** Max message length in chars (default: 8000) */
775
997
  maxMessageLength?: number;
998
+ /** Maximum output token request the gateway accepts. Defaults to 4096. */
999
+ maxOutputTokens?: number;
1000
+ /** Output token limit used when a request omits `max_tokens`. Defaults to 1024. */
1001
+ defaultOutputTokens?: number;
1002
+ /**
1003
+ * Return a safe upper bound for the complete provider input.
1004
+ * Include system, chat framing, retained history, tools, harness, and workspace context.
1005
+ */
1006
+ inputTokenBound?: (input: {
1007
+ agent: AgentMeta;
1008
+ messages: ChatMessage[];
1009
+ }) => number;
1010
+ /** Hidden provider spend limits included in the pre-execution payment quote. */
1011
+ executionBudget?: {
1012
+ maxReasoningTokens?: number;
1013
+ maxToolTokens?: number;
1014
+ maxToolCalls?: number;
1015
+ maxProviderCostUsd?: number;
1016
+ };
776
1017
  /** Required scope for chat endpoint (default: "chat"). API keys must include this scope. */
777
1018
  requiredScope?: string;
778
1019
  /** Block requests with detected injection patterns (default: false — log only) */
@@ -794,16 +1035,27 @@ interface GatewayConfig {
794
1035
  */
795
1036
  observer?: GatewayObserver;
796
1037
  /**
797
- * A2A protocol configuration. When set, the gateway exposes the A2A
798
- * surface alongside its OpenAI-compatible endpoints:
1038
+ * A2A protocol configuration. The gateway exposes A2A with an in-memory
1039
+ * task store by default. Set this object to provide durable storage or push:
799
1040
  * GET /:slug/.well-known/agent.json — AgentCard discovery
800
1041
  * POST /:slug — JSON-RPC 2.0 endpoint
801
1042
  * methods: message/send, message/stream, tasks/get, tasks/cancel
802
1043
  * Auth + rate-limit + injection-filter + authorization all share the
803
1044
  * same pipeline as the OpenAI-compat path. `taskStore` defaults to
804
1045
  * `InMemoryTaskStore`; swap in D1/postgres/DO for durable deployments.
805
- */
1046
+ */
806
1047
  a2a?: {
1048
+ /**
1049
+ * Authorize reads, cancellation, resubscription, and push configuration
1050
+ * for an existing task. Production control methods fail closed when this
1051
+ * hook is absent; explicit demo mode permits local tests.
1052
+ */
1053
+ authorizeTaskAccess?: (task: Task, context: {
1054
+ method: string;
1055
+ agentSlug: string;
1056
+ authorization: string;
1057
+ paymentSignature: string;
1058
+ }) => Promise<boolean>;
807
1059
  /**
808
1060
  * Where tasks live. Defaults to `InMemoryTaskStore`; swap in
809
1061
  * `SqlTaskStore` (D1, postgres, sqlite, libSQL) for durability across
@@ -822,8 +1074,8 @@ interface GatewayConfig {
822
1074
  * Shared HMAC secret used to sign webhook deliveries (`X-A2A-Signature:
823
1075
  * sha256=<hex>`). The consumer's webhook verifies the body against this
824
1076
  * secret to confirm the call originated from this gateway. Required when
825
- * `pushStore` is set; without it, deliveries fire unsigned and a
826
- * malicious party that knows the webhook URL can forge deliveries.
1077
+ * `pushStore` is set in production. Explicit demo mode may omit it for
1078
+ * local tests; production deliveries never run unsigned.
827
1079
  */
828
1080
  webhookSecret?: string;
829
1081
  /**
@@ -831,6 +1083,11 @@ interface GatewayConfig {
831
1083
  * `fetch`. Override for tests or to wire a queue-backed sender.
832
1084
  */
833
1085
  pushFetcher?: typeof fetch;
1086
+ /**
1087
+ * DNS-aware policy for push destinations. Required when production
1088
+ * push delivery is enabled so private DNS names cannot receive task data.
1089
+ */
1090
+ pushUrlValidator?: (url: URL) => boolean | Promise<boolean>;
834
1091
  };
835
1092
  }
836
1093
  interface ChatMessage {
@@ -859,4 +1116,4 @@ interface ChatCompletionChunk {
859
1116
  }>;
860
1117
  }
861
1118
 
862
- export { type TaskStatusUpdateEvent as $, type ApiKeyInfo as A, type PushNotificationAuthentication as B, type ChatMessage as C, type D1DatabaseLike as D, type PushNotificationConfig as E, type FilePart as F, type GatewayConfig as G, type PushNotificationStore as H, InMemoryPushNotificationStore as I, type JSONRPCErrorResponse as J, type SandboxStreamEvent as K, type SqlAdapter as L, type MppConfig as M, SqlPushNotificationStore as N, SqlTaskStore as O, type Part as P, type StreamingEvent as Q, type RequestContext as R, type SandboxBox as S, type Task as T, type TaskArtifactUpdateEvent as U, type TaskIdParams as V, type TaskPushNotificationConfig as W, type X402Config as X, type TaskPushNotificationConfigGetParams as Y, type TaskState as Z, type TaskStatus as _, A2A_ERROR_CODES as a, type TaskStore as a0, type TextPart as a1, d1ToSqlAdapter as a2, deliverPushNotifications as a3, generateRequestId as a4, type AgentCapabilities as b, type AgentCard as c, type AgentCardAuthentication as d, type AgentMeta as e, type AgentProvider as f, type AgentSkill as g, type Artifact as h, type AuthFailureReason as i, type ChatCompletionChunk as j, type ChatCompletionRequest as k, CompositeObserver as l, ConsoleObserver as m, type D1StmtLike as n, type DataPart as o, type GatewayObserver as p, type GatewayUsageEvent as q, InMemoryTaskStore as r, type JSONRPCRequest as s, type JSONRPCResponse as t, type JSONRPCSuccessResponse as u, type Message as v, type MessageSendParams as w, type PaymentMethod as x, type PaymentResult as y, type PushDeliveryResult as z };
1119
+ export { type PaymentRecoveryTarget as $, type ApiKeyInfo as A, type MppChargeOperation as B, type ChatMessage as C, type D1DatabaseLike as D, type MppChargeOperationState as E, type FilePart as F, type GatewayConfig as G, type MppChargeRecoveryResult as H, InMemoryPushNotificationStore as I, type JSONRPCErrorResponse as J, type MppChargeRequest as K, PAYMENT_PROTOCOL_VERSION as L, type MppAuthenticatedCredential as M, PAYMENT_RECOVERY_VERSION as N, type Part as O, type PaymentOperationRecoveryResult as P, type PaymentAuthorizationContext as Q, type PaymentOperation as R, type SqlAdapter as S, type PaymentOperationNotFound as T, type PaymentOperationState as U, type PaymentOperations as V, type PaymentRecoveryAttribution as W, type X402Config as X, type PaymentRecoveryConfig as Y, PaymentRecoveryFenceError as Z, type PaymentRecoveryState as _, type PaymentRecoveryRecord as a, type PaymentResult as a0, type PaymentSettlementInput as a1, type PushDeliveryResult as a2, type PushNotificationAuthentication as a3, type PushNotificationConfig as a4, type PushNotificationDeliveryOptions as a5, type PushNotificationStore as a6, type SandboxBox as a7, type SandboxStreamEvent as a8, SqlPushNotificationStore as a9, SqlTaskStore as aa, type StreamingEvent as ab, type Task as ac, type TaskArtifactUpdateEvent as ad, type TaskIdParams as ae, type TaskPushNotificationConfig as af, type TaskPushNotificationConfigGetParams as ag, type TaskState as ah, type TaskStatus as ai, type TaskStatusUpdateEvent as aj, type TaskStore as ak, type TextPart as al, d1ToSqlAdapter as am, deliverDemoPushNotifications as an, deliverPushNotifications as ao, mppPaymentOperationId as ap, validatePushNotificationUrl as aq, type MppConfig as b, type PaymentRecoveryStore as c, A2A_ERROR_CODES as d, type AgentCapabilities as e, type AgentCard as f, type AgentCardAuthentication as g, type AgentMeta as h, type AgentProvider as i, type AgentSkill as j, type Artifact as k, type ChatCompletionChunk as l, type ChatCompletionRequest as m, type D1StmtLike as n, type DataPart as o, InMemoryTaskStore as p, type JSONRPCRequest as q, type JSONRPCResponse as r, type JSONRPCSuccessResponse as s, MPP_CHARGE_PROTOCOL_VERSION as t, MemoryPaymentOperations as u, type MemoryPaymentOperationsOptions as v, MemoryPaymentRecoveryStore as w, type Message as x, type MessageSendParams as y, type MppChargeLifecycle as z };