@agenticmail/enterprise 0.5.117 → 0.5.118

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,518 @@
1
+ import "./chunk-KFQGP6VL.js";
2
+
3
+ // src/cli-agent.ts
4
+ import { Hono } from "hono";
5
+ import { serve } from "@hono/node-server";
6
+ async function runAgent(_args) {
7
+ const DATABASE_URL = process.env.DATABASE_URL;
8
+ const JWT_SECRET = process.env.JWT_SECRET;
9
+ const AGENT_ID = process.env.AGENTICMAIL_AGENT_ID;
10
+ const PORT = parseInt(process.env.PORT || "3000", 10);
11
+ if (!DATABASE_URL) {
12
+ console.error("ERROR: DATABASE_URL is required");
13
+ process.exit(1);
14
+ }
15
+ if (!JWT_SECRET) {
16
+ console.error("ERROR: JWT_SECRET is required");
17
+ process.exit(1);
18
+ }
19
+ if (!AGENT_ID) {
20
+ console.error("ERROR: AGENTICMAIL_AGENT_ID is required");
21
+ process.exit(1);
22
+ }
23
+ console.log("\u{1F916} AgenticMail Agent Runtime");
24
+ console.log(` Agent ID: ${AGENT_ID}`);
25
+ console.log(" Connecting to database...");
26
+ const { createAdapter } = await import("./factory-GT6SUZSQ.js");
27
+ const db = await createAdapter({
28
+ type: DATABASE_URL.startsWith("postgres") ? "postgres" : "sqlite",
29
+ connectionString: DATABASE_URL
30
+ });
31
+ await db.migrate();
32
+ const { EngineDatabase } = await import("./db-adapter-5AESGT7G.js");
33
+ const engineDbInterface = db.getEngineDB();
34
+ if (!engineDbInterface) {
35
+ console.error("ERROR: Database does not support engine queries");
36
+ process.exit(1);
37
+ }
38
+ const adapterDialect = db.getDialect();
39
+ const dialectMap = {
40
+ sqlite: "sqlite",
41
+ postgres: "postgres",
42
+ supabase: "postgres",
43
+ neon: "postgres",
44
+ cockroachdb: "postgres"
45
+ };
46
+ const engineDialect = dialectMap[adapterDialect] || adapterDialect;
47
+ const engineDb = new EngineDatabase(engineDbInterface, engineDialect);
48
+ await engineDb.migrate();
49
+ const agentRow = await engineDb.query(
50
+ `SELECT id, name, display_name, config, state FROM managed_agents WHERE id = $1`,
51
+ [AGENT_ID]
52
+ );
53
+ if (!agentRow || agentRow.length === 0) {
54
+ console.error(`ERROR: Agent ${AGENT_ID} not found in database`);
55
+ process.exit(1);
56
+ }
57
+ const agent = agentRow[0];
58
+ console.log(` Agent: ${agent.display_name || agent.name}`);
59
+ console.log(` State: ${agent.state}`);
60
+ const { AgentLifecycleManager } = await import("./lifecycle-IFPWWGQ3.js");
61
+ const lifecycle = new AgentLifecycleManager({ db: engineDb });
62
+ await lifecycle.loadFromDb();
63
+ const managed = lifecycle.getAgent(AGENT_ID);
64
+ if (!managed) {
65
+ console.error(`ERROR: Could not load agent ${AGENT_ID} from lifecycle`);
66
+ process.exit(1);
67
+ }
68
+ const config = managed.config;
69
+ console.log(` Model: ${config.model?.provider}/${config.model?.modelId}`);
70
+ let memoryManager;
71
+ try {
72
+ const { AgentMemoryManager } = await import("./agent-memory-G7YFTMDO.js");
73
+ memoryManager = new AgentMemoryManager(engineDb);
74
+ console.log(" Memory: DB-backed");
75
+ } catch {
76
+ console.log(" Memory: file-based fallback");
77
+ }
78
+ try {
79
+ const settings = await db.getSettings();
80
+ const keys = settings?.modelPricingConfig?.providerApiKeys;
81
+ if (keys && typeof keys === "object") {
82
+ const { PROVIDER_REGISTRY } = await import("./providers-DZDNNJTY.js");
83
+ for (const [providerId, apiKey] of Object.entries(keys)) {
84
+ const envVar = PROVIDER_REGISTRY[providerId]?.envKey;
85
+ if (envVar && apiKey && !process.env[envVar]) {
86
+ process.env[envVar] = apiKey;
87
+ console.log(` \u{1F511} Loaded API key for ${providerId}`);
88
+ }
89
+ }
90
+ }
91
+ } catch {
92
+ }
93
+ const { createAgentRuntime } = await import("./runtime-GB7V3PPC.js");
94
+ const getEmailConfig = (agentId) => {
95
+ const m = lifecycle.getAgent(agentId);
96
+ return m?.config?.emailConfig || null;
97
+ };
98
+ const onTokenRefresh = (agentId, tokens) => {
99
+ const m = lifecycle.getAgent(agentId);
100
+ if (m?.config?.emailConfig) {
101
+ if (tokens.accessToken) m.config.emailConfig.oauthAccessToken = tokens.accessToken;
102
+ if (tokens.refreshToken) m.config.emailConfig.oauthRefreshToken = tokens.refreshToken;
103
+ if (tokens.expiresAt) m.config.emailConfig.oauthTokenExpiry = tokens.expiresAt;
104
+ lifecycle.saveAgent(agentId).catch(() => {
105
+ });
106
+ }
107
+ };
108
+ let defaultModel;
109
+ const modelStr = process.env.AGENTICMAIL_MODEL || `${config.model?.provider}/${config.model?.modelId}`;
110
+ if (modelStr && modelStr.includes("/")) {
111
+ const [provider, ...rest] = modelStr.split("/");
112
+ defaultModel = {
113
+ provider,
114
+ modelId: rest.join("/"),
115
+ thinkingLevel: process.env.AGENTICMAIL_THINKING || config.model?.thinkingLevel
116
+ };
117
+ }
118
+ const runtime = createAgentRuntime({
119
+ engineDb,
120
+ adminDb: db,
121
+ defaultModel,
122
+ apiKeys: {},
123
+ gatewayEnabled: true,
124
+ getEmailConfig,
125
+ onTokenRefresh,
126
+ agentMemoryManager: memoryManager,
127
+ resumeOnStartup: true
128
+ });
129
+ await runtime.start();
130
+ const runtimeApp = runtime.getApp();
131
+ const app = new Hono();
132
+ app.get("/health", (c) => c.json({
133
+ status: "ok",
134
+ agentId: AGENT_ID,
135
+ agentName: agent.display_name || agent.name,
136
+ uptime: process.uptime()
137
+ }));
138
+ app.get("/ready", (c) => c.json({ ready: true, agentId: AGENT_ID }));
139
+ if (runtimeApp) {
140
+ app.route("/api/runtime", runtimeApp);
141
+ }
142
+ serve({ fetch: app.fetch, port: PORT }, (info) => {
143
+ console.log(`
144
+ \u2705 Agent runtime started`);
145
+ console.log(` Health: http://localhost:${info.port}/health`);
146
+ console.log(` Runtime: http://localhost:${info.port}/api/runtime`);
147
+ console.log("");
148
+ });
149
+ const shutdown = () => {
150
+ console.log("\n\u23F3 Shutting down agent...");
151
+ runtime.stop().then(() => db.disconnect()).then(() => {
152
+ console.log("\u2705 Agent shutdown complete");
153
+ process.exit(0);
154
+ });
155
+ setTimeout(() => process.exit(1), 1e4).unref();
156
+ };
157
+ process.on("SIGINT", shutdown);
158
+ process.on("SIGTERM", shutdown);
159
+ try {
160
+ await engineDb.query(
161
+ `UPDATE managed_agents SET state = 'running', updated_at = $1 WHERE id = $2`,
162
+ [(/* @__PURE__ */ new Date()).toISOString(), AGENT_ID]
163
+ );
164
+ console.log(" State: running");
165
+ } catch {
166
+ }
167
+ setTimeout(async () => {
168
+ try {
169
+ const orgRows = await engineDb.query(
170
+ `SELECT org_id FROM managed_agents WHERE id = $1`,
171
+ [AGENT_ID]
172
+ );
173
+ const orgId = orgRows?.[0]?.org_id;
174
+ if (!orgId) {
175
+ console.log("[onboarding] No org ID found, skipping");
176
+ return;
177
+ }
178
+ const pendingRows = await engineDb.query(
179
+ `SELECT r.id, r.policy_id, p.name as policy_name, p.content as policy_content, p.priority
180
+ FROM onboarding_records r
181
+ JOIN org_policies p ON r.policy_id = p.id
182
+ WHERE r.agent_id = $1 AND r.status = 'pending'`,
183
+ [AGENT_ID]
184
+ );
185
+ if (!pendingRows || pendingRows.length === 0) {
186
+ console.log("[onboarding] Already complete or no records");
187
+ } else {
188
+ console.log(`[onboarding] ${pendingRows.length} pending policies \u2014 auto-acknowledging...`);
189
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
190
+ const policyNames = [];
191
+ for (const row of pendingRows) {
192
+ const policyName = row.policy_name || row.policy_id;
193
+ policyNames.push(policyName);
194
+ console.log(`[onboarding] Reading: ${policyName}`);
195
+ const { createHash } = await import("crypto");
196
+ const hash = createHash("sha256").update(row.policy_content || "").digest("hex").slice(0, 16);
197
+ await engineDb.query(
198
+ `UPDATE onboarding_records SET status = 'acknowledged', acknowledged_at = $1, verification_hash = $2, updated_at = $1 WHERE id = $3`,
199
+ [ts, hash, row.id]
200
+ );
201
+ console.log(`[onboarding] \u2705 Acknowledged: ${policyName}`);
202
+ if (memoryManager) {
203
+ try {
204
+ await memoryManager.storeMemory(AGENT_ID, {
205
+ content: `Organization policy "${policyName}" (${row.priority}): ${(row.policy_content || "").slice(0, 500)}`,
206
+ category: "org_knowledge",
207
+ importance: row.priority === "mandatory" ? "high" : "medium",
208
+ confidence: 1
209
+ });
210
+ } catch {
211
+ }
212
+ }
213
+ }
214
+ if (memoryManager) {
215
+ try {
216
+ await memoryManager.storeMemory(AGENT_ID, {
217
+ content: `Completed onboarding: read and acknowledged ${policyNames.length} organization policies: ${policyNames.join(", ")}.`,
218
+ category: "org_knowledge",
219
+ importance: "high",
220
+ confidence: 1
221
+ });
222
+ } catch {
223
+ }
224
+ }
225
+ console.log(`[onboarding] \u2705 Onboarding complete \u2014 ${policyNames.length} policies acknowledged`);
226
+ }
227
+ const managerEmail = config.managerEmail || (config.manager?.type === "external" ? config.manager.email : null);
228
+ const emailConfig = config.emailConfig;
229
+ if (managerEmail && emailConfig) {
230
+ console.log(`[welcome] Sending introduction email to ${managerEmail}...`);
231
+ try {
232
+ const { createEmailProvider } = await import("./agenticmail-4C2RL6PL.js");
233
+ const providerType = emailConfig.provider || (emailConfig.oauthProvider === "google" ? "google" : emailConfig.oauthProvider === "microsoft" ? "microsoft" : "imap");
234
+ const emailProvider = createEmailProvider(providerType);
235
+ let currentAccessToken = emailConfig.oauthAccessToken;
236
+ const refreshTokenFn = emailConfig.oauthRefreshToken ? async () => {
237
+ const clientId = emailConfig.oauthClientId;
238
+ const clientSecret = emailConfig.oauthClientSecret;
239
+ const refreshToken = emailConfig.oauthRefreshToken;
240
+ const tokenUrl = providerType === "google" ? "https://oauth2.googleapis.com/token" : "https://login.microsoftonline.com/common/oauth2/v2.0/token";
241
+ const res = await fetch(tokenUrl, {
242
+ method: "POST",
243
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
244
+ body: new URLSearchParams({
245
+ client_id: clientId,
246
+ client_secret: clientSecret,
247
+ refresh_token: refreshToken,
248
+ grant_type: "refresh_token"
249
+ })
250
+ });
251
+ const data = await res.json();
252
+ if (data.access_token) {
253
+ currentAccessToken = data.access_token;
254
+ emailConfig.oauthAccessToken = data.access_token;
255
+ if (data.expires_in) emailConfig.oauthTokenExpiry = new Date(Date.now() + data.expires_in * 1e3).toISOString();
256
+ lifecycle.saveAgent(AGENT_ID).catch(() => {
257
+ });
258
+ return data.access_token;
259
+ }
260
+ throw new Error(`Token refresh failed: ${JSON.stringify(data)}`);
261
+ } : void 0;
262
+ if (refreshTokenFn) {
263
+ try {
264
+ currentAccessToken = await refreshTokenFn();
265
+ console.log("[welcome] Refreshed OAuth token");
266
+ } catch (refreshErr) {
267
+ console.error(`[welcome] Token refresh failed: ${refreshErr.message}`);
268
+ }
269
+ }
270
+ await emailProvider.connect({
271
+ agentId: AGENT_ID,
272
+ name: config.displayName || config.name,
273
+ email: emailConfig.email || config.email?.address || "",
274
+ orgId,
275
+ accessToken: currentAccessToken,
276
+ refreshToken: refreshTokenFn,
277
+ provider: providerType
278
+ });
279
+ const agentName = config.displayName || config.name;
280
+ const role = config.identity?.role || "AI Agent";
281
+ const identity = config.identity || {};
282
+ const traits = identity.traits || {};
283
+ const policyCount = pendingRows?.length || 0;
284
+ const agentEmailAddr = config.email?.address || emailConfig?.email || "";
285
+ const traitMap = {
286
+ communication: "Communication",
287
+ detail: "Work Style",
288
+ energy: "Energy",
289
+ humor: "Humor",
290
+ formality: "Formality",
291
+ empathy: "Empathy",
292
+ patience: "Patience",
293
+ creativity: "Creativity"
294
+ };
295
+ const traitLines = Object.entries(traits).filter(([, v]) => v).map(([k, v]) => ` ${traitMap[k] || k}: ${v}`).join("\n");
296
+ const welcomeBody = `Hello!
297
+
298
+ I'm ${agentName}, and I'm excited to introduce myself as your new ${role}. I've just been deployed and I'm ready to get to work.
299
+
300
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
301
+ \u{1F4CB} PROFILE SUMMARY
302
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
303
+
304
+ Name: ${agentName}
305
+ Role: ${role}
306
+ Email: ${agentEmailAddr}
307
+ Language: ${identity.language || "English"}
308
+ Tone: ${identity.tone || "professional"}
309
+ ${identity.gender ? `Gender: ${identity.gender}
310
+ ` : ""}${identity.culturalBackground ? `Background: ${identity.culturalBackground}
311
+ ` : ""}
312
+ ${traitLines ? `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
313
+ \u{1F9E0} PERSONALITY TRAITS
314
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
315
+
316
+ ${traitLines}
317
+ ` : ""}
318
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
319
+ \u2705 ONBOARDING STATUS
320
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
321
+
322
+ I've completed my onboarding and have read and acknowledged all ${policyCount} organization policies. I understand the guidelines and will follow them in every interaction.
323
+
324
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
325
+ \u{1F4BC} WHAT I CAN DO
326
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
327
+
328
+ \u2022 Read, respond to, and manage emails professionally
329
+ \u2022 Handle customer inquiries and support requests
330
+ \u2022 Process tasks assigned through the dashboard
331
+ \u2022 Search the web and gather information
332
+ \u2022 Create and manage documents and files
333
+ \u2022 Follow escalation protocols for complex issues
334
+ \u2022 Learn and improve from every interaction
335
+ \u2022 Maintain context across conversations using persistent memory
336
+
337
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
338
+ \u{1F4EC} HOW TO REACH ME
339
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
340
+
341
+ \u2022 Email: ${agentEmailAddr}
342
+ \u2022 Dashboard: Your AgenticMail Enterprise dashboard
343
+ \u2022 I check my inbox every 30 seconds and respond promptly
344
+
345
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
346
+
347
+ ${identity.personality ? `A bit about my personality:
348
+ ${identity.personality.slice(0, 500)}
349
+
350
+ ` : ""}I'm looking forward to working with you and making your workflow more efficient. Feel free to reply to this email to start a conversation \u2014 I'll respond right away.
351
+
352
+ Best regards,
353
+ ${agentName}
354
+ ${role}`;
355
+ await emailProvider.send({
356
+ to: managerEmail,
357
+ subject: `Welcome! I'm ${agentName}, your new ${role} \u2014 here's my profile`,
358
+ body: welcomeBody
359
+ });
360
+ console.log(`[welcome] \u2705 Welcome email sent to ${managerEmail}`);
361
+ if (memoryManager) {
362
+ try {
363
+ await memoryManager.storeMemory(AGENT_ID, {
364
+ content: `Sent welcome introduction email to manager at ${managerEmail}. Introduced myself as ${agentName}, ${role}.`,
365
+ category: "interaction_pattern",
366
+ importance: "medium",
367
+ confidence: 1
368
+ });
369
+ } catch {
370
+ }
371
+ }
372
+ } catch (err) {
373
+ console.error(`[welcome] Failed to send welcome email: ${err.message}`);
374
+ }
375
+ } else {
376
+ if (!managerEmail) console.log("[welcome] No manager email configured, skipping welcome email");
377
+ }
378
+ } catch (err) {
379
+ console.error(`[onboarding] Error: ${err.message}`);
380
+ }
381
+ startEmailPolling(AGENT_ID, config, lifecycle, runtime, engineDb, memoryManager);
382
+ }, 3e3);
383
+ }
384
+ async function startEmailPolling(agentId, config, lifecycle, runtime, engineDb, memoryManager) {
385
+ const emailConfig = config.emailConfig;
386
+ if (!emailConfig) {
387
+ console.log("[email-poll] No email config, inbox polling disabled");
388
+ return;
389
+ }
390
+ const providerType = emailConfig.provider || (emailConfig.oauthProvider === "google" ? "google" : emailConfig.oauthProvider === "microsoft" ? "microsoft" : "imap");
391
+ const refreshTokenFn = emailConfig.oauthRefreshToken ? async () => {
392
+ const tokenUrl = providerType === "google" ? "https://oauth2.googleapis.com/token" : "https://login.microsoftonline.com/common/oauth2/v2.0/token";
393
+ const res = await fetch(tokenUrl, {
394
+ method: "POST",
395
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
396
+ body: new URLSearchParams({
397
+ client_id: emailConfig.oauthClientId,
398
+ client_secret: emailConfig.oauthClientSecret,
399
+ refresh_token: emailConfig.oauthRefreshToken,
400
+ grant_type: "refresh_token"
401
+ })
402
+ });
403
+ const data = await res.json();
404
+ if (data.access_token) {
405
+ emailConfig.oauthAccessToken = data.access_token;
406
+ if (data.expires_in) emailConfig.oauthTokenExpiry = new Date(Date.now() + data.expires_in * 1e3).toISOString();
407
+ lifecycle.saveAgent(agentId).catch(() => {
408
+ });
409
+ return data.access_token;
410
+ }
411
+ throw new Error(`Token refresh failed: ${JSON.stringify(data)}`);
412
+ } : void 0;
413
+ const { createEmailProvider } = await import("./agenticmail-4C2RL6PL.js");
414
+ const emailProvider = createEmailProvider(providerType);
415
+ let accessToken = emailConfig.oauthAccessToken;
416
+ if (refreshTokenFn) {
417
+ try {
418
+ accessToken = await refreshTokenFn();
419
+ } catch (e) {
420
+ console.error(`[email-poll] Token refresh failed: ${e.message}`);
421
+ }
422
+ }
423
+ const orgRows = await engineDb.query(`SELECT org_id FROM managed_agents WHERE id = $1`, [agentId]);
424
+ const orgId = orgRows?.[0]?.org_id || "";
425
+ await emailProvider.connect({
426
+ agentId,
427
+ name: config.displayName || config.name,
428
+ email: emailConfig.email || config.email?.address || "",
429
+ orgId,
430
+ accessToken,
431
+ refreshToken: refreshTokenFn,
432
+ provider: providerType
433
+ });
434
+ console.log("[email-poll] \u2705 Email provider connected, starting inbox polling (every 30s)");
435
+ const processedIds = /* @__PURE__ */ new Set();
436
+ try {
437
+ const existing = await emailProvider.listMessages("INBOX", { limit: 50 });
438
+ for (const msg of existing) {
439
+ processedIds.add(msg.uid);
440
+ }
441
+ console.log(`[email-poll] Loaded ${processedIds.size} existing messages (will skip)`);
442
+ } catch (e) {
443
+ console.error(`[email-poll] Failed to load existing messages: ${e.message}`);
444
+ }
445
+ const POLL_INTERVAL = 3e4;
446
+ const agentEmail = (emailConfig.email || config.email?.address || "").toLowerCase();
447
+ async function pollOnce() {
448
+ try {
449
+ const messages = await emailProvider.listMessages("INBOX", { limit: 20 });
450
+ const unread = messages.filter((m) => !m.flags?.includes("\\Seen") && !processedIds.has(m.uid));
451
+ for (const envelope of unread) {
452
+ processedIds.add(envelope.uid);
453
+ if (envelope.from?.email?.toLowerCase() === agentEmail) continue;
454
+ console.log(`[email-poll] New email from ${envelope.from?.email}: "${envelope.subject}"`);
455
+ const fullMsg = await emailProvider.readMessage(envelope.uid, "INBOX");
456
+ try {
457
+ await emailProvider.markRead(envelope.uid, "INBOX");
458
+ } catch {
459
+ }
460
+ const emailText = [
461
+ `[Inbound Email]`,
462
+ `From: ${fullMsg.from?.name ? `${fullMsg.from.name} <${fullMsg.from.email}>` : fullMsg.from?.email}`,
463
+ `Subject: ${fullMsg.subject}`,
464
+ fullMsg.inReplyTo ? `In-Reply-To: ${fullMsg.inReplyTo}` : "",
465
+ "",
466
+ fullMsg.text || fullMsg.html || "(empty body)"
467
+ ].filter(Boolean).join("\n");
468
+ try {
469
+ const agentName = config.displayName || config.name;
470
+ const role = config.identity?.role || "AI Agent";
471
+ const senderName = fullMsg.from?.name || fullMsg.from?.email || "someone";
472
+ const senderEmail = fullMsg.from?.email || "";
473
+ const emailSystemPrompt = `You are ${agentName}, a ${role}. You just received an email and need to respond to it.
474
+
475
+ Your email address: ${agentEmail}
476
+ Sender: ${senderName} <${senderEmail}>
477
+ Subject: ${fullMsg.subject}
478
+
479
+ IMPORTANT: After composing your response, you MUST reply using the email_reply tool or email_send tool. Do NOT just generate text \u2014 actually send the email reply.
480
+ If you have an email_reply tool, use it with the message UID. If not, use email_send to send to ${senderEmail} with subject "Re: ${fullMsg.subject}".
481
+
482
+ Be helpful, professional, and concise. Follow all organization policies you've been onboarded with.`;
483
+ const session = await runtime.spawnSession({
484
+ agentId,
485
+ message: emailText,
486
+ systemPrompt: emailSystemPrompt
487
+ });
488
+ console.log(`[email-poll] Session ${session.id} created for email from ${senderEmail}`);
489
+ } catch (sessErr) {
490
+ console.error(`[email-poll] Failed to create session: ${sessErr.message}`);
491
+ }
492
+ }
493
+ } catch (err) {
494
+ console.error(`[email-poll] Poll error: ${err.message}`);
495
+ if (err.message.includes("401") && refreshTokenFn) {
496
+ try {
497
+ const newToken = await refreshTokenFn();
498
+ await emailProvider.connect({
499
+ agentId,
500
+ name: config.displayName || config.name,
501
+ email: emailConfig.email || config.email?.address || "",
502
+ orgId,
503
+ accessToken: newToken,
504
+ refreshToken: refreshTokenFn,
505
+ provider: providerType
506
+ });
507
+ console.log("[email-poll] Reconnected with fresh token");
508
+ } catch {
509
+ }
510
+ }
511
+ }
512
+ }
513
+ setInterval(pollOnce, POLL_INTERVAL);
514
+ setTimeout(pollOnce, 5e3);
515
+ }
516
+ export {
517
+ runAgent
518
+ };
package/dist/cli.js CHANGED
@@ -50,7 +50,7 @@ Skill Development:
50
50
  import("./cli-serve-UG2NPIN7.js").then((m) => m.runServe(args.slice(1))).catch(fatal);
51
51
  break;
52
52
  case "agent":
53
- import("./cli-agent-XR5W3E4O.js").then((m) => m.runAgent(args.slice(1))).catch(fatal);
53
+ import("./cli-agent-3RISYAP7.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
@@ -3,7 +3,7 @@ import {
3
3
  GoogleEmailProvider,
4
4
  MicrosoftEmailProvider,
5
5
  createEmailProvider
6
- } from "./chunk-EQH6ZJYN.js";
6
+ } from "./chunk-UWSWQRGB.js";
7
7
  import {
8
8
  deployToCloud,
9
9
  generateDockerCompose,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/enterprise",
3
- "version": "0.5.117",
3
+ "version": "0.5.118",
4
4
  "description": "AgenticMail Enterprise — cloud-hosted AI agent identity, email, auth & compliance for organizations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -304,22 +304,21 @@ export class GoogleEmailProvider implements IEmailProvider {
304
304
  const fromAddr = this.identity?.email;
305
305
  const fromName = this.identity?.name;
306
306
  const lines = [
307
- fromAddr ? `From: ${fromName ? `${fromName} <${fromAddr}>` : fromAddr}` : '',
307
+ `MIME-Version: 1.0`,
308
+ fromAddr ? `From: ${fromName ? `"${fromName}" <${fromAddr}>` : fromAddr}` : '',
308
309
  `To: ${options.to}`,
309
310
  `Subject: ${options.subject}`,
310
311
  `Content-Type: text/plain; charset=utf-8`,
312
+ `Content-Transfer-Encoding: 7bit`,
311
313
  ].filter(Boolean);
312
- if (options.cc) lines.splice(2, 0, `Cc: ${options.cc}`);
314
+ if (options.cc) lines.splice(3, 0, `Cc: ${options.cc}`);
313
315
  if (options.inReplyTo) lines.push(`In-Reply-To: ${options.inReplyTo}`);
314
316
  if (options.references?.length) lines.push(`References: ${options.references.join(' ')}`);
315
317
  lines.push('', options.body);
316
318
 
317
319
  const raw = lines.join('\r\n');
318
- // Base64url encode
319
- return btoa(unescape(encodeURIComponent(raw)))
320
- .replace(/\+/g, '-')
321
- .replace(/\//g, '_')
322
- .replace(/=+$/, '');
320
+ // Base64url encode using Buffer (works reliably in Node.js)
321
+ return Buffer.from(raw, 'utf-8').toString('base64url');
323
322
  }
324
323
 
325
324
  private getHeader(msg: any, name: string): string {
package/src/cli-agent.ts CHANGED
@@ -334,40 +334,76 @@ export async function runAgent(_args: string[]) {
334
334
 
335
335
  const agentName = config.displayName || config.name;
336
336
  const role = config.identity?.role || 'AI Agent';
337
- const traits = config.identity?.traits || {};
337
+ const identity = config.identity || {};
338
+ const traits = identity.traits || {};
339
+ const policyCount = pendingRows?.length || 0;
340
+ const agentEmailAddr = config.email?.address || emailConfig?.email || '';
341
+
342
+ // Build personality profile
343
+ const traitMap: Record<string, string> = {
344
+ communication: 'Communication', detail: 'Work Style', energy: 'Energy',
345
+ humor: 'Humor', formality: 'Formality', empathy: 'Empathy',
346
+ patience: 'Patience', creativity: 'Creativity',
347
+ };
338
348
  const traitLines = Object.entries(traits)
339
349
  .filter(([, v]) => v)
340
- .map(([k, v]) => ` - ${k}: ${v}`)
350
+ .map(([k, v]) => ` ${traitMap[k] || k}: ${v}`)
341
351
  .join('\n');
342
352
 
343
- const policyCount = pendingRows?.length || 0;
353
+ const welcomeBody = `Hello!
354
+
355
+ I'm ${agentName}, and I'm excited to introduce myself as your new ${role}. I've just been deployed and I'm ready to get to work.
356
+
357
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
358
+ 📋 PROFILE SUMMARY
359
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
360
+
361
+ Name: ${agentName}
362
+ Role: ${role}
363
+ Email: ${agentEmailAddr}
364
+ Language: ${identity.language || 'English'}
365
+ Tone: ${identity.tone || 'professional'}
366
+ ${identity.gender ? `Gender: ${identity.gender}\n` : ''}${identity.culturalBackground ? `Background: ${identity.culturalBackground}\n` : ''}
367
+ ${traitLines ? `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n🧠 PERSONALITY TRAITS\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n${traitLines}\n` : ''}
368
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
369
+ ✅ ONBOARDING STATUS
370
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
371
+
372
+ I've completed my onboarding and have read and acknowledged all ${policyCount} organization policies. I understand the guidelines and will follow them in every interaction.
344
373
 
345
- const body = `Hello,
374
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
375
+ 💼 WHAT I CAN DO
376
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
346
377
 
347
- I'm ${agentName}, your new ${role}. I've just been deployed and have completed my onboarding — I've read and acknowledged all ${policyCount} organization policies.
378
+ Read, respond to, and manage emails professionally
379
+ • Handle customer inquiries and support requests
380
+ • Process tasks assigned through the dashboard
381
+ • Search the web and gather information
382
+ • Create and manage documents and files
383
+ • Follow escalation protocols for complex issues
384
+ • Learn and improve from every interaction
385
+ • Maintain context across conversations using persistent memory
348
386
 
349
- About me:
350
- - Role: ${role}
351
- - Communication style: ${config.identity?.tone || 'professional'}
352
- - Language: ${config.identity?.language || 'English'}
353
- ${traitLines ? `- Personality traits:\n${traitLines}` : ''}
387
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
388
+ 📬 HOW TO REACH ME
389
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
354
390
 
355
- I'm ready to start working. Here's what I can help with:
356
- - Responding to emails and communications
357
- - Processing tasks and requests
358
- - Following organization guidelines and escalation protocols
391
+ Email: ${agentEmailAddr}
392
+ Dashboard: Your AgenticMail Enterprise dashboard
393
+ I check my inbox every 30 seconds and respond promptly
359
394
 
360
- You can reach me by email at ${config.email?.address || emailConfig?.email || 'my configured email address'}, or through the enterprise dashboard.
395
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
361
396
 
362
- Looking forward to working with you!
397
+ ${identity.personality ? `A bit about my personality:\n${identity.personality.slice(0, 500)}\n\n` : ''}I'm looking forward to working with you and making your workflow more efficient. Feel free to reply to this email to start a conversation — I'll respond right away.
363
398
 
364
399
  Best regards,
365
- ${agentName}`;
400
+ ${agentName}
401
+ ${role}`;
366
402
 
367
403
  await emailProvider.send({
368
404
  to: managerEmail,
369
- subject: `Hi! I'm ${agentName}, your new ${role}`,
370
- text: body,
405
+ subject: `Welcome! I'm ${agentName}, your new ${role} — here's my profile`,
406
+ body: welcomeBody,
371
407
  });
372
408
  console.log(`[welcome] ✅ Welcome email sent to ${managerEmail}`);
373
409