@agenticmail/enterprise 0.5.49 → 0.5.51

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/dist/index.js CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  executeTool,
36
36
  runAgentLoop,
37
37
  toolsToDefinitions
38
- } from "./chunk-6T5PE7NL.js";
38
+ } from "./chunk-G7BBCWAX.js";
39
39
  import "./chunk-TYW5XTOW.js";
40
40
  import {
41
41
  ValidationError,
@@ -50,11 +50,11 @@ import {
50
50
  requireRole,
51
51
  securityHeaders,
52
52
  validate
53
- } from "./chunk-JFXH5PXO.js";
53
+ } from "./chunk-Q4WDMWLJ.js";
54
54
  import {
55
55
  provision,
56
56
  runSetupWizard
57
- } from "./chunk-R3JR6Z3H.js";
57
+ } from "./chunk-FKDN7ZV3.js";
58
58
  import {
59
59
  ENGINE_TABLES,
60
60
  ENGINE_TABLES_POSTGRES,
@@ -123,6 +123,734 @@ import "./chunk-KFQGP6VL.js";
123
123
  // src/engine/index.ts
124
124
  init_tool_catalog();
125
125
  init_guardrails();
126
+
127
+ // src/agenticmail/providers/microsoft.ts
128
+ var GRAPH_BASE = "https://graph.microsoft.com/v1.0";
129
+ var MicrosoftEmailProvider = class {
130
+ provider = "microsoft";
131
+ identity = null;
132
+ get token() {
133
+ if (!this.identity) throw new Error("Not connected");
134
+ return this.identity.accessToken;
135
+ }
136
+ async refreshIfNeeded() {
137
+ if (this.identity?.refreshToken) {
138
+ this.identity.accessToken = await this.identity.refreshToken();
139
+ }
140
+ }
141
+ async graphFetch(path, opts) {
142
+ await this.refreshIfNeeded();
143
+ const res = await fetch(`${GRAPH_BASE}${path}`, {
144
+ ...opts,
145
+ headers: {
146
+ Authorization: `Bearer ${this.token}`,
147
+ "Content-Type": "application/json",
148
+ ...opts?.headers
149
+ }
150
+ });
151
+ if (!res.ok) {
152
+ const text = await res.text().catch(() => "");
153
+ throw new Error(`Graph API ${res.status}: ${text}`);
154
+ }
155
+ if (res.status === 204) return null;
156
+ return res.json();
157
+ }
158
+ // ─── Connection ─────────────────────────────────────
159
+ async connect(identity) {
160
+ this.identity = identity;
161
+ await this.graphFetch("/me?$select=mail,displayName");
162
+ }
163
+ async disconnect() {
164
+ this.identity = null;
165
+ }
166
+ // ─── List / Read ────────────────────────────────────
167
+ async listMessages(folder, opts) {
168
+ const folderId = this.resolveFolderId(folder);
169
+ const top = opts?.limit || 20;
170
+ const skip = opts?.offset || 0;
171
+ const data = await this.graphFetch(
172
+ `/me/mailFolders/${folderId}/messages?$top=${top}&$skip=${skip}&$select=id,subject,from,toRecipients,receivedDateTime,isRead,flag,hasAttachments,bodyPreview&$orderby=receivedDateTime desc`
173
+ );
174
+ return (data.value || []).map((m) => this.toEnvelope(m));
175
+ }
176
+ async readMessage(uid) {
177
+ const data = await this.graphFetch(`/me/messages/${uid}?$select=id,subject,from,toRecipients,ccRecipients,bccRecipients,receivedDateTime,isRead,flag,hasAttachments,body,bodyPreview,replyTo,internetMessageId,internetMessageHeaders,conversationId`);
178
+ return this.toMessage(data);
179
+ }
180
+ async searchMessages(criteria) {
181
+ const filters = [];
182
+ if (criteria.from) filters.push(`from/emailAddress/address eq '${criteria.from}'`);
183
+ if (criteria.subject) filters.push(`contains(subject, '${criteria.subject}')`);
184
+ if (criteria.since) filters.push(`receivedDateTime ge ${criteria.since}`);
185
+ if (criteria.before) filters.push(`receivedDateTime lt ${criteria.before}`);
186
+ if (criteria.seen !== void 0) filters.push(`isRead eq ${criteria.seen}`);
187
+ let path = "/me/messages?$top=50&$select=id,subject,from,toRecipients,receivedDateTime,isRead,flag,hasAttachments,bodyPreview&$orderby=receivedDateTime desc";
188
+ if (filters.length) path += "&$filter=" + encodeURIComponent(filters.join(" and "));
189
+ if (criteria.text) path = `/me/messages?$search="${encodeURIComponent(criteria.text)}"&$top=50&$select=id,subject,from,toRecipients,receivedDateTime,isRead,flag,hasAttachments,bodyPreview`;
190
+ const data = await this.graphFetch(path);
191
+ return (data.value || []).map((m) => this.toEnvelope(m));
192
+ }
193
+ async listFolders() {
194
+ const data = await this.graphFetch("/me/mailFolders?$select=id,displayName,unreadItemCount,totalItemCount");
195
+ return (data.value || []).map((f) => ({
196
+ name: f.displayName,
197
+ path: f.id,
198
+ unread: f.unreadItemCount || 0,
199
+ total: f.totalItemCount || 0
200
+ }));
201
+ }
202
+ async createFolder(name) {
203
+ await this.graphFetch("/me/mailFolders", {
204
+ method: "POST",
205
+ body: JSON.stringify({ displayName: name })
206
+ });
207
+ }
208
+ // ─── Send ───────────────────────────────────────────
209
+ async send(options) {
210
+ const message = this.buildGraphMessage(options);
211
+ await this.graphFetch("/me/sendMail", {
212
+ method: "POST",
213
+ body: JSON.stringify({ message, saveToSentItems: true })
214
+ });
215
+ return { messageId: `graph-${Date.now()}` };
216
+ }
217
+ async reply(uid, body, replyAll = false) {
218
+ const endpoint = replyAll ? "replyAll" : "reply";
219
+ await this.graphFetch(`/me/messages/${uid}/${endpoint}`, {
220
+ method: "POST",
221
+ body: JSON.stringify({ comment: body })
222
+ });
223
+ return { messageId: `graph-reply-${Date.now()}` };
224
+ }
225
+ async forward(uid, to, body) {
226
+ await this.graphFetch(`/me/messages/${uid}/forward`, {
227
+ method: "POST",
228
+ body: JSON.stringify({
229
+ comment: body || "",
230
+ toRecipients: [{ emailAddress: { address: to } }]
231
+ })
232
+ });
233
+ return { messageId: `graph-fwd-${Date.now()}` };
234
+ }
235
+ // ─── Organize ───────────────────────────────────────
236
+ async moveMessage(uid, toFolder) {
237
+ const folderId = this.resolveFolderId(toFolder);
238
+ await this.graphFetch(`/me/messages/${uid}/move`, {
239
+ method: "POST",
240
+ body: JSON.stringify({ destinationId: folderId })
241
+ });
242
+ }
243
+ async deleteMessage(uid) {
244
+ await this.graphFetch(`/me/messages/${uid}`, { method: "DELETE" });
245
+ }
246
+ async markRead(uid) {
247
+ await this.graphFetch(`/me/messages/${uid}`, {
248
+ method: "PATCH",
249
+ body: JSON.stringify({ isRead: true })
250
+ });
251
+ }
252
+ async markUnread(uid) {
253
+ await this.graphFetch(`/me/messages/${uid}`, {
254
+ method: "PATCH",
255
+ body: JSON.stringify({ isRead: false })
256
+ });
257
+ }
258
+ async flagMessage(uid) {
259
+ await this.graphFetch(`/me/messages/${uid}`, {
260
+ method: "PATCH",
261
+ body: JSON.stringify({ flag: { flagStatus: "flagged" } })
262
+ });
263
+ }
264
+ async unflagMessage(uid) {
265
+ await this.graphFetch(`/me/messages/${uid}`, {
266
+ method: "PATCH",
267
+ body: JSON.stringify({ flag: { flagStatus: "notFlagged" } })
268
+ });
269
+ }
270
+ // ─── Batch ──────────────────────────────────────────
271
+ async batchMarkRead(uids) {
272
+ await Promise.all(uids.map((uid) => this.markRead(uid)));
273
+ }
274
+ async batchMarkUnread(uids) {
275
+ await Promise.all(uids.map((uid) => this.markUnread(uid)));
276
+ }
277
+ async batchMove(uids, toFolder) {
278
+ await Promise.all(uids.map((uid) => this.moveMessage(uid, toFolder)));
279
+ }
280
+ async batchDelete(uids) {
281
+ await Promise.all(uids.map((uid) => this.deleteMessage(uid)));
282
+ }
283
+ // ─── Helpers ────────────────────────────────────────
284
+ resolveFolderId(folder) {
285
+ const map = {
286
+ INBOX: "inbox",
287
+ inbox: "inbox",
288
+ Sent: "sentItems",
289
+ sent: "sentItems",
290
+ sentitems: "sentItems",
291
+ Drafts: "drafts",
292
+ drafts: "drafts",
293
+ Trash: "deletedItems",
294
+ trash: "deletedItems",
295
+ deleteditems: "deletedItems",
296
+ Junk: "junkemail",
297
+ junk: "junkemail",
298
+ spam: "junkemail",
299
+ Archive: "archive",
300
+ archive: "archive"
301
+ };
302
+ return map[folder] || folder;
303
+ }
304
+ buildGraphMessage(options) {
305
+ const msg = {
306
+ subject: options.subject,
307
+ body: { contentType: options.html ? "HTML" : "Text", content: options.html || options.body },
308
+ toRecipients: options.to.split(",").map((e) => ({ emailAddress: { address: e.trim() } }))
309
+ };
310
+ if (options.cc) msg.ccRecipients = options.cc.split(",").map((e) => ({ emailAddress: { address: e.trim() } }));
311
+ if (options.bcc) msg.bccRecipients = options.bcc.split(",").map((e) => ({ emailAddress: { address: e.trim() } }));
312
+ if (options.inReplyTo) msg.internetMessageHeaders = [{ name: "In-Reply-To", value: options.inReplyTo }];
313
+ return msg;
314
+ }
315
+ toEnvelope(m) {
316
+ return {
317
+ uid: m.id,
318
+ from: { name: m.from?.emailAddress?.name, email: m.from?.emailAddress?.address || "" },
319
+ to: (m.toRecipients || []).map((r) => ({ name: r.emailAddress?.name, email: r.emailAddress?.address || "" })),
320
+ subject: m.subject || "",
321
+ date: m.receivedDateTime || "",
322
+ read: !!m.isRead,
323
+ flagged: m.flag?.flagStatus === "flagged",
324
+ hasAttachments: !!m.hasAttachments,
325
+ preview: m.bodyPreview || ""
326
+ };
327
+ }
328
+ toMessage(m) {
329
+ return {
330
+ uid: m.id,
331
+ from: { name: m.from?.emailAddress?.name, email: m.from?.emailAddress?.address || "" },
332
+ to: (m.toRecipients || []).map((r) => ({ name: r.emailAddress?.name, email: r.emailAddress?.address || "" })),
333
+ cc: (m.ccRecipients || []).map((r) => ({ name: r.emailAddress?.name, email: r.emailAddress?.address || "" })),
334
+ subject: m.subject || "",
335
+ body: m.body?.contentType === "HTML" ? "" : m.body?.content || "",
336
+ html: m.body?.contentType === "HTML" ? m.body?.content : void 0,
337
+ date: m.receivedDateTime || "",
338
+ read: !!m.isRead,
339
+ flagged: m.flag?.flagStatus === "flagged",
340
+ folder: "inbox",
341
+ messageId: m.internetMessageId,
342
+ attachments: []
343
+ };
344
+ }
345
+ };
346
+
347
+ // src/agenticmail/providers/google.ts
348
+ var GMAIL_BASE = "https://gmail.googleapis.com/gmail/v1";
349
+ var GoogleEmailProvider = class {
350
+ provider = "google";
351
+ identity = null;
352
+ userId = "me";
353
+ get token() {
354
+ if (!this.identity) throw new Error("Not connected");
355
+ return this.identity.accessToken;
356
+ }
357
+ async refreshIfNeeded() {
358
+ if (this.identity?.refreshToken) {
359
+ this.identity.accessToken = await this.identity.refreshToken();
360
+ }
361
+ }
362
+ async gmailFetch(path, opts) {
363
+ await this.refreshIfNeeded();
364
+ const res = await fetch(`${GMAIL_BASE}/users/${this.userId}${path}`, {
365
+ ...opts,
366
+ headers: {
367
+ Authorization: `Bearer ${this.token}`,
368
+ "Content-Type": "application/json",
369
+ ...opts?.headers
370
+ }
371
+ });
372
+ if (!res.ok) {
373
+ const text = await res.text().catch(() => "");
374
+ throw new Error(`Gmail API ${res.status}: ${text}`);
375
+ }
376
+ if (res.status === 204) return null;
377
+ return res.json();
378
+ }
379
+ // ─── Connection ─────────────────────────────────────
380
+ async connect(identity) {
381
+ this.identity = identity;
382
+ await this.gmailFetch("/profile");
383
+ }
384
+ async disconnect() {
385
+ this.identity = null;
386
+ }
387
+ // ─── List / Read ────────────────────────────────────
388
+ async listMessages(folder, opts) {
389
+ const labelId = this.resolveLabelId(folder);
390
+ const maxResults = opts?.limit || 20;
391
+ const q = labelId === "INBOX" ? "" : "";
392
+ const data = await this.gmailFetch(`/messages?labelIds=${labelId}&maxResults=${maxResults}${q ? "&q=" + encodeURIComponent(q) : ""}`);
393
+ if (!data.messages?.length) return [];
394
+ const envelopes = [];
395
+ for (const msg of data.messages) {
396
+ try {
397
+ const detail = await this.gmailFetch(`/messages/${msg.id}?format=metadata&metadataHeaders=From&metadataHeaders=To&metadataHeaders=Subject&metadataHeaders=Date`);
398
+ envelopes.push(this.metadataToEnvelope(detail));
399
+ } catch {
400
+ }
401
+ }
402
+ return envelopes;
403
+ }
404
+ async readMessage(uid) {
405
+ const data = await this.gmailFetch(`/messages/${uid}?format=full`);
406
+ return this.fullToMessage(data);
407
+ }
408
+ async searchMessages(criteria) {
409
+ const parts = [];
410
+ if (criteria.from) parts.push(`from:${criteria.from}`);
411
+ if (criteria.to) parts.push(`to:${criteria.to}`);
412
+ if (criteria.subject) parts.push(`subject:${criteria.subject}`);
413
+ if (criteria.text) parts.push(criteria.text);
414
+ if (criteria.since) parts.push(`after:${criteria.since.split("T")[0]}`);
415
+ if (criteria.before) parts.push(`before:${criteria.before.split("T")[0]}`);
416
+ if (criteria.seen === true) parts.push("is:read");
417
+ if (criteria.seen === false) parts.push("is:unread");
418
+ const q = parts.join(" ");
419
+ const data = await this.gmailFetch(`/messages?q=${encodeURIComponent(q)}&maxResults=50`);
420
+ if (!data.messages?.length) return [];
421
+ const envelopes = [];
422
+ for (const msg of data.messages.slice(0, 20)) {
423
+ try {
424
+ const detail = await this.gmailFetch(`/messages/${msg.id}?format=metadata&metadataHeaders=From&metadataHeaders=To&metadataHeaders=Subject&metadataHeaders=Date`);
425
+ envelopes.push(this.metadataToEnvelope(detail));
426
+ } catch {
427
+ }
428
+ }
429
+ return envelopes;
430
+ }
431
+ async listFolders() {
432
+ const data = await this.gmailFetch("/labels");
433
+ return (data.labels || []).map((l) => ({
434
+ name: l.name,
435
+ path: l.id,
436
+ unread: l.messagesUnread || 0,
437
+ total: l.messagesTotal || 0
438
+ }));
439
+ }
440
+ async createFolder(name) {
441
+ await this.gmailFetch("/labels", {
442
+ method: "POST",
443
+ body: JSON.stringify({ name, labelListVisibility: "labelShow", messageListVisibility: "show" })
444
+ });
445
+ }
446
+ // ─── Send ───────────────────────────────────────────
447
+ async send(options) {
448
+ const raw = this.buildRawEmail(options);
449
+ const data = await this.gmailFetch("/messages/send", {
450
+ method: "POST",
451
+ body: JSON.stringify({ raw })
452
+ });
453
+ return { messageId: data.id };
454
+ }
455
+ async reply(uid, body, replyAll = false) {
456
+ const original = await this.readMessage(uid);
457
+ const to = replyAll ? [original.from.email, ...(original.to || []).map((t) => t.email), ...(original.cc || []).map((c) => c.email)].filter((e) => e !== this.identity?.email).join(", ") : original.from.email;
458
+ return this.send({
459
+ to,
460
+ subject: original.subject.startsWith("Re:") ? original.subject : `Re: ${original.subject}`,
461
+ body,
462
+ inReplyTo: original.messageId,
463
+ references: original.references ? [...original.references, original.messageId] : [original.messageId]
464
+ });
465
+ }
466
+ async forward(uid, to, body) {
467
+ const original = await this.readMessage(uid);
468
+ return this.send({
469
+ to,
470
+ subject: `Fwd: ${original.subject}`,
471
+ body: (body ? body + "\n\n" : "") + `---------- Forwarded message ----------
472
+ From: ${original.from.email}
473
+ Date: ${original.date}
474
+ Subject: ${original.subject}
475
+
476
+ ${original.body}`
477
+ });
478
+ }
479
+ // ─── Organize ───────────────────────────────────────
480
+ async moveMessage(uid, toFolder, fromFolder) {
481
+ const addLabel = this.resolveLabelId(toFolder);
482
+ const removeLabel = fromFolder ? this.resolveLabelId(fromFolder) : "INBOX";
483
+ await this.gmailFetch(`/messages/${uid}/modify`, {
484
+ method: "POST",
485
+ body: JSON.stringify({ addLabelIds: [addLabel], removeLabelIds: [removeLabel] })
486
+ });
487
+ }
488
+ async deleteMessage(uid) {
489
+ await this.gmailFetch(`/messages/${uid}/trash`, { method: "POST" });
490
+ }
491
+ async markRead(uid) {
492
+ await this.gmailFetch(`/messages/${uid}/modify`, {
493
+ method: "POST",
494
+ body: JSON.stringify({ removeLabelIds: ["UNREAD"] })
495
+ });
496
+ }
497
+ async markUnread(uid) {
498
+ await this.gmailFetch(`/messages/${uid}/modify`, {
499
+ method: "POST",
500
+ body: JSON.stringify({ addLabelIds: ["UNREAD"] })
501
+ });
502
+ }
503
+ async flagMessage(uid) {
504
+ await this.gmailFetch(`/messages/${uid}/modify`, {
505
+ method: "POST",
506
+ body: JSON.stringify({ addLabelIds: ["STARRED"] })
507
+ });
508
+ }
509
+ async unflagMessage(uid) {
510
+ await this.gmailFetch(`/messages/${uid}/modify`, {
511
+ method: "POST",
512
+ body: JSON.stringify({ removeLabelIds: ["STARRED"] })
513
+ });
514
+ }
515
+ // ─── Batch ──────────────────────────────────────────
516
+ async batchMarkRead(uids) {
517
+ await this.gmailFetch("/messages/batchModify", {
518
+ method: "POST",
519
+ body: JSON.stringify({ ids: uids, removeLabelIds: ["UNREAD"] })
520
+ });
521
+ }
522
+ async batchMarkUnread(uids) {
523
+ await this.gmailFetch("/messages/batchModify", {
524
+ method: "POST",
525
+ body: JSON.stringify({ ids: uids, addLabelIds: ["UNREAD"] })
526
+ });
527
+ }
528
+ async batchMove(uids, toFolder, fromFolder) {
529
+ const addLabel = this.resolveLabelId(toFolder);
530
+ const removeLabel = fromFolder ? this.resolveLabelId(fromFolder) : "INBOX";
531
+ await this.gmailFetch("/messages/batchModify", {
532
+ method: "POST",
533
+ body: JSON.stringify({ ids: uids, addLabelIds: [addLabel], removeLabelIds: [removeLabel] })
534
+ });
535
+ }
536
+ async batchDelete(uids) {
537
+ await Promise.all(uids.map((uid) => this.deleteMessage(uid)));
538
+ }
539
+ // ─── Helpers ────────────────────────────────────────
540
+ resolveLabelId(folder) {
541
+ const map = {
542
+ INBOX: "INBOX",
543
+ inbox: "INBOX",
544
+ Sent: "SENT",
545
+ sent: "SENT",
546
+ Drafts: "DRAFT",
547
+ drafts: "DRAFT",
548
+ Trash: "TRASH",
549
+ trash: "TRASH",
550
+ Spam: "SPAM",
551
+ spam: "SPAM",
552
+ Junk: "SPAM",
553
+ junk: "SPAM",
554
+ Starred: "STARRED",
555
+ starred: "STARRED",
556
+ Important: "IMPORTANT",
557
+ important: "IMPORTANT"
558
+ };
559
+ return map[folder] || folder;
560
+ }
561
+ buildRawEmail(options) {
562
+ const lines = [
563
+ `To: ${options.to}`,
564
+ `Subject: ${options.subject}`,
565
+ `Content-Type: text/plain; charset=utf-8`
566
+ ];
567
+ if (options.cc) lines.splice(1, 0, `Cc: ${options.cc}`);
568
+ if (options.inReplyTo) lines.push(`In-Reply-To: ${options.inReplyTo}`);
569
+ if (options.references?.length) lines.push(`References: ${options.references.join(" ")}`);
570
+ lines.push("", options.body);
571
+ const raw = lines.join("\r\n");
572
+ return btoa(unescape(encodeURIComponent(raw))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
573
+ }
574
+ getHeader(msg, name) {
575
+ const headers = msg.payload?.headers || [];
576
+ const h = headers.find((h2) => h2.name.toLowerCase() === name.toLowerCase());
577
+ return h?.value || "";
578
+ }
579
+ metadataToEnvelope(msg) {
580
+ const from = this.getHeader(msg, "From");
581
+ const fromMatch = from.match(/^(.*?)\s*<(.+?)>$/) || [null, "", from];
582
+ return {
583
+ uid: msg.id,
584
+ from: { name: fromMatch[1]?.replace(/"/g, "").trim() || void 0, email: fromMatch[2] || from },
585
+ to: [{ email: this.getHeader(msg, "To") }],
586
+ subject: this.getHeader(msg, "Subject"),
587
+ date: this.getHeader(msg, "Date"),
588
+ read: !(msg.labelIds || []).includes("UNREAD"),
589
+ flagged: (msg.labelIds || []).includes("STARRED"),
590
+ hasAttachments: false,
591
+ preview: msg.snippet || ""
592
+ };
593
+ }
594
+ fullToMessage(msg) {
595
+ const from = this.getHeader(msg, "From");
596
+ const fromMatch = from.match(/^(.*?)\s*<(.+?)>$/) || [null, "", from];
597
+ let body = "";
598
+ let html;
599
+ const extractBody = (payload) => {
600
+ if (payload.mimeType === "text/plain" && payload.body?.data) {
601
+ body = Buffer.from(payload.body.data, "base64url").toString("utf-8");
602
+ }
603
+ if (payload.mimeType === "text/html" && payload.body?.data) {
604
+ html = Buffer.from(payload.body.data, "base64url").toString("utf-8");
605
+ }
606
+ if (payload.parts) payload.parts.forEach(extractBody);
607
+ };
608
+ if (msg.payload) extractBody(msg.payload);
609
+ return {
610
+ uid: msg.id,
611
+ from: { name: fromMatch[1]?.replace(/"/g, "").trim() || void 0, email: fromMatch[2] || from },
612
+ to: [{ email: this.getHeader(msg, "To") }],
613
+ cc: this.getHeader(msg, "Cc") ? [{ email: this.getHeader(msg, "Cc") }] : void 0,
614
+ subject: this.getHeader(msg, "Subject"),
615
+ body,
616
+ html,
617
+ date: this.getHeader(msg, "Date"),
618
+ read: !(msg.labelIds || []).includes("UNREAD"),
619
+ flagged: (msg.labelIds || []).includes("STARRED"),
620
+ folder: (msg.labelIds || []).includes("INBOX") ? "inbox" : "other",
621
+ messageId: this.getHeader(msg, "Message-ID"),
622
+ inReplyTo: this.getHeader(msg, "In-Reply-To") || void 0,
623
+ references: this.getHeader(msg, "References") ? this.getHeader(msg, "References").split(/\s+/) : void 0,
624
+ attachments: []
625
+ };
626
+ }
627
+ };
628
+
629
+ // src/agenticmail/providers/index.ts
630
+ function createEmailProvider(provider) {
631
+ switch (provider) {
632
+ case "microsoft":
633
+ return new MicrosoftEmailProvider();
634
+ case "google":
635
+ return new GoogleEmailProvider();
636
+ case "imap":
637
+ throw new Error("Generic IMAP provider not yet implemented \u2014 use Microsoft or Google");
638
+ default:
639
+ throw new Error(`Unknown email provider: ${provider}`);
640
+ }
641
+ }
642
+
643
+ // src/agenticmail/manager.ts
644
+ var AgenticMailManager = class {
645
+ providers = /* @__PURE__ */ new Map();
646
+ identities = /* @__PURE__ */ new Map();
647
+ db;
648
+ constructor(opts) {
649
+ this.db = opts?.db;
650
+ }
651
+ setDb(db) {
652
+ this.db = db;
653
+ }
654
+ // ─── Agent Registration ─────────────────────────────
655
+ /**
656
+ * Register an agent's email identity from the org's OAuth/SSO.
657
+ * Called when an agent is created or when its OAuth token is refreshed.
658
+ */
659
+ async registerAgent(identity) {
660
+ this.identities.set(identity.agentId, identity);
661
+ const provider = createEmailProvider(identity.provider);
662
+ await provider.connect(identity);
663
+ this.providers.set(identity.agentId, provider);
664
+ }
665
+ /**
666
+ * Unregister an agent (on deletion or token revocation).
667
+ */
668
+ async unregisterAgent(agentId) {
669
+ const provider = this.providers.get(agentId);
670
+ if (provider) {
671
+ await provider.disconnect().catch(() => {
672
+ });
673
+ this.providers.delete(agentId);
674
+ }
675
+ this.identities.delete(agentId);
676
+ }
677
+ /**
678
+ * Get the email provider for an agent.
679
+ * Throws if agent is not registered.
680
+ */
681
+ getProvider(agentId) {
682
+ const provider = this.providers.get(agentId);
683
+ if (!provider) throw new Error(`Agent ${agentId} has no email provider registered. Ensure the agent has been connected via org OAuth.`);
684
+ return provider;
685
+ }
686
+ /**
687
+ * Get the email identity for an agent.
688
+ */
689
+ getIdentity(agentId) {
690
+ return this.identities.get(agentId);
691
+ }
692
+ /**
693
+ * Check if an agent has email access.
694
+ */
695
+ hasEmail(agentId) {
696
+ return this.providers.has(agentId);
697
+ }
698
+ // ─── Inter-Agent Messaging ──────────────────────────
699
+ // These use the enterprise DB directly, not email.
700
+ // Agents in the same org can message each other without email.
701
+ /**
702
+ * Send a message from one agent to another (internal, no email).
703
+ */
704
+ async sendAgentMessage(from, to, subject, body, priority = "normal") {
705
+ const msg = {
706
+ id: crypto.randomUUID(),
707
+ from,
708
+ to,
709
+ subject,
710
+ body,
711
+ priority,
712
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
713
+ read: false
714
+ };
715
+ if (this.db) {
716
+ await this.db.execute(
717
+ `INSERT INTO agent_messages (id, from_agent, to_agent, subject, body, priority, created_at, read)
718
+ VALUES (?, ?, ?, ?, ?, ?, ?, 0)`,
719
+ [msg.id, msg.from, msg.to, msg.subject, msg.body, msg.priority, msg.createdAt]
720
+ ).catch(() => {
721
+ });
722
+ }
723
+ return msg;
724
+ }
725
+ /**
726
+ * Get unread messages for an agent.
727
+ */
728
+ async getAgentMessages(agentId, opts) {
729
+ if (!this.db) return [];
730
+ try {
731
+ let sql = "SELECT * FROM agent_messages WHERE to_agent = ?";
732
+ const params = [agentId];
733
+ if (opts?.unreadOnly) {
734
+ sql += " AND read = 0";
735
+ }
736
+ sql += " ORDER BY created_at DESC LIMIT ?";
737
+ params.push(opts?.limit || 20);
738
+ const rows = await this.db.query(sql, params);
739
+ return rows.map((r) => ({
740
+ id: r.id,
741
+ from: r.from_agent,
742
+ to: r.to_agent,
743
+ subject: r.subject,
744
+ body: r.body,
745
+ priority: r.priority,
746
+ createdAt: r.created_at,
747
+ read: !!r.read
748
+ }));
749
+ } catch {
750
+ return [];
751
+ }
752
+ }
753
+ // ─── Task Management ────────────────────────────────
754
+ // Tasks also use the enterprise DB directly.
755
+ /**
756
+ * Create a task assigned to an agent.
757
+ */
758
+ async createTask(assigner, assignee, title, description, priority = "normal") {
759
+ const now = (/* @__PURE__ */ new Date()).toISOString();
760
+ const task = {
761
+ id: crypto.randomUUID(),
762
+ assigner,
763
+ assignee,
764
+ title,
765
+ description,
766
+ status: "pending",
767
+ priority,
768
+ createdAt: now,
769
+ updatedAt: now
770
+ };
771
+ if (this.db) {
772
+ await this.db.execute(
773
+ `INSERT INTO agent_tasks (id, assigner, assignee, title, description, status, priority, created_at, updated_at)
774
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
775
+ [task.id, task.assigner, task.assignee, task.title, task.description || null, task.status, task.priority, task.createdAt, task.updatedAt]
776
+ ).catch(() => {
777
+ });
778
+ }
779
+ return task;
780
+ }
781
+ /**
782
+ * Get tasks for an agent.
783
+ */
784
+ async getAgentTasks(agentId, direction = "incoming", status) {
785
+ if (!this.db) return [];
786
+ try {
787
+ const col = direction === "incoming" ? "assignee" : "assigner";
788
+ let sql = `SELECT * FROM agent_tasks WHERE ${col} = ?`;
789
+ const params = [agentId];
790
+ if (status) {
791
+ sql += " AND status = ?";
792
+ params.push(status);
793
+ }
794
+ sql += " ORDER BY created_at DESC LIMIT 50";
795
+ const rows = await this.db.query(sql, params);
796
+ return rows.map((r) => ({
797
+ id: r.id,
798
+ assigner: r.assigner,
799
+ assignee: r.assignee,
800
+ title: r.title,
801
+ description: r.description,
802
+ status: r.status,
803
+ priority: r.priority,
804
+ result: r.result ? JSON.parse(r.result) : void 0,
805
+ createdAt: r.created_at,
806
+ updatedAt: r.updated_at
807
+ }));
808
+ } catch {
809
+ return [];
810
+ }
811
+ }
812
+ /**
813
+ * Update task status.
814
+ */
815
+ async updateTask(taskId, updates) {
816
+ if (!this.db) return;
817
+ const sets = ["updated_at = ?"];
818
+ const params = [(/* @__PURE__ */ new Date()).toISOString()];
819
+ if (updates.status) {
820
+ sets.push("status = ?");
821
+ params.push(updates.status);
822
+ }
823
+ if (updates.result !== void 0) {
824
+ sets.push("result = ?");
825
+ params.push(JSON.stringify(updates.result));
826
+ }
827
+ params.push(taskId);
828
+ await this.db.execute(`UPDATE agent_tasks SET ${sets.join(", ")} WHERE id = ?`, params).catch(() => {
829
+ });
830
+ }
831
+ // ─── Lifecycle ──────────────────────────────────────
832
+ /**
833
+ * Get all registered agents and their email status.
834
+ */
835
+ getRegisteredAgents() {
836
+ const agents = [];
837
+ for (const [agentId, identity] of this.identities) {
838
+ agents.push({ agentId, email: identity.email, provider: identity.provider });
839
+ }
840
+ return agents;
841
+ }
842
+ /**
843
+ * Shutdown — disconnect all providers.
844
+ */
845
+ async shutdown() {
846
+ for (const provider of this.providers.values()) {
847
+ await provider.disconnect().catch(() => {
848
+ });
849
+ }
850
+ this.providers.clear();
851
+ this.identities.clear();
852
+ }
853
+ };
126
854
  export {
127
855
  AGENTICMAIL_TOOLS,
128
856
  ALL_TOOLS,
@@ -133,6 +861,7 @@ export {
133
861
  AgentLifecycleManager,
134
862
  AgentMemoryManager,
135
863
  AgentRuntime,
864
+ AgenticMailManager,
136
865
  ApprovalEngine,
137
866
  BUILTIN_SKILLS,
138
867
  CORE_TOOLS,
@@ -148,6 +877,7 @@ export {
148
877
  EmailChannel,
149
878
  EngineDatabase,
150
879
  FollowUpScheduler,
880
+ GoogleEmailProvider,
151
881
  GuardrailEngine,
152
882
  HealthMonitor,
153
883
  KeyedRateLimiter,
@@ -155,6 +885,7 @@ export {
155
885
  MIGRATIONS,
156
886
  MIGRATIONS_TABLE,
157
887
  MIGRATIONS_TABLE_POSTGRES,
888
+ MicrosoftEmailProvider,
158
889
  OnboardingManager,
159
890
  OrgPolicyEngine,
160
891
  PLAN_LIMITS,
@@ -183,6 +914,7 @@ export {
183
914
  createAdminRoutes,
184
915
  createAgentRuntime,
185
916
  createAuthRoutes,
917
+ createEmailProvider,
186
918
  createNoopHooks,
187
919
  createRuntimeHooks,
188
920
  createServer,