@agenticmail/enterprise 0.5.168 → 0.5.170

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.
@@ -14,6 +14,60 @@ import type { EngineDatabase } from './db-adapter.js';
14
14
 
15
15
  // ─── Types ──────────────────────────────────────────────
16
16
 
17
+ /**
18
+ * Autonomy settings — all configurable via dashboard.
19
+ * Stored in managed_agents.config.autonomy JSON field.
20
+ */
21
+ export interface AutonomySettings {
22
+ /** Master switch — disables all autonomy features */
23
+ enabled: boolean;
24
+
25
+ /** Auto clock-in/out based on work schedule */
26
+ clockEnabled: boolean;
27
+
28
+ /**
29
+ * Daily/Weekly catchup: times are read from config.dailyCatchUp (Manager & Catch-Up tab).
30
+ * These booleans just enable/disable the behavior.
31
+ */
32
+ dailyCatchupEnabled: boolean;
33
+ weeklyCatchupEnabled: boolean;
34
+ weeklyCatchupDay: number; // 0=Sun..6=Sat, default 1 (Monday)
35
+
36
+ /** Goal progress check */
37
+ goalCheckEnabled: boolean;
38
+ goalCheckHours: number[]; // hours of day to check, default [14, 17]
39
+
40
+ /** Knowledge contribution */
41
+ knowledgeContribEnabled: boolean;
42
+ knowledgeContribDay: number; // 0=Sun..6=Sat, default 5 (Friday)
43
+ knowledgeContribHour: number; // default 15
44
+
45
+ /** Smart escalation */
46
+ escalationEnabled: boolean;
47
+
48
+ /** Guardrail enforcement at runtime */
49
+ guardrailEnforcementEnabled: boolean;
50
+
51
+ /** Drive access request on 403 */
52
+ driveAccessRequestEnabled: boolean;
53
+ }
54
+
55
+ export const DEFAULT_AUTONOMY_SETTINGS: AutonomySettings = {
56
+ enabled: true,
57
+ clockEnabled: true,
58
+ dailyCatchupEnabled: true,
59
+ weeklyCatchupEnabled: true,
60
+ weeklyCatchupDay: 1,
61
+ goalCheckEnabled: true,
62
+ goalCheckHours: [14, 17],
63
+ knowledgeContribEnabled: true,
64
+ knowledgeContribDay: 5,
65
+ knowledgeContribHour: 15,
66
+ escalationEnabled: true,
67
+ guardrailEnforcementEnabled: true,
68
+ driveAccessRequestEnabled: true,
69
+ };
70
+
17
71
  export interface AutonomyConfig {
18
72
  agentId: string;
19
73
  orgId: string;
@@ -27,6 +81,7 @@ export interface AutonomyConfig {
27
81
  engineDb: EngineDatabase;
28
82
  memoryManager?: any;
29
83
  lifecycle?: any;
84
+ settings?: Partial<AutonomySettings>;
30
85
  }
31
86
 
32
87
  export interface ClockState {
@@ -49,6 +104,7 @@ interface CatchupData {
49
104
 
50
105
  export class AgentAutonomyManager {
51
106
  private config: AutonomyConfig;
107
+ private settings: AutonomySettings;
52
108
  private clockState: ClockState = { clockedIn: false };
53
109
  private schedulerInterval?: NodeJS.Timeout;
54
110
  private catchupInterval?: NodeJS.Timeout;
@@ -57,29 +113,68 @@ export class AgentAutonomyManager {
57
113
 
58
114
  constructor(config: AutonomyConfig) {
59
115
  this.config = config;
116
+ this.settings = { ...DEFAULT_AUTONOMY_SETTINGS, ...(config.settings || {}) };
117
+ }
118
+
119
+ /** Reload settings from DB (called when config changes via dashboard) */
120
+ async reloadSettings(): Promise<void> {
121
+ try {
122
+ const rows = await this.config.engineDb.query<any>(
123
+ `SELECT config FROM managed_agents WHERE id = $1`, [this.config.agentId]
124
+ );
125
+ if (rows?.[0]?.config) {
126
+ const cfg = typeof rows[0].config === 'string' ? JSON.parse(rows[0].config) : rows[0].config;
127
+ if (cfg.autonomy) {
128
+ this.settings = { ...DEFAULT_AUTONOMY_SETTINGS, ...cfg.autonomy };
129
+ console.log('[autonomy] Settings reloaded from DB');
130
+ }
131
+ }
132
+ } catch (err: any) {
133
+ console.warn(`[autonomy] Failed to reload settings: ${err.message}`);
134
+ }
60
135
  }
61
136
 
137
+ getSettings(): AutonomySettings { return { ...this.settings }; }
138
+
62
139
  async start(): Promise<void> {
140
+ if (!this.settings.enabled) {
141
+ console.log('[autonomy] Disabled via settings, skipping');
142
+ return;
143
+ }
144
+
63
145
  console.log('[autonomy] Starting agent autonomy system...');
64
146
 
147
+ // Load latest settings from DB
148
+ await this.reloadSettings();
149
+
65
150
  // Check clock state on boot
66
- await this.checkClockState();
151
+ if (this.settings.clockEnabled) await this.checkClockState();
67
152
 
68
153
  // Schedule checker runs every minute
69
- this.schedulerInterval = setInterval(() => this.checkClockState(), 60_000);
154
+ this.schedulerInterval = setInterval(() => {
155
+ if (this.settings.clockEnabled) this.checkClockState();
156
+ }, 60_000);
70
157
 
71
- // Catchup email checker runs every 30 minutes (checks if it's time for daily/weekly)
72
- this.catchupInterval = setInterval(() => this.checkCatchupSchedule(), 30 * 60_000);
73
- // Check immediately on start (after 30s delay for systems to settle)
158
+ // Catchup email checker runs every 15 minutes
159
+ this.catchupInterval = setInterval(() => this.checkCatchupSchedule(), 15 * 60_000);
74
160
  setTimeout(() => this.checkCatchupSchedule(), 30_000);
75
161
 
76
162
  // Knowledge contribution checker runs every hour
77
163
  this.knowledgeInterval = setInterval(() => this.checkKnowledgeContribution(), 60 * 60_000);
78
164
 
79
- // Goal progress checker runs every 2 hours
80
- this.goalCheckInterval = setInterval(() => this.checkGoalProgress(), 2 * 60 * 60_000);
165
+ // Goal progress checker runs every 30 minutes (more granular than 2h)
166
+ this.goalCheckInterval = setInterval(() => this.checkGoalProgress(), 30 * 60_000);
167
+
168
+ // Reload settings from DB every 10 minutes (picks up dashboard changes)
169
+ setInterval(() => this.reloadSettings(), 10 * 60_000);
81
170
 
82
- console.log('[autonomy] System started — clock, catchup, knowledge, goals active');
171
+ const features = [];
172
+ if (this.settings.clockEnabled) features.push('clock');
173
+ if (this.settings.dailyCatchupEnabled) features.push('daily-catchup(time from Manager tab)');
174
+ if (this.settings.weeklyCatchupEnabled) features.push('weekly-catchup@' + ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][this.settings.weeklyCatchupDay]);
175
+ if (this.settings.goalCheckEnabled) features.push('goals@' + this.settings.goalCheckHours.join(','));
176
+ if (this.settings.knowledgeContribEnabled) features.push('knowledge@' + ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][this.settings.knowledgeContribDay]);
177
+ console.log('[autonomy] Active features: ' + features.join(', '));
83
178
  }
84
179
 
85
180
  stop(): void {
@@ -182,20 +277,46 @@ export class AgentAutonomyManager {
182
277
  private async checkCatchupSchedule(): Promise<void> {
183
278
  if (!this.config.managerEmail || !this.config.runtime) return;
184
279
 
280
+ // Read catchup time from config.dailyCatchUp (set in Manager & Catch-Up tab)
281
+ let catchUpHour = 9;
282
+ let catchUpMinute = 0;
283
+ let catchUpTz = this.config.timezone || 'UTC';
284
+ try {
285
+ const rows = await this.config.engineDb.query<any>(
286
+ `SELECT config FROM managed_agents WHERE id = $1`, [this.config.agentId]
287
+ );
288
+ if (rows?.[0]?.config) {
289
+ const cfg = typeof rows[0].config === 'string' ? JSON.parse(rows[0].config) : rows[0].config;
290
+ if (cfg.dailyCatchUp?.time) {
291
+ const parts = cfg.dailyCatchUp.time.split(':');
292
+ catchUpHour = parseInt(parts[0]) || 9;
293
+ catchUpMinute = parseInt(parts[1]) || 0;
294
+ }
295
+ if (cfg.dailyCatchUp?.timezone) catchUpTz = cfg.dailyCatchUp.timezone;
296
+ // If dailyCatchUp is explicitly disabled in Manager tab, respect that
297
+ if (cfg.dailyCatchUp && cfg.dailyCatchUp.enabled === false) return;
298
+ }
299
+ } catch {}
300
+
185
301
  const now = new Date();
186
- const tz = this.config.timezone || 'UTC';
187
- const localTime = new Date(now.toLocaleString('en-US', { timeZone: tz }));
302
+ const localTime = new Date(now.toLocaleString('en-US', { timeZone: catchUpTz }));
188
303
  const hour = localTime.getHours();
189
304
  const minute = localTime.getMinutes();
190
- const dayOfWeek = localTime.getDay(); // 0=Sun, 1=Mon
305
+ const dayOfWeek = localTime.getDay();
191
306
  const dateStr = localTime.toISOString().split('T')[0];
192
307
 
193
- // Daily catchup: 9:00 AM on workdays (within first 30 min window)
194
- const isDailyCatchupTime = hour === 9 && minute < 30;
195
- // Weekly catchup: Monday 9:00 AM (within first 30 min window)
196
- const isWeeklyCatchupTime = dayOfWeek === 1 && hour === 9 && minute < 30;
308
+ // Weekly catchup: configurable day, same time as daily
309
+ const isWeeklyCatchupTime = this.settings.weeklyCatchupEnabled
310
+ && dayOfWeek === this.settings.weeklyCatchupDay
311
+ && hour === catchUpHour
312
+ && minute >= catchUpMinute && minute < catchUpMinute + 15;
313
+
314
+ // Daily catchup: uses time from Manager & Catch-Up tab
315
+ const isDailyCatchupTime = this.settings.dailyCatchupEnabled
316
+ && hour === catchUpHour
317
+ && minute >= catchUpMinute && minute < catchUpMinute + 15;
197
318
 
198
- if (!isDailyCatchupTime) return;
319
+ if (!isDailyCatchupTime && !isWeeklyCatchupTime) return;
199
320
 
200
321
  // Check if we already sent today's catchup
201
322
  const catchupKey = isWeeklyCatchupTime ? `weekly_catchup_${dateStr}` : `daily_catchup_${dateStr}`;
@@ -330,15 +451,16 @@ Available tools: gmail_send (to, subject, body), google_tasks_create (listId, ti
330
451
  // ─── 3. Goal Setting & Auto-Reminders ──────────────
331
452
 
332
453
  private async checkGoalProgress(): Promise<void> {
333
- if (!this.config.runtime || !this.clockState.clockedIn) return;
454
+ if (!this.settings.goalCheckEnabled || !this.config.runtime || !this.clockState.clockedIn) return;
334
455
 
335
456
  const now = new Date();
336
457
  const tz = this.config.timezone || 'UTC';
337
458
  const localTime = new Date(now.toLocaleString('en-US', { timeZone: tz }));
338
459
  const hour = localTime.getHours();
339
460
 
340
- // Check goals at 2 PM and 5 PM (end-of-day review)
341
- if (hour !== 14 && hour !== 17) return;
461
+ // Check goals at configured hours
462
+ const goalHours = this.settings.goalCheckHours || [14, 17];
463
+ if (!goalHours.includes(hour)) return;
342
464
 
343
465
  const dateStr = localTime.toISOString().split('T')[0];
344
466
  const checkKey = `goal_check_${dateStr}_${hour}`;
@@ -348,7 +470,8 @@ Available tools: gmail_send (to, subject, body), google_tasks_create (listId, ti
348
470
  console.log(`[autonomy] Goal progress check at ${hour}:00`);
349
471
 
350
472
  try {
351
- const prompt = hour === 17
473
+ const isEndOfDay = hour === Math.max(...goalHours);
474
+ const prompt = isEndOfDay
352
475
  ? `It's end of day. Review your goals and tasks:
353
476
  1. Call google_tasks_list to see your current tasks
354
477
  2. Review what you completed today
@@ -365,7 +488,7 @@ Available tools: gmail_send (to, subject, body), google_tasks_create (listId, ti
365
488
  const session = await this.config.runtime.spawnSession({
366
489
  agentId: this.config.agentId,
367
490
  message: prompt,
368
- systemPrompt: `You are ${this.config.agentName}, a ${this.config.role}. You are doing a ${hour === 17 ? 'end-of-day' : 'mid-day'} goal review. Be thorough but efficient.`,
491
+ systemPrompt: `You are ${this.config.agentName}, a ${this.config.role}. You are doing a ${isEndOfDay ? 'end-of-day' : 'mid-day'} goal review. Be thorough but efficient.`,
369
492
  });
370
493
  console.log(`[autonomy] ✅ Goal check session ${session.id} created`);
371
494
  await this.setMemoryFlag(checkKey);
@@ -377,17 +500,17 @@ Available tools: gmail_send (to, subject, body), google_tasks_create (listId, ti
377
500
  // ─── 4. Knowledge Contribution (Friday) ────────────
378
501
 
379
502
  private async checkKnowledgeContribution(): Promise<void> {
380
- if (!this.config.runtime || !this.clockState.clockedIn) return;
503
+ if (!this.settings.knowledgeContribEnabled || !this.config.runtime || !this.clockState.clockedIn) return;
381
504
 
382
505
  const now = new Date();
383
506
  const tz = this.config.timezone || 'UTC';
384
507
  const localTime = new Date(now.toLocaleString('en-US', { timeZone: tz }));
385
- const dayOfWeek = localTime.getDay(); // 5=Friday
508
+ const dayOfWeek = localTime.getDay();
386
509
  const hour = localTime.getHours();
387
510
  const dateStr = localTime.toISOString().split('T')[0];
388
511
 
389
- // Friday at 3 PM
390
- if (dayOfWeek !== 5 || hour !== 15) return;
512
+ // Configurable day and hour
513
+ if (dayOfWeek !== this.settings.knowledgeContribDay || hour !== this.settings.knowledgeContribHour) return;
391
514
 
392
515
  const contribKey = `knowledge_contribution_${dateStr}`;
393
516
  const alreadyDone = await this.checkMemoryFlag(contribKey);