@aglyn/plugins-ai 1.0.0-beta.231 → 1.0.0-beta.232

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 (220) hide show
  1. package/package.json +17 -17
  2. package/src/lib/activity/ai-activity-actions.js +8 -4
  3. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  4. package/src/lib/components/ai-job-follow.component.js +1 -0
  5. package/src/lib/components/ai-job-follow.component.js.map +1 -1
  6. package/src/lib/components/ai-job-links.js +13 -0
  7. package/src/lib/components/ai-job-links.js.map +1 -1
  8. package/src/lib/components/ai-jobs-list-page.component.js +1 -0
  9. package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
  10. package/src/lib/components/ai-media-create.component.d.ts +20 -7
  11. package/src/lib/components/ai-media-create.component.js +107 -55
  12. package/src/lib/components/ai-media-create.component.js.map +1 -1
  13. package/src/lib/components/ai-seo-fields-card.component.js +1 -0
  14. package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
  15. package/src/lib/components/ai-site-memory-card.component.d.ts +32 -0
  16. package/src/lib/components/ai-site-memory-card.component.js +187 -0
  17. package/src/lib/components/ai-site-memory-card.component.js.map +1 -0
  18. package/src/lib/components/ai-site-start-card.component.js +80 -77
  19. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  20. package/src/lib/components/ai-theme-proposal-card.component.js +4 -0
  21. package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
  22. package/src/lib/components/ai-usage-strip.component.js +1 -0
  23. package/src/lib/components/ai-usage-strip.component.js.map +1 -1
  24. package/src/lib/components/assist-panel.component.d.ts +10 -0
  25. package/src/lib/components/assist-panel.component.js +119 -9
  26. package/src/lib/components/assist-panel.component.js.map +1 -1
  27. package/src/lib/components/assist-signals-page.component.js +2 -0
  28. package/src/lib/components/assist-signals-page.component.js.map +1 -1
  29. package/src/lib/components/billing-ai-allotments.component.js +3 -0
  30. package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
  31. package/src/lib/components/billing-ai-top-users.component.js +1 -0
  32. package/src/lib/components/billing-ai-top-users.component.js.map +1 -1
  33. package/src/lib/components/billing-assist-overage-card.component.js +2 -0
  34. package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
  35. package/src/lib/components/member-ai-usage-card.component.js +10 -0
  36. package/src/lib/components/member-ai-usage-card.component.js.map +1 -1
  37. package/src/lib/components/staff-org-ai-card.component.js +1 -0
  38. package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
  39. package/src/lib/components/staff-org-ai-conversations.component.d.ts +23 -0
  40. package/src/lib/components/staff-org-ai-conversations.component.js +346 -0
  41. package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -0
  42. package/src/lib/jobs/ai-build-capabilities.d.ts +13 -1
  43. package/src/lib/jobs/ai-build-capabilities.js +101 -7
  44. package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
  45. package/src/lib/jobs/ai-business-profile-prefill.d.ts +29 -0
  46. package/src/lib/jobs/ai-business-profile-prefill.js +85 -0
  47. package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -0
  48. package/src/lib/jobs/ai-job-build-step.js +3 -1
  49. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  50. package/src/lib/jobs/ai-job-edit-step.d.ts +225 -0
  51. package/src/lib/jobs/ai-job-edit-step.js +631 -0
  52. package/src/lib/jobs/ai-job-edit-step.js.map +1 -0
  53. package/src/lib/jobs/ai-job-insight-step.js +21 -4
  54. package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
  55. package/src/lib/jobs/ai-job-layout-language.d.ts +8 -3
  56. package/src/lib/jobs/ai-job-layout-language.js +8 -2
  57. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  58. package/src/lib/jobs/ai-job-layout-step.js +7 -6
  59. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  60. package/src/lib/jobs/ai-job-logic-step.d.ts +20 -0
  61. package/src/lib/jobs/ai-job-logic-step.js +87 -6
  62. package/src/lib/jobs/ai-job-logic-step.js.map +1 -1
  63. package/src/lib/jobs/ai-job-page-language.d.ts +52 -7
  64. package/src/lib/jobs/ai-job-page-language.js +72 -9
  65. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  66. package/src/lib/jobs/ai-job-page-step.js +12 -2
  67. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  68. package/src/lib/jobs/ai-job-plan-step.d.ts +19 -5
  69. package/src/lib/jobs/ai-job-plan-step.js +54 -20
  70. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  71. package/src/lib/jobs/ai-job-site-content.d.ts +163 -0
  72. package/src/lib/jobs/ai-job-site-content.js +443 -0
  73. package/src/lib/jobs/ai-job-site-content.js.map +1 -0
  74. package/src/lib/jobs/ai-job-site-look.d.ts +112 -0
  75. package/src/lib/jobs/ai-job-site-look.js +322 -0
  76. package/src/lib/jobs/ai-job-site-look.js.map +1 -0
  77. package/src/lib/jobs/ai-job-site-step.d.ts +26 -5
  78. package/src/lib/jobs/ai-job-site-step.js +163 -24
  79. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  80. package/src/lib/jobs/ai-jobs-notify.d.ts +6 -1
  81. package/src/lib/jobs/ai-jobs-notify.js +15 -3
  82. package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
  83. package/src/lib/layout-language/ai-layout-compiler.d.ts +9 -0
  84. package/src/lib/layout-language/ai-layout-compiler.js +120 -33
  85. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  86. package/src/lib/layout-language/ai-layout-frame.js +5 -4
  87. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  88. package/src/lib/layout-language/ai-layout-language.d.ts +12 -5
  89. package/src/lib/layout-language/ai-layout-language.js +92 -9
  90. package/src/lib/layout-language/ai-layout-language.js.map +1 -1
  91. package/src/lib/layout-language/ai-layout-pictures.d.ts +144 -0
  92. package/src/lib/layout-language/ai-layout-pictures.js +214 -0
  93. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -0
  94. package/src/lib/layout-language/ai-layout-store.d.ts +2 -0
  95. package/src/lib/layout-language/ai-layout-store.js +7 -1
  96. package/src/lib/layout-language/ai-layout-store.js.map +1 -1
  97. package/src/lib/model/ai-build-progress.js +4 -1
  98. package/src/lib/model/ai-build-progress.js.map +1 -1
  99. package/src/lib/model/ai-edit-job.d.ts +109 -0
  100. package/src/lib/model/ai-edit-job.js +157 -0
  101. package/src/lib/model/ai-edit-job.js.map +1 -0
  102. package/src/lib/model/ai-job-activity.js +1 -0
  103. package/src/lib/model/ai-job-activity.js.map +1 -1
  104. package/src/lib/model/ai-jobs.types.d.ts +3 -1
  105. package/src/lib/model/ai-jobs.types.js.map +1 -1
  106. package/src/lib/model/ai-logic-job.d.ts +18 -1
  107. package/src/lib/model/ai-logic-job.js +24 -3
  108. package/src/lib/model/ai-logic-job.js.map +1 -1
  109. package/src/lib/model/ai-media-credits.d.ts +8 -4
  110. package/src/lib/model/ai-media-credits.js +11 -4
  111. package/src/lib/model/ai-media-credits.js.map +1 -1
  112. package/src/lib/model/ai-media-kinds.d.ts +68 -0
  113. package/src/lib/model/ai-media-kinds.js +98 -0
  114. package/src/lib/model/ai-media-kinds.js.map +1 -0
  115. package/src/lib/model/ai-site-build-progress.d.ts +4 -0
  116. package/src/lib/model/ai-site-build-progress.js +28 -11
  117. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  118. package/src/lib/model/ai-site-context.d.ts +111 -0
  119. package/src/lib/model/ai-site-context.js +222 -0
  120. package/src/lib/model/ai-site-context.js.map +1 -0
  121. package/src/lib/model/ai-site-job.d.ts +10 -2
  122. package/src/lib/model/ai-site-job.js +12 -5
  123. package/src/lib/model/ai-site-job.js.map +1 -1
  124. package/src/lib/model/ai-site-kinds.d.ts +164 -0
  125. package/src/lib/model/ai-site-kinds.js +1942 -0
  126. package/src/lib/model/ai-site-kinds.js.map +1 -0
  127. package/src/lib/model/ai-site-look.d.ts +150 -0
  128. package/src/lib/model/ai-site-look.js +1236 -0
  129. package/src/lib/model/ai-site-look.js.map +1 -0
  130. package/src/lib/model/ai-site-memory.d.ts +80 -0
  131. package/src/lib/model/ai-site-memory.js +171 -0
  132. package/src/lib/model/ai-site-memory.js.map +1 -0
  133. package/src/lib/model/ai-site-start.d.ts +10 -1
  134. package/src/lib/model/ai-site-start.js +16 -2
  135. package/src/lib/model/ai-site-start.js.map +1 -1
  136. package/src/lib/model/assist-follow-up.d.ts +92 -0
  137. package/src/lib/model/assist-follow-up.js +128 -0
  138. package/src/lib/model/assist-follow-up.js.map +1 -0
  139. package/src/lib/plugin-config.js +2 -0
  140. package/src/lib/plugin-config.js.map +1 -1
  141. package/src/lib/plugin.js +20 -1
  142. package/src/lib/plugin.js.map +1 -1
  143. package/src/lib/providers/anthropic.d.ts +9 -1
  144. package/src/lib/providers/anthropic.js +143 -9
  145. package/src/lib/providers/anthropic.js.map +1 -1
  146. package/src/lib/providers/catalog.d.ts +37 -7
  147. package/src/lib/providers/catalog.js +103 -11
  148. package/src/lib/providers/catalog.js.map +1 -1
  149. package/src/lib/providers/contract.d.ts +29 -0
  150. package/src/lib/providers/contract.js.map +1 -1
  151. package/src/lib/providers/image-contract.d.ts +22 -1
  152. package/src/lib/providers/image-contract.js +40 -1
  153. package/src/lib/providers/image-contract.js.map +1 -1
  154. package/src/lib/providers/image-endpoints.d.ts +24 -0
  155. package/src/lib/providers/image-endpoints.js +28 -0
  156. package/src/lib/providers/image-endpoints.js.map +1 -0
  157. package/src/lib/providers/model-choice.js +23 -2
  158. package/src/lib/providers/model-choice.js.map +1 -1
  159. package/src/lib/providers/routing.js +18 -0
  160. package/src/lib/providers/routing.js.map +1 -1
  161. package/src/lib/providers/vertex-image-endpoint.d.ts +48 -0
  162. package/src/lib/providers/vertex-image-endpoint.js +80 -0
  163. package/src/lib/providers/vertex-image-endpoint.js.map +1 -0
  164. package/src/lib/providers/vertex-image.d.ts +13 -27
  165. package/src/lib/providers/vertex-image.js +15 -42
  166. package/src/lib/providers/vertex-image.js.map +1 -1
  167. package/src/lib/runtime/ai-blog-post-generation.d.ts +62 -0
  168. package/src/lib/runtime/ai-blog-post-generation.js +89 -0
  169. package/src/lib/runtime/ai-blog-post-generation.js.map +1 -0
  170. package/src/lib/runtime/ai-dev-env.d.ts +53 -0
  171. package/src/lib/runtime/ai-dev-env.js +72 -0
  172. package/src/lib/runtime/ai-dev-env.js.map +1 -0
  173. package/src/lib/runtime/ai-dev-replay.d.ts +116 -0
  174. package/src/lib/runtime/ai-dev-replay.js +313 -0
  175. package/src/lib/runtime/ai-dev-replay.js.map +1 -0
  176. package/src/lib/runtime/ai-doctrine.js +5 -1
  177. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  178. package/src/lib/runtime/ai-eval-live.js +4 -0
  179. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  180. package/src/lib/runtime/ai-eval.d.ts +13 -1
  181. package/src/lib/runtime/ai-eval.js +84 -1
  182. package/src/lib/runtime/ai-eval.js.map +1 -1
  183. package/src/lib/runtime/ai-live-batch.d.ts +45 -0
  184. package/src/lib/runtime/ai-live-batch.js +89 -0
  185. package/src/lib/runtime/ai-live-batch.js.map +1 -0
  186. package/src/lib/runtime/ai-runtime.js +46 -2
  187. package/src/lib/runtime/ai-runtime.js.map +1 -1
  188. package/src/lib/runtime/site-context.d.ts +58 -0
  189. package/src/lib/runtime/site-context.js +127 -0
  190. package/src/lib/runtime/site-context.js.map +1 -0
  191. package/src/lib/server/ai-admin-conversations.d.ts +22 -0
  192. package/src/lib/server/ai-admin-conversations.js +197 -0
  193. package/src/lib/server/ai-admin-conversations.js.map +1 -0
  194. package/src/lib/server/ai-media-image.d.ts +5 -5
  195. package/src/lib/server/ai-media-image.js +25 -11
  196. package/src/lib/server/ai-media-image.js.map +1 -1
  197. package/src/lib/server/ai-media-raster-prompt.d.ts +34 -0
  198. package/src/lib/server/ai-media-raster-prompt.js +53 -0
  199. package/src/lib/server/ai-media-raster-prompt.js.map +1 -0
  200. package/src/lib/server/assist-chat.d.ts +11 -0
  201. package/src/lib/server/assist-chat.js +57 -18
  202. package/src/lib/server/assist-chat.js.map +1 -1
  203. package/src/lib/server/assist-edit-applied.js +16 -3
  204. package/src/lib/server/assist-edit-applied.js.map +1 -1
  205. package/src/lib/server/assist-view-context.d.ts +19 -1
  206. package/src/lib/server/assist-view-context.js +38 -1
  207. package/src/lib/server/assist-view-context.js.map +1 -1
  208. package/src/lib/server.js +10 -0
  209. package/src/lib/server.js.map +1 -1
  210. package/src/lib/subprocessors.js +17 -1
  211. package/src/lib/subprocessors.js.map +1 -1
  212. package/src/lib/tools/ai-blog-post-tool.d.ts +60 -0
  213. package/src/lib/tools/ai-blog-post-tool.js +175 -0
  214. package/src/lib/tools/ai-blog-post-tool.js.map +1 -0
  215. package/src/lib/usage/assist-usage.d.ts +3 -0
  216. package/src/lib/usage/assist-usage.js +4 -2
  217. package/src/lib/usage/assist-usage.js.map +1 -1
  218. package/src/lib/usage/staff-org-ai-conversations.d.ts +103 -0
  219. package/src/lib/usage/staff-org-ai-conversations.js +152 -0
  220. package/src/lib/usage/staff-org-ai-conversations.js.map +1 -0
@@ -0,0 +1,187 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
+ import { aiAddonName, pluginDocsHelp } from "@aglyn/aglyn";
19
+ import { CardDisplay, useConfirmationContext } from "@aglyn/shared-ui-jsx";
20
+ import EmptyStateComponent from "@aglyn/shared-ui-jsx/components/empty-state.component";
21
+ import { useSnackbar } from "@aglyn/shared-ui-snackstack";
22
+ import { useFirestore, useFirestoreCollection } from "@aglyn/tenant-feature-instance";
23
+ import { Button, Divider, IconButton, Stack, Tooltip, Typography } from "@mui/material";
24
+ import { collection, deleteDoc, doc, limit, query, writeBatch } from "firebase/firestore";
25
+ import { useCallback, useMemo, useState } from "react";
26
+ import { AI_SITE_MEMORY_MAX, AI_SITE_MEMORY_SUBCOLLECTION, aiSitePreferenceOf } from "../model/ai-site-memory.js";
27
+ /** The configured brand's AI, never ours by literal (AGL-2153). */ const AI_NAME = aiAddonName();
28
+ /** A close mark, drawn inline so the card ships no icon set. */ const ForgetIcon = ()=>/*#__PURE__*/ _jsx("svg", {
29
+ width: "18",
30
+ height: "18",
31
+ viewBox: "0 0 24 24",
32
+ "aria-hidden": true,
33
+ focusable: "false",
34
+ children: /*#__PURE__*/ _jsx("path", {
35
+ fill: "currentColor",
36
+ d: "M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
37
+ })
38
+ });
39
+ /**
40
+ * What Aglyn AI learned from this site's edits (AGL-3661), on Setup →
41
+ * Business profile: the short preferences the assist edit route distilled
42
+ * from the edits the owner applied, which every later AI job for the site
43
+ * reads. The owner reads them here and forgets one, or all of them.
44
+ *
45
+ * Read and deleted on the client: the rules let a site's members read and
46
+ * delete `aiMemory` and refuse them a create or an update, because a client
47
+ * write would put words of its choosing into later prompts.
48
+ */ export function AiSiteMemoryCard(props) {
49
+ const { hostId } = props;
50
+ const firestore = useFirestore();
51
+ const { enqueueSnackbar } = useSnackbar();
52
+ const { confirm } = useConfirmationContext();
53
+ const [busy, setBusy] = useState(false);
54
+ const { data, status } = useFirestoreCollection(()=>query(collection(firestore, 'hosts', hostId, AI_SITE_MEMORY_SUBCOLLECTION), limit(AI_SITE_MEMORY_MAX * 2)), [
55
+ firestore,
56
+ hostId
57
+ ], {
58
+ idField: '$id'
59
+ });
60
+ const preferences = useMemo(()=>data.map((row)=>aiSitePreferenceOf(row.$id, row)).filter((row)=>row !== null).sort((a, b)=>b.count - a.count || b.lastSeenAtMs - a.lastSeenAtMs), [
61
+ data
62
+ ]);
63
+ const forget = useCallback(async (preference)=>{
64
+ setBusy(true);
65
+ try {
66
+ await deleteDoc(doc(firestore, 'hosts', hostId, AI_SITE_MEMORY_SUBCOLLECTION, preference.id));
67
+ enqueueSnackbar(`${AI_NAME} will no longer use that preference`, {
68
+ variant: 'success'
69
+ });
70
+ } catch (error) {
71
+ console.error('ai memory forget failed', error);
72
+ enqueueSnackbar('That preference was not removed. Try again.', {
73
+ variant: 'error'
74
+ });
75
+ } finally{
76
+ setBusy(false);
77
+ }
78
+ }, [
79
+ enqueueSnackbar,
80
+ firestore,
81
+ hostId
82
+ ]);
83
+ const forgetAll = useCallback(async ()=>{
84
+ const confirmed = await confirm({
85
+ title: `Forget everything ${AI_NAME} learned here?`,
86
+ description: `Later AI jobs for this site stop using these preferences. ${AI_NAME} learns again from the edits you apply next.`,
87
+ confirmationText: 'Forget all',
88
+ confirmationButtonProps: {
89
+ color: 'error'
90
+ }
91
+ }).then(()=>true).catch(()=>false);
92
+ if (!confirmed) return;
93
+ setBusy(true);
94
+ try {
95
+ const batch = writeBatch(firestore);
96
+ for (const preference of preferences){
97
+ batch.delete(doc(firestore, 'hosts', hostId, AI_SITE_MEMORY_SUBCOLLECTION, preference.id));
98
+ }
99
+ await batch.commit();
100
+ enqueueSnackbar(`${AI_NAME} forgot what it learned on this site`, {
101
+ variant: 'success'
102
+ });
103
+ } catch (error) {
104
+ console.error('ai memory clear failed', error);
105
+ enqueueSnackbar('The preferences were not cleared. Try again.', {
106
+ variant: 'error'
107
+ });
108
+ } finally{
109
+ setBusy(false);
110
+ }
111
+ }, [
112
+ confirm,
113
+ enqueueSnackbar,
114
+ firestore,
115
+ hostId,
116
+ preferences
117
+ ]);
118
+ return /*#__PURE__*/ _jsx(CardDisplay, {
119
+ header: `What ${AI_NAME} learned`,
120
+ subheader: "Short preferences from the edits you applied with Assist. Every AI job for this site reads them.",
121
+ help: pluginDocsHelp('aiBusinessProfile', {
122
+ anchor: '#what-aglyn-ai-learned',
123
+ excerpt: `Preferences ${AI_NAME} took from the edits you applied. Forget one, or all, at any time.`
124
+ }),
125
+ actions: preferences.length ? /*#__PURE__*/ _jsx(Button, {
126
+ color: "error",
127
+ disabled: busy,
128
+ onClick: ()=>void forgetAll(),
129
+ children: 'Forget all'
130
+ }) : undefined,
131
+ contentGutterX: true,
132
+ contentGutterY: true,
133
+ children: preferences.length ? /*#__PURE__*/ _jsx(Stack, {
134
+ divider: /*#__PURE__*/ _jsx(Divider, {
135
+ flexItem: true
136
+ }),
137
+ "data-testid": "ai-site-memory",
138
+ children: preferences.map((preference)=>/*#__PURE__*/ _jsxs(Stack, {
139
+ direction: "row",
140
+ spacing: 2,
141
+ sx: {
142
+ alignItems: 'center',
143
+ py: 1
144
+ },
145
+ children: [
146
+ /*#__PURE__*/ _jsxs(Stack, {
147
+ sx: {
148
+ flex: 1,
149
+ minWidth: 0
150
+ },
151
+ children: [
152
+ /*#__PURE__*/ _jsx(Typography, {
153
+ variant: "body2",
154
+ children: preference.text
155
+ }),
156
+ /*#__PURE__*/ _jsx(Typography, {
157
+ variant: "caption",
158
+ color: "text.secondary",
159
+ children: preference.count === 1 ? 'Seen in 1 applied edit' : `Seen in ${preference.count} applied edits`
160
+ })
161
+ ]
162
+ }),
163
+ /*#__PURE__*/ _jsx(Tooltip, {
164
+ title: "Forget this",
165
+ children: /*#__PURE__*/ _jsx("span", {
166
+ children: /*#__PURE__*/ _jsx(IconButton, {
167
+ size: "small",
168
+ "aria-label": `Forget “${preference.text}”`,
169
+ disabled: busy,
170
+ onClick: ()=>void forget(preference),
171
+ children: /*#__PURE__*/ _jsx(ForgetIcon, {})
172
+ })
173
+ })
174
+ })
175
+ ]
176
+ }, preference.id))
177
+ }) : /*#__PURE__*/ _jsx(EmptyStateComponent, {
178
+ compact: true,
179
+ label: status === 'loading' ? 'Loading…' : 'Nothing learned yet',
180
+ description: status === 'loading' ? undefined : 'When you apply an Assist edit that shows a preference, such as shorter copy or no emoji, it appears here.'
181
+ })
182
+ });
183
+ }
184
+ AiSiteMemoryCard.displayName = 'AiSiteMemoryCard';
185
+ export default AiSiteMemoryCard;
186
+
187
+ //# sourceMappingURL=ai-site-memory-card.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-site-memory-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { aiAddonName, pluginDocsHelp } from '@aglyn/aglyn'\nimport type { ConsoleHostBusinessProfileZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport EmptyStateComponent from '@aglyn/shared-ui-jsx/components/empty-state.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useFirestore, useFirestoreCollection } from '@aglyn/tenant-feature-instance'\nimport { Button, Divider, IconButton, Stack, Tooltip, Typography } from '@mui/material'\nimport { collection, deleteDoc, doc, limit, query, writeBatch } from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport {\n AI_SITE_MEMORY_MAX,\n AI_SITE_MEMORY_SUBCOLLECTION,\n aiSitePreferenceOf,\n type AiSitePreference,\n} from '../model/ai-site-memory'\n\n/** The configured brand's AI, never ours by literal (AGL-2153). */\nconst AI_NAME = aiAddonName()\n\n/** A close mark, drawn inline so the card ships no icon set. */\nconst ForgetIcon = () => (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" aria-hidden focusable=\"false\">\n <path\n fill=\"currentColor\"\n d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\n />\n </svg>\n)\n\n/**\n * What Aglyn AI learned from this site's edits (AGL-3661), on Setup →\n * Business profile: the short preferences the assist edit route distilled\n * from the edits the owner applied, which every later AI job for the site\n * reads. The owner reads them here and forgets one, or all of them.\n *\n * Read and deleted on the client: the rules let a site's members read and\n * delete `aiMemory` and refuse them a create or an update, because a client\n * write would put words of its choosing into later prompts.\n */\nexport function AiSiteMemoryCard(props: ConsoleHostBusinessProfileZoneProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const [busy, setBusy] = useState(false)\n const { data, status } = useFirestoreCollection<Record<string, unknown> & { $id: string }>(\n () => query(collection(firestore, 'hosts', hostId, AI_SITE_MEMORY_SUBCOLLECTION), limit(AI_SITE_MEMORY_MAX * 2)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const preferences = useMemo(\n () =>\n data\n .map((row) => aiSitePreferenceOf(row.$id, row))\n .filter((row): row is AiSitePreference => row !== null)\n .sort((a, b) => b.count - a.count || b.lastSeenAtMs - a.lastSeenAtMs),\n [data],\n )\n\n const forget = useCallback(\n async (preference: AiSitePreference) => {\n setBusy(true)\n try {\n await deleteDoc(doc(firestore, 'hosts', hostId, AI_SITE_MEMORY_SUBCOLLECTION, preference.id))\n enqueueSnackbar(`${AI_NAME} will no longer use that preference`, { variant: 'success' })\n } catch (error) {\n console.error('ai memory forget failed', error)\n enqueueSnackbar('That preference was not removed. Try again.', { variant: 'error' })\n } finally {\n setBusy(false)\n }\n },\n [enqueueSnackbar, firestore, hostId],\n )\n\n const forgetAll = useCallback(async () => {\n const confirmed = await confirm({\n title: `Forget everything ${AI_NAME} learned here?`,\n description:\n `Later AI jobs for this site stop using these preferences. ${AI_NAME} learns again from the edits you apply next.`,\n confirmationText: 'Forget all',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n setBusy(true)\n try {\n const batch = writeBatch(firestore)\n for (const preference of preferences) {\n batch.delete(doc(firestore, 'hosts', hostId, AI_SITE_MEMORY_SUBCOLLECTION, preference.id))\n }\n await batch.commit()\n enqueueSnackbar(`${AI_NAME} forgot what it learned on this site`, { variant: 'success' })\n } catch (error) {\n console.error('ai memory clear failed', error)\n enqueueSnackbar('The preferences were not cleared. Try again.', { variant: 'error' })\n } finally {\n setBusy(false)\n }\n }, [confirm, enqueueSnackbar, firestore, hostId, preferences])\n\n return (\n <CardDisplay\n header={`What ${AI_NAME} learned`}\n subheader=\"Short preferences from the edits you applied with Assist. Every AI job for this site reads them.\"\n help={pluginDocsHelp('aiBusinessProfile', {\n anchor: '#what-aglyn-ai-learned',\n excerpt: `Preferences ${AI_NAME} took from the edits you applied. Forget one, or all, at any time.`,\n })}\n actions={\n preferences.length ? (\n <Button color=\"error\" disabled={busy} onClick={() => void forgetAll()}>\n {'Forget all'}\n </Button>\n ) : undefined\n }\n contentGutterX\n contentGutterY\n >\n {preferences.length ? (\n <Stack divider={<Divider flexItem />} data-testid=\"ai-site-memory\">\n {preferences.map((preference) => (\n <Stack key={preference.id} direction=\"row\" spacing={2} sx={{ alignItems: 'center', py: 1 }}>\n <Stack sx={{ flex: 1, minWidth: 0 }}>\n <Typography variant=\"body2\">{preference.text}</Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {preference.count === 1 ? 'Seen in 1 applied edit' : `Seen in ${preference.count} applied edits`}\n </Typography>\n </Stack>\n <Tooltip title=\"Forget this\">\n <span>\n <IconButton\n size=\"small\"\n aria-label={`Forget “${preference.text}”`}\n disabled={busy}\n onClick={() => void forget(preference)}\n >\n <ForgetIcon />\n </IconButton>\n </span>\n </Tooltip>\n </Stack>\n ))}\n </Stack>\n ) : (\n <EmptyStateComponent\n compact\n label={status === 'loading' ? 'Loading…' : 'Nothing learned yet'}\n description={\n status === 'loading'\n ? undefined\n : 'When you apply an Assist edit that shows a preference, such as shorter copy or no emoji, it appears here.'\n }\n />\n )}\n </CardDisplay>\n )\n}\nAiSiteMemoryCard.displayName = 'AiSiteMemoryCard'\n\nexport default AiSiteMemoryCard\n"],"names":["aiAddonName","pluginDocsHelp","CardDisplay","useConfirmationContext","EmptyStateComponent","useSnackbar","useFirestore","useFirestoreCollection","Button","Divider","IconButton","Stack","Tooltip","Typography","collection","deleteDoc","doc","limit","query","writeBatch","useCallback","useMemo","useState","AI_SITE_MEMORY_MAX","AI_SITE_MEMORY_SUBCOLLECTION","aiSitePreferenceOf","AI_NAME","ForgetIcon","svg","width","height","viewBox","aria-hidden","focusable","path","fill","d","AiSiteMemoryCard","props","hostId","firestore","enqueueSnackbar","confirm","busy","setBusy","data","status","idField","preferences","map","row","$id","filter","sort","a","b","count","lastSeenAtMs","forget","preference","id","variant","error","console","forgetAll","confirmed","title","description","confirmationText","confirmationButtonProps","color","then","catch","batch","delete","commit","header","subheader","help","anchor","excerpt","actions","length","disabled","onClick","undefined","contentGutterX","contentGutterY","divider","flexItem","data-testid","direction","spacing","sx","alignItems","py","flex","minWidth","text","span","size","aria-label","compact","label","displayName"],"mappings":"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"}
@@ -19,8 +19,9 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
19
19
  import { lockdownRefusalText, parseLockdownRefusal, PLATFORM_BRAND_NAME } from "@aglyn/aglyn";
20
20
  import { trackEvent } from "@aglyn/aglyn/app-utils/analytics-events";
21
21
  import { ICON_VARIANT_CLOSE } from "@aglyn/shared-data-enums";
22
- import { mdiCheckCircle, mdiCreation, mdiInformationOutline, mdiPageLayoutHeaderFooter } from "@aglyn/shared-data-mdi";
22
+ import { mdiAccount, mdiBookOpenPageVariant, mdiBriefcaseOutline, mdiCalendarHeart, mdiCamera, mdiClockOutline, mdiContentCut, mdiCreation, mdiDumbbell, mdiHandHeart, mdiHeartPulse, mdiHomeCity, mdiMusic, mdiPaletteOutline, mdiRocketLaunch, mdiScaleBalance, mdiSchool, mdiShopping, mdiSilverwareForkKnife, mdiViewGridOutline, mdiWrench, mdiYoga, mdiInformationOutline, mdiPageLayoutHeaderFooter } from "@aglyn/shared-data-mdi";
23
23
  import { MdiIcon } from "@aglyn/shared-ui-jsx";
24
+ import { OptionCardGrid } from "@aglyn/shared-ui-jsx/components/option-card-grid.component";
24
25
  import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
25
26
  import { useUser } from "@aglyn/tenant-feature-instance";
26
27
  import { Alert, AppBar, Box, Button, Card, CardActionArea, Chip, CircularProgress, Container, Dialog, DialogActions, DialogContent, IconButton, MenuItem, Stack, Step, StepLabel, Stepper, TextField, Toolbar, Tooltip, Typography } from "@mui/material";
@@ -29,8 +30,34 @@ import { useRouter } from "next/navigation";
29
30
  import { useCallback, useEffect, useId, useRef, useState } from "react";
30
31
  import { AI_JOB_AUTO_CONFIRM_INPUT, AI_SITE_FREE_PAGES, AI_SITE_FREE_PAGES_NOTE, AI_SITE_SUBMISSION_CHOICES, aiFreeSiteCreditEstimate, aiSiteCreditEstimate, aiSitePagesBand } from "../model/ai-site-job.js";
31
32
  import { FREE_AI_TASTE_CREDITS_PER_MONTH } from "../plan-entitlements.js";
32
- import { AI_SITE_START_ANSWERS, AI_SITE_START_EXAMPLES, AI_SITE_START_TYPES, aiSiteStartBrief, aiSiteStartInputs, aiSiteStartRefusal } from "../model/ai-site-start.js";
33
+ import { AI_SITE_START_ANSWERS, AI_SITE_START_TYPES, aiSiteStartBrief, aiSiteStartKind, aiSiteStartInputs, aiSiteStartRefusal } from "../model/ai-site-start.js";
34
+ import { AI_SITE_KINDS } from "../model/ai-site-kinds.js";
35
+ /** The icon each kind of site's card shows. */ const AI_SITE_KIND_ICONS = {
36
+ business: mdiBriefcaseOutline.path,
37
+ trades: mdiWrench.path,
38
+ professional: mdiScaleBalance.path,
39
+ wellness: mdiHeartPulse.path,
40
+ restaurant: mdiSilverwareForkKnife.path,
41
+ store: mdiShopping.path,
42
+ portfolio: mdiViewGridOutline.path,
43
+ studio: mdiPaletteOutline.path,
44
+ photography: mdiCamera.path,
45
+ blog: mdiBookOpenPageVariant.path,
46
+ events: mdiCalendarHeart.path,
47
+ fitness: mdiDumbbell.path,
48
+ yoga: mdiYoga.path,
49
+ beauty: mdiContentCut.path,
50
+ realestate: mdiHomeCity.path,
51
+ education: mdiSchool.path,
52
+ nonprofit: mdiHandHeart.path,
53
+ music: mdiMusic.path,
54
+ personal: mdiAccount.path,
55
+ landing: mdiRocketLaunch.path,
56
+ 'coming-soon': mdiClockOutline.path
57
+ };
33
58
  import { AiJobFollow } from "./ai-job-follow.component.js";
59
+ import { AiModelSelector } from "./ai-model-selector.component.js";
60
+ import { useAiModelChoice } from "./use-ai-model-choice.js";
34
61
  import { aiSiteBuildHref } from "./ai-job-links.js";
35
62
  import { publishAiJob } from "./ai-jobs-store.js";
36
63
  /** Labels the dialog for a reader, from its own heading. */ const TITLE_ID = 'ai-site-start-title';
@@ -154,6 +181,7 @@ import { publishAiJob } from "./ai-jobs-store.js";
154
181
  }
155
182
  export function AiSiteStartCard({ hostId, orgId, orgSlug, host, startBlank, leave, initialAnswers = null }) {
156
183
  var _ref;
184
+ var _modelChoice_options_options_find, _modelChoice_options;
157
185
  const { data: user } = useUser();
158
186
  const router = useRouter();
159
187
  // Held in a ref so a request reads WHO is signed in, never the identity of
@@ -207,6 +235,27 @@ export function AiSiteStartCard({ hostId, orgId, orgSlug, host, startBlank, leav
207
235
  freeTaste
208
236
  });
209
237
  const band = aiSitePagesBand(freeTaste);
238
+ // On a paid plan the person picks the model that builds the site (AGL-3660),
239
+ // Auto by default, from the models their plan allows; every step of the job
240
+ // runs on it. A Free workspace builds on the default and is offered none.
241
+ const modelChoice = useAiModelChoice({
242
+ orgId,
243
+ hostId,
244
+ surface: 'jobs',
245
+ kind: 'job.page'
246
+ });
247
+ // A pick remembered from before needs its cost to estimate by; Auto does not,
248
+ // and the list is otherwise read when the picker opens.
249
+ const { load: loadModels, model: rememberedModel } = modelChoice;
250
+ useEffect(()=>{
251
+ if (!freeTaste && step === 'describe' && rememberedModel) loadModels();
252
+ }, [
253
+ freeTaste,
254
+ step,
255
+ rememberedModel,
256
+ loadModels
257
+ ]);
258
+ const pickedModel = freeTaste ? null : modelChoice.model;
210
259
  const plan = useCallback(async ()=>{
211
260
  if (!orgId || aiSiteStartRefusal(answers, {
212
261
  freeTaste
@@ -220,7 +269,7 @@ export function AiSiteStartCard({ hostId, orgId, orgSlug, host, startBlank, leav
220
269
  headers: {
221
270
  'Content-Type': 'application/json'
222
271
  },
223
- body: JSON.stringify({
272
+ body: JSON.stringify(_extends({
224
273
  orgId,
225
274
  hostId,
226
275
  kind: 'site',
@@ -230,7 +279,9 @@ export function AiSiteStartCard({ hostId, orgId, orgSlug, host, startBlank, leav
230
279
  inputs: _extends({}, aiSiteStartInputs(answers), {
231
280
  [AI_JOB_AUTO_CONFIRM_INPUT]: true
232
281
  })
233
- })
282
+ }, pickedModel ? {
283
+ model: pickedModel
284
+ } : {}))
234
285
  });
235
286
  const payload = await response.json().catch(()=>null);
236
287
  if (!response.ok) {
@@ -270,7 +321,8 @@ export function AiSiteStartCard({ hostId, orgId, orgSlug, host, startBlank, leav
270
321
  orgSlug,
271
322
  host,
272
323
  leave,
273
- router
324
+ router,
325
+ pickedModel
274
326
  ]);
275
327
  const chooseStarter = useCallback(()=>{
276
328
  setStartingStarter(true);
@@ -291,9 +343,11 @@ export function AiSiteStartCard({ hostId, orgId, orgSlug, host, startBlank, leav
291
343
  // Before a job is started every way out is the blank site, which writes the
292
344
  // starter (AGL-3594); after, it only closes — the job builds the site.
293
345
  const exit = started ? leave != null ? leave : startBlank : startBlank;
294
- const estimate = freeTaste ? aiFreeSiteCreditEstimate(answers.pages) : aiSiteCreditEstimate(answers.pages, {
346
+ // The chosen model's cost against Auto's, as the model list states it.
347
+ const modelMultiplier = pickedModel && ((_modelChoice_options = modelChoice.options) == null ? void 0 : (_modelChoice_options_options_find = _modelChoice_options.options.find((option)=>option.id === pickedModel)) == null ? void 0 : _modelChoice_options_options_find.multiplier) || 1;
348
+ const estimate = freeTaste ? aiFreeSiteCreditEstimate(answers.pages) : Math.round(aiSiteCreditEstimate(answers.pages, {
295
349
  welcomeEmail: answers.welcomeEmail
296
- });
350
+ }) * modelMultiplier);
297
351
  const estimateText = freeTaste ? `Up to about ${estimate.toLocaleString('en-US')} of the ${FREE_AI_TASTE_CREDITS_PER_MONTH} AI credits your Free workspace has each month` : `About ${estimate.toLocaleString('en-US')} credits, estimated`;
298
352
  const choosing = step === 'choose' && !started;
299
353
  return /*#__PURE__*/ _jsxs(Dialog, {
@@ -522,83 +576,28 @@ export function AiSiteStartCard({ hostId, orgId, orgSlug, host, startBlank, leav
522
576
  /*#__PURE__*/ _jsxs(Stack, {
523
577
  spacing: 2,
524
578
  component: "section",
525
- "aria-label": "Look & layout",
579
+ "aria-label": "Style",
526
580
  children: [
527
581
  /*#__PURE__*/ _jsx(SectionLabel, {
528
- children: 'Look & layout'
582
+ children: 'Style'
529
583
  }),
530
584
  /*#__PURE__*/ _jsx(Typography, {
531
585
  variant: "body2",
532
586
  color: "text.secondary",
533
- children: 'Optional. Picking one steers the shape of the site, not its words.'
587
+ children: answers.kind ? 'This decides how your site looks and which pages it usually has.' : 'Picked from your answer above. Choose another to change how your site looks and which pages it usually has.'
534
588
  }),
535
- /*#__PURE__*/ _jsx(Box, {
536
- sx: {
537
- display: 'grid',
538
- gridTemplateColumns: {
539
- xs: '1fr',
540
- sm: '1fr 1fr'
541
- },
542
- gap: 2
543
- },
544
- children: AI_SITE_START_EXAMPLES.map((example)=>{
545
- const selected = answers.example === example.id;
546
- return /*#__PURE__*/ _jsx(Card, {
547
- variant: "outlined",
548
- sx: (theme)=>({
549
- borderRadius: 2,
550
- borderColor: selected ? 'primary.main' : 'divider',
551
- bgcolor: selected ? alpha(theme.palette.primary.main, 0.08) : 'transparent'
552
- }),
553
- children: /*#__PURE__*/ _jsx(CardActionArea, {
554
- "aria-pressed": selected,
555
- "aria-label": `${example.label} — ${example.blurb}`,
556
- onClick: ()=>answer({
557
- example: selected ? null : example.id
558
- }),
559
- sx: {
560
- p: 2,
561
- height: '100%'
562
- },
563
- children: /*#__PURE__*/ _jsxs(Stack, {
564
- direction: "row",
565
- spacing: 1.5,
566
- sx: {
567
- alignItems: 'flex-start'
568
- },
569
- children: [
570
- /*#__PURE__*/ _jsxs(Box, {
571
- sx: {
572
- flex: 1,
573
- minWidth: 0
574
- },
575
- children: [
576
- /*#__PURE__*/ _jsx(Typography, {
577
- variant: "subtitle2",
578
- children: example.label
579
- }),
580
- /*#__PURE__*/ _jsx(Typography, {
581
- variant: "body2",
582
- color: "text.secondary",
583
- children: example.blurb
584
- })
585
- ]
586
- }),
587
- selected && /*#__PURE__*/ _jsx(Box, {
588
- sx: {
589
- color: 'primary.main',
590
- display: 'flex'
591
- },
592
- "aria-hidden": true,
593
- children: /*#__PURE__*/ _jsx(MdiIcon, {
594
- path: mdiCheckCircle.path
595
- })
596
- })
597
- ]
598
- })
599
- })
600
- }, example.id);
601
- })
589
+ /*#__PURE__*/ _jsx(OptionCardGrid, {
590
+ label: "Style of site",
591
+ options: AI_SITE_KINDS.map((kind)=>({
592
+ id: kind.id,
593
+ title: kind.label,
594
+ description: kind.blurb,
595
+ icon: AI_SITE_KIND_ICONS[kind.id]
596
+ })),
597
+ value: aiSiteStartKind(answers).id,
598
+ onChange: (kind)=>answer({
599
+ kind
600
+ })
602
601
  })
603
602
  ]
604
603
  }),
@@ -728,6 +727,10 @@ export function AiSiteStartCard({ hostId, orgId, orgSlug, host, startBlank, leav
728
727
  color: "text.secondary",
729
728
  children: refusal
730
729
  }),
730
+ !freeTaste && /*#__PURE__*/ _jsx(AiModelSelector, {
731
+ choice: modelChoice,
732
+ disabled: busy
733
+ }),
731
734
  /*#__PURE__*/ _jsx(Button, {
732
735
  onClick: ()=>setStep('choose'),
733
736
  children: 'Back'