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

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 (139) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-assist-provider-on-host.component.d.ts +66 -0
  3. package/src/lib/components/ai-assist-provider-on-host.component.js +78 -0
  4. package/src/lib/components/ai-assist-provider-on-host.component.js.map +1 -0
  5. package/src/lib/components/ai-assist-provider.component.js +9 -10
  6. package/src/lib/components/ai-assist-provider.component.js.map +1 -1
  7. package/src/lib/components/ai-job-plan.component.js +3 -1
  8. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  9. package/src/lib/components/ai-permissions-on-host.component.d.ts +3 -46
  10. package/src/lib/components/ai-permissions-on-host.component.js +5 -53
  11. package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
  12. package/src/lib/components/ai-site-build-page.component.js +20 -8
  13. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  14. package/src/lib/jobs/ai-build-unit-outcome.js +4 -2
  15. package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
  16. package/src/lib/jobs/ai-job-auto-confirm.d.ts +8 -0
  17. package/src/lib/jobs/ai-job-auto-confirm.js +30 -6
  18. package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -1
  19. package/src/lib/jobs/ai-job-build-step.js +11 -0
  20. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  21. package/src/lib/jobs/ai-job-generation.d.ts +8 -0
  22. package/src/lib/jobs/ai-job-generation.js +13 -2
  23. package/src/lib/jobs/ai-job-generation.js.map +1 -1
  24. package/src/lib/jobs/ai-job-insight-step.js +8 -1
  25. package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
  26. package/src/lib/jobs/ai-job-layout-language.d.ts +73 -0
  27. package/src/lib/jobs/ai-job-layout-language.js +194 -0
  28. package/src/lib/jobs/ai-job-layout-language.js.map +1 -0
  29. package/src/lib/jobs/ai-job-layout-site-pages.d.ts +13 -2
  30. package/src/lib/jobs/ai-job-layout-site-pages.js +18 -2
  31. package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
  32. package/src/lib/jobs/ai-job-layout-step.d.ts +5 -0
  33. package/src/lib/jobs/ai-job-layout-step.js +105 -5
  34. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  35. package/src/lib/jobs/ai-job-page-language.d.ts +149 -0
  36. package/src/lib/jobs/ai-job-page-language.js +355 -0
  37. package/src/lib/jobs/ai-job-page-language.js.map +1 -0
  38. package/src/lib/jobs/ai-job-page-sections.d.ts +37 -1
  39. package/src/lib/jobs/ai-job-page-sections.js +93 -11
  40. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  41. package/src/lib/jobs/ai-job-page-step.d.ts +4 -4
  42. package/src/lib/jobs/ai-job-page-step.js +105 -14
  43. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  44. package/src/lib/jobs/ai-job-plan-step.d.ts +1 -1
  45. package/src/lib/jobs/ai-job-plan-step.js +8 -2
  46. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  47. package/src/lib/jobs/ai-job-site-step.d.ts +4 -0
  48. package/src/lib/jobs/ai-job-site-step.js +20 -3
  49. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  50. package/src/lib/jobs/ai-job-workflow-step.js +2 -2
  51. package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
  52. package/src/lib/jobs/ai-jobs.d.ts +1 -1
  53. package/src/lib/jobs/ai-jobs.js +17 -8
  54. package/src/lib/jobs/ai-jobs.js.map +1 -1
  55. package/src/lib/jobs/ai-layout-full-height.d.ts +40 -0
  56. package/src/lib/jobs/ai-layout-full-height.js +82 -0
  57. package/src/lib/jobs/ai-layout-full-height.js.map +1 -0
  58. package/src/lib/jobs/ai-layout-site-facts.d.ts +54 -0
  59. package/src/lib/jobs/ai-layout-site-facts.js +153 -0
  60. package/src/lib/jobs/ai-layout-site-facts.js.map +1 -0
  61. package/src/lib/layout-language/ai-layout-compiler.d.ts +145 -0
  62. package/src/lib/layout-language/ai-layout-compiler.js +1148 -0
  63. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -0
  64. package/src/lib/layout-language/ai-layout-copy.d.ts +73 -0
  65. package/src/lib/layout-language/ai-layout-copy.js +137 -0
  66. package/src/lib/layout-language/ai-layout-copy.js.map +1 -0
  67. package/src/lib/layout-language/ai-layout-frame.d.ts +52 -0
  68. package/src/lib/layout-language/ai-layout-frame.js +457 -0
  69. package/src/lib/layout-language/ai-layout-frame.js.map +1 -0
  70. package/src/lib/layout-language/ai-layout-gaps.d.ts +63 -0
  71. package/src/lib/layout-language/ai-layout-gaps.js +345 -0
  72. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -0
  73. package/src/lib/layout-language/ai-layout-language.d.ts +161 -0
  74. package/src/lib/layout-language/ai-layout-language.js +446 -0
  75. package/src/lib/layout-language/ai-layout-language.js.map +1 -0
  76. package/src/lib/layout-language/ai-layout-links.d.ts +80 -0
  77. package/src/lib/layout-language/ai-layout-links.js +109 -0
  78. package/src/lib/layout-language/ai-layout-links.js.map +1 -0
  79. package/src/lib/layout-language/ai-layout-store.d.ts +39 -0
  80. package/src/lib/layout-language/ai-layout-store.js +69 -0
  81. package/src/lib/layout-language/ai-layout-store.js.map +1 -0
  82. package/src/lib/layout-language/ai-layout-tree.d.ts +59 -0
  83. package/src/lib/layout-language/ai-layout-tree.js +84 -0
  84. package/src/lib/layout-language/ai-layout-tree.js.map +1 -0
  85. package/src/lib/lazy-widget.d.ts +45 -0
  86. package/src/lib/lazy-widget.js +53 -0
  87. package/src/lib/lazy-widget.js.map +1 -0
  88. package/src/lib/model/ai-build-job.js +2 -1
  89. package/src/lib/model/ai-build-job.js.map +1 -1
  90. package/src/lib/model/ai-build-progress.d.ts +20 -3
  91. package/src/lib/model/ai-build-progress.js +48 -6
  92. package/src/lib/model/ai-build-progress.js.map +1 -1
  93. package/src/lib/model/ai-host-publish-context.d.ts +51 -0
  94. package/src/lib/model/ai-host-publish-context.js +104 -0
  95. package/src/lib/model/ai-host-publish-context.js.map +1 -0
  96. package/src/lib/model/ai-job-activity.d.ts +22 -0
  97. package/src/lib/model/ai-job-activity.js +22 -1
  98. package/src/lib/model/ai-job-activity.js.map +1 -1
  99. package/src/lib/model/ai-job-failure-copy.d.ts +38 -1
  100. package/src/lib/model/ai-job-failure-copy.js +56 -1
  101. package/src/lib/model/ai-job-failure-copy.js.map +1 -1
  102. package/src/lib/model/ai-job-notice.d.ts +3 -1
  103. package/src/lib/model/ai-job-notice.js +31 -9
  104. package/src/lib/model/ai-job-notice.js.map +1 -1
  105. package/src/lib/model/ai-jobs.types.d.ts +19 -0
  106. package/src/lib/model/ai-jobs.types.js.map +1 -1
  107. package/src/lib/model/ai-site-build-progress.d.ts +5 -1
  108. package/src/lib/model/ai-site-build-progress.js +27 -4
  109. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  110. package/src/lib/model/ai-site-job.d.ts +8 -0
  111. package/src/lib/model/ai-site-job.js +9 -0
  112. package/src/lib/model/ai-site-job.js.map +1 -1
  113. package/src/lib/model/ai-site-start-seo.d.ts +1 -1
  114. package/src/lib/model/ai-site-start.d.ts +5 -2
  115. package/src/lib/model/ai-site-start.js +5 -2
  116. package/src/lib/model/ai-site-start.js.map +1 -1
  117. package/src/lib/plugin.js +79 -47
  118. package/src/lib/plugin.js.map +1 -1
  119. package/src/lib/providers/catalog.d.ts +1 -1
  120. package/src/lib/providers/catalog.js +3 -3
  121. package/src/lib/providers/catalog.js.map +1 -1
  122. package/src/lib/runtime/ai-doctrine-validators.d.ts +82 -0
  123. package/src/lib/runtime/ai-doctrine-validators.js +436 -4
  124. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  125. package/src/lib/runtime/ai-doctrine.js +12 -3
  126. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  127. package/src/lib/runtime/ai-icon-library.d.ts +257 -0
  128. package/src/lib/runtime/ai-icon-library.js +254 -0
  129. package/src/lib/runtime/ai-icon-library.js.map +1 -0
  130. package/src/lib/runtime/ai-node-tree.d.ts +9 -0
  131. package/src/lib/runtime/ai-node-tree.js +30 -4
  132. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  133. package/src/lib/runtime/ai-palette.d.ts +6 -0
  134. package/src/lib/runtime/ai-palette.generated.js +70 -0
  135. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  136. package/src/lib/runtime/ai-palette.js.map +1 -1
  137. package/src/lib/tools/ai-workflow-tool.d.ts +6 -2
  138. package/src/lib/tools/ai-workflow-tool.js +16 -14
  139. package/src/lib/tools/ai-workflow-tool.js.map +1 -1
@@ -17,65 +17,17 @@
17
17
  import { _ as _extends } from "@swc/helpers/_/_extends";
18
18
  import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
19
19
  import { jsx as _jsx } from "react/jsx-runtime";
20
- import { useMemo } from "react";
21
- import { AI_PLUGIN_ID } from "../constants.js";
22
- import { aiPermissionsOf } from "../model/ai-permissions.js";
23
- import { AiAssistProvider } from "./ai-assist-provider.component.js";
24
20
  import { AssistPanelComponent } from "./assist-panel.component.js";
21
+ import { useAiPermissionsOnHost } from "./ai-assist-provider-on-host.component.js";
22
+ // The provider half lives in its own module (AGL-3649), so the eager
23
+ // provider loads nothing of the dock's; it is re-exported here for the
24
+ // importers that knew it by this name.
25
+ export { AiAssistProviderOnHost, useAiPermissionsOnHost } from "./ai-assist-provider-on-host.component.js";
25
26
  const HELD = {
26
27
  loaded: false,
27
28
  use: false,
28
29
  generate: false
29
30
  };
30
- /**
31
- * The shell's answer read for the two AI keys. `undefined` while the shell
32
- * has handed nothing, which the provider reads as pending; one object per
33
- * verdict, so the callbacks keyed on it are not rebuilt on every render.
34
- */ export function useAiPermissionsOnHost(answer) {
35
- const present = answer !== undefined;
36
- const loaded = (answer == null ? void 0 : answer.loaded) === true;
37
- const verdict = aiPermissionsOf(loaded ? answer == null ? void 0 : answer.granted : null);
38
- const use = verdict['ai.use'];
39
- const generate = verdict['ai.generate'];
40
- return useMemo(()=>present ? {
41
- loaded,
42
- use,
43
- generate
44
- } : undefined, [
45
- present,
46
- loaded,
47
- use,
48
- generate
49
- ]);
50
- }
51
- /**
52
- * Every AI door closed, as a settled answer: what the provider holds on a site
53
- * that switched AI off. One object, so the callbacks keyed on it are stable.
54
- */ const OFF_FOR_SITE = {
55
- loaded: true,
56
- use: false,
57
- generate: false
58
- };
59
- /**
60
- * The besigner copy assistant's provider, holding on the shell's permission
61
- * answer.
62
- *
63
- * On a site with AI switched off (AGL-3028) it stays MOUNTED — it wraps every
64
- * console page, and a provider that came and went per site would remount the
65
- * whole tree beneath it — and opens nothing: a settled refusal publishes no
66
- * callback, so no control can reach its dialogs.
67
- */ export function AiAssistProviderOnHost(props) {
68
- const { permissionsOnHost, enabledPluginIds } = props, rest = _object_without_properties_loose(props, [
69
- "permissionsOnHost",
70
- "enabledPluginIds"
71
- ]);
72
- const aiPermissions = useAiPermissionsOnHost(permissionsOnHost);
73
- const offForSite = Boolean(enabledPluginIds) && !(enabledPluginIds == null ? void 0 : enabledPluginIds.includes(AI_PLUGIN_ID));
74
- return /*#__PURE__*/ _jsx(AiAssistProvider, _extends({}, rest, {
75
- aiPermissions: offForSite ? OFF_FOR_SITE : aiPermissions
76
- }));
77
- }
78
- AiAssistProviderOnHost.displayName = 'AiAssistProviderOnHost';
79
31
  /**
80
32
  * The assistant, drawn in the console dock, holding on the shell's
81
33
  * permission answer and asking the shell for its own release flags.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-permissions-on-host.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 type { ConsoleDockZoneProps } from '@aglyn/aglyn'\nimport { useMemo, type ComponentProps } from 'react'\nimport { AI_PLUGIN_ID } from '../constants'\nimport { aiPermissionsOf } from '../model/ai-permissions'\nimport { AiAssistProvider } from './ai-assist-provider.component'\nimport { AssistPanelComponent } from './assist-panel.component'\n\n/**\n * What the console shell hands a provider and the assistant dock (AGL-2984):\n * the reader's verdict for every catalog key a plugin declared, on the site\n * in view, with `loaded` false until every read behind it has answered.\n */\nexport interface ShellPermissionsOnHost {\n loaded: boolean\n granted: Readonly<Record<string, boolean>>\n}\n\n/** The AI half of that answer, in the shape the AI surfaces hold on. */\nexport interface AiPermissionsAnswer {\n loaded: boolean\n use: boolean\n generate: boolean\n}\n\nconst HELD: AiPermissionsAnswer = { loaded: false, use: false, generate: false }\n\n/**\n * The shell's answer read for the two AI keys. `undefined` while the shell\n * has handed nothing, which the provider reads as pending; one object per\n * verdict, so the callbacks keyed on it are not rebuilt on every render.\n */\nexport function useAiPermissionsOnHost(\n answer: ShellPermissionsOnHost | undefined,\n): AiPermissionsAnswer | undefined {\n const present = answer !== undefined\n const loaded = answer?.loaded === true\n const verdict = aiPermissionsOf(loaded ? answer?.granted : null)\n const use = verdict['ai.use']\n const generate = verdict['ai.generate']\n return useMemo(\n () => (present ? { loaded, use, generate } : undefined),\n [present, loaded, use, generate],\n )\n}\n\ntype ProviderProps = Omit<ComponentProps<typeof AiAssistProvider>, 'aiPermissions'> & {\n permissionsOnHost?: ShellPermissionsOnHost\n /**\n * The plugins that run on the site in view, as the shell resolved them\n * (AGL-3028). Absent where the shell hands none, which reads as no site\n * narrowing at all.\n */\n enabledPluginIds?: readonly string[]\n}\n\n/**\n * Every AI door closed, as a settled answer: what the provider holds on a site\n * that switched AI off. One object, so the callbacks keyed on it are stable.\n */\nconst OFF_FOR_SITE: AiPermissionsAnswer = { loaded: true, use: false, generate: false }\n\n/**\n * The besigner copy assistant's provider, holding on the shell's permission\n * answer.\n *\n * On a site with AI switched off (AGL-3028) it stays MOUNTED — it wraps every\n * console page, and a provider that came and went per site would remount the\n * whole tree beneath it — and opens nothing: a settled refusal publishes no\n * callback, so no control can reach its dialogs.\n */\nexport function AiAssistProviderOnHost(props: ProviderProps) {\n const { permissionsOnHost, enabledPluginIds, ...rest } = props\n const aiPermissions = useAiPermissionsOnHost(permissionsOnHost)\n const offForSite = Boolean(enabledPluginIds) && !enabledPluginIds?.includes(AI_PLUGIN_ID)\n return (\n <AiAssistProvider {...rest} aiPermissions={offForSite ? OFF_FOR_SITE : aiPermissions} />\n )\n}\nAiAssistProviderOnHost.displayName = 'AiAssistProviderOnHost'\n\ntype PanelProps = Omit<\n ComponentProps<typeof AssistPanelComponent>,\n 'aiPermissions' | 'assistVisible' | 'assistStaffPreview' | 'generativeVisible'\n> & {\n permissionsOnHost?: ShellPermissionsOnHost\n /**\n * The shell's verdict for any release flag (the `consoleDock` zone's\n * `releaseVerdict`): the assistant asks for its own two, so the shell names\n * no AI flag.\n */\n releaseVerdict: ConsoleDockZoneProps['releaseVerdict']\n}\n\n/**\n * The assistant, drawn in the console dock, holding on the shell's\n * permission answer and asking the shell for its own release flags.\n */\nexport function AssistPanelOnHost(props: PanelProps) {\n const { permissionsOnHost, releaseVerdict, ...rest } = props\n const aiPermissions = useAiPermissionsOnHost(permissionsOnHost) ?? HELD\n const assist = releaseVerdict('release_assist')\n const generative = releaseVerdict('release_ai_generative')\n return (\n <AssistPanelComponent\n {...rest}\n assistVisible={assist.visible}\n assistStaffPreview={assist.staffPreview}\n generativeVisible={generative.visible}\n aiPermissions={aiPermissions}\n />\n )\n}\nAssistPanelOnHost.displayName = 'AssistPanelOnHost'\n"],"names":["useMemo","AI_PLUGIN_ID","aiPermissionsOf","AiAssistProvider","AssistPanelComponent","HELD","loaded","use","generate","useAiPermissionsOnHost","answer","present","undefined","verdict","granted","OFF_FOR_SITE","AiAssistProviderOnHost","props","permissionsOnHost","enabledPluginIds","rest","aiPermissions","offForSite","Boolean","includes","displayName","AssistPanelOnHost","releaseVerdict","assist","generative","assistVisible","visible","assistStaffPreview","staffPreview","generativeVisible"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;;AAGA,SAASA,OAAO,QAA6B,QAAO;AACpD,SAASC,YAAY,QAAQ,kBAAc;AAC3C,SAASC,eAAe,QAAQ,6BAAyB;AACzD,SAASC,gBAAgB,QAAQ,oCAAgC;AACjE,SAASC,oBAAoB,QAAQ,8BAA0B;AAmB/D,MAAMC,OAA4B;IAAEC,QAAQ;IAAOC,KAAK;IAAOC,UAAU;AAAM;AAE/E;;;;CAIC,GACD,OAAO,SAASC,uBACdC,MAA0C;IAE1C,MAAMC,UAAUD,WAAWE;IAC3B,MAAMN,SAASI,CAAAA,0BAAAA,OAAQJ,MAAM,MAAK;IAClC,MAAMO,UAAUX,gBAAgBI,SAASI,0BAAAA,OAAQI,OAAO,GAAG;IAC3D,MAAMP,MAAMM,OAAO,CAAC,SAAS;IAC7B,MAAML,WAAWK,OAAO,CAAC,cAAc;IACvC,OAAOb,QACL,IAAOW,UAAU;YAAEL;YAAQC;YAAKC;QAAS,IAAII,WAC7C;QAACD;QAASL;QAAQC;QAAKC;KAAS;AAEpC;AAYA;;;CAGC,GACD,MAAMO,eAAoC;IAAET,QAAQ;IAAMC,KAAK;IAAOC,UAAU;AAAM;AAEtF;;;;;;;;CAQC,GACD,OAAO,SAASQ,uBAAuBC,KAAoB;IACzD,MAAM,EAAEC,iBAAiB,EAAEC,gBAAgB,EAAW,GAAGF,OAATG,wCAASH;;;;IACzD,MAAMI,gBAAgBZ,uBAAuBS;IAC7C,MAAMI,aAAaC,QAAQJ,qBAAqB,EAACA,oCAAAA,iBAAkBK,QAAQ,CAACvB;IAC5E,qBACE,KAACE,+BAAqBiB;QAAMC,eAAeC,aAAaP,eAAeM;;AAE3E;AACAL,uBAAuBS,WAAW,GAAG;AAerC;;;CAGC,GACD,OAAO,SAASC,kBAAkBT,KAAiB;QAE3BR;IADtB,MAAM,EAAES,iBAAiB,EAAES,cAAc,EAAW,GAAGV,OAATG,wCAASH;;;;IACvD,MAAMI,iBAAgBZ,0BAAAA,uBAAuBS,8BAAvBT,0BAA6CJ;IACnE,MAAMuB,SAASD,eAAe;IAC9B,MAAME,aAAaF,eAAe;IAClC,qBACE,KAACvB,mCACKgB;QACJU,eAAeF,OAAOG,OAAO;QAC7BC,oBAAoBJ,OAAOK,YAAY;QACvCC,mBAAmBL,WAAWE,OAAO;QACrCV,eAAeA;;AAGrB;AACAK,kBAAkBD,WAAW,GAAG"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-permissions-on-host.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 type { ConsoleDockZoneProps } from '@aglyn/aglyn'\nimport type { ComponentProps } from 'react'\nimport { AssistPanelComponent } from './assist-panel.component'\nimport {\n useAiPermissionsOnHost,\n type AiPermissionsAnswer,\n type ShellPermissionsOnHost,\n} from './ai-assist-provider-on-host.component'\n\n// The provider half lives in its own module (AGL-3649), so the eager\n// provider loads nothing of the dock's; it is re-exported here for the\n// importers that knew it by this name.\nexport {\n AiAssistProviderOnHost,\n useAiPermissionsOnHost,\n type AiPermissionsAnswer,\n type ShellPermissionsOnHost,\n} from './ai-assist-provider-on-host.component'\n\nconst HELD: AiPermissionsAnswer = { loaded: false, use: false, generate: false }\n\ntype PanelProps = Omit<\n ComponentProps<typeof AssistPanelComponent>,\n 'aiPermissions' | 'assistVisible' | 'assistStaffPreview' | 'generativeVisible'\n> & {\n permissionsOnHost?: ShellPermissionsOnHost\n /**\n * The shell's verdict for any release flag (the `consoleDock` zone's\n * `releaseVerdict`): the assistant asks for its own two, so the shell names\n * no AI flag.\n */\n releaseVerdict: ConsoleDockZoneProps['releaseVerdict']\n}\n\n/**\n * The assistant, drawn in the console dock, holding on the shell's\n * permission answer and asking the shell for its own release flags.\n */\nexport function AssistPanelOnHost(props: PanelProps) {\n const { permissionsOnHost, releaseVerdict, ...rest } = props\n const aiPermissions = useAiPermissionsOnHost(permissionsOnHost) ?? HELD\n const assist = releaseVerdict('release_assist')\n const generative = releaseVerdict('release_ai_generative')\n return (\n <AssistPanelComponent\n {...rest}\n assistVisible={assist.visible}\n assistStaffPreview={assist.staffPreview}\n generativeVisible={generative.visible}\n aiPermissions={aiPermissions}\n />\n )\n}\nAssistPanelOnHost.displayName = 'AssistPanelOnHost'\n"],"names":["AssistPanelComponent","useAiPermissionsOnHost","AiAssistProviderOnHost","HELD","loaded","use","generate","AssistPanelOnHost","props","permissionsOnHost","releaseVerdict","rest","aiPermissions","assist","generative","assistVisible","visible","assistStaffPreview","staffPreview","generativeVisible","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;;AAIA,SAASA,oBAAoB,QAAQ,8BAA0B;AAC/D,SACEC,sBAAsB,QAGjB,4CAAwC;AAE/C,qEAAqE;AACrE,uEAAuE;AACvE,uCAAuC;AACvC,SACEC,sBAAsB,EACtBD,sBAAsB,QAGjB,4CAAwC;AAE/C,MAAME,OAA4B;IAAEC,QAAQ;IAAOC,KAAK;IAAOC,UAAU;AAAM;AAe/E;;;CAGC,GACD,OAAO,SAASC,kBAAkBC,KAAiB;QAE3BP;IADtB,MAAM,EAAEQ,iBAAiB,EAAEC,cAAc,EAAW,GAAGF,OAATG,wCAASH;;;;IACvD,MAAMI,iBAAgBX,0BAAAA,uBAAuBQ,8BAAvBR,0BAA6CE;IACnE,MAAMU,SAASH,eAAe;IAC9B,MAAMI,aAAaJ,eAAe;IAClC,qBACE,KAACV,mCACKW;QACJI,eAAeF,OAAOG,OAAO;QAC7BC,oBAAoBJ,OAAOK,YAAY;QACvCC,mBAAmBL,WAAWE,OAAO;QACrCJ,eAAeA;;AAGrB;AACAL,kBAAkBa,WAAW,GAAG"}
@@ -14,11 +14,13 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */ 'use client';
17
+ import { _ as _extends } from "@swc/helpers/_/_extends";
17
18
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
19
  import { PLATFORM_BRAND_NAME } from "@aglyn/aglyn";
19
20
  import { PageHeaderRecord } from "@aglyn/aglyn/app-utils/page-header-record-context";
20
21
  import { mdiAlertCircle, mdiCheckCircle, mdiChevronDown, mdiClockOutline, mdiMinusCircleOutline } from "@aglyn/shared-data-mdi";
21
22
  import { AppLink, MdiIcon } from "@aglyn/shared-ui-jsx";
23
+ import { SITE_LIVE_NOTICE_BODY, SITE_LIVE_NOTICE_TITLE, SiteLiveNotice } from "@aglyn/shared-ui-jsx/components/site-live-notice.component";
22
24
  import { useUser } from "@aglyn/tenant-feature-instance";
23
25
  import { Accordion, AccordionDetails, AccordionSummary, Alert, Box, Button, Card, CircularProgress, Container, LinearProgress, Stack, Typography } from "@mui/material";
24
26
  import { useCallback, useEffect, useRef, useState } from "react";
@@ -114,7 +116,7 @@ const ROW_ICON = {
114
116
  failed: {
115
117
  path: mdiAlertCircle.path,
116
118
  color: 'error.main',
117
- label: 'Stopped'
119
+ label: 'Couldn’t be built'
118
120
  },
119
121
  skipped: {
120
122
  path: mdiMinusCircleOutline.path,
@@ -192,11 +194,11 @@ function Frame({ siteName, heading, lede, children }) {
192
194
  color: "text.secondary",
193
195
  children: siteName
194
196
  }) : null,
195
- /*#__PURE__*/ _jsx(Typography, {
197
+ heading ? /*#__PURE__*/ _jsx(Typography, {
196
198
  variant: "h4",
197
199
  component: "h1",
198
200
  children: heading
199
- }),
201
+ }) : null,
200
202
  lede ? /*#__PURE__*/ _jsx(Typography, {
201
203
  variant: "body1",
202
204
  color: "text.secondary",
@@ -345,12 +347,22 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
345
347
  // own answers, as a fresh job: the questions reopen filled in.
346
348
  const restartAnswers = ready.kind === 'site' && (phase === 'failed' || phase === 'canceled') ? aiSiteStartAnswersFromInputs(ready.siteInputs) : null;
347
349
  const refund = phase === 'failed' || phase === 'stopped' || phase === 'canceled' ? aiJobRefundCopy(ready) : null;
348
- return /*#__PURE__*/ _jsxs(Frame, {
349
- siteName: siteName,
350
+ // A whole site put live says so in the words every new site goes live in
351
+ // (AGL-3663); a partial build or a page left a draft keeps its own account.
352
+ const liveNotice = liveUrl !== null && !(sitePublish == null ? void 0 : sitePublish.drafts.length) && copy.heading === SITE_LIVE_NOTICE_TITLE && copy.lede === SITE_LIVE_NOTICE_BODY;
353
+ return /*#__PURE__*/ _jsxs(Frame, _extends({
354
+ siteName: siteName
355
+ }, liveNotice ? {} : {
350
356
  heading: copy.heading,
351
- lede: copy.lede,
357
+ lede: copy.lede
358
+ }, {
352
359
  children: [
353
360
  record,
361
+ liveNotice && /*#__PURE__*/ _jsx(SiteLiveNotice, {
362
+ liveUrl: liveUrl,
363
+ pagesHref: links.pages,
364
+ headingComponent: "h1"
365
+ }),
354
366
  notice && /*#__PURE__*/ _jsx(Alert, {
355
367
  severity: "warning",
356
368
  children: notice
@@ -526,7 +538,7 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
526
538
  children: 'Try again what failed'
527
539
  })
528
540
  }),
529
- phase === 'done' && /*#__PURE__*/ _jsxs(Stack, {
541
+ phase === 'done' && !liveNotice && /*#__PURE__*/ _jsxs(Stack, {
530
542
  direction: {
531
543
  xs: 'column',
532
544
  sm: 'row'
@@ -645,7 +657,7 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
645
657
  ]
646
658
  })
647
659
  ]
648
- });
660
+ }));
649
661
  }
650
662
  export default AiSiteBuildPage;
651
663
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-site-build-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\n'use client'\n\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn'\nimport { PageHeaderRecord } from '@aglyn/aglyn/app-utils/page-header-record-context'\nimport type { ConsolePluginPageProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport {\n mdiAlertCircle,\n mdiCheckCircle,\n mdiChevronDown,\n mdiClockOutline,\n mdiMinusCircleOutline,\n} from '@aglyn/shared-data-mdi'\nimport { AppLink, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport type { MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Accordion,\n AccordionDetails,\n AccordionSummary,\n Alert,\n Box,\n Button,\n Card,\n CircularProgress,\n Container,\n LinearProgress,\n Stack,\n Typography,\n} from '@mui/material'\nimport { type ReactNode, useCallback, useEffect, useRef, useState } from 'react'\nimport {\n aiJobPageCopy,\n aiSiteBuildCreditsLine,\n aiSiteBuildFraction,\n aiSiteBuildPhase,\n aiSiteBuildRows,\n type AiSiteBuildRowState,\n} from '../model/ai-site-build-progress'\nimport { aiJobRefundCopy, aiSiteStarterFallbackOffered } from '../model/ai-job-failure-copy'\nimport { aiSiteStartAnswersFromInputs } from '../model/ai-site-start'\nimport { AiSiteStartCard } from './ai-site-start-card.component'\nimport { AI_JOB_TERMINAL_STATUSES, type AiJobSummary } from '../model/ai-jobs.types'\nimport { followAiJobEvents } from './ai-job-events'\nimport { aiSiteBuildDoneLinks } from './ai-job-links'\nimport { AiJobPlan } from './ai-job-plan.component'\nimport { aiBuildCanRetry } from '../model/ai-build-progress'\nimport { resumeAiJobRequest } from './ai-job-requests'\nimport { useAiJobSite } from './ai-job-site'\nimport { AiSiteStarterFallback } from './ai-site-starter-fallback.component'\n\n/**\n * \"Building your site\" (AGL-3594): one job's page, at `/ai-jobs/{jobId}`\n * under its site — where the guided start sends a person the moment they\n * plan a site, and where a notification about that job opens.\n *\n * A guided site start confirms its own plan (`autoConfirm`), so there is no\n * approval to make here: the page says what is happening, step by step, live\n * from the job's events route; shows what is being built; and, when the job\n * ends, leads with the next thing to do — the live site and the pages to\n * edit, or, where it stopped, the plain sentence for why and what to do\n * about it. It is linkable and survives a reload, because it reads the job by\n * the id in its own address under the site's own route.\n *\n * Every wait ends (AGL-3596): a site that cannot be read, a job the route\n * does not know, a route that fails, and a first state that never arrives\n * each end on a sentence instead of a spinner.\n *\n * Mounted by `AiJobsPage`, the plugin's unlisted `/ai-jobs` route: no tab on\n * the site's strip, an address under it.\n */\n\n/** How long the job's first state may take before the page says it could not be loaded. */\nexport const AI_JOB_FIRST_STATE_TIMEOUT_MS = 20_000\n\n/**\n * One job, followed by id: its latest summary; `null` while the first state\n * is out; `'missing'` when the route says there is no such job for this\n * reader; `'error'` when the route failed or no state arrived in time.\n */\nexport type AiJobByIdState = AiJobSummary | 'missing' | 'error' | null\n\n/** Follows one job by id. `retry` follows it again after a `'missing'` or `'error'`. */\nexport function useAiJobById(\n user: MaybeTokenSource,\n orgId: string | null,\n jobId: string | null,\n): [AiJobByIdState, (job: AiJobSummary) => void, () => void] {\n const userRef = useRef(user)\n userRef.current = user\n const [job, setJob] = useState<AiJobByIdState>(null)\n const [attempt, setAttempt] = useState(0)\n const status = job && typeof job === 'object' ? job.status : null\n // Followed until the job ENDS (AGL-3596), not until it first stops: a guided\n // start parks on its plan for the instant it takes to confirm it, and a page\n // that stopped following there showed that park — the plan's 13 credits and\n // \"the plan is ready\" — after the job had built, spent and failed.\n const moving = status === null || !AI_JOB_TERMINAL_STATUSES.includes(status)\n useEffect(() => {\n if (!orgId || !jobId || !moving) return undefined\n const controller = new AbortController()\n let heard = false\n const timer = setTimeout(() => {\n if (!heard) setJob((current) => current ?? 'error')\n }, AI_JOB_FIRST_STATE_TIMEOUT_MS)\n void followAiJobEvents(() => userRef.current, orgId, jobId, controller.signal, (next) => {\n heard = true\n setJob(next)\n }).then((end) => {\n if (heard || controller.signal.aborted) return\n const unheard: AiJobByIdState = end === 'not-found' ? 'missing' : 'error'\n setJob((current) => (current && typeof current === 'object' ? current : unheard))\n })\n return () => {\n clearTimeout(timer)\n controller.abort()\n }\n }, [orgId, jobId, moving, attempt])\n const retry = useCallback(() => {\n setJob(null)\n setAttempt((count) => count + 1)\n }, [])\n return [job, setJob, retry]\n}\n\nconst ROW_ICON: Readonly<Record<Exclude<AiSiteBuildRowState, 'active'>, { path: string; color: string; label: string }>> = {\n done: { path: mdiCheckCircle.path, color: 'success.main', label: 'Done' },\n waiting: { path: mdiClockOutline.path, color: 'text.disabled', label: 'Waiting' },\n failed: { path: mdiAlertCircle.path, color: 'error.main', label: 'Stopped' },\n skipped: { path: mdiMinusCircleOutline.path, color: 'text.disabled', label: 'Not built' },\n}\n\nfunction RowIcon({ state }: { state: AiSiteBuildRowState }) {\n if (state === 'active') {\n return <CircularProgress size={20} aria-label=\"In progress\" />\n }\n const icon = ROW_ICON[state]\n return (\n <Box component=\"span\" role=\"img\" aria-label={icon.label} sx={{ color: icon.color, display: 'inline-flex', fontSize: (theme) => theme.spacing(2.5) }}>\n <MdiIcon path={icon.path} aria-hidden />\n </Box>\n )\n}\n\n/** The page's frame: the site's name over the heading and its sentence. */\n/** \"0:42\", \"3:05\": how long the active row has been running, from when it started or was first seen. */\nfunction useElapsed(key: string | null, startedAt: string | null | undefined): string | null {\n const [now, setNow] = useState(() => Date.now())\n const seen = useRef<{ key: string | null; at: number }>({ key: null, at: Date.now() })\n if (seen.current.key !== key) seen.current = { key, at: Date.now() }\n useEffect(() => {\n if (!key) return\n const timer = setInterval(() => setNow(Date.now()), 1000)\n return () => clearInterval(timer)\n }, [key])\n if (!key) return null\n const started = startedAt ? Date.parse(startedAt) : NaN\n const from = Number.isFinite(started) ? Math.min(started, seen.current.at) : seen.current.at\n const seconds = Math.max(0, Math.floor((now - from) / 1000))\n return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, '0')}`\n}\n\nfunction Frame({\n siteName,\n heading,\n lede,\n children,\n}: {\n siteName?: string\n heading: string\n lede?: string\n children?: ReactNode\n}) {\n return (\n <Container maxWidth=\"sm\" sx={{ py: { xs: 3, sm: 5 }, px: { xs: 2, sm: 3 } }}>\n <Stack spacing={4}>\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 {heading}\n </Typography>\n {lede ? (\n <Typography variant=\"body1\" color=\"text.secondary\">\n {lede}\n </Typography>\n ) : null}\n </Stack>\n {children}\n </Stack>\n </Container>\n )\n}\n\nexport function AiSiteBuildPage({ hostId, segments, basePath }: ConsolePluginPageProps) {\n const { data: user } = useUser()\n const jobId = segments?.[0] ? decodeURIComponent(String(segments[0])) : null\n const orgSlug = (basePath ?? '').split('/')[1] ?? ''\n // `/{orgSlug}/hosts/{subdomain}/ai-jobs`: the site's subdomain, which a console URL names it by.\n const hostSubdomain = (basePath ?? '').split('/')[3] || null\n const site = useAiJobSite(hostId)\n const orgId = site.status === 'ready' ? site.orgId : null\n const siteName = site.status === 'ready' ? site.name : undefined\n\n const [job, setJob, retry] = useAiJobById(user, orgId, jobId)\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n // A failed guided start's Try again reopens its questions, filled in (AGL-3596);\n // `?retry=1` — what the AI jobs drawer links — opens them on arrival.\n const [retrying, setRetrying] = useState(\n () => typeof window !== 'undefined' && new URLSearchParams(window.location.search).get('retry') === '1',\n )\n const tryAgain = useCallback(async () => {\n if (!orgId || !job || typeof job !== 'object') return\n setBusy(true)\n setNotice(null)\n // A finished build tries again only what failed (AGL-3616).\n const decision = await resumeAiJobRequest(\n user,\n orgId,\n job,\n aiBuildCanRetry(job) ? { retry: 'failed-items' } : {},\n )\n if (decision.job) setJob(decision.job)\n if (decision.error) setNotice(decision.error)\n setBusy(false)\n }, [orgId, job, user, setJob])\n const confirmPlan = useCallback(\n async (target: AiJobSummary, options: { publish?: boolean } = {}) => {\n if (!orgId) return\n setBusy(true)\n setNotice(null)\n const decision = await resumeAiJobRequest(user, orgId, target, options)\n if (decision.job) setJob(decision.job)\n if (decision.error) setNotice(decision.error)\n setBusy(false)\n },\n [orgId, user, setJob],\n )\n\n const ready = job && typeof job === 'object' ? job : null\n const copy = ready ? aiJobPageCopy(ready, PLATFORM_BRAND_NAME) : null\n // The active row's running time, read before any early return (a hook).\n const liveRow =\n ready && aiSiteBuildPhase(ready) === 'working'\n ? (aiSiteBuildRows(ready).find((row) => row.state === 'active') ?? null)\n : null\n const elapsed = useElapsed(liveRow ? `${ready?.id}:${liveRow.id}` : null, liveRow?.startedAt)\n // The shell's header names the job's page as the page does, and its trail\n // walks back to the site's AI jobs.\n const record = <PageHeaderRecord title={copy?.heading} />\n\n if (site.status === 'error') {\n return (\n <Frame heading=\"This site could not be read\">\n <Alert severity=\"warning\">\n {'The site this job belongs to could not be loaded. Check your connection and reload the page.'}\n </Alert>\n </Frame>\n )\n }\n if (job === 'missing') {\n return (\n <Frame siteName={siteName} heading=\"This job could not be found\">\n <Alert severity=\"info\">{'It may belong to another workspace, or it may have expired.'}</Alert>\n {basePath ? (\n <Box>\n <Button variant=\"outlined\" component={AppLink} href={basePath}>\n {'See this site’s AI jobs'}\n </Button>\n </Box>\n ) : null}\n </Frame>\n )\n }\n if (job === 'error') {\n return (\n <Frame siteName={siteName} heading=\"This job could not be loaded\">\n <Alert\n severity=\"warning\"\n action={\n <Button color=\"inherit\" size=\"small\" onClick={retry}>\n {'Try again'}\n </Button>\n }\n >\n {'Something went wrong reading it. It may still be running: try again in a moment.'}\n </Alert>\n </Frame>\n )\n }\n if (!ready || !copy) {\n return (\n <Frame siteName={siteName} heading=\"Building your site\">\n <Stack sx={{ alignItems: 'center', py: 4 }}>\n <CircularProgress aria-label=\"Loading the job\" />\n </Stack>\n </Frame>\n )\n }\n\n const phase = aiSiteBuildPhase(ready)\n const rows = aiSiteBuildRows(ready)\n const fraction = aiSiteBuildFraction(rows)\n const activeRow = phase === 'working' ? (rows.find((row) => row.state === 'active') ?? null) : null\n const credits = aiSiteBuildCreditsLine(ready)\n const links = phase === 'done' ? aiSiteBuildDoneLinks(ready, orgSlug) : { view: null, pages: null }\n const sitePublish = ready.kind === 'site' && phase === 'done' ? (ready.sitePublish ?? null) : null\n const liveUrl = sitePublish && sitePublish.published.length > 0 ? sitePublish.liveUrl : null\n const retryRefusal = ready.review?.retryRefusal\n const buildRetry = aiBuildCanRetry(ready)\n const canRetry = (phase === 'stopped' && ready.review?.reason === 'doctrine') || buildRetry\n const buildPlanWaiting = ready.kind === 'build' && phase === 'stopped' && ready.review?.reason === 'plan'\n // A guided start that ended without building its site starts over from its\n // own answers, as a fresh job: the questions reopen filled in.\n const restartAnswers =\n ready.kind === 'site' && (phase === 'failed' || phase === 'canceled')\n ? aiSiteStartAnswersFromInputs(ready.siteInputs)\n : null\n const refund = phase === 'failed' || phase === 'stopped' || phase === 'canceled' ? aiJobRefundCopy(ready) : null\n\n return (\n <Frame siteName={siteName} heading={copy.heading} lede={copy.lede}>\n {record}\n {notice && <Alert severity=\"warning\">{notice}</Alert>}\n <Card variant=\"outlined\" sx={{ borderRadius: 2 }}>\n {phase === 'working' &&\n (fraction !== null ? (\n <LinearProgress\n variant=\"determinate\"\n value={Math.round(fraction * 100)}\n aria-label={`Building: ${rows.filter((row) => row.state === 'done').length} of ${rows.length} steps done`}\n />\n ) : (\n <LinearProgress aria-label=\"Building\" />\n ))}\n <Box component=\"ol\" aria-label=\"Progress\" sx={{ listStyle: 'none', m: 0, p: { xs: 2, sm: 3 } }}>\n <Stack spacing={2}>\n {rows.map((row) => (\n <Stack component=\"li\" key={row.id} direction=\"row\" spacing={2} sx={{ alignItems: 'flex-start' }}>\n <Box sx={{ width: (theme) => theme.spacing(3), display: 'flex', justifyContent: 'center' }}>\n <RowIcon state={row.state} />\n </Box>\n <Box sx={{ flex: 1, minWidth: 0 }}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'baseline', justifyContent: 'space-between' }}>\n <Typography\n variant=\"body1\"\n color={row.state === 'waiting' || row.state === 'skipped' ? 'text.secondary' : 'text.primary'}\n >\n {row.label}\n </Typography>\n {row.id === activeRow?.id && elapsed ? (\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ fontVariantNumeric: 'tabular-nums' }}>\n {elapsed}\n </Typography>\n ) : typeof row.credits === 'number' && row.credits > 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ whiteSpace: 'nowrap' }}>\n {`${row.credits} ${row.credits === 1 ? 'credit' : 'credits'}`}\n </Typography>\n ) : null}\n </Stack>\n {row.id === activeRow?.id && row.hint ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {row.hint}\n </Typography>\n ) : null}\n {row.sections?.length && (row.state === 'active' || row.state === 'done') ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`${row.state === 'active' ? 'Writing' : 'Wrote'}: ${row.sections.join(', ')}`}\n </Typography>\n ) : null}\n {/* A build item's failure, note or refund (AGL-3616). */}\n {row.detail ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {row.detail}\n </Typography>\n ) : null}\n </Box>\n </Stack>\n ))}\n </Stack>\n </Box>\n </Card>\n {refund && (ready.refundedCredits ?? 0) > 0 ? (\n <Alert severity=\"info\">{refund}</Alert>\n ) : credits ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {credits}\n </Typography>\n ) : null}\n {retrying && restartAnswers && hostId ? (\n <AiSiteStartCard\n hostId={hostId}\n orgId={orgId ?? undefined}\n orgSlug={orgSlug}\n host={hostSubdomain}\n initialAnswers={restartAnswers}\n startBlank={() => setRetrying(false)}\n leave={() => setRetrying(false)}\n />\n ) : null}\n {buildPlanWaiting ? (\n // A build's plan, confirmed here as in the chat (AGL-3616).\n <AiJobPlan job={ready} busy={busy} onResume={(target, options) => void confirmPlan(target, options)} />\n ) : null}\n {sitePublish && sitePublish.drafts.length > 0 && (\n // The pages the publish left as drafts, each with its plain reason;\n // Edit your pages is where they are fixed and published.\n <Alert severity=\"warning\">\n <Typography variant=\"body2\" sx={{ mb: 1 }}>\n {sitePublish.drafts.length === 1 ? 'This page stayed a draft:' : 'These pages stayed drafts:'}\n </Typography>\n <Box component=\"ul\" sx={{ m: 0, pl: 2.5 }}>\n {sitePublish.drafts.map((draft) => (\n <li key={draft.id}>\n <Typography variant=\"body2\">{`${draft.label}: ${draft.reason}`}</Typography>\n </li>\n ))}\n </Box>\n <Typography variant=\"body2\" sx={{ mt: 1 }}>\n {'Open Edit your pages to fix and publish them.'}\n </Typography>\n </Alert>\n )}\n {phase === 'done' && buildRetry && (\n <Box>\n <Button variant=\"outlined\" disabled={busy} onClick={() => void tryAgain()}>\n {'Try again what failed'}\n </Button>\n </Box>\n )}\n {phase === 'done' && (\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>\n {liveUrl ? (\n // The live site, in a tab of its own: the console stays where it is.\n <Button\n variant=\"contained\"\n size=\"large\"\n component=\"a\"\n href={liveUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {'View your site'}\n </Button>\n ) : links.view ? (\n <Button variant=\"contained\" size=\"large\" component={AppLink} href={links.view}>\n {'View your site'}\n </Button>\n ) : null}\n {links.pages && (\n <Button variant=\"outlined\" size=\"large\" component={AppLink} href={links.pages}>\n {'Edit your pages'}\n </Button>\n )}\n </Stack>\n )}\n {(phase === 'stopped' || phase === 'failed' || phase === 'canceled') && (\n <Stack spacing={1}>\n {retryRefusal && (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {retryRefusal}\n </Typography>\n )}\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} sx={{ alignItems: { sm: 'center' } }}>\n {restartAnswers && hostId && (\n <Button variant=\"contained\" onClick={() => setRetrying(true)}>\n {'Try again'}\n </Button>\n )}\n {canRetry && (\n <Button\n variant=\"contained\"\n disabled={busy || Boolean(retryRefusal)}\n onClick={() => void tryAgain()}\n >\n {'Try again'}\n </Button>\n )}\n {aiSiteStarterFallbackOffered(ready) && user ? <AiSiteStarterFallback job={ready} user={user} /> : null}\n </Stack>\n </Stack>\n )}\n {ready.plan && ready.plan.screens.length > 0 && (\n <Accordion\n // Open while the site is built, so the plan is in view as each part of it is written.\n defaultExpanded={phase === 'working'}\n variant=\"outlined\"\n disableGutters\n sx={{ borderRadius: 2, '&::before': { display: 'none' } }}\n >\n <AccordionSummary expandIcon={<MdiIcon path={mdiChevronDown.path} />}>\n <Typography variant=\"subtitle1\">{'What we’re building'}</Typography>\n </AccordionSummary>\n <AccordionDetails>\n <Stack component=\"ul\" spacing={1.5} sx={{ m: 0, pl: 2.5 }}>\n {ready.plan.screens.map((screen, index) => (\n <Box component=\"li\" key={`${screen.slug}-${index}`}>\n <Typography variant=\"body1\">{`${screen.title} — ${screen.slug}`}</Typography>\n {screen.sections.length > 0 && (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {screen.sections.map((section) => section.name).join(', ')}\n </Typography>\n )}\n </Box>\n ))}\n </Stack>\n </AccordionDetails>\n </Accordion>\n )}\n </Frame>\n )\n}\n\nexport default AiSiteBuildPage\n"],"names":["PLATFORM_BRAND_NAME","PageHeaderRecord","mdiAlertCircle","mdiCheckCircle","mdiChevronDown","mdiClockOutline","mdiMinusCircleOutline","AppLink","MdiIcon","useUser","Accordion","AccordionDetails","AccordionSummary","Alert","Box","Button","Card","CircularProgress","Container","LinearProgress","Stack","Typography","useCallback","useEffect","useRef","useState","aiJobPageCopy","aiSiteBuildCreditsLine","aiSiteBuildFraction","aiSiteBuildPhase","aiSiteBuildRows","aiJobRefundCopy","aiSiteStarterFallbackOffered","aiSiteStartAnswersFromInputs","AiSiteStartCard","AI_JOB_TERMINAL_STATUSES","followAiJobEvents","aiSiteBuildDoneLinks","AiJobPlan","aiBuildCanRetry","resumeAiJobRequest","useAiJobSite","AiSiteStarterFallback","AI_JOB_FIRST_STATE_TIMEOUT_MS","useAiJobById","user","orgId","jobId","userRef","current","job","setJob","attempt","setAttempt","status","moving","includes","undefined","controller","AbortController","heard","timer","setTimeout","signal","next","then","end","aborted","unheard","clearTimeout","abort","retry","count","ROW_ICON","done","path","color","label","waiting","failed","skipped","RowIcon","state","size","aria-label","icon","component","role","sx","display","fontSize","theme","spacing","aria-hidden","useElapsed","key","startedAt","now","setNow","Date","seen","at","setInterval","clearInterval","started","parse","NaN","from","Number","isFinite","Math","min","seconds","max","floor","String","padStart","Frame","siteName","heading","lede","children","maxWidth","py","xs","sm","px","variant","AiSiteBuildPage","hostId","segments","basePath","rows","ready","data","decodeURIComponent","orgSlug","split","hostSubdomain","site","name","busy","setBusy","notice","setNotice","retrying","setRetrying","window","URLSearchParams","location","search","get","tryAgain","decision","error","confirmPlan","target","options","copy","liveRow","find","row","elapsed","id","record","title","severity","href","action","onClick","alignItems","phase","fraction","activeRow","credits","links","view","pages","sitePublish","kind","liveUrl","published","length","retryRefusal","review","buildRetry","canRetry","reason","buildPlanWaiting","restartAnswers","siteInputs","refund","borderRadius","value","round","filter","listStyle","m","p","map","direction","width","justifyContent","flex","minWidth","fontVariantNumeric","whiteSpace","hint","sections","join","detail","refundedCredits","host","initialAnswers","startBlank","leave","onResume","drafts","mb","pl","draft","li","mt","disabled","rel","Boolean","plan","screens","defaultExpanded","disableGutters","expandIcon","screen","index","slug","section"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-site-build-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\n'use client'\n\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn'\nimport { PageHeaderRecord } from '@aglyn/aglyn/app-utils/page-header-record-context'\nimport type { ConsolePluginPageProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport {\n mdiAlertCircle,\n mdiCheckCircle,\n mdiChevronDown,\n mdiClockOutline,\n mdiMinusCircleOutline,\n} from '@aglyn/shared-data-mdi'\nimport { AppLink, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport {\n SITE_LIVE_NOTICE_BODY,\n SITE_LIVE_NOTICE_TITLE,\n SiteLiveNotice,\n} from '@aglyn/shared-ui-jsx/components/site-live-notice.component'\nimport type { MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Accordion,\n AccordionDetails,\n AccordionSummary,\n Alert,\n Box,\n Button,\n Card,\n CircularProgress,\n Container,\n LinearProgress,\n Stack,\n Typography,\n} from '@mui/material'\nimport { type ReactNode, useCallback, useEffect, useRef, useState } from 'react'\nimport {\n aiJobPageCopy,\n aiSiteBuildCreditsLine,\n aiSiteBuildFraction,\n aiSiteBuildPhase,\n aiSiteBuildRows,\n type AiSiteBuildRowState,\n} from '../model/ai-site-build-progress'\nimport { aiJobRefundCopy, aiSiteStarterFallbackOffered } from '../model/ai-job-failure-copy'\nimport { aiSiteStartAnswersFromInputs } from '../model/ai-site-start'\nimport { AiSiteStartCard } from './ai-site-start-card.component'\nimport { AI_JOB_TERMINAL_STATUSES, type AiJobSummary } from '../model/ai-jobs.types'\nimport { followAiJobEvents } from './ai-job-events'\nimport { aiSiteBuildDoneLinks } from './ai-job-links'\nimport { AiJobPlan } from './ai-job-plan.component'\nimport { aiBuildCanRetry } from '../model/ai-build-progress'\nimport { resumeAiJobRequest } from './ai-job-requests'\nimport { useAiJobSite } from './ai-job-site'\nimport { AiSiteStarterFallback } from './ai-site-starter-fallback.component'\n\n/**\n * \"Building your site\" (AGL-3594): one job's page, at `/ai-jobs/{jobId}`\n * under its site — where the guided start sends a person the moment they\n * plan a site, and where a notification about that job opens.\n *\n * A guided site start confirms its own plan (`autoConfirm`), so there is no\n * approval to make here: the page says what is happening, step by step, live\n * from the job's events route; shows what is being built; and, when the job\n * ends, leads with the next thing to do — the live site and the pages to\n * edit, or, where it stopped, the plain sentence for why and what to do\n * about it. It is linkable and survives a reload, because it reads the job by\n * the id in its own address under the site's own route.\n *\n * Every wait ends (AGL-3596): a site that cannot be read, a job the route\n * does not know, a route that fails, and a first state that never arrives\n * each end on a sentence instead of a spinner.\n *\n * Mounted by `AiJobsPage`, the plugin's unlisted `/ai-jobs` route: no tab on\n * the site's strip, an address under it.\n */\n\n/** How long the job's first state may take before the page says it could not be loaded. */\nexport const AI_JOB_FIRST_STATE_TIMEOUT_MS = 20_000\n\n/**\n * One job, followed by id: its latest summary; `null` while the first state\n * is out; `'missing'` when the route says there is no such job for this\n * reader; `'error'` when the route failed or no state arrived in time.\n */\nexport type AiJobByIdState = AiJobSummary | 'missing' | 'error' | null\n\n/** Follows one job by id. `retry` follows it again after a `'missing'` or `'error'`. */\nexport function useAiJobById(\n user: MaybeTokenSource,\n orgId: string | null,\n jobId: string | null,\n): [AiJobByIdState, (job: AiJobSummary) => void, () => void] {\n const userRef = useRef(user)\n userRef.current = user\n const [job, setJob] = useState<AiJobByIdState>(null)\n const [attempt, setAttempt] = useState(0)\n const status = job && typeof job === 'object' ? job.status : null\n // Followed until the job ENDS (AGL-3596), not until it first stops: a guided\n // start parks on its plan for the instant it takes to confirm it, and a page\n // that stopped following there showed that park — the plan's 13 credits and\n // \"the plan is ready\" — after the job had built, spent and failed.\n const moving = status === null || !AI_JOB_TERMINAL_STATUSES.includes(status)\n useEffect(() => {\n if (!orgId || !jobId || !moving) return undefined\n const controller = new AbortController()\n let heard = false\n const timer = setTimeout(() => {\n if (!heard) setJob((current) => current ?? 'error')\n }, AI_JOB_FIRST_STATE_TIMEOUT_MS)\n void followAiJobEvents(() => userRef.current, orgId, jobId, controller.signal, (next) => {\n heard = true\n setJob(next)\n }).then((end) => {\n if (heard || controller.signal.aborted) return\n const unheard: AiJobByIdState = end === 'not-found' ? 'missing' : 'error'\n setJob((current) => (current && typeof current === 'object' ? current : unheard))\n })\n return () => {\n clearTimeout(timer)\n controller.abort()\n }\n }, [orgId, jobId, moving, attempt])\n const retry = useCallback(() => {\n setJob(null)\n setAttempt((count) => count + 1)\n }, [])\n return [job, setJob, retry]\n}\n\nconst ROW_ICON: Readonly<Record<Exclude<AiSiteBuildRowState, 'active'>, { path: string; color: string; label: string }>> = {\n done: { path: mdiCheckCircle.path, color: 'success.main', label: 'Done' },\n waiting: { path: mdiClockOutline.path, color: 'text.disabled', label: 'Waiting' },\n failed: { path: mdiAlertCircle.path, color: 'error.main', label: 'Couldn’t be built' },\n skipped: { path: mdiMinusCircleOutline.path, color: 'text.disabled', label: 'Not built' },\n}\n\nfunction RowIcon({ state }: { state: AiSiteBuildRowState }) {\n if (state === 'active') {\n return <CircularProgress size={20} aria-label=\"In progress\" />\n }\n const icon = ROW_ICON[state]\n return (\n <Box component=\"span\" role=\"img\" aria-label={icon.label} sx={{ color: icon.color, display: 'inline-flex', fontSize: (theme) => theme.spacing(2.5) }}>\n <MdiIcon path={icon.path} aria-hidden />\n </Box>\n )\n}\n\n/** The page's frame: the site's name over the heading and its sentence. */\n/** \"0:42\", \"3:05\": how long the active row has been running, from when it started or was first seen. */\nfunction useElapsed(key: string | null, startedAt: string | null | undefined): string | null {\n const [now, setNow] = useState(() => Date.now())\n const seen = useRef<{ key: string | null; at: number }>({ key: null, at: Date.now() })\n if (seen.current.key !== key) seen.current = { key, at: Date.now() }\n useEffect(() => {\n if (!key) return\n const timer = setInterval(() => setNow(Date.now()), 1000)\n return () => clearInterval(timer)\n }, [key])\n if (!key) return null\n const started = startedAt ? Date.parse(startedAt) : NaN\n const from = Number.isFinite(started) ? Math.min(started, seen.current.at) : seen.current.at\n const seconds = Math.max(0, Math.floor((now - from) / 1000))\n return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, '0')}`\n}\n\nfunction Frame({\n siteName,\n heading,\n lede,\n children,\n}: {\n siteName?: string\n /** Left out when the content leads with a heading of its own. */\n heading?: string\n lede?: string\n children?: ReactNode\n}) {\n return (\n <Container maxWidth=\"sm\" sx={{ py: { xs: 3, sm: 5 }, px: { xs: 2, sm: 3 } }}>\n <Stack spacing={4}>\n <Stack spacing={1}>\n {siteName ? (\n <Typography variant=\"overline\" color=\"text.secondary\">\n {siteName}\n </Typography>\n ) : null}\n {heading ? (\n <Typography variant=\"h4\" component=\"h1\">\n {heading}\n </Typography>\n ) : null}\n {lede ? (\n <Typography variant=\"body1\" color=\"text.secondary\">\n {lede}\n </Typography>\n ) : null}\n </Stack>\n {children}\n </Stack>\n </Container>\n )\n}\n\nexport function AiSiteBuildPage({ hostId, segments, basePath }: ConsolePluginPageProps) {\n const { data: user } = useUser()\n const jobId = segments?.[0] ? decodeURIComponent(String(segments[0])) : null\n const orgSlug = (basePath ?? '').split('/')[1] ?? ''\n // `/{orgSlug}/hosts/{subdomain}/ai-jobs`: the site's subdomain, which a console URL names it by.\n const hostSubdomain = (basePath ?? '').split('/')[3] || null\n const site = useAiJobSite(hostId)\n const orgId = site.status === 'ready' ? site.orgId : null\n const siteName = site.status === 'ready' ? site.name : undefined\n\n const [job, setJob, retry] = useAiJobById(user, orgId, jobId)\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n // A failed guided start's Try again reopens its questions, filled in (AGL-3596);\n // `?retry=1` — what the AI jobs drawer links — opens them on arrival.\n const [retrying, setRetrying] = useState(\n () => typeof window !== 'undefined' && new URLSearchParams(window.location.search).get('retry') === '1',\n )\n const tryAgain = useCallback(async () => {\n if (!orgId || !job || typeof job !== 'object') return\n setBusy(true)\n setNotice(null)\n // A finished build tries again only what failed (AGL-3616).\n const decision = await resumeAiJobRequest(\n user,\n orgId,\n job,\n aiBuildCanRetry(job) ? { retry: 'failed-items' } : {},\n )\n if (decision.job) setJob(decision.job)\n if (decision.error) setNotice(decision.error)\n setBusy(false)\n }, [orgId, job, user, setJob])\n const confirmPlan = useCallback(\n async (target: AiJobSummary, options: { publish?: boolean } = {}) => {\n if (!orgId) return\n setBusy(true)\n setNotice(null)\n const decision = await resumeAiJobRequest(user, orgId, target, options)\n if (decision.job) setJob(decision.job)\n if (decision.error) setNotice(decision.error)\n setBusy(false)\n },\n [orgId, user, setJob],\n )\n\n const ready = job && typeof job === 'object' ? job : null\n const copy = ready ? aiJobPageCopy(ready, PLATFORM_BRAND_NAME) : null\n // The active row's running time, read before any early return (a hook).\n const liveRow =\n ready && aiSiteBuildPhase(ready) === 'working'\n ? (aiSiteBuildRows(ready).find((row) => row.state === 'active') ?? null)\n : null\n const elapsed = useElapsed(liveRow ? `${ready?.id}:${liveRow.id}` : null, liveRow?.startedAt)\n // The shell's header names the job's page as the page does, and its trail\n // walks back to the site's AI jobs.\n const record = <PageHeaderRecord title={copy?.heading} />\n\n if (site.status === 'error') {\n return (\n <Frame heading=\"This site could not be read\">\n <Alert severity=\"warning\">\n {'The site this job belongs to could not be loaded. Check your connection and reload the page.'}\n </Alert>\n </Frame>\n )\n }\n if (job === 'missing') {\n return (\n <Frame siteName={siteName} heading=\"This job could not be found\">\n <Alert severity=\"info\">{'It may belong to another workspace, or it may have expired.'}</Alert>\n {basePath ? (\n <Box>\n <Button variant=\"outlined\" component={AppLink} href={basePath}>\n {'See this site’s AI jobs'}\n </Button>\n </Box>\n ) : null}\n </Frame>\n )\n }\n if (job === 'error') {\n return (\n <Frame siteName={siteName} heading=\"This job could not be loaded\">\n <Alert\n severity=\"warning\"\n action={\n <Button color=\"inherit\" size=\"small\" onClick={retry}>\n {'Try again'}\n </Button>\n }\n >\n {'Something went wrong reading it. It may still be running: try again in a moment.'}\n </Alert>\n </Frame>\n )\n }\n if (!ready || !copy) {\n return (\n <Frame siteName={siteName} heading=\"Building your site\">\n <Stack sx={{ alignItems: 'center', py: 4 }}>\n <CircularProgress aria-label=\"Loading the job\" />\n </Stack>\n </Frame>\n )\n }\n\n const phase = aiSiteBuildPhase(ready)\n const rows = aiSiteBuildRows(ready)\n const fraction = aiSiteBuildFraction(rows)\n const activeRow = phase === 'working' ? (rows.find((row) => row.state === 'active') ?? null) : null\n const credits = aiSiteBuildCreditsLine(ready)\n const links = phase === 'done' ? aiSiteBuildDoneLinks(ready, orgSlug) : { view: null, pages: null }\n const sitePublish = ready.kind === 'site' && phase === 'done' ? (ready.sitePublish ?? null) : null\n const liveUrl = sitePublish && sitePublish.published.length > 0 ? sitePublish.liveUrl : null\n const retryRefusal = ready.review?.retryRefusal\n const buildRetry = aiBuildCanRetry(ready)\n const canRetry = (phase === 'stopped' && ready.review?.reason === 'doctrine') || buildRetry\n const buildPlanWaiting = ready.kind === 'build' && phase === 'stopped' && ready.review?.reason === 'plan'\n // A guided start that ended without building its site starts over from its\n // own answers, as a fresh job: the questions reopen filled in.\n const restartAnswers =\n ready.kind === 'site' && (phase === 'failed' || phase === 'canceled')\n ? aiSiteStartAnswersFromInputs(ready.siteInputs)\n : null\n const refund = phase === 'failed' || phase === 'stopped' || phase === 'canceled' ? aiJobRefundCopy(ready) : null\n // A whole site put live says so in the words every new site goes live in\n // (AGL-3663); a partial build or a page left a draft keeps its own account.\n const liveNotice =\n liveUrl !== null &&\n !sitePublish?.drafts.length &&\n copy.heading === SITE_LIVE_NOTICE_TITLE &&\n copy.lede === SITE_LIVE_NOTICE_BODY\n\n return (\n <Frame siteName={siteName} {...(liveNotice ? {} : { heading: copy.heading, lede: copy.lede })}>\n {record}\n {liveNotice && <SiteLiveNotice liveUrl={liveUrl} pagesHref={links.pages} headingComponent=\"h1\" />}\n {notice && <Alert severity=\"warning\">{notice}</Alert>}\n <Card variant=\"outlined\" sx={{ borderRadius: 2 }}>\n {phase === 'working' &&\n (fraction !== null ? (\n <LinearProgress\n variant=\"determinate\"\n value={Math.round(fraction * 100)}\n aria-label={`Building: ${rows.filter((row) => row.state === 'done').length} of ${rows.length} steps done`}\n />\n ) : (\n <LinearProgress aria-label=\"Building\" />\n ))}\n <Box component=\"ol\" aria-label=\"Progress\" sx={{ listStyle: 'none', m: 0, p: { xs: 2, sm: 3 } }}>\n <Stack spacing={2}>\n {rows.map((row) => (\n <Stack component=\"li\" key={row.id} direction=\"row\" spacing={2} sx={{ alignItems: 'flex-start' }}>\n <Box sx={{ width: (theme) => theme.spacing(3), display: 'flex', justifyContent: 'center' }}>\n <RowIcon state={row.state} />\n </Box>\n <Box sx={{ flex: 1, minWidth: 0 }}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'baseline', justifyContent: 'space-between' }}>\n <Typography\n variant=\"body1\"\n color={row.state === 'waiting' || row.state === 'skipped' ? 'text.secondary' : 'text.primary'}\n >\n {row.label}\n </Typography>\n {row.id === activeRow?.id && elapsed ? (\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ fontVariantNumeric: 'tabular-nums' }}>\n {elapsed}\n </Typography>\n ) : typeof row.credits === 'number' && row.credits > 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ whiteSpace: 'nowrap' }}>\n {`${row.credits} ${row.credits === 1 ? 'credit' : 'credits'}`}\n </Typography>\n ) : null}\n </Stack>\n {row.id === activeRow?.id && row.hint ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {row.hint}\n </Typography>\n ) : null}\n {row.sections?.length && (row.state === 'active' || row.state === 'done') ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`${row.state === 'active' ? 'Writing' : 'Wrote'}: ${row.sections.join(', ')}`}\n </Typography>\n ) : null}\n {/* A build item's failure, note or refund (AGL-3616). */}\n {row.detail ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {row.detail}\n </Typography>\n ) : null}\n </Box>\n </Stack>\n ))}\n </Stack>\n </Box>\n </Card>\n {refund && (ready.refundedCredits ?? 0) > 0 ? (\n <Alert severity=\"info\">{refund}</Alert>\n ) : credits ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {credits}\n </Typography>\n ) : null}\n {retrying && restartAnswers && hostId ? (\n <AiSiteStartCard\n hostId={hostId}\n orgId={orgId ?? undefined}\n orgSlug={orgSlug}\n host={hostSubdomain}\n initialAnswers={restartAnswers}\n startBlank={() => setRetrying(false)}\n leave={() => setRetrying(false)}\n />\n ) : null}\n {buildPlanWaiting ? (\n // A build's plan, confirmed here as in the chat (AGL-3616).\n <AiJobPlan job={ready} busy={busy} onResume={(target, options) => void confirmPlan(target, options)} />\n ) : null}\n {sitePublish && sitePublish.drafts.length > 0 && (\n // The pages the publish left as drafts, each with its plain reason;\n // Edit your pages is where they are fixed and published.\n <Alert severity=\"warning\">\n <Typography variant=\"body2\" sx={{ mb: 1 }}>\n {sitePublish.drafts.length === 1 ? 'This page stayed a draft:' : 'These pages stayed drafts:'}\n </Typography>\n <Box component=\"ul\" sx={{ m: 0, pl: 2.5 }}>\n {sitePublish.drafts.map((draft) => (\n <li key={draft.id}>\n <Typography variant=\"body2\">{`${draft.label}: ${draft.reason}`}</Typography>\n </li>\n ))}\n </Box>\n <Typography variant=\"body2\" sx={{ mt: 1 }}>\n {'Open Edit your pages to fix and publish them.'}\n </Typography>\n </Alert>\n )}\n {phase === 'done' && buildRetry && (\n <Box>\n <Button variant=\"outlined\" disabled={busy} onClick={() => void tryAgain()}>\n {'Try again what failed'}\n </Button>\n </Box>\n )}\n {phase === 'done' && !liveNotice && (\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>\n {liveUrl ? (\n // The live site, in a tab of its own: the console stays where it is.\n <Button\n variant=\"contained\"\n size=\"large\"\n component=\"a\"\n href={liveUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {'View your site'}\n </Button>\n ) : links.view ? (\n <Button variant=\"contained\" size=\"large\" component={AppLink} href={links.view}>\n {'View your site'}\n </Button>\n ) : null}\n {links.pages && (\n <Button variant=\"outlined\" size=\"large\" component={AppLink} href={links.pages}>\n {'Edit your pages'}\n </Button>\n )}\n </Stack>\n )}\n {(phase === 'stopped' || phase === 'failed' || phase === 'canceled') && (\n <Stack spacing={1}>\n {retryRefusal && (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {retryRefusal}\n </Typography>\n )}\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} sx={{ alignItems: { sm: 'center' } }}>\n {restartAnswers && hostId && (\n <Button variant=\"contained\" onClick={() => setRetrying(true)}>\n {'Try again'}\n </Button>\n )}\n {canRetry && (\n <Button\n variant=\"contained\"\n disabled={busy || Boolean(retryRefusal)}\n onClick={() => void tryAgain()}\n >\n {'Try again'}\n </Button>\n )}\n {aiSiteStarterFallbackOffered(ready) && user ? <AiSiteStarterFallback job={ready} user={user} /> : null}\n </Stack>\n </Stack>\n )}\n {ready.plan && ready.plan.screens.length > 0 && (\n <Accordion\n // Open while the site is built, so the plan is in view as each part of it is written.\n defaultExpanded={phase === 'working'}\n variant=\"outlined\"\n disableGutters\n sx={{ borderRadius: 2, '&::before': { display: 'none' } }}\n >\n <AccordionSummary expandIcon={<MdiIcon path={mdiChevronDown.path} />}>\n <Typography variant=\"subtitle1\">{'What we’re building'}</Typography>\n </AccordionSummary>\n <AccordionDetails>\n <Stack component=\"ul\" spacing={1.5} sx={{ m: 0, pl: 2.5 }}>\n {ready.plan.screens.map((screen, index) => (\n <Box component=\"li\" key={`${screen.slug}-${index}`}>\n <Typography variant=\"body1\">{`${screen.title} — ${screen.slug}`}</Typography>\n {screen.sections.length > 0 && (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {screen.sections.map((section) => section.name).join(', ')}\n </Typography>\n )}\n </Box>\n ))}\n </Stack>\n </AccordionDetails>\n </Accordion>\n )}\n </Frame>\n )\n}\n\nexport default AiSiteBuildPage\n"],"names":["PLATFORM_BRAND_NAME","PageHeaderRecord","mdiAlertCircle","mdiCheckCircle","mdiChevronDown","mdiClockOutline","mdiMinusCircleOutline","AppLink","MdiIcon","SITE_LIVE_NOTICE_BODY","SITE_LIVE_NOTICE_TITLE","SiteLiveNotice","useUser","Accordion","AccordionDetails","AccordionSummary","Alert","Box","Button","Card","CircularProgress","Container","LinearProgress","Stack","Typography","useCallback","useEffect","useRef","useState","aiJobPageCopy","aiSiteBuildCreditsLine","aiSiteBuildFraction","aiSiteBuildPhase","aiSiteBuildRows","aiJobRefundCopy","aiSiteStarterFallbackOffered","aiSiteStartAnswersFromInputs","AiSiteStartCard","AI_JOB_TERMINAL_STATUSES","followAiJobEvents","aiSiteBuildDoneLinks","AiJobPlan","aiBuildCanRetry","resumeAiJobRequest","useAiJobSite","AiSiteStarterFallback","AI_JOB_FIRST_STATE_TIMEOUT_MS","useAiJobById","user","orgId","jobId","userRef","current","job","setJob","attempt","setAttempt","status","moving","includes","undefined","controller","AbortController","heard","timer","setTimeout","signal","next","then","end","aborted","unheard","clearTimeout","abort","retry","count","ROW_ICON","done","path","color","label","waiting","failed","skipped","RowIcon","state","size","aria-label","icon","component","role","sx","display","fontSize","theme","spacing","aria-hidden","useElapsed","key","startedAt","now","setNow","Date","seen","at","setInterval","clearInterval","started","parse","NaN","from","Number","isFinite","Math","min","seconds","max","floor","String","padStart","Frame","siteName","heading","lede","children","maxWidth","py","xs","sm","px","variant","AiSiteBuildPage","hostId","segments","basePath","rows","ready","data","decodeURIComponent","orgSlug","split","hostSubdomain","site","name","busy","setBusy","notice","setNotice","retrying","setRetrying","window","URLSearchParams","location","search","get","tryAgain","decision","error","confirmPlan","target","options","copy","liveRow","find","row","elapsed","id","record","title","severity","href","action","onClick","alignItems","phase","fraction","activeRow","credits","links","view","pages","sitePublish","kind","liveUrl","published","length","retryRefusal","review","buildRetry","canRetry","reason","buildPlanWaiting","restartAnswers","siteInputs","refund","liveNotice","drafts","pagesHref","headingComponent","borderRadius","value","round","filter","listStyle","m","p","map","direction","width","justifyContent","flex","minWidth","fontVariantNumeric","whiteSpace","hint","sections","join","detail","refundedCredits","host","initialAnswers","startBlank","leave","onResume","mb","pl","draft","li","mt","disabled","rel","Boolean","plan","screens","defaultExpanded","disableGutters","expandIcon","screen","index","slug","section"],"mappings":"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"}
@@ -61,11 +61,13 @@ import { AiUpstreamError } from "../runtime/ai-runtime.js";
61
61
  return {
62
62
  slot,
63
63
  status: 'failed',
64
- failure: {
64
+ failure: _extends({
65
65
  ours,
66
66
  reason: ours ? 'doctrine-refused' : 'review',
67
67
  message: outcome.review.message || AI_BUILD_UNIT_EMPTY_COPY
68
- },
68
+ }, outcome.review.detail ? {
69
+ detail: outcome.review.detail
70
+ } : {}),
69
71
  outputs
70
72
  };
71
73
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-build-unit-outcome.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { aiBuildItemDelivered, type AiBuildUnit } from '../model/ai-build-job'\nimport type { AiJobItemLedger, AiJobOutput } from '../model/ai-jobs.types'\nimport { AiUpstreamError } from '../runtime/ai-runtime'\nimport type { AiSiteBuiltRef } from './ai-job-site-step'\nimport type { AiJobItemOutcome, AiJobStepOutcome } from './ai-job-text-step'\n\n/**\n * One delegated unit's pass as an ITEM of a job settled item by item\n * (AGL-3616) — a `build`, and a site scaffold, which is the build's preset.\n * The runner's spend is the pass's; what the runner came to is the item's\n * row: still running, built (or built without something that failed),\n * failed on our side, or failed on the model's or the workspace's.\n *\n * Shared by both steps, and importing neither, so neither imports the other.\n */\n\n/** What a unit says when it finished without reporting anything. */\nexport const AI_BUILD_UNIT_EMPTY_COPY = 'It could not be built this time.'\n\n/** What a unit's model declining the request says. */\nexport const AI_BUILD_UNIT_REFUSED_COPY = 'The AI declined to build this one.'\n\n/** A runner's outcome narrowed to its spend: no plan, no review, nothing the delegating job keeps. */\nexport function aiUnitSpend(outcome: AiJobStepOutcome): AiJobStepOutcome {\n return {\n outputs: outcome.outputs,\n usage: outcome.usage,\n estCostUsd: outcome.estCostUsd,\n model: outcome.model,\n stopReason: outcome.stopReason,\n ...(outcome.effort ? { effort: outcome.effort } : {}),\n }\n}\n\n/**\n * What a runner's outcome comes to for its item; `null` when the unit\n * reported what it built and the caller decides whether that counts.\n */\nexport function aiUnitFailure(slot: string, outcome: AiJobStepOutcome): AiJobItemOutcome | null {\n const outputs = outcome.outputs.map((output) => output.id)\n if (outcome.refused) {\n return { slot, status: 'failed', failure: { ours: false, reason: 'refused', message: AI_BUILD_UNIT_REFUSED_COPY }, outputs }\n }\n if (outcome.review) {\n // A building rule still broken after its re-ask is ours (AGL-3596); a\n // site at an allowance is the workspace's to change.\n const ours = outcome.review.reason === 'doctrine'\n return {\n slot,\n status: 'failed',\n failure: { ours, reason: ours ? 'doctrine-refused' : 'review', message: outcome.review.message || AI_BUILD_UNIT_EMPTY_COPY },\n outputs,\n }\n }\n if (outcome.failure) {\n return { slot, status: 'failed', failure: { ours: true, reason: 'step-failure', message: outcome.failure }, outputs }\n }\n if (outcome.continue) return { slot, status: 'running', outputs }\n if (!outcome.outputs.length) {\n return { slot, status: 'failed', failure: { ours: true, reason: 'step-failure', message: AI_BUILD_UNIT_EMPTY_COPY } }\n }\n return null\n}\n\n/**\n * Whether an error a unit's runner threw is the machine's to retry — a\n * provider that will answer later, or a budget that ended — rather than this\n * item's failure.\n */\nexport function aiUnitErrorRetryable(error: unknown): boolean {\n if (error instanceof AiUpstreamError && error.retryable) return true\n const name = (error as { name?: string } | null)?.name\n return name === 'AbortError' || name === 'TimeoutError'\n}\n\n/**\n * What the build has created that a later unit can name: each delivered\n * creation's record, by its plan name. A creation's first output is the\n * record it wrote.\n */\nexport function aiBuildBuiltRefs(\n units: readonly AiBuildUnit[],\n ledger: readonly AiJobItemLedger[],\n outputs: readonly Pick<AiJobOutput, 'id' | 'label'>[],\n): Map<string, AiSiteBuiltRef> {\n const rows = new Map(ledger.map((row) => [row.slot, row]))\n const built = new Map<string, AiSiteBuiltRef>()\n for (const unit of units) {\n const creation = unit.creation\n const row = rows.get(unit.slot)\n if (!creation || !row || !aiBuildItemDelivered(row) || !row.outputs.length) continue\n if (creation.kind !== 'layout' && creation.kind !== 'form' && creation.kind !== 'component') continue\n const id = row.outputs[0]\n const label = outputs.find((output) => output.id === id)?.label || creation.name\n built.set(creation.name.toLowerCase(), { id, label, kind: creation.kind })\n }\n return built\n}\n"],"names":["aiBuildItemDelivered","AiUpstreamError","AI_BUILD_UNIT_EMPTY_COPY","AI_BUILD_UNIT_REFUSED_COPY","aiUnitSpend","outcome","outputs","usage","estCostUsd","model","stopReason","effort","aiUnitFailure","slot","map","output","id","refused","status","failure","ours","reason","message","review","continue","length","aiUnitErrorRetryable","error","retryable","name","aiBuildBuiltRefs","units","ledger","rows","Map","row","built","unit","creation","get","kind","label","find","set","toLowerCase"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-build-unit-outcome.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { aiBuildItemDelivered, type AiBuildUnit } from '../model/ai-build-job'\nimport type { AiJobItemLedger, AiJobOutput } from '../model/ai-jobs.types'\nimport { AiUpstreamError } from '../runtime/ai-runtime'\nimport type { AiSiteBuiltRef } from './ai-job-site-step'\nimport type { AiJobItemOutcome, AiJobStepOutcome } from './ai-job-text-step'\n\n/**\n * One delegated unit's pass as an ITEM of a job settled item by item\n * (AGL-3616) — a `build`, and a site scaffold, which is the build's preset.\n * The runner's spend is the pass's; what the runner came to is the item's\n * row: still running, built (or built without something that failed),\n * failed on our side, or failed on the model's or the workspace's.\n *\n * Shared by both steps, and importing neither, so neither imports the other.\n */\n\n/** What a unit says when it finished without reporting anything. */\nexport const AI_BUILD_UNIT_EMPTY_COPY = 'It could not be built this time.'\n\n/** What a unit's model declining the request says. */\nexport const AI_BUILD_UNIT_REFUSED_COPY = 'The AI declined to build this one.'\n\n/** A runner's outcome narrowed to its spend: no plan, no review, nothing the delegating job keeps. */\nexport function aiUnitSpend(outcome: AiJobStepOutcome): AiJobStepOutcome {\n return {\n outputs: outcome.outputs,\n usage: outcome.usage,\n estCostUsd: outcome.estCostUsd,\n model: outcome.model,\n stopReason: outcome.stopReason,\n ...(outcome.effort ? { effort: outcome.effort } : {}),\n }\n}\n\n/**\n * What a runner's outcome comes to for its item; `null` when the unit\n * reported what it built and the caller decides whether that counts.\n */\nexport function aiUnitFailure(slot: string, outcome: AiJobStepOutcome): AiJobItemOutcome | null {\n const outputs = outcome.outputs.map((output) => output.id)\n if (outcome.refused) {\n return { slot, status: 'failed', failure: { ours: false, reason: 'refused', message: AI_BUILD_UNIT_REFUSED_COPY }, outputs }\n }\n if (outcome.review) {\n // A building rule still broken after its re-ask is ours (AGL-3596); a\n // site at an allowance is the workspace's to change.\n const ours = outcome.review.reason === 'doctrine'\n return {\n slot,\n status: 'failed',\n failure: {\n ours,\n reason: ours ? 'doctrine-refused' : 'review',\n message: outcome.review.message || AI_BUILD_UNIT_EMPTY_COPY,\n ...(outcome.review.detail ? { detail: outcome.review.detail } : {}),\n },\n outputs,\n }\n }\n if (outcome.failure) {\n return { slot, status: 'failed', failure: { ours: true, reason: 'step-failure', message: outcome.failure }, outputs }\n }\n if (outcome.continue) return { slot, status: 'running', outputs }\n if (!outcome.outputs.length) {\n return { slot, status: 'failed', failure: { ours: true, reason: 'step-failure', message: AI_BUILD_UNIT_EMPTY_COPY } }\n }\n return null\n}\n\n/**\n * Whether an error a unit's runner threw is the machine's to retry — a\n * provider that will answer later, or a budget that ended — rather than this\n * item's failure.\n */\nexport function aiUnitErrorRetryable(error: unknown): boolean {\n if (error instanceof AiUpstreamError && error.retryable) return true\n const name = (error as { name?: string } | null)?.name\n return name === 'AbortError' || name === 'TimeoutError'\n}\n\n/**\n * What the build has created that a later unit can name: each delivered\n * creation's record, by its plan name. A creation's first output is the\n * record it wrote.\n */\nexport function aiBuildBuiltRefs(\n units: readonly AiBuildUnit[],\n ledger: readonly AiJobItemLedger[],\n outputs: readonly Pick<AiJobOutput, 'id' | 'label'>[],\n): Map<string, AiSiteBuiltRef> {\n const rows = new Map(ledger.map((row) => [row.slot, row]))\n const built = new Map<string, AiSiteBuiltRef>()\n for (const unit of units) {\n const creation = unit.creation\n const row = rows.get(unit.slot)\n if (!creation || !row || !aiBuildItemDelivered(row) || !row.outputs.length) continue\n if (creation.kind !== 'layout' && creation.kind !== 'form' && creation.kind !== 'component') continue\n const id = row.outputs[0]\n const label = outputs.find((output) => output.id === id)?.label || creation.name\n built.set(creation.name.toLowerCase(), { id, label, kind: creation.kind })\n }\n return built\n}\n"],"names":["aiBuildItemDelivered","AiUpstreamError","AI_BUILD_UNIT_EMPTY_COPY","AI_BUILD_UNIT_REFUSED_COPY","aiUnitSpend","outcome","outputs","usage","estCostUsd","model","stopReason","effort","aiUnitFailure","slot","map","output","id","refused","status","failure","ours","reason","message","review","detail","continue","length","aiUnitErrorRetryable","error","retryable","name","aiBuildBuiltRefs","units","ledger","rows","Map","row","built","unit","creation","get","kind","label","find","set","toLowerCase"],"mappings":"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"}
@@ -44,6 +44,14 @@ export declare function aiJobAutoConfirms(job: Pick<AiJob, 'kind' | 'inputs'>):
44
44
  * A job's inputs as the create door stores them: `autoConfirm` kept only on a
45
45
  * `site` job for a site that publishes nothing of the owner's yet, and dropped
46
46
  * from every other request, which then waits for a person as it always has.
47
+ *
48
+ * A `site` job that names no business is given its site's own name
49
+ * (AGL-3596). The guided start asks what the site is for, never what it is
50
+ * called — the person named it when they created it — and a plan, a header
51
+ * and a footer told no name invented one. Filled here, on the click, from the
52
+ * one read of the site the confirmation already makes, so every door that
53
+ * creates a site job carries it and nothing is read while the guided start
54
+ * renders. A name the request gave — an agency batch's — is kept.
47
55
  */
48
56
  export declare function aiJobAdmittedInputs(firestore: FirebaseFirestore.Firestore, request: {
49
57
  kind: string;
@@ -17,7 +17,7 @@ import { _ as _to_property_key } from "@swc/helpers/_/_to_property_key";
17
17
  * See the License for the specific language governing permissions and
18
18
  * limitations under the License.
19
19
  */ import { isFirstPublishedRoute } from "@aglyn/aglyn/app-utils/analytics-events";
20
- import { AI_JOB_AUTO_CONFIRM_INPUT } from "../model/ai-site-job.js";
20
+ import { AI_JOB_AUTO_CONFIRM_INPUT, AI_SITE_INPUT_MAX_CHARS } from "../model/ai-site-job.js";
21
21
  /**
22
22
  * A guided site start confirms its own plan (AGL-3594).
23
23
  *
@@ -46,17 +46,41 @@ import { AI_JOB_AUTO_CONFIRM_INPUT } from "../model/ai-site-job.js";
46
46
  * A job's inputs as the create door stores them: `autoConfirm` kept only on a
47
47
  * `site` job for a site that publishes nothing of the owner's yet, and dropped
48
48
  * from every other request, which then waits for a person as it always has.
49
+ *
50
+ * A `site` job that names no business is given its site's own name
51
+ * (AGL-3596). The guided start asks what the site is for, never what it is
52
+ * called — the person named it when they created it — and a plan, a header
53
+ * and a footer told no name invented one. Filled here, on the click, from the
54
+ * one read of the site the confirmation already makes, so every door that
55
+ * creates a site job carries it and nothing is read while the guided start
56
+ * renders. A name the request gave — an agency batch's — is kept.
49
57
  */ export async function aiJobAdmittedInputs(firestore, request) {
50
- if (!(AI_JOB_AUTO_CONFIRM_INPUT in request.inputs)) return request.inputs;
58
+ const askedToConfirm = AI_JOB_AUTO_CONFIRM_INPUT in request.inputs;
59
+ const unnamedSite = request.kind === 'site' && !!request.hostId && !aiSiteJobNamed(request.inputs);
60
+ if (!askedToConfirm && !unnamedSite) return request.inputs;
51
61
  const _request_inputs = request.inputs, { [AI_JOB_AUTO_CONFIRM_INPUT]: asked } = _request_inputs, rest = _object_without_properties_loose(_request_inputs, [
52
62
  _to_property_key(AI_JOB_AUTO_CONFIRM_INPUT)
53
63
  ]);
54
- if (request.kind !== 'site' || asked !== true || !request.hostId) return rest;
64
+ const mayConfirm = request.kind === 'site' && asked === true && !!request.hostId;
65
+ if (!mayConfirm && !unnamedSite) return rest;
55
66
  const host = await firestore.collection('hosts').doc(request.hostId).get();
56
- const blank = host.exists && isFirstPublishedRoute(host.get('screens'), host.get('defaultHomeScreenId'));
57
- return blank ? _extends({}, rest, {
67
+ const blank = mayConfirm && host.exists && isFirstPublishedRoute(host.get('screens'), host.get('defaultHomeScreenId'));
68
+ const name = unnamedSite && host.exists ? aiSiteJobNameOf(host.get('displayName')) : '';
69
+ // A request that asked nothing of this door, for a site with no name to give, is passed through untouched.
70
+ if (!askedToConfirm && !name) return request.inputs;
71
+ return _extends({}, rest, name ? {
72
+ businessName: name
73
+ } : {}, blank ? {
58
74
  [AI_JOB_AUTO_CONFIRM_INPUT]: true
59
- }) : rest;
75
+ } : {});
76
+ }
77
+ /** Whether a site job's inputs already name the business. */ function aiSiteJobNamed(inputs) {
78
+ const name = inputs['businessName'];
79
+ return typeof name === 'string' && name.trim() !== '';
80
+ }
81
+ /** A site's display name as a site job's `businessName`: one line, within what the job admits. */ function aiSiteJobNameOf(value) {
82
+ if (typeof value !== 'string') return '';
83
+ return value.replace(/\s+/g, ' ').trim().slice(0, AI_SITE_INPUT_MAX_CHARS).trim();
60
84
  }
61
85
 
62
86
  //# sourceMappingURL=ai-job-auto-confirm.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-auto-confirm.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isFirstPublishedRoute } from '@aglyn/aglyn/app-utils/analytics-events'\nimport type { AiJob } from '../model/ai-jobs.types'\nimport { AI_JOB_AUTO_CONFIRM_INPUT } from '../model/ai-site-job'\n\n/**\n * A guided site start confirms its own plan (AGL-3594).\n *\n * Every planned job stops after its plan for a person to read and confirm it.\n * For the guided start on a new site that stop read as a step nobody knew they\n * had to take: a person who had just answered \"what is your site?\" was shown a\n * plan to approve, and a site nobody approved was a site never built. So the\n * guided start asks for its plan to be confirmed for it, with this input, and\n * the machine confirms it the moment the plan step keeps a plan — the same\n * confirmation the resume door makes, by the job's creator, with the same\n * hold on what the plan is estimated to cost.\n *\n * Honored only where it is safe to skip the read: a `site` job, on a site\n * that has published nothing of its own yet — the site the guided start is\n * offered on. Everywhere else the create door drops it, so a page, a form or\n * a site job on an established site still waits for a person. What bounds the\n * spend is unchanged: the plan step's own checks (the Free page cap and the\n * Free wall among them), the plan's admission, and the reservation each step\n * takes.\n */\n\n/** The job input the guided start sets. */\nexport { AI_JOB_AUTO_CONFIRM_INPUT }\n\n/** Whether a job's plan is confirmed for it when the plan step keeps one. */\nexport function aiJobAutoConfirms(job: Pick<AiJob, 'kind' | 'inputs'>): boolean {\n return job.kind === 'site' && (job.inputs ?? {})[AI_JOB_AUTO_CONFIRM_INPUT] === true\n}\n\n/**\n * A job's inputs as the create door stores them: `autoConfirm` kept only on a\n * `site` job for a site that publishes nothing of the owner's yet, and dropped\n * from every other request, which then waits for a person as it always has.\n */\nexport async function aiJobAdmittedInputs(\n firestore: FirebaseFirestore.Firestore,\n request: { kind: string; hostId: string | null; inputs: Record<string, unknown> },\n): Promise<Record<string, unknown>> {\n if (!(AI_JOB_AUTO_CONFIRM_INPUT in request.inputs)) return request.inputs\n const { [AI_JOB_AUTO_CONFIRM_INPUT]: asked, ...rest } = request.inputs\n if (request.kind !== 'site' || asked !== true || !request.hostId) return rest\n const host = await firestore.collection('hosts').doc(request.hostId).get()\n const blank =\n host.exists &&\n isFirstPublishedRoute(\n host.get('screens') as Record<string, unknown> | undefined,\n host.get('defaultHomeScreenId') as string | undefined,\n )\n return blank ? { ...rest, [AI_JOB_AUTO_CONFIRM_INPUT]: true } : rest\n}\n"],"names":["isFirstPublishedRoute","AI_JOB_AUTO_CONFIRM_INPUT","aiJobAutoConfirms","job","kind","inputs","aiJobAdmittedInputs","firestore","request","asked","rest","hostId","host","collection","doc","get","blank","exists"],"mappings":";;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,qBAAqB,QAAQ,0CAAyC;AAE/E,SAASC,yBAAyB,QAAQ,0BAAsB;AAEhE;;;;;;;;;;;;;;;;;;;CAmBC,GAED,yCAAyC,GACzC,SAASA,yBAAyB,GAAE;AAEpC,2EAA2E,GAC3E,OAAO,SAASC,kBAAkBC,GAAmC;QACpCA;IAA/B,OAAOA,IAAIC,IAAI,KAAK,UAAU,EAACD,cAAAA,IAAIE,MAAM,YAAVF,cAAc,CAAC,EAAE,CAACF,0BAA0B,KAAK;AAClF;AAEA;;;;CAIC,GACD,OAAO,eAAeK,oBACpBC,SAAsC,EACtCC,OAAiF;IAEjF,IAAI,CAAEP,CAAAA,6BAA6BO,QAAQH,MAAM,AAAD,GAAI,OAAOG,QAAQH,MAAM;IACzE,MAAwDG,kBAAAA,QAAQH,MAAM,EAAhE,EAAE,CAACJ,0BAA0B,EAAEQ,KAAK,EAAW,GAAGD,iBAATE,wCAASF;yBAA/CP;;IACT,IAAIO,QAAQJ,IAAI,KAAK,UAAUK,UAAU,QAAQ,CAACD,QAAQG,MAAM,EAAE,OAAOD;IACzE,MAAME,OAAO,MAAML,UAAUM,UAAU,CAAC,SAASC,GAAG,CAACN,QAAQG,MAAM,EAAEI,GAAG;IACxE,MAAMC,QACJJ,KAAKK,MAAM,IACXjB,sBACEY,KAAKG,GAAG,CAAC,YACTH,KAAKG,GAAG,CAAC;IAEb,OAAOC,QAAQ,aAAKN;QAAM,CAACT,0BAA0B,EAAE;SAASS;AAClE"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-auto-confirm.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isFirstPublishedRoute } from '@aglyn/aglyn/app-utils/analytics-events'\nimport type { AiJob } from '../model/ai-jobs.types'\nimport { AI_JOB_AUTO_CONFIRM_INPUT, AI_SITE_INPUT_MAX_CHARS } from '../model/ai-site-job'\n\n/**\n * A guided site start confirms its own plan (AGL-3594).\n *\n * Every planned job stops after its plan for a person to read and confirm it.\n * For the guided start on a new site that stop read as a step nobody knew they\n * had to take: a person who had just answered \"what is your site?\" was shown a\n * plan to approve, and a site nobody approved was a site never built. So the\n * guided start asks for its plan to be confirmed for it, with this input, and\n * the machine confirms it the moment the plan step keeps a plan — the same\n * confirmation the resume door makes, by the job's creator, with the same\n * hold on what the plan is estimated to cost.\n *\n * Honored only where it is safe to skip the read: a `site` job, on a site\n * that has published nothing of its own yet — the site the guided start is\n * offered on. Everywhere else the create door drops it, so a page, a form or\n * a site job on an established site still waits for a person. What bounds the\n * spend is unchanged: the plan step's own checks (the Free page cap and the\n * Free wall among them), the plan's admission, and the reservation each step\n * takes.\n */\n\n/** The job input the guided start sets. */\nexport { AI_JOB_AUTO_CONFIRM_INPUT }\n\n/** Whether a job's plan is confirmed for it when the plan step keeps one. */\nexport function aiJobAutoConfirms(job: Pick<AiJob, 'kind' | 'inputs'>): boolean {\n return job.kind === 'site' && (job.inputs ?? {})[AI_JOB_AUTO_CONFIRM_INPUT] === true\n}\n\n/**\n * A job's inputs as the create door stores them: `autoConfirm` kept only on a\n * `site` job for a site that publishes nothing of the owner's yet, and dropped\n * from every other request, which then waits for a person as it always has.\n *\n * A `site` job that names no business is given its site's own name\n * (AGL-3596). The guided start asks what the site is for, never what it is\n * called — the person named it when they created it — and a plan, a header\n * and a footer told no name invented one. Filled here, on the click, from the\n * one read of the site the confirmation already makes, so every door that\n * creates a site job carries it and nothing is read while the guided start\n * renders. A name the request gave — an agency batch's — is kept.\n */\nexport async function aiJobAdmittedInputs(\n firestore: FirebaseFirestore.Firestore,\n request: { kind: string; hostId: string | null; inputs: Record<string, unknown> },\n): Promise<Record<string, unknown>> {\n const askedToConfirm = AI_JOB_AUTO_CONFIRM_INPUT in request.inputs\n const unnamedSite = request.kind === 'site' && !!request.hostId && !aiSiteJobNamed(request.inputs)\n if (!askedToConfirm && !unnamedSite) return request.inputs\n const { [AI_JOB_AUTO_CONFIRM_INPUT]: asked, ...rest } = request.inputs\n const mayConfirm = request.kind === 'site' && asked === true && !!request.hostId\n if (!mayConfirm && !unnamedSite) return rest\n const host = await firestore.collection('hosts').doc(request.hostId as string).get()\n const blank =\n mayConfirm &&\n host.exists &&\n isFirstPublishedRoute(\n host.get('screens') as Record<string, unknown> | undefined,\n host.get('defaultHomeScreenId') as string | undefined,\n )\n const name = unnamedSite && host.exists ? aiSiteJobNameOf(host.get('displayName')) : ''\n // A request that asked nothing of this door, for a site with no name to give, is passed through untouched.\n if (!askedToConfirm && !name) return request.inputs\n return {\n ...rest,\n ...(name ? { businessName: name } : {}),\n ...(blank ? { [AI_JOB_AUTO_CONFIRM_INPUT]: true } : {}),\n }\n}\n\n/** Whether a site job's inputs already name the business. */\nfunction aiSiteJobNamed(inputs: Readonly<Record<string, unknown>>): boolean {\n const name = inputs['businessName']\n return typeof name === 'string' && name.trim() !== ''\n}\n\n/** A site's display name as a site job's `businessName`: one line, within what the job admits. */\nfunction aiSiteJobNameOf(value: unknown): string {\n if (typeof value !== 'string') return ''\n return value.replace(/\\s+/g, ' ').trim().slice(0, AI_SITE_INPUT_MAX_CHARS).trim()\n}\n"],"names":["isFirstPublishedRoute","AI_JOB_AUTO_CONFIRM_INPUT","AI_SITE_INPUT_MAX_CHARS","aiJobAutoConfirms","job","kind","inputs","aiJobAdmittedInputs","firestore","request","askedToConfirm","unnamedSite","hostId","aiSiteJobNamed","asked","rest","mayConfirm","host","collection","doc","get","blank","exists","name","aiSiteJobNameOf","businessName","trim","value","replace","slice"],"mappings":";;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,qBAAqB,QAAQ,0CAAyC;AAE/E,SAASC,yBAAyB,EAAEC,uBAAuB,QAAQ,0BAAsB;AAEzF;;;;;;;;;;;;;;;;;;;CAmBC,GAED,yCAAyC,GACzC,SAASD,yBAAyB,GAAE;AAEpC,2EAA2E,GAC3E,OAAO,SAASE,kBAAkBC,GAAmC;QACpCA;IAA/B,OAAOA,IAAIC,IAAI,KAAK,UAAU,EAACD,cAAAA,IAAIE,MAAM,YAAVF,cAAc,CAAC,EAAE,CAACH,0BAA0B,KAAK;AAClF;AAEA;;;;;;;;;;;;CAYC,GACD,OAAO,eAAeM,oBACpBC,SAAsC,EACtCC,OAAiF;IAEjF,MAAMC,iBAAiBT,6BAA6BQ,QAAQH,MAAM;IAClE,MAAMK,cAAcF,QAAQJ,IAAI,KAAK,UAAU,CAAC,CAACI,QAAQG,MAAM,IAAI,CAACC,eAAeJ,QAAQH,MAAM;IACjG,IAAI,CAACI,kBAAkB,CAACC,aAAa,OAAOF,QAAQH,MAAM;IAC1D,MAAwDG,kBAAAA,QAAQH,MAAM,EAAhE,EAAE,CAACL,0BAA0B,EAAEa,KAAK,EAAW,GAAGL,iBAATM,wCAASN;yBAA/CR;;IACT,MAAMe,aAAaP,QAAQJ,IAAI,KAAK,UAAUS,UAAU,QAAQ,CAAC,CAACL,QAAQG,MAAM;IAChF,IAAI,CAACI,cAAc,CAACL,aAAa,OAAOI;IACxC,MAAME,OAAO,MAAMT,UAAUU,UAAU,CAAC,SAASC,GAAG,CAACV,QAAQG,MAAM,EAAYQ,GAAG;IAClF,MAAMC,QACJL,cACAC,KAAKK,MAAM,IACXtB,sBACEiB,KAAKG,GAAG,CAAC,YACTH,KAAKG,GAAG,CAAC;IAEb,MAAMG,OAAOZ,eAAeM,KAAKK,MAAM,GAAGE,gBAAgBP,KAAKG,GAAG,CAAC,kBAAkB;IACrF,2GAA2G;IAC3G,IAAI,CAACV,kBAAkB,CAACa,MAAM,OAAOd,QAAQH,MAAM;IACnD,OAAO,aACFS,MACCQ,OAAO;QAAEE,cAAcF;IAAK,IAAI,CAAC,GACjCF,QAAQ;QAAE,CAACpB,0BAA0B,EAAE;IAAK,IAAI,CAAC;AAEzD;AAEA,2DAA2D,GAC3D,SAASY,eAAeP,MAAyC;IAC/D,MAAMiB,OAAOjB,MAAM,CAAC,eAAe;IACnC,OAAO,OAAOiB,SAAS,YAAYA,KAAKG,IAAI,OAAO;AACrD;AAEA,gGAAgG,GAChG,SAASF,gBAAgBG,KAAc;IACrC,IAAI,OAAOA,UAAU,UAAU,OAAO;IACtC,OAAOA,MAAMC,OAAO,CAAC,QAAQ,KAAKF,IAAI,GAAGG,KAAK,CAAC,GAAG3B,yBAAyBwB,IAAI;AACjF"}
@@ -33,6 +33,8 @@ import { aiConfirmedPlan, aiUnspentOutcome } from "./ai-job-generation.js";
33
33
  import { AI_JOB_PAGE_STEP_MINIMUM_MS } from "./ai-job-page-budget.js";
34
34
  import { aiPluginDraftAdmissionRefusal, aiPluginDraftWriter } from "./ai-job-plugin-drafts.js";
35
35
  import { aiSitePageWritten, aiSiteResolvedRef } from "./ai-job-site-step.js";
36
+ import { AI_LAYOUT_SITE_PAGES_INPUT, aiLayoutSitePagesOfPlan } from "./ai-job-layout-site-pages.js";
37
+ import { AI_LAYOUT_FORM_PAGE_INPUT, AI_LAYOUT_LANGUAGE_INPUT, aiLayoutFormPageOfPlan } from "./ai-job-page-language.js";
36
38
  import { AI_JOB_BRIEF_MAX_CHARS } from "./ai-job-text-step.js";
37
39
  import { aiJobPublishesBuild, aiPublishGuidedSite } from "./ai-site-publish.js";
38
40
  import { AI_PLANNED_JOB_KINDS, aiJobStepRunMinimumMs, aiJobStepRunnerFor, registerAiJobStep, registerAiJobStepPasses } from "./ai-jobs.js";
@@ -185,6 +187,15 @@ import { AI_PLANNED_JOB_KINDS, aiJobStepRunMinimumMs, aiJobStepRunnerFor, regist
185
187
  originJobId: aiOriginJobId(job)
186
188
  });
187
189
  delete inputs[AI_BUILD_PUBLISH_INPUT];
190
+ // A build's pages and layouts are designed in the layout language and
191
+ // compiled (AGL-3660), told the pages the build plans and where its form is.
192
+ if (context.kind === 'page' || context.kind === 'layout') {
193
+ inputs[AI_LAYOUT_LANGUAGE_INPUT] = true;
194
+ const pages = aiLayoutSitePagesOfPlan(plan.screens);
195
+ if (pages.length) inputs[AI_LAYOUT_SITE_PAGES_INPUT] = pages;
196
+ const formPage = aiLayoutFormPageOfPlan(plan);
197
+ if (formPage) inputs[AI_LAYOUT_FORM_PAGE_INPUT] = formPage;
198
+ }
188
199
  return _extends({}, job, {
189
200
  $id: aiBuildUnitJobId(job, unit),
190
201
  kind: context.kind,