@aglyn/plugins-ai 1.0.0-beta.230 → 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 (305) 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-assist-provider-on-host.component.d.ts +66 -0
  5. package/src/lib/components/ai-assist-provider-on-host.component.js +78 -0
  6. package/src/lib/components/ai-assist-provider-on-host.component.js.map +1 -0
  7. package/src/lib/components/ai-assist-provider.component.js +9 -10
  8. package/src/lib/components/ai-assist-provider.component.js.map +1 -1
  9. package/src/lib/components/ai-job-follow.component.js +1 -0
  10. package/src/lib/components/ai-job-follow.component.js.map +1 -1
  11. package/src/lib/components/ai-job-links.js +13 -0
  12. package/src/lib/components/ai-job-links.js.map +1 -1
  13. package/src/lib/components/ai-job-plan.component.js +3 -1
  14. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  15. package/src/lib/components/ai-jobs-list-page.component.js +1 -0
  16. package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
  17. package/src/lib/components/ai-media-create.component.d.ts +20 -7
  18. package/src/lib/components/ai-media-create.component.js +107 -55
  19. package/src/lib/components/ai-media-create.component.js.map +1 -1
  20. package/src/lib/components/ai-permissions-on-host.component.d.ts +3 -46
  21. package/src/lib/components/ai-permissions-on-host.component.js +5 -53
  22. package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
  23. package/src/lib/components/ai-seo-fields-card.component.js +1 -0
  24. package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
  25. package/src/lib/components/ai-site-build-page.component.js +20 -8
  26. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  27. package/src/lib/components/ai-site-memory-card.component.d.ts +32 -0
  28. package/src/lib/components/ai-site-memory-card.component.js +187 -0
  29. package/src/lib/components/ai-site-memory-card.component.js.map +1 -0
  30. package/src/lib/components/ai-site-start-card.component.js +80 -77
  31. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  32. package/src/lib/components/ai-theme-proposal-card.component.js +4 -0
  33. package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
  34. package/src/lib/components/ai-usage-strip.component.js +1 -0
  35. package/src/lib/components/ai-usage-strip.component.js.map +1 -1
  36. package/src/lib/components/assist-panel.component.d.ts +10 -0
  37. package/src/lib/components/assist-panel.component.js +119 -9
  38. package/src/lib/components/assist-panel.component.js.map +1 -1
  39. package/src/lib/components/assist-signals-page.component.js +2 -0
  40. package/src/lib/components/assist-signals-page.component.js.map +1 -1
  41. package/src/lib/components/billing-ai-allotments.component.js +3 -0
  42. package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
  43. package/src/lib/components/billing-ai-top-users.component.js +1 -0
  44. package/src/lib/components/billing-ai-top-users.component.js.map +1 -1
  45. package/src/lib/components/billing-assist-overage-card.component.js +2 -0
  46. package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
  47. package/src/lib/components/member-ai-usage-card.component.js +10 -0
  48. package/src/lib/components/member-ai-usage-card.component.js.map +1 -1
  49. package/src/lib/components/staff-org-ai-card.component.js +1 -0
  50. package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
  51. package/src/lib/components/staff-org-ai-conversations.component.d.ts +23 -0
  52. package/src/lib/components/staff-org-ai-conversations.component.js +346 -0
  53. package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -0
  54. package/src/lib/jobs/ai-build-capabilities.d.ts +13 -1
  55. package/src/lib/jobs/ai-build-capabilities.js +101 -7
  56. package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
  57. package/src/lib/jobs/ai-build-unit-outcome.js +4 -2
  58. package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
  59. package/src/lib/jobs/ai-business-profile-prefill.d.ts +29 -0
  60. package/src/lib/jobs/ai-business-profile-prefill.js +85 -0
  61. package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -0
  62. package/src/lib/jobs/ai-job-auto-confirm.d.ts +8 -0
  63. package/src/lib/jobs/ai-job-auto-confirm.js +30 -6
  64. package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -1
  65. package/src/lib/jobs/ai-job-build-step.js +14 -1
  66. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  67. package/src/lib/jobs/ai-job-edit-step.d.ts +225 -0
  68. package/src/lib/jobs/ai-job-edit-step.js +631 -0
  69. package/src/lib/jobs/ai-job-edit-step.js.map +1 -0
  70. package/src/lib/jobs/ai-job-generation.d.ts +8 -0
  71. package/src/lib/jobs/ai-job-generation.js +13 -2
  72. package/src/lib/jobs/ai-job-generation.js.map +1 -1
  73. package/src/lib/jobs/ai-job-insight-step.js +29 -5
  74. package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
  75. package/src/lib/jobs/ai-job-layout-language.d.ts +78 -0
  76. package/src/lib/jobs/ai-job-layout-language.js +200 -0
  77. package/src/lib/jobs/ai-job-layout-language.js.map +1 -0
  78. package/src/lib/jobs/ai-job-layout-site-pages.d.ts +13 -2
  79. package/src/lib/jobs/ai-job-layout-site-pages.js +18 -2
  80. package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
  81. package/src/lib/jobs/ai-job-layout-step.d.ts +5 -0
  82. package/src/lib/jobs/ai-job-layout-step.js +107 -6
  83. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  84. package/src/lib/jobs/ai-job-logic-step.d.ts +20 -0
  85. package/src/lib/jobs/ai-job-logic-step.js +87 -6
  86. package/src/lib/jobs/ai-job-logic-step.js.map +1 -1
  87. package/src/lib/jobs/ai-job-page-language.d.ts +194 -0
  88. package/src/lib/jobs/ai-job-page-language.js +418 -0
  89. package/src/lib/jobs/ai-job-page-language.js.map +1 -0
  90. package/src/lib/jobs/ai-job-page-sections.d.ts +37 -1
  91. package/src/lib/jobs/ai-job-page-sections.js +93 -11
  92. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  93. package/src/lib/jobs/ai-job-page-step.d.ts +4 -4
  94. package/src/lib/jobs/ai-job-page-step.js +115 -14
  95. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  96. package/src/lib/jobs/ai-job-plan-step.d.ts +20 -6
  97. package/src/lib/jobs/ai-job-plan-step.js +62 -22
  98. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  99. package/src/lib/jobs/ai-job-site-content.d.ts +163 -0
  100. package/src/lib/jobs/ai-job-site-content.js +443 -0
  101. package/src/lib/jobs/ai-job-site-content.js.map +1 -0
  102. package/src/lib/jobs/ai-job-site-look.d.ts +112 -0
  103. package/src/lib/jobs/ai-job-site-look.js +322 -0
  104. package/src/lib/jobs/ai-job-site-look.js.map +1 -0
  105. package/src/lib/jobs/ai-job-site-step.d.ts +30 -5
  106. package/src/lib/jobs/ai-job-site-step.js +183 -27
  107. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  108. package/src/lib/jobs/ai-job-workflow-step.js +2 -2
  109. package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
  110. package/src/lib/jobs/ai-jobs-notify.d.ts +6 -1
  111. package/src/lib/jobs/ai-jobs-notify.js +15 -3
  112. package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
  113. package/src/lib/jobs/ai-jobs.d.ts +1 -1
  114. package/src/lib/jobs/ai-jobs.js +17 -8
  115. package/src/lib/jobs/ai-jobs.js.map +1 -1
  116. package/src/lib/jobs/ai-layout-full-height.d.ts +40 -0
  117. package/src/lib/jobs/ai-layout-full-height.js +82 -0
  118. package/src/lib/jobs/ai-layout-full-height.js.map +1 -0
  119. package/src/lib/jobs/ai-layout-site-facts.d.ts +54 -0
  120. package/src/lib/jobs/ai-layout-site-facts.js +153 -0
  121. package/src/lib/jobs/ai-layout-site-facts.js.map +1 -0
  122. package/src/lib/layout-language/ai-layout-compiler.d.ts +154 -0
  123. package/src/lib/layout-language/ai-layout-compiler.js +1235 -0
  124. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -0
  125. package/src/lib/layout-language/ai-layout-copy.d.ts +73 -0
  126. package/src/lib/layout-language/ai-layout-copy.js +137 -0
  127. package/src/lib/layout-language/ai-layout-copy.js.map +1 -0
  128. package/src/lib/layout-language/ai-layout-frame.d.ts +52 -0
  129. package/src/lib/layout-language/ai-layout-frame.js +458 -0
  130. package/src/lib/layout-language/ai-layout-frame.js.map +1 -0
  131. package/src/lib/layout-language/ai-layout-gaps.d.ts +63 -0
  132. package/src/lib/layout-language/ai-layout-gaps.js +345 -0
  133. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -0
  134. package/src/lib/layout-language/ai-layout-language.d.ts +168 -0
  135. package/src/lib/layout-language/ai-layout-language.js +529 -0
  136. package/src/lib/layout-language/ai-layout-language.js.map +1 -0
  137. package/src/lib/layout-language/ai-layout-links.d.ts +80 -0
  138. package/src/lib/layout-language/ai-layout-links.js +109 -0
  139. package/src/lib/layout-language/ai-layout-links.js.map +1 -0
  140. package/src/lib/layout-language/ai-layout-pictures.d.ts +144 -0
  141. package/src/lib/layout-language/ai-layout-pictures.js +214 -0
  142. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -0
  143. package/src/lib/layout-language/ai-layout-store.d.ts +41 -0
  144. package/src/lib/layout-language/ai-layout-store.js +75 -0
  145. package/src/lib/layout-language/ai-layout-store.js.map +1 -0
  146. package/src/lib/layout-language/ai-layout-tree.d.ts +59 -0
  147. package/src/lib/layout-language/ai-layout-tree.js +84 -0
  148. package/src/lib/layout-language/ai-layout-tree.js.map +1 -0
  149. package/src/lib/lazy-widget.d.ts +45 -0
  150. package/src/lib/lazy-widget.js +53 -0
  151. package/src/lib/lazy-widget.js.map +1 -0
  152. package/src/lib/model/ai-build-job.js +2 -1
  153. package/src/lib/model/ai-build-job.js.map +1 -1
  154. package/src/lib/model/ai-build-progress.d.ts +20 -3
  155. package/src/lib/model/ai-build-progress.js +52 -7
  156. package/src/lib/model/ai-build-progress.js.map +1 -1
  157. package/src/lib/model/ai-edit-job.d.ts +109 -0
  158. package/src/lib/model/ai-edit-job.js +157 -0
  159. package/src/lib/model/ai-edit-job.js.map +1 -0
  160. package/src/lib/model/ai-host-publish-context.d.ts +51 -0
  161. package/src/lib/model/ai-host-publish-context.js +104 -0
  162. package/src/lib/model/ai-host-publish-context.js.map +1 -0
  163. package/src/lib/model/ai-job-activity.d.ts +22 -0
  164. package/src/lib/model/ai-job-activity.js +23 -1
  165. package/src/lib/model/ai-job-activity.js.map +1 -1
  166. package/src/lib/model/ai-job-failure-copy.d.ts +38 -1
  167. package/src/lib/model/ai-job-failure-copy.js +56 -1
  168. package/src/lib/model/ai-job-failure-copy.js.map +1 -1
  169. package/src/lib/model/ai-job-notice.d.ts +3 -1
  170. package/src/lib/model/ai-job-notice.js +31 -9
  171. package/src/lib/model/ai-job-notice.js.map +1 -1
  172. package/src/lib/model/ai-jobs.types.d.ts +22 -1
  173. package/src/lib/model/ai-jobs.types.js.map +1 -1
  174. package/src/lib/model/ai-logic-job.d.ts +18 -1
  175. package/src/lib/model/ai-logic-job.js +24 -3
  176. package/src/lib/model/ai-logic-job.js.map +1 -1
  177. package/src/lib/model/ai-media-credits.d.ts +8 -4
  178. package/src/lib/model/ai-media-credits.js +11 -4
  179. package/src/lib/model/ai-media-credits.js.map +1 -1
  180. package/src/lib/model/ai-media-kinds.d.ts +68 -0
  181. package/src/lib/model/ai-media-kinds.js +98 -0
  182. package/src/lib/model/ai-media-kinds.js.map +1 -0
  183. package/src/lib/model/ai-site-build-progress.d.ts +9 -1
  184. package/src/lib/model/ai-site-build-progress.js +54 -14
  185. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  186. package/src/lib/model/ai-site-context.d.ts +111 -0
  187. package/src/lib/model/ai-site-context.js +222 -0
  188. package/src/lib/model/ai-site-context.js.map +1 -0
  189. package/src/lib/model/ai-site-job.d.ts +18 -2
  190. package/src/lib/model/ai-site-job.js +21 -5
  191. package/src/lib/model/ai-site-job.js.map +1 -1
  192. package/src/lib/model/ai-site-kinds.d.ts +164 -0
  193. package/src/lib/model/ai-site-kinds.js +1942 -0
  194. package/src/lib/model/ai-site-kinds.js.map +1 -0
  195. package/src/lib/model/ai-site-look.d.ts +150 -0
  196. package/src/lib/model/ai-site-look.js +1236 -0
  197. package/src/lib/model/ai-site-look.js.map +1 -0
  198. package/src/lib/model/ai-site-memory.d.ts +80 -0
  199. package/src/lib/model/ai-site-memory.js +171 -0
  200. package/src/lib/model/ai-site-memory.js.map +1 -0
  201. package/src/lib/model/ai-site-start-seo.d.ts +1 -1
  202. package/src/lib/model/ai-site-start.d.ts +15 -3
  203. package/src/lib/model/ai-site-start.js +21 -4
  204. package/src/lib/model/ai-site-start.js.map +1 -1
  205. package/src/lib/model/assist-follow-up.d.ts +92 -0
  206. package/src/lib/model/assist-follow-up.js +128 -0
  207. package/src/lib/model/assist-follow-up.js.map +1 -0
  208. package/src/lib/plugin-config.js +2 -0
  209. package/src/lib/plugin-config.js.map +1 -1
  210. package/src/lib/plugin.js +99 -48
  211. package/src/lib/plugin.js.map +1 -1
  212. package/src/lib/providers/anthropic.d.ts +9 -1
  213. package/src/lib/providers/anthropic.js +143 -9
  214. package/src/lib/providers/anthropic.js.map +1 -1
  215. package/src/lib/providers/catalog.d.ts +38 -8
  216. package/src/lib/providers/catalog.js +106 -14
  217. package/src/lib/providers/catalog.js.map +1 -1
  218. package/src/lib/providers/contract.d.ts +29 -0
  219. package/src/lib/providers/contract.js.map +1 -1
  220. package/src/lib/providers/image-contract.d.ts +22 -1
  221. package/src/lib/providers/image-contract.js +40 -1
  222. package/src/lib/providers/image-contract.js.map +1 -1
  223. package/src/lib/providers/image-endpoints.d.ts +24 -0
  224. package/src/lib/providers/image-endpoints.js +28 -0
  225. package/src/lib/providers/image-endpoints.js.map +1 -0
  226. package/src/lib/providers/model-choice.js +23 -2
  227. package/src/lib/providers/model-choice.js.map +1 -1
  228. package/src/lib/providers/routing.js +18 -0
  229. package/src/lib/providers/routing.js.map +1 -1
  230. package/src/lib/providers/vertex-image-endpoint.d.ts +48 -0
  231. package/src/lib/providers/vertex-image-endpoint.js +80 -0
  232. package/src/lib/providers/vertex-image-endpoint.js.map +1 -0
  233. package/src/lib/providers/vertex-image.d.ts +13 -27
  234. package/src/lib/providers/vertex-image.js +15 -42
  235. package/src/lib/providers/vertex-image.js.map +1 -1
  236. package/src/lib/runtime/ai-blog-post-generation.d.ts +62 -0
  237. package/src/lib/runtime/ai-blog-post-generation.js +89 -0
  238. package/src/lib/runtime/ai-blog-post-generation.js.map +1 -0
  239. package/src/lib/runtime/ai-dev-env.d.ts +53 -0
  240. package/src/lib/runtime/ai-dev-env.js +72 -0
  241. package/src/lib/runtime/ai-dev-env.js.map +1 -0
  242. package/src/lib/runtime/ai-dev-replay.d.ts +116 -0
  243. package/src/lib/runtime/ai-dev-replay.js +313 -0
  244. package/src/lib/runtime/ai-dev-replay.js.map +1 -0
  245. package/src/lib/runtime/ai-doctrine-validators.d.ts +82 -0
  246. package/src/lib/runtime/ai-doctrine-validators.js +436 -4
  247. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  248. package/src/lib/runtime/ai-doctrine.js +16 -3
  249. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  250. package/src/lib/runtime/ai-eval-live.js +4 -0
  251. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  252. package/src/lib/runtime/ai-eval.d.ts +13 -1
  253. package/src/lib/runtime/ai-eval.js +84 -1
  254. package/src/lib/runtime/ai-eval.js.map +1 -1
  255. package/src/lib/runtime/ai-icon-library.d.ts +257 -0
  256. package/src/lib/runtime/ai-icon-library.js +254 -0
  257. package/src/lib/runtime/ai-icon-library.js.map +1 -0
  258. package/src/lib/runtime/ai-live-batch.d.ts +45 -0
  259. package/src/lib/runtime/ai-live-batch.js +89 -0
  260. package/src/lib/runtime/ai-live-batch.js.map +1 -0
  261. package/src/lib/runtime/ai-node-tree.d.ts +9 -0
  262. package/src/lib/runtime/ai-node-tree.js +30 -4
  263. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  264. package/src/lib/runtime/ai-palette.d.ts +6 -0
  265. package/src/lib/runtime/ai-palette.generated.js +70 -0
  266. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  267. package/src/lib/runtime/ai-palette.js.map +1 -1
  268. package/src/lib/runtime/ai-runtime.js +46 -2
  269. package/src/lib/runtime/ai-runtime.js.map +1 -1
  270. package/src/lib/runtime/site-context.d.ts +58 -0
  271. package/src/lib/runtime/site-context.js +127 -0
  272. package/src/lib/runtime/site-context.js.map +1 -0
  273. package/src/lib/server/ai-admin-conversations.d.ts +22 -0
  274. package/src/lib/server/ai-admin-conversations.js +197 -0
  275. package/src/lib/server/ai-admin-conversations.js.map +1 -0
  276. package/src/lib/server/ai-media-image.d.ts +5 -5
  277. package/src/lib/server/ai-media-image.js +25 -11
  278. package/src/lib/server/ai-media-image.js.map +1 -1
  279. package/src/lib/server/ai-media-raster-prompt.d.ts +34 -0
  280. package/src/lib/server/ai-media-raster-prompt.js +53 -0
  281. package/src/lib/server/ai-media-raster-prompt.js.map +1 -0
  282. package/src/lib/server/assist-chat.d.ts +11 -0
  283. package/src/lib/server/assist-chat.js +57 -18
  284. package/src/lib/server/assist-chat.js.map +1 -1
  285. package/src/lib/server/assist-edit-applied.js +16 -3
  286. package/src/lib/server/assist-edit-applied.js.map +1 -1
  287. package/src/lib/server/assist-view-context.d.ts +19 -1
  288. package/src/lib/server/assist-view-context.js +38 -1
  289. package/src/lib/server/assist-view-context.js.map +1 -1
  290. package/src/lib/server.js +10 -0
  291. package/src/lib/server.js.map +1 -1
  292. package/src/lib/subprocessors.js +17 -1
  293. package/src/lib/subprocessors.js.map +1 -1
  294. package/src/lib/tools/ai-blog-post-tool.d.ts +60 -0
  295. package/src/lib/tools/ai-blog-post-tool.js +175 -0
  296. package/src/lib/tools/ai-blog-post-tool.js.map +1 -0
  297. package/src/lib/tools/ai-workflow-tool.d.ts +6 -2
  298. package/src/lib/tools/ai-workflow-tool.js +16 -14
  299. package/src/lib/tools/ai-workflow-tool.js.map +1 -1
  300. package/src/lib/usage/assist-usage.d.ts +3 -0
  301. package/src/lib/usage/assist-usage.js +4 -2
  302. package/src/lib/usage/assist-usage.js.map +1 -1
  303. package/src/lib/usage/staff-org-ai-conversations.d.ts +103 -0
  304. package/src/lib/usage/staff-org-ai-conversations.js +152 -0
  305. package/src/lib/usage/staff-org-ai-conversations.js.map +1 -0
@@ -21,6 +21,8 @@ import { type AiSiteJobInputs } from '../model/ai-site-job';
21
21
  import { type AiJobAdmission } from './ai-job-admission';
22
22
  import { readAiDraftNodes } from './ai-job-drafts';
23
23
  import { aiPublishGuidedSite } from './ai-site-publish';
24
+ import { aiPublishSitePosts, aiSiteContentRefusal } from './ai-job-site-content';
25
+ import { dropPluginSiteCache } from '@aglyn/aglyn/plugin-manager/plugin-site-cache';
24
26
  import { type AiJobStepContext, type AiJobStepOutcome, type AiJobStepRunner } from './ai-job-text-step';
25
27
  import { aiJobStepRunnerFor } from './ai-jobs';
26
28
  /**
@@ -76,8 +78,13 @@ import { aiJobStepRunnerFor } from './ai-jobs';
76
78
  * `new:<name>` references resolved to what was built (`aiSiteBuiltRefs`,
77
79
  * `aiSiteUnitJob`). There is one loop, and it is the machine's.
78
80
  */
79
- /** What a scaffold's units are, in the order it builds them; a component is a page job's. */
80
- export type AiSiteUnitKind = 'theme' | 'layout' | 'form' | 'component' | 'page' | 'email';
81
+ /**
82
+ * What a scaffold's units are, in the order it builds them; a component is a
83
+ * page job's. A blog's first posts and a store's first products (AGL-3676)
84
+ * are built after the layout and the form and before the pages, which are
85
+ * told what they are.
86
+ */
87
+ export type AiSiteUnitKind = 'theme' | 'layout' | 'form' | 'component' | 'posts' | 'products' | 'page' | 'email';
81
88
  export interface AiSiteUnit {
82
89
  kind: AiSiteUnitKind;
83
90
  /** The job kind whose registered runner builds it. */
@@ -98,6 +105,8 @@ export interface AiSiteUnit {
98
105
  * are addressed by; `null` for a creation no unit builds.
99
106
  */
100
107
  export declare function aiCreationUnit(creation: AiBuildPlanCreate, slot: string): AiSiteUnit | null;
108
+ /** What the look's unit is called on the ledger. */
109
+ export declare const AI_SITE_LOOK_LABEL = "Your look";
101
110
  export declare const AI_SITE_NO_PLAN_COPY = "This site job has no confirmed plan to build.";
102
111
  /** What a scaffold answers where the step that builds a page is not loaded. */
103
112
  export declare const AI_SITE_NO_PAGE_STEP_COPY = "Building a whole site is not available yet.";
@@ -105,9 +114,10 @@ export declare const AI_SITE_NO_PAGE_STEP_COPY = "Building a whole site is not a
105
114
  export declare const AI_SITE_UNIT_EMPTY_COPY = "Part of this site could not be built. Try the site brief again.";
106
115
  /**
107
116
  * The most passes a scaffold's step may take: the largest plan it admits —
108
- * eight pages of eight sections, its three creations and a welcome email —
109
- * with nothing to spare, so a runner that never finishes is still bounded
110
- * while a real site is not.
117
+ * eight pages of eight sections, its three creations, a blog's first posts
118
+ * (one a pass, the largest part) and a welcome email — with nothing to
119
+ * spare, so a runner that never finishes is still bounded while a real site
120
+ * is not.
111
121
  */
112
122
  export declare const AI_SITE_MAX_PASSES: number;
113
123
  /**
@@ -119,6 +129,7 @@ export declare const AI_SITE_MAX_PASSES: number;
119
129
  */
120
130
  export declare function aiSiteJobUnits(plan: Pick<AiJobPlan, 'create' | 'screens'>, options?: {
121
131
  welcomeEmail?: boolean;
132
+ content?: 'posts' | 'products' | null;
122
133
  }): AiSiteUnit[];
123
134
  /**
124
135
  * The units a scaffold still owes after the outputs recorded so far. Each
@@ -162,6 +173,10 @@ export declare function aiSiteResolvedRef(ref: string | null, built: BuiltRefs):
162
173
  * guided start writes it into the brief itself, but an agency batch's brief
163
174
  * is a member's own sentence and may never mention it, and a theme, a layout
164
175
  * or a form asked to serve nobody in particular serves nobody in particular.
176
+ *
177
+ * The name is said twice (AGL-3596): as a field, and as the instruction to use
178
+ * it as written. A layout told only "a dog groomer in Austin" put a business
179
+ * name of its own in the header and the footer.
165
180
  */
166
181
  export declare function aiSiteBriefLines(brief: string, inputs: AiSiteJobInputs): string[];
167
182
  /**
@@ -246,6 +261,16 @@ export interface AiJobSiteStepDeps {
246
261
  readNodes?: typeof readAiDraftNodes;
247
262
  /** The guided start's publish; specs hand in a fake. */
248
263
  publish?: typeof aiPublishGuidedSite;
264
+ /** The look's pass (AGL-3660); specs hand in a fake. */
265
+ look?: (context: AiJobStepContext, job: AiJob) => Promise<AiJobStepOutcome>;
266
+ /** The first posts' and first products' passes (AGL-3676); specs hand in fakes. */
267
+ posts?: AiJobStepRunner;
268
+ products?: AiJobStepRunner;
269
+ /** Whether a part may be built for this member, before its first pass. */
270
+ contentRefusal?: typeof aiSiteContentRefusal;
271
+ /** The posts' publish once the pages are live, and the cache drop after it. */
272
+ publishPosts?: typeof aiPublishSitePosts;
273
+ dropCache?: typeof dropPluginSiteCache;
249
274
  }
250
275
  /** A page the scaffold reported built that holds none of its plan's sections. */
251
276
  export declare const AI_SITE_PAGE_NOT_WRITTEN_COPY = "A page of this site was not written from its plan. Try the site brief again.";
@@ -19,7 +19,7 @@ import { resolveOrgIdForHost } from "@aglyn/tenant-data-admin/server/organizatio
19
19
  import { isAiPlanNewRef } from "../model/ai-build-plan.js";
20
20
  import { aiBuildDegradation, aiBuildInitialLedger, aiBuildItemDelivered, aiBuildItemOpen, aiBuildNextUnit } from "../model/ai-build-job.js";
21
21
  import { aiBuildBuiltRefs, aiUnitErrorRetryable, aiUnitFailure, aiUnitSpend } from "./ai-build-unit-outcome.js";
22
- import { AI_SITE_EMAIL_TYPE, AI_SITE_MAX_SECTIONS, AI_SITE_PAGES, aiSitePagesRefusal, aiSitePlanRefusal, aiSiteSubmissions, aiSiteWords, parseAiSiteJobInputs } from "../model/ai-site-job.js";
22
+ import { AI_SITE_EMAIL_TYPE, AI_SITE_MAX_SECTIONS, AI_SITE_PAGES, aiSiteNameSentence, aiSitePagesRefusal, aiSitePlanRefusal, aiSiteSubmissions, aiSiteWords, parseAiSiteJobInputs } from "../model/ai-site-job.js";
23
23
  import { AI_SITE_SEO_OUTPUT_ID, aiSiteSeoProposalForInputs } from "../model/ai-site-start-seo.js";
24
24
  import { aiModelForStep } from "../providers/routing.js";
25
25
  import { registerAiJobAdmission } from "./ai-job-admission.js";
@@ -27,7 +27,11 @@ import { aiOriginJobId, aiRecordedJobDraftId } from "./ai-job-draft-ids.js";
27
27
  import { readAiDraftNodes } from "./ai-job-drafts.js";
28
28
  import { AI_LAYOUT_SITE_PAGES_INPUT, aiLayoutSitePagesOfPlan } from "./ai-job-layout-site-pages.js";
29
29
  import { aiPageSectionNodeId } from "./ai-job-page-sections.js";
30
+ import { AI_LAYOUT_FORM_PAGE_INPUT, AI_LAYOUT_LANGUAGE_INPUT, aiLayoutFormPageOfPlan } from "./ai-job-page-language.js";
30
31
  import { aiJobPublishesSite, aiPublishGuidedSite } from "./ai-site-publish.js";
32
+ import { AI_SITE_CONTENT_INPUT, AI_SITE_POST_BUDGET, AI_SITE_POSTS, AI_SITE_POSTS_LABEL, AI_SITE_PRODUCTS_LABEL, aiPublishSitePosts, aiSiteContentBriefLines, aiSiteContentPart, aiSiteContentRefusal, aiSiteProductsBriefLine, createAiSiteProductsRunner, runAiSitePostsUnit } from "./ai-job-site-content.js";
33
+ import { dropPluginSiteCache } from "@aglyn/aglyn/plugin-manager/plugin-site-cache";
34
+ import { AI_SITE_LOOK_BUDGET, aiRunSiteLook } from "./ai-job-site-look.js";
31
35
  import { aiConfirmedPlan, aiUnspentOutcome } from "./ai-job-generation.js";
32
36
  import { AI_JOB_BRIEF_MAX_CHARS } from "./ai-job-text-step.js";
33
37
  import { AI_JOB_PAGE_STEP_MINIMUM_MS } from "./ai-job-page-budget.js";
@@ -56,6 +60,17 @@ import { aiJobStepRunMinimumMs, aiJobStepRunnerFor, registerAiJobStep, registerA
56
60
  email: {
57
61
  jobKind: 'email',
58
62
  resource: 'emailScreen'
63
+ },
64
+ // The scaffold's own runner writes them (`ai-job-site-content.ts`); the
65
+ // posts' job kind is the one whose derived job a post's brief is, and the
66
+ // products' the step that proposes the catalog they are written from.
67
+ posts: {
68
+ jobKind: 'text',
69
+ resource: 'entry'
70
+ },
71
+ products: {
72
+ jobKind: 'products',
73
+ resource: 'product'
59
74
  }
60
75
  };
61
76
  /** The unit kind a creation is built as, where a unit builds it. */ const CREATION_UNIT_KINDS = {
@@ -92,15 +107,17 @@ import { aiJobStepRunMinimumMs, aiJobStepRunnerFor, registerAiJobStep, registerA
92
107
  create: 'form'
93
108
  }
94
109
  ];
110
+ /** What the look's unit is called on the ledger. */ export const AI_SITE_LOOK_LABEL = 'Your look';
95
111
  export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to build.';
96
112
  /** What a scaffold answers where the step that builds a page is not loaded. */ export const AI_SITE_NO_PAGE_STEP_COPY = 'Building a whole site is not available yet.';
97
113
  /** A unit that finished without reporting what it built; the scaffold cannot go on. */ export const AI_SITE_UNIT_EMPTY_COPY = 'Part of this site could not be built. Try the site brief again.';
98
114
  /**
99
115
  * The most passes a scaffold's step may take: the largest plan it admits —
100
- * eight pages of eight sections, its three creations and a welcome email —
101
- * with nothing to spare, so a runner that never finishes is still bounded
102
- * while a real site is not.
103
- */ export const AI_SITE_MAX_PASSES = AI_SITE_PAGES.max * (AI_SITE_MAX_SECTIONS + 1) + CREATION_UNITS.length + 1;
116
+ * eight pages of eight sections, its three creations, a blog's first posts
117
+ * (one a pass, the largest part) and a welcome email — with nothing to
118
+ * spare, so a runner that never finishes is still bounded while a real site
119
+ * is not.
120
+ */ export const AI_SITE_MAX_PASSES = AI_SITE_PAGES.max * (AI_SITE_MAX_SECTIONS + 1) + CREATION_UNITS.length + AI_SITE_POSTS + 1;
104
121
  /**
105
122
  * The units a plan implies, in build order: the palette first, because a
106
123
  * member reads it while the pages are still building; then the layout every
@@ -108,8 +125,19 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
108
125
  * id and so needs them to exist; then the pages; then the welcome email,
109
126
  * which is about the site rather than part of it.
110
127
  */ export function aiSiteJobUnits(plan, options = {}) {
111
- const units = [];
128
+ // The site's look is designed first, on every scaffold (AGL-3660): the
129
+ // header, the footer and every page render in it from their first draft.
130
+ // A theme change the plan names is that same unit, never a second one.
131
+ const units = [
132
+ _extends({
133
+ kind: 'theme'
134
+ }, UNIT_KINDS.theme, {
135
+ slot: 't',
136
+ label: AI_SITE_LOOK_LABEL
137
+ })
138
+ ];
112
139
  for (const { unit, create } of CREATION_UNITS){
140
+ if (unit === 'theme') continue;
113
141
  const creation = plan.create.find((entry)=>entry.kind === create);
114
142
  if (!creation) continue;
115
143
  units.push(_extends({
@@ -120,6 +148,22 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
120
148
  label: creation.name
121
149
  }));
122
150
  }
151
+ // A paid blog's first posts and a paid store's first products (AGL-3676).
152
+ if (options.content === 'posts') {
153
+ units.push(_extends({
154
+ kind: 'posts'
155
+ }, UNIT_KINDS.posts, {
156
+ slot: 'posts',
157
+ label: AI_SITE_POSTS_LABEL
158
+ }));
159
+ } else if (options.content === 'products') {
160
+ units.push(_extends({
161
+ kind: 'products'
162
+ }, UNIT_KINDS.products, {
163
+ slot: 'products',
164
+ label: AI_SITE_PRODUCTS_LABEL
165
+ }));
166
+ }
123
167
  plan.screens.forEach((screen, index)=>{
124
168
  units.push(_extends({
125
169
  kind: 'page'
@@ -234,6 +278,10 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
234
278
  * guided start writes it into the brief itself, but an agency batch's brief
235
279
  * is a member's own sentence and may never mention it, and a theme, a layout
236
280
  * or a form asked to serve nobody in particular serves nobody in particular.
281
+ *
282
+ * The name is said twice (AGL-3596): as a field, and as the instruction to use
283
+ * it as written. A layout told only "a dog groomer in Austin" put a business
284
+ * name of its own in the header and the footer.
237
285
  */ export function aiSiteBriefLines(brief, inputs) {
238
286
  const lines = [
239
287
  brief.trim()
@@ -246,6 +294,7 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
246
294
  inputs.brand ? `brand: ${inputs.brand}` : ''
247
295
  ].filter(Boolean);
248
296
  lines.push(`Site — ${site.join('; ')}.`);
297
+ if (inputs.businessName) lines.push(aiSiteNameSentence(inputs.businessName));
249
298
  return lines;
250
299
  }
251
300
  /**
@@ -330,7 +379,7 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
330
379
  brief.push(`Build the ${unit.creation.kind} “${unit.creation.name}”: ${unit.creation.why}`);
331
380
  }
332
381
  if (unit.screen) {
333
- var _aiSiteResolvedRef, _aiSiteResolvedRef1;
382
+ var _aiSiteResolvedRef, _aiSiteResolvedRef1, _job_outputs;
334
383
  const screen = unit.screen;
335
384
  // Everything the scaffold has already built is a record the site has, so
336
385
  // the page is planned against it the way a page job is planned against
@@ -364,7 +413,10 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
364
413
  })
365
414
  ];
366
415
  brief.push(`Build the page “${screen.title}” of this site, at ${screen.slug}.`);
416
+ // What the blog's posts or the store's products are, once built (AGL-3676).
417
+ if (job.kind === 'site') brief.push(...aiSiteContentBriefLines((_job_outputs = job.outputs) != null ? _job_outputs : []));
367
418
  }
419
+ if (unit.kind === 'products') brief.push(aiSiteProductsBriefLine());
368
420
  if (unit.kind === 'email') {
369
421
  brief.push(...aiSiteEmailBriefLines(job.inputs));
370
422
  }
@@ -375,10 +427,46 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
375
427
  });
376
428
  // The layout is built before the pages, so it is told them (AGL-3596): their
377
429
  // ids are minted on the plan, and the platform writes the header's links.
378
- if (job.kind === 'site' && unit.kind === 'layout') {
379
- const pages = aiLayoutSitePagesOfPlan(plan.screens);
430
+ // A page is told them too, so its buttons may go to a page built after it.
431
+ if (job.kind === 'site' && (unit.kind === 'layout' || unit.kind === 'page')) {
432
+ // A guided start links every page the person asked for (AGL-3660).
433
+ const pages = aiLayoutSitePagesOfPlan(plan.screens, {
434
+ guided: true
435
+ });
380
436
  if (pages.length) unitInputs[AI_LAYOUT_SITE_PAGES_INPUT] = pages;
381
437
  }
438
+ // A site's pages and its layout are designed in the layout language and
439
+ // compiled (AGL-3660), and a page is told which page places the site's form.
440
+ if (unit.kind === 'layout' || unit.kind === 'page') {
441
+ var _job_outputs1;
442
+ var _look_proposal;
443
+ // The look designed first (AGL-3660): its header arrangement and band rhythm.
444
+ const look = ((_job_outputs1 = job.outputs) != null ? _job_outputs1 : []).find((output)=>output.resource === 'theme' && output.id === 'look');
445
+ const style = look == null ? void 0 : (_look_proposal = look.proposal) == null ? void 0 : _look_proposal['style'];
446
+ if (style) unitInputs['siteStyle'] = {
447
+ headerAlign: style['headerAlign'],
448
+ rhythm: style['rhythm']
449
+ };
450
+ unitInputs[AI_LAYOUT_LANGUAGE_INPUT] = true;
451
+ const formPage = aiLayoutFormPageOfPlan(plan);
452
+ if (formPage) unitInputs[AI_LAYOUT_FORM_PAGE_INPUT] = formPage;
453
+ }
454
+ if (unit.kind === 'posts') {
455
+ var _job_outputs2, _inputs_businessName;
456
+ // The posts already written (one a pass), the addresses the site's own
457
+ // pages answer at, and the byline the person gave (AGL-3676).
458
+ const posts = {
459
+ written: ((_job_outputs2 = job.outputs) != null ? _job_outputs2 : []).filter((output)=>output.resource === 'entry').map((output)=>({
460
+ id: output.id,
461
+ title: output.label
462
+ })),
463
+ total: AI_SITE_POSTS,
464
+ avoidSlugs: plan.screens.map((screen)=>screen.slug.replace(/^\/+/, '').split('/')[0]).filter(Boolean),
465
+ byline: typeof inputs === 'string' ? '' : (_inputs_businessName = inputs.businessName) != null ? _inputs_businessName : ''
466
+ };
467
+ unitInputs[AI_SITE_CONTENT_INPUT] = posts;
468
+ }
469
+ if (unit.kind === 'products') unitInputs['target'] = 'catalog';
382
470
  return _extends({}, job, {
383
471
  $id: aiSiteUnitJobId(job, unit),
384
472
  kind: unit.jobKind,
@@ -529,7 +617,7 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
529
617
  const creations = units.filter((unit)=>unit.kind === 'layout' || unit.kind === 'form').map((unit)=>unit.slot);
530
618
  return units.map((unit)=>_extends({
531
619
  slot: unit.slot,
532
- op: unit.kind === 'theme' ? 'theme' : unit.jobKind,
620
+ op: unit.kind === 'theme' || unit.kind === 'posts' || unit.kind === 'products' ? unit.kind : unit.jobKind,
533
621
  label: unit.label
534
622
  }, unit.creation ? {
535
623
  creation: unit.creation
@@ -566,17 +654,22 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
566
654
  }
567
655
  /** The units a scaffold owes on this job: what its plan implies that this deployment can build. */ function aiSiteOwedUnits(job, plan, inputs, freeTaste, runnerFor) {
568
656
  return aiSiteJobUnits(plan, {
569
- welcomeEmail: aiSiteWelcomeEmail(inputs, freeTaste)
570
- }).filter((unit)=>runnerFor(unit.jobKind));
657
+ welcomeEmail: aiSiteWelcomeEmail(inputs, freeTaste),
658
+ content: aiSiteContentPart(job.inputs, freeTaste)
659
+ }).filter((unit)=>unit.kind === 'theme' || unit.kind === 'posts' || runnerFor(unit.jobKind));
571
660
  }
572
661
  export function createAiJobSiteStep(deps = {}) {
573
- var _deps_runnerFor, _deps_readNodes, _deps_publish;
662
+ var _deps_runnerFor, _deps_readNodes, _deps_publish, _deps_look, _deps_contentRefusal, _deps_publishPosts, _deps_dropCache;
574
663
  const runnerFor = (_deps_runnerFor = deps.runnerFor) != null ? _deps_runnerFor : aiJobStepRunnerFor;
575
664
  const readNodes = (_deps_readNodes = deps.readNodes) != null ? _deps_readNodes : readAiDraftNodes;
576
665
  const publish = (_deps_publish = deps.publish) != null ? _deps_publish : aiPublishGuidedSite;
666
+ const look = (_deps_look = deps.look) != null ? _deps_look : aiRunSiteLook;
667
+ const contentRefusal = (_deps_contentRefusal = deps.contentRefusal) != null ? _deps_contentRefusal : aiSiteContentRefusal;
668
+ const publishPosts = (_deps_publishPosts = deps.publishPosts) != null ? _deps_publishPosts : aiPublishSitePosts;
669
+ const dropCache = (_deps_dropCache = deps.dropCache) != null ? _deps_dropCache : dropPluginSiteCache;
577
670
  return async (context)=>{
578
- var _ref, _job_outputs, _job_outputs1;
579
- var _job_items;
671
+ var _ref, _job_outputs, _deps_posts, _deps_products, _job_outputs1;
672
+ var _job_items, _rows_get;
580
673
  const { job } = context;
581
674
  // The scaffold asks no model of its own. What it names where it spends
582
675
  // nothing is the model its plan ran on, which is the one exchange this
@@ -630,10 +723,36 @@ export function createAiJobSiteStep(deps = {}) {
630
723
  });
631
724
  return null;
632
725
  });
726
+ if (sitePublish == null ? void 0 : sitePublish.published.length) await finishPosts();
633
727
  return sitePublish ? _extends({}, outcome, {
634
728
  sitePublish
635
729
  }) : outcome;
636
730
  };
731
+ /** The blog's posts go live with its pages (AGL-3676), as a person's Publish would put them. */ const finishPosts = async ()=>{
732
+ var _job_outputs;
733
+ const row = rows.get('posts');
734
+ if (!row || !aiBuildItemDelivered(row) || !job.hostId) return;
735
+ const ids = new Set(row.outputs);
736
+ const posts = ((_job_outputs = job.outputs) != null ? _job_outputs : []).filter((output)=>output.resource === 'entry' && ids.has(output.id));
737
+ const published = await publishPosts(context.firestore, {
738
+ job,
739
+ outputs: posts,
740
+ now: context.now
741
+ });
742
+ if (!(published == null ? void 0 : published.paths.length)) return;
743
+ await dropCache({
744
+ hostIds: [
745
+ job.hostId
746
+ ],
747
+ reason: 'guided AI site start published its posts',
748
+ paths: {
749
+ [job.hostId]: published.paths
750
+ }
751
+ }).catch((error)=>console.warn('ai site posts: cache not dropped', {
752
+ jobId: job.$id,
753
+ error
754
+ }));
755
+ };
637
756
  /** The pages built so far, as the job reported them. */ const builtPages = (extra = [])=>{
638
757
  var _job_outputs;
639
758
  const ids = new Set(units.filter((unit)=>{
@@ -653,7 +772,11 @@ export function createAiJobSiteStep(deps = {}) {
653
772
  };
654
773
  if (!next) return finish(_extends({}, aiUnspentOutcome(model), init), builtPages());
655
774
  const unit = units.find((one)=>one.slot === next.slot);
656
- const runner = runnerFor(unit.jobKind);
775
+ // The look is the scaffold's own pass (AGL-3660), not the theme job's proposal.
776
+ const catalog = unit.kind === 'products' ? runnerFor('products') : undefined;
777
+ const runner = unit.kind === 'theme' ? (lookContext)=>look(lookContext, lookContext.job) : unit.kind === 'posts' ? (_deps_posts = deps.posts) != null ? _deps_posts : runAiSitePostsUnit : unit.kind === 'products' ? (_deps_products = deps.products) != null ? _deps_products : catalog ? createAiSiteProductsRunner({
778
+ catalog
779
+ }) : undefined : runnerFor(unit.jobKind);
657
780
  if (!runner) return _extends({}, aiUnspentOutcome(model), init);
658
781
  const othersOpen = ledgerUnits.some((one)=>{
659
782
  var _rows_get;
@@ -675,6 +798,33 @@ export function createAiJobSiteStep(deps = {}) {
675
798
  }, item.status === 'running' || othersOpen ? {
676
799
  continue: true
677
800
  } : {});
801
+ /**
802
+ * A unit that failed or was skipped with nothing else open ends the job
803
+ * on this pass (AGL-3676): a guided start whose last unit — its welcome
804
+ * email — failed was never published, because the pass that publishes is
805
+ * the one after. So the end is the publish, whatever the last unit came to.
806
+ */ const closing = async (outcome)=>outcome.continue ? outcome : finish(outcome, builtPages());
807
+ // A blog's posts and a store's products ask, before their first pass,
808
+ // whether this member may have them here (AGL-3676): a refusal spends
809
+ // nothing, and the row says why, as a build's item does.
810
+ if ((unit.kind === 'posts' || unit.kind === 'products') && ((_rows_get = rows.get(unit.slot)) == null ? void 0 : _rows_get.status) !== 'running') {
811
+ const refusal = await contentRefusal(unit.kind, _extends({}, context, {
812
+ job
813
+ })).catch((error)=>{
814
+ console.error('ai site part admission failed', {
815
+ orgId: job.orgId,
816
+ jobId: job.$id,
817
+ slot: unit.slot,
818
+ error
819
+ });
820
+ return AI_SITE_UNIT_EMPTY_COPY;
821
+ });
822
+ if (refusal) return closing(settle({
823
+ slot: unit.slot,
824
+ status: 'skipped',
825
+ note: `Not built: ${refusal}`
826
+ }));
827
+ }
678
828
  // A page whose layout or form failed is built without it, and says so.
679
829
  const degradation = aiBuildDegradation(next, {
680
830
  units: ledgerUnits,
@@ -703,7 +853,7 @@ export function createAiJobSiteStep(deps = {}) {
703
853
  slot: unit.slot,
704
854
  error
705
855
  });
706
- return settle({
856
+ return closing(settle({
707
857
  slot: unit.slot,
708
858
  status: 'failed',
709
859
  failure: {
@@ -711,16 +861,16 @@ export function createAiJobSiteStep(deps = {}) {
711
861
  reason: 'provider',
712
862
  message: AI_SITE_UNIT_EMPTY_COPY
713
863
  }
714
- });
864
+ }));
715
865
  }
716
866
  const spent = aiUnitSpend(outcome);
717
867
  const stopped = aiUnitFailure(unit.slot, outcome);
718
868
  if (stopped) {
719
- return settle(stopped.status === 'failed' && stopped.failure && !outcome.failure && !outcome.refused && !outcome.review ? _extends({}, stopped, {
869
+ return closing(settle(stopped.status === 'failed' && stopped.failure && !outcome.failure && !outcome.refused && !outcome.review ? _extends({}, stopped, {
720
870
  failure: _extends({}, stopped.failure, {
721
871
  message: AI_SITE_UNIT_EMPTY_COPY
722
872
  })
723
- }) : stopped, spent);
873
+ }) : stopped, spent));
724
874
  }
725
875
  // A page counts as built only when its plan's sections are in it
726
876
  // (AGL-3596): otherwise the page fails, on our side, and is not reported.
@@ -733,7 +883,7 @@ export function createAiJobSiteStep(deps = {}) {
733
883
  draftId: page.id
734
884
  }, readNodes);
735
885
  if (!written) {
736
- return settle({
886
+ return closing(settle({
737
887
  slot: unit.slot,
738
888
  status: 'failed',
739
889
  failure: {
@@ -743,7 +893,7 @@ export function createAiJobSiteStep(deps = {}) {
743
893
  }
744
894
  }, _extends({}, spent, {
745
895
  outputs: spent.outputs.filter((output)=>output.resource !== 'screen')
746
- }));
896
+ })));
747
897
  }
748
898
  }
749
899
  const done = settle(_extends({
@@ -770,19 +920,25 @@ export const runAiJobSiteStep = createAiJobSiteStep();
770
920
  * for a page. A scaffold with nothing left to build, or with no plan it can
771
921
  * build, spends nothing, and a page pass's time covers it.
772
922
  */ export function aiSiteJobRunMinimumMs(job) {
773
- var _job_outputs;
774
- var _job_items;
923
+ var _job_items, _job_outputs;
924
+ var _job_items1;
775
925
  const plan = aiConfirmedPlan(job);
776
926
  const inputs = parseAiSiteJobInputs(job.inputs);
777
927
  if (!plan || typeof inputs === 'string') return AI_JOB_PAGE_STEP_MINIMUM_MS;
928
+ // The org is not read here, so a blog's or a store's part is counted as a
929
+ // paid plan's; a ledger, once the job has one, says which units it owes.
930
+ const owed = new Set(((_job_items = job.items) != null ? _job_items : []).map((row)=>row.slot));
778
931
  const units = aiSiteJobUnits(plan, {
779
- welcomeEmail: inputs.welcomeEmail
780
- }).filter((unit)=>aiJobStepRunnerFor(unit.jobKind));
932
+ welcomeEmail: inputs.welcomeEmail,
933
+ content: aiSiteContentPart(job.inputs, false)
934
+ }).filter((unit)=>(unit.kind === 'theme' || unit.kind === 'posts' || aiJobStepRunnerFor(unit.jobKind)) && (!owed.size || owed.has(unit.slot)));
781
935
  const outputs = (_job_outputs = job.outputs) != null ? _job_outputs : [];
782
- const ledger = ((_job_items = job.items) == null ? void 0 : _job_items.length) ? job.items : aiSiteInitialLedger(units, outputs);
936
+ const ledger = ((_job_items1 = job.items) == null ? void 0 : _job_items1.length) ? job.items : aiSiteInitialLedger(units, outputs);
783
937
  const next = aiBuildNextUnit(aiSiteLedgerUnits(units), ledger);
784
938
  const unit = next ? units.find((one)=>one.slot === next.slot) : undefined;
785
939
  if (!unit) return AI_JOB_PAGE_STEP_MINIMUM_MS;
940
+ if (unit.kind === 'theme') return AI_SITE_LOOK_BUDGET.minimumMs;
941
+ if (unit.kind === 'posts') return AI_SITE_POST_BUDGET.minimumMs;
786
942
  return aiJobStepRunMinimumMs(aiSiteUnitJob(job, unit, aiBuildBuiltRefs(aiSiteLedgerUnits(units), ledger, outputs)));
787
943
  }
788
944
  /**