@aglyn/plugins-ai 1.0.0-beta.246 → 1.0.0-beta.249
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.
- package/package.json +17 -17
- package/src/lib/activity/ai-activity-actions.js +5 -1
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-job-links.js +14 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
- package/src/lib/components/ai-site-start-card.component.js +92 -19
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/declarations.server.js +12 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/jobs/ai-build-capabilities.js +56 -2
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.d.ts +3 -8
- package/src/lib/jobs/ai-job-build-step.js +49 -12
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-form-dataset.d.ts +8 -5
- package/src/lib/jobs/ai-job-form-dataset.js +28 -10
- package/src/lib/jobs/ai-job-form-dataset.js.map +1 -1
- package/src/lib/jobs/ai-job-form-step.d.ts +20 -4
- package/src/lib/jobs/ai-job-form-step.js +98 -28
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +6 -2
- package/src/lib/jobs/ai-job-layout-language.js +16 -5
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +4 -1
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-media-step.d.ts +239 -0
- package/src/lib/jobs/ai-job-media-step.js +610 -0
- package/src/lib/jobs/ai-job-media-step.js.map +1 -0
- package/src/lib/jobs/ai-job-page-language.d.ts +2 -0
- package/src/lib/jobs/ai-job-page-language.js +9 -0
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.js +4 -3
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +3 -2
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +10 -1
- package/src/lib/jobs/ai-job-plan-step.js +73 -15
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-settings-step.d.ts +0 -4
- package/src/lib/jobs/ai-job-settings-step.js +2 -7
- package/src/lib/jobs/ai-job-settings-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +2 -2
- package/src/lib/jobs/ai-job-site-content.js +5 -3
- package/src/lib/jobs/ai-job-site-content.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +1 -1
- package/src/lib/jobs/ai-job-site-datasets.js +3 -2
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-job-site-events.d.ts +148 -0
- package/src/lib/jobs/ai-job-site-events.js +313 -0
- package/src/lib/jobs/ai-job-site-events.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +9 -3
- package/src/lib/jobs/ai-job-site-step.js +63 -17
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-tracking-step.d.ts +184 -0
- package/src/lib/jobs/ai-job-tracking-step.js +501 -0
- package/src/lib/jobs/ai-job-tracking-step.js.map +1 -0
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +41 -0
- package/src/lib/jobs/ai-layout-stock-photos.js +71 -3
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-publish.js +2 -3
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/ai-stock-photo-picker.d.ts +24 -0
- package/src/lib/jobs/ai-stock-photo-picker.js +108 -0
- package/src/lib/jobs/ai-stock-photo-picker.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.d.ts +37 -0
- package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.js +47 -0
- package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.d.ts +21 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.js +128 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3769-recordings.d.ts +21 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3769-recordings.js +126 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3769-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +10 -4
- package/src/lib/layout-language/ai-layout-compiler.js +38 -19
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.d.ts +7 -5
- package/src/lib/layout-language/ai-layout-frame.js +12 -9
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-gaps.js +3 -1
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
- package/src/lib/layout-language/ai-layout-labels.d.ts +70 -0
- package/src/lib/layout-language/ai-layout-labels.js +162 -0
- package/src/lib/layout-language/ai-layout-labels.js.map +1 -0
- package/src/lib/layout-language/ai-layout-links.js +5 -2
- package/src/lib/layout-language/ai-layout-links.js.map +1 -1
- package/src/lib/layout-language/ai-layout-listings.d.ts +14 -1
- package/src/lib/layout-language/ai-layout-listings.js +32 -5
- package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
- package/src/lib/model/ai-build-event-page.d.ts +70 -0
- package/src/lib/model/ai-build-event-page.js +145 -0
- package/src/lib/model/ai-build-event-page.js.map +1 -0
- package/src/lib/model/ai-build-job.d.ts +21 -0
- package/src/lib/model/ai-build-job.js +39 -0
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +25 -14
- package/src/lib/model/ai-build-plan.js +33 -23
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-job-activity.js +3 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +10 -2
- package/src/lib/model/ai-jobs.types.js +3 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-plan-capabilities.d.ts +8 -0
- package/src/lib/model/ai-plan-capabilities.js.map +1 -1
- package/src/lib/model/ai-site-inventory.d.ts +2 -0
- package/src/lib/model/ai-site-inventory.js +5 -0
- package/src/lib/model/ai-site-inventory.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +2 -2
- package/src/lib/model/ai-site-job.js +3 -3
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +25 -3
- package/src/lib/runtime/ai-doctrine-validators.js +53 -6
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.d.ts +6 -1
- package/src/lib/runtime/ai-doctrine.js +13 -9
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.d.ts +1 -1
- package/src/lib/runtime/ai-eval-live.js +1 -1
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +3 -1
- package/src/lib/runtime/ai-palette.generated.js +498 -10
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js +5 -3
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/runtime/ai-template-examples.js +8 -2
- package/src/lib/runtime/ai-template-examples.js.map +1 -1
- package/src/lib/server.js +8 -0
- package/src/lib/server.js.map +1 -1
|
@@ -0,0 +1,313 @@
|
|
|
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 { resolveSiteTimeZone } from "@aglyn/aglyn/app-utils/collection-entry-date";
|
|
18
|
+
import { pluginAiCapability, pluginAiCapabilityArgsProblems } from "@aglyn/aglyn/plugin-manager/plugin-ai-capabilities";
|
|
19
|
+
import { aiLayoutEventsPlacements, aiLayoutListingId } from "../layout-language/ai-layout-listings.js";
|
|
20
|
+
import { aiBuildItemOutputNote } from "../model/ai-build-job.js";
|
|
21
|
+
import { aiSiteWords } from "../model/ai-site-job.js";
|
|
22
|
+
import { aiModelForStep } from "../providers/routing.js";
|
|
23
|
+
import { aiOriginJobId } from "./ai-job-draft-ids.js";
|
|
24
|
+
import { aiUnspentOutcome } from "./ai-job-generation.js";
|
|
25
|
+
import { aiPluginDraftAdmissionRefusal, aiPluginDraftWriter } from "./ai-job-plugin-drafts.js";
|
|
26
|
+
/**
|
|
27
|
+
* A GUIDED START'S FIRST EVENTS (AGL-3755).
|
|
28
|
+
*
|
|
29
|
+
* A guided start for a business that holds dated events — a venue, a club, a
|
|
30
|
+
* studio with classes, a meetup, a church, a theater — drafts its first few
|
|
31
|
+
* upcoming events and lists them on the page about them, so the site opens
|
|
32
|
+
* with its calendar rather than with events written out as copy.
|
|
33
|
+
*
|
|
34
|
+
* Nothing here knows what an event is. The events-calendar plugin owns the
|
|
35
|
+
* `event` operation (its capability on `core.ai-capabilities`) and the
|
|
36
|
+
* `event` writer (on `core.resource-drafts`); this module plans its items and
|
|
37
|
+
* runs them exactly as a build does, so every rule is the owner's:
|
|
38
|
+
*
|
|
39
|
+
* - THE GATE is the build's: the operation as `aiBuildOps` admits it on this
|
|
40
|
+
* site and plan (the `eventCalendar` add-on, the plugin on and released,
|
|
41
|
+
* a Free start only if the operation allows it), and the writer admits
|
|
42
|
+
* this member now. A start that cannot have events plans none, and its
|
|
43
|
+
* plan's turn is told nothing about them.
|
|
44
|
+
* - THE PLAN is told only for a brief that reads as event-led
|
|
45
|
+
* ({@link aiSiteEventLed}), how many (at most {@link AI_SITE_EVENTS_MAX}),
|
|
46
|
+
* today's date, the business's time zone where the site or workspace has
|
|
47
|
+
* one, and that the events are named in the uses of a section the plan
|
|
48
|
+
* already has, so the plan's section caps stay its budget.
|
|
49
|
+
* - THE BUILD writes each through the owner's writer before the pages,
|
|
50
|
+
* published where the member may publish, with its stock cover; the site's
|
|
51
|
+
* own zone wins over the plan's, so an event lands in the business's time.
|
|
52
|
+
* - THE PAGES list them: the section that names them places the Event List
|
|
53
|
+
* (`events` listing), with the events that were written.
|
|
54
|
+
*/ /** The operation and resource the events-calendar plugin registers; named, never imported. */ export const AI_SITE_EVENT_OP = 'event';
|
|
55
|
+
export const AI_SITE_EVENT_RESOURCE = 'event';
|
|
56
|
+
/** What the site's plugin list calls the events plugin, for a refusal a person reads. */ const EVENTS_PLUGIN_LABEL = 'Events';
|
|
57
|
+
/** How many events a start drafts: its first few. */ export const AI_SITE_EVENTS_MAX = 3;
|
|
58
|
+
/** The unit input an event unit's item travels in. */ export const AI_SITE_EVENT_INPUT = 'siteEvent';
|
|
59
|
+
/** What the ledger calls an event unit when its item names none. */ export const AI_SITE_EVENT_LABEL = 'An event';
|
|
60
|
+
/**
|
|
61
|
+
* A brief that reads as a business holding dated events. A gate on whether
|
|
62
|
+
* the plan's turn mentions events at all, not the decision: the plan is told
|
|
63
|
+
* to plan none for a business that holds none.
|
|
64
|
+
*/ const EVENT_LED = /\b(venues?|clubs?|class(?:es)?|meetups?|meet-ups?|church(?:es)?|parish|congregation|ministr(?:y|ies)|theat(?:re|er)s?|playhouse|concerts?|live music|gigs?|workshops?|festivals?|comedy|open mic|dance|choir|(?:neighborhood|civic|recreation) (?:center|centre)|associations?|local groups?|events?|tastings?|book club|trivia|seminars?|retreats?|lessons|courses?|performances?|shows)\b/i;
|
|
65
|
+
/** Whether a site start's brief and answers read as an event-led business. */ export function aiSiteEventLed(job) {
|
|
66
|
+
var _job_brief;
|
|
67
|
+
const { about } = aiSiteWords(job.inputs);
|
|
68
|
+
return EVENT_LED.test(`${about} ${(_job_brief = job.brief) != null ? _job_brief : ''}`);
|
|
69
|
+
}
|
|
70
|
+
/** The site start's events, as its plan holds them: the `event` items, its first few. */ export function aiSitePlanEvents(plan) {
|
|
71
|
+
var _plan_items;
|
|
72
|
+
return ((_plan_items = plan.items) != null ? _plan_items : []).filter((item)=>item.op === AI_SITE_EVENT_OP).slice(0, AI_SITE_EVENTS_MAX);
|
|
73
|
+
}
|
|
74
|
+
/** The site's or its workspace's own zone, or `null` where neither names one. */ export function aiSiteEventZone(org, host) {
|
|
75
|
+
const zone = resolveSiteTimeZone(org, host);
|
|
76
|
+
return [
|
|
77
|
+
host == null ? void 0 : host.timeZone,
|
|
78
|
+
org == null ? void 0 : org.timeZone
|
|
79
|
+
].some((said)=>String(said != null ? said : '').trim() === zone) ? zone : null;
|
|
80
|
+
}
|
|
81
|
+
/** Whether this member may have events drafted on the job's site now; the writer's sentence where not. */ export async function aiSiteEventRefusal(context, deps = {}) {
|
|
82
|
+
var _context_org, _deps_admission, _ref;
|
|
83
|
+
const { job } = context;
|
|
84
|
+
if (!job.hostId) return 'Open the site first.';
|
|
85
|
+
const admission = {
|
|
86
|
+
firestore: context.firestore,
|
|
87
|
+
orgId: job.orgId,
|
|
88
|
+
hostId: job.hostId,
|
|
89
|
+
inputs: {},
|
|
90
|
+
org: (_context_org = context.org) != null ? _context_org : null,
|
|
91
|
+
uid: job.createdBy
|
|
92
|
+
};
|
|
93
|
+
const refused = await ((_deps_admission = deps.admission) != null ? _deps_admission : aiPluginDraftAdmissionRefusal)(admission, _extends({
|
|
94
|
+
kind: 'site',
|
|
95
|
+
drafts: [
|
|
96
|
+
{
|
|
97
|
+
resource: AI_SITE_EVENT_RESOURCE,
|
|
98
|
+
label: EVENTS_PLUGIN_LABEL
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}, deps.writerFor ? {
|
|
102
|
+
writerFor: deps.writerFor
|
|
103
|
+
} : {}, {
|
|
104
|
+
now: context.now
|
|
105
|
+
}));
|
|
106
|
+
return (_ref = refused == null ? void 0 : refused.error) != null ? _ref : null;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* What a site start may plan of events (AGL-3755): `null` unless the job is
|
|
110
|
+
* a site start whose brief reads as event-led, the build's gate admits the
|
|
111
|
+
* `event` operation on this site and plan, and the writer admits this member.
|
|
112
|
+
*/ export async function readAiSiteEventPlanning(input, deps = {}) {
|
|
113
|
+
const { job } = input;
|
|
114
|
+
if (job.kind !== 'site' || !job.hostId || !aiSiteEventLed(job)) return null;
|
|
115
|
+
try {
|
|
116
|
+
var _deps_refusal, _data;
|
|
117
|
+
const event = (await input.readOps()).get(AI_SITE_EVENT_OP);
|
|
118
|
+
if (!event) return null;
|
|
119
|
+
const refused = await ((_deps_refusal = deps.refusal) != null ? _deps_refusal : aiSiteEventRefusal)({
|
|
120
|
+
firestore: input.firestore,
|
|
121
|
+
org: input.org,
|
|
122
|
+
now: input.now,
|
|
123
|
+
job
|
|
124
|
+
});
|
|
125
|
+
if (refused) return null;
|
|
126
|
+
const host = (_data = (await input.firestore.collection('hosts').doc(job.hostId).get()).data()) != null ? _data : null;
|
|
127
|
+
return {
|
|
128
|
+
ops: new Map([
|
|
129
|
+
[
|
|
130
|
+
AI_SITE_EVENT_OP,
|
|
131
|
+
event
|
|
132
|
+
]
|
|
133
|
+
]),
|
|
134
|
+
timeZone: aiSiteEventZone(input.org, host)
|
|
135
|
+
};
|
|
136
|
+
} catch (error) {
|
|
137
|
+
// Events are a start's extra: a read that fails plans none, and the start goes on.
|
|
138
|
+
console.warn('ai site events: not offered', {
|
|
139
|
+
orgId: job.orgId,
|
|
140
|
+
jobId: job.$id,
|
|
141
|
+
error: String(error)
|
|
142
|
+
});
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** The lines a site plan's turn is told where it may plan events. */ export function aiSiteEventPlanLines(planning, now) {
|
|
147
|
+
var _event_intents_;
|
|
148
|
+
const event = planning.ops.get(AI_SITE_EVENT_OP);
|
|
149
|
+
if (!event) return [];
|
|
150
|
+
const zone = planning.timeZone;
|
|
151
|
+
const args = Object.entries(event.argsSchema.properties).map(([name, property])=>{
|
|
152
|
+
var _event_argsSchema_required;
|
|
153
|
+
const required = ((_event_argsSchema_required = event.argsSchema.required) == null ? void 0 : _event_argsSchema_required.includes(name)) ? '' : ' (optional)';
|
|
154
|
+
return `${name}: ${property.type}${required} — ${property.description}`;
|
|
155
|
+
});
|
|
156
|
+
return [
|
|
157
|
+
`If this business holds dated events (a venue, a club, a studio's classes, a meetup, a church's services, a theater's shows), plan its first ${AI_SITE_EVENTS_MAX} upcoming events in items, each with op ${AI_SITE_EVENT_OP}, dated after today within the next two months, on days and at hours it would hold them. A day, date or time the brief gives is kept exactly as given; never date an event against it. If it holds none, leave items empty.`,
|
|
158
|
+
zone ? `The business's time zone is ${zone}: every event's timezone is ${zone}.` : "Every event's timezone is the IANA zone of the business's city.",
|
|
159
|
+
"Put every event's new:<name> in the uses of the one section that lists them, on the page about the events or else the home page: the platform lists them there. That section is one of the sections the plan already has, never an extra one.",
|
|
160
|
+
`- ${event.op}: one event; ${(_event_intents_ = event.intents[0]) != null ? _event_intents_ : 'a dated event'}. Args: { ${args.join('; ')} }.`,
|
|
161
|
+
`Today is ${now.toISOString().slice(0, 10)} (UTC). Every event falls after today.`
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* What a site plan with events is held to (AGL-3755): its items are only
|
|
166
|
+
* events, its first few, with arguments the operation's schema admits, each
|
|
167
|
+
* named in a section's uses, and built after nothing.
|
|
168
|
+
*/ export function aiSiteEventItemsCheck(planning) {
|
|
169
|
+
return (plan)=>{
|
|
170
|
+
var _plan_items;
|
|
171
|
+
const items = (_plan_items = plan.items) != null ? _plan_items : [];
|
|
172
|
+
if (!items.length) return [];
|
|
173
|
+
const event = planning == null ? void 0 : planning.ops.get(AI_SITE_EVENT_OP);
|
|
174
|
+
const fail = (message)=>[
|
|
175
|
+
{
|
|
176
|
+
rule: null,
|
|
177
|
+
code: 'plan-job-shape',
|
|
178
|
+
message
|
|
179
|
+
}
|
|
180
|
+
];
|
|
181
|
+
if (!event) return fail('This site plan makes no items: leave items empty.');
|
|
182
|
+
const other = items.find((item)=>item.op !== AI_SITE_EVENT_OP);
|
|
183
|
+
if (other) return fail(`items[${items.indexOf(other)}] is "${other.op}": a site plan's items are only ${AI_SITE_EVENT_OP} items.`);
|
|
184
|
+
if (items.length > AI_SITE_EVENTS_MAX) return fail(`Plan at most ${AI_SITE_EVENTS_MAX} events, not ${items.length}.`);
|
|
185
|
+
const uses = new Set(plan.screens.flatMap((screen)=>screen.sections.flatMap((section)=>section.uses.map((ref)=>ref.trim().toLowerCase()))));
|
|
186
|
+
for (const [index, item] of items.entries()){
|
|
187
|
+
const problems = pluginAiCapabilityArgsProblems(event.argsSchema, item.args);
|
|
188
|
+
if (problems.length) return fail(`items[${index}] ("${item.name}"): ${problems.join('; ')}.`);
|
|
189
|
+
if (item.dependsOn.length) return fail(`items[${index}] ("${item.name}") depends on nothing: leave its dependsOn empty.`);
|
|
190
|
+
if (!uses.has(`new:${item.name}`.trim().toLowerCase())) {
|
|
191
|
+
return fail(`items[${index}] ("${item.name}") is listed nowhere: put new:${item.name} in the uses of the section that lists the events.`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return [];
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/** The item an event unit's derived job carries. */ export function aiSiteEventItemOf(inputs) {
|
|
198
|
+
const raw = inputs == null ? void 0 : inputs[AI_SITE_EVENT_INPUT];
|
|
199
|
+
return raw && typeof raw.name === 'string' && raw.name.trim() && raw.args && typeof raw.args === 'object' ? raw : null;
|
|
200
|
+
}
|
|
201
|
+
/** The events this start wrote, by their output's draft resource. */ function eventOutputs(outputs) {
|
|
202
|
+
return outputs.filter((output)=>output.resource === 'draft' && output.draftResource === AI_SITE_EVENT_RESOURCE);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* The site's events as its pages list them (AGL-3755): one `events` listing
|
|
206
|
+
* naming the events this start wrote, placed in each section whose plan
|
|
207
|
+
* names any of them. None where no event was written or no section names one.
|
|
208
|
+
*/ export function aiSiteEventListings(input) {
|
|
209
|
+
return aiEventsListings({
|
|
210
|
+
written: eventOutputs(input.outputs).map((output)=>output.label),
|
|
211
|
+
planned: input.items.map((item)=>item.name),
|
|
212
|
+
screens: input.screens
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* The `events` listing for events a job wrote (AGL-3755): placed in each
|
|
217
|
+
* section whose plan names any event the job planned, naming the ones it
|
|
218
|
+
* wrote. None where it wrote none or no section names one. A guided start's
|
|
219
|
+
* and a build's alike.
|
|
220
|
+
*/ export function aiEventsListings(input) {
|
|
221
|
+
const written = input.written.filter(Boolean);
|
|
222
|
+
if (!written.length || !input.planned.length) return [];
|
|
223
|
+
const placements = aiLayoutEventsPlacements(input.planned, input.screens);
|
|
224
|
+
if (!placements.length) return [];
|
|
225
|
+
return [
|
|
226
|
+
{
|
|
227
|
+
id: aiLayoutListingId('events'),
|
|
228
|
+
kind: 'events',
|
|
229
|
+
name: 'the events',
|
|
230
|
+
records: written.slice(0, 12),
|
|
231
|
+
placements
|
|
232
|
+
}
|
|
233
|
+
];
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* An event unit's pass: the item's content from the owner's capability, the
|
|
237
|
+
* site's own time zone over the plan's, held to the owner's check, and
|
|
238
|
+
* written by the owner's writer as the member who started the job. No model
|
|
239
|
+
* runs and nothing is spent.
|
|
240
|
+
*/ export function createAiSiteEventRunner(deps = {}) {
|
|
241
|
+
var _deps_writerFor, _deps_capability;
|
|
242
|
+
const writerFor = (_deps_writerFor = deps.writerFor) != null ? _deps_writerFor : aiPluginDraftWriter;
|
|
243
|
+
const capabilityOf = (_deps_capability = deps.capability) != null ? _deps_capability : ()=>{
|
|
244
|
+
var _ref;
|
|
245
|
+
var _pluginAiCapability;
|
|
246
|
+
return (_ref = (_pluginAiCapability = pluginAiCapability(AI_SITE_EVENT_OP)) == null ? void 0 : _pluginAiCapability.capability) != null ? _ref : null;
|
|
247
|
+
};
|
|
248
|
+
return async (context)=>{
|
|
249
|
+
var _ref, _data, _ref1, _checked_problems_, _context_org;
|
|
250
|
+
const { job } = context;
|
|
251
|
+
const model = (_ref = context.modelFor == null ? void 0 : context.modelFor.call(context, 'job.plan')) != null ? _ref : aiModelForStep('job.plan');
|
|
252
|
+
const unspent = aiUnspentOutcome(model);
|
|
253
|
+
const item = aiSiteEventItemOf(job.inputs);
|
|
254
|
+
const writer = writerFor(AI_SITE_EVENT_RESOURCE);
|
|
255
|
+
const capability = capabilityOf();
|
|
256
|
+
if (!item || !writer || !capability || !job.hostId) {
|
|
257
|
+
return _extends({}, unspent, {
|
|
258
|
+
failure: 'Events are not available on this site.'
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
const host = (_data = (await context.firestore.collection('hosts').doc(job.hostId).get()).data()) != null ? _data : null;
|
|
262
|
+
const zone = aiSiteEventZone(context.org, host);
|
|
263
|
+
const content = _extends({}, (_ref1 = capability.draftContent == null ? void 0 : capability.draftContent.call(capability, {
|
|
264
|
+
name: item.name,
|
|
265
|
+
args: item.args
|
|
266
|
+
}, {
|
|
267
|
+
hostId: job.hostId,
|
|
268
|
+
dependencies: {}
|
|
269
|
+
})) != null ? _ref1 : item.args, zone ? {
|
|
270
|
+
timezone: zone
|
|
271
|
+
} : {});
|
|
272
|
+
const checked = writer.check(content, {
|
|
273
|
+
hostId: job.hostId
|
|
274
|
+
});
|
|
275
|
+
if (checked.ok === false) return _extends({}, unspent, {
|
|
276
|
+
failure: (_checked_problems_ = checked.problems[0]) != null ? _checked_problems_ : 'The event could not be read.'
|
|
277
|
+
});
|
|
278
|
+
const written = await writer.write({
|
|
279
|
+
orgId: job.orgId,
|
|
280
|
+
hostId: job.hostId,
|
|
281
|
+
uid: job.createdBy,
|
|
282
|
+
org: (_context_org = context.org) != null ? _context_org : null,
|
|
283
|
+
now: context.now,
|
|
284
|
+
act: {
|
|
285
|
+
id: aiOriginJobId(job),
|
|
286
|
+
business: aiSiteWords(job.inputs).about || job.brief || ''
|
|
287
|
+
},
|
|
288
|
+
id: job.$id,
|
|
289
|
+
name: item.name,
|
|
290
|
+
content
|
|
291
|
+
});
|
|
292
|
+
if (written.ok === false) return _extends({}, unspent, {
|
|
293
|
+
failure: written.error
|
|
294
|
+
});
|
|
295
|
+
return _extends({}, unspent, {
|
|
296
|
+
outputs: [
|
|
297
|
+
{
|
|
298
|
+
resource: 'draft',
|
|
299
|
+
id: written.id,
|
|
300
|
+
versionId: written.versionId,
|
|
301
|
+
hostId: job.hostId,
|
|
302
|
+
label: written.name,
|
|
303
|
+
draftResource: AI_SITE_EVENT_RESOURCE,
|
|
304
|
+
note: aiBuildItemOutputNote(capability, written)
|
|
305
|
+
}
|
|
306
|
+
]
|
|
307
|
+
});
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
export const runAiSiteEventUnit = createAiSiteEventRunner();
|
|
311
|
+
/** ASSUMED: an event's pass — one draft write and its cover photo's search and copy, no model. */ export const AI_SITE_EVENT_MINIMUM_MS = 45000;
|
|
312
|
+
|
|
313
|
+
//# sourceMappingURL=ai-job-site-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-site-events.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 { resolveSiteTimeZone } from '@aglyn/aglyn/app-utils/collection-entry-date'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport {\n pluginAiCapability,\n pluginAiCapabilityArgsProblems,\n type PluginAiCapability,\n} from '@aglyn/aglyn/plugin-manager/plugin-ai-capabilities'\nimport {\n aiLayoutEventsPlacements,\n aiLayoutListingId,\n type AiLayoutListing,\n type AiLayoutListingScreen,\n} from '../layout-language/ai-layout-listings'\nimport { aiBuildItemOutputNote, type AiBuildOps } from '../model/ai-build-job'\nimport type { AiBuildItem, AiBuildPlan } from '../model/ai-build-plan'\nimport type { AiJob, AiJobOutput } from '../model/ai-jobs.types'\nimport { aiSiteWords } from '../model/ai-site-job'\nimport { aiModelForStep } from '../providers/routing'\nimport type { AiDoctrineViolation } from '../runtime/ai-doctrine-validators'\nimport type { AiJobAdmissionContext } from './ai-job-admission'\nimport { aiOriginJobId } from './ai-job-draft-ids'\nimport { aiUnspentOutcome } from './ai-job-generation'\nimport {\n aiPluginDraftAdmissionRefusal,\n aiPluginDraftWriter,\n type AiPluginDraftWriterLookup,\n} from './ai-job-plugin-drafts'\nimport type { AiJobStepContext, AiJobStepOutcome, AiJobStepRunner } from './ai-job-text-step'\n\n/**\n * A GUIDED START'S FIRST EVENTS (AGL-3755).\n *\n * A guided start for a business that holds dated events — a venue, a club, a\n * studio with classes, a meetup, a church, a theater — drafts its first few\n * upcoming events and lists them on the page about them, so the site opens\n * with its calendar rather than with events written out as copy.\n *\n * Nothing here knows what an event is. The events-calendar plugin owns the\n * `event` operation (its capability on `core.ai-capabilities`) and the\n * `event` writer (on `core.resource-drafts`); this module plans its items and\n * runs them exactly as a build does, so every rule is the owner's:\n *\n * - THE GATE is the build's: the operation as `aiBuildOps` admits it on this\n * site and plan (the `eventCalendar` add-on, the plugin on and released,\n * a Free start only if the operation allows it), and the writer admits\n * this member now. A start that cannot have events plans none, and its\n * plan's turn is told nothing about them.\n * - THE PLAN is told only for a brief that reads as event-led\n * ({@link aiSiteEventLed}), how many (at most {@link AI_SITE_EVENTS_MAX}),\n * today's date, the business's time zone where the site or workspace has\n * one, and that the events are named in the uses of a section the plan\n * already has, so the plan's section caps stay its budget.\n * - THE BUILD writes each through the owner's writer before the pages,\n * published where the member may publish, with its stock cover; the site's\n * own zone wins over the plan's, so an event lands in the business's time.\n * - THE PAGES list them: the section that names them places the Event List\n * (`events` listing), with the events that were written.\n */\n\n/** The operation and resource the events-calendar plugin registers; named, never imported. */\nexport const AI_SITE_EVENT_OP = 'event'\nexport const AI_SITE_EVENT_RESOURCE = 'event'\n\n/** What the site's plugin list calls the events plugin, for a refusal a person reads. */\nconst EVENTS_PLUGIN_LABEL = 'Events'\n\n/** How many events a start drafts: its first few. */\nexport const AI_SITE_EVENTS_MAX = 3\n\n/** The unit input an event unit's item travels in. */\nexport const AI_SITE_EVENT_INPUT = 'siteEvent'\n\n/** What the ledger calls an event unit when its item names none. */\nexport const AI_SITE_EVENT_LABEL = 'An event'\n\n/**\n * A brief that reads as a business holding dated events. A gate on whether\n * the plan's turn mentions events at all, not the decision: the plan is told\n * to plan none for a business that holds none.\n */\nconst EVENT_LED =\n /\\b(venues?|clubs?|class(?:es)?|meetups?|meet-ups?|church(?:es)?|parish|congregation|ministr(?:y|ies)|theat(?:re|er)s?|playhouse|concerts?|live music|gigs?|workshops?|festivals?|comedy|open mic|dance|choir|(?:neighborhood|civic|recreation) (?:center|centre)|associations?|local groups?|events?|tastings?|book club|trivia|seminars?|retreats?|lessons|courses?|performances?|shows)\\b/i\n\n/** Whether a site start's brief and answers read as an event-led business. */\nexport function aiSiteEventLed(job: Pick<AiJob, 'brief' | 'inputs'>): boolean {\n const { about } = aiSiteWords(job.inputs)\n return EVENT_LED.test(`${about} ${job.brief ?? ''}`)\n}\n\n/** The site start's events, as its plan holds them: the `event` items, its first few. */\nexport function aiSitePlanEvents(plan: Pick<AiBuildPlan, 'items'>): AiBuildItem[] {\n return (plan.items ?? []).filter((item) => item.op === AI_SITE_EVENT_OP).slice(0, AI_SITE_EVENTS_MAX)\n}\n\n/** What a site start may plan of events here, or `null` where it plans none. */\nexport interface AiSiteEventPlanning {\n /** The `event` operation alone, as the build's gate admits it. */\n ops: AiBuildOps\n /** The business's IANA zone where the site or its workspace has one. */\n timeZone: string | null\n}\n\n/** The site's or its workspace's own zone, or `null` where neither names one. */\nexport function aiSiteEventZone(\n org: { timeZone?: string } | null | undefined,\n host: { timeZone?: string } | null | undefined,\n): string | null {\n const zone = resolveSiteTimeZone(org, host)\n return [host?.timeZone, org?.timeZone].some((said) => String(said ?? '').trim() === zone) ? zone : null\n}\n\n/** Whether this member may have events drafted on the job's site now; the writer's sentence where not. */\nexport async function aiSiteEventRefusal(\n context: Pick<AiJobStepContext, 'firestore' | 'org' | 'now'> & { job: Pick<AiJob, 'orgId' | 'hostId' | 'createdBy'> },\n deps: { admission?: typeof aiPluginDraftAdmissionRefusal; writerFor?: AiPluginDraftWriterLookup } = {},\n): Promise<string | null> {\n const { job } = context\n if (!job.hostId) return 'Open the site first.'\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_EVENT_RESOURCE, label: EVENTS_PLUGIN_LABEL }],\n ...(deps.writerFor ? { writerFor: deps.writerFor } : {}),\n now: context.now,\n })\n return refused?.error ?? null\n}\n\n/**\n * What a site start may plan of events (AGL-3755): `null` unless the job is\n * a site start whose brief reads as event-led, the build's gate admits the\n * `event` operation on this site and plan, and the writer admits this member.\n */\nexport async function readAiSiteEventPlanning(\n input: {\n job: AiJob\n org: Partial<AglynOrgBilling> | null\n firestore: FirebaseFirestore.Firestore\n now: Date\n /** The build's gated operations for the job's site. */\n readOps: () => Promise<AiBuildOps>\n },\n deps: { refusal?: typeof aiSiteEventRefusal } = {},\n): Promise<AiSiteEventPlanning | null> {\n const { job } = input\n if (job.kind !== 'site' || !job.hostId || !aiSiteEventLed(job)) return null\n try {\n const event = (await input.readOps()).get(AI_SITE_EVENT_OP)\n if (!event) return null\n const refused = await (deps.refusal ?? aiSiteEventRefusal)({ firestore: input.firestore, org: input.org, now: input.now, job })\n if (refused) return null\n const host = (await input.firestore.collection('hosts').doc(job.hostId).get()).data() ?? null\n return {\n ops: new Map([[AI_SITE_EVENT_OP, event]]),\n timeZone: aiSiteEventZone(input.org as { timeZone?: string } | null, host as { timeZone?: string } | null),\n }\n } catch (error) {\n // Events are a start's extra: a read that fails plans none, and the start goes on.\n console.warn('ai site events: not offered', { orgId: job.orgId, jobId: job.$id, error: String(error) })\n return null\n }\n}\n\n/** The lines a site plan's turn is told where it may plan events. */\nexport function aiSiteEventPlanLines(planning: AiSiteEventPlanning, now: Date): string[] {\n const event = planning.ops.get(AI_SITE_EVENT_OP)\n if (!event) return []\n const zone = planning.timeZone\n const args = Object.entries(event.argsSchema.properties).map(([name, property]) => {\n const required = event.argsSchema.required?.includes(name) ? '' : ' (optional)'\n return `${name}: ${property.type}${required} — ${property.description}`\n })\n return [\n `If this business holds dated events (a venue, a club, a studio's classes, a meetup, a church's services, a theater's shows), plan its first ${AI_SITE_EVENTS_MAX} upcoming events in items, each with op ${AI_SITE_EVENT_OP}, dated after today within the next two months, on days and at hours it would hold them. A day, date or time the brief gives is kept exactly as given; never date an event against it. If it holds none, leave items empty.`,\n zone\n ? `The business's time zone is ${zone}: every event's timezone is ${zone}.`\n : \"Every event's timezone is the IANA zone of the business's city.\",\n \"Put every event's new:<name> in the uses of the one section that lists them, on the page about the events or else the home page: the platform lists them there. That section is one of the sections the plan already has, never an extra one.\",\n `- ${event.op}: one event; ${event.intents[0] ?? 'a dated event'}. Args: { ${args.join('; ')} }.`,\n `Today is ${now.toISOString().slice(0, 10)} (UTC). Every event falls after today.`,\n ]\n}\n\n/**\n * What a site plan with events is held to (AGL-3755): its items are only\n * events, its first few, with arguments the operation's schema admits, each\n * named in a section's uses, and built after nothing.\n */\nexport function aiSiteEventItemsCheck(planning: AiSiteEventPlanning | null): (plan: AiBuildPlan) => AiDoctrineViolation[] {\n return (plan) => {\n const items = plan.items ?? []\n if (!items.length) return []\n const event = planning?.ops.get(AI_SITE_EVENT_OP)\n const fail = (message: string): AiDoctrineViolation[] => [{ rule: null, code: 'plan-job-shape', message }]\n if (!event) return fail('This site plan makes no items: leave items empty.')\n const other = items.find((item) => item.op !== AI_SITE_EVENT_OP)\n if (other) return fail(`items[${items.indexOf(other)}] is \"${other.op}\": a site plan's items are only ${AI_SITE_EVENT_OP} items.`)\n if (items.length > AI_SITE_EVENTS_MAX) return fail(`Plan at most ${AI_SITE_EVENTS_MAX} events, not ${items.length}.`)\n const uses = new Set(plan.screens.flatMap((screen) => screen.sections.flatMap((section) => section.uses.map((ref) => ref.trim().toLowerCase()))))\n for (const [index, item] of items.entries()) {\n const problems = pluginAiCapabilityArgsProblems(event.argsSchema, item.args)\n if (problems.length) return fail(`items[${index}] (\"${item.name}\"): ${problems.join('; ')}.`)\n if (item.dependsOn.length) return fail(`items[${index}] (\"${item.name}\") depends on nothing: leave its dependsOn empty.`)\n if (!uses.has(`new:${item.name}`.trim().toLowerCase())) {\n return fail(`items[${index}] (\"${item.name}\") is listed nowhere: put new:${item.name} in the uses of the section that lists the events.`)\n }\n }\n return []\n }\n}\n\n/** The item an event unit's derived job carries. */\nexport function aiSiteEventItemOf(inputs: Readonly<Record<string, unknown>> | null | undefined): AiBuildItem | null {\n const raw = inputs?.[AI_SITE_EVENT_INPUT] as Partial<AiBuildItem> | undefined\n return raw && typeof raw.name === 'string' && raw.name.trim() && raw.args && typeof raw.args === 'object'\n ? (raw as AiBuildItem)\n : null\n}\n\n/** The events this start wrote, by their output's draft resource. */\nfunction eventOutputs(outputs: readonly AiJobOutput[]): AiJobOutput[] {\n return outputs.filter((output) => output.resource === 'draft' && output.draftResource === AI_SITE_EVENT_RESOURCE)\n}\n\n/**\n * The site's events as its pages list them (AGL-3755): one `events` listing\n * naming the events this start wrote, placed in each section whose plan\n * names any of them. None where no event was written or no section names one.\n */\nexport function aiSiteEventListings(input: {\n outputs: readonly AiJobOutput[]\n items: readonly AiBuildItem[]\n screens: readonly AiLayoutListingScreen[]\n}): AiLayoutListing[] {\n return aiEventsListings({\n written: eventOutputs(input.outputs).map((output) => output.label),\n planned: input.items.map((item) => item.name),\n screens: input.screens,\n })\n}\n\n/**\n * The `events` listing for events a job wrote (AGL-3755): placed in each\n * section whose plan names any event the job planned, naming the ones it\n * wrote. None where it wrote none or no section names one. A guided start's\n * and a build's alike.\n */\nexport function aiEventsListings(input: {\n written: readonly string[]\n planned: readonly string[]\n screens: readonly AiLayoutListingScreen[]\n}): AiLayoutListing[] {\n const written = input.written.filter(Boolean)\n if (!written.length || !input.planned.length) return []\n const placements = aiLayoutEventsPlacements(input.planned, input.screens)\n if (!placements.length) return []\n return [\n {\n id: aiLayoutListingId('events'),\n kind: 'events',\n name: 'the events',\n records: written.slice(0, 12),\n placements,\n },\n ]\n}\n\nexport interface AiSiteEventRunnerDeps {\n writerFor?: AiPluginDraftWriterLookup\n /** The `event` capability; the registry's otherwise. */\n capability?: () => PluginAiCapability | null\n}\n\n/**\n * An event unit's pass: the item's content from the owner's capability, the\n * site's own time zone over the plan's, held to the owner's check, and\n * written by the owner's writer as the member who started the job. No model\n * runs and nothing is spent.\n */\nexport function createAiSiteEventRunner(deps: AiSiteEventRunnerDeps = {}): AiJobStepRunner {\n const writerFor = deps.writerFor ?? aiPluginDraftWriter\n const capabilityOf = deps.capability ?? (() => pluginAiCapability(AI_SITE_EVENT_OP)?.capability ?? null)\n return async (context): Promise<AiJobStepOutcome> => {\n const { job } = context\n const model = context.modelFor?.('job.plan') ?? aiModelForStep('job.plan')\n const unspent = aiUnspentOutcome(model)\n const item = aiSiteEventItemOf(job.inputs)\n const writer = writerFor(AI_SITE_EVENT_RESOURCE)\n const capability = capabilityOf()\n if (!item || !writer || !capability || !job.hostId) {\n return { ...unspent, failure: 'Events are not available on this site.' }\n }\n const host = (await context.firestore.collection('hosts').doc(job.hostId).get()).data() ?? null\n const zone = aiSiteEventZone(context.org as { timeZone?: string } | null, host as { timeZone?: string } | null)\n const content = {\n ...(capability.draftContent?.({ name: item.name, args: item.args }, { hostId: job.hostId, dependencies: {} }) ?? item.args),\n // The event lands in the business's own time zone where the site names one.\n ...(zone ? { timezone: zone } : {}),\n }\n const checked = writer.check(content, { hostId: job.hostId })\n if (checked.ok === false) return { ...unspent, failure: checked.problems[0] ?? 'The event could not be read.' }\n const written = await 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 act: { id: aiOriginJobId(job), business: aiSiteWords(job.inputs).about || job.brief || '' },\n id: job.$id,\n name: item.name,\n content,\n })\n if (written.ok === false) return { ...unspent, failure: written.error }\n return {\n ...unspent,\n outputs: [\n {\n resource: 'draft',\n id: written.id,\n versionId: written.versionId,\n hostId: job.hostId,\n label: written.name,\n draftResource: AI_SITE_EVENT_RESOURCE,\n note: aiBuildItemOutputNote(capability, written),\n },\n ],\n }\n }\n}\n\nexport const runAiSiteEventUnit = createAiSiteEventRunner()\n\n/** ASSUMED: an event's pass — one draft write and its cover photo's search and copy, no model. */\nexport const AI_SITE_EVENT_MINIMUM_MS = 45_000\n"],"names":["resolveSiteTimeZone","pluginAiCapability","pluginAiCapabilityArgsProblems","aiLayoutEventsPlacements","aiLayoutListingId","aiBuildItemOutputNote","aiSiteWords","aiModelForStep","aiOriginJobId","aiUnspentOutcome","aiPluginDraftAdmissionRefusal","aiPluginDraftWriter","AI_SITE_EVENT_OP","AI_SITE_EVENT_RESOURCE","EVENTS_PLUGIN_LABEL","AI_SITE_EVENTS_MAX","AI_SITE_EVENT_INPUT","AI_SITE_EVENT_LABEL","EVENT_LED","aiSiteEventLed","job","about","inputs","test","brief","aiSitePlanEvents","plan","items","filter","item","op","slice","aiSiteEventZone","org","host","zone","timeZone","some","said","String","trim","aiSiteEventRefusal","context","deps","hostId","admission","firestore","orgId","uid","createdBy","refused","kind","drafts","resource","label","writerFor","now","error","readAiSiteEventPlanning","input","event","readOps","get","refusal","collection","doc","data","ops","Map","console","warn","jobId","$id","aiSiteEventPlanLines","planning","args","Object","entries","argsSchema","properties","map","name","property","required","includes","type","description","intents","join","toISOString","aiSiteEventItemsCheck","length","fail","message","rule","code","other","find","indexOf","uses","Set","screens","flatMap","screen","sections","section","ref","toLowerCase","index","problems","dependsOn","has","aiSiteEventItemOf","raw","eventOutputs","outputs","output","draftResource","aiSiteEventListings","aiEventsListings","written","planned","Boolean","placements","id","records","createAiSiteEventRunner","capabilityOf","capability","checked","model","modelFor","unspent","writer","failure","content","draftContent","dependencies","timezone","check","ok","write","act","business","versionId","note","runAiSiteEventUnit","AI_SITE_EVENT_MINIMUM_MS"],"mappings":"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"}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
-
import { type AiBuildPlanCreate, type AiBuildPlanScreen } from '../model/ai-build-plan';
|
|
17
|
+
import { type AiBuildItem, type AiBuildPlanCreate, type AiBuildPlanScreen } from '../model/ai-build-plan';
|
|
18
18
|
import type { AiJob, AiJobItemLedger, AiJobKind, AiJobOutput, AiJobOutputResource, AiJobPlan } from '../model/ai-jobs.types';
|
|
19
19
|
import { type AiBuildUnit } from '../model/ai-build-job';
|
|
20
20
|
import { type AiSiteJobInputs } from '../model/ai-site-job';
|
|
@@ -25,6 +25,7 @@ import { aiPublishGuidedSite, aiRestoreGuidedBlogEntry } from './ai-site-publish
|
|
|
25
25
|
import { aiPublishSitePosts, aiSiteContentRefusal } from './ai-job-site-content';
|
|
26
26
|
import { dropPluginSiteCache } from '@aglyn/aglyn/plugin-manager/plugin-site-cache';
|
|
27
27
|
import { aiSiteDatasetRefusal } from './ai-job-site-datasets';
|
|
28
|
+
import { aiSiteEventRefusal } from './ai-job-site-events';
|
|
28
29
|
import { aiSiteStorePagesRefusal, type AiSiteStorePagesInput } from './ai-job-site-store-pages';
|
|
29
30
|
import { type AiJobStepContext, type AiJobStepOutcome, type AiJobStepRunner } from './ai-job-text-step';
|
|
30
31
|
import { aiJobStepRunnerFor } from './ai-jobs';
|
|
@@ -90,7 +91,7 @@ import { aiJobStepRunnerFor } from './ai-jobs';
|
|
|
90
91
|
* cart and policy pages (`store`, AGL-3676) come after its planned pages,
|
|
91
92
|
* which keep the first claim on the site's pages allowance.
|
|
92
93
|
*/
|
|
93
|
-
export type AiSiteUnitKind = 'theme' | 'layout' | 'form' | 'component' | 'dataset' | 'posts' | 'products' | 'page' | 'store' | 'email';
|
|
94
|
+
export type AiSiteUnitKind = 'theme' | 'layout' | 'form' | 'component' | 'dataset' | 'event' | 'posts' | 'products' | 'page' | 'store' | 'email';
|
|
94
95
|
export interface AiSiteUnit {
|
|
95
96
|
kind: AiSiteUnitKind;
|
|
96
97
|
/** The job kind whose registered runner builds it. */
|
|
@@ -103,6 +104,8 @@ export interface AiSiteUnit {
|
|
|
103
104
|
screen?: AiBuildPlanScreen;
|
|
104
105
|
/** The creation a theme, layout or form unit builds. */
|
|
105
106
|
creation?: AiBuildPlanCreate;
|
|
107
|
+
/** The event an event unit drafts (AGL-3755). */
|
|
108
|
+
item?: AiBuildItem;
|
|
106
109
|
/** What the unit is, for the derived job's brief. */
|
|
107
110
|
label: string;
|
|
108
111
|
}
|
|
@@ -135,7 +138,7 @@ export declare const AI_SITE_MAX_PASSES: number;
|
|
|
135
138
|
* then the pages; then the welcome email, which is about the site rather
|
|
136
139
|
* than part of it.
|
|
137
140
|
*/
|
|
138
|
-
export declare function aiSiteJobUnits(plan: Pick<AiJobPlan, 'create' | 'screens'
|
|
141
|
+
export declare function aiSiteJobUnits(plan: Pick<AiJobPlan, 'create' | 'screens'> & Partial<Pick<AiJobPlan, 'items'>>, options?: {
|
|
139
142
|
welcomeEmail?: boolean;
|
|
140
143
|
content?: 'posts' | 'products' | null;
|
|
141
144
|
}): AiSiteUnit[];
|
|
@@ -308,6 +311,9 @@ export interface AiJobSiteStepDeps {
|
|
|
308
311
|
/** A dataset's pass and whether one may be made here (AGL-3616); specs hand in fakes. */
|
|
309
312
|
dataset?: AiJobStepRunner;
|
|
310
313
|
datasetRefusal?: typeof aiSiteDatasetRefusal;
|
|
314
|
+
/** An event's pass and whether one may be drafted here (AGL-3755); specs hand in fakes. */
|
|
315
|
+
event?: AiJobStepRunner;
|
|
316
|
+
eventRefusal?: typeof aiSiteEventRefusal;
|
|
311
317
|
/** The posts' publish once the pages are live, and the cache drop after it. */
|
|
312
318
|
publishPosts?: typeof aiPublishSitePosts;
|
|
313
319
|
dropCache?: typeof dropPluginSiteCache;
|