@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
@@ -29,6 +29,9 @@ import { AI_LAYOUT_SITE_PAGES_INPUT, aiLayoutSitePagesOfPlan } from "./ai-job-la
29
29
  import { aiPageSectionNodeId } from "./ai-job-page-sections.js";
30
30
  import { AI_LAYOUT_FORM_PAGE_INPUT, AI_LAYOUT_LANGUAGE_INPUT, aiLayoutFormPageOfPlan } from "./ai-job-page-language.js";
31
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";
32
35
  import { aiConfirmedPlan, aiUnspentOutcome } from "./ai-job-generation.js";
33
36
  import { AI_JOB_BRIEF_MAX_CHARS } from "./ai-job-text-step.js";
34
37
  import { AI_JOB_PAGE_STEP_MINIMUM_MS } from "./ai-job-page-budget.js";
@@ -57,6 +60,17 @@ import { aiJobStepRunMinimumMs, aiJobStepRunnerFor, registerAiJobStep, registerA
57
60
  email: {
58
61
  jobKind: 'email',
59
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'
60
74
  }
61
75
  };
62
76
  /** The unit kind a creation is built as, where a unit builds it. */ const CREATION_UNIT_KINDS = {
@@ -93,15 +107,17 @@ import { aiJobStepRunMinimumMs, aiJobStepRunnerFor, registerAiJobStep, registerA
93
107
  create: 'form'
94
108
  }
95
109
  ];
110
+ /** What the look's unit is called on the ledger. */ export const AI_SITE_LOOK_LABEL = 'Your look';
96
111
  export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to build.';
97
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.';
98
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.';
99
114
  /**
100
115
  * The most passes a scaffold's step may take: the largest plan it admits —
101
- * eight pages of eight sections, its three creations and a welcome email —
102
- * with nothing to spare, so a runner that never finishes is still bounded
103
- * while a real site is not.
104
- */ 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;
105
121
  /**
106
122
  * The units a plan implies, in build order: the palette first, because a
107
123
  * member reads it while the pages are still building; then the layout every
@@ -109,8 +125,19 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
109
125
  * id and so needs them to exist; then the pages; then the welcome email,
110
126
  * which is about the site rather than part of it.
111
127
  */ export function aiSiteJobUnits(plan, options = {}) {
112
- 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
+ ];
113
139
  for (const { unit, create } of CREATION_UNITS){
140
+ if (unit === 'theme') continue;
114
141
  const creation = plan.create.find((entry)=>entry.kind === create);
115
142
  if (!creation) continue;
116
143
  units.push(_extends({
@@ -121,6 +148,22 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
121
148
  label: creation.name
122
149
  }));
123
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
+ }
124
167
  plan.screens.forEach((screen, index)=>{
125
168
  units.push(_extends({
126
169
  kind: 'page'
@@ -336,7 +379,7 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
336
379
  brief.push(`Build the ${unit.creation.kind} “${unit.creation.name}”: ${unit.creation.why}`);
337
380
  }
338
381
  if (unit.screen) {
339
- var _aiSiteResolvedRef, _aiSiteResolvedRef1;
382
+ var _aiSiteResolvedRef, _aiSiteResolvedRef1, _job_outputs;
340
383
  const screen = unit.screen;
341
384
  // Everything the scaffold has already built is a record the site has, so
342
385
  // the page is planned against it the way a page job is planned against
@@ -370,7 +413,10 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
370
413
  })
371
414
  ];
372
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 : []));
373
418
  }
419
+ if (unit.kind === 'products') brief.push(aiSiteProductsBriefLine());
374
420
  if (unit.kind === 'email') {
375
421
  brief.push(...aiSiteEmailBriefLines(job.inputs));
376
422
  }
@@ -392,10 +438,35 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
392
438
  // A site's pages and its layout are designed in the layout language and
393
439
  // compiled (AGL-3660), and a page is told which page places the site's form.
394
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
+ };
395
450
  unitInputs[AI_LAYOUT_LANGUAGE_INPUT] = true;
396
451
  const formPage = aiLayoutFormPageOfPlan(plan);
397
452
  if (formPage) unitInputs[AI_LAYOUT_FORM_PAGE_INPUT] = formPage;
398
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';
399
470
  return _extends({}, job, {
400
471
  $id: aiSiteUnitJobId(job, unit),
401
472
  kind: unit.jobKind,
@@ -546,7 +617,7 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
546
617
  const creations = units.filter((unit)=>unit.kind === 'layout' || unit.kind === 'form').map((unit)=>unit.slot);
547
618
  return units.map((unit)=>_extends({
548
619
  slot: unit.slot,
549
- op: unit.kind === 'theme' ? 'theme' : unit.jobKind,
620
+ op: unit.kind === 'theme' || unit.kind === 'posts' || unit.kind === 'products' ? unit.kind : unit.jobKind,
550
621
  label: unit.label
551
622
  }, unit.creation ? {
552
623
  creation: unit.creation
@@ -583,17 +654,22 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
583
654
  }
584
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) {
585
656
  return aiSiteJobUnits(plan, {
586
- welcomeEmail: aiSiteWelcomeEmail(inputs, freeTaste)
587
- }).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));
588
660
  }
589
661
  export function createAiJobSiteStep(deps = {}) {
590
- var _deps_runnerFor, _deps_readNodes, _deps_publish;
662
+ var _deps_runnerFor, _deps_readNodes, _deps_publish, _deps_look, _deps_contentRefusal, _deps_publishPosts, _deps_dropCache;
591
663
  const runnerFor = (_deps_runnerFor = deps.runnerFor) != null ? _deps_runnerFor : aiJobStepRunnerFor;
592
664
  const readNodes = (_deps_readNodes = deps.readNodes) != null ? _deps_readNodes : readAiDraftNodes;
593
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;
594
670
  return async (context)=>{
595
- var _ref, _job_outputs, _job_outputs1;
596
- var _job_items;
671
+ var _ref, _job_outputs, _deps_posts, _deps_products, _job_outputs1;
672
+ var _job_items, _rows_get;
597
673
  const { job } = context;
598
674
  // The scaffold asks no model of its own. What it names where it spends
599
675
  // nothing is the model its plan ran on, which is the one exchange this
@@ -647,10 +723,36 @@ export function createAiJobSiteStep(deps = {}) {
647
723
  });
648
724
  return null;
649
725
  });
726
+ if (sitePublish == null ? void 0 : sitePublish.published.length) await finishPosts();
650
727
  return sitePublish ? _extends({}, outcome, {
651
728
  sitePublish
652
729
  }) : outcome;
653
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
+ };
654
756
  /** The pages built so far, as the job reported them. */ const builtPages = (extra = [])=>{
655
757
  var _job_outputs;
656
758
  const ids = new Set(units.filter((unit)=>{
@@ -670,7 +772,11 @@ export function createAiJobSiteStep(deps = {}) {
670
772
  };
671
773
  if (!next) return finish(_extends({}, aiUnspentOutcome(model), init), builtPages());
672
774
  const unit = units.find((one)=>one.slot === next.slot);
673
- 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);
674
780
  if (!runner) return _extends({}, aiUnspentOutcome(model), init);
675
781
  const othersOpen = ledgerUnits.some((one)=>{
676
782
  var _rows_get;
@@ -692,6 +798,33 @@ export function createAiJobSiteStep(deps = {}) {
692
798
  }, item.status === 'running' || othersOpen ? {
693
799
  continue: true
694
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
+ }
695
828
  // A page whose layout or form failed is built without it, and says so.
696
829
  const degradation = aiBuildDegradation(next, {
697
830
  units: ledgerUnits,
@@ -720,7 +853,7 @@ export function createAiJobSiteStep(deps = {}) {
720
853
  slot: unit.slot,
721
854
  error
722
855
  });
723
- return settle({
856
+ return closing(settle({
724
857
  slot: unit.slot,
725
858
  status: 'failed',
726
859
  failure: {
@@ -728,16 +861,16 @@ export function createAiJobSiteStep(deps = {}) {
728
861
  reason: 'provider',
729
862
  message: AI_SITE_UNIT_EMPTY_COPY
730
863
  }
731
- });
864
+ }));
732
865
  }
733
866
  const spent = aiUnitSpend(outcome);
734
867
  const stopped = aiUnitFailure(unit.slot, outcome);
735
868
  if (stopped) {
736
- 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, {
737
870
  failure: _extends({}, stopped.failure, {
738
871
  message: AI_SITE_UNIT_EMPTY_COPY
739
872
  })
740
- }) : stopped, spent);
873
+ }) : stopped, spent));
741
874
  }
742
875
  // A page counts as built only when its plan's sections are in it
743
876
  // (AGL-3596): otherwise the page fails, on our side, and is not reported.
@@ -750,7 +883,7 @@ export function createAiJobSiteStep(deps = {}) {
750
883
  draftId: page.id
751
884
  }, readNodes);
752
885
  if (!written) {
753
- return settle({
886
+ return closing(settle({
754
887
  slot: unit.slot,
755
888
  status: 'failed',
756
889
  failure: {
@@ -760,7 +893,7 @@ export function createAiJobSiteStep(deps = {}) {
760
893
  }
761
894
  }, _extends({}, spent, {
762
895
  outputs: spent.outputs.filter((output)=>output.resource !== 'screen')
763
- }));
896
+ })));
764
897
  }
765
898
  }
766
899
  const done = settle(_extends({
@@ -787,19 +920,25 @@ export const runAiJobSiteStep = createAiJobSiteStep();
787
920
  * for a page. A scaffold with nothing left to build, or with no plan it can
788
921
  * build, spends nothing, and a page pass's time covers it.
789
922
  */ export function aiSiteJobRunMinimumMs(job) {
790
- var _job_outputs;
791
- var _job_items;
923
+ var _job_items, _job_outputs;
924
+ var _job_items1;
792
925
  const plan = aiConfirmedPlan(job);
793
926
  const inputs = parseAiSiteJobInputs(job.inputs);
794
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));
795
931
  const units = aiSiteJobUnits(plan, {
796
- welcomeEmail: inputs.welcomeEmail
797
- }).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)));
798
935
  const outputs = (_job_outputs = job.outputs) != null ? _job_outputs : [];
799
- 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);
800
937
  const next = aiBuildNextUnit(aiSiteLedgerUnits(units), ledger);
801
938
  const unit = next ? units.find((one)=>one.slot === next.slot) : undefined;
802
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;
803
942
  return aiJobStepRunMinimumMs(aiSiteUnitJob(job, unit, aiBuildBuiltRefs(aiSiteLedgerUnits(units), ledger, outputs)));
804
943
  }
805
944
  /**