@agenticmail/enterprise 0.5.154 → 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-6DJML73P.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.154",
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,32 +599,65 @@ 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
660
  if (envelope.from?.email?.toLowerCase() === agentEmail) {
621
- console.log(`[email-poll] Skipping self-email: "${envelope.subject}" from ${envelope.from?.email}`);
622
661
  continue;
623
662
  }
624
663
 
@@ -635,14 +674,11 @@ async function startEmailPolling(
635
674
  console.warn(`[email-poll] Failed to persist processed ID: ${peErr.message}`);
636
675
  }
637
676
 
638
- // Read full message
639
- const fullMsg = await emailProvider.readMessage(envelope.uid, 'INBOX');
640
-
641
677
  // Mark as read
642
- try { await emailProvider.markRead(envelope.uid, 'INBOX'); } catch {}
678
+ try { await emailProvider.markRead(msgId, 'INBOX'); } catch {}
643
679
 
644
680
  // Format as agent message and spawn a session
645
- const emailUid = envelope.uid;
681
+ const emailUid = msgId;
646
682
  const emailText = [
647
683
  `[Inbound Email]`,
648
684
  `Message-ID: ${emailUid}`,