@agenticmail/enterprise 0.2.1

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.
Files changed (69) hide show
  1. package/ARCHITECTURE.md +183 -0
  2. package/agenticmail-enterprise.db +0 -0
  3. package/dashboards/README.md +120 -0
  4. package/dashboards/dotnet/Program.cs +261 -0
  5. package/dashboards/express/app.js +146 -0
  6. package/dashboards/go/main.go +513 -0
  7. package/dashboards/html/index.html +535 -0
  8. package/dashboards/java/AgenticMailDashboard.java +376 -0
  9. package/dashboards/php/index.php +414 -0
  10. package/dashboards/python/app.py +273 -0
  11. package/dashboards/ruby/app.rb +195 -0
  12. package/dist/chunk-77IDQJL3.js +7 -0
  13. package/dist/chunk-7RGCCHIT.js +115 -0
  14. package/dist/chunk-DXNKR3TG.js +1355 -0
  15. package/dist/chunk-IQWA44WT.js +970 -0
  16. package/dist/chunk-LCUZGIDH.js +965 -0
  17. package/dist/chunk-N2JVTNNJ.js +2553 -0
  18. package/dist/chunk-O462UJBH.js +363 -0
  19. package/dist/chunk-PNKVD2UK.js +26 -0
  20. package/dist/cli.js +218 -0
  21. package/dist/dashboard/index.html +558 -0
  22. package/dist/db-adapter-DEWEFNIV.js +7 -0
  23. package/dist/dynamodb-CCGL2E77.js +426 -0
  24. package/dist/engine/index.js +1261 -0
  25. package/dist/index.js +522 -0
  26. package/dist/mongodb-ODTXIVPV.js +319 -0
  27. package/dist/mysql-RM3S2FV5.js +521 -0
  28. package/dist/postgres-LN7A6MGQ.js +518 -0
  29. package/dist/routes-2JEPIIKC.js +441 -0
  30. package/dist/routes-74ZLKJKP.js +399 -0
  31. package/dist/server.js +7 -0
  32. package/dist/sqlite-3K5YOZ4K.js +439 -0
  33. package/dist/turso-LDWODSDI.js +442 -0
  34. package/package.json +49 -0
  35. package/src/admin/routes.ts +331 -0
  36. package/src/auth/routes.ts +130 -0
  37. package/src/cli.ts +260 -0
  38. package/src/dashboard/index.html +558 -0
  39. package/src/db/adapter.ts +230 -0
  40. package/src/db/dynamodb.ts +456 -0
  41. package/src/db/factory.ts +51 -0
  42. package/src/db/mongodb.ts +360 -0
  43. package/src/db/mysql.ts +472 -0
  44. package/src/db/postgres.ts +479 -0
  45. package/src/db/sql-schema.ts +123 -0
  46. package/src/db/sqlite.ts +391 -0
  47. package/src/db/turso.ts +411 -0
  48. package/src/deploy/fly.ts +368 -0
  49. package/src/deploy/managed.ts +213 -0
  50. package/src/engine/activity.ts +474 -0
  51. package/src/engine/agent-config.ts +429 -0
  52. package/src/engine/agenticmail-bridge.ts +296 -0
  53. package/src/engine/approvals.ts +278 -0
  54. package/src/engine/db-adapter.ts +682 -0
  55. package/src/engine/db-schema.ts +335 -0
  56. package/src/engine/deployer.ts +595 -0
  57. package/src/engine/index.ts +134 -0
  58. package/src/engine/knowledge.ts +486 -0
  59. package/src/engine/lifecycle.ts +635 -0
  60. package/src/engine/openclaw-hook.ts +371 -0
  61. package/src/engine/routes.ts +528 -0
  62. package/src/engine/skills.ts +473 -0
  63. package/src/engine/tenant.ts +345 -0
  64. package/src/engine/tool-catalog.ts +189 -0
  65. package/src/index.ts +64 -0
  66. package/src/lib/resilience.ts +326 -0
  67. package/src/middleware/index.ts +286 -0
  68. package/src/server.ts +310 -0
  69. package/tsconfig.json +14 -0
package/dist/index.js ADDED
@@ -0,0 +1,522 @@
1
+ import {
2
+ AGENTICMAIL_TOOLS,
3
+ ALL_TOOLS,
4
+ ActivityTracker,
5
+ AgentConfigGenerator,
6
+ AgentLifecycleManager,
7
+ ApprovalEngine,
8
+ BUILTIN_SKILLS,
9
+ DeploymentEngine,
10
+ KnowledgeBaseEngine,
11
+ OPENCLAW_CORE_TOOLS,
12
+ PLAN_LIMITS,
13
+ PRESET_PROFILES,
14
+ PermissionEngine,
15
+ TOOL_INDEX,
16
+ TenantManager,
17
+ generateOpenClawToolPolicy,
18
+ getToolsBySkill,
19
+ init_tool_catalog
20
+ } from "./chunk-N2JVTNNJ.js";
21
+ import {
22
+ ENGINE_TABLES,
23
+ ENGINE_TABLES_POSTGRES,
24
+ EngineDatabase,
25
+ MIGRATIONS,
26
+ MIGRATIONS_TABLE,
27
+ MIGRATIONS_TABLE_POSTGRES,
28
+ sqliteToMySQL,
29
+ sqliteToPostgres
30
+ } from "./chunk-IQWA44WT.js";
31
+ import {
32
+ CircuitBreaker,
33
+ CircuitOpenError,
34
+ HealthMonitor,
35
+ KeyedRateLimiter,
36
+ RateLimiter,
37
+ ValidationError,
38
+ auditLogger,
39
+ createAdapter,
40
+ createAdminRoutes,
41
+ createAuthRoutes,
42
+ createServer,
43
+ deployToCloud,
44
+ errorHandler,
45
+ generateDockerCompose,
46
+ generateFlyToml,
47
+ getSupportedDatabases,
48
+ rateLimiter,
49
+ requestIdMiddleware,
50
+ requestLogger,
51
+ requireRole,
52
+ securityHeaders,
53
+ validate,
54
+ withRetry
55
+ } from "./chunk-DXNKR3TG.js";
56
+ import {
57
+ DatabaseAdapter
58
+ } from "./chunk-77IDQJL3.js";
59
+ import "./chunk-PNKVD2UK.js";
60
+
61
+ // src/engine/index.ts
62
+ init_tool_catalog();
63
+
64
+ // src/engine/openclaw-hook.ts
65
+ var EnterpriseHook = class {
66
+ config;
67
+ permissionCache = /* @__PURE__ */ new Map();
68
+ pendingToolCalls = /* @__PURE__ */ new Map();
69
+ // callId → toolCallRecordId
70
+ connected = false;
71
+ constructor(config) {
72
+ this.config = {
73
+ engineUrl: config.engineUrl,
74
+ agentId: config.agentId,
75
+ orgId: config.orgId,
76
+ apiToken: config.apiToken || "",
77
+ knowledgeBaseEnabled: config.knowledgeBaseEnabled ?? true,
78
+ kbMaxTokens: config.kbMaxTokens ?? 2e3,
79
+ activityStreamEnabled: config.activityStreamEnabled ?? true,
80
+ failMode: config.failMode ?? "open",
81
+ permissionCacheTtlSec: config.permissionCacheTtlSec ?? 30
82
+ };
83
+ }
84
+ /**
85
+ * BEFORE a tool call — check permissions, record start
86
+ */
87
+ async beforeToolCall(ctx) {
88
+ try {
89
+ const cached = this.permissionCache.get(ctx.toolId);
90
+ if (cached && cached.expires > Date.now()) {
91
+ await this.recordToolCallStart(ctx);
92
+ return cached.result;
93
+ }
94
+ const permResult = await this.apiCall("/api/engine/permissions/check", "POST", {
95
+ agentId: this.config.agentId,
96
+ toolId: ctx.toolId
97
+ });
98
+ const result = {
99
+ allowed: permResult.allowed ?? this.config.failMode === "open",
100
+ reason: permResult.reason || "Unknown",
101
+ requiresApproval: permResult.requiresApproval || false,
102
+ sandbox: permResult.sandbox || false
103
+ };
104
+ this.permissionCache.set(ctx.toolId, {
105
+ result,
106
+ expires: Date.now() + this.config.permissionCacheTtlSec * 1e3
107
+ });
108
+ if (result.requiresApproval && result.allowed) {
109
+ const approval = await this.requestApproval(ctx);
110
+ if (approval) {
111
+ result.approvalId = approval.id;
112
+ if (approval.status === "denied") {
113
+ result.allowed = false;
114
+ result.reason = `Denied by ${approval.decision?.by}: ${approval.decision?.reason || "No reason given"}`;
115
+ } else if (approval.status === "expired") {
116
+ result.allowed = false;
117
+ result.reason = "Approval request expired";
118
+ }
119
+ }
120
+ }
121
+ if (result.allowed) {
122
+ await this.recordToolCallStart(ctx);
123
+ } else {
124
+ await this.recordActivity("tool_blocked", {
125
+ toolId: ctx.toolId,
126
+ toolName: ctx.toolName,
127
+ reason: result.reason
128
+ });
129
+ }
130
+ return result;
131
+ } catch (error) {
132
+ const allowed = this.config.failMode === "open";
133
+ return {
134
+ allowed,
135
+ reason: allowed ? `Engine unreachable (fail-open): ${error.message}` : `Engine unreachable (fail-closed): ${error.message}`,
136
+ requiresApproval: false
137
+ };
138
+ }
139
+ }
140
+ /**
141
+ * AFTER a tool call — record result, update usage
142
+ */
143
+ async afterToolCall(ctx, result) {
144
+ try {
145
+ await this.apiCall("/api/engine/agents/" + this.config.agentId + "/record-tool-call", "POST", {
146
+ toolId: ctx.toolId,
147
+ tokensUsed: (result.inputTokens || 0) + (result.outputTokens || 0),
148
+ costUsd: result.costUsd || 0,
149
+ isExternalAction: this.isExternalAction(ctx.toolId),
150
+ error: !result.success
151
+ });
152
+ await this.recordActivity(result.success ? "tool_call_end" : "tool_call_error", {
153
+ toolId: ctx.toolId,
154
+ toolName: ctx.toolName,
155
+ success: result.success,
156
+ error: result.error,
157
+ durationMs: ctx.timestamp ? Date.now() - ctx.timestamp.getTime() : void 0,
158
+ inputTokens: result.inputTokens,
159
+ outputTokens: result.outputTokens,
160
+ costUsd: result.costUsd
161
+ });
162
+ } catch {
163
+ }
164
+ }
165
+ /**
166
+ * BEFORE an LLM call — inject knowledge base context
167
+ */
168
+ async getKnowledgeContext(userMessage) {
169
+ if (!this.config.knowledgeBaseEnabled) return null;
170
+ try {
171
+ const result = await this.apiCall("/api/engine/knowledge-bases/context", "POST", {
172
+ agentId: this.config.agentId,
173
+ query: userMessage,
174
+ maxTokens: this.config.kbMaxTokens
175
+ });
176
+ return result.context || null;
177
+ } catch {
178
+ return null;
179
+ }
180
+ }
181
+ /**
182
+ * ON session start
183
+ */
184
+ async onSessionStart(sessionId) {
185
+ try {
186
+ await this.recordActivity("session_start", { sessionId });
187
+ } catch {
188
+ }
189
+ }
190
+ /**
191
+ * ON session end
192
+ */
193
+ async onSessionEnd(sessionId) {
194
+ try {
195
+ await this.recordActivity("session_end", { sessionId });
196
+ } catch {
197
+ }
198
+ }
199
+ /**
200
+ * Record a conversation message
201
+ */
202
+ async recordMessage(opts) {
203
+ try {
204
+ await this.apiCall("/api/engine/activity/record-message", "POST", {
205
+ agentId: this.config.agentId,
206
+ ...opts
207
+ });
208
+ } catch {
209
+ }
210
+ }
211
+ /**
212
+ * Get the tool policy for this agent (used on startup to configure OpenClaw)
213
+ */
214
+ async getToolPolicy() {
215
+ try {
216
+ return await this.apiCall(`/api/engine/permissions/${this.config.agentId}/policy`, "GET");
217
+ } catch {
218
+ return null;
219
+ }
220
+ }
221
+ /**
222
+ * Check if engine is reachable
223
+ */
224
+ async healthCheck() {
225
+ try {
226
+ const result = await this.apiCall("/health", "GET");
227
+ this.connected = result.status === "ok";
228
+ return this.connected;
229
+ } catch {
230
+ this.connected = false;
231
+ return false;
232
+ }
233
+ }
234
+ // ─── Private ──────────────────────────────────────────
235
+ async requestApproval(ctx) {
236
+ try {
237
+ const result = await this.apiCall("/api/engine/approvals/request", "POST", {
238
+ agentId: this.config.agentId,
239
+ agentName: this.config.agentId,
240
+ toolId: ctx.toolId,
241
+ toolName: ctx.toolName,
242
+ parameters: ctx.parameters,
243
+ context: `Session ${ctx.sessionId}`
244
+ });
245
+ if (result.request?.id) {
246
+ const start = Date.now();
247
+ while (Date.now() - start < 3e5) {
248
+ await new Promise((r) => setTimeout(r, 3e3));
249
+ const check = await this.apiCall(`/api/engine/approvals/${result.request.id}`, "GET");
250
+ if (check.request?.status !== "pending") return check.request;
251
+ }
252
+ return { status: "expired" };
253
+ }
254
+ return null;
255
+ } catch {
256
+ return null;
257
+ }
258
+ }
259
+ async recordToolCallStart(ctx) {
260
+ if (!this.config.activityStreamEnabled) return;
261
+ try {
262
+ await this.recordActivity("tool_call_start", {
263
+ toolId: ctx.toolId,
264
+ toolName: ctx.toolName,
265
+ sessionId: ctx.sessionId
266
+ });
267
+ } catch {
268
+ }
269
+ }
270
+ async recordActivity(type, data) {
271
+ try {
272
+ await this.apiCall("/api/engine/activity/record", "POST", {
273
+ agentId: this.config.agentId,
274
+ orgId: this.config.orgId,
275
+ type,
276
+ data
277
+ });
278
+ } catch {
279
+ }
280
+ }
281
+ isExternalAction(toolId) {
282
+ const externalTools = [
283
+ "agenticmail_send",
284
+ "agenticmail_reply",
285
+ "agenticmail_forward",
286
+ "agenticmail_sms_send",
287
+ "message",
288
+ "tts"
289
+ ];
290
+ return externalTools.includes(toolId);
291
+ }
292
+ async apiCall(path, method, body) {
293
+ const opts = {
294
+ method,
295
+ headers: {
296
+ "Content-Type": "application/json",
297
+ ...this.config.apiToken ? { "Authorization": `Bearer ${this.config.apiToken}` } : {}
298
+ },
299
+ signal: AbortSignal.timeout(5e3)
300
+ };
301
+ if (body && method !== "GET") opts.body = JSON.stringify(body);
302
+ const resp = await fetch(`${this.config.engineUrl}${path}`, opts);
303
+ return resp.json();
304
+ }
305
+ };
306
+ function createEnterpriseHook(config) {
307
+ return new EnterpriseHook(config);
308
+ }
309
+
310
+ // src/engine/agenticmail-bridge.ts
311
+ init_tool_catalog();
312
+ var AgenticMailBridge = class {
313
+ hook;
314
+ config;
315
+ toolPolicy = null;
316
+ sessionId = "";
317
+ rateLimiter = { count: 0, resetAt: 0 };
318
+ constructor(config) {
319
+ this.config = config;
320
+ this.hook = new EnterpriseHook(config);
321
+ }
322
+ /**
323
+ * Initialize the bridge — load config, verify connection
324
+ */
325
+ async initialize() {
326
+ const connected = await this.hook.healthCheck();
327
+ if (connected && this.config.autoConfigureTools !== false) {
328
+ this.toolPolicy = await this.hook.getToolPolicy();
329
+ }
330
+ if (this.config.verbose) {
331
+ console.log(`[Enterprise] Bridge initialized. Connected: ${connected}`);
332
+ if (this.toolPolicy) {
333
+ console.log(`[Enterprise] Tool policy: ${this.toolPolicy.allowedTools.length} allowed, ${this.toolPolicy.blockedTools.length} blocked`);
334
+ }
335
+ }
336
+ return { connected, toolPolicy: this.toolPolicy };
337
+ }
338
+ /**
339
+ * Get a tool interceptor that can be registered with OpenClaw
340
+ */
341
+ getInterceptor() {
342
+ return {
343
+ beforeTool: async (toolId, params, sessionId) => {
344
+ this.sessionId = sessionId;
345
+ if (this.toolPolicy?.blockedTools.includes(toolId)) {
346
+ if (this.config.verbose) console.log(`[Enterprise] BLOCKED: ${toolId} (policy)`);
347
+ return { allowed: false, reason: `Tool "${toolId}" is blocked by enterprise policy` };
348
+ }
349
+ const now = Date.now();
350
+ if (now > this.rateLimiter.resetAt) {
351
+ this.rateLimiter = { count: 0, resetAt: now + 6e4 };
352
+ }
353
+ this.rateLimiter.count++;
354
+ const limit = this.toolPolicy?.rateLimits?.toolCallsPerMinute || 120;
355
+ if (this.rateLimiter.count > limit) {
356
+ return { allowed: false, reason: `Rate limit exceeded: ${this.rateLimiter.count}/${limit} calls/min` };
357
+ }
358
+ const result = await this.hook.beforeToolCall({
359
+ toolId,
360
+ toolName: toolId,
361
+ parameters: params,
362
+ sessionId,
363
+ timestamp: /* @__PURE__ */ new Date()
364
+ });
365
+ if (this.config.verbose && !result.allowed) {
366
+ console.log(`[Enterprise] BLOCKED: ${toolId} \u2014 ${result.reason}`);
367
+ }
368
+ return {
369
+ allowed: result.allowed,
370
+ reason: result.reason,
371
+ modifiedParams: result.modifiedParameters
372
+ };
373
+ },
374
+ afterTool: async (toolId, params, result, sessionId) => {
375
+ await this.hook.afterToolCall(
376
+ { toolId, toolName: toolId, parameters: params, sessionId, timestamp: /* @__PURE__ */ new Date() },
377
+ {
378
+ success: !result?.error,
379
+ output: typeof result === "string" ? result : JSON.stringify(result)?.slice(0, 500),
380
+ error: result?.error
381
+ }
382
+ );
383
+ }
384
+ };
385
+ }
386
+ /**
387
+ * Get knowledge base context to inject before LLM call
388
+ */
389
+ async getKBContext(userMessage) {
390
+ return this.hook.getKnowledgeContext(userMessage);
391
+ }
392
+ /**
393
+ * Generate coordination context for the agent's system prompt
394
+ */
395
+ getCoordinationContext() {
396
+ if (!this.config.injectCoordinationContext) return "";
397
+ const blocked = this.toolPolicy?.blockedTools || [];
398
+ const needsApproval = this.toolPolicy?.approvalRequired || [];
399
+ let ctx = "\n<enterprise-context>\n";
400
+ ctx += "\u{1F3E2} This agent is managed by AgenticMail Enterprise.\n";
401
+ if (blocked.length > 0) {
402
+ ctx += `\u26D4 Blocked tools (do not attempt): ${blocked.join(", ")}
403
+ `;
404
+ }
405
+ if (needsApproval.length > 0) {
406
+ ctx += `\u26A0\uFE0F Tools requiring human approval: ${needsApproval.join(", ")}
407
+ `;
408
+ ctx += "When using these tools, the call will pause until a human approves or denies.\n";
409
+ }
410
+ ctx += "</enterprise-context>\n";
411
+ return ctx;
412
+ }
413
+ /**
414
+ * Notify engine of session lifecycle
415
+ */
416
+ async onSessionStart(sessionId) {
417
+ this.sessionId = sessionId;
418
+ await this.hook.onSessionStart(sessionId);
419
+ }
420
+ async onSessionEnd(sessionId) {
421
+ await this.hook.onSessionEnd(sessionId);
422
+ }
423
+ /**
424
+ * Record a message in the conversation log
425
+ */
426
+ async recordMessage(role, content, opts) {
427
+ await this.hook.recordMessage({
428
+ sessionId: this.sessionId,
429
+ role,
430
+ content,
431
+ channel: opts?.channel,
432
+ tokenCount: opts?.tokenCount || Math.ceil(content.length / 4)
433
+ });
434
+ }
435
+ /**
436
+ * Get the loaded tool policy
437
+ */
438
+ getToolPolicy() {
439
+ return this.toolPolicy;
440
+ }
441
+ /**
442
+ * Generate OpenClaw-compatible config for tools.allow / tools.deny
443
+ * This is what gets written to the gateway config
444
+ */
445
+ getOpenClawToolConfig() {
446
+ if (!this.toolPolicy) return {};
447
+ return generateOpenClawToolPolicy(
448
+ this.toolPolicy.allowedTools,
449
+ this.toolPolicy.blockedTools
450
+ );
451
+ }
452
+ };
453
+ async function createAgenticMailBridge(config) {
454
+ const bridge = new AgenticMailBridge({
455
+ autoConfigureTools: true,
456
+ injectCoordinationContext: true,
457
+ verbose: false,
458
+ failMode: "open",
459
+ permissionCacheTtlSec: 30,
460
+ kbMaxTokens: 2e3,
461
+ knowledgeBaseEnabled: true,
462
+ activityStreamEnabled: true,
463
+ ...config
464
+ });
465
+ await bridge.initialize();
466
+ return bridge;
467
+ }
468
+ export {
469
+ AGENTICMAIL_TOOLS,
470
+ ALL_TOOLS,
471
+ ActivityTracker,
472
+ AgentConfigGenerator,
473
+ AgentLifecycleManager,
474
+ AgenticMailBridge,
475
+ ApprovalEngine,
476
+ BUILTIN_SKILLS,
477
+ CircuitBreaker,
478
+ CircuitOpenError,
479
+ DatabaseAdapter,
480
+ DeploymentEngine,
481
+ ENGINE_TABLES,
482
+ ENGINE_TABLES_POSTGRES,
483
+ EngineDatabase,
484
+ EnterpriseHook,
485
+ HealthMonitor,
486
+ KeyedRateLimiter,
487
+ KnowledgeBaseEngine,
488
+ MIGRATIONS,
489
+ MIGRATIONS_TABLE,
490
+ MIGRATIONS_TABLE_POSTGRES,
491
+ OPENCLAW_CORE_TOOLS,
492
+ PLAN_LIMITS,
493
+ PRESET_PROFILES,
494
+ PermissionEngine,
495
+ RateLimiter,
496
+ TOOL_INDEX,
497
+ TenantManager,
498
+ ValidationError,
499
+ auditLogger,
500
+ createAdapter,
501
+ createAdminRoutes,
502
+ createAgenticMailBridge,
503
+ createAuthRoutes,
504
+ createEnterpriseHook,
505
+ createServer,
506
+ deployToCloud,
507
+ errorHandler,
508
+ generateDockerCompose,
509
+ generateFlyToml,
510
+ generateOpenClawToolPolicy,
511
+ getSupportedDatabases,
512
+ getToolsBySkill,
513
+ rateLimiter,
514
+ requestIdMiddleware,
515
+ requestLogger,
516
+ requireRole,
517
+ securityHeaders,
518
+ sqliteToMySQL,
519
+ sqliteToPostgres,
520
+ validate,
521
+ withRetry
522
+ };