@aglyn/plugins-ai 1.0.0-beta.292 → 1.0.0-beta.293

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 (149) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-site-import-job.component.js +5 -4
  3. package/src/lib/components/ai-site-import-job.component.js.map +1 -1
  4. package/src/lib/components/ai-site-import-start.component.js +3 -3
  5. package/src/lib/components/ai-site-import-start.component.js.map +1 -1
  6. package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
  7. package/src/lib/components/ai-site-start-card.component.js +129 -29
  8. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  9. package/src/lib/components/ai-site-start-steps.component.d.ts +15 -3
  10. package/src/lib/components/ai-site-start-steps.component.js +42 -25
  11. package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
  12. package/src/lib/components/ai-site-style-previews.d.ts +17 -0
  13. package/src/lib/components/ai-site-style-previews.js +26 -95
  14. package/src/lib/components/ai-site-style-previews.js.map +1 -1
  15. package/src/lib/declarations.server.js +22 -0
  16. package/src/lib/declarations.server.js.map +1 -1
  17. package/src/lib/jobs/ai-job-import-step.d.ts +21 -2
  18. package/src/lib/jobs/ai-job-import-step.js +179 -49
  19. package/src/lib/jobs/ai-job-import-step.js.map +1 -1
  20. package/src/lib/jobs/ai-job-layout-language.d.ts +27 -3
  21. package/src/lib/jobs/ai-job-layout-language.js +156 -113
  22. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  23. package/src/lib/jobs/ai-job-layout-step.js +56 -17
  24. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  25. package/src/lib/jobs/ai-job-page-language.d.ts +25 -15
  26. package/src/lib/jobs/ai-job-page-language.js +120 -66
  27. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  28. package/src/lib/jobs/ai-job-page-step.js +27 -7
  29. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  30. package/src/lib/jobs/ai-job-site-datasets.d.ts +12 -4
  31. package/src/lib/jobs/ai-job-site-datasets.js +36 -16
  32. package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
  33. package/src/lib/jobs/ai-jobs.js +6 -2
  34. package/src/lib/jobs/ai-jobs.js.map +1 -1
  35. package/src/lib/jobs/ai-layout-stock-photos.d.ts +50 -1
  36. package/src/lib/jobs/ai-layout-stock-photos.js +586 -35
  37. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  38. package/src/lib/jobs/ai-site-import-compile.d.ts +147 -0
  39. package/src/lib/jobs/ai-site-import-compile.js +1143 -0
  40. package/src/lib/jobs/ai-site-import-compile.js.map +1 -0
  41. package/src/lib/jobs/ai-site-import-document.d.ts +9 -36
  42. package/src/lib/jobs/ai-site-import-document.js +23 -489
  43. package/src/lib/jobs/ai-site-import-document.js.map +1 -1
  44. package/src/lib/jobs/ai-site-import-look.d.ts +54 -7
  45. package/src/lib/jobs/ai-site-import-look.js +163 -52
  46. package/src/lib/jobs/ai-site-import-look.js.map +1 -1
  47. package/src/lib/jobs/ai-site-import-publish.d.ts +7 -1
  48. package/src/lib/jobs/ai-site-import-publish.js +19 -5
  49. package/src/lib/jobs/ai-site-import-publish.js.map +1 -1
  50. package/src/lib/jobs/ai-site-late-photos.js +4 -2
  51. package/src/lib/jobs/ai-site-late-photos.js.map +1 -1
  52. package/src/lib/jobs/ai-site-publish.d.ts +10 -1
  53. package/src/lib/jobs/ai-site-publish.js +8 -3
  54. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  55. package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.d.ts +23 -0
  56. package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.js +677 -0
  57. package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.js.map +1 -0
  58. package/src/lib/layout-language/ai-layout-compiler.d.ts +26 -0
  59. package/src/lib/layout-language/ai-layout-compiler.js +677 -33
  60. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  61. package/src/lib/layout-language/ai-layout-frame-hollows.js +5 -1
  62. package/src/lib/layout-language/ai-layout-frame-hollows.js.map +1 -1
  63. package/src/lib/layout-language/ai-layout-frame-source.d.ts +196 -0
  64. package/src/lib/layout-language/ai-layout-frame-source.js +726 -0
  65. package/src/lib/layout-language/ai-layout-frame-source.js.map +1 -0
  66. package/src/lib/layout-language/ai-layout-frame.d.ts +22 -0
  67. package/src/lib/layout-language/ai-layout-frame.js +121 -29
  68. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  69. package/src/lib/layout-language/ai-layout-language.d.ts +103 -37
  70. package/src/lib/layout-language/ai-layout-language.js +85 -120
  71. package/src/lib/layout-language/ai-layout-language.js.map +1 -1
  72. package/src/lib/model/ai-credit-estimate.d.ts +11 -7
  73. package/src/lib/model/ai-credit-estimate.js +11 -7
  74. package/src/lib/model/ai-credit-estimate.js.map +1 -1
  75. package/src/lib/model/ai-site-import.d.ts +307 -13
  76. package/src/lib/model/ai-site-import.js +227 -19
  77. package/src/lib/model/ai-site-import.js.map +1 -1
  78. package/src/lib/model/ai-site-look.d.ts +65 -1
  79. package/src/lib/model/ai-site-look.js +141 -23
  80. package/src/lib/model/ai-site-look.js.map +1 -1
  81. package/src/lib/model/ai-site-photos.d.ts +2 -0
  82. package/src/lib/model/ai-site-photos.js.map +1 -1
  83. package/src/lib/model/ai-site-start-steps.d.ts +4 -3
  84. package/src/lib/model/ai-site-start-steps.js +11 -3
  85. package/src/lib/model/ai-site-start-steps.js.map +1 -1
  86. package/src/lib/model/ai-site-start.d.ts +23 -2
  87. package/src/lib/model/ai-site-start.js +25 -3
  88. package/src/lib/model/ai-site-start.js.map +1 -1
  89. package/src/lib/providers/routing.d.ts +11 -0
  90. package/src/lib/providers/routing.js +23 -0
  91. package/src/lib/providers/routing.js.map +1 -1
  92. package/src/lib/runtime/ai-doctrine-validators.d.ts +4 -2
  93. package/src/lib/runtime/ai-doctrine-validators.js +7 -2
  94. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  95. package/src/lib/runtime/ai-icon-library.d.ts +4 -0
  96. package/src/lib/runtime/ai-icon-library.js +4 -0
  97. package/src/lib/runtime/ai-icon-library.js.map +1 -1
  98. package/src/lib/runtime/ai-node-tree.js +5 -1
  99. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  100. package/src/lib/runtime/ai-palette.d.ts +2 -0
  101. package/src/lib/runtime/ai-palette.generated.js +19 -3
  102. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  103. package/src/lib/runtime/ai-palette.js +2 -1
  104. package/src/lib/runtime/ai-palette.js.map +1 -1
  105. package/src/lib/site-import/fixtures/site-layout-recordings.d.ts +50 -0
  106. package/src/lib/site-import/fixtures/site-layout-recordings.js +86 -0
  107. package/src/lib/site-import/fixtures/site-layout-recordings.js.map +1 -0
  108. package/src/lib/site-import/site-import-crawl.d.ts +18 -1
  109. package/src/lib/site-import/site-import-crawl.js +70 -10
  110. package/src/lib/site-import/site-import-crawl.js.map +1 -1
  111. package/src/lib/site-import/site-import-css.d.ts +110 -0
  112. package/src/lib/site-import/site-import-css.js +826 -0
  113. package/src/lib/site-import/site-import-css.js.map +1 -0
  114. package/src/lib/site-import/site-import-html.d.ts +116 -1
  115. package/src/lib/site-import/site-import-html.js +538 -46
  116. package/src/lib/site-import/site-import-html.js.map +1 -1
  117. package/src/lib/site-import/site-import-layout.d.ts +79 -0
  118. package/src/lib/site-import/site-import-layout.js +1608 -0
  119. package/src/lib/site-import/site-import-layout.js.map +1 -0
  120. package/src/lib/site-import/site-import-nav.d.ts +40 -0
  121. package/src/lib/site-import/site-import-nav.js +437 -0
  122. package/src/lib/site-import/site-import-nav.js.map +1 -0
  123. package/src/lib/site-import/site-import-page.d.ts +8 -1
  124. package/src/lib/site-import/site-import-page.js +24 -2
  125. package/src/lib/site-import/site-import-page.js.map +1 -1
  126. package/src/lib/assets/site-styles/beauty.webp +0 -0
  127. package/src/lib/assets/site-styles/blog.webp +0 -0
  128. package/src/lib/assets/site-styles/business.webp +0 -0
  129. package/src/lib/assets/site-styles/coming-soon.webp +0 -0
  130. package/src/lib/assets/site-styles/education.webp +0 -0
  131. package/src/lib/assets/site-styles/events.webp +0 -0
  132. package/src/lib/assets/site-styles/fitness.webp +0 -0
  133. package/src/lib/assets/site-styles/landing.webp +0 -0
  134. package/src/lib/assets/site-styles/music.webp +0 -0
  135. package/src/lib/assets/site-styles/nonprofit.webp +0 -0
  136. package/src/lib/assets/site-styles/personal.webp +0 -0
  137. package/src/lib/assets/site-styles/photography.webp +0 -0
  138. package/src/lib/assets/site-styles/portfolio.webp +0 -0
  139. package/src/lib/assets/site-styles/professional.webp +0 -0
  140. package/src/lib/assets/site-styles/realestate.webp +0 -0
  141. package/src/lib/assets/site-styles/restaurant.webp +0 -0
  142. package/src/lib/assets/site-styles/store.webp +0 -0
  143. package/src/lib/assets/site-styles/studio.webp +0 -0
  144. package/src/lib/assets/site-styles/trades.webp +0 -0
  145. package/src/lib/assets/site-styles/wellness.webp +0 -0
  146. package/src/lib/assets/site-styles/yoga.webp +0 -0
  147. package/src/lib/site-import/site-import-review.d.ts +0 -40
  148. package/src/lib/site-import/site-import-review.js +0 -149
  149. package/src/lib/site-import/site-import-review.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/plugins/ai/src/lib/declarations.server.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// Each registry from its own module, not the `@aglyn/aglyn/server` barrel:\n// boot needs three registries rather than the whole server surface, and a\n// suite that stubs that barrel wholesale still loads these declarations\n// against the real registries.\nimport {\n listPluginEventHandlers,\n registerPluginEventHandler,\n} from '@aglyn/aglyn/plugin-manager/plugin-events'\nimport {\n listPluginUserErasers,\n registerPluginUserEraser,\n} from '@aglyn/aglyn/plugin-manager/plugin-user-erasure'\nimport {\n listUsageAlertContributors,\n registerUsageAlertContributor,\n} from '@aglyn/aglyn/plugin-manager/usage-alert-contributors'\nimport { registerPluginUsageMeter } from '@aglyn/aglyn/plugin-manager/plugin-usage-meters'\nimport { registerOperatorAlerts } from '@aglyn/aglyn/plugin-manager/operator-alerts'\nimport { registerStockPhotoPicker } from '@aglyn/aglyn/plugin-manager/stock-photo-provider'\nimport { AI_PLUGIN_ID, AI_USAGE_METER_ID } from './constants'\nimport { AI_BUILD_PARTLY_FAILED, AI_JOB_FAILED, AI_PROVIDER_UNAVAILABLE } from './operator-alerts'\nimport { registerAiDeclarations } from './declarations'\n\n/** The provider-spend staff alerts' contributor id under the plugin. */\nconst AI_USAGE_ALERTS_ID = 'provider-spend'\n\n/**\n * The plugin's SERVER declarations (AGL-2939): what the server must know\n * at boot, before any door of the plugin has been called. The two platform\n * events the plugin writes activity for — the AI add-on bought or dropped,\n * an `ai.*` permission moved — are raised by core billing and membership\n * routes that never load the plugin's API surface, so the subscriptions\n * have to be in place from the first request. So does the eraser: an\n * account erasure is a core route too, and a person's AI usage months sit\n * under each org, keyed by the uid, where no core delete reaches them. So\n * does the usage alert contributor (AGL-2984): the usage-alerts sweep is a\n * core cron, and it evaluates the plugin's staff alerts on provider spend for\n * every org it reads.\n *\n * Light at boot by construction: the writers behind the handlers are\n * imported when the first event arrives, and the alert rules when the sweep\n * first evaluates an org, not when the process starts, so the boot cost is\n * the registration and nothing that touches Firestore.\n */\nexport function registerAiServerDeclarations(): void {\n registerAiDeclarations()\n // Idempotent against each REGISTRY rather than a module flag, so a\n // registry reset (a spec) or a second module instance registers again.\n if (!listPluginEventHandlers('org.seatAddons.changed').includes(AI_PLUGIN_ID)) {\n registerPluginEventHandler(\n 'org.seatAddons.changed',\n async ({ orgId, actor, before, after }) => {\n const { logAiAddonChanged } = await import('./activity/ai-activity')\n await logAiAddonChanged(orgId, actor, { before, after })\n },\n { pluginId: AI_PLUGIN_ID },\n )\n registerPluginEventHandler(\n 'org.permissions.changed',\n async ({ orgId, actor, subject, permission, granted }) => {\n if (!permission.startsWith('ai.')) return\n const { logAiPermissionChanged } = await import('./activity/ai-activity')\n await logAiPermissionChanged(orgId, actor, { subject, permission, granted })\n },\n { pluginId: AI_PLUGIN_ID },\n )\n }\n // THE PLATFORM'S PER-SITE TOGGLE DOOR (AGL-4047). The collaborators card\n // sets every per-site key through the platform's generic door now, so this\n // plugin keeps its display copy (`aiPermissions` on the site's roster row,\n // which the native apps read) in step from the verdict that door wrote.\n if (!listPluginEventHandlers('host.member.permissions.changed').includes(AI_PLUGIN_ID)) {\n registerPluginEventHandler(\n 'host.member.permissions.changed',\n async ({ hostId, memberId, permissions }) => {\n const { recordAiPermissionsOnRoster } = await import('./server/ai-host-permissions-roster')\n await recordAiPermissionsOnRoster(hostId, memberId, permissions)\n },\n { pluginId: AI_PLUGIN_ID },\n )\n }\n // THE PLATFORM'S BILLING EVENTS (AGL-3011). Subscribed at boot for the\n // same reason as the two above: they are raised by the billing webhook,\n // which never loads an AI door, and a workspace's overage standing moving\n // late is a workspace charged or refused against a fact we already had.\n //\n // The handler modules are imported when the first event arrives, so a\n // process that never receives one pays only for the registration.\n if (!listPluginEventHandlers('billing.invoice.paid').includes(AI_PLUGIN_ID)) {\n registerPluginEventHandler(\n 'billing.invoice.paid',\n async (payload) => {\n const { onAiBillingInvoicePaid } = await import('./billing/ai-overage-events')\n await onAiBillingInvoicePaid(payload)\n },\n { pluginId: AI_PLUGIN_ID },\n )\n registerPluginEventHandler(\n 'billing.invoice.failed',\n async (payload) => {\n const { onAiBillingInvoiceFailed } = await import('./billing/ai-overage-events')\n await onAiBillingInvoiceFailed(payload)\n },\n { pluginId: AI_PLUGIN_ID },\n )\n registerPluginEventHandler(\n 'billing.invoice.closed',\n async (payload) => {\n const { onAiBillingInvoiceClosed } = await import('./billing/ai-overage-events')\n await onAiBillingInvoiceClosed(payload)\n },\n { pluginId: AI_PLUGIN_ID },\n )\n registerPluginEventHandler(\n 'billing.dispute.opened',\n async (payload) => {\n const { onAiBillingDisputeOpened } = await import('./billing/ai-overage-events')\n await onAiBillingDisputeOpened(payload)\n },\n { pluginId: AI_PLUGIN_ID },\n )\n registerPluginEventHandler(\n 'billing.paymentMethod.changed',\n async (payload) => {\n const { onAiBillingPaymentMethodChanged } = await import(\n './billing/ai-overage-events'\n )\n await onAiBillingPaymentMethodChanged(payload)\n },\n { pluginId: AI_PLUGIN_ID },\n )\n }\n // The plugin's meter in the monthly usage sweep: the month's provider spend\n // onto the rollup, the credits past the band onto the invoice, and — from\n // the cutover month (AGL-3011) — the close-out of what the plugin invoices\n // itself. Registered at boot because the sweep is a core cron, and declared\n // in `usageAxes` so the sweep refuses to bill a month without it. The same\n // registration again replaces itself.\n registerPluginUsageMeter({\n pluginId: AI_PLUGIN_ID,\n id: AI_USAGE_METER_ID,\n measure: async (context) => {\n const { measureAiMonth } = await import('./billing/ai-month-meter')\n return measureAiMonth(context)\n },\n closeMonth: async (context) => {\n const { closeAiMonth } = await import('./billing/ai-month-meter')\n await closeAiMonth(context)\n },\n })\n if (!listPluginUserErasers().includes(AI_PLUGIN_ID)) {\n registerPluginUserEraser(\n async (request) => {\n const { eraseAiUsageForUser } = await import('./usage/ai-usage-eraser')\n return eraseAiUsageForUser(request)\n },\n { pluginId: AI_PLUGIN_ID },\n )\n }\n // The platform provider account alert (AGL-3377), and a job failing or a\n // build finishing with parts failed on our side (AGL-3683), listed on\n // Staff → Operator alerts before the first one is raised.\n registerOperatorAlerts([AI_PROVIDER_UNAVAILABLE, AI_JOB_FAILED, AI_BUILD_PARTLY_FAILED], {\n pluginId: AI_PLUGIN_ID,\n })\n // The stock photo picker another plugin's draft writer asks for (AGL-3755):\n // an event's cover, chosen as a page's pictures are. The choosing loads on\n // the first pick; registering again replaces it.\n registerStockPhotoPicker(\n {\n pick: async (request) => {\n const { aiStockPhotoPick } = await import('./jobs/ai-stock-photo-picker')\n return aiStockPhotoPick(request)\n },\n },\n { pluginId: AI_PLUGIN_ID },\n )\n if (\n !listUsageAlertContributors().some(\n (contributor) =>\n contributor.pluginId === AI_PLUGIN_ID &&\n contributor.id === AI_USAGE_ALERTS_ID,\n )\n ) {\n registerUsageAlertContributor({\n pluginId: AI_PLUGIN_ID,\n id: AI_USAGE_ALERTS_ID,\n evaluate: async (context) => {\n const { evaluateAiUsageAlerts } = await import('./usage/ai-usage-alerts')\n await evaluateAiUsageAlerts(context)\n },\n quotaChecks: async (context) => {\n const { aiQuotaChecks } = await import('./usage/ai-quota-checks')\n return aiQuotaChecks(context)\n },\n })\n }\n}\n\nregisterAiServerDeclarations()\n"],"names":["listPluginEventHandlers","registerPluginEventHandler","listPluginUserErasers","registerPluginUserEraser","listUsageAlertContributors","registerUsageAlertContributor","registerPluginUsageMeter","registerOperatorAlerts","registerStockPhotoPicker","AI_PLUGIN_ID","AI_USAGE_METER_ID","AI_BUILD_PARTLY_FAILED","AI_JOB_FAILED","AI_PROVIDER_UNAVAILABLE","registerAiDeclarations","AI_USAGE_ALERTS_ID","registerAiServerDeclarations","includes","orgId","actor","before","after","logAiAddonChanged","pluginId","subject","permission","granted","startsWith","logAiPermissionChanged","hostId","memberId","permissions","recordAiPermissionsOnRoster","payload","onAiBillingInvoicePaid","onAiBillingInvoiceFailed","onAiBillingInvoiceClosed","onAiBillingDisputeOpened","onAiBillingPaymentMethodChanged","id","measure","context","measureAiMonth","closeMonth","closeAiMonth","request","eraseAiUsageForUser","pick","aiStockPhotoPick","some","contributor","evaluate","evaluateAiUsageAlerts","quotaChecks","aiQuotaChecks"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../libs/plugins/ai/src/lib/declarations.server.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// Each registry from its own module, not the `@aglyn/aglyn/server` barrel:\n// boot needs three registries rather than the whole server surface, and a\n// suite that stubs that barrel wholesale still loads these declarations\n// against the real registries.\nimport {\n listPluginEventHandlers,\n registerPluginEventHandler,\n} from '@aglyn/aglyn/plugin-manager/plugin-events'\nimport {\n listPluginUserErasers,\n registerPluginUserEraser,\n} from '@aglyn/aglyn/plugin-manager/plugin-user-erasure'\nimport {\n listUsageAlertContributors,\n registerUsageAlertContributor,\n} from '@aglyn/aglyn/plugin-manager/usage-alert-contributors'\nimport { registerPluginUsageMeter } from '@aglyn/aglyn/plugin-manager/plugin-usage-meters'\nimport { registerOperatorAlerts } from '@aglyn/aglyn/plugin-manager/operator-alerts'\nimport { registerStockPhotoPicker } from '@aglyn/aglyn/plugin-manager/stock-photo-provider'\nimport { registerPluginSiteStyleLooks } from '@aglyn/aglyn/plugin-manager/plugin-site-style-looks'\nimport { listServerThemePresets } from '@aglyn/aglyn/plugin-manager/plugin-theme-presets'\nimport { AI_PLUGIN_ID, AI_USAGE_METER_ID } from './constants'\nimport { AI_BUILD_PARTLY_FAILED, AI_JOB_FAILED, AI_PROVIDER_UNAVAILABLE } from './operator-alerts'\nimport { registerAiDeclarations } from './declarations'\n\n/** The provider-spend staff alerts' contributor id under the plugin. */\nconst AI_USAGE_ALERTS_ID = 'provider-spend'\n\n/**\n * The plugin's SERVER declarations (AGL-2939): what the server must know\n * at boot, before any door of the plugin has been called. The two platform\n * events the plugin writes activity for — the AI add-on bought or dropped,\n * an `ai.*` permission moved — are raised by core billing and membership\n * routes that never load the plugin's API surface, so the subscriptions\n * have to be in place from the first request. So does the eraser: an\n * account erasure is a core route too, and a person's AI usage months sit\n * under each org, keyed by the uid, where no core delete reaches them. So\n * does the usage alert contributor (AGL-2984): the usage-alerts sweep is a\n * core cron, and it evaluates the plugin's staff alerts on provider spend for\n * every org it reads.\n *\n * Light at boot by construction: the writers behind the handlers are\n * imported when the first event arrives, and the alert rules when the sweep\n * first evaluates an org, not when the process starts, so the boot cost is\n * the registration and nothing that touches Firestore.\n */\nexport function registerAiServerDeclarations(): void {\n registerAiDeclarations()\n // Idempotent against each REGISTRY rather than a module flag, so a\n // registry reset (a spec) or a second module instance registers again.\n if (!listPluginEventHandlers('org.seatAddons.changed').includes(AI_PLUGIN_ID)) {\n registerPluginEventHandler(\n 'org.seatAddons.changed',\n async ({ orgId, actor, before, after }) => {\n const { logAiAddonChanged } = await import('./activity/ai-activity')\n await logAiAddonChanged(orgId, actor, { before, after })\n },\n { pluginId: AI_PLUGIN_ID },\n )\n registerPluginEventHandler(\n 'org.permissions.changed',\n async ({ orgId, actor, subject, permission, granted }) => {\n if (!permission.startsWith('ai.')) return\n const { logAiPermissionChanged } = await import('./activity/ai-activity')\n await logAiPermissionChanged(orgId, actor, { subject, permission, granted })\n },\n { pluginId: AI_PLUGIN_ID },\n )\n }\n // THE PLATFORM'S PER-SITE TOGGLE DOOR (AGL-4047). The collaborators card\n // sets every per-site key through the platform's generic door now, so this\n // plugin keeps its display copy (`aiPermissions` on the site's roster row,\n // which the native apps read) in step from the verdict that door wrote.\n if (!listPluginEventHandlers('host.member.permissions.changed').includes(AI_PLUGIN_ID)) {\n registerPluginEventHandler(\n 'host.member.permissions.changed',\n async ({ hostId, memberId, permissions }) => {\n const { recordAiPermissionsOnRoster } = await import('./server/ai-host-permissions-roster')\n await recordAiPermissionsOnRoster(hostId, memberId, permissions)\n },\n { pluginId: AI_PLUGIN_ID },\n )\n }\n // THE PLATFORM'S BILLING EVENTS (AGL-3011). Subscribed at boot for the\n // same reason as the two above: they are raised by the billing webhook,\n // which never loads an AI door, and a workspace's overage standing moving\n // late is a workspace charged or refused against a fact we already had.\n //\n // The handler modules are imported when the first event arrives, so a\n // process that never receives one pays only for the registration.\n if (!listPluginEventHandlers('billing.invoice.paid').includes(AI_PLUGIN_ID)) {\n registerPluginEventHandler(\n 'billing.invoice.paid',\n async (payload) => {\n const { onAiBillingInvoicePaid } = await import('./billing/ai-overage-events')\n await onAiBillingInvoicePaid(payload)\n },\n { pluginId: AI_PLUGIN_ID },\n )\n registerPluginEventHandler(\n 'billing.invoice.failed',\n async (payload) => {\n const { onAiBillingInvoiceFailed } = await import('./billing/ai-overage-events')\n await onAiBillingInvoiceFailed(payload)\n },\n { pluginId: AI_PLUGIN_ID },\n )\n registerPluginEventHandler(\n 'billing.invoice.closed',\n async (payload) => {\n const { onAiBillingInvoiceClosed } = await import('./billing/ai-overage-events')\n await onAiBillingInvoiceClosed(payload)\n },\n { pluginId: AI_PLUGIN_ID },\n )\n registerPluginEventHandler(\n 'billing.dispute.opened',\n async (payload) => {\n const { onAiBillingDisputeOpened } = await import('./billing/ai-overage-events')\n await onAiBillingDisputeOpened(payload)\n },\n { pluginId: AI_PLUGIN_ID },\n )\n registerPluginEventHandler(\n 'billing.paymentMethod.changed',\n async (payload) => {\n const { onAiBillingPaymentMethodChanged } = await import(\n './billing/ai-overage-events'\n )\n await onAiBillingPaymentMethodChanged(payload)\n },\n { pluginId: AI_PLUGIN_ID },\n )\n }\n // The plugin's meter in the monthly usage sweep: the month's provider spend\n // onto the rollup, the credits past the band onto the invoice, and — from\n // the cutover month (AGL-3011) — the close-out of what the plugin invoices\n // itself. Registered at boot because the sweep is a core cron, and declared\n // in `usageAxes` so the sweep refuses to bill a month without it. The same\n // registration again replaces itself.\n registerPluginUsageMeter({\n pluginId: AI_PLUGIN_ID,\n id: AI_USAGE_METER_ID,\n measure: async (context) => {\n const { measureAiMonth } = await import('./billing/ai-month-meter')\n return measureAiMonth(context)\n },\n closeMonth: async (context) => {\n const { closeAiMonth } = await import('./billing/ai-month-meter')\n await closeAiMonth(context)\n },\n })\n if (!listPluginUserErasers().includes(AI_PLUGIN_ID)) {\n registerPluginUserEraser(\n async (request) => {\n const { eraseAiUsageForUser } = await import('./usage/ai-usage-eraser')\n return eraseAiUsageForUser(request)\n },\n { pluginId: AI_PLUGIN_ID },\n )\n }\n // The platform provider account alert (AGL-3377), and a job failing or a\n // build finishing with parts failed on our side (AGL-3683), listed on\n // Staff → Operator alerts before the first one is raised.\n registerOperatorAlerts([AI_PROVIDER_UNAVAILABLE, AI_JOB_FAILED, AI_BUILD_PARTLY_FAILED], {\n pluginId: AI_PLUGIN_ID,\n })\n // The stock photo picker another plugin's draft writer asks for (AGL-3755):\n // an event's cover, chosen as a page's pictures are. The choosing loads on\n // the first pick; registering again replaces it.\n registerStockPhotoPicker(\n {\n pick: async (request) => {\n const { aiStockPhotoPick } = await import('./jobs/ai-stock-photo-picker')\n return aiStockPhotoPick(request)\n },\n },\n { pluginId: AI_PLUGIN_ID },\n )\n // The look each style starts a new site in (AGL-4049): Create site asks a\n // style before it creates anything, and the 21 styles are this plugin's\n // kinds of site. Asked on the create, so the look module loads then; the\n // built-in themes this deployment has are read at that moment too.\n registerPluginSiteStyleLooks(\n {\n look: async (styleId) => {\n const [{ aiSiteKind }, { aiSiteKindDefaultLook }] = await Promise.all([\n import('./model/ai-site-kinds'),\n import('./model/ai-site-look'),\n ])\n const kind = aiSiteKind(styleId)\n if (!kind) return null\n const presetIds = new Set(listServerThemePresets().map((preset) => preset.id))\n return aiSiteKindDefaultLook(kind, { presetIds }).theme\n },\n },\n { pluginId: AI_PLUGIN_ID },\n )\n if (\n !listUsageAlertContributors().some(\n (contributor) =>\n contributor.pluginId === AI_PLUGIN_ID &&\n contributor.id === AI_USAGE_ALERTS_ID,\n )\n ) {\n registerUsageAlertContributor({\n pluginId: AI_PLUGIN_ID,\n id: AI_USAGE_ALERTS_ID,\n evaluate: async (context) => {\n const { evaluateAiUsageAlerts } = await import('./usage/ai-usage-alerts')\n await evaluateAiUsageAlerts(context)\n },\n quotaChecks: async (context) => {\n const { aiQuotaChecks } = await import('./usage/ai-quota-checks')\n return aiQuotaChecks(context)\n },\n })\n }\n}\n\nregisterAiServerDeclarations()\n"],"names":["listPluginEventHandlers","registerPluginEventHandler","listPluginUserErasers","registerPluginUserEraser","listUsageAlertContributors","registerUsageAlertContributor","registerPluginUsageMeter","registerOperatorAlerts","registerStockPhotoPicker","registerPluginSiteStyleLooks","listServerThemePresets","AI_PLUGIN_ID","AI_USAGE_METER_ID","AI_BUILD_PARTLY_FAILED","AI_JOB_FAILED","AI_PROVIDER_UNAVAILABLE","registerAiDeclarations","AI_USAGE_ALERTS_ID","registerAiServerDeclarations","includes","orgId","actor","before","after","logAiAddonChanged","pluginId","subject","permission","granted","startsWith","logAiPermissionChanged","hostId","memberId","permissions","recordAiPermissionsOnRoster","payload","onAiBillingInvoicePaid","onAiBillingInvoiceFailed","onAiBillingInvoiceClosed","onAiBillingDisputeOpened","onAiBillingPaymentMethodChanged","id","measure","context","measureAiMonth","closeMonth","closeAiMonth","request","eraseAiUsageForUser","pick","aiStockPhotoPick","look","styleId","aiSiteKind","aiSiteKindDefaultLook","Promise","all","kind","presetIds","Set","map","preset","theme","some","contributor","evaluate","evaluateAiUsageAlerts","quotaChecks","aiQuotaChecks"],"mappings":"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"}
@@ -19,10 +19,10 @@ import type { AiJob, AiJobItemLedger } from '../model/ai-jobs.types';
19
19
  import { type AiSiteImportBuild, type AiSiteImportHome, type AiSiteImportLeftOutReason, type AiSiteImportPage, type AiSiteImportPageRead, type AiSiteImportState, type AiSiteImportWave } from '../model/ai-site-import';
20
20
  import { crawlSiteForImport, type SiteImportCrawlSeams, type SiteImportInventory } from '../site-import/site-import-crawl';
21
21
  import { type SiteImportPhotosDeps } from '../site-import/site-import-images';
22
+ import { type AiLayoutFrameSource, type AiLayoutFrameSourceGround, type AiLayoutFrameSourceTo } from '../layout-language/ai-layout-frame-source';
22
23
  import { type AiSiteImportDocument } from './ai-site-import-document';
23
24
  import { aiRunSiteImportLook, type AiSiteImportLookDeps } from './ai-site-import-look';
24
25
  import { aiPublishImportedWave } from './ai-site-import-publish';
25
- import { fileSiteImportReview } from '../site-import/site-import-review';
26
26
  import { type AiJobAdmission } from './ai-job-admission';
27
27
  import { readAiScreenRoom } from './ai-job-drafts';
28
28
  import { type AiLayoutSitePage } from './ai-job-layout-site-pages';
@@ -90,7 +90,6 @@ export interface AiSiteImportStepDeps {
90
90
  fetchImage?: SiteImportPhotosDeps['fetch'];
91
91
  readRoom?: typeof readAiScreenRoom;
92
92
  readSiteName?: (firestore: FirebaseFirestore.Firestore, hostId: string) => Promise<string>;
93
- fileReview?: typeof fileSiteImportReview;
94
93
  pageWritten?: typeof aiSitePageWritten;
95
94
  mintId?: () => string;
96
95
  clock?: () => number;
@@ -122,6 +121,26 @@ export declare function aiSiteImportBuildOf(page: Pick<AiSiteImportPage, 'group'
122
121
  * punctuation, else the person's.
123
122
  */
124
123
  export declare function aiSiteImportBrandName(given: string, own: string): string;
124
+ /**
125
+ * Where a link of the source's header or footer goes on the new site
126
+ * (AGL-4108): a page the import brings in by its id, once its wave is the
127
+ * header's or before it; a page of a later wave by its address on the new
128
+ * site, which the page takes when its wave lands, since a link by id to a
129
+ * page not yet live is taken out when a wave goes live; and anywhere else by
130
+ * the source's own address, so a button that leaves the source leaves the
131
+ * new site too. `null` for an address no link may carry.
132
+ */
133
+ export declare function aiSiteImportFrameLinkTo(state: Pick<AiSiteImportState, 'domain' | 'pages'>, wave: number): (href: string | null | undefined) => AiLayoutFrameSourceTo | null;
134
+ /** The ground a source's header bar sits on (AGL-4108): dark, the brand's color, the page's own, or paper. */
135
+ export declare function aiSiteImportHeaderGround(background: string | null, look: AiSiteImportHome['look']): AiLayoutFrameSourceGround;
136
+ /**
137
+ * The site's header and footer as the source builds them (AGL-4108): its
138
+ * menu with its menus and their entries, its buttons with their own
139
+ * addresses and how they are drawn, and its footer, every link resolved
140
+ * (`aiSiteImportFrameLinkTo`). Every label is the source's, as written.
141
+ * `null` where the reader found no menu on the home page.
142
+ */
143
+ export declare function aiSiteImportFrameSource(state: AiSiteImportState, wave: Pick<AiSiteImportWave, 'index'>): AiLayoutFrameSource | null;
125
144
  /** The header and footer's job: a layout of the site's brand, linking the wave's pages, in the layout language. */
126
145
  export declare function aiSiteImportFrameJob(job: AiJob, state: AiSiteImportState, wave: AiSiteImportWave, items: readonly AiJobItemLedger[]): AiJob;
127
146
  /** The page brought in, as its page job carries it: its lines, its photos, and the pages its links may go to (AGL-4084). */
@@ -21,8 +21,8 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
21
21
  *
22
22
  * 1. READ the proved site (no model): the crawler reads it as AglynBot,
23
23
  * main pages first, up to the plan's page count; the inventory is cut
24
- * into waves (`model/ai-site-import.ts`), every page gets a ledger row,
25
- * and Trust & Safety gets its row. The job then WAITS (`needs_review`,
24
+ * into waves (`model/ai-site-import.ts`) and every page gets a ledger
25
+ * row. The job then WAITS (`needs_review`,
26
26
  * reason `wave`): nothing is brought in until the person starts wave 1.
27
27
  * 2. START A WAVE (no model), on the person's say-so: the proof is still
28
28
  * current, the day's limit has room, the plan still has room; the
@@ -31,9 +31,13 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
31
31
  * 3. THE LOOK, in the first wave (no model, AGL-4084): the theme drawn
32
32
  * from the owner's own home page, its logo into the library, the site's
33
33
  * listing from the home page's own words (`ai-site-import-look.ts`).
34
- * 4. THE HEADER AND FOOTER, in the first wave: the layout step's own pass,
35
- * with the source's brand as its header draws it, its own header and
36
- * footer words as the footer's facts, and the site's menu order.
34
+ * 4. THE HEADER AND FOOTER, in the first wave: the source's own menu, its
35
+ * menus, its buttons and its footer as its home page builds them
36
+ * (AGL-4108, `aiSiteImportFrameSource`), drawn by the layout step with
37
+ * no model; for a source whose menu could not be read, the layout
38
+ * step's own pass, with the source's brand as its header draws it, its
39
+ * own header and footer words as the footer's facts, and the site's
40
+ * menu order.
37
41
  * 5. EACH PAGE: its photos into the media library (no model, deduped by
38
42
  * source key), then the page step's own passes, NEVER REWRITTEN
39
43
  * (AGL-4084, `ai-site-import-document.ts`): a collection's page or a
@@ -57,18 +61,20 @@ import { listDomainOwnershipClaims, normalizeOwnershipDomain } from "@aglyn/tena
57
61
  import { getOrgForUser, memberHasOrgPermission } from "@aglyn/tenant-data-admin/server/organizations";
58
62
  import { consumeRateLimit } from "@aglyn/tenant-data-admin/server/rate-limit-store";
59
63
  import { isServerReleaseFlagOnForOrg } from "@aglyn/tenant-data-admin/server/release-flags";
60
- import { AI_SITE_IMPORT_DOMAIN_INPUT, AI_SITE_IMPORT_FRAME_SLOT, AI_SITE_IMPORT_LABEL_MAX_CHARS, AI_SITE_IMPORT_LEFT_OUT_COPY, AI_SITE_IMPORT_MAX_PAGES, AI_SITE_IMPORT_MIN_WORDS, AI_SITE_IMPORT_WAVE_PAGES, aiSiteImportInventory, aiSiteImportItemOpen, aiSiteImportLedger, aiSiteImportNextWork, aiSiteImportPageBuild, aiSiteImportPageSettled, aiSiteImportPagesRead, aiSiteImportPhotosIn, aiSiteImportReadStops, aiSiteImportReadVerdict, aiSiteImportReadVerdictMessage, aiSiteImportReadVerdictReason, aiSiteImportWaveClosed, aiSiteImportWaveMessage, aiSiteImportWaveStarted } from "../model/ai-site-import.js";
64
+ import { AI_SITE_IMPORT_DOMAIN_INPUT, AI_SITE_IMPORT_FRAME_SLOT, AI_SITE_IMPORT_LABEL_MAX_CHARS, AI_SITE_IMPORT_LEFT_OUT_COPY, AI_SITE_IMPORT_MAX_PAGES, AI_SITE_IMPORT_MIN_WORDS, AI_SITE_IMPORT_WAVE_PAGES, aiSiteImportFrameIsRead, aiSiteImportInventory, aiSiteImportItemOpen, aiSiteImportLedger, aiSiteImportNextWork, aiSiteImportPageBuild, aiSiteImportPageSettled, aiSiteImportPagesRead, aiSiteImportPhotosIn, aiSiteImportReadStops, aiSiteImportReadVerdict, aiSiteImportReadVerdictMessage, aiSiteImportReadVerdictReason, aiSiteImportWaveClosed, aiSiteImportWaveMessage, aiSiteImportWaveStarted } from "../model/ai-site-import.js";
61
65
  import { aiModelForStep } from "../providers/routing.js";
62
66
  import { SITE_IMPORT_RELEASE_FLAG } from "../site-import/site-import-flag.js";
63
- import { crawlSiteForImport } from "../site-import/site-import-crawl.js";
67
+ import { crawlSiteForImport, siteImportOrigins } from "../site-import/site-import-crawl.js";
64
68
  import { bringInSiteImportPhotos } from "../site-import/site-import-images.js";
65
69
  import { siteImportHeaderBrand, siteImportLabel, siteImportLeftOutReason, siteImportListedPage, siteImportPageRead, siteImportPageWords, siteImportSiteFacts, siteImportSiteName } from "../site-import/site-import-page.js";
66
70
  import { aiSiteKindFor } from "../model/ai-site-kinds.js";
67
- import { AI_LAYOUT_SITE_BRAND_INPUT, AI_LAYOUT_SITE_FACTS_INPUT } from "./ai-job-layout-language.js";
68
- import { AI_LAYOUT_IMPORTED_SECTIONS_INPUT, aiSiteImportDocLines } from "./ai-site-import-document.js";
71
+ import { AI_LAYOUT_SITE_BRAND_INPUT, AI_LAYOUT_SITE_FACTS_INPUT, AI_LAYOUT_SITE_FRAME_INPUT } from "./ai-job-layout-language.js";
72
+ import { aiLayoutTelHref } from "../layout-language/ai-layout-frame-source.js";
73
+ import { aiHexOklch } from "../model/ai-site-look.js";
74
+ import { AI_LAYOUT_IMPORTED_SECTIONS_INPUT } from "./ai-site-import-document.js";
75
+ import { aiSiteImportSectionNames } from "./ai-site-import-compile.js";
69
76
  import { aiRunSiteImportLook } from "./ai-site-import-look.js";
70
77
  import { aiPublishImportedWave } from "./ai-site-import-publish.js";
71
- import { fileSiteImportReview } from "../site-import/site-import-review.js";
72
78
  import { aiUnitErrorRetryable, aiUnitFailure, aiUnitSpend } from "./ai-build-unit-outcome.js";
73
79
  import { registerAiJobAdmission } from "./ai-job-admission.js";
74
80
  import { AI_JOB_STEP_OVERHEAD_MS } from "./ai-job-budget.js";
@@ -210,9 +216,127 @@ const delivered = (items, slot)=>items.some((row)=>row.slot === slot && (row.sta
210
216
  } : {})
211
217
  } : {});
212
218
  }
219
+ /**
220
+ * Where a link of the source's header or footer goes on the new site
221
+ * (AGL-4108): a page the import brings in by its id, once its wave is the
222
+ * header's or before it; a page of a later wave by its address on the new
223
+ * site, which the page takes when its wave lands, since a link by id to a
224
+ * page not yet live is taken out when a wave goes live; and anywhere else by
225
+ * the source's own address, so a button that leaves the source leaves the
226
+ * new site too. `null` for an address no link may carry.
227
+ */ export function aiSiteImportFrameLinkTo(state, wave) {
228
+ const origins = new Set(siteImportOrigins(state.domain));
229
+ const key = (path)=>path.replace(/\/+$/, '') || '/';
230
+ const pages = new Map(state.pages.filter((page)=>!page.refused).map((page)=>[
231
+ key(page.path),
232
+ page
233
+ ]));
234
+ return (href)=>{
235
+ const raw = String(href != null ? href : '').trim();
236
+ if (!raw) return null;
237
+ if (/^tel:/i.test(raw)) return aiLayoutTelHref(raw.slice(4));
238
+ if (/^mailto:/i.test(raw)) return /^mailto:[^\s@<>?#"]+@[^\s@<>?#"]+\.[^\s@<>?#".]{2,}$/i.test(raw) ? raw : null;
239
+ let url;
240
+ try {
241
+ url = new URL(raw);
242
+ } catch (unused) {
243
+ return null;
244
+ }
245
+ if (url.protocol !== 'https:' && url.protocol !== 'http:') return null;
246
+ if (origins.has(`https://${url.host}`)) {
247
+ const page = pages.get(key(decodeURIComponent(url.pathname)));
248
+ if (page) return page.slug === '/' || page.wave <= wave ? `page:${page.draftId}` : `/${page.slug}`;
249
+ }
250
+ // Anywhere else stays where the source sends it, over https.
251
+ url.protocol = 'https:';
252
+ return url.toString();
253
+ };
254
+ }
255
+ /** The ground a source's header bar sits on (AGL-4108): dark, the brand's color, the page's own, or paper. */ export function aiSiteImportHeaderGround(background, look) {
256
+ const hex = /^#[0-9a-f]{6}$/i.test(background != null ? background : '') ? background : null;
257
+ if (!hex) return 'paper';
258
+ const own = aiHexOklch(hex);
259
+ if (own.L < 0.4) return 'dark';
260
+ const near = (other, within)=>{
261
+ if (!other || !/^#[0-9a-f]{6}$/i.test(other)) return false;
262
+ const them = aiHexOklch(other);
263
+ const a = own.C * Math.cos(own.h * Math.PI / 180) - them.C * Math.cos(them.h * Math.PI / 180);
264
+ const b = own.C * Math.sin(own.h * Math.PI / 180) - them.C * Math.sin(them.h * Math.PI / 180);
265
+ return Math.hypot(own.L - them.L, a, b) <= within;
266
+ };
267
+ if (near(look == null ? void 0 : look.buttonFill, 0.05)) return 'brand';
268
+ // The page's own ground, unless that ground is white, which paper is.
269
+ if (near(look == null ? void 0 : look.background, 0.02) && own.L < 0.99) return 'page';
270
+ return 'paper';
271
+ }
272
+ /**
273
+ * The site's header and footer as the source builds them (AGL-4108): its
274
+ * menu with its menus and their entries, its buttons with their own
275
+ * addresses and how they are drawn, and its footer, every link resolved
276
+ * (`aiSiteImportFrameLinkTo`). Every label is the source's, as written.
277
+ * `null` where the reader found no menu on the home page.
278
+ */ export function aiSiteImportFrameSource(state, wave) {
279
+ var _home_footer, _home_look, _footer_copyright;
280
+ const home = state.home;
281
+ const nav = home == null ? void 0 : home.nav;
282
+ if (!home || !nav || !aiSiteImportFrameIsRead(state)) return null;
283
+ const to = aiSiteImportFrameLinkTo(state, wave.index);
284
+ const link = (label, href)=>{
285
+ const target = to(href);
286
+ return label && target ? {
287
+ label,
288
+ to: target
289
+ } : null;
290
+ };
291
+ const present = (value)=>value !== null;
292
+ const footer = (_home_footer = home.footer) != null ? _home_footer : null;
293
+ return {
294
+ nav: nav.items.map((group)=>({
295
+ label: group.label,
296
+ to: to(group.href),
297
+ items: group.items.map((entry)=>{
298
+ var _entry_viewBox;
299
+ const target = link(entry.label, entry.href);
300
+ if (!target) return null;
301
+ return _extends({}, target, entry.text ? {
302
+ text: entry.text
303
+ } : {}, entry.iconPath && ((_entry_viewBox = entry.viewBox) != null ? _entry_viewBox : '0 0 24 24') === '0 0 24 24' ? {
304
+ iconPath: entry.iconPath
305
+ } : {}, entry.column ? {
306
+ column: entry.column
307
+ } : {});
308
+ }).filter(present)
309
+ })).filter((group)=>group.items.length || group.to),
310
+ ctas: nav.ctas.map((cta)=>{
311
+ const target = link(cta.label, cta.href);
312
+ return target ? _extends({}, target, {
313
+ style: cta.style
314
+ }) : null;
315
+ }).filter(present),
316
+ header: aiSiteImportHeaderGround(nav.background, (_home_look = home.look) != null ? _home_look : null),
317
+ footer: footer ? {
318
+ columns: footer.columns.map((column)=>({
319
+ heading: column.heading,
320
+ links: column.links.map((entry)=>link(entry.label, entry.href)).filter(present)
321
+ })).filter((column)=>column.links.length),
322
+ links: footer.links.map((entry)=>link(entry.label, entry.href)).filter(present),
323
+ text: footer.text,
324
+ social: footer.social.map((entry)=>/^https:\/\//i.test(entry.href) ? {
325
+ label: entry.label,
326
+ to: entry.href,
327
+ network: entry.network
328
+ } : null).filter(present),
329
+ address: footer.address,
330
+ phone: footer.phone,
331
+ email: footer.email,
332
+ // The © line as the source writes it; where it writes none, the source's own name.
333
+ copyright: (_footer_copyright = footer.copyright) != null ? _footer_copyright : home.siteName ? `© ${home.siteName}` : null
334
+ } : null
335
+ };
336
+ }
213
337
  /** The header and footer's job: a layout of the site's brand, linking the wave's pages, in the layout language. */ export function aiSiteImportFrameJob(job, state, wave, items) {
214
338
  var _ref, _state_home, _ref1, _ref2;
215
- var _state_frame, _state_look;
339
+ var _state_frame, _state_look, _home_brand_logo;
216
340
  const draftId = (_ref = (_state_frame = state.frame) == null ? void 0 : _state_frame.draftId) != null ? _ref : `${job.$id}-${AI_SITE_IMPORT_FRAME_SLOT}`;
217
341
  const creation = {
218
342
  kind: 'layout',
@@ -230,13 +354,23 @@ const delivered = (items, slot)=>items.some((row)=>row.slot === slot && (row.sta
230
354
  const home = (_state_home = state.home) != null ? _state_home : null;
231
355
  const logo = (_ref1 = (_state_look = state.look) == null ? void 0 : _state_look.logo) != null ? _ref1 : null;
232
356
  // The brand as the source's header draws it; with no logo in the library, its name.
233
- const brand = home ? _extends({}, logo && home.brand.kind !== 'name-only' ? {
357
+ // The logo is named as the source names it, never by the host's name (AGL-4108).
358
+ const withLogo = !!logo && !!home && home.brand.kind !== 'name-only';
359
+ const alt = home ? (((_home_brand_logo = home.brand.logo) == null ? void 0 : _home_brand_logo.alt) || home.siteName || home.brand.text).replace(/\s+/g, ' ').trim().slice(0, 120) : '';
360
+ const aspect = (logo == null ? void 0 : logo.width) && logo.height ? Math.round(logo.width / logo.height * 1000) / 1000 : null;
361
+ const brand = home ? _extends({}, withLogo ? _extends({
234
362
  logo: {
235
363
  src: logo.src
236
364
  }
237
- } : {}, {
365
+ }, alt ? {
366
+ alt
367
+ } : {}, aspect ? {
368
+ aspect
369
+ } : {}) : {}, {
238
370
  name: !logo || home.brand.kind !== 'logo-only'
239
371
  }) : null;
372
+ // The source's own menu, buttons and footer, drawn with no model (AGL-4108).
373
+ const source = aiSiteImportFrameSource(state, wave);
240
374
  return _extends({}, job, {
241
375
  $id: draftId,
242
376
  kind: 'layout',
@@ -256,6 +390,8 @@ const delivered = (items, slot)=>items.some((row)=>row.slot === slot && (row.sta
256
390
  [AI_LAYOUT_SITE_PAGES_INPUT]: aiSiteImportLinkedPages(state, items, wave.index)
257
391
  }, aiSiteImportDesignInputs(state), brand ? {
258
392
  [AI_LAYOUT_SITE_BRAND_INPUT]: brand
393
+ } : {}, source ? {
394
+ [AI_LAYOUT_SITE_FRAME_INPUT]: source
259
395
  } : {}, home ? {
260
396
  [AI_LAYOUT_SITE_FACTS_INPUT]: {
261
397
  url: home.url,
@@ -269,16 +405,18 @@ const delivered = (items, slot)=>items.some((row)=>row.slot === slot && (row.sta
269
405
  }
270
406
  /** The page brought in, as its page job carries it: its lines, its photos, and the pages its links may go to (AGL-4084). */ export function aiSiteImportDocumentFor(state, page) {
271
407
  var _page_build, _ref;
272
- var _page_read, _page_photos;
408
+ var _page_read, _page_photos, _page_read1;
273
409
  const blocks = (_page_read = page.read) == null ? void 0 : _page_read.blocks;
274
410
  if (!(blocks == null ? void 0 : blocks.length)) return null;
275
- return {
411
+ return _extends({
276
412
  build: (_page_build = page.build) != null ? _page_build : aiSiteImportBuildOf(page),
277
413
  title: page.title,
278
414
  url: page.url,
279
415
  blocks,
280
416
  photos: (_ref = (_page_photos = page.photos) == null ? void 0 : _page_photos.assets) != null ? _ref : [],
281
- // Home's own links are words: a link home says home, which the source's words may not.
417
+ // Every page the import plans, in any wave (AGL-4107): a link to one still
418
+ // to come shows its words until it is live. Home's own links are words: a
419
+ // link home says home, which the source's words may not.
282
420
  links: state.pages.filter((entry)=>entry.slug !== '/' && !entry.refused).map((entry)=>({
283
421
  path: entry.path.replace(/\/+$/, '') || '/',
284
422
  id: entry.draftId
@@ -287,14 +425,17 @@ const delivered = (items, slot)=>items.some((row)=>row.slot === slot && (row.sta
287
425
  `https://${state.domain}`,
288
426
  `https://www.${state.domain.replace(/^www\./, '')}`
289
427
  ]
290
- };
428
+ }, ((_page_read1 = page.read) == null ? void 0 : _page_read1.layout) ? {
429
+ layout: page.read.layout
430
+ } : {});
291
431
  }
292
432
  /** One page's job: a page job's plan of one screen, its sections the old page's own, its words carried as they are. */ export function aiSiteImportPageJob(job, state, page, items) {
293
- var _ref, _ref1, _ref2, _ref3;
294
- var _page_read, _state_home, _page_read1, _page_photos;
433
+ var _ref, _ref1, _ref2, _ref3, _ref4;
434
+ var _page_read, _page_read_meta, _page_read1, _page_read_meta1, _page_read2, _state_home, _page_read3, _page_photos;
295
435
  const doc = aiSiteImportDocumentFor(state, page);
296
- // The page's sections are the source's own (AGL-4084): its opening, then one at each second-level heading.
297
- const names = doc ? aiSiteImportDocLines(doc.blocks, doc.title).sections.map((section)=>section.name) : (_ref = (_page_read = page.read) == null ? void 0 : _page_read.sections) != null ? _ref : [];
436
+ // The page's sections are the source's own (AGL-4107): as its layout names
437
+ // them, else its opening and one at each second-level heading.
438
+ const names = doc ? aiSiteImportSectionNames(doc) : (_ref = (_page_read = page.read) == null ? void 0 : _page_read.sections) != null ? _ref : [];
298
439
  const sections = (names.length ? names : [
299
440
  'Introduction'
300
441
  ]).map((name)=>({
@@ -310,8 +451,9 @@ const delivered = (items, slot)=>items.some((row)=>row.slot === slot && (row.sta
310
451
  template: null,
311
452
  duplicateOf: null,
312
453
  nav: false,
313
- seoTitle: page.title,
314
- seoDescription: '',
454
+ // The source's own title and description, word for word (AGL-4107).
455
+ seoTitle: ((_page_read1 = page.read) == null ? void 0 : (_page_read_meta = _page_read1.meta) == null ? void 0 : _page_read_meta.title) || page.title,
456
+ seoDescription: (_ref1 = (_page_read2 = page.read) == null ? void 0 : (_page_read_meta1 = _page_read2.meta) == null ? void 0 : _page_read_meta1.description) != null ? _ref1 : '',
315
457
  sections,
316
458
  record: null,
317
459
  id: page.draftId
@@ -332,7 +474,7 @@ const delivered = (items, slot)=>items.some((row)=>row.slot === slot && (row.sta
332
474
  }),
333
475
  inputs: _extends({
334
476
  originJobId: job.$id,
335
- businessName: aiSiteImportBrandName(state.siteName, (_ref1 = (_state_home = state.home) == null ? void 0 : _state_home.siteName) != null ? _ref1 : ''),
477
+ businessName: aiSiteImportBrandName(state.siteName, (_ref2 = (_state_home = state.home) == null ? void 0 : _state_home.siteName) != null ? _ref2 : ''),
336
478
  [AI_LAYOUT_LANGUAGE_INPUT]: true,
337
479
  [AI_LAYOUT_SITE_PAGES_INPUT]: aiSiteImportLinkedPages(state, items, page.wave)
338
480
  }, aiSiteImportDesignInputs(state), doc ? {
@@ -340,10 +482,10 @@ const delivered = (items, slot)=>items.some((row)=>row.slot === slot && (row.sta
340
482
  } : {
341
483
  [AI_LAYOUT_IMPORTED_PAGE_INPUT]: {
342
484
  url: page.url,
343
- text: (_ref2 = (_page_read1 = page.read) == null ? void 0 : _page_read1.text) != null ? _ref2 : ''
485
+ text: (_ref3 = (_page_read3 = page.read) == null ? void 0 : _page_read3.text) != null ? _ref3 : ''
344
486
  }
345
487
  }, {
346
- [AI_LAYOUT_PAGE_PHOTOS_INPUT]: (_ref3 = (_page_photos = page.photos) == null ? void 0 : _page_photos.assets) != null ? _ref3 : []
488
+ [AI_LAYOUT_PAGE_PHOTOS_INPUT]: (_ref4 = (_page_photos = page.photos) == null ? void 0 : _page_photos.assets) != null ? _ref4 : []
347
489
  }, others.length ? {
348
490
  [AI_STOCK_PHOTO_PAGES_INPUT]: others
349
491
  } : {}),
@@ -380,7 +522,7 @@ const delivered = (items, slot)=>items.some((row)=>row.slot === slot && (row.sta
380
522
  const text = (line != null ? line : '').replace(/\s+/g, ' ').trim();
381
523
  if (text && !lines.some((entry)=>entry.includes(text))) lines.push(text);
382
524
  }
383
- return {
525
+ return _extends({
384
526
  url: page.url,
385
527
  title: siteImportLabel((_page_title = page.title) != null ? _page_title : '', 160),
386
528
  description: siteImportLabel((_page_description = page.description) != null ? _page_description : '', 320),
@@ -402,7 +544,13 @@ const delivered = (items, slot)=>items.some((row)=>row.slot === slot && (row.sta
402
544
  social: facts.social,
403
545
  address: (_ref = (_facts_organization = facts.organization) == null ? void 0 : _facts_organization.address) != null ? _ref : ''
404
546
  }
405
- };
547
+ }, page.nav ? {
548
+ nav: page.nav
549
+ } : {}, page.footer ? {
550
+ footer: page.footer
551
+ } : {}, page.look ? {
552
+ look: page.look
553
+ } : {});
406
554
  }
407
555
  /** The least time the import's next pass needs: a read's, a photo pass's, or the time of the step its unit is handed to. */ export function aiSiteImportRunMinimumMs(job, runnerFor = aiJobStepRunnerFor) {
408
556
  var _job_siteImport, _job_items;
@@ -499,7 +647,7 @@ async function readDraftWords(firestore, hostId, draftId) {
499
647
  return wave.slots.every((slot)=>!aiSiteImportItemOpen(items, slot));
500
648
  }
501
649
  export function createAiJobImportStep(deps = {}) {
502
- var _deps_crawl, _deps_runnerFor, _deps_ownership, _deps_rateLimit, _deps_releaseFlagOn, _deps_ingest, _deps_readRoom, _deps_readSiteName, _deps_fileReview, _deps_pageWritten, _deps_mintId, _deps_clock, _deps_look, _deps_publishWave, _deps_draftWords;
650
+ var _deps_crawl, _deps_runnerFor, _deps_ownership, _deps_rateLimit, _deps_releaseFlagOn, _deps_ingest, _deps_readRoom, _deps_readSiteName, _deps_pageWritten, _deps_mintId, _deps_clock, _deps_look, _deps_publishWave, _deps_draftWords;
503
651
  const crawl = (_deps_crawl = deps.crawl) != null ? _deps_crawl : crawlSiteForImport;
504
652
  const runnerFor = (_deps_runnerFor = deps.runnerFor) != null ? _deps_runnerFor : aiJobStepRunnerFor;
505
653
  const ownershipOf = (_deps_ownership = deps.ownership) != null ? _deps_ownership : readOwnership;
@@ -508,7 +656,6 @@ export function createAiJobImportStep(deps = {}) {
508
656
  const ingestOf = (_deps_ingest = deps.ingest) != null ? _deps_ingest : pluginMediaIngest;
509
657
  const readRoom = (_deps_readRoom = deps.readRoom) != null ? _deps_readRoom : readAiScreenRoom;
510
658
  const siteNameOf = (_deps_readSiteName = deps.readSiteName) != null ? _deps_readSiteName : readSiteName;
511
- const fileReview = (_deps_fileReview = deps.fileReview) != null ? _deps_fileReview : fileSiteImportReview;
512
659
  const pageWritten = (_deps_pageWritten = deps.pageWritten) != null ? _deps_pageWritten : aiSitePageWritten;
513
660
  const mintId = (_deps_mintId = deps.mintId) != null ? _deps_mintId : createResourceUid;
514
661
  const clock = (_deps_clock = deps.clock) != null ? _deps_clock : Date.now;
@@ -533,17 +680,6 @@ export function createAiJobImportStep(deps = {}) {
533
680
  const items = (_job_items = job.items) != null ? _job_items : [];
534
681
  const work = aiSiteImportNextWork(state, items);
535
682
  const nowIso = now.toISOString();
536
- const review = (event, next, ledger, wave)=>fileReview(firestore, _extends({
537
- orgId: job.orgId,
538
- hostId,
539
- jobId: job.$id,
540
- state: next,
541
- items: ledger,
542
- event,
543
- nowMs: now.getTime()
544
- }, wave !== undefined ? {
545
- wave
546
- } : {}));
547
683
  /** Closes the wave when this pass settled its last row: the review, or the job's end after the last wave. */ const closing = async (next, ledger, wave, outcome)=>{
548
684
  var _closed_waves_find;
549
685
  if (!waveSettled(next, wave, ledger)) return _extends({}, outcome, {
@@ -578,7 +714,6 @@ export function createAiJobImportStep(deps = {}) {
578
714
  const published = sitePublish ? {
579
715
  sitePublish
580
716
  } : {};
581
- await review('wave-finished', closed, ledger, wave.index);
582
717
  const waiting = ((_closed_waves_find = closed.waves.find((entry)=>entry.index === wave.index)) == null ? void 0 : _closed_waves_find.status) === 'review';
583
718
  if (waiting) {
584
719
  const message = (sitePublish == null ? void 0 : sitePublish.published.length) ? aiSiteImportPreviewMessage(closed, wave, sitePublish.published.length) : aiSiteImportWaveMessage(closed, ledger);
@@ -701,14 +836,10 @@ export function createAiJobImportStep(deps = {}) {
701
836
  home: aiSiteImportHomeOf(crawled)
702
837
  });
703
838
  const ledger = aiSiteImportLedger(planned);
704
- const reviewId = await review('read', planned, ledger);
705
- const next = _extends({}, planned, {
706
- reviewId
707
- });
708
839
  return unspent({
709
840
  items: ledger,
710
- siteImport: next,
711
- review: waveReview(aiSiteImportWaveMessage(next, ledger))
841
+ siteImport: planned,
842
+ review: waveReview(aiSiteImportWaveMessage(planned, ledger))
712
843
  });
713
844
  }
714
845
  if (!state) return unspent({
@@ -771,7 +902,6 @@ export function createAiJobImportStep(deps = {}) {
771
902
  }) : row);
772
903
  }
773
904
  const started = aiSiteImportWithBuilds(aiSiteImportWaveStarted(state, wave.index, nowIso, reads));
774
- await review('wave-started', started, ledger, wave.index);
775
905
  const running = started.waves.find((entry)=>entry.index === wave.index);
776
906
  return closing(started, ledger, running, unspent({
777
907
  items: ledger