@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
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Microsoft Outlook Mail Tools
3
3
  *
4
- * Full Outlook/Exchange mail management via Microsoft Graph API.
5
- * Covers inbox, send, reply, forward, drafts, folders, attachments, search.
4
+ * Comprehensive Outlook/Exchange mail management via Microsoft Graph API.
5
+ * 20 tools covering inbox, send, reply, forward, drafts, folders, attachments,
6
+ * search, threads, rules, auto-reply, signatures, and categories.
6
7
  */
7
8
 
8
9
  import type { AnyAgentTool, ToolCreationOptions } from '../../types.js';
@@ -10,21 +11,61 @@ import { jsonResult, errorResult } from '../../common.js';
10
11
  import type { MicrosoftToolsConfig } from './index.js';
11
12
  import { graph } from './graph-api.js';
12
13
 
14
+ function mapMessage(m: any, full = false): any {
15
+ const result: any = {
16
+ id: m.id,
17
+ subject: m.subject,
18
+ from: m.from?.emailAddress?.address,
19
+ fromName: m.from?.emailAddress?.name,
20
+ to: m.toRecipients?.map((r: any) => r.emailAddress?.address),
21
+ date: m.receivedDateTime,
22
+ isRead: m.isRead,
23
+ hasAttachments: m.hasAttachments,
24
+ importance: m.importance,
25
+ conversationId: m.conversationId,
26
+ };
27
+ if (!full) {
28
+ result.preview = m.bodyPreview;
29
+ result.flagged = m.flag?.flagStatus === 'flagged';
30
+ result.cc = m.ccRecipients?.map((r: any) => r.emailAddress?.address);
31
+ } else {
32
+ result.to = m.toRecipients?.map((r: any) => ({ email: r.emailAddress?.address, name: r.emailAddress?.name }));
33
+ result.cc = m.ccRecipients?.map((r: any) => ({ email: r.emailAddress?.address, name: r.emailAddress?.name }));
34
+ result.bcc = m.bccRecipients?.map((r: any) => ({ email: r.emailAddress?.address, name: r.emailAddress?.name }));
35
+ result.replyTo = m.replyTo?.map((r: any) => r.emailAddress?.address);
36
+ result.sentDate = m.sentDateTime;
37
+ result.body = m.body?.content;
38
+ result.bodyType = m.body?.contentType;
39
+ result.internetMessageId = m.internetMessageId;
40
+ result.categories = m.categories;
41
+ result.flagged = m.flag?.flagStatus === 'flagged';
42
+ }
43
+ return result;
44
+ }
45
+
46
+ const LIST_SELECT = 'id,subject,from,toRecipients,ccRecipients,receivedDateTime,isRead,hasAttachments,importance,bodyPreview,flag,conversationId,categories';
47
+ const FULL_SELECT = 'id,subject,from,toRecipients,ccRecipients,bccRecipients,replyTo,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,body,flag,conversationId,internetMessageId,parentFolderId,categories';
48
+
49
+ function parseRecipients(str: string) {
50
+ return str.split(',').map((e: string) => ({ emailAddress: { address: e.trim() } }));
51
+ }
52
+
13
53
  export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?: ToolCreationOptions): AnyAgentTool[] {
14
54
  const tp = config.tokenProvider;
15
55
 
16
56
  return [
17
57
  {
18
58
  name: 'outlook_mail_list',
19
- description: 'List messages from Outlook mailbox. Returns recent emails from inbox or specified folder.',
59
+ description: 'List messages from Outlook mailbox. Returns recent emails from inbox or specified folder. Supports OData filtering and search.',
20
60
  category: 'utility' as const,
21
61
  parameters: {
22
62
  type: 'object' as const,
23
63
  properties: {
24
- folder: { type: 'string', description: 'Folder: inbox, sentitems, drafts, deleteditems, junkemail, or folder ID (default: inbox)' },
64
+ folder: { type: 'string', description: 'Folder: inbox, sentitems, drafts, deleteditems, junkemail, archive, or folder ID (default: inbox)' },
25
65
  maxResults: { type: 'number', description: 'Max messages to return (default: 20, max: 50)' },
26
- filter: { type: 'string', description: 'OData $filter expression (e.g., "isRead eq false", "from/emailAddress/address eq \'user@example.com\'")' },
27
- search: { type: 'string', description: 'Search query (searches subject, body, sender)' },
66
+ filter: { type: 'string', description: 'OData $filter (e.g., "isRead eq false", "from/emailAddress/address eq \'user@example.com\'", "hasAttachments eq true", "importance eq \'high\'")' },
67
+ search: { type: 'string', description: 'Search query (searches subject, body, sender — natural language or KQL)' },
68
+ orderBy: { type: 'string', description: 'Sort order (default: "receivedDateTime desc"). Options: receivedDateTime, from/emailAddress/address, subject, importance' },
28
69
  },
29
70
  required: [],
30
71
  },
@@ -35,56 +76,43 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
35
76
  const top = Math.min(params.maxResults || 20, 50);
36
77
  const query: Record<string, string> = {
37
78
  '$top': String(top),
38
- '$orderby': 'receivedDateTime desc',
39
- '$select': 'id,subject,from,toRecipients,ccRecipients,receivedDateTime,isRead,hasAttachments,importance,bodyPreview,flag,conversationId',
79
+ '$orderby': params.orderBy || 'receivedDateTime desc',
80
+ '$select': LIST_SELECT,
40
81
  };
41
82
  if (params.filter) query['$filter'] = params.filter;
42
83
  if (params.search) query['$search'] = `"${params.search}"`;
43
-
44
84
  const data = await graph(token, `/me/mailFolders/${folder}/messages`, { query });
45
- const messages = (data.value || []).map((m: any) => ({
46
- id: m.id,
47
- subject: m.subject,
48
- from: m.from?.emailAddress?.address,
49
- fromName: m.from?.emailAddress?.name,
50
- to: m.toRecipients?.map((r: any) => r.emailAddress?.address),
51
- cc: m.ccRecipients?.map((r: any) => r.emailAddress?.address),
52
- date: m.receivedDateTime,
53
- isRead: m.isRead,
54
- hasAttachments: m.hasAttachments,
55
- importance: m.importance,
56
- preview: m.bodyPreview,
57
- flagged: m.flag?.flagStatus === 'flagged',
58
- conversationId: m.conversationId,
59
- }));
60
- return jsonResult({ messages, count: messages.length });
85
+ const messages = (data.value || []).map((m: any) => mapMessage(m));
86
+ return jsonResult({ messages, count: messages.length, folder });
61
87
  } catch (e: any) { return errorResult(e.message); }
62
88
  },
63
89
  },
64
90
 
65
91
  {
66
92
  name: 'outlook_mail_read',
67
- description: 'Read a specific email message by ID. Returns full body, headers, and attachment list.',
93
+ description: 'Read a specific email message by ID. Returns full body, headers, attachments list, and categories.',
68
94
  category: 'utility' as const,
69
95
  parameters: {
70
96
  type: 'object' as const,
71
97
  properties: {
72
98
  messageId: { type: 'string', description: 'Message ID to read' },
73
99
  markAsRead: { type: 'boolean', description: 'Mark as read when opening (default: true)' },
100
+ preferText: { type: 'boolean', description: 'Request plain text body instead of HTML (default: false)' },
74
101
  },
75
102
  required: ['messageId'],
76
103
  },
77
104
  async execute(_id: string, params: any) {
78
105
  try {
79
106
  const token = await tp.getAccessToken();
107
+ const headers: Record<string, string> = {};
108
+ if (params.preferText) headers['Prefer'] = 'outlook.body-content-type="text"';
80
109
  const msg = await graph(token, `/me/messages/${params.messageId}`, {
81
- query: { '$select': 'id,subject,from,toRecipients,ccRecipients,bccRecipients,replyTo,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,body,flag,conversationId,internetMessageId,parentFolderId' }
110
+ query: { '$select': FULL_SELECT },
111
+ headers,
82
112
  });
83
- // Mark as read
84
113
  if (params.markAsRead !== false && !msg.isRead) {
85
114
  graph(token, `/me/messages/${params.messageId}`, { method: 'PATCH', body: { isRead: true } }).catch(() => {});
86
115
  }
87
- // Get attachments if any
88
116
  let attachments: any[] = [];
89
117
  if (msg.hasAttachments) {
90
118
  const att = await graph(token, `/me/messages/${params.messageId}/attachments`, {
@@ -95,32 +123,53 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
95
123
  size: a.size, isInline: a.isInline,
96
124
  }));
97
125
  }
98
- return jsonResult({
99
- id: msg.id,
100
- subject: msg.subject,
101
- from: msg.from?.emailAddress?.address,
102
- fromName: msg.from?.emailAddress?.name,
103
- to: msg.toRecipients?.map((r: any) => ({ email: r.emailAddress?.address, name: r.emailAddress?.name })),
104
- cc: msg.ccRecipients?.map((r: any) => ({ email: r.emailAddress?.address, name: r.emailAddress?.name })),
105
- bcc: msg.bccRecipients?.map((r: any) => ({ email: r.emailAddress?.address, name: r.emailAddress?.name })),
106
- replyTo: msg.replyTo?.map((r: any) => r.emailAddress?.address),
107
- date: msg.receivedDateTime,
108
- sentDate: msg.sentDateTime,
109
- body: msg.body?.content,
110
- bodyType: msg.body?.contentType,
111
- isRead: msg.isRead,
112
- importance: msg.importance,
113
- attachments,
114
- conversationId: msg.conversationId,
115
- internetMessageId: msg.internetMessageId,
126
+ const result = mapMessage(msg, true);
127
+ result.attachments = attachments;
128
+ result.attachmentCount = attachments.length;
129
+ return jsonResult(result);
130
+ } catch (e: any) { return errorResult(e.message); }
131
+ },
132
+ },
133
+
134
+ {
135
+ name: 'outlook_mail_thread',
136
+ description: 'Get all messages in a conversation thread. Groups related emails by conversationId for context.',
137
+ category: 'utility' as const,
138
+ parameters: {
139
+ type: 'object' as const,
140
+ properties: {
141
+ conversationId: { type: 'string', description: 'Conversation ID (from any message in the thread)' },
142
+ messageId: { type: 'string', description: 'Message ID — will auto-find its conversationId' },
143
+ maxResults: { type: 'number', description: 'Max messages in thread (default: 25)' },
144
+ },
145
+ required: [],
146
+ },
147
+ async execute(_id: string, params: any) {
148
+ try {
149
+ const token = await tp.getAccessToken();
150
+ let convId = params.conversationId;
151
+ if (!convId && params.messageId) {
152
+ const msg = await graph(token, `/me/messages/${params.messageId}`, { query: { '$select': 'conversationId' } });
153
+ convId = msg.conversationId;
154
+ }
155
+ if (!convId) throw new Error('Provide conversationId or messageId');
156
+ const data = await graph(token, '/me/messages', {
157
+ query: {
158
+ '$filter': `conversationId eq '${convId}'`,
159
+ '$orderby': 'receivedDateTime asc',
160
+ '$top': String(params.maxResults || 25),
161
+ '$select': FULL_SELECT,
162
+ }
116
163
  });
164
+ const messages = (data.value || []).map((m: any) => mapMessage(m, true));
165
+ return jsonResult({ conversationId: convId, messages, count: messages.length });
117
166
  } catch (e: any) { return errorResult(e.message); }
118
167
  },
119
168
  },
120
169
 
121
170
  {
122
171
  name: 'outlook_mail_send',
123
- description: 'Send an email via Outlook. Supports to, cc, bcc, HTML body, importance, and reply-to.',
172
+ description: 'Send an email via Outlook. Supports to, cc, bcc, HTML body, attachments (base64), importance, and reply-to.',
124
173
  category: 'utility' as const,
125
174
  parameters: {
126
175
  type: 'object' as const,
@@ -131,28 +180,36 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
131
180
  cc: { type: 'string', description: 'CC recipients, comma-separated' },
132
181
  bcc: { type: 'string', description: 'BCC recipients, comma-separated' },
133
182
  importance: { type: 'string', description: 'low, normal, or high (default: normal)' },
134
- isHtml: { type: 'boolean', description: 'Whether body is HTML (default: false)' },
183
+ isHtml: { type: 'boolean', description: 'Whether body is HTML (default: auto-detect)' },
135
184
  replyTo: { type: 'string', description: 'Reply-to address' },
136
185
  saveToSent: { type: 'boolean', description: 'Save to Sent Items (default: true)' },
186
+ attachments: { type: 'array', description: 'Array of {name, contentType, contentBytes} — contentBytes is base64' },
187
+ categories: { type: 'array', items: { type: 'string' }, description: 'Category labels to apply' },
137
188
  },
138
189
  required: ['to', 'subject', 'body'],
139
190
  },
140
191
  async execute(_id: string, params: any) {
141
192
  try {
142
193
  const token = await tp.getAccessToken();
143
- const toRecipients = params.to.split(',').map((e: string) => ({
144
- emailAddress: { address: e.trim() }
145
- }));
194
+ const isHtml = params.isHtml ?? (/<[a-z][\s\S]*>/i.test(params.body));
146
195
  const message: any = {
147
196
  subject: params.subject,
148
- body: { contentType: params.isHtml ? 'HTML' : 'Text', content: params.body },
149
- toRecipients,
197
+ body: { contentType: isHtml ? 'HTML' : 'Text', content: params.body },
198
+ toRecipients: parseRecipients(params.to),
150
199
  };
151
- if (params.cc) message.ccRecipients = params.cc.split(',').map((e: string) => ({ emailAddress: { address: e.trim() } }));
152
- if (params.bcc) message.bccRecipients = params.bcc.split(',').map((e: string) => ({ emailAddress: { address: e.trim() } }));
200
+ if (params.cc) message.ccRecipients = parseRecipients(params.cc);
201
+ if (params.bcc) message.bccRecipients = parseRecipients(params.bcc);
153
202
  if (params.importance) message.importance = params.importance;
154
203
  if (params.replyTo) message.replyTo = [{ emailAddress: { address: params.replyTo } }];
155
-
204
+ if (params.categories) message.categories = params.categories;
205
+ if (params.attachments?.length) {
206
+ message.attachments = params.attachments.map((a: any) => ({
207
+ '@odata.type': '#microsoft.graph.fileAttachment',
208
+ name: a.name,
209
+ contentType: a.contentType || 'application/octet-stream',
210
+ contentBytes: a.contentBytes,
211
+ }));
212
+ }
156
213
  await graph(token, '/me/sendMail', {
157
214
  method: 'POST',
158
215
  body: { message, saveToSentItems: params.saveToSent !== false },
@@ -164,7 +221,7 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
164
221
 
165
222
  {
166
223
  name: 'outlook_mail_reply',
167
- description: 'Reply to an email message. Can reply to sender only or reply-all.',
224
+ description: 'Reply to an email. Can reply to sender only or reply-all. Supports HTML and attachments.',
168
225
  category: 'utility' as const,
169
226
  parameters: {
170
227
  type: 'object' as const,
@@ -173,6 +230,7 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
173
230
  body: { type: 'string', description: 'Reply body text' },
174
231
  replyAll: { type: 'boolean', description: 'Reply to all recipients (default: false)' },
175
232
  isHtml: { type: 'boolean', description: 'Whether body is HTML (default: false)' },
233
+ attachments: { type: 'array', description: 'Array of {name, contentType, contentBytes}' },
176
234
  },
177
235
  required: ['messageId', 'body'],
178
236
  },
@@ -180,10 +238,33 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
180
238
  try {
181
239
  const token = await tp.getAccessToken();
182
240
  const action = params.replyAll ? 'replyAll' : 'reply';
183
- await graph(token, `/me/messages/${params.messageId}/${action}`, {
184
- method: 'POST',
185
- body: { comment: params.body },
186
- });
241
+ const body: any = { comment: params.body };
242
+ if (params.attachments?.length) {
243
+ // Need to create reply as draft, add attachments, then send
244
+ const draft = await graph(token, `/me/messages/${params.messageId}/createReply`, { method: 'POST' });
245
+ // Update body
246
+ await graph(token, `/me/messages/${draft.id}`, {
247
+ method: 'PATCH',
248
+ body: {
249
+ body: { contentType: params.isHtml ? 'HTML' : 'Text', content: params.body },
250
+ },
251
+ });
252
+ // Add attachments
253
+ for (const a of params.attachments) {
254
+ await graph(token, `/me/messages/${draft.id}/attachments`, {
255
+ method: 'POST',
256
+ body: {
257
+ '@odata.type': '#microsoft.graph.fileAttachment',
258
+ name: a.name,
259
+ contentType: a.contentType || 'application/octet-stream',
260
+ contentBytes: a.contentBytes,
261
+ },
262
+ });
263
+ }
264
+ await graph(token, `/me/messages/${draft.id}/send`, { method: 'POST' });
265
+ return jsonResult({ replied: true, messageId: params.messageId, replyAll: !!params.replyAll, withAttachments: true });
266
+ }
267
+ await graph(token, `/me/messages/${params.messageId}/${action}`, { method: 'POST', body });
187
268
  return jsonResult({ replied: true, messageId: params.messageId, replyAll: !!params.replyAll });
188
269
  } catch (e: any) { return errorResult(e.message); }
189
270
  },
@@ -191,24 +272,23 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
191
272
 
192
273
  {
193
274
  name: 'outlook_mail_forward',
194
- description: 'Forward an email message to another recipient.',
275
+ description: 'Forward an email to another recipient with an optional comment.',
195
276
  category: 'utility' as const,
196
277
  parameters: {
197
278
  type: 'object' as const,
198
279
  properties: {
199
280
  messageId: { type: 'string', description: 'Message ID to forward' },
200
281
  to: { type: 'string', description: 'Forward recipient email(s), comma-separated' },
201
- comment: { type: 'string', description: 'Optional comment to add above the forwarded message' },
282
+ comment: { type: 'string', description: 'Optional comment above the forwarded message' },
202
283
  },
203
284
  required: ['messageId', 'to'],
204
285
  },
205
286
  async execute(_id: string, params: any) {
206
287
  try {
207
288
  const token = await tp.getAccessToken();
208
- const toRecipients = params.to.split(',').map((e: string) => ({ emailAddress: { address: e.trim() } }));
209
289
  await graph(token, `/me/messages/${params.messageId}/forward`, {
210
290
  method: 'POST',
211
- body: { comment: params.comment || '', toRecipients },
291
+ body: { comment: params.comment || '', toRecipients: parseRecipients(params.to) },
212
292
  });
213
293
  return jsonResult({ forwarded: true, messageId: params.messageId, to: params.to });
214
294
  } catch (e: any) { return errorResult(e.message); }
@@ -223,14 +303,13 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
223
303
  type: 'object' as const,
224
304
  properties: {
225
305
  messageId: { type: 'string', description: 'Message ID to move' },
226
- folder: { type: 'string', description: 'Destination folder: inbox, archive, deleteditems, junkemail, or folder ID' },
306
+ folder: { type: 'string', description: 'Destination: inbox, archive, deleteditems, junkemail, sentitems, drafts, or folder ID' },
227
307
  },
228
308
  required: ['messageId', 'folder'],
229
309
  },
230
310
  async execute(_id: string, params: any) {
231
311
  try {
232
312
  const token = await tp.getAccessToken();
233
- // Resolve well-known folder names
234
313
  const folderMap: Record<string, string> = {
235
314
  inbox: 'inbox', archive: 'archive', trash: 'deleteditems',
236
315
  deleteditems: 'deleteditems', junk: 'junkemail', junkemail: 'junkemail',
@@ -238,8 +317,7 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
238
317
  };
239
318
  const destId = folderMap[params.folder.toLowerCase()] || params.folder;
240
319
  const result = await graph(token, `/me/messages/${params.messageId}/move`, {
241
- method: 'POST',
242
- body: { destinationId: destId },
320
+ method: 'POST', body: { destinationId: destId },
243
321
  });
244
322
  return jsonResult({ moved: true, newId: result.id, folder: params.folder });
245
323
  } catch (e: any) { return errorResult(e.message); }
@@ -268,7 +346,7 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
268
346
 
269
347
  {
270
348
  name: 'outlook_mail_update',
271
- description: 'Update message properties: mark as read/unread, flag, change importance, add categories.',
349
+ description: 'Update message properties: read/unread, flag, importance, categories.',
272
350
  category: 'utility' as const,
273
351
  parameters: {
274
352
  type: 'object' as const,
@@ -277,7 +355,7 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
277
355
  isRead: { type: 'boolean', description: 'Mark as read or unread' },
278
356
  importance: { type: 'string', description: 'low, normal, or high' },
279
357
  flag: { type: 'string', description: 'notFlagged, flagged, or complete' },
280
- categories: { type: 'array', items: { type: 'string' }, description: 'Category labels to apply' },
358
+ categories: { type: 'array', items: { type: 'string' }, description: 'Category labels to set (replaces existing)' },
281
359
  },
282
360
  required: ['messageId'],
283
361
  },
@@ -297,38 +375,30 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
297
375
 
298
376
  {
299
377
  name: 'outlook_mail_search',
300
- description: 'Search emails across all folders using Microsoft Search. Supports natural language queries.',
378
+ description: 'Search emails across all folders. Supports natural language and KQL syntax.',
301
379
  category: 'utility' as const,
302
380
  parameters: {
303
381
  type: 'object' as const,
304
382
  properties: {
305
- query: { type: 'string', description: 'Search query (natural language or KQL: "from:user@example.com subject:report")' },
306
- maxResults: { type: 'number', description: 'Max results (default: 10, max: 25)' },
383
+ query: { type: 'string', description: 'Search query (natural language or KQL: "from:user@example.com subject:report has:attachment")' },
384
+ maxResults: { type: 'number', description: 'Max results (default: 15, max: 25)' },
385
+ folder: { type: 'string', description: 'Limit search to specific folder (default: all folders)' },
307
386
  },
308
387
  required: ['query'],
309
388
  },
310
389
  async execute(_id: string, params: any) {
311
390
  try {
312
391
  const token = await tp.getAccessToken();
313
- const top = Math.min(params.maxResults || 10, 25);
314
- // Use $search on messages endpoint
315
- const data = await graph(token, '/me/messages', {
392
+ const top = Math.min(params.maxResults || 15, 25);
393
+ const basePath = params.folder ? `/me/mailFolders/${params.folder}/messages` : '/me/messages';
394
+ const data = await graph(token, basePath, {
316
395
  query: {
317
396
  '$search': `"${params.query}"`,
318
397
  '$top': String(top),
319
- '$select': 'id,subject,from,receivedDateTime,bodyPreview,isRead,hasAttachments,importance',
398
+ '$select': LIST_SELECT,
320
399
  }
321
400
  });
322
- const messages = (data.value || []).map((m: any) => ({
323
- id: m.id,
324
- subject: m.subject,
325
- from: m.from?.emailAddress?.address,
326
- fromName: m.from?.emailAddress?.name,
327
- date: m.receivedDateTime,
328
- preview: m.bodyPreview,
329
- isRead: m.isRead,
330
- hasAttachments: m.hasAttachments,
331
- }));
401
+ const messages = (data.value || []).map((m: any) => mapMessage(m));
332
402
  return jsonResult({ messages, count: messages.length, query: params.query });
333
403
  } catch (e: any) { return errorResult(e.message); }
334
404
  },
@@ -336,7 +406,7 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
336
406
 
337
407
  {
338
408
  name: 'outlook_mail_draft',
339
- description: 'Create a draft email in Outlook. Can be edited and sent later.',
409
+ description: 'Create a draft email. Can be edited and sent later with outlook_mail_send_draft.',
340
410
  category: 'utility' as const,
341
411
  parameters: {
342
412
  type: 'object' as const,
@@ -346,6 +416,7 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
346
416
  body: { type: 'string', description: 'Email body' },
347
417
  cc: { type: 'string', description: 'CC recipients' },
348
418
  isHtml: { type: 'boolean', description: 'Whether body is HTML' },
419
+ importance: { type: 'string', description: 'low, normal, or high' },
349
420
  },
350
421
  required: ['to', 'subject', 'body'],
351
422
  },
@@ -355,9 +426,10 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
355
426
  const message: any = {
356
427
  subject: params.subject,
357
428
  body: { contentType: params.isHtml ? 'HTML' : 'Text', content: params.body },
358
- toRecipients: params.to.split(',').map((e: string) => ({ emailAddress: { address: e.trim() } })),
429
+ toRecipients: parseRecipients(params.to),
359
430
  };
360
- if (params.cc) message.ccRecipients = params.cc.split(',').map((e: string) => ({ emailAddress: { address: e.trim() } }));
431
+ if (params.cc) message.ccRecipients = parseRecipients(params.cc);
432
+ if (params.importance) message.importance = params.importance;
361
433
  const draft = await graph(token, '/me/messages', { method: 'POST', body: message });
362
434
  return jsonResult({ draftId: draft.id, subject: params.subject, status: 'draft_created' });
363
435
  } catch (e: any) { return errorResult(e.message); }
@@ -386,27 +458,67 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
386
458
 
387
459
  {
388
460
  name: 'outlook_mail_folders',
389
- description: 'List mail folders in the mailbox.',
461
+ description: 'List all mail folders including nested child folders.',
390
462
  category: 'utility' as const,
391
- parameters: { type: 'object' as const, properties: {}, required: [] },
392
- async execute(_id: string) {
463
+ parameters: {
464
+ type: 'object' as const,
465
+ properties: {
466
+ parentFolderId: { type: 'string', description: 'Parent folder ID to list children (omit for top-level)' },
467
+ includeHidden: { type: 'boolean', description: 'Include hidden folders (default: false)' },
468
+ },
469
+ required: [],
470
+ },
471
+ async execute(_id: string, params: any) {
393
472
  try {
394
473
  const token = await tp.getAccessToken();
395
- const data = await graph(token, '/me/mailFolders', {
396
- query: { '$top': '50', '$select': 'id,displayName,totalItemCount,unreadItemCount,parentFolderId' }
397
- });
474
+ const path = params.parentFolderId
475
+ ? `/me/mailFolders/${params.parentFolderId}/childFolders`
476
+ : '/me/mailFolders';
477
+ const query: Record<string, string> = {
478
+ '$top': '100',
479
+ '$select': 'id,displayName,totalItemCount,unreadItemCount,parentFolderId,isHidden,childFolderCount',
480
+ };
481
+ if (!params.includeHidden) query['$filter'] = 'isHidden eq false';
482
+ const data = await graph(token, path, { query });
398
483
  const folders = (data.value || []).map((f: any) => ({
399
484
  id: f.id, name: f.displayName,
400
485
  totalItems: f.totalItemCount, unreadItems: f.unreadItemCount,
486
+ childFolders: f.childFolderCount, isHidden: f.isHidden,
401
487
  }));
402
488
  return jsonResult({ folders, count: folders.length });
403
489
  } catch (e: any) { return errorResult(e.message); }
404
490
  },
405
491
  },
406
492
 
493
+ {
494
+ name: 'outlook_mail_create_folder',
495
+ description: 'Create a new mail folder.',
496
+ category: 'utility' as const,
497
+ parameters: {
498
+ type: 'object' as const,
499
+ properties: {
500
+ name: { type: 'string', description: 'Folder name' },
501
+ parentFolderId: { type: 'string', description: 'Parent folder ID (omit for top-level)' },
502
+ },
503
+ required: ['name'],
504
+ },
505
+ async execute(_id: string, params: any) {
506
+ try {
507
+ const token = await tp.getAccessToken();
508
+ const path = params.parentFolderId
509
+ ? `/me/mailFolders/${params.parentFolderId}/childFolders`
510
+ : '/me/mailFolders';
511
+ const folder = await graph(token, path, {
512
+ method: 'POST', body: { displayName: params.name },
513
+ });
514
+ return jsonResult({ id: folder.id, name: folder.displayName });
515
+ } catch (e: any) { return errorResult(e.message); }
516
+ },
517
+ },
518
+
407
519
  {
408
520
  name: 'outlook_mail_attachment_download',
409
- description: 'Download an attachment from an email. Returns the attachment content as base64.',
521
+ description: 'Download an attachment from an email. Returns content as base64.',
410
522
  category: 'utility' as const,
411
523
  parameters: {
412
524
  type: 'object' as const,
@@ -422,7 +534,187 @@ export function createOutlookMailTools(config: MicrosoftToolsConfig, _options?:
422
534
  const att = await graph(token, `/me/messages/${params.messageId}/attachments/${params.attachmentId}`);
423
535
  return jsonResult({
424
536
  name: att.name, contentType: att.contentType, size: att.size,
425
- content: att.contentBytes, // base64 encoded
537
+ content: att.contentBytes,
538
+ });
539
+ } catch (e: any) { return errorResult(e.message); }
540
+ },
541
+ },
542
+
543
+ {
544
+ name: 'outlook_mail_auto_reply',
545
+ description: 'Configure automatic replies (out-of-office / vacation responder).',
546
+ category: 'utility' as const,
547
+ parameters: {
548
+ type: 'object' as const,
549
+ properties: {
550
+ status: { type: 'string', description: 'disabled, alwaysEnabled, or scheduled' },
551
+ internalMessage: { type: 'string', description: 'Auto-reply message for internal senders (HTML)' },
552
+ externalMessage: { type: 'string', description: 'Auto-reply message for external senders (HTML)' },
553
+ externalAudience: { type: 'string', description: 'none, contactsOnly, or all (default: all)' },
554
+ startDate: { type: 'string', description: 'Start date (ISO 8601, required if scheduled)' },
555
+ endDate: { type: 'string', description: 'End date (ISO 8601, required if scheduled)' },
556
+ },
557
+ required: ['status'],
558
+ },
559
+ async execute(_id: string, params: any) {
560
+ try {
561
+ const token = await tp.getAccessToken();
562
+ if (params.status === 'disabled') {
563
+ await graph(token, '/me/mailboxSettings', {
564
+ method: 'PATCH',
565
+ body: { automaticRepliesSetting: { status: 'disabled' } },
566
+ });
567
+ return jsonResult({ autoReply: 'disabled' });
568
+ }
569
+ const setting: any = { status: params.status };
570
+ if (params.internalMessage) setting.internalReplyMessage = params.internalMessage;
571
+ if (params.externalMessage) setting.externalReplyMessage = params.externalMessage;
572
+ if (params.externalAudience) setting.externalAudience = params.externalAudience;
573
+ if (params.status === 'scheduled') {
574
+ setting.scheduledStartDateTime = { dateTime: params.startDate, timeZone: 'UTC' };
575
+ setting.scheduledEndDateTime = { dateTime: params.endDate, timeZone: 'UTC' };
576
+ }
577
+ await graph(token, '/me/mailboxSettings', {
578
+ method: 'PATCH',
579
+ body: { automaticRepliesSetting: setting },
580
+ });
581
+ return jsonResult({ autoReply: params.status, configured: true });
582
+ } catch (e: any) { return errorResult(e.message); }
583
+ },
584
+ },
585
+
586
+ {
587
+ name: 'outlook_mail_get_auto_reply',
588
+ description: 'Get current automatic reply (out-of-office) settings.',
589
+ category: 'utility' as const,
590
+ parameters: { type: 'object' as const, properties: {}, required: [] },
591
+ async execute(_id: string) {
592
+ try {
593
+ const token = await tp.getAccessToken();
594
+ const settings = await graph(token, '/me/mailboxSettings/automaticRepliesSetting');
595
+ return jsonResult({
596
+ status: settings.status,
597
+ internalMessage: settings.internalReplyMessage,
598
+ externalMessage: settings.externalReplyMessage,
599
+ externalAudience: settings.externalAudience,
600
+ startDate: settings.scheduledStartDateTime?.dateTime,
601
+ endDate: settings.scheduledEndDateTime?.dateTime,
602
+ });
603
+ } catch (e: any) { return errorResult(e.message); }
604
+ },
605
+ },
606
+
607
+ {
608
+ name: 'outlook_mail_rules',
609
+ description: 'List, create, or delete inbox rules (message rules). Rules auto-process incoming mail.',
610
+ category: 'utility' as const,
611
+ parameters: {
612
+ type: 'object' as const,
613
+ properties: {
614
+ action: { type: 'string', description: 'list, create, or delete' },
615
+ ruleId: { type: 'string', description: 'Rule ID (for delete)' },
616
+ displayName: { type: 'string', description: 'Rule name (for create)' },
617
+ conditions: { type: 'object', description: 'Match conditions object — e.g., {fromAddresses:[{emailAddress:{address:"user@example.com"}}], subjectContains:["invoice"]}' },
618
+ actions: { type: 'object', description: 'Actions when matched — e.g., {moveToFolder:"archive", markAsRead:true, stopProcessingRules:true}' },
619
+ isEnabled: { type: 'boolean', description: 'Enable/disable rule (default: true)' },
620
+ },
621
+ required: ['action'],
622
+ },
623
+ async execute(_id: string, params: any) {
624
+ try {
625
+ const token = await tp.getAccessToken();
626
+ if (params.action === 'list') {
627
+ const data = await graph(token, '/me/mailFolders/inbox/messageRules');
628
+ const rules = (data.value || []).map((r: any) => ({
629
+ id: r.id, name: r.displayName, isEnabled: r.isEnabled,
630
+ sequence: r.sequence, conditions: r.conditions, actions: r.actions,
631
+ }));
632
+ return jsonResult({ rules, count: rules.length });
633
+ }
634
+ if (params.action === 'delete') {
635
+ await graph(token, `/me/mailFolders/inbox/messageRules/${params.ruleId}`, { method: 'DELETE' });
636
+ return jsonResult({ deleted: true, ruleId: params.ruleId });
637
+ }
638
+ if (params.action === 'create') {
639
+ const rule = await graph(token, '/me/mailFolders/inbox/messageRules', {
640
+ method: 'POST',
641
+ body: {
642
+ displayName: params.displayName || 'Agent Rule',
643
+ isEnabled: params.isEnabled !== false,
644
+ conditions: params.conditions || {},
645
+ actions: params.actions || {},
646
+ },
647
+ });
648
+ return jsonResult({ id: rule.id, name: rule.displayName, created: true });
649
+ }
650
+ throw new Error('action must be list, create, or delete');
651
+ } catch (e: any) { return errorResult(e.message); }
652
+ },
653
+ },
654
+
655
+ {
656
+ name: 'outlook_mail_categories',
657
+ description: 'Manage Outlook categories (color-coded labels). List, create, or delete.',
658
+ category: 'utility' as const,
659
+ parameters: {
660
+ type: 'object' as const,
661
+ properties: {
662
+ action: { type: 'string', description: 'list, create, or delete' },
663
+ name: { type: 'string', description: 'Category name (for create)' },
664
+ color: { type: 'string', description: 'Color preset: None, Red, Orange, Brown, Yellow, Green, Teal, Olive, Blue, Purple, Cranberry, Steel, DarkSteel, Gray, DarkGray, Black (for create)' },
665
+ categoryId: { type: 'string', description: 'Category ID (for delete)' },
666
+ },
667
+ required: ['action'],
668
+ },
669
+ async execute(_id: string, params: any) {
670
+ try {
671
+ const token = await tp.getAccessToken();
672
+ if (params.action === 'list') {
673
+ const data = await graph(token, '/me/outlook/masterCategories');
674
+ const cats = (data.value || []).map((c: any) => ({
675
+ id: c.id, name: c.displayName, color: c.color,
676
+ }));
677
+ return jsonResult({ categories: cats, count: cats.length });
678
+ }
679
+ if (params.action === 'create') {
680
+ const cat = await graph(token, '/me/outlook/masterCategories', {
681
+ method: 'POST',
682
+ body: { displayName: params.name, color: params.color || 'None' },
683
+ });
684
+ return jsonResult({ id: cat.id, name: cat.displayName, color: cat.color });
685
+ }
686
+ if (params.action === 'delete') {
687
+ await graph(token, `/me/outlook/masterCategories/${params.categoryId}`, { method: 'DELETE' });
688
+ return jsonResult({ deleted: true });
689
+ }
690
+ throw new Error('action must be list, create, or delete');
691
+ } catch (e: any) { return errorResult(e.message); }
692
+ },
693
+ },
694
+
695
+ {
696
+ name: 'outlook_mail_profile',
697
+ description: 'Get the mailbox user profile — email address, display name, timezone, language, and mailbox settings.',
698
+ category: 'utility' as const,
699
+ parameters: { type: 'object' as const, properties: {}, required: [] },
700
+ async execute(_id: string) {
701
+ try {
702
+ const token = await tp.getAccessToken();
703
+ const [user, settings] = await Promise.all([
704
+ graph(token, '/me', { query: { '$select': 'displayName,mail,userPrincipalName,jobTitle,department,officeLocation' } }),
705
+ graph(token, '/me/mailboxSettings'),
706
+ ]);
707
+ return jsonResult({
708
+ email: user.mail || user.userPrincipalName,
709
+ name: user.displayName,
710
+ jobTitle: user.jobTitle,
711
+ department: user.department,
712
+ office: user.officeLocation,
713
+ timeZone: settings.timeZone,
714
+ language: settings.language?.locale,
715
+ dateFormat: settings.dateFormat,
716
+ timeFormat: settings.timeFormat,
717
+ delegateMeetingRequests: settings.delegateMeetingMessageDeliveryOptions,
426
718
  });
427
719
  } catch (e: any) { return errorResult(e.message); }
428
720
  },