@aglyn/plugins-ai 1.0.0-beta.236 → 1.0.0-beta.238

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 (164) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-credits-prompt.component.d.ts +44 -0
  3. package/src/lib/components/ai-credits-prompt.component.js +117 -0
  4. package/src/lib/components/ai-credits-prompt.component.js.map +1 -0
  5. package/src/lib/components/ai-job-cancel.component.d.ts +38 -0
  6. package/src/lib/components/ai-job-cancel.component.js +156 -0
  7. package/src/lib/components/ai-job-cancel.component.js.map +1 -0
  8. package/src/lib/components/ai-job-plan.component.d.ts +5 -4
  9. package/src/lib/components/ai-job-plan.component.js +48 -12
  10. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  11. package/src/lib/components/ai-job-requests.d.ts +10 -0
  12. package/src/lib/components/ai-job-requests.js +12 -4
  13. package/src/lib/components/ai-job-requests.js.map +1 -1
  14. package/src/lib/components/ai-jobs-list-page.component.js +141 -72
  15. package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
  16. package/src/lib/components/ai-site-build-page.component.js +48 -9
  17. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  18. package/src/lib/components/ai-site-start-card.component.js +68 -22
  19. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  20. package/src/lib/components/ai-usage-strip.component.d.ts +19 -2
  21. package/src/lib/components/ai-usage-strip.component.js +40 -9
  22. package/src/lib/components/ai-usage-strip.component.js.map +1 -1
  23. package/src/lib/components/assist-build-card.component.d.ts +3 -1
  24. package/src/lib/components/assist-build-card.component.js +58 -7
  25. package/src/lib/components/assist-build-card.component.js.map +1 -1
  26. package/src/lib/components/assist-jobs-drawer.component.js +66 -22
  27. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  28. package/src/lib/components/assist-panel.component.js +89 -52
  29. package/src/lib/components/assist-panel.component.js.map +1 -1
  30. package/src/lib/jobs/ai-job-admission.d.ts +22 -2
  31. package/src/lib/jobs/ai-job-admission.js.map +1 -1
  32. package/src/lib/jobs/ai-job-build-step.d.ts +24 -11
  33. package/src/lib/jobs/ai-job-build-step.js +54 -35
  34. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  35. package/src/lib/jobs/ai-job-cancel.d.ts +71 -0
  36. package/src/lib/jobs/ai-job-cancel.js +102 -0
  37. package/src/lib/jobs/ai-job-cancel.js.map +1 -0
  38. package/src/lib/jobs/ai-job-component-step.js +4 -1
  39. package/src/lib/jobs/ai-job-component-step.js.map +1 -1
  40. package/src/lib/jobs/ai-job-drafts.js +2 -0
  41. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  42. package/src/lib/jobs/ai-job-form-step.js +4 -1
  43. package/src/lib/jobs/ai-job-form-step.js.map +1 -1
  44. package/src/lib/jobs/ai-job-layout-language.d.ts +2 -0
  45. package/src/lib/jobs/ai-job-layout-language.js +13 -1
  46. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  47. package/src/lib/jobs/ai-job-layout-step.js +6 -1
  48. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  49. package/src/lib/jobs/ai-job-page-language.js +12 -0
  50. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  51. package/src/lib/jobs/ai-job-page-step.d.ts +13 -0
  52. package/src/lib/jobs/ai-job-page-step.js +46 -4
  53. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  54. package/src/lib/jobs/ai-job-plan-step.d.ts +42 -0
  55. package/src/lib/jobs/ai-job-plan-step.js +118 -18
  56. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  57. package/src/lib/jobs/ai-job-site-content.d.ts +49 -3
  58. package/src/lib/jobs/ai-job-site-content.js +196 -37
  59. package/src/lib/jobs/ai-job-site-content.js.map +1 -1
  60. package/src/lib/jobs/ai-job-site-step.js +49 -16
  61. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  62. package/src/lib/jobs/ai-job-template-step.js +4 -1
  63. package/src/lib/jobs/ai-job-template-step.js.map +1 -1
  64. package/src/lib/jobs/ai-job-text-step.d.ts +8 -0
  65. package/src/lib/jobs/ai-job-text-step.js.map +1 -1
  66. package/src/lib/jobs/ai-jobs.d.ts +55 -13
  67. package/src/lib/jobs/ai-jobs.js +213 -41
  68. package/src/lib/jobs/ai-jobs.js.map +1 -1
  69. package/src/lib/jobs/ai-layout-stock-photos.d.ts +194 -34
  70. package/src/lib/jobs/ai-layout-stock-photos.js +861 -146
  71. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  72. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.d.ts +30 -0
  73. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js +220 -0
  74. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js.map +1 -0
  75. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.d.ts +27 -0
  76. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js +151 -0
  77. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js.map +1 -0
  78. package/src/lib/layout-language/ai-layout-compiler.d.ts +33 -0
  79. package/src/lib/layout-language/ai-layout-compiler.js +381 -18
  80. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  81. package/src/lib/layout-language/ai-layout-design.d.ts +6 -3
  82. package/src/lib/layout-language/ai-layout-design.js +5 -3
  83. package/src/lib/layout-language/ai-layout-design.js.map +1 -1
  84. package/src/lib/layout-language/ai-layout-frame.d.ts +12 -0
  85. package/src/lib/layout-language/ai-layout-frame.js +58 -3
  86. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  87. package/src/lib/layout-language/ai-layout-gaps.js +3 -1
  88. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
  89. package/src/lib/layout-language/ai-layout-language.js +1 -1
  90. package/src/lib/layout-language/ai-layout-language.js.map +1 -1
  91. package/src/lib/layout-language/ai-layout-listings.d.ts +49 -1
  92. package/src/lib/layout-language/ai-layout-listings.js +117 -47
  93. package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
  94. package/src/lib/layout-language/ai-layout-pictures.d.ts +10 -1
  95. package/src/lib/layout-language/ai-layout-pictures.js +5 -2
  96. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  97. package/src/lib/layout-language/ai-layout-storefront.d.ts +82 -0
  98. package/src/lib/layout-language/ai-layout-storefront.js +223 -0
  99. package/src/lib/layout-language/ai-layout-storefront.js.map +1 -0
  100. package/src/lib/model/ai-build-job.d.ts +39 -0
  101. package/src/lib/model/ai-build-job.js +100 -1
  102. package/src/lib/model/ai-build-job.js.map +1 -1
  103. package/src/lib/model/ai-build-progress.d.ts +8 -0
  104. package/src/lib/model/ai-build-progress.js +35 -0
  105. package/src/lib/model/ai-build-progress.js.map +1 -1
  106. package/src/lib/model/ai-credit-estimate.d.ts +249 -0
  107. package/src/lib/model/ai-credit-estimate.js +278 -0
  108. package/src/lib/model/ai-credit-estimate.js.map +1 -0
  109. package/src/lib/model/ai-job-cancel-copy.d.ts +44 -0
  110. package/src/lib/model/ai-job-cancel-copy.js +53 -0
  111. package/src/lib/model/ai-job-cancel-copy.js.map +1 -0
  112. package/src/lib/model/ai-job-failure-copy.d.ts +4 -1
  113. package/src/lib/model/ai-job-failure-copy.js +8 -3
  114. package/src/lib/model/ai-job-failure-copy.js.map +1 -1
  115. package/src/lib/model/ai-jobs.types.d.ts +35 -0
  116. package/src/lib/model/ai-jobs.types.js.map +1 -1
  117. package/src/lib/model/ai-site-build-progress.js +7 -4
  118. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  119. package/src/lib/model/ai-site-job.d.ts +76 -22
  120. package/src/lib/model/ai-site-job.js +125 -23
  121. package/src/lib/model/ai-site-job.js.map +1 -1
  122. package/src/lib/model/ai-site-kinds.d.ts +4 -1
  123. package/src/lib/model/ai-site-kinds.js +35 -7
  124. package/src/lib/model/ai-site-kinds.js.map +1 -1
  125. package/src/lib/runtime/ai-doctrine-validators.d.ts +37 -1
  126. package/src/lib/runtime/ai-doctrine-validators.js +161 -7
  127. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  128. package/src/lib/runtime/ai-doctrine.js +4 -3
  129. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  130. package/src/lib/runtime/ai-node-tree.d.ts +11 -0
  131. package/src/lib/runtime/ai-node-tree.js +26 -0
  132. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  133. package/src/lib/runtime/ai-palette.generated.js +592 -14
  134. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  135. package/src/lib/server/ai-jobs-cancel.d.ts +13 -5
  136. package/src/lib/server/ai-jobs-cancel.js +27 -9
  137. package/src/lib/server/ai-jobs-cancel.js.map +1 -1
  138. package/src/lib/server/ai-jobs-gate.d.ts +12 -2
  139. package/src/lib/server/ai-jobs-gate.js +20 -9
  140. package/src/lib/server/ai-jobs-gate.js.map +1 -1
  141. package/src/lib/server/ai-jobs-resume.js +93 -35
  142. package/src/lib/server/ai-jobs-resume.js.map +1 -1
  143. package/src/lib/server/ai-jobs-route.js +45 -4
  144. package/src/lib/server/ai-jobs-route.js.map +1 -1
  145. package/src/lib/server/assist-chat.js +4 -1
  146. package/src/lib/server/assist-chat.js.map +1 -1
  147. package/src/lib/server/assist-edit.js +3 -1
  148. package/src/lib/server/assist-edit.js.map +1 -1
  149. package/src/lib/server/assist-music.d.ts +36 -0
  150. package/src/lib/server/assist-music.js +36 -0
  151. package/src/lib/server/assist-music.js.map +1 -0
  152. package/src/lib/server.js +6 -0
  153. package/src/lib/server.js.map +1 -1
  154. package/src/lib/usage/ai-usage-meter.d.ts +2 -1
  155. package/src/lib/usage/ai-usage-meter.js +17 -1
  156. package/src/lib/usage/ai-usage-meter.js.map +1 -1
  157. package/src/lib/usage/ai-usage-wire.d.ts +6 -0
  158. package/src/lib/usage/ai-usage-wire.js.map +1 -1
  159. package/src/lib/usage/assist-usage.d.ts +7 -0
  160. package/src/lib/usage/assist-usage.js +14 -4
  161. package/src/lib/usage/assist-usage.js.map +1 -1
  162. package/src/lib/usage/free-ai-credits-left.d.ts +7 -0
  163. package/src/lib/usage/free-ai-credits-left.js +4 -2
  164. package/src/lib/usage/free-ai-credits-left.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-plan.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AI_BUILD_PLAN_CREATION_NOUNS,\n AI_BUILD_PLAN_EMBED_HOST_NAMES,\n isAiPlanNewRef,\n type AiBuildPlanEmbed,\n} from '../model/ai-build-plan'\nimport type { AiJobReview, AiJobSummary } from '../model/ai-jobs.types'\nimport { aiJobCreditEstimate } from '../model/ai-build-job'\nimport { aiBuildOpNoun } from '../model/ai-build-progress'\nimport { aiJobConfirmingOwnPlan } from '../model/ai-job-activity'\nimport { aiSiteStarterFallbackOffered } from '../model/ai-job-failure-copy'\nimport { AiSiteStarterFallback } from './ai-site-starter-fallback.component'\nimport { Box, Button, Checkbox, Collapse, FormControlLabel, Stack, Typography } from '@mui/material'\nimport { useState } from 'react'\n\n/**\n * A job's plan and what it waits for (AGL-2935), inside the AI jobs drawer\n * and the dialog that started the job (AGL-3593): what the job reuses from\n * the site, what it creates and why, the screens it builds — and, while the\n * job waits for a person, the one button that confirms the plan or tries a\n * refused step again. Both send the same request (`resumeAiJobRequest`);\n * this renders.\n */\nexport interface AiJobPlanProps {\n job: AiJobSummary\n /**\n * Confirms the plan, or tries the refused step again. A build whose request\n * asked to publish confirms with `publish` when its box is ticked (AGL-3616).\n */\n onResume: (job: AiJobSummary, options?: { publish?: boolean }) => void\n /** A resume for this job is in flight. */\n busy?: boolean\n /**\n * The viewer is staff (AGL-3078), who can open where a refused answer broke\n * its rules and the outline of those parts, to tell a shape the rules should\n * allow from an answer that ignored its re-ask. A member reads the findings.\n */\n staff?: boolean\n /**\n * The signed-in user, for \"Use the starter site instead\" on a guided start\n * that did not work out (AGL-3594); the action is drawn only when given.\n */\n user?: Parameters<typeof AiSiteStarterFallback>[0]['user']\n}\n\n/**\n * Where a refused answer broke its rules, and the outline of those parts, as\n * lines staff read (AGL-3078): each finding with the nodes or plan entries it\n * names, then each outlined node indented by its depth, with its element, a\n * Grid's layout as written, and the names of what it sets and holds.\n */\nexport function aiJobReviewDetails(review: AiJobReview | null): string[] {\n if (review?.reason !== 'doctrine') return []\n const lines = review.findings.flatMap((finding) => {\n const where = finding.nodeIds?.length\n ? `nodes ${finding.nodeIds.join(', ')}`\n : finding.paths?.length\n ? `at ${finding.paths.join(', ')}`\n : null\n return where ? [`${finding.rule === null ? '' : `Rule ${finding.rule} `}${finding.code}: ${where}`] : []\n })\n for (const node of review.outline ?? []) {\n lines.push(\n [\n `${' '.repeat(node.depth)}${node.id} ${node.componentId}`,\n ...Object.entries(node.grid ?? {}).map(([name, value]) => `${name}=${JSON.stringify(value)}`),\n ...(node.props.length ? [`props ${node.props.join(', ')}`] : []),\n ...(node.sx?.length ? [`sx ${node.sx.join(', ')}`] : []),\n ...(node.children.length ? [`holds ${node.children.join(', ')}`] : []),\n ].join(' · '),\n )\n }\n return lines\n}\n\n/**\n * A third-party player the plan lists, with what it costs the page, read\n * before the member confirms it (rule 16, AGL-3433). The cost is the\n * platform's sentence, never the model's: the player is framed lazily, so its\n * host's code loads when a visitor reaches it, whether or not they press play.\n */\nexport function aiPlanEmbedLine(\n embed: AiBuildPlanEmbed,\n named: (ref: string) => string,\n): string {\n const host = AI_BUILD_PLAN_EMBED_HOST_NAMES[embed.host]\n const where = isAiPlanNewRef(embed.where) ? `the component ${named(embed.where)}` : embed.where\n const link = embed.url ? ` playing ${embed.url}` : ', its link left for you to paste'\n return `Embeds a ${host} player on ${where}${link}, as you asked (“${embed.asked}”). It loads ${host}’s own code when a visitor reaches it, whether or not they press play.`\n}\n\nexport function AiJobPlan({\n job,\n onResume,\n busy = false,\n staff = false,\n user,\n}: AiJobPlanProps): JSX.Element | null {\n const [detailsOpen, setDetailsOpen] = useState(false)\n // Unticked until the person ticks it: a build publishes only when its\n // request asked AND they confirm it here (AGL-3616).\n const [publish, setPublish] = useState(false)\n const { plan, review } = job\n if (!plan && !review) return null\n const details = staff ? aiJobReviewDetails(review) : []\n /** A reference as a person reads it: the record's name, or what the plan creates. */\n const named = (ref: string | null): string =>\n !ref\n ? ''\n : isAiPlanNewRef(ref)\n ? ref.slice('new:'.length)\n : (plan?.labels[ref] ?? ref)\n // A plan the machine is confirming itself is not one to confirm (AGL-3596).\n const waiting = job.status === 'needs_review' && review !== null && !aiJobConfirmingOwnPlan(job)\n // The guard rail (AGL-2911): what the plan is estimated to cost is read\n // before it is confirmed, not after it has been spent. An estimate, and\n // said to be one — the plan's own passes at the nominal credits a step\n // holds, where what a step really costs is its model's tokens. A page job\n // counts the creations it builds before its page (AGL-3031).\n const estimate =\n plan && waiting && review.reason === 'plan' ? aiJobCreditEstimate(job.kind, plan) : 0\n const offersPublish = job.kind === 'build' && job.publishAsked === true && waiting && review.reason === 'plan'\n return (\n <Box sx={{ mt: 1 }}>\n {plan && (\n <Stack spacing={0.5} role=\"list\" aria-label=\"Plan\">\n <Typography variant=\"caption\" color=\"text.secondary\">\n {plan.status === 'confirmed' ? 'Confirmed plan' : 'Proposed plan'}\n </Typography>\n {plan.reuse.map((entry, index) => (\n <Typography key={`reuse-${index}`} variant=\"body2\" role=\"listitem\">\n Reuses the {entry.kind === 'screen' ? 'page' : entry.kind} {named(entry.id)} — {entry.purpose}\n </Typography>\n ))}\n {plan.create.map((entry, index) => (\n <Typography key={`create-${index}`} variant=\"body2\" role=\"listitem\">\n Creates the {AI_BUILD_PLAN_CREATION_NOUNS[entry.kind].noun} {entry.name}\n {entry.duplicateOf\n ? `, from a copy of ${named(entry.duplicateOf)}`\n : ''}{' '}\n — {entry.why}\n </Typography>\n ))}\n {plan.screens.map((screen, index) => (\n <Typography key={`screen-${index}`} variant=\"body2\" role=\"listitem\">\n {screen.record\n ? `Builds the record template ${screen.title}, a page per record of ${named(screen.record.dataset)} at /${screen.record.base}/…`\n : `Builds the page ${screen.title} at ${screen.slug}`}\n {screen.duplicateOf\n ? `, from a copy of ${named(screen.duplicateOf)}`\n : ''}\n {screen.layout ? ` in ${named(screen.layout)}` : ''}\n {screen.sections.length\n ? `: ${screen.sections.map((section) => section.name).join(', ')}`\n : ''}\n </Typography>\n ))}\n {(plan.items ?? []).map((item) => (\n <Typography key={`item-${item.slot}`} variant=\"body2\" role=\"listitem\">\n {`Makes the ${aiBuildOpNoun(item.op).toLowerCase()} ${item.name}`}\n {item.why ? ` — ${item.why}` : ''}\n </Typography>\n ))}\n {(plan.embeds ?? []).map((embed, index) => (\n <Typography key={`embed-${index}`} variant=\"body2\" role=\"listitem\">\n {aiPlanEmbedLine(embed, named)}\n </Typography>\n ))}\n </Stack>\n )}\n {/*\n A review written for the member (AGL-3594) carries the checks' own\n sentence as `detail`: its findings are then staff reading too, and the\n member reads the job's one sentence alone.\n */}\n {staff && review?.detail && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 0.5 }}>\n {review.detail}\n </Typography>\n )}\n {review?.reason === 'doctrine' && review.findings.length > 0 && (staff || !review.detail) && (\n <Box\n component=\"ul\"\n sx={{ my: 0.5, pl: 2.5 }}\n aria-label=\"Building rules broken\"\n >\n {review.findings.map((finding, index) => (\n <Typography\n key={`finding-${index}`}\n component=\"li\"\n variant=\"caption\"\n >\n {finding.message}\n </Typography>\n ))}\n </Box>\n )}\n {details.length > 0 && (\n <Box sx={{ mt: 0.5 }}>\n <Button\n size=\"small\"\n aria-expanded={detailsOpen}\n onClick={() => setDetailsOpen((prior) => !prior)}\n >\n {detailsOpen ? 'Hide what was refused' : 'Show what was refused'}\n </Button>\n <Collapse in={detailsOpen} unmountOnExit>\n <Typography\n variant=\"caption\"\n component=\"pre\"\n aria-label=\"What was refused\"\n sx={{ m: 0, fontFamily: 'monospace', whiteSpace: 'pre', overflowX: 'auto' }}\n >\n {details.join('\\n')}\n </Typography>\n </Collapse>\n </Box>\n )}\n {estimate > 0 && (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', mt: 1 }}\n >\n {`Estimated cost: about ${estimate.toLocaleString('en-US')} credits. What it costs is what its steps spend.`}\n </Typography>\n )}\n {waiting && review.retryRefusal && review.reason !== 'plan' && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 1 }}>\n {review.retryRefusal}\n </Typography>\n )}\n {offersPublish && (\n <FormControlLabel\n sx={{ display: 'flex', mt: 1 }}\n control={<Checkbox size=\"small\" checked={publish} onChange={(event) => setPublish(event.target.checked)} />}\n label={<Typography variant=\"body2\">{'Publish the new pages when they are built'}</Typography>}\n />\n )}\n {waiting && (\n <Button\n size=\"small\"\n variant=\"contained\"\n // Disabled with its reason above when the Free allowance left\n // cannot pay for another try (AGL-3594).\n disabled={busy || (review.reason !== 'plan' && Boolean(review.retryRefusal))}\n onClick={() => onResume(job, offersPublish && publish ? { publish: true } : undefined)}\n sx={{ mt: 1 }}\n >\n {review.reason === 'plan' ? 'Confirm plan' : 'Try again'}\n </Button>\n )}\n {/* A guided start that did not work out can take the starter instead (AGL-3594). */}\n {waiting && user && aiSiteStarterFallbackOffered(job) && <AiSiteStarterFallback job={job} user={user} />}\n </Box>\n )\n}\n\nexport default AiJobPlan\n"],"names":["AI_BUILD_PLAN_CREATION_NOUNS","AI_BUILD_PLAN_EMBED_HOST_NAMES","isAiPlanNewRef","aiJobCreditEstimate","aiBuildOpNoun","aiJobConfirmingOwnPlan","aiSiteStarterFallbackOffered","AiSiteStarterFallback","Box","Button","Checkbox","Collapse","FormControlLabel","Stack","Typography","useState","aiJobReviewDetails","review","reason","lines","findings","flatMap","finding","where","nodeIds","length","join","paths","rule","code","node","outline","push","repeat","depth","id","componentId","Object","entries","grid","map","name","value","JSON","stringify","props","sx","children","aiPlanEmbedLine","embed","named","host","link","url","asked","AiJobPlan","job","onResume","busy","staff","user","plan","detailsOpen","setDetailsOpen","publish","setPublish","details","ref","slice","labels","waiting","status","estimate","kind","offersPublish","publishAsked","mt","spacing","role","aria-label","variant","color","reuse","entry","index","purpose","create","noun","duplicateOf","why","screens","screen","record","title","dataset","base","slug","layout","sections","section","items","item","op","toLowerCase","slot","embeds","detail","component","my","pl","message","size","aria-expanded","onClick","prior","in","unmountOnExit","m","fontFamily","whiteSpace","overflowX","display","toLocaleString","retryRefusal","control","checked","onChange","event","target","label","disabled","Boolean","undefined"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-plan.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AI_BUILD_PLAN_CREATION_NOUNS,\n AI_BUILD_PLAN_EMBED_HOST_NAMES,\n isAiPlanNewRef,\n type AiBuildPlanEmbed,\n} from '../model/ai-build-plan'\nimport type { AiJobReview, AiJobSummary } from '../model/ai-jobs.types'\nimport { aiBuildFreePageCapNote, aiBuildSmaller, aiJobCreditRange } from '../model/ai-build-job'\nimport { aiCreditRangeText, aiCreditsPromptFor } from '../model/ai-credit-estimate'\nimport { aiFreeCreditsResetLabel } from '../model/ai-site-job'\nimport { AiCreditsPromptNotice } from './ai-credits-prompt.component'\nimport type { AiJobResumeOptions } from './ai-job-requests'\nimport { aiBuildOpNoun } from '../model/ai-build-progress'\nimport { aiJobConfirmingOwnPlan } from '../model/ai-job-activity'\nimport { aiSiteStarterFallbackOffered } from '../model/ai-job-failure-copy'\nimport { AiSiteStarterFallback } from './ai-site-starter-fallback.component'\nimport { Box, Button, Checkbox, Collapse, FormControlLabel, Stack, Typography } from '@mui/material'\nimport { useState } from 'react'\n\n/**\n * A job's plan and what it waits for (AGL-2935), inside the AI jobs drawer\n * and the dialog that started the job (AGL-3593): what the job reuses from\n * the site, what it creates and why, the screens it builds — and, while the\n * job waits for a person, the one button that confirms the plan or tries a\n * refused step again. Both send the same request (`resumeAiJobRequest`);\n * this renders.\n */\nexport interface AiJobPlanProps {\n job: AiJobSummary\n /**\n * Confirms the plan, or tries the refused step again. A build whose request\n * asked to publish confirms with `publish` when its box is ticked (AGL-3616).\n */\n onResume: (job: AiJobSummary, options?: AiJobResumeOptions) => void\n /** A resume for this job is in flight. */\n busy?: boolean\n /**\n * The viewer is staff (AGL-3078), who can open where a refused answer broke\n * its rules and the outline of those parts, to tell a shape the rules should\n * allow from an answer that ignored its re-ask. A member reads the findings.\n */\n staff?: boolean\n /**\n * The signed-in user, for \"Use the starter site instead\" on a guided start\n * that did not work out (AGL-3594); the action is drawn only when given.\n */\n user?: Parameters<typeof AiSiteStarterFallback>[0]['user']\n /** The workspace's path slug, for the Upgrade a Free build past what is left offers (AGL-3722). */\n orgSlug?: string | null\n}\n\n/**\n * Where a refused answer broke its rules, and the outline of those parts, as\n * lines staff read (AGL-3078): each finding with the nodes or plan entries it\n * names, then each outlined node indented by its depth, with its element, a\n * Grid's layout as written, and the names of what it sets and holds.\n */\nexport function aiJobReviewDetails(review: AiJobReview | null): string[] {\n if (review?.reason !== 'doctrine') return []\n const lines = review.findings.flatMap((finding) => {\n const where = finding.nodeIds?.length\n ? `nodes ${finding.nodeIds.join(', ')}`\n : finding.paths?.length\n ? `at ${finding.paths.join(', ')}`\n : null\n return where ? [`${finding.rule === null ? '' : `Rule ${finding.rule} `}${finding.code}: ${where}`] : []\n })\n for (const node of review.outline ?? []) {\n lines.push(\n [\n `${' '.repeat(node.depth)}${node.id} ${node.componentId}`,\n ...Object.entries(node.grid ?? {}).map(([name, value]) => `${name}=${JSON.stringify(value)}`),\n ...(node.props.length ? [`props ${node.props.join(', ')}`] : []),\n ...(node.sx?.length ? [`sx ${node.sx.join(', ')}`] : []),\n ...(node.children.length ? [`holds ${node.children.join(', ')}`] : []),\n ].join(' · '),\n )\n }\n return lines\n}\n\n/**\n * A third-party player the plan lists, with what it costs the page, read\n * before the member confirms it (rule 16, AGL-3433). The cost is the\n * platform's sentence, never the model's: the player is framed lazily, so its\n * host's code loads when a visitor reaches it, whether or not they press play.\n */\nexport function aiPlanEmbedLine(\n embed: AiBuildPlanEmbed,\n named: (ref: string) => string,\n): string {\n const host = AI_BUILD_PLAN_EMBED_HOST_NAMES[embed.host]\n const where = isAiPlanNewRef(embed.where) ? `the component ${named(embed.where)}` : embed.where\n const link = embed.url ? ` playing ${embed.url}` : ', its link left for you to paste'\n return `Embeds a ${host} player on ${where}${link}, as you asked (“${embed.asked}”). It loads ${host}’s own code when a visitor reaches it, whether or not they press play.`\n}\n\nexport function AiJobPlan({\n job,\n onResume,\n busy = false,\n staff = false,\n user,\n orgSlug,\n}: AiJobPlanProps): JSX.Element | null {\n const [detailsOpen, setDetailsOpen] = useState(false)\n // Unticked until the person ticks it: a build publishes only when its\n // request asked AND they confirm it here (AGL-3616).\n const [publish, setPublish] = useState(false)\n const { plan, review } = job\n if (!plan && !review) return null\n const details = staff ? aiJobReviewDetails(review) : []\n /** A reference as a person reads it: the record's name, or what the plan creates. */\n const named = (ref: string | null): string =>\n !ref\n ? ''\n : isAiPlanNewRef(ref)\n ? ref.slice('new:'.length)\n : (plan?.labels[ref] ?? ref)\n // A plan the machine is confirming itself is not one to confirm (AGL-3596).\n const waiting = job.status === 'needs_review' && review !== null && !aiJobConfirmingOwnPlan(job)\n // The guard rail (AGL-2911): what the plan is estimated to cost is read\n // before it is confirmed, not after it has been spent. An estimate, and\n // said to be one: what it is likely to cost — what builds like it measured\n // — and its ceiling, every pass at its reserve (AGL-3722). A page job\n // counts the creations it builds before its page (AGL-3031).\n const range = plan && waiting && review.reason === 'plan' ? aiJobCreditRange(job.kind, plan) : null\n // A Free build past what is left asks first (AGL-3722): build what fits,\n // the home page first, or upgrade — never a refusal with no way on.\n const freeCredits = review?.freeCredits ?? null\n const prompt =\n plan && range && job.kind === 'build' ? aiCreditsPromptFor(range, freeCredits, aiBuildSmaller(plan)) : null\n // A Free build at its page cap says so (AGL-3722): pages past it were left for a later request.\n const pageCapNote =\n plan && waiting && review.reason === 'plan' && job.kind === 'build'\n ? aiBuildFreePageCapNote(plan, Boolean(freeCredits))\n : null\n const offersPublish = job.kind === 'build' && job.publishAsked === true && waiting && review.reason === 'plan'\n const publishing = offersPublish && publish ? { publish: true } : {}\n return (\n <Box sx={{ mt: 1 }}>\n {plan && (\n <Stack spacing={0.5} role=\"list\" aria-label=\"Plan\">\n <Typography variant=\"caption\" color=\"text.secondary\">\n {plan.status === 'confirmed' ? 'Confirmed plan' : 'Proposed plan'}\n </Typography>\n {plan.reuse.map((entry, index) => (\n <Typography key={`reuse-${index}`} variant=\"body2\" role=\"listitem\">\n Reuses the {entry.kind === 'screen' ? 'page' : entry.kind} {named(entry.id)} — {entry.purpose}\n </Typography>\n ))}\n {plan.create.map((entry, index) => (\n <Typography key={`create-${index}`} variant=\"body2\" role=\"listitem\">\n Creates the {AI_BUILD_PLAN_CREATION_NOUNS[entry.kind].noun} {entry.name}\n {entry.duplicateOf\n ? `, from a copy of ${named(entry.duplicateOf)}`\n : ''}{' '}\n — {entry.why}\n </Typography>\n ))}\n {plan.screens.map((screen, index) => (\n <Typography key={`screen-${index}`} variant=\"body2\" role=\"listitem\">\n {screen.record\n ? `Builds the record template ${screen.title}, a page per record of ${named(screen.record.dataset)} at /${screen.record.base}/…`\n : `Builds the page ${screen.title} at ${screen.slug}`}\n {screen.duplicateOf\n ? `, from a copy of ${named(screen.duplicateOf)}`\n : ''}\n {screen.layout ? ` in ${named(screen.layout)}` : ''}\n {screen.sections.length\n ? `: ${screen.sections.map((section) => section.name).join(', ')}`\n : ''}\n </Typography>\n ))}\n {(plan.items ?? []).map((item) => (\n <Typography key={`item-${item.slot}`} variant=\"body2\" role=\"listitem\">\n {`Makes the ${aiBuildOpNoun(item.op).toLowerCase()} ${item.name}`}\n {item.why ? ` — ${item.why}` : ''}\n </Typography>\n ))}\n {(plan.embeds ?? []).map((embed, index) => (\n <Typography key={`embed-${index}`} variant=\"body2\" role=\"listitem\">\n {aiPlanEmbedLine(embed, named)}\n </Typography>\n ))}\n </Stack>\n )}\n {/*\n A review written for the member (AGL-3594) carries the checks' own\n sentence as `detail`: its findings are then staff reading too, and the\n member reads the job's one sentence alone.\n */}\n {staff && review?.detail && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 0.5 }}>\n {review.detail}\n </Typography>\n )}\n {review?.reason === 'doctrine' && review.findings.length > 0 && (staff || !review.detail) && (\n <Box\n component=\"ul\"\n sx={{ my: 0.5, pl: 2.5 }}\n aria-label=\"Building rules broken\"\n >\n {review.findings.map((finding, index) => (\n <Typography\n key={`finding-${index}`}\n component=\"li\"\n variant=\"caption\"\n >\n {finding.message}\n </Typography>\n ))}\n </Box>\n )}\n {details.length > 0 && (\n <Box sx={{ mt: 0.5 }}>\n <Button\n size=\"small\"\n aria-expanded={detailsOpen}\n onClick={() => setDetailsOpen((prior) => !prior)}\n >\n {detailsOpen ? 'Hide what was refused' : 'Show what was refused'}\n </Button>\n <Collapse in={detailsOpen} unmountOnExit>\n <Typography\n variant=\"caption\"\n component=\"pre\"\n aria-label=\"What was refused\"\n sx={{ m: 0, fontFamily: 'monospace', whiteSpace: 'pre', overflowX: 'auto' }}\n >\n {details.join('\\n')}\n </Typography>\n </Collapse>\n </Box>\n )}\n {range && range.ceiling > 0 && (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', mt: 1 }}\n >\n {`Estimated cost: ${aiCreditRangeText(range)}. What it costs is what its steps spend.`}\n {freeCredits && !prompt\n ? ` You have ${freeCredits.left.toLocaleString('en-US')} of your free AI credits left this month, until ${aiFreeCreditsResetLabel(freeCredits.resetsOn)}.`\n : ''}\n </Typography>\n )}\n {pageCapNote && (\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ display: 'block', mt: 0.5 }}>\n {pageCapNote}\n </Typography>\n )}\n {waiting && review.retryRefusal && review.reason !== 'plan' && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 1 }}>\n {review.retryRefusal}\n </Typography>\n )}\n {offersPublish && (\n <FormControlLabel\n sx={{ display: 'flex', mt: 1 }}\n control={<Checkbox size=\"small\" checked={publish} onChange={(event) => setPublish(event.target.checked)} />}\n label={<Typography variant=\"body2\">{'Publish the new pages when they are built'}</Typography>}\n />\n )}\n {waiting && prompt && (\n <AiCreditsPromptNotice\n prompt={prompt}\n noun=\"build\"\n orgSlug={orgSlug}\n busy={busy}\n onBuildWhatFits={() => onResume(job, { ...publishing, creditsConfirmed: true })}\n onSmaller={() => onResume(job, { ...publishing, creditsConfirmed: true, reduce: 'first-page' })}\n />\n )}\n {waiting && !prompt && (\n <Button\n size=\"small\"\n variant=\"contained\"\n // Disabled with its reason above when the Free allowance left\n // cannot pay for another try (AGL-3594).\n disabled={busy || (review.reason !== 'plan' && Boolean(review.retryRefusal))}\n onClick={() => onResume(job, offersPublish && publish ? publishing : undefined)}\n sx={{ mt: 1 }}\n >\n {review.reason === 'plan' ? 'Confirm plan' : 'Try again'}\n </Button>\n )}\n {/* A guided start that did not work out can take the starter instead (AGL-3594). */}\n {waiting && user && aiSiteStarterFallbackOffered(job) && <AiSiteStarterFallback job={job} user={user} />}\n </Box>\n )\n}\n\nexport default AiJobPlan\n"],"names":["AI_BUILD_PLAN_CREATION_NOUNS","AI_BUILD_PLAN_EMBED_HOST_NAMES","isAiPlanNewRef","aiBuildFreePageCapNote","aiBuildSmaller","aiJobCreditRange","aiCreditRangeText","aiCreditsPromptFor","aiFreeCreditsResetLabel","AiCreditsPromptNotice","aiBuildOpNoun","aiJobConfirmingOwnPlan","aiSiteStarterFallbackOffered","AiSiteStarterFallback","Box","Button","Checkbox","Collapse","FormControlLabel","Stack","Typography","useState","aiJobReviewDetails","review","reason","lines","findings","flatMap","finding","where","nodeIds","length","join","paths","rule","code","node","outline","push","repeat","depth","id","componentId","Object","entries","grid","map","name","value","JSON","stringify","props","sx","children","aiPlanEmbedLine","embed","named","host","link","url","asked","AiJobPlan","job","onResume","busy","staff","user","orgSlug","plan","detailsOpen","setDetailsOpen","publish","setPublish","details","ref","slice","labels","waiting","status","range","kind","freeCredits","prompt","pageCapNote","Boolean","offersPublish","publishAsked","publishing","mt","spacing","role","aria-label","variant","color","reuse","entry","index","purpose","create","noun","duplicateOf","why","screens","screen","record","title","dataset","base","slug","layout","sections","section","items","item","op","toLowerCase","slot","embeds","detail","component","my","pl","message","size","aria-expanded","onClick","prior","in","unmountOnExit","m","fontFamily","whiteSpace","overflowX","ceiling","display","left","toLocaleString","resetsOn","retryRefusal","control","checked","onChange","event","target","label","onBuildWhatFits","creditsConfirmed","onSmaller","reduce","disabled","undefined"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { type MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token';
2
+ import { type AiCreditsPrompt } from '../model/ai-credit-estimate';
2
3
  import type { AiJobSummary } from '../model/ai-jobs.types';
3
4
  import { type AssistBuildProposal } from '../model/assist-build';
4
5
  /**
@@ -15,6 +16,11 @@ import { type AssistBuildProposal } from '../model/assist-build';
15
16
  export interface AiJobDecision {
16
17
  job: AiJobSummary | null;
17
18
  error: string | null;
19
+ /**
20
+ * A Free job past what is left (AGL-3722): the door's 409 asks for the
21
+ * person's go-ahead with these figures and choices instead of starting it.
22
+ */
23
+ credits?: AiCreditsPrompt | null;
18
24
  }
19
25
  /**
20
26
  * What a resume asks beyond confirming (AGL-3616): a build's plan confirmed
@@ -23,6 +29,10 @@ export interface AiJobDecision {
23
29
  export interface AiJobResumeOptions {
24
30
  publish?: boolean;
25
31
  retry?: 'failed-items';
32
+ /** "Build what fits" (AGL-3722): a Free job past what is left, started on the person's go-ahead. */
33
+ creditsConfirmed?: boolean;
34
+ /** "Build the home page first" (AGL-3722): the plan narrowed to its home page and what it needs. */
35
+ reduce?: 'first-page';
26
36
  }
27
37
  /**
28
38
  * Confirms a plan, or tries again a step whose answer broke a building rule
@@ -16,6 +16,7 @@
16
16
  */ 'use client';
17
17
  import { _ as _extends } from "@swc/helpers/_/_extends";
18
18
  import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
19
+ import { AI_CREDITS_CONFIRM_CODE } from "../model/ai-credit-estimate.js";
19
20
  import { assistBuildJobRequest } from "../model/assist-build.js";
20
21
  import { publishAiJob } from "./ai-jobs-store.js";
21
22
  /**
@@ -23,7 +24,7 @@ import { publishAiJob } from "./ai-jobs-store.js";
23
24
  * (AGL-2935). The door runs the next step inline and answers with the job.
24
25
  */ export async function resumeAiJobRequest(user, orgId, job, options = {}) {
25
26
  try {
26
- var _ref, _ref1;
27
+ var _ref, _ref1, _ref2;
27
28
  const response = await authorizedFetch(user, `/api/ai/jobs/${encodeURIComponent(job.id)}/resume`, {
28
29
  method: 'POST',
29
30
  headers: {
@@ -36,15 +37,22 @@ import { publishAiJob } from "./ai-jobs-store.js";
36
37
  publish: true
37
38
  } : {}, options.retry ? {
38
39
  retry: options.retry
40
+ } : {}, options.creditsConfirmed ? {
41
+ creditsConfirmed: true
42
+ } : {}, options.reduce ? {
43
+ reduce: options.reduce
39
44
  } : {}))
40
45
  });
41
46
  const payload = await response.json().catch(()=>null);
42
47
  const next = (_ref = payload == null ? void 0 : payload.job) != null ? _ref : null;
43
48
  publishAiJob(next);
44
- return {
49
+ const credits = response.status === 409 && (payload == null ? void 0 : payload.code) === AI_CREDITS_CONFIRM_CODE ? (_ref1 = payload == null ? void 0 : payload.credits) != null ? _ref1 : null : null;
50
+ return _extends({
45
51
  job: next,
46
- error: response.ok ? null : String((_ref1 = payload == null ? void 0 : payload.error) != null ? _ref1 : 'The job could not be resumed — try again.')
47
- };
52
+ error: response.ok ? null : String((_ref2 = payload == null ? void 0 : payload.error) != null ? _ref2 : 'The job could not be resumed — try again.')
53
+ }, credits ? {
54
+ credits
55
+ } : {});
48
56
  } catch (unused) {
49
57
  return {
50
58
  job: null,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-requests.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'use client'\n\nimport { authorizedFetch, type MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token'\nimport type { AiJobSummary } from '../model/ai-jobs.types'\nimport { assistBuildJobRequest, type AssistBuildProposal } from '../model/assist-build'\nimport { publishAiJob } from './ai-jobs-store'\n\n/**\n * The two decisions a person makes about a job — confirm its plan (or try a\n * refused step again), and cancel it — as ONE request each, whichever\n * surface they press it on (AGL-3593). The AI jobs drawer and the dialog that\n * started the job both call these, so confirming in the dialog is the same\n * door, the same body and the same answer as confirming in the drawer.\n *\n * Each hands the job it gets back to every counting surface, so the top-bar\n * indicator stops saying \"plan ready\" the moment the plan is confirmed.\n */\n\n/** A door's answer: the job as it now stands, and the refusal when there is one. */\nexport interface AiJobDecision {\n job: AiJobSummary | null\n error: string | null\n}\n\n/**\n * What a resume asks beyond confirming (AGL-3616): a build's plan confirmed\n * with its publish box ticked, or a finished build's failed items tried again.\n */\nexport interface AiJobResumeOptions {\n publish?: boolean\n retry?: 'failed-items'\n}\n\n/**\n * Confirms a plan, or tries again a step whose answer broke a building rule\n * (AGL-2935). The door runs the next step inline and answers with the job.\n */\nexport async function resumeAiJobRequest(\n user: MaybeTokenSource,\n orgId: string,\n job: Pick<AiJobSummary, 'id' | 'hostId'>,\n options: AiJobResumeOptions = {},\n): Promise<AiJobDecision> {\n try {\n const response = await authorizedFetch(\n user,\n `/api/ai/jobs/${encodeURIComponent(job.id)}/resume`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n orgId,\n hostId: job.hostId,\n ...(options.publish ? { publish: true } : {}),\n ...(options.retry ? { retry: options.retry } : {}),\n }),\n },\n )\n const payload = await response.json().catch(() => null)\n const next = (payload?.job as AiJobSummary | undefined) ?? null\n publishAiJob(next)\n return {\n job: next,\n error: response.ok\n ? null\n : String(payload?.error ?? 'The job could not be resumed — try again.'),\n }\n } catch {\n return { job: null, error: 'The job could not be resumed — try again.' }\n }\n}\n\n/**\n * Starts the build a chat turn proposed (AGL-3616): one `build` job on the\n * proposal's site, through the same door every job is created by, which\n * climbs its own gates and plans inline. What comes back stops at its plan\n * card; nothing is built until the person confirms it.\n */\nexport async function startAssistBuildRequest(\n user: MaybeTokenSource,\n orgId: string,\n proposal: AssistBuildProposal,\n): Promise<AiJobDecision> {\n const failed = 'The build could not be planned — try asking again.'\n try {\n const response = await authorizedFetch(user, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(assistBuildJobRequest(orgId, proposal)),\n })\n const payload = await response.json().catch(() => null)\n const next = (payload?.job as AiJobSummary | undefined) ?? null\n if (response.ok && next) publishAiJob(next)\n return {\n job: response.ok ? next : null,\n error: response.ok && next ? null : String(payload?.error ?? failed),\n }\n } catch {\n return { job: null, error: failed }\n }\n}\n\n/** Cancels a job that has not settled. */\nexport async function cancelAiJobRequest(\n user: MaybeTokenSource,\n orgId: string,\n jobId: string,\n): Promise<AiJobDecision> {\n try {\n const response = await authorizedFetch(\n user,\n `/api/ai/jobs/${encodeURIComponent(jobId)}/cancel`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ orgId }),\n },\n )\n const payload = await response.json().catch(() => null)\n const next = (payload?.job as AiJobSummary | undefined) ?? null\n publishAiJob(next)\n return {\n job: next,\n error: response.ok\n ? null\n : String(payload?.error ?? 'The job could not be canceled — try again.'),\n }\n } catch {\n return { job: null, error: 'The job could not be canceled — try again.' }\n }\n}\n"],"names":["authorizedFetch","assistBuildJobRequest","publishAiJob","resumeAiJobRequest","user","orgId","job","options","response","encodeURIComponent","id","method","headers","body","JSON","stringify","hostId","publish","retry","payload","json","catch","next","error","ok","String","startAssistBuildRequest","proposal","failed","cancelAiJobRequest","jobId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,eAAe,QAA+B,2CAA0C;AAEjG,SAASC,qBAAqB,QAAkC,2BAAuB;AACvF,SAASC,YAAY,QAAQ,qBAAiB;AA4B9C;;;CAGC,GACD,OAAO,eAAeC,mBACpBC,IAAsB,EACtBC,KAAa,EACbC,GAAwC,EACxCC,UAA8B,CAAC,CAAC;IAEhC,IAAI;;QACF,MAAMC,WAAW,MAAMR,gBACrBI,MACA,CAAC,aAAa,EAAEK,mBAAmBH,IAAII,EAAE,EAAE,OAAO,CAAC,EACnD;YACEC,QAAQ;YACRC,SAAS;gBAAE,gBAAgB;YAAmB;YAC9CC,MAAMC,KAAKC,SAAS,CAAC;gBACnBV;gBACAW,QAAQV,IAAIU,MAAM;eACdT,QAAQU,OAAO,GAAG;gBAAEA,SAAS;YAAK,IAAI,CAAC,GACvCV,QAAQW,KAAK,GAAG;gBAAEA,OAAOX,QAAQW,KAAK;YAAC,IAAI,CAAC;QAEpD;QAEF,MAAMC,UAAU,MAAMX,SAASY,IAAI,GAAGC,KAAK,CAAC,IAAM;QAClD,MAAMC,eAAQH,2BAAAA,QAASb,GAAG,mBAAiC;QAC3DJ,aAAaoB;QACb,OAAO;YACLhB,KAAKgB;YACLC,OAAOf,SAASgB,EAAE,GACd,OACAC,gBAAON,2BAAAA,QAASI,KAAK,oBAAI;QAC/B;IACF,EAAE,eAAM;QACN,OAAO;YAAEjB,KAAK;YAAMiB,OAAO;QAA4C;IACzE;AACF;AAEA;;;;;CAKC,GACD,OAAO,eAAeG,wBACpBtB,IAAsB,EACtBC,KAAa,EACbsB,QAA6B;IAE7B,MAAMC,SAAS;IACf,IAAI;;QACF,MAAMpB,WAAW,MAAMR,gBAAgBI,MAAM,gBAAgB;YAC3DO,QAAQ;YACRC,SAAS;gBAAE,gBAAgB;YAAmB;YAC9CC,MAAMC,KAAKC,SAAS,CAACd,sBAAsBI,OAAOsB;QACpD;QACA,MAAMR,UAAU,MAAMX,SAASY,IAAI,GAAGC,KAAK,CAAC,IAAM;QAClD,MAAMC,eAAQH,2BAAAA,QAASb,GAAG,mBAAiC;QAC3D,IAAIE,SAASgB,EAAE,IAAIF,MAAMpB,aAAaoB;QACtC,OAAO;YACLhB,KAAKE,SAASgB,EAAE,GAAGF,OAAO;YAC1BC,OAAOf,SAASgB,EAAE,IAAIF,OAAO,OAAOG,gBAAON,2BAAAA,QAASI,KAAK,oBAAIK;QAC/D;IACF,EAAE,eAAM;QACN,OAAO;YAAEtB,KAAK;YAAMiB,OAAOK;QAAO;IACpC;AACF;AAEA,wCAAwC,GACxC,OAAO,eAAeC,mBACpBzB,IAAsB,EACtBC,KAAa,EACbyB,KAAa;IAEb,IAAI;;QACF,MAAMtB,WAAW,MAAMR,gBACrBI,MACA,CAAC,aAAa,EAAEK,mBAAmBqB,OAAO,OAAO,CAAC,EAClD;YACEnB,QAAQ;YACRC,SAAS;gBAAE,gBAAgB;YAAmB;YAC9CC,MAAMC,KAAKC,SAAS,CAAC;gBAAEV;YAAM;QAC/B;QAEF,MAAMc,UAAU,MAAMX,SAASY,IAAI,GAAGC,KAAK,CAAC,IAAM;QAClD,MAAMC,eAAQH,2BAAAA,QAASb,GAAG,mBAAiC;QAC3DJ,aAAaoB;QACb,OAAO;YACLhB,KAAKgB;YACLC,OAAOf,SAASgB,EAAE,GACd,OACAC,gBAAON,2BAAAA,QAASI,KAAK,oBAAI;QAC/B;IACF,EAAE,eAAM;QACN,OAAO;YAAEjB,KAAK;YAAMiB,OAAO;QAA6C;IAC1E;AACF"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-requests.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'use client'\n\nimport { authorizedFetch, type MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token'\nimport { AI_CREDITS_CONFIRM_CODE, type AiCreditsPrompt } from '../model/ai-credit-estimate'\nimport type { AiJobSummary } from '../model/ai-jobs.types'\nimport { assistBuildJobRequest, type AssistBuildProposal } from '../model/assist-build'\nimport { publishAiJob } from './ai-jobs-store'\n\n/**\n * The two decisions a person makes about a job — confirm its plan (or try a\n * refused step again), and cancel it — as ONE request each, whichever\n * surface they press it on (AGL-3593). The AI jobs drawer and the dialog that\n * started the job both call these, so confirming in the dialog is the same\n * door, the same body and the same answer as confirming in the drawer.\n *\n * Each hands the job it gets back to every counting surface, so the top-bar\n * indicator stops saying \"plan ready\" the moment the plan is confirmed.\n */\n\n/** A door's answer: the job as it now stands, and the refusal when there is one. */\nexport interface AiJobDecision {\n job: AiJobSummary | null\n error: string | null\n /**\n * A Free job past what is left (AGL-3722): the door's 409 asks for the\n * person's go-ahead with these figures and choices instead of starting it.\n */\n credits?: AiCreditsPrompt | null\n}\n\n/**\n * What a resume asks beyond confirming (AGL-3616): a build's plan confirmed\n * with its publish box ticked, or a finished build's failed items tried again.\n */\nexport interface AiJobResumeOptions {\n publish?: boolean\n retry?: 'failed-items'\n /** \"Build what fits\" (AGL-3722): a Free job past what is left, started on the person's go-ahead. */\n creditsConfirmed?: boolean\n /** \"Build the home page first\" (AGL-3722): the plan narrowed to its home page and what it needs. */\n reduce?: 'first-page'\n}\n\n/**\n * Confirms a plan, or tries again a step whose answer broke a building rule\n * (AGL-2935). The door runs the next step inline and answers with the job.\n */\nexport async function resumeAiJobRequest(\n user: MaybeTokenSource,\n orgId: string,\n job: Pick<AiJobSummary, 'id' | 'hostId'>,\n options: AiJobResumeOptions = {},\n): Promise<AiJobDecision> {\n try {\n const response = await authorizedFetch(\n user,\n `/api/ai/jobs/${encodeURIComponent(job.id)}/resume`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n orgId,\n hostId: job.hostId,\n ...(options.publish ? { publish: true } : {}),\n ...(options.retry ? { retry: options.retry } : {}),\n ...(options.creditsConfirmed ? { creditsConfirmed: true } : {}),\n ...(options.reduce ? { reduce: options.reduce } : {}),\n }),\n },\n )\n const payload = await response.json().catch(() => null)\n const next = (payload?.job as AiJobSummary | undefined) ?? null\n publishAiJob(next)\n const credits =\n response.status === 409 && payload?.code === AI_CREDITS_CONFIRM_CODE\n ? ((payload?.credits as AiCreditsPrompt | undefined) ?? null)\n : null\n return {\n job: next,\n error: response.ok\n ? null\n : String(payload?.error ?? 'The job could not be resumed — try again.'),\n ...(credits ? { credits } : {}),\n }\n } catch {\n return { job: null, error: 'The job could not be resumed — try again.' }\n }\n}\n\n/**\n * Starts the build a chat turn proposed (AGL-3616): one `build` job on the\n * proposal's site, through the same door every job is created by, which\n * climbs its own gates and plans inline. What comes back stops at its plan\n * card; nothing is built until the person confirms it.\n */\nexport async function startAssistBuildRequest(\n user: MaybeTokenSource,\n orgId: string,\n proposal: AssistBuildProposal,\n): Promise<AiJobDecision> {\n const failed = 'The build could not be planned — try asking again.'\n try {\n const response = await authorizedFetch(user, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(assistBuildJobRequest(orgId, proposal)),\n })\n const payload = await response.json().catch(() => null)\n const next = (payload?.job as AiJobSummary | undefined) ?? null\n if (response.ok && next) publishAiJob(next)\n return {\n job: response.ok ? next : null,\n error: response.ok && next ? null : String(payload?.error ?? failed),\n }\n } catch {\n return { job: null, error: failed }\n }\n}\n\n/** Cancels a job that has not settled. */\nexport async function cancelAiJobRequest(\n user: MaybeTokenSource,\n orgId: string,\n jobId: string,\n): Promise<AiJobDecision> {\n try {\n const response = await authorizedFetch(\n user,\n `/api/ai/jobs/${encodeURIComponent(jobId)}/cancel`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ orgId }),\n },\n )\n const payload = await response.json().catch(() => null)\n const next = (payload?.job as AiJobSummary | undefined) ?? null\n publishAiJob(next)\n return {\n job: next,\n error: response.ok\n ? null\n : String(payload?.error ?? 'The job could not be canceled — try again.'),\n }\n } catch {\n return { job: null, error: 'The job could not be canceled — try again.' }\n }\n}\n"],"names":["authorizedFetch","AI_CREDITS_CONFIRM_CODE","assistBuildJobRequest","publishAiJob","resumeAiJobRequest","user","orgId","job","options","response","encodeURIComponent","id","method","headers","body","JSON","stringify","hostId","publish","retry","creditsConfirmed","reduce","payload","json","catch","next","credits","status","code","error","ok","String","startAssistBuildRequest","proposal","failed","cancelAiJobRequest","jobId"],"mappings":"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"}
@@ -17,11 +17,14 @@
17
17
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
18
  import { PLATFORM_BRAND_NAME } from "@aglyn/aglyn";
19
19
  import { aiJobRefundCopy } from "../model/ai-job-failure-copy.js";
20
- import { AppLink } from "@aglyn/shared-ui-jsx";
20
+ import { mdiDotsVertical } from "@aglyn/shared-data-mdi";
21
+ import { AppLink, MdiIcon } from "@aglyn/shared-ui-jsx";
21
22
  import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
22
23
  import { useUser } from "@aglyn/tenant-feature-instance";
23
- import { Alert, Box, Button, Card, Chip, CircularProgress, Container, List, ListItemButton, Stack, Typography } from "@mui/material";
24
+ import { Alert, Box, Button, Card, Chip, CircularProgress, Container, IconButton, List, ListItem, ListItemButton, ListItemText, Menu, MenuItem, Stack, Typography } from "@mui/material";
24
25
  import { useCallback, useEffect, useRef, useState } from "react";
26
+ import { aiJobCancelBlockedReason } from "../model/ai-job-cancel-copy.js";
27
+ import { useAiJobCancel } from "./ai-job-cancel.component.js";
25
28
  import { AI_JOB_PHASE_LABELS, aiJobKindNoun, aiJobPhase } from "../model/ai-job-activity.js";
26
29
  import { AI_JOB_KINDS } from "../model/ai-jobs.types.js";
27
30
  import { publishAiJob } from "./ai-jobs-store.js";
@@ -100,7 +103,7 @@ async function readSiteJobs(user, orgId, hostId, signal) {
100
103
  }
101
104
  }
102
105
  export function AiJobsListPage({ hostId, basePath }) {
103
- var _ref;
106
+ var _ref, _ref1;
104
107
  const { data: user } = useUser();
105
108
  const userRef = useRef(user);
106
109
  userRef.current = user;
@@ -145,6 +148,19 @@ export function AiJobsListPage({ hostId, basePath }) {
145
148
  attempt
146
149
  ]);
147
150
  const retry = useCallback(()=>setAttempt((count)=>count + 1), []);
151
+ // A row's menu (AGL-3616): open the job, or cancel it after the confirm.
152
+ const [menu, setMenu] = useState(null);
153
+ const patchJob = useCallback((next)=>{
154
+ setList((current)=>current.status === 'ready' ? {
155
+ status: 'ready',
156
+ jobs: current.jobs.map((job)=>job.id === next.id ? next : job)
157
+ } : current);
158
+ }, []);
159
+ const canceling = useAiJobCancel({
160
+ user,
161
+ orgId,
162
+ onJob: patchJob
163
+ });
148
164
  const siteName = site.status === 'ready' ? site.name : '';
149
165
  const state = site.status === 'error' ? {
150
166
  status: 'error',
@@ -231,88 +247,141 @@ export function AiJobsListPage({ hostId, basePath }) {
231
247
  })
232
248
  ]
233
249
  })
234
- }) : /*#__PURE__*/ _jsx(Card, {
250
+ }) : /*#__PURE__*/ _jsxs(Card, {
235
251
  variant: "outlined",
236
252
  sx: {
237
253
  borderRadius: 2
238
254
  },
239
- children: /*#__PURE__*/ _jsx(List, {
240
- disablePadding: true,
241
- "aria-label": "AI jobs",
242
- children: state.jobs.map((job, index)=>{
243
- const phase = aiJobPhase(job);
244
- const credits = aiJobNetCredits(job);
245
- const refund = aiJobRefundCopy(job);
246
- const brief = job.brief.length > BRIEF_PREVIEW_CHARS ? `${job.brief.slice(0, BRIEF_PREVIEW_CHARS)}…` : job.brief;
247
- const created = new Date(job.createdAt);
248
- return /*#__PURE__*/ _jsxs(ListItemButton, {
249
- component: AppLink,
250
- href: `${basePath != null ? basePath : ''}/${encodeURIComponent(job.id)}`,
251
- divider: index < state.jobs.length - 1,
252
- sx: {
253
- display: 'block',
254
- py: 1.5
255
- },
256
- children: [
257
- /*#__PURE__*/ _jsxs(Stack, {
258
- direction: "row",
259
- spacing: 1,
260
- useFlexGap: true,
255
+ children: [
256
+ /*#__PURE__*/ _jsx(List, {
257
+ disablePadding: true,
258
+ "aria-label": "AI jobs",
259
+ children: state.jobs.map((job, index)=>{
260
+ const phase = aiJobPhase(job);
261
+ const credits = aiJobNetCredits(job);
262
+ const refund = aiJobRefundCopy(job);
263
+ const brief = job.brief.length > BRIEF_PREVIEW_CHARS ? `${job.brief.slice(0, BRIEF_PREVIEW_CHARS)}…` : job.brief;
264
+ const created = new Date(job.createdAt);
265
+ return /*#__PURE__*/ _jsx(ListItem, {
266
+ disablePadding: true,
267
+ divider: index < state.jobs.length - 1,
268
+ secondaryAction: /*#__PURE__*/ _jsx(IconButton, {
269
+ edge: "end",
270
+ "aria-label": `Actions for this ${aiJobKindLabel(job.kind).toLowerCase()} job`,
271
+ "aria-haspopup": "menu",
272
+ onClick: (event)=>setMenu({
273
+ anchor: event.currentTarget,
274
+ job
275
+ }),
276
+ children: /*#__PURE__*/ _jsx(MdiIcon, {
277
+ path: mdiDotsVertical.path,
278
+ fontSize: "small"
279
+ })
280
+ }),
281
+ children: /*#__PURE__*/ _jsxs(ListItemButton, {
282
+ component: AppLink,
283
+ href: `${basePath != null ? basePath : ''}/${encodeURIComponent(job.id)}`,
261
284
  sx: {
262
- alignItems: 'center',
263
- flexWrap: 'wrap',
264
- rowGap: 0.5
285
+ display: 'block',
286
+ py: 1.5,
287
+ pr: 7
265
288
  },
266
289
  children: [
267
- /*#__PURE__*/ _jsx(Chip, {
268
- size: "small",
269
- variant: "outlined",
270
- label: aiJobKindLabel(job.kind)
271
- }),
272
- /*#__PURE__*/ _jsx(Chip, {
273
- size: "small",
274
- color: PHASE_COLOR[phase],
275
- label: AI_JOB_PHASE_LABELS[phase]
290
+ /*#__PURE__*/ _jsxs(Stack, {
291
+ direction: "row",
292
+ spacing: 1,
293
+ useFlexGap: true,
294
+ sx: {
295
+ alignItems: 'center',
296
+ flexWrap: 'wrap',
297
+ rowGap: 0.5
298
+ },
299
+ children: [
300
+ /*#__PURE__*/ _jsx(Chip, {
301
+ size: "small",
302
+ variant: "outlined",
303
+ label: aiJobKindLabel(job.kind)
304
+ }),
305
+ /*#__PURE__*/ _jsx(Chip, {
306
+ size: "small",
307
+ color: PHASE_COLOR[phase],
308
+ label: AI_JOB_PHASE_LABELS[phase]
309
+ }),
310
+ /*#__PURE__*/ _jsx(Box, {
311
+ sx: {
312
+ flexGrow: 1
313
+ }
314
+ }),
315
+ /*#__PURE__*/ _jsxs(Typography, {
316
+ variant: "caption",
317
+ color: "text.secondary",
318
+ children: [
319
+ credits > 0 ? `${credits.toLocaleString('en-US')} credits · ` : '',
320
+ Number.isNaN(created.getTime()) ? null : /*#__PURE__*/ _jsx("time", {
321
+ dateTime: job.createdAt,
322
+ children: WHEN.format(created)
323
+ })
324
+ ]
325
+ })
326
+ ]
276
327
  }),
277
- /*#__PURE__*/ _jsx(Box, {
328
+ /*#__PURE__*/ _jsx(Typography, {
329
+ variant: "body2",
278
330
  sx: {
279
- flexGrow: 1
280
- }
331
+ mt: 0.75,
332
+ wordBreak: 'break-word'
333
+ },
334
+ children: brief
281
335
  }),
282
- /*#__PURE__*/ _jsxs(Typography, {
336
+ refund ? /*#__PURE__*/ _jsx(Typography, {
283
337
  variant: "caption",
284
338
  color: "text.secondary",
285
- children: [
286
- credits > 0 ? `${credits.toLocaleString('en-US')} credits · ` : '',
287
- Number.isNaN(created.getTime()) ? null : /*#__PURE__*/ _jsx("time", {
288
- dateTime: job.createdAt,
289
- children: WHEN.format(created)
290
- })
291
- ]
292
- })
339
+ component: "div",
340
+ sx: {
341
+ mt: 0.5
342
+ },
343
+ children: refund
344
+ }) : null
293
345
  ]
294
- }),
295
- /*#__PURE__*/ _jsx(Typography, {
296
- variant: "body2",
297
- sx: {
298
- mt: 0.75,
299
- wordBreak: 'break-word'
300
- },
301
- children: brief
302
- }),
303
- refund ? /*#__PURE__*/ _jsx(Typography, {
304
- variant: "caption",
305
- color: "text.secondary",
306
- component: "div",
307
- sx: {
308
- mt: 0.5
309
- },
310
- children: refund
311
- }) : null
312
- ]
313
- }, job.id);
314
- })
315
- })
346
+ })
347
+ }, job.id);
348
+ })
349
+ }),
350
+ /*#__PURE__*/ _jsxs(Menu, {
351
+ anchorEl: (_ref1 = menu == null ? void 0 : menu.anchor) != null ? _ref1 : null,
352
+ open: menu !== null,
353
+ onClose: ()=>setMenu(null),
354
+ children: [
355
+ menu ? /*#__PURE__*/ _jsx(MenuItem, {
356
+ component: AppLink,
357
+ href: `${basePath != null ? basePath : ''}/${encodeURIComponent(menu.job.id)}`,
358
+ onClick: ()=>setMenu(null),
359
+ children: 'Open job'
360
+ }) : null,
361
+ menu ? /*#__PURE__*/ _jsx(MenuItem, {
362
+ disabled: Boolean(aiJobCancelBlockedReason(menu.job)),
363
+ onClick: ()=>{
364
+ const job = menu.job;
365
+ setMenu(null);
366
+ canceling.ask(job);
367
+ },
368
+ children: /*#__PURE__*/ _jsx(ListItemText, {
369
+ primary: "Cancel job",
370
+ secondary: aiJobCancelBlockedReason(menu.job),
371
+ slotProps: {
372
+ secondary: {
373
+ sx: {
374
+ whiteSpace: 'normal',
375
+ maxWidth: 260
376
+ }
377
+ }
378
+ }
379
+ })
380
+ }) : null
381
+ ]
382
+ }),
383
+ canceling.dialog
384
+ ]
316
385
  }),
317
386
  state.status === 'ready' && state.jobs.length >= AI_JOBS_PAGE_LIMIT ? /*#__PURE__*/ _jsx(Typography, {
318
387
  variant: "body2",