@aglyn/plugins-ai 1.0.0-beta.236 → 1.0.0-beta.238
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/components/ai-credits-prompt.component.d.ts +44 -0
- package/src/lib/components/ai-credits-prompt.component.js +117 -0
- package/src/lib/components/ai-credits-prompt.component.js.map +1 -0
- package/src/lib/components/ai-job-cancel.component.d.ts +38 -0
- package/src/lib/components/ai-job-cancel.component.js +156 -0
- package/src/lib/components/ai-job-cancel.component.js.map +1 -0
- package/src/lib/components/ai-job-plan.component.d.ts +5 -4
- package/src/lib/components/ai-job-plan.component.js +48 -12
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-job-requests.d.ts +10 -0
- package/src/lib/components/ai-job-requests.js +12 -4
- package/src/lib/components/ai-job-requests.js.map +1 -1
- package/src/lib/components/ai-jobs-list-page.component.js +141 -72
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +48 -9
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.js +68 -22
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-usage-strip.component.d.ts +19 -2
- package/src/lib/components/ai-usage-strip.component.js +40 -9
- package/src/lib/components/ai-usage-strip.component.js.map +1 -1
- package/src/lib/components/assist-build-card.component.d.ts +3 -1
- package/src/lib/components/assist-build-card.component.js +58 -7
- package/src/lib/components/assist-build-card.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +66 -22
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +89 -52
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/jobs/ai-job-admission.d.ts +22 -2
- package/src/lib/jobs/ai-job-admission.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.d.ts +24 -11
- package/src/lib/jobs/ai-job-build-step.js +54 -35
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-cancel.d.ts +71 -0
- package/src/lib/jobs/ai-job-cancel.js +102 -0
- package/src/lib/jobs/ai-job-cancel.js.map +1 -0
- package/src/lib/jobs/ai-job-component-step.js +4 -1
- package/src/lib/jobs/ai-job-component-step.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.js +2 -0
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-form-step.js +4 -1
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +2 -0
- package/src/lib/jobs/ai-job-layout-language.js +13 -1
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +6 -1
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.js +12 -0
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +13 -0
- package/src/lib/jobs/ai-job-page-step.js +46 -4
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +42 -0
- package/src/lib/jobs/ai-job-plan-step.js +118 -18
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +49 -3
- package/src/lib/jobs/ai-job-site-content.js +196 -37
- package/src/lib/jobs/ai-job-site-content.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.js +49 -16
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-template-step.js +4 -1
- package/src/lib/jobs/ai-job-template-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +8 -0
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +55 -13
- package/src/lib/jobs/ai-jobs.js +213 -41
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +194 -34
- package/src/lib/jobs/ai-layout-stock-photos.js +861 -146
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.d.ts +30 -0
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js +220 -0
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.d.ts +27 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js +151 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +33 -0
- package/src/lib/layout-language/ai-layout-compiler.js +381 -18
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-design.d.ts +6 -3
- package/src/lib/layout-language/ai-layout-design.js +5 -3
- package/src/lib/layout-language/ai-layout-design.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.d.ts +12 -0
- package/src/lib/layout-language/ai-layout-frame.js +58 -3
- 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-language.js +1 -1
- package/src/lib/layout-language/ai-layout-language.js.map +1 -1
- package/src/lib/layout-language/ai-layout-listings.d.ts +49 -1
- package/src/lib/layout-language/ai-layout-listings.js +117 -47
- package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +10 -1
- package/src/lib/layout-language/ai-layout-pictures.js +5 -2
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/layout-language/ai-layout-storefront.d.ts +82 -0
- package/src/lib/layout-language/ai-layout-storefront.js +223 -0
- package/src/lib/layout-language/ai-layout-storefront.js.map +1 -0
- package/src/lib/model/ai-build-job.d.ts +39 -0
- package/src/lib/model/ai-build-job.js +100 -1
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +8 -0
- package/src/lib/model/ai-build-progress.js +35 -0
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +249 -0
- package/src/lib/model/ai-credit-estimate.js +278 -0
- package/src/lib/model/ai-credit-estimate.js.map +1 -0
- package/src/lib/model/ai-job-cancel-copy.d.ts +44 -0
- package/src/lib/model/ai-job-cancel-copy.js +53 -0
- package/src/lib/model/ai-job-cancel-copy.js.map +1 -0
- package/src/lib/model/ai-job-failure-copy.d.ts +4 -1
- package/src/lib/model/ai-job-failure-copy.js +8 -3
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +35 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-site-build-progress.js +7 -4
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +76 -22
- package/src/lib/model/ai-site-job.js +125 -23
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.d.ts +4 -1
- package/src/lib/model/ai-site-kinds.js +35 -7
- package/src/lib/model/ai-site-kinds.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +37 -1
- package/src/lib/runtime/ai-doctrine-validators.js +161 -7
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +4 -3
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.d.ts +11 -0
- package/src/lib/runtime/ai-node-tree.js +26 -0
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +592 -14
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/server/ai-jobs-cancel.d.ts +13 -5
- package/src/lib/server/ai-jobs-cancel.js +27 -9
- package/src/lib/server/ai-jobs-cancel.js.map +1 -1
- package/src/lib/server/ai-jobs-gate.d.ts +12 -2
- package/src/lib/server/ai-jobs-gate.js +20 -9
- package/src/lib/server/ai-jobs-gate.js.map +1 -1
- package/src/lib/server/ai-jobs-resume.js +93 -35
- package/src/lib/server/ai-jobs-resume.js.map +1 -1
- package/src/lib/server/ai-jobs-route.js +45 -4
- package/src/lib/server/ai-jobs-route.js.map +1 -1
- package/src/lib/server/assist-chat.js +4 -1
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit.js +3 -1
- package/src/lib/server/assist-edit.js.map +1 -1
- package/src/lib/server/assist-music.d.ts +36 -0
- package/src/lib/server/assist-music.js +36 -0
- package/src/lib/server/assist-music.js.map +1 -0
- package/src/lib/server.js +6 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/usage/ai-usage-meter.d.ts +2 -1
- package/src/lib/usage/ai-usage-meter.js +17 -1
- package/src/lib/usage/ai-usage-meter.js.map +1 -1
- package/src/lib/usage/ai-usage-wire.d.ts +6 -0
- package/src/lib/usage/ai-usage-wire.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +7 -0
- package/src/lib/usage/assist-usage.js +14 -4
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/free-ai-credits-left.d.ts +7 -0
- package/src/lib/usage/free-ai-credits-left.js +4 -2
- package/src/lib/usage/free-ai-credits-left.js.map +1 -1
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* WHAT A JOB REALLY COSTS, NEXT TO WHAT IT CAN COST (AGL-3722).
|
|
19
|
+
*
|
|
20
|
+
* Every job's cost used to be quoted at its CEILING: each pass at the
|
|
21
|
+
* `AI_SITE_PASS_CREDITS` (50) a pass is reserved against. A Free Assist
|
|
22
|
+
* build of two pages, a quote form and a layout was quoted 600 credits and
|
|
23
|
+
* refused with 164 left, when builds like it measure about a fifth of that.
|
|
24
|
+
*
|
|
25
|
+
* So a job is now quoted as a RANGE: what it is likely to cost (the sum of
|
|
26
|
+
* the measured medians of what it builds), what it costs nine times in ten
|
|
27
|
+
* (the measured p90s), and its ceiling (the old figure, unchanged). The
|
|
28
|
+
* meter still charges actual use, and the reservation each pass takes is
|
|
29
|
+
* still the ceiling's 50 — only the words and the Free admission move:
|
|
30
|
+
* a Free job is admitted outright when its p90 fits what is left, and asked
|
|
31
|
+
* to be confirmed when it does not.
|
|
32
|
+
*
|
|
33
|
+
* This module is pure: the console, the doors and the specs read one table.
|
|
34
|
+
*/
|
|
35
|
+
/** One measured cost: the median and the 90th percentile, in credits. */
|
|
36
|
+
export interface AiMeasuredCredits {
|
|
37
|
+
median: number;
|
|
38
|
+
p90: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* MEASURED IN PRODUCTION, credits per unit, over every `done` AI job since
|
|
42
|
+
* 2026-10-01 (read 2026-10-09; a whole Free site job came to 148–160):
|
|
43
|
+
*
|
|
44
|
+
* page item 30 / 40 (a page of about `AI_MEASURED_PAGE_SECTIONS` sections, its listing included)
|
|
45
|
+
* form 29 / 31
|
|
46
|
+
* layout 23 / 24 (header and footer, one answer)
|
|
47
|
+
* theme 4 / 8 (a site start's look)
|
|
48
|
+
* plan step 21 / 70
|
|
49
|
+
*
|
|
50
|
+
* HOW TO RE-MEASURE: a read-only collection-group read of `aiJobs` in
|
|
51
|
+
* production where `status == 'done'` and `createdAt` is in the window; group
|
|
52
|
+
* each ledger row's (`items[]`) `creditsSpent` by its `op` (the site start's
|
|
53
|
+
* look is the `theme` row), take the plan step's `steps[].creditsSpent` for
|
|
54
|
+
* `plan`, and each job's `creditsSpent` for the whole-site band. Put each
|
|
55
|
+
* group's median and 90th percentile here, and the dates in
|
|
56
|
+
* `AI_MEASURED_AT`. `ai-credit-estimate.spec.ts` holds the table, the page
|
|
57
|
+
* scaling and the measured whole-site band together, so a re-measure that
|
|
58
|
+
* moves one moves the spec with it.
|
|
59
|
+
*/
|
|
60
|
+
export declare const AI_MEASURED_UNIT_CREDITS: {
|
|
61
|
+
readonly page: {
|
|
62
|
+
readonly median: 30;
|
|
63
|
+
readonly p90: 40;
|
|
64
|
+
};
|
|
65
|
+
readonly form: {
|
|
66
|
+
readonly median: 29;
|
|
67
|
+
readonly p90: 31;
|
|
68
|
+
};
|
|
69
|
+
readonly layout: {
|
|
70
|
+
readonly median: 23;
|
|
71
|
+
readonly p90: 24;
|
|
72
|
+
};
|
|
73
|
+
readonly theme: {
|
|
74
|
+
readonly median: 4;
|
|
75
|
+
readonly p90: 8;
|
|
76
|
+
};
|
|
77
|
+
readonly plan: {
|
|
78
|
+
readonly median: 21;
|
|
79
|
+
readonly p90: 70;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
/** When the table above was read, and from when its jobs ran. */
|
|
83
|
+
export declare const AI_MEASURED_AT: {
|
|
84
|
+
readonly read: "2026-10-09";
|
|
85
|
+
readonly since: "2026-10-01";
|
|
86
|
+
};
|
|
87
|
+
/** What a whole Free two-page site job measured at, in credits, in the same window. */
|
|
88
|
+
export declare const AI_MEASURED_FREE_SITE_CREDITS: {
|
|
89
|
+
readonly low: 148;
|
|
90
|
+
readonly high: 160;
|
|
91
|
+
};
|
|
92
|
+
/** The sections the measured page item is about: a page is scaled around it. */
|
|
93
|
+
export declare const AI_MEASURED_PAGE_SECTIONS = 5;
|
|
94
|
+
/**
|
|
95
|
+
* What a page of this many sections is likely to cost: the measured page
|
|
96
|
+
* scaled by its sections around the ~5-section median — about 6 credits a
|
|
97
|
+
* section, 8 at the p90.
|
|
98
|
+
*/
|
|
99
|
+
export declare function aiMeasuredPageCredits(sections: number): AiMeasuredCredits;
|
|
100
|
+
/**
|
|
101
|
+
* What a page costs when it is written ON ITS OWN — a page an Assist build or
|
|
102
|
+
* a page job writes, rather than one of a site start's pages (AGL-3722).
|
|
103
|
+
*
|
|
104
|
+
* The table's page item is a site start's: its pages are written one after
|
|
105
|
+
* another inside one job, the later ones reading the prompt the first one
|
|
106
|
+
* paid to write, so a 2-section second page measured 19–28. A page written on
|
|
107
|
+
* its own pays that prompt itself and reads the site it lands on, and costs
|
|
108
|
+
* what a site start's FIRST page costs: a 5-section home page measured 39–78
|
|
109
|
+
* (median 40, p90 78), standalone page jobs' writing step 82–105 for a
|
|
110
|
+
* 2-section page, and the Assist build pages of 2026-10-09 22–89 each. Quoting
|
|
111
|
+
* a build's pages at the site start's scaled median told a Free workspace
|
|
112
|
+
* "about 48 credits" for two 4-section pages (AGL-3722, 2026-10-10).
|
|
113
|
+
*
|
|
114
|
+
* So it is priced as a fixed cost — the prompt, the site, the listing — and a
|
|
115
|
+
* cost per section: a 4-section page about 40 (p90 78), a 5-section 46 (p90 90).
|
|
116
|
+
*/
|
|
117
|
+
export declare const AI_MEASURED_STANDALONE_PAGE: {
|
|
118
|
+
readonly fixed: {
|
|
119
|
+
readonly median: 16;
|
|
120
|
+
readonly p90: 30;
|
|
121
|
+
};
|
|
122
|
+
readonly perSection: {
|
|
123
|
+
readonly median: 6;
|
|
124
|
+
readonly p90: 12;
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
/** What a page of this many sections written on its own is likely to cost. */
|
|
128
|
+
export declare function aiMeasuredStandalonePageCredits(sections: number): AiMeasuredCredits;
|
|
129
|
+
/**
|
|
130
|
+
* What a creation costs when it is written ON ITS OWN (AGL-3722), where it
|
|
131
|
+
* measured apart from a site start's: an Assist build's layout came to 24–28
|
|
132
|
+
* (2026-10-09/10), against a site start's 23/24 written in its warm prompt.
|
|
133
|
+
* A form measured the same either way (25–30 on its own, 29/31 in a site
|
|
134
|
+
* start), so it keeps the table's figure.
|
|
135
|
+
*/
|
|
136
|
+
export declare const AI_MEASURED_STANDALONE_CREATION_CREDITS: {
|
|
137
|
+
readonly layout: {
|
|
138
|
+
readonly median: 26;
|
|
139
|
+
readonly p90: 30;
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* What one pass of a kind with no measurement of its own is likely to cost
|
|
144
|
+
* (a component, an email design, a template, an item another runner
|
|
145
|
+
* builds), written on its own: a component job's writing step measured
|
|
146
|
+
* 52–65, a form job's 37, a layout job's 63, and the Assist build of
|
|
147
|
+
* 2026-10-09 a welcome email at 46 and a products step at 60. Until
|
|
148
|
+
* 2026-10-10 this was a site start's form (29/31), which is written inside a
|
|
149
|
+
* site job's warm prompt and is the cheapest single answer there is.
|
|
150
|
+
*/
|
|
151
|
+
export declare const AI_MEASURED_PASS_CREDITS: AiMeasuredCredits;
|
|
152
|
+
/** A job's cost as it is quoted: likely, nine times in ten, and at its worst. */
|
|
153
|
+
export interface AiCreditRange {
|
|
154
|
+
/** The sum of the measured medians: what the job is likely to spend. */
|
|
155
|
+
likely: number;
|
|
156
|
+
/** The sum of the measured p90s: what a Free job is admitted on. */
|
|
157
|
+
p90: number;
|
|
158
|
+
/** Every pass at its reserve: what it can spend at most. */
|
|
159
|
+
ceiling: number;
|
|
160
|
+
}
|
|
161
|
+
export declare const AI_CREDIT_RANGE_ZERO: AiCreditRange;
|
|
162
|
+
/** Two ranges added. */
|
|
163
|
+
export declare function aiCreditRangeAdd(a: AiCreditRange, b: AiCreditRange): AiCreditRange;
|
|
164
|
+
/** A measured cost as a range with its own ceiling, the likely and p90 never above it. */
|
|
165
|
+
export declare function aiCreditRangeOf(measured: AiMeasuredCredits, ceiling: number): AiCreditRange;
|
|
166
|
+
/** A range whose likely and p90 are each at least the last, and at most the ceiling. */
|
|
167
|
+
export declare function aiCreditRangeOrdered(range: AiCreditRange): AiCreditRange;
|
|
168
|
+
/**
|
|
169
|
+
* The range as every surface quotes it: "About 106 credits (up to 650)".
|
|
170
|
+
* A job whose likely figure is its ceiling says one number.
|
|
171
|
+
*/
|
|
172
|
+
export declare function aiCreditRangeText(range: AiCreditRange): string;
|
|
173
|
+
/**
|
|
174
|
+
* Whether a Free job of this range needs the person's go-ahead before it
|
|
175
|
+
* starts (AGL-3722): its p90 is more than what is left. A job whose p90
|
|
176
|
+
* fits starts with no prompt; one that does not starts only when confirmed,
|
|
177
|
+
* builds as much as what is left pays for, and pauses with Resume.
|
|
178
|
+
*/
|
|
179
|
+
export declare function aiCreditsNeedConfirm(range: Pick<AiCreditRange, 'p90'>, left: number): boolean;
|
|
180
|
+
/** The smaller first build a Free job that does not fit is offered (AGL-3722). */
|
|
181
|
+
export interface AiCreditsSmaller {
|
|
182
|
+
/** "Build the home page first". */
|
|
183
|
+
label: string;
|
|
184
|
+
likely: number;
|
|
185
|
+
p90: number;
|
|
186
|
+
ceiling: number;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* What a Free job that does not fit what is left says before it starts, and
|
|
190
|
+
* the choices it offers: build what fits, a smaller first build, or Upgrade.
|
|
191
|
+
* The door that refuses an unconfirmed start answers with it (status 409,
|
|
192
|
+
* `code: AI_CREDITS_CONFIRM_CODE`), and the plan card and the guided start
|
|
193
|
+
* draw it from the same figures.
|
|
194
|
+
*/
|
|
195
|
+
export interface AiCreditsPrompt extends AiCreditRange {
|
|
196
|
+
left: number;
|
|
197
|
+
resetsOn: string;
|
|
198
|
+
smaller: AiCreditsSmaller | null;
|
|
199
|
+
}
|
|
200
|
+
/** The `code` a door answers an unconfirmed Free start with. */
|
|
201
|
+
export declare const AI_CREDITS_CONFIRM_CODE = "credits-confirm";
|
|
202
|
+
/** "Build what fits": the go-ahead for a Free job past what is left. */
|
|
203
|
+
export declare const AI_CREDITS_BUILD_WHAT_FITS = "Build what fits";
|
|
204
|
+
/** The smaller first build's button: "Build the home page first (about 64 credits)". */
|
|
205
|
+
export declare function aiCreditsSmallerText(smaller: Pick<AiCreditsSmaller, 'label' | 'likely'>): string;
|
|
206
|
+
/** Which way on a prompt offers, in the order it offers them. */
|
|
207
|
+
export type AiCreditsChoiceKey = 'fits' | 'smaller' | 'upgrade';
|
|
208
|
+
/** One way on, as its option button reads it: a short label and the figure under it (AGL-3722). */
|
|
209
|
+
export interface AiCreditsChoice {
|
|
210
|
+
key: AiCreditsChoiceKey;
|
|
211
|
+
label: string;
|
|
212
|
+
detail: string;
|
|
213
|
+
/** The one choice drawn as the primary button. */
|
|
214
|
+
recommended: boolean;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* The prompt's ways on, as its three option buttons read them (Zach,
|
|
218
|
+
* 2026-10-10: "more appealing and consistent"): build what fits, the smaller
|
|
219
|
+
* first build when there is one, and Upgrade when it can be offered — each a
|
|
220
|
+
* short sentence-case label with its figure under it. The smaller build is
|
|
221
|
+
* the recommended one when its p90 fits what is left, since it then finishes;
|
|
222
|
+
* otherwise building what fits is.
|
|
223
|
+
*/
|
|
224
|
+
export declare function aiCreditsChoices(prompt: Pick<AiCreditsPrompt, 'left' | 'smaller'>, offers: {
|
|
225
|
+
smaller: boolean;
|
|
226
|
+
upgrade: boolean;
|
|
227
|
+
}): AiCreditsChoice[];
|
|
228
|
+
/**
|
|
229
|
+
* The plain sentence (Zach, 2026-10-09): "This build is about N credits (up
|
|
230
|
+
* to M). You have L left, so it will build as much as it can and pause when
|
|
231
|
+
* your credits run out. You can upgrade or resume when they renew."
|
|
232
|
+
*/
|
|
233
|
+
export declare function aiCreditsPromptText(prompt: AiCreditsPrompt, noun?: 'build' | 'site'): string;
|
|
234
|
+
/**
|
|
235
|
+
* The record a confirmed Free start keeps (AGL-3722): who said go, when,
|
|
236
|
+
* what was left then and the range they were shown.
|
|
237
|
+
*/
|
|
238
|
+
export interface AiJobCreditsConfirmation extends AiCreditRange {
|
|
239
|
+
by: string;
|
|
240
|
+
at: Date | string;
|
|
241
|
+
left: number;
|
|
242
|
+
}
|
|
243
|
+
/** The confirmation a door keeps on the job for the prompt the person confirmed, or `null` when there was none. */
|
|
244
|
+
export declare function aiCreditsConfirmation(prompt: AiCreditsPrompt | null, by: string, at: Date): AiJobCreditsConfirmation | null;
|
|
245
|
+
/** The prompt a range needs against what is left, or `null` when it fits. */
|
|
246
|
+
export declare function aiCreditsPromptFor(range: AiCreditRange, credits: {
|
|
247
|
+
left: number;
|
|
248
|
+
resetsOn: string;
|
|
249
|
+
} | null | undefined, smaller?: AiCreditsSmaller | null): AiCreditsPrompt | null;
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /**
|
|
17
|
+
* WHAT A JOB REALLY COSTS, NEXT TO WHAT IT CAN COST (AGL-3722).
|
|
18
|
+
*
|
|
19
|
+
* Every job's cost used to be quoted at its CEILING: each pass at the
|
|
20
|
+
* `AI_SITE_PASS_CREDITS` (50) a pass is reserved against. A Free Assist
|
|
21
|
+
* build of two pages, a quote form and a layout was quoted 600 credits and
|
|
22
|
+
* refused with 164 left, when builds like it measure about a fifth of that.
|
|
23
|
+
*
|
|
24
|
+
* So a job is now quoted as a RANGE: what it is likely to cost (the sum of
|
|
25
|
+
* the measured medians of what it builds), what it costs nine times in ten
|
|
26
|
+
* (the measured p90s), and its ceiling (the old figure, unchanged). The
|
|
27
|
+
* meter still charges actual use, and the reservation each pass takes is
|
|
28
|
+
* still the ceiling's 50 — only the words and the Free admission move:
|
|
29
|
+
* a Free job is admitted outright when its p90 fits what is left, and asked
|
|
30
|
+
* to be confirmed when it does not.
|
|
31
|
+
*
|
|
32
|
+
* This module is pure: the console, the doors and the specs read one table.
|
|
33
|
+
*/ /** One measured cost: the median and the 90th percentile, in credits. */ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
34
|
+
/**
|
|
35
|
+
* MEASURED IN PRODUCTION, credits per unit, over every `done` AI job since
|
|
36
|
+
* 2026-10-01 (read 2026-10-09; a whole Free site job came to 148–160):
|
|
37
|
+
*
|
|
38
|
+
* page item 30 / 40 (a page of about `AI_MEASURED_PAGE_SECTIONS` sections, its listing included)
|
|
39
|
+
* form 29 / 31
|
|
40
|
+
* layout 23 / 24 (header and footer, one answer)
|
|
41
|
+
* theme 4 / 8 (a site start's look)
|
|
42
|
+
* plan step 21 / 70
|
|
43
|
+
*
|
|
44
|
+
* HOW TO RE-MEASURE: a read-only collection-group read of `aiJobs` in
|
|
45
|
+
* production where `status == 'done'` and `createdAt` is in the window; group
|
|
46
|
+
* each ledger row's (`items[]`) `creditsSpent` by its `op` (the site start's
|
|
47
|
+
* look is the `theme` row), take the plan step's `steps[].creditsSpent` for
|
|
48
|
+
* `plan`, and each job's `creditsSpent` for the whole-site band. Put each
|
|
49
|
+
* group's median and 90th percentile here, and the dates in
|
|
50
|
+
* `AI_MEASURED_AT`. `ai-credit-estimate.spec.ts` holds the table, the page
|
|
51
|
+
* scaling and the measured whole-site band together, so a re-measure that
|
|
52
|
+
* moves one moves the spec with it.
|
|
53
|
+
*/ export const AI_MEASURED_UNIT_CREDITS = {
|
|
54
|
+
page: {
|
|
55
|
+
median: 30,
|
|
56
|
+
p90: 40
|
|
57
|
+
},
|
|
58
|
+
form: {
|
|
59
|
+
median: 29,
|
|
60
|
+
p90: 31
|
|
61
|
+
},
|
|
62
|
+
layout: {
|
|
63
|
+
median: 23,
|
|
64
|
+
p90: 24
|
|
65
|
+
},
|
|
66
|
+
theme: {
|
|
67
|
+
median: 4,
|
|
68
|
+
p90: 8
|
|
69
|
+
},
|
|
70
|
+
plan: {
|
|
71
|
+
median: 21,
|
|
72
|
+
p90: 70
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
/** When the table above was read, and from when its jobs ran. */ export const AI_MEASURED_AT = {
|
|
76
|
+
read: '2026-10-09',
|
|
77
|
+
since: '2026-10-01'
|
|
78
|
+
};
|
|
79
|
+
/** What a whole Free two-page site job measured at, in credits, in the same window. */ export const AI_MEASURED_FREE_SITE_CREDITS = {
|
|
80
|
+
low: 148,
|
|
81
|
+
high: 160
|
|
82
|
+
};
|
|
83
|
+
/** The sections the measured page item is about: a page is scaled around it. */ export const AI_MEASURED_PAGE_SECTIONS = 5;
|
|
84
|
+
/**
|
|
85
|
+
* What a page of this many sections is likely to cost: the measured page
|
|
86
|
+
* scaled by its sections around the ~5-section median — about 6 credits a
|
|
87
|
+
* section, 8 at the p90.
|
|
88
|
+
*/ export function aiMeasuredPageCredits(sections) {
|
|
89
|
+
const count = Math.max(1, Math.floor(Number.isFinite(sections) ? sections : AI_MEASURED_PAGE_SECTIONS));
|
|
90
|
+
const { median, p90 } = AI_MEASURED_UNIT_CREDITS.page;
|
|
91
|
+
return {
|
|
92
|
+
median: Math.ceil(median * count / AI_MEASURED_PAGE_SECTIONS),
|
|
93
|
+
p90: Math.ceil(p90 * count / AI_MEASURED_PAGE_SECTIONS)
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* What a page costs when it is written ON ITS OWN — a page an Assist build or
|
|
98
|
+
* a page job writes, rather than one of a site start's pages (AGL-3722).
|
|
99
|
+
*
|
|
100
|
+
* The table's page item is a site start's: its pages are written one after
|
|
101
|
+
* another inside one job, the later ones reading the prompt the first one
|
|
102
|
+
* paid to write, so a 2-section second page measured 19–28. A page written on
|
|
103
|
+
* its own pays that prompt itself and reads the site it lands on, and costs
|
|
104
|
+
* what a site start's FIRST page costs: a 5-section home page measured 39–78
|
|
105
|
+
* (median 40, p90 78), standalone page jobs' writing step 82–105 for a
|
|
106
|
+
* 2-section page, and the Assist build pages of 2026-10-09 22–89 each. Quoting
|
|
107
|
+
* a build's pages at the site start's scaled median told a Free workspace
|
|
108
|
+
* "about 48 credits" for two 4-section pages (AGL-3722, 2026-10-10).
|
|
109
|
+
*
|
|
110
|
+
* So it is priced as a fixed cost — the prompt, the site, the listing — and a
|
|
111
|
+
* cost per section: a 4-section page about 40 (p90 78), a 5-section 46 (p90 90).
|
|
112
|
+
*/ export const AI_MEASURED_STANDALONE_PAGE = {
|
|
113
|
+
fixed: {
|
|
114
|
+
median: 16,
|
|
115
|
+
p90: 30
|
|
116
|
+
},
|
|
117
|
+
perSection: {
|
|
118
|
+
median: 6,
|
|
119
|
+
p90: 12
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
/** What a page of this many sections written on its own is likely to cost. */ export function aiMeasuredStandalonePageCredits(sections) {
|
|
123
|
+
const count = Math.max(1, Math.floor(Number.isFinite(sections) ? sections : AI_MEASURED_PAGE_SECTIONS));
|
|
124
|
+
const { fixed, perSection } = AI_MEASURED_STANDALONE_PAGE;
|
|
125
|
+
return {
|
|
126
|
+
median: fixed.median + perSection.median * count,
|
|
127
|
+
p90: fixed.p90 + perSection.p90 * count
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* What a creation costs when it is written ON ITS OWN (AGL-3722), where it
|
|
132
|
+
* measured apart from a site start's: an Assist build's layout came to 24–28
|
|
133
|
+
* (2026-10-09/10), against a site start's 23/24 written in its warm prompt.
|
|
134
|
+
* A form measured the same either way (25–30 on its own, 29/31 in a site
|
|
135
|
+
* start), so it keeps the table's figure.
|
|
136
|
+
*/ export const AI_MEASURED_STANDALONE_CREATION_CREDITS = {
|
|
137
|
+
layout: {
|
|
138
|
+
median: 26,
|
|
139
|
+
p90: 30
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* What one pass of a kind with no measurement of its own is likely to cost
|
|
144
|
+
* (a component, an email design, a template, an item another runner
|
|
145
|
+
* builds), written on its own: a component job's writing step measured
|
|
146
|
+
* 52–65, a form job's 37, a layout job's 63, and the Assist build of
|
|
147
|
+
* 2026-10-09 a welcome email at 46 and a products step at 60. Until
|
|
148
|
+
* 2026-10-10 this was a site start's form (29/31), which is written inside a
|
|
149
|
+
* site job's warm prompt and is the cheapest single answer there is.
|
|
150
|
+
*/ export const AI_MEASURED_PASS_CREDITS = {
|
|
151
|
+
median: 50,
|
|
152
|
+
p90: 65
|
|
153
|
+
};
|
|
154
|
+
export const AI_CREDIT_RANGE_ZERO = {
|
|
155
|
+
likely: 0,
|
|
156
|
+
p90: 0,
|
|
157
|
+
ceiling: 0
|
|
158
|
+
};
|
|
159
|
+
/** Two ranges added. */ export function aiCreditRangeAdd(a, b) {
|
|
160
|
+
return {
|
|
161
|
+
likely: a.likely + b.likely,
|
|
162
|
+
p90: a.p90 + b.p90,
|
|
163
|
+
ceiling: a.ceiling + b.ceiling
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/** A measured cost as a range with its own ceiling, the likely and p90 never above it. */ export function aiCreditRangeOf(measured, ceiling) {
|
|
167
|
+
const top = Math.max(0, Math.ceil(ceiling));
|
|
168
|
+
return {
|
|
169
|
+
likely: Math.min(measured.median, top),
|
|
170
|
+
p90: Math.min(measured.p90, top),
|
|
171
|
+
ceiling: top
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/** A range whose likely and p90 are each at least the last, and at most the ceiling. */ export function aiCreditRangeOrdered(range) {
|
|
175
|
+
const ceiling = Math.max(0, Math.ceil(range.ceiling));
|
|
176
|
+
const likely = Math.min(Math.max(0, Math.ceil(range.likely)), ceiling);
|
|
177
|
+
const p90 = Math.min(Math.max(likely, Math.ceil(range.p90)), ceiling);
|
|
178
|
+
return {
|
|
179
|
+
likely,
|
|
180
|
+
p90,
|
|
181
|
+
ceiling
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const n = (value)=>value.toLocaleString('en-US');
|
|
185
|
+
/**
|
|
186
|
+
* The range as every surface quotes it: "About 106 credits (up to 650)".
|
|
187
|
+
* A job whose likely figure is its ceiling says one number.
|
|
188
|
+
*/ export function aiCreditRangeText(range) {
|
|
189
|
+
if (range.likely >= range.ceiling) return `About ${n(range.ceiling)} credits`;
|
|
190
|
+
return `About ${n(range.likely)} credits (up to ${n(range.ceiling)})`;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Whether a Free job of this range needs the person's go-ahead before it
|
|
194
|
+
* starts (AGL-3722): its p90 is more than what is left. A job whose p90
|
|
195
|
+
* fits starts with no prompt; one that does not starts only when confirmed,
|
|
196
|
+
* builds as much as what is left pays for, and pauses with Resume.
|
|
197
|
+
*/ export function aiCreditsNeedConfirm(range, left) {
|
|
198
|
+
return Number.isFinite(left) && range.p90 > Math.max(0, Math.floor(left));
|
|
199
|
+
}
|
|
200
|
+
/** The `code` a door answers an unconfirmed Free start with. */ export const AI_CREDITS_CONFIRM_CODE = 'credits-confirm';
|
|
201
|
+
/** "Build what fits": the go-ahead for a Free job past what is left. */ export const AI_CREDITS_BUILD_WHAT_FITS = 'Build what fits';
|
|
202
|
+
/** The smaller first build's button: "Build the home page first (about 64 credits)". */ export function aiCreditsSmallerText(smaller) {
|
|
203
|
+
return `${smaller.label} (about ${n(smaller.likely)} credits)`;
|
|
204
|
+
}
|
|
205
|
+
const credit = (count)=>`${n(count)} ${count === 1 ? 'credit' : 'credits'}`;
|
|
206
|
+
/**
|
|
207
|
+
* The prompt's ways on, as its three option buttons read them (Zach,
|
|
208
|
+
* 2026-10-10: "more appealing and consistent"): build what fits, the smaller
|
|
209
|
+
* first build when there is one, and Upgrade when it can be offered — each a
|
|
210
|
+
* short sentence-case label with its figure under it. The smaller build is
|
|
211
|
+
* the recommended one when its p90 fits what is left, since it then finishes;
|
|
212
|
+
* otherwise building what fits is.
|
|
213
|
+
*/ export function aiCreditsChoices(prompt, offers) {
|
|
214
|
+
const left = Math.max(0, Math.floor(prompt.left));
|
|
215
|
+
const smaller = offers.smaller ? prompt.smaller : null;
|
|
216
|
+
const smallerFits = smaller !== null && smaller.p90 <= left;
|
|
217
|
+
const choices = [
|
|
218
|
+
{
|
|
219
|
+
key: 'fits',
|
|
220
|
+
label: AI_CREDITS_BUILD_WHAT_FITS,
|
|
221
|
+
detail: `Uses your ${credit(left)} left, then pauses`,
|
|
222
|
+
recommended: !smallerFits
|
|
223
|
+
}
|
|
224
|
+
];
|
|
225
|
+
if (smaller) {
|
|
226
|
+
choices.push({
|
|
227
|
+
key: 'smaller',
|
|
228
|
+
label: smaller.label,
|
|
229
|
+
detail: smallerFits ? `About ${credit(smaller.likely)}, fits what you have left` : `About ${credit(smaller.likely)}`,
|
|
230
|
+
recommended: smallerFits
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
if (offers.upgrade) choices.push({
|
|
234
|
+
key: 'upgrade',
|
|
235
|
+
label: 'Upgrade',
|
|
236
|
+
detail: 'Get more credits',
|
|
237
|
+
recommended: false
|
|
238
|
+
});
|
|
239
|
+
return choices;
|
|
240
|
+
}
|
|
241
|
+
/** A reset day as a person reads it: "November 1". */ function resetLabel(resetsOn) {
|
|
242
|
+
const at = new Date(`${resetsOn}T00:00:00.000Z`);
|
|
243
|
+
return Number.isFinite(at.getTime()) ? at.toLocaleDateString('en-US', {
|
|
244
|
+
month: 'long',
|
|
245
|
+
day: 'numeric',
|
|
246
|
+
timeZone: 'UTC'
|
|
247
|
+
}) : 'the first of next month';
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* The plain sentence (Zach, 2026-10-09): "This build is about N credits (up
|
|
251
|
+
* to M). You have L left, so it will build as much as it can and pause when
|
|
252
|
+
* your credits run out. You can upgrade or resume when they renew."
|
|
253
|
+
*/ export function aiCreditsPromptText(prompt, noun = 'build') {
|
|
254
|
+
const size = prompt.likely >= prompt.ceiling ? `about ${n(prompt.ceiling)} credits` : `about ${n(prompt.likely)} credits (up to ${n(prompt.ceiling)})`;
|
|
255
|
+
const left = Math.max(0, Math.floor(prompt.left));
|
|
256
|
+
return `This ${noun} is ${size}. You have ${n(left)} left, so it will build as much as it can and pause ` + `when your credits run out. You can upgrade or resume when they renew on ${resetLabel(prompt.resetsOn)}.`;
|
|
257
|
+
}
|
|
258
|
+
/** The confirmation a door keeps on the job for the prompt the person confirmed, or `null` when there was none. */ export function aiCreditsConfirmation(prompt, by, at) {
|
|
259
|
+
if (!prompt) return null;
|
|
260
|
+
return {
|
|
261
|
+
by,
|
|
262
|
+
at,
|
|
263
|
+
left: prompt.left,
|
|
264
|
+
likely: prompt.likely,
|
|
265
|
+
p90: prompt.p90,
|
|
266
|
+
ceiling: prompt.ceiling
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
/** The prompt a range needs against what is left, or `null` when it fits. */ export function aiCreditsPromptFor(range, credits, smaller = null) {
|
|
270
|
+
if (!credits || !aiCreditsNeedConfirm(range, credits.left)) return null;
|
|
271
|
+
return _extends({}, range, {
|
|
272
|
+
left: Math.max(0, Math.floor(credits.left)),
|
|
273
|
+
resetsOn: credits.resetsOn,
|
|
274
|
+
smaller
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
//# sourceMappingURL=ai-credit-estimate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-credit-estimate.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\n/**\n * WHAT A JOB REALLY COSTS, NEXT TO WHAT IT CAN COST (AGL-3722).\n *\n * Every job's cost used to be quoted at its CEILING: each pass at the\n * `AI_SITE_PASS_CREDITS` (50) a pass is reserved against. A Free Assist\n * build of two pages, a quote form and a layout was quoted 600 credits and\n * refused with 164 left, when builds like it measure about a fifth of that.\n *\n * So a job is now quoted as a RANGE: what it is likely to cost (the sum of\n * the measured medians of what it builds), what it costs nine times in ten\n * (the measured p90s), and its ceiling (the old figure, unchanged). The\n * meter still charges actual use, and the reservation each pass takes is\n * still the ceiling's 50 — only the words and the Free admission move:\n * a Free job is admitted outright when its p90 fits what is left, and asked\n * to be confirmed when it does not.\n *\n * This module is pure: the console, the doors and the specs read one table.\n */\n\n/** One measured cost: the median and the 90th percentile, in credits. */\nexport interface AiMeasuredCredits {\n median: number\n p90: number\n}\n\n/**\n * MEASURED IN PRODUCTION, credits per unit, over every `done` AI job since\n * 2026-10-01 (read 2026-10-09; a whole Free site job came to 148–160):\n *\n * page item 30 / 40 (a page of about `AI_MEASURED_PAGE_SECTIONS` sections, its listing included)\n * form 29 / 31\n * layout 23 / 24 (header and footer, one answer)\n * theme 4 / 8 (a site start's look)\n * plan step 21 / 70\n *\n * HOW TO RE-MEASURE: a read-only collection-group read of `aiJobs` in\n * production where `status == 'done'` and `createdAt` is in the window; group\n * each ledger row's (`items[]`) `creditsSpent` by its `op` (the site start's\n * look is the `theme` row), take the plan step's `steps[].creditsSpent` for\n * `plan`, and each job's `creditsSpent` for the whole-site band. Put each\n * group's median and 90th percentile here, and the dates in\n * `AI_MEASURED_AT`. `ai-credit-estimate.spec.ts` holds the table, the page\n * scaling and the measured whole-site band together, so a re-measure that\n * moves one moves the spec with it.\n */\nexport const AI_MEASURED_UNIT_CREDITS = {\n page: { median: 30, p90: 40 },\n form: { median: 29, p90: 31 },\n layout: { median: 23, p90: 24 },\n theme: { median: 4, p90: 8 },\n plan: { median: 21, p90: 70 },\n} as const satisfies Record<string, AiMeasuredCredits>\n\n/** When the table above was read, and from when its jobs ran. */\nexport const AI_MEASURED_AT = { read: '2026-10-09', since: '2026-10-01' } as const\n\n/** What a whole Free two-page site job measured at, in credits, in the same window. */\nexport const AI_MEASURED_FREE_SITE_CREDITS = { low: 148, high: 160 } as const\n\n/** The sections the measured page item is about: a page is scaled around it. */\nexport const AI_MEASURED_PAGE_SECTIONS = 5\n\n/**\n * What a page of this many sections is likely to cost: the measured page\n * scaled by its sections around the ~5-section median — about 6 credits a\n * section, 8 at the p90.\n */\nexport function aiMeasuredPageCredits(sections: number): AiMeasuredCredits {\n const count = Math.max(1, Math.floor(Number.isFinite(sections) ? sections : AI_MEASURED_PAGE_SECTIONS))\n const { median, p90 } = AI_MEASURED_UNIT_CREDITS.page\n return {\n median: Math.ceil((median * count) / AI_MEASURED_PAGE_SECTIONS),\n p90: Math.ceil((p90 * count) / AI_MEASURED_PAGE_SECTIONS),\n }\n}\n\n/**\n * What a page costs when it is written ON ITS OWN — a page an Assist build or\n * a page job writes, rather than one of a site start's pages (AGL-3722).\n *\n * The table's page item is a site start's: its pages are written one after\n * another inside one job, the later ones reading the prompt the first one\n * paid to write, so a 2-section second page measured 19–28. A page written on\n * its own pays that prompt itself and reads the site it lands on, and costs\n * what a site start's FIRST page costs: a 5-section home page measured 39–78\n * (median 40, p90 78), standalone page jobs' writing step 82–105 for a\n * 2-section page, and the Assist build pages of 2026-10-09 22–89 each. Quoting\n * a build's pages at the site start's scaled median told a Free workspace\n * \"about 48 credits\" for two 4-section pages (AGL-3722, 2026-10-10).\n *\n * So it is priced as a fixed cost — the prompt, the site, the listing — and a\n * cost per section: a 4-section page about 40 (p90 78), a 5-section 46 (p90 90).\n */\nexport const AI_MEASURED_STANDALONE_PAGE = {\n fixed: { median: 16, p90: 30 },\n perSection: { median: 6, p90: 12 },\n} as const satisfies Record<string, AiMeasuredCredits>\n\n/** What a page of this many sections written on its own is likely to cost. */\nexport function aiMeasuredStandalonePageCredits(sections: number): AiMeasuredCredits {\n const count = Math.max(1, Math.floor(Number.isFinite(sections) ? sections : AI_MEASURED_PAGE_SECTIONS))\n const { fixed, perSection } = AI_MEASURED_STANDALONE_PAGE\n return {\n median: fixed.median + perSection.median * count,\n p90: fixed.p90 + perSection.p90 * count,\n }\n}\n\n/**\n * What a creation costs when it is written ON ITS OWN (AGL-3722), where it\n * measured apart from a site start's: an Assist build's layout came to 24–28\n * (2026-10-09/10), against a site start's 23/24 written in its warm prompt.\n * A form measured the same either way (25–30 on its own, 29/31 in a site\n * start), so it keeps the table's figure.\n */\nexport const AI_MEASURED_STANDALONE_CREATION_CREDITS = {\n layout: { median: 26, p90: 30 },\n} as const satisfies Record<string, AiMeasuredCredits>\n\n/**\n * What one pass of a kind with no measurement of its own is likely to cost\n * (a component, an email design, a template, an item another runner\n * builds), written on its own: a component job's writing step measured\n * 52–65, a form job's 37, a layout job's 63, and the Assist build of\n * 2026-10-09 a welcome email at 46 and a products step at 60. Until\n * 2026-10-10 this was a site start's form (29/31), which is written inside a\n * site job's warm prompt and is the cheapest single answer there is.\n */\nexport const AI_MEASURED_PASS_CREDITS: AiMeasuredCredits = { median: 50, p90: 65 }\n\n/** A job's cost as it is quoted: likely, nine times in ten, and at its worst. */\nexport interface AiCreditRange {\n /** The sum of the measured medians: what the job is likely to spend. */\n likely: number\n /** The sum of the measured p90s: what a Free job is admitted on. */\n p90: number\n /** Every pass at its reserve: what it can spend at most. */\n ceiling: number\n}\n\nexport const AI_CREDIT_RANGE_ZERO: AiCreditRange = { likely: 0, p90: 0, ceiling: 0 }\n\n/** Two ranges added. */\nexport function aiCreditRangeAdd(a: AiCreditRange, b: AiCreditRange): AiCreditRange {\n return { likely: a.likely + b.likely, p90: a.p90 + b.p90, ceiling: a.ceiling + b.ceiling }\n}\n\n/** A measured cost as a range with its own ceiling, the likely and p90 never above it. */\nexport function aiCreditRangeOf(measured: AiMeasuredCredits, ceiling: number): AiCreditRange {\n const top = Math.max(0, Math.ceil(ceiling))\n return { likely: Math.min(measured.median, top), p90: Math.min(measured.p90, top), ceiling: top }\n}\n\n/** A range whose likely and p90 are each at least the last, and at most the ceiling. */\nexport function aiCreditRangeOrdered(range: AiCreditRange): AiCreditRange {\n const ceiling = Math.max(0, Math.ceil(range.ceiling))\n const likely = Math.min(Math.max(0, Math.ceil(range.likely)), ceiling)\n const p90 = Math.min(Math.max(likely, Math.ceil(range.p90)), ceiling)\n return { likely, p90, ceiling }\n}\n\nconst n = (value: number) => value.toLocaleString('en-US')\n\n/**\n * The range as every surface quotes it: \"About 106 credits (up to 650)\".\n * A job whose likely figure is its ceiling says one number.\n */\nexport function aiCreditRangeText(range: AiCreditRange): string {\n if (range.likely >= range.ceiling) return `About ${n(range.ceiling)} credits`\n return `About ${n(range.likely)} credits (up to ${n(range.ceiling)})`\n}\n\n/**\n * Whether a Free job of this range needs the person's go-ahead before it\n * starts (AGL-3722): its p90 is more than what is left. A job whose p90\n * fits starts with no prompt; one that does not starts only when confirmed,\n * builds as much as what is left pays for, and pauses with Resume.\n */\nexport function aiCreditsNeedConfirm(range: Pick<AiCreditRange, 'p90'>, left: number): boolean {\n return Number.isFinite(left) && range.p90 > Math.max(0, Math.floor(left))\n}\n\n/** The smaller first build a Free job that does not fit is offered (AGL-3722). */\nexport interface AiCreditsSmaller {\n /** \"Build the home page first\". */\n label: string\n likely: number\n p90: number\n ceiling: number\n}\n\n/**\n * What a Free job that does not fit what is left says before it starts, and\n * the choices it offers: build what fits, a smaller first build, or Upgrade.\n * The door that refuses an unconfirmed start answers with it (status 409,\n * `code: AI_CREDITS_CONFIRM_CODE`), and the plan card and the guided start\n * draw it from the same figures.\n */\nexport interface AiCreditsPrompt extends AiCreditRange {\n left: number\n resetsOn: string\n smaller: AiCreditsSmaller | null\n}\n\n/** The `code` a door answers an unconfirmed Free start with. */\nexport const AI_CREDITS_CONFIRM_CODE = 'credits-confirm'\n\n/** \"Build what fits\": the go-ahead for a Free job past what is left. */\nexport const AI_CREDITS_BUILD_WHAT_FITS = 'Build what fits'\n\n/** The smaller first build's button: \"Build the home page first (about 64 credits)\". */\nexport function aiCreditsSmallerText(smaller: Pick<AiCreditsSmaller, 'label' | 'likely'>): string {\n return `${smaller.label} (about ${n(smaller.likely)} credits)`\n}\n\n/** Which way on a prompt offers, in the order it offers them. */\nexport type AiCreditsChoiceKey = 'fits' | 'smaller' | 'upgrade'\n\n/** One way on, as its option button reads it: a short label and the figure under it (AGL-3722). */\nexport interface AiCreditsChoice {\n key: AiCreditsChoiceKey\n label: string\n detail: string\n /** The one choice drawn as the primary button. */\n recommended: boolean\n}\n\nconst credit = (count: number) => `${n(count)} ${count === 1 ? 'credit' : 'credits'}`\n\n/**\n * The prompt's ways on, as its three option buttons read them (Zach,\n * 2026-10-10: \"more appealing and consistent\"): build what fits, the smaller\n * first build when there is one, and Upgrade when it can be offered — each a\n * short sentence-case label with its figure under it. The smaller build is\n * the recommended one when its p90 fits what is left, since it then finishes;\n * otherwise building what fits is.\n */\nexport function aiCreditsChoices(\n prompt: Pick<AiCreditsPrompt, 'left' | 'smaller'>,\n offers: { smaller: boolean; upgrade: boolean },\n): AiCreditsChoice[] {\n const left = Math.max(0, Math.floor(prompt.left))\n const smaller = offers.smaller ? prompt.smaller : null\n const smallerFits = smaller !== null && smaller.p90 <= left\n const choices: AiCreditsChoice[] = [\n {\n key: 'fits',\n label: AI_CREDITS_BUILD_WHAT_FITS,\n detail: `Uses your ${credit(left)} left, then pauses`,\n recommended: !smallerFits,\n },\n ]\n if (smaller) {\n choices.push({\n key: 'smaller',\n label: smaller.label,\n detail: smallerFits ? `About ${credit(smaller.likely)}, fits what you have left` : `About ${credit(smaller.likely)}`,\n recommended: smallerFits,\n })\n }\n if (offers.upgrade) choices.push({ key: 'upgrade', label: 'Upgrade', detail: 'Get more credits', recommended: false })\n return choices\n}\n\n/** A reset day as a person reads it: \"November 1\". */\nfunction resetLabel(resetsOn: string): string {\n const at = new Date(`${resetsOn}T00:00:00.000Z`)\n return Number.isFinite(at.getTime())\n ? at.toLocaleDateString('en-US', { month: 'long', day: 'numeric', timeZone: 'UTC' })\n : 'the first of next month'\n}\n\n/**\n * The plain sentence (Zach, 2026-10-09): \"This build is about N credits (up\n * to M). You have L left, so it will build as much as it can and pause when\n * your credits run out. You can upgrade or resume when they renew.\"\n */\nexport function aiCreditsPromptText(prompt: AiCreditsPrompt, noun: 'build' | 'site' = 'build'): string {\n const size =\n prompt.likely >= prompt.ceiling\n ? `about ${n(prompt.ceiling)} credits`\n : `about ${n(prompt.likely)} credits (up to ${n(prompt.ceiling)})`\n const left = Math.max(0, Math.floor(prompt.left))\n return (\n `This ${noun} is ${size}. You have ${n(left)} left, so it will build as much as it can and pause ` +\n `when your credits run out. You can upgrade or resume when they renew on ${resetLabel(prompt.resetsOn)}.`\n )\n}\n\n/**\n * The record a confirmed Free start keeps (AGL-3722): who said go, when,\n * what was left then and the range they were shown.\n */\nexport interface AiJobCreditsConfirmation extends AiCreditRange {\n by: string\n at: Date | string\n left: number\n}\n\n/** The confirmation a door keeps on the job for the prompt the person confirmed, or `null` when there was none. */\nexport function aiCreditsConfirmation(\n prompt: AiCreditsPrompt | null,\n by: string,\n at: Date,\n): AiJobCreditsConfirmation | null {\n if (!prompt) return null\n return { by, at, left: prompt.left, likely: prompt.likely, p90: prompt.p90, ceiling: prompt.ceiling }\n}\n\n/** The prompt a range needs against what is left, or `null` when it fits. */\nexport function aiCreditsPromptFor(\n range: AiCreditRange,\n credits: { left: number; resetsOn: string } | null | undefined,\n smaller: AiCreditsSmaller | null = null,\n): AiCreditsPrompt | null {\n if (!credits || !aiCreditsNeedConfirm(range, credits.left)) return null\n return { ...range, left: Math.max(0, Math.floor(credits.left)), resetsOn: credits.resetsOn, smaller }\n}\n"],"names":["AI_MEASURED_UNIT_CREDITS","page","median","p90","form","layout","theme","plan","AI_MEASURED_AT","read","since","AI_MEASURED_FREE_SITE_CREDITS","low","high","AI_MEASURED_PAGE_SECTIONS","aiMeasuredPageCredits","sections","count","Math","max","floor","Number","isFinite","ceil","AI_MEASURED_STANDALONE_PAGE","fixed","perSection","aiMeasuredStandalonePageCredits","AI_MEASURED_STANDALONE_CREATION_CREDITS","AI_MEASURED_PASS_CREDITS","AI_CREDIT_RANGE_ZERO","likely","ceiling","aiCreditRangeAdd","a","b","aiCreditRangeOf","measured","top","min","aiCreditRangeOrdered","range","n","value","toLocaleString","aiCreditRangeText","aiCreditsNeedConfirm","left","AI_CREDITS_CONFIRM_CODE","AI_CREDITS_BUILD_WHAT_FITS","aiCreditsSmallerText","smaller","label","credit","aiCreditsChoices","prompt","offers","smallerFits","choices","key","detail","recommended","push","upgrade","resetLabel","resetsOn","at","Date","getTime","toLocaleDateString","month","day","timeZone","aiCreditsPromptText","noun","size","aiCreditsConfirmation","by","aiCreditsPromptFor","credits"],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { AiJobSummary } from './ai-jobs.types';
|
|
18
|
+
/**
|
|
19
|
+
* What every surface that cancels an AI job says (AGL-3616): the job's page,
|
|
20
|
+
* a row of the site's AI jobs list, and the AI jobs list the top-bar chip
|
|
21
|
+
* opens. One wording, so a person reads the same promise wherever they stop
|
|
22
|
+
* a job.
|
|
23
|
+
*/
|
|
24
|
+
/** The confirm dialog's title. */
|
|
25
|
+
export declare const AI_JOB_CANCEL_TITLE = "Cancel this AI job?";
|
|
26
|
+
/** What canceling does, said before it is done. */
|
|
27
|
+
export declare const AI_JOB_CANCEL_CONFIRM_COPY = "Stop building. Pages already built stay as drafts; you won\u2019t be charged for steps that haven\u2019t run.";
|
|
28
|
+
/** While the step in flight finishes or stops. */
|
|
29
|
+
export declare const AI_JOB_CANCELING_COPY = "Stopping. The step in progress finishes or stops first, and nothing after it runs.";
|
|
30
|
+
/** What a canceled job's page says under its heading. */
|
|
31
|
+
export declare const AI_JOB_CANCELED_LEDE = "You canceled this job. Anything it already built stays as an unpublished draft, and nothing was published.";
|
|
32
|
+
/**
|
|
33
|
+
* Why a job cannot be canceled, or `null` when it can: one that is done,
|
|
34
|
+
* failed or already canceled has nothing left to stop, and one already
|
|
35
|
+
* stopping needs no second cancel. The button stays, disabled, with this as
|
|
36
|
+
* its reason.
|
|
37
|
+
*/
|
|
38
|
+
export declare function aiJobCancelBlockedReason(job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'cancelRequested'>>): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* What a canceled job cost (AGL-3616): the credits its finished steps used,
|
|
41
|
+
* which stay charged, less anything given back. "This job used 42 credits
|
|
42
|
+
* before it stopped." — or that it used none.
|
|
43
|
+
*/
|
|
44
|
+
export declare function aiJobCanceledCreditsCopy(job: Partial<Pick<AiJobSummary, 'creditsSpent' | 'refundedCredits'>>): string;
|