@agenticmail/enterprise 0.5.319 → 0.5.321

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 (330) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/CODE_OF_CONDUCT.md +31 -0
  3. package/README.md +118 -38
  4. package/SECURITY.md +42 -0
  5. package/dist/agent-heartbeat-3FWNHZFX.js +510 -0
  6. package/dist/agent-heartbeat-4RWHZR7H.js +510 -0
  7. package/dist/agent-heartbeat-6ZGB5ILY.js +510 -0
  8. package/dist/agent-heartbeat-BIVHLKFM.js +510 -0
  9. package/dist/agent-heartbeat-HRKVFK2T.js +510 -0
  10. package/dist/agent-heartbeat-JC5GWVXD.js +510 -0
  11. package/dist/agent-heartbeat-K6A4HMHB.js +510 -0
  12. package/dist/agent-heartbeat-LCDXWFVB.js +510 -0
  13. package/dist/agent-heartbeat-P7HZCZAQ.js +510 -0
  14. package/dist/agent-heartbeat-PUIRSNIO.js +510 -0
  15. package/dist/agent-heartbeat-SN5ILQ6Y.js +510 -0
  16. package/dist/agent-heartbeat-TW5YTDYC.js +510 -0
  17. package/dist/agent-heartbeat-Z2QQXROL.js +510 -0
  18. package/dist/agent-notify-OEQBCZLN.js +43 -0
  19. package/dist/{agent-tools-263HM5QU.js → agent-tools-3W7XLUYA.js} +1 -1
  20. package/dist/agent-tools-4QK7LLNP.js +9 -0
  21. package/dist/agent-tools-54VZGT6L.js +9 -0
  22. package/dist/{agent-tools-AT4D276V.js → agent-tools-AYYDPO27.js} +7 -7
  23. package/dist/{agent-tools-MSTAPX2I.js → agent-tools-F2X47FKF.js} +7 -7
  24. package/dist/{agent-tools-FA26SY5O.js → agent-tools-O6W3QAZL.js} +11 -6
  25. package/dist/agent-tools-OAWVZBMW.js +9 -0
  26. package/dist/agent-tools-QCCU74PN.js +13949 -0
  27. package/dist/chunk-2LHUARN6.js +4929 -0
  28. package/dist/chunk-2WVCNCYC.js +5087 -0
  29. package/dist/{chunk-6PWDS7KY.js → chunk-3FM6YQUK.js} +20 -20
  30. package/dist/chunk-3UAFHUEC.js +212 -0
  31. package/dist/{chunk-WJO57PMO.js → chunk-46GOWZT4.js} +20 -20
  32. package/dist/{chunk-BNRE7TSX.js → chunk-5KYJAUZV.js} +3 -3
  33. package/dist/chunk-6C5PKREN.js +467 -0
  34. package/dist/{chunk-447MTPZF.js → chunk-6ZMLNEHB.js} +3 -3
  35. package/dist/chunk-BPZQT5N5.js +25652 -0
  36. package/dist/chunk-BQM7MBPS.js +1380 -0
  37. package/dist/{chunk-ZRFKGPIU.js → chunk-C52OQNNY.js} +20 -20
  38. package/dist/chunk-C7HGQF4Y.js +25652 -0
  39. package/dist/chunk-CAHNZGGK.js +25656 -0
  40. package/dist/{chunk-FL3CH3ET.js → chunk-CK7R6UHE.js} +51 -27
  41. package/dist/chunk-D36RPWB7.js +25652 -0
  42. package/dist/{chunk-36NM2B4C.js → chunk-DJK2UPFH.js} +63 -93
  43. package/dist/chunk-DM7FTF7W.js +4929 -0
  44. package/dist/chunk-DMD24UFZ.js +5101 -0
  45. package/dist/{chunk-36XNMIHA.js → chunk-DXZGPUAF.js} +20 -20
  46. package/dist/chunk-F46WB5IL.js +5087 -0
  47. package/dist/chunk-F5QG5SQH.js +5087 -0
  48. package/dist/{chunk-JGEVQZDR.js → chunk-FLQ5FLHW.js} +13 -16
  49. package/dist/chunk-H7GP733U.js +5087 -0
  50. package/dist/{chunk-OZSQLOV6.js → chunk-HHBXWB5U.js} +415 -19
  51. package/dist/{chunk-D24JY75H.js → chunk-IMXS4N6W.js} +3 -3
  52. package/dist/{chunk-6PVBV6ZP.js → chunk-JNMDD7JY.js} +3 -3
  53. package/dist/chunk-JTV5LA47.js +1519 -0
  54. package/dist/chunk-KV6G7NZX.js +1519 -0
  55. package/dist/chunk-MU5MEBIK.js +1519 -0
  56. package/dist/chunk-NLT5MC7X.js +465 -0
  57. package/dist/{chunk-GTFZZUXX.js → chunk-NVLYIM4J.js} +51 -27
  58. package/dist/{chunk-6G5SXLXC.js → chunk-NZY2BIZH.js} +63 -93
  59. package/dist/chunk-O42L6G67.js +1519 -0
  60. package/dist/chunk-OCNERGGM.js +4891 -0
  61. package/dist/chunk-OJSNHONE.js +1519 -0
  62. package/dist/{chunk-2TAZJWJN.js → chunk-OWL3QVH7.js} +18 -0
  63. package/dist/{chunk-P3HVY2HS.js → chunk-OWTLNV4Q.js} +382 -7
  64. package/dist/chunk-PCNYEP6T.js +4891 -0
  65. package/dist/{chunk-YL3Z5KPR.js → chunk-PI4AQ4Z6.js} +438 -15
  66. package/dist/chunk-PN3EGTCA.js +194 -0
  67. package/dist/chunk-Q37UKNRC.js +1519 -0
  68. package/dist/chunk-QXTC6J7H.js +5087 -0
  69. package/dist/{chunk-SPBQVNDI.js → chunk-RKERL5LZ.js} +25 -21
  70. package/dist/chunk-RVBK2IOX.js +25652 -0
  71. package/dist/chunk-SAKODCZ5.js +4891 -0
  72. package/dist/{chunk-XV4TU65E.js → chunk-SALGFC5L.js} +51 -27
  73. package/dist/chunk-STGWZ2MS.js +1519 -0
  74. package/dist/chunk-UY3ZVQDP.js +25652 -0
  75. package/dist/chunk-V6OSD62M.js +5087 -0
  76. package/dist/chunk-VP6YAHX4.js +1519 -0
  77. package/dist/chunk-WDYJOEAI.js +5087 -0
  78. package/dist/chunk-WEAFQNOS.js +195 -0
  79. package/dist/chunk-XKUSAZGP.js +5087 -0
  80. package/dist/chunk-Z6K5FKAB.js +548 -0
  81. package/dist/chunk-ZGE3XAXY.js +1519 -0
  82. package/dist/chunk-ZGYVXYQQ.js +3296 -0
  83. package/dist/cli-agent-7TB2BWS6.js +2370 -0
  84. package/dist/cli-agent-AKXFFST2.js +2370 -0
  85. package/dist/cli-agent-DZTKLITB.js +2357 -0
  86. package/dist/cli-agent-FOF7PFEP.js +2357 -0
  87. package/dist/cli-agent-H74M2ZYN.js +2357 -0
  88. package/dist/cli-agent-HORWVPHB.js +2370 -0
  89. package/dist/cli-agent-HSZT6SKF.js +2423 -0
  90. package/dist/cli-agent-JLUQ4ZU6.js +2424 -0
  91. package/dist/cli-agent-MVCDH4HV.js +2370 -0
  92. package/dist/cli-agent-NZXOEPJ2.js +2357 -0
  93. package/dist/cli-agent-PADN3QRC.js +2357 -0
  94. package/dist/cli-agent-QAYEX3BE.js +2441 -0
  95. package/dist/cli-agent-QT64DT5J.js +2370 -0
  96. package/dist/cli-agent-TFL2M6UK.js +2424 -0
  97. package/dist/cli-agent-UIKXATTD.js +2357 -0
  98. package/dist/cli-agent-UJN6FYTO.js +2370 -0
  99. package/dist/cli-agent-VIQAYVY4.js +2357 -0
  100. package/dist/cli-agent-WNWFVOFM.js +2370 -0
  101. package/dist/cli-agent-XBQX67VJ.js +2423 -0
  102. package/dist/cli-agent-ZLSC6FF4.js +2357 -0
  103. package/dist/cli-serve-2IL5DTEY.js +153 -0
  104. package/dist/cli-serve-47N5UKKW.js +153 -0
  105. package/dist/cli-serve-4XGZFUV2.js +140 -0
  106. package/dist/cli-serve-6OT3UEAN.js +140 -0
  107. package/dist/cli-serve-7L6EY5UH.js +153 -0
  108. package/dist/cli-serve-BDGOOOKQ.js +260 -0
  109. package/dist/cli-serve-BFNIW2LF.js +153 -0
  110. package/dist/cli-serve-C7MN6U5Q.js +153 -0
  111. package/dist/cli-serve-CR3OY3IM.js +153 -0
  112. package/dist/cli-serve-DAJFRWQ7.js +153 -0
  113. package/dist/cli-serve-FW6FHFW4.js +153 -0
  114. package/dist/cli-serve-GEEOQS77.js +153 -0
  115. package/dist/cli-serve-H562I3ZK.js +153 -0
  116. package/dist/cli-serve-HDQZF4C4.js +153 -0
  117. package/dist/cli-serve-LICAOMEB.js +140 -0
  118. package/dist/cli-serve-LLGYLWFS.js +153 -0
  119. package/dist/cli-serve-N3OISDNB.js +153 -0
  120. package/dist/cli-serve-TIZ27EVR.js +153 -0
  121. package/dist/cli-serve-TUNI2RCN.js +153 -0
  122. package/dist/cli-serve-WNOZMAWD.js +153 -0
  123. package/dist/cli-validate-Z726VJCN.js +150 -0
  124. package/dist/cli.js +4 -4
  125. package/dist/connection-manager-KAWEUWUR.js +9 -0
  126. package/dist/dashboard/app.js +9 -3
  127. package/dist/dashboard/components/knowledge-link.js +15 -0
  128. package/dist/dashboard/components/settings-help.js +4 -2
  129. package/dist/dashboard/docs/agent-deployment.html +33 -1
  130. package/dist/dashboard/docs/settings-network.html +321 -0
  131. package/dist/dashboard/docs/settings-security.html +347 -0
  132. package/dist/dashboard/docs/settings-tool-security.html +176 -0
  133. package/dist/dashboard/docs/settings.html +36 -16
  134. package/dist/dashboard/pages/agent-detail/deployment.js +39 -6
  135. package/dist/dashboard/pages/agent-detail/tools.js +10 -0
  136. package/dist/dashboard/pages/database-access.js +4 -3
  137. package/dist/dashboard/pages/settings.js +174 -37
  138. package/dist/dashboard/pages/task-pipeline.js +400 -843
  139. package/dist/db-adapter-2T56ORSD.js +7 -0
  140. package/dist/db-adapter-IRHOUMVC.js +7 -0
  141. package/dist/index.js +41 -41
  142. package/dist/microsoft-VREAZ7M2.js +3955 -0
  143. package/dist/routes-3MMLQTB6.js +90 -0
  144. package/dist/routes-4ZUIJ4HE.js +90 -0
  145. package/dist/routes-5MXHKKH4.js +90 -0
  146. package/dist/routes-64NJFK3B.js +90 -0
  147. package/dist/routes-6AKQ2LBV.js +90 -0
  148. package/dist/routes-CRRBUDO4.js +90 -0
  149. package/dist/routes-DIAF3MC3.js +90 -0
  150. package/dist/routes-KMUNU6CY.js +90 -0
  151. package/dist/routes-LRRLXIZR.js +90 -0
  152. package/dist/routes-N647AJYG.js +90 -0
  153. package/dist/routes-SSSELAAR.js +90 -0
  154. package/dist/routes-STERVGKJ.js +90 -0
  155. package/dist/routes-ZEZZACZP.js +90 -0
  156. package/dist/runtime-5EQN4GFM.js +45 -0
  157. package/dist/runtime-5LP7PUD4.js +45 -0
  158. package/dist/runtime-6BULDBR3.js +45 -0
  159. package/dist/runtime-6YEENDN3.js +45 -0
  160. package/dist/runtime-7LQFRG3B.js +45 -0
  161. package/dist/runtime-AMXJU2MB.js +45 -0
  162. package/dist/runtime-D6WSE7FG.js +45 -0
  163. package/dist/runtime-EYVN7NFJ.js +45 -0
  164. package/dist/runtime-F6RPWQVW.js +45 -0
  165. package/dist/runtime-FYMJURFC.js +45 -0
  166. package/dist/runtime-JRNBL4O4.js +45 -0
  167. package/dist/runtime-OM2NIBMI.js +45 -0
  168. package/dist/runtime-QWPVD7CY.js +45 -0
  169. package/dist/runtime-YLIIPTE4.js +45 -0
  170. package/dist/runtime-YU6P22CG.js +45 -0
  171. package/dist/screen-unlock-4RPZBHOI.js +118 -0
  172. package/dist/server-AMCSXINC.js +28 -0
  173. package/dist/server-CU6LVQS4.js +28 -0
  174. package/dist/server-DFYGH2CV.js +28 -0
  175. package/dist/server-EELWOC3X.js +28 -0
  176. package/dist/server-EN5E2OWQ.js +28 -0
  177. package/dist/server-GW2HYJYI.js +28 -0
  178. package/dist/server-J25NCRWJ.js +28 -0
  179. package/dist/server-JDGNOTFV.js +28 -0
  180. package/dist/server-NE5HD5DJ.js +28 -0
  181. package/dist/server-NQOT7W77.js +28 -0
  182. package/dist/server-PWE5PQTR.js +28 -0
  183. package/dist/server-Q2Q32H2B.js +28 -0
  184. package/dist/server-Q77ME7TL.js +28 -0
  185. package/dist/server-WLLH4WST.js +28 -0
  186. package/dist/server-WTUJ2O3F.js +28 -0
  187. package/dist/server-X4CJTHHF.js +28 -0
  188. package/dist/server-XK3ILCJC.js +28 -0
  189. package/dist/server-ZRD3NDJE.js +28 -0
  190. package/dist/setup-44VBAO4J.js +20 -0
  191. package/dist/setup-4ONNQBWB.js +20 -0
  192. package/dist/setup-4OSBXSCL.js +20 -0
  193. package/dist/setup-4QFGRBLZ.js +20 -0
  194. package/dist/setup-6766SGAR.js +20 -0
  195. package/dist/setup-AYY24DKM.js +20 -0
  196. package/dist/setup-B34N4HPU.js +20 -0
  197. package/dist/setup-E2YLC2EY.js +20 -0
  198. package/dist/setup-ER6NXTY5.js +20 -0
  199. package/dist/setup-H2AGCBW5.js +20 -0
  200. package/dist/setup-ICOZRKCX.js +20 -0
  201. package/dist/setup-JFTJH7UF.js +20 -0
  202. package/dist/setup-PRFNI6YW.js +20 -0
  203. package/dist/setup-RAHBMYHE.js +20 -0
  204. package/dist/setup-TXPR5UQX.js +20 -0
  205. package/dist/setup-XCJMELVU.js +20 -0
  206. package/dist/setup-XIYEIFVK.js +20 -0
  207. package/dist/setup-Z4PZSHBI.js +20 -0
  208. package/dist/skills-FR7I5V7H.js +16 -0
  209. package/dist/skills-HCVBA6PK.js +16 -0
  210. package/dist/system-prompts-TM7OA32C.js +913 -0
  211. package/dist/task-queue-O7IVZYUO.js +9 -0
  212. package/dist/transport-encryption-2T7PIXKG.js +25 -0
  213. package/logs/cloudflared-error.log +61 -0
  214. package/logs/cloudflared-out.log +0 -0
  215. package/logs/enterprise-error.log +0 -0
  216. package/logs/enterprise-out.log +3 -0
  217. package/logs/fola-error.log +0 -0
  218. package/logs/fola-out.log +0 -0
  219. package/logs/john-error.log +8 -0
  220. package/logs/john-out.log +0 -0
  221. package/package.json +31 -3
  222. package/src/agent-tools/tool-resolver.ts +50 -61
  223. package/src/agent-tools/tools/enterprise-database.ts +5 -5
  224. package/src/agent-tools/tools/local/dependency-manager.ts +2 -2
  225. package/src/agent-tools/tools/microsoft/graph-api.ts +137 -26
  226. package/src/agent-tools/tools/microsoft/outlook-mail.ts +392 -100
  227. package/src/agent-tools/tools/microsoft/teams.ts +267 -48
  228. package/src/auth/routes.ts +4 -4
  229. package/src/cli-agent.ts +108 -8
  230. package/src/cli-serve.ts +140 -0
  231. package/src/dashboard/app.js +9 -3
  232. package/src/dashboard/components/knowledge-link.js +15 -0
  233. package/src/dashboard/components/settings-help.js +4 -2
  234. package/src/dashboard/docs/agent-deployment.html +33 -1
  235. package/src/dashboard/docs/settings-network.html +321 -0
  236. package/src/dashboard/docs/settings-security.html +347 -0
  237. package/src/dashboard/docs/settings-tool-security.html +176 -0
  238. package/src/dashboard/docs/settings.html +36 -16
  239. package/src/dashboard/pages/agent-detail/deployment.js +39 -6
  240. package/src/dashboard/pages/agent-detail/tools.js +10 -0
  241. package/src/dashboard/pages/database-access.js +4 -3
  242. package/src/dashboard/pages/settings.js +174 -37
  243. package/src/dashboard/pages/task-pipeline.js +400 -843
  244. package/src/database-access/agent-tools.ts +78 -63
  245. package/src/database-access/connection-manager.ts +13 -2
  246. package/src/database-access/routes.ts +13 -1
  247. package/src/db/adapter.ts +1 -0
  248. package/src/engine/agent-memory.ts +2 -1
  249. package/src/engine/agent-notify.ts +50 -0
  250. package/src/engine/agent-routes.ts +257 -4
  251. package/src/engine/db-adapter.ts +16 -0
  252. package/src/engine/lifecycle.ts +4 -0
  253. package/src/engine/routes.ts +4 -3
  254. package/src/engine/screen-unlock.ts +136 -0
  255. package/src/engine/skills/database-access.ts +78 -0
  256. package/src/engine/skills/index.ts +3 -2
  257. package/src/engine/skills.ts +2 -0
  258. package/src/engine/task-queue-routes.ts +18 -0
  259. package/src/engine/task-queue.ts +15 -2
  260. package/src/middleware/transport-encryption.ts +1 -4
  261. package/src/runtime/agent-loop.ts +4 -0
  262. package/src/runtime/index.ts +15 -6
  263. package/src/server.ts +14 -1
  264. package/src/system-prompts/google/index.ts +1 -2
  265. package/src/system-prompts/index.ts +1 -1
  266. package/src/system-prompts/microsoft/contacts.ts +34 -0
  267. package/src/system-prompts/microsoft/excel.ts +52 -0
  268. package/src/system-prompts/microsoft/index.ts +31 -0
  269. package/src/system-prompts/microsoft/onedrive.ts +41 -0
  270. package/src/system-prompts/microsoft/onenote.ts +36 -0
  271. package/src/system-prompts/microsoft/outlook-calendar.ts +37 -0
  272. package/src/system-prompts/microsoft/outlook-mail.ts +46 -0
  273. package/src/system-prompts/microsoft/planner.ts +37 -0
  274. package/src/system-prompts/microsoft/powerbi.ts +38 -0
  275. package/src/system-prompts/microsoft/powerpoint.ts +35 -0
  276. package/src/system-prompts/microsoft/sharepoint.ts +44 -0
  277. package/src/system-prompts/microsoft/teams.ts +49 -0
  278. package/src/system-prompts/microsoft/todo.ts +37 -0
  279. package/src/types/hono-env.ts +4 -0
  280. package/.github/CODEOWNERS +0 -23
  281. package/.github/workflows/publish-community-skills.yml +0 -121
  282. package/.github/workflows/validate-community-skills.yml +0 -172
  283. package/agriculture_southwest_nigeria_research.txt +0 -10
  284. package/boa_credit_cards_research.txt +0 -10
  285. package/customer_support_research_feb2026.txt +0 -10
  286. package/dist/agent-tools-LRA7PPXG.js +0 -13922
  287. package/dist/agent-tools-VAU5DOQB.js +0 -13910
  288. package/dist/agent-tools-VWV7OWXU.js +0 -13922
  289. package/dist/chunk-2Z7MWTCX.js +0 -4977
  290. package/dist/chunk-3T4XU3VV.js +0 -5010
  291. package/dist/chunk-445QM4NX.js +0 -5061
  292. package/dist/chunk-5TW3Y7DJ.js +0 -1519
  293. package/dist/chunk-6I7VY3LT.js +0 -5060
  294. package/dist/chunk-6W5EK3UP.js +0 -4977
  295. package/dist/chunk-AQMSHJQT.js +0 -5069
  296. package/dist/chunk-ASSQW7HX.js +0 -5051
  297. package/dist/chunk-CIN27FGC.js +0 -5037
  298. package/dist/chunk-CMXY3NUB.js +0 -4977
  299. package/dist/chunk-DRLMRUDP.js +0 -5052
  300. package/dist/chunk-EHI7Z446.js +0 -1519
  301. package/dist/chunk-FEAILFAQ.js +0 -1519
  302. package/dist/chunk-GA3PYBZL.js +0 -1519
  303. package/dist/chunk-GWX63G5J.js +0 -1519
  304. package/dist/chunk-HHMZ4UY6.js +0 -1519
  305. package/dist/chunk-HVQMNF7E.js +0 -4921
  306. package/dist/chunk-HXM7F3YN.js +0 -1519
  307. package/dist/chunk-K6NGOUXG.js +0 -5060
  308. package/dist/chunk-KPG5WINJ.js +0 -4977
  309. package/dist/chunk-LBCUBYDL.js +0 -1519
  310. package/dist/chunk-LIRQSWLR.js +0 -5014
  311. package/dist/chunk-LRCKO5KE.js +0 -1519
  312. package/dist/chunk-M7XL3DJD.js +0 -5069
  313. package/dist/chunk-MHJULEIQ.js +0 -1519
  314. package/dist/chunk-MJGGW6MC.js +0 -106
  315. package/dist/chunk-MMYBDHDB.js +0 -4921
  316. package/dist/chunk-MQT5FXKD.js +0 -1519
  317. package/dist/chunk-OIMPEQF5.js +0 -4977
  318. package/dist/chunk-OOU7JUYE.js +0 -542
  319. package/dist/chunk-OW4GLBHP.js +0 -1519
  320. package/dist/chunk-Q4K4MMLU.js +0 -4977
  321. package/dist/chunk-RUK4CRPF.js +0 -1519
  322. package/dist/chunk-T7H65XQY.js +0 -1519
  323. package/dist/chunk-TQVFWG57.js +0 -5064
  324. package/dist/chunk-UEPK3IMC.js +0 -1519
  325. package/dist/chunk-VUWTXJH6.js +0 -1519
  326. package/dist/chunk-WCPGGSAD.js +0 -1519
  327. package/dist/chunk-WO63NZOJ.js +0 -1519
  328. package/dist/chunk-YPJDRVUM.js +0 -5064
  329. package/dist/chunk-ZROMH5DL.js +0 -4921
  330. package/src/dashboard/docs/_template.txt +0 -92
@@ -263,7 +263,9 @@ export function createAgentRoutes(opts: {
263
263
  router.put('/agents/:id/budget', async (c) => {
264
264
  const config = await c.req.json();
265
265
  try {
266
- await lifecycle.setBudgetConfig(c.req.param('id'), config);
266
+ const aid = c.req.param('id');
267
+ await lifecycle.setBudgetConfig(aid, config);
268
+ import('./agent-notify.js').then(({ notifyAgent }) => notifyAgent(aid, 'budget', lifecycle)).catch(() => {});
267
269
  return c.json({ success: true, budgetConfig: config });
268
270
  } catch (e: any) {
269
271
  return c.json({ error: e.message }, 400);
@@ -366,6 +368,178 @@ export function createAgentRoutes(opts: {
366
368
  }
367
369
  });
368
370
 
371
+ // ─── Port Availability Check ──────────────────────────────
372
+
373
+ router.post('/system/check-port', async (c) => {
374
+ try {
375
+ const { port } = await c.req.json();
376
+ const p = parseInt(port);
377
+ if (!p || p < 1 || p > 65535) {
378
+ return c.json({ available: false, error: 'Invalid port number (1-65535)' });
379
+ }
380
+ // Try to bind to the port on all interfaces to check availability
381
+ const net = await import('net');
382
+ const tryBind = (host: string) => new Promise<boolean>((resolve) => {
383
+ const server = net.createServer();
384
+ server.once('error', () => resolve(false));
385
+ server.once('listening', () => { server.close(() => resolve(true)); });
386
+ server.listen(p, host);
387
+ });
388
+ // Check both 0.0.0.0 and 127.0.0.1 — a port is only available if free on both
389
+ const [availAll, availLocal] = await Promise.all([tryBind('0.0.0.0'), tryBind('127.0.0.1')]);
390
+ const available = availAll && availLocal;
391
+ if (!available) {
392
+ // Try to identify what's using it
393
+ let processInfo = '';
394
+ try {
395
+ const { execSync } = await import('child_process');
396
+ if (process.platform === 'darwin' || process.platform === 'linux') {
397
+ const lsofBin = process.platform === 'darwin' ? '/usr/sbin/lsof' : 'lsof';
398
+ const out = execSync(`${lsofBin} -i :${p} -P -n 2>/dev/null | head -5`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000 }).trim();
399
+ if (out) {
400
+ const lines = out.split('\n').slice(1); // skip header
401
+ if (lines.length > 0) {
402
+ const parts = lines[0].split(/\s+/);
403
+ processInfo = parts[0] ? `${parts[0]} (PID ${parts[1]})` : '';
404
+ }
405
+ }
406
+ } else if (process.platform === 'win32') {
407
+ const out = execSync(`netstat -ano | findstr :${p}`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000 }).trim();
408
+ if (out) {
409
+ const parts = out.split(/\s+/);
410
+ processInfo = `PID ${parts[parts.length - 1]}`;
411
+ }
412
+ }
413
+ } catch {}
414
+ return c.json({ available: false, port: p, inUse: true, process: processInfo || 'Unknown process' });
415
+ }
416
+ return c.json({ available: true, port: p });
417
+ } catch (e: any) {
418
+ return c.json({ available: false, error: e.message });
419
+ }
420
+ });
421
+
422
+ // ─── Screen Unlock ──────────────────────────────────────
423
+
424
+ router.post('/system/unlock-screen', async (c) => {
425
+ try {
426
+ const platform = process.platform;
427
+ if (platform === 'darwin') {
428
+ // macOS: Use AppleScript via osascript to wake and unlock
429
+ const { execSync } = await import('child_process');
430
+ // First wake the display
431
+ try { execSync('caffeinate -u -t 2', { stdio: 'pipe', timeout: 5000 }); } catch {}
432
+ // Check if screen is locked
433
+ const isLocked = (() => {
434
+ try {
435
+ const out = execSync('python3 -c "import Quartz; d=Quartz.CGSessionCopyCurrentDictionary(); print(d.get(\'CGSSessionScreenIsLocked\', 0))"', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000 }).trim();
436
+ return out === '1' || out === 'True';
437
+ } catch {
438
+ // Fallback: check if loginwindow is frontmost
439
+ try {
440
+ const out = execSync('osascript -e \'tell application "System Events" to get name of first application process whose frontmost is true\'', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000 }).trim();
441
+ return out === 'loginwindow' || out === 'ScreenSaverEngine';
442
+ } catch { return false; }
443
+ }
444
+ })();
445
+ if (!isLocked) {
446
+ return c.json({ success: true, wasLocked: false, message: 'Screen is already unlocked' });
447
+ }
448
+ // Get password from agent's security config or request body
449
+ const body = await c.req.json().catch(() => ({}));
450
+ const password = body.password;
451
+ if (!password) {
452
+ return c.json({ success: false, locked: true, error: 'Screen is locked but no password provided. Configure the system password in Settings > Security or the agent\'s Permissions tab.' });
453
+ }
454
+ // Use cliclick or AppleScript to type password and press Enter
455
+ // Method 1: Use osascript to simulate keystrokes at the login window
456
+ try {
457
+ execSync(`osascript -e 'tell application "System Events" to keystroke "${password.replace(/["\\]/g, '\\$&')}"' -e 'delay 0.3' -e 'tell application "System Events" to key code 36'`, {
458
+ stdio: 'pipe', timeout: 10000
459
+ });
460
+ // Wait a moment and check if unlocked
461
+ await new Promise(r => setTimeout(r, 2000));
462
+ const stillLocked = (() => {
463
+ try {
464
+ const out = execSync('python3 -c "import Quartz; d=Quartz.CGSessionCopyCurrentDictionary(); print(d.get(\'CGSSessionScreenIsLocked\', 0))"', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000 }).trim();
465
+ return out === '1' || out === 'True';
466
+ } catch { return false; }
467
+ })();
468
+ if (stillLocked) {
469
+ return c.json({ success: false, error: 'Failed to unlock — password may be incorrect' });
470
+ }
471
+ return c.json({ success: true, wasLocked: true, message: 'Screen unlocked successfully' });
472
+ } catch (e: any) {
473
+ return c.json({ success: false, error: 'Unlock attempt failed: ' + e.message });
474
+ }
475
+ } else if (platform === 'linux') {
476
+ const { execSync } = await import('child_process');
477
+ // Check for common screen lockers and unlock them
478
+ const body = await c.req.json().catch(() => ({}));
479
+ const password = body.password;
480
+ // Try loginctl unlock-session
481
+ try {
482
+ execSync('loginctl unlock-session $(loginctl list-sessions --no-legend | head -1 | awk \'{print $1}\')', { stdio: 'pipe', timeout: 5000 });
483
+ return c.json({ success: true, message: 'Session unlocked via loginctl' });
484
+ } catch {}
485
+ // Try xdotool for X11 based lockers
486
+ if (password) {
487
+ try {
488
+ execSync(`xdotool key --clearmodifiers super; sleep 0.5; xdotool type --clearmodifiers "${password.replace(/["\\]/g, '\\$&')}"; xdotool key Return`, { stdio: 'pipe', timeout: 10000 });
489
+ return c.json({ success: true, message: 'Unlock attempted via xdotool' });
490
+ } catch {}
491
+ }
492
+ return c.json({ success: false, error: 'Could not unlock Linux session. Supported: loginctl, xdotool.' });
493
+ } else if (platform === 'win32') {
494
+ return c.json({ success: false, error: 'Windows unlock not yet supported. Use Remote Desktop or disable lock screen.' });
495
+ } else {
496
+ return c.json({ success: false, error: `Unsupported platform: ${platform}` });
497
+ }
498
+ } catch (e: any) {
499
+ return c.json({ success: false, error: e.message });
500
+ }
501
+ });
502
+
503
+ router.get('/system/screen-status', async (c) => {
504
+ try {
505
+ const platform = process.platform;
506
+ if (platform === 'darwin') {
507
+ const { execSync } = await import('child_process');
508
+ const isLocked = (() => {
509
+ try {
510
+ const out = execSync('python3 -c "import Quartz; d=Quartz.CGSessionCopyCurrentDictionary(); print(d.get(\'CGSSessionScreenIsLocked\', 0))"', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000 }).trim();
511
+ return out === '1' || out === 'True';
512
+ } catch {
513
+ try {
514
+ const out = execSync('osascript -e \'tell application "System Events" to get name of first application process whose frontmost is true\'', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000 }).trim();
515
+ return out === 'loginwindow' || out === 'ScreenSaverEngine';
516
+ } catch { return false; }
517
+ }
518
+ })();
519
+ // Check if display is asleep
520
+ const displayAsleep = (() => {
521
+ try {
522
+ const out = execSync('ioreg -r -d 1 -k IODisplayWrangler | grep -i "currentpowerstate"', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000 });
523
+ return out.includes('= 0') || out.includes('= 1');
524
+ } catch { return false; }
525
+ })();
526
+ return c.json({ locked: isLocked, displayAsleep, platform: 'macOS' });
527
+ } else if (platform === 'linux') {
528
+ const { execSync } = await import('child_process');
529
+ const isLocked = (() => {
530
+ try {
531
+ const out = execSync('loginctl show-session $(loginctl list-sessions --no-legend | head -1 | awk \'{print $1}\') -p LockedHint --value', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000 }).trim();
532
+ return out === 'yes';
533
+ } catch { return false; }
534
+ })();
535
+ return c.json({ locked: isLocked, platform: 'Linux' });
536
+ }
537
+ return c.json({ locked: false, platform });
538
+ } catch (e: any) {
539
+ return c.json({ locked: false, error: e.message });
540
+ }
541
+ });
542
+
369
543
  // ─── Agent Creation Bridge ──────────────────────────────
370
544
 
371
545
  /**
@@ -1128,7 +1302,7 @@ export function createAgentRoutes(opts: {
1128
1302
 
1129
1303
  // Also clear directly in DB to ensure no stale data
1130
1304
  try {
1131
- const db = lifecycle.getDb?.() || (globalThis as any).__engineDb;
1305
+ const db = (lifecycle as any).getDb?.() || (globalThis as any).__engineDb;
1132
1306
  if (db) {
1133
1307
  const isPostgres = !!(db as any).pool;
1134
1308
  if (isPostgres) {
@@ -1315,6 +1489,85 @@ export function createAgentRoutes(opts: {
1315
1489
  icon: Emoji.telegram || Emoji.chat, requiresIntegration: 'telegram',
1316
1490
  tools: ['telegram_send', 'telegram_send_media', 'telegram_get_me', 'telegram_get_chat'],
1317
1491
  },
1492
+ // ── Microsoft 365 ──────────────────────────────────
1493
+ {
1494
+ id: 'outlook_mail', name: 'Outlook Mail', description: 'Full email management — inbox, send, reply, forward, search, threads, drafts, rules, auto-reply, categories',
1495
+ icon: Emoji.envelope, requiresOAuth: 'microsoft',
1496
+ tools: ['outlook_mail_list', 'outlook_mail_read', 'outlook_mail_thread', 'outlook_mail_send', 'outlook_mail_reply',
1497
+ 'outlook_mail_forward', 'outlook_mail_move', 'outlook_mail_delete', 'outlook_mail_update', 'outlook_mail_search',
1498
+ 'outlook_mail_draft', 'outlook_mail_send_draft', 'outlook_mail_folders', 'outlook_mail_create_folder',
1499
+ 'outlook_mail_attachment_download', 'outlook_mail_auto_reply', 'outlook_mail_get_auto_reply',
1500
+ 'outlook_mail_rules', 'outlook_mail_categories', 'outlook_mail_profile'],
1501
+ },
1502
+ {
1503
+ id: 'outlook_calendar', name: 'Outlook Calendar', description: 'Calendar events, scheduling, free/busy lookup, Teams meeting creation, invite responses',
1504
+ icon: Emoji.calendar, requiresOAuth: 'microsoft',
1505
+ tools: ['outlook_calendar_list', 'outlook_calendar_events', 'outlook_calendar_create', 'outlook_calendar_update',
1506
+ 'outlook_calendar_delete', 'outlook_calendar_respond', 'outlook_calendar_freebusy'],
1507
+ },
1508
+ {
1509
+ id: 'onedrive', name: 'OneDrive', description: 'Cloud file management — list, search, read, upload, share, create folders',
1510
+ icon: Emoji.folder, requiresOAuth: 'microsoft',
1511
+ tools: ['onedrive_list', 'onedrive_search', 'onedrive_read', 'onedrive_upload', 'onedrive_create_folder',
1512
+ 'onedrive_delete', 'onedrive_share'],
1513
+ },
1514
+ {
1515
+ id: 'teams', name: 'Microsoft Teams', description: 'Team messaging, channels, chats, file sharing, presence, member management',
1516
+ icon: Emoji.chat, requiresOAuth: 'microsoft',
1517
+ tools: ['teams_list_teams', 'teams_list_channels', 'teams_create_channel', 'teams_send_channel_message',
1518
+ 'teams_reply_to_message', 'teams_read_channel_messages', 'teams_list_chats', 'teams_send_chat_message',
1519
+ 'teams_read_chat_messages', 'teams_list_members', 'teams_add_member', 'teams_share_file',
1520
+ 'teams_presence', 'teams_set_status'],
1521
+ },
1522
+ {
1523
+ id: 'todo', name: 'Microsoft To Do', description: 'Task lists, task CRUD with due dates, reminders, and importance',
1524
+ icon: Emoji.check, requiresOAuth: 'microsoft',
1525
+ tools: ['todo_list_lists', 'todo_list_tasks', 'todo_create_task', 'todo_update_task', 'todo_delete_task', 'todo_create_list'],
1526
+ },
1527
+ {
1528
+ id: 'outlook_contacts', name: 'Outlook Contacts', description: 'Contact management, address book, people search',
1529
+ icon: Emoji.people, requiresOAuth: 'microsoft',
1530
+ tools: ['outlook_contacts_list', 'outlook_contacts_create', 'outlook_contacts_update', 'outlook_contacts_delete', 'outlook_people_search'],
1531
+ },
1532
+ {
1533
+ id: 'excel', name: 'Microsoft Excel', description: 'Read/write cells, ranges, tables, worksheets, formulas, charts, formatting',
1534
+ icon: Emoji.chartUp, requiresOAuth: 'microsoft',
1535
+ tools: ['excel_list_worksheets', 'excel_read_range', 'excel_write_range', 'excel_add_row', 'excel_list_tables',
1536
+ 'excel_read_table', 'excel_create_worksheet', 'excel_create_session', 'excel_close_session',
1537
+ 'excel_evaluate_formula', 'excel_named_ranges', 'excel_read_named_range', 'excel_list_charts',
1538
+ 'excel_chart_image', 'excel_pivot_refresh', 'excel_set_cell_format'],
1539
+ },
1540
+ {
1541
+ id: 'sharepoint', name: 'SharePoint', description: 'Sites, document libraries, lists, search, file management across SharePoint Online',
1542
+ icon: Emoji.database, requiresOAuth: 'microsoft',
1543
+ tools: ['sharepoint_list_sites', 'sharepoint_get_site', 'sharepoint_list_drives', 'sharepoint_list_files',
1544
+ 'sharepoint_upload_file', 'sharepoint_list_lists', 'sharepoint_list_items', 'sharepoint_create_list_item',
1545
+ 'sharepoint_update_list_item', 'sharepoint_search'],
1546
+ },
1547
+ {
1548
+ id: 'onenote', name: 'OneNote', description: 'Notebooks, sections, pages — read, create, and update notes',
1549
+ icon: Emoji.note, requiresOAuth: 'microsoft',
1550
+ tools: ['onenote_list_notebooks', 'onenote_list_sections', 'onenote_list_pages', 'onenote_read_page',
1551
+ 'onenote_create_page', 'onenote_update_page'],
1552
+ },
1553
+ {
1554
+ id: 'powerpoint', name: 'PowerPoint', description: 'Presentation metadata, PDF export, thumbnails, templates, embed URLs',
1555
+ icon: Emoji.art, requiresOAuth: 'microsoft',
1556
+ tools: ['powerpoint_get_info', 'powerpoint_export_pdf', 'powerpoint_get_thumbnails',
1557
+ 'powerpoint_create_from_template', 'powerpoint_get_embed_url'],
1558
+ },
1559
+ {
1560
+ id: 'planner', name: 'Microsoft Planner', description: 'Project boards — plans, buckets, tasks (Kanban-style task management)',
1561
+ icon: Emoji.clipboard, requiresOAuth: 'microsoft',
1562
+ tools: ['planner_list_plans', 'planner_list_buckets', 'planner_list_tasks', 'planner_create_task',
1563
+ 'planner_update_task', 'planner_delete_task'],
1564
+ },
1565
+ {
1566
+ id: 'powerbi', name: 'Power BI', description: 'Workspaces, reports, dashboards, datasets, DAX queries, data refresh',
1567
+ icon: Emoji.barChart, requiresOAuth: 'microsoft',
1568
+ tools: ['powerbi_list_workspaces', 'powerbi_list_reports', 'powerbi_list_dashboards', 'powerbi_list_datasets',
1569
+ 'powerbi_refresh_dataset', 'powerbi_refresh_history', 'powerbi_execute_query', 'powerbi_dashboard_tiles'],
1570
+ },
1318
1571
  ];
1319
1572
 
1320
1573
  // ═══════════════════════════════════════════════════════════
@@ -1845,7 +2098,7 @@ export function createAgentRoutes(opts: {
1845
2098
  if (body.requireApproval) profile.requireApproval = body.requireApproval;
1846
2099
  if (body.rateLimits) profile.rateLimits = body.rateLimits;
1847
2100
  if (body.constraints) profile.constraints = body.constraints;
1848
- permissions.setProfile(agentId, profile, managed.org_id);
2101
+ permissions.setProfile(agentId, profile, managed.orgId);
1849
2102
  }
1850
2103
 
1851
2104
  managed.updatedAt = new Date().toISOString();
@@ -1948,7 +2201,7 @@ export function createAgentRoutes(opts: {
1948
2201
  }
1949
2202
  profile.tools.blocked = [...newBlocked];
1950
2203
  profile.tools.allowed = [...newAllowed];
1951
- permissions.setProfile(agentId, profile, managed.org_id);
2204
+ permissions.setProfile(agentId, profile, managed.orgId);
1952
2205
  }
1953
2206
 
1954
2207
  managed.updatedAt = new Date().toISOString();
@@ -237,6 +237,19 @@ export class EngineDatabase {
237
237
  return this.db.all<T>(sql, params);
238
238
  }
239
239
 
240
+ async get<T = any>(sql: string, params?: any[]): Promise<T | undefined> {
241
+ return this.db.get<T>(sql, params);
242
+ }
243
+
244
+ async getSettings(): Promise<Record<string, any>> {
245
+ const rows = await this.db.all<any>('SELECT key, value FROM engine_settings', []);
246
+ const result: Record<string, any> = {};
247
+ for (const row of rows) {
248
+ try { result[row.key] = JSON.parse(row.value); } catch { result[row.key] = row.value; }
249
+ }
250
+ return result;
251
+ }
252
+
240
253
  async execute(sql: string, params?: any[]): Promise<void> {
241
254
  return this.db.run(sql, params);
242
255
  }
@@ -1095,6 +1108,9 @@ export class EngineDatabase {
1095
1108
  const config = sj(row.config);
1096
1109
  const agent: ManagedAgent = {
1097
1110
  id: row.id,
1111
+ name: row.display_name || row.name || config?.displayName || config?.name || row.id,
1112
+ displayName: row.display_name || config?.displayName || config?.name,
1113
+ display_name: row.display_name || config?.displayName || config?.name,
1098
1114
  orgId: row.org_id,
1099
1115
  config,
1100
1116
  state: row.state,
@@ -39,6 +39,8 @@ export interface ManagedAgent {
39
39
  displayName?: string; // Human-facing display name
40
40
  display_name?: string; // Snake_case alias (DB compat)
41
41
  orgId: string; // Which company owns this agent
42
+ org_id?: string; // Snake_case alias (DB compat)
43
+ client_org_id?: string; // Client org binding (if external)
42
44
  config: AgentConfig;
43
45
  state: AgentState;
44
46
  permissionProfileId?: string; // Permission profile reference
@@ -1202,6 +1204,8 @@ export class AgentLifecycleManager {
1202
1204
  await this.persistAgent(agent);
1203
1205
  // Emit generic agent save event — all config keys may have changed
1204
1206
  configBus.emitAgentConfig(agentId, '_save', undefined, 'lifecycle');
1207
+ // Push config to standalone agent process in real-time
1208
+ import('./agent-notify.js').then(({ notifyAgent }) => notifyAgent(agentId, 'config', this)).catch(() => {});
1205
1209
  }
1206
1210
  }
1207
1211
 
@@ -96,6 +96,10 @@ import type { DatabaseAdapter } from '../db/adapter.js';
96
96
  const engine = new Hono<AppEnv>();
97
97
  let _engineApp: Hono<AppEnv> = engine;
98
98
 
99
+ // Forward declarations (set later via setEngineDb)
100
+ let _engineDb: import('./db-adapter.js').EngineDatabase | null = null;
101
+ let _adminDb: DatabaseAdapter | null = null;
102
+
99
103
  // ─── Shared Instances ───────────────────────────────────
100
104
 
101
105
  const permissionEngine = new PermissionEngine(FULL_SKILL_DEFINITIONS);
@@ -754,9 +758,6 @@ engine.post('/email-poller/rediscover', async (c) => {
754
758
 
755
759
  // ─── setEngineDb ────────────────────────────────────────
756
760
 
757
- let _engineDb: import('./db-adapter.js').EngineDatabase | null = null;
758
- let _adminDb: DatabaseAdapter | null = null;
759
-
760
761
  export async function setEngineDb(
761
762
  db: import('./db-adapter.js').EngineDatabase,
762
763
  adminDb?: DatabaseAdapter,
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Screen Unlock & Machine Access Utilities
3
+ *
4
+ * Provides auto-unlock functionality for macOS and Linux.
5
+ * Called by the heartbeat system, agent startup, and browser automation
6
+ * when the screen is detected as locked.
7
+ */
8
+
9
+ import { execSync } from 'child_process';
10
+
11
+ let _caffeinate: any = null;
12
+
13
+ /**
14
+ * Check if the screen is currently locked.
15
+ */
16
+ export function isScreenLocked(): boolean {
17
+ try {
18
+ if (process.platform === 'darwin') {
19
+ try {
20
+ const out = execSync('python3 -c "import Quartz; d=Quartz.CGSessionCopyCurrentDictionary(); print(d.get(\'CGSSessionScreenIsLocked\', 0))"', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000 }).trim();
21
+ return out === '1' || out === 'True';
22
+ } catch {
23
+ try {
24
+ const out = execSync('osascript -e \'tell application "System Events" to get name of first application process whose frontmost is true\'', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000 }).trim();
25
+ return out === 'loginwindow' || out === 'ScreenSaverEngine';
26
+ } catch { return false; }
27
+ }
28
+ } else if (process.platform === 'linux') {
29
+ try {
30
+ const out = execSync('loginctl show-session $(loginctl list-sessions --no-legend | head -1 | awk \'{print $1}\') -p LockedHint --value', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000 }).trim();
31
+ return out === 'yes';
32
+ } catch { return false; }
33
+ }
34
+ } catch {}
35
+ return false;
36
+ }
37
+
38
+ /**
39
+ * Attempt to unlock the screen with the given password.
40
+ * Returns true if successful.
41
+ */
42
+ export async function unlockScreen(password: string): Promise<{ success: boolean; message: string }> {
43
+ if (!password) return { success: false, message: 'No password provided' };
44
+
45
+ try {
46
+ if (process.platform === 'darwin') {
47
+ // Wake the display first
48
+ try { execSync('caffeinate -u -t 2', { stdio: 'pipe', timeout: 5000 }); } catch {}
49
+ await new Promise(r => setTimeout(r, 500));
50
+
51
+ if (!isScreenLocked()) {
52
+ return { success: true, message: 'Screen is already unlocked' };
53
+ }
54
+
55
+ // Type password using AppleScript
56
+ const escaped = password.replace(/["\\]/g, '\\$&');
57
+ execSync(`osascript -e 'tell application "System Events" to keystroke "${escaped}"' -e 'delay 0.3' -e 'tell application "System Events" to key code 36'`, {
58
+ stdio: 'pipe', timeout: 10000
59
+ });
60
+
61
+ // Wait and verify
62
+ await new Promise(r => setTimeout(r, 2000));
63
+ if (isScreenLocked()) {
64
+ return { success: false, message: 'Failed to unlock — password may be incorrect' };
65
+ }
66
+ return { success: true, message: 'Screen unlocked successfully' };
67
+ } else if (process.platform === 'linux') {
68
+ // Try loginctl first
69
+ try {
70
+ execSync('loginctl unlock-session $(loginctl list-sessions --no-legend | head -1 | awk \'{print $1}\')', { stdio: 'pipe', timeout: 5000 });
71
+ return { success: true, message: 'Session unlocked via loginctl' };
72
+ } catch {}
73
+ // Try xdotool
74
+ try {
75
+ const escaped = password.replace(/["\\]/g, '\\$&');
76
+ execSync(`xdotool key --clearmodifiers super; sleep 0.5; xdotool type --clearmodifiers "${escaped}"; xdotool key Return`, { stdio: 'pipe', timeout: 10000 });
77
+ return { success: true, message: 'Unlock attempted via xdotool' };
78
+ } catch {}
79
+ return { success: false, message: 'Could not unlock Linux session' };
80
+ }
81
+ return { success: false, message: `Unsupported platform: ${process.platform}` };
82
+ } catch (e: any) {
83
+ return { success: false, message: e.message };
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Ensure the screen is unlocked. Uses the security config to get the password.
89
+ * Call this before any operation that requires screen access (browser, desktop automation).
90
+ */
91
+ export async function ensureScreenUnlocked(getSecurityConfig: () => Promise<any>): Promise<boolean> {
92
+ if (!isScreenLocked()) return true;
93
+
94
+ try {
95
+ const config = await getSecurityConfig();
96
+ const screenAccess = config?.screenAccess;
97
+ if (!screenAccess?.enabled || !screenAccess?.autoUnlock) return false;
98
+ if (!screenAccess?.systemPassword) return false;
99
+
100
+ const result = await unlockScreen(screenAccess.systemPassword);
101
+ return result.success;
102
+ } catch {
103
+ return false;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Start caffeinate to prevent system sleep. Call once at server startup if configured.
109
+ */
110
+ export function startPreventSleep(): void {
111
+ if (_caffeinate) return; // Already running
112
+ if (process.platform !== 'darwin' && process.platform !== 'linux') return;
113
+
114
+ try {
115
+ const { spawn } = require('child_process');
116
+ if (process.platform === 'darwin') {
117
+ // caffeinate -d prevents display sleep, -i prevents idle sleep
118
+ _caffeinate = spawn('caffeinate', ['-d', '-i'], { stdio: 'ignore', detached: true });
119
+ _caffeinate.unref();
120
+ } else {
121
+ // Linux: systemd-inhibit
122
+ _caffeinate = spawn('systemd-inhibit', ['--what=idle:sleep', '--who=agenticmail', '--why=Agent activity', 'sleep', 'infinity'], { stdio: 'ignore', detached: true });
123
+ _caffeinate.unref();
124
+ }
125
+ } catch {}
126
+ }
127
+
128
+ /**
129
+ * Stop preventing system sleep.
130
+ */
131
+ export function stopPreventSleep(): void {
132
+ if (_caffeinate) {
133
+ try { _caffeinate.kill(); } catch {}
134
+ _caffeinate = null;
135
+ }
136
+ }
@@ -0,0 +1,78 @@
1
+ import { Emoji } from '../emoji.js';
2
+ import type { SkillDefinition, ToolDefinition } from '../skills.js';
3
+
4
+ export const SKILL_DEF: Omit<SkillDefinition, 'tools'> = {
5
+ id: 'database-access',
6
+ name: 'External Database Access',
7
+ description: 'Query external databases (Postgres, MySQL, MongoDB, Redis, Supabase, etc.) that have been granted to this agent by an admin. Supports read/write operations based on granted permissions.',
8
+ category: 'database',
9
+ risk: 'medium',
10
+ icon: Emoji.database,
11
+ source: 'builtin',
12
+ version: '1.0.0',
13
+ author: 'AgenticMail',
14
+ };
15
+
16
+ export const TOOLS: ToolDefinition[] = [
17
+ {
18
+ id: 'db_list_connections',
19
+ name: 'List Database Connections',
20
+ description: 'List all external database connections this agent has been granted access to.',
21
+ category: 'read',
22
+ risk: 'low',
23
+ skillId: 'database-access',
24
+ sideEffects: [],
25
+ parameters: { type: 'object', properties: {} },
26
+ },
27
+ {
28
+ id: 'db_query',
29
+ name: 'Query External Database',
30
+ description: 'Execute a SQL query on a granted external database connection.',
31
+ category: 'read',
32
+ risk: 'medium',
33
+ skillId: 'database-access',
34
+ sideEffects: ['database_write'],
35
+ parameters: {
36
+ type: 'object',
37
+ properties: {
38
+ connectionId: { type: 'string', description: 'Database connection ID' },
39
+ sql: { type: 'string', description: 'SQL query to execute' },
40
+ params: { type: 'array', items: { type: 'string' }, description: 'Query parameters' },
41
+ },
42
+ required: ['connectionId', 'sql'],
43
+ },
44
+ },
45
+ {
46
+ id: 'db_describe_table',
47
+ name: 'Describe Table Schema',
48
+ description: 'Get the schema (columns, types, constraints) of a table in an external database.',
49
+ category: 'read',
50
+ risk: 'low',
51
+ skillId: 'database-access',
52
+ sideEffects: ['network-request'],
53
+ parameters: {
54
+ type: 'object',
55
+ properties: {
56
+ connectionId: { type: 'string', description: 'Database connection ID' },
57
+ table: { type: 'string', description: 'Table name' },
58
+ },
59
+ required: ['connectionId', 'table'],
60
+ },
61
+ },
62
+ {
63
+ id: 'db_list_tables',
64
+ name: 'List Tables',
65
+ description: 'List all tables in an external database connection.',
66
+ category: 'read',
67
+ risk: 'low',
68
+ skillId: 'database-access',
69
+ sideEffects: ['network-request'],
70
+ parameters: {
71
+ type: 'object',
72
+ properties: {
73
+ connectionId: { type: 'string', description: 'Database connection ID' },
74
+ },
75
+ required: ['connectionId'],
76
+ },
77
+ },
78
+ ];
@@ -101,6 +101,7 @@ export const AGENTICMAIL_TOOLS: ToolDefinition[] = AGENTICMAIL_MODULES.flatMap(m
101
101
  // ─── Enterprise Utility Skills ─────────────────────────
102
102
 
103
103
  import * as EntDatabase from './enterprise-database.js';
104
+ import * as DatabaseAccess from './database-access.js';
104
105
  import * as EntSpreadsheet from './enterprise-spreadsheet.js';
105
106
  import * as EntDocuments from './enterprise-documents.js';
106
107
  import * as EntHttp from './enterprise-http.js';
@@ -109,7 +110,7 @@ import * as EntCodeSandbox from './enterprise-code-sandbox.js';
109
110
  import * as EntDiff from './enterprise-diff.js';
110
111
 
111
112
  export const ENTERPRISE_MODULES = [
112
- EntDatabase, EntSpreadsheet, EntDocuments, EntHttp,
113
+ EntDatabase, DatabaseAccess, EntSpreadsheet, EntDocuments, EntHttp,
113
114
  EntSecurityScan, EntCodeSandbox, EntDiff,
114
115
  ] as const;
115
116
 
@@ -152,7 +153,7 @@ export {
152
153
  GwsSheets, GwsSlides, GwsMeet, GwsChat,
153
154
  GwsForms, GwsSites, GwsKeep, GwsAdmin,
154
155
  GwsVault, GwsGroups, GwsMaps, GwsContacts, GwsTasks,
155
- EntDatabase, EntSpreadsheet, EntDocuments, EntHttp,
156
+ EntDatabase, DatabaseAccess, EntSpreadsheet, EntDocuments, EntHttp,
156
157
  EntSecurityScan, EntCodeSandbox, EntDiff,
157
158
  CoreTools, MeetingLifecycle, AgentMemory, VisualMemory, KnowledgeSearch,
158
159
  };
@@ -481,6 +481,8 @@ export class PermissionEngine {
481
481
  console.error(`[permissions] Failed to persist profile for agent ${agentId}:`, err);
482
482
  });
483
483
  }
484
+ // Push to standalone agent in real-time
485
+ import('./agent-notify.js').then(({ notifyAgent }) => notifyAgent(agentId, 'permissions')).catch(() => {});
484
486
  }
485
487
 
486
488
  getProfile(agentId: string): AgentPermissionProfile | undefined {
@@ -72,6 +72,24 @@ export function createTaskQueueRoutes(taskQueue: TaskQueueManager) {
72
72
  });
73
73
  });
74
74
 
75
+ // POST /task-pipeline/webhook — receive task events from standalone agent processes
76
+ // Agents create/update tasks in shared DB but SSE subscribers are on this process.
77
+ // This endpoint re-emits the event to all SSE subscribers for real-time updates.
78
+ router.post('/webhook', async (c) => {
79
+ try {
80
+ const event = await c.req.json();
81
+ if (event && event.task) {
82
+ // Re-emit to local SSE subscribers
83
+ for (const l of (taskQueue as any).listeners || []) {
84
+ try { l(event); } catch { /* ignore */ }
85
+ }
86
+ }
87
+ return c.json({ ok: true });
88
+ } catch {
89
+ return c.json({ ok: false }, 400);
90
+ }
91
+ });
92
+
75
93
  // GET /task-pipeline/:id — single task detail
76
94
  router.get('/:id', (c) => {
77
95
  const task = taskQueue.getTask(c.req.param('id'));