adminizer 5.0.0-build.2 → 5.0.0-build.21

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 (300) hide show
  1. package/assets/abnfDiagram-VCTEODGH-CkmsvdDi.js +1 -0
  2. package/assets/add-CESPH_aI.js +1 -0
  3. package/assets/add-group-CQ4Xm8Ru.js +1 -0
  4. package/assets/add-user-B-QNjR4B.js +1 -0
  5. package/assets/ai-assistant/agent.es.js +28362 -0
  6. package/assets/app-C5Ju5Zra.css +1 -0
  7. package/assets/app-Ca5yUSBv.js +138 -0
  8. package/assets/arc-B-Dt8YNl.js +1 -0
  9. package/assets/architectureDiagram-5GKGNRK7-Ca58kq-l.js +36 -0
  10. package/assets/asmcrypto-DZeQH0LC.js +1 -0
  11. package/assets/blockDiagram-NRAW4CY4-CuqE52IG.js +129 -0
  12. package/assets/c4Diagram-UCG6FXSJ-Cx8z-0Bw.js +38 -0
  13. package/assets/catalog-CkSbxvx0.js +6 -0
  14. package/assets/channel-C9z6Pi_j.js +1 -0
  15. package/assets/chunk-2Q5K7J3B-CXISVKTC.js +1 -0
  16. package/assets/chunk-5VM5RSS4-DYGeeVYe.js +15 -0
  17. package/assets/chunk-F27PBJKO-BeY-dbKj.js +1 -0
  18. package/assets/chunk-G27WJ6UU-DeX_eH7C.js +231 -0
  19. package/assets/chunk-JWPE2WC7-DaxI9q0u.js +1 -0
  20. package/assets/chunk-LCL6LL3I-B7PkaEz_.js +206 -0
  21. package/assets/chunk-POPQ4Y6H-lwu6ZpsN.js +1 -0
  22. package/assets/chunk-SVP7TREG-CVFllgl1.js +88 -0
  23. package/assets/chunk-XXDRQBXY-DDLN0rcg.js +1 -0
  24. package/assets/classDiagram-DTDB5LWJ-BKtCIC3B.js +1 -0
  25. package/assets/classDiagram-v2-JRS7N3AN-BKtCIC3B.js +1 -0
  26. package/assets/controls/ckeditor.es.js +9370 -9306
  27. package/assets/controls/handsontable.es.js +11971 -11938
  28. package/assets/controls/jsoneditor.es.js +13104 -13008
  29. package/assets/controls/leaflet.es.js +1023 -1004
  30. package/assets/controls/toast-ui.es.js +1843 -1834
  31. package/assets/cose-bilkent-JH36ORCC-CWRyUkhQ.js +1 -0
  32. package/assets/cynefin-OW5HDTMX-B1iLDXCC.js +166 -0
  33. package/assets/cynefinDiagram-5FMLGOSQ-DaXQHr3l.js +62 -0
  34. package/assets/cytoscape.esm-yzknjiTM.js +321 -0
  35. package/assets/dagre-3AP2YEHR-DFORa4Hz.js +4 -0
  36. package/assets/dashboard-Ssa1_Of3.js +3 -0
  37. package/assets/debounce-B3mXI_sx.js +1 -0
  38. package/assets/defaultLocale-DX6XiGOO.js +1 -0
  39. package/assets/diagram-S7CK7UJ4-CgP6KBo0.js +30 -0
  40. package/assets/diagram-UQ7AKVKN-oYfQe6w0.js +41 -0
  41. package/assets/diagram-VSXAHHWV-DfCU5wgt.js +3 -0
  42. package/assets/diagram-VX7I27RA-BITJVrNY.js +24 -0
  43. package/assets/diagram-Z3DM3KII-D8J9Ssjy.js +24 -0
  44. package/assets/docs-DHMt_Rq1.js +1 -0
  45. package/assets/ebnfDiagram-PWID7BFC-qpNmoYgx.js +1 -0
  46. package/assets/erDiagram-SSCWMZ5O-DDF-Y0zs.js +99 -0
  47. package/assets/flowDiagram-A5DVABFB-CjKVkR9T.js +168 -0
  48. package/assets/ganttDiagram-EL5Y4UJY-BXGQ53w6.js +292 -0
  49. package/assets/gitGraphDiagram-WWUBYQGX-BLMevdLh.js +106 -0
  50. package/assets/history-tBOo2gd5.js +1 -0
  51. package/assets/infoDiagram-RXCK75RN-BAnPdnyw.js +2 -0
  52. package/assets/init-Gi6I4Gst.js +1 -0
  53. package/assets/init-user-DrNUHa6r.js +1 -0
  54. package/assets/isObject-DL-BuaBl.js +1 -0
  55. package/assets/ishikawaDiagram-5VMMS53U-PnL0Z4Li.js +70 -0
  56. package/assets/journeyDiagram-EYS64GPL-CLcsCSwF.js +139 -0
  57. package/assets/kanban-definition-3QL26DDD-C2eU3uBE.js +89 -0
  58. package/assets/katex-HP8lGamR.js +257 -0
  59. package/assets/layout-BXoYdHdx.js +1 -0
  60. package/assets/linear-BSzrP0oP.js +1 -0
  61. package/assets/list-CLg6xeIK.js +4 -0
  62. package/assets/login-Ce7o3-o_.js +1 -0
  63. package/assets/manifest.json +834 -58
  64. package/assets/{media-manager-BDbpSSjH.js → media-manager-D7G1P3fz.js} +3 -3
  65. package/assets/mermaid.core-aKMJHeEk.js +310 -0
  66. package/assets/mindmap-definition-FBJOCRG2-Dzbz5ghA.js +96 -0
  67. package/assets/module-BsXph_pA.js +1 -0
  68. package/assets/notification-D-Zz1gF_.js +20 -0
  69. package/assets/ordinal-Cboi1Yqb.js +1 -0
  70. package/assets/pegDiagram-XKGWAZYB-BTdWkU0Y.js +1 -0
  71. package/assets/pieDiagram-E7YTZNPT-Dnw8-TB-.js +39 -0
  72. package/assets/quadrantDiagram-AXDQQJYC-CALzLlg9.js +7 -0
  73. package/assets/railroadDiagram-O6MQD6OU-CTA1vzcv.js +1 -0
  74. package/assets/register-CNnBkzej.js +1 -0
  75. package/assets/requirementDiagram-EFPCY7ZU-UX2CF5HA.js +84 -0
  76. package/assets/ru-CoNgAwFK.js +1 -0
  77. package/assets/sankeyDiagram-P5KCCOFB-4aPp3ATV.js +40 -0
  78. package/assets/sequenceDiagram-WJ2MYXX4--kltkwJw.js +162 -0
  79. package/assets/sizeCapture-X5ZJPWSS-Ca9-g5QY.js +1 -0
  80. package/assets/stateDiagram-HBIQ2CUA-Diaf5Q7I.js +1 -0
  81. package/assets/stateDiagram-v2-4QOOHH4V-C6FlRp7u.js +1 -0
  82. package/assets/swimlanes-XN3QIQJK-DhqX_myE.js +1 -0
  83. package/assets/swimlanesDiagram-VK2B7HYN-i_3LTEBk.js +8 -0
  84. package/assets/timeline-definition-24CTP7MA-CwkJQbhq.js +120 -0
  85. package/assets/toNumber-DZb-r9sy.js +1 -0
  86. package/assets/use-appearance-DC-DESKB.js +1 -0
  87. package/assets/user-filters-list-W47DTBp2.js +1 -0
  88. package/assets/vennDiagram-4TSXK5OY-BNM3_cVS.js +34 -0
  89. package/assets/wardleyDiagram-VM6X3IG4-BODb1NP8.js +78 -0
  90. package/assets/welcome-BeBm6NIi.js +1 -0
  91. package/assets/with-app-layout-B90DtzYC.js +31 -0
  92. package/assets/xychartDiagram-S5SC5T6Z-DthqEh3g.js +7 -0
  93. package/controllers/access-rights/permissionOptions.d.ts +2 -0
  94. package/controllers/access-rights/permissionOptions.js +23 -0
  95. package/controllers/add.js +1 -1
  96. package/controllers/addGroup.js +15 -6
  97. package/controllers/ai/AiAgentController.d.ts +66 -0
  98. package/controllers/ai/AiAgentController.js +435 -0
  99. package/controllers/ai/AiAssistantController.d.ts +0 -4
  100. package/controllers/ai/AiAssistantController.js +4 -78
  101. package/controllers/catalog/FrontendCatalogAdapter.d.ts +1 -1
  102. package/controllers/catalog/FrontentCatalogAdapter.d.ts +1 -1
  103. package/controllers/docs/index.d.ts +22 -0
  104. package/controllers/docs/index.js +126 -0
  105. package/controllers/edit.js +1 -1
  106. package/controllers/editGroup.js +15 -6
  107. package/controllers/feedback/feedbackController.d.ts +1 -1
  108. package/controllers/filter-fields/savedFilters.js +1 -1
  109. package/controllers/history-actions/HistoryController.d.ts +1 -1
  110. package/controllers/history-actions/HistoryController.js +6 -6
  111. package/controllers/list.js +1 -1
  112. package/controllers/login.js +1 -1
  113. package/controllers/media-manager/mediaManagerAdapter.d.ts +1 -1
  114. package/controllers/notifications/NotificationController.js +14 -8
  115. package/controllers/view.js +2 -2
  116. package/controllers/widgets/filterInfo.js +1 -1
  117. package/helpers/accessRightsHelper.d.ts +11 -4
  118. package/helpers/accessRightsHelper.js +82 -12
  119. package/helpers/assetVersionHelper.d.ts +14 -0
  120. package/helpers/assetVersionHelper.js +42 -0
  121. package/helpers/configHelper.d.ts +3 -3
  122. package/helpers/configHelper.js +20 -18
  123. package/helpers/controllerHelper.d.ts +3 -2
  124. package/helpers/controllerHelper.js +25 -15
  125. package/helpers/fieldsHelper.d.ts +2 -2
  126. package/helpers/fieldsHelper.js +3 -3
  127. package/helpers/filterDatetimeHelper.d.ts +1 -1
  128. package/helpers/inertiaActionsHelper.d.ts +3 -3
  129. package/helpers/inertiaActionsHelper.js +4 -5
  130. package/helpers/inertiaAddHelper.d.ts +5 -5
  131. package/helpers/inertiaAddHelper.js +7 -6
  132. package/helpers/inertiaGroupHelper.d.ts +3 -3
  133. package/helpers/inertiaGroupHelper.js +10 -2
  134. package/helpers/inertiaListHelper.d.ts +4 -4
  135. package/helpers/inertiaListHelper.js +7 -8
  136. package/helpers/inertiaMenuHelper.d.ts +2 -3
  137. package/helpers/inertiaMenuHelper.js +3 -26
  138. package/helpers/inertiaUserHelper.d.ts +3 -3
  139. package/helpers/menuHelper.d.ts +2 -2
  140. package/helpers/modelResourceHelper.d.ts +9 -0
  141. package/helpers/modelResourceHelper.js +43 -0
  142. package/helpers/navigationAccessHelper.d.ts +13 -0
  143. package/helpers/navigationAccessHelper.js +40 -0
  144. package/helpers/translationHelper.d.ts +2 -2
  145. package/index.d.ts +69 -58
  146. package/index.js +11 -0
  147. package/interfaces/adminpanelConfig.d.ts +18 -5
  148. package/interfaces/global.d.ts +6 -6
  149. package/interfaces/internalModelAccess.d.ts +1 -1
  150. package/interfaces/listQueryBuilder.d.ts +1 -1
  151. package/interfaces/types.d.ts +171 -2
  152. package/lib/Adminizer.d.ts +46 -26
  153. package/lib/Adminizer.js +37 -1
  154. package/lib/DataAccessor.d.ts +7 -6
  155. package/lib/DataAccessor.js +50 -48
  156. package/lib/MiddlewareManager.d.ts +1 -1
  157. package/lib/access-rights/AccessRightsHandler.d.ts +2 -2
  158. package/lib/admin-links/AdminLinkHandler.d.ts +72 -0
  159. package/lib/admin-links/AdminLinkHandler.js +175 -0
  160. package/lib/ai-assistant/AbstractAiConversationHistoryService.d.ts +39 -0
  161. package/lib/ai-assistant/AbstractAiConversationHistoryService.js +98 -0
  162. package/lib/ai-assistant/AbstractAiModelService.d.ts +140 -3
  163. package/lib/ai-assistant/AbstractAiModelService.js +154 -1
  164. package/lib/ai-assistant/AiAgentRunManager.d.ts +35 -0
  165. package/lib/ai-assistant/AiAgentRunManager.js +109 -0
  166. package/lib/ai-assistant/AiAssistantAgentSkillHandler.d.ts +58 -0
  167. package/lib/ai-assistant/AiAssistantAgentSkillHandler.js +87 -0
  168. package/lib/ai-assistant/AiAssistantHandler.d.ts +16 -5
  169. package/lib/ai-assistant/AiAssistantHandler.js +35 -1
  170. package/lib/ai-assistant/AiAssistantUiMethodHandler.d.ts +60 -0
  171. package/lib/ai-assistant/AiAssistantUiMethodHandler.js +138 -0
  172. package/lib/ai-assistant/builtinAgentSkills.d.ts +9 -0
  173. package/lib/ai-assistant/builtinAgentSkills.js +256 -0
  174. package/lib/ai-assistant/jsonSafe.d.ts +13 -0
  175. package/lib/ai-assistant/jsonSafe.js +81 -0
  176. package/lib/app-manager/AdminizerApp.d.ts +40 -11
  177. package/lib/app-manager/AppManager.d.ts +2 -2
  178. package/lib/app-manager/AppManager.js +54 -50
  179. package/lib/app-manager/AssetHandler.d.ts +2 -2
  180. package/lib/app-manager/ConfigLayerHandler.d.ts +2 -2
  181. package/lib/app-manager/ControllerHandler.d.ts +2 -2
  182. package/lib/catalog/AbstractCatalog.d.ts +1 -1
  183. package/lib/catalog/CatalogHandler.d.ts +1 -1
  184. package/lib/controls/ControlsHandler.d.ts +1 -1
  185. package/lib/controls/codeEditor/MonacoEditor.d.ts +1 -1
  186. package/lib/controls/geojsoneditor/GeoEditor.d.ts +1 -1
  187. package/lib/controls/jsoneditor/JsonEditor.d.ts +1 -1
  188. package/lib/controls/markdown/ToastUiEditor.d.ts +1 -1
  189. package/lib/controls/table/Handsontable.d.ts +1 -1
  190. package/lib/controls/wysiwyg/CKeditor.d.ts +1 -1
  191. package/lib/docs/AbstractDocumentation.d.ts +100 -0
  192. package/lib/docs/AbstractDocumentation.js +108 -0
  193. package/lib/docs/DocumentationHandler.d.ts +100 -0
  194. package/lib/docs/DocumentationHandler.js +198 -0
  195. package/lib/docs/FileDocumentation.d.ts +47 -0
  196. package/lib/docs/FileDocumentation.js +189 -0
  197. package/lib/docs/docsAgentSkills.d.ts +10 -0
  198. package/lib/docs/docsAgentSkills.js +93 -0
  199. package/lib/feedback/FeedbackHandler.d.ts +2 -2
  200. package/lib/filters/ConditionValidator.d.ts +1 -1
  201. package/lib/filters/FilterCustomFieldHandler.d.ts +2 -2
  202. package/lib/filters/FilterService.d.ts +4 -4
  203. package/lib/filters/FilterService.js +4 -3
  204. package/lib/filters/index.d.ts +6 -6
  205. package/lib/history-actions/AbstractHistoryAdapter.d.ts +6 -6
  206. package/lib/history-actions/AbstractHistoryAdapter.js +11 -9
  207. package/lib/history-actions/DefaultHistoryAdapter.d.ts +4 -4
  208. package/lib/history-actions/HistoryHandler.d.ts +1 -1
  209. package/lib/list-query-builder/ListQueryBuilder.d.ts +7 -7
  210. package/lib/media-manager/AbstractMediaManager.d.ts +2 -2
  211. package/lib/media-manager/MediaManagerHandler.d.ts +1 -1
  212. package/lib/media-manager/Thumb.d.ts +1 -1
  213. package/lib/media-manager/helpers/MediaManagerHelper.d.ts +5 -5
  214. package/lib/model/AbstractModel.d.ts +11 -4
  215. package/lib/model/InternalModelAccessFactory.d.ts +2 -2
  216. package/lib/model/ModelHandler.d.ts +50 -5
  217. package/lib/model/ModelHandler.js +107 -8
  218. package/lib/model/adapter/sequelize.d.ts +7 -5
  219. package/lib/model/adapter/sequelize.js +79 -46
  220. package/lib/model/adapter/typeorm.d.ts +2 -2
  221. package/lib/model/adapter/typeorm.js +1 -1
  222. package/lib/model/internalModelRepository.d.ts +2 -2
  223. package/lib/notifications/AbstractNotificationService.d.ts +4 -4
  224. package/lib/notifications/GeneralNotificationService.d.ts +2 -2
  225. package/lib/notifications/GeneralNotificationService.js +6 -5
  226. package/lib/notifications/NotificationHandler.d.ts +2 -2
  227. package/lib/notifications/SystemNotificationService.d.ts +2 -2
  228. package/lib/notifications/SystemNotificationService.js +1 -1
  229. package/lib/requestProcessor.d.ts +1 -1
  230. package/lib/ui-i18n/uiTranslationKeys.d.ts +2 -1
  231. package/lib/ui-i18n/uiTranslationKeys.js +42 -1
  232. package/lib/widgets/abstractAction.d.ts +1 -1
  233. package/lib/widgets/abstractCustom.d.ts +2 -2
  234. package/lib/widgets/abstractInfo.d.ts +1 -1
  235. package/lib/widgets/abstractLink.d.ts +2 -2
  236. package/lib/widgets/abstractSwitch.d.ts +1 -1
  237. package/lib/widgets/abstractWidgetBase.d.ts +1 -1
  238. package/lib/widgets/widgetHandler.d.ts +9 -9
  239. package/lib/widgets/widgetHandler.js +5 -1
  240. package/models/Filter.d.ts +1 -1
  241. package/models/FilterColumn.d.ts +1 -1
  242. package/models/Group.d.ts +3 -2
  243. package/models/HistoryActions.d.ts +1 -1
  244. package/models/MediaManagerAP.d.ts +2 -2
  245. package/models/MediaManagerAssociationsAP.d.ts +1 -1
  246. package/models/MediaManagerMetaAP.d.ts +1 -1
  247. package/models/User.d.ts +2 -2
  248. package/models/UserNotification.d.ts +1 -1
  249. package/package.json +29 -20
  250. package/policies/authPolicies.js +6 -6
  251. package/policies/permissionResolvers.d.ts +1 -0
  252. package/policies/permissionResolvers.js +3 -0
  253. package/services/FeedService.d.ts +3 -3
  254. package/system/Router.d.ts +1 -1
  255. package/system/Router.js +27 -1
  256. package/system/bindAccessRights.d.ts +1 -1
  257. package/system/bindAccessRights.js +2 -3
  258. package/system/bindAuthorization.d.ts +1 -1
  259. package/system/bindControls.d.ts +1 -1
  260. package/system/bindCors.d.ts +1 -1
  261. package/system/bindCustomFilterHandlers.d.ts +1 -1
  262. package/system/bindDashboardWidgets.d.ts +1 -1
  263. package/system/bindDev.d.ts +1 -1
  264. package/system/bindDocs.d.ts +13 -0
  265. package/system/bindDocs.js +77 -0
  266. package/system/bindHistory.d.ts +1 -1
  267. package/system/bindInertia.d.ts +1 -1
  268. package/system/bindInertia.js +23 -1
  269. package/system/bindMediaManager.d.ts +1 -1
  270. package/system/bindNotifications.d.ts +1 -1
  271. package/system/bindReqFunctions.d.ts +1 -1
  272. package/system/bindTranslations.d.ts +1 -1
  273. package/system/buildInternalModelAccess.d.ts +3 -3
  274. package/system/buildInternalModelAccess.js +5 -2
  275. package/system/defaults.d.ts +1 -1
  276. package/system/routePolicies.d.ts +1 -1
  277. package/system/systemModelContracts.d.ts +1 -1
  278. package/system/systemModelContracts.js +11 -7
  279. package/system/validateSystemModels.d.ts +1 -1
  280. package/system/validateSystemModels.js +29 -9
  281. package/assets/add-group-BdHFzKng.js +0 -1
  282. package/assets/add-p6yGCoZ7.js +0 -1
  283. package/assets/add-user-CDspCLu4.js +0 -1
  284. package/assets/app-DNXiApCk.css +0 -1
  285. package/assets/app-layout-DCr_-JY3.js +0 -1
  286. package/assets/app.js +0 -138
  287. package/assets/catalog-XN-_p9TB.js +0 -6
  288. package/assets/dashboard-CHZO2QFY.js +0 -3
  289. package/assets/debounce-BMmXVQ06.js +0 -1
  290. package/assets/history-CQd0FojQ.js +0 -1
  291. package/assets/init-user-8aUohVDq.js +0 -1
  292. package/assets/list-PHWgTHsg.js +0 -4
  293. package/assets/login-CO4laf6m.js +0 -1
  294. package/assets/module-DVyQtIxm.js +0 -1
  295. package/assets/notification-B87XG8nQ.js +0 -20
  296. package/assets/register-DBYIMXGQ.js +0 -1
  297. package/assets/ru-rYKdQbWF.js +0 -1
  298. package/assets/use-appearance-_EbbjHTh.js +0 -1
  299. package/assets/user-filters-list-BRzDbPkx.js +0 -1
  300. package/assets/welcome-DiXmrTgc.js +0 -1
@@ -1,14 +1,151 @@
1
- import { AiAssistantMessage, AiAssistantModelInfo } from '../../interfaces/types';
2
- import { User } from '../../models/User';
1
+ import { AiAgentConnectionStatus, AiAgentLimits, AiAgentModelOption, AiAgentPublish, AiAgentSavedFile, AiAgentSessionMeta, AiAgentUiHints, AiAgentUiSchema, AiAgentUploadedFile, AiAssistantMessage, AiAssistantModelInfo } from '../../interfaces/types.js';
2
+ import { User } from '../../models/User.js';
3
+ import type { Adminizer } from '../Adminizer.js';
4
+ import { AbstractAiConversationHistoryService } from './AbstractAiConversationHistoryService.js';
5
+ import type { AiAssistantAdminLink, AiAssistantNavigationTarget } from './AiAssistantUiMethodHandler.js';
6
+ import type { AiAssistantAgentSkillDescriptor, AiAssistantSkillUser } from './AiAssistantAgentSkillHandler.js';
7
+ export interface AiAgentChatCommand {
8
+ /** Command name without the leading slash, e.g. `summarize`. */
9
+ id: string;
10
+ /** Short explanation rendered in the command picker. */
11
+ description?: string;
12
+ }
13
+ export interface AiAgentChatCommandContext {
14
+ command: AiAgentChatCommand;
15
+ arguments: string;
16
+ input: string | Array<Record<string, unknown>>;
17
+ user: User;
18
+ publish: AiAgentPublish;
19
+ }
20
+ export interface AiAgentChatCommandResult {
21
+ /** Text rendered as the command response and stored in the dialog. */
22
+ text?: string;
23
+ /** Set false for ephemeral commands that must not enter agent history. */
24
+ persist?: boolean;
25
+ }
26
+ export type AiAgentChatCommandHandler = (context: AiAgentChatCommandContext) => Promise<AiAgentChatCommandResult | void> | AiAgentChatCommandResult | void;
3
27
  /**
4
28
  * Base class for AI assistant models. App modules register access rights
5
29
  * and expose model metadata through this service.
30
+ *
31
+ * Two levels of capability:
32
+ *
33
+ * 1. `generateReply` — required. A plain request/response chat model; the
34
+ * assistant panel runs it through the same streaming transport and shows
35
+ * the answer as a single text part.
36
+ * 2. The optional agent members below. A service that implements `streamReply`
37
+ * is driven as a real agent: tool calls, reasoning, attachments and a
38
+ * server-side per-user session (history, compaction, model switching) are
39
+ * surfaced by the panel. Whatever the service does not implement is simply
40
+ * not rendered, so partial implementations are fine.
6
41
  */
7
42
  export declare abstract class AbstractAiModelService {
8
43
  readonly id: string;
9
44
  readonly name: string;
10
45
  readonly description?: string;
11
- protected constructor(metadata: AiAssistantModelInfo);
46
+ /**
47
+ * Set when the model is registered. Agent implementations need it to read
48
+ * the panel config, the model registry and the user's permissions.
49
+ */
50
+ protected adminizer: Adminizer;
51
+ readonly conversationHistory: AbstractAiConversationHistoryService;
52
+ private readonly chatCommands;
53
+ protected constructor(metadata: AiAssistantModelInfo, conversationHistory?: AbstractAiConversationHistoryService);
54
+ /** Called by AiAssistantHandler on registration; not part of the model API. */
55
+ attachAdminizer(adminizer: Adminizer): void;
12
56
  getMetadata(): AiAssistantModelInfo;
57
+ /**
58
+ * Registers a slash command owned by this agent. The panel only displays
59
+ * it and sends its raw `/command arguments` text back to this service;
60
+ * command handling never lives in the browser.
61
+ */
62
+ registerChatCommand(command: AiAgentChatCommand, handler?: AiAgentChatCommandHandler): void;
63
+ /** Commands available for autocomplete in the panel. */
64
+ getChatCommands(): AiAgentChatCommand[];
65
+ /** One declarative contract for the shared agent UI. */
66
+ getUiSchema(locale?: string): AiAgentUiSchema;
67
+ /**
68
+ * Browser capabilities available to a particular user. Tool-capable
69
+ * services can turn these descriptors into their provider's tool format.
70
+ */
71
+ protected getUiMethods(user: User): import("./AiAssistantUiMethodHandler").AiAssistantUiMethod[];
72
+ /**
73
+ * Server-side implementation of the built-in `search-admin-links` skill.
74
+ * An OpenHarness/AI SDK adapter can return this value directly from its
75
+ * tool's `execute` callback.
76
+ */
77
+ protected searchAdminLinks(user: User, query?: string): AiAssistantAdminLink[];
78
+ /**
79
+ * Built-in and app-contributed server skills this user may call. Data
80
+ * skills are already scoped to that user's model permissions, so an agent
81
+ * exposing them can be offered to non-administrator accounts.
82
+ */
83
+ protected getAgentSkills(user: User): AiAssistantAgentSkillDescriptor[];
84
+ /**
85
+ * Executes an app-owned server skill after its permission check. The result
86
+ * is normalized to plain JSON: it becomes a tool result inside the agent's
87
+ * message history, which every later turn re-validates.
88
+ */
89
+ protected executeAgentSkill(id: string, input: Record<string, unknown>, user: User): Promise<unknown>;
90
+ /** Identity of the user an agent turn is running for. */
91
+ protected describeUser(user: User): AiAssistantSkillUser;
92
+ /**
93
+ * Opens an admin page for the user: either a concrete `href` or a link
94
+ * template plus `params` (a record page, a catalog item…). The target is
95
+ * validated against that user's permissions and the resolved URL is
96
+ * returned, so it can be reported back as the tool result.
97
+ */
98
+ protected openAdminLink(target: AiAssistantNavigationTarget, user: User, publish: AiAgentPublish): string;
99
+ /**
100
+ * Ask the shared panel to execute a registered browser method. The
101
+ * registry and permission check stay server-side, so an LLM cannot invoke
102
+ * an arbitrary client event.
103
+ */
104
+ protected publishUiMethod(id: string, input: Record<string, unknown>, user: User, publish: AiAgentPublish): void;
105
+ /**
106
+ * Invoked by the agent transport for a registered `/command`. Returning
107
+ * null intentionally lets an unhandled command flow into `streamReply`
108
+ * or `generateReply` as regular agent input. A handler should return text
109
+ * instead of publishing a plain text frame itself, so its response can be
110
+ * persisted in the active conversation.
111
+ */
112
+ executeChatCommand(prompt: string, input: string | Array<Record<string, unknown>>, user: User, publish: AiAgentPublish): Promise<AiAgentChatCommandResult | null>;
13
113
  abstract generateReply(prompt: string, history: AiAssistantMessage[], user: User): Promise<string>;
114
+ /** True when the service streams runs itself instead of answering in one shot. */
115
+ isStreamingAgent(): boolean;
116
+ /**
117
+ * Run one turn and report progress through `publish`. `input` is either the
118
+ * plain prompt or an ai-sdk style ModelMessage[] when files are attached.
119
+ * The closing `done` frame is emitted by the run manager, not by the service.
120
+ */
121
+ streamReply?(input: string | Array<Record<string, unknown>>, user: User, publish: AiAgentPublish): Promise<void>;
122
+ /** Current model, context usage and turn count of this user's session. */
123
+ getSessionMeta?(user: User): AiAgentSessionMeta;
124
+ /** Session history as ai-sdk ModelMessage[]; the panel rebuilds the thread from it. */
125
+ getSessionHistory?(user: User): Array<Record<string, unknown>>;
126
+ /** Restores a stored dialog when the user switches conversations. */
127
+ restoreSessionHistory?(user: User, messages: Array<Record<string, unknown>>): Promise<void> | void;
128
+ /** Drop the session. Returns whether there was anything to drop. */
129
+ resetSession?(user: User): boolean;
130
+ /** Summarize the session to free context. Returns e.g. tokensBefore/tokensAfter. */
131
+ compactSession?(user: User): Promise<Record<string, unknown>>;
132
+ /** LLMs the user may switch between inside this service. */
133
+ getModelChoices?(): Promise<AiAgentModelOption[]>;
134
+ /** Switch the session model. Returns whether the model changed. */
135
+ setCurrentModel?(user: User, model: string): Promise<boolean>;
136
+ /** Persist uploads before the turn, so tools can refer to them by id. */
137
+ saveUploadedFiles?(user: User, files: AiAgentUploadedFile[]): Promise<AiAgentSavedFile[]>;
138
+ /**
139
+ * Provisioning state. Omit when the service is always usable; when present
140
+ * and not `ready`, the panel shows a loader instead of the composer.
141
+ */
142
+ getConnectionStatus?(): Promise<AiAgentConnectionStatus> | AiAgentConnectionStatus;
143
+ /** Budget / rate limits of the provider key, when the provider exposes them. */
144
+ getLimits?(forceRefresh: boolean): Promise<AiAgentLimits | null>;
145
+ /**
146
+ * Panel copy for this service: title, welcome hint, composer placeholder,
147
+ * starter prompts and connection screens. The locale is resolved from the
148
+ * current admin user before this method is called.
149
+ */
150
+ getUiHints?(locale?: string): AiAgentUiHints;
14
151
  }
@@ -1,15 +1,41 @@
1
+ import { InMemoryAiConversationHistoryService } from './AbstractAiConversationHistoryService.js';
2
+ import { toJsonSafe } from './jsonSafe.js';
1
3
  /**
2
4
  * Base class for AI assistant models. App modules register access rights
3
5
  * and expose model metadata through this service.
6
+ *
7
+ * Two levels of capability:
8
+ *
9
+ * 1. `generateReply` — required. A plain request/response chat model; the
10
+ * assistant panel runs it through the same streaming transport and shows
11
+ * the answer as a single text part.
12
+ * 2. The optional agent members below. A service that implements `streamReply`
13
+ * is driven as a real agent: tool calls, reasoning, attachments and a
14
+ * server-side per-user session (history, compaction, model switching) are
15
+ * surfaced by the panel. Whatever the service does not implement is simply
16
+ * not rendered, so partial implementations are fine.
4
17
  */
5
18
  export class AbstractAiModelService {
6
19
  id;
7
20
  name;
8
21
  description;
9
- constructor(metadata) {
22
+ /**
23
+ * Set when the model is registered. Agent implementations need it to read
24
+ * the panel config, the model registry and the user's permissions.
25
+ */
26
+ adminizer;
27
+ conversationHistory;
28
+ chatCommands = new Map();
29
+ constructor(metadata, conversationHistory = new InMemoryAiConversationHistoryService()) {
10
30
  this.id = metadata.id;
11
31
  this.name = metadata.name;
12
32
  this.description = metadata.description;
33
+ this.conversationHistory = conversationHistory;
34
+ }
35
+ /** Called by AiAssistantHandler on registration; not part of the model API. */
36
+ attachAdminizer(adminizer) {
37
+ this.adminizer = adminizer;
38
+ this.conversationHistory.initialize(this, adminizer);
13
39
  }
14
40
  getMetadata() {
15
41
  return {
@@ -18,4 +44,131 @@ export class AbstractAiModelService {
18
44
  description: this.description,
19
45
  };
20
46
  }
47
+ /**
48
+ * Registers a slash command owned by this agent. The panel only displays
49
+ * it and sends its raw `/command arguments` text back to this service;
50
+ * command handling never lives in the browser.
51
+ */
52
+ registerChatCommand(command, handler) {
53
+ const id = command.id.trim().replace(/^\/+/, '').toLowerCase();
54
+ if (!/^[a-z][a-z0-9_-]*$/.test(id)) {
55
+ throw new Error(`Invalid AI agent command id: ${command.id}`);
56
+ }
57
+ this.chatCommands.set(id, {
58
+ command: { id, description: command.description?.trim() || undefined },
59
+ handler,
60
+ });
61
+ }
62
+ /** Commands available for autocomplete in the panel. */
63
+ getChatCommands() {
64
+ const commands = [...this.chatCommands.values()].map(({ command }) => ({ ...command }));
65
+ if (typeof this.compactSession === 'function' && !this.chatCommands.has('compact')) {
66
+ commands.unshift({
67
+ id: 'compact',
68
+ description: 'Prune or summarize older messages to free context.',
69
+ });
70
+ }
71
+ return commands;
72
+ }
73
+ /** One declarative contract for the shared agent UI. */
74
+ getUiSchema(locale) {
75
+ return {
76
+ ...(this.getUiHints?.(locale) ?? {}),
77
+ commands: this.getChatCommands(),
78
+ panels: {
79
+ history: true,
80
+ models: typeof this.getModelChoices === 'function' && typeof this.setCurrentModel === 'function',
81
+ limits: typeof this.getLimits === 'function',
82
+ },
83
+ };
84
+ }
85
+ /**
86
+ * Browser capabilities available to a particular user. Tool-capable
87
+ * services can turn these descriptors into their provider's tool format.
88
+ */
89
+ getUiMethods(user) {
90
+ return this.adminizer.aiAssistantUiMethodHandler.getAvailable(user);
91
+ }
92
+ /**
93
+ * Server-side implementation of the built-in `search-admin-links` skill.
94
+ * An OpenHarness/AI SDK adapter can return this value directly from its
95
+ * tool's `execute` callback.
96
+ */
97
+ searchAdminLinks(user, query) {
98
+ return this.adminizer.aiAssistantUiMethodHandler.searchAdminLinks(user, query);
99
+ }
100
+ /**
101
+ * Built-in and app-contributed server skills this user may call. Data
102
+ * skills are already scoped to that user's model permissions, so an agent
103
+ * exposing them can be offered to non-administrator accounts.
104
+ */
105
+ getAgentSkills(user) {
106
+ return this.adminizer.aiAssistantAgentSkillHandler.getAvailable(user);
107
+ }
108
+ /**
109
+ * Executes an app-owned server skill after its permission check. The result
110
+ * is normalized to plain JSON: it becomes a tool result inside the agent's
111
+ * message history, which every later turn re-validates.
112
+ */
113
+ async executeAgentSkill(id, input, user) {
114
+ const result = await this.adminizer.aiAssistantAgentSkillHandler.execute(id, input, user);
115
+ return toJsonSafe(result) ?? null;
116
+ }
117
+ /** Identity of the user an agent turn is running for. */
118
+ describeUser(user) {
119
+ return this.adminizer.aiAssistantAgentSkillHandler.describeUser(user);
120
+ }
121
+ /**
122
+ * Opens an admin page for the user: either a concrete `href` or a link
123
+ * template plus `params` (a record page, a catalog item…). The target is
124
+ * validated against that user's permissions and the resolved URL is
125
+ * returned, so it can be reported back as the tool result.
126
+ */
127
+ openAdminLink(target, user, publish) {
128
+ const href = this.adminizer.aiAssistantUiMethodHandler.resolveNavigation(user, target);
129
+ this.publishUiMethod('navigate', { href }, user, publish);
130
+ return href;
131
+ }
132
+ /**
133
+ * Ask the shared panel to execute a registered browser method. The
134
+ * registry and permission check stay server-side, so an LLM cannot invoke
135
+ * an arbitrary client event.
136
+ */
137
+ publishUiMethod(id, input, user, publish) {
138
+ const method = this.getUiMethods(user).find((candidate) => candidate.id === id);
139
+ if (!method)
140
+ throw new Error(`AI assistant UI method "${id}" is not available`);
141
+ publish({ type: 'ui.method', method: method.id, action: method.action, input });
142
+ }
143
+ /**
144
+ * Invoked by the agent transport for a registered `/command`. Returning
145
+ * null intentionally lets an unhandled command flow into `streamReply`
146
+ * or `generateReply` as regular agent input. A handler should return text
147
+ * instead of publishing a plain text frame itself, so its response can be
148
+ * persisted in the active conversation.
149
+ */
150
+ async executeChatCommand(prompt, input, user, publish) {
151
+ const match = /^\/([a-z][a-z0-9_-]*)(?:\s+([\s\S]*))?$/i.exec(prompt.trim());
152
+ if (!match)
153
+ return null;
154
+ const entry = this.chatCommands.get(match[1].toLowerCase());
155
+ if (entry?.handler) {
156
+ const result = await entry.handler({ command: entry.command, arguments: match[2] ?? '', input, user, publish });
157
+ return { persist: true, ...result };
158
+ }
159
+ if (match[1].toLowerCase() !== 'compact' || typeof this.compactSession !== 'function')
160
+ return null;
161
+ const result = await this.compactSession(user);
162
+ const compacted = result.compacted === true;
163
+ const before = Number(result.tokensBefore ?? 0);
164
+ const after = Number(result.tokensAfter ?? 0);
165
+ const text = compacted
166
+ ? `Context compacted${before > 0 ? `: ${before} → ${after} tokens.` : '.'}`
167
+ : 'Nothing to compact yet.';
168
+ return { text, persist: true };
169
+ }
170
+ /** True when the service streams runs itself instead of answering in one shot. */
171
+ isStreamingAgent() {
172
+ return typeof this.streamReply === 'function';
173
+ }
21
174
  }
@@ -0,0 +1,35 @@
1
+ import { AiAgentStreamEvent } from '../../interfaces/types.js';
2
+ interface AgentRun {
3
+ id: string;
4
+ userId: number;
5
+ modelId: string;
6
+ createdAt: number;
7
+ events: AiAgentStreamEvent[];
8
+ clients: Set<ResType>;
9
+ done: boolean;
10
+ }
11
+ /**
12
+ * Buffered SSE transport for agent runs.
13
+ *
14
+ * A run is started by a POST that returns immediately (so the browser can open
15
+ * the stream) and then writes its events here. Every event is stored with its
16
+ * index and used as the SSE event id, which lets EventSource resume through
17
+ * `Last-Event-ID` after a reconnect without replaying what the client already
18
+ * processed.
19
+ */
20
+ export declare class AiAgentRunManager {
21
+ private readonly runs;
22
+ create(userId: number, modelId: string): AgentRun;
23
+ /** Only the owner may read a run. */
24
+ get(runId: string, userId: number): AgentRun | undefined;
25
+ publish(run: AgentRun, event: AiAgentStreamEvent): void;
26
+ /** Marks the run finished and flushes the closing frame to every client. */
27
+ finish(run: AgentRun, event: AiAgentStreamEvent): void;
28
+ /** Streams a run to one client: replays what it missed, then follows live. */
29
+ subscribe(run: AgentRun, req: ReqType, res: ResType): void;
30
+ private write;
31
+ /** SSE payloads must be JSON-safe and bounded. */
32
+ private sanitize;
33
+ private cleanup;
34
+ }
35
+ export type { AgentRun };
@@ -0,0 +1,109 @@
1
+ /** Runs are kept only long enough for a reconnecting EventSource to catch up. */
2
+ const MAX_RUN_AGE_MS = 10 * 60 * 1000;
3
+ /** Hard cap per SSE frame, so a chatty tool result cannot blow up memory. */
4
+ const MAX_EVENT_JSON_CHARS = 64 * 1024;
5
+ /**
6
+ * Buffered SSE transport for agent runs.
7
+ *
8
+ * A run is started by a POST that returns immediately (so the browser can open
9
+ * the stream) and then writes its events here. Every event is stored with its
10
+ * index and used as the SSE event id, which lets EventSource resume through
11
+ * `Last-Event-ID` after a reconnect without replaying what the client already
12
+ * processed.
13
+ */
14
+ export class AiAgentRunManager {
15
+ runs = new Map();
16
+ create(userId, modelId) {
17
+ this.cleanup();
18
+ const id = `run-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
19
+ const run = {
20
+ id,
21
+ userId,
22
+ modelId,
23
+ createdAt: Date.now(),
24
+ events: [],
25
+ clients: new Set(),
26
+ done: false,
27
+ };
28
+ this.runs.set(id, run);
29
+ return run;
30
+ }
31
+ /** Only the owner may read a run. */
32
+ get(runId, userId) {
33
+ const run = this.runs.get(runId);
34
+ return run && run.userId === userId ? run : undefined;
35
+ }
36
+ publish(run, event) {
37
+ const safe = this.sanitize(event);
38
+ const index = run.events.push(safe) - 1;
39
+ for (const client of run.clients)
40
+ this.write(client, 'agent', safe, index);
41
+ }
42
+ /** Marks the run finished and flushes the closing frame to every client. */
43
+ finish(run, event) {
44
+ run.done = true;
45
+ this.publish(run, event);
46
+ for (const client of run.clients)
47
+ client.end();
48
+ run.clients.clear();
49
+ }
50
+ /** Streams a run to one client: replays what it missed, then follows live. */
51
+ subscribe(run, req, res) {
52
+ res.setHeader('Content-Type', 'text/event-stream');
53
+ res.setHeader('Cache-Control', 'no-cache, no-transform');
54
+ res.setHeader('Connection', 'keep-alive');
55
+ res.setHeader('X-Accel-Buffering', 'no');
56
+ res.flushHeaders?.();
57
+ res.write('retry: 3000\n\n');
58
+ this.write(res, 'connected', { runId: run.id });
59
+ const lastEventId = Number(req.headers['last-event-id']);
60
+ const start = Number.isFinite(lastEventId) ? lastEventId + 1 : 0;
61
+ for (let i = start; i < run.events.length; i++) {
62
+ this.write(res, 'agent', run.events[i], i);
63
+ }
64
+ if (run.done) {
65
+ res.end();
66
+ return;
67
+ }
68
+ run.clients.add(res);
69
+ req.on('close', () => run.clients.delete(res));
70
+ }
71
+ write(res, event, data, id) {
72
+ if (id !== undefined)
73
+ res.write(`id: ${id}\n`);
74
+ res.write(`event: ${event}\n`);
75
+ res.write(`data: ${JSON.stringify(data)}\n\n`);
76
+ }
77
+ /** SSE payloads must be JSON-safe and bounded. */
78
+ sanitize(event) {
79
+ if (!event || typeof event !== 'object')
80
+ return event;
81
+ let safe = event;
82
+ // A finished agent turn may carry the whole history — the panel never
83
+ // uses it and it would dwarf every other frame.
84
+ if (safe.type === 'done' && 'messages' in safe) {
85
+ const { messages: _messages, ...rest } = safe;
86
+ safe = rest;
87
+ }
88
+ if (safe.error instanceof Error) {
89
+ safe = { ...safe, error: safe.error.message };
90
+ }
91
+ const json = JSON.stringify(safe);
92
+ if (json.length <= MAX_EVENT_JSON_CHARS)
93
+ return safe;
94
+ return {
95
+ type: safe.type || 'event',
96
+ truncated: true,
97
+ originalChars: json.length,
98
+ message: `${safe.type || 'event'} payload was truncated to keep SSE memory bounded.`,
99
+ preview: `${json.slice(0, MAX_EVENT_JSON_CHARS - 1)}…`,
100
+ };
101
+ }
102
+ cleanup() {
103
+ const cutoff = Date.now() - MAX_RUN_AGE_MS;
104
+ for (const [id, run] of this.runs) {
105
+ if (run.createdAt < cutoff)
106
+ this.runs.delete(id);
107
+ }
108
+ }
109
+ }
@@ -0,0 +1,58 @@
1
+ import type { User } from '../../models/User.js';
2
+ import type { Adminizer } from '../Adminizer.js';
3
+ import type { AppRuntime } from '../app-manager/AdminizerApp.js';
4
+ /** Identity of the admin user on whose behalf a skill runs. */
5
+ export interface AiAssistantSkillUser {
6
+ id?: number;
7
+ login: string;
8
+ fullName?: string;
9
+ email?: string;
10
+ locale?: string;
11
+ isAdministrator: boolean;
12
+ groups: string[];
13
+ }
14
+ export interface AiAssistantAgentSkillContext {
15
+ user: User;
16
+ /** The same user as a plain descriptor, safe to return in a tool result. */
17
+ userIdentity: AiAssistantSkillUser;
18
+ runtime: AppRuntime;
19
+ }
20
+ /** A server-side tool made available to every compatible registered agent. */
21
+ export interface AiAssistantAgentSkill {
22
+ id: string;
23
+ description: string;
24
+ inputSchema: Record<string, unknown>;
25
+ accessRightsToken?: string;
26
+ /**
27
+ * Advertise the calling user inside the input schema. The agent then sees
28
+ * who it acts for and echoes it back; the value is always overwritten with
29
+ * the authenticated identity, so it cannot be used to impersonate.
30
+ */
31
+ requiresUser?: boolean;
32
+ /**
33
+ * Per-user refinement of what the agent is told about this skill, e.g. an
34
+ * enum of the models this particular user may read.
35
+ */
36
+ describe?(user: User, adminizer: Adminizer): {
37
+ description?: string;
38
+ inputSchema?: Record<string, unknown>;
39
+ } | undefined;
40
+ execute(input: Record<string, unknown>, context: AiAssistantAgentSkillContext): unknown | Promise<unknown>;
41
+ }
42
+ export type AiAssistantAgentSkillDescriptor = Omit<AiAssistantAgentSkill, 'execute' | 'describe'>;
43
+ /** Input property that carries the calling user for `requiresUser` skills. */
44
+ export declare const CURRENT_USER_PARAM = "currentUser";
45
+ export declare class AiAssistantAgentSkillHandler {
46
+ private readonly adminizer;
47
+ private readonly skills;
48
+ constructor(adminizer: Adminizer);
49
+ add(skill: AiAssistantAgentSkill, owner?: string): void;
50
+ remove(id: string, owner?: string): boolean;
51
+ /** Skills this user may call, described with their permissions applied. */
52
+ getAvailable(user: User): AiAssistantAgentSkillDescriptor[];
53
+ execute(id: string, input: Record<string, unknown>, user: User): Promise<unknown>;
54
+ /** Plain, JSON-safe identity of an admin user. */
55
+ describeUser(user: User): AiAssistantSkillUser;
56
+ private isPermitted;
57
+ private describe;
58
+ }
@@ -0,0 +1,87 @@
1
+ import { buildBuiltinAgentSkills } from './builtinAgentSkills.js';
2
+ /** Input property that carries the calling user for `requiresUser` skills. */
3
+ export const CURRENT_USER_PARAM = 'currentUser';
4
+ export class AiAssistantAgentSkillHandler {
5
+ adminizer;
6
+ skills = new Map();
7
+ constructor(adminizer) {
8
+ this.adminizer = adminizer;
9
+ // Built-in skills only read `adminizer` when they run, so registering
10
+ // them from the constructor is safe while Adminizer is still wiring up.
11
+ for (const skill of buildBuiltinAgentSkills(adminizer))
12
+ this.add(skill);
13
+ }
14
+ add(skill, owner = 'host') {
15
+ const id = skill.id.trim().toLowerCase();
16
+ if (!/^[a-z][a-z0-9_-]*$/.test(id))
17
+ throw new Error(`Invalid AI assistant skill id: ${skill.id}`);
18
+ if (!skill.description.trim())
19
+ throw new Error(`AI assistant skill "${id}" requires a description`);
20
+ if (this.skills.has(id))
21
+ throw new Error(`AI assistant skill "${id}" is already registered`);
22
+ this.skills.set(id, { ...skill, id, owner });
23
+ }
24
+ remove(id, owner) {
25
+ const skill = this.skills.get(id);
26
+ if (!skill || (owner && skill.owner !== owner))
27
+ return false;
28
+ return this.skills.delete(id);
29
+ }
30
+ /** Skills this user may call, described with their permissions applied. */
31
+ getAvailable(user) {
32
+ return [...this.skills.values()]
33
+ .filter((skill) => this.isPermitted(skill, user))
34
+ .map((skill) => this.describe(skill, user));
35
+ }
36
+ async execute(id, input, user) {
37
+ const skill = this.skills.get(id.trim().toLowerCase());
38
+ if (!skill || !this.isPermitted(skill, user)) {
39
+ throw new Error(`AI assistant skill "${id}" is not available`);
40
+ }
41
+ const userIdentity = this.describeUser(user);
42
+ // Whatever the model passed as the user is discarded: a skill acts for
43
+ // the authenticated admin user only.
44
+ const payload = skill.requiresUser
45
+ ? { ...input, [CURRENT_USER_PARAM]: userIdentity.login }
46
+ : input;
47
+ return skill.execute(payload, {
48
+ user,
49
+ userIdentity,
50
+ runtime: this.adminizer.appManager.createRuntime(skill.owner),
51
+ });
52
+ }
53
+ /** Plain, JSON-safe identity of an admin user. */
54
+ describeUser(user) {
55
+ return {
56
+ id: user.id,
57
+ login: user.login,
58
+ fullName: user.fullName || undefined,
59
+ email: user.email || undefined,
60
+ locale: user.locale || undefined,
61
+ isAdministrator: Boolean(user.isAdministrator),
62
+ groups: (user.groups ?? []).map((group) => group?.name).filter(Boolean),
63
+ };
64
+ }
65
+ isPermitted(skill, user) {
66
+ return !skill.accessRightsToken || this.adminizer.accessRightsHelper.hasPermission(skill.accessRightsToken, user);
67
+ }
68
+ describe(skill, user) {
69
+ const refinement = skill.describe?.(user, this.adminizer) ?? {};
70
+ const { execute: _execute, describe: _describe, owner: _owner, ...rest } = skill;
71
+ const inputSchema = { ...(refinement.inputSchema ?? skill.inputSchema) };
72
+ if (skill.requiresUser) {
73
+ const identity = this.describeUser(user);
74
+ const properties = { ...(inputSchema.properties ?? {}) };
75
+ properties[CURRENT_USER_PARAM] = {
76
+ type: 'string',
77
+ enum: [identity.login],
78
+ description: `Login of the admin user this conversation belongs to (id ${identity.id}`
79
+ + `${identity.fullName ? `, ${identity.fullName}` : ''}). Always pass "${identity.login}".`,
80
+ };
81
+ const required = new Set([...(inputSchema.required ?? []), CURRENT_USER_PARAM]);
82
+ inputSchema.properties = properties;
83
+ inputSchema.required = [...required];
84
+ }
85
+ return { ...rest, description: refinement.description ?? skill.description, inputSchema };
86
+ }
87
+ }
@@ -1,17 +1,28 @@
1
- import { Adminizer } from '../Adminizer';
2
- import { AbstractAiModelService } from './AbstractAiModelService';
3
- import { AiAssistantMessage, AiAssistantModelInfo } from '../../interfaces/types';
4
- import { User } from '../../models/User';
1
+ import { Adminizer } from '../Adminizer.js';
2
+ import { AbstractAiModelService } from './AbstractAiModelService.js';
3
+ import { AiAssistantMessage, AiAssistantModelInfo } from '../../interfaces/types.js';
4
+ import { User } from '../../models/User.js';
5
5
  export declare class AiAssistantHandler {
6
6
  private readonly adminizer;
7
7
  private readonly models;
8
+ private readonly owners;
8
9
  private readonly history;
9
10
  constructor(adminizer: Adminizer);
10
- registerModel(service: AbstractAiModelService): void;
11
+ /**
12
+ * @param owner App name when the model belongs to an app. Models without an
13
+ * owner belong to the host application and are never removed automatically.
14
+ */
15
+ registerModel(service: AbstractAiModelService, owner?: string): void;
16
+ /**
17
+ * @param owner When passed, the model is kept if it is owned by someone
18
+ * else, so a disabled app never removes another owner's model.
19
+ */
20
+ unregisterModel(id: string, owner?: string): void;
11
21
  getModel(id: string): AbstractAiModelService | undefined;
12
22
  getModels(): AiAssistantModelInfo[];
13
23
  getHistory(userId: number, modelId: string): AiAssistantMessage[];
14
24
  resetHistory(userId: number, modelId: string): void;
25
+ replaceHistory(userId: number, modelId: string, history: AiAssistantMessage[]): void;
15
26
  sendMessage(user: User, modelId: string, prompt: string): Promise<{
16
27
  history: AiAssistantMessage[];
17
28
  }>;