@aglyn/plugins-ai 1.0.0-beta.238 → 1.0.0-beta.240

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 (88) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-job-links.js +8 -0
  3. package/src/lib/components/ai-job-links.js.map +1 -1
  4. package/src/lib/components/ai-job-requests.d.ts +5 -0
  5. package/src/lib/components/ai-job-requests.js +4 -2
  6. package/src/lib/components/ai-job-requests.js.map +1 -1
  7. package/src/lib/components/ai-jobs-store.d.ts +12 -0
  8. package/src/lib/components/ai-jobs-store.js +15 -0
  9. package/src/lib/components/ai-jobs-store.js.map +1 -1
  10. package/src/lib/components/ai-usage-meter-refresh.d.ts +35 -0
  11. package/src/lib/components/ai-usage-meter-refresh.js +63 -0
  12. package/src/lib/components/ai-usage-meter-refresh.js.map +1 -0
  13. package/src/lib/components/assist-panel.component.js +17 -1
  14. package/src/lib/components/assist-panel.component.js.map +1 -1
  15. package/src/lib/components/use-ai-usage-meter.d.ts +2 -0
  16. package/src/lib/components/use-ai-usage-meter.js +8 -3
  17. package/src/lib/components/use-ai-usage-meter.js.map +1 -1
  18. package/src/lib/jobs/ai-build-unit-outcome.d.ts +3 -1
  19. package/src/lib/jobs/ai-build-unit-outcome.js +19 -3
  20. package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
  21. package/src/lib/jobs/ai-job-build-step.js +29 -2
  22. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  23. package/src/lib/jobs/ai-job-draft-ids.js +7 -2
  24. package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
  25. package/src/lib/jobs/ai-job-form-dataset.d.ts +100 -0
  26. package/src/lib/jobs/ai-job-form-dataset.js +161 -0
  27. package/src/lib/jobs/ai-job-form-dataset.js.map +1 -0
  28. package/src/lib/jobs/ai-job-form-step.d.ts +15 -3
  29. package/src/lib/jobs/ai-job-form-step.js +37 -9
  30. package/src/lib/jobs/ai-job-form-step.js.map +1 -1
  31. package/src/lib/jobs/ai-job-layout-language.js +7 -2
  32. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  33. package/src/lib/jobs/ai-job-page-language.d.ts +9 -0
  34. package/src/lib/jobs/ai-job-page-language.js +44 -4
  35. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  36. package/src/lib/jobs/ai-job-page-sections.js +13 -2
  37. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  38. package/src/lib/jobs/ai-job-page-step.js +32 -2
  39. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  40. package/src/lib/jobs/ai-job-plan-step.d.ts +18 -0
  41. package/src/lib/jobs/ai-job-plan-step.js +41 -4
  42. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  43. package/src/lib/jobs/ai-job-site-datasets.d.ts +183 -0
  44. package/src/lib/jobs/ai-job-site-datasets.js +530 -0
  45. package/src/lib/jobs/ai-job-site-datasets.js.map +1 -0
  46. package/src/lib/jobs/ai-job-site-step.d.ts +15 -8
  47. package/src/lib/jobs/ai-job-site-step.js +148 -29
  48. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  49. package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.d.ts +30 -0
  50. package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js +637 -0
  51. package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js.map +1 -0
  52. package/src/lib/layout-language/ai-layout-compiler.d.ts +10 -0
  53. package/src/lib/layout-language/ai-layout-compiler.js +93 -1
  54. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  55. package/src/lib/layout-language/ai-layout-gaps.js +17 -1
  56. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
  57. package/src/lib/layout-language/ai-layout-listings.d.ts +32 -3
  58. package/src/lib/layout-language/ai-layout-listings.js +62 -9
  59. package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
  60. package/src/lib/layout-language/ai-layout-pictures.js +12 -4
  61. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  62. package/src/lib/model/ai-build-job.d.ts +2 -0
  63. package/src/lib/model/ai-build-job.js +16 -1
  64. package/src/lib/model/ai-build-job.js.map +1 -1
  65. package/src/lib/model/ai-build-plan.d.ts +23 -3
  66. package/src/lib/model/ai-build-plan.js +47 -6
  67. package/src/lib/model/ai-build-plan.js.map +1 -1
  68. package/src/lib/model/ai-plan-capabilities.d.ts +7 -0
  69. package/src/lib/model/ai-plan-capabilities.js.map +1 -1
  70. package/src/lib/model/ai-site-job.d.ts +19 -2
  71. package/src/lib/model/ai-site-job.js +21 -6
  72. package/src/lib/model/ai-site-job.js.map +1 -1
  73. package/src/lib/runtime/ai-dataset-generation.d.ts +77 -0
  74. package/src/lib/runtime/ai-dataset-generation.js +127 -0
  75. package/src/lib/runtime/ai-dataset-generation.js.map +1 -0
  76. package/src/lib/runtime/ai-doctrine-validators.d.ts +84 -0
  77. package/src/lib/runtime/ai-doctrine-validators.js +307 -1
  78. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  79. package/src/lib/runtime/ai-doctrine.js +10 -4
  80. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  81. package/src/lib/runtime/ai-node-tree.d.ts +2 -0
  82. package/src/lib/runtime/ai-node-tree.js +16 -0
  83. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  84. package/src/lib/tools/ai-dataset-tool.d.ts +68 -0
  85. package/src/lib/tools/ai-dataset-tool.js +231 -0
  86. package/src/lib/tools/ai-dataset-tool.js.map +1 -0
  87. package/src/lib/usage/free-ai-credits-left.js +26 -2
  88. package/src/lib/usage/free-ai-credits-left.js.map +1 -1
@@ -0,0 +1,530 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { checkDatasetQuota } from "@aglyn/aglyn/app-utils/plan-entitlements";
18
+ import { pluginResourceDraftWriter } from "@aglyn/aglyn/plugin-manager/plugin-resource-drafts";
19
+ import { isAiPlanNewRef } from "../model/ai-build-plan.js";
20
+ import { AI_SITE_DATASETS_MAX } from "../model/ai-site-job.js";
21
+ import { AI_STEP_TIERS } from "../providers/catalog.js";
22
+ import { aiModelForStep } from "../providers/routing.js";
23
+ import { AI_DATASET_MAX_TOKENS, AI_DATASET_STEP, aiDatasetDraftContent, generateAiDataset } from "../runtime/ai-dataset-generation.js";
24
+ import { aiLayoutListingId, aiLayoutRecordsPlacements } from "../layout-language/ai-layout-listings.js";
25
+ import { AI_DATASET_DRAFT_RESOURCE } from "./ai-build-unit-outcome.js";
26
+ import { aiJobStepBudget } from "./ai-job-budget.js";
27
+ import { aiGenerationSpent, aiUnspentOutcome } from "./ai-job-generation.js";
28
+ import { aiPluginDraftAdmissionRefusal } from "./ai-job-plugin-drafts.js";
29
+ /**
30
+ * A SITE START'S DATASETS (AGL-3616): "Aglyn AI can also use/create datasets
31
+ * too for whatever it needs" (Zach, 2026-10-10).
32
+ *
33
+ * A site whose content is a list of like things — a restaurant's menu, a
34
+ * studio's team, a contractor's services, a photographer's portfolio, an
35
+ * events list, its questions and answers — keeps it as a DATASET, the way the
36
+ * docs teach a person to: the owner edits one record and every page listing
37
+ * it follows. On a workspace that may create datasets, the site plan creates
38
+ * one for such content (`create`, kind `dataset`) and names it in the `uses`
39
+ * of each section that lists it. The scaffold then builds each dataset as a
40
+ * unit of its own, after the layout and before the form (which may write
41
+ * its submissions to one) and the pages:
42
+ *
43
+ * - DESIGNED by the `submit_dataset` generation (`ai-dataset-generation.ts`):
44
+ * the planned fields typed, at most a few more, and 3 to 12 records seeded
45
+ * from the brief. Nothing anybody did not say: no review, testimonial,
46
+ * rating, quote or person's name, no price the brief does not state.
47
+ * - WRITTEN by the data plugin's `dataset` writer — this plugin never names
48
+ * the datasets collection — with every rule the console's datasets route
49
+ * has: the member's role and `data.manage`, `release_data_store`,
50
+ * `dataStore` (Starter and up), `datasetsPerOrg` counted inside the create,
51
+ * `recordsPerDataset` and the storage band.
52
+ * - LISTED by the pages built after it: each section naming it repeats over
53
+ * the dataset (`aiSiteDatasetListings`, a `records` listing the layout
54
+ * compiler draws), and a page the plan made its record template binds its
55
+ * fields by id and keeps the address field the writer added.
56
+ *
57
+ * Where datasets cannot be created — the Free taste, Starter's two used up,
58
+ * the data store not released — the plan is told so and creates none, so its
59
+ * sections write the items out. A dataset unit that fails or is refused
60
+ * leaves the pages that list it built without it (they say so), and its
61
+ * record page is not built.
62
+ */ /** The most datasets one site start creates. */ export { AI_SITE_DATASETS_MAX } from "../model/ai-site-job.js";
63
+ /** The resource the data plugin's writer is registered under. */ export const AI_SITE_DATASET_RESOURCE = AI_DATASET_DRAFT_RESOURCE;
64
+ /** What the site's plugin list calls the plugin, for "Turn on Data for this site". */ const DATA_PLUGIN_LABEL = 'Data';
65
+ /** The input a dataset unit's own job carries what it needs in. */ export const AI_SITE_DATASET_INPUT = 'siteDataset';
66
+ /** What a dataset row says when its design could not be written: our failure, refunded. */ export const AI_SITE_DATASET_NOT_WRITTEN_COPY = 'This dataset could not be added this time.';
67
+ /** The note a written dataset carries on the job's page. */ export function aiSiteDatasetNote(records) {
68
+ return `${records} ${records === 1 ? 'record' : 'records'} to start, in Data. Edit them there and every page that lists them follows.`;
69
+ }
70
+ /** The note a record template's row adds: the binding is the member's to save (rule 13). */ export const AI_SITE_RECORD_PAGE_NOTE = 'A page per record is drafted, not live: open Page Properties → Record pages to save it.';
71
+ /** ASSUMED: what one dataset pass reads and writes beside its generation: the site, the writer's reads and its transaction. */ export const AI_SITE_DATASET_WRITES_MS = 2000;
72
+ /** One dataset's pass: its generation and re-ask at its ceiling, and its writes. */ export const AI_SITE_DATASET_BUDGET = aiJobStepBudget({
73
+ tier: AI_STEP_TIERS[AI_DATASET_STEP],
74
+ maxTokens: AI_DATASET_MAX_TOKENS,
75
+ lookups: 0,
76
+ ownReadsMs: AI_SITE_DATASET_WRITES_MS
77
+ });
78
+ const str = (value)=>typeof value === 'string' ? value.trim() : '';
79
+ /** The plan's reference to a dataset as a section names it: `new:<name>`, compared lowercased. */ function namesDataset(ref, name) {
80
+ return isAiPlanNewRef(ref) && ref.slice(ref.indexOf(':') + 1).trim().toLowerCase() === name.trim().toLowerCase();
81
+ }
82
+ /** The plan's datasets, in plan order, at most the most a start creates. */ export function aiSitePlanDatasets(plan) {
83
+ return plan.create.filter((entry)=>entry.kind === 'dataset').slice(0, AI_SITE_DATASETS_MAX);
84
+ }
85
+ /** Where the plan lists a dataset, in words its design reads, whether a page is its record template, and the form that writes to it. */ export function aiSiteDatasetInputOf(dataset, screens, create = []) {
86
+ const shownIn = [];
87
+ for (const screen of screens){
88
+ for (const section of screen.sections){
89
+ if (!section.uses.some((ref)=>namesDataset(ref, dataset.name))) continue;
90
+ shownIn.push(`${screen.title} › ${section.name}${section.items ? ` (${section.items} items)` : ''}`);
91
+ }
92
+ }
93
+ const recordPages = screens.some((screen)=>!!screen.record && namesDataset(screen.record.dataset, dataset.name));
94
+ const form = create.find((entry)=>entry.kind === 'form' && !!entry.writesTo && namesDataset(entry.writesTo, dataset.name));
95
+ return _extends({
96
+ shownIn: shownIn.slice(0, 8),
97
+ recordPages
98
+ }, form ? {
99
+ forForm: {
100
+ name: form.name,
101
+ fields: form.fields.slice(0, AI_SITE_FORM_DATASET_FIELDS_MAX)
102
+ }
103
+ } : {});
104
+ }
105
+ /** Whether a plan screen is the record template of a dataset the plan creates, and which. */ export function aiSiteRecordTemplateOf(screen) {
106
+ var _screen_record;
107
+ const ref = (_screen_record = screen.record) == null ? void 0 : _screen_record.dataset;
108
+ return ref && isAiPlanNewRef(ref) ? ref.slice(ref.indexOf(':') + 1).trim() : null;
109
+ }
110
+ function datasetInputOf(job) {
111
+ var _ref;
112
+ var _job_inputs;
113
+ const raw = (_ref = (_job_inputs = job.inputs) == null ? void 0 : _job_inputs[AI_SITE_DATASET_INPUT]) != null ? _ref : {};
114
+ const form = raw.forForm && typeof raw.forForm === 'object' ? raw.forForm : null;
115
+ return _extends({
116
+ shownIn: Array.isArray(raw.shownIn) ? raw.shownIn.map(str).filter(Boolean).slice(0, 8) : [],
117
+ recordPages: raw.recordPages === true
118
+ }, form && str(form.name) ? {
119
+ forForm: {
120
+ name: str(form.name),
121
+ fields: Array.isArray(form.fields) ? form.fields.map(str).filter(Boolean).slice(0, AI_SITE_FORM_DATASET_FIELDS_MAX) : []
122
+ }
123
+ } : {});
124
+ }
125
+ /** The most fields a form's dataset is designed with: the data plugin writer's own ceiling is higher. */ export const AI_SITE_FORM_DATASET_FIELDS_MAX = 12;
126
+ /** The field types a planned `Name:type` may name, as the data plugin's writer takes them. */ const FORM_DATASET_FIELD_TYPES = [
127
+ 'text',
128
+ 'number',
129
+ 'integer',
130
+ 'boolean',
131
+ 'list'
132
+ ];
133
+ /** A planned field as a person reads it: `fullName` and `full_name` are "Full name". */ export function aiSiteDatasetFieldName(planned) {
134
+ const words = planned.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ').replace(/\s+/g, ' ').trim();
135
+ return words ? words.charAt(0).toUpperCase() + words.slice(1).toLowerCase() : '';
136
+ }
137
+ /**
138
+ * A dataset a form writes to, as the data plugin's writer takes it
139
+ * (AGL-3616): designed with no model, since its records are the
140
+ * submissions. Its fields are its planned ones — or, where the plan gave it
141
+ * none, the form's — each a person's words for it, text unless the plan
142
+ * typed it as `Name:type`, never required, so a submission that leaves one
143
+ * blank is still a record. It starts with no records. Pure.
144
+ */ export function aiSiteFormDatasetContent(name, planned, form) {
145
+ const specs = (planned.length ? planned : form.fields).slice(0, AI_SITE_FORM_DATASET_FIELDS_MAX);
146
+ const seen = new Set();
147
+ const fields = [];
148
+ for (const spec of specs){
149
+ const at = spec.lastIndexOf(':');
150
+ const typed = at > 0 ? spec.slice(at + 1).trim().toLowerCase() : '';
151
+ const type = FORM_DATASET_FIELD_TYPES.includes(typed) ? typed : 'text';
152
+ const fieldName = aiSiteDatasetFieldName(type === typed ? spec.slice(0, at) : spec).slice(0, 60);
153
+ if (!fieldName || seen.has(fieldName.toLowerCase())) continue;
154
+ seen.add(fieldName.toLowerCase());
155
+ fields.push({
156
+ name: fieldName,
157
+ type
158
+ });
159
+ }
160
+ return {
161
+ name,
162
+ fields,
163
+ records: []
164
+ };
165
+ }
166
+ /** The note a form's dataset carries on the job's page. */ export function aiSiteFormDatasetNote(form) {
167
+ return `Empty to start: each submission of the form “${form}” is added here, in Data.`;
168
+ }
169
+ const limitReview = (message)=>({
170
+ reason: 'limit',
171
+ message,
172
+ findings: []
173
+ });
174
+ /**
175
+ * The fields of a designed dataset that its records leave empty on most of
176
+ * them — every record, or more than half — by the id the data plugin gave
177
+ * each (AGL-3616). Its first field is the record's name and is never one. The
178
+ * live low-tide-hollow start (beta.239) seeded its tour dates with no venue,
179
+ * rightly inventing none, and its cards bound the venue as their eyebrow.
180
+ */ export function aiDatasetSparseFields(dataset, written) {
181
+ if (!dataset.records.length) return [];
182
+ const idOf = (name, column)=>{
183
+ var _ref, _ref1;
184
+ var _written_find, _written_column;
185
+ return (_ref = (_ref1 = (_written_find = written.find((field)=>field.name.trim().toLowerCase() === name.trim().toLowerCase())) == null ? void 0 : _written_find.id) != null ? _ref1 : (_written_column = written[column]) == null ? void 0 : _written_column.id) != null ? _ref : null;
186
+ };
187
+ return dataset.fields.flatMap((field, column)=>{
188
+ if (column === 0) return [];
189
+ const filled = dataset.records.filter((values)=>{
190
+ var _values_column;
191
+ return ((_values_column = values[column]) != null ? _values_column : '').trim();
192
+ }).length;
193
+ const id = idOf(field.name, column);
194
+ return id && filled * 2 <= dataset.records.length ? [
195
+ id
196
+ ] : [];
197
+ });
198
+ }
199
+ function proposalOf(output) {
200
+ var _output_proposal;
201
+ const raw = (_output_proposal = output.proposal) != null ? _output_proposal : {};
202
+ return {
203
+ fields: Array.isArray(raw.fields) ? raw.fields.filter((field)=>field && typeof field.id === 'string' && field.id).map((field)=>({
204
+ id: field.id,
205
+ name: str(field.name) || field.id,
206
+ type: str(field.type) || 'text'
207
+ })) : [],
208
+ recordNames: Array.isArray(raw.recordNames) ? raw.recordNames.map(str).filter(Boolean).slice(0, 12) : [],
209
+ addressField: str(raw.addressField) || null,
210
+ sparseFields: Array.isArray(raw.sparseFields) ? raw.sparseFields.map(str).filter(Boolean) : []
211
+ };
212
+ }
213
+ /**
214
+ * The dataset unit's runner, on the job the scaffold derived for it: the one
215
+ * creation its plan carries, designed by the model and written by the data
216
+ * plugin, under the creation's own id. A pass repeated after its write finds
217
+ * the dataset the writer wrote and reports it again, spending nothing.
218
+ */ export function createAiSiteDatasetRunner(deps = {}) {
219
+ var _deps_generate, _deps_writerFor;
220
+ const generate = (_deps_generate = deps.generate) != null ? _deps_generate : generateAiDataset;
221
+ const writerFor = (_deps_writerFor = deps.writerFor) != null ? _deps_writerFor : pluginResourceDraftWriter;
222
+ return async (context)=>{
223
+ var _ref, _context_org, _facts_fields, _facts_addressField, _facts_fields1, _facts_records;
224
+ var _job_plan_create, _job_plan;
225
+ const { job, firestore, signal } = context;
226
+ const model = (_ref = context.modelFor == null ? void 0 : context.modelFor.call(context, AI_DATASET_STEP)) != null ? _ref : aiModelForStep(AI_DATASET_STEP);
227
+ const creation = (_job_plan = job.plan) == null ? void 0 : (_job_plan_create = _job_plan.create) == null ? void 0 : _job_plan_create.find((entry)=>entry.kind === 'dataset');
228
+ const keeper = writerFor(AI_SITE_DATASET_RESOURCE);
229
+ if (!job.hostId || !creation) return _extends({}, aiUnspentOutcome(model), {
230
+ failure: 'Open the site before adding its data.'
231
+ });
232
+ if (!keeper) return _extends({}, aiUnspentOutcome(model), {
233
+ failure: 'Datasets are not available on this site.'
234
+ });
235
+ const host = await firestore.collection('hosts').doc(job.hostId).get();
236
+ const hostSubdomain = str(host.get('subdomain')) || null;
237
+ const output = (id, name, proposal, records)=>{
238
+ var _job_hostId;
239
+ return {
240
+ resource: 'draft',
241
+ draftResource: AI_SITE_DATASET_RESOURCE,
242
+ id,
243
+ hostId: (_job_hostId = job.hostId) != null ? _job_hostId : null,
244
+ hostSubdomain,
245
+ label: name,
246
+ note: aiSiteDatasetNote(records),
247
+ proposal: proposal
248
+ };
249
+ };
250
+ const input = datasetInputOf(job);
251
+ // Asked again under the same id: the dataset already written.
252
+ const written = await keeper.writer.read({
253
+ hostId: job.hostId,
254
+ id: job.$id
255
+ });
256
+ if (written) {
257
+ var _facts_fields2, _facts_addressField1;
258
+ const facts = written.facts;
259
+ const again = output(written.id, written.name || creation.name, {
260
+ fields: (_facts_fields2 = facts.fields) != null ? _facts_fields2 : [],
261
+ recordNames: [],
262
+ addressField: (_facts_addressField1 = facts.addressField) != null ? _facts_addressField1 : null
263
+ }, 0);
264
+ return aiUnspentOutcome(model, {
265
+ outputs: [
266
+ input.forForm ? _extends({}, again, {
267
+ note: aiSiteFormDatasetNote(input.forForm.name)
268
+ }) : again
269
+ ]
270
+ });
271
+ }
272
+ // A form's dataset (AGL-3616): its records are the submissions, so it is
273
+ // designed from the plan with no model, and starts empty.
274
+ if (input.forForm) {
275
+ var _context_org1, _facts_fields3;
276
+ const content = aiSiteFormDatasetContent(creation.name, creation.fields, input.forForm);
277
+ const checked = keeper.writer.check(content, {
278
+ hostId: job.hostId
279
+ });
280
+ if (checked.ok === false) {
281
+ console.error('ai site dataset: the dataset writer refused a form dataset we made', {
282
+ orgId: job.orgId,
283
+ jobId: job.$id,
284
+ problems: checked.problems
285
+ });
286
+ return _extends({}, aiUnspentOutcome(model), {
287
+ failure: AI_SITE_DATASET_NOT_WRITTEN_COPY
288
+ });
289
+ }
290
+ const result = await keeper.writer.write({
291
+ orgId: job.orgId,
292
+ hostId: job.hostId,
293
+ uid: job.createdBy,
294
+ org: (_context_org1 = context.org) != null ? _context_org1 : null,
295
+ now: context.now,
296
+ id: job.$id,
297
+ name: creation.name,
298
+ content
299
+ });
300
+ if (result.ok === false) {
301
+ if (result.status === 400) return _extends({}, aiUnspentOutcome(model), {
302
+ failure: AI_SITE_DATASET_NOT_WRITTEN_COPY
303
+ });
304
+ return _extends({}, aiUnspentOutcome(model), {
305
+ review: limitReview(result.error)
306
+ });
307
+ }
308
+ const facts = result.facts;
309
+ return aiUnspentOutcome(model, {
310
+ outputs: [
311
+ _extends({}, output(result.id, result.name, {
312
+ fields: (_facts_fields3 = facts.fields) != null ? _facts_fields3 : [],
313
+ recordNames: [],
314
+ addressField: null
315
+ }, 0), {
316
+ note: aiSiteFormDatasetNote(input.forForm.name)
317
+ })
318
+ ]
319
+ });
320
+ }
321
+ const generation = await generate(_extends({
322
+ brief: job.brief,
323
+ merchantWords: job.brief,
324
+ name: creation.name,
325
+ why: creation.why,
326
+ fields: creation.fields,
327
+ shownIn: input.shownIn,
328
+ recordPages: input.recordPages,
329
+ model,
330
+ maxTokens: AI_SITE_DATASET_BUDGET.maxTokens(model)
331
+ }, signal ? {
332
+ signal
333
+ } : {}));
334
+ const spent = aiGenerationSpent(generation);
335
+ if (generation.status === 'refused') return _extends({}, spent, {
336
+ refused: true
337
+ });
338
+ if (generation.status === 'needs_input') return _extends({}, spent, {
339
+ failure: generation.message
340
+ });
341
+ const content = aiDatasetDraftContent(creation.name, generation.value, {
342
+ recordPages: input.recordPages
343
+ });
344
+ // The data plugin's own rules, before anything is written: content we
345
+ // made that its model refuses is our failure, refunded.
346
+ const checked = keeper.writer.check(content, {
347
+ hostId: job.hostId
348
+ });
349
+ if (checked.ok === false) {
350
+ console.error('ai site dataset: the dataset writer refused a dataset we made', {
351
+ orgId: job.orgId,
352
+ jobId: job.$id,
353
+ problems: checked.problems
354
+ });
355
+ return _extends({}, spent, {
356
+ failure: AI_SITE_DATASET_NOT_WRITTEN_COPY
357
+ });
358
+ }
359
+ const result = await keeper.writer.write({
360
+ orgId: job.orgId,
361
+ hostId: job.hostId,
362
+ uid: job.createdBy,
363
+ org: (_context_org = context.org) != null ? _context_org : null,
364
+ now: context.now,
365
+ id: job.$id,
366
+ name: creation.name,
367
+ content
368
+ });
369
+ if (result.ok === false) {
370
+ if (result.status === 400) return _extends({}, spent, {
371
+ failure: AI_SITE_DATASET_NOT_WRITTEN_COPY
372
+ });
373
+ // The allowance, the role, the plan or the flag: the workspace's to change.
374
+ return _extends({}, spent, {
375
+ review: limitReview(result.error)
376
+ });
377
+ }
378
+ const facts = result.facts;
379
+ const proposal = {
380
+ fields: (_facts_fields = facts.fields) != null ? _facts_fields : [],
381
+ recordNames: generation.value.records.map((values)=>{
382
+ var _values_;
383
+ return (_values_ = values[0]) != null ? _values_ : '';
384
+ }).filter(Boolean).slice(0, 12),
385
+ addressField: (_facts_addressField = facts.addressField) != null ? _facts_addressField : null,
386
+ sparseFields: aiDatasetSparseFields(generation.value, (_facts_fields1 = facts.fields) != null ? _facts_fields1 : [])
387
+ };
388
+ return _extends({}, spent, {
389
+ outputs: [
390
+ output(result.id, result.name, proposal, (_facts_records = facts.records) != null ? _facts_records : generation.value.records.length)
391
+ ]
392
+ });
393
+ };
394
+ }
395
+ export const runAiSiteDatasetUnit = createAiSiteDatasetRunner();
396
+ /**
397
+ * Whether a dataset may be created for this member on this site, asked before
398
+ * its first pass so a refusal spends nothing; `null` admits. The data plugin
399
+ * must run here (on for the workspace and the site, past its release flag),
400
+ * and its writer says whether this member may make one now: the role, the
401
+ * plan, the flag, the allowance.
402
+ */ export async function aiSiteDatasetRefusal(context, deps = {}) {
403
+ var _deps_writerFor, _context_org, _deps_admission, _ref;
404
+ const { job } = context;
405
+ if (!job.hostId) return 'Open the site first.';
406
+ const writerFor = (_deps_writerFor = deps.writerFor) != null ? _deps_writerFor : pluginResourceDraftWriter;
407
+ const admission = {
408
+ firestore: context.firestore,
409
+ orgId: job.orgId,
410
+ hostId: job.hostId,
411
+ inputs: {},
412
+ org: (_context_org = context.org) != null ? _context_org : null,
413
+ uid: job.createdBy
414
+ };
415
+ const refused = await ((_deps_admission = deps.admission) != null ? _deps_admission : aiPluginDraftAdmissionRefusal)(admission, {
416
+ kind: 'site',
417
+ drafts: [
418
+ {
419
+ resource: AI_SITE_DATASET_RESOURCE,
420
+ label: DATA_PLUGIN_LABEL
421
+ }
422
+ ],
423
+ writerFor: (resource)=>{
424
+ var _ref;
425
+ var _writerFor;
426
+ return (_ref = (_writerFor = writerFor(resource)) == null ? void 0 : _writerFor.writer) != null ? _ref : null;
427
+ },
428
+ ownerFor: (resource)=>{
429
+ var _ref;
430
+ var _writerFor;
431
+ return (_ref = (_writerFor = writerFor(resource)) == null ? void 0 : _writerFor.pluginId) != null ? _ref : null;
432
+ },
433
+ now: context.now
434
+ });
435
+ return (_ref = refused == null ? void 0 : refused.error) != null ? _ref : null;
436
+ }
437
+ /**
438
+ * A site plan's dataset capability on this workspace (AGL-3616): where the
439
+ * workspace's plan includes datasets, whether the data plugin runs here and
440
+ * this member may create one now, and how many a start may make. A clause the
441
+ * plan's turn completes is the reason where it may not.
442
+ */ export async function aiSitePlanDatasetCapability(capabilities, context, deps = {}) {
443
+ if (!capabilities.create.dataset.allowed) return capabilities;
444
+ const refusal = await aiSiteDatasetRefusal(context, deps).catch((error)=>{
445
+ console.error('ai site plan: the dataset admission failed', {
446
+ orgId: context.job.orgId,
447
+ error
448
+ });
449
+ return 'datasets could not be checked just now';
450
+ });
451
+ const left = Math.min(AI_SITE_DATASETS_MAX, checkDatasetQuota(context.org, 0).limit);
452
+ const dataset = refusal ? {
453
+ allowed: false,
454
+ left: 0,
455
+ reason: `datasets cannot be created here: ${refusal.replace(/\.$/, '')}`
456
+ } : {
457
+ allowed: true,
458
+ left,
459
+ reason: null
460
+ };
461
+ return _extends({}, capabilities, {
462
+ create: _extends({}, capabilities.create, {
463
+ dataset
464
+ })
465
+ });
466
+ }
467
+ /**
468
+ * A `records` listing for each dataset that was made and that a section of the
469
+ * plan names (AGL-3616). Its fields, where none are given, are read off the
470
+ * site's inventory when the page is designed (`aiLayoutListingsWithDatasets`).
471
+ */ export function aiDatasetListingsOf(datasets, screens) {
472
+ return datasets.flatMap((dataset)=>{
473
+ var _dataset_records;
474
+ var _dataset_fields;
475
+ const placements = aiLayoutRecordsPlacements(dataset, screens);
476
+ if (!placements.length) return [];
477
+ return [
478
+ _extends({
479
+ id: aiLayoutListingId('records', dataset.id),
480
+ kind: 'records',
481
+ name: dataset.name,
482
+ records: (_dataset_records = dataset.records) != null ? _dataset_records : [],
483
+ datasetId: dataset.id
484
+ }, ((_dataset_fields = dataset.fields) == null ? void 0 : _dataset_fields.length) ? {
485
+ fields: dataset.fields
486
+ } : {}, {
487
+ placements
488
+ })
489
+ ];
490
+ });
491
+ }
492
+ /**
493
+ * The site's datasets a layout or a page unit is handed (AGL-3616), each a
494
+ * `records` listing with the sections of the plan's pages that name it, once
495
+ * its unit wrote it. A dataset not yet written, or one that failed, lists
496
+ * nowhere: the pages built without it write the items out.
497
+ */ export function aiSiteDatasetListings(input) {
498
+ const made = input.datasets.flatMap((dataset)=>{
499
+ const id = input.delivered.get(dataset.name.trim().toLowerCase());
500
+ const output = id ? input.outputs.find((entry)=>entry.resource === 'draft' && entry.draftResource === AI_SITE_DATASET_RESOURCE && entry.id === id) : undefined;
501
+ if (!id || !output) return [];
502
+ const proposal = proposalOf(output);
503
+ return [
504
+ {
505
+ id,
506
+ // Placed by the name the plan's sections gave it.
507
+ name: dataset.name,
508
+ records: proposal.recordNames,
509
+ // The address field is the record page's, never a card's words; a
510
+ // field most records leave empty is no card's either (AGL-3616).
511
+ fields: proposal.fields.filter((field, index)=>{
512
+ var _proposal_sparseFields;
513
+ return field.id !== proposal.addressField && (index === 0 || !((_proposal_sparseFields = proposal.sparseFields) == null ? void 0 : _proposal_sparseFields.includes(field.id)));
514
+ })
515
+ }
516
+ ];
517
+ });
518
+ return aiDatasetListingsOf(made, input.screens);
519
+ }
520
+ /** What a page is told about the datasets built before it. */ export function aiSiteDatasetBriefLines(outputs) {
521
+ const datasets = outputs.filter((output)=>output.resource === 'draft' && output.draftResource === AI_SITE_DATASET_RESOURCE);
522
+ return datasets.flatMap((output)=>{
523
+ const names = proposalOf(output).recordNames;
524
+ return names.length ? [
525
+ `This site's dataset “${output.label}” holds: ${names.map((name)=>`“${name}”`).join(', ')}. Where a page names one, name it as written; never name one that is not in this list.`
526
+ ] : [];
527
+ });
528
+ }
529
+
530
+ //# sourceMappingURL=ai-job-site-datasets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-site-datasets.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 { checkDatasetQuota } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { pluginResourceDraftWriter } from '@aglyn/aglyn/plugin-manager/plugin-resource-drafts'\nimport { isAiPlanNewRef, type AiBuildPlanCreate, type AiBuildPlanScreen } from '../model/ai-build-plan'\nimport type { AiJob, AiJobOutput, AiJobPlan } from '../model/ai-jobs.types'\nimport type { AiPlanCapabilities } from '../model/ai-plan-capabilities'\nimport { AI_SITE_DATASETS_MAX } from '../model/ai-site-job'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { aiModelForStep } from '../providers/routing'\nimport {\n AI_DATASET_MAX_TOKENS,\n AI_DATASET_STEP,\n aiDatasetDraftContent,\n generateAiDataset,\n} from '../runtime/ai-dataset-generation'\nimport {\n aiLayoutListingId,\n aiLayoutRecordsPlacements,\n type AiLayoutListing,\n type AiLayoutListingField,\n type AiLayoutListingScreen,\n} from '../layout-language/ai-layout-listings'\nimport type { AiJobAdmissionContext } from './ai-job-admission'\nimport { AI_DATASET_DRAFT_RESOURCE } from './ai-build-unit-outcome'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { aiGenerationSpent, aiUnspentOutcome } from './ai-job-generation'\nimport { aiPluginDraftAdmissionRefusal } from './ai-job-plugin-drafts'\nimport type { AiJobStepContext, AiJobStepOutcome, AiJobStepRunner } from './ai-job-text-step'\n\n/**\n * A SITE START'S DATASETS (AGL-3616): \"Aglyn AI can also use/create datasets\n * too for whatever it needs\" (Zach, 2026-10-10).\n *\n * A site whose content is a list of like things — a restaurant's menu, a\n * studio's team, a contractor's services, a photographer's portfolio, an\n * events list, its questions and answers — keeps it as a DATASET, the way the\n * docs teach a person to: the owner edits one record and every page listing\n * it follows. On a workspace that may create datasets, the site plan creates\n * one for such content (`create`, kind `dataset`) and names it in the `uses`\n * of each section that lists it. The scaffold then builds each dataset as a\n * unit of its own, after the layout and before the form (which may write\n * its submissions to one) and the pages:\n *\n * - DESIGNED by the `submit_dataset` generation (`ai-dataset-generation.ts`):\n * the planned fields typed, at most a few more, and 3 to 12 records seeded\n * from the brief. Nothing anybody did not say: no review, testimonial,\n * rating, quote or person's name, no price the brief does not state.\n * - WRITTEN by the data plugin's `dataset` writer — this plugin never names\n * the datasets collection — with every rule the console's datasets route\n * has: the member's role and `data.manage`, `release_data_store`,\n * `dataStore` (Starter and up), `datasetsPerOrg` counted inside the create,\n * `recordsPerDataset` and the storage band.\n * - LISTED by the pages built after it: each section naming it repeats over\n * the dataset (`aiSiteDatasetListings`, a `records` listing the layout\n * compiler draws), and a page the plan made its record template binds its\n * fields by id and keeps the address field the writer added.\n *\n * Where datasets cannot be created — the Free taste, Starter's two used up,\n * the data store not released — the plan is told so and creates none, so its\n * sections write the items out. A dataset unit that fails or is refused\n * leaves the pages that list it built without it (they say so), and its\n * record page is not built.\n */\n\n/** The most datasets one site start creates. */\nexport { AI_SITE_DATASETS_MAX } from '../model/ai-site-job'\n\n/** The resource the data plugin's writer is registered under. */\nexport const AI_SITE_DATASET_RESOURCE = AI_DATASET_DRAFT_RESOURCE\n\n/** What the site's plugin list calls the plugin, for \"Turn on Data for this site\". */\nconst DATA_PLUGIN_LABEL = 'Data'\n\n/** The input a dataset unit's own job carries what it needs in. */\nexport const AI_SITE_DATASET_INPUT = 'siteDataset'\n\n/** What a dataset unit's job is told beside its creation. */\nexport interface AiSiteDatasetInput {\n /** The sections that list it, by page: \"Menu › Our dishes (12 items)\". */\n shownIn: string[]\n /** Whether a page of the plan is its record template. */\n recordPages: boolean\n /**\n * The form of the plan that writes its submissions to it (AGL-3616), by\n * name, with that form's planned fields; absent where no form does.\n */\n forForm?: { name: string; fields: string[] }\n}\n\n/** What a dataset row says when its design could not be written: our failure, refunded. */\nexport const AI_SITE_DATASET_NOT_WRITTEN_COPY = 'This dataset could not be added this time.'\n\n/** The note a written dataset carries on the job's page. */\nexport function aiSiteDatasetNote(records: number): string {\n return `${records} ${records === 1 ? 'record' : 'records'} to start, in Data. Edit them there and every page that lists them follows.`\n}\n\n/** The note a record template's row adds: the binding is the member's to save (rule 13). */\nexport const AI_SITE_RECORD_PAGE_NOTE =\n 'A page per record is drafted, not live: open Page Properties → Record pages to save it.'\n\n/** ASSUMED: what one dataset pass reads and writes beside its generation: the site, the writer's reads and its transaction. */\nexport const AI_SITE_DATASET_WRITES_MS = 2_000\n\n/** One dataset's pass: its generation and re-ask at its ceiling, and its writes. */\nexport const AI_SITE_DATASET_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS[AI_DATASET_STEP],\n maxTokens: AI_DATASET_MAX_TOKENS,\n lookups: 0,\n ownReadsMs: AI_SITE_DATASET_WRITES_MS,\n})\n\nconst str = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\n/** The plan's reference to a dataset as a section names it: `new:<name>`, compared lowercased. */\nfunction namesDataset(ref: string, name: string): boolean {\n return isAiPlanNewRef(ref) && ref.slice(ref.indexOf(':') + 1).trim().toLowerCase() === name.trim().toLowerCase()\n}\n\n/** The plan's datasets, in plan order, at most the most a start creates. */\nexport function aiSitePlanDatasets(plan: Pick<AiJobPlan, 'create'>): AiBuildPlanCreate[] {\n return plan.create.filter((entry) => entry.kind === 'dataset').slice(0, AI_SITE_DATASETS_MAX)\n}\n\n/** Where the plan lists a dataset, in words its design reads, whether a page is its record template, and the form that writes to it. */\nexport function aiSiteDatasetInputOf(\n dataset: Pick<AiBuildPlanCreate, 'name'>,\n screens: readonly AiBuildPlanScreen[],\n create: readonly AiBuildPlanCreate[] = [],\n): AiSiteDatasetInput {\n const shownIn: string[] = []\n for (const screen of screens) {\n for (const section of screen.sections) {\n if (!section.uses.some((ref) => namesDataset(ref, dataset.name))) continue\n shownIn.push(`${screen.title} › ${section.name}${section.items ? ` (${section.items} items)` : ''}`)\n }\n }\n const recordPages = screens.some((screen) => !!screen.record && namesDataset(screen.record.dataset, dataset.name))\n const form = create.find((entry) => entry.kind === 'form' && !!entry.writesTo && namesDataset(entry.writesTo, dataset.name))\n return {\n shownIn: shownIn.slice(0, 8),\n recordPages,\n ...(form ? { forForm: { name: form.name, fields: form.fields.slice(0, AI_SITE_FORM_DATASET_FIELDS_MAX) } } : {}),\n }\n}\n\n/** Whether a plan screen is the record template of a dataset the plan creates, and which. */\nexport function aiSiteRecordTemplateOf(screen: Pick<AiBuildPlanScreen, 'record'>): string | null {\n const ref = screen.record?.dataset\n return ref && isAiPlanNewRef(ref) ? ref.slice(ref.indexOf(':') + 1).trim() : null\n}\n\nfunction datasetInputOf(job: Pick<AiJob, 'inputs'>): AiSiteDatasetInput {\n const raw = (job.inputs?.[AI_SITE_DATASET_INPUT] ?? {}) as Partial<AiSiteDatasetInput>\n const form = raw.forForm && typeof raw.forForm === 'object' ? raw.forForm : null\n return {\n shownIn: Array.isArray(raw.shownIn) ? raw.shownIn.map(str).filter(Boolean).slice(0, 8) : [],\n recordPages: raw.recordPages === true,\n ...(form && str(form.name)\n ? {\n forForm: {\n name: str(form.name),\n fields: Array.isArray(form.fields) ? form.fields.map(str).filter(Boolean).slice(0, AI_SITE_FORM_DATASET_FIELDS_MAX) : [],\n },\n }\n : {}),\n }\n}\n\n/** The most fields a form's dataset is designed with: the data plugin writer's own ceiling is higher. */\nexport const AI_SITE_FORM_DATASET_FIELDS_MAX = 12\n\n/** The field types a planned `Name:type` may name, as the data plugin's writer takes them. */\nconst FORM_DATASET_FIELD_TYPES = ['text', 'number', 'integer', 'boolean', 'list'] as const\n\n/** A planned field as a person reads it: `fullName` and `full_name` are \"Full name\". */\nexport function aiSiteDatasetFieldName(planned: string): string {\n const words = planned\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/[_-]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n return words ? words.charAt(0).toUpperCase() + words.slice(1).toLowerCase() : ''\n}\n\n/**\n * A dataset a form writes to, as the data plugin's writer takes it\n * (AGL-3616): designed with no model, since its records are the\n * submissions. Its fields are its planned ones — or, where the plan gave it\n * none, the form's — each a person's words for it, text unless the plan\n * typed it as `Name:type`, never required, so a submission that leaves one\n * blank is still a record. It starts with no records. Pure.\n */\nexport function aiSiteFormDatasetContent(\n name: string,\n planned: readonly string[],\n form: { fields: readonly string[] },\n): Record<string, unknown> {\n const specs = (planned.length ? planned : form.fields).slice(0, AI_SITE_FORM_DATASET_FIELDS_MAX)\n const seen = new Set<string>()\n const fields: Array<{ name: string; type: string }> = []\n for (const spec of specs) {\n const at = spec.lastIndexOf(':')\n const typed = at > 0 ? spec.slice(at + 1).trim().toLowerCase() : ''\n const type = (FORM_DATASET_FIELD_TYPES as readonly string[]).includes(typed) ? typed : 'text'\n const fieldName = aiSiteDatasetFieldName(type === typed ? spec.slice(0, at) : spec).slice(0, 60)\n if (!fieldName || seen.has(fieldName.toLowerCase())) continue\n seen.add(fieldName.toLowerCase())\n fields.push({ name: fieldName, type })\n }\n return { name, fields, records: [] }\n}\n\n/** The note a form's dataset carries on the job's page. */\nexport function aiSiteFormDatasetNote(form: string): string {\n return `Empty to start: each submission of the form “${form}” is added here, in Data.`\n}\n\nconst limitReview = (message: string) => ({ reason: 'limit' as const, message, findings: [] })\n\nexport interface AiSiteDatasetRunnerDeps {\n generate?: typeof generateAiDataset\n writerFor?: typeof pluginResourceDraftWriter\n}\n\n/** The fields and record names a written dataset reports, as its output's proposal keeps them. */\ninterface AiSiteDatasetProposal {\n fields: AiLayoutListingField[]\n /** Each record's name: its first field's value. */\n recordNames: string[]\n addressField: string | null\n /**\n * The fields most of its first records leave empty, by id (AGL-3616): a\n * tour's venues not yet known. A card never binds one — it would print\n * nothing on most records — so its listing leaves them out.\n */\n sparseFields?: string[]\n}\n\n/**\n * The fields of a designed dataset that its records leave empty on most of\n * them — every record, or more than half — by the id the data plugin gave\n * each (AGL-3616). Its first field is the record's name and is never one. The\n * live low-tide-hollow start (beta.239) seeded its tour dates with no venue,\n * rightly inventing none, and its cards bound the venue as their eyebrow.\n */\nexport function aiDatasetSparseFields(\n dataset: { fields: ReadonlyArray<{ name: string }>; records: ReadonlyArray<ReadonlyArray<string | undefined>> },\n written: readonly AiLayoutListingField[],\n): string[] {\n if (!dataset.records.length) return []\n const idOf = (name: string, column: number) =>\n written.find((field) => field.name.trim().toLowerCase() === name.trim().toLowerCase())?.id ?? written[column]?.id ?? null\n return dataset.fields.flatMap((field, column) => {\n if (column === 0) return []\n const filled = dataset.records.filter((values) => (values[column] ?? '').trim()).length\n const id = idOf(field.name, column)\n return id && filled * 2 <= dataset.records.length ? [id] : []\n })\n}\n\nfunction proposalOf(output: Pick<AiJobOutput, 'proposal'>): AiSiteDatasetProposal {\n const raw = (output.proposal ?? {}) as Partial<AiSiteDatasetProposal>\n return {\n fields: Array.isArray(raw.fields)\n ? raw.fields.filter((field) => field && typeof field.id === 'string' && field.id).map((field) => ({ id: field.id, name: str(field.name) || field.id, type: str(field.type) || 'text' }))\n : [],\n recordNames: Array.isArray(raw.recordNames) ? raw.recordNames.map(str).filter(Boolean).slice(0, 12) : [],\n addressField: str(raw.addressField) || null,\n sparseFields: Array.isArray(raw.sparseFields) ? raw.sparseFields.map(str).filter(Boolean) : [],\n }\n}\n\n/**\n * The dataset unit's runner, on the job the scaffold derived for it: the one\n * creation its plan carries, designed by the model and written by the data\n * plugin, under the creation's own id. A pass repeated after its write finds\n * the dataset the writer wrote and reports it again, spending nothing.\n */\nexport function createAiSiteDatasetRunner(deps: AiSiteDatasetRunnerDeps = {}): AiJobStepRunner {\n const generate = deps.generate ?? generateAiDataset\n const writerFor = deps.writerFor ?? pluginResourceDraftWriter\n return async (context): Promise<AiJobStepOutcome> => {\n const { job, firestore, signal } = context\n const model = context.modelFor?.(AI_DATASET_STEP) ?? aiModelForStep(AI_DATASET_STEP)\n const creation = (job.plan as AiJobPlan | null)?.create?.find((entry) => entry.kind === 'dataset')\n const keeper = writerFor(AI_SITE_DATASET_RESOURCE)\n if (!job.hostId || !creation) return { ...aiUnspentOutcome(model), failure: 'Open the site before adding its data.' }\n if (!keeper) return { ...aiUnspentOutcome(model), failure: 'Datasets are not available on this site.' }\n const host = await firestore.collection('hosts').doc(job.hostId).get()\n const hostSubdomain = str(host.get('subdomain')) || null\n const output = (id: string, name: string, proposal: AiSiteDatasetProposal, records: number): AiJobOutput => ({\n resource: 'draft',\n draftResource: AI_SITE_DATASET_RESOURCE,\n id,\n hostId: job.hostId ?? null,\n hostSubdomain,\n label: name,\n note: aiSiteDatasetNote(records),\n proposal: proposal as unknown as Record<string, unknown>,\n })\n const input = datasetInputOf(job)\n // Asked again under the same id: the dataset already written.\n const written = await keeper.writer.read({ hostId: job.hostId, id: job.$id })\n if (written) {\n const facts = written.facts as { fields?: AiLayoutListingField[]; addressField?: string | null }\n const again = output(written.id, written.name || creation.name, { fields: facts.fields ?? [], recordNames: [], addressField: facts.addressField ?? null }, 0)\n return aiUnspentOutcome(model, {\n outputs: [input.forForm ? { ...again, note: aiSiteFormDatasetNote(input.forForm.name) } : again],\n })\n }\n // A form's dataset (AGL-3616): its records are the submissions, so it is\n // designed from the plan with no model, and starts empty.\n if (input.forForm) {\n const content = aiSiteFormDatasetContent(creation.name, creation.fields, input.forForm)\n const checked = keeper.writer.check(content, { hostId: job.hostId })\n if (checked.ok === false) {\n console.error('ai site dataset: the dataset writer refused a form dataset we made', { orgId: job.orgId, jobId: job.$id, problems: checked.problems })\n return { ...aiUnspentOutcome(model), failure: AI_SITE_DATASET_NOT_WRITTEN_COPY }\n }\n const result = await keeper.writer.write({\n orgId: job.orgId,\n hostId: job.hostId,\n uid: job.createdBy,\n org: (context.org ?? null) as Readonly<Record<string, unknown>> | null,\n now: context.now,\n id: job.$id,\n name: creation.name,\n content,\n })\n if (result.ok === false) {\n if (result.status === 400) return { ...aiUnspentOutcome(model), failure: AI_SITE_DATASET_NOT_WRITTEN_COPY }\n return { ...aiUnspentOutcome(model), review: limitReview(result.error) }\n }\n const facts = result.facts as { fields?: AiLayoutListingField[]; addressField?: string | null }\n return aiUnspentOutcome(model, {\n outputs: [\n {\n ...output(result.id, result.name, { fields: facts.fields ?? [], recordNames: [], addressField: null }, 0),\n note: aiSiteFormDatasetNote(input.forForm.name),\n },\n ],\n })\n }\n const generation = await generate({\n brief: job.brief,\n merchantWords: job.brief,\n name: creation.name,\n why: creation.why,\n fields: creation.fields,\n shownIn: input.shownIn,\n recordPages: input.recordPages,\n model,\n maxTokens: AI_SITE_DATASET_BUDGET.maxTokens(model),\n ...(signal ? { signal } : {}),\n })\n const spent = aiGenerationSpent(generation)\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, failure: generation.message }\n const content = aiDatasetDraftContent(creation.name, generation.value, { recordPages: input.recordPages })\n // The data plugin's own rules, before anything is written: content we\n // made that its model refuses is our failure, refunded.\n const checked = keeper.writer.check(content, { hostId: job.hostId })\n if (checked.ok === false) {\n console.error('ai site dataset: the dataset writer refused a dataset we made', { orgId: job.orgId, jobId: job.$id, problems: checked.problems })\n return { ...spent, failure: AI_SITE_DATASET_NOT_WRITTEN_COPY }\n }\n const result = await keeper.writer.write({\n orgId: job.orgId,\n hostId: job.hostId,\n uid: job.createdBy,\n org: (context.org ?? null) as Readonly<Record<string, unknown>> | null,\n now: context.now,\n id: job.$id,\n name: creation.name,\n content,\n })\n if (result.ok === false) {\n if (result.status === 400) return { ...spent, failure: AI_SITE_DATASET_NOT_WRITTEN_COPY }\n // The allowance, the role, the plan or the flag: the workspace's to change.\n return { ...spent, review: limitReview(result.error) }\n }\n const facts = result.facts as { fields?: AiLayoutListingField[]; records?: number; addressField?: string | null }\n const proposal: AiSiteDatasetProposal = {\n fields: facts.fields ?? [],\n recordNames: generation.value.records.map((values) => values[0] ?? '').filter(Boolean).slice(0, 12),\n addressField: facts.addressField ?? null,\n sparseFields: aiDatasetSparseFields(generation.value, facts.fields ?? []),\n }\n return { ...spent, outputs: [output(result.id, result.name, proposal, facts.records ?? generation.value.records.length)] }\n }\n}\n\nexport const runAiSiteDatasetUnit = createAiSiteDatasetRunner()\n\n/**\n * Whether a dataset may be created for this member on this site, asked before\n * its first pass so a refusal spends nothing; `null` admits. The data plugin\n * must run here (on for the workspace and the site, past its release flag),\n * and its writer says whether this member may make one now: the role, the\n * plan, the flag, the allowance.\n */\nexport async function aiSiteDatasetRefusal(\n context: Pick<AiJobStepContext, 'firestore' | 'org' | 'now'> & { job: Pick<AiJob, 'orgId' | 'hostId' | 'createdBy'> },\n deps: { admission?: typeof aiPluginDraftAdmissionRefusal; writerFor?: (resource: string) => ReturnType<typeof pluginResourceDraftWriter> } = {},\n): Promise<string | null> {\n const { job } = context\n if (!job.hostId) return 'Open the site first.'\n const writerFor = deps.writerFor ?? pluginResourceDraftWriter\n const admission: AiJobAdmissionContext = {\n firestore: context.firestore,\n orgId: job.orgId,\n hostId: job.hostId,\n inputs: {},\n org: context.org ?? null,\n uid: job.createdBy,\n }\n const refused = await (deps.admission ?? aiPluginDraftAdmissionRefusal)(admission, {\n kind: 'site',\n drafts: [{ resource: AI_SITE_DATASET_RESOURCE, label: DATA_PLUGIN_LABEL }],\n writerFor: (resource) => writerFor(resource)?.writer ?? null,\n ownerFor: (resource) => writerFor(resource)?.pluginId ?? null,\n now: context.now,\n })\n return refused?.error ?? null\n}\n\n/**\n * A site plan's dataset capability on this workspace (AGL-3616): where the\n * workspace's plan includes datasets, whether the data plugin runs here and\n * this member may create one now, and how many a start may make. A clause the\n * plan's turn completes is the reason where it may not.\n */\nexport async function aiSitePlanDatasetCapability(\n capabilities: AiPlanCapabilities,\n context: Parameters<typeof aiSiteDatasetRefusal>[0],\n deps: Parameters<typeof aiSiteDatasetRefusal>[1] = {},\n): Promise<AiPlanCapabilities> {\n if (!capabilities.create.dataset.allowed) return capabilities\n const refusal = await aiSiteDatasetRefusal(context, deps).catch((error: unknown) => {\n console.error('ai site plan: the dataset admission failed', { orgId: context.job.orgId, error })\n return 'datasets could not be checked just now'\n })\n const left = Math.min(AI_SITE_DATASETS_MAX, checkDatasetQuota(context.org as never, 0).limit)\n const dataset = refusal\n ? { allowed: false, left: 0, reason: `datasets cannot be created here: ${refusal.replace(/\\.$/, '')}` }\n : { allowed: true, left, reason: null }\n return { ...capabilities, create: { ...capabilities.create, dataset } }\n}\n\n/**\n * A `records` listing for each dataset that was made and that a section of the\n * plan names (AGL-3616). Its fields, where none are given, are read off the\n * site's inventory when the page is designed (`aiLayoutListingsWithDatasets`).\n */\nexport function aiDatasetListingsOf(\n datasets: ReadonlyArray<{ id: string; name: string; records?: string[]; fields?: AiLayoutListingField[] }>,\n screens: readonly AiLayoutListingScreen[],\n): AiLayoutListing[] {\n return datasets.flatMap((dataset) => {\n const placements = aiLayoutRecordsPlacements(dataset, screens)\n if (!placements.length) return []\n return [\n {\n id: aiLayoutListingId('records', dataset.id),\n kind: 'records' as const,\n name: dataset.name,\n records: dataset.records ?? [],\n datasetId: dataset.id,\n ...(dataset.fields?.length ? { fields: dataset.fields } : {}),\n placements,\n },\n ]\n })\n}\n\n/**\n * The site's datasets a layout or a page unit is handed (AGL-3616), each a\n * `records` listing with the sections of the plan's pages that name it, once\n * its unit wrote it. A dataset not yet written, or one that failed, lists\n * nowhere: the pages built without it write the items out.\n */\nexport function aiSiteDatasetListings(input: {\n outputs: readonly AiJobOutput[]\n datasets: ReadonlyArray<Pick<AiBuildPlanCreate, 'name'>>\n screens: readonly AiLayoutListingScreen[]\n /** The ids each dataset's unit delivered, by plan name lowercased. */\n delivered: ReadonlyMap<string, string>\n}): AiLayoutListing[] {\n const made = input.datasets.flatMap((dataset) => {\n const id = input.delivered.get(dataset.name.trim().toLowerCase())\n const output = id\n ? input.outputs.find((entry) => entry.resource === 'draft' && entry.draftResource === AI_SITE_DATASET_RESOURCE && entry.id === id)\n : undefined\n if (!id || !output) return []\n const proposal = proposalOf(output)\n return [\n {\n id,\n // Placed by the name the plan's sections gave it.\n name: dataset.name,\n records: proposal.recordNames,\n // The address field is the record page's, never a card's words; a\n // field most records leave empty is no card's either (AGL-3616).\n fields: proposal.fields.filter(\n (field, index) => field.id !== proposal.addressField && (index === 0 || !proposal.sparseFields?.includes(field.id)),\n ),\n },\n ]\n })\n return aiDatasetListingsOf(made, input.screens)\n}\n\n/** What a page is told about the datasets built before it. */\nexport function aiSiteDatasetBriefLines(outputs: readonly AiJobOutput[]): string[] {\n const datasets = outputs.filter((output) => output.resource === 'draft' && output.draftResource === AI_SITE_DATASET_RESOURCE)\n return datasets.flatMap((output) => {\n const names = proposalOf(output).recordNames\n return names.length\n ? [`This site's dataset “${output.label}” holds: ${names.map((name) => `“${name}”`).join(', ')}. Where a page names one, name it as written; never name one that is not in this list.`]\n : []\n })\n}\n"],"names":["checkDatasetQuota","pluginResourceDraftWriter","isAiPlanNewRef","AI_SITE_DATASETS_MAX","AI_STEP_TIERS","aiModelForStep","AI_DATASET_MAX_TOKENS","AI_DATASET_STEP","aiDatasetDraftContent","generateAiDataset","aiLayoutListingId","aiLayoutRecordsPlacements","AI_DATASET_DRAFT_RESOURCE","aiJobStepBudget","aiGenerationSpent","aiUnspentOutcome","aiPluginDraftAdmissionRefusal","AI_SITE_DATASET_RESOURCE","DATA_PLUGIN_LABEL","AI_SITE_DATASET_INPUT","AI_SITE_DATASET_NOT_WRITTEN_COPY","aiSiteDatasetNote","records","AI_SITE_RECORD_PAGE_NOTE","AI_SITE_DATASET_WRITES_MS","AI_SITE_DATASET_BUDGET","tier","maxTokens","lookups","ownReadsMs","str","value","trim","namesDataset","ref","name","slice","indexOf","toLowerCase","aiSitePlanDatasets","plan","create","filter","entry","kind","aiSiteDatasetInputOf","dataset","screens","shownIn","screen","section","sections","uses","some","push","title","items","recordPages","record","form","find","writesTo","forForm","fields","AI_SITE_FORM_DATASET_FIELDS_MAX","aiSiteRecordTemplateOf","datasetInputOf","job","raw","inputs","Array","isArray","map","Boolean","FORM_DATASET_FIELD_TYPES","aiSiteDatasetFieldName","planned","words","replace","charAt","toUpperCase","aiSiteFormDatasetContent","specs","length","seen","Set","spec","at","lastIndexOf","typed","type","includes","fieldName","has","add","aiSiteFormDatasetNote","limitReview","message","reason","findings","aiDatasetSparseFields","written","idOf","column","field","id","flatMap","filled","values","proposalOf","output","proposal","recordNames","addressField","sparseFields","createAiSiteDatasetRunner","deps","generate","writerFor","context","facts","firestore","signal","model","modelFor","creation","keeper","hostId","failure","host","collection","doc","get","hostSubdomain","resource","draftResource","label","note","input","writer","read","$id","again","outputs","content","checked","check","ok","console","error","orgId","jobId","problems","result","write","uid","createdBy","org","now","status","review","generation","brief","merchantWords","why","spent","refused","runAiSiteDatasetUnit","aiSiteDatasetRefusal","admission","drafts","ownerFor","pluginId","aiSitePlanDatasetCapability","capabilities","allowed","refusal","catch","left","Math","min","limit","aiDatasetListingsOf","datasets","placements","datasetId","aiSiteDatasetListings","made","delivered","undefined","index","aiSiteDatasetBriefLines","names","join"],"mappings":"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"}