@aglyn/plugins-ai 1.0.0-beta.246 → 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 (127) hide show
  1. package/package.json +17 -17
  2. package/src/lib/activity/ai-activity-actions.js +5 -1
  3. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  4. package/src/lib/components/ai-job-links.js +14 -0
  5. package/src/lib/components/ai-job-links.js.map +1 -1
  6. package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
  7. package/src/lib/components/ai-site-start-card.component.js +92 -19
  8. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  9. package/src/lib/declarations.server.js +12 -0
  10. package/src/lib/declarations.server.js.map +1 -1
  11. package/src/lib/jobs/ai-build-capabilities.js +56 -2
  12. package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
  13. package/src/lib/jobs/ai-job-build-step.d.ts +3 -8
  14. package/src/lib/jobs/ai-job-build-step.js +49 -12
  15. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  16. package/src/lib/jobs/ai-job-form-dataset.d.ts +8 -5
  17. package/src/lib/jobs/ai-job-form-dataset.js +28 -10
  18. package/src/lib/jobs/ai-job-form-dataset.js.map +1 -1
  19. package/src/lib/jobs/ai-job-form-step.d.ts +20 -4
  20. package/src/lib/jobs/ai-job-form-step.js +98 -28
  21. package/src/lib/jobs/ai-job-form-step.js.map +1 -1
  22. package/src/lib/jobs/ai-job-layout-language.d.ts +6 -2
  23. package/src/lib/jobs/ai-job-layout-language.js +16 -5
  24. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  25. package/src/lib/jobs/ai-job-layout-step.js +4 -1
  26. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  27. package/src/lib/jobs/ai-job-media-step.d.ts +239 -0
  28. package/src/lib/jobs/ai-job-media-step.js +610 -0
  29. package/src/lib/jobs/ai-job-media-step.js.map +1 -0
  30. package/src/lib/jobs/ai-job-page-language.d.ts +2 -0
  31. package/src/lib/jobs/ai-job-page-language.js +9 -0
  32. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  33. package/src/lib/jobs/ai-job-page-sections.js +4 -3
  34. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  35. package/src/lib/jobs/ai-job-page-step.js +3 -2
  36. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  37. package/src/lib/jobs/ai-job-plan-step.d.ts +10 -1
  38. package/src/lib/jobs/ai-job-plan-step.js +73 -15
  39. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  40. package/src/lib/jobs/ai-job-settings-step.d.ts +0 -4
  41. package/src/lib/jobs/ai-job-settings-step.js +2 -7
  42. package/src/lib/jobs/ai-job-settings-step.js.map +1 -1
  43. package/src/lib/jobs/ai-job-site-content.d.ts +2 -2
  44. package/src/lib/jobs/ai-job-site-content.js +5 -3
  45. package/src/lib/jobs/ai-job-site-content.js.map +1 -1
  46. package/src/lib/jobs/ai-job-site-datasets.d.ts +1 -1
  47. package/src/lib/jobs/ai-job-site-datasets.js +3 -2
  48. package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
  49. package/src/lib/jobs/ai-job-site-events.d.ts +148 -0
  50. package/src/lib/jobs/ai-job-site-events.js +313 -0
  51. package/src/lib/jobs/ai-job-site-events.js.map +1 -0
  52. package/src/lib/jobs/ai-job-site-step.d.ts +9 -3
  53. package/src/lib/jobs/ai-job-site-step.js +63 -17
  54. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  55. package/src/lib/jobs/ai-job-tracking-step.d.ts +184 -0
  56. package/src/lib/jobs/ai-job-tracking-step.js +501 -0
  57. package/src/lib/jobs/ai-job-tracking-step.js.map +1 -0
  58. package/src/lib/jobs/ai-layout-stock-photos.d.ts +21 -0
  59. package/src/lib/jobs/ai-layout-stock-photos.js +41 -4
  60. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  61. package/src/lib/jobs/ai-site-publish.js +2 -3
  62. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  63. package/src/lib/jobs/ai-stock-photo-picker.d.ts +24 -0
  64. package/src/lib/jobs/ai-stock-photo-picker.js +108 -0
  65. package/src/lib/jobs/ai-stock-photo-picker.js.map +1 -0
  66. package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.d.ts +37 -0
  67. package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.js +47 -0
  68. package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.js.map +1 -0
  69. package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.d.ts +21 -0
  70. package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.js +128 -0
  71. package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.js.map +1 -0
  72. package/src/lib/layout-language/ai-layout-compiler.d.ts +10 -4
  73. package/src/lib/layout-language/ai-layout-compiler.js +38 -19
  74. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  75. package/src/lib/layout-language/ai-layout-frame.d.ts +7 -5
  76. package/src/lib/layout-language/ai-layout-frame.js +12 -9
  77. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  78. package/src/lib/layout-language/ai-layout-gaps.js +3 -1
  79. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
  80. package/src/lib/layout-language/ai-layout-labels.d.ts +70 -0
  81. package/src/lib/layout-language/ai-layout-labels.js +162 -0
  82. package/src/lib/layout-language/ai-layout-labels.js.map +1 -0
  83. package/src/lib/layout-language/ai-layout-links.js +5 -2
  84. package/src/lib/layout-language/ai-layout-links.js.map +1 -1
  85. package/src/lib/layout-language/ai-layout-listings.d.ts +14 -1
  86. package/src/lib/layout-language/ai-layout-listings.js +32 -5
  87. package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
  88. package/src/lib/model/ai-build-event-page.d.ts +70 -0
  89. package/src/lib/model/ai-build-event-page.js +145 -0
  90. package/src/lib/model/ai-build-event-page.js.map +1 -0
  91. package/src/lib/model/ai-build-job.d.ts +21 -0
  92. package/src/lib/model/ai-build-job.js +39 -0
  93. package/src/lib/model/ai-build-job.js.map +1 -1
  94. package/src/lib/model/ai-build-plan.d.ts +25 -14
  95. package/src/lib/model/ai-build-plan.js +33 -23
  96. package/src/lib/model/ai-build-plan.js.map +1 -1
  97. package/src/lib/model/ai-job-activity.js +3 -1
  98. package/src/lib/model/ai-job-activity.js.map +1 -1
  99. package/src/lib/model/ai-jobs.types.d.ts +10 -2
  100. package/src/lib/model/ai-jobs.types.js +3 -1
  101. package/src/lib/model/ai-jobs.types.js.map +1 -1
  102. package/src/lib/model/ai-plan-capabilities.d.ts +8 -0
  103. package/src/lib/model/ai-plan-capabilities.js.map +1 -1
  104. package/src/lib/model/ai-site-inventory.d.ts +2 -0
  105. package/src/lib/model/ai-site-inventory.js +5 -0
  106. package/src/lib/model/ai-site-inventory.js.map +1 -1
  107. package/src/lib/model/ai-site-job.d.ts +2 -2
  108. package/src/lib/model/ai-site-job.js +3 -3
  109. package/src/lib/model/ai-site-job.js.map +1 -1
  110. package/src/lib/runtime/ai-doctrine-validators.d.ts +25 -3
  111. package/src/lib/runtime/ai-doctrine-validators.js +53 -6
  112. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  113. package/src/lib/runtime/ai-doctrine.d.ts +6 -1
  114. package/src/lib/runtime/ai-doctrine.js +13 -9
  115. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  116. package/src/lib/runtime/ai-eval-live.d.ts +1 -1
  117. package/src/lib/runtime/ai-eval-live.js +1 -1
  118. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  119. package/src/lib/runtime/ai-palette.d.ts +3 -1
  120. package/src/lib/runtime/ai-palette.generated.js +498 -10
  121. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  122. package/src/lib/runtime/ai-palette.js +5 -3
  123. package/src/lib/runtime/ai-palette.js.map +1 -1
  124. package/src/lib/runtime/ai-template-examples.js +8 -2
  125. package/src/lib/runtime/ai-template-examples.js.map +1 -1
  126. package/src/lib/server.js +8 -0
  127. package/src/lib/server.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';
@@ -135,6 +136,16 @@ export declare function aiBuildPlanShapeRefusal(plan: AiBuildPlan, options?: {
135
136
  freeTaste?: boolean;
136
137
  ops?: AiBuildOps;
137
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;
138
149
  /** "a booking service", "an email design". */
139
150
  export declare function aiArticle(noun: string): string;
140
151
  /**
@@ -240,6 +251,8 @@ export declare function aiBuildPlacedItemLines(unit: AiBuildUnit, context: {
240
251
  units: readonly AiBuildUnit[];
241
252
  ledger: readonly AiJobItemLedger[];
242
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[];
243
256
  }): string[];
244
257
  /** What a build came to: whether anything was delivered, and whether anything is left. */
245
258
  export declare function aiBuildSettlement(ledger: readonly AiJobItemLedger[]): {
@@ -265,3 +278,11 @@ export declare function aiBuildRetryLedger(ledger: readonly AiJobItemLedger[], u
265
278
  * page built after the layouts and forms the ledger holds.
266
279
  */
267
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;
@@ -300,6 +300,29 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
300
300
  }
301
301
  return null;
302
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
+ }
303
326
  /** "a booking service", "an email design". */ export function aiArticle(noun) {
304
327
  return `${/^[aeiou]/i.test(noun) ? 'an' : 'a'} ${noun}`;
305
328
  }
@@ -565,8 +588,10 @@ const OPEN = [
565
588
  const placed = new Map();
566
589
  for (const { dep, capability } of aiBuildSectionItems(section, context.units, context.ops)){
567
590
  var _ref, _placed_get, _ref1;
591
+ var _context_placedByCode;
568
592
  const row = rows.get(dep.slot);
569
593
  if (!row || !aiBuildItemDelivered(row)) continue;
594
+ if ((_context_placedByCode = context.placedByCode) == null ? void 0 : _context_placedByCode.includes(dep.op)) continue;
570
595
  const blocks = (_ref = capability == null ? void 0 : capability.pageBlocks) != null ? _ref : [];
571
596
  if (!blocks.length) continue;
572
597
  const key = aiBuildBlocksKey(blocks);
@@ -691,5 +716,19 @@ function aiBuildBlocksKey(blocks) {
691
716
  deps: row.op === 'page' ? creations : []
692
717
  }));
693
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
+ }
694
733
 
695
734
  //# sourceMappingURL=ai-build-job.js.map