@agentuity/core 1.0.29 → 1.0.30

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.
@@ -2,6 +2,18 @@ import { FetchAdapter } from './adapter.ts';
2
2
  import { buildUrl, toServiceException } from './_util.ts';
3
3
  import { StructuredError } from '../error.ts';
4
4
 
5
+ /**
6
+ * Creates an {@link AbortSignal} that will abort after the specified timeout.
7
+ *
8
+ * @remarks
9
+ * Falls back to a manual `AbortController` + `setTimeout` if `AbortSignal.timeout`
10
+ * is not available in the runtime.
11
+ *
12
+ * @param ms - Timeout in milliseconds
13
+ * @returns An abort signal that triggers after `ms` milliseconds
14
+ *
15
+ * @default 30000
16
+ */
5
17
  function createTimeoutSignal(ms = 30_000): AbortSignal {
6
18
  if (typeof AbortSignal.timeout === 'function') {
7
19
  return AbortSignal.timeout(ms);
@@ -12,120 +24,329 @@ function createTimeoutSignal(ms = 30_000): AbortSignal {
12
24
  return controller.signal;
13
25
  }
14
26
 
27
+ /**
28
+ * A webhook endpoint that can receive HTTP requests. Each webhook has a unique
29
+ * ingest URL and can forward received payloads to configured destinations.
30
+ */
15
31
  export interface Webhook {
32
+ /** Unique identifier for the webhook. */
16
33
  id: string;
34
+
35
+ /** ISO 8601 timestamp when the webhook was created. */
17
36
  created_at: string;
37
+
38
+ /** ISO 8601 timestamp when the webhook was last modified. */
18
39
  updated_at: string;
40
+
41
+ /** ID of the user who created the webhook. */
19
42
  created_by: string;
43
+
44
+ /** Human-readable name for the webhook. */
20
45
  name: string;
46
+
47
+ /** Optional description of the webhook's purpose. */
21
48
  description: string | null;
49
+
50
+ /**
51
+ * The fully-qualified ingest URL where HTTP requests should be sent.
52
+ *
53
+ * @remarks Format: `https://<catalyst-url>/webhook/<orgId>-<webhookId>`
54
+ */
22
55
  url: string;
23
56
  }
24
57
 
58
+ /**
59
+ * A delivery target for a webhook. When an HTTP request is received at the
60
+ * webhook's ingest URL, the payload is forwarded to each of its destinations.
61
+ */
25
62
  export interface WebhookDestination {
63
+ /** Unique identifier for the destination. */
26
64
  id: string;
65
+
66
+ /** The ID of the parent webhook. */
27
67
  webhook_id: string;
68
+
69
+ /** ISO 8601 timestamp when the destination was created. */
28
70
  created_at: string;
71
+
72
+ /** ISO 8601 timestamp when the destination was last modified. */
29
73
  updated_at: string;
74
+
75
+ /** ID of the user who created the destination. */
30
76
  created_by: string;
77
+
78
+ /**
79
+ * The destination type.
80
+ *
81
+ * @remarks Currently only `'url'` is supported.
82
+ */
31
83
  type: string;
84
+
85
+ /**
86
+ * Destination-specific configuration.
87
+ *
88
+ * @remarks For `'url'` type: `{ url: string, headers?: Record<string, string> }`.
89
+ * The URL must use http or https.
90
+ */
32
91
  config: Record<string, unknown>;
33
92
  }
34
93
 
94
+ /**
95
+ * A record of an HTTP request received at a webhook's ingest URL. Each receipt
96
+ * captures the request headers and payload for auditing and replay.
97
+ */
35
98
  export interface WebhookReceipt {
99
+ /** Unique identifier for the receipt. */
36
100
  id: string;
101
+
102
+ /** ISO 8601 timestamp when the request was received. */
37
103
  date: string;
104
+
105
+ /** The ID of the webhook that received the request. */
38
106
  webhook_id: string;
107
+
108
+ /** HTTP headers from the incoming request. */
39
109
  headers: Record<string, string>;
110
+
111
+ /** The request body/payload, preserved in its original form. */
40
112
  payload: unknown;
41
113
  }
42
114
 
115
+ /**
116
+ * A record of a delivery attempt from a webhook receipt to a destination.
117
+ * Tracks the forwarding status and supports retries for failed deliveries.
118
+ */
43
119
  export interface WebhookDelivery {
120
+ /** Unique identifier for the delivery attempt. */
44
121
  id: string;
122
+
123
+ /** ISO 8601 timestamp when the delivery was attempted. */
45
124
  date: string;
125
+
126
+ /** The ID of the parent webhook. */
46
127
  webhook_id: string;
128
+
129
+ /** The ID of the destination this delivery was sent to. */
47
130
  webhook_destination_id: string;
131
+
132
+ /** The ID of the receipt that triggered this delivery. */
48
133
  webhook_receipt_id: string;
134
+
135
+ /**
136
+ * Delivery status.
137
+ *
138
+ * - `'pending'` — Queued for delivery.
139
+ * - `'success'` — Successfully delivered.
140
+ * - `'failed'` — Delivery error occurred.
141
+ */
49
142
  status: 'pending' | 'success' | 'failed';
143
+
144
+ /** Number of retry attempts made. */
50
145
  retries: number;
146
+
147
+ /** Error message if delivery failed, `null` on success. */
51
148
  error: string | null;
149
+
150
+ /** The response received from the destination endpoint, or `null`. */
52
151
  response: Record<string, unknown> | null;
53
152
  }
54
153
 
154
+ /**
155
+ * Parameters for creating a new webhook.
156
+ */
55
157
  export interface CreateWebhookParams {
158
+ /** Human-readable name for the webhook (required). */
56
159
  name: string;
160
+
161
+ /** Optional description of the webhook's purpose. */
57
162
  description?: string;
58
163
  }
59
164
 
165
+ /**
166
+ * Parameters for updating a webhook. Only provided fields are modified.
167
+ */
60
168
  export interface UpdateWebhookParams {
169
+ /** Updated name. */
61
170
  name?: string;
171
+
172
+ /** Updated description. */
62
173
  description?: string;
63
174
  }
64
175
 
176
+ /**
177
+ * Parameters for creating a new webhook destination.
178
+ */
65
179
  export interface CreateWebhookDestinationParams {
180
+ /**
181
+ * The destination type.
182
+ *
183
+ * @remarks Currently only `'url'` is supported.
184
+ */
66
185
  type: string;
186
+
187
+ /**
188
+ * Type-specific configuration.
189
+ *
190
+ * @remarks For `'url'` type: `{ url: string, headers?: Record<string, string> }`.
191
+ */
67
192
  config: Record<string, unknown>;
68
193
  }
69
194
 
195
+ /**
196
+ * Paginated list of webhooks.
197
+ */
70
198
  export interface WebhookListResult {
199
+ /** Array of webhooks. */
71
200
  webhooks: Webhook[];
201
+
202
+ /** Total number of webhooks. */
72
203
  total: number;
73
204
  }
74
205
 
206
+ /**
207
+ * Result of fetching a single webhook, including its configured destinations.
208
+ */
75
209
  export interface WebhookGetResult {
210
+ /** The requested webhook. */
76
211
  webhook: Webhook;
212
+
213
+ /** Array of destinations configured for this webhook. */
77
214
  destinations: WebhookDestination[];
78
215
  }
79
216
 
217
+ /**
218
+ * Result of creating a webhook.
219
+ */
80
220
  export interface WebhookCreateResult {
221
+ /** The newly created webhook. */
81
222
  webhook: Webhook;
82
223
  }
83
224
 
225
+ /**
226
+ * Result of updating a webhook.
227
+ */
84
228
  export interface UpdateWebhookResult {
229
+ /** The updated webhook. */
85
230
  webhook: Webhook;
86
231
  }
87
232
 
233
+ /**
234
+ * Result of creating a webhook destination.
235
+ */
88
236
  export interface CreateDestinationResult {
237
+ /** The newly created destination. */
89
238
  destination: WebhookDestination;
90
239
  }
91
240
 
241
+ /**
242
+ * List of destinations for a webhook.
243
+ */
92
244
  export interface ListDestinationsResult {
245
+ /** Array of destinations. */
93
246
  destinations: WebhookDestination[];
94
247
  }
95
248
 
249
+ /**
250
+ * List of receipts (incoming requests) for a webhook.
251
+ */
96
252
  export interface WebhookReceiptListResult {
253
+ /** Array of receipt records. */
97
254
  receipts: WebhookReceipt[];
98
255
  }
99
256
 
257
+ /**
258
+ * List of delivery attempts for a webhook.
259
+ */
100
260
  export interface WebhookDeliveryListResult {
261
+ /** Array of delivery records. */
101
262
  deliveries: WebhookDelivery[];
102
263
  }
103
264
 
265
+ /**
266
+ * Internal API success response envelope for webhook operations.
267
+ */
104
268
  interface WebhookSuccessResponse<T> {
105
269
  success: true;
106
270
  data: T;
107
271
  }
108
272
 
273
+ /**
274
+ * Internal API error response envelope for webhook operations.
275
+ */
109
276
  interface WebhookErrorResponse {
110
277
  success: false;
111
278
  message: string;
112
279
  }
113
280
 
281
+ /**
282
+ * Discriminated union of API success and error responses for webhook operations.
283
+ */
114
284
  type WebhookResponse<T> = WebhookSuccessResponse<T> | WebhookErrorResponse;
115
285
 
286
+ /**
287
+ * Thrown when the API returns a success HTTP status but the response body indicates failure.
288
+ */
116
289
  const WebhookResponseError = StructuredError('WebhookResponseError')<{
117
290
  status: number;
118
291
  }>();
119
292
 
293
+ /**
294
+ * Client for the Agentuity Webhook service.
295
+ *
296
+ * Provides methods for creating and managing webhook endpoints that can receive
297
+ * HTTP requests and forward them to configured destinations. The service supports:
298
+ *
299
+ * - **Webhooks**: Named endpoints with unique ingest URLs
300
+ * - **Destinations**: URL-based targets that receive forwarded payloads
301
+ * - **Receipts**: Records of incoming HTTP requests for auditing
302
+ * - **Deliveries**: Records of forwarding attempts with retry support
303
+ *
304
+ * When an HTTP request hits a webhook's ingest URL, it is recorded as a receipt
305
+ * and then delivered to all configured destinations. Failed deliveries can be
306
+ * retried via {@link WebhookService.retryDelivery}.
307
+ *
308
+ * All methods are instrumented with OpenTelemetry spans for observability.
309
+ *
310
+ * @example
311
+ * ```typescript
312
+ * const webhooks = new WebhookService(baseUrl, adapter);
313
+ *
314
+ * // Create a webhook
315
+ * const { webhook } = await webhooks.create({ name: 'GitHub Events' });
316
+ * console.log('Ingest URL:', webhook.url);
317
+ *
318
+ * // Add a destination
319
+ * await webhooks.createDestination(webhook.id, {
320
+ * type: 'url',
321
+ * config: { url: 'https://example.com/handle-github' },
322
+ * });
323
+ *
324
+ * // Check delivery history
325
+ * const { deliveries } = await webhooks.listDeliveries(webhook.id);
326
+ * ```
327
+ */
120
328
  export class WebhookService {
121
329
  #adapter: FetchAdapter;
122
330
  #baseUrl: string;
123
331
 
332
+ /**
333
+ * Creates a new WebhookService instance.
334
+ *
335
+ * @param baseUrl - The base URL of the webhook API
336
+ * @param adapter - The HTTP fetch adapter used for making API requests
337
+ */
124
338
  constructor(baseUrl: string, adapter: FetchAdapter) {
125
339
  this.#adapter = adapter;
126
340
  this.#baseUrl = baseUrl;
127
341
  }
128
342
 
343
+ /**
344
+ * Unwrap a potentially nested response envelope. Some API responses wrap the data
345
+ * in an additional `{ data: T }` layer; this method normalizes both shapes.
346
+ *
347
+ * @param raw - The raw response data to unwrap
348
+ * @returns The unwrapped data of type `T`
349
+ */
129
350
  #unwrap<T>(raw: unknown): T {
130
351
  if (raw !== null && typeof raw === 'object' && 'data' in raw) {
131
352
  return (raw as Record<string, unknown>).data as T;
@@ -133,6 +354,22 @@ export class WebhookService {
133
354
  return raw as T;
134
355
  }
135
356
 
357
+ /**
358
+ * Create a new webhook endpoint.
359
+ *
360
+ * @param params - The webhook creation parameters including name and optional description
361
+ * @returns The newly created webhook with its unique ingest URL
362
+ * @throws {@link ServiceException} if the API request fails
363
+ *
364
+ * @example
365
+ * ```typescript
366
+ * const { webhook } = await webhooks.create({
367
+ * name: 'Stripe Events',
368
+ * description: 'Receives payment webhooks from Stripe',
369
+ * });
370
+ * console.log('Ingest URL:', webhook.url);
371
+ * ```
372
+ */
136
373
  async create(params: CreateWebhookParams): Promise<WebhookCreateResult> {
137
374
  const url = buildUrl(this.#baseUrl, '/webhook/2026-02-24/create');
138
375
  const signal = createTimeoutSignal();
@@ -159,6 +396,22 @@ export class WebhookService {
159
396
  throw await toServiceException('POST', url, res.response);
160
397
  }
161
398
 
399
+ /**
400
+ * List all webhooks with optional pagination.
401
+ *
402
+ * @param params - Optional pagination parameters
403
+ * @returns Paginated list of webhooks with total count
404
+ * @throws {@link ServiceException} if the API request fails
405
+ *
406
+ * @example
407
+ * ```typescript
408
+ * const { webhooks, total } = await webhooks.list({ limit: 10, offset: 0 });
409
+ * console.log(`Showing ${webhooks.length} of ${total} webhooks`);
410
+ * for (const wh of webhooks) {
411
+ * console.log(`${wh.name}: ${wh.url}`);
412
+ * }
413
+ * ```
414
+ */
162
415
  async list(params?: { limit?: number; offset?: number }): Promise<WebhookListResult> {
163
416
  const qs = new URLSearchParams();
164
417
  if (params?.limit !== undefined) {
@@ -202,6 +455,19 @@ export class WebhookService {
202
455
  throw await toServiceException('GET', url, res.response);
203
456
  }
204
457
 
458
+ /**
459
+ * Get a webhook by its ID, including its configured destinations.
460
+ *
461
+ * @param webhookId - The unique webhook identifier
462
+ * @returns The webhook and its list of destinations
463
+ * @throws {@link ServiceException} if the API request fails
464
+ *
465
+ * @example
466
+ * ```typescript
467
+ * const { webhook, destinations } = await webhooks.get('wh_abc123');
468
+ * console.log(`${webhook.name} has ${destinations.length} destination(s)`);
469
+ * ```
470
+ */
205
471
  async get(webhookId: string): Promise<WebhookGetResult> {
206
472
  const url = buildUrl(
207
473
  this.#baseUrl,
@@ -230,6 +496,23 @@ export class WebhookService {
230
496
  throw await toServiceException('GET', url, res.response);
231
497
  }
232
498
 
499
+ /**
500
+ * Update a webhook's name and/or description.
501
+ *
502
+ * @param webhookId - The unique webhook identifier
503
+ * @param params - Fields to update; only provided fields are changed
504
+ * @returns The updated webhook
505
+ * @throws {@link ServiceException} if the API request fails
506
+ *
507
+ * @example
508
+ * ```typescript
509
+ * const { webhook } = await webhooks.update('wh_abc123', {
510
+ * name: 'GitHub Events (Production)',
511
+ * description: 'Production webhook for GitHub push events',
512
+ * });
513
+ * console.log('Updated:', webhook.name);
514
+ * ```
515
+ */
233
516
  async update(webhookId: string, params: UpdateWebhookParams): Promise<UpdateWebhookResult> {
234
517
  const url = buildUrl(
235
518
  this.#baseUrl,
@@ -259,6 +542,18 @@ export class WebhookService {
259
542
  throw await toServiceException('PUT', url, res.response);
260
543
  }
261
544
 
545
+ /**
546
+ * Delete a webhook and all its associated destinations, receipts, and deliveries.
547
+ *
548
+ * @param webhookId - The unique webhook identifier
549
+ * @throws {@link ServiceException} if the API request fails
550
+ *
551
+ * @example
552
+ * ```typescript
553
+ * await webhooks.delete('wh_abc123');
554
+ * console.log('Webhook deleted');
555
+ * ```
556
+ */
262
557
  async delete(webhookId: string): Promise<void> {
263
558
  const url = buildUrl(
264
559
  this.#baseUrl,
@@ -289,6 +584,27 @@ export class WebhookService {
289
584
  throw await toServiceException('DELETE', url, res.response);
290
585
  }
291
586
 
587
+ /**
588
+ * Create a new destination for a webhook. When the webhook receives a request,
589
+ * the payload will be forwarded to this destination.
590
+ *
591
+ * @param webhookId - The ID of the parent webhook
592
+ * @param params - Destination configuration including type and type-specific config
593
+ * @returns The newly created destination
594
+ * @throws {@link ServiceException} if the API request fails
595
+ *
596
+ * @example
597
+ * ```typescript
598
+ * const { destination } = await webhooks.createDestination('wh_abc123', {
599
+ * type: 'url',
600
+ * config: {
601
+ * url: 'https://example.com/webhook-handler',
602
+ * headers: { 'X-Custom-Header': 'my-value' },
603
+ * },
604
+ * });
605
+ * console.log('Destination created:', destination.id);
606
+ * ```
607
+ */
292
608
  async createDestination(
293
609
  webhookId: string,
294
610
  params: CreateWebhookDestinationParams
@@ -322,6 +638,21 @@ export class WebhookService {
322
638
  throw await toServiceException('POST', url, res.response);
323
639
  }
324
640
 
641
+ /**
642
+ * List all destinations configured for a webhook.
643
+ *
644
+ * @param webhookId - The ID of the webhook
645
+ * @returns List of destinations
646
+ * @throws {@link ServiceException} if the API request fails
647
+ *
648
+ * @example
649
+ * ```typescript
650
+ * const { destinations } = await webhooks.listDestinations('wh_abc123');
651
+ * for (const dest of destinations) {
652
+ * console.log(`${dest.type}: ${JSON.stringify(dest.config)}`);
653
+ * }
654
+ * ```
655
+ */
325
656
  async listDestinations(webhookId: string): Promise<ListDestinationsResult> {
326
657
  const url = buildUrl(
327
658
  this.#baseUrl,
@@ -349,6 +680,19 @@ export class WebhookService {
349
680
  throw await toServiceException('GET', url, res.response);
350
681
  }
351
682
 
683
+ /**
684
+ * Delete a destination from a webhook.
685
+ *
686
+ * @param webhookId - The ID of the parent webhook
687
+ * @param destinationId - The ID of the destination to delete
688
+ * @throws {@link ServiceException} if the API request fails
689
+ *
690
+ * @example
691
+ * ```typescript
692
+ * await webhooks.deleteDestination('wh_abc123', 'dest_xyz789');
693
+ * console.log('Destination deleted');
694
+ * ```
695
+ */
352
696
  async deleteDestination(webhookId: string, destinationId: string): Promise<void> {
353
697
  const url = buildUrl(
354
698
  this.#baseUrl,
@@ -380,6 +724,24 @@ export class WebhookService {
380
724
  throw await toServiceException('DELETE', url, res.response);
381
725
  }
382
726
 
727
+ /**
728
+ * List receipts (records of incoming HTTP requests) for a webhook.
729
+ *
730
+ * @param webhookId - The ID of the webhook
731
+ * @param params - Optional pagination parameters
732
+ * @returns List of receipt records
733
+ * @throws {@link ServiceException} if the API request fails
734
+ *
735
+ * @example
736
+ * ```typescript
737
+ * const { receipts } = await webhooks.listReceipts('wh_abc123', {
738
+ * limit: 50,
739
+ * });
740
+ * for (const receipt of receipts) {
741
+ * console.log(`${receipt.date}: ${JSON.stringify(receipt.headers)}`);
742
+ * }
743
+ * ```
744
+ */
383
745
  async listReceipts(
384
746
  webhookId: string,
385
747
  params?: { limit?: number; offset?: number }
@@ -419,6 +781,21 @@ export class WebhookService {
419
781
  throw await toServiceException('GET', url, res.response);
420
782
  }
421
783
 
784
+ /**
785
+ * Get a single receipt by its ID, including the full payload.
786
+ *
787
+ * @param webhookId - The ID of the webhook
788
+ * @param receiptId - The unique receipt identifier
789
+ * @returns The receipt record with headers and payload
790
+ * @throws {@link ServiceException} if the API request fails
791
+ *
792
+ * @example
793
+ * ```typescript
794
+ * const receipt = await webhooks.getReceipt('wh_abc123', 'rcpt_def456');
795
+ * console.log('Payload:', receipt.payload);
796
+ * console.log('Headers:', receipt.headers);
797
+ * ```
798
+ */
422
799
  async getReceipt(webhookId: string, receiptId: string): Promise<WebhookReceipt> {
423
800
  const url = buildUrl(
424
801
  this.#baseUrl,
@@ -447,6 +824,22 @@ export class WebhookService {
447
824
  throw await toServiceException('GET', url, res.response);
448
825
  }
449
826
 
827
+ /**
828
+ * List delivery attempts for a webhook with optional pagination.
829
+ *
830
+ * @param webhookId - The ID of the webhook
831
+ * @param params - Optional pagination parameters
832
+ * @returns List of delivery records showing forwarding status
833
+ * @throws {@link ServiceException} if the API request fails
834
+ *
835
+ * @example
836
+ * ```typescript
837
+ * const { deliveries } = await webhooks.listDeliveries('wh_abc123');
838
+ * for (const d of deliveries) {
839
+ * console.log(`${d.status} → dest ${d.webhook_destination_id} (retries: ${d.retries})`);
840
+ * }
841
+ * ```
842
+ */
450
843
  async listDeliveries(
451
844
  webhookId: string,
452
845
  params?: { limit?: number; offset?: number }
@@ -486,6 +879,25 @@ export class WebhookService {
486
879
  throw await toServiceException('GET', url, res.response);
487
880
  }
488
881
 
882
+ /**
883
+ * Retry a failed delivery attempt. Only deliveries with `'failed'` status can be retried.
884
+ *
885
+ * @param webhookId - The ID of the parent webhook
886
+ * @param deliveryId - The ID of the failed delivery to retry
887
+ * @throws {@link ServiceException} if the API request fails
888
+ *
889
+ * @example
890
+ * ```typescript
891
+ * // Find and retry failed deliveries
892
+ * const { deliveries } = await webhooks.listDeliveries('wh_abc123');
893
+ * for (const d of deliveries) {
894
+ * if (d.status === 'failed') {
895
+ * await webhooks.retryDelivery('wh_abc123', d.id);
896
+ * console.log('Retrying delivery:', d.id);
897
+ * }
898
+ * }
899
+ * ```
900
+ */
489
901
  async retryDelivery(webhookId: string, deliveryId: string): Promise<void> {
490
902
  const url = buildUrl(
491
903
  this.#baseUrl,