@poncho-ai/messaging 0.2.0 → 0.2.2
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 +5 -5
- package/.turbo/turbo-test.log +29 -0
- package/CHANGELOG.md +24 -0
- package/dist/index.d.ts +151 -4
- package/dist/index.js +627 -14
- package/package.json +14 -3
- package/src/adapters/email/utils.ts +259 -0
- package/src/adapters/resend/index.ts +653 -0
- package/src/adapters/slack/index.ts +7 -1
- package/src/bridge.ts +43 -13
- package/src/index.ts +15 -0
- package/src/types.ts +53 -4
- package/test/adapters/email-utils.test.ts +290 -0
- package/test/adapters/resend.test.ts +108 -0
- package/test/bridge.test.ts +121 -8
package/test/bridge.test.ts
CHANGED
|
@@ -9,12 +9,18 @@ import type {
|
|
|
9
9
|
ThreadRef,
|
|
10
10
|
} from "../src/types.js";
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
import type { FileAttachment } from "../src/types.js";
|
|
13
|
+
|
|
14
|
+
const makeAdapter = (
|
|
15
|
+
opts: { autoReply?: boolean; hasSentInCurrentRequest?: boolean } = {},
|
|
16
|
+
): MessagingAdapter & {
|
|
13
17
|
_handler: IncomingMessageHandler | undefined;
|
|
14
|
-
_replies: Array<{ ref: ThreadRef; content: string }>;
|
|
18
|
+
_replies: Array<{ ref: ThreadRef; content: string; files?: FileAttachment[] }>;
|
|
15
19
|
_processing: ThreadRef[];
|
|
16
20
|
} => ({
|
|
17
21
|
platform: "test",
|
|
22
|
+
autoReply: opts.autoReply ?? true,
|
|
23
|
+
hasSentInCurrentRequest: opts.hasSentInCurrentRequest ?? false,
|
|
18
24
|
_handler: undefined,
|
|
19
25
|
_replies: [],
|
|
20
26
|
_processing: [],
|
|
@@ -23,8 +29,8 @@ const makeAdapter = (): MessagingAdapter & {
|
|
|
23
29
|
onMessage(handler) {
|
|
24
30
|
this._handler = handler;
|
|
25
31
|
},
|
|
26
|
-
async sendReply(ref, content) {
|
|
27
|
-
this._replies.push({ ref, content });
|
|
32
|
+
async sendReply(ref, content, options) {
|
|
33
|
+
this._replies.push({ ref, content, files: options?.files });
|
|
28
34
|
},
|
|
29
35
|
async indicateProcessing(ref) {
|
|
30
36
|
this._processing.push(ref);
|
|
@@ -37,12 +43,13 @@ const makeAdapter = (): MessagingAdapter & {
|
|
|
37
43
|
|
|
38
44
|
const makeRunner = (
|
|
39
45
|
response = "Hello from agent",
|
|
46
|
+
responseFiles?: FileAttachment[],
|
|
40
47
|
): AgentRunner & {
|
|
41
48
|
_conversations: Map<string, { messages: Array<{ role: string; content: string }> }>;
|
|
42
|
-
_runs: Array<{ id: string; task: string }>;
|
|
49
|
+
_runs: Array<{ id: string; task: string; files?: FileAttachment[]; metadata?: { platform: string; sender: { id: string; name?: string }; threadId: string } }>;
|
|
43
50
|
} => {
|
|
44
51
|
const conversations = new Map<string, { messages: Array<{ role: string; content: string }> }>();
|
|
45
|
-
const runs: Array<{ id: string; task: string }> = [];
|
|
52
|
+
const runs: Array<{ id: string; task: string; files?: FileAttachment[]; metadata?: { platform: string; sender: { id: string; name?: string }; threadId: string } }> = [];
|
|
46
53
|
return {
|
|
47
54
|
_conversations: conversations,
|
|
48
55
|
_runs: runs,
|
|
@@ -53,8 +60,8 @@ const makeRunner = (
|
|
|
53
60
|
return conversations.get(id)!;
|
|
54
61
|
},
|
|
55
62
|
async run(id, input) {
|
|
56
|
-
runs.push({ id, task: input.task });
|
|
57
|
-
return { response };
|
|
63
|
+
runs.push({ id, task: input.task, files: input.files, metadata: input.metadata });
|
|
64
|
+
return { response, files: responseFiles };
|
|
58
65
|
},
|
|
59
66
|
};
|
|
60
67
|
};
|
|
@@ -162,4 +169,110 @@ describe("AgentBridge", () => {
|
|
|
162
169
|
expect(waitUntil).toHaveBeenCalledTimes(1);
|
|
163
170
|
expect(waitUntil.mock.calls[0]![0]).toBeInstanceOf(Promise);
|
|
164
171
|
});
|
|
172
|
+
|
|
173
|
+
it("pipes inbound files from message to runner", async () => {
|
|
174
|
+
const adapter = makeAdapter();
|
|
175
|
+
const runner = makeRunner();
|
|
176
|
+
const bridge = new AgentBridge({ adapter, runner });
|
|
177
|
+
await bridge.start();
|
|
178
|
+
|
|
179
|
+
const files: FileAttachment[] = [
|
|
180
|
+
{ data: "base64data", mediaType: "image/png", filename: "screenshot.png" },
|
|
181
|
+
];
|
|
182
|
+
await adapter._handler!(sampleMessage({ files }));
|
|
183
|
+
|
|
184
|
+
expect(runner._runs[0]!.files).toEqual(files);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("pipes outbound files from runner result to adapter sendReply", async () => {
|
|
188
|
+
const outFiles: FileAttachment[] = [
|
|
189
|
+
{ data: "csvdata", mediaType: "text/csv", filename: "report.csv" },
|
|
190
|
+
];
|
|
191
|
+
const adapter = makeAdapter();
|
|
192
|
+
const runner = makeRunner("Here is the report.", outFiles);
|
|
193
|
+
const bridge = new AgentBridge({ adapter, runner });
|
|
194
|
+
await bridge.start();
|
|
195
|
+
|
|
196
|
+
await adapter._handler!(sampleMessage());
|
|
197
|
+
|
|
198
|
+
expect(adapter._replies[0]!.files).toEqual(outFiles);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("passes undefined files when message has no attachments", async () => {
|
|
202
|
+
const adapter = makeAdapter();
|
|
203
|
+
const runner = makeRunner();
|
|
204
|
+
const bridge = new AgentBridge({ adapter, runner });
|
|
205
|
+
await bridge.start();
|
|
206
|
+
|
|
207
|
+
await adapter._handler!(sampleMessage());
|
|
208
|
+
|
|
209
|
+
expect(runner._runs[0]!.files).toBeUndefined();
|
|
210
|
+
expect(adapter._replies[0]!.files).toBeUndefined();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("passes sender metadata to runner", async () => {
|
|
214
|
+
const adapter = makeAdapter();
|
|
215
|
+
const runner = makeRunner();
|
|
216
|
+
const bridge = new AgentBridge({ adapter, runner });
|
|
217
|
+
await bridge.start();
|
|
218
|
+
|
|
219
|
+
await adapter._handler!(sampleMessage());
|
|
220
|
+
|
|
221
|
+
expect(runner._runs[0]!.metadata).toEqual({
|
|
222
|
+
platform: "test",
|
|
223
|
+
sender: { id: "U123", name: "alice" },
|
|
224
|
+
threadId: "ts_123",
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("skips sendReply when autoReply is false", async () => {
|
|
229
|
+
const adapter = makeAdapter({ autoReply: false });
|
|
230
|
+
const runner = makeRunner("agent response");
|
|
231
|
+
const bridge = new AgentBridge({ adapter, runner });
|
|
232
|
+
await bridge.start();
|
|
233
|
+
|
|
234
|
+
await adapter._handler!(sampleMessage());
|
|
235
|
+
|
|
236
|
+
expect(adapter._replies).toHaveLength(0);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("sends reply when autoReply is true (default)", async () => {
|
|
240
|
+
const adapter = makeAdapter({ autoReply: true });
|
|
241
|
+
const runner = makeRunner("agent response");
|
|
242
|
+
const bridge = new AgentBridge({ adapter, runner });
|
|
243
|
+
await bridge.start();
|
|
244
|
+
|
|
245
|
+
await adapter._handler!(sampleMessage());
|
|
246
|
+
|
|
247
|
+
expect(adapter._replies).toHaveLength(1);
|
|
248
|
+
expect(adapter._replies[0]!.content).toBe("agent response");
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it("suppresses error reply when hasSentInCurrentRequest is true", async () => {
|
|
252
|
+
const adapter = makeAdapter({ autoReply: false });
|
|
253
|
+
(adapter as unknown as { hasSentInCurrentRequest: boolean }).hasSentInCurrentRequest = true;
|
|
254
|
+
const runner = makeRunner();
|
|
255
|
+
runner.run = async () => {
|
|
256
|
+
throw new Error("Oops");
|
|
257
|
+
};
|
|
258
|
+
const bridge = new AgentBridge({ adapter, runner });
|
|
259
|
+
await bridge.start();
|
|
260
|
+
|
|
261
|
+
await adapter._handler!(sampleMessage());
|
|
262
|
+
|
|
263
|
+
expect(adapter._replies).toHaveLength(0);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("calls resetRequestState before handling each message", async () => {
|
|
267
|
+
const adapter = makeAdapter({ autoReply: false });
|
|
268
|
+
const resetSpy = vi.fn();
|
|
269
|
+
adapter.resetRequestState = resetSpy;
|
|
270
|
+
const runner = makeRunner();
|
|
271
|
+
const bridge = new AgentBridge({ adapter, runner });
|
|
272
|
+
await bridge.start();
|
|
273
|
+
|
|
274
|
+
await adapter._handler!(sampleMessage());
|
|
275
|
+
|
|
276
|
+
expect(resetSpy).toHaveBeenCalledTimes(1);
|
|
277
|
+
});
|
|
165
278
|
});
|