@hed-hog/core 0.0.399 → 0.0.401

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 (815) hide show
  1. package/dist/ai/ai-agent.sort.d.ts +6 -0
  2. package/dist/ai/ai-agent.sort.d.ts.map +1 -0
  3. package/dist/ai/ai-agent.sort.js +21 -0
  4. package/dist/ai/ai-agent.sort.js.map +1 -0
  5. package/dist/ai/ai.controller.d.ts +6 -4
  6. package/dist/ai/ai.controller.d.ts.map +1 -1
  7. package/dist/ai/ai.service.d.ts +121 -6
  8. package/dist/ai/ai.service.d.ts.map +1 -1
  9. package/dist/ai/ai.service.js +355 -160
  10. package/dist/ai/ai.service.js.map +1 -1
  11. package/dist/ai-instruction/ai-instruction.service.d.ts.map +1 -1
  12. package/dist/ai-instruction/ai-instruction.service.js +26 -23
  13. package/dist/ai-instruction/ai-instruction.service.js.map +1 -1
  14. package/dist/ai-instruction/ai-instruction.sort.d.ts +16 -0
  15. package/dist/ai-instruction/ai-instruction.sort.d.ts.map +1 -0
  16. package/dist/ai-instruction/ai-instruction.sort.js +32 -0
  17. package/dist/ai-instruction/ai-instruction.sort.js.map +1 -0
  18. package/dist/ai-instruction/ai-prompt-resolver.service.d.ts +12 -0
  19. package/dist/ai-instruction/ai-prompt-resolver.service.d.ts.map +1 -1
  20. package/dist/ai-instruction/ai-prompt-resolver.service.js +26 -0
  21. package/dist/ai-instruction/ai-prompt-resolver.service.js.map +1 -1
  22. package/dist/auth/auth.controller.d.ts +24 -8
  23. package/dist/auth/auth.controller.d.ts.map +1 -1
  24. package/dist/auth/auth.module.d.ts.map +1 -1
  25. package/dist/auth/auth.module.js +8 -1
  26. package/dist/auth/auth.module.js.map +1 -1
  27. package/dist/auth/auth.service.d.ts +72 -25
  28. package/dist/auth/auth.service.d.ts.map +1 -1
  29. package/dist/auth/auth.service.js +41 -4
  30. package/dist/auth/auth.service.js.map +1 -1
  31. package/dist/auth/guards/auth.guard.d.ts +3 -1
  32. package/dist/auth/guards/auth.guard.d.ts.map +1 -1
  33. package/dist/auth/guards/auth.guard.js +12 -3
  34. package/dist/auth/guards/auth.guard.js.map +1 -1
  35. package/dist/authorize/authorize.controller.d.ts +29 -5
  36. package/dist/authorize/authorize.controller.d.ts.map +1 -1
  37. package/dist/authorize/authorize.controller.js +120 -13
  38. package/dist/authorize/authorize.controller.js.map +1 -1
  39. package/dist/authorize/authorize.errors.d.ts +34 -0
  40. package/dist/authorize/authorize.errors.d.ts.map +1 -0
  41. package/dist/authorize/authorize.errors.js +37 -0
  42. package/dist/authorize/authorize.errors.js.map +1 -0
  43. package/dist/authorize/authorize.module.d.ts.map +1 -1
  44. package/dist/authorize/authorize.module.js +6 -1
  45. package/dist/authorize/authorize.module.js.map +1 -1
  46. package/dist/authorize/authorize.service.d.ts +77 -3
  47. package/dist/authorize/authorize.service.d.ts.map +1 -1
  48. package/dist/authorize/authorize.service.js +212 -21
  49. package/dist/authorize/authorize.service.js.map +1 -1
  50. package/dist/authorize/dto/authorize-info.dto.d.ts +10 -1
  51. package/dist/authorize/dto/authorize-info.dto.d.ts.map +1 -1
  52. package/dist/authorize/dto/authorize-info.dto.js +16 -1
  53. package/dist/authorize/dto/authorize-info.dto.js.map +1 -1
  54. package/dist/authorize/dto/consent.dto.d.ts +10 -1
  55. package/dist/authorize/dto/consent.dto.d.ts.map +1 -1
  56. package/dist/authorize/dto/consent.dto.js +16 -1
  57. package/dist/authorize/dto/consent.dto.js.map +1 -1
  58. package/dist/authorize/dto/token.dto.d.ts +12 -0
  59. package/dist/authorize/dto/token.dto.d.ts.map +1 -1
  60. package/dist/authorize/dto/token.dto.js +21 -1
  61. package/dist/authorize/dto/token.dto.js.map +1 -1
  62. package/dist/authorize/oauth-exception.filter.d.ts +17 -0
  63. package/dist/authorize/oauth-exception.filter.d.ts.map +1 -0
  64. package/dist/authorize/oauth-exception.filter.js +54 -0
  65. package/dist/authorize/oauth-exception.filter.js.map +1 -0
  66. package/dist/authorize/oauth-metadata.service.d.ts +39 -0
  67. package/dist/authorize/oauth-metadata.service.d.ts.map +1 -0
  68. package/dist/authorize/oauth-metadata.service.js +81 -0
  69. package/dist/authorize/oauth-metadata.service.js.map +1 -0
  70. package/dist/authorize/well-known.controller.d.ts +67 -0
  71. package/dist/authorize/well-known.controller.d.ts.map +1 -0
  72. package/dist/authorize/well-known.controller.js +126 -0
  73. package/dist/authorize/well-known.controller.js.map +1 -0
  74. package/dist/captcha/captcha.service.d.ts +32 -2
  75. package/dist/captcha/captcha.service.d.ts.map +1 -1
  76. package/dist/captcha/captcha.service.js +85 -12
  77. package/dist/captcha/captcha.service.js.map +1 -1
  78. package/dist/challenge/challenge.service.d.ts +2 -2
  79. package/dist/core.module.d.ts.map +1 -1
  80. package/dist/core.module.js +9 -0
  81. package/dist/core.module.js.map +1 -1
  82. package/dist/dashboard/dashboard/dashboard.controller.d.ts +9 -9
  83. package/dist/dashboard/dashboard/dashboard.service.d.ts +9 -9
  84. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +3 -3
  85. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +3 -3
  86. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +3 -3
  87. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +3 -3
  88. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts.map +1 -1
  89. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.js +2 -4
  90. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.js.map +1 -1
  91. package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.d.ts +8 -0
  92. package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.d.ts.map +1 -0
  93. package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.js +18 -0
  94. package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.js.map +1 -0
  95. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
  96. package/dist/dashboard/dashboard-item/dashboard-item.service.js +2 -4
  97. package/dist/dashboard/dashboard-item/dashboard-item.service.js.map +1 -1
  98. package/dist/dashboard/dashboard-item/dashboard-item.sort.d.ts +9 -0
  99. package/dist/dashboard/dashboard-item/dashboard-item.sort.d.ts.map +1 -0
  100. package/dist/dashboard/dashboard-item/dashboard-item.sort.js +19 -0
  101. package/dist/dashboard/dashboard-item/dashboard-item.sort.js.map +1 -0
  102. package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +3 -3
  103. package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +3 -3
  104. package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts.map +1 -1
  105. package/dist/dashboard/dashboard-role/dashboard-role.service.js +2 -4
  106. package/dist/dashboard/dashboard-role/dashboard-role.service.js.map +1 -1
  107. package/dist/dashboard/dashboard-role/dashboard-role.sort.d.ts +8 -0
  108. package/dist/dashboard/dashboard-role/dashboard-role.sort.d.ts.map +1 -0
  109. package/dist/dashboard/dashboard-role/dashboard-role.sort.js +16 -0
  110. package/dist/dashboard/dashboard-role/dashboard-role.sort.js.map +1 -0
  111. package/dist/document-kit/document-kit.module.d.ts +3 -0
  112. package/dist/document-kit/document-kit.module.d.ts.map +1 -0
  113. package/dist/document-kit/document-kit.module.js +24 -0
  114. package/dist/document-kit/document-kit.module.js.map +1 -0
  115. package/dist/document-kit/document-kit.registry.d.ts +55 -0
  116. package/dist/document-kit/document-kit.registry.d.ts.map +1 -0
  117. package/dist/document-kit/document-kit.registry.js +91 -0
  118. package/dist/document-kit/document-kit.registry.js.map +1 -0
  119. package/dist/document-kit/document-kit.service.d.ts +49 -0
  120. package/dist/document-kit/document-kit.service.d.ts.map +1 -0
  121. package/dist/document-kit/document-kit.service.js +135 -0
  122. package/dist/document-kit/document-kit.service.js.map +1 -0
  123. package/dist/file/file-upload-ticket.service.d.ts +160 -0
  124. package/dist/file/file-upload-ticket.service.d.ts.map +1 -0
  125. package/dist/file/file-upload-ticket.service.js +382 -0
  126. package/dist/file/file-upload-ticket.service.js.map +1 -0
  127. package/dist/file/file.controller.d.ts +24 -1
  128. package/dist/file/file.controller.d.ts.map +1 -1
  129. package/dist/file/file.controller.js +63 -2
  130. package/dist/file/file.controller.js.map +1 -1
  131. package/dist/file/file.module.d.ts.map +1 -1
  132. package/dist/file/file.module.js +14 -2
  133. package/dist/file/file.module.js.map +1 -1
  134. package/dist/file/file.service.d.ts +197 -7
  135. package/dist/file/file.service.d.ts.map +1 -1
  136. package/dist/file/file.service.js +438 -78
  137. package/dist/file/file.service.js.map +1 -1
  138. package/dist/file/filename-encoding.util.d.ts +24 -0
  139. package/dist/file/filename-encoding.util.d.ts.map +1 -0
  140. package/dist/file/filename-encoding.util.js +63 -0
  141. package/dist/file/filename-encoding.util.js.map +1 -0
  142. package/dist/file/image-optimization.service.d.ts +27 -0
  143. package/dist/file/image-optimization.service.d.ts.map +1 -1
  144. package/dist/file/image-optimization.service.js +63 -14
  145. package/dist/file/image-optimization.service.js.map +1 -1
  146. package/dist/file/mcp-file-source.schema.d.ts +33 -0
  147. package/dist/file/mcp-file-source.schema.d.ts.map +1 -0
  148. package/dist/file/mcp-file-source.schema.js +78 -0
  149. package/dist/file/mcp-file-source.schema.js.map +1 -0
  150. package/dist/file/mcp-file-source.service.d.ts +124 -0
  151. package/dist/file/mcp-file-source.service.d.ts.map +1 -0
  152. package/dist/file/mcp-file-source.service.js +262 -0
  153. package/dist/file/mcp-file-source.service.js.map +1 -0
  154. package/dist/file/mimetype-sniff.util.d.ts +62 -0
  155. package/dist/file/mimetype-sniff.util.d.ts.map +1 -0
  156. package/dist/file/mimetype-sniff.util.js +226 -0
  157. package/dist/file/mimetype-sniff.util.js.map +1 -0
  158. package/dist/file/remote-fetch.util.d.ts +35 -0
  159. package/dist/file/remote-fetch.util.d.ts.map +1 -0
  160. package/dist/file/remote-fetch.util.js +236 -0
  161. package/dist/file/remote-fetch.util.js.map +1 -0
  162. package/dist/impersonation/dto/start-impersonation.dto.d.ts +4 -1
  163. package/dist/impersonation/dto/start-impersonation.dto.d.ts.map +1 -1
  164. package/dist/impersonation/dto/start-impersonation.dto.js +1 -1
  165. package/dist/impersonation/dto/start-impersonation.dto.js.map +1 -1
  166. package/dist/impersonation/impersonation.service.d.ts +8 -3
  167. package/dist/impersonation/impersonation.service.d.ts.map +1 -1
  168. package/dist/impersonation/impersonation.service.js +14 -4
  169. package/dist/impersonation/impersonation.service.js.map +1 -1
  170. package/dist/incident/incident.controller.d.ts +2 -1
  171. package/dist/incident/incident.controller.d.ts.map +1 -1
  172. package/dist/incident/incident.controller.js +10 -6
  173. package/dist/incident/incident.controller.js.map +1 -1
  174. package/dist/incident/incident.service.d.ts +2 -0
  175. package/dist/incident/incident.service.d.ts.map +1 -1
  176. package/dist/incident/incident.service.js +4 -0
  177. package/dist/incident/incident.service.js.map +1 -1
  178. package/dist/index.d.ts +22 -0
  179. package/dist/index.d.ts.map +1 -1
  180. package/dist/index.js +22 -0
  181. package/dist/index.js.map +1 -1
  182. package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
  183. package/dist/integration-profile/integration-profile.secrets.js +28 -0
  184. package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
  185. package/dist/integration-profile/integration-profile.service.d.ts +0 -13
  186. package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
  187. package/dist/integration-profile/integration-profile.service.js +5 -40
  188. package/dist/integration-profile/integration-profile.service.js.map +1 -1
  189. package/dist/integration-profile/integration-profile.utils.js +5 -5
  190. package/dist/integration-profile/integration-profile.utils.js.map +1 -1
  191. package/dist/integration-profile/testers/ai.testers.d.ts.map +1 -1
  192. package/dist/integration-profile/testers/ai.testers.js +46 -8
  193. package/dist/integration-profile/testers/ai.testers.js.map +1 -1
  194. package/dist/integration-profile/testers/index.d.ts.map +1 -1
  195. package/dist/integration-profile/testers/index.js +2 -0
  196. package/dist/integration-profile/testers/index.js.map +1 -1
  197. package/dist/integration-profile/testers/shipping.testers.d.ts +3 -0
  198. package/dist/integration-profile/testers/shipping.testers.d.ts.map +1 -0
  199. package/dist/integration-profile/testers/shipping.testers.js +71 -0
  200. package/dist/integration-profile/testers/shipping.testers.js.map +1 -0
  201. package/dist/locale/locale.controller.d.ts +1 -1
  202. package/dist/locale/locale.service.d.ts +1 -1
  203. package/dist/mail/dto/send.dto.d.ts +6 -1
  204. package/dist/mail/dto/send.dto.d.ts.map +1 -1
  205. package/dist/mail/dto/send.dto.js.map +1 -1
  206. package/dist/mail/mail.service.d.ts +9 -0
  207. package/dist/mail/mail.service.d.ts.map +1 -1
  208. package/dist/mail/mail.service.js +34 -14
  209. package/dist/mail/mail.service.js.map +1 -1
  210. package/dist/mail-sent/mail-sent.controller.d.ts +1 -1
  211. package/dist/mail-sent/mail-sent.service.d.ts +1 -1
  212. package/dist/mail-sent/mail-sent.service.d.ts.map +1 -1
  213. package/dist/mail-sent/mail-sent.service.js +2 -1
  214. package/dist/mail-sent/mail-sent.service.js.map +1 -1
  215. package/dist/mail-sent/mail-sent.sort.d.ts +9 -0
  216. package/dist/mail-sent/mail-sent.sort.d.ts.map +1 -0
  217. package/dist/mail-sent/mail-sent.sort.js +19 -0
  218. package/dist/mail-sent/mail-sent.sort.js.map +1 -0
  219. package/dist/mcp/filters/mcp-challenge.filter.d.ts +20 -0
  220. package/dist/mcp/filters/mcp-challenge.filter.d.ts.map +1 -0
  221. package/dist/mcp/filters/mcp-challenge.filter.js +69 -0
  222. package/dist/mcp/filters/mcp-challenge.filter.js.map +1 -0
  223. package/dist/mcp/guards/mcp-auth.guard.d.ts +4 -1
  224. package/dist/mcp/guards/mcp-auth.guard.d.ts.map +1 -1
  225. package/dist/mcp/guards/mcp-auth.guard.js +19 -5
  226. package/dist/mcp/guards/mcp-auth.guard.js.map +1 -1
  227. package/dist/mcp/guards/mcp-permission.guard.d.ts +25 -3
  228. package/dist/mcp/guards/mcp-permission.guard.d.ts.map +1 -1
  229. package/dist/mcp/guards/mcp-permission.guard.js +46 -26
  230. package/dist/mcp/guards/mcp-permission.guard.js.map +1 -1
  231. package/dist/mcp/mcp-audit.service.d.ts +17 -0
  232. package/dist/mcp/mcp-audit.service.d.ts.map +1 -1
  233. package/dist/mcp/mcp-audit.service.js +32 -3
  234. package/dist/mcp/mcp-audit.service.js.map +1 -1
  235. package/dist/mcp/mcp-permission.service.d.ts +64 -0
  236. package/dist/mcp/mcp-permission.service.d.ts.map +1 -0
  237. package/dist/mcp/mcp-permission.service.js +178 -0
  238. package/dist/mcp/mcp-permission.service.js.map +1 -0
  239. package/dist/mcp/mcp-server.service.d.ts +3 -1
  240. package/dist/mcp/mcp-server.service.d.ts.map +1 -1
  241. package/dist/mcp/mcp-server.service.js +36 -15
  242. package/dist/mcp/mcp-server.service.js.map +1 -1
  243. package/dist/mcp/mcp.controller.d.ts.map +1 -1
  244. package/dist/mcp/mcp.controller.js +10 -2
  245. package/dist/mcp/mcp.controller.js.map +1 -1
  246. package/dist/mcp/mcp.module.d.ts.map +1 -1
  247. package/dist/mcp/mcp.module.js +7 -1
  248. package/dist/mcp/mcp.module.js.map +1 -1
  249. package/dist/mcp/tools/core-access-log.mcp-tools.d.ts +17 -0
  250. package/dist/mcp/tools/core-access-log.mcp-tools.d.ts.map +1 -0
  251. package/dist/mcp/tools/core-access-log.mcp-tools.js +74 -0
  252. package/dist/mcp/tools/core-access-log.mcp-tools.js.map +1 -0
  253. package/dist/mcp/tools/core-ai.mcp-tools.js +2 -9
  254. package/dist/mcp/tools/core-ai.mcp-tools.js.map +1 -1
  255. package/dist/mcp/tools/core-dashboard-items.mcp-tools.js +1 -1
  256. package/dist/mcp/tools/core-dashboard-items.mcp-tools.js.map +1 -1
  257. package/dist/mcp/tools/core-dashboard.mcp-tools.js +2 -9
  258. package/dist/mcp/tools/core-dashboard.mcp-tools.js.map +1 -1
  259. package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts.map +1 -1
  260. package/dist/mcp/tools/core-event-webhooks.mcp-tools.js +2 -10
  261. package/dist/mcp/tools/core-event-webhooks.mcp-tools.js.map +1 -1
  262. package/dist/mcp/tools/core-files.mcp-tools.d.ts +17 -5
  263. package/dist/mcp/tools/core-files.mcp-tools.d.ts.map +1 -1
  264. package/dist/mcp/tools/core-files.mcp-tools.js +186 -24
  265. package/dist/mcp/tools/core-files.mcp-tools.js.map +1 -1
  266. package/dist/mcp/tools/core-integration-events.mcp-tools.js +2 -9
  267. package/dist/mcp/tools/core-integration-events.mcp-tools.js.map +1 -1
  268. package/dist/mcp/tools/core-mail-sent.mcp-tools.js +2 -14
  269. package/dist/mcp/tools/core-mail-sent.mcp-tools.js.map +1 -1
  270. package/dist/mcp/tools/core-mail.mcp-tools.js +2 -9
  271. package/dist/mcp/tools/core-mail.mcp-tools.js.map +1 -1
  272. package/dist/mcp/tools/core-menus.mcp-tools.d.ts.map +1 -1
  273. package/dist/mcp/tools/core-menus.mcp-tools.js +2 -10
  274. package/dist/mcp/tools/core-menus.mcp-tools.js.map +1 -1
  275. package/dist/mcp/tools/core-notifications.mcp-tools.d.ts.map +1 -1
  276. package/dist/mcp/tools/core-notifications.mcp-tools.js +0 -1
  277. package/dist/mcp/tools/core-notifications.mcp-tools.js.map +1 -1
  278. package/dist/mcp/tools/core-roles.mcp-tools.d.ts.map +1 -1
  279. package/dist/mcp/tools/core-roles.mcp-tools.js +2 -13
  280. package/dist/mcp/tools/core-roles.mcp-tools.js.map +1 -1
  281. package/dist/mcp/tools/core-routes.mcp-tools.d.ts.map +1 -1
  282. package/dist/mcp/tools/core-routes.mcp-tools.js +2 -11
  283. package/dist/mcp/tools/core-routes.mcp-tools.js.map +1 -1
  284. package/dist/mcp/tools/core-screens.mcp-tools.d.ts.map +1 -1
  285. package/dist/mcp/tools/core-screens.mcp-tools.js +2 -11
  286. package/dist/mcp/tools/core-screens.mcp-tools.js.map +1 -1
  287. package/dist/mcp/tools/core-sessions.mcp-tools.d.ts.map +1 -1
  288. package/dist/mcp/tools/core-sessions.mcp-tools.js +0 -2
  289. package/dist/mcp/tools/core-sessions.mcp-tools.js.map +1 -1
  290. package/dist/mcp/tools/core-settings.mcp-tools.d.ts.map +1 -1
  291. package/dist/mcp/tools/core-settings.mcp-tools.js +2 -10
  292. package/dist/mcp/tools/core-settings.mcp-tools.js.map +1 -1
  293. package/dist/mcp/tools/core-users.mcp-tools.js +2 -9
  294. package/dist/mcp/tools/core-users.mcp-tools.js.map +1 -1
  295. package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts.map +1 -1
  296. package/dist/mcp/tools/core-webhooks.mcp-tools.js +2 -10
  297. package/dist/mcp/tools/core-webhooks.mcp-tools.js.map +1 -1
  298. package/dist/mcp/types/mcp-context.type.d.ts +9 -0
  299. package/dist/mcp/types/mcp-context.type.d.ts.map +1 -1
  300. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
  301. package/dist/mcp-chat/mcp-chat.service.js +8 -0
  302. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  303. package/dist/meeting/meeting-provider.registry.d.ts +187 -0
  304. package/dist/meeting/meeting-provider.registry.d.ts.map +1 -1
  305. package/dist/meeting/meeting-provider.registry.js.map +1 -1
  306. package/dist/meeting/meeting.constants.d.ts +70 -0
  307. package/dist/meeting/meeting.constants.d.ts.map +1 -1
  308. package/dist/meeting/meeting.constants.js +9 -1
  309. package/dist/meeting/meeting.constants.js.map +1 -1
  310. package/dist/menu/menu.service.d.ts.map +1 -1
  311. package/dist/menu/menu.service.js +29 -9
  312. package/dist/menu/menu.service.js.map +1 -1
  313. package/dist/message-template/dto/update-message-template.dto.d.ts +18 -0
  314. package/dist/message-template/dto/update-message-template.dto.d.ts.map +1 -0
  315. package/dist/message-template/dto/update-message-template.dto.js +38 -0
  316. package/dist/message-template/dto/update-message-template.dto.js.map +1 -0
  317. package/dist/message-template/message-template-admin.service.d.ts +70 -0
  318. package/dist/message-template/message-template-admin.service.d.ts.map +1 -0
  319. package/dist/message-template/message-template-admin.service.js +226 -0
  320. package/dist/message-template/message-template-admin.service.js.map +1 -0
  321. package/dist/message-template/message-template.controller.d.ts +29 -0
  322. package/dist/message-template/message-template.controller.d.ts.map +1 -0
  323. package/dist/message-template/message-template.controller.js +101 -0
  324. package/dist/message-template/message-template.controller.js.map +1 -0
  325. package/dist/message-template/message-template.module.d.ts +10 -0
  326. package/dist/message-template/message-template.module.d.ts.map +1 -0
  327. package/dist/message-template/message-template.module.js +33 -0
  328. package/dist/message-template/message-template.module.js.map +1 -0
  329. package/dist/message-template/message-template.registry.d.ts +55 -0
  330. package/dist/message-template/message-template.registry.d.ts.map +1 -0
  331. package/dist/message-template/message-template.registry.js +64 -0
  332. package/dist/message-template/message-template.registry.js.map +1 -0
  333. package/dist/message-template/message-template.service.d.ts +59 -0
  334. package/dist/message-template/message-template.service.d.ts.map +1 -0
  335. package/dist/message-template/message-template.service.js +166 -0
  336. package/dist/message-template/message-template.service.js.map +1 -0
  337. package/dist/notification/notification.constants.d.ts +11 -0
  338. package/dist/notification/notification.constants.d.ts.map +1 -0
  339. package/dist/notification/notification.constants.js +14 -0
  340. package/dist/notification/notification.constants.js.map +1 -0
  341. package/dist/notification/notification.controller.d.ts +20 -20
  342. package/dist/notification/notification.module.d.ts.map +1 -1
  343. package/dist/notification/notification.module.js +3 -0
  344. package/dist/notification/notification.module.js.map +1 -1
  345. package/dist/notification/notification.service.d.ts +33 -21
  346. package/dist/notification/notification.service.d.ts.map +1 -1
  347. package/dist/notification/notification.service.js +36 -2
  348. package/dist/notification/notification.service.js.map +1 -1
  349. package/dist/oauth/oauth-token-broker.service.d.ts +50 -1
  350. package/dist/oauth/oauth-token-broker.service.d.ts.map +1 -1
  351. package/dist/oauth/oauth-token-broker.service.js +105 -3
  352. package/dist/oauth/oauth-token-broker.service.js.map +1 -1
  353. package/dist/oauth/oauth.controller.d.ts +4 -6
  354. package/dist/oauth/oauth.controller.d.ts.map +1 -1
  355. package/dist/oauth/oauth.controller.js +0 -1
  356. package/dist/oauth/oauth.controller.js.map +1 -1
  357. package/dist/oauth/oauth.errors.d.ts +15 -1
  358. package/dist/oauth/oauth.errors.d.ts.map +1 -1
  359. package/dist/oauth/oauth.errors.js +9 -1
  360. package/dist/oauth/oauth.errors.js.map +1 -1
  361. package/dist/oauth/oauth.module.d.ts.map +1 -1
  362. package/dist/oauth/oauth.module.js +6 -1
  363. package/dist/oauth/oauth.module.js.map +1 -1
  364. package/dist/oauth/oauth.service.d.ts +25 -5
  365. package/dist/oauth/oauth.service.d.ts.map +1 -1
  366. package/dist/oauth/oauth.service.js +63 -27
  367. package/dist/oauth/oauth.service.js.map +1 -1
  368. package/dist/oauth/providers/abstract.provider.d.ts +10 -0
  369. package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
  370. package/dist/oauth/providers/abstract.provider.js +39 -12
  371. package/dist/oauth/providers/abstract.provider.js.map +1 -1
  372. package/dist/oauth/providers/github.provider.d.ts +1 -1
  373. package/dist/oauth/providers/github.provider.d.ts.map +1 -1
  374. package/dist/oauth/providers/github.provider.js +48 -22
  375. package/dist/oauth/providers/github.provider.js.map +1 -1
  376. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts +6 -0
  377. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
  378. package/dist/oauth/providers/microsoft-entra-id.provider.js +36 -4
  379. package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
  380. package/dist/observability/monitored-cron.d.ts +29 -12
  381. package/dist/observability/monitored-cron.d.ts.map +1 -1
  382. package/dist/observability/monitored-cron.js +131 -46
  383. package/dist/observability/monitored-cron.js.map +1 -1
  384. package/dist/pairing/pairing.service.d.ts +6 -2
  385. package/dist/pairing/pairing.service.d.ts.map +1 -1
  386. package/dist/pdf/document/charts/chart-bar.d.ts +24 -0
  387. package/dist/pdf/document/charts/chart-bar.d.ts.map +1 -0
  388. package/dist/pdf/document/charts/chart-bar.js +117 -0
  389. package/dist/pdf/document/charts/chart-bar.js.map +1 -0
  390. package/dist/pdf/document/charts/chart-donut.d.ts +16 -0
  391. package/dist/pdf/document/charts/chart-donut.d.ts.map +1 -0
  392. package/dist/pdf/document/charts/chart-donut.js +89 -0
  393. package/dist/pdf/document/charts/chart-donut.js.map +1 -0
  394. package/dist/pdf/document/charts/chart-gauge.d.ts +15 -0
  395. package/dist/pdf/document/charts/chart-gauge.d.ts.map +1 -0
  396. package/dist/pdf/document/charts/chart-gauge.js +60 -0
  397. package/dist/pdf/document/charts/chart-gauge.js.map +1 -0
  398. package/dist/pdf/document/charts/chart-line.d.ts +29 -0
  399. package/dist/pdf/document/charts/chart-line.d.ts.map +1 -0
  400. package/dist/pdf/document/charts/chart-line.js +89 -0
  401. package/dist/pdf/document/charts/chart-line.js.map +1 -0
  402. package/dist/pdf/document/charts/chart-primitives.d.ts +64 -0
  403. package/dist/pdf/document/charts/chart-primitives.d.ts.map +1 -0
  404. package/dist/pdf/document/charts/chart-primitives.js +110 -0
  405. package/dist/pdf/document/charts/chart-primitives.js.map +1 -0
  406. package/dist/pdf/document/charts/chart-radar.d.ts +13 -0
  407. package/dist/pdf/document/charts/chart-radar.d.ts.map +1 -0
  408. package/dist/pdf/document/charts/chart-radar.js +83 -0
  409. package/dist/pdf/document/charts/chart-radar.js.map +1 -0
  410. package/dist/pdf/document/charts/chart-scale.d.ts +39 -0
  411. package/dist/pdf/document/charts/chart-scale.d.ts.map +1 -0
  412. package/dist/pdf/document/charts/chart-scale.js +118 -0
  413. package/dist/pdf/document/charts/chart-scale.js.map +1 -0
  414. package/dist/pdf/document/charts/index.d.ts +8 -0
  415. package/dist/pdf/document/charts/index.d.ts.map +1 -0
  416. package/dist/pdf/document/charts/index.js +24 -0
  417. package/dist/pdf/document/charts/index.js.map +1 -0
  418. package/dist/pdf/document/index.d.ts +8 -0
  419. package/dist/pdf/document/index.d.ts.map +1 -0
  420. package/dist/pdf/document/index.js +24 -0
  421. package/dist/pdf/document/index.js.map +1 -0
  422. package/dist/pdf/document/pdf-document.chrome.d.ts +20 -0
  423. package/dist/pdf/document/pdf-document.chrome.d.ts.map +1 -0
  424. package/dist/pdf/document/pdf-document.chrome.js +36 -0
  425. package/dist/pdf/document/pdf-document.chrome.js.map +1 -0
  426. package/dist/pdf/document/pdf-document.css.d.ts +20 -0
  427. package/dist/pdf/document/pdf-document.css.d.ts.map +1 -0
  428. package/dist/pdf/document/pdf-document.css.js +239 -0
  429. package/dist/pdf/document/pdf-document.css.js.map +1 -0
  430. package/dist/pdf/document/pdf-document.renderer.d.ts +10 -0
  431. package/dist/pdf/document/pdf-document.renderer.d.ts.map +1 -0
  432. package/dist/pdf/document/pdf-document.renderer.js +153 -0
  433. package/dist/pdf/document/pdf-document.renderer.js.map +1 -0
  434. package/dist/pdf/document/pdf-document.service.d.ts +27 -0
  435. package/dist/pdf/document/pdf-document.service.d.ts.map +1 -0
  436. package/dist/pdf/document/pdf-document.service.js +81 -0
  437. package/dist/pdf/document/pdf-document.service.js.map +1 -0
  438. package/dist/pdf/document/pdf-document.theme.d.ts +41 -0
  439. package/dist/pdf/document/pdf-document.theme.d.ts.map +1 -0
  440. package/dist/pdf/document/pdf-document.theme.js +105 -0
  441. package/dist/pdf/document/pdf-document.theme.js.map +1 -0
  442. package/dist/pdf/document/pdf-document.types.d.ts +108 -0
  443. package/dist/pdf/document/pdf-document.types.d.ts.map +1 -0
  444. package/dist/pdf/document/pdf-document.types.js +18 -0
  445. package/dist/pdf/document/pdf-document.types.js.map +1 -0
  446. package/dist/pdf/pdf.module.d.ts.map +1 -1
  447. package/dist/pdf/pdf.module.js +3 -2
  448. package/dist/pdf/pdf.module.js.map +1 -1
  449. package/dist/pdf/pdf.service.d.ts +69 -1
  450. package/dist/pdf/pdf.service.d.ts.map +1 -1
  451. package/dist/pdf/pdf.service.js +164 -63
  452. package/dist/pdf/pdf.service.js.map +1 -1
  453. package/dist/pdf/templates/commercial-proposal.template.d.ts +96 -0
  454. package/dist/pdf/templates/commercial-proposal.template.d.ts.map +1 -0
  455. package/dist/pdf/templates/commercial-proposal.template.js +276 -0
  456. package/dist/pdf/templates/commercial-proposal.template.js.map +1 -0
  457. package/dist/profile/dto/change-password.dto.d.ts.map +1 -1
  458. package/dist/profile/dto/change-password.dto.js +4 -6
  459. package/dist/profile/dto/change-password.dto.js.map +1 -1
  460. package/dist/profile/profile.controller.d.ts +1 -1
  461. package/dist/profile/profile.service.d.ts +1 -1
  462. package/dist/profile/profile.service.d.ts.map +1 -1
  463. package/dist/profile/profile.service.js +9 -3
  464. package/dist/profile/profile.service.js.map +1 -1
  465. package/dist/realtime/realtime-tick.coalescer.d.ts +34 -0
  466. package/dist/realtime/realtime-tick.coalescer.d.ts.map +1 -0
  467. package/dist/realtime/realtime-tick.coalescer.js +60 -0
  468. package/dist/realtime/realtime-tick.coalescer.js.map +1 -0
  469. package/dist/realtime/realtime.service.d.ts +2 -0
  470. package/dist/realtime/realtime.service.d.ts.map +1 -1
  471. package/dist/realtime/realtime.service.js +22 -2
  472. package/dist/realtime/realtime.service.js.map +1 -1
  473. package/dist/redis/redis-kv.service.d.ts +90 -0
  474. package/dist/redis/redis-kv.service.d.ts.map +1 -0
  475. package/dist/redis/redis-kv.service.js +247 -0
  476. package/dist/redis/redis-kv.service.js.map +1 -0
  477. package/dist/redis/redis-master.d.ts +29 -0
  478. package/dist/redis/redis-master.d.ts.map +1 -0
  479. package/dist/redis/redis-master.js +81 -0
  480. package/dist/redis/redis-master.js.map +1 -0
  481. package/dist/redis/redis-pubsub.d.ts +12 -0
  482. package/dist/redis/redis-pubsub.d.ts.map +1 -1
  483. package/dist/redis/redis-pubsub.js +25 -2
  484. package/dist/redis/redis-pubsub.js.map +1 -1
  485. package/dist/redis/redis.module.d.ts +11 -0
  486. package/dist/redis/redis.module.d.ts.map +1 -0
  487. package/dist/redis/redis.module.js +30 -0
  488. package/dist/redis/redis.module.js.map +1 -0
  489. package/dist/session/session.controller.d.ts +6 -2
  490. package/dist/session/session.controller.d.ts.map +1 -1
  491. package/dist/session/session.service.d.ts +118 -15
  492. package/dist/session/session.service.d.ts.map +1 -1
  493. package/dist/session/session.service.js +251 -77
  494. package/dist/session/session.service.js.map +1 -1
  495. package/dist/setting/favicon.controller.d.ts +26 -0
  496. package/dist/setting/favicon.controller.d.ts.map +1 -0
  497. package/dist/setting/favicon.controller.js +130 -0
  498. package/dist/setting/favicon.controller.js.map +1 -0
  499. package/dist/setting/setting.controller.d.ts +5 -0
  500. package/dist/setting/setting.controller.d.ts.map +1 -1
  501. package/dist/setting/setting.module.d.ts.map +1 -1
  502. package/dist/setting/setting.module.js +2 -1
  503. package/dist/setting/setting.module.js.map +1 -1
  504. package/dist/setting/setting.service.d.ts +7 -0
  505. package/dist/setting/setting.service.d.ts.map +1 -1
  506. package/dist/setting/setting.service.js +38 -3
  507. package/dist/setting/setting.service.js.map +1 -1
  508. package/dist/task/task.service.d.ts +17 -0
  509. package/dist/task/task.service.d.ts.map +1 -1
  510. package/dist/task/task.service.js +48 -13
  511. package/dist/task/task.service.js.map +1 -1
  512. package/dist/token/token.service.d.ts +19 -1
  513. package/dist/token/token.service.d.ts.map +1 -1
  514. package/dist/token/token.service.js +39 -2
  515. package/dist/token/token.service.js.map +1 -1
  516. package/dist/user/user-credential.util.d.ts +10 -0
  517. package/dist/user/user-credential.util.d.ts.map +1 -1
  518. package/dist/user/user-credential.util.js +14 -1
  519. package/dist/user/user-credential.util.js.map +1 -1
  520. package/dist/user/user.service.d.ts +12 -4
  521. package/dist/user/user.service.d.ts.map +1 -1
  522. package/dist/user/user.service.js +2 -11
  523. package/dist/user/user.service.js.map +1 -1
  524. package/dist/user/user.sort.d.ts +16 -0
  525. package/dist/user/user.sort.d.ts.map +1 -0
  526. package/dist/user/user.sort.js +25 -0
  527. package/dist/user/user.sort.js.map +1 -0
  528. package/dist/utils/abbreviate-name.d.ts +13 -0
  529. package/dist/utils/abbreviate-name.d.ts.map +1 -0
  530. package/dist/utils/abbreviate-name.js +42 -0
  531. package/dist/utils/abbreviate-name.js.map +1 -0
  532. package/dist/utils/date-range-query.dto.d.ts +11 -0
  533. package/dist/utils/date-range-query.dto.d.ts.map +1 -0
  534. package/dist/utils/date-range-query.dto.js +63 -0
  535. package/dist/utils/date-range-query.dto.js.map +1 -0
  536. package/dist/utils/date-range.d.ts +96 -0
  537. package/dist/utils/date-range.d.ts.map +1 -0
  538. package/dist/utils/date-range.js +172 -0
  539. package/dist/utils/date-range.js.map +1 -0
  540. package/dist/webhook-integration/webhook-utils.d.ts.map +1 -1
  541. package/dist/webhook-integration/webhook-utils.js +24 -0
  542. package/dist/webhook-integration/webhook-utils.js.map +1 -1
  543. package/dist/whatsapp-sender/whatsapp-recipients.d.ts +49 -0
  544. package/dist/whatsapp-sender/whatsapp-recipients.d.ts.map +1 -0
  545. package/dist/whatsapp-sender/whatsapp-recipients.js +66 -0
  546. package/dist/whatsapp-sender/whatsapp-recipients.js.map +1 -0
  547. package/dist/whatsapp-sender/whatsapp-sender.service.d.ts +25 -0
  548. package/dist/whatsapp-sender/whatsapp-sender.service.d.ts.map +1 -1
  549. package/dist/whatsapp-sender/whatsapp-sender.service.js +71 -7
  550. package/dist/whatsapp-sender/whatsapp-sender.service.js.map +1 -1
  551. package/hedhog/data/integration_provider.yaml +9 -0
  552. package/hedhog/data/integration_type.yaml +12 -0
  553. package/hedhog/data/locale.yaml +0 -1
  554. package/hedhog/data/mail.yaml +2 -2
  555. package/hedhog/data/mail_layout.yaml +6 -3
  556. package/hedhog/data/menu.yaml +25 -5
  557. package/hedhog/data/route.yaml +148 -0
  558. package/hedhog/data/setting_group.yaml +189 -3
  559. package/hedhog/frontend/app/access-log/page.tsx.ejs +47 -53
  560. package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +33 -8
  561. package/hedhog/frontend/app/account/components/mcp-tokens.tsx.ejs +54 -13
  562. package/hedhog/frontend/app/account/components/mfa-method-card.tsx.ejs +7 -1
  563. package/hedhog/frontend/app/account/components/recovery-codes-dialog.tsx.ejs +1 -0
  564. package/hedhog/frontend/app/account/components/tokens.tsx.ejs +47 -17
  565. package/hedhog/frontend/app/ai_agent/page.tsx.ejs +46 -5
  566. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-form.tsx.ejs +101 -30
  567. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-versions.tsx.ejs +24 -8
  568. package/hedhog/frontend/app/ai_instruction/page.tsx.ejs +80 -15
  569. package/hedhog/frontend/app/configurations/[slug]/components/image-optimization-action.tsx.ejs +166 -0
  570. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.test.tsx.ejs +145 -45
  571. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +99 -219
  572. package/hedhog/frontend/app/configurations/[slug]/page.tsx.ejs +19 -1
  573. package/hedhog/frontend/app/configurations/layout.tsx.ejs +19 -5
  574. package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +2 -8
  575. package/hedhog/frontend/app/dashboard/components/dynamic-widget.tsx.ejs +3 -0
  576. package/hedhog/frontend/app/dashboard/components/widget-wrapper.tsx.ejs +4 -0
  577. package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +43 -44
  578. package/hedhog/frontend/app/dashboard/management/tabs/component-roles-tab.tsx.ejs +41 -3
  579. package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +42 -2
  580. package/hedhog/frontend/app/dashboard/management/tabs/dashboard-roles-tab.tsx.ejs +41 -3
  581. package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +39 -4
  582. package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +42 -3
  583. package/hedhog/frontend/app/incidents/page.tsx.ejs +60 -31
  584. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +45 -5
  585. package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +92 -15
  586. package/hedhog/frontend/app/mail/log/page.tsx.ejs +109 -61
  587. package/hedhog/frontend/app/mail/template/page.tsx.ejs +125 -23
  588. package/hedhog/frontend/app/mcp_chat/chat-components.tsx.ejs +13 -15
  589. package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +116 -110
  590. package/hedhog/frontend/app/message-templates/_components/message-template-sheet.tsx.ejs +245 -0
  591. package/hedhog/frontend/app/message-templates/_lib/types.ts.ejs +24 -0
  592. package/hedhog/frontend/app/message-templates/page.tsx.ejs +182 -0
  593. package/hedhog/frontend/app/pages/_components/page-wizard-sheet.tsx.ejs +1 -0
  594. package/hedhog/frontend/app/pages/page.tsx.ejs +94 -9
  595. package/hedhog/frontend/app/roles/menus.tsx.ejs +4 -4
  596. package/hedhog/frontend/app/roles/routes.tsx.ejs +4 -4
  597. package/hedhog/frontend/app/roles/users.tsx.ejs +4 -4
  598. package/hedhog/frontend/app/setting-list/page.tsx.ejs +71 -9
  599. package/hedhog/frontend/app/users/page.tsx.ejs +77 -67
  600. package/hedhog/frontend/messages/en.json +237 -37
  601. package/hedhog/frontend/messages/pt.json +237 -37
  602. package/hedhog/table/cron_lock.yaml +17 -0
  603. package/hedhog/table/file.yaml +13 -1
  604. package/hedhog/table/file_upload_chunk.yaml +23 -0
  605. package/hedhog/table/file_upload_ticket.yaml +67 -0
  606. package/hedhog/table/mcp_tool_call_log.yaml +30 -22
  607. package/hedhog/table/message_template.yaml +33 -0
  608. package/hedhog/table/setting.yaml +8 -0
  609. package/hedhog/table/user_session.yaml +36 -1
  610. package/package.json +5 -3
  611. package/src/ai/ai-agent.sort.ts +21 -0
  612. package/src/ai/ai-attachment.fragments.spec.ts +56 -3
  613. package/src/ai/ai.service.responses.spec.ts +429 -0
  614. package/src/ai/ai.service.spec.ts +173 -1
  615. package/src/ai/ai.service.ts +473 -220
  616. package/src/ai-instruction/ai-instruction.service.ts +32 -28
  617. package/src/ai-instruction/ai-instruction.sort.ts +32 -0
  618. package/src/ai-instruction/ai-prompt-resolver.service.spec.ts +44 -0
  619. package/src/ai-instruction/ai-prompt-resolver.service.ts +29 -0
  620. package/src/auth/auth.module.ts +8 -1
  621. package/src/auth/auth.service.spec.ts +108 -1
  622. package/src/auth/auth.service.ts +59 -5
  623. package/src/auth/guards/auth.guard.ts +9 -1
  624. package/src/authorize/authorize.controller.ts +126 -11
  625. package/src/authorize/authorize.errors.ts +55 -0
  626. package/src/authorize/authorize.module.ts +6 -1
  627. package/src/authorize/authorize.service.spec.ts +379 -7
  628. package/src/authorize/authorize.service.ts +309 -23
  629. package/src/authorize/dto/authorize-info.dto.ts +21 -3
  630. package/src/authorize/dto/consent.dto.ts +21 -3
  631. package/src/authorize/dto/token.dto.ts +24 -2
  632. package/src/authorize/oauth-exception.filter.ts +58 -0
  633. package/src/authorize/oauth-metadata.service.ts +67 -0
  634. package/src/authorize/well-known.controller.spec.ts +84 -0
  635. package/src/authorize/well-known.controller.ts +96 -0
  636. package/src/captcha/captcha.service.spec.ts +211 -4
  637. package/src/captcha/captcha.service.ts +127 -10
  638. package/src/core.module.ts +9 -0
  639. package/src/dashboard/dashboard-component-role/dashboard-component-role.service.ts +2 -3
  640. package/src/dashboard/dashboard-component-role/dashboard-component-role.sort.ts +18 -0
  641. package/src/dashboard/dashboard-item/dashboard-item.service.ts +2 -3
  642. package/src/dashboard/dashboard-item/dashboard-item.sort.ts +19 -0
  643. package/src/dashboard/dashboard-role/dashboard-role.service.ts +2 -3
  644. package/src/dashboard/dashboard-role/dashboard-role.sort.ts +16 -0
  645. package/src/document-kit/document-kit.module.ts +12 -0
  646. package/src/document-kit/document-kit.registry.ts +125 -0
  647. package/src/document-kit/document-kit.service.spec.ts +156 -0
  648. package/src/document-kit/document-kit.service.ts +173 -0
  649. package/src/file/file-upload-ticket.service.spec.ts +422 -0
  650. package/src/file/file-upload-ticket.service.ts +513 -0
  651. package/src/file/file.controller.ts +65 -0
  652. package/src/file/file.module.ts +14 -2
  653. package/src/file/file.service.spec.ts +262 -1
  654. package/src/file/file.service.ts +505 -87
  655. package/src/file/filename-encoding.util.spec.ts +54 -0
  656. package/src/file/filename-encoding.util.ts +67 -0
  657. package/src/file/image-optimization.service.spec.ts +156 -19
  658. package/src/file/image-optimization.service.ts +106 -21
  659. package/src/file/mcp-file-source.schema.ts +86 -0
  660. package/src/file/mcp-file-source.service.spec.ts +473 -0
  661. package/src/file/mcp-file-source.service.ts +445 -0
  662. package/src/file/mimetype-sniff.util.spec.ts +126 -0
  663. package/src/file/mimetype-sniff.util.ts +265 -0
  664. package/src/file/remote-fetch.util.spec.ts +117 -0
  665. package/src/file/remote-fetch.util.ts +330 -0
  666. package/src/impersonation/dto/start-impersonation.dto.ts +5 -2
  667. package/src/impersonation/impersonation.service.spec.ts +24 -1
  668. package/src/impersonation/impersonation.service.ts +14 -4
  669. package/src/incident/incident.controller.ts +9 -1
  670. package/src/incident/incident.service.ts +7 -0
  671. package/src/index.ts +23 -1
  672. package/src/integration-profile/integration-profile.secrets.ts +28 -0
  673. package/src/integration-profile/integration-profile.service.ts +7 -64
  674. package/src/integration-profile/integration-profile.utils.spec.ts +9 -0
  675. package/src/integration-profile/integration-profile.utils.ts +5 -5
  676. package/src/integration-profile/testers/ai.testers.ts +70 -5
  677. package/src/integration-profile/testers/index.ts +2 -0
  678. package/src/integration-profile/testers/remaining.testers.spec.ts +144 -1
  679. package/src/integration-profile/testers/shipping.testers.ts +94 -0
  680. package/src/integration-profile/testers/tester-coverage.spec.ts +10 -0
  681. package/src/mail/dto/send.dto.ts +6 -1
  682. package/src/mail/mail-template.spec.ts +58 -1
  683. package/src/mail/mail.service.spec.ts +112 -0
  684. package/src/mail/mail.service.ts +42 -4
  685. package/src/mail-sent/mail-sent.service.ts +3 -0
  686. package/src/mail-sent/mail-sent.sort.ts +19 -0
  687. package/src/mcp/filters/mcp-challenge.filter.spec.ts +76 -0
  688. package/src/mcp/filters/mcp-challenge.filter.ts +70 -0
  689. package/src/mcp/guards/mcp-auth.guard.spec.ts +51 -2
  690. package/src/mcp/guards/mcp-auth.guard.ts +24 -1
  691. package/src/mcp/guards/mcp-permission.guard.spec.ts +109 -15
  692. package/src/mcp/guards/mcp-permission.guard.ts +76 -51
  693. package/src/mcp/mcp-audit.service.spec.ts +107 -0
  694. package/src/mcp/mcp-audit.service.ts +37 -1
  695. package/src/mcp/mcp-permission.service.spec.ts +197 -0
  696. package/src/mcp/mcp-permission.service.ts +184 -0
  697. package/src/mcp/mcp-server.service.spec.ts +84 -2
  698. package/src/mcp/mcp-server.service.ts +111 -87
  699. package/src/mcp/mcp.controller.ts +8 -1
  700. package/src/mcp/mcp.module.ts +7 -1
  701. package/src/mcp/tools/core-access-log.mcp-tools.ts +66 -0
  702. package/src/mcp/tools/core-ai.mcp-tools.ts +1 -1
  703. package/src/mcp/tools/core-dashboard-items.mcp-tools.ts +1 -1
  704. package/src/mcp/tools/core-dashboard.mcp-tools.ts +1 -1
  705. package/src/mcp/tools/core-event-webhooks.mcp-tools.ts +1 -2
  706. package/src/mcp/tools/core-files.mcp-tools.ts +202 -22
  707. package/src/mcp/tools/core-integration-events.mcp-tools.ts +1 -1
  708. package/src/mcp/tools/core-mail-sent.mcp-tools.ts +1 -1
  709. package/src/mcp/tools/core-mail.mcp-tools.ts +1 -1
  710. package/src/mcp/tools/core-menus.mcp-tools.ts +1 -2
  711. package/src/mcp/tools/core-notifications.mcp-tools.ts +0 -1
  712. package/src/mcp/tools/core-roles.mcp-tools.ts +1 -5
  713. package/src/mcp/tools/core-routes.mcp-tools.ts +1 -3
  714. package/src/mcp/tools/core-screens.mcp-tools.ts +1 -3
  715. package/src/mcp/tools/core-sessions.mcp-tools.ts +0 -2
  716. package/src/mcp/tools/core-settings.mcp-tools.ts +1 -2
  717. package/src/mcp/tools/core-users.mcp-tools.ts +1 -1
  718. package/src/mcp/tools/core-webhooks.mcp-tools.ts +1 -2
  719. package/src/mcp/types/mcp-context.type.ts +15 -6
  720. package/src/mcp-chat/mcp-chat.service.ts +8 -0
  721. package/src/meeting/meeting-provider.registry.ts +201 -0
  722. package/src/meeting/meeting.constants.ts +74 -0
  723. package/src/menu/menu.service.spec.ts +89 -0
  724. package/src/menu/menu.service.ts +29 -11
  725. package/src/message-template/dto/update-message-template.dto.ts +22 -0
  726. package/src/message-template/message-template-admin.service.spec.ts +163 -0
  727. package/src/message-template/message-template-admin.service.ts +239 -0
  728. package/src/message-template/message-template.controller.ts +52 -0
  729. package/src/message-template/message-template.module.ts +21 -0
  730. package/src/message-template/message-template.registry.spec.ts +62 -0
  731. package/src/message-template/message-template.registry.ts +125 -0
  732. package/src/message-template/message-template.service.spec.ts +149 -0
  733. package/src/message-template/message-template.service.ts +149 -0
  734. package/src/notification/notification.constants.ts +10 -0
  735. package/src/notification/notification.module.ts +3 -0
  736. package/src/notification/notification.service.spec.ts +75 -1
  737. package/src/notification/notification.service.ts +39 -1
  738. package/src/oauth/oauth-token-broker.service.spec.ts +108 -0
  739. package/src/oauth/oauth-token-broker.service.ts +129 -2
  740. package/src/oauth/oauth.controller.ts +0 -1
  741. package/src/oauth/oauth.errors.ts +7 -0
  742. package/src/oauth/oauth.module.ts +6 -1
  743. package/src/oauth/oauth.service.spec.ts +58 -7
  744. package/src/oauth/oauth.service.ts +71 -32
  745. package/src/oauth/providers/abstract.provider.spec.ts +192 -0
  746. package/src/oauth/providers/abstract.provider.ts +46 -14
  747. package/src/oauth/providers/github.provider.ts +57 -34
  748. package/src/oauth/providers/microsoft-entra-id.provider.spec.ts +133 -0
  749. package/src/oauth/providers/microsoft-entra-id.provider.ts +37 -3
  750. package/src/observability/monitored-cron.spec.ts +164 -31
  751. package/src/observability/monitored-cron.ts +154 -62
  752. package/src/pdf/document/charts/chart-bar.ts +217 -0
  753. package/src/pdf/document/charts/chart-donut.ts +163 -0
  754. package/src/pdf/document/charts/chart-gauge.ts +94 -0
  755. package/src/pdf/document/charts/chart-line.ts +159 -0
  756. package/src/pdf/document/charts/chart-primitives.ts +188 -0
  757. package/src/pdf/document/charts/chart-radar.ts +136 -0
  758. package/src/pdf/document/charts/chart-scale.spec.ts +93 -0
  759. package/src/pdf/document/charts/chart-scale.ts +141 -0
  760. package/src/pdf/document/charts/charts.spec.ts +157 -0
  761. package/src/pdf/document/charts/index.ts +7 -0
  762. package/src/pdf/document/index.ts +7 -0
  763. package/src/pdf/document/pdf-document.chrome.ts +38 -0
  764. package/src/pdf/document/pdf-document.css.ts +238 -0
  765. package/src/pdf/document/pdf-document.renderer.spec.ts +129 -0
  766. package/src/pdf/document/pdf-document.renderer.ts +215 -0
  767. package/src/pdf/document/pdf-document.service.ts +77 -0
  768. package/src/pdf/document/pdf-document.theme.ts +146 -0
  769. package/src/pdf/document/pdf-document.types.ts +97 -0
  770. package/src/pdf/pdf.module.ts +3 -2
  771. package/src/pdf/pdf.service.spec.ts +131 -0
  772. package/src/pdf/pdf.service.ts +245 -82
  773. package/src/pdf/templates/commercial-proposal.template.spec.ts +177 -0
  774. package/src/pdf/templates/commercial-proposal.template.ts +400 -0
  775. package/src/profile/dto/change-password.dto.ts +25 -7
  776. package/src/profile/profile.service.spec.ts +97 -0
  777. package/src/profile/profile.service.ts +9 -3
  778. package/src/realtime/realtime-tick.coalescer.spec.ts +92 -0
  779. package/src/realtime/realtime-tick.coalescer.ts +60 -0
  780. package/src/realtime/realtime.service.spec.ts +33 -0
  781. package/src/realtime/realtime.service.ts +28 -2
  782. package/src/redis/redis-kv.service.ts +261 -0
  783. package/src/redis/redis-master.spec.ts +111 -0
  784. package/src/redis/redis-master.ts +86 -0
  785. package/src/redis/redis-pubsub.ts +27 -3
  786. package/src/redis/redis.module.ts +17 -0
  787. package/src/session/session.service.spec.ts +121 -42
  788. package/src/session/session.service.ts +279 -82
  789. package/src/setting/favicon.controller.spec.ts +92 -0
  790. package/src/setting/favicon.controller.ts +135 -0
  791. package/src/setting/setting.module.ts +2 -1
  792. package/src/setting/setting.service.ts +44 -3
  793. package/src/task/task.service.spec.ts +41 -0
  794. package/src/task/task.service.ts +53 -13
  795. package/src/token/token.service.spec.ts +90 -0
  796. package/src/token/token.service.ts +48 -2
  797. package/src/user/user-credential.util.ts +14 -5
  798. package/src/user/user.service.ts +2 -12
  799. package/src/user/user.sort.ts +25 -0
  800. package/src/utils/abbreviate-name.spec.ts +50 -0
  801. package/src/utils/abbreviate-name.ts +44 -0
  802. package/src/utils/date-range-query.dto.ts +56 -0
  803. package/src/utils/date-range.spec.ts +175 -0
  804. package/src/utils/date-range.ts +246 -0
  805. package/src/webhook-integration/webhook-utils.spec.ts +67 -0
  806. package/src/webhook-integration/webhook-utils.ts +26 -0
  807. package/src/whatsapp-sender/whatsapp-recipients.spec.ts +89 -0
  808. package/src/whatsapp-sender/whatsapp-recipients.ts +83 -0
  809. package/src/whatsapp-sender/whatsapp-sender.service.spec.ts +185 -0
  810. package/src/whatsapp-sender/whatsapp-sender.service.ts +90 -7
  811. package/hedhog/query/device_pairing_request.sql +0 -46
  812. package/hedhog/query/impersonation_grant.sql +0 -71
  813. package/hedhog/query/oauth_authorize_pkce.sql +0 -58
  814. package/hedhog/query/oauth_mobile_state_token.sql +0 -30
  815. package/hedhog/query/oauth_web_ref_token.sql +0 -25
@@ -17,6 +17,7 @@ import {
17
17
  AiPromptResolverService,
18
18
  applyGeminiModelParams,
19
19
  applyOpenAiModelParams,
20
+ applyOpenAiResponsesParams,
20
21
  mergeModelParams,
21
22
  type PromptSource,
22
23
  } from '../ai-instruction/ai-prompt-resolver.service';
@@ -29,6 +30,7 @@ import {
29
30
  DEEPSEEK_DEFAULT_MODEL,
30
31
  } from '../integration-profile/integration-profile.utils';
31
32
  import { SettingService } from '../setting/setting.service';
33
+ import { AI_AGENT_SORT } from './ai-agent.sort';
32
34
  import {
33
35
  attachedFileBlock,
34
36
  binaryAttachmentMarker,
@@ -96,6 +98,56 @@ type AiAttachment = {
96
98
  buffer: Buffer;
97
99
  };
98
100
 
101
+ /**
102
+ * Como o anexo efetivamente chegou ao modelo na última chamada.
103
+ *
104
+ * Sem isso quem chama só consegue adivinhar — foi exatamente o que o extrator
105
+ * de notas fiscais fazia, deduzindo pelo tamanho do texto do PDF, e por isso
106
+ * insistia em reperguntar ao modelo sobre um anexo que ele nunca recebeu.
107
+ * `unreadable-pdf` e `binary` são os dois casos em que o modelo recebeu um
108
+ * marcador no lugar do documento.
109
+ */
110
+ export type AiAttachmentDelivery =
111
+ | 'none'
112
+ | 'image'
113
+ | 'pdf-document'
114
+ | 'pdf-text'
115
+ | 'text'
116
+ | 'unreadable-pdf'
117
+ | 'binary';
118
+
119
+ /** Anexo que o modelo realmente leu, em vez de um marcador dizendo que não deu. */
120
+ const DELIVERED_TO_MODEL: ReadonlySet<AiAttachmentDelivery> = new Set([
121
+ 'image',
122
+ 'pdf-document',
123
+ 'pdf-text',
124
+ 'text',
125
+ ]);
126
+
127
+ /** Entregas em que o modelo enxergou os pixels da página, não só texto. */
128
+ const DELIVERED_AS_PIXELS: ReadonlySet<AiAttachmentDelivery> = new Set([
129
+ 'image',
130
+ 'pdf-document',
131
+ ]);
132
+
133
+ export function isAttachmentDeliveredToModel(
134
+ delivery?: AiAttachmentDelivery | null,
135
+ ): boolean {
136
+ return !!delivery && DELIVERED_TO_MODEL.has(delivery);
137
+ }
138
+
139
+ export function isAttachmentDeliveredAsPixels(
140
+ delivery?: AiAttachmentDelivery | null,
141
+ ): boolean {
142
+ return !!delivery && DELIVERED_AS_PIXELS.has(delivery);
143
+ }
144
+
145
+ type AiChatResult = {
146
+ content: string;
147
+ usage?: { input: number; output: number; total: number; cached?: number };
148
+ delivery?: AiAttachmentDelivery;
149
+ };
150
+
99
151
  type AiProfileCredentials = {
100
152
  apiKey: string;
101
153
  organization?: string;
@@ -104,6 +156,29 @@ type AiProfileCredentials = {
104
156
  baseUrl?: string;
105
157
  };
106
158
 
159
+ /**
160
+ * O setting de um provider aponta para o perfil de outro provider.
161
+ *
162
+ * Classe própria, e não um `BadRequestException` solto, para uma escada de
163
+ * fallback conseguir parar antes de disparar chamadas que vão dar 4xx com
164
+ * certeza — era o que o extrator de notas fiscais fazia oito vezes por upload,
165
+ * engolindo cada uma delas.
166
+ */
167
+ export class AiProfileProviderMismatchError extends BadRequestException {
168
+ constructor(
169
+ readonly requestedProvider: string,
170
+ readonly profileSlug: string,
171
+ readonly profileProviderSlug: string,
172
+ readonly settingSlug: string,
173
+ ) {
174
+ super(
175
+ `AI profile "${profileSlug}" (setting "${settingSlug}") belongs to provider ` +
176
+ `"${profileProviderSlug}", not "${requestedProvider}". ` +
177
+ `Point Settings → AI → "${settingSlug}" at a ${requestedProvider} profile, or leave it empty.`,
178
+ );
179
+ }
180
+ }
181
+
107
182
  @Injectable()
108
183
  export class AiService implements OnModuleInit {
109
184
  private readonly logger = new Logger(AiService.name);
@@ -147,7 +222,7 @@ export class AiService implements OnModuleInit {
147
222
  const stepStart = Date.now();
148
223
 
149
224
  try {
150
- const { content, usage } = await this.withTimeout(
225
+ const { content, usage, delivery } = await this.withTimeout(
151
226
  this.chatWithProvider(provider, {
152
227
  message: data.message,
153
228
  model,
@@ -156,6 +231,7 @@ export class AiService implements OnModuleInit {
156
231
  apiKey: aiProfile.apiKey,
157
232
  baseUrl: aiProfile.baseUrl,
158
233
  params,
234
+ timeoutMs,
159
235
  }),
160
236
  timeoutMs,
161
237
  );
@@ -172,7 +248,12 @@ export class AiService implements OnModuleInit {
172
248
  });
173
249
  await this.aiExecution.finish(executionId, 'completed');
174
250
 
175
- return { provider, model, content };
251
+ return {
252
+ provider,
253
+ model,
254
+ content,
255
+ attachmentDelivery: delivery ?? 'none',
256
+ };
176
257
  } catch (err) {
177
258
  await this.aiExecution.finish(executionId, 'failed', String(err));
178
259
  throw err;
@@ -200,6 +281,15 @@ export class AiService implements OnModuleInit {
200
281
  * sistema. Mesmo padrão que `sendTemplatedMail` já usa para e-mail.
201
282
  */
202
283
  profileSlug?: string;
284
+ /**
285
+ * Modelo a usar, no lugar do que o perfil define.
286
+ *
287
+ * Existe para o chamador que tem a própria configuração de modelo — o SAC
288
+ * lê `sac.ai.model` do operador e não tinha por onde entregá-la. Vence a
289
+ * linha de `ai_instruction` e o perfil, na precedência que
290
+ * `mergeModelParams` documenta: argumento explícito primeiro.
291
+ */
292
+ model?: string;
203
293
  params?: AiChatModelParams;
204
294
  contextSlug?: string;
205
295
  }): Promise<{
@@ -207,6 +297,14 @@ export class AiService implements OnModuleInit {
207
297
  model: string;
208
298
  content: string;
209
299
  promptSources: Record<string, PromptSource>;
300
+ /**
301
+ * Linha de `ai_execution` desta chamada, onde o custo estimado é registrado.
302
+ *
303
+ * `null` quando o registro não pôde ser aberto: `AiExecutionService.start`
304
+ * devolve `0` nesse caso, e quem grava esse id numa coluna própria estaria
305
+ * apontando para uma execução que não existe.
306
+ */
307
+ executionId: number | null;
210
308
  }> {
211
309
  const slugs = input.systemPromptSlug
212
310
  ? [input.systemPromptSlug, input.userPromptSlug]
@@ -233,7 +331,10 @@ export class AiService implements OnModuleInit {
233
331
  const provider = target.provider;
234
332
  const aiProfile = target.profile;
235
333
  const model =
236
- stored.model || aiProfile.model || this.getDefaultModel(provider);
334
+ input.model?.trim() ||
335
+ stored.model ||
336
+ aiProfile.model ||
337
+ this.getDefaultModel(provider);
237
338
  const timeoutMs = aiProfile.timeoutMs ?? DEFAULT_AI_CALL_TIMEOUT_MS;
238
339
 
239
340
  const params: AiChatModelParams = {
@@ -277,32 +378,44 @@ export class AiService implements OnModuleInit {
277
378
  Object.entries(resolved).map(([slug, prompt]) => [slug, prompt.source]),
278
379
  );
279
380
 
280
- return { provider, model, content, promptSources };
381
+ // `executionId || null`: `start` devolve 0 quando não conseguiu abrir a
382
+ // linha, e quem guarda esse id (o SAC, em `ai_execution_id`) ficaria
383
+ // apontando para uma execução inexistente — a coluna não tem FK para
384
+ // barrar isso.
385
+ return { provider, model, content, promptSources, executionId: executionId || null };
281
386
  } catch (err) {
282
387
  await this.aiExecution.finish(executionId, 'failed', String(err));
283
388
  throw err;
284
389
  }
285
390
  }
286
391
 
392
+ /**
393
+ * `external_agent_id` nasce nulo: a Assistants API da OpenAI, que devolvia o
394
+ * `asst_...` guardado nessa coluna, foi desligada em 26/08/2026 e todo
395
+ * `POST /v1/assistants` passou a responder 404. As instruções do agente viram
396
+ * system prompt do `chat/completions` em `chatWithAgent`, então elas precisam
397
+ * estar na linha — daí resolver aqui a instrução padrão que antes ia embutida
398
+ * no assistant remoto, em vez de deixar o agente sem prompt nenhum.
399
+ */
287
400
  async createAgent(data: CreateAgentDTO) {
288
401
  const slug = data.slug.trim();
289
402
  const provider = data.provider || 'openai';
290
403
  const model = data.model || this.getDefaultModel(provider);
291
- const instructions = data.instructions?.trim() || null;
292
404
 
293
405
  const existing = await this.findAgentBySlug(slug);
294
406
  if (existing) {
295
407
  return existing;
296
408
  }
297
409
 
298
- let externalAgentId: string | null = null;
410
+ let instructions = data.instructions?.trim() || null;
299
411
 
300
- if (provider === 'openai') {
301
- externalAgentId = await this.createOpenAiAssistant({
302
- slug,
303
- model,
304
- instructions,
412
+ if (!instructions) {
413
+ const defaultInstructions = await this.promptResolver.resolve({
414
+ slug: CORE_AI_PROMPT_SLUGS.agentDefaultInstructions,
415
+ variables: { slug },
305
416
  });
417
+
418
+ instructions = defaultInstructions.content;
306
419
  }
307
420
 
308
421
  await this.prismaService.$executeRaw`
@@ -312,7 +425,7 @@ export class AiService implements OnModuleInit {
312
425
  CAST(${provider} AS ai_agent_provider_7cb93b1143_enum),
313
426
  ${model},
314
427
  ${instructions},
315
- ${externalAgentId}
428
+ NULL
316
429
  )
317
430
  `;
318
431
 
@@ -358,29 +471,22 @@ export class AiService implements OnModuleInit {
358
471
  Math.max(Number(paginationParams?.pageSize || 10), 1),
359
472
  100,
360
473
  );
361
- const offset = (page - 1) * pageSize;
474
+ const skip = (page - 1) * pageSize;
362
475
  const search = String(paginationParams?.search || '').trim();
363
476
 
364
- const whereClause = search
365
- ? Prisma.sql`WHERE LOWER(slug) LIKE LOWER(${`%${search}%`})`
366
- : Prisma.empty;
367
-
368
- const data = await this.prismaService.$queryRaw<AiAgentRecord[]>(Prisma.sql`
369
- SELECT id, slug, provider, model, instructions, external_agent_id, created_at, updated_at
370
- FROM ai_agent
371
- ${whereClause}
372
- ORDER BY id DESC
373
- LIMIT ${pageSize}
374
- OFFSET ${offset}
375
- `);
376
-
377
- const totalResult = await this.prismaService.$queryRaw<Array<{ total: bigint | number | string }>>(Prisma.sql`
378
- SELECT COUNT(*) AS total
379
- FROM ai_agent
380
- ${whereClause}
381
- `);
477
+ const where: Prisma.ai_agentWhereInput = search
478
+ ? { slug: { contains: search, mode: 'insensitive' } }
479
+ : {};
382
480
 
383
- const total = Number(totalResult?.[0]?.total || 0);
481
+ const [data, total] = await Promise.all([
482
+ this.prismaService.ai_agent.findMany({
483
+ where,
484
+ orderBy: AI_AGENT_SORT.toPrismaOrderBy(paginationParams),
485
+ skip,
486
+ take: pageSize,
487
+ }),
488
+ this.prismaService.ai_agent.count({ where }),
489
+ ]);
384
490
 
385
491
  return {
386
492
  data,
@@ -420,25 +526,15 @@ export class AiService implements OnModuleInit {
420
526
  }
421
527
  }
422
528
 
423
- let externalAgentId = currentAgent.external_agent_id;
424
-
425
- if (provider !== 'openai') {
426
- externalAgentId = null;
427
- } else if (!externalAgentId) {
428
- externalAgentId = await this.createOpenAiAssistant({
429
- slug,
430
- model,
431
- instructions,
432
- });
433
- }
434
-
529
+ // Zera o `asst_...` legado: a coluna deixou de ser alimentada quando a
530
+ // Assistants API foi desligada, e valor antigo aqui não tem mais para onde ir.
435
531
  await this.prismaService.$executeRaw`
436
532
  UPDATE ai_agent
437
533
  SET slug = ${slug},
438
534
  provider = CAST(${provider} AS ai_agent_provider_7cb93b1143_enum),
439
535
  model = ${model},
440
536
  instructions = ${instructions},
441
- external_agent_id = ${externalAgentId},
537
+ external_agent_id = NULL,
442
538
  updated_at = NOW()
443
539
  WHERE id = ${agentId}
444
540
  `;
@@ -479,36 +575,36 @@ export class AiService implements OnModuleInit {
479
575
  throw new NotFoundException(`AI agent with slug "${slug}" was not found.`);
480
576
  }
481
577
 
482
- if (agent.provider === 'openai' && agent.external_agent_id && !attachment) {
483
- const content = await this.chatWithOpenAiAssistant(
484
- agent.external_agent_id,
485
- data.message,
486
- );
487
-
488
- return {
489
- slug: agent.slug,
490
- provider: agent.provider,
491
- model: agent.model,
492
- content,
493
- };
494
- }
495
-
578
+ // Um único caminho para todo agente, com ou sem anexo: as instruções da
579
+ // linha viram system prompt do provider. Até 26/08/2026 o agente da OpenAI
580
+ // com `external_agent_id` e sem anexo era atendido por thread + run da
581
+ // Assistants API — que a OpenAI desligou, e que já ignorava model e
582
+ // instructions do banco porque só mandava o `assistant_id`.
496
583
  const aiProfile = await this.getAiProfile(agent.provider);
584
+ const timeoutMs = aiProfile.timeoutMs ?? DEFAULT_AI_CALL_TIMEOUT_MS;
497
585
 
498
- const { content } = await this.chatWithProvider(agent.provider, {
499
- message: data.message,
500
- model: agent.model || this.getDefaultModel(agent.provider),
501
- systemPrompt: agent.instructions || undefined,
502
- attachment,
503
- apiKey: aiProfile.apiKey,
504
- baseUrl: aiProfile.baseUrl,
505
- });
586
+ // Sem o `withTimeout` este era o único ponto público sem prazo nenhum: uma
587
+ // chamada travada no provider segurava a requisição HTTP de quem chamou
588
+ // indefinidamente.
589
+ const { content, delivery } = await this.withTimeout(
590
+ this.chatWithProvider(agent.provider, {
591
+ message: data.message,
592
+ model: agent.model || this.getDefaultModel(agent.provider),
593
+ systemPrompt: agent.instructions || undefined,
594
+ attachment,
595
+ apiKey: aiProfile.apiKey,
596
+ baseUrl: aiProfile.baseUrl,
597
+ timeoutMs,
598
+ }),
599
+ timeoutMs,
600
+ );
506
601
 
507
602
  return {
508
603
  slug: agent.slug,
509
604
  provider: agent.provider,
510
605
  model: agent.model,
511
606
  content,
607
+ attachmentDelivery: delivery ?? 'none',
512
608
  };
513
609
  }
514
610
 
@@ -580,7 +676,7 @@ export class AiService implements OnModuleInit {
580
676
  sha256: createHash('sha256').update(attachment.buffer).digest('hex'),
581
677
  };
582
678
 
583
- this.logger.warn(
679
+ this.logger.debug(
584
680
  `[AI-ATTACHMENT-DEBUG] fileId=${fileId || 'upload'} filename="${result.filename}" mime=${result.mimeType} mode=${result.mode} bytes=${result.bytes} textLength=${result.textLength}`,
585
681
  );
586
682
 
@@ -649,8 +745,9 @@ export class AiService implements OnModuleInit {
649
745
  apiKey?: string;
650
746
  baseUrl?: string;
651
747
  params?: AiChatModelParams;
748
+ timeoutMs?: number;
652
749
  },
653
- ) {
750
+ ): Promise<AiChatResult> {
654
751
  if (provider === 'gemini') {
655
752
  return this.chatWithGemini(input);
656
753
  }
@@ -721,7 +818,49 @@ export class AiService implements OnModuleInit {
721
818
  );
722
819
  }
723
820
 
724
- return (await this.loadAiProfileBySlug(profileSlug)).config;
821
+ const loaded = await this.loadAiProfileBySlug(profileSlug);
822
+ const mapped = PROVIDER_BY_INTEGRATION_SLUG[loaded.providerSlug];
823
+
824
+ // `resolveProviderAndProfile` já protegia o caminho do perfil explícito
825
+ // ("pedir openai e apontar um perfil Claude bateria na API errada com a
826
+ // chave errada"); o caminho do setting global ficara de fora. Sem isso, um
827
+ // perfil OpenAI no campo do Gemini fazia o serviço postar a chave da OpenAI
828
+ // no endpoint do Google e engolir o 4xx de cada modelo tentado.
829
+ if (mapped !== provider) {
830
+ this.logger.error(
831
+ `[AI-PROFILE] "${slug}" = "${profileSlug}" é um perfil ${loaded.providerSlug}, ` +
832
+ `mas ${provider} foi pedido.`,
833
+ );
834
+ throw new AiProfileProviderMismatchError(
835
+ provider,
836
+ profileSlug,
837
+ loaded.providerSlug,
838
+ slug,
839
+ );
840
+ }
841
+
842
+ return loaded.config;
843
+ }
844
+
845
+ /**
846
+ * "Este provider dá para usar?", sem lançar.
847
+ *
848
+ * Existe para uma escada de fallback perguntar antes de tentar, em vez de
849
+ * gastar uma requisição HTTP por modelo só para descobrir que a credencial
850
+ * nem é daquele provider.
851
+ */
852
+ async isProviderConfigured(
853
+ provider: AiProvider,
854
+ ): Promise<{ ok: boolean; reason?: string }> {
855
+ try {
856
+ await this.getAiProfile(provider);
857
+ return { ok: true };
858
+ } catch (error) {
859
+ return {
860
+ ok: false,
861
+ reason: error instanceof Error ? error.message : String(error),
862
+ };
863
+ }
725
864
  }
726
865
 
727
866
  private async loadAiProfileBySlug(profileSlug: string): Promise<{
@@ -776,6 +915,7 @@ export class AiService implements OnModuleInit {
776
915
  provider = 'openai',
777
916
  baseUrl = 'https://api.openai.com/v1',
778
917
  supportsImages = true,
918
+ timeoutMs,
779
919
  }: {
780
920
  message: string;
781
921
  model: string;
@@ -787,16 +927,44 @@ export class AiService implements OnModuleInit {
787
927
  provider?: AiProvider;
788
928
  baseUrl?: string;
789
929
  supportsImages?: boolean;
790
- }): Promise<{ content: string; usage?: { input: number; output: number; total: number } }> {
930
+ timeoutMs?: number;
931
+ }): Promise<AiChatResult> {
791
932
  const openai = apiKey ?? (await this.getAiApiKey(provider));
792
933
 
934
+ if (this.shouldUseOpenAiResponses({ provider, baseUrl, model, attachment })) {
935
+ try {
936
+ return await this.chatWithOpenAiResponses({
937
+ message,
938
+ model,
939
+ systemPrompt,
940
+ attachment: attachment!,
941
+ apiKey: openai,
942
+ baseUrl,
943
+ params,
944
+ timeoutMs,
945
+ });
946
+ } catch (error) {
947
+ const status = Number((error as any)?.response?.status);
948
+ // 400/404/422 = este modelo ou esta conta não aceita `input_file`;
949
+ // cair para o chat/completions é melhor que falhar. Qualquer outra
950
+ // coisa (401, 429, 5xx, timeout) é falha de verdade e não pode ser
951
+ // mascarada por uma segunda chamada mais fraca, que só reproduziria o
952
+ // marcador de PDF ilegível e devolveria "não achei nada".
953
+ if (![400, 404, 422].includes(status)) throw error;
954
+ this.logger.warn(
955
+ `[AI-RESPONSES] /v1/responses recusou (status=${status}); usando chat/completions. ` +
956
+ `apiError=${(error as any)?.response?.data?.error?.message ?? 'n/a'}`,
957
+ );
958
+ }
959
+ }
960
+
793
961
  const messages: Array<any> = [];
794
962
 
795
963
  if (systemPrompt) {
796
964
  messages.push({ role: 'system', content: systemPrompt });
797
965
  }
798
966
 
799
- const userContent = await this.buildOpenAiUserContent(
967
+ const { content: userContent, delivery } = await this.buildOpenAiUserContent(
800
968
  message,
801
969
  attachment,
802
970
  supportsImages,
@@ -823,10 +991,200 @@ export class AiService implements OnModuleInit {
823
991
  const u = response.data?.usage;
824
992
  return {
825
993
  content: response.data?.choices?.[0]?.message?.content || '',
994
+ delivery,
826
995
  usage: u ? { input: u.prompt_tokens ?? 0, output: u.completion_tokens ?? 0, total: u.total_tokens ?? 0 } : undefined,
827
996
  };
828
997
  }
829
998
 
999
+ /**
1000
+ * Bytes crus de PDF acima disto não vão inline: base64 infla ~1,37x e a
1001
+ * OpenAI limita a requisição a 50 MB.
1002
+ */
1003
+ private static readonly OPENAI_RESPONSES_MAX_PDF_BYTES = 20 * 1024 * 1024;
1004
+
1005
+ /** Acima disto a conta de tokens das páginas renderizadas deixa de compensar. */
1006
+ private static readonly OPENAI_RESPONSES_MAX_PDF_PAGES = 20;
1007
+
1008
+ /**
1009
+ * Modelos que renderizam as páginas do PDF como imagem na Responses API.
1010
+ *
1011
+ * Um modelo sem visão recebendo `input_file` só recebe a camada de texto de
1012
+ * volta — o mesmo nada do chat/completions, num endpoint diferente. Deixá-lo
1013
+ * no caminho antigo mantém a falha conhecida em vez de criar uma nova.
1014
+ */
1015
+ private static readonly OPENAI_DOCUMENT_MODEL_RE =
1016
+ /^(gpt-4o|gpt-4\.1|gpt-4-turbo|gpt-5|o3|o4)/i;
1017
+
1018
+ private shouldUseOpenAiResponses(input: {
1019
+ provider: AiProvider;
1020
+ baseUrl: string;
1021
+ model: string;
1022
+ attachment?: AiAttachment | null;
1023
+ }): boolean {
1024
+ // O DeepSeek reusa o formato da OpenAI, mas não tem /v1/responses; e um
1025
+ // gateway apontado por `base_url` pode simplesmente não repassar a rota.
1026
+ if (input.provider !== 'openai') return false;
1027
+ if (!input.baseUrl.startsWith('https://api.openai.com')) return false;
1028
+ if (!input.attachment) return false;
1029
+ // Imagem já funciona pelo bloco de visão do chat/completions.
1030
+ if (!this.isPdfMime(input.attachment.mimeType)) return false;
1031
+ if (
1032
+ input.attachment.buffer.length > AiService.OPENAI_RESPONSES_MAX_PDF_BYTES
1033
+ ) {
1034
+ return false;
1035
+ }
1036
+ return AiService.OPENAI_DOCUMENT_MODEL_RE.test(input.model);
1037
+ }
1038
+
1039
+ private buildOpenAiResponsesBody(input: {
1040
+ message: string;
1041
+ model: string;
1042
+ systemPrompt?: string;
1043
+ attachment: AiAttachment;
1044
+ params?: AiChatModelParams;
1045
+ }): Record<string, unknown> {
1046
+ const base64 = input.attachment.buffer.toString('base64');
1047
+
1048
+ // Arquivo antes da instrução, como no exemplo da própria OpenAI: o modelo
1049
+ // lê o documento antes de saber o que tirar dele.
1050
+ const body: Record<string, unknown> = {
1051
+ input: [
1052
+ {
1053
+ role: 'user',
1054
+ content: [
1055
+ {
1056
+ type: 'input_file',
1057
+ filename: input.attachment.filename,
1058
+ file_data: `data:${input.attachment.mimeType};base64,${base64}`,
1059
+ },
1060
+ { type: 'input_text', text: input.message },
1061
+ ],
1062
+ },
1063
+ ],
1064
+ };
1065
+
1066
+ // `instructions` é o equivalente do `messages[0].role === 'system'`.
1067
+ if (input.systemPrompt) body.instructions = input.systemPrompt;
1068
+
1069
+ return applyOpenAiResponsesParams(
1070
+ body,
1071
+ {
1072
+ model: null,
1073
+ temperature: input.params?.temperature ?? null,
1074
+ maxTokens: input.params?.maxTokens ?? null,
1075
+ },
1076
+ {
1077
+ model: input.model,
1078
+ jsonObject: input.params?.responseFormat === 'json_object',
1079
+ },
1080
+ );
1081
+ }
1082
+
1083
+ /**
1084
+ * Texto da resposta da Responses API.
1085
+ *
1086
+ * `output_text` é conveniência do SDK — o JSON HTTP cru normalmente não traz.
1087
+ * O texto mora no array `output`, ao lado de itens `reasoning` que não têm
1088
+ * `content` nenhum. Percorrer o array é o caminho real; o atalho pelo
1089
+ * `output_text` só vale para quando o campo por acaso estiver lá.
1090
+ */
1091
+ private readOpenAiResponsesText(data: any): string {
1092
+ if (typeof data?.output_text === 'string' && data.output_text.trim()) {
1093
+ return data.output_text;
1094
+ }
1095
+
1096
+ const output: any[] = Array.isArray(data?.output) ? data.output : [];
1097
+ const chunks: string[] = [];
1098
+
1099
+ for (const item of output) {
1100
+ if (item?.type && item.type !== 'message') continue;
1101
+ for (const part of item?.content ?? []) {
1102
+ if (part?.type === 'output_text' && typeof part.text === 'string') {
1103
+ chunks.push(part.text);
1104
+ } else if (part?.type === 'refusal' && typeof part.refusal === 'string') {
1105
+ this.logger.warn(
1106
+ `[AI-RESPONSES] modelo recusou: ${part.refusal.slice(0, 200)}`,
1107
+ );
1108
+ }
1109
+ }
1110
+ }
1111
+
1112
+ return chunks.join('');
1113
+ }
1114
+
1115
+ /**
1116
+ * PDF como documento, via `POST /v1/responses`.
1117
+ *
1118
+ * É o único caminho em que um PDF escaneado chega ao modelo: para modelos com
1119
+ * visão a API extrai o texto e renderiza cada página como imagem. O
1120
+ * chat/completions, mesmo aceitando um content part de PDF, só extrai texto —
1121
+ * não serviria para um scan.
1122
+ */
1123
+ private async chatWithOpenAiResponses(input: {
1124
+ message: string;
1125
+ model: string;
1126
+ systemPrompt?: string;
1127
+ attachment: AiAttachment;
1128
+ apiKey: string;
1129
+ baseUrl: string;
1130
+ params?: AiChatModelParams;
1131
+ timeoutMs?: number;
1132
+ }): Promise<AiChatResult> {
1133
+ const pages = await this.countPdfPages(input.attachment.buffer);
1134
+
1135
+ if (pages > AiService.OPENAI_RESPONSES_MAX_PDF_PAGES) {
1136
+ this.logger.warn(
1137
+ `[AI-RESPONSES] "${input.attachment.filename}" tem ${pages} páginas ` +
1138
+ `(máx. ${AiService.OPENAI_RESPONSES_MAX_PDF_PAGES}); usando chat/completions.`,
1139
+ );
1140
+ // 422 é o status que o chamador trata como "degrade", e não como falha.
1141
+ throw Object.assign(new Error('PDF acima do limite de páginas'), {
1142
+ response: { status: 422, data: { error: { message: 'too many pages' } } },
1143
+ });
1144
+ }
1145
+
1146
+ const response = await axios.post(
1147
+ `${input.baseUrl.replace(/\/+$/, '')}/responses`,
1148
+ this.buildOpenAiResponsesBody(input),
1149
+ {
1150
+ headers: {
1151
+ Authorization: `Bearer ${input.apiKey}`,
1152
+ 'Content-Type': 'application/json',
1153
+ },
1154
+ // O corpo é o PDF em base64, na casa dos megabytes, e a renderização
1155
+ // das páginas é lenta: com os padrões do axios isso trunca ou trava.
1156
+ timeout: input.timeoutMs ?? DEFAULT_AI_CALL_TIMEOUT_MS,
1157
+ maxBodyLength: Infinity,
1158
+ maxContentLength: Infinity,
1159
+ },
1160
+ );
1161
+
1162
+ if (response.data?.status && response.data.status !== 'completed') {
1163
+ this.logger.warn(
1164
+ `[AI-RESPONSES] status=${response.data.status} ` +
1165
+ `reason=${response.data?.incomplete_details?.reason ?? 'n/a'} ` +
1166
+ `model=${input.model} file="${input.attachment.filename}"`,
1167
+ );
1168
+ }
1169
+
1170
+ const u = response.data?.usage;
1171
+ return {
1172
+ content: this.readOpenAiResponsesText(response.data),
1173
+ delivery: 'pdf-document',
1174
+ usage: u
1175
+ ? {
1176
+ input: u.input_tokens ?? 0,
1177
+ output: u.output_tokens ?? 0,
1178
+ total:
1179
+ u.total_tokens ?? (u.input_tokens ?? 0) + (u.output_tokens ?? 0),
1180
+ ...(u.input_tokens_details?.cached_tokens
1181
+ ? { cached: u.input_tokens_details.cached_tokens }
1182
+ : {}),
1183
+ }
1184
+ : undefined,
1185
+ };
1186
+ }
1187
+
830
1188
  /**
831
1189
  * Anthropic (Claude).
832
1190
  *
@@ -851,7 +1209,7 @@ export class AiService implements OnModuleInit {
851
1209
  systemPrompt?: string;
852
1210
  apiKey?: string;
853
1211
  params?: AiChatModelParams;
854
- }): Promise<{ content: string; usage?: { input: number; output: number; total: number } }> {
1212
+ }): Promise<AiChatResult> {
855
1213
  const key = apiKey ?? (await this.getAiApiKey('claude'));
856
1214
 
857
1215
  const body: Record<string, unknown> = {
@@ -887,138 +1245,6 @@ export class AiService implements OnModuleInit {
887
1245
  };
888
1246
  }
889
1247
 
890
- private async createOpenAiAssistant({
891
- slug,
892
- model,
893
- instructions,
894
- }: {
895
- slug: string;
896
- model: string;
897
- instructions: string | null;
898
- }): Promise<string> {
899
- const openai = await this.getAiApiKey('openai');
900
-
901
- const defaultInstructions = await this.promptResolver.resolve({
902
- slug: CORE_AI_PROMPT_SLUGS.agentDefaultInstructions,
903
- variables: { slug },
904
- });
905
-
906
- const response = await axios.post(
907
- 'https://api.openai.com/v1/assistants',
908
- {
909
- name: slug,
910
- model,
911
- instructions: instructions || defaultInstructions.content,
912
- },
913
- {
914
- headers: {
915
- Authorization: `Bearer ${openai}`,
916
- 'Content-Type': 'application/json',
917
- 'OpenAI-Beta': 'assistants=v2',
918
- },
919
- },
920
- );
921
-
922
- const assistantId = response.data?.id;
923
-
924
- if (!assistantId) {
925
- throw new BadRequestException('OpenAI assistant creation failed.');
926
- }
927
-
928
- return assistantId;
929
- }
930
-
931
- private async chatWithOpenAiAssistant(
932
- assistantId: string,
933
- message: string,
934
- ): Promise<string> {
935
- const openai = await this.getAiApiKey('openai');
936
-
937
- const threadResponse = await axios.post(
938
- 'https://api.openai.com/v1/threads',
939
- {
940
- messages: [{ role: 'user', content: message }],
941
- },
942
- {
943
- headers: {
944
- Authorization: `Bearer ${openai}`,
945
- 'Content-Type': 'application/json',
946
- 'OpenAI-Beta': 'assistants=v2',
947
- },
948
- },
949
- );
950
-
951
- const threadId = threadResponse.data?.id;
952
-
953
- if (!threadId) {
954
- throw new BadRequestException('OpenAI thread creation failed.');
955
- }
956
-
957
- const runResponse = await axios.post(
958
- `https://api.openai.com/v1/threads/${threadId}/runs`,
959
- {
960
- assistant_id: assistantId,
961
- },
962
- {
963
- headers: {
964
- Authorization: `Bearer ${openai}`,
965
- 'Content-Type': 'application/json',
966
- 'OpenAI-Beta': 'assistants=v2',
967
- },
968
- },
969
- );
970
-
971
- const runId = runResponse.data?.id;
972
-
973
- if (!runId) {
974
- throw new BadRequestException('OpenAI assistant run failed to start.');
975
- }
976
-
977
- for (let i = 0; i < 30; i++) {
978
- const runStatusResponse = await axios.get(
979
- `https://api.openai.com/v1/threads/${threadId}/runs/${runId}`,
980
- {
981
- headers: {
982
- Authorization: `Bearer ${openai}`,
983
- 'OpenAI-Beta': 'assistants=v2',
984
- },
985
- },
986
- );
987
-
988
- const status = runStatusResponse.data?.status;
989
-
990
- if (status === 'completed') {
991
- break;
992
- }
993
-
994
- if (['cancelled', 'failed', 'expired'].includes(status)) {
995
- throw new BadRequestException(
996
- `OpenAI assistant run did not complete successfully (status: ${status}).`,
997
- );
998
- }
999
-
1000
- await this.sleep(1000);
1001
- }
1002
-
1003
- const messagesResponse = await axios.get(
1004
- `https://api.openai.com/v1/threads/${threadId}/messages`,
1005
- {
1006
- headers: {
1007
- Authorization: `Bearer ${openai}`,
1008
- 'OpenAI-Beta': 'assistants=v2',
1009
- },
1010
- },
1011
- );
1012
-
1013
- const assistantMessage = messagesResponse.data?.data?.find(
1014
- (item: any) => item.role === 'assistant',
1015
- );
1016
-
1017
- const output = assistantMessage?.content?.[0]?.text?.value;
1018
-
1019
- return output || '';
1020
- }
1021
-
1022
1248
  private async chatWithGemini({
1023
1249
  message,
1024
1250
  model,
@@ -1033,24 +1259,27 @@ export class AiService implements OnModuleInit {
1033
1259
  attachment?: AiAttachment | null;
1034
1260
  apiKey?: string;
1035
1261
  params?: AiChatModelParams;
1036
- }): Promise<{ content: string; usage?: { input: number; output: number; total: number } }> {
1262
+ }): Promise<AiChatResult> {
1037
1263
  const gemini = apiKey ?? (await this.getAiApiKey('gemini'));
1038
1264
 
1039
1265
  const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${gemini}`;
1040
1266
 
1041
1267
  const userParts: any[] = [{ text: message }];
1268
+ let delivery: AiAttachmentDelivery = 'none';
1042
1269
 
1043
1270
  if (attachment) {
1044
1271
  if (this.isTextMime(attachment.mimeType)) {
1045
1272
  userParts.push({
1046
1273
  text: attachedFileBlock(attachment.filename, this.toUtf8Text(attachment.buffer)),
1047
1274
  });
1275
+ delivery = 'text';
1048
1276
  } else if (this.isPdfMime(attachment.mimeType)) {
1049
1277
  const pdfText = await this.extractPdfText(attachment.buffer);
1050
1278
  if (pdfText) {
1051
1279
  userParts.push({
1052
1280
  text: attachedFileBlock(attachment.filename, pdfText),
1053
1281
  });
1282
+ delivery = 'pdf-text';
1054
1283
  } else {
1055
1284
  userParts.push({
1056
1285
  inline_data: {
@@ -1058,6 +1287,7 @@ export class AiService implements OnModuleInit {
1058
1287
  data: attachment.buffer.toString('base64'),
1059
1288
  },
1060
1289
  });
1290
+ delivery = 'pdf-document';
1061
1291
  }
1062
1292
  } else {
1063
1293
  userParts.push({
@@ -1066,6 +1296,7 @@ export class AiService implements OnModuleInit {
1066
1296
  data: attachment.buffer.toString('base64'),
1067
1297
  },
1068
1298
  });
1299
+ delivery = this.isImageMime(attachment.mimeType) ? 'image' : 'binary';
1069
1300
  }
1070
1301
  }
1071
1302
 
@@ -1096,14 +1327,11 @@ export class AiService implements OnModuleInit {
1096
1327
  const u = response.data?.usageMetadata;
1097
1328
  return {
1098
1329
  content,
1330
+ delivery,
1099
1331
  usage: u ? { input: u.promptTokenCount ?? 0, output: u.candidatesTokenCount ?? 0, total: u.totalTokenCount ?? 0 } : undefined,
1100
1332
  };
1101
1333
  }
1102
1334
 
1103
- private async sleep(ms: number) {
1104
- await new Promise((resolve) => setTimeout(resolve, ms));
1105
- }
1106
-
1107
1335
  private async resolveAttachment(
1108
1336
  uploadFile?: MulterFile,
1109
1337
  fileId?: number,
@@ -1136,40 +1364,56 @@ export class AiService implements OnModuleInit {
1136
1364
  return null;
1137
1365
  }
1138
1366
 
1367
+ /**
1368
+ * Conteúdo da mensagem do usuário no chat/completions, mais como o anexo foi
1369
+ * parar lá dentro.
1370
+ *
1371
+ * O `delivery` sai daqui porque é aqui que a decisão acontece: um PDF sem
1372
+ * camada de texto vira o marcador de ilegível, e só este método sabe disso.
1373
+ */
1139
1374
  private async buildOpenAiUserContent(
1140
1375
  message: string,
1141
1376
  attachment?: AiAttachment | null,
1142
1377
  supportsImages = true,
1143
- ) {
1378
+ ): Promise<{ content: unknown; delivery: AiAttachmentDelivery }> {
1144
1379
  if (!attachment) {
1145
- return message;
1380
+ return { content: message, delivery: 'none' };
1146
1381
  }
1147
1382
 
1148
1383
  if (supportsImages && this.isImageMime(attachment.mimeType)) {
1149
1384
  const dataUri = `data:${attachment.mimeType};base64,${attachment.buffer.toString('base64')}`;
1150
- return [
1151
- { type: 'text', text: message },
1152
- { type: 'image_url', image_url: { url: dataUri } },
1153
- ];
1385
+ return {
1386
+ content: [
1387
+ { type: 'text', text: message },
1388
+ { type: 'image_url', image_url: { url: dataUri } },
1389
+ ],
1390
+ delivery: 'image',
1391
+ };
1154
1392
  }
1155
1393
 
1156
1394
  let text: string;
1395
+ let delivery: AiAttachmentDelivery;
1157
1396
 
1158
1397
  if (this.isTextMime(attachment.mimeType)) {
1159
1398
  text = this.toUtf8Text(attachment.buffer);
1399
+ delivery = 'text';
1160
1400
  } else if (this.isPdfMime(attachment.mimeType)) {
1161
- text =
1162
- (await this.extractPdfText(attachment.buffer)) ||
1163
- unreadablePdfMarker(attachment.filename);
1401
+ const pdfText = await this.extractPdfText(attachment.buffer);
1402
+ text = pdfText || unreadablePdfMarker(attachment.filename);
1403
+ delivery = pdfText ? 'pdf-text' : 'unreadable-pdf';
1164
1404
  } else {
1165
1405
  text = binaryAttachmentMarker(
1166
1406
  attachment.filename,
1167
1407
  attachment.mimeType,
1168
1408
  attachment.buffer.length,
1169
1409
  );
1410
+ delivery = 'binary';
1170
1411
  }
1171
1412
 
1172
- return `${message}${attachedFileBlock(attachment.filename, text)}`;
1413
+ return {
1414
+ content: `${message}${attachedFileBlock(attachment.filename, text)}`,
1415
+ delivery,
1416
+ };
1173
1417
  }
1174
1418
 
1175
1419
  private isImageMime(mimeType: string) {
@@ -1229,6 +1473,15 @@ export class AiService implements OnModuleInit {
1229
1473
  }
1230
1474
  }
1231
1475
 
1476
+ /** Páginas do PDF, ou 0 quando ele não abre — nesse caso o teto não barra. */
1477
+ private async countPdfPages(buffer: Buffer): Promise<number> {
1478
+ try {
1479
+ return (await pdfParse(buffer))?.numpages ?? 0;
1480
+ } catch {
1481
+ return 0;
1482
+ }
1483
+ }
1484
+
1232
1485
  private toUtf8Text(buffer: Buffer) {
1233
1486
  return buffer.toString('utf8');
1234
1487
  }