@agenticmail/enterprise 0.5.441 → 0.5.442

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,305 @@
1
+ import {
2
+ __esm
3
+ } from "./chunk-KFQGP6VL.js";
4
+
5
+ // src/engine/agent-status.ts
6
+ function describeToolActivity(toolName) {
7
+ const MAP = {
8
+ gmail_search: "Searching Gmail",
9
+ gmail_read: "Reading email",
10
+ gmail_send: "Sending email",
11
+ gmail_reply: "Replying to email",
12
+ gmail_forward: "Forwarding email",
13
+ gmail_drafts: "Managing email drafts",
14
+ google_calendar_events: "Checking calendar",
15
+ google_calendar_list: "Listing calendars",
16
+ google_chat_send_message: "Sending chat message",
17
+ google_chat_list_messages: "Reading chat messages",
18
+ google_chat_list_spaces: "Browsing chat spaces",
19
+ meeting_join: "Joining a meeting",
20
+ meeting_speak: "Speaking in meeting",
21
+ meeting_prepare: "Preparing for meeting",
22
+ browser: "Browsing the web",
23
+ web_search: "Searching the web",
24
+ web_fetch: "Fetching a webpage",
25
+ memory: "Accessing memory",
26
+ memory_reflect: "Reflecting on learnings",
27
+ knowledge_base_search: "Searching knowledge base",
28
+ knowledge_hub_search: "Searching knowledge hub",
29
+ google_drive_search: "Searching Drive",
30
+ google_drive_get: "Reading a Drive file",
31
+ google_docs_create: "Creating a document",
32
+ google_sheets_read: "Reading a spreadsheet",
33
+ bash: "Running a command",
34
+ read: "Reading a file",
35
+ write: "Writing a file"
36
+ };
37
+ return MAP[toolName] || toolName.replace(/_/g, " ");
38
+ }
39
+ var AgentStatusTracker;
40
+ var init_agent_status = __esm({
41
+ "src/engine/agent-status.ts"() {
42
+ AgentStatusTracker = class {
43
+ statuses = /* @__PURE__ */ new Map();
44
+ listeners = /* @__PURE__ */ new Set();
45
+ staleCheckTimer = null;
46
+ dbSyncTimer = null;
47
+ dirtyAgents = /* @__PURE__ */ new Set();
48
+ db = null;
49
+ // EngineDatabase reference for persisting state
50
+ /** How long without a heartbeat before agent is considered offline */
51
+ staleThresholdMs = 9e4;
52
+ // 90 seconds
53
+ constructor() {
54
+ this.staleCheckTimer = setInterval(() => this.checkStale(), 3e4);
55
+ if (this.staleCheckTimer.unref) this.staleCheckTimer.unref();
56
+ this.dbSyncTimer = setInterval(() => this.flushToDb(), 1e4);
57
+ if (this.dbSyncTimer.unref) this.dbSyncTimer.unref();
58
+ }
59
+ /** Set database reference for persisting status to managed_agents */
60
+ setDb(db) {
61
+ this.db = db;
62
+ }
63
+ // ─── Core State Updates ────────────────────────────────
64
+ /** Agent process sent a heartbeat (proves it's alive) */
65
+ heartbeat(agentId) {
66
+ const snap = this.getOrCreate(agentId);
67
+ snap.lastHeartbeat = (/* @__PURE__ */ new Date()).toISOString();
68
+ if (snap.status === "offline" || snap.status === "error") {
69
+ snap.status = "idle";
70
+ snap.onlineSince = (/* @__PURE__ */ new Date()).toISOString();
71
+ }
72
+ this.emit(agentId, snap);
73
+ }
74
+ /** Agent started working on something */
75
+ startActivity(agentId, activity) {
76
+ const snap = this.getOrCreate(agentId);
77
+ snap.status = "online";
78
+ snap.currentActivity = { ...activity, startedAt: (/* @__PURE__ */ new Date()).toISOString() };
79
+ snap.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
80
+ snap.lastHeartbeat = (/* @__PURE__ */ new Date()).toISOString();
81
+ if (!snap.onlineSince) snap.onlineSince = (/* @__PURE__ */ new Date()).toISOString();
82
+ this.emit(agentId, snap);
83
+ }
84
+ /** Agent finished current activity */
85
+ endActivity(agentId) {
86
+ const snap = this.getOrCreate(agentId);
87
+ snap.currentActivity = null;
88
+ snap.status = snap.activeSessions > 0 ? "online" : "idle";
89
+ snap.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
90
+ this.emit(agentId, snap);
91
+ }
92
+ /** Agent executing a tool */
93
+ toolStart(agentId, toolName, detail) {
94
+ const snap = this.getOrCreate(agentId);
95
+ snap.status = "online";
96
+ snap.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
97
+ snap.lastHeartbeat = (/* @__PURE__ */ new Date()).toISOString();
98
+ if (snap.currentActivity) {
99
+ snap.currentActivity.tool = toolName;
100
+ if (detail) snap.currentActivity.detail = detail;
101
+ } else {
102
+ snap.currentActivity = {
103
+ type: "tool_call",
104
+ detail: detail || toolName,
105
+ tool: toolName,
106
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
107
+ };
108
+ }
109
+ this.emit(agentId, snap);
110
+ }
111
+ /** Tool execution finished */
112
+ toolEnd(agentId) {
113
+ const snap = this.getOrCreate(agentId);
114
+ if (snap.currentActivity?.tool) {
115
+ snap.currentActivity.tool = void 0;
116
+ }
117
+ this.emit(agentId, snap);
118
+ }
119
+ /** Session started */
120
+ sessionStart(agentId, sessionId, kind, detail) {
121
+ const snap = this.getOrCreate(agentId);
122
+ snap.activeSessions++;
123
+ snap.status = "online";
124
+ snap.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
125
+ snap.lastHeartbeat = (/* @__PURE__ */ new Date()).toISOString();
126
+ if (!snap.onlineSince) snap.onlineSince = (/* @__PURE__ */ new Date()).toISOString();
127
+ snap.currentActivity = {
128
+ type: kind || "task",
129
+ detail: detail || kind,
130
+ sessionId,
131
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
132
+ };
133
+ this.emit(agentId, snap);
134
+ }
135
+ /** Session ended */
136
+ sessionEnd(agentId, _sessionId) {
137
+ const snap = this.getOrCreate(agentId);
138
+ snap.activeSessions = Math.max(0, snap.activeSessions - 1);
139
+ if (snap.activeSessions === 0) {
140
+ snap.currentActivity = null;
141
+ snap.status = "idle";
142
+ }
143
+ snap.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
144
+ this.emit(agentId, snap);
145
+ }
146
+ /** Agent clocked in */
147
+ clockIn(agentId) {
148
+ const snap = this.getOrCreate(agentId);
149
+ snap.clockedIn = true;
150
+ snap.status = snap.activeSessions > 0 ? "online" : "idle";
151
+ snap.onlineSince = (/* @__PURE__ */ new Date()).toISOString();
152
+ this.emit(agentId, snap);
153
+ }
154
+ /** Agent clocked out */
155
+ clockOut(agentId) {
156
+ const snap = this.getOrCreate(agentId);
157
+ snap.clockedIn = false;
158
+ snap.status = "offline";
159
+ snap.currentActivity = null;
160
+ snap.onlineSince = null;
161
+ this.emit(agentId, snap);
162
+ }
163
+ /** Agent process crashed or went offline */
164
+ markOffline(agentId, reason) {
165
+ const snap = this.getOrCreate(agentId);
166
+ snap.status = reason ? "error" : "offline";
167
+ snap.currentActivity = null;
168
+ snap.activeSessions = 0;
169
+ snap.onlineSince = null;
170
+ this.emit(agentId, snap);
171
+ }
172
+ // ─── Queries ───────────────────────────────────────────
173
+ getStatus(agentId) {
174
+ const snap = this.getOrCreate(agentId);
175
+ if (snap.onlineSince) {
176
+ snap.uptimeMs = Date.now() - new Date(snap.onlineSince).getTime();
177
+ } else {
178
+ snap.uptimeMs = null;
179
+ }
180
+ return { ...snap };
181
+ }
182
+ getAllStatuses() {
183
+ return Array.from(this.statuses.values()).map((s) => ({
184
+ ...s,
185
+ uptimeMs: s.onlineSince ? Date.now() - new Date(s.onlineSince).getTime() : null
186
+ }));
187
+ }
188
+ // ─── Subscriptions (for SSE) ───────────────────────────
189
+ subscribe(listener) {
190
+ this.listeners.add(listener);
191
+ return () => this.listeners.delete(listener);
192
+ }
193
+ // ─── Internals ─────────────────────────────────────────
194
+ getOrCreate(agentId) {
195
+ let snap = this.statuses.get(agentId);
196
+ if (!snap) {
197
+ snap = {
198
+ agentId,
199
+ status: "offline",
200
+ clockedIn: false,
201
+ currentActivity: null,
202
+ activeSessions: 0,
203
+ lastHeartbeat: null,
204
+ lastActivity: null,
205
+ uptimeMs: null,
206
+ onlineSince: null
207
+ };
208
+ this.statuses.set(agentId, snap);
209
+ }
210
+ return snap;
211
+ }
212
+ emit(agentId, snapshot) {
213
+ this.dirtyAgents.add(agentId);
214
+ for (const listener of this.listeners) {
215
+ try {
216
+ listener(agentId, snapshot);
217
+ } catch {
218
+ }
219
+ }
220
+ }
221
+ /** Map SSE status → DB state column value */
222
+ statusToDbState(status, activeSessions) {
223
+ switch (status) {
224
+ case "online":
225
+ return "running";
226
+ case "idle":
227
+ return activeSessions > 0 ? "running" : "ready";
228
+ case "error":
229
+ return "error";
230
+ case "offline":
231
+ return "stopped";
232
+ default:
233
+ return "unknown";
234
+ }
235
+ }
236
+ /** Flush dirty agent statuses to the database */
237
+ async flushToDb() {
238
+ if (!this.db || this.dirtyAgents.size === 0) return;
239
+ const batch = [...this.dirtyAgents];
240
+ this.dirtyAgents.clear();
241
+ for (const agentId of batch) {
242
+ const snap = this.statuses.get(agentId);
243
+ if (!snap) continue;
244
+ const dbState = this.statusToDbState(snap.status, snap.activeSessions);
245
+ try {
246
+ await this.db.run(
247
+ `UPDATE managed_agents SET state = $1, updated_at = $2 WHERE id = $3`,
248
+ [dbState, (/* @__PURE__ */ new Date()).toISOString(), agentId]
249
+ );
250
+ } catch {
251
+ try {
252
+ await this.db.run(
253
+ `UPDATE managed_agents SET state = ?, updated_at = ? WHERE id = ?`,
254
+ [dbState, (/* @__PURE__ */ new Date()).toISOString(), agentId]
255
+ );
256
+ } catch (e) {
257
+ console.warn(`[agent-status] DB sync failed for ${agentId}: ${e?.message}`);
258
+ }
259
+ }
260
+ }
261
+ }
262
+ checkStale() {
263
+ const now = Date.now();
264
+ for (const [agentId, snap] of this.statuses) {
265
+ if (snap.status === "offline" || snap.status === "error") continue;
266
+ if (snap.lastHeartbeat) {
267
+ const elapsed = now - new Date(snap.lastHeartbeat).getTime();
268
+ if (elapsed > this.staleThresholdMs) {
269
+ snap.status = "offline";
270
+ snap.currentActivity = null;
271
+ snap.activeSessions = 0;
272
+ snap.onlineSince = null;
273
+ this.emit(agentId, snap);
274
+ }
275
+ }
276
+ }
277
+ }
278
+ /** Accept a full status update from an external agent process */
279
+ externalUpdate(agentId, update) {
280
+ const snap = this.getOrCreate(agentId);
281
+ if (update.status != null) snap.status = update.status;
282
+ if (update.clockedIn != null) snap.clockedIn = update.clockedIn;
283
+ if (update.currentActivity !== void 0) snap.currentActivity = update.currentActivity;
284
+ if (update.activeSessions != null) snap.activeSessions = update.activeSessions;
285
+ snap.lastHeartbeat = (/* @__PURE__ */ new Date()).toISOString();
286
+ if (snap.status !== "offline" && !snap.onlineSince) snap.onlineSince = (/* @__PURE__ */ new Date()).toISOString();
287
+ this.emit(agentId, snap);
288
+ }
289
+ destroy() {
290
+ if (this.staleCheckTimer) clearInterval(this.staleCheckTimer);
291
+ if (this.dbSyncTimer) clearInterval(this.dbSyncTimer);
292
+ this.flushToDb().catch(() => {
293
+ });
294
+ this.listeners.clear();
295
+ this.statuses.clear();
296
+ }
297
+ };
298
+ }
299
+ });
300
+
301
+ export {
302
+ AgentStatusTracker,
303
+ describeToolActivity,
304
+ init_agent_status
305
+ };