@aglyn/plugins-ai 1.0.0-beta.230 → 1.0.0-beta.232
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/src/lib/activity/ai-activity-actions.js +8 -4
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-assist-provider-on-host.component.d.ts +66 -0
- package/src/lib/components/ai-assist-provider-on-host.component.js +78 -0
- package/src/lib/components/ai-assist-provider-on-host.component.js.map +1 -0
- package/src/lib/components/ai-assist-provider.component.js +9 -10
- package/src/lib/components/ai-assist-provider.component.js.map +1 -1
- package/src/lib/components/ai-job-follow.component.js +1 -0
- package/src/lib/components/ai-job-follow.component.js.map +1 -1
- package/src/lib/components/ai-job-links.js +13 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.js +3 -1
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-jobs-list-page.component.js +1 -0
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
- package/src/lib/components/ai-media-create.component.d.ts +20 -7
- package/src/lib/components/ai-media-create.component.js +107 -55
- package/src/lib/components/ai-media-create.component.js.map +1 -1
- package/src/lib/components/ai-permissions-on-host.component.d.ts +3 -46
- package/src/lib/components/ai-permissions-on-host.component.js +5 -53
- package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
- package/src/lib/components/ai-seo-fields-card.component.js +1 -0
- package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +20 -8
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-memory-card.component.d.ts +32 -0
- package/src/lib/components/ai-site-memory-card.component.js +187 -0
- package/src/lib/components/ai-site-memory-card.component.js.map +1 -0
- package/src/lib/components/ai-site-start-card.component.js +80 -77
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-theme-proposal-card.component.js +4 -0
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
- package/src/lib/components/ai-usage-strip.component.js +1 -0
- package/src/lib/components/ai-usage-strip.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.d.ts +10 -0
- package/src/lib/components/assist-panel.component.js +119 -9
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/assist-signals-page.component.js +2 -0
- package/src/lib/components/assist-signals-page.component.js.map +1 -1
- package/src/lib/components/billing-ai-allotments.component.js +3 -0
- package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
- package/src/lib/components/billing-ai-top-users.component.js +1 -0
- package/src/lib/components/billing-ai-top-users.component.js.map +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js +2 -0
- package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
- package/src/lib/components/member-ai-usage-card.component.js +10 -0
- package/src/lib/components/member-ai-usage-card.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-card.component.js +1 -0
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-conversations.component.d.ts +23 -0
- package/src/lib/components/staff-org-ai-conversations.component.js +346 -0
- package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -0
- package/src/lib/jobs/ai-build-capabilities.d.ts +13 -1
- package/src/lib/jobs/ai-build-capabilities.js +101 -7
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-build-unit-outcome.js +4 -2
- package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.d.ts +29 -0
- package/src/lib/jobs/ai-business-profile-prefill.js +85 -0
- package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -0
- package/src/lib/jobs/ai-job-auto-confirm.d.ts +8 -0
- package/src/lib/jobs/ai-job-auto-confirm.js +30 -6
- package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +14 -1
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-edit-step.d.ts +225 -0
- package/src/lib/jobs/ai-job-edit-step.js +631 -0
- package/src/lib/jobs/ai-job-edit-step.js.map +1 -0
- package/src/lib/jobs/ai-job-generation.d.ts +8 -0
- package/src/lib/jobs/ai-job-generation.js +13 -2
- package/src/lib/jobs/ai-job-generation.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +29 -5
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +78 -0
- package/src/lib/jobs/ai-job-layout-language.js +200 -0
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -0
- package/src/lib/jobs/ai-job-layout-site-pages.d.ts +13 -2
- package/src/lib/jobs/ai-job-layout-site-pages.js +18 -2
- package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.d.ts +5 -0
- package/src/lib/jobs/ai-job-layout-step.js +107 -6
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-logic-step.d.ts +20 -0
- package/src/lib/jobs/ai-job-logic-step.js +87 -6
- package/src/lib/jobs/ai-job-logic-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +194 -0
- package/src/lib/jobs/ai-job-page-language.js +418 -0
- package/src/lib/jobs/ai-job-page-language.js.map +1 -0
- package/src/lib/jobs/ai-job-page-sections.d.ts +37 -1
- package/src/lib/jobs/ai-job-page-sections.js +93 -11
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +4 -4
- package/src/lib/jobs/ai-job-page-step.js +115 -14
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +20 -6
- package/src/lib/jobs/ai-job-plan-step.js +62 -22
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +163 -0
- package/src/lib/jobs/ai-job-site-content.js +443 -0
- package/src/lib/jobs/ai-job-site-content.js.map +1 -0
- package/src/lib/jobs/ai-job-site-look.d.ts +112 -0
- package/src/lib/jobs/ai-job-site-look.js +322 -0
- package/src/lib/jobs/ai-job-site-look.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +30 -5
- package/src/lib/jobs/ai-job-site-step.js +183 -27
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.js +2 -2
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs-notify.d.ts +6 -1
- package/src/lib/jobs/ai-jobs-notify.js +15 -3
- package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +1 -1
- package/src/lib/jobs/ai-jobs.js +17 -8
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-full-height.d.ts +40 -0
- package/src/lib/jobs/ai-layout-full-height.js +82 -0
- package/src/lib/jobs/ai-layout-full-height.js.map +1 -0
- package/src/lib/jobs/ai-layout-site-facts.d.ts +54 -0
- package/src/lib/jobs/ai-layout-site-facts.js +153 -0
- package/src/lib/jobs/ai-layout-site-facts.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +154 -0
- package/src/lib/layout-language/ai-layout-compiler.js +1235 -0
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -0
- package/src/lib/layout-language/ai-layout-copy.d.ts +73 -0
- package/src/lib/layout-language/ai-layout-copy.js +137 -0
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -0
- package/src/lib/layout-language/ai-layout-frame.d.ts +52 -0
- package/src/lib/layout-language/ai-layout-frame.js +458 -0
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -0
- package/src/lib/layout-language/ai-layout-gaps.d.ts +63 -0
- package/src/lib/layout-language/ai-layout-gaps.js +345 -0
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -0
- package/src/lib/layout-language/ai-layout-language.d.ts +168 -0
- package/src/lib/layout-language/ai-layout-language.js +529 -0
- package/src/lib/layout-language/ai-layout-language.js.map +1 -0
- package/src/lib/layout-language/ai-layout-links.d.ts +80 -0
- package/src/lib/layout-language/ai-layout-links.js +109 -0
- package/src/lib/layout-language/ai-layout-links.js.map +1 -0
- package/src/lib/layout-language/ai-layout-pictures.d.ts +144 -0
- package/src/lib/layout-language/ai-layout-pictures.js +214 -0
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -0
- package/src/lib/layout-language/ai-layout-store.d.ts +41 -0
- package/src/lib/layout-language/ai-layout-store.js +75 -0
- package/src/lib/layout-language/ai-layout-store.js.map +1 -0
- package/src/lib/layout-language/ai-layout-tree.d.ts +59 -0
- package/src/lib/layout-language/ai-layout-tree.js +84 -0
- package/src/lib/layout-language/ai-layout-tree.js.map +1 -0
- package/src/lib/lazy-widget.d.ts +45 -0
- package/src/lib/lazy-widget.js +53 -0
- package/src/lib/lazy-widget.js.map +1 -0
- package/src/lib/model/ai-build-job.js +2 -1
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +20 -3
- package/src/lib/model/ai-build-progress.js +52 -7
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-edit-job.d.ts +109 -0
- package/src/lib/model/ai-edit-job.js +157 -0
- package/src/lib/model/ai-edit-job.js.map +1 -0
- package/src/lib/model/ai-host-publish-context.d.ts +51 -0
- package/src/lib/model/ai-host-publish-context.js +104 -0
- package/src/lib/model/ai-host-publish-context.js.map +1 -0
- package/src/lib/model/ai-job-activity.d.ts +22 -0
- package/src/lib/model/ai-job-activity.js +23 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-job-failure-copy.d.ts +38 -1
- package/src/lib/model/ai-job-failure-copy.js +56 -1
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +3 -1
- package/src/lib/model/ai-job-notice.js +31 -9
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +22 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-logic-job.d.ts +18 -1
- package/src/lib/model/ai-logic-job.js +24 -3
- package/src/lib/model/ai-logic-job.js.map +1 -1
- package/src/lib/model/ai-media-credits.d.ts +8 -4
- package/src/lib/model/ai-media-credits.js +11 -4
- package/src/lib/model/ai-media-credits.js.map +1 -1
- package/src/lib/model/ai-media-kinds.d.ts +68 -0
- package/src/lib/model/ai-media-kinds.js +98 -0
- package/src/lib/model/ai-media-kinds.js.map +1 -0
- package/src/lib/model/ai-site-build-progress.d.ts +9 -1
- package/src/lib/model/ai-site-build-progress.js +54 -14
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-context.d.ts +111 -0
- package/src/lib/model/ai-site-context.js +222 -0
- package/src/lib/model/ai-site-context.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +18 -2
- package/src/lib/model/ai-site-job.js +21 -5
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.d.ts +164 -0
- package/src/lib/model/ai-site-kinds.js +1942 -0
- package/src/lib/model/ai-site-kinds.js.map +1 -0
- package/src/lib/model/ai-site-look.d.ts +150 -0
- package/src/lib/model/ai-site-look.js +1236 -0
- package/src/lib/model/ai-site-look.js.map +1 -0
- package/src/lib/model/ai-site-memory.d.ts +80 -0
- package/src/lib/model/ai-site-memory.js +171 -0
- package/src/lib/model/ai-site-memory.js.map +1 -0
- package/src/lib/model/ai-site-start-seo.d.ts +1 -1
- package/src/lib/model/ai-site-start.d.ts +15 -3
- package/src/lib/model/ai-site-start.js +21 -4
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/assist-follow-up.d.ts +92 -0
- package/src/lib/model/assist-follow-up.js +128 -0
- package/src/lib/model/assist-follow-up.js.map +1 -0
- package/src/lib/plugin-config.js +2 -0
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +99 -48
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/anthropic.d.ts +9 -1
- package/src/lib/providers/anthropic.js +143 -9
- package/src/lib/providers/anthropic.js.map +1 -1
- package/src/lib/providers/catalog.d.ts +38 -8
- package/src/lib/providers/catalog.js +106 -14
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/contract.d.ts +29 -0
- package/src/lib/providers/contract.js.map +1 -1
- package/src/lib/providers/image-contract.d.ts +22 -1
- package/src/lib/providers/image-contract.js +40 -1
- package/src/lib/providers/image-contract.js.map +1 -1
- package/src/lib/providers/image-endpoints.d.ts +24 -0
- package/src/lib/providers/image-endpoints.js +28 -0
- package/src/lib/providers/image-endpoints.js.map +1 -0
- package/src/lib/providers/model-choice.js +23 -2
- package/src/lib/providers/model-choice.js.map +1 -1
- package/src/lib/providers/routing.js +18 -0
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/providers/vertex-image-endpoint.d.ts +48 -0
- package/src/lib/providers/vertex-image-endpoint.js +80 -0
- package/src/lib/providers/vertex-image-endpoint.js.map +1 -0
- package/src/lib/providers/vertex-image.d.ts +13 -27
- package/src/lib/providers/vertex-image.js +15 -42
- package/src/lib/providers/vertex-image.js.map +1 -1
- package/src/lib/runtime/ai-blog-post-generation.d.ts +62 -0
- package/src/lib/runtime/ai-blog-post-generation.js +89 -0
- package/src/lib/runtime/ai-blog-post-generation.js.map +1 -0
- package/src/lib/runtime/ai-dev-env.d.ts +53 -0
- package/src/lib/runtime/ai-dev-env.js +72 -0
- package/src/lib/runtime/ai-dev-env.js.map +1 -0
- package/src/lib/runtime/ai-dev-replay.d.ts +116 -0
- package/src/lib/runtime/ai-dev-replay.js +313 -0
- package/src/lib/runtime/ai-dev-replay.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +82 -0
- package/src/lib/runtime/ai-doctrine-validators.js +436 -4
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +16 -3
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +4 -0
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +13 -1
- package/src/lib/runtime/ai-eval.js +84 -1
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-icon-library.d.ts +257 -0
- package/src/lib/runtime/ai-icon-library.js +254 -0
- package/src/lib/runtime/ai-icon-library.js.map +1 -0
- package/src/lib/runtime/ai-live-batch.d.ts +45 -0
- package/src/lib/runtime/ai-live-batch.js +89 -0
- package/src/lib/runtime/ai-live-batch.js.map +1 -0
- package/src/lib/runtime/ai-node-tree.d.ts +9 -0
- package/src/lib/runtime/ai-node-tree.js +30 -4
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +6 -0
- package/src/lib/runtime/ai-palette.generated.js +70 -0
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/runtime/ai-runtime.js +46 -2
- package/src/lib/runtime/ai-runtime.js.map +1 -1
- package/src/lib/runtime/site-context.d.ts +58 -0
- package/src/lib/runtime/site-context.js +127 -0
- package/src/lib/runtime/site-context.js.map +1 -0
- package/src/lib/server/ai-admin-conversations.d.ts +22 -0
- package/src/lib/server/ai-admin-conversations.js +197 -0
- package/src/lib/server/ai-admin-conversations.js.map +1 -0
- package/src/lib/server/ai-media-image.d.ts +5 -5
- package/src/lib/server/ai-media-image.js +25 -11
- package/src/lib/server/ai-media-image.js.map +1 -1
- package/src/lib/server/ai-media-raster-prompt.d.ts +34 -0
- package/src/lib/server/ai-media-raster-prompt.js +53 -0
- package/src/lib/server/ai-media-raster-prompt.js.map +1 -0
- package/src/lib/server/assist-chat.d.ts +11 -0
- package/src/lib/server/assist-chat.js +57 -18
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit-applied.js +16 -3
- package/src/lib/server/assist-edit-applied.js.map +1 -1
- package/src/lib/server/assist-view-context.d.ts +19 -1
- package/src/lib/server/assist-view-context.js +38 -1
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server.js +10 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/subprocessors.js +17 -1
- package/src/lib/subprocessors.js.map +1 -1
- package/src/lib/tools/ai-blog-post-tool.d.ts +60 -0
- package/src/lib/tools/ai-blog-post-tool.js +175 -0
- package/src/lib/tools/ai-blog-post-tool.js.map +1 -0
- package/src/lib/tools/ai-workflow-tool.d.ts +6 -2
- package/src/lib/tools/ai-workflow-tool.js +16 -14
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +3 -0
- package/src/lib/usage/assist-usage.js +4 -2
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/staff-org-ai-conversations.d.ts +103 -0
- package/src/lib/usage/staff-org-ai-conversations.js +152 -0
- package/src/lib/usage/staff-org-ai-conversations.js.map +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-start.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AI_SITE_INPUT_MAX_CHARS,\n AI_SITE_SUBMISSIONS,\n aiSitePagesBand,\n type AiSiteJobInputs,\n type AiSiteSubmissions,\n} from './ai-site-job'\n\n/**\n * The guided start (AGL-2918): the questions a person answers on a site they\n * have just made, and the site job those answers become.\n *\n * A scaffold already exists — the `site` job plans four to eight pages, a\n * layout, a contact form, a palette and a welcome email, and builds every one\n * of them as a draft. What it had no way in through was a brief: somebody had\n * to know what to write, on a site where nothing has happened yet. So this\n * module is the QUESTIONS, and nothing else: what is asked, what an answer may\n * be, and how a set of answers becomes the brief and the scalar inputs the\n * `site` door already takes.\n *\n * ── Answering is never the way out ───────────────────────────────────────\n *\n * There is no answer here that means \"not this\". The way past the questions\n * is the `hostFirstRun` zone's own `startBlank`, which is drawn beside them\n * from the first one and creates nothing at all — see the zone in\n * `CONSOLE_WIDGET_SLOTS`. Putting the exit inside the question set would make\n * leaving a thing you answer your way to, and it is not.\n *\n * ── Every answer but the first is optional ───────────────────────────────\n *\n * Only what a site is FOR has no default worth guessing. The audience narrows\n * the copy, the example steers the shape, the page count and the welcome\n * email are defaults a person may change — so a person who types six words\n * and confirms gets a planned site, and the rest of the questions are there\n * for the person who wants to answer them.\n *\n * Pure: shapes, vocabularies and two string builders. No React, no network,\n * and no import of the starter catalog — {@link AI_SITE_START_EXAMPLES} names\n * the starters rather than reading them, so the console does not carry a\n * catalog of node maps to draw five cards. `ai-site-start.spec.ts` holds the\n * two lists to each other.\n */\n\n/** A worked example a person picks to steer what the site should look like. */\nexport interface AiSiteStartExample {\n /** A `STARTER_TEMPLATES` id, which is a persisted identifier. */\n id: string\n /** The starter as its card names it. */\n label: string\n /** The starter's category, which is how the examples group. */\n category: string\n /** What the example is, in one line a person reads before picking it. */\n blurb: string\n}\n\n/**\n * The examples the start offers, in the order they are shown. The platform's\n * own starter sites: a person who has no words for what they want has seen\n * these five shapes before, and picking one says more than a sentence would.\n */\nexport const AI_SITE_START_EXAMPLES: readonly AiSiteStartExample[] = [\n {\n id: 'business',\n label: 'Business',\n category: 'Business',\n blurb: 'A home page led by what you do, an about page and a contact page',\n },\n {\n id: 'landing',\n label: 'Landing Page',\n category: 'Marketing',\n blurb: 'One page that leads to one action, with the reasons to take it',\n },\n {\n id: 'portfolio',\n label: 'Portfolio',\n category: 'Personal',\n blurb: 'An introduction, a grid of work and a way to get in touch',\n },\n {\n id: 'physical-shop',\n label: 'Shop (physical products)',\n category: 'Commerce',\n blurb: 'Featured products, a shop to browse, and a cart',\n },\n {\n id: 'digital-shop',\n label: 'Shop (digital products)',\n category: 'Commerce',\n blurb: 'Downloads up front, a shop to browse, and a cart',\n },\n]\n\n/**\n * Kinds of site offered beside the first question's box, so answering it can\n * be a tap. They fill the box rather than replacing it: what somebody is\n * building is theirs to say, and a list of eight is a list somebody is not on.\n */\nexport const AI_SITE_START_TYPES: readonly string[] = [\n 'a local service business',\n 'a restaurant or cafe',\n 'a professional practice',\n 'a trades business',\n 'a shop',\n 'a nonprofit',\n 'a personal portfolio',\n 'an event',\n]\n\n/** What a person answered; the shape the card holds and the brief is built from. */\nexport interface AiSiteStartAnswers {\n /** What kind of site this is. The one answer with no default. */\n siteType: string\n /** Who the site is for; empty when they did not say. */\n audience: string\n /** The example they liked, by {@link AiSiteStartExample.id}; `null` for none. */\n example: string | null\n /** How many pages to plan. */\n pages: number\n /** Where the contact form's submissions go. */\n submissions: AiSiteSubmissions\n /** Whether the scaffold also drafts a welcome email. */\n welcomeEmail: boolean\n}\n\n/**\n * What the questions start on. Five pages because it is the middle of the\n * band the scaffold builds, a welcome email because the scaffold drafts one\n * unless it is told not to, and the Inbox because it is what every submission\n * does anyway — filing a lead as well is the addition, and an addition is the\n * thing a person should have to choose.\n */\nexport const AI_SITE_START_ANSWERS: AiSiteStartAnswers = {\n siteType: '',\n audience: '',\n example: null,\n pages: 5,\n submissions: 'inbox',\n welcomeEmail: true,\n}\n\n/** The example an answer names, or `null` when it names none this list knows. */\nexport function aiSiteStartExample(\n answers: AiSiteStartAnswers,\n): AiSiteStartExample | null {\n if (!answers.example) return null\n return AI_SITE_START_EXAMPLES.find((entry) => entry.id === answers.example) ?? null\n}\n\n/**\n * Why these answers cannot start a site, in a sentence a person reads; `null`\n * when they can. The door's own parser is what actually admits the job — this\n * is what the card says before it asks for one.\n */\nexport function aiSiteStartRefusal(\n answers: AiSiteStartAnswers,\n options: { freeTaste?: boolean } = {},\n): string | null {\n // The workspace's own band (AGL-3594): one or two pages on the Free taste.\n const band = aiSitePagesBand(options.freeTaste)\n const siteType = answers.siteType.trim()\n if (!siteType) return 'Say what kind of site this is.'\n if (siteType.length > AI_SITE_INPUT_MAX_CHARS) {\n return `Keep what kind of site this is under ${AI_SITE_INPUT_MAX_CHARS} characters.`\n }\n if (answers.audience.trim().length > AI_SITE_INPUT_MAX_CHARS) {\n return `Keep who the site is for under ${AI_SITE_INPUT_MAX_CHARS} characters.`\n }\n if (!Number.isInteger(answers.pages) || answers.pages < band.min || answers.pages > band.max) {\n return `A site is planned with ${band.min} to ${band.max} pages.`\n }\n if (answers.example !== null && !aiSiteStartExample(answers)) {\n return 'Pick one of the examples, or none of them.'\n }\n if (!(AI_SITE_SUBMISSIONS as readonly string[]).includes(answers.submissions)) {\n return 'Say where the contact form’s submissions go.'\n }\n return null\n}\n\n/**\n * The answers as the brief the `site` job is created with: the plan step reads\n * it as the person's own words, so it says what they said and asks for nothing\n * they did not.\n */\nexport function aiSiteStartBrief(answers: AiSiteStartAnswers): string {\n const siteType = answers.siteType.trim()\n const audience = answers.audience.trim()\n const example = aiSiteStartExample(answers)\n const sentences = [\n `A ${answers.pages}-page website for ${siteType}.`,\n ]\n if (audience) sentences.push(`It is for ${audience}.`)\n if (example) {\n sentences.push(\n `Follow the shape of the ${example.label} starter: ${example.blurb.toLowerCase()}.`,\n )\n }\n sentences.push(\n 'Plan a page for each thing a visitor comes to do, and a contact form on the page that asks to be contacted.',\n )\n if (audience) {\n // Said of the FORM as well as of the copy: who fills a form in is what\n // decides which fields it asks for, and a brief that named the audience\n // once, in a sentence about the pages, left the form to guess.\n sentences.push(`Ask the contact form for what ${audience} would be asked for.`)\n }\n return sentences.join(' ')\n}\n\n/**\n * The answers as the `site` job's scalar inputs. Read by the door's\n * {@link parseAiSiteJobInputs}, and put in front of the model one per line by\n * the plan step, which is how the audience and the example reach the plan\n * without a prompt of their own.\n *\n * The per-site variables an agency batch fills — the business name, the city,\n * the brand — are left empty: this is one person's own site, and its name is\n * on the site already.\n */\nexport function aiSiteStartInputs(\n answers: AiSiteStartAnswers,\n): Omit<AiSiteJobInputs, 'batchId'> {\n const example = aiSiteStartExample(answers)\n return {\n businessType: answers.siteType.trim(),\n audience: answers.audience.trim(),\n starter: example?.id ?? '',\n pages: answers.pages,\n businessName: '',\n city: '',\n brand: '',\n submissions: answers.submissions,\n welcomeEmail: answers.welcomeEmail,\n }\n}\n\n/** The site inputs a guided start's answers become, by name: what a summary carries back (AGL-3596). */\nexport const AI_SITE_START_INPUT_KEYS = [\n 'businessType',\n 'audience',\n 'starter',\n 'pages',\n 'businessName',\n 'city',\n 'brand',\n 'submissions',\n 'welcomeEmail',\n] as const\n\n/** A site job's scalar inputs, and nothing else its inputs carry; `null` when it has none. */\nexport function aiSiteStartInputsOf(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n): Record<string, unknown> | null {\n if (!inputs) return null\n const kept: Record<string, unknown> = {}\n for (const key of AI_SITE_START_INPUT_KEYS) {\n const value = inputs[key]\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') kept[key] = value\n }\n return Object.keys(kept).length ? kept : null\n}\n\n/**\n * A failed start's answers, back as the questions hold them (AGL-3596), so\n * Try again reopens the guided start filled in for the person to adjust:\n * the inverse of {@link aiSiteStartInputs}. Read defensively — an answer the\n * questions no longer offer goes back to its default — and held to the\n * workspace's page band. `null` when the inputs say no kind of site at all.\n */\nexport function aiSiteStartAnswersFromInputs(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n options: { freeTaste?: boolean } = {},\n): AiSiteStartAnswers | null {\n const text = (key: string) => (typeof inputs?.[key] === 'string' ? (inputs[key] as string).trim() : '')\n const siteType = text('businessType')\n if (!siteType) return null\n const band = aiSitePagesBand(options.freeTaste)\n const pages = typeof inputs?.['pages'] === 'number' ? Math.round(inputs['pages'] as number) : AI_SITE_START_ANSWERS.pages\n const starter = text('starter')\n const submissions = text('submissions')\n return {\n siteType,\n audience: text('audience'),\n example: AI_SITE_START_EXAMPLES.some((entry) => entry.id === starter) ? starter : null,\n pages: Math.min(band.max, Math.max(band.min, pages)),\n submissions: (AI_SITE_SUBMISSIONS as readonly string[]).includes(submissions)\n ? (submissions as AiSiteSubmissions)\n : AI_SITE_START_ANSWERS.submissions,\n welcomeEmail: options.freeTaste ? false : inputs?.['welcomeEmail'] !== false,\n }\n}\n"],"names":["AI_SITE_INPUT_MAX_CHARS","AI_SITE_SUBMISSIONS","aiSitePagesBand","AI_SITE_START_EXAMPLES","id","label","category","blurb","AI_SITE_START_TYPES","AI_SITE_START_ANSWERS","siteType","audience","example","pages","submissions","welcomeEmail","aiSiteStartExample","answers","find","entry","aiSiteStartRefusal","options","band","freeTaste","trim","length","Number","isInteger","min","max","includes","aiSiteStartBrief","sentences","push","toLowerCase","join","aiSiteStartInputs","businessType","starter","businessName","city","brand","AI_SITE_START_INPUT_KEYS","aiSiteStartInputsOf","inputs","kept","key","value","Object","keys","aiSiteStartAnswersFromInputs","text","Math","round","some"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-start.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { aiSiteKind, aiSiteKindFor, type AiSiteKind } from './ai-site-kinds'\nimport {\n AI_SITE_INPUT_MAX_CHARS,\n AI_SITE_SUBMISSIONS,\n aiSitePagesBand,\n type AiSiteJobInputs,\n type AiSiteSubmissions,\n} from './ai-site-job'\n\n/**\n * The guided start (AGL-2918): the questions a person answers on a site they\n * have just made, and the site job those answers become.\n *\n * A scaffold already exists — the `site` job plans four to eight pages, a\n * layout, a contact form, a palette and a welcome email, and builds every one\n * of them as a draft. What it had no way in through was a brief: somebody had\n * to know what to write, on a site where nothing has happened yet. So this\n * module is the QUESTIONS, and nothing else: what is asked, what an answer may\n * be, and how a set of answers becomes the brief and the scalar inputs the\n * `site` door already takes.\n *\n * ── Answering is never the way out ───────────────────────────────────────\n *\n * There is no answer here that means \"not this\". The way past the questions\n * is the `hostFirstRun` zone's own `startBlank`, which is drawn beside them\n * from the first one and creates nothing at all — see the zone in\n * `CONSOLE_WIDGET_SLOTS`. Putting the exit inside the question set would make\n * leaving a thing you answer your way to, and it is not.\n *\n * ── Every answer but the first is optional ───────────────────────────────\n *\n * Only what a site is FOR has no default worth guessing. The audience narrows\n * the copy, the example steers the shape, the page count and the welcome\n * email are defaults a person may change — so a person who types six words\n * and confirms gets a planned site, and the rest of the questions are there\n * for the person who wants to answer them.\n *\n * Pure: shapes, vocabularies and two string builders. No React, no network,\n * and no import of the starter catalog — {@link AI_SITE_START_EXAMPLES} names\n * the starters rather than reading them, so the console does not carry a\n * catalog of node maps to draw five cards. `ai-site-start.spec.ts` holds the\n * two lists to each other.\n */\n\n/** A worked example a person picks to steer what the site should look like. */\nexport interface AiSiteStartExample {\n /** A `STARTER_TEMPLATES` id, which is a persisted identifier. */\n id: string\n /** The starter as its card names it. */\n label: string\n /** The starter's category, which is how the examples group. */\n category: string\n /** What the example is, in one line a person reads before picking it. */\n blurb: string\n}\n\n/**\n * The examples the start offers, in the order they are shown. The platform's\n * own starter sites: a person who has no words for what they want has seen\n * these five shapes before, and picking one says more than a sentence would.\n */\nexport const AI_SITE_START_EXAMPLES: readonly AiSiteStartExample[] = [\n {\n id: 'business',\n label: 'Business',\n category: 'Business',\n blurb: 'A home page led by what you do, an about page and a contact page',\n },\n {\n id: 'landing',\n label: 'Landing Page',\n category: 'Marketing',\n blurb: 'One page that leads to one action, with the reasons to take it',\n },\n {\n id: 'portfolio',\n label: 'Portfolio',\n category: 'Personal',\n blurb: 'An introduction, a grid of work and a way to get in touch',\n },\n {\n id: 'physical-shop',\n label: 'Shop (physical products)',\n category: 'Commerce',\n blurb: 'Featured products, a shop to browse, and a cart',\n },\n {\n id: 'digital-shop',\n label: 'Shop (digital products)',\n category: 'Commerce',\n blurb: 'Downloads up front, a shop to browse, and a cart',\n },\n]\n\n/**\n * Kinds of site offered beside the first question's box, so answering it can\n * be a tap. They fill the box rather than replacing it: what somebody is\n * building is theirs to say, and a list of eight is a list somebody is not on.\n */\nexport const AI_SITE_START_TYPES: readonly string[] = [\n 'a local service business',\n 'a restaurant or cafe',\n 'a professional practice',\n 'a trades business',\n 'a shop',\n 'a nonprofit',\n 'a personal portfolio',\n 'an event',\n]\n\n/** What a person answered; the shape the card holds and the brief is built from. */\nexport interface AiSiteStartAnswers {\n /** What kind of site this is. The one answer with no default. */\n siteType: string\n /** Who the site is for; empty when they did not say. */\n audience: string\n /** The example they liked, by {@link AiSiteStartExample.id}; `null` for none. */\n example: string | null\n /**\n * The kind of site they picked (AGL-3660), by its `AI_SITE_KINDS` id;\n * `null` until they pick one, when the kind their first answer suggests is\n * the one used ({@link aiSiteStartKind}).\n */\n kind: string | null\n /** How many pages to plan. */\n pages: number\n /** Where the contact form's submissions go. */\n submissions: AiSiteSubmissions\n /** Whether the scaffold also drafts a welcome email. */\n welcomeEmail: boolean\n}\n\n/**\n * What the questions start on. Five pages because it is the middle of the\n * band the scaffold builds, a welcome email because the scaffold drafts one\n * unless it is told not to, and the Inbox because it is what every submission\n * does anyway — filing a lead as well is the addition, and an addition is the\n * thing a person should have to choose.\n */\nexport const AI_SITE_START_ANSWERS: AiSiteStartAnswers = {\n siteType: '',\n audience: '',\n example: null,\n kind: null,\n pages: 5,\n submissions: 'inbox',\n welcomeEmail: true,\n}\n\n/** The kind of site the answers pick, else the one what the site is for suggests. */\nexport function aiSiteStartKind(answers: Pick<AiSiteStartAnswers, 'kind' | 'siteType'>): AiSiteKind {\n return aiSiteKind(answers.kind) ?? aiSiteKindFor(answers.siteType)\n}\n\n/** The example an answer names, or `null` when it names none this list knows. */\nexport function aiSiteStartExample(\n answers: AiSiteStartAnswers,\n): AiSiteStartExample | null {\n if (!answers.example) return null\n return AI_SITE_START_EXAMPLES.find((entry) => entry.id === answers.example) ?? null\n}\n\n/**\n * Why these answers cannot start a site, in a sentence a person reads; `null`\n * when they can. The door's own parser is what actually admits the job — this\n * is what the card says before it asks for one.\n */\nexport function aiSiteStartRefusal(\n answers: AiSiteStartAnswers,\n options: { freeTaste?: boolean } = {},\n): string | null {\n // The workspace's own band (AGL-3594): one or two pages on the Free taste.\n const band = aiSitePagesBand(options.freeTaste)\n const siteType = answers.siteType.trim()\n if (!siteType) return 'Say what kind of site this is.'\n if (siteType.length > AI_SITE_INPUT_MAX_CHARS) {\n return `Keep what kind of site this is under ${AI_SITE_INPUT_MAX_CHARS} characters.`\n }\n if (answers.audience.trim().length > AI_SITE_INPUT_MAX_CHARS) {\n return `Keep who the site is for under ${AI_SITE_INPUT_MAX_CHARS} characters.`\n }\n if (!Number.isInteger(answers.pages) || answers.pages < band.min || answers.pages > band.max) {\n return `A site is planned with ${band.min} to ${band.max} pages.`\n }\n if (answers.example !== null && !aiSiteStartExample(answers)) {\n return 'Pick one of the examples, or none of them.'\n }\n if (!(AI_SITE_SUBMISSIONS as readonly string[]).includes(answers.submissions)) {\n return 'Say where the contact form’s submissions go.'\n }\n return null\n}\n\n/**\n * The answers as the brief the `site` job is created with: the plan step reads\n * it as the person's own words, so it says what they said and asks for nothing\n * they did not.\n */\nexport function aiSiteStartBrief(answers: AiSiteStartAnswers): string {\n const siteType = answers.siteType.trim()\n const audience = answers.audience.trim()\n const example = aiSiteStartExample(answers)\n const sentences = [\n `A ${answers.pages}-page website for ${siteType}.`,\n ]\n if (audience) sentences.push(`It is for ${audience}.`)\n if (example) {\n sentences.push(\n `Follow the shape of the ${example.label} starter: ${example.blurb.toLowerCase()}.`,\n )\n }\n // The kind they picked, or the one their answer suggests (AGL-3660).\n sentences.push(`Make it a ${aiSiteStartKind(answers).label.toLowerCase()} site.`)\n sentences.push(\n 'Plan a page for each thing a visitor comes to do, and a contact form on the page that asks to be contacted.',\n )\n if (audience) {\n // Said of the FORM as well as of the copy: who fills a form in is what\n // decides which fields it asks for, and a brief that named the audience\n // once, in a sentence about the pages, left the form to guess.\n sentences.push(`Ask the contact form for what ${audience} would be asked for.`)\n }\n return sentences.join(' ')\n}\n\n/**\n * The answers as the `site` job's scalar inputs. Read by the door's\n * {@link parseAiSiteJobInputs}, and put in front of the model one per line by\n * the plan step, which is how the audience and the example reach the plan\n * without a prompt of their own.\n *\n * The per-site variables an agency batch fills — the business name, the city,\n * the brand — are left empty here: the questions never ask them. The name is\n * the site's own, and the create door fills it from the site\n * (`aiJobAdmittedInputs`, AGL-3596) when the job is started — a job that\n * reached the model with no name had the model invent one for its header and\n * footer.\n */\nexport function aiSiteStartInputs(\n answers: AiSiteStartAnswers,\n): Omit<AiSiteJobInputs, 'batchId'> {\n const example = aiSiteStartExample(answers)\n const kind = aiSiteStartKind(answers)\n return {\n businessType: answers.siteType.trim(),\n audience: answers.audience.trim(),\n starter: example?.id ?? kind.starter,\n siteKind: kind.id,\n pages: answers.pages,\n businessName: '',\n city: '',\n brand: '',\n submissions: answers.submissions,\n welcomeEmail: answers.welcomeEmail,\n }\n}\n\n/** The site inputs a guided start's answers become, by name: what a summary carries back (AGL-3596). */\nexport const AI_SITE_START_INPUT_KEYS = [\n 'businessType',\n 'audience',\n 'starter',\n 'siteKind',\n 'pages',\n 'businessName',\n 'city',\n 'brand',\n 'submissions',\n 'welcomeEmail',\n] as const\n\n/** A site job's scalar inputs, and nothing else its inputs carry; `null` when it has none. */\nexport function aiSiteStartInputsOf(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n): Record<string, unknown> | null {\n if (!inputs) return null\n const kept: Record<string, unknown> = {}\n for (const key of AI_SITE_START_INPUT_KEYS) {\n const value = inputs[key]\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') kept[key] = value\n }\n return Object.keys(kept).length ? kept : null\n}\n\n/**\n * A failed start's answers, back as the questions hold them (AGL-3596), so\n * Try again reopens the guided start filled in for the person to adjust:\n * the inverse of {@link aiSiteStartInputs}. Read defensively — an answer the\n * questions no longer offer goes back to its default — and held to the\n * workspace's page band. `null` when the inputs say no kind of site at all.\n */\nexport function aiSiteStartAnswersFromInputs(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n options: { freeTaste?: boolean } = {},\n): AiSiteStartAnswers | null {\n const text = (key: string) => (typeof inputs?.[key] === 'string' ? (inputs[key] as string).trim() : '')\n const siteType = text('businessType')\n if (!siteType) return null\n const band = aiSitePagesBand(options.freeTaste)\n const pages = typeof inputs?.['pages'] === 'number' ? Math.round(inputs['pages'] as number) : AI_SITE_START_ANSWERS.pages\n const starter = text('starter')\n const submissions = text('submissions')\n return {\n siteType,\n audience: text('audience'),\n example: AI_SITE_START_EXAMPLES.some((entry) => entry.id === starter) ? starter : null,\n kind: aiSiteKind(text('siteKind'))?.id ?? null,\n pages: Math.min(band.max, Math.max(band.min, pages)),\n submissions: (AI_SITE_SUBMISSIONS as readonly string[]).includes(submissions)\n ? (submissions as AiSiteSubmissions)\n : AI_SITE_START_ANSWERS.submissions,\n welcomeEmail: options.freeTaste ? false : inputs?.['welcomeEmail'] !== false,\n }\n}\n"],"names":["aiSiteKind","aiSiteKindFor","AI_SITE_INPUT_MAX_CHARS","AI_SITE_SUBMISSIONS","aiSitePagesBand","AI_SITE_START_EXAMPLES","id","label","category","blurb","AI_SITE_START_TYPES","AI_SITE_START_ANSWERS","siteType","audience","example","kind","pages","submissions","welcomeEmail","aiSiteStartKind","answers","aiSiteStartExample","find","entry","aiSiteStartRefusal","options","band","freeTaste","trim","length","Number","isInteger","min","max","includes","aiSiteStartBrief","sentences","push","toLowerCase","join","aiSiteStartInputs","businessType","starter","siteKind","businessName","city","brand","AI_SITE_START_INPUT_KEYS","aiSiteStartInputsOf","inputs","kept","key","value","Object","keys","aiSiteStartAnswersFromInputs","text","Math","round","some"],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { AiJobOutput } from './ai-jobs.types';
|
|
18
|
+
/**
|
|
19
|
+
* A follow-up on what a build made (AGL-3616): "make the about page shorter"
|
|
20
|
+
* after Assist built the About page.
|
|
21
|
+
*
|
|
22
|
+
* The edit card works only on the document open in the Besigner. So when a
|
|
23
|
+
* request is about a draft the build in this chat made, Assist proposes
|
|
24
|
+
* OPENING that draft's Besigner, and the panel asks the same request again
|
|
25
|
+
* once the draft's canvas is open — where the edit rung answers it with an
|
|
26
|
+
* edit card the author applies. Nothing is changed by the proposal itself.
|
|
27
|
+
*
|
|
28
|
+
* The panel names the drafts it can open by a short ref (`d1`, `d2`, …) and a
|
|
29
|
+
* label; the ids and the address stay in the panel, which got them from the
|
|
30
|
+
* job door. The chat sees only refs and labels, and a proposal it makes is
|
|
31
|
+
* held to a ref the request listed.
|
|
32
|
+
*
|
|
33
|
+
* Shared by the chat door and the panel. Pure: no request, no React.
|
|
34
|
+
*/
|
|
35
|
+
/** The navigation's id, in the proposal channel and the analytics event. */
|
|
36
|
+
export declare const ASSIST_OPEN_DRAFT_ACTION_ID = "open.build.draft";
|
|
37
|
+
/** The one parameter the navigation takes: the ref of the draft to open. */
|
|
38
|
+
export declare const ASSIST_OPEN_DRAFT_PARAM = "draft";
|
|
39
|
+
/** The most drafts one request lists: a build makes at most 16 units. */
|
|
40
|
+
export declare const ASSIST_BUILD_DRAFTS_MAX = 16;
|
|
41
|
+
/** The longest label a request carries for one draft. */
|
|
42
|
+
export declare const ASSIST_BUILD_DRAFT_LABEL_MAX = 80;
|
|
43
|
+
/** How long a follow-up waits for its canvas before it is dropped. */
|
|
44
|
+
export declare const ASSIST_FOLLOW_UP_TTL_MS: number;
|
|
45
|
+
/** A draft the chat may offer to open, as the request names it. */
|
|
46
|
+
export interface AssistBuildDraft {
|
|
47
|
+
/** `d1`, `d2`, …: what the model chooses from. */
|
|
48
|
+
ref: string;
|
|
49
|
+
/** What the draft is called: the page's title, the layout's name. */
|
|
50
|
+
label: string;
|
|
51
|
+
/** What kind of thing it is, in a word a person reads. */
|
|
52
|
+
noun: string;
|
|
53
|
+
}
|
|
54
|
+
/** A draft the panel can open, with the address it opens at. */
|
|
55
|
+
export interface AssistBuildDraftLink extends AssistBuildDraft {
|
|
56
|
+
href: string;
|
|
57
|
+
}
|
|
58
|
+
/** The Besigner address a job's output opens at, or `null` where it has none. */
|
|
59
|
+
export declare function assistDraftBesignerHref(output: AiJobOutput, orgSlug: string): string | null;
|
|
60
|
+
/**
|
|
61
|
+
* The drafts the panel can offer, newest build first: every output of the
|
|
62
|
+
* given jobs that opens in the Besigner on the version the job wrote, once
|
|
63
|
+
* each, at most {@link ASSIST_BUILD_DRAFTS_MAX}.
|
|
64
|
+
*/
|
|
65
|
+
export declare function assistBuildDraftLinks(jobs: ReadonlyArray<{
|
|
66
|
+
outputs: readonly AiJobOutput[];
|
|
67
|
+
}>, orgSlug: string): AssistBuildDraftLink[];
|
|
68
|
+
/** A label as it may enter a prompt: one line, no fence, quotes or brackets, bounded. */
|
|
69
|
+
export declare function assistDraftLabel(value: unknown): string;
|
|
70
|
+
/**
|
|
71
|
+
* The drafts a request lists, as the chat door will use them: well-formed
|
|
72
|
+
* refs, once each, labels cleaned, nouns from the closed set, bounded. A
|
|
73
|
+
* malformed entry is dropped, never repaired.
|
|
74
|
+
*/
|
|
75
|
+
export declare function parseAssistBuildDrafts(raw: unknown): AssistBuildDraft[];
|
|
76
|
+
/**
|
|
77
|
+
* The lines the chat is told about the drafts, after every cache breakpoint:
|
|
78
|
+
* what each ref is, and that a request to change one is a navigation to it,
|
|
79
|
+
* not a new build.
|
|
80
|
+
*/
|
|
81
|
+
export declare function assistBuildDraftsBlock(drafts: readonly AssistBuildDraft[]): string;
|
|
82
|
+
/** A follow-up waiting for its canvas: asked again once the panel stands on `path`. */
|
|
83
|
+
export interface AssistPendingFollowUp {
|
|
84
|
+
path: string;
|
|
85
|
+
question: string;
|
|
86
|
+
/** When it was confirmed, in ms; one older than the TTL is dropped. */
|
|
87
|
+
at: number;
|
|
88
|
+
}
|
|
89
|
+
/** Whether a stored follow-up is still one the panel should ask. */
|
|
90
|
+
export declare function isLiveAssistFollowUp(value: unknown, now: number): value is AssistPendingFollowUp;
|
|
91
|
+
/** The version a Besigner address names, for the open editor to match. */
|
|
92
|
+
export declare function assistBesignerVersionOf(path: string): string | null;
|
|
@@ -0,0 +1,128 @@
|
|
|
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
|
+
*/ import { AI_JOB_BESIGNER_SEGMENT } from "./ai-job-notice.js";
|
|
17
|
+
/**
|
|
18
|
+
* A follow-up on what a build made (AGL-3616): "make the about page shorter"
|
|
19
|
+
* after Assist built the About page.
|
|
20
|
+
*
|
|
21
|
+
* The edit card works only on the document open in the Besigner. So when a
|
|
22
|
+
* request is about a draft the build in this chat made, Assist proposes
|
|
23
|
+
* OPENING that draft's Besigner, and the panel asks the same request again
|
|
24
|
+
* once the draft's canvas is open — where the edit rung answers it with an
|
|
25
|
+
* edit card the author applies. Nothing is changed by the proposal itself.
|
|
26
|
+
*
|
|
27
|
+
* The panel names the drafts it can open by a short ref (`d1`, `d2`, …) and a
|
|
28
|
+
* label; the ids and the address stay in the panel, which got them from the
|
|
29
|
+
* job door. The chat sees only refs and labels, and a proposal it makes is
|
|
30
|
+
* held to a ref the request listed.
|
|
31
|
+
*
|
|
32
|
+
* Shared by the chat door and the panel. Pure: no request, no React.
|
|
33
|
+
*/ /** The navigation's id, in the proposal channel and the analytics event. */ export const ASSIST_OPEN_DRAFT_ACTION_ID = 'open.build.draft';
|
|
34
|
+
/** The one parameter the navigation takes: the ref of the draft to open. */ export const ASSIST_OPEN_DRAFT_PARAM = 'draft';
|
|
35
|
+
/** The most drafts one request lists: a build makes at most 16 units. */ export const ASSIST_BUILD_DRAFTS_MAX = 16;
|
|
36
|
+
/** The longest label a request carries for one draft. */ export const ASSIST_BUILD_DRAFT_LABEL_MAX = 80;
|
|
37
|
+
/** How long a follow-up waits for its canvas before it is dropped. */ export const ASSIST_FOLLOW_UP_TTL_MS = 2 * 60000;
|
|
38
|
+
/** What one Besigner-backed output is called in a sentence. */ const NOUNS = {
|
|
39
|
+
screen: 'page',
|
|
40
|
+
reusableComponent: 'component',
|
|
41
|
+
layout: 'layout',
|
|
42
|
+
template: 'page template',
|
|
43
|
+
emailScreen: 'email design'
|
|
44
|
+
};
|
|
45
|
+
/** The Besigner address a job's output opens at, or `null` where it has none. */ export function assistDraftBesignerHref(output, orgSlug) {
|
|
46
|
+
const segment = AI_JOB_BESIGNER_SEGMENT[output.resource];
|
|
47
|
+
if (!segment || !orgSlug || !output.hostSubdomain || !output.versionId) return null;
|
|
48
|
+
return `/${orgSlug}/hosts/${output.hostSubdomain}/${segment}/${output.id}/versions/${output.versionId}/besigner`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The drafts the panel can offer, newest build first: every output of the
|
|
52
|
+
* given jobs that opens in the Besigner on the version the job wrote, once
|
|
53
|
+
* each, at most {@link ASSIST_BUILD_DRAFTS_MAX}.
|
|
54
|
+
*/ export function assistBuildDraftLinks(jobs, orgSlug) {
|
|
55
|
+
const links = [];
|
|
56
|
+
const seen = new Set();
|
|
57
|
+
for (const job of jobs){
|
|
58
|
+
for (const output of job.outputs){
|
|
59
|
+
var _NOUNS_output_resource;
|
|
60
|
+
if (links.length >= ASSIST_BUILD_DRAFTS_MAX) return links;
|
|
61
|
+
const href = assistDraftBesignerHref(output, orgSlug);
|
|
62
|
+
if (!href || seen.has(href)) continue;
|
|
63
|
+
seen.add(href);
|
|
64
|
+
links.push({
|
|
65
|
+
ref: `d${links.length + 1}`,
|
|
66
|
+
label: assistDraftLabel(output.label),
|
|
67
|
+
noun: (_NOUNS_output_resource = NOUNS[output.resource]) != null ? _NOUNS_output_resource : 'draft',
|
|
68
|
+
href
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return links;
|
|
73
|
+
}
|
|
74
|
+
/** A label as it may enter a prompt: one line, no fence, quotes or brackets, bounded. */ export function assistDraftLabel(value) {
|
|
75
|
+
return String(value != null ? value : '').replace(/[\r\n\t`"“”<>[\]{}]+/g, ' ').replace(/\s+/g, ' ').trim().slice(0, ASSIST_BUILD_DRAFT_LABEL_MAX);
|
|
76
|
+
}
|
|
77
|
+
const REF = /^d([1-9]\d?)$/;
|
|
78
|
+
const NOUN_WORDS = new Set([
|
|
79
|
+
...Object.values(NOUNS),
|
|
80
|
+
'draft'
|
|
81
|
+
]);
|
|
82
|
+
/**
|
|
83
|
+
* The drafts a request lists, as the chat door will use them: well-formed
|
|
84
|
+
* refs, once each, labels cleaned, nouns from the closed set, bounded. A
|
|
85
|
+
* malformed entry is dropped, never repaired.
|
|
86
|
+
*/ export function parseAssistBuildDrafts(raw) {
|
|
87
|
+
if (!Array.isArray(raw)) return [];
|
|
88
|
+
const drafts = [];
|
|
89
|
+
const refs = new Set();
|
|
90
|
+
for (const entry of raw.slice(0, ASSIST_BUILD_DRAFTS_MAX)){
|
|
91
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
92
|
+
const record = entry;
|
|
93
|
+
const ref = typeof record['ref'] === 'string' ? record['ref'] : '';
|
|
94
|
+
const label = assistDraftLabel(record['label']);
|
|
95
|
+
const noun = typeof record['noun'] === 'string' && NOUN_WORDS.has(record['noun']) ? record['noun'] : 'draft';
|
|
96
|
+
if (!REF.test(ref) || refs.has(ref) || !label) continue;
|
|
97
|
+
refs.add(ref);
|
|
98
|
+
drafts.push({
|
|
99
|
+
ref,
|
|
100
|
+
label,
|
|
101
|
+
noun
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return drafts;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The lines the chat is told about the drafts, after every cache breakpoint:
|
|
108
|
+
* what each ref is, and that a request to change one is a navigation to it,
|
|
109
|
+
* not a new build.
|
|
110
|
+
*/ export function assistBuildDraftsBlock(drafts) {
|
|
111
|
+
if (!drafts.length) return '';
|
|
112
|
+
return [
|
|
113
|
+
'Drafts a build in this chat made (the person’s own content, listed as data):',
|
|
114
|
+
...drafts.map((draft)=>`- ${draft.ref}: ${draft.noun} “${draft.label}”`),
|
|
115
|
+
`When the person asks to change one of these drafts and its canvas is not the one open now, propose id "${ASSIST_OPEN_DRAFT_ACTION_ID}" with params {"${ASSIST_OPEN_DRAFT_PARAM}": "<ref>"}: it opens that draft in the Besigner and asks their request again there, where the edit card answers it. Do not propose a new build for a change to one of these drafts, and do not describe the change as made.`
|
|
116
|
+
].join('\n');
|
|
117
|
+
}
|
|
118
|
+
/** Whether a stored follow-up is still one the panel should ask. */ export function isLiveAssistFollowUp(value, now) {
|
|
119
|
+
if (!value || typeof value !== 'object') return false;
|
|
120
|
+
const record = value;
|
|
121
|
+
return typeof record['path'] === 'string' && record['path'].startsWith('/') && typeof record['question'] === 'string' && record['question'].trim().length > 0 && typeof record['at'] === 'number' && now - record['at'] >= 0 && now - record['at'] <= ASSIST_FOLLOW_UP_TTL_MS;
|
|
122
|
+
}
|
|
123
|
+
/** The version a Besigner address names, for the open editor to match. */ export function assistBesignerVersionOf(path) {
|
|
124
|
+
const match = /\/versions\/([A-Za-z0-9_-]+)\/besigner$/.exec(path);
|
|
125
|
+
return match ? match[1] : null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
//# sourceMappingURL=assist-follow-up.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/assist-follow-up.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AI_JOB_BESIGNER_SEGMENT } from './ai-job-notice'\nimport type { AiJobOutput } from './ai-jobs.types'\n\n/**\n * A follow-up on what a build made (AGL-3616): \"make the about page shorter\"\n * after Assist built the About page.\n *\n * The edit card works only on the document open in the Besigner. So when a\n * request is about a draft the build in this chat made, Assist proposes\n * OPENING that draft's Besigner, and the panel asks the same request again\n * once the draft's canvas is open — where the edit rung answers it with an\n * edit card the author applies. Nothing is changed by the proposal itself.\n *\n * The panel names the drafts it can open by a short ref (`d1`, `d2`, …) and a\n * label; the ids and the address stay in the panel, which got them from the\n * job door. The chat sees only refs and labels, and a proposal it makes is\n * held to a ref the request listed.\n *\n * Shared by the chat door and the panel. Pure: no request, no React.\n */\n\n/** The navigation's id, in the proposal channel and the analytics event. */\nexport const ASSIST_OPEN_DRAFT_ACTION_ID = 'open.build.draft'\n\n/** The one parameter the navigation takes: the ref of the draft to open. */\nexport const ASSIST_OPEN_DRAFT_PARAM = 'draft'\n\n/** The most drafts one request lists: a build makes at most 16 units. */\nexport const ASSIST_BUILD_DRAFTS_MAX = 16\n\n/** The longest label a request carries for one draft. */\nexport const ASSIST_BUILD_DRAFT_LABEL_MAX = 80\n\n/** How long a follow-up waits for its canvas before it is dropped. */\nexport const ASSIST_FOLLOW_UP_TTL_MS = 2 * 60_000\n\n/** A draft the chat may offer to open, as the request names it. */\nexport interface AssistBuildDraft {\n /** `d1`, `d2`, …: what the model chooses from. */\n ref: string\n /** What the draft is called: the page's title, the layout's name. */\n label: string\n /** What kind of thing it is, in a word a person reads. */\n noun: string\n}\n\n/** A draft the panel can open, with the address it opens at. */\nexport interface AssistBuildDraftLink extends AssistBuildDraft {\n href: string\n}\n\n/** What one Besigner-backed output is called in a sentence. */\nconst NOUNS: Partial<Record<AiJobOutput['resource'], string>> = {\n screen: 'page',\n reusableComponent: 'component',\n layout: 'layout',\n template: 'page template',\n emailScreen: 'email design',\n}\n\n/** The Besigner address a job's output opens at, or `null` where it has none. */\nexport function assistDraftBesignerHref(output: AiJobOutput, orgSlug: string): string | null {\n const segment = AI_JOB_BESIGNER_SEGMENT[output.resource]\n if (!segment || !orgSlug || !output.hostSubdomain || !output.versionId) return null\n return `/${orgSlug}/hosts/${output.hostSubdomain}/${segment}/${output.id}/versions/${output.versionId}/besigner`\n}\n\n/**\n * The drafts the panel can offer, newest build first: every output of the\n * given jobs that opens in the Besigner on the version the job wrote, once\n * each, at most {@link ASSIST_BUILD_DRAFTS_MAX}.\n */\nexport function assistBuildDraftLinks(\n jobs: ReadonlyArray<{ outputs: readonly AiJobOutput[] }>,\n orgSlug: string,\n): AssistBuildDraftLink[] {\n const links: AssistBuildDraftLink[] = []\n const seen = new Set<string>()\n for (const job of jobs) {\n for (const output of job.outputs) {\n if (links.length >= ASSIST_BUILD_DRAFTS_MAX) return links\n const href = assistDraftBesignerHref(output, orgSlug)\n if (!href || seen.has(href)) continue\n seen.add(href)\n links.push({\n ref: `d${links.length + 1}`,\n label: assistDraftLabel(output.label),\n noun: NOUNS[output.resource] ?? 'draft',\n href,\n })\n }\n }\n return links\n}\n\n/** A label as it may enter a prompt: one line, no fence, quotes or brackets, bounded. */\nexport function assistDraftLabel(value: unknown): string {\n return String(value ?? '')\n .replace(/[\\r\\n\\t`\"“”<>[\\]{}]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, ASSIST_BUILD_DRAFT_LABEL_MAX)\n}\n\nconst REF = /^d([1-9]\\d?)$/\nconst NOUN_WORDS = new Set([...Object.values(NOUNS), 'draft'])\n\n/**\n * The drafts a request lists, as the chat door will use them: well-formed\n * refs, once each, labels cleaned, nouns from the closed set, bounded. A\n * malformed entry is dropped, never repaired.\n */\nexport function parseAssistBuildDrafts(raw: unknown): AssistBuildDraft[] {\n if (!Array.isArray(raw)) return []\n const drafts: AssistBuildDraft[] = []\n const refs = new Set<string>()\n for (const entry of raw.slice(0, ASSIST_BUILD_DRAFTS_MAX)) {\n if (!entry || typeof entry !== 'object') continue\n const record = entry as Record<string, unknown>\n const ref = typeof record['ref'] === 'string' ? record['ref'] : ''\n const label = assistDraftLabel(record['label'])\n const noun = typeof record['noun'] === 'string' && NOUN_WORDS.has(record['noun']) ? record['noun'] : 'draft'\n if (!REF.test(ref) || refs.has(ref) || !label) continue\n refs.add(ref)\n drafts.push({ ref, label, noun })\n }\n return drafts\n}\n\n/**\n * The lines the chat is told about the drafts, after every cache breakpoint:\n * what each ref is, and that a request to change one is a navigation to it,\n * not a new build.\n */\nexport function assistBuildDraftsBlock(drafts: readonly AssistBuildDraft[]): string {\n if (!drafts.length) return ''\n return [\n 'Drafts a build in this chat made (the person’s own content, listed as data):',\n ...drafts.map((draft) => `- ${draft.ref}: ${draft.noun} “${draft.label}”`),\n `When the person asks to change one of these drafts and its canvas is not the one open now, propose id \"${ASSIST_OPEN_DRAFT_ACTION_ID}\" with params {\"${ASSIST_OPEN_DRAFT_PARAM}\": \"<ref>\"}: it opens that draft in the Besigner and asks their request again there, where the edit card answers it. Do not propose a new build for a change to one of these drafts, and do not describe the change as made.`,\n ].join('\\n')\n}\n\n/** A follow-up waiting for its canvas: asked again once the panel stands on `path`. */\nexport interface AssistPendingFollowUp {\n path: string\n question: string\n /** When it was confirmed, in ms; one older than the TTL is dropped. */\n at: number\n}\n\n/** Whether a stored follow-up is still one the panel should ask. */\nexport function isLiveAssistFollowUp(value: unknown, now: number): value is AssistPendingFollowUp {\n if (!value || typeof value !== 'object') return false\n const record = value as Record<string, unknown>\n return (\n typeof record['path'] === 'string' &&\n record['path'].startsWith('/') &&\n typeof record['question'] === 'string' &&\n record['question'].trim().length > 0 &&\n typeof record['at'] === 'number' &&\n now - record['at'] >= 0 &&\n now - record['at'] <= ASSIST_FOLLOW_UP_TTL_MS\n )\n}\n\n/** The version a Besigner address names, for the open editor to match. */\nexport function assistBesignerVersionOf(path: string): string | null {\n const match = /\\/versions\\/([A-Za-z0-9_-]+)\\/besigner$/.exec(path)\n return match ? match[1] : null\n}\n"],"names":["AI_JOB_BESIGNER_SEGMENT","ASSIST_OPEN_DRAFT_ACTION_ID","ASSIST_OPEN_DRAFT_PARAM","ASSIST_BUILD_DRAFTS_MAX","ASSIST_BUILD_DRAFT_LABEL_MAX","ASSIST_FOLLOW_UP_TTL_MS","NOUNS","screen","reusableComponent","layout","template","emailScreen","assistDraftBesignerHref","output","orgSlug","segment","resource","hostSubdomain","versionId","id","assistBuildDraftLinks","jobs","links","seen","Set","job","outputs","length","href","has","add","push","ref","label","assistDraftLabel","noun","value","String","replace","trim","slice","REF","NOUN_WORDS","Object","values","parseAssistBuildDrafts","raw","Array","isArray","drafts","refs","entry","record","test","assistBuildDraftsBlock","map","draft","join","isLiveAssistFollowUp","now","startsWith","assistBesignerVersionOf","path","match","exec"],"mappings":"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"}
|
package/src/lib/plugin-config.js
CHANGED
|
@@ -39,6 +39,7 @@ import { AI_CATALOG_PROVIDERS, AI_MODEL_CATALOG } from "./providers/catalog.js";
|
|
|
39
39
|
'job.insight': 'jobInsightModel',
|
|
40
40
|
'job.layout': 'jobLayoutModel',
|
|
41
41
|
'job.logic': 'jobLogicModel',
|
|
42
|
+
'job.edit': 'jobEditModel',
|
|
42
43
|
'job.template': 'jobTemplateModel',
|
|
43
44
|
'job.page': 'jobPageModel',
|
|
44
45
|
'job.products': 'jobProductsModel',
|
|
@@ -98,6 +99,7 @@ export const AI_CONFIG_SCHEMA = {
|
|
|
98
99
|
modelField('job.insight', 'Insights model', 'Answering a question about the site’s figures, and the weekly insights.'),
|
|
99
100
|
modelField('job.layout', 'Generation job (layout) model', 'Generating a shared layout from a confirmed plan.'),
|
|
100
101
|
modelField('job.logic', 'Generation job (logic) model', 'Proposing a site function or variable, and explaining a function.'),
|
|
102
|
+
modelField('job.edit', 'Generation job (edit) model', 'Changing a page or layout the site already has, into a version visitors do not see.'),
|
|
101
103
|
modelField('job.template', 'Generation job (template) model', 'Generating a page template from a confirmed plan.'),
|
|
102
104
|
modelField('job.page', 'Generation job (page) model', 'Generating a page, one section at a time, from a confirmed plan.'),
|
|
103
105
|
modelField('job.products', 'Generation job (products) model', 'Writing product copy, and proposing products, categories and discounts for a store.'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/plugins/ai/src/lib/plugin-config.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PluginConfigSchema } from '@aglyn/aglyn'\nimport { AI_PLUGIN_SETTINGS_ID } from './constants'\nimport { AI_CATALOG_PROVIDERS, AI_MODEL_CATALOG, type AiStepKind } from './providers/catalog'\n\n/**\n * The org's AI settings (AGL-2939), stored in `pluginSettings/ai` and\n * inherited per site the way every plugin setting is (AGL-428): which\n * provider serves the workspace, and which catalog model each kind of step\n * runs on. `platform` means \"whatever the deployment's environment says\",\n * which is what every workspace runs with until it chooses.\n *\n * PURE DATA (type-only aglyn import): the client barrel registers it via\n * '@aglyn/aglyn' and the /server entry via '@aglyn/aglyn/server'.\n */\n\n/** The value that defers to the deployment's environment. */\nexport const AI_SETTING_PLATFORM = 'platform'\n\n/** The setting key each step kind's model override is stored under. */\nexport const AI_STEP_MODEL_SETTING: Record<AiStepKind, string> = {\n 'assist.chat': 'chatModel',\n 'copy.element': 'copyElementModel',\n 'copy.section': 'copySectionModel',\n 'copy.blog': 'copyBlogModel',\n 'generate.section': 'generateModel',\n 'job.component': 'jobComponentModel',\n 'job.crm': 'jobCrmModel',\n 'job.experiment': 'jobExperimentModel',\n 'job.form': 'jobFormModel',\n 'job.insight': 'jobInsightModel',\n 'job.layout': 'jobLayoutModel',\n 'job.logic': 'jobLogicModel',\n 'job.template': 'jobTemplateModel',\n 'job.page': 'jobPageModel',\n 'job.products': 'jobProductsModel',\n 'job.seo': 'jobSeoModel',\n 'job.text': 'jobTextModel',\n 'job.theme': 'jobThemeModel',\n 'job.plan': 'jobPlanModel',\n 'job.email': 'jobEmailModel',\n 'job.campaign': 'jobCampaignModel',\n 'job.workflow': 'jobWorkflowModel',\n}\n\nconst MODEL_OPTIONS = [\n { value: AI_SETTING_PLATFORM, label: 'Platform default' },\n ...AI_MODEL_CATALOG.map((entry) => ({\n value: entry.id,\n label: `${entry.label} (${entry.provider})`,\n })),\n]\n\nconst modelField = (kind: AiStepKind, label: string, description: string) => ({\n key: AI_STEP_MODEL_SETTING[kind],\n label,\n type: 'select' as const,\n options: MODEL_OPTIONS,\n description,\n})\n\nexport const AI_CONFIG_SCHEMA: PluginConfigSchema = {\n pluginId: AI_PLUGIN_SETTINGS_ID,\n fields: [\n {\n key: 'provider',\n label: 'AI provider',\n type: 'select',\n options: [\n { value: AI_SETTING_PLATFORM, label: 'Platform default' },\n ...AI_CATALOG_PROVIDERS.map((provider) => ({ value: provider.id, label: provider.label })),\n ],\n description:\n 'Which provider answers this workspace. A model chosen below must ' +\n 'be one the provider serves; otherwise the provider’s default for ' +\n 'that kind of step is used.',\n },\n modelField('assist.chat', 'Assistant model', 'The in-console assistant’s answers.'),\n modelField('copy.element', 'Copy assistant (element) model', 'Rewriting one element’s text.'),\n modelField('copy.section', 'Copy assistant (section) model', 'Writing a section’s copy.'),\n modelField('copy.blog', 'Copy assistant (blog) model', 'Drafting a blog post.'),\n modelField('generate.section', 'Section generation model', 'Generating a section’s structure.'),\n modelField('job.component', 'Generation job (component) model', 'Generating a reusable component and its properties.'),\n modelField('job.crm', 'Generation job (CRM) model', 'CRM record summaries and next steps, email drafts and import column matches.'),\n modelField(\n 'job.experiment',\n 'Generation job (A/B test) model',\n 'Writing variants for an A/B test, and explaining what a test’s result means.',\n ),\n modelField('job.form', 'Generation job (form) model', 'Generating a form from a confirmed plan.'),\n modelField('job.insight', 'Insights model', 'Answering a question about the site’s figures, and the weekly insights.'),\n modelField('job.layout', 'Generation job (layout) model', 'Generating a shared layout from a confirmed plan.'),\n modelField('job.logic', 'Generation job (logic) model', 'Proposing a site function or variable, and explaining a function.'),\n modelField('job.template', 'Generation job (template) model', 'Generating a page template from a confirmed plan.'),\n modelField('job.page', 'Generation job (page) model', 'Generating a page, one section at a time, from a confirmed plan.'),\n modelField('job.products', 'Generation job (products) model', 'Writing product copy, and proposing products, categories and discounts for a store.'),\n modelField('job.seo', 'Generation job (SEO) model', 'Search listings, image descriptions and a site audit’s fixes.'),\n modelField('job.text', 'Generation job (text) model', 'The text step of a generation job.'),\n modelField('job.theme', 'Generation job (theme) model', 'Proposing a change to a site’s theme.'),\n modelField('job.plan', 'Generation job (plan) model', 'The plan step of a generation job.'),\n modelField('job.email', 'Generation job (email) model', 'Generating an email design from a confirmed plan.'),\n modelField('job.campaign', 'Generation job (campaign) model', 'Drafting a campaign and its email from a brief.'),\n modelField('job.workflow', 'Generation job (automation) model', 'Drafting an automation from a description, and explaining one.'),\n ],\n defaults: {\n provider: AI_SETTING_PLATFORM,\n ...Object.fromEntries(\n Object.values(AI_STEP_MODEL_SETTING).map((key) => [key, AI_SETTING_PLATFORM]),\n ),\n },\n}\n"],"names":["AI_PLUGIN_SETTINGS_ID","AI_CATALOG_PROVIDERS","AI_MODEL_CATALOG","AI_SETTING_PLATFORM","AI_STEP_MODEL_SETTING","MODEL_OPTIONS","value","label","map","entry","id","provider","modelField","kind","description","key","type","options","AI_CONFIG_SCHEMA","pluginId","fields","defaults","Object","fromEntries","values"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SAASA,qBAAqB,QAAQ,iBAAa;AACnD,SAASC,oBAAoB,EAAEC,gBAAgB,QAAyB,yBAAqB;AAE7F;;;;;;;;;CASC,GAED,2DAA2D,GAC3D,OAAO,MAAMC,sBAAsB,WAAU;AAE7C,qEAAqE,GACrE,OAAO,MAAMC,wBAAoD;IAC/D,eAAe;IACf,gBAAgB;IAChB,gBAAgB;IAChB,aAAa;IACb,oBAAoB;IACpB,iBAAiB;IACjB,WAAW;IACX,kBAAkB;IAClB,YAAY;IACZ,eAAe;IACf,cAAc;IACd,aAAa;IACb,gBAAgB;IAChB,YAAY;IACZ,gBAAgB;IAChB,WAAW;IACX,YAAY;IACZ,aAAa;IACb,YAAY;IACZ,aAAa;IACb,gBAAgB;IAChB,gBAAgB;AAClB,EAAC;AAED,MAAMC,gBAAgB;IACpB;QAAEC,OAAOH;QAAqBI,OAAO;IAAmB;OACrDL,iBAAiBM,GAAG,CAAC,CAACC,QAAW,CAAA;YAClCH,OAAOG,MAAMC,EAAE;YACfH,OAAO,GAAGE,MAAMF,KAAK,CAAC,EAAE,EAAEE,MAAME,QAAQ,CAAC,CAAC,CAAC;QAC7C,CAAA;CACD;AAED,MAAMC,aAAa,CAACC,MAAkBN,OAAeO,cAAyB,CAAA;QAC5EC,KAAKX,qBAAqB,CAACS,KAAK;QAChCN;QACAS,MAAM;QACNC,SAASZ;QACTS;IACF,CAAA;AAEA,OAAO,MAAMI,mBAAuC;IAClDC,UAAUnB;IACVoB,QAAQ;QACN;YACEL,KAAK;YACLR,OAAO;YACPS,MAAM;YACNC,SAAS;gBACP;oBAAEX,OAAOH;oBAAqBI,OAAO;gBAAmB;mBACrDN,qBAAqBO,GAAG,CAAC,CAACG,WAAc,CAAA;wBAAEL,OAAOK,SAASD,EAAE;wBAAEH,OAAOI,SAASJ,KAAK;oBAAC,CAAA;aACxF;YACDO,aACE,sEACA,sEACA;QACJ;QACAF,WAAW,eAAe,mBAAmB;QAC7CA,WAAW,gBAAgB,kCAAkC;QAC7DA,WAAW,gBAAgB,kCAAkC;QAC7DA,WAAW,aAAa,+BAA+B;QACvDA,WAAW,oBAAoB,4BAA4B;QAC3DA,WAAW,iBAAiB,oCAAoC;QAChEA,WAAW,WAAW,8BAA8B;QACpDA,WACE,kBACA,mCACA;QAEFA,WAAW,YAAY,+BAA+B;QACtDA,WAAW,eAAe,kBAAkB;QAC5CA,WAAW,cAAc,iCAAiC;QAC1DA,WAAW,aAAa,gCAAgC;QACxDA,WAAW,gBAAgB,mCAAmC;QAC9DA,WAAW,YAAY,+BAA+B;QACtDA,WAAW,gBAAgB,mCAAmC;QAC9DA,WAAW,WAAW,8BAA8B;QACpDA,WAAW,YAAY,+BAA+B;QACtDA,WAAW,aAAa,gCAAgC;QACxDA,WAAW,YAAY,+BAA+B;QACtDA,WAAW,aAAa,gCAAgC;QACxDA,WAAW,gBAAgB,mCAAmC;QAC9DA,WAAW,gBAAgB,qCAAqC;KACjE;IACDS,UAAU;QACRV,UAAUR;OACPmB,OAAOC,WAAW,CACnBD,OAAOE,MAAM,CAACpB,uBAAuBI,GAAG,CAAC,CAACO,MAAQ;YAACA;YAAKZ;SAAoB;AAGlF,EAAC"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/plugins/ai/src/lib/plugin-config.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PluginConfigSchema } from '@aglyn/aglyn'\nimport { AI_PLUGIN_SETTINGS_ID } from './constants'\nimport { AI_CATALOG_PROVIDERS, AI_MODEL_CATALOG, type AiStepKind } from './providers/catalog'\n\n/**\n * The org's AI settings (AGL-2939), stored in `pluginSettings/ai` and\n * inherited per site the way every plugin setting is (AGL-428): which\n * provider serves the workspace, and which catalog model each kind of step\n * runs on. `platform` means \"whatever the deployment's environment says\",\n * which is what every workspace runs with until it chooses.\n *\n * PURE DATA (type-only aglyn import): the client barrel registers it via\n * '@aglyn/aglyn' and the /server entry via '@aglyn/aglyn/server'.\n */\n\n/** The value that defers to the deployment's environment. */\nexport const AI_SETTING_PLATFORM = 'platform'\n\n/** The setting key each step kind's model override is stored under. */\nexport const AI_STEP_MODEL_SETTING: Record<AiStepKind, string> = {\n 'assist.chat': 'chatModel',\n 'copy.element': 'copyElementModel',\n 'copy.section': 'copySectionModel',\n 'copy.blog': 'copyBlogModel',\n 'generate.section': 'generateModel',\n 'job.component': 'jobComponentModel',\n 'job.crm': 'jobCrmModel',\n 'job.experiment': 'jobExperimentModel',\n 'job.form': 'jobFormModel',\n 'job.insight': 'jobInsightModel',\n 'job.layout': 'jobLayoutModel',\n 'job.logic': 'jobLogicModel',\n 'job.edit': 'jobEditModel',\n 'job.template': 'jobTemplateModel',\n 'job.page': 'jobPageModel',\n 'job.products': 'jobProductsModel',\n 'job.seo': 'jobSeoModel',\n 'job.text': 'jobTextModel',\n 'job.theme': 'jobThemeModel',\n 'job.plan': 'jobPlanModel',\n 'job.email': 'jobEmailModel',\n 'job.campaign': 'jobCampaignModel',\n 'job.workflow': 'jobWorkflowModel',\n}\n\nconst MODEL_OPTIONS = [\n { value: AI_SETTING_PLATFORM, label: 'Platform default' },\n ...AI_MODEL_CATALOG.map((entry) => ({\n value: entry.id,\n label: `${entry.label} (${entry.provider})`,\n })),\n]\n\nconst modelField = (kind: AiStepKind, label: string, description: string) => ({\n key: AI_STEP_MODEL_SETTING[kind],\n label,\n type: 'select' as const,\n options: MODEL_OPTIONS,\n description,\n})\n\nexport const AI_CONFIG_SCHEMA: PluginConfigSchema = {\n pluginId: AI_PLUGIN_SETTINGS_ID,\n fields: [\n {\n key: 'provider',\n label: 'AI provider',\n type: 'select',\n options: [\n { value: AI_SETTING_PLATFORM, label: 'Platform default' },\n ...AI_CATALOG_PROVIDERS.map((provider) => ({ value: provider.id, label: provider.label })),\n ],\n description:\n 'Which provider answers this workspace. A model chosen below must ' +\n 'be one the provider serves; otherwise the provider’s default for ' +\n 'that kind of step is used.',\n },\n modelField('assist.chat', 'Assistant model', 'The in-console assistant’s answers.'),\n modelField('copy.element', 'Copy assistant (element) model', 'Rewriting one element’s text.'),\n modelField('copy.section', 'Copy assistant (section) model', 'Writing a section’s copy.'),\n modelField('copy.blog', 'Copy assistant (blog) model', 'Drafting a blog post.'),\n modelField('generate.section', 'Section generation model', 'Generating a section’s structure.'),\n modelField('job.component', 'Generation job (component) model', 'Generating a reusable component and its properties.'),\n modelField('job.crm', 'Generation job (CRM) model', 'CRM record summaries and next steps, email drafts and import column matches.'),\n modelField(\n 'job.experiment',\n 'Generation job (A/B test) model',\n 'Writing variants for an A/B test, and explaining what a test’s result means.',\n ),\n modelField('job.form', 'Generation job (form) model', 'Generating a form from a confirmed plan.'),\n modelField('job.insight', 'Insights model', 'Answering a question about the site’s figures, and the weekly insights.'),\n modelField('job.layout', 'Generation job (layout) model', 'Generating a shared layout from a confirmed plan.'),\n modelField('job.logic', 'Generation job (logic) model', 'Proposing a site function or variable, and explaining a function.'),\n modelField('job.edit', 'Generation job (edit) model', 'Changing a page or layout the site already has, into a version visitors do not see.'),\n modelField('job.template', 'Generation job (template) model', 'Generating a page template from a confirmed plan.'),\n modelField('job.page', 'Generation job (page) model', 'Generating a page, one section at a time, from a confirmed plan.'),\n modelField('job.products', 'Generation job (products) model', 'Writing product copy, and proposing products, categories and discounts for a store.'),\n modelField('job.seo', 'Generation job (SEO) model', 'Search listings, image descriptions and a site audit’s fixes.'),\n modelField('job.text', 'Generation job (text) model', 'The text step of a generation job.'),\n modelField('job.theme', 'Generation job (theme) model', 'Proposing a change to a site’s theme.'),\n modelField('job.plan', 'Generation job (plan) model', 'The plan step of a generation job.'),\n modelField('job.email', 'Generation job (email) model', 'Generating an email design from a confirmed plan.'),\n modelField('job.campaign', 'Generation job (campaign) model', 'Drafting a campaign and its email from a brief.'),\n modelField('job.workflow', 'Generation job (automation) model', 'Drafting an automation from a description, and explaining one.'),\n ],\n defaults: {\n provider: AI_SETTING_PLATFORM,\n ...Object.fromEntries(\n Object.values(AI_STEP_MODEL_SETTING).map((key) => [key, AI_SETTING_PLATFORM]),\n ),\n },\n}\n"],"names":["AI_PLUGIN_SETTINGS_ID","AI_CATALOG_PROVIDERS","AI_MODEL_CATALOG","AI_SETTING_PLATFORM","AI_STEP_MODEL_SETTING","MODEL_OPTIONS","value","label","map","entry","id","provider","modelField","kind","description","key","type","options","AI_CONFIG_SCHEMA","pluginId","fields","defaults","Object","fromEntries","values"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SAASA,qBAAqB,QAAQ,iBAAa;AACnD,SAASC,oBAAoB,EAAEC,gBAAgB,QAAyB,yBAAqB;AAE7F;;;;;;;;;CASC,GAED,2DAA2D,GAC3D,OAAO,MAAMC,sBAAsB,WAAU;AAE7C,qEAAqE,GACrE,OAAO,MAAMC,wBAAoD;IAC/D,eAAe;IACf,gBAAgB;IAChB,gBAAgB;IAChB,aAAa;IACb,oBAAoB;IACpB,iBAAiB;IACjB,WAAW;IACX,kBAAkB;IAClB,YAAY;IACZ,eAAe;IACf,cAAc;IACd,aAAa;IACb,YAAY;IACZ,gBAAgB;IAChB,YAAY;IACZ,gBAAgB;IAChB,WAAW;IACX,YAAY;IACZ,aAAa;IACb,YAAY;IACZ,aAAa;IACb,gBAAgB;IAChB,gBAAgB;AAClB,EAAC;AAED,MAAMC,gBAAgB;IACpB;QAAEC,OAAOH;QAAqBI,OAAO;IAAmB;OACrDL,iBAAiBM,GAAG,CAAC,CAACC,QAAW,CAAA;YAClCH,OAAOG,MAAMC,EAAE;YACfH,OAAO,GAAGE,MAAMF,KAAK,CAAC,EAAE,EAAEE,MAAME,QAAQ,CAAC,CAAC,CAAC;QAC7C,CAAA;CACD;AAED,MAAMC,aAAa,CAACC,MAAkBN,OAAeO,cAAyB,CAAA;QAC5EC,KAAKX,qBAAqB,CAACS,KAAK;QAChCN;QACAS,MAAM;QACNC,SAASZ;QACTS;IACF,CAAA;AAEA,OAAO,MAAMI,mBAAuC;IAClDC,UAAUnB;IACVoB,QAAQ;QACN;YACEL,KAAK;YACLR,OAAO;YACPS,MAAM;YACNC,SAAS;gBACP;oBAAEX,OAAOH;oBAAqBI,OAAO;gBAAmB;mBACrDN,qBAAqBO,GAAG,CAAC,CAACG,WAAc,CAAA;wBAAEL,OAAOK,SAASD,EAAE;wBAAEH,OAAOI,SAASJ,KAAK;oBAAC,CAAA;aACxF;YACDO,aACE,sEACA,sEACA;QACJ;QACAF,WAAW,eAAe,mBAAmB;QAC7CA,WAAW,gBAAgB,kCAAkC;QAC7DA,WAAW,gBAAgB,kCAAkC;QAC7DA,WAAW,aAAa,+BAA+B;QACvDA,WAAW,oBAAoB,4BAA4B;QAC3DA,WAAW,iBAAiB,oCAAoC;QAChEA,WAAW,WAAW,8BAA8B;QACpDA,WACE,kBACA,mCACA;QAEFA,WAAW,YAAY,+BAA+B;QACtDA,WAAW,eAAe,kBAAkB;QAC5CA,WAAW,cAAc,iCAAiC;QAC1DA,WAAW,aAAa,gCAAgC;QACxDA,WAAW,YAAY,+BAA+B;QACtDA,WAAW,gBAAgB,mCAAmC;QAC9DA,WAAW,YAAY,+BAA+B;QACtDA,WAAW,gBAAgB,mCAAmC;QAC9DA,WAAW,WAAW,8BAA8B;QACpDA,WAAW,YAAY,+BAA+B;QACtDA,WAAW,aAAa,gCAAgC;QACxDA,WAAW,YAAY,+BAA+B;QACtDA,WAAW,aAAa,gCAAgC;QACxDA,WAAW,gBAAgB,mCAAmC;QAC9DA,WAAW,gBAAgB,qCAAqC;KACjE;IACDS,UAAU;QACRV,UAAUR;OACPmB,OAAOC,WAAW,CACnBD,OAAOE,MAAM,CAACpB,uBAAuBI,GAAG,CAAC,CAACO,MAAQ;YAACA;YAAKZ;SAAoB;AAGlF,EAAC"}
|
package/src/lib/plugin.js
CHANGED
|
@@ -13,56 +13,90 @@
|
|
|
13
13
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
|
-
*/ import { PLATFORM_BRAND_NAME, registerConsoleExtension } from "@aglyn/aglyn";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { AiAssistProviderOnHost, AssistPanelOnHost } from "./components/ai-permissions-on-host.component.js";
|
|
21
|
-
import AiCreditsCard from "./components/ai-credits-card.component.js";
|
|
22
|
-
import AiJobsTopBarIndicator from "./components/ai-jobs-indicator.component.js";
|
|
23
|
-
import { AiCollaboratorCreditsCell, AiCollaboratorCreditsHeader, AiMemberCreditsCell, AiMemberCreditsHeader } from "./components/ai-credits-columns.component.js";
|
|
24
|
-
import { AssistSignalsPage } from "./components/assist-signals-page.component.js";
|
|
25
|
-
import BillingAssistOverageCard from "./components/billing-assist-overage-card.component.js";
|
|
26
|
-
import { AiTopUsersCard } from "./components/billing-ai-top-users.component.js";
|
|
27
|
-
import { AiAllotmentsCard } from "./components/billing-ai-allotments.component.js";
|
|
28
|
-
import { AiCollaboratorAllotmentCell, AiCollaboratorAllotmentHeader, AiSiteAllotmentCard } from "./components/host-ai-allotments.component.js";
|
|
29
|
-
import MemberAiAllotmentCard from "./components/member-ai-allotment-card.component.js";
|
|
30
|
-
import MemberAiUsageCard from "./components/member-ai-usage-card.component.js";
|
|
31
|
-
import AiThemeProposalCard from "./components/ai-theme-proposal-card.component.js";
|
|
32
|
-
import StaffOrgAiCard from "./components/staff-org-ai-card.component.js";
|
|
33
|
-
import { StaffOrgUsageAiCreditsCell, StaffOrgUsageAiOverageCell, StaffOrgUsageAiPool, StaffOrgUsageAssistCell } from "./components/staff-org-usage-ai-columns.component.js";
|
|
34
|
-
import { StaffOrgsAiSpendCell, StaffOrgsAiSpendHeader } from "./components/staff-orgs-ai-spend-column.component.js";
|
|
35
|
-
import StaffUserAiUsageCard from "./components/staff-user-ai-usage-card.component.js";
|
|
36
|
-
import AiSeoAuditCard from "./components/ai-seo-audit-card.component.js";
|
|
37
|
-
import AiSiteBatchCard from "./components/ai-site-batch-card.component.js";
|
|
38
|
-
import AiSiteSeoStartCard from "./components/ai-site-seo-start-card.component.js";
|
|
39
|
-
import AiSiteStartCard from "./components/ai-site-start-card.component.js";
|
|
40
|
-
import AiSeoFieldsCard from "./components/ai-seo-fields-card.component.js";
|
|
41
|
-
import AiDescribeAutomationButton from "./components/ai-describe-automation.component.js";
|
|
42
|
-
import AiReviseAutomation from "./components/ai-revise-automation.component.js";
|
|
43
|
-
import AiDescribeOrgAutomationButton from "./components/ai-describe-org-automation.component.js";
|
|
44
|
-
import { AiInsightHostCard, AiInsightOrgCard } from "./components/ai-insight-card.component.js";
|
|
45
|
-
import { AiLogicCreateButton, AiLogicFixReference, AiLogicFunctionTools } from "./components/ai-logic.component.js";
|
|
46
|
-
import { AiFunnelAskButton, AiFunnelCreateButton } from "./components/ai-funnel-zones.component.js";
|
|
47
|
-
import AiCrmEmailDraft from "./components/ai-crm-email-draft.component.js";
|
|
48
|
-
import AiCrmImportMapping from "./components/ai-crm-import-mapping.component.js";
|
|
49
|
-
import AiCrmRecordCard from "./components/ai-crm-record-card.component.js";
|
|
50
|
-
import AiDescribePageButton from "./components/ai-describe-page.component.js";
|
|
51
|
-
import AiMediaCreateButton from "./components/ai-media-create.component.js";
|
|
52
|
-
import { AiDescribeComponentButton, AiDescribeFormButton, AiDescribeLayoutButton, AiDescribeTemplateButton } from "./components/ai-describe-button.component.js";
|
|
53
|
-
import { AiDescribeEmailButton } from "./components/ai-describe-email.component.js";
|
|
54
|
-
import { AiExperimentResultCard, AiExperimentVariantsCard } from "./components/ai-experiment-cards.component.js";
|
|
55
|
-
import { AiExplainAutomation, AiExplainRunFailure } from "./components/ai-explain-automation.component.js";
|
|
56
|
-
import AiCreateCampaignButton from "./components/ai-campaign-create.component.js";
|
|
57
|
-
import AiMarketingInsightButton from "./components/ai-marketing-insight.component.js";
|
|
58
|
-
import { AiCreateOverlayButton, AiOverlayEditorCard } from "./components/ai-overlay-cards.component.js";
|
|
59
|
-
import AiProductCopyCard from "./components/ai-product-copy-card.component.js";
|
|
60
|
-
import AiProductImportOption from "./components/ai-product-import-option.component.js";
|
|
61
|
-
import AiProductsHubCard from "./components/ai-products-hub-card.component.js";
|
|
62
|
-
import AiCreateProductsButton from "./components/ai-products-create-button.component.js";
|
|
63
|
-
import AiJobsPage from "./components/ai-jobs-page.component.js";
|
|
16
|
+
*/ import { PLATFORM_BRAND_NAME, aiAddonName, registerConsoleExtension } from "@aglyn/aglyn";
|
|
17
|
+
import { LinearProgress } from "@mui/material";
|
|
18
|
+
import { createElement } from "react";
|
|
19
|
+
import { AiAssistProviderOnHost } from "./components/ai-assist-provider-on-host.component.js";
|
|
64
20
|
import { AI_PLUGIN_ID } from "./constants.js";
|
|
65
21
|
import { registerAiDeclarations } from "./declarations.js";
|
|
22
|
+
import { lazyWidget } from "./lazy-widget.js";
|
|
23
|
+
/*
|
|
24
|
+
* Every component below is registered by name and loaded the first time the
|
|
25
|
+
* shell draws it (AGL-3649); see `lazyWidget`. The console loads this plugin
|
|
26
|
+
* on every screen, so a static import of a component here is downloaded by
|
|
27
|
+
* every reader of every page, staff cards included.
|
|
28
|
+
*/ /** What a page shows while its code arrives. */ const PAGE_LOADING = createElement(LinearProgress, {
|
|
29
|
+
'aria-label': 'Loading'
|
|
30
|
+
});
|
|
31
|
+
const AiCollaboratorPermissionsCell = lazyWidget('AiCollaboratorPermissionsCell', ()=>import("./components/ai-collaborator-permissions-column.component.js").then((m)=>m.AiCollaboratorPermissionsCell));
|
|
32
|
+
const AiSaveAsComponent = lazyWidget('AiSaveAsComponent', ()=>import("./components/ai-save-as-component.component.js").then((m)=>m.AiSaveAsComponent));
|
|
33
|
+
const AiGenerateSectionControl = lazyWidget('AiGenerateSectionControl', ()=>import("./components/besigner-ai-controls.component.js").then((m)=>m.AiGenerateSectionControl));
|
|
34
|
+
const AiRewriteControl = lazyWidget('AiRewriteControl', ()=>import("./components/besigner-ai-controls.component.js").then((m)=>m.AiRewriteControl));
|
|
35
|
+
const AssistPanelOnHost = lazyWidget('AssistPanelOnHost', ()=>import("./components/ai-permissions-on-host.component.js").then((m)=>m.AssistPanelOnHost));
|
|
36
|
+
const AiCreditsCard = lazyWidget('AiCreditsCard', ()=>import("./components/ai-credits-card.component.js").then((m)=>m.default));
|
|
37
|
+
const AiJobsTopBarIndicator = lazyWidget('AiJobsTopBarIndicator', ()=>import("./components/ai-jobs-indicator.component.js").then((m)=>m.default));
|
|
38
|
+
const AiCollaboratorCreditsCell = lazyWidget('AiCollaboratorCreditsCell', ()=>import("./components/ai-credits-columns.component.js").then((m)=>m.AiCollaboratorCreditsCell));
|
|
39
|
+
const AiCollaboratorCreditsHeader = lazyWidget('AiCollaboratorCreditsHeader', ()=>import("./components/ai-credits-columns.component.js").then((m)=>m.AiCollaboratorCreditsHeader));
|
|
40
|
+
const AiMemberCreditsCell = lazyWidget('AiMemberCreditsCell', ()=>import("./components/ai-credits-columns.component.js").then((m)=>m.AiMemberCreditsCell));
|
|
41
|
+
const AiMemberCreditsHeader = lazyWidget('AiMemberCreditsHeader', ()=>import("./components/ai-credits-columns.component.js").then((m)=>m.AiMemberCreditsHeader));
|
|
42
|
+
const AssistSignalsPage = lazyWidget('AssistSignalsPage', ()=>import("./components/assist-signals-page.component.js").then((m)=>m.AssistSignalsPage), PAGE_LOADING);
|
|
43
|
+
const BillingAssistOverageCard = lazyWidget('BillingAssistOverageCard', ()=>import("./components/billing-assist-overage-card.component.js").then((m)=>m.default));
|
|
44
|
+
const AiTopUsersCard = lazyWidget('AiTopUsersCard', ()=>import("./components/billing-ai-top-users.component.js").then((m)=>m.AiTopUsersCard));
|
|
45
|
+
const AiAllotmentsCard = lazyWidget('AiAllotmentsCard', ()=>import("./components/billing-ai-allotments.component.js").then((m)=>m.AiAllotmentsCard));
|
|
46
|
+
const AiCollaboratorAllotmentCell = lazyWidget('AiCollaboratorAllotmentCell', ()=>import("./components/host-ai-allotments.component.js").then((m)=>m.AiCollaboratorAllotmentCell));
|
|
47
|
+
const AiCollaboratorAllotmentHeader = lazyWidget('AiCollaboratorAllotmentHeader', ()=>import("./components/host-ai-allotments.component.js").then((m)=>m.AiCollaboratorAllotmentHeader));
|
|
48
|
+
const AiSiteAllotmentCard = lazyWidget('AiSiteAllotmentCard', ()=>import("./components/host-ai-allotments.component.js").then((m)=>m.AiSiteAllotmentCard));
|
|
49
|
+
const MemberAiAllotmentCard = lazyWidget('MemberAiAllotmentCard', ()=>import("./components/member-ai-allotment-card.component.js").then((m)=>m.default));
|
|
50
|
+
const MemberAiUsageCard = lazyWidget('MemberAiUsageCard', ()=>import("./components/member-ai-usage-card.component.js").then((m)=>m.default));
|
|
51
|
+
const AiThemeProposalCard = lazyWidget('AiThemeProposalCard', ()=>import("./components/ai-theme-proposal-card.component.js").then((m)=>m.default));
|
|
52
|
+
const StaffOrgAiCard = lazyWidget('StaffOrgAiCard', ()=>import("./components/staff-org-ai-card.component.js").then((m)=>m.default));
|
|
53
|
+
const StaffOrgAiConversations = lazyWidget('StaffOrgAiConversations', ()=>import("./components/staff-org-ai-conversations.component.js").then((m)=>m.default));
|
|
54
|
+
const StaffOrgUsageAiCreditsCell = lazyWidget('StaffOrgUsageAiCreditsCell', ()=>import("./components/staff-org-usage-ai-columns.component.js").then((m)=>m.StaffOrgUsageAiCreditsCell));
|
|
55
|
+
const StaffOrgUsageAiOverageCell = lazyWidget('StaffOrgUsageAiOverageCell', ()=>import("./components/staff-org-usage-ai-columns.component.js").then((m)=>m.StaffOrgUsageAiOverageCell));
|
|
56
|
+
const StaffOrgUsageAiPool = lazyWidget('StaffOrgUsageAiPool', ()=>import("./components/staff-org-usage-ai-columns.component.js").then((m)=>m.StaffOrgUsageAiPool));
|
|
57
|
+
const StaffOrgUsageAssistCell = lazyWidget('StaffOrgUsageAssistCell', ()=>import("./components/staff-org-usage-ai-columns.component.js").then((m)=>m.StaffOrgUsageAssistCell));
|
|
58
|
+
const StaffOrgsAiSpendCell = lazyWidget('StaffOrgsAiSpendCell', ()=>import("./components/staff-orgs-ai-spend-column.component.js").then((m)=>m.StaffOrgsAiSpendCell));
|
|
59
|
+
const StaffOrgsAiSpendHeader = lazyWidget('StaffOrgsAiSpendHeader', ()=>import("./components/staff-orgs-ai-spend-column.component.js").then((m)=>m.StaffOrgsAiSpendHeader));
|
|
60
|
+
const StaffUserAiUsageCard = lazyWidget('StaffUserAiUsageCard', ()=>import("./components/staff-user-ai-usage-card.component.js").then((m)=>m.default));
|
|
61
|
+
const AiSeoAuditCard = lazyWidget('AiSeoAuditCard', ()=>import("./components/ai-seo-audit-card.component.js").then((m)=>m.default));
|
|
62
|
+
const AiSiteBatchCard = lazyWidget('AiSiteBatchCard', ()=>import("./components/ai-site-batch-card.component.js").then((m)=>m.default));
|
|
63
|
+
const AiSiteSeoStartCard = lazyWidget('AiSiteSeoStartCard', ()=>import("./components/ai-site-seo-start-card.component.js").then((m)=>m.default));
|
|
64
|
+
const AiSiteMemoryCard = lazyWidget('AiSiteMemoryCard', ()=>import("./components/ai-site-memory-card.component.js").then((m)=>m.default));
|
|
65
|
+
const AiSiteStartCard = lazyWidget('AiSiteStartCard', ()=>import("./components/ai-site-start-card.component.js").then((m)=>m.default));
|
|
66
|
+
const AiSeoFieldsCard = lazyWidget('AiSeoFieldsCard', ()=>import("./components/ai-seo-fields-card.component.js").then((m)=>m.default));
|
|
67
|
+
const AiDescribeAutomationButton = lazyWidget('AiDescribeAutomationButton', ()=>import("./components/ai-describe-automation.component.js").then((m)=>m.default));
|
|
68
|
+
const AiReviseAutomation = lazyWidget('AiReviseAutomation', ()=>import("./components/ai-revise-automation.component.js").then((m)=>m.default));
|
|
69
|
+
const AiDescribeOrgAutomationButton = lazyWidget('AiDescribeOrgAutomationButton', ()=>import("./components/ai-describe-org-automation.component.js").then((m)=>m.default));
|
|
70
|
+
const AiInsightHostCard = lazyWidget('AiInsightHostCard', ()=>import("./components/ai-insight-card.component.js").then((m)=>m.AiInsightHostCard));
|
|
71
|
+
const AiInsightOrgCard = lazyWidget('AiInsightOrgCard', ()=>import("./components/ai-insight-card.component.js").then((m)=>m.AiInsightOrgCard));
|
|
72
|
+
const AiLogicCreateButton = lazyWidget('AiLogicCreateButton', ()=>import("./components/ai-logic.component.js").then((m)=>m.AiLogicCreateButton));
|
|
73
|
+
const AiLogicFixReference = lazyWidget('AiLogicFixReference', ()=>import("./components/ai-logic.component.js").then((m)=>m.AiLogicFixReference));
|
|
74
|
+
const AiLogicFunctionTools = lazyWidget('AiLogicFunctionTools', ()=>import("./components/ai-logic.component.js").then((m)=>m.AiLogicFunctionTools));
|
|
75
|
+
const AiFunnelAskButton = lazyWidget('AiFunnelAskButton', ()=>import("./components/ai-funnel-zones.component.js").then((m)=>m.AiFunnelAskButton));
|
|
76
|
+
const AiFunnelCreateButton = lazyWidget('AiFunnelCreateButton', ()=>import("./components/ai-funnel-zones.component.js").then((m)=>m.AiFunnelCreateButton));
|
|
77
|
+
const AiCrmEmailDraft = lazyWidget('AiCrmEmailDraft', ()=>import("./components/ai-crm-email-draft.component.js").then((m)=>m.default));
|
|
78
|
+
const AiCrmImportMapping = lazyWidget('AiCrmImportMapping', ()=>import("./components/ai-crm-import-mapping.component.js").then((m)=>m.default));
|
|
79
|
+
const AiCrmRecordCard = lazyWidget('AiCrmRecordCard', ()=>import("./components/ai-crm-record-card.component.js").then((m)=>m.default));
|
|
80
|
+
const AiDescribePageButton = lazyWidget('AiDescribePageButton', ()=>import("./components/ai-describe-page.component.js").then((m)=>m.default));
|
|
81
|
+
const AiMediaCreateButton = lazyWidget('AiMediaCreateButton', ()=>import("./components/ai-media-create.component.js").then((m)=>m.default));
|
|
82
|
+
const AiDescribeComponentButton = lazyWidget('AiDescribeComponentButton', ()=>import("./components/ai-describe-button.component.js").then((m)=>m.AiDescribeComponentButton));
|
|
83
|
+
const AiDescribeFormButton = lazyWidget('AiDescribeFormButton', ()=>import("./components/ai-describe-button.component.js").then((m)=>m.AiDescribeFormButton));
|
|
84
|
+
const AiDescribeLayoutButton = lazyWidget('AiDescribeLayoutButton', ()=>import("./components/ai-describe-button.component.js").then((m)=>m.AiDescribeLayoutButton));
|
|
85
|
+
const AiDescribeTemplateButton = lazyWidget('AiDescribeTemplateButton', ()=>import("./components/ai-describe-button.component.js").then((m)=>m.AiDescribeTemplateButton));
|
|
86
|
+
const AiDescribeEmailButton = lazyWidget('AiDescribeEmailButton', ()=>import("./components/ai-describe-email.component.js").then((m)=>m.AiDescribeEmailButton));
|
|
87
|
+
const AiExperimentResultCard = lazyWidget('AiExperimentResultCard', ()=>import("./components/ai-experiment-cards.component.js").then((m)=>m.AiExperimentResultCard));
|
|
88
|
+
const AiExperimentVariantsCard = lazyWidget('AiExperimentVariantsCard', ()=>import("./components/ai-experiment-cards.component.js").then((m)=>m.AiExperimentVariantsCard));
|
|
89
|
+
const AiExplainAutomation = lazyWidget('AiExplainAutomation', ()=>import("./components/ai-explain-automation.component.js").then((m)=>m.AiExplainAutomation));
|
|
90
|
+
const AiExplainRunFailure = lazyWidget('AiExplainRunFailure', ()=>import("./components/ai-explain-automation.component.js").then((m)=>m.AiExplainRunFailure));
|
|
91
|
+
const AiCreateCampaignButton = lazyWidget('AiCreateCampaignButton', ()=>import("./components/ai-campaign-create.component.js").then((m)=>m.default));
|
|
92
|
+
const AiMarketingInsightButton = lazyWidget('AiMarketingInsightButton', ()=>import("./components/ai-marketing-insight.component.js").then((m)=>m.default));
|
|
93
|
+
const AiCreateOverlayButton = lazyWidget('AiCreateOverlayButton', ()=>import("./components/ai-overlay-cards.component.js").then((m)=>m.AiCreateOverlayButton));
|
|
94
|
+
const AiOverlayEditorCard = lazyWidget('AiOverlayEditorCard', ()=>import("./components/ai-overlay-cards.component.js").then((m)=>m.AiOverlayEditorCard));
|
|
95
|
+
const AiProductCopyCard = lazyWidget('AiProductCopyCard', ()=>import("./components/ai-product-copy-card.component.js").then((m)=>m.default));
|
|
96
|
+
const AiProductImportOption = lazyWidget('AiProductImportOption', ()=>import("./components/ai-product-import-option.component.js").then((m)=>m.default));
|
|
97
|
+
const AiProductsHubCard = lazyWidget('AiProductsHubCard', ()=>import("./components/ai-products-hub-card.component.js").then((m)=>m.default));
|
|
98
|
+
const AiCreateProductsButton = lazyWidget('AiCreateProductsButton', ()=>import("./components/ai-products-create-button.component.js").then((m)=>m.default));
|
|
99
|
+
const AiJobsPage = lazyWidget('AiJobsPage', ()=>import("./components/ai-jobs-page.component.js").then((m)=>m.default), PAGE_LOADING);
|
|
66
100
|
/**
|
|
67
101
|
* The Aglyn AI plugin's console half (AGL-2939): the assistant dock, the
|
|
68
102
|
* besigner copy assistant's provider, the billing, member and staff cards,
|
|
@@ -261,6 +295,12 @@ import { registerAiDeclarations } from "./declarations.js";
|
|
|
261
295
|
title: 'AI usage',
|
|
262
296
|
Component: StaffOrgAiCard
|
|
263
297
|
},
|
|
298
|
+
{
|
|
299
|
+
slot: 'staffOrg',
|
|
300
|
+
widgetId: 'ai-org-conversations',
|
|
301
|
+
title: 'AI conversations',
|
|
302
|
+
Component: StaffOrgAiConversations
|
|
303
|
+
},
|
|
264
304
|
{
|
|
265
305
|
slot: 'staffUser',
|
|
266
306
|
widgetId: 'ai-account-usage',
|
|
@@ -647,6 +687,17 @@ import { registerAiDeclarations } from "./declarations.js";
|
|
|
647
687
|
permission: 'ai.generate',
|
|
648
688
|
Component: AiSiteSeoStartCard
|
|
649
689
|
},
|
|
690
|
+
// What Aglyn AI learned from the site's applied edits (AGL-3661), on
|
|
691
|
+
// Setup → Business profile, where the owner reads and forgets it. No
|
|
692
|
+
// generate permission: reading and clearing what the AI keeps about a
|
|
693
|
+
// site is every editor's, and the rules hold the write to the server.
|
|
694
|
+
{
|
|
695
|
+
slot: 'hostBusinessProfile',
|
|
696
|
+
widgetId: 'ai-site-memory',
|
|
697
|
+
title: `What ${aiAddonName()} learned`,
|
|
698
|
+
featureFlag: 'aiGenerative',
|
|
699
|
+
Component: AiSiteMemoryCard
|
|
700
|
+
},
|
|
650
701
|
// The agency batch (AGL-2911): one brief across many of the org's
|
|
651
702
|
// sites, from the page that lists them. The card asks the jobs route
|
|
652
703
|
// about the release flag before it shows anything, and its own door
|