@tangle-network/agent-gateway 0.6.0 → 0.7.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.
- package/README.md +31 -6
- package/dist/chunk-Q4YAIEZY.js +1763 -0
- package/dist/chunk-Q4YAIEZY.js.map +1 -0
- package/dist/index.d.ts +13 -9
- package/dist/index.js +112 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware.d.ts +2 -2
- package/dist/middleware.js +1 -1
- package/dist/types-DEsMmS-X.d.ts +875 -0
- package/dist/types.d.ts +1 -1
- package/package.json +14 -10
- package/src/a2a/agent-card.ts +55 -0
- package/src/a2a/handler.ts +797 -0
- package/src/a2a/jsonrpc.ts +65 -0
- package/src/a2a/push-notifications.ts +299 -0
- package/src/a2a/task-store-sql.ts +189 -0
- package/src/a2a/task-store.ts +53 -0
- package/src/a2a/translate.ts +77 -0
- package/src/a2a/types.ts +217 -0
- package/src/dispatch.ts +486 -0
- package/src/index.ts +58 -1
- package/src/middleware.ts +139 -294
- package/src/types.ts +76 -2
- package/src/verify.ts +93 -26
- package/dist/chunk-373QHRKV.js +0 -635
- package/dist/chunk-373QHRKV.js.map +0 -1
- package/dist/types-C_L7yXXI.d.ts +0 -362
|
@@ -0,0 +1,875 @@
|
|
|
1
|
+
import { NonceStore } from './nonce-store.js';
|
|
2
|
+
import { RateLimitStore } from './rate-limit.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A2A protocol types (Google Agent-to-Agent, April 2025).
|
|
6
|
+
*
|
|
7
|
+
* Subset shipped by this gateway:
|
|
8
|
+
* - Discovery: AgentCard via `.well-known/agent.json`
|
|
9
|
+
* - Messaging: `message/send`, `message/stream`
|
|
10
|
+
* - Task control: `tasks/get`, `tasks/cancel`, `tasks/resubscribe`
|
|
11
|
+
* - Push: `tasks/pushNotificationConfig/{set,get,list,delete}` (gated on `pushStore`)
|
|
12
|
+
* - Multi-turn: `input-required` state + follow-up `message/send` with the same `taskId`
|
|
13
|
+
* - Capabilities: streaming = true; pushNotifications gated on config; stateTransitionHistory = false
|
|
14
|
+
* - Parts: text only on input/output (data/file parts rejected with CONTENT_TYPE_NOT_SUPPORTED)
|
|
15
|
+
*
|
|
16
|
+
* Deferred until a real consumer needs them: authenticated extended card,
|
|
17
|
+
* data/file parts, OAuth2/mTLS auth schemes.
|
|
18
|
+
*/
|
|
19
|
+
interface JSONRPCRequest {
|
|
20
|
+
jsonrpc: '2.0';
|
|
21
|
+
id: string | number | null;
|
|
22
|
+
method: string;
|
|
23
|
+
params?: unknown;
|
|
24
|
+
}
|
|
25
|
+
interface JSONRPCSuccessResponse<T = unknown> {
|
|
26
|
+
jsonrpc: '2.0';
|
|
27
|
+
id: string | number | null;
|
|
28
|
+
result: T;
|
|
29
|
+
}
|
|
30
|
+
interface JSONRPCErrorResponse {
|
|
31
|
+
jsonrpc: '2.0';
|
|
32
|
+
id: string | number | null;
|
|
33
|
+
error: {
|
|
34
|
+
code: number;
|
|
35
|
+
message: string;
|
|
36
|
+
data?: unknown;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
type JSONRPCResponse<T = unknown> = JSONRPCSuccessResponse<T> | JSONRPCErrorResponse;
|
|
40
|
+
/** Standard JSON-RPC + A2A-specific codes. Negative ints per JSON-RPC spec. */
|
|
41
|
+
declare const A2A_ERROR_CODES: {
|
|
42
|
+
readonly PARSE_ERROR: -32700;
|
|
43
|
+
readonly INVALID_REQUEST: -32600;
|
|
44
|
+
readonly METHOD_NOT_FOUND: -32601;
|
|
45
|
+
readonly INVALID_PARAMS: -32602;
|
|
46
|
+
readonly INTERNAL_ERROR: -32603;
|
|
47
|
+
readonly TASK_NOT_FOUND: -32001;
|
|
48
|
+
readonly TASK_NOT_CANCELABLE: -32002;
|
|
49
|
+
readonly PUSH_NOT_SUPPORTED: -32003;
|
|
50
|
+
readonly UNSUPPORTED_OPERATION: -32004;
|
|
51
|
+
readonly CONTENT_TYPE_NOT_SUPPORTED: -32005;
|
|
52
|
+
readonly INVALID_AGENT_RESPONSE: -32006;
|
|
53
|
+
readonly AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED: -32007;
|
|
54
|
+
};
|
|
55
|
+
interface TextPart {
|
|
56
|
+
kind: 'text';
|
|
57
|
+
text: string;
|
|
58
|
+
metadata?: Record<string, unknown>;
|
|
59
|
+
}
|
|
60
|
+
interface DataPart {
|
|
61
|
+
kind: 'data';
|
|
62
|
+
data: Record<string, unknown>;
|
|
63
|
+
metadata?: Record<string, unknown>;
|
|
64
|
+
}
|
|
65
|
+
interface FilePart {
|
|
66
|
+
kind: 'file';
|
|
67
|
+
file: {
|
|
68
|
+
name?: string;
|
|
69
|
+
mimeType?: string;
|
|
70
|
+
bytes?: string;
|
|
71
|
+
uri?: string;
|
|
72
|
+
};
|
|
73
|
+
metadata?: Record<string, unknown>;
|
|
74
|
+
}
|
|
75
|
+
type Part = TextPart | DataPart | FilePart;
|
|
76
|
+
interface Message {
|
|
77
|
+
kind: 'message';
|
|
78
|
+
role: 'user' | 'agent';
|
|
79
|
+
parts: Part[];
|
|
80
|
+
messageId: string;
|
|
81
|
+
taskId?: string;
|
|
82
|
+
contextId?: string;
|
|
83
|
+
metadata?: Record<string, unknown>;
|
|
84
|
+
}
|
|
85
|
+
type TaskState = 'submitted' | 'working' | 'input-required' | 'completed' | 'canceled' | 'failed' | 'rejected' | 'auth-required';
|
|
86
|
+
interface TaskStatus {
|
|
87
|
+
state: TaskState;
|
|
88
|
+
message?: Message;
|
|
89
|
+
timestamp: string;
|
|
90
|
+
}
|
|
91
|
+
interface Artifact {
|
|
92
|
+
artifactId: string;
|
|
93
|
+
name?: string;
|
|
94
|
+
description?: string;
|
|
95
|
+
parts: Part[];
|
|
96
|
+
metadata?: Record<string, unknown>;
|
|
97
|
+
}
|
|
98
|
+
interface Task {
|
|
99
|
+
kind: 'task';
|
|
100
|
+
id: string;
|
|
101
|
+
contextId: string;
|
|
102
|
+
status: TaskStatus;
|
|
103
|
+
history?: Message[];
|
|
104
|
+
artifacts?: Artifact[];
|
|
105
|
+
metadata?: Record<string, unknown>;
|
|
106
|
+
}
|
|
107
|
+
interface TaskStatusUpdateEvent {
|
|
108
|
+
kind: 'status-update';
|
|
109
|
+
taskId: string;
|
|
110
|
+
contextId: string;
|
|
111
|
+
status: TaskStatus;
|
|
112
|
+
/** True on the terminal event; clients close the stream after this. */
|
|
113
|
+
final: boolean;
|
|
114
|
+
metadata?: Record<string, unknown>;
|
|
115
|
+
}
|
|
116
|
+
interface TaskArtifactUpdateEvent {
|
|
117
|
+
kind: 'artifact-update';
|
|
118
|
+
taskId: string;
|
|
119
|
+
contextId: string;
|
|
120
|
+
artifact: Artifact;
|
|
121
|
+
/** True when this artifact's parts should be appended to the prior emit (incremental streaming). */
|
|
122
|
+
append?: boolean;
|
|
123
|
+
/** True on the artifact's final chunk. */
|
|
124
|
+
lastChunk?: boolean;
|
|
125
|
+
metadata?: Record<string, unknown>;
|
|
126
|
+
}
|
|
127
|
+
type StreamingEvent = TaskStatusUpdateEvent | TaskArtifactUpdateEvent;
|
|
128
|
+
interface MessageSendParams {
|
|
129
|
+
message: Message;
|
|
130
|
+
configuration?: {
|
|
131
|
+
acceptedOutputModes?: string[];
|
|
132
|
+
blocking?: boolean;
|
|
133
|
+
historyLength?: number;
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
interface TaskIdParams {
|
|
137
|
+
id: string;
|
|
138
|
+
metadata?: Record<string, unknown>;
|
|
139
|
+
}
|
|
140
|
+
interface TaskPushNotificationConfigGetParams {
|
|
141
|
+
/** Task id whose configs are being queried. */
|
|
142
|
+
id: string;
|
|
143
|
+
/** Specific config id to fetch. Required for `set` and `delete`; omitted for `list`. */
|
|
144
|
+
pushNotificationConfigId?: string;
|
|
145
|
+
metadata?: Record<string, unknown>;
|
|
146
|
+
}
|
|
147
|
+
interface AgentSkill {
|
|
148
|
+
id: string;
|
|
149
|
+
name: string;
|
|
150
|
+
description: string;
|
|
151
|
+
tags?: string[];
|
|
152
|
+
examples?: string[];
|
|
153
|
+
inputModes?: string[];
|
|
154
|
+
outputModes?: string[];
|
|
155
|
+
}
|
|
156
|
+
interface AgentCapabilities {
|
|
157
|
+
streaming?: boolean;
|
|
158
|
+
pushNotifications?: boolean;
|
|
159
|
+
stateTransitionHistory?: boolean;
|
|
160
|
+
}
|
|
161
|
+
interface AgentProvider {
|
|
162
|
+
organization: string;
|
|
163
|
+
url?: string;
|
|
164
|
+
}
|
|
165
|
+
interface AgentCardAuthentication {
|
|
166
|
+
/** Auth scheme names the agent accepts (e.g. 'Bearer', 'x402', 'mpp'). */
|
|
167
|
+
schemes: string[];
|
|
168
|
+
/** Optional human-readable hint about obtaining credentials. */
|
|
169
|
+
credentials?: string;
|
|
170
|
+
}
|
|
171
|
+
interface AgentCard {
|
|
172
|
+
name: string;
|
|
173
|
+
description: string;
|
|
174
|
+
/** JSON-RPC endpoint URL — clients POST methods here. */
|
|
175
|
+
url: string;
|
|
176
|
+
version: string;
|
|
177
|
+
documentationUrl?: string;
|
|
178
|
+
provider?: AgentProvider;
|
|
179
|
+
capabilities: AgentCapabilities;
|
|
180
|
+
authentication: AgentCardAuthentication;
|
|
181
|
+
defaultInputModes: string[];
|
|
182
|
+
defaultOutputModes: string[];
|
|
183
|
+
skills: AgentSkill[];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Task persistence behind the JSON-RPC dispatcher. Default adapter is in
|
|
188
|
+
* memory with a 1-hour TTL — adequate for tests, scratch, and Workers with
|
|
189
|
+
* a short-lived process. Production deployments wire their own
|
|
190
|
+
* `TaskStore` (D1, postgres, Durable Object) via `GatewayConfig.a2a`.
|
|
191
|
+
*/
|
|
192
|
+
|
|
193
|
+
interface TaskStore {
|
|
194
|
+
get(id: string): Promise<Task | undefined>;
|
|
195
|
+
put(task: Task): Promise<void>;
|
|
196
|
+
delete(id: string): Promise<void>;
|
|
197
|
+
}
|
|
198
|
+
declare class InMemoryTaskStore implements TaskStore {
|
|
199
|
+
private readonly ttlMs;
|
|
200
|
+
private readonly entries;
|
|
201
|
+
constructor(ttlMs?: number);
|
|
202
|
+
get(id: string): Promise<Task | undefined>;
|
|
203
|
+
put(task: Task): Promise<void>;
|
|
204
|
+
delete(id: string): Promise<void>;
|
|
205
|
+
/**
|
|
206
|
+
* Sweep expired tasks. Called inline on every read/write — cheap for the
|
|
207
|
+
* Map sizes this is designed for (10s–1000s of concurrent tasks).
|
|
208
|
+
*/
|
|
209
|
+
private gc;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @stable
|
|
214
|
+
*
|
|
215
|
+
* Durable `TaskStore` against any SQL store. Adapter-agnostic: callers wire a
|
|
216
|
+
* `SqlAdapter` against their driver (D1, postgres, sqlite, libSQL, Turso) and
|
|
217
|
+
* the same store survives gateway restarts so an in-flight task (and its
|
|
218
|
+
* artifacts) is recoverable after a Worker recycle.
|
|
219
|
+
*
|
|
220
|
+
* Schema is one table: tasks keyed by id with the full JSON payload, plus a
|
|
221
|
+
* secondary index on `context_id` so `tasks/resubscribe` and conversational
|
|
222
|
+
* 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.
|
|
226
|
+
*
|
|
227
|
+
* Why not bake in a specific driver? Hono workers run on Cloudflare (D1),
|
|
228
|
+
* Node (pg / sqlite), Bun, Deno. Burning a hard dependency on one client
|
|
229
|
+
* limits the gateway's reach. The adapter indirection costs ~5 lines per
|
|
230
|
+
* driver in the consumer's code and keeps the package free of native deps.
|
|
231
|
+
*
|
|
232
|
+
* @example D1
|
|
233
|
+
* import { SqlTaskStore, d1ToSqlAdapter } from '@tangle-network/agent-gateway'
|
|
234
|
+
* const store = new SqlTaskStore(d1ToSqlAdapter(env.DB))
|
|
235
|
+
* await store.migrate()
|
|
236
|
+
* const gw = createAgentGateway({ ..., a2a: { taskStore: store } })
|
|
237
|
+
*
|
|
238
|
+
* @example libSQL / Turso
|
|
239
|
+
* import { createClient } from '@libsql/client'
|
|
240
|
+
* const client = createClient({ url: process.env.TURSO_URL!, authToken: process.env.TURSO_TOKEN! })
|
|
241
|
+
* const libsql: SqlAdapter = {
|
|
242
|
+
* exec: async (sql, params = []) => {
|
|
243
|
+
* const r = await client.execute({ sql, args: params as never[] })
|
|
244
|
+
* return { rowsAffected: Number(r.rowsAffected ?? 0) }
|
|
245
|
+
* },
|
|
246
|
+
* query: async (sql, params = []) => {
|
|
247
|
+
* const r = await client.execute({ sql, args: params as never[] })
|
|
248
|
+
* return r.rows as unknown as Record<string, unknown>[]
|
|
249
|
+
* },
|
|
250
|
+
* }
|
|
251
|
+
* const store = new SqlTaskStore(libsql)
|
|
252
|
+
* await store.migrate()
|
|
253
|
+
*/
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Minimal SQL driver shape — identical to agent-runtime's `SqlAdapter` so the
|
|
257
|
+
* same wrapper code works for both packages. Parameter placeholders MUST be
|
|
258
|
+
* `?` (positional); driver wrappers that use `$1`, `$2`, … should rewrite at
|
|
259
|
+
* the adapter boundary (see node-postgres example in the durability docs).
|
|
260
|
+
*/
|
|
261
|
+
interface SqlAdapter {
|
|
262
|
+
exec(sql: string, params?: readonly unknown[]): Promise<{
|
|
263
|
+
rowsAffected: number;
|
|
264
|
+
}>;
|
|
265
|
+
query<TRow = Record<string, unknown>>(sql: string, params?: readonly unknown[]): Promise<TRow[]>;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Adapt a Cloudflare D1 binding to `SqlAdapter`. The package never imports
|
|
269
|
+
* `@cloudflare/workers-types`; the binding's structural shape lines up via
|
|
270
|
+
* TypeScript structural compatibility.
|
|
271
|
+
*/
|
|
272
|
+
declare function d1ToSqlAdapter(db: D1DatabaseLike): SqlAdapter;
|
|
273
|
+
interface D1DatabaseLike {
|
|
274
|
+
prepare(sql: string): D1StmtLike;
|
|
275
|
+
}
|
|
276
|
+
interface D1StmtLike {
|
|
277
|
+
bind(...params: unknown[]): D1StmtLike;
|
|
278
|
+
run(): Promise<unknown>;
|
|
279
|
+
all<TRow = unknown>(): Promise<{
|
|
280
|
+
results?: TRow[];
|
|
281
|
+
}>;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* SQL-backed TaskStore. Stores the full Task JSON; reads return a deep clone
|
|
285
|
+
* so callers never observe shared references. TTL is enforced at read time:
|
|
286
|
+
* expired rows are filtered out and (best-effort) deleted, matching the
|
|
287
|
+
* in-memory store's semantics so behavior is portable across both adapters.
|
|
288
|
+
*/
|
|
289
|
+
declare class SqlTaskStore implements TaskStore {
|
|
290
|
+
private readonly db;
|
|
291
|
+
private readonly opts;
|
|
292
|
+
constructor(db: SqlAdapter, opts?: {
|
|
293
|
+
ttlMs?: number;
|
|
294
|
+
table?: string;
|
|
295
|
+
});
|
|
296
|
+
private get ttlMs();
|
|
297
|
+
private get table();
|
|
298
|
+
/** Idempotent. Call once at deploy. */
|
|
299
|
+
migrate(): Promise<void>;
|
|
300
|
+
get(id: string): Promise<Task | undefined>;
|
|
301
|
+
put(task: Task): Promise<void>;
|
|
302
|
+
delete(id: string): Promise<void>;
|
|
303
|
+
/**
|
|
304
|
+
* Lookup tasks by contextId — used by `tasks/resubscribe` and the multi-turn
|
|
305
|
+
* dispatcher. Returns most-recent-first. Not part of the base TaskStore
|
|
306
|
+
* interface since the in-memory store doesn't expose it; consumers that
|
|
307
|
+
* specifically wire SqlTaskStore can use it for richer queries.
|
|
308
|
+
*/
|
|
309
|
+
listByContext(contextId: string): Promise<Task[]>;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* @stable
|
|
314
|
+
*
|
|
315
|
+
* A2A push notifications — a webhook delivery channel that fires when a task
|
|
316
|
+
* reaches a terminal state (`completed`, `canceled`, `failed`, `rejected`).
|
|
317
|
+
* The protocol specifies four JSON-RPC methods (`tasks/pushNotificationConfig/`
|
|
318
|
+
* {set, get, list, delete}) for registering / inspecting / removing configs,
|
|
319
|
+
* plus the delivery contract: an HTTP POST to the registered URL with the
|
|
320
|
+
* task envelope as the body and an HMAC-SHA256 signature for verification.
|
|
321
|
+
*
|
|
322
|
+
* This is the minimum shape long-horizon agents need. A consumer that finishes
|
|
323
|
+
* a task in 30 minutes can't keep an SSE stream open against a Worker (CPU
|
|
324
|
+
* limits) or an unauthenticated browser tab (network drops) — they need a
|
|
325
|
+
* fire-and-forget endpoint the gateway calls when the task is done.
|
|
326
|
+
*
|
|
327
|
+
* Out of scope for the first pass: retries, queue durability, partial-state
|
|
328
|
+
* notifications. If the webhook returns non-2xx or the request fails, the
|
|
329
|
+
* gateway logs and moves on — the consumer's endpoint should idempotently
|
|
330
|
+
* pull state via `tasks/get` rather than rely on at-least-once delivery.
|
|
331
|
+
*
|
|
332
|
+
* @example registering a webhook
|
|
333
|
+
* {
|
|
334
|
+
* "jsonrpc": "2.0", "id": 1, "method": "tasks/pushNotificationConfig/set",
|
|
335
|
+
* "params": {
|
|
336
|
+
* "taskId": "task_abc",
|
|
337
|
+
* "pushNotificationConfig": {
|
|
338
|
+
* "id": "cfg_1",
|
|
339
|
+
* "url": "https://my-consumer.example.com/agent/done",
|
|
340
|
+
* "token": "my-shared-secret-not-the-hmac-secret"
|
|
341
|
+
* }
|
|
342
|
+
* }
|
|
343
|
+
* }
|
|
344
|
+
*
|
|
345
|
+
* @example webhook delivery
|
|
346
|
+
* POST https://my-consumer.example.com/agent/done
|
|
347
|
+
* X-A2A-Notification-Token: my-shared-secret-not-the-hmac-secret
|
|
348
|
+
* X-A2A-Signature: sha256=<hex(HMAC-SHA256(webhookSecret, body))>
|
|
349
|
+
* Content-Type: application/json
|
|
350
|
+
* { "taskId": "task_abc", "state": "completed", "task": { ...full Task... } }
|
|
351
|
+
*/
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Authentication metadata for the webhook itself. The A2A spec leaves this
|
|
355
|
+
* to consumers — the most common shape is a bearer token the gateway sends as
|
|
356
|
+
* `Authorization: <scheme> <credential>`. We pass it through verbatim.
|
|
357
|
+
*/
|
|
358
|
+
interface PushNotificationAuthentication {
|
|
359
|
+
schemes: string[];
|
|
360
|
+
credentials?: string;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Per-task push notification configuration. A task can have multiple configs
|
|
364
|
+
* (e.g. one for the consumer's own webhook + one for an audit log endpoint).
|
|
365
|
+
*/
|
|
366
|
+
interface PushNotificationConfig {
|
|
367
|
+
/** Stable id within the task's config set. Required for get/delete addressing. */
|
|
368
|
+
id: string;
|
|
369
|
+
/** HTTPS URL the gateway will POST to. */
|
|
370
|
+
url: string;
|
|
371
|
+
/**
|
|
372
|
+
* Opaque token the gateway sends back as `X-A2A-Notification-Token` so the
|
|
373
|
+
* webhook can verify the call originated from a registration the consumer
|
|
374
|
+
* authorized. Distinct from the HMAC signature (which proves the body
|
|
375
|
+
* wasn't tampered with) — this proves the registration is recognised.
|
|
376
|
+
*/
|
|
377
|
+
token?: string;
|
|
378
|
+
/** Optional webhook-side auth metadata. */
|
|
379
|
+
authentication?: PushNotificationAuthentication;
|
|
380
|
+
}
|
|
381
|
+
interface TaskPushNotificationConfig {
|
|
382
|
+
taskId: string;
|
|
383
|
+
pushNotificationConfig: PushNotificationConfig;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Storage for push configs. The default in-memory store is fine for single
|
|
387
|
+
* Worker instances + tests; production multi-instance deployments need
|
|
388
|
+
* `SqlPushNotificationStore` (or any other shared-state adapter) so a config
|
|
389
|
+
* registered on instance A is visible to a delivery firing from instance B.
|
|
390
|
+
*/
|
|
391
|
+
interface PushNotificationStore {
|
|
392
|
+
set(taskId: string, config: PushNotificationConfig): Promise<void>;
|
|
393
|
+
get(taskId: string, configId: string): Promise<PushNotificationConfig | undefined>;
|
|
394
|
+
list(taskId: string): Promise<PushNotificationConfig[]>;
|
|
395
|
+
delete(taskId: string, configId: string): Promise<void>;
|
|
396
|
+
}
|
|
397
|
+
declare class InMemoryPushNotificationStore implements PushNotificationStore {
|
|
398
|
+
private readonly byTask;
|
|
399
|
+
set(taskId: string, config: PushNotificationConfig): Promise<void>;
|
|
400
|
+
get(taskId: string, configId: string): Promise<PushNotificationConfig | undefined>;
|
|
401
|
+
list(taskId: string): Promise<PushNotificationConfig[]>;
|
|
402
|
+
delete(taskId: string, configId: string): Promise<void>;
|
|
403
|
+
}
|
|
404
|
+
/** SQL-backed push config store. Schema: one row per (taskId, configId). */
|
|
405
|
+
declare class SqlPushNotificationStore implements PushNotificationStore {
|
|
406
|
+
private readonly db;
|
|
407
|
+
private readonly table;
|
|
408
|
+
constructor(db: SqlAdapter, table?: string);
|
|
409
|
+
migrate(): Promise<void>;
|
|
410
|
+
set(taskId: string, config: PushNotificationConfig): Promise<void>;
|
|
411
|
+
get(taskId: string, configId: string): Promise<PushNotificationConfig | undefined>;
|
|
412
|
+
list(taskId: string): Promise<PushNotificationConfig[]>;
|
|
413
|
+
delete(taskId: string, configId: string): Promise<void>;
|
|
414
|
+
}
|
|
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: {
|
|
425
|
+
task: Task;
|
|
426
|
+
store: PushNotificationStore;
|
|
427
|
+
webhookSecret: string | undefined;
|
|
428
|
+
/** Inject for tests. Defaults to global `fetch`. */
|
|
429
|
+
fetcher?: typeof fetch;
|
|
430
|
+
/** Optional callback so the gateway's observer can log delivery outcomes. */
|
|
431
|
+
onDelivery?: (result: PushDeliveryResult) => void;
|
|
432
|
+
}): Promise<PushDeliveryResult[]>;
|
|
433
|
+
interface PushDeliveryResult {
|
|
434
|
+
taskId: string;
|
|
435
|
+
configId: string;
|
|
436
|
+
url: string;
|
|
437
|
+
ok: boolean;
|
|
438
|
+
status?: number;
|
|
439
|
+
error?: string;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
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.
|
|
452
|
+
*/
|
|
453
|
+
|
|
454
|
+
interface RequestContext {
|
|
455
|
+
requestId: string;
|
|
456
|
+
agentSlug: string;
|
|
457
|
+
startMs: number;
|
|
458
|
+
}
|
|
459
|
+
interface AuthFailureReason {
|
|
460
|
+
method: 'x402' | 'mpp' | 'apikey' | 'none';
|
|
461
|
+
code: string;
|
|
462
|
+
httpStatus: number;
|
|
463
|
+
}
|
|
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>;
|
|
482
|
+
/**
|
|
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.
|
|
486
|
+
*/
|
|
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>;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
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).
|
|
510
|
+
*
|
|
511
|
+
* Usage:
|
|
512
|
+
* new ConsoleObserver(({ level, event, ...rest }) => logger.info({ event, ...rest }))
|
|
513
|
+
*/
|
|
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;
|
|
545
|
+
}
|
|
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>;
|
|
563
|
+
}
|
|
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
|
+
|
|
570
|
+
interface AgentMeta {
|
|
571
|
+
/** Unique agent identifier (workspace ID, session ID, etc.) */
|
|
572
|
+
id: string;
|
|
573
|
+
/** Owner/creator user ID */
|
|
574
|
+
ownerId: string;
|
|
575
|
+
/** Public URL slug */
|
|
576
|
+
slug: string;
|
|
577
|
+
/** System prompt for the agent (injected before consumer messages) */
|
|
578
|
+
systemPrompt?: string;
|
|
579
|
+
/** Per-token price in USD (default: 0.00002) */
|
|
580
|
+
pricePerTokenUsd: number;
|
|
581
|
+
/** Platform fee as decimal 0-1 (default: 0.20 = 20%) */
|
|
582
|
+
platformFeePercent: number;
|
|
583
|
+
/** Remote operator endpoint for sovereignty mode (null = centralized) */
|
|
584
|
+
sandboxEndpoint: string | null;
|
|
585
|
+
/** Sandbox ID on remote operator */
|
|
586
|
+
remoteSandboxId: string | null;
|
|
587
|
+
/** PASETO bearer token for remote operator auth */
|
|
588
|
+
remoteBearerToken: string | null;
|
|
589
|
+
/** Whether agent is published and accepting requests */
|
|
590
|
+
enabled: boolean;
|
|
591
|
+
/**
|
|
592
|
+
* CLI harness backend that runs this agent inside the sandbox sidecar.
|
|
593
|
+
*
|
|
594
|
+
* When set, the host's `getSandbox()` SHOULD return a `SandboxBox`
|
|
595
|
+
* whose `streamPrompt` POSTs to the sidecar's
|
|
596
|
+
* `POST /agent/invoke/chat/completions` endpoint with
|
|
597
|
+
* `model: "<harness>/<harnessModel>"` — that endpoint runs the
|
|
598
|
+
* harness against the sandbox workspace and streams OpenAI-shape
|
|
599
|
+
* `chat.completion.chunk` frames back.
|
|
600
|
+
*
|
|
601
|
+
* When unset (legacy / template mode), the host's `streamPrompt`
|
|
602
|
+
* falls back to the template's own `/api/chat/completions` (proxied
|
|
603
|
+
* via the sidecar's `/agent/invoke`).
|
|
604
|
+
*
|
|
605
|
+
* Known harnesses (registered in agent-dev-container's
|
|
606
|
+
* cli-agent-bindings.ts): opencode, claude-code, codex, kimi-code,
|
|
607
|
+
* amp, factory-droids, pi, hermes, openclaw, forge, acp, cursor.
|
|
608
|
+
* Aliases the sidecar canonicalizes: claude → claude-code,
|
|
609
|
+
* kimi → kimi-code, factory → factory-droids.
|
|
610
|
+
*/
|
|
611
|
+
harness?: string;
|
|
612
|
+
/**
|
|
613
|
+
* Model identifier to pass after the harness in the
|
|
614
|
+
* `<harness>/<model>` slash form. Format is harness-specific:
|
|
615
|
+
* claude-code: "sonnet", "opus", or a versioned id like
|
|
616
|
+
* "claude-sonnet-4-20250514"
|
|
617
|
+
* opencode: "anthropic/claude-sonnet-4-5", "openai/gpt-4o", …
|
|
618
|
+
* (opencode embeds provider before model)
|
|
619
|
+
* codex: "gpt-5-codex"
|
|
620
|
+
* kimi-code: "kimi-for-coding"
|
|
621
|
+
*
|
|
622
|
+
* Only meaningful when `harness` is set; ignored otherwise.
|
|
623
|
+
*/
|
|
624
|
+
harnessModel?: string;
|
|
625
|
+
/**
|
|
626
|
+
* Optional human description surfaced in the A2A Agent Card. Defaults to
|
|
627
|
+
* `"{slug} agent"` when absent.
|
|
628
|
+
*/
|
|
629
|
+
description?: string;
|
|
630
|
+
/**
|
|
631
|
+
* Optional A2A skill descriptors. Each entry advertises what the agent
|
|
632
|
+
* can do so non-Tangle A2A clients can select agents by capability. When
|
|
633
|
+
* absent, the gateway synthesizes a single default `chat` skill from
|
|
634
|
+
* `slug` + `description`.
|
|
635
|
+
*/
|
|
636
|
+
skills?: AgentSkill[];
|
|
637
|
+
}
|
|
638
|
+
type PaymentMethod = 'x402' | 'mpp' | 'apikey' | 'none';
|
|
639
|
+
interface X402Config {
|
|
640
|
+
/** Ethereum operator address for SpendAuth verification */
|
|
641
|
+
operatorAddress: string;
|
|
642
|
+
/** Blockchain network ID (default: 3799) */
|
|
643
|
+
chainId: number;
|
|
644
|
+
/** ShieldedCredits contract address */
|
|
645
|
+
creditsAddress?: string;
|
|
646
|
+
/** RPC URL for on-chain verification (optional, demo mode skips this) */
|
|
647
|
+
rpcUrl?: string;
|
|
648
|
+
/** Demo mode: skip signature verification (default: false). NEVER enable in production. */
|
|
649
|
+
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>;
|
|
652
|
+
}
|
|
653
|
+
interface MppConfig {
|
|
654
|
+
/** MPP realm (e.g. "agents.tangle.tools") */
|
|
655
|
+
realm: string;
|
|
656
|
+
/** MPP method name (default: "blueprintevm") */
|
|
657
|
+
method?: string;
|
|
658
|
+
/**
|
|
659
|
+
* Production verifier for the method-specific credential. Return the
|
|
660
|
+
* authenticated consumer id, or null when the credential is invalid.
|
|
661
|
+
* The callback receives the decoded JSON payload when one exists plus the
|
|
662
|
+
* original decoded credential so non-JSON methods can verify their own form.
|
|
663
|
+
* Omit only when x402.demoMode is explicitly enabled for local testing, or
|
|
664
|
+
* when x402.verifySigner handles an x402-compatible MPP credential.
|
|
665
|
+
*/
|
|
666
|
+
verifySigner?: (payload: Record<string, unknown>, context: {
|
|
667
|
+
method: string;
|
|
668
|
+
credential: string;
|
|
669
|
+
}) => Promise<string | null>;
|
|
670
|
+
}
|
|
671
|
+
interface PaymentResult {
|
|
672
|
+
method: PaymentMethod;
|
|
673
|
+
consumerId: string;
|
|
674
|
+
/**
|
|
675
|
+
* Per-request id (matches `RequestContext.requestId` from the
|
|
676
|
+
* Observer pattern). Threaded into `settlePayment` so callers can
|
|
677
|
+
* attribute revenue deterministically per-request without scanning
|
|
678
|
+
* a FIFO queue keyed by consumerId — when the same consumer is
|
|
679
|
+
* paying for two concurrent requests against agents A and B, FIFO
|
|
680
|
+
* misroutes one. With `requestId` the call site can write a
|
|
681
|
+
* settlement row keyed exactly to the request that earned it.
|
|
682
|
+
*/
|
|
683
|
+
requestId: string;
|
|
684
|
+
}
|
|
685
|
+
interface ApiKeyInfo {
|
|
686
|
+
keyId: string;
|
|
687
|
+
consumerId: string;
|
|
688
|
+
/** Scopes this key is authorized for (e.g. ["chat", "forms"]) */
|
|
689
|
+
scopes?: string[];
|
|
690
|
+
/** Per-key rate limit override (requests per minute). If set, overrides global rate limit. */
|
|
691
|
+
rateLimitPerMinute?: number;
|
|
692
|
+
/** Per-key daily limit override. */
|
|
693
|
+
dailyLimit?: number;
|
|
694
|
+
}
|
|
695
|
+
interface GatewayUsageEvent {
|
|
696
|
+
/**
|
|
697
|
+
* Per-request id (matches `RequestContext.requestId`). Lets
|
|
698
|
+
* `recordUsage` correlate the usage row to the same request that
|
|
699
|
+
* `settlePayment` settles, observability hooks observe, and
|
|
700
|
+
* `onRequestComplete` reports — without re-deriving from a
|
|
701
|
+
* synthetic key. Required field as of 0.4.0; the gateway always has
|
|
702
|
+
* it in scope at the recordUsage call site.
|
|
703
|
+
*/
|
|
704
|
+
requestId: string;
|
|
705
|
+
agentId: string;
|
|
706
|
+
agentSlug: string;
|
|
707
|
+
consumerId: string;
|
|
708
|
+
paymentMethod: PaymentMethod;
|
|
709
|
+
inputTokens: number;
|
|
710
|
+
outputTokens: number;
|
|
711
|
+
totalCostUsd: number;
|
|
712
|
+
ownerEarnedUsd: number;
|
|
713
|
+
platformFeeUsd: number;
|
|
714
|
+
durationMs: number;
|
|
715
|
+
}
|
|
716
|
+
interface SandboxStreamEvent {
|
|
717
|
+
type?: string;
|
|
718
|
+
data?: {
|
|
719
|
+
part?: {
|
|
720
|
+
type?: string;
|
|
721
|
+
text?: string;
|
|
722
|
+
};
|
|
723
|
+
delta?: string;
|
|
724
|
+
finalText?: string;
|
|
725
|
+
/**
|
|
726
|
+
* Optional sandbox-side signal that the agent has paused and is waiting
|
|
727
|
+
* for additional input from the caller. The A2A gateway translates this
|
|
728
|
+
* into an `input-required` task status; the caller can then submit a
|
|
729
|
+
* follow-up `message/send` with the same `taskId` to continue. Ignored
|
|
730
|
+
* by the OpenAI-compat path. Carry an optional `prompt` to surface to
|
|
731
|
+
* the caller (rendered as the input-required message body).
|
|
732
|
+
*/
|
|
733
|
+
inputRequired?: {
|
|
734
|
+
prompt?: string;
|
|
735
|
+
};
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
interface SandboxBox {
|
|
739
|
+
streamPrompt(message: string, opts?: {
|
|
740
|
+
sessionId?: string;
|
|
741
|
+
systemPrompt?: string;
|
|
742
|
+
}): AsyncIterable<SandboxStreamEvent>;
|
|
743
|
+
}
|
|
744
|
+
interface GatewayConfig {
|
|
745
|
+
/** Resolve agent metadata by slug. Return null if not found or not published. */
|
|
746
|
+
resolveAgent: (slug: string) => Promise<AgentMeta | null>;
|
|
747
|
+
/** Get a sandbox instance for the agent. Called after payment is verified. */
|
|
748
|
+
getSandbox: (agent: AgentMeta) => Promise<SandboxBox>;
|
|
749
|
+
/**
|
|
750
|
+
* Optional host authorization hook fired after payment verification
|
|
751
|
+
* and before sandbox resolution. Use it for per-agent allowlists,
|
|
752
|
+
* per-consumer quotas, contract scope checks, and instance ownership.
|
|
753
|
+
*/
|
|
754
|
+
authorizeConsumer?: (agent: AgentMeta, consumer: {
|
|
755
|
+
method: PaymentMethod;
|
|
756
|
+
consumerId: string;
|
|
757
|
+
keyId?: string;
|
|
758
|
+
requestId: string;
|
|
759
|
+
}) => Promise<{
|
|
760
|
+
allow: true;
|
|
761
|
+
} | {
|
|
762
|
+
allow: false;
|
|
763
|
+
reason: string;
|
|
764
|
+
code: string;
|
|
765
|
+
}>;
|
|
766
|
+
/** Record a usage event after request completes. */
|
|
767
|
+
recordUsage: (event: GatewayUsageEvent) => Promise<void>;
|
|
768
|
+
/** x402 payment configuration */
|
|
769
|
+
x402: X402Config;
|
|
770
|
+
/** MPP (Machine Payments Protocol) configuration. It is advertised only when a production verifier or explicit demo mode is available. */
|
|
771
|
+
mpp?: MppConfig;
|
|
772
|
+
/**
|
|
773
|
+
* Verify an API key. Return key info if valid, null if invalid.
|
|
774
|
+
* In explicit x402 demo mode, the built-in verifier accepts `sk_agent_*` keys.
|
|
775
|
+
* Production gateways must provide this callback.
|
|
776
|
+
*/
|
|
777
|
+
verifyApiKey?: (authHeader: string) => Promise<ApiKeyInfo | null>;
|
|
778
|
+
/**
|
|
779
|
+
* Settle payment after successful response.
|
|
780
|
+
* For x402: call ShieldedCredits.claimPayment()
|
|
781
|
+
* For API key: deduct from spending limit
|
|
782
|
+
* Default: no-op (demo mode).
|
|
783
|
+
*/
|
|
784
|
+
settlePayment?: (payment: PaymentResult, cost: number) => Promise<void>;
|
|
785
|
+
/** Base URL for API key purchase links (e.g. "https://film.tangle.tools") */
|
|
786
|
+
baseUrl?: string;
|
|
787
|
+
/** Max message length in chars (default: 8000) */
|
|
788
|
+
maxMessageLength?: number;
|
|
789
|
+
/** Required scope for chat endpoint (default: "chat"). API keys must include this scope. */
|
|
790
|
+
requiredScope?: string;
|
|
791
|
+
/** Block requests with detected injection patterns (default: false — log only) */
|
|
792
|
+
blockInjection?: boolean;
|
|
793
|
+
/** Rate limiting config. Default: 60 requests per 60 seconds per consumer. */
|
|
794
|
+
rateLimit?: {
|
|
795
|
+
limit: number;
|
|
796
|
+
windowSeconds: number;
|
|
797
|
+
};
|
|
798
|
+
/** Custom rate limit store (default: in-memory). Use KV-backed for Workers. */
|
|
799
|
+
rateLimitStore?: RateLimitStore;
|
|
800
|
+
/** Nonce replay protection store (default: in-memory). Rejects reused x402 nonces. */
|
|
801
|
+
nonceStore?: NonceStore;
|
|
802
|
+
/**
|
|
803
|
+
* Observability hook. When set, the gateway emits typed events for request
|
|
804
|
+
* lifecycle, auth outcomes, rate limits, injection detection, usage, errors,
|
|
805
|
+
* and settlement failures. See ./observer.ts for the interface and
|
|
806
|
+
* ConsoleObserver / CompositeObserver implementations.
|
|
807
|
+
*/
|
|
808
|
+
observer?: GatewayObserver;
|
|
809
|
+
/**
|
|
810
|
+
* A2A protocol configuration. When set, the gateway exposes the A2A
|
|
811
|
+
* surface alongside its OpenAI-compatible endpoints:
|
|
812
|
+
* GET /:slug/.well-known/agent.json — AgentCard discovery
|
|
813
|
+
* POST /:slug — JSON-RPC 2.0 endpoint
|
|
814
|
+
* methods: message/send, message/stream, tasks/get, tasks/cancel
|
|
815
|
+
* Auth + rate-limit + injection-filter + authorization all share the
|
|
816
|
+
* same pipeline as the OpenAI-compat path. `taskStore` defaults to
|
|
817
|
+
* `InMemoryTaskStore`; swap in D1/postgres/DO for durable deployments.
|
|
818
|
+
*/
|
|
819
|
+
a2a?: {
|
|
820
|
+
/**
|
|
821
|
+
* Where tasks live. Defaults to `InMemoryTaskStore`; swap in
|
|
822
|
+
* `SqlTaskStore` (D1, postgres, sqlite, libSQL) for durability across
|
|
823
|
+
* gateway restarts.
|
|
824
|
+
*/
|
|
825
|
+
taskStore?: TaskStore;
|
|
826
|
+
/**
|
|
827
|
+
* Where push notification configs live. When set, the gateway advertises
|
|
828
|
+
* `capabilities.pushNotifications: true` and exposes the four
|
|
829
|
+
* `tasks/pushNotificationConfig/*` JSON-RPC methods. Defaults to
|
|
830
|
+
* undefined (push support disabled), so the agent card honestly reflects
|
|
831
|
+
* what the gateway will actually do.
|
|
832
|
+
*/
|
|
833
|
+
pushStore?: PushNotificationStore;
|
|
834
|
+
/**
|
|
835
|
+
* Shared HMAC secret used to sign webhook deliveries (`X-A2A-Signature:
|
|
836
|
+
* sha256=<hex>`). The consumer's webhook verifies the body against this
|
|
837
|
+
* secret to confirm the call originated from this gateway. Required when
|
|
838
|
+
* `pushStore` is set; without it, deliveries fire unsigned and a
|
|
839
|
+
* malicious party that knows the webhook URL can forge deliveries.
|
|
840
|
+
*/
|
|
841
|
+
webhookSecret?: string;
|
|
842
|
+
/**
|
|
843
|
+
* Optional fetcher override for webhook delivery. Defaults to global
|
|
844
|
+
* `fetch`. Override for tests or to wire a queue-backed sender.
|
|
845
|
+
*/
|
|
846
|
+
pushFetcher?: typeof fetch;
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
interface ChatMessage {
|
|
850
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
851
|
+
content: string;
|
|
852
|
+
}
|
|
853
|
+
interface ChatCompletionRequest {
|
|
854
|
+
model?: string;
|
|
855
|
+
messages: ChatMessage[];
|
|
856
|
+
stream?: boolean;
|
|
857
|
+
temperature?: number;
|
|
858
|
+
max_tokens?: number;
|
|
859
|
+
}
|
|
860
|
+
interface ChatCompletionChunk {
|
|
861
|
+
id: string;
|
|
862
|
+
object: 'chat.completion.chunk';
|
|
863
|
+
created: number;
|
|
864
|
+
model: string;
|
|
865
|
+
choices: Array<{
|
|
866
|
+
index: number;
|
|
867
|
+
delta: {
|
|
868
|
+
content?: string;
|
|
869
|
+
role?: string;
|
|
870
|
+
};
|
|
871
|
+
finish_reason: string | null;
|
|
872
|
+
}>;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
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 };
|