@lenne.tech/nest-server 11.25.5 → 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 (341) 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/better-auth/core-better-auth-user.mapper.js +7 -1
  222. package/dist/core/modules/better-auth/core-better-auth-user.mapper.js.map +1 -1
  223. package/dist/core/modules/error-code/error-codes.d.ts +81 -0
  224. package/dist/core/modules/error-code/error-codes.js +72 -0
  225. package/dist/core/modules/error-code/error-codes.js.map +1 -1
  226. package/dist/core/modules/tenant/core-tenant.service.js +3 -3
  227. package/dist/core/modules/tenant/core-tenant.service.js.map +1 -1
  228. package/dist/core.module.js +8 -0
  229. package/dist/core.module.js.map +1 -1
  230. package/dist/index.d.ts +1 -0
  231. package/dist/index.js +1 -0
  232. package/dist/index.js.map +1 -1
  233. package/dist/server/modules/ai/ai-tools.module.d.ts +5 -0
  234. package/dist/server/modules/ai/ai-tools.module.js +36 -0
  235. package/dist/server/modules/ai/ai-tools.module.js.map +1 -0
  236. package/dist/server/modules/ai/tools/delete-user.tool.d.ts +30 -0
  237. package/dist/server/modules/ai/tools/delete-user.tool.js +49 -0
  238. package/dist/server/modules/ai/tools/delete-user.tool.js.map +1 -0
  239. package/dist/server/modules/ai/tools/find-users.tool.d.ts +30 -0
  240. package/dist/server/modules/ai/tools/find-users.tool.js +52 -0
  241. package/dist/server/modules/ai/tools/find-users.tool.js.map +1 -0
  242. package/dist/server/modules/ai/tools/get-user.tool.d.ts +26 -0
  243. package/dist/server/modules/ai/tools/get-user.tool.js +48 -0
  244. package/dist/server/modules/ai/tools/get-user.tool.js.map +1 -0
  245. package/dist/server/modules/ai/tools/update-user-job-title.tool.d.ts +32 -0
  246. package/dist/server/modules/ai/tools/update-user-job-title.tool.js +54 -0
  247. package/dist/server/modules/ai/tools/update-user-job-title.tool.js.map +1 -0
  248. package/dist/server/modules/error-code/error-codes.d.ts +9 -0
  249. package/dist/server/server.module.js +2 -0
  250. package/dist/server/server.module.js.map +1 -1
  251. package/dist/tsconfig.build.tsbuildinfo +1 -1
  252. package/docs/REQUEST-LIFECYCLE.md +10 -0
  253. package/migration-guides/11.25.x-to-11.26.0.md +278 -0
  254. package/package.json +44 -26
  255. package/src/config.env.ts +48 -1
  256. package/src/core/common/helpers/config.helper.ts +5 -2
  257. package/src/core/common/interceptors/check-security.interceptor.ts +13 -3
  258. package/src/core/common/interfaces/server-options.interface.ts +312 -0
  259. package/src/core/modules/ai/INTEGRATION-CHECKLIST.md +220 -0
  260. package/src/core/modules/ai/README.md +526 -0
  261. package/src/core/modules/ai/core-ai-mcp.controller.ts +223 -0
  262. package/src/core/modules/ai/core-ai.controller.ts +547 -0
  263. package/src/core/modules/ai/core-ai.module.ts +269 -0
  264. package/src/core/modules/ai/core-ai.resolver.ts +526 -0
  265. package/src/core/modules/ai/helpers/ai-mcp-oauth.helper.ts +35 -0
  266. package/src/core/modules/ai/hooks/ai-hook.base.ts +23 -0
  267. package/src/core/modules/ai/hooks/ai-hook.registry.ts +119 -0
  268. package/src/core/modules/ai/index.ts +70 -0
  269. package/src/core/modules/ai/inputs/core-ai-budget-limit-create.input.ts +27 -0
  270. package/src/core/modules/ai/inputs/core-ai-budget-limit.input.ts +52 -0
  271. package/src/core/modules/ai/inputs/core-ai-connection-create.input.ts +32 -0
  272. package/src/core/modules/ai/inputs/core-ai-connection-preference.input.ts +42 -0
  273. package/src/core/modules/ai/inputs/core-ai-connection.input.ts +235 -0
  274. package/src/core/modules/ai/inputs/core-ai-conversation-create.input.ts +13 -0
  275. package/src/core/modules/ai/inputs/core-ai-conversation.input.ts +33 -0
  276. package/src/core/modules/ai/inputs/core-ai-prompt-create.input.ts +50 -0
  277. package/src/core/modules/ai/inputs/core-ai-prompt-hint-create.input.ts +26 -0
  278. package/src/core/modules/ai/inputs/core-ai-prompt-hint.input.ts +43 -0
  279. package/src/core/modules/ai/inputs/core-ai-prompt-update.input.ts +42 -0
  280. package/src/core/modules/ai/inputs/core-ai-prompt.input.ts +144 -0
  281. package/src/core/modules/ai/inputs/core-ai-slot-create.input.ts +26 -0
  282. package/src/core/modules/ai/inputs/core-ai-slot-update.input.ts +65 -0
  283. package/src/core/modules/ai/interfaces/ai-hook.interface.ts +74 -0
  284. package/src/core/modules/ai/interfaces/ai-placeholder.interface.ts +49 -0
  285. package/src/core/modules/ai/interfaces/ai-tool.interface.ts +112 -0
  286. package/src/core/modules/ai/interfaces/llm-provider.interface.ts +187 -0
  287. package/src/core/modules/ai/interfaces/resolved-ai-connection.interface.ts +55 -0
  288. package/src/core/modules/ai/models/core-ai-action.model.ts +37 -0
  289. package/src/core/modules/ai/models/core-ai-available-connection.model.ts +42 -0
  290. package/src/core/modules/ai/models/core-ai-budget-limit.model.ts +82 -0
  291. package/src/core/modules/ai/models/core-ai-connection-preference.model.ts +72 -0
  292. package/src/core/modules/ai/models/core-ai-connection.model.ts +330 -0
  293. package/src/core/modules/ai/models/core-ai-conversation.model.ts +87 -0
  294. package/src/core/modules/ai/models/core-ai-interaction.model.ts +145 -0
  295. package/src/core/modules/ai/models/core-ai-message.model.ts +29 -0
  296. package/src/core/modules/ai/models/core-ai-mode.model.ts +93 -0
  297. package/src/core/modules/ai/models/core-ai-prompt-hint.model.ts +98 -0
  298. package/src/core/modules/ai/models/core-ai-prompt.model.ts +120 -0
  299. package/src/core/modules/ai/models/core-ai-response.model.ts +119 -0
  300. package/src/core/modules/ai/models/core-ai-slot.model.ts +146 -0
  301. package/src/core/modules/ai/models/core-ai-tool-grant.model.ts +87 -0
  302. package/src/core/modules/ai/models/core-ai-tool-policy.model.ts +115 -0
  303. package/src/core/modules/ai/models/core-ai-usage-info.model.ts +107 -0
  304. package/src/core/modules/ai/models/core-ai-usage.model.ts +29 -0
  305. package/src/core/modules/ai/providers/claude-cli.provider.ts +240 -0
  306. package/src/core/modules/ai/providers/llm-provider.factory.ts +63 -0
  307. package/src/core/modules/ai/providers/openai-compatible.provider.ts +329 -0
  308. package/src/core/modules/ai/services/ai-crypto.service.ts +120 -0
  309. package/src/core/modules/ai/services/core-ai-budget.service.ts +297 -0
  310. package/src/core/modules/ai/services/core-ai-connection-preference.service.ts +74 -0
  311. package/src/core/modules/ai/services/core-ai-connection-resolver.service.ts +308 -0
  312. package/src/core/modules/ai/services/core-ai-connection.service.ts +387 -0
  313. package/src/core/modules/ai/services/core-ai-conversation.service.ts +105 -0
  314. package/src/core/modules/ai/services/core-ai-interaction.service.ts +56 -0
  315. package/src/core/modules/ai/services/core-ai-mcp-client.service.ts +169 -0
  316. package/src/core/modules/ai/services/core-ai-mcp-oauth.service.ts +445 -0
  317. package/src/core/modules/ai/services/core-ai-mcp.service.ts +94 -0
  318. package/src/core/modules/ai/services/core-ai-mode.service.ts +43 -0
  319. package/src/core/modules/ai/services/core-ai-placeholder.registry.ts +123 -0
  320. package/src/core/modules/ai/services/core-ai-prompt-builder.service.ts +228 -0
  321. package/src/core/modules/ai/services/core-ai-prompt-hint.service.ts +133 -0
  322. package/src/core/modules/ai/services/core-ai-prompt.service.ts +164 -0
  323. package/src/core/modules/ai/services/core-ai-slot.service.ts +526 -0
  324. package/src/core/modules/ai/services/core-ai-tool-grant.service.ts +107 -0
  325. package/src/core/modules/ai/services/core-ai-tool-policy.service.ts +124 -0
  326. package/src/core/modules/ai/services/core-ai.service.ts +1396 -0
  327. package/src/core/modules/ai/tools/ai-tool.base.ts +46 -0
  328. package/src/core/modules/ai/tools/ai-tool.registry.ts +124 -0
  329. package/src/core/modules/ai/tools/ask-user-question.tool.ts +76 -0
  330. package/src/core/modules/ai/tools/search-tools.tool.ts +70 -0
  331. package/src/core/modules/better-auth/core-better-auth-user.mapper.ts +15 -2
  332. package/src/core/modules/error-code/error-codes.ts +86 -0
  333. package/src/core/modules/tenant/core-tenant.service.ts +3 -3
  334. package/src/core.module.ts +12 -0
  335. package/src/index.ts +6 -0
  336. package/src/server/modules/ai/ai-tools.module.ts +33 -0
  337. package/src/server/modules/ai/tools/delete-user.tool.ts +44 -0
  338. package/src/server/modules/ai/tools/find-users.tool.ts +49 -0
  339. package/src/server/modules/ai/tools/get-user.tool.ts +43 -0
  340. package/src/server/modules/ai/tools/update-user-job-title.tool.ts +62 -0
  341. package/src/server/server.module.ts +5 -0
@@ -0,0 +1,526 @@
1
+ # AI Module
2
+
3
+ An extensible AI-assistant layer for `@lenne.tech/nest-server`. Users send prompts
4
+ from the frontend; the API optimizes/enriches the prompt, talks to a configurable
5
+ LLM (local or external), and lets the model call **backend tools** to read and
6
+ manipulate data — MCP-style — before returning a structured answer.
7
+
8
+ ## Highlights
9
+
10
+ - **Database-backed LLM connections** (`CoreAiConnection`) — endpoints, models and
11
+ **encrypted API keys** are managed at runtime (admin CRUD; later a frontend
12
+ settings area), not in `config.env.ts`.
13
+ - **Vendor-agnostic provider abstraction** (`ILlmProvider`) — swap local runtimes
14
+ and hosted backends via config. Ships with a dependency-free `fetch`-based
15
+ provider for the OpenAI-compatible chat API (a de-facto standard, not a vendor).
16
+ - **Capability gradations** — each backend declares its `capabilities`
17
+ (`nativeTools`, `jsonResponse`, `systemPrompt`); the orchestrator compensates
18
+ across the whole spectrum (full native → none = emulated). No backend is special-cased.
19
+ - **Tool registry** (`AiToolRegistry`) — a global registry of backend capabilities
20
+ the LLM may call. Tools self-register from any module and are filtered by the
21
+ caller's roles.
22
+ - **Emulated tool calling** — for backends without native function calling, the
23
+ tool catalog is injected into the system prompt and tool calls are parsed from
24
+ the model's JSON output. Native tool calling is used transparently when supported.
25
+ - **Security first** — tools execute through `CrudService` with the caller's
26
+ permissions, so `@Restricted`, `securityCheck()` and tenant context all apply.
27
+ The LLM can never escalate beyond what the user is allowed to do.
28
+
29
+ ## Architecture
30
+
31
+ ```
32
+ Frontend prompt
33
+ │ aiPrompt (GraphQL) / POST /ai/prompt (REST) @Roles(S_USER)
34
+
35
+ CoreAiService (orchestrator / agent loop)
36
+ │ 1. rate-limit → resolve connection (DB) → build provider
37
+ │ 2. filter tools by the user's roles
38
+ │ 3. loop: LLM call → execute tool calls (CrudService, user perms) → feed back
39
+
40
+ ILlmProvider ── OpenAiCompatibleProvider (any OpenAI-compatible endpoint) / custom providers
41
+
42
+ CoreAiResponse { text, actions[], data, usage, iterations }
43
+ ```
44
+
45
+ ## Configuration
46
+
47
+ Presence of the `ai` block enables the module (omit to disable, `{ enabled: false }`
48
+ to keep config but disable). See `IAi` in `server-options.interface.ts`.
49
+
50
+ ```typescript
51
+ // config.env.ts
52
+ ai: {
53
+ encryptionSecret: process.env.NSC__AI__ENCRYPTION_SECRET, // 32+ chars in prod
54
+ maxIterations: 5,
55
+ rateLimit: { max: 20, windowSeconds: 60 },
56
+ systemPrompt: 'You are a helpful assistant for …',
57
+ contextWindow: 8192, // fallback when a connection has no detected window
58
+ maxToolResultChars: 12000, // cap a tool-results payload fed back to the model
59
+ promptLearning: { autoApply: false }, // governed self-improvement (admin approves hints)
60
+ // Optional one-time seed of a default connection (DB is the source of truth):
61
+ defaultConnection: {
62
+ name: 'Default LLM',
63
+ baseUrl: process.env.AI_BASE_URL, // any OpenAI-compatible endpoint
64
+ model: process.env.AI_MODEL,
65
+ apiKeyEnv: 'AI_API_KEY', // prefer env over an inline apiKey
66
+ supportsNativeTools: false, // set per the backend's actual support
67
+ supportsJsonResponse: false,
68
+ },
69
+ }
70
+ ```
71
+
72
+ ### Capabilities (vendor-agnostic)
73
+
74
+ The module makes no assumptions about a specific vendor. Each connection declares
75
+ what its backend supports via `supportsNativeTools` and `supportsJsonResponse`:
76
+
77
+ - Both **true** → native function calling + JSON mode are used directly.
78
+ - **false** → the module compensates: tool calling is emulated via the system
79
+ prompt and JSON is requested in-prompt and parsed defensively.
80
+ - **undefined** (omitted) → **auto-detected** by probing the endpoint (see below).
81
+
82
+ This covers the full spectrum — from feature-rich hosted models with native tools
83
+ to minimal local runtimes or gateways with no native tool/JSON support — without
84
+ naming or special-casing any provider. Add entirely different backends/protocols by
85
+ registering a builder on `LlmProviderFactory`.
86
+
87
+ > **Emulated-mode limitation (weak models):** In emulated tool calling the model
88
+ > is _asked_ (via the system prompt) to emit a `tool_calls` request before acting.
89
+ > Weaker models sometimes ignore this and reply with a natural-language "done"
90
+ > message **without** actually emitting the tool call. This is a _false positive_
91
+ > (the tool never ran — no `actions` are recorded and no confirmation gate is
92
+ > shown), **not** a security issue: a tool can only execute by going through the
93
+ > backend's `executeToolCall()` + confirmation gate, so the model can never trigger
94
+ > a real (or unconfirmed) side-effect by merely claiming success. For workflows
95
+ > that depend on reliable, action-heavy tool use, prefer a connection whose backend
96
+ > supports **native** tool calling (`supportsNativeTools: true`).
97
+
98
+ #### Capability auto-detection
99
+
100
+ Leave `supportsJsonResponse` / `supportsNativeTools` **undefined** to let the module
101
+ detect them automatically (explicit `true`/`false` is always authoritative and is
102
+ never probed). Detection runs in two complementary ways:
103
+
104
+ - **Eager (on create):** when a connection is created with an undefined flag, the
105
+ endpoint is probed once and the result is persisted. Best-effort — a probe failure
106
+ never blocks the create.
107
+ - **Lazy (on first prompt):** if a flag is still undefined at prompt time (e.g. the
108
+ eager probe was not possible, or the connection was seeded), the orchestrator probes
109
+ once, persists, and uses the result. Until then the safe emulated baseline applies.
110
+ - **On demand:** admins can re-probe via `detectAiConnectionCapabilities` /
111
+ `POST /ai/connections/:id/detect-capabilities` (e.g. after changing `baseUrl`/`model`).
112
+
113
+ The probe is provider-agnostic best effort: `response_format: json_object` is sent
114
+ (2xx → JSON supported); a trivial tool with `tool_choice: 'required'` is sent (2xx
115
+ returning a `tool_calls` result → native tools supported; a `4xx` or a silent ignore
116
+ → unsupported). Override `OpenAiCompatibleProvider.detectCapabilities()` for custom
117
+ backends, or implement the optional `ILlmProvider.detectCapabilities()` in your own provider.
118
+
119
+ ### Backend examples (external, local, CLI)
120
+
121
+ The same module connects to all of these — the only differences are the connection's
122
+ `providerType`, `baseUrl`/`model` and which provider builder is registered:
123
+
124
+ | Backend | How to connect | Tools |
125
+ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------- |
126
+ | **External** (hosted) | Built-in `openai-compatible` provider. `baseUrl` = the vendor's OpenAI-compatible endpoint, `apiKey` set. | native or emulated (auto-detected) |
127
+ | **Local** (e.g. Ollama) | Built-in `openai-compatible` provider. `baseUrl: 'http://localhost:11434/v1'`, any `apiKey` (Ollama ignores it). A tool-capable model (e.g. `qwen2.5`) supports native tools — set `supportsNativeTools: true` explicitly if the auto-probe is conservative. | native or emulated |
128
+ | **Claude Code CLI** | Opt-in `ClaudeCliProvider` (shells out to the local `claude` CLI). `providerType: 'claude-cli'`, `model` = a Claude alias (`opus`/`sonnet`/`haiku`). | emulated (the CLI is run **tool-free**) |
129
+
130
+ `openai-compatible` is registered out of the box. For the CLI, register the provider
131
+ once (e.g. in a consumer module's `onModuleInit`) — it is **not** auto-registered:
132
+
133
+ ```typescript
134
+ import { ClaudeCliProvider, LlmProviderFactory } from '@lenne.tech/nest-server';
135
+
136
+ @Module({ providers: [] })
137
+ export class AiProvidersModule implements OnModuleInit {
138
+ constructor(private readonly factory: LlmProviderFactory) {}
139
+ onModuleInit() {
140
+ this.factory.registerBuilder('claude-cli', (conn) => new ClaudeCliProvider(conn));
141
+ }
142
+ }
143
+ ```
144
+
145
+ Then create a connection with `providerType: 'claude-cli'`. The provider invokes
146
+ `claude -p --output-format json --tools "" --system-prompt <orchestrator-prompt>`:
147
+ Claude Code runs **with all of its own tools disabled**, so it cannot read files or
148
+ run shell commands — it is a pure text generator and the orchestrator emulates tool
149
+ calling and executes tools itself through `CrudService` with the caller's permissions.
150
+ `spawn` uses an argument array (no shell), the conversation is piped via stdin, and
151
+ the child runs in a temp dir so no `CLAUDE.md`/settings leak into the context. See
152
+ `ClaudeCliProvider` for the full security model and the optional `ai.claudeCli` config.
153
+
154
+ ## Connections (DB configuration)
155
+
156
+ Connections live in the `aiConnections` collection and are managed by admins via
157
+ GraphQL (`findAiConnections`, `getAiConnection`, `createAiConnection`,
158
+ `updateAiConnection`, `deleteAiConnection`) or REST (`/ai/connections*`).
159
+
160
+ - The API key is **AES-256-GCM encrypted at rest** (`AiCryptoService`) and **never
161
+ returned** — responses expose only `hasApiKey: boolean`.
162
+ - Patch semantics for `apiKey`: a value sets it, `''` clears it, omitting it leaves
163
+ it untouched.
164
+ - `resolve(id?)` returns a runtime `ResolvedAiConnection` (with the decrypted key)
165
+ for the orchestrator — system-internal, never sent to clients.
166
+ - One connection can be `isDefault`; the service keeps it unique.
167
+ - Each connection declares its capabilities (`supportsJsonResponse`,
168
+ `supportsNativeTools`) so the orchestrator compensates for backends without
169
+ native tool calling / JSON output (emulated tool calling via the system prompt).
170
+
171
+ ## Connection selection (resolution chain)
172
+
173
+ When several connections exist the system picks one via a **prioritized, fully
174
+ overridable chain** (`CoreAiConnectionResolverService`). No connections at all →
175
+ AI handling is effectively disabled (the orchestrator returns a translated
176
+ "unavailable" response). Exactly one connection → it is the implicit default.
177
+
178
+ Availability can be **restricted per tenant** (`tenantIds` on the connection; empty
179
+ = available to all). A connection's availability is the gate for the "soft" layers;
180
+ the mandatory ("hard") layers win regardless.
181
+
182
+ Resolution order (ascending priority — a later layer overrides an earlier one):
183
+
184
+ | # | Layer | Source | Type |
185
+ | --- | ------------------------- | -------------------------------------------- | ---- |
186
+ | 1 | Global default | connection `isDefault` | soft |
187
+ | 2 | Tenant default | preference `scope:'tenant'`, not enforced | soft |
188
+ | 3 | User default | preference `scope:'user'` | soft |
189
+ | 4 | Client selection | `input.connectionId` | soft |
190
+ | 5 | Tenant-enforced | preference `scope:'tenant'`, `enforced:true` | hard |
191
+ | 6 | Admin-enforced global | connection `enforced` | hard |
192
+ | 7 | Admin-enforced per tenant | connection `enforcedTenantIds` | hard |
193
+ | 8 | Code override | `serviceOptions._aiConnectionId` | hard |
194
+
195
+ - **Tenant/user defaults & tenant-enforced** live in the `aiConnectionPreferences`
196
+ collection (`CoreAiConnectionPreferenceService`), keyed uniquely by `(scope, refId)`.
197
+ - **Self-service:** users set their own default via `aiSetUserConnection` /
198
+ `POST /ai/connections/select` (validated against availability). They list what they
199
+ may use via `aiAvailableConnections` / `GET /ai/connections/available` — a
200
+ non-sensitive list flagging the currently `selected` one and whether the choice is
201
+ `locked` by a mandatory layer.
202
+ - **Admins** manage tenant/user preferences via `setAiConnectionPreference` /
203
+ `findAiConnectionPreferences` / `deleteAiConnectionPreference` (REST under
204
+ `/ai/connections/preferences`).
205
+ - **Code override** (`serviceOptions._aiConnectionId`) is the deliberate, trusted
206
+ top layer. Projects that need admin/tenant mandates to be absolute can reorder the
207
+ chain — every layer is an overridable `protected` method and `resolutionLayers()`
208
+ can be replaced wholesale (pass a subclass via `ai: { connectionResolver }`).
209
+ - **Robustness:** a selection that points to a deleted/disabled connection (e.g. an
210
+ orphaned enforced preference or a stale code override) is dropped with a `warn` log
211
+ and the chain degrades to the fallback instead of throwing mid-prompt. Admin
212
+ `setAiConnectionPreference` validates that the connection exists before saving, and
213
+ deleting a connection automatically removes preferences pointing to it.
214
+
215
+ ## Tools
216
+
217
+ > **⚠️ Read this before integrating the AI module into a project — also for AI agents
218
+ > assisting with that integration.**
219
+ >
220
+ > The AI module **does NOT have any automatic access to your domain models** (users,
221
+ > orders, files, …). This is intentional: the LLM is treated as untrusted, so it can
222
+ > only call tools that a developer has **explicitly registered**. A freshly added AI
223
+ > module on a new project will answer "I cannot find a tool to do that" for every
224
+ > domain question — that's correct behaviour, **not** a misconfiguration.
225
+ >
226
+ > **What a project must do** for the assistant to be useful:
227
+ >
228
+ > 1. For every domain operation that should be reachable from chat (read a list,
229
+ > fetch a record, create / update / delete something, kick off a workflow), write
230
+ > a small `AiTool` subclass that delegates to your existing `*Service` (NEVER
231
+ > directly to the Mongoose Model — see the security contract below).
232
+ > 2. Group them in a project module (e.g. `AiToolsModule`) and import it from
233
+ > `ServerModule.imports`.
234
+ > 3. Mark mutating tools `mutating: true` (confirmation prompt) and irreversible
235
+ > ones `destructive: true` (always confirmed regardless of admin override).
236
+ >
237
+ > The framework ships reference tools in
238
+ > `node_modules/@lenne.tech/nest-server/src/server/modules/ai/tools/` —
239
+ > **`find-users.tool.ts`**, **`get-user.tool.ts`**, **`delete-user.tool.ts`**,
240
+ > **`update-user-job-title.tool.ts`** — that demonstrate read / restricted-read /
241
+ > destructive-write / mutating-write patterns. They are **NOT auto-registered in
242
+ > consumer projects** (they live in the framework's own test fixtures and aren't
243
+ > exported); copy them to `<api>/src/server/modules/ai/tools/`, replace the relative
244
+ > `'../../../../core/...'` imports with `'@lenne.tech/nest-server'`, and register
245
+ > them via a project `AiToolsModule`.
246
+ >
247
+ > **Security contract:** every tool MUST go through a service that uses `CrudService`
248
+ >
249
+ > - `serviceOptions.currentUser`, so `@Restricted` field filtering, `securityCheck()`
250
+ > and tenant context still apply. Direct `Model.find()` etc. bypasses the framework's
251
+ > permission layer — see `.claude/rules/security-rules.md` in this repo for the
252
+ > complete rules.
253
+
254
+ A tool implements `IAiTool` (or extends the `AiTool` base class) and self-registers
255
+ in the global `AiToolRegistry`:
256
+
257
+ ```typescript
258
+ @Injectable()
259
+ export class FindUsersAiTool extends AiTool {
260
+ readonly name = 'find_users';
261
+ readonly description = 'Search users by email or username. Admin only.';
262
+ readonly parameters = { properties: { search: { type: 'string' } }, type: 'object' };
263
+ readonly roles = [RoleEnum.ADMIN];
264
+
265
+ constructor(
266
+ registry: AiToolRegistry,
267
+ private readonly userService: UserService,
268
+ ) {
269
+ super(registry);
270
+ }
271
+
272
+ async execute(args, context) {
273
+ // Routes through CrudService with the caller's serviceOptions → permissions apply.
274
+ const users = await this.userService.find(
275
+ {
276
+ filterQuery: {
277
+ /* … */
278
+ },
279
+ },
280
+ context.serviceOptions,
281
+ );
282
+ return { data: users, success: true };
283
+ }
284
+ }
285
+ ```
286
+
287
+ Declare the tool as a provider in any module (see `src/server/modules/ai/` for the
288
+ reference implementation). Registering a tool with an existing name overrides it —
289
+ the supported way to customize a core tool.
290
+
291
+ **Security rule:** always route data access through `CrudService` using
292
+ `context.serviceOptions`. Returning raw `.lean()`/aggregate data bypasses
293
+ `@Restricted` field filtering and may leak data into the LLM context.
294
+
295
+ ## Extending / overriding
296
+
297
+ Every collaborator can be replaced with a project subclass via
298
+ `CoreModule.forRoot(envConfig, { ai: { … } })`:
299
+
300
+ | Override | Base class |
301
+ | --------------------- | ----------------------------------------------------------------- |
302
+ | `service` | `CoreAiService` (orchestrator, prompt loop, rate-limit, audit) |
303
+ | `promptBuilder` | `CoreAiPromptBuilderService` (system prompt, RAG) |
304
+ | `connectionService` | `CoreAiConnectionService` |
305
+ | `connectionResolver` | `CoreAiConnectionResolverService` (resolution chain) |
306
+ | `preferenceService` | `CoreAiConnectionPreferenceService` (tenant/user preferences) |
307
+ | `budgetService` | `CoreAiBudgetService` (token/prompt budgets + usage) |
308
+ | `conversationService` | `CoreAiConversationService` (multi-turn history) |
309
+ | `interactionService` | `CoreAiInteractionService` (audit records) |
310
+ | `slotService` | `CoreAiSlotService` (admin slots — system-prompt building blocks) |
311
+ | `promptService` | `CoreAiPromptService` (user re-usable prompts / "Vorlagen") |
312
+ | `promptHintService` | `CoreAiPromptHintService` (governed learning loop) |
313
+ | `placeholderRegistry` | `CoreAiPlaceholderRegistry` (runtime `{{placeholder}}` registry) |
314
+ | `resolver` | `CoreAiResolver` (re-declare GraphQL decorators when overriding) |
315
+ | `controller` | `CoreAiController` |
316
+
317
+ Add a new LLM backend by registering a builder on `LlmProviderFactory`:
318
+
319
+ ```typescript
320
+ factory.registerBuilder('my-provider', (conn) => new MyProvider(conn));
321
+ ```
322
+
323
+ ## Conversations, audit & streaming
324
+
325
+ - **Multi-turn conversations** (`aiConversations`, owner-scoped): pass
326
+ `conversationId` to `aiPrompt` to load prior turns and append the new ones.
327
+ Manage via `createAiConversation` / `findAiConversations` / `getAiConversation`
328
+ / `deleteAiConversation`.
329
+ - **Audit** (`ai.audit: true`): every run is persisted to `aiInteractions`
330
+ (admin-readable via `findAiInteractions` / `getAiInteraction`).
331
+ - **Streaming**: `POST /ai/stream` returns Server-Sent Events (`action`, `token`,
332
+ `final`, `error`). `CoreAiService.promptStream()` powers it.
333
+
334
+ ## Plan mode (pre-flight permission validation, all-or-nothing)
335
+
336
+ Send `input.mode: 'plan'` (or set `ai.defaultMode: 'plan'`). The model first
337
+ produces a complete plan; the backend then **authorizes every step up front**
338
+ (registry role filter + each tool's optional `authorize()` data-level check). If
339
+ ANY step is not permitted, **nothing executes** and a translated (de/en) error with
340
+ `deniedActions` is returned. Otherwise all steps run in order, feeding results
341
+ forward. This is the strongest fit for "do several connected actions automatically,
342
+ but only if the user may perform all of them".
343
+
344
+ ```typescript
345
+ @Injectable()
346
+ export class TransferFundsTool extends AiTool {
347
+ readonly name = 'transfer_funds';
348
+ readonly mutating = true; // governed by the confirmation policy
349
+ readonly destructive = true; // always requires confirmation
350
+ // Pre-flight check WITHOUT mutating — decides if the user may run this:
351
+ async authorize(args, context) {
352
+ const account = await this.accountService.get(args.fromId, context.serviceOptions).catch(() => null);
353
+ return { allowed: !!account, reason: account ? undefined : 'No access to source account' };
354
+ }
355
+ async execute(args, context) {
356
+ /* … */
357
+ }
358
+ }
359
+ ```
360
+
361
+ ## Confirmation for changes
362
+
363
+ - `destructive` tools always require confirmation.
364
+ - `mutating` tools follow `ai.confirmation.mutating: { default, enforced }`: the admin
365
+ default can be overridden per request via `input.requireConfirmation` — unless the
366
+ admin set `enforced: true`.
367
+ - When confirmation is needed the response has `requiresConfirmation: true` +
368
+ `pendingActions`; re-send the prompt with `confirm: true` to proceed.
369
+
370
+ ## Client metadata & prompt enrichment
371
+
372
+ - `input.metadata` (current URL, navigation steps, console logs, …) is injected as a
373
+ clearly-delimited, size-capped, **untrusted** context block.
374
+ - The system prompt is enriched with the user's roles + available tools and optional
375
+ `ai.documentation` (override `CoreAiPromptBuilderService.getDocumentation()` for RAG).
376
+
377
+ ## Self-optimizing prompts (editable templates + governed learning)
378
+
379
+ The system prompt is assembled from **keyed fragments** by `CoreAiPromptBuilderService`,
380
+ so non-technical users only enter their domain prompt while the backend keeps the model
381
+ optimally informed (capabilities, available tools + catalog, the user's roles, an
382
+ anti-hallucination + output contract, and the emulated tool protocol when needed). Every
383
+ prompt text is **transparent and editable** — there are no hard-coded, inaccessible
384
+ prompt strings.
385
+
386
+ **Editable slots (`aiSlots`, admin CRUD, tenant-scoped).** The builder ships sensible
387
+ built-in defaults for every slot (`getSystemDefaultSlots()`), so the module works with
388
+ zero rows. A row here **overrides** the default for its `key` (logical slot, e.g. `base`,
389
+ `permissions`, `anti_hallucination`, `output_contract`, `tool_protocol_emulated`),
390
+ optionally scoped by `locale` and `capability` (`all` / `native` / `emulated`). When
391
+ multi-tenancy is active, overrides apply only to the admin's tenant; without multi-tenancy
392
+ they are effectively system-wide. `create()` is idempotent on `(tenantId, key)` — a second
393
+ "override" of the same system slot UPDATES the existing row instead of inserting a
394
+ duplicate. Content may use `{{placeholders}}` (resolved via the placeholder registry —
395
+ see below) rendered at build time. Manage via `createAiSlot` / `findAiSlots` / … (GraphQL)
396
+ or `/ai/slots` (REST), with admin-friendly extras: `GET /ai/slots/effective` returns the
397
+ combined view (framework defaults + tenant overrides + custom slots with `isSystem` /
398
+ `isOverride` flags) and `POST /ai/slots/:id/reset` deletes an override → the framework
399
+ default applies again.
400
+
401
+ **Placeholder registry (`CoreAiPlaceholderRegistry`).** Tokens like `{{userId}}`,
402
+ `{{roles}}`, `{{tools}}`, `{{toolCatalog}}`, `{{documentation}}`, `{{learnedHints}}` are
403
+ resolved at run time by a registry. The framework registers six defaults at boot; projects
404
+ add their own via `register({ name, description, resolve })` from any provider. The
405
+ current list is served via `GET /ai/placeholders` (S*USER), so any admin/editor UI can
406
+ render an up-to-date sidebar without hard-coded names in the frontend. **User-prompt
407
+ placeholders** (in user prompts coming from `aiPrompt` / SSE) are ALSO resolved before
408
+ the LLM sees them — a stored user prompt template like *"Erkläre dem Nutzer mit ID
409
+ `{{userId}}` …"\_ gets the real value substituted at run time. Unknown tokens are left
410
+ untouched so plain text with curly braces survives.
411
+
412
+ **User-facing prompts (`aiPrompts`).** Re-usable short user prompts ("Vorlagen") that
413
+ each user authors for themselves (`scope: 'user'` — private) or for the whole tenant
414
+ (`scope: 'tenant'` — public). Inserted into the chat input by a picker. Mutations are
415
+ owner-only.
416
+
417
+ **Governed learning loop (`aiPromptHints`, admin CRUD).** When the orchestrator hits a
418
+ recurring failure (tool not available, tool error/exception), it records a learned
419
+ **hint** scoped to the tool. Hints are `suggested` by default and only reach the prompt
420
+ once an admin **approves** them; set `ai.promptLearning.autoApply: true` to auto-approve.
421
+ Review/approve/reject via `updateAiPromptHint` (GraphQL) or `/ai/prompt-hints` (REST).
422
+ The model also receives **structured tool errors** (`{ error: { code, message, hint } }`)
423
+ so it can recover within the run.
424
+
425
+ > **Security:** learned hints and template overrides only ever **add textual guidance** —
426
+ > they can never relax the permission model. Tool role-filtering, `authorize()`,
427
+ > `CrudService`/`@Restricted` and `secretFields` are enforced backend-side regardless of
428
+ > the prompt.
429
+
430
+ ```typescript
431
+ ai: {
432
+ promptLearning: {
433
+ enabled: true, // record + apply learned hints (default true)
434
+ autoApply: false, // false = admin approves each hint (governed); true = auto-approve
435
+ },
436
+ }
437
+ ```
438
+
439
+ All stores are fully overridable per project via `CoreModule.forRoot(env, { ai: {
440
+ slotService, promptService, promptHintService, promptBuilder, placeholderRegistry } })`.
441
+
442
+ ## Context window (per user/session, auto-detected)
443
+
444
+ The assembled session conversation is budgeted against the model's **context window**.
445
+ The window is determined automatically per connection (`ILlmProvider.detectContextWindow()`
446
+ — e.g. a local Ollama `/api/show` probe, a known-model table, or the Claude alias) and
447
+ persisted on the connection alongside the capability flags; set `connection.contextWindow`
448
+ explicitly to override, or `ai.contextWindow` (default `8192`) as the global fallback.
449
+ When a user's session history would overflow, the **oldest non-system turns are dropped**
450
+ (the latest turn is truncated if still too large) and oversized tool-results are capped to
451
+ `ai.maxToolResultChars` (default `12000`) — so long-running conversations never exceed the
452
+ model's limit.
453
+
454
+ ## Token budgets & usage
455
+
456
+ Token/prompt limits are **per user AND per tenant**, with **config defaults** so you
457
+ need not configure each one individually. Limits are **optional** — a missing or `0`
458
+ limit means unlimited (only the LLM's own limit then applies). Requires `audit`.
459
+
460
+ ```typescript
461
+ ai: {
462
+ audit: true,
463
+ budget: {
464
+ period: 'day', // 'day' | 'month' | 'none'
465
+ user: { maxTokens: 50000 }, // default per user
466
+ tenant: { maxTokens: 2000000 }, // default per whole tenant
467
+ },
468
+ }
469
+ ```
470
+
471
+ - Admins override per user/tenant at runtime (`aiBudgetLimits`, admin CRUD via
472
+ `createAiBudgetLimit` / REST `/ai/budget-limits`): `{ scope: 'user'|'tenant', refId, maxTokens?, maxPrompts?, period? }`.
473
+ - Resolution per scope: persisted override → config default → unlimited.
474
+ - Enforced before the run (user OR tenant limit) → HTTP 429 + translated message.
475
+
476
+ **Usage reported to the client:** every prompt response carries a compact
477
+ `budget` summary — `promptTokens` (this prompt), `usedTokens`, `remainingTokens`
478
+ (null = unlimited) and `resetAt`. The full breakdown (user + tenant scopes, prompts
479
+
480
+ - tokens, limits, reset) is available via the `aiUsage` query / `GET /ai/usage`.
481
+
482
+ ## MCP server (`ai.mcp: true`)
483
+
484
+ The `AiToolRegistry` also feeds a real **MCP server** at `POST/GET/DELETE /ai/mcp`
485
+ (Streamable HTTP), so external MCP clients use the same backend tools with the
486
+ same role gating. Enable with `ai: { mcp: true }`.
487
+
488
+ **Install the SDK** in your project (it is a peer-style optional dependency that
489
+ the controller lazy-imports only when an MCP request arrives — projects that
490
+ don't enable MCP pay no install cost):
491
+
492
+ ```bash
493
+ pnpm add @modelcontextprotocol/sdk
494
+ ```
495
+
496
+ When `ai.mcp` is set but the SDK is missing, `/ai/mcp` returns **503 Service
497
+ Unavailable** with an actionable install-hint message instead of a 500 stack
498
+ trace.
499
+
500
+ - Auth: the request must carry a valid Bearer token/session (resolved by the
501
+ framework's existing auth) — the MCP session is bound to that user, and
502
+ `tools/list` / `tools/call` are filtered to and executed with their permissions.
503
+ - Unauthenticated requests get `401` with a `WWW-Authenticate` header.
504
+
505
+ ### OAuth 2.1 (`ai.mcp.oauth: true`)
506
+
507
+ For generic MCP clients that auto-discover + register:
508
+ `CoreAiMcpOAuthService` provides HMAC-signed access tokens (constant-time verify),
509
+ PKCE S256, and MongoDB-backed client/code/refresh stores; the MCP controller then
510
+ also accepts OAuth access tokens. Mount the discovery/token endpoints in `main.ts`:
511
+
512
+ ```typescript
513
+ // main.ts, after app.init()
514
+ import { mountAiMcpOAuth } from '@lenne.tech/nest-server';
515
+ await mountAiMcpOAuth(app, { baseUrl: process.env.BASE_URL });
516
+ ```
517
+
518
+ Override `CoreAiMcpOAuthService.authorizeConsent()` to wire your login/consent UI.
519
+ Set `ai.mcp.oauthSecret` (or reuse `ai.encryptionSecret`) to a random 32+ char value.
520
+
521
+ ## Tests
522
+
523
+ - `tests/unit/ai.spec.ts` — crypto, registry role filtering, orchestrator loop,
524
+ streaming, destructive confirmation, MCP role gating (fakes)
525
+ - `tests/ai.e2e-spec.ts` — full DI graph: connection CRUD + encryption, prompt
526
+ flow, multi-turn conversation, audit persistence, MCP 401