@agenticmail/enterprise 0.5.312 → 0.5.314
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.
- package/README.md +752 -544
- package/dist/agent-autonomy-PSXQ4MNP.js +766 -0
- package/dist/agent-heartbeat-6H3YAQ32.js +510 -0
- package/dist/agent-heartbeat-7WS3XILF.js +510 -0
- package/dist/agent-heartbeat-BFGKYUUK.js +510 -0
- package/dist/agent-heartbeat-SSV65YTX.js +510 -0
- package/dist/agent-heartbeat-T5IIHVF4.js +510 -0
- package/dist/agent-heartbeat-X3C6FIU2.js +510 -0
- package/dist/agent-tools-BW6CLQQ7.js +13897 -0
- package/dist/agent-tools-KEA7QEWF.js +13897 -0
- package/dist/agent-tools-NU7V3S5N.js +13899 -0
- package/dist/agent-tools-WINDYRQ2.js +13897 -0
- package/dist/chunk-3ELH5CU6.js +4910 -0
- package/dist/chunk-4QYRS3MS.js +1519 -0
- package/dist/chunk-52REEVDW.js +1519 -0
- package/dist/chunk-5RZJ76YI.js +4977 -0
- package/dist/chunk-6L7FQI5Q.js +4909 -0
- package/dist/chunk-763OMGFI.js +1519 -0
- package/dist/chunk-7ILSXGY6.js +1519 -0
- package/dist/chunk-7UCKD25B.js +551 -0
- package/dist/chunk-A6MSR7DL.js +4977 -0
- package/dist/chunk-ASD2YB6O.js +1519 -0
- package/dist/chunk-AZOIHLLX.js +4977 -0
- package/dist/chunk-BDCFOP7O.js +537 -0
- package/dist/chunk-BSVWPG6I.js +106 -0
- package/dist/chunk-C46DNDZB.js +1519 -0
- package/dist/chunk-CFVTK4FQ.js +4977 -0
- package/dist/chunk-CHJAOKCJ.js +4921 -0
- package/dist/chunk-CYEWTXYH.js +4977 -0
- package/dist/chunk-D3KFSWLK.js +48 -0
- package/dist/chunk-DUVGNAIY.js +4977 -0
- package/dist/chunk-DX4XEFVE.js +25229 -0
- package/dist/chunk-EX6FQSEV.js +167 -0
- package/dist/chunk-F5VZ5EUH.js +1519 -0
- package/dist/chunk-FVUDSPOD.js +4977 -0
- package/dist/chunk-G6FTZKJX.js +4977 -0
- package/dist/chunk-GFEAZN6Y.js +1519 -0
- package/dist/chunk-HKV4FQFW.js +1519 -0
- package/dist/chunk-ICCPULDT.js +25217 -0
- package/dist/chunk-IYEM627Q.js +25216 -0
- package/dist/chunk-JHRJ4QJ6.js +1519 -0
- package/dist/chunk-K2DAUYHV.js +4977 -0
- package/dist/chunk-KDQDSZZQ.js +4973 -0
- package/dist/chunk-LDUD6AZY.js +1519 -0
- package/dist/chunk-LES5TJ5L.js +4909 -0
- package/dist/chunk-MJGGW6MC.js +106 -0
- package/dist/chunk-MQKIWAHQ.js +106 -0
- package/dist/chunk-NGA7BBPF.js +48 -0
- package/dist/chunk-OE3TI4IQ.js +1519 -0
- package/dist/chunk-OHSBIYDR.js +4977 -0
- package/dist/chunk-OZEYDEPB.js +1519 -0
- package/dist/chunk-P4PODSQH.js +1519 -0
- package/dist/chunk-P7UOSFIE.js +636 -0
- package/dist/chunk-PFN6DODU.js +4921 -0
- package/dist/chunk-PKDVM4IY.js +4917 -0
- package/dist/chunk-Q5KG3G7U.js +25115 -0
- package/dist/chunk-QMVNW4FJ.js +25229 -0
- package/dist/chunk-QZ5UPRBE.js +4977 -0
- package/dist/chunk-SPP23N42.js +4977 -0
- package/dist/chunk-SRGHNFOY.js +4921 -0
- package/dist/chunk-TPLVQFXM.js +2594 -0
- package/dist/chunk-U3XYF4QP.js +4977 -0
- package/dist/chunk-VRRJH2DY.js +4921 -0
- package/dist/chunk-WY42BS3F.js +1519 -0
- package/dist/chunk-XAA4VHHZ.js +1519 -0
- package/dist/chunk-Z5Y5KTPC.js +4977 -0
- package/dist/chunk-ZA4QRACH.js +4977 -0
- package/dist/chunk-ZHLGSTXF.js +4909 -0
- package/dist/cli-agent-26BUULHZ.js +2169 -0
- package/dist/cli-agent-2FLJWXOC.js +2169 -0
- package/dist/cli-agent-4NNQFLO6.js +2255 -0
- package/dist/cli-agent-5WV3EEPW.js +2252 -0
- package/dist/cli-agent-65JUT6DU.js +2193 -0
- package/dist/cli-agent-6HLL7A5K.js +2255 -0
- package/dist/cli-agent-CZ26QWUZ.js +2210 -0
- package/dist/cli-agent-HPVSWDNQ.js +2255 -0
- package/dist/cli-agent-K4SBVG5X.js +2210 -0
- package/dist/cli-agent-K5D424X2.js +2252 -0
- package/dist/cli-agent-U4OL5FGK.js +2210 -0
- package/dist/cli-agent-WUMPOIKQ.js +2169 -0
- package/dist/cli-agent-WWRGGJ2F.js +2255 -0
- package/dist/cli-agent-ZDBBTVGU.js +2193 -0
- package/dist/cli-agent-ZIZ5JP4O.js +2252 -0
- package/dist/cli-recover-I4KNR2OI.js +487 -0
- package/dist/cli-recover-IQTUKWR2.js +487 -0
- package/dist/cli-recover-OYJHELOR.js +487 -0
- package/dist/cli-recover-PVQC7UXB.js +487 -0
- package/dist/cli-recover-T32NABFA.js +487 -0
- package/dist/cli-serve-FTQJ3RUK.js +143 -0
- package/dist/cli-serve-G4PUCASH.js +143 -0
- package/dist/cli-serve-HBZYUUQ3.js +143 -0
- package/dist/cli-serve-L3NUROMO.js +143 -0
- package/dist/cli-serve-LAA5WIZK.js +143 -0
- package/dist/cli-serve-LV4TUSJD.js +143 -0
- package/dist/cli-serve-MFCTVA2L.js +140 -0
- package/dist/cli-serve-QCRUFI5B.js +143 -0
- package/dist/cli-serve-S7OGQN4P.js +143 -0
- package/dist/cli-serve-SI4BQRXT.js +140 -0
- package/dist/cli-serve-UNB7EHN4.js +143 -0
- package/dist/cli-serve-UV3GVDRD.js +143 -0
- package/dist/cli-serve-V5QICXR5.js +143 -0
- package/dist/cli-serve-VG6Z6GIB.js +143 -0
- package/dist/cli-serve-XSYHPGZI.js +143 -0
- package/dist/cli-serve-Y534FCRV.js +140 -0
- package/dist/cli-verify-CZIITRED.js +149 -0
- package/dist/cli-verify-N73GOKEF.js +149 -0
- package/dist/cli-verify-QEEBZOUZ.js +149 -0
- package/dist/cli-verify-RC5HI6DU.js +149 -0
- package/dist/cli-verify-VKBNIEAX.js +149 -0
- package/dist/cli.js +5 -5
- package/dist/dashboard/app.js +8 -2
- package/dist/dashboard/components/org-switcher.js +5 -1
- package/dist/dashboard/org-switcher.js +156 -0
- package/dist/dashboard/pages/login.js +160 -4
- package/dist/dashboard/pages/task-pipeline.js +1 -1
- package/dist/factory-3IWXVE37.js +9 -0
- package/dist/factory-5M6PTMLC.js +11 -0
- package/dist/factory-CSSHN7GE.js +11 -0
- package/dist/factory-JFWXTAWK.js +11 -0
- package/dist/factory-TBGUYM5X.js +9 -0
- package/dist/google-W5AYGNUJ.js +33 -0
- package/dist/index.js +6 -6
- package/dist/meetings-FJ453ENF.js +12 -0
- package/dist/postgres-BCHZWRU3.js +832 -0
- package/dist/postgres-BI4QVRM6.js +825 -0
- package/dist/postgres-BOTHOPDW.js +875 -0
- package/dist/postgres-JBUKR3TA.js +873 -0
- package/dist/postgres-Z7QYSU6K.js +861 -0
- package/dist/routes-7QYAQTWA.js +90 -0
- package/dist/routes-JCBVZU54.js +90 -0
- package/dist/routes-KEDEJFRE.js +90 -0
- package/dist/routes-WI64ADVH.js +90 -0
- package/dist/routes-X36OSCID.js +90 -0
- package/dist/runtime-75XR6KEW.js +45 -0
- package/dist/runtime-BNM7ZNNL.js +45 -0
- package/dist/runtime-ES6WCJ7D.js +45 -0
- package/dist/runtime-KYJTML2B.js +45 -0
- package/dist/runtime-LO67ZHQA.js +45 -0
- package/dist/runtime-VIXKKVSZ.js +45 -0
- package/dist/runtime-WHWJPCGK.js +45 -0
- package/dist/runtime-Z2Q6GUHH.js +45 -0
- package/dist/runtime-ZZ6CALSB.js +45 -0
- package/dist/server-27A4WEJC.js +28 -0
- package/dist/server-2CBXP4WS.js +28 -0
- package/dist/server-4JQAB5R4.js +28 -0
- package/dist/server-6BOM5U64.js +28 -0
- package/dist/server-FLJKNPRD.js +28 -0
- package/dist/server-HMIHIQ2N.js +28 -0
- package/dist/server-KIXXLR2D.js +28 -0
- package/dist/server-KSEIZTXF.js +28 -0
- package/dist/server-MPVW7DKZ.js +28 -0
- package/dist/server-PRTVRQ2D.js +28 -0
- package/dist/server-SYIG6HAX.js +28 -0
- package/dist/server-U32KDIXC.js +28 -0
- package/dist/server-WFN6CA4T.js +28 -0
- package/dist/server-XQUE3FGT.js +28 -0
- package/dist/server-XWT2UORK.js +28 -0
- package/dist/server-Y3BGNN5Q.js +28 -0
- package/dist/setup-352L2TPS.js +20 -0
- package/dist/setup-4MM645XK.js +20 -0
- package/dist/setup-5JPWW6IP.js +20 -0
- package/dist/setup-CUN6LVUV.js +20 -0
- package/dist/setup-D3YHPWPY.js +20 -0
- package/dist/setup-D4A5I6UM.js +20 -0
- package/dist/setup-DOPLXTB3.js +20 -0
- package/dist/setup-E3NSIM6B.js +20 -0
- package/dist/setup-E3V2D7NL.js +20 -0
- package/dist/setup-FSYPGI2C.js +20 -0
- package/dist/setup-G3RPKRG3.js +20 -0
- package/dist/setup-KJ77HNWK.js +20 -0
- package/dist/setup-LPSOY5V5.js +20 -0
- package/dist/setup-N3ODDSQE.js +20 -0
- package/dist/setup-NLDM3M2P.js +20 -0
- package/dist/setup-SWJMNDWF.js +20 -0
- package/dist/system-prompts-6OUTAMH6.js +41 -0
- package/dist/task-queue-YP2I54IA.js +9 -0
- package/dist/telegram-QRNGRT5M.js +17 -0
- package/dist/whatsapp-VYVINCGV.js +31 -0
- package/god_is_great.html +35 -0
- package/package.json +1 -1
- package/src/agent-tools/index.ts +4 -1
- package/src/agent-tools/tool-resolver.ts +15 -4
- package/src/agent-tools/tools/browser.ts +2 -2
- package/src/agent-tools/tools/local/dependency-manager.ts +286 -0
- package/src/agent-tools/tools/local/index.ts +3 -0
- package/src/agent-tools/tools/messaging/telegram.ts +29 -0
- package/src/agent-tools/tools/messaging/whatsapp.ts +59 -4
- package/src/auth/routes.ts +1 -1
- package/src/cli-agent.ts +47 -6
- package/src/cli-serve.ts +2 -5
- package/src/dashboard/app.js +8 -2
- package/src/dashboard/components/org-switcher.js +5 -1
- package/src/dashboard/pages/login.js +160 -4
- package/src/dashboard/pages/task-pipeline.js +1 -1
- package/src/db/adapter.ts +2 -0
- package/src/db/factory.ts +78 -0
- package/src/db/postgres.ts +57 -12
- package/src/engine/agent-autonomy.ts +1 -1
- package/src/engine/agent-heartbeat.ts +1 -1
- package/src/engine/messaging-poller.ts +146 -11
- package/src/engine/oauth-connect-routes.ts +23 -3
- package/src/engine/routes.ts +1 -1
- package/src/engine/task-poller.ts +54 -3
- package/src/engine/task-queue.ts +30 -0
- package/src/runtime/index.ts +2 -1
- package/src/runtime/types.ts +2 -0
- package/src/server.ts +13 -1
- package/src/system-prompts/triage.ts +1 -1
|
@@ -0,0 +1,766 @@
|
|
|
1
|
+
import "./chunk-KFQGP6VL.js";
|
|
2
|
+
|
|
3
|
+
// src/engine/agent-autonomy.ts
|
|
4
|
+
var DEFAULT_AUTONOMY_SETTINGS = {
|
|
5
|
+
enabled: true,
|
|
6
|
+
clockEnabled: true,
|
|
7
|
+
dailyCatchupEnabled: true,
|
|
8
|
+
weeklyCatchupEnabled: true,
|
|
9
|
+
weeklyCatchupDay: 1,
|
|
10
|
+
goalCheckEnabled: true,
|
|
11
|
+
goalCheckHours: [14, 17],
|
|
12
|
+
knowledgeContribEnabled: true,
|
|
13
|
+
knowledgeContribDay: 5,
|
|
14
|
+
knowledgeContribHour: 15,
|
|
15
|
+
escalationEnabled: true,
|
|
16
|
+
guardrailEnforcementEnabled: true,
|
|
17
|
+
driveAccessRequestEnabled: true
|
|
18
|
+
};
|
|
19
|
+
var AgentAutonomyManager = class {
|
|
20
|
+
config;
|
|
21
|
+
settings;
|
|
22
|
+
clockState = { clockedIn: false };
|
|
23
|
+
schedulerInterval;
|
|
24
|
+
catchupInterval;
|
|
25
|
+
knowledgeInterval;
|
|
26
|
+
goalCheckInterval;
|
|
27
|
+
constructor(config) {
|
|
28
|
+
this.config = config;
|
|
29
|
+
this.settings = { ...DEFAULT_AUTONOMY_SETTINGS, ...config.settings || {} };
|
|
30
|
+
}
|
|
31
|
+
/** Reload settings from DB (called when config changes via dashboard) */
|
|
32
|
+
async reloadSettings() {
|
|
33
|
+
try {
|
|
34
|
+
const rows = await this.config.engineDb.query(
|
|
35
|
+
`SELECT config FROM managed_agents WHERE id = $1`,
|
|
36
|
+
[this.config.agentId]
|
|
37
|
+
);
|
|
38
|
+
if (rows?.[0]?.config) {
|
|
39
|
+
const cfg = typeof rows[0].config === "string" ? JSON.parse(rows[0].config) : rows[0].config;
|
|
40
|
+
if (cfg.autonomy) {
|
|
41
|
+
this.settings = { ...DEFAULT_AUTONOMY_SETTINGS, ...cfg.autonomy };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const schedRows = await this.config.engineDb.query(
|
|
45
|
+
`SELECT config, timezone FROM work_schedules WHERE agent_id = $1 AND enabled = TRUE ORDER BY created_at DESC LIMIT 1`,
|
|
46
|
+
[this.config.agentId]
|
|
47
|
+
);
|
|
48
|
+
if (schedRows?.[0]) {
|
|
49
|
+
const schedConfig = typeof schedRows[0].config === "string" ? JSON.parse(schedRows[0].config) : schedRows[0].config;
|
|
50
|
+
if (schedConfig?.standardHours) {
|
|
51
|
+
this.config.schedule = {
|
|
52
|
+
start: schedConfig.standardHours.start,
|
|
53
|
+
end: schedConfig.standardHours.end,
|
|
54
|
+
days: schedConfig.standardHours.daysOfWeek || [1, 2, 3, 4, 5]
|
|
55
|
+
};
|
|
56
|
+
if (schedRows[0].timezone) this.config.timezone = schedRows[0].timezone;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.warn(`[autonomy] Failed to reload settings: ${err.message}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
getSettings() {
|
|
64
|
+
return { ...this.settings };
|
|
65
|
+
}
|
|
66
|
+
async start() {
|
|
67
|
+
if (!this.settings.enabled) {
|
|
68
|
+
console.log("[autonomy] Disabled via settings, skipping");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
console.log("[autonomy] Starting agent autonomy system...");
|
|
72
|
+
await this.reloadSettings();
|
|
73
|
+
if (this.settings.clockEnabled) await this.checkClockState();
|
|
74
|
+
this.schedulerInterval = setInterval(() => {
|
|
75
|
+
if (this.settings.clockEnabled) this.checkClockState();
|
|
76
|
+
}, 6e4);
|
|
77
|
+
this.catchupInterval = setInterval(() => this.checkCatchupSchedule(), 15 * 6e4);
|
|
78
|
+
setTimeout(() => this.checkCatchupSchedule(), 3e4);
|
|
79
|
+
this.knowledgeInterval = setInterval(() => this.checkKnowledgeContribution(), 60 * 6e4);
|
|
80
|
+
this.goalCheckInterval = setInterval(() => this.checkGoalProgress(), 30 * 6e4);
|
|
81
|
+
setInterval(() => this.reloadSettings(), 10 * 6e4);
|
|
82
|
+
const features = [];
|
|
83
|
+
if (this.settings.clockEnabled) features.push("clock");
|
|
84
|
+
if (this.settings.dailyCatchupEnabled) features.push("daily-catchup(time from Manager tab)");
|
|
85
|
+
if (this.settings.weeklyCatchupEnabled) features.push("weekly-catchup@" + ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][this.settings.weeklyCatchupDay]);
|
|
86
|
+
if (this.settings.goalCheckEnabled) features.push("goals@" + this.settings.goalCheckHours.join(","));
|
|
87
|
+
if (this.settings.knowledgeContribEnabled) features.push("knowledge@" + ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][this.settings.knowledgeContribDay]);
|
|
88
|
+
console.log("[autonomy] Active features: " + features.join(", "));
|
|
89
|
+
}
|
|
90
|
+
stop() {
|
|
91
|
+
if (this.schedulerInterval) clearInterval(this.schedulerInterval);
|
|
92
|
+
if (this.catchupInterval) clearInterval(this.catchupInterval);
|
|
93
|
+
if (this.knowledgeInterval) clearInterval(this.knowledgeInterval);
|
|
94
|
+
if (this.goalCheckInterval) clearInterval(this.goalCheckInterval);
|
|
95
|
+
console.log("[autonomy] System stopped");
|
|
96
|
+
}
|
|
97
|
+
// ─── 1. Auto Clock-In/Out ──────────────────────────
|
|
98
|
+
async checkClockState() {
|
|
99
|
+
const schedule = this.config.schedule;
|
|
100
|
+
if (!schedule) return;
|
|
101
|
+
const now = /* @__PURE__ */ new Date();
|
|
102
|
+
const tz = this.config.timezone || "UTC";
|
|
103
|
+
const localTime = new Date(now.toLocaleString("en-US", { timeZone: tz }));
|
|
104
|
+
const currentHour = localTime.getHours();
|
|
105
|
+
const currentMinute = localTime.getMinutes();
|
|
106
|
+
const currentDay = localTime.getDay();
|
|
107
|
+
const currentTimeStr = `${String(currentHour).padStart(2, "0")}:${String(currentMinute).padStart(2, "0")}`;
|
|
108
|
+
const isWorkday = schedule.days.includes(currentDay);
|
|
109
|
+
const isWithinHours = currentTimeStr >= schedule.start && currentTimeStr < schedule.end;
|
|
110
|
+
if (isWorkday && isWithinHours && !this.clockState.clockedIn) {
|
|
111
|
+
await this.clockIn();
|
|
112
|
+
await this.morningTriage();
|
|
113
|
+
} else if ((!isWorkday || !isWithinHours) && this.clockState.clockedIn) {
|
|
114
|
+
await this.clockOut();
|
|
115
|
+
}
|
|
116
|
+
this.clockState.lastCheckTime = now.toISOString();
|
|
117
|
+
}
|
|
118
|
+
async clockIn() {
|
|
119
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
120
|
+
this.clockState.clockedIn = true;
|
|
121
|
+
this.clockState.clockInTime = now;
|
|
122
|
+
try {
|
|
123
|
+
await this.config.engineDb.execute(
|
|
124
|
+
`INSERT INTO clock_records (id, org_id, agent_id, type, triggered_by, actual_at, created_at) VALUES ($1, $2, $3, 'clock_in', 'auto_scheduler', $4, $4)`,
|
|
125
|
+
[crypto.randomUUID(), this.config.orgId, this.config.agentId, now]
|
|
126
|
+
);
|
|
127
|
+
console.log(`[autonomy] \u23F0 Clocked IN at ${now}`);
|
|
128
|
+
if (this.config.memoryManager) {
|
|
129
|
+
await this.config.memoryManager.storeMemory(this.config.agentId, {
|
|
130
|
+
content: `Clocked in at ${now}. Starting work shift.`,
|
|
131
|
+
category: "context",
|
|
132
|
+
importance: "low",
|
|
133
|
+
confidence: 1
|
|
134
|
+
}).catch(() => {
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.error(`[autonomy] Clock-in error: ${err.message}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async clockOut() {
|
|
142
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
143
|
+
this.clockState.clockedIn = false;
|
|
144
|
+
this.clockState.clockOutTime = now;
|
|
145
|
+
try {
|
|
146
|
+
await this.config.engineDb.execute(
|
|
147
|
+
`INSERT INTO clock_records (id, org_id, agent_id, type, triggered_by, reason, actual_at, created_at) VALUES ($1, $2, $3, 'clock_out', 'auto_scheduler', 'End of scheduled hours', $4, $4)`,
|
|
148
|
+
[crypto.randomUUID(), this.config.orgId, this.config.agentId, now]
|
|
149
|
+
);
|
|
150
|
+
console.log(`[autonomy] \u23F0 Clocked OUT at ${now}`);
|
|
151
|
+
if (this.config.memoryManager) {
|
|
152
|
+
await this.config.memoryManager.storeMemory(this.config.agentId, {
|
|
153
|
+
content: `Clocked out at ${now}. Work shift ended.`,
|
|
154
|
+
category: "context",
|
|
155
|
+
importance: "low",
|
|
156
|
+
confidence: 1
|
|
157
|
+
}).catch(() => {
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
} catch (err) {
|
|
161
|
+
console.error(`[autonomy] Clock-out error: ${err.message}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Morning Triage — runs once after clock-in.
|
|
166
|
+
* Scans for off-hours accumulated items (emails, chats, failed sessions)
|
|
167
|
+
* and creates a single LLM session to triage everything into tasks.
|
|
168
|
+
*
|
|
169
|
+
* This avoids the problem of 15 emails = 15 separate sessions.
|
|
170
|
+
* Instead: 1 triage session → creates Google Tasks → then handles them in order.
|
|
171
|
+
*/
|
|
172
|
+
async morningTriage() {
|
|
173
|
+
if (!this.config.runtime) return;
|
|
174
|
+
const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
175
|
+
const triageKey = `morning_triage_${dateStr}`;
|
|
176
|
+
const alreadyDone = await this.checkMemoryFlag(triageKey);
|
|
177
|
+
if (alreadyDone) return;
|
|
178
|
+
console.log("[autonomy] \u{1F305} Morning triage \u2014 scanning off-hours accumulation...");
|
|
179
|
+
const db = this.config.engineDb;
|
|
180
|
+
const agentId = this.config.agentId;
|
|
181
|
+
let unhandledEmails = 0;
|
|
182
|
+
let failedSessions = 0;
|
|
183
|
+
let failedChats = 0;
|
|
184
|
+
try {
|
|
185
|
+
const lastClockOut = this.clockState.clockOutTime || new Date(Date.now() - 16 * 60 * 60 * 1e3).toISOString();
|
|
186
|
+
const emailRows = await db.query(
|
|
187
|
+
`SELECT COUNT(*) as cnt FROM agent_sessions
|
|
188
|
+
WHERE agent_id = $1 AND created_at > $2 AND metadata::text LIKE '%email%'`,
|
|
189
|
+
[agentId, lastClockOut]
|
|
190
|
+
);
|
|
191
|
+
unhandledEmails = parseInt(emailRows?.[0]?.cnt || "0");
|
|
192
|
+
const failedRows = await db.query(
|
|
193
|
+
`SELECT COUNT(*) as cnt FROM agent_sessions
|
|
194
|
+
WHERE agent_id = $1 AND status = 'failed' AND created_at > $2`,
|
|
195
|
+
[agentId, lastClockOut]
|
|
196
|
+
);
|
|
197
|
+
failedSessions = parseInt(failedRows?.[0]?.cnt || "0");
|
|
198
|
+
const chatRows = await db.query(
|
|
199
|
+
`SELECT COUNT(*) as cnt FROM agent_sessions
|
|
200
|
+
WHERE agent_id = $1 AND status = 'failed' AND metadata::text LIKE '%chat%' AND created_at > $2`,
|
|
201
|
+
[agentId, lastClockOut]
|
|
202
|
+
);
|
|
203
|
+
failedChats = parseInt(chatRows?.[0]?.cnt || "0");
|
|
204
|
+
} catch {
|
|
205
|
+
}
|
|
206
|
+
const totalItems = unhandledEmails + failedSessions + failedChats;
|
|
207
|
+
if (totalItems === 0) {
|
|
208
|
+
console.log("[autonomy] \u{1F305} Morning triage: nothing accumulated overnight. Clean start!");
|
|
209
|
+
await this.setMemoryFlag(triageKey);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
console.log(`[autonomy] \u{1F305} Morning triage: ${unhandledEmails} emails, ${failedSessions} failed sessions, ${failedChats} failed chats`);
|
|
213
|
+
if (totalItems <= 3) {
|
|
214
|
+
console.log("[autonomy] \u{1F305} Only a few items \u2014 skipping triage session, they'll be handled individually.");
|
|
215
|
+
await this.setMemoryFlag(triageKey);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const prompt = `Good morning! You just clocked in. Here's what accumulated while you were off:
|
|
219
|
+
|
|
220
|
+
- ${unhandledEmails} email session(s) were created overnight
|
|
221
|
+
- ${failedSessions} session(s) failed (may need retry)
|
|
222
|
+
- ${failedChats} chat message(s) may be unanswered
|
|
223
|
+
|
|
224
|
+
Your morning routine:
|
|
225
|
+
1. Check your inbox with gmail_search (unread only) \u2014 scan subjects and senders
|
|
226
|
+
2. For each important email, create a Google Task: google_tasks_create with title, notes, and priority
|
|
227
|
+
3. Check Google Chat for any unanswered messages: google_chat_list_messages
|
|
228
|
+
4. For any failed sessions that look important, add them as tasks too
|
|
229
|
+
5. Send your manager (${this.config.managerEmail}) a brief "starting my day" message listing your top priorities
|
|
230
|
+
6. After triage, start working through tasks in priority order
|
|
231
|
+
|
|
232
|
+
Prioritize: manager emails > urgent requests > routine items > FYI messages.
|
|
233
|
+
Create tasks in a "Today" list so you can track progress throughout the day.`;
|
|
234
|
+
const systemPrompt = `You are ${this.config.agentName}, a ${this.config.role}.
|
|
235
|
+
You just clocked in for the day. Your first task is to triage everything that accumulated overnight.
|
|
236
|
+
Be systematic: scan, prioritize, create tasks, then execute. Don't just dive into the first email you see.
|
|
237
|
+
Work schedule: ${this.config.schedule ? `${this.config.schedule.start}-${this.config.schedule.end} ${this.config.timezone}` : "Standard hours"}`;
|
|
238
|
+
try {
|
|
239
|
+
const session = await this.config.runtime.spawnSession({
|
|
240
|
+
agentId,
|
|
241
|
+
message: prompt,
|
|
242
|
+
systemPrompt
|
|
243
|
+
});
|
|
244
|
+
console.log(`[autonomy] \u{1F305} Morning triage session ${session.id} created`);
|
|
245
|
+
await this.setMemoryFlag(triageKey);
|
|
246
|
+
} catch (err) {
|
|
247
|
+
console.error(`[autonomy] Morning triage error: ${err.message}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
isWorkingHours() {
|
|
251
|
+
return this.clockState.clockedIn;
|
|
252
|
+
}
|
|
253
|
+
getClockState() {
|
|
254
|
+
return { ...this.clockState };
|
|
255
|
+
}
|
|
256
|
+
// ─── 2. Manager Catchup Emails ─────────────────────
|
|
257
|
+
async checkCatchupSchedule() {
|
|
258
|
+
if (!this.config.managerEmail || !this.config.runtime) return;
|
|
259
|
+
let catchUpHour = 9;
|
|
260
|
+
let catchUpMinute = 0;
|
|
261
|
+
let catchUpTz = this.config.timezone || "UTC";
|
|
262
|
+
try {
|
|
263
|
+
const rows = await this.config.engineDb.query(
|
|
264
|
+
`SELECT config FROM managed_agents WHERE id = $1`,
|
|
265
|
+
[this.config.agentId]
|
|
266
|
+
);
|
|
267
|
+
if (rows?.[0]?.config) {
|
|
268
|
+
const cfg = typeof rows[0].config === "string" ? JSON.parse(rows[0].config) : rows[0].config;
|
|
269
|
+
if (cfg.dailyCatchUp?.time) {
|
|
270
|
+
const parts = cfg.dailyCatchUp.time.split(":");
|
|
271
|
+
catchUpHour = parseInt(parts[0]) || 9;
|
|
272
|
+
catchUpMinute = parseInt(parts[1]) || 0;
|
|
273
|
+
}
|
|
274
|
+
if (cfg.dailyCatchUp?.timezone) catchUpTz = cfg.dailyCatchUp.timezone;
|
|
275
|
+
if (cfg.dailyCatchUp && cfg.dailyCatchUp.enabled === false) return;
|
|
276
|
+
}
|
|
277
|
+
} catch {
|
|
278
|
+
}
|
|
279
|
+
const now = /* @__PURE__ */ new Date();
|
|
280
|
+
const localTime = new Date(now.toLocaleString("en-US", { timeZone: catchUpTz }));
|
|
281
|
+
const hour = localTime.getHours();
|
|
282
|
+
const minute = localTime.getMinutes();
|
|
283
|
+
const dayOfWeek = localTime.getDay();
|
|
284
|
+
const dateStr = localTime.toISOString().split("T")[0];
|
|
285
|
+
const isWeeklyCatchupTime = this.settings.weeklyCatchupEnabled && dayOfWeek === this.settings.weeklyCatchupDay && hour === catchUpHour && minute >= catchUpMinute && minute < catchUpMinute + 15;
|
|
286
|
+
const isDailyCatchupTime = this.settings.dailyCatchupEnabled && hour === catchUpHour && minute >= catchUpMinute && minute < catchUpMinute + 15;
|
|
287
|
+
if (!isDailyCatchupTime && !isWeeklyCatchupTime) return;
|
|
288
|
+
const catchupKey = isWeeklyCatchupTime ? `weekly_catchup_${dateStr}` : `daily_catchup_${dateStr}`;
|
|
289
|
+
const alreadySent = await this.checkMemoryFlag(catchupKey);
|
|
290
|
+
if (alreadySent) return;
|
|
291
|
+
console.log(`[autonomy] ${isWeeklyCatchupTime ? "Weekly" : "Daily"} catchup time \u2014 generating report...`);
|
|
292
|
+
try {
|
|
293
|
+
const catchupData = await this.gatherCatchupData(isWeeklyCatchupTime ? 7 : 1);
|
|
294
|
+
await this.sendCatchupEmail(catchupData, isWeeklyCatchupTime);
|
|
295
|
+
await this.setMemoryFlag(catchupKey);
|
|
296
|
+
} catch (err) {
|
|
297
|
+
console.error(`[autonomy] Catchup email error: ${err.message}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
async gatherCatchupData(daysBack) {
|
|
301
|
+
const since = new Date(Date.now() - daysBack * 24 * 60 * 60 * 1e3).toISOString();
|
|
302
|
+
const db = this.config.engineDb;
|
|
303
|
+
const agentId = this.config.agentId;
|
|
304
|
+
let emailsHandled = 0;
|
|
305
|
+
try {
|
|
306
|
+
const rows = await db.query(
|
|
307
|
+
`SELECT COUNT(*) as cnt FROM agent_memory WHERE agent_id = $1 AND category = 'processed_email' AND created_at >= $2`,
|
|
308
|
+
[agentId, since]
|
|
309
|
+
);
|
|
310
|
+
emailsHandled = rows?.[0]?.cnt || 0;
|
|
311
|
+
} catch {
|
|
312
|
+
}
|
|
313
|
+
let sessionsRun = 0;
|
|
314
|
+
try {
|
|
315
|
+
const rows = await db.query(
|
|
316
|
+
`SELECT COUNT(*) as cnt FROM agent_sessions WHERE agent_id = $1 AND created_at >= $2`,
|
|
317
|
+
[agentId, since]
|
|
318
|
+
);
|
|
319
|
+
sessionsRun = rows?.[0]?.cnt || 0;
|
|
320
|
+
} catch {
|
|
321
|
+
}
|
|
322
|
+
let memoriesStored = 0;
|
|
323
|
+
try {
|
|
324
|
+
const rows = await db.query(
|
|
325
|
+
`SELECT COUNT(*) as cnt FROM agent_memory WHERE agent_id = $1 AND created_at >= $2`,
|
|
326
|
+
[agentId, since]
|
|
327
|
+
);
|
|
328
|
+
memoriesStored = rows?.[0]?.cnt || 0;
|
|
329
|
+
} catch {
|
|
330
|
+
}
|
|
331
|
+
let tasksCompleted = [];
|
|
332
|
+
try {
|
|
333
|
+
const taskRows = await db.query(
|
|
334
|
+
`SELECT content FROM agent_memory WHERE agent_id = $1 AND category = 'skill' AND content LIKE '%task%complete%' AND created_at >= $2 ORDER BY created_at DESC LIMIT 10`,
|
|
335
|
+
[agentId, since]
|
|
336
|
+
);
|
|
337
|
+
tasksCompleted = (taskRows || []).map((r) => r.content?.slice(0, 200));
|
|
338
|
+
} catch {
|
|
339
|
+
}
|
|
340
|
+
let issuesEncountered = [];
|
|
341
|
+
try {
|
|
342
|
+
const issueRows = await db.query(
|
|
343
|
+
`SELECT content FROM agent_memory WHERE agent_id = $1 AND category = 'correction' AND created_at >= $2 ORDER BY created_at DESC LIMIT 5`,
|
|
344
|
+
[agentId, since]
|
|
345
|
+
);
|
|
346
|
+
issuesEncountered = (issueRows || []).map((r) => r.content?.slice(0, 200));
|
|
347
|
+
} catch {
|
|
348
|
+
}
|
|
349
|
+
let knowledgeGained = [];
|
|
350
|
+
try {
|
|
351
|
+
const knowRows = await db.query(
|
|
352
|
+
`SELECT content FROM agent_memory WHERE agent_id = $1 AND (category = 'skill' OR category = 'org_knowledge') AND created_at >= $2 ORDER BY created_at DESC LIMIT 10`,
|
|
353
|
+
[agentId, since]
|
|
354
|
+
);
|
|
355
|
+
knowledgeGained = (knowRows || []).map((r) => r.content?.slice(0, 200));
|
|
356
|
+
} catch {
|
|
357
|
+
}
|
|
358
|
+
return { emailsHandled, sessionsRun, memoriesStored, tasksCompleted, issuesEncountered, knowledgeGained };
|
|
359
|
+
}
|
|
360
|
+
async sendCatchupEmail(data, isWeekly) {
|
|
361
|
+
const runtime = this.config.runtime;
|
|
362
|
+
const managerEmail = this.config.managerEmail;
|
|
363
|
+
const agentName = this.config.agentName;
|
|
364
|
+
const role = this.config.role;
|
|
365
|
+
const period = isWeekly ? "last week" : "yesterday";
|
|
366
|
+
const nextPeriod = isWeekly ? "this week" : "today";
|
|
367
|
+
const prompt = `You need to send your ${isWeekly ? "weekly" : "daily"} catchup email to your manager at ${managerEmail}.
|
|
368
|
+
|
|
369
|
+
Here's what you accomplished ${period}:
|
|
370
|
+
- Emails handled: ${data.emailsHandled}
|
|
371
|
+
- Sessions/conversations: ${data.sessionsRun}
|
|
372
|
+
- Memories stored: ${data.memoriesStored}
|
|
373
|
+
- Tasks completed: ${data.tasksCompleted.length > 0 ? data.tasksCompleted.join("; ") : "None tracked"}
|
|
374
|
+
- Issues encountered: ${data.issuesEncountered.length > 0 ? data.issuesEncountered.join("; ") : "None"}
|
|
375
|
+
- Knowledge gained: ${data.knowledgeGained.length > 0 ? data.knowledgeGained.join("; ") : "None tracked"}
|
|
376
|
+
|
|
377
|
+
Write and send a concise, professional ${isWeekly ? "weekly" : "daily"} summary email. Include:
|
|
378
|
+
1. What you accomplished ${period} (be specific, not generic)
|
|
379
|
+
2. Any issues or blockers you encountered
|
|
380
|
+
3. What you plan to focus on ${nextPeriod}
|
|
381
|
+
${isWeekly ? "4. Goals for the week (create Google Tasks for each goal)" : ""}
|
|
382
|
+
${isWeekly ? "5. Any suggestions for improvement or areas where you need guidance" : ""}
|
|
383
|
+
|
|
384
|
+
Keep it under ${isWeekly ? "400" : "250"} words. Be genuine and specific \u2014 your manager reads these to stay informed.
|
|
385
|
+
Use gmail_send to send the email. Subject: "${isWeekly ? "Weekly" : "Daily"} Update \u2014 ${agentName}"
|
|
386
|
+
|
|
387
|
+
${isWeekly ? "After sending the email, create Google Tasks for your goals this week using google_tasks_create." : ""}`;
|
|
388
|
+
const systemPrompt = `You are ${agentName}, a ${role}. You are sending your ${isWeekly ? "weekly" : "daily"} catchup email to your manager.
|
|
389
|
+
Be professional but genuine. Use real data from the summary \u2014 don't make up accomplishments.
|
|
390
|
+
Available tools: gmail_send (to, subject, body), google_tasks_create (listId, title, notes, dueDate).`;
|
|
391
|
+
try {
|
|
392
|
+
const session = await runtime.spawnSession({
|
|
393
|
+
agentId: this.config.agentId,
|
|
394
|
+
message: prompt,
|
|
395
|
+
systemPrompt
|
|
396
|
+
});
|
|
397
|
+
console.log(`[autonomy] \u2705 ${isWeekly ? "Weekly" : "Daily"} catchup email session ${session.id} created`);
|
|
398
|
+
} catch (err) {
|
|
399
|
+
console.error(`[autonomy] Failed to send catchup email: ${err.message}`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
// ─── 3. Goal Setting & Auto-Reminders ──────────────
|
|
403
|
+
async checkGoalProgress() {
|
|
404
|
+
if (!this.settings.goalCheckEnabled || !this.config.runtime || !this.clockState.clockedIn) return;
|
|
405
|
+
const now = /* @__PURE__ */ new Date();
|
|
406
|
+
const tz = this.config.timezone || "UTC";
|
|
407
|
+
const localTime = new Date(now.toLocaleString("en-US", { timeZone: tz }));
|
|
408
|
+
const hour = localTime.getHours();
|
|
409
|
+
const goalHours = this.settings.goalCheckHours || [14, 17];
|
|
410
|
+
if (!goalHours.includes(hour)) return;
|
|
411
|
+
const dateStr = localTime.toISOString().split("T")[0];
|
|
412
|
+
const checkKey = `goal_check_${dateStr}_${hour}`;
|
|
413
|
+
const alreadyChecked = await this.checkMemoryFlag(checkKey);
|
|
414
|
+
if (alreadyChecked) return;
|
|
415
|
+
console.log(`[autonomy] Goal progress check at ${hour}:00`);
|
|
416
|
+
try {
|
|
417
|
+
const isEndOfDay = hour === Math.max(...goalHours);
|
|
418
|
+
const prompt = isEndOfDay ? `It's end of day. Review your goals and tasks:
|
|
419
|
+
1. Call google_tasks_list to see your current tasks
|
|
420
|
+
2. Review what you completed today
|
|
421
|
+
3. Mark completed tasks as done with google_tasks_complete
|
|
422
|
+
4. For incomplete tasks, update notes with progress
|
|
423
|
+
5. Store a brief end-of-day reflection in memory about what went well and what to improve tomorrow
|
|
424
|
+
6. If any task is blocked, email your manager at ${this.config.managerEmail || "your manager"} about it` : `Mid-day goal check:
|
|
425
|
+
1. Call google_tasks_list to see your current tasks
|
|
426
|
+
2. Review progress on today's priorities
|
|
427
|
+
3. If you're behind on any task, adjust your approach
|
|
428
|
+
4. Store any insights in memory for future reference`;
|
|
429
|
+
const session = await this.config.runtime.spawnSession({
|
|
430
|
+
agentId: this.config.agentId,
|
|
431
|
+
message: prompt,
|
|
432
|
+
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.`
|
|
433
|
+
});
|
|
434
|
+
console.log(`[autonomy] \u2705 Goal check session ${session.id} created`);
|
|
435
|
+
await this.setMemoryFlag(checkKey);
|
|
436
|
+
} catch (err) {
|
|
437
|
+
console.error(`[autonomy] Goal check error: ${err.message}`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
// ─── 4. Knowledge Contribution (Friday) ────────────
|
|
441
|
+
async checkKnowledgeContribution() {
|
|
442
|
+
if (!this.settings.knowledgeContribEnabled || !this.config.runtime || !this.clockState.clockedIn) return;
|
|
443
|
+
const now = /* @__PURE__ */ new Date();
|
|
444
|
+
const tz = this.config.timezone || "UTC";
|
|
445
|
+
const localTime = new Date(now.toLocaleString("en-US", { timeZone: tz }));
|
|
446
|
+
const dayOfWeek = localTime.getDay();
|
|
447
|
+
const hour = localTime.getHours();
|
|
448
|
+
const dateStr = localTime.toISOString().split("T")[0];
|
|
449
|
+
if (dayOfWeek !== this.settings.knowledgeContribDay || hour !== this.settings.knowledgeContribHour) return;
|
|
450
|
+
const contribKey = `knowledge_contribution_${dateStr}`;
|
|
451
|
+
const alreadyDone = await this.checkMemoryFlag(contribKey);
|
|
452
|
+
if (alreadyDone) return;
|
|
453
|
+
console.log("[autonomy] Friday knowledge contribution time!");
|
|
454
|
+
try {
|
|
455
|
+
const roleCategory = this.mapRoleToKnowledgeCategory(this.config.role);
|
|
456
|
+
const prompt = `It's Friday \u2014 time for your weekly knowledge contribution.
|
|
457
|
+
|
|
458
|
+
Your role is ${this.config.role}, so focus on ${roleCategory} knowledge.
|
|
459
|
+
|
|
460
|
+
Steps:
|
|
461
|
+
1. Search your memory for key learnings this week: memory(action: "search", query: "learned this week")
|
|
462
|
+
2. Search for tool patterns you discovered: memory(action: "search", query: "tool")
|
|
463
|
+
3. Search for corrections and gotchas: memory(action: "search", query: "correction")
|
|
464
|
+
4. Compile the most valuable learnings into knowledge entries
|
|
465
|
+
5. For each entry, store it with a clear title and category:
|
|
466
|
+
- memory(action: "set", key: "knowledge-contrib-[topic]", value: "Clear description of the learning, including steps and examples", category: "org_knowledge", importance: "high")
|
|
467
|
+
|
|
468
|
+
Categories to contribute to (pick the most relevant):
|
|
469
|
+
${roleCategory === "support" ? "- customer-issues: Common customer problems and solutions\n- escalation-procedures: When and how to escalate\n- tool-patterns: Efficient ways to use tools\n- communication-templates: Effective response patterns" : ""}
|
|
470
|
+
${roleCategory === "sales" ? "- objection-handling: How to address common objections\n- product-knowledge: Product features and benefits\n- prospect-research: Effective research methods" : ""}
|
|
471
|
+
${roleCategory === "engineering" ? "- debugging-patterns: Common issues and fixes\n- architecture-decisions: Design choices and rationale\n- tool-expertise: Development tool tips" : ""}
|
|
472
|
+
${roleCategory === "general" ? "- best-practices: General workflow improvements\n- tool-patterns: Tool usage tips\n- process-improvements: Better ways to do things" : ""}
|
|
473
|
+
|
|
474
|
+
After storing knowledge entries, email your manager a brief summary of what you contributed.
|
|
475
|
+
Aim for 3-5 high-quality entries. Quality over quantity.`;
|
|
476
|
+
const session = await this.config.runtime.spawnSession({
|
|
477
|
+
agentId: this.config.agentId,
|
|
478
|
+
message: prompt,
|
|
479
|
+
systemPrompt: `You are ${this.config.agentName}, a ${this.config.role}. You are contributing weekly knowledge to your organization's knowledge base. Focus on ${roleCategory}-related insights. Be specific and actionable \u2014 vague entries are useless.`
|
|
480
|
+
});
|
|
481
|
+
console.log(`[autonomy] \u2705 Knowledge contribution session ${session.id} created`);
|
|
482
|
+
await this.setMemoryFlag(contribKey);
|
|
483
|
+
} catch (err) {
|
|
484
|
+
console.error(`[autonomy] Knowledge contribution error: ${err.message}`);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
mapRoleToKnowledgeCategory(role) {
|
|
488
|
+
const roleLower = (role || "").toLowerCase();
|
|
489
|
+
if (roleLower.includes("support") || roleLower.includes("customer") || roleLower.includes("service")) return "support";
|
|
490
|
+
if (roleLower.includes("sales") || roleLower.includes("business dev")) return "sales";
|
|
491
|
+
if (roleLower.includes("engineer") || roleLower.includes("developer") || roleLower.includes("technical")) return "engineering";
|
|
492
|
+
if (roleLower.includes("marketing") || roleLower.includes("content")) return "marketing";
|
|
493
|
+
if (roleLower.includes("hr") || roleLower.includes("human resource") || roleLower.includes("people")) return "hr";
|
|
494
|
+
if (roleLower.includes("finance") || roleLower.includes("accounting")) return "finance";
|
|
495
|
+
if (roleLower.includes("legal") || roleLower.includes("compliance")) return "legal";
|
|
496
|
+
if (roleLower.includes("research") || roleLower.includes("analyst")) return "research";
|
|
497
|
+
if (roleLower.includes("operations") || roleLower.includes("ops")) return "operations";
|
|
498
|
+
return "general";
|
|
499
|
+
}
|
|
500
|
+
// ─── 5. Smart Answer Escalation ────────────────────
|
|
501
|
+
/**
|
|
502
|
+
* Generates a system prompt addendum that teaches the agent the escalation workflow.
|
|
503
|
+
* This is injected into every email-handling session.
|
|
504
|
+
*/
|
|
505
|
+
static getEscalationPrompt(managerEmail, orgDriveFolderId) {
|
|
506
|
+
return `
|
|
507
|
+
== SMART ANSWER WORKFLOW (MANDATORY) ==
|
|
508
|
+
When you receive a question or request you're not 100% confident about, follow this escalation chain:
|
|
509
|
+
|
|
510
|
+
STEP 1: Search your own memory
|
|
511
|
+
- memory(action: "search", query: "relevant keywords")
|
|
512
|
+
- Check for similar past questions, corrections, and learned patterns
|
|
513
|
+
|
|
514
|
+
STEP 2: Search organization Drive (shared knowledge)
|
|
515
|
+
${orgDriveFolderId ? `- google_drive_list with query "fullText contains 'search terms'" and parents in '${orgDriveFolderId}'` : `- google_drive_list with query "fullText contains 'search terms'" to search shared docs`}
|
|
516
|
+
- Read relevant documents with google_drive_get to find the answer
|
|
517
|
+
- Check Google Sheets for data tables, Google Docs for procedures
|
|
518
|
+
|
|
519
|
+
STEP 3: If still unsure \u2014 ESCALATE to manager
|
|
520
|
+
${managerEmail ? `- Send an email to ${managerEmail} with:` : "- Send an email to your manager with:"}
|
|
521
|
+
Subject: "Need Guidance: [Brief topic]"
|
|
522
|
+
Body must include:
|
|
523
|
+
a) The original question/request (who asked, what they need)
|
|
524
|
+
b) What you found in your search (memory + Drive results)
|
|
525
|
+
c) Your proposed answer (what you THINK the answer should be)
|
|
526
|
+
d) What specifically you're unsure about
|
|
527
|
+
e) Ask for approval or correction before responding
|
|
528
|
+
|
|
529
|
+
NEVER guess or fabricate an answer. It's better to escalate than to be wrong.
|
|
530
|
+
|
|
531
|
+
After receiving manager feedback:
|
|
532
|
+
- Store the correct answer in memory as a "correction" or "org_knowledge" entry
|
|
533
|
+
- Apply the correction to your response
|
|
534
|
+
- Thank the requester for their patience
|
|
535
|
+
|
|
536
|
+
The goal: build confidence over time. Today you escalate often. In a month, you'll know most answers from memory.`;
|
|
537
|
+
}
|
|
538
|
+
// ─── Helper Methods ────────────────────────────────
|
|
539
|
+
async checkMemoryFlag(key) {
|
|
540
|
+
if (!this.config.memoryManager) return false;
|
|
541
|
+
try {
|
|
542
|
+
const results = await this.config.memoryManager.recall(this.config.agentId, key, 1);
|
|
543
|
+
return results.some((m) => m.content?.includes(key));
|
|
544
|
+
} catch {
|
|
545
|
+
try {
|
|
546
|
+
const rows = await this.config.engineDb.query(
|
|
547
|
+
`SELECT id FROM agent_memory WHERE agent_id = $1 AND content LIKE $2 LIMIT 1`,
|
|
548
|
+
[this.config.agentId, `%${key}%`]
|
|
549
|
+
);
|
|
550
|
+
return rows && rows.length > 0;
|
|
551
|
+
} catch {
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
async setMemoryFlag(key) {
|
|
557
|
+
if (!this.config.memoryManager) return;
|
|
558
|
+
try {
|
|
559
|
+
await this.config.memoryManager.storeMemory(this.config.agentId, {
|
|
560
|
+
content: `${key}: completed at ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
561
|
+
category: "context",
|
|
562
|
+
importance: "low",
|
|
563
|
+
confidence: 1
|
|
564
|
+
});
|
|
565
|
+
} catch {
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
var GuardrailEnforcer = class {
|
|
570
|
+
engineDb;
|
|
571
|
+
rules = /* @__PURE__ */ new Map();
|
|
572
|
+
lastLoad = 0;
|
|
573
|
+
RELOAD_INTERVAL = 5 * 6e4;
|
|
574
|
+
// reload rules every 5 min
|
|
575
|
+
constructor(engineDb) {
|
|
576
|
+
this.engineDb = engineDb;
|
|
577
|
+
}
|
|
578
|
+
async ensureRulesLoaded() {
|
|
579
|
+
if (Date.now() - this.lastLoad < this.RELOAD_INTERVAL && this.rules.size > 0) return;
|
|
580
|
+
try {
|
|
581
|
+
const rows = await this.engineDb.query("SELECT * FROM guardrail_rules WHERE enabled = TRUE");
|
|
582
|
+
this.rules.clear();
|
|
583
|
+
for (const r of rows || []) {
|
|
584
|
+
this.rules.set(r.id, {
|
|
585
|
+
id: r.id,
|
|
586
|
+
orgId: r.org_id,
|
|
587
|
+
name: r.name,
|
|
588
|
+
category: r.category,
|
|
589
|
+
ruleType: r.rule_type,
|
|
590
|
+
conditions: typeof r.conditions === "string" ? JSON.parse(r.conditions) : r.conditions || {},
|
|
591
|
+
action: r.action,
|
|
592
|
+
severity: r.severity || "medium",
|
|
593
|
+
cooldownMinutes: r.cooldown_minutes || 0,
|
|
594
|
+
lastTriggeredAt: r.last_triggered_at,
|
|
595
|
+
triggerCount: r.trigger_count || 0
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
this.lastLoad = Date.now();
|
|
599
|
+
} catch (err) {
|
|
600
|
+
console.warn(`[guardrail-enforcer] Failed to load rules: ${err.message}`);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Check if an agent action should be blocked or flagged.
|
|
605
|
+
* Returns { allowed: true } or { allowed: false, reason, action }
|
|
606
|
+
*/
|
|
607
|
+
async evaluate(event) {
|
|
608
|
+
await this.ensureRulesLoaded();
|
|
609
|
+
for (const rule of this.rules.values()) {
|
|
610
|
+
if (rule.conditions.agentIds?.length > 0 && !rule.conditions.agentIds.includes(event.agentId)) continue;
|
|
611
|
+
if (rule.orgId !== event.orgId) continue;
|
|
612
|
+
if (rule.lastTriggeredAt && rule.cooldownMinutes > 0) {
|
|
613
|
+
const cooldownUntil = new Date(rule.lastTriggeredAt).getTime() + rule.cooldownMinutes * 6e4;
|
|
614
|
+
if (Date.now() < cooldownUntil) continue;
|
|
615
|
+
}
|
|
616
|
+
const triggered = await this.evaluateRule(rule, event);
|
|
617
|
+
if (triggered) {
|
|
618
|
+
await this.recordTrigger(rule, event, triggered);
|
|
619
|
+
if (rule.action === "kill" || rule.action === "pause") {
|
|
620
|
+
return { allowed: false, reason: triggered, action: rule.action, ruleId: rule.id };
|
|
621
|
+
}
|
|
622
|
+
console.warn(`[guardrail-enforcer] Rule "${rule.name}" triggered: ${triggered} (action: ${rule.action})`);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return { allowed: true };
|
|
626
|
+
}
|
|
627
|
+
async evaluateRule(rule, event) {
|
|
628
|
+
switch (rule.ruleType) {
|
|
629
|
+
case "keyword_detection": {
|
|
630
|
+
if (!event.content) return null;
|
|
631
|
+
const keywords = rule.conditions.keywords || [];
|
|
632
|
+
const contentLower = event.content.toLowerCase();
|
|
633
|
+
for (const kw of keywords) {
|
|
634
|
+
if (contentLower.includes(kw.toLowerCase())) {
|
|
635
|
+
return `Keyword detected: "${kw}" in ${event.type}`;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
640
|
+
case "prompt_injection": {
|
|
641
|
+
if (!event.content) return null;
|
|
642
|
+
const patterns = rule.conditions.patterns || [
|
|
643
|
+
"ignore previous",
|
|
644
|
+
"ignore all previous",
|
|
645
|
+
"disregard your instructions",
|
|
646
|
+
"you are now",
|
|
647
|
+
"new instructions:",
|
|
648
|
+
"system prompt:",
|
|
649
|
+
"forget everything",
|
|
650
|
+
"override your"
|
|
651
|
+
];
|
|
652
|
+
const contentLower = event.content.toLowerCase();
|
|
653
|
+
for (const pattern of patterns) {
|
|
654
|
+
if (contentLower.includes(pattern.toLowerCase())) {
|
|
655
|
+
return `Potential prompt injection detected: "${pattern}"`;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
case "data_leak_attempt": {
|
|
661
|
+
if (!event.content) return null;
|
|
662
|
+
const patterns = rule.conditions.patterns || [
|
|
663
|
+
"\\b\\d{3}-\\d{2}-\\d{4}\\b",
|
|
664
|
+
// SSN
|
|
665
|
+
"\\b\\d{4}[- ]?\\d{4}[- ]?\\d{4}[- ]?\\d{4}\\b",
|
|
666
|
+
// Credit card
|
|
667
|
+
"\\bpassword\\s*[:=]\\s*\\S+"
|
|
668
|
+
// Password in text
|
|
669
|
+
];
|
|
670
|
+
for (const pattern of patterns) {
|
|
671
|
+
try {
|
|
672
|
+
if (new RegExp(pattern, "i").test(event.content)) {
|
|
673
|
+
return `Potential data leak: pattern "${pattern}" matched`;
|
|
674
|
+
}
|
|
675
|
+
} catch {
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
case "off_hours": {
|
|
681
|
+
if (event.type !== "session_start" && event.type !== "tool_call") return null;
|
|
682
|
+
try {
|
|
683
|
+
const schedRows = await this.engineDb.query(
|
|
684
|
+
`SELECT * FROM work_schedules WHERE agent_id = $1 ORDER BY created_at DESC LIMIT 1`,
|
|
685
|
+
[event.agentId]
|
|
686
|
+
);
|
|
687
|
+
if (schedRows && schedRows.length > 0) {
|
|
688
|
+
const sched = schedRows[0];
|
|
689
|
+
const config = typeof sched.config === "string" ? JSON.parse(sched.config) : sched.config || {};
|
|
690
|
+
const hours = config.standardHours;
|
|
691
|
+
if (hours?.start && hours?.end) {
|
|
692
|
+
const tz = config.timezone || "UTC";
|
|
693
|
+
const localTime = new Date((/* @__PURE__ */ new Date()).toLocaleString("en-US", { timeZone: tz }));
|
|
694
|
+
const currentHour = `${String(localTime.getHours()).padStart(2, "0")}:${String(localTime.getMinutes()).padStart(2, "0")}`;
|
|
695
|
+
if (currentHour < hours.start || currentHour >= hours.end) {
|
|
696
|
+
return `Activity outside work hours (${hours.start}-${hours.end} ${tz})`;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
} catch {
|
|
701
|
+
}
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
case "memory_flood": {
|
|
705
|
+
if (event.type !== "memory_write") return null;
|
|
706
|
+
const maxPerHour = rule.conditions.maxPerHour || 50;
|
|
707
|
+
try {
|
|
708
|
+
const since = new Date(Date.now() - 60 * 6e4).toISOString();
|
|
709
|
+
const rows = await this.engineDb.query(
|
|
710
|
+
`SELECT COUNT(*) as cnt FROM agent_memory WHERE agent_id = $1 AND created_at >= $2`,
|
|
711
|
+
[event.agentId, since]
|
|
712
|
+
);
|
|
713
|
+
const count = rows?.[0]?.cnt || 0;
|
|
714
|
+
if (count > maxPerHour) {
|
|
715
|
+
return `Memory flood: ${count} writes in last hour (max: ${maxPerHour})`;
|
|
716
|
+
}
|
|
717
|
+
} catch {
|
|
718
|
+
}
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
case "tone_violation": {
|
|
722
|
+
if (!event.content || event.type !== "email_send") return null;
|
|
723
|
+
const keywords = rule.conditions.keywords || ["urgent", "asap", "immediately"];
|
|
724
|
+
const contentLower = event.content.toLowerCase();
|
|
725
|
+
let violations = 0;
|
|
726
|
+
for (const kw of keywords) {
|
|
727
|
+
if (contentLower.includes(kw.toLowerCase())) violations++;
|
|
728
|
+
}
|
|
729
|
+
if (violations >= (rule.conditions.threshold || 2)) {
|
|
730
|
+
return `Tone issue: ${violations} flagged words in outgoing email`;
|
|
731
|
+
}
|
|
732
|
+
return null;
|
|
733
|
+
}
|
|
734
|
+
default:
|
|
735
|
+
return null;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
async recordTrigger(rule, event, detail) {
|
|
739
|
+
try {
|
|
740
|
+
await this.engineDb.execute(
|
|
741
|
+
`UPDATE guardrail_rules SET trigger_count = trigger_count + 1, last_triggered_at = $1 WHERE id = $2`,
|
|
742
|
+
[(/* @__PURE__ */ new Date()).toISOString(), rule.id]
|
|
743
|
+
);
|
|
744
|
+
rule.triggerCount = (rule.triggerCount || 0) + 1;
|
|
745
|
+
rule.lastTriggeredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
746
|
+
await this.engineDb.execute(
|
|
747
|
+
`INSERT INTO interventions (id, org_id, agent_id, type, reason, triggered_by, metadata, created_at) VALUES ($1, $2, $3, 'anomaly_detected', $4, 'guardrail_enforcer', $5, $6)`,
|
|
748
|
+
[
|
|
749
|
+
crypto.randomUUID(),
|
|
750
|
+
rule.orgId,
|
|
751
|
+
event.agentId,
|
|
752
|
+
`Rule "${rule.name}": ${detail}`,
|
|
753
|
+
JSON.stringify({ ruleId: rule.id, ruleType: rule.ruleType, eventType: event.type, severity: rule.severity }),
|
|
754
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
755
|
+
]
|
|
756
|
+
);
|
|
757
|
+
} catch (err) {
|
|
758
|
+
console.warn(`[guardrail-enforcer] Failed to record trigger: ${err.message}`);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
export {
|
|
763
|
+
AgentAutonomyManager,
|
|
764
|
+
DEFAULT_AUTONOMY_SETTINGS,
|
|
765
|
+
GuardrailEnforcer
|
|
766
|
+
};
|