@agenticmail/enterprise 0.5.378 → 0.5.379
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-VKF257FY.js +510 -0
- package/dist/{agent-tools-GJUIPPHV.js → agent-tools-2XUSIG4S.js} +2 -2
- package/dist/{chunk-VPWGFA5K.js → chunk-ACP7QSXU.js} +12 -12
- package/dist/{chunk-4WLIQQ5L.js → chunk-BUKTZ35L.js} +7 -2
- package/dist/{chunk-BC4VT5GK.js → chunk-CCYSUKRG.js} +19 -19
- package/dist/{chunk-OPPT7QRL.js → chunk-N5OJNR25.js} +2 -2
- package/dist/{chunk-EX6FQSEV.js → chunk-REAJCMQE.js} +9 -2
- package/dist/{chunk-MI4NMRKF.js → chunk-VB32Q4SH.js} +14 -14
- package/dist/cli-agent-3MGMIL4H.js +2499 -0
- package/dist/cli-serve-H5LPD45H.js +286 -0
- package/dist/cli.js +3 -3
- package/dist/index.js +4 -4
- package/dist/routes-K2GAVUXI.js +92 -0
- package/dist/runtime-5OKJ5DT6.js +45 -0
- package/dist/server-OYPIKSG2.js +28 -0
- package/dist/setup-T2U77CEY.js +20 -0
- package/dist/telegram-NG7PNNIN.js +17 -0
- package/dist/whatsapp-RAQUV6ZL.js +31 -0
- package/package.json +1 -1
|
@@ -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-K2GAVUXI.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
|
+
};
|
|
@@ -28,11 +28,11 @@ import {
|
|
|
28
28
|
import {
|
|
29
29
|
createTelegramTools,
|
|
30
30
|
init_telegram
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-REAJCMQE.js";
|
|
32
32
|
import {
|
|
33
33
|
createWhatsAppTools,
|
|
34
34
|
init_whatsapp
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-BUKTZ35L.js";
|
|
36
36
|
import {
|
|
37
37
|
MemorySearchIndex,
|
|
38
38
|
init_text_search
|
|
@@ -7103,7 +7103,7 @@ function createAgentRoutes(opts) {
|
|
|
7103
7103
|
}
|
|
7104
7104
|
const managedAgent = await lifecycle2.createAgent(orgId, config, actor);
|
|
7105
7105
|
try {
|
|
7106
|
-
const { knowledgeBase: kbEngine } = await import("./routes-
|
|
7106
|
+
const { knowledgeBase: kbEngine } = await import("./routes-K2GAVUXI.js");
|
|
7107
7107
|
const allKbs = kbEngine.getAllKnowledgeBases();
|
|
7108
7108
|
const clientOrgId = managedAgent?.clientOrgId || config?.clientOrgId || null;
|
|
7109
7109
|
let kbAssigned = 0;
|
|
@@ -8862,7 +8862,7 @@ function createAgentRoutes(opts) {
|
|
|
8862
8862
|
try {
|
|
8863
8863
|
const body = await c.req.json().catch(() => ({}));
|
|
8864
8864
|
const mode = body.mode || "";
|
|
8865
|
-
const { createWhatsAppTools } = await import("./whatsapp-
|
|
8865
|
+
const { createWhatsAppTools } = await import("./whatsapp-RAQUV6ZL.js");
|
|
8866
8866
|
const dataDir = process.env.DATA_DIR || "/tmp/agenticmail-data";
|
|
8867
8867
|
const connId = mode === "business" ? `biz-${agentId}` : agentId;
|
|
8868
8868
|
const connDir = mode === "business" ? `${dataDir}/agents/${agentId}/whatsapp-business` : `${dataDir}/agents/${agentId}/whatsapp`;
|
|
@@ -8879,7 +8879,7 @@ function createAgentRoutes(opts) {
|
|
|
8879
8879
|
const agentId = c.req.param("id");
|
|
8880
8880
|
const mode = c.req.query("mode") || "";
|
|
8881
8881
|
try {
|
|
8882
|
-
const { getConnectionStatus } = await import("./whatsapp-
|
|
8882
|
+
const { getConnectionStatus } = await import("./whatsapp-RAQUV6ZL.js");
|
|
8883
8883
|
const connId = mode === "business" ? `biz-${agentId}` : agentId;
|
|
8884
8884
|
return c.json(getConnectionStatus(connId));
|
|
8885
8885
|
} catch {
|
|
@@ -8892,7 +8892,7 @@ function createAgentRoutes(opts) {
|
|
|
8892
8892
|
const body = await c.req.json().catch(() => ({}));
|
|
8893
8893
|
const mode = body.mode || "";
|
|
8894
8894
|
const connId = mode === "business" ? `biz-${agentId}` : agentId;
|
|
8895
|
-
const { sendTestMessage } = await import("./whatsapp-
|
|
8895
|
+
const { sendTestMessage } = await import("./whatsapp-RAQUV6ZL.js");
|
|
8896
8896
|
const result = await sendTestMessage(connId, body.to);
|
|
8897
8897
|
return c.json(result);
|
|
8898
8898
|
} catch (err) {
|
|
@@ -8903,7 +8903,7 @@ function createAgentRoutes(opts) {
|
|
|
8903
8903
|
const agentId = c.req.param("id");
|
|
8904
8904
|
try {
|
|
8905
8905
|
const body = await c.req.json();
|
|
8906
|
-
const { getConnection } = await import("./whatsapp-
|
|
8906
|
+
const { getConnection } = await import("./whatsapp-RAQUV6ZL.js");
|
|
8907
8907
|
const conn = getConnection(agentId);
|
|
8908
8908
|
if (!conn?.connected) return c.json({ error: "Not connected" }, 503);
|
|
8909
8909
|
const toJid = (to) => to?.includes("@") ? to : (to || "").replace(/[^0-9]/g, "") + "@s.whatsapp.net";
|
|
@@ -8947,7 +8947,7 @@ function createAgentRoutes(opts) {
|
|
|
8947
8947
|
try {
|
|
8948
8948
|
const body = await c.req.json().catch(() => ({}));
|
|
8949
8949
|
const mode = body.mode || "";
|
|
8950
|
-
const { createWhatsAppTools } = await import("./whatsapp-
|
|
8950
|
+
const { createWhatsAppTools } = await import("./whatsapp-RAQUV6ZL.js");
|
|
8951
8951
|
const dataDir = process.env.DATA_DIR || "/tmp/agenticmail-data";
|
|
8952
8952
|
const connId = mode === "business" ? `biz-${agentId}` : agentId;
|
|
8953
8953
|
const connDir = mode === "business" ? `${dataDir}/agents/${agentId}/whatsapp-business` : `${dataDir}/agents/${agentId}/whatsapp`;
|
|
@@ -22917,7 +22917,7 @@ var init_messaging_poller = __esm({
|
|
|
22917
22917
|
await this.startWhatsApp(ep);
|
|
22918
22918
|
}
|
|
22919
22919
|
try {
|
|
22920
|
-
var { autoStartConnections } = await import("./whatsapp-
|
|
22920
|
+
var { autoStartConnections } = await import("./whatsapp-RAQUV6ZL.js");
|
|
22921
22921
|
await autoStartConnections(agentEndpoints.map((a) => ({ id: a.id, dataDir: a.dataDir })));
|
|
22922
22922
|
} catch (err) {
|
|
22923
22923
|
console.error("[messaging] WhatsApp auto-start failed:", err.message);
|
|
@@ -22995,7 +22995,7 @@ var init_messaging_poller = __esm({
|
|
|
22995
22995
|
// ─── WhatsApp (event-driven — Baileys WebSocket) ──
|
|
22996
22996
|
async startWhatsApp(agent) {
|
|
22997
22997
|
try {
|
|
22998
|
-
var { onWhatsAppMessage } = await import("./whatsapp-
|
|
22998
|
+
var { onWhatsAppMessage } = await import("./whatsapp-RAQUV6ZL.js");
|
|
22999
22999
|
var unsub = onWhatsAppMessage(agent.id, (msg) => {
|
|
23000
23000
|
var waMediaFiles;
|
|
23001
23001
|
if (msg.mediaPath && msg.mediaType) {
|
|
@@ -23021,7 +23021,7 @@ var init_messaging_poller = __esm({
|
|
|
23021
23021
|
}
|
|
23022
23022
|
// ─── Telegram (webhook preferred, polling fallback) ─
|
|
23023
23023
|
async startTelegram(botToken, agent) {
|
|
23024
|
-
var { setTelegramWebhook, deleteTelegramWebhook, getTelegramWebhookInfo } = await import("./telegram-
|
|
23024
|
+
var { setTelegramWebhook, deleteTelegramWebhook, getTelegramWebhookInfo } = await import("./telegram-NG7PNNIN.js");
|
|
23025
23025
|
var { randomBytes: randomBytes2 } = await import("crypto");
|
|
23026
23026
|
var webhookSecret = randomBytes2(32).toString("hex");
|
|
23027
23027
|
if (this.config.publicUrl && this.config.app) {
|
|
@@ -23092,7 +23092,7 @@ var init_messaging_poller = __esm({
|
|
|
23092
23092
|
offset = await loadOffset();
|
|
23093
23093
|
while (running && this.running) {
|
|
23094
23094
|
try {
|
|
23095
|
-
var { pollTelegramUpdates } = await import("./telegram-
|
|
23095
|
+
var { pollTelegramUpdates } = await import("./telegram-NG7PNNIN.js");
|
|
23096
23096
|
var { updates, nextOffset } = await pollTelegramUpdates(botToken, offset, 25);
|
|
23097
23097
|
if (nextOffset > offset) {
|
|
23098
23098
|
offset = nextOffset;
|
|
@@ -23468,7 +23468,7 @@ ${desc}` : desc;
|
|
|
23468
23468
|
var typingJson = await typingResp.json();
|
|
23469
23469
|
console.log(`[messaging] Telegram typing sent to ${chatId}: ${JSON.stringify(typingJson)}`);
|
|
23470
23470
|
} else if (ctx.source === "whatsapp") {
|
|
23471
|
-
var { getConnection } = await import("./whatsapp-
|
|
23471
|
+
var { getConnection } = await import("./whatsapp-RAQUV6ZL.js");
|
|
23472
23472
|
var conn = getConnection(agent.id);
|
|
23473
23473
|
if (!conn?.connected) return;
|
|
23474
23474
|
var jid = ctx.senderId.includes("@") ? ctx.senderId : ctx.senderId.replace(/[^0-9]/g, "") + "@s.whatsapp.net";
|
|
@@ -23483,7 +23483,7 @@ ${desc}` : desc;
|
|
|
23483
23483
|
async sendDirectReply(agent, ctx, text) {
|
|
23484
23484
|
try {
|
|
23485
23485
|
if (ctx.source === "whatsapp") {
|
|
23486
|
-
var { getConnection } = await import("./whatsapp-
|
|
23486
|
+
var { getConnection } = await import("./whatsapp-RAQUV6ZL.js");
|
|
23487
23487
|
var conn = getConnection(agent.id);
|
|
23488
23488
|
if (!conn?.connected) return;
|
|
23489
23489
|
var jid = ctx.senderId.includes("@") ? ctx.senderId : ctx.senderId.replace(/[^0-9]/g, "") + "@s.whatsapp.net";
|
|
@@ -6,6 +6,10 @@ import {
|
|
|
6
6
|
import { join } from "path";
|
|
7
7
|
import { mkdir, readFile, stat } from "fs/promises";
|
|
8
8
|
import { EventEmitter } from "events";
|
|
9
|
+
function stripMarkdown(text) {
|
|
10
|
+
if (!text) return text;
|
|
11
|
+
return text.replace(/\*\*(.+?)\*\*/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/__(.+?)__/g, "$1").replace(/~~(.+?)~~/g, "$1").replace(/^#{1,6}\s+/gm, "").replace(/```[\s\S]*?```/g, (m) => m.replace(/```\w*\n?/g, "").trim()).replace(/`([^`]+)`/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").trim();
|
|
12
|
+
}
|
|
9
13
|
async function hasAuthState(config) {
|
|
10
14
|
try {
|
|
11
15
|
var authDir = join(config.dataDir, "auth");
|
|
@@ -274,11 +278,12 @@ function createWhatsAppTools(config) {
|
|
|
274
278
|
var conn = connections.get(config.agentId);
|
|
275
279
|
if (conn?.connected) {
|
|
276
280
|
var jid = toJid(input.to);
|
|
281
|
+
var cleanText = stripMarkdown(input.text);
|
|
277
282
|
try {
|
|
278
283
|
await conn?.sock?.sendPresenceUpdate("composing", jid);
|
|
279
284
|
} catch {
|
|
280
285
|
}
|
|
281
|
-
var r = await conn.sock.sendMessage(jid, { text:
|
|
286
|
+
var r = await conn.sock.sendMessage(jid, { text: cleanText });
|
|
282
287
|
try {
|
|
283
288
|
await conn?.sock?.sendPresenceUpdate("paused", jid);
|
|
284
289
|
} catch {
|
|
@@ -289,7 +294,7 @@ function createWhatsAppTools(config) {
|
|
|
289
294
|
}
|
|
290
295
|
return { ok: true, id: r?.key?.id };
|
|
291
296
|
}
|
|
292
|
-
return proxySend(config.agentId, { to: input.to, text: input.text });
|
|
297
|
+
return proxySend(config.agentId, { to: input.to, text: stripMarkdown(input.text) });
|
|
293
298
|
}
|
|
294
299
|
},
|
|
295
300
|
{
|