@aglyn/plugins-ai 1.0.0-beta.236 → 1.0.0-beta.237
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.
- package/package.json +17 -17
- package/src/lib/components/ai-credits-prompt.component.d.ts +37 -0
- package/src/lib/components/ai-credits-prompt.component.js +67 -0
- package/src/lib/components/ai-credits-prompt.component.js.map +1 -0
- package/src/lib/components/ai-job-plan.component.d.ts +5 -4
- package/src/lib/components/ai-job-plan.component.js +37 -12
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-job-requests.d.ts +10 -0
- package/src/lib/components/ai-job-requests.js +12 -4
- package/src/lib/components/ai-job-requests.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +26 -7
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.js +68 -22
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/assist-build-card.component.d.ts +3 -1
- package/src/lib/components/assist-build-card.component.js +58 -7
- package/src/lib/components/assist-build-card.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +54 -6
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +2 -1
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/jobs/ai-job-admission.d.ts +22 -2
- package/src/lib/jobs/ai-job-admission.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.d.ts +24 -11
- package/src/lib/jobs/ai-job-build-step.js +50 -34
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.js +1 -1
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.js +4 -0
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +13 -0
- package/src/lib/jobs/ai-job-page-step.js +34 -3
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +23 -0
- package/src/lib/jobs/ai-job-plan-step.js +75 -15
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +34 -3
- package/src/lib/jobs/ai-job-site-content.js +132 -32
- package/src/lib/jobs/ai-job-site-content.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.js +45 -15
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +8 -0
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +18 -1
- package/src/lib/jobs/ai-jobs.js +29 -13
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +105 -18
- package/src/lib/jobs/ai-layout-stock-photos.js +356 -114
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/layout-language/ai-layout-compiler.d.ts +27 -0
- package/src/lib/layout-language/ai-layout-compiler.js +78 -11
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-gaps.js +3 -1
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
- package/src/lib/layout-language/ai-layout-language.js +1 -1
- package/src/lib/layout-language/ai-layout-language.js.map +1 -1
- package/src/lib/layout-language/ai-layout-listings.d.ts +37 -1
- package/src/lib/layout-language/ai-layout-listings.js +66 -11
- package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +2 -1
- package/src/lib/layout-language/ai-layout-pictures.js +5 -2
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/model/ai-build-job.d.ts +31 -0
- package/src/lib/model/ai-build-job.js +86 -1
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +8 -0
- package/src/lib/model/ai-build-progress.js +33 -0
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +182 -0
- package/src/lib/model/ai-credit-estimate.js +190 -0
- package/src/lib/model/ai-credit-estimate.js.map +1 -0
- package/src/lib/model/ai-jobs.types.d.ts +15 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +59 -22
- package/src/lib/model/ai-site-job.js +104 -23
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.js +3 -3
- package/src/lib/model/ai-site-kinds.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.js +10 -1
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.d.ts +11 -0
- package/src/lib/runtime/ai-node-tree.js +26 -0
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +539 -7
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/server/ai-jobs-resume.js +93 -35
- package/src/lib/server/ai-jobs-resume.js.map +1 -1
- package/src/lib/server/ai-jobs-route.js +45 -4
- package/src/lib/server/ai-jobs-route.js.map +1 -1
- package/src/lib/server/assist-chat.js +4 -1
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit.js +3 -1
- package/src/lib/server/assist-edit.js.map +1 -1
- package/src/lib/server/assist-music.d.ts +36 -0
- package/src/lib/server/assist-music.js +36 -0
- package/src/lib/server/assist-music.js.map +1 -0
- package/src/lib/server.js +6 -0
- package/src/lib/server.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-ai",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.237",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,22 +25,22 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-data-types": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
35
|
-
"@aglyn/shared-ui-theme": "1.0.0-beta.
|
|
36
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
37
|
-
"@aglyn/shared-util-errors": "1.0.0-beta.
|
|
38
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
39
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
40
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
41
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
42
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
43
|
-
"@aglyn/tenant-runtime": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.237",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.237",
|
|
30
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.237",
|
|
31
|
+
"@aglyn/shared-data-types": "1.0.0-beta.237",
|
|
32
|
+
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.237",
|
|
33
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.237",
|
|
34
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.237",
|
|
35
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.237",
|
|
36
|
+
"@aglyn/shared-util-email": "1.0.0-beta.237",
|
|
37
|
+
"@aglyn/shared-util-errors": "1.0.0-beta.237",
|
|
38
|
+
"@aglyn/shared-util-http": "1.0.0-beta.237",
|
|
39
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.237",
|
|
40
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.237",
|
|
41
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.237",
|
|
42
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.237",
|
|
43
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.237",
|
|
44
44
|
"@swc/helpers": "0.5.23"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type AiCreditsPrompt } from '../model/ai-credit-estimate';
|
|
18
|
+
/**
|
|
19
|
+
* What a Free job past what is left asks before it starts (AGL-3722), on the
|
|
20
|
+
* Assist build plan card and in the guided start alike: the plain sentence —
|
|
21
|
+
* about what it costs, what is left, that it builds what it can and pauses —
|
|
22
|
+
* and its three ways on. Only "Build what fits" (or the smaller first build)
|
|
23
|
+
* starts anything; nothing is started by showing this.
|
|
24
|
+
*/
|
|
25
|
+
export interface AiCreditsPromptProps {
|
|
26
|
+
prompt: AiCreditsPrompt;
|
|
27
|
+
noun: 'build' | 'site';
|
|
28
|
+
/** The workspace's path slug, for Upgrade; Upgrade is left out without it. */
|
|
29
|
+
orgSlug?: string | null;
|
|
30
|
+
busy?: boolean;
|
|
31
|
+
/** "Build what fits": start it, to pause where the credits run out. */
|
|
32
|
+
onBuildWhatFits: () => void;
|
|
33
|
+
/** The smaller first build; offered only when the prompt has one and this is given. */
|
|
34
|
+
onSmaller?: () => void;
|
|
35
|
+
}
|
|
36
|
+
export declare function AiCreditsPromptNotice({ prompt, noun, orgSlug, busy, onBuildWhatFits, onSmaller, }: AiCreditsPromptProps): JSX.Element;
|
|
37
|
+
export default AiCreditsPromptNotice;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
|
+
import { Alert, Button, Stack } from "@mui/material";
|
|
19
|
+
import { AI_CREDITS_BUILD_WHAT_FITS, aiCreditsPromptText, aiCreditsSmallerText } from "../model/ai-credit-estimate.js";
|
|
20
|
+
import { aiCreditsBillingHref } from "./ai-job-links.js";
|
|
21
|
+
export function AiCreditsPromptNotice({ prompt, noun, orgSlug, busy = false, onBuildWhatFits, onSmaller }) {
|
|
22
|
+
return /*#__PURE__*/ _jsxs(Alert, {
|
|
23
|
+
severity: "warning",
|
|
24
|
+
sx: {
|
|
25
|
+
mt: 1
|
|
26
|
+
},
|
|
27
|
+
role: "alert",
|
|
28
|
+
"aria-label": "More than your credits left",
|
|
29
|
+
children: [
|
|
30
|
+
aiCreditsPromptText(prompt, noun),
|
|
31
|
+
/*#__PURE__*/ _jsxs(Stack, {
|
|
32
|
+
direction: "row",
|
|
33
|
+
spacing: 1,
|
|
34
|
+
useFlexGap: true,
|
|
35
|
+
sx: {
|
|
36
|
+
mt: 1,
|
|
37
|
+
flexWrap: 'wrap'
|
|
38
|
+
},
|
|
39
|
+
children: [
|
|
40
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
41
|
+
size: "small",
|
|
42
|
+
variant: "contained",
|
|
43
|
+
disabled: busy,
|
|
44
|
+
onClick: onBuildWhatFits,
|
|
45
|
+
children: AI_CREDITS_BUILD_WHAT_FITS
|
|
46
|
+
}),
|
|
47
|
+
prompt.smaller && onSmaller ? /*#__PURE__*/ _jsx(Button, {
|
|
48
|
+
size: "small",
|
|
49
|
+
variant: "outlined",
|
|
50
|
+
disabled: busy,
|
|
51
|
+
onClick: onSmaller,
|
|
52
|
+
children: aiCreditsSmallerText(prompt.smaller)
|
|
53
|
+
}) : null,
|
|
54
|
+
orgSlug ? /*#__PURE__*/ _jsx(Button, {
|
|
55
|
+
size: "small",
|
|
56
|
+
color: "inherit",
|
|
57
|
+
href: aiCreditsBillingHref(orgSlug),
|
|
58
|
+
children: 'Upgrade'
|
|
59
|
+
}) : null
|
|
60
|
+
]
|
|
61
|
+
})
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
export default AiCreditsPromptNotice;
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=ai-credits-prompt.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-credits-prompt.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { Alert, Button, Stack } from '@mui/material'\nimport {\n AI_CREDITS_BUILD_WHAT_FITS,\n aiCreditsPromptText,\n aiCreditsSmallerText,\n type AiCreditsPrompt,\n} from '../model/ai-credit-estimate'\nimport { aiCreditsBillingHref } from './ai-job-links'\n\n/**\n * What a Free job past what is left asks before it starts (AGL-3722), on the\n * Assist build plan card and in the guided start alike: the plain sentence —\n * about what it costs, what is left, that it builds what it can and pauses —\n * and its three ways on. Only \"Build what fits\" (or the smaller first build)\n * starts anything; nothing is started by showing this.\n */\nexport interface AiCreditsPromptProps {\n prompt: AiCreditsPrompt\n noun: 'build' | 'site'\n /** The workspace's path slug, for Upgrade; Upgrade is left out without it. */\n orgSlug?: string | null\n busy?: boolean\n /** \"Build what fits\": start it, to pause where the credits run out. */\n onBuildWhatFits: () => void\n /** The smaller first build; offered only when the prompt has one and this is given. */\n onSmaller?: () => void\n}\n\nexport function AiCreditsPromptNotice({\n prompt,\n noun,\n orgSlug,\n busy = false,\n onBuildWhatFits,\n onSmaller,\n}: AiCreditsPromptProps): JSX.Element {\n return (\n <Alert severity=\"warning\" sx={{ mt: 1 }} role=\"alert\" aria-label=\"More than your credits left\">\n {aiCreditsPromptText(prompt, noun)}\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ mt: 1, flexWrap: 'wrap' }}>\n <Button size=\"small\" variant=\"contained\" disabled={busy} onClick={onBuildWhatFits}>\n {AI_CREDITS_BUILD_WHAT_FITS}\n </Button>\n {prompt.smaller && onSmaller ? (\n <Button size=\"small\" variant=\"outlined\" disabled={busy} onClick={onSmaller}>\n {aiCreditsSmallerText(prompt.smaller)}\n </Button>\n ) : null}\n {orgSlug ? (\n <Button size=\"small\" color=\"inherit\" href={aiCreditsBillingHref(orgSlug)}>\n {'Upgrade'}\n </Button>\n ) : null}\n </Stack>\n </Alert>\n )\n}\n\nexport default AiCreditsPromptNotice\n"],"names":["Alert","Button","Stack","AI_CREDITS_BUILD_WHAT_FITS","aiCreditsPromptText","aiCreditsSmallerText","aiCreditsBillingHref","AiCreditsPromptNotice","prompt","noun","orgSlug","busy","onBuildWhatFits","onSmaller","severity","sx","mt","role","aria-label","direction","spacing","useFlexGap","flexWrap","size","variant","disabled","onClick","smaller","color","href"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;AAEA,SAASA,KAAK,EAAEC,MAAM,EAAEC,KAAK,QAAQ,gBAAe;AACpD,SACEC,0BAA0B,EAC1BC,mBAAmB,EACnBC,oBAAoB,QAEf,iCAA6B;AACpC,SAASC,oBAAoB,QAAQ,oBAAgB;AAqBrD,OAAO,SAASC,sBAAsB,EACpCC,MAAM,EACNC,IAAI,EACJC,OAAO,EACPC,OAAO,KAAK,EACZC,eAAe,EACfC,SAAS,EACY;IACrB,qBACE,MAACb;QAAMc,UAAS;QAAUC,IAAI;YAAEC,IAAI;QAAE;QAAGC,MAAK;QAAQC,cAAW;;YAC9Dd,oBAAoBI,QAAQC;0BAC7B,MAACP;gBAAMiB,WAAU;gBAAMC,SAAS;gBAAGC,UAAU;gBAACN,IAAI;oBAAEC,IAAI;oBAAGM,UAAU;gBAAO;;kCAC1E,KAACrB;wBAAOsB,MAAK;wBAAQC,SAAQ;wBAAYC,UAAUd;wBAAMe,SAASd;kCAC/DT;;oBAEFK,OAAOmB,OAAO,IAAId,0BACjB,KAACZ;wBAAOsB,MAAK;wBAAQC,SAAQ;wBAAWC,UAAUd;wBAAMe,SAASb;kCAC9DR,qBAAqBG,OAAOmB,OAAO;yBAEpC;oBACHjB,wBACC,KAACT;wBAAOsB,MAAK;wBAAQK,OAAM;wBAAUC,MAAMvB,qBAAqBI;kCAC7D;yBAED;;;;;AAIZ;AAEA,eAAeH,sBAAqB"}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { type AiBuildPlanEmbed } from '../model/ai-build-plan';
|
|
18
18
|
import type { AiJobReview, AiJobSummary } from '../model/ai-jobs.types';
|
|
19
|
+
import type { AiJobResumeOptions } from './ai-job-requests';
|
|
19
20
|
import { AiSiteStarterFallback } from './ai-site-starter-fallback.component';
|
|
20
21
|
/**
|
|
21
22
|
* A job's plan and what it waits for (AGL-2935), inside the AI jobs drawer
|
|
@@ -31,9 +32,7 @@ export interface AiJobPlanProps {
|
|
|
31
32
|
* Confirms the plan, or tries the refused step again. A build whose request
|
|
32
33
|
* asked to publish confirms with `publish` when its box is ticked (AGL-3616).
|
|
33
34
|
*/
|
|
34
|
-
onResume: (job: AiJobSummary, options?:
|
|
35
|
-
publish?: boolean;
|
|
36
|
-
}) => void;
|
|
35
|
+
onResume: (job: AiJobSummary, options?: AiJobResumeOptions) => void;
|
|
37
36
|
/** A resume for this job is in flight. */
|
|
38
37
|
busy?: boolean;
|
|
39
38
|
/**
|
|
@@ -47,6 +46,8 @@ export interface AiJobPlanProps {
|
|
|
47
46
|
* that did not work out (AGL-3594); the action is drawn only when given.
|
|
48
47
|
*/
|
|
49
48
|
user?: Parameters<typeof AiSiteStarterFallback>[0]['user'];
|
|
49
|
+
/** The workspace's path slug, for the Upgrade a Free build past what is left offers (AGL-3722). */
|
|
50
|
+
orgSlug?: string | null;
|
|
50
51
|
}
|
|
51
52
|
/**
|
|
52
53
|
* Where a refused answer broke its rules, and the outline of those parts, as
|
|
@@ -62,5 +63,5 @@ export declare function aiJobReviewDetails(review: AiJobReview | null): string[]
|
|
|
62
63
|
* host's code loads when a visitor reaches it, whether or not they press play.
|
|
63
64
|
*/
|
|
64
65
|
export declare function aiPlanEmbedLine(embed: AiBuildPlanEmbed, named: (ref: string) => string): string;
|
|
65
|
-
export declare function AiJobPlan({ job, onResume, busy, staff, user, }: AiJobPlanProps): JSX.Element | null;
|
|
66
|
+
export declare function AiJobPlan({ job, onResume, busy, staff, user, orgSlug, }: AiJobPlanProps): JSX.Element | null;
|
|
66
67
|
export default AiJobPlan;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
/**
|
|
3
4
|
* @license
|
|
@@ -15,7 +16,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
15
16
|
* See the License for the specific language governing permissions and
|
|
16
17
|
* limitations under the License.
|
|
17
18
|
*/ import { AI_BUILD_PLAN_CREATION_NOUNS, AI_BUILD_PLAN_EMBED_HOST_NAMES, isAiPlanNewRef } from "../model/ai-build-plan.js";
|
|
18
|
-
import {
|
|
19
|
+
import { aiBuildSmaller, aiJobCreditRange } from "../model/ai-build-job.js";
|
|
20
|
+
import { aiCreditRangeText, aiCreditsPromptFor } from "../model/ai-credit-estimate.js";
|
|
21
|
+
import { aiFreeCreditsResetLabel } from "../model/ai-site-job.js";
|
|
22
|
+
import { AiCreditsPromptNotice } from "./ai-credits-prompt.component.js";
|
|
19
23
|
import { aiBuildOpNoun } from "../model/ai-build-progress.js";
|
|
20
24
|
import { aiJobConfirmingOwnPlan } from "../model/ai-job-activity.js";
|
|
21
25
|
import { aiSiteStarterFallbackOffered } from "../model/ai-job-failure-copy.js";
|
|
@@ -67,8 +71,8 @@ import { useState } from "react";
|
|
|
67
71
|
const link = embed.url ? ` playing ${embed.url}` : ', its link left for you to paste';
|
|
68
72
|
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.`;
|
|
69
73
|
}
|
|
70
|
-
export function AiJobPlan({ job, onResume, busy = false, staff = false, user }) {
|
|
71
|
-
var _plan_items, _plan_embeds;
|
|
74
|
+
export function AiJobPlan({ job, onResume, busy = false, staff = false, user, orgSlug }) {
|
|
75
|
+
var _ref, _plan_items, _plan_embeds;
|
|
72
76
|
const [detailsOpen, setDetailsOpen] = useState(false);
|
|
73
77
|
// Unticked until the person ticks it: a build publishes only when its
|
|
74
78
|
// request asked AND they confirm it here (AGL-3616).
|
|
@@ -84,11 +88,18 @@ export function AiJobPlan({ job, onResume, busy = false, staff = false, user })
|
|
|
84
88
|
const waiting = job.status === 'needs_review' && review !== null && !aiJobConfirmingOwnPlan(job);
|
|
85
89
|
// The guard rail (AGL-2911): what the plan is estimated to cost is read
|
|
86
90
|
// before it is confirmed, not after it has been spent. An estimate, and
|
|
87
|
-
// said to be one
|
|
88
|
-
//
|
|
91
|
+
// said to be one: what it is likely to cost — what builds like it measured
|
|
92
|
+
// — and its ceiling, every pass at its reserve (AGL-3722). A page job
|
|
89
93
|
// counts the creations it builds before its page (AGL-3031).
|
|
90
|
-
const
|
|
94
|
+
const range = plan && waiting && review.reason === 'plan' ? aiJobCreditRange(job.kind, plan) : null;
|
|
95
|
+
// A Free build past what is left asks first (AGL-3722): build what fits,
|
|
96
|
+
// the home page first, or upgrade — never a refusal with no way on.
|
|
97
|
+
const freeCredits = (_ref = review == null ? void 0 : review.freeCredits) != null ? _ref : null;
|
|
98
|
+
const prompt = plan && range && job.kind === 'build' ? aiCreditsPromptFor(range, freeCredits, aiBuildSmaller(plan)) : null;
|
|
91
99
|
const offersPublish = job.kind === 'build' && job.publishAsked === true && waiting && review.reason === 'plan';
|
|
100
|
+
const publishing = offersPublish && publish ? {
|
|
101
|
+
publish: true
|
|
102
|
+
} : {};
|
|
92
103
|
return /*#__PURE__*/ _jsxs(Box, {
|
|
93
104
|
sx: {
|
|
94
105
|
mt: 1
|
|
@@ -206,14 +217,17 @@ export function AiJobPlan({ job, onResume, busy = false, staff = false, user })
|
|
|
206
217
|
})
|
|
207
218
|
]
|
|
208
219
|
}),
|
|
209
|
-
|
|
220
|
+
range && range.ceiling > 0 && /*#__PURE__*/ _jsxs(Typography, {
|
|
210
221
|
variant: "caption",
|
|
211
222
|
color: "text.secondary",
|
|
212
223
|
sx: {
|
|
213
224
|
display: 'block',
|
|
214
225
|
mt: 1
|
|
215
226
|
},
|
|
216
|
-
children:
|
|
227
|
+
children: [
|
|
228
|
+
`Estimated cost: ${aiCreditRangeText(range)}. What it costs is what its steps spend.`,
|
|
229
|
+
freeCredits && !prompt ? ` You have ${freeCredits.left.toLocaleString('en-US')} of your free AI credits left this month, until ${aiFreeCreditsResetLabel(freeCredits.resetsOn)}.` : ''
|
|
230
|
+
]
|
|
217
231
|
}),
|
|
218
232
|
waiting && review.retryRefusal && review.reason !== 'plan' && /*#__PURE__*/ _jsx(Typography, {
|
|
219
233
|
variant: "caption",
|
|
@@ -239,15 +253,26 @@ export function AiJobPlan({ job, onResume, busy = false, staff = false, user })
|
|
|
239
253
|
children: 'Publish the new pages when they are built'
|
|
240
254
|
})
|
|
241
255
|
}),
|
|
242
|
-
waiting && /*#__PURE__*/ _jsx(
|
|
256
|
+
waiting && prompt && /*#__PURE__*/ _jsx(AiCreditsPromptNotice, {
|
|
257
|
+
prompt: prompt,
|
|
258
|
+
noun: "build",
|
|
259
|
+
orgSlug: orgSlug,
|
|
260
|
+
busy: busy,
|
|
261
|
+
onBuildWhatFits: ()=>onResume(job, _extends({}, publishing, {
|
|
262
|
+
creditsConfirmed: true
|
|
263
|
+
})),
|
|
264
|
+
onSmaller: ()=>onResume(job, _extends({}, publishing, {
|
|
265
|
+
creditsConfirmed: true,
|
|
266
|
+
reduce: 'first-page'
|
|
267
|
+
}))
|
|
268
|
+
}),
|
|
269
|
+
waiting && !prompt && /*#__PURE__*/ _jsx(Button, {
|
|
243
270
|
size: "small",
|
|
244
271
|
variant: "contained",
|
|
245
272
|
// Disabled with its reason above when the Free allowance left
|
|
246
273
|
// cannot pay for another try (AGL-3594).
|
|
247
274
|
disabled: busy || review.reason !== 'plan' && Boolean(review.retryRefusal),
|
|
248
|
-
onClick: ()=>onResume(job, offersPublish && publish ?
|
|
249
|
-
publish: true
|
|
250
|
-
} : undefined),
|
|
275
|
+
onClick: ()=>onResume(job, offersPublish && publish ? publishing : undefined),
|
|
251
276
|
sx: {
|
|
252
277
|
mt: 1
|
|
253
278
|
},
|
|
@@ -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":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,4BAA4B,EAC5BC,8BAA8B,EAC9BC,cAAc,QAET,4BAAwB;AAE/B,SAASC,mBAAmB,QAAQ,2BAAuB;AAC3D,SAASC,aAAa,QAAQ,gCAA4B;AAC1D,SAASC,sBAAsB,QAAQ,8BAA0B;AACjE,SAASC,4BAA4B,QAAQ,kCAA8B;AAC3E,SAASC,qBAAqB,QAAQ,0CAAsC;AAC5E,SAASC,GAAG,EAAEC,MAAM,EAAEC,QAAQ,EAAEC,QAAQ,EAAEC,gBAAgB,EAAEC,KAAK,EAAEC,UAAU,QAAQ,gBAAe;AACpG,SAASC,QAAQ,QAAQ,QAAO;AAgChC;;;;;CAKC,GACD,OAAO,SAASC,mBAAmBC,MAA0B;QAUxCA;IATnB,IAAIA,CAAAA,0BAAAA,OAAQC,MAAM,MAAK,YAAY,OAAO,EAAE;IAC5C,MAAMC,QAAQF,OAAOG,QAAQ,CAACC,OAAO,CAAC,CAACC;YACvBA,kBAEVA;QAFJ,MAAMC,QAAQD,EAAAA,mBAAAA,QAAQE,OAAO,qBAAfF,iBAAiBG,MAAM,IACjC,CAAC,MAAM,EAAEH,QAAQE,OAAO,CAACE,IAAI,CAAC,OAAO,GACrCJ,EAAAA,iBAAAA,QAAQK,KAAK,qBAAbL,eAAeG,MAAM,IACnB,CAAC,GAAG,EAAEH,QAAQK,KAAK,CAACD,IAAI,CAAC,OAAO,GAChC;QACN,OAAOH,QAAQ;YAAC,GAAGD,QAAQM,IAAI,KAAK,OAAO,KAAK,CAAC,KAAK,EAAEN,QAAQM,IAAI,CAAC,CAAC,CAAC,GAAGN,QAAQO,IAAI,CAAC,EAAE,EAAEN,OAAO;SAAC,GAAG,EAAE;IAC1G;IACA,KAAK,MAAMO,SAAQb,kBAAAA,OAAOc,OAAO,YAAdd,kBAAkB,EAAE,CAAE;YAIjBa;YAEdA;QALRX,MAAMa,IAAI,CACR;YACE,GAAG,KAAKC,MAAM,CAACH,KAAKI,KAAK,IAAIJ,KAAKK,EAAE,CAAC,CAAC,EAAEL,KAAKM,WAAW,EAAE;eACvDC,OAAOC,OAAO,EAACR,aAAAA,KAAKS,IAAI,YAATT,aAAa,CAAC,GAAGU,GAAG,CAAC,CAAC,CAACC,MAAMC,MAAM,GAAK,GAAGD,KAAK,CAAC,EAAEE,KAAKC,SAAS,CAACF,QAAQ;eACxFZ,KAAKe,KAAK,CAACpB,MAAM,GAAG;gBAAC,CAAC,MAAM,EAAEK,KAAKe,KAAK,CAACnB,IAAI,CAAC,OAAO;aAAC,GAAG,EAAE;eAC3DI,EAAAA,WAAAA,KAAKgB,EAAE,qBAAPhB,SAASL,MAAM,IAAG;gBAAC,CAAC,GAAG,EAAEK,KAAKgB,EAAE,CAACpB,IAAI,CAAC,OAAO;aAAC,GAAG,EAAE;eACnDI,KAAKiB,QAAQ,CAACtB,MAAM,GAAG;gBAAC,CAAC,MAAM,EAAEK,KAAKiB,QAAQ,CAACrB,IAAI,CAAC,OAAO;aAAC,GAAG,EAAE;SACtE,CAACA,IAAI,CAAC;IAEX;IACA,OAAOP;AACT;AAEA;;;;;CAKC,GACD,OAAO,SAAS6B,gBACdC,KAAuB,EACvBC,KAA8B;IAE9B,MAAMC,OAAOlD,8BAA8B,CAACgD,MAAME,IAAI,CAAC;IACvD,MAAM5B,QAAQrB,eAAe+C,MAAM1B,KAAK,IAAI,CAAC,cAAc,EAAE2B,MAAMD,MAAM1B,KAAK,GAAG,GAAG0B,MAAM1B,KAAK;IAC/F,MAAM6B,OAAOH,MAAMI,GAAG,GAAG,CAAC,SAAS,EAAEJ,MAAMI,GAAG,EAAE,GAAG;IACnD,OAAO,CAAC,SAAS,EAAEF,KAAK,WAAW,EAAE5B,QAAQ6B,KAAK,iBAAiB,EAAEH,MAAMK,KAAK,CAAC,aAAa,EAAEH,KAAK,sEAAsE,CAAC;AAC9K;AAEA,OAAO,SAASI,UAAU,EACxBC,GAAG,EACHC,QAAQ,EACRC,OAAO,KAAK,EACZC,QAAQ,KAAK,EACbC,IAAI,EACW;QA4DLC,aAMAA;IAjEV,MAAM,CAACC,aAAaC,eAAe,GAAGhD,SAAS;IAC/C,sEAAsE;IACtE,qDAAqD;IACrD,MAAM,CAACiD,SAASC,WAAW,GAAGlD,SAAS;IACvC,MAAM,EAAE8C,IAAI,EAAE5C,MAAM,EAAE,GAAGuC;IACzB,IAAI,CAACK,QAAQ,CAAC5C,QAAQ,OAAO;IAC7B,MAAMiD,UAAUP,QAAQ3C,mBAAmBC,UAAU,EAAE;IACvD,mFAAmF,GACnF,MAAMiC,QAAQ,CAACiB;;eACb,CAACA,MACG,KACAjE,eAAeiE,OACbA,IAAIC,KAAK,CAAC,OAAO3C,MAAM,YACtBoC,wBAAAA,KAAMQ,MAAM,CAACF,IAAI,mBAAIA;;IAC9B,4EAA4E;IAC5E,MAAMG,UAAUd,IAAIe,MAAM,KAAK,kBAAkBtD,WAAW,QAAQ,CAACZ,uBAAuBmD;IAC5F,wEAAwE;IACxE,wEAAwE;IACxE,uEAAuE;IACvE,0EAA0E;IAC1E,6DAA6D;IAC7D,MAAMgB,WACJX,QAAQS,WAAWrD,OAAOC,MAAM,KAAK,SAASf,oBAAoBqD,IAAIiB,IAAI,EAAEZ,QAAQ;IACtF,MAAMa,gBAAgBlB,IAAIiB,IAAI,KAAK,WAAWjB,IAAImB,YAAY,KAAK,QAAQL,WAAWrD,OAAOC,MAAM,KAAK;IACxG,qBACE,MAACV;QAAIsC,IAAI;YAAE8B,IAAI;QAAE;;YACdf,sBACC,MAAChD;gBAAMgE,SAAS;gBAAKC,MAAK;gBAAOC,cAAW;;kCAC1C,KAACjE;wBAAWkE,SAAQ;wBAAUC,OAAM;kCACjCpB,KAAKU,MAAM,KAAK,cAAc,mBAAmB;;oBAEnDV,KAAKqB,KAAK,CAAC1C,GAAG,CAAC,CAAC2C,OAAOC,sBACtB,MAACtE;4BAAkCkE,SAAQ;4BAAQF,MAAK;;gCAAW;gCACrDK,MAAMV,IAAI,KAAK,WAAW,SAASU,MAAMV,IAAI;gCAAC;gCAAEvB,MAAMiC,MAAMhD,EAAE;gCAAE;gCAAIgD,MAAME,OAAO;;2BAD9E,CAAC,MAAM,EAAED,OAAO;oBAIlCvB,KAAKyB,MAAM,CAAC9C,GAAG,CAAC,CAAC2C,OAAOC,sBACvB,MAACtE;4BAAmCkE,SAAQ;4BAAQF,MAAK;;gCAAW;gCACrD9E,4BAA4B,CAACmF,MAAMV,IAAI,CAAC,CAACc,IAAI;gCAAC;gCAAEJ,MAAM1C,IAAI;gCACtE0C,MAAMK,WAAW,GACd,CAAC,iBAAiB,EAAEtC,MAAMiC,MAAMK,WAAW,GAAG,GAC9C;gCAAI;gCAAI;gCACTL,MAAMM,GAAG;;2BALG,CAAC,OAAO,EAAEL,OAAO;oBAQnCvB,KAAK6B,OAAO,CAAClD,GAAG,CAAC,CAACmD,QAAQP,sBACzB,MAACtE;4BAAmCkE,SAAQ;4BAAQF,MAAK;;gCACtDa,OAAOC,MAAM,GACV,CAAC,2BAA2B,EAAED,OAAOE,KAAK,CAAC,uBAAuB,EAAE3C,MAAMyC,OAAOC,MAAM,CAACE,OAAO,EAAE,KAAK,EAAEH,OAAOC,MAAM,CAACG,IAAI,CAAC,EAAE,CAAC,GAC9H,CAAC,gBAAgB,EAAEJ,OAAOE,KAAK,CAAC,IAAI,EAAEF,OAAOK,IAAI,EAAE;gCACtDL,OAAOH,WAAW,GACf,CAAC,iBAAiB,EAAEtC,MAAMyC,OAAOH,WAAW,GAAG,GAC/C;gCACHG,OAAOM,MAAM,GAAG,CAAC,IAAI,EAAE/C,MAAMyC,OAAOM,MAAM,GAAG,GAAG;gCAChDN,OAAOO,QAAQ,CAACzE,MAAM,GACnB,CAAC,EAAE,EAAEkE,OAAOO,QAAQ,CAAC1D,GAAG,CAAC,CAAC2D,UAAYA,QAAQ1D,IAAI,EAAEf,IAAI,CAAC,OAAO,GAChE;;2BAVW,CAAC,OAAO,EAAE0D,OAAO;oBAanC,EAACvB,cAAAA,KAAKuC,KAAK,YAAVvC,cAAc,EAAE,EAAErB,GAAG,CAAC,CAAC6D,qBACvB,MAACvF;4BAAqCkE,SAAQ;4BAAQF,MAAK;;gCACxD,CAAC,UAAU,EAAE1E,cAAciG,KAAKC,EAAE,EAAEC,WAAW,GAAG,CAAC,EAAEF,KAAK5D,IAAI,EAAE;gCAChE4D,KAAKZ,GAAG,GAAG,CAAC,GAAG,EAAEY,KAAKZ,GAAG,EAAE,GAAG;;2BAFhB,CAAC,KAAK,EAAEY,KAAKG,IAAI,EAAE;oBAKrC,EAAC3C,eAAAA,KAAK4C,MAAM,YAAX5C,eAAe,EAAE,EAAErB,GAAG,CAAC,CAACS,OAAOmC,sBAC/B,KAACtE;4BAAkCkE,SAAQ;4BAAQF,MAAK;sCACrD9B,gBAAgBC,OAAOC;2BADT,CAAC,MAAM,EAAEkC,OAAO;;;YAWtCzB,UAAS1C,0BAAAA,OAAQyF,MAAM,mBACtB,KAAC5F;gBAAWkE,SAAQ;gBAAUC,OAAM;gBAAiB0B,WAAU;gBAAM7D,IAAI;oBAAE8B,IAAI;gBAAI;0BAChF3D,OAAOyF,MAAM;;YAGjBzF,CAAAA,0BAAAA,OAAQC,MAAM,MAAK,cAAcD,OAAOG,QAAQ,CAACK,MAAM,GAAG,KAAMkC,CAAAA,SAAS,CAAC1C,OAAOyF,MAAM,AAAD,mBACrF,KAAClG;gBACCmG,WAAU;gBACV7D,IAAI;oBAAE8D,IAAI;oBAAKC,IAAI;gBAAI;gBACvB9B,cAAW;0BAEV9D,OAAOG,QAAQ,CAACoB,GAAG,CAAC,CAAClB,SAAS8D,sBAC7B,KAACtE;wBAEC6F,WAAU;wBACV3B,SAAQ;kCAEP1D,QAAQwF,OAAO;uBAJX,CAAC,QAAQ,EAAE1B,OAAO;;YAS9BlB,QAAQzC,MAAM,GAAG,mBAChB,MAACjB;gBAAIsC,IAAI;oBAAE8B,IAAI;gBAAI;;kCACjB,KAACnE;wBACCsG,MAAK;wBACLC,iBAAelD;wBACfmD,SAAS,IAAMlD,eAAe,CAACmD,QAAU,CAACA;kCAEzCpD,cAAc,0BAA0B;;kCAE3C,KAACnD;wBAASwG,IAAIrD;wBAAasD,aAAa;kCACtC,cAAA,KAACtG;4BACCkE,SAAQ;4BACR2B,WAAU;4BACV5B,cAAW;4BACXjC,IAAI;gCAAEuE,GAAG;gCAAGC,YAAY;gCAAaC,YAAY;gCAAOC,WAAW;4BAAO;sCAEzEtD,QAAQxC,IAAI,CAAC;;;;;YAKrB8C,WAAW,mBACV,KAAC1D;gBACCkE,SAAQ;gBACRC,OAAM;gBACNnC,IAAI;oBAAE2E,SAAS;oBAAS7C,IAAI;gBAAE;0BAE7B,CAAC,sBAAsB,EAAEJ,SAASkD,cAAc,CAAC,SAAS,gDAAgD,CAAC;;YAG/GpD,WAAWrD,OAAO0G,YAAY,IAAI1G,OAAOC,MAAM,KAAK,wBACnD,KAACJ;gBAAWkE,SAAQ;gBAAUC,OAAM;gBAAiB0B,WAAU;gBAAM7D,IAAI;oBAAE8B,IAAI;gBAAE;0BAC9E3D,OAAO0G,YAAY;;YAGvBjD,+BACC,KAAC9D;gBACCkC,IAAI;oBAAE2E,SAAS;oBAAQ7C,IAAI;gBAAE;gBAC7BgD,uBAAS,KAAClH;oBAASqG,MAAK;oBAAQc,SAAS7D;oBAAS8D,UAAU,CAACC,QAAU9D,WAAW8D,MAAMC,MAAM,CAACH,OAAO;;gBACtGI,qBAAO,KAACnH;oBAAWkE,SAAQ;8BAAS;;;YAGvCV,yBACC,KAAC7D;gBACCsG,MAAK;gBACL/B,SAAQ;gBACR,8DAA8D;gBAC9D,yCAAyC;gBACzCkD,UAAUxE,QAASzC,OAAOC,MAAM,KAAK,UAAUiH,QAAQlH,OAAO0G,YAAY;gBAC1EV,SAAS,IAAMxD,SAASD,KAAKkB,iBAAiBV,UAAU;wBAAEA,SAAS;oBAAK,IAAIoE;gBAC5EtF,IAAI;oBAAE8B,IAAI;gBAAE;0BAEX3D,OAAOC,MAAM,KAAK,SAAS,iBAAiB;;YAIhDoD,WAAWV,QAAQtD,6BAA6BkD,sBAAQ,KAACjD;gBAAsBiD,KAAKA;gBAAKI,MAAMA;;;;AAGtG;AAEA,eAAeL,UAAS"}
|
|
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 { 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 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 {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","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","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","Boolean","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
|
-
|
|
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((
|
|
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;
|
|
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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"}
|
|
@@ -33,8 +33,10 @@ import { AI_JOB_TERMINAL_STATUSES } from "../model/ai-jobs.types.js";
|
|
|
33
33
|
import { followAiJobEvents } from "./ai-job-events.js";
|
|
34
34
|
import { aiCreditsBillingHref, aiSiteBuildDoneLinks } from "./ai-job-links.js";
|
|
35
35
|
import { AiJobPlan } from "./ai-job-plan.component.js";
|
|
36
|
-
import { aiBuildCanRetry } from "../model/ai-build-progress.js";
|
|
36
|
+
import { aiBuildCanRetry, aiBuildRetryCreditRange } from "../model/ai-build-progress.js";
|
|
37
37
|
import { resumeAiJobRequest } from "./ai-job-requests.js";
|
|
38
|
+
import { AiCreditsPromptNotice } from "./ai-credits-prompt.component.js";
|
|
39
|
+
import { aiCreditRangeText } from "../model/ai-credit-estimate.js";
|
|
38
40
|
import { useAiJobSite } from "./ai-job-site.js";
|
|
39
41
|
import { AiSiteStarterFallback } from "./ai-site-starter-fallback.component.js";
|
|
40
42
|
/**
|
|
@@ -235,16 +237,21 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
|
|
|
235
237
|
// A failed guided start's Try again reopens its questions, filled in (AGL-3596);
|
|
236
238
|
// `?retry=1` — what the AI jobs drawer links — opens them on arrival.
|
|
237
239
|
const [retrying, setRetrying] = useState(()=>typeof window !== 'undefined' && new URLSearchParams(window.location.search).get('retry') === '1');
|
|
238
|
-
|
|
240
|
+
// A Free Try again past what is left (AGL-3722): its prompt, until chosen.
|
|
241
|
+
const [retryPrompt, setRetryPrompt] = useState(null);
|
|
242
|
+
const tryAgain = useCallback(async (options = {})=>{
|
|
239
243
|
if (!orgId || !job || typeof job !== 'object') return;
|
|
240
244
|
setBusy(true);
|
|
241
245
|
setNotice(null);
|
|
246
|
+
setRetryPrompt(null);
|
|
242
247
|
// A finished build tries again only what failed (AGL-3616).
|
|
243
|
-
const
|
|
248
|
+
const retry = aiBuildCanRetry(job);
|
|
249
|
+
const decision = await resumeAiJobRequest(user, orgId, job, retry ? _extends({
|
|
244
250
|
retry: 'failed-items'
|
|
245
|
-
} : {});
|
|
251
|
+
}, options) : {});
|
|
246
252
|
if (decision.job) setJob(decision.job);
|
|
247
|
-
if (decision.
|
|
253
|
+
if (decision.credits && retry) setRetryPrompt(decision.credits);
|
|
254
|
+
else if (decision.error) setNotice(decision.error);
|
|
248
255
|
setBusy(false);
|
|
249
256
|
}, [
|
|
250
257
|
orgId,
|
|
@@ -258,7 +265,8 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
|
|
|
258
265
|
setNotice(null);
|
|
259
266
|
const decision = await resumeAiJobRequest(user, orgId, target, options);
|
|
260
267
|
if (decision.job) setJob(decision.job);
|
|
261
|
-
|
|
268
|
+
// A plan past what is left draws its own prompt from the job (AGL-3722).
|
|
269
|
+
if (decision.error && !(decision.credits && target.status === 'needs_review')) setNotice(decision.error);
|
|
262
270
|
setBusy(false);
|
|
263
271
|
}, [
|
|
264
272
|
orgId,
|
|
@@ -350,6 +358,7 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
|
|
|
350
358
|
const paused = ready.status === 'needs_input';
|
|
351
359
|
const retryRefusal = (_ready_review = ready.review) == null ? void 0 : _ready_review.retryRefusal;
|
|
352
360
|
const buildRetry = aiBuildCanRetry(ready);
|
|
361
|
+
const retryRange = buildRetry ? aiBuildRetryCreditRange(ready) : null;
|
|
353
362
|
const canRetry = phase === 'stopped' && ((_ready_review1 = ready.review) == null ? void 0 : _ready_review1.reason) === 'doctrine' || buildRetry;
|
|
354
363
|
const buildPlanWaiting = ready.kind === 'build' && phase === 'stopped' && ((_ready_review2 = ready.review) == null ? void 0 : _ready_review2.reason) === 'plan';
|
|
355
364
|
// A guided start that ended without building its site starts over from its
|
|
@@ -503,6 +512,7 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
|
|
|
503
512
|
/*#__PURE__*/ _jsx(AiJobPlan, {
|
|
504
513
|
job: ready,
|
|
505
514
|
busy: busy,
|
|
515
|
+
orgSlug: orgSlug,
|
|
506
516
|
onResume: (target, options)=>void confirmPlan(target, options)
|
|
507
517
|
}) : null,
|
|
508
518
|
sitePublish && sitePublish.drafts.length > 0 && // The pages the publish left as drafts, each with its plain reason;
|
|
@@ -544,9 +554,18 @@ export function AiSiteBuildPage({ hostId, segments, basePath }) {
|
|
|
544
554
|
variant: "outlined",
|
|
545
555
|
disabled: busy,
|
|
546
556
|
onClick: ()=>void tryAgain(),
|
|
547
|
-
children: 'Try again what failed'
|
|
557
|
+
children: retryRange ? `Try again what failed · ${aiCreditRangeText(retryRange)}` : 'Try again what failed'
|
|
548
558
|
})
|
|
549
559
|
}),
|
|
560
|
+
retryPrompt && /*#__PURE__*/ _jsx(AiCreditsPromptNotice, {
|
|
561
|
+
prompt: retryPrompt,
|
|
562
|
+
noun: ready.kind === 'site' ? 'site' : 'build',
|
|
563
|
+
orgSlug: orgSlug,
|
|
564
|
+
busy: busy,
|
|
565
|
+
onBuildWhatFits: ()=>void tryAgain({
|
|
566
|
+
creditsConfirmed: true
|
|
567
|
+
})
|
|
568
|
+
}),
|
|
550
569
|
phase === 'done' && !liveNotice && /*#__PURE__*/ _jsxs(Stack, {
|
|
551
570
|
direction: {
|
|
552
571
|
xs: 'column',
|