@tangle-network/agent-gateway 0.7.1 → 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 (64) hide show
  1. package/README.md +83 -2
  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 +70 -10
  9. package/dist/index.js +303 -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-DEsMmS-X.d.ts → types-BHISsm7D.d.ts} +414 -170
  20. package/dist/types.d.ts +2 -1
  21. package/package.json +1 -1
  22. package/src/a2a/execution-fence.ts +162 -0
  23. package/src/a2a/handler.ts +506 -560
  24. package/src/a2a/message-send-execution.ts +241 -0
  25. package/src/a2a/message-stream-execution.ts +392 -0
  26. package/src/a2a/payment-recovery.ts +431 -0
  27. package/src/a2a/push-config-methods.ts +158 -0
  28. package/src/a2a/push-notifications.ts +172 -22
  29. package/src/a2a/task-cancellation.ts +50 -0
  30. package/src/a2a/task-finalization.ts +451 -0
  31. package/src/a2a/task-lifecycle.ts +54 -0
  32. package/src/a2a/task-methods.ts +163 -0
  33. package/src/a2a/task-push-delivery.ts +119 -0
  34. package/src/a2a/task-recovery.ts +11 -0
  35. package/src/a2a/task-state.ts +99 -0
  36. package/src/a2a/task-store-sql.ts +222 -24
  37. package/src/a2a/task-store.ts +58 -1
  38. package/src/a2a/task-submission-recovery.ts +178 -0
  39. package/src/a2a/types.ts +1 -0
  40. package/src/dispatch-authorization.ts +437 -0
  41. package/src/dispatch-payment-recovery.ts +248 -0
  42. package/src/dispatch-payment.ts +425 -0
  43. package/src/dispatch-pricing.ts +108 -0
  44. package/src/dispatch-sandbox.ts +422 -0
  45. package/src/dispatch-settlement.ts +139 -0
  46. package/src/dispatch-types.ts +81 -0
  47. package/src/dispatch.ts +35 -483
  48. package/src/index.ts +57 -1
  49. package/src/middleware.ts +307 -26
  50. package/src/mpp-payment.ts +117 -0
  51. package/src/nonce-store.ts +122 -20
  52. package/src/observer-types.ts +63 -0
  53. package/src/observer.ts +3 -63
  54. package/src/payment-operations.ts +485 -0
  55. package/src/payment-recovery-sql.ts +108 -0
  56. package/src/payment-recovery-worker.ts +488 -0
  57. package/src/payment-recovery.ts +331 -0
  58. package/src/payment-types.ts +48 -0
  59. package/src/types.ts +144 -46
  60. package/src/verify.ts +233 -71
  61. package/dist/chunk-M7ZJAK4K.js +0 -53
  62. package/dist/chunk-M7ZJAK4K.js.map +0 -1
  63. package/dist/chunk-Q4YAIEZY.js +0 -1763
  64. package/dist/chunk-Q4YAIEZY.js.map +0 -1
@@ -3,16 +3,33 @@
3
3
  * Tracks seen nonces to prevent the same payment from being used twice.
4
4
  */
5
5
  interface NonceStore {
6
- /** Check if nonce has been seen. Returns true if already used (reject). */
6
+ /** Check if nonce has been seen. This method never grants ownership. */
7
7
  hasSeen(nonce: string): Promise<boolean>;
8
- /** Mark nonce as used. TTL = how long to remember it (seconds). */
9
- markSeen(nonce: string, ttlSeconds: number): Promise<void>;
8
+ /**
9
+ * Atomically claim a nonce. An owner id makes a retry by the same payment
10
+ * operation idempotent. This is optional only for the 0.7.1 check-and-mark
11
+ * compatibility contract; durable owner claims require this method.
12
+ */
13
+ claim?(nonce: string, ttlSeconds: number, ownerId?: string): Promise<boolean>;
14
+ /** @deprecated Use claim() for atomic ownership in new stores. */
15
+ markSeen?(nonce: string, ttlSeconds: number): Promise<void>;
16
+ }
17
+ interface AtomicNonceStore extends NonceStore {
18
+ claim(nonce: string, ttlSeconds: number, ownerId?: string): Promise<boolean>;
10
19
  }
20
+ /**
21
+ * Return the seconds for which a signed nonce must remain stored.
22
+ *
23
+ * The signed expiry is the replay boundary. A fixed one-hour cap would allow
24
+ * a still-valid authorization to replay after the nonce entry expires.
25
+ */
26
+ declare function nonceTtlSeconds(expiry: bigint, nowSeconds?: number): number | undefined;
11
27
  /** In-memory nonce store with automatic eviction. Use in tests or single-worker deploys. */
12
28
  declare class MemoryNonceStore implements NonceStore {
13
29
  private seen;
14
30
  private lastEviction;
15
31
  hasSeen(nonce: string): Promise<boolean>;
32
+ claim(nonce: string, ttlSeconds: number, ownerId?: string): Promise<boolean>;
16
33
  markSeen(nonce: string, ttlSeconds: number): Promise<void>;
17
34
  private evictExpired;
18
35
  }
@@ -28,20 +45,31 @@ interface KVNamespace {
28
45
  put(key: string, value: string, options?: {
29
46
  expirationTtl?: number;
30
47
  }): Promise<void>;
48
+ /** Optional linearizable create-if-absent extension. Cloudflare KV does not provide it. */
49
+ putIfAbsent?(key: string, value: string, options?: {
50
+ expirationTtl?: number;
51
+ }): Promise<boolean>;
31
52
  delete(key: string): Promise<void>;
32
53
  }
54
+ /** Atomic claim supplied by D1, a Durable Object, or another linearizable store. */
55
+ type AtomicKvNonceClaim = (key: string, ttlSeconds: number, ownerId?: string) => Promise<boolean>;
56
+ interface KvNonceStoreOptions {
57
+ /**
58
+ * Claim the fully namespaced key atomically.
59
+ * The callback must make same-owner retries idempotent.
60
+ */
61
+ atomicClaim?: AtomicKvNonceClaim;
62
+ }
33
63
  /**
34
64
  * KV-backed NonceStore for distributed Cloudflare Workers deployments.
35
65
  *
36
66
  * Why this exists: MemoryNonceStore works on a single worker instance, but
37
67
  * Cloudflare routes requests across multiple isolates. Without shared state,
38
68
  * an attacker could retry a replayed nonce against a different isolate and
39
- * have it accepted. This implementation uses Workers KV with native TTL so
40
- * the nonce automatically expires at payment-expiry time.
41
- *
42
- * TTL precision: KV is eventually consistent (propagation ~60s). For x402
43
- * with 10-minute expiry windows this is fine — by the time KV propagates,
44
- * the payment itself would be expired anyway.
69
+ * have it accepted. Cloudflare KV has no conditional write, so a plain KV
70
+ * binding is not an atomic payment store. Supply `atomicClaim` from D1,
71
+ * Durable Objects, or another linearizable service before using this store
72
+ * for paid requests.
45
73
  *
46
74
  * Usage:
47
75
  * const nonceStore = new KvNonceStore(env.NONCE_KV, 'x402')
@@ -51,12 +79,20 @@ declare class KvNonceStore implements NonceStore {
51
79
  private readonly kv;
52
80
  /** Key prefix to namespace within a shared KV (default: "nonce"). */
53
81
  private readonly prefix;
82
+ private readonly atomicClaim?;
54
83
  constructor(kv: KVNamespace,
55
84
  /** Key prefix to namespace within a shared KV (default: "nonce"). */
56
- prefix?: string);
85
+ prefix?: string, options?: KvNonceStoreOptions);
57
86
  hasSeen(nonce: string): Promise<boolean>;
58
87
  markSeen(nonce: string, ttlSeconds: number): Promise<void>;
88
+ claim(nonce: string, ttlSeconds: number, ownerId?: string): Promise<boolean>;
89
+ /** Used by gateway validation to reject plain, non-atomic KV bindings. */
90
+ hasAtomicClaim(): boolean;
59
91
  private key;
60
92
  }
93
+ /** Claim through the one atomic contract used by every payment path. */
94
+ declare function claimStoredNonce(store: NonceStore, nonce: string, ttlSeconds: number, ownerId?: string): Promise<boolean>;
95
+ /** Durable payment paths must use a store with a single atomic claim operation. */
96
+ declare function isAtomicNonceStore(store: NonceStore): store is AtomicNonceStore;
61
97
 
62
- export { type KVNamespace, KvNonceStore, MemoryNonceStore, type NonceStore };
98
+ export { type AtomicKvNonceClaim, type AtomicNonceStore, type KVNamespace, KvNonceStore, type KvNonceStoreOptions, MemoryNonceStore, type NonceStore, claimStoredNonce, isAtomicNonceStore, nonceTtlSeconds };
@@ -1,9 +1,15 @@
1
1
  import {
2
2
  KvNonceStore,
3
- MemoryNonceStore
4
- } from "./chunk-M7ZJAK4K.js";
3
+ MemoryNonceStore,
4
+ claimStoredNonce,
5
+ isAtomicNonceStore,
6
+ nonceTtlSeconds
7
+ } from "./chunk-J5SDVHOL.js";
5
8
  export {
6
9
  KvNonceStore,
7
- MemoryNonceStore
10
+ MemoryNonceStore,
11
+ claimStoredNonce,
12
+ isAtomicNonceStore,
13
+ nonceTtlSeconds
8
14
  };
9
15
  //# sourceMappingURL=nonce-store.js.map
@@ -0,0 +1,95 @@
1
+ type PaymentMethod = 'x402' | 'mpp' | 'apikey' | 'none';
2
+ interface SandboxExecutionBudget {
3
+ maxInputTokens: number;
4
+ maxOutputTokens: number;
5
+ maxReasoningTokens: number;
6
+ maxToolTokens: number;
7
+ maxToolCalls: number;
8
+ maxProviderCostUsd: number;
9
+ }
10
+ interface SandboxUsageReceipt {
11
+ inputTokens: number;
12
+ outputTokens: number;
13
+ reasoningTokens: number;
14
+ toolTokens: number;
15
+ toolCallCount: number;
16
+ providerCostUsd: number;
17
+ /** True only when the provider/adapter enforced every supplied budget. */
18
+ budgetEnforced: boolean;
19
+ }
20
+ type PaymentSettlementBasis = 'usage-receipt' | 'quoted-ceiling';
21
+ interface GatewayUsageEvent {
22
+ /** Correlates usage, settlement, and observer records for one request. */
23
+ requestId: string;
24
+ agentId: string;
25
+ agentSlug: string;
26
+ consumerId: string;
27
+ paymentMethod: PaymentMethod;
28
+ inputTokens: number;
29
+ outputTokens: number;
30
+ /** Optional in 0.7.2 so 0.7.1 event constructors remain source-compatible. */
31
+ reasoningTokens?: number;
32
+ /** Optional in 0.7.2 so 0.7.1 event constructors remain source-compatible. */
33
+ toolTokens?: number;
34
+ /** Optional in 0.7.2 so 0.7.1 event constructors remain source-compatible. */
35
+ toolCallCount?: number;
36
+ /** Optional in 0.7.2 so 0.7.1 event constructors remain source-compatible. */
37
+ providerCostUsd?: number;
38
+ totalCostUsd: number;
39
+ ownerEarnedUsd: number;
40
+ platformFeeUsd: number;
41
+ durationMs: number;
42
+ /** Exact receipt in normal operation; quoted ceiling only after receipt timeout. */
43
+ settlementBasis?: PaymentSettlementBasis;
44
+ }
45
+
46
+ interface RequestContext {
47
+ requestId: string;
48
+ agentSlug: string;
49
+ startMs: number;
50
+ }
51
+ interface AuthFailureReason {
52
+ method: PaymentMethod;
53
+ code: string;
54
+ httpStatus: number;
55
+ }
56
+ interface GatewayObserver {
57
+ /** Called at the start of every chat completions POST. */
58
+ onRequestStart?: (ctx: RequestContext) => void | Promise<void>;
59
+ /** Called when a payment method has been successfully verified. */
60
+ onPaymentVerified?: (ctx: RequestContext, info: {
61
+ method: PaymentMethod;
62
+ consumerId: string;
63
+ keyId?: string;
64
+ }) => void | Promise<void>;
65
+ /** Called when auth fails — every branch. */
66
+ onAuthFailure?: (ctx: RequestContext, reason: AuthFailureReason) => void | Promise<void>;
67
+ /** Called when a consumer hits the rate limit. */
68
+ onRateLimited?: (ctx: RequestContext, info: {
69
+ consumerId: string;
70
+ retryAfterSeconds: number;
71
+ }) => void | Promise<void>;
72
+ /** Called when the request body exceeds the 64KB limit. */
73
+ onBodyTooLarge?: (ctx: RequestContext, contentLength: number) => void | Promise<void>;
74
+ /** Called when prompt-injection patterns are detected. */
75
+ onInjectionDetected?: (ctx: RequestContext, info: {
76
+ consumerId: string;
77
+ patterns: string[];
78
+ blocked: boolean;
79
+ }) => void | Promise<void>;
80
+ /** Called after a successful stream completes and recordUsage has fired. */
81
+ onRequestComplete?: (ctx: RequestContext, usage: GatewayUsageEvent) => void | Promise<void>;
82
+ /** Called when the sandbox throws. The error message is pre-scrubbed. */
83
+ onStreamError?: (ctx: RequestContext, info: {
84
+ consumerId: string;
85
+ errorMessage: string;
86
+ }) => void | Promise<void>;
87
+ /** Called when settlement fails. Payment already occurred; this is async bookkeeping. */
88
+ onSettlementError?: (ctx: RequestContext, info: {
89
+ consumerId: string;
90
+ method: PaymentMethod;
91
+ errorMessage: string;
92
+ }) => void | Promise<void>;
93
+ }
94
+
95
+ export type { AuthFailureReason as A, GatewayObserver as G, PaymentMethod as P, RequestContext as R, SandboxUsageReceipt as S, GatewayUsageEvent as a, PaymentSettlementBasis as b, SandboxExecutionBudget as c };
@@ -0,0 +1,79 @@
1
+ import { G as GatewayObserver, R as RequestContext, A as AuthFailureReason, a as GatewayUsageEvent, P as PaymentMethod } from './observer-types-A0RtA8uL.js';
2
+
3
+ /**
4
+ * Observability hook surface.
5
+ *
6
+ * Consumers implement GatewayObserver to wire the gateway into their existing
7
+ * telemetry stack (Langfuse, OTEL, structured logs, Prometheus, etc.) without
8
+ * the gateway itself depending on any of those libraries.
9
+ *
10
+ * Every event carries a requestId so downstream metrics can correlate the
11
+ * payment verification, sandbox execution, and settlement for one request.
12
+ * When no observer is configured, the gateway stays silent.
13
+ */
14
+
15
+ /**
16
+ * Structured-log observer. Emits one JSON line per event on the `log` function.
17
+ * Default sink: console.log. Production consumers usually pipe their own
18
+ * structured logger (pino, winston, the cf Logs binding).
19
+ *
20
+ * Usage:
21
+ * new ConsoleObserver(({ level, event, ...rest }) => logger.info({ event, ...rest }))
22
+ */
23
+ declare class ConsoleObserver implements GatewayObserver {
24
+ private readonly log;
25
+ constructor(log?: (entry: Record<string, unknown>) => void);
26
+ private emit;
27
+ onRequestStart(ctx: RequestContext): void;
28
+ onPaymentVerified(ctx: RequestContext, info: {
29
+ method: PaymentMethod;
30
+ consumerId: string;
31
+ keyId?: string;
32
+ }): void;
33
+ onAuthFailure(ctx: RequestContext, reason: AuthFailureReason): void;
34
+ onRateLimited(ctx: RequestContext, info: {
35
+ consumerId: string;
36
+ retryAfterSeconds: number;
37
+ }): void;
38
+ onBodyTooLarge(ctx: RequestContext, contentLength: number): void;
39
+ onInjectionDetected(ctx: RequestContext, info: {
40
+ consumerId: string;
41
+ patterns: string[];
42
+ blocked: boolean;
43
+ }): void;
44
+ onRequestComplete(ctx: RequestContext, usage: GatewayUsageEvent): void;
45
+ onStreamError(ctx: RequestContext, info: {
46
+ consumerId: string;
47
+ errorMessage: string;
48
+ }): void;
49
+ onSettlementError(ctx: RequestContext, info: {
50
+ consumerId: string;
51
+ method: PaymentMethod;
52
+ errorMessage: string;
53
+ }): void;
54
+ }
55
+ /**
56
+ * Compose multiple observers into one. Errors in any individual observer
57
+ * don't break the others (fire-and-forget telemetry).
58
+ */
59
+ declare class CompositeObserver implements GatewayObserver {
60
+ private readonly observers;
61
+ constructor(observers: GatewayObserver[]);
62
+ private fanOut;
63
+ onRequestStart: (ctx: RequestContext) => Promise<void>;
64
+ onPaymentVerified: (ctx: RequestContext, info: Parameters<Required<GatewayObserver>["onPaymentVerified"]>[1]) => Promise<void>;
65
+ onAuthFailure: (ctx: RequestContext, reason: AuthFailureReason) => Promise<void>;
66
+ onRateLimited: (ctx: RequestContext, info: Parameters<Required<GatewayObserver>["onRateLimited"]>[1]) => Promise<void>;
67
+ onBodyTooLarge: (ctx: RequestContext, contentLength: number) => Promise<void>;
68
+ onInjectionDetected: (ctx: RequestContext, info: Parameters<Required<GatewayObserver>["onInjectionDetected"]>[1]) => Promise<void>;
69
+ onRequestComplete: (ctx: RequestContext, usage: GatewayUsageEvent) => Promise<void>;
70
+ onStreamError: (ctx: RequestContext, info: Parameters<Required<GatewayObserver>["onStreamError"]>[1]) => Promise<void>;
71
+ onSettlementError: (ctx: RequestContext, info: Parameters<Required<GatewayObserver>["onSettlementError"]>[1]) => Promise<void>;
72
+ }
73
+ /**
74
+ * Generate a request-id. Crypto-random 16 bytes, hex-encoded with an `req_` prefix.
75
+ * Works in Workers, Node, and browsers — all have globalThis.crypto.
76
+ */
77
+ declare function generateRequestId(): string;
78
+
79
+ export { AuthFailureReason, CompositeObserver, ConsoleObserver, GatewayObserver, RequestContext, generateRequestId };
@@ -0,0 +1,11 @@
1
+ import {
2
+ CompositeObserver,
3
+ ConsoleObserver,
4
+ generateRequestId
5
+ } from "./chunk-GITV7CPT.js";
6
+ export {
7
+ CompositeObserver,
8
+ ConsoleObserver,
9
+ generateRequestId
10
+ };
11
+ //# sourceMappingURL=observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}