@aglyn/plugins-ai 1.0.0-beta.229 → 1.0.0-beta.231
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +17 -17
- package/src/lib/activity/ai-activity-actions.js +7 -1
- 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-automation-zones.d.ts +38 -0
- package/src/lib/components/ai-automation-zones.js +5 -1
- package/src/lib/components/ai-automation-zones.js.map +1 -1
- package/src/lib/components/ai-brief-dialog.component.d.ts +20 -2
- package/src/lib/components/ai-brief-dialog.component.js +87 -8
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-campaign-create.component.d.ts +54 -0
- package/src/lib/components/ai-campaign-create.component.js +270 -0
- package/src/lib/components/ai-campaign-create.component.js.map +1 -0
- package/src/lib/components/ai-describe-automation.component.d.ts +8 -3
- package/src/lib/components/ai-describe-automation.component.js +15 -6
- package/src/lib/components/ai-describe-automation.component.js.map +1 -1
- package/src/lib/components/ai-describe-button.component.d.ts +20 -8
- package/src/lib/components/ai-describe-button.component.js +19 -36
- package/src/lib/components/ai-describe-button.component.js.map +1 -1
- package/src/lib/components/ai-describe-email.component.d.ts +31 -0
- package/src/lib/components/ai-describe-email.component.js +22 -0
- package/src/lib/components/ai-describe-email.component.js.map +1 -0
- package/src/lib/components/ai-describe-org-automation.component.d.ts +36 -0
- package/src/lib/components/ai-describe-org-automation.component.js +238 -0
- package/src/lib/components/ai-describe-org-automation.component.js.map +1 -0
- package/src/lib/components/ai-experiment-cards.component.d.ts +9 -1
- package/src/lib/components/ai-experiment-cards.component.js +104 -18
- package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
- package/src/lib/components/ai-explain-automation.component.js +1 -5
- package/src/lib/components/ai-explain-automation.component.js.map +1 -1
- package/src/lib/components/ai-funnel-zones.component.d.ts +61 -0
- package/src/lib/components/ai-funnel-zones.component.js +178 -0
- package/src/lib/components/ai-funnel-zones.component.js.map +1 -0
- package/src/lib/components/ai-insight-card.component.d.ts +42 -0
- package/src/lib/components/ai-insight-card.component.js +137 -0
- package/src/lib/components/ai-insight-card.component.js.map +1 -0
- package/src/lib/components/ai-insight-dialog.component.d.ts +6 -0
- package/src/lib/components/ai-insight-dialog.component.js +9 -3
- package/src/lib/components/ai-insight-dialog.component.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.d.ts +7 -2
- package/src/lib/components/ai-job-plan.component.js +38 -6
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-job-requests.d.ts +17 -1
- package/src/lib/components/ai-job-requests.js +39 -3
- package/src/lib/components/ai-job-requests.js.map +1 -1
- package/src/lib/components/ai-logic-zones.d.ts +64 -0
- package/src/lib/components/ai-logic-zones.js +18 -0
- package/src/lib/components/ai-logic-zones.js.map +1 -0
- package/src/lib/components/ai-logic.component.d.ts +77 -0
- package/src/lib/components/ai-logic.component.js +553 -0
- package/src/lib/components/ai-logic.component.js.map +1 -0
- package/src/lib/components/ai-marketing-insight.component.d.ts +30 -0
- package/src/lib/components/ai-marketing-insight.component.js +80 -0
- package/src/lib/components/ai-marketing-insight.component.js.map +1 -0
- package/src/lib/components/ai-media-create.component.d.ts +55 -0
- package/src/lib/components/ai-media-create.component.js +433 -0
- package/src/lib/components/ai-media-create.component.js.map +1 -0
- package/src/lib/components/ai-overlay-cards.component.d.ts +83 -0
- package/src/lib/components/ai-overlay-cards.component.js +505 -0
- package/src/lib/components/ai-overlay-cards.component.js.map +1 -0
- 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-product-zones.d.ts +5 -0
- package/src/lib/components/ai-product-zones.js +1 -1
- package/src/lib/components/ai-product-zones.js.map +1 -1
- package/src/lib/components/ai-products-create-button.component.d.ts +42 -0
- package/src/lib/components/ai-products-create-button.component.js +67 -0
- package/src/lib/components/ai-products-create-button.component.js.map +1 -0
- package/src/lib/components/ai-products-hub-card.component.js +100 -81
- package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
- package/src/lib/components/ai-revise-automation.component.d.ts +16 -0
- package/src/lib/components/ai-revise-automation.component.js +207 -0
- package/src/lib/components/ai-revise-automation.component.js.map +1 -0
- package/src/lib/components/ai-site-build-page.component.js +160 -24
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-upsell-dialog.component.d.ts +53 -0
- package/src/lib/components/ai-upsell-dialog.component.js +177 -0
- package/src/lib/components/ai-upsell-dialog.component.js.map +1 -0
- package/src/lib/components/assist-build-card.component.d.ts +42 -0
- package/src/lib/components/assist-build-card.component.js +183 -0
- package/src/lib/components/assist-build-card.component.js.map +1 -0
- package/src/lib/components/assist-edit-canvas.d.ts +2 -0
- package/src/lib/components/assist-edit-canvas.js +56 -1
- package/src/lib/components/assist-edit-canvas.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +48 -3
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +51 -2
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/use-ai-job-run.d.ts +3 -14
- package/src/lib/components/use-ai-job-run.js +0 -50
- package/src/lib/components/use-ai-job-run.js.map +1 -1
- package/src/lib/jobs/ai-build-capabilities.d.ts +58 -0
- package/src/lib/jobs/ai-build-capabilities.js +256 -0
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -0
- package/src/lib/jobs/ai-build-unit-outcome.d.ts +52 -0
- package/src/lib/jobs/ai-build-unit-outcome.js +140 -0
- package/src/lib/jobs/ai-build-unit-outcome.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.d.ts +144 -0
- package/src/lib/jobs/ai-job-build-step.js +668 -0
- package/src/lib/jobs/ai-job-build-step.js.map +1 -0
- package/src/lib/jobs/ai-job-draft-ids.js +9 -1
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.d.ts +14 -4
- package/src/lib/jobs/ai-job-drafts.js +25 -4
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- 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 +8 -1
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +73 -0
- package/src/lib/jobs/ai-job-layout-language.js +194 -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 +105 -5
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-logic-step.d.ts +107 -0
- package/src/lib/jobs/ai-job-logic-step.js +395 -0
- package/src/lib/jobs/ai-job-logic-step.js.map +1 -0
- package/src/lib/jobs/ai-job-overlay-copy.d.ts +70 -0
- package/src/lib/jobs/ai-job-overlay-copy.js +178 -0
- package/src/lib/jobs/ai-job-overlay-copy.js.map +1 -0
- package/src/lib/jobs/ai-job-page-language.d.ts +149 -0
- package/src/lib/jobs/ai-job-page-language.js +355 -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 +111 -20
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +27 -2
- package/src/lib/jobs/ai-job-plan-step.js +87 -13
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +19 -1
- package/src/lib/jobs/ai-job-site-step.js +220 -61
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +22 -1
- package/src/lib/jobs/ai-job-text-step.js +12 -0
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.d.ts +54 -4
- package/src/lib/jobs/ai-job-workflow-step.js +320 -15
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +80 -2
- package/src/lib/jobs/ai-jobs.js +391 -18
- 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/jobs/ai-logic-records.d.ts +40 -0
- package/src/lib/jobs/ai-logic-records.js +55 -0
- package/src/lib/jobs/ai-logic-records.js.map +1 -0
- package/src/lib/jobs/ai-site-publish.d.ts +7 -0
- package/src/lib/jobs/ai-site-publish.js +9 -0
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/ai-workflow-records.d.ts +4 -0
- package/src/lib/jobs/ai-workflow-records.js +40 -1
- package/src/lib/jobs/ai-workflow-records.js.map +1 -1
- package/src/lib/layout-language/ai-layout-compiler.d.ts +145 -0
- package/src/lib/layout-language/ai-layout-compiler.js +1148 -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 +457 -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 +161 -0
- package/src/lib/layout-language/ai-layout-language.js +446 -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-store.d.ts +39 -0
- package/src/lib/layout-language/ai-layout-store.js +69 -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-automation-draft.d.ts +18 -0
- package/src/lib/model/ai-automation-draft.js +86 -0
- package/src/lib/model/ai-automation-draft.js.map +1 -1
- package/src/lib/model/ai-build-job.d.ts +201 -0
- package/src/lib/model/ai-build-job.js +472 -0
- package/src/lib/model/ai-build-job.js.map +1 -0
- package/src/lib/model/ai-build-plan.d.ts +61 -0
- package/src/lib/model/ai-build-plan.js +154 -9
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +73 -0
- package/src/lib/model/ai-build-progress.js +170 -0
- package/src/lib/model/ai-build-progress.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-insight.d.ts +20 -10
- package/src/lib/model/ai-insight.js +72 -9
- package/src/lib/model/ai-insight.js.map +1 -1
- package/src/lib/model/ai-job-activity.d.ts +22 -0
- package/src/lib/model/ai-job-activity.js +25 -2
- 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 +99 -2
- package/src/lib/model/ai-jobs.types.js +3 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-logic-job.d.ts +108 -0
- package/src/lib/model/ai-logic-job.js +327 -0
- package/src/lib/model/ai-logic-job.js.map +1 -0
- package/src/lib/model/ai-media-credits.d.ts +52 -0
- package/src/lib/model/ai-media-credits.js +61 -0
- package/src/lib/model/ai-media-credits.js.map +1 -0
- package/src/lib/model/ai-overlay-copy.d.ts +114 -0
- package/src/lib/model/ai-overlay-copy.js +175 -0
- package/src/lib/model/ai-overlay-copy.js.map +1 -0
- package/src/lib/model/ai-site-build-progress.d.ts +25 -4
- package/src/lib/model/ai-site-build-progress.js +194 -11
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +8 -0
- package/src/lib/model/ai-site-job.js +9 -0
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-start-seo.d.ts +1 -1
- package/src/lib/model/ai-site-start.d.ts +5 -2
- package/src/lib/model/ai-site-start.js +5 -2
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/ai-svg.d.ts +69 -0
- package/src/lib/model/ai-svg.js +265 -0
- package/src/lib/model/ai-svg.js.map +1 -0
- package/src/lib/model/ai-usage-by-user.d.ts +3 -2
- package/src/lib/model/ai-usage-by-user.js.map +1 -1
- package/src/lib/model/ai-workflow-job.d.ts +92 -4
- package/src/lib/model/ai-workflow-job.js +124 -7
- package/src/lib/model/ai-workflow-job.js.map +1 -1
- package/src/lib/model/assist-build.d.ts +58 -0
- package/src/lib/model/assist-build.js +52 -0
- package/src/lib/model/assist-build.js.map +1 -0
- package/src/lib/model/assist-edit.d.ts +48 -3
- package/src/lib/model/assist-edit.js +48 -4
- package/src/lib/model/assist-edit.js.map +1 -1
- package/src/lib/plugin-config.js +2 -0
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +308 -47
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/catalog.d.ts +82 -5
- package/src/lib/providers/catalog.js +83 -12
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/image-contract.d.ts +116 -0
- package/src/lib/providers/image-contract.js +74 -0
- package/src/lib/providers/image-contract.js.map +1 -0
- package/src/lib/providers/model-choice.js +11 -0
- 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.d.ts +121 -0
- package/src/lib/providers/vertex-image.js +312 -0
- package/src/lib/providers/vertex-image.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +101 -0
- package/src/lib/runtime/ai-doctrine-validators.js +525 -4
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +21 -6
- 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 +15 -1
- package/src/lib/runtime/ai-eval.js +52 -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-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/experiment-variant-copy.d.ts +34 -0
- package/src/lib/runtime/experiment-variant-copy.js +102 -0
- package/src/lib/runtime/experiment-variant-copy.js.map +1 -0
- package/src/lib/server/ai-experiment-versions.d.ts +51 -0
- package/src/lib/server/ai-experiment-versions.js +270 -0
- package/src/lib/server/ai-experiment-versions.js.map +1 -0
- package/src/lib/server/ai-jobs-resume.js +74 -4
- package/src/lib/server/ai-jobs-resume.js.map +1 -1
- package/src/lib/server/ai-media-image.d.ts +66 -0
- package/src/lib/server/ai-media-image.js +521 -0
- package/src/lib/server/ai-media-image.js.map +1 -0
- package/src/lib/server/ai-media-svg.d.ts +91 -0
- package/src/lib/server/ai-media-svg.js +186 -0
- package/src/lib/server/ai-media-svg.js.map +1 -0
- package/src/lib/server/assist-build.d.ts +54 -0
- package/src/lib/server/assist-build.js +121 -0
- package/src/lib/server/assist-build.js.map +1 -0
- package/src/lib/server/assist-chat.js +108 -12
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit.js +213 -3
- package/src/lib/server/assist-edit.js.map +1 -1
- package/src/lib/server/assist-view-context.js +242 -4
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server.js +26 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/tools/ai-logic-tool.d.ts +27 -0
- package/src/lib/tools/ai-logic-tool.js +207 -0
- package/src/lib/tools/ai-logic-tool.js.map +1 -0
- package/src/lib/tools/ai-overlay-tool.d.ts +35 -0
- package/src/lib/tools/ai-overlay-tool.js +107 -0
- package/src/lib/tools/ai-overlay-tool.js.map +1 -0
- package/src/lib/tools/ai-workflow-tool.d.ts +17 -2
- package/src/lib/tools/ai-workflow-tool.js +47 -14
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
- package/src/lib/usage/assist-job-refund.d.ts +28 -1
- package/src/lib/usage/assist-job-refund.js +44 -9
- package/src/lib/usage/assist-job-refund.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +5 -0
- package/src/lib/usage/assist-usage.js +2 -0
- package/src/lib/usage/assist-usage.js.map +1 -1
|
@@ -20,56 +20,6 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
20
20
|
import { AI_JOB_TERMINAL_STATUSES } from "../model/ai-jobs.types.js";
|
|
21
21
|
import { followAiJobEvents } from "./ai-job-events.js";
|
|
22
22
|
import { publishAiJob } from "./ai-jobs-store.js";
|
|
23
|
-
/**
|
|
24
|
-
* One answer per member and workspace, however many controls ask. The
|
|
25
|
-
* automation run history draws a control on every failed run, and each asking
|
|
26
|
-
* the route would be a request a row.
|
|
27
|
-
*/ const verdicts = new Map();
|
|
28
|
-
/** Forgets every answer, so the next control to mount asks the route again. */ export function forgetAiJobsVerdicts() {
|
|
29
|
-
verdicts.clear();
|
|
30
|
-
}
|
|
31
|
-
function askJobsRoute(user, uid, orgId) {
|
|
32
|
-
const key = `${uid}\n${orgId}`;
|
|
33
|
-
const known = verdicts.get(key);
|
|
34
|
-
if (known) return known;
|
|
35
|
-
const answer = authorizedFetch(user, `/api/ai/jobs?orgId=${encodeURIComponent(orgId)}&limit=1`)// The release flag off (404) and a plan or member without generation
|
|
36
|
-
// (403): the feature is not this workspace's, so the control is absent.
|
|
37
|
-
.then((response)=>response.status === 404 || response.status === 403 ? 'hidden' : 'ready').catch(()=>{
|
|
38
|
-
// A request that never arrived is no answer, so the next control asks again.
|
|
39
|
-
verdicts.delete(key);
|
|
40
|
-
return 'hidden';
|
|
41
|
-
});
|
|
42
|
-
verdicts.set(key, answer);
|
|
43
|
-
return answer;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Whether a control that starts a job should be drawn. The shell decided the
|
|
47
|
-
* plan and the member's permission before mounting it; the release flag is the
|
|
48
|
-
* route's to decide.
|
|
49
|
-
*/ export function useAiJobsVerdict(user, orgId) {
|
|
50
|
-
var _ref;
|
|
51
|
-
// Held in a ref so the question keys on WHO is signed in, never on the
|
|
52
|
-
// identity of the object that says so.
|
|
53
|
-
const userRef = useRef(user);
|
|
54
|
-
userRef.current = user;
|
|
55
|
-
const uid = (_ref = user == null ? void 0 : user.uid) != null ? _ref : null;
|
|
56
|
-
const [verdict, setVerdict] = useState('checking');
|
|
57
|
-
useEffect(()=>{
|
|
58
|
-
if (!orgId || !uid) return;
|
|
59
|
-
let active = true;
|
|
60
|
-
setVerdict('checking');
|
|
61
|
-
void askJobsRoute(userRef.current, uid, orgId).then((answer)=>{
|
|
62
|
-
if (active) setVerdict(answer);
|
|
63
|
-
});
|
|
64
|
-
return ()=>{
|
|
65
|
-
active = false;
|
|
66
|
-
};
|
|
67
|
-
}, [
|
|
68
|
-
orgId,
|
|
69
|
-
uid
|
|
70
|
-
]);
|
|
71
|
-
return verdict;
|
|
72
|
-
}
|
|
73
23
|
/** Whether a job has stopped moving: finished, or waiting on something only a person can change. */ export function aiJobSettled(job) {
|
|
74
24
|
if (!job) return false;
|
|
75
25
|
return AI_JOB_TERMINAL_STATUSES.includes(job.status) || job.status === 'needs_input' || job.status === 'needs_review';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/use-ai-job-run.ts"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport {\n authorizedFetch,\n type MaybeTokenSource,\n type TokenSource,\n} from '@aglyn/shared-util-http/authorized-token'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n AI_JOB_TERMINAL_STATUSES,\n type AiJobKind,\n type AiJobSummary,\n} from '../model/ai-jobs.types'\nimport { followAiJobEvents } from './ai-job-events'\nimport { publishAiJob } from './ai-jobs-store'\n\n/**\n * Starting one AI job from a console control, and following it until it\n * settles (AGL-2919): the jobs route's create door, then its events door, the\n * way the SEO card follows its jobs. A control that starts a job the beat\n * runs shows its progress here and its result when it arrives; closing the\n * control stops following, and the job carries on in AI jobs.\n */\n\n/** Whether the jobs route serves a workspace: still asking, yes, or no. */\nexport type AiJobsVerdict = 'checking' | 'ready' | 'hidden'\n\n/**\n * One answer per member and workspace, however many controls ask. The\n * automation run history draws a control on every failed run, and each asking\n * the route would be a request a row.\n */\nconst verdicts = new Map<string, Promise<Exclude<AiJobsVerdict, 'checking'>>>()\n\n/** Forgets every answer, so the next control to mount asks the route again. */\nexport function forgetAiJobsVerdicts(): void {\n verdicts.clear()\n}\n\nfunction askJobsRoute(\n user: MaybeTokenSource,\n uid: string,\n orgId: string,\n): Promise<Exclude<AiJobsVerdict, 'checking'>> {\n const key = `${uid}\\n${orgId}`\n const known = verdicts.get(key)\n if (known) return known\n const answer = authorizedFetch(user, `/api/ai/jobs?orgId=${encodeURIComponent(orgId)}&limit=1`)\n // The release flag off (404) and a plan or member without generation\n // (403): the feature is not this workspace's, so the control is absent.\n .then((response): Exclude<AiJobsVerdict, 'checking'> =>\n response.status === 404 || response.status === 403 ? 'hidden' : 'ready',\n )\n .catch((): Exclude<AiJobsVerdict, 'checking'> => {\n // A request that never arrived is no answer, so the next control asks again.\n verdicts.delete(key)\n return 'hidden'\n })\n verdicts.set(key, answer)\n return answer\n}\n\n/**\n * Whether a control that starts a job should be drawn. The shell decided the\n * plan and the member's permission before mounting it; the release flag is the\n * route's to decide.\n */\nexport function useAiJobsVerdict(\n user: (TokenSource & { uid?: string | null }) | null | undefined,\n orgId: string | undefined,\n): AiJobsVerdict {\n // Held in a ref so the question keys on WHO is signed in, never on the\n // identity of the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const uid = user?.uid ?? null\n const [verdict, setVerdict] = useState<AiJobsVerdict>('checking')\n\n useEffect(() => {\n if (!orgId || !uid) return\n let active = true\n setVerdict('checking')\n void askJobsRoute(userRef.current, uid, orgId).then((answer) => {\n if (active) setVerdict(answer)\n })\n return () => {\n active = false\n }\n }, [orgId, uid])\n\n return verdict\n}\n\n/** What a control asks the create door for. */\nexport interface AiJobRequest {\n orgId: string\n hostId: string\n kind: AiJobKind\n brief: string\n inputs: Record<string, string>\n}\n\n/** Whether a job has stopped moving: finished, or waiting on something only a person can change. */\nexport function aiJobSettled(job: AiJobSummary | null): boolean {\n if (!job) return false\n return (\n AI_JOB_TERMINAL_STATUSES.includes(job.status) ||\n job.status === 'needs_input' ||\n job.status === 'needs_review'\n )\n}\n\n/**\n * Why a settled job has no result to show, in words a person can act on, or\n * `null` for a job that finished.\n */\nexport function aiJobProblem(job: AiJobSummary | null, failed: string): string | null {\n if (!job || !aiJobSettled(job) || job.status === 'done') return null\n if (job.status === 'canceled') return 'The job was canceled.'\n if (job.status === 'needs_review') return job.review?.message || job.error || failed\n return job.error || failed\n}\n\nexport interface AiJobRun {\n /** The job as it last arrived; `null` before one is started. */\n job: AiJobSummary | null\n /** The create door is being asked. */\n starting: boolean\n /** The job exists and has not settled. */\n running: boolean\n /** Why the create door refused, or could not be reached. */\n notice: string | null\n /** Starts the job, `true` once it exists, and follows it from there; `false` when the door refused. */\n start: (request: AiJobRequest) => Promise<boolean>\n /** Stops following and forgets the job, for a control asked again. */\n reset: () => void\n}\n\nexport function useAiJobRun(user: MaybeTokenSource, failed: string): AiJobRun {\n const userRef = useRef(user)\n userRef.current = user\n const [job, setJob] = useState<AiJobSummary | null>(null)\n const [starting, setStarting] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n const watchRef = useRef<AbortController | null>(null)\n\n useEffect(() => () => watchRef.current?.abort(), [])\n\n /** Follows a job through the events route until it settles or the control goes. */\n const watch = useCallback(async (jobId: string, orgId: string) => {\n watchRef.current?.abort()\n const controller = new AbortController()\n watchRef.current = controller\n await followAiJobEvents(() => userRef.current, orgId, jobId, controller.signal, setJob)\n }, [])\n\n const start = useCallback(\n async (request: AiJobRequest) => {\n watchRef.current?.abort()\n setStarting(true)\n setNotice(null)\n setJob(null)\n let next: AiJobSummary | undefined\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(request),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(locked ? lockdownRefusalText(locked) : String(payload?.error ?? failed))\n return false\n }\n next = payload?.job as AiJobSummary | undefined\n if (!next) {\n setNotice(failed)\n return false\n }\n setJob(next)\n // The indicator counts it from the moment it exists (AGL-3593).\n publishAiJob(next)\n } catch {\n setNotice(failed)\n return false\n } finally {\n setStarting(false)\n }\n if (!aiJobSettled(next)) void watch(next.id, request.orgId)\n return true\n },\n [failed, watch],\n )\n\n const reset = useCallback(() => {\n watchRef.current?.abort()\n setJob(null)\n setNotice(null)\n }, [])\n\n return { job, starting, running: Boolean(job && !aiJobSettled(job)), notice, start, reset }\n}\n"],"names":["lockdownRefusalText","parseLockdownRefusal","authorizedFetch","useCallback","useEffect","useRef","useState","AI_JOB_TERMINAL_STATUSES","followAiJobEvents","publishAiJob","verdicts","Map","forgetAiJobsVerdicts","clear","askJobsRoute","user","uid","orgId","key","known","get","answer","encodeURIComponent","then","response","status","catch","delete","set","useAiJobsVerdict","userRef","current","verdict","setVerdict","active","aiJobSettled","job","includes","aiJobProblem","failed","review","message","error","useAiJobRun","setJob","starting","setStarting","notice","setNotice","watchRef","abort","watch","jobId","controller","AbortController","signal","start","request","next","method","headers","body","JSON","stringify","payload","json","ok","locked","String","id","reset","running","Boolean"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/use-ai-job-run.ts"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport {\n authorizedFetch,\n type MaybeTokenSource,\n} from '@aglyn/shared-util-http/authorized-token'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n AI_JOB_TERMINAL_STATUSES,\n type AiJobKind,\n type AiJobSummary,\n} from '../model/ai-jobs.types'\nimport { followAiJobEvents } from './ai-job-events'\nimport { publishAiJob } from './ai-jobs-store'\n\n/**\n * Starting one AI job from a console control, and following it until it\n * settles (AGL-2919): the jobs route's create door, then its events door, the\n * way the SEO card follows its jobs. A control that starts a job the beat\n * runs shows its progress here and its result when it arrives; closing the\n * control stops following, and the job carries on in AI jobs.\n */\n\n/** What a control asks the create door for. */\nexport interface AiJobRequest {\n orgId: string\n /** `null` for a job about the workspace rather than one site. */\n hostId: string | null\n kind: AiJobKind\n brief: string\n inputs: Record<string, string>\n}\n\n/** Whether a job has stopped moving: finished, or waiting on something only a person can change. */\nexport function aiJobSettled(job: AiJobSummary | null): boolean {\n if (!job) return false\n return (\n AI_JOB_TERMINAL_STATUSES.includes(job.status) ||\n job.status === 'needs_input' ||\n job.status === 'needs_review'\n )\n}\n\n/**\n * Why a settled job has no result to show, in words a person can act on, or\n * `null` for a job that finished.\n */\nexport function aiJobProblem(job: AiJobSummary | null, failed: string): string | null {\n if (!job || !aiJobSettled(job) || job.status === 'done') return null\n if (job.status === 'canceled') return 'The job was canceled.'\n if (job.status === 'needs_review') return job.review?.message || job.error || failed\n return job.error || failed\n}\n\nexport interface AiJobRun {\n /** The job as it last arrived; `null` before one is started. */\n job: AiJobSummary | null\n /** The create door is being asked. */\n starting: boolean\n /** The job exists and has not settled. */\n running: boolean\n /** Why the create door refused, or could not be reached. */\n notice: string | null\n /** Starts the job, `true` once it exists, and follows it from there; `false` when the door refused. */\n start: (request: AiJobRequest) => Promise<boolean>\n /** Stops following and forgets the job, for a control asked again. */\n reset: () => void\n}\n\nexport function useAiJobRun(user: MaybeTokenSource, failed: string): AiJobRun {\n const userRef = useRef(user)\n userRef.current = user\n const [job, setJob] = useState<AiJobSummary | null>(null)\n const [starting, setStarting] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n const watchRef = useRef<AbortController | null>(null)\n\n useEffect(() => () => watchRef.current?.abort(), [])\n\n /** Follows a job through the events route until it settles or the control goes. */\n const watch = useCallback(async (jobId: string, orgId: string) => {\n watchRef.current?.abort()\n const controller = new AbortController()\n watchRef.current = controller\n await followAiJobEvents(() => userRef.current, orgId, jobId, controller.signal, setJob)\n }, [])\n\n const start = useCallback(\n async (request: AiJobRequest) => {\n watchRef.current?.abort()\n setStarting(true)\n setNotice(null)\n setJob(null)\n let next: AiJobSummary | undefined\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(request),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(locked ? lockdownRefusalText(locked) : String(payload?.error ?? failed))\n return false\n }\n next = payload?.job as AiJobSummary | undefined\n if (!next) {\n setNotice(failed)\n return false\n }\n setJob(next)\n // The indicator counts it from the moment it exists (AGL-3593).\n publishAiJob(next)\n } catch {\n setNotice(failed)\n return false\n } finally {\n setStarting(false)\n }\n if (!aiJobSettled(next)) void watch(next.id, request.orgId)\n return true\n },\n [failed, watch],\n )\n\n const reset = useCallback(() => {\n watchRef.current?.abort()\n setJob(null)\n setNotice(null)\n }, [])\n\n return { job, starting, running: Boolean(job && !aiJobSettled(job)), notice, start, reset }\n}\n"],"names":["lockdownRefusalText","parseLockdownRefusal","authorizedFetch","useCallback","useEffect","useRef","useState","AI_JOB_TERMINAL_STATUSES","followAiJobEvents","publishAiJob","aiJobSettled","job","includes","status","aiJobProblem","failed","review","message","error","useAiJobRun","user","userRef","current","setJob","starting","setStarting","notice","setNotice","watchRef","abort","watch","jobId","orgId","controller","AbortController","signal","start","request","next","response","method","headers","body","JSON","stringify","payload","json","catch","ok","locked","String","id","reset","running","Boolean"],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
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 { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types';
|
|
18
|
+
import { type PluginAiCapability, type ResolvedPluginAiCapability } from '@aglyn/aglyn/plugin-manager/plugin-ai-capabilities';
|
|
19
|
+
import type { AiBuildOps } from '../model/ai-build-job';
|
|
20
|
+
/** The operations this plugin owns, as it registers them. */
|
|
21
|
+
export declare const AI_OWNED_CAPABILITIES: readonly PluginAiCapability[];
|
|
22
|
+
/** Registers this plugin's build operations; the console surface calls it. */
|
|
23
|
+
export declare function registerAiBuildCapabilities(): void;
|
|
24
|
+
/** What decides which operations a build may use here. */
|
|
25
|
+
export interface AiBuildOpsContext {
|
|
26
|
+
orgId: string;
|
|
27
|
+
hostId: string | null;
|
|
28
|
+
org: Partial<AglynOrgBilling> | null;
|
|
29
|
+
/** The site document, for the plugins switched on and off on it. */
|
|
30
|
+
host: Readonly<Record<string, unknown>> | null;
|
|
31
|
+
freeTaste: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface AiBuildOpsDeps {
|
|
34
|
+
/** The registered capabilities; the core registry's otherwise. */
|
|
35
|
+
capabilities?: () => ResolvedPluginAiCapability[];
|
|
36
|
+
/** The release-flag read; the platform's otherwise. */
|
|
37
|
+
releasedPlugins?: (pluginIds: string[], orgId: string) => Promise<string[]>;
|
|
38
|
+
/** Whether a runner is registered for a kind; the machine's otherwise. */
|
|
39
|
+
hasRunner?: (kind: string) => boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The operations a build may plan on this site, by op (AGL-3616): every
|
|
43
|
+
* registered capability whose owner runs here — switched on for the
|
|
44
|
+
* workspace and the site, past its release flag — whose entitlement the plan
|
|
45
|
+
* holds, which the Free taste may use where the workspace is on it, and,
|
|
46
|
+
* for an AI-run operation, whose runner this process loaded. One release-flag
|
|
47
|
+
* read for every owner at once. What a member may do and what an allowance
|
|
48
|
+
* has left are the owner's to say when the item runs (`refusal`).
|
|
49
|
+
*/
|
|
50
|
+
export declare function aiBuildOps(context: AiBuildOpsContext, deps?: AiBuildOpsDeps): Promise<AiBuildOps>;
|
|
51
|
+
/**
|
|
52
|
+
* The lines the plan step's request lists the item operations in: each op
|
|
53
|
+
* with what it makes, its arguments and their meaning. Per site, so they ride
|
|
54
|
+
* the job's own turn and never a cached system block.
|
|
55
|
+
*/
|
|
56
|
+
export declare function aiBuildOpLines(ops: AiBuildOps, structural: readonly string[]): string[];
|
|
57
|
+
/** What a build can make here, in the owner's words, for the chat's instructions. */
|
|
58
|
+
export declare function aiBuildIntents(ops: AiBuildOps): string[];
|
|
@@ -0,0 +1,256 @@
|
|
|
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 { checkEntitlement } from "@aglyn/aglyn/app-utils/plan-entitlements";
|
|
17
|
+
import { isHostPluginEnabled } from "@aglyn/aglyn/plugin-manager/enabled-plugins";
|
|
18
|
+
import { pluginAiCapabilities, registerPluginAiCapability } from "@aglyn/aglyn/plugin-manager/plugin-ai-capabilities";
|
|
19
|
+
import { filterEnabledPluginsByReleaseFlags } from "@aglyn/tenant-data-admin/server/release-flags";
|
|
20
|
+
import { AI_TEMPLATE_SUBJECTS } from "../model/ai-template-subjects.js";
|
|
21
|
+
import { AI_SITE_PASS_CREDITS } from "../model/ai-site-job.js";
|
|
22
|
+
import { aiJobStepRunnerFor } from "./ai-jobs.js";
|
|
23
|
+
/**
|
|
24
|
+
* What THIS plugin lets a build make (AGL-3616), registered on the core's
|
|
25
|
+
* capability contract exactly as any other plugin registers its own, so the
|
|
26
|
+
* planner, the gates and the estimate read one list and name no plugin.
|
|
27
|
+
*
|
|
28
|
+
* Every one is built by a runner this plugin already has (`runnerKind`):
|
|
29
|
+
* pages, layouts, forms, components and email designs are the plan's
|
|
30
|
+
* creations and screens; templates, campaigns and automations are items with
|
|
31
|
+
* arguments. A campaign and an automation are still WRITTEN by the marketing
|
|
32
|
+
* and workflows plugins, through the writers their runners already call.
|
|
33
|
+
*/ /** This plugin's id, as its registrations name their owner. */ const AI_PLUGIN_ID = 'ai';
|
|
34
|
+
const NO_ARGS = {
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: {},
|
|
37
|
+
additionalProperties: false
|
|
38
|
+
};
|
|
39
|
+
const passes = (count)=>()=>count * AI_SITE_PASS_CREDITS;
|
|
40
|
+
/** The operations this plugin owns, as it registers them. */ export const AI_OWNED_CAPABILITIES = [
|
|
41
|
+
{
|
|
42
|
+
op: 'page',
|
|
43
|
+
noun: 'page',
|
|
44
|
+
where: 'Pages',
|
|
45
|
+
intents: [
|
|
46
|
+
'new pages: home, about, services, contact, pricing, a landing page'
|
|
47
|
+
],
|
|
48
|
+
argsSchema: NO_ARGS,
|
|
49
|
+
maxPerPlan: 8,
|
|
50
|
+
freeAllowed: true,
|
|
51
|
+
runnerKind: 'page',
|
|
52
|
+
estimateCredits: passes(6),
|
|
53
|
+
degrade: 'omit'
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
op: 'layout',
|
|
57
|
+
noun: 'layout',
|
|
58
|
+
where: 'Layouts',
|
|
59
|
+
intents: [
|
|
60
|
+
'a header and footer shared by pages'
|
|
61
|
+
],
|
|
62
|
+
argsSchema: NO_ARGS,
|
|
63
|
+
maxPerPlan: 1,
|
|
64
|
+
freeAllowed: true,
|
|
65
|
+
runnerKind: 'layout',
|
|
66
|
+
estimateCredits: passes(1),
|
|
67
|
+
degrade: 'fallback'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
op: 'form',
|
|
71
|
+
noun: 'form',
|
|
72
|
+
where: 'Forms',
|
|
73
|
+
intents: [
|
|
74
|
+
'a contact form, a quote request, a sign-up form'
|
|
75
|
+
],
|
|
76
|
+
argsSchema: NO_ARGS,
|
|
77
|
+
maxPerPlan: 3,
|
|
78
|
+
freeAllowed: true,
|
|
79
|
+
runnerKind: 'form',
|
|
80
|
+
estimateCredits: passes(1),
|
|
81
|
+
degrade: 'omit'
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
op: 'component',
|
|
85
|
+
noun: 'component',
|
|
86
|
+
where: 'Components',
|
|
87
|
+
intents: [
|
|
88
|
+
'a reusable card or block placed on several pages'
|
|
89
|
+
],
|
|
90
|
+
argsSchema: NO_ARGS,
|
|
91
|
+
maxPerPlan: 4,
|
|
92
|
+
freeAllowed: false,
|
|
93
|
+
runnerKind: 'component',
|
|
94
|
+
estimateCredits: passes(1),
|
|
95
|
+
degrade: 'omit'
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
op: 'email',
|
|
99
|
+
noun: 'email design',
|
|
100
|
+
where: 'Emails → Templates',
|
|
101
|
+
intents: [
|
|
102
|
+
'an email design: a welcome, a reply, an announcement'
|
|
103
|
+
],
|
|
104
|
+
argsSchema: NO_ARGS,
|
|
105
|
+
maxPerPlan: 2,
|
|
106
|
+
freeAllowed: false,
|
|
107
|
+
runnerKind: 'email',
|
|
108
|
+
estimateCredits: passes(1),
|
|
109
|
+
degrade: 'omit'
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
op: 'template',
|
|
113
|
+
noun: 'page template',
|
|
114
|
+
where: 'Templates',
|
|
115
|
+
intents: [
|
|
116
|
+
'a page template for blog posts, products or authors'
|
|
117
|
+
],
|
|
118
|
+
argsSchema: {
|
|
119
|
+
type: 'object',
|
|
120
|
+
properties: {
|
|
121
|
+
subject: {
|
|
122
|
+
type: 'string',
|
|
123
|
+
description: 'What each page shows: entry (a collection entry), product or author.',
|
|
124
|
+
enum: AI_TEMPLATE_SUBJECTS
|
|
125
|
+
},
|
|
126
|
+
collectionId: {
|
|
127
|
+
type: 'string',
|
|
128
|
+
description: 'For entry: the collection id from the site inventory.',
|
|
129
|
+
maxLength: 100
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
required: [
|
|
133
|
+
'subject'
|
|
134
|
+
],
|
|
135
|
+
additionalProperties: false
|
|
136
|
+
},
|
|
137
|
+
maxPerPlan: 2,
|
|
138
|
+
freeAllowed: false,
|
|
139
|
+
runnerKind: 'template',
|
|
140
|
+
estimateCredits: passes(6),
|
|
141
|
+
degrade: 'omit'
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
op: 'campaign',
|
|
145
|
+
noun: 'email campaign',
|
|
146
|
+
where: 'Marketing → Campaigns',
|
|
147
|
+
intents: [
|
|
148
|
+
'an email campaign drafted and left unsent'
|
|
149
|
+
],
|
|
150
|
+
argsSchema: {
|
|
151
|
+
type: 'object',
|
|
152
|
+
properties: {
|
|
153
|
+
name: {
|
|
154
|
+
type: 'string',
|
|
155
|
+
description: 'What the campaign is called.',
|
|
156
|
+
maxLength: 120
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
additionalProperties: false
|
|
160
|
+
},
|
|
161
|
+
maxPerPlan: 2,
|
|
162
|
+
freeAllowed: false,
|
|
163
|
+
runnerKind: 'campaign',
|
|
164
|
+
estimateCredits: passes(2),
|
|
165
|
+
degrade: 'omit'
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
op: 'workflow',
|
|
169
|
+
noun: 'automation',
|
|
170
|
+
where: 'Automations',
|
|
171
|
+
intents: [
|
|
172
|
+
'an automation: when a form is sent, do something'
|
|
173
|
+
],
|
|
174
|
+
argsSchema: NO_ARGS,
|
|
175
|
+
maxPerPlan: 2,
|
|
176
|
+
freeAllowed: false,
|
|
177
|
+
runnerKind: 'workflow',
|
|
178
|
+
estimateCredits: passes(1),
|
|
179
|
+
degrade: 'omit'
|
|
180
|
+
}
|
|
181
|
+
];
|
|
182
|
+
/** Registers this plugin's build operations; the console surface calls it. */ export function registerAiBuildCapabilities() {
|
|
183
|
+
for (const capability of AI_OWNED_CAPABILITIES){
|
|
184
|
+
registerPluginAiCapability(capability, {
|
|
185
|
+
pluginId: AI_PLUGIN_ID
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* The operations a build may plan on this site, by op (AGL-3616): every
|
|
191
|
+
* registered capability whose owner runs here — switched on for the
|
|
192
|
+
* workspace and the site, past its release flag — whose entitlement the plan
|
|
193
|
+
* holds, which the Free taste may use where the workspace is on it, and,
|
|
194
|
+
* for an AI-run operation, whose runner this process loaded. One release-flag
|
|
195
|
+
* read for every owner at once. What a member may do and what an allowance
|
|
196
|
+
* has left are the owner's to say when the item runs (`refusal`).
|
|
197
|
+
*/ export async function aiBuildOps(context, deps = {}) {
|
|
198
|
+
var _deps_capabilities, _deps_hasRunner, _deps_releasedPlugins;
|
|
199
|
+
const all = ((_deps_capabilities = deps.capabilities) != null ? _deps_capabilities : pluginAiCapabilities)();
|
|
200
|
+
const hasRunner = (_deps_hasRunner = deps.hasRunner) != null ? _deps_hasRunner : (kind)=>aiJobStepRunnerFor(kind) !== null;
|
|
201
|
+
const owners = [
|
|
202
|
+
...new Set(all.map((one)=>one.pluginId).filter((id)=>id !== AI_PLUGIN_ID))
|
|
203
|
+
];
|
|
204
|
+
const released = new Set(owners.length ? await ((_deps_releasedPlugins = deps.releasedPlugins) != null ? _deps_releasedPlugins : (ids, orgId)=>filterEnabledPluginsByReleaseFlags(ids, {
|
|
205
|
+
orgId,
|
|
206
|
+
authorization: null
|
|
207
|
+
}))(owners, context.orgId) : []);
|
|
208
|
+
const ops = new Map();
|
|
209
|
+
for (const { pluginId, capability } of all){
|
|
210
|
+
if (pluginId !== AI_PLUGIN_ID) {
|
|
211
|
+
if (!released.has(pluginId)) continue;
|
|
212
|
+
if (!isHostPluginEnabled(context.org, context.host, pluginId)) continue;
|
|
213
|
+
}
|
|
214
|
+
if (capability.feature && !checkEntitlement(context.org, capability.feature)) continue;
|
|
215
|
+
if (context.freeTaste && !capability.freeAllowed) continue;
|
|
216
|
+
if (capability.runnerKind && !hasRunner(capability.runnerKind)) continue;
|
|
217
|
+
ops.set(capability.op, capability);
|
|
218
|
+
}
|
|
219
|
+
return ops;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* The lines the plan step's request lists the item operations in: each op
|
|
223
|
+
* with what it makes, its arguments and their meaning. Per site, so they ride
|
|
224
|
+
* the job's own turn and never a cached system block.
|
|
225
|
+
*/ export function aiBuildOpLines(ops, structural) {
|
|
226
|
+
const items = [
|
|
227
|
+
...ops.values()
|
|
228
|
+
].filter((one)=>!structural.includes(one.op));
|
|
229
|
+
if (!items.length) return [
|
|
230
|
+
'Operations for items: none on this site. Leave items empty.'
|
|
231
|
+
];
|
|
232
|
+
const lines = [
|
|
233
|
+
'Operations for items (anything else goes in create or screens):'
|
|
234
|
+
];
|
|
235
|
+
for (const one of items){
|
|
236
|
+
var _one_pageBlocks;
|
|
237
|
+
const args = Object.entries(one.argsSchema.properties).map(([name, property])=>{
|
|
238
|
+
var _one_argsSchema_required;
|
|
239
|
+
const required = ((_one_argsSchema_required = one.argsSchema.required) == null ? void 0 : _one_argsSchema_required.includes(name)) ? '' : ' (optional)';
|
|
240
|
+
const choices = property.enum ? ` one of ${property.enum.join('|')}` : '';
|
|
241
|
+
return `${name}: ${property.type}${choices}${required} — ${property.description}`;
|
|
242
|
+
});
|
|
243
|
+
lines.push(`- ${one.op}: ${aiNounWithArticle(one.noun)}, at most ${one.maxPerPlan}; for ${one.intents.join('; ')}.${((_one_pageBlocks = one.pageBlocks) == null ? void 0 : _one_pageBlocks.length) ? ` A page section that uses it places the ${one.pageBlocks.join(' or ')} block: put new:<name> in that section's uses.` : ''} Args: ${args.length ? `{ ${args.join('; ')} }` : '{}'}.`);
|
|
244
|
+
}
|
|
245
|
+
return lines;
|
|
246
|
+
}
|
|
247
|
+
function aiNounWithArticle(noun) {
|
|
248
|
+
return `${/^[aeiou]/i.test(noun) ? 'an' : 'a'} ${noun}`;
|
|
249
|
+
}
|
|
250
|
+
/** What a build can make here, in the owner's words, for the chat's instructions. */ export function aiBuildIntents(ops) {
|
|
251
|
+
return [
|
|
252
|
+
...ops.values()
|
|
253
|
+
].flatMap((one)=>one.intents);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
//# sourceMappingURL=ai-build-capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-build-capabilities.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 { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { isHostPluginEnabled } from '@aglyn/aglyn/plugin-manager/enabled-plugins'\nimport {\n pluginAiCapabilities,\n registerPluginAiCapability,\n type PluginAiCapability,\n type ResolvedPluginAiCapability,\n} from '@aglyn/aglyn/plugin-manager/plugin-ai-capabilities'\nimport { filterEnabledPluginsByReleaseFlags } from '@aglyn/tenant-data-admin/server/release-flags'\nimport { AI_TEMPLATE_SUBJECTS } from '../model/ai-template-subjects'\nimport { AI_SITE_PASS_CREDITS } from '../model/ai-site-job'\nimport type { AiBuildOps } from '../model/ai-build-job'\nimport { aiJobStepRunnerFor } from './ai-jobs'\n\n/**\n * What THIS plugin lets a build make (AGL-3616), registered on the core's\n * capability contract exactly as any other plugin registers its own, so the\n * planner, the gates and the estimate read one list and name no plugin.\n *\n * Every one is built by a runner this plugin already has (`runnerKind`):\n * pages, layouts, forms, components and email designs are the plan's\n * creations and screens; templates, campaigns and automations are items with\n * arguments. A campaign and an automation are still WRITTEN by the marketing\n * and workflows plugins, through the writers their runners already call.\n */\n\n/** This plugin's id, as its registrations name their owner. */\nconst AI_PLUGIN_ID = 'ai'\n\nconst NO_ARGS = { type: 'object', properties: {}, additionalProperties: false } as const\n\nconst passes = (count: number) => () => count * AI_SITE_PASS_CREDITS\n\n/** The operations this plugin owns, as it registers them. */\nexport const AI_OWNED_CAPABILITIES: readonly PluginAiCapability[] = [\n {\n op: 'page',\n noun: 'page',\n where: 'Pages',\n intents: ['new pages: home, about, services, contact, pricing, a landing page'],\n argsSchema: NO_ARGS,\n maxPerPlan: 8,\n freeAllowed: true,\n runnerKind: 'page',\n estimateCredits: passes(6),\n degrade: 'omit',\n },\n {\n op: 'layout',\n noun: 'layout',\n where: 'Layouts',\n intents: ['a header and footer shared by pages'],\n argsSchema: NO_ARGS,\n maxPerPlan: 1,\n freeAllowed: true,\n runnerKind: 'layout',\n estimateCredits: passes(1),\n degrade: 'fallback',\n },\n {\n op: 'form',\n noun: 'form',\n where: 'Forms',\n intents: ['a contact form, a quote request, a sign-up form'],\n argsSchema: NO_ARGS,\n maxPerPlan: 3,\n freeAllowed: true,\n runnerKind: 'form',\n estimateCredits: passes(1),\n degrade: 'omit',\n },\n {\n op: 'component',\n noun: 'component',\n where: 'Components',\n intents: ['a reusable card or block placed on several pages'],\n argsSchema: NO_ARGS,\n maxPerPlan: 4,\n freeAllowed: false,\n runnerKind: 'component',\n estimateCredits: passes(1),\n degrade: 'omit',\n },\n {\n op: 'email',\n noun: 'email design',\n where: 'Emails → Templates',\n intents: ['an email design: a welcome, a reply, an announcement'],\n argsSchema: NO_ARGS,\n maxPerPlan: 2,\n freeAllowed: false,\n runnerKind: 'email',\n estimateCredits: passes(1),\n degrade: 'omit',\n },\n {\n op: 'template',\n noun: 'page template',\n where: 'Templates',\n intents: ['a page template for blog posts, products or authors'],\n argsSchema: {\n type: 'object',\n properties: {\n subject: {\n type: 'string',\n description: 'What each page shows: entry (a collection entry), product or author.',\n enum: AI_TEMPLATE_SUBJECTS,\n },\n collectionId: {\n type: 'string',\n description: 'For entry: the collection id from the site inventory.',\n maxLength: 100,\n },\n },\n required: ['subject'],\n additionalProperties: false,\n },\n maxPerPlan: 2,\n freeAllowed: false,\n runnerKind: 'template',\n estimateCredits: passes(6),\n degrade: 'omit',\n },\n {\n op: 'campaign',\n noun: 'email campaign',\n where: 'Marketing → Campaigns',\n intents: ['an email campaign drafted and left unsent'],\n argsSchema: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'What the campaign is called.', maxLength: 120 },\n },\n additionalProperties: false,\n },\n maxPerPlan: 2,\n freeAllowed: false,\n runnerKind: 'campaign',\n estimateCredits: passes(2),\n degrade: 'omit',\n },\n {\n op: 'workflow',\n noun: 'automation',\n where: 'Automations',\n intents: ['an automation: when a form is sent, do something'],\n argsSchema: NO_ARGS,\n maxPerPlan: 2,\n freeAllowed: false,\n runnerKind: 'workflow',\n estimateCredits: passes(1),\n degrade: 'omit',\n },\n]\n\n/** Registers this plugin's build operations; the console surface calls it. */\nexport function registerAiBuildCapabilities(): void {\n for (const capability of AI_OWNED_CAPABILITIES) {\n registerPluginAiCapability(capability, { pluginId: AI_PLUGIN_ID })\n }\n}\n\n/** What decides which operations a build may use here. */\nexport interface AiBuildOpsContext {\n orgId: string\n hostId: string | null\n org: Partial<AglynOrgBilling> | null\n /** The site document, for the plugins switched on and off on it. */\n host: Readonly<Record<string, unknown>> | null\n freeTaste: boolean\n}\n\nexport interface AiBuildOpsDeps {\n /** The registered capabilities; the core registry's otherwise. */\n capabilities?: () => ResolvedPluginAiCapability[]\n /** The release-flag read; the platform's otherwise. */\n releasedPlugins?: (pluginIds: string[], orgId: string) => Promise<string[]>\n /** Whether a runner is registered for a kind; the machine's otherwise. */\n hasRunner?: (kind: string) => boolean\n}\n\n/**\n * The operations a build may plan on this site, by op (AGL-3616): every\n * registered capability whose owner runs here — switched on for the\n * workspace and the site, past its release flag — whose entitlement the plan\n * holds, which the Free taste may use where the workspace is on it, and,\n * for an AI-run operation, whose runner this process loaded. One release-flag\n * read for every owner at once. What a member may do and what an allowance\n * has left are the owner's to say when the item runs (`refusal`).\n */\nexport async function aiBuildOps(context: AiBuildOpsContext, deps: AiBuildOpsDeps = {}): Promise<AiBuildOps> {\n const all = (deps.capabilities ?? pluginAiCapabilities)()\n const hasRunner = deps.hasRunner ?? ((kind: string) => aiJobStepRunnerFor(kind as never) !== null)\n const owners = [...new Set(all.map((one) => one.pluginId).filter((id) => id !== AI_PLUGIN_ID))]\n const released = new Set(\n owners.length\n ? await (deps.releasedPlugins ??\n ((ids, orgId) => filterEnabledPluginsByReleaseFlags(ids, { orgId, authorization: null })))(owners, context.orgId)\n : [],\n )\n const ops = new Map<string, PluginAiCapability>()\n for (const { pluginId, capability } of all) {\n if (pluginId !== AI_PLUGIN_ID) {\n if (!released.has(pluginId)) continue\n if (!isHostPluginEnabled(context.org as { enabledPlugins?: string[] } | null, context.host, pluginId)) continue\n }\n if (capability.feature && !checkEntitlement(context.org, capability.feature)) continue\n if (context.freeTaste && !capability.freeAllowed) continue\n if (capability.runnerKind && !hasRunner(capability.runnerKind)) continue\n ops.set(capability.op, capability)\n }\n return ops\n}\n\n/**\n * The lines the plan step's request lists the item operations in: each op\n * with what it makes, its arguments and their meaning. Per site, so they ride\n * the job's own turn and never a cached system block.\n */\nexport function aiBuildOpLines(ops: AiBuildOps, structural: readonly string[]): string[] {\n const items = [...ops.values()].filter((one) => !structural.includes(one.op))\n if (!items.length) return ['Operations for items: none on this site. Leave items empty.']\n const lines = ['Operations for items (anything else goes in create or screens):']\n for (const one of items) {\n const args = Object.entries(one.argsSchema.properties).map(([name, property]) => {\n const required = one.argsSchema.required?.includes(name) ? '' : ' (optional)'\n const choices = property.enum ? ` one of ${property.enum.join('|')}` : ''\n return `${name}: ${property.type}${choices}${required} — ${property.description}`\n })\n lines.push(\n `- ${one.op}: ${aiNounWithArticle(one.noun)}, at most ${one.maxPerPlan}; for ${one.intents.join('; ')}.${\n one.pageBlocks?.length ? ` A page section that uses it places the ${one.pageBlocks.join(' or ')} block: put new:<name> in that section's uses.` : ''\n } Args: ${args.length ? `{ ${args.join('; ')} }` : '{}'}.`,\n )\n }\n return lines\n}\n\nfunction aiNounWithArticle(noun: string): string {\n return `${/^[aeiou]/i.test(noun) ? 'an' : 'a'} ${noun}`\n}\n\n/** What a build can make here, in the owner's words, for the chat's instructions. */\nexport function aiBuildIntents(ops: AiBuildOps): string[] {\n return [...ops.values()].flatMap((one) => one.intents)\n}\n"],"names":["checkEntitlement","isHostPluginEnabled","pluginAiCapabilities","registerPluginAiCapability","filterEnabledPluginsByReleaseFlags","AI_TEMPLATE_SUBJECTS","AI_SITE_PASS_CREDITS","aiJobStepRunnerFor","AI_PLUGIN_ID","NO_ARGS","type","properties","additionalProperties","passes","count","AI_OWNED_CAPABILITIES","op","noun","where","intents","argsSchema","maxPerPlan","freeAllowed","runnerKind","estimateCredits","degrade","subject","description","enum","collectionId","maxLength","required","name","registerAiBuildCapabilities","capability","pluginId","aiBuildOps","context","deps","all","capabilities","hasRunner","kind","owners","Set","map","one","filter","id","released","length","releasedPlugins","ids","orgId","authorization","ops","Map","has","org","host","feature","freeTaste","set","aiBuildOpLines","structural","items","values","includes","lines","args","Object","entries","property","choices","join","push","aiNounWithArticle","pageBlocks","test","aiBuildIntents","flatMap"],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
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 AiBuildUnit } from '../model/ai-build-job';
|
|
18
|
+
import type { AiJobItemLedger, AiJobOutput } from '../model/ai-jobs.types';
|
|
19
|
+
import type { AiSiteBuiltRef } from './ai-job-site-step';
|
|
20
|
+
import type { AiJobItemOutcome, AiJobStepOutcome } from './ai-job-text-step';
|
|
21
|
+
/**
|
|
22
|
+
* One delegated unit's pass as an ITEM of a job settled item by item
|
|
23
|
+
* (AGL-3616) — a `build`, and a site scaffold, which is the build's preset.
|
|
24
|
+
* The runner's spend is the pass's; what the runner came to is the item's
|
|
25
|
+
* row: still running, built (or built without something that failed),
|
|
26
|
+
* failed on our side, or failed on the model's or the workspace's.
|
|
27
|
+
*
|
|
28
|
+
* Shared by both steps, and importing neither, so neither imports the other.
|
|
29
|
+
*/
|
|
30
|
+
/** What a unit says when it finished without reporting anything. */
|
|
31
|
+
export declare const AI_BUILD_UNIT_EMPTY_COPY = "It could not be built this time.";
|
|
32
|
+
/** What a unit's model declining the request says. */
|
|
33
|
+
export declare const AI_BUILD_UNIT_REFUSED_COPY = "The AI declined to build this one.";
|
|
34
|
+
/** A runner's outcome narrowed to its spend: no plan, no review, nothing the delegating job keeps. */
|
|
35
|
+
export declare function aiUnitSpend(outcome: AiJobStepOutcome): AiJobStepOutcome;
|
|
36
|
+
/**
|
|
37
|
+
* What a runner's outcome comes to for its item; `null` when the unit
|
|
38
|
+
* reported what it built and the caller decides whether that counts.
|
|
39
|
+
*/
|
|
40
|
+
export declare function aiUnitFailure(slot: string, outcome: AiJobStepOutcome): AiJobItemOutcome | null;
|
|
41
|
+
/**
|
|
42
|
+
* Whether an error a unit's runner threw is the machine's to retry — a
|
|
43
|
+
* provider that will answer later, or a budget that ended — rather than this
|
|
44
|
+
* item's failure.
|
|
45
|
+
*/
|
|
46
|
+
export declare function aiUnitErrorRetryable(error: unknown): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* What the build has created that a later unit can name: each delivered
|
|
49
|
+
* creation's record, by its plan name. A creation's first output is the
|
|
50
|
+
* record it wrote.
|
|
51
|
+
*/
|
|
52
|
+
export declare function aiBuildBuiltRefs(units: readonly AiBuildUnit[], ledger: readonly AiJobItemLedger[], outputs: readonly Pick<AiJobOutput, 'id' | 'label'>[]): Map<string, AiSiteBuiltRef>;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { aiBuildItemDelivered } from "../model/ai-build-job.js";
|
|
18
|
+
import { AiUpstreamError } from "../runtime/ai-runtime.js";
|
|
19
|
+
/**
|
|
20
|
+
* One delegated unit's pass as an ITEM of a job settled item by item
|
|
21
|
+
* (AGL-3616) — a `build`, and a site scaffold, which is the build's preset.
|
|
22
|
+
* The runner's spend is the pass's; what the runner came to is the item's
|
|
23
|
+
* row: still running, built (or built without something that failed),
|
|
24
|
+
* failed on our side, or failed on the model's or the workspace's.
|
|
25
|
+
*
|
|
26
|
+
* Shared by both steps, and importing neither, so neither imports the other.
|
|
27
|
+
*/ /** What a unit says when it finished without reporting anything. */ export const AI_BUILD_UNIT_EMPTY_COPY = 'It could not be built this time.';
|
|
28
|
+
/** What a unit's model declining the request says. */ export const AI_BUILD_UNIT_REFUSED_COPY = 'The AI declined to build this one.';
|
|
29
|
+
/** A runner's outcome narrowed to its spend: no plan, no review, nothing the delegating job keeps. */ export function aiUnitSpend(outcome) {
|
|
30
|
+
return _extends({
|
|
31
|
+
outputs: outcome.outputs,
|
|
32
|
+
usage: outcome.usage,
|
|
33
|
+
estCostUsd: outcome.estCostUsd,
|
|
34
|
+
model: outcome.model,
|
|
35
|
+
stopReason: outcome.stopReason
|
|
36
|
+
}, outcome.effort ? {
|
|
37
|
+
effort: outcome.effort
|
|
38
|
+
} : {});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* What a runner's outcome comes to for its item; `null` when the unit
|
|
42
|
+
* reported what it built and the caller decides whether that counts.
|
|
43
|
+
*/ export function aiUnitFailure(slot, outcome) {
|
|
44
|
+
const outputs = outcome.outputs.map((output)=>output.id);
|
|
45
|
+
if (outcome.refused) {
|
|
46
|
+
return {
|
|
47
|
+
slot,
|
|
48
|
+
status: 'failed',
|
|
49
|
+
failure: {
|
|
50
|
+
ours: false,
|
|
51
|
+
reason: 'refused',
|
|
52
|
+
message: AI_BUILD_UNIT_REFUSED_COPY
|
|
53
|
+
},
|
|
54
|
+
outputs
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (outcome.review) {
|
|
58
|
+
// A building rule still broken after its re-ask is ours (AGL-3596); a
|
|
59
|
+
// site at an allowance is the workspace's to change.
|
|
60
|
+
const ours = outcome.review.reason === 'doctrine';
|
|
61
|
+
return {
|
|
62
|
+
slot,
|
|
63
|
+
status: 'failed',
|
|
64
|
+
failure: _extends({
|
|
65
|
+
ours,
|
|
66
|
+
reason: ours ? 'doctrine-refused' : 'review',
|
|
67
|
+
message: outcome.review.message || AI_BUILD_UNIT_EMPTY_COPY
|
|
68
|
+
}, outcome.review.detail ? {
|
|
69
|
+
detail: outcome.review.detail
|
|
70
|
+
} : {}),
|
|
71
|
+
outputs
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (outcome.failure) {
|
|
75
|
+
return {
|
|
76
|
+
slot,
|
|
77
|
+
status: 'failed',
|
|
78
|
+
failure: {
|
|
79
|
+
ours: true,
|
|
80
|
+
reason: 'step-failure',
|
|
81
|
+
message: outcome.failure
|
|
82
|
+
},
|
|
83
|
+
outputs
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (outcome.continue) return {
|
|
87
|
+
slot,
|
|
88
|
+
status: 'running',
|
|
89
|
+
outputs
|
|
90
|
+
};
|
|
91
|
+
if (!outcome.outputs.length) {
|
|
92
|
+
return {
|
|
93
|
+
slot,
|
|
94
|
+
status: 'failed',
|
|
95
|
+
failure: {
|
|
96
|
+
ours: true,
|
|
97
|
+
reason: 'step-failure',
|
|
98
|
+
message: AI_BUILD_UNIT_EMPTY_COPY
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Whether an error a unit's runner threw is the machine's to retry — a
|
|
106
|
+
* provider that will answer later, or a budget that ended — rather than this
|
|
107
|
+
* item's failure.
|
|
108
|
+
*/ export function aiUnitErrorRetryable(error) {
|
|
109
|
+
if (error instanceof AiUpstreamError && error.retryable) return true;
|
|
110
|
+
const name = error == null ? void 0 : error.name;
|
|
111
|
+
return name === 'AbortError' || name === 'TimeoutError';
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* What the build has created that a later unit can name: each delivered
|
|
115
|
+
* creation's record, by its plan name. A creation's first output is the
|
|
116
|
+
* record it wrote.
|
|
117
|
+
*/ export function aiBuildBuiltRefs(units, ledger, outputs) {
|
|
118
|
+
const rows = new Map(ledger.map((row)=>[
|
|
119
|
+
row.slot,
|
|
120
|
+
row
|
|
121
|
+
]));
|
|
122
|
+
const built = new Map();
|
|
123
|
+
for (const unit of units){
|
|
124
|
+
var _outputs_find;
|
|
125
|
+
const creation = unit.creation;
|
|
126
|
+
const row = rows.get(unit.slot);
|
|
127
|
+
if (!creation || !row || !aiBuildItemDelivered(row) || !row.outputs.length) continue;
|
|
128
|
+
if (creation.kind !== 'layout' && creation.kind !== 'form' && creation.kind !== 'component') continue;
|
|
129
|
+
const id = row.outputs[0];
|
|
130
|
+
const label = ((_outputs_find = outputs.find((output)=>output.id === id)) == null ? void 0 : _outputs_find.label) || creation.name;
|
|
131
|
+
built.set(creation.name.toLowerCase(), {
|
|
132
|
+
id,
|
|
133
|
+
label,
|
|
134
|
+
kind: creation.kind
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return built;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
//# sourceMappingURL=ai-build-unit-outcome.js.map
|