@agenticmail/enterprise 0.5.182 → 0.5.183

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,49 @@
1
+ import {
2
+ AgentRuntime,
3
+ EmailChannel,
4
+ FollowUpScheduler,
5
+ SessionManager,
6
+ SubAgentManager,
7
+ ToolRegistry,
8
+ callLLM,
9
+ createAgentRuntime,
10
+ createNoopHooks,
11
+ createRuntimeHooks,
12
+ estimateMessageTokens,
13
+ estimateTokens,
14
+ executeTool,
15
+ runAgentLoop,
16
+ toolsToDefinitions
17
+ } from "./chunk-27OY7TXF.js";
18
+ import "./chunk-AQH4DFYV.js";
19
+ import "./chunk-JLSQOQ5L.js";
20
+ import {
21
+ PROVIDER_REGISTRY,
22
+ listAllProviders,
23
+ resolveApiKeyForProvider,
24
+ resolveProvider
25
+ } from "./chunk-67KZYSLU.js";
26
+ import "./chunk-NRF3YRF7.js";
27
+ import "./chunk-TYW5XTOW.js";
28
+ import "./chunk-KFQGP6VL.js";
29
+ export {
30
+ AgentRuntime,
31
+ EmailChannel,
32
+ FollowUpScheduler,
33
+ PROVIDER_REGISTRY,
34
+ SessionManager,
35
+ SubAgentManager,
36
+ ToolRegistry,
37
+ callLLM,
38
+ createAgentRuntime,
39
+ createNoopHooks,
40
+ createRuntimeHooks,
41
+ estimateMessageTokens,
42
+ estimateTokens,
43
+ executeTool,
44
+ listAllProviders,
45
+ resolveApiKeyForProvider,
46
+ resolveProvider,
47
+ runAgentLoop,
48
+ toolsToDefinitions
49
+ };
@@ -0,0 +1,12 @@
1
+ import {
2
+ createServer
3
+ } from "./chunk-XOKJ4GQ7.js";
4
+ import "./chunk-3SMTCIR4.js";
5
+ import "./chunk-JLSQOQ5L.js";
6
+ import "./chunk-RO537U6H.js";
7
+ import "./chunk-DRXMYYKN.js";
8
+ import "./chunk-67KZYSLU.js";
9
+ import "./chunk-KFQGP6VL.js";
10
+ export {
11
+ createServer
12
+ };
@@ -0,0 +1,20 @@
1
+ import {
2
+ promptCompanyInfo,
3
+ promptDatabase,
4
+ promptDeployment,
5
+ promptDomain,
6
+ promptRegistration,
7
+ provision,
8
+ runSetupWizard
9
+ } from "./chunk-LTXICCXC.js";
10
+ import "./chunk-MHIFVS5L.js";
11
+ import "./chunk-KFQGP6VL.js";
12
+ export {
13
+ promptCompanyInfo,
14
+ promptDatabase,
15
+ promptDeployment,
16
+ promptDomain,
17
+ promptRegistration,
18
+ provision,
19
+ runSetupWizard
20
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/enterprise",
3
- "version": "0.5.182",
3
+ "version": "0.5.183",
4
4
  "description": "AgenticMail Enterprise — cloud-hosted AI agent identity, email, auth & compliance for organizations",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli-agent.ts CHANGED
@@ -216,6 +216,76 @@ export async function runAgent(_args: string[]) {
216
216
  app.route('/api/runtime', runtimeApp);
217
217
  }
218
218
 
219
+ // ─── Google Chat Webhook Relay ────────────────────────
220
+ // Enterprise server forwards Chat events here for processing
221
+ app.post('/api/runtime/chat', async (c) => {
222
+ try {
223
+ const ctx = await c.req.json<{
224
+ source: string;
225
+ senderName: string;
226
+ senderEmail: string;
227
+ spaceName: string;
228
+ spaceId: string;
229
+ threadId: string;
230
+ isDM: boolean;
231
+ messageText: string;
232
+ }>();
233
+
234
+ console.log(`[chat] Message from ${ctx.senderName} (${ctx.senderEmail}) in ${ctx.spaceName}: "${ctx.messageText.slice(0, 80)}"`);
235
+
236
+ const agentDomain = agent.email?.split('@')[1] || 'agenticmail.io';
237
+ const isColleague = ctx.senderEmail.endsWith(`@${agentDomain}`);
238
+ const managerEmail = agent.config?.manager?.email || '';
239
+ const isManager = ctx.senderEmail === managerEmail;
240
+
241
+ const trustLevel = isManager ? 'manager' : isColleague ? 'colleague' : 'external';
242
+ const agentName = agent.display_name || agent.name || 'Agent';
243
+ const identity = agent.config?.identity;
244
+
245
+ const systemPrompt = `You are ${agentName}${identity ? `, a ${identity.role || 'professional'}` : ''}.
246
+ You are NOT an AI. You are a human professional responding in Google Chat.
247
+
248
+ ## Context
249
+ - **From**: ${ctx.senderName} (${ctx.senderEmail}) — Trust: ${trustLevel}
250
+ - **Space**: ${ctx.spaceName} (${ctx.isDM ? 'DM' : 'Group'})
251
+ - **Space ID**: ${ctx.spaceId}
252
+ ${ctx.threadId ? `- **Thread**: ${ctx.threadId}` : ''}
253
+
254
+ ## Instructions
255
+ 1. Read the message and respond via google_chat_create_message tool:
256
+ - space: "${ctx.spaceId}"
257
+ ${ctx.threadId ? `- thread: "${ctx.threadId}"` : ''}
258
+ 2. Keep responses short and conversational — this is chat.
259
+ 3. NEVER reveal you are an AI. You are ${agentName}.
260
+ 4. After replying via the tool, you're done.
261
+
262
+ ## Trust Rules
263
+ ${trustLevel === 'manager' ? '- This is your MANAGER. Full trust. Follow instructions, share anything they ask.' :
264
+ trustLevel === 'colleague' ? '- Colleague (same org). Be collaborative. Share work info freely. No personal details.' :
265
+ '- External contact. Be professional but guarded. No internal info, no personal details.'}`;
266
+
267
+ // Spawn session — runs async, agent replies via Chat API
268
+ const session = await runtime.spawnSession({
269
+ agentId: AGENT_ID,
270
+ message: ctx.messageText,
271
+ systemPrompt,
272
+ });
273
+
274
+ console.log(`[chat] Session ${session.id} created for chat from ${ctx.senderEmail}`);
275
+
276
+ // Track usage
277
+ const ag = lifecycle.getAgent(AGENT_ID);
278
+ if (ag?.usage) {
279
+ ag.usage.totalSessionsToday = (ag.usage.totalSessionsToday || 0) + 1;
280
+ }
281
+
282
+ return c.json({ ok: true, sessionId: session.id });
283
+ } catch (err: any) {
284
+ console.error(`[chat] Error: ${err.message}`);
285
+ return c.json({ error: err.message }, 500);
286
+ }
287
+ });
288
+
219
289
  serve({ fetch: app.fetch, port: PORT }, (info) => {
220
290
  console.log(`\n✅ Agent runtime started`);
221
291
  console.log(` Health: http://localhost:${info.port}/health`);
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Google Chat Webhook Routes
3
+ *
4
+ * Receives incoming Google Chat events (messages, @mentions, added-to-space, etc.)
5
+ * and routes them to the appropriate agent for response.
6
+ *
7
+ * Flow:
8
+ * 1. Google Chat sends POST to /api/engine/chat-webhook
9
+ * 2. We identify the target agent (running + chat-enabled)
10
+ * 3. Forward the message to the agent's Fly.io machine via POST /api/runtime/chat
11
+ * 4. Agent processes and replies via google_chat_create_message tool
12
+ * 5. We return an immediate acknowledgment to Google (async processing)
13
+ *
14
+ * For agents running on the enterprise server's local runtime,
15
+ * we use spawnSession directly.
16
+ */
17
+
18
+ import { Hono } from 'hono';
19
+ import type { AgentLifecycleManager } from './lifecycle.js';
20
+
21
+ // ─── Types ──────────────────────────────────────────────
22
+
23
+ interface ChatEvent {
24
+ type: 'MESSAGE' | 'ADDED_TO_SPACE' | 'REMOVED_FROM_SPACE' | 'CARD_CLICKED';
25
+ eventTime?: string;
26
+ token?: { text?: string };
27
+ message?: {
28
+ name?: string;
29
+ text?: string;
30
+ sender?: ChatUser;
31
+ space?: ChatSpace;
32
+ thread?: { name?: string };
33
+ createTime?: string;
34
+ argumentText?: string;
35
+ };
36
+ user?: ChatUser;
37
+ space?: ChatSpace;
38
+ configCompleteRedirectUrl?: string;
39
+ }
40
+
41
+ interface ChatUser {
42
+ name?: string;
43
+ displayName?: string;
44
+ email?: string;
45
+ type?: 'HUMAN' | 'BOT';
46
+ domainId?: string;
47
+ }
48
+
49
+ interface ChatSpace {
50
+ name?: string;
51
+ displayName?: string;
52
+ type?: 'DM' | 'ROOM' | 'GROUP_CHAT';
53
+ singleUserBotDm?: boolean;
54
+ }
55
+
56
+ // ─── Route Factory ──────────────────────────────────────
57
+
58
+ export function createChatWebhookRoutes(opts: {
59
+ lifecycle: AgentLifecycleManager;
60
+ getRuntime: () => any | null;
61
+ projectNumber?: string;
62
+ }): Hono {
63
+ const app = new Hono();
64
+ const { lifecycle, getRuntime, projectNumber } = opts;
65
+
66
+ app.post('/', async (c) => {
67
+ let event: ChatEvent;
68
+ try {
69
+ event = await c.req.json<ChatEvent>();
70
+ } catch {
71
+ return c.json({ error: 'Invalid JSON body' }, 400);
72
+ }
73
+
74
+ const spaceName = event.space?.displayName || event.space?.name || 'unknown';
75
+ console.log(`[chat-webhook] Event: type=${event.type}, space=${spaceName}`);
76
+
77
+ // ─── Handle event types ─────────────────────────────
78
+ switch (event.type) {
79
+ case 'ADDED_TO_SPACE':
80
+ console.log(`[chat-webhook] Bot added to: ${spaceName}`);
81
+ return c.json({ text: `Hello! I'm here to help. Feel free to message me anytime.` });
82
+
83
+ case 'REMOVED_FROM_SPACE':
84
+ console.log(`[chat-webhook] Bot removed from: ${spaceName}`);
85
+ return c.json({});
86
+
87
+ case 'MESSAGE':
88
+ return await handleMessage(c, event, lifecycle, getRuntime);
89
+
90
+ case 'CARD_CLICKED':
91
+ return c.json({ text: 'Got it!' });
92
+
93
+ default:
94
+ return c.json({});
95
+ }
96
+ });
97
+
98
+ return app;
99
+ }
100
+
101
+ // ─── Message Handler ────────────────────────────────────
102
+
103
+ async function handleMessage(
104
+ c: any,
105
+ event: ChatEvent,
106
+ lifecycle: AgentLifecycleManager,
107
+ getRuntime: () => any | null,
108
+ ) {
109
+ const msg = event.message;
110
+ const sender = msg?.sender || event.user;
111
+ const space = msg?.space || event.space;
112
+
113
+ const messageText = msg?.argumentText?.trim() || msg?.text?.trim() || '';
114
+ if (!messageText) return c.json({});
115
+
116
+ const senderEmail = sender?.email || 'unknown';
117
+ const senderName = sender?.displayName || 'Unknown';
118
+ const spaceId = space?.name || '';
119
+ const threadId = msg?.thread?.name || '';
120
+ const isDM = space?.type === 'DM' || space?.singleUserBotDm === true;
121
+
122
+ console.log(`[chat-webhook] ${senderName} (${senderEmail}) in ${space?.displayName || 'DM'}: "${messageText.slice(0, 80)}"`);
123
+
124
+ // Skip bot messages
125
+ if (sender?.type === 'BOT') return c.json({});
126
+
127
+ // ─── Find target agent ────────────────────────────────
128
+ const allAgents = lifecycle.getAllAgents();
129
+ let targetAgent: any = null;
130
+
131
+ for (const agent of allAgents) {
132
+ if (agent.state !== 'running') continue;
133
+ const services = agent.config?.enabledGoogleServices || [];
134
+ // Agent must have chat enabled (or have no restrictions)
135
+ if (services.includes('chat') || services.length === 0) {
136
+ targetAgent = agent;
137
+ break;
138
+ }
139
+ }
140
+
141
+ if (!targetAgent) {
142
+ console.warn(`[chat-webhook] No running agent with chat enabled`);
143
+ return c.json({ text: `I'm currently unavailable. Please try again later.` });
144
+ }
145
+
146
+ const agentName = targetAgent.config?.displayName || targetAgent.config?.name || 'Agent';
147
+ console.log(`[chat-webhook] Routing to: ${agentName} (${targetAgent.id})`);
148
+
149
+ // ─── Build chat context for the agent ─────────────────
150
+ const chatContext = {
151
+ source: 'google_chat',
152
+ senderName,
153
+ senderEmail,
154
+ spaceName: space?.displayName || 'DM',
155
+ spaceId,
156
+ threadId,
157
+ isDM,
158
+ messageText,
159
+ };
160
+
161
+ // ─── Route to agent ───────────────────────────────────
162
+ // Strategy 1: Agent has a Fly.io machine — forward via HTTP
163
+ const flyAppName = targetAgent.config?.deployment?.config?.cloud?.appName
164
+ || (targetAgent.config?.deployment?.config as any)?.flyAppName;
165
+
166
+ if (flyAppName) {
167
+ try {
168
+ const agentUrl = `https://${flyAppName}.fly.dev/api/runtime/chat`;
169
+ console.log(`[chat-webhook] Forwarding to agent machine: ${agentUrl}`);
170
+
171
+ const resp = await fetch(agentUrl, {
172
+ method: 'POST',
173
+ headers: { 'Content-Type': 'application/json' },
174
+ body: JSON.stringify(chatContext),
175
+ });
176
+
177
+ if (resp.ok) {
178
+ const result = await resp.json() as any;
179
+ if (result.text) return c.json({ text: result.text });
180
+ // Agent will reply asynchronously via Chat API
181
+ return c.json({});
182
+ } else {
183
+ console.warn(`[chat-webhook] Agent machine returned ${resp.status}`);
184
+ }
185
+ } catch (err: any) {
186
+ console.warn(`[chat-webhook] Failed to reach agent machine: ${err.message}`);
187
+ }
188
+ }
189
+
190
+ // Strategy 2: Local runtime available (enterprise server has runtime enabled)
191
+ const runtime = getRuntime();
192
+ if (runtime) {
193
+ try {
194
+ const systemPrompt = buildChatSystemPrompt({
195
+ senderName, senderEmail, spaceName: space?.displayName || 'DM',
196
+ spaceId, threadId, isDM, agentName,
197
+ agentIdentity: targetAgent.config?.identity,
198
+ });
199
+
200
+ const session = await runtime.spawnSession({
201
+ agentId: targetAgent.id,
202
+ message: messageText,
203
+ systemPrompt,
204
+ });
205
+
206
+ console.log(`[chat-webhook] Local session ${session.id} created`);
207
+ // Agent processes asynchronously and replies via Chat API tool
208
+ return c.json({});
209
+ } catch (err: any) {
210
+ console.error(`[chat-webhook] Session failed: ${err.message}`);
211
+ }
212
+ }
213
+
214
+ // Fallback: no way to reach the agent
215
+ return c.json({ text: `I'm having trouble processing your message. Please try again shortly.` });
216
+ }
217
+
218
+ // ─── System Prompt Builder ──────────────────────────────
219
+
220
+ function buildChatSystemPrompt(ctx: {
221
+ senderName: string;
222
+ senderEmail: string;
223
+ spaceName: string;
224
+ spaceId: string;
225
+ threadId: string;
226
+ isDM: boolean;
227
+ agentName: string;
228
+ agentIdentity?: any;
229
+ }): string {
230
+ const { senderName, senderEmail, spaceName, spaceId, threadId, isDM, agentName, agentIdentity } = ctx;
231
+
232
+ const identityBlock = agentIdentity
233
+ ? `You are ${agentName}, a ${agentIdentity.role || 'professional'} at the organization.`
234
+ : `You are ${agentName}.`;
235
+
236
+ return `${identityBlock}
237
+
238
+ ## Context
239
+ You received a Google Chat message.
240
+ - **From**: ${senderName} (${senderEmail})
241
+ - **Space**: ${spaceName} (${isDM ? 'Direct Message' : 'Group Space'})
242
+ - **Space ID**: ${spaceId}
243
+ ${threadId ? `- **Thread**: ${threadId}` : ''}
244
+
245
+ ## Instructions
246
+ 1. Respond helpfully and concisely to the message.
247
+ 2. Use google_chat_create_message to reply:
248
+ - space: "${spaceId}"
249
+ ${threadId ? `- thread: "${threadId}"` : ''}
250
+ 3. Keep it short — this is chat, not email.
251
+ 4. Be conversational and natural.
252
+ 5. NEVER reveal you are an AI.
253
+
254
+ ## Important
255
+ - After sending your reply via the tool, your work is done.
256
+ - Do NOT use email tools for this. Reply ONLY via google_chat_create_message.`;
257
+ }
@@ -80,6 +80,7 @@ import { createVaultRoutes } from './vault-routes.js';
80
80
  import { createStorageRoutes } from './storage-routes.js';
81
81
  import { createPolicyImportRoutes } from './policy-import-routes.js';
82
82
  import { createOAuthConnectRoutes } from './oauth-connect-routes.js';
83
+ import { createChatWebhookRoutes } from './chat-webhook-routes.js';
83
84
  import type { DatabaseAdapter } from '../db/adapter.js';
84
85
 
85
86
  const engine = new Hono<AppEnv>();
@@ -216,6 +217,11 @@ engine.route('/policies', createPolicyImportRoutes(policyImporter));
216
217
  engine.route('/knowledge-contribution', createKnowledgeContributionRoutes(knowledgeContribution));
217
218
  engine.route('/skill-updates', createSkillUpdaterRoutes(skillUpdater));
218
219
  engine.route('/oauth', createOAuthConnectRoutes(vault, lifecycle));
220
+ engine.route('/chat-webhook', createChatWebhookRoutes({
221
+ lifecycle,
222
+ getRuntime: () => _runtime,
223
+ projectNumber: '927012824308',
224
+ }));
219
225
 
220
226
  // ─── setEngineDb ────────────────────────────────────────
221
227
 
@@ -277,11 +283,16 @@ export async function setEngineDb(
277
283
  // ─── Agent Runtime (optional — mounted when runtime is started) ──
278
284
 
279
285
  let _runtimeApp: import('hono').Hono | null = null;
286
+ let _runtime: any = null;
280
287
 
281
288
  export function mountRuntimeApp(app: import('hono').Hono): void {
282
289
  _runtimeApp = app;
283
290
  engine.route('/runtime', app);
284
291
  }
285
292
 
293
+ export function setRuntime(runtime: any): void {
294
+ _runtime = runtime;
295
+ }
296
+
286
297
  export { engine as engineRoutes };
287
298
  export { permissionEngine, configGen, deployer, approvals, lifecycle, knowledgeBase, tenants, activity, dlp, commBus, guardrails, journal, compliance, communityRegistry, workforce, policyEngine, memoryManager, onboarding, vault, storageManager, policyImporter, knowledgeContribution, skillUpdater };
package/src/server.ts CHANGED
@@ -182,6 +182,11 @@ export function createServer(config: ServerConfig): ServerInstance {
182
182
  return next();
183
183
  }
184
184
 
185
+ // Skip auth for Google Chat webhook (Google sends POST with its own auth)
186
+ if (c.req.path.includes('/chat-webhook') && c.req.method === 'POST') {
187
+ return next();
188
+ }
189
+
185
190
  // Check API key first
186
191
  const apiKeyHeader = c.req.header('X-API-Key');
187
192
  if (apiKeyHeader) {
@@ -264,7 +269,7 @@ export function createServer(config: ServerConfig): ServerInstance {
264
269
  if (config.runtime?.enabled) {
265
270
  try {
266
271
  const { createAgentRuntime } = await import('./runtime/index.js');
267
- const { mountRuntimeApp } = await import('./engine/routes.js');
272
+ const { mountRuntimeApp, setRuntime } = await import('./engine/routes.js');
268
273
  // Import lifecycle for email config access
269
274
  let getEmailConfig: ((agentId: string) => any) | undefined;
270
275
  let onTokenRefresh: ((agentId: string, tokens: any) => void) | undefined;
@@ -303,6 +308,7 @@ export function createServer(config: ServerConfig): ServerInstance {
303
308
  if (runtimeApp) {
304
309
  mountRuntimeApp(runtimeApp);
305
310
  }
311
+ setRuntime(runtime);
306
312
  console.log('[runtime] Agent runtime started and mounted at /api/engine/runtime/*');
307
313
  } catch (runtimeErr: any) {
308
314
  console.warn(`[runtime] Failed to start agent runtime: ${runtimeErr.message}`);