@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
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-follow.component.tsx"],"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'use client'\n\nimport { AppLink } from '@aglyn/shared-ui-jsx'\nimport type { MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token'\nimport { Alert, Box, Button, Chip, CircularProgress, Stack, Typography } from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n AI_JOB_PHASE_LABELS,\n aiJobPhase,\n type AiJobPhase,\n} from '../model/ai-job-activity'\nimport { AI_JOB_TERMINAL_STATUSES, type AiJobSummary } from '../model/ai-jobs.types'\nimport { AiJobPlan } from './ai-job-plan.component'\nimport { cancelAiJobRequest, resumeAiJobRequest } from './ai-job-requests'\nimport { openAiJobs, publishAiJob } from './ai-jobs-store'\nimport { followAiJobEvents } from './ai-job-events'\nimport { aiJobPrimaryLink } from './ai-job-links'\nimport { aiJobProblem } from './use-ai-job-run'\n\n/**\n * A JOB, WATCHED AND DECIDED WHERE IT WAS STARTED (AGL-3593).\n *\n * The dialog that starts a planned job — \"Plan my site\", \"Plan the page\" —\n * used to end on a sentence pointing at a drawer a person had to go and find,\n * and a person who could not find it never got their site built. This is\n * what such a dialog shows instead, once its job exists:\n *\n * - where the job stands, live — queued, planning, plan ready, building,\n * then done or failed — read from the same events route the AI jobs\n * drawer follows;\n * - the plan, once it is ready, with the drawer's own plan component and its\n * Confirm, through the same request the drawer sends\n * ({@link resumeAiJobRequest}), and Cancel beside it;\n * - once it is done, the one action that opens what it built;\n * - and always \"Open AI jobs\", which follows the job anywhere else in the\n * console.\n *\n * Closing the dialog stops following; the job carries on, and the top-bar\n * indicator, the Assist launcher and the notification the machine raises say\n * when it needs the person again.\n */\n\nexport interface AiJobFollowProps {\n /** The job as the create door answered with it. */\n job: AiJobSummary\n orgId: string\n /**\n * Path slug for the link to what the job built. A dialog mounted on a\n * console page that names its org can leave it out: the slug is the first\n * segment of every org-scoped console address.\n */\n orgSlug?: string | null\n user: MaybeTokenSource\n /** What the dialog says about the job it started, above its live status. */\n intro: string\n /**\n * Closes the dialog. Called beside opening AI jobs, because a dialog\n * covers the panel AI jobs opens in.\n */\n onOpenJobs?: () => void\n /** The viewer is staff (AGL-3078): a refused answer's details are theirs to open. */\n staff?: boolean\n}\n\n/** What the status line says in each phase, and how loudly. */\nconst PHASE_LINE: Readonly<\n Record<AiJobPhase, { severity: 'info' | 'warning' | 'success' | 'error'; text: string }>\n> = {\n queued: { severity: 'info', text: 'Queued. It starts in a moment.' },\n planning: { severity: 'info', text: 'Planning. The plan appears here when it is ready.' },\n 'plan-ready': {\n severity: 'warning',\n text: 'Your plan is ready. Read it below and confirm it to build — nothing is built until you do.',\n },\n building: { severity: 'info', text: 'Building. Everything it builds is an unpublished draft.' },\n attention: { severity: 'warning', text: 'The job stopped and needs you.' },\n done: { severity: 'success', text: 'Done. Your drafts are ready to open.' },\n failed: { severity: 'error', text: 'The job stopped before it finished.' },\n canceled: { severity: 'info', text: 'The job was canceled.' },\n}\n\n/** The org slug a console address names: its first segment. */\nfunction orgSlugFromLocation(): string {\n const path = typeof globalThis.location?.pathname === 'string' ? globalThis.location.pathname : ''\n return path.split('/')[1] ?? ''\n}\n\n/** Follows `initial` through the events route while mounted, and lets a decision replace it. */\nexport function useFollowedAiJob(\n user: MaybeTokenSource,\n orgId: string,\n initial: AiJobSummary,\n): [AiJobSummary, (job: AiJobSummary) => void] {\n const userRef = useRef(user)\n userRef.current = user\n const [job, setJob] = useState(initial)\n useEffect(() => setJob(initial), [initial])\n\n // One stream while the job is moving. A job parked for the person settles\n // the stream; confirming moves it again, and the status change re-opens it.\n const moving = job.status === 'queued' || job.status === 'running'\n useEffect(() => {\n if (!moving) return undefined\n const controller = new AbortController()\n void followAiJobEvents(() => userRef.current, orgId, job.id, controller.signal, setJob)\n return () => controller.abort()\n }, [moving, orgId, job.id])\n\n const replace = useCallback((next: AiJobSummary) => {\n publishAiJob(next)\n setJob(next)\n }, [])\n return [job, replace]\n}\n\nexport function AiJobFollow({\n job: initial,\n orgId,\n orgSlug,\n user,\n intro,\n onOpenJobs,\n staff = false,\n}: AiJobFollowProps) {\n const [job, replace] = useFollowedAiJob(user, orgId, initial)\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n const phase = aiJobPhase(job)\n const line = PHASE_LINE[phase]\n const moving = phase === 'queued' || phase === 'planning' || phase === 'building'\n const settled = AI_JOB_TERMINAL_STATUSES.includes(job.status)\n const primary = aiJobPrimaryLink(job, orgSlug || orgSlugFromLocation())\n const problem = phase === 'attention' || phase === 'failed' ? aiJobProblem(job, line.text) : null\n const stepsDone = job.steps.filter((step) => step.status === 'done').length\n\n const decide = useCallback(\n async (request: () => ReturnType<typeof resumeAiJobRequest>) => {\n setBusy(true)\n setNotice(null)\n const { job: next, error } = await request()\n if (next) replace(next)\n if (error) setNotice(error)\n setBusy(false)\n },\n [replace],\n )\n\n const openJobs = () => {\n openAiJobs({ jobId: job.id })\n onOpenJobs?.()\n }\n\n return (\n <Stack spacing={2} aria-live=\"polite\">\n <Typography variant=\"body2\" color=\"text.secondary\">\n {intro}\n </Typography>\n <Alert\n severity={line.severity}\n icon={moving ? <CircularProgress size={18} aria-label={AI_JOB_PHASE_LABELS[phase]} /> : undefined}\n action={\n <Button color=\"inherit\" size=\"small\" variant=\"outlined\" onClick={openJobs}>\n {'Open AI jobs'}\n </Button>\n }\n >\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 0.5 }}>\n <Chip size=\"small\" label={AI_JOB_PHASE_LABELS[phase]} />\n {job.steps.length > 1 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${stepsDone} of ${job.steps.length} steps`}\n </Typography>\n ) : null}\n </Stack>\n <Typography variant=\"body2\" sx={{ mt: 0.5 }}>\n {problem ?? line.text}\n </Typography>\n </Alert>\n {primary ? (\n <Box>\n <Button variant=\"contained\" component={AppLink} href={primary.href}>\n {primary.label}\n </Button>\n </Box>\n ) : null}\n {/* The drawer's own plan and its Confirm, on the drawer's request. */}\n <AiJobPlan\n job={job}\n onResume={(target) => void decide(() => resumeAiJobRequest(user, orgId, target))}\n busy={busy}\n staff={staff}\n // \"Use the starter site instead\" on a guided start that did not work out (AGL-3594).\n user={user ?? undefined}\n />\n {!settled ? (\n <Box>\n <Button\n size=\"small\"\n disabled={busy}\n onClick={() => void decide(() => cancelAiJobRequest(user, orgId, job.id))}\n >\n {'Cancel job'}\n </Button>\n </Box>\n ) : null}\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n </Stack>\n )\n}\n\nexport default AiJobFollow\n"],"names":["AppLink","Alert","Box","Button","Chip","CircularProgress","Stack","Typography","useCallback","useEffect","useRef","useState","AI_JOB_PHASE_LABELS","aiJobPhase","AI_JOB_TERMINAL_STATUSES","AiJobPlan","cancelAiJobRequest","resumeAiJobRequest","openAiJobs","publishAiJob","followAiJobEvents","aiJobPrimaryLink","aiJobProblem","PHASE_LINE","queued","severity","text","planning","building","attention","done","failed","canceled","orgSlugFromLocation","path","globalThis","location","pathname","split","useFollowedAiJob","user","orgId","initial","userRef","current","job","setJob","moving","status","undefined","controller","AbortController","id","signal","abort","replace","next","AiJobFollow","orgSlug","intro","onOpenJobs","staff","busy","setBusy","notice","setNotice","phase","line","settled","includes","primary","problem","stepsDone","steps","filter","step","length","decide","request","error","openJobs","jobId","spacing","aria-live","variant","color","icon","size","aria-label","action","onClick","direction","sx","alignItems","flexWrap","rowGap","label","mt","component","href","onResume","target","disabled"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-follow.component.tsx"],"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'use client'\n\nimport { AppLink } from '@aglyn/shared-ui-jsx'\nimport type { MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token'\nimport { Alert, Box, Button, Chip, CircularProgress, Stack, Typography } from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n AI_JOB_PHASE_LABELS,\n aiJobPhase,\n type AiJobPhase,\n} from '../model/ai-job-activity'\nimport { AI_JOB_TERMINAL_STATUSES, type AiJobSummary } from '../model/ai-jobs.types'\nimport { AiJobPlan } from './ai-job-plan.component'\nimport { cancelAiJobRequest, resumeAiJobRequest } from './ai-job-requests'\nimport { openAiJobs, publishAiJob } from './ai-jobs-store'\nimport { followAiJobEvents } from './ai-job-events'\nimport { aiJobPrimaryLink } from './ai-job-links'\nimport { aiJobProblem } from './use-ai-job-run'\n\n/**\n * A JOB, WATCHED AND DECIDED WHERE IT WAS STARTED (AGL-3593).\n *\n * The dialog that starts a planned job — \"Plan my site\", \"Plan the page\" —\n * used to end on a sentence pointing at a drawer a person had to go and find,\n * and a person who could not find it never got their site built. This is\n * what such a dialog shows instead, once its job exists:\n *\n * - where the job stands, live — queued, planning, plan ready, building,\n * then done or failed — read from the same events route the AI jobs\n * drawer follows;\n * - the plan, once it is ready, with the drawer's own plan component and its\n * Confirm, through the same request the drawer sends\n * ({@link resumeAiJobRequest}), and Cancel beside it;\n * - once it is done, the one action that opens what it built;\n * - and always \"Open AI jobs\", which follows the job anywhere else in the\n * console.\n *\n * Closing the dialog stops following; the job carries on, and the top-bar\n * indicator, the Assist launcher and the notification the machine raises say\n * when it needs the person again.\n */\n\nexport interface AiJobFollowProps {\n /** The job as the create door answered with it. */\n job: AiJobSummary\n orgId: string\n /**\n * Path slug for the link to what the job built. A dialog mounted on a\n * console page that names its org can leave it out: the slug is the first\n * segment of every org-scoped console address.\n */\n orgSlug?: string | null\n user: MaybeTokenSource\n /** What the dialog says about the job it started, above its live status. */\n intro: string\n /**\n * Closes the dialog. Called beside opening AI jobs, because a dialog\n * covers the panel AI jobs opens in.\n */\n onOpenJobs?: () => void\n /** The viewer is staff (AGL-3078): a refused answer's details are theirs to open. */\n staff?: boolean\n}\n\n/** What the status line says in each phase, and how loudly. */\nconst PHASE_LINE: Readonly<\n Record<AiJobPhase, { severity: 'info' | 'warning' | 'success' | 'error'; text: string }>\n> = {\n queued: { severity: 'info', text: 'Queued. It starts in a moment.' },\n planning: { severity: 'info', text: 'Planning. The plan appears here when it is ready.' },\n 'plan-ready': {\n severity: 'warning',\n text: 'Your plan is ready. Read it below and confirm it to build — nothing is built until you do.',\n },\n building: { severity: 'info', text: 'Building. Everything it builds is an unpublished draft.' },\n attention: { severity: 'warning', text: 'The job stopped and needs you.' },\n done: { severity: 'success', text: 'Done. Your drafts are ready to open.' },\n failed: { severity: 'error', text: 'The job stopped before it finished.' },\n canceled: { severity: 'info', text: 'The job was canceled.' },\n}\n\n/** The org slug a console address names: its first segment. */\nfunction orgSlugFromLocation(): string {\n const path = typeof globalThis.location?.pathname === 'string' ? globalThis.location.pathname : ''\n return path.split('/')[1] ?? ''\n}\n\n/** Follows `initial` through the events route while mounted, and lets a decision replace it. */\nexport function useFollowedAiJob(\n user: MaybeTokenSource,\n orgId: string,\n initial: AiJobSummary,\n): [AiJobSummary, (job: AiJobSummary) => void] {\n const userRef = useRef(user)\n userRef.current = user\n const [job, setJob] = useState(initial)\n useEffect(() => setJob(initial), [initial])\n\n // One stream while the job is moving. A job parked for the person settles\n // the stream; confirming moves it again, and the status change re-opens it.\n const moving = job.status === 'queued' || job.status === 'running'\n useEffect(() => {\n if (!moving) return undefined\n const controller = new AbortController()\n void followAiJobEvents(() => userRef.current, orgId, job.id, controller.signal, setJob)\n return () => controller.abort()\n }, [moving, orgId, job.id])\n\n const replace = useCallback((next: AiJobSummary) => {\n publishAiJob(next)\n setJob(next)\n }, [])\n return [job, replace]\n}\n\nexport function AiJobFollow({\n job: initial,\n orgId,\n orgSlug,\n user,\n intro,\n onOpenJobs,\n staff = false,\n}: AiJobFollowProps) {\n const [job, replace] = useFollowedAiJob(user, orgId, initial)\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n const phase = aiJobPhase(job)\n const line = PHASE_LINE[phase]\n const moving = phase === 'queued' || phase === 'planning' || phase === 'building'\n const settled = AI_JOB_TERMINAL_STATUSES.includes(job.status)\n const primary = aiJobPrimaryLink(job, orgSlug || orgSlugFromLocation())\n const problem = phase === 'attention' || phase === 'failed' ? aiJobProblem(job, line.text) : null\n const stepsDone = job.steps.filter((step) => step.status === 'done').length\n\n const decide = useCallback(\n async (request: () => ReturnType<typeof resumeAiJobRequest>) => {\n setBusy(true)\n setNotice(null)\n const { job: next, error } = await request()\n if (next) replace(next)\n if (error) setNotice(error)\n setBusy(false)\n },\n [replace],\n )\n\n const openJobs = () => {\n openAiJobs({ jobId: job.id })\n onOpenJobs?.()\n }\n\n return (\n <Stack spacing={2} aria-live=\"polite\">\n <Typography variant=\"body2\" color=\"text.secondary\">\n {intro}\n </Typography>\n <Alert\n severity={line.severity}\n icon={moving ? <CircularProgress size={18} aria-label={AI_JOB_PHASE_LABELS[phase]} /> : undefined}\n action={\n <Button color=\"inherit\" size=\"small\" variant=\"outlined\" onClick={openJobs}>\n {'Open AI jobs'}\n </Button>\n }\n >\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 0.5 }}>\n <Chip size=\"small\" label={AI_JOB_PHASE_LABELS[phase]} />\n {job.steps.length > 1 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${stepsDone} of ${job.steps.length} steps`}\n </Typography>\n ) : null}\n </Stack>\n <Typography variant=\"body2\" sx={{ mt: 0.5 }}>\n {problem ?? line.text}\n </Typography>\n </Alert>\n {primary ? (\n <Box>\n <Button variant=\"contained\" component={AppLink} href={primary.href}>\n {primary.label}\n </Button>\n </Box>\n ) : null}\n {/* The drawer's own plan and its Confirm, on the drawer's request. */}\n <AiJobPlan\n job={job}\n onResume={(target) => void decide(() => resumeAiJobRequest(user, orgId, target))}\n busy={busy}\n staff={staff}\n // \"Use the starter site instead\" on a guided start that did not work out (AGL-3594).\n user={user ?? undefined}\n />\n {!settled ? (\n <Box>\n <Button\n size=\"small\"\n disabled={busy}\n onClick={() => void decide(() => cancelAiJobRequest(user, orgId, job.id))}\n >\n {'Cancel job'}\n </Button>\n </Box>\n ) : null}\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n </Stack>\n )\n}\n\nexport default AiJobFollow\n"],"names":["AppLink","Alert","Box","Button","Chip","CircularProgress","Stack","Typography","useCallback","useEffect","useRef","useState","AI_JOB_PHASE_LABELS","aiJobPhase","AI_JOB_TERMINAL_STATUSES","AiJobPlan","cancelAiJobRequest","resumeAiJobRequest","openAiJobs","publishAiJob","followAiJobEvents","aiJobPrimaryLink","aiJobProblem","PHASE_LINE","queued","severity","text","planning","building","attention","done","failed","canceled","orgSlugFromLocation","path","globalThis","location","pathname","split","useFollowedAiJob","user","orgId","initial","userRef","current","job","setJob","moving","status","undefined","controller","AbortController","id","signal","abort","replace","next","AiJobFollow","orgSlug","intro","onOpenJobs","staff","busy","setBusy","notice","setNotice","phase","line","settled","includes","primary","problem","stepsDone","steps","filter","step","length","decide","request","error","openJobs","jobId","spacing","aria-live","variant","color","icon","size","aria-label","action","onClick","useFlexGap","direction","sx","alignItems","flexWrap","rowGap","label","mt","component","href","onResume","target","disabled"],"mappings":"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"}
@@ -53,6 +53,19 @@ import { AI_JOB_BESIGNER_SEGMENT as BESIGNER_SEGMENT, AI_SITE_BUILD_HREF } from
53
53
  host
54
54
  });
55
55
  }
56
+ if (output.resource === 'entry') {
57
+ var _output_proposal;
58
+ // A post a guided start wrote (AGL-3676) opens in its blog's list in Content.
59
+ const collectionSlug = (_output_proposal = output.proposal) == null ? void 0 : _output_proposal['collectionSlug'];
60
+ return typeof collectionSlug === 'string' && collectionSlug ? buildRoute(Route.HOST_CONTENT_COLLECTION, {
61
+ orgSlug,
62
+ host,
63
+ collectionSlug
64
+ }) : buildRoute(Route.HOST_CONTENT, {
65
+ orgSlug,
66
+ host
67
+ });
68
+ }
56
69
  if (output.resource === 'theme') {
57
70
  return buildRoute(Route.HOST_SETUP_THEME, {
58
71
  orgSlug,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-links.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 */\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport {\n pluginRecordHref,\n pluginRecordListHref,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-routes'\nimport { AI_JOB_BESIGNER_SEGMENT as BESIGNER_SEGMENT, AI_SITE_BUILD_HREF } from '../model/ai-job-notice'\nimport type { AiJobOutput, AiJobSummary } from '../model/ai-jobs.types'\n\n// Where a job's work opens in the console (AGL-2904), shared by the AI jobs\n// drawer and every dialog that follows the job it started (AGL-3593).\n\n/**\n * Where \"open draft\" goes, or `null` when the output has no page of its\n * own — a `text` output carries its copy on the job and is shown inline.\n * A versioned resource opens in the besigner on the version the job wrote;\n * one the console lists without a detail page opens its list; a `theme`\n * proposal opens the site's Theme section, where it is put in the editor.\n *\n * A console URL names a site by its SUBDOMAIN: the `[host]` segment resolves\n * through the member's host projection by subdomain, never by document id.\n * So an output that carries no `hostSubdomain` gets no link rather than one\n * that opens no site.\n */\nexport function aiJobOutputHref(output: AiJobOutput, orgSlug: string): string | null {\n const host = output.hostSubdomain\n if (!orgSlug || !host) return null\n const segment = BESIGNER_SEGMENT[output.resource]\n if (segment) {\n const base = `/${orgSlug}/hosts/${host}/${segment}/${output.id}`\n return output.versionId ? `${base}/versions/${output.versionId}/besigner` : base\n }\n if (output.resource === 'product') {\n // The catalog a drafted product lands in: the commerce plugin's address\n // for it (AGL-3080), or no link where commerce is not loaded.\n return pluginRecordListHref('product', { orgSlug, host })\n }\n if (output.resource === 'workflow') {\n // A drafted automation is an action (AGL-2919), listed switched off on\n // the workflows plugin's list of actions, or no link where it is not loaded.\n return pluginRecordListHref('action', { orgSlug, host })\n }\n if (output.resource === 'theme') {\n return buildRoute(Route.HOST_SETUP_THEME, { orgSlug, host })\n }\n if (output.resource === 'form') {\n // A new form has no version for the besigner to open: its own page — the\n // forms plugin's address for it — mints the first one, and holds the\n // routing and consent it declares.\n return pluginRecordHref('form', { orgSlug, host }, output.id)\n }\n if (output.resource === 'campaign') {\n // A campaign's page is the marketing plugin's, under the site: its\n // address for the campaign (AGL-3080), or no link where it is not loaded.\n return pluginRecordHref('campaign', { orgSlug, host }, output.id)\n }\n return null\n}\n\n/**\n * The ONE action a finished job's row, its notification and the dialog that\n * started it lead with (AGL-3593): what the job built. Several pages from one\n * job — a site's — open the site's Pages list, where they wait as drafts; one\n * output with a page of its own opens that page; anything else has no single\n * place to go, and the row's own links stand. `null` until the job is done.\n */\nexport function aiJobPrimaryLink(\n job: Pick<AiJobSummary, 'status' | 'outputs'>,\n orgSlug: string,\n): { href: string; label: string } | null {\n if (job.status !== 'done' || !orgSlug) return null\n const screens = job.outputs.filter(\n (output) => output.resource === 'screen' && output.hostSubdomain,\n )\n if (screens.length > 1) {\n return {\n href: buildRoute(Route.HOST_SCREENS, { orgSlug, host: String(screens[0].hostSubdomain) }),\n label: 'Open your draft pages',\n }\n }\n for (const output of job.outputs) {\n const href = aiJobOutputHref(output, orgSlug)\n if (href) {\n return {\n href,\n label: output.resource === 'screen' ? 'Open the draft page' : `Open ${output.label}`,\n }\n }\n }\n return null\n}\n\nexport { AI_SITE_BUILD_HREF }\n\n/**\n * The \"Building your site\" page for one site job (AGL-3594): linkable, so the\n * guided start lands on it, a reload keeps it and a notification opens it.\n * `null` without the org slug or the site's subdomain a console URL needs.\n */\nexport function aiSiteBuildHref(\n orgSlug: string | null | undefined,\n hostSubdomain: string | null | undefined,\n jobId: string,\n): string | null {\n if (!orgSlug || !hostSubdomain || !jobId) return null\n return `/${orgSlug}/hosts/${hostSubdomain}${AI_SITE_BUILD_HREF}/${encodeURIComponent(jobId)}`\n}\n\n/**\n * A finished site's two ways in (AGL-3594): its first page's draft, opened in\n * the console's preview of that version, and the Pages list every draft waits\n * in. Either is `null` where the job reported nothing to open.\n */\nexport function aiSiteBuildDoneLinks(\n job: Pick<AiJobSummary, 'outputs'>,\n orgSlug: string,\n): { view: string | null; pages: string | null } {\n const screens = job.outputs.filter((output) => output.resource === 'screen' && output.hostSubdomain)\n const first = screens[0]\n if (!first || !orgSlug) return { view: null, pages: null }\n const host = String(first.hostSubdomain)\n return {\n view: first.versionId\n ? `/${orgSlug}/hosts/${host}/screens/${first.id}/versions/${first.versionId}/view`\n : null,\n pages: buildRoute(Route.HOST_SCREENS, { orgSlug, host }),\n }\n}\n"],"names":["buildRoute","Route","pluginRecordHref","pluginRecordListHref","AI_JOB_BESIGNER_SEGMENT","BESIGNER_SEGMENT","AI_SITE_BUILD_HREF","aiJobOutputHref","output","orgSlug","host","hostSubdomain","segment","resource","base","id","versionId","HOST_SETUP_THEME","aiJobPrimaryLink","job","status","screens","outputs","filter","length","href","HOST_SCREENS","String","label","aiSiteBuildHref","jobId","encodeURIComponent","aiSiteBuildDoneLinks","first","view","pages"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-links.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 */\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport {\n pluginRecordHref,\n pluginRecordListHref,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-routes'\nimport { AI_JOB_BESIGNER_SEGMENT as BESIGNER_SEGMENT, AI_SITE_BUILD_HREF } from '../model/ai-job-notice'\nimport type { AiJobOutput, AiJobSummary } from '../model/ai-jobs.types'\n\n// Where a job's work opens in the console (AGL-2904), shared by the AI jobs\n// drawer and every dialog that follows the job it started (AGL-3593).\n\n/**\n * Where \"open draft\" goes, or `null` when the output has no page of its\n * own — a `text` output carries its copy on the job and is shown inline.\n * A versioned resource opens in the besigner on the version the job wrote;\n * one the console lists without a detail page opens its list; a `theme`\n * proposal opens the site's Theme section, where it is put in the editor.\n *\n * A console URL names a site by its SUBDOMAIN: the `[host]` segment resolves\n * through the member's host projection by subdomain, never by document id.\n * So an output that carries no `hostSubdomain` gets no link rather than one\n * that opens no site.\n */\nexport function aiJobOutputHref(output: AiJobOutput, orgSlug: string): string | null {\n const host = output.hostSubdomain\n if (!orgSlug || !host) return null\n const segment = BESIGNER_SEGMENT[output.resource]\n if (segment) {\n const base = `/${orgSlug}/hosts/${host}/${segment}/${output.id}`\n return output.versionId ? `${base}/versions/${output.versionId}/besigner` : base\n }\n if (output.resource === 'product') {\n // The catalog a drafted product lands in: the commerce plugin's address\n // for it (AGL-3080), or no link where commerce is not loaded.\n return pluginRecordListHref('product', { orgSlug, host })\n }\n if (output.resource === 'workflow') {\n // A drafted automation is an action (AGL-2919), listed switched off on\n // the workflows plugin's list of actions, or no link where it is not loaded.\n return pluginRecordListHref('action', { orgSlug, host })\n }\n if (output.resource === 'entry') {\n // A post a guided start wrote (AGL-3676) opens in its blog's list in Content.\n const collectionSlug = output.proposal?.['collectionSlug']\n return typeof collectionSlug === 'string' && collectionSlug\n ? buildRoute(Route.HOST_CONTENT_COLLECTION, { orgSlug, host, collectionSlug })\n : buildRoute(Route.HOST_CONTENT, { orgSlug, host })\n }\n if (output.resource === 'theme') {\n return buildRoute(Route.HOST_SETUP_THEME, { orgSlug, host })\n }\n if (output.resource === 'form') {\n // A new form has no version for the besigner to open: its own page — the\n // forms plugin's address for it — mints the first one, and holds the\n // routing and consent it declares.\n return pluginRecordHref('form', { orgSlug, host }, output.id)\n }\n if (output.resource === 'campaign') {\n // A campaign's page is the marketing plugin's, under the site: its\n // address for the campaign (AGL-3080), or no link where it is not loaded.\n return pluginRecordHref('campaign', { orgSlug, host }, output.id)\n }\n return null\n}\n\n/**\n * The ONE action a finished job's row, its notification and the dialog that\n * started it lead with (AGL-3593): what the job built. Several pages from one\n * job — a site's — open the site's Pages list, where they wait as drafts; one\n * output with a page of its own opens that page; anything else has no single\n * place to go, and the row's own links stand. `null` until the job is done.\n */\nexport function aiJobPrimaryLink(\n job: Pick<AiJobSummary, 'status' | 'outputs'>,\n orgSlug: string,\n): { href: string; label: string } | null {\n if (job.status !== 'done' || !orgSlug) return null\n const screens = job.outputs.filter(\n (output) => output.resource === 'screen' && output.hostSubdomain,\n )\n if (screens.length > 1) {\n return {\n href: buildRoute(Route.HOST_SCREENS, { orgSlug, host: String(screens[0].hostSubdomain) }),\n label: 'Open your draft pages',\n }\n }\n for (const output of job.outputs) {\n const href = aiJobOutputHref(output, orgSlug)\n if (href) {\n return {\n href,\n label: output.resource === 'screen' ? 'Open the draft page' : `Open ${output.label}`,\n }\n }\n }\n return null\n}\n\nexport { AI_SITE_BUILD_HREF }\n\n/**\n * The \"Building your site\" page for one site job (AGL-3594): linkable, so the\n * guided start lands on it, a reload keeps it and a notification opens it.\n * `null` without the org slug or the site's subdomain a console URL needs.\n */\nexport function aiSiteBuildHref(\n orgSlug: string | null | undefined,\n hostSubdomain: string | null | undefined,\n jobId: string,\n): string | null {\n if (!orgSlug || !hostSubdomain || !jobId) return null\n return `/${orgSlug}/hosts/${hostSubdomain}${AI_SITE_BUILD_HREF}/${encodeURIComponent(jobId)}`\n}\n\n/**\n * A finished site's two ways in (AGL-3594): its first page's draft, opened in\n * the console's preview of that version, and the Pages list every draft waits\n * in. Either is `null` where the job reported nothing to open.\n */\nexport function aiSiteBuildDoneLinks(\n job: Pick<AiJobSummary, 'outputs'>,\n orgSlug: string,\n): { view: string | null; pages: string | null } {\n const screens = job.outputs.filter((output) => output.resource === 'screen' && output.hostSubdomain)\n const first = screens[0]\n if (!first || !orgSlug) return { view: null, pages: null }\n const host = String(first.hostSubdomain)\n return {\n view: first.versionId\n ? `/${orgSlug}/hosts/${host}/screens/${first.id}/versions/${first.versionId}/view`\n : null,\n pages: buildRoute(Route.HOST_SCREENS, { orgSlug, host }),\n }\n}\n"],"names":["buildRoute","Route","pluginRecordHref","pluginRecordListHref","AI_JOB_BESIGNER_SEGMENT","BESIGNER_SEGMENT","AI_SITE_BUILD_HREF","aiJobOutputHref","output","orgSlug","host","hostSubdomain","segment","resource","base","id","versionId","collectionSlug","proposal","HOST_CONTENT_COLLECTION","HOST_CONTENT","HOST_SETUP_THEME","aiJobPrimaryLink","job","status","screens","outputs","filter","length","href","HOST_SCREENS","String","label","aiSiteBuildHref","jobId","encodeURIComponent","aiSiteBuildDoneLinks","first","view","pages"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD,SAASA,UAAU,EAAEC,KAAK,QAAQ,wCAAuC;AACzE,SACEC,gBAAgB,EAChBC,oBAAoB,QACf,mDAAkD;AACzD,SAASC,2BAA2BC,gBAAgB,EAAEC,kBAAkB,QAAQ,4BAAwB;AAGxG,4EAA4E;AAC5E,sEAAsE;AAEtE;;;;;;;;;;;CAWC,GACD,OAAO,SAASC,gBAAgBC,MAAmB,EAAEC,OAAe;IAClE,MAAMC,OAAOF,OAAOG,aAAa;IACjC,IAAI,CAACF,WAAW,CAACC,MAAM,OAAO;IAC9B,MAAME,UAAUP,gBAAgB,CAACG,OAAOK,QAAQ,CAAC;IACjD,IAAID,SAAS;QACX,MAAME,OAAO,CAAC,CAAC,EAAEL,QAAQ,OAAO,EAAEC,KAAK,CAAC,EAAEE,QAAQ,CAAC,EAAEJ,OAAOO,EAAE,EAAE;QAChE,OAAOP,OAAOQ,SAAS,GAAG,GAAGF,KAAK,UAAU,EAAEN,OAAOQ,SAAS,CAAC,SAAS,CAAC,GAAGF;IAC9E;IACA,IAAIN,OAAOK,QAAQ,KAAK,WAAW;QACjC,wEAAwE;QACxE,8DAA8D;QAC9D,OAAOV,qBAAqB,WAAW;YAAEM;YAASC;QAAK;IACzD;IACA,IAAIF,OAAOK,QAAQ,KAAK,YAAY;QAClC,uEAAuE;QACvE,6EAA6E;QAC7E,OAAOV,qBAAqB,UAAU;YAAEM;YAASC;QAAK;IACxD;IACA,IAAIF,OAAOK,QAAQ,KAAK,SAAS;YAERL;QADvB,8EAA8E;QAC9E,MAAMS,kBAAiBT,mBAAAA,OAAOU,QAAQ,qBAAfV,gBAAiB,CAAC,iBAAiB;QAC1D,OAAO,OAAOS,mBAAmB,YAAYA,iBACzCjB,WAAWC,MAAMkB,uBAAuB,EAAE;YAAEV;YAASC;YAAMO;QAAe,KAC1EjB,WAAWC,MAAMmB,YAAY,EAAE;YAAEX;YAASC;QAAK;IACrD;IACA,IAAIF,OAAOK,QAAQ,KAAK,SAAS;QAC/B,OAAOb,WAAWC,MAAMoB,gBAAgB,EAAE;YAAEZ;YAASC;QAAK;IAC5D;IACA,IAAIF,OAAOK,QAAQ,KAAK,QAAQ;QAC9B,yEAAyE;QACzE,qEAAqE;QACrE,mCAAmC;QACnC,OAAOX,iBAAiB,QAAQ;YAAEO;YAASC;QAAK,GAAGF,OAAOO,EAAE;IAC9D;IACA,IAAIP,OAAOK,QAAQ,KAAK,YAAY;QAClC,mEAAmE;QACnE,0EAA0E;QAC1E,OAAOX,iBAAiB,YAAY;YAAEO;YAASC;QAAK,GAAGF,OAAOO,EAAE;IAClE;IACA,OAAO;AACT;AAEA;;;;;;CAMC,GACD,OAAO,SAASO,iBACdC,GAA6C,EAC7Cd,OAAe;IAEf,IAAIc,IAAIC,MAAM,KAAK,UAAU,CAACf,SAAS,OAAO;IAC9C,MAAMgB,UAAUF,IAAIG,OAAO,CAACC,MAAM,CAChC,CAACnB,SAAWA,OAAOK,QAAQ,KAAK,YAAYL,OAAOG,aAAa;IAElE,IAAIc,QAAQG,MAAM,GAAG,GAAG;QACtB,OAAO;YACLC,MAAM7B,WAAWC,MAAM6B,YAAY,EAAE;gBAAErB;gBAASC,MAAMqB,OAAON,OAAO,CAAC,EAAE,CAACd,aAAa;YAAE;YACvFqB,OAAO;QACT;IACF;IACA,KAAK,MAAMxB,UAAUe,IAAIG,OAAO,CAAE;QAChC,MAAMG,OAAOtB,gBAAgBC,QAAQC;QACrC,IAAIoB,MAAM;YACR,OAAO;gBACLA;gBACAG,OAAOxB,OAAOK,QAAQ,KAAK,WAAW,wBAAwB,CAAC,KAAK,EAAEL,OAAOwB,KAAK,EAAE;YACtF;QACF;IACF;IACA,OAAO;AACT;AAEA,SAAS1B,kBAAkB,GAAE;AAE7B;;;;CAIC,GACD,OAAO,SAAS2B,gBACdxB,OAAkC,EAClCE,aAAwC,EACxCuB,KAAa;IAEb,IAAI,CAACzB,WAAW,CAACE,iBAAiB,CAACuB,OAAO,OAAO;IACjD,OAAO,CAAC,CAAC,EAAEzB,QAAQ,OAAO,EAAEE,gBAAgBL,mBAAmB,CAAC,EAAE6B,mBAAmBD,QAAQ;AAC/F;AAEA;;;;CAIC,GACD,OAAO,SAASE,qBACdb,GAAkC,EAClCd,OAAe;IAEf,MAAMgB,UAAUF,IAAIG,OAAO,CAACC,MAAM,CAAC,CAACnB,SAAWA,OAAOK,QAAQ,KAAK,YAAYL,OAAOG,aAAa;IACnG,MAAM0B,QAAQZ,OAAO,CAAC,EAAE;IACxB,IAAI,CAACY,SAAS,CAAC5B,SAAS,OAAO;QAAE6B,MAAM;QAAMC,OAAO;IAAK;IACzD,MAAM7B,OAAOqB,OAAOM,MAAM1B,aAAa;IACvC,OAAO;QACL2B,MAAMD,MAAMrB,SAAS,GACjB,CAAC,CAAC,EAAEP,QAAQ,OAAO,EAAEC,KAAK,SAAS,EAAE2B,MAAMtB,EAAE,CAAC,UAAU,EAAEsB,MAAMrB,SAAS,CAAC,KAAK,CAAC,GAChF;QACJuB,OAAOvC,WAAWC,MAAM6B,YAAY,EAAE;YAAErB;YAASC;QAAK;IACxD;AACF"}
@@ -17,6 +17,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
17
17
  */ import { AI_BUILD_PLAN_CREATION_NOUNS, AI_BUILD_PLAN_EMBED_HOST_NAMES, isAiPlanNewRef } from "../model/ai-build-plan.js";
18
18
  import { aiJobCreditEstimate } from "../model/ai-build-job.js";
19
19
  import { aiBuildOpNoun } from "../model/ai-build-progress.js";
20
+ import { aiJobConfirmingOwnPlan } from "../model/ai-job-activity.js";
20
21
  import { aiSiteStarterFallbackOffered } from "../model/ai-job-failure-copy.js";
21
22
  import { AiSiteStarterFallback } from "./ai-site-starter-fallback.component.js";
22
23
  import { Box, Button, Checkbox, Collapse, FormControlLabel, Stack, Typography } from "@mui/material";
@@ -79,7 +80,8 @@ export function AiJobPlan({ job, onResume, busy = false, staff = false, user })
79
80
  var _ref;
80
81
  return !ref ? '' : isAiPlanNewRef(ref) ? ref.slice('new:'.length) : (_ref = plan == null ? void 0 : plan.labels[ref]) != null ? _ref : ref;
81
82
  };
82
- const waiting = job.status === 'needs_review' && review !== null;
83
+ // A plan the machine is confirming itself is not one to confirm (AGL-3596).
84
+ const waiting = job.status === 'needs_review' && review !== null && !aiJobConfirmingOwnPlan(job);
83
85
  // The guard rail (AGL-2911): what the plan is estimated to cost is read
84
86
  // before it is confirmed, not after it has been spent. An estimate, and
85
87
  // said to be one — the plan's own passes at the nominal credits a step
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-plan.component.tsx"],"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 AI_BUILD_PLAN_CREATION_NOUNS,\n AI_BUILD_PLAN_EMBED_HOST_NAMES,\n isAiPlanNewRef,\n type AiBuildPlanEmbed,\n} from '../model/ai-build-plan'\nimport type { AiJobReview, AiJobSummary } from '../model/ai-jobs.types'\nimport { aiJobCreditEstimate } from '../model/ai-build-job'\nimport { aiBuildOpNoun } from '../model/ai-build-progress'\nimport { aiSiteStarterFallbackOffered } from '../model/ai-job-failure-copy'\nimport { AiSiteStarterFallback } from './ai-site-starter-fallback.component'\nimport { Box, Button, Checkbox, Collapse, FormControlLabel, Stack, Typography } from '@mui/material'\nimport { useState } from 'react'\n\n/**\n * A job's plan and what it waits for (AGL-2935), inside the AI jobs drawer\n * and the dialog that started the job (AGL-3593): what the job reuses from\n * the site, what it creates and why, the screens it builds — and, while the\n * job waits for a person, the one button that confirms the plan or tries a\n * refused step again. Both send the same request (`resumeAiJobRequest`);\n * this renders.\n */\nexport interface AiJobPlanProps {\n job: AiJobSummary\n /**\n * Confirms the plan, or tries the refused step again. A build whose request\n * asked to publish confirms with `publish` when its box is ticked (AGL-3616).\n */\n onResume: (job: AiJobSummary, options?: { publish?: boolean }) => void\n /** A resume for this job is in flight. */\n busy?: boolean\n /**\n * The viewer is staff (AGL-3078), who can open where a refused answer broke\n * its rules and the outline of those parts, to tell a shape the rules should\n * allow from an answer that ignored its re-ask. A member reads the findings.\n */\n staff?: boolean\n /**\n * The signed-in user, for \"Use the starter site instead\" on a guided start\n * that did not work out (AGL-3594); the action is drawn only when given.\n */\n user?: Parameters<typeof AiSiteStarterFallback>[0]['user']\n}\n\n/**\n * Where a refused answer broke its rules, and the outline of those parts, as\n * lines staff read (AGL-3078): each finding with the nodes or plan entries it\n * names, then each outlined node indented by its depth, with its element, a\n * Grid's layout as written, and the names of what it sets and holds.\n */\nexport function aiJobReviewDetails(review: AiJobReview | null): string[] {\n if (review?.reason !== 'doctrine') return []\n const lines = review.findings.flatMap((finding) => {\n const where = finding.nodeIds?.length\n ? `nodes ${finding.nodeIds.join(', ')}`\n : finding.paths?.length\n ? `at ${finding.paths.join(', ')}`\n : null\n return where ? [`${finding.rule === null ? '' : `Rule ${finding.rule} `}${finding.code}: ${where}`] : []\n })\n for (const node of review.outline ?? []) {\n lines.push(\n [\n `${' '.repeat(node.depth)}${node.id} ${node.componentId}`,\n ...Object.entries(node.grid ?? {}).map(([name, value]) => `${name}=${JSON.stringify(value)}`),\n ...(node.props.length ? [`props ${node.props.join(', ')}`] : []),\n ...(node.sx?.length ? [`sx ${node.sx.join(', ')}`] : []),\n ...(node.children.length ? [`holds ${node.children.join(', ')}`] : []),\n ].join(' · '),\n )\n }\n return lines\n}\n\n/**\n * A third-party player the plan lists, with what it costs the page, read\n * before the member confirms it (rule 16, AGL-3433). The cost is the\n * platform's sentence, never the model's: the player is framed lazily, so its\n * host's code loads when a visitor reaches it, whether or not they press play.\n */\nexport function aiPlanEmbedLine(\n embed: AiBuildPlanEmbed,\n named: (ref: string) => string,\n): string {\n const host = AI_BUILD_PLAN_EMBED_HOST_NAMES[embed.host]\n const where = isAiPlanNewRef(embed.where) ? `the component ${named(embed.where)}` : embed.where\n const link = embed.url ? ` playing ${embed.url}` : ', its link left for you to paste'\n return `Embeds a ${host} player on ${where}${link}, as you asked (“${embed.asked}”). It loads ${host}’s own code when a visitor reaches it, whether or not they press play.`\n}\n\nexport function AiJobPlan({\n job,\n onResume,\n busy = false,\n staff = false,\n user,\n}: AiJobPlanProps): JSX.Element | null {\n const [detailsOpen, setDetailsOpen] = useState(false)\n // Unticked until the person ticks it: a build publishes only when its\n // request asked AND they confirm it here (AGL-3616).\n const [publish, setPublish] = useState(false)\n const { plan, review } = job\n if (!plan && !review) return null\n const details = staff ? aiJobReviewDetails(review) : []\n /** A reference as a person reads it: the record's name, or what the plan creates. */\n const named = (ref: string | null): string =>\n !ref\n ? ''\n : isAiPlanNewRef(ref)\n ? ref.slice('new:'.length)\n : (plan?.labels[ref] ?? ref)\n const waiting = job.status === 'needs_review' && review !== null\n // The guard rail (AGL-2911): what the plan is estimated to cost is read\n // before it is confirmed, not after it has been spent. An estimate, and\n // said to be one — the plan's own passes at the nominal credits a step\n // holds, where what a step really costs is its model's tokens. A page job\n // counts the creations it builds before its page (AGL-3031).\n const estimate =\n plan && waiting && review.reason === 'plan' ? aiJobCreditEstimate(job.kind, plan) : 0\n const offersPublish = job.kind === 'build' && job.publishAsked === true && waiting && review.reason === 'plan'\n return (\n <Box sx={{ mt: 1 }}>\n {plan && (\n <Stack spacing={0.5} role=\"list\" aria-label=\"Plan\">\n <Typography variant=\"caption\" color=\"text.secondary\">\n {plan.status === 'confirmed' ? 'Confirmed plan' : 'Proposed plan'}\n </Typography>\n {plan.reuse.map((entry, index) => (\n <Typography key={`reuse-${index}`} variant=\"body2\" role=\"listitem\">\n Reuses the {entry.kind === 'screen' ? 'page' : entry.kind} {named(entry.id)} — {entry.purpose}\n </Typography>\n ))}\n {plan.create.map((entry, index) => (\n <Typography key={`create-${index}`} variant=\"body2\" role=\"listitem\">\n Creates the {AI_BUILD_PLAN_CREATION_NOUNS[entry.kind].noun} {entry.name}\n {entry.duplicateOf\n ? `, from a copy of ${named(entry.duplicateOf)}`\n : ''}{' '}\n — {entry.why}\n </Typography>\n ))}\n {plan.screens.map((screen, index) => (\n <Typography key={`screen-${index}`} variant=\"body2\" role=\"listitem\">\n {screen.record\n ? `Builds the record template ${screen.title}, a page per record of ${named(screen.record.dataset)} at /${screen.record.base}/…`\n : `Builds the page ${screen.title} at ${screen.slug}`}\n {screen.duplicateOf\n ? `, from a copy of ${named(screen.duplicateOf)}`\n : ''}\n {screen.layout ? ` in ${named(screen.layout)}` : ''}\n {screen.sections.length\n ? `: ${screen.sections.map((section) => section.name).join(', ')}`\n : ''}\n </Typography>\n ))}\n {(plan.items ?? []).map((item) => (\n <Typography key={`item-${item.slot}`} variant=\"body2\" role=\"listitem\">\n {`Makes the ${aiBuildOpNoun(item.op).toLowerCase()} ${item.name}`}\n {item.why ? ` — ${item.why}` : ''}\n </Typography>\n ))}\n {(plan.embeds ?? []).map((embed, index) => (\n <Typography key={`embed-${index}`} variant=\"body2\" role=\"listitem\">\n {aiPlanEmbedLine(embed, named)}\n </Typography>\n ))}\n </Stack>\n )}\n {/*\n A review written for the member (AGL-3594) carries the checks' own\n sentence as `detail`: its findings are then staff reading too, and the\n member reads the job's one sentence alone.\n */}\n {staff && review?.detail && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 0.5 }}>\n {review.detail}\n </Typography>\n )}\n {review?.reason === 'doctrine' && review.findings.length > 0 && (staff || !review.detail) && (\n <Box\n component=\"ul\"\n sx={{ my: 0.5, pl: 2.5 }}\n aria-label=\"Building rules broken\"\n >\n {review.findings.map((finding, index) => (\n <Typography\n key={`finding-${index}`}\n component=\"li\"\n variant=\"caption\"\n >\n {finding.message}\n </Typography>\n ))}\n </Box>\n )}\n {details.length > 0 && (\n <Box sx={{ mt: 0.5 }}>\n <Button\n size=\"small\"\n aria-expanded={detailsOpen}\n onClick={() => setDetailsOpen((prior) => !prior)}\n >\n {detailsOpen ? 'Hide what was refused' : 'Show what was refused'}\n </Button>\n <Collapse in={detailsOpen} unmountOnExit>\n <Typography\n variant=\"caption\"\n component=\"pre\"\n aria-label=\"What was refused\"\n sx={{ m: 0, fontFamily: 'monospace', whiteSpace: 'pre', overflowX: 'auto' }}\n >\n {details.join('\\n')}\n </Typography>\n </Collapse>\n </Box>\n )}\n {estimate > 0 && (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', mt: 1 }}\n >\n {`Estimated cost: about ${estimate.toLocaleString('en-US')} credits. What it costs is what its steps spend.`}\n </Typography>\n )}\n {waiting && review.retryRefusal && review.reason !== 'plan' && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 1 }}>\n {review.retryRefusal}\n </Typography>\n )}\n {offersPublish && (\n <FormControlLabel\n sx={{ display: 'flex', mt: 1 }}\n control={<Checkbox size=\"small\" checked={publish} onChange={(event) => setPublish(event.target.checked)} />}\n label={<Typography variant=\"body2\">{'Publish the new pages when they are built'}</Typography>}\n />\n )}\n {waiting && (\n <Button\n size=\"small\"\n variant=\"contained\"\n // Disabled with its reason above when the Free allowance left\n // cannot pay for another try (AGL-3594).\n disabled={busy || (review.reason !== 'plan' && Boolean(review.retryRefusal))}\n onClick={() => onResume(job, offersPublish && publish ? { publish: true } : undefined)}\n sx={{ mt: 1 }}\n >\n {review.reason === 'plan' ? 'Confirm plan' : 'Try again'}\n </Button>\n )}\n {/* A guided start that did not work out can take the starter instead (AGL-3594). */}\n {waiting && user && aiSiteStarterFallbackOffered(job) && <AiSiteStarterFallback job={job} user={user} />}\n </Box>\n )\n}\n\nexport default AiJobPlan\n"],"names":["AI_BUILD_PLAN_CREATION_NOUNS","AI_BUILD_PLAN_EMBED_HOST_NAMES","isAiPlanNewRef","aiJobCreditEstimate","aiBuildOpNoun","aiSiteStarterFallbackOffered","AiSiteStarterFallback","Box","Button","Checkbox","Collapse","FormControlLabel","Stack","Typography","useState","aiJobReviewDetails","review","reason","lines","findings","flatMap","finding","where","nodeIds","length","join","paths","rule","code","node","outline","push","repeat","depth","id","componentId","Object","entries","grid","map","name","value","JSON","stringify","props","sx","children","aiPlanEmbedLine","embed","named","host","link","url","asked","AiJobPlan","job","onResume","busy","staff","user","plan","detailsOpen","setDetailsOpen","publish","setPublish","details","ref","slice","labels","waiting","status","estimate","kind","offersPublish","publishAsked","mt","spacing","role","aria-label","variant","color","reuse","entry","index","purpose","create","noun","duplicateOf","why","screens","screen","record","title","dataset","base","slug","layout","sections","section","items","item","op","toLowerCase","slot","embeds","detail","component","my","pl","message","size","aria-expanded","onClick","prior","in","unmountOnExit","m","fontFamily","whiteSpace","overflowX","display","toLocaleString","retryRefusal","control","checked","onChange","event","target","label","disabled","Boolean","undefined"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-plan.component.tsx"],"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 AI_BUILD_PLAN_CREATION_NOUNS,\n AI_BUILD_PLAN_EMBED_HOST_NAMES,\n isAiPlanNewRef,\n type AiBuildPlanEmbed,\n} from '../model/ai-build-plan'\nimport type { AiJobReview, AiJobSummary } from '../model/ai-jobs.types'\nimport { aiJobCreditEstimate } from '../model/ai-build-job'\nimport { aiBuildOpNoun } from '../model/ai-build-progress'\nimport { aiJobConfirmingOwnPlan } from '../model/ai-job-activity'\nimport { aiSiteStarterFallbackOffered } from '../model/ai-job-failure-copy'\nimport { AiSiteStarterFallback } from './ai-site-starter-fallback.component'\nimport { Box, Button, Checkbox, Collapse, FormControlLabel, Stack, Typography } from '@mui/material'\nimport { useState } from 'react'\n\n/**\n * A job's plan and what it waits for (AGL-2935), inside the AI jobs drawer\n * and the dialog that started the job (AGL-3593): what the job reuses from\n * the site, what it creates and why, the screens it builds — and, while the\n * job waits for a person, the one button that confirms the plan or tries a\n * refused step again. Both send the same request (`resumeAiJobRequest`);\n * this renders.\n */\nexport interface AiJobPlanProps {\n job: AiJobSummary\n /**\n * Confirms the plan, or tries the refused step again. A build whose request\n * asked to publish confirms with `publish` when its box is ticked (AGL-3616).\n */\n onResume: (job: AiJobSummary, options?: { publish?: boolean }) => void\n /** A resume for this job is in flight. */\n busy?: boolean\n /**\n * The viewer is staff (AGL-3078), who can open where a refused answer broke\n * its rules and the outline of those parts, to tell a shape the rules should\n * allow from an answer that ignored its re-ask. A member reads the findings.\n */\n staff?: boolean\n /**\n * The signed-in user, for \"Use the starter site instead\" on a guided start\n * that did not work out (AGL-3594); the action is drawn only when given.\n */\n user?: Parameters<typeof AiSiteStarterFallback>[0]['user']\n}\n\n/**\n * Where a refused answer broke its rules, and the outline of those parts, as\n * lines staff read (AGL-3078): each finding with the nodes or plan entries it\n * names, then each outlined node indented by its depth, with its element, a\n * Grid's layout as written, and the names of what it sets and holds.\n */\nexport function aiJobReviewDetails(review: AiJobReview | null): string[] {\n if (review?.reason !== 'doctrine') return []\n const lines = review.findings.flatMap((finding) => {\n const where = finding.nodeIds?.length\n ? `nodes ${finding.nodeIds.join(', ')}`\n : finding.paths?.length\n ? `at ${finding.paths.join(', ')}`\n : null\n return where ? [`${finding.rule === null ? '' : `Rule ${finding.rule} `}${finding.code}: ${where}`] : []\n })\n for (const node of review.outline ?? []) {\n lines.push(\n [\n `${' '.repeat(node.depth)}${node.id} ${node.componentId}`,\n ...Object.entries(node.grid ?? {}).map(([name, value]) => `${name}=${JSON.stringify(value)}`),\n ...(node.props.length ? [`props ${node.props.join(', ')}`] : []),\n ...(node.sx?.length ? [`sx ${node.sx.join(', ')}`] : []),\n ...(node.children.length ? [`holds ${node.children.join(', ')}`] : []),\n ].join(' · '),\n )\n }\n return lines\n}\n\n/**\n * A third-party player the plan lists, with what it costs the page, read\n * before the member confirms it (rule 16, AGL-3433). The cost is the\n * platform's sentence, never the model's: the player is framed lazily, so its\n * host's code loads when a visitor reaches it, whether or not they press play.\n */\nexport function aiPlanEmbedLine(\n embed: AiBuildPlanEmbed,\n named: (ref: string) => string,\n): string {\n const host = AI_BUILD_PLAN_EMBED_HOST_NAMES[embed.host]\n const where = isAiPlanNewRef(embed.where) ? `the component ${named(embed.where)}` : embed.where\n const link = embed.url ? ` playing ${embed.url}` : ', its link left for you to paste'\n return `Embeds a ${host} player on ${where}${link}, as you asked (“${embed.asked}”). It loads ${host}’s own code when a visitor reaches it, whether or not they press play.`\n}\n\nexport function AiJobPlan({\n job,\n onResume,\n busy = false,\n staff = false,\n user,\n}: AiJobPlanProps): JSX.Element | null {\n const [detailsOpen, setDetailsOpen] = useState(false)\n // Unticked until the person ticks it: a build publishes only when its\n // request asked AND they confirm it here (AGL-3616).\n const [publish, setPublish] = useState(false)\n const { plan, review } = job\n if (!plan && !review) return null\n const details = staff ? aiJobReviewDetails(review) : []\n /** A reference as a person reads it: the record's name, or what the plan creates. */\n const named = (ref: string | null): string =>\n !ref\n ? ''\n : isAiPlanNewRef(ref)\n ? ref.slice('new:'.length)\n : (plan?.labels[ref] ?? ref)\n // A plan the machine is confirming itself is not one to confirm (AGL-3596).\n const waiting = job.status === 'needs_review' && review !== null && !aiJobConfirmingOwnPlan(job)\n // The guard rail (AGL-2911): what the plan is estimated to cost is read\n // before it is confirmed, not after it has been spent. An estimate, and\n // said to be one — the plan's own passes at the nominal credits a step\n // holds, where what a step really costs is its model's tokens. A page job\n // counts the creations it builds before its page (AGL-3031).\n const estimate =\n plan && waiting && review.reason === 'plan' ? aiJobCreditEstimate(job.kind, plan) : 0\n const offersPublish = job.kind === 'build' && job.publishAsked === true && waiting && review.reason === 'plan'\n return (\n <Box sx={{ mt: 1 }}>\n {plan && (\n <Stack spacing={0.5} role=\"list\" aria-label=\"Plan\">\n <Typography variant=\"caption\" color=\"text.secondary\">\n {plan.status === 'confirmed' ? 'Confirmed plan' : 'Proposed plan'}\n </Typography>\n {plan.reuse.map((entry, index) => (\n <Typography key={`reuse-${index}`} variant=\"body2\" role=\"listitem\">\n Reuses the {entry.kind === 'screen' ? 'page' : entry.kind} {named(entry.id)} — {entry.purpose}\n </Typography>\n ))}\n {plan.create.map((entry, index) => (\n <Typography key={`create-${index}`} variant=\"body2\" role=\"listitem\">\n Creates the {AI_BUILD_PLAN_CREATION_NOUNS[entry.kind].noun} {entry.name}\n {entry.duplicateOf\n ? `, from a copy of ${named(entry.duplicateOf)}`\n : ''}{' '}\n — {entry.why}\n </Typography>\n ))}\n {plan.screens.map((screen, index) => (\n <Typography key={`screen-${index}`} variant=\"body2\" role=\"listitem\">\n {screen.record\n ? `Builds the record template ${screen.title}, a page per record of ${named(screen.record.dataset)} at /${screen.record.base}/…`\n : `Builds the page ${screen.title} at ${screen.slug}`}\n {screen.duplicateOf\n ? `, from a copy of ${named(screen.duplicateOf)}`\n : ''}\n {screen.layout ? ` in ${named(screen.layout)}` : ''}\n {screen.sections.length\n ? `: ${screen.sections.map((section) => section.name).join(', ')}`\n : ''}\n </Typography>\n ))}\n {(plan.items ?? []).map((item) => (\n <Typography key={`item-${item.slot}`} variant=\"body2\" role=\"listitem\">\n {`Makes the ${aiBuildOpNoun(item.op).toLowerCase()} ${item.name}`}\n {item.why ? ` — ${item.why}` : ''}\n </Typography>\n ))}\n {(plan.embeds ?? []).map((embed, index) => (\n <Typography key={`embed-${index}`} variant=\"body2\" role=\"listitem\">\n {aiPlanEmbedLine(embed, named)}\n </Typography>\n ))}\n </Stack>\n )}\n {/*\n A review written for the member (AGL-3594) carries the checks' own\n sentence as `detail`: its findings are then staff reading too, and the\n member reads the job's one sentence alone.\n */}\n {staff && review?.detail && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 0.5 }}>\n {review.detail}\n </Typography>\n )}\n {review?.reason === 'doctrine' && review.findings.length > 0 && (staff || !review.detail) && (\n <Box\n component=\"ul\"\n sx={{ my: 0.5, pl: 2.5 }}\n aria-label=\"Building rules broken\"\n >\n {review.findings.map((finding, index) => (\n <Typography\n key={`finding-${index}`}\n component=\"li\"\n variant=\"caption\"\n >\n {finding.message}\n </Typography>\n ))}\n </Box>\n )}\n {details.length > 0 && (\n <Box sx={{ mt: 0.5 }}>\n <Button\n size=\"small\"\n aria-expanded={detailsOpen}\n onClick={() => setDetailsOpen((prior) => !prior)}\n >\n {detailsOpen ? 'Hide what was refused' : 'Show what was refused'}\n </Button>\n <Collapse in={detailsOpen} unmountOnExit>\n <Typography\n variant=\"caption\"\n component=\"pre\"\n aria-label=\"What was refused\"\n sx={{ m: 0, fontFamily: 'monospace', whiteSpace: 'pre', overflowX: 'auto' }}\n >\n {details.join('\\n')}\n </Typography>\n </Collapse>\n </Box>\n )}\n {estimate > 0 && (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', mt: 1 }}\n >\n {`Estimated cost: about ${estimate.toLocaleString('en-US')} credits. What it costs is what its steps spend.`}\n </Typography>\n )}\n {waiting && review.retryRefusal && review.reason !== 'plan' && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 1 }}>\n {review.retryRefusal}\n </Typography>\n )}\n {offersPublish && (\n <FormControlLabel\n sx={{ display: 'flex', mt: 1 }}\n control={<Checkbox size=\"small\" checked={publish} onChange={(event) => setPublish(event.target.checked)} />}\n label={<Typography variant=\"body2\">{'Publish the new pages when they are built'}</Typography>}\n />\n )}\n {waiting && (\n <Button\n size=\"small\"\n variant=\"contained\"\n // Disabled with its reason above when the Free allowance left\n // cannot pay for another try (AGL-3594).\n disabled={busy || (review.reason !== 'plan' && Boolean(review.retryRefusal))}\n onClick={() => onResume(job, offersPublish && publish ? { publish: true } : undefined)}\n sx={{ mt: 1 }}\n >\n {review.reason === 'plan' ? 'Confirm plan' : 'Try again'}\n </Button>\n )}\n {/* A guided start that did not work out can take the starter instead (AGL-3594). */}\n {waiting && user && aiSiteStarterFallbackOffered(job) && <AiSiteStarterFallback job={job} user={user} />}\n </Box>\n )\n}\n\nexport default AiJobPlan\n"],"names":["AI_BUILD_PLAN_CREATION_NOUNS","AI_BUILD_PLAN_EMBED_HOST_NAMES","isAiPlanNewRef","aiJobCreditEstimate","aiBuildOpNoun","aiJobConfirmingOwnPlan","aiSiteStarterFallbackOffered","AiSiteStarterFallback","Box","Button","Checkbox","Collapse","FormControlLabel","Stack","Typography","useState","aiJobReviewDetails","review","reason","lines","findings","flatMap","finding","where","nodeIds","length","join","paths","rule","code","node","outline","push","repeat","depth","id","componentId","Object","entries","grid","map","name","value","JSON","stringify","props","sx","children","aiPlanEmbedLine","embed","named","host","link","url","asked","AiJobPlan","job","onResume","busy","staff","user","plan","detailsOpen","setDetailsOpen","publish","setPublish","details","ref","slice","labels","waiting","status","estimate","kind","offersPublish","publishAsked","mt","spacing","role","aria-label","variant","color","reuse","entry","index","purpose","create","noun","duplicateOf","why","screens","screen","record","title","dataset","base","slug","layout","sections","section","items","item","op","toLowerCase","slot","embeds","detail","component","my","pl","message","size","aria-expanded","onClick","prior","in","unmountOnExit","m","fontFamily","whiteSpace","overflowX","display","toLocaleString","retryRefusal","control","checked","onChange","event","target","label","disabled","Boolean","undefined"],"mappings":"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"}
@@ -244,6 +244,7 @@ export function AiJobsListPage({ hostId, basePath }) {
244
244
  /*#__PURE__*/ _jsxs(Stack, {
245
245
  direction: "row",
246
246
  spacing: 1,
247
+ useFlexGap: true,
247
248
  sx: {
248
249
  alignItems: 'center',
249
250
  flexWrap: 'wrap',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-jobs-list-page.component.tsx"],"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'use client'\n\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn'\nimport type { ConsolePluginPageProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { aiJobRefundCopy } from '../model/ai-job-failure-copy'\nimport { AppLink } from '@aglyn/shared-ui-jsx'\nimport { authorizedFetch, type MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Box,\n Button,\n Card,\n Chip,\n CircularProgress,\n Container,\n List,\n ListItemButton,\n Stack,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { AI_JOB_PHASE_LABELS, aiJobKindNoun, aiJobPhase, type AiJobPhase } from '../model/ai-job-activity'\nimport type { AiJobSummary } from '../model/ai-jobs.types'\nimport { publishAiJob } from './ai-jobs-store'\nimport { useAiJobSite } from './ai-job-site'\n\n/**\n * A site's AI jobs (AGL-3596), at `/ai-jobs` under the site: every job that\n * worked on this site, newest first, each linking to its own page.\n *\n * Read through the jobs route, never Firestore, so the membership, flag and\n * plan rungs that answer every other jobs read answer this one. The site is\n * a query on the route (`hostId`), not a filter of the workspace's list.\n *\n * Every wait ends: the site's read and the list's each settle on content,\n * the empty state, or a sentence with Try again.\n */\n\n/** The most jobs the page lists; the newest, which is what a person comes for. */\nexport const AI_JOBS_PAGE_LIMIT = 50\n\n/** How long the list may take before the page says it could not be loaded. */\nexport const AI_JOBS_LIST_TIMEOUT_MS = 20_000\n\n/** How much of a brief a row shows. */\nconst BRIEF_PREVIEW_CHARS = 120\n\n/** A row's chip, colored as the AI jobs drawer colors it. */\nconst PHASE_COLOR: Record<AiJobPhase, 'default' | 'primary' | 'success' | 'error' | 'warning'> = {\n queued: 'default',\n planning: 'primary',\n 'plan-ready': 'warning',\n building: 'primary',\n attention: 'warning',\n done: 'success',\n failed: 'error',\n canceled: 'default',\n}\n\nconst WHEN = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium', timeStyle: 'short' })\n\n/** What a job cost the person: what it spent, less what we refunded. */\nexport function aiJobNetCredits(job: Pick<AiJobSummary, 'creditsSpent' | 'refundedCredits'>): number {\n return Math.max(0, (job.creditsSpent ?? 0) - (job.refundedCredits ?? 0))\n}\n\n/** The kind as a row names it: `Site`, `Page`, `AI job`. */\nexport function aiJobKindLabel(kind: AiJobSummary['kind']): string {\n const noun = aiJobKindNoun(kind)\n return noun === 'AI job' ? noun : noun.charAt(0).toUpperCase() + noun.slice(1)\n}\n\ntype ListState =\n | { status: 'loading' }\n | { status: 'ready'; jobs: AiJobSummary[] }\n | { status: 'error'; message: string }\n\nconst LOAD_ERROR = 'This site’s AI jobs could not be loaded. Try again in a moment.'\n\nasync function readSiteJobs(\n user: MaybeTokenSource,\n orgId: string,\n hostId: string,\n signal: AbortSignal,\n): Promise<ListState> {\n try {\n const response = await authorizedFetch(\n user,\n `/api/ai/jobs?orgId=${encodeURIComponent(orgId)}&hostId=${encodeURIComponent(hostId)}&limit=${AI_JOBS_PAGE_LIMIT}`,\n { signal },\n )\n const payload = (await response.json().catch(() => null)) as { jobs?: AiJobSummary[]; error?: string } | null\n if (!response.ok) return { status: 'error', message: String(payload?.error ?? LOAD_ERROR) }\n const jobs = payload?.jobs ?? []\n // A job the top-bar indicator still counts that has settled since leaves it.\n for (const job of jobs) publishAiJob(job)\n return { status: 'ready', jobs }\n } catch {\n return { status: 'error', message: LOAD_ERROR }\n }\n}\n\nexport function AiJobsListPage({ hostId, basePath }: ConsolePluginPageProps) {\n const { data: user } = useUser()\n const userRef = useRef<MaybeTokenSource>(user)\n userRef.current = user\n const site = useAiJobSite(hostId)\n const orgId = site.status === 'ready' ? site.orgId : null\n const [list, setList] = useState<ListState>({ status: 'loading' })\n const [attempt, setAttempt] = useState(0)\n const uid = user?.uid ?? null\n\n useEffect(() => {\n if (!orgId || !hostId || !uid) return undefined\n const controller = new AbortController()\n let settled = false\n setList({ status: 'loading' })\n const timer = setTimeout(() => {\n if (settled) return\n settled = true\n controller.abort()\n setList({ status: 'error', message: LOAD_ERROR })\n }, AI_JOBS_LIST_TIMEOUT_MS)\n void readSiteJobs(userRef.current, orgId, hostId, controller.signal).then((next) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n setList(next)\n })\n return () => {\n settled = true\n clearTimeout(timer)\n controller.abort()\n }\n }, [orgId, hostId, uid, attempt])\n\n const retry = useCallback(() => setAttempt((count) => count + 1), [])\n\n const siteName = site.status === 'ready' ? site.name : ''\n const state: ListState =\n site.status === 'error'\n ? { status: 'error', message: 'This site could not be read. Check your connection and reload the page.' }\n : list\n\n return (\n <Container maxWidth=\"md\" sx={{ py: { xs: 3, sm: 5 }, px: { xs: 2, sm: 3 } }}>\n <Stack spacing={3}>\n <Stack spacing={1}>\n {siteName ? (\n <Typography variant=\"overline\" color=\"text.secondary\">\n {siteName}\n </Typography>\n ) : null}\n <Typography variant=\"h4\" component=\"h1\">\n {'AI jobs'}\n </Typography>\n <Typography variant=\"body1\" color=\"text.secondary\">\n {`What ${PLATFORM_BRAND_NAME} AI has done and is doing on this site, newest first.`}\n </Typography>\n </Stack>\n {state.status === 'loading' ? (\n <Stack sx={{ alignItems: 'center', py: 4 }}>\n <CircularProgress aria-label=\"Loading AI jobs\" />\n </Stack>\n ) : state.status === 'error' ? (\n <Alert\n severity=\"warning\"\n action={\n site.status === 'error' ? undefined : (\n <Button color=\"inherit\" size=\"small\" onClick={retry}>\n {'Try again'}\n </Button>\n )\n }\n >\n {state.message}\n </Alert>\n ) : state.jobs.length === 0 ? (\n <Card variant=\"outlined\" sx={{ borderRadius: 2, p: { xs: 2, sm: 3 } }}>\n <Stack spacing={1}>\n <Typography variant=\"h6\" component=\"h2\">\n {'No AI jobs on this site yet'}\n </Typography>\n <Typography variant=\"body1\" color=\"text.secondary\">\n {`An AI job is ${PLATFORM_BRAND_NAME} AI building something for this site from a short ` +\n 'description you write: a page, a page template, a layout, a form or a reusable component. ' +\n 'Each one gets a page here that shows its progress and what it built.'}\n </Typography>\n <Typography variant=\"body1\" color=\"text.secondary\">\n {'To start one, choose Create with AI on this site’s Pages, Templates, Layouts, Forms or ' +\n 'Components page. A new site can also be built with AI when it is created.'}\n </Typography>\n </Stack>\n </Card>\n ) : (\n <Card variant=\"outlined\" sx={{ borderRadius: 2 }}>\n <List disablePadding aria-label=\"AI jobs\">\n {state.jobs.map((job, index) => {\n const phase = aiJobPhase(job)\n const credits = aiJobNetCredits(job)\n const refund = aiJobRefundCopy(job)\n const brief =\n job.brief.length > BRIEF_PREVIEW_CHARS ? `${job.brief.slice(0, BRIEF_PREVIEW_CHARS)}…` : job.brief\n const created = new Date(job.createdAt)\n return (\n <ListItemButton\n key={job.id}\n component={AppLink}\n href={`${basePath ?? ''}/${encodeURIComponent(job.id)}`}\n divider={index < state.jobs.length - 1}\n sx={{ display: 'block', py: 1.5 }}\n >\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 0.5 }}>\n <Chip size=\"small\" variant=\"outlined\" label={aiJobKindLabel(job.kind)} />\n <Chip size=\"small\" color={PHASE_COLOR[phase]} label={AI_JOB_PHASE_LABELS[phase]} />\n <Box sx={{ flexGrow: 1 }} />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {credits > 0 ? `${credits.toLocaleString('en-US')} credits · ` : ''}\n {Number.isNaN(created.getTime()) ? null : (\n <time dateTime={job.createdAt}>{WHEN.format(created)}</time>\n )}\n </Typography>\n </Stack>\n <Typography variant=\"body2\" sx={{ mt: 0.75, wordBreak: 'break-word' }}>\n {brief}\n </Typography>\n {/* What became of its credits, from the job's recorded give-back (AGL-3596). */}\n {refund ? (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 0.5 }}>\n {refund}\n </Typography>\n ) : null}\n </ListItemButton>\n )\n })}\n </List>\n </Card>\n )}\n {state.status === 'ready' && state.jobs.length >= AI_JOBS_PAGE_LIMIT ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Showing the ${AI_JOBS_PAGE_LIMIT} most recent jobs.`}\n </Typography>\n ) : null}\n </Stack>\n </Container>\n )\n}\n\nexport default AiJobsListPage\n"],"names":["PLATFORM_BRAND_NAME","aiJobRefundCopy","AppLink","authorizedFetch","useUser","Alert","Box","Button","Card","Chip","CircularProgress","Container","List","ListItemButton","Stack","Typography","useCallback","useEffect","useRef","useState","AI_JOB_PHASE_LABELS","aiJobKindNoun","aiJobPhase","publishAiJob","useAiJobSite","AI_JOBS_PAGE_LIMIT","AI_JOBS_LIST_TIMEOUT_MS","BRIEF_PREVIEW_CHARS","PHASE_COLOR","queued","planning","building","attention","done","failed","canceled","WHEN","Intl","DateTimeFormat","dateStyle","timeStyle","aiJobNetCredits","job","Math","max","creditsSpent","refundedCredits","aiJobKindLabel","kind","noun","charAt","toUpperCase","slice","LOAD_ERROR","readSiteJobs","user","orgId","hostId","signal","response","encodeURIComponent","payload","json","catch","ok","status","message","String","error","jobs","AiJobsListPage","basePath","data","userRef","current","site","list","setList","attempt","setAttempt","uid","undefined","controller","AbortController","settled","timer","setTimeout","abort","then","next","clearTimeout","retry","count","siteName","name","state","maxWidth","sx","py","xs","sm","px","spacing","variant","color","component","alignItems","aria-label","severity","action","size","onClick","length","borderRadius","p","disablePadding","map","index","phase","credits","refund","brief","created","Date","createdAt","href","id","divider","display","direction","flexWrap","rowGap","label","flexGrow","toLocaleString","Number","isNaN","getTime","time","dateTime","format","mt","wordBreak"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-jobs-list-page.component.tsx"],"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'use client'\n\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn'\nimport type { ConsolePluginPageProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { aiJobRefundCopy } from '../model/ai-job-failure-copy'\nimport { AppLink } from '@aglyn/shared-ui-jsx'\nimport { authorizedFetch, type MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Box,\n Button,\n Card,\n Chip,\n CircularProgress,\n Container,\n List,\n ListItemButton,\n Stack,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { AI_JOB_PHASE_LABELS, aiJobKindNoun, aiJobPhase, type AiJobPhase } from '../model/ai-job-activity'\nimport type { AiJobSummary } from '../model/ai-jobs.types'\nimport { publishAiJob } from './ai-jobs-store'\nimport { useAiJobSite } from './ai-job-site'\n\n/**\n * A site's AI jobs (AGL-3596), at `/ai-jobs` under the site: every job that\n * worked on this site, newest first, each linking to its own page.\n *\n * Read through the jobs route, never Firestore, so the membership, flag and\n * plan rungs that answer every other jobs read answer this one. The site is\n * a query on the route (`hostId`), not a filter of the workspace's list.\n *\n * Every wait ends: the site's read and the list's each settle on content,\n * the empty state, or a sentence with Try again.\n */\n\n/** The most jobs the page lists; the newest, which is what a person comes for. */\nexport const AI_JOBS_PAGE_LIMIT = 50\n\n/** How long the list may take before the page says it could not be loaded. */\nexport const AI_JOBS_LIST_TIMEOUT_MS = 20_000\n\n/** How much of a brief a row shows. */\nconst BRIEF_PREVIEW_CHARS = 120\n\n/** A row's chip, colored as the AI jobs drawer colors it. */\nconst PHASE_COLOR: Record<AiJobPhase, 'default' | 'primary' | 'success' | 'error' | 'warning'> = {\n queued: 'default',\n planning: 'primary',\n 'plan-ready': 'warning',\n building: 'primary',\n attention: 'warning',\n done: 'success',\n failed: 'error',\n canceled: 'default',\n}\n\nconst WHEN = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium', timeStyle: 'short' })\n\n/** What a job cost the person: what it spent, less what we refunded. */\nexport function aiJobNetCredits(job: Pick<AiJobSummary, 'creditsSpent' | 'refundedCredits'>): number {\n return Math.max(0, (job.creditsSpent ?? 0) - (job.refundedCredits ?? 0))\n}\n\n/** The kind as a row names it: `Site`, `Page`, `AI job`. */\nexport function aiJobKindLabel(kind: AiJobSummary['kind']): string {\n const noun = aiJobKindNoun(kind)\n return noun === 'AI job' ? noun : noun.charAt(0).toUpperCase() + noun.slice(1)\n}\n\ntype ListState =\n | { status: 'loading' }\n | { status: 'ready'; jobs: AiJobSummary[] }\n | { status: 'error'; message: string }\n\nconst LOAD_ERROR = 'This site’s AI jobs could not be loaded. Try again in a moment.'\n\nasync function readSiteJobs(\n user: MaybeTokenSource,\n orgId: string,\n hostId: string,\n signal: AbortSignal,\n): Promise<ListState> {\n try {\n const response = await authorizedFetch(\n user,\n `/api/ai/jobs?orgId=${encodeURIComponent(orgId)}&hostId=${encodeURIComponent(hostId)}&limit=${AI_JOBS_PAGE_LIMIT}`,\n { signal },\n )\n const payload = (await response.json().catch(() => null)) as { jobs?: AiJobSummary[]; error?: string } | null\n if (!response.ok) return { status: 'error', message: String(payload?.error ?? LOAD_ERROR) }\n const jobs = payload?.jobs ?? []\n // A job the top-bar indicator still counts that has settled since leaves it.\n for (const job of jobs) publishAiJob(job)\n return { status: 'ready', jobs }\n } catch {\n return { status: 'error', message: LOAD_ERROR }\n }\n}\n\nexport function AiJobsListPage({ hostId, basePath }: ConsolePluginPageProps) {\n const { data: user } = useUser()\n const userRef = useRef<MaybeTokenSource>(user)\n userRef.current = user\n const site = useAiJobSite(hostId)\n const orgId = site.status === 'ready' ? site.orgId : null\n const [list, setList] = useState<ListState>({ status: 'loading' })\n const [attempt, setAttempt] = useState(0)\n const uid = user?.uid ?? null\n\n useEffect(() => {\n if (!orgId || !hostId || !uid) return undefined\n const controller = new AbortController()\n let settled = false\n setList({ status: 'loading' })\n const timer = setTimeout(() => {\n if (settled) return\n settled = true\n controller.abort()\n setList({ status: 'error', message: LOAD_ERROR })\n }, AI_JOBS_LIST_TIMEOUT_MS)\n void readSiteJobs(userRef.current, orgId, hostId, controller.signal).then((next) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n setList(next)\n })\n return () => {\n settled = true\n clearTimeout(timer)\n controller.abort()\n }\n }, [orgId, hostId, uid, attempt])\n\n const retry = useCallback(() => setAttempt((count) => count + 1), [])\n\n const siteName = site.status === 'ready' ? site.name : ''\n const state: ListState =\n site.status === 'error'\n ? { status: 'error', message: 'This site could not be read. Check your connection and reload the page.' }\n : list\n\n return (\n <Container maxWidth=\"md\" sx={{ py: { xs: 3, sm: 5 }, px: { xs: 2, sm: 3 } }}>\n <Stack spacing={3}>\n <Stack spacing={1}>\n {siteName ? (\n <Typography variant=\"overline\" color=\"text.secondary\">\n {siteName}\n </Typography>\n ) : null}\n <Typography variant=\"h4\" component=\"h1\">\n {'AI jobs'}\n </Typography>\n <Typography variant=\"body1\" color=\"text.secondary\">\n {`What ${PLATFORM_BRAND_NAME} AI has done and is doing on this site, newest first.`}\n </Typography>\n </Stack>\n {state.status === 'loading' ? (\n <Stack sx={{ alignItems: 'center', py: 4 }}>\n <CircularProgress aria-label=\"Loading AI jobs\" />\n </Stack>\n ) : state.status === 'error' ? (\n <Alert\n severity=\"warning\"\n action={\n site.status === 'error' ? undefined : (\n <Button color=\"inherit\" size=\"small\" onClick={retry}>\n {'Try again'}\n </Button>\n )\n }\n >\n {state.message}\n </Alert>\n ) : state.jobs.length === 0 ? (\n <Card variant=\"outlined\" sx={{ borderRadius: 2, p: { xs: 2, sm: 3 } }}>\n <Stack spacing={1}>\n <Typography variant=\"h6\" component=\"h2\">\n {'No AI jobs on this site yet'}\n </Typography>\n <Typography variant=\"body1\" color=\"text.secondary\">\n {`An AI job is ${PLATFORM_BRAND_NAME} AI building something for this site from a short ` +\n 'description you write: a page, a page template, a layout, a form or a reusable component. ' +\n 'Each one gets a page here that shows its progress and what it built.'}\n </Typography>\n <Typography variant=\"body1\" color=\"text.secondary\">\n {'To start one, choose Create with AI on this site’s Pages, Templates, Layouts, Forms or ' +\n 'Components page. A new site can also be built with AI when it is created.'}\n </Typography>\n </Stack>\n </Card>\n ) : (\n <Card variant=\"outlined\" sx={{ borderRadius: 2 }}>\n <List disablePadding aria-label=\"AI jobs\">\n {state.jobs.map((job, index) => {\n const phase = aiJobPhase(job)\n const credits = aiJobNetCredits(job)\n const refund = aiJobRefundCopy(job)\n const brief =\n job.brief.length > BRIEF_PREVIEW_CHARS ? `${job.brief.slice(0, BRIEF_PREVIEW_CHARS)}…` : job.brief\n const created = new Date(job.createdAt)\n return (\n <ListItemButton\n key={job.id}\n component={AppLink}\n href={`${basePath ?? ''}/${encodeURIComponent(job.id)}`}\n divider={index < state.jobs.length - 1}\n sx={{ display: 'block', py: 1.5 }}\n >\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 0.5 }}>\n <Chip size=\"small\" variant=\"outlined\" label={aiJobKindLabel(job.kind)} />\n <Chip size=\"small\" color={PHASE_COLOR[phase]} label={AI_JOB_PHASE_LABELS[phase]} />\n <Box sx={{ flexGrow: 1 }} />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {credits > 0 ? `${credits.toLocaleString('en-US')} credits · ` : ''}\n {Number.isNaN(created.getTime()) ? null : (\n <time dateTime={job.createdAt}>{WHEN.format(created)}</time>\n )}\n </Typography>\n </Stack>\n <Typography variant=\"body2\" sx={{ mt: 0.75, wordBreak: 'break-word' }}>\n {brief}\n </Typography>\n {/* What became of its credits, from the job's recorded give-back (AGL-3596). */}\n {refund ? (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 0.5 }}>\n {refund}\n </Typography>\n ) : null}\n </ListItemButton>\n )\n })}\n </List>\n </Card>\n )}\n {state.status === 'ready' && state.jobs.length >= AI_JOBS_PAGE_LIMIT ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Showing the ${AI_JOBS_PAGE_LIMIT} most recent jobs.`}\n </Typography>\n ) : null}\n </Stack>\n </Container>\n )\n}\n\nexport default AiJobsListPage\n"],"names":["PLATFORM_BRAND_NAME","aiJobRefundCopy","AppLink","authorizedFetch","useUser","Alert","Box","Button","Card","Chip","CircularProgress","Container","List","ListItemButton","Stack","Typography","useCallback","useEffect","useRef","useState","AI_JOB_PHASE_LABELS","aiJobKindNoun","aiJobPhase","publishAiJob","useAiJobSite","AI_JOBS_PAGE_LIMIT","AI_JOBS_LIST_TIMEOUT_MS","BRIEF_PREVIEW_CHARS","PHASE_COLOR","queued","planning","building","attention","done","failed","canceled","WHEN","Intl","DateTimeFormat","dateStyle","timeStyle","aiJobNetCredits","job","Math","max","creditsSpent","refundedCredits","aiJobKindLabel","kind","noun","charAt","toUpperCase","slice","LOAD_ERROR","readSiteJobs","user","orgId","hostId","signal","response","encodeURIComponent","payload","json","catch","ok","status","message","String","error","jobs","AiJobsListPage","basePath","data","userRef","current","site","list","setList","attempt","setAttempt","uid","undefined","controller","AbortController","settled","timer","setTimeout","abort","then","next","clearTimeout","retry","count","siteName","name","state","maxWidth","sx","py","xs","sm","px","spacing","variant","color","component","alignItems","aria-label","severity","action","size","onClick","length","borderRadius","p","disablePadding","map","index","phase","credits","refund","brief","created","Date","createdAt","href","id","divider","display","direction","useFlexGap","flexWrap","rowGap","label","flexGrow","toLocaleString","Number","isNaN","getTime","time","dateTime","format","mt","wordBreak"],"mappings":"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"}
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  import type { ConsoleMediaLibraryZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins';
18
- import { type AiImageMode, type AiSvgStyle } from '../providers/image-contract';
18
+ import { type AiImageMode, type AiImageSize } from '../providers/image-contract';
19
19
  /**
20
20
  * "Create with AI" in Media (AGL-3602): pictures from a description, added to
21
21
  * the library that is open, through the `mediaLibrary` zone beside Upload
@@ -24,9 +24,9 @@ import { type AiImageMode, type AiSvgStyle } from '../providers/image-contract';
24
24
  * It draws from the shell's gates alone — the plan, the member's
25
25
  * `ai.generate`, the plugin being on for the workspace and the site — and asks
26
26
  * no server anything until someone creates a picture: the door decides then,
27
- * and the dialog says what it decided. Two modes: an Illustration (an SVG the
28
- * text AI draws, wherever text AI runs) and, where the deployment makes them,
29
- * a Photo.
27
+ * and the dialog says what it decided. One Kind menu, in sections: Vector
28
+ * kinds (an SVG the text AI draws, wherever text AI runs) and, where the
29
+ * deployment makes them, Photo, Art and Design kinds from the image provider.
30
30
  */
31
31
  /** Where the shell sends a reader to buy what the plan lacks (AGL-3601). */
32
32
  export interface AiMediaUpgrade {
@@ -41,10 +41,23 @@ export interface AiMediaCreateButtonProps extends ConsoleMediaLibraryZoneProps {
41
41
  entitled?: boolean;
42
42
  upgrade?: AiMediaUpgrade;
43
43
  }
44
- /** The words each kind of illustration is offered in. */
45
- export declare const AI_SVG_STYLE_LABELS: Record<AiSvgStyle, string>;
44
+ /**
45
+ * What a picture from the image provider may be declined for, as the code
46
+ * and the provider decide it: Google's filters, and its policies on real
47
+ * people and brands. A declined picture is never charged.
48
+ */
49
+ export declare const AI_MEDIA_RASTER_SAFETY_NOTE = "Pictures of real, identifiable people, celebrities or brands may be declined by the image service. A declined picture is not charged.";
50
+ /** The line under the description for a kind whose colors are described rather than picked. */
51
+ export declare const AI_MEDIA_DESCRIBE_COLORS_NOTE = "To steer the colors, name them in the description, such as navy and coral.";
52
+ /**
53
+ * The size the door will make a picture at for this workspace (AGL-3602):
54
+ * 512 px on the Free plan, 1K on a paid one. The door decides from the plan
55
+ * itself; this reads the same plan so the estimate matches, and reads 1K —
56
+ * the dearer estimate — until the workspace has arrived.
57
+ */
58
+ export declare function useAiMediaImageSize(orgId: string): AiImageSize;
46
59
  /** The sentence the estimate reads as, before anything is spent. */
47
- export declare function aiMediaCreditEstimate(mode: AiImageMode, count: number): string;
60
+ export declare function aiMediaCreditEstimate(mode: AiImageMode, count: number, size?: AiImageSize): string;
48
61
  export declare function AiMediaCreateButton(props: AiMediaCreateButtonProps): import("react").JSX.Element;
49
62
  export interface AiMediaCreateDialogProps extends Pick<ConsoleMediaLibraryZoneProps, 'hostId' | 'library' | 'folderId' | 'onCreated'> {
50
63
  open: boolean;