@agentuity/core 1.0.23 → 1.0.25

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,30 @@ import { safeStringify } from '../json.ts';
2
2
  import type { Body, HttpMethod } from './adapter.ts';
3
3
  import { ServiceException } from './exception.ts';
4
4
 
5
+ /**
6
+ * Extract a human-readable error message from an HTML error page or plain text body.
7
+ * Looks for content inside `<p>` tags first (Agentuity error pages put the message there),
8
+ * then falls back to the raw body.
9
+ */
10
+ function extractMessageFromBody(body: string): string {
11
+ if (body.includes('<html') || body.includes('<!DOCTYPE')) {
12
+ // Try to extract message from <p> tags (Agentuity error pages use <p> for the message)
13
+ const pMatch = /<p[^>]*>([^<]+)<\/p>/i.exec(body);
14
+ if (pMatch?.[1]) {
15
+ // Strip ref suffix like "(ref: sess_xxx)" to get clean message
16
+ const msg = pMatch[1].trim();
17
+ const refMatch = /^(.+?)\s*\(ref:\s*\S+\)$/.exec(msg);
18
+ return refMatch?.[1]?.trim() ?? msg;
19
+ }
20
+ // Try <h1> as fallback
21
+ const h1Match = /<h1[^>]*>([^<]+)<\/h1>/i.exec(body);
22
+ if (h1Match?.[1]) {
23
+ return h1Match[1].trim();
24
+ }
25
+ }
26
+ return body;
27
+ }
28
+
5
29
  export const buildUrl = (
6
30
  base: string,
7
31
  path: string,
@@ -90,8 +114,10 @@ export async function toServiceException(
90
114
  }
91
115
  try {
92
116
  const body = await response.text();
117
+ // If the response is HTML (e.g. server error page), extract the human-readable message
118
+ const message = extractMessageFromBody(body);
93
119
  return new ServiceException({
94
- message: body,
120
+ message,
95
121
  method,
96
122
  url,
97
123
  statusCode: response.status,
@@ -36,8 +36,10 @@ export interface EmailInbound {
36
36
  to: string;
37
37
  subject?: string;
38
38
  text?: string;
39
- status?: string;
39
+ html?: string;
40
40
  received_at?: string;
41
+ headers?: Record<string, unknown>;
42
+ attachments?: EmailStoredAttachment[];
41
43
  }
42
44
 
43
45
  /**
@@ -52,9 +54,9 @@ export interface EmailOutbound {
52
54
  html?: string;
53
55
  status?: string;
54
56
  error?: string;
55
- sent_at?: string;
56
57
  created_at?: string;
57
- updated_at?: string;
58
+ headers?: Record<string, unknown>;
59
+ attachments?: EmailStoredAttachment[];
58
60
  }
59
61
 
60
62
  /**
@@ -77,6 +79,25 @@ export interface EmailAttachment {
77
79
  contentType?: string;
78
80
  }
79
81
 
82
+ /**
83
+ * A stored email attachment with S3 location metadata.
84
+ * Returned by inbound/outbound email queries — different from EmailAttachment used for sending.
85
+ */
86
+ export interface EmailStoredAttachment {
87
+ /** The original filename */
88
+ filename: string;
89
+ /** The MIME content type */
90
+ content_type?: string;
91
+ /** File size in bytes */
92
+ size: number;
93
+ /** The S3 bucket name where the attachment is stored */
94
+ bucket: string;
95
+ /** The S3 object key */
96
+ key: string;
97
+ /** Optional pre-signed download URL */
98
+ url?: string;
99
+ }
100
+
80
101
  /**
81
102
  * Parameters for sending an email
82
103
  */
@@ -110,6 +131,11 @@ export interface EmailSendParams {
110
131
  * File attachments
111
132
  */
112
133
  attachments?: EmailAttachment[];
134
+
135
+ /**
136
+ * Custom email headers (e.g., In-Reply-To, References for threading)
137
+ */
138
+ headers?: Record<string, string>;
113
139
  }
114
140
 
115
141
  /**
@@ -276,6 +302,18 @@ export interface EmailService {
276
302
  */
277
303
  getInbound(id: string): Promise<EmailInbound | null>;
278
304
 
305
+ /**
306
+ * Delete an inbound email by ID
307
+ *
308
+ * @param id - the inbound email ID
309
+ *
310
+ * @example
311
+ * ```typescript
312
+ * await email.deleteInbound('inb_abc123');
313
+ * ```
314
+ */
315
+ deleteInbound(id: string): Promise<void>;
316
+
279
317
  /**
280
318
  * List outbound emails
281
319
  *
@@ -307,6 +345,18 @@ export interface EmailService {
307
345
  * ```
308
346
  */
309
347
  getOutbound(id: string): Promise<EmailOutbound | null>;
348
+
349
+ /**
350
+ * Delete an outbound email by ID
351
+ *
352
+ * @param id - the outbound email ID
353
+ *
354
+ * @example
355
+ * ```typescript
356
+ * await email.deleteOutbound('out_abc123');
357
+ * ```
358
+ */
359
+ deleteOutbound(id: string): Promise<void>;
310
360
  }
311
361
 
312
362
  /**
@@ -382,10 +432,7 @@ export class EmailStorageService implements EmailService {
382
432
  }
383
433
 
384
434
  async getAddress(id: string): Promise<EmailAddress | null> {
385
- const url = buildUrl(
386
- this.#baseUrl,
387
- `/email/2025-03-17/addresses/${encodeURIComponent(id)}`
388
- );
435
+ const url = buildUrl(this.#baseUrl, `/email/2025-03-17/addresses/${encodeURIComponent(id)}`);
389
436
  const signal = AbortSignal.timeout(30_000);
390
437
  const res = await this.#adapter.invoke<unknown>(url, {
391
438
  method: 'GET',
@@ -407,10 +454,7 @@ export class EmailStorageService implements EmailService {
407
454
  }
408
455
 
409
456
  async deleteAddress(id: string): Promise<void> {
410
- const url = buildUrl(
411
- this.#baseUrl,
412
- `/email/2025-03-17/addresses/${encodeURIComponent(id)}`
413
- );
457
+ const url = buildUrl(this.#baseUrl, `/email/2025-03-17/addresses/${encodeURIComponent(id)}`);
414
458
  const signal = AbortSignal.timeout(30_000);
415
459
  const res = await this.#adapter.invoke<unknown>(url, {
416
460
  method: 'DELETE',
@@ -525,10 +569,13 @@ export class EmailStorageService implements EmailService {
525
569
  if (params.attachments && params.attachments.length > 0) {
526
570
  body.attachments = params.attachments.map((a) => ({
527
571
  filename: a.filename,
528
- content_base64: a.content,
572
+ content: a.content,
529
573
  ...(a.contentType && { content_type: a.contentType }),
530
574
  }));
531
575
  }
576
+ if (params.headers && Object.keys(params.headers).length > 0) {
577
+ body.headers = params.headers;
578
+ }
532
579
 
533
580
  const res = await this.#adapter.invoke<unknown>(url, {
534
581
  method: 'POST',
@@ -581,10 +628,7 @@ export class EmailStorageService implements EmailService {
581
628
  }
582
629
 
583
630
  async getInbound(id: string): Promise<EmailInbound | null> {
584
- const url = buildUrl(
585
- this.#baseUrl,
586
- `/email/2025-03-17/inbound/${encodeURIComponent(id)}`
587
- );
631
+ const url = buildUrl(this.#baseUrl, `/email/2025-03-17/inbound/${encodeURIComponent(id)}`);
588
632
  const signal = AbortSignal.timeout(30_000);
589
633
  const res = await this.#adapter.invoke<unknown>(url, {
590
634
  method: 'GET',
@@ -605,6 +649,25 @@ export class EmailStorageService implements EmailService {
605
649
  throw await toServiceException('GET', url, res.response);
606
650
  }
607
651
 
652
+ async deleteInbound(id: string): Promise<void> {
653
+ const url = buildUrl(this.#baseUrl, `/email/2025-03-17/inbound/${encodeURIComponent(id)}`);
654
+ const signal = AbortSignal.timeout(30_000);
655
+ const res = await this.#adapter.invoke<unknown>(url, {
656
+ method: 'DELETE',
657
+ signal,
658
+ telemetry: {
659
+ name: 'agentuity.email.deleteInbound',
660
+ attributes: {
661
+ id,
662
+ },
663
+ },
664
+ });
665
+ if (res.ok || res.response.status === 404) {
666
+ return;
667
+ }
668
+ throw await toServiceException('DELETE', url, res.response);
669
+ }
670
+
608
671
  async listOutbound(addressId?: string): Promise<EmailOutbound[]> {
609
672
  const queryParams = new URLSearchParams();
610
673
  if (addressId) {
@@ -637,10 +700,7 @@ export class EmailStorageService implements EmailService {
637
700
  }
638
701
 
639
702
  async getOutbound(id: string): Promise<EmailOutbound | null> {
640
- const url = buildUrl(
641
- this.#baseUrl,
642
- `/email/2025-03-17/outbound/${encodeURIComponent(id)}`
643
- );
703
+ const url = buildUrl(this.#baseUrl, `/email/2025-03-17/outbound/${encodeURIComponent(id)}`);
644
704
  const signal = AbortSignal.timeout(30_000);
645
705
  const res = await this.#adapter.invoke<unknown>(url, {
646
706
  method: 'GET',
@@ -660,4 +720,23 @@ export class EmailStorageService implements EmailService {
660
720
  }
661
721
  throw await toServiceException('GET', url, res.response);
662
722
  }
723
+
724
+ async deleteOutbound(id: string): Promise<void> {
725
+ const url = buildUrl(this.#baseUrl, `/email/2025-03-17/outbound/${encodeURIComponent(id)}`);
726
+ const signal = AbortSignal.timeout(30_000);
727
+ const res = await this.#adapter.invoke<unknown>(url, {
728
+ method: 'DELETE',
729
+ signal,
730
+ telemetry: {
731
+ name: 'agentuity.email.deleteOutbound',
732
+ attributes: {
733
+ id,
734
+ },
735
+ },
736
+ });
737
+ if (res.ok || res.response.status === 404) {
738
+ return;
739
+ }
740
+ throw await toServiceException('DELETE', url, res.response);
741
+ }
663
742
  }
@@ -9,5 +9,23 @@ export * from './session.ts';
9
9
  export * from './stream.ts';
10
10
  export * from './task.ts';
11
11
  export * from './vector.ts';
12
+ export { WebhookService } from './webhook.ts';
13
+ export type {
14
+ Webhook,
15
+ WebhookDestination,
16
+ WebhookReceipt,
17
+ WebhookDelivery,
18
+ CreateWebhookParams,
19
+ UpdateWebhookParams,
20
+ CreateWebhookDestinationParams,
21
+ WebhookListResult,
22
+ WebhookGetResult,
23
+ WebhookCreateResult,
24
+ UpdateWebhookResult,
25
+ CreateDestinationResult,
26
+ ListDestinationsResult,
27
+ WebhookReceiptListResult,
28
+ WebhookDeliveryListResult,
29
+ } from './webhook.ts';
12
30
  export * from './email.ts';
13
31
  export { buildUrl, toServiceException, toPayload, fromResponse } from './_util.ts';