@webiny/ai-powerups 6.6.0-alpha.4 → 6.6.0-alpha.5

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 (211) hide show
  1. package/AiPowerups.d.ts +12 -0
  2. package/AiPowerups.js +1 -2
  3. package/AiPowerups.js.map +1 -1
  4. package/admin/Extension.d.ts +1 -1
  5. package/admin/Extension.js +6 -2
  6. package/admin/Extension.js.map +1 -1
  7. package/admin/domain/modelRoles.d.ts +17 -0
  8. package/admin/domain/modelRoles.js +8 -0
  9. package/admin/domain/modelRoles.js.map +1 -0
  10. package/admin/features/feature.js +2 -0
  11. package/admin/features/feature.js.map +1 -1
  12. package/admin/features/listCapabilities/ListCapabilitiesGateway.d.ts +12 -0
  13. package/admin/features/listCapabilities/ListCapabilitiesGateway.js +34 -0
  14. package/admin/features/listCapabilities/ListCapabilitiesGateway.js.map +1 -0
  15. package/admin/features/listCapabilities/ListCapabilitiesRepository.d.ts +13 -0
  16. package/admin/features/listCapabilities/ListCapabilitiesRepository.js +27 -0
  17. package/admin/features/listCapabilities/ListCapabilitiesRepository.js.map +1 -0
  18. package/admin/features/listCapabilities/ListCapabilitiesUseCase.d.ts +10 -0
  19. package/admin/features/listCapabilities/ListCapabilitiesUseCase.js +18 -0
  20. package/admin/features/listCapabilities/ListCapabilitiesUseCase.js.map +1 -0
  21. package/admin/features/listCapabilities/abstractions.d.ts +28 -0
  22. package/admin/features/listCapabilities/abstractions.js +7 -0
  23. package/admin/features/listCapabilities/abstractions.js.map +1 -0
  24. package/admin/features/listCapabilities/feature.d.ts +3 -0
  25. package/admin/features/listCapabilities/feature.js +21 -0
  26. package/admin/features/listCapabilities/feature.js.map +1 -0
  27. package/admin/features/listCapabilities/index.d.ts +3 -0
  28. package/admin/features/listCapabilities/index.js +2 -0
  29. package/admin/features/settings/shared/abstractions.d.ts +58 -25
  30. package/admin/features/settings/shared/abstractions.js.map +1 -1
  31. package/admin/presentation/AiPowerUpsSettings/AiPowerUpsSettingsPresenter.js +13 -1
  32. package/admin/presentation/AiPowerUpsSettings/AiPowerUpsSettingsPresenter.js.map +1 -1
  33. package/admin/presentation/AiPowerUpsSettings/feature.js +6 -2
  34. package/admin/presentation/AiPowerUpsSettings/feature.js.map +1 -1
  35. package/admin/presentation/AiPowerUpsSettings/hasUsableModel.d.ts +13 -0
  36. package/admin/presentation/AiPowerUpsSettings/hasUsableModel.js +9 -0
  37. package/admin/presentation/AiPowerUpsSettings/hasUsableModel.js.map +1 -0
  38. package/admin/presentation/AiPowerUpsSettings/index.d.ts +1 -0
  39. package/admin/presentation/AiPowerUpsSettings/index.js +1 -0
  40. package/admin/presentation/AiPowerUpsSettings/settingsGroup.d.ts +9 -0
  41. package/admin/presentation/AiPowerUpsSettings/settingsGroup.js.map +1 -1
  42. package/admin/presentation/CapabilitiesSettings.d.ts +39 -0
  43. package/admin/presentation/CapabilitiesSettings.js +104 -0
  44. package/admin/presentation/CapabilitiesSettings.js.map +1 -0
  45. package/admin/presentation/CmsContentGeneration/CmsGenerateContentButton.js +2 -2
  46. package/admin/presentation/CmsContentGeneration/CmsGenerateContentButton.js.map +1 -1
  47. package/admin/presentation/ConnectionsSettings.d.ts +25 -0
  48. package/admin/presentation/ConnectionsSettings.js +52 -0
  49. package/admin/presentation/ConnectionsSettings.js.map +1 -0
  50. package/admin/presentation/ModelRolesSettings.d.ts +29 -0
  51. package/admin/presentation/ModelRolesSettings.js +67 -0
  52. package/admin/presentation/ModelRolesSettings.js.map +1 -0
  53. package/admin/presentation/WbContentGeneration/GenerateContentButton.js +2 -2
  54. package/admin/presentation/WbContentGeneration/GenerateContentButton.js.map +1 -1
  55. package/admin/presentation/modelRoles.d.ts +10 -0
  56. package/admin/presentation/modelRoles.js +23 -0
  57. package/admin/presentation/modelRoles.js.map +1 -0
  58. package/api/Extension.js +10 -2
  59. package/api/Extension.js.map +1 -1
  60. package/api/features/AdminAssistant/AdminAssistantStreamRoute.d.ts +34 -0
  61. package/api/features/AdminAssistant/AdminAssistantStreamRoute.js +42 -0
  62. package/api/features/AdminAssistant/AdminAssistantStreamRoute.js.map +1 -0
  63. package/api/features/AdminAssistant/AdminAssistantUseCase.d.ts +42 -0
  64. package/api/features/AdminAssistant/AdminAssistantUseCase.js +170 -0
  65. package/api/features/AdminAssistant/AdminAssistantUseCase.js.map +1 -0
  66. package/api/features/AdminAssistant/abstractions.d.ts +38 -0
  67. package/api/features/AdminAssistant/abstractions.js +6 -0
  68. package/api/features/AdminAssistant/abstractions.js.map +1 -0
  69. package/api/features/AdminAssistant/approvals.d.ts +29 -0
  70. package/api/features/AdminAssistant/approvals.js +30 -0
  71. package/api/features/AdminAssistant/approvals.js.map +1 -0
  72. package/api/features/AdminAssistant/capability.d.ts +31 -0
  73. package/api/features/AdminAssistant/capability.js +19 -0
  74. package/api/features/AdminAssistant/capability.js.map +1 -0
  75. package/api/features/AdminAssistant/events.d.ts +54 -0
  76. package/api/features/AdminAssistant/events.js +0 -0
  77. package/api/features/AdminAssistant/feature.d.ts +12 -0
  78. package/api/features/AdminAssistant/feature.js +23 -0
  79. package/api/features/AdminAssistant/feature.js.map +1 -0
  80. package/api/features/AdminAssistant/index.d.ts +5 -0
  81. package/api/features/AdminAssistant/index.js +3 -0
  82. package/api/features/AdminAssistant/parseChatBody.d.ts +14 -0
  83. package/api/features/AdminAssistant/parseChatBody.js +33 -0
  84. package/api/features/AdminAssistant/parseChatBody.js.map +1 -0
  85. package/api/features/AdminAssistant/systemPrompt.d.ts +7 -0
  86. package/api/features/AdminAssistant/systemPrompt.js +24 -0
  87. package/api/features/AdminAssistant/systemPrompt.js.map +1 -0
  88. package/api/features/AiImageEnrichment/PrepareImageEnrichmentUseCase.d.ts +3 -5
  89. package/api/features/AiImageEnrichment/PrepareImageEnrichmentUseCase.js +11 -16
  90. package/api/features/AiImageEnrichment/PrepareImageEnrichmentUseCase.js.map +1 -1
  91. package/api/features/AiImageEnrichment/abstractions.d.ts +6 -0
  92. package/api/features/AiImageEnrichment/abstractions.js.map +1 -1
  93. package/api/features/AiImageEnrichment/buildEnrichmentAiRequest.js +2 -2
  94. package/api/features/AiImageEnrichment/buildEnrichmentAiRequest.js.map +1 -1
  95. package/api/features/AiImageEnrichment/capability.d.ts +18 -0
  96. package/api/features/AiImageEnrichment/capability.js +19 -0
  97. package/api/features/AiImageEnrichment/capability.js.map +1 -0
  98. package/api/features/AiImageEnrichment/errors.d.ts +6 -1
  99. package/api/features/AiImageEnrichment/errors.js +2 -2
  100. package/api/features/AiImageEnrichment/errors.js.map +1 -1
  101. package/api/features/AiImageEnrichment/feature.js +2 -0
  102. package/api/features/AiImageEnrichment/feature.js.map +1 -1
  103. package/api/features/Capabilities/CapabilitiesHandler.d.ts +28 -0
  104. package/api/features/Capabilities/CapabilitiesHandler.js +56 -0
  105. package/api/features/Capabilities/CapabilitiesHandler.js.map +1 -0
  106. package/api/features/Capabilities/ListAiCapabilitiesUseCase.d.ts +18 -0
  107. package/api/features/Capabilities/ListAiCapabilitiesUseCase.js +28 -0
  108. package/api/features/Capabilities/ListAiCapabilitiesUseCase.js.map +1 -0
  109. package/api/features/Capabilities/ResolveAiCapabilityUseCase.d.ts +30 -0
  110. package/api/features/Capabilities/ResolveAiCapabilityUseCase.js +88 -0
  111. package/api/features/Capabilities/ResolveAiCapabilityUseCase.js.map +1 -0
  112. package/api/features/Capabilities/abstractions.d.ts +77 -0
  113. package/api/features/Capabilities/abstractions.js +7 -0
  114. package/api/features/Capabilities/abstractions.js.map +1 -0
  115. package/api/features/Capabilities/composeSystemPrompt.d.ts +14 -0
  116. package/api/features/Capabilities/composeSystemPrompt.js +13 -0
  117. package/api/features/Capabilities/composeSystemPrompt.js.map +1 -0
  118. package/api/features/Capabilities/feature.d.ts +4 -0
  119. package/api/features/Capabilities/feature.js +15 -0
  120. package/api/features/Capabilities/feature.js.map +1 -0
  121. package/api/features/Capabilities/index.d.ts +5 -0
  122. package/api/features/Capabilities/index.js +3 -0
  123. package/api/features/Capabilities/types.d.ts +86 -0
  124. package/api/features/Capabilities/types.js +4 -0
  125. package/api/features/Capabilities/types.js.map +1 -0
  126. package/api/features/CmsCompareEntryRevisions/CmsCompareEntryRevisionsUseCase.d.ts +3 -5
  127. package/api/features/CmsCompareEntryRevisions/CmsCompareEntryRevisionsUseCase.js +11 -65
  128. package/api/features/CmsCompareEntryRevisions/CmsCompareEntryRevisionsUseCase.js.map +1 -1
  129. package/api/features/CmsCompareEntryRevisions/capability.d.ts +13 -0
  130. package/api/features/CmsCompareEntryRevisions/capability.js +64 -0
  131. package/api/features/CmsCompareEntryRevisions/capability.js.map +1 -0
  132. package/api/features/CmsCompareEntryRevisions/feature.js +2 -0
  133. package/api/features/CmsCompareEntryRevisions/feature.js.map +1 -1
  134. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentUseCase.d.ts +3 -5
  135. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentUseCase.js +11 -19
  136. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentUseCase.js.map +1 -1
  137. package/api/features/CmsGenerateEntryContent/capability.d.ts +17 -0
  138. package/api/features/CmsGenerateEntryContent/capability.js +17 -0
  139. package/api/features/CmsGenerateEntryContent/capability.js.map +1 -0
  140. package/api/features/CmsGenerateEntryContent/feature.js +2 -0
  141. package/api/features/CmsGenerateEntryContent/feature.js.map +1 -1
  142. package/api/features/Connections/ConnectionsGraphQLMapper.d.ts +20 -0
  143. package/api/features/Connections/ConnectionsGraphQLMapper.js +37 -0
  144. package/api/features/Connections/ConnectionsGraphQLMapper.js.map +1 -0
  145. package/api/features/Connections/ConnectionsHandler.d.ts +25 -0
  146. package/api/features/Connections/ConnectionsHandler.js +84 -0
  147. package/api/features/Connections/ConnectionsHandler.js.map +1 -0
  148. package/api/features/Connections/feature.d.ts +4 -0
  149. package/api/features/Connections/feature.js +13 -0
  150. package/api/features/Connections/feature.js.map +1 -0
  151. package/api/features/Connections/index.d.ts +2 -0
  152. package/api/features/Connections/index.js +1 -0
  153. package/api/features/Connections/legacyProviders.d.ts +26 -0
  154. package/api/features/Connections/legacyProviders.js +9 -0
  155. package/api/features/Connections/legacyProviders.js.map +1 -0
  156. package/api/features/Connections/types.d.ts +36 -0
  157. package/api/features/Connections/types.js +0 -0
  158. package/api/features/GetSettings/GetSettingsRepository.js +1 -1
  159. package/api/features/GetSettings/GetSettingsRepository.js.map +1 -1
  160. package/api/features/ModelRoles/ModelRolesHandler.d.ts +15 -0
  161. package/api/features/ModelRoles/ModelRolesHandler.js +68 -0
  162. package/api/features/ModelRoles/ModelRolesHandler.js.map +1 -0
  163. package/api/features/ModelRoles/feature.d.ts +4 -0
  164. package/api/features/ModelRoles/feature.js +11 -0
  165. package/api/features/ModelRoles/feature.js.map +1 -0
  166. package/api/features/ModelRoles/index.d.ts +4 -0
  167. package/api/features/ModelRoles/index.js +2 -0
  168. package/api/features/ModelRoles/roles.d.ts +16 -0
  169. package/api/features/ModelRoles/roles.js +9 -0
  170. package/api/features/ModelRoles/roles.js.map +1 -0
  171. package/api/features/ModelRoles/types.d.ts +21 -0
  172. package/api/features/ModelRoles/types.js +7 -0
  173. package/api/features/ModelRoles/types.js.map +1 -0
  174. package/api/features/UpdateSettings/UpdateSettingsRepository.js +2 -2
  175. package/api/features/UpdateSettings/UpdateSettingsRepository.js.map +1 -1
  176. package/api/features/WbGeneratePageContent/WbGeneratePageContentUseCase.d.ts +3 -5
  177. package/api/features/WbGeneratePageContent/WbGeneratePageContentUseCase.js +11 -19
  178. package/api/features/WbGeneratePageContent/WbGeneratePageContentUseCase.js.map +1 -1
  179. package/api/features/WbGeneratePageContent/capability.d.ts +16 -0
  180. package/api/features/WbGeneratePageContent/capability.js +17 -0
  181. package/api/features/WbGeneratePageContent/capability.js.map +1 -0
  182. package/api/features/WbGeneratePageContent/feature.js +2 -0
  183. package/api/features/WbGeneratePageContent/feature.js.map +1 -1
  184. package/api/features/WbTranslatePage/WbTranslatePageDecorator.d.ts +5 -5
  185. package/api/features/WbTranslatePage/WbTranslatePageDecorator.js +19 -19
  186. package/api/features/WbTranslatePage/WbTranslatePageDecorator.js.map +1 -1
  187. package/api/features/WbTranslatePage/capability.d.ts +13 -0
  188. package/api/features/WbTranslatePage/capability.js +19 -0
  189. package/api/features/WbTranslatePage/capability.js.map +1 -0
  190. package/api/features/WbTranslatePage/feature.js +2 -0
  191. package/api/features/WbTranslatePage/feature.js.map +1 -1
  192. package/api/features/shared/abstractions.d.ts +6 -1
  193. package/api/features/shared/abstractions.js.map +1 -1
  194. package/api/features/shared/modelId.d.ts +11 -0
  195. package/api/features/shared/modelId.js +4 -0
  196. package/api/features/shared/modelId.js.map +1 -0
  197. package/api/graphql/BaseGraphQLSchema.js +16 -0
  198. package/api/graphql/BaseGraphQLSchema.js.map +1 -1
  199. package/api/types.d.ts +3 -0
  200. package/api/types.js +3 -0
  201. package/exports/api/ai-powerups.d.ts +5 -0
  202. package/exports/api/ai-powerups.js +3 -0
  203. package/package.json +24 -26
  204. package/api/features/AiChatProvider/PowerUpsAiChatProvider.d.ts +0 -24
  205. package/api/features/AiChatProvider/PowerUpsAiChatProvider.js +0 -31
  206. package/api/features/AiChatProvider/PowerUpsAiChatProvider.js.map +0 -1
  207. package/api/features/AiChatProvider/feature.d.ts +0 -9
  208. package/api/features/AiChatProvider/feature.js +0 -11
  209. package/api/features/AiChatProvider/feature.js.map +0 -1
  210. package/api/features/AiChatProvider/index.d.ts +0 -2
  211. package/api/features/AiChatProvider/index.js +0 -2
@@ -0,0 +1,54 @@
1
+ import type { ModelMessage } from "ai";
2
+ import type { PendingApproval } from "./approvals.js";
3
+ /**
4
+ * What the assistant emits while it works.
5
+ *
6
+ * Deliberately a small, purpose-built set rather than the AI SDK's UI-message protocol: the client is
7
+ * a command palette, not a chat framework, and it needs one thing the SDK protocol does not model for
8
+ * us — the approval pause. Keeping our own events also means the admin bundle carries no AI SDK code.
9
+ *
10
+ * Transport-free by design. A transport frames these (as SSE, as newline JSON, as a CLI render); the
11
+ * feature only says what happened.
12
+ */
13
+ export type AdminAssistantEvent =
14
+ /** A fragment of the answer. Concatenate in arrival order. */
15
+ {
16
+ type: "text";
17
+ text: string;
18
+ }
19
+ /** A tool started running. Named so the UI can show work in progress, not just a spinner. */
20
+ | {
21
+ type: "tool-call";
22
+ name: string;
23
+ }
24
+ /** A tool finished. Pairs with `tool-call` by name. */
25
+ | {
26
+ type: "tool-result";
27
+ name: string;
28
+ }
29
+ /**
30
+ * A tool threw. Also pairs with `tool-call` by name, and is NOT terminal: the model reads the
31
+ * message and usually corrects itself on the next step, which is why tools report failure by
32
+ * throwing. Without this the call would have no outcome and its chip would spin forever.
33
+ */
34
+ | {
35
+ type: "tool-error";
36
+ name: string;
37
+ message: string;
38
+ }
39
+ /** The loop paused: these calls change something and need a human decision. */
40
+ | {
41
+ type: "approval";
42
+ approvals: PendingApproval[];
43
+ }
44
+ /** Terminal success. Carries what the client must replay to resume after an approval. */
45
+ | {
46
+ type: "done";
47
+ messages: ModelMessage[];
48
+ steps: number;
49
+ }
50
+ /** Terminal failure. The run produced no usable answer. */
51
+ | {
52
+ type: "error";
53
+ message: string;
54
+ };
File without changes
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The admin assistant: its capability, the use case, and the HTTP route that reaches it.
3
+ *
4
+ * Lives in AI Power-Ups rather than in a package of its own because everything it needs is here —
5
+ * the settings that choose its model, the capability resolver that reads them, and the tools it
6
+ * calls. As a separate package it could only reach those through an abstraction registered back
7
+ * from this side, which is an indirection that bought nothing.
8
+ */
9
+ export declare const AdminAssistantFeature: {
10
+ name: string;
11
+ register(container: import("@webiny/di").Container): void;
12
+ };
@@ -0,0 +1,23 @@
1
+ import { createFeature } from "@webiny/feature/api";
2
+ import { FeatureFlags } from "@webiny/api-core/features/featureFlags/abstractions.js";
3
+ import { AdminAssistantConfig } from "./abstractions.js";
4
+ import { AdminAssistantUseCase } from "./AdminAssistantUseCase.js";
5
+ import { AdminAssistantStreamRouteDefinition } from "./AdminAssistantStreamRoute.js";
6
+ import { AdminAssistantCapability } from "./capability.js";
7
+ const MAX_STEPS = 12;
8
+ const AdminAssistantFeature = createFeature({
9
+ name: "AiPowerUps/AdminAssistant",
10
+ register (container) {
11
+ const enabled = container.resolve(FeatureFlags).get().isEnabled("aiPowerups.adminAssistant");
12
+ if (!enabled) return;
13
+ container.register(AdminAssistantCapability);
14
+ container.registerInstance(AdminAssistantConfig, {
15
+ maxSteps: MAX_STEPS
16
+ });
17
+ container.register(AdminAssistantUseCase);
18
+ container.register(AdminAssistantStreamRouteDefinition);
19
+ }
20
+ });
21
+ export { AdminAssistantFeature };
22
+
23
+ //# sourceMappingURL=feature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api/features/AdminAssistant/feature.js","sources":["../../../../src/api/features/AdminAssistant/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { FeatureFlags } from \"@webiny/api-core/features/featureFlags/abstractions.js\";\nimport { AdminAssistantConfig } from \"./abstractions.js\";\nimport { AdminAssistantUseCase } from \"./AdminAssistantUseCase.js\";\nimport { AdminAssistantStreamRouteDefinition } from \"./AdminAssistantStreamRoute.js\";\nimport { AdminAssistantCapability } from \"./capability.js\";\n\n/**\n * How many rounds the agent loop may take before it is cut off.\n *\n * Enough for the deepest expected chain: list models, describe one, query it, answer — plus room for\n * a corrected retry after a rejected filter.\n *\n * Deliberately not configurable. It is a runaway guard, closer to a timeout than a setting, and the\n * number only means something next to the tool chains the assistant actually walks. Exposing it\n * invites a project to raise it to 100 and find out what that costs.\n */\nconst MAX_STEPS = 12;\n\n/**\n * The admin assistant: its capability, the use case, and the HTTP route that reaches it.\n *\n * Lives in AI Power-Ups rather than in a package of its own because everything it needs is here —\n * the settings that choose its model, the capability resolver that reads them, and the tools it\n * calls. As a separate package it could only reach those through an abstraction registered back\n * from this side, which is an indirection that bought nothing.\n */\nexport const AdminAssistantFeature = createFeature({\n name: \"AiPowerUps/AdminAssistant\",\n register(container) {\n // Register-time gate on the effective flags (project config && live WCP license), matching\n // how every other AI feature gates itself. Nothing is registered when it is off, so an\n // unlicensed project has no `/stream/ai/admin-assistant` route and no settings row.\n const enabled = container\n .resolve(FeatureFlags)\n .get()\n .isEnabled(\"aiPowerups.adminAssistant\");\n\n if (!enabled) {\n return;\n }\n\n container.register(AdminAssistantCapability);\n container.registerInstance(AdminAssistantConfig, { maxSteps: MAX_STEPS });\n container.register(AdminAssistantUseCase);\n\n /*\n * Only the definition is registered: the router matches on it and builds the handler it\n * names once a request actually hits the path.\n */\n container.register(AdminAssistantStreamRouteDefinition);\n }\n});\n"],"names":["MAX_STEPS","AdminAssistantFeature","createFeature","container","enabled","FeatureFlags","AdminAssistantCapability","AdminAssistantConfig","AdminAssistantUseCase","AdminAssistantStreamRouteDefinition"],"mappings":";;;;;;AAiBA,MAAMA,YAAY;AAUX,MAAMC,wBAAwBC,cAAc;IAC/C,MAAM;IACN,UAASC,SAAS;QAId,MAAMC,UAAUD,UACX,OAAO,CAACE,cACR,GAAG,GACH,SAAS,CAAC;QAEf,IAAI,CAACD,SACD;QAGJD,UAAU,QAAQ,CAACG;QACnBH,UAAU,gBAAgB,CAACI,sBAAsB;YAAE,UAAUP;QAAU;QACvEG,UAAU,QAAQ,CAACK;QAMnBL,UAAU,QAAQ,CAACM;IACvB;AACJ"}
@@ -0,0 +1,5 @@
1
+ export { AdminAssistantUseCase, AdminAssistantConfig } from "./abstractions.js";
2
+ export type { AdminAssistantParams } from "./abstractions.js";
3
+ export type { AdminAssistantEvent } from "./events.js";
4
+ export { AdminAssistantCapability, ADMIN_ASSISTANT_CAPABILITY } from "./capability.js";
5
+ export { AdminAssistantFeature } from "./feature.js";
@@ -0,0 +1,3 @@
1
+ export { AdminAssistantConfig, AdminAssistantUseCase } from "./abstractions.js";
2
+ export { ADMIN_ASSISTANT_CAPABILITY, AdminAssistantCapability } from "./capability.js";
3
+ export { AdminAssistantFeature } from "./feature.js";
@@ -0,0 +1,14 @@
1
+ import type { ModelMessage } from "ai";
2
+ import type { ApprovalDecision } from "./approvals.js";
3
+ export interface ParsedChatBody {
4
+ messages: ModelMessage[];
5
+ decisions: ApprovalDecision[];
6
+ }
7
+ /**
8
+ * Accepts either a fresh question or a continuation. A continuation replays `messages` verbatim,
9
+ * including the assistant message carrying the approval request, because the SDK matches an approval
10
+ * response to its request by id and that request exists nowhere else. We keep no session.
11
+ *
12
+ * Returns null for anything unusable, so the caller decides the status code.
13
+ */
14
+ export declare const parseChatBody: (body: unknown) => ParsedChatBody | null;
@@ -0,0 +1,33 @@
1
+ import { parseDecisions } from "./approvals.js";
2
+ const safeParse = (value)=>{
3
+ try {
4
+ return JSON.parse(value);
5
+ } catch {
6
+ return;
7
+ }
8
+ };
9
+ const parseChatBody = (body)=>{
10
+ const raw = "string" == typeof body ? safeParse(body) : body;
11
+ const payload = raw;
12
+ if (!payload) return null;
13
+ const decisions = parseDecisions(payload["approvals"]);
14
+ const prompt = payload["prompt"];
15
+ if ("string" == typeof prompt && prompt.trim()) return {
16
+ messages: [
17
+ {
18
+ role: "user",
19
+ content: prompt
20
+ }
21
+ ],
22
+ decisions
23
+ };
24
+ const messages = payload["messages"];
25
+ if (!Array.isArray(messages) || 0 === messages.length) return null;
26
+ return {
27
+ messages: messages,
28
+ decisions
29
+ };
30
+ };
31
+ export { parseChatBody };
32
+
33
+ //# sourceMappingURL=parseChatBody.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api/features/AdminAssistant/parseChatBody.js","sources":["../../../../src/api/features/AdminAssistant/parseChatBody.ts"],"sourcesContent":["import type { ModelMessage } from \"ai\";\nimport { parseDecisions } from \"./approvals.js\";\nimport type { ApprovalDecision } from \"./approvals.js\";\n\nexport interface ParsedChatBody {\n messages: ModelMessage[];\n decisions: ApprovalDecision[];\n}\n\nconst safeParse = (value: string): unknown => {\n try {\n return JSON.parse(value);\n } catch {\n return undefined;\n }\n};\n\n/**\n * Accepts either a fresh question or a continuation. A continuation replays `messages` verbatim,\n * including the assistant message carrying the approval request, because the SDK matches an approval\n * response to its request by id and that request exists nowhere else. We keep no session.\n *\n * Returns null for anything unusable, so the caller decides the status code.\n */\nexport const parseChatBody = (body: unknown): ParsedChatBody | null => {\n const raw = typeof body === \"string\" ? safeParse(body) : body;\n const payload = raw as Record<string, unknown> | undefined;\n\n if (!payload) {\n return null;\n }\n\n const decisions = parseDecisions(payload[\"approvals\"]);\n const prompt = payload[\"prompt\"];\n\n if (typeof prompt === \"string\" && prompt.trim()) {\n return { messages: [{ role: \"user\", content: prompt }], decisions };\n }\n\n const messages = payload[\"messages\"];\n\n if (!Array.isArray(messages) || messages.length === 0) {\n return null;\n }\n\n return { messages: messages as ModelMessage[], decisions };\n};\n"],"names":["safeParse","value","JSON","parseChatBody","body","raw","payload","decisions","parseDecisions","prompt","messages","Array"],"mappings":";AASA,MAAMA,YAAY,CAACC;IACf,IAAI;QACA,OAAOC,KAAK,KAAK,CAACD;IACtB,EAAE,OAAM;QACJ;IACJ;AACJ;AASO,MAAME,gBAAgB,CAACC;IAC1B,MAAMC,MAAM,AAAgB,YAAhB,OAAOD,OAAoBJ,UAAUI,QAAQA;IACzD,MAAME,UAAUD;IAEhB,IAAI,CAACC,SACD,OAAO;IAGX,MAAMC,YAAYC,eAAeF,OAAO,CAAC,YAAY;IACrD,MAAMG,SAASH,OAAO,CAAC,SAAS;IAEhC,IAAI,AAAkB,YAAlB,OAAOG,UAAuBA,OAAO,IAAI,IACzC,OAAO;QAAE,UAAU;YAAC;gBAAE,MAAM;gBAAQ,SAASA;YAAO;SAAE;QAAEF;IAAU;IAGtE,MAAMG,WAAWJ,OAAO,CAAC,WAAW;IAEpC,IAAI,CAACK,MAAM,OAAO,CAACD,aAAaA,AAAoB,MAApBA,SAAS,MAAM,EAC3C,OAAO;IAGX,OAAO;QAAE,UAAUA;QAA4BH;IAAU;AAC7D"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The assistant is deliberately told to DISCOVER rather than guess. Model IDs and field IDs are
3
+ * project-specific — there is no way to know them from training data — so every concrete answer has to
4
+ * come from a tool call. Without this instruction models happily invent a plausible `modelId` and then
5
+ * report the resulting error as if the content did not exist.
6
+ */
7
+ export declare const SYSTEM_PROMPT = "You are the Webiny admin assistant. You help editors and developers find, understand and change the content in their Webiny project.\n\nYou have tools available. Use them \u2014 never answer a question about this project's content, models, or files from memory or assumption.\n\nHow to work:\n- Content model IDs and field IDs are specific to this project. Always discover them with listContentModels, then describeContentModel, before querying.\n- Build filters with the field IDs describeContentModel returned. Do not guess field names.\n- If a tool returns an error, read it and correct your call. A \"not allowed\" error means the user lacks permission \u2014 say so plainly rather than retrying.\n- If a query returns nothing, say so. Do not present an empty result as if it were data.\n\nChanging things:\n- Tools that change something are not executed until the user approves them. Propose the change by calling the tool; the user sees exactly what you asked for and confirms it.\n- Read first, then propose. Look up the ids you need (teams, folders, entries) rather than guessing them, because the user is approving the arguments you supply.\n- Propose one coherent change at a time. Do not bundle unrelated edits into a single step.\n- If an approval is denied, do not retry the same call. Acknowledge it and stop.\n\nHow to answer:\n- Be brief. These answers appear in a command palette, not a chat window.\n- Lead with the answer. No preamble, no restating the question.\n- When you list entries, give the few fields that matter, not every field you received.\n- State counts precisely (\"3 products are on sale\"), never vaguely.";
@@ -0,0 +1,24 @@
1
+ const SYSTEM_PROMPT = `You are the Webiny admin assistant. You help editors and developers find, understand and change the content in their Webiny project.
2
+
3
+ You have tools available. Use them — never answer a question about this project's content, models, or files from memory or assumption.
4
+
5
+ How to work:
6
+ - Content model IDs and field IDs are specific to this project. Always discover them with listContentModels, then describeContentModel, before querying.
7
+ - Build filters with the field IDs describeContentModel returned. Do not guess field names.
8
+ - If a tool returns an error, read it and correct your call. A "not allowed" error means the user lacks permission — say so plainly rather than retrying.
9
+ - If a query returns nothing, say so. Do not present an empty result as if it were data.
10
+
11
+ Changing things:
12
+ - Tools that change something are not executed until the user approves them. Propose the change by calling the tool; the user sees exactly what you asked for and confirms it.
13
+ - Read first, then propose. Look up the ids you need (teams, folders, entries) rather than guessing them, because the user is approving the arguments you supply.
14
+ - Propose one coherent change at a time. Do not bundle unrelated edits into a single step.
15
+ - If an approval is denied, do not retry the same call. Acknowledge it and stop.
16
+
17
+ How to answer:
18
+ - Be brief. These answers appear in a command palette, not a chat window.
19
+ - Lead with the answer. No preamble, no restating the question.
20
+ - When you list entries, give the few fields that matter, not every field you received.
21
+ - State counts precisely ("3 products are on sale"), never vaguely.`;
22
+ export { SYSTEM_PROMPT };
23
+
24
+ //# sourceMappingURL=systemPrompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api/features/AdminAssistant/systemPrompt.js","sources":["../../../../src/api/features/AdminAssistant/systemPrompt.ts"],"sourcesContent":["/**\n * The assistant is deliberately told to DISCOVER rather than guess. Model IDs and field IDs are\n * project-specific — there is no way to know them from training data — so every concrete answer has to\n * come from a tool call. Without this instruction models happily invent a plausible `modelId` and then\n * report the resulting error as if the content did not exist.\n */\nexport const SYSTEM_PROMPT = `You are the Webiny admin assistant. You help editors and developers find, understand and change the content in their Webiny project.\n\nYou have tools available. Use them — never answer a question about this project's content, models, or files from memory or assumption.\n\nHow to work:\n- Content model IDs and field IDs are specific to this project. Always discover them with listContentModels, then describeContentModel, before querying.\n- Build filters with the field IDs describeContentModel returned. Do not guess field names.\n- If a tool returns an error, read it and correct your call. A \"not allowed\" error means the user lacks permission — say so plainly rather than retrying.\n- If a query returns nothing, say so. Do not present an empty result as if it were data.\n\nChanging things:\n- Tools that change something are not executed until the user approves them. Propose the change by calling the tool; the user sees exactly what you asked for and confirms it.\n- Read first, then propose. Look up the ids you need (teams, folders, entries) rather than guessing them, because the user is approving the arguments you supply.\n- Propose one coherent change at a time. Do not bundle unrelated edits into a single step.\n- If an approval is denied, do not retry the same call. Acknowledge it and stop.\n\nHow to answer:\n- Be brief. These answers appear in a command palette, not a chat window.\n- Lead with the answer. No preamble, no restating the question.\n- When you list entries, give the few fields that matter, not every field you received.\n- State counts precisely (\"3 products are on sale\"), never vaguely.`;\n"],"names":["SYSTEM_PROMPT"],"mappings":"AAMO,MAAMA,gBAAgB,CAAC;;;;;;;;;;;;;;;;;;;;mEAoBqC,CAAC"}
@@ -1,16 +1,14 @@
1
1
  import { Result } from "@webiny/feature/api";
2
- import { Encryption } from "@webiny/api-core/features/encryption/index.js";
3
2
  import { GetFileUseCase } from "@webiny/api-file-manager/features/file/GetFile/index.js";
4
3
  import { GetFileContentsByIdUseCase } from "@webiny/api-file-manager/features/file/GetFileContentsById/abstractions.js";
5
4
  import { PrepareImageEnrichmentUseCase as UseCaseAbstraction, type IPreparedImageEnrichment } from "./abstractions.js";
6
- import { GetSettingsUseCase } from "../../../api/features/GetSettings/index.js";
5
+ import { ResolveAiCapabilityUseCase } from "../../../api/features/Capabilities/index.js";
7
6
  import type { ImageEnrichmentError } from "./errors.js";
8
7
  declare class PrepareImageEnrichmentUseCaseImpl implements UseCaseAbstraction.Interface {
9
8
  private getFile;
10
9
  private getFileContents;
11
- private getSettings;
12
- private encryption;
13
- constructor(getFile: GetFileUseCase.Interface, getFileContents: GetFileContentsByIdUseCase.Interface, getSettings: GetSettingsUseCase.Interface, encryption: Encryption.Interface);
10
+ private resolveCapability;
11
+ constructor(getFile: GetFileUseCase.Interface, getFileContents: GetFileContentsByIdUseCase.Interface, resolveCapability: ResolveAiCapabilityUseCase.Interface);
14
12
  execute(fileId: string): Promise<Result<IPreparedImageEnrichment, ImageEnrichmentError>>;
15
13
  }
16
14
  export declare const PrepareImageEnrichmentUseCase: typeof PrepareImageEnrichmentUseCaseImpl & {
@@ -1,16 +1,15 @@
1
1
  import { Result } from "@webiny/feature/api";
2
- import { Encryption } from "@webiny/api-core/features/encryption/index.js";
3
2
  import { GetFileUseCase } from "@webiny/api-file-manager/features/file/GetFile/index.js";
4
3
  import { GetFileContentsByIdUseCase } from "@webiny/api-file-manager/features/file/GetFileContentsById/abstractions.js";
5
4
  import { PrepareImageEnrichmentUseCase } from "./abstractions.js";
6
- import { GetSettingsUseCase } from "../GetSettings/index.js";
5
+ import { ResolveAiCapabilityUseCase, withAdditionalInstructions } from "../Capabilities/index.js";
6
+ import { FM_IMAGE_ENRICHMENT_CAPABILITY } from "./capability.js";
7
7
  import { EnrichmentFileContentsError, EnrichmentFileNotFoundError, EnrichmentNoProviderError, EnrichmentNotAnImageError } from "./errors.js";
8
8
  class PrepareImageEnrichmentUseCaseImpl {
9
- constructor(getFile, getFileContents, getSettings, encryption){
9
+ constructor(getFile, getFileContents, resolveCapability){
10
10
  this.getFile = getFile;
11
11
  this.getFileContents = getFileContents;
12
- this.getSettings = getSettings;
13
- this.encryption = encryption;
12
+ this.resolveCapability = resolveCapability;
14
13
  }
15
14
  async execute(fileId) {
16
15
  const fileResult = await this.getFile.execute(fileId);
@@ -19,19 +18,16 @@ class PrepareImageEnrichmentUseCaseImpl {
19
18
  if (!file.type.startsWith("image/")) return Result.fail(new EnrichmentNotAnImageError(file.type));
20
19
  const contentsResult = await this.getFileContents.execute(fileId);
21
20
  if (contentsResult.isFail()) return Result.fail(new EnrichmentFileContentsError(contentsResult.error.message));
22
- const aiSettingsResult = await this.getSettings.execute();
23
- if (aiSettingsResult.isFail()) return Result.fail(new EnrichmentNoProviderError());
24
- const firstProvider = aiSettingsResult.value.providers.presets[0];
25
- if (!firstProvider) return Result.fail(new EnrichmentNoProviderError());
21
+ const resolved = await this.resolveCapability.execute(FM_IMAGE_ENRICHMENT_CAPABILITY);
22
+ if (resolved.isFail()) return Result.fail(new EnrichmentNoProviderError(resolved.error.message));
23
+ const capability = resolved.value;
26
24
  return Result.ok({
27
25
  fileId: file.id,
28
26
  imageBase64: contentsResult.value.buffer.toString("base64"),
29
27
  imageMediaType: contentsResult.value.contentType,
30
- model: firstProvider.model,
31
- connection: {
32
- sdkName: firstProvider.model.split("/")[0],
33
- apiKey: await this.encryption.decrypt(firstProvider.apiKeyEncrypted)
34
- }
28
+ model: capability.model,
29
+ prompt: withAdditionalInstructions(capability),
30
+ connection: capability.connection
35
31
  });
36
32
  }
37
33
  }
@@ -40,8 +36,7 @@ const PrepareImageEnrichmentUseCase_PrepareImageEnrichmentUseCase = PrepareImage
40
36
  dependencies: [
41
37
  GetFileUseCase,
42
38
  GetFileContentsByIdUseCase,
43
- GetSettingsUseCase,
44
- Encryption
39
+ ResolveAiCapabilityUseCase
45
40
  ]
46
41
  });
47
42
  export { PrepareImageEnrichmentUseCase_PrepareImageEnrichmentUseCase as PrepareImageEnrichmentUseCase };
@@ -1 +1 @@
1
- {"version":3,"file":"api/features/AiImageEnrichment/PrepareImageEnrichmentUseCase.js","sources":["../../../../src/api/features/AiImageEnrichment/PrepareImageEnrichmentUseCase.ts"],"sourcesContent":["import { Result } from \"@webiny/feature/api\";\nimport { Encryption } from \"@webiny/api-core/features/encryption/index.js\";\nimport { GetFileUseCase } from \"@webiny/api-file-manager/features/file/GetFile/index.js\";\nimport { GetFileContentsByIdUseCase } from \"@webiny/api-file-manager/features/file/GetFileContentsById/abstractions.js\";\nimport {\n PrepareImageEnrichmentUseCase as UseCaseAbstraction,\n type IPreparedImageEnrichment\n} from \"./abstractions.js\";\nimport { GetSettingsUseCase } from \"~/api/features/GetSettings/index.js\";\nimport {\n EnrichmentFileContentsError,\n EnrichmentFileNotFoundError,\n EnrichmentNoProviderError,\n EnrichmentNotAnImageError\n} from \"./errors.js\";\nimport type { ImageEnrichmentError } from \"./errors.js\";\n\nclass PrepareImageEnrichmentUseCaseImpl implements UseCaseAbstraction.Interface {\n constructor(\n private getFile: GetFileUseCase.Interface,\n private getFileContents: GetFileContentsByIdUseCase.Interface,\n private getSettings: GetSettingsUseCase.Interface,\n private encryption: Encryption.Interface\n ) {}\n\n async execute(fileId: string): Promise<Result<IPreparedImageEnrichment, ImageEnrichmentError>> {\n const fileResult = await this.getFile.execute(fileId);\n if (fileResult.isFail()) {\n return Result.fail(new EnrichmentFileNotFoundError(fileId));\n }\n\n const file = fileResult.value;\n\n if (!file.type.startsWith(\"image/\")) {\n return Result.fail(new EnrichmentNotAnImageError(file.type));\n }\n\n // Read the image bytes and send them to the AI as base64, NOT a URL. A URL forces the\n // provider to fetch the file — which fails for private/access-controlled files, leaks the\n // domain, and can't reach a non-public origin (e.g. local dev). base64 is a portable standard\n // that works on every setup. (Also resolves the AI-SDK \"image part\" deprecation.)\n const contentsResult = await this.getFileContents.execute(fileId);\n if (contentsResult.isFail()) {\n return Result.fail(new EnrichmentFileContentsError(contentsResult.error.message));\n }\n\n const aiSettingsResult = await this.getSettings.execute();\n if (aiSettingsResult.isFail()) {\n return Result.fail(new EnrichmentNoProviderError());\n }\n\n const firstProvider = aiSettingsResult.value.providers.presets[0];\n if (!firstProvider) {\n return Result.fail(new EnrichmentNoProviderError());\n }\n\n return Result.ok({\n fileId: file.id,\n imageBase64: contentsResult.value.buffer.toString(\"base64\"),\n imageMediaType: contentsResult.value.contentType,\n model: firstProvider.model,\n connection: {\n sdkName: firstProvider.model.split(\"/\")[0],\n apiKey: await this.encryption.decrypt(firstProvider.apiKeyEncrypted)\n }\n });\n }\n}\n\nexport const PrepareImageEnrichmentUseCase = UseCaseAbstraction.createImplementation({\n implementation: PrepareImageEnrichmentUseCaseImpl,\n dependencies: [GetFileUseCase, GetFileContentsByIdUseCase, GetSettingsUseCase, Encryption]\n});\n"],"names":["PrepareImageEnrichmentUseCaseImpl","getFile","getFileContents","getSettings","encryption","fileId","fileResult","Result","EnrichmentFileNotFoundError","file","EnrichmentNotAnImageError","contentsResult","EnrichmentFileContentsError","aiSettingsResult","EnrichmentNoProviderError","firstProvider","PrepareImageEnrichmentUseCase","UseCaseAbstraction","GetFileUseCase","GetFileContentsByIdUseCase","GetSettingsUseCase","Encryption"],"mappings":";;;;;;;AAiBA,MAAMA;IACF,YACYC,OAAiC,EACjCC,eAAqD,EACrDC,WAAyC,EACzCC,UAAgC,CAC1C;aAJUH,OAAO,GAAPA;aACAC,eAAe,GAAfA;aACAC,WAAW,GAAXA;aACAC,UAAU,GAAVA;IACT;IAEH,MAAM,QAAQC,MAAc,EAAmE;QAC3F,MAAMC,aAAa,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAACD;QAC9C,IAAIC,WAAW,MAAM,IACjB,OAAOC,OAAO,IAAI,CAAC,IAAIC,4BAA4BH;QAGvD,MAAMI,OAAOH,WAAW,KAAK;QAE7B,IAAI,CAACG,KAAK,IAAI,CAAC,UAAU,CAAC,WACtB,OAAOF,OAAO,IAAI,CAAC,IAAIG,0BAA0BD,KAAK,IAAI;QAO9D,MAAME,iBAAiB,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAACN;QAC1D,IAAIM,eAAe,MAAM,IACrB,OAAOJ,OAAO,IAAI,CAAC,IAAIK,4BAA4BD,eAAe,KAAK,CAAC,OAAO;QAGnF,MAAME,mBAAmB,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO;QACvD,IAAIA,iBAAiB,MAAM,IACvB,OAAON,OAAO,IAAI,CAAC,IAAIO;QAG3B,MAAMC,gBAAgBF,iBAAiB,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE;QACjE,IAAI,CAACE,eACD,OAAOR,OAAO,IAAI,CAAC,IAAIO;QAG3B,OAAOP,OAAO,EAAE,CAAC;YACb,QAAQE,KAAK,EAAE;YACf,aAAaE,eAAe,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC;YAClD,gBAAgBA,eAAe,KAAK,CAAC,WAAW;YAChD,OAAOI,cAAc,KAAK;YAC1B,YAAY;gBACR,SAASA,cAAc,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;gBAC1C,QAAQ,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAACA,cAAc,eAAe;YACvE;QACJ;IACJ;AACJ;AAEO,MAAMC,8DAAgCC,8BAAAA,oBAAuC,CAAC;IACjF,gBAAgBjB;IAChB,cAAc;QAACkB;QAAgBC;QAA4BC;QAAoBC;KAAW;AAC9F"}
1
+ {"version":3,"file":"api/features/AiImageEnrichment/PrepareImageEnrichmentUseCase.js","sources":["../../../../src/api/features/AiImageEnrichment/PrepareImageEnrichmentUseCase.ts"],"sourcesContent":["import { Result } from \"@webiny/feature/api\";\nimport { GetFileUseCase } from \"@webiny/api-file-manager/features/file/GetFile/index.js\";\nimport { GetFileContentsByIdUseCase } from \"@webiny/api-file-manager/features/file/GetFileContentsById/abstractions.js\";\nimport {\n PrepareImageEnrichmentUseCase as UseCaseAbstraction,\n type IPreparedImageEnrichment\n} from \"./abstractions.js\";\nimport {\n ResolveAiCapabilityUseCase,\n withAdditionalInstructions\n} from \"~/api/features/Capabilities/index.js\";\nimport { FM_IMAGE_ENRICHMENT_CAPABILITY } from \"./capability.js\";\nimport {\n EnrichmentFileContentsError,\n EnrichmentFileNotFoundError,\n EnrichmentNoProviderError,\n EnrichmentNotAnImageError\n} from \"./errors.js\";\nimport type { ImageEnrichmentError } from \"./errors.js\";\n\nclass PrepareImageEnrichmentUseCaseImpl implements UseCaseAbstraction.Interface {\n constructor(\n private getFile: GetFileUseCase.Interface,\n private getFileContents: GetFileContentsByIdUseCase.Interface,\n private resolveCapability: ResolveAiCapabilityUseCase.Interface\n ) {}\n\n async execute(fileId: string): Promise<Result<IPreparedImageEnrichment, ImageEnrichmentError>> {\n const fileResult = await this.getFile.execute(fileId);\n if (fileResult.isFail()) {\n return Result.fail(new EnrichmentFileNotFoundError(fileId));\n }\n\n const file = fileResult.value;\n\n if (!file.type.startsWith(\"image/\")) {\n return Result.fail(new EnrichmentNotAnImageError(file.type));\n }\n\n // Read the image bytes and send them to the AI as base64, NOT a URL. A URL forces the\n // provider to fetch the file — which fails for private/access-controlled files, leaks the\n // domain, and can't reach a non-public origin (e.g. local dev). base64 is a portable standard\n // that works on every setup. (Also resolves the AI-SDK \"image part\" deprecation.)\n const contentsResult = await this.getFileContents.execute(fileId);\n if (contentsResult.isFail()) {\n return Result.fail(new EnrichmentFileContentsError(contentsResult.error.message));\n }\n\n const resolved = await this.resolveCapability.execute(FM_IMAGE_ENRICHMENT_CAPABILITY);\n if (resolved.isFail()) {\n return Result.fail(new EnrichmentNoProviderError(resolved.error.message));\n }\n\n const capability = resolved.value;\n\n return Result.ok({\n fileId: file.id,\n imageBase64: contentsResult.value.buffer.toString(\"base64\"),\n imageMediaType: contentsResult.value.contentType,\n model: capability.model,\n prompt: withAdditionalInstructions(capability),\n connection: capability.connection\n });\n }\n}\n\nexport const PrepareImageEnrichmentUseCase = UseCaseAbstraction.createImplementation({\n implementation: PrepareImageEnrichmentUseCaseImpl,\n dependencies: [GetFileUseCase, GetFileContentsByIdUseCase, ResolveAiCapabilityUseCase]\n});\n"],"names":["PrepareImageEnrichmentUseCaseImpl","getFile","getFileContents","resolveCapability","fileId","fileResult","Result","EnrichmentFileNotFoundError","file","EnrichmentNotAnImageError","contentsResult","EnrichmentFileContentsError","resolved","FM_IMAGE_ENRICHMENT_CAPABILITY","EnrichmentNoProviderError","capability","withAdditionalInstructions","PrepareImageEnrichmentUseCase","UseCaseAbstraction","GetFileUseCase","GetFileContentsByIdUseCase","ResolveAiCapabilityUseCase"],"mappings":";;;;;;;AAoBA,MAAMA;IACF,YACYC,OAAiC,EACjCC,eAAqD,EACrDC,iBAAuD,CACjE;aAHUF,OAAO,GAAPA;aACAC,eAAe,GAAfA;aACAC,iBAAiB,GAAjBA;IACT;IAEH,MAAM,QAAQC,MAAc,EAAmE;QAC3F,MAAMC,aAAa,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAACD;QAC9C,IAAIC,WAAW,MAAM,IACjB,OAAOC,OAAO,IAAI,CAAC,IAAIC,4BAA4BH;QAGvD,MAAMI,OAAOH,WAAW,KAAK;QAE7B,IAAI,CAACG,KAAK,IAAI,CAAC,UAAU,CAAC,WACtB,OAAOF,OAAO,IAAI,CAAC,IAAIG,0BAA0BD,KAAK,IAAI;QAO9D,MAAME,iBAAiB,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAACN;QAC1D,IAAIM,eAAe,MAAM,IACrB,OAAOJ,OAAO,IAAI,CAAC,IAAIK,4BAA4BD,eAAe,KAAK,CAAC,OAAO;QAGnF,MAAME,WAAW,MAAM,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAACC;QACtD,IAAID,SAAS,MAAM,IACf,OAAON,OAAO,IAAI,CAAC,IAAIQ,0BAA0BF,SAAS,KAAK,CAAC,OAAO;QAG3E,MAAMG,aAAaH,SAAS,KAAK;QAEjC,OAAON,OAAO,EAAE,CAAC;YACb,QAAQE,KAAK,EAAE;YACf,aAAaE,eAAe,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC;YAClD,gBAAgBA,eAAe,KAAK,CAAC,WAAW;YAChD,OAAOK,WAAW,KAAK;YACvB,QAAQC,2BAA2BD;YACnC,YAAYA,WAAW,UAAU;QACrC;IACJ;AACJ;AAEO,MAAME,8DAAgCC,8BAAAA,oBAAuC,CAAC;IACjF,gBAAgBlB;IAChB,cAAc;QAACmB;QAAgBC;QAA4BC;KAA2B;AAC1F"}
@@ -29,6 +29,12 @@ export interface IPreparedImageEnrichment {
29
29
  imageBase64: string;
30
30
  imageMediaType: string;
31
31
  model: string;
32
+ /**
33
+ * The resolved prompt: this capability's guidance (or the project's replacement for it) with
34
+ * the project's additional instructions appended. `AI_ENRICHMENT_PROMPT` is the default that
35
+ * feeds it, not what gets sent.
36
+ */
37
+ prompt: string;
32
38
  connection: {
33
39
  sdkName: string;
34
40
  apiKey: string;
@@ -1 +1 @@
1
- {"version":3,"file":"api/features/AiImageEnrichment/abstractions.js","sources":["../../../../src/api/features/AiImageEnrichment/abstractions.ts"],"sourcesContent":["import { createAbstraction, Result } from \"@webiny/feature/api\";\nimport { z } from \"zod\";\nimport type { ImageEnrichmentError } from \"./errors.js\";\n\nexport const AI_ENRICHMENT_PROMPT =\n \"Analyze this image and return up to 5 lowercase descriptive tags and one short sentence describing the image.\";\n\n/**\n * The shape both entry points ask the model for. Exported as the bare zod schema rather than a\n * ready-made `Output.object(...)`: the AI SDK's `Output` type can't be named in emitted declarations\n * (TS4023), so each call site wraps this locally instead.\n */\nexport const aiEnrichmentSchema = z.object({\n tags: z.array(z.string()),\n description: z.string()\n});\n\n/** The enrichment result: what the model produces and what gets persisted. */\nexport interface IEnrichmentOutput {\n tags: string[];\n description: string;\n}\n\n/**\n * Everything needed to run the AI call, gathered up front: the image bytes, the resolved provider,\n * and the file's current tags (needed to merge rather than overwrite).\n *\n * Deliberately split from the AI call itself so both entry points share the same preparation and the\n * same failure modes — and so the streaming route can answer with a real HTTP status code for\n * anything knowable before the stream opens, instead of burying it in a 200 response.\n */\nexport interface IPreparedImageEnrichment {\n fileId: string;\n imageBase64: string;\n imageMediaType: string;\n model: string;\n connection: {\n sdkName: string;\n apiKey: string;\n };\n}\n\nexport interface IPrepareImageEnrichmentUseCase {\n execute(fileId: string): Promise<Result<IPreparedImageEnrichment, ImageEnrichmentError>>;\n}\n\nexport const PrepareImageEnrichmentUseCase = createAbstraction<IPrepareImageEnrichmentUseCase>(\n \"PrepareImageEnrichmentUseCase\"\n);\n\nexport namespace PrepareImageEnrichmentUseCase {\n export type Interface = IPrepareImageEnrichmentUseCase;\n}\n\nexport interface IApplyImageEnrichmentParams {\n fileId: string;\n tags: string[];\n description: string;\n}\n\nexport interface IAppliedImageEnrichment {\n fileId: string;\n tags: string[];\n description: string;\n}\n\nexport interface IApplyImageEnrichmentUseCase {\n execute(\n params: IApplyImageEnrichmentParams\n ): Promise<Result<IAppliedImageEnrichment, ImageEnrichmentError>>;\n}\n\nexport const ApplyImageEnrichmentUseCase = createAbstraction<IApplyImageEnrichmentUseCase>(\n \"ApplyImageEnrichmentUseCase\"\n);\n\nexport namespace ApplyImageEnrichmentUseCase {\n export type Interface = IApplyImageEnrichmentUseCase;\n}\n"],"names":["AI_ENRICHMENT_PROMPT","aiEnrichmentSchema","z","PrepareImageEnrichmentUseCase","createAbstraction","ApplyImageEnrichmentUseCase"],"mappings":";;AAIO,MAAMA,uBACT;AAOG,MAAMC,qBAAqBC,EAAE,MAAM,CAAC;IACvC,MAAMA,EAAE,KAAK,CAACA,EAAE,MAAM;IACtB,aAAaA,EAAE,MAAM;AACzB;AA+BO,MAAMC,gCAAgCC,kBACzC;AAyBG,MAAMC,8BAA8BD,kBACvC"}
1
+ {"version":3,"file":"api/features/AiImageEnrichment/abstractions.js","sources":["../../../../src/api/features/AiImageEnrichment/abstractions.ts"],"sourcesContent":["import { createAbstraction, Result } from \"@webiny/feature/api\";\nimport { z } from \"zod\";\nimport type { ImageEnrichmentError } from \"./errors.js\";\n\nexport const AI_ENRICHMENT_PROMPT =\n \"Analyze this image and return up to 5 lowercase descriptive tags and one short sentence describing the image.\";\n\n/**\n * The shape both entry points ask the model for. Exported as the bare zod schema rather than a\n * ready-made `Output.object(...)`: the AI SDK's `Output` type can't be named in emitted declarations\n * (TS4023), so each call site wraps this locally instead.\n */\nexport const aiEnrichmentSchema = z.object({\n tags: z.array(z.string()),\n description: z.string()\n});\n\n/** The enrichment result: what the model produces and what gets persisted. */\nexport interface IEnrichmentOutput {\n tags: string[];\n description: string;\n}\n\n/**\n * Everything needed to run the AI call, gathered up front: the image bytes, the resolved provider,\n * and the file's current tags (needed to merge rather than overwrite).\n *\n * Deliberately split from the AI call itself so both entry points share the same preparation and the\n * same failure modes — and so the streaming route can answer with a real HTTP status code for\n * anything knowable before the stream opens, instead of burying it in a 200 response.\n */\nexport interface IPreparedImageEnrichment {\n fileId: string;\n imageBase64: string;\n imageMediaType: string;\n model: string;\n /**\n * The resolved prompt: this capability's guidance (or the project's replacement for it) with\n * the project's additional instructions appended. `AI_ENRICHMENT_PROMPT` is the default that\n * feeds it, not what gets sent.\n */\n prompt: string;\n connection: {\n sdkName: string;\n apiKey: string;\n };\n}\n\nexport interface IPrepareImageEnrichmentUseCase {\n execute(fileId: string): Promise<Result<IPreparedImageEnrichment, ImageEnrichmentError>>;\n}\n\nexport const PrepareImageEnrichmentUseCase = createAbstraction<IPrepareImageEnrichmentUseCase>(\n \"PrepareImageEnrichmentUseCase\"\n);\n\nexport namespace PrepareImageEnrichmentUseCase {\n export type Interface = IPrepareImageEnrichmentUseCase;\n}\n\nexport interface IApplyImageEnrichmentParams {\n fileId: string;\n tags: string[];\n description: string;\n}\n\nexport interface IAppliedImageEnrichment {\n fileId: string;\n tags: string[];\n description: string;\n}\n\nexport interface IApplyImageEnrichmentUseCase {\n execute(\n params: IApplyImageEnrichmentParams\n ): Promise<Result<IAppliedImageEnrichment, ImageEnrichmentError>>;\n}\n\nexport const ApplyImageEnrichmentUseCase = createAbstraction<IApplyImageEnrichmentUseCase>(\n \"ApplyImageEnrichmentUseCase\"\n);\n\nexport namespace ApplyImageEnrichmentUseCase {\n export type Interface = IApplyImageEnrichmentUseCase;\n}\n"],"names":["AI_ENRICHMENT_PROMPT","aiEnrichmentSchema","z","PrepareImageEnrichmentUseCase","createAbstraction","ApplyImageEnrichmentUseCase"],"mappings":";;AAIO,MAAMA,uBACT;AAOG,MAAMC,qBAAqBC,EAAE,MAAM,CAAC;IACvC,MAAMA,EAAE,KAAK,CAACA,EAAE,MAAM;IACtB,aAAaA,EAAE,MAAM;AACzB;AAqCO,MAAMC,gCAAgCC,kBACzC;AAyBG,MAAMC,8BAA8BD,kBACvC"}
@@ -1,5 +1,5 @@
1
1
  import { Output } from "ai";
2
- import { AI_ENRICHMENT_PROMPT, aiEnrichmentSchema } from "./abstractions.js";
2
+ import { aiEnrichmentSchema } from "./abstractions.js";
3
3
  function buildEnrichmentAiRequest(prepared) {
4
4
  return {
5
5
  model: prepared.model,
@@ -18,7 +18,7 @@ function buildEnrichmentAiRequest(prepared) {
18
18
  },
19
19
  {
20
20
  type: "text",
21
- text: AI_ENRICHMENT_PROMPT
21
+ text: prepared.prompt
22
22
  }
23
23
  ]
24
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"api/features/AiImageEnrichment/buildEnrichmentAiRequest.js","sources":["../../../../src/api/features/AiImageEnrichment/buildEnrichmentAiRequest.ts"],"sourcesContent":["import { Output } from \"ai\";\nimport type { Ai } from \"@webiny/api-core/features/ai/index.js\";\nimport { AI_ENRICHMENT_PROMPT, aiEnrichmentSchema } from \"./abstractions.js\";\nimport type { IPreparedImageEnrichment } from \"./abstractions.js\";\n\n/**\n * Builds the AI request both enrichment entry points send — the background task via `generateText`\n * and the streaming route via `streamText`. Identical model, structured output, connection, and\n * message payload; only the call differs.\n *\n * This is also where `Output.object(...)` lives rather than on `IPreparedImageEnrichment`: the AI\n * SDK's `Output` type can't be named in emitted declarations (TS4023), so it has to be built inside a\n * function body, not held on an exported interface.\n */\nexport function buildEnrichmentAiRequest(\n prepared: IPreparedImageEnrichment\n): Ai.GenerateTextParams {\n return {\n model: prepared.model,\n output: Output.object({ schema: aiEnrichmentSchema }),\n connection: prepared.connection,\n messages: [\n {\n role: \"user\",\n content: [\n {\n // Sent as base64, not a URL: a URL forces the provider to fetch the file,\n // which fails for private files and can't reach a non-public origin.\n type: \"file\",\n data: prepared.imageBase64,\n mediaType: prepared.imageMediaType\n },\n {\n type: \"text\",\n text: AI_ENRICHMENT_PROMPT\n }\n ]\n }\n ]\n };\n}\n"],"names":["buildEnrichmentAiRequest","prepared","Output","aiEnrichmentSchema","AI_ENRICHMENT_PROMPT"],"mappings":";;AAcO,SAASA,yBACZC,QAAkC;IAElC,OAAO;QACH,OAAOA,SAAS,KAAK;QACrB,QAAQC,OAAO,MAAM,CAAC;YAAE,QAAQC;QAAmB;QACnD,YAAYF,SAAS,UAAU;QAC/B,UAAU;YACN;gBACI,MAAM;gBACN,SAAS;oBACL;wBAGI,MAAM;wBACN,MAAMA,SAAS,WAAW;wBAC1B,WAAWA,SAAS,cAAc;oBACtC;oBACA;wBACI,MAAM;wBACN,MAAMG;oBACV;iBACH;YACL;SACH;IACL;AACJ"}
1
+ {"version":3,"file":"api/features/AiImageEnrichment/buildEnrichmentAiRequest.js","sources":["../../../../src/api/features/AiImageEnrichment/buildEnrichmentAiRequest.ts"],"sourcesContent":["import { Output } from \"ai\";\nimport type { Ai } from \"@webiny/api-core/features/ai/index.js\";\nimport { aiEnrichmentSchema } from \"./abstractions.js\";\nimport type { IPreparedImageEnrichment } from \"./abstractions.js\";\n\n/**\n * Builds the AI request both enrichment entry points send — the background task via `generateText`\n * and the streaming route via `streamText`. Identical model, structured output, connection, and\n * message payload; only the call differs.\n *\n * This is also where `Output.object(...)` lives rather than on `IPreparedImageEnrichment`: the AI\n * SDK's `Output` type can't be named in emitted declarations (TS4023), so it has to be built inside a\n * function body, not held on an exported interface.\n */\nexport function buildEnrichmentAiRequest(\n prepared: IPreparedImageEnrichment\n): Ai.GenerateTextParams {\n return {\n model: prepared.model,\n output: Output.object({ schema: aiEnrichmentSchema }),\n connection: prepared.connection,\n messages: [\n {\n role: \"user\",\n content: [\n {\n // Sent as base64, not a URL: a URL forces the provider to fetch the file,\n // which fails for private files and can't reach a non-public origin.\n type: \"file\",\n data: prepared.imageBase64,\n mediaType: prepared.imageMediaType\n },\n {\n type: \"text\",\n text: prepared.prompt\n }\n ]\n }\n ]\n };\n}\n"],"names":["buildEnrichmentAiRequest","prepared","Output","aiEnrichmentSchema"],"mappings":";;AAcO,SAASA,yBACZC,QAAkC;IAElC,OAAO;QACH,OAAOA,SAAS,KAAK;QACrB,QAAQC,OAAO,MAAM,CAAC;YAAE,QAAQC;QAAmB;QACnD,YAAYF,SAAS,UAAU;QAC/B,UAAU;YACN;gBACI,MAAM;gBACN,SAAS;oBACL;wBAGI,MAAM;wBACN,MAAMA,SAAS,WAAW;wBAC1B,WAAWA,SAAS,cAAc;oBACtC;oBACA;wBACI,MAAM;wBACN,MAAMA,SAAS,MAAM;oBACzB;iBACH;YACL;SACH;IACL;AACJ"}
@@ -0,0 +1,18 @@
1
+ import { AiCapability } from "../../../api/features/Capabilities/index.js";
2
+ export declare const FM_IMAGE_ENRICHMENT_CAPABILITY = "fm.imageEnrichment";
3
+ declare class FmImageEnrichmentCapabilityImpl implements AiCapability.Interface {
4
+ readonly id = "fm.imageEnrichment";
5
+ readonly label = "Image enrichment";
6
+ readonly description = "Reads an uploaded image and writes its alt text, title and tags. Needs a model that accepts image input.";
7
+ readonly defaultRole: "vision";
8
+ /**
9
+ * The one prompt here whose structure does not come from the prose: `Output.object(...)` in
10
+ * `buildEnrichmentAiRequest` enforces the tags/description shape. Projects still adjust it by
11
+ * appending, which is where house tag vocabularies go ("use our taxonomy", "British spelling").
12
+ */
13
+ readonly guidance = "Analyze this image and return up to 5 lowercase descriptive tags and one short sentence describing the image.";
14
+ }
15
+ export declare const FmImageEnrichmentCapability: typeof FmImageEnrichmentCapabilityImpl & {
16
+ __abstraction: import("@webiny/di").Abstraction<import("~/api/features/Capabilities/index.js").IAiCapability>;
17
+ };
18
+ export {};
@@ -0,0 +1,19 @@
1
+ import { AiCapability } from "../Capabilities/index.js";
2
+ import { AI_ENRICHMENT_PROMPT } from "./abstractions.js";
3
+ const FM_IMAGE_ENRICHMENT_CAPABILITY = "fm.imageEnrichment";
4
+ class FmImageEnrichmentCapabilityImpl {
5
+ constructor(){
6
+ this.id = FM_IMAGE_ENRICHMENT_CAPABILITY;
7
+ this.label = "Image enrichment";
8
+ this.description = "Reads an uploaded image and writes its alt text, title and tags. Needs a model that accepts image input.";
9
+ this.defaultRole = "vision";
10
+ this.guidance = AI_ENRICHMENT_PROMPT;
11
+ }
12
+ }
13
+ const FmImageEnrichmentCapability = AiCapability.createImplementation({
14
+ implementation: FmImageEnrichmentCapabilityImpl,
15
+ dependencies: []
16
+ });
17
+ export { FM_IMAGE_ENRICHMENT_CAPABILITY, FmImageEnrichmentCapability };
18
+
19
+ //# sourceMappingURL=capability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api/features/AiImageEnrichment/capability.js","sources":["../../../../src/api/features/AiImageEnrichment/capability.ts"],"sourcesContent":["import { AiCapability } from \"~/api/features/Capabilities/index.js\";\nimport { AI_ENRICHMENT_PROMPT } from \"./abstractions.js\";\n\nexport const FM_IMAGE_ENRICHMENT_CAPABILITY = \"fm.imageEnrichment\";\n\nclass FmImageEnrichmentCapabilityImpl implements AiCapability.Interface {\n readonly id = FM_IMAGE_ENRICHMENT_CAPABILITY;\n readonly label = \"Image enrichment\";\n readonly description =\n \"Reads an uploaded image and writes its alt text, title and tags. Needs a model that accepts image input.\";\n readonly defaultRole = \"vision\" as const;\n /**\n * The one prompt here whose structure does not come from the prose: `Output.object(...)` in\n * `buildEnrichmentAiRequest` enforces the tags/description shape. Projects still adjust it by\n * appending, which is where house tag vocabularies go (\"use our taxonomy\", \"British spelling\").\n */\n readonly guidance = AI_ENRICHMENT_PROMPT;\n}\n\nexport const FmImageEnrichmentCapability = AiCapability.createImplementation({\n implementation: FmImageEnrichmentCapabilityImpl,\n dependencies: []\n});\n"],"names":["FM_IMAGE_ENRICHMENT_CAPABILITY","FmImageEnrichmentCapabilityImpl","AI_ENRICHMENT_PROMPT","FmImageEnrichmentCapability","AiCapability"],"mappings":";;AAGO,MAAMA,iCAAiC;AAE9C,MAAMC;;aACO,EAAE,GAAGD;aACL,KAAK,GAAG;aACR,WAAW,GAChB;aACK,WAAW,GAAG;QAKtB,KACQ,QAAQ,GAAGE;;AACxB;AAEO,MAAMC,8BAA8BC,aAAa,oBAAoB,CAAC;IACzE,gBAAgBH;IAChB,cAAc,EAAE;AACpB"}
@@ -17,7 +17,12 @@ export declare class EnrichmentFileContentsError extends Error {
17
17
  }
18
18
  export declare class EnrichmentNoProviderError extends Error {
19
19
  readonly code: "ENRICHMENT_NO_AI_PROVIDER";
20
- constructor();
20
+ /**
21
+ * The reason comes from capability resolution, which knows which of the several ways this can
22
+ * fail actually happened: no model in the Vision role, a deleted connection, a missing key.
23
+ * Passing it through beats replacing all of them with one generic sentence.
24
+ */
25
+ constructor(reason?: string);
21
26
  }
22
27
  export declare class EnrichmentPersistError extends Error {
23
28
  readonly code: "ENRICHMENT_PERSIST_FAILED";
@@ -14,8 +14,8 @@ class EnrichmentFileContentsError extends Error {
14
14
  }
15
15
  }
16
16
  class EnrichmentNoProviderError extends Error {
17
- constructor(){
18
- super("No AI provider configured. Add a provider in AI Power Ups settings."), this.code = "ENRICHMENT_NO_AI_PROVIDER";
17
+ constructor(reason){
18
+ super(reason ?? "No AI model is configured for image enrichment. Pick one under Settings → AI Power-Ups → Model roles."), this.code = "ENRICHMENT_NO_AI_PROVIDER";
19
19
  }
20
20
  }
21
21
  class EnrichmentPersistError extends Error {
@@ -1 +1 @@
1
- {"version":3,"file":"api/features/AiImageEnrichment/errors.js","sources":["../../../../src/api/features/AiImageEnrichment/errors.ts"],"sourcesContent":["/**\n * Errors shared by every image-enrichment entry point (the background task and the streaming HTTP\n * route). Each carries a `code` so callers can map it to their own transport: the task turns them\n * into task results, the route into HTTP status codes.\n */\nexport class EnrichmentFileNotFoundError extends Error {\n readonly code = \"ENRICHMENT_FILE_NOT_FOUND\" as const;\n\n constructor(fileId: string) {\n super(`File not found: ${fileId}`);\n }\n}\n\nexport class EnrichmentNotAnImageError extends Error {\n readonly code = \"ENRICHMENT_NOT_AN_IMAGE\" as const;\n\n constructor(type: string) {\n super(`File is not an image (received \"${type}\"); skipping AI enrichment.`);\n }\n}\n\nexport class EnrichmentFileContentsError extends Error {\n readonly code = \"ENRICHMENT_FILE_CONTENTS_UNREADABLE\" as const;\n\n constructor(reason: string) {\n super(`Unable to read file contents: ${reason}`);\n }\n}\n\nexport class EnrichmentNoProviderError extends Error {\n readonly code = \"ENRICHMENT_NO_AI_PROVIDER\" as const;\n\n constructor() {\n super(\"No AI provider configured. Add a provider in AI Power Ups settings.\");\n }\n}\n\nexport class EnrichmentPersistError extends Error {\n readonly code = \"ENRICHMENT_PERSIST_FAILED\" as const;\n\n constructor(reason: string) {\n super(`Failed to update file: ${reason}`);\n }\n}\n\nexport type ImageEnrichmentError =\n | EnrichmentFileNotFoundError\n | EnrichmentNotAnImageError\n | EnrichmentFileContentsError\n | EnrichmentNoProviderError\n | EnrichmentPersistError;\n"],"names":["EnrichmentFileNotFoundError","Error","fileId","EnrichmentNotAnImageError","type","EnrichmentFileContentsError","reason","EnrichmentNoProviderError","EnrichmentPersistError"],"mappings":"AAKO,MAAMA,oCAAoCC;IAG7C,YAAYC,MAAc,CAAE;QACxB,KAAK,CAAC,CAAC,gBAAgB,EAAEA,QAAQ,QAH5B,IAAI,GAAG;IAIhB;AACJ;AAEO,MAAMC,kCAAkCF;IAG3C,YAAYG,IAAY,CAAE;QACtB,KAAK,CAAC,CAAC,gCAAgC,EAAEA,KAAK,2BAA2B,CAAC,QAHrE,IAAI,GAAG;IAIhB;AACJ;AAEO,MAAMC,oCAAoCJ;IAG7C,YAAYK,MAAc,CAAE;QACxB,KAAK,CAAC,CAAC,8BAA8B,EAAEA,QAAQ,QAH1C,IAAI,GAAG;IAIhB;AACJ;AAEO,MAAMC,kCAAkCN;IAG3C,aAAc;QACV,KAAK,CAAC,6EAHD,IAAI,GAAG;IAIhB;AACJ;AAEO,MAAMO,+BAA+BP;IAGxC,YAAYK,MAAc,CAAE;QACxB,KAAK,CAAC,CAAC,uBAAuB,EAAEA,QAAQ,QAHnC,IAAI,GAAG;IAIhB;AACJ"}
1
+ {"version":3,"file":"api/features/AiImageEnrichment/errors.js","sources":["../../../../src/api/features/AiImageEnrichment/errors.ts"],"sourcesContent":["/**\n * Errors shared by every image-enrichment entry point (the background task and the streaming HTTP\n * route). Each carries a `code` so callers can map it to their own transport: the task turns them\n * into task results, the route into HTTP status codes.\n */\nexport class EnrichmentFileNotFoundError extends Error {\n readonly code = \"ENRICHMENT_FILE_NOT_FOUND\" as const;\n\n constructor(fileId: string) {\n super(`File not found: ${fileId}`);\n }\n}\n\nexport class EnrichmentNotAnImageError extends Error {\n readonly code = \"ENRICHMENT_NOT_AN_IMAGE\" as const;\n\n constructor(type: string) {\n super(`File is not an image (received \"${type}\"); skipping AI enrichment.`);\n }\n}\n\nexport class EnrichmentFileContentsError extends Error {\n readonly code = \"ENRICHMENT_FILE_CONTENTS_UNREADABLE\" as const;\n\n constructor(reason: string) {\n super(`Unable to read file contents: ${reason}`);\n }\n}\n\nexport class EnrichmentNoProviderError extends Error {\n readonly code = \"ENRICHMENT_NO_AI_PROVIDER\" as const;\n\n /**\n * The reason comes from capability resolution, which knows which of the several ways this can\n * fail actually happened: no model in the Vision role, a deleted connection, a missing key.\n * Passing it through beats replacing all of them with one generic sentence.\n */\n constructor(reason?: string) {\n super(\n reason ??\n \"No AI model is configured for image enrichment. Pick one under Settings → AI Power-Ups → Model roles.\"\n );\n }\n}\n\nexport class EnrichmentPersistError extends Error {\n readonly code = \"ENRICHMENT_PERSIST_FAILED\" as const;\n\n constructor(reason: string) {\n super(`Failed to update file: ${reason}`);\n }\n}\n\nexport type ImageEnrichmentError =\n | EnrichmentFileNotFoundError\n | EnrichmentNotAnImageError\n | EnrichmentFileContentsError\n | EnrichmentNoProviderError\n | EnrichmentPersistError;\n"],"names":["EnrichmentFileNotFoundError","Error","fileId","EnrichmentNotAnImageError","type","EnrichmentFileContentsError","reason","EnrichmentNoProviderError","EnrichmentPersistError"],"mappings":"AAKO,MAAMA,oCAAoCC;IAG7C,YAAYC,MAAc,CAAE;QACxB,KAAK,CAAC,CAAC,gBAAgB,EAAEA,QAAQ,QAH5B,IAAI,GAAG;IAIhB;AACJ;AAEO,MAAMC,kCAAkCF;IAG3C,YAAYG,IAAY,CAAE;QACtB,KAAK,CAAC,CAAC,gCAAgC,EAAEA,KAAK,2BAA2B,CAAC,QAHrE,IAAI,GAAG;IAIhB;AACJ;AAEO,MAAMC,oCAAoCJ;IAG7C,YAAYK,MAAc,CAAE;QACxB,KAAK,CAAC,CAAC,8BAA8B,EAAEA,QAAQ,QAH1C,IAAI,GAAG;IAIhB;AACJ;AAEO,MAAMC,kCAAkCN;IAQ3C,YAAYK,MAAe,CAAE;QACzB,KAAK,CACDA,UACI,+GAVH,IAAI,GAAG;IAYhB;AACJ;AAEO,MAAME,+BAA+BP;IAGxC,YAAYK,MAAc,CAAE;QACxB,KAAK,CAAC,CAAC,uBAAuB,EAAEA,QAAQ,QAHnC,IAAI,GAAG;IAIhB;AACJ"}
@@ -5,11 +5,13 @@ import { AiImageEnrichmentTask } from "./AiImageEnrichmentTask.js";
5
5
  import { AiImageEnrichmentStreamRouteDefinition } from "./AiImageEnrichmentStreamRoute.js";
6
6
  import { PrepareImageEnrichmentUseCase } from "./PrepareImageEnrichmentUseCase.js";
7
7
  import { ApplyImageEnrichmentUseCase } from "./ApplyImageEnrichmentUseCase.js";
8
+ import { FmImageEnrichmentCapability } from "./capability.js";
8
9
  const AiImageEnrichmentFeature = createFeature({
9
10
  name: "AiPowerUps/AiImageEnrichment",
10
11
  register (container) {
11
12
  const enabled = container.resolve(FeatureFlags).get().isEnabled("aiPowerups.fileManager.imageEnrichment");
12
13
  if (!enabled) return;
14
+ container.register(FmImageEnrichmentCapability);
13
15
  container.register(PrepareImageEnrichmentUseCase);
14
16
  container.register(ApplyImageEnrichmentUseCase);
15
17
  container.register(AiImageEnrichmentAfterCreateHandler);
@@ -1 +1 @@
1
- {"version":3,"file":"api/features/AiImageEnrichment/feature.js","sources":["../../../../src/api/features/AiImageEnrichment/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { FeatureFlags } from \"@webiny/api-core/features/featureFlags/abstractions.js\";\nimport { AiImageEnrichmentAfterCreateHandler } from \"./AiImageEnrichmentAfterCreateHandler.js\";\nimport { AiImageEnrichmentTask } from \"./AiImageEnrichmentTask.js\";\nimport { AiImageEnrichmentStreamRouteDefinition } from \"./AiImageEnrichmentStreamRoute.js\";\nimport { PrepareImageEnrichmentUseCase } from \"./PrepareImageEnrichmentUseCase.js\";\nimport { ApplyImageEnrichmentUseCase } from \"./ApplyImageEnrichmentUseCase.js\";\n\nexport const AiImageEnrichmentFeature = createFeature({\n name: \"AiPowerUps/AiImageEnrichment\",\n register(container) {\n // Register-time gate on the effective feature flags (userFlag && live WCP license). Valid at\n // register() time because the license is refreshed PRE-register (WcpLicenseLoader.load() in\n // registerApiRequestStack) and FeatureFlags reads that process cache — and it re-evaluates per\n // request since the child re-registers, so a license change takes effect on the next request.\n const enabled = container\n .resolve(FeatureFlags)\n .get()\n .isEnabled(\"aiPowerups.fileManager.imageEnrichment\");\n\n if (!enabled) {\n return;\n }\n\n container.register(PrepareImageEnrichmentUseCase);\n container.register(ApplyImageEnrichmentUseCase);\n\n // Automatic enrichment after a file is created.\n container.register(AiImageEnrichmentAfterCreateHandler);\n container.register(AiImageEnrichmentTask);\n\n // On-demand re-enrichment, streamed.\n container.register(AiImageEnrichmentStreamRouteDefinition);\n }\n});\n"],"names":["AiImageEnrichmentFeature","createFeature","container","enabled","FeatureFlags","PrepareImageEnrichmentUseCase","ApplyImageEnrichmentUseCase","AiImageEnrichmentAfterCreateHandler","AiImageEnrichmentTask","AiImageEnrichmentStreamRouteDefinition"],"mappings":";;;;;;;AAQO,MAAMA,2BAA2BC,cAAc;IAClD,MAAM;IACN,UAASC,SAAS;QAKd,MAAMC,UAAUD,UACX,OAAO,CAACE,cACR,GAAG,GACH,SAAS,CAAC;QAEf,IAAI,CAACD,SACD;QAGJD,UAAU,QAAQ,CAACG;QACnBH,UAAU,QAAQ,CAACI;QAGnBJ,UAAU,QAAQ,CAACK;QACnBL,UAAU,QAAQ,CAACM;QAGnBN,UAAU,QAAQ,CAACO;IACvB;AACJ"}
1
+ {"version":3,"file":"api/features/AiImageEnrichment/feature.js","sources":["../../../../src/api/features/AiImageEnrichment/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { FeatureFlags } from \"@webiny/api-core/features/featureFlags/abstractions.js\";\nimport { AiImageEnrichmentAfterCreateHandler } from \"./AiImageEnrichmentAfterCreateHandler.js\";\nimport { AiImageEnrichmentTask } from \"./AiImageEnrichmentTask.js\";\nimport { AiImageEnrichmentStreamRouteDefinition } from \"./AiImageEnrichmentStreamRoute.js\";\nimport { PrepareImageEnrichmentUseCase } from \"./PrepareImageEnrichmentUseCase.js\";\nimport { ApplyImageEnrichmentUseCase } from \"./ApplyImageEnrichmentUseCase.js\";\nimport { FmImageEnrichmentCapability } from \"./capability.js\";\n\nexport const AiImageEnrichmentFeature = createFeature({\n name: \"AiPowerUps/AiImageEnrichment\",\n register(container) {\n // Register-time gate on the effective feature flags (userFlag && live WCP license). Valid at\n // register() time because the license is refreshed PRE-register (WcpLicenseLoader.load() in\n // registerApiRequestStack) and FeatureFlags reads that process cache — and it re-evaluates per\n // request since the child re-registers, so a license change takes effect on the next request.\n const enabled = container\n .resolve(FeatureFlags)\n .get()\n .isEnabled(\"aiPowerups.fileManager.imageEnrichment\");\n\n if (!enabled) {\n return;\n }\n\n // Registered inside the gate, so a project without the license does not get a settings\n // row for a feature it cannot use.\n container.register(FmImageEnrichmentCapability);\n container.register(PrepareImageEnrichmentUseCase);\n container.register(ApplyImageEnrichmentUseCase);\n\n // Automatic enrichment after a file is created.\n container.register(AiImageEnrichmentAfterCreateHandler);\n container.register(AiImageEnrichmentTask);\n\n // On-demand re-enrichment, streamed.\n container.register(AiImageEnrichmentStreamRouteDefinition);\n }\n});\n"],"names":["AiImageEnrichmentFeature","createFeature","container","enabled","FeatureFlags","FmImageEnrichmentCapability","PrepareImageEnrichmentUseCase","ApplyImageEnrichmentUseCase","AiImageEnrichmentAfterCreateHandler","AiImageEnrichmentTask","AiImageEnrichmentStreamRouteDefinition"],"mappings":";;;;;;;;AASO,MAAMA,2BAA2BC,cAAc;IAClD,MAAM;IACN,UAASC,SAAS;QAKd,MAAMC,UAAUD,UACX,OAAO,CAACE,cACR,GAAG,GACH,SAAS,CAAC;QAEf,IAAI,CAACD,SACD;QAKJD,UAAU,QAAQ,CAACG;QACnBH,UAAU,QAAQ,CAACI;QACnBJ,UAAU,QAAQ,CAACK;QAGnBL,UAAU,QAAQ,CAACM;QACnBN,UAAU,QAAQ,CAACO;QAGnBP,UAAU,QAAQ,CAACQ;IACvB;AACJ"}
@@ -0,0 +1,28 @@
1
+ import { z } from "zod";
2
+ import { AiPowerUpsSettingsGroupHandler } from "../../../api/features/shared/index.js";
3
+ import type { CapabilitiesSettings } from "./types.js";
4
+ import type { PersistedCapabilities } from "./types.js";
5
+ declare class CapabilitiesHandlerImpl implements AiPowerUpsSettingsGroupHandler.Interface {
6
+ readonly name = "capabilities";
7
+ readonly inputSchema: z.ZodObject<{
8
+ items: z.ZodRecord<z.ZodString, z.ZodObject<{
9
+ enabled: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
10
+ overrides: z.ZodOptional<z.ZodNullable<z.ZodObject<{
11
+ roleId: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodEnum<{
12
+ fast: "fast";
13
+ standard: "standard";
14
+ vision: "vision";
15
+ }>, z.ZodLiteral<"">]>>>;
16
+ connectionId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
17
+ model: z.ZodOptional<z.ZodNullable<z.ZodString>>;
18
+ additionalInstructions: z.ZodOptional<z.ZodNullable<z.ZodString>>;
19
+ }, z.core.$strip>>>;
20
+ }, z.core.$strip>>;
21
+ }, z.core.$strip>;
22
+ mapFromStorage(persisted: unknown): CapabilitiesSettings;
23
+ mapToStorage(internal: unknown): Promise<PersistedCapabilities>;
24
+ }
25
+ declare const _default: typeof CapabilitiesHandlerImpl & {
26
+ __abstraction: import("@webiny/di").Abstraction<import("../shared/abstractions.js").IAiPowerUpsSettingsGroupHandler>;
27
+ };
28
+ export default _default;
@@ -0,0 +1,56 @@
1
+ import { z } from "zod";
2
+ import { AiPowerUpsSettingsGroupHandler } from "../shared/index.js";
3
+ import { AI_MODEL_ROLE_IDS } from "../ModelRoles/index.js";
4
+ const overrideSchema = z.object({
5
+ roleId: z.union([
6
+ z["enum"](AI_MODEL_ROLE_IDS),
7
+ z.literal("")
8
+ ]).nullish(),
9
+ connectionId: z.string().nullish(),
10
+ model: z.string().nullish(),
11
+ additionalInstructions: z.string().nullish()
12
+ });
13
+ const entrySchema = z.object({
14
+ enabled: z.boolean().nullish(),
15
+ overrides: overrideSchema.nullish()
16
+ });
17
+ const inputSchema = z.object({
18
+ items: z.record(z.string(), entrySchema)
19
+ });
20
+ const dropEmptyValues = (override)=>Object.fromEntries(Object.entries(override).filter(([, value])=>{
21
+ if (null == value) return false;
22
+ return "string" == typeof value ? "" !== value.trim() : true;
23
+ }));
24
+ const normaliseEnabled = (enabled)=>false !== enabled;
25
+ class CapabilitiesHandlerImpl {
26
+ mapFromStorage(persisted) {
27
+ const stored = persisted ?? {};
28
+ return {
29
+ items: stored.items ?? {}
30
+ };
31
+ }
32
+ async mapToStorage(internal) {
33
+ const input = internal;
34
+ const entries = Object.entries(input.items ?? {}).filter((entry)=>Boolean(entry[1])).map(([id, entry])=>[
35
+ id,
36
+ {
37
+ enabled: normaliseEnabled(entry.enabled),
38
+ overrides: dropEmptyValues(entry.overrides ?? {})
39
+ }
40
+ ]);
41
+ return {
42
+ items: Object.fromEntries(entries)
43
+ };
44
+ }
45
+ constructor(){
46
+ this.name = "capabilities";
47
+ this.inputSchema = inputSchema;
48
+ }
49
+ }
50
+ const CapabilitiesHandler = AiPowerUpsSettingsGroupHandler.createImplementation({
51
+ implementation: CapabilitiesHandlerImpl,
52
+ dependencies: []
53
+ });
54
+ export default CapabilitiesHandler;
55
+
56
+ //# sourceMappingURL=CapabilitiesHandler.js.map