@tangle-network/agent-gateway 0.7.1 → 0.8.1

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 +90 -3
  2. package/dist/chunk-C7Z2BRYV.js +5693 -0
  3. package/dist/chunk-C7Z2BRYV.js.map +1 -0
  4. package/dist/chunk-GITV7CPT.js +84 -0
  5. package/dist/chunk-GITV7CPT.js.map +1 -0
  6. package/dist/chunk-J5SDVHOL.js +104 -0
  7. package/dist/chunk-J5SDVHOL.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-oQ58UakD.d.ts} +447 -172
  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 +468 -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 +424 -0
  45. package/src/dispatch-settlement.ts +139 -0
  46. package/src/dispatch-types.ts +84 -0
  47. package/src/dispatch.ts +35 -483
  48. package/src/index.ts +59 -1
  49. package/src/middleware.ts +339 -35
  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 +188 -49
  60. package/src/verify.ts +240 -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
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- export { e as AgentMeta, A as ApiKeyInfo, j as ChatCompletionChunk, k as ChatCompletionRequest, C as ChatMessage, G as GatewayConfig, q as GatewayUsageEvent, M as MppConfig, x as PaymentMethod, y as PaymentResult, S as SandboxBox, K as SandboxStreamEvent, X as X402Config } from './types-DEsMmS-X.js';
1
+ export { h as AgentMeta, k as ApiKeyGatewayConfig, A as ApiKeyInfo, m as ChatCompletionChunk, n as ChatCompletionRequest, C as ChatMessage, o as CreateAgentGatewayConfig, G as GatewayConfig, at as GatewaySandboxContext, b as MppConfig, a2 as PaymentResult, a9 as SandboxBox, aa as SandboxStreamEvent, X as X402Config } from './types-oQ58UakD.js';
2
2
  import './nonce-store.js';
3
3
  import './rate-limit.js';
4
+ export { a as GatewayUsageEvent, P as PaymentMethod, c as SandboxExecutionBudget, S as SandboxUsageReceipt } from './observer-types-A0RtA8uL.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-gateway",
3
- "version": "0.7.1",
3
+ "version": "0.8.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/tangle-network/agent-gateway.git"
@@ -0,0 +1,162 @@
1
+ import type { Task } from './types'
2
+
3
+ interface ExecutionTaskStore {
4
+ get(id: string): Promise<Task | undefined>
5
+ compareAndSet?(expected: Task, next: Task): Promise<boolean>
6
+ compareAndSetExecution?(
7
+ expected: Task,
8
+ next: Task,
9
+ requestId: string,
10
+ now: number,
11
+ ): Promise<boolean>
12
+ }
13
+
14
+ /** Durable marker that prevents cancellation from racing sandbox start. */
15
+ export const TASK_EXECUTION_METADATA_KEY = 'gatewayExecution'
16
+
17
+ const TASK_EXECUTION_VERSION = 1 as const
18
+ const TASK_EXECUTION_LEASE_MS = 5 * 60 * 1000
19
+
20
+ export interface TaskExecutionMarker {
21
+ version: typeof TASK_EXECUTION_VERSION
22
+ requestId: string
23
+ lease: { id: string; expiresAt: number }
24
+ }
25
+
26
+ export type TaskExecutionInspection =
27
+ | { state: 'absent' }
28
+ | { state: 'valid'; marker: TaskExecutionMarker }
29
+ | { state: 'malformed'; reason: string }
30
+
31
+ export class TaskExecutionCanceledError extends Error {
32
+ constructor(taskId: string) {
33
+ super(`A2A task '${taskId}' was canceled before sandbox execution`)
34
+ this.name = 'TaskExecutionCanceledError'
35
+ }
36
+ }
37
+
38
+ /** Claim the right to start one task after its sandbox has been acquired. */
39
+ export async function claimTaskExecution(
40
+ store: ExecutionTaskStore,
41
+ task: Task,
42
+ requestId: string,
43
+ now = Date.now(),
44
+ ): Promise<Task> {
45
+ for (let attempt = 0; attempt < 8; attempt += 1) {
46
+ const current = await store.get(task.id)
47
+ if (!current || current.status.state !== 'working') {
48
+ throw new TaskExecutionCanceledError(task.id)
49
+ }
50
+ const inspection = inspectTaskExecution(current)
51
+ if (inspection.state === 'malformed') {
52
+ throw new Error(`A2A task '${task.id}' has a malformed execution marker`)
53
+ }
54
+ const existing = inspection.state === 'valid' ? inspection.marker : undefined
55
+ if (existing && existing.lease.expiresAt > now) {
56
+ if (existing.requestId === requestId) return current
57
+ throw new Error(`A2A task '${task.id}' is already executing`)
58
+ }
59
+ const next = withTaskExecution(current, requestId, now)
60
+ if (store.compareAndSet && await store.compareAndSet(current, next)) return next
61
+ }
62
+ throw new Error(`A2A task '${task.id}' changed too many times before sandbox execution`)
63
+ }
64
+
65
+ /** Renew both the task execution fence and its cancellation protection. */
66
+ export async function renewTaskExecution(
67
+ store: ExecutionTaskStore,
68
+ taskId: string,
69
+ requestId: string,
70
+ now?: number,
71
+ ): Promise<Task> {
72
+ for (let attempt = 0; attempt < 8; attempt += 1) {
73
+ const current = await store.get(taskId)
74
+ const inspection = current ? inspectTaskExecution(current) : { state: 'absent' as const }
75
+ if (inspection.state === 'malformed') {
76
+ throw new Error(`A2A task '${taskId}' has a malformed execution marker`)
77
+ }
78
+ const marker = inspection.state === 'valid' ? inspection.marker : undefined
79
+ if (!current || current.status.state !== 'working' || marker?.requestId !== requestId) {
80
+ throw new TaskExecutionCanceledError(taskId)
81
+ }
82
+ const renewalNow = now ?? Date.now()
83
+ if (marker.lease.expiresAt <= renewalNow) {
84
+ throw new TaskExecutionCanceledError(taskId)
85
+ }
86
+ const next = withTaskExecution(current, requestId, renewalNow)
87
+ if (!store.compareAndSetExecution) {
88
+ throw new Error('A2A task store does not provide atomic execution renewal')
89
+ }
90
+ if (await store.compareAndSetExecution(current, next, requestId, renewalNow)) return next
91
+ }
92
+ throw new Error(`A2A task '${taskId}' changed too many times while execution was active`)
93
+ }
94
+
95
+ /** Remote cancellation is rejected while a live execution fence is held. */
96
+ export function hasActiveTaskExecution(task: Task, now = Date.now()): boolean {
97
+ const marker = readTaskExecution(task)
98
+ return marker !== undefined && marker.lease.expiresAt > now
99
+ }
100
+
101
+ /** A working task with this marker has lost its execution owner. */
102
+ export function hasExpiredTaskExecution(task: Task, now = Date.now()): boolean {
103
+ const marker = readTaskExecution(task)
104
+ return marker !== undefined && !hasActiveTaskExecution(task, now)
105
+ }
106
+
107
+ /** A working task with an execution key that cannot be trusted. */
108
+ export function hasMalformedTaskExecution(task: Task): boolean {
109
+ return inspectTaskExecution(task).state === 'malformed'
110
+ }
111
+
112
+ /** Remove the marker when the task reaches a terminal or paused state. */
113
+ export function clearTaskExecution(task: Task): Task {
114
+ if (!task.metadata || !(TASK_EXECUTION_METADATA_KEY in task.metadata)) return task
115
+ const metadata = { ...task.metadata }
116
+ delete metadata[TASK_EXECUTION_METADATA_KEY]
117
+ return Object.keys(metadata).length > 0
118
+ ? { ...task, metadata }
119
+ : (() => {
120
+ const { metadata: _metadata, ...withoutMetadata } = task
121
+ return withoutMetadata
122
+ })()
123
+ }
124
+
125
+ function withTaskExecution(task: Task, requestId: string, now: number): Task {
126
+ return {
127
+ ...task,
128
+ metadata: {
129
+ ...(task.metadata ?? {}),
130
+ [TASK_EXECUTION_METADATA_KEY]: {
131
+ version: TASK_EXECUTION_VERSION,
132
+ requestId,
133
+ lease: { id: requestId, expiresAt: now + TASK_EXECUTION_LEASE_MS },
134
+ } satisfies TaskExecutionMarker,
135
+ },
136
+ }
137
+ }
138
+
139
+ function readTaskExecution(task: Task): TaskExecutionMarker | undefined {
140
+ const inspection = inspectTaskExecution(task)
141
+ return inspection.state === 'valid' ? inspection.marker : undefined
142
+ }
143
+
144
+ export function inspectTaskExecution(task: Task): TaskExecutionInspection {
145
+ const raw = task.metadata?.[TASK_EXECUTION_METADATA_KEY]
146
+ if (raw === undefined) return { state: 'absent' }
147
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
148
+ return { state: 'malformed', reason: 'marker must be an object' }
149
+ }
150
+ const marker = raw as Partial<TaskExecutionMarker>
151
+ if (
152
+ marker.version !== TASK_EXECUTION_VERSION ||
153
+ typeof marker.requestId !== 'string' ||
154
+ marker.requestId.length === 0 ||
155
+ !marker.lease ||
156
+ typeof marker.lease.id !== 'string' ||
157
+ marker.lease.id.length === 0 ||
158
+ typeof marker.lease.expiresAt !== 'number' ||
159
+ !Number.isFinite(marker.lease.expiresAt)
160
+ ) return { state: 'malformed', reason: 'marker fields are invalid' }
161
+ return { state: 'valid', marker: marker as TaskExecutionMarker }
162
+ }