@agenticmail/enterprise 0.5.310 → 0.5.312

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,45 @@
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-FNUFLQJ3.js";
18
+ import {
19
+ PROVIDER_REGISTRY,
20
+ listAllProviders,
21
+ resolveApiKeyForProvider,
22
+ resolveProvider
23
+ } from "./chunk-UF3ZJMJO.js";
24
+ import "./chunk-KFQGP6VL.js";
25
+ export {
26
+ AgentRuntime,
27
+ EmailChannel,
28
+ FollowUpScheduler,
29
+ PROVIDER_REGISTRY,
30
+ SessionManager,
31
+ SubAgentManager,
32
+ ToolRegistry,
33
+ callLLM,
34
+ createAgentRuntime,
35
+ createNoopHooks,
36
+ createRuntimeHooks,
37
+ estimateMessageTokens,
38
+ estimateTokens,
39
+ executeTool,
40
+ listAllProviders,
41
+ resolveApiKeyForProvider,
42
+ resolveProvider,
43
+ runAgentLoop,
44
+ toolsToDefinitions
45
+ };
@@ -0,0 +1,45 @@
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-HP42WFMQ.js";
18
+ import {
19
+ PROVIDER_REGISTRY,
20
+ listAllProviders,
21
+ resolveApiKeyForProvider,
22
+ resolveProvider
23
+ } from "./chunk-UF3ZJMJO.js";
24
+ import "./chunk-KFQGP6VL.js";
25
+ export {
26
+ AgentRuntime,
27
+ EmailChannel,
28
+ FollowUpScheduler,
29
+ PROVIDER_REGISTRY,
30
+ SessionManager,
31
+ SubAgentManager,
32
+ ToolRegistry,
33
+ callLLM,
34
+ createAgentRuntime,
35
+ createNoopHooks,
36
+ createRuntimeHooks,
37
+ estimateMessageTokens,
38
+ estimateTokens,
39
+ executeTool,
40
+ listAllProviders,
41
+ resolveApiKeyForProvider,
42
+ resolveProvider,
43
+ runAgentLoop,
44
+ toolsToDefinitions
45
+ };
@@ -0,0 +1,28 @@
1
+ import {
2
+ createServer
3
+ } from "./chunk-VKSOKBVG.js";
4
+ import "./chunk-DJBCRQTD.js";
5
+ import "./chunk-UF3ZJMJO.js";
6
+ import "./chunk-EKMS2KSE.js";
7
+ import "./chunk-VSBC4SWO.js";
8
+ import "./chunk-AF3WSNVX.js";
9
+ import "./chunk-74ZCQKYU.js";
10
+ import "./chunk-MOGIJA6K.js";
11
+ import "./chunk-C6JP5NR6.js";
12
+ import "./chunk-M6ZIC5H3.js";
13
+ import "./chunk-PWWV2U5P.js";
14
+ import "./chunk-MXK7RLTF.js";
15
+ import "./chunk-3FMK32KQ.js";
16
+ import "./chunk-Z7NVD3OQ.js";
17
+ import "./chunk-YDD5TC5Q.js";
18
+ import "./chunk-37ABTUFU.js";
19
+ import "./chunk-NU657BBQ.js";
20
+ import "./chunk-PGAU3W3M.js";
21
+ import "./chunk-JGEVQZDR.js";
22
+ import "./chunk-WUAWWKTN.js";
23
+ import "./chunk-HS5YWSGM.js";
24
+ import "./chunk-22U7TZPN.js";
25
+ import "./chunk-KFQGP6VL.js";
26
+ export {
27
+ createServer
28
+ };
@@ -0,0 +1,28 @@
1
+ import {
2
+ createServer
3
+ } from "./chunk-Y2KIY4BA.js";
4
+ import "./chunk-DJBCRQTD.js";
5
+ import "./chunk-UF3ZJMJO.js";
6
+ import "./chunk-EKMS2KSE.js";
7
+ import "./chunk-VSBC4SWO.js";
8
+ import "./chunk-AF3WSNVX.js";
9
+ import "./chunk-74ZCQKYU.js";
10
+ import "./chunk-MOGIJA6K.js";
11
+ import "./chunk-C6JP5NR6.js";
12
+ import "./chunk-M6ZIC5H3.js";
13
+ import "./chunk-PWWV2U5P.js";
14
+ import "./chunk-MXK7RLTF.js";
15
+ import "./chunk-3FMK32KQ.js";
16
+ import "./chunk-Z7NVD3OQ.js";
17
+ import "./chunk-YDD5TC5Q.js";
18
+ import "./chunk-37ABTUFU.js";
19
+ import "./chunk-NU657BBQ.js";
20
+ import "./chunk-PGAU3W3M.js";
21
+ import "./chunk-JGEVQZDR.js";
22
+ import "./chunk-WUAWWKTN.js";
23
+ import "./chunk-HS5YWSGM.js";
24
+ import "./chunk-22U7TZPN.js";
25
+ import "./chunk-KFQGP6VL.js";
26
+ export {
27
+ createServer
28
+ };
@@ -0,0 +1,28 @@
1
+ import {
2
+ createServer
3
+ } from "./chunk-NM4KTDG2.js";
4
+ import "./chunk-DJBCRQTD.js";
5
+ import "./chunk-UF3ZJMJO.js";
6
+ import "./chunk-HZZPIICV.js";
7
+ import "./chunk-VSBC4SWO.js";
8
+ import "./chunk-AF3WSNVX.js";
9
+ import "./chunk-74ZCQKYU.js";
10
+ import "./chunk-MOGIJA6K.js";
11
+ import "./chunk-C6JP5NR6.js";
12
+ import "./chunk-M6ZIC5H3.js";
13
+ import "./chunk-PWWV2U5P.js";
14
+ import "./chunk-MXK7RLTF.js";
15
+ import "./chunk-3FMK32KQ.js";
16
+ import "./chunk-Z7NVD3OQ.js";
17
+ import "./chunk-YDD5TC5Q.js";
18
+ import "./chunk-37ABTUFU.js";
19
+ import "./chunk-NU657BBQ.js";
20
+ import "./chunk-PGAU3W3M.js";
21
+ import "./chunk-JGEVQZDR.js";
22
+ import "./chunk-WUAWWKTN.js";
23
+ import "./chunk-HS5YWSGM.js";
24
+ import "./chunk-22U7TZPN.js";
25
+ import "./chunk-KFQGP6VL.js";
26
+ export {
27
+ createServer
28
+ };
@@ -0,0 +1,20 @@
1
+ import {
2
+ promptCompanyInfo,
3
+ promptDatabase,
4
+ promptDeployment,
5
+ promptDomain,
6
+ promptRegistration,
7
+ provision,
8
+ runSetupWizard
9
+ } from "./chunk-PFMIOSR5.js";
10
+ import "./chunk-4WLJIZZB.js";
11
+ import "./chunk-KFQGP6VL.js";
12
+ export {
13
+ promptCompanyInfo,
14
+ promptDatabase,
15
+ promptDeployment,
16
+ promptDomain,
17
+ promptRegistration,
18
+ provision,
19
+ runSetupWizard
20
+ };
@@ -0,0 +1,20 @@
1
+ import {
2
+ promptCompanyInfo,
3
+ promptDatabase,
4
+ promptDeployment,
5
+ promptDomain,
6
+ promptRegistration,
7
+ provision,
8
+ runSetupWizard
9
+ } from "./chunk-OPOBUYJT.js";
10
+ import "./chunk-4WLJIZZB.js";
11
+ import "./chunk-KFQGP6VL.js";
12
+ export {
13
+ promptCompanyInfo,
14
+ promptDatabase,
15
+ promptDeployment,
16
+ promptDomain,
17
+ promptRegistration,
18
+ provision,
19
+ runSetupWizard
20
+ };
@@ -0,0 +1,20 @@
1
+ import {
2
+ promptCompanyInfo,
3
+ promptDatabase,
4
+ promptDeployment,
5
+ promptDomain,
6
+ promptRegistration,
7
+ provision,
8
+ runSetupWizard
9
+ } from "./chunk-MBT5ND3D.js";
10
+ import "./chunk-4WLJIZZB.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.310",
3
+ "version": "0.5.312",
4
4
  "description": "AgenticMail Enterprise — cloud-hosted AI agent identity, email, auth & compliance for organizations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -757,13 +757,15 @@ export function createAdminRoutes(db: DatabaseAdapter) {
757
757
  const userRole = c.get('userRole' as any);
758
758
  if (!userId) return c.json({ error: 'Not authenticated' }, 401);
759
759
 
760
+ const user = await db.getUser(userId);
761
+ const clientOrgId = user?.clientOrgId || c.get('clientOrgId' as any) || null;
762
+
760
763
  // Owner and admin always get full access
761
764
  if (userRole === 'owner' || userRole === 'admin') {
762
- return c.json({ permissions: '*', role: userRole });
765
+ return c.json({ permissions: '*', role: userRole, clientOrgId });
763
766
  }
764
767
 
765
- const user = await db.getUser(userId);
766
- return c.json({ permissions: user?.permissions ?? '*', role: userRole, clientOrgId: user?.clientOrgId || null });
768
+ return c.json({ permissions: user?.permissions ?? '*', role: userRole, clientOrgId });
767
769
  });
768
770
 
769
771
  // ─── Platform Capabilities ──────────────────────────
@@ -2195,8 +2197,26 @@ export function createAdminRoutes(db: DatabaseAdapter) {
2195
2197
 
2196
2198
  // ─── Client Organizations ─────────────────────────────
2197
2199
 
2198
- api.get('/organizations', requireRole('admin'), async (c) => {
2200
+ api.get('/organizations', async (c) => {
2199
2201
  try {
2202
+ // Org-bound users can only see their own organization
2203
+ const clientOrgId = c.get('clientOrgId' as any);
2204
+ if (clientOrgId) {
2205
+ const isPostgres = (db as any).pool;
2206
+ if (isPostgres) {
2207
+ const { rows } = await (db as any)._query(
2208
+ `SELECT o.*, COUNT(a.id) as agent_count FROM client_organizations o LEFT JOIN agents a ON a.client_org_id = o.id WHERE o.id = $1 GROUP BY o.id`, [clientOrgId]);
2209
+ return c.json({ organizations: rows });
2210
+ }
2211
+ return c.json({ organizations: [] });
2212
+ }
2213
+
2214
+ // Full access for admins/owners
2215
+ const userRole = c.get('userRole' as any);
2216
+ if (userRole !== 'admin' && userRole !== 'owner') {
2217
+ return c.json({ error: 'Insufficient permissions' }, 403);
2218
+ }
2219
+
2200
2220
  const isPostgres = (db as any).pool;
2201
2221
  if (isPostgres) {
2202
2222
  const { rows } = await (db as any)._query(`
@@ -73,11 +73,14 @@ export function createAuthRoutes(
73
73
  return process.env.NODE_ENV === 'production' || process.env.SECURE_COOKIES === '1';
74
74
  };
75
75
 
76
- async function issueTokens(userId: string, email: string, role: string) {
76
+ async function issueTokens(userId: string, email: string, role: string, clientOrgId?: string | null) {
77
77
  const { SignJWT } = await import('jose');
78
78
  const secret = new TextEncoder().encode(jwtSecret);
79
79
 
80
- const token = await new SignJWT({ sub: userId, email, role })
80
+ const payload: Record<string, any> = { sub: userId, email, role };
81
+ if (clientOrgId) payload.clientOrgId = clientOrgId;
82
+
83
+ const token = await new SignJWT(payload)
81
84
  .setProtectedHeader({ alg: 'HS256' })
82
85
  .setIssuedAt()
83
86
  .setExpirationTime(TOKEN_TTL)
@@ -93,8 +96,8 @@ export function createAuthRoutes(
93
96
  }
94
97
 
95
98
  /** Set session cookies and return token info */
96
- async function setSessionCookies(c: any, userId: string, email: string, role: string, method: string) {
97
- const { token, refreshToken } = await issueTokens(userId, email, role);
99
+ async function setSessionCookies(c: any, userId: string, email: string, role: string, method: string, clientOrgId?: string | null) {
100
+ const { token, refreshToken } = await issueTokens(userId, email, role, clientOrgId);
98
101
  const csrf = generateCsrf();
99
102
  const secure = isSecure();
100
103
 
@@ -301,7 +304,7 @@ export function createAuthRoutes(
301
304
  });
302
305
  }
303
306
 
304
- const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'password');
307
+ const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'password', user.clientOrgId);
305
308
 
306
309
  return c.json({
307
310
  token,
@@ -360,7 +363,7 @@ export function createAuthRoutes(
360
363
 
361
364
  pending2fa.delete(challengeToken);
362
365
 
363
- const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'password+2fa');
366
+ const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'password+2fa', user.clientOrgId);
364
367
 
365
368
  return c.json({
366
369
  token,
@@ -614,7 +617,7 @@ export function createAuthRoutes(
614
617
  const user = await db.getUser(key.createdBy);
615
618
  if (!user) return c.json({ error: 'API key owner not found' }, 401);
616
619
 
617
- const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'api-key');
620
+ const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'api-key', user.clientOrgId);
618
621
 
619
622
  return c.json({
620
623
  token,
@@ -643,7 +646,7 @@ export function createAuthRoutes(
643
646
  const user = await db.getUser(payload.sub as string);
644
647
  if (!user) return c.json({ error: 'User not found' }, 401);
645
648
 
646
- const { token, refreshToken } = await issueTokens(user.id, user.email, user.role);
649
+ const { token, refreshToken } = await issueTokens(user.id, user.email, user.role, user.clientOrgId);
647
650
  const csrf = generateCsrf();
648
651
  const secure = isSecure();
649
652
 
@@ -890,7 +893,7 @@ export function createAuthRoutes(
890
893
  ip: c.req.header('x-forwarded-for') || c.req.header('x-real-ip'),
891
894
  });
892
895
 
893
- const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'bootstrap');
896
+ const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'bootstrap', user.clientOrgId);
894
897
 
895
898
  // Notify server that setup is complete (flips the dashboard latch)
896
899
  opts?.onBootstrap?.();
@@ -1214,7 +1217,7 @@ export function createAuthRoutes(
1214
1217
  }
1215
1218
 
1216
1219
  // Issue session
1217
- await setSessionCookies(c, result.user.id, result.user.email, result.user.role, 'oidc');
1220
+ await setSessionCookies(c, result.user.id, result.user.email, result.user.role, 'oidc', result.user.clientOrgId);
1218
1221
 
1219
1222
  // Redirect to dashboard
1220
1223
  return c.redirect('/dashboard');
@@ -1363,7 +1366,7 @@ export function createAuthRoutes(
1363
1366
  }
1364
1367
 
1365
1368
  // Issue session
1366
- await setSessionCookies(c, result.user.id, result.user.email, result.user.role, 'saml');
1369
+ await setSessionCookies(c, result.user.id, result.user.email, result.user.role, 'saml', result.user.clientOrgId);
1367
1370
 
1368
1371
  // Redirect to dashboard (or RelayState)
1369
1372
  return c.redirect('/dashboard');
@@ -163,6 +163,12 @@ function App() {
163
163
  if (document.cookie.match(/em_session|em_csrf/)) {
164
164
  authCall('/me').then(async d => {
165
165
  setUser(d.user || d);
166
+ // If user is org-bound, lock org context immediately
167
+ var u = d.user || d;
168
+ if (u.clientOrgId) {
169
+ setSelectedOrgId(u.clientOrgId);
170
+ localStorage.setItem('em_client_org_id', u.clientOrgId);
171
+ }
166
172
  // Init transport encryption BEFORE setting authed — ensures all subsequent
167
173
  // API calls from child components are encrypted
168
174
  try {
@@ -202,9 +208,10 @@ function App() {
202
208
  apiCall('/settings').then(d => { const s = d.settings || d || {}; if (s.primaryColor) applyBrandColor(s.primaryColor); if (s.orgId) setOrgId(s.orgId); }).catch(() => {});
203
209
  apiCall('/me/permissions').then(d => {
204
210
  if (d && d.permissions) setPermissions(d.permissions);
205
- // If user is assigned to a client org, auto-set org context
211
+ // If user is assigned to a client org, auto-set org context and lock switcher
206
212
  if (d && d.clientOrgId) {
207
213
  localStorage.setItem('em_client_org_id', d.clientOrgId);
214
+ setSelectedOrgId(d.clientOrgId);
208
215
  } else {
209
216
  localStorage.removeItem('em_client_org_id');
210
217
  }
@@ -705,7 +705,7 @@ export class ComplianceReporter {
705
705
  timeline.push({ timestamp: v.created_at, source: 'vault_access', category: 'access', agentId: v.agent_id, detail: `Vault ${v.action}: ${v.key}`, data: { action: v.action, key: v.key } });
706
706
  }
707
707
 
708
- timeline.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
708
+ timeline.sort((a, b) => String(a.timestamp || '').localeCompare(String(b.timestamp || '')));
709
709
  data.timeline = timeline;
710
710
 
711
711
  // Summary statistics
@@ -720,7 +720,7 @@ export class ComplianceReporter {
720
720
  data.summary.bySource[e.source] = (data.summary.bySource[e.source] || 0) + 1;
721
721
  data.summary.byCategory[e.category] = (data.summary.byCategory[e.category] || 0) + 1;
722
722
  data.summary.byAgent[e.agentId] = (data.summary.byAgent[e.agentId] || 0) + 1;
723
- const day = e.timestamp?.substring(0, 10);
723
+ const day = String(e.timestamp || '').substring(0, 10);
724
724
  if (day) data.summary.byDay[day] = (data.summary.byDay[day] || 0) + 1;
725
725
  }
726
726
 
package/src/server.ts CHANGED
@@ -278,6 +278,7 @@ export function createServer(config: ServerConfig): ServerInstance {
278
278
  c.set('userRole', (payload.role as string) || '');
279
279
  c.set('userEmail', (payload.email as string) || '');
280
280
  c.set('userOrgId', (payload.orgId as string) || '');
281
+ c.set('clientOrgId', (payload.clientOrgId as string) || '');
281
282
  c.set('authType', cookieToken ? 'cookie' : 'jwt');
282
283
  return next();
283
284
  } catch {
@@ -285,6 +286,35 @@ export function createServer(config: ServerConfig): ServerInstance {
285
286
  }
286
287
  });
287
288
 
289
+ // ─── Org-scoping enforcement for client-bound users ───
290
+ // Users with clientOrgId in JWT can ONLY access their bound org's data
291
+ api.use('*', async (c: any, next: any) => {
292
+ const clientOrgId = c.get('clientOrgId');
293
+ if (!clientOrgId) return next(); // Not org-bound, full access
294
+
295
+ // Override orgId in query params to enforce scoping
296
+ const url = new URL(c.req.url);
297
+ const requestedOrg = url.searchParams.get('orgId');
298
+ if (requestedOrg && requestedOrg !== clientOrgId) {
299
+ return c.json({ error: 'Access denied: you can only access your assigned organization' }, 403);
300
+ }
301
+
302
+ // Block org listing — only return their own org
303
+ const path = c.req.path;
304
+ if (path === '/api/organizations' && c.req.method === 'GET') {
305
+ // Let it through but we'll filter in the response
306
+ // Store the clientOrgId for downstream filtering
307
+ }
308
+
309
+ // Block access to other orgs' data via path params (e.g., /api/engine/stats/:orgId)
310
+ const orgIdMatch = path.match(/\/stats\/([^/]+)/);
311
+ if (orgIdMatch && orgIdMatch[1] !== clientOrgId) {
312
+ return c.json({ error: 'Access denied: you can only access your assigned organization' }, 403);
313
+ }
314
+
315
+ return next();
316
+ });
317
+
288
318
  // Audit logging on all API mutations
289
319
  api.use('*', auditLogger(config.db));
290
320