@aglyn/plugins-ai 1.0.0-beta.227 → 1.0.0-beta.229
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/README.md +1 -1
- package/package.json +17 -17
- package/src/lib/components/ai-brief-dialog.component.js +1 -1
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-describe-automation.component.js +7 -2
- package/src/lib/components/ai-describe-automation.component.js.map +1 -1
- package/src/lib/components/ai-describe-button.component.js +6 -1
- package/src/lib/components/ai-describe-button.component.js.map +1 -1
- package/src/lib/components/ai-describe-page.component.d.ts +1 -1
- package/src/lib/components/ai-describe-page.component.js +1 -1
- package/src/lib/components/ai-describe-page.component.js.map +1 -1
- package/src/lib/components/ai-job-events.d.ts +7 -1
- package/src/lib/components/ai-job-events.js +4 -2
- package/src/lib/components/ai-job-events.js.map +1 -1
- package/src/lib/components/ai-job-site.d.ts +22 -0
- package/src/lib/components/ai-job-site.js +73 -0
- package/src/lib/components/ai-job-site.js.map +1 -0
- package/src/lib/components/ai-jobs-list-page.component.d.ts +23 -0
- package/src/lib/components/ai-jobs-list-page.component.js +314 -0
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -0
- package/src/lib/components/ai-jobs-page.component.d.ts +7 -0
- package/src/lib/components/ai-jobs-page.component.js +30 -0
- package/src/lib/components/ai-jobs-page.component.js.map +1 -0
- package/src/lib/components/ai-page-brief-dialog.component.d.ts +1 -1
- package/src/lib/components/ai-page-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.d.ts +31 -3
- package/src/lib/components/ai-site-build-page.component.js +394 -243
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.d.ts +18 -1
- package/src/lib/components/ai-site-start-card.component.js +27 -15
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.d.ts +11 -0
- package/src/lib/components/assist-jobs-drawer.component.js +37 -1
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +9 -4
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/jobs/ai-job-draft-ids.d.ts +6 -0
- package/src/lib/jobs/ai-job-draft-ids.js +9 -0
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.d.ts +7 -0
- package/src/lib/jobs/ai-job-drafts.js +16 -7
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-site-pages.d.ts +70 -0
- package/src/lib/jobs/ai-job-layout-site-pages.js +216 -0
- package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -0
- package/src/lib/jobs/ai-job-layout-step.d.ts +2 -2
- package/src/lib/jobs/ai-job-layout-step.js +21 -2
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.d.ts +6 -5
- package/src/lib/jobs/ai-job-page-sections.js +3 -3
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +19 -1
- package/src/lib/jobs/ai-job-page-step.js +50 -8
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.js +7 -2
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +21 -0
- package/src/lib/jobs/ai-job-site-step.js +103 -17
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +7 -1
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +19 -1
- package/src/lib/jobs/ai-jobs.js +130 -53
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-site-publish.d.ts +98 -0
- package/src/lib/jobs/ai-site-publish.js +372 -0
- package/src/lib/jobs/ai-site-publish.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-page-briefs.d.ts +7 -6
- package/src/lib/jobs/fixtures/ai-page-briefs.js +23 -56
- package/src/lib/jobs/fixtures/ai-page-briefs.js.map +1 -1
- package/src/lib/model/ai-job-failure-copy.d.ts +20 -1
- package/src/lib/model/ai-job-failure-copy.js +28 -1
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +6 -1
- package/src/lib/model/ai-job-notice.js +29 -3
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +34 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-plan-capabilities.d.ts +25 -10
- package/src/lib/model/ai-plan-capabilities.js +15 -2
- package/src/lib/model/ai-plan-capabilities.js.map +1 -1
- package/src/lib/model/ai-site-build-progress.d.ts +25 -1
- package/src/lib/model/ai-site-build-progress.js +129 -15
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-start-seo.d.ts +35 -0
- package/src/lib/model/ai-site-start-seo.js +145 -7
- package/src/lib/model/ai-site-start-seo.js.map +1 -1
- package/src/lib/model/ai-site-start.d.ts +14 -0
- package/src/lib/model/ai-site-start.js +43 -0
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/plugin.js +18 -10
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +16 -12
- package/src/lib/runtime/ai-doctrine-validators.js +19 -36
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.d.ts +6 -0
- package/src/lib/runtime/ai-doctrine.js +6 -2
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.d.ts +1 -1
- package/src/lib/runtime/ai-eval-live.js +1 -1
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/server/ai-jobs-live-sites.d.ts +26 -0
- package/src/lib/server/ai-jobs-live-sites.js +58 -0
- package/src/lib/server/ai-jobs-live-sites.js.map +1 -0
- package/src/lib/server/ai-jobs-route.js +22 -3
- package/src/lib/server/ai-jobs-route.js.map +1 -1
- package/src/lib/usage/assist-job-refund.d.ts +3 -2
- package/src/lib/usage/assist-job-refund.js +2 -1
- package/src/lib/usage/assist-job-refund.js.map +1 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ Peer dependencies: `react`, `next`, `@mui/material`, `@mui/x-data-grid`, `fireba
|
|
|
18
18
|
|
|
19
19
|
- The assistant, drawn in the console dock (`consoleDock`) and a provider the shell mounts around every console page for the Besigner copy assistant, with controls in the `besignerToolbar` and `besignerInspector` slots.
|
|
20
20
|
- The AI jobs indicator in the console's top bar (`consoleTopBar`): what the workspace's jobs are doing, and the way to them.
|
|
21
|
-
- "
|
|
21
|
+
- "Create with AI" entry points and cards on a site's pages list and its components, layouts, templates, forms, theme, SEO and automations pages (`hostScreens`, `hostComponents`, `hostLayouts`, `hostTemplates`, `hostForms`, `hostTheme`, `hostSeo`, `seoFields`, `hostAutomations`, `automationEditor`, `automationRun`, `hostFirstRun`, `orgSites`).
|
|
22
22
|
- Cards in zones other plugins host: product copy, import and the products hub (`productEditor`, `productImport`, `productsHub`), record insights, email drafts and import column matching (`recordInsights`, `recordEmail`, `importMapping`), and A/B test variants and results (`experimentVariants`, `experimentResult`).
|
|
23
23
|
- Usage and access: credit and usage cards on billing (`orgBillingUsage`), per-member cards and columns (`orgMember`, `orgMembersListColumn`, `hostMembers`), and staff cards, columns and one staff page (`staffOrg`, `staffUser`, `staffOrgsListColumn`, `staffOrgUsageColumn`).
|
|
24
24
|
|
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.229",
|
|
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.229",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.229",
|
|
30
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.229",
|
|
31
|
+
"@aglyn/shared-data-types": "1.0.0-beta.229",
|
|
32
|
+
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.229",
|
|
33
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.229",
|
|
34
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.229",
|
|
35
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.229",
|
|
36
|
+
"@aglyn/shared-util-email": "1.0.0-beta.229",
|
|
37
|
+
"@aglyn/shared-util-errors": "1.0.0-beta.229",
|
|
38
|
+
"@aglyn/shared-util-http": "1.0.0-beta.229",
|
|
39
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.229",
|
|
40
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.229",
|
|
41
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.229",
|
|
42
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.229",
|
|
43
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.229",
|
|
44
44
|
"@swc/helpers": "0.5.23"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
@@ -26,7 +26,7 @@ import { AiJobFollow } from "./ai-job-follow.component.js";
|
|
|
26
26
|
import { publishAiJob } from "./ai-jobs-store.js";
|
|
27
27
|
import { AiTemplateCollectionField } from "./ai-template-collection-field.component.js";
|
|
28
28
|
/**
|
|
29
|
-
* The brief dialog every "
|
|
29
|
+
* The brief dialog every "Create with AI" opens (AGL-2907, AGL-3043, AGL-3051):
|
|
30
30
|
* a page from the Screens page and from the Assist panel's AI jobs, a page
|
|
31
31
|
* template from Templates, a layout from Layouts, a form from Forms and a
|
|
32
32
|
* reusable component from Components.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-brief-dialog.component.tsx"],"sourcesContent":["'use client'\n\n/**\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 { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Alert,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { AiJobKind, AiJobSummary } from '../model/ai-jobs.types'\nimport { AI_PAGE_TYPES, type AiPageType } from '../model/ai-page-job'\nimport {\n AI_TEMPLATE_SUBJECTS,\n parseAiTemplateJobInputs,\n type AiTemplateSubject,\n} from '../model/ai-template-subjects'\nimport { AiJobFollow } from './ai-job-follow.component'\nimport { publishAiJob } from './ai-jobs-store'\nimport { AiTemplateCollectionField } from './ai-template-collection-field.component'\n\n/**\n * The brief dialog every \"Describe it\" opens (AGL-2907, AGL-3043, AGL-3051):\n * a page from the Screens page and from the Assist panel's AI jobs, a page\n * template from Templates, a layout from Layouts, a form from Forms and a\n * reusable component from Components.\n *\n * One text box, and whatever the kind's door reads beside it, start one job of\n * that kind: a page's optional page type; a template's subject and, for a\n * collection entry's page, its collection. A layout, a form and a component\n * read nothing but the brief and the site. The job plans first; once it\n * exists the dialog follows it live (AGL-3593) — planning, the plan with its\n * Confirm, building, then the draft to open — and \"Open AI jobs\" follows it\n * anywhere else in the console. Nothing here builds or writes anything until\n * the member confirms the plan.\n */\n\n/** The longest brief a job admits. */\nconst BRIEF_MAX_CHARS = 4_000\n\n/** The job kinds a member describes from a console page. */\nexport type AiBriefKind = Extract<\n AiJobKind,\n 'page' | 'template' | 'layout' | 'form' | 'component'\n>\n\n/** What the dialog says for one kind. */\nexport interface AiBriefCopy {\n title: string\n /** The brief box's label. */\n briefLabel: string\n placeholder: string\n /** What happens after the member starts the job, under the fields. */\n next: string\n /** The action that starts the job. */\n submit: string\n /** What the dialog says once the job exists, above its live status. */\n started: string\n /** What it says when the door gave no reason, or could not be reached. */\n failed: string\n}\n\nexport const AI_BRIEF_COPY: Readonly<Record<AiBriefKind, AiBriefCopy>> = {\n page: {\n title: 'Describe a page',\n briefLabel: 'What is the page for?',\n placeholder:\n 'A landing page for our spring roof inspection offer, with the quote request form',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The page uses your ' +\n 'theme and what the site already has, and the plan lists anything the job builds first.',\n submit: 'Plan the page',\n started:\n 'The page is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The page is built as an unpublished draft.',\n failed: 'The page could not be started. Try again.',\n },\n template: {\n title: 'Describe a page template',\n briefLabel: 'What should each page show?',\n placeholder:\n 'A page for each of our team members: their name and photo, their role, a short ' +\n 'biography and a way to get in touch',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What changes from one ' +\n 'page to the next, such as a name or a photo, is filled in from each record rather ' +\n 'than typed.',\n submit: 'Plan the template',\n started:\n 'The template is being planned. Review the plan here or in AI jobs, and confirm it ' +\n 'to build. The template is built as a draft in your library, and nothing on your ' +\n 'site uses it until you do.',\n failed: 'The template could not be started. Try again.',\n },\n layout: {\n title: 'Describe a layout',\n briefLabel: 'What should the layout hold?',\n placeholder:\n 'A header with our name and links to Services, About and Contact, and a footer with ' +\n 'our address and opening hours',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The layout links your ' +\n 'pages, and places your navigation or menu component when the site has one.',\n submit: 'Plan the layout',\n started:\n 'The layout is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The layout is built as a draft, and no page uses it until you assign it.',\n failed: 'The layout could not be started. Try again.',\n },\n form: {\n title: 'Describe a form',\n briefLabel: 'What is the form for?',\n placeholder:\n 'A quote request form for a roofing company, asking what needs fixing and the best ' +\n 'time to call',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The form’s fields, its ' +\n 'marketing consent and where each submission goes are planned together.',\n submit: 'Plan the form',\n started:\n 'The form is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The form is built as a draft, and it collects nothing until you place it on ' +\n 'a page.',\n failed: 'The form could not be started. Try again.',\n },\n component: {\n title: 'Describe a reusable component',\n briefLabel: 'What should the component show?',\n placeholder:\n 'A testimonial card with the customer’s name, their role, their quote and a photo',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What each page can ' +\n 'change, such as a name or a photo, becomes a property of the component.',\n submit: 'Plan the component',\n started:\n 'The component is being planned. Review the plan here or in AI jobs, and confirm it ' +\n 'to build. The component is built as a draft, and no page shows it until you insert ' +\n 'it.',\n failed: 'The component could not be started. Try again.',\n },\n}\n\n/** Each template subject as its chip names it: what one page is drawn for. */\nexport const AI_TEMPLATE_SUBJECT_LABELS: Readonly<Record<AiTemplateSubject, string>> = {\n entry: 'Collection entry',\n product: 'Product',\n author: 'Author',\n}\n\n/** What a member picked beside the brief; each kind reads only its own. */\nexport interface AiBriefChoice {\n /** A page's type, or `null` for none. */\n pageType: AiPageType | null\n /** What a template's pages are drawn for, or `null` until one is picked. */\n subject: AiTemplateSubject | null\n /** A collection entry template's collection, or `null` until one is picked. */\n collectionId: string | null\n}\n\nexport const AI_BRIEF_NO_CHOICE: AiBriefChoice = {\n pageType: null,\n subject: null,\n collectionId: null,\n}\n\n/**\n * The job's `inputs` for what the member picked, or `null` while the kind's\n * door would refuse them.\n *\n * A template's are read by the parser its door reads, so the dialog cannot\n * send a subject the door does not know or an entry page with no collection.\n * A page's type is optional. A layout, a form and a component read no inputs\n * at all (AGL-2909, AGL-2913, AGL-2908), so theirs are empty.\n */\nexport function aiBriefJobInputs(\n kind: AiBriefKind,\n choice: AiBriefChoice,\n): Record<string, string> | null {\n if (kind === 'page') return choice.pageType ? { pageType: choice.pageType } : {}\n if (kind === 'template') {\n const inputs = parseAiTemplateJobInputs({\n subject: choice.subject,\n collectionId: choice.collectionId,\n })\n if (typeof inputs === 'string') return null\n return inputs.collectionId\n ? { subject: inputs.subject, collectionId: inputs.collectionId }\n : { subject: inputs.subject }\n }\n return {}\n}\n\nexport interface AiBriefDialogProps {\n /** The job the brief starts. */\n kind: AiBriefKind\n open: boolean\n onClose: () => void\n orgId: string | undefined\n hostId: string\n /** Who is signed in, whose token the request carries. */\n user: Parameters<typeof authorizedFetch>[0]\n /** Path slug for the link to the draft; read from the address when omitted. */\n orgSlug?: string | null\n /** The viewer is staff (AGL-3078), for a refused plan's details. */\n isStaff?: boolean\n}\n\nexport function AiBriefDialog({\n kind,\n open,\n onClose,\n orgId,\n hostId,\n user,\n orgSlug,\n isStaff,\n}: AiBriefDialogProps) {\n const copy = AI_BRIEF_COPY[kind]\n // Held in a ref so the request reads WHO is signed in, and nothing keys on\n // the identity of the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const [brief, setBrief] = useState('')\n const [choice, setChoice] = useState<AiBriefChoice>(AI_BRIEF_NO_CHOICE)\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n /** The job the brief started, as the create door answered with it. */\n const [started, setStarted] = useState<AiJobSummary | null>(null)\n\n useEffect(() => {\n if (!open) return\n setNotice(null)\n setStarted(null)\n }, [open])\n\n const pick = useCallback(\n (patch: Partial<AiBriefChoice>) => setChoice((current) => ({ ...current, ...patch })),\n [],\n )\n const ready = aiBriefJobInputs(kind, choice) !== null\n\n const start = useCallback(async () => {\n const inputs = aiBriefJobInputs(kind, choice)\n if (!orgId || !brief.trim() || !inputs) return\n setBusy(true)\n setNotice(null)\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ orgId, hostId, kind, brief: brief.trim(), inputs }),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(locked ? lockdownRefusalText(locked) : String(payload?.error ?? copy.failed))\n return\n }\n const created = (payload?.job as AiJobSummary | undefined) ?? null\n if (!created) {\n setNotice(copy.failed)\n return\n }\n // The indicator and the launcher count it from the moment it exists.\n publishAiJob(created)\n setStarted(created)\n setBrief('')\n setChoice(AI_BRIEF_NO_CHOICE)\n } catch {\n setNotice(copy.failed)\n } finally {\n setBusy(false)\n }\n }, [orgId, hostId, kind, brief, choice, copy])\n\n return (\n <Dialog open={open} onClose={busy ? undefined : onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{copy.title}</DialogTitle>\n <DialogContent>\n {started && orgId ? (\n <AiJobFollow\n job={started}\n orgId={orgId}\n orgSlug={orgSlug}\n user={user}\n intro={copy.started}\n staff={isStaff}\n // The dialog covers the panel AI jobs opens in.\n onOpenJobs={onClose}\n />\n ) : (\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n label={copy.briefLabel}\n placeholder={copy.placeholder}\n multiline\n minRows={4}\n value={brief}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n helperText={`${brief.length.toLocaleString('en-US')} / ${BRIEF_MAX_CHARS.toLocaleString('en-US')}`}\n disabled={busy}\n autoFocus\n />\n {kind === 'page' ? (\n <ChipGroup\n id=\"ai-page-type-label\"\n label=\"Page type (optional)\"\n options={AI_PAGE_TYPES.map((type) => ({ id: type.id, label: type.label }))}\n value={choice.pageType}\n // Optional: pressing the pressed type again sends none.\n onPress={(id) =>\n pick({ pageType: choice.pageType === id ? null : (id as AiPageType) })\n }\n disabled={busy}\n />\n ) : null}\n {kind === 'template' ? (\n <>\n <ChipGroup\n id=\"ai-template-subject-label\"\n // Marked required because it IS (AGL-3143 §12): \"Plan the\n // template\" stays disabled until one is picked, and the page\n // dialog's chip group beside it reads \"(optional)\" — so an\n // unmarked label here read as optional too, and a member who\n // wrote a brief and stopped had nothing telling them why the\n // button would not light.\n label=\"One page for each (required)\"\n options={AI_TEMPLATE_SUBJECTS.map((subject) => ({\n id: subject,\n label: AI_TEMPLATE_SUBJECT_LABELS[subject],\n }))}\n value={choice.subject}\n // Required, so a press picks and never clears.\n onPress={(id) => pick({ subject: id as AiTemplateSubject })}\n disabled={busy}\n />\n {choice.subject === 'entry' ? (\n <AiTemplateCollectionField\n hostId={hostId}\n value={choice.collectionId}\n onChange={(collectionId) => pick({ collectionId })}\n disabled={busy}\n />\n ) : null}\n </>\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {copy.next}\n </Typography>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n </Stack>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={busy}>\n {started ? 'Close' : 'Cancel'}\n </Button>\n {started ? null : (\n <Button\n variant=\"contained\"\n onClick={() => void start()}\n disabled={busy || !orgId || !brief.trim() || !ready}\n >\n {copy.submit}\n </Button>\n )}\n </DialogActions>\n </Dialog>\n )\n}\n\n/** A labeled row of chips, each a toggle button that says whether it is pressed. */\nfunction ChipGroup(props: {\n id: string\n label: string\n options: ReadonlyArray<{ id: string; label: string }>\n value: string | null\n onPress: (id: string) => void\n disabled: boolean\n}) {\n const { id, label, options, value, onPress, disabled } = props\n return (\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\" id={id}>\n {label}\n </Typography>\n <Stack\n direction=\"row\"\n useFlexGap\n role=\"group\"\n aria-labelledby={id}\n sx={{ flexWrap: 'wrap', gap: 1 }}\n >\n {options.map((option) => (\n <Chip\n key={option.id}\n label={option.label}\n color={value === option.id ? 'primary' : 'default'}\n variant={value === option.id ? 'filled' : 'outlined'}\n onClick={() => onPress(option.id)}\n aria-pressed={value === option.id}\n disabled={disabled}\n />\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default AiBriefDialog\n"],"names":["lockdownRefusalText","parseLockdownRefusal","authorizedFetch","Alert","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Stack","TextField","Typography","useCallback","useEffect","useRef","useState","AI_PAGE_TYPES","AI_TEMPLATE_SUBJECTS","parseAiTemplateJobInputs","AiJobFollow","publishAiJob","AiTemplateCollectionField","BRIEF_MAX_CHARS","AI_BRIEF_COPY","page","title","briefLabel","placeholder","next","submit","started","failed","template","layout","form","component","AI_TEMPLATE_SUBJECT_LABELS","entry","product","author","AI_BRIEF_NO_CHOICE","pageType","subject","collectionId","aiBriefJobInputs","kind","choice","inputs","AiBriefDialog","open","onClose","orgId","hostId","user","orgSlug","isStaff","copy","userRef","current","brief","setBrief","setChoice","busy","setBusy","notice","setNotice","setStarted","pick","patch","ready","start","trim","response","method","headers","body","JSON","stringify","payload","json","catch","ok","locked","status","String","error","created","job","undefined","fullWidth","maxWidth","intro","staff","onOpenJobs","spacing","sx","pt","label","multiline","minRows","value","onChange","event","target","slice","helperText","length","toLocaleString","disabled","autoFocus","ChipGroup","id","options","map","type","onPress","variant","color","severity","onClick","props","direction","useFlexGap","role","aria-labelledby","flexWrap","gap","option","aria-pressed"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-brief-dialog.component.tsx"],"sourcesContent":["'use client'\n\n/**\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 { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Alert,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { AiJobKind, AiJobSummary } from '../model/ai-jobs.types'\nimport { AI_PAGE_TYPES, type AiPageType } from '../model/ai-page-job'\nimport {\n AI_TEMPLATE_SUBJECTS,\n parseAiTemplateJobInputs,\n type AiTemplateSubject,\n} from '../model/ai-template-subjects'\nimport { AiJobFollow } from './ai-job-follow.component'\nimport { publishAiJob } from './ai-jobs-store'\nimport { AiTemplateCollectionField } from './ai-template-collection-field.component'\n\n/**\n * The brief dialog every \"Create with AI\" opens (AGL-2907, AGL-3043, AGL-3051):\n * a page from the Screens page and from the Assist panel's AI jobs, a page\n * template from Templates, a layout from Layouts, a form from Forms and a\n * reusable component from Components.\n *\n * One text box, and whatever the kind's door reads beside it, start one job of\n * that kind: a page's optional page type; a template's subject and, for a\n * collection entry's page, its collection. A layout, a form and a component\n * read nothing but the brief and the site. The job plans first; once it\n * exists the dialog follows it live (AGL-3593) — planning, the plan with its\n * Confirm, building, then the draft to open — and \"Open AI jobs\" follows it\n * anywhere else in the console. Nothing here builds or writes anything until\n * the member confirms the plan.\n */\n\n/** The longest brief a job admits. */\nconst BRIEF_MAX_CHARS = 4_000\n\n/** The job kinds a member describes from a console page. */\nexport type AiBriefKind = Extract<\n AiJobKind,\n 'page' | 'template' | 'layout' | 'form' | 'component'\n>\n\n/** What the dialog says for one kind. */\nexport interface AiBriefCopy {\n title: string\n /** The brief box's label. */\n briefLabel: string\n placeholder: string\n /** What happens after the member starts the job, under the fields. */\n next: string\n /** The action that starts the job. */\n submit: string\n /** What the dialog says once the job exists, above its live status. */\n started: string\n /** What it says when the door gave no reason, or could not be reached. */\n failed: string\n}\n\nexport const AI_BRIEF_COPY: Readonly<Record<AiBriefKind, AiBriefCopy>> = {\n page: {\n title: 'Describe a page',\n briefLabel: 'What is the page for?',\n placeholder:\n 'A landing page for our spring roof inspection offer, with the quote request form',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The page uses your ' +\n 'theme and what the site already has, and the plan lists anything the job builds first.',\n submit: 'Plan the page',\n started:\n 'The page is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The page is built as an unpublished draft.',\n failed: 'The page could not be started. Try again.',\n },\n template: {\n title: 'Describe a page template',\n briefLabel: 'What should each page show?',\n placeholder:\n 'A page for each of our team members: their name and photo, their role, a short ' +\n 'biography and a way to get in touch',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What changes from one ' +\n 'page to the next, such as a name or a photo, is filled in from each record rather ' +\n 'than typed.',\n submit: 'Plan the template',\n started:\n 'The template is being planned. Review the plan here or in AI jobs, and confirm it ' +\n 'to build. The template is built as a draft in your library, and nothing on your ' +\n 'site uses it until you do.',\n failed: 'The template could not be started. Try again.',\n },\n layout: {\n title: 'Describe a layout',\n briefLabel: 'What should the layout hold?',\n placeholder:\n 'A header with our name and links to Services, About and Contact, and a footer with ' +\n 'our address and opening hours',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The layout links your ' +\n 'pages, and places your navigation or menu component when the site has one.',\n submit: 'Plan the layout',\n started:\n 'The layout is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The layout is built as a draft, and no page uses it until you assign it.',\n failed: 'The layout could not be started. Try again.',\n },\n form: {\n title: 'Describe a form',\n briefLabel: 'What is the form for?',\n placeholder:\n 'A quote request form for a roofing company, asking what needs fixing and the best ' +\n 'time to call',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The form’s fields, its ' +\n 'marketing consent and where each submission goes are planned together.',\n submit: 'Plan the form',\n started:\n 'The form is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The form is built as a draft, and it collects nothing until you place it on ' +\n 'a page.',\n failed: 'The form could not be started. Try again.',\n },\n component: {\n title: 'Describe a reusable component',\n briefLabel: 'What should the component show?',\n placeholder:\n 'A testimonial card with the customer’s name, their role, their quote and a photo',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What each page can ' +\n 'change, such as a name or a photo, becomes a property of the component.',\n submit: 'Plan the component',\n started:\n 'The component is being planned. Review the plan here or in AI jobs, and confirm it ' +\n 'to build. The component is built as a draft, and no page shows it until you insert ' +\n 'it.',\n failed: 'The component could not be started. Try again.',\n },\n}\n\n/** Each template subject as its chip names it: what one page is drawn for. */\nexport const AI_TEMPLATE_SUBJECT_LABELS: Readonly<Record<AiTemplateSubject, string>> = {\n entry: 'Collection entry',\n product: 'Product',\n author: 'Author',\n}\n\n/** What a member picked beside the brief; each kind reads only its own. */\nexport interface AiBriefChoice {\n /** A page's type, or `null` for none. */\n pageType: AiPageType | null\n /** What a template's pages are drawn for, or `null` until one is picked. */\n subject: AiTemplateSubject | null\n /** A collection entry template's collection, or `null` until one is picked. */\n collectionId: string | null\n}\n\nexport const AI_BRIEF_NO_CHOICE: AiBriefChoice = {\n pageType: null,\n subject: null,\n collectionId: null,\n}\n\n/**\n * The job's `inputs` for what the member picked, or `null` while the kind's\n * door would refuse them.\n *\n * A template's are read by the parser its door reads, so the dialog cannot\n * send a subject the door does not know or an entry page with no collection.\n * A page's type is optional. A layout, a form and a component read no inputs\n * at all (AGL-2909, AGL-2913, AGL-2908), so theirs are empty.\n */\nexport function aiBriefJobInputs(\n kind: AiBriefKind,\n choice: AiBriefChoice,\n): Record<string, string> | null {\n if (kind === 'page') return choice.pageType ? { pageType: choice.pageType } : {}\n if (kind === 'template') {\n const inputs = parseAiTemplateJobInputs({\n subject: choice.subject,\n collectionId: choice.collectionId,\n })\n if (typeof inputs === 'string') return null\n return inputs.collectionId\n ? { subject: inputs.subject, collectionId: inputs.collectionId }\n : { subject: inputs.subject }\n }\n return {}\n}\n\nexport interface AiBriefDialogProps {\n /** The job the brief starts. */\n kind: AiBriefKind\n open: boolean\n onClose: () => void\n orgId: string | undefined\n hostId: string\n /** Who is signed in, whose token the request carries. */\n user: Parameters<typeof authorizedFetch>[0]\n /** Path slug for the link to the draft; read from the address when omitted. */\n orgSlug?: string | null\n /** The viewer is staff (AGL-3078), for a refused plan's details. */\n isStaff?: boolean\n}\n\nexport function AiBriefDialog({\n kind,\n open,\n onClose,\n orgId,\n hostId,\n user,\n orgSlug,\n isStaff,\n}: AiBriefDialogProps) {\n const copy = AI_BRIEF_COPY[kind]\n // Held in a ref so the request reads WHO is signed in, and nothing keys on\n // the identity of the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const [brief, setBrief] = useState('')\n const [choice, setChoice] = useState<AiBriefChoice>(AI_BRIEF_NO_CHOICE)\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n /** The job the brief started, as the create door answered with it. */\n const [started, setStarted] = useState<AiJobSummary | null>(null)\n\n useEffect(() => {\n if (!open) return\n setNotice(null)\n setStarted(null)\n }, [open])\n\n const pick = useCallback(\n (patch: Partial<AiBriefChoice>) => setChoice((current) => ({ ...current, ...patch })),\n [],\n )\n const ready = aiBriefJobInputs(kind, choice) !== null\n\n const start = useCallback(async () => {\n const inputs = aiBriefJobInputs(kind, choice)\n if (!orgId || !brief.trim() || !inputs) return\n setBusy(true)\n setNotice(null)\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ orgId, hostId, kind, brief: brief.trim(), inputs }),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(locked ? lockdownRefusalText(locked) : String(payload?.error ?? copy.failed))\n return\n }\n const created = (payload?.job as AiJobSummary | undefined) ?? null\n if (!created) {\n setNotice(copy.failed)\n return\n }\n // The indicator and the launcher count it from the moment it exists.\n publishAiJob(created)\n setStarted(created)\n setBrief('')\n setChoice(AI_BRIEF_NO_CHOICE)\n } catch {\n setNotice(copy.failed)\n } finally {\n setBusy(false)\n }\n }, [orgId, hostId, kind, brief, choice, copy])\n\n return (\n <Dialog open={open} onClose={busy ? undefined : onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{copy.title}</DialogTitle>\n <DialogContent>\n {started && orgId ? (\n <AiJobFollow\n job={started}\n orgId={orgId}\n orgSlug={orgSlug}\n user={user}\n intro={copy.started}\n staff={isStaff}\n // The dialog covers the panel AI jobs opens in.\n onOpenJobs={onClose}\n />\n ) : (\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n label={copy.briefLabel}\n placeholder={copy.placeholder}\n multiline\n minRows={4}\n value={brief}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n helperText={`${brief.length.toLocaleString('en-US')} / ${BRIEF_MAX_CHARS.toLocaleString('en-US')}`}\n disabled={busy}\n autoFocus\n />\n {kind === 'page' ? (\n <ChipGroup\n id=\"ai-page-type-label\"\n label=\"Page type (optional)\"\n options={AI_PAGE_TYPES.map((type) => ({ id: type.id, label: type.label }))}\n value={choice.pageType}\n // Optional: pressing the pressed type again sends none.\n onPress={(id) =>\n pick({ pageType: choice.pageType === id ? null : (id as AiPageType) })\n }\n disabled={busy}\n />\n ) : null}\n {kind === 'template' ? (\n <>\n <ChipGroup\n id=\"ai-template-subject-label\"\n // Marked required because it IS (AGL-3143 §12): \"Plan the\n // template\" stays disabled until one is picked, and the page\n // dialog's chip group beside it reads \"(optional)\" — so an\n // unmarked label here read as optional too, and a member who\n // wrote a brief and stopped had nothing telling them why the\n // button would not light.\n label=\"One page for each (required)\"\n options={AI_TEMPLATE_SUBJECTS.map((subject) => ({\n id: subject,\n label: AI_TEMPLATE_SUBJECT_LABELS[subject],\n }))}\n value={choice.subject}\n // Required, so a press picks and never clears.\n onPress={(id) => pick({ subject: id as AiTemplateSubject })}\n disabled={busy}\n />\n {choice.subject === 'entry' ? (\n <AiTemplateCollectionField\n hostId={hostId}\n value={choice.collectionId}\n onChange={(collectionId) => pick({ collectionId })}\n disabled={busy}\n />\n ) : null}\n </>\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {copy.next}\n </Typography>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n </Stack>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={busy}>\n {started ? 'Close' : 'Cancel'}\n </Button>\n {started ? null : (\n <Button\n variant=\"contained\"\n onClick={() => void start()}\n disabled={busy || !orgId || !brief.trim() || !ready}\n >\n {copy.submit}\n </Button>\n )}\n </DialogActions>\n </Dialog>\n )\n}\n\n/** A labeled row of chips, each a toggle button that says whether it is pressed. */\nfunction ChipGroup(props: {\n id: string\n label: string\n options: ReadonlyArray<{ id: string; label: string }>\n value: string | null\n onPress: (id: string) => void\n disabled: boolean\n}) {\n const { id, label, options, value, onPress, disabled } = props\n return (\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\" id={id}>\n {label}\n </Typography>\n <Stack\n direction=\"row\"\n useFlexGap\n role=\"group\"\n aria-labelledby={id}\n sx={{ flexWrap: 'wrap', gap: 1 }}\n >\n {options.map((option) => (\n <Chip\n key={option.id}\n label={option.label}\n color={value === option.id ? 'primary' : 'default'}\n variant={value === option.id ? 'filled' : 'outlined'}\n onClick={() => onPress(option.id)}\n aria-pressed={value === option.id}\n disabled={disabled}\n />\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default AiBriefDialog\n"],"names":["lockdownRefusalText","parseLockdownRefusal","authorizedFetch","Alert","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Stack","TextField","Typography","useCallback","useEffect","useRef","useState","AI_PAGE_TYPES","AI_TEMPLATE_SUBJECTS","parseAiTemplateJobInputs","AiJobFollow","publishAiJob","AiTemplateCollectionField","BRIEF_MAX_CHARS","AI_BRIEF_COPY","page","title","briefLabel","placeholder","next","submit","started","failed","template","layout","form","component","AI_TEMPLATE_SUBJECT_LABELS","entry","product","author","AI_BRIEF_NO_CHOICE","pageType","subject","collectionId","aiBriefJobInputs","kind","choice","inputs","AiBriefDialog","open","onClose","orgId","hostId","user","orgSlug","isStaff","copy","userRef","current","brief","setBrief","setChoice","busy","setBusy","notice","setNotice","setStarted","pick","patch","ready","start","trim","response","method","headers","body","JSON","stringify","payload","json","catch","ok","locked","status","String","error","created","job","undefined","fullWidth","maxWidth","intro","staff","onOpenJobs","spacing","sx","pt","label","multiline","minRows","value","onChange","event","target","slice","helperText","length","toLocaleString","disabled","autoFocus","ChipGroup","id","options","map","type","onPress","variant","color","severity","onClick","props","direction","useFlexGap","role","aria-labelledby","flexWrap","gap","option","aria-pressed"],"mappings":"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"}
|
|
@@ -16,13 +16,15 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
16
16
|
* See the License for the specific language governing permissions and
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/ import { pluginDocsHelp } from "@aglyn/aglyn/app-utils/docs-help";
|
|
19
|
+
import { mdiCreation } from "@aglyn/shared-data-mdi";
|
|
20
|
+
import { MdiIcon } from "@aglyn/shared-ui-jsx";
|
|
19
21
|
import { useUser } from "@aglyn/tenant-feature-instance";
|
|
20
22
|
import { Alert, Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle, Link, Stack, TextField, Typography } from "@mui/material";
|
|
21
23
|
import { useEffect, useState } from "react";
|
|
22
24
|
import { openAiJobs } from "./ai-jobs-store.js";
|
|
23
25
|
import { aiJobProblem, useAiJobRun, useAiJobsVerdict } from "./use-ai-job-run.js";
|
|
24
26
|
/**
|
|
25
|
-
* "
|
|
27
|
+
* "Create with AI" on the Automation page's Actions (AGL-2919), beside Add action
|
|
26
28
|
* and Recipes through the `hostAutomations` zone: a description becomes a
|
|
27
29
|
* `workflow` job that drafts one automation, switched off.
|
|
28
30
|
*
|
|
@@ -222,8 +224,11 @@ export function AiAutomationBriefDialog(props) {
|
|
|
222
224
|
/*#__PURE__*/ _jsx(Button, {
|
|
223
225
|
size: "small",
|
|
224
226
|
variant: "outlined",
|
|
227
|
+
startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
228
|
+
path: mdiCreation.path
|
|
229
|
+
}),
|
|
225
230
|
onClick: ()=>setOpen(true),
|
|
226
|
-
children: '
|
|
231
|
+
children: 'Create with AI'
|
|
227
232
|
}),
|
|
228
233
|
/*#__PURE__*/ _jsx(AiAutomationBriefDialog, {
|
|
229
234
|
open: open,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-describe-automation.component.tsx"],"sourcesContent":["'use client'\n\n/**\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 { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport type { ConsoleHostAutomationsZoneProps } from './ai-automation-zones'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Button,\n CircularProgress,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Link,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useState } from 'react'\nimport { openAiJobs } from './ai-jobs-store'\nimport { aiJobProblem, useAiJobRun, useAiJobsVerdict } from './use-ai-job-run'\n\n/**\n * \"Describe it\" on the Automation page's Actions (AGL-2919), beside Add action\n * and Recipes through the `hostAutomations` zone: a description becomes a\n * `workflow` job that drafts one automation, switched off.\n *\n * The dialog follows the shared brief dialog's shape — one text box, what\n * happens next, and the action that starts the job — and then follows the\n * job, because an automation's draft needs no plan to confirm: when it is\n * written, the dialog says what to fill in and opens it in the Actions editor.\n */\n\n/** The longest description a job admits. */\nconst BRIEF_MAX_CHARS = 4_000\n\nexport const AI_AUTOMATION_BRIEF_COPY = {\n title: 'Describe an automation',\n briefLabel: 'What should happen, and when?',\n placeholder:\n 'When someone submits the newsletter form, add them to the newsletter list, make them ' +\n 'a lead and send them a welcome email',\n next:\n 'The automation is drafted switched off, using only the triggers and steps your plan ' +\n 'includes. Anything it needs that your site does not have yet is left for you to fill in.',\n submit: 'Draft the automation',\n started:\n 'The automation is being drafted. This can take a few minutes. You can close this ' +\n 'window: the draft appears in the list, switched off, when it is ready. Open AI ' +\n 'jobs to follow it meanwhile.',\n drafted: 'Drafted',\n review: 'Review it',\n notListed: 'The draft is saved. It appears in the list in a moment.',\n failed: 'The automation could not be drafted. Try again.',\n} as const\n\nexport interface AiAutomationBriefDialogProps {\n open: boolean\n onClose: () => void\n orgId: string | undefined\n hostId: string\n /** Who is signed in, whose token the requests carry. */\n user: Parameters<typeof useAiJobRun>[0]\n /** Opens a listed action in the Actions editor; `false` when the list has not read it yet. */\n openAction: (actionId: string) => boolean\n}\n\nexport function AiAutomationBriefDialog(props: AiAutomationBriefDialogProps) {\n const { open, onClose, orgId, hostId, user, openAction } = props\n const copy = AI_AUTOMATION_BRIEF_COPY\n const help = pluginDocsHelp('aiAutomations')\n const [brief, setBrief] = useState('')\n const [notListed, setNotListed] = useState(false)\n const run = useAiJobRun(user, copy.failed)\n const { job, starting, running, notice, reset } = run\n\n // Opening or closing starts over and stops following: a job still drafting\n // carries on in AI jobs, and its draft appears in the list when it is written.\n useEffect(() => {\n reset()\n setNotListed(false)\n }, [open, reset])\n\n const draft = job?.status === 'done' ? job.outputs.find((output) => output.resource === 'workflow') : undefined\n const problem = aiJobProblem(job, copy.failed) ?? (job?.status === 'done' && !draft ? copy.failed : null)\n const asking = !job || Boolean(notice)\n\n const start = async () => {\n const trimmed = brief.trim()\n if (!orgId || !trimmed) return\n setNotListed(false)\n if (await run.start({ orgId, hostId, kind: 'workflow', brief: trimmed, inputs: { mode: 'draft' } })) {\n setBrief('')\n }\n }\n\n const review = () => {\n if (!draft) return\n if (openAction(draft.id)) onClose()\n else setNotListed(true)\n }\n\n return (\n <Dialog open={open} onClose={starting ? undefined : onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{copy.title}</DialogTitle>\n <DialogContent>\n {asking ? (\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n label={copy.briefLabel}\n placeholder={copy.placeholder}\n multiline\n minRows={4}\n value={brief}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n helperText={`${brief.length.toLocaleString('en-US')} / ${BRIEF_MAX_CHARS.toLocaleString('en-US')}`}\n disabled={starting}\n autoFocus\n />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {copy.next}{' '}\n <Link href={help.href} target=\"_blank\" rel=\"noopener\" title={help.excerpt}>\n {'How it works'}\n </Link>\n </Typography>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n </Stack>\n ) : (\n <Stack spacing={2} sx={{ pt: 1 }}>\n {running && job ? (\n <Alert\n severity=\"info\"\n icon={<CircularProgress size={18} aria-label=\"Drafting\" />}\n action={\n // Follows the job in the Assist panel (AGL-3593); the dialog\n // covers it, so it closes.\n <Button\n color=\"inherit\"\n size=\"small\"\n variant=\"outlined\"\n onClick={() => {\n openAiJobs({ jobId: job.id })\n onClose()\n }}\n >\n {'Open AI jobs'}\n </Button>\n }\n >\n {copy.started}\n </Alert>\n ) : null}\n {draft ? (\n <Alert severity=\"success\">\n <Typography variant=\"body2\" sx={{ fontWeight: 600 }}>\n {`${copy.drafted}: ${draft.label}`}\n </Typography>\n {draft.note ? <Typography variant=\"body2\">{draft.note}</Typography> : null}\n </Alert>\n ) : null}\n {problem ? <Alert severity={job?.status === 'failed' ? 'error' : 'warning'}>{problem}</Alert> : null}\n {notListed ? <Alert severity=\"info\">{copy.notListed}</Alert> : null}\n </Stack>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={starting}>\n {asking ? 'Cancel' : 'Close'}\n </Button>\n {asking ? (\n <Button\n variant=\"contained\"\n onClick={() => void start()}\n disabled={starting || !orgId || !brief.trim()}\n >\n {copy.submit}\n </Button>\n ) : draft ? (\n <Button variant=\"contained\" onClick={review}>\n {copy.review}\n </Button>\n ) : null}\n </DialogActions>\n </Dialog>\n )\n}\n\n/**\n * Renders nothing until the jobs route has answered for this workspace, and\n * nothing when it says the feature is not this workspace's.\n */\nexport function AiDescribeAutomationButton({ hostId, orgId, openAction }: ConsoleHostAutomationsZoneProps) {\n const { data: user } = useUser()\n const verdict = useAiJobsVerdict(user, orgId)\n const [open, setOpen] = useState(false)\n\n if (verdict !== 'ready') return null\n return (\n <>\n <Button size=\"small\" variant=\"outlined\" onClick={() => setOpen(true)}>\n {'Describe it'}\n </Button>\n <AiAutomationBriefDialog\n open={open}\n onClose={() => setOpen(false)}\n orgId={orgId}\n hostId={hostId}\n user={user}\n openAction={openAction}\n />\n </>\n )\n}\n\nexport default AiDescribeAutomationButton\n"],"names":["pluginDocsHelp","useUser","Alert","Button","CircularProgress","Dialog","DialogActions","DialogContent","DialogTitle","Link","Stack","TextField","Typography","useEffect","useState","openAiJobs","aiJobProblem","useAiJobRun","useAiJobsVerdict","BRIEF_MAX_CHARS","AI_AUTOMATION_BRIEF_COPY","title","briefLabel","placeholder","next","submit","started","drafted","review","notListed","failed","AiAutomationBriefDialog","props","open","onClose","orgId","hostId","user","openAction","copy","help","brief","setBrief","setNotListed","run","job","starting","running","notice","reset","draft","status","outputs","find","output","resource","undefined","problem","asking","Boolean","start","trimmed","trim","kind","inputs","mode","id","fullWidth","maxWidth","spacing","sx","pt","label","multiline","minRows","value","onChange","event","target","slice","helperText","length","toLocaleString","disabled","autoFocus","variant","color","href","rel","excerpt","severity","icon","size","aria-label","action","onClick","jobId","fontWeight","note","AiDescribeAutomationButton","data","verdict","setOpen"],"mappings":"AAAA;;AAEA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,cAAc,QAAQ,mCAAkC;AAEjE,SAASC,OAAO,QAAQ,iCAAgC;AACxD,SACEC,KAAK,EACLC,MAAM,EACNC,gBAAgB,EAChBC,MAAM,EACNC,aAAa,EACbC,aAAa,EACbC,WAAW,EACXC,IAAI,EACJC,KAAK,EACLC,SAAS,EACTC,UAAU,QACL,gBAAe;AACtB,SAASC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAC3C,SAASC,UAAU,QAAQ,qBAAiB;AAC5C,SAASC,YAAY,EAAEC,WAAW,EAAEC,gBAAgB,QAAQ,sBAAkB;AAE9E;;;;;;;;;CASC,GAED,0CAA0C,GAC1C,MAAMC,kBAAkB;AAExB,OAAO,MAAMC,2BAA2B;IACtCC,OAAO;IACPC,YAAY;IACZC,aACE,0FACA;IACFC,MACE,yFACA;IACFC,QAAQ;IACRC,SACE,sFACA,oFACA;IACFC,SAAS;IACTC,QAAQ;IACRC,WAAW;IACXC,QAAQ;AACV,EAAU;AAaV,OAAO,SAASC,wBAAwBC,KAAmC;QAiBzDhB;IAhBhB,MAAM,EAAEiB,IAAI,EAAEC,OAAO,EAAEC,KAAK,EAAEC,MAAM,EAAEC,IAAI,EAAEC,UAAU,EAAE,GAAGN;IAC3D,MAAMO,OAAOnB;IACb,MAAMoB,OAAOxC,eAAe;IAC5B,MAAM,CAACyC,OAAOC,SAAS,GAAG5B,SAAS;IACnC,MAAM,CAACe,WAAWc,aAAa,GAAG7B,SAAS;IAC3C,MAAM8B,MAAM3B,YAAYoB,MAAME,KAAKT,MAAM;IACzC,MAAM,EAAEe,GAAG,EAAEC,QAAQ,EAAEC,OAAO,EAAEC,MAAM,EAAEC,KAAK,EAAE,GAAGL;IAElD,2EAA2E;IAC3E,+EAA+E;IAC/E/B,UAAU;QACRoC;QACAN,aAAa;IACf,GAAG;QAACV;QAAMgB;KAAM;IAEhB,MAAMC,QAAQL,CAAAA,uBAAAA,IAAKM,MAAM,MAAK,SAASN,IAAIO,OAAO,CAACC,IAAI,CAAC,CAACC,SAAWA,OAAOC,QAAQ,KAAK,cAAcC;IACtG,MAAMC,WAAUzC,gBAAAA,aAAa6B,KAAKN,KAAKT,MAAM,aAA7Bd,gBAAmC6B,CAAAA,uBAAAA,IAAKM,MAAM,MAAK,UAAU,CAACD,QAAQX,KAAKT,MAAM,GAAG;IACpG,MAAM4B,SAAS,CAACb,OAAOc,QAAQX;IAE/B,MAAMY,QAAQ;QACZ,MAAMC,UAAUpB,MAAMqB,IAAI;QAC1B,IAAI,CAAC3B,SAAS,CAAC0B,SAAS;QACxBlB,aAAa;QACb,IAAI,MAAMC,IAAIgB,KAAK,CAAC;YAAEzB;YAAOC;YAAQ2B,MAAM;YAAYtB,OAAOoB;YAASG,QAAQ;gBAAEC,MAAM;YAAQ;QAAE,IAAI;YACnGvB,SAAS;QACX;IACF;IAEA,MAAMd,SAAS;QACb,IAAI,CAACsB,OAAO;QACZ,IAAIZ,WAAWY,MAAMgB,EAAE,GAAGhC;aACrBS,aAAa;IACpB;IAEA,qBACE,MAACtC;QAAO4B,MAAMA;QAAMC,SAASY,WAAWU,YAAYtB;QAASiC,SAAS;QAACC,UAAS;;0BAC9E,KAAC5D;0BAAa+B,KAAKlB,KAAK;;0BACxB,KAACd;0BACEmD,uBACC,MAAChD;oBAAM2D,SAAS;oBAAGC,IAAI;wBAAEC,IAAI;oBAAE;;sCAC7B,KAAC5D;4BACC6D,OAAOjC,KAAKjB,UAAU;4BACtBC,aAAagB,KAAKhB,WAAW;4BAC7BkD,SAAS;4BACTC,SAAS;4BACTC,OAAOlC;4BACPmC,UAAU,CAACC,QAAUnC,SAASmC,MAAMC,MAAM,CAACH,KAAK,CAACI,KAAK,CAAC,GAAG5D;4BAC1D6D,YAAY,GAAGvC,MAAMwC,MAAM,CAACC,cAAc,CAAC,SAAS,GAAG,EAAE/D,gBAAgB+D,cAAc,CAAC,UAAU;4BAClGC,UAAUrC;4BACVsC,SAAS;;sCAEX,MAACxE;4BAAWyE,SAAQ;4BAAQC,OAAM;;gCAC/B/C,KAAKf,IAAI;gCAAE;8CACZ,KAACf;oCAAK8E,MAAM/C,KAAK+C,IAAI;oCAAET,QAAO;oCAASU,KAAI;oCAAWnE,OAAOmB,KAAKiD,OAAO;8CACtE;;;;wBAGJzC,uBAAS,KAAC9C;4BAAMwF,UAAS;sCAAW1C;6BAAkB;;mCAGzD,MAACtC;oBAAM2D,SAAS;oBAAGC,IAAI;wBAAEC,IAAI;oBAAE;;wBAC5BxB,WAAWF,oBACV,KAAC3C;4BACCwF,UAAS;4BACTC,oBAAM,KAACvF;gCAAiBwF,MAAM;gCAAIC,cAAW;;4BAC7CC,QACE,6DAA6D;4BAC7D,2BAA2B;0CAC3B,KAAC3F;gCACCmF,OAAM;gCACNM,MAAK;gCACLP,SAAQ;gCACRU,SAAS;oCACPhF,WAAW;wCAAEiF,OAAOnD,IAAIqB,EAAE;oCAAC;oCAC3BhC;gCACF;0CAEC;;sCAIJK,KAAKb,OAAO;6BAEb;wBACHwB,sBACC,MAAChD;4BAAMwF,UAAS;;8CACd,KAAC9E;oCAAWyE,SAAQ;oCAAQf,IAAI;wCAAE2B,YAAY;oCAAI;8CAC/C,GAAG1D,KAAKZ,OAAO,CAAC,EAAE,EAAEuB,MAAMsB,KAAK,EAAE;;gCAEnCtB,MAAMgD,IAAI,iBAAG,KAACtF;oCAAWyE,SAAQ;8CAASnC,MAAMgD,IAAI;qCAAiB;;6BAEtE;wBACHzC,wBAAU,KAACvD;4BAAMwF,UAAU7C,CAAAA,uBAAAA,IAAKM,MAAM,MAAK,WAAW,UAAU;sCAAYM;6BAAmB;wBAC/F5B,0BAAY,KAAC3B;4BAAMwF,UAAS;sCAAQnD,KAAKV,SAAS;6BAAY;;;;0BAIrE,MAACvB;;kCACC,KAACH;wBAAO4F,SAAS7D;wBAASiD,UAAUrC;kCACjCY,SAAS,WAAW;;oBAEtBA,uBACC,KAACvD;wBACCkF,SAAQ;wBACRU,SAAS,IAAM,KAAKnC;wBACpBuB,UAAUrC,YAAY,CAACX,SAAS,CAACM,MAAMqB,IAAI;kCAE1CvB,KAAKd,MAAM;yBAEZyB,sBACF,KAAC/C;wBAAOkF,SAAQ;wBAAYU,SAASnE;kCAClCW,KAAKX,MAAM;yBAEZ;;;;;AAIZ;AAEA;;;CAGC,GACD,OAAO,SAASuE,2BAA2B,EAAE/D,MAAM,EAAED,KAAK,EAAEG,UAAU,EAAmC;IACvG,MAAM,EAAE8D,MAAM/D,IAAI,EAAE,GAAGpC;IACvB,MAAMoG,UAAUnF,iBAAiBmB,MAAMF;IACvC,MAAM,CAACF,MAAMqE,QAAQ,GAAGxF,SAAS;IAEjC,IAAIuF,YAAY,SAAS,OAAO;IAChC,qBACE;;0BACE,KAAClG;gBAAOyF,MAAK;gBAAQP,SAAQ;gBAAWU,SAAS,IAAMO,QAAQ;0BAC5D;;0BAEH,KAACvE;gBACCE,MAAMA;gBACNC,SAAS,IAAMoE,QAAQ;gBACvBnE,OAAOA;gBACPC,QAAQA;gBACRC,MAAMA;gBACNC,YAAYA;;;;AAIpB;AAEA,eAAe6D,2BAA0B"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-describe-automation.component.tsx"],"sourcesContent":["'use client'\n\n/**\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 { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport type { ConsoleHostAutomationsZoneProps } from './ai-automation-zones'\nimport { mdiCreation } from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Button,\n CircularProgress,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Link,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useState } from 'react'\nimport { openAiJobs } from './ai-jobs-store'\nimport { aiJobProblem, useAiJobRun, useAiJobsVerdict } from './use-ai-job-run'\n\n/**\n * \"Create with AI\" on the Automation page's Actions (AGL-2919), beside Add action\n * and Recipes through the `hostAutomations` zone: a description becomes a\n * `workflow` job that drafts one automation, switched off.\n *\n * The dialog follows the shared brief dialog's shape — one text box, what\n * happens next, and the action that starts the job — and then follows the\n * job, because an automation's draft needs no plan to confirm: when it is\n * written, the dialog says what to fill in and opens it in the Actions editor.\n */\n\n/** The longest description a job admits. */\nconst BRIEF_MAX_CHARS = 4_000\n\nexport const AI_AUTOMATION_BRIEF_COPY = {\n title: 'Describe an automation',\n briefLabel: 'What should happen, and when?',\n placeholder:\n 'When someone submits the newsletter form, add them to the newsletter list, make them ' +\n 'a lead and send them a welcome email',\n next:\n 'The automation is drafted switched off, using only the triggers and steps your plan ' +\n 'includes. Anything it needs that your site does not have yet is left for you to fill in.',\n submit: 'Draft the automation',\n started:\n 'The automation is being drafted. This can take a few minutes. You can close this ' +\n 'window: the draft appears in the list, switched off, when it is ready. Open AI ' +\n 'jobs to follow it meanwhile.',\n drafted: 'Drafted',\n review: 'Review it',\n notListed: 'The draft is saved. It appears in the list in a moment.',\n failed: 'The automation could not be drafted. Try again.',\n} as const\n\nexport interface AiAutomationBriefDialogProps {\n open: boolean\n onClose: () => void\n orgId: string | undefined\n hostId: string\n /** Who is signed in, whose token the requests carry. */\n user: Parameters<typeof useAiJobRun>[0]\n /** Opens a listed action in the Actions editor; `false` when the list has not read it yet. */\n openAction: (actionId: string) => boolean\n}\n\nexport function AiAutomationBriefDialog(props: AiAutomationBriefDialogProps) {\n const { open, onClose, orgId, hostId, user, openAction } = props\n const copy = AI_AUTOMATION_BRIEF_COPY\n const help = pluginDocsHelp('aiAutomations')\n const [brief, setBrief] = useState('')\n const [notListed, setNotListed] = useState(false)\n const run = useAiJobRun(user, copy.failed)\n const { job, starting, running, notice, reset } = run\n\n // Opening or closing starts over and stops following: a job still drafting\n // carries on in AI jobs, and its draft appears in the list when it is written.\n useEffect(() => {\n reset()\n setNotListed(false)\n }, [open, reset])\n\n const draft = job?.status === 'done' ? job.outputs.find((output) => output.resource === 'workflow') : undefined\n const problem = aiJobProblem(job, copy.failed) ?? (job?.status === 'done' && !draft ? copy.failed : null)\n const asking = !job || Boolean(notice)\n\n const start = async () => {\n const trimmed = brief.trim()\n if (!orgId || !trimmed) return\n setNotListed(false)\n if (await run.start({ orgId, hostId, kind: 'workflow', brief: trimmed, inputs: { mode: 'draft' } })) {\n setBrief('')\n }\n }\n\n const review = () => {\n if (!draft) return\n if (openAction(draft.id)) onClose()\n else setNotListed(true)\n }\n\n return (\n <Dialog open={open} onClose={starting ? undefined : onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{copy.title}</DialogTitle>\n <DialogContent>\n {asking ? (\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n label={copy.briefLabel}\n placeholder={copy.placeholder}\n multiline\n minRows={4}\n value={brief}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n helperText={`${brief.length.toLocaleString('en-US')} / ${BRIEF_MAX_CHARS.toLocaleString('en-US')}`}\n disabled={starting}\n autoFocus\n />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {copy.next}{' '}\n <Link href={help.href} target=\"_blank\" rel=\"noopener\" title={help.excerpt}>\n {'How it works'}\n </Link>\n </Typography>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n </Stack>\n ) : (\n <Stack spacing={2} sx={{ pt: 1 }}>\n {running && job ? (\n <Alert\n severity=\"info\"\n icon={<CircularProgress size={18} aria-label=\"Drafting\" />}\n action={\n // Follows the job in the Assist panel (AGL-3593); the dialog\n // covers it, so it closes.\n <Button\n color=\"inherit\"\n size=\"small\"\n variant=\"outlined\"\n onClick={() => {\n openAiJobs({ jobId: job.id })\n onClose()\n }}\n >\n {'Open AI jobs'}\n </Button>\n }\n >\n {copy.started}\n </Alert>\n ) : null}\n {draft ? (\n <Alert severity=\"success\">\n <Typography variant=\"body2\" sx={{ fontWeight: 600 }}>\n {`${copy.drafted}: ${draft.label}`}\n </Typography>\n {draft.note ? <Typography variant=\"body2\">{draft.note}</Typography> : null}\n </Alert>\n ) : null}\n {problem ? <Alert severity={job?.status === 'failed' ? 'error' : 'warning'}>{problem}</Alert> : null}\n {notListed ? <Alert severity=\"info\">{copy.notListed}</Alert> : null}\n </Stack>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={starting}>\n {asking ? 'Cancel' : 'Close'}\n </Button>\n {asking ? (\n <Button\n variant=\"contained\"\n onClick={() => void start()}\n disabled={starting || !orgId || !brief.trim()}\n >\n {copy.submit}\n </Button>\n ) : draft ? (\n <Button variant=\"contained\" onClick={review}>\n {copy.review}\n </Button>\n ) : null}\n </DialogActions>\n </Dialog>\n )\n}\n\n/**\n * Renders nothing until the jobs route has answered for this workspace, and\n * nothing when it says the feature is not this workspace's.\n */\nexport function AiDescribeAutomationButton({ hostId, orgId, openAction }: ConsoleHostAutomationsZoneProps) {\n const { data: user } = useUser()\n const verdict = useAiJobsVerdict(user, orgId)\n const [open, setOpen] = useState(false)\n\n if (verdict !== 'ready') return null\n return (\n <>\n <Button\n size=\"small\"\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiCreation.path} />}\n onClick={() => setOpen(true)}\n >\n {'Create with AI'}\n </Button>\n <AiAutomationBriefDialog\n open={open}\n onClose={() => setOpen(false)}\n orgId={orgId}\n hostId={hostId}\n user={user}\n openAction={openAction}\n />\n </>\n )\n}\n\nexport default AiDescribeAutomationButton\n"],"names":["pluginDocsHelp","mdiCreation","MdiIcon","useUser","Alert","Button","CircularProgress","Dialog","DialogActions","DialogContent","DialogTitle","Link","Stack","TextField","Typography","useEffect","useState","openAiJobs","aiJobProblem","useAiJobRun","useAiJobsVerdict","BRIEF_MAX_CHARS","AI_AUTOMATION_BRIEF_COPY","title","briefLabel","placeholder","next","submit","started","drafted","review","notListed","failed","AiAutomationBriefDialog","props","open","onClose","orgId","hostId","user","openAction","copy","help","brief","setBrief","setNotListed","run","job","starting","running","notice","reset","draft","status","outputs","find","output","resource","undefined","problem","asking","Boolean","start","trimmed","trim","kind","inputs","mode","id","fullWidth","maxWidth","spacing","sx","pt","label","multiline","minRows","value","onChange","event","target","slice","helperText","length","toLocaleString","disabled","autoFocus","variant","color","href","rel","excerpt","severity","icon","size","aria-label","action","onClick","jobId","fontWeight","note","AiDescribeAutomationButton","data","verdict","setOpen","startIcon","path"],"mappings":"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"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
3
3
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
4
|
+
import { mdiCreation } from "@aglyn/shared-data-mdi";
|
|
5
|
+
import { MdiIcon } from "@aglyn/shared-ui-jsx";
|
|
4
6
|
import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
5
7
|
import { useUser } from "@aglyn/tenant-feature-instance";
|
|
6
8
|
import { Button } from "@mui/material";
|
|
@@ -48,8 +50,11 @@ import { AiBriefDialog } from "./ai-brief-dialog.component.js";
|
|
|
48
50
|
/*#__PURE__*/ _jsx(Button, {
|
|
49
51
|
size: "small",
|
|
50
52
|
variant: "outlined",
|
|
53
|
+
startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
54
|
+
path: mdiCreation.path
|
|
55
|
+
}),
|
|
51
56
|
onClick: ()=>setOpen(true),
|
|
52
|
-
children: '
|
|
57
|
+
children: 'Create with AI'
|
|
53
58
|
}),
|
|
54
59
|
/*#__PURE__*/ _jsx(AiBriefDialog, {
|
|
55
60
|
kind: kind,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-describe-button.component.tsx"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n ConsoleHostComponentsZoneProps,\n ConsoleHostLayoutsZoneProps,\n ConsoleHostScreensZoneProps,\n ConsoleHostTemplatesZoneProps,\n} from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { Button } from '@mui/material'\nimport { useEffect, useRef, useState } from 'react'\nimport { AiBriefDialog, type AiBriefKind } from './ai-brief-dialog.component'\n\n/**\n * \"
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-describe-button.component.tsx"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n ConsoleHostComponentsZoneProps,\n ConsoleHostLayoutsZoneProps,\n ConsoleHostScreensZoneProps,\n ConsoleHostTemplatesZoneProps,\n} from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { mdiCreation } from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { Button } from '@mui/material'\nimport { useEffect, useRef, useState } from 'react'\nimport { AiBriefDialog, type AiBriefKind } from './ai-brief-dialog.component'\n\n/**\n * \"Create with AI\" (AGL-2907, AGL-3043, AGL-3051): a job from a brief, beside the\n * create actions of the page that lists what the job makes — a page on\n * Screens, a page template on Templates, a layout on Layouts, a form on Forms\n * and a reusable component on Components — each mounted through that page's\n * zone. The Assist panel's AI jobs opens the same dialog for a page.\n */\n\ntype Verdict = 'checking' | 'ready' | 'hidden'\n\nexport interface AiDescribeButtonProps extends ConsoleHostScreensZoneProps {\n /** The job the brief starts: what the page the button sits on lists. */\n kind: AiBriefKind\n}\n\n/**\n * Renders nothing until the jobs route has answered for this workspace: the\n * shell decided the plan, the member's permission and the site's AI switch\n * before mounting it, and the release flag is the route's to decide, so a 404\n * or a 403 there is this control staying absent. A lockdown is the start\n * door's to say, in the dialog, in its own words.\n */\nexport function AiDescribeButton({ kind, hostId, orgId }: AiDescribeButtonProps) {\n const { data: user } = useUser()\n // Held in a ref so the probe keys on WHO is signed in, never on the\n // identity of the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const uid = user?.uid ?? null\n const [verdict, setVerdict] = useState<Verdict>('checking')\n const [open, setOpen] = useState(false)\n\n useEffect(() => {\n if (!orgId || !uid) return\n let active = true\n void (async () => {\n try {\n const response = await authorizedFetch(\n userRef.current,\n `/api/ai/jobs?orgId=${encodeURIComponent(orgId)}&limit=1`,\n )\n if (active) {\n setVerdict(response.status === 404 || response.status === 403 ? 'hidden' : 'ready')\n }\n } catch {\n if (active) setVerdict('hidden')\n }\n })()\n return () => {\n active = false\n }\n }, [orgId, uid])\n\n if (verdict !== 'ready') return null\n return (\n <>\n <Button\n size=\"small\"\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiCreation.path} />}\n onClick={() => setOpen(true)}\n >\n {'Create with AI'}\n </Button>\n <AiBriefDialog\n kind={kind}\n open={open}\n onClose={() => setOpen(false)}\n orgId={orgId}\n hostId={hostId}\n user={user}\n />\n </>\n )\n}\n\n/** A page template from a brief, on the Templates page (`hostTemplates`). */\nexport function AiDescribeTemplateButton(props: ConsoleHostTemplatesZoneProps) {\n return <AiDescribeButton {...props} kind=\"template\" />\n}\n\n/** A layout from a brief, on the Layouts page (`hostLayouts`). */\nexport function AiDescribeLayoutButton(props: ConsoleHostLayoutsZoneProps) {\n return <AiDescribeButton {...props} kind=\"layout\" />\n}\n\n/**\n * A form from a brief, on the Forms page (`hostForms`, which the forms plugin\n * hosts on the `hostScreens` contract).\n */\nexport function AiDescribeFormButton(props: ConsoleHostScreensZoneProps) {\n return <AiDescribeButton {...props} kind=\"form\" />\n}\n\n/** A reusable component from a brief, on the Components page (`hostComponents`). */\nexport function AiDescribeComponentButton(props: ConsoleHostComponentsZoneProps) {\n return <AiDescribeButton {...props} kind=\"component\" />\n}\n\nexport default AiDescribeButton\n"],"names":["mdiCreation","MdiIcon","authorizedFetch","useUser","Button","useEffect","useRef","useState","AiBriefDialog","AiDescribeButton","kind","hostId","orgId","data","user","userRef","current","uid","verdict","setVerdict","open","setOpen","active","response","encodeURIComponent","status","size","variant","startIcon","path","onClick","onClose","AiDescribeTemplateButton","props","AiDescribeLayoutButton","AiDescribeFormButton","AiDescribeComponentButton"],"mappings":"AAAA;;;AAyBA,SAASA,WAAW,QAAQ,yBAAwB;AACpD,SAASC,OAAO,QAAQ,uBAAsB;AAC9C,SAASC,eAAe,QAAQ,2CAA0C;AAC1E,SAASC,OAAO,QAAQ,iCAAgC;AACxD,SAASC,MAAM,QAAQ,gBAAe;AACtC,SAASC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAO;AACnD,SAASC,aAAa,QAA0B,iCAA6B;AAiB7E;;;;;;CAMC,GACD,OAAO,SAASC,iBAAiB,EAAEC,IAAI,EAAEC,MAAM,EAAEC,KAAK,EAAyB;;IAC7E,MAAM,EAAEC,MAAMC,IAAI,EAAE,GAAGX;IACvB,oEAAoE;IACpE,uCAAuC;IACvC,MAAMY,UAAUT,OAAOQ;IACvBC,QAAQC,OAAO,GAAGF;IAClB,MAAMG,cAAMH,wBAAAA,KAAMG,GAAG,mBAAI;IACzB,MAAM,CAACC,SAASC,WAAW,GAAGZ,SAAkB;IAChD,MAAM,CAACa,MAAMC,QAAQ,GAAGd,SAAS;IAEjCF,UAAU;QACR,IAAI,CAACO,SAAS,CAACK,KAAK;QACpB,IAAIK,SAAS;QACb,KAAK,AAAC,CAAA;YACJ,IAAI;gBACF,MAAMC,WAAW,MAAMrB,gBACrBa,QAAQC,OAAO,EACf,CAAC,mBAAmB,EAAEQ,mBAAmBZ,OAAO,QAAQ,CAAC;gBAE3D,IAAIU,QAAQ;oBACVH,WAAWI,SAASE,MAAM,KAAK,OAAOF,SAASE,MAAM,KAAK,MAAM,WAAW;gBAC7E;YACF,EAAE,eAAM;gBACN,IAAIH,QAAQH,WAAW;YACzB;QACF,CAAA;QACA,OAAO;YACLG,SAAS;QACX;IACF,GAAG;QAACV;QAAOK;KAAI;IAEf,IAAIC,YAAY,SAAS,OAAO;IAChC,qBACE;;0BACE,KAACd;gBACCsB,MAAK;gBACLC,SAAQ;gBACRC,yBAAW,KAAC3B;oBAAQ4B,MAAM7B,YAAY6B,IAAI;;gBAC1CC,SAAS,IAAMT,QAAQ;0BAEtB;;0BAEH,KAACb;gBACCE,MAAMA;gBACNU,MAAMA;gBACNW,SAAS,IAAMV,QAAQ;gBACvBT,OAAOA;gBACPD,QAAQA;gBACRG,MAAMA;;;;AAId;AAEA,2EAA2E,GAC3E,OAAO,SAASkB,yBAAyBC,KAAoC;IAC3E,qBAAO,KAACxB,+BAAqBwB;QAAOvB,MAAK;;AAC3C;AAEA,gEAAgE,GAChE,OAAO,SAASwB,uBAAuBD,KAAkC;IACvE,qBAAO,KAACxB,+BAAqBwB;QAAOvB,MAAK;;AAC3C;AAEA;;;CAGC,GACD,OAAO,SAASyB,qBAAqBF,KAAkC;IACrE,qBAAO,KAACxB,+BAAqBwB;QAAOvB,MAAK;;AAC3C;AAEA,kFAAkF,GAClF,OAAO,SAAS0B,0BAA0BH,KAAqC;IAC7E,qBAAO,KAACxB,+BAAqBwB;QAAOvB,MAAK;;AAC3C;AAEA,eAAeD,iBAAgB"}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import type { ConsoleHostScreensZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins';
|
|
18
18
|
/**
|
|
19
|
-
* "
|
|
19
|
+
* "Create with AI" (AGL-2907): a page from a brief, beside Templates and Create
|
|
20
20
|
* New Screen on a site's Screens page, mounted through the `hostScreens`
|
|
21
21
|
* zone. The Assist panel's AI jobs opens the same dialog. The button, its
|
|
22
22
|
* probe and its dialog are the ones every describe entry shares
|
|
@@ -3,7 +3,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
3
3
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
4
4
|
import { AiDescribeButton } from "./ai-describe-button.component.js";
|
|
5
5
|
/**
|
|
6
|
-
* "
|
|
6
|
+
* "Create with AI" (AGL-2907): a page from a brief, beside Templates and Create
|
|
7
7
|
* New Screen on a site's Screens page, mounted through the `hostScreens`
|
|
8
8
|
* zone. The Assist panel's AI jobs opens the same dialog. The button, its
|
|
9
9
|
* probe and its dialog are the ones every describe entry shares
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-describe-page.component.tsx"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ConsoleHostScreensZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { AiDescribeButton } from './ai-describe-button.component'\n\n/**\n * \"
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-describe-page.component.tsx"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ConsoleHostScreensZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { AiDescribeButton } from './ai-describe-button.component'\n\n/**\n * \"Create with AI\" (AGL-2907): a page from a brief, beside Templates and Create\n * New Screen on a site's Screens page, mounted through the `hostScreens`\n * zone. The Assist panel's AI jobs opens the same dialog. The button, its\n * probe and its dialog are the ones every describe entry shares\n * (`ai-describe-button.component.tsx`, AGL-3043).\n */\nexport function AiDescribePageButton(props: ConsoleHostScreensZoneProps) {\n return <AiDescribeButton {...props} kind=\"page\" />\n}\n\nexport default AiDescribePageButton\n"],"names":["AiDescribeButton","AiDescribePageButton","props","kind"],"mappings":"AAAA;;;AAoBA,SAASA,gBAAgB,QAAQ,oCAAgC;AAEjE;;;;;;CAMC,GACD,OAAO,SAASC,qBAAqBC,KAAkC;IACrE,qBAAO,KAACF,+BAAqBE;QAAOC,MAAK;;AAC3C;AAEA,eAAeF,qBAAoB"}
|
|
@@ -5,10 +5,16 @@ import type { AiJobSummary } from '../model/ai-jobs.types';
|
|
|
5
5
|
* every panel that watches a job through the events route.
|
|
6
6
|
*/
|
|
7
7
|
export declare function readEventFrames(body: ReadableStream<Uint8Array>, onEvent: (event: Record<string, unknown>) => void): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* How a follow ended (AGL-3596): the stream ran its course (`ok`), the route
|
|
10
|
+
* answered that the job is not this reader's to see or does not exist
|
|
11
|
+
* (`not-found`), or the route could not be reached or failed (`error`).
|
|
12
|
+
*/
|
|
13
|
+
export type AiJobFollowEnd = 'ok' | 'not-found' | 'error';
|
|
8
14
|
/**
|
|
9
15
|
* Follows one job through the events route until it settles, the signal
|
|
10
16
|
* aborts or the route stops answering, handing each state to `onJob` and to
|
|
11
17
|
* every surface counting the workspace's jobs (AGL-3593). The server closes a
|
|
12
18
|
* stream at its deadline with `reconnect`; this re-opens it.
|
|
13
19
|
*/
|
|
14
|
-
export declare function followAiJobEvents(user: () => MaybeTokenSource, orgId: string, jobId: string, signal: AbortSignal, onJob: (job: AiJobSummary) => void): Promise<
|
|
20
|
+
export declare function followAiJobEvents(user: () => MaybeTokenSource, orgId: string, jobId: string, signal: AbortSignal, onJob: (job: AiJobSummary) => void): Promise<AiJobFollowEnd>;
|
|
@@ -58,7 +58,8 @@ import { publishAiJob } from "./ai-jobs-store.js";
|
|
|
58
58
|
const response = await authorizedFetch(user(), `/api/ai/jobs/${encodeURIComponent(jobId)}/events?orgId=${encodeURIComponent(orgId)}`, {
|
|
59
59
|
signal
|
|
60
60
|
});
|
|
61
|
-
if (
|
|
61
|
+
if (response.status === 404 || response.status === 403) return 'not-found';
|
|
62
|
+
if (!response.ok || !response.body) return 'error';
|
|
62
63
|
await readEventFrames(response.body, (event)=>{
|
|
63
64
|
if (signal.aborted) return;
|
|
64
65
|
if (event['type'] === 'state') {
|
|
@@ -68,9 +69,10 @@ import { publishAiJob } from "./ai-jobs-store.js";
|
|
|
68
69
|
} else if (event['type'] === 'reconnect') again = true;
|
|
69
70
|
});
|
|
70
71
|
} catch (unused) {
|
|
71
|
-
return;
|
|
72
|
+
return 'error';
|
|
72
73
|
}
|
|
73
74
|
}
|
|
75
|
+
return 'ok';
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
//# sourceMappingURL=ai-job-events.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-events.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 type { MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport type { AiJobSummary } from '../model/ai-jobs.types'\nimport { publishAiJob } from './ai-jobs-store'\n\n// Reading a job's events stream (AGL-2904), shared by the AI jobs drawer and\n// every dialog that follows the job it started (AGL-3593).\n\n/**\n * `data:` frames out of an SSE body, one parsed event per frame. Shared with\n * every panel that watches a job through the events route.\n */\nexport async function readEventFrames(\n body: ReadableStream<Uint8Array>,\n onEvent: (event: Record<string, unknown>) => void,\n): Promise<void> {\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n buffer += decoder.decode(value, { stream: true })\n const frames = buffer.split('\\n\\n')\n buffer = frames.pop() ?? ''\n for (const frame of frames) {\n const line = frame.split('\\n').find((part) => part.startsWith('data: '))\n if (!line) continue\n try {\n onEvent(JSON.parse(line.slice('data: '.length)))\n } catch {\n // A torn frame is dropped; the next state event carries the whole job.\n }\n }\n }\n}\n\n/**\n * Follows one job through the events route until it settles, the signal\n * aborts or the route stops answering, handing each state to `onJob` and to\n * every surface counting the workspace's jobs (AGL-3593). The server closes a\n * stream at its deadline with `reconnect`; this re-opens it.\n */\nexport async function followAiJobEvents(\n user: () => MaybeTokenSource,\n orgId: string,\n jobId: string,\n signal: AbortSignal,\n onJob: (job: AiJobSummary) => void,\n): Promise<
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-events.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 type { MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport type { AiJobSummary } from '../model/ai-jobs.types'\nimport { publishAiJob } from './ai-jobs-store'\n\n// Reading a job's events stream (AGL-2904), shared by the AI jobs drawer and\n// every dialog that follows the job it started (AGL-3593).\n\n/**\n * `data:` frames out of an SSE body, one parsed event per frame. Shared with\n * every panel that watches a job through the events route.\n */\nexport async function readEventFrames(\n body: ReadableStream<Uint8Array>,\n onEvent: (event: Record<string, unknown>) => void,\n): Promise<void> {\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n buffer += decoder.decode(value, { stream: true })\n const frames = buffer.split('\\n\\n')\n buffer = frames.pop() ?? ''\n for (const frame of frames) {\n const line = frame.split('\\n').find((part) => part.startsWith('data: '))\n if (!line) continue\n try {\n onEvent(JSON.parse(line.slice('data: '.length)))\n } catch {\n // A torn frame is dropped; the next state event carries the whole job.\n }\n }\n }\n}\n\n/**\n * How a follow ended (AGL-3596): the stream ran its course (`ok`), the route\n * answered that the job is not this reader's to see or does not exist\n * (`not-found`), or the route could not be reached or failed (`error`).\n */\nexport type AiJobFollowEnd = 'ok' | 'not-found' | 'error'\n\n/**\n * Follows one job through the events route until it settles, the signal\n * aborts or the route stops answering, handing each state to `onJob` and to\n * every surface counting the workspace's jobs (AGL-3593). The server closes a\n * stream at its deadline with `reconnect`; this re-opens it.\n */\nexport async function followAiJobEvents(\n user: () => MaybeTokenSource,\n orgId: string,\n jobId: string,\n signal: AbortSignal,\n onJob: (job: AiJobSummary) => void,\n): Promise<AiJobFollowEnd> {\n let again = true\n while (again && !signal.aborted) {\n again = false\n try {\n const response = await authorizedFetch(\n user(),\n `/api/ai/jobs/${encodeURIComponent(jobId)}/events?orgId=${encodeURIComponent(orgId)}`,\n { signal },\n )\n if (response.status === 404 || response.status === 403) return 'not-found'\n if (!response.ok || !response.body) return 'error'\n await readEventFrames(response.body, (event) => {\n if (signal.aborted) return\n if (event['type'] === 'state') {\n const next = event['job'] as AiJobSummary\n publishAiJob(next)\n onJob(next)\n } else if (event['type'] === 'reconnect') again = true\n })\n } catch {\n return 'error'\n }\n }\n return 'ok'\n}\n"],"names":["authorizedFetch","publishAiJob","readEventFrames","body","onEvent","reader","getReader","decoder","TextDecoder","buffer","frames","done","value","read","decode","stream","split","pop","frame","line","find","part","startsWith","JSON","parse","slice","length","followAiJobEvents","user","orgId","jobId","signal","onJob","again","aborted","response","encodeURIComponent","status","ok","event","next"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;AAGA,SAASA,eAAe,QAAQ,2CAA0C;AAE1E,SAASC,YAAY,QAAQ,qBAAiB;AAE9C,6EAA6E;AAC7E,2DAA2D;AAE3D;;;CAGC,GACD,OAAO,eAAeC,gBACpBC,IAAgC,EAChCC,OAAiD;IAEjD,MAAMC,SAASF,KAAKG,SAAS;IAC7B,MAAMC,UAAU,IAAIC;IACpB,IAAIC,SAAS;IACb,OAAS;YAKEC;QAJT,MAAM,EAAEC,IAAI,EAAEC,KAAK,EAAE,GAAG,MAAMP,OAAOQ,IAAI;QACzC,IAAIF,MAAM;QACVF,UAAUF,QAAQO,MAAM,CAACF,OAAO;YAAEG,QAAQ;QAAK;QAC/C,MAAML,SAASD,OAAOO,KAAK,CAAC;QAC5BP,UAASC,cAAAA,OAAOO,GAAG,cAAVP,cAAgB;QACzB,KAAK,MAAMQ,SAASR,OAAQ;YAC1B,MAAMS,OAAOD,MAAMF,KAAK,CAAC,MAAMI,IAAI,CAAC,CAACC,OAASA,KAAKC,UAAU,CAAC;YAC9D,IAAI,CAACH,MAAM;YACX,IAAI;gBACFf,QAAQmB,KAAKC,KAAK,CAACL,KAAKM,KAAK,CAAC,SAASC,MAAM;YAC/C,EAAE,eAAM;YACN,uEAAuE;YACzE;QACF;IACF;AACF;AASA;;;;;CAKC,GACD,OAAO,eAAeC,kBACpBC,IAA4B,EAC5BC,KAAa,EACbC,KAAa,EACbC,MAAmB,EACnBC,KAAkC;IAElC,IAAIC,QAAQ;IACZ,MAAOA,SAAS,CAACF,OAAOG,OAAO,CAAE;QAC/BD,QAAQ;QACR,IAAI;YACF,MAAME,WAAW,MAAMnC,gBACrB4B,QACA,CAAC,aAAa,EAAEQ,mBAAmBN,OAAO,cAAc,EAAEM,mBAAmBP,QAAQ,EACrF;gBAAEE;YAAO;YAEX,IAAII,SAASE,MAAM,KAAK,OAAOF,SAASE,MAAM,KAAK,KAAK,OAAO;YAC/D,IAAI,CAACF,SAASG,EAAE,IAAI,CAACH,SAAShC,IAAI,EAAE,OAAO;YAC3C,MAAMD,gBAAgBiC,SAAShC,IAAI,EAAE,CAACoC;gBACpC,IAAIR,OAAOG,OAAO,EAAE;gBACpB,IAAIK,KAAK,CAAC,OAAO,KAAK,SAAS;oBAC7B,MAAMC,OAAOD,KAAK,CAAC,MAAM;oBACzBtC,aAAauC;oBACbR,MAAMQ;gBACR,OAAO,IAAID,KAAK,CAAC,OAAO,KAAK,aAAaN,QAAQ;YACpD;QACF,EAAE,eAAM;YACN,OAAO;QACT;IACF;IACA,OAAO;AACT"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The site a site's AI jobs pages are about (AGL-3596): its workspace, which
|
|
3
|
+
* every jobs request names, and its display name. Read once off the site
|
|
4
|
+
* document the reader may already read.
|
|
5
|
+
*
|
|
6
|
+
* It always settles. A read that fails, a document without a workspace, or a
|
|
7
|
+
* read still out after {@link AI_JOB_SITE_TIMEOUT_MS} is `error`, so a page
|
|
8
|
+
* waiting on it says so instead of spinning: without a workspace there is no
|
|
9
|
+
* job to ask for, and nothing would ever arrive to end the wait.
|
|
10
|
+
*/
|
|
11
|
+
export type AiJobSite = {
|
|
12
|
+
status: 'loading';
|
|
13
|
+
} | {
|
|
14
|
+
status: 'ready';
|
|
15
|
+
orgId: string;
|
|
16
|
+
name: string;
|
|
17
|
+
} | {
|
|
18
|
+
status: 'error';
|
|
19
|
+
};
|
|
20
|
+
/** How long the site's read may take before the page says it could not be read. */
|
|
21
|
+
export declare const AI_JOB_SITE_TIMEOUT_MS = 15000;
|
|
22
|
+
export declare function useAiJobSite(hostId: string | null | undefined): AiJobSite;
|
|
@@ -0,0 +1,73 @@
|
|
|
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 { useFirestore } from "@aglyn/tenant-feature-instance";
|
|
18
|
+
import { doc, getDoc } from "firebase/firestore";
|
|
19
|
+
import { useEffect, useRef, useState } from "react";
|
|
20
|
+
/** How long the site's read may take before the page says it could not be read. */ export const AI_JOB_SITE_TIMEOUT_MS = 15000;
|
|
21
|
+
export function useAiJobSite(hostId) {
|
|
22
|
+
// Read through a ref: the read is keyed on the site alone, so a provider
|
|
23
|
+
// that hands back a new handle each render does not read it again.
|
|
24
|
+
const firestore = useFirestore();
|
|
25
|
+
const firestoreRef = useRef(firestore);
|
|
26
|
+
firestoreRef.current = firestore;
|
|
27
|
+
const [site, setSite] = useState({
|
|
28
|
+
status: 'loading'
|
|
29
|
+
});
|
|
30
|
+
useEffect(()=>{
|
|
31
|
+
if (!hostId) {
|
|
32
|
+
setSite({
|
|
33
|
+
status: 'error'
|
|
34
|
+
});
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
let active = true;
|
|
38
|
+
setSite((current)=>current.status === 'loading' ? current : {
|
|
39
|
+
status: 'loading'
|
|
40
|
+
});
|
|
41
|
+
const settle = (next)=>{
|
|
42
|
+
if (!active) return;
|
|
43
|
+
active = false;
|
|
44
|
+
setSite(next);
|
|
45
|
+
};
|
|
46
|
+
const timer = setTimeout(()=>settle({
|
|
47
|
+
status: 'error'
|
|
48
|
+
}), AI_JOB_SITE_TIMEOUT_MS);
|
|
49
|
+
void getDoc(doc(firestoreRef.current, 'hosts', hostId)).then((snapshot)=>{
|
|
50
|
+
var _snapshot_data;
|
|
51
|
+
const data = (_snapshot_data = snapshot.data()) != null ? _snapshot_data : {};
|
|
52
|
+
const orgId = typeof data.orgId === 'string' ? data.orgId : '';
|
|
53
|
+
settle(orgId ? {
|
|
54
|
+
status: 'ready',
|
|
55
|
+
orgId,
|
|
56
|
+
name: typeof data.displayName === 'string' ? data.displayName : ''
|
|
57
|
+
} : {
|
|
58
|
+
status: 'error'
|
|
59
|
+
});
|
|
60
|
+
}).catch(()=>settle({
|
|
61
|
+
status: 'error'
|
|
62
|
+
}));
|
|
63
|
+
return ()=>{
|
|
64
|
+
active = false;
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
};
|
|
67
|
+
}, [
|
|
68
|
+
hostId
|
|
69
|
+
]);
|
|
70
|
+
return site;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
//# sourceMappingURL=ai-job-site.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-site.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 { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { doc, getDoc } from 'firebase/firestore'\nimport { useEffect, useRef, useState } from 'react'\n\n/**\n * The site a site's AI jobs pages are about (AGL-3596): its workspace, which\n * every jobs request names, and its display name. Read once off the site\n * document the reader may already read.\n *\n * It always settles. A read that fails, a document without a workspace, or a\n * read still out after {@link AI_JOB_SITE_TIMEOUT_MS} is `error`, so a page\n * waiting on it says so instead of spinning: without a workspace there is no\n * job to ask for, and nothing would ever arrive to end the wait.\n */\nexport type AiJobSite =\n | { status: 'loading' }\n | { status: 'ready'; orgId: string; name: string }\n | { status: 'error' }\n\n/** How long the site's read may take before the page says it could not be read. */\nexport const AI_JOB_SITE_TIMEOUT_MS = 15_000\n\nexport function useAiJobSite(hostId: string | null | undefined): AiJobSite {\n // Read through a ref: the read is keyed on the site alone, so a provider\n // that hands back a new handle each render does not read it again.\n const firestore = useFirestore()\n const firestoreRef = useRef(firestore)\n firestoreRef.current = firestore\n const [site, setSite] = useState<AiJobSite>({ status: 'loading' })\n useEffect(() => {\n if (!hostId) {\n setSite({ status: 'error' })\n return undefined\n }\n let active = true\n setSite((current) => (current.status === 'loading' ? current : { status: 'loading' }))\n const settle = (next: AiJobSite) => {\n if (!active) return\n active = false\n setSite(next)\n }\n const timer = setTimeout(() => settle({ status: 'error' }), AI_JOB_SITE_TIMEOUT_MS)\n void getDoc(doc(firestoreRef.current, 'hosts', hostId))\n .then((snapshot) => {\n const data = (snapshot.data() ?? {}) as { orgId?: unknown; displayName?: unknown }\n const orgId = typeof data.orgId === 'string' ? data.orgId : ''\n settle(\n orgId\n ? { status: 'ready', orgId, name: typeof data.displayName === 'string' ? data.displayName : '' }\n : { status: 'error' },\n )\n })\n .catch(() => settle({ status: 'error' }))\n return () => {\n active = false\n clearTimeout(timer)\n }\n }, [hostId])\n return site\n}\n"],"names":["useFirestore","doc","getDoc","useEffect","useRef","useState","AI_JOB_SITE_TIMEOUT_MS","useAiJobSite","hostId","firestore","firestoreRef","current","site","setSite","status","undefined","active","settle","next","timer","setTimeout","then","snapshot","data","orgId","name","displayName","catch","clearTimeout"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;AAEA,SAASA,YAAY,QAAQ,iCAAgC;AAC7D,SAASC,GAAG,EAAEC,MAAM,QAAQ,qBAAoB;AAChD,SAASC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAO;AAiBnD,iFAAiF,GACjF,OAAO,MAAMC,yBAAyB,MAAM;AAE5C,OAAO,SAASC,aAAaC,MAAiC;IAC5D,yEAAyE;IACzE,mEAAmE;IACnE,MAAMC,YAAYT;IAClB,MAAMU,eAAeN,OAAOK;IAC5BC,aAAaC,OAAO,GAAGF;IACvB,MAAM,CAACG,MAAMC,QAAQ,GAAGR,SAAoB;QAAES,QAAQ;IAAU;IAChEX,UAAU;QACR,IAAI,CAACK,QAAQ;YACXK,QAAQ;gBAAEC,QAAQ;YAAQ;YAC1B,OAAOC;QACT;QACA,IAAIC,SAAS;QACbH,QAAQ,CAACF,UAAaA,QAAQG,MAAM,KAAK,YAAYH,UAAU;gBAAEG,QAAQ;YAAU;QACnF,MAAMG,SAAS,CAACC;YACd,IAAI,CAACF,QAAQ;YACbA,SAAS;YACTH,QAAQK;QACV;QACA,MAAMC,QAAQC,WAAW,IAAMH,OAAO;gBAAEH,QAAQ;YAAQ,IAAIR;QAC5D,KAAKJ,OAAOD,IAAIS,aAAaC,OAAO,EAAE,SAASH,SAC5Ca,IAAI,CAAC,CAACC;gBACSA;YAAd,MAAMC,QAAQD,iBAAAA,SAASC,IAAI,cAAbD,iBAAmB,CAAC;YAClC,MAAME,QAAQ,OAAOD,KAAKC,KAAK,KAAK,WAAWD,KAAKC,KAAK,GAAG;YAC5DP,OACEO,QACI;gBAAEV,QAAQ;gBAASU;gBAAOC,MAAM,OAAOF,KAAKG,WAAW,KAAK,WAAWH,KAAKG,WAAW,GAAG;YAAG,IAC7F;gBAAEZ,QAAQ;YAAQ;QAE1B,GACCa,KAAK,CAAC,IAAMV,OAAO;gBAAEH,QAAQ;YAAQ;QACxC,OAAO;YACLE,SAAS;YACTY,aAAaT;QACf;IACF,GAAG;QAACX;KAAO;IACX,OAAOI;AACT"}
|