@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
@@ -0,0 +1,81 @@
1
+ import type { MppChargeOperation } from './mpp-payment'
2
+ import type { NonceStore } from './nonce-store'
3
+ import type { GatewayObserver } from './observer'
4
+ import type { RateLimitStore } from './rate-limit'
5
+ import type { PaymentOperation } from './payment-operations'
6
+ import type { PaymentSettlementBasis } from './payment-recovery'
7
+ import type {
8
+ AgentMeta,
9
+ ApiKeyInfo,
10
+ PaymentMethod,
11
+ SandboxExecutionBudget,
12
+ SandboxUsageReceipt,
13
+ } from './types'
14
+
15
+ /** Long-lived state shared by all handlers created for one gateway. */
16
+ export interface GatewayState {
17
+ rateLimitStore: RateLimitStore
18
+ nonceStore: NonceStore
19
+ globalRateLimit: { limit: number; windowSeconds: number }
20
+ requiredScope: string
21
+ maxLen: number
22
+ maxOutputTokens: number
23
+ defaultOutputTokens: number
24
+ maxReasoningTokens: number
25
+ maxToolTokens: number
26
+ maxToolCalls: number
27
+ maxProviderCostUsd?: number
28
+ obs?: GatewayObserver
29
+ }
30
+
31
+ /** Successful output from the request authorization pipeline. */
32
+ export interface AuthorizedRequest {
33
+ agent: AgentMeta
34
+ consumerId: string
35
+ paymentMethod: PaymentMethod
36
+ keyInfo: ApiKeyInfo | null
37
+ userMessage: string
38
+ rateLimitRemaining: number | undefined
39
+ requestId: string
40
+ startMs: number
41
+ maxOutputTokens: number
42
+ executionBudget: SandboxExecutionBudget
43
+ requiredPaymentAmount: bigint
44
+ paymentPayload: Record<string, unknown> | null
45
+ paymentNonceKey?: string
46
+ mppMethod?: string
47
+ /** Live generic MPP credential. Never write it to the recovery store. */
48
+ mppCredential?: string
49
+ /** Stable method identity. Persist only its digest. */
50
+ mppPaymentIdentity?: string
51
+ mppChargeOperation?: MppChargeOperation
52
+ paymentOperation?: PaymentOperation
53
+ paymentOperationAcquired?: boolean
54
+ paymentRecoveryId?: string
55
+ /** Unique ownership fence for live or recovery transitions. */
56
+ paymentRecoveryFence?: string
57
+ }
58
+
59
+ export interface PaymentClaimHooks {
60
+ /** Persist the recovery identity before the provider can mutate payment state. */
61
+ onRecoveryPrepared?: (recoveryId: string) => Promise<void>
62
+ }
63
+
64
+ export type A2ADispatchEvent =
65
+ | { kind: 'text'; delta: string }
66
+ | { kind: 'input-required'; prompt?: string }
67
+ | { kind: 'activity' }
68
+ | { kind: 'usage'; usage: SandboxUsageReceipt }
69
+
70
+ export interface SettleAndRecordOptions {
71
+ /** Skip attribution after a durable finalization marker confirms it ran. */
72
+ usageAlreadyRecorded?: boolean
73
+ /** Skip provider settlement after an authoritative read found it settled. */
74
+ paymentAlreadySettled?: boolean
75
+ /** Persist the recovery marker after attribution succeeds. */
76
+ onUsageRecorded?: () => Promise<void>
77
+ /** Recovery uses the original quoted ceiling when no receipt arrives. */
78
+ settlementBasis?: PaymentSettlementBasis
79
+ /** Exact base-unit charge selected by the recovery policy. */
80
+ paymentAmount?: bigint
81
+ }
package/src/dispatch.ts CHANGED
@@ -1,465 +1,38 @@
1
1
  /**
2
- * Shared inner pipeline used by every wire-format the gateway exposes
3
- * (OpenAI-compatible chat completions, A2A JSON-RPC). Each handler parses its
4
- * own protocol's request body into a canonical `messages[]` form + headers,
5
- * then calls into here for auth → rate-limit → injection filter →
6
- * authorize → sandbox stream → settle. Keeping the pipeline single-sourced
7
- * means every protocol surface gets the same security and billing guarantees
8
- * for free; bugs fixed here fix every wrapper.
2
+ * Stable dispatch surface shared by the OpenAI-compatible and A2A handlers.
3
+ * Each implementation lives in the module that owns its state transitions.
9
4
  */
10
5
 
11
- import type { Context } from 'hono'
12
-
13
- import { filterConsumerMessagesStrict, redactSystemPromptFromOutput } from './filter'
14
- import { type GatewayObserver, type RequestContext, generateRequestId } from './observer'
15
- import { type RateLimitStore, checkRateLimit } from './rate-limit'
16
- import type { NonceStore } from './nonce-store'
17
- import type {
18
- AgentMeta,
19
- ApiKeyInfo,
20
- ChatMessage,
21
- GatewayConfig,
22
- PaymentMethod,
23
- } from './types'
24
- import { defaultVerifyApiKey, verifyMpp, verifyX402 } from './verify'
25
-
26
- /** Single bundle of long-lived gateway state shared across all handlers in one createAgentGateway call. */
27
- export interface GatewayState {
28
- rateLimitStore: RateLimitStore
29
- nonceStore: NonceStore
30
- globalRateLimit: { limit: number; windowSeconds: number }
31
- requiredScope: string
32
- maxLen: number
33
- obs?: GatewayObserver
34
- }
35
-
36
- /** Returned by {@link authenticateAndGuard} on the success path. */
37
- export interface AuthorizedRequest {
38
- agent: AgentMeta
39
- consumerId: string
40
- paymentMethod: PaymentMethod
41
- keyInfo: ApiKeyInfo | null
42
- userMessage: string
43
- rateLimitRemaining: number | undefined
44
- requestId: string
45
- startMs: number
46
- }
47
-
48
- /**
49
- * Resolve the agent, then run the full pre-dispatch pipeline: payment +
50
- * rate-limit + injection filter + user-message extraction + optional
51
- * `authorizeConsumer` hook. Returns the success record on the happy path
52
- * or a fully-formed `Response` (402/404/429/400/403) on any short-circuit.
53
- *
54
- * Body parsing is the caller's responsibility — different wire formats
55
- * (OpenAI chat completions vs A2A JSON-RPC) have different envelopes; both
56
- * still ultimately produce a `ChatMessage[]`.
57
- */
58
- export async function authenticateAndGuard(
59
- c: Context,
60
- slug: string,
61
- messages: ChatMessage[],
62
- config: GatewayConfig,
63
- state: GatewayState,
64
- ): Promise<AuthorizedRequest | Response> {
65
- const startMs = Date.now()
66
- const requestId = generateRequestId()
67
- const ctx: RequestContext = { requestId, agentSlug: slug, startMs }
68
- await state.obs?.onRequestStart?.(ctx)
69
-
70
- const agent = await config.resolveAgent(slug)
71
- if (!agent) {
72
- return c.json({ error: { message: 'Agent not found', type: 'not_found' } }, 404)
73
- }
74
- if (!messages?.length) {
75
- return c.json(
76
- { error: { message: 'messages array required', type: 'invalid_request' } },
77
- 400,
78
- )
79
- }
80
-
81
- // Payment / auth.
82
- const spendAuthHeader = c.req.header('X-Payment-Signature')
83
- const authHeader = c.req.header('Authorization') ?? ''
84
- let consumerId: string | null = null
85
- let paymentMethod: PaymentMethod = 'none'
86
- let keyInfo: ApiKeyInfo | null = null
87
-
88
- if (spendAuthHeader) {
89
- const signer = await verifyX402(spendAuthHeader, config.x402, state.nonceStore)
90
- if (!signer) {
91
- await state.obs?.onAuthFailure?.(ctx, {
92
- method: 'x402',
93
- code: 'invalid_spend_auth',
94
- httpStatus: 402,
95
- })
96
- return c.json(
97
- {
98
- error: {
99
- message: 'Invalid X-Payment-Signature',
100
- type: 'authentication_error',
101
- code: 'invalid_spend_auth',
102
- },
103
- },
104
- {
105
- status: 402,
106
- headers: { 'X-Payment-Required': 'spendauth', 'X-Request-Id': requestId },
107
- },
108
- )
109
- }
110
- consumerId = signer
111
- paymentMethod = 'x402'
112
- } else if (config.mpp && authHeader.toLowerCase().startsWith('payment ')) {
113
- const signer = await verifyMpp(authHeader, config.mpp, config.x402)
114
- if (!signer) {
115
- const realm = config.mpp.realm
116
- const method = config.mpp.method ?? 'blueprintevm'
117
- await state.obs?.onAuthFailure?.(ctx, {
118
- method: 'mpp',
119
- code: 'invalid_mpp_credential',
120
- httpStatus: 401,
121
- })
122
- return c.json(
123
- {
124
- error: {
125
- message: 'Invalid Payment credential',
126
- type: 'authentication_error',
127
- code: 'invalid_mpp_credential',
128
- },
129
- },
130
- {
131
- status: 401,
132
- headers: {
133
- 'WWW-Authenticate': `Payment realm="${realm}", method="${method}"`,
134
- 'X-Request-Id': requestId,
135
- },
136
- },
137
- )
138
- }
139
- consumerId = signer
140
- paymentMethod = 'mpp'
141
- } else if (authHeader.startsWith('Bearer ')) {
142
- const verify = config.verifyApiKey ?? defaultVerifyApiKey
143
- const key = await verify(authHeader)
144
- if (!key) {
145
- await state.obs?.onAuthFailure?.(ctx, {
146
- method: 'apikey',
147
- code: 'invalid_api_key',
148
- httpStatus: 401,
149
- })
150
- return c.json(
151
- { error: { message: 'Invalid API key', type: 'authentication_error' } },
152
- { status: 401, headers: { 'X-Request-Id': requestId } },
153
- )
154
- }
155
- if (key.scopes && key.scopes.length > 0 && !key.scopes.includes(state.requiredScope)) {
156
- await state.obs?.onAuthFailure?.(ctx, {
157
- method: 'apikey',
158
- code: 'insufficient_scope',
159
- httpStatus: 403,
160
- })
161
- return c.json(
162
- {
163
- error: {
164
- message: `API key missing required scope: ${state.requiredScope}`,
165
- type: 'forbidden',
166
- code: 'insufficient_scope',
167
- },
168
- },
169
- { status: 403, headers: { 'X-Request-Id': requestId } },
170
- )
171
- }
172
- consumerId = key.consumerId
173
- paymentMethod = 'apikey'
174
- keyInfo = key
175
- } else {
176
- await state.obs?.onAuthFailure?.(ctx, {
177
- method: 'none',
178
- code: 'payment_required',
179
- httpStatus: 402,
180
- })
181
- const methods: string[] = ['x402']
182
- if (config.mpp) methods.push('mpp')
183
- methods.push('api_key')
184
- const headers: Record<string, string> = {
185
- 'X-Payment-Required': methods.join(', '),
186
- 'X-Request-Id': requestId,
187
- }
188
- if (config.mpp) {
189
- headers['WWW-Authenticate'] =
190
- `Payment realm="${config.mpp.realm}", method="${config.mpp.method ?? 'blueprintevm'}"`
191
- }
192
- return c.json(
193
- {
194
- error: {
195
- message: 'Payment required',
196
- type: 'payment_required',
197
- payment_methods: methods,
198
- x402: {
199
- operator: config.x402.operatorAddress,
200
- chain_id: config.x402.chainId,
201
- credits_address: config.x402.creditsAddress,
202
- estimated_amount_per_request: '20000',
203
- },
204
- ...(config.mpp
205
- ? { mpp: { realm: config.mpp.realm, method: config.mpp.method ?? 'blueprintevm' } }
206
- : {}),
207
- api_key: {
208
- purchase_url: config.baseUrl
209
- ? `${config.baseUrl}/agents/${slug}/api-keys`
210
- : undefined,
211
- },
212
- },
213
- },
214
- { status: 402, headers },
215
- )
216
- }
217
-
218
- await state.obs?.onPaymentVerified?.(ctx, {
219
- method: paymentMethod,
220
- consumerId: consumerId,
221
- keyId: keyInfo?.keyId,
222
- })
223
-
224
- // Rate limit.
225
- const effectiveRateLimit = keyInfo?.rateLimitPerMinute
226
- ? { limit: keyInfo.rateLimitPerMinute, windowSeconds: 60 }
227
- : state.globalRateLimit
228
- const rl = await checkRateLimit(consumerId, effectiveRateLimit, state.rateLimitStore)
229
- if (!rl.allowed) {
230
- await state.obs?.onRateLimited?.(ctx, {
231
- consumerId: consumerId,
232
- retryAfterSeconds: rl.retryAfterSeconds ?? 60,
233
- })
234
- return c.json(
235
- {
236
- error: {
237
- message: 'Rate limit exceeded',
238
- type: 'rate_limit_error',
239
- retry_after: rl.retryAfterSeconds,
240
- },
241
- },
242
- {
243
- status: 429,
244
- headers: {
245
- 'Retry-After': String(rl.retryAfterSeconds ?? 60),
246
- 'X-Request-Id': requestId,
247
- },
248
- },
249
- )
250
- }
251
-
252
- // Filter consumer messages — strip consumer-side system, length-cap, injection scan.
253
- const { messages: filtered, injectionWarnings } = filterConsumerMessagesStrict(
254
- messages,
255
- state.maxLen,
256
- )
257
- if (injectionWarnings.length > 0) {
258
- await state.obs?.onInjectionDetected?.(ctx, {
259
- consumerId: consumerId,
260
- patterns: injectionWarnings,
261
- blocked: !!config.blockInjection,
262
- })
263
- if (config.blockInjection) {
264
- return c.json(
265
- {
266
- error: {
267
- message: 'Request rejected: potential prompt injection detected',
268
- type: 'content_policy_violation',
269
- },
270
- },
271
- { status: 400, headers: { 'X-Request-Id': requestId } },
272
- )
273
- }
274
- }
275
-
276
- const userMessage = filtered
277
- .filter((m) => m.role === 'user')
278
- .map((m) => m.content)
279
- .join('\n\n')
280
- if (!userMessage) {
281
- return c.json(
282
- { error: { message: 'No user message provided', type: 'invalid_request' } },
283
- 400,
284
- )
285
- }
286
-
287
- if (config.authorizeConsumer) {
288
- const authz = await config.authorizeConsumer(agent, {
289
- method: paymentMethod,
290
- consumerId: consumerId,
291
- keyId: keyInfo?.keyId,
292
- requestId,
293
- })
294
- if (!authz.allow) {
295
- return c.json(
296
- {
297
- error: {
298
- message: authz.reason,
299
- type: 'authorization_denied',
300
- code: authz.code,
301
- },
302
- },
303
- { status: 403, headers: { 'X-Request-Id': requestId } },
304
- )
305
- }
306
- }
307
-
308
- return {
309
- agent,
310
- consumerId,
311
- paymentMethod,
312
- keyInfo,
313
- userMessage,
314
- rateLimitRemaining: rl.remaining,
315
- requestId,
316
- startMs,
317
- }
318
- }
319
-
320
- /**
321
- * Yield the inner sandbox's response as text deltas, applying the
322
- * system-prompt redaction filter on each delta so leakage of the agent's
323
- * system prompt back through the model's output is suppressed identically
324
- * whether the caller is on the OpenAI-compat path or A2A.
325
- *
326
- * Aborts when `signal` fires (used by A2A `tasks/cancel`).
327
- */
328
- export async function* dispatchSandboxStream(
329
- agent: AgentMeta,
330
- userMessage: string,
331
- consumerId: string,
332
- config: GatewayConfig,
333
- signal?: AbortSignal,
334
- sessionId?: string,
335
- ): AsyncIterable<string> {
336
- for await (const event of dispatchSandboxStreamRich(
337
- agent,
338
- userMessage,
339
- consumerId,
340
- config,
341
- signal,
342
- sessionId,
343
- )) {
344
- if (event.kind === 'text') yield event.delta
345
- }
346
- }
347
-
348
- /**
349
- * A2A-shaped dispatch event. Distinguishes text deltas from sandbox-signalled
350
- * pause-for-input events. The A2A handler uses this richer variant so it can
351
- * emit `input-required` status updates; the OpenAI-compat path consumes the
352
- * text-only `dispatchSandboxStream` adapter above.
353
- */
354
- export type A2ADispatchEvent =
355
- | { kind: 'text'; delta: string }
356
- | { kind: 'input-required'; prompt?: string }
357
-
358
- /**
359
- * Like `dispatchSandboxStream` but yields a discriminated union so callers can
360
- * react to `input-required` signals from the sandbox. The sandbox opts in by
361
- * emitting `{ type: 'input-required', data: { inputRequired: { prompt? } } }`
362
- * (or by setting `data.inputRequired` on any event); sandboxes that don't
363
- * emit such events see identical behavior.
364
- *
365
- * `sessionId` defaults to `consumer:<id>` matching the existing single-turn
366
- * path; multi-turn continuations pass an explicit `taskId` so the sandbox can
367
- * keep per-task conversation memory.
368
- */
369
- export async function* dispatchSandboxStreamRich(
370
- agent: AgentMeta,
371
- userMessage: string,
372
- consumerId: string,
373
- config: GatewayConfig,
374
- signal?: AbortSignal,
375
- sessionId?: string,
376
- ): AsyncIterable<A2ADispatchEvent> {
377
- const box = await config.getSandbox(agent)
378
- const promptStream = box.streamPrompt(userMessage, {
379
- sessionId: sessionId ?? `consumer:${consumerId}`,
380
- systemPrompt: agent.systemPrompt,
381
- })
382
- for await (const event of promptStream) {
383
- if (signal?.aborted) return
384
- if (
385
- event.type === 'message.part.updated' &&
386
- event.data?.part?.type === 'text' &&
387
- event.data.delta
388
- ) {
389
- yield {
390
- kind: 'text',
391
- delta: redactSystemPromptFromOutput(event.data.delta, agent.systemPrompt),
392
- }
393
- continue
394
- }
395
- if (event.type === 'input-required' || event.data?.inputRequired) {
396
- yield { kind: 'input-required', prompt: event.data?.inputRequired?.prompt }
397
- // Terminal for the sandbox stream — sandbox SHOULD stop emitting until
398
- // the gateway dispatches a continuation message with the new user input.
399
- return
400
- }
401
- }
402
- }
403
-
404
- /**
405
- * Record usage event + settle payment + invoke the observer. Both wire
406
- * formats call this once their stream has drained, so settlement happens
407
- * exactly once per request regardless of protocol.
408
- */
409
- export async function settleAndRecord(
410
- agent: AgentMeta,
411
- authz: AuthorizedRequest,
412
- inputTokens: number,
413
- outputTokens: number,
414
- config: GatewayConfig,
415
- obs: GatewayObserver | undefined,
416
- ): Promise<void> {
417
- const totalCost = (inputTokens + outputTokens) * agent.pricePerTokenUsd
418
- const ownerEarned = totalCost * (1 - agent.platformFeePercent)
419
- const platformFee = totalCost * agent.platformFeePercent
420
- const usageEvent = {
421
- requestId: authz.requestId,
422
- agentId: agent.id,
423
- agentSlug: agent.slug,
424
- consumerId: authz.consumerId,
425
- paymentMethod: authz.paymentMethod,
426
- inputTokens,
427
- outputTokens,
428
- totalCostUsd: totalCost,
429
- ownerEarnedUsd: ownerEarned,
430
- platformFeeUsd: platformFee,
431
- durationMs: Date.now() - authz.startMs,
432
- }
433
- await config.recordUsage(usageEvent)
434
- const ctx: RequestContext = {
435
- requestId: authz.requestId,
436
- agentSlug: agent.slug,
437
- startMs: authz.startMs,
438
- }
439
- await obs?.onRequestComplete?.(ctx, usageEvent)
440
- if (config.settlePayment) {
441
- await config
442
- .settlePayment(
443
- {
444
- method: authz.paymentMethod,
445
- consumerId: authz.consumerId,
446
- requestId: authz.requestId,
447
- },
448
- totalCost,
449
- )
450
- .catch(async (err) => {
451
- const msg = err instanceof Error ? err.message : String(err)
452
- console.error(`[agent-gateway] settlement failed for ${authz.consumerId}: ${msg}`)
453
- await obs?.onSettlementError?.(ctx, {
454
- consumerId: authz.consumerId,
455
- method: authz.paymentMethod,
456
- errorMessage: msg,
457
- })
458
- })
459
- }
460
- }
461
-
462
- /** Token estimate matching the existing chat-completions handler (4 chars ≈ 1 token). */
463
- export function estimateTokens(text: string): number {
464
- return Math.ceil(text.length / 4)
465
- }
6
+ export type {
7
+ A2ADispatchEvent,
8
+ AuthorizedRequest,
9
+ GatewayState,
10
+ PaymentClaimHooks,
11
+ SettleAndRecordOptions,
12
+ } from './dispatch-types'
13
+
14
+ export {
15
+ estimateBillableInputTokens,
16
+ estimateTokens,
17
+ maximumBillableInputTokens,
18
+ requiredX402Amount,
19
+ } from './dispatch-pricing'
20
+
21
+ export { authenticateAndGuard } from './dispatch-authorization'
22
+
23
+ export {
24
+ beginPaymentExecution,
25
+ claimPayment,
26
+ markPaymentExecutionStarted,
27
+ reclaimPayment,
28
+ releasePayment,
29
+ releasePaymentAfterFailure,
30
+ renewPaymentExecution,
31
+ } from './dispatch-payment'
32
+
33
+ export {
34
+ dispatchSandboxStream,
35
+ dispatchSandboxStreamRich,
36
+ } from './dispatch-sandbox'
37
+
38
+ export { settleAndRecord } from './dispatch-settlement'
package/src/index.ts CHANGED
@@ -1,5 +1,58 @@
1
1
  export { createAgentGateway } from './middleware'
2
- export { verifyX402, verifyMpp, defaultVerifyApiKey } from './verify'
2
+ export { reclaimPayment } from './dispatch'
3
+ export {
4
+ recoverPayment,
5
+ recoverPayments,
6
+ type RecoverPaymentOptions,
7
+ type RecoverPaymentsOptions,
8
+ type PaymentRecoveryRun,
9
+ } from './payment-recovery-worker'
10
+ export {
11
+ verifyX402,
12
+ verifyMpp,
13
+ verifyMppCredential,
14
+ defaultVerifyApiKey,
15
+ isApiKeyAuthEnabled,
16
+ isMppAuthEnabled,
17
+ mppReplayNonceKey,
18
+ mppPaymentCredential,
19
+ type VerifiedMppCredential,
20
+ } from './verify'
21
+ export {
22
+ MPP_CHARGE_PROTOCOL_VERSION,
23
+ mppPaymentOperationId,
24
+ type MppAuthenticatedCredential,
25
+ type MppChargeLifecycle,
26
+ type MppChargeOperation,
27
+ type MppChargeOperationState,
28
+ type MppChargeRecoveryResult,
29
+ type MppChargeRequest,
30
+ } from './mpp-payment'
31
+ export {
32
+ PAYMENT_RECOVERY_VERSION,
33
+ MemoryPaymentRecoveryStore,
34
+ PaymentRecoveryFenceError,
35
+ type PaymentRecoveryAttribution,
36
+ type PaymentRecoveryConfig,
37
+ type PaymentRecoveryRecord,
38
+ type PaymentRecoveryState,
39
+ type PaymentRecoveryStore,
40
+ type PaymentRecoveryTarget,
41
+ type PaymentSettlementBasis,
42
+ } from './payment-recovery'
43
+ export { SqlPaymentRecoveryStore } from './payment-recovery-sql'
44
+ export {
45
+ PAYMENT_PROTOCOL_VERSION,
46
+ MemoryPaymentOperations,
47
+ type MemoryPaymentOperationsOptions,
48
+ type PaymentAuthorizationContext,
49
+ type PaymentOperation,
50
+ type PaymentOperationNotFound,
51
+ type PaymentOperationRecoveryResult,
52
+ type PaymentOperationState,
53
+ type PaymentOperations,
54
+ type PaymentSettlementInput,
55
+ } from './payment-operations'
3
56
  export {
4
57
  filterConsumerMessages,
5
58
  filterConsumerMessagesStrict,
@@ -25,6 +78,10 @@ export {
25
78
  export {
26
79
  MemoryNonceStore,
27
80
  KvNonceStore,
81
+ isAtomicNonceStore,
82
+ type AtomicKvNonceClaim,
83
+ type AtomicNonceStore,
84
+ type KvNonceStoreOptions,
28
85
  type NonceStore,
29
86
  } from './nonce-store'
30
87
  export {
@@ -50,6 +107,8 @@ export type {
50
107
  PaymentResult,
51
108
  ApiKeyInfo,
52
109
  GatewayUsageEvent,
110
+ SandboxExecutionBudget,
111
+ SandboxUsageReceipt,
53
112
  SandboxStreamEvent,
54
113
  SandboxBox,
55
114
  GatewayConfig,
@@ -60,7 +119,7 @@ export type {
60
119
 
61
120
  // --- A2A protocol surface (Google Agent-to-Agent) ---
62
121
  // Types + task-store adapter. Handlers are wired automatically by
63
- // createAgentGateway when `GatewayConfig.a2a` (or its default) is honored;
122
+ // createAgentGateway with an in-memory store by default;
64
123
  // consumers only import these to BYO a durable TaskStore (D1, postgres, DO)
65
124
  // or to declare richer AgentMeta.skills for the Agent Card.
66
125
  export { InMemoryTaskStore, type TaskStore } from './a2a/task-store'
@@ -72,9 +131,12 @@ export {
72
131
  SqlTaskStore,
73
132
  } from './a2a/task-store-sql'
74
133
  export {
134
+ deliverDemoPushNotifications,
75
135
  deliverPushNotifications,
76
136
  InMemoryPushNotificationStore,
137
+ validatePushNotificationUrl,
77
138
  type PushDeliveryResult,
139
+ type PushNotificationDeliveryOptions,
78
140
  type PushNotificationAuthentication,
79
141
  type PushNotificationConfig,
80
142
  type PushNotificationStore,