@aglyn/plugins-ai 1.0.0-beta.233 → 1.0.0-beta.234

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 (104) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-job-links.d.ts +6 -0
  3. package/src/lib/components/ai-job-links.js +9 -0
  4. package/src/lib/components/ai-job-links.js.map +1 -1
  5. package/src/lib/components/ai-site-build-page.component.js +41 -3
  6. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  7. package/src/lib/components/ai-site-start-card.component.js +91 -8
  8. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  9. package/src/lib/jobs/ai-job-admission.d.ts +2 -1
  10. package/src/lib/jobs/ai-job-admission.js.map +1 -1
  11. package/src/lib/jobs/ai-job-email-step.d.ts +39 -0
  12. package/src/lib/jobs/ai-job-email-step.js +116 -5
  13. package/src/lib/jobs/ai-job-email-step.js.map +1 -1
  14. package/src/lib/jobs/ai-job-layout-language.d.ts +3 -0
  15. package/src/lib/jobs/ai-job-layout-language.js +3 -1
  16. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  17. package/src/lib/jobs/ai-job-layout-site-pages.d.ts +2 -0
  18. package/src/lib/jobs/ai-job-layout-site-pages.js +23 -11
  19. package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
  20. package/src/lib/jobs/ai-job-layout-step.js +3 -2
  21. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  22. package/src/lib/jobs/ai-job-page-language.d.ts +16 -0
  23. package/src/lib/jobs/ai-job-page-language.js +35 -6
  24. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  25. package/src/lib/jobs/ai-job-page-step.d.ts +3 -0
  26. package/src/lib/jobs/ai-job-page-step.js +18 -4
  27. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  28. package/src/lib/jobs/ai-job-plan-step.d.ts +34 -0
  29. package/src/lib/jobs/ai-job-plan-step.js +106 -14
  30. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  31. package/src/lib/jobs/ai-job-site-content.d.ts +6 -6
  32. package/src/lib/jobs/ai-job-site-content.js +6 -12
  33. package/src/lib/jobs/ai-job-site-content.js.map +1 -1
  34. package/src/lib/jobs/ai-job-site-step.d.ts +11 -0
  35. package/src/lib/jobs/ai-job-site-step.js +96 -44
  36. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  37. package/src/lib/jobs/ai-jobs.d.ts +8 -4
  38. package/src/lib/jobs/ai-jobs.js +19 -2
  39. package/src/lib/jobs/ai-jobs.js.map +1 -1
  40. package/src/lib/jobs/ai-layout-stock-photos.d.ts +98 -0
  41. package/src/lib/jobs/ai-layout-stock-photos.js +248 -0
  42. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -0
  43. package/src/lib/jobs/ai-site-publish.d.ts +11 -0
  44. package/src/lib/jobs/ai-site-publish.js +31 -1
  45. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  46. package/src/lib/jobs/fixtures/ai-juniper-portfolio-page-recording.d.ts +151 -0
  47. package/src/lib/jobs/fixtures/ai-juniper-portfolio-page-recording.js +183 -0
  48. package/src/lib/jobs/fixtures/ai-juniper-portfolio-page-recording.js.map +1 -0
  49. package/src/lib/jobs/fixtures/ai-slow-roads-site-plan-recording.d.ts +27 -0
  50. package/src/lib/jobs/fixtures/ai-slow-roads-site-plan-recording.js +192 -0
  51. package/src/lib/jobs/fixtures/ai-slow-roads-site-plan-recording.js.map +1 -0
  52. package/src/lib/jobs/fixtures/ai-yoga-site-plan-recording.d.ts +27 -0
  53. package/src/lib/jobs/fixtures/ai-yoga-site-plan-recording.js +222 -0
  54. package/src/lib/jobs/fixtures/ai-yoga-site-plan-recording.js.map +1 -0
  55. package/src/lib/layout-language/ai-layout-compiler.d.ts +29 -0
  56. package/src/lib/layout-language/ai-layout-compiler.js +698 -12
  57. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  58. package/src/lib/layout-language/ai-layout-design.d.ts +107 -0
  59. package/src/lib/layout-language/ai-layout-design.js +197 -0
  60. package/src/lib/layout-language/ai-layout-design.js.map +1 -0
  61. package/src/lib/layout-language/ai-layout-frame.d.ts +6 -0
  62. package/src/lib/layout-language/ai-layout-frame.js +22 -6
  63. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  64. package/src/lib/layout-language/ai-layout-language.d.ts +2 -0
  65. package/src/lib/layout-language/ai-layout-language.js +15 -1
  66. package/src/lib/layout-language/ai-layout-language.js.map +1 -1
  67. package/src/lib/layout-language/ai-layout-links.d.ts +11 -0
  68. package/src/lib/layout-language/ai-layout-links.js +4 -0
  69. package/src/lib/layout-language/ai-layout-links.js.map +1 -1
  70. package/src/lib/layout-language/ai-layout-pictures.d.ts +11 -5
  71. package/src/lib/layout-language/ai-layout-pictures.js +80 -17
  72. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  73. package/src/lib/model/ai-job-notice.d.ts +9 -1
  74. package/src/lib/model/ai-job-notice.js +23 -0
  75. package/src/lib/model/ai-job-notice.js.map +1 -1
  76. package/src/lib/model/ai-plan-capabilities.d.ts +7 -0
  77. package/src/lib/model/ai-plan-capabilities.js.map +1 -1
  78. package/src/lib/model/ai-site-build-progress.d.ts +12 -2
  79. package/src/lib/model/ai-site-build-progress.js +78 -4
  80. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  81. package/src/lib/model/ai-site-job.d.ts +209 -22
  82. package/src/lib/model/ai-site-job.js +247 -20
  83. package/src/lib/model/ai-site-job.js.map +1 -1
  84. package/src/lib/model/ai-site-kinds.js +6 -1
  85. package/src/lib/model/ai-site-kinds.js.map +1 -1
  86. package/src/lib/model/ai-site-look.d.ts +21 -0
  87. package/src/lib/model/ai-site-look.js +63 -14
  88. package/src/lib/model/ai-site-look.js.map +1 -1
  89. package/src/lib/runtime/ai-doctrine-validators.d.ts +32 -1
  90. package/src/lib/runtime/ai-doctrine-validators.js +227 -4
  91. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  92. package/src/lib/runtime/ai-doctrine.js +5 -3
  93. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  94. package/src/lib/runtime/ai-palette.generated.d.ts +2 -0
  95. package/src/lib/runtime/ai-palette.generated.js +18 -2
  96. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  97. package/src/lib/server/ai-jobs-resume.js.map +1 -1
  98. package/src/lib/server/ai-jobs-route.js +15 -3
  99. package/src/lib/server/ai-jobs-route.js.map +1 -1
  100. package/src/lib/server/assist-edit.js +5 -2
  101. package/src/lib/server/assist-edit.js.map +1 -1
  102. package/src/lib/usage/free-ai-credits-left.d.ts +54 -0
  103. package/src/lib/usage/free-ai-credits-left.js +62 -0
  104. package/src/lib/usage/free-ai-credits-left.js.map +1 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-job-notice.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 type { AglynNotification } from '@aglyn/aglyn/app-utils/notifications'\nimport { aiBuildOutcomeLine, aiSitePartialCopy } from './ai-build-progress'\nimport { aiJobKindNoun } from './ai-job-activity'\nimport { aiCustomerSafeCopy, aiJobRefundCopy } from './ai-job-failure-copy'\nimport type { AiJobItemLedger, AiJobKind, AiJobOutput, AiJobReviewReason, AiJobSitePublish } from './ai-jobs.types'\n\n/** The besigner segment each versioned resource lives under. */\nexport const AI_JOB_BESIGNER_SEGMENT: Partial<Record<AiJobOutput['resource'], string>> = {\n screen: 'screens',\n reusableComponent: 'components',\n layout: 'layouts',\n template: 'templates',\n // An email design is a screen: it opens in the screen besigner, as the\n // Emails page's own Edit design does.\n emailScreen: 'screens',\n}\n\n/**\n * The query parameter a console address carries to open AI jobs on one job\n * (AGL-3593): the Assist panel reads it on arrival.\n */\nexport const AI_JOB_LINK_PARAM = 'aiJob'\n\n/** Where a site's \"Building your site\" page lives, under the site (AGL-3594). */\nexport const AI_SITE_BUILD_HREF = '/ai-jobs'\n\n/** A change the person who started a job is told about. */\nexport type AiJobNoticeTransition = 'needs-review' | 'done' | 'failed'\n\n/** The fields of a stored job a notice is written from. */\nexport interface AiJobNoticeSource {\n $id: string\n orgId: string\n hostId?: string | null\n kind: AiJobKind\n review?: { reason: AiJobReviewReason; message?: string | null } | null\n outputs?: readonly AiJobOutput[] | null\n error?: string | null\n /** What a guided site start put live (AGL-3596); absent on every other job. */\n sitePublish?: Pick<AiJobSitePublish, 'published' | 'drafts'> | null\n /** What the job spent, and gave back for a failure on our side (AGL-3596). */\n creditsSpent?: number\n refundedCredits?: number\n /** A build's or a guided start's items (AGL-3616), so a finished one says what was not built. */\n items?: AiJobItemLedger[] | null\n}\n\n/** A stopped job's sentence with what became of its credits, from the job's recorded give-back (AGL-3596). */\nfunction withRefund(job: AiJobNoticeSource, text: string, status: 'failed' | 'needs_review'): string {\n const refund = aiJobRefundCopy({ status, refundedCredits: job.refundedCredits, creditsSpent: job.creditsSpent })\n return refund ? `${text} ${refund}` : text\n}\n\nexport type AiJobNotice = Pick<\n AglynNotification,\n 'type' | 'title' | 'body' | 'link' | 'orgId' | 'hostId' | 'level'\n>\n\n/**\n * Where a notice's link goes, in the stored form the console rewrites when it\n * is followed (`normalizeNotificationLink`): `/{hostId}/…` for the job's site,\n * `/org…` for a job of the workspace's.\n *\n * A job that is done leads to what it built (AGL-3593): several pages of the\n * job's site open that site's Pages list, one versioned output opens in the\n * editor on the version the job wrote. Everything else — a plan to confirm, a\n * job that stopped, a done job with nothing of its own to open — opens AI\n * jobs on the job.\n */\nexport function aiJobNoticeLink(job: AiJobNoticeSource, to: AiJobNoticeTransition): string {\n const base = job.hostId ? `/${job.hostId}` : '/org'\n // A site job's every notice opens its \"Building your site\" page (AGL-3594),\n // which leads with the next step whatever the job's state.\n if (job.kind === 'site' && job.hostId) return `${base}${AI_SITE_BUILD_HREF}/${encodeURIComponent(job.$id)}`\n if (to === 'done' && job.hostId) {\n const own = (job.outputs ?? []).filter((output) => output.hostId === job.hostId)\n const screens = own.filter((output) => output.resource === 'screen')\n if (screens.length > 1) return `${base}/screens`\n const versioned = own.find(\n (output) => AI_JOB_BESIGNER_SEGMENT[output.resource] && output.versionId,\n )\n if (own.length === 1 && versioned) {\n const segment = AI_JOB_BESIGNER_SEGMENT[versioned.resource]\n return `${base}/${segment}/${versioned.id}/versions/${versioned.versionId}/besigner`\n }\n }\n return `${base}?${AI_JOB_LINK_PARAM}=${encodeURIComponent(job.$id)}`\n}\n\n/**\n * The in-app notification for one change of one job (AGL-3593), for the\n * person who created it. Customer-safe throughout: a stopped job's sentence is\n * the job's own `error`, which the machine writes as a fixed sentence.\n */\nexport function aiJobNotice(job: AiJobNoticeSource, to: AiJobNoticeTransition): AiJobNotice {\n const noun = aiJobKindNoun(job.kind)\n const scope = { orgId: job.orgId, ...(job.hostId ? { hostId: job.hostId } : {}) }\n const link = aiJobNoticeLink(job, to)\n if (to === 'needs-review') {\n if (job.review?.reason === 'plan') {\n return {\n type: 'content.aiJobNeedsYou',\n level: 'warning',\n title:\n noun === 'AI job'\n ? 'Your plan is ready: confirm it to build'\n : `Your ${noun} plan is ready: confirm it to build`,\n body: 'Nothing is built until you confirm the plan. It waits in AI jobs.',\n link,\n ...scope,\n }\n }\n return {\n type: 'content.aiJobNeedsYou',\n level: 'warning',\n title: `Your ${noun === 'AI job' ? 'AI job' : `${noun} job`} needs you`,\n // The doctrine's own words never reach a customer (AGL-3596).\n body: withRefund(\n job,\n aiCustomerSafeCopy(job.review?.message || job.error || 'It stopped for your decision. It waits in AI jobs.', { page: job.kind === 'page' }),\n 'needs_review',\n ),\n link,\n ...scope,\n }\n }\n if (to === 'done') {\n // A job that finished with part of it unbuilt (AGL-3596) says so in the\n // words its page uses, as a warning: \"done\" alone hid a page that failed.\n const items = job.items ?? []\n const unbuilt = items.some((row) => row.status === 'failed' || row.status === 'skipped')\n const level = unbuilt ? 'warning' : 'success'\n const partial =\n job.kind === 'site'\n ? aiSitePartialCopy({ items, status: 'done' })\n : unbuilt\n ? aiBuildOutcomeLine({ items, status: 'done' })\n : null\n // A guided site start publishes its pages when it finishes (AGL-3596):\n // with one live, the site is, and the build page lists any that stayed\n // drafts with the reason for each.\n const live = job.kind === 'site' ? (job.sitePublish?.published.length ?? 0) : 0\n if (live > 0) {\n const drafts = job.sitePublish?.drafts.length ?? 0\n const stayed = drafts === 1 ? 'one that stayed a draft' : `${drafts} that stayed drafts`\n return {\n type: 'content.aiJobDone',\n level,\n title: partial ? 'Your site is live, but part of it wasn’t built' : 'Your site is live',\n body: partial\n ? `${partial} ${drafts > 0 ? `What was built is published, except ${stayed}.` : 'What was built is published.'}`\n : drafts > 0\n ? `Your pages are published, except ${stayed}. Open it to see why.`\n : 'Your pages are published. Open it to view your site or edit your pages.',\n link,\n ...scope,\n }\n }\n const pages = (job.outputs ?? []).filter((output) => output.resource === 'screen').length\n const title =\n job.kind === 'site' || pages > 1\n ? 'Your site’s draft pages are ready'\n : job.kind === 'page'\n ? 'Your draft page is ready'\n : noun === 'AI job'\n ? 'Your AI job finished'\n : `Your ${noun} is ready`\n return {\n type: 'content.aiJobDone',\n level,\n title,\n body:\n job.kind === 'insight' || job.kind === 'text'\n ? 'Open AI jobs to read it.'\n : [partial, 'Everything it built is an unpublished draft until you publish it.'].filter(Boolean).join(' '),\n link,\n ...scope,\n }\n }\n return {\n type: 'content.aiJobFailed',\n level: 'warning',\n title: `Your ${noun === 'AI job' ? 'AI job' : `${noun} job`} stopped`,\n body: withRefund(job, aiCustomerSafeCopy(job.error || 'It stopped before it finished.', { page: job.kind === 'page' }), 'failed'),\n link,\n ...scope,\n }\n}\n"],"names":["aiBuildOutcomeLine","aiSitePartialCopy","aiJobKindNoun","aiCustomerSafeCopy","aiJobRefundCopy","AI_JOB_BESIGNER_SEGMENT","screen","reusableComponent","layout","template","emailScreen","AI_JOB_LINK_PARAM","AI_SITE_BUILD_HREF","withRefund","job","text","status","refund","refundedCredits","creditsSpent","aiJobNoticeLink","to","base","hostId","kind","encodeURIComponent","$id","own","outputs","filter","output","screens","resource","length","versioned","find","versionId","segment","id","aiJobNotice","noun","scope","orgId","link","review","reason","type","level","title","body","message","error","page","items","unbuilt","some","row","partial","live","sitePublish","published","drafts","stayed","pages","Boolean","join"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SAASA,kBAAkB,EAAEC,iBAAiB,QAAQ,yBAAqB;AAC3E,SAASC,aAAa,QAAQ,uBAAmB;AACjD,SAASC,kBAAkB,EAAEC,eAAe,QAAQ,2BAAuB;AAG3E,8DAA8D,GAC9D,OAAO,MAAMC,0BAA4E;IACvFC,QAAQ;IACRC,mBAAmB;IACnBC,QAAQ;IACRC,UAAU;IACV,uEAAuE;IACvE,sCAAsC;IACtCC,aAAa;AACf,EAAC;AAED;;;CAGC,GACD,OAAO,MAAMC,oBAAoB,QAAO;AAExC,+EAA+E,GAC/E,OAAO,MAAMC,qBAAqB,WAAU;AAuB5C,4GAA4G,GAC5G,SAASC,WAAWC,GAAsB,EAAEC,IAAY,EAAEC,MAAiC;IACzF,MAAMC,SAASb,gBAAgB;QAAEY;QAAQE,iBAAiBJ,IAAII,eAAe;QAAEC,cAAcL,IAAIK,YAAY;IAAC;IAC9G,OAAOF,SAAS,GAAGF,KAAK,CAAC,EAAEE,QAAQ,GAAGF;AACxC;AAOA;;;;;;;;;;CAUC,GACD,OAAO,SAASK,gBAAgBN,GAAsB,EAAEO,EAAyB;IAC/E,MAAMC,OAAOR,IAAIS,MAAM,GAAG,CAAC,CAAC,EAAET,IAAIS,MAAM,EAAE,GAAG;IAC7C,4EAA4E;IAC5E,2DAA2D;IAC3D,IAAIT,IAAIU,IAAI,KAAK,UAAUV,IAAIS,MAAM,EAAE,OAAO,GAAGD,OAAOV,mBAAmB,CAAC,EAAEa,mBAAmBX,IAAIY,GAAG,GAAG;IAC3G,IAAIL,OAAO,UAAUP,IAAIS,MAAM,EAAE;YAClBT;QAAb,MAAMa,MAAM,EAACb,eAAAA,IAAIc,OAAO,YAAXd,eAAe,EAAE,EAAEe,MAAM,CAAC,CAACC,SAAWA,OAAOP,MAAM,KAAKT,IAAIS,MAAM;QAC/E,MAAMQ,UAAUJ,IAAIE,MAAM,CAAC,CAACC,SAAWA,OAAOE,QAAQ,KAAK;QAC3D,IAAID,QAAQE,MAAM,GAAG,GAAG,OAAO,GAAGX,KAAK,QAAQ,CAAC;QAChD,MAAMY,YAAYP,IAAIQ,IAAI,CACxB,CAACL,SAAWzB,uBAAuB,CAACyB,OAAOE,QAAQ,CAAC,IAAIF,OAAOM,SAAS;QAE1E,IAAIT,IAAIM,MAAM,KAAK,KAAKC,WAAW;YACjC,MAAMG,UAAUhC,uBAAuB,CAAC6B,UAAUF,QAAQ,CAAC;YAC3D,OAAO,GAAGV,KAAK,CAAC,EAAEe,QAAQ,CAAC,EAAEH,UAAUI,EAAE,CAAC,UAAU,EAAEJ,UAAUE,SAAS,CAAC,SAAS,CAAC;QACtF;IACF;IACA,OAAO,GAAGd,KAAK,CAAC,EAAEX,kBAAkB,CAAC,EAAEc,mBAAmBX,IAAIY,GAAG,GAAG;AACtE;AAEA;;;;CAIC,GACD,OAAO,SAASa,YAAYzB,GAAsB,EAAEO,EAAyB;IAC3E,MAAMmB,OAAOtC,cAAcY,IAAIU,IAAI;IACnC,MAAMiB,QAAQ;QAAEC,OAAO5B,IAAI4B,KAAK;OAAM5B,IAAIS,MAAM,GAAG;QAAEA,QAAQT,IAAIS,MAAM;IAAC,IAAI,CAAC;IAC7E,MAAMoB,OAAOvB,gBAAgBN,KAAKO;IAClC,IAAIA,OAAO,gBAAgB;YACrBP,aAoBmBA;QApBvB,IAAIA,EAAAA,cAAAA,IAAI8B,MAAM,qBAAV9B,YAAY+B,MAAM,MAAK,QAAQ;YACjC,OAAO;gBACLC,MAAM;gBACNC,OAAO;gBACPC,OACER,SAAS,WACL,4CACA,CAAC,KAAK,EAAEA,KAAK,mCAAmC,CAAC;gBACvDS,MAAM;gBACNN;eACGF;QAEP;QACA,OAAO;YACLK,MAAM;YACNC,OAAO;YACPC,OAAO,CAAC,KAAK,EAAER,SAAS,WAAW,WAAW,GAAGA,KAAK,IAAI,CAAC,CAAC,UAAU,CAAC;YACvE,8DAA8D;YAC9DS,MAAMpC,WACJC,KACAX,mBAAmBW,EAAAA,eAAAA,IAAI8B,MAAM,qBAAV9B,aAAYoC,OAAO,KAAIpC,IAAIqC,KAAK,IAAI,sDAAsD;gBAAEC,MAAMtC,IAAIU,IAAI,KAAK;YAAO,IACzI;YAEFmB;WACGF;IAEP;IACA,IAAIpB,OAAO,QAAQ;YAGHP,kBA6BCA;YAjBqBA;QAdpC,wEAAwE;QACxE,0EAA0E;QAC1E,MAAMuC,SAAQvC,aAAAA,IAAIuC,KAAK,YAATvC,aAAa,EAAE;QAC7B,MAAMwC,UAAUD,MAAME,IAAI,CAAC,CAACC,MAAQA,IAAIxC,MAAM,KAAK,YAAYwC,IAAIxC,MAAM,KAAK;QAC9E,MAAM+B,QAAQO,UAAU,YAAY;QACpC,MAAMG,UACJ3C,IAAIU,IAAI,KAAK,SACTvB,kBAAkB;YAAEoD;YAAOrC,QAAQ;QAAO,KAC1CsC,UACEtD,mBAAmB;YAAEqD;YAAOrC,QAAQ;QAAO,KAC3C;QACR,uEAAuE;QACvE,uEAAuE;QACvE,mCAAmC;QACnC,MAAM0C,OAAO5C,IAAIU,IAAI,KAAK,kBAAUV,mBAAAA,IAAI6C,WAAW,qBAAf7C,iBAAiB8C,SAAS,CAAC3B,MAAM,mBAAI,IAAK;QAC9E,IAAIyB,OAAO,GAAG;;gBACG5C;YAAf,MAAM+C,mBAAS/C,oBAAAA,IAAI6C,WAAW,qBAAf7C,kBAAiB+C,MAAM,CAAC5B,MAAM,oBAAI;YACjD,MAAM6B,SAASD,WAAW,IAAI,4BAA4B,GAAGA,OAAO,mBAAmB,CAAC;YACxF,OAAO;gBACLf,MAAM;gBACNC;gBACAC,OAAOS,UAAU,mDAAmD;gBACpER,MAAMQ,UACF,GAAGA,QAAQ,CAAC,EAAEI,SAAS,IAAI,CAAC,oCAAoC,EAAEC,OAAO,CAAC,CAAC,GAAG,gCAAgC,GAC9GD,SAAS,IACP,CAAC,iCAAiC,EAAEC,OAAO,qBAAqB,CAAC,GACjE;gBACNnB;eACGF;QAEP;QACA,MAAMsB,QAAQ,EAACjD,eAAAA,IAAIc,OAAO,YAAXd,eAAe,EAAE,EAAEe,MAAM,CAAC,CAACC,SAAWA,OAAOE,QAAQ,KAAK,UAAUC,MAAM;QACzF,MAAMe,QACJlC,IAAIU,IAAI,KAAK,UAAUuC,QAAQ,IAC3B,sCACAjD,IAAIU,IAAI,KAAK,SACX,6BACAgB,SAAS,WACP,yBACA,CAAC,KAAK,EAAEA,KAAK,SAAS,CAAC;QACjC,OAAO;YACLM,MAAM;YACNC;YACAC;YACAC,MACEnC,IAAIU,IAAI,KAAK,aAAaV,IAAIU,IAAI,KAAK,SACnC,6BACA;gBAACiC;gBAAS;aAAoE,CAAC5B,MAAM,CAACmC,SAASC,IAAI,CAAC;YAC1GtB;WACGF;IAEP;IACA,OAAO;QACLK,MAAM;QACNC,OAAO;QACPC,OAAO,CAAC,KAAK,EAAER,SAAS,WAAW,WAAW,GAAGA,KAAK,IAAI,CAAC,CAAC,QAAQ,CAAC;QACrES,MAAMpC,WAAWC,KAAKX,mBAAmBW,IAAIqC,KAAK,IAAI,kCAAkC;YAAEC,MAAMtC,IAAIU,IAAI,KAAK;QAAO,IAAI;QACxHmB;OACGF;AAEP"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-job-notice.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 type { AglynNotification } from '@aglyn/aglyn/app-utils/notifications'\nimport { aiBuildOutcomeLine, aiSitePartialCopy } from './ai-build-progress'\nimport { aiJobKindNoun } from './ai-job-activity'\nimport { aiCustomerSafeCopy, aiJobRefundCopy } from './ai-job-failure-copy'\nimport type { AiJobItemLedger, AiJobKind, AiJobOutput, AiJobReviewReason, AiJobSitePublish } from './ai-jobs.types'\n\n/** The besigner segment each versioned resource lives under. */\nexport const AI_JOB_BESIGNER_SEGMENT: Partial<Record<AiJobOutput['resource'], string>> = {\n screen: 'screens',\n reusableComponent: 'components',\n layout: 'layouts',\n template: 'templates',\n // An email design is a screen: it opens in the screen besigner, as the\n // Emails page's own Edit design does.\n emailScreen: 'screens',\n}\n\n/**\n * The query parameter a console address carries to open AI jobs on one job\n * (AGL-3593): the Assist panel reads it on arrival.\n */\nexport const AI_JOB_LINK_PARAM = 'aiJob'\n\n/** Where a site's \"Building your site\" page lives, under the site (AGL-3594). */\nexport const AI_SITE_BUILD_HREF = '/ai-jobs'\n\n/**\n * What a job the meter paused says comes next (AGL-3660), on its notice and\n * its page alike: nothing it built is lost, and it carries on from where it\n * stopped — by Resume once there are credits, or by itself when they return.\n */\nexport const AI_JOB_PAUSED_NEXT_COPY =\n 'Nothing is lost: what it built so far is saved. Once you have AI credits again — from Billing, ' +\n 'or when your monthly credits reset — choose Resume, or it carries on by itself within about an hour.'\n\n/** A paused job's heading, by what it builds. */\nexport function aiJobPausedTitle(kind: AiJobKind): string {\n const noun = aiJobKindNoun(kind)\n return noun === 'AI job' ? 'Your AI job is paused' : `Your ${noun} ${noun.endsWith('s') ? 'are' : 'is'} paused`\n}\n\n/** A change the person who started a job is told about. */\nexport type AiJobNoticeTransition = 'needs-review' | 'done' | 'failed' | 'paused'\n\n/** The fields of a stored job a notice is written from. */\nexport interface AiJobNoticeSource {\n $id: string\n orgId: string\n hostId?: string | null\n kind: AiJobKind\n review?: { reason: AiJobReviewReason; message?: string | null } | null\n outputs?: readonly AiJobOutput[] | null\n error?: string | null\n /** What a guided site start put live (AGL-3596); absent on every other job. */\n sitePublish?: Pick<AiJobSitePublish, 'published' | 'drafts'> | null\n /** What the job spent, and gave back for a failure on our side (AGL-3596). */\n creditsSpent?: number\n refundedCredits?: number\n /** A build's or a guided start's items (AGL-3616), so a finished one says what was not built. */\n items?: AiJobItemLedger[] | null\n}\n\n/** A stopped job's sentence with what became of its credits, from the job's recorded give-back (AGL-3596). */\nfunction withRefund(job: AiJobNoticeSource, text: string, status: 'failed' | 'needs_review'): string {\n const refund = aiJobRefundCopy({ status, refundedCredits: job.refundedCredits, creditsSpent: job.creditsSpent })\n return refund ? `${text} ${refund}` : text\n}\n\nexport type AiJobNotice = Pick<\n AglynNotification,\n 'type' | 'title' | 'body' | 'link' | 'orgId' | 'hostId' | 'level'\n>\n\n/**\n * Where a notice's link goes, in the stored form the console rewrites when it\n * is followed (`normalizeNotificationLink`): `/{hostId}/…` for the job's site,\n * `/org…` for a job of the workspace's.\n *\n * A job that is done leads to what it built (AGL-3593): several pages of the\n * job's site open that site's Pages list, one versioned output opens in the\n * editor on the version the job wrote. Everything else — a plan to confirm, a\n * job that stopped, a done job with nothing of its own to open — opens AI\n * jobs on the job.\n */\nexport function aiJobNoticeLink(job: AiJobNoticeSource, to: AiJobNoticeTransition): string {\n const base = job.hostId ? `/${job.hostId}` : '/org'\n // A site job's every notice opens its \"Building your site\" page (AGL-3594),\n // which leads with the next step whatever the job's state.\n if (job.kind === 'site' && job.hostId) return `${base}${AI_SITE_BUILD_HREF}/${encodeURIComponent(job.$id)}`\n if (to === 'done' && job.hostId) {\n const own = (job.outputs ?? []).filter((output) => output.hostId === job.hostId)\n const screens = own.filter((output) => output.resource === 'screen')\n if (screens.length > 1) return `${base}/screens`\n const versioned = own.find(\n (output) => AI_JOB_BESIGNER_SEGMENT[output.resource] && output.versionId,\n )\n if (own.length === 1 && versioned) {\n const segment = AI_JOB_BESIGNER_SEGMENT[versioned.resource]\n return `${base}/${segment}/${versioned.id}/versions/${versioned.versionId}/besigner`\n }\n }\n return `${base}?${AI_JOB_LINK_PARAM}=${encodeURIComponent(job.$id)}`\n}\n\n/**\n * The in-app notification for one change of one job (AGL-3593), for the\n * person who created it. Customer-safe throughout: a stopped job's sentence is\n * the job's own `error`, which the machine writes as a fixed sentence.\n */\nexport function aiJobNotice(job: AiJobNoticeSource, to: AiJobNoticeTransition): AiJobNotice {\n const noun = aiJobKindNoun(job.kind)\n const scope = { orgId: job.orgId, ...(job.hostId ? { hostId: job.hostId } : {}) }\n const link = aiJobNoticeLink(job, to)\n if (to === 'needs-review') {\n if (job.review?.reason === 'plan') {\n return {\n type: 'content.aiJobNeedsYou',\n level: 'warning',\n title:\n noun === 'AI job'\n ? 'Your plan is ready: confirm it to build'\n : `Your ${noun} plan is ready: confirm it to build`,\n body: 'Nothing is built until you confirm the plan. It waits in AI jobs.',\n link,\n ...scope,\n }\n }\n return {\n type: 'content.aiJobNeedsYou',\n level: 'warning',\n title: `Your ${noun === 'AI job' ? 'AI job' : `${noun} job`} needs you`,\n // The doctrine's own words never reach a customer (AGL-3596).\n body: withRefund(\n job,\n aiCustomerSafeCopy(job.review?.message || job.error || 'It stopped for your decision. It waits in AI jobs.', { page: job.kind === 'page' }),\n 'needs_review',\n ),\n link,\n ...scope,\n }\n }\n if (to === 'paused') {\n // Paused by the meter (AGL-3660): out of credits or at a cap. Nothing is\n // lost and the job is not over, so it is a warning that names the way on.\n return {\n type: 'content.aiJobNeedsYou',\n level: 'warning',\n title: aiJobPausedTitle(job.kind),\n body: [job.error, AI_JOB_PAUSED_NEXT_COPY].filter(Boolean).join(' '),\n link,\n ...scope,\n }\n }\n if (to === 'done') {\n // A job that finished with part of it unbuilt (AGL-3596) says so in the\n // words its page uses, as a warning: \"done\" alone hid a page that failed.\n const items = job.items ?? []\n const unbuilt = items.some((row) => row.status === 'failed' || row.status === 'skipped')\n const level = unbuilt ? 'warning' : 'success'\n const partial =\n job.kind === 'site'\n ? aiSitePartialCopy({ items, status: 'done' })\n : unbuilt\n ? aiBuildOutcomeLine({ items, status: 'done' })\n : null\n // A guided site start publishes its pages when it finishes (AGL-3596):\n // with one live, the site is, and the build page lists any that stayed\n // drafts with the reason for each.\n const live = job.kind === 'site' ? (job.sitePublish?.published.length ?? 0) : 0\n if (live > 0) {\n const drafts = job.sitePublish?.drafts.length ?? 0\n const stayed = drafts === 1 ? 'one that stayed a draft' : `${drafts} that stayed drafts`\n return {\n type: 'content.aiJobDone',\n level,\n title: partial ? 'Your site is live, but part of it wasn’t built' : 'Your site is live',\n body: partial\n ? `${partial} ${drafts > 0 ? `What was built is published, except ${stayed}.` : 'What was built is published.'}`\n : drafts > 0\n ? `Your pages are published, except ${stayed}. Open it to see why.`\n : 'Your pages are published. Open it to view your site or edit your pages.',\n link,\n ...scope,\n }\n }\n const pages = (job.outputs ?? []).filter((output) => output.resource === 'screen').length\n const title =\n job.kind === 'site' || pages > 1\n ? 'Your site’s draft pages are ready'\n : job.kind === 'page'\n ? 'Your draft page is ready'\n : noun === 'AI job'\n ? 'Your AI job finished'\n : `Your ${noun} is ready`\n return {\n type: 'content.aiJobDone',\n level,\n title,\n body:\n job.kind === 'insight' || job.kind === 'text'\n ? 'Open AI jobs to read it.'\n : [partial, 'Everything it built is an unpublished draft until you publish it.'].filter(Boolean).join(' '),\n link,\n ...scope,\n }\n }\n return {\n type: 'content.aiJobFailed',\n level: 'warning',\n title: `Your ${noun === 'AI job' ? 'AI job' : `${noun} job`} stopped`,\n body: withRefund(job, aiCustomerSafeCopy(job.error || 'It stopped before it finished.', { page: job.kind === 'page' }), 'failed'),\n link,\n ...scope,\n }\n}\n"],"names":["aiBuildOutcomeLine","aiSitePartialCopy","aiJobKindNoun","aiCustomerSafeCopy","aiJobRefundCopy","AI_JOB_BESIGNER_SEGMENT","screen","reusableComponent","layout","template","emailScreen","AI_JOB_LINK_PARAM","AI_SITE_BUILD_HREF","AI_JOB_PAUSED_NEXT_COPY","aiJobPausedTitle","kind","noun","endsWith","withRefund","job","text","status","refund","refundedCredits","creditsSpent","aiJobNoticeLink","to","base","hostId","encodeURIComponent","$id","own","outputs","filter","output","screens","resource","length","versioned","find","versionId","segment","id","aiJobNotice","scope","orgId","link","review","reason","type","level","title","body","message","error","page","Boolean","join","items","unbuilt","some","row","partial","live","sitePublish","published","drafts","stayed","pages"],"mappings":"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"}
@@ -93,6 +93,13 @@ export interface AiPlanCapabilities {
93
93
  * calls a repeat. A plan is then not asked for a component for a section's
94
94
  * repeated items (AGL-3616); a section two pages share still is one. The
95
95
  * plan step sets it for a `build` job. Absent is `false`.
96
+ *
97
+ * It is the plan half of one rule. The page half is the tree check's
98
+ * `AiDoctrineTreeContext.repeatsCompiled`, which every page the layout
99
+ * language compiles is checked with: where the site's design draws a
100
+ * section's items as picture cards — larger than the compact form, and so
101
+ * a block rule 1 would count — those cards are the compiler's, not a block
102
+ * typed out twice, and rule 1 asks no component of them (AGL-3660).
96
103
  */
97
104
  repeatsCompiled?: boolean;
98
105
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-plan-capabilities.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AI_BUILD_PLAN_CREATE_KINDS,\n AI_BUILD_PLAN_CREATION_NOUNS,\n type AiBuildPlan,\n type AiBuildPlanCreateKind,\n} from './ai-build-plan'\n\n/**\n * What a plan may create on one site (AGL-3030): the workspace's plan and the\n * site's counts against its limits, narrowed to what the job itself builds.\n *\n * A plan that names a creation nothing can make is a dead end the member pays\n * for twice: once for the plan, and again for the plan they describe after\n * being told to make the creation by hand — which, on a workspace whose plan\n * does not include it, they cannot. So the plan step is TOLD what may be\n * created before it answers, and the plan rules refuse a creation outside it\n * with the one re-ask every other rule gets.\n *\n * ── Where the doctrine builds inline ─────────────────────────────────────\n *\n * The building doctrine makes a repeat a reusable component and a form a\n * saved form placed by id. A workspace whose plan keeps no reusable\n * components draws a list's repeated items in one section instead\n * (AGL-3071); `reusableComponents` is the one flag every validator reads for\n * that.\n *\n * A form is never drawn inline (AGL-3596): it is a saved form, counted\n * against the site's form allowance like one a member makes. Where the site\n * has none to place and may create none, the page places no form at all.\n *\n * ── Kept out of the cached prefix ────────────────────────────────────────\n *\n * The capabilities are per workspace and per site, so they ride the plan's\n * USER turn, never a system block: the doctrine's cached prefix stays one\n * entry for the platform. The doctrine states the rule once — create only\n * what the request says may be created, and build inline where it says the\n * workspace keeps no reusable components — and the request states the facts.\n *\n * This module imports nothing at runtime beyond the plan's own model, so the\n * validators, the plan step, the doors and their specs read one vocabulary.\n * What a workspace's plan and counts come to is read by\n * `readAiPlanCapabilities` in `jobs/ai-job-drafts.ts`, beside the band\n * arithmetic the create route and the draft writer share.\n */\n\n/** One creation kind, as a job on this site may make it or not. */\nexport interface AiPlanCreation {\n allowed: boolean\n /** How many more the site may hold; `null` when the plan counts none. */\n left: number | null\n /**\n * Why not, as a clause a sentence completes: \"this workspace's plan does\n * not include reusable components\". `null` when allowed.\n */\n reason: string | null\n}\n\nexport interface AiPlanCapabilities {\n /**\n * Whether the workspace keeps reusable components. Where it does not, a\n * list's repeated items are drawn in one section. Forms are not read off\n * it: whether one may be created is `create.form` (AGL-3596).\n */\n reusableComponents: boolean\n /** Every creation kind a plan may name, and whether this job may make one here. */\n create: Readonly<Record<AiBuildPlanCreateKind, AiPlanCreation>>\n /**\n * Whether the workspace spends the Free taste, whose monthly credits are a\n * wall (AGL-2925): a plan is then held to the sections the wall's worst\n * case pays for (AGL-3070). Absent is `false`.\n */\n freeTaste?: boolean\n /**\n * A Free workspace's site scaffold (AGL-3594): the most pages its plan may\n * build, which the plan step sets for a `site` job on the Free taste. The\n * Free wall then holds the plan to that many pages and to the sections a\n * SITE plan's worst case leaves room for. Absent for every other job.\n */\n freeSitePages?: number\n /**\n * Whether this job's pages are written in the compact layout language\n * (AGL-3660), whose compiler draws a section's repeated items itself: as\n * instances of a component the plan places there, or, with none, in their\n * compact form, a title over its text, which is smaller than a block rule 1\n * calls a repeat. A plan is then not asked for a component for a section's\n * repeated items (AGL-3616); a section two pages share still is one. The\n * plan step sets it for a `build` job. Absent is `false`.\n */\n repeatsCompiled?: boolean\n}\n\n/** What a job of one kind builds from its own plan, where it builds only some creations. */\nexport interface AiPlanJobScope {\n /** The job as a refusal names it: \"a page job\". */\n noun: string\n /** The creation kinds the job builds itself; any other is refused before the plan is kept. */\n creates: readonly AiBuildPlanCreateKind[]\n}\n\nconst ALLOWED: AiPlanCreation = { allowed: true, left: null, reason: null }\n\n/**\n * Capabilities that restrict nothing: what a plan is held to when no\n * workspace was read — an org-level job, an eval, a spec about something\n * else. The doctrine applies whole.\n */\nexport function aiUnrestrictedPlanCapabilities(): AiPlanCapabilities {\n return {\n reusableComponents: true,\n create: Object.fromEntries(\n AI_BUILD_PLAN_CREATE_KINDS.map((kind) => [kind, ALLOWED]),\n ) as Record<AiBuildPlanCreateKind, AiPlanCreation>,\n }\n}\n\n/**\n * The workspace's capabilities narrowed to what a job of one kind builds.\n * A creation the workspace itself cannot make keeps the workspace's reason,\n * which is the one a member can act on; one the job does not build says so.\n */\nexport function aiPlanCapabilitiesForJob(\n capabilities: AiPlanCapabilities,\n scope: AiPlanJobScope | null | undefined,\n): AiPlanCapabilities {\n if (!scope) return capabilities\n const create = { ...capabilities.create }\n for (const kind of AI_BUILD_PLAN_CREATE_KINDS) {\n if (!create[kind].allowed || scope.creates.includes(kind)) continue\n create[kind] = { allowed: false, left: null, reason: `${scope.noun} does not build one` }\n }\n return { ...capabilities, create }\n}\n\n/** \"a layout\", \"an email design\": a creation kind with its article. */\nexport function aiCreationNoun(kind: AiBuildPlanCreateKind): string {\n const noun = AI_BUILD_PLAN_CREATION_NOUNS[kind].noun\n return `${/^[aeiou]/.test(noun) ? 'an' : 'a'} ${noun}`\n}\n\n/**\n * The sentence a request states when the workspace keeps no reusable\n * components. It says a list's repeated items go in one section (AGL-3071):\n * \"a repeated item in its own section\" reads just as well as a section for\n * each item, which is how a live About page's four practice areas came to be\n * planned as four sections of one item.\n */\nexport const AI_PLAN_INLINE_SENTENCE =\n \"This workspace keeps no reusable components: draw a list's repeated items in one section.\"\n\n/**\n * The sentence a build's request states (AGL-3616): its pages are written in\n * the layout language, which draws a section's repeated items itself, so the\n * plan counts them in the section's `items` and needs no component for them.\n */\nexport const AI_PLAN_COMPILED_REPEATS_SENTENCE =\n \"A section's repeated items need no component here: count them in its items and the page draws them. A section two pages share is still one component.\"\n\n/**\n * The sentence a request states when the site has no saved form and this job\n * may create none (AGL-3596): the plan places no form, rather than one drawn\n * on the page with no saved form behind it.\n */\nexport const AI_PLAN_NO_FORM_SENTENCE =\n 'This site has no saved form and may create none: plan no form, and no section that collects answers.'\n\n/**\n * What the plan step's user turn says the job may create here: one line a\n * kind, and the inline sentence where the workspace keeps no reusable\n * components. Per workspace and per site, so it is never a system block.\n */\nexport function aiPlanCapabilityLines(capabilities: AiPlanCapabilities): string[] {\n const lines = ['What this job may create on this site:']\n for (const kind of AI_BUILD_PLAN_CREATE_KINDS) {\n const entry = capabilities.create[kind]\n const noun = AI_BUILD_PLAN_CREATION_NOUNS[kind].noun\n if (entry.allowed) {\n lines.push(`- ${noun}: yes${entry.left === null ? '' : `, ${entry.left} more`}`)\n } else {\n lines.push(`- ${noun}: no, because ${entry.reason ?? 'it cannot be made here'}`)\n }\n }\n if (!capabilities.reusableComponents) lines.push(AI_PLAN_INLINE_SENTENCE)\n return lines\n}\n\n/**\n * Whether a plan on this site can place a saved form at all (AGL-3596): the\n * site has one, or the job may create one. Absent capabilities restrict\n * nothing, as everywhere else.\n */\nexport function aiPlanCanPlaceForm(\n inventory: { forms: readonly unknown[] } | null,\n capabilities: Pick<AiPlanCapabilities, 'create'> | null,\n): boolean {\n if (inventory?.forms.length) return true\n return capabilities?.create.form.allowed !== false\n}\n\n/** What to do instead of a creation the job may not make, for the re-ask and the review. */\nfunction insteadOf(kind: AiBuildPlanCreateKind, capabilities: AiPlanCapabilities): string {\n switch (kind) {\n case 'component':\n return capabilities.reusableComponents\n ? 'Place a component the site already has.'\n : \"Draw a list's repeated items in one section instead.\"\n case 'form':\n return 'Place a form the site already has, or leave the form off the page.'\n case 'layout':\n return 'Put the page in a layout the site already has.'\n case 'template':\n return 'Build the page without a template.'\n case 'theme-change':\n return \"Use the colors the theme already has.\"\n case 'dataset':\n return 'Bind a dataset or collection the site already has, or keep the list short enough to write out.'\n case 'email':\n return 'Leave the email out of this job.'\n }\n}\n\n/** One creation a plan names that this job may not make here. */\nexport interface AiPlanUncreatable {\n kind: AiBuildPlanCreateKind\n name: string\n /** The plan path of the creation: `create[2]`. */\n path: string\n /** Why not, as a clause: \"this site has room for 1 more\". */\n reason: string\n /** One customer-safe sentence: what was refused, why, and what to do instead. */\n message: string\n}\n\n/**\n * Why the nth creation of a kind may not be made here, as a clause; `null`\n * when it may. A kind the site may make only so many more of refuses the ones\n * past that count, not the first of them.\n */\nfunction refusedReason(capability: AiPlanCreation, nth: number): string | null {\n const overCount = capability.allowed && capability.left !== null && nth > capability.left\n if (capability.allowed && !overCount) return null\n return overCount\n ? `this site has room for ${capability.left} more`\n : (capability.reason ?? 'it cannot be made here')\n}\n\n/** The creations a plan names that the capabilities refuse, in plan order. */\nexport function aiPlanUncreatable(\n plan: Pick<AiBuildPlan, 'create'>,\n capabilities: AiPlanCapabilities,\n): AiPlanUncreatable[] {\n const refused: AiPlanUncreatable[] = []\n const counted = new Map<AiBuildPlanCreateKind, number>()\n plan.create.forEach((entry, index) => {\n const nth = (counted.get(entry.kind) ?? 0) + 1\n counted.set(entry.kind, nth)\n const reason = refusedReason(capabilities.create[entry.kind], nth)\n if (reason === null) return\n refused.push({\n kind: entry.kind,\n name: entry.name,\n path: `create[${index}]`,\n reason,\n message: `The plan creates ${aiCreationNoun(entry.kind)} named \"${entry.name}\", and ${reason}. ${insteadOf(entry.kind, capabilities)}`,\n })\n })\n return refused\n}\n\n/**\n * Why the plan could not add one more creation of a kind here, and what to\n * do instead; `null` when it could (AGL-3040). Counted past the creations of\n * that kind the plan already names, as `aiPlanUncreatable` counts them, so a\n * plan told to declare a creation is never told to declare one the next\n * answer is refused for.\n */\nexport function aiPlanUncreatableKind(\n plan: Pick<AiBuildPlan, 'create'>,\n kind: AiBuildPlanCreateKind,\n capabilities: AiPlanCapabilities,\n): { reason: string; instead: string } | null {\n const planned = plan.create.filter((entry) => entry.kind === kind).length\n const reason = refusedReason(capabilities.create[kind], planned + 1)\n return reason === null ? null : { reason, instead: insteadOf(kind, capabilities) }\n}\n"],"names":["AI_BUILD_PLAN_CREATE_KINDS","AI_BUILD_PLAN_CREATION_NOUNS","ALLOWED","allowed","left","reason","aiUnrestrictedPlanCapabilities","reusableComponents","create","Object","fromEntries","map","kind","aiPlanCapabilitiesForJob","capabilities","scope","creates","includes","noun","aiCreationNoun","test","AI_PLAN_INLINE_SENTENCE","AI_PLAN_COMPILED_REPEATS_SENTENCE","AI_PLAN_NO_FORM_SENTENCE","aiPlanCapabilityLines","lines","entry","push","aiPlanCanPlaceForm","inventory","forms","length","form","insteadOf","refusedReason","capability","nth","overCount","aiPlanUncreatable","plan","refused","counted","Map","forEach","index","get","set","name","path","message","aiPlanUncreatableKind","planned","filter","instead"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-plan-capabilities.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AI_BUILD_PLAN_CREATE_KINDS,\n AI_BUILD_PLAN_CREATION_NOUNS,\n type AiBuildPlan,\n type AiBuildPlanCreateKind,\n} from './ai-build-plan'\n\n/**\n * What a plan may create on one site (AGL-3030): the workspace's plan and the\n * site's counts against its limits, narrowed to what the job itself builds.\n *\n * A plan that names a creation nothing can make is a dead end the member pays\n * for twice: once for the plan, and again for the plan they describe after\n * being told to make the creation by hand — which, on a workspace whose plan\n * does not include it, they cannot. So the plan step is TOLD what may be\n * created before it answers, and the plan rules refuse a creation outside it\n * with the one re-ask every other rule gets.\n *\n * ── Where the doctrine builds inline ─────────────────────────────────────\n *\n * The building doctrine makes a repeat a reusable component and a form a\n * saved form placed by id. A workspace whose plan keeps no reusable\n * components draws a list's repeated items in one section instead\n * (AGL-3071); `reusableComponents` is the one flag every validator reads for\n * that.\n *\n * A form is never drawn inline (AGL-3596): it is a saved form, counted\n * against the site's form allowance like one a member makes. Where the site\n * has none to place and may create none, the page places no form at all.\n *\n * ── Kept out of the cached prefix ────────────────────────────────────────\n *\n * The capabilities are per workspace and per site, so they ride the plan's\n * USER turn, never a system block: the doctrine's cached prefix stays one\n * entry for the platform. The doctrine states the rule once — create only\n * what the request says may be created, and build inline where it says the\n * workspace keeps no reusable components — and the request states the facts.\n *\n * This module imports nothing at runtime beyond the plan's own model, so the\n * validators, the plan step, the doors and their specs read one vocabulary.\n * What a workspace's plan and counts come to is read by\n * `readAiPlanCapabilities` in `jobs/ai-job-drafts.ts`, beside the band\n * arithmetic the create route and the draft writer share.\n */\n\n/** One creation kind, as a job on this site may make it or not. */\nexport interface AiPlanCreation {\n allowed: boolean\n /** How many more the site may hold; `null` when the plan counts none. */\n left: number | null\n /**\n * Why not, as a clause a sentence completes: \"this workspace's plan does\n * not include reusable components\". `null` when allowed.\n */\n reason: string | null\n}\n\nexport interface AiPlanCapabilities {\n /**\n * Whether the workspace keeps reusable components. Where it does not, a\n * list's repeated items are drawn in one section. Forms are not read off\n * it: whether one may be created is `create.form` (AGL-3596).\n */\n reusableComponents: boolean\n /** Every creation kind a plan may name, and whether this job may make one here. */\n create: Readonly<Record<AiBuildPlanCreateKind, AiPlanCreation>>\n /**\n * Whether the workspace spends the Free taste, whose monthly credits are a\n * wall (AGL-2925): a plan is then held to the sections the wall's worst\n * case pays for (AGL-3070). Absent is `false`.\n */\n freeTaste?: boolean\n /**\n * A Free workspace's site scaffold (AGL-3594): the most pages its plan may\n * build, which the plan step sets for a `site` job on the Free taste. The\n * Free wall then holds the plan to that many pages and to the sections a\n * SITE plan's worst case leaves room for. Absent for every other job.\n */\n freeSitePages?: number\n /**\n * Whether this job's pages are written in the compact layout language\n * (AGL-3660), whose compiler draws a section's repeated items itself: as\n * instances of a component the plan places there, or, with none, in their\n * compact form, a title over its text, which is smaller than a block rule 1\n * calls a repeat. A plan is then not asked for a component for a section's\n * repeated items (AGL-3616); a section two pages share still is one. The\n * plan step sets it for a `build` job. Absent is `false`.\n *\n * It is the plan half of one rule. The page half is the tree check's\n * `AiDoctrineTreeContext.repeatsCompiled`, which every page the layout\n * language compiles is checked with: where the site's design draws a\n * section's items as picture cards — larger than the compact form, and so\n * a block rule 1 would count — those cards are the compiler's, not a block\n * typed out twice, and rule 1 asks no component of them (AGL-3660).\n */\n repeatsCompiled?: boolean\n}\n\n/** What a job of one kind builds from its own plan, where it builds only some creations. */\nexport interface AiPlanJobScope {\n /** The job as a refusal names it: \"a page job\". */\n noun: string\n /** The creation kinds the job builds itself; any other is refused before the plan is kept. */\n creates: readonly AiBuildPlanCreateKind[]\n}\n\nconst ALLOWED: AiPlanCreation = { allowed: true, left: null, reason: null }\n\n/**\n * Capabilities that restrict nothing: what a plan is held to when no\n * workspace was read — an org-level job, an eval, a spec about something\n * else. The doctrine applies whole.\n */\nexport function aiUnrestrictedPlanCapabilities(): AiPlanCapabilities {\n return {\n reusableComponents: true,\n create: Object.fromEntries(\n AI_BUILD_PLAN_CREATE_KINDS.map((kind) => [kind, ALLOWED]),\n ) as Record<AiBuildPlanCreateKind, AiPlanCreation>,\n }\n}\n\n/**\n * The workspace's capabilities narrowed to what a job of one kind builds.\n * A creation the workspace itself cannot make keeps the workspace's reason,\n * which is the one a member can act on; one the job does not build says so.\n */\nexport function aiPlanCapabilitiesForJob(\n capabilities: AiPlanCapabilities,\n scope: AiPlanJobScope | null | undefined,\n): AiPlanCapabilities {\n if (!scope) return capabilities\n const create = { ...capabilities.create }\n for (const kind of AI_BUILD_PLAN_CREATE_KINDS) {\n if (!create[kind].allowed || scope.creates.includes(kind)) continue\n create[kind] = { allowed: false, left: null, reason: `${scope.noun} does not build one` }\n }\n return { ...capabilities, create }\n}\n\n/** \"a layout\", \"an email design\": a creation kind with its article. */\nexport function aiCreationNoun(kind: AiBuildPlanCreateKind): string {\n const noun = AI_BUILD_PLAN_CREATION_NOUNS[kind].noun\n return `${/^[aeiou]/.test(noun) ? 'an' : 'a'} ${noun}`\n}\n\n/**\n * The sentence a request states when the workspace keeps no reusable\n * components. It says a list's repeated items go in one section (AGL-3071):\n * \"a repeated item in its own section\" reads just as well as a section for\n * each item, which is how a live About page's four practice areas came to be\n * planned as four sections of one item.\n */\nexport const AI_PLAN_INLINE_SENTENCE =\n \"This workspace keeps no reusable components: draw a list's repeated items in one section.\"\n\n/**\n * The sentence a build's request states (AGL-3616): its pages are written in\n * the layout language, which draws a section's repeated items itself, so the\n * plan counts them in the section's `items` and needs no component for them.\n */\nexport const AI_PLAN_COMPILED_REPEATS_SENTENCE =\n \"A section's repeated items need no component here: count them in its items and the page draws them. A section two pages share is still one component.\"\n\n/**\n * The sentence a request states when the site has no saved form and this job\n * may create none (AGL-3596): the plan places no form, rather than one drawn\n * on the page with no saved form behind it.\n */\nexport const AI_PLAN_NO_FORM_SENTENCE =\n 'This site has no saved form and may create none: plan no form, and no section that collects answers.'\n\n/**\n * What the plan step's user turn says the job may create here: one line a\n * kind, and the inline sentence where the workspace keeps no reusable\n * components. Per workspace and per site, so it is never a system block.\n */\nexport function aiPlanCapabilityLines(capabilities: AiPlanCapabilities): string[] {\n const lines = ['What this job may create on this site:']\n for (const kind of AI_BUILD_PLAN_CREATE_KINDS) {\n const entry = capabilities.create[kind]\n const noun = AI_BUILD_PLAN_CREATION_NOUNS[kind].noun\n if (entry.allowed) {\n lines.push(`- ${noun}: yes${entry.left === null ? '' : `, ${entry.left} more`}`)\n } else {\n lines.push(`- ${noun}: no, because ${entry.reason ?? 'it cannot be made here'}`)\n }\n }\n if (!capabilities.reusableComponents) lines.push(AI_PLAN_INLINE_SENTENCE)\n return lines\n}\n\n/**\n * Whether a plan on this site can place a saved form at all (AGL-3596): the\n * site has one, or the job may create one. Absent capabilities restrict\n * nothing, as everywhere else.\n */\nexport function aiPlanCanPlaceForm(\n inventory: { forms: readonly unknown[] } | null,\n capabilities: Pick<AiPlanCapabilities, 'create'> | null,\n): boolean {\n if (inventory?.forms.length) return true\n return capabilities?.create.form.allowed !== false\n}\n\n/** What to do instead of a creation the job may not make, for the re-ask and the review. */\nfunction insteadOf(kind: AiBuildPlanCreateKind, capabilities: AiPlanCapabilities): string {\n switch (kind) {\n case 'component':\n return capabilities.reusableComponents\n ? 'Place a component the site already has.'\n : \"Draw a list's repeated items in one section instead.\"\n case 'form':\n return 'Place a form the site already has, or leave the form off the page.'\n case 'layout':\n return 'Put the page in a layout the site already has.'\n case 'template':\n return 'Build the page without a template.'\n case 'theme-change':\n return \"Use the colors the theme already has.\"\n case 'dataset':\n return 'Bind a dataset or collection the site already has, or keep the list short enough to write out.'\n case 'email':\n return 'Leave the email out of this job.'\n }\n}\n\n/** One creation a plan names that this job may not make here. */\nexport interface AiPlanUncreatable {\n kind: AiBuildPlanCreateKind\n name: string\n /** The plan path of the creation: `create[2]`. */\n path: string\n /** Why not, as a clause: \"this site has room for 1 more\". */\n reason: string\n /** One customer-safe sentence: what was refused, why, and what to do instead. */\n message: string\n}\n\n/**\n * Why the nth creation of a kind may not be made here, as a clause; `null`\n * when it may. A kind the site may make only so many more of refuses the ones\n * past that count, not the first of them.\n */\nfunction refusedReason(capability: AiPlanCreation, nth: number): string | null {\n const overCount = capability.allowed && capability.left !== null && nth > capability.left\n if (capability.allowed && !overCount) return null\n return overCount\n ? `this site has room for ${capability.left} more`\n : (capability.reason ?? 'it cannot be made here')\n}\n\n/** The creations a plan names that the capabilities refuse, in plan order. */\nexport function aiPlanUncreatable(\n plan: Pick<AiBuildPlan, 'create'>,\n capabilities: AiPlanCapabilities,\n): AiPlanUncreatable[] {\n const refused: AiPlanUncreatable[] = []\n const counted = new Map<AiBuildPlanCreateKind, number>()\n plan.create.forEach((entry, index) => {\n const nth = (counted.get(entry.kind) ?? 0) + 1\n counted.set(entry.kind, nth)\n const reason = refusedReason(capabilities.create[entry.kind], nth)\n if (reason === null) return\n refused.push({\n kind: entry.kind,\n name: entry.name,\n path: `create[${index}]`,\n reason,\n message: `The plan creates ${aiCreationNoun(entry.kind)} named \"${entry.name}\", and ${reason}. ${insteadOf(entry.kind, capabilities)}`,\n })\n })\n return refused\n}\n\n/**\n * Why the plan could not add one more creation of a kind here, and what to\n * do instead; `null` when it could (AGL-3040). Counted past the creations of\n * that kind the plan already names, as `aiPlanUncreatable` counts them, so a\n * plan told to declare a creation is never told to declare one the next\n * answer is refused for.\n */\nexport function aiPlanUncreatableKind(\n plan: Pick<AiBuildPlan, 'create'>,\n kind: AiBuildPlanCreateKind,\n capabilities: AiPlanCapabilities,\n): { reason: string; instead: string } | null {\n const planned = plan.create.filter((entry) => entry.kind === kind).length\n const reason = refusedReason(capabilities.create[kind], planned + 1)\n return reason === null ? null : { reason, instead: insteadOf(kind, capabilities) }\n}\n"],"names":["AI_BUILD_PLAN_CREATE_KINDS","AI_BUILD_PLAN_CREATION_NOUNS","ALLOWED","allowed","left","reason","aiUnrestrictedPlanCapabilities","reusableComponents","create","Object","fromEntries","map","kind","aiPlanCapabilitiesForJob","capabilities","scope","creates","includes","noun","aiCreationNoun","test","AI_PLAN_INLINE_SENTENCE","AI_PLAN_COMPILED_REPEATS_SENTENCE","AI_PLAN_NO_FORM_SENTENCE","aiPlanCapabilityLines","lines","entry","push","aiPlanCanPlaceForm","inventory","forms","length","form","insteadOf","refusedReason","capability","nth","overCount","aiPlanUncreatable","plan","refused","counted","Map","forEach","index","get","set","name","path","message","aiPlanUncreatableKind","planned","filter","instead"],"mappings":"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"}
@@ -27,7 +27,11 @@ import type { AiJobSummary } from './ai-jobs.types';
27
27
  *
28
28
  * Pure: no React, no request. The page and its spec read the same rows.
29
29
  */
30
- export type AiSiteBuildRowState = 'done' | 'active' | 'waiting' | 'failed' | 'skipped';
30
+ /**
31
+ * `paused` (AGL-3660): the row a job the meter paused stopped at — not
32
+ * running, not failed, and carried on from by Resume.
33
+ */
34
+ export type AiSiteBuildRowState = 'done' | 'active' | 'waiting' | 'failed' | 'skipped' | 'paused';
31
35
  export interface AiSiteBuildRow {
32
36
  id: string;
33
37
  label: string;
@@ -91,4 +95,10 @@ export interface AiJobPageCopy {
91
95
  * done site job without that record — an older job, or one that was not a
92
96
  * guided start — built drafts, and says that instead.
93
97
  */
94
- export declare function aiJobPageCopy(job: Pick<AiJobSummary, 'kind' | 'status' | 'review' | 'error'> & Partial<Pick<AiJobSummary, 'sitePublish' | 'items'>>, brand: string): AiJobPageCopy;
98
+ export declare function aiJobPageCopy(job: Pick<AiJobSummary, 'kind' | 'status' | 'review' | 'error'> & Partial<Pick<AiJobSummary, 'sitePublish' | 'items' | 'outputs'>>, brand: string): AiJobPageCopy;
99
+ /**
100
+ * What a job has already built, in a sentence (AGL-3660): its look, the header
101
+ * and footer, its form, its pages — from the item ledger where it has one,
102
+ * else from what it has reported as outputs. `null` when it has built nothing.
103
+ */
104
+ export declare function aiJobBuiltSoFar(job: Pick<AiJobSummary, 'kind'> & Partial<Pick<AiJobSummary, 'items' | 'outputs'>>): string | null;
@@ -16,7 +16,8 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
16
16
  * limitations under the License.
17
17
  */ import { aiJobConfirmingOwnPlan, aiJobKindNoun } from "./ai-job-activity.js";
18
18
  import { aiJobRefundCopy } from "./ai-job-failure-copy.js";
19
- import { aiBuildItemRows, aiBuildOutcomeLine, aiSitePartialCopy } from "./ai-build-progress.js";
19
+ import { aiBuildItemRows, aiBuildOpNoun, aiBuildOutcomeLine, aiSitePartialCopy } from "./ai-build-progress.js";
20
+ import { AI_JOB_PAUSED_NEXT_COPY, aiJobPausedTitle } from "./ai-job-notice.js";
20
21
  export function aiSiteBuildPhase(job) {
21
22
  // A guided start's plan parked for the instant its confirmation takes is
22
23
  // the build going on (AGL-3596), never a stop.
@@ -60,6 +61,32 @@ export function aiSiteBuildPhase(job) {
60
61
  * where a failure stopped it: a layout that broke is the layout's row, never
61
62
  * the first page's.
62
63
  */ export function aiSiteBuildRows(job) {
64
+ const rows = aiSiteBuildRowsOf(job);
65
+ return job.status === 'needs_input' ? aiPausedRows(rows) : rows;
66
+ }
67
+ /**
68
+ * A job the meter paused (AGL-3660) shows no spinner: the row it stopped at —
69
+ * one still marked running, else the first not yet built — reads `paused`,
70
+ * with no running clock and no "building now" hint, and every row after it
71
+ * keeps waiting.
72
+ */ function aiPausedRows(rows) {
73
+ const at = rows.findIndex((row)=>row.state === 'active' || row.state === 'paused');
74
+ const index = at >= 0 ? at : rows.findIndex((row)=>row.state === 'waiting');
75
+ return rows.map((row, position)=>{
76
+ if (row.state === 'active' && position !== index) return _extends({}, row, {
77
+ state: 'waiting',
78
+ hint: null,
79
+ startedAt: null
80
+ });
81
+ if (position !== index) return row;
82
+ return _extends({}, row, {
83
+ state: 'paused',
84
+ hint: null,
85
+ startedAt: null
86
+ });
87
+ });
88
+ }
89
+ function aiSiteBuildRowsOf(job) {
63
90
  var _ref, _planStep_creditsSpent, _built_get, _ref1;
64
91
  var _job_items, _job_plan;
65
92
  const phase = aiSiteBuildPhase(job);
@@ -112,7 +139,8 @@ export function aiSiteBuildPhase(job) {
112
139
  const planStep = job.steps.find((step)=>step.name === 'plan');
113
140
  const planDone = (planStep == null ? void 0 : planStep.status) === 'done' || Boolean(job.plan);
114
141
  const stopped = phase === 'failed' || phase === 'stopped';
115
- const planState = planDone ? 'done' : stopped ? 'failed' : phase === 'working' ? 'active' : 'waiting';
142
+ const paused = job.status === 'needs_input';
143
+ const planState = planDone ? 'done' : paused ? 'paused' : stopped ? 'failed' : phase === 'working' ? 'active' : 'waiting';
116
144
  const rows = [
117
145
  _extends({
118
146
  id: 'plan',
@@ -203,7 +231,7 @@ export function aiSiteBuildPhase(job) {
203
231
  rows.push(_extends({
204
232
  id: stage.id,
205
233
  label: stage.label,
206
- state: done ? 'done' : current && stopped ? 'failed' : current && phase === 'working' ? 'active' : 'waiting'
234
+ state: done ? 'done' : current && paused ? 'paused' : current && stopped ? 'failed' : current && phase === 'working' ? 'active' : 'waiting'
207
235
  }, ((_stage_sections = stage.sections) == null ? void 0 : _stage_sections.length) ? {
208
236
  sections: stage.sections
209
237
  } : {}));
@@ -244,7 +272,7 @@ export function aiSiteBuildPhase(job) {
244
272
  * and the active one half. `null` before there is more than one row to count.
245
273
  */ export function aiSiteBuildFraction(rows) {
246
274
  if (rows.length < 2) return null;
247
- const counted = rows.reduce((sum, row)=>sum + (row.state === 'active' ? 0.5 : row.state === 'waiting' ? 0 : 1), 0);
275
+ const counted = rows.reduce((sum, row)=>sum + (row.state === 'active' ? 0.5 : row.state === 'waiting' || row.state === 'paused' ? 0 : 1), 0);
248
276
  return Math.min(1, counted / rows.length);
249
277
  }
250
278
  /**
@@ -281,6 +309,18 @@ export function aiSiteBuildPhase(job) {
281
309
  var _ref, _ref1;
282
310
  var _job_review;
283
311
  const phase = aiSiteBuildPhase(job);
312
+ // Paused by the meter (AGL-3660): why, what is already built, and that it
313
+ // carries on — never "was not built", which read as a dead end.
314
+ if (job.status === 'needs_input') {
315
+ return {
316
+ heading: aiJobPausedTitle(job.kind),
317
+ lede: [
318
+ job.error,
319
+ aiJobBuiltSoFar(job),
320
+ AI_JOB_PAUSED_NEXT_COPY
321
+ ].filter(Boolean).join(' ')
322
+ };
323
+ }
284
324
  // A build is what the person asked for, item by item (AGL-3616).
285
325
  if (job.kind === 'build') return aiBuildPageCopy(job, brand);
286
326
  const noun = job.kind === 'site' ? 'site' : aiJobKindNoun(job.kind);
@@ -339,6 +379,40 @@ export function aiSiteBuildPhase(job) {
339
379
  lede: why != null ? why : generic ? 'Something went wrong running this job.' : `Something went wrong building your ${noun}.`
340
380
  };
341
381
  }
382
+ const joinWords = (names)=>{
383
+ var _names_;
384
+ return names.length <= 1 ? (_names_ = names[0]) != null ? _names_ : '' : `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`;
385
+ };
386
+ /**
387
+ * What a job has already built, in a sentence (AGL-3660): its look, the header
388
+ * and footer, its form, its pages — from the item ledger where it has one,
389
+ * else from what it has reported as outputs. `null` when it has built nothing.
390
+ */ export function aiJobBuiltSoFar(job) {
391
+ var _job_items;
392
+ var _job_items1;
393
+ const built = ((_job_items = job.items) != null ? _job_items : []).filter((row)=>row.status === 'succeeded' || row.status === 'degraded');
394
+ const names = [];
395
+ if ((_job_items1 = job.items) == null ? void 0 : _job_items1.length) {
396
+ for (const row of built){
397
+ if (row.op === 'theme') names.push('your look');
398
+ else if (row.op === 'layout') names.push('the header and footer');
399
+ else if (row.op === 'form') names.push(`the form “${row.label}”`);
400
+ else if (row.op === 'page') names.push(`the page “${row.label}”`);
401
+ else if (row.op === 'email') names.push('your welcome email');
402
+ else names.push(`${aiBuildOpNoun(row.op).toLowerCase()} “${row.label}”`);
403
+ }
404
+ } else {
405
+ var _job_outputs;
406
+ const outputs = (_job_outputs = job.outputs) != null ? _job_outputs : [];
407
+ const count = (resource)=>outputs.filter((output)=>output.resource === resource).length;
408
+ if (count('theme')) names.push('your look');
409
+ if (count('layout')) names.push('the header and footer');
410
+ if (count('form')) names.push(count('form') === 1 ? 'the form' : `${count('form')} forms`);
411
+ const pages = count('screen');
412
+ if (pages) names.push(pages === 1 ? '1 page' : `${pages} pages`);
413
+ }
414
+ return names.length ? `Built so far: ${joinWords(names)}.` : null;
415
+ }
342
416
  /** A build's page heading and sentence (AGL-3616). */ function aiBuildPageCopy(job, brand) {
343
417
  var _job_items, _job_error;
344
418
  const phase = aiSiteBuildPhase(job);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-build-progress.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 { aiJobConfirmingOwnPlan, aiJobKindNoun } from './ai-job-activity'\nimport { aiJobRefundCopy } from './ai-job-failure-copy'\nimport { aiBuildItemRows, aiBuildOutcomeLine, aiSitePartialCopy } from './ai-build-progress'\nimport type { AiJobSummary } from './ai-jobs.types'\n\n/**\n * What the \"Building your site\" page shows of a site job (AGL-3594): one row\n * a stage — planning the pages, then each page — with its state, the state\n * the whole job is in, and the job's one price.\n *\n * Read off the job summary every surface already follows: the plan step's\n * status, the plan's pages, and the pages the build has reported as outputs.\n * A scaffold builds its units in plan order and reports each page when it is\n * written, so the pages written so far are the first pages of the plan.\n *\n * Pure: no React, no request. The page and its spec read the same rows.\n */\n\nexport type AiSiteBuildRowState = 'done' | 'active' | 'waiting' | 'failed' | 'skipped'\n\nexport interface AiSiteBuildRow {\n id: string\n label: string\n state: AiSiteBuildRowState\n /** What the person should know about this row (AGL-3616): a build item's failure, note or refund. */\n detail?: string | null\n /** What the row is doing while it is the active one, in a sentence. */\n hint?: string | null\n /** A page's planned sections, in order, so the person sees what is being written. */\n sections?: string[]\n /** The credits this row's work used, once it has finished; absent while it runs. */\n credits?: number\n /** When this row's work started, for the elapsed time beside the active row. */\n startedAt?: string | null\n}\n\n/** Where the whole job stands, as the page's header and actions read it. */\nexport type AiSiteBuildPhase = 'working' | 'done' | 'stopped' | 'failed' | 'canceled'\n\nexport function aiSiteBuildPhase(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'review' | 'autoConfirm' | 'updatedAt'>>,\n): AiSiteBuildPhase {\n // A guided start's plan parked for the instant its confirmation takes is\n // the build going on (AGL-3596), never a stop.\n if (aiJobConfirmingOwnPlan(job)) return 'working'\n switch (job.status) {\n case 'done':\n return 'done'\n case 'failed':\n return 'failed'\n case 'canceled':\n return 'canceled'\n case 'needs_review':\n case 'needs_input':\n return 'stopped'\n default:\n return 'working'\n }\n}\n\n/** The creations a scaffold builds before its pages, in its build order, and what each row says. */\nconst CREATION_ROWS: ReadonlyArray<{ kind: string; resource: string; label: (name: string) => string }> = [\n { kind: 'theme-change', resource: 'theme', label: () => AI_SITE_LOOK_ROW_LABEL },\n { kind: 'layout', resource: 'layout', label: (name) => `Building the header and footer: ${name}` },\n { kind: 'form', resource: 'form', label: (name) => `Building the form: ${name}` },\n]\n\n/**\n * One row a stage: planning, then each creation the plan builds before its\n * pages — the colors, the layout, the form — then each page (AGL-3596). A\n * scaffold builds them in that order and reports each as an output when it\n * is written, so the first row without its output is where the build is, and\n * where a failure stopped it: a layout that broke is the layout's row, never\n * the first page's.\n */\nexport function aiSiteBuildRows(\n job: Pick<AiJobSummary, 'status' | 'steps' | 'plan' | 'outputs' | 'review'> &\n Partial<Pick<AiJobSummary, 'items' | 'kind' | 'siteInputs' | 'sitePublish'>>,\n): AiSiteBuildRow[] {\n const phase = aiSiteBuildPhase(job)\n // A build is read off its item ledger (AGL-3616): one row an item, each with\n // its own state and what became of its credits.\n if (job.items?.length) {\n const site = job.kind !== 'build'\n const pages = (job.items ?? []).filter((row) => row.op === 'page')\n // The plan keeps the credits it used once the items take over the page.\n const planStep = job.steps.find((step) => step.name === 'plan')\n return [\n {\n id: 'plan',\n label: site ? 'Planning your pages' : 'Planning what to build',\n state: 'done',\n ...(planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ...aiBuildItemRows(job).map((row, index) => {\n const ledger = (job.items ?? [])[index]\n // A site keeps the scaffold's own words for each stage (AGL-3596).\n const label =\n !site || !ledger\n ? row.label\n : ledger.op === 'theme'\n ? AI_SITE_LOOK_ROW_LABEL\n : ledger.op === 'layout'\n ? `Building the header and footer: ${ledger.label}`\n : ledger.op === 'form'\n ? `Building the form: ${ledger.label}`\n : ledger.op === 'page'\n ? `Writing page ${pages.indexOf(ledger) + 1} of ${pages.length}: ${ledger.label}`\n : ledger.op === 'email'\n ? 'Writing your welcome email'\n : row.label\n const screen = ledger?.op === 'page' ? aiPlanScreenFor(job.plan, ledger.label, pages.indexOf(ledger)) : null\n const finished = row.state === 'done' || row.state === 'failed'\n const net = ledger ? Math.max(0, Math.floor((ledger.creditsSpent ?? 0) - (ledger.creditsRefunded ?? 0))) : 0\n return {\n id: row.slot,\n label,\n state: row.state,\n // A part that failed while the rest is still being built says the\n // build goes on (AGL-3596): read alone, it looked like the site had.\n detail:\n row.state === 'failed' && phase === 'working'\n ? [row.detail, site ? 'The rest of your site keeps building.' : 'The rest keeps building.'].filter(Boolean).join(' ')\n : row.detail,\n ...(row.state === 'active'\n ? {\n startedAt: row.startedAt ?? null,\n hint:\n ledger?.op === 'page'\n ? AI_SITE_PAGE_HINT\n : site && ledger?.op === 'theme'\n ? AI_SITE_LOOK_HINT\n : AI_SITE_ITEM_HINT,\n }\n : {}),\n ...(screen?.sections.length ? { sections: screen.sections.map((section) => section.name) } : {}),\n ...(finished && ledger ? { credits: net } : {}),\n }\n }),\n ...aiSitePublishRow(job, phase),\n ]\n }\n const planStep = job.steps.find((step) => step.name === 'plan')\n const planDone = planStep?.status === 'done' || Boolean(job.plan)\n const stopped = phase === 'failed' || phase === 'stopped'\n const planState: AiSiteBuildRowState = planDone ? 'done' : stopped ? 'failed' : phase === 'working' ? 'active' : 'waiting'\n const rows: AiSiteBuildRow[] = [\n {\n id: 'plan',\n label: 'Planning your pages',\n state: planState,\n startedAt: planStep?.startedAt ?? null,\n hint: planState === 'active' ? AI_SITE_PLAN_HINT : null,\n ...(planDone && planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ]\n // Before there is a plan, the stages a guided start always goes through\n // are named from its answers, so the page shows the whole way from the\n // start rather than one row.\n if (!job.plan && job.kind !== 'build') {\n const pagesAsked = typeof job.siteInputs?.['pages'] === 'number' ? Math.round(job.siteInputs['pages'] as number) : 0\n if (job.siteInputs) {\n // The look is designed first, before the header and footer (AGL-3660).\n rows.push({ id: 'look', label: AI_SITE_LOOK_ROW_LABEL, state: 'waiting' })\n rows.push({ id: 'layout', label: 'Building the header and footer', state: 'waiting' })\n // A guided start that says where its contact form's submissions go\n // plans that form, built after the layout and before the pages (AGL-3596).\n const submissions = job.siteInputs['submissions']\n if (submissions === 'inbox' || submissions === 'lead') {\n rows.push({ id: 'form', label: 'Building your contact form', state: 'waiting' })\n }\n rows.push({\n id: 'pages',\n label: pagesAsked > 1 ? `Writing your ${pagesAsked} pages` : 'Writing your pages',\n state: 'waiting',\n })\n rows.push(...aiSitePublishRow(job, phase))\n }\n return rows\n }\n const built = new Map<string, number>()\n for (const output of job.outputs) built.set(output.resource, (built.get(output.resource) ?? 0) + 1)\n const take = (resource: string): boolean => {\n const left = built.get(resource) ?? 0\n if (left <= 0) return false\n built.set(resource, left - 1)\n return true\n }\n const stages: Array<{ id: string; label: string; resource: string; sections?: string[] }> = []\n for (const row of CREATION_ROWS) {\n const creation = job.plan?.create.find((entry) => entry.kind === row.kind)\n // A job read without its item ledger is one from before the look had a\n // unit of its own (AGL-3660): its look is a stage only where it built one.\n if (creation || (row.resource === 'theme' && job.outputs.some((output) => output.resource === 'theme'))) {\n stages.push({ id: row.resource, label: row.label(creation?.name ?? ''), resource: row.resource })\n }\n }\n const pages = job.plan?.screens ?? []\n pages.forEach((page, index) => {\n stages.push({\n id: `page-${index}`,\n label: `Writing page ${index + 1} of ${pages.length}: ${page.title}`,\n resource: 'screen',\n sections: page.sections.map((section) => section.name),\n })\n })\n let reached = false\n for (const stage of stages) {\n const done = phase === 'done' || take(stage.resource)\n const current = !done && !reached && planDone\n if (current) reached = true\n rows.push({\n id: stage.id,\n label: stage.label,\n state: done ? 'done' : current && stopped ? 'failed' : current && phase === 'working' ? 'active' : 'waiting',\n ...(stage.sections?.length ? { sections: stage.sections } : {}),\n })\n }\n rows.push(...aiSitePublishRow(job, phase))\n return rows\n}\n\n/** The row the site's look is designed on (AGL-3660). */\nexport const AI_SITE_LOOK_ROW_LABEL = 'Designing your look'\n\n/** What the look's row says while it runs. */\nexport const AI_SITE_LOOK_HINT =\n 'Choosing a base theme, colors, fonts and the style of your buttons, cards and forms for your kind of site. This takes a few seconds.'\n\n/** What the planning row says while it runs. */\nexport const AI_SITE_PLAN_HINT =\n 'Reading your answers and choosing your pages, what each one says, and the forms and layout they need. This usually takes under a minute.'\n\n/** What the page being written says while it is the active row (AGL-3596). */\nexport const AI_SITE_PAGE_HINT =\n 'Writing this page’s sections from your plan. It is done once every section is in.'\n\n/** What any other part being built says while it is the active row. */\nexport const AI_SITE_ITEM_HINT = 'Building this now. The next step starts when it is done.'\n\n/** The plan's page an item builds: by its title, else by its place among the pages. */\nfunction aiPlanScreenFor(\n plan: AiJobSummary['plan'],\n title: string,\n index: number,\n): NonNullable<AiJobSummary['plan']>['screens'][number] | null {\n const screens = plan?.screens ?? []\n return screens.find((screen) => screen.title === title) ?? (index >= 0 ? (screens[index] ?? null) : null)\n}\n\n/**\n * A guided start ends by putting its pages live (AGL-3596): the last row, so\n * the person sees that step coming and when it is done. Only a site job\n * publishes on its own; a build publishes when asked, and says so in its plan.\n */\nfunction aiSitePublishRow(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'kind' | 'sitePublish'>>,\n phase: AiSiteBuildPhase,\n): AiSiteBuildRow[] {\n if (job.kind !== 'site') return []\n const published = (job.sitePublish?.published.length ?? 0) > 0\n return [\n {\n id: 'publish',\n label: 'Publishing your site',\n // A job that stopped or failed publishes nothing, so the row is passed over.\n state: phase === 'done' ? (published ? 'done' : 'skipped') : phase === 'working' ? 'waiting' : 'skipped',\n },\n ]\n}\n\n/**\n * How far the job is, from 0 to 1, by its rows: a finished row counts whole\n * and the active one half. `null` before there is more than one row to count.\n */\nexport function aiSiteBuildFraction(rows: readonly AiSiteBuildRow[]): number | null {\n if (rows.length < 2) return null\n const counted = rows.reduce(\n (sum, row) => sum + (row.state === 'active' ? 0.5 : row.state === 'waiting' ? 0 : 1),\n 0,\n )\n return Math.min(1, counted / rows.length)\n}\n\n/**\n * The job's one price (AGL-3594), never a plan's on its own: what it has used\n * so far while it works, what it used once done, and, on a failure we gave\n * back, that nothing was charged.\n */\nexport function aiSiteBuildCreditsLine(\n job: Pick<AiJobSummary, 'status' | 'creditsSpent' | 'refundedCredits'> & Partial<Pick<AiJobSummary, 'kind'>>,\n): string | null {\n const spent = Math.max(0, job.creditsSpent ?? 0)\n const refunded = Math.max(0, job.refundedCredits ?? 0)\n const net = Math.max(0, spent - refunded)\n const phase = aiSiteBuildPhase(job)\n // What the job made, by its kind; a job that names none is a site's.\n const subject = !job.kind || job.kind === 'site' ? 'This site' : aiJobKindNoun(job.kind) === 'AI job' ? 'This AI job' : `This ${aiJobKindNoun(job.kind)}`\n // A failure on our side says so in the words every surface uses (AGL-3596),\n // from the job's recorded give-back.\n const refund = phase === 'working' || phase === 'done' ? null : aiJobRefundCopy(job)\n if (refund && refunded > 0) return refund\n if (phase === 'done') return `${subject} used ${net} credits.`\n if (phase === 'working') return `Credits used so far: ${net}`\n if (refund) return `${subject} used ${net} credits. ${refund}`\n return net > 0 ? `${subject} used ${net} credits.` : null\n}\n\n/** The heading and the sentence under it, for one job's page in its current phase. */\nexport interface AiJobPageCopy {\n heading: string\n lede: string\n}\n\n/**\n * What one job's page says at the top (AGL-3594, AGL-3596). A site job reads\n * as \"Building your site\"; any other kind names what it makes — \"Building\n * your page\" — or, for a kind with no noun, the job itself.\n *\n * A guided site start publishes its pages when it finishes, and says so\n * (`sitePublish`): with a page live the heading is \"Your site is live\". A\n * done site job without that record — an older job, or one that was not a\n * guided start — built drafts, and says that instead.\n */\nexport function aiJobPageCopy(\n job: Pick<AiJobSummary, 'kind' | 'status' | 'review' | 'error'> & Partial<Pick<AiJobSummary, 'sitePublish' | 'items'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n // A build is what the person asked for, item by item (AGL-3616).\n if (job.kind === 'build') return aiBuildPageCopy(job, brand)\n const noun = job.kind === 'site' ? 'site' : aiJobKindNoun(job.kind)\n const generic = noun === 'AI job'\n // `products` is the one plural noun.\n const isAre = noun.endsWith('s') ? 'are' : 'is'\n const why = job.review?.message ?? job.error ?? null\n if (phase === 'working') {\n return {\n heading: generic ? 'Your AI job is running' : `Building your ${noun}`,\n lede:\n job.kind === 'site'\n ? `${brand} AI is planning your pages and writing each one. You can leave this page; it keeps going.`\n : `${brand} AI is working on it. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'done') {\n const published = job.sitePublish?.published.length ?? 0\n // Part of the site was built (AGL-3616): what was, what was not, and its credits.\n const partial = job.kind === 'site' ? aiSitePartialCopy({ items: job.items, status: job.status }) : null\n if (job.kind === 'site' && published > 0) {\n return {\n heading: 'Your site is live',\n lede: [\n partial,\n job.sitePublish?.drafts.length\n ? 'Your pages are published, except the ones listed below, which stayed drafts.'\n : 'Your pages are published, and anyone can visit your site now.',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (partial) return { heading: 'Your site is ready', lede: `${partial} Your new pages are drafts. Publish them when you’re happy.` }\n if (job.kind === 'site' && job.sitePublish) {\n return {\n heading: 'Your site is ready',\n lede: 'None of your new pages could be published, so they are drafts. The reasons are below.',\n }\n }\n return {\n heading: generic ? 'Your AI job finished' : `Your ${noun} ${isAre} ready`,\n lede:\n job.kind === 'site'\n ? 'Your new pages are drafts. Publish them when you’re happy.'\n : 'Everything it built is an unpublished draft until you publish it.',\n }\n }\n if (phase === 'canceled') {\n return { heading: generic ? 'Your AI job was canceled' : `Your ${noun} was not built`, lede: 'The job was canceled.' }\n }\n return {\n heading: generic ? 'Your AI job stopped' : `Your ${noun} ${noun.endsWith('s') ? 'were' : 'was'} not built`,\n lede: why ?? (generic ? 'Something went wrong running this job.' : `Something went wrong building your ${noun}.`),\n }\n}\n\n/** A build's page heading and sentence (AGL-3616). */\nfunction aiBuildPageCopy(\n job: Pick<AiJobSummary, 'status' | 'review' | 'error'> & Partial<Pick<AiJobSummary, 'items' | 'sitePublish'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n const outcome = aiBuildOutcomeLine({ items: job.items ?? [], status: job.status })\n if (phase === 'working') {\n return {\n heading: 'Building what you asked for',\n lede: `${brand} AI is building each part in turn. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'stopped') {\n return {\n heading: job.review?.reason === 'plan' ? 'Your plan is ready' : 'Your build needs you',\n lede: job.review?.message ?? job.error ?? 'It waits for your decision.',\n }\n }\n if (phase === 'done') {\n const failed = (job.items ?? []).some((row) => row.status === 'failed' || row.status === 'skipped')\n const live = job.sitePublish?.published.length ?? 0\n return {\n heading: failed ? 'Most of it is ready' : 'Everything you asked for is ready',\n lede: [\n outcome,\n live ? 'Your new pages are published.' : 'Everything it built is an unpublished draft until you publish it.',\n failed ? 'Try again builds only what failed.' : '',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (phase === 'canceled') return { heading: 'Your build was canceled', lede: 'The job was canceled.' }\n return {\n heading: 'Nothing could be built',\n lede: [job.error ?? 'Something went wrong building what you asked for.', outcome].filter(Boolean).join(' '),\n }\n}\n"],"names":["aiJobConfirmingOwnPlan","aiJobKindNoun","aiJobRefundCopy","aiBuildItemRows","aiBuildOutcomeLine","aiSitePartialCopy","aiSiteBuildPhase","job","status","CREATION_ROWS","kind","resource","label","AI_SITE_LOOK_ROW_LABEL","name","aiSiteBuildRows","planStep","built","phase","items","length","site","pages","filter","row","op","steps","find","step","id","state","credits","Math","max","floor","creditsSpent","map","index","ledger","indexOf","screen","aiPlanScreenFor","plan","finished","net","creditsRefunded","slot","detail","Boolean","join","startedAt","hint","AI_SITE_PAGE_HINT","AI_SITE_LOOK_HINT","AI_SITE_ITEM_HINT","sections","section","aiSitePublishRow","planDone","stopped","planState","rows","AI_SITE_PLAN_HINT","pagesAsked","siteInputs","round","push","submissions","Map","output","outputs","set","get","take","left","stages","creation","create","entry","some","screens","forEach","page","title","reached","stage","done","current","published","sitePublish","aiSiteBuildFraction","counted","reduce","sum","min","aiSiteBuildCreditsLine","spent","refunded","refundedCredits","subject","refund","aiJobPageCopy","brand","aiBuildPageCopy","noun","generic","isAre","endsWith","why","review","message","error","heading","lede","partial","drafts","outcome","reason","failed","live"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,EAAEC,aAAa,QAAQ,uBAAmB;AACzE,SAASC,eAAe,QAAQ,2BAAuB;AACvD,SAASC,eAAe,EAAEC,kBAAkB,EAAEC,iBAAiB,QAAQ,yBAAqB;AAqC5F,OAAO,SAASC,iBACdC,GAAuG;IAEvG,yEAAyE;IACzE,+CAA+C;IAC/C,IAAIP,uBAAuBO,MAAM,OAAO;IACxC,OAAQA,IAAIC,MAAM;QAChB,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;YACH,OAAO;QACT;YACE,OAAO;IACX;AACF;AAEA,kGAAkG,GAClG,MAAMC,gBAAoG;IACxG;QAAEC,MAAM;QAAgBC,UAAU;QAASC,OAAO,IAAMC;IAAuB;IAC/E;QAAEH,MAAM;QAAUC,UAAU;QAAUC,OAAO,CAACE,OAAS,CAAC,gCAAgC,EAAEA,MAAM;IAAC;IACjG;QAAEJ,MAAM;QAAQC,UAAU;QAAQC,OAAO,CAACE,OAAS,CAAC,mBAAmB,EAAEA,MAAM;IAAC;CACjF;AAED;;;;;;;CAOC,GACD,OAAO,SAASC,gBACdR,GAC8E;cA4EbS,wBA4BHC;QAnG1DV,YAmHUA;IAtHd,MAAMW,QAAQZ,iBAAiBC;IAC/B,6EAA6E;IAC7E,gDAAgD;IAChD,KAAIA,aAAAA,IAAIY,KAAK,qBAATZ,WAAWa,MAAM,EAAE;YAENb,aAQsCS;QATrD,MAAMK,OAAOd,IAAIG,IAAI,KAAK;QAC1B,MAAMY,QAAQ,EAACf,cAAAA,IAAIY,KAAK,YAATZ,cAAa,EAAE,EAAEgB,MAAM,CAAC,CAACC,MAAQA,IAAIC,EAAE,KAAK;QAC3D,wEAAwE;QACxE,MAAMT,WAAWT,IAAImB,KAAK,CAACC,IAAI,CAAC,CAACC,OAASA,KAAKd,IAAI,KAAK;QACxD,OAAO;YACL;gBACEe,IAAI;gBACJjB,OAAOS,OAAO,wBAAwB;gBACtCS,OAAO;eACHd,WAAW;gBAAEe,SAASC,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,EAAClB,0BAAAA,SAASmB,YAAY,YAArBnB,0BAAyB;YAAI,IAAI,CAAC;eAElFb,gBAAgBI,KAAK6B,GAAG,CAAC,CAACZ,KAAKa;oBAChB9B,YAkB6B+B,sBAA6BA,yBAavDd;gBA/BnB,MAAMc,SAAS,EAAC/B,aAAAA,IAAIY,KAAK,YAATZ,aAAa,EAAE,CAAC,CAAC8B,MAAM;gBACvC,mEAAmE;gBACnE,MAAMzB,QACJ,CAACS,QAAQ,CAACiB,SACNd,IAAIZ,KAAK,GACT0B,OAAOb,EAAE,KAAK,UACZZ,yBACAyB,OAAOb,EAAE,KAAK,WACZ,CAAC,gCAAgC,EAAEa,OAAO1B,KAAK,EAAE,GACjD0B,OAAOb,EAAE,KAAK,SACZ,CAAC,mBAAmB,EAAEa,OAAO1B,KAAK,EAAE,GACpC0B,OAAOb,EAAE,KAAK,SACZ,CAAC,aAAa,EAAEH,MAAMiB,OAAO,CAACD,UAAU,EAAE,IAAI,EAAEhB,MAAMF,MAAM,CAAC,EAAE,EAAEkB,OAAO1B,KAAK,EAAE,GAC/E0B,OAAOb,EAAE,KAAK,UACZ,+BACAD,IAAIZ,KAAK;gBACzB,MAAM4B,SAASF,CAAAA,0BAAAA,OAAQb,EAAE,MAAK,SAASgB,gBAAgBlC,IAAImC,IAAI,EAAEJ,OAAO1B,KAAK,EAAEU,MAAMiB,OAAO,CAACD,WAAW;gBACxG,MAAMK,WAAWnB,IAAIM,KAAK,KAAK,UAAUN,IAAIM,KAAK,KAAK;gBACvD,MAAMc,MAAMN,SAASN,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,CAAC,EAACI,uBAAAA,OAAOH,YAAY,YAAnBG,uBAAuB,OAAMA,0BAAAA,OAAOO,eAAe,YAAtBP,0BAA0B,OAAO;gBAC3G,OAAO;oBACLT,IAAIL,IAAIsB,IAAI;oBACZlC;oBACAkB,OAAON,IAAIM,KAAK;oBAChB,kEAAkE;oBAClE,qEAAqE;oBACrEiB,QACEvB,IAAIM,KAAK,KAAK,YAAYZ,UAAU,YAChC;wBAACM,IAAIuB,MAAM;wBAAE1B,OAAO,0CAA0C;qBAA2B,CAACE,MAAM,CAACyB,SAASC,IAAI,CAAC,OAC/GzB,IAAIuB,MAAM;mBACZvB,IAAIM,KAAK,KAAK,WACd;oBACEoB,SAAS,GAAE1B,iBAAAA,IAAI0B,SAAS,YAAb1B,iBAAiB;oBAC5B2B,MACEb,CAAAA,0BAAAA,OAAQb,EAAE,MAAK,SACX2B,oBACA/B,QAAQiB,CAAAA,0BAAAA,OAAQb,EAAE,MAAK,UACrB4B,oBACAC;gBACV,IACA,CAAC,GACDd,CAAAA,0BAAAA,OAAQe,QAAQ,CAACnC,MAAM,IAAG;oBAAEmC,UAAUf,OAAOe,QAAQ,CAACnB,GAAG,CAAC,CAACoB,UAAYA,QAAQ1C,IAAI;gBAAE,IAAI,CAAC,GAC1F6B,YAAYL,SAAS;oBAAEP,SAASa;gBAAI,IAAI,CAAC;YAEjD;eACGa,iBAAiBlD,KAAKW;SAC1B;IACH;IACA,MAAMF,WAAWT,IAAImB,KAAK,CAACC,IAAI,CAAC,CAACC,OAASA,KAAKd,IAAI,KAAK;IACxD,MAAM4C,WAAW1C,CAAAA,4BAAAA,SAAUR,MAAM,MAAK,UAAUwC,QAAQzC,IAAImC,IAAI;IAChE,MAAMiB,UAAUzC,UAAU,YAAYA,UAAU;IAChD,MAAM0C,YAAiCF,WAAW,SAASC,UAAU,WAAWzC,UAAU,YAAY,WAAW;IACjH,MAAM2C,OAAyB;QAC7B;YACEhC,IAAI;YACJjB,OAAO;YACPkB,OAAO8B;YACPV,SAAS,UAAElC,4BAAAA,SAAUkC,SAAS,mBAAI;YAClCC,MAAMS,cAAc,WAAWE,oBAAoB;WAC/CJ,YAAY1C,WAAW;YAAEe,SAASC,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,EAAClB,yBAAAA,SAASmB,YAAY,YAArBnB,yBAAyB;QAAI,IAAI,CAAC;KAElG;IACD,wEAAwE;IACxE,uEAAuE;IACvE,6BAA6B;IAC7B,IAAI,CAACT,IAAImC,IAAI,IAAInC,IAAIG,IAAI,KAAK,SAAS;YACXH;QAA1B,MAAMwD,aAAa,SAAOxD,kBAAAA,IAAIyD,UAAU,qBAAdzD,eAAgB,CAAC,QAAQ,MAAK,WAAWyB,KAAKiC,KAAK,CAAC1D,IAAIyD,UAAU,CAAC,QAAQ,IAAc;QACnH,IAAIzD,IAAIyD,UAAU,EAAE;YAClB,uEAAuE;YACvEH,KAAKK,IAAI,CAAC;gBAAErC,IAAI;gBAAQjB,OAAOC;gBAAwBiB,OAAO;YAAU;YACxE+B,KAAKK,IAAI,CAAC;gBAAErC,IAAI;gBAAUjB,OAAO;gBAAkCkB,OAAO;YAAU;YACpF,mEAAmE;YACnE,2EAA2E;YAC3E,MAAMqC,cAAc5D,IAAIyD,UAAU,CAAC,cAAc;YACjD,IAAIG,gBAAgB,WAAWA,gBAAgB,QAAQ;gBACrDN,KAAKK,IAAI,CAAC;oBAAErC,IAAI;oBAAQjB,OAAO;oBAA8BkB,OAAO;gBAAU;YAChF;YACA+B,KAAKK,IAAI,CAAC;gBACRrC,IAAI;gBACJjB,OAAOmD,aAAa,IAAI,CAAC,aAAa,EAAEA,WAAW,MAAM,CAAC,GAAG;gBAC7DjC,OAAO;YACT;YACA+B,KAAKK,IAAI,IAAIT,iBAAiBlD,KAAKW;QACrC;QACA,OAAO2C;IACT;IACA,MAAM5C,QAAQ,IAAImD;IAClB,KAAK,MAAMC,UAAU9D,IAAI+D,OAAO,CAAErD,MAAMsD,GAAG,CAACF,OAAO1D,QAAQ,EAAE,EAACM,aAAAA,MAAMuD,GAAG,CAACH,OAAO1D,QAAQ,aAAzBM,aAA8B,KAAK;IACjG,MAAMwD,OAAO,CAAC9D;YACCM;QAAb,MAAMyD,QAAOzD,aAAAA,MAAMuD,GAAG,CAAC7D,qBAAVM,aAAuB;QACpC,IAAIyD,QAAQ,GAAG,OAAO;QACtBzD,MAAMsD,GAAG,CAAC5D,UAAU+D,OAAO;QAC3B,OAAO;IACT;IACA,MAAMC,SAAsF,EAAE;IAC9F,KAAK,MAAMnD,OAAOf,cAAe;YACdF;QAAjB,MAAMqE,YAAWrE,aAAAA,IAAImC,IAAI,qBAARnC,WAAUsE,MAAM,CAAClD,IAAI,CAAC,CAACmD,QAAUA,MAAMpE,IAAI,KAAKc,IAAId,IAAI;QACzE,uEAAuE;QACvE,2EAA2E;QAC3E,IAAIkE,YAAapD,IAAIb,QAAQ,KAAK,WAAWJ,IAAI+D,OAAO,CAACS,IAAI,CAAC,CAACV,SAAWA,OAAO1D,QAAQ,KAAK,UAAW;;YACvGgE,OAAOT,IAAI,CAAC;gBAAErC,IAAIL,IAAIb,QAAQ;gBAAEC,OAAOY,IAAIZ,KAAK,UAACgE,4BAAAA,SAAU9D,IAAI,oBAAI;gBAAKH,UAAUa,IAAIb,QAAQ;YAAC;QACjG;IACF;IACA,MAAMW,kBAAQf,YAAAA,IAAImC,IAAI,qBAARnC,UAAUyE,OAAO,oBAAI,EAAE;IACrC1D,MAAM2D,OAAO,CAAC,CAACC,MAAM7C;QACnBsC,OAAOT,IAAI,CAAC;YACVrC,IAAI,CAAC,KAAK,EAAEQ,OAAO;YACnBzB,OAAO,CAAC,aAAa,EAAEyB,QAAQ,EAAE,IAAI,EAAEf,MAAMF,MAAM,CAAC,EAAE,EAAE8D,KAAKC,KAAK,EAAE;YACpExE,UAAU;YACV4C,UAAU2B,KAAK3B,QAAQ,CAACnB,GAAG,CAAC,CAACoB,UAAYA,QAAQ1C,IAAI;QACvD;IACF;IACA,IAAIsE,UAAU;IACd,KAAK,MAAMC,SAASV,OAAQ;YAQpBU;QAPN,MAAMC,OAAOpE,UAAU,UAAUuD,KAAKY,MAAM1E,QAAQ;QACpD,MAAM4E,UAAU,CAACD,QAAQ,CAACF,WAAW1B;QACrC,IAAI6B,SAASH,UAAU;QACvBvB,KAAKK,IAAI,CAAC;YACRrC,IAAIwD,MAAMxD,EAAE;YACZjB,OAAOyE,MAAMzE,KAAK;YAClBkB,OAAOwD,OAAO,SAASC,WAAW5B,UAAU,WAAW4B,WAAWrE,UAAU,YAAY,WAAW;WAC/FmE,EAAAA,kBAAAA,MAAM9B,QAAQ,qBAAd8B,gBAAgBjE,MAAM,IAAG;YAAEmC,UAAU8B,MAAM9B,QAAQ;QAAC,IAAI,CAAC;IAEjE;IACAM,KAAKK,IAAI,IAAIT,iBAAiBlD,KAAKW;IACnC,OAAO2C;AACT;AAEA,uDAAuD,GACvD,OAAO,MAAMhD,yBAAyB,sBAAqB;AAE3D,4CAA4C,GAC5C,OAAO,MAAMwC,oBACX,uIAAsI;AAExI,8CAA8C,GAC9C,OAAO,MAAMS,oBACX,2IAA0I;AAE5I,4EAA4E,GAC5E,OAAO,MAAMV,oBACX,oFAAmF;AAErF,qEAAqE,GACrE,OAAO,MAAME,oBAAoB,2DAA0D;AAE3F,qFAAqF,GACrF,SAASb,gBACPC,IAA0B,EAC1ByC,KAAa,EACb9C,KAAa;cAGN2C,eAAmEA;IAD1E,MAAMA,kBAAUtC,wBAAAA,KAAMsC,OAAO,mBAAI,EAAE;IACnC,QAAOA,gBAAAA,QAAQrD,IAAI,CAAC,CAACa,SAAWA,OAAO2C,KAAK,KAAKA,kBAA1CH,gBAAqD3C,SAAS,KAAK2C,iBAAAA,OAAO,CAAC3C,MAAM,YAAd2C,iBAAkB,OAAQ;AACtG;AAEA;;;;CAIC,GACD,SAASvB,iBACPlD,GAAuF,EACvFW,KAAuB;;QAGJX;IADnB,IAAIA,IAAIG,IAAI,KAAK,QAAQ,OAAO,EAAE;IAClC,MAAM8E,YAAY,UAACjF,mBAAAA,IAAIkF,WAAW,qBAAflF,iBAAiBiF,SAAS,CAACpE,MAAM,mBAAI,KAAK;IAC7D,OAAO;QACL;YACES,IAAI;YACJjB,OAAO;YACP,6EAA6E;YAC7EkB,OAAOZ,UAAU,SAAUsE,YAAY,SAAS,YAAatE,UAAU,YAAY,YAAY;QACjG;KACD;AACH;AAEA;;;CAGC,GACD,OAAO,SAASwE,oBAAoB7B,IAA+B;IACjE,IAAIA,KAAKzC,MAAM,GAAG,GAAG,OAAO;IAC5B,MAAMuE,UAAU9B,KAAK+B,MAAM,CACzB,CAACC,KAAKrE,MAAQqE,MAAOrE,CAAAA,IAAIM,KAAK,KAAK,WAAW,MAAMN,IAAIM,KAAK,KAAK,YAAY,IAAI,CAAA,GAClF;IAEF,OAAOE,KAAK8D,GAAG,CAAC,GAAGH,UAAU9B,KAAKzC,MAAM;AAC1C;AAEA;;;;CAIC,GACD,OAAO,SAAS2E,uBACdxF,GAA4G;QAElFA,mBACGA;IAD7B,MAAMyF,QAAQhE,KAAKC,GAAG,CAAC,IAAG1B,oBAAAA,IAAI4B,YAAY,YAAhB5B,oBAAoB;IAC9C,MAAM0F,WAAWjE,KAAKC,GAAG,CAAC,IAAG1B,uBAAAA,IAAI2F,eAAe,YAAnB3F,uBAAuB;IACpD,MAAMqC,MAAMZ,KAAKC,GAAG,CAAC,GAAG+D,QAAQC;IAChC,MAAM/E,QAAQZ,iBAAiBC;IAC/B,qEAAqE;IACrE,MAAM4F,UAAU,CAAC5F,IAAIG,IAAI,IAAIH,IAAIG,IAAI,KAAK,SAAS,cAAcT,cAAcM,IAAIG,IAAI,MAAM,WAAW,gBAAgB,CAAC,KAAK,EAAET,cAAcM,IAAIG,IAAI,GAAG;IACzJ,4EAA4E;IAC5E,qCAAqC;IACrC,MAAM0F,SAASlF,UAAU,aAAaA,UAAU,SAAS,OAAOhB,gBAAgBK;IAChF,IAAI6F,UAAUH,WAAW,GAAG,OAAOG;IACnC,IAAIlF,UAAU,QAAQ,OAAO,GAAGiF,QAAQ,MAAM,EAAEvD,IAAI,SAAS,CAAC;IAC9D,IAAI1B,UAAU,WAAW,OAAO,CAAC,qBAAqB,EAAE0B,KAAK;IAC7D,IAAIwD,QAAQ,OAAO,GAAGD,QAAQ,MAAM,EAAEvD,IAAI,UAAU,EAAEwD,QAAQ;IAC9D,OAAOxD,MAAM,IAAI,GAAGuD,QAAQ,MAAM,EAAEvD,IAAI,SAAS,CAAC,GAAG;AACvD;AAQA;;;;;;;;;CASC,GACD,OAAO,SAASyD,cACd9F,GAAsH,EACtH+F,KAAa;QASD/F;QAAAA;IAPZ,MAAMW,QAAQZ,iBAAiBC;IAC/B,iEAAiE;IACjE,IAAIA,IAAIG,IAAI,KAAK,SAAS,OAAO6F,gBAAgBhG,KAAK+F;IACtD,MAAME,OAAOjG,IAAIG,IAAI,KAAK,SAAS,SAAST,cAAcM,IAAIG,IAAI;IAClE,MAAM+F,UAAUD,SAAS;IACzB,qCAAqC;IACrC,MAAME,QAAQF,KAAKG,QAAQ,CAAC,OAAO,QAAQ;IAC3C,MAAMC,OAAMrG,iBAAAA,cAAAA,IAAIsG,MAAM,qBAAVtG,YAAYuG,OAAO,oBAAIvG,IAAIwG,KAAK,YAAhCxG,OAAoC;IAChD,IAAIW,UAAU,WAAW;QACvB,OAAO;YACL8F,SAASP,UAAU,2BAA2B,CAAC,cAAc,EAAED,MAAM;YACrES,MACE1G,IAAIG,IAAI,KAAK,SACT,GAAG4F,MAAM,yFAAyF,CAAC,GACnG,GAAGA,MAAM,8DAA8D,CAAC;QAChF;IACF;IACA,IAAIpF,UAAU,QAAQ;;YACFX;QAAlB,MAAMiF,sBAAYjF,mBAAAA,IAAIkF,WAAW,qBAAflF,iBAAiBiF,SAAS,CAACpE,MAAM,oBAAI;QACvD,kFAAkF;QAClF,MAAM8F,UAAU3G,IAAIG,IAAI,KAAK,SAASL,kBAAkB;YAAEc,OAAOZ,IAAIY,KAAK;YAAEX,QAAQD,IAAIC,MAAM;QAAC,KAAK;QACpG,IAAID,IAAIG,IAAI,KAAK,UAAU8E,YAAY,GAAG;gBAKpCjF;YAJJ,OAAO;gBACLyG,SAAS;gBACTC,MAAM;oBACJC;oBACA3G,EAAAA,oBAAAA,IAAIkF,WAAW,qBAAflF,kBAAiB4G,MAAM,CAAC/F,MAAM,IAC1B,iFACA;iBACL,CACEG,MAAM,CAACyB,SACPC,IAAI,CAAC;YACV;QACF;QACA,IAAIiE,SAAS,OAAO;YAAEF,SAAS;YAAsBC,MAAM,GAAGC,QAAQ,2DAA2D,CAAC;QAAC;QACnI,IAAI3G,IAAIG,IAAI,KAAK,UAAUH,IAAIkF,WAAW,EAAE;YAC1C,OAAO;gBACLuB,SAAS;gBACTC,MAAM;YACR;QACF;QACA,OAAO;YACLD,SAASP,UAAU,yBAAyB,CAAC,KAAK,EAAED,KAAK,CAAC,EAAEE,MAAM,MAAM,CAAC;YACzEO,MACE1G,IAAIG,IAAI,KAAK,SACT,+DACA;QACR;IACF;IACA,IAAIQ,UAAU,YAAY;QACxB,OAAO;YAAE8F,SAASP,UAAU,6BAA6B,CAAC,KAAK,EAAED,KAAK,cAAc,CAAC;YAAES,MAAM;QAAwB;IACvH;IACA,OAAO;QACLD,SAASP,UAAU,wBAAwB,CAAC,KAAK,EAAED,KAAK,CAAC,EAAEA,KAAKG,QAAQ,CAAC,OAAO,SAAS,MAAM,UAAU,CAAC;QAC1GM,IAAI,EAAEL,cAAAA,MAAQH,UAAU,2CAA2C,CAAC,mCAAmC,EAAED,KAAK,CAAC,CAAC;IAClH;AACF;AAEA,oDAAoD,GACpD,SAASD,gBACPhG,GAA6G,EAC7G+F,KAAa;QAG+B/F,YA8BnCA;IA/BT,MAAMW,QAAQZ,iBAAiBC;IAC/B,MAAM6G,UAAUhH,mBAAmB;QAAEe,KAAK,GAAEZ,aAAAA,IAAIY,KAAK,YAATZ,aAAa,EAAE;QAAEC,QAAQD,IAAIC,MAAM;IAAC;IAChF,IAAIU,UAAU,WAAW;QACvB,OAAO;YACL8F,SAAS;YACTC,MAAM,GAAGX,MAAM,2EAA2E,CAAC;QAC7F;IACF;IACA,IAAIpF,UAAU,WAAW;YAGfX;YADGA,aACHA;QAFR,OAAO;YACLyG,SAASzG,EAAAA,cAAAA,IAAIsG,MAAM,qBAAVtG,YAAY8G,MAAM,MAAK,SAAS,uBAAuB;YAChEJ,IAAI,GAAE1G,iBAAAA,eAAAA,IAAIsG,MAAM,qBAAVtG,aAAYuG,OAAO,oBAAIvG,IAAIwG,KAAK,YAAhCxG,OAAoC;QAC5C;IACF;IACA,IAAIW,UAAU,QAAQ;YACJX;YACHA;QADb,MAAM+G,SAAS,EAAC/G,cAAAA,IAAIY,KAAK,YAATZ,cAAa,EAAE,EAAEwE,IAAI,CAAC,CAACvD,MAAQA,IAAIhB,MAAM,KAAK,YAAYgB,IAAIhB,MAAM,KAAK;QACzF,MAAM+G,iBAAOhH,mBAAAA,IAAIkF,WAAW,qBAAflF,iBAAiBiF,SAAS,CAACpE,MAAM,oBAAI;QAClD,OAAO;YACL4F,SAASM,SAAS,wBAAwB;YAC1CL,MAAM;gBACJG;gBACAG,OAAO,kCAAkC;gBACzCD,SAAS,uCAAuC;aACjD,CACE/F,MAAM,CAACyB,SACPC,IAAI,CAAC;QACV;IACF;IACA,IAAI/B,UAAU,YAAY,OAAO;QAAE8F,SAAS;QAA2BC,MAAM;IAAwB;IACrG,OAAO;QACLD,SAAS;QACTC,MAAM;aAAC1G,aAAAA,IAAIwG,KAAK,YAATxG,aAAa;YAAqD6G;SAAQ,CAAC7F,MAAM,CAACyB,SAASC,IAAI,CAAC;IACzG;AACF"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-build-progress.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 { aiJobConfirmingOwnPlan, aiJobKindNoun } from './ai-job-activity'\nimport { aiJobRefundCopy } from './ai-job-failure-copy'\nimport { aiBuildItemRows, aiBuildOpNoun, aiBuildOutcomeLine, aiSitePartialCopy } from './ai-build-progress'\nimport { AI_JOB_PAUSED_NEXT_COPY, aiJobPausedTitle } from './ai-job-notice'\nimport type { AiJobSummary } from './ai-jobs.types'\n\n/**\n * What the \"Building your site\" page shows of a site job (AGL-3594): one row\n * a stage — planning the pages, then each page — with its state, the state\n * the whole job is in, and the job's one price.\n *\n * Read off the job summary every surface already follows: the plan step's\n * status, the plan's pages, and the pages the build has reported as outputs.\n * A scaffold builds its units in plan order and reports each page when it is\n * written, so the pages written so far are the first pages of the plan.\n *\n * Pure: no React, no request. The page and its spec read the same rows.\n */\n\n/**\n * `paused` (AGL-3660): the row a job the meter paused stopped at — not\n * running, not failed, and carried on from by Resume.\n */\nexport type AiSiteBuildRowState = 'done' | 'active' | 'waiting' | 'failed' | 'skipped' | 'paused'\n\nexport interface AiSiteBuildRow {\n id: string\n label: string\n state: AiSiteBuildRowState\n /** What the person should know about this row (AGL-3616): a build item's failure, note or refund. */\n detail?: string | null\n /** What the row is doing while it is the active one, in a sentence. */\n hint?: string | null\n /** A page's planned sections, in order, so the person sees what is being written. */\n sections?: string[]\n /** The credits this row's work used, once it has finished; absent while it runs. */\n credits?: number\n /** When this row's work started, for the elapsed time beside the active row. */\n startedAt?: string | null\n}\n\n/** Where the whole job stands, as the page's header and actions read it. */\nexport type AiSiteBuildPhase = 'working' | 'done' | 'stopped' | 'failed' | 'canceled'\n\nexport function aiSiteBuildPhase(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'review' | 'autoConfirm' | 'updatedAt'>>,\n): AiSiteBuildPhase {\n // A guided start's plan parked for the instant its confirmation takes is\n // the build going on (AGL-3596), never a stop.\n if (aiJobConfirmingOwnPlan(job)) return 'working'\n switch (job.status) {\n case 'done':\n return 'done'\n case 'failed':\n return 'failed'\n case 'canceled':\n return 'canceled'\n case 'needs_review':\n case 'needs_input':\n return 'stopped'\n default:\n return 'working'\n }\n}\n\n/** The creations a scaffold builds before its pages, in its build order, and what each row says. */\nconst CREATION_ROWS: ReadonlyArray<{ kind: string; resource: string; label: (name: string) => string }> = [\n { kind: 'theme-change', resource: 'theme', label: () => AI_SITE_LOOK_ROW_LABEL },\n { kind: 'layout', resource: 'layout', label: (name) => `Building the header and footer: ${name}` },\n { kind: 'form', resource: 'form', label: (name) => `Building the form: ${name}` },\n]\n\n/**\n * One row a stage: planning, then each creation the plan builds before its\n * pages — the colors, the layout, the form — then each page (AGL-3596). A\n * scaffold builds them in that order and reports each as an output when it\n * is written, so the first row without its output is where the build is, and\n * where a failure stopped it: a layout that broke is the layout's row, never\n * the first page's.\n */\nexport function aiSiteBuildRows(\n job: Pick<AiJobSummary, 'status' | 'steps' | 'plan' | 'outputs' | 'review'> &\n Partial<Pick<AiJobSummary, 'items' | 'kind' | 'siteInputs' | 'sitePublish'>>,\n): AiSiteBuildRow[] {\n const rows = aiSiteBuildRowsOf(job)\n return job.status === 'needs_input' ? aiPausedRows(rows) : rows\n}\n\n/**\n * A job the meter paused (AGL-3660) shows no spinner: the row it stopped at —\n * one still marked running, else the first not yet built — reads `paused`,\n * with no running clock and no \"building now\" hint, and every row after it\n * keeps waiting.\n */\nfunction aiPausedRows(rows: AiSiteBuildRow[]): AiSiteBuildRow[] {\n const at = rows.findIndex((row) => row.state === 'active' || row.state === 'paused')\n const index = at >= 0 ? at : rows.findIndex((row) => row.state === 'waiting')\n return rows.map((row, position) => {\n if (row.state === 'active' && position !== index) return { ...row, state: 'waiting', hint: null, startedAt: null }\n if (position !== index) return row\n return { ...row, state: 'paused', hint: null, startedAt: null }\n })\n}\n\nfunction aiSiteBuildRowsOf(\n job: Pick<AiJobSummary, 'status' | 'steps' | 'plan' | 'outputs' | 'review'> &\n Partial<Pick<AiJobSummary, 'items' | 'kind' | 'siteInputs' | 'sitePublish'>>,\n): AiSiteBuildRow[] {\n const phase = aiSiteBuildPhase(job)\n // A build is read off its item ledger (AGL-3616): one row an item, each with\n // its own state and what became of its credits.\n if (job.items?.length) {\n const site = job.kind !== 'build'\n const pages = (job.items ?? []).filter((row) => row.op === 'page')\n // The plan keeps the credits it used once the items take over the page.\n const planStep = job.steps.find((step) => step.name === 'plan')\n return [\n {\n id: 'plan',\n label: site ? 'Planning your pages' : 'Planning what to build',\n state: 'done',\n ...(planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ...aiBuildItemRows(job).map((row, index) => {\n const ledger = (job.items ?? [])[index]\n // A site keeps the scaffold's own words for each stage (AGL-3596).\n const label =\n !site || !ledger\n ? row.label\n : ledger.op === 'theme'\n ? AI_SITE_LOOK_ROW_LABEL\n : ledger.op === 'layout'\n ? `Building the header and footer: ${ledger.label}`\n : ledger.op === 'form'\n ? `Building the form: ${ledger.label}`\n : ledger.op === 'page'\n ? `Writing page ${pages.indexOf(ledger) + 1} of ${pages.length}: ${ledger.label}`\n : ledger.op === 'email'\n ? 'Writing your welcome email'\n : row.label\n const screen = ledger?.op === 'page' ? aiPlanScreenFor(job.plan, ledger.label, pages.indexOf(ledger)) : null\n const finished = row.state === 'done' || row.state === 'failed'\n const net = ledger ? Math.max(0, Math.floor((ledger.creditsSpent ?? 0) - (ledger.creditsRefunded ?? 0))) : 0\n return {\n id: row.slot,\n label,\n state: row.state,\n // A part that failed while the rest is still being built says the\n // build goes on (AGL-3596): read alone, it looked like the site had.\n detail:\n row.state === 'failed' && phase === 'working'\n ? [row.detail, site ? 'The rest of your site keeps building.' : 'The rest keeps building.'].filter(Boolean).join(' ')\n : row.detail,\n ...(row.state === 'active'\n ? {\n startedAt: row.startedAt ?? null,\n hint:\n ledger?.op === 'page'\n ? AI_SITE_PAGE_HINT\n : site && ledger?.op === 'theme'\n ? AI_SITE_LOOK_HINT\n : AI_SITE_ITEM_HINT,\n }\n : {}),\n ...(screen?.sections.length ? { sections: screen.sections.map((section) => section.name) } : {}),\n ...(finished && ledger ? { credits: net } : {}),\n }\n }),\n ...aiSitePublishRow(job, phase),\n ]\n }\n const planStep = job.steps.find((step) => step.name === 'plan')\n const planDone = planStep?.status === 'done' || Boolean(job.plan)\n const stopped = phase === 'failed' || phase === 'stopped'\n const paused = job.status === 'needs_input'\n const planState: AiSiteBuildRowState = planDone\n ? 'done'\n : paused\n ? 'paused'\n : stopped\n ? 'failed'\n : phase === 'working'\n ? 'active'\n : 'waiting'\n const rows: AiSiteBuildRow[] = [\n {\n id: 'plan',\n label: 'Planning your pages',\n state: planState,\n startedAt: planStep?.startedAt ?? null,\n hint: planState === 'active' ? AI_SITE_PLAN_HINT : null,\n ...(planDone && planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ]\n // Before there is a plan, the stages a guided start always goes through\n // are named from its answers, so the page shows the whole way from the\n // start rather than one row.\n if (!job.plan && job.kind !== 'build') {\n const pagesAsked = typeof job.siteInputs?.['pages'] === 'number' ? Math.round(job.siteInputs['pages'] as number) : 0\n if (job.siteInputs) {\n // The look is designed first, before the header and footer (AGL-3660).\n rows.push({ id: 'look', label: AI_SITE_LOOK_ROW_LABEL, state: 'waiting' })\n rows.push({ id: 'layout', label: 'Building the header and footer', state: 'waiting' })\n // A guided start that says where its contact form's submissions go\n // plans that form, built after the layout and before the pages (AGL-3596).\n const submissions = job.siteInputs['submissions']\n if (submissions === 'inbox' || submissions === 'lead') {\n rows.push({ id: 'form', label: 'Building your contact form', state: 'waiting' })\n }\n rows.push({\n id: 'pages',\n label: pagesAsked > 1 ? `Writing your ${pagesAsked} pages` : 'Writing your pages',\n state: 'waiting',\n })\n rows.push(...aiSitePublishRow(job, phase))\n }\n return rows\n }\n const built = new Map<string, number>()\n for (const output of job.outputs) built.set(output.resource, (built.get(output.resource) ?? 0) + 1)\n const take = (resource: string): boolean => {\n const left = built.get(resource) ?? 0\n if (left <= 0) return false\n built.set(resource, left - 1)\n return true\n }\n const stages: Array<{ id: string; label: string; resource: string; sections?: string[] }> = []\n for (const row of CREATION_ROWS) {\n const creation = job.plan?.create.find((entry) => entry.kind === row.kind)\n // A job read without its item ledger is one from before the look had a\n // unit of its own (AGL-3660): its look is a stage only where it built one.\n if (creation || (row.resource === 'theme' && job.outputs.some((output) => output.resource === 'theme'))) {\n stages.push({ id: row.resource, label: row.label(creation?.name ?? ''), resource: row.resource })\n }\n }\n const pages = job.plan?.screens ?? []\n pages.forEach((page, index) => {\n stages.push({\n id: `page-${index}`,\n label: `Writing page ${index + 1} of ${pages.length}: ${page.title}`,\n resource: 'screen',\n sections: page.sections.map((section) => section.name),\n })\n })\n let reached = false\n for (const stage of stages) {\n const done = phase === 'done' || take(stage.resource)\n const current = !done && !reached && planDone\n if (current) reached = true\n rows.push({\n id: stage.id,\n label: stage.label,\n state: done\n ? 'done'\n : current && paused\n ? 'paused'\n : current && stopped\n ? 'failed'\n : current && phase === 'working'\n ? 'active'\n : 'waiting',\n ...(stage.sections?.length ? { sections: stage.sections } : {}),\n })\n }\n rows.push(...aiSitePublishRow(job, phase))\n return rows\n}\n\n/** The row the site's look is designed on (AGL-3660). */\nexport const AI_SITE_LOOK_ROW_LABEL = 'Designing your look'\n\n/** What the look's row says while it runs. */\nexport const AI_SITE_LOOK_HINT =\n 'Choosing a base theme, colors, fonts and the style of your buttons, cards and forms for your kind of site. This takes a few seconds.'\n\n/** What the planning row says while it runs. */\nexport const AI_SITE_PLAN_HINT =\n 'Reading your answers and choosing your pages, what each one says, and the forms and layout they need. This usually takes under a minute.'\n\n/** What the page being written says while it is the active row (AGL-3596). */\nexport const AI_SITE_PAGE_HINT =\n 'Writing this page’s sections from your plan. It is done once every section is in.'\n\n/** What any other part being built says while it is the active row. */\nexport const AI_SITE_ITEM_HINT = 'Building this now. The next step starts when it is done.'\n\n/** The plan's page an item builds: by its title, else by its place among the pages. */\nfunction aiPlanScreenFor(\n plan: AiJobSummary['plan'],\n title: string,\n index: number,\n): NonNullable<AiJobSummary['plan']>['screens'][number] | null {\n const screens = plan?.screens ?? []\n return screens.find((screen) => screen.title === title) ?? (index >= 0 ? (screens[index] ?? null) : null)\n}\n\n/**\n * A guided start ends by putting its pages live (AGL-3596): the last row, so\n * the person sees that step coming and when it is done. Only a site job\n * publishes on its own; a build publishes when asked, and says so in its plan.\n */\nfunction aiSitePublishRow(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'kind' | 'sitePublish'>>,\n phase: AiSiteBuildPhase,\n): AiSiteBuildRow[] {\n if (job.kind !== 'site') return []\n const published = (job.sitePublish?.published.length ?? 0) > 0\n return [\n {\n id: 'publish',\n label: 'Publishing your site',\n // A job that stopped or failed publishes nothing, so the row is passed over.\n state: phase === 'done' ? (published ? 'done' : 'skipped') : phase === 'working' ? 'waiting' : 'skipped',\n },\n ]\n}\n\n/**\n * How far the job is, from 0 to 1, by its rows: a finished row counts whole\n * and the active one half. `null` before there is more than one row to count.\n */\nexport function aiSiteBuildFraction(rows: readonly AiSiteBuildRow[]): number | null {\n if (rows.length < 2) return null\n const counted = rows.reduce(\n (sum, row) => sum + (row.state === 'active' ? 0.5 : row.state === 'waiting' || row.state === 'paused' ? 0 : 1),\n 0,\n )\n return Math.min(1, counted / rows.length)\n}\n\n/**\n * The job's one price (AGL-3594), never a plan's on its own: what it has used\n * so far while it works, what it used once done, and, on a failure we gave\n * back, that nothing was charged.\n */\nexport function aiSiteBuildCreditsLine(\n job: Pick<AiJobSummary, 'status' | 'creditsSpent' | 'refundedCredits'> & Partial<Pick<AiJobSummary, 'kind'>>,\n): string | null {\n const spent = Math.max(0, job.creditsSpent ?? 0)\n const refunded = Math.max(0, job.refundedCredits ?? 0)\n const net = Math.max(0, spent - refunded)\n const phase = aiSiteBuildPhase(job)\n // What the job made, by its kind; a job that names none is a site's.\n const subject = !job.kind || job.kind === 'site' ? 'This site' : aiJobKindNoun(job.kind) === 'AI job' ? 'This AI job' : `This ${aiJobKindNoun(job.kind)}`\n // A failure on our side says so in the words every surface uses (AGL-3596),\n // from the job's recorded give-back.\n const refund = phase === 'working' || phase === 'done' ? null : aiJobRefundCopy(job)\n if (refund && refunded > 0) return refund\n if (phase === 'done') return `${subject} used ${net} credits.`\n if (phase === 'working') return `Credits used so far: ${net}`\n if (refund) return `${subject} used ${net} credits. ${refund}`\n return net > 0 ? `${subject} used ${net} credits.` : null\n}\n\n/** The heading and the sentence under it, for one job's page in its current phase. */\nexport interface AiJobPageCopy {\n heading: string\n lede: string\n}\n\n/**\n * What one job's page says at the top (AGL-3594, AGL-3596). A site job reads\n * as \"Building your site\"; any other kind names what it makes — \"Building\n * your page\" — or, for a kind with no noun, the job itself.\n *\n * A guided site start publishes its pages when it finishes, and says so\n * (`sitePublish`): with a page live the heading is \"Your site is live\". A\n * done site job without that record — an older job, or one that was not a\n * guided start — built drafts, and says that instead.\n */\nexport function aiJobPageCopy(\n job: Pick<AiJobSummary, 'kind' | 'status' | 'review' | 'error'> &\n Partial<Pick<AiJobSummary, 'sitePublish' | 'items' | 'outputs'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n // Paused by the meter (AGL-3660): why, what is already built, and that it\n // carries on — never \"was not built\", which read as a dead end.\n if (job.status === 'needs_input') {\n return {\n heading: aiJobPausedTitle(job.kind),\n lede: [job.error, aiJobBuiltSoFar(job), AI_JOB_PAUSED_NEXT_COPY].filter(Boolean).join(' '),\n }\n }\n // A build is what the person asked for, item by item (AGL-3616).\n if (job.kind === 'build') return aiBuildPageCopy(job, brand)\n const noun = job.kind === 'site' ? 'site' : aiJobKindNoun(job.kind)\n const generic = noun === 'AI job'\n // `products` is the one plural noun.\n const isAre = noun.endsWith('s') ? 'are' : 'is'\n const why = job.review?.message ?? job.error ?? null\n if (phase === 'working') {\n return {\n heading: generic ? 'Your AI job is running' : `Building your ${noun}`,\n lede:\n job.kind === 'site'\n ? `${brand} AI is planning your pages and writing each one. You can leave this page; it keeps going.`\n : `${brand} AI is working on it. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'done') {\n const published = job.sitePublish?.published.length ?? 0\n // Part of the site was built (AGL-3616): what was, what was not, and its credits.\n const partial = job.kind === 'site' ? aiSitePartialCopy({ items: job.items, status: job.status }) : null\n if (job.kind === 'site' && published > 0) {\n return {\n heading: 'Your site is live',\n lede: [\n partial,\n job.sitePublish?.drafts.length\n ? 'Your pages are published, except the ones listed below, which stayed drafts.'\n : 'Your pages are published, and anyone can visit your site now.',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (partial) return { heading: 'Your site is ready', lede: `${partial} Your new pages are drafts. Publish them when you’re happy.` }\n if (job.kind === 'site' && job.sitePublish) {\n return {\n heading: 'Your site is ready',\n lede: 'None of your new pages could be published, so they are drafts. The reasons are below.',\n }\n }\n return {\n heading: generic ? 'Your AI job finished' : `Your ${noun} ${isAre} ready`,\n lede:\n job.kind === 'site'\n ? 'Your new pages are drafts. Publish them when you’re happy.'\n : 'Everything it built is an unpublished draft until you publish it.',\n }\n }\n if (phase === 'canceled') {\n return { heading: generic ? 'Your AI job was canceled' : `Your ${noun} was not built`, lede: 'The job was canceled.' }\n }\n return {\n heading: generic ? 'Your AI job stopped' : `Your ${noun} ${noun.endsWith('s') ? 'were' : 'was'} not built`,\n lede: why ?? (generic ? 'Something went wrong running this job.' : `Something went wrong building your ${noun}.`),\n }\n}\n\nconst joinWords = (names: string[]): string =>\n names.length <= 1 ? (names[0] ?? '') : `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`\n\n/**\n * What a job has already built, in a sentence (AGL-3660): its look, the header\n * and footer, its form, its pages — from the item ledger where it has one,\n * else from what it has reported as outputs. `null` when it has built nothing.\n */\nexport function aiJobBuiltSoFar(\n job: Pick<AiJobSummary, 'kind'> & Partial<Pick<AiJobSummary, 'items' | 'outputs'>>,\n): string | null {\n const built = (job.items ?? []).filter((row) => row.status === 'succeeded' || row.status === 'degraded')\n const names: string[] = []\n if (job.items?.length) {\n for (const row of built) {\n if (row.op === 'theme') names.push('your look')\n else if (row.op === 'layout') names.push('the header and footer')\n else if (row.op === 'form') names.push(`the form “${row.label}”`)\n else if (row.op === 'page') names.push(`the page “${row.label}”`)\n else if (row.op === 'email') names.push('your welcome email')\n else names.push(`${aiBuildOpNoun(row.op).toLowerCase()} “${row.label}”`)\n }\n } else {\n const outputs = job.outputs ?? []\n const count = (resource: string) => outputs.filter((output) => output.resource === resource).length\n if (count('theme')) names.push('your look')\n if (count('layout')) names.push('the header and footer')\n if (count('form')) names.push(count('form') === 1 ? 'the form' : `${count('form')} forms`)\n const pages = count('screen')\n if (pages) names.push(pages === 1 ? '1 page' : `${pages} pages`)\n }\n return names.length ? `Built so far: ${joinWords(names)}.` : null\n}\n\n/** A build's page heading and sentence (AGL-3616). */\nfunction aiBuildPageCopy(\n job: Pick<AiJobSummary, 'status' | 'review' | 'error'> & Partial<Pick<AiJobSummary, 'items' | 'sitePublish'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n const outcome = aiBuildOutcomeLine({ items: job.items ?? [], status: job.status })\n if (phase === 'working') {\n return {\n heading: 'Building what you asked for',\n lede: `${brand} AI is building each part in turn. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'stopped') {\n return {\n heading: job.review?.reason === 'plan' ? 'Your plan is ready' : 'Your build needs you',\n lede: job.review?.message ?? job.error ?? 'It waits for your decision.',\n }\n }\n if (phase === 'done') {\n const failed = (job.items ?? []).some((row) => row.status === 'failed' || row.status === 'skipped')\n const live = job.sitePublish?.published.length ?? 0\n return {\n heading: failed ? 'Most of it is ready' : 'Everything you asked for is ready',\n lede: [\n outcome,\n live ? 'Your new pages are published.' : 'Everything it built is an unpublished draft until you publish it.',\n failed ? 'Try again builds only what failed.' : '',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (phase === 'canceled') return { heading: 'Your build was canceled', lede: 'The job was canceled.' }\n return {\n heading: 'Nothing could be built',\n lede: [job.error ?? 'Something went wrong building what you asked for.', outcome].filter(Boolean).join(' '),\n }\n}\n"],"names":["aiJobConfirmingOwnPlan","aiJobKindNoun","aiJobRefundCopy","aiBuildItemRows","aiBuildOpNoun","aiBuildOutcomeLine","aiSitePartialCopy","AI_JOB_PAUSED_NEXT_COPY","aiJobPausedTitle","aiSiteBuildPhase","job","status","CREATION_ROWS","kind","resource","label","AI_SITE_LOOK_ROW_LABEL","name","aiSiteBuildRows","rows","aiSiteBuildRowsOf","aiPausedRows","at","findIndex","row","state","index","map","position","hint","startedAt","planStep","built","phase","items","length","site","pages","filter","op","steps","find","step","id","credits","Math","max","floor","creditsSpent","ledger","indexOf","screen","aiPlanScreenFor","plan","finished","net","creditsRefunded","slot","detail","Boolean","join","AI_SITE_PAGE_HINT","AI_SITE_LOOK_HINT","AI_SITE_ITEM_HINT","sections","section","aiSitePublishRow","planDone","stopped","paused","planState","AI_SITE_PLAN_HINT","pagesAsked","siteInputs","round","push","submissions","Map","output","outputs","set","get","take","left","stages","creation","create","entry","some","screens","forEach","page","title","reached","stage","done","current","published","sitePublish","aiSiteBuildFraction","counted","reduce","sum","min","aiSiteBuildCreditsLine","spent","refunded","refundedCredits","subject","refund","aiJobPageCopy","brand","heading","lede","error","aiJobBuiltSoFar","aiBuildPageCopy","noun","generic","isAre","endsWith","why","review","message","partial","drafts","joinWords","names","slice","toLowerCase","count","outcome","reason","failed","live"],"mappings":"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"}