@aglyn/plugins-ai 1.0.0-beta.230 → 1.0.0-beta.232

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 (305) hide show
  1. package/package.json +17 -17
  2. package/src/lib/activity/ai-activity-actions.js +8 -4
  3. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  4. package/src/lib/components/ai-assist-provider-on-host.component.d.ts +66 -0
  5. package/src/lib/components/ai-assist-provider-on-host.component.js +78 -0
  6. package/src/lib/components/ai-assist-provider-on-host.component.js.map +1 -0
  7. package/src/lib/components/ai-assist-provider.component.js +9 -10
  8. package/src/lib/components/ai-assist-provider.component.js.map +1 -1
  9. package/src/lib/components/ai-job-follow.component.js +1 -0
  10. package/src/lib/components/ai-job-follow.component.js.map +1 -1
  11. package/src/lib/components/ai-job-links.js +13 -0
  12. package/src/lib/components/ai-job-links.js.map +1 -1
  13. package/src/lib/components/ai-job-plan.component.js +3 -1
  14. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  15. package/src/lib/components/ai-jobs-list-page.component.js +1 -0
  16. package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
  17. package/src/lib/components/ai-media-create.component.d.ts +20 -7
  18. package/src/lib/components/ai-media-create.component.js +107 -55
  19. package/src/lib/components/ai-media-create.component.js.map +1 -1
  20. package/src/lib/components/ai-permissions-on-host.component.d.ts +3 -46
  21. package/src/lib/components/ai-permissions-on-host.component.js +5 -53
  22. package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
  23. package/src/lib/components/ai-seo-fields-card.component.js +1 -0
  24. package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
  25. package/src/lib/components/ai-site-build-page.component.js +20 -8
  26. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  27. package/src/lib/components/ai-site-memory-card.component.d.ts +32 -0
  28. package/src/lib/components/ai-site-memory-card.component.js +187 -0
  29. package/src/lib/components/ai-site-memory-card.component.js.map +1 -0
  30. package/src/lib/components/ai-site-start-card.component.js +80 -77
  31. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  32. package/src/lib/components/ai-theme-proposal-card.component.js +4 -0
  33. package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
  34. package/src/lib/components/ai-usage-strip.component.js +1 -0
  35. package/src/lib/components/ai-usage-strip.component.js.map +1 -1
  36. package/src/lib/components/assist-panel.component.d.ts +10 -0
  37. package/src/lib/components/assist-panel.component.js +119 -9
  38. package/src/lib/components/assist-panel.component.js.map +1 -1
  39. package/src/lib/components/assist-signals-page.component.js +2 -0
  40. package/src/lib/components/assist-signals-page.component.js.map +1 -1
  41. package/src/lib/components/billing-ai-allotments.component.js +3 -0
  42. package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
  43. package/src/lib/components/billing-ai-top-users.component.js +1 -0
  44. package/src/lib/components/billing-ai-top-users.component.js.map +1 -1
  45. package/src/lib/components/billing-assist-overage-card.component.js +2 -0
  46. package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
  47. package/src/lib/components/member-ai-usage-card.component.js +10 -0
  48. package/src/lib/components/member-ai-usage-card.component.js.map +1 -1
  49. package/src/lib/components/staff-org-ai-card.component.js +1 -0
  50. package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
  51. package/src/lib/components/staff-org-ai-conversations.component.d.ts +23 -0
  52. package/src/lib/components/staff-org-ai-conversations.component.js +346 -0
  53. package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -0
  54. package/src/lib/jobs/ai-build-capabilities.d.ts +13 -1
  55. package/src/lib/jobs/ai-build-capabilities.js +101 -7
  56. package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
  57. package/src/lib/jobs/ai-build-unit-outcome.js +4 -2
  58. package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
  59. package/src/lib/jobs/ai-business-profile-prefill.d.ts +29 -0
  60. package/src/lib/jobs/ai-business-profile-prefill.js +85 -0
  61. package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -0
  62. package/src/lib/jobs/ai-job-auto-confirm.d.ts +8 -0
  63. package/src/lib/jobs/ai-job-auto-confirm.js +30 -6
  64. package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -1
  65. package/src/lib/jobs/ai-job-build-step.js +14 -1
  66. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  67. package/src/lib/jobs/ai-job-edit-step.d.ts +225 -0
  68. package/src/lib/jobs/ai-job-edit-step.js +631 -0
  69. package/src/lib/jobs/ai-job-edit-step.js.map +1 -0
  70. package/src/lib/jobs/ai-job-generation.d.ts +8 -0
  71. package/src/lib/jobs/ai-job-generation.js +13 -2
  72. package/src/lib/jobs/ai-job-generation.js.map +1 -1
  73. package/src/lib/jobs/ai-job-insight-step.js +29 -5
  74. package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
  75. package/src/lib/jobs/ai-job-layout-language.d.ts +78 -0
  76. package/src/lib/jobs/ai-job-layout-language.js +200 -0
  77. package/src/lib/jobs/ai-job-layout-language.js.map +1 -0
  78. package/src/lib/jobs/ai-job-layout-site-pages.d.ts +13 -2
  79. package/src/lib/jobs/ai-job-layout-site-pages.js +18 -2
  80. package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
  81. package/src/lib/jobs/ai-job-layout-step.d.ts +5 -0
  82. package/src/lib/jobs/ai-job-layout-step.js +107 -6
  83. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  84. package/src/lib/jobs/ai-job-logic-step.d.ts +20 -0
  85. package/src/lib/jobs/ai-job-logic-step.js +87 -6
  86. package/src/lib/jobs/ai-job-logic-step.js.map +1 -1
  87. package/src/lib/jobs/ai-job-page-language.d.ts +194 -0
  88. package/src/lib/jobs/ai-job-page-language.js +418 -0
  89. package/src/lib/jobs/ai-job-page-language.js.map +1 -0
  90. package/src/lib/jobs/ai-job-page-sections.d.ts +37 -1
  91. package/src/lib/jobs/ai-job-page-sections.js +93 -11
  92. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  93. package/src/lib/jobs/ai-job-page-step.d.ts +4 -4
  94. package/src/lib/jobs/ai-job-page-step.js +115 -14
  95. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  96. package/src/lib/jobs/ai-job-plan-step.d.ts +20 -6
  97. package/src/lib/jobs/ai-job-plan-step.js +62 -22
  98. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  99. package/src/lib/jobs/ai-job-site-content.d.ts +163 -0
  100. package/src/lib/jobs/ai-job-site-content.js +443 -0
  101. package/src/lib/jobs/ai-job-site-content.js.map +1 -0
  102. package/src/lib/jobs/ai-job-site-look.d.ts +112 -0
  103. package/src/lib/jobs/ai-job-site-look.js +322 -0
  104. package/src/lib/jobs/ai-job-site-look.js.map +1 -0
  105. package/src/lib/jobs/ai-job-site-step.d.ts +30 -5
  106. package/src/lib/jobs/ai-job-site-step.js +183 -27
  107. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  108. package/src/lib/jobs/ai-job-workflow-step.js +2 -2
  109. package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
  110. package/src/lib/jobs/ai-jobs-notify.d.ts +6 -1
  111. package/src/lib/jobs/ai-jobs-notify.js +15 -3
  112. package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
  113. package/src/lib/jobs/ai-jobs.d.ts +1 -1
  114. package/src/lib/jobs/ai-jobs.js +17 -8
  115. package/src/lib/jobs/ai-jobs.js.map +1 -1
  116. package/src/lib/jobs/ai-layout-full-height.d.ts +40 -0
  117. package/src/lib/jobs/ai-layout-full-height.js +82 -0
  118. package/src/lib/jobs/ai-layout-full-height.js.map +1 -0
  119. package/src/lib/jobs/ai-layout-site-facts.d.ts +54 -0
  120. package/src/lib/jobs/ai-layout-site-facts.js +153 -0
  121. package/src/lib/jobs/ai-layout-site-facts.js.map +1 -0
  122. package/src/lib/layout-language/ai-layout-compiler.d.ts +154 -0
  123. package/src/lib/layout-language/ai-layout-compiler.js +1235 -0
  124. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -0
  125. package/src/lib/layout-language/ai-layout-copy.d.ts +73 -0
  126. package/src/lib/layout-language/ai-layout-copy.js +137 -0
  127. package/src/lib/layout-language/ai-layout-copy.js.map +1 -0
  128. package/src/lib/layout-language/ai-layout-frame.d.ts +52 -0
  129. package/src/lib/layout-language/ai-layout-frame.js +458 -0
  130. package/src/lib/layout-language/ai-layout-frame.js.map +1 -0
  131. package/src/lib/layout-language/ai-layout-gaps.d.ts +63 -0
  132. package/src/lib/layout-language/ai-layout-gaps.js +345 -0
  133. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -0
  134. package/src/lib/layout-language/ai-layout-language.d.ts +168 -0
  135. package/src/lib/layout-language/ai-layout-language.js +529 -0
  136. package/src/lib/layout-language/ai-layout-language.js.map +1 -0
  137. package/src/lib/layout-language/ai-layout-links.d.ts +80 -0
  138. package/src/lib/layout-language/ai-layout-links.js +109 -0
  139. package/src/lib/layout-language/ai-layout-links.js.map +1 -0
  140. package/src/lib/layout-language/ai-layout-pictures.d.ts +144 -0
  141. package/src/lib/layout-language/ai-layout-pictures.js +214 -0
  142. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -0
  143. package/src/lib/layout-language/ai-layout-store.d.ts +41 -0
  144. package/src/lib/layout-language/ai-layout-store.js +75 -0
  145. package/src/lib/layout-language/ai-layout-store.js.map +1 -0
  146. package/src/lib/layout-language/ai-layout-tree.d.ts +59 -0
  147. package/src/lib/layout-language/ai-layout-tree.js +84 -0
  148. package/src/lib/layout-language/ai-layout-tree.js.map +1 -0
  149. package/src/lib/lazy-widget.d.ts +45 -0
  150. package/src/lib/lazy-widget.js +53 -0
  151. package/src/lib/lazy-widget.js.map +1 -0
  152. package/src/lib/model/ai-build-job.js +2 -1
  153. package/src/lib/model/ai-build-job.js.map +1 -1
  154. package/src/lib/model/ai-build-progress.d.ts +20 -3
  155. package/src/lib/model/ai-build-progress.js +52 -7
  156. package/src/lib/model/ai-build-progress.js.map +1 -1
  157. package/src/lib/model/ai-edit-job.d.ts +109 -0
  158. package/src/lib/model/ai-edit-job.js +157 -0
  159. package/src/lib/model/ai-edit-job.js.map +1 -0
  160. package/src/lib/model/ai-host-publish-context.d.ts +51 -0
  161. package/src/lib/model/ai-host-publish-context.js +104 -0
  162. package/src/lib/model/ai-host-publish-context.js.map +1 -0
  163. package/src/lib/model/ai-job-activity.d.ts +22 -0
  164. package/src/lib/model/ai-job-activity.js +23 -1
  165. package/src/lib/model/ai-job-activity.js.map +1 -1
  166. package/src/lib/model/ai-job-failure-copy.d.ts +38 -1
  167. package/src/lib/model/ai-job-failure-copy.js +56 -1
  168. package/src/lib/model/ai-job-failure-copy.js.map +1 -1
  169. package/src/lib/model/ai-job-notice.d.ts +3 -1
  170. package/src/lib/model/ai-job-notice.js +31 -9
  171. package/src/lib/model/ai-job-notice.js.map +1 -1
  172. package/src/lib/model/ai-jobs.types.d.ts +22 -1
  173. package/src/lib/model/ai-jobs.types.js.map +1 -1
  174. package/src/lib/model/ai-logic-job.d.ts +18 -1
  175. package/src/lib/model/ai-logic-job.js +24 -3
  176. package/src/lib/model/ai-logic-job.js.map +1 -1
  177. package/src/lib/model/ai-media-credits.d.ts +8 -4
  178. package/src/lib/model/ai-media-credits.js +11 -4
  179. package/src/lib/model/ai-media-credits.js.map +1 -1
  180. package/src/lib/model/ai-media-kinds.d.ts +68 -0
  181. package/src/lib/model/ai-media-kinds.js +98 -0
  182. package/src/lib/model/ai-media-kinds.js.map +1 -0
  183. package/src/lib/model/ai-site-build-progress.d.ts +9 -1
  184. package/src/lib/model/ai-site-build-progress.js +54 -14
  185. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  186. package/src/lib/model/ai-site-context.d.ts +111 -0
  187. package/src/lib/model/ai-site-context.js +222 -0
  188. package/src/lib/model/ai-site-context.js.map +1 -0
  189. package/src/lib/model/ai-site-job.d.ts +18 -2
  190. package/src/lib/model/ai-site-job.js +21 -5
  191. package/src/lib/model/ai-site-job.js.map +1 -1
  192. package/src/lib/model/ai-site-kinds.d.ts +164 -0
  193. package/src/lib/model/ai-site-kinds.js +1942 -0
  194. package/src/lib/model/ai-site-kinds.js.map +1 -0
  195. package/src/lib/model/ai-site-look.d.ts +150 -0
  196. package/src/lib/model/ai-site-look.js +1236 -0
  197. package/src/lib/model/ai-site-look.js.map +1 -0
  198. package/src/lib/model/ai-site-memory.d.ts +80 -0
  199. package/src/lib/model/ai-site-memory.js +171 -0
  200. package/src/lib/model/ai-site-memory.js.map +1 -0
  201. package/src/lib/model/ai-site-start-seo.d.ts +1 -1
  202. package/src/lib/model/ai-site-start.d.ts +15 -3
  203. package/src/lib/model/ai-site-start.js +21 -4
  204. package/src/lib/model/ai-site-start.js.map +1 -1
  205. package/src/lib/model/assist-follow-up.d.ts +92 -0
  206. package/src/lib/model/assist-follow-up.js +128 -0
  207. package/src/lib/model/assist-follow-up.js.map +1 -0
  208. package/src/lib/plugin-config.js +2 -0
  209. package/src/lib/plugin-config.js.map +1 -1
  210. package/src/lib/plugin.js +99 -48
  211. package/src/lib/plugin.js.map +1 -1
  212. package/src/lib/providers/anthropic.d.ts +9 -1
  213. package/src/lib/providers/anthropic.js +143 -9
  214. package/src/lib/providers/anthropic.js.map +1 -1
  215. package/src/lib/providers/catalog.d.ts +38 -8
  216. package/src/lib/providers/catalog.js +106 -14
  217. package/src/lib/providers/catalog.js.map +1 -1
  218. package/src/lib/providers/contract.d.ts +29 -0
  219. package/src/lib/providers/contract.js.map +1 -1
  220. package/src/lib/providers/image-contract.d.ts +22 -1
  221. package/src/lib/providers/image-contract.js +40 -1
  222. package/src/lib/providers/image-contract.js.map +1 -1
  223. package/src/lib/providers/image-endpoints.d.ts +24 -0
  224. package/src/lib/providers/image-endpoints.js +28 -0
  225. package/src/lib/providers/image-endpoints.js.map +1 -0
  226. package/src/lib/providers/model-choice.js +23 -2
  227. package/src/lib/providers/model-choice.js.map +1 -1
  228. package/src/lib/providers/routing.js +18 -0
  229. package/src/lib/providers/routing.js.map +1 -1
  230. package/src/lib/providers/vertex-image-endpoint.d.ts +48 -0
  231. package/src/lib/providers/vertex-image-endpoint.js +80 -0
  232. package/src/lib/providers/vertex-image-endpoint.js.map +1 -0
  233. package/src/lib/providers/vertex-image.d.ts +13 -27
  234. package/src/lib/providers/vertex-image.js +15 -42
  235. package/src/lib/providers/vertex-image.js.map +1 -1
  236. package/src/lib/runtime/ai-blog-post-generation.d.ts +62 -0
  237. package/src/lib/runtime/ai-blog-post-generation.js +89 -0
  238. package/src/lib/runtime/ai-blog-post-generation.js.map +1 -0
  239. package/src/lib/runtime/ai-dev-env.d.ts +53 -0
  240. package/src/lib/runtime/ai-dev-env.js +72 -0
  241. package/src/lib/runtime/ai-dev-env.js.map +1 -0
  242. package/src/lib/runtime/ai-dev-replay.d.ts +116 -0
  243. package/src/lib/runtime/ai-dev-replay.js +313 -0
  244. package/src/lib/runtime/ai-dev-replay.js.map +1 -0
  245. package/src/lib/runtime/ai-doctrine-validators.d.ts +82 -0
  246. package/src/lib/runtime/ai-doctrine-validators.js +436 -4
  247. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  248. package/src/lib/runtime/ai-doctrine.js +16 -3
  249. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  250. package/src/lib/runtime/ai-eval-live.js +4 -0
  251. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  252. package/src/lib/runtime/ai-eval.d.ts +13 -1
  253. package/src/lib/runtime/ai-eval.js +84 -1
  254. package/src/lib/runtime/ai-eval.js.map +1 -1
  255. package/src/lib/runtime/ai-icon-library.d.ts +257 -0
  256. package/src/lib/runtime/ai-icon-library.js +254 -0
  257. package/src/lib/runtime/ai-icon-library.js.map +1 -0
  258. package/src/lib/runtime/ai-live-batch.d.ts +45 -0
  259. package/src/lib/runtime/ai-live-batch.js +89 -0
  260. package/src/lib/runtime/ai-live-batch.js.map +1 -0
  261. package/src/lib/runtime/ai-node-tree.d.ts +9 -0
  262. package/src/lib/runtime/ai-node-tree.js +30 -4
  263. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  264. package/src/lib/runtime/ai-palette.d.ts +6 -0
  265. package/src/lib/runtime/ai-palette.generated.js +70 -0
  266. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  267. package/src/lib/runtime/ai-palette.js.map +1 -1
  268. package/src/lib/runtime/ai-runtime.js +46 -2
  269. package/src/lib/runtime/ai-runtime.js.map +1 -1
  270. package/src/lib/runtime/site-context.d.ts +58 -0
  271. package/src/lib/runtime/site-context.js +127 -0
  272. package/src/lib/runtime/site-context.js.map +1 -0
  273. package/src/lib/server/ai-admin-conversations.d.ts +22 -0
  274. package/src/lib/server/ai-admin-conversations.js +197 -0
  275. package/src/lib/server/ai-admin-conversations.js.map +1 -0
  276. package/src/lib/server/ai-media-image.d.ts +5 -5
  277. package/src/lib/server/ai-media-image.js +25 -11
  278. package/src/lib/server/ai-media-image.js.map +1 -1
  279. package/src/lib/server/ai-media-raster-prompt.d.ts +34 -0
  280. package/src/lib/server/ai-media-raster-prompt.js +53 -0
  281. package/src/lib/server/ai-media-raster-prompt.js.map +1 -0
  282. package/src/lib/server/assist-chat.d.ts +11 -0
  283. package/src/lib/server/assist-chat.js +57 -18
  284. package/src/lib/server/assist-chat.js.map +1 -1
  285. package/src/lib/server/assist-edit-applied.js +16 -3
  286. package/src/lib/server/assist-edit-applied.js.map +1 -1
  287. package/src/lib/server/assist-view-context.d.ts +19 -1
  288. package/src/lib/server/assist-view-context.js +38 -1
  289. package/src/lib/server/assist-view-context.js.map +1 -1
  290. package/src/lib/server.js +10 -0
  291. package/src/lib/server.js.map +1 -1
  292. package/src/lib/subprocessors.js +17 -1
  293. package/src/lib/subprocessors.js.map +1 -1
  294. package/src/lib/tools/ai-blog-post-tool.d.ts +60 -0
  295. package/src/lib/tools/ai-blog-post-tool.js +175 -0
  296. package/src/lib/tools/ai-blog-post-tool.js.map +1 -0
  297. package/src/lib/tools/ai-workflow-tool.d.ts +6 -2
  298. package/src/lib/tools/ai-workflow-tool.js +16 -14
  299. package/src/lib/tools/ai-workflow-tool.js.map +1 -1
  300. package/src/lib/usage/assist-usage.d.ts +3 -0
  301. package/src/lib/usage/assist-usage.js +4 -2
  302. package/src/lib/usage/assist-usage.js.map +1 -1
  303. package/src/lib/usage/staff-org-ai-conversations.d.ts +103 -0
  304. package/src/lib/usage/staff-org-ai-conversations.js +152 -0
  305. package/src/lib/usage/staff-org-ai-conversations.js.map +1 -0
@@ -0,0 +1,111 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { type ResolvedBusinessProfile } from '@aglyn/aglyn/app-utils/business-profile';
18
+ import type { AiSystemBlock } from '../providers/contract';
19
+ import type { AiHostPublishContext } from './ai-host-publish-context';
20
+ import type { AiSiteInventory } from './ai-site-inventory';
21
+ /**
22
+ * What every AI job knows about the site it works on (AGL-3661), as ONE
23
+ * compact block: the business profile, what the site already has and whether
24
+ * it is live, and what the owner's edits taught it.
25
+ *
26
+ * ── Why it exists ─────────────────────────────────────────────────────────
27
+ *
28
+ * Before it, a job saw its brief, the guided start's answers and the site's
29
+ * name. It named a Free site "Austin Paws Grooming" that was called nothing
30
+ * of the kind, and nothing stopped it inventing a phone number. The block
31
+ * gives it the facts the owner entered and a standing rule for the ones they
32
+ * did not: never invent a name, a contact detail, a price or a review.
33
+ *
34
+ * ── Where it sits, and what that costs ────────────────────────────────────
35
+ *
36
+ * {@link aiSiteContextSystemBlock} marks it a `cacheBreakpoint`, placed AFTER
37
+ * every platform-wide cached block and before the volatile ones. The
38
+ * platform's prefix is still shared by every workspace; this block extends it
39
+ * with one cache entry per site, which every request of a job — each re-ask,
40
+ * each section of a page — reads at the cache rate. It changes only when the
41
+ * owner edits the profile, a page is published, or an edit is remembered.
42
+ *
43
+ * ── Held to a ceiling ─────────────────────────────────────────────────────
44
+ *
45
+ * {@link AI_SITE_CONTEXT_MAX_CHARS}: 400 tokens at the runtime's four
46
+ * characters a token. Lines are dropped from the least useful up — the site
47
+ * index, the profiles, then the preferences — and the closing rule is never
48
+ * dropped, because it is the line that stops an invented phone number.
49
+ */
50
+ /** The block's ceiling: 400 tokens at four characters a token. */
51
+ export declare const AI_SITE_CONTEXT_MAX_CHARS = 1600;
52
+ /** Pages, forms, components and layouts listed by name in the site index, each. */
53
+ export declare const AI_SITE_CONTEXT_INDEX_PER_KIND = 8;
54
+ /** The rule every block closes with, whatever else it says. */
55
+ export declare const AI_SITE_CONTEXT_RULE = "Never invent a business name, a person\u2019s name, an email address, a phone number, a street address, opening hours, prices, reviews or testimonials. Use only the facts above; where a page needs one that is missing, write around it or leave a bracketed placeholder such as [phone number] for the owner to fill.";
56
+ export interface AiSiteContextInput {
57
+ /** The resolved business profile; `null` when the site has none to read. */
58
+ profile: ResolvedBusinessProfile | null;
59
+ /**
60
+ * The site's inventory, for a caller that does NOT already send it. A job
61
+ * whose prompt carries the inventory block (the plan step and the writers
62
+ * built on the doctrine) leaves this out: the block would only repeat it.
63
+ */
64
+ inventory?: AiSiteInventory | null;
65
+ /** Whether the site is live and where. */
66
+ publish?: AiHostPublishContext | null;
67
+ /** What the owner's edits taught, strongest first. */
68
+ preferences?: readonly string[];
69
+ /**
70
+ * Whether the block may carry contact details. An answer that must name
71
+ * nobody and print no address (the insight rules) leaves them out.
72
+ */
73
+ contact?: boolean;
74
+ }
75
+ /**
76
+ * The site's index (AGL-3661): what it already has, by name and id, so a job
77
+ * reuses before it builds. Pages say whether they are live; a form says how
78
+ * many fields it asks for. Pure, so a caller holding an inventory pays one
79
+ * render and no read.
80
+ */
81
+ export declare function aiSiteInventoryIndex(inventory: AiSiteInventory | null | undefined, publish?: AiHostPublishContext | null): string[];
82
+ /**
83
+ * The block as text (AGL-3661), never over {@link AI_SITE_CONTEXT_MAX_CHARS}.
84
+ * `''` when there is nothing to say, so a caller sends no block at all.
85
+ */
86
+ export declare function aiSiteContextBlock(input: AiSiteContextInput): string;
87
+ /**
88
+ * The block as the system block a door appends after its own cached blocks
89
+ * (AGL-3661): a `cacheBreakpoint` of its own, so the platform's prefix ahead
90
+ * of it is still shared and this one is cached per site. `[]` when there is
91
+ * nothing to say.
92
+ *
93
+ * A door already carrying the runtime's limit of breakpoints passes
94
+ * `cache: false`, and the block rides as a volatile block after them instead.
95
+ */
96
+ export declare function aiSiteContextSystemBlock(input: AiSiteContextInput | null | undefined, options?: {
97
+ cache?: boolean;
98
+ }): AiSystemBlock[];
99
+ /**
100
+ * A door's own instructions with the site context after them (AGL-3661) —
101
+ * THE call a generator makes to give its model the site:
102
+ *
103
+ * instructions: aiInstructionsWithSiteContext(MY_INSTRUCTIONS, context)
104
+ *
105
+ * The door's last instruction block is marked a breakpoint, so the
106
+ * platform-wide prefix (the doctrine and the door's rules) stays one cache
107
+ * entry for every workspace, and the context block after it is the per-site
108
+ * entry. Without a context it hands the instructions back unchanged, so a
109
+ * door's prompt is byte-for-byte what it was for a job that has no site.
110
+ */
111
+ export declare function aiInstructionsWithSiteContext(instructions: readonly AiSystemBlock[], context: AiSiteContextInput | null | undefined): readonly AiSystemBlock[];
@@ -0,0 +1,222 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { BUSINESS_TONE_LABELS } from "@aglyn/aglyn/app-utils/business-profile";
18
+ /**
19
+ * What every AI job knows about the site it works on (AGL-3661), as ONE
20
+ * compact block: the business profile, what the site already has and whether
21
+ * it is live, and what the owner's edits taught it.
22
+ *
23
+ * ── Why it exists ─────────────────────────────────────────────────────────
24
+ *
25
+ * Before it, a job saw its brief, the guided start's answers and the site's
26
+ * name. It named a Free site "Austin Paws Grooming" that was called nothing
27
+ * of the kind, and nothing stopped it inventing a phone number. The block
28
+ * gives it the facts the owner entered and a standing rule for the ones they
29
+ * did not: never invent a name, a contact detail, a price or a review.
30
+ *
31
+ * ── Where it sits, and what that costs ────────────────────────────────────
32
+ *
33
+ * {@link aiSiteContextSystemBlock} marks it a `cacheBreakpoint`, placed AFTER
34
+ * every platform-wide cached block and before the volatile ones. The
35
+ * platform's prefix is still shared by every workspace; this block extends it
36
+ * with one cache entry per site, which every request of a job — each re-ask,
37
+ * each section of a page — reads at the cache rate. It changes only when the
38
+ * owner edits the profile, a page is published, or an edit is remembered.
39
+ *
40
+ * ── Held to a ceiling ─────────────────────────────────────────────────────
41
+ *
42
+ * {@link AI_SITE_CONTEXT_MAX_CHARS}: 400 tokens at the runtime's four
43
+ * characters a token. Lines are dropped from the least useful up — the site
44
+ * index, the profiles, then the preferences — and the closing rule is never
45
+ * dropped, because it is the line that stops an invented phone number.
46
+ */ /** The block's ceiling: 400 tokens at four characters a token. */ export const AI_SITE_CONTEXT_MAX_CHARS = 1600;
47
+ /** Pages, forms, components and layouts listed by name in the site index, each. */ export const AI_SITE_CONTEXT_INDEX_PER_KIND = 8;
48
+ /** The rule every block closes with, whatever else it says. */ export const AI_SITE_CONTEXT_RULE = 'Never invent a business name, a person’s name, an email address, a phone number, a street address, opening hours, prices, reviews or testimonials. Use only the facts above; where a page needs one that is missing, write around it or leave a bracketed placeholder such as [phone number] for the owner to fill.';
49
+ const clip = (value, max)=>value.length > max ? `${value.slice(0, max - 1)}…` : value;
50
+ /** One labelled line, or nothing for an empty value. */ const line = (label, value, max = 240)=>value && value.trim() ? `${label}: ${clip(value.trim(), max)}` : null;
51
+ const pathOf = (slug)=>`/${slug.trim().replace(/^\/+|\/+$/g, '')}`;
52
+ /**
53
+ * The site's index (AGL-3661): what it already has, by name and id, so a job
54
+ * reuses before it builds. Pages say whether they are live; a form says how
55
+ * many fields it asks for. Pure, so a caller holding an inventory pays one
56
+ * render and no read.
57
+ */ export function aiSiteInventoryIndex(inventory, publish) {
58
+ var _ref;
59
+ if (!inventory) return [];
60
+ const live = new Set(((_ref = publish == null ? void 0 : publish.publishedPages) != null ? _ref : []).map((path)=>pathOf(path)));
61
+ const cap = AI_SITE_CONTEXT_INDEX_PER_KIND;
62
+ const more = (total)=>total > cap ? `, and ${total - cap} more` : '';
63
+ const lines = [];
64
+ const pages = inventory.screens.filter((screen)=>!screen.template);
65
+ if (pages.length) {
66
+ const rows = pages.slice(0, cap).map((page)=>{
67
+ const path = pathOf(page.slug);
68
+ return `${page.name} ${path} (${live.has(path) ? 'live' : 'draft'})`;
69
+ });
70
+ lines.push(`Pages: ${rows.join('; ')}${more(pages.length)}`);
71
+ }
72
+ if (inventory.forms.length) {
73
+ const rows = inventory.forms.slice(0, cap).map((form)=>`${form.name} [${form.id}, ${form.fields.length} fields]`);
74
+ lines.push(`Forms: ${rows.join('; ')}${more(inventory.forms.length)}`);
75
+ }
76
+ if (inventory.components.length) {
77
+ const rows = inventory.components.slice(0, cap).map((component)=>`${component.name} [${component.id}]`);
78
+ lines.push(`Components: ${rows.join('; ')}${more(inventory.components.length)}`);
79
+ }
80
+ if (inventory.layouts.length) {
81
+ const rows = inventory.layouts.slice(0, cap).map((layout)=>`${layout.name} [${layout.id}]`);
82
+ lines.push(`Layouts: ${rows.join('; ')}${more(inventory.layouts.length)}`);
83
+ }
84
+ return lines;
85
+ }
86
+ /** The block's lines in priority order; each group is dropped whole when it does not fit. */ function contextGroups(input) {
87
+ const { profile, publish, preferences } = input;
88
+ const business = [];
89
+ if (profile) {
90
+ var _ref;
91
+ var _profile_toneNotes, _profile_name, _profile_whatYouDo, _profile_services, _profile_serviceArea, _profile_audience;
92
+ const toneLabel = profile.tone ? BUSINESS_TONE_LABELS[profile.tone.value] : null;
93
+ const toneNotes = (_ref = (_profile_toneNotes = profile.toneNotes) == null ? void 0 : _profile_toneNotes.value) != null ? _ref : '';
94
+ business.push(...[
95
+ line('Business name', (_profile_name = profile.name) == null ? void 0 : _profile_name.value, 120),
96
+ line('What it does', (_profile_whatYouDo = profile.whatYouDo) == null ? void 0 : _profile_whatYouDo.value),
97
+ line('Business type', profile.businessType, 80),
98
+ line('Services', (_profile_services = profile.services) == null ? void 0 : _profile_services.value.join('; ')),
99
+ line('Area served', (_profile_serviceArea = profile.serviceArea) == null ? void 0 : _profile_serviceArea.value, 160),
100
+ line('Audience', (_profile_audience = profile.audience) == null ? void 0 : _profile_audience.value, 160),
101
+ line('Tone of voice', [
102
+ toneLabel,
103
+ toneNotes
104
+ ].filter(Boolean).join(' — ') || null)
105
+ ].filter((entry)=>Boolean(entry)));
106
+ }
107
+ const contact = [];
108
+ if (profile && input.contact !== false) {
109
+ const parts = [
110
+ profile.contact.email ? `email ${profile.contact.email}` : null,
111
+ profile.contact.phone ? `phone ${profile.contact.phone}` : null,
112
+ profile.contact.address ? `address ${profile.contact.address}` : null,
113
+ profile.contact.hours ? `hours ${profile.contact.hours}` : null
114
+ ].filter(Boolean);
115
+ contact.push(parts.length ? `Contact details the owner entered (use exactly as written): ${clip(parts.join('; '), 400)}` : 'Contact details: none entered yet.');
116
+ }
117
+ const status = [];
118
+ if (publish) {
119
+ var _publish_liveUrl;
120
+ status.push(publish.published ? `Site: live at ${(_publish_liveUrl = publish.liveUrl) != null ? _publish_liveUrl : 'its address'}; live pages ${clip(publish.publishedPages.join(', '), 200)}` : `Site: not published yet${publish.liveUrl ? `; it will be at ${publish.liveUrl}` : ''}`);
121
+ }
122
+ const remembered = (preferences == null ? void 0 : preferences.length) ? [
123
+ `What the owner's past edits showed: ${clip(preferences.join('; '), 360)}.`
124
+ ] : [];
125
+ const profiles = profile && input.contact !== false && profile.profiles.length ? [
126
+ `Social profiles: ${clip(profile.profiles.join(', '), 300)}`
127
+ ] : [];
128
+ const index = aiSiteInventoryIndex(input.inventory, publish);
129
+ // Priority order: what the business is, how to reach it, whether it is
130
+ // live, what the owner taught, its profiles, then what the site holds.
131
+ return [
132
+ business,
133
+ contact,
134
+ status,
135
+ remembered,
136
+ profiles,
137
+ index
138
+ ];
139
+ }
140
+ /**
141
+ * The block as text (AGL-3661), never over {@link AI_SITE_CONTEXT_MAX_CHARS}.
142
+ * `''` when there is nothing to say, so a caller sends no block at all.
143
+ */ export function aiSiteContextBlock(input) {
144
+ const groups = contextGroups(input).filter((group)=>group.length);
145
+ if (!groups.length) return '';
146
+ const head = 'About this site, from its owner’s settings. Write for this business and do not contradict these facts:';
147
+ const render = (kept)=>[
148
+ head,
149
+ ...kept.flat(),
150
+ AI_SITE_CONTEXT_RULE
151
+ ].join('\n');
152
+ const kept = [
153
+ ...groups
154
+ ];
155
+ let text = render(kept);
156
+ while(text.length > AI_SITE_CONTEXT_MAX_CHARS && kept.length > 1){
157
+ kept.pop();
158
+ text = render(kept);
159
+ }
160
+ if (text.length <= AI_SITE_CONTEXT_MAX_CHARS) return text;
161
+ // One group still too long (a very long profile): its lines are cut, the
162
+ // closing rule is kept whole.
163
+ const room = AI_SITE_CONTEXT_MAX_CHARS - head.length - AI_SITE_CONTEXT_RULE.length - 2;
164
+ return [
165
+ head,
166
+ clip(kept.flat().join('\n'), Math.max(0, room)),
167
+ AI_SITE_CONTEXT_RULE
168
+ ].join('\n');
169
+ }
170
+ /**
171
+ * The block as the system block a door appends after its own cached blocks
172
+ * (AGL-3661): a `cacheBreakpoint` of its own, so the platform's prefix ahead
173
+ * of it is still shared and this one is cached per site. `[]` when there is
174
+ * nothing to say.
175
+ *
176
+ * A door already carrying the runtime's limit of breakpoints passes
177
+ * `cache: false`, and the block rides as a volatile block after them instead.
178
+ */ export function aiSiteContextSystemBlock(input, options = {}) {
179
+ const text = input ? aiSiteContextBlock(input) : '';
180
+ if (!text) return [];
181
+ return [
182
+ options.cache === false ? {
183
+ text,
184
+ volatile: true,
185
+ site: true
186
+ } : {
187
+ text,
188
+ cacheBreakpoint: true,
189
+ site: true
190
+ }
191
+ ];
192
+ }
193
+ /**
194
+ * A door's own instructions with the site context after them (AGL-3661) —
195
+ * THE call a generator makes to give its model the site:
196
+ *
197
+ * instructions: aiInstructionsWithSiteContext(MY_INSTRUCTIONS, context)
198
+ *
199
+ * The door's last instruction block is marked a breakpoint, so the
200
+ * platform-wide prefix (the doctrine and the door's rules) stays one cache
201
+ * entry for every workspace, and the context block after it is the per-site
202
+ * entry. Without a context it hands the instructions back unchanged, so a
203
+ * door's prompt is byte-for-byte what it was for a job that has no site.
204
+ */ export function aiInstructionsWithSiteContext(instructions, context) {
205
+ const site = aiSiteContextSystemBlock(context);
206
+ if (!site.length) return instructions;
207
+ const blocks = [
208
+ ...instructions
209
+ ];
210
+ const last = blocks.length - 1;
211
+ if (last >= 0 && !blocks[last].volatile && !blocks.some((block)=>block.cacheBreakpoint)) {
212
+ blocks[last] = _extends({}, blocks[last], {
213
+ cacheBreakpoint: true
214
+ });
215
+ }
216
+ return [
217
+ ...blocks,
218
+ ...site
219
+ ];
220
+ }
221
+
222
+ //# sourceMappingURL=ai-site-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-context.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n BUSINESS_TONE_LABELS,\n type ResolvedBusinessProfile,\n} from '@aglyn/aglyn/app-utils/business-profile'\nimport type { AiSystemBlock } from '../providers/contract'\nimport type { AiHostPublishContext } from './ai-host-publish-context'\nimport type { AiSiteInventory } from './ai-site-inventory'\n\n/**\n * What every AI job knows about the site it works on (AGL-3661), as ONE\n * compact block: the business profile, what the site already has and whether\n * it is live, and what the owner's edits taught it.\n *\n * ── Why it exists ─────────────────────────────────────────────────────────\n *\n * Before it, a job saw its brief, the guided start's answers and the site's\n * name. It named a Free site \"Austin Paws Grooming\" that was called nothing\n * of the kind, and nothing stopped it inventing a phone number. The block\n * gives it the facts the owner entered and a standing rule for the ones they\n * did not: never invent a name, a contact detail, a price or a review.\n *\n * ── Where it sits, and what that costs ────────────────────────────────────\n *\n * {@link aiSiteContextSystemBlock} marks it a `cacheBreakpoint`, placed AFTER\n * every platform-wide cached block and before the volatile ones. The\n * platform's prefix is still shared by every workspace; this block extends it\n * with one cache entry per site, which every request of a job — each re-ask,\n * each section of a page — reads at the cache rate. It changes only when the\n * owner edits the profile, a page is published, or an edit is remembered.\n *\n * ── Held to a ceiling ─────────────────────────────────────────────────────\n *\n * {@link AI_SITE_CONTEXT_MAX_CHARS}: 400 tokens at the runtime's four\n * characters a token. Lines are dropped from the least useful up — the site\n * index, the profiles, then the preferences — and the closing rule is never\n * dropped, because it is the line that stops an invented phone number.\n */\n\n/** The block's ceiling: 400 tokens at four characters a token. */\nexport const AI_SITE_CONTEXT_MAX_CHARS = 1_600\n\n/** Pages, forms, components and layouts listed by name in the site index, each. */\nexport const AI_SITE_CONTEXT_INDEX_PER_KIND = 8\n\n/** The rule every block closes with, whatever else it says. */\nexport const AI_SITE_CONTEXT_RULE =\n 'Never invent a business name, a person’s name, an email address, a phone number, a street address, opening hours, prices, reviews or testimonials. Use only the facts above; where a page needs one that is missing, write around it or leave a bracketed placeholder such as [phone number] for the owner to fill.'\n\nexport interface AiSiteContextInput {\n /** The resolved business profile; `null` when the site has none to read. */\n profile: ResolvedBusinessProfile | null\n /**\n * The site's inventory, for a caller that does NOT already send it. A job\n * whose prompt carries the inventory block (the plan step and the writers\n * built on the doctrine) leaves this out: the block would only repeat it.\n */\n inventory?: AiSiteInventory | null\n /** Whether the site is live and where. */\n publish?: AiHostPublishContext | null\n /** What the owner's edits taught, strongest first. */\n preferences?: readonly string[]\n /**\n * Whether the block may carry contact details. An answer that must name\n * nobody and print no address (the insight rules) leaves them out.\n */\n contact?: boolean\n}\n\nconst clip = (value: string, max: number) => (value.length > max ? `${value.slice(0, max - 1)}…` : value)\n\n/** One labelled line, or nothing for an empty value. */\nconst line = (label: string, value: string | null | undefined, max = 240): string | null =>\n value && value.trim() ? `${label}: ${clip(value.trim(), max)}` : null\n\nconst pathOf = (slug: string) => `/${slug.trim().replace(/^\\/+|\\/+$/g, '')}`\n\n/**\n * The site's index (AGL-3661): what it already has, by name and id, so a job\n * reuses before it builds. Pages say whether they are live; a form says how\n * many fields it asks for. Pure, so a caller holding an inventory pays one\n * render and no read.\n */\nexport function aiSiteInventoryIndex(\n inventory: AiSiteInventory | null | undefined,\n publish?: AiHostPublishContext | null,\n): string[] {\n if (!inventory) return []\n const live = new Set((publish?.publishedPages ?? []).map((path) => pathOf(path)))\n const cap = AI_SITE_CONTEXT_INDEX_PER_KIND\n const more = (total: number) => (total > cap ? `, and ${total - cap} more` : '')\n const lines: string[] = []\n const pages = inventory.screens.filter((screen) => !screen.template)\n if (pages.length) {\n const rows = pages.slice(0, cap).map((page) => {\n const path = pathOf(page.slug)\n return `${page.name} ${path} (${live.has(path) ? 'live' : 'draft'})`\n })\n lines.push(`Pages: ${rows.join('; ')}${more(pages.length)}`)\n }\n if (inventory.forms.length) {\n const rows = inventory.forms.slice(0, cap).map((form) => `${form.name} [${form.id}, ${form.fields.length} fields]`)\n lines.push(`Forms: ${rows.join('; ')}${more(inventory.forms.length)}`)\n }\n if (inventory.components.length) {\n const rows = inventory.components.slice(0, cap).map((component) => `${component.name} [${component.id}]`)\n lines.push(`Components: ${rows.join('; ')}${more(inventory.components.length)}`)\n }\n if (inventory.layouts.length) {\n const rows = inventory.layouts.slice(0, cap).map((layout) => `${layout.name} [${layout.id}]`)\n lines.push(`Layouts: ${rows.join('; ')}${more(inventory.layouts.length)}`)\n }\n return lines\n}\n\n/** The block's lines in priority order; each group is dropped whole when it does not fit. */\nfunction contextGroups(input: AiSiteContextInput): string[][] {\n const { profile, publish, preferences } = input\n const business: string[] = []\n if (profile) {\n const toneLabel = profile.tone ? BUSINESS_TONE_LABELS[profile.tone.value] : null\n const toneNotes = profile.toneNotes?.value ?? ''\n business.push(\n ...[\n line('Business name', profile.name?.value, 120),\n line('What it does', profile.whatYouDo?.value),\n line('Business type', profile.businessType, 80),\n line('Services', profile.services?.value.join('; ')),\n line('Area served', profile.serviceArea?.value, 160),\n line('Audience', profile.audience?.value, 160),\n line('Tone of voice', [toneLabel, toneNotes].filter(Boolean).join(' — ') || null),\n ].filter((entry): entry is string => Boolean(entry)),\n )\n }\n const contact: string[] = []\n if (profile && input.contact !== false) {\n const parts = [\n profile.contact.email ? `email ${profile.contact.email}` : null,\n profile.contact.phone ? `phone ${profile.contact.phone}` : null,\n profile.contact.address ? `address ${profile.contact.address}` : null,\n profile.contact.hours ? `hours ${profile.contact.hours}` : null,\n ].filter(Boolean)\n contact.push(\n parts.length\n ? `Contact details the owner entered (use exactly as written): ${clip(parts.join('; '), 400)}`\n : 'Contact details: none entered yet.',\n )\n }\n const status: string[] = []\n if (publish) {\n status.push(\n publish.published\n ? `Site: live at ${publish.liveUrl ?? 'its address'}; live pages ${clip(publish.publishedPages.join(', '), 200)}`\n : `Site: not published yet${publish.liveUrl ? `; it will be at ${publish.liveUrl}` : ''}`,\n )\n }\n const remembered = preferences?.length\n ? [`What the owner's past edits showed: ${clip(preferences.join('; '), 360)}.`]\n : []\n const profiles =\n profile && input.contact !== false && profile.profiles.length\n ? [`Social profiles: ${clip(profile.profiles.join(', '), 300)}`]\n : []\n const index = aiSiteInventoryIndex(input.inventory, publish)\n // Priority order: what the business is, how to reach it, whether it is\n // live, what the owner taught, its profiles, then what the site holds.\n return [business, contact, status, remembered, profiles, index]\n}\n\n/**\n * The block as text (AGL-3661), never over {@link AI_SITE_CONTEXT_MAX_CHARS}.\n * `''` when there is nothing to say, so a caller sends no block at all.\n */\nexport function aiSiteContextBlock(input: AiSiteContextInput): string {\n const groups = contextGroups(input).filter((group) => group.length)\n if (!groups.length) return ''\n const head = 'About this site, from its owner’s settings. Write for this business and do not contradict these facts:'\n const render = (kept: string[][]) => [head, ...kept.flat(), AI_SITE_CONTEXT_RULE].join('\\n')\n const kept = [...groups]\n let text = render(kept)\n while (text.length > AI_SITE_CONTEXT_MAX_CHARS && kept.length > 1) {\n kept.pop()\n text = render(kept)\n }\n if (text.length <= AI_SITE_CONTEXT_MAX_CHARS) return text\n // One group still too long (a very long profile): its lines are cut, the\n // closing rule is kept whole.\n const room = AI_SITE_CONTEXT_MAX_CHARS - head.length - AI_SITE_CONTEXT_RULE.length - 2\n return [head, clip(kept.flat().join('\\n'), Math.max(0, room)), AI_SITE_CONTEXT_RULE].join('\\n')\n}\n\n/**\n * The block as the system block a door appends after its own cached blocks\n * (AGL-3661): a `cacheBreakpoint` of its own, so the platform's prefix ahead\n * of it is still shared and this one is cached per site. `[]` when there is\n * nothing to say.\n *\n * A door already carrying the runtime's limit of breakpoints passes\n * `cache: false`, and the block rides as a volatile block after them instead.\n */\nexport function aiSiteContextSystemBlock(\n input: AiSiteContextInput | null | undefined,\n options: { cache?: boolean } = {},\n): AiSystemBlock[] {\n const text = input ? aiSiteContextBlock(input) : ''\n if (!text) return []\n return [options.cache === false ? { text, volatile: true, site: true } : { text, cacheBreakpoint: true, site: true }]\n}\n\n/**\n * A door's own instructions with the site context after them (AGL-3661) —\n * THE call a generator makes to give its model the site:\n *\n * instructions: aiInstructionsWithSiteContext(MY_INSTRUCTIONS, context)\n *\n * The door's last instruction block is marked a breakpoint, so the\n * platform-wide prefix (the doctrine and the door's rules) stays one cache\n * entry for every workspace, and the context block after it is the per-site\n * entry. Without a context it hands the instructions back unchanged, so a\n * door's prompt is byte-for-byte what it was for a job that has no site.\n */\nexport function aiInstructionsWithSiteContext(\n instructions: readonly AiSystemBlock[],\n context: AiSiteContextInput | null | undefined,\n): readonly AiSystemBlock[] {\n const site = aiSiteContextSystemBlock(context)\n if (!site.length) return instructions\n const blocks = [...instructions]\n const last = blocks.length - 1\n if (last >= 0 && !blocks[last].volatile && !blocks.some((block) => block.cacheBreakpoint)) {\n blocks[last] = { ...blocks[last], cacheBreakpoint: true }\n }\n return [...blocks, ...site]\n}\n"],"names":["BUSINESS_TONE_LABELS","AI_SITE_CONTEXT_MAX_CHARS","AI_SITE_CONTEXT_INDEX_PER_KIND","AI_SITE_CONTEXT_RULE","clip","value","max","length","slice","line","label","trim","pathOf","slug","replace","aiSiteInventoryIndex","inventory","publish","live","Set","publishedPages","map","path","cap","more","total","lines","pages","screens","filter","screen","template","rows","page","name","has","push","join","forms","form","id","fields","components","component","layouts","layout","contextGroups","input","profile","preferences","business","toneLabel","tone","toneNotes","whatYouDo","businessType","services","serviceArea","audience","Boolean","entry","contact","parts","email","phone","address","hours","status","published","liveUrl","remembered","profiles","index","aiSiteContextBlock","groups","group","head","render","kept","flat","text","pop","room","Math","aiSiteContextSystemBlock","options","cache","volatile","site","cacheBreakpoint","aiInstructionsWithSiteContext","instructions","context","blocks","last","some","block"],"mappings":"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"}
@@ -84,6 +84,8 @@ export declare const AI_SITE_PLAN_MAX_TOKENS: {
84
84
  export interface AiFreeSiteWorstCase {
85
85
  /** The site plan: its answer and its one re-ask, each at its ceiling. */
86
86
  plan: number;
87
+ /** The site's look (AGL-3660): its one answer at its ceiling, on the fast tier. */
88
+ look: number;
87
89
  /** The one layout a Free plan includes, built first on a site with none. */
88
90
  layout: number;
89
91
  /** A page's first section pass, which writes the cached prefix. */
@@ -104,7 +106,7 @@ export interface AiFreeSiteWorstCase {
104
106
  export declare const AI_FREE_SITE_WORST_CASE_CREDITS: Readonly<AiFreeSiteWorstCase>;
105
107
  /**
106
108
  * The most sections a Free site's plan fits in the Free taste at its worst,
107
- * across all its pages: the plan, the layouts it builds first, each page's
109
+ * across all its pages: the plan, the look, the layouts it builds first, each page's
108
110
  * listing and first pass, and ROOM FOR ONE RETRIED SECTION, then as many later
109
111
  * passes as the rest pays for; none when that is already past the wall. Every
110
112
  * page's first pass is counted as a cache write, which errs dear.
@@ -117,7 +119,7 @@ export declare function aiFreeSiteSectionsWithin(creations: {
117
119
  export declare const AI_FREE_SITE_NOMINAL_SECTIONS = 3;
118
120
  /**
119
121
  * About what a Free site start of this many pages costs at its worst, in
120
- * credits, for the dialog that asks for one: the site plan, each page's
122
+ * credits, for the dialog that asks for one: the site plan, its look, each page's
121
123
  * listing and its sections at the nominal count, from the figures the wall is
122
124
  * proven with rather than the nominal credits a paid estimate counts.
123
125
  */
@@ -173,6 +175,12 @@ export interface AiSiteJobInputs {
173
175
  * few words in a brief, not a lookup.
174
176
  */
175
177
  starter: string;
178
+ /**
179
+ * The kind of site the person picked (AGL-3660), by its `AI_SITE_KINDS`
180
+ * id; empty when nobody picked one, and the business type suggests it.
181
+ * It sets the look the site is designed in and how its pages are arranged.
182
+ */
183
+ siteKind: string;
176
184
  /** How many pages the member asked for. */
177
185
  pages: number;
178
186
  /** The per-site variables an agency batch varies; empty when none was given. */
@@ -236,6 +244,14 @@ export declare const AI_SITE_SUBMISSION_CHOICES: ReadonlyArray<{
236
244
  * because it is the first thing the model is told.
237
245
  */
238
246
  export declare const AI_SITE_EMAIL_TYPE = "reply";
247
+ /**
248
+ * What a site job's plan and every unit it builds are told about the
249
+ * business's name (AGL-3596): that it is the name, used as written. Said on
250
+ * the job's own turn, never in a cached system block, because it is one
251
+ * site's. A model handed only what the business does named it itself —
252
+ * "Hillside Dog Grooming" was built as "Austin Paws Grooming".
253
+ */
254
+ export declare function aiSiteNameSentence(name: string): string;
239
255
  /** Where a job's inputs say submissions go, or `null` where they do not say. */
240
256
  export declare function aiSiteSubmissions(inputs: Readonly<Record<string, unknown>> | null | undefined): AiSiteSubmissions | null;
241
257
  /** A scaffold's inputs, or the sentence the door refuses them with. */
@@ -13,7 +13,8 @@
13
13
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
- */ import { AI_BUILD_PLAN_CREATION_NOUNS, aiPlanUndeclaredRefs } from "./ai-build-plan.js";
16
+ */ import { aiSiteKind } from "./ai-site-kinds.js";
17
+ import { AI_BUILD_PLAN_CREATION_NOUNS, aiPlanUndeclaredRefs } from "./ai-build-plan.js";
17
18
  import { AI_JOB_CREATE_KINDS } from "./ai-job-creations.js";
18
19
  import { AI_PAGE_CREATE_KINDS } from "./ai-page-job.js";
19
20
  /**
@@ -82,6 +83,7 @@ import { AI_PAGE_CREATE_KINDS } from "./ai-page-job.js";
82
83
  * moves and this does not.
83
84
  */ export const AI_FREE_SITE_WORST_CASE_CREDITS = {
84
85
  plan: 35,
86
+ look: 7,
85
87
  layout: 64,
86
88
  firstSection: 44,
87
89
  laterSection: 20,
@@ -89,25 +91,25 @@ import { AI_PAGE_CREATE_KINDS } from "./ai-page-job.js";
89
91
  };
90
92
  /**
91
93
  * The most sections a Free site's plan fits in the Free taste at its worst,
92
- * across all its pages: the plan, the layouts it builds first, each page's
94
+ * across all its pages: the plan, the look, the layouts it builds first, each page's
93
95
  * listing and first pass, and ROOM FOR ONE RETRIED SECTION, then as many later
94
96
  * passes as the rest pays for; none when that is already past the wall. Every
95
97
  * page's first pass is counted as a cache write, which errs dear.
96
98
  */ export function aiFreeSiteSectionsWithin(creations, taste, credits = AI_FREE_SITE_WORST_CASE_CREDITS) {
97
99
  const pages = Math.max(1, Math.floor(creations.pages));
98
- const before = credits.plan + creations.layouts * credits.layout + pages * (credits.listing + credits.firstSection) + credits.firstSection;
100
+ const before = credits.plan + credits.look + creations.layouts * credits.layout + pages * (credits.listing + credits.firstSection) + credits.firstSection;
99
101
  if (before > taste) return 0;
100
102
  return pages + Math.floor((taste - before) / credits.laterSection);
101
103
  }
102
104
  /** Sections a Free site's page is assumed to hold before its plan names them. */ export const AI_FREE_SITE_NOMINAL_SECTIONS = 3;
103
105
  /**
104
106
  * About what a Free site start of this many pages costs at its worst, in
105
- * credits, for the dialog that asks for one: the site plan, each page's
107
+ * credits, for the dialog that asks for one: the site plan, its look, each page's
106
108
  * listing and its sections at the nominal count, from the figures the wall is
107
109
  * proven with rather than the nominal credits a paid estimate counts.
108
110
  */ export function aiFreeSiteCreditEstimate(pages, credits = AI_FREE_SITE_WORST_CASE_CREDITS) {
109
111
  const count = Math.max(1, Math.floor(pages));
110
- return credits.plan + count * (credits.listing + credits.firstSection) + count * (AI_FREE_SITE_NOMINAL_SECTIONS - 1) * credits.laterSection;
112
+ return credits.plan + credits.look + count * (credits.listing + credits.firstSection) + count * (AI_FREE_SITE_NOMINAL_SECTIONS - 1) * credits.laterSection;
111
113
  }
112
114
  /**
113
115
  * The most sections one scaffolded page may hold. The plan model allows more
@@ -179,12 +181,23 @@ import { AI_PAGE_CREATE_KINDS } from "./ai-page-job.js";
179
181
  * up, and a kind that misdescribes the email is worse than an absent one
180
182
  * because it is the first thing the model is told.
181
183
  */ export const AI_SITE_EMAIL_TYPE = 'reply';
184
+ /**
185
+ * What a site job's plan and every unit it builds are told about the
186
+ * business's name (AGL-3596): that it is the name, used as written. Said on
187
+ * the job's own turn, never in a cached system block, because it is one
188
+ * site's. A model handed only what the business does named it itself —
189
+ * "Hillside Dog Grooming" was built as "Austin Paws Grooming".
190
+ */ export function aiSiteNameSentence(name) {
191
+ return `The business is named “${name}”: the site names it exactly that way, in the header, the footer, the copy and the search listing, and never by any other name.`;
192
+ }
182
193
  /** Where a job's inputs say submissions go, or `null` where they do not say. */ export function aiSiteSubmissions(inputs) {
183
194
  const value = inputs == null ? void 0 : inputs['submissions'];
184
195
  return AI_SITE_SUBMISSIONS.includes(value) ? value : null;
185
196
  }
186
197
  const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
187
198
  /** A scaffold's inputs, or the sentence the door refuses them with. */ export function parseAiSiteJobInputs(inputs) {
199
+ var _ref;
200
+ var _aiSiteKind;
188
201
  const text = (key)=>{
189
202
  const value = inputs == null ? void 0 : inputs[key];
190
203
  if (value === undefined || value === null || value === '') return '';
@@ -202,6 +215,8 @@ const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
202
215
  const brand = text('brand');
203
216
  const audience = text('audience');
204
217
  const starter = text('starter');
218
+ // A kind this deployment does not know is nobody having picked one.
219
+ const siteKind = (_ref = (_aiSiteKind = aiSiteKind(inputs == null ? void 0 : inputs['siteKind'])) == null ? void 0 : _aiSiteKind.id) != null ? _ref : '';
205
220
  for (const [key, value] of [
206
221
  [
207
222
  'businessName',
@@ -244,6 +259,7 @@ const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
244
259
  businessType,
245
260
  audience: audience,
246
261
  starter: starter,
262
+ siteKind,
247
263
  pages,
248
264
  businessName: businessName,
249
265
  city: city,