@poncho-ai/messaging 0.2.6 → 0.2.8

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @poncho-ai/messaging@0.2.6 build /home/runner/work/poncho-ai/poncho-ai/packages/messaging
2
+ > @poncho-ai/messaging@0.2.8 build /home/runner/work/poncho-ai/poncho-ai/packages/messaging
3
3
  > tsup src/index.ts --format esm --dts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -7,8 +7,8 @@
7
7
  CLI tsup v8.5.1
8
8
  CLI Target: es2022
9
9
  ESM Build start
10
- ESM dist/index.js 29.35 KB
11
- ESM ⚡️ Build success in 52ms
10
+ ESM dist/index.js 30.13 KB
11
+ ESM ⚡️ Build success in 77ms
12
12
  DTS Build start
13
- DTS ⚡️ Build success in 4588ms
14
- DTS dist/index.d.ts 8.89 KB
13
+ DTS ⚡️ Build success in 5331ms
14
+ DTS dist/index.d.ts 8.98 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @poncho-ai/messaging
2
2
 
3
+ ## 0.2.8
4
+
5
+ ### Patch Changes
6
+
7
+ - [`deb134e`](https://github.com/cesr/poncho-ai/commit/deb134e8a6ecf38d85dc200f57998e33406eff61) Thanks [@cesr](https://github.com/cesr)! - Retry Resend API requests on transient socket errors (common on Vercel cold starts).
8
+
9
+ ## 0.2.7
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies [[`075b9ac`](https://github.com/cesr/poncho-ai/commit/075b9ac3556847af913bf2b58f030575c3b99852)]:
14
+ - @poncho-ai/sdk@1.4.0
15
+
3
16
  ## 0.2.6
4
17
 
5
18
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -144,6 +144,7 @@ interface ResendAdapterOptions {
144
144
  apiKeyEnv?: string;
145
145
  webhookSecretEnv?: string;
146
146
  fromEnv?: string;
147
+ replyToEnv?: string;
147
148
  allowedSenders?: string[];
148
149
  mode?: "auto-reply" | "tool";
149
150
  allowedRecipients?: string[];
@@ -157,9 +158,11 @@ declare class ResendAdapter implements MessagingAdapter {
157
158
  private apiKey;
158
159
  private webhookSecret;
159
160
  private fromAddress;
161
+ private replyToAddress;
160
162
  private readonly apiKeyEnv;
161
163
  private readonly webhookSecretEnv;
162
164
  private readonly fromEnv;
165
+ private readonly replyToEnv;
163
166
  private readonly allowedSenders;
164
167
  private readonly allowedRecipients;
165
168
  private readonly maxSendsPerRun;
package/dist/index.js CHANGED
@@ -450,6 +450,20 @@ function matchesSenderPattern(sender, patterns) {
450
450
  }
451
451
 
452
452
  // src/adapters/resend/index.ts
453
+ var isSocketError = (err) => err instanceof TypeError && err.message === "fetch failed" && err.cause?.code === "UND_ERR_SOCKET";
454
+ async function fetchWithRetry(input, init, retries = 2) {
455
+ for (let attempt = 0; ; attempt++) {
456
+ try {
457
+ return await fetch(input, init);
458
+ } catch (err) {
459
+ if (attempt < retries && isSocketError(err)) {
460
+ await new Promise((r) => setTimeout(r, 200 * (attempt + 1)));
461
+ continue;
462
+ }
463
+ throw err;
464
+ }
465
+ }
466
+ }
453
467
  function verifySvixSignature(rawBody, svixId, svixTimestamp, svixSignature, secret) {
454
468
  const now = Math.floor(Date.now() / 1e3);
455
469
  const ts = parseInt(svixTimestamp, 10);
@@ -518,9 +532,11 @@ var ResendAdapter = class {
518
532
  apiKey = "";
519
533
  webhookSecret = "";
520
534
  fromAddress = "";
535
+ replyToAddress = "";
521
536
  apiKeyEnv;
522
537
  webhookSecretEnv;
523
538
  fromEnv;
539
+ replyToEnv;
524
540
  allowedSenders;
525
541
  allowedRecipients;
526
542
  maxSendsPerRun;
@@ -535,6 +551,7 @@ var ResendAdapter = class {
535
551
  this.apiKeyEnv = options.apiKeyEnv ?? "RESEND_API_KEY";
536
552
  this.webhookSecretEnv = options.webhookSecretEnv ?? "RESEND_WEBHOOK_SECRET";
537
553
  this.fromEnv = options.fromEnv ?? "RESEND_FROM";
554
+ this.replyToEnv = options.replyToEnv ?? "RESEND_REPLY_TO";
538
555
  this.allowedSenders = options.allowedSenders;
539
556
  this.mode = options.mode ?? "auto-reply";
540
557
  this.autoReply = this.mode !== "tool";
@@ -552,6 +569,7 @@ var ResendAdapter = class {
552
569
  this.apiKey = process.env[this.apiKeyEnv] ?? "";
553
570
  this.webhookSecret = process.env[this.webhookSecretEnv] ?? "";
554
571
  this.fromAddress = process.env[this.fromEnv] ?? "";
572
+ this.replyToAddress = process.env[this.replyToEnv] ?? "";
555
573
  if (!this.apiKey) {
556
574
  throw new Error(
557
575
  `Resend messaging: ${this.apiKeyEnv} environment variable is not set`
@@ -605,6 +623,7 @@ var ResendAdapter = class {
605
623
  subject,
606
624
  text: content,
607
625
  html: markdownToEmailHtml(content),
626
+ reply_to: this.replyToAddress ? [this.replyToAddress] : void 0,
608
627
  headers,
609
628
  attachments: attachments && attachments.length > 0 ? attachments : void 0
610
629
  });
@@ -712,6 +731,7 @@ var ResendAdapter = class {
712
731
  html: markdownToEmailHtml(body),
713
732
  cc: cc && cc.length > 0 ? cc : void 0,
714
733
  bcc: bcc && bcc.length > 0 ? bcc : void 0,
734
+ reply_to: this.replyToAddress ? [this.replyToAddress] : void 0,
715
735
  headers: Object.keys(headers).length > 0 ? headers : void 0
716
736
  });
717
737
  if (result.error) {
@@ -798,7 +818,7 @@ var ResendAdapter = class {
798
818
  let emailHeaders;
799
819
  if (emailId) {
800
820
  try {
801
- const resp = await fetch(
821
+ const resp = await fetchWithRetry(
802
822
  `https://api.resend.com/emails/receiving/${emailId}`,
803
823
  { headers: { Authorization: `Bearer ${this.apiKey}` } }
804
824
  );
@@ -854,7 +874,7 @@ var ResendAdapter = class {
854
874
  if (!emailId || !webhookAttachments || webhookAttachments.length === 0) return [];
855
875
  let attachments = [];
856
876
  try {
857
- const resp = await fetch(
877
+ const resp = await fetchWithRetry(
858
878
  `https://api.resend.com/emails/receiving/${emailId}/attachments`,
859
879
  { headers: { Authorization: `Bearer ${this.apiKey}` } }
860
880
  );
@@ -873,7 +893,7 @@ var ResendAdapter = class {
873
893
  for (const att of attachments) {
874
894
  if (!att.download_url) continue;
875
895
  try {
876
- const resp = await fetch(att.download_url);
896
+ const resp = await fetchWithRetry(att.download_url);
877
897
  if (!resp.ok) continue;
878
898
  const buf = Buffer.from(await resp.arrayBuffer());
879
899
  results.push({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poncho-ai/messaging",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "Messaging platform adapters for Poncho agents (Slack, Telegram, etc.)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,7 +20,7 @@
20
20
  }
21
21
  },
22
22
  "dependencies": {
23
- "@poncho-ai/sdk": "1.3.0"
23
+ "@poncho-ai/sdk": "1.4.0"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "resend": ">=4.0.0"
@@ -9,6 +9,30 @@ import type {
9
9
  RouteRegistrar,
10
10
  ThreadRef,
11
11
  } from "../../types.js";
12
+
13
+ const isSocketError = (err: unknown): boolean =>
14
+ err instanceof TypeError &&
15
+ err.message === "fetch failed" &&
16
+ (err as { cause?: { code?: string } }).cause?.code === "UND_ERR_SOCKET";
17
+
18
+ async function fetchWithRetry(
19
+ input: string | URL | Request,
20
+ init?: RequestInit,
21
+ retries = 2,
22
+ ): Promise<Response> {
23
+ for (let attempt = 0; ; attempt++) {
24
+ try {
25
+ return await fetch(input, init);
26
+ } catch (err) {
27
+ if (attempt < retries && isSocketError(err)) {
28
+ await new Promise((r) => setTimeout(r, 200 * (attempt + 1)));
29
+ continue;
30
+ }
31
+ throw err;
32
+ }
33
+ }
34
+ }
35
+
12
36
  import {
13
37
  buildReplyHeaders,
14
38
  buildReplySubject,
@@ -34,6 +58,7 @@ interface ResendClient {
34
58
  html?: string;
35
59
  cc?: string[];
36
60
  bcc?: string[];
61
+ reply_to?: string[];
37
62
  headers?: Record<string, string>;
38
63
  attachments?: Array<{ filename: string; content?: string; path?: string; contentType?: string }>;
39
64
  }): Promise<{ data?: { id: string }; error?: unknown }>;
@@ -158,6 +183,7 @@ export interface ResendAdapterOptions {
158
183
  apiKeyEnv?: string;
159
184
  webhookSecretEnv?: string;
160
185
  fromEnv?: string;
186
+ replyToEnv?: string;
161
187
  allowedSenders?: string[];
162
188
  mode?: "auto-reply" | "tool";
163
189
  allowedRecipients?: string[];
@@ -188,9 +214,11 @@ export class ResendAdapter implements MessagingAdapter {
188
214
  private apiKey = "";
189
215
  private webhookSecret = "";
190
216
  private fromAddress = "";
217
+ private replyToAddress = "";
191
218
  private readonly apiKeyEnv: string;
192
219
  private readonly webhookSecretEnv: string;
193
220
  private readonly fromEnv: string;
221
+ private readonly replyToEnv: string;
194
222
  private readonly allowedSenders: string[] | undefined;
195
223
  private readonly allowedRecipients: string[] | undefined;
196
224
  private readonly maxSendsPerRun: number;
@@ -208,6 +236,7 @@ export class ResendAdapter implements MessagingAdapter {
208
236
  this.apiKeyEnv = options.apiKeyEnv ?? "RESEND_API_KEY";
209
237
  this.webhookSecretEnv = options.webhookSecretEnv ?? "RESEND_WEBHOOK_SECRET";
210
238
  this.fromEnv = options.fromEnv ?? "RESEND_FROM";
239
+ this.replyToEnv = options.replyToEnv ?? "RESEND_REPLY_TO";
211
240
  this.allowedSenders = options.allowedSenders;
212
241
  this.mode = options.mode ?? "auto-reply";
213
242
  this.autoReply = this.mode !== "tool";
@@ -228,6 +257,7 @@ export class ResendAdapter implements MessagingAdapter {
228
257
  this.apiKey = process.env[this.apiKeyEnv] ?? "";
229
258
  this.webhookSecret = process.env[this.webhookSecretEnv] ?? "";
230
259
  this.fromAddress = process.env[this.fromEnv] ?? "";
260
+ this.replyToAddress = process.env[this.replyToEnv] ?? "";
231
261
 
232
262
  if (!this.apiKey) {
233
263
  throw new Error(
@@ -298,6 +328,7 @@ export class ResendAdapter implements MessagingAdapter {
298
328
  subject,
299
329
  text: content,
300
330
  html: markdownToEmailHtml(content),
331
+ reply_to: this.replyToAddress ? [this.replyToAddress] : undefined,
301
332
  headers,
302
333
  attachments: attachments && attachments.length > 0 ? attachments : undefined,
303
334
  });
@@ -423,6 +454,7 @@ export class ResendAdapter implements MessagingAdapter {
423
454
  html: markdownToEmailHtml(body),
424
455
  cc: cc && cc.length > 0 ? cc : undefined,
425
456
  bcc: bcc && bcc.length > 0 ? bcc : undefined,
457
+ reply_to: this.replyToAddress ? [this.replyToAddress] : undefined,
426
458
  headers: Object.keys(headers).length > 0 ? headers : undefined,
427
459
  });
428
460
 
@@ -542,7 +574,7 @@ export class ResendAdapter implements MessagingAdapter {
542
574
 
543
575
  if (emailId) {
544
576
  try {
545
- const resp = await fetch(
577
+ const resp = await fetchWithRetry(
546
578
  `https://api.resend.com/emails/receiving/${emailId}`,
547
579
  { headers: { Authorization: `Bearer ${this.apiKey}` } },
548
580
  );
@@ -616,7 +648,7 @@ export class ResendAdapter implements MessagingAdapter {
616
648
  // Fetch attachment metadata (with download_url) from the Resend API
617
649
  let attachments: Array<{ filename?: string; content_type?: string; download_url?: string }> = [];
618
650
  try {
619
- const resp = await fetch(
651
+ const resp = await fetchWithRetry(
620
652
  `https://api.resend.com/emails/receiving/${emailId}/attachments`,
621
653
  { headers: { Authorization: `Bearer ${this.apiKey}` } },
622
654
  );
@@ -636,7 +668,7 @@ export class ResendAdapter implements MessagingAdapter {
636
668
  for (const att of attachments) {
637
669
  if (!att.download_url) continue;
638
670
  try {
639
- const resp = await fetch(att.download_url);
671
+ const resp = await fetchWithRetry(att.download_url);
640
672
  if (!resp.ok) continue;
641
673
  const buf = Buffer.from(await resp.arrayBuffer());
642
674
  results.push({