@poncho-ai/messaging 0.2.7 → 0.2.9
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/.turbo/turbo-build.log +4 -4
- package/.turbo/turbo-test.log +29 -0
- package/CHANGELOG.md +12 -0
- package/dist/index.js +31 -6
- package/package.json +1 -1
- package/src/adapters/resend/index.ts +27 -3
- package/src/bridge.ts +15 -3
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @poncho-ai/messaging@0.2.
|
|
2
|
+
> @poncho-ai/messaging@0.2.9 build /Users/cesar/Dev/latitude/poncho-ai/packages/messaging
|
|
3
3
|
> tsup src/index.ts --format esm --dts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
[34mCLI[39m tsup v8.5.1
|
|
8
8
|
[34mCLI[39m Target: es2022
|
|
9
9
|
[34mESM[39m Build start
|
|
10
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
11
|
-
[32mESM[39m ⚡️ Build success in
|
|
10
|
+
[32mESM[39m [1mdist/index.js [22m[32m30.49 KB[39m
|
|
11
|
+
[32mESM[39m ⚡️ Build success in 58ms
|
|
12
12
|
[34mDTS[39m Build start
|
|
13
|
-
[32mDTS[39m ⚡️ Build success in
|
|
13
|
+
[32mDTS[39m ⚡️ Build success in 1611ms
|
|
14
14
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m8.98 KB[39m
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
|
|
2
|
+
> @poncho-ai/messaging@0.2.4 test /Users/cesar/Dev/latitude/poncho-ai/packages/messaging
|
|
3
|
+
> vitest
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
[7m[1m[36m RUN [39m[22m[27m [36mv1.6.1[39m [90m/Users/cesar/Dev/latitude/poncho-ai/packages/messaging[39m
|
|
7
|
+
|
|
8
|
+
[90mstderr[2m | test/bridge.test.ts[2m > [22m[2mAgentBridge[2m > [22m[2mposts an error message and cleans up on runner failure[22m[39m
|
|
9
|
+
[agent-bridge] handleMessage error: Model overloaded
|
|
10
|
+
|
|
11
|
+
[90mstderr[2m | test/bridge.test.ts[2m > [22m[2mAgentBridge[2m > [22m[2mskips sendReply when autoReply is false[22m[39m
|
|
12
|
+
[agent-bridge] tool mode completed without send_email being called; no reply sent
|
|
13
|
+
|
|
14
|
+
[90mstderr[2m | test/bridge.test.ts[2m > [22m[2mAgentBridge[2m > [22m[2msuppresses error reply when hasSentInCurrentRequest is true[22m[39m
|
|
15
|
+
[agent-bridge] handleMessage error: Oops
|
|
16
|
+
|
|
17
|
+
[90mstderr[2m | test/bridge.test.ts[2m > [22m[2mAgentBridge[2m > [22m[2mcalls resetRequestState before handling each message[22m[39m
|
|
18
|
+
[agent-bridge] tool mode completed without send_email being called; no reply sent
|
|
19
|
+
|
|
20
|
+
[32m✓[39m test/bridge.test.ts [2m ([22m[2m15 tests[22m[2m)[22m[90m 7[2mms[22m[39m
|
|
21
|
+
[32m✓[39m test/adapters/email-utils.test.ts [2m ([22m[2m44 tests[22m[2m)[22m[90m 13[2mms[22m[39m
|
|
22
|
+
[32m✓[39m test/adapters/resend.test.ts [2m ([22m[2m13 tests[22m[2m)[22m[90m 7[2mms[22m[39m
|
|
23
|
+
[32m✓[39m test/adapters/slack.test.ts [2m ([22m[2m17 tests[22m[2m)[22m[90m 61[2mms[22m[39m
|
|
24
|
+
|
|
25
|
+
[2m Test Files [22m [1m[32m4 passed[39m[22m[90m (4)[39m
|
|
26
|
+
[2m Tests [22m [1m[32m89 passed[39m[22m[90m (89)[39m
|
|
27
|
+
[2m Start at [22m 17:47:42
|
|
28
|
+
[2m Duration [22m 442ms[2m (transform 223ms, setup 0ms, collect 336ms, tests 88ms, environment 0ms, prepare 304ms)[22m
|
|
29
|
+
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @poncho-ai/messaging
|
|
2
2
|
|
|
3
|
+
## 0.2.9
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`e9b801f`](https://github.com/cesr/poncho-ai/commit/e9b801f0c70ffab6cb434b7adf05df22b29ea9fe) Thanks [@cesr](https://github.com/cesr)! - Derive deterministic UUIDs for messaging conversation IDs instead of composite strings. Fixes Latitude telemetry rejection and ensures consistency with web UI/API conversations.
|
|
8
|
+
|
|
9
|
+
## 0.2.8
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`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).
|
|
14
|
+
|
|
3
15
|
## 0.2.7
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
// src/bridge.ts
|
|
2
|
-
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
|
+
var conversationIdFromThread = (platform, ref) => {
|
|
4
|
+
const key = `${platform}:${ref.channelId}:${ref.platformThreadId}`;
|
|
5
|
+
const hex = createHash("sha256").update(key).digest("hex").slice(0, 32);
|
|
6
|
+
return [
|
|
7
|
+
hex.slice(0, 8),
|
|
8
|
+
hex.slice(8, 12),
|
|
9
|
+
`4${hex.slice(13, 16)}`,
|
|
10
|
+
(parseInt(hex.slice(16, 18), 16) & 63 | 128).toString(16).padStart(2, "0") + hex.slice(18, 20),
|
|
11
|
+
hex.slice(20, 32)
|
|
12
|
+
].join("-");
|
|
13
|
+
};
|
|
3
14
|
var AgentBridge = class {
|
|
4
15
|
adapter;
|
|
5
16
|
runner;
|
|
@@ -319,7 +330,7 @@ var SlackAdapter = class {
|
|
|
319
330
|
import { createHmac as createHmac2 } from "crypto";
|
|
320
331
|
|
|
321
332
|
// src/adapters/email/utils.ts
|
|
322
|
-
import { createHash } from "crypto";
|
|
333
|
+
import { createHash as createHash2 } from "crypto";
|
|
323
334
|
var ADDR_RE = /<([^>]+)>/;
|
|
324
335
|
function extractEmailAddress(formatted) {
|
|
325
336
|
const match = ADDR_RE.exec(formatted);
|
|
@@ -350,7 +361,7 @@ function deriveRootMessageId(references, currentMessageId, fallback) {
|
|
|
350
361
|
if (references.length > 0) return references[0];
|
|
351
362
|
if (fallback) {
|
|
352
363
|
const normalised = normaliseSubject(fallback.subject) + "\0" + fallback.sender.toLowerCase();
|
|
353
|
-
const hash =
|
|
364
|
+
const hash = createHash2("sha256").update(normalised).digest("hex").slice(0, 16);
|
|
354
365
|
return `<fallback:${hash}>`;
|
|
355
366
|
}
|
|
356
367
|
return currentMessageId;
|
|
@@ -450,6 +461,20 @@ function matchesSenderPattern(sender, patterns) {
|
|
|
450
461
|
}
|
|
451
462
|
|
|
452
463
|
// src/adapters/resend/index.ts
|
|
464
|
+
var isSocketError = (err) => err instanceof TypeError && err.message === "fetch failed" && err.cause?.code === "UND_ERR_SOCKET";
|
|
465
|
+
async function fetchWithRetry(input, init, retries = 2) {
|
|
466
|
+
for (let attempt = 0; ; attempt++) {
|
|
467
|
+
try {
|
|
468
|
+
return await fetch(input, init);
|
|
469
|
+
} catch (err) {
|
|
470
|
+
if (attempt < retries && isSocketError(err)) {
|
|
471
|
+
await new Promise((r) => setTimeout(r, 200 * (attempt + 1)));
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
throw err;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
453
478
|
function verifySvixSignature(rawBody, svixId, svixTimestamp, svixSignature, secret) {
|
|
454
479
|
const now = Math.floor(Date.now() / 1e3);
|
|
455
480
|
const ts = parseInt(svixTimestamp, 10);
|
|
@@ -804,7 +829,7 @@ var ResendAdapter = class {
|
|
|
804
829
|
let emailHeaders;
|
|
805
830
|
if (emailId) {
|
|
806
831
|
try {
|
|
807
|
-
const resp = await
|
|
832
|
+
const resp = await fetchWithRetry(
|
|
808
833
|
`https://api.resend.com/emails/receiving/${emailId}`,
|
|
809
834
|
{ headers: { Authorization: `Bearer ${this.apiKey}` } }
|
|
810
835
|
);
|
|
@@ -860,7 +885,7 @@ var ResendAdapter = class {
|
|
|
860
885
|
if (!emailId || !webhookAttachments || webhookAttachments.length === 0) return [];
|
|
861
886
|
let attachments = [];
|
|
862
887
|
try {
|
|
863
|
-
const resp = await
|
|
888
|
+
const resp = await fetchWithRetry(
|
|
864
889
|
`https://api.resend.com/emails/receiving/${emailId}/attachments`,
|
|
865
890
|
{ headers: { Authorization: `Bearer ${this.apiKey}` } }
|
|
866
891
|
);
|
|
@@ -879,7 +904,7 @@ var ResendAdapter = class {
|
|
|
879
904
|
for (const att of attachments) {
|
|
880
905
|
if (!att.download_url) continue;
|
|
881
906
|
try {
|
|
882
|
-
const resp = await
|
|
907
|
+
const resp = await fetchWithRetry(att.download_url);
|
|
883
908
|
if (!resp.ok) continue;
|
|
884
909
|
const buf = Buffer.from(await resp.arrayBuffer());
|
|
885
910
|
results.push({
|
package/package.json
CHANGED
|
@@ -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,
|
|
@@ -550,7 +574,7 @@ export class ResendAdapter implements MessagingAdapter {
|
|
|
550
574
|
|
|
551
575
|
if (emailId) {
|
|
552
576
|
try {
|
|
553
|
-
const resp = await
|
|
577
|
+
const resp = await fetchWithRetry(
|
|
554
578
|
`https://api.resend.com/emails/receiving/${emailId}`,
|
|
555
579
|
{ headers: { Authorization: `Bearer ${this.apiKey}` } },
|
|
556
580
|
);
|
|
@@ -624,7 +648,7 @@ export class ResendAdapter implements MessagingAdapter {
|
|
|
624
648
|
// Fetch attachment metadata (with download_url) from the Resend API
|
|
625
649
|
let attachments: Array<{ filename?: string; content_type?: string; download_url?: string }> = [];
|
|
626
650
|
try {
|
|
627
|
-
const resp = await
|
|
651
|
+
const resp = await fetchWithRetry(
|
|
628
652
|
`https://api.resend.com/emails/receiving/${emailId}/attachments`,
|
|
629
653
|
{ headers: { Authorization: `Bearer ${this.apiKey}` } },
|
|
630
654
|
);
|
|
@@ -644,7 +668,7 @@ export class ResendAdapter implements MessagingAdapter {
|
|
|
644
668
|
for (const att of attachments) {
|
|
645
669
|
if (!att.download_url) continue;
|
|
646
670
|
try {
|
|
647
|
-
const resp = await
|
|
671
|
+
const resp = await fetchWithRetry(att.download_url);
|
|
648
672
|
if (!resp.ok) continue;
|
|
649
673
|
const buf = Buffer.from(await resp.arrayBuffer());
|
|
650
674
|
results.push({
|
package/src/bridge.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import type {
|
|
2
3
|
AgentBridgeOptions,
|
|
3
4
|
IncomingMessage,
|
|
@@ -7,13 +8,24 @@ import type {
|
|
|
7
8
|
} from "./types.js";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
|
-
* Derive a
|
|
11
|
-
*
|
|
11
|
+
* Derive a deterministic UUID from a platform thread reference.
|
|
12
|
+
* SHA-256 hashes the composite key and formats 16 bytes as a UUID v4-shaped
|
|
13
|
+
* string, ensuring a valid UUID that's stable across requests for the same thread.
|
|
12
14
|
*/
|
|
13
15
|
const conversationIdFromThread = (
|
|
14
16
|
platform: string,
|
|
15
17
|
ref: ThreadRef,
|
|
16
|
-
): string =>
|
|
18
|
+
): string => {
|
|
19
|
+
const key = `${platform}:${ref.channelId}:${ref.platformThreadId}`;
|
|
20
|
+
const hex = createHash("sha256").update(key).digest("hex").slice(0, 32);
|
|
21
|
+
return [
|
|
22
|
+
hex.slice(0, 8),
|
|
23
|
+
hex.slice(8, 12),
|
|
24
|
+
`4${hex.slice(13, 16)}`,
|
|
25
|
+
((parseInt(hex.slice(16, 18), 16) & 0x3f) | 0x80).toString(16).padStart(2, "0") + hex.slice(18, 20),
|
|
26
|
+
hex.slice(20, 32),
|
|
27
|
+
].join("-");
|
|
28
|
+
};
|
|
17
29
|
|
|
18
30
|
export class AgentBridge {
|
|
19
31
|
private readonly adapter: MessagingAdapter;
|