@hed-hog/core 0.0.350 → 0.0.351

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 (284) hide show
  1. package/dist/access-log/access-log.controller.d.ts +16 -0
  2. package/dist/access-log/access-log.controller.d.ts.map +1 -0
  3. package/dist/access-log/access-log.controller.js +44 -0
  4. package/dist/access-log/access-log.controller.js.map +1 -0
  5. package/dist/access-log/access-log.interceptor.d.ts +9 -0
  6. package/dist/access-log/access-log.interceptor.d.ts.map +1 -0
  7. package/dist/access-log/access-log.interceptor.js +46 -0
  8. package/dist/access-log/access-log.interceptor.js.map +1 -0
  9. package/dist/access-log/access-log.module.d.ts +3 -0
  10. package/dist/access-log/access-log.module.d.ts.map +1 -0
  11. package/dist/access-log/access-log.module.js +29 -0
  12. package/dist/access-log/access-log.module.js.map +1 -0
  13. package/dist/access-log/access-log.service.d.ts +47 -0
  14. package/dist/access-log/access-log.service.d.ts.map +1 -0
  15. package/dist/access-log/access-log.service.js +113 -0
  16. package/dist/access-log/access-log.service.js.map +1 -0
  17. package/dist/access-log/dto/query-access-log.dto.d.ts +8 -0
  18. package/dist/access-log/dto/query-access-log.dto.d.ts.map +1 -0
  19. package/dist/access-log/dto/query-access-log.dto.js +42 -0
  20. package/dist/access-log/dto/query-access-log.dto.js.map +1 -0
  21. package/dist/auth/auth.controller.d.ts +9 -9
  22. package/dist/auth/auth.service.d.ts +19 -19
  23. package/dist/auth/auth.service.d.ts.map +1 -1
  24. package/dist/auth/auth.service.js +0 -4
  25. package/dist/auth/auth.service.js.map +1 -1
  26. package/dist/challenge/challenge.service.d.ts +2 -2
  27. package/dist/challenge/challenge.service.d.ts.map +1 -1
  28. package/dist/challenge/challenge.service.js +0 -1
  29. package/dist/challenge/challenge.service.js.map +1 -1
  30. package/dist/core/system.module.d.ts.map +1 -1
  31. package/dist/core/system.module.js +1 -0
  32. package/dist/core/system.module.js.map +1 -1
  33. package/dist/core.module.d.ts.map +1 -1
  34. package/dist/core.module.js +12 -0
  35. package/dist/core.module.js.map +1 -1
  36. package/dist/dashboard/dashboard-user/dashboard-user.controller.d.ts +3 -3
  37. package/dist/dashboard/dashboard-user/dashboard-user.service.d.ts +3 -3
  38. package/dist/dashboard/dashboard.module.d.ts.map +1 -1
  39. package/dist/dashboard/dashboard.module.js +4 -1
  40. package/dist/dashboard/dashboard.module.js.map +1 -1
  41. package/dist/file/file.controller.js +0 -1
  42. package/dist/file/file.controller.js.map +1 -1
  43. package/dist/file/file.service.d.ts +65 -0
  44. package/dist/file/file.service.d.ts.map +1 -1
  45. package/dist/file/file.service.js +151 -3
  46. package/dist/file/file.service.js.map +1 -1
  47. package/dist/file/provider/abstract.provider.d.ts +8 -0
  48. package/dist/file/provider/abstract.provider.d.ts.map +1 -1
  49. package/dist/file/provider/abstract.provider.js.map +1 -1
  50. package/dist/file/provider/azure.provider.d.ts +4 -0
  51. package/dist/file/provider/azure.provider.d.ts.map +1 -1
  52. package/dist/file/provider/azure.provider.js +14 -0
  53. package/dist/file/provider/azure.provider.js.map +1 -1
  54. package/dist/file/provider/gcs.provider.d.ts +4 -0
  55. package/dist/file/provider/gcs.provider.d.ts.map +1 -1
  56. package/dist/file/provider/gcs.provider.js +14 -0
  57. package/dist/file/provider/gcs.provider.js.map +1 -1
  58. package/dist/file/provider/local.provider.d.ts +4 -0
  59. package/dist/file/provider/local.provider.d.ts.map +1 -1
  60. package/dist/file/provider/local.provider.js +19 -0
  61. package/dist/file/provider/local.provider.js.map +1 -1
  62. package/dist/file/provider/provider.factory.d.ts.map +1 -1
  63. package/dist/file/provider/provider.factory.js +3 -0
  64. package/dist/file/provider/provider.factory.js.map +1 -1
  65. package/dist/file/provider/s3.provider.d.ts +4 -0
  66. package/dist/file/provider/s3.provider.d.ts.map +1 -1
  67. package/dist/file/provider/s3.provider.js +17 -0
  68. package/dist/file/provider/s3.provider.js.map +1 -1
  69. package/dist/mcp/mcp-audit.service.d.ts +3 -1
  70. package/dist/mcp/mcp-audit.service.d.ts.map +1 -1
  71. package/dist/mcp/mcp-audit.service.js +14 -2
  72. package/dist/mcp/mcp-audit.service.js.map +1 -1
  73. package/dist/mcp/mcp.controller.d.ts +3 -1
  74. package/dist/mcp/mcp.controller.d.ts.map +1 -1
  75. package/dist/mcp/mcp.controller.js +10 -2
  76. package/dist/mcp/mcp.controller.js.map +1 -1
  77. package/dist/mcp/mcp.module.d.ts.map +1 -1
  78. package/dist/mcp/mcp.module.js +72 -0
  79. package/dist/mcp/mcp.module.js.map +1 -1
  80. package/dist/mcp/tools/core-ai.mcp-tools.d.ts +45 -0
  81. package/dist/mcp/tools/core-ai.mcp-tools.d.ts.map +1 -0
  82. package/dist/mcp/tools/core-ai.mcp-tools.js +214 -0
  83. package/dist/mcp/tools/core-ai.mcp-tools.js.map +1 -0
  84. package/dist/mcp/tools/core-dashboard-items.mcp-tools.d.ts +21 -0
  85. package/dist/mcp/tools/core-dashboard-items.mcp-tools.d.ts.map +1 -0
  86. package/dist/mcp/tools/core-dashboard-items.mcp-tools.js +89 -0
  87. package/dist/mcp/tools/core-dashboard-items.mcp-tools.js.map +1 -0
  88. package/dist/mcp/tools/core-dashboard.mcp-tools.d.ts +29 -0
  89. package/dist/mcp/tools/core-dashboard.mcp-tools.d.ts.map +1 -0
  90. package/dist/mcp/tools/core-dashboard.mcp-tools.js +149 -0
  91. package/dist/mcp/tools/core-dashboard.mcp-tools.js.map +1 -0
  92. package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts +30 -0
  93. package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts.map +1 -0
  94. package/dist/mcp/tools/core-event-webhooks.mcp-tools.js +185 -0
  95. package/dist/mcp/tools/core-event-webhooks.mcp-tools.js.map +1 -0
  96. package/dist/mcp/tools/core-files.mcp-tools.d.ts +26 -0
  97. package/dist/mcp/tools/core-files.mcp-tools.d.ts.map +1 -0
  98. package/dist/mcp/tools/core-files.mcp-tools.js +140 -0
  99. package/dist/mcp/tools/core-files.mcp-tools.js.map +1 -0
  100. package/dist/mcp/tools/core-integration-events.mcp-tools.d.ts +26 -0
  101. package/dist/mcp/tools/core-integration-events.mcp-tools.d.ts.map +1 -0
  102. package/dist/mcp/tools/core-integration-events.mcp-tools.js +168 -0
  103. package/dist/mcp/tools/core-integration-events.mcp-tools.js.map +1 -0
  104. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts +32 -0
  105. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts.map +1 -0
  106. package/dist/mcp/tools/core-mail-profile.mcp-tools.js +178 -0
  107. package/dist/mcp/tools/core-mail-profile.mcp-tools.js.map +1 -0
  108. package/dist/mcp/tools/core-mail-sent.mcp-tools.d.ts +14 -0
  109. package/dist/mcp/tools/core-mail-sent.mcp-tools.d.ts.map +1 -0
  110. package/dist/mcp/tools/core-mail-sent.mcp-tools.js +106 -0
  111. package/dist/mcp/tools/core-mail-sent.mcp-tools.js.map +1 -0
  112. package/dist/mcp/tools/core-mail.mcp-tools.d.ts +36 -0
  113. package/dist/mcp/tools/core-mail.mcp-tools.d.ts.map +1 -0
  114. package/dist/mcp/tools/core-mail.mcp-tools.js +184 -0
  115. package/dist/mcp/tools/core-mail.mcp-tools.js.map +1 -0
  116. package/dist/mcp/tools/core-mcp-chat.mcp-tools.d.ts +21 -0
  117. package/dist/mcp/tools/core-mcp-chat.mcp-tools.d.ts.map +1 -0
  118. package/dist/mcp/tools/core-mcp-chat.mcp-tools.js +118 -0
  119. package/dist/mcp/tools/core-mcp-chat.mcp-tools.js.map +1 -0
  120. package/dist/mcp/tools/core-menus.mcp-tools.d.ts +40 -0
  121. package/dist/mcp/tools/core-menus.mcp-tools.d.ts.map +1 -0
  122. package/dist/mcp/tools/core-menus.mcp-tools.js +268 -0
  123. package/dist/mcp/tools/core-menus.mcp-tools.js.map +1 -0
  124. package/dist/mcp/tools/core-notifications.mcp-tools.d.ts +31 -0
  125. package/dist/mcp/tools/core-notifications.mcp-tools.d.ts.map +1 -0
  126. package/dist/mcp/tools/core-notifications.mcp-tools.js +178 -0
  127. package/dist/mcp/tools/core-notifications.mcp-tools.js.map +1 -0
  128. package/dist/mcp/tools/core-profile.mcp-tools.d.ts +20 -0
  129. package/dist/mcp/tools/core-profile.mcp-tools.d.ts.map +1 -0
  130. package/dist/mcp/tools/core-profile.mcp-tools.js +111 -0
  131. package/dist/mcp/tools/core-profile.mcp-tools.js.map +1 -0
  132. package/dist/mcp/tools/core-roles.mcp-tools.d.ts +70 -0
  133. package/dist/mcp/tools/core-roles.mcp-tools.d.ts.map +1 -0
  134. package/dist/mcp/tools/core-roles.mcp-tools.js +355 -0
  135. package/dist/mcp/tools/core-roles.mcp-tools.js.map +1 -0
  136. package/dist/mcp/tools/core-routes.mcp-tools.d.ts +52 -0
  137. package/dist/mcp/tools/core-routes.mcp-tools.d.ts.map +1 -0
  138. package/dist/mcp/tools/core-routes.mcp-tools.js +254 -0
  139. package/dist/mcp/tools/core-routes.mcp-tools.js.map +1 -0
  140. package/dist/mcp/tools/core-screens.mcp-tools.d.ts +47 -0
  141. package/dist/mcp/tools/core-screens.mcp-tools.d.ts.map +1 -0
  142. package/dist/mcp/tools/core-screens.mcp-tools.js +250 -0
  143. package/dist/mcp/tools/core-screens.mcp-tools.js.map +1 -0
  144. package/dist/mcp/tools/core-sessions.mcp-tools.d.ts +20 -0
  145. package/dist/mcp/tools/core-sessions.mcp-tools.d.ts.map +1 -0
  146. package/dist/mcp/tools/core-sessions.mcp-tools.js +128 -0
  147. package/dist/mcp/tools/core-sessions.mcp-tools.js.map +1 -0
  148. package/dist/mcp/tools/core-settings.mcp-tools.d.ts +25 -0
  149. package/dist/mcp/tools/core-settings.mcp-tools.d.ts.map +1 -1
  150. package/dist/mcp/tools/core-settings.mcp-tools.js +138 -0
  151. package/dist/mcp/tools/core-settings.mcp-tools.js.map +1 -1
  152. package/dist/mcp/tools/core-system.mcp-tools.d.ts +8 -0
  153. package/dist/mcp/tools/core-system.mcp-tools.d.ts.map +1 -0
  154. package/dist/mcp/tools/core-system.mcp-tools.js +40 -0
  155. package/dist/mcp/tools/core-system.mcp-tools.js.map +1 -0
  156. package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts +46 -0
  157. package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts.map +1 -0
  158. package/dist/mcp/tools/core-webhooks.mcp-tools.js +266 -0
  159. package/dist/mcp/tools/core-webhooks.mcp-tools.js.map +1 -0
  160. package/dist/mcp-chat/mcp-chat.controller.d.ts +1 -1
  161. package/dist/mcp-chat/mcp-chat.service.d.ts +25 -2
  162. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
  163. package/dist/mcp-chat/mcp-chat.service.js +666 -36
  164. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  165. package/dist/notification/notification.controller.d.ts +8 -4
  166. package/dist/notification/notification.controller.d.ts.map +1 -1
  167. package/dist/notification/notification.controller.js +12 -0
  168. package/dist/notification/notification.controller.js.map +1 -1
  169. package/dist/notification/notification.service.d.ts +19 -5
  170. package/dist/notification/notification.service.d.ts.map +1 -1
  171. package/dist/notification/notification.service.js +155 -9
  172. package/dist/notification/notification.service.js.map +1 -1
  173. package/dist/pdf/pdf.service.js +1 -1
  174. package/dist/pdf/pdf.service.js.map +1 -1
  175. package/dist/profile/profile.controller.d.ts +3 -3
  176. package/dist/profile/profile.controller.js +0 -1
  177. package/dist/profile/profile.controller.js.map +1 -1
  178. package/dist/profile/profile.module.d.ts.map +1 -1
  179. package/dist/profile/profile.module.js +1 -0
  180. package/dist/profile/profile.module.js.map +1 -1
  181. package/dist/profile/profile.service.d.ts +3 -3
  182. package/dist/session/session.controller.d.ts +2 -2
  183. package/dist/session/session.service.d.ts +6 -6
  184. package/dist/setting/setting.controller.d.ts +1 -1
  185. package/dist/setting/setting.service.d.ts +1 -1
  186. package/dist/setting/setting.service.d.ts.map +1 -1
  187. package/dist/setting/setting.service.js +1 -4
  188. package/dist/setting/setting.service.js.map +1 -1
  189. package/dist/task/task.service.d.ts +1 -0
  190. package/dist/task/task.service.d.ts.map +1 -1
  191. package/dist/task/task.service.js +25 -0
  192. package/dist/task/task.service.js.map +1 -1
  193. package/dist/token/token.service.d.ts.map +1 -1
  194. package/dist/token/token.service.js +0 -1
  195. package/dist/token/token.service.js.map +1 -1
  196. package/dist/user/user.controller.d.ts +9 -9
  197. package/dist/user/user.service.d.ts +21 -21
  198. package/dist/webhook-integration/dto/webhook-action.dto.d.ts +22 -0
  199. package/dist/webhook-integration/dto/webhook-action.dto.d.ts.map +1 -0
  200. package/dist/webhook-integration/dto/webhook-action.dto.js +120 -0
  201. package/dist/webhook-integration/dto/webhook-action.dto.js.map +1 -0
  202. package/dist/webhook-integration/webhook-integration.controller.d.ts +7 -5
  203. package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
  204. package/dist/webhook-integration/webhook-integration.controller.js +18 -5
  205. package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
  206. package/dist/webhook-integration/webhook-integration.service.d.ts +14 -5
  207. package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
  208. package/dist/webhook-integration/webhook-integration.service.js +174 -41
  209. package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
  210. package/hedhog/data/menu.yaml +15 -0
  211. package/hedhog/data/route.yaml +1304 -0
  212. package/hedhog/data/setting_group.yaml +67 -0
  213. package/hedhog/data/setting_subgroup.yaml +16 -0
  214. package/hedhog/frontend/app/access-log/page.tsx.ejs +772 -0
  215. package/hedhog/frontend/app/ai_agent/page.tsx.ejs +10 -3
  216. package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +33 -13
  217. package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +12 -5
  218. package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +12 -5
  219. package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +1817 -356
  220. package/hedhog/frontend/app/mcp_chat/chat-components.tsx.ejs +43 -5
  221. package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +35 -7
  222. package/hedhog/frontend/app/menu/page.tsx.ejs +9 -3
  223. package/hedhog/frontend/app/roles/page.tsx.ejs +18 -6
  224. package/hedhog/frontend/app/routes/page.tsx.ejs +9 -3
  225. package/hedhog/frontend/app/setting-list/page.tsx.ejs +344 -171
  226. package/hedhog/frontend/app/users/user-access-sheet.tsx.ejs +310 -304
  227. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +25 -7
  228. package/hedhog/frontend/messages/en.json +104 -3
  229. package/hedhog/frontend/messages/pt.json +104 -3
  230. package/hedhog/table/access_log.yaml +31 -0
  231. package/package.json +5 -4
  232. package/src/access-log/access-log.controller.ts +29 -0
  233. package/src/access-log/access-log.interceptor.ts +38 -0
  234. package/src/access-log/access-log.module.ts +16 -0
  235. package/src/access-log/access-log.service.ts +129 -0
  236. package/src/access-log/dto/query-access-log.dto.ts +23 -0
  237. package/src/auth/auth.service.ts +0 -7
  238. package/src/challenge/challenge.service.ts +0 -2
  239. package/src/core/system.module.ts +1 -0
  240. package/src/core.module.ts +12 -0
  241. package/src/dashboard/dashboard.module.ts +4 -1
  242. package/src/file/file.controller.ts +14 -14
  243. package/src/file/file.service.ts +198 -4
  244. package/src/file/provider/abstract.provider.ts +10 -0
  245. package/src/file/provider/azure.provider.ts +24 -0
  246. package/src/file/provider/gcs.provider.ts +20 -0
  247. package/src/file/provider/local.provider.ts +30 -2
  248. package/src/file/provider/provider.factory.ts +3 -0
  249. package/src/file/provider/s3.provider.ts +22 -0
  250. package/src/mcp/mcp-audit.service.ts +47 -36
  251. package/src/mcp/mcp.controller.ts +40 -30
  252. package/src/mcp/mcp.module.ts +72 -0
  253. package/src/mcp/tools/core-ai.mcp-tools.ts +156 -0
  254. package/src/mcp/tools/core-dashboard-items.mcp-tools.ts +63 -0
  255. package/src/mcp/tools/core-dashboard.mcp-tools.ts +103 -0
  256. package/src/mcp/tools/core-event-webhooks.mcp-tools.ts +134 -0
  257. package/src/mcp/tools/core-files.mcp-tools.ts +105 -0
  258. package/src/mcp/tools/core-integration-events.mcp-tools.ts +118 -0
  259. package/src/mcp/tools/core-mail-profile.mcp-tools.ts +128 -0
  260. package/src/mcp/tools/core-mail-sent.mcp-tools.ts +76 -0
  261. package/src/mcp/tools/core-mail.mcp-tools.ts +130 -0
  262. package/src/mcp/tools/core-mcp-chat.mcp-tools.ts +81 -0
  263. package/src/mcp/tools/core-menus.mcp-tools.ts +197 -0
  264. package/src/mcp/tools/core-notifications.mcp-tools.ts +128 -0
  265. package/src/mcp/tools/core-profile.mcp-tools.ts +74 -0
  266. package/src/mcp/tools/core-roles.mcp-tools.ts +288 -0
  267. package/src/mcp/tools/core-routes.mcp-tools.ts +196 -0
  268. package/src/mcp/tools/core-screens.mcp-tools.ts +186 -0
  269. package/src/mcp/tools/core-sessions.mcp-tools.ts +97 -0
  270. package/src/mcp/tools/core-settings.mcp-tools.ts +171 -53
  271. package/src/mcp/tools/core-system.mcp-tools.ts +19 -0
  272. package/src/mcp/tools/core-webhooks.mcp-tools.ts +199 -0
  273. package/src/mcp-chat/mcp-chat.service.ts +887 -38
  274. package/src/notification/notification.controller.ts +20 -8
  275. package/src/notification/notification.service.ts +209 -9
  276. package/src/pdf/pdf.service.ts +1 -1
  277. package/src/profile/profile.controller.ts +1 -1
  278. package/src/profile/profile.module.ts +1 -0
  279. package/src/setting/setting.service.ts +8 -11
  280. package/src/task/task.service.ts +31 -0
  281. package/src/token/token.service.ts +0 -2
  282. package/src/webhook-integration/dto/webhook-action.dto.ts +96 -0
  283. package/src/webhook-integration/webhook-integration.controller.ts +124 -103
  284. package/src/webhook-integration/webhook-integration.service.ts +267 -64
@@ -23,11 +23,152 @@ const axios_1 = __importDefault(require("axios"));
23
23
  const mcp_registry_service_1 = require("../mcp/mcp-registry.service");
24
24
  const setting_service_1 = require("../setting/setting.service");
25
25
  const MAX_OPENAI_TOOLS = 128;
26
+ const TARGET_SELECTED_TOOLS = 112;
27
+ const MAX_ITERATIONS = 20;
28
+ const FORCE_FINISH_THRESHOLD = 2;
29
+ const MIN_DOMAIN_CONFIDENCE = 2;
30
+ const AMBIGUOUS_DOMAIN_DELTA = 1;
26
31
  const SYSTEM_PROMPT = 'You are a helpful assistant with access to tools from the HedHog system. ' +
27
32
  'Use the available tools to answer the user\'s questions accurately. ' +
28
33
  'Always respond in the same language the user writes in.';
29
34
  const sanitize = (n) => n.replace(/\./g, '__');
30
35
  const restore = (n) => n.replace(/__/g, '.');
36
+ const DOMAIN_KEYWORDS = {
37
+ finance: [
38
+ 'finance',
39
+ 'financeiro',
40
+ 'financeira',
41
+ 'financas',
42
+ 'finanças',
43
+ 'banco',
44
+ 'bancos',
45
+ 'bank',
46
+ 'banks',
47
+ 'conta',
48
+ 'contas',
49
+ 'account',
50
+ 'accounts',
51
+ 'conta bancaria',
52
+ 'contas bancarias',
53
+ 'conta bancária',
54
+ 'contas bancárias',
55
+ 'extrato',
56
+ 'extratos',
57
+ 'statement',
58
+ 'statements',
59
+ 'pagamento',
60
+ 'pagamentos',
61
+ 'payment',
62
+ 'payments',
63
+ 'recebimento',
64
+ 'recebimentos',
65
+ 'receivable',
66
+ 'payable',
67
+ 'currency',
68
+ 'moeda',
69
+ 'transferencia',
70
+ 'transferência',
71
+ ],
72
+ operations: [
73
+ 'operations',
74
+ 'operacoes',
75
+ 'operações',
76
+ 'tarefa',
77
+ 'tarefas',
78
+ 'task',
79
+ 'tasks',
80
+ 'projeto',
81
+ 'projetos',
82
+ 'project',
83
+ 'projects',
84
+ 'timesheet',
85
+ 'apontamento',
86
+ 'aprovacao',
87
+ 'aprovação',
88
+ 'approval',
89
+ 'colaborador',
90
+ 'collaborator',
91
+ ],
92
+ lms: [
93
+ 'lms',
94
+ 'curso',
95
+ 'cursos',
96
+ 'course',
97
+ 'courses',
98
+ 'aula',
99
+ 'aulas',
100
+ 'lesson',
101
+ 'lessons',
102
+ 'treinamento',
103
+ 'training',
104
+ 'turma',
105
+ 'class',
106
+ 'aluno',
107
+ 'student',
108
+ 'prova',
109
+ 'exam',
110
+ ],
111
+ core: [
112
+ 'core',
113
+ 'usuario',
114
+ 'usuário',
115
+ 'usuarios',
116
+ 'usuários',
117
+ 'user',
118
+ 'users',
119
+ 'perfil',
120
+ 'profile',
121
+ 'role',
122
+ 'roles',
123
+ 'menu',
124
+ 'menus',
125
+ 'rota',
126
+ 'rotas',
127
+ 'route',
128
+ 'routes',
129
+ 'configuracao',
130
+ 'configuração',
131
+ 'setting',
132
+ 'settings',
133
+ ],
134
+ contact: [
135
+ 'contato',
136
+ 'contatos',
137
+ 'contact',
138
+ 'contacts',
139
+ 'pessoa',
140
+ 'pessoas',
141
+ 'person',
142
+ 'proposal',
143
+ 'proposta',
144
+ ],
145
+ };
146
+ const ACTION_KEYWORDS = [
147
+ 'listar',
148
+ 'lista',
149
+ 'buscar',
150
+ 'procurar',
151
+ 'consultar',
152
+ 'mostrar',
153
+ 'ver',
154
+ 'criar',
155
+ 'adicionar',
156
+ 'atualizar',
157
+ 'alterar',
158
+ 'remover',
159
+ 'deletar',
160
+ 'excluir',
161
+ 'list',
162
+ 'search',
163
+ 'find',
164
+ 'show',
165
+ 'get',
166
+ 'create',
167
+ 'add',
168
+ 'update',
169
+ 'delete',
170
+ 'remove',
171
+ ];
31
172
  let McpChatService = McpChatService_1 = class McpChatService {
32
173
  constructor(prisma, setting, registry) {
33
174
  this.prisma = prisma;
@@ -65,9 +206,12 @@ let McpChatService = McpChatService_1 = class McpChatService {
65
206
  });
66
207
  }
67
208
  async sendMessage(conversationId, userId, locale, message) {
68
- var _a, _b, _c, _d, _e, _f, _g, _h;
209
+ const cfg = await this.setting.getSettingValues(['mcp-enabled']);
210
+ if (cfg['mcp-enabled'] === false) {
211
+ throw new common_1.BadRequestException('MCP server is disabled.');
212
+ }
69
213
  await this.assertOwnership(conversationId, userId);
70
- const apiKey = await this.getOpenAiKey();
214
+ const { provider, apiKey } = await this.getProviderKey();
71
215
  // Save user message
72
216
  await this.prisma.mcp_conversation_message.create({
73
217
  data: {
@@ -86,7 +230,14 @@ let McpChatService = McpChatService_1 = class McpChatService {
86
230
  data: { title: message.slice(0, 60) },
87
231
  });
88
232
  }
89
- // Build OpenAI messages from history
233
+ if (provider === 'gemini') {
234
+ return this.runGeminiLoop(conversationId, userId, locale, message, apiKey);
235
+ }
236
+ return this.runOpenAiLoop(conversationId, userId, locale, message, apiKey);
237
+ }
238
+ // ── OpenAI loop ──────────────────────────────────────────────────────────
239
+ async runOpenAiLoop(conversationId, userId, locale, message, apiKey) {
240
+ var _a, _b, _c, _d, _e, _f, _g, _h;
90
241
  const history = await this.prisma.mcp_conversation_message.findMany({
91
242
  where: { conversation_id: conversationId },
92
243
  orderBy: { created_at: 'asc' },
@@ -95,18 +246,22 @@ let McpChatService = McpChatService_1 = class McpChatService {
95
246
  { role: 'system', content: SYSTEM_PROMPT },
96
247
  ...this.toOpenAiMessages(history),
97
248
  ];
98
- // Build tool definitions
99
- const tools = this.buildOpenAiTools(message, conversationId, userId);
100
- // Tool-calling loop
101
- let maxIterations = 10;
249
+ const toolSelection = await this.buildOpenAiTools(message, conversationId, userId, history);
250
+ if (toolSelection.clarification) {
251
+ return this.recordAssistantAndReturnHistory(conversationId, toolSelection.clarification);
252
+ }
253
+ const tools = toolSelection.tools;
254
+ const callSignatures = new Set();
255
+ let maxIterations = MAX_ITERATIONS;
102
256
  while (maxIterations-- > 0) {
257
+ const forceFinish = maxIterations < FORCE_FINISH_THRESHOLD;
103
258
  let response;
104
259
  try {
105
260
  response = await axios_1.default.post('https://api.openai.com/v1/chat/completions', {
106
261
  model: 'gpt-4o-mini',
107
262
  messages: openAiMessages,
108
- tools: tools.length > 0 ? tools : undefined,
109
- tool_choice: tools.length > 0 ? 'auto' : undefined,
263
+ tools: !forceFinish && tools.length > 0 ? tools : undefined,
264
+ tool_choice: !forceFinish && tools.length > 0 ? 'auto' : undefined,
110
265
  }, {
111
266
  headers: {
112
267
  Authorization: `Bearer ${apiKey}`,
@@ -127,14 +282,12 @@ let McpChatService = McpChatService_1 = class McpChatService {
127
282
  }
128
283
  const choice = (_g = (_f = response.data) === null || _f === void 0 ? void 0 : _f.choices) === null || _g === void 0 ? void 0 : _g[0];
129
284
  const assistantMsg = choice === null || choice === void 0 ? void 0 : choice.message;
130
- if ((_h = assistantMsg === null || assistantMsg === void 0 ? void 0 : assistantMsg.tool_calls) === null || _h === void 0 ? void 0 : _h.length) {
131
- // Add assistant tool-call message to context
285
+ if (((_h = assistantMsg === null || assistantMsg === void 0 ? void 0 : assistantMsg.tool_calls) === null || _h === void 0 ? void 0 : _h.length) && !forceFinish) {
132
286
  openAiMessages.push({
133
287
  role: 'assistant',
134
288
  content: null,
135
289
  tool_calls: assistantMsg.tool_calls,
136
290
  });
137
- // Execute each tool call
138
291
  for (const toolCall of assistantMsg.tool_calls) {
139
292
  const originalName = restore(toolCall.function.name);
140
293
  const argsJson = toolCall.function.arguments;
@@ -145,7 +298,6 @@ let McpChatService = McpChatService_1 = class McpChatService {
145
298
  catch (_j) {
146
299
  args = {};
147
300
  }
148
- // Persist tool_call message
149
301
  await this.prisma.mcp_conversation_message.create({
150
302
  data: {
151
303
  conversation_id: conversationId,
@@ -155,7 +307,28 @@ let McpChatService = McpChatService_1 = class McpChatService {
155
307
  tool_call_id: toolCall.id,
156
308
  },
157
309
  });
158
- // Execute the tool
310
+ const sig = `${toolCall.function.name}:${argsJson}`;
311
+ if (callSignatures.has(sig)) {
312
+ this.logger.warn(`MCP chat duplicate tool call detected — aborting re-execution. ` +
313
+ `conversationId=${conversationId} tool=${originalName}`);
314
+ const dupResult = JSON.stringify({ error: 'Duplicate tool call detected, aborting loop.' });
315
+ await this.prisma.mcp_conversation_message.create({
316
+ data: {
317
+ conversation_id: conversationId,
318
+ role: 'tool_result',
319
+ content: dupResult,
320
+ tool_name: originalName,
321
+ tool_call_id: toolCall.id,
322
+ },
323
+ });
324
+ openAiMessages.push({
325
+ role: 'tool',
326
+ tool_call_id: toolCall.id,
327
+ content: dupResult,
328
+ });
329
+ continue;
330
+ }
331
+ callSignatures.add(sig);
159
332
  let resultContent;
160
333
  const entry = this.registry.get(originalName);
161
334
  if (entry) {
@@ -176,7 +349,6 @@ let McpChatService = McpChatService_1 = class McpChatService {
176
349
  else {
177
350
  resultContent = JSON.stringify({ error: `Tool '${originalName}' not found` });
178
351
  }
179
- // Persist tool_result message
180
352
  await this.prisma.mcp_conversation_message.create({
181
353
  data: {
182
354
  conversation_id: conversationId,
@@ -186,16 +358,14 @@ let McpChatService = McpChatService_1 = class McpChatService {
186
358
  tool_call_id: toolCall.id,
187
359
  },
188
360
  });
189
- // Add tool result to context for next iteration
190
361
  openAiMessages.push({
191
362
  role: 'tool',
192
363
  tool_call_id: toolCall.id,
193
364
  content: resultContent,
194
365
  });
195
366
  }
196
- continue; // next iteration with tool results
367
+ continue;
197
368
  }
198
- // Final assistant response
199
369
  const finalContent = (assistantMsg === null || assistantMsg === void 0 ? void 0 : assistantMsg.content) || '';
200
370
  await this.prisma.mcp_conversation_message.create({
201
371
  data: {
@@ -211,6 +381,123 @@ let McpChatService = McpChatService_1 = class McpChatService {
211
381
  }
212
382
  throw new common_1.BadRequestException('Maximum tool call iterations exceeded.');
213
383
  }
384
+ // ── Gemini loop ──────────────────────────────────────────────────────────
385
+ async runGeminiLoop(conversationId, userId, locale, message, apiKey) {
386
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
387
+ const history = await this.prisma.mcp_conversation_message.findMany({
388
+ where: { conversation_id: conversationId },
389
+ orderBy: { created_at: 'asc' },
390
+ });
391
+ const geminiContents = this.toGeminiContents(history);
392
+ const toolSelection = await this.buildGeminiTools(message, conversationId, userId, history);
393
+ if (toolSelection.clarification) {
394
+ return this.recordAssistantAndReturnHistory(conversationId, toolSelection.clarification);
395
+ }
396
+ const functionDeclarations = toolSelection.tools;
397
+ let maxIterations = MAX_ITERATIONS;
398
+ let callIndex = 0;
399
+ while (maxIterations-- > 0) {
400
+ const forceFinish = maxIterations < FORCE_FINISH_THRESHOLD;
401
+ let response;
402
+ try {
403
+ response = await axios_1.default.post(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`, {
404
+ systemInstruction: { parts: [{ text: SYSTEM_PROMPT }] },
405
+ contents: geminiContents,
406
+ tools: !forceFinish && functionDeclarations.length > 0
407
+ ? [{ functionDeclarations }]
408
+ : undefined,
409
+ }, { headers: { 'Content-Type': 'application/json' } });
410
+ }
411
+ catch (err) {
412
+ const status = (_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.status;
413
+ const errData = (_b = err === null || err === void 0 ? void 0 : err.response) === null || _b === void 0 ? void 0 : _b.data;
414
+ if (status === 429) {
415
+ throw new common_1.HttpException((_d = (_c = errData === null || errData === void 0 ? void 0 : errData.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : 'Gemini rate limit exceeded. Please try again later.', common_1.HttpStatus.TOO_MANY_REQUESTS);
416
+ }
417
+ if (status === 400) {
418
+ throw new common_1.BadRequestException((_f = (_e = errData === null || errData === void 0 ? void 0 : errData.error) === null || _e === void 0 ? void 0 : _e.message) !== null && _f !== void 0 ? _f : 'Invalid Gemini API request.');
419
+ }
420
+ if (status === 401 || status === 403) {
421
+ throw new common_1.BadRequestException('Invalid Gemini API key.');
422
+ }
423
+ throw new common_1.HttpException((_h = (_g = errData === null || errData === void 0 ? void 0 : errData.error) === null || _g === void 0 ? void 0 : _g.message) !== null && _h !== void 0 ? _h : 'Gemini API error.', status !== null && status !== void 0 ? status : common_1.HttpStatus.BAD_GATEWAY);
424
+ }
425
+ const candidate = (_k = (_j = response.data) === null || _j === void 0 ? void 0 : _j.candidates) === null || _k === void 0 ? void 0 : _k[0];
426
+ const parts = (_m = (_l = candidate === null || candidate === void 0 ? void 0 : candidate.content) === null || _l === void 0 ? void 0 : _l.parts) !== null && _m !== void 0 ? _m : [];
427
+ const functionCallParts = parts.filter((p) => p.functionCall);
428
+ const textParts = parts.filter((p) => p.text);
429
+ if (functionCallParts.length > 0 && !forceFinish) {
430
+ geminiContents.push({ role: 'model', parts: functionCallParts });
431
+ const functionResponseParts = [];
432
+ for (const part of functionCallParts) {
433
+ const { name, args } = part.functionCall;
434
+ const originalName = restore(name);
435
+ const argsJson = JSON.stringify(args !== null && args !== void 0 ? args : {});
436
+ const toolCallId = `gemini-${callIndex++}`;
437
+ await this.prisma.mcp_conversation_message.create({
438
+ data: {
439
+ conversation_id: conversationId,
440
+ role: 'tool_call',
441
+ content: argsJson,
442
+ tool_name: originalName,
443
+ tool_call_id: toolCallId,
444
+ },
445
+ });
446
+ let resultContent;
447
+ const entry = this.registry.get(originalName);
448
+ if (entry) {
449
+ const context = {
450
+ userId,
451
+ sessionId: '',
452
+ locale,
453
+ toolName: originalName,
454
+ };
455
+ try {
456
+ const result = await entry.handler(args !== null && args !== void 0 ? args : {}, context);
457
+ resultContent = JSON.stringify(result);
458
+ }
459
+ catch (e) {
460
+ resultContent = JSON.stringify({ error: e.message });
461
+ }
462
+ }
463
+ else {
464
+ resultContent = JSON.stringify({ error: `Tool '${originalName}' not found` });
465
+ }
466
+ await this.prisma.mcp_conversation_message.create({
467
+ data: {
468
+ conversation_id: conversationId,
469
+ role: 'tool_result',
470
+ content: resultContent,
471
+ tool_name: originalName,
472
+ tool_call_id: toolCallId,
473
+ },
474
+ });
475
+ functionResponseParts.push({
476
+ functionResponse: {
477
+ name,
478
+ response: { output: resultContent },
479
+ },
480
+ });
481
+ }
482
+ geminiContents.push({ role: 'user', parts: functionResponseParts });
483
+ continue;
484
+ }
485
+ const finalContent = textParts.map((p) => p.text).join('');
486
+ await this.prisma.mcp_conversation_message.create({
487
+ data: {
488
+ conversation_id: conversationId,
489
+ role: 'assistant',
490
+ content: finalContent,
491
+ },
492
+ });
493
+ return this.prisma.mcp_conversation_message.findMany({
494
+ where: { conversation_id: conversationId },
495
+ orderBy: { created_at: 'asc' },
496
+ });
497
+ }
498
+ throw new common_1.BadRequestException('Maximum tool call iterations exceeded.');
499
+ }
500
+ // ── Helpers ──────────────────────────────────────────────────────────────
214
501
  toOpenAiMessages(history) {
215
502
  const messages = [];
216
503
  const toolCallMap = new Map(); // tool_call_id → sanitized name
@@ -246,6 +533,58 @@ let McpChatService = McpChatService_1 = class McpChatService {
246
533
  }
247
534
  return messages;
248
535
  }
536
+ toGeminiContents(history) {
537
+ var _a, _b;
538
+ const contents = [];
539
+ let i = 0;
540
+ while (i < history.length) {
541
+ const msg = history[i];
542
+ if (msg.role === 'user') {
543
+ contents.push({ role: 'user', parts: [{ text: msg.content }] });
544
+ i++;
545
+ }
546
+ else if (msg.role === 'assistant') {
547
+ contents.push({ role: 'model', parts: [{ text: msg.content }] });
548
+ i++;
549
+ }
550
+ else if (msg.role === 'tool_call') {
551
+ const functionCallParts = [];
552
+ while (i < history.length && history[i].role === 'tool_call') {
553
+ const m = history[i];
554
+ let args = {};
555
+ try {
556
+ args = JSON.parse(m.content);
557
+ }
558
+ catch (_c) {
559
+ args = {};
560
+ }
561
+ functionCallParts.push({
562
+ functionCall: { name: sanitize((_a = m.tool_name) !== null && _a !== void 0 ? _a : ''), args },
563
+ });
564
+ i++;
565
+ }
566
+ contents.push({ role: 'model', parts: functionCallParts });
567
+ }
568
+ else if (msg.role === 'tool_result') {
569
+ const functionResponseParts = [];
570
+ while (i < history.length && history[i].role === 'tool_result') {
571
+ const m = history[i];
572
+ functionResponseParts.push({
573
+ functionResponse: {
574
+ name: sanitize((_b = m.tool_name) !== null && _b !== void 0 ? _b : ''),
575
+ response: { output: m.content },
576
+ },
577
+ });
578
+ i++;
579
+ }
580
+ contents.push({ role: 'user', parts: functionResponseParts });
581
+ }
582
+ else {
583
+ i++;
584
+ }
585
+ }
586
+ return contents;
587
+ }
249
588
  async assertOwnership(conversationId, userId) {
250
589
  const conv = await this.prisma.mcp_conversation.findUnique({
251
590
  where: { id: conversationId },
@@ -258,33 +597,75 @@ let McpChatService = McpChatService_1 = class McpChatService {
258
597
  throw new common_1.ForbiddenException('Access denied to this conversation.');
259
598
  }
260
599
  }
261
- async getOpenAiKey() {
262
- const settings = await this.setting.getSettingValues(['ai-openai-api-key']);
600
+ async getProviderKey() {
601
+ const settings = await this.setting.getSettingValues([
602
+ 'mcp-ai-provider',
603
+ 'ai-openai-api-key',
604
+ 'ai-gemini-api-key',
605
+ ]);
606
+ const provider = (settings['mcp-ai-provider'] || 'openai');
607
+ if (provider === 'gemini') {
608
+ const key = settings['ai-gemini-api-key'];
609
+ if (!key) {
610
+ throw new common_1.BadRequestException('Gemini API key is not configured. Please set it in Settings → AI → Gemini API Key.');
611
+ }
612
+ return { provider: 'gemini', apiKey: key };
613
+ }
263
614
  const key = settings['ai-openai-api-key'];
264
615
  if (!key) {
265
- throw new common_1.BadRequestException('OpenAI API key is not configured (setting "ai-openai-api-key").');
616
+ throw new common_1.BadRequestException('OpenAI API key is not configured. Please set it in Settings → AI → OpenAI API Key.');
266
617
  }
267
- return key;
618
+ return { provider: 'openai', apiKey: key };
268
619
  }
269
- buildOpenAiTools(message, conversationId, userId) {
620
+ async buildOpenAiTools(message, conversationId, userId, history) {
621
+ return this.selectOpenAiTools(message, conversationId, userId, history);
622
+ }
623
+ async buildGeminiTools(message, conversationId, userId, history) {
624
+ const selection = await this.selectOpenAiTools(message, conversationId, userId, history);
625
+ return {
626
+ clarification: selection.clarification,
627
+ tools: selection.tools.map((t) => t.function),
628
+ };
629
+ }
630
+ async selectOpenAiTools(message, conversationId, userId, history) {
270
631
  var _a;
271
632
  const allEntries = this.registry.getAll();
633
+ const allowedToolNames = await this.getAllowedMcpToolNames(userId);
634
+ const allowedEntries = allEntries.filter((entry) => allowedToolNames.has(entry.definition.name));
272
635
  const dedupedBySanitized = new Map();
273
636
  const excludedDuplicates = [];
274
- const normalizedMessage = message.toLowerCase();
275
- for (const [index, entry] of allEntries.entries()) {
637
+ const routerText = this.buildToolRouterText(message, history);
638
+ const normalizedMessage = this.normalizeText(routerText);
639
+ const messageTerms = this.getMessageKeywordSet(normalizedMessage);
640
+ const domainScores = this.scoreToolDomains(normalizedMessage, allowedEntries);
641
+ const candidateDomains = this.getCandidateDomains(domainScores);
642
+ const clarification = this.getToolSelectionClarification(message, normalizedMessage, domainScores, candidateDomains, allowedEntries);
643
+ if (clarification) {
644
+ this.logger.log(`MCP tools need clarification: conversationId=${conversationId} userId=${userId} ` +
645
+ `registered=${allEntries.length} authorized=${allowedEntries.length} ` +
646
+ `domains=${this.formatDomainScores(domainScores)} selected=0`);
647
+ return { tools: [], clarification };
648
+ }
649
+ const entriesForRanking = candidateDomains.length > 0
650
+ ? allowedEntries.filter((entry) => candidateDomains.includes(this.getToolDomain(entry.definition.name)))
651
+ : allowedEntries;
652
+ for (const [index, entry] of entriesForRanking.entries()) {
276
653
  const originalName = entry.definition.name;
277
654
  const sanitizedName = sanitize(originalName);
278
655
  const readOnly = Boolean(entry.definition.readOnly);
656
+ const domain = this.getToolDomain(originalName);
657
+ const matchScore = this.scoreToolMatch(normalizedMessage, messageTerms, entry, candidateDomains);
279
658
  if (dedupedBySanitized.has(sanitizedName)) {
280
659
  excludedDuplicates.push(originalName);
281
660
  continue;
282
661
  }
283
662
  const scored = {
284
663
  name: originalName,
664
+ domain,
285
665
  readOnly,
286
- priorityBucket: this.getDomainPriorityBucket(originalName),
666
+ priorityBucket: this.getDomainPriorityBucket(originalName, candidateDomains),
287
667
  keywordBoost: this.getKeywordBoost(normalizedMessage, originalName),
668
+ matchScore,
288
669
  index,
289
670
  tool: {
290
671
  type: 'function',
@@ -302,6 +683,9 @@ let McpChatService = McpChatService_1 = class McpChatService {
302
683
  }
303
684
  const scoredTools = Array.from(dedupedBySanitized.values());
304
685
  scoredTools.sort((a, b) => {
686
+ if (a.matchScore !== b.matchScore) {
687
+ return b.matchScore - a.matchScore;
688
+ }
305
689
  if (a.priorityBucket !== b.priorityBucket) {
306
690
  return a.priorityBucket - b.priorityBucket;
307
691
  }
@@ -313,32 +697,62 @@ let McpChatService = McpChatService_1 = class McpChatService {
313
697
  }
314
698
  return a.index - b.index;
315
699
  });
316
- const selected = scoredTools.slice(0, MAX_OPENAI_TOOLS);
700
+ const selected = scoredTools.slice(0, TARGET_SELECTED_TOOLS);
317
701
  const excludedByLimit = scoredTools
318
- .slice(MAX_OPENAI_TOOLS)
702
+ .slice(TARGET_SELECTED_TOOLS)
319
703
  .map((tool) => tool.name);
320
704
  if (excludedDuplicates.length > 0) {
321
705
  this.logger.warn(`MCP tools duplicate sanitize names ignored: ${excludedDuplicates.length}. ` +
322
706
  `Examples: ${excludedDuplicates.slice(0, 10).join(', ')}`);
323
707
  }
324
708
  if (excludedByLimit.length > 0) {
325
- this.logger.warn(`MCP tools truncated for OpenAI limit (${MAX_OPENAI_TOOLS}). ` +
709
+ this.logger.warn(`MCP tools truncated for provider budget (${TARGET_SELECTED_TOOLS}/${MAX_OPENAI_TOOLS}). ` +
326
710
  `conversationId=${conversationId} userId=${userId} ` +
327
711
  `available=${scoredTools.length} selected=${selected.length} excluded=${excludedByLimit.length}. ` +
328
712
  `Top excluded: ${excludedByLimit.slice(0, 12).join(', ')}`);
329
713
  }
330
714
  this.logger.log(`MCP tools prepared: conversationId=${conversationId} userId=${userId} ` +
331
- `raw=${allEntries.length} deduped=${scoredTools.length} selected=${selected.length}`);
332
- return selected.map((entry) => entry.tool);
715
+ `registered=${allEntries.length} authorized=${allowedEntries.length} ` +
716
+ `domains=${this.formatDomainScores(domainScores)} ranked=${scoredTools.length} ` +
717
+ `selected=${selected.length}`);
718
+ return { tools: selected.map((entry) => entry.tool) };
333
719
  }
334
- getDomainPriorityBucket(toolName) {
720
+ async getAllowedMcpToolNames(userId) {
721
+ const routes = await this.prisma.route.findMany({
722
+ where: {
723
+ type: 'MCP',
724
+ tool_name: { not: null },
725
+ role_route: {
726
+ some: {
727
+ role: {
728
+ role_user: {
729
+ some: { user_id: userId },
730
+ },
731
+ },
732
+ },
733
+ },
734
+ },
735
+ select: { tool_name: true },
736
+ });
737
+ return new Set(routes
738
+ .map((route) => route.tool_name)
739
+ .filter((toolName) => typeof toolName === 'string'));
740
+ }
741
+ getDomainPriorityBucket(toolName, candidateDomains) {
742
+ const domain = this.getToolDomain(toolName);
743
+ const candidateIndex = candidateDomains.indexOf(domain);
744
+ if (candidateIndex >= 0) {
745
+ return candidateIndex;
746
+ }
335
747
  if (toolName.startsWith('operations.'))
336
- return 0;
748
+ return 10;
749
+ if (toolName.startsWith('finance.'))
750
+ return 11;
337
751
  if (toolName.startsWith('lms.'))
338
- return 1;
752
+ return 12;
339
753
  if (toolName.startsWith('core.'))
340
- return 2;
341
- return 3;
754
+ return 13;
755
+ return 99;
342
756
  }
343
757
  getKeywordBoost(message, toolName) {
344
758
  const terms = this.getMessageKeywordSet(message);
@@ -368,6 +782,216 @@ let McpChatService = McpChatService_1 = class McpChatService {
368
782
  }
369
783
  return boost;
370
784
  }
785
+ buildToolRouterText(message, history) {
786
+ const recentContext = history
787
+ .slice(-8)
788
+ .map((item) => [item.role === 'user' ? item.content : undefined, item.tool_name]
789
+ .filter(Boolean)
790
+ .join(' '))
791
+ .join(' ');
792
+ return `${recentContext} ${message}`;
793
+ }
794
+ normalizeText(value) {
795
+ return value
796
+ .toLowerCase()
797
+ .normalize('NFD')
798
+ .replace(/[\u0300-\u036f]/g, '')
799
+ .replace(/[^a-z0-9]+/g, ' ')
800
+ .replace(/\s+/g, ' ')
801
+ .trim();
802
+ }
803
+ getToolDomain(toolName) {
804
+ return toolName.split('.')[0] || 'unknown';
805
+ }
806
+ scoreToolDomains(normalizedMessage, entries) {
807
+ var _a, _b;
808
+ const scores = new Map();
809
+ for (const [domain, keywords] of Object.entries(DOMAIN_KEYWORDS)) {
810
+ let score = 0;
811
+ if (this.hasTerm(normalizedMessage, domain)) {
812
+ score += 3;
813
+ }
814
+ for (const keyword of keywords) {
815
+ const normalizedKeyword = this.normalizeText(keyword);
816
+ if (normalizedKeyword && normalizedMessage.includes(normalizedKeyword)) {
817
+ score += normalizedKeyword.includes(' ') ? 3 : 2;
818
+ }
819
+ }
820
+ if (score > 0 && entries.some((entry) => this.getToolDomain(entry.definition.name) === domain)) {
821
+ scores.set(domain, score);
822
+ }
823
+ }
824
+ for (const entry of entries) {
825
+ const domain = this.getToolDomain(entry.definition.name);
826
+ const toolText = this.normalizeText(`${entry.definition.name} ${(_a = entry.definition.description) !== null && _a !== void 0 ? _a : ''}`);
827
+ let score = (_b = scores.get(domain)) !== null && _b !== void 0 ? _b : 0;
828
+ for (const term of normalizedMessage.split(' ')) {
829
+ if (term.length >= 4 && toolText.includes(term)) {
830
+ score += 1;
831
+ }
832
+ }
833
+ if (score > 0) {
834
+ scores.set(domain, score);
835
+ }
836
+ }
837
+ return scores;
838
+ }
839
+ getCandidateDomains(domainScores) {
840
+ const ranked = Array.from(domainScores.entries())
841
+ .filter(([, score]) => score >= MIN_DOMAIN_CONFIDENCE)
842
+ .sort((a, b) => b[1] - a[1]);
843
+ if (ranked.length === 0) {
844
+ return [];
845
+ }
846
+ const topScore = ranked[0][1];
847
+ return ranked
848
+ .filter(([, score]) => topScore - score <= AMBIGUOUS_DOMAIN_DELTA)
849
+ .map(([domain]) => domain);
850
+ }
851
+ getToolSelectionClarification(originalMessage, normalizedMessage, domainScores, candidateDomains, entries) {
852
+ if (!this.messageLooksLikeToolRequest(normalizedMessage)) {
853
+ return undefined;
854
+ }
855
+ const rankedDomains = Array.from(domainScores.entries()).sort((a, b) => b[1] - a[1]);
856
+ if (rankedDomains.length === 0 || rankedDomains[0][1] < MIN_DOMAIN_CONFIDENCE) {
857
+ return this.isLikelyPortuguese(originalMessage)
858
+ ? 'Você quer consultar qual área do sistema? Por exemplo: financeiro, operações, LMS ou configurações.'
859
+ : 'Which system area do you want to query? For example: finance, operations, LMS, or settings.';
860
+ }
861
+ if (candidateDomains.length > 1) {
862
+ const options = candidateDomains
863
+ .slice(0, 3)
864
+ .map((domain) => this.getDomainLabel(domain, originalMessage))
865
+ .join(', ');
866
+ return this.isLikelyPortuguese(originalMessage)
867
+ ? `Você quer consultar qual área: ${options}?`
868
+ : `Which area do you want to query: ${options}?`;
869
+ }
870
+ if (candidateDomains[0] === 'finance' &&
871
+ this.isAmbiguousFinanceAccountsRequest(normalizedMessage, entries)) {
872
+ return this.isLikelyPortuguese(originalMessage)
873
+ ? 'Você quer consultar contas bancárias, contas a pagar ou contas a receber?'
874
+ : 'Do you want bank accounts, accounts payable, or accounts receivable?';
875
+ }
876
+ return undefined;
877
+ }
878
+ scoreToolMatch(normalizedMessage, terms, entry, candidateDomains) {
879
+ var _a;
880
+ const toolName = entry.definition.name;
881
+ const toolText = this.normalizeText(`${toolName} ${(_a = entry.definition.description) !== null && _a !== void 0 ? _a : ''} ${this.getSchemaText(entry.definition.inputSchema)}`);
882
+ const domain = this.getToolDomain(toolName);
883
+ let score = candidateDomains.includes(domain) ? 8 : 0;
884
+ for (const term of normalizedMessage.split(' ')) {
885
+ if (term.length < 3)
886
+ continue;
887
+ if (toolText.includes(term)) {
888
+ score += term.length >= 5 ? 2 : 1;
889
+ }
890
+ }
891
+ if (terms.has('list') && entry.definition.readOnly)
892
+ score += 4;
893
+ if (terms.has('write') && !entry.definition.readOnly)
894
+ score += 4;
895
+ score += this.getFinanceSpecificBoost(normalizedMessage, toolName);
896
+ score += this.getKeywordBoost(normalizedMessage, toolName);
897
+ return score;
898
+ }
899
+ getSchemaText(schema) {
900
+ if (!schema || typeof schema !== 'object') {
901
+ return '';
902
+ }
903
+ const properties = schema.properties;
904
+ if (!properties || typeof properties !== 'object') {
905
+ return '';
906
+ }
907
+ return Object.keys(properties).join(' ');
908
+ }
909
+ getFinanceSpecificBoost(message, toolName) {
910
+ if (!toolName.startsWith('finance.')) {
911
+ return 0;
912
+ }
913
+ let boost = 0;
914
+ if (/banco|bancaria|bancarias|bank/.test(message) &&
915
+ toolName.includes('bank-accounts')) {
916
+ boost += 10;
917
+ }
918
+ if (/extrato|statement/.test(message) && toolName.includes('statements')) {
919
+ boost += 10;
920
+ }
921
+ if (/pagar|payable/.test(message) && toolName.includes('accounts-payable')) {
922
+ boost += 10;
923
+ }
924
+ if (/receber|receivable/.test(message) && toolName.includes('accounts-receivable')) {
925
+ boost += 10;
926
+ }
927
+ if (/moeda|currency/.test(message) && toolName.includes('currencies')) {
928
+ boost += 8;
929
+ }
930
+ if (/transferencia|transfer|transferencias/.test(message) && toolName.includes('transfers')) {
931
+ boost += 8;
932
+ }
933
+ return boost;
934
+ }
935
+ messageLooksLikeToolRequest(normalizedMessage) {
936
+ return ACTION_KEYWORDS.some((keyword) => this.hasTerm(normalizedMessage, this.normalizeText(keyword)));
937
+ }
938
+ isAmbiguousFinanceAccountsRequest(normalizedMessage, entries) {
939
+ const mentionsGenericAccounts = /\b(conta|contas|account|accounts)\b/.test(normalizedMessage);
940
+ const hasQualifier = /banco|bancaria|bancarias|bank|extrato|statement|pagar|payable|receber|receivable/.test(normalizedMessage);
941
+ if (!mentionsGenericAccounts || hasQualifier) {
942
+ return false;
943
+ }
944
+ const financeToolNames = entries
945
+ .map((entry) => entry.definition.name)
946
+ .filter((name) => name.startsWith('finance.'));
947
+ return (financeToolNames.some((name) => name.includes('bank-accounts')) &&
948
+ (financeToolNames.some((name) => name.includes('accounts-payable')) ||
949
+ financeToolNames.some((name) => name.includes('accounts-receivable'))));
950
+ }
951
+ hasTerm(text, term) {
952
+ return new RegExp(`(^|\\s)${this.escapeRegex(term)}($|\\s)`).test(text);
953
+ }
954
+ escapeRegex(value) {
955
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
956
+ }
957
+ getDomainLabel(domain, message) {
958
+ const labels = {
959
+ finance: { pt: 'financeiro', en: 'finance' },
960
+ operations: { pt: 'operações', en: 'operations' },
961
+ lms: { pt: 'LMS/cursos', en: 'LMS/courses' },
962
+ core: { pt: 'configurações/core', en: 'settings/core' },
963
+ contact: { pt: 'contatos', en: 'contacts' },
964
+ };
965
+ const label = labels[domain];
966
+ if (!label) {
967
+ return domain;
968
+ }
969
+ return this.isLikelyPortuguese(message) ? label.pt : label.en;
970
+ }
971
+ isLikelyPortuguese(message) {
972
+ return /(voce|você|qual|consultar|listar|conta|contas|bancaria|bancária|extrato|tarefas|curso|cursos)/i.test(message);
973
+ }
974
+ formatDomainScores(domainScores) {
975
+ const formatted = Array.from(domainScores.entries())
976
+ .sort((a, b) => b[1] - a[1])
977
+ .slice(0, 5)
978
+ .map(([domain, score]) => `${domain}:${score}`)
979
+ .join(',');
980
+ return formatted || 'none';
981
+ }
982
+ async recordAssistantAndReturnHistory(conversationId, content) {
983
+ await this.prisma.mcp_conversation_message.create({
984
+ data: {
985
+ conversation_id: conversationId,
986
+ role: 'assistant',
987
+ content,
988
+ },
989
+ });
990
+ return this.prisma.mcp_conversation_message.findMany({
991
+ where: { conversation_id: conversationId },
992
+ orderBy: { created_at: 'asc' },
993
+ });
994
+ }
371
995
  getMessageKeywordSet(message) {
372
996
  const keywords = new Set();
373
997
  if (/(tarefa|tarefas|task|tasks)/i.test(message)) {
@@ -382,6 +1006,12 @@ let McpChatService = McpChatService_1 = class McpChatService {
382
1006
  if (/(pendente|pendentes|pending|open|aberta|abertas)/i.test(message)) {
383
1007
  keywords.add('pending');
384
1008
  }
1009
+ if (/(listar|lista|buscar|procurar|consultar|mostrar|ver|list|search|find|show|get)/i.test(message)) {
1010
+ keywords.add('list');
1011
+ }
1012
+ if (/(criar|adicionar|atualizar|alterar|remover|deletar|excluir|create|add|update|delete|remove)/i.test(message)) {
1013
+ keywords.add('write');
1014
+ }
385
1015
  return keywords;
386
1016
  }
387
1017
  };