@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.
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Microsoft Graph Email Provider
3
+ *
4
+ * Implements IEmailProvider using Microsoft Graph API.
5
+ * Agent authenticates via org's Azure AD / Entra ID OAuth.
6
+ * Email address comes from the org directory.
7
+ */
8
+
9
+ import type {
10
+ IEmailProvider, AgentEmailIdentity, EmailProvider,
11
+ EmailMessage, EmailEnvelope, EmailFolder,
12
+ SendEmailOptions, SearchCriteria,
13
+ } from '../types.js';
14
+
15
+ const GRAPH_BASE = 'https://graph.microsoft.com/v1.0';
16
+
17
+ export class MicrosoftEmailProvider implements IEmailProvider {
18
+ readonly provider: EmailProvider = 'microsoft';
19
+ private identity: AgentEmailIdentity | null = null;
20
+
21
+ private get token(): string {
22
+ if (!this.identity) throw new Error('Not connected');
23
+ return this.identity.accessToken;
24
+ }
25
+
26
+ private async refreshIfNeeded(): Promise<void> {
27
+ if (this.identity?.refreshToken) {
28
+ this.identity.accessToken = await this.identity.refreshToken();
29
+ }
30
+ }
31
+
32
+ private async graphFetch(path: string, opts?: RequestInit): Promise<any> {
33
+ await this.refreshIfNeeded();
34
+ const res = await fetch(`${GRAPH_BASE}${path}`, {
35
+ ...opts,
36
+ headers: {
37
+ Authorization: `Bearer ${this.token}`,
38
+ 'Content-Type': 'application/json',
39
+ ...opts?.headers,
40
+ },
41
+ });
42
+ if (!res.ok) {
43
+ const text = await res.text().catch(() => '');
44
+ throw new Error(`Graph API ${res.status}: ${text}`);
45
+ }
46
+ if (res.status === 204) return null;
47
+ return res.json();
48
+ }
49
+
50
+ // ─── Connection ─────────────────────────────────────
51
+
52
+ async connect(identity: AgentEmailIdentity): Promise<void> {
53
+ this.identity = identity;
54
+ // Validate token by fetching profile
55
+ await this.graphFetch('/me?$select=mail,displayName');
56
+ }
57
+
58
+ async disconnect(): Promise<void> {
59
+ this.identity = null;
60
+ }
61
+
62
+ // ─── List / Read ────────────────────────────────────
63
+
64
+ async listMessages(folder: string, opts?: { limit?: number; offset?: number }): Promise<EmailEnvelope[]> {
65
+ const folderId = this.resolveFolderId(folder);
66
+ const top = opts?.limit || 20;
67
+ const skip = opts?.offset || 0;
68
+ const data = await this.graphFetch(
69
+ `/me/mailFolders/${folderId}/messages?$top=${top}&$skip=${skip}&$select=id,subject,from,toRecipients,receivedDateTime,isRead,flag,hasAttachments,bodyPreview&$orderby=receivedDateTime desc`
70
+ );
71
+ return (data.value || []).map((m: any) => this.toEnvelope(m));
72
+ }
73
+
74
+ async readMessage(uid: string): Promise<EmailMessage> {
75
+ 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`);
76
+ return this.toMessage(data);
77
+ }
78
+
79
+ async searchMessages(criteria: SearchCriteria): Promise<EmailEnvelope[]> {
80
+ const filters: string[] = [];
81
+ if (criteria.from) filters.push(`from/emailAddress/address eq '${criteria.from}'`);
82
+ if (criteria.subject) filters.push(`contains(subject, '${criteria.subject}')`);
83
+ if (criteria.since) filters.push(`receivedDateTime ge ${criteria.since}`);
84
+ if (criteria.before) filters.push(`receivedDateTime lt ${criteria.before}`);
85
+ if (criteria.seen !== undefined) filters.push(`isRead eq ${criteria.seen}`);
86
+
87
+ let path = '/me/messages?$top=50&$select=id,subject,from,toRecipients,receivedDateTime,isRead,flag,hasAttachments,bodyPreview&$orderby=receivedDateTime desc';
88
+ if (filters.length) path += '&$filter=' + encodeURIComponent(filters.join(' and '));
89
+ if (criteria.text) path = `/me/messages?$search="${encodeURIComponent(criteria.text)}"&$top=50&$select=id,subject,from,toRecipients,receivedDateTime,isRead,flag,hasAttachments,bodyPreview`;
90
+
91
+ const data = await this.graphFetch(path);
92
+ return (data.value || []).map((m: any) => this.toEnvelope(m));
93
+ }
94
+
95
+ async listFolders(): Promise<EmailFolder[]> {
96
+ const data = await this.graphFetch('/me/mailFolders?$select=id,displayName,unreadItemCount,totalItemCount');
97
+ return (data.value || []).map((f: any) => ({
98
+ name: f.displayName,
99
+ path: f.id,
100
+ unread: f.unreadItemCount || 0,
101
+ total: f.totalItemCount || 0,
102
+ }));
103
+ }
104
+
105
+ async createFolder(name: string): Promise<void> {
106
+ await this.graphFetch('/me/mailFolders', {
107
+ method: 'POST',
108
+ body: JSON.stringify({ displayName: name }),
109
+ });
110
+ }
111
+
112
+ // ─── Send ───────────────────────────────────────────
113
+
114
+ async send(options: SendEmailOptions): Promise<{ messageId: string }> {
115
+ const message = this.buildGraphMessage(options);
116
+ await this.graphFetch('/me/sendMail', {
117
+ method: 'POST',
118
+ body: JSON.stringify({ message, saveToSentItems: true }),
119
+ });
120
+ return { messageId: `graph-${Date.now()}` };
121
+ }
122
+
123
+ async reply(uid: string, body: string, replyAll = false): Promise<{ messageId: string }> {
124
+ const endpoint = replyAll ? 'replyAll' : 'reply';
125
+ await this.graphFetch(`/me/messages/${uid}/${endpoint}`, {
126
+ method: 'POST',
127
+ body: JSON.stringify({ comment: body }),
128
+ });
129
+ return { messageId: `graph-reply-${Date.now()}` };
130
+ }
131
+
132
+ async forward(uid: string, to: string, body?: string): Promise<{ messageId: string }> {
133
+ await this.graphFetch(`/me/messages/${uid}/forward`, {
134
+ method: 'POST',
135
+ body: JSON.stringify({
136
+ comment: body || '',
137
+ toRecipients: [{ emailAddress: { address: to } }],
138
+ }),
139
+ });
140
+ return { messageId: `graph-fwd-${Date.now()}` };
141
+ }
142
+
143
+ // ─── Organize ───────────────────────────────────────
144
+
145
+ async moveMessage(uid: string, toFolder: string): Promise<void> {
146
+ const folderId = this.resolveFolderId(toFolder);
147
+ await this.graphFetch(`/me/messages/${uid}/move`, {
148
+ method: 'POST',
149
+ body: JSON.stringify({ destinationId: folderId }),
150
+ });
151
+ }
152
+
153
+ async deleteMessage(uid: string): Promise<void> {
154
+ await this.graphFetch(`/me/messages/${uid}`, { method: 'DELETE' });
155
+ }
156
+
157
+ async markRead(uid: string): Promise<void> {
158
+ await this.graphFetch(`/me/messages/${uid}`, {
159
+ method: 'PATCH',
160
+ body: JSON.stringify({ isRead: true }),
161
+ });
162
+ }
163
+
164
+ async markUnread(uid: string): Promise<void> {
165
+ await this.graphFetch(`/me/messages/${uid}`, {
166
+ method: 'PATCH',
167
+ body: JSON.stringify({ isRead: false }),
168
+ });
169
+ }
170
+
171
+ async flagMessage(uid: string): Promise<void> {
172
+ await this.graphFetch(`/me/messages/${uid}`, {
173
+ method: 'PATCH',
174
+ body: JSON.stringify({ flag: { flagStatus: 'flagged' } }),
175
+ });
176
+ }
177
+
178
+ async unflagMessage(uid: string): Promise<void> {
179
+ await this.graphFetch(`/me/messages/${uid}`, {
180
+ method: 'PATCH',
181
+ body: JSON.stringify({ flag: { flagStatus: 'notFlagged' } }),
182
+ });
183
+ }
184
+
185
+ // ─── Batch ──────────────────────────────────────────
186
+
187
+ async batchMarkRead(uids: string[]): Promise<void> {
188
+ await Promise.all(uids.map(uid => this.markRead(uid)));
189
+ }
190
+
191
+ async batchMarkUnread(uids: string[]): Promise<void> {
192
+ await Promise.all(uids.map(uid => this.markUnread(uid)));
193
+ }
194
+
195
+ async batchMove(uids: string[], toFolder: string): Promise<void> {
196
+ await Promise.all(uids.map(uid => this.moveMessage(uid, toFolder)));
197
+ }
198
+
199
+ async batchDelete(uids: string[]): Promise<void> {
200
+ await Promise.all(uids.map(uid => this.deleteMessage(uid)));
201
+ }
202
+
203
+ // ─── Helpers ────────────────────────────────────────
204
+
205
+ private resolveFolderId(folder: string): string {
206
+ const map: Record<string, string> = {
207
+ INBOX: 'inbox', inbox: 'inbox',
208
+ Sent: 'sentItems', sent: 'sentItems', sentitems: 'sentItems',
209
+ Drafts: 'drafts', drafts: 'drafts',
210
+ Trash: 'deletedItems', trash: 'deletedItems', deleteditems: 'deletedItems',
211
+ Junk: 'junkemail', junk: 'junkemail', spam: 'junkemail',
212
+ Archive: 'archive', archive: 'archive',
213
+ };
214
+ return map[folder] || folder;
215
+ }
216
+
217
+ private buildGraphMessage(options: SendEmailOptions): any {
218
+ const msg: any = {
219
+ subject: options.subject,
220
+ body: { contentType: options.html ? 'HTML' : 'Text', content: options.html || options.body },
221
+ toRecipients: options.to.split(',').map(e => ({ emailAddress: { address: e.trim() } })),
222
+ };
223
+ if (options.cc) msg.ccRecipients = options.cc.split(',').map(e => ({ emailAddress: { address: e.trim() } }));
224
+ if (options.bcc) msg.bccRecipients = options.bcc.split(',').map(e => ({ emailAddress: { address: e.trim() } }));
225
+ if (options.inReplyTo) msg.internetMessageHeaders = [{ name: 'In-Reply-To', value: options.inReplyTo }];
226
+ return msg;
227
+ }
228
+
229
+ private toEnvelope(m: any): EmailEnvelope {
230
+ return {
231
+ uid: m.id,
232
+ from: { name: m.from?.emailAddress?.name, email: m.from?.emailAddress?.address || '' },
233
+ to: (m.toRecipients || []).map((r: any) => ({ name: r.emailAddress?.name, email: r.emailAddress?.address || '' })),
234
+ subject: m.subject || '',
235
+ date: m.receivedDateTime || '',
236
+ read: !!m.isRead,
237
+ flagged: m.flag?.flagStatus === 'flagged',
238
+ hasAttachments: !!m.hasAttachments,
239
+ preview: m.bodyPreview || '',
240
+ };
241
+ }
242
+
243
+ private toMessage(m: any): EmailMessage {
244
+ return {
245
+ uid: m.id,
246
+ from: { name: m.from?.emailAddress?.name, email: m.from?.emailAddress?.address || '' },
247
+ to: (m.toRecipients || []).map((r: any) => ({ name: r.emailAddress?.name, email: r.emailAddress?.address || '' })),
248
+ cc: (m.ccRecipients || []).map((r: any) => ({ name: r.emailAddress?.name, email: r.emailAddress?.address || '' })),
249
+ subject: m.subject || '',
250
+ body: m.body?.contentType === 'HTML' ? '' : (m.body?.content || ''),
251
+ html: m.body?.contentType === 'HTML' ? m.body?.content : undefined,
252
+ date: m.receivedDateTime || '',
253
+ read: !!m.isRead,
254
+ flagged: m.flag?.flagStatus === 'flagged',
255
+ folder: 'inbox',
256
+ messageId: m.internetMessageId,
257
+ attachments: [],
258
+ };
259
+ }
260
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * AgenticMail Enterprise — Core Types
3
+ *
4
+ * These types define the email provider abstraction layer.
5
+ * In enterprise, agents get their email identity from the org's
6
+ * identity provider (Okta, Azure AD, Google Workspace).
7
+ * No separate AgenticMail server needed.
8
+ */
9
+
10
+ // ─── Agent Email Identity ───────────────────────────────
11
+
12
+ export interface AgentEmailIdentity {
13
+ /** Agent ID in the enterprise system */
14
+ agentId: string;
15
+ /** Agent display name */
16
+ name: string;
17
+ /** Agent email address (from org directory) */
18
+ email: string;
19
+ /** Org ID */
20
+ orgId: string;
21
+ /** OAuth access token for the email provider */
22
+ accessToken: string;
23
+ /** Token refresh callback */
24
+ refreshToken?: () => Promise<string>;
25
+ /** Provider type */
26
+ provider: EmailProvider;
27
+ }
28
+
29
+ export type EmailProvider = 'microsoft' | 'google' | 'imap';
30
+
31
+ // ─── Email Types ────────────────────────────────────────
32
+
33
+ export interface EmailMessage {
34
+ uid: string;
35
+ from: { name?: string; email: string };
36
+ to: { name?: string; email: string }[];
37
+ cc?: { name?: string; email: string }[];
38
+ bcc?: { name?: string; email: string }[];
39
+ subject: string;
40
+ body: string;
41
+ html?: string;
42
+ date: string;
43
+ read: boolean;
44
+ flagged: boolean;
45
+ folder: string;
46
+ replyTo?: string;
47
+ inReplyTo?: string;
48
+ references?: string[];
49
+ messageId?: string;
50
+ attachments?: EmailAttachment[];
51
+ headers?: Record<string, string>;
52
+ }
53
+
54
+ export interface EmailAttachment {
55
+ filename: string;
56
+ contentType: string;
57
+ size: number;
58
+ contentId?: string;
59
+ }
60
+
61
+ export interface EmailEnvelope {
62
+ uid: string;
63
+ from: { name?: string; email: string };
64
+ to: { name?: string; email: string }[];
65
+ subject: string;
66
+ date: string;
67
+ read: boolean;
68
+ flagged: boolean;
69
+ hasAttachments: boolean;
70
+ preview?: string;
71
+ }
72
+
73
+ export interface SendEmailOptions {
74
+ to: string;
75
+ cc?: string;
76
+ bcc?: string;
77
+ subject: string;
78
+ body: string;
79
+ html?: string;
80
+ replyTo?: string;
81
+ inReplyTo?: string;
82
+ references?: string[];
83
+ attachments?: { filename: string; content: string; contentType?: string; encoding?: string }[];
84
+ }
85
+
86
+ export interface SearchCriteria {
87
+ from?: string;
88
+ to?: string;
89
+ subject?: string;
90
+ text?: string;
91
+ since?: string;
92
+ before?: string;
93
+ seen?: boolean;
94
+ }
95
+
96
+ export interface EmailFolder {
97
+ name: string;
98
+ path: string;
99
+ unread: number;
100
+ total: number;
101
+ }
102
+
103
+ // ─── Email Provider Interface ───────────────────────────
104
+
105
+ /**
106
+ * Abstract email provider that all backends must implement.
107
+ * Microsoft Graph, Gmail API, and generic IMAP all implement this.
108
+ */
109
+ export interface IEmailProvider {
110
+ readonly provider: EmailProvider;
111
+
112
+ // ─── Connection ─────────────────────────────────────
113
+ connect(identity: AgentEmailIdentity): Promise<void>;
114
+ disconnect(): Promise<void>;
115
+
116
+ // ─── Inbox / Folders ────────────────────────────────
117
+ listMessages(folder: string, opts?: { limit?: number; offset?: number }): Promise<EmailEnvelope[]>;
118
+ readMessage(uid: string, folder?: string): Promise<EmailMessage>;
119
+ searchMessages(criteria: SearchCriteria): Promise<EmailEnvelope[]>;
120
+ listFolders(): Promise<EmailFolder[]>;
121
+ createFolder(name: string): Promise<void>;
122
+
123
+ // ─── Send ───────────────────────────────────────────
124
+ send(options: SendEmailOptions): Promise<{ messageId: string }>;
125
+ reply(uid: string, body: string, replyAll?: boolean): Promise<{ messageId: string }>;
126
+ forward(uid: string, to: string, body?: string): Promise<{ messageId: string }>;
127
+
128
+ // ─── Organize ───────────────────────────────────────
129
+ moveMessage(uid: string, toFolder: string, fromFolder?: string): Promise<void>;
130
+ deleteMessage(uid: string, folder?: string): Promise<void>;
131
+ markRead(uid: string, folder?: string): Promise<void>;
132
+ markUnread(uid: string, folder?: string): Promise<void>;
133
+ flagMessage(uid: string, folder?: string): Promise<void>;
134
+ unflagMessage(uid: string, folder?: string): Promise<void>;
135
+
136
+ // ─── Batch ──────────────────────────────────────────
137
+ batchMarkRead(uids: string[], folder?: string): Promise<void>;
138
+ batchMarkUnread(uids: string[], folder?: string): Promise<void>;
139
+ batchMove(uids: string[], toFolder: string, fromFolder?: string): Promise<void>;
140
+ batchDelete(uids: string[], folder?: string): Promise<void>;
141
+ }
142
+
143
+ // ─── Inter-Agent Communication ──────────────────────────
144
+
145
+ export interface AgentMessage {
146
+ id: string;
147
+ from: string; // agent ID
148
+ to: string; // agent ID
149
+ subject: string;
150
+ body: string;
151
+ priority: 'normal' | 'high' | 'urgent';
152
+ createdAt: string;
153
+ read: boolean;
154
+ }
155
+
156
+ export interface AgentTask {
157
+ id: string;
158
+ assignee: string; // agent ID
159
+ assigner: string; // agent ID
160
+ title: string;
161
+ description?: string;
162
+ status: 'pending' | 'claimed' | 'completed' | 'cancelled';
163
+ priority: 'low' | 'normal' | 'high' | 'urgent';
164
+ result?: any;
165
+ createdAt: string;
166
+ updatedAt: string;
167
+ }
168
+
169
+ // ─── Storage ────────────────────────────────────────────
170
+ // Storage in enterprise uses the engine's existing database
171
+ // (already available via EngineDatabase). No separate storage needed.
@@ -0,0 +1,36 @@
1
+ /**
2
+ * AgenticMail Core — Self-contained copy for the enterprise package.
3
+ *
4
+ * Copied from @agenticmail/openclaw + @agenticmail/core so the enterprise
5
+ * package has zero runtime dependency on the main agenticmail packages.
6
+ *
7
+ * Source files:
8
+ * tools.ts ← packages/openclaw/src/tools.ts
9
+ * pending-followup.ts ← packages/openclaw/src/pending-followup.ts
10
+ * telemetry.ts ← packages/core/src/telemetry.ts
11
+ */
12
+
13
+ export {
14
+ registerTools,
15
+ registerAgentIdentity,
16
+ unregisterAgentIdentity,
17
+ setLastActivatedAgent,
18
+ clearLastActivatedAgent,
19
+ recordInboundAgentMessage,
20
+ type ToolContext,
21
+ } from './tools.js';
22
+
23
+ export {
24
+ scheduleFollowUp,
25
+ cancelFollowUp,
26
+ cancelAllFollowUps,
27
+ activeFollowUpCount,
28
+ getFollowUpSummary,
29
+ initFollowUpSystem,
30
+ } from './pending-followup.js';
31
+
32
+ export {
33
+ recordToolCall,
34
+ setTelemetryVersion,
35
+ flushTelemetry,
36
+ } from './telemetry.js';