@aglyn/plugins-ai 1.0.0-beta.237 → 1.0.0-beta.239

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 (172) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-credits-prompt.component.d.ts +11 -4
  3. package/src/lib/components/ai-credits-prompt.component.js +84 -34
  4. package/src/lib/components/ai-credits-prompt.component.js.map +1 -1
  5. package/src/lib/components/ai-job-cancel.component.d.ts +38 -0
  6. package/src/lib/components/ai-job-cancel.component.js +156 -0
  7. package/src/lib/components/ai-job-cancel.component.js.map +1 -0
  8. package/src/lib/components/ai-job-links.js +8 -0
  9. package/src/lib/components/ai-job-links.js.map +1 -1
  10. package/src/lib/components/ai-job-plan.component.js +12 -1
  11. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  12. package/src/lib/components/ai-jobs-list-page.component.js +141 -72
  13. package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
  14. package/src/lib/components/ai-jobs-store.d.ts +12 -0
  15. package/src/lib/components/ai-jobs-store.js +15 -0
  16. package/src/lib/components/ai-jobs-store.js.map +1 -1
  17. package/src/lib/components/ai-site-build-page.component.js +22 -2
  18. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  19. package/src/lib/components/ai-usage-meter-refresh.d.ts +35 -0
  20. package/src/lib/components/ai-usage-meter-refresh.js +63 -0
  21. package/src/lib/components/ai-usage-meter-refresh.js.map +1 -0
  22. package/src/lib/components/ai-usage-strip.component.d.ts +19 -2
  23. package/src/lib/components/ai-usage-strip.component.js +40 -9
  24. package/src/lib/components/ai-usage-strip.component.js.map +1 -1
  25. package/src/lib/components/assist-jobs-drawer.component.js +12 -16
  26. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  27. package/src/lib/components/assist-panel.component.js +91 -51
  28. package/src/lib/components/assist-panel.component.js.map +1 -1
  29. package/src/lib/components/use-ai-usage-meter.d.ts +2 -0
  30. package/src/lib/components/use-ai-usage-meter.js +8 -3
  31. package/src/lib/components/use-ai-usage-meter.js.map +1 -1
  32. package/src/lib/jobs/ai-build-unit-outcome.d.ts +3 -1
  33. package/src/lib/jobs/ai-build-unit-outcome.js +19 -3
  34. package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
  35. package/src/lib/jobs/ai-job-build-step.js +33 -3
  36. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  37. package/src/lib/jobs/ai-job-cancel.d.ts +71 -0
  38. package/src/lib/jobs/ai-job-cancel.js +102 -0
  39. package/src/lib/jobs/ai-job-cancel.js.map +1 -0
  40. package/src/lib/jobs/ai-job-component-step.js +4 -1
  41. package/src/lib/jobs/ai-job-component-step.js.map +1 -1
  42. package/src/lib/jobs/ai-job-draft-ids.js +7 -2
  43. package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
  44. package/src/lib/jobs/ai-job-drafts.js +2 -0
  45. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  46. package/src/lib/jobs/ai-job-form-dataset.d.ts +100 -0
  47. package/src/lib/jobs/ai-job-form-dataset.js +161 -0
  48. package/src/lib/jobs/ai-job-form-dataset.js.map +1 -0
  49. package/src/lib/jobs/ai-job-form-step.d.ts +15 -3
  50. package/src/lib/jobs/ai-job-form-step.js +41 -10
  51. package/src/lib/jobs/ai-job-form-step.js.map +1 -1
  52. package/src/lib/jobs/ai-job-layout-language.d.ts +2 -0
  53. package/src/lib/jobs/ai-job-layout-language.js +13 -1
  54. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  55. package/src/lib/jobs/ai-job-layout-step.js +6 -1
  56. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  57. package/src/lib/jobs/ai-job-page-language.d.ts +9 -0
  58. package/src/lib/jobs/ai-job-page-language.js +45 -2
  59. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  60. package/src/lib/jobs/ai-job-page-step.js +12 -1
  61. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  62. package/src/lib/jobs/ai-job-plan-step.d.ts +38 -1
  63. package/src/lib/jobs/ai-job-plan-step.js +90 -13
  64. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  65. package/src/lib/jobs/ai-job-site-content.d.ts +15 -0
  66. package/src/lib/jobs/ai-job-site-content.js +66 -7
  67. package/src/lib/jobs/ai-job-site-content.js.map +1 -1
  68. package/src/lib/jobs/ai-job-site-datasets.d.ts +170 -0
  69. package/src/lib/jobs/ai-job-site-datasets.js +499 -0
  70. package/src/lib/jobs/ai-job-site-datasets.js.map +1 -0
  71. package/src/lib/jobs/ai-job-site-step.d.ts +15 -8
  72. package/src/lib/jobs/ai-job-site-step.js +152 -30
  73. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  74. package/src/lib/jobs/ai-job-template-step.js +4 -1
  75. package/src/lib/jobs/ai-job-template-step.js.map +1 -1
  76. package/src/lib/jobs/ai-jobs.d.ts +37 -12
  77. package/src/lib/jobs/ai-jobs.js +184 -28
  78. package/src/lib/jobs/ai-jobs.js.map +1 -1
  79. package/src/lib/jobs/ai-layout-stock-photos.d.ts +143 -70
  80. package/src/lib/jobs/ai-layout-stock-photos.js +649 -176
  81. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  82. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.d.ts +30 -0
  83. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js +220 -0
  84. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js.map +1 -0
  85. package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.d.ts +30 -0
  86. package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js +637 -0
  87. package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js.map +1 -0
  88. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.d.ts +27 -0
  89. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js +151 -0
  90. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js.map +1 -0
  91. package/src/lib/layout-language/ai-layout-compiler.d.ts +16 -0
  92. package/src/lib/layout-language/ai-layout-compiler.js +397 -9
  93. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  94. package/src/lib/layout-language/ai-layout-design.d.ts +6 -3
  95. package/src/lib/layout-language/ai-layout-design.js +5 -3
  96. package/src/lib/layout-language/ai-layout-design.js.map +1 -1
  97. package/src/lib/layout-language/ai-layout-frame.d.ts +12 -0
  98. package/src/lib/layout-language/ai-layout-frame.js +58 -3
  99. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  100. package/src/lib/layout-language/ai-layout-gaps.js +17 -1
  101. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
  102. package/src/lib/layout-language/ai-layout-listings.d.ts +44 -3
  103. package/src/lib/layout-language/ai-layout-listings.js +118 -50
  104. package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
  105. package/src/lib/layout-language/ai-layout-pictures.d.ts +8 -0
  106. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  107. package/src/lib/layout-language/ai-layout-storefront.d.ts +82 -0
  108. package/src/lib/layout-language/ai-layout-storefront.js +223 -0
  109. package/src/lib/layout-language/ai-layout-storefront.js.map +1 -0
  110. package/src/lib/model/ai-build-job.d.ts +11 -1
  111. package/src/lib/model/ai-build-job.js +35 -6
  112. package/src/lib/model/ai-build-job.js.map +1 -1
  113. package/src/lib/model/ai-build-plan.d.ts +23 -3
  114. package/src/lib/model/ai-build-plan.js +47 -6
  115. package/src/lib/model/ai-build-plan.js.map +1 -1
  116. package/src/lib/model/ai-build-progress.d.ts +2 -2
  117. package/src/lib/model/ai-build-progress.js +6 -4
  118. package/src/lib/model/ai-build-progress.js.map +1 -1
  119. package/src/lib/model/ai-credit-estimate.d.ts +69 -2
  120. package/src/lib/model/ai-credit-estimate.js +91 -3
  121. package/src/lib/model/ai-credit-estimate.js.map +1 -1
  122. package/src/lib/model/ai-job-cancel-copy.d.ts +44 -0
  123. package/src/lib/model/ai-job-cancel-copy.js +53 -0
  124. package/src/lib/model/ai-job-cancel-copy.js.map +1 -0
  125. package/src/lib/model/ai-job-failure-copy.d.ts +4 -1
  126. package/src/lib/model/ai-job-failure-copy.js +8 -3
  127. package/src/lib/model/ai-job-failure-copy.js.map +1 -1
  128. package/src/lib/model/ai-jobs.types.d.ts +20 -0
  129. package/src/lib/model/ai-jobs.types.js.map +1 -1
  130. package/src/lib/model/ai-plan-capabilities.d.ts +7 -0
  131. package/src/lib/model/ai-plan-capabilities.js.map +1 -1
  132. package/src/lib/model/ai-site-build-progress.js +7 -4
  133. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  134. package/src/lib/model/ai-site-job.d.ts +38 -4
  135. package/src/lib/model/ai-site-job.js +50 -14
  136. package/src/lib/model/ai-site-job.js.map +1 -1
  137. package/src/lib/model/ai-site-kinds.d.ts +4 -1
  138. package/src/lib/model/ai-site-kinds.js +34 -6
  139. package/src/lib/model/ai-site-kinds.js.map +1 -1
  140. package/src/lib/runtime/ai-dataset-generation.d.ts +77 -0
  141. package/src/lib/runtime/ai-dataset-generation.js +127 -0
  142. package/src/lib/runtime/ai-dataset-generation.js.map +1 -0
  143. package/src/lib/runtime/ai-doctrine-validators.d.ts +77 -1
  144. package/src/lib/runtime/ai-doctrine-validators.js +280 -6
  145. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  146. package/src/lib/runtime/ai-doctrine.js +10 -4
  147. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  148. package/src/lib/runtime/ai-node-tree.d.ts +2 -0
  149. package/src/lib/runtime/ai-node-tree.js +16 -0
  150. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  151. package/src/lib/runtime/ai-palette.generated.js +53 -7
  152. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  153. package/src/lib/server/ai-jobs-cancel.d.ts +13 -5
  154. package/src/lib/server/ai-jobs-cancel.js +27 -9
  155. package/src/lib/server/ai-jobs-cancel.js.map +1 -1
  156. package/src/lib/server/ai-jobs-gate.d.ts +12 -2
  157. package/src/lib/server/ai-jobs-gate.js +20 -9
  158. package/src/lib/server/ai-jobs-gate.js.map +1 -1
  159. package/src/lib/tools/ai-dataset-tool.d.ts +68 -0
  160. package/src/lib/tools/ai-dataset-tool.js +231 -0
  161. package/src/lib/tools/ai-dataset-tool.js.map +1 -0
  162. package/src/lib/usage/ai-usage-meter.d.ts +2 -1
  163. package/src/lib/usage/ai-usage-meter.js +17 -1
  164. package/src/lib/usage/ai-usage-meter.js.map +1 -1
  165. package/src/lib/usage/ai-usage-wire.d.ts +6 -0
  166. package/src/lib/usage/ai-usage-wire.js.map +1 -1
  167. package/src/lib/usage/assist-usage.d.ts +7 -0
  168. package/src/lib/usage/assist-usage.js +14 -4
  169. package/src/lib/usage/assist-usage.js.map +1 -1
  170. package/src/lib/usage/free-ai-credits-left.d.ts +7 -0
  171. package/src/lib/usage/free-ai-credits-left.js +30 -4
  172. package/src/lib/usage/free-ai-credits-left.js.map +1 -1
@@ -14,7 +14,8 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
14
14
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
- */ import { resolveEffectivePlan } from "@aglyn/aglyn/app-utils/plan-entitlements";
17
+ */ import { aiJobCancelAsked } from "./ai-job-cancel.js";
18
+ import { resolveEffectivePlan } from "@aglyn/aglyn/app-utils/plan-entitlements";
18
19
  import { resolveOrgIdForHost } from "@aglyn/tenant-data-admin/server/organizations";
19
20
  import { isAiPlanNewRef } from "../model/ai-build-plan.js";
20
21
  import { aiBuildDegradation, aiBuildInitialLedger, aiBuildItemDelivered, aiBuildItemOpen, aiBuildNextUnit } from "../model/ai-build-job.js";
@@ -36,7 +37,9 @@ import { AI_LAYOUT_FORM_PAGE_INPUT, AI_LAYOUT_LANGUAGE_INPUT, aiLayoutFormPageOf
36
37
  import { aiJobPublishesSite, aiPublishGuidedSite } from "./ai-site-publish.js";
37
38
  import { AI_SITE_CONTENT_INPUT, AI_SITE_POST_BUDGET, AI_SITE_POSTS, AI_SITE_POSTS_LABEL, AI_SITE_PRODUCTS_LABEL, AI_SITE_PRODUCTS_PRICE_NOTE, aiPublishSitePosts, aiSiteContentBriefLines, aiSiteContentPart, aiSiteContentRefusal, aiSiteListings, aiSiteProductsBriefLine, createAiSiteProductsRunner, runAiSitePostsUnit } from "./ai-job-site-content.js";
38
39
  import { dropPluginSiteCache } from "@aglyn/aglyn/plugin-manager/plugin-site-cache";
40
+ import { AI_SITE_DATASET_BUDGET, AI_SITE_DATASET_INPUT, AI_SITE_DATASETS_MAX, aiSiteDatasetBriefLines, aiSiteDatasetInputOf, aiSiteDatasetListings, aiSiteDatasetRefusal, aiSitePlanDatasets, aiSiteRecordTemplateOf, runAiSiteDatasetUnit } from "./ai-job-site-datasets.js";
39
41
  import { AI_SITE_LOOK_BUDGET, aiRunSiteLook } from "./ai-job-site-look.js";
42
+ import { AI_FORM_DATASET_MADE_INPUT } from "./ai-job-form-dataset.js";
40
43
  import { aiConfirmedPlan, aiUnspentOutcome } from "./ai-job-generation.js";
41
44
  import { AI_JOB_BRIEF_MAX_CHARS } from "./ai-job-text-step.js";
42
45
  import { AI_JOB_PAGE_STEP_MINIMUM_MS } from "./ai-job-page-budget.js";
@@ -76,6 +79,12 @@ import { aiJobStepRunMinimumMs, aiJobStepRunnerFor, registerAiJobStep, registerA
76
79
  products: {
77
80
  jobKind: 'products',
78
81
  resource: 'product'
82
+ },
83
+ // The scaffold's own runner designs it (`ai-job-site-datasets.ts`), and the
84
+ // data plugin's writer keeps it: a draft of another plugin's (AGL-3616).
85
+ dataset: {
86
+ jobKind: 'text',
87
+ resource: 'draft'
79
88
  }
80
89
  };
81
90
  /** The unit kind a creation is built as, where a unit builds it. */ const CREATION_UNIT_KINDS = {
@@ -122,13 +131,15 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
122
131
  * (one a pass, the largest part) and a welcome email — with nothing to
123
132
  * spare, so a runner that never finishes is still bounded while a real site
124
133
  * is not.
125
- */ export const AI_SITE_MAX_PASSES = AI_SITE_PAGES.max * (AI_SITE_MAX_SECTIONS + 1) + CREATION_UNITS.length + AI_SITE_POSTS + 1;
134
+ */ export const AI_SITE_MAX_PASSES = AI_SITE_PAGES.max * (AI_SITE_MAX_SECTIONS + 1) + CREATION_UNITS.length + AI_SITE_DATASETS_MAX + AI_SITE_POSTS + 1;
126
135
  /**
127
136
  * The units a plan implies, in build order: the palette first, because a
128
137
  * member reads it while the pages are still building; then the layout every
129
- * page renders inside and the form they place, because a page binds both by
130
- * id and so needs them to exist; then the pages; then the welcome email,
131
- * which is about the site rather than part of it.
138
+ * page renders inside, the site's datasets, and the form they place —
139
+ * because a page binds the layout and the form by id, and a form binds the
140
+ * dataset it writes to (AGL-3616), so each needs what it binds to exist;
141
+ * then the pages; then the welcome email, which is about the site rather
142
+ * than part of it.
132
143
  */ export function aiSiteJobUnits(plan, options = {}) {
133
144
  // The site's look is designed first, on every scaffold (AGL-3660): the
134
145
  // header, the footer and every page render in it from their first draft.
@@ -143,6 +154,19 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
143
154
  ];
144
155
  for (const { unit, create } of CREATION_UNITS){
145
156
  if (unit === 'theme') continue;
157
+ // The site's datasets (AGL-3616), before anything that lists them and
158
+ // before the form, which may write its submissions to one of them.
159
+ if (unit === 'form') {
160
+ aiSitePlanDatasets(plan).forEach((creation, index)=>{
161
+ units.push(_extends({
162
+ kind: 'dataset'
163
+ }, UNIT_KINDS.dataset, {
164
+ slot: `d${index}`,
165
+ creation,
166
+ label: creation.name
167
+ }));
168
+ });
169
+ }
146
170
  const creation = plan.create.find((entry)=>entry.kind === create);
147
171
  if (!creation) continue;
148
172
  units.push(_extends({
@@ -253,8 +277,8 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
253
277
  const row = rows == null ? void 0 : rows.shift();
254
278
  if (!row || !unit.creation) continue;
255
279
  // A theme change is a proposal with no record to reference; a layout, a
256
- // form and a component become ids a page can name.
257
- if (unit.creation.kind !== 'layout' && unit.creation.kind !== 'form' && unit.creation.kind !== 'component') {
280
+ // form, a component and a dataset (AGL-3616) become ids a page can name.
281
+ if (unit.creation.kind !== 'layout' && unit.creation.kind !== 'form' && unit.creation.kind !== 'component' && unit.creation.kind !== 'dataset') {
258
282
  continue;
259
283
  }
260
284
  built.set(unit.creation.name.toLowerCase(), {
@@ -358,6 +382,7 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
358
382
  * for leaving it out.
359
383
  */ export function aiSiteUnitJob(job, unit, built) {
360
384
  var _plan_labels;
385
+ var _unitPlan_create_, _unit_creation;
361
386
  const plan = job.plan;
362
387
  const labels = _extends({}, (_plan_labels = plan.labels) != null ? _plan_labels : {});
363
388
  for (const entry of built.values())labels[entry.id] = entry.label;
@@ -379,12 +404,14 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
379
404
  unitPlan.create = [
380
405
  _extends({}, unit.creation, {
381
406
  duplicateOf: aiSiteResolvedRef(unit.creation.duplicateOf, built)
382
- })
407
+ }, unit.creation.writesTo ? {
408
+ writesTo: aiSiteResolvedRef(unit.creation.writesTo, built)
409
+ } : {})
383
410
  ];
384
411
  brief.push(`Build the ${unit.creation.kind} “${unit.creation.name}”: ${unit.creation.why}`);
385
412
  }
386
413
  if (unit.screen) {
387
- var _aiSiteResolvedRef, _aiSiteResolvedRef1, _job_outputs;
414
+ var _aiSiteResolvedRef, _aiSiteResolvedRef1;
388
415
  const screen = unit.screen;
389
416
  // Everything the scaffold has already built is a record the site has, so
390
417
  // the page is planned against it the way a page job is planned against
@@ -418,8 +445,12 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
418
445
  })
419
446
  ];
420
447
  brief.push(`Build the page “${screen.title}” of this site, at ${screen.slug}.`);
421
- // What the blog's posts or the store's products are, once built (AGL-3676).
422
- if (job.kind === 'site') brief.push(...aiSiteContentBriefLines((_job_outputs = job.outputs) != null ? _job_outputs : []));
448
+ // What the blog's posts or the store's products are, once built (AGL-3676),
449
+ // and what the site's datasets hold (AGL-3616).
450
+ if (job.kind === 'site') {
451
+ var _job_outputs, _job_outputs1;
452
+ brief.push(...aiSiteContentBriefLines((_job_outputs = job.outputs) != null ? _job_outputs : []), ...aiSiteDatasetBriefLines((_job_outputs1 = job.outputs) != null ? _job_outputs1 : []));
453
+ }
423
454
  }
424
455
  if (unit.kind === 'products') brief.push(aiSiteProductsBriefLine());
425
456
  if (unit.kind === 'email') {
@@ -433,15 +464,15 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
433
464
  // A page is told the pages built before it, whose photos it does not place
434
465
  // again (AGL-3660): the maker's portrait in About is never a vase in Work.
435
466
  if (unit.screen) {
436
- var _job_outputs1;
437
- const pages = ((_job_outputs1 = job.outputs) != null ? _job_outputs1 : []).filter((output)=>output.resource === 'screen' && output.hostId === job.hostId && output.id).map((output)=>output.id);
467
+ var _job_outputs2;
468
+ const pages = ((_job_outputs2 = job.outputs) != null ? _job_outputs2 : []).filter((output)=>output.resource === 'screen' && output.hostId === job.hostId && output.id).map((output)=>output.id);
438
469
  if (pages.length) unitInputs[AI_STOCK_PHOTO_PAGES_INPUT] = pages;
439
470
  }
440
471
  // The layout is built before the pages, so it is told them (AGL-3596): their
441
472
  // ids are minted on the plan, and the platform writes the header's links.
442
473
  // A page is told them too, so its buttons may go to a page built after it.
443
474
  if (job.kind === 'site' && (unit.kind === 'layout' || unit.kind === 'page')) {
444
- var _job_outputs2;
475
+ var _job_outputs3, _job_outputs4;
445
476
  var _aiSiteKindOfInputs, _aiSiteKindOfInputs1;
446
477
  // A guided start links every page the person asked for (AGL-3660), and
447
478
  // the blog its first posts are written into, by its path, second after
@@ -480,7 +511,7 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
480
511
  // The store's catalog and the blog, listed by the sections that show them,
481
512
  // and the cart in a selling site's header (AGL-3676).
482
513
  const listings = aiSiteListings({
483
- outputs: (_job_outputs2 = job.outputs) != null ? _job_outputs2 : [],
514
+ outputs: (_job_outputs3 = job.outputs) != null ? _job_outputs3 : [],
484
515
  screens: merged.screens,
485
516
  sells: aiSiteSellsProducts(job),
486
517
  // A store lists its catalog on its Shop page even when its first
@@ -489,15 +520,28 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
489
520
  // A music site places an empty player for the artist's own tracks (AGL-3716).
490
521
  music: ((_aiSiteKindOfInputs1 = aiSiteKindOfInputs(job.inputs)) == null ? void 0 : _aiSiteKindOfInputs1.id) === 'music'
491
522
  });
523
+ // The site's datasets, each repeated in the sections that name it (AGL-3616).
524
+ const delivered = new Map([
525
+ ...built.entries()
526
+ ].filter(([, entry])=>entry.kind === 'dataset').map(([name, entry])=>[
527
+ name,
528
+ entry.id
529
+ ]));
530
+ listings.push(...aiSiteDatasetListings({
531
+ outputs: (_job_outputs4 = job.outputs) != null ? _job_outputs4 : [],
532
+ datasets: aiSitePlanDatasets(plan),
533
+ screens: merged.screens,
534
+ delivered
535
+ }));
492
536
  if (listings.length) unitInputs[AI_LAYOUT_LISTINGS_INPUT] = listings;
493
537
  }
494
538
  // A site's pages and its layout are designed in the layout language and
495
539
  // compiled (AGL-3660), and a page is told which page places the site's form.
496
540
  if (unit.kind === 'layout' || unit.kind === 'page') {
497
- var _job_outputs3;
541
+ var _job_outputs5;
498
542
  var _look_proposal;
499
543
  // The look designed first (AGL-3660): its header arrangement and band rhythm.
500
- const look = ((_job_outputs3 = job.outputs) != null ? _job_outputs3 : []).find((output)=>output.resource === 'theme' && output.id === 'look');
544
+ const look = ((_job_outputs5 = job.outputs) != null ? _job_outputs5 : []).find((output)=>output.resource === 'theme' && output.id === 'look');
501
545
  const style = look == null ? void 0 : (_look_proposal = look.proposal) == null ? void 0 : _look_proposal['style'];
502
546
  if (style) unitInputs['siteStyle'] = {
503
547
  headerAlign: style['headerAlign'],
@@ -509,11 +553,11 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
509
553
  if (formPage) unitInputs[AI_LAYOUT_FORM_PAGE_INPUT] = formPage;
510
554
  }
511
555
  if (unit.kind === 'posts') {
512
- var _job_outputs4, _inputs_businessName;
556
+ var _job_outputs6, _inputs_businessName;
513
557
  // The posts already written (one a pass), the addresses the site's own
514
558
  // pages answer at, and the byline the person gave (AGL-3676).
515
559
  const posts = {
516
- written: ((_job_outputs4 = job.outputs) != null ? _job_outputs4 : []).filter((output)=>output.resource === 'entry').map((output)=>({
560
+ written: ((_job_outputs6 = job.outputs) != null ? _job_outputs6 : []).filter((output)=>output.resource === 'entry').map((output)=>({
517
561
  id: output.id,
518
562
  title: output.label
519
563
  })),
@@ -525,6 +569,14 @@ export const AI_SITE_NO_PLAN_COPY = 'This site job has no confirmed plan to buil
525
569
  unitInputs[AI_SITE_CONTENT_INPUT] = posts;
526
570
  }
527
571
  if (unit.kind === 'products') unitInputs['target'] = 'catalog';
572
+ // Where the plan lists a dataset, which its design reads, and the form
573
+ // that writes to it (AGL-3616).
574
+ if (unit.kind === 'dataset' && unit.creation) {
575
+ unitInputs[AI_SITE_DATASET_INPUT] = aiSiteDatasetInputOf(unit.creation, plan.screens, plan.create);
576
+ }
577
+ // A form's dataset this start made, which the form may bind (AGL-3616).
578
+ const writesTo = unit.kind === 'form' ? (_unitPlan_create_ = unitPlan.create[0]) == null ? void 0 : _unitPlan_create_.writesTo : null;
579
+ if (writesTo && isAiPlanNewRef((_unit_creation = unit.creation) == null ? void 0 : _unit_creation.writesTo)) unitInputs[AI_FORM_DATASET_MADE_INPUT] = writesTo;
528
580
  return _extends({}, job, {
529
581
  $id: aiSiteUnitJobId(job, unit),
530
582
  kind: unit.jobKind,
@@ -728,16 +780,31 @@ export const aiSiteJobAdmission = createAiSiteJobAdmission();
728
780
  * Try again read, so a site is settled item by item exactly as a build is.
729
781
  */ export function aiSiteLedgerUnits(units) {
730
782
  const creations = units.filter((unit)=>unit.kind === 'layout' || unit.kind === 'form').map((unit)=>unit.slot);
783
+ const datasets = units.filter((unit)=>unit.kind === 'dataset' && unit.creation);
784
+ /** The dataset a form writes to (AGL-3616): built before it, and it without the binding if it fails. */ const writtenTo = (creation)=>datasets.filter((unit)=>{
785
+ var _ref;
786
+ var _unit_creation;
787
+ return !!(creation == null ? void 0 : creation.writesTo) && creation.writesTo.trim().toLowerCase() === `new:${(_ref = (_unit_creation = unit.creation) == null ? void 0 : _unit_creation.name) != null ? _ref : ''}`.trim().toLowerCase();
788
+ }).map((unit)=>unit.slot);
789
+ /** The datasets a page lists or is the record template of (AGL-3616): built before it, and it without them if they fail. */ const listed = (screen)=>datasets.filter((unit)=>{
790
+ var _ref, _ref1;
791
+ var _unit_creation, _screen_record;
792
+ const named = `new:${(_ref = (_unit_creation = unit.creation) == null ? void 0 : _unit_creation.name) != null ? _ref : ''}`.trim().toLowerCase();
793
+ return (screen == null ? void 0 : (_screen_record = screen.record) == null ? void 0 : _screen_record.dataset.trim().toLowerCase()) === named || ((_ref1 = screen == null ? void 0 : screen.sections) != null ? _ref1 : []).some((section)=>section.uses.some((ref)=>ref.trim().toLowerCase() === named));
794
+ }).map((unit)=>unit.slot);
731
795
  return units.map((unit)=>_extends({
732
796
  slot: unit.slot,
733
- op: unit.kind === 'theme' || unit.kind === 'posts' || unit.kind === 'products' ? unit.kind : unit.jobKind,
797
+ op: unit.kind === 'theme' || unit.kind === 'posts' || unit.kind === 'products' || unit.kind === 'dataset' ? unit.kind : unit.jobKind,
734
798
  label: unit.label
735
799
  }, unit.creation ? {
736
800
  creation: unit.creation
737
801
  } : {}, unit.screen ? {
738
802
  screen: unit.screen
739
803
  } : {}, {
740
- deps: unit.kind === 'page' ? creations : []
804
+ deps: unit.kind === 'page' ? [
805
+ ...creations,
806
+ ...listed(unit.screen)
807
+ ] : unit.kind === 'form' ? writtenTo(unit.creation) : []
741
808
  }));
742
809
  }
743
810
  /**
@@ -769,20 +836,21 @@ export const aiSiteJobAdmission = createAiSiteJobAdmission();
769
836
  return aiSiteJobUnits(plan, {
770
837
  welcomeEmail: aiSiteWelcomeEmail(inputs, freeTaste),
771
838
  content: aiSiteContentPart(job.inputs, freeTaste)
772
- }).filter((unit)=>unit.kind === 'theme' || unit.kind === 'posts' || runnerFor(unit.jobKind));
839
+ }).filter((unit)=>unit.kind === 'theme' || unit.kind === 'posts' || unit.kind === 'dataset' || runnerFor(unit.jobKind));
773
840
  }
774
841
  export function createAiJobSiteStep(deps = {}) {
775
- var _deps_runnerFor, _deps_readNodes, _deps_publish, _deps_look, _deps_contentRefusal, _deps_publishPosts, _deps_dropCache;
842
+ var _deps_runnerFor, _deps_readNodes, _deps_publish, _deps_look, _deps_contentRefusal, _deps_datasetRefusal, _deps_publishPosts, _deps_dropCache;
776
843
  const runnerFor = (_deps_runnerFor = deps.runnerFor) != null ? _deps_runnerFor : aiJobStepRunnerFor;
777
844
  const readNodes = (_deps_readNodes = deps.readNodes) != null ? _deps_readNodes : readAiDraftNodes;
778
845
  const publish = (_deps_publish = deps.publish) != null ? _deps_publish : aiPublishGuidedSite;
779
846
  const look = (_deps_look = deps.look) != null ? _deps_look : aiRunSiteLook;
780
847
  const contentRefusal = (_deps_contentRefusal = deps.contentRefusal) != null ? _deps_contentRefusal : aiSiteContentRefusal;
848
+ const datasetRefusal = (_deps_datasetRefusal = deps.datasetRefusal) != null ? _deps_datasetRefusal : aiSiteDatasetRefusal;
781
849
  const publishPosts = (_deps_publishPosts = deps.publishPosts) != null ? _deps_publishPosts : aiPublishSitePosts;
782
850
  const dropCache = (_deps_dropCache = deps.dropCache) != null ? _deps_dropCache : dropPluginSiteCache;
783
851
  return async (context)=>{
784
- var _ref, _job_outputs, _deps_posts, _deps_products, _job_outputs1;
785
- var _job_items, _rows_get;
852
+ var _ref, _job_outputs, _deps_posts, _deps_products, _deps_dataset, _job_outputs1;
853
+ var _job_items, _rows_get, _rows_get1;
786
854
  const { job } = context;
787
855
  // The scaffold asks no model of its own. What it names where it spends
788
856
  // nothing is the model its plan ran on, which is the one exchange this
@@ -824,6 +892,8 @@ export function createAiJobSiteStep(deps = {}) {
824
892
  /** A guided start's last pass puts what it built on the site, once (AGL-3596): only its pages that were built. */ const finish = async (outcome, pages)=>{
825
893
  var _postsRow_outputs;
826
894
  if (!aiJobPublishesSite(job) || job.sitePublish || !job.hostId || !pages.length) return outcome;
895
+ // A canceled start never puts the site live (AGL-3616): its pages stay drafts.
896
+ if (await aiJobCancelAsked(context.firestore, job, context.signal)) return outcome;
827
897
  // A blog the header links by path before its posts exist (AGL-3660):
828
898
  // owed and not delivered, its links come out of what is published.
829
899
  const postsRow = rows.get('posts');
@@ -872,11 +942,20 @@ export function createAiJobSiteStep(deps = {}) {
872
942
  error
873
943
  }));
874
944
  };
875
- /** The pages built so far, as the job reported them. */ const builtPages = (extra = [])=>{
945
+ /**
946
+ * The pages built so far, as the job reported them. A dataset's record
947
+ * template is not among them (AGL-3616): it renders once per record only
948
+ * once the member saves its binding, so it is never published at its own
949
+ * address, where its `{{item.*}}` would show.
950
+ */ const templates = new Set(units.filter((unit)=>{
951
+ var _unit_screen;
952
+ return unit.kind === 'page' && ((_unit_screen = unit.screen) == null ? void 0 : _unit_screen.record);
953
+ }).map((unit)=>unit.slot));
954
+ const builtPages = (extra = [])=>{
876
955
  var _job_outputs;
877
956
  const ids = new Set(units.filter((unit)=>{
878
957
  var _rows_get;
879
- return unit.kind === 'page' && aiBuildItemDelivered((_rows_get = rows.get(unit.slot)) != null ? _rows_get : {
958
+ return unit.kind === 'page' && !templates.has(unit.slot) && aiBuildItemDelivered((_rows_get = rows.get(unit.slot)) != null ? _rows_get : {
880
959
  status: 'pending'
881
960
  });
882
961
  }).flatMap((unit)=>{
@@ -895,7 +974,7 @@ export function createAiJobSiteStep(deps = {}) {
895
974
  const catalog = unit.kind === 'products' ? runnerFor('products') : undefined;
896
975
  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({
897
976
  catalog
898
- }) : undefined : runnerFor(unit.jobKind);
977
+ }) : undefined : unit.kind === 'dataset' ? (_deps_dataset = deps.dataset) != null ? _deps_dataset : runAiSiteDatasetUnit : runnerFor(unit.jobKind);
899
978
  if (!runner) return _extends({}, aiUnspentOutcome(model), init);
900
979
  const othersOpen = ledgerUnits.some((one)=>{
901
980
  var _rows_get;
@@ -954,6 +1033,48 @@ export function createAiJobSiteStep(deps = {}) {
954
1033
  note: `Not built: ${refusal}`
955
1034
  }));
956
1035
  }
1036
+ // A dataset asks the same before its first pass (AGL-3616): the data
1037
+ // plugin runs here, and its writer admits this member now.
1038
+ if (unit.kind === 'dataset' && ((_rows_get1 = rows.get(unit.slot)) == null ? void 0 : _rows_get1.status) !== 'running') {
1039
+ const refusal = await datasetRefusal(_extends({}, context, {
1040
+ job
1041
+ })).catch((error)=>{
1042
+ console.error('ai site dataset admission failed', {
1043
+ orgId: job.orgId,
1044
+ jobId: job.$id,
1045
+ slot: unit.slot,
1046
+ error
1047
+ });
1048
+ return AI_SITE_UNIT_EMPTY_COPY;
1049
+ });
1050
+ if (refusal) return closing(settle({
1051
+ slot: unit.slot,
1052
+ status: 'skipped',
1053
+ note: `Not built: ${refusal}`
1054
+ }));
1055
+ }
1056
+ // A record template whose dataset was not made has nothing to show
1057
+ // (AGL-3616): it is not built, and the pages that list the dataset are
1058
+ // built without it.
1059
+ const templateOf = unit.kind === 'page' && unit.screen ? aiSiteRecordTemplateOf(unit.screen) : null;
1060
+ if (templateOf) {
1061
+ const dataset = units.find((one)=>{
1062
+ var _one_creation;
1063
+ return one.kind === 'dataset' && ((_one_creation = one.creation) == null ? void 0 : _one_creation.name.trim().toLowerCase()) === templateOf.toLowerCase();
1064
+ });
1065
+ const row = dataset ? rows.get(dataset.slot) : undefined;
1066
+ if (!row || !aiBuildItemDelivered(row) || !row.outputs.length) {
1067
+ return closing(settle(_extends({
1068
+ slot: unit.slot,
1069
+ status: 'skipped',
1070
+ note: `Not built: the dataset “${templateOf}” it shows a page of each record of could not be created.`
1071
+ }, dataset ? {
1072
+ degradedBy: [
1073
+ dataset.slot
1074
+ ]
1075
+ } : {})));
1076
+ }
1077
+ }
957
1078
  // A page whose layout or form failed is built without it, and says so.
958
1079
  const degradation = aiBuildDegradation(next, {
959
1080
  units: ledgerUnits,
@@ -1039,7 +1160,7 @@ export function createAiJobSiteStep(deps = {}) {
1039
1160
  rows.set(unit.slot, _extends({}, rows.get(unit.slot), {
1040
1161
  status: degraded ? 'degraded' : 'succeeded'
1041
1162
  }));
1042
- return finish(done, builtPages(unit.kind === 'page' ? outcome.outputs.filter((output)=>output.resource === 'screen') : []));
1163
+ return finish(done, builtPages(unit.kind === 'page' && !templates.has(unit.slot) ? outcome.outputs.filter((output)=>output.resource === 'screen') : []));
1043
1164
  };
1044
1165
  }
1045
1166
  export const runAiJobSiteStep = createAiJobSiteStep();
@@ -1062,7 +1183,7 @@ export const runAiJobSiteStep = createAiJobSiteStep();
1062
1183
  const units = aiSiteJobUnits(plan, {
1063
1184
  welcomeEmail: inputs.welcomeEmail,
1064
1185
  content: aiSiteContentPart(job.inputs, false)
1065
- }).filter((unit)=>(unit.kind === 'theme' || unit.kind === 'posts' || aiJobStepRunnerFor(unit.jobKind)) && (!owed.size || owed.has(unit.slot)));
1186
+ }).filter((unit)=>(unit.kind === 'theme' || unit.kind === 'posts' || unit.kind === 'dataset' || aiJobStepRunnerFor(unit.jobKind)) && (!owed.size || owed.has(unit.slot)));
1066
1187
  const outputs = (_job_outputs = job.outputs) != null ? _job_outputs : [];
1067
1188
  const ledger = ((_job_items1 = job.items) == null ? void 0 : _job_items1.length) ? job.items : aiSiteInitialLedger(units, outputs);
1068
1189
  const next = aiBuildNextUnit(aiSiteLedgerUnits(units), ledger);
@@ -1070,6 +1191,7 @@ export const runAiJobSiteStep = createAiJobSiteStep();
1070
1191
  if (!unit) return AI_JOB_PAGE_STEP_MINIMUM_MS;
1071
1192
  if (unit.kind === 'theme') return AI_SITE_LOOK_BUDGET.minimumMs;
1072
1193
  if (unit.kind === 'posts') return AI_SITE_POST_BUDGET.minimumMs;
1194
+ if (unit.kind === 'dataset') return AI_SITE_DATASET_BUDGET.minimumMs;
1073
1195
  return aiJobStepRunMinimumMs(aiSiteUnitJob(job, unit, aiBuildBuiltRefs(aiSiteLedgerUnits(units), ledger, outputs)));
1074
1196
  }
1075
1197
  /**