@agenticmail/enterprise 0.5.153 → 0.5.155

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/cli.js CHANGED
@@ -50,7 +50,7 @@ Skill Development:
50
50
  import("./cli-serve-DHVSPFW5.js").then((m) => m.runServe(args.slice(1))).catch(fatal);
51
51
  break;
52
52
  case "agent":
53
- import("./cli-agent-7TWEYMAP.js").then((m) => m.runAgent(args.slice(1))).catch(fatal);
53
+ import("./cli-agent-VNXOYV4K.js").then((m) => m.runAgent(args.slice(1))).catch(fatal);
54
54
  break;
55
55
  case "setup":
56
56
  default:
package/dist/index.js CHANGED
@@ -84,7 +84,7 @@ import {
84
84
  GoogleEmailProvider,
85
85
  MicrosoftEmailProvider,
86
86
  createEmailProvider
87
- } from "./chunk-VEPWCYAL.js";
87
+ } from "./chunk-HKCKMCPY.js";
88
88
  import "./chunk-RO537U6H.js";
89
89
  import "./chunk-DRXMYYKN.js";
90
90
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/enterprise",
3
- "version": "0.5.153",
3
+ "version": "0.5.155",
4
4
  "description": "AgenticMail Enterprise — cloud-hosted AI agent identity, email, auth & compliance for organizations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -50,10 +50,14 @@ export class GoogleEmailProvider implements IEmailProvider {
50
50
 
51
51
  // ─── Connection ─────────────────────────────────────
52
52
 
53
+ /** Last known historyId — used for efficient polling via history.list */
54
+ public lastHistoryId: string = '';
55
+
53
56
  async connect(identity: AgentEmailIdentity): Promise<void> {
54
57
  this.identity = identity;
55
- // Validate token
56
- await this.gmailFetch('/profile');
58
+ // Validate token and capture historyId
59
+ const profile = await this.gmailFetch('/profile');
60
+ this.lastHistoryId = profile.historyId || '';
57
61
  }
58
62
 
59
63
  async disconnect(): Promise<void> {
package/src/cli-agent.ts CHANGED
@@ -577,13 +577,19 @@ async function startEmailPolling(
577
577
  } catch {}
578
578
 
579
579
  // Initial load — mark existing messages as processed so we don't reply to old emails
580
+ // Also capture historyId for Gmail History API polling
581
+ let lastHistoryId = '';
580
582
  try {
581
583
  console.log('[email-poll] Loading existing messages...');
582
584
  const existing = await emailProvider.listMessages('INBOX', { limit: 50 });
583
585
  for (const msg of existing) {
584
586
  processedIds.add(msg.uid);
585
587
  }
586
- console.log(`[email-poll] Loaded ${processedIds.size} existing messages (will skip)`);
588
+ // Get historyId from Google provider if available
589
+ if ('lastHistoryId' in emailProvider) {
590
+ lastHistoryId = (emailProvider as any).lastHistoryId || '';
591
+ }
592
+ console.log(`[email-poll] Loaded ${processedIds.size} existing messages (will skip)${lastHistoryId ? `, historyId: ${lastHistoryId}` : ''}`);
587
593
  } catch (e: any) {
588
594
  console.error(`[email-poll] Failed to load existing messages: ${e.message}`);
589
595
  }
@@ -593,31 +599,67 @@ async function startEmailPolling(
593
599
  // Poll loop
594
600
  const POLL_INTERVAL = 30_000; // 30 seconds
595
601
  const agentEmail = (emailConfig.email || config.email?.address || '').toLowerCase();
602
+ const useHistoryApi = 'getHistory' in emailProvider && !!lastHistoryId;
603
+ if (useHistoryApi) console.log('[email-poll] Using Gmail History API for reliable change detection');
596
604
 
597
605
  async function pollOnce() {
598
606
  try {
599
- // Search for recent emails using after: filter (Gmail API can have eventual consistency delays)
600
- const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
601
- const recentSearch = await emailProvider.searchMessages({ since: today }).catch(() => [] as any[]);
602
- const inboxList = await emailProvider.listMessages('INBOX', { limit: 50 });
603
- // Combine and deduplicate
604
- const seenUids = new Set<string>();
605
- const msgs: typeof inboxList = [];
606
- for (const m of [...(recentSearch || []), ...inboxList]) {
607
- if (!seenUids.has(m.uid)) { seenUids.add(m.uid); msgs.push(m); }
607
+ let newMessageIds: string[] = [];
608
+
609
+ if (useHistoryApi && lastHistoryId) {
610
+ // Use Gmail History API most reliable for detecting new messages
611
+ try {
612
+ const history = await (emailProvider as any).getHistory(lastHistoryId);
613
+ if (history.historyId) lastHistoryId = history.historyId;
614
+ // Filter to only messages we haven't processed
615
+ newMessageIds = history.messages
616
+ .map((m: any) => m.id)
617
+ .filter((id: string) => !processedIds.has(id));
618
+ if (newMessageIds.length > 0) {
619
+ console.log(`[email-poll] History API: ${newMessageIds.length} new messages since historyId ${lastHistoryId}`);
620
+ }
621
+ } catch (histErr: any) {
622
+ // historyId might be too old (404), fall back to list
623
+ console.warn(`[email-poll] History API failed (${histErr.message?.slice(0, 60)}), falling back to list`);
624
+ const msgs = await emailProvider.listMessages('INBOX', { limit: 50 });
625
+ newMessageIds = msgs.filter(m => !processedIds.has(m.uid)).map(m => m.uid);
626
+ // Update historyId from provider
627
+ if ('lastHistoryId' in emailProvider) lastHistoryId = (emailProvider as any).lastHistoryId || lastHistoryId;
628
+ }
629
+ } else {
630
+ // Fallback: list + search combo
631
+ const today = new Date().toISOString().split('T')[0];
632
+ const recentSearch = await emailProvider.searchMessages({ since: today }).catch(() => [] as any[]);
633
+ const inboxList = await emailProvider.listMessages('INBOX', { limit: 50 });
634
+ const seenUids = new Set<string>();
635
+ const combined: string[] = [];
636
+ for (const m of [...(recentSearch || []), ...inboxList]) {
637
+ if (!seenUids.has(m.uid) && !processedIds.has(m.uid)) { seenUids.add(m.uid); combined.push(m.uid); }
638
+ }
639
+ newMessageIds = combined;
608
640
  }
609
- const newMessages = msgs.filter(m => !processedIds.has(m.uid));
610
- if (newMessages.length > 0) {
611
- console.log(`[email-poll] Found ${newMessages.length} new messages (${msgs.length} total, ${processedIds.size} known)`);
612
- } else if (msgs.length < processedIds.size) {
613
- console.log(`[email-poll] No new (${msgs.length} fetched < ${processedIds.size} known — possible API gap)`);
641
+
642
+ if (newMessageIds.length > 0) {
643
+ console.log(`[email-poll] Processing ${newMessageIds.length} new messages`);
614
644
  }
615
645
 
616
- for (const envelope of newMessages) {
617
- processedIds.add(envelope.uid);
646
+ for (const msgId of newMessageIds) {
647
+ processedIds.add(msgId);
648
+
649
+ // Read full message
650
+ let fullMsg: any;
651
+ try {
652
+ fullMsg = await emailProvider.readMessage(msgId, 'INBOX');
653
+ } catch (readErr: any) {
654
+ console.warn(`[email-poll] Failed to read message ${msgId}: ${readErr.message?.slice(0, 80)}`);
655
+ continue;
656
+ }
657
+ const envelope = { uid: msgId, from: fullMsg.from, to: fullMsg.to, subject: fullMsg.subject, date: fullMsg.date };
618
658
 
619
659
  // Skip emails from ourselves
620
- if (envelope.from?.email?.toLowerCase() === agentEmail) continue;
660
+ if (envelope.from?.email?.toLowerCase() === agentEmail) {
661
+ continue;
662
+ }
621
663
 
622
664
  console.log(`[email-poll] New email from ${envelope.from?.email}: "${envelope.subject}"`);
623
665
 
@@ -632,14 +674,11 @@ async function startEmailPolling(
632
674
  console.warn(`[email-poll] Failed to persist processed ID: ${peErr.message}`);
633
675
  }
634
676
 
635
- // Read full message
636
- const fullMsg = await emailProvider.readMessage(envelope.uid, 'INBOX');
637
-
638
677
  // Mark as read
639
- try { await emailProvider.markRead(envelope.uid, 'INBOX'); } catch {}
678
+ try { await emailProvider.markRead(msgId, 'INBOX'); } catch {}
640
679
 
641
680
  // Format as agent message and spawn a session
642
- const emailUid = envelope.uid;
681
+ const emailUid = msgId;
643
682
  const emailText = [
644
683
  `[Inbound Email]`,
645
684
  `Message-ID: ${emailUid}`,