@aglyn/plugins-ai 1.0.0-beta.260 → 1.0.0-beta.262

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 (81) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-brief-dialog.component.d.ts +21 -58
  3. package/src/lib/components/ai-brief-dialog.component.js +8 -132
  4. package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
  5. package/src/lib/components/ai-job-follow.component.js +2 -35
  6. package/src/lib/components/ai-job-follow.component.js.map +1 -1
  7. package/src/lib/components/ai-job-plan.component.d.ts +2 -8
  8. package/src/lib/components/ai-job-plan.component.js +13 -74
  9. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  10. package/src/lib/components/ai-site-start-card.component.js +43 -34
  11. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  12. package/src/lib/components/ai-template-collection-field.component.d.ts +2 -12
  13. package/src/lib/components/ai-template-collection-field.component.js +13 -10
  14. package/src/lib/components/ai-template-collection-field.component.js.map +1 -1
  15. package/src/lib/components/ai-upsell-dialog.component.d.ts +3 -25
  16. package/src/lib/components/ai-upsell-dialog.component.js +9 -66
  17. package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
  18. package/src/lib/components/use-ai-job-run.d.ts +3 -8
  19. package/src/lib/components/use-ai-job-run.js +4 -15
  20. package/src/lib/components/use-ai-job-run.js.map +1 -1
  21. package/src/lib/insights/ai-figure-readers.js +15 -5
  22. package/src/lib/insights/ai-figure-readers.js.map +1 -1
  23. package/src/lib/insights/ai-figure-summaries.d.ts +27 -0
  24. package/src/lib/insights/ai-figure-summaries.js +79 -0
  25. package/src/lib/insights/ai-figure-summaries.js.map +1 -0
  26. package/src/lib/insights/ai-insight-readers.d.ts +2 -1
  27. package/src/lib/insights/ai-insight-readers.js +11 -26
  28. package/src/lib/insights/ai-insight-readers.js.map +1 -1
  29. package/src/lib/jobs/ai-job-admission.d.ts +6 -0
  30. package/src/lib/jobs/ai-job-admission.js.map +1 -1
  31. package/src/lib/jobs/ai-job-site-step.js +10 -6
  32. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  33. package/src/lib/jobs/ai-layout-stock-photos.d.ts +43 -11
  34. package/src/lib/jobs/ai-layout-stock-photos.js +329 -32
  35. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  36. package/src/lib/jobs/ai-stock-photo-picker.js +13 -7
  37. package/src/lib/jobs/ai-stock-photo-picker.js.map +1 -1
  38. package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-recordings.d.ts +35 -0
  39. package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-recordings.js +404 -0
  40. package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-recordings.js.map +1 -0
  41. package/src/lib/layout-language/ai-layout-motion.d.ts +9 -3
  42. package/src/lib/layout-language/ai-layout-motion.js +9 -3
  43. package/src/lib/layout-language/ai-layout-motion.js.map +1 -1
  44. package/src/lib/layout-language/ai-layout-pictures.d.ts +10 -1
  45. package/src/lib/layout-language/ai-layout-pictures.js +12 -6
  46. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  47. package/src/lib/model/ai-brief-copy.d.ts +85 -0
  48. package/src/lib/model/ai-brief-copy.js +152 -0
  49. package/src/lib/model/ai-brief-copy.js.map +1 -0
  50. package/src/lib/model/ai-create-view.d.ts +200 -0
  51. package/src/lib/model/ai-create-view.js +263 -0
  52. package/src/lib/model/ai-create-view.js.map +1 -0
  53. package/src/lib/model/ai-insight.d.ts +1 -1
  54. package/src/lib/model/ai-insight.js +4 -39
  55. package/src/lib/model/ai-insight.js.map +1 -1
  56. package/src/lib/model/ai-job-activity.d.ts +24 -0
  57. package/src/lib/model/ai-job-activity.js +52 -1
  58. package/src/lib/model/ai-job-activity.js.map +1 -1
  59. package/src/lib/model/ai-job-plan-lines.d.ts +46 -0
  60. package/src/lib/model/ai-job-plan-lines.js +70 -0
  61. package/src/lib/model/ai-job-plan-lines.js.map +1 -0
  62. package/src/lib/model/ai-template-collections.d.ts +28 -0
  63. package/src/lib/model/ai-template-collections.js +28 -0
  64. package/src/lib/model/ai-template-collections.js.map +1 -0
  65. package/src/lib/model/ai-upsell-copy.d.ts +41 -0
  66. package/src/lib/model/ai-upsell-copy.js +85 -0
  67. package/src/lib/model/ai-upsell-copy.js.map +1 -0
  68. package/src/lib/runtime/ai-doctrine-validators.d.ts +40 -0
  69. package/src/lib/runtime/ai-doctrine-validators.js +148 -3
  70. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  71. package/src/lib/runtime/ai-doctrine.js +6 -2
  72. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  73. package/src/lib/runtime/ai-eval-memory-firestore.js +2 -0
  74. package/src/lib/runtime/ai-eval-memory-firestore.js.map +1 -1
  75. package/src/lib/server/ai-create-route.d.ts +32 -0
  76. package/src/lib/server/ai-create-route.js +295 -0
  77. package/src/lib/server/ai-create-route.js.map +1 -0
  78. package/src/lib/server/ai-jobs-route.js +19 -0
  79. package/src/lib/server/ai-jobs-route.js.map +1 -1
  80. package/src/lib/server.js +14 -0
  81. package/src/lib/server.js.map +1 -1
@@ -642,6 +642,15 @@ const COLOR_KEYWORDS = new Set([
642
642
  const LITERAL_COLOR = /#[0-9a-f]{3,8}\b|\b(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color)\s*\(|\b(?:black|white|red|green|blue|yellow|orange|purple|pink|gray|grey|brown|cyan|magenta|navy|teal|maroon|olive|lime|aqua|silver|gold|indigo|violet|beige|coral|crimson|salmon|tomato|turquoise|khaki|lavender|ivory|azure)\b/i;
643
643
  const LENGTH_WITH_UNIT = /-?\d*\.?\d+\s*(?:px|rem|em|pt|vh|vw|ch)\b/i;
644
644
  const paletteTokens = new Set(AI_SX_TOKENS.palette);
645
+ /**
646
+ * A color written as a value — a hex code or a color function — and never a
647
+ * color WORD. Plan prose names things in words: a photographer's "Blue Ridge
648
+ * elopements" or "Black and white portraits" is a place and a style, not a
649
+ * color, and the live 2026-10-16 guided start was refused for it (AGL-3864).
650
+ */ const LITERAL_COLOR_VALUE = /#[0-9a-f]{3,8}\b|\b(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color)\s*\(/i;
651
+ /** Whether plan prose writes a color by value: a hex code or a color function. */ function proseHasLiteralColor(value) {
652
+ return typeof value === 'string' && LITERAL_COLOR_VALUE.test(value);
653
+ }
645
654
  /** Whether a value names a color by value rather than by theme token. */ function hasLiteralColor(value) {
646
655
  if (typeof value !== 'string') return false;
647
656
  const parts = value.trim().split(/\s+/).filter((part)=>!paletteTokens.has(part) && !COLOR_KEYWORDS.has(part.toLowerCase()));
@@ -3892,6 +3901,53 @@ const FORM_SECTION_NAME = /\bform\b|\bsign[- ]?up\b|\bsubscribe\b|\bnewsletter\b
3892
3901
  screens
3893
3902
  }) : plan;
3894
3903
  }
3904
+ /**
3905
+ * References to nothing at all are taken out before the rules read the plan
3906
+ * (AGL-3845), where each has one reading. The live Free candle-shop start of
3907
+ * 2026-10-16, on an empty site, reused a layout `laySite` and duplicated a
3908
+ * page `scrStarter` the site does not have, and wrote what each section
3909
+ * shows into its uses ("hero", "product grid", "help text"). It was refused
3910
+ * for the unknown records, and the Free wall's trim, which never takes a
3911
+ * section that places something, could take none of them.
3912
+ *
3913
+ * - A reuse of a record the site does not have is dropped, and a page laid
3914
+ * out in it names no layout, for the layouts settle to place.
3915
+ * - A page duplicated from a page the site does not have starts fresh.
3916
+ * - A section's use that names no record the site has and no creation of the
3917
+ * plan is a description, not a reference, and comes out.
3918
+ *
3919
+ * Runs after `aiSettlePlanRefs`, so a creation spelled another way is
3920
+ * already pointed at, never dropped.
3921
+ */ export function aiSettlePlanUnknownIds(plan, inventory) {
3922
+ const kinds = inventoryKinds(inventory);
3923
+ const reuse = plan.reuse.filter((entry)=>entry.kind === 'theme' || kinds.get(entry.id) === entry.kind);
3924
+ const dropped = new Set(plan.reuse.filter((entry)=>!reuse.includes(entry)).map((entry)=>entry.id));
3925
+ const names = (ref)=>!!ref && (kinds.has(ref) || !!aiPlanCreateFor(plan, ref) || !!aiPlanItemFor(plan, ref) || isAiPlanNewRef(ref) && !dropped.has(ref));
3926
+ let changed = reuse.length !== plan.reuse.length;
3927
+ const screens = plan.screens.map((screen)=>{
3928
+ const layout = screen.layout && dropped.has(screen.layout) ? null : screen.layout;
3929
+ const duplicateOf = screen.duplicateOf && !kinds.has(screen.duplicateOf) ? null : screen.duplicateOf;
3930
+ const sections = screen.sections.map((section)=>{
3931
+ const uses = section.uses.filter(names);
3932
+ return uses.length === section.uses.length ? section : _extends({}, section, {
3933
+ uses
3934
+ });
3935
+ });
3936
+ if (layout === screen.layout && duplicateOf === screen.duplicateOf && sections.every((one, index)=>one === screen.sections[index])) {
3937
+ return screen;
3938
+ }
3939
+ changed = true;
3940
+ return _extends({}, screen, {
3941
+ layout,
3942
+ duplicateOf,
3943
+ sections
3944
+ });
3945
+ });
3946
+ return changed ? _extends({}, plan, {
3947
+ reuse,
3948
+ screens
3949
+ }) : plan;
3950
+ }
3895
3951
  /**
3896
3952
  * What customers say, which no dataset of the site's holds: a review, a
3897
3953
  * testimonial, a rating, a quote (AGL-3616). A job never writes them for the
@@ -4466,12 +4522,12 @@ const FORM_SECTION_NAME = /\bform\b|\bsign[- ]?up\b|\bsubscribe\b|\bnewsletter\b
4466
4522
  const paths = [];
4467
4523
  plan.create.forEach((entry, index)=>{
4468
4524
  if (entry.kind === 'theme-change') return;
4469
- if (hasLiteralColor(entry.why) || entry.fields.some(hasLiteralColor)) {
4525
+ if (proseHasLiteralColor(entry.why) || entry.fields.some(proseHasLiteralColor)) {
4470
4526
  paths.push(`create[${index}]`);
4471
4527
  }
4472
4528
  });
4473
4529
  for (const { section, path } of sectionPaths(plan)){
4474
- if (hasLiteralColor(section.name)) paths.push(path);
4530
+ if (proseHasLiteralColor(section.name)) paths.push(path);
4475
4531
  }
4476
4532
  return paths.length ? [
4477
4533
  {
@@ -4560,10 +4616,94 @@ const REUSE_BEFORE_CREATE_KINDS = new Set([
4560
4616
  * a component. Only the message reads it; the reference is refused whatever
4561
4617
  * it was meant to be.
4562
4618
  */ function undeclaredPlacementKind(name, section, plan, kinds) {
4563
- if (FORM_SECTION_NAME.test(name)) return 'form';
4619
+ if (readsAsForm(name)) return 'form';
4564
4620
  const placesForm = section.uses.some((ref)=>refKind(ref, plan, kinds) === 'form');
4565
4621
  return FORM_SECTION_NAME.test(section.name) && !placesForm ? 'form' : 'component';
4566
4622
  }
4623
+ /** A creation's name in words: `contactForm` → "contact Form", so `\\bform\\b` can read it. */ function nameInWords(name) {
4624
+ return name.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[-_]+/g, ' ').trim();
4625
+ }
4626
+ /** Whether a creation's name, camelCase or not, names a form. */ function readsAsForm(name) {
4627
+ return FORM_SECTION_NAME.test(nameInWords(name));
4628
+ }
4629
+ const NEWSLETTER_NAME = /\bnewsletter\b|\bsubscribe\b|\bsign[- ]?up\b/i;
4630
+ /**
4631
+ * A section that places a creation the plan never declares is settled
4632
+ * before the rules read the plan, where it has one answer (AGL-3845). The
4633
+ * live candle-shop start of 2026-10-16 placed `new:contactForm` in its
4634
+ * "Care, shipping and contact" section on both answers and stopped needing
4635
+ * input: the camelCase name read as a component, which a site start may not
4636
+ * make, and the refusal itself said to take it out.
4637
+ *
4638
+ * - A name that reads as a form, where this job may make one more form, is
4639
+ * declared as one: a newsletter's takes an email, any other the contact
4640
+ * form's fields, named in words ("Contact form") with the reference
4641
+ * pointed at it.
4642
+ * - Anything else this job could not make one more of is taken out of the
4643
+ * section's uses, and the section is drawn on the page — unless the
4644
+ * section itself is the form ("consultation request form"): without it
4645
+ * the section has no purpose, and what to put there instead is the
4646
+ * rule's to ask.
4647
+ * - A component the job could declare is left for the rule to ask about:
4648
+ * what it should be has more than one answer.
4649
+ */ export function aiSettlePlanUndeclaredUses(plan, inventory, capabilities = null) {
4650
+ if (!capabilities) return plan;
4651
+ const kinds = inventoryKinds(inventory);
4652
+ let next = plan;
4653
+ const repoint = new Map();
4654
+ const drop = new Set();
4655
+ for (const ref of aiPlanUndeclaredRefs(plan)){
4656
+ if (ref.field !== 'uses') continue;
4657
+ const key = ref.name.toLowerCase();
4658
+ if (repoint.has(key) || drop.has(ref.path)) continue;
4659
+ const section = plan.screens[ref.screenIndex].sections[ref.sectionIndex];
4660
+ const kind = undeclaredPlacementKind(ref.name, section, next, kinds);
4661
+ if (kind === 'form' && readsAsForm(ref.name) && !aiPlanUncreatableKind(next, 'form', capabilities)) {
4662
+ const words = nameInWords(ref.name).toLowerCase();
4663
+ const name = `${words.charAt(0).toUpperCase()}${words.slice(1)}`;
4664
+ repoint.set(key, `new:${name}`);
4665
+ if (!aiPlanCreateFor(next, `new:${name}`)) {
4666
+ next = _extends({}, next, {
4667
+ create: [
4668
+ ...next.create,
4669
+ {
4670
+ kind: 'form',
4671
+ name,
4672
+ why: AI_PLAN_SETTLED_FORM.why,
4673
+ duplicateOf: null,
4674
+ fields: NEWSLETTER_NAME.test(words) ? [
4675
+ 'email'
4676
+ ] : [
4677
+ ...AI_PLAN_SETTLED_FORM.fields
4678
+ ]
4679
+ }
4680
+ ]
4681
+ });
4682
+ }
4683
+ } else if (!FORM_SECTION_NAME.test(section.name) && aiPlanUncreatableKind(next, kind, capabilities)) {
4684
+ drop.add(ref.path);
4685
+ }
4686
+ }
4687
+ if (!repoint.size && !drop.size) return plan;
4688
+ const undeclaredName = (ref)=>isAiPlanNewRef(ref) && !aiPlanCreateFor(plan, ref) && !aiPlanItemFor(plan, ref) ? ref.slice('new:'.length).trim().toLowerCase() : null;
4689
+ return _extends({}, next, {
4690
+ screens: next.screens.map((screen, screenIndex)=>_extends({}, screen, {
4691
+ sections: screen.sections.map((section, sectionIndex)=>{
4692
+ const uses = section.uses.flatMap((ref, useIndex)=>{
4693
+ var _repoint_get;
4694
+ if (drop.has(`screens[${screenIndex}].sections[${sectionIndex}].uses[${useIndex}]`)) return [];
4695
+ const name = undeclaredName(ref);
4696
+ return [
4697
+ name !== null ? (_repoint_get = repoint.get(name)) != null ? _repoint_get : ref : ref
4698
+ ];
4699
+ });
4700
+ return uses.length === section.uses.length && uses.every((ref, index)=>ref === section.uses[index]) ? section : _extends({}, section, {
4701
+ uses
4702
+ });
4703
+ })
4704
+ }))
4705
+ });
4706
+ }
4567
4707
  /**
4568
4708
  * Rule 7 (plan): what a plan places, it reuses or it creates (AGL-3040). The
4569
4709
  * doctrine has a plan refer to what it creates as new:<name>, so a reference
@@ -4635,6 +4775,11 @@ const REUSE_BEFORE_CREATE_KINDS = new Set([
4635
4775
  ].includes(String(refKind(ref, plan, kinds))));
4636
4776
  if (bindsData) continue;
4637
4777
  if (section.items >= AI_TYPED_LIST_MIN_ITEMS) typed.push(path);
4778
+ // A form section COLLECTS what its dataset holds; it never copies it
4779
+ // (AGL-3864). A workspace whose other site made a "Consultation
4780
+ // requests" dataset refused a new law firm's "Consultation requests" form.
4781
+ const collects = FORM_SECTION_NAME.test(section.name) || section.uses.some((ref)=>refKind(ref, plan, kinds) === 'form');
4782
+ if (collects) continue;
4638
4783
  const match = data.find((row)=>aiNamesMatch(row.name, section.name));
4639
4784
  if (match) unbound.push({
4640
4785
  path,