@aglyn/plugins-ai 1.0.0-beta.288 → 1.0.0-beta.290

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/package.json +17 -17
  2. package/src/lib/activity/ai-activity-actions.d.ts +3 -2
  3. package/src/lib/activity/ai-activity-actions.js +3 -0
  4. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  5. package/src/lib/components/ai-job-requests.d.ts +2 -0
  6. package/src/lib/components/ai-job-requests.js +2 -0
  7. package/src/lib/components/ai-job-requests.js.map +1 -1
  8. package/src/lib/components/ai-site-build-page.component.js +4 -0
  9. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  10. package/src/lib/components/ai-site-import-job.component.d.ts +6 -1
  11. package/src/lib/components/ai-site-import-job.component.js +124 -17
  12. package/src/lib/components/ai-site-import-job.component.js.map +1 -1
  13. package/src/lib/components/ai-site-import-start.component.d.ts +6 -0
  14. package/src/lib/components/ai-site-import-start.component.js +18 -4
  15. package/src/lib/components/ai-site-import-start.component.js.map +1 -1
  16. package/src/lib/components/ai-site-start-card.component.js +2 -1
  17. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  18. package/src/lib/jobs/ai-image-reads.d.ts +20 -2
  19. package/src/lib/jobs/ai-image-reads.js +47 -0
  20. package/src/lib/jobs/ai-image-reads.js.map +1 -1
  21. package/src/lib/jobs/ai-job-import-step.d.ts +53 -3
  22. package/src/lib/jobs/ai-job-import-step.js +386 -64
  23. package/src/lib/jobs/ai-job-import-step.js.map +1 -1
  24. package/src/lib/jobs/ai-job-layout-language.d.ts +33 -0
  25. package/src/lib/jobs/ai-job-layout-language.js +75 -1
  26. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  27. package/src/lib/jobs/ai-job-layout-step.js +8 -3
  28. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  29. package/src/lib/jobs/ai-job-page-language.d.ts +29 -0
  30. package/src/lib/jobs/ai-job-page-language.js +178 -2
  31. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  32. package/src/lib/jobs/ai-job-page-step.js +3 -0
  33. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  34. package/src/lib/jobs/ai-site-import-document.d.ts +117 -0
  35. package/src/lib/jobs/ai-site-import-document.js +710 -0
  36. package/src/lib/jobs/ai-site-import-document.js.map +1 -0
  37. package/src/lib/jobs/ai-site-import-look.d.ts +112 -0
  38. package/src/lib/jobs/ai-site-import-look.js +402 -0
  39. package/src/lib/jobs/ai-site-import-look.js.map +1 -0
  40. package/src/lib/jobs/ai-site-import-publish.d.ts +41 -0
  41. package/src/lib/jobs/ai-site-import-publish.js +92 -0
  42. package/src/lib/jobs/ai-site-import-publish.js.map +1 -0
  43. package/src/lib/jobs/ai-theme-brand-inputs.d.ts +27 -2
  44. package/src/lib/jobs/ai-theme-brand-inputs.js +37 -8
  45. package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -1
  46. package/src/lib/layout-language/ai-layout-frame.d.ts +17 -0
  47. package/src/lib/layout-language/ai-layout-frame.js +73 -2
  48. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  49. package/src/lib/layout-language/ai-layout-language.d.ts +43 -0
  50. package/src/lib/layout-language/ai-layout-language.js +199 -0
  51. package/src/lib/layout-language/ai-layout-language.js.map +1 -1
  52. package/src/lib/model/ai-credit-estimate.d.ts +15 -0
  53. package/src/lib/model/ai-credit-estimate.js +14 -1
  54. package/src/lib/model/ai-credit-estimate.js.map +1 -1
  55. package/src/lib/model/ai-jobs.types.d.ts +13 -1
  56. package/src/lib/model/ai-jobs.types.js.map +1 -1
  57. package/src/lib/model/ai-site-import-view.d.ts +47 -3
  58. package/src/lib/model/ai-site-import-view.js +122 -20
  59. package/src/lib/model/ai-site-import-view.js.map +1 -1
  60. package/src/lib/model/ai-site-import.d.ts +232 -7
  61. package/src/lib/model/ai-site-import.js +365 -35
  62. package/src/lib/model/ai-site-import.js.map +1 -1
  63. package/src/lib/runtime/ai-doctrine-validators.d.ts +18 -0
  64. package/src/lib/runtime/ai-doctrine-validators.js +16 -5
  65. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  66. package/src/lib/runtime/ai-palette.generated.js +31 -0
  67. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  68. package/src/lib/server/ai-image-reads-route.js +24 -1
  69. package/src/lib/server/ai-image-reads-route.js.map +1 -1
  70. package/src/lib/server/ai-jobs-resume.js +7 -1
  71. package/src/lib/server/ai-jobs-resume.js.map +1 -1
  72. package/src/lib/site-import/fixtures/aglyn-com-recordings.d.ts +57 -0
  73. package/src/lib/site-import/fixtures/aglyn-com-recordings.js +172 -0
  74. package/src/lib/site-import/fixtures/aglyn-com-recordings.js.map +1 -0
  75. package/src/lib/site-import/site-import-crawl.d.ts +32 -7
  76. package/src/lib/site-import/site-import-crawl.js +133 -44
  77. package/src/lib/site-import/site-import-crawl.js.map +1 -1
  78. package/src/lib/site-import/site-import-edge-server.d.ts +31 -0
  79. package/src/lib/site-import/site-import-edge-server.js +31 -0
  80. package/src/lib/site-import/site-import-edge-server.js.map +1 -0
  81. package/src/lib/site-import/site-import-edge.d.ts +44 -0
  82. package/src/lib/site-import/site-import-edge.js +108 -0
  83. package/src/lib/site-import/site-import-edge.js.map +1 -0
  84. package/src/lib/site-import/site-import-html.d.ts +73 -3
  85. package/src/lib/site-import/site-import-html.js +332 -45
  86. package/src/lib/site-import/site-import-html.js.map +1 -1
  87. package/src/lib/site-import/site-import-images.d.ts +22 -0
  88. package/src/lib/site-import/site-import-images.js +78 -2
  89. package/src/lib/site-import/site-import-images.js.map +1 -1
  90. package/src/lib/site-import/site-import-page.d.ts +44 -2
  91. package/src/lib/site-import/site-import-page.js +116 -16
  92. package/src/lib/site-import/site-import-page.js.map +1 -1
@@ -14,6 +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 { SiteImportBlock, SiteImportForm } from '../site-import/site-import-html';
17
18
  import { type AiCreditRange } from './ai-credit-estimate';
18
19
  import type { AiJobItemLedger } from './ai-jobs.types';
19
20
  /** The job kind. */
@@ -32,10 +33,32 @@ export declare const AI_SITE_IMPORT_MAX_PAGES = 200;
32
33
  export declare const AI_SITE_IMPORT_COLLECTION_MIN = 3;
33
34
  /** The most photos brought in from one page. */
34
35
  export declare const AI_SITE_IMPORT_IMAGES_PER_PAGE = 8;
35
- /** The most characters of one page's text a wave keeps for its model answer. */
36
+ /**
37
+ * The most characters of one page's text a wave keeps for the page step's
38
+ * prompt, which reads no more than this; every word is kept on the read's
39
+ * `blocks` (AGL-4083).
40
+ */
36
41
  export declare const AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS = 6000;
37
- /** The most sections one imported page is planned with. */
38
- export declare const AI_SITE_IMPORT_MAX_SECTIONS = 6;
42
+ /** The most sections one imported page is planned with: one for every second-level heading (AGL-4083). */
43
+ export declare const AI_SITE_IMPORT_MAX_SECTIONS = 24;
44
+ /** The most blocks of one page a wave's read keeps. */
45
+ export declare const AI_SITE_IMPORT_READ_MAX_BLOCKS = 2000;
46
+ /** The most characters of one page's blocks a wave's read keeps: a long policy is 40,000. */
47
+ export declare const AI_SITE_IMPORT_READ_MAX_CHARS = 120000;
48
+ /**
49
+ * The most characters of blocks the reads of one import hold at once: they
50
+ * ride the job document, which Firestore caps at 1 MiB, beside the
51
+ * inventory and the ledger. A wave's later page past it keeps what fits and
52
+ * says it was cut.
53
+ */
54
+ export declare const AI_SITE_IMPORT_WAVE_READ_MAX_CHARS = 400000;
55
+ /**
56
+ * A page with more words of its own than this is copied as it is, never
57
+ * arranged (AGL-4083): a policy, an article, a long story.
58
+ */
59
+ export declare const AI_SITE_IMPORT_PROSE_WORDS = 900;
60
+ /** The answers bot protection and firewalls turn a crawler away with: asked to sign in, refused, or told to slow down. */
61
+ export declare const AI_SITE_IMPORT_BLOCKED_STATUSES: ReadonlySet<number>;
39
62
  /** The most characters a section's name, a page's title or a label keeps. */
40
63
  export declare const AI_SITE_IMPORT_LABEL_MAX_CHARS = 60;
41
64
  /** A page with fewer words than this and no photo has nothing to bring in. */
@@ -44,6 +67,8 @@ export declare const AI_SITE_IMPORT_MIN_WORDS = 5;
44
67
  export declare const AI_SITE_IMPORT_FRAME_SLOT = "frame";
45
68
  /** The ceiling one imported page reserves: its answer pass and its listing pass, at 50 each. */
46
69
  export declare const AI_SITE_IMPORT_PAGE_CEILING_CREDITS = 100;
70
+ /** The ceiling a page copied as it is reserves: its listing pass alone. */
71
+ export declare const AI_SITE_IMPORT_COPIED_CEILING_CREDITS = 50;
47
72
  /** The ceiling the header and footer reserve: one answer at 50. */
48
73
  export declare const AI_SITE_IMPORT_FRAME_CEILING_CREDITS = 50;
49
74
  /** The most left-out pages listed by address; the rest are counted. */
@@ -83,8 +108,12 @@ export type AiSiteImportLeftOutReason =
83
108
  /** Over the size one page may be. */
84
109
  | 'too-large'
85
110
  /** It sends visitors to another website. */
86
- | 'off-site';
111
+ | 'off-site'
112
+ /** The read stopped (its clock, its bytes, its page count) before it got to it (AGL-4083). */
113
+ | 'not-reached';
87
114
  export declare const AI_SITE_IMPORT_LEFT_OUT_REASONS: readonly AiSiteImportLeftOutReason[];
115
+ /** The reasons that mean the page was there and AglynBot could not read it: the "could not be read" of what was found. */
116
+ export declare const AI_SITE_IMPORT_UNREAD_REASONS: readonly AiSiteImportLeftOutReason[];
88
117
  export interface AiSiteImportLeftOut {
89
118
  url: string;
90
119
  path: string;
@@ -98,6 +127,8 @@ export interface AiSiteImportPhoto {
98
127
  width?: number;
99
128
  height?: number;
100
129
  alt?: string;
130
+ /** The address on the old site it was read from, where a page that is copied places it (AGL-4084). */
131
+ from?: string;
101
132
  }
102
133
  /** What a wave's read found on one page, for its model answer. Never on the wire. */
103
134
  export interface AiSiteImportPageRead {
@@ -112,7 +143,23 @@ export interface AiSiteImportPageRead {
112
143
  url: string;
113
144
  alt: string;
114
145
  }>;
146
+ /**
147
+ * Every block of the page's main region, in order (AGL-4083): its words as
148
+ * written, its headings, lists, links and pictures, within
149
+ * `AI_SITE_IMPORT_READ_MAX_BLOCKS` and `AI_SITE_IMPORT_READ_MAX_CHARS`.
150
+ * Untrusted data: fenced wherever a prompt reads it. Emptied once the page
151
+ * is in; absent on a read made before it was kept.
152
+ */
153
+ blocks?: SiteImportBlock[];
154
+ /** Set when `blocks` had to stop short of the page, at its budget or the wave's. */
155
+ cut?: boolean;
156
+ /** The words of the page's main region, as the old page has them. */
157
+ words?: number;
158
+ /** The forms the page asks visitors to fill in, by their fields. */
159
+ forms?: SiteImportForm[];
115
160
  }
161
+ /** How a page is built from its old page, as the step chose: its words copied as they are, or arranged by Aglyn AI. */
162
+ export type AiSiteImportBuild = 'copied' | 'arranged';
116
163
  export interface AiSiteImportPage {
117
164
  /** The item ledger slot: `p0`, `p1`, … */
118
165
  slot: string;
@@ -131,6 +178,12 @@ export interface AiSiteImportPage {
131
178
  read?: AiSiteImportPageRead | null;
132
179
  /** Set when the wave's read found the page cannot be brought in after all. */
133
180
  refused?: AiSiteImportLeftOutReason | null;
181
+ /** How the step builds the page (`aiSiteImportPageBuild`); absent until it chooses, and the page is priced as an AI page. */
182
+ build?: AiSiteImportBuild | null;
183
+ /** What came through once the page is in: its words on the new page. */
184
+ brought?: {
185
+ words: number;
186
+ } | null;
134
187
  /** Filled once the page's photos are in the library. */
135
188
  photos?: {
136
189
  brought: number;
@@ -184,7 +237,139 @@ export interface AiSiteImportState {
184
237
  } | null;
185
238
  /** The Trust & Safety row this import is logged under. */
186
239
  reviewId: string | null;
240
+ /**
241
+ * "Start every remaining wave" (AGL-4083): each wave closes and the next
242
+ * starts with no review between them; the last wave's close is the stop.
243
+ * Off unless the person chose it.
244
+ */
245
+ autoContinue?: boolean;
246
+ /**
247
+ * The wave "Start every remaining wave" was chosen at (AGL-4084): the one
248
+ * start the person made, which the day's limit counts; the waves it starts
249
+ * after it are the same start.
250
+ */
251
+ autoContinueFrom?: number | null;
252
+ /** What the read found of the site as a whole; a stopping verdict plans nothing. */
253
+ verdict?: AiSiteImportReadVerdict | null;
254
+ /** What the home page says of the site as a whole (AGL-4084): its look, its header and footer, its listing. */
255
+ home?: AiSiteImportHome | null;
256
+ /** The look pass, once it ran in the first wave (AGL-4084). */
257
+ look?: AiSiteImportLook | null;
258
+ }
259
+ /**
260
+ * What the home page says of the site as a whole (AGL-4084), kept from the
261
+ * read: the site's listing in its own words, its header's brand as the
262
+ * source draws it, and the words its header and footer carry. Untrusted
263
+ * data from the owner's site, fenced wherever a prompt reads it.
264
+ */
265
+ export interface AiSiteImportHome {
266
+ url: string;
267
+ title: string;
268
+ description: string;
269
+ ogImage: string | null;
270
+ siteName: string;
271
+ brand: {
272
+ kind: 'logo-only' | 'logo-and-name' | 'name-only';
273
+ /** The name as the header prints it, where it prints one. */
274
+ text: string;
275
+ logo: {
276
+ url: string;
277
+ alt: string;
278
+ type: 'svg' | 'raster' | null;
279
+ } | null;
280
+ };
281
+ facts: {
282
+ /** The header's and footer's lines, as written, in order. */
283
+ lines: string[];
284
+ emails: string[];
285
+ phones: string[];
286
+ social: string[];
287
+ address: string;
288
+ };
289
+ }
290
+ /** What the look pass did (AGL-4084): no model is asked. */
291
+ export interface AiSiteImportLook {
292
+ at: string;
293
+ /** The theme: drawn from the source (`applied`), the owner's kept (`kept`), or the source's look could not be read. */
294
+ theme: 'applied' | 'kept' | 'unread';
295
+ /** The source's colors it was drawn from, the strongest first. */
296
+ colors: string[];
297
+ /** The source's logo, as the library holds it, for the header's brand. */
298
+ logo: AiSiteImportPhoto | null;
299
+ /** Whether the site's listing was taken from the home page's own words. */
300
+ seo: boolean;
301
+ /** The look's seed, which every page of the import is drawn with. */
302
+ seed: number | null;
303
+ /** The look's header arrangement, band rhythm and motion, which the header, the footer and every page are drawn in. */
304
+ style?: {
305
+ headerAlign?: string;
306
+ rhythm?: string;
307
+ motion?: string;
308
+ } | null;
309
+ }
310
+ /**
311
+ * - `home-blocked`: bot protection or a firewall turned AglynBot away from
312
+ * the home page (401, 403 or 429).
313
+ * - `home-unreadable`: the home page could not be read for any other reason,
314
+ * or read as an empty shell (a site that needs a browser to show its words).
315
+ * - `mostly-blocked`: the home page came in, but more than half the site's
316
+ * addresses were turned away; the import goes on, and says so.
317
+ * - `ok`: anything else.
318
+ */
319
+ export type AiSiteImportReadVerdictKind = 'ok' | 'home-blocked' | 'home-unreadable' | 'mostly-blocked';
320
+ /** Why the home page did not come in. */
321
+ export type AiSiteImportHomeProblem = 'blocked' | 'http-error' | 'unreachable' | 'not-html' | 'too-large' | 'off-site' | 'robots' | 'login-form' | 'not-reached' | 'empty';
322
+ export interface AiSiteImportReadVerdict {
323
+ kind: AiSiteImportReadVerdictKind;
324
+ /** Addresses turned away by bot protection or a firewall, the home page among them. */
325
+ blocked: number;
326
+ /** The site's addresses the read knew of: the sitemaps', or everything it asked for. */
327
+ total: number;
328
+ /** What the home page answered, when it answered with an error. */
329
+ homeStatus: number | null;
330
+ homeProblem: AiSiteImportHomeProblem | null;
331
+ /** Whose edge turned the home page away, when it said. */
332
+ edge: 'vercel' | 'cloudflare' | null;
333
+ }
334
+ /** A crawl as the verdict reads it; the crawler's `SiteImportInventory` is one. */
335
+ export interface AiSiteImportReadInput {
336
+ pages: ReadonlyArray<{
337
+ path: string;
338
+ foundBy: string;
339
+ blocks: ReadonlyArray<{
340
+ kind: string;
341
+ text?: string;
342
+ inline?: boolean;
343
+ }>;
344
+ images: readonly unknown[];
345
+ }>;
346
+ skipped: ReadonlyArray<{
347
+ url: string;
348
+ reason: string;
349
+ status?: number;
350
+ edge?: string | null;
351
+ }>;
352
+ sitemapUrls: number;
187
353
  }
354
+ /**
355
+ * The read's verdict (AGL-4083), pure over the crawl: whether the home page
356
+ * came in, and how much of the site bot protection turned away. An import
357
+ * whose verdict stops (`aiSiteImportReadStops`) plans no wave, quotes
358
+ * nothing and spends nothing: the person is told why and checks again.
359
+ */
360
+ export declare function aiSiteImportReadVerdict(crawled: AiSiteImportReadInput): AiSiteImportReadVerdict;
361
+ /** Whether a verdict stops the import before anything is planned: its home page is not in. */
362
+ export declare function aiSiteImportReadStops(verdict: AiSiteImportReadVerdict | null | undefined): boolean;
363
+ /** The review an import that stopped at its read waits under: `blocked` or `unreadable`; `null` when it goes on. */
364
+ export declare function aiSiteImportReadVerdictReason(verdict: AiSiteImportReadVerdict | null | undefined): 'blocked' | 'unreadable' | null;
365
+ /** Where a site owner lets AglynBot through, on the edges that name themselves. */
366
+ export declare const AI_SITE_IMPORT_EDGE_HINTS: Readonly<Record<'vercel' | 'cloudflare', string>>;
367
+ /**
368
+ * What the person is told about the read (AGL-4083), in console words: why
369
+ * nothing was brought in and what to do, or that most of the site was turned
370
+ * away. `null` when there is nothing to say.
371
+ */
372
+ export declare function aiSiteImportReadVerdictMessage(verdict: AiSiteImportReadVerdict | null | undefined, domain: string): string | null;
188
373
  /** A collection's prefix for a page address: its first segment, a dated archive's `posts`; `null` for a top-level page. */
189
374
  export declare function aiSiteImportPrefix(path: string): string | null;
190
375
  /** "news-and-events" → "News and events". */
@@ -221,6 +406,12 @@ export interface AiSiteImportInventoryInput {
221
406
  /** Skips the crawl only counted. */
222
407
  skippedOverflow?: number;
223
408
  stoppedBy: string | null;
409
+ /**
410
+ * The read's verdict from the crawl itself (`aiSiteImportReadVerdict`), with
411
+ * the status and edge the home page answered with; read from `pages` and
412
+ * `skipped` when absent.
413
+ */
414
+ verdict?: AiSiteImportReadVerdict | null;
224
415
  /** The plan's page count; `null` for none. */
225
416
  pageLimit: number | null;
226
417
  existingPages: number;
@@ -276,26 +467,60 @@ export declare function aiSiteImportReviewWave(state: AiSiteImportState | null |
276
467
  export declare function aiSiteImportNextWork(state: AiSiteImportState | null | undefined, items: readonly AiJobItemLedger[] | null | undefined): AiSiteImportWork;
277
468
  /** A wave started: running, with what its read found on each page. */
278
469
  export declare function aiSiteImportWaveStarted(state: AiSiteImportState, index: number, at: string, reads: ReadonlyMap<string, AiSiteImportPageRead | AiSiteImportLeftOutReason>): AiSiteImportState;
279
- /** More of a running wave's pages, read now. */
470
+ /** How many characters a block holds, as the job document stores it. */
471
+ export declare function aiSiteImportBlockChars(block: SiteImportBlock): number;
472
+ /** A page's blocks in order, as many as fit `maxChars` and `maxBlocks`, and whether any had to go. */
473
+ export declare function aiSiteImportCapBlocks(blocks: readonly SiteImportBlock[], maxChars: number, maxBlocks?: number): {
474
+ blocks: SiteImportBlock[];
475
+ chars: number;
476
+ cut: boolean;
477
+ };
478
+ /**
479
+ * More of a running wave's pages, read now. Their blocks share
480
+ * `AI_SITE_IMPORT_WAVE_READ_MAX_CHARS` with every read still held: a page
481
+ * past it keeps what fits, in order, and is marked `cut`.
482
+ */
280
483
  export declare function aiSiteImportPagesRead(state: AiSiteImportState, reads: ReadonlyMap<string, AiSiteImportPageRead | AiSiteImportLeftOutReason>): AiSiteImportState;
281
484
  /** A page's photos are in the library. */
282
485
  export declare function aiSiteImportPhotosIn(state: AiSiteImportState, slot: string, photos: NonNullable<AiSiteImportPage['photos']>): AiSiteImportState;
283
- /** A page is in: its text is no longer kept. */
486
+ /** A page is in: its text and its blocks are no longer kept. */
284
487
  export declare function aiSiteImportPageSettled(state: AiSiteImportState, slot: string): AiSiteImportState;
285
488
  /** A wave is in: waiting for its review, or done when no wave follows it. */
286
489
  export declare function aiSiteImportWaveClosed(state: AiSiteImportState, index: number, at: string): AiSiteImportState;
287
490
  /** The state as the console reads it: every page's read text left off. */
288
491
  export declare function aiSiteImportWire(state: AiSiteImportState | null | undefined): AiSiteImportState | null;
492
+ /**
493
+ * How a page is built (AGL-4083), decided by code: every page of a collection
494
+ * (a blog, the legal pages, news) and any page longer than
495
+ * `AI_SITE_IMPORT_PROSE_WORDS` is copied as it is; the main pages are
496
+ * arranged by Aglyn AI, their words kept as written.
497
+ */
498
+ export declare function aiSiteImportPageBuild(page: Pick<AiSiteImportPage, 'group' | 'words'>): AiSiteImportBuild;
289
499
  /**
290
500
  * What bringing in this many pages costs, quoted before it starts (AGL-3976):
291
501
  * each page an AI page as measured, its ceiling its two passes, and the
292
- * site's header and footer once when the wave builds them.
502
+ * site's header and footer once when the wave builds them. Pages the step has
503
+ * chosen a build for (AGL-4083) are priced by it: `arranged` at the
504
+ * arrangement answer and its listing, `copied` at the listing alone.
293
505
  */
294
506
  export declare function aiSiteImportCreditRange(pages: number, options?: {
295
507
  frame?: boolean;
508
+ arranged?: number;
509
+ copied?: number;
296
510
  }): AiCreditRange;
511
+ /** What a wave still owes, by how its pages are built, and whether its header and footer are owed. */
512
+ export declare function aiSiteImportWaveOwed(state: AiSiteImportState, wave: AiSiteImportWave, items?: readonly AiJobItemLedger[] | null): {
513
+ pages: number;
514
+ arranged: number;
515
+ copied: number;
516
+ frame: boolean;
517
+ };
297
518
  /** A wave's quote: the pages it has still to bring in, and its header and footer while they are owed. */
298
519
  export declare function aiSiteImportWaveCreditRange(state: AiSiteImportState, wave: AiSiteImportWave, items?: readonly AiJobItemLedger[] | null): AiCreditRange;
520
+ /** Every wave still waiting, quoted at once: "Start every remaining wave" (AGL-4083). */
521
+ export declare function aiSiteImportRemainingCreditRange(state: AiSiteImportState, items?: readonly AiJobItemLedger[] | null): AiCreditRange;
522
+ /** "About 95 credits (up to 600): 7 pages arranged by Aglyn AI, 3 copied as they are"; the build is named once the step has chosen. */
523
+ export declare function aiSiteImportQuoteText(range: AiCreditRange, owed: Pick<ReturnType<typeof aiSiteImportWaveOwed>, 'arranged' | 'copied'>): string;
299
524
  /** Why a page is not brought in, as a person reads it. */
300
525
  export declare const AI_SITE_IMPORT_LEFT_OUT_COPY: Readonly<Record<AiSiteImportLeftOutReason, string>>;
301
526
  /** "3 pages" / "1 page". */