@aglyn/plugins-ai 1.0.0-beta.244 → 1.0.0-beta.247

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 (173) hide show
  1. package/package.json +17 -17
  2. package/src/lib/activity/ai-activity-actions.js +7 -1
  3. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  4. package/src/lib/components/ai-job-links.js +21 -0
  5. package/src/lib/components/ai-job-links.js.map +1 -1
  6. package/src/lib/components/ai-product-zones.d.ts +3 -1
  7. package/src/lib/components/ai-product-zones.js.map +1 -1
  8. package/src/lib/components/ai-products-hub-card.component.js +1 -1
  9. package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
  10. package/src/lib/components/ai-products-proposals.component.js +17 -9
  11. package/src/lib/components/ai-products-proposals.component.js.map +1 -1
  12. package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
  13. package/src/lib/components/ai-site-start-card.component.js +98 -23
  14. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  15. package/src/lib/components/ai-upsell-dialog.component.js +1 -1
  16. package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
  17. package/src/lib/declarations.server.js +12 -0
  18. package/src/lib/declarations.server.js.map +1 -1
  19. package/src/lib/jobs/ai-build-capabilities.js +216 -2
  20. package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
  21. package/src/lib/jobs/ai-job-build-step.d.ts +3 -1
  22. package/src/lib/jobs/ai-job-build-step.js +124 -18
  23. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  24. package/src/lib/jobs/ai-job-duplicate-step.d.ts +123 -0
  25. package/src/lib/jobs/ai-job-duplicate-step.js +269 -0
  26. package/src/lib/jobs/ai-job-duplicate-step.js.map +1 -0
  27. package/src/lib/jobs/ai-job-form-dataset.d.ts +8 -5
  28. package/src/lib/jobs/ai-job-form-dataset.js +28 -10
  29. package/src/lib/jobs/ai-job-form-dataset.js.map +1 -1
  30. package/src/lib/jobs/ai-job-form-step.d.ts +20 -4
  31. package/src/lib/jobs/ai-job-form-step.js +98 -28
  32. package/src/lib/jobs/ai-job-form-step.js.map +1 -1
  33. package/src/lib/jobs/ai-job-layout-language.d.ts +6 -2
  34. package/src/lib/jobs/ai-job-layout-language.js +29 -5
  35. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  36. package/src/lib/jobs/ai-job-layout-step.js +4 -1
  37. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  38. package/src/lib/jobs/ai-job-media-step.d.ts +239 -0
  39. package/src/lib/jobs/ai-job-media-step.js +610 -0
  40. package/src/lib/jobs/ai-job-media-step.js.map +1 -0
  41. package/src/lib/jobs/ai-job-navigation-step.d.ts +157 -0
  42. package/src/lib/jobs/ai-job-navigation-step.js +439 -0
  43. package/src/lib/jobs/ai-job-navigation-step.js.map +1 -0
  44. package/src/lib/jobs/ai-job-page-language.d.ts +4 -1
  45. package/src/lib/jobs/ai-job-page-language.js +30 -5
  46. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  47. package/src/lib/jobs/ai-job-page-sections.js +5 -4
  48. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  49. package/src/lib/jobs/ai-job-page-step.js +3 -2
  50. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  51. package/src/lib/jobs/ai-job-plan-step.d.ts +14 -3
  52. package/src/lib/jobs/ai-job-plan-step.js +87 -20
  53. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  54. package/src/lib/jobs/ai-job-products-step.d.ts +2 -2
  55. package/src/lib/jobs/ai-job-products-step.js +2 -2
  56. package/src/lib/jobs/ai-job-products-step.js.map +1 -1
  57. package/src/lib/jobs/ai-job-settings-step.d.ts +268 -0
  58. package/src/lib/jobs/ai-job-settings-step.js +690 -0
  59. package/src/lib/jobs/ai-job-settings-step.js.map +1 -0
  60. package/src/lib/jobs/ai-job-site-content.d.ts +22 -8
  61. package/src/lib/jobs/ai-job-site-content.js +33 -15
  62. package/src/lib/jobs/ai-job-site-content.js.map +1 -1
  63. package/src/lib/jobs/ai-job-site-datasets.d.ts +3 -1
  64. package/src/lib/jobs/ai-job-site-datasets.js +13 -5
  65. package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
  66. package/src/lib/jobs/ai-job-site-events.d.ts +148 -0
  67. package/src/lib/jobs/ai-job-site-events.js +313 -0
  68. package/src/lib/jobs/ai-job-site-events.js.map +1 -0
  69. package/src/lib/jobs/ai-job-site-step.d.ts +12 -4
  70. package/src/lib/jobs/ai-job-site-step.js +132 -26
  71. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  72. package/src/lib/jobs/ai-job-tracking-step.d.ts +184 -0
  73. package/src/lib/jobs/ai-job-tracking-step.js +501 -0
  74. package/src/lib/jobs/ai-job-tracking-step.js.map +1 -0
  75. package/src/lib/jobs/ai-layout-stock-photos.d.ts +21 -0
  76. package/src/lib/jobs/ai-layout-stock-photos.js +41 -4
  77. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  78. package/src/lib/jobs/ai-site-publish.d.ts +62 -4
  79. package/src/lib/jobs/ai-site-publish.js +202 -56
  80. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  81. package/src/lib/jobs/ai-stock-photo-picker.d.ts +24 -0
  82. package/src/lib/jobs/ai-stock-photo-picker.js +108 -0
  83. package/src/lib/jobs/ai-stock-photo-picker.js.map +1 -0
  84. package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.d.ts +37 -0
  85. package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.js +47 -0
  86. package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.js.map +1 -0
  87. package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.d.ts +21 -0
  88. package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.js +128 -0
  89. package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.js.map +1 -0
  90. package/src/lib/layout-language/ai-layout-compiler.d.ts +16 -7
  91. package/src/lib/layout-language/ai-layout-compiler.js +62 -42
  92. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  93. package/src/lib/layout-language/ai-layout-frame-hollows.d.ts +41 -0
  94. package/src/lib/layout-language/ai-layout-frame-hollows.js +330 -0
  95. package/src/lib/layout-language/ai-layout-frame-hollows.js.map +1 -0
  96. package/src/lib/layout-language/ai-layout-frame.d.ts +24 -5
  97. package/src/lib/layout-language/ai-layout-frame.js +93 -20
  98. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  99. package/src/lib/layout-language/ai-layout-gaps.js +3 -1
  100. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
  101. package/src/lib/layout-language/ai-layout-labels.d.ts +70 -0
  102. package/src/lib/layout-language/ai-layout-labels.js +162 -0
  103. package/src/lib/layout-language/ai-layout-labels.js.map +1 -0
  104. package/src/lib/layout-language/ai-layout-links.js +5 -2
  105. package/src/lib/layout-language/ai-layout-links.js.map +1 -1
  106. package/src/lib/layout-language/ai-layout-listings.d.ts +49 -1
  107. package/src/lib/layout-language/ai-layout-listings.js +66 -5
  108. package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
  109. package/src/lib/layout-language/ai-layout-tree.d.ts +2 -0
  110. package/src/lib/layout-language/ai-layout-tree.js +17 -0
  111. package/src/lib/layout-language/ai-layout-tree.js.map +1 -1
  112. package/src/lib/model/ai-build-event-page.d.ts +70 -0
  113. package/src/lib/model/ai-build-event-page.js +145 -0
  114. package/src/lib/model/ai-build-event-page.js.map +1 -0
  115. package/src/lib/model/ai-build-job.d.ts +44 -0
  116. package/src/lib/model/ai-build-job.js +141 -13
  117. package/src/lib/model/ai-build-job.js.map +1 -1
  118. package/src/lib/model/ai-build-plan.d.ts +39 -3
  119. package/src/lib/model/ai-build-plan.js +59 -5
  120. package/src/lib/model/ai-build-plan.js.map +1 -1
  121. package/src/lib/model/ai-credit-estimate.d.ts +52 -0
  122. package/src/lib/model/ai-credit-estimate.js +51 -0
  123. package/src/lib/model/ai-credit-estimate.js.map +1 -1
  124. package/src/lib/model/ai-job-activity.js +6 -1
  125. package/src/lib/model/ai-job-activity.js.map +1 -1
  126. package/src/lib/model/ai-jobs.types.d.ts +18 -2
  127. package/src/lib/model/ai-jobs.types.js +6 -1
  128. package/src/lib/model/ai-jobs.types.js.map +1 -1
  129. package/src/lib/model/ai-layout-menus.d.ts +104 -0
  130. package/src/lib/model/ai-layout-menus.js +518 -0
  131. package/src/lib/model/ai-layout-menus.js.map +1 -0
  132. package/src/lib/model/ai-plan-capabilities.d.ts +8 -0
  133. package/src/lib/model/ai-plan-capabilities.js.map +1 -1
  134. package/src/lib/model/ai-products.d.ts +31 -1
  135. package/src/lib/model/ai-products.js +34 -1
  136. package/src/lib/model/ai-products.js.map +1 -1
  137. package/src/lib/model/ai-site-inventory.d.ts +17 -0
  138. package/src/lib/model/ai-site-inventory.js +5 -0
  139. package/src/lib/model/ai-site-inventory.js.map +1 -1
  140. package/src/lib/model/ai-site-job.d.ts +12 -2
  141. package/src/lib/model/ai-site-job.js +39 -12
  142. package/src/lib/model/ai-site-job.js.map +1 -1
  143. package/src/lib/model/ai-site-store-pages.js +1 -1
  144. package/src/lib/model/ai-site-store-pages.js.map +1 -1
  145. package/src/lib/runtime/ai-doctrine-validators.d.ts +78 -3
  146. package/src/lib/runtime/ai-doctrine-validators.js +219 -31
  147. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  148. package/src/lib/runtime/ai-doctrine.d.ts +6 -1
  149. package/src/lib/runtime/ai-doctrine.js +16 -7
  150. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  151. package/src/lib/runtime/ai-eval-live.d.ts +1 -1
  152. package/src/lib/runtime/ai-eval-live.js +1 -1
  153. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  154. package/src/lib/runtime/ai-palette.d.ts +3 -1
  155. package/src/lib/runtime/ai-palette.generated.js +499 -11
  156. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  157. package/src/lib/runtime/ai-palette.js +5 -3
  158. package/src/lib/runtime/ai-palette.js.map +1 -1
  159. package/src/lib/runtime/ai-products-generation.d.ts +4 -2
  160. package/src/lib/runtime/ai-products-generation.js +12 -6
  161. package/src/lib/runtime/ai-products-generation.js.map +1 -1
  162. package/src/lib/runtime/ai-template-examples.js +8 -2
  163. package/src/lib/runtime/ai-template-examples.js.map +1 -1
  164. package/src/lib/runtime/site-inventory.d.ts +12 -0
  165. package/src/lib/runtime/site-inventory.js +29 -1
  166. package/src/lib/runtime/site-inventory.js.map +1 -1
  167. package/src/lib/server.js +20 -0
  168. package/src/lib/server.js.map +1 -1
  169. package/src/lib/testing/stand-in-dataset-index.js +37 -3
  170. package/src/lib/testing/stand-in-dataset-index.js.map +1 -1
  171. package/src/lib/tools/ai-products-tool.d.ts +3 -1
  172. package/src/lib/tools/ai-products-tool.js +15 -5
  173. package/src/lib/tools/ai-products-tool.js.map +1 -1
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { AiDoctrineViolation } from '../runtime/ai-doctrine-validators';
18
+ import type { AiBuildOps } from './ai-build-job';
19
+ import { type AiBuildItem, type AiBuildPlan } from './ai-build-plan';
20
+ /**
21
+ * THE PAGE A BUILD'S EVENTS ARE LISTED ON (AGL-3755).
22
+ *
23
+ * A build's `event` items are written as events, and an event is seen on the
24
+ * site only through an Event List a page section places — the section whose
25
+ * uses name the event. The live eval of 2026-10-13 asked "add our next three
26
+ * Friday night jazz shows at 8pm to an events page" of a site with only a
27
+ * Home page, and the plan made the three events and no page: they landed
28
+ * nowhere a visitor could see. So, settled in code where there is one right
29
+ * answer ({@link aiSettleBuildEventPage}), and re-asked where there is not
30
+ * ({@link aiBuildUnlistedEventsCheck}):
31
+ *
32
+ * 1. Events no section names, and a page the plan makes about them (its
33
+ * address or title says events): the events are named in that page's
34
+ * section about them, else its second section, else its first.
35
+ * 2. None such, the site has no page at `/events`, and the plan has room
36
+ * for one more page: the plan gains an Events page at `/events`, linked
37
+ * in the navigation and in the layout the other pages render in, whose
38
+ * one section lists them.
39
+ * 3. Anything else — a site that already has an events page, a plan at its
40
+ * page cap — is left to the plan's one re-ask, which names the events.
41
+ *
42
+ * Only the `event` operation: other placed operations keep their own rules.
43
+ */
44
+ /** The operation whose items this settles; the events-calendar plugin's, named, never imported. */
45
+ export declare const AI_BUILD_EVENT_OP = "event";
46
+ /**
47
+ * The page a build adds for its events. Its address is the SITE's page,
48
+ * spelled from its title so it is never read as the console's Events page.
49
+ */
50
+ export declare const AI_BUILD_EVENT_PAGE: {
51
+ readonly title: "Events";
52
+ readonly slug: `/${string}`;
53
+ readonly section: "Upcoming events";
54
+ };
55
+ /** The plan's event items that no page section names. */
56
+ export declare function aiBuildUnlistedEvents(plan: Pick<AiBuildPlan, 'items' | 'screens'>): AiBuildItem[];
57
+ export interface AiBuildEventPageContext {
58
+ /** The operations this site has; the settling runs only where it has `event`. */
59
+ ops: AiBuildOps | null;
60
+ /** The site's pages, by address. */
61
+ siteSlugs: readonly string[];
62
+ /** How many pages the build may plan. */
63
+ pageLimit: number;
64
+ /** The layout the site's pages render in (its home page's, else its one layout), or null. */
65
+ siteLayout: string | null;
66
+ }
67
+ /** A build plan with its unlisted events placed where there is one right answer; otherwise as it came. */
68
+ export declare function aiSettleBuildEventPage<T extends AiBuildPlan>(plan: T, context: AiBuildEventPageContext): T;
69
+ /** The re-ask for events the settling could not place: each named, and where to put it. */
70
+ export declare function aiBuildUnlistedEventsCheck(ops: AiBuildOps | null): (plan: AiBuildPlan) => AiDoctrineViolation[];
@@ -0,0 +1,145 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ import { _ as _extends } from "@swc/helpers/_/_extends";
17
+ import { AI_BUILD_PLAN_LIMITS, aiPlanSlugKey } from "./ai-build-plan.js";
18
+ /**
19
+ * THE PAGE A BUILD'S EVENTS ARE LISTED ON (AGL-3755).
20
+ *
21
+ * A build's `event` items are written as events, and an event is seen on the
22
+ * site only through an Event List a page section places — the section whose
23
+ * uses name the event. The live eval of 2026-10-13 asked "add our next three
24
+ * Friday night jazz shows at 8pm to an events page" of a site with only a
25
+ * Home page, and the plan made the three events and no page: they landed
26
+ * nowhere a visitor could see. So, settled in code where there is one right
27
+ * answer ({@link aiSettleBuildEventPage}), and re-asked where there is not
28
+ * ({@link aiBuildUnlistedEventsCheck}):
29
+ *
30
+ * 1. Events no section names, and a page the plan makes about them (its
31
+ * address or title says events): the events are named in that page's
32
+ * section about them, else its second section, else its first.
33
+ * 2. None such, the site has no page at `/events`, and the plan has room
34
+ * for one more page: the plan gains an Events page at `/events`, linked
35
+ * in the navigation and in the layout the other pages render in, whose
36
+ * one section lists them.
37
+ * 3. Anything else — a site that already has an events page, a plan at its
38
+ * page cap — is left to the plan's one re-ask, which names the events.
39
+ *
40
+ * Only the `event` operation: other placed operations keep their own rules.
41
+ */ /** The operation whose items this settles; the events-calendar plugin's, named, never imported. */ export const AI_BUILD_EVENT_OP = 'event';
42
+ const EVENT_PAGE_TITLE = 'Events';
43
+ /**
44
+ * The page a build adds for its events. Its address is the SITE's page,
45
+ * spelled from its title so it is never read as the console's Events page.
46
+ */ export const AI_BUILD_EVENT_PAGE = {
47
+ title: EVENT_PAGE_TITLE,
48
+ slug: `/${EVENT_PAGE_TITLE.toLowerCase()}`,
49
+ section: 'Upcoming events'
50
+ };
51
+ /** A page or section that is about events. */ const ABOUT_EVENTS = /\b(events?|calendar|upcoming|what'?s on|shows?|gigs?|schedule|classes|workshops?)\b/i;
52
+ const refOf = (item)=>`new:${item.name}`;
53
+ /** The plan's event items that no page section names. */ export function aiBuildUnlistedEvents(plan) {
54
+ var _plan_items;
55
+ const uses = new Set(plan.screens.flatMap((screen)=>screen.sections.flatMap((section)=>section.uses.map((ref)=>ref.trim().toLowerCase()))));
56
+ return ((_plan_items = plan.items) != null ? _plan_items : []).filter((item)=>item.op === AI_BUILD_EVENT_OP && !uses.has(refOf(item).trim().toLowerCase()));
57
+ }
58
+ /** Whether a planned page is about events: its address or its title says so. */ function aboutEvents(screen) {
59
+ var _screen_slug_trim_replace_split_;
60
+ const first = (_screen_slug_trim_replace_split_ = screen.slug.trim().replace(/^\/+/, '').split('/')[0]) != null ? _screen_slug_trim_replace_split_ : '';
61
+ return first !== '' && ABOUT_EVENTS.test(`${first.replace(/-/g, ' ')} ${screen.title}`);
62
+ }
63
+ /** The section of an events page that lists them: the one named for them, else its second, else its first. */ function listingSection(screen) {
64
+ const named = screen.sections.findIndex((section)=>ABOUT_EVENTS.test(section.name));
65
+ if (named !== -1) return named;
66
+ return screen.sections.length > 1 ? 1 : 0;
67
+ }
68
+ /** A build plan with its unlisted events placed where there is one right answer; otherwise as it came. */ export function aiSettleBuildEventPage(plan, context) {
69
+ var _ref;
70
+ var _context_ops, _plan_screens_find;
71
+ if (!((_context_ops = context.ops) == null ? void 0 : _context_ops.has(AI_BUILD_EVENT_OP))) return plan;
72
+ const unlisted = aiBuildUnlistedEvents(plan);
73
+ if (!unlisted.length) return plan;
74
+ const refs = unlisted.map(refOf);
75
+ // 1. A page the plan makes about them lists them.
76
+ const page = plan.screens.findIndex((screen)=>!screen.record && screen.sections.length && aboutEvents(screen));
77
+ if (page !== -1) {
78
+ const screen = plan.screens[page];
79
+ const at = listingSection(screen);
80
+ const section = screen.sections[at];
81
+ if (!section || section.uses.length + refs.length > AI_BUILD_PLAN_LIMITS.uses) return plan;
82
+ const sections = screen.sections.map((one, index)=>index === at ? _extends({}, one, {
83
+ uses: [
84
+ ...one.uses,
85
+ ...refs
86
+ ]
87
+ }) : one);
88
+ return _extends({}, plan, {
89
+ screens: plan.screens.map((one, index)=>index === page ? _extends({}, screen, {
90
+ sections
91
+ }) : one)
92
+ });
93
+ }
94
+ // 2. An Events page of its own, where the address is free and the plan has room.
95
+ const taken = new Set([
96
+ ...context.siteSlugs,
97
+ ...plan.screens.map((screen)=>screen.slug)
98
+ ].map(aiPlanSlugKey));
99
+ if (taken.has(aiPlanSlugKey(AI_BUILD_EVENT_PAGE.slug)) || plan.screens.length >= context.pageLimit) return plan;
100
+ if (refs.length > AI_BUILD_PLAN_LIMITS.uses) return plan;
101
+ const names = unlisted.map((item)=>item.name);
102
+ const listed = names.length === 1 ? names[0] : `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`;
103
+ const events = {
104
+ title: AI_BUILD_EVENT_PAGE.title,
105
+ slug: AI_BUILD_EVENT_PAGE.slug,
106
+ // The layout the plan's other pages render in, else the site's.
107
+ layout: (_ref = (_plan_screens_find = plan.screens.find((screen)=>screen.layout)) == null ? void 0 : _plan_screens_find.layout) != null ? _ref : context.siteLayout,
108
+ template: null,
109
+ duplicateOf: null,
110
+ nav: true,
111
+ seoTitle: AI_BUILD_EVENT_PAGE.title,
112
+ seoDescription: `Upcoming events: ${listed}.`.slice(0, AI_BUILD_PLAN_LIMITS.seoDescription),
113
+ sections: [
114
+ {
115
+ name: AI_BUILD_EVENT_PAGE.section,
116
+ uses: refs,
117
+ items: 0
118
+ }
119
+ ],
120
+ record: null
121
+ };
122
+ return _extends({}, plan, {
123
+ screens: [
124
+ ...plan.screens,
125
+ events
126
+ ]
127
+ });
128
+ }
129
+ /** The re-ask for events the settling could not place: each named, and where to put it. */ export function aiBuildUnlistedEventsCheck(ops) {
130
+ return (plan)=>{
131
+ if (!(ops == null ? void 0 : ops.has(AI_BUILD_EVENT_OP))) return [];
132
+ const unlisted = aiBuildUnlistedEvents(plan);
133
+ if (!unlisted.length) return [];
134
+ const named = unlisted.map((item)=>`new:${item.name}`).join(', ');
135
+ return [
136
+ {
137
+ rule: null,
138
+ code: 'plan-job-shape',
139
+ message: `The events ${named} are listed on no page, so no visitor would see them. Put each in the uses of the section that lists the events, on the page the request names for them or a page about events this plan makes.`
140
+ }
141
+ ];
142
+ };
143
+ }
144
+
145
+ //# sourceMappingURL=ai-build-event-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-build-event-page.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiDoctrineViolation } from '../runtime/ai-doctrine-validators'\nimport type { AiBuildOps } from './ai-build-job'\nimport {\n AI_BUILD_PLAN_LIMITS,\n aiPlanSlugKey,\n type AiBuildItem,\n type AiBuildPlan,\n type AiBuildPlanScreen,\n} from './ai-build-plan'\n\n/**\n * THE PAGE A BUILD'S EVENTS ARE LISTED ON (AGL-3755).\n *\n * A build's `event` items are written as events, and an event is seen on the\n * site only through an Event List a page section places — the section whose\n * uses name the event. The live eval of 2026-10-13 asked \"add our next three\n * Friday night jazz shows at 8pm to an events page\" of a site with only a\n * Home page, and the plan made the three events and no page: they landed\n * nowhere a visitor could see. So, settled in code where there is one right\n * answer ({@link aiSettleBuildEventPage}), and re-asked where there is not\n * ({@link aiBuildUnlistedEventsCheck}):\n *\n * 1. Events no section names, and a page the plan makes about them (its\n * address or title says events): the events are named in that page's\n * section about them, else its second section, else its first.\n * 2. None such, the site has no page at `/events`, and the plan has room\n * for one more page: the plan gains an Events page at `/events`, linked\n * in the navigation and in the layout the other pages render in, whose\n * one section lists them.\n * 3. Anything else — a site that already has an events page, a plan at its\n * page cap — is left to the plan's one re-ask, which names the events.\n *\n * Only the `event` operation: other placed operations keep their own rules.\n */\n\n/** The operation whose items this settles; the events-calendar plugin's, named, never imported. */\nexport const AI_BUILD_EVENT_OP = 'event'\n\nconst EVENT_PAGE_TITLE = 'Events'\n\n/**\n * The page a build adds for its events. Its address is the SITE's page,\n * spelled from its title so it is never read as the console's Events page.\n */\nexport const AI_BUILD_EVENT_PAGE = {\n title: EVENT_PAGE_TITLE,\n slug: `/${EVENT_PAGE_TITLE.toLowerCase()}`,\n section: 'Upcoming events',\n} as const\n\n/** A page or section that is about events. */\nconst ABOUT_EVENTS = /\\b(events?|calendar|upcoming|what'?s on|shows?|gigs?|schedule|classes|workshops?)\\b/i\n\nconst refOf = (item: Pick<AiBuildItem, 'name'>) => `new:${item.name}`\n\n/** The plan's event items that no page section names. */\nexport function aiBuildUnlistedEvents(plan: Pick<AiBuildPlan, 'items' | 'screens'>): AiBuildItem[] {\n const uses = new Set(\n plan.screens.flatMap((screen) => screen.sections.flatMap((section) => section.uses.map((ref) => ref.trim().toLowerCase()))),\n )\n return (plan.items ?? []).filter((item) => item.op === AI_BUILD_EVENT_OP && !uses.has(refOf(item).trim().toLowerCase()))\n}\n\n/** Whether a planned page is about events: its address or its title says so. */\nfunction aboutEvents(screen: Pick<AiBuildPlanScreen, 'slug' | 'title'>): boolean {\n const first = screen.slug.trim().replace(/^\\/+/, '').split('/')[0] ?? ''\n return first !== '' && ABOUT_EVENTS.test(`${first.replace(/-/g, ' ')} ${screen.title}`)\n}\n\n/** The section of an events page that lists them: the one named for them, else its second, else its first. */\nfunction listingSection(screen: AiBuildPlanScreen): number {\n const named = screen.sections.findIndex((section) => ABOUT_EVENTS.test(section.name))\n if (named !== -1) return named\n return screen.sections.length > 1 ? 1 : 0\n}\n\nexport interface AiBuildEventPageContext {\n /** The operations this site has; the settling runs only where it has `event`. */\n ops: AiBuildOps | null\n /** The site's pages, by address. */\n siteSlugs: readonly string[]\n /** How many pages the build may plan. */\n pageLimit: number\n /** The layout the site's pages render in (its home page's, else its one layout), or null. */\n siteLayout: string | null\n}\n\n/** A build plan with its unlisted events placed where there is one right answer; otherwise as it came. */\nexport function aiSettleBuildEventPage<T extends AiBuildPlan>(plan: T, context: AiBuildEventPageContext): T {\n if (!context.ops?.has(AI_BUILD_EVENT_OP)) return plan\n const unlisted = aiBuildUnlistedEvents(plan)\n if (!unlisted.length) return plan\n const refs = unlisted.map(refOf)\n // 1. A page the plan makes about them lists them.\n const page = plan.screens.findIndex((screen) => !screen.record && screen.sections.length && aboutEvents(screen))\n if (page !== -1) {\n const screen = plan.screens[page] as AiBuildPlanScreen\n const at = listingSection(screen)\n const section = screen.sections[at]\n if (!section || section.uses.length + refs.length > AI_BUILD_PLAN_LIMITS.uses) return plan\n const sections = screen.sections.map((one, index) => (index === at ? { ...one, uses: [...one.uses, ...refs] } : one))\n return { ...plan, screens: plan.screens.map((one, index) => (index === page ? { ...screen, sections } : one)) }\n }\n // 2. An Events page of its own, where the address is free and the plan has room.\n const taken = new Set([...context.siteSlugs, ...plan.screens.map((screen) => screen.slug)].map(aiPlanSlugKey))\n if (taken.has(aiPlanSlugKey(AI_BUILD_EVENT_PAGE.slug)) || plan.screens.length >= context.pageLimit) return plan\n if (refs.length > AI_BUILD_PLAN_LIMITS.uses) return plan\n const names = unlisted.map((item) => item.name)\n const listed = names.length === 1 ? names[0] : `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`\n const events: AiBuildPlanScreen = {\n title: AI_BUILD_EVENT_PAGE.title,\n slug: AI_BUILD_EVENT_PAGE.slug,\n // The layout the plan's other pages render in, else the site's.\n layout: plan.screens.find((screen) => screen.layout)?.layout ?? context.siteLayout,\n template: null,\n duplicateOf: null,\n nav: true,\n seoTitle: AI_BUILD_EVENT_PAGE.title,\n seoDescription: `Upcoming events: ${listed}.`.slice(0, AI_BUILD_PLAN_LIMITS.seoDescription),\n sections: [{ name: AI_BUILD_EVENT_PAGE.section, uses: refs, items: 0 }],\n record: null,\n }\n return { ...plan, screens: [...plan.screens, events] }\n}\n\n/** The re-ask for events the settling could not place: each named, and where to put it. */\nexport function aiBuildUnlistedEventsCheck(ops: AiBuildOps | null): (plan: AiBuildPlan) => AiDoctrineViolation[] {\n return (plan) => {\n if (!ops?.has(AI_BUILD_EVENT_OP)) return []\n const unlisted = aiBuildUnlistedEvents(plan)\n if (!unlisted.length) return []\n const named = unlisted.map((item) => `new:${item.name}`).join(', ')\n return [\n {\n rule: null,\n code: 'plan-job-shape',\n message: `The events ${named} are listed on no page, so no visitor would see them. Put each in the uses of the section that lists the events, on the page the request names for them or a page about events this plan makes.`,\n },\n ]\n }\n}\n"],"names":["AI_BUILD_PLAN_LIMITS","aiPlanSlugKey","AI_BUILD_EVENT_OP","EVENT_PAGE_TITLE","AI_BUILD_EVENT_PAGE","title","slug","toLowerCase","section","ABOUT_EVENTS","refOf","item","name","aiBuildUnlistedEvents","plan","uses","Set","screens","flatMap","screen","sections","map","ref","trim","items","filter","op","has","aboutEvents","first","replace","split","test","listingSection","named","findIndex","length","aiSettleBuildEventPage","context","ops","unlisted","refs","page","record","at","one","index","taken","siteSlugs","pageLimit","names","listed","slice","join","events","layout","find","siteLayout","template","duplicateOf","nav","seoTitle","seoDescription","aiBuildUnlistedEventsCheck","rule","code","message"],"mappings":"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"}
@@ -15,6 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  import { type PluginAiCapability } from '@aglyn/aglyn/plugin-manager/plugin-ai-capabilities';
18
+ import type { PluginDraftRecord } from '@aglyn/aglyn/plugin-manager/plugin-resource-drafts';
18
19
  import { type AiBuildItem, type AiBuildPlan, type AiBuildPlanCreate, type AiBuildPlanCreateKind, type AiBuildPlanScreen } from './ai-build-plan';
19
20
  import type { AiJobItemLedger } from './ai-jobs.types';
20
21
  import { type AiCreditRange, type AiCreditsSmaller } from './ai-credit-estimate';
@@ -59,6 +60,22 @@ export declare const AI_BUILD_CREATE_KINDS: readonly AiBuildPlanCreateKind[];
59
60
  * `items`: an item naming one is a plan that put it in the wrong list.
60
61
  */
61
62
  export declare const AI_BUILD_STRUCTURAL_OPS: readonly string[];
63
+ /**
64
+ * What a build hands an item it made before it (AGL-3728): its pages, by the
65
+ * title the plan gave each and the id it was written under, and its layouts,
66
+ * by name. An item's `new:<title>` destinations are read against it.
67
+ */
68
+ export declare const AI_BUILD_MADE_INPUT = "buildMade";
69
+ export interface AiBuildMade {
70
+ pages: Array<{
71
+ title: string;
72
+ id: string;
73
+ }>;
74
+ layouts: Array<{
75
+ name: string;
76
+ id: string;
77
+ }>;
78
+ }
62
79
  /** The job input a build's Assist proposal sets when the brief asked to publish. */
63
80
  export declare const AI_BUILD_PUBLISH_INPUT = "publish";
64
81
  /** A unit of a build: a creation, a page, or an item. */
@@ -75,6 +92,13 @@ export interface AiBuildUnit {
75
92
  }
76
93
  /** The name a `new:<name>` reference gives, lowercased. */
77
94
  export declare function aiBuildRefName(ref: string): string;
95
+ /**
96
+ * The `new:<name>` references an item's arguments make (AGL-3728): an
97
+ * argument that is one, or the destination after `=>` in an entry such as a
98
+ * menu item's `About => new:About`. Read from the arguments, never from the
99
+ * model's `dependsOn`, so what an item links is always built before it.
100
+ */
101
+ export declare function aiBuildItemArgRefs(args: Readonly<Record<string, unknown>> | null | undefined): string[];
78
102
  /** Every reference a page makes: its layout, template, starting page and what its sections place. */
79
103
  export declare function aiBuildScreenRefs(screen: AiBuildPlanScreen): string[];
80
104
  /**
@@ -112,6 +136,16 @@ export declare function aiBuildPlanShapeRefusal(plan: AiBuildPlan, options?: {
112
136
  freeTaste?: boolean;
113
137
  ops?: AiBuildOps;
114
138
  }): string | null;
139
+ /**
140
+ * What the person must still say before this plan can be built, as the
141
+ * questions its operations ask (`missingInput`), or `null` when nothing is
142
+ * missing: an item the planner kept with nothing it may build from — a
143
+ * tracking change naming no id, AGL-3751 — is asked about, never built
144
+ * empty or from a value the planner made up. Not a plan rule: a re-ask
145
+ * cannot supply what only the person knows, so the plan is not kept and
146
+ * the build answers with the question.
147
+ */
148
+ export declare function aiBuildPlanMissingInput(plan: AiBuildPlan, ops: AiBuildOps | null | undefined): string | null;
115
149
  /** "a booking service", "an email design". */
116
150
  export declare function aiArticle(noun: string): string;
117
151
  /**
@@ -217,6 +251,8 @@ export declare function aiBuildPlacedItemLines(unit: AiBuildUnit, context: {
217
251
  units: readonly AiBuildUnit[];
218
252
  ledger: readonly AiJobItemLedger[];
219
253
  ops?: AiBuildOps;
254
+ /** Operations whose items the platform places itself (a listing), so the page is told nothing of them. */
255
+ placedByCode?: readonly string[];
220
256
  }): string[];
221
257
  /** What a build came to: whether anything was delivered, and whether anything is left. */
222
258
  export declare function aiBuildSettlement(ledger: readonly AiJobItemLedger[]): {
@@ -242,3 +278,11 @@ export declare function aiBuildRetryLedger(ledger: readonly AiJobItemLedger[], u
242
278
  * page built after the layouts and forms the ledger holds.
243
279
  */
244
280
  export declare function aiLedgerUnits(ledger: readonly AiJobItemLedger[]): AiBuildUnit[];
281
+ /**
282
+ * What the job drawer says under an item another plugin's writer made: a
283
+ * draft, or, for setup its writer saves live (`live`, a CRM pipeline or
284
+ * field, AGL-3746), that it was saved. A writer that went live only when the
285
+ * member may (an event the publishing role publishes, AGL-3755) reports
286
+ * `live: false` for one it kept as a draft, with its `notice` saying why.
287
+ */
288
+ export declare function aiBuildItemOutputNote(capability: Pick<PluginAiCapability, 'where' | 'live'>, written?: Pick<PluginDraftRecord, 'live' | 'notice'>): string;
@@ -65,6 +65,11 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
65
65
  'page',
66
66
  ...AI_BUILD_CREATE_KINDS
67
67
  ];
68
+ /**
69
+ * What a build hands an item it made before it (AGL-3728): its pages, by the
70
+ * title the plan gave each and the id it was written under, and its layouts,
71
+ * by name. An item's `new:<title>` destinations are read against it.
72
+ */ export const AI_BUILD_MADE_INPUT = 'buildMade';
68
73
  /** The job input a build's Assist proposal sets when the brief asked to publish. */ export const AI_BUILD_PUBLISH_INPUT = 'publish';
69
74
  /** The operation a creation is built as. */ function creationOp(creation) {
70
75
  return creation.kind;
@@ -81,6 +86,30 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
81
86
  /** The name a `new:<name>` reference gives, lowercased. */ export function aiBuildRefName(ref) {
82
87
  return ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim().toLowerCase();
83
88
  }
89
+ /**
90
+ * The `new:<name>` references an item's arguments make (AGL-3728): an
91
+ * argument that is one, or the destination after `=>` in an entry such as a
92
+ * menu item's `About => new:About`. Read from the arguments, never from the
93
+ * model's `dependsOn`, so what an item links is always built before it.
94
+ */ export function aiBuildItemArgRefs(args) {
95
+ const refs = [];
96
+ const take = (value)=>{
97
+ var _value_split_slice_;
98
+ if (typeof value !== 'string') return;
99
+ for (const part of [
100
+ value,
101
+ (_value_split_slice_ = value.split('=>').slice(-1)[0]) != null ? _value_split_slice_ : ''
102
+ ]){
103
+ const ref = part.trim();
104
+ if (isAiPlanNewRef(ref) && !refs.includes(ref)) refs.push(ref);
105
+ }
106
+ };
107
+ for (const value of Object.values(args != null ? args : {})){
108
+ if (Array.isArray(value)) value.forEach(take);
109
+ else take(value);
110
+ }
111
+ return refs;
112
+ }
84
113
  /** Every reference a page makes: its layout, template, starting page and what its sections place. */ export function aiBuildScreenRefs(screen) {
85
114
  return [
86
115
  screen.layout,
@@ -97,11 +126,19 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
97
126
  */ export function aiBuildUnits(plan) {
98
127
  var _plan_items;
99
128
  const slots = namedSlots(plan);
100
- const depsOf = (refs, self)=>{
129
+ // A page, by the title the plan gave it: what an item's arguments may link
130
+ // as `new:<title>` (a menu item, AGL-3728), built before the item.
131
+ const pageSlots = new Map();
132
+ plan.screens.forEach((screen, index)=>{
133
+ const title = screen.title.trim().toLowerCase();
134
+ if (title && !pageSlots.has(title)) pageSlots.set(title, `p${index}`);
135
+ });
136
+ const depsOf = (refs, self, pages = false)=>{
101
137
  const deps = [];
102
138
  for (const ref of refs){
139
+ var _slots_get;
103
140
  if (!isAiPlanNewRef(ref)) continue;
104
- const slot = slots.get(aiBuildRefName(ref));
141
+ const slot = (_slots_get = slots.get(aiBuildRefName(ref))) != null ? _slots_get : pages ? pageSlots.get(aiBuildRefName(ref)) : undefined;
105
142
  if (slot && slot !== self && !deps.includes(slot)) deps.push(slot);
106
143
  }
107
144
  return deps;
@@ -129,7 +166,11 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
129
166
  op: item.op,
130
167
  label: item.name,
131
168
  item,
132
- deps: depsOf(item.dependsOn, item.slot)
169
+ // What it depends on, and what its arguments link as `new:` (AGL-3728).
170
+ deps: depsOf([
171
+ ...item.dependsOn,
172
+ ...aiBuildItemArgRefs(item.args)
173
+ ], item.slot, true)
133
174
  });
134
175
  }
135
176
  plan.screens.forEach((screen, index)=>{
@@ -259,6 +300,29 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
259
300
  }
260
301
  return null;
261
302
  }
303
+ /**
304
+ * What the person must still say before this plan can be built, as the
305
+ * questions its operations ask (`missingInput`), or `null` when nothing is
306
+ * missing: an item the planner kept with nothing it may build from — a
307
+ * tracking change naming no id, AGL-3751 — is asked about, never built
308
+ * empty or from a value the planner made up. Not a plan rule: a re-ask
309
+ * cannot supply what only the person knows, so the plan is not kept and
310
+ * the build answers with the question.
311
+ */ export function aiBuildPlanMissingInput(plan, ops) {
312
+ var _plan_items;
313
+ if (!ops) return null;
314
+ const questions = [];
315
+ for (const item of (_plan_items = plan.items) != null ? _plan_items : []){
316
+ var _ops_get_missingInput, _ops_get;
317
+ const question = (_ops_get = ops.get(item.op)) == null ? void 0 : (_ops_get_missingInput = _ops_get.missingInput) == null ? void 0 : _ops_get_missingInput.call(_ops_get, {
318
+ name: item.name,
319
+ why: item.why,
320
+ args: item.args
321
+ });
322
+ if (question && !questions.includes(question)) questions.push(question);
323
+ }
324
+ return questions.length ? questions.join(' ') : null;
325
+ }
262
326
  /** "a booking service", "an email design". */ export function aiArticle(noun) {
263
327
  return `${/^[aeiou]/i.test(noun) ? 'an' : 'a'} ${noun}`;
264
328
  }
@@ -477,9 +541,21 @@ const OPEN = [
477
541
  continue;
478
542
  }
479
543
  if (unit.screen) {
480
- var _capability_pageBlocks;
544
+ var _capability_pageBlocks, _capability_pageBlocks1;
481
545
  const section = unit.screen.sections.find((one)=>one.uses.some((ref)=>isAiPlanNewRef(ref) && aiBuildRefName(ref) === dep.label.toLowerCase()));
482
- const blocks = (capability == null ? void 0 : (_capability_pageBlocks = capability.pageBlocks) == null ? void 0 : _capability_pageBlocks.length) ? ` the ${capability.pageBlocks.join(' or ')} block` : '';
546
+ // Another item this section places with the same block was built (one
547
+ // of several events, AGL-3743): the block stays, and only this item is
548
+ // missing from what it shows.
549
+ const blockStays = Boolean(section && (capability == null ? void 0 : (_capability_pageBlocks = capability.pageBlocks) == null ? void 0 : _capability_pageBlocks.length) && aiBuildSectionDeliveredBlocks(section, _extends({}, context, {
550
+ rows,
551
+ except: slot
552
+ })).has(aiBuildBlocksKey(capability.pageBlocks)));
553
+ if (blockStays) {
554
+ result.notes.push(`Built without the ${noun} “${dep.label}”: it could not be created.`);
555
+ result.briefLines.push(`Leave the ${noun} “${dep.label}” out of the “${section == null ? void 0 : section.name}” section: it could not be created.`);
556
+ continue;
557
+ }
558
+ const blocks = (capability == null ? void 0 : (_capability_pageBlocks1 = capability.pageBlocks) == null ? void 0 : _capability_pageBlocks1.length) ? ` the ${capability.pageBlocks.join(' or ')} block` : '';
483
559
  result.notes.push(`Built without${blocks || ` the ${noun}`}: the ${noun} “${dep.label}” could not be created.`);
484
560
  result.briefLines.push(`${section ? `Build the “${section.name}” section` : 'Build the page'} without the ${noun} “${dep.label}”${blocks ? ` and without${blocks}` : ''}: it could not be created.`);
485
561
  } else {
@@ -507,22 +583,60 @@ const OPEN = [
507
583
  ]));
508
584
  const lines = [];
509
585
  for (const section of unit.screen.sections){
510
- for (const ref of section.uses){
511
- var _ref, _ref1;
512
- var _context_ops;
513
- if (!isAiPlanNewRef(ref)) continue;
514
- const dep = context.units.find((one)=>one.item && one.item.name.toLowerCase() === aiBuildRefName(ref));
515
- if (!(dep == null ? void 0 : dep.item)) continue;
586
+ // One line per block a section places, naming every built item it shows:
587
+ // a section listing four events places the Event List once (AGL-3743).
588
+ const placed = new Map();
589
+ for (const { dep, capability } of aiBuildSectionItems(section, context.units, context.ops)){
590
+ var _ref, _placed_get, _ref1;
591
+ var _context_placedByCode;
516
592
  const row = rows.get(dep.slot);
517
593
  if (!row || !aiBuildItemDelivered(row)) continue;
518
- const capability = (_context_ops = context.ops) == null ? void 0 : _context_ops.get(dep.op);
594
+ if ((_context_placedByCode = context.placedByCode) == null ? void 0 : _context_placedByCode.includes(dep.op)) continue;
519
595
  const blocks = (_ref = capability == null ? void 0 : capability.pageBlocks) != null ? _ref : [];
520
596
  if (!blocks.length) continue;
521
- lines.push(`In the “${section.name}” section, place the ${blocks.join(' or ')} block for the ${(_ref1 = capability == null ? void 0 : capability.noun) != null ? _ref1 : dep.op} “${dep.label}”.`);
597
+ const key = aiBuildBlocksKey(blocks);
598
+ const entry = (_placed_get = placed.get(key)) != null ? _placed_get : {
599
+ blocks,
600
+ noun: (_ref1 = capability == null ? void 0 : capability.noun) != null ? _ref1 : dep.op,
601
+ labels: []
602
+ };
603
+ entry.labels.push(`“${dep.label}”`);
604
+ placed.set(key, entry);
605
+ }
606
+ for (const { blocks, noun, labels } of placed.values()){
607
+ const named = labels.length === 1 ? `the ${noun} ${labels[0]}` : `the ${noun}s ${labels.slice(0, -1).join(', ')} and ${labels[labels.length - 1]}`;
608
+ lines.push(`In the “${section.name}” section, place the ${blocks.join(' or ')} block for ${named}.`);
522
609
  }
523
610
  }
524
611
  return lines;
525
612
  }
613
+ /** The items a page section places, each with its capability, in the order its uses name them. */ function aiBuildSectionItems(section, units, ops) {
614
+ const found = [];
615
+ for (const ref of section.uses){
616
+ if (!isAiPlanNewRef(ref)) continue;
617
+ const dep = units.find((one)=>one.item && one.item.name.toLowerCase() === aiBuildRefName(ref));
618
+ if (dep == null ? void 0 : dep.item) found.push({
619
+ dep,
620
+ capability: ops == null ? void 0 : ops.get(dep.op)
621
+ });
622
+ }
623
+ return found;
624
+ }
625
+ /** The blocks a section's built items place, each set as one key; `except` leaves one item out. */ function aiBuildSectionDeliveredBlocks(section, context) {
626
+ const keys = new Set();
627
+ for (const { dep, capability } of aiBuildSectionItems(section, context.units, context.ops)){
628
+ var _capability_pageBlocks;
629
+ if (dep.slot === context.except || !(capability == null ? void 0 : (_capability_pageBlocks = capability.pageBlocks) == null ? void 0 : _capability_pageBlocks.length)) continue;
630
+ const row = context.rows.get(dep.slot);
631
+ if (row && aiBuildItemDelivered(row)) keys.add(aiBuildBlocksKey(capability.pageBlocks));
632
+ }
633
+ return keys;
634
+ }
635
+ function aiBuildBlocksKey(blocks) {
636
+ return [
637
+ ...blocks
638
+ ].sort().join('|');
639
+ }
526
640
  /** What a build came to: whether anything was delivered, and whether anything is left. */ export function aiBuildSettlement(ledger) {
527
641
  return {
528
642
  delivered: ledger.some(aiBuildItemDelivered),
@@ -602,5 +716,19 @@ const OPEN = [
602
716
  deps: row.op === 'page' ? creations : []
603
717
  }));
604
718
  }
719
+ /**
720
+ * What the job drawer says under an item another plugin's writer made: a
721
+ * draft, or, for setup its writer saves live (`live`, a CRM pipeline or
722
+ * field, AGL-3746), that it was saved. A writer that went live only when the
723
+ * member may (an event the publishing role publishes, AGL-3755) reports
724
+ * `live: false` for one it kept as a draft, with its `notice` saying why.
725
+ */ export function aiBuildItemOutputNote(capability, written) {
726
+ var _ref;
727
+ var _written_notice;
728
+ const live = (_ref = written == null ? void 0 : written.live) != null ? _ref : capability.live;
729
+ const said = live ? `Live now, in ${capability.where}.` : `A draft, in ${capability.where}.`;
730
+ const notice = written == null ? void 0 : (_written_notice = written.notice) == null ? void 0 : _written_notice.trim();
731
+ return notice ? `${said} ${notice}` : said;
732
+ }
605
733
 
606
734
  //# sourceMappingURL=ai-build-job.js.map