@agenticmail/enterprise 0.5.373 → 0.5.374
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/dist/agent-heartbeat-HZCGY3KS.js +510 -0
- package/dist/{chunk-CE4XOGCE.js → chunk-4THTH64Q.js} +2 -2
- package/dist/{chunk-JXI6PCUZ.js → chunk-CYBP2ZIB.js} +19 -19
- package/dist/chunk-LVKRN4IR.js +26082 -0
- package/dist/{chunk-DPQWI26Y.js → chunk-OOP2M3YE.js} +13 -13
- package/dist/cli-agent-CFLWIYRJ.js +2483 -0
- package/dist/cli-serve-42HTQ45A.js +281 -0
- package/dist/cli.js +3 -3
- package/dist/index.js +4 -4
- package/dist/routes-Q3YMFXX3.js +92 -0
- package/dist/runtime-WKWJ5BGT.js +45 -0
- package/dist/server-I533SQLB.js +28 -0
- package/dist/setup-4DKCCU6S.js +20 -0
- package/logs/cloudflared-error.log +12 -0
- package/package.json +1 -1
- package/dist/agent-tools-22C6IARS.js +0 -14007
- package/dist/agent-tools-AR3FPB24.js +0 -14008
- package/dist/agent-tools-QHR2LY5V.js +0 -14007
- package/dist/chunk-2EI4AYXM.js +0 -2591
- package/dist/chunk-4MFGMCJO.js +0 -1727
- package/dist/chunk-5WDYN4UV.js +0 -3332
- package/dist/chunk-F6SI2DE3.js +0 -5101
- package/dist/chunk-HIQY73V2.js +0 -467
- package/dist/chunk-IO5EPYLA.js +0 -4945
- package/dist/chunk-JNNZKFBP.js +0 -5101
- package/dist/chunk-L7OG7R6F.js +0 -1727
- package/dist/chunk-LEACSX6C.js +0 -4945
- package/dist/chunk-MUH6AHIS.js +0 -4945
- package/dist/chunk-OZW6NYWD.js +0 -5101
- package/dist/chunk-QHBOQ2IG.js +0 -1727
- package/dist/chunk-SXDQEUHA.js +0 -1727
- package/dist/chunk-Z3I6GNTS.js +0 -106
- package/dist/chunk-ZW24HPRG.js +0 -5101
- package/dist/local-OXFH4V7E.js +0 -1144
- package/dist/local-QQ2V7CYC.js +0 -1190
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
import "./chunk-KFQGP6VL.js";
|
|
2
|
+
|
|
3
|
+
// src/engine/agent-heartbeat.ts
|
|
4
|
+
var DEFAULT_SETTINGS = {
|
|
5
|
+
enabled: true,
|
|
6
|
+
baseIntervalMs: 5 * 6e4,
|
|
7
|
+
// 5 minutes
|
|
8
|
+
maxIntervalMs: 30 * 6e4,
|
|
9
|
+
// 30 minutes
|
|
10
|
+
dampingFactor: 0.5,
|
|
11
|
+
maxBatchSize: 5,
|
|
12
|
+
quietHoursStart: 23,
|
|
13
|
+
quietHoursEnd: 8
|
|
14
|
+
};
|
|
15
|
+
function createUnreadEmailCheck() {
|
|
16
|
+
return {
|
|
17
|
+
id: "unread_emails",
|
|
18
|
+
name: "Unread Emails",
|
|
19
|
+
intervalMs: 10 * 6e4,
|
|
20
|
+
// 10 minutes
|
|
21
|
+
priority: "high",
|
|
22
|
+
requiresClockIn: false,
|
|
23
|
+
// emails can be urgent outside hours
|
|
24
|
+
consecutiveNoOps: 0,
|
|
25
|
+
enabled: true,
|
|
26
|
+
check: async (ctx) => {
|
|
27
|
+
try {
|
|
28
|
+
const rows = await ctx.db.query(
|
|
29
|
+
`SELECT COUNT(*) as cnt FROM agent_memory
|
|
30
|
+
WHERE agent_id = $1
|
|
31
|
+
AND category = 'processed_email'
|
|
32
|
+
AND created_at > NOW() - INTERVAL '4 hours'`,
|
|
33
|
+
[ctx.agentId]
|
|
34
|
+
);
|
|
35
|
+
const processedRecently = parseInt(rows?.[0]?.cnt || "0");
|
|
36
|
+
const lastProcessed = await ctx.db.query(
|
|
37
|
+
`SELECT MAX(created_at) as last_at FROM agent_memory
|
|
38
|
+
WHERE agent_id = $1 AND category = 'processed_email'`,
|
|
39
|
+
[ctx.agentId]
|
|
40
|
+
);
|
|
41
|
+
const lastAt = lastProcessed?.[0]?.last_at;
|
|
42
|
+
if (ctx.isWorkHours && processedRecently === 0 && ctx.hour >= 10) {
|
|
43
|
+
return {
|
|
44
|
+
needsAction: true,
|
|
45
|
+
summary: "No emails processed in the last 4 hours during work hours. Check inbox.",
|
|
46
|
+
priority: "medium",
|
|
47
|
+
data: { lastProcessedAt: lastAt }
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return { needsAction: false, priority: "low" };
|
|
51
|
+
} catch {
|
|
52
|
+
return { needsAction: false, priority: "low" };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function createUpcomingEventsCheck() {
|
|
58
|
+
return {
|
|
59
|
+
id: "upcoming_events",
|
|
60
|
+
name: "Upcoming Calendar Events",
|
|
61
|
+
intervalMs: 15 * 6e4,
|
|
62
|
+
// 15 minutes
|
|
63
|
+
priority: "high",
|
|
64
|
+
requiresClockIn: false,
|
|
65
|
+
consecutiveNoOps: 0,
|
|
66
|
+
enabled: true,
|
|
67
|
+
check: async (ctx) => {
|
|
68
|
+
try {
|
|
69
|
+
const _twoHoursFromNow = new Date(ctx.now.getTime() + 2 * 60 * 60 * 1e3).toISOString();
|
|
70
|
+
const rows = await ctx.db.query(
|
|
71
|
+
`SELECT content FROM agent_memory
|
|
72
|
+
WHERE agent_id = $1
|
|
73
|
+
AND category = 'context'
|
|
74
|
+
AND content LIKE '%meeting%'
|
|
75
|
+
AND created_at > NOW() - INTERVAL '24 hours'
|
|
76
|
+
ORDER BY created_at DESC LIMIT 5`,
|
|
77
|
+
[ctx.agentId]
|
|
78
|
+
);
|
|
79
|
+
const upcomingMeetings = (rows || []).filter((r) => {
|
|
80
|
+
const content = r.content || "";
|
|
81
|
+
return content.includes("meeting") || content.includes("event");
|
|
82
|
+
});
|
|
83
|
+
if (upcomingMeetings.length > 0) {
|
|
84
|
+
return {
|
|
85
|
+
needsAction: true,
|
|
86
|
+
summary: `${upcomingMeetings.length} potential upcoming event(s). Agent should check Google Calendar.`,
|
|
87
|
+
priority: "high",
|
|
88
|
+
data: { count: upcomingMeetings.length }
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return { needsAction: false, priority: "low" };
|
|
92
|
+
} catch {
|
|
93
|
+
return { needsAction: false, priority: "low" };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function createStaleSessionsCheck() {
|
|
99
|
+
return {
|
|
100
|
+
id: "stale_sessions",
|
|
101
|
+
name: "Stale Sessions",
|
|
102
|
+
intervalMs: 30 * 6e4,
|
|
103
|
+
// 30 minutes
|
|
104
|
+
priority: "medium",
|
|
105
|
+
requiresClockIn: true,
|
|
106
|
+
consecutiveNoOps: 0,
|
|
107
|
+
enabled: true,
|
|
108
|
+
check: async (ctx) => {
|
|
109
|
+
try {
|
|
110
|
+
const rows = await ctx.db.query(
|
|
111
|
+
`SELECT COUNT(*) as cnt FROM agent_sessions
|
|
112
|
+
WHERE agent_id = $1
|
|
113
|
+
AND status = 'active'
|
|
114
|
+
AND updated_at < NOW() - INTERVAL '2 hours'`,
|
|
115
|
+
[ctx.agentId]
|
|
116
|
+
);
|
|
117
|
+
const staleCount = parseInt(rows?.[0]?.cnt || "0");
|
|
118
|
+
if (staleCount > 0) {
|
|
119
|
+
return {
|
|
120
|
+
needsAction: true,
|
|
121
|
+
summary: `${staleCount} stale session(s) detected (active > 2 hours with no updates). May need cleanup.`,
|
|
122
|
+
priority: "medium",
|
|
123
|
+
data: { staleCount }
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return { needsAction: false, priority: "low" };
|
|
127
|
+
} catch {
|
|
128
|
+
return { needsAction: false, priority: "low" };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function createMemoryHealthCheck() {
|
|
134
|
+
return {
|
|
135
|
+
id: "memory_health",
|
|
136
|
+
name: "Memory Health",
|
|
137
|
+
intervalMs: 60 * 6e4,
|
|
138
|
+
// 1 hour
|
|
139
|
+
priority: "low",
|
|
140
|
+
requiresClockIn: true,
|
|
141
|
+
consecutiveNoOps: 0,
|
|
142
|
+
enabled: true,
|
|
143
|
+
check: async (ctx) => {
|
|
144
|
+
try {
|
|
145
|
+
const rows = await ctx.db.query(
|
|
146
|
+
`SELECT COUNT(*) as cnt FROM agent_memory
|
|
147
|
+
WHERE agent_id = $1 AND created_at > NOW() - INTERVAL '24 hours'`,
|
|
148
|
+
[ctx.agentId]
|
|
149
|
+
);
|
|
150
|
+
const recentCount = parseInt(rows?.[0]?.cnt || "0");
|
|
151
|
+
if (recentCount > 200) {
|
|
152
|
+
return {
|
|
153
|
+
needsAction: true,
|
|
154
|
+
summary: `Memory flood detected: ${recentCount} memories in 24h (threshold: 200). Consider pruning.`,
|
|
155
|
+
priority: "medium",
|
|
156
|
+
data: { recentCount }
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return { needsAction: false, priority: "low" };
|
|
160
|
+
} catch {
|
|
161
|
+
return { needsAction: false, priority: "low" };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function createUnansweredChatCheck() {
|
|
167
|
+
return {
|
|
168
|
+
id: "unanswered_chat",
|
|
169
|
+
name: "Unanswered Chat Messages",
|
|
170
|
+
intervalMs: 5 * 6e4,
|
|
171
|
+
// 5 minutes
|
|
172
|
+
priority: "high",
|
|
173
|
+
requiresClockIn: false,
|
|
174
|
+
consecutiveNoOps: 0,
|
|
175
|
+
enabled: true,
|
|
176
|
+
check: async (ctx) => {
|
|
177
|
+
try {
|
|
178
|
+
const rows = await ctx.db.query(
|
|
179
|
+
`SELECT COUNT(*) as cnt FROM agent_sessions
|
|
180
|
+
WHERE agent_id = $1
|
|
181
|
+
AND status = 'failed'
|
|
182
|
+
AND metadata::text LIKE '%chat%'
|
|
183
|
+
AND created_at > NOW() - INTERVAL '1 hour'`,
|
|
184
|
+
[ctx.agentId]
|
|
185
|
+
);
|
|
186
|
+
const failedChats = parseInt(rows?.[0]?.cnt || "0");
|
|
187
|
+
if (failedChats > 0) {
|
|
188
|
+
return {
|
|
189
|
+
needsAction: true,
|
|
190
|
+
summary: `${failedChats} failed chat session(s) in the last hour. Messages may be unanswered.`,
|
|
191
|
+
priority: "urgent",
|
|
192
|
+
data: { failedChats }
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
return { needsAction: false, priority: "low" };
|
|
196
|
+
} catch {
|
|
197
|
+
return { needsAction: false, priority: "low" };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function createTaskDeadlineCheck() {
|
|
203
|
+
return {
|
|
204
|
+
id: "task_deadlines",
|
|
205
|
+
name: "Task Deadlines",
|
|
206
|
+
intervalMs: 60 * 6e4,
|
|
207
|
+
// 1 hour
|
|
208
|
+
priority: "medium",
|
|
209
|
+
requiresClockIn: true,
|
|
210
|
+
consecutiveNoOps: 0,
|
|
211
|
+
enabled: true,
|
|
212
|
+
check: async (ctx) => {
|
|
213
|
+
try {
|
|
214
|
+
const rows = await ctx.db.query(
|
|
215
|
+
`SELECT COUNT(*) as cnt FROM agent_memory
|
|
216
|
+
WHERE agent_id = $1
|
|
217
|
+
AND category = 'context'
|
|
218
|
+
AND importance = 'high'
|
|
219
|
+
AND content LIKE '%deadline%'
|
|
220
|
+
AND created_at > NOW() - INTERVAL '48 hours'`,
|
|
221
|
+
[ctx.agentId]
|
|
222
|
+
);
|
|
223
|
+
const urgentTasks = parseInt(rows?.[0]?.cnt || "0");
|
|
224
|
+
if (urgentTasks > 0) {
|
|
225
|
+
return {
|
|
226
|
+
needsAction: true,
|
|
227
|
+
summary: `${urgentTasks} task(s) with approaching deadlines. Agent should review and prioritize.`,
|
|
228
|
+
priority: "high",
|
|
229
|
+
data: { urgentTasks }
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
return { needsAction: false, priority: "low" };
|
|
233
|
+
} catch {
|
|
234
|
+
return { needsAction: false, priority: "low" };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function createErrorRateCheck() {
|
|
240
|
+
return {
|
|
241
|
+
id: "error_rate",
|
|
242
|
+
name: "Error Rate Monitor",
|
|
243
|
+
intervalMs: 15 * 6e4,
|
|
244
|
+
// 15 minutes
|
|
245
|
+
priority: "high",
|
|
246
|
+
requiresClockIn: false,
|
|
247
|
+
// errors can happen anytime
|
|
248
|
+
consecutiveNoOps: 0,
|
|
249
|
+
enabled: true,
|
|
250
|
+
check: async (ctx) => {
|
|
251
|
+
try {
|
|
252
|
+
const totalRows = await ctx.db.query(
|
|
253
|
+
`SELECT COUNT(*) as cnt FROM agent_sessions
|
|
254
|
+
WHERE agent_id = $1 AND created_at > NOW() - INTERVAL '1 hour'`,
|
|
255
|
+
[ctx.agentId]
|
|
256
|
+
);
|
|
257
|
+
const failedRows = await ctx.db.query(
|
|
258
|
+
`SELECT COUNT(*) as cnt FROM agent_sessions
|
|
259
|
+
WHERE agent_id = $1 AND status = 'failed' AND created_at > NOW() - INTERVAL '1 hour'`,
|
|
260
|
+
[ctx.agentId]
|
|
261
|
+
);
|
|
262
|
+
const total = parseInt(totalRows?.[0]?.cnt || "0");
|
|
263
|
+
const failed = parseInt(failedRows?.[0]?.cnt || "0");
|
|
264
|
+
if (total >= 3 && failed / total > 0.5) {
|
|
265
|
+
return {
|
|
266
|
+
needsAction: true,
|
|
267
|
+
summary: `High error rate: ${failed}/${total} sessions failed in the last hour (${Math.round(failed / total * 100)}%). Possible infrastructure issue.`,
|
|
268
|
+
priority: "urgent",
|
|
269
|
+
data: { total, failed, rate: failed / total }
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
return { needsAction: false, priority: "low" };
|
|
273
|
+
} catch {
|
|
274
|
+
return { needsAction: false, priority: "low" };
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
var AgentHeartbeatManager = class {
|
|
280
|
+
config;
|
|
281
|
+
settings;
|
|
282
|
+
checks = /* @__PURE__ */ new Map();
|
|
283
|
+
tickTimer = null;
|
|
284
|
+
globalConsecutiveNoOps = 0;
|
|
285
|
+
lastActionTimestamp = 0;
|
|
286
|
+
stats = {
|
|
287
|
+
totalTicks: 0,
|
|
288
|
+
totalChecksRun: 0,
|
|
289
|
+
totalActionsTriggered: 0,
|
|
290
|
+
totalTokensSaved: 0,
|
|
291
|
+
// estimated tokens NOT spent due to no-op ticks
|
|
292
|
+
startedAt: Date.now()
|
|
293
|
+
};
|
|
294
|
+
constructor(config, settings) {
|
|
295
|
+
this.config = config;
|
|
296
|
+
this.settings = { ...DEFAULT_SETTINGS, ...settings };
|
|
297
|
+
this.registerCheck(createUnreadEmailCheck());
|
|
298
|
+
this.registerCheck(createUpcomingEventsCheck());
|
|
299
|
+
this.registerCheck(createStaleSessionsCheck());
|
|
300
|
+
this.registerCheck(createMemoryHealthCheck());
|
|
301
|
+
this.registerCheck(createUnansweredChatCheck());
|
|
302
|
+
this.registerCheck(createTaskDeadlineCheck());
|
|
303
|
+
this.registerCheck(createErrorRateCheck());
|
|
304
|
+
if (config.enabledChecks) {
|
|
305
|
+
for (const [id, enabled] of Object.entries(config.enabledChecks)) {
|
|
306
|
+
const check = this.checks.get(id);
|
|
307
|
+
if (check) check.enabled = enabled;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
registerCheck(check) {
|
|
312
|
+
this.checks.set(check.id, check);
|
|
313
|
+
}
|
|
314
|
+
async start() {
|
|
315
|
+
if (!this.settings.enabled) {
|
|
316
|
+
console.log("[heartbeat] Disabled, skipping");
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
console.log(`[heartbeat] Starting with ${this.checks.size} checks, base interval ${this.settings.baseIntervalMs / 1e3}s`);
|
|
320
|
+
setTimeout(() => this.tick(), 6e4);
|
|
321
|
+
this.scheduleNextTick();
|
|
322
|
+
}
|
|
323
|
+
stop() {
|
|
324
|
+
if (this.tickTimer) {
|
|
325
|
+
clearTimeout(this.tickTimer);
|
|
326
|
+
this.tickTimer = null;
|
|
327
|
+
}
|
|
328
|
+
console.log(`[heartbeat] Stopped. Stats: ${this.stats.totalTicks} ticks, ${this.stats.totalChecksRun} checks, ${this.stats.totalActionsTriggered} actions, ~${this.stats.totalTokensSaved} tokens saved`);
|
|
329
|
+
}
|
|
330
|
+
getStats() {
|
|
331
|
+
return {
|
|
332
|
+
...this.stats,
|
|
333
|
+
uptimeMs: Date.now() - this.stats.startedAt,
|
|
334
|
+
currentIntervalMs: this.calculateInterval(),
|
|
335
|
+
globalConsecutiveNoOps: this.globalConsecutiveNoOps,
|
|
336
|
+
checks: Array.from(this.checks.values()).map((c) => ({
|
|
337
|
+
id: c.id,
|
|
338
|
+
name: c.name,
|
|
339
|
+
enabled: c.enabled,
|
|
340
|
+
lastRunAt: c.lastRunAt,
|
|
341
|
+
lastActionAt: c.lastActionAt,
|
|
342
|
+
consecutiveNoOps: c.consecutiveNoOps
|
|
343
|
+
}))
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
// ─── Core Tick Logic ────────────────────────────────
|
|
347
|
+
async tick() {
|
|
348
|
+
this.stats.totalTicks++;
|
|
349
|
+
const now = /* @__PURE__ */ new Date();
|
|
350
|
+
const tz = this.config.timezone || "UTC";
|
|
351
|
+
const localTime = new Date(now.toLocaleString("en-US", { timeZone: tz }));
|
|
352
|
+
const hour = localTime.getHours();
|
|
353
|
+
const minute = localTime.getMinutes();
|
|
354
|
+
const dayOfWeek = localTime.getDay();
|
|
355
|
+
const isWorkHours = this.isWithinWorkHours(hour, minute, dayOfWeek);
|
|
356
|
+
const isQuietHours = this.isQuietHours(hour);
|
|
357
|
+
const isClockedIn = this.config.isClockedIn();
|
|
358
|
+
const ctx = {
|
|
359
|
+
agentId: this.config.agentId,
|
|
360
|
+
orgId: this.config.orgId,
|
|
361
|
+
agentName: this.config.agentName,
|
|
362
|
+
role: this.config.role,
|
|
363
|
+
managerEmail: this.config.managerEmail,
|
|
364
|
+
timezone: tz,
|
|
365
|
+
db: this.config.db,
|
|
366
|
+
now,
|
|
367
|
+
localTime,
|
|
368
|
+
hour,
|
|
369
|
+
minute,
|
|
370
|
+
dayOfWeek,
|
|
371
|
+
isWorkHours,
|
|
372
|
+
isClockedIn
|
|
373
|
+
};
|
|
374
|
+
const actionableItems = [];
|
|
375
|
+
for (const check of this.checks.values()) {
|
|
376
|
+
if (!check.enabled) continue;
|
|
377
|
+
if (check.requiresClockIn && !isClockedIn) continue;
|
|
378
|
+
if (isQuietHours && check.priority !== "high") continue;
|
|
379
|
+
const adaptiveInterval = Math.min(
|
|
380
|
+
check.intervalMs * (1 + this.settings.dampingFactor * check.consecutiveNoOps),
|
|
381
|
+
this.settings.maxIntervalMs
|
|
382
|
+
);
|
|
383
|
+
const timeSinceLastRun = now.getTime() - (check.lastRunAt || 0);
|
|
384
|
+
if (timeSinceLastRun < adaptiveInterval) continue;
|
|
385
|
+
this.stats.totalChecksRun++;
|
|
386
|
+
check.lastRunAt = now.getTime();
|
|
387
|
+
try {
|
|
388
|
+
const result = await check.check(ctx);
|
|
389
|
+
if (result.needsAction) {
|
|
390
|
+
actionableItems.push({ check, result });
|
|
391
|
+
check.consecutiveNoOps = 0;
|
|
392
|
+
check.lastActionAt = now.getTime();
|
|
393
|
+
} else {
|
|
394
|
+
check.consecutiveNoOps++;
|
|
395
|
+
this.stats.totalTokensSaved += 500;
|
|
396
|
+
}
|
|
397
|
+
} catch (err) {
|
|
398
|
+
console.warn(`[heartbeat] Check ${check.id} error: ${err.message}`);
|
|
399
|
+
check.consecutiveNoOps++;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
if (actionableItems.length > 0) {
|
|
403
|
+
this.globalConsecutiveNoOps = 0;
|
|
404
|
+
this.lastActionTimestamp = now.getTime();
|
|
405
|
+
this.stats.totalActionsTriggered++;
|
|
406
|
+
const priorityOrder = { urgent: 0, high: 1, medium: 2, low: 3 };
|
|
407
|
+
actionableItems.sort((a, b) => priorityOrder[a.result.priority] - priorityOrder[b.result.priority]);
|
|
408
|
+
const batch = actionableItems.slice(0, this.settings.maxBatchSize);
|
|
409
|
+
await this.dispatchBatch(batch, ctx);
|
|
410
|
+
} else {
|
|
411
|
+
this.globalConsecutiveNoOps++;
|
|
412
|
+
}
|
|
413
|
+
this.scheduleNextTick();
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Calculate next tick interval using damped oscillator model:
|
|
417
|
+
* interval = base * (1 + damping * consecutiveNoOps)
|
|
418
|
+
*
|
|
419
|
+
* With fibonacci-like acceleration:
|
|
420
|
+
* After 0 no-ops: 5min (base)
|
|
421
|
+
* After 1 no-op: 7.5min
|
|
422
|
+
* After 2 no-ops: 10min
|
|
423
|
+
* After 4 no-ops: 15min
|
|
424
|
+
* After 8 no-ops: 25min
|
|
425
|
+
* After 10 no-ops: 30min (max)
|
|
426
|
+
*
|
|
427
|
+
* Resets to base immediately when action is detected.
|
|
428
|
+
*/
|
|
429
|
+
calculateInterval() {
|
|
430
|
+
const base = this.settings.baseIntervalMs;
|
|
431
|
+
const max = this.settings.maxIntervalMs;
|
|
432
|
+
const damping = this.settings.dampingFactor;
|
|
433
|
+
const noOps = this.globalConsecutiveNoOps;
|
|
434
|
+
return Math.min(base * (1 + damping * noOps), max);
|
|
435
|
+
}
|
|
436
|
+
scheduleNextTick() {
|
|
437
|
+
if (this.tickTimer) clearTimeout(this.tickTimer);
|
|
438
|
+
const interval = this.calculateInterval();
|
|
439
|
+
this.tickTimer = setTimeout(() => this.tick(), interval);
|
|
440
|
+
this.tickTimer.unref();
|
|
441
|
+
}
|
|
442
|
+
isWithinWorkHours(hour, minute, dayOfWeek) {
|
|
443
|
+
const schedule = this.config.schedule;
|
|
444
|
+
if (!schedule) return true;
|
|
445
|
+
const isWorkday = schedule.days.includes(dayOfWeek);
|
|
446
|
+
const timeStr = `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
|
|
447
|
+
const isWithinHours = timeStr >= schedule.start && timeStr < schedule.end;
|
|
448
|
+
return isWorkday && isWithinHours;
|
|
449
|
+
}
|
|
450
|
+
isQuietHours(hour) {
|
|
451
|
+
const start = this.settings.quietHoursStart;
|
|
452
|
+
const end = this.settings.quietHoursEnd;
|
|
453
|
+
if (start === void 0 || end === void 0) return false;
|
|
454
|
+
if (start > end) {
|
|
455
|
+
return hour >= start || hour < end;
|
|
456
|
+
}
|
|
457
|
+
return hour >= start && hour < end;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Dispatch a batch of actionable items to the agent via one LLM session.
|
|
461
|
+
* This is the ONLY place tokens are spent.
|
|
462
|
+
*/
|
|
463
|
+
async dispatchBatch(items, ctx) {
|
|
464
|
+
if (!this.config.runtime) {
|
|
465
|
+
console.warn("[heartbeat] No runtime \u2014 cannot dispatch actions");
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
try {
|
|
469
|
+
const { guardrails } = await import("./routes-Q3YMFXX3.js");
|
|
470
|
+
const status = await guardrails.getStatus(ctx.agentId);
|
|
471
|
+
if (status.paused || status.offDuty) {
|
|
472
|
+
console.log(`[heartbeat] Skipping action dispatch \u2014 agent is ${status.offDuty ? "off duty" : "paused"}`);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
} catch {
|
|
476
|
+
}
|
|
477
|
+
const summaries = items.map(
|
|
478
|
+
(item, i) => `${i + 1}. [${item.result.priority.toUpperCase()}] ${item.check.name}: ${item.result.summary}`
|
|
479
|
+
).join("\n");
|
|
480
|
+
const prompt = `HEARTBEAT ALERT \u2014 The following ${items.length} item(s) need your attention:
|
|
481
|
+
|
|
482
|
+
${summaries}
|
|
483
|
+
|
|
484
|
+
For each item, take the appropriate action:
|
|
485
|
+
- For unread emails: Check your inbox with gmail_search and respond to any urgent ones.
|
|
486
|
+
- For upcoming events: Check google_calendar_list for the next 2 hours and prepare.
|
|
487
|
+
- For stale sessions: Review and close any stuck sessions.
|
|
488
|
+
- For unanswered chats: Check Google Chat and respond.
|
|
489
|
+
- For error rate issues: Investigate recent failures and notify your manager if critical.
|
|
490
|
+
- For task deadlines: Review google_tasks_list and prioritize.
|
|
491
|
+
- For memory health: Consider pruning old or low-importance memories.
|
|
492
|
+
|
|
493
|
+
Be efficient \u2014 handle what you can and note what needs human intervention.
|
|
494
|
+
If something needs your manager's attention, email ${ctx.managerEmail || "your manager"}.`;
|
|
495
|
+
const systemPrompt = `You are ${ctx.agentName}, a ${ctx.role}. This is an automated heartbeat check \u2014 items flagged as needing attention. Handle them efficiently. Don't create unnecessary work \u2014 only act on what's genuinely important.`;
|
|
496
|
+
try {
|
|
497
|
+
const session = await this.config.runtime.spawnSession({
|
|
498
|
+
agentId: ctx.agentId,
|
|
499
|
+
message: prompt,
|
|
500
|
+
systemPrompt
|
|
501
|
+
});
|
|
502
|
+
console.log(`[heartbeat] \u2705 Action session ${session.id} dispatched (${items.length} items: ${items.map((i) => i.check.id).join(", ")})`);
|
|
503
|
+
} catch (err) {
|
|
504
|
+
console.error(`[heartbeat] Failed to dispatch action: ${err.message}`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
export {
|
|
509
|
+
AgentHeartbeatManager
|
|
510
|
+
};
|
|
@@ -1234,7 +1234,7 @@ async function deploy(config, db, jwtSecret, vaultKey, spinner, chalk) {
|
|
|
1234
1234
|
const { deployTarget, company, database, domain, tunnel, cloud } = config;
|
|
1235
1235
|
if (deployTarget === "cloudflare-tunnel" && tunnel) {
|
|
1236
1236
|
spinner.start(`Starting local server on port ${tunnel.port}...`);
|
|
1237
|
-
const { createServer: createServer2 } = await import("./server-
|
|
1237
|
+
const { createServer: createServer2 } = await import("./server-I533SQLB.js");
|
|
1238
1238
|
const server2 = createServer2({ port: tunnel.port, db, jwtSecret });
|
|
1239
1239
|
const handle2 = await server2.start();
|
|
1240
1240
|
spinner.succeed("Server running");
|
|
@@ -1535,7 +1535,7 @@ async function deploy(config, db, jwtSecret, vaultKey, spinner, chalk) {
|
|
|
1535
1535
|
return {};
|
|
1536
1536
|
}
|
|
1537
1537
|
spinner.start("Starting local server...");
|
|
1538
|
-
const { createServer } = await import("./server-
|
|
1538
|
+
const { createServer } = await import("./server-I533SQLB.js");
|
|
1539
1539
|
const server = createServer({ port: 3e3, db, jwtSecret });
|
|
1540
1540
|
const handle = await server.start();
|
|
1541
1541
|
spinner.succeed("Server running");
|
|
@@ -356,7 +356,7 @@ function createRuntimeHooks(deps) {
|
|
|
356
356
|
var injectedMessages = [...messages];
|
|
357
357
|
if (deps.knowledgeBaseEnabled !== false) {
|
|
358
358
|
try {
|
|
359
|
-
var { knowledgeBase } = await import("./routes-
|
|
359
|
+
var { knowledgeBase } = await import("./routes-Q3YMFXX3.js");
|
|
360
360
|
var kbs = await knowledgeBase.listForAgent(agentId);
|
|
361
361
|
if (kbs.length > 0) {
|
|
362
362
|
var contextParts = [];
|
|
@@ -389,7 +389,7 @@ ${contextParts.join("\n\n")}`;
|
|
|
389
389
|
}
|
|
390
390
|
if (deps.memoryEnabled !== false) {
|
|
391
391
|
try {
|
|
392
|
-
var { memoryManager } = await import("./routes-
|
|
392
|
+
var { memoryManager } = await import("./routes-Q3YMFXX3.js");
|
|
393
393
|
var memories = await memoryManager.queryMemories({
|
|
394
394
|
agentId,
|
|
395
395
|
limit: 10,
|
|
@@ -410,7 +410,7 @@ ${memories.map(function(m) {
|
|
|
410
410
|
}
|
|
411
411
|
if (deps.policyEnabled !== false) {
|
|
412
412
|
try {
|
|
413
|
-
var { policyEngine } = await import("./routes-
|
|
413
|
+
var { policyEngine } = await import("./routes-Q3YMFXX3.js");
|
|
414
414
|
var policies = await policyEngine.getAgentPolicies(agentId, deps.orgId);
|
|
415
415
|
if (policies.length > 0) {
|
|
416
416
|
var policyText = policies.map(function(p) {
|
|
@@ -435,7 +435,7 @@ ${policyText}`;
|
|
|
435
435
|
// ─── Budget Check ──────────────────────────────
|
|
436
436
|
async checkBudget(agentId, _orgId, _estimatedTokens) {
|
|
437
437
|
try {
|
|
438
|
-
var { lifecycle } = await import("./routes-
|
|
438
|
+
var { lifecycle } = await import("./routes-Q3YMFXX3.js");
|
|
439
439
|
var now = Date.now();
|
|
440
440
|
var cacheKey = `budget_sync_${agentId}`;
|
|
441
441
|
var lastSync = globalThis[cacheKey] || 0;
|
|
@@ -528,7 +528,7 @@ ${policyText}`;
|
|
|
528
528
|
// ─── Record LLM Usage ──────────────────────────
|
|
529
529
|
async recordLLMUsage(agentId, orgId, usage) {
|
|
530
530
|
try {
|
|
531
|
-
var { lifecycle } = await import("./routes-
|
|
531
|
+
var { lifecycle } = await import("./routes-Q3YMFXX3.js");
|
|
532
532
|
console.log(`[hooks] recordLLMUsage: agent=${agentId}, input=${usage.inputTokens}, output=${usage.outputTokens}`);
|
|
533
533
|
await lifecycle.recordLLMUsage(agentId, {
|
|
534
534
|
inputTokens: usage.inputTokens,
|
|
@@ -539,7 +539,7 @@ ${policyText}`;
|
|
|
539
539
|
console.log(`[hooks] recordLLMUsage error: ${recordErr.message}`);
|
|
540
540
|
}
|
|
541
541
|
try {
|
|
542
|
-
var { activity } = await import("./routes-
|
|
542
|
+
var { activity } = await import("./routes-Q3YMFXX3.js");
|
|
543
543
|
await activity.record({
|
|
544
544
|
agentId,
|
|
545
545
|
orgId,
|
|
@@ -587,7 +587,7 @@ ${policyText}`;
|
|
|
587
587
|
var cacheKey = `${ctx.agentId}:${ctx.toolName}`;
|
|
588
588
|
var cached = getCachedPermission(cacheKey);
|
|
589
589
|
if (cached) return cached;
|
|
590
|
-
var { permissionEngine } = await import("./routes-
|
|
590
|
+
var { permissionEngine } = await import("./routes-Q3YMFXX3.js");
|
|
591
591
|
var permResult = await permissionEngine.checkPermission(ctx.agentId, ctx.toolName);
|
|
592
592
|
var result = {
|
|
593
593
|
allowed: permResult.allowed,
|
|
@@ -596,7 +596,7 @@ ${policyText}`;
|
|
|
596
596
|
};
|
|
597
597
|
if (result.allowed && deps.guardrailsEnabled !== false) {
|
|
598
598
|
try {
|
|
599
|
-
var { guardrails } = await import("./routes-
|
|
599
|
+
var { guardrails } = await import("./routes-Q3YMFXX3.js");
|
|
600
600
|
var status = await guardrails.getStatus(ctx.agentId);
|
|
601
601
|
if (status.paused || status.offDuty) {
|
|
602
602
|
result.allowed = false;
|
|
@@ -608,7 +608,7 @@ ${policyText}`;
|
|
|
608
608
|
}
|
|
609
609
|
if (result.allowed && deps.dlpEnabled !== false && ctx.parameters) {
|
|
610
610
|
try {
|
|
611
|
-
var { dlp } = await import("./routes-
|
|
611
|
+
var { dlp } = await import("./routes-Q3YMFXX3.js");
|
|
612
612
|
var dlpResult = await dlp.scanParameters({
|
|
613
613
|
orgId: ctx.orgId,
|
|
614
614
|
agentId: ctx.agentId,
|
|
@@ -628,7 +628,7 @@ ${policyText}`;
|
|
|
628
628
|
}
|
|
629
629
|
if (result.requiresApproval && result.allowed) {
|
|
630
630
|
try {
|
|
631
|
-
var { approvals } = await import("./routes-
|
|
631
|
+
var { approvals } = await import("./routes-Q3YMFXX3.js");
|
|
632
632
|
var approval = await approvals.createAndWait({
|
|
633
633
|
agentId: ctx.agentId,
|
|
634
634
|
orgId: ctx.orgId,
|
|
@@ -658,7 +658,7 @@ ${policyText}`;
|
|
|
658
658
|
// ─── After Tool Call ────────────────────────────
|
|
659
659
|
async afterToolCall(ctx, result) {
|
|
660
660
|
try {
|
|
661
|
-
var { activity } = await import("./routes-
|
|
661
|
+
var { activity } = await import("./routes-Q3YMFXX3.js");
|
|
662
662
|
await activity.record({
|
|
663
663
|
agentId: ctx.agentId,
|
|
664
664
|
orgId: ctx.orgId,
|
|
@@ -675,7 +675,7 @@ ${policyText}`;
|
|
|
675
675
|
} catch {
|
|
676
676
|
}
|
|
677
677
|
try {
|
|
678
|
-
var { lifecycle } = await import("./routes-
|
|
678
|
+
var { lifecycle } = await import("./routes-Q3YMFXX3.js");
|
|
679
679
|
await lifecycle.recordToolCall(ctx.agentId, {
|
|
680
680
|
toolId: ctx.toolName,
|
|
681
681
|
tokensUsed: 0,
|
|
@@ -687,7 +687,7 @@ ${policyText}`;
|
|
|
687
687
|
}
|
|
688
688
|
if (result.success && EXTERNAL_TOOLS.has(ctx.toolName)) {
|
|
689
689
|
try {
|
|
690
|
-
var { journal } = await import("./routes-
|
|
690
|
+
var { journal } = await import("./routes-Q3YMFXX3.js");
|
|
691
691
|
await journal.record({
|
|
692
692
|
orgId: ctx.orgId,
|
|
693
693
|
agentId: ctx.agentId,
|
|
@@ -702,7 +702,7 @@ ${policyText}`;
|
|
|
702
702
|
}
|
|
703
703
|
if (result.success && COMMUNICATION_TOOLS.has(ctx.toolName)) {
|
|
704
704
|
try {
|
|
705
|
-
var { commBus } = await import("./routes-
|
|
705
|
+
var { commBus } = await import("./routes-Q3YMFXX3.js");
|
|
706
706
|
await commBus.observeMessage({
|
|
707
707
|
orgId: ctx.orgId,
|
|
708
708
|
agentId: ctx.agentId,
|
|
@@ -716,7 +716,7 @@ ${policyText}`;
|
|
|
716
716
|
// ─── Session Lifecycle ──────────────────────────
|
|
717
717
|
async onSessionStart(sessionId, agentId, orgId) {
|
|
718
718
|
try {
|
|
719
|
-
var { activity } = await import("./routes-
|
|
719
|
+
var { activity } = await import("./routes-Q3YMFXX3.js");
|
|
720
720
|
await activity.record({
|
|
721
721
|
agentId,
|
|
722
722
|
orgId,
|
|
@@ -729,7 +729,7 @@ ${policyText}`;
|
|
|
729
729
|
},
|
|
730
730
|
async onSessionEnd(sessionId, agentId, orgId) {
|
|
731
731
|
try {
|
|
732
|
-
var { activity } = await import("./routes-
|
|
732
|
+
var { activity } = await import("./routes-Q3YMFXX3.js");
|
|
733
733
|
await activity.record({
|
|
734
734
|
agentId,
|
|
735
735
|
orgId,
|
|
@@ -743,7 +743,7 @@ ${policyText}`;
|
|
|
743
743
|
// ─── Context Compaction ─────────────────────────
|
|
744
744
|
async onContextCompaction(sessionId, agentId, summary) {
|
|
745
745
|
try {
|
|
746
|
-
var { memoryManager } = await import("./routes-
|
|
746
|
+
var { memoryManager } = await import("./routes-Q3YMFXX3.js");
|
|
747
747
|
await memoryManager.createMemory({
|
|
748
748
|
agentId,
|
|
749
749
|
orgId: deps.orgId,
|
|
@@ -2518,7 +2518,7 @@ async function runAgentLoop(config, initialMessages, hooks, options) {
|
|
|
2518
2518
|
console.log(`[agent-loop] \u2705 Tool ${toolCall.name} succeeded (${content.length} chars): ${content.slice(0, 300)}`);
|
|
2519
2519
|
}
|
|
2520
2520
|
try {
|
|
2521
|
-
const { activity } = await import("./routes-
|
|
2521
|
+
const { activity } = await import("./routes-Q3YMFXX3.js");
|
|
2522
2522
|
activity.recordToolCallCompact({
|
|
2523
2523
|
agentId: config.agentId,
|
|
2524
2524
|
orgId: config.orgId,
|
|
@@ -3402,7 +3402,7 @@ function createRequestToolsTool(allTools, activeSets, _context) {
|
|
|
3402
3402
|
};
|
|
3403
3403
|
}
|
|
3404
3404
|
async function createToolsForContext(options, context, opts) {
|
|
3405
|
-
const { createAllTools } = await import("./agent-tools-
|
|
3405
|
+
const { createAllTools } = await import("./agent-tools-CUJHKMUN.js");
|
|
3406
3406
|
if (context === "full") {
|
|
3407
3407
|
return createAllTools(options);
|
|
3408
3408
|
}
|