@aglyn/plugins-ai 1.0.0-beta.229 → 1.0.0-beta.230

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 (248) hide show
  1. package/README.md +3 -3
  2. package/package.json +17 -17
  3. package/src/lib/activity/ai-activity-actions.js +7 -1
  4. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  5. package/src/lib/components/ai-automation-zones.d.ts +38 -0
  6. package/src/lib/components/ai-automation-zones.js +5 -1
  7. package/src/lib/components/ai-automation-zones.js.map +1 -1
  8. package/src/lib/components/ai-brief-dialog.component.d.ts +20 -2
  9. package/src/lib/components/ai-brief-dialog.component.js +87 -8
  10. package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
  11. package/src/lib/components/ai-campaign-create.component.d.ts +54 -0
  12. package/src/lib/components/ai-campaign-create.component.js +270 -0
  13. package/src/lib/components/ai-campaign-create.component.js.map +1 -0
  14. package/src/lib/components/ai-describe-automation.component.d.ts +8 -3
  15. package/src/lib/components/ai-describe-automation.component.js +15 -6
  16. package/src/lib/components/ai-describe-automation.component.js.map +1 -1
  17. package/src/lib/components/ai-describe-button.component.d.ts +20 -8
  18. package/src/lib/components/ai-describe-button.component.js +19 -36
  19. package/src/lib/components/ai-describe-button.component.js.map +1 -1
  20. package/src/lib/components/ai-describe-email.component.d.ts +31 -0
  21. package/src/lib/components/ai-describe-email.component.js +22 -0
  22. package/src/lib/components/ai-describe-email.component.js.map +1 -0
  23. package/src/lib/components/ai-describe-org-automation.component.d.ts +36 -0
  24. package/src/lib/components/ai-describe-org-automation.component.js +238 -0
  25. package/src/lib/components/ai-describe-org-automation.component.js.map +1 -0
  26. package/src/lib/components/ai-experiment-cards.component.d.ts +9 -1
  27. package/src/lib/components/ai-experiment-cards.component.js +104 -18
  28. package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
  29. package/src/lib/components/ai-explain-automation.component.js +1 -5
  30. package/src/lib/components/ai-explain-automation.component.js.map +1 -1
  31. package/src/lib/components/ai-funnel-zones.component.d.ts +61 -0
  32. package/src/lib/components/ai-funnel-zones.component.js +178 -0
  33. package/src/lib/components/ai-funnel-zones.component.js.map +1 -0
  34. package/src/lib/components/ai-insight-card.component.d.ts +42 -0
  35. package/src/lib/components/ai-insight-card.component.js +137 -0
  36. package/src/lib/components/ai-insight-card.component.js.map +1 -0
  37. package/src/lib/components/ai-insight-dialog.component.d.ts +6 -0
  38. package/src/lib/components/ai-insight-dialog.component.js +9 -3
  39. package/src/lib/components/ai-insight-dialog.component.js.map +1 -1
  40. package/src/lib/components/ai-job-plan.component.d.ts +7 -2
  41. package/src/lib/components/ai-job-plan.component.js +35 -5
  42. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  43. package/src/lib/components/ai-job-requests.d.ts +17 -1
  44. package/src/lib/components/ai-job-requests.js +39 -3
  45. package/src/lib/components/ai-job-requests.js.map +1 -1
  46. package/src/lib/components/ai-logic-zones.d.ts +64 -0
  47. package/src/lib/components/ai-logic-zones.js +18 -0
  48. package/src/lib/components/ai-logic-zones.js.map +1 -0
  49. package/src/lib/components/ai-logic.component.d.ts +77 -0
  50. package/src/lib/components/ai-logic.component.js +553 -0
  51. package/src/lib/components/ai-logic.component.js.map +1 -0
  52. package/src/lib/components/ai-marketing-insight.component.d.ts +30 -0
  53. package/src/lib/components/ai-marketing-insight.component.js +80 -0
  54. package/src/lib/components/ai-marketing-insight.component.js.map +1 -0
  55. package/src/lib/components/ai-media-create.component.d.ts +55 -0
  56. package/src/lib/components/ai-media-create.component.js +433 -0
  57. package/src/lib/components/ai-media-create.component.js.map +1 -0
  58. package/src/lib/components/ai-overlay-cards.component.d.ts +83 -0
  59. package/src/lib/components/ai-overlay-cards.component.js +505 -0
  60. package/src/lib/components/ai-overlay-cards.component.js.map +1 -0
  61. package/src/lib/components/ai-product-zones.d.ts +5 -0
  62. package/src/lib/components/ai-product-zones.js +1 -1
  63. package/src/lib/components/ai-product-zones.js.map +1 -1
  64. package/src/lib/components/ai-products-create-button.component.d.ts +42 -0
  65. package/src/lib/components/ai-products-create-button.component.js +67 -0
  66. package/src/lib/components/ai-products-create-button.component.js.map +1 -0
  67. package/src/lib/components/ai-products-hub-card.component.js +100 -81
  68. package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
  69. package/src/lib/components/ai-revise-automation.component.d.ts +16 -0
  70. package/src/lib/components/ai-revise-automation.component.js +207 -0
  71. package/src/lib/components/ai-revise-automation.component.js.map +1 -0
  72. package/src/lib/components/ai-site-build-page.component.js +140 -16
  73. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  74. package/src/lib/components/ai-upsell-dialog.component.d.ts +53 -0
  75. package/src/lib/components/ai-upsell-dialog.component.js +177 -0
  76. package/src/lib/components/ai-upsell-dialog.component.js.map +1 -0
  77. package/src/lib/components/assist-build-card.component.d.ts +42 -0
  78. package/src/lib/components/assist-build-card.component.js +183 -0
  79. package/src/lib/components/assist-build-card.component.js.map +1 -0
  80. package/src/lib/components/assist-edit-canvas.d.ts +2 -0
  81. package/src/lib/components/assist-edit-canvas.js +56 -1
  82. package/src/lib/components/assist-edit-canvas.js.map +1 -1
  83. package/src/lib/components/assist-jobs-drawer.component.js +48 -3
  84. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  85. package/src/lib/components/assist-panel.component.js +51 -2
  86. package/src/lib/components/assist-panel.component.js.map +1 -1
  87. package/src/lib/components/use-ai-job-run.d.ts +3 -14
  88. package/src/lib/components/use-ai-job-run.js +0 -50
  89. package/src/lib/components/use-ai-job-run.js.map +1 -1
  90. package/src/lib/jobs/ai-build-capabilities.d.ts +58 -0
  91. package/src/lib/jobs/ai-build-capabilities.js +256 -0
  92. package/src/lib/jobs/ai-build-capabilities.js.map +1 -0
  93. package/src/lib/jobs/ai-build-unit-outcome.d.ts +52 -0
  94. package/src/lib/jobs/ai-build-unit-outcome.js +138 -0
  95. package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -0
  96. package/src/lib/jobs/ai-job-build-step.d.ts +144 -0
  97. package/src/lib/jobs/ai-job-build-step.js +657 -0
  98. package/src/lib/jobs/ai-job-build-step.js.map +1 -0
  99. package/src/lib/jobs/ai-job-draft-ids.js +9 -1
  100. package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
  101. package/src/lib/jobs/ai-job-drafts.d.ts +14 -4
  102. package/src/lib/jobs/ai-job-drafts.js +25 -4
  103. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  104. package/src/lib/jobs/ai-job-logic-step.d.ts +107 -0
  105. package/src/lib/jobs/ai-job-logic-step.js +395 -0
  106. package/src/lib/jobs/ai-job-logic-step.js.map +1 -0
  107. package/src/lib/jobs/ai-job-overlay-copy.d.ts +70 -0
  108. package/src/lib/jobs/ai-job-overlay-copy.js +178 -0
  109. package/src/lib/jobs/ai-job-overlay-copy.js.map +1 -0
  110. package/src/lib/jobs/ai-job-page-step.js +6 -6
  111. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  112. package/src/lib/jobs/ai-job-plan-step.d.ts +26 -1
  113. package/src/lib/jobs/ai-job-plan-step.js +79 -11
  114. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  115. package/src/lib/jobs/ai-job-site-step.d.ts +15 -1
  116. package/src/lib/jobs/ai-job-site-step.js +200 -58
  117. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  118. package/src/lib/jobs/ai-job-text-step.d.ts +22 -1
  119. package/src/lib/jobs/ai-job-text-step.js +12 -0
  120. package/src/lib/jobs/ai-job-text-step.js.map +1 -1
  121. package/src/lib/jobs/ai-job-workflow-step.d.ts +54 -4
  122. package/src/lib/jobs/ai-job-workflow-step.js +318 -13
  123. package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
  124. package/src/lib/jobs/ai-jobs.d.ts +80 -2
  125. package/src/lib/jobs/ai-jobs.js +377 -13
  126. package/src/lib/jobs/ai-jobs.js.map +1 -1
  127. package/src/lib/jobs/ai-logic-records.d.ts +40 -0
  128. package/src/lib/jobs/ai-logic-records.js +55 -0
  129. package/src/lib/jobs/ai-logic-records.js.map +1 -0
  130. package/src/lib/jobs/ai-site-publish.d.ts +7 -0
  131. package/src/lib/jobs/ai-site-publish.js +9 -0
  132. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  133. package/src/lib/jobs/ai-workflow-records.d.ts +4 -0
  134. package/src/lib/jobs/ai-workflow-records.js +40 -1
  135. package/src/lib/jobs/ai-workflow-records.js.map +1 -1
  136. package/src/lib/model/ai-automation-draft.d.ts +18 -0
  137. package/src/lib/model/ai-automation-draft.js +86 -0
  138. package/src/lib/model/ai-automation-draft.js.map +1 -1
  139. package/src/lib/model/ai-build-job.d.ts +201 -0
  140. package/src/lib/model/ai-build-job.js +471 -0
  141. package/src/lib/model/ai-build-job.js.map +1 -0
  142. package/src/lib/model/ai-build-plan.d.ts +61 -0
  143. package/src/lib/model/ai-build-plan.js +154 -9
  144. package/src/lib/model/ai-build-plan.js.map +1 -1
  145. package/src/lib/model/ai-build-progress.d.ts +56 -0
  146. package/src/lib/model/ai-build-progress.js +128 -0
  147. package/src/lib/model/ai-build-progress.js.map +1 -0
  148. package/src/lib/model/ai-insight.d.ts +20 -10
  149. package/src/lib/model/ai-insight.js +72 -9
  150. package/src/lib/model/ai-insight.js.map +1 -1
  151. package/src/lib/model/ai-job-activity.js +3 -1
  152. package/src/lib/model/ai-job-activity.js.map +1 -1
  153. package/src/lib/model/ai-jobs.types.d.ts +80 -2
  154. package/src/lib/model/ai-jobs.types.js +3 -1
  155. package/src/lib/model/ai-jobs.types.js.map +1 -1
  156. package/src/lib/model/ai-logic-job.d.ts +108 -0
  157. package/src/lib/model/ai-logic-job.js +327 -0
  158. package/src/lib/model/ai-logic-job.js.map +1 -0
  159. package/src/lib/model/ai-media-credits.d.ts +52 -0
  160. package/src/lib/model/ai-media-credits.js +61 -0
  161. package/src/lib/model/ai-media-credits.js.map +1 -0
  162. package/src/lib/model/ai-overlay-copy.d.ts +114 -0
  163. package/src/lib/model/ai-overlay-copy.js +175 -0
  164. package/src/lib/model/ai-overlay-copy.js.map +1 -0
  165. package/src/lib/model/ai-site-build-progress.d.ts +20 -3
  166. package/src/lib/model/ai-site-build-progress.js +170 -10
  167. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  168. package/src/lib/model/ai-svg.d.ts +69 -0
  169. package/src/lib/model/ai-svg.js +265 -0
  170. package/src/lib/model/ai-svg.js.map +1 -0
  171. package/src/lib/model/ai-usage-by-user.d.ts +3 -2
  172. package/src/lib/model/ai-usage-by-user.js.map +1 -1
  173. package/src/lib/model/ai-workflow-job.d.ts +92 -4
  174. package/src/lib/model/ai-workflow-job.js +124 -7
  175. package/src/lib/model/ai-workflow-job.js.map +1 -1
  176. package/src/lib/model/assist-build.d.ts +58 -0
  177. package/src/lib/model/assist-build.js +52 -0
  178. package/src/lib/model/assist-build.js.map +1 -0
  179. package/src/lib/model/assist-edit.d.ts +48 -3
  180. package/src/lib/model/assist-edit.js +48 -4
  181. package/src/lib/model/assist-edit.js.map +1 -1
  182. package/src/lib/plugin-config.js +2 -0
  183. package/src/lib/plugin-config.js.map +1 -1
  184. package/src/lib/plugin.js +240 -11
  185. package/src/lib/plugin.js.map +1 -1
  186. package/src/lib/providers/catalog.d.ts +81 -4
  187. package/src/lib/providers/catalog.js +80 -9
  188. package/src/lib/providers/catalog.js.map +1 -1
  189. package/src/lib/providers/image-contract.d.ts +116 -0
  190. package/src/lib/providers/image-contract.js +74 -0
  191. package/src/lib/providers/image-contract.js.map +1 -0
  192. package/src/lib/providers/model-choice.js +11 -0
  193. package/src/lib/providers/model-choice.js.map +1 -1
  194. package/src/lib/providers/routing.js +18 -0
  195. package/src/lib/providers/routing.js.map +1 -1
  196. package/src/lib/providers/vertex-image.d.ts +121 -0
  197. package/src/lib/providers/vertex-image.js +312 -0
  198. package/src/lib/providers/vertex-image.js.map +1 -0
  199. package/src/lib/runtime/ai-doctrine-validators.d.ts +19 -0
  200. package/src/lib/runtime/ai-doctrine-validators.js +89 -0
  201. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  202. package/src/lib/runtime/ai-doctrine.js +11 -5
  203. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  204. package/src/lib/runtime/ai-eval-live.js +4 -0
  205. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  206. package/src/lib/runtime/ai-eval.d.ts +15 -1
  207. package/src/lib/runtime/ai-eval.js +52 -1
  208. package/src/lib/runtime/ai-eval.js.map +1 -1
  209. package/src/lib/runtime/experiment-variant-copy.d.ts +34 -0
  210. package/src/lib/runtime/experiment-variant-copy.js +102 -0
  211. package/src/lib/runtime/experiment-variant-copy.js.map +1 -0
  212. package/src/lib/server/ai-experiment-versions.d.ts +51 -0
  213. package/src/lib/server/ai-experiment-versions.js +270 -0
  214. package/src/lib/server/ai-experiment-versions.js.map +1 -0
  215. package/src/lib/server/ai-jobs-resume.js +74 -4
  216. package/src/lib/server/ai-jobs-resume.js.map +1 -1
  217. package/src/lib/server/ai-media-image.d.ts +66 -0
  218. package/src/lib/server/ai-media-image.js +521 -0
  219. package/src/lib/server/ai-media-image.js.map +1 -0
  220. package/src/lib/server/ai-media-svg.d.ts +91 -0
  221. package/src/lib/server/ai-media-svg.js +186 -0
  222. package/src/lib/server/ai-media-svg.js.map +1 -0
  223. package/src/lib/server/assist-build.d.ts +54 -0
  224. package/src/lib/server/assist-build.js +121 -0
  225. package/src/lib/server/assist-build.js.map +1 -0
  226. package/src/lib/server/assist-chat.js +108 -12
  227. package/src/lib/server/assist-chat.js.map +1 -1
  228. package/src/lib/server/assist-edit.js +213 -3
  229. package/src/lib/server/assist-edit.js.map +1 -1
  230. package/src/lib/server/assist-view-context.js +242 -4
  231. package/src/lib/server/assist-view-context.js.map +1 -1
  232. package/src/lib/server.js +26 -0
  233. package/src/lib/server.js.map +1 -1
  234. package/src/lib/tools/ai-logic-tool.d.ts +27 -0
  235. package/src/lib/tools/ai-logic-tool.js +207 -0
  236. package/src/lib/tools/ai-logic-tool.js.map +1 -0
  237. package/src/lib/tools/ai-overlay-tool.d.ts +35 -0
  238. package/src/lib/tools/ai-overlay-tool.js +107 -0
  239. package/src/lib/tools/ai-overlay-tool.js.map +1 -0
  240. package/src/lib/tools/ai-workflow-tool.d.ts +11 -0
  241. package/src/lib/tools/ai-workflow-tool.js +31 -0
  242. package/src/lib/tools/ai-workflow-tool.js.map +1 -1
  243. package/src/lib/usage/assist-job-refund.d.ts +28 -1
  244. package/src/lib/usage/assist-job-refund.js +44 -9
  245. package/src/lib/usage/assist-job-refund.js.map +1 -1
  246. package/src/lib/usage/assist-usage.d.ts +5 -0
  247. package/src/lib/usage/assist-usage.js +2 -0
  248. package/src/lib/usage/assist-usage.js.map +1 -1
@@ -0,0 +1,207 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * @license
5
+ * Copyright 2026 Aglyn LLC
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */ import { pluginDocsHelp } from "@aglyn/aglyn/app-utils/docs-help";
19
+ import { mdiCreation } from "@aglyn/shared-data-mdi";
20
+ import { MdiIcon } from "@aglyn/shared-ui-jsx";
21
+ import { useUser } from "@aglyn/tenant-feature-instance";
22
+ import { Alert, Box, Button, CircularProgress, Link, Stack, TextField, Typography } from "@mui/material";
23
+ import { useState } from "react";
24
+ import { openAiJobs } from "./ai-jobs-store.js";
25
+ import { aiJobProblem, useAiJobRun } from "./use-ai-job-run.js";
26
+ /**
27
+ * "Fix with AI" and "Change with AI" in the editor of a saved ACTION
28
+ * (AGL-3603), through the `automationEditor` zone. Each starts a `workflow`
29
+ * job in `revise` mode: the action as it is saved, and the change asked of
30
+ * it, become a NEW automation drafted switched off. The saved action is never
31
+ * written; the person opens the copy, reads what changed, and switches it on
32
+ * in place of the old one when it is right.
33
+ *
34
+ * A workflow of function calls is not offered either: the draft writer writes
35
+ * actions, and a workflow is explained rather than revised.
36
+ */ /** The longest change a job admits as its brief. */ const BRIEF_MAX_CHARS = 4000;
37
+ export const AI_AUTOMATION_REVISE_COPY = {
38
+ intro: 'Describe a change, or let AI fix what would stop it working. You get a changed copy, switched off, to review — this one is left as it is.',
39
+ label: 'What should change?',
40
+ placeholder: 'Also tag them “newsletter”, and wait a day before the email',
41
+ change: 'Change with AI',
42
+ fix: 'Fix with AI',
43
+ running: 'Drafting the changed copy. This can take a few minutes.',
44
+ drafted: 'Changed copy drafted',
45
+ review: 'Review the copy',
46
+ notListed: 'The copy is saved. It appears in the list in a moment.',
47
+ failed: 'The automation could not be changed. Try again.',
48
+ fixBrief: (name)=>`Fix whatever in “${name}” would stop it working as intended — a record the site no longer has, a condition that can never match, a step in the wrong order. Change nothing else.`
49
+ };
50
+ export function AiReviseAutomation({ hostId, orgId, target, openAction }) {
51
+ var _aiJobProblem;
52
+ const copy = AI_AUTOMATION_REVISE_COPY;
53
+ const help = pluginDocsHelp('aiAutomations', {
54
+ anchor: '#change'
55
+ });
56
+ const { data: user } = useUser();
57
+ const run = useAiJobRun(user, copy.failed);
58
+ const [brief, setBrief] = useState('');
59
+ const [notListed, setNotListed] = useState(false);
60
+ if (target.type !== 'action') return null;
61
+ const { job } = run;
62
+ const busy = run.starting || run.running;
63
+ const draft = (job == null ? void 0 : job.status) === 'done' ? job.outputs.find((output)=>output.resource === 'workflow') : undefined;
64
+ const problem = (_aiJobProblem = aiJobProblem(job, copy.failed)) != null ? _aiJobProblem : (job == null ? void 0 : job.status) === 'done' && !draft ? copy.failed : null;
65
+ const start = async (text)=>{
66
+ const trimmed = text.trim();
67
+ if (!orgId || !trimmed) return;
68
+ setNotListed(false);
69
+ if (await run.start({
70
+ orgId,
71
+ hostId,
72
+ kind: 'workflow',
73
+ brief: trimmed,
74
+ inputs: {
75
+ mode: 'revise',
76
+ targetType: 'action',
77
+ targetId: target.id
78
+ }
79
+ })) {
80
+ setBrief('');
81
+ }
82
+ };
83
+ const review = ()=>{
84
+ if (!draft) return;
85
+ if (!(openAction == null ? void 0 : openAction(draft.id))) setNotListed(true);
86
+ };
87
+ return /*#__PURE__*/ _jsx(Box, {
88
+ sx: {
89
+ border: 1,
90
+ borderColor: 'divider',
91
+ borderRadius: 1,
92
+ p: 1.5
93
+ },
94
+ "aria-label": "Change this automation with AI",
95
+ children: /*#__PURE__*/ _jsxs(Stack, {
96
+ spacing: 1,
97
+ children: [
98
+ /*#__PURE__*/ _jsxs(Typography, {
99
+ variant: "body2",
100
+ color: "text.secondary",
101
+ children: [
102
+ copy.intro,
103
+ ' ',
104
+ /*#__PURE__*/ _jsx(Link, {
105
+ href: help.href,
106
+ target: "_blank",
107
+ rel: "noopener",
108
+ title: help.excerpt,
109
+ children: 'How it works'
110
+ })
111
+ ]
112
+ }),
113
+ /*#__PURE__*/ _jsx(TextField, {
114
+ size: "small",
115
+ label: copy.label,
116
+ placeholder: copy.placeholder,
117
+ value: brief,
118
+ multiline: true,
119
+ maxRows: 4,
120
+ onChange: (event)=>setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS)),
121
+ disabled: busy
122
+ }),
123
+ /*#__PURE__*/ _jsxs(Stack, {
124
+ direction: "row",
125
+ spacing: 1,
126
+ children: [
127
+ /*#__PURE__*/ _jsx(Button, {
128
+ size: "small",
129
+ variant: "outlined",
130
+ startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
131
+ path: mdiCreation.path
132
+ }),
133
+ disabled: busy || !orgId || !brief.trim(),
134
+ onClick: ()=>void start(brief),
135
+ children: copy.change
136
+ }),
137
+ /*#__PURE__*/ _jsx(Button, {
138
+ size: "small",
139
+ variant: "text",
140
+ startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
141
+ path: mdiCreation.path
142
+ }),
143
+ disabled: busy || !orgId,
144
+ onClick: ()=>void start(copy.fixBrief(target.name)),
145
+ children: copy.fix
146
+ })
147
+ ]
148
+ }),
149
+ run.running && job ? /*#__PURE__*/ _jsx(Alert, {
150
+ severity: "info",
151
+ icon: /*#__PURE__*/ _jsx(CircularProgress, {
152
+ size: 18,
153
+ "aria-label": "Drafting"
154
+ }),
155
+ action: /*#__PURE__*/ _jsx(Button, {
156
+ color: "inherit",
157
+ size: "small",
158
+ variant: "outlined",
159
+ onClick: ()=>openAiJobs({
160
+ jobId: job.id
161
+ }),
162
+ children: 'Open AI jobs'
163
+ }),
164
+ children: copy.running
165
+ }) : null,
166
+ draft ? /*#__PURE__*/ _jsxs(Alert, {
167
+ severity: "success",
168
+ action: openAction ? /*#__PURE__*/ _jsx(Button, {
169
+ color: "inherit",
170
+ size: "small",
171
+ variant: "outlined",
172
+ onClick: review,
173
+ children: copy.review
174
+ }) : undefined,
175
+ children: [
176
+ /*#__PURE__*/ _jsx(Typography, {
177
+ variant: "body2",
178
+ sx: {
179
+ fontWeight: 600
180
+ },
181
+ children: `${copy.drafted}: ${draft.label}`
182
+ }),
183
+ draft.note ? /*#__PURE__*/ _jsx(Typography, {
184
+ variant: "body2",
185
+ children: draft.note
186
+ }) : null
187
+ ]
188
+ }) : null,
189
+ problem ? /*#__PURE__*/ _jsx(Alert, {
190
+ severity: (job == null ? void 0 : job.status) === 'failed' ? 'error' : 'warning',
191
+ children: problem
192
+ }) : null,
193
+ run.notice ? /*#__PURE__*/ _jsx(Alert, {
194
+ severity: "warning",
195
+ children: run.notice
196
+ }) : null,
197
+ notListed ? /*#__PURE__*/ _jsx(Alert, {
198
+ severity: "info",
199
+ children: copy.notListed
200
+ }) : null
201
+ ]
202
+ })
203
+ });
204
+ }
205
+ export default AiReviseAutomation;
206
+
207
+ //# sourceMappingURL=ai-revise-automation.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-revise-automation.component.tsx"],"sourcesContent":["'use client'\n\n/**\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\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { mdiCreation } from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Box,\n Button,\n CircularProgress,\n Link,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useState } from 'react'\nimport { openAiJobs } from './ai-jobs-store'\nimport type { ConsoleAutomationEditorZoneProps } from './ai-automation-zones'\nimport { aiJobProblem, useAiJobRun } from './use-ai-job-run'\n\n/**\n * \"Fix with AI\" and \"Change with AI\" in the editor of a saved ACTION\n * (AGL-3603), through the `automationEditor` zone. Each starts a `workflow`\n * job in `revise` mode: the action as it is saved, and the change asked of\n * it, become a NEW automation drafted switched off. The saved action is never\n * written; the person opens the copy, reads what changed, and switches it on\n * in place of the old one when it is right.\n *\n * A workflow of function calls is not offered either: the draft writer writes\n * actions, and a workflow is explained rather than revised.\n */\n\n/** The longest change a job admits as its brief. */\nconst BRIEF_MAX_CHARS = 4_000\n\nexport const AI_AUTOMATION_REVISE_COPY = {\n intro: 'Describe a change, or let AI fix what would stop it working. You get a changed copy, switched off, to review — this one is left as it is.',\n label: 'What should change?',\n placeholder: 'Also tag them “newsletter”, and wait a day before the email',\n change: 'Change with AI',\n fix: 'Fix with AI',\n running: 'Drafting the changed copy. This can take a few minutes.',\n drafted: 'Changed copy drafted',\n review: 'Review the copy',\n notListed: 'The copy is saved. It appears in the list in a moment.',\n failed: 'The automation could not be changed. Try again.',\n fixBrief: (name: string) =>\n `Fix whatever in “${name}” would stop it working as intended — a record the site no longer has, a condition that can never match, a step in the wrong order. Change nothing else.`,\n} as const\n\nexport function AiReviseAutomation({ hostId, orgId, target, openAction }: ConsoleAutomationEditorZoneProps) {\n const copy = AI_AUTOMATION_REVISE_COPY\n const help = pluginDocsHelp('aiAutomations', { anchor: '#change' })\n const { data: user } = useUser()\n const run = useAiJobRun(user, copy.failed)\n const [brief, setBrief] = useState('')\n const [notListed, setNotListed] = useState(false)\n\n if (target.type !== 'action') return null\n const { job } = run\n const busy = run.starting || run.running\n const draft = job?.status === 'done' ? job.outputs.find((output) => output.resource === 'workflow') : undefined\n const problem = aiJobProblem(job, copy.failed) ?? (job?.status === 'done' && !draft ? copy.failed : null)\n\n const start = async (text: string) => {\n const trimmed = text.trim()\n if (!orgId || !trimmed) return\n setNotListed(false)\n if (\n await run.start({\n orgId,\n hostId,\n kind: 'workflow',\n brief: trimmed,\n inputs: { mode: 'revise', targetType: 'action', targetId: target.id },\n })\n ) {\n setBrief('')\n }\n }\n\n const review = () => {\n if (!draft) return\n if (!openAction?.(draft.id)) setNotListed(true)\n }\n\n return (\n <Box sx={{ border: 1, borderColor: 'divider', borderRadius: 1, p: 1.5 }} aria-label=\"Change this automation with AI\">\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {copy.intro}{' '}\n <Link href={help.href} target=\"_blank\" rel=\"noopener\" title={help.excerpt}>\n {'How it works'}\n </Link>\n </Typography>\n <TextField\n size=\"small\"\n label={copy.label}\n placeholder={copy.placeholder}\n value={brief}\n multiline\n maxRows={4}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n disabled={busy}\n />\n <Stack direction=\"row\" spacing={1}>\n <Button\n size=\"small\"\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiCreation.path} />}\n disabled={busy || !orgId || !brief.trim()}\n onClick={() => void start(brief)}\n >\n {copy.change}\n </Button>\n <Button\n size=\"small\"\n variant=\"text\"\n startIcon={<MdiIcon path={mdiCreation.path} />}\n disabled={busy || !orgId}\n onClick={() => void start(copy.fixBrief(target.name))}\n >\n {copy.fix}\n </Button>\n </Stack>\n {run.running && job ? (\n <Alert\n severity=\"info\"\n icon={<CircularProgress size={18} aria-label=\"Drafting\" />}\n action={\n <Button color=\"inherit\" size=\"small\" variant=\"outlined\" onClick={() => openAiJobs({ jobId: job.id })}>\n {'Open AI jobs'}\n </Button>\n }\n >\n {copy.running}\n </Alert>\n ) : null}\n {draft ? (\n <Alert\n severity=\"success\"\n action={\n openAction ? (\n <Button color=\"inherit\" size=\"small\" variant=\"outlined\" onClick={review}>\n {copy.review}\n </Button>\n ) : undefined\n }\n >\n <Typography variant=\"body2\" sx={{ fontWeight: 600 }}>\n {`${copy.drafted}: ${draft.label}`}\n </Typography>\n {draft.note ? <Typography variant=\"body2\">{draft.note}</Typography> : null}\n </Alert>\n ) : null}\n {problem ? <Alert severity={job?.status === 'failed' ? 'error' : 'warning'}>{problem}</Alert> : null}\n {run.notice ? <Alert severity=\"warning\">{run.notice}</Alert> : null}\n {notListed ? <Alert severity=\"info\">{copy.notListed}</Alert> : null}\n </Stack>\n </Box>\n )\n}\n\nexport default AiReviseAutomation\n"],"names":["pluginDocsHelp","mdiCreation","MdiIcon","useUser","Alert","Box","Button","CircularProgress","Link","Stack","TextField","Typography","useState","openAiJobs","aiJobProblem","useAiJobRun","BRIEF_MAX_CHARS","AI_AUTOMATION_REVISE_COPY","intro","label","placeholder","change","fix","running","drafted","review","notListed","failed","fixBrief","name","AiReviseAutomation","hostId","orgId","target","openAction","copy","help","anchor","data","user","run","brief","setBrief","setNotListed","type","job","busy","starting","draft","status","outputs","find","output","resource","undefined","problem","start","text","trimmed","trim","kind","inputs","mode","targetType","targetId","id","sx","border","borderColor","borderRadius","p","aria-label","spacing","variant","color","href","rel","title","excerpt","size","value","multiline","maxRows","onChange","event","slice","disabled","direction","startIcon","path","onClick","severity","icon","action","jobId","fontWeight","note","notice"],"mappings":"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"}
@@ -17,18 +17,20 @@
17
17
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
18
  import { PLATFORM_BRAND_NAME } from "@aglyn/aglyn";
19
19
  import { PageHeaderRecord } from "@aglyn/aglyn/app-utils/page-header-record-context";
20
- import { mdiAlertCircle, mdiCheckCircle, mdiChevronDown, mdiClockOutline } from "@aglyn/shared-data-mdi";
20
+ import { mdiAlertCircle, mdiCheckCircle, mdiChevronDown, mdiClockOutline, mdiMinusCircleOutline } from "@aglyn/shared-data-mdi";
21
21
  import { AppLink, MdiIcon } from "@aglyn/shared-ui-jsx";
22
22
  import { useUser } from "@aglyn/tenant-feature-instance";
23
23
  import { Accordion, AccordionDetails, AccordionSummary, Alert, Box, Button, Card, CircularProgress, Container, LinearProgress, Stack, Typography } from "@mui/material";
24
24
  import { useCallback, useEffect, useRef, useState } from "react";
25
- import { aiJobPageCopy, aiSiteBuildCreditsLine, aiSiteBuildPhase, aiSiteBuildRows } from "../model/ai-site-build-progress.js";
25
+ import { aiJobPageCopy, aiSiteBuildCreditsLine, aiSiteBuildFraction, aiSiteBuildPhase, aiSiteBuildRows } from "../model/ai-site-build-progress.js";
26
26
  import { aiJobRefundCopy, aiSiteStarterFallbackOffered } from "../model/ai-job-failure-copy.js";
27
27
  import { aiSiteStartAnswersFromInputs } from "../model/ai-site-start.js";
28
28
  import { AiSiteStartCard } from "./ai-site-start-card.component.js";
29
29
  import { AI_JOB_TERMINAL_STATUSES } from "../model/ai-jobs.types.js";
30
30
  import { followAiJobEvents } from "./ai-job-events.js";
31
31
  import { aiSiteBuildDoneLinks } from "./ai-job-links.js";
32
+ import { AiJobPlan } from "./ai-job-plan.component.js";
33
+ import { aiBuildCanRetry } from "../model/ai-build-progress.js";
32
34
  import { resumeAiJobRequest } from "./ai-job-requests.js";
33
35
  import { useAiJobSite } from "./ai-job-site.js";
34
36
  import { AiSiteStarterFallback } from "./ai-site-starter-fallback.component.js";
@@ -113,6 +115,11 @@ const ROW_ICON = {
113
115
  path: mdiAlertCircle.path,
114
116
  color: 'error.main',
115
117
  label: 'Stopped'
118
+ },
119
+ skipped: {
120
+ path: mdiMinusCircleOutline.path,
121
+ color: 'text.disabled',
122
+ label: 'Not built'
116
123
  }
117
124
  };
118
125
  function RowIcon({ state }) {
@@ -138,7 +145,30 @@ function RowIcon({ state }) {
138
145
  })
139
146
  });
140
147
  }
141
- /** The page's frame: the site's name over the heading and its sentence. */ function Frame({ siteName, heading, lede, children }) {
148
+ /** The page's frame: the site's name over the heading and its sentence. */ /** "0:42", "3:05": how long the active row has been running, from when it started or was first seen. */ function useElapsed(key, startedAt) {
149
+ const [now, setNow] = useState(()=>Date.now());
150
+ const seen = useRef({
151
+ key: null,
152
+ at: Date.now()
153
+ });
154
+ if (seen.current.key !== key) seen.current = {
155
+ key,
156
+ at: Date.now()
157
+ };
158
+ useEffect(()=>{
159
+ if (!key) return;
160
+ const timer = setInterval(()=>setNow(Date.now()), 1000);
161
+ return ()=>clearInterval(timer);
162
+ }, [
163
+ key
164
+ ]);
165
+ if (!key) return null;
166
+ const started = startedAt ? Date.parse(startedAt) : NaN;
167
+ const from = Number.isFinite(started) ? Math.min(started, seen.current.at) : seen.current.at;
168
+ const seconds = Math.max(0, Math.floor((now - from) / 1000));
169
+ return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, '0')}`;
170
+ }
171
+ function Frame({ siteName, heading, lede, children }) {
142
172
  return /*#__PURE__*/ _jsx(Container, {
143
173
  maxWidth: "sm",
144
174
  sx: {
@@ -180,8 +210,8 @@ function RowIcon({ state }) {
180
210
  });
181
211
  }
182
212
  export function AiSiteBuildPage({ hostId, segments, basePath }) {
183
- var _split_, _ready_sitePublish, _ready_refundedCredits;
184
- var _ready_review, _ready_review1;
213
+ var _split_, _aiSiteBuildRows_find, _rows_find, _ready_sitePublish, _ready_refundedCredits;
214
+ var _ready_review, _ready_review1, _ready_review2;
185
215
  const { data: user } = useUser();
186
216
  const jobId = (segments == null ? void 0 : segments[0]) ? decodeURIComponent(String(segments[0])) : null;
187
217
  const orgSlug = (_split_ = (basePath != null ? basePath : '').split('/')[1]) != null ? _split_ : '';
@@ -200,7 +230,10 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
200
230
  if (!orgId || !job || typeof job !== 'object') return;
201
231
  setBusy(true);
202
232
  setNotice(null);
203
- const decision = await resumeAiJobRequest(user, orgId, job);
233
+ // A finished build tries again only what failed (AGL-3616).
234
+ const decision = await resumeAiJobRequest(user, orgId, job, aiBuildCanRetry(job) ? {
235
+ retry: 'failed-items'
236
+ } : {});
204
237
  if (decision.job) setJob(decision.job);
205
238
  if (decision.error) setNotice(decision.error);
206
239
  setBusy(false);
@@ -210,8 +243,24 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
210
243
  user,
211
244
  setJob
212
245
  ]);
246
+ const confirmPlan = useCallback(async (target, options = {})=>{
247
+ if (!orgId) return;
248
+ setBusy(true);
249
+ setNotice(null);
250
+ const decision = await resumeAiJobRequest(user, orgId, target, options);
251
+ if (decision.job) setJob(decision.job);
252
+ if (decision.error) setNotice(decision.error);
253
+ setBusy(false);
254
+ }, [
255
+ orgId,
256
+ user,
257
+ setJob
258
+ ]);
213
259
  const ready = job && typeof job === 'object' ? job : null;
214
260
  const copy = ready ? aiJobPageCopy(ready, PLATFORM_BRAND_NAME) : null;
261
+ // The active row's running time, read before any early return (a hook).
262
+ const liveRow = ready && aiSiteBuildPhase(ready) === 'working' ? (_aiSiteBuildRows_find = aiSiteBuildRows(ready).find((row)=>row.state === 'active')) != null ? _aiSiteBuildRows_find : null : null;
263
+ const elapsed = useElapsed(liveRow ? `${ready == null ? void 0 : ready.id}:${liveRow.id}` : null, liveRow == null ? void 0 : liveRow.startedAt);
215
264
  // The shell's header names the job's page as the page does, and its trail
216
265
  // walks back to the site's AI jobs.
217
266
  const record = /*#__PURE__*/ _jsx(PageHeaderRecord, {
@@ -279,6 +328,8 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
279
328
  }
280
329
  const phase = aiSiteBuildPhase(ready);
281
330
  const rows = aiSiteBuildRows(ready);
331
+ const fraction = aiSiteBuildFraction(rows);
332
+ const activeRow = phase === 'working' ? (_rows_find = rows.find((row)=>row.state === 'active')) != null ? _rows_find : null : null;
282
333
  const credits = aiSiteBuildCreditsLine(ready);
283
334
  const links = phase === 'done' ? aiSiteBuildDoneLinks(ready, orgSlug) : {
284
335
  view: null,
@@ -287,7 +338,9 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
287
338
  const sitePublish = ready.kind === 'site' && phase === 'done' ? (_ready_sitePublish = ready.sitePublish) != null ? _ready_sitePublish : null : null;
288
339
  const liveUrl = sitePublish && sitePublish.published.length > 0 ? sitePublish.liveUrl : null;
289
340
  const retryRefusal = (_ready_review = ready.review) == null ? void 0 : _ready_review.retryRefusal;
290
- const canRetry = phase === 'stopped' && ((_ready_review1 = ready.review) == null ? void 0 : _ready_review1.reason) === 'doctrine';
341
+ const buildRetry = aiBuildCanRetry(ready);
342
+ const canRetry = phase === 'stopped' && ((_ready_review1 = ready.review) == null ? void 0 : _ready_review1.reason) === 'doctrine' || buildRetry;
343
+ const buildPlanWaiting = ready.kind === 'build' && phase === 'stopped' && ((_ready_review2 = ready.review) == null ? void 0 : _ready_review2.reason) === 'plan';
291
344
  // A guided start that ended without building its site starts over from its
292
345
  // own answers, as a fresh job: the questions reopen filled in.
293
346
  const restartAnswers = ready.kind === 'site' && (phase === 'failed' || phase === 'canceled') ? aiSiteStartAnswersFromInputs(ready.siteInputs) : null;
@@ -308,9 +361,13 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
308
361
  borderRadius: 2
309
362
  },
310
363
  children: [
311
- phase === 'working' && /*#__PURE__*/ _jsx(LinearProgress, {
364
+ phase === 'working' && (fraction !== null ? /*#__PURE__*/ _jsx(LinearProgress, {
365
+ variant: "determinate",
366
+ value: Math.round(fraction * 100),
367
+ "aria-label": `Building: ${rows.filter((row)=>row.state === 'done').length} of ${rows.length} steps done`
368
+ }) : /*#__PURE__*/ _jsx(LinearProgress, {
312
369
  "aria-label": "Building"
313
- }),
370
+ })),
314
371
  /*#__PURE__*/ _jsx(Box, {
315
372
  component: "ol",
316
373
  "aria-label": "Progress",
@@ -324,12 +381,14 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
324
381
  },
325
382
  children: /*#__PURE__*/ _jsx(Stack, {
326
383
  spacing: 2,
327
- children: rows.map((row)=>/*#__PURE__*/ _jsxs(Stack, {
384
+ children: rows.map((row)=>{
385
+ var _row_sections;
386
+ return /*#__PURE__*/ _jsxs(Stack, {
328
387
  component: "li",
329
388
  direction: "row",
330
389
  spacing: 2,
331
390
  sx: {
332
- alignItems: 'center'
391
+ alignItems: 'flex-start'
333
392
  },
334
393
  children: [
335
394
  /*#__PURE__*/ _jsx(Box, {
@@ -342,13 +401,62 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
342
401
  state: row.state
343
402
  })
344
403
  }),
345
- /*#__PURE__*/ _jsx(Typography, {
346
- variant: "body1",
347
- color: row.state === 'waiting' ? 'text.secondary' : 'text.primary',
348
- children: row.label
404
+ /*#__PURE__*/ _jsxs(Box, {
405
+ sx: {
406
+ flex: 1,
407
+ minWidth: 0
408
+ },
409
+ children: [
410
+ /*#__PURE__*/ _jsxs(Stack, {
411
+ direction: "row",
412
+ spacing: 1,
413
+ sx: {
414
+ alignItems: 'baseline',
415
+ justifyContent: 'space-between'
416
+ },
417
+ children: [
418
+ /*#__PURE__*/ _jsx(Typography, {
419
+ variant: "body1",
420
+ color: row.state === 'waiting' || row.state === 'skipped' ? 'text.secondary' : 'text.primary',
421
+ children: row.label
422
+ }),
423
+ row.id === (activeRow == null ? void 0 : activeRow.id) && elapsed ? /*#__PURE__*/ _jsx(Typography, {
424
+ variant: "body2",
425
+ color: "text.secondary",
426
+ sx: {
427
+ fontVariantNumeric: 'tabular-nums'
428
+ },
429
+ children: elapsed
430
+ }) : typeof row.credits === 'number' && row.credits > 0 ? /*#__PURE__*/ _jsx(Typography, {
431
+ variant: "body2",
432
+ color: "text.secondary",
433
+ sx: {
434
+ whiteSpace: 'nowrap'
435
+ },
436
+ children: `${row.credits} ${row.credits === 1 ? 'credit' : 'credits'}`
437
+ }) : null
438
+ ]
439
+ }),
440
+ row.id === (activeRow == null ? void 0 : activeRow.id) && row.hint ? /*#__PURE__*/ _jsx(Typography, {
441
+ variant: "body2",
442
+ color: "text.secondary",
443
+ children: row.hint
444
+ }) : null,
445
+ ((_row_sections = row.sections) == null ? void 0 : _row_sections.length) && (row.state === 'active' || row.state === 'done') ? /*#__PURE__*/ _jsx(Typography, {
446
+ variant: "body2",
447
+ color: "text.secondary",
448
+ children: `${row.state === 'active' ? 'Writing' : 'Wrote'}: ${row.sections.join(', ')}`
449
+ }) : null,
450
+ row.detail ? /*#__PURE__*/ _jsx(Typography, {
451
+ variant: "body2",
452
+ color: "text.secondary",
453
+ children: row.detail
454
+ }) : null
455
+ ]
349
456
  })
350
457
  ]
351
- }, row.id))
458
+ }, row.id);
459
+ })
352
460
  })
353
461
  })
354
462
  ]
@@ -370,6 +478,12 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
370
478
  startBlank: ()=>setRetrying(false),
371
479
  leave: ()=>setRetrying(false)
372
480
  }) : null,
481
+ buildPlanWaiting ? // A build's plan, confirmed here as in the chat (AGL-3616).
482
+ /*#__PURE__*/ _jsx(AiJobPlan, {
483
+ job: ready,
484
+ busy: busy,
485
+ onResume: (target, options)=>void confirmPlan(target, options)
486
+ }) : null,
373
487
  sitePublish && sitePublish.drafts.length > 0 && // The pages the publish left as drafts, each with its plain reason;
374
488
  // Edit your pages is where they are fixed and published.
375
489
  /*#__PURE__*/ _jsxs(Alert, {
@@ -404,6 +518,14 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
404
518
  })
405
519
  ]
406
520
  }),
521
+ phase === 'done' && buildRetry && /*#__PURE__*/ _jsx(Box, {
522
+ children: /*#__PURE__*/ _jsx(Button, {
523
+ variant: "outlined",
524
+ disabled: busy,
525
+ onClick: ()=>void tryAgain(),
526
+ children: 'Try again what failed'
527
+ })
528
+ }),
407
529
  phase === 'done' && /*#__PURE__*/ _jsxs(Stack, {
408
530
  direction: {
409
531
  xs: 'column',
@@ -476,6 +598,8 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
476
598
  ]
477
599
  }),
478
600
  ready.plan && ready.plan.screens.length > 0 && /*#__PURE__*/ _jsxs(Accordion, {
601
+ // Open while the site is built, so the plan is in view as each part of it is written.
602
+ defaultExpanded: phase === 'working',
479
603
  variant: "outlined",
480
604
  disableGutters: true,
481
605
  sx: {