@aglyn/plugins-ai 1.0.0-beta.270 → 1.0.0-beta.271
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.d.ts +1 -1
- package/src/lib/activity/ai-activity-actions.js +1 -0
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/assets/site-styles/beauty.webp +0 -0
- package/src/lib/assets/site-styles/blog.webp +0 -0
- package/src/lib/assets/site-styles/business.webp +0 -0
- package/src/lib/assets/site-styles/coming-soon.webp +0 -0
- package/src/lib/assets/site-styles/education.webp +0 -0
- package/src/lib/assets/site-styles/events.webp +0 -0
- package/src/lib/assets/site-styles/fitness.webp +0 -0
- package/src/lib/assets/site-styles/landing.webp +0 -0
- package/src/lib/assets/site-styles/music.webp +0 -0
- package/src/lib/assets/site-styles/nonprofit.webp +0 -0
- package/src/lib/assets/site-styles/personal.webp +0 -0
- package/src/lib/assets/site-styles/photography.webp +0 -0
- package/src/lib/assets/site-styles/portfolio.webp +0 -0
- package/src/lib/assets/site-styles/professional.webp +0 -0
- package/src/lib/assets/site-styles/realestate.webp +0 -0
- package/src/lib/assets/site-styles/restaurant.webp +0 -0
- package/src/lib/assets/site-styles/store.webp +0 -0
- package/src/lib/assets/site-styles/studio.webp +0 -0
- package/src/lib/assets/site-styles/trades.webp +0 -0
- package/src/lib/assets/site-styles/wellness.webp +0 -0
- package/src/lib/assets/site-styles/yoga.webp +0 -0
- package/src/lib/components/ai-credits-card.component.d.ts +4 -3
- package/src/lib/components/ai-credits-card.component.js +15 -15
- package/src/lib/components/ai-credits-card.component.js.map +1 -1
- package/src/lib/components/ai-job-input-form.component.d.ts +36 -0
- package/src/lib/components/ai-job-input-form.component.js +100 -0
- package/src/lib/components/ai-job-input-form.component.js.map +1 -0
- package/src/lib/components/ai-job-requests.d.ts +4 -0
- package/src/lib/components/ai-job-requests.js +4 -0
- package/src/lib/components/ai-job-requests.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
- package/src/lib/components/ai-site-start-card.component.js +99 -31
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-site-start-steps.component.d.ts +43 -2
- package/src/lib/components/ai-site-start-steps.component.js +651 -15
- package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
- package/src/lib/components/ai-site-start-tracking.d.ts +10 -0
- package/src/lib/components/ai-site-start-tracking.js +14 -0
- package/src/lib/components/ai-site-start-tracking.js.map +1 -1
- package/src/lib/components/ai-site-style-previews.js +26 -22
- package/src/lib/components/ai-site-style-previews.js.map +1 -1
- package/src/lib/components/ai-upsell-dialog.component.d.ts +7 -5
- package/src/lib/components/ai-upsell-dialog.component.js +8 -6
- package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
- package/src/lib/components/assist-build-card.component.js +11 -1
- package/src/lib/components/assist-build-card.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +9 -1
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +8 -4
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-card.component.js +4 -7
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
- package/src/lib/declarations.d.ts +2 -2
- package/src/lib/declarations.js +9 -5
- package/src/lib/declarations.js.map +1 -1
- package/src/lib/jobs/ai-build-capabilities.js +3 -1
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.d.ts +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.js +14 -3
- package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +5 -3
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-form-step.d.ts +15 -1
- package/src/lib/jobs/ai-job-form-step.js +75 -8
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +13 -0
- package/src/lib/jobs/ai-job-page-language.js +37 -1
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +17 -0
- package/src/lib/jobs/ai-job-page-step.js +37 -16
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +25 -2
- package/src/lib/jobs/ai-job-plan-step.js +103 -29
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-settings-step.js +4 -3
- package/src/lib/jobs/ai-job-settings-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +27 -0
- package/src/lib/jobs/ai-job-site-datasets.js +140 -14
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-job-site-look.js +18 -5
- package/src/lib/jobs/ai-job-site-look.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +16 -1
- package/src/lib/jobs/ai-job-site-step.js +95 -22
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +8 -1
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-job-tracking-step.d.ts +14 -3
- package/src/lib/jobs/ai-job-tracking-step.js +28 -5
- package/src/lib/jobs/ai-job-tracking-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +8 -1
- package/src/lib/jobs/ai-jobs.js +63 -8
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +27 -2
- package/src/lib/jobs/ai-layout-stock-photos.js +190 -19
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-inspiration-signals.d.ts +98 -0
- package/src/lib/jobs/ai-site-inspiration-signals.js +585 -0
- package/src/lib/jobs/ai-site-inspiration-signals.js.map +1 -0
- package/src/lib/jobs/ai-site-inspiration.d.ts +157 -0
- package/src/lib/jobs/ai-site-inspiration.js +346 -0
- package/src/lib/jobs/ai-site-inspiration.js.map +1 -0
- package/src/lib/jobs/ai-site-publish.d.ts +26 -1
- package/src/lib/jobs/ai-site-publish.js +162 -13
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/ai-theme-brand-inputs.d.ts +16 -0
- package/src/lib/jobs/ai-theme-brand-inputs.js +18 -4
- package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.d.ts +97 -0
- package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.js +137 -0
- package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-site-inspiration-pages.d.ts +38 -0
- package/src/lib/jobs/fixtures/ai-site-inspiration-pages.js +103 -0
- package/src/lib/jobs/fixtures/ai-site-inspiration-pages.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.d.ts +25 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.js +174 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +1 -1
- package/src/lib/layout-language/ai-layout-compiler.js +438 -25
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-contact.d.ts +57 -0
- package/src/lib/layout-language/ai-layout-contact.js +137 -0
- package/src/lib/layout-language/ai-layout-contact.js.map +1 -0
- package/src/lib/layout-language/ai-layout-copy.d.ts +12 -0
- package/src/lib/layout-language/ai-layout-copy.js +34 -2
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -1
- package/src/lib/layout-language/ai-layout-design.d.ts +65 -8
- package/src/lib/layout-language/ai-layout-design.js +411 -97
- package/src/lib/layout-language/ai-layout-design.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.js +23 -0
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-labels.d.ts +2 -0
- package/src/lib/layout-language/ai-layout-labels.js +12 -6
- package/src/lib/layout-language/ai-layout-labels.js.map +1 -1
- package/src/lib/layout-language/ai-layout-language.d.ts +3 -2
- package/src/lib/layout-language/ai-layout-language.js +4 -3
- package/src/lib/layout-language/ai-layout-language.js.map +1 -1
- package/src/lib/layout-language/ai-layout-links.d.ts +13 -0
- package/src/lib/layout-language/ai-layout-links.js +30 -0
- package/src/lib/layout-language/ai-layout-links.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +49 -6
- package/src/lib/layout-language/ai-layout-pictures.js +145 -22
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/model/ai-build-job.d.ts +16 -1
- package/src/lib/model/ai-build-job.js +40 -0
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-job-input.d.ts +33 -0
- package/src/lib/model/ai-job-input.js +59 -0
- package/src/lib/model/ai-job-input.js.map +1 -0
- package/src/lib/model/ai-job-notice.d.ts +4 -0
- package/src/lib/model/ai-job-notice.js +11 -1
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +40 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-site-details.d.ts +122 -0
- package/src/lib/model/ai-site-details.js +300 -0
- package/src/lib/model/ai-site-details.js.map +1 -0
- package/src/lib/model/ai-site-entity.d.ts +181 -0
- package/src/lib/model/ai-site-entity.js +390 -0
- package/src/lib/model/ai-site-entity.js.map +1 -0
- package/src/lib/model/ai-site-inspiration.d.ts +72 -0
- package/src/lib/model/ai-site-inspiration.js +109 -0
- package/src/lib/model/ai-site-inspiration.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +13 -1
- package/src/lib/model/ai-site-job.js +15 -2
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.js +109 -21
- package/src/lib/model/ai-site-kinds.js.map +1 -1
- package/src/lib/model/ai-site-look.d.ts +10 -0
- package/src/lib/model/ai-site-look.js +24 -0
- package/src/lib/model/ai-site-look.js.map +1 -1
- package/src/lib/model/ai-site-start-seo.d.ts +19 -5
- package/src/lib/model/ai-site-start-seo.js +25 -7
- package/src/lib/model/ai-site-start-seo.js.map +1 -1
- package/src/lib/model/ai-site-start-steps.d.ts +9 -3
- package/src/lib/model/ai-site-start-steps.js +67 -5
- package/src/lib/model/ai-site-start-steps.js.map +1 -1
- package/src/lib/model/ai-site-start.d.ts +68 -4
- package/src/lib/model/ai-site-start.js +166 -13
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/ai-upsell-copy.js +4 -1
- package/src/lib/model/ai-upsell-copy.js.map +1 -1
- package/src/lib/plan-entitlements.d.ts +4 -3
- package/src/lib/plan-entitlements.js +3 -8
- package/src/lib/plan-entitlements.js.map +1 -1
- package/src/lib/runtime/ai-dataset-generation.d.ts +8 -1
- package/src/lib/runtime/ai-dataset-generation.js +12 -2
- package/src/lib/runtime/ai-dataset-generation.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +26 -33
- package/src/lib/runtime/ai-doctrine-validators.js +54 -44
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +1 -1
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.d.ts +7 -0
- package/src/lib/runtime/ai-node-tree.js +9 -1
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/server/ai-create-route.d.ts +1 -1
- package/src/lib/server/ai-create-route.js +3 -4
- package/src/lib/server/ai-create-route.js.map +1 -1
- package/src/lib/server/ai-jobs-resume.js +27 -3
- package/src/lib/server/ai-jobs-resume.js.map +1 -1
- package/src/lib/server/ai-jobs-route.js +7 -0
- package/src/lib/server/ai-jobs-route.js.map +1 -1
- package/src/lib/usage/assist-credits.d.ts +4 -4
- package/src/lib/usage/assist-credits.js +4 -4
- package/src/lib/usage/assist-credits.js.map +1 -1
- package/src/lib/usage/staff-org-ai-view.js +6 -3
- package/src/lib/usage/staff-org-ai-view.js.map +1 -1
- package/src/lib/usage/staff-org-ai.d.ts +4 -2
- package/src/lib/usage/staff-org-ai.js +7 -7
- package/src/lib/usage/staff-org-ai.js.map +1 -1
- package/src/lib/usage/staff-org-usage-ai.js +2 -2
- package/src/lib/usage/staff-org-usage-ai.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-ai",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.271",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,22 +25,22 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-data-types": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
35
|
-
"@aglyn/shared-ui-theme": "1.0.0-beta.
|
|
36
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
37
|
-
"@aglyn/shared-util-errors": "1.0.0-beta.
|
|
38
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
39
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
40
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
41
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
42
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
43
|
-
"@aglyn/tenant-runtime": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.271",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.271",
|
|
30
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.271",
|
|
31
|
+
"@aglyn/shared-data-types": "1.0.0-beta.271",
|
|
32
|
+
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.271",
|
|
33
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.271",
|
|
34
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.271",
|
|
35
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.271",
|
|
36
|
+
"@aglyn/shared-util-email": "1.0.0-beta.271",
|
|
37
|
+
"@aglyn/shared-util-errors": "1.0.0-beta.271",
|
|
38
|
+
"@aglyn/shared-util-http": "1.0.0-beta.271",
|
|
39
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.271",
|
|
40
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.271",
|
|
41
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.271",
|
|
42
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.271",
|
|
43
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.271",
|
|
44
44
|
"@swc/helpers": "0.5.23"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
@@ -89,7 +89,7 @@ export declare const AI_ACTIVITY_FILTER_LABEL = "AI";
|
|
|
89
89
|
* (AGL-2935), or the site is at an allowance its plan sets for what the job
|
|
90
90
|
* writes (AGL-2909).
|
|
91
91
|
*/
|
|
92
|
-
export type AiJobNeedsInputReason = 'band' | 'cap' | 'messages' | 'budget' | 'allotment' | 'account' | 'requests' | 'refusals' | 'platform' | 'plan' | 'doctrine' | 'limit';
|
|
92
|
+
export type AiJobNeedsInputReason = 'band' | 'cap' | 'messages' | 'budget' | 'allotment' | 'account' | 'requests' | 'refusals' | 'platform' | 'plan' | 'doctrine' | 'limit' | 'input';
|
|
93
93
|
export declare const AI_JOB_NEEDS_INPUT_REASON_LABELS: Record<AiJobNeedsInputReason, string>;
|
|
94
94
|
/**
|
|
95
95
|
* The feed target types a generation job's output can be filed under: the
|
|
@@ -126,6 +126,7 @@ export const AI_JOB_NEEDS_INPUT_REASON_LABELS = {
|
|
|
126
126
|
plan: 'its plan is ready for review',
|
|
127
127
|
doctrine: 'its answer broke a building rule twice',
|
|
128
128
|
limit: 'the site reached an allowance of its plan',
|
|
129
|
+
input: 'it needs an ID only the person can give',
|
|
129
130
|
band: 'the included AI band is used up',
|
|
130
131
|
cap: 'the overage ceiling was reached',
|
|
131
132
|
messages: 'the monthly message cap was reached',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/activity/ai-activity-actions.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\n/**\n * The AI actions an activity log can carry (AGL-2929).\n *\n * Every other action in `orgs/{orgId}/activity` and `hosts/{hostId}/activity`\n * is a prose sentence written at the call site — \"Saved the screen\". The AI\n * rows are CODES instead, because they are read by more than a person: the\n * org feed's \"AI\" chip, the staff audit facet and the actor table all need\n * to recognize an AI row without parsing a sentence, and a sentence that\n * three writers phrase three ways is three rows that look unrelated.\n *\n * One list, so the writers in `ai-activity.ts`, the feed presenter and the\n * staff facet cannot disagree about what counts as AI. A code missing from\n * `AI_ACTIVITY_ACTION_LABELS` fails the type, not the reader.\n */\n\nimport type { AiJobOutputResource } from '../model/ai-jobs.types'\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn/app-utils/platform-brand'\nimport {\n pluginStaffAuditActionGroup,\n pluginStaffAuditActionGroupLabel,\n registerPluginActivityActions,\n type PluginActivityScope,\n} from '@aglyn/aglyn/plugin-manager/plugin-activity-actions'\n\n/** The action code each AI writer stores. */\nexport const AI_ACTIVITY_ACTIONS = {\n jobCreated: 'ai.job.created',\n jobOutput: 'ai.job.output',\n jobCanceled: 'ai.job.canceled',\n jobNeedsInput: 'ai.job.needs_input',\n editApplied: 'ai.edit.applied',\n seoApplied: 'ai.seo.applied',\n assistSection: 'ai.assist.section',\n overageHardCap: 'ai.overage.hardCap',\n overageCap: 'ai.overage.cap',\n allotmentChanged: 'ai.allotment.changed',\n permissionChanged: 'ai.permission.changed',\n addonPurchased: 'ai.addon.purchased',\n addonRemoved: 'ai.addon.removed',\n} as const\n\nexport type AiActivityAction =\n (typeof AI_ACTIVITY_ACTIONS)[keyof typeof AI_ACTIVITY_ACTIONS]\n\n/** What a person reads for each code, in the feed and the actor table. */\nexport const AI_ACTIVITY_ACTION_LABELS: Record<AiActivityAction, string> = {\n 'ai.job.created': 'Started an AI generation',\n 'ai.job.output': 'AI generated',\n 'ai.job.canceled': 'Canceled an AI generation',\n 'ai.job.needs_input': 'AI generation paused for input',\n 'ai.edit.applied': 'Applied AI edits',\n 'ai.seo.applied': 'Applied AI SEO fixes as drafts',\n 'ai.assist.section': 'AI generated a section',\n 'ai.overage.hardCap': 'AI stop-at-band switch',\n 'ai.overage.cap': 'AI overage ceiling',\n 'ai.allotment.changed': 'AI allotment changed',\n 'ai.permission.changed': 'AI permission changed',\n 'ai.addon.purchased': 'Added the AI add-on',\n 'ai.addon.removed': 'Removed the AI add-on',\n}\n\n/**\n * The codes whose row reads as a sentence about its target (AGL-3660):\n * `{target}` is the target's noun and name, so an output row reads\n * `Created page Home with Aglyn AI` rather than `AI generated` beside a\n * Target cell that had to finish the thought.\n */\nexport const AI_ACTIVITY_ACTION_SENTENCES: Partial<Record<AiActivityAction, string>> = {\n 'ai.job.output': `Created {target} with ${PLATFORM_BRAND_NAME} AI`,\n 'ai.edit.applied': `Applied ${PLATFORM_BRAND_NAME} AI edits to {target}`,\n 'ai.seo.applied': `Applied ${PLATFORM_BRAND_NAME} AI search fixes to {target}`,\n 'ai.assist.section': `Added a section to {target} with ${PLATFORM_BRAND_NAME} AI`,\n}\n\n/**\n * What the staff audit trail reads for each code the AI doors write to\n * `adminAudit` (AGL-3660). `ai.job.output` is spelled out per row by the\n * shared describer, which names the thing made.\n */\nexport const AI_STAFF_AUDIT_LABELS: Readonly<Record<string, string>> = {\n 'ai.job.output': `Created a draft with ${PLATFORM_BRAND_NAME} AI`,\n 'ai.job.cancel': `Canceled an ${PLATFORM_BRAND_NAME} AI job`,\n 'ai.job.resume': `Confirmed an ${PLATFORM_BRAND_NAME} AI plan`,\n 'ai.job.apply': `Applied an ${PLATFORM_BRAND_NAME} AI result`,\n 'ai.generate': `Generated with ${PLATFORM_BRAND_NAME} AI`,\n 'ai.allotments.set': 'Set AI allotments',\n 'ai.credits.giveBack': 'Gave AI credits back',\n 'ai.credits.reset': 'Reset AI credits',\n 'ai.overage.clearCeiling': 'Cleared the AI overage ceiling',\n 'ai.overage.liftPause': 'Lifted the AI pause',\n 'ai.overage.resetStep': 'Reset the AI overage step',\n 'ai.overage.setCeiling': 'Set the AI overage ceiling',\n 'platform.aiFreeSpend.paused': 'Paused free AI for the day',\n 'org.ai-viewed': 'Viewed the organization’s AI usage',\n 'user.ai-usage-viewed': 'Viewed the account’s AI usage',\n 'org.ai-conversations-viewed': `Viewed the organization’s ${PLATFORM_BRAND_NAME} AI requests`,\n 'user.ai-requests-viewed': `Viewed the account’s ${PLATFORM_BRAND_NAME} AI requests`,\n ...Object.fromEntries(\n Object.entries(AI_ACTIVITY_ACTION_LABELS).filter(([key]) => key !== 'ai.job.output'),\n ),\n}\n\n/** A job output's `resource`, as the noun the audit sentence uses. */\nconst AI_OUTPUT_NOUNS: Readonly<Record<string, string>> = {\n screen: 'page',\n reusableComponent: 'component',\n emailScreen: 'email',\n orgAutomation: 'automation',\n workflow: 'automation',\n entry: 'post',\n text: 'copy',\n seo: 'search fixes',\n crm: 'CRM answer',\n insight: 'insight',\n}\n\n/**\n * An `ai.job.output` audit row in words (AGL-3660), from the `after` the\n * job writer stores — `{ resource, label }`: `Created page Home with Aglyn\n * AI`, and its outcome, a draft.\n */\nexport function aiStaffAuditDescribe(entry: {\n action: string\n after: Readonly<Record<string, unknown>>\n}): { action?: string; result?: string } | undefined {\n if (entry.action !== AI_ACTIVITY_ACTIONS.jobOutput) return undefined\n const resource = typeof entry.after['resource'] === 'string' ? entry.after['resource'] : ''\n const label = typeof entry.after['label'] === 'string' ? entry.after['label'].trim() : ''\n const noun =\n AI_OUTPUT_NOUNS[resource] ??\n (resource ? resource.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase() : 'draft')\n return {\n action: `Created ${noun}${label ? ` ${label}` : ''} with ${PLATFORM_BRAND_NAME} AI`,\n result: 'Draft created',\n }\n}\n\n/** Every code, in catalog order — what a filter sends as `isAnyOf`. */\nexport const AI_ACTIVITY_ACTION_LIST: readonly AiActivityAction[] =\n Object.values(AI_ACTIVITY_ACTIONS)\n\n/** The feed's filter chip and the staff facet's group, by one name. */\nexport const AI_ACTIVITY_FILTER_LABEL = 'AI'\n\n/**\n * Why a generation stopped and asked for a person (AGL-2904): the plan's\n * band ran out, the org's own ceiling refused, the monthly message cap or\n * the job's own token budget did — or, on the Free taste (AGL-2925), one of\n * its own precautions: the account's credits, its daily request cap, a\n * pause after declined briefs, or the platform's day of free spend. Those\n * are the meter's `AssistRefusedBy` less its `null`, so a ceiling the meter\n * can name is one the feed can label. The last three are the job's own: its\n * plan is ready for review, or an answer broke a building rule on its re-ask\n * (AGL-2935), or the site is at an allowance its plan sets for what the job\n * writes (AGL-2909).\n */\nexport type AiJobNeedsInputReason =\n | 'band'\n | 'cap'\n | 'messages'\n | 'budget'\n | 'allotment'\n | 'account'\n | 'requests'\n | 'refusals'\n | 'platform'\n | 'plan'\n | 'doctrine'\n | 'limit'\n\nexport const AI_JOB_NEEDS_INPUT_REASON_LABELS: Record<\n AiJobNeedsInputReason,\n string\n> = {\n plan: 'its plan is ready for review',\n doctrine: 'its answer broke a building rule twice',\n limit: 'the site reached an allowance of its plan',\n band: 'the included AI band is used up',\n cap: 'the overage ceiling was reached',\n messages: 'the monthly message cap was reached',\n budget: 'the job budget was reached',\n allotment: 'the creator’s or the site’s AI allotment is used up',\n account: 'the account’s free AI credits are used up',\n requests: 'the daily free request cap was reached',\n refusals: 'free generation is paused for the day after declined briefs',\n platform: 'free AI generation is paused for the day',\n}\n\n/**\n * The feed target types a generation job's output can be filed under: the\n * kinds both activity logs know, so the org row and its host copy share a\n * shape.\n */\nexport type AiOutputTargetType =\n | 'screen'\n | 'layout'\n | 'component'\n | 'template'\n | 'workflow'\n | 'content'\n | 'theme'\n\n/**\n * Where the feed files each resource kind a job can write (AGL-2904).\n *\n * A job names its output by the resource it wrote, and the activity logs\n * know a narrower set of targets. The map is total, so a resource kind\n * added to the job document must say where the feed shows it before the\n * machine can log it. `reusableComponent` is the feed's `component`; a\n * `text` output is copy, which the feed files as `content`; a `theme`\n * proposal is filed under the site's theme, which the host feed links to the\n * Theme section (AGL-2938); the kinds whose runners have not shipped are\n * content of the site too, and are filed there until a target of their own\n * exists.\n */\nconst AI_OUTPUT_TARGET_TYPES: Record<AiJobOutputResource, AiOutputTargetType> = {\n screen: 'screen',\n reusableComponent: 'component',\n layout: 'layout',\n template: 'template',\n form: 'content',\n emailScreen: 'content',\n campaign: 'content',\n product: 'content',\n experiment: 'content',\n workflow: 'workflow',\n // An SEO proposal (AGL-2910) is about the site's content, and is filed there.\n seo: 'content',\n text: 'content',\n theme: 'theme',\n // An insight answer (AGL-2915) is about the site's figures; the feed row\n // names it and never what it says.\n insight: 'content',\n // A CRM proposal (AGL-2917) writes nothing; the feed files it with content.\n crm: 'content',\n // Another plugin's draft a build asked its writer for (AGL-3616).\n draft: 'content',\n // A logic proposal (AGL-3603) writes nothing either.\n logic: 'content',\n // So does an org automation's (AGL-3603): the editor's Save is the write.\n orgAutomation: 'content',\n // A blog post a guided start wrote (AGL-3676) is the site's content.\n entry: 'content',\n // A site's settings a build saved (AGL-3748) describe the site, filed with its content.\n settings: 'content',\n // Files a build organized in the site's media library (AGL-3752), filed with its content.\n media: 'content',\n // A site's tracking tags a build saved (AGL-3751), filed with its settings.\n tracking: 'content',\n}\n\nexport function aiOutputTargetType(resource: AiJobOutputResource): AiOutputTargetType {\n return AI_OUTPUT_TARGET_TYPES[resource]\n}\n\n/** Whether a stored action is one of the AI codes above. */\nexport function isAiActivityAction(action: unknown): action is AiActivityAction {\n return (\n typeof action === 'string' &&\n (AI_ACTIVITY_ACTION_LIST as readonly string[]).includes(action)\n )\n}\n\n/** The readable label for an AI code; `undefined` for any other action. */\nexport function aiActivityActionLabel(action: unknown): string | undefined {\n return isAiActivityAction(action)\n ? AI_ACTIVITY_ACTION_LABELS[action]\n : undefined\n}\n\n/**\n * Staff audit rows that are ABOUT AI without carrying an `ai.` code: the\n * customer's overage controls already write `billing.assistOverage.*`, and\n * the platform's free-spend auto-pause writes `platform.aiFreeSpend.*`\n * (AGL-2925). The audit page's facet folds them under the same group as the\n * codes, because \"what did we do about AI this week\" is one question.\n */\nexport const AI_STAFF_AUDIT_ACTION_PREFIXES: readonly string[] = [\n 'ai.',\n 'billing.assistOverage.',\n 'platform.aiFreeSpend.',\n]\n\n/** The facet group an `adminAudit` action belongs to. */\nexport const STAFF_AUDIT_AI_GROUP = 'ai'\n\n/**\n * Which log each code is written to. The job and edit codes land in both\n * the org feed and the host's copy; the billing and permission codes are\n * org-level facts and land in the org feed alone.\n */\nconst AI_ACTIVITY_ACTION_SCOPES: Record<\n AiActivityAction,\n PluginActivityScope | readonly PluginActivityScope[]\n> = {\n 'ai.job.created': ['org', 'host'],\n 'ai.job.output': ['org', 'host'],\n 'ai.job.canceled': ['org', 'host'],\n 'ai.job.needs_input': ['org', 'host'],\n 'ai.edit.applied': ['org', 'host'],\n 'ai.seo.applied': ['org', 'host'],\n 'ai.assist.section': ['org', 'host'],\n 'ai.overage.hardCap': 'org',\n 'ai.overage.cap': 'org',\n 'ai.allotment.changed': 'org',\n 'ai.permission.changed': 'org',\n 'ai.addon.purchased': 'org',\n 'ai.addon.removed': 'org',\n}\n\n/**\n * The catalog, declared through the generic activity-action seam\n * (AGL-2940) so the feed's chip, the actor table's filter and the staff\n * facet read it from the registry beside every other plugin's codes.\n * `registerAiDeclarations` calls it, in both apps and in the browser.\n */\nexport function registerAiActivityActions(): void {\n registerPluginActivityActions({\n pluginId: 'ai',\n group: {\n id: STAFF_AUDIT_AI_GROUP,\n label: AI_ACTIVITY_FILTER_LABEL,\n staffAuditPrefixes: AI_STAFF_AUDIT_ACTION_PREFIXES.filter(\n (prefix) => prefix !== 'ai.',\n ),\n // The staff cards opening on an org (AGL-2930) and on one account\n // (AGL-2928), which no longer write these, so the rows already in the\n // log still classify as reads; and a staff member reading what an org\n // asked Aglyn AI (AGL-3675), which writes one per page.\n // An account's AI requests on the staff user page (AGL-3660) likewise.\n staffAuditAccessActions: [\n 'org.ai-viewed',\n 'user.ai-usage-viewed',\n 'org.ai-conversations-viewed',\n 'user.ai-requests-viewed',\n ],\n staffAuditLabels: AI_STAFF_AUDIT_LABELS,\n // The audit trail's words for a job's output, its job collection and\n // the feed's job rows, read by core's shared describer (AGL-3660).\n staffAuditDescribe: aiStaffAuditDescribe,\n staffAuditCollections: { aiJobs: { noun: `${PLATFORM_BRAND_NAME} AI job`, job: true } },\n jobTargetTypes: ['aiJob'],\n },\n actions: AI_ACTIVITY_ACTION_LIST.map((key) => ({\n key,\n label: AI_ACTIVITY_ACTION_LABELS[key],\n ...(AI_ACTIVITY_ACTION_SENTENCES[key] ? { sentence: AI_ACTIVITY_ACTION_SENTENCES[key] } : {}),\n scope: AI_ACTIVITY_ACTION_SCOPES[key],\n })),\n // A generation job (AGL-2904): the org feed names the job, and each of\n // its outputs names the resource it produced. Filed as `aiJob` since\n // before plugin target types were namespaced, so the noun is declared\n // here rather than read off the type (AGL-3080).\n targetTypes: { aiJob: 'AI generation' },\n })\n}\n\n/**\n * The group the staff audit facet files an action under: `ai` for every\n * AI row, otherwise the action's leading namespace (`billing`, `org`,\n * `plugins`). Roughly seventy distinct actions write to `adminAudit`, so\n * the facet offers namespaces rather than a seventy-entry menu. Answered\n * by the registry, so every plugin's group is filed the same way.\n */\nexport function staffAuditActionGroup(action: unknown): string {\n return pluginStaffAuditActionGroup(action)\n}\n\n/** How a facet group reads in the menu: `ai` → `AI`, `billing` → `billing`. */\nexport function staffAuditActionGroupLabel(group: string): string {\n return pluginStaffAuditActionGroupLabel(group)\n}\n"],"names":["PLATFORM_BRAND_NAME","pluginStaffAuditActionGroup","pluginStaffAuditActionGroupLabel","registerPluginActivityActions","AI_ACTIVITY_ACTIONS","jobCreated","jobOutput","jobCanceled","jobNeedsInput","editApplied","seoApplied","assistSection","overageHardCap","overageCap","allotmentChanged","permissionChanged","addonPurchased","addonRemoved","AI_ACTIVITY_ACTION_LABELS","AI_ACTIVITY_ACTION_SENTENCES","AI_STAFF_AUDIT_LABELS","Object","fromEntries","entries","filter","key","AI_OUTPUT_NOUNS","screen","reusableComponent","emailScreen","orgAutomation","workflow","entry","text","seo","crm","insight","aiStaffAuditDescribe","action","undefined","resource","after","label","trim","noun","replace","toLowerCase","result","AI_ACTIVITY_ACTION_LIST","values","AI_ACTIVITY_FILTER_LABEL","AI_JOB_NEEDS_INPUT_REASON_LABELS","plan","doctrine","limit","band","cap","messages","budget","allotment","account","requests","refusals","platform","AI_OUTPUT_TARGET_TYPES","layout","template","form","campaign","product","experiment","theme","draft","logic","settings","media","tracking","aiOutputTargetType","isAiActivityAction","includes","aiActivityActionLabel","AI_STAFF_AUDIT_ACTION_PREFIXES","STAFF_AUDIT_AI_GROUP","AI_ACTIVITY_ACTION_SCOPES","registerAiActivityActions","pluginId","group","id","staffAuditPrefixes","prefix","staffAuditAccessActions","staffAuditLabels","staffAuditDescribe","staffAuditCollections","aiJobs","job","jobTargetTypes","actions","map","sentence","scope","targetTypes","aiJob","staffAuditActionGroup","staffAuditActionGroupLabel"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/activity/ai-activity-actions.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\n/**\n * The AI actions an activity log can carry (AGL-2929).\n *\n * Every other action in `orgs/{orgId}/activity` and `hosts/{hostId}/activity`\n * is a prose sentence written at the call site — \"Saved the screen\". The AI\n * rows are CODES instead, because they are read by more than a person: the\n * org feed's \"AI\" chip, the staff audit facet and the actor table all need\n * to recognize an AI row without parsing a sentence, and a sentence that\n * three writers phrase three ways is three rows that look unrelated.\n *\n * One list, so the writers in `ai-activity.ts`, the feed presenter and the\n * staff facet cannot disagree about what counts as AI. A code missing from\n * `AI_ACTIVITY_ACTION_LABELS` fails the type, not the reader.\n */\n\nimport type { AiJobOutputResource } from '../model/ai-jobs.types'\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn/app-utils/platform-brand'\nimport {\n pluginStaffAuditActionGroup,\n pluginStaffAuditActionGroupLabel,\n registerPluginActivityActions,\n type PluginActivityScope,\n} from '@aglyn/aglyn/plugin-manager/plugin-activity-actions'\n\n/** The action code each AI writer stores. */\nexport const AI_ACTIVITY_ACTIONS = {\n jobCreated: 'ai.job.created',\n jobOutput: 'ai.job.output',\n jobCanceled: 'ai.job.canceled',\n jobNeedsInput: 'ai.job.needs_input',\n editApplied: 'ai.edit.applied',\n seoApplied: 'ai.seo.applied',\n assistSection: 'ai.assist.section',\n overageHardCap: 'ai.overage.hardCap',\n overageCap: 'ai.overage.cap',\n allotmentChanged: 'ai.allotment.changed',\n permissionChanged: 'ai.permission.changed',\n addonPurchased: 'ai.addon.purchased',\n addonRemoved: 'ai.addon.removed',\n} as const\n\nexport type AiActivityAction =\n (typeof AI_ACTIVITY_ACTIONS)[keyof typeof AI_ACTIVITY_ACTIONS]\n\n/** What a person reads for each code, in the feed and the actor table. */\nexport const AI_ACTIVITY_ACTION_LABELS: Record<AiActivityAction, string> = {\n 'ai.job.created': 'Started an AI generation',\n 'ai.job.output': 'AI generated',\n 'ai.job.canceled': 'Canceled an AI generation',\n 'ai.job.needs_input': 'AI generation paused for input',\n 'ai.edit.applied': 'Applied AI edits',\n 'ai.seo.applied': 'Applied AI SEO fixes as drafts',\n 'ai.assist.section': 'AI generated a section',\n 'ai.overage.hardCap': 'AI stop-at-band switch',\n 'ai.overage.cap': 'AI overage ceiling',\n 'ai.allotment.changed': 'AI allotment changed',\n 'ai.permission.changed': 'AI permission changed',\n 'ai.addon.purchased': 'Added the AI add-on',\n 'ai.addon.removed': 'Removed the AI add-on',\n}\n\n/**\n * The codes whose row reads as a sentence about its target (AGL-3660):\n * `{target}` is the target's noun and name, so an output row reads\n * `Created page Home with Aglyn AI` rather than `AI generated` beside a\n * Target cell that had to finish the thought.\n */\nexport const AI_ACTIVITY_ACTION_SENTENCES: Partial<Record<AiActivityAction, string>> = {\n 'ai.job.output': `Created {target} with ${PLATFORM_BRAND_NAME} AI`,\n 'ai.edit.applied': `Applied ${PLATFORM_BRAND_NAME} AI edits to {target}`,\n 'ai.seo.applied': `Applied ${PLATFORM_BRAND_NAME} AI search fixes to {target}`,\n 'ai.assist.section': `Added a section to {target} with ${PLATFORM_BRAND_NAME} AI`,\n}\n\n/**\n * What the staff audit trail reads for each code the AI doors write to\n * `adminAudit` (AGL-3660). `ai.job.output` is spelled out per row by the\n * shared describer, which names the thing made.\n */\nexport const AI_STAFF_AUDIT_LABELS: Readonly<Record<string, string>> = {\n 'ai.job.output': `Created a draft with ${PLATFORM_BRAND_NAME} AI`,\n 'ai.job.cancel': `Canceled an ${PLATFORM_BRAND_NAME} AI job`,\n 'ai.job.resume': `Confirmed an ${PLATFORM_BRAND_NAME} AI plan`,\n 'ai.job.apply': `Applied an ${PLATFORM_BRAND_NAME} AI result`,\n 'ai.generate': `Generated with ${PLATFORM_BRAND_NAME} AI`,\n 'ai.allotments.set': 'Set AI allotments',\n 'ai.credits.giveBack': 'Gave AI credits back',\n 'ai.credits.reset': 'Reset AI credits',\n 'ai.overage.clearCeiling': 'Cleared the AI overage ceiling',\n 'ai.overage.liftPause': 'Lifted the AI pause',\n 'ai.overage.resetStep': 'Reset the AI overage step',\n 'ai.overage.setCeiling': 'Set the AI overage ceiling',\n 'platform.aiFreeSpend.paused': 'Paused free AI for the day',\n 'org.ai-viewed': 'Viewed the organization’s AI usage',\n 'user.ai-usage-viewed': 'Viewed the account’s AI usage',\n 'org.ai-conversations-viewed': `Viewed the organization’s ${PLATFORM_BRAND_NAME} AI requests`,\n 'user.ai-requests-viewed': `Viewed the account’s ${PLATFORM_BRAND_NAME} AI requests`,\n ...Object.fromEntries(\n Object.entries(AI_ACTIVITY_ACTION_LABELS).filter(([key]) => key !== 'ai.job.output'),\n ),\n}\n\n/** A job output's `resource`, as the noun the audit sentence uses. */\nconst AI_OUTPUT_NOUNS: Readonly<Record<string, string>> = {\n screen: 'page',\n reusableComponent: 'component',\n emailScreen: 'email',\n orgAutomation: 'automation',\n workflow: 'automation',\n entry: 'post',\n text: 'copy',\n seo: 'search fixes',\n crm: 'CRM answer',\n insight: 'insight',\n}\n\n/**\n * An `ai.job.output` audit row in words (AGL-3660), from the `after` the\n * job writer stores — `{ resource, label }`: `Created page Home with Aglyn\n * AI`, and its outcome, a draft.\n */\nexport function aiStaffAuditDescribe(entry: {\n action: string\n after: Readonly<Record<string, unknown>>\n}): { action?: string; result?: string } | undefined {\n if (entry.action !== AI_ACTIVITY_ACTIONS.jobOutput) return undefined\n const resource = typeof entry.after['resource'] === 'string' ? entry.after['resource'] : ''\n const label = typeof entry.after['label'] === 'string' ? entry.after['label'].trim() : ''\n const noun =\n AI_OUTPUT_NOUNS[resource] ??\n (resource ? resource.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase() : 'draft')\n return {\n action: `Created ${noun}${label ? ` ${label}` : ''} with ${PLATFORM_BRAND_NAME} AI`,\n result: 'Draft created',\n }\n}\n\n/** Every code, in catalog order — what a filter sends as `isAnyOf`. */\nexport const AI_ACTIVITY_ACTION_LIST: readonly AiActivityAction[] =\n Object.values(AI_ACTIVITY_ACTIONS)\n\n/** The feed's filter chip and the staff facet's group, by one name. */\nexport const AI_ACTIVITY_FILTER_LABEL = 'AI'\n\n/**\n * Why a generation stopped and asked for a person (AGL-2904): the plan's\n * band ran out, the org's own ceiling refused, the monthly message cap or\n * the job's own token budget did — or, on the Free taste (AGL-2925), one of\n * its own precautions: the account's credits, its daily request cap, a\n * pause after declined briefs, or the platform's day of free spend. Those\n * are the meter's `AssistRefusedBy` less its `null`, so a ceiling the meter\n * can name is one the feed can label. The last three are the job's own: its\n * plan is ready for review, or an answer broke a building rule on its re-ask\n * (AGL-2935), or the site is at an allowance its plan sets for what the job\n * writes (AGL-2909).\n */\nexport type AiJobNeedsInputReason =\n | 'band'\n | 'cap'\n | 'messages'\n | 'budget'\n | 'allotment'\n | 'account'\n | 'requests'\n | 'refusals'\n | 'platform'\n | 'plan'\n | 'doctrine'\n | 'limit'\n | 'input'\n\nexport const AI_JOB_NEEDS_INPUT_REASON_LABELS: Record<\n AiJobNeedsInputReason,\n string\n> = {\n plan: 'its plan is ready for review',\n doctrine: 'its answer broke a building rule twice',\n limit: 'the site reached an allowance of its plan',\n input: 'it needs an ID only the person can give',\n band: 'the included AI band is used up',\n cap: 'the overage ceiling was reached',\n messages: 'the monthly message cap was reached',\n budget: 'the job budget was reached',\n allotment: 'the creator’s or the site’s AI allotment is used up',\n account: 'the account’s free AI credits are used up',\n requests: 'the daily free request cap was reached',\n refusals: 'free generation is paused for the day after declined briefs',\n platform: 'free AI generation is paused for the day',\n}\n\n/**\n * The feed target types a generation job's output can be filed under: the\n * kinds both activity logs know, so the org row and its host copy share a\n * shape.\n */\nexport type AiOutputTargetType =\n | 'screen'\n | 'layout'\n | 'component'\n | 'template'\n | 'workflow'\n | 'content'\n | 'theme'\n\n/**\n * Where the feed files each resource kind a job can write (AGL-2904).\n *\n * A job names its output by the resource it wrote, and the activity logs\n * know a narrower set of targets. The map is total, so a resource kind\n * added to the job document must say where the feed shows it before the\n * machine can log it. `reusableComponent` is the feed's `component`; a\n * `text` output is copy, which the feed files as `content`; a `theme`\n * proposal is filed under the site's theme, which the host feed links to the\n * Theme section (AGL-2938); the kinds whose runners have not shipped are\n * content of the site too, and are filed there until a target of their own\n * exists.\n */\nconst AI_OUTPUT_TARGET_TYPES: Record<AiJobOutputResource, AiOutputTargetType> = {\n screen: 'screen',\n reusableComponent: 'component',\n layout: 'layout',\n template: 'template',\n form: 'content',\n emailScreen: 'content',\n campaign: 'content',\n product: 'content',\n experiment: 'content',\n workflow: 'workflow',\n // An SEO proposal (AGL-2910) is about the site's content, and is filed there.\n seo: 'content',\n text: 'content',\n theme: 'theme',\n // An insight answer (AGL-2915) is about the site's figures; the feed row\n // names it and never what it says.\n insight: 'content',\n // A CRM proposal (AGL-2917) writes nothing; the feed files it with content.\n crm: 'content',\n // Another plugin's draft a build asked its writer for (AGL-3616).\n draft: 'content',\n // A logic proposal (AGL-3603) writes nothing either.\n logic: 'content',\n // So does an org automation's (AGL-3603): the editor's Save is the write.\n orgAutomation: 'content',\n // A blog post a guided start wrote (AGL-3676) is the site's content.\n entry: 'content',\n // A site's settings a build saved (AGL-3748) describe the site, filed with its content.\n settings: 'content',\n // Files a build organized in the site's media library (AGL-3752), filed with its content.\n media: 'content',\n // A site's tracking tags a build saved (AGL-3751), filed with its settings.\n tracking: 'content',\n}\n\nexport function aiOutputTargetType(resource: AiJobOutputResource): AiOutputTargetType {\n return AI_OUTPUT_TARGET_TYPES[resource]\n}\n\n/** Whether a stored action is one of the AI codes above. */\nexport function isAiActivityAction(action: unknown): action is AiActivityAction {\n return (\n typeof action === 'string' &&\n (AI_ACTIVITY_ACTION_LIST as readonly string[]).includes(action)\n )\n}\n\n/** The readable label for an AI code; `undefined` for any other action. */\nexport function aiActivityActionLabel(action: unknown): string | undefined {\n return isAiActivityAction(action)\n ? AI_ACTIVITY_ACTION_LABELS[action]\n : undefined\n}\n\n/**\n * Staff audit rows that are ABOUT AI without carrying an `ai.` code: the\n * customer's overage controls already write `billing.assistOverage.*`, and\n * the platform's free-spend auto-pause writes `platform.aiFreeSpend.*`\n * (AGL-2925). The audit page's facet folds them under the same group as the\n * codes, because \"what did we do about AI this week\" is one question.\n */\nexport const AI_STAFF_AUDIT_ACTION_PREFIXES: readonly string[] = [\n 'ai.',\n 'billing.assistOverage.',\n 'platform.aiFreeSpend.',\n]\n\n/** The facet group an `adminAudit` action belongs to. */\nexport const STAFF_AUDIT_AI_GROUP = 'ai'\n\n/**\n * Which log each code is written to. The job and edit codes land in both\n * the org feed and the host's copy; the billing and permission codes are\n * org-level facts and land in the org feed alone.\n */\nconst AI_ACTIVITY_ACTION_SCOPES: Record<\n AiActivityAction,\n PluginActivityScope | readonly PluginActivityScope[]\n> = {\n 'ai.job.created': ['org', 'host'],\n 'ai.job.output': ['org', 'host'],\n 'ai.job.canceled': ['org', 'host'],\n 'ai.job.needs_input': ['org', 'host'],\n 'ai.edit.applied': ['org', 'host'],\n 'ai.seo.applied': ['org', 'host'],\n 'ai.assist.section': ['org', 'host'],\n 'ai.overage.hardCap': 'org',\n 'ai.overage.cap': 'org',\n 'ai.allotment.changed': 'org',\n 'ai.permission.changed': 'org',\n 'ai.addon.purchased': 'org',\n 'ai.addon.removed': 'org',\n}\n\n/**\n * The catalog, declared through the generic activity-action seam\n * (AGL-2940) so the feed's chip, the actor table's filter and the staff\n * facet read it from the registry beside every other plugin's codes.\n * `registerAiDeclarations` calls it, in both apps and in the browser.\n */\nexport function registerAiActivityActions(): void {\n registerPluginActivityActions({\n pluginId: 'ai',\n group: {\n id: STAFF_AUDIT_AI_GROUP,\n label: AI_ACTIVITY_FILTER_LABEL,\n staffAuditPrefixes: AI_STAFF_AUDIT_ACTION_PREFIXES.filter(\n (prefix) => prefix !== 'ai.',\n ),\n // The staff cards opening on an org (AGL-2930) and on one account\n // (AGL-2928), which no longer write these, so the rows already in the\n // log still classify as reads; and a staff member reading what an org\n // asked Aglyn AI (AGL-3675), which writes one per page.\n // An account's AI requests on the staff user page (AGL-3660) likewise.\n staffAuditAccessActions: [\n 'org.ai-viewed',\n 'user.ai-usage-viewed',\n 'org.ai-conversations-viewed',\n 'user.ai-requests-viewed',\n ],\n staffAuditLabels: AI_STAFF_AUDIT_LABELS,\n // The audit trail's words for a job's output, its job collection and\n // the feed's job rows, read by core's shared describer (AGL-3660).\n staffAuditDescribe: aiStaffAuditDescribe,\n staffAuditCollections: { aiJobs: { noun: `${PLATFORM_BRAND_NAME} AI job`, job: true } },\n jobTargetTypes: ['aiJob'],\n },\n actions: AI_ACTIVITY_ACTION_LIST.map((key) => ({\n key,\n label: AI_ACTIVITY_ACTION_LABELS[key],\n ...(AI_ACTIVITY_ACTION_SENTENCES[key] ? { sentence: AI_ACTIVITY_ACTION_SENTENCES[key] } : {}),\n scope: AI_ACTIVITY_ACTION_SCOPES[key],\n })),\n // A generation job (AGL-2904): the org feed names the job, and each of\n // its outputs names the resource it produced. Filed as `aiJob` since\n // before plugin target types were namespaced, so the noun is declared\n // here rather than read off the type (AGL-3080).\n targetTypes: { aiJob: 'AI generation' },\n })\n}\n\n/**\n * The group the staff audit facet files an action under: `ai` for every\n * AI row, otherwise the action's leading namespace (`billing`, `org`,\n * `plugins`). Roughly seventy distinct actions write to `adminAudit`, so\n * the facet offers namespaces rather than a seventy-entry menu. Answered\n * by the registry, so every plugin's group is filed the same way.\n */\nexport function staffAuditActionGroup(action: unknown): string {\n return pluginStaffAuditActionGroup(action)\n}\n\n/** How a facet group reads in the menu: `ai` → `AI`, `billing` → `billing`. */\nexport function staffAuditActionGroupLabel(group: string): string {\n return pluginStaffAuditActionGroupLabel(group)\n}\n"],"names":["PLATFORM_BRAND_NAME","pluginStaffAuditActionGroup","pluginStaffAuditActionGroupLabel","registerPluginActivityActions","AI_ACTIVITY_ACTIONS","jobCreated","jobOutput","jobCanceled","jobNeedsInput","editApplied","seoApplied","assistSection","overageHardCap","overageCap","allotmentChanged","permissionChanged","addonPurchased","addonRemoved","AI_ACTIVITY_ACTION_LABELS","AI_ACTIVITY_ACTION_SENTENCES","AI_STAFF_AUDIT_LABELS","Object","fromEntries","entries","filter","key","AI_OUTPUT_NOUNS","screen","reusableComponent","emailScreen","orgAutomation","workflow","entry","text","seo","crm","insight","aiStaffAuditDescribe","action","undefined","resource","after","label","trim","noun","replace","toLowerCase","result","AI_ACTIVITY_ACTION_LIST","values","AI_ACTIVITY_FILTER_LABEL","AI_JOB_NEEDS_INPUT_REASON_LABELS","plan","doctrine","limit","input","band","cap","messages","budget","allotment","account","requests","refusals","platform","AI_OUTPUT_TARGET_TYPES","layout","template","form","campaign","product","experiment","theme","draft","logic","settings","media","tracking","aiOutputTargetType","isAiActivityAction","includes","aiActivityActionLabel","AI_STAFF_AUDIT_ACTION_PREFIXES","STAFF_AUDIT_AI_GROUP","AI_ACTIVITY_ACTION_SCOPES","registerAiActivityActions","pluginId","group","id","staffAuditPrefixes","prefix","staffAuditAccessActions","staffAuditLabels","staffAuditDescribe","staffAuditCollections","aiJobs","job","jobTargetTypes","actions","map","sentence","scope","targetTypes","aiJob","staffAuditActionGroup","staffAuditActionGroupLabel"],"mappings":"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"}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -23,9 +23,10 @@ export interface AiCreditsCardProps {
|
|
|
23
23
|
* override, draws no meter — a "0 of 0" meter is not a readout of anything —
|
|
24
24
|
* but says the plan has no credits and links to Billing (AGL-3982), since a
|
|
25
25
|
* missing card reads as AI not existing here. ONE meter for one pool: the
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* plan's included credits pay for every AI action, generation included
|
|
27
|
+
* (AGL-3965), and an Aglyn AI tier widens `assistCreditsPerMonth` rather than
|
|
28
|
+
* opening a second band, so the limit here is already plan plus tier and the
|
|
29
|
+
* caption under it says how much of that the tier is.
|
|
29
30
|
*
|
|
30
31
|
* From the server, not from Firestore like the platform's meters:
|
|
31
32
|
* `orgs/{orgId}/assistUsage` is default-deny for every client, so a browser
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ 'use client';
|
|
17
17
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
|
-
import {
|
|
18
|
+
import { AI_TIERS, aiAddonName, aiTierName, isUnlimitedQuota, orgCanBuyAiTier, resolveAiTier, resolveOrgEntitlements } from "@aglyn/aglyn";
|
|
19
19
|
import { pluginDocsHelp } from "@aglyn/aglyn/app-utils/docs-help";
|
|
20
20
|
import { CardDisplay } from "@aglyn/shared-ui-jsx";
|
|
21
21
|
import { PrerequisiteNotice } from "@aglyn/shared-ui-jsx/components/prerequisite-notice.component";
|
|
@@ -41,9 +41,10 @@ import { useUser } from "@aglyn/tenant-feature-instance";
|
|
|
41
41
|
* override, draws no meter — a "0 of 0" meter is not a readout of anything —
|
|
42
42
|
* but says the plan has no credits and links to Billing (AGL-3982), since a
|
|
43
43
|
* missing card reads as AI not existing here. ONE meter for one pool: the
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
44
|
+
* plan's included credits pay for every AI action, generation included
|
|
45
|
+
* (AGL-3965), and an Aglyn AI tier widens `assistCreditsPerMonth` rather than
|
|
46
|
+
* opening a second band, so the limit here is already plan plus tier and the
|
|
47
|
+
* caption under it says how much of that the tier is.
|
|
47
48
|
*
|
|
48
49
|
* From the server, not from Firestore like the platform's meters:
|
|
49
50
|
* `orgs/{orgId}/assistUsage` is default-deny for every client, so a browser
|
|
@@ -77,7 +78,7 @@ import { useUser } from "@aglyn/tenant-feature-instance";
|
|
|
77
78
|
const entitlements = resolveOrgEntitlements(org);
|
|
78
79
|
const help = pluginDocsHelp('aiCredits', {
|
|
79
80
|
anchor: '#see-your-credits',
|
|
80
|
-
excerpt: 'AI credits drawn this month against
|
|
81
|
+
excerpt: 'AI credits drawn this month against one pool: your plan’s included ' + 'credits plus your AI tier’s, if you have one. Every AI action draws on ' + 'it. The pool resets on the first of the month.'
|
|
81
82
|
});
|
|
82
83
|
// A band of zero (AGL-3982): said, with the way to a plan that has one,
|
|
83
84
|
// rather than a missing card that reads as AI not existing here.
|
|
@@ -94,11 +95,10 @@ import { useUser } from "@aglyn/tenant-feature-instance";
|
|
|
94
95
|
})
|
|
95
96
|
});
|
|
96
97
|
}
|
|
97
|
-
const
|
|
98
|
-
const aiAddonCredits = hasAiAddon(org) ? AI_ADDON_CREDITS_PER_MONTH[plan] : 0;
|
|
98
|
+
const aiTier = resolveAiTier(org);
|
|
99
99
|
// Sold to THIS workspace, not merely on its plan: a staff comp has no
|
|
100
|
-
// subscription to add the item to, so
|
|
101
|
-
const
|
|
100
|
+
// subscription to add the item to, so it sells none (AGL-3034).
|
|
101
|
+
const tiersSold = orgCanBuyAiTier(org);
|
|
102
102
|
return /*#__PURE__*/ _jsxs(CardDisplay, {
|
|
103
103
|
header: 'AI credits',
|
|
104
104
|
help: help,
|
|
@@ -112,7 +112,7 @@ import { useUser } from "@aglyn/tenant-feature-instance";
|
|
|
112
112
|
unlimited: isUnlimitedQuota(entitlements.assistCreditsPerMonth),
|
|
113
113
|
upgradeHref: billingHref ? `${billingHref}#plans` : '#plans'
|
|
114
114
|
}),
|
|
115
|
-
|
|
115
|
+
aiTier ? /*#__PURE__*/ _jsx(Typography, {
|
|
116
116
|
variant: "caption",
|
|
117
117
|
color: "text.secondary",
|
|
118
118
|
sx: {
|
|
@@ -120,9 +120,9 @@ import { useUser } from "@aglyn/tenant-feature-instance";
|
|
|
120
120
|
mt: -1.5,
|
|
121
121
|
mb: 2
|
|
122
122
|
},
|
|
123
|
-
children: `Includes ${
|
|
123
|
+
children: `Includes ${AI_TIERS[aiTier].credits.toLocaleString()} credits a month ` + `from ${aiTierName(aiTier)}, on top of your plan's.`
|
|
124
124
|
}) : null,
|
|
125
|
-
!
|
|
125
|
+
!aiTier && tiersSold ? /*#__PURE__*/ _jsxs(Typography, {
|
|
126
126
|
variant: "caption",
|
|
127
127
|
color: "text.secondary",
|
|
128
128
|
sx: {
|
|
@@ -131,14 +131,14 @@ import { useUser } from "@aglyn/tenant-feature-instance";
|
|
|
131
131
|
mb: 2
|
|
132
132
|
},
|
|
133
133
|
children: [
|
|
134
|
-
`
|
|
134
|
+
`Need more? An ${aiAddonName()} tier adds ` + `${AI_TIERS.spark.credits.toLocaleString()} to ` + `${AI_TIERS.studio.credits.toLocaleString()} credits a month to this pool: `,
|
|
135
135
|
/*#__PURE__*/ _jsx(Link, {
|
|
136
136
|
href: `${billingHref != null ? billingHref : ''}#addons`,
|
|
137
137
|
color: "primary",
|
|
138
138
|
underline: "hover",
|
|
139
|
-
children:
|
|
139
|
+
children: 'Choose a tier'
|
|
140
140
|
}),
|
|
141
|
-
' in Billing →
|
|
141
|
+
' in Billing → Plan.'
|
|
142
142
|
]
|
|
143
143
|
}) : null
|
|
144
144
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-credits-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n AI_ADDON_CREDITS_PER_MONTH,\n aiAddonName,\n hasAiAddon,\n isUnlimitedQuota,\n resolveEffectivePlan,\n resolveOrgEntitlements,\n resolvePlanPricing,\n type AglynOrgBilling,\n} from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { PrerequisiteNotice } from '@aglyn/shared-ui-jsx/components/prerequisite-notice.component'\nimport { UsageMeter } from '@aglyn/shared-ui-jsx/components/usage-meter.component'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { Link, Typography } from '@mui/material'\nimport { useEffect, useState } from 'react'\nimport { useUser } from '@aglyn/tenant-feature-instance'\n\nexport interface AiCreditsCardProps {\n orgId?: string | null\n /** The billing-merged org doc the Usage page resolved. */\n org?: Partial<AglynOrgBilling> | null\n /** The Billing overview's path, for the one caption that points at the add-ons card. */\n billingHref?: string\n}\n\n/**\n * The AI credits meter (AGL-2899), on the Billing → Usage page's plugin\n * band below the platform's own meters (AGL-2939).\n *\n * Credits, because AI actions differ in cost by up to two orders of\n * magnitude — a question against generating a screen — and a message count\n * would price them the same. The band is refused at the cap, so this\n * readout is the only thing standing between a customer and learning their\n * limit by being turned down mid-build.\n *\n * Rendered only where a band is sold, which since AGL-3203 is every plan\n * row: Free carries the taste and Starter 750, so both render a meter even\n * though neither carries `aiAssist` — the band is credits, not the guided\n * rung. A band of ZERO, which now reaches this component only as a per-org\n * override, draws no meter — a \"0 of 0\" meter is not a readout of anything —\n * but says the plan has no credits and links to Billing (AGL-3982), since a\n * missing card reads as AI not existing here. ONE meter for one pool: the\n * add-on widens `assistCreditsPerMonth` rather than opening a second band,\n * so the limit here is already plan plus add-on and the caption under it\n * says how much of that the add-on is.\n *\n * From the server, not from Firestore like the platform's meters:\n * `orgs/{orgId}/assistUsage` is default-deny for every client, so a browser\n * read would fail silently and leave this reading \"not yet metered\" forever.\n * Credits, never dollars: the route is where the one conversion happens.\n */\nexport default function AiCreditsCard(props: AiCreditsCardProps) {\n const { orgId, org, billingHref } = props\n const { data: user } = useUser()\n const [credits, setCredits] = useState<{ used: number; limit: number | null } | null>(null)\n useEffect(() => {\n if (!orgId || !user) return undefined\n let active = true\n void (async () => {\n try {\n const response = await authorizedFetch(\n user,\n `/api/ai/billing/credits?orgId=${encodeURIComponent(orgId)}`,\n )\n if (!response.ok) return\n const result = await response.json()\n if (active && result?.credits) setCredits(result.credits)\n } catch {\n // The meter keeps its \"not yet metered\" state on failure —\n // deliberately not 0, which reads as \"you have used none\".\n }\n })()\n return () => {\n active = false\n }\n }, [orgId, user])\n\n const entitlements = resolveOrgEntitlements(org)\n const help = pluginDocsHelp('aiCredits', {\n anchor: '#see-your-credits',\n excerpt:\n 'AI credits drawn this month against your included band: your plan’s ' +\n 'credits plus the AI add-on’s. The band resets on the first of the month.',\n })\n // A band of zero (AGL-3982): said, with the way to a plan that has one,\n // rather than a missing card that reads as AI not existing here.\n if (entitlements.assistCreditsPerMonth <= 0) {\n return (\n <CardDisplay header={'AI credits'} help={help} contentGutterX contentGutterY>\n <PrerequisiteNotice href={billingHref ? `${billingHref}#plans` : '#plans'} actionLabel=\"See plans\">\n {'Your plan includes no AI credits. Choose a plan with AI credits in Billing to use the AI features.'}\n </PrerequisiteNotice>\n </CardDisplay>\n )\n }\n const plan = resolveEffectivePlan(org)\n const aiAddonCredits = hasAiAddon(org) ? AI_ADDON_CREDITS_PER_MONTH[plan] : 0\n // Sold to THIS workspace, not merely on its plan: a staff comp has no\n // subscription to add the item to, so its price list sells none (AGL-3034).\n const aiAddonSold = resolvePlanPricing(org).aiAddonMonthlyUsd != null\n return (\n <CardDisplay\n header={'AI credits'}\n help={help}\n contentGutterX\n contentGutterY\n >\n {/* An uncapped staff comp (AGL-3049) resolves this band `UNLIMITED`:\n the meter reads \"Unlimited\" and draws no bar, never \"/ Infinity\". */}\n <UsageMeter\n label=\"AI credits (this month)\"\n used={credits ? credits.used : null}\n limit={entitlements.assistCreditsPerMonth}\n unlimited={isUnlimitedQuota(entitlements.assistCreditsPerMonth)}\n upgradeHref={billingHref ? `${billingHref}#plans` : '#plans'}\n />\n {aiAddonCredits > 0 ? (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', mt: -1.5, mb: 2 }}\n >\n {`Includes ${aiAddonCredits.toLocaleString()} credits a month from ` +\n `the ${aiAddonName()} add-on.`}\n </Typography>\n ) : null}\n {/* A plan with a band that sells the add-on and has not bought it:\n the one line on the meters that points at more capacity rather\n than at an upgrade. */}\n {!entitlements.features.aiGenerative && aiAddonSold ? (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', mt: -1.5, mb: 2 }}\n >\n {`Generate pages, emails, campaigns and more with ${aiAddonName()}, ` +\n `and add ${AI_ADDON_CREDITS_PER_MONTH[plan].toLocaleString()} ` +\n 'credits a month to this pool: '}\n <Link href={`${billingHref ?? ''}#addons`} color=\"primary\" underline=\"hover\">\n {`Add ${aiAddonName()}`}\n </Link>\n {' in Billing → Plans.'}\n </Typography>\n ) : null}\n </CardDisplay>\n )\n}\n"],"names":["AI_ADDON_CREDITS_PER_MONTH","aiAddonName","hasAiAddon","isUnlimitedQuota","resolveEffectivePlan","resolveOrgEntitlements","resolvePlanPricing","pluginDocsHelp","CardDisplay","PrerequisiteNotice","UsageMeter","authorizedFetch","Link","Typography","useEffect","useState","useUser","AiCreditsCard","props","orgId","org","billingHref","data","user","credits","setCredits","undefined","active","response","encodeURIComponent","ok","result","json","entitlements","help","anchor","excerpt","assistCreditsPerMonth","header","contentGutterX","contentGutterY","href","actionLabel","plan","aiAddonCredits","aiAddonSold","aiAddonMonthlyUsd","label","used","limit","unlimited","upgradeHref","variant","color","sx","display","mt","mb","toLocaleString","features","aiGenerative","underline"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-credits-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n AI_TIERS,\n aiAddonName,\n aiTierName,\n isUnlimitedQuota,\n orgCanBuyAiTier,\n resolveAiTier,\n resolveOrgEntitlements,\n type AglynOrgBilling,\n} from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { PrerequisiteNotice } from '@aglyn/shared-ui-jsx/components/prerequisite-notice.component'\nimport { UsageMeter } from '@aglyn/shared-ui-jsx/components/usage-meter.component'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { Link, Typography } from '@mui/material'\nimport { useEffect, useState } from 'react'\nimport { useUser } from '@aglyn/tenant-feature-instance'\n\nexport interface AiCreditsCardProps {\n orgId?: string | null\n /** The billing-merged org doc the Usage page resolved. */\n org?: Partial<AglynOrgBilling> | null\n /** The Billing overview's path, for the one caption that points at the add-ons card. */\n billingHref?: string\n}\n\n/**\n * The AI credits meter (AGL-2899), on the Billing → Usage page's plugin\n * band below the platform's own meters (AGL-2939).\n *\n * Credits, because AI actions differ in cost by up to two orders of\n * magnitude — a question against generating a screen — and a message count\n * would price them the same. The band is refused at the cap, so this\n * readout is the only thing standing between a customer and learning their\n * limit by being turned down mid-build.\n *\n * Rendered only where a band is sold, which since AGL-3203 is every plan\n * row: Free carries the taste and Starter 750, so both render a meter even\n * though neither carries `aiAssist` — the band is credits, not the guided\n * rung. A band of ZERO, which now reaches this component only as a per-org\n * override, draws no meter — a \"0 of 0\" meter is not a readout of anything —\n * but says the plan has no credits and links to Billing (AGL-3982), since a\n * missing card reads as AI not existing here. ONE meter for one pool: the\n * plan's included credits pay for every AI action, generation included\n * (AGL-3965), and an Aglyn AI tier widens `assistCreditsPerMonth` rather than\n * opening a second band, so the limit here is already plan plus tier and the\n * caption under it says how much of that the tier is.\n *\n * From the server, not from Firestore like the platform's meters:\n * `orgs/{orgId}/assistUsage` is default-deny for every client, so a browser\n * read would fail silently and leave this reading \"not yet metered\" forever.\n * Credits, never dollars: the route is where the one conversion happens.\n */\nexport default function AiCreditsCard(props: AiCreditsCardProps) {\n const { orgId, org, billingHref } = props\n const { data: user } = useUser()\n const [credits, setCredits] = useState<{ used: number; limit: number | null } | null>(null)\n useEffect(() => {\n if (!orgId || !user) return undefined\n let active = true\n void (async () => {\n try {\n const response = await authorizedFetch(\n user,\n `/api/ai/billing/credits?orgId=${encodeURIComponent(orgId)}`,\n )\n if (!response.ok) return\n const result = await response.json()\n if (active && result?.credits) setCredits(result.credits)\n } catch {\n // The meter keeps its \"not yet metered\" state on failure —\n // deliberately not 0, which reads as \"you have used none\".\n }\n })()\n return () => {\n active = false\n }\n }, [orgId, user])\n\n const entitlements = resolveOrgEntitlements(org)\n const help = pluginDocsHelp('aiCredits', {\n anchor: '#see-your-credits',\n excerpt:\n 'AI credits drawn this month against one pool: your plan’s included ' +\n 'credits plus your AI tier’s, if you have one. Every AI action draws on ' +\n 'it. The pool resets on the first of the month.',\n })\n // A band of zero (AGL-3982): said, with the way to a plan that has one,\n // rather than a missing card that reads as AI not existing here.\n if (entitlements.assistCreditsPerMonth <= 0) {\n return (\n <CardDisplay header={'AI credits'} help={help} contentGutterX contentGutterY>\n <PrerequisiteNotice href={billingHref ? `${billingHref}#plans` : '#plans'} actionLabel=\"See plans\">\n {'Your plan includes no AI credits. Choose a plan with AI credits in Billing to use the AI features.'}\n </PrerequisiteNotice>\n </CardDisplay>\n )\n }\n const aiTier = resolveAiTier(org)\n // Sold to THIS workspace, not merely on its plan: a staff comp has no\n // subscription to add the item to, so it sells none (AGL-3034).\n const tiersSold = orgCanBuyAiTier(org)\n return (\n <CardDisplay\n header={'AI credits'}\n help={help}\n contentGutterX\n contentGutterY\n >\n {/* An uncapped staff comp (AGL-3049) resolves this band `UNLIMITED`:\n the meter reads \"Unlimited\" and draws no bar, never \"/ Infinity\". */}\n <UsageMeter\n label=\"AI credits (this month)\"\n used={credits ? credits.used : null}\n limit={entitlements.assistCreditsPerMonth}\n unlimited={isUnlimitedQuota(entitlements.assistCreditsPerMonth)}\n upgradeHref={billingHref ? `${billingHref}#plans` : '#plans'}\n />\n {aiTier ? (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', mt: -1.5, mb: 2 }}\n >\n {`Includes ${AI_TIERS[aiTier].credits.toLocaleString()} credits a month ` +\n `from ${aiTierName(aiTier)}, on top of your plan's.`}\n </Typography>\n ) : null}\n {/* A plan that sells tiers and holds none: the one line on the meters\n that points at more capacity rather than at an upgrade. */}\n {!aiTier && tiersSold ? (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', mt: -1.5, mb: 2 }}\n >\n {`Need more? An ${aiAddonName()} tier adds ` +\n `${AI_TIERS.spark.credits.toLocaleString()} to ` +\n `${AI_TIERS.studio.credits.toLocaleString()} credits a month to this pool: `}\n <Link href={`${billingHref ?? ''}#addons`} color=\"primary\" underline=\"hover\">\n {'Choose a tier'}\n </Link>\n {' in Billing → Plan.'}\n </Typography>\n ) : null}\n </CardDisplay>\n )\n}\n"],"names":["AI_TIERS","aiAddonName","aiTierName","isUnlimitedQuota","orgCanBuyAiTier","resolveAiTier","resolveOrgEntitlements","pluginDocsHelp","CardDisplay","PrerequisiteNotice","UsageMeter","authorizedFetch","Link","Typography","useEffect","useState","useUser","AiCreditsCard","props","orgId","org","billingHref","data","user","credits","setCredits","undefined","active","response","encodeURIComponent","ok","result","json","entitlements","help","anchor","excerpt","assistCreditsPerMonth","header","contentGutterX","contentGutterY","href","actionLabel","aiTier","tiersSold","label","used","limit","unlimited","upgradeHref","variant","color","sx","display","mt","mb","toLocaleString","spark","studio","underline"],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
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 { MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token';
|
|
18
|
+
import type { AiJobInputRequest, AiJobSummary } from '../model/ai-jobs.types';
|
|
19
|
+
export interface AiJobInputFormProps {
|
|
20
|
+
job: AiJobSummary & {
|
|
21
|
+
input: AiJobInputRequest;
|
|
22
|
+
};
|
|
23
|
+
orgId: string;
|
|
24
|
+
user: MaybeTokenSource;
|
|
25
|
+
/** The job as the door answered: back on its plan card once the answer reads. */
|
|
26
|
+
onJob: (job: AiJobSummary) => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* What a job waiting in `needs_input` for a person asks (AGL-3978) — a
|
|
30
|
+
* tracking change's Meta pixel ID — as one field per id, each naming its
|
|
31
|
+
* provider. Continue sends the answers through the resume door, which holds
|
|
32
|
+
* each to its pattern and answers a refusal by field, shown under it; an
|
|
33
|
+
* answer that reads returns the job to its plan card. Paused, never failed.
|
|
34
|
+
* The AI jobs drawer and the Assist build card both draw it.
|
|
35
|
+
*/
|
|
36
|
+
export declare function AiJobInputForm({ job, orgId, user, onJob }: AiJobInputFormProps): JSX.Element;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
18
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
|
+
import { Alert, Button, Stack, TextField } from "@mui/material";
|
|
20
|
+
import { useState } from "react";
|
|
21
|
+
import { aiJobInputKey } from "../model/ai-job-input.js";
|
|
22
|
+
import { resumeAiJobRequest } from "./ai-job-requests.js";
|
|
23
|
+
/**
|
|
24
|
+
* What a job waiting in `needs_input` for a person asks (AGL-3978) — a
|
|
25
|
+
* tracking change's Meta pixel ID — as one field per id, each naming its
|
|
26
|
+
* provider. Continue sends the answers through the resume door, which holds
|
|
27
|
+
* each to its pattern and answers a refusal by field, shown under it; an
|
|
28
|
+
* answer that reads returns the job to its plan card. Paused, never failed.
|
|
29
|
+
* The AI jobs drawer and the Assist build card both draw it.
|
|
30
|
+
*/ export function AiJobInputForm({ job, orgId, user, onJob }) {
|
|
31
|
+
const [values, setValues] = useState({});
|
|
32
|
+
const [errors, setErrors] = useState({});
|
|
33
|
+
const [notice, setNotice] = useState(null);
|
|
34
|
+
const [busy, setBusy] = useState(false);
|
|
35
|
+
const submit = async (event)=>{
|
|
36
|
+
var _decision_fieldErrors;
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
setBusy(true);
|
|
39
|
+
setNotice(null);
|
|
40
|
+
const decision = await resumeAiJobRequest(user, orgId, job, {
|
|
41
|
+
input: values
|
|
42
|
+
});
|
|
43
|
+
setErrors((_decision_fieldErrors = decision.fieldErrors) != null ? _decision_fieldErrors : {});
|
|
44
|
+
if (decision.error && !decision.fieldErrors) setNotice(decision.error);
|
|
45
|
+
if (decision.job) onJob(decision.job);
|
|
46
|
+
setBusy(false);
|
|
47
|
+
};
|
|
48
|
+
return /*#__PURE__*/ _jsx(Alert, {
|
|
49
|
+
severity: "warning",
|
|
50
|
+
icon: false,
|
|
51
|
+
sx: {
|
|
52
|
+
mt: 0.5
|
|
53
|
+
},
|
|
54
|
+
"aria-label": "Waiting for your answer",
|
|
55
|
+
children: /*#__PURE__*/ _jsxs(Stack, {
|
|
56
|
+
component: "form",
|
|
57
|
+
spacing: 1,
|
|
58
|
+
onSubmit: (event)=>void submit(event),
|
|
59
|
+
noValidate: true,
|
|
60
|
+
children: [
|
|
61
|
+
/*#__PURE__*/ _jsx("span", {
|
|
62
|
+
children: `Paused. ${job.input.message} Nothing is built until you answer and confirm the plan.`
|
|
63
|
+
}),
|
|
64
|
+
job.input.fields.map((field)=>{
|
|
65
|
+
var _values_key, _errors_key;
|
|
66
|
+
const key = aiJobInputKey(field);
|
|
67
|
+
return /*#__PURE__*/ _jsx(TextField, {
|
|
68
|
+
size: "small",
|
|
69
|
+
label: field.label,
|
|
70
|
+
value: (_values_key = values[key]) != null ? _values_key : '',
|
|
71
|
+
onChange: (event)=>setValues((current)=>_extends({}, current, {
|
|
72
|
+
[key]: event.target.value
|
|
73
|
+
})),
|
|
74
|
+
error: Boolean(errors[key]),
|
|
75
|
+
helperText: (_errors_key = errors[key]) != null ? _errors_key : `It looks like ${field.shape}.`,
|
|
76
|
+
disabled: busy,
|
|
77
|
+
required: true,
|
|
78
|
+
fullWidth: true
|
|
79
|
+
}, key);
|
|
80
|
+
}),
|
|
81
|
+
notice ? /*#__PURE__*/ _jsx("span", {
|
|
82
|
+
role: "alert",
|
|
83
|
+
children: notice
|
|
84
|
+
}) : null,
|
|
85
|
+
/*#__PURE__*/ _jsx(Stack, {
|
|
86
|
+
direction: "row",
|
|
87
|
+
children: /*#__PURE__*/ _jsx(Button, {
|
|
88
|
+
type: "submit",
|
|
89
|
+
size: "small",
|
|
90
|
+
variant: "contained",
|
|
91
|
+
disabled: busy,
|
|
92
|
+
children: 'Continue'
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
]
|
|
96
|
+
})
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
//# sourceMappingURL=ai-job-input-form.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-input-form.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport type { MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token'\nimport { Alert, Button, Stack, TextField } from '@mui/material'\nimport { useState, type FormEvent } from 'react'\nimport { aiJobInputKey } from '../model/ai-job-input'\nimport type { AiJobInputRequest, AiJobSummary } from '../model/ai-jobs.types'\nimport { resumeAiJobRequest } from './ai-job-requests'\n\nexport interface AiJobInputFormProps {\n job: AiJobSummary & { input: AiJobInputRequest }\n orgId: string\n user: MaybeTokenSource\n /** The job as the door answered: back on its plan card once the answer reads. */\n onJob: (job: AiJobSummary) => void\n}\n\n/**\n * What a job waiting in `needs_input` for a person asks (AGL-3978) — a\n * tracking change's Meta pixel ID — as one field per id, each naming its\n * provider. Continue sends the answers through the resume door, which holds\n * each to its pattern and answers a refusal by field, shown under it; an\n * answer that reads returns the job to its plan card. Paused, never failed.\n * The AI jobs drawer and the Assist build card both draw it.\n */\nexport function AiJobInputForm({ job, orgId, user, onJob }: AiJobInputFormProps): JSX.Element {\n const [values, setValues] = useState<Record<string, string>>({})\n const [errors, setErrors] = useState<Record<string, string>>({})\n const [notice, setNotice] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n\n const submit = async (event: FormEvent) => {\n event.preventDefault()\n setBusy(true)\n setNotice(null)\n const decision = await resumeAiJobRequest(user, orgId, job, { input: values })\n setErrors(decision.fieldErrors ?? {})\n if (decision.error && !decision.fieldErrors) setNotice(decision.error)\n if (decision.job) onJob(decision.job)\n setBusy(false)\n }\n\n return (\n <Alert severity=\"warning\" icon={false} sx={{ mt: 0.5 }} aria-label=\"Waiting for your answer\">\n <Stack component=\"form\" spacing={1} onSubmit={(event) => void submit(event)} noValidate>\n <span>{`Paused. ${job.input.message} Nothing is built until you answer and confirm the plan.`}</span>\n {job.input.fields.map((field) => {\n const key = aiJobInputKey(field)\n return (\n <TextField\n key={key}\n size=\"small\"\n label={field.label}\n value={values[key] ?? ''}\n onChange={(event) => setValues((current) => ({ ...current, [key]: event.target.value }))}\n error={Boolean(errors[key])}\n helperText={errors[key] ?? `It looks like ${field.shape}.`}\n disabled={busy}\n required\n fullWidth\n />\n )\n })}\n {notice ? (\n <span role=\"alert\">{notice}</span>\n ) : null}\n <Stack direction=\"row\">\n <Button type=\"submit\" size=\"small\" variant=\"contained\" disabled={busy}>\n {'Continue'}\n </Button>\n </Stack>\n </Stack>\n </Alert>\n )\n}\n"],"names":["Alert","Button","Stack","TextField","useState","aiJobInputKey","resumeAiJobRequest","AiJobInputForm","job","orgId","user","onJob","values","setValues","errors","setErrors","notice","setNotice","busy","setBusy","submit","event","decision","preventDefault","input","fieldErrors","error","severity","icon","sx","mt","aria-label","component","spacing","onSubmit","noValidate","span","message","fields","map","field","key","size","label","value","onChange","current","target","Boolean","helperText","shape","disabled","required","fullWidth","role","direction","type","variant"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;AAGA,SAASA,KAAK,EAAEC,MAAM,EAAEC,KAAK,EAAEC,SAAS,QAAQ,gBAAe;AAC/D,SAASC,QAAQ,QAAwB,QAAO;AAChD,SAASC,aAAa,QAAQ,2BAAuB;AAErD,SAASC,kBAAkB,QAAQ,uBAAmB;AAUtD;;;;;;;CAOC,GACD,OAAO,SAASC,eAAe,EAAEC,GAAG,EAAEC,KAAK,EAAEC,IAAI,EAAEC,KAAK,EAAuB;IAC7E,MAAM,CAACC,QAAQC,UAAU,GAAGT,SAAiC,CAAC;IAC9D,MAAM,CAACU,QAAQC,UAAU,GAAGX,SAAiC,CAAC;IAC9D,MAAM,CAACY,QAAQC,UAAU,GAAGb,SAAwB;IACpD,MAAM,CAACc,MAAMC,QAAQ,GAAGf,SAAS;IAEjC,MAAMgB,SAAS,OAAOC;YAKVC;QAJVD,MAAME,cAAc;QACpBJ,QAAQ;QACRF,UAAU;QACV,MAAMK,WAAW,MAAMhB,mBAAmBI,MAAMD,OAAOD,KAAK;YAAEgB,OAAOZ;QAAO;QAC5EG,WAAUO,wBAAAA,SAASG,WAAW,YAApBH,wBAAwB,CAAC;QACnC,IAAIA,SAASI,KAAK,IAAI,CAACJ,SAASG,WAAW,EAAER,UAAUK,SAASI,KAAK;QACrE,IAAIJ,SAASd,GAAG,EAAEG,MAAMW,SAASd,GAAG;QACpCW,QAAQ;IACV;IAEA,qBACE,KAACnB;QAAM2B,UAAS;QAAUC,MAAM;QAAOC,IAAI;YAAEC,IAAI;QAAI;QAAGC,cAAW;kBACjE,cAAA,MAAC7B;YAAM8B,WAAU;YAAOC,SAAS;YAAGC,UAAU,CAACb,QAAU,KAAKD,OAAOC;YAAQc,UAAU;;8BACrF,KAACC;8BAAM,CAAC,QAAQ,EAAE5B,IAAIgB,KAAK,CAACa,OAAO,CAAC,wDAAwD,CAAC;;gBAC5F7B,IAAIgB,KAAK,CAACc,MAAM,CAACC,GAAG,CAAC,CAACC;wBAOV5B,aAGKE;oBAThB,MAAM2B,MAAMpC,cAAcmC;oBAC1B,qBACE,KAACrC;wBAECuC,MAAK;wBACLC,OAAOH,MAAMG,KAAK;wBAClBC,KAAK,GAAEhC,cAAAA,MAAM,CAAC6B,IAAI,YAAX7B,cAAe;wBACtBiC,UAAU,CAACxB,QAAUR,UAAU,CAACiC,UAAa,aAAKA;oCAAS,CAACL,IAAI,EAAEpB,MAAM0B,MAAM,CAACH,KAAK;;wBACpFlB,OAAOsB,QAAQlC,MAAM,CAAC2B,IAAI;wBAC1BQ,UAAU,GAAEnC,cAAAA,MAAM,CAAC2B,IAAI,YAAX3B,cAAe,CAAC,cAAc,EAAE0B,MAAMU,KAAK,CAAC,CAAC,CAAC;wBAC1DC,UAAUjC;wBACVkC,QAAQ;wBACRC,SAAS;uBATJZ;gBAYX;gBACCzB,uBACC,KAACoB;oBAAKkB,MAAK;8BAAStC;qBAClB;8BACJ,KAACd;oBAAMqD,WAAU;8BACf,cAAA,KAACtD;wBAAOuD,MAAK;wBAASd,MAAK;wBAAQe,SAAQ;wBAAYN,UAAUjC;kCAC9D;;;;;;AAMb"}
|
|
@@ -26,6 +26,8 @@ export interface AiJobDecision {
|
|
|
26
26
|
* inline (AGL-3722): what the plan just spent, so the strip moves with it.
|
|
27
27
|
*/
|
|
28
28
|
meter?: unknown;
|
|
29
|
+
/** Why each answer a `needs_input` job asked for cannot be used, by field (AGL-3978). */
|
|
30
|
+
fieldErrors?: Record<string, string> | null;
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
31
33
|
* What a resume asks beyond confirming (AGL-3616): a build's plan confirmed
|
|
@@ -38,6 +40,8 @@ export interface AiJobResumeOptions {
|
|
|
38
40
|
creditsConfirmed?: boolean;
|
|
39
41
|
/** "Build the home page first" (AGL-3722): the plan narrowed to its home page and what it needs. */
|
|
40
42
|
reduce?: 'first-page';
|
|
43
|
+
/** The answers a job waiting in `needs_input` asks for (AGL-3978), by `aiJobInputKey`. */
|
|
44
|
+
input?: Record<string, string>;
|
|
41
45
|
}
|
|
42
46
|
/**
|
|
43
47
|
* Confirms a plan, or tries again a step whose answer broke a building rule
|
|
@@ -41,6 +41,8 @@ import { publishAiJob } from "./ai-jobs-store.js";
|
|
|
41
41
|
creditsConfirmed: true
|
|
42
42
|
} : {}, options.reduce ? {
|
|
43
43
|
reduce: options.reduce
|
|
44
|
+
} : {}, options.input ? {
|
|
45
|
+
input: options.input
|
|
44
46
|
} : {}))
|
|
45
47
|
});
|
|
46
48
|
const payload = await response.json().catch(()=>null);
|
|
@@ -52,6 +54,8 @@ import { publishAiJob } from "./ai-jobs-store.js";
|
|
|
52
54
|
error: response.ok ? null : String((_ref2 = payload == null ? void 0 : payload.error) != null ? _ref2 : 'The job could not be resumed — try again.')
|
|
53
55
|
}, credits ? {
|
|
54
56
|
credits
|
|
57
|
+
} : {}, (payload == null ? void 0 : payload.fieldErrors) ? {
|
|
58
|
+
fieldErrors: payload.fieldErrors
|
|
55
59
|
} : {});
|
|
56
60
|
} catch (unused) {
|
|
57
61
|
return {
|