@hed-hog/core 0.0.387 → 0.0.390

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 (535) hide show
  1. package/README.md +12 -11
  2. package/dist/access-log/access-log.interceptor.d.ts.map +1 -1
  3. package/dist/access-log/access-log.interceptor.js +4 -2
  4. package/dist/access-log/access-log.interceptor.js.map +1 -1
  5. package/dist/ai/ai-attachment.fragments.d.ts +23 -0
  6. package/dist/ai/ai-attachment.fragments.d.ts.map +1 -0
  7. package/dist/ai/ai-attachment.fragments.js +34 -0
  8. package/dist/ai/ai-attachment.fragments.js.map +1 -0
  9. package/dist/ai/ai.controller.d.ts +7 -16
  10. package/dist/ai/ai.controller.d.ts.map +1 -1
  11. package/dist/ai/ai.prompts.d.ts +16 -0
  12. package/dist/ai/ai.prompts.d.ts.map +1 -0
  13. package/dist/ai/ai.prompts.js +37 -0
  14. package/dist/ai/ai.prompts.js.map +1 -0
  15. package/dist/ai/ai.service.d.ts +63 -12
  16. package/dist/ai/ai.service.d.ts.map +1 -1
  17. package/dist/ai/ai.service.js +215 -88
  18. package/dist/ai/ai.service.js.map +1 -1
  19. package/dist/ai/dto/chat.dto.d.ts +4 -1
  20. package/dist/ai/dto/chat.dto.d.ts.map +1 -1
  21. package/dist/ai/dto/chat.dto.js +23 -1
  22. package/dist/ai/dto/chat.dto.js.map +1 -1
  23. package/dist/ai/dto/create-agent.dto.d.ts +1 -1
  24. package/dist/ai/dto/create-agent.dto.d.ts.map +1 -1
  25. package/dist/ai/dto/create-agent.dto.js +1 -1
  26. package/dist/ai/dto/create-agent.dto.js.map +1 -1
  27. package/dist/ai/dto/update-agent.dto.d.ts +1 -1
  28. package/dist/ai/dto/update-agent.dto.d.ts.map +1 -1
  29. package/dist/ai/dto/update-agent.dto.js +1 -1
  30. package/dist/ai/dto/update-agent.dto.js.map +1 -1
  31. package/dist/ai-instruction/ai-instruction.controller.d.ts +61 -7
  32. package/dist/ai-instruction/ai-instruction.controller.d.ts.map +1 -1
  33. package/dist/ai-instruction/ai-instruction.controller.js +63 -20
  34. package/dist/ai-instruction/ai-instruction.controller.js.map +1 -1
  35. package/dist/ai-instruction/ai-instruction.module.d.ts.map +1 -1
  36. package/dist/ai-instruction/ai-instruction.module.js +13 -2
  37. package/dist/ai-instruction/ai-instruction.module.js.map +1 -1
  38. package/dist/ai-instruction/ai-instruction.service.d.ts +131 -6
  39. package/dist/ai-instruction/ai-instruction.service.d.ts.map +1 -1
  40. package/dist/ai-instruction/ai-instruction.service.js +533 -60
  41. package/dist/ai-instruction/ai-instruction.service.js.map +1 -1
  42. package/dist/ai-instruction/ai-prompt-builder.service.d.ts +8 -0
  43. package/dist/ai-instruction/ai-prompt-builder.service.d.ts.map +1 -1
  44. package/dist/ai-instruction/ai-prompt-builder.service.js +10 -1
  45. package/dist/ai-instruction/ai-prompt-builder.service.js.map +1 -1
  46. package/dist/ai-instruction/ai-prompt-registry.d.ts +45 -0
  47. package/dist/ai-instruction/ai-prompt-registry.d.ts.map +1 -0
  48. package/dist/ai-instruction/ai-prompt-registry.js +49 -0
  49. package/dist/ai-instruction/ai-prompt-registry.js.map +1 -0
  50. package/dist/ai-instruction/ai-prompt-resolver.service.d.ts +89 -0
  51. package/dist/ai-instruction/ai-prompt-resolver.service.d.ts.map +1 -0
  52. package/dist/ai-instruction/ai-prompt-resolver.service.js +222 -0
  53. package/dist/ai-instruction/ai-prompt-resolver.service.js.map +1 -0
  54. package/dist/ai-instruction/ai-prompt-template.util.d.ts +70 -0
  55. package/dist/ai-instruction/ai-prompt-template.util.d.ts.map +1 -0
  56. package/dist/ai-instruction/ai-prompt-template.util.js +154 -0
  57. package/dist/ai-instruction/ai-prompt-template.util.js.map +1 -0
  58. package/dist/ai-instruction/dto/create-ai-instruction.dto.d.ts +5 -0
  59. package/dist/ai-instruction/dto/create-ai-instruction.dto.d.ts.map +1 -1
  60. package/dist/ai-instruction/dto/create-ai-instruction.dto.js +27 -1
  61. package/dist/ai-instruction/dto/create-ai-instruction.dto.js.map +1 -1
  62. package/dist/ai-instruction/dto/preview-ai-instruction.dto.d.ts +14 -0
  63. package/dist/ai-instruction/dto/preview-ai-instruction.dto.d.ts.map +1 -0
  64. package/dist/ai-instruction/dto/preview-ai-instruction.dto.js +42 -0
  65. package/dist/ai-instruction/dto/preview-ai-instruction.dto.js.map +1 -0
  66. package/dist/ai-instruction/dto/update-ai-instruction.dto.d.ts +11 -0
  67. package/dist/ai-instruction/dto/update-ai-instruction.dto.d.ts.map +1 -1
  68. package/dist/ai-instruction/dto/update-ai-instruction.dto.js +34 -0
  69. package/dist/ai-instruction/dto/update-ai-instruction.dto.js.map +1 -1
  70. package/dist/ai-instruction/types/ai-instruction.types.d.ts +13 -1
  71. package/dist/ai-instruction/types/ai-instruction.types.d.ts.map +1 -1
  72. package/dist/ai-instruction/types/ai-instruction.types.js +10 -0
  73. package/dist/ai-instruction/types/ai-instruction.types.js.map +1 -1
  74. package/dist/auth/auth.controller.d.ts +24 -9
  75. package/dist/auth/auth.controller.d.ts.map +1 -1
  76. package/dist/auth/auth.controller.js +49 -21
  77. package/dist/auth/auth.controller.js.map +1 -1
  78. package/dist/auth/auth.service.d.ts +33 -8
  79. package/dist/auth/auth.service.d.ts.map +1 -1
  80. package/dist/auth/auth.service.js +78 -29
  81. package/dist/auth/auth.service.js.map +1 -1
  82. package/dist/captcha/captcha.module.d.ts +3 -0
  83. package/dist/captcha/captcha.module.d.ts.map +1 -0
  84. package/dist/captcha/captcha.module.js +23 -0
  85. package/dist/captcha/captcha.module.js.map +1 -0
  86. package/dist/captcha/captcha.service.d.ts +38 -0
  87. package/dist/captcha/captcha.service.d.ts.map +1 -0
  88. package/dist/captcha/captcha.service.js +144 -0
  89. package/dist/captcha/captcha.service.js.map +1 -0
  90. package/dist/challenge/challenge.service.d.ts +16 -7
  91. package/dist/challenge/challenge.service.d.ts.map +1 -1
  92. package/dist/challenge/challenge.service.js +64 -16
  93. package/dist/challenge/challenge.service.js.map +1 -1
  94. package/dist/core.module.d.ts +3 -1
  95. package/dist/core.module.d.ts.map +1 -1
  96. package/dist/core.module.js +29 -2
  97. package/dist/core.module.js.map +1 -1
  98. package/dist/dashboard/dashboard/dashboard.controller.d.ts +15 -15
  99. package/dist/dashboard/dashboard/dashboard.service.d.ts +15 -15
  100. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +6 -6
  101. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +6 -6
  102. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +20 -20
  103. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +20 -20
  104. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +1 -1
  105. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +1 -1
  106. package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +13 -13
  107. package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +13 -13
  108. package/dist/file/file.service.d.ts +6 -0
  109. package/dist/file/file.service.d.ts.map +1 -1
  110. package/dist/file/file.service.js +44 -4
  111. package/dist/file/file.service.js.map +1 -1
  112. package/dist/incident/incident-notifier.service.d.ts +36 -0
  113. package/dist/incident/incident-notifier.service.d.ts.map +1 -0
  114. package/dist/incident/incident-notifier.service.js +208 -0
  115. package/dist/incident/incident-notifier.service.js.map +1 -0
  116. package/dist/incident/incident.controller.d.ts +23 -0
  117. package/dist/incident/incident.controller.d.ts.map +1 -0
  118. package/dist/incident/incident.controller.js +75 -0
  119. package/dist/incident/incident.controller.js.map +1 -0
  120. package/dist/incident/incident.module.d.ts +7 -0
  121. package/dist/incident/incident.module.d.ts.map +1 -0
  122. package/dist/incident/incident.module.js +32 -0
  123. package/dist/incident/incident.module.js.map +1 -0
  124. package/dist/incident/incident.service.d.ts +72 -0
  125. package/dist/incident/incident.service.d.ts.map +1 -0
  126. package/dist/incident/incident.service.js +184 -0
  127. package/dist/incident/incident.service.js.map +1 -0
  128. package/dist/index.d.ts +26 -0
  129. package/dist/index.d.ts.map +1 -1
  130. package/dist/index.js +30 -0
  131. package/dist/index.js.map +1 -1
  132. package/dist/integration-profile/dto/test.dto.d.ts +13 -0
  133. package/dist/integration-profile/dto/test.dto.d.ts.map +1 -0
  134. package/dist/integration-profile/dto/test.dto.js +26 -0
  135. package/dist/integration-profile/dto/test.dto.js.map +1 -0
  136. package/dist/integration-profile/integration-profile.controller.d.ts +12 -1
  137. package/dist/integration-profile/integration-profile.controller.d.ts.map +1 -1
  138. package/dist/integration-profile/integration-profile.controller.js +9 -1
  139. package/dist/integration-profile/integration-profile.controller.js.map +1 -1
  140. package/dist/integration-profile/integration-profile.module.d.ts +6 -1
  141. package/dist/integration-profile/integration-profile.module.d.ts.map +1 -1
  142. package/dist/integration-profile/integration-profile.module.js +26 -3
  143. package/dist/integration-profile/integration-profile.module.js.map +1 -1
  144. package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
  145. package/dist/integration-profile/integration-profile.secrets.js +9 -1
  146. package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
  147. package/dist/integration-profile/integration-profile.service.d.ts +26 -7
  148. package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
  149. package/dist/integration-profile/integration-profile.service.js +94 -91
  150. package/dist/integration-profile/integration-profile.service.js.map +1 -1
  151. package/dist/integration-profile/integration-profile.utils.d.ts +13 -1
  152. package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
  153. package/dist/integration-profile/integration-profile.utils.js +20 -5
  154. package/dist/integration-profile/integration-profile.utils.js.map +1 -1
  155. package/dist/integration-profile/testers/ai.testers.d.ts +3 -0
  156. package/dist/integration-profile/testers/ai.testers.d.ts.map +1 -0
  157. package/dist/integration-profile/testers/ai.testers.js +125 -0
  158. package/dist/integration-profile/testers/ai.testers.js.map +1 -0
  159. package/dist/integration-profile/testers/captcha.testers.d.ts +3 -0
  160. package/dist/integration-profile/testers/captcha.testers.d.ts.map +1 -0
  161. package/dist/integration-profile/testers/captcha.testers.js +121 -0
  162. package/dist/integration-profile/testers/captcha.testers.js.map +1 -0
  163. package/dist/integration-profile/testers/email.testers.d.ts +3 -0
  164. package/dist/integration-profile/testers/email.testers.d.ts.map +1 -0
  165. package/dist/integration-profile/testers/email.testers.js +128 -0
  166. package/dist/integration-profile/testers/email.testers.js.map +1 -0
  167. package/dist/integration-profile/testers/http-probe.d.ts +57 -0
  168. package/dist/integration-profile/testers/http-probe.d.ts.map +1 -0
  169. package/dist/integration-profile/testers/http-probe.js +140 -0
  170. package/dist/integration-profile/testers/http-probe.js.map +1 -0
  171. package/dist/integration-profile/testers/index.d.ts +12 -0
  172. package/dist/integration-profile/testers/index.d.ts.map +1 -0
  173. package/dist/integration-profile/testers/index.js +45 -0
  174. package/dist/integration-profile/testers/index.js.map +1 -0
  175. package/dist/integration-profile/testers/infrastructure.testers.d.ts +3 -0
  176. package/dist/integration-profile/testers/infrastructure.testers.d.ts.map +1 -0
  177. package/dist/integration-profile/testers/infrastructure.testers.js +105 -0
  178. package/dist/integration-profile/testers/infrastructure.testers.js.map +1 -0
  179. package/dist/integration-profile/testers/integration-tester.registry.d.ts +17 -0
  180. package/dist/integration-profile/testers/integration-tester.registry.d.ts.map +1 -0
  181. package/dist/integration-profile/testers/integration-tester.registry.js +52 -0
  182. package/dist/integration-profile/testers/integration-tester.registry.js.map +1 -0
  183. package/dist/integration-profile/testers/integration-tester.types.d.ts +55 -0
  184. package/dist/integration-profile/testers/integration-tester.types.d.ts.map +1 -0
  185. package/dist/integration-profile/testers/integration-tester.types.js +16 -0
  186. package/dist/integration-profile/testers/integration-tester.types.js.map +1 -0
  187. package/dist/integration-profile/testers/messaging.testers.d.ts +3 -0
  188. package/dist/integration-profile/testers/messaging.testers.d.ts.map +1 -0
  189. package/dist/integration-profile/testers/messaging.testers.js +70 -0
  190. package/dist/integration-profile/testers/messaging.testers.js.map +1 -0
  191. package/dist/integration-profile/testers/oauth.testers.d.ts +3 -0
  192. package/dist/integration-profile/testers/oauth.testers.d.ts.map +1 -0
  193. package/dist/integration-profile/testers/oauth.testers.js +285 -0
  194. package/dist/integration-profile/testers/oauth.testers.js.map +1 -0
  195. package/dist/integration-profile/testers/payment.testers.d.ts +3 -0
  196. package/dist/integration-profile/testers/payment.testers.d.ts.map +1 -0
  197. package/dist/integration-profile/testers/payment.testers.js +169 -0
  198. package/dist/integration-profile/testers/payment.testers.js.map +1 -0
  199. package/dist/integration-profile/testers/storage.testers.d.ts +3 -0
  200. package/dist/integration-profile/testers/storage.testers.d.ts.map +1 -0
  201. package/dist/integration-profile/testers/storage.testers.js +208 -0
  202. package/dist/integration-profile/testers/storage.testers.js.map +1 -0
  203. package/dist/language/en.json +4 -2
  204. package/dist/language/pt.json +4 -2
  205. package/dist/leaked-password/leaked-password.controller.d.ts +24 -0
  206. package/dist/leaked-password/leaked-password.controller.d.ts.map +1 -0
  207. package/dist/leaked-password/leaked-password.controller.js +78 -0
  208. package/dist/leaked-password/leaked-password.controller.js.map +1 -0
  209. package/dist/leaked-password/leaked-password.module.d.ts +8 -0
  210. package/dist/leaked-password/leaked-password.module.d.ts.map +1 -0
  211. package/dist/leaked-password/leaked-password.module.js +31 -0
  212. package/dist/leaked-password/leaked-password.module.js.map +1 -0
  213. package/dist/leaked-password/leaked-password.service.d.ts +64 -0
  214. package/dist/leaked-password/leaked-password.service.d.ts.map +1 -0
  215. package/dist/leaked-password/leaked-password.service.js +218 -0
  216. package/dist/leaked-password/leaked-password.service.js.map +1 -0
  217. package/dist/locale/locale.controller.d.ts +1 -1
  218. package/dist/locale/locale.service.d.ts +1 -1
  219. package/dist/mail/dto/preview.dto.d.ts +18 -0
  220. package/dist/mail/dto/preview.dto.d.ts.map +1 -0
  221. package/dist/mail/dto/preview.dto.js +66 -0
  222. package/dist/mail/dto/preview.dto.js.map +1 -0
  223. package/dist/mail/dto/send.dto.d.ts +7 -0
  224. package/dist/mail/dto/send.dto.d.ts.map +1 -1
  225. package/dist/mail/dto/send.dto.js +10 -0
  226. package/dist/mail/dto/send.dto.js.map +1 -1
  227. package/dist/mail/mail.controller.d.ts +4 -0
  228. package/dist/mail/mail.controller.d.ts.map +1 -1
  229. package/dist/mail/mail.controller.js +16 -2
  230. package/dist/mail/mail.controller.js.map +1 -1
  231. package/dist/mail/mail.service.d.ts +23 -2
  232. package/dist/mail/mail.service.d.ts.map +1 -1
  233. package/dist/mail/mail.service.js +102 -13
  234. package/dist/mail/mail.service.js.map +1 -1
  235. package/dist/mail-layout/dto/update.dto.d.ts +17 -0
  236. package/dist/mail-layout/dto/update.dto.d.ts.map +1 -0
  237. package/dist/mail-layout/dto/update.dto.js +27 -0
  238. package/dist/mail-layout/dto/update.dto.js.map +1 -0
  239. package/dist/mail-layout/mail-layout.controller.d.ts +15 -0
  240. package/dist/mail-layout/mail-layout.controller.d.ts.map +1 -0
  241. package/dist/mail-layout/mail-layout.controller.js +71 -0
  242. package/dist/mail-layout/mail-layout.controller.js.map +1 -0
  243. package/dist/mail-layout/mail-layout.module.d.ts +3 -0
  244. package/dist/mail-layout/mail-layout.module.d.ts.map +1 -0
  245. package/dist/mail-layout/mail-layout.module.js +35 -0
  246. package/dist/mail-layout/mail-layout.module.js.map +1 -0
  247. package/dist/mail-layout/mail-layout.service.d.ts +27 -0
  248. package/dist/mail-layout/mail-layout.service.d.ts.map +1 -0
  249. package/dist/mail-layout/mail-layout.service.js +80 -0
  250. package/dist/mail-layout/mail-layout.service.js.map +1 -0
  251. package/dist/mcp-chat/mcp-chat.controller.d.ts +2 -2
  252. package/dist/mcp-chat/mcp-chat.service.d.ts +24 -2
  253. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
  254. package/dist/mcp-chat/mcp-chat.service.js +80 -24
  255. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  256. package/dist/menu/menu.controller.d.ts +3 -3
  257. package/dist/menu/menu.service.d.ts +3 -3
  258. package/dist/notification/notification.controller.d.ts +12 -12
  259. package/dist/notification/notification.service.d.ts +12 -12
  260. package/dist/oauth/oauth-token-broker.service.d.ts +72 -0
  261. package/dist/oauth/oauth-token-broker.service.d.ts.map +1 -0
  262. package/dist/oauth/oauth-token-broker.service.js +258 -0
  263. package/dist/oauth/oauth-token-broker.service.js.map +1 -0
  264. package/dist/oauth/oauth.module.d.ts.map +1 -1
  265. package/dist/oauth/oauth.module.js +3 -1
  266. package/dist/oauth/oauth.module.js.map +1 -1
  267. package/dist/oauth/oauth.service.d.ts +2 -0
  268. package/dist/oauth/oauth.service.d.ts.map +1 -1
  269. package/dist/oauth/oauth.service.js +6 -3
  270. package/dist/oauth/oauth.service.js.map +1 -1
  271. package/dist/pdf/pdf.service.d.ts +8 -0
  272. package/dist/pdf/pdf.service.d.ts.map +1 -1
  273. package/dist/pdf/pdf.service.js +52 -0
  274. package/dist/pdf/pdf.service.js.map +1 -1
  275. package/dist/presence/presence.controller.d.ts +16 -0
  276. package/dist/presence/presence.controller.d.ts.map +1 -0
  277. package/dist/presence/presence.controller.js +60 -0
  278. package/dist/presence/presence.controller.js.map +1 -0
  279. package/dist/presence/presence.listener.d.ts +29 -0
  280. package/dist/presence/presence.listener.d.ts.map +1 -0
  281. package/dist/presence/presence.listener.js +82 -0
  282. package/dist/presence/presence.listener.js.map +1 -0
  283. package/dist/presence/presence.module.d.ts +3 -0
  284. package/dist/presence/presence.module.d.ts.map +1 -0
  285. package/dist/presence/presence.module.js +32 -0
  286. package/dist/presence/presence.module.js.map +1 -0
  287. package/dist/presence/presence.service.d.ts +42 -0
  288. package/dist/presence/presence.service.d.ts.map +1 -0
  289. package/dist/presence/presence.service.js +147 -0
  290. package/dist/presence/presence.service.js.map +1 -0
  291. package/dist/profile/dto/delete-account.dto.d.ts +8 -0
  292. package/dist/profile/dto/delete-account.dto.d.ts.map +1 -0
  293. package/dist/profile/dto/delete-account.dto.js +22 -0
  294. package/dist/profile/dto/delete-account.dto.js.map +1 -0
  295. package/dist/profile/events/profile-account-deleted.event.d.ts +12 -0
  296. package/dist/profile/events/profile-account-deleted.event.d.ts.map +1 -0
  297. package/dist/profile/events/profile-account-deleted.event.js +17 -0
  298. package/dist/profile/events/profile-account-deleted.event.js.map +1 -0
  299. package/dist/profile/profile.controller.d.ts +21 -15
  300. package/dist/profile/profile.controller.d.ts.map +1 -1
  301. package/dist/profile/profile.controller.js +75 -46
  302. package/dist/profile/profile.controller.js.map +1 -1
  303. package/dist/profile/profile.service.d.ts +30 -18
  304. package/dist/profile/profile.service.d.ts.map +1 -1
  305. package/dist/profile/profile.service.js +113 -37
  306. package/dist/profile/profile.service.js.map +1 -1
  307. package/dist/realtime/realtime.gateway.d.ts +48 -0
  308. package/dist/realtime/realtime.gateway.d.ts.map +1 -0
  309. package/dist/realtime/realtime.gateway.js +156 -0
  310. package/dist/realtime/realtime.gateway.js.map +1 -0
  311. package/dist/realtime/realtime.module.d.ts +8 -0
  312. package/dist/realtime/realtime.module.d.ts.map +1 -0
  313. package/dist/realtime/realtime.module.js +29 -0
  314. package/dist/realtime/realtime.module.js.map +1 -0
  315. package/dist/realtime/realtime.service.d.ts +51 -0
  316. package/dist/realtime/realtime.service.d.ts.map +1 -0
  317. package/dist/realtime/realtime.service.js +177 -0
  318. package/dist/realtime/realtime.service.js.map +1 -0
  319. package/dist/redis/redis-pubsub.d.ts +27 -0
  320. package/dist/redis/redis-pubsub.d.ts.map +1 -0
  321. package/dist/redis/redis-pubsub.js +140 -0
  322. package/dist/redis/redis-pubsub.js.map +1 -0
  323. package/dist/role/role.controller.d.ts +3 -3
  324. package/dist/role/role.service.d.ts +3 -3
  325. package/dist/setting/dto/create.dto.d.ts.map +1 -1
  326. package/dist/setting/dto/create.dto.js +9 -2
  327. package/dist/setting/dto/create.dto.js.map +1 -1
  328. package/dist/setting/setting.controller.d.ts +5 -5
  329. package/dist/setting/setting.service.d.ts +8 -5
  330. package/dist/setting/setting.service.d.ts.map +1 -1
  331. package/dist/setting/setting.service.js +25 -1
  332. package/dist/setting/setting.service.js.map +1 -1
  333. package/dist/user/dto/create-with-email-and-password.dto.d.ts +2 -0
  334. package/dist/user/dto/create-with-email-and-password.dto.d.ts.map +1 -1
  335. package/dist/user/dto/create-with-email-and-password.dto.js +5 -0
  336. package/dist/user/dto/create-with-email-and-password.dto.js.map +1 -1
  337. package/dist/user/dto/query-user-mail-list.dto.d.ts +5 -0
  338. package/dist/user/dto/query-user-mail-list.dto.d.ts.map +1 -0
  339. package/dist/user/dto/query-user-mail-list.dto.js +25 -0
  340. package/dist/user/dto/query-user-mail-list.dto.js.map +1 -0
  341. package/dist/user/user-credential.util.d.ts +19 -0
  342. package/dist/user/user-credential.util.d.ts.map +1 -0
  343. package/dist/user/user-credential.util.js +32 -0
  344. package/dist/user/user-credential.util.js.map +1 -0
  345. package/dist/user/user.controller.d.ts +17 -5
  346. package/dist/user/user.controller.d.ts.map +1 -1
  347. package/dist/user/user.controller.js +18 -3
  348. package/dist/user/user.controller.js.map +1 -1
  349. package/dist/user/user.service.d.ts +28 -6
  350. package/dist/user/user.service.d.ts.map +1 -1
  351. package/dist/user/user.service.js +204 -13
  352. package/dist/user/user.service.js.map +1 -1
  353. package/dist/validators/is-strong-password-with-settings.validator.d.ts.map +1 -1
  354. package/dist/validators/is-strong-password-with-settings.validator.js +11 -0
  355. package/dist/validators/is-strong-password-with-settings.validator.js.map +1 -1
  356. package/dist/validators/service-locator.d.ts +9 -0
  357. package/dist/validators/service-locator.d.ts.map +1 -1
  358. package/dist/validators/service-locator.js +12 -0
  359. package/dist/validators/service-locator.js.map +1 -1
  360. package/dist/whatsapp-sender/whatsapp-sender.module.d.ts +3 -0
  361. package/dist/whatsapp-sender/whatsapp-sender.module.d.ts.map +1 -0
  362. package/dist/whatsapp-sender/whatsapp-sender.module.js +23 -0
  363. package/dist/whatsapp-sender/whatsapp-sender.module.js.map +1 -0
  364. package/dist/whatsapp-sender/whatsapp-sender.service.d.ts +50 -0
  365. package/dist/whatsapp-sender/whatsapp-sender.service.d.ts.map +1 -0
  366. package/dist/whatsapp-sender/whatsapp-sender.service.js +224 -0
  367. package/dist/whatsapp-sender/whatsapp-sender.service.js.map +1 -0
  368. package/hedhog/data/integration_provider.yaml +16 -0
  369. package/hedhog/data/mail.yaml +88 -61
  370. package/hedhog/data/mail_layout.yaml +188 -0
  371. package/hedhog/data/menu.yaml +67 -3
  372. package/hedhog/data/route.yaml +135 -0
  373. package/hedhog/data/setting_group.yaml +139 -1
  374. package/hedhog/frontend/app/account/components/mfa-setup-dialog.tsx.ejs +36 -0
  375. package/hedhog/frontend/app/account/components/two-factor-auth.tsx.ejs +2 -0
  376. package/hedhog/frontend/app/account/hooks/use-mfa-setup.ts.ejs +10 -7
  377. package/hedhog/frontend/app/ai_agent/page.tsx.ejs +7 -4
  378. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-form.tsx.ejs +580 -0
  379. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-preview.tsx.ejs +150 -0
  380. package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-versions.tsx.ejs +199 -0
  381. package/hedhog/frontend/app/ai_instruction/_components/prompt-diff.tsx.ejs +63 -0
  382. package/hedhog/frontend/app/ai_instruction/_lib/line-diff.spec.ts.ejs +59 -0
  383. package/hedhog/frontend/app/ai_instruction/_lib/line-diff.ts.ejs +72 -0
  384. package/hedhog/frontend/app/ai_instruction/_lib/prompt-template.spec.ts.ejs +170 -0
  385. package/hedhog/frontend/app/ai_instruction/_lib/prompt-template.ts.ejs +167 -0
  386. package/hedhog/frontend/app/ai_instruction/_lib/types.ts.ejs +99 -0
  387. package/hedhog/frontend/app/ai_instruction/page.tsx.ejs +422 -0
  388. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +51 -4
  389. package/hedhog/frontend/app/incidents/page.tsx.ejs +400 -0
  390. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +153 -84
  391. package/hedhog/frontend/app/mail/layout/page.tsx.ejs +344 -0
  392. package/hedhog/frontend/app/mail/template/page.tsx.ejs +155 -121
  393. package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +129 -17
  394. package/hedhog/frontend/app/pages/_components/page-delete-dialog.tsx.ejs +343 -0
  395. package/hedhog/frontend/app/pages/_components/page-detail-sheet.tsx.ejs +438 -0
  396. package/hedhog/frontend/app/pages/_components/page-wizard-sheet.tsx.ejs +1009 -0
  397. package/hedhog/frontend/app/pages/_components/system-page-types.ts.ejs +56 -0
  398. package/hedhog/frontend/app/pages/_components/use-system-pages.ts.ejs +171 -0
  399. package/hedhog/frontend/app/pages/page.tsx.ejs +514 -0
  400. package/hedhog/frontend/app/users/page.tsx.ejs +119 -13
  401. package/hedhog/frontend/app/users/user-activity-timeline.tsx.ejs +231 -0
  402. package/hedhog/frontend/app/users/user-mails.tsx.ejs +399 -0
  403. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +60 -75
  404. package/hedhog/frontend/messages/en.json +499 -3
  405. package/hedhog/frontend/messages/pt.json +499 -3
  406. package/hedhog/table/ai_agent.yaml +1 -0
  407. package/hedhog/table/ai_instruction.yaml +15 -0
  408. package/hedhog/table/leaked_password.yaml +18 -0
  409. package/hedhog/table/mail_layout.yaml +35 -0
  410. package/hedhog/table/mail_var.yaml +4 -0
  411. package/hedhog/table/setting.yaml +1 -0
  412. package/hedhog/table/system_incident.yaml +59 -0
  413. package/hedhog/table/user_presence.yaml +36 -0
  414. package/package.json +8 -4
  415. package/src/access-log/access-log.interceptor.ts +4 -2
  416. package/src/ai/ai-attachment.fragments.spec.ts +319 -0
  417. package/src/ai/ai-attachment.fragments.ts +35 -0
  418. package/src/ai/ai.prompts.ts +38 -0
  419. package/src/ai/ai.service.spec.ts +280 -0
  420. package/src/ai/ai.service.ts +338 -82
  421. package/src/ai/dto/chat.dto.ts +33 -3
  422. package/src/ai/dto/create-agent.dto.ts +2 -2
  423. package/src/ai/dto/update-agent.dto.ts +2 -2
  424. package/src/ai-instruction/ai-instruction.controller.ts +40 -9
  425. package/src/ai-instruction/ai-instruction.module.ts +13 -2
  426. package/src/ai-instruction/ai-instruction.service.spec.ts +658 -0
  427. package/src/ai-instruction/ai-instruction.service.ts +661 -62
  428. package/src/ai-instruction/ai-prompt-builder.service.ts +10 -1
  429. package/src/ai-instruction/ai-prompt-registry.ts +87 -0
  430. package/src/ai-instruction/ai-prompt-resolver.service.spec.ts +375 -0
  431. package/src/ai-instruction/ai-prompt-resolver.service.ts +295 -0
  432. package/src/ai-instruction/ai-prompt-template.util.spec.ts +162 -0
  433. package/src/ai-instruction/ai-prompt-template.util.ts +199 -0
  434. package/src/ai-instruction/dto/create-ai-instruction.dto.ts +39 -3
  435. package/src/ai-instruction/dto/preview-ai-instruction.dto.ts +26 -0
  436. package/src/ai-instruction/dto/update-ai-instruction.dto.ts +42 -1
  437. package/src/ai-instruction/types/ai-instruction.types.ts +24 -1
  438. package/src/auth/auth.controller.ts +31 -5
  439. package/src/auth/auth.service.spec.ts +3 -1
  440. package/src/auth/auth.service.ts +101 -36
  441. package/src/captcha/captcha.module.ts +10 -0
  442. package/src/captcha/captcha.service.spec.ts +210 -0
  443. package/src/captcha/captcha.service.ts +191 -0
  444. package/src/challenge/challenge.service.spec.ts +39 -4
  445. package/src/challenge/challenge.service.ts +72 -17
  446. package/src/core.module.ts +27 -1
  447. package/src/file/file.service.spec.ts +105 -0
  448. package/src/file/file.service.ts +60 -3
  449. package/src/incident/incident-notifier.service.spec.ts +205 -0
  450. package/src/incident/incident-notifier.service.ts +241 -0
  451. package/src/incident/incident.controller.ts +38 -0
  452. package/src/incident/incident.module.ts +19 -0
  453. package/src/incident/incident.service.spec.ts +150 -0
  454. package/src/incident/incident.service.ts +209 -0
  455. package/src/index.ts +37 -0
  456. package/src/integration-profile/dto/test.dto.ts +19 -0
  457. package/src/integration-profile/integration-profile.controller.ts +10 -1
  458. package/src/integration-profile/integration-profile.module.spec.ts +51 -0
  459. package/src/integration-profile/integration-profile.module.ts +23 -4
  460. package/src/integration-profile/integration-profile.secrets.ts +9 -1
  461. package/src/integration-profile/integration-profile.service.spec.ts +279 -0
  462. package/src/integration-profile/integration-profile.service.ts +120 -124
  463. package/src/integration-profile/integration-profile.utils.spec.ts +22 -0
  464. package/src/integration-profile/integration-profile.utils.ts +40 -3
  465. package/src/integration-profile/testers/ai.testers.ts +165 -0
  466. package/src/integration-profile/testers/captcha.testers.spec.ts +92 -0
  467. package/src/integration-profile/testers/captcha.testers.ts +139 -0
  468. package/src/integration-profile/testers/email.testers.ts +161 -0
  469. package/src/integration-profile/testers/http-probe.spec.ts +153 -0
  470. package/src/integration-profile/testers/http-probe.ts +171 -0
  471. package/src/integration-profile/testers/index.ts +30 -0
  472. package/src/integration-profile/testers/infrastructure.testers.ts +122 -0
  473. package/src/integration-profile/testers/integration-tester.registry.spec.ts +75 -0
  474. package/src/integration-profile/testers/integration-tester.registry.ts +43 -0
  475. package/src/integration-profile/testers/integration-tester.types.ts +61 -0
  476. package/src/integration-profile/testers/messaging.testers.ts +102 -0
  477. package/src/integration-profile/testers/oauth.testers.spec.ts +220 -0
  478. package/src/integration-profile/testers/oauth.testers.ts +305 -0
  479. package/src/integration-profile/testers/payment.testers.ts +185 -0
  480. package/src/integration-profile/testers/remaining.testers.spec.ts +401 -0
  481. package/src/integration-profile/testers/storage.testers.spec.ts +185 -0
  482. package/src/integration-profile/testers/storage.testers.ts +226 -0
  483. package/src/integration-profile/testers/tester-coverage.spec.ts +58 -0
  484. package/src/language/en.json +4 -2
  485. package/src/language/pt.json +4 -2
  486. package/src/leaked-password/README.md +83 -0
  487. package/src/leaked-password/leaked-password.controller.ts +52 -0
  488. package/src/leaked-password/leaked-password.module.ts +18 -0
  489. package/src/leaked-password/leaked-password.service.ts +242 -0
  490. package/src/leaked-password/seed/build-seed-sql.mjs +180 -0
  491. package/src/leaked-password/seed/common-passwords.txt +335 -0
  492. package/src/mail/dto/preview.dto.ts +50 -0
  493. package/src/mail/dto/send.dto.ts +14 -1
  494. package/src/mail/mail-template.spec.ts +197 -0
  495. package/src/mail/mail.controller.ts +9 -2
  496. package/src/mail/mail.service.spec.ts +116 -1
  497. package/src/mail/mail.service.ts +118 -12
  498. package/src/mail-layout/dto/update.dto.ts +19 -0
  499. package/src/mail-layout/mail-layout.controller.ts +47 -0
  500. package/src/mail-layout/mail-layout.module.ts +22 -0
  501. package/src/mail-layout/mail-layout.service.ts +94 -0
  502. package/src/mcp-chat/mcp-chat.service.spec.ts +236 -0
  503. package/src/mcp-chat/mcp-chat.service.ts +122 -26
  504. package/src/oauth/oauth-token-broker.service.spec.ts +172 -0
  505. package/src/oauth/oauth-token-broker.service.ts +363 -0
  506. package/src/oauth/oauth.module.ts +3 -1
  507. package/src/oauth/oauth.service.ts +8 -2
  508. package/src/pdf/pdf.service.ts +75 -0
  509. package/src/presence/presence.controller.ts +29 -0
  510. package/src/presence/presence.listener.ts +53 -0
  511. package/src/presence/presence.module.ts +19 -0
  512. package/src/presence/presence.service.spec.ts +97 -0
  513. package/src/presence/presence.service.ts +160 -0
  514. package/src/profile/dto/delete-account.dto.ts +11 -0
  515. package/src/profile/events/profile-account-deleted.event.ts +13 -0
  516. package/src/profile/profile.controller.ts +46 -34
  517. package/src/profile/profile.service.ts +138 -38
  518. package/src/realtime/realtime.gateway.ts +138 -0
  519. package/src/realtime/realtime.module.ts +16 -0
  520. package/src/realtime/realtime.service.spec.ts +50 -0
  521. package/src/realtime/realtime.service.ts +187 -0
  522. package/src/redis/redis-pubsub.ts +179 -0
  523. package/src/setting/dto/create.dto.ts +13 -2
  524. package/src/setting/setting.service.spec.ts +56 -0
  525. package/src/setting/setting.service.ts +29 -1
  526. package/src/user/dto/create-with-email-and-password.dto.ts +6 -1
  527. package/src/user/dto/query-user-mail-list.dto.ts +10 -0
  528. package/src/user/user-credential.util.ts +35 -0
  529. package/src/user/user.controller.ts +17 -2
  530. package/src/user/user.service.spec.ts +153 -0
  531. package/src/user/user.service.ts +255 -12
  532. package/src/validators/is-strong-password-with-settings.validator.ts +12 -0
  533. package/src/validators/service-locator.ts +15 -0
  534. package/src/whatsapp-sender/whatsapp-sender.module.ts +10 -0
  535. package/src/whatsapp-sender/whatsapp-sender.service.ts +313 -0
@@ -0,0 +1,295 @@
1
+ import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
2
+ import { AiInstructionService } from './ai-instruction.service';
3
+ import { getAiPromptDefinition } from './ai-prompt-registry';
4
+ import {
5
+ PromptTemplateError,
6
+ renderTemplate,
7
+ type TemplateVars,
8
+ } from './ai-prompt-template.util';
9
+ import type { AiInstructionRecord } from './types/ai-instruction.types';
10
+
11
+ export type PromptSource =
12
+ /** Stored content was used. */
13
+ | 'db'
14
+ /** No usable row (missing, inactive or empty) — code constant was used. */
15
+ | 'fallback'
16
+ /** Stored content was rejected (guard phrases gone or template broken). */
17
+ | 'fallback-guard'
18
+ /** Database read failed — code constant was used. */
19
+ | 'fallback-error';
20
+
21
+ export type ResolvedPrompt = {
22
+ slug: string;
23
+ /** Already interpolated and ready to send. */
24
+ content: string;
25
+ source: PromptSource;
26
+ /** Only populated when `source === 'db'`; otherwise the caller keeps its own default. */
27
+ model: string | null;
28
+ temperature: number | null;
29
+ maxTokens: number | null;
30
+ };
31
+
32
+ export type ResolvePromptInput = {
33
+ slug: string;
34
+ /** Overrides the registry fallback. Rarely needed — the registry is the source of truth. */
35
+ fallback?: string;
36
+ variables?: TemplateVars;
37
+ };
38
+
39
+ export type ModelParams = Pick<ResolvedPrompt, 'model' | 'temperature' | 'maxTokens'>;
40
+
41
+ const WARN_INTERVAL_MS = 5 * 60 * 1000;
42
+
43
+ const EMPTY_PARAMS: ModelParams = { model: null, temperature: null, maxTokens: null };
44
+
45
+ /**
46
+ * Resolves task prompts from `ai_instruction`, falling back to the constant
47
+ * declared in code.
48
+ *
49
+ * `resolve()` never throws: a misconfigured or missing prompt must degrade to
50
+ * the shipped behaviour, not break the AI flow that depends on it.
51
+ */
52
+ @Injectable()
53
+ export class AiPromptResolverService {
54
+ private readonly logger = new Logger(AiPromptResolverService.name);
55
+ /** Last warning timestamp per `slug|reason`, to keep hot paths out of the log. */
56
+ private readonly lastWarnedAt = new Map<string, number>();
57
+
58
+ constructor(
59
+ @Inject(forwardRef(() => AiInstructionService))
60
+ private readonly instructionService: AiInstructionService,
61
+ ) {}
62
+
63
+ async resolve(input: ResolvePromptInput): Promise<ResolvedPrompt> {
64
+ let record: AiInstructionRecord | null = null;
65
+ let readFailed = false;
66
+
67
+ try {
68
+ record = await this.instructionService.getRecordBySlug(input.slug);
69
+ } catch (error) {
70
+ readFailed = true;
71
+ this.warnOnce(input.slug, 'error', `Failed to read AI prompt: ${error}`);
72
+ }
73
+
74
+ return this.build(input, record, readFailed);
75
+ }
76
+
77
+ /**
78
+ * One database round-trip for pipelines that need several prompts. The essay
79
+ * grading flow resolves six per correction.
80
+ */
81
+ async resolveMany(inputs: ResolvePromptInput[]): Promise<Record<string, ResolvedPrompt>> {
82
+ let records = new Map<string, AiInstructionRecord | null>();
83
+ let readFailed = false;
84
+
85
+ try {
86
+ records = await this.instructionService.getRecordsBySlugs(inputs.map((i) => i.slug));
87
+ } catch (error) {
88
+ readFailed = true;
89
+ this.warnOnce(inputs.map((i) => i.slug).join(','), 'error', `Failed to read AI prompts: ${error}`);
90
+ }
91
+
92
+ const result: Record<string, ResolvedPrompt> = {};
93
+ for (const input of inputs) {
94
+ result[input.slug] = this.build(input, records.get(input.slug) || null, readFailed);
95
+ }
96
+ return result;
97
+ }
98
+
99
+ private build(
100
+ input: ResolvePromptInput,
101
+ record: AiInstructionRecord | null,
102
+ readFailed: boolean,
103
+ ): ResolvedPrompt {
104
+ const definition = getAiPromptDefinition(input.slug);
105
+ const fallback = input.fallback ?? definition?.fallback ?? '';
106
+ const variables = input.variables || {};
107
+
108
+ const { template, source } = this.pickTemplate(input.slug, record, readFailed, fallback);
109
+
110
+ try {
111
+ return {
112
+ slug: input.slug,
113
+ content: renderTemplate(template, variables),
114
+ source,
115
+ ...(source === 'db' ? this.modelParamsOf(record) : EMPTY_PARAMS),
116
+ };
117
+ } catch (error) {
118
+ // Only a stored template can be malformed — the code constants are covered
119
+ // by tests. Render the fallback instead of sending half a prompt.
120
+ const reason = error instanceof PromptTemplateError ? error.message : String(error);
121
+ this.logger.error(
122
+ `Stored AI prompt "${input.slug}" is malformed (${reason}); using code fallback.`,
123
+ );
124
+
125
+ return {
126
+ slug: input.slug,
127
+ content: renderTemplate(fallback, variables),
128
+ source: 'fallback-guard',
129
+ ...EMPTY_PARAMS,
130
+ };
131
+ }
132
+ }
133
+
134
+ private pickTemplate(
135
+ slug: string,
136
+ record: AiInstructionRecord | null,
137
+ readFailed: boolean,
138
+ fallback: string,
139
+ ): { template: string; source: PromptSource } {
140
+ if (readFailed) return { template: fallback, source: 'fallback-error' };
141
+
142
+ if (!record) {
143
+ this.warnOnce(slug, 'missing', `AI prompt "${slug}" has no row; using code fallback.`);
144
+ return { template: fallback, source: 'fallback' };
145
+ }
146
+ if (!record.is_active) {
147
+ this.warnOnce(slug, 'inactive', `AI prompt "${slug}" is inactive; using code fallback.`);
148
+ return { template: fallback, source: 'fallback' };
149
+ }
150
+ if (!record.content?.trim()) {
151
+ this.warnOnce(slug, 'empty', `AI prompt "${slug}" is empty; using code fallback.`);
152
+ return { template: fallback, source: 'fallback' };
153
+ }
154
+
155
+ const requiredPhrases = getAiPromptDefinition(slug)?.requiredPhrases;
156
+ if (requiredPhrases?.length) {
157
+ const hasAny = requiredPhrases.some((phrase) => record.content.includes(phrase));
158
+ if (!hasAny) {
159
+ // Not rate-limited: a security-sensitive prompt losing every guard
160
+ // phrase should be loud on every single call.
161
+ this.logger.error(
162
+ `AI prompt "${slug}" lost all of its required safety phrases ` +
163
+ `(${requiredPhrases.join(' | ')}); using code fallback.`,
164
+ );
165
+ return { template: fallback, source: 'fallback-guard' };
166
+ }
167
+ }
168
+
169
+ return { template: record.content, source: 'db' };
170
+ }
171
+
172
+ private modelParamsOf(record: AiInstructionRecord | null): ModelParams {
173
+ if (!record) return EMPTY_PARAMS;
174
+ return {
175
+ model: record.model ?? null,
176
+ temperature: record.temperature ?? null,
177
+ maxTokens: record.max_tokens ?? null,
178
+ };
179
+ }
180
+
181
+ /**
182
+ * Without rate limiting, a single missing slug floods the log — task prompts
183
+ * are resolved on every LLM call.
184
+ */
185
+ private warnOnce(slug: string, reason: string, message: string) {
186
+ const key = `${slug}|${reason}`;
187
+ const now = Date.now();
188
+ const last = this.lastWarnedAt.get(key);
189
+
190
+ if (last && now - last < WARN_INTERVAL_MS) {
191
+ this.logger.debug(message);
192
+ return;
193
+ }
194
+
195
+ this.lastWarnedAt.set(key, now);
196
+ this.logger.warn(message);
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Precedence, implemented here only: explicit argument > `ai_instruction` row >
202
+ * integration profile / code default.
203
+ *
204
+ * When a flow has a system and a user prompt, the system prompt's params win —
205
+ * it is the one that represents the call.
206
+ */
207
+ export function mergeModelParams(...resolved: Array<ResolvedPrompt | null | undefined>): ModelParams {
208
+ const merged: ModelParams = { ...EMPTY_PARAMS };
209
+
210
+ for (const prompt of resolved) {
211
+ if (!prompt) continue;
212
+ if (merged.model === null && prompt.model !== null) merged.model = prompt.model;
213
+ if (merged.temperature === null && prompt.temperature !== null) {
214
+ merged.temperature = prompt.temperature;
215
+ }
216
+ if (merged.maxTokens === null && prompt.maxTokens !== null) merged.maxTokens = prompt.maxTokens;
217
+ }
218
+
219
+ return merged;
220
+ }
221
+
222
+ export type OpenAiParamDefaults = {
223
+ model: string;
224
+ temperature?: number;
225
+ maxTokens?: number;
226
+ jsonObject?: boolean;
227
+ };
228
+
229
+ /**
230
+ * Applies model params to an OpenAI chat completions body.
231
+ *
232
+ * Keys are only written when there is a value, because the current bodies must
233
+ * stay byte-identical when nothing is configured — some newer models reject
234
+ * `temperature` and `max_tokens` outright.
235
+ */
236
+ export function applyOpenAiModelParams(
237
+ body: Record<string, unknown>,
238
+ params: ModelParams,
239
+ defaults: OpenAiParamDefaults,
240
+ ): Record<string, unknown> {
241
+ const model = params.model || defaults.model;
242
+ const temperature = params.temperature ?? defaults.temperature;
243
+ const maxTokens = params.maxTokens ?? defaults.maxTokens;
244
+
245
+ const result: Record<string, unknown> = { model, ...body };
246
+
247
+ if (temperature != null) result.temperature = temperature;
248
+ if (maxTokens != null) {
249
+ // Reasoning models and the gpt-5 family renamed this parameter.
250
+ const key = /^(o\d|gpt-5)/.test(model) ? 'max_completion_tokens' : 'max_tokens';
251
+ result[key] = maxTokens;
252
+ }
253
+ if (defaults.jsonObject) result.response_format = { type: 'json_object' };
254
+
255
+ return result;
256
+ }
257
+
258
+ export function applyGeminiModelParams(
259
+ payload: Record<string, unknown>,
260
+ params: ModelParams,
261
+ defaults: { temperature?: number; maxTokens?: number; jsonObject?: boolean },
262
+ ): Record<string, unknown> {
263
+ const temperature = params.temperature ?? defaults.temperature;
264
+ const maxTokens = params.maxTokens ?? defaults.maxTokens;
265
+
266
+ const generationConfig: Record<string, unknown> = {
267
+ ...((payload.generationConfig as Record<string, unknown>) || {}),
268
+ };
269
+
270
+ if (temperature != null) generationConfig.temperature = temperature;
271
+ if (maxTokens != null) generationConfig.maxOutputTokens = maxTokens;
272
+ if (defaults.jsonObject) generationConfig.responseMimeType = 'application/json';
273
+
274
+ if (!Object.keys(generationConfig).length) return payload;
275
+ return { ...payload, generationConfig };
276
+ }
277
+
278
+ export function applyClaudeModelParams(
279
+ body: Record<string, unknown>,
280
+ params: ModelParams,
281
+ defaults: { model: string; temperature?: number; maxTokens: number },
282
+ ): Record<string, unknown> {
283
+ const temperature = params.temperature ?? defaults.temperature;
284
+
285
+ const result: Record<string, unknown> = {
286
+ model: params.model || defaults.model,
287
+ // Anthropic requires max_tokens, so there is always a value here.
288
+ max_tokens: params.maxTokens ?? defaults.maxTokens,
289
+ ...body,
290
+ };
291
+
292
+ if (temperature != null) result.temperature = temperature;
293
+
294
+ return result;
295
+ }
@@ -0,0 +1,162 @@
1
+ import {
2
+ PromptTemplateError,
3
+ estimateTokens,
4
+ extractPlaceholders,
5
+ renderTemplate,
6
+ validateTemplate,
7
+ } from './ai-prompt-template.util';
8
+
9
+ describe('renderTemplate', () => {
10
+ it('substitui {{var}} pelo valor', () => {
11
+ expect(renderTemplate('Olá {{nome}}!', { nome: 'Ana' })).toBe('Olá Ana!');
12
+ });
13
+
14
+ it('mantém o placeholder literal quando a variável não foi informada', () => {
15
+ // Apagar o texto silenciosamente é pior que deixar o placeholder visível.
16
+ expect(renderTemplate('Olá {{nome}}!', {})).toBe('Olá {{nome}}!');
17
+ });
18
+
19
+ it('trata null e undefined como string vazia quando a chave existe', () => {
20
+ expect(renderTemplate('[{{a}}][{{b}}]', { a: null, b: undefined })).toBe('[][{{b}}]');
21
+ });
22
+
23
+ it('converte números e booleanos', () => {
24
+ expect(renderTemplate('{{n}} / {{b}}', { n: 42, b: true })).toBe('42 / true');
25
+ });
26
+
27
+ it('renderiza bloco {{#var}} quando a variável é truthy', () => {
28
+ expect(renderTemplate('a{{#x}}SIM{{/x}}b', { x: true })).toBe('aSIMb');
29
+ expect(renderTemplate('a{{#x}}SIM{{/x}}b', { x: false })).toBe('ab');
30
+ });
31
+
32
+ it('renderiza bloco {{^var}} quando a variável é falsy', () => {
33
+ expect(renderTemplate('a{{^x}}NAO{{/x}}b', { x: false })).toBe('aNAOb');
34
+ expect(renderTemplate('a{{^x}}NAO{{/x}}b', { x: true })).toBe('ab');
35
+ });
36
+
37
+ it('trata as strings "false" e "0" como falsy', () => {
38
+ // Valores vindos de query string / form chegam como string.
39
+ expect(renderTemplate('{{#x}}SIM{{/x}}', { x: 'false' })).toBe('');
40
+ expect(renderTemplate('{{#x}}SIM{{/x}}', { x: '0' })).toBe('');
41
+ expect(renderTemplate('{{#x}}SIM{{/x}}', { x: ' ' })).toBe('');
42
+ expect(renderTemplate('{{#x}}SIM{{/x}}', { x: 0 })).toBe('');
43
+ });
44
+
45
+ it('substitui variáveis dentro de um bloco mantido', () => {
46
+ expect(renderTemplate('{{#tem}}Tom: {{tom}}{{/tem}}', { tem: true, tom: 'formal' })).toBe(
47
+ 'Tom: formal',
48
+ );
49
+ });
50
+
51
+ it('não substitui variáveis dentro de um bloco descartado', () => {
52
+ expect(renderTemplate('{{#tem}}Tom: {{tom}}{{/tem}}fim', { tem: false, tom: 'formal' })).toBe(
53
+ 'fim',
54
+ );
55
+ });
56
+
57
+ it('resolve blocos irmãos independentes', () => {
58
+ const template = '{{#a}}A{{/a}}{{^a}}nao-A{{/a}}|{{#b}}B{{/b}}';
59
+ expect(renderTemplate(template, { a: true, b: false })).toBe('A|');
60
+ expect(renderTemplate(template, { a: false, b: true })).toBe('nao-A|B');
61
+ });
62
+
63
+ it('lança quando um bloco não é fechado', () => {
64
+ // Um {{#x}} sem {{/x}} engoliria o resto do prompt.
65
+ expect(() => renderTemplate('inicio {{#x}}corpo sem fim', { x: true })).toThrow(
66
+ PromptTemplateError,
67
+ );
68
+ });
69
+
70
+ it('preserva chaves de JSON no corpo do prompt', () => {
71
+ const template = 'Retorne {"isCorrect": boolean, "score": number} para {{aluno}}';
72
+ expect(renderTemplate(template, { aluno: 'Ana' })).toBe(
73
+ 'Retorne {"isCorrect": boolean, "score": number} para Ana',
74
+ );
75
+ });
76
+
77
+ it('preserva chaves triplas e chaves soltas', () => {
78
+ expect(renderTemplate('{{{x}}}', { x: 'v' })).toBe('{v}');
79
+ expect(renderTemplate('{ nao é placeholder }', {})).toBe('{ nao é placeholder }');
80
+ });
81
+
82
+ it('não interpreta placeholders com hífen ou ponto', () => {
83
+ expect(renderTemplate('{{a-b}} {{a.b}}', { 'a-b': 'x' })).toBe('{{a-b}} {{a.b}}');
84
+ });
85
+ });
86
+
87
+ describe('extractPlaceholders', () => {
88
+ it('separa substituições de blocos', () => {
89
+ const result = extractPlaceholders('{{a}} {{#b}}{{c}}{{/b}} {{^d}}{{/d}}');
90
+ expect(result.substitutions.sort()).toEqual(['a', 'c']);
91
+ expect(result.blocks.sort()).toEqual(['b', 'd']);
92
+ expect(result.unbalanced).toEqual([]);
93
+ });
94
+
95
+ it('detecta bloco aberto sem fechamento', () => {
96
+ expect(extractPlaceholders('{{#a}}texto').unbalanced).toEqual(['a']);
97
+ });
98
+
99
+ it('detecta fechamento sem abertura', () => {
100
+ expect(extractPlaceholders('texto{{/a}}').unbalanced).toEqual(['a']);
101
+ });
102
+
103
+ it('não duplica nomes repetidos', () => {
104
+ expect(extractPlaceholders('{{a}} {{a}}').substitutions).toEqual(['a']);
105
+ });
106
+ });
107
+
108
+ describe('validateTemplate', () => {
109
+ const schema = {
110
+ version: 1 as const,
111
+ variables: {
112
+ obrigatoria: { type: 'string' as const, required: true },
113
+ opcional: { type: 'string' as const },
114
+ condicional: { type: 'boolean' as const, required: true, conditional: true },
115
+ },
116
+ };
117
+
118
+ it('aceita um template que usa todas as obrigatórias', () => {
119
+ const result = validateTemplate('{{obrigatoria}} {{#condicional}}x{{/condicional}}', schema);
120
+ expect(result.missingRequired).toEqual([]);
121
+ expect(result.unknownPlaceholders).toEqual([]);
122
+ expect(result.unbalancedBlocks).toEqual([]);
123
+ });
124
+
125
+ it('reporta obrigatória removida', () => {
126
+ const result = validateTemplate('{{#condicional}}x{{/condicional}}', schema);
127
+ expect(result.missingRequired).toEqual(['obrigatoria']);
128
+ });
129
+
130
+ it('aceita a condicional usada apenas como bloco', () => {
131
+ const result = validateTemplate('{{obrigatoria}}{{^condicional}}y{{/condicional}}', schema);
132
+ expect(result.missingRequired).toEqual([]);
133
+ });
134
+
135
+ it('reporta placeholder desconhecido', () => {
136
+ const result = validateTemplate('{{obrigatoria}} {{#condicional}}{{/condicional}} {{typo}}', schema);
137
+ expect(result.unknownPlaceholders).toEqual(['typo']);
138
+ });
139
+
140
+ it('reporta bloco não balanceado', () => {
141
+ const result = validateTemplate('{{obrigatoria}} {{#condicional}}sem fim', schema);
142
+ expect(result.unbalancedBlocks).toEqual(['condicional']);
143
+ });
144
+
145
+ it('não reporta desconhecidos quando não há schema declarado', () => {
146
+ const result = validateTemplate('{{qualquer}}', null);
147
+ expect(result.unknownPlaceholders).toEqual([]);
148
+ expect(result.missingRequired).toEqual([]);
149
+ });
150
+
151
+ it('aceita um mapa de variáveis sem o envelope version/variables', () => {
152
+ const result = validateTemplate('{{a}}', { a: { type: 'string', required: true } });
153
+ expect(result.missingRequired).toEqual([]);
154
+ });
155
+ });
156
+
157
+ describe('estimateTokens', () => {
158
+ it('estima aproximadamente 4 caracteres por token', () => {
159
+ expect(estimateTokens('12345678')).toBe(2);
160
+ expect(estimateTokens('')).toBe(0);
161
+ });
162
+ });
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Minimal Mustache subset for AI prompt templates.
3
+ *
4
+ * Why not plain `{{var}}` replacement: about half of the prompts moved into
5
+ * `ai_instruction` build sections conditionally in TypeScript (`answers.tom ?
6
+ * `Tom: ${answers.tom}` : ''`). Without block syntax those prompts could not be
7
+ * represented faithfully in the database, and migrating them would silently
8
+ * change what the model receives.
9
+ *
10
+ * Supported:
11
+ * {{var}} substitution
12
+ * {{#var}}…{{/var}} render when truthy
13
+ * {{^var}}…{{/var}} render when falsy
14
+ *
15
+ * Not supported (deliberately — these are prompts, not a template language):
16
+ * nested blocks, loops over arrays, partials, filters, dotted paths.
17
+ */
18
+
19
+ export type TemplateVars = Record<string, string | number | boolean | null | undefined>;
20
+
21
+ export type VariableSpec = {
22
+ type: 'string' | 'number' | 'boolean';
23
+ required?: boolean;
24
+ /**
25
+ * Used only inside `{{#var}}`/`{{^var}}`. The validator will not demand a
26
+ * plain `{{var}}` occurrence for these.
27
+ */
28
+ conditional?: boolean;
29
+ description?: string;
30
+ sample?: string;
31
+ };
32
+
33
+ export type VariablesSchema = {
34
+ version: 1;
35
+ variables: Record<string, VariableSpec>;
36
+ };
37
+
38
+ export type TemplatePlaceholders = {
39
+ substitutions: string[];
40
+ blocks: string[];
41
+ /** Block names opened without a matching close tag, or closed without open. */
42
+ unbalanced: string[];
43
+ };
44
+
45
+ export type TemplateValidation = {
46
+ missingRequired: string[];
47
+ unknownPlaceholders: string[];
48
+ unbalancedBlocks: string[];
49
+ };
50
+
51
+ export class PromptTemplateError extends Error {
52
+ constructor(message: string) {
53
+ super(message);
54
+ this.name = 'PromptTemplateError';
55
+ }
56
+ }
57
+
58
+ const SUBSTITUTION_RE = /\{\{(\w+)\}\}/g;
59
+ const BLOCK_OPEN_RE = /\{\{([#^])(\w+)\}\}/g;
60
+ const BLOCK_CLOSE_RE = /\{\{\/(\w+)\}\}/g;
61
+
62
+ /**
63
+ * A value counts as truthy when it would have produced text in the original
64
+ * TypeScript ternary. `'false'` and `'0'` are falsy because these values arrive
65
+ * as strings from query params and form fields.
66
+ */
67
+ function isTruthy(value: TemplateVars[string]): boolean {
68
+ if (value === undefined || value === null) return false;
69
+ if (typeof value === 'boolean') return value;
70
+ if (typeof value === 'number') return value !== 0;
71
+
72
+ const trimmed = value.trim();
73
+ if (!trimmed) return false;
74
+ return trimmed !== 'false' && trimmed !== '0';
75
+ }
76
+
77
+ function stringify(value: TemplateVars[string]): string {
78
+ if (value === undefined || value === null) return '';
79
+ return String(value);
80
+ }
81
+
82
+ /**
83
+ * Renders blocks first, then substitutions, so `{{#x}}text {{y}}{{/x}}` works.
84
+ *
85
+ * Throws `PromptTemplateError` on an unbalanced block: a `{{#x}}` without
86
+ * `{{/x}}` would silently swallow the rest of the prompt, which is far worse
87
+ * than failing. The resolver catches this and falls back to the code constant.
88
+ */
89
+ export function renderTemplate(template: string, vars: TemplateVars = {}): string {
90
+ const rendered = renderBlocks(template, vars);
91
+
92
+ return rendered.replace(SUBSTITUTION_RE, (match, key: string) => {
93
+ const value = vars[key];
94
+ // Keep the placeholder literal when the variable was never provided, so a
95
+ // typo shows up in the output instead of deleting text silently.
96
+ return value === undefined ? match : stringify(value);
97
+ });
98
+ }
99
+
100
+ function renderBlocks(template: string, vars: TemplateVars): string {
101
+ let result = '';
102
+ let cursor = 0;
103
+
104
+ const openRe = new RegExp(BLOCK_OPEN_RE.source, 'g');
105
+ let open: RegExpExecArray | null;
106
+
107
+ while ((open = openRe.exec(template)) !== null) {
108
+ const [openTag, kind, name] = open;
109
+ const bodyStart = open.index + openTag.length;
110
+ const closeTag = `{{/${name}}}`;
111
+ const closeIndex = template.indexOf(closeTag, bodyStart);
112
+
113
+ if (closeIndex === -1) {
114
+ throw new PromptTemplateError(`Unclosed template block "{{${kind}${name}}}".`);
115
+ }
116
+
117
+ result += template.slice(cursor, open.index);
118
+
119
+ const body = template.slice(bodyStart, closeIndex);
120
+ const keep = kind === '#' ? isTruthy(vars[name]) : !isTruthy(vars[name]);
121
+ if (keep) result += body;
122
+
123
+ cursor = closeIndex + closeTag.length;
124
+ openRe.lastIndex = cursor;
125
+ }
126
+
127
+ return result + template.slice(cursor);
128
+ }
129
+
130
+ /**
131
+ * Lists every placeholder in the template. Used by the admin to show which
132
+ * variables a prompt references and to flag the ones it does not know about.
133
+ */
134
+ export function extractPlaceholders(template: string): TemplatePlaceholders {
135
+ const substitutions = new Set<string>();
136
+ const blocks = new Set<string>();
137
+ const closes = new Set<string>();
138
+
139
+ for (const match of template.matchAll(new RegExp(BLOCK_OPEN_RE.source, 'g'))) {
140
+ blocks.add(match[2]);
141
+ }
142
+ for (const match of template.matchAll(new RegExp(BLOCK_CLOSE_RE.source, 'g'))) {
143
+ closes.add(match[1]);
144
+ }
145
+ for (const match of template.matchAll(new RegExp(SUBSTITUTION_RE.source, 'g'))) {
146
+ substitutions.add(match[1]);
147
+ }
148
+
149
+ const unbalanced = [
150
+ ...[...blocks].filter((name) => !closes.has(name)),
151
+ ...[...closes].filter((name) => !blocks.has(name)),
152
+ ];
153
+
154
+ return {
155
+ substitutions: [...substitutions],
156
+ blocks: [...blocks],
157
+ unbalanced: [...new Set(unbalanced)],
158
+ };
159
+ }
160
+
161
+ /**
162
+ * Checks a draft against its declared variables. The admin runs this locally on
163
+ * every keystroke and the API runs it again on save.
164
+ */
165
+ export function validateTemplate(
166
+ content: string,
167
+ schema?: VariablesSchema | Record<string, VariableSpec> | null,
168
+ ): TemplateValidation {
169
+ const { substitutions, blocks, unbalanced } = extractPlaceholders(content);
170
+ const variables = normalizeVariables(schema);
171
+ const declared = new Set(Object.keys(variables));
172
+ const used = new Set([...substitutions, ...blocks]);
173
+
174
+ const missingRequired = Object.entries(variables)
175
+ .filter(([name, spec]) => spec.required && !used.has(name))
176
+ .map(([name]) => name);
177
+
178
+ const unknownPlaceholders = declared.size
179
+ ? [...used].filter((name) => !declared.has(name))
180
+ : [];
181
+
182
+ return { missingRequired, unknownPlaceholders, unbalancedBlocks: unbalanced };
183
+ }
184
+
185
+ /** Accepts both the `{version, variables}` envelope and a bare variables map. */
186
+ export function normalizeVariables(
187
+ schema?: VariablesSchema | Record<string, VariableSpec> | null,
188
+ ): Record<string, VariableSpec> {
189
+ if (!schema) return {};
190
+ if ('variables' in schema && schema.variables && typeof schema.variables === 'object') {
191
+ return (schema as VariablesSchema).variables;
192
+ }
193
+ return schema as Record<string, VariableSpec>;
194
+ }
195
+
196
+ /** Rough enough to warn that a prompt doubled in size. Not a billing figure. */
197
+ export function estimateTokens(text: string): number {
198
+ return Math.ceil(text.length / 4);
199
+ }