@agenticmail/api 0.7.3 → 0.7.6

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.
Files changed (3) hide show
  1. package/README.md +5 -1
  2. package/dist/index.js +240 -52
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,4 +1,8 @@
1
- # @agenticmail/api
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/agenticmail/agenticmail/main/docs/images/logo-200.png" alt="AgenticMail logo (pink bow)" width="180" />
3
+ </p>
4
+
5
+ <h1 align="center">@agenticmail/api</h1>
2
6
 
3
7
  The API server for [AgenticMail](https://github.com/agenticmail/agenticmail) — the part that lets AI agents (and humans) talk to the email and SMS system over the network.
4
8
 
package/dist/index.js CHANGED
@@ -35,7 +35,7 @@ function safeEqual(a, b) {
35
35
  const hb = createHash("sha256").update(b).digest();
36
36
  return timingSafeEqual(ha, hb);
37
37
  }
38
- function createAuthMiddleware(masterKey, accountManager, db) {
38
+ function createAuthMiddleware(masterKey, accountManager2, db) {
39
39
  return async (req, res, next) => {
40
40
  const authHeader = req.headers.authorization;
41
41
  if (!authHeader?.startsWith("Bearer ")) {
@@ -53,7 +53,7 @@ function createAuthMiddleware(masterKey, accountManager, db) {
53
53
  return;
54
54
  }
55
55
  try {
56
- const agent = await accountManager.getByApiKey(token);
56
+ const agent = await accountManager2.getByApiKey(token);
57
57
  if (agent) {
58
58
  req.agent = agent;
59
59
  if (db) {
@@ -324,9 +324,9 @@ function sanitizeAgent(agent) {
324
324
  }
325
325
  return agent;
326
326
  }
327
- function createAccountRoutes(accountManager, db, config) {
327
+ function createAccountRoutes(accountManager2, db, config) {
328
328
  const router = Router3();
329
- const deletionService = new AgentDeletionService(db, accountManager, config);
329
+ const deletionService = new AgentDeletionService(db, accountManager2, config);
330
330
  router.post("/accounts", requireMaster, async (req, res, next) => {
331
331
  if (!req.body || typeof req.body !== "object") {
332
332
  res.status(400).json({ error: "Request body must be JSON" });
@@ -357,7 +357,7 @@ function createAccountRoutes(accountManager, db, config) {
357
357
  const cleanMeta = metadata ? Object.fromEntries(
358
358
  Object.entries(metadata).filter(([k]) => !k.startsWith("_"))
359
359
  ) : void 0;
360
- const agent = await accountManager.create({ name: accountName, domain, password: password || void 0, metadata: cleanMeta, role });
360
+ const agent = await accountManager2.create({ name: accountName, domain, password: password || void 0, metadata: cleanMeta, role });
361
361
  try {
362
362
  db.prepare("UPDATE agents SET last_activity_at = datetime('now') WHERE id = ?").run(agent.id);
363
363
  } catch {
@@ -389,7 +389,7 @@ function createAccountRoutes(accountManager, db, config) {
389
389
  });
390
390
  router.get("/accounts", requireMaster, async (_req, res, next) => {
391
391
  try {
392
- const agents = await accountManager.list();
392
+ const agents = await accountManager2.list();
393
393
  res.json({ agents: agents.map(sanitizeAgent) });
394
394
  } catch (err) {
395
395
  next(err);
@@ -397,7 +397,7 @@ function createAccountRoutes(accountManager, db, config) {
397
397
  });
398
398
  router.get("/accounts/directory", requireAuth, async (_req, res, next) => {
399
399
  try {
400
- const agents = await accountManager.list();
400
+ const agents = await accountManager2.list();
401
401
  const directory = agents.map((a) => ({ name: a.name, email: a.email, role: a.role }));
402
402
  res.json({ agents: directory });
403
403
  } catch (err) {
@@ -406,7 +406,7 @@ function createAccountRoutes(accountManager, db, config) {
406
406
  });
407
407
  router.get("/accounts/directory/:name", requireAuth, async (req, res, next) => {
408
408
  try {
409
- const agent = await accountManager.getByName(req.params.name);
409
+ const agent = await accountManager2.getByName(req.params.name);
410
410
  if (!agent) {
411
411
  res.status(404).json({ error: "Agent not found" });
412
412
  return;
@@ -471,7 +471,7 @@ function createAccountRoutes(accountManager, db, config) {
471
471
  const deleted = [];
472
472
  for (const row of rows) {
473
473
  try {
474
- await accountManager.delete(row.id);
474
+ await accountManager2.delete(row.id);
475
475
  deleted.push(row.name);
476
476
  } catch {
477
477
  }
@@ -483,7 +483,7 @@ function createAccountRoutes(accountManager, db, config) {
483
483
  });
484
484
  router.get("/accounts/:id", requireMaster, async (req, res, next) => {
485
485
  try {
486
- const agent = await accountManager.getById(req.params.id);
486
+ const agent = await accountManager2.getById(req.params.id);
487
487
  if (!agent) {
488
488
  res.status(404).json({ error: "Agent not found" });
489
489
  return;
@@ -501,7 +501,7 @@ function createAccountRoutes(accountManager, db, config) {
501
501
  res.status(400).json({ error: "metadata must be an object" });
502
502
  return;
503
503
  }
504
- const updated = await accountManager.updateMetadata(agent.id, metadata);
504
+ const updated = await accountManager2.updateMetadata(agent.id, metadata);
505
505
  if (!updated) {
506
506
  res.status(404).json({ error: "Agent not found" });
507
507
  return;
@@ -526,7 +526,7 @@ function createAccountRoutes(accountManager, db, config) {
526
526
  });
527
527
  router.delete("/accounts/:id", requireMaster, async (req, res, next) => {
528
528
  try {
529
- const allAgents = await accountManager.list();
529
+ const allAgents = await accountManager2.list();
530
530
  if (allAgents.length <= 1) {
531
531
  res.status(400).json({ error: "Cannot delete the last agent. At least one agent must remain." });
532
532
  return;
@@ -544,7 +544,7 @@ function createAccountRoutes(accountManager, db, config) {
544
544
  }
545
545
  res.json(summary);
546
546
  } else {
547
- const deleted = await accountManager.delete(req.params.id);
547
+ const deleted = await accountManager2.delete(req.params.id);
548
548
  if (!deleted) {
549
549
  res.status(404).json({ error: "Agent not found" });
550
550
  return;
@@ -828,6 +828,8 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
828
828
  return;
829
829
  }
830
830
  const agent = req.agent;
831
+ const wakeList = normalizeWakeList(req.body?.wake);
832
+ const customHeaders = wakeHeaders(wakeList);
831
833
  const mailOpts = {
832
834
  to: draft.to_addr,
833
835
  subject: draft.subject || "(no subject)",
@@ -836,7 +838,8 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
836
838
  cc: draft.cc || void 0,
837
839
  bcc: draft.bcc || void 0,
838
840
  inReplyTo: draft.in_reply_to || void 0,
839
- references: draft.refs ? JSON.parse(draft.refs) : void 0
841
+ references: draft.refs ? JSON.parse(draft.refs) : void 0,
842
+ ...Object.keys(customHeaders).length > 0 ? { headers: customHeaders } : {}
840
843
  };
841
844
  if (gatewayManager) {
842
845
  const gatewayResult = await gatewayManager.routeOutbound(agent.name, mailOpts);
@@ -857,6 +860,19 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
857
860
  try {
858
861
  const result = await sender.send(mailOpts);
859
862
  db.prepare("DELETE FROM drafts WHERE id = ?").run(draft.id);
863
+ notifyLocalRecipientsOfNewMail(
864
+ accountManager,
865
+ mailOpts.to,
866
+ mailOpts.cc,
867
+ mailOpts.bcc,
868
+ agent,
869
+ mailOpts.subject,
870
+ result.messageId,
871
+ config,
872
+ wakeList
873
+ ).catch((err) => {
874
+ console.warn(`[drafts] SSE notify failed: ${err.message}`);
875
+ });
860
876
  res.json(result);
861
877
  } finally {
862
878
  sender.close();
@@ -1078,20 +1094,24 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
1078
1094
  res.status(404).json({ error: "Template not found" });
1079
1095
  return;
1080
1096
  }
1081
- const { to, variables, cc, bcc } = req.body || {};
1097
+ const { to, variables, cc, bcc, wake } = req.body || {};
1082
1098
  if (!to) {
1083
1099
  res.status(400).json({ error: "to is required" });
1084
1100
  return;
1085
1101
  }
1086
1102
  const applyVars = (text, vars2) => text.replace(/\{\{(\w+)\}\}/g, (m, key) => vars2[key] ?? m);
1103
+ const wakeList = normalizeWakeList(wake);
1104
+ const customHeaders = wakeHeaders(wakeList);
1087
1105
  const vars = variables && typeof variables === "object" ? variables : {};
1106
+ const renderedSubject = applyVars(template.subject || "(no subject)", vars);
1088
1107
  const mailOpts = {
1089
1108
  to,
1090
- subject: applyVars(template.subject || "(no subject)", vars),
1109
+ subject: renderedSubject,
1091
1110
  text: template.text_body ? applyVars(template.text_body, vars) : void 0,
1092
1111
  html: template.html_body ? applyVars(template.html_body, vars) : void 0,
1093
1112
  cc: cc || void 0,
1094
- bcc: bcc || void 0
1113
+ bcc: bcc || void 0,
1114
+ ...Object.keys(customHeaders).length > 0 ? { headers: customHeaders } : {}
1095
1115
  };
1096
1116
  const agent = req.agent;
1097
1117
  if (gatewayManager) {
@@ -1111,6 +1131,19 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
1111
1131
  });
1112
1132
  try {
1113
1133
  const result = await sender.send(mailOpts);
1134
+ notifyLocalRecipientsOfNewMail(
1135
+ accountManager,
1136
+ to,
1137
+ cc,
1138
+ bcc,
1139
+ agent,
1140
+ renderedSubject,
1141
+ result.messageId,
1142
+ config,
1143
+ wakeList
1144
+ ).catch((err) => {
1145
+ console.warn(`[templates] SSE notify failed: ${err.message}`);
1146
+ });
1114
1147
  res.json(result);
1115
1148
  } finally {
1116
1149
  sender.close();
@@ -1181,7 +1214,7 @@ function evaluateRules(db, agentId, email) {
1181
1214
  }
1182
1215
  return null;
1183
1216
  }
1184
- function startScheduledSender(db, accountManager, config, gatewayManager) {
1217
+ function startScheduledSender(db, accountManager2, config, gatewayManager) {
1185
1218
  return setInterval(async () => {
1186
1219
  try {
1187
1220
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1190,7 +1223,7 @@ function startScheduledSender(db, accountManager, config, gatewayManager) {
1190
1223
  ).all(now);
1191
1224
  for (const row of pending) {
1192
1225
  try {
1193
- const agent = await accountManager.getById(row.agent_id);
1226
+ const agent = await accountManager2.getById(row.agent_id);
1194
1227
  if (!agent) {
1195
1228
  db.prepare("UPDATE scheduled_emails SET status = 'failed', error = ? WHERE id = ?").run("Agent not found", row.id);
1196
1229
  continue;
@@ -1289,7 +1322,7 @@ async function closeAllWatchers() {
1289
1322
  }
1290
1323
  activeWatchers.clear();
1291
1324
  }
1292
- function createEventRoutes(accountManager, config, db) {
1325
+ function createEventRoutes(accountManager2, config, db) {
1293
1326
  const router = Router5();
1294
1327
  router.get("/events", requireAgent, async (req, res, next) => {
1295
1328
  try {
@@ -1677,7 +1710,18 @@ function normalizeMessageId(id) {
1677
1710
  if (!id) return "";
1678
1711
  return id.trim().replace(/^<+|>+$/g, "").toLowerCase();
1679
1712
  }
1680
- async function notifyLocalRecipientsOfNewMail(accountManager, toField, ccField, bccField, fromAgent, subject, messageId, config) {
1713
+ function normalizeWakeList(value) {
1714
+ if (value === void 0 || value === null) return void 0;
1715
+ const strip = (s) => s.trim().replace(/@localhost$/i, "").toLowerCase();
1716
+ if (Array.isArray(value)) return value.map((v) => strip(String(v))).filter(Boolean);
1717
+ if (typeof value === "string") return value.split(",").map(strip).filter(Boolean);
1718
+ return void 0;
1719
+ }
1720
+ function wakeHeaders(wakeList) {
1721
+ if (wakeList === void 0) return {};
1722
+ return { "X-AgenticMail-Wake": wakeList.join(", ") };
1723
+ }
1724
+ async function notifyLocalRecipientsOfNewMail(accountManager2, toField, ccField, bccField, fromAgent, subject, messageId, config, wakeList) {
1681
1725
  const collected = [];
1682
1726
  const push = (v) => {
1683
1727
  if (!v) return;
@@ -1707,7 +1751,7 @@ async function notifyLocalRecipientsOfNewMail(accountManager, toField, ccField,
1707
1751
  if (addr === fromAgent.email.toLowerCase()) continue;
1708
1752
  let recipient = null;
1709
1753
  try {
1710
- recipient = await accountManager.getByName(localPart);
1754
+ recipient = await accountManager2.getByName(localPart);
1711
1755
  } catch {
1712
1756
  }
1713
1757
  if (!recipient || notified.has(recipient.id)) continue;
@@ -1738,7 +1782,12 @@ async function notifyLocalRecipientsOfNewMail(accountManager, toField, ccField,
1738
1782
  internal: true,
1739
1783
  from: { name: fromAgent.name, address: fromAgent.email },
1740
1784
  subject,
1741
- messageId
1785
+ messageId,
1786
+ // Wake gating signal. Present iff the sender opted in. The
1787
+ // dispatcher reads this and spawns a Claude worker only for
1788
+ // recipients whose name is on the list (or for everyone if the
1789
+ // field is absent, preserving the v0.8.x default).
1790
+ ...wakeList !== void 0 ? { wakeAllowlist: wakeList } : {}
1742
1791
  });
1743
1792
  }
1744
1793
  }
@@ -1752,7 +1801,7 @@ function saveSentCopy(authUser, password, config, raw) {
1752
1801
  }
1753
1802
  })();
1754
1803
  }
1755
- function createMailRoutes(accountManager, config, db, gatewayManager) {
1804
+ function createMailRoutes(accountManager2, config, db, gatewayManager) {
1756
1805
  const router = Router6();
1757
1806
  router.post("/mail/send", requireAgent, async (req, res, next) => {
1758
1807
  try {
@@ -1761,7 +1810,7 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
1761
1810
  return;
1762
1811
  }
1763
1812
  const agent = req.agent;
1764
- const { to, subject, text, html, cc, bcc, replyTo, inReplyTo, references, attachments, allowSensitive } = req.body;
1813
+ const { to, subject, text, html, cc, bcc, replyTo, inReplyTo, references, attachments, allowSensitive, wake } = req.body;
1765
1814
  if (!to || !subject) {
1766
1815
  res.status(400).json({ error: "to and subject are required" });
1767
1816
  return;
@@ -1789,7 +1838,21 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
1789
1838
  const pendingId = crypto.randomUUID();
1790
1839
  const ownerName2 = agent.metadata?.ownerName;
1791
1840
  const fromName2 = ownerName2 ? `${agent.name} from ${ownerName2}` : agent.name;
1792
- const mailOptions = { to, subject, text, html, cc, bcc, replyTo, inReplyTo, references, attachments, fromName: fromName2 };
1841
+ const wakeListForPersist = normalizeWakeList(wake);
1842
+ const mailOptions = {
1843
+ to,
1844
+ subject,
1845
+ text,
1846
+ html,
1847
+ cc,
1848
+ bcc,
1849
+ replyTo,
1850
+ inReplyTo,
1851
+ references,
1852
+ attachments,
1853
+ fromName: fromName2,
1854
+ ...wakeListForPersist !== void 0 ? { wakeList: wakeListForPersist } : {}
1855
+ };
1793
1856
  db.prepare(
1794
1857
  `INSERT INTO pending_outbound (id, agent_id, mail_options, warnings, summary) VALUES (?, ?, ?, ?, ?)`
1795
1858
  ).run(pendingId, agent.id, JSON.stringify(mailOptions), JSON.stringify(scanResult.warnings), scanResult.summary);
@@ -1858,7 +1921,22 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
1858
1921
  }
1859
1922
  const ownerName = agent.metadata?.ownerName;
1860
1923
  const fromName = ownerName ? `${agent.name} from ${ownerName}` : agent.name;
1861
- const mailOpts = { to, subject, text, html, cc, bcc, replyTo, inReplyTo, references, attachments, fromName };
1924
+ const wakeList = normalizeWakeList(wake);
1925
+ const customHeaders = wakeHeaders(wakeList);
1926
+ const mailOpts = {
1927
+ to,
1928
+ subject,
1929
+ text,
1930
+ html,
1931
+ cc,
1932
+ bcc,
1933
+ replyTo,
1934
+ inReplyTo,
1935
+ references,
1936
+ attachments,
1937
+ fromName,
1938
+ ...Object.keys(customHeaders).length > 0 ? { headers: customHeaders } : {}
1939
+ };
1862
1940
  const password = getAgentPassword(agent);
1863
1941
  if (gatewayManager) {
1864
1942
  const gatewayResult = await gatewayManager.routeOutbound(agent.name, mailOpts);
@@ -1875,14 +1953,15 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
1875
1953
  const result = await sender.send(mailOpts);
1876
1954
  saveSentCopy(agent.stalwartPrincipal, password, config, result.raw);
1877
1955
  notifyLocalRecipientsOfNewMail(
1878
- accountManager,
1956
+ accountManager2,
1879
1957
  to,
1880
1958
  cc,
1881
1959
  bcc,
1882
1960
  agent,
1883
1961
  subject,
1884
1962
  result.messageId,
1885
- config
1963
+ config,
1964
+ wakeList
1886
1965
  ).catch((err) => {
1887
1966
  console.warn(`[mail] Internal SSE notify failed: ${err.message}`);
1888
1967
  });
@@ -2454,7 +2533,7 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
2454
2533
  res.status(400).json({ error: `Email already ${row.status}` });
2455
2534
  return;
2456
2535
  }
2457
- const agent = await accountManager.getById(row.agent_id);
2536
+ const agent = await accountManager2.getById(row.agent_id);
2458
2537
  if (!agent) {
2459
2538
  res.status(404).json({ error: "Agent account no longer exists" });
2460
2539
  return;
@@ -2469,6 +2548,11 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
2469
2548
  }
2470
2549
  }
2471
2550
  }
2551
+ const persistedWakeList = Array.isArray(mailOpts.wakeList) ? mailOpts.wakeList : void 0;
2552
+ if (persistedWakeList !== void 0) {
2553
+ mailOpts.headers = { ...mailOpts.headers ?? {}, ...wakeHeaders(persistedWakeList) };
2554
+ delete mailOpts.wakeList;
2555
+ }
2472
2556
  const password = getAgentPassword(agent);
2473
2557
  let response;
2474
2558
  if (gatewayManager) {
@@ -2486,14 +2570,15 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
2486
2570
  const result = await sender.send(mailOpts);
2487
2571
  saveSentCopy(agent.stalwartPrincipal, password, config, result.raw);
2488
2572
  notifyLocalRecipientsOfNewMail(
2489
- accountManager,
2573
+ accountManager2,
2490
2574
  mailOpts.to,
2491
2575
  mailOpts.cc,
2492
2576
  mailOpts.bcc,
2493
2577
  agent,
2494
2578
  mailOpts.subject,
2495
2579
  result.messageId,
2496
- config
2580
+ config,
2581
+ persistedWakeList
2497
2582
  ).catch((err) => {
2498
2583
  console.warn(`[mail] Internal SSE notify (approve) failed: ${err.message}`);
2499
2584
  });
@@ -2542,7 +2627,7 @@ var INBOUND_SECRET = process.env.AGENTICMAIL_INBOUND_SECRET || (() => {
2542
2627
  return generated;
2543
2628
  })();
2544
2629
  var DEBUG = () => !!process.env.AGENTICMAIL_DEBUG;
2545
- function createInboundRoutes(accountManager, config, gatewayManager) {
2630
+ function createInboundRoutes(accountManager2, config, gatewayManager) {
2546
2631
  const router = Router7();
2547
2632
  router.post("/mail/inbound", async (req, res, next) => {
2548
2633
  try {
@@ -2558,7 +2643,7 @@ function createInboundRoutes(accountManager, config, gatewayManager) {
2558
2643
  }
2559
2644
  const recipientEmail = typeof to === "string" ? to : to[0];
2560
2645
  const localPart = recipientEmail.split("@")[0];
2561
- const agent = await accountManager.getByName(localPart);
2646
+ const agent = await accountManager2.getByName(localPart);
2562
2647
  if (!agent) {
2563
2648
  console.warn(`[Inbound] No agent found for "${localPart}" (${recipientEmail})`);
2564
2649
  res.status(404).json({ error: `No agent found for ${recipientEmail}` });
@@ -3042,7 +3127,7 @@ import { Router as Router10 } from "express";
3042
3127
  import { v4 as uuidv43 } from "uuid";
3043
3128
  import { MailSender as MailSender4 } from "@agenticmail/core";
3044
3129
  var rpcResolvers = /* @__PURE__ */ new Map();
3045
- function createTaskRoutes(db, accountManager, config) {
3130
+ function createTaskRoutes(db, accountManager2, config) {
3046
3131
  const router = Router10();
3047
3132
  router.post("/tasks/assign", requireAuth, async (req, res, next) => {
3048
3133
  try {
@@ -3051,7 +3136,7 @@ function createTaskRoutes(db, accountManager, config) {
3051
3136
  res.status(400).json({ error: "assignee (agent name) is required" });
3052
3137
  return;
3053
3138
  }
3054
- const target = await accountManager.getByName(assignee);
3139
+ const target = await accountManager2.getByName(assignee);
3055
3140
  if (!target) {
3056
3141
  res.status(404).json({ error: `Agent "${assignee}" not found` });
3057
3142
  return;
@@ -3110,7 +3195,7 @@ Please check your pending tasks.`
3110
3195
  let assigneeId = req.agent.id;
3111
3196
  const assigneeName = req.query.assignee;
3112
3197
  if (assigneeName) {
3113
- const target = await accountManager.getByName(assigneeName);
3198
+ const target = await accountManager2.getByName(assigneeName);
3114
3199
  if (target) assigneeId = target.id;
3115
3200
  }
3116
3201
  const rows = db.prepare(
@@ -3238,7 +3323,7 @@ Please check your pending tasks.`
3238
3323
  res.status(400).json({ error: "target (agent name) and task are required" });
3239
3324
  return;
3240
3325
  }
3241
- const targetAgent = await accountManager.getByName(target);
3326
+ const targetAgent = await accountManager2.getByName(target);
3242
3327
  if (!targetAgent) {
3243
3328
  res.status(404).json({ error: `Agent "${target}" not found` });
3244
3329
  return;
@@ -3372,7 +3457,7 @@ import {
3372
3457
  normalizePhoneNumber,
3373
3458
  isValidPhoneNumber
3374
3459
  } from "@agenticmail/core";
3375
- function createSmsRoutes(db, accountManager, config, gatewayManager) {
3460
+ function createSmsRoutes(db, accountManager2, config, gatewayManager) {
3376
3461
  const router = Router11();
3377
3462
  const smsManager = new SmsManager(db);
3378
3463
  function getAgent(req, res) {
@@ -3760,7 +3845,7 @@ function adaptBetterSqlite(raw) {
3760
3845
  }
3761
3846
  };
3762
3847
  }
3763
- function createStorageRoutes(rawDb, accountManager, config, dialect = "sqlite") {
3848
+ function createStorageRoutes(rawDb, accountManager2, config, dialect = "sqlite") {
3764
3849
  const db = adaptBetterSqlite(rawDb);
3765
3850
  const router = Router12();
3766
3851
  function getAgent(req, res) {
@@ -4590,6 +4675,108 @@ function createStorageRoutes(rawDb, accountManager, config, dialect = "sqlite")
4590
4675
  return router;
4591
4676
  }
4592
4677
 
4678
+ // src/routes/dispatcher-activity.ts
4679
+ import { Router as Router13 } from "express";
4680
+ var ACTIVE_TTL_MS = 30 * 60 * 1e3;
4681
+ var RECENT_TTL_MS = 2 * 60 * 1e3;
4682
+ var HARD_CAP = 256;
4683
+ var active = /* @__PURE__ */ new Map();
4684
+ var recent = /* @__PURE__ */ new Map();
4685
+ function prune(nowMs) {
4686
+ for (const [id, w] of active) {
4687
+ if (nowMs - w.startedAtMs > ACTIVE_TTL_MS) active.delete(id);
4688
+ }
4689
+ for (const [id, w] of recent) {
4690
+ const t = w.endedAtMs ?? w.startedAtMs;
4691
+ if (nowMs - t > RECENT_TTL_MS) recent.delete(id);
4692
+ }
4693
+ while (active.size > HARD_CAP) {
4694
+ const first = active.keys().next().value;
4695
+ if (!first) break;
4696
+ active.delete(first);
4697
+ }
4698
+ while (recent.size > HARD_CAP) {
4699
+ const first = recent.keys().next().value;
4700
+ if (!first) break;
4701
+ recent.delete(first);
4702
+ }
4703
+ }
4704
+ function createDispatcherActivityRoutes() {
4705
+ const router = Router13();
4706
+ router.post("/dispatcher/worker-started", requireMaster, (req, res) => {
4707
+ const body = req.body ?? {};
4708
+ if (typeof body.workerId !== "string" || typeof body.agentName !== "string") {
4709
+ res.status(400).json({ error: "workerId and agentName are required" });
4710
+ return;
4711
+ }
4712
+ const info = {
4713
+ workerId: body.workerId,
4714
+ agentName: body.agentName,
4715
+ agentEmail: typeof body.agentEmail === "string" ? body.agentEmail : void 0,
4716
+ kind: typeof body.kind === "string" ? body.kind : "unknown",
4717
+ trigger: body.trigger && typeof body.trigger === "object" ? body.trigger : void 0,
4718
+ startedAtMs: Date.now()
4719
+ };
4720
+ prune(info.startedAtMs);
4721
+ active.set(info.workerId, info);
4722
+ try {
4723
+ pushSystemEvent({
4724
+ type: "worker_started",
4725
+ worker: { ...info }
4726
+ });
4727
+ } catch {
4728
+ }
4729
+ res.status(201).json({ ok: true });
4730
+ });
4731
+ router.post("/dispatcher/worker-finished", requireMaster, (req, res) => {
4732
+ const body = req.body ?? {};
4733
+ if (typeof body.workerId !== "string") {
4734
+ res.status(400).json({ error: "workerId is required" });
4735
+ return;
4736
+ }
4737
+ const existing = active.get(body.workerId);
4738
+ const nowMs = Date.now();
4739
+ const info = {
4740
+ ...existing ?? {
4741
+ workerId: body.workerId,
4742
+ agentName: typeof body.agentName === "string" ? body.agentName : "unknown",
4743
+ kind: "unknown",
4744
+ startedAtMs: nowMs
4745
+ },
4746
+ endedAtMs: nowMs,
4747
+ ok: body.ok === false ? false : true,
4748
+ resultPreview: typeof body.resultPreview === "string" ? body.resultPreview.slice(0, 240) : void 0
4749
+ };
4750
+ active.delete(body.workerId);
4751
+ recent.set(body.workerId, info);
4752
+ prune(nowMs);
4753
+ try {
4754
+ pushSystemEvent({
4755
+ type: "worker_finished",
4756
+ worker: { ...info }
4757
+ });
4758
+ } catch {
4759
+ }
4760
+ res.json({ ok: true });
4761
+ });
4762
+ router.get("/dispatcher/activity", requireMaster, (_req, res) => {
4763
+ const nowMs = Date.now();
4764
+ prune(nowMs);
4765
+ res.json({
4766
+ now: nowMs,
4767
+ active: Array.from(active.values()).map((w) => ({
4768
+ ...w,
4769
+ durationMs: nowMs - w.startedAtMs
4770
+ })),
4771
+ recent: Array.from(recent.values()).map((w) => ({
4772
+ ...w,
4773
+ durationMs: (w.endedAtMs ?? nowMs) - w.startedAtMs
4774
+ }))
4775
+ });
4776
+ });
4777
+ return router;
4778
+ }
4779
+
4593
4780
  // src/app.ts
4594
4781
  var integrationRouteFactoryPromise = (async () => {
4595
4782
  try {
@@ -4628,12 +4815,12 @@ function createApp(configOverrides) {
4628
4815
  adminUser: config.stalwart.adminUser,
4629
4816
  adminPassword: config.stalwart.adminPassword
4630
4817
  });
4631
- const accountManager = new AccountManager(db, stalwart);
4818
+ const accountManager2 = new AccountManager(db, stalwart);
4632
4819
  const domainManager = new DomainManager(db, stalwart);
4633
4820
  const gatewayManager = new GatewayManager2({
4634
4821
  db,
4635
4822
  stalwart,
4636
- accountManager,
4823
+ accountManager: accountManager2,
4637
4824
  localSmtp: {
4638
4825
  host: config.smtp.host,
4639
4826
  port: config.smtp.port,
@@ -4660,27 +4847,28 @@ function createApp(configOverrides) {
4660
4847
  })
4661
4848
  );
4662
4849
  app2.use("/api/agenticmail", createHealthRoutes(stalwart));
4663
- app2.use("/api/agenticmail", createInboundRoutes(accountManager, config, gatewayManager));
4850
+ app2.use("/api/agenticmail", createInboundRoutes(accountManager2, config, gatewayManager));
4664
4851
  const integrationFactory = readResolvedFactory(integrationRouteFactoryPromise);
4665
4852
  if (integrationFactory) {
4666
4853
  app2.use("/api/agenticmail", integrationFactory());
4667
4854
  }
4668
- app2.use("/api/agenticmail", createAuthMiddleware(config.masterKey, accountManager, db));
4669
- app2.use("/api/agenticmail", createAccountRoutes(accountManager, db, config));
4670
- app2.use("/api/agenticmail", createMailRoutes(accountManager, config, db, gatewayManager));
4671
- app2.use("/api/agenticmail", createEventRoutes(accountManager, config, db));
4855
+ app2.use("/api/agenticmail", createAuthMiddleware(config.masterKey, accountManager2, db));
4856
+ app2.use("/api/agenticmail", createAccountRoutes(accountManager2, db, config));
4857
+ app2.use("/api/agenticmail", createMailRoutes(accountManager2, config, db, gatewayManager));
4858
+ app2.use("/api/agenticmail", createEventRoutes(accountManager2, config, db));
4672
4859
  app2.use("/api/agenticmail", createDomainRoutes(domainManager));
4673
4860
  app2.use("/api/agenticmail", createGatewayRoutes(gatewayManager));
4674
- app2.use("/api/agenticmail", createFeatureRoutes(db, accountManager, config, gatewayManager));
4675
- app2.use("/api/agenticmail", createTaskRoutes(db, accountManager, config));
4676
- app2.use("/api/agenticmail", createSmsRoutes(db, accountManager, config, gatewayManager));
4677
- app2.use("/api/agenticmail", createStorageRoutes(db, accountManager, config));
4861
+ app2.use("/api/agenticmail", createFeatureRoutes(db, accountManager2, config, gatewayManager));
4862
+ app2.use("/api/agenticmail", createTaskRoutes(db, accountManager2, config));
4863
+ app2.use("/api/agenticmail", createSmsRoutes(db, accountManager2, config, gatewayManager));
4864
+ app2.use("/api/agenticmail", createStorageRoutes(db, accountManager2, config));
4678
4865
  app2.use("/api/agenticmail", createSystemEventRoutes());
4866
+ app2.use("/api/agenticmail", createDispatcherActivityRoutes());
4679
4867
  app2.use("/api/agenticmail", (_req, res) => {
4680
4868
  res.status(404).json({ error: "Not found" });
4681
4869
  });
4682
4870
  app2.use(errorHandler);
4683
- const context2 = { config, db, stalwart, accountManager, domainManager, gatewayManager };
4871
+ const context2 = { config, db, stalwart, accountManager: accountManager2, domainManager, gatewayManager };
4684
4872
  return { app: app2, context: context2 };
4685
4873
  }
4686
4874
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/api",
3
- "version": "0.7.3",
3
+ "version": "0.7.6",
4
4
  "description": "REST API server for AgenticMail — email and SMS endpoints for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",