@lenne.tech/nest-server 11.25.6 → 11.26.0

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 (335) hide show
  1. package/.claude/rules/configurable-features.md +2 -0
  2. package/CLAUDE.md +18 -0
  3. package/FRAMEWORK-API.md +48 -1
  4. package/dist/config.env.js +35 -1
  5. package/dist/config.env.js.map +1 -1
  6. package/dist/core/common/helpers/config.helper.js +2 -2
  7. package/dist/core/common/helpers/config.helper.js.map +1 -1
  8. package/dist/core/common/interceptors/check-security.interceptor.js +9 -2
  9. package/dist/core/common/interceptors/check-security.interceptor.js.map +1 -1
  10. package/dist/core/common/interfaces/server-options.interface.d.ts +89 -0
  11. package/dist/core/modules/ai/core-ai-mcp.controller.d.ts +21 -0
  12. package/dist/core/modules/ai/core-ai-mcp.controller.js +191 -0
  13. package/dist/core/modules/ai/core-ai-mcp.controller.js.map +1 -0
  14. package/dist/core/modules/ai/core-ai.controller.d.ts +90 -0
  15. package/dist/core/modules/ai/core-ai.controller.js +576 -0
  16. package/dist/core/modules/ai/core-ai.controller.js.map +1 -0
  17. package/dist/core/modules/ai/core-ai.module.d.ts +43 -0
  18. package/dist/core/modules/ai/core-ai.module.js +164 -0
  19. package/dist/core/modules/ai/core-ai.module.js.map +1 -0
  20. package/dist/core/modules/ai/core-ai.resolver.d.ts +84 -0
  21. package/dist/core/modules/ai/core-ai.resolver.js +530 -0
  22. package/dist/core/modules/ai/core-ai.resolver.js.map +1 -0
  23. package/dist/core/modules/ai/helpers/ai-mcp-oauth.helper.d.ts +7 -0
  24. package/dist/core/modules/ai/helpers/ai-mcp-oauth.helper.js +16 -0
  25. package/dist/core/modules/ai/helpers/ai-mcp-oauth.helper.js.map +1 -0
  26. package/dist/core/modules/ai/hooks/ai-hook.base.d.ts +10 -0
  27. package/dist/core/modules/ai/hooks/ai-hook.base.js +30 -0
  28. package/dist/core/modules/ai/hooks/ai-hook.base.js.map +1 -0
  29. package/dist/core/modules/ai/hooks/ai-hook.registry.d.ts +22 -0
  30. package/dist/core/modules/ai/hooks/ai-hook.registry.js +98 -0
  31. package/dist/core/modules/ai/hooks/ai-hook.registry.js.map +1 -0
  32. package/dist/core/modules/ai/index.d.ts +69 -0
  33. package/dist/core/modules/ai/index.js +86 -0
  34. package/dist/core/modules/ai/index.js.map +1 -0
  35. package/dist/core/modules/ai/inputs/core-ai-budget-limit-create.input.d.ts +5 -0
  36. package/dist/core/modules/ai/inputs/core-ai-budget-limit-create.input.js +43 -0
  37. package/dist/core/modules/ai/inputs/core-ai-budget-limit-create.input.js.map +1 -0
  38. package/dist/core/modules/ai/inputs/core-ai-budget-limit.input.d.ts +7 -0
  39. package/dist/core/modules/ai/inputs/core-ai-budget-limit.input.js +74 -0
  40. package/dist/core/modules/ai/inputs/core-ai-budget-limit.input.js.map +1 -0
  41. package/dist/core/modules/ai/inputs/core-ai-connection-create.input.d.ts +6 -0
  42. package/dist/core/modules/ai/inputs/core-ai-connection-create.input.js +49 -0
  43. package/dist/core/modules/ai/inputs/core-ai-connection-create.input.js.map +1 -0
  44. package/dist/core/modules/ai/inputs/core-ai-connection-preference.input.d.ts +6 -0
  45. package/dist/core/modules/ai/inputs/core-ai-connection-preference.input.js +60 -0
  46. package/dist/core/modules/ai/inputs/core-ai-connection-preference.input.js.map +1 -0
  47. package/dist/core/modules/ai/inputs/core-ai-connection.input.d.ts +23 -0
  48. package/dist/core/modules/ai/inputs/core-ai-connection.input.js +229 -0
  49. package/dist/core/modules/ai/inputs/core-ai-connection.input.js.map +1 -0
  50. package/dist/core/modules/ai/inputs/core-ai-conversation-create.input.d.ts +3 -0
  51. package/dist/core/modules/ai/inputs/core-ai-conversation-create.input.js +21 -0
  52. package/dist/core/modules/ai/inputs/core-ai-conversation-create.input.js.map +1 -0
  53. package/dist/core/modules/ai/inputs/core-ai-conversation.input.d.ts +4 -0
  54. package/dist/core/modules/ai/inputs/core-ai-conversation.input.js +42 -0
  55. package/dist/core/modules/ai/inputs/core-ai-conversation.input.js.map +1 -0
  56. package/dist/core/modules/ai/inputs/core-ai-prompt-create.input.d.ts +10 -0
  57. package/dist/core/modules/ai/inputs/core-ai-prompt-create.input.js +74 -0
  58. package/dist/core/modules/ai/inputs/core-ai-prompt-create.input.js.map +1 -0
  59. package/dist/core/modules/ai/inputs/core-ai-prompt-hint-create.input.d.ts +5 -0
  60. package/dist/core/modules/ai/inputs/core-ai-prompt-hint-create.input.js +41 -0
  61. package/dist/core/modules/ai/inputs/core-ai-prompt-hint-create.input.js.map +1 -0
  62. package/dist/core/modules/ai/inputs/core-ai-prompt-hint.input.d.ts +6 -0
  63. package/dist/core/modules/ai/inputs/core-ai-prompt-hint.input.js +63 -0
  64. package/dist/core/modules/ai/inputs/core-ai-prompt-hint.input.js.map +1 -0
  65. package/dist/core/modules/ai/inputs/core-ai-prompt-update.input.d.ts +10 -0
  66. package/dist/core/modules/ai/inputs/core-ai-prompt-update.input.js +70 -0
  67. package/dist/core/modules/ai/inputs/core-ai-prompt-update.input.js.map +1 -0
  68. package/dist/core/modules/ai/inputs/core-ai-prompt.input.d.ts +18 -0
  69. package/dist/core/modules/ai/inputs/core-ai-prompt.input.js +131 -0
  70. package/dist/core/modules/ai/inputs/core-ai-prompt.input.js.map +1 -0
  71. package/dist/core/modules/ai/inputs/core-ai-slot-create.input.d.ts +5 -0
  72. package/dist/core/modules/ai/inputs/core-ai-slot-create.input.js +41 -0
  73. package/dist/core/modules/ai/inputs/core-ai-slot-create.input.js.map +1 -0
  74. package/dist/core/modules/ai/inputs/core-ai-slot-update.input.d.ts +9 -0
  75. package/dist/core/modules/ai/inputs/core-ai-slot-update.input.js +91 -0
  76. package/dist/core/modules/ai/inputs/core-ai-slot-update.input.js.map +1 -0
  77. package/dist/core/modules/ai/interfaces/ai-hook.interface.d.ts +28 -0
  78. package/dist/core/modules/ai/interfaces/ai-hook.interface.js +3 -0
  79. package/dist/core/modules/ai/interfaces/ai-hook.interface.js.map +1 -0
  80. package/dist/core/modules/ai/interfaces/ai-placeholder.interface.d.ts +21 -0
  81. package/dist/core/modules/ai/interfaces/ai-placeholder.interface.js +3 -0
  82. package/dist/core/modules/ai/interfaces/ai-placeholder.interface.js.map +1 -0
  83. package/dist/core/modules/ai/interfaces/ai-tool.interface.d.ts +30 -0
  84. package/dist/core/modules/ai/interfaces/ai-tool.interface.js +3 -0
  85. package/dist/core/modules/ai/interfaces/ai-tool.interface.js.map +1 -0
  86. package/dist/core/modules/ai/interfaces/llm-provider.interface.d.ts +55 -0
  87. package/dist/core/modules/ai/interfaces/llm-provider.interface.js +3 -0
  88. package/dist/core/modules/ai/interfaces/llm-provider.interface.js.map +1 -0
  89. package/dist/core/modules/ai/interfaces/resolved-ai-connection.interface.d.ts +16 -0
  90. package/dist/core/modules/ai/interfaces/resolved-ai-connection.interface.js +3 -0
  91. package/dist/core/modules/ai/interfaces/resolved-ai-connection.interface.js.map +1 -0
  92. package/dist/core/modules/ai/models/core-ai-action.model.d.ts +6 -0
  93. package/dist/core/modules/ai/models/core-ai-action.model.js +44 -0
  94. package/dist/core/modules/ai/models/core-ai-action.model.js.map +1 -0
  95. package/dist/core/modules/ai/models/core-ai-available-connection.model.d.ts +8 -0
  96. package/dist/core/modules/ai/models/core-ai-available-connection.model.js +53 -0
  97. package/dist/core/modules/ai/models/core-ai-available-connection.model.js.map +1 -0
  98. package/dist/core/modules/ai/models/core-ai-budget-limit.model.d.ts +97 -0
  99. package/dist/core/modules/ai/models/core-ai-budget-limit.model.js +79 -0
  100. package/dist/core/modules/ai/models/core-ai-budget-limit.model.js.map +1 -0
  101. package/dist/core/modules/ai/models/core-ai-connection-preference.model.d.ts +91 -0
  102. package/dist/core/modules/ai/models/core-ai-connection-preference.model.js +67 -0
  103. package/dist/core/modules/ai/models/core-ai-connection-preference.model.js.map +1 -0
  104. package/dist/core/modules/ai/models/core-ai-connection.model.d.ts +200 -0
  105. package/dist/core/modules/ai/models/core-ai-connection.model.js +263 -0
  106. package/dist/core/modules/ai/models/core-ai-connection.model.js.map +1 -0
  107. package/dist/core/modules/ai/models/core-ai-conversation.model.d.ts +93 -0
  108. package/dist/core/modules/ai/models/core-ai-conversation.model.js +82 -0
  109. package/dist/core/modules/ai/models/core-ai-conversation.model.js.map +1 -0
  110. package/dist/core/modules/ai/models/core-ai-interaction.model.d.ts +133 -0
  111. package/dist/core/modules/ai/models/core-ai-interaction.model.js +137 -0
  112. package/dist/core/modules/ai/models/core-ai-interaction.model.js.map +1 -0
  113. package/dist/core/modules/ai/models/core-ai-message.model.d.ts +5 -0
  114. package/dist/core/modules/ai/models/core-ai-message.model.js +38 -0
  115. package/dist/core/modules/ai/models/core-ai-message.model.js.map +1 -0
  116. package/dist/core/modules/ai/models/core-ai-mode.model.d.ts +109 -0
  117. package/dist/core/modules/ai/models/core-ai-mode.model.js +100 -0
  118. package/dist/core/modules/ai/models/core-ai-mode.model.js.map +1 -0
  119. package/dist/core/modules/ai/models/core-ai-prompt-hint.model.d.ts +103 -0
  120. package/dist/core/modules/ai/models/core-ai-prompt-hint.model.js +89 -0
  121. package/dist/core/modules/ai/models/core-ai-prompt-hint.model.js.map +1 -0
  122. package/dist/core/modules/ai/models/core-ai-prompt.model.d.ts +122 -0
  123. package/dist/core/modules/ai/models/core-ai-prompt.model.js +117 -0
  124. package/dist/core/modules/ai/models/core-ai-prompt.model.js.map +1 -0
  125. package/dist/core/modules/ai/models/core-ai-response.model.d.ts +29 -0
  126. package/dist/core/modules/ai/models/core-ai-response.model.js +105 -0
  127. package/dist/core/modules/ai/models/core-ai-response.model.js.map +1 -0
  128. package/dist/core/modules/ai/models/core-ai-slot.model.d.ts +121 -0
  129. package/dist/core/modules/ai/models/core-ai-slot.model.js +119 -0
  130. package/dist/core/modules/ai/models/core-ai-slot.model.js.map +1 -0
  131. package/dist/core/modules/ai/models/core-ai-tool-grant.model.d.ts +103 -0
  132. package/dist/core/modules/ai/models/core-ai-tool-grant.model.js +88 -0
  133. package/dist/core/modules/ai/models/core-ai-tool-grant.model.js.map +1 -0
  134. package/dist/core/modules/ai/models/core-ai-tool-policy.model.d.ts +122 -0
  135. package/dist/core/modules/ai/models/core-ai-tool-policy.model.js +121 -0
  136. package/dist/core/modules/ai/models/core-ai-tool-policy.model.js.map +1 -0
  137. package/dist/core/modules/ai/models/core-ai-usage-info.model.d.ts +24 -0
  138. package/dist/core/modules/ai/models/core-ai-usage-info.model.js +127 -0
  139. package/dist/core/modules/ai/models/core-ai-usage-info.model.js.map +1 -0
  140. package/dist/core/modules/ai/models/core-ai-usage.model.d.ts +5 -0
  141. package/dist/core/modules/ai/models/core-ai-usage.model.js +38 -0
  142. package/dist/core/modules/ai/models/core-ai-usage.model.js.map +1 -0
  143. package/dist/core/modules/ai/providers/claude-cli.provider.d.ts +16 -0
  144. package/dist/core/modules/ai/providers/claude-cli.provider.js +151 -0
  145. package/dist/core/modules/ai/providers/claude-cli.provider.js.map +1 -0
  146. package/dist/core/modules/ai/providers/llm-provider.factory.d.ts +10 -0
  147. package/dist/core/modules/ai/providers/llm-provider.factory.js +45 -0
  148. package/dist/core/modules/ai/providers/llm-provider.factory.js.map +1 -0
  149. package/dist/core/modules/ai/providers/openai-compatible.provider.d.ts +32 -0
  150. package/dist/core/modules/ai/providers/openai-compatible.provider.js +236 -0
  151. package/dist/core/modules/ai/providers/openai-compatible.provider.js.map +1 -0
  152. package/dist/core/modules/ai/services/ai-crypto.service.d.ts +11 -0
  153. package/dist/core/modules/ai/services/ai-crypto.service.js +84 -0
  154. package/dist/core/modules/ai/services/ai-crypto.service.js.map +1 -0
  155. package/dist/core/modules/ai/services/core-ai-budget.service.d.ts +42 -0
  156. package/dist/core/modules/ai/services/core-ai-budget.service.js +211 -0
  157. package/dist/core/modules/ai/services/core-ai-budget.service.js.map +1 -0
  158. package/dist/core/modules/ai/services/core-ai-connection-preference.service.d.ts +18 -0
  159. package/dist/core/modules/ai/services/core-ai-connection-preference.service.js +53 -0
  160. package/dist/core/modules/ai/services/core-ai-connection-preference.service.js.map +1 -0
  161. package/dist/core/modules/ai/services/core-ai-connection-resolver.service.d.ts +55 -0
  162. package/dist/core/modules/ai/services/core-ai-connection-resolver.service.js +177 -0
  163. package/dist/core/modules/ai/services/core-ai-connection-resolver.service.js.map +1 -0
  164. package/dist/core/modules/ai/services/core-ai-connection.service.d.ts +43 -0
  165. package/dist/core/modules/ai/services/core-ai-connection.service.js +272 -0
  166. package/dist/core/modules/ai/services/core-ai-connection.service.js.map +1 -0
  167. package/dist/core/modules/ai/services/core-ai-conversation.service.d.ts +26 -0
  168. package/dist/core/modules/ai/services/core-ai-conversation.service.js +72 -0
  169. package/dist/core/modules/ai/services/core-ai-conversation.service.js.map +1 -0
  170. package/dist/core/modules/ai/services/core-ai-interaction.service.d.ts +13 -0
  171. package/dist/core/modules/ai/services/core-ai-interaction.service.js +52 -0
  172. package/dist/core/modules/ai/services/core-ai-interaction.service.js.map +1 -0
  173. package/dist/core/modules/ai/services/core-ai-mcp-client.service.d.ts +51 -0
  174. package/dist/core/modules/ai/services/core-ai-mcp-client.service.js +123 -0
  175. package/dist/core/modules/ai/services/core-ai-mcp-client.service.js.map +1 -0
  176. package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.d.ts +68 -0
  177. package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.js +279 -0
  178. package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.js.map +1 -0
  179. package/dist/core/modules/ai/services/core-ai-mcp.service.d.ts +22 -0
  180. package/dist/core/modules/ai/services/core-ai-mcp.service.js +59 -0
  181. package/dist/core/modules/ai/services/core-ai-mcp.service.js.map +1 -0
  182. package/dist/core/modules/ai/services/core-ai-mode.service.d.ts +14 -0
  183. package/dist/core/modules/ai/services/core-ai-mode.service.js +56 -0
  184. package/dist/core/modules/ai/services/core-ai-mode.service.js.map +1 -0
  185. package/dist/core/modules/ai/services/core-ai-placeholder.registry.d.ts +15 -0
  186. package/dist/core/modules/ai/services/core-ai-placeholder.registry.js +102 -0
  187. package/dist/core/modules/ai/services/core-ai-placeholder.registry.js.map +1 -0
  188. package/dist/core/modules/ai/services/core-ai-prompt-builder.service.d.ts +42 -0
  189. package/dist/core/modules/ai/services/core-ai-prompt-builder.service.js +136 -0
  190. package/dist/core/modules/ai/services/core-ai-prompt-builder.service.js.map +1 -0
  191. package/dist/core/modules/ai/services/core-ai-prompt-hint.service.d.ts +29 -0
  192. package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js +98 -0
  193. package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js.map +1 -0
  194. package/dist/core/modules/ai/services/core-ai-prompt.service.d.ts +21 -0
  195. package/dist/core/modules/ai/services/core-ai-prompt.service.js +143 -0
  196. package/dist/core/modules/ai/services/core-ai-prompt.service.js.map +1 -0
  197. package/dist/core/modules/ai/services/core-ai-slot.service.d.ts +51 -0
  198. package/dist/core/modules/ai/services/core-ai-slot.service.js +372 -0
  199. package/dist/core/modules/ai/services/core-ai-slot.service.js.map +1 -0
  200. package/dist/core/modules/ai/services/core-ai-tool-grant.service.d.ts +22 -0
  201. package/dist/core/modules/ai/services/core-ai-tool-grant.service.js +97 -0
  202. package/dist/core/modules/ai/services/core-ai-tool-grant.service.js.map +1 -0
  203. package/dist/core/modules/ai/services/core-ai-tool-policy.service.d.ts +23 -0
  204. package/dist/core/modules/ai/services/core-ai-tool-policy.service.js +102 -0
  205. package/dist/core/modules/ai/services/core-ai-tool-policy.service.js.map +1 -0
  206. package/dist/core/modules/ai/services/core-ai.service.d.ts +144 -0
  207. package/dist/core/modules/ai/services/core-ai.service.js +991 -0
  208. package/dist/core/modules/ai/services/core-ai.service.js.map +1 -0
  209. package/dist/core/modules/ai/tools/ai-tool.base.d.ts +15 -0
  210. package/dist/core/modules/ai/tools/ai-tool.base.js +30 -0
  211. package/dist/core/modules/ai/tools/ai-tool.base.js.map +1 -0
  212. package/dist/core/modules/ai/tools/ai-tool.registry.d.ts +19 -0
  213. package/dist/core/modules/ai/tools/ai-tool.registry.js +71 -0
  214. package/dist/core/modules/ai/tools/ai-tool.registry.js.map +1 -0
  215. package/dist/core/modules/ai/tools/ask-user-question.tool.d.ts +38 -0
  216. package/dist/core/modules/ai/tools/ask-user-question.tool.js +65 -0
  217. package/dist/core/modules/ai/tools/ask-user-question.tool.js.map +1 -0
  218. package/dist/core/modules/ai/tools/search-tools.tool.d.ts +24 -0
  219. package/dist/core/modules/ai/tools/search-tools.tool.js +68 -0
  220. package/dist/core/modules/ai/tools/search-tools.tool.js.map +1 -0
  221. package/dist/core/modules/error-code/error-codes.d.ts +81 -0
  222. package/dist/core/modules/error-code/error-codes.js +72 -0
  223. package/dist/core/modules/error-code/error-codes.js.map +1 -1
  224. package/dist/core.module.js +8 -0
  225. package/dist/core.module.js.map +1 -1
  226. package/dist/index.d.ts +1 -0
  227. package/dist/index.js +1 -0
  228. package/dist/index.js.map +1 -1
  229. package/dist/server/modules/ai/ai-tools.module.d.ts +5 -0
  230. package/dist/server/modules/ai/ai-tools.module.js +36 -0
  231. package/dist/server/modules/ai/ai-tools.module.js.map +1 -0
  232. package/dist/server/modules/ai/tools/delete-user.tool.d.ts +30 -0
  233. package/dist/server/modules/ai/tools/delete-user.tool.js +49 -0
  234. package/dist/server/modules/ai/tools/delete-user.tool.js.map +1 -0
  235. package/dist/server/modules/ai/tools/find-users.tool.d.ts +30 -0
  236. package/dist/server/modules/ai/tools/find-users.tool.js +52 -0
  237. package/dist/server/modules/ai/tools/find-users.tool.js.map +1 -0
  238. package/dist/server/modules/ai/tools/get-user.tool.d.ts +26 -0
  239. package/dist/server/modules/ai/tools/get-user.tool.js +48 -0
  240. package/dist/server/modules/ai/tools/get-user.tool.js.map +1 -0
  241. package/dist/server/modules/ai/tools/update-user-job-title.tool.d.ts +32 -0
  242. package/dist/server/modules/ai/tools/update-user-job-title.tool.js +54 -0
  243. package/dist/server/modules/ai/tools/update-user-job-title.tool.js.map +1 -0
  244. package/dist/server/modules/error-code/error-codes.d.ts +9 -0
  245. package/dist/server/server.module.js +2 -0
  246. package/dist/server/server.module.js.map +1 -1
  247. package/dist/tsconfig.build.tsbuildinfo +1 -1
  248. package/docs/REQUEST-LIFECYCLE.md +10 -0
  249. package/migration-guides/11.25.x-to-11.26.0.md +278 -0
  250. package/package.json +2 -1
  251. package/src/config.env.ts +48 -1
  252. package/src/core/common/helpers/config.helper.ts +5 -2
  253. package/src/core/common/interceptors/check-security.interceptor.ts +13 -3
  254. package/src/core/common/interfaces/server-options.interface.ts +312 -0
  255. package/src/core/modules/ai/INTEGRATION-CHECKLIST.md +220 -0
  256. package/src/core/modules/ai/README.md +526 -0
  257. package/src/core/modules/ai/core-ai-mcp.controller.ts +223 -0
  258. package/src/core/modules/ai/core-ai.controller.ts +547 -0
  259. package/src/core/modules/ai/core-ai.module.ts +269 -0
  260. package/src/core/modules/ai/core-ai.resolver.ts +526 -0
  261. package/src/core/modules/ai/helpers/ai-mcp-oauth.helper.ts +35 -0
  262. package/src/core/modules/ai/hooks/ai-hook.base.ts +23 -0
  263. package/src/core/modules/ai/hooks/ai-hook.registry.ts +119 -0
  264. package/src/core/modules/ai/index.ts +70 -0
  265. package/src/core/modules/ai/inputs/core-ai-budget-limit-create.input.ts +27 -0
  266. package/src/core/modules/ai/inputs/core-ai-budget-limit.input.ts +52 -0
  267. package/src/core/modules/ai/inputs/core-ai-connection-create.input.ts +32 -0
  268. package/src/core/modules/ai/inputs/core-ai-connection-preference.input.ts +42 -0
  269. package/src/core/modules/ai/inputs/core-ai-connection.input.ts +235 -0
  270. package/src/core/modules/ai/inputs/core-ai-conversation-create.input.ts +13 -0
  271. package/src/core/modules/ai/inputs/core-ai-conversation.input.ts +33 -0
  272. package/src/core/modules/ai/inputs/core-ai-prompt-create.input.ts +50 -0
  273. package/src/core/modules/ai/inputs/core-ai-prompt-hint-create.input.ts +26 -0
  274. package/src/core/modules/ai/inputs/core-ai-prompt-hint.input.ts +43 -0
  275. package/src/core/modules/ai/inputs/core-ai-prompt-update.input.ts +42 -0
  276. package/src/core/modules/ai/inputs/core-ai-prompt.input.ts +144 -0
  277. package/src/core/modules/ai/inputs/core-ai-slot-create.input.ts +26 -0
  278. package/src/core/modules/ai/inputs/core-ai-slot-update.input.ts +65 -0
  279. package/src/core/modules/ai/interfaces/ai-hook.interface.ts +74 -0
  280. package/src/core/modules/ai/interfaces/ai-placeholder.interface.ts +49 -0
  281. package/src/core/modules/ai/interfaces/ai-tool.interface.ts +112 -0
  282. package/src/core/modules/ai/interfaces/llm-provider.interface.ts +187 -0
  283. package/src/core/modules/ai/interfaces/resolved-ai-connection.interface.ts +55 -0
  284. package/src/core/modules/ai/models/core-ai-action.model.ts +37 -0
  285. package/src/core/modules/ai/models/core-ai-available-connection.model.ts +42 -0
  286. package/src/core/modules/ai/models/core-ai-budget-limit.model.ts +82 -0
  287. package/src/core/modules/ai/models/core-ai-connection-preference.model.ts +72 -0
  288. package/src/core/modules/ai/models/core-ai-connection.model.ts +330 -0
  289. package/src/core/modules/ai/models/core-ai-conversation.model.ts +87 -0
  290. package/src/core/modules/ai/models/core-ai-interaction.model.ts +145 -0
  291. package/src/core/modules/ai/models/core-ai-message.model.ts +29 -0
  292. package/src/core/modules/ai/models/core-ai-mode.model.ts +93 -0
  293. package/src/core/modules/ai/models/core-ai-prompt-hint.model.ts +98 -0
  294. package/src/core/modules/ai/models/core-ai-prompt.model.ts +120 -0
  295. package/src/core/modules/ai/models/core-ai-response.model.ts +119 -0
  296. package/src/core/modules/ai/models/core-ai-slot.model.ts +146 -0
  297. package/src/core/modules/ai/models/core-ai-tool-grant.model.ts +87 -0
  298. package/src/core/modules/ai/models/core-ai-tool-policy.model.ts +115 -0
  299. package/src/core/modules/ai/models/core-ai-usage-info.model.ts +107 -0
  300. package/src/core/modules/ai/models/core-ai-usage.model.ts +29 -0
  301. package/src/core/modules/ai/providers/claude-cli.provider.ts +240 -0
  302. package/src/core/modules/ai/providers/llm-provider.factory.ts +63 -0
  303. package/src/core/modules/ai/providers/openai-compatible.provider.ts +329 -0
  304. package/src/core/modules/ai/services/ai-crypto.service.ts +120 -0
  305. package/src/core/modules/ai/services/core-ai-budget.service.ts +297 -0
  306. package/src/core/modules/ai/services/core-ai-connection-preference.service.ts +74 -0
  307. package/src/core/modules/ai/services/core-ai-connection-resolver.service.ts +308 -0
  308. package/src/core/modules/ai/services/core-ai-connection.service.ts +387 -0
  309. package/src/core/modules/ai/services/core-ai-conversation.service.ts +105 -0
  310. package/src/core/modules/ai/services/core-ai-interaction.service.ts +56 -0
  311. package/src/core/modules/ai/services/core-ai-mcp-client.service.ts +169 -0
  312. package/src/core/modules/ai/services/core-ai-mcp-oauth.service.ts +445 -0
  313. package/src/core/modules/ai/services/core-ai-mcp.service.ts +94 -0
  314. package/src/core/modules/ai/services/core-ai-mode.service.ts +43 -0
  315. package/src/core/modules/ai/services/core-ai-placeholder.registry.ts +123 -0
  316. package/src/core/modules/ai/services/core-ai-prompt-builder.service.ts +228 -0
  317. package/src/core/modules/ai/services/core-ai-prompt-hint.service.ts +133 -0
  318. package/src/core/modules/ai/services/core-ai-prompt.service.ts +164 -0
  319. package/src/core/modules/ai/services/core-ai-slot.service.ts +526 -0
  320. package/src/core/modules/ai/services/core-ai-tool-grant.service.ts +107 -0
  321. package/src/core/modules/ai/services/core-ai-tool-policy.service.ts +124 -0
  322. package/src/core/modules/ai/services/core-ai.service.ts +1396 -0
  323. package/src/core/modules/ai/tools/ai-tool.base.ts +46 -0
  324. package/src/core/modules/ai/tools/ai-tool.registry.ts +124 -0
  325. package/src/core/modules/ai/tools/ask-user-question.tool.ts +76 -0
  326. package/src/core/modules/ai/tools/search-tools.tool.ts +70 -0
  327. package/src/core/modules/error-code/error-codes.ts +86 -0
  328. package/src/core.module.ts +12 -0
  329. package/src/index.ts +6 -0
  330. package/src/server/modules/ai/ai-tools.module.ts +33 -0
  331. package/src/server/modules/ai/tools/delete-user.tool.ts +44 -0
  332. package/src/server/modules/ai/tools/find-users.tool.ts +49 -0
  333. package/src/server/modules/ai/tools/get-user.tool.ts +43 -0
  334. package/src/server/modules/ai/tools/update-user-job-title.tool.ts +62 -0
  335. package/src/server/server.module.ts +5 -0
@@ -0,0 +1,46 @@
1
+ import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
2
+
3
+ import { RoleEnum } from '../../../common/enums/role.enum';
4
+ import { AiToolContext, AiToolResult, IAiTool } from '../interfaces/ai-tool.interface';
5
+ import { AiToolRegistry } from './ai-tool.registry';
6
+
7
+ /**
8
+ * Convenience base class for AI tools.
9
+ *
10
+ * Extending {@link AiTool} gives a tool automatic self-registration in the
11
+ * {@link AiToolRegistry} on module init — so a project only needs to declare the
12
+ * class as a provider in its module. Implement {@link name}, {@link description},
13
+ * {@link parameters}, {@link roles} and {@link execute}.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * @Injectable()
18
+ * export class FindUsersAiTool extends AiTool {
19
+ * readonly name = 'find_users';
20
+ * readonly description = 'Search users by email or name.';
21
+ * readonly parameters = { properties: { query: { type: 'string' } }, type: 'object' };
22
+ * readonly roles = [RoleEnum.ADMIN];
23
+ * constructor(registry: AiToolRegistry, private readonly userService: UserService) { super(registry); }
24
+ * async execute(args, context) {
25
+ * return this.userService.find({ filterQuery: { ... } }, context.serviceOptions);
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ @Injectable()
31
+ export abstract class AiTool implements IAiTool, OnModuleInit {
32
+ protected readonly logger = new Logger(this.constructor.name);
33
+
34
+ abstract readonly description: string;
35
+ abstract readonly name: string;
36
+ abstract readonly parameters: Record<string, any>;
37
+ abstract readonly roles: (RoleEnum | string)[];
38
+
39
+ protected constructor(protected readonly registry: AiToolRegistry) {}
40
+
41
+ onModuleInit(): void {
42
+ this.registry.register(this);
43
+ }
44
+
45
+ abstract execute(args: Record<string, any>, context: AiToolContext): Promise<AiToolResult | unknown>;
46
+ }
@@ -0,0 +1,124 @@
1
+ import { Injectable, Logger } from '@nestjs/common';
2
+
3
+ import { RoleEnum } from '../../../common/enums/role.enum';
4
+ import { IAiTool } from '../interfaces/ai-tool.interface';
5
+
6
+ /**
7
+ * Minimal shape of the current user needed for tool-visibility checks.
8
+ */
9
+ export interface AiToolUser {
10
+ [key: string]: any;
11
+ emailVerified?: boolean;
12
+ id?: string;
13
+ roles?: string[];
14
+ verified?: boolean;
15
+ verifiedAt?: Date | string;
16
+ }
17
+
18
+ /**
19
+ * Central registry of all AI tools — the single source of truth that feeds both
20
+ * the internal agent loop ({@link CoreAiService}) and any external MCP server.
21
+ *
22
+ * The registry is a global singleton service: tools self-register (typically
23
+ * from `onModuleInit`), so projects can add or override tools from ANY module
24
+ * without running into NestJS DI-scope limitations. Registering a tool whose
25
+ * name already exists overrides the previous one (last write wins) — this is the
26
+ * supported override mechanism for projects customizing core tools.
27
+ */
28
+ @Injectable()
29
+ export class AiToolRegistry {
30
+ private readonly logger = new Logger(AiToolRegistry.name);
31
+ private readonly tools = new Map<string, IAiTool>();
32
+
33
+ /**
34
+ * Register (or override) a tool by its name.
35
+ */
36
+ register(tool: IAiTool): void {
37
+ if (this.tools.has(tool.name)) {
38
+ this.logger.warn(`AI tool "${tool.name}" is being overridden.`);
39
+ }
40
+ this.tools.set(tool.name, tool);
41
+ this.logger.debug(`Registered AI tool: ${tool.name}`);
42
+ }
43
+
44
+ /**
45
+ * Remove a tool from the registry.
46
+ */
47
+ unregister(name: string): void {
48
+ this.tools.delete(name);
49
+ }
50
+
51
+ /**
52
+ * Get a tool by name.
53
+ */
54
+ get(name: string): IAiTool | undefined {
55
+ return this.tools.get(name);
56
+ }
57
+
58
+ /**
59
+ * All registered tools (unfiltered).
60
+ */
61
+ all(): IAiTool[] {
62
+ return [...this.tools.values()];
63
+ }
64
+
65
+ /**
66
+ * Tools the given user is allowed to see and use.
67
+ *
68
+ * This is a first-line visibility filter (defense in depth) — the authoritative
69
+ * authorization happens when a tool's `execute()` routes through a CrudService
70
+ * with the user's serviceOptions.
71
+ */
72
+ forUser(user: AiToolUser | null | undefined): IAiTool[] {
73
+ return this.all().filter((tool) => this.userCanAccess(tool, user));
74
+ }
75
+
76
+ /**
77
+ * Whether a user satisfies a tool's role requirements.
78
+ */
79
+ userCanAccess(tool: IAiTool, user: AiToolUser | null | undefined): boolean {
80
+ const roles = tool.roles ?? [];
81
+
82
+ // Locked tools are never accessible.
83
+ if (roles.includes(RoleEnum.S_NO_ONE)) {
84
+ return false;
85
+ }
86
+
87
+ // Public tools are always accessible.
88
+ if (roles.includes(RoleEnum.S_EVERYONE)) {
89
+ return true;
90
+ }
91
+
92
+ // Everything below requires an authenticated user.
93
+ if (!user?.id) {
94
+ return false;
95
+ }
96
+
97
+ // Admin bypass.
98
+ if (user.roles?.includes(RoleEnum.ADMIN)) {
99
+ return true;
100
+ }
101
+
102
+ // Any authenticated user.
103
+ if (roles.includes(RoleEnum.S_USER)) {
104
+ return true;
105
+ }
106
+
107
+ // Verified users.
108
+ if (roles.includes(RoleEnum.S_VERIFIED) && (user.verified || user.verifiedAt || user.emailVerified)) {
109
+ return true;
110
+ }
111
+
112
+ // Real roles: user must hold at least one of the required roles.
113
+ const systemRoles: string[] = [
114
+ RoleEnum.S_CREATOR,
115
+ RoleEnum.S_EVERYONE,
116
+ RoleEnum.S_NO_ONE,
117
+ RoleEnum.S_SELF,
118
+ RoleEnum.S_USER,
119
+ RoleEnum.S_VERIFIED,
120
+ ];
121
+ const requiredRealRoles = roles.filter((r) => !systemRoles.includes(r));
122
+ return requiredRealRoles.some((r) => user.roles?.includes(r));
123
+ }
124
+ }
@@ -0,0 +1,76 @@
1
+ import { Injectable } from '@nestjs/common';
2
+
3
+ import { RoleEnum } from '../../../common/enums/role.enum';
4
+ import { AiToolContext, AiToolResult } from '../interfaces/ai-tool.interface';
5
+ import { AiTool } from './ai-tool.base';
6
+ import { AiToolRegistry } from './ai-tool.registry';
7
+
8
+ /**
9
+ * Sentinel marker the orchestrator detects to short-circuit the loop with a
10
+ * `pendingQuestion`. Tools never set this directly — only this built-in does.
11
+ */
12
+ export const ASK_USER_QUESTION_SENTINEL = Symbol.for('@lenne.tech/nest-server/ai/ask-user-question');
13
+
14
+ /**
15
+ * Built-in tool the model can call to pause the run and ask the end user for a
16
+ * clarification (e.g. "did you mean A, B or C?") instead of guessing — important
17
+ * because our target users are NOT prompt experts: a focused question often beats
18
+ * a wrong action. The tool does not perform an action; the orchestrator detects the
19
+ * sentinel return shape, breaks the loop, and surfaces the question on `CoreAiResponse.pendingQuestion`.
20
+ *
21
+ * After answering, the client sends the user's answer as the next prompt — no
22
+ * special "answer" field is needed; the conversation continues naturally.
23
+ *
24
+ * Roles: `S_USER` (anyone can be asked). Not `mutating`/`destructive` (it is a
25
+ * read-only interaction surface).
26
+ */
27
+ @Injectable()
28
+ export class AskUserQuestionAiTool extends AiTool {
29
+ readonly description =
30
+ 'Ask the end user a clarifying question before acting. Use ONLY when you genuinely cannot proceed without more information from the user; otherwise prefer using a tool to find the answer.';
31
+ readonly name = 'ask_user_question';
32
+ readonly parameters = {
33
+ properties: {
34
+ options: {
35
+ description: 'Optional list of suggested answers ({label, value}) for a multiple-choice question.',
36
+ items: {
37
+ properties: {
38
+ label: { type: 'string' },
39
+ value: { type: 'string' },
40
+ },
41
+ required: ['label', 'value'],
42
+ type: 'object',
43
+ },
44
+ type: 'array',
45
+ },
46
+ question: { description: 'The clarifying question to ask the user, in their language.', type: 'string' },
47
+ },
48
+ required: ['question'],
49
+ type: 'object',
50
+ };
51
+ readonly roles = [RoleEnum.S_USER];
52
+
53
+ // Re-declares the constructor publicly so NestJS DI can instantiate this provider
54
+ // (AiTool's constructor is `protected`).
55
+ // eslint-disable-next-line no-useless-constructor
56
+ constructor(registry: AiToolRegistry) {
57
+ super(registry);
58
+ }
59
+
60
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
61
+ async execute(args: Record<string, any>, _context: AiToolContext): Promise<AiToolResult> {
62
+ const question = typeof args?.question === 'string' ? args.question.trim() : '';
63
+ if (!question) {
64
+ return { message: 'question is required.', success: false };
65
+ }
66
+ const options = Array.isArray(args?.options)
67
+ ? args.options
68
+ .filter((o: any) => o && typeof o.value === 'string' && typeof o.label === 'string')
69
+ .map((o: any) => ({ label: o.label, value: o.value }))
70
+ : undefined;
71
+ return {
72
+ data: { [ASK_USER_QUESTION_SENTINEL as unknown as string]: true, options, question },
73
+ success: true,
74
+ };
75
+ }
76
+ }
@@ -0,0 +1,70 @@
1
+ import { Injectable } from '@nestjs/common';
2
+
3
+ import { RoleEnum } from '../../../common/enums/role.enum';
4
+ import { AiToolContext, AiToolResult } from '../interfaces/ai-tool.interface';
5
+ import { AiTool } from './ai-tool.base';
6
+ import { AiToolRegistry } from './ai-tool.registry';
7
+
8
+ /**
9
+ * Built-in **meta-tool** for the "deferred tool-schemas" mode
10
+ * (`ai.deferToolSchemas: true`): the system prompt then contains only the tool
11
+ * NAMES + short descriptions, not the full JSON-Schemas — which can drastically
12
+ * cut the context cost when many tools are registered. The LLM uses this tool to
13
+ * fetch the parameter schema for a specific tool BEFORE calling it.
14
+ *
15
+ * The result respects the caller's role visibility — only tools the user can use
16
+ * are returned (defense in depth on top of the orchestrator's role filter).
17
+ */
18
+ @Injectable()
19
+ export class SearchToolsAiTool extends AiTool {
20
+ readonly description =
21
+ 'Search the available backend tools and fetch the parameter schema of a specific tool. Use this before calling a tool whose parameter schema you have not yet seen.';
22
+ readonly name = 'search_tools';
23
+ readonly parameters = {
24
+ properties: {
25
+ name: { description: 'The exact tool name to fetch the full schema for.', type: 'string' },
26
+ query: {
27
+ description: 'Optional substring matched against tool name and description (returns a short list).',
28
+ type: 'string',
29
+ },
30
+ },
31
+ type: 'object',
32
+ };
33
+ readonly roles = [RoleEnum.S_USER];
34
+
35
+ // Re-declares the constructor publicly so NestJS DI can instantiate this provider
36
+ // (AiTool's constructor is `protected`).
37
+ // eslint-disable-next-line no-useless-constructor
38
+ constructor(registry: AiToolRegistry) {
39
+ super(registry);
40
+ }
41
+
42
+ async execute(args: Record<string, any>, context: AiToolContext): Promise<AiToolResult> {
43
+ const allowed = this.registry.forUser(context.currentUser);
44
+ const query = typeof args?.query === 'string' ? args.query.trim().toLowerCase() : '';
45
+ const name = typeof args?.name === 'string' ? args.name.trim() : '';
46
+ if (name) {
47
+ const tool = allowed.find((t) => t.name === name);
48
+ if (!tool) {
49
+ return { message: `Tool "${name}" is not available to you.`, success: false };
50
+ }
51
+ return {
52
+ data: {
53
+ description: tool.description,
54
+ mutating: !!tool.mutating,
55
+ destructive: !!tool.destructive,
56
+ name: tool.name,
57
+ parameters: tool.parameters,
58
+ },
59
+ success: true,
60
+ };
61
+ }
62
+ const matches = query
63
+ ? allowed.filter((t) => t.name.toLowerCase().includes(query) || t.description.toLowerCase().includes(query))
64
+ : allowed;
65
+ return {
66
+ data: matches.map((t) => ({ description: t.description, name: t.name })),
67
+ success: true,
68
+ };
69
+ }
70
+ }
@@ -21,6 +21,7 @@
21
21
  * - LTNS_0300-LTNS_0399: Validation errors
22
22
  * - LTNS_0400-LTNS_0499: Resource errors
23
23
  * - LTNS_0500-LTNS_0599: File errors
24
+ * - LTNS_0600-LTNS_0699: AI errors
24
25
  * - LTNS_0900-LTNS_0999: Internal errors
25
26
  *
26
27
  * @example
@@ -493,6 +494,91 @@ export const LtnsErrors = {
493
494
  en: 'System setup requires BetterAuth.',
494
495
  },
495
496
  },
497
+
498
+ // =====================================================
499
+ // AI Errors (LTNS_0600-LTNS_0699)
500
+ // =====================================================
501
+
502
+ AI_NO_CONNECTION: {
503
+ code: 'LTNS_0600',
504
+ message: 'No usable AI connection configured',
505
+ translations: {
506
+ de: 'Es ist keine nutzbare KI-Verbindung konfiguriert.',
507
+ en: 'No usable AI connection is configured.',
508
+ },
509
+ },
510
+
511
+ AI_CONNECTION_DISABLED: {
512
+ code: 'LTNS_0601',
513
+ message: 'AI connection is disabled',
514
+ translations: {
515
+ de: 'Die KI-Verbindung ist deaktiviert.',
516
+ en: 'The AI connection is disabled.',
517
+ },
518
+ },
519
+
520
+ AI_CONNECTION_NOT_AVAILABLE: {
521
+ code: 'LTNS_0602',
522
+ message: 'The selected AI connection is not available',
523
+ translations: {
524
+ de: 'Die ausgewählte KI-Verbindung ist nicht verfügbar.',
525
+ en: 'The selected AI connection is not available.',
526
+ },
527
+ },
528
+
529
+ AI_CONNECTION_NOT_FOUND: {
530
+ code: 'LTNS_0603',
531
+ message: 'AI connection does not exist or is not usable',
532
+ translations: {
533
+ de: 'Die KI-Verbindung existiert nicht oder ist nicht nutzbar.',
534
+ en: 'The AI connection does not exist or is not usable.',
535
+ },
536
+ },
537
+
538
+ AI_PREFERENCES_UNAVAILABLE: {
539
+ code: 'LTNS_0604',
540
+ message: 'AI connection preferences are not available',
541
+ translations: {
542
+ de: 'KI-Verbindungseinstellungen sind nicht verfügbar.',
543
+ en: 'AI connection preferences are not available.',
544
+ },
545
+ },
546
+
547
+ AI_BUDGET_EXCEEDED: {
548
+ code: 'LTNS_0605',
549
+ message: 'AI token budget for this period is exhausted',
550
+ translations: {
551
+ de: 'Dein KI-Token-Kontingent für diesen Zeitraum ist aufgebraucht. Bitte versuche es nach dem nächsten Reset erneut.',
552
+ en: 'Your AI token budget for this period is exhausted. Please try again after the next reset.',
553
+ },
554
+ },
555
+
556
+ AI_RATE_LIMITED: {
557
+ code: 'LTNS_0606',
558
+ message: 'Too many AI requests',
559
+ translations: {
560
+ de: 'Zu viele KI-Anfragen. Bitte etwas langsamer.',
561
+ en: 'Too many AI requests, please slow down.',
562
+ },
563
+ },
564
+
565
+ AI_PROVIDER_ERROR: {
566
+ code: 'LTNS_0607',
567
+ message: 'The AI provider request failed',
568
+ translations: {
569
+ de: 'Die Anfrage an den KI-Anbieter ist fehlgeschlagen.',
570
+ en: 'The AI provider request failed.',
571
+ },
572
+ },
573
+
574
+ AI_CONNECTION_INVALID_URL: {
575
+ code: 'LTNS_0608',
576
+ message: 'AI connection has no valid base URL',
577
+ translations: {
578
+ de: 'Die KI-Verbindung hat keine gültige Basis-URL.',
579
+ en: 'The AI connection has no valid base URL.',
580
+ },
581
+ },
496
582
  } as const satisfies IErrorRegistry;
497
583
 
498
584
  // =====================================================
@@ -37,6 +37,7 @@ import { EmailService } from './core/common/services/email.service';
37
37
  import { MailjetService } from './core/common/services/mailjet.service';
38
38
  import { ModelDocService } from './core/common/services/model-doc.service';
39
39
  import { TemplateService } from './core/common/services/template.service';
40
+ import { CoreAiModule } from './core/modules/ai/core-ai.module';
40
41
  import { CoreBetterAuthUserMapper } from './core/modules/better-auth/core-better-auth-user.mapper';
41
42
  import { CoreBetterAuthModule } from './core/modules/better-auth/core-better-auth.module';
42
43
  import { CoreBetterAuthService } from './core/modules/better-auth/core-better-auth.service';
@@ -443,6 +444,17 @@ export class CoreModule implements NestModule {
443
444
  imports.push(CoreSystemSetupModule);
444
445
  }
445
446
 
447
+ // Add CoreAiModule when an `ai` config block is present (presence implies enabled)
448
+ const aiConfig = config.ai;
449
+ const isAiEnabled =
450
+ aiConfig === true || (typeof aiConfig === 'object' && aiConfig !== null && aiConfig.enabled !== false);
451
+ if (isAiEnabled) {
452
+ const aiObj = typeof aiConfig === 'object' && aiConfig !== null ? aiConfig : undefined;
453
+ const mcpEnabled =
454
+ aiObj?.mcp === true || (typeof aiObj?.mcp === 'object' && aiObj.mcp !== null && aiObj.mcp.enabled !== false);
455
+ imports.push(CoreAiModule.forRoot({ ...overrides?.ai, mcpEnabled }));
456
+ }
457
+
446
458
  // Add CoreTenantModule when multiTenancy is configured (presence implies enabled)
447
459
  if (config.multiTenancy && config.multiTenancy.enabled !== false) {
448
460
  // Auto-add TenantMember to excludeSchemas (membership is tenant-spanning)
package/src/index.ts CHANGED
@@ -140,6 +140,12 @@ export * from './core/modules/auth/strategies/jwt-refresh.strategy';
140
140
  export * from './core/modules/auth/strategies/jwt.strategy';
141
141
  export * from './core/modules/auth/tokens.decorator';
142
142
 
143
+ // =====================================================================================================================
144
+ // Core - Modules - AI
145
+ // =====================================================================================================================
146
+
147
+ export * from './core/modules/ai';
148
+
143
149
  // =====================================================================================================================
144
150
  // Core - Modules - BetterAuth
145
151
  // =====================================================================================================================
@@ -0,0 +1,33 @@
1
+ import { Module } from '@nestjs/common';
2
+
3
+ import { ClaudeCliProvider } from '../../../core/modules/ai/providers/claude-cli.provider';
4
+ import { LlmProviderFactory } from '../../../core/modules/ai/providers/llm-provider.factory';
5
+ import { UserModule } from '../user/user.module';
6
+ import { DeleteUserAiTool } from './tools/delete-user.tool';
7
+ import { FindUsersAiTool } from './tools/find-users.tool';
8
+ import { GetUserAiTool } from './tools/get-user.tool';
9
+ import { UpdateUserJobTitleAiTool } from './tools/update-user-job-title.tool';
10
+
11
+ /**
12
+ * Registers the example AI tools.
13
+ *
14
+ * Each tool extends `AiTool` and self-registers in the global `AiToolRegistry`
15
+ * on module init. The registry is provided globally by `CoreAiModule` (auto-
16
+ * registered by `CoreModule` when an `ai` config block is present), so no extra
17
+ * import is needed for it here — only `UserModule` for `UserService`.
18
+ *
19
+ * This is the reference implementation showing how a project adds (or overrides)
20
+ * AI tools from its own module. It also demonstrates the opt-in registration of
21
+ * the Claude Code CLI provider on the global {@link LlmProviderFactory}, so a
22
+ * connection with `providerType: 'claude-cli'` can be used.
23
+ */
24
+ @Module({
25
+ imports: [UserModule],
26
+ providers: [DeleteUserAiTool, FindUsersAiTool, GetUserAiTool, UpdateUserJobTitleAiTool],
27
+ })
28
+ export class AiToolsModule {
29
+ constructor(protected readonly providerFactory: LlmProviderFactory) {
30
+ // Opt-in: enable connections with providerType 'claude-cli' (Claude Code CLI backend).
31
+ this.providerFactory.registerBuilder('claude-cli', (connection) => new ClaudeCliProvider(connection));
32
+ }
33
+ }
@@ -0,0 +1,44 @@
1
+ import { Injectable } from '@nestjs/common';
2
+
3
+ import { AiToolContext } from '../../../../core/modules/ai/interfaces/ai-tool.interface';
4
+ import { AiTool } from '../../../../core/modules/ai/tools/ai-tool.base';
5
+ import { AiToolRegistry } from '../../../../core/modules/ai/tools/ai-tool.registry';
6
+ import { RoleEnum } from '../../../../core/common/enums/role.enum';
7
+ import { UserService } from '../../user/user.service';
8
+
9
+ /**
10
+ * Example destructive AI tool: delete a user (admin only).
11
+ *
12
+ * Marked `destructive: true`, so the orchestrator returns a `requiresConfirmation`
13
+ * response listing this action instead of executing it — the prompt must be
14
+ * re-sent with `confirm: true` to actually delete.
15
+ */
16
+ @Injectable()
17
+ export class DeleteUserAiTool extends AiTool {
18
+ readonly description = 'Delete a user identified by id. Destructive — requires confirmation. Admin only.';
19
+ readonly destructive = true;
20
+ readonly name = 'delete_user';
21
+ readonly parameters = {
22
+ properties: {
23
+ id: { description: 'The user id', type: 'string' },
24
+ },
25
+ required: ['id'],
26
+ type: 'object',
27
+ };
28
+ readonly roles = [RoleEnum.ADMIN];
29
+
30
+ constructor(
31
+ registry: AiToolRegistry,
32
+ private readonly userService: UserService,
33
+ ) {
34
+ super(registry);
35
+ }
36
+
37
+ async execute(args: Record<string, any>, context: AiToolContext) {
38
+ const deleted = await this.userService.delete(String(args.id), {
39
+ ...context.serviceOptions,
40
+ roles: [RoleEnum.ADMIN],
41
+ });
42
+ return { data: { id: deleted?.id }, message: 'User deleted', success: true };
43
+ }
44
+ }
@@ -0,0 +1,49 @@
1
+ import { Injectable } from '@nestjs/common';
2
+
3
+ import { AiToolContext } from '../../../../core/modules/ai/interfaces/ai-tool.interface';
4
+ import { AiTool } from '../../../../core/modules/ai/tools/ai-tool.base';
5
+ import { AiToolRegistry } from '../../../../core/modules/ai/tools/ai-tool.registry';
6
+ import { RoleEnum } from '../../../../core/common/enums/role.enum';
7
+ import { UserService } from '../../user/user.service';
8
+
9
+ /**
10
+ * Example AI tool: search users (admin only).
11
+ *
12
+ * Demonstrates the secure pattern: the tool routes through `UserService.find`
13
+ * with the caller's `serviceOptions`, so `@Restricted` field filtering and
14
+ * `securityCheck()` still apply — the LLM never receives data the user may not see.
15
+ */
16
+ @Injectable()
17
+ export class FindUsersAiTool extends AiTool {
18
+ readonly description =
19
+ 'Search users by email or username and return the matches. Use for questions like ' +
20
+ '"how many users are there", "find the user with email X" or "list recent users". Admin only.';
21
+ readonly name = 'find_users';
22
+ readonly parameters = {
23
+ properties: {
24
+ limit: { description: 'Maximum number of results (default 20, max 100)', type: 'number' },
25
+ search: { description: 'Search term matched against email and username', type: 'string' },
26
+ },
27
+ type: 'object',
28
+ };
29
+ readonly roles = [RoleEnum.ADMIN];
30
+
31
+ constructor(
32
+ registry: AiToolRegistry,
33
+ private readonly userService: UserService,
34
+ ) {
35
+ super(registry);
36
+ }
37
+
38
+ async execute(args: Record<string, any>, context: AiToolContext) {
39
+ const search = typeof args.search === 'string' ? args.search.trim() : '';
40
+ const limit = Math.min(Math.max(Number(args.limit) || 20, 1), 100);
41
+ const escaped = search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
42
+ const filterQuery = search
43
+ ? { $or: [{ email: { $options: 'i', $regex: escaped } }, { username: { $options: 'i', $regex: escaped } }] }
44
+ : {};
45
+
46
+ const users = await this.userService.find({ filterQuery, queryOptions: { limit } }, context.serviceOptions);
47
+ return { data: users, message: `Found ${users.length} user(s)`, success: true };
48
+ }
49
+ }
@@ -0,0 +1,43 @@
1
+ import { Injectable } from '@nestjs/common';
2
+
3
+ import { AiToolContext } from '../../../../core/modules/ai/interfaces/ai-tool.interface';
4
+ import { AiTool } from '../../../../core/modules/ai/tools/ai-tool.base';
5
+ import { AiToolRegistry } from '../../../../core/modules/ai/tools/ai-tool.registry';
6
+ import { RoleEnum } from '../../../../core/common/enums/role.enum';
7
+ import { UserService } from '../../user/user.service';
8
+
9
+ /**
10
+ * Example AI tool: get a single user by id (any authenticated user).
11
+ *
12
+ * Authorization is enforced by passing `roles: [ADMIN, S_CREATOR, S_SELF]` to the
13
+ * service — regular users only get their own record, admins get any.
14
+ */
15
+ @Injectable()
16
+ export class GetUserAiTool extends AiTool {
17
+ readonly description =
18
+ 'Get a single user by id. Regular users can only access their own data; admins can access any user.';
19
+ readonly name = 'get_user';
20
+ readonly parameters = {
21
+ properties: {
22
+ id: { description: 'The user id', type: 'string' },
23
+ },
24
+ required: ['id'],
25
+ type: 'object',
26
+ };
27
+ readonly roles = [RoleEnum.S_USER];
28
+
29
+ constructor(
30
+ registry: AiToolRegistry,
31
+ private readonly userService: UserService,
32
+ ) {
33
+ super(registry);
34
+ }
35
+
36
+ async execute(args: Record<string, any>, context: AiToolContext) {
37
+ const user = await this.userService.get(String(args.id), {
38
+ ...context.serviceOptions,
39
+ roles: [RoleEnum.ADMIN, RoleEnum.S_CREATOR, RoleEnum.S_SELF],
40
+ });
41
+ return { data: user, success: true };
42
+ }
43
+ }