@agenticmail/enterprise 0.5.167 → 0.5.169

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,34 @@
1
+ import "./chunk-KFQGP6VL.js";
2
+
3
+ // src/cli-serve.ts
4
+ async function runServe(_args) {
5
+ const DATABASE_URL = process.env.DATABASE_URL;
6
+ const JWT_SECRET = process.env.JWT_SECRET;
7
+ const PORT = parseInt(process.env.PORT || "8080", 10);
8
+ if (!DATABASE_URL) {
9
+ console.error("ERROR: DATABASE_URL environment variable is required");
10
+ process.exit(1);
11
+ }
12
+ if (!JWT_SECRET) {
13
+ console.error("ERROR: JWT_SECRET environment variable is required");
14
+ process.exit(1);
15
+ }
16
+ const { createAdapter } = await import("./factory-MBP7N2OQ.js");
17
+ const { createServer } = await import("./server-O45YMZQ3.js");
18
+ const db = await createAdapter({
19
+ type: DATABASE_URL.startsWith("postgres") ? "postgres" : "sqlite",
20
+ connectionString: DATABASE_URL
21
+ });
22
+ await db.migrate();
23
+ const server = createServer({
24
+ port: PORT,
25
+ db,
26
+ jwtSecret: JWT_SECRET,
27
+ corsOrigins: ["*"]
28
+ });
29
+ await server.start();
30
+ console.log(`AgenticMail Enterprise server running on :${PORT}`);
31
+ }
32
+ export {
33
+ runServe
34
+ };
package/dist/cli.js CHANGED
@@ -47,14 +47,14 @@ Skill Development:
47
47
  `);
48
48
  break;
49
49
  case "serve":
50
- import("./cli-serve-ZNKBEF4M.js").then((m) => m.runServe(args.slice(1))).catch(fatal);
50
+ import("./cli-serve-D5G3XNSI.js").then((m) => m.runServe(args.slice(1))).catch(fatal);
51
51
  break;
52
52
  case "agent":
53
- import("./cli-agent-MX4RTFC5.js").then((m) => m.runAgent(args.slice(1))).catch(fatal);
53
+ import("./cli-agent-SHOCSBKU.js").then((m) => m.runAgent(args.slice(1))).catch(fatal);
54
54
  break;
55
55
  case "setup":
56
56
  default:
57
- import("./setup-4ZOKKRK3.js").then((m) => m.runSetupWizard()).catch(fatal);
57
+ import("./setup-NLVGTWZ3.js").then((m) => m.runSetupWizard()).catch(fatal);
58
58
  break;
59
59
  }
60
60
  function fatal(err) {
@@ -6410,6 +6410,227 @@ function ToolSecuritySection(props) {
6410
6410
  // AGENT DETAIL PAGE (Main Orchestrator)
6411
6411
  // ════════════════════════════════════════════════════════════
6412
6412
 
6413
+ // ─── Autonomy Settings Section ──────────────────────────
6414
+
6415
+ function AutonomySection(props) {
6416
+ var agentId = props.agentId;
6417
+ var engineAgent = props.engineAgent;
6418
+ var reload = props.reload;
6419
+ var app = useApp();
6420
+ var toast = app.toast;
6421
+
6422
+ var defaults = {
6423
+ enabled: true,
6424
+ clockEnabled: true,
6425
+ dailyCatchupEnabled: true, dailyCatchupHour: 9, dailyCatchupMinute: 0,
6426
+ weeklyCatchupEnabled: true, weeklyCatchupDay: 1, weeklyCatchupHour: 9, weeklyCatchupMinute: 0,
6427
+ goalCheckEnabled: true, goalCheckHours: [14, 17],
6428
+ knowledgeContribEnabled: true, knowledgeContribDay: 5, knowledgeContribHour: 15,
6429
+ escalationEnabled: true, guardrailEnforcementEnabled: true, driveAccessRequestEnabled: true,
6430
+ };
6431
+
6432
+ var existing = (engineAgent?.config?.autonomy) || {};
6433
+ var _form = useState(Object.assign({}, defaults, existing));
6434
+ var form = _form[0]; var setForm = _form[1];
6435
+ var _saving = useState(false);
6436
+ var saving = _saving[0]; var setSaving = _saving[1];
6437
+ var _dirty = useState(false);
6438
+ var dirty = _dirty[0]; var setDirty = _dirty[1];
6439
+
6440
+ var set = function(key, val) {
6441
+ var u = Object.assign({}, form);
6442
+ u[key] = val;
6443
+ setForm(u);
6444
+ setDirty(true);
6445
+ };
6446
+
6447
+ var save = function() {
6448
+ setSaving(true);
6449
+ var ea = engineAgent || {};
6450
+ var isRunning = ea.state === 'running' || ea.state === 'active' || ea.state === 'degraded';
6451
+ var endpoint = isRunning ? '/agents/' + agentId + '/hot-update' : '/agents/' + agentId + '/config';
6452
+ var method = isRunning ? 'POST' : 'PATCH';
6453
+ engineCall(endpoint, {
6454
+ method: method,
6455
+ body: JSON.stringify({ updates: { autonomy: form }, updatedBy: 'dashboard' })
6456
+ }).then(function() {
6457
+ toast('Autonomy settings saved' + (isRunning ? ' (agent will reload within 10 min)' : ''), 'success');
6458
+ setDirty(false);
6459
+ setSaving(false);
6460
+ if (reload) reload();
6461
+ }).catch(function(err) {
6462
+ toast(err.message, 'error');
6463
+ setSaving(false);
6464
+ });
6465
+ };
6466
+
6467
+ var DAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
6468
+
6469
+ var toggleStyle = function(on) {
6470
+ return {
6471
+ width: 40, height: 22, borderRadius: 11,
6472
+ background: on ? 'var(--success)' : 'var(--border)',
6473
+ cursor: 'pointer', position: 'relative', flexShrink: 0,
6474
+ transition: 'background 0.2s', display: 'inline-block',
6475
+ };
6476
+ };
6477
+ var knobStyle = function(on) {
6478
+ return {
6479
+ width: 18, height: 18, borderRadius: '50%', background: '#fff',
6480
+ position: 'absolute', top: 2, left: on ? 20 : 2,
6481
+ transition: 'left 0.2s', boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
6482
+ };
6483
+ };
6484
+
6485
+ var Toggle = function(p) {
6486
+ return h('div', { style: { display: 'flex', alignItems: 'center', gap: 10 } },
6487
+ h('div', { style: toggleStyle(p.value), onClick: function() { set(p.field, !p.value); } },
6488
+ h('div', { style: knobStyle(p.value) })
6489
+ ),
6490
+ h('span', { style: { fontSize: 13, fontWeight: 500 } }, p.label),
6491
+ p.desc && h('span', { style: { fontSize: 11, color: 'var(--text-muted)' } }, p.desc)
6492
+ );
6493
+ };
6494
+
6495
+ var TimeSelect = function(p) {
6496
+ return h('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
6497
+ h('select', { className: 'input', style: { width: 80 }, value: p.hour, onChange: function(e) { set(p.hourField, parseInt(e.target.value)); } },
6498
+ Array.from({length: 24}, function(_, i) {
6499
+ var label = i === 0 ? '12 AM' : i < 12 ? i + ' AM' : i === 12 ? '12 PM' : (i - 12) + ' PM';
6500
+ return h('option', { key: i, value: i }, label);
6501
+ })
6502
+ ),
6503
+ h('span', null, ':'),
6504
+ h('select', { className: 'input', style: { width: 65 }, value: p.minute, onChange: function(e) { set(p.minuteField, parseInt(e.target.value)); } },
6505
+ [0, 15, 30, 45].map(function(m) { return h('option', { key: m, value: m }, String(m).padStart(2, '0')); })
6506
+ )
6507
+ );
6508
+ };
6509
+
6510
+ var DaySelect = function(p) {
6511
+ return h('select', { className: 'input', style: { width: 130 }, value: p.value, onChange: function(e) { set(p.field, parseInt(e.target.value)); } },
6512
+ DAYS.map(function(d, i) { return h('option', { key: i, value: i }, d); })
6513
+ );
6514
+ };
6515
+
6516
+ var cardStyle = { marginBottom: 12 };
6517
+ var rowStyle = { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '10px 16px', borderBottom: '1px solid var(--border)' };
6518
+ var configRow = { display: 'flex', gap: 12, alignItems: 'center', padding: '8px 16px 8px 48px', borderBottom: '1px solid var(--border)', fontSize: 13 };
6519
+
6520
+ return h(Fragment, null,
6521
+ // Header
6522
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } },
6523
+ h('div', null,
6524
+ h('div', { style: { fontSize: 15, fontWeight: 600 } }, 'Agent Autonomy Settings'),
6525
+ h('div', { style: { fontSize: 12, color: 'var(--text-muted)' } }, 'Configure automated behaviors — all times use agent timezone')
6526
+ ),
6527
+ h('div', { style: { display: 'flex', gap: 8 } },
6528
+ dirty && h('span', { style: { fontSize: 11, color: 'var(--warning)', alignSelf: 'center' } }, 'Unsaved changes'),
6529
+ h('button', { className: 'btn btn-primary btn-sm', disabled: !dirty || saving, onClick: save }, saving ? 'Saving...' : 'Save')
6530
+ )
6531
+ ),
6532
+
6533
+ // Master switch
6534
+ h('div', { className: 'card', style: cardStyle },
6535
+ h('div', { style: rowStyle },
6536
+ h(Toggle, { field: 'enabled', value: form.enabled, label: 'Enable Autonomy System', desc: 'Master switch for all automated agent behaviors' })
6537
+ )
6538
+ ),
6539
+
6540
+ // Clock In/Out
6541
+ h('div', { className: 'card', style: Object.assign({}, cardStyle, { opacity: form.enabled ? 1 : 0.5 }) },
6542
+ h('div', { style: rowStyle },
6543
+ h(Toggle, { field: 'clockEnabled', value: form.clockEnabled, label: 'Auto Clock-In/Out', desc: 'Clock in/out based on work schedule' })
6544
+ ),
6545
+ form.clockEnabled && h('div', { style: configRow },
6546
+ h('span', { style: { color: 'var(--text-muted)' } }, 'Uses times from the Workforce tab schedule')
6547
+ )
6548
+ ),
6549
+
6550
+ // Daily Catchup
6551
+ h('div', { className: 'card', style: Object.assign({}, cardStyle, { opacity: form.enabled ? 1 : 0.5 }) },
6552
+ h('div', { style: rowStyle },
6553
+ h(Toggle, { field: 'dailyCatchupEnabled', value: form.dailyCatchupEnabled, label: 'Daily Manager Catchup', desc: 'Email summary to manager each workday' })
6554
+ ),
6555
+ form.dailyCatchupEnabled && h('div', { style: configRow },
6556
+ h('span', null, 'Send at'),
6557
+ h(TimeSelect, { hour: form.dailyCatchupHour, minute: form.dailyCatchupMinute, hourField: 'dailyCatchupHour', minuteField: 'dailyCatchupMinute' })
6558
+ )
6559
+ ),
6560
+
6561
+ // Weekly Catchup
6562
+ h('div', { className: 'card', style: Object.assign({}, cardStyle, { opacity: form.enabled ? 1 : 0.5 }) },
6563
+ h('div', { style: rowStyle },
6564
+ h(Toggle, { field: 'weeklyCatchupEnabled', value: form.weeklyCatchupEnabled, label: 'Weekly Manager Catchup', desc: 'Broader summary + goals, replaces daily on chosen day' })
6565
+ ),
6566
+ form.weeklyCatchupEnabled && h('div', { style: configRow },
6567
+ h('span', null, 'Send on'),
6568
+ h(DaySelect, { value: form.weeklyCatchupDay, field: 'weeklyCatchupDay' }),
6569
+ h('span', null, 'at'),
6570
+ h(TimeSelect, { hour: form.weeklyCatchupHour, minute: form.weeklyCatchupMinute, hourField: 'weeklyCatchupHour', minuteField: 'weeklyCatchupMinute' })
6571
+ )
6572
+ ),
6573
+
6574
+ // Goal Check
6575
+ h('div', { className: 'card', style: Object.assign({}, cardStyle, { opacity: form.enabled ? 1 : 0.5 }) },
6576
+ h('div', { style: rowStyle },
6577
+ h(Toggle, { field: 'goalCheckEnabled', value: form.goalCheckEnabled, label: 'Goal Progress Checks', desc: 'Reviews Google Tasks at set hours (last = end-of-day review)' })
6578
+ ),
6579
+ form.goalCheckEnabled && h('div', { style: configRow },
6580
+ h('span', null, 'Check at hours:'),
6581
+ h('input', { className: 'input', style: { width: 150 },
6582
+ value: (form.goalCheckHours || [14, 17]).join(', '),
6583
+ placeholder: '14, 17',
6584
+ onChange: function(e) {
6585
+ var hrs = e.target.value.split(',').map(function(s) { return parseInt(s.trim()); }).filter(function(n) { return !isNaN(n) && n >= 0 && n <= 23; });
6586
+ set('goalCheckHours', hrs.length > 0 ? hrs : [14, 17]);
6587
+ }
6588
+ }),
6589
+ h('span', { style: { fontSize: 11, color: 'var(--text-muted)' } }, '(24h format, comma-separated)')
6590
+ )
6591
+ ),
6592
+
6593
+ // Knowledge Contribution
6594
+ h('div', { className: 'card', style: Object.assign({}, cardStyle, { opacity: form.enabled ? 1 : 0.5 }) },
6595
+ h('div', { style: rowStyle },
6596
+ h(Toggle, { field: 'knowledgeContribEnabled', value: form.knowledgeContribEnabled, label: 'Weekly Knowledge Contribution', desc: 'Agent reviews learnings and contributes to role-based knowledge base' })
6597
+ ),
6598
+ form.knowledgeContribEnabled && h('div', { style: configRow },
6599
+ h('span', null, 'Contribute on'),
6600
+ h(DaySelect, { value: form.knowledgeContribDay, field: 'knowledgeContribDay' }),
6601
+ h('span', null, 'at'),
6602
+ h('select', { className: 'input', style: { width: 80 }, value: form.knowledgeContribHour, onChange: function(e) { set('knowledgeContribHour', parseInt(e.target.value)); } },
6603
+ Array.from({length: 24}, function(_, i) {
6604
+ var label = i === 0 ? '12 AM' : i < 12 ? i + ' AM' : i === 12 ? '12 PM' : (i - 12) + ' PM';
6605
+ return h('option', { key: i, value: i }, label);
6606
+ })
6607
+ )
6608
+ )
6609
+ ),
6610
+
6611
+ // Smart Escalation
6612
+ h('div', { className: 'card', style: Object.assign({}, cardStyle, { opacity: form.enabled ? 1 : 0.5 }) },
6613
+ h('div', { style: rowStyle },
6614
+ h(Toggle, { field: 'escalationEnabled', value: form.escalationEnabled, label: 'Smart Answer Escalation', desc: 'Search memory → Drive → escalate to manager when unsure' })
6615
+ )
6616
+ ),
6617
+
6618
+ // Guardrail Enforcement
6619
+ h('div', { className: 'card', style: Object.assign({}, cardStyle, { opacity: form.enabled ? 1 : 0.5 }) },
6620
+ h('div', { style: rowStyle },
6621
+ h(Toggle, { field: 'guardrailEnforcementEnabled', value: form.guardrailEnforcementEnabled, label: 'Runtime Guardrail Enforcement', desc: 'Evaluate guardrail rules on inbound emails and tool calls' })
6622
+ )
6623
+ ),
6624
+
6625
+ // Drive Access Requests
6626
+ h('div', { className: 'card', style: Object.assign({}, cardStyle, { opacity: form.enabled ? 1 : 0.5 }) },
6627
+ h('div', { style: rowStyle },
6628
+ h(Toggle, { field: 'driveAccessRequestEnabled', value: form.driveAccessRequestEnabled, label: 'Drive Access Requests', desc: 'When agent cannot access a file, it requests access from manager instead of failing silently' })
6629
+ )
6630
+ )
6631
+ );
6632
+ }
6633
+
6413
6634
  function AgentDetailPage(props) {
6414
6635
  var agentId = props.agentId;
6415
6636
  var onBack = props.onBack;
@@ -6430,8 +6651,8 @@ function AgentDetailPage(props) {
6430
6651
  var _agents = useState([]);
6431
6652
  var agents = _agents[0]; var setAgents = _agents[1];
6432
6653
 
6433
- var TABS = ['overview', 'personal', 'email', 'configuration', 'manager', 'tools', 'skills', 'permissions', 'activity', 'communication', 'workforce', 'memory', 'guardrails', 'budget', 'tool-security', 'deployment'];
6434
- var TAB_LABELS = { 'tool-security': 'Tool Security', 'manager': 'Manager & Catch-Up', 'email': 'Email', 'tools': 'Tools' };
6654
+ var TABS = ['overview', 'personal', 'email', 'configuration', 'manager', 'tools', 'skills', 'permissions', 'activity', 'communication', 'workforce', 'memory', 'guardrails', 'autonomy', 'budget', 'tool-security', 'deployment'];
6655
+ var TAB_LABELS = { 'tool-security': 'Tool Security', 'manager': 'Manager & Catch-Up', 'email': 'Email', 'tools': 'Tools', 'autonomy': 'Autonomy' };
6435
6656
 
6436
6657
  var load = function() {
6437
6658
  setLoading(true);
@@ -6563,6 +6784,7 @@ function AgentDetailPage(props) {
6563
6784
  tab === 'workforce' && h(WorkforceSection, { agentId: agentId }),
6564
6785
  tab === 'memory' && h(MemorySection, { agentId: agentId }),
6565
6786
  tab === 'guardrails' && h(GuardrailsSection, { agentId: agentId, agents: agents }),
6787
+ tab === 'autonomy' && h(AutonomySection, { agentId: agentId, engineAgent: engineAgent, reload: load }),
6566
6788
  tab === 'budget' && h(BudgetSection, { agentId: agentId }),
6567
6789
  tab === 'tool-security' && h(ToolSecuritySection, { agentId: agentId }),
6568
6790
  tab === 'deployment' && h(DeploymentSection, { agentId: agentId, engineAgent: engineAgent, agent: agent, reload: load, onBack: onBack })
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  import {
8
8
  provision,
9
9
  runSetupWizard
10
- } from "./chunk-Y3SVP4G3.js";
10
+ } from "./chunk-RNHBMPPK.js";
11
11
  import {
12
12
  ActionJournal,
13
13
  ActivityTracker,
@@ -54,7 +54,7 @@ import {
54
54
  executeTool,
55
55
  runAgentLoop,
56
56
  toolsToDefinitions
57
- } from "./chunk-4FHY5GLO.js";
57
+ } from "./chunk-OZ7OBEUD.js";
58
58
  import "./chunk-AQH4DFYV.js";
59
59
  import {
60
60
  ValidationError,
@@ -69,7 +69,7 @@ import {
69
69
  requireRole,
70
70
  securityHeaders,
71
71
  validate
72
- } from "./chunk-SJRF5OLB.js";
72
+ } from "./chunk-2ALM5WEM.js";
73
73
  import "./chunk-3SMTCIR4.js";
74
74
  import {
75
75
  CircuitBreaker,
@@ -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-OZ7OBEUD.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-2ALM5WEM.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-RNHBMPPK.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.167",
3
+ "version": "0.5.169",
4
4
  "description": "AgenticMail Enterprise — cloud-hosted AI agent identity, email, auth & compliance for organizations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,6 +11,17 @@ import type { GoogleToolsConfig } from './index.js';
11
11
  const BASE = 'https://www.googleapis.com/drive/v3';
12
12
  const UPLOAD_BASE = 'https://www.googleapis.com/upload/drive/v3';
13
13
 
14
+ class DriveAccessError extends Error {
15
+ status: number;
16
+ fileId?: string;
17
+ constructor(message: string, status: number, fileId?: string) {
18
+ super(message);
19
+ this.name = 'DriveAccessError';
20
+ this.status = status;
21
+ this.fileId = fileId;
22
+ }
23
+ }
24
+
14
25
  async function gapi(token: string, path: string, opts?: { method?: string; body?: any; query?: Record<string, string>; base?: string; rawBody?: BodyInit; headers?: Record<string, string> }): Promise<any> {
15
26
  const method = opts?.method || 'GET';
16
27
  const base = opts?.base || BASE;
@@ -22,7 +33,15 @@ async function gapi(token: string, path: string, opts?: { method?: string; body?
22
33
  method, headers,
23
34
  body: opts?.rawBody || (opts?.body ? JSON.stringify(opts.body) : undefined),
24
35
  });
25
- if (!res.ok) { const err = await res.text(); throw new Error(`Google Drive API ${res.status}: ${err}`); }
36
+ if (!res.ok) {
37
+ const err = await res.text();
38
+ if (res.status === 403 || res.status === 404) {
39
+ // Extract fileId from path if available
40
+ const fileIdMatch = path.match(/\/files\/([^/]+)/);
41
+ throw new DriveAccessError(`Google Drive API ${res.status}: ${err}`, res.status, fileIdMatch?.[1]);
42
+ }
43
+ throw new Error(`Google Drive API ${res.status}: ${err}`);
44
+ }
26
45
  if (res.status === 204) return {};
27
46
  return res.json();
28
47
  }
@@ -84,7 +103,7 @@ export function createGoogleDriveTools(config: GoogleToolsConfig, _options?: Too
84
103
  },
85
104
  {
86
105
  name: 'google_drive_get',
87
- description: 'Get metadata and content of a file. For Google Docs/Sheets/Slides, exports as text. For other files, returns metadata only.',
106
+ description: 'Get metadata and content of a file. For Google Docs/Sheets/Slides, exports as text. For other files, returns metadata only. If you get ACCESS_DENIED, use google_drive_request_access to ask the file owner for access.',
88
107
  category: 'utility' as const,
89
108
  parameters: {
90
109
  type: 'object' as const,
@@ -129,6 +148,57 @@ export function createGoogleDriveTools(config: GoogleToolsConfig, _options?: Too
129
148
  }
130
149
  }
131
150
  return jsonResult(result);
151
+ } catch (e: any) {
152
+ if (e instanceof DriveAccessError && (e.status === 403 || e.status === 404)) {
153
+ return jsonResult({
154
+ error: 'ACCESS_DENIED',
155
+ fileId: params.fileId,
156
+ message: `You do not have access to this file (${e.status}). Use google_drive_request_access to request access from the file owner or your manager.`,
157
+ suggestion: `Call google_drive_request_access with fileId="${params.fileId}" and a reason for needing access.`,
158
+ });
159
+ }
160
+ return errorResult(e.message);
161
+ }
162
+ },
163
+ },
164
+ {
165
+ name: 'google_drive_request_access',
166
+ description: 'Request access to a file you cannot read. Sends an email to your manager (or file owner) asking for access. Use this when google_drive_get returns ACCESS_DENIED.',
167
+ category: 'utility' as const,
168
+ parameters: {
169
+ type: 'object' as const,
170
+ properties: {
171
+ fileId: { type: 'string', description: 'File ID to request access to (required)' },
172
+ reason: { type: 'string', description: 'Why you need access to this file (required)' },
173
+ requesterContext: { type: 'string', description: 'Who asked you to look at this file / what question you are trying to answer' },
174
+ },
175
+ required: ['fileId', 'reason'],
176
+ },
177
+ async execute(_id: string, params: any) {
178
+ try {
179
+ const token = await tp.getAccessToken();
180
+ // Try to get basic file metadata (may fail for 404 but works for some 403)
181
+ let fileName = params.fileId;
182
+ let fileOwner = '';
183
+ let fileLink = `https://drive.google.com/file/d/${params.fileId}/view`;
184
+ try {
185
+ const meta = await gapi(token, `/files/${params.fileId}`, { query: { fields: 'id,name,owners,webViewLink', supportsAllDrives: 'true' } });
186
+ if (meta.name) fileName = meta.name;
187
+ if (meta.owners?.[0]?.emailAddress) fileOwner = meta.owners[0].emailAddress;
188
+ if (meta.webViewLink) fileLink = meta.webViewLink;
189
+ } catch { /* may not have metadata access either */ }
190
+
191
+ // Store the access request in memory for tracking
192
+ return jsonResult({
193
+ accessRequested: true,
194
+ fileId: params.fileId,
195
+ fileName,
196
+ fileOwner: fileOwner || 'unknown',
197
+ fileLink,
198
+ reason: params.reason,
199
+ requesterContext: params.requesterContext || '',
200
+ instruction: `Access request recorded. Email your manager to request access to "${fileName}" (${fileLink}). Include why you need it: ${params.reason}. Wait for the manager to grant access before trying to read the file again. Store this in memory so you can follow up.`,
201
+ });
132
202
  } catch (e: any) { return errorResult(e.message); }
133
203
  },
134
204
  },
package/src/cli-agent.ts CHANGED
@@ -498,6 +498,72 @@ Available tools: gmail_send (to, subject, body) or agenticmail_send (to, subject
498
498
 
499
499
  // 14. Start calendar polling loop (checks for upcoming meetings to auto-join)
500
500
  startCalendarPolling(AGENT_ID, config, runtime, engineDb, memoryManager);
501
+
502
+ // 15. Start agent autonomy system (clock-in/out, catchup emails, goals, knowledge)
503
+ try {
504
+ const { AgentAutonomyManager } = await import('./engine/agent-autonomy.js');
505
+ const orgRows2 = await engineDb.query<any>(`SELECT org_id FROM managed_agents WHERE id = $1`, [AGENT_ID]);
506
+ const autoOrgId = orgRows2?.[0]?.org_id || orgId;
507
+ const managerEmail2 = (config as any).managerEmail || ((config as any).manager?.type === 'external' ? (config as any).manager.email : null);
508
+
509
+ // Parse schedule from work_schedules table
510
+ let schedule: { start: string; end: string; days: number[] } | undefined;
511
+ try {
512
+ const schedRows = await engineDb.query<any>(
513
+ `SELECT config FROM work_schedules WHERE agent_id = $1 ORDER BY created_at DESC LIMIT 1`,
514
+ [AGENT_ID]
515
+ );
516
+ if (schedRows && schedRows.length > 0) {
517
+ const schedConfig = typeof schedRows[0].config === 'string' ? JSON.parse(schedRows[0].config) : schedRows[0].config;
518
+ if (schedConfig?.standardHours) {
519
+ schedule = {
520
+ start: schedConfig.standardHours.start,
521
+ end: schedConfig.standardHours.end,
522
+ days: schedConfig.workDays || [0, 1, 2, 3, 4, 5, 6],
523
+ };
524
+ }
525
+ }
526
+ } catch {}
527
+
528
+ const autonomy = new AgentAutonomyManager({
529
+ agentId: AGENT_ID,
530
+ orgId: autoOrgId,
531
+ agentName: config.displayName || config.name,
532
+ role: config.identity?.role || 'AI Agent',
533
+ managerEmail: managerEmail2,
534
+ timezone: config.timezone || 'America/New_York',
535
+ schedule,
536
+ runtime,
537
+ engineDb,
538
+ memoryManager,
539
+ lifecycle,
540
+ settings: (config as any).autonomy || {},
541
+ });
542
+ await autonomy.start();
543
+ console.log('[autonomy] ✅ Agent autonomy system started');
544
+
545
+ // Store autonomy ref for shutdown
546
+ const origShutdown = process.listeners('SIGTERM');
547
+ process.on('SIGTERM', () => autonomy.stop());
548
+ process.on('SIGINT', () => autonomy.stop());
549
+ } catch (autoErr: any) {
550
+ console.warn(`[autonomy] Failed to start: ${autoErr.message}`);
551
+ }
552
+
553
+ // 16. Start guardrail enforcement (if enabled in autonomy settings)
554
+ const autoSettings = (config as any).autonomy || {};
555
+ if (autoSettings.guardrailEnforcementEnabled !== false) {
556
+ try {
557
+ const { GuardrailEnforcer } = await import('./engine/agent-autonomy.js');
558
+ const enforcer = new GuardrailEnforcer(engineDb);
559
+ (global as any).__guardrailEnforcer = enforcer;
560
+ console.log('[guardrails] ✅ Runtime guardrail enforcer active');
561
+ } catch (gErr: any) {
562
+ console.warn(`[guardrails] Failed to start enforcer: ${gErr.message}`);
563
+ }
564
+ } else {
565
+ console.log('[guardrails] Disabled via autonomy settings');
566
+ }
501
567
  }, 3000);
502
568
  }
503
569
 
@@ -858,10 +924,55 @@ For meetings you organize or are invited to:
858
924
 
859
925
  When you receive an email containing a meeting link (meet.google.com, zoom.us, teams.microsoft.com):
860
926
  - Extract the link and attempt to join immediately if the meeting is now
861
- - If the meeting is in the future, store it in memory and note the time`;
927
+ - If the meeting is in the future, store it in memory and note the time
928
+
929
+ == SMART ANSWER WORKFLOW (MANDATORY) ==
930
+ When you receive a question or request you're not 100% confident about, follow this escalation chain:
931
+
932
+ STEP 1: Search your own memory
933
+ - memory(action: "search", query: "relevant keywords")
934
+ - Check for similar past questions, corrections, and learned patterns
935
+
936
+ STEP 2: Search organization Drive (shared knowledge)
937
+ - google_drive_list with query parameter to search shared docs (e.g. "fullText contains 'search terms'")
938
+ - Read relevant documents with google_drive_get to find the answer
939
+ - Check Google Sheets for data tables, Google Docs for procedures
940
+
941
+ STEP 3: If still unsure — ESCALATE to manager
942
+ ${managerEmail ? `- Send an email to ${managerEmail} with:` : '- Email your manager with:'}
943
+ Subject: "Need Guidance: [Brief topic]"
944
+ Body must include:
945
+ a) The original question/request (who asked, what they need)
946
+ b) What you found in your search (memory + Drive results)
947
+ c) Your proposed answer (what you THINK the answer should be)
948
+ d) What specifically you're unsure about
949
+ e) Ask for approval or correction before responding
950
+
951
+ NEVER guess or fabricate an answer when unsure. It's better to escalate than to be wrong.
952
+ After receiving manager feedback, store the correct answer in memory as a "correction" or "org_knowledge" entry.`;
862
953
 
863
954
 
864
955
 
956
+ // Guardrail check: scan inbound email for security rules
957
+ const enforcer = (global as any).__guardrailEnforcer;
958
+ if (enforcer) {
959
+ try {
960
+ const orgRows3 = await engineDb.query<any>(`SELECT org_id FROM managed_agents WHERE id = $1`, [agentId]);
961
+ const gOrgId = orgRows3?.[0]?.org_id || '';
962
+ const check = await enforcer.evaluate({
963
+ agentId, orgId: gOrgId, type: 'email_send' as const,
964
+ content: emailText, metadata: { from: senderEmail, subject: fullMsg.subject },
965
+ });
966
+ if (!check.allowed) {
967
+ console.warn(`[email-poll] ⚠️ Guardrail blocked email from ${senderEmail}: ${check.reason} (action: ${check.action})`);
968
+ continue; // Skip this email
969
+ }
970
+ } catch (gErr: any) {
971
+ console.warn(`[email-poll] Guardrail check error: ${gErr.message}`);
972
+ // Continue anyway — don't block on guardrail failures
973
+ }
974
+ }
975
+
865
976
  const session = await runtime.spawnSession({
866
977
  agentId,
867
978
  message: emailText,