@aglyn/plugins-ai 1.0.0-beta.218 → 1.0.0-beta.220
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 +2 -2
- package/package.json +18 -18
- package/src/index.d.ts +1 -0
- package/src/index.js.map +1 -1
- package/src/lib/activity/ai-activity-actions.js +8 -1
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/billing/ai-month-meter.d.ts +51 -0
- package/src/lib/billing/ai-month-meter.js +89 -0
- package/src/lib/billing/ai-month-meter.js.map +1 -0
- package/src/lib/billing/ai-overage-close.d.ts +11 -33
- package/src/lib/billing/ai-overage-close.js +4 -3
- package/src/lib/billing/ai-overage-close.js.map +1 -1
- package/src/lib/billing/ai-overage-cutover.d.ts +28 -12
- package/src/lib/billing/ai-overage-cutover.js +1 -28
- package/src/lib/billing/ai-overage-cutover.js.map +1 -1
- package/src/lib/billing/ai-overage-ledger.js +1 -1
- package/src/lib/billing/ai-overage-ledger.js.map +1 -1
- package/src/lib/billing/ai-overage-standing.js +1 -1
- package/src/lib/billing/ai-overage-standing.js.map +1 -1
- package/src/lib/components/ai-allotment-editor.component.js +1 -1
- package/src/lib/components/ai-allotment-editor.component.js.map +1 -1
- package/src/lib/components/ai-assist-provider.component.js +5 -4
- package/src/lib/components/ai-assist-provider.component.js.map +1 -1
- package/src/lib/components/ai-automation-zones.d.ts +63 -0
- package/src/lib/components/ai-automation-zones.js +31 -0
- package/src/lib/components/ai-automation-zones.js.map +1 -0
- package/src/lib/components/ai-brief-dialog.component.js +3 -3
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-describe-automation.component.d.ts +1 -1
- package/src/lib/components/ai-describe-automation.component.js.map +1 -1
- package/src/lib/components/ai-describe-button.component.d.ts +6 -3
- package/src/lib/components/ai-describe-button.component.js +4 -1
- package/src/lib/components/ai-describe-button.component.js.map +1 -1
- package/src/lib/components/ai-experiment-cards.component.js +1 -1
- package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
- package/src/lib/components/ai-explain-automation.component.d.ts +1 -1
- package/src/lib/components/ai-explain-automation.component.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.d.ts +8 -0
- package/src/lib/components/ai-job-plan.component.js +21 -4
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-permissions-on-host.component.d.ts +12 -2
- package/src/lib/components/ai-permissions-on-host.component.js +12 -3
- package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
- package/src/lib/components/ai-product-copy-card.component.d.ts +1 -1
- package/src/lib/components/ai-product-copy-card.component.js.map +1 -1
- package/src/lib/components/ai-product-import-option.component.d.ts +1 -1
- package/src/lib/components/ai-product-import-option.component.js.map +1 -1
- package/src/lib/components/ai-product-zones.d.ts +124 -0
- package/src/lib/components/ai-product-zones.js +32 -0
- package/src/lib/components/ai-product-zones.js.map +1 -0
- package/src/lib/components/ai-products-hub-card.component.d.ts +1 -1
- package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
- package/src/lib/components/ai-products-proposals.component.d.ts +1 -1
- package/src/lib/components/ai-products-proposals.component.js.map +1 -1
- package/src/lib/components/ai-seo-audit-card.component.js +35 -92
- package/src/lib/components/ai-seo-audit-card.component.js.map +1 -1
- package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +21 -15
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +2 -2
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/assist-signals-page.component.js +2 -2
- package/src/lib/components/assist-signals-page.component.js.map +1 -1
- package/src/lib/components/billing-ai-allotments.component.js +1 -1
- package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
- package/src/lib/constants.d.ts +5 -0
- package/src/lib/constants.js +4 -0
- package/src/lib/constants.js.map +1 -1
- package/src/lib/declarations.js +2 -0
- package/src/lib/declarations.js.map +1 -1
- package/src/lib/declarations.server.js +24 -10
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/insights/ai-dataset-catalog.d.ts +49 -0
- package/src/lib/insights/ai-dataset-catalog.js +57 -0
- package/src/lib/insights/ai-dataset-catalog.js.map +1 -0
- package/src/lib/insights/ai-figure-readers.d.ts +6 -39
- package/src/lib/insights/ai-figure-readers.js +2 -391
- package/src/lib/insights/ai-figure-readers.js.map +1 -1
- package/src/lib/insights/ai-insight-digest.d.ts +10 -1
- package/src/lib/insights/ai-insight-digest.js +16 -4
- package/src/lib/insights/ai-insight-digest.js.map +1 -1
- package/src/lib/insights/ai-insight-readers.d.ts +1 -1
- package/src/lib/insights/ai-insight-readers.js.map +1 -1
- package/src/lib/jobs/ai-email-bindings.d.ts +28 -21
- package/src/lib/jobs/ai-email-bindings.js +75 -42
- package/src/lib/jobs/ai-email-bindings.js.map +1 -1
- package/src/lib/jobs/ai-job-campaign-step.d.ts +0 -2
- package/src/lib/jobs/ai-job-campaign-step.js +5 -10
- package/src/lib/jobs/ai-job-campaign-step.js.map +1 -1
- package/src/lib/jobs/ai-job-component-step.d.ts +6 -1
- package/src/lib/jobs/ai-job-component-step.js +32 -9
- package/src/lib/jobs/ai-job-component-step.js.map +1 -1
- package/src/lib/jobs/ai-job-draft-ids.js +11 -0
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.js +1 -1
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-email-step.d.ts +5 -3
- package/src/lib/jobs/ai-job-email-step.js +11 -4
- package/src/lib/jobs/ai-job-email-step.js.map +1 -1
- package/src/lib/jobs/ai-job-experiment-step.d.ts +1 -1
- package/src/lib/jobs/ai-job-experiment-step.js +1 -1
- package/src/lib/jobs/ai-job-experiment-step.js.map +1 -1
- package/src/lib/jobs/ai-job-form-step.d.ts +17 -1
- package/src/lib/jobs/ai-job-form-step.js +60 -8
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-generation.d.ts +22 -1
- package/src/lib/jobs/ai-job-generation.js +63 -3
- package/src/lib/jobs/ai-job-generation.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +1 -1
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.d.ts +15 -4
- package/src/lib/jobs/ai-job-layout-step.js +46 -13
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.d.ts +4 -2
- package/src/lib/jobs/ai-job-page-sections.js +7 -4
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +7 -2
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-conformance.d.ts +5 -0
- package/src/lib/jobs/ai-job-plan-conformance.js +40 -1
- package/src/lib/jobs/ai-job-plan-conformance.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-creations.d.ts +80 -0
- package/src/lib/jobs/ai-job-plan-creations.js +199 -0
- package/src/lib/jobs/ai-job-plan-creations.js.map +1 -0
- package/src/lib/jobs/ai-job-plan-step.d.ts +11 -2
- package/src/lib/jobs/ai-job-plan-step.js +49 -23
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plugin-drafts.d.ts +14 -6
- package/src/lib/jobs/ai-job-plugin-drafts.js +12 -4
- package/src/lib/jobs/ai-job-plugin-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-products-step.d.ts +7 -0
- package/src/lib/jobs/ai-job-products-step.js +54 -32
- package/src/lib/jobs/ai-job-products-step.js.map +1 -1
- package/src/lib/jobs/ai-job-seo-step.d.ts +3 -24
- package/src/lib/jobs/ai-job-seo-step.js +35 -108
- package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +2 -0
- package/src/lib/jobs/ai-job-site-step.js +6 -6
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-template-step.d.ts +9 -6
- package/src/lib/jobs/ai-job-template-step.js +73 -15
- package/src/lib/jobs/ai-job-template-step.js.map +1 -1
- package/src/lib/jobs/ai-job-theme-step.d.ts +1 -1
- package/src/lib/jobs/ai-job-theme-step.js +1 -1
- package/src/lib/jobs/ai-job-theme-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.js +17 -9
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.js +1 -1
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-workflow-records.d.ts +20 -11
- package/src/lib/jobs/ai-workflow-records.js +80 -43
- package/src/lib/jobs/ai-workflow-records.js.map +1 -1
- package/src/lib/model/ai-allotments.d.ts +27 -6
- package/src/lib/model/ai-allotments.js +54 -14
- package/src/lib/model/ai-allotments.js.map +1 -1
- package/src/lib/model/ai-automation-outline.d.ts +21 -2
- package/src/lib/model/ai-automation-outline.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +57 -0
- package/src/lib/model/ai-build-plan.js +116 -7
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-job-creations.d.ts +55 -0
- package/src/lib/model/ai-job-creations.js +85 -0
- package/src/lib/model/ai-job-creations.js.map +1 -0
- package/src/lib/model/ai-page-job.d.ts +3 -2
- package/src/lib/model/ai-page-job.js +4 -3
- package/src/lib/model/ai-page-job.js.map +1 -1
- package/src/lib/model/ai-seo.d.ts +11 -51
- package/src/lib/model/ai-seo.js +0 -17
- package/src/lib/model/ai-seo.js.map +1 -1
- package/src/lib/model/ai-site-inventory.js +1 -1
- package/src/lib/model/ai-site-inventory.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +4 -2
- package/src/lib/model/ai-site-job.js +8 -4
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-template-subjects.d.ts +10 -0
- package/src/lib/model/ai-template-subjects.js +16 -2
- package/src/lib/model/ai-template-subjects.js.map +1 -1
- package/src/lib/model/ai-theme-proposal.d.ts +1 -1
- package/src/lib/model/ai-theme-proposal.js +1 -1
- package/src/lib/model/ai-theme-proposal.js.map +1 -1
- package/src/lib/model/ai-tokens.js +1 -1
- package/src/lib/model/ai-tokens.js.map +1 -1
- package/src/lib/model/ai-usage-by-user.js +1 -1
- package/src/lib/model/ai-usage-by-user.js.map +1 -1
- package/src/lib/model/ai-workflow-job.d.ts +1 -3
- package/src/lib/model/ai-workflow-job.js +1 -2
- package/src/lib/model/ai-workflow-job.js.map +1 -1
- package/src/lib/plan-entitlements.d.ts +149 -0
- package/src/lib/plan-entitlements.js +168 -0
- package/src/lib/plan-entitlements.js.map +1 -0
- package/src/lib/plugin.js +1 -1
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/catalog.js +1 -1
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/model-choice.js +1 -1
- package/src/lib/providers/model-choice.js.map +1 -1
- package/src/lib/runtime/ai-device-audit.d.ts +1 -1
- package/src/lib/runtime/ai-device-audit.js +1 -1
- package/src/lib/runtime/ai-device-audit.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +25 -4
- package/src/lib/runtime/ai-doctrine-validators.js +178 -21
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +5 -5
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +30 -15
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +9 -2
- package/src/lib/runtime/ai-eval.js +29 -2
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-gate.js +1 -1
- package/src/lib/runtime/ai-gate.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.js +3 -3
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +7 -1
- package/src/lib/runtime/ai-palette.generated.d.ts +1 -1
- package/src/lib/runtime/ai-palette.generated.js +16 -13
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js +7 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/runtime/seo-audit.d.ts +13 -63
- package/src/lib/runtime/seo-audit.js +22 -207
- package/src/lib/runtime/seo-audit.js.map +1 -1
- package/src/lib/runtime/site-inventory.js +29 -23
- package/src/lib/runtime/site-inventory.js.map +1 -1
- package/src/lib/server/ai-admin-org.js +1 -1
- package/src/lib/server/ai-admin-org.js.map +1 -1
- package/src/lib/server/ai-admin-orgs-spend.js +1 -1
- package/src/lib/server/ai-admin-orgs-spend.js.map +1 -1
- package/src/lib/server/ai-admin-signals.js +1 -1
- package/src/lib/server/ai-admin-signals.js.map +1 -1
- package/src/lib/server/ai-allotments.js +1 -1
- package/src/lib/server/ai-allotments.js.map +1 -1
- package/src/lib/server/ai-assist-prompts.js +3 -3
- package/src/lib/server/ai-assist-prompts.js.map +1 -1
- package/src/lib/server/ai-assist.js +2 -2
- package/src/lib/server/ai-assist.js.map +1 -1
- package/src/lib/server/assist-chat.d.ts +1 -1
- package/src/lib/server/assist-chat.js +13 -12
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-view-context.js +23 -23
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server/billing-credits.d.ts +5 -0
- package/src/lib/server/billing-credits.js +8 -2
- package/src/lib/server/billing-credits.js.map +1 -1
- package/src/lib/server/billing-overage.js +2 -2
- package/src/lib/server/billing-overage.js.map +1 -1
- package/src/lib/server.d.ts +1 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/testing/stand-in-automation-index.d.ts +20 -0
- package/src/lib/testing/stand-in-automation-index.js +102 -0
- package/src/lib/testing/stand-in-automation-index.js.map +1 -0
- package/src/lib/testing/stand-in-dataset-index.d.ts +20 -0
- package/src/lib/testing/stand-in-dataset-index.js +80 -0
- package/src/lib/testing/stand-in-dataset-index.js.map +1 -0
- package/src/lib/testing/stand-in-list-send-time.d.ts +4 -0
- package/src/lib/testing/stand-in-list-send-time.js +77 -0
- package/src/lib/testing/stand-in-list-send-time.js.map +1 -0
- package/src/lib/testing/stand-in-pipeline-index.d.ts +32 -0
- package/src/lib/testing/stand-in-pipeline-index.js +59 -0
- package/src/lib/testing/stand-in-pipeline-index.js.map +1 -0
- package/src/lib/testing/stand-in-product-index.d.ts +20 -0
- package/src/lib/testing/stand-in-product-index.js +105 -0
- package/src/lib/testing/stand-in-product-index.js.map +1 -0
- package/src/lib/tools/ai-component-tool.js +2 -2
- package/src/lib/tools/ai-component-tool.js.map +1 -1
- package/src/lib/tools/ai-duplicate-resource-tool.js +1 -1
- package/src/lib/tools/ai-duplicate-resource-tool.js.map +1 -1
- package/src/lib/tools/ai-seo-tool.d.ts +6 -18
- package/src/lib/tools/ai-seo-tool.js +7 -38
- package/src/lib/tools/ai-seo-tool.js.map +1 -1
- package/src/lib/tools/ai-workflow-tool.d.ts +1 -1
- package/src/lib/tools/ai-workflow-tool.js +1 -1
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
- package/src/lib/usage/ai-allotment-alerts.d.ts +10 -7
- package/src/lib/usage/ai-allotment-alerts.js +39 -15
- package/src/lib/usage/ai-allotment-alerts.js.map +1 -1
- package/src/lib/usage/ai-quota-checks.d.ts +37 -0
- package/src/lib/usage/ai-quota-checks.js +69 -0
- package/src/lib/usage/ai-quota-checks.js.map +1 -0
- package/src/lib/usage/ai-usage-alerts.js +22 -10
- package/src/lib/usage/ai-usage-alerts.js.map +1 -1
- package/src/lib/usage/ai-usage-by-user.js +1 -1
- package/src/lib/usage/ai-usage-by-user.js.map +1 -1
- package/src/lib/usage/assist-ceiling.d.ts +2 -2
- package/src/lib/usage/assist-ceiling.js +4 -4
- package/src/lib/usage/assist-ceiling.js.map +1 -1
- package/src/lib/usage/assist-credits.d.ts +494 -0
- package/src/lib/usage/assist-credits.js +524 -0
- package/src/lib/usage/assist-credits.js.map +1 -0
- package/src/lib/usage/assist-free-taste.d.ts +1 -1
- package/src/lib/usage/assist-free-taste.js +3 -2
- package/src/lib/usage/assist-free-taste.js.map +1 -1
- package/src/lib/usage/assist-signal-mining.js +1 -1
- package/src/lib/usage/assist-signal-mining.js.map +1 -1
- package/src/lib/usage/assist-spend-guards.d.ts +153 -0
- package/src/lib/usage/assist-spend-guards.js +182 -0
- package/src/lib/usage/assist-spend-guards.js.map +1 -0
- package/src/lib/usage/assist-usage.d.ts +1 -1
- package/src/lib/usage/assist-usage.js +1 -1
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/staff-org-ai.js +1 -1
- package/src/lib/usage/staff-org-ai.js.map +1 -1
- package/src/lib/usage-axes.d.ts +49 -0
- package/src/lib/usage-axes.js +108 -0
- package/src/lib/usage-axes.js.map +1 -0
- package/src/lib/runtime/seo-page-facts.d.ts +0 -61
- package/src/lib/runtime/seo-page-facts.js +0 -150
- package/src/lib/runtime/seo-page-facts.js.map +0 -1
|
@@ -0,0 +1,199 @@
|
|
|
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 { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
|
+
import { AI_BUILD_PLAN_CREATION_NOUNS } from "../model/ai-build-plan.js";
|
|
18
|
+
import { AI_JOB_CREATE_KINDS, AI_JOB_CREATE_NOUNS, aiJobOtherCreations } from "../model/ai-job-creations.js";
|
|
19
|
+
import { aiPageCreationRefusal } from "../model/ai-page-job.js";
|
|
20
|
+
import { aiPlanCapabilitiesForJob, aiPlanUncreatable } from "../model/ai-plan-capabilities.js";
|
|
21
|
+
import { aiModelForStep } from "../providers/routing.js";
|
|
22
|
+
import { readAiPlanCapabilities } from "./ai-job-drafts.js";
|
|
23
|
+
import { aiConfirmedPlan, aiUnspentOutcome } from "./ai-job-generation.js";
|
|
24
|
+
import { aiCreationUnit, aiRunJobUnit, aiSiteBuiltRefs, aiSitePendingUnits, aiSiteResolvedRef, aiSiteUnitJob } from "./ai-job-site-step.js";
|
|
25
|
+
import { aiJobStepRunMinimumMs, aiJobStepRunnerFor } from "./ai-jobs.js";
|
|
26
|
+
/**
|
|
27
|
+
* A template, layout or component job builds the rest of its plan before its
|
|
28
|
+
* own record (AGL-3143 §15), through the machinery a page job builds its
|
|
29
|
+
* creations with (AGL-3031): one creation a pass, handed to the step that
|
|
30
|
+
* builds that kind under a job derived from this one, where the job stands
|
|
31
|
+
* read from its outputs. Once every creation is built, the kind's own step
|
|
32
|
+
* runs on the plan with each `new:<name>` resolved to the record that was
|
|
33
|
+
* built and listed among what the plan reuses — so the template's reuse check
|
|
34
|
+
* holds it to placing the component its plan created, exactly as it holds it
|
|
35
|
+
* to one the site always had.
|
|
36
|
+
*
|
|
37
|
+
* What each kind builds is `AI_JOB_CREATE_KINDS`; a form or an email design
|
|
38
|
+
* places nothing another job builds, so its plan is only held to that list.
|
|
39
|
+
*/ /** A creation the plan names that this deployment has no step to build. */ export const AI_JOB_CREATION_UNAVAILABLE_COPY = 'This needs something that cannot be built here yet. Describe it again.';
|
|
40
|
+
/** A creation that finished without reporting what it built; the job's own record cannot place it. */ export const AI_JOB_CREATION_EMPTY_COPY = 'Part of this could not be built. Describe it again.';
|
|
41
|
+
/** The order a page job builds creations in: a form before the component that may place it. */ const UNIT_ORDER = [
|
|
42
|
+
'form',
|
|
43
|
+
'component'
|
|
44
|
+
];
|
|
45
|
+
/**
|
|
46
|
+
* The units the creations a job of `kind` builds before its own record are
|
|
47
|
+
* built as, in build order, each addressed by its place in the plan so a unit
|
|
48
|
+
* run again finds its own draft.
|
|
49
|
+
*/ export function aiJobCreationUnits(kind, plan) {
|
|
50
|
+
const others = aiJobOtherCreations(kind, plan);
|
|
51
|
+
return UNIT_ORDER.flatMap((order)=>others.flatMap(({ creation, index })=>{
|
|
52
|
+
if (creation.kind !== order) return [];
|
|
53
|
+
const unit = aiCreationUnit(creation, `c${index}`);
|
|
54
|
+
return unit ? [
|
|
55
|
+
unit
|
|
56
|
+
] : [];
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The job a unit is derived from. A unit places none of the components the
|
|
61
|
+
* plan reuses: those are the job's own record's to place, and a card told to
|
|
62
|
+
* place them would be refused for leaving them out.
|
|
63
|
+
*/ function forUnits(job, plan) {
|
|
64
|
+
return _extends({}, job, {
|
|
65
|
+
plan: _extends({}, plan, {
|
|
66
|
+
reuse: plan.reuse.filter((entry)=>entry.kind !== 'component')
|
|
67
|
+
})
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The job the kind's own step builds its record under once every creation is
|
|
72
|
+
* built: the plan's own creation alone, from a copy resolved to what was
|
|
73
|
+
* built, and every built record reused, with the reason the plan gave for it.
|
|
74
|
+
*/ export function aiJobWithBuiltCreations(job, kind, units) {
|
|
75
|
+
var _job_outputs, _plan_labels;
|
|
76
|
+
const plan = job.plan;
|
|
77
|
+
const built = aiSiteBuiltRefs(units, (_job_outputs = job.outputs) != null ? _job_outputs : []);
|
|
78
|
+
const labels = _extends({}, (_plan_labels = plan.labels) != null ? _plan_labels : {});
|
|
79
|
+
const reuse = [
|
|
80
|
+
...plan.reuse
|
|
81
|
+
];
|
|
82
|
+
for (const unit of units){
|
|
83
|
+
const record = unit.creation ? built.get(unit.creation.name.toLowerCase()) : undefined;
|
|
84
|
+
if (!record || !unit.creation) continue;
|
|
85
|
+
labels[record.id] = record.label;
|
|
86
|
+
reuse.push({
|
|
87
|
+
kind: record.kind,
|
|
88
|
+
id: record.id,
|
|
89
|
+
purpose: `made for this plan: ${unit.creation.why}`
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const own = plan.create.find((entry)=>entry.kind === kind);
|
|
93
|
+
return _extends({}, job, {
|
|
94
|
+
plan: _extends({}, plan, {
|
|
95
|
+
labels,
|
|
96
|
+
reuse,
|
|
97
|
+
create: own ? [
|
|
98
|
+
_extends({}, own, {
|
|
99
|
+
duplicateOf: aiSiteResolvedRef(own.duplicateOf, built)
|
|
100
|
+
})
|
|
101
|
+
] : []
|
|
102
|
+
})
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The kind's own runner, building the plan's other creations first: one a
|
|
107
|
+
* pass while any is unbuilt, then the kind's own record on the plan they
|
|
108
|
+
* resolve. A plan with nothing else to build runs the kind's own step as it
|
|
109
|
+
* always ran.
|
|
110
|
+
*/ export function aiBuildingPlanCreations(kind, step, runner, deps = {}) {
|
|
111
|
+
var _deps_runnerFor;
|
|
112
|
+
const runnerFor = (_deps_runnerFor = deps.runnerFor) != null ? _deps_runnerFor : aiJobStepRunnerFor;
|
|
113
|
+
return async (context)=>{
|
|
114
|
+
var _job_outputs;
|
|
115
|
+
const { job } = context;
|
|
116
|
+
const plan = aiConfirmedPlan(job);
|
|
117
|
+
const units = plan ? aiJobCreationUnits(kind, plan) : [];
|
|
118
|
+
if (!plan || !units.length) return runner(context);
|
|
119
|
+
const [unit] = aiSitePendingUnits(units, (_job_outputs = job.outputs) != null ? _job_outputs : []);
|
|
120
|
+
if (!unit) return runner(_extends({}, context, {
|
|
121
|
+
job: aiJobWithBuiltCreations(job, kind, units)
|
|
122
|
+
}));
|
|
123
|
+
const unitRunner = runnerFor(unit.jobKind);
|
|
124
|
+
if (!unitRunner) {
|
|
125
|
+
var _ref;
|
|
126
|
+
const model = (_ref = context.modelFor == null ? void 0 : context.modelFor.call(context, step)) != null ? _ref : aiModelForStep(step);
|
|
127
|
+
return _extends({}, aiUnspentOutcome(model), {
|
|
128
|
+
failure: AI_JOB_CREATION_UNAVAILABLE_COPY
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const pass = await aiRunJobUnit(_extends({}, context, {
|
|
132
|
+
job: forUnits(job, plan)
|
|
133
|
+
}), {
|
|
134
|
+
unit,
|
|
135
|
+
units,
|
|
136
|
+
runner: unitRunner,
|
|
137
|
+
emptyCopy: AI_JOB_CREATION_EMPTY_COPY
|
|
138
|
+
});
|
|
139
|
+
// A built creation is followed by the next, or by the job's own record.
|
|
140
|
+
return pass.built ? _extends({}, pass.outcome, {
|
|
141
|
+
continue: true
|
|
142
|
+
}) : pass.outcome;
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The least time this job's next pass needs (AGL-3035): a pass that builds a
|
|
147
|
+
* creation needs what the step that builds it registers for the job derived
|
|
148
|
+
* for it; the kind's own pass needs what the kind registered.
|
|
149
|
+
*/ export function aiPlanCreationsRunMinimumMs(kind) {
|
|
150
|
+
return (job)=>{
|
|
151
|
+
var _job_outputs;
|
|
152
|
+
const plan = aiConfirmedPlan(job);
|
|
153
|
+
if (!plan) return 0;
|
|
154
|
+
const units = aiJobCreationUnits(kind, plan);
|
|
155
|
+
const outputs = (_job_outputs = job.outputs) != null ? _job_outputs : [];
|
|
156
|
+
const [unit] = aiSitePendingUnits(units, outputs);
|
|
157
|
+
if (!unit) return 0;
|
|
158
|
+
return aiJobStepRunMinimumMs(aiSiteUnitJob(forUnits(job, plan), unit, aiSiteBuiltRefs(units, outputs)));
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Why a job of `kind` cannot build the creations its plan names, as the door
|
|
163
|
+
* that confirms the plan answers; `null` when it can, or when there is no plan
|
|
164
|
+
* yet. A creation the kind does not build, one this deployment has no step
|
|
165
|
+
* for, and one the workspace may not make on this site now are each refused
|
|
166
|
+
* before a credit is spent, the way a page job's door refuses them.
|
|
167
|
+
*/ export async function aiJobPlanCreationsRefusal(kind, context, hostId, deps = {}) {
|
|
168
|
+
var _ref, _deps_runnerFor, _deps_readCapabilities, _AI_JOB_CREATE_NOUNS_kind;
|
|
169
|
+
var _AI_BUILD_PLAN_CREATION_NOUNS_kind;
|
|
170
|
+
const plan = context.plan;
|
|
171
|
+
const creates = AI_JOB_CREATE_KINDS[kind];
|
|
172
|
+
if (!plan || !creates) return null;
|
|
173
|
+
// A plan creating only the job's own record is held by the draft allowance, as it always was.
|
|
174
|
+
const own = plan.create.findIndex((entry)=>entry.kind === kind);
|
|
175
|
+
if (!plan.create.some((_, index)=>index !== own)) return null;
|
|
176
|
+
const noun = (_ref = (_AI_BUILD_PLAN_CREATION_NOUNS_kind = AI_BUILD_PLAN_CREATION_NOUNS[kind]) == null ? void 0 : _AI_BUILD_PLAN_CREATION_NOUNS_kind.noun) != null ? _ref : kind;
|
|
177
|
+
const runnerFor = (_deps_runnerFor = deps.runnerFor) != null ? _deps_runnerFor : aiJobStepRunnerFor;
|
|
178
|
+
if (aiJobCreationUnits(kind, plan).some((unit)=>!runnerFor(unit.jobKind))) {
|
|
179
|
+
return {
|
|
180
|
+
status: 400,
|
|
181
|
+
error: AI_JOB_CREATION_UNAVAILABLE_COPY
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const readCapabilities = (_deps_readCapabilities = deps.readCapabilities) != null ? _deps_readCapabilities : readAiPlanCapabilities;
|
|
185
|
+
const capabilities = aiPlanCapabilitiesForJob(await readCapabilities(context.firestore, {
|
|
186
|
+
hostId,
|
|
187
|
+
org: context.org
|
|
188
|
+
}), {
|
|
189
|
+
noun: (_AI_JOB_CREATE_NOUNS_kind = AI_JOB_CREATE_NOUNS[kind]) != null ? _AI_JOB_CREATE_NOUNS_kind : `a ${kind} job`,
|
|
190
|
+
creates
|
|
191
|
+
});
|
|
192
|
+
const refusal = aiPageCreationRefusal(aiPlanUncreatable(plan, capabilities), noun);
|
|
193
|
+
return refusal ? {
|
|
194
|
+
status: 403,
|
|
195
|
+
error: refusal
|
|
196
|
+
} : null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
//# sourceMappingURL=ai-job-plan-creations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-plan-creations.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { AI_BUILD_PLAN_CREATION_NOUNS, type AiBuildPlanCreateKind } from '../model/ai-build-plan'\nimport { AI_JOB_CREATE_KINDS, AI_JOB_CREATE_NOUNS, aiJobOtherCreations } from '../model/ai-job-creations'\nimport type { AiJob, AiJobKind, AiJobPlan } from '../model/ai-jobs.types'\nimport { aiPageCreationRefusal } from '../model/ai-page-job'\nimport { aiPlanCapabilitiesForJob, aiPlanUncreatable } from '../model/ai-plan-capabilities'\nimport type { AiStepKind } from '../providers/catalog'\nimport { aiModelForStep } from '../providers/routing'\nimport type { AiJobAdmissionContext, AiJobAdmissionRefusal } from './ai-job-admission'\nimport { readAiPlanCapabilities } from './ai-job-drafts'\nimport { aiConfirmedPlan, aiUnspentOutcome } from './ai-job-generation'\nimport {\n aiCreationUnit,\n aiRunJobUnit,\n aiSiteBuiltRefs,\n aiSitePendingUnits,\n aiSiteResolvedRef,\n aiSiteUnitJob,\n type AiSiteUnit,\n} from './ai-job-site-step'\nimport type { AiJobStepRunner } from './ai-job-text-step'\nimport { aiJobStepRunMinimumMs, aiJobStepRunnerFor } from './ai-jobs'\n\n/**\n * A template, layout or component job builds the rest of its plan before its\n * own record (AGL-3143 §15), through the machinery a page job builds its\n * creations with (AGL-3031): one creation a pass, handed to the step that\n * builds that kind under a job derived from this one, where the job stands\n * read from its outputs. Once every creation is built, the kind's own step\n * runs on the plan with each `new:<name>` resolved to the record that was\n * built and listed among what the plan reuses — so the template's reuse check\n * holds it to placing the component its plan created, exactly as it holds it\n * to one the site always had.\n *\n * What each kind builds is `AI_JOB_CREATE_KINDS`; a form or an email design\n * places nothing another job builds, so its plan is only held to that list.\n */\n\n/** A creation the plan names that this deployment has no step to build. */\nexport const AI_JOB_CREATION_UNAVAILABLE_COPY =\n 'This needs something that cannot be built here yet. Describe it again.'\n\n/** A creation that finished without reporting what it built; the job's own record cannot place it. */\nexport const AI_JOB_CREATION_EMPTY_COPY = 'Part of this could not be built. Describe it again.'\n\n/** The order a page job builds creations in: a form before the component that may place it. */\nconst UNIT_ORDER: readonly AiBuildPlanCreateKind[] = ['form', 'component']\n\n/**\n * The units the creations a job of `kind` builds before its own record are\n * built as, in build order, each addressed by its place in the plan so a unit\n * run again finds its own draft.\n */\nexport function aiJobCreationUnits(kind: AiJobKind, plan: Pick<AiJobPlan, 'create'>): AiSiteUnit[] {\n const others = aiJobOtherCreations(kind, plan)\n return UNIT_ORDER.flatMap((order) =>\n others.flatMap(({ creation, index }) => {\n if (creation.kind !== order) return []\n const unit = aiCreationUnit(creation, `c${index}`)\n return unit ? [unit] : []\n }),\n )\n}\n\n/**\n * The job a unit is derived from. A unit places none of the components the\n * plan reuses: those are the job's own record's to place, and a card told to\n * place them would be refused for leaving them out.\n */\nfunction forUnits(job: AiJob, plan: AiJobPlan): AiJob {\n return { ...job, plan: { ...plan, reuse: plan.reuse.filter((entry) => entry.kind !== 'component') } }\n}\n\n/**\n * The job the kind's own step builds its record under once every creation is\n * built: the plan's own creation alone, from a copy resolved to what was\n * built, and every built record reused, with the reason the plan gave for it.\n */\nexport function aiJobWithBuiltCreations(job: AiJob, kind: AiJobKind, units: readonly AiSiteUnit[]): AiJob {\n const plan = job.plan as AiJobPlan\n const built = aiSiteBuiltRefs(units, job.outputs ?? [])\n const labels = { ...(plan.labels ?? {}) }\n const reuse = [...plan.reuse]\n for (const unit of units) {\n const record = unit.creation ? built.get(unit.creation.name.toLowerCase()) : undefined\n if (!record || !unit.creation) continue\n labels[record.id] = record.label\n reuse.push({ kind: record.kind, id: record.id, purpose: `made for this plan: ${unit.creation.why}` })\n }\n const own = plan.create.find((entry) => entry.kind === kind)\n return {\n ...job,\n plan: {\n ...plan,\n labels,\n reuse,\n create: own ? [{ ...own, duplicateOf: aiSiteResolvedRef(own.duplicateOf, built) }] : [],\n },\n }\n}\n\nexport interface AiJobPlanCreationsDeps {\n /** The runners a plan's creations are built by; the registry's otherwise. */\n runnerFor?: typeof aiJobStepRunnerFor\n}\n\n/**\n * The kind's own runner, building the plan's other creations first: one a\n * pass while any is unbuilt, then the kind's own record on the plan they\n * resolve. A plan with nothing else to build runs the kind's own step as it\n * always ran.\n */\nexport function aiBuildingPlanCreations(\n kind: AiJobKind,\n step: AiStepKind,\n runner: AiJobStepRunner,\n deps: AiJobPlanCreationsDeps = {},\n): AiJobStepRunner {\n const runnerFor = deps.runnerFor ?? aiJobStepRunnerFor\n return async (context) => {\n const { job } = context\n const plan = aiConfirmedPlan(job)\n const units = plan ? aiJobCreationUnits(kind, plan) : []\n if (!plan || !units.length) return runner(context)\n const [unit] = aiSitePendingUnits(units, job.outputs ?? [])\n if (!unit) return runner({ ...context, job: aiJobWithBuiltCreations(job, kind, units) })\n const unitRunner = runnerFor(unit.jobKind)\n if (!unitRunner) {\n const model = context.modelFor?.(step) ?? aiModelForStep(step)\n return { ...aiUnspentOutcome(model), failure: AI_JOB_CREATION_UNAVAILABLE_COPY }\n }\n const pass = await aiRunJobUnit(\n { ...context, job: forUnits(job, plan) },\n { unit, units, runner: unitRunner, emptyCopy: AI_JOB_CREATION_EMPTY_COPY },\n )\n // A built creation is followed by the next, or by the job's own record.\n return pass.built ? { ...pass.outcome, continue: true } : pass.outcome\n }\n}\n\n/**\n * The least time this job's next pass needs (AGL-3035): a pass that builds a\n * creation needs what the step that builds it registers for the job derived\n * for it; the kind's own pass needs what the kind registered.\n */\nexport function aiPlanCreationsRunMinimumMs(kind: AiJobKind): (job: AiJob) => number {\n return (job) => {\n const plan = aiConfirmedPlan(job)\n if (!plan) return 0\n const units = aiJobCreationUnits(kind, plan)\n const outputs = job.outputs ?? []\n const [unit] = aiSitePendingUnits(units, outputs)\n if (!unit) return 0\n return aiJobStepRunMinimumMs(aiSiteUnitJob(forUnits(job, plan), unit, aiSiteBuiltRefs(units, outputs)))\n }\n}\n\n/**\n * Why a job of `kind` cannot build the creations its plan names, as the door\n * that confirms the plan answers; `null` when it can, or when there is no plan\n * yet. A creation the kind does not build, one this deployment has no step\n * for, and one the workspace may not make on this site now are each refused\n * before a credit is spent, the way a page job's door refuses them.\n */\nexport async function aiJobPlanCreationsRefusal(\n kind: AiJobKind,\n context: Pick<AiJobAdmissionContext, 'firestore' | 'org' | 'plan'>,\n hostId: string,\n deps: AiJobPlanCreationsDeps & { readCapabilities?: typeof readAiPlanCapabilities } = {},\n): Promise<AiJobAdmissionRefusal | null> {\n const plan = context.plan\n const creates = AI_JOB_CREATE_KINDS[kind]\n if (!plan || !creates) return null\n // A plan creating only the job's own record is held by the draft allowance, as it always was.\n const own = plan.create.findIndex((entry) => entry.kind === kind)\n if (!plan.create.some((_, index) => index !== own)) return null\n const noun = AI_BUILD_PLAN_CREATION_NOUNS[kind as AiBuildPlanCreateKind]?.noun ?? kind\n const runnerFor = deps.runnerFor ?? aiJobStepRunnerFor\n if (aiJobCreationUnits(kind, plan).some((unit) => !runnerFor(unit.jobKind))) {\n return { status: 400, error: AI_JOB_CREATION_UNAVAILABLE_COPY }\n }\n const readCapabilities = deps.readCapabilities ?? readAiPlanCapabilities\n const capabilities = aiPlanCapabilitiesForJob(\n await readCapabilities(context.firestore, {\n hostId,\n org: context.org as Partial<AglynOrgBilling> | null,\n }),\n { noun: AI_JOB_CREATE_NOUNS[kind] ?? `a ${kind} job`, creates },\n )\n const refusal = aiPageCreationRefusal(aiPlanUncreatable(plan, capabilities), noun)\n return refusal ? { status: 403, error: refusal } : null\n}\n"],"names":["AI_BUILD_PLAN_CREATION_NOUNS","AI_JOB_CREATE_KINDS","AI_JOB_CREATE_NOUNS","aiJobOtherCreations","aiPageCreationRefusal","aiPlanCapabilitiesForJob","aiPlanUncreatable","aiModelForStep","readAiPlanCapabilities","aiConfirmedPlan","aiUnspentOutcome","aiCreationUnit","aiRunJobUnit","aiSiteBuiltRefs","aiSitePendingUnits","aiSiteResolvedRef","aiSiteUnitJob","aiJobStepRunMinimumMs","aiJobStepRunnerFor","AI_JOB_CREATION_UNAVAILABLE_COPY","AI_JOB_CREATION_EMPTY_COPY","UNIT_ORDER","aiJobCreationUnits","kind","plan","others","flatMap","order","creation","index","unit","forUnits","job","reuse","filter","entry","aiJobWithBuiltCreations","units","built","outputs","labels","record","get","name","toLowerCase","undefined","id","label","push","purpose","why","own","create","find","duplicateOf","aiBuildingPlanCreations","step","runner","deps","runnerFor","context","length","unitRunner","jobKind","model","modelFor","failure","pass","emptyCopy","outcome","continue","aiPlanCreationsRunMinimumMs","aiJobPlanCreationsRefusal","hostId","creates","findIndex","some","_","noun","status","error","readCapabilities","capabilities","firestore","org","refusal"],"mappings":"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"}
|
|
@@ -19,6 +19,7 @@ import { type AiBuildPlan } from '../model/ai-build-plan';
|
|
|
19
19
|
import type { AiJob, AiJobKind, AiJobPlan, AiJobStatus } from '../model/ai-jobs.types';
|
|
20
20
|
import { type AiPlanCapabilities, type AiPlanJobScope } from '../model/ai-plan-capabilities';
|
|
21
21
|
import type { AiSiteInventory } from '../model/ai-site-inventory';
|
|
22
|
+
import type { AiTool } from '../providers/contract';
|
|
22
23
|
import type { AiSystemBlock } from '../runtime/ai-runtime';
|
|
23
24
|
import { readSiteInventory } from '../runtime/site-inventory';
|
|
24
25
|
import { aiJobAdmissionRefusal } from './ai-job-admission';
|
|
@@ -96,6 +97,11 @@ export declare function aiJobPlanPrompt(job: Pick<AiJob, 'kind' | 'brief' | 'inp
|
|
|
96
97
|
* of the Free page's 300-credit wall, taking the room it keeps for a re-asked
|
|
97
98
|
* section from 45 credits to 44 and breaking `ai-job-free-page.spec.ts`. A
|
|
98
99
|
* page job must not pay for a sentence about templates.
|
|
100
|
+
*
|
|
101
|
+
* The list is the tokens a reader sees, never the whole catalog (AGL-3143
|
|
102
|
+
* §16). Offered a slug and the publish timestamp, a live plan promised them,
|
|
103
|
+
* and its build kept the promise the only way a slug can be kept: it printed
|
|
104
|
+
* "entry slug: {{entry.slug}}" on every article.
|
|
99
105
|
*/
|
|
100
106
|
export declare function aiPlanTemplateTokenLines(job: Pick<AiJob, 'kind' | 'inputs'>): string[];
|
|
101
107
|
/** A kind that builds only some plans: the creations it makes, and the shapes it refuses. */
|
|
@@ -106,8 +112,9 @@ export interface AiJobPlanScope extends AiPlanJobScope {
|
|
|
106
112
|
/**
|
|
107
113
|
* The kinds whose confirm door builds only some plans (AGL-3030). A page job
|
|
108
114
|
* builds one screen and a scaffold four to eight; each builds only the
|
|
109
|
-
* creations its list names.
|
|
110
|
-
*
|
|
115
|
+
* creations its list names. A template, layout, component, form or email job
|
|
116
|
+
* builds its own record and what that record places (AGL-3143 §15,
|
|
117
|
+
* `AI_JOB_CREATE_KINDS`), and holds no plan to a shape.
|
|
111
118
|
*/
|
|
112
119
|
export declare const AI_JOB_PLAN_SCOPES: Readonly<Partial<Record<AiJobKind, AiJobPlanScope>>>;
|
|
113
120
|
/**
|
|
@@ -158,6 +165,8 @@ export declare function aiJobPlanKey(input: {
|
|
|
158
165
|
prompt: string;
|
|
159
166
|
model: string;
|
|
160
167
|
system: readonly AiSystemBlock[];
|
|
168
|
+
/** The plan tool the request offers (AGL-3433); the plain one when absent. */
|
|
169
|
+
tool?: AiTool;
|
|
161
170
|
}): string;
|
|
162
171
|
/**
|
|
163
172
|
* The newest plan worth reusing out of what the finder returned: another
|
|
@@ -15,13 +15,15 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
15
15
|
* See the License for the specific language governing permissions and
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/ import { createHash } from "node:crypto";
|
|
18
|
-
import { AI_BUILD_PLAN_TOOL, isAiPlanNewRef } from "../model/ai-build-plan.js";
|
|
19
|
-
import { AI_TEMPLATE_SUBJECT_DEFINITIONS } from "../model/ai-template-subjects.js";
|
|
18
|
+
import { AI_BUILD_PLAN_TOOL, aiBuildPlanToolFor, isAiPlanNewRef } from "../model/ai-build-plan.js";
|
|
19
|
+
import { AI_TEMPLATE_SUBJECT_DEFINITIONS, aiTemplateShownTokens } from "../model/ai-template-subjects.js";
|
|
20
|
+
import { AI_JOB_CREATE_KINDS, AI_JOB_CREATE_NOUNS } from "../model/ai-job-creations.js";
|
|
20
21
|
import { AI_PAGE_CREATE_KINDS, aiPagePlanShapeRefusal } from "../model/ai-page-job.js";
|
|
21
22
|
import { aiPlanCapabilitiesForJob, aiPlanCapabilityLines } from "../model/ai-plan-capabilities.js";
|
|
22
23
|
import { AI_SITE_CREATE_KINDS, aiSitePlanShapeRefusal } from "../model/ai-site-job.js";
|
|
23
24
|
import { aiDoctrineSystemBlocks, runValidatedGeneration } from "../runtime/ai-doctrine.js";
|
|
24
25
|
import { AI_STEP_TIERS } from "../providers/catalog.js";
|
|
26
|
+
import { aiPlanEmbedBriefViolations } from "../runtime/ai-doctrine-validators.js";
|
|
25
27
|
import { AI_ROUTING_TABLE, aiModelForStep } from "../providers/routing.js";
|
|
26
28
|
import { readSiteInventory } from "../runtime/site-inventory.js";
|
|
27
29
|
import { aiJobAdmissionRefusal } from "./ai-job-admission.js";
|
|
@@ -58,7 +60,7 @@ import { AI_JOBS_COLLECTION, registerAiJobPlanStep } from "./ai-jobs.js";
|
|
|
58
60
|
* its plan is not kept, and it holds nothing.
|
|
59
61
|
*/ /** The plan step's own instructions, cached after the doctrine. */ export const AI_JOB_PLAN_INSTRUCTIONS = [
|
|
60
62
|
{
|
|
61
|
-
text: 'You plan what a website builder job will build, before anything is built. You are given the kind of job and the brief from the person who owns the site. ' + 'Answer with submit_build_plan: what the site inventory already has that the job reuses, what it must create and why nothing listed will do, and each
|
|
63
|
+
text: 'You plan what a website builder job will build, before anything is built. You are given the kind of job and the brief from the person who owns the site. ' + 'Answer with submit_build_plan: what the site inventory already has that the job reuses, what it must create and why nothing listed will do, and each page it builds (the screens list) with its layout, template, slug, search title, search description and sections from top to bottom. ' + 'Refer to inventory records by id and to what the plan creates as new:<name>. Plan only what the brief asks for: a component, layout, template, form or email job plans no pages unless the brief asks for them, and a page job plans exactly one page.'
|
|
62
64
|
}
|
|
63
65
|
];
|
|
64
66
|
/**
|
|
@@ -120,6 +122,11 @@ import { AI_JOBS_COLLECTION, registerAiJobPlanStep } from "./ai-jobs.js";
|
|
|
120
122
|
* of the Free page's 300-credit wall, taking the room it keeps for a re-asked
|
|
121
123
|
* section from 45 credits to 44 and breaking `ai-job-free-page.spec.ts`. A
|
|
122
124
|
* page job must not pay for a sentence about templates.
|
|
125
|
+
*
|
|
126
|
+
* The list is the tokens a reader sees, never the whole catalog (AGL-3143
|
|
127
|
+
* §16). Offered a slug and the publish timestamp, a live plan promised them,
|
|
128
|
+
* and its build kept the promise the only way a slug can be kept: it printed
|
|
129
|
+
* "entry slug: {{entry.slug}}" on every article.
|
|
123
130
|
*/ export function aiPlanTemplateTokenLines(job) {
|
|
124
131
|
var _job_inputs;
|
|
125
132
|
if (job.kind !== 'template') return [];
|
|
@@ -127,15 +134,16 @@ import { AI_JOBS_COLLECTION, registerAiJobPlanStep } from "./ai-jobs.js";
|
|
|
127
134
|
const definition = typeof subject === 'string' ? AI_TEMPLATE_SUBJECT_DEFINITIONS[subject] : undefined;
|
|
128
135
|
if (!definition) return [];
|
|
129
136
|
return [
|
|
130
|
-
`The template's fields are the binding tokens its page shows, each one of: ${definition
|
|
137
|
+
`The template's fields are the binding tokens its page shows a reader, each one of: ${aiTemplateShownTokens(definition).join(', ')}. A link or picture token is shown by the link or picture holding it. Promise only what the page fills.`
|
|
131
138
|
];
|
|
132
139
|
}
|
|
133
140
|
/**
|
|
134
141
|
* The kinds whose confirm door builds only some plans (AGL-3030). A page job
|
|
135
142
|
* builds one screen and a scaffold four to eight; each builds only the
|
|
136
|
-
* creations its list names.
|
|
137
|
-
*
|
|
138
|
-
|
|
143
|
+
* creations its list names. A template, layout, component, form or email job
|
|
144
|
+
* builds its own record and what that record places (AGL-3143 §15,
|
|
145
|
+
* `AI_JOB_CREATE_KINDS`), and holds no plan to a shape.
|
|
146
|
+
*/ export const AI_JOB_PLAN_SCOPES = _extends({
|
|
139
147
|
page: {
|
|
140
148
|
noun: 'a page job',
|
|
141
149
|
creates: AI_PAGE_CREATE_KINDS,
|
|
@@ -146,18 +154,33 @@ import { AI_JOBS_COLLECTION, registerAiJobPlanStep } from "./ai-jobs.js";
|
|
|
146
154
|
creates: AI_SITE_CREATE_KINDS,
|
|
147
155
|
shapeRefusal: aiSitePlanShapeRefusal
|
|
148
156
|
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
157
|
+
}, Object.fromEntries(Object.entries(AI_JOB_CREATE_KINDS).map(([kind, creates])=>[
|
|
158
|
+
kind,
|
|
159
|
+
{
|
|
160
|
+
noun: AI_JOB_CREATE_NOUNS[kind],
|
|
161
|
+
creates,
|
|
162
|
+
shapeRefusal: ()=>null
|
|
163
|
+
}
|
|
164
|
+
])));
|
|
165
|
+
/**
|
|
166
|
+
* What the plan step holds beyond the doctrine's plan rules, as the
|
|
167
|
+
* violations the plan's one re-ask names: the players the plan lists against
|
|
168
|
+
* the brief it answers (AGL-3433), which only this step reads beside the plan,
|
|
169
|
+
* and a kind's shape refusal.
|
|
170
|
+
*/ function planViolations(scope, brief) {
|
|
152
171
|
return (plan)=>{
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
172
|
+
var _ref;
|
|
173
|
+
const message = (_ref = scope == null ? void 0 : scope.shapeRefusal(plan)) != null ? _ref : null;
|
|
174
|
+
return [
|
|
175
|
+
...aiPlanEmbedBriefViolations(plan, brief),
|
|
176
|
+
...message ? [
|
|
177
|
+
{
|
|
178
|
+
rule: null,
|
|
179
|
+
code: 'plan-job-shape',
|
|
180
|
+
message
|
|
181
|
+
}
|
|
182
|
+
] : []
|
|
183
|
+
];
|
|
161
184
|
};
|
|
162
185
|
}
|
|
163
186
|
/**
|
|
@@ -226,7 +249,7 @@ import { AI_JOBS_COLLECTION, registerAiJobPlanStep } from "./ai-jobs.js";
|
|
|
226
249
|
* MEANS, rather than what it covers, bumps it and strands every old key
|
|
227
250
|
* harmlessly, since a key nothing matches simply asks the model.
|
|
228
251
|
*/ export function aiJobPlanKey(input) {
|
|
229
|
-
var _input_job_hostId;
|
|
252
|
+
var _input_job_hostId, _input_tool;
|
|
230
253
|
const digest = createHash('sha256');
|
|
231
254
|
for (const part of [
|
|
232
255
|
'plan.v1',
|
|
@@ -235,7 +258,7 @@ import { AI_JOBS_COLLECTION, registerAiJobPlanStep } from "./ai-jobs.js";
|
|
|
235
258
|
input.model,
|
|
236
259
|
input.prompt,
|
|
237
260
|
...input.system.map((block)=>block.text),
|
|
238
|
-
JSON.stringify(AI_BUILD_PLAN_TOOL)
|
|
261
|
+
JSON.stringify((_input_tool = input.tool) != null ? _input_tool : AI_BUILD_PLAN_TOOL)
|
|
239
262
|
]){
|
|
240
263
|
// Length-prefixed, so two different splits of the same characters cannot
|
|
241
264
|
// collide by running into one another.
|
|
@@ -373,13 +396,16 @@ export function createAiJobPlanStep(deps = {}) {
|
|
|
373
396
|
// — this job's plan is proposed to this member, whatever the last one did
|
|
374
397
|
// with theirs.
|
|
375
398
|
const resolved = model != null ? model : aiModelForStep('job.plan');
|
|
399
|
+
// A brief that asks for a video is offered the plan's list of players (AGL-3433).
|
|
400
|
+
const tool = aiBuildPlanToolFor(job.brief);
|
|
376
401
|
const key = aiJobPlanKey({
|
|
377
402
|
job,
|
|
378
403
|
prompt,
|
|
379
404
|
model: resolved,
|
|
380
405
|
system: aiDoctrineSystemBlocks(inventory, {
|
|
381
406
|
instructions: AI_JOB_PLAN_INSTRUCTIONS
|
|
382
|
-
})
|
|
407
|
+
}),
|
|
408
|
+
tool
|
|
383
409
|
});
|
|
384
410
|
const reused = findPlansByKey ? aiReusablePlan(await findPlansByKey(job.orgId, key, firestore), {
|
|
385
411
|
jobId: job.$id,
|
|
@@ -420,7 +446,7 @@ export function createAiJobPlanStep(deps = {}) {
|
|
|
420
446
|
content: prompt
|
|
421
447
|
}
|
|
422
448
|
],
|
|
423
|
-
tool
|
|
449
|
+
tool,
|
|
424
450
|
// The routing ceiling, lowered on a tier too slow to look records up,
|
|
425
451
|
// answer and ask again inside the least time the step registered.
|
|
426
452
|
maxTokens: aiJobPlanMaxTokens(resolved)
|
|
@@ -432,7 +458,7 @@ export function createAiJobPlanStep(deps = {}) {
|
|
|
432
458
|
signal
|
|
433
459
|
} : {}, {
|
|
434
460
|
capabilities,
|
|
435
|
-
extend:
|
|
461
|
+
extend: planViolations(scope, job.brief)
|
|
436
462
|
}));
|
|
437
463
|
const spent = _extends({
|
|
438
464
|
outputs: [],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-plan-step.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 { createHash } from 'node:crypto'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport {\n AI_BUILD_PLAN_TOOL,\n isAiPlanNewRef,\n type AiBuildPlan,\n} from '../model/ai-build-plan'\nimport type { AiJob, AiJobKind, AiJobPlan, AiJobStatus } from '../model/ai-jobs.types'\nimport {\n AI_TEMPLATE_SUBJECT_DEFINITIONS,\n type AiTemplateSubject,\n} from '../model/ai-template-subjects'\nimport { AI_PAGE_CREATE_KINDS, aiPagePlanShapeRefusal } from '../model/ai-page-job'\nimport {\n aiPlanCapabilitiesForJob,\n aiPlanCapabilityLines,\n type AiPlanCapabilities,\n type AiPlanJobScope,\n} from '../model/ai-plan-capabilities'\nimport type { AiSiteInventory } from '../model/ai-site-inventory'\nimport { AI_SITE_CREATE_KINDS, aiSitePlanShapeRefusal } from '../model/ai-site-job'\nimport { aiDoctrineSystemBlocks, runValidatedGeneration } from '../runtime/ai-doctrine'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport type { AiDoctrineViolation } from '../runtime/ai-doctrine-validators'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { readSiteInventory } from '../runtime/site-inventory'\nimport { aiJobAdmissionRefusal } from './ai-job-admission'\nimport { readAiPlanCapabilities } from './ai-job-drafts'\nimport {\n AI_JOB_BRIEF_MAX_CHARS,\n type AiJobStepOutcome,\n type AiJobStepRunner,\n} from './ai-job-text-step'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { aiDoctrineReview, aiUnspentOutcome } from './ai-job-generation'\nimport { aiPlanWithDraftIds } from './ai-job-draft-ids'\nimport { AI_JOBS_COLLECTION, registerAiJobPlanStep } from './ai-jobs'\n\n/**\n * The plan step (AGL-2935): the first step of every job that builds site\n * structure. Before a node is generated, the model answers with a typed plan\n * — what the site already has that the job reuses, what it creates and why,\n * and the screens it builds from them — held to the doctrine's plan rules\n * against the site's inventory and re-asked once when it breaks one.\n *\n * A plan that passes is kept on the job, and the job stops for review: the\n * member reads what it will build before a credit is spent building it, and\n * confirms through the resume door. A plan that still breaks a rule on its\n * re-ask stops the job the same way, with the rules named. Either way the\n * step writes nothing itself; the machine records the plan, the spend and\n * the stop, as it records every step.\n *\n * ── Told what it may create, refused what it cannot build (AGL-3030) ─────\n *\n * Before it asks, the step reads what the job may create on its site: the\n * workspace's plan, the site's counts against its limits, and — for a kind\n * that builds only some plans — what the job builds itself. The user turn\n * states it, the plan rules refuse a creation outside it with the one re-ask,\n * and where the workspace keeps no reusable components they accept the page\n * built inline. A plan the confirm door would still refuse — the kind's own\n * admission, handed the plan, as the resume door hands it — is refused HERE,\n * before a member is shown a Confirm: the job fails with the door's sentence,\n * its plan is not kept, and it holds nothing.\n */\n\n/** The plan step's own instructions, cached after the doctrine. */\nexport const AI_JOB_PLAN_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text:\n 'You plan what a website builder job will build, before anything is built. You are given the kind of job and the brief from the person who owns the site. ' +\n 'Answer with submit_build_plan: what the site inventory already has that the job reuses, what it must create and why nothing listed will do, and each screen it builds with its layout, template, slug, search title, search description and sections from top to bottom. ' +\n 'Refer to inventory records by id and to what the plan creates as new:<name>. Plan only what the brief asks for: a component, layout, template, form or email job plans no screens unless the brief asks for pages, and a page job plans exactly one screen.',\n },\n]\n\n/**\n * The plan step's time (AGL-3026, AGL-3036): its two inventory-lookup rounds,\n * its answer and its re-ask at the routing table's ceiling for `job.plan`, on\n * the tier that step kind is served from, with the step's reads and writes,\n * at the rates `ai-job-budget.ts` assumes — fitted to what a beat can give a\n * step, so the served tier asks a little under the routing ceiling.\n */\nexport const AI_JOB_PLAN_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.plan'],\n maxTokens: AI_ROUTING_TABLE['job.plan'].maxTokens,\n})\n\n/**\n * The least time a plan needs before it starts. Registered with the step, so\n * an inline door never starts a plan. A plan thinks before it answers and\n * routinely runs past an inline door's budget, and a provider call that\n * budget cuts off is still generated and billed upstream while the meter\n * records nothing. The beat starts a plan only with this much of its own\n * budget left, and a spec holds it inside that budget.\n */\nexport const AI_JOB_PLAN_STEP_MINIMUM_MS = AI_JOB_PLAN_STEP_BUDGET.minimumMs\n\n/**\n * The plan's answer ceiling on the model a job runs: the most whose worst\n * case fits the least time the step registered, and never more than the\n * routing table's. A model the catalog does not know is planned at the\n * slowest.\n */\nexport function aiJobPlanMaxTokens(model: string): number {\n return AI_JOB_PLAN_STEP_BUDGET.maxTokens(model)\n}\n\n/** What the member reads while a plan waits for them. */\nexport const AI_JOB_PLAN_REVIEW_COPY =\n 'The plan is ready. Review what the job will reuse and create, then confirm it to build.'\n\n/**\n * The job as the plan step's user turn: its kind, its brief, its scalar\n * inputs and, where the job read them, what it may create on its site\n * (AGL-3030). Per workspace and per site, which is why they ride here and\n * never in a system block the platform's cache entry is keyed on.\n */\nexport function aiJobPlanPrompt(\n job: Pick<AiJob, 'kind' | 'brief' | 'inputs'>,\n capabilities: AiPlanCapabilities | null = null,\n): string {\n const lines = [`Job kind: ${job.kind}`, `Brief: ${job.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`]\n for (const [key, value] of Object.entries(job.inputs ?? {})) {\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n lines.push(`${key}: ${String(value)}`)\n }\n }\n if (capabilities) lines.push(...aiPlanCapabilityLines(capabilities))\n lines.push(...aiPlanTemplateTokenLines(job))\n return lines.join('\\n')\n}\n\n/**\n * What a TEMPLATE job's plan is told its creation's `fields` are (AGL-3143\n * §11): the subject's binding tokens, listed so the planner promises only\n * what the page can fill and the build can be held to it\n * (`aiPlanTemplateTokenViolations`).\n *\n * ⛔ It rides the job's own turn and NOT the `fields` description in the plan\n * tool, which every kind's request caches. Written there it read better and\n * cost 40 tokens of the shared prefix — 2,980 to 3,020 — which is one credit\n * of the Free page's 300-credit wall, taking the room it keeps for a re-asked\n * section from 45 credits to 44 and breaking `ai-job-free-page.spec.ts`. A\n * page job must not pay for a sentence about templates.\n */\nexport function aiPlanTemplateTokenLines(job: Pick<AiJob, 'kind' | 'inputs'>): string[] {\n if (job.kind !== 'template') return []\n const subject = (job.inputs ?? {})['subject']\n const definition =\n typeof subject === 'string'\n ? AI_TEMPLATE_SUBJECT_DEFINITIONS[subject as AiTemplateSubject]\n : undefined\n if (!definition) return []\n return [\n `The template's fields are the binding tokens its page shows, each one of: ${definition.tokens\n .map((entry) => entry.token)\n .join(', ')}. Promise only what the page fills.`,\n ]\n}\n\n/** A kind that builds only some plans: the creations it makes, and the shapes it refuses. */\nexport interface AiJobPlanScope extends AiPlanJobScope {\n /** Why a job of the kind cannot build a plan of this shape; `null` when it can. Pure. */\n shapeRefusal: (plan: AiBuildPlan) => string | null\n}\n\n/**\n * The kinds whose confirm door builds only some plans (AGL-3030). A page job\n * builds one screen and a scaffold four to eight; each builds only the\n * creations its list names. Every other planned kind builds its own one\n * output, and its plan is held to the workspace alone.\n */\nexport const AI_JOB_PLAN_SCOPES: Readonly<Partial<Record<AiJobKind, AiJobPlanScope>>> = {\n page: { noun: 'a page job', creates: AI_PAGE_CREATE_KINDS, shapeRefusal: aiPagePlanShapeRefusal },\n site: { noun: 'a site scaffold', creates: AI_SITE_CREATE_KINDS, shapeRefusal: aiSitePlanShapeRefusal },\n}\n\n/** A kind's shape refusal as the violation the plan's one re-ask names. */\nfunction shapeViolations(scope: AiJobPlanScope | null): ((plan: AiBuildPlan) => AiDoctrineViolation[]) | undefined {\n if (!scope) return undefined\n return (plan) => {\n const message = scope.shapeRefusal(plan)\n return message ? [{ rule: null, code: 'plan-job-shape', message }] : []\n }\n}\n\n/**\n * The inventory names of every id the plan references, so the proposal reads\n * \"the Services layout\" rather than an id — kept on the plan because the\n * inventory is not read again when the member opens it.\n */\nexport function aiPlanLabels(\n plan: AiBuildPlan,\n inventory: AiSiteInventory | null,\n): Record<string, string> {\n const names = new Map<string, string>()\n for (const rows of [\n inventory?.components,\n inventory?.layouts,\n inventory?.templates,\n inventory?.forms,\n inventory?.datasets,\n inventory?.collections,\n inventory?.screens,\n ]) {\n for (const row of rows ?? []) names.set(row.id, row.name)\n }\n const refs = [\n ...plan.reuse.map((entry) => entry.id),\n ...plan.create.map((entry) => entry.duplicateOf),\n ...plan.screens.flatMap((screen) => [\n screen.layout,\n screen.template,\n screen.duplicateOf,\n ...screen.sections.flatMap((section) => section.uses),\n ]),\n ]\n const labels: Record<string, string> = {}\n for (const ref of refs) {\n if (!ref || isAiPlanNewRef(ref)) continue\n const name = names.get(ref)\n if (name) labels[ref] = name\n }\n return labels\n}\n\n/* ------------------------------------------------------------------------ *\n * Reusing an identical brief's plan\n * ------------------------------------------------------------------------ */\n\n/**\n * How long a plan may be reused for (AGL-2937).\n *\n * The window is what makes reuse safe rather than merely cheap. The key\n * already covers everything the request said — the brief, the inputs, the\n * model and the prompt as rendered, site inventory included — so a plan is\n * reused only when nothing the model was shown has changed. What the key\n * cannot see is the world outside the request: a page published, a component\n * renamed after the inventory was read, a member who meant something\n * different the second time. Fifteen minutes is short enough that the answer\n * is still the one the site would give, and long enough to cover what reuse\n * is actually for — the same brief run twice in a sitting, a job resubmitted\n * after a refused reservation, two members starting the same work.\n */\nexport const AI_PLAN_REUSE_WINDOW_MS = 15 * 60 * 1_000\n\n/** Jobs one lookup reads. More than one can share a key; the newest plan wins. */\nexport const AI_PLAN_REUSE_CANDIDATES = 5\n\n/** A job whose plan might be reused, as the finder reports it. */\nexport interface AiJobPlanCandidate {\n jobId: string\n status: AiJobStatus\n plan: AiJobPlan\n}\n\n/**\n * The digest a plan is reused by: the whole request, never the answer.\n *\n * It hashes what the model was asked and what it was shown — the job's kind\n * and site, the user turn (which carries the trimmed brief and every scalar\n * input), the model that would answer, every rendered system block including\n * the site inventory, and the tool's schema. Two requests that hash the same\n * would have been sent the same bytes to the same model, so the second can\n * keep the first's answer.\n *\n * The version tag is the escape hatch: anything that changes what a key\n * MEANS, rather than what it covers, bumps it and strands every old key\n * harmlessly, since a key nothing matches simply asks the model.\n */\nexport function aiJobPlanKey(input: {\n job: Pick<AiJob, 'kind' | 'hostId'>\n prompt: string\n model: string\n system: readonly AiSystemBlock[]\n}): string {\n const digest = createHash('sha256')\n for (const part of [\n 'plan.v1',\n input.job.kind,\n input.job.hostId ?? '',\n input.model,\n input.prompt,\n ...input.system.map((block) => block.text),\n JSON.stringify(AI_BUILD_PLAN_TOOL),\n ]) {\n // Length-prefixed, so two different splits of the same characters cannot\n // collide by running into one another.\n digest.update(`${part.length}:${part}\\u0000`)\n }\n return digest.digest('hex')\n}\n\n/** A job that could not have produced a reusable plan, whatever its key says. */\nconst UNREUSABLE: readonly AiJobStatus[] = ['canceled', 'failed']\n\nfunction planMillis(plan: AiJobPlan): number | null {\n const at = plan.proposedAt as unknown\n if (at instanceof Date) return at.getTime()\n if (at && typeof (at as { toMillis?: unknown }).toMillis === 'function') {\n return (at as { toMillis(): number }).toMillis()\n }\n return null\n}\n\n/**\n * The newest plan worth reusing out of what the finder returned: another\n * job's, still proposed or already confirmed, on a job that was neither\n * canceled nor failed, proposed inside the window.\n *\n * A canceled or failed job is excluded even though its plan may be perfectly\n * good: those are the jobs a member walked away from, and handing their plan\n * to the next one would make a rejected answer look like a fresh one.\n */\nexport function aiReusablePlan(\n candidates: readonly AiJobPlanCandidate[],\n context: { jobId: string; now: Date },\n): AiJobPlanCandidate | null {\n const floor = context.now.getTime() - AI_PLAN_REUSE_WINDOW_MS\n let best: AiJobPlanCandidate | null = null\n let bestAt = -1\n for (const candidate of candidates) {\n if (candidate.jobId === context.jobId) continue\n if (UNREUSABLE.includes(candidate.status)) continue\n if (candidate.plan.status !== 'proposed' && candidate.plan.status !== 'confirmed') continue\n const at = planMillis(candidate.plan)\n if (at === null || at < floor || at > context.now.getTime()) continue\n if (at > bestAt) {\n best = candidate\n bestAt = at\n }\n }\n return best\n}\n\nexport type AiJobPlanFinder = (\n orgId: string,\n key: string,\n firestore?: FirebaseFirestore.Firestore,\n) => Promise<AiJobPlanCandidate[]>\n\n/**\n * The finder the step uses in production: an equality on `plan.key` inside\n * the org's own jobs.\n *\n * One equality on one field, with no ordering beside it, so Firestore's\n * automatic single-field index answers it and no composite index is deployed.\n * The window and the statuses are applied in memory instead, which is what\n * keeps it that way.\n */\nexport const findAiJobsByPlanKey: AiJobPlanFinder = async (orgId, key, firestore) => {\n if (!firestore) return []\n const snapshot = await firestore\n .collection('orgs')\n .doc(orgId)\n .collection(AI_JOBS_COLLECTION)\n .where('plan.key', '==', key)\n .limit(AI_PLAN_REUSE_CANDIDATES)\n .get()\n return snapshot.docs.flatMap((doc) => {\n const data = doc.data() as { status?: AiJobStatus; plan?: AiJobPlan | null }\n return data.plan\n ? [{ jobId: doc.id, status: data.status ?? 'queued', plan: data.plan }]\n : []\n })\n}\n\n/** What a job may create on its site, read for the plan step; `null` for no site. */\nexport type AiPlanCapabilitiesReader = (input: {\n job: AiJob\n org: Partial<AglynOrgBilling> | null\n firestore: FirebaseFirestore.Firestore\n}) => Promise<AiPlanCapabilities | null>\n\n/** The reader the step uses in production: the draft bands' own arithmetic, for the job's site. */\nexport const readAiJobPlanCapabilities: AiPlanCapabilitiesReader = async ({ job, org, firestore }) =>\n job.hostId ? readAiPlanCapabilities(firestore, { hostId: job.hostId, org }) : null\n\nexport interface AiJobPlanStepDeps {\n /** The inventory reader; specs hand in a fake. */\n readInventory?: typeof readSiteInventory\n /** The reuse lookup; specs hand in a fake, and `null` turns reuse off. */\n findPlansByKey?: AiJobPlanFinder | null\n /** What the job may create on its site (AGL-3030); specs and the eval recorder hand in their own. */\n readCapabilities?: AiPlanCapabilitiesReader\n /** The kind's admission, asked of a plan before it is kept; the registry's otherwise. */\n admissionRefusal?: typeof aiJobAdmissionRefusal\n}\n\nexport function createAiJobPlanStep(deps: AiJobPlanStepDeps = {}): AiJobStepRunner {\n const readInventory = deps.readInventory ?? readSiteInventory\n const findPlansByKey =\n deps.findPlansByKey === undefined ? findAiJobsByPlanKey : deps.findPlansByKey\n const readCapabilities = deps.readCapabilities ?? readAiJobPlanCapabilities\n const admissionRefusal = deps.admissionRefusal ?? aiJobAdmissionRefusal\n return async ({ job, now, signal, firestore, modelFor, org: orgDocument }) => {\n const org = (orgDocument ?? null) as Partial<AglynOrgBilling> | null\n const [inventory, workspace] = await Promise.all([\n job.hostId ? readInventory(job.orgId, job.hostId, { firestore }) : Promise.resolve(null),\n readCapabilities({ job, org, firestore }),\n ])\n const scope = AI_JOB_PLAN_SCOPES[job.kind] ?? null\n const capabilities = workspace ? aiPlanCapabilitiesForJob(workspace, scope) : null\n // The model switch's answer for this job (AGL-2942): the creator's pick\n // where the plan, the org restriction and the allotment allowlists allow\n // it, and Auto held to those same lists otherwise. Without a resolver the\n // doctrine asks the routing table itself.\n const model = modelFor?.('job.plan')\n const route = AI_ROUTING_TABLE['job.plan']\n const prompt = aiJobPlanPrompt(job, capabilities)\n\n /**\n * The confirm door's answer for this plan, asked before the plan is kept\n * (AGL-3030): the kind's own admission, handed the plan as the resume\n * door hands it. A refusal fails the job with the door's sentence; a door\n * that could not answer keeps the plan, since the resume door asks again\n * before anything is built.\n */\n const refusalOnKeep = async (plan: AiJobPlan, outcome: AiJobStepOutcome): Promise<AiJobStepOutcome | null> => {\n let refusal: Awaited<ReturnType<typeof aiJobAdmissionRefusal>> = null\n try {\n refusal = await admissionRefusal(job.kind, {\n firestore,\n orgId: job.orgId,\n hostId: job.hostId ?? null,\n inputs: job.inputs ?? {},\n org,\n plan,\n uid: job.createdBy,\n })\n } catch (error) {\n console.error('ai plan admission failed', { orgId: job.orgId, jobId: job.$id, error })\n }\n return refusal ? { ...outcome, failure: refusal.error } : null\n }\n\n // Reuse before asking (AGL-2937). The key covers the whole request, so a\n // hit is a request that would have been sent the same bytes to the same\n // model; the window covers what the key cannot see. A reused plan spends\n // nothing, so the machine's spent-nothing branch releases the reservation\n // and meters no credit, and the job still stops for the member to confirm\n // — this job's plan is proposed to this member, whatever the last one did\n // with theirs.\n const resolved = model ?? aiModelForStep('job.plan')\n const key = aiJobPlanKey({\n job,\n prompt,\n model: resolved,\n system: aiDoctrineSystemBlocks(inventory, { instructions: AI_JOB_PLAN_INSTRUCTIONS }),\n })\n const reused = findPlansByKey\n ? aiReusablePlan(await findPlansByKey(job.orgId, key, firestore), {\n jobId: job.$id,\n now,\n })\n : null\n if (reused) {\n // The reused plan's draft ids name the other job's drafts; this job's are its own.\n const plan: AiJobPlan = aiPlanWithDraftIds(job.kind, {\n ...reused.plan,\n status: 'proposed',\n labels: aiPlanLabels(reused.plan, inventory),\n proposedAt: now as unknown as AiJobPlan['proposedAt'],\n confirmedAt: null,\n confirmedBy: null,\n key,\n reusedFrom: reused.jobId,\n })\n const unspent = aiUnspentOutcome(resolved)\n return (\n (await refusalOnKeep(plan, unspent)) ?? {\n ...unspent,\n plan,\n review: { reason: 'plan', message: AI_JOB_PLAN_REVIEW_COPY, findings: [] },\n }\n )\n }\n\n const result = await runValidatedGeneration('plan', {\n step: 'job.plan',\n ...(model ? { model } : {}),\n instructions: AI_JOB_PLAN_INSTRUCTIONS,\n inventory,\n messages: [{ role: 'user', content: prompt }],\n tool: AI_BUILD_PLAN_TOOL,\n // The routing ceiling, lowered on a tier too slow to look records up,\n // answer and ask again inside the least time the step registered.\n maxTokens: aiJobPlanMaxTokens(resolved),\n ...(route.thinking ? { thinking: route.thinking } : {}),\n ...(route.effort ? { effort: route.effort } : {}),\n ...(signal ? { signal } : {}),\n capabilities,\n extend: shapeViolations(scope),\n })\n const spent: AiJobStepOutcome = {\n outputs: [],\n usage: result.usage,\n estCostUsd: result.estCostUsd,\n model: result.model,\n stopReason: result.stopReason,\n ...(result.effort ? { effort: result.effort } : {}),\n }\n if (result.status === 'refused') return { ...spent, refused: true }\n if (result.status === 'needs_input') return { ...spent, review: aiDoctrineReview(result) }\n // Each draft the plan decides is named as the plan is kept, before anything is built (AGL-3079).\n const plan: AiJobPlan = aiPlanWithDraftIds(job.kind, {\n ...result.value,\n status: 'proposed',\n labels: aiPlanLabels(result.value, inventory),\n // A Date the Admin SDK stores as a timestamp, like every instant the machine writes.\n proposedAt: now as unknown as AiJobPlan['proposedAt'],\n confirmedAt: null,\n confirmedBy: null,\n key,\n })\n return (\n (await refusalOnKeep(plan, spent)) ?? {\n ...spent,\n plan,\n review: { reason: 'plan', message: AI_JOB_PLAN_REVIEW_COPY, findings: [] },\n }\n )\n }\n}\n\nexport const runAiJobPlanStep = createAiJobPlanStep()\n\n/**\n * Registers the plan step every planned kind runs first, with the least time\n * a plan needs; the plugin's console surface calls it.\n */\nexport function registerAiJobPlan(): void {\n registerAiJobPlanStep(runAiJobPlanStep, { minimumMs: AI_JOB_PLAN_STEP_MINIMUM_MS })\n}\n"],"names":["createHash","AI_BUILD_PLAN_TOOL","isAiPlanNewRef","AI_TEMPLATE_SUBJECT_DEFINITIONS","AI_PAGE_CREATE_KINDS","aiPagePlanShapeRefusal","aiPlanCapabilitiesForJob","aiPlanCapabilityLines","AI_SITE_CREATE_KINDS","aiSitePlanShapeRefusal","aiDoctrineSystemBlocks","runValidatedGeneration","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","readSiteInventory","aiJobAdmissionRefusal","readAiPlanCapabilities","AI_JOB_BRIEF_MAX_CHARS","aiJobStepBudget","aiDoctrineReview","aiUnspentOutcome","aiPlanWithDraftIds","AI_JOBS_COLLECTION","registerAiJobPlanStep","AI_JOB_PLAN_INSTRUCTIONS","text","AI_JOB_PLAN_STEP_BUDGET","tier","maxTokens","AI_JOB_PLAN_STEP_MINIMUM_MS","minimumMs","aiJobPlanMaxTokens","model","AI_JOB_PLAN_REVIEW_COPY","aiJobPlanPrompt","job","capabilities","lines","kind","brief","slice","key","value","Object","entries","inputs","push","String","aiPlanTemplateTokenLines","join","subject","definition","undefined","tokens","map","entry","token","AI_JOB_PLAN_SCOPES","page","noun","creates","shapeRefusal","site","shapeViolations","scope","plan","message","rule","code","aiPlanLabels","inventory","names","Map","rows","components","layouts","templates","forms","datasets","collections","screens","row","set","id","name","refs","reuse","create","duplicateOf","flatMap","screen","layout","template","sections","section","uses","labels","ref","get","AI_PLAN_REUSE_WINDOW_MS","AI_PLAN_REUSE_CANDIDATES","aiJobPlanKey","input","digest","part","hostId","prompt","system","block","JSON","stringify","update","length","UNREUSABLE","planMillis","at","proposedAt","Date","getTime","toMillis","aiReusablePlan","candidates","context","floor","now","best","bestAt","candidate","jobId","includes","status","findAiJobsByPlanKey","orgId","firestore","snapshot","collection","doc","where","limit","docs","data","readAiJobPlanCapabilities","org","createAiJobPlanStep","deps","readInventory","findPlansByKey","readCapabilities","admissionRefusal","signal","modelFor","orgDocument","workspace","Promise","all","resolve","route","refusalOnKeep","outcome","refusal","uid","createdBy","error","console","$id","failure","resolved","instructions","reused","confirmedAt","confirmedBy","reusedFrom","unspent","review","reason","findings","result","step","messages","role","content","tool","thinking","effort","extend","spent","outputs","usage","estCostUsd","stopReason","refused","runAiJobPlanStep","registerAiJobPlan"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-plan-step.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 { createHash } from 'node:crypto'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport {\n AI_BUILD_PLAN_TOOL,\n aiBuildPlanToolFor,\n isAiPlanNewRef,\n type AiBuildPlan,\n} from '../model/ai-build-plan'\nimport type { AiJob, AiJobKind, AiJobPlan, AiJobStatus } from '../model/ai-jobs.types'\nimport {\n AI_TEMPLATE_SUBJECT_DEFINITIONS,\n aiTemplateShownTokens,\n type AiTemplateSubject,\n} from '../model/ai-template-subjects'\nimport { AI_JOB_CREATE_KINDS, AI_JOB_CREATE_NOUNS } from '../model/ai-job-creations'\nimport { AI_PAGE_CREATE_KINDS, aiPagePlanShapeRefusal } from '../model/ai-page-job'\nimport {\n aiPlanCapabilitiesForJob,\n aiPlanCapabilityLines,\n type AiPlanCapabilities,\n type AiPlanJobScope,\n} from '../model/ai-plan-capabilities'\nimport type { AiSiteInventory } from '../model/ai-site-inventory'\nimport { AI_SITE_CREATE_KINDS, aiSitePlanShapeRefusal } from '../model/ai-site-job'\nimport { aiDoctrineSystemBlocks, runValidatedGeneration } from '../runtime/ai-doctrine'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport {\n aiPlanEmbedBriefViolations,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport type { AiTool } from '../providers/contract'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { readSiteInventory } from '../runtime/site-inventory'\nimport { aiJobAdmissionRefusal } from './ai-job-admission'\nimport { readAiPlanCapabilities } from './ai-job-drafts'\nimport {\n AI_JOB_BRIEF_MAX_CHARS,\n type AiJobStepOutcome,\n type AiJobStepRunner,\n} from './ai-job-text-step'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { aiDoctrineReview, aiUnspentOutcome } from './ai-job-generation'\nimport { aiPlanWithDraftIds } from './ai-job-draft-ids'\nimport { AI_JOBS_COLLECTION, registerAiJobPlanStep } from './ai-jobs'\n\n/**\n * The plan step (AGL-2935): the first step of every job that builds site\n * structure. Before a node is generated, the model answers with a typed plan\n * — what the site already has that the job reuses, what it creates and why,\n * and the screens it builds from them — held to the doctrine's plan rules\n * against the site's inventory and re-asked once when it breaks one.\n *\n * A plan that passes is kept on the job, and the job stops for review: the\n * member reads what it will build before a credit is spent building it, and\n * confirms through the resume door. A plan that still breaks a rule on its\n * re-ask stops the job the same way, with the rules named. Either way the\n * step writes nothing itself; the machine records the plan, the spend and\n * the stop, as it records every step.\n *\n * ── Told what it may create, refused what it cannot build (AGL-3030) ─────\n *\n * Before it asks, the step reads what the job may create on its site: the\n * workspace's plan, the site's counts against its limits, and — for a kind\n * that builds only some plans — what the job builds itself. The user turn\n * states it, the plan rules refuse a creation outside it with the one re-ask,\n * and where the workspace keeps no reusable components they accept the page\n * built inline. A plan the confirm door would still refuse — the kind's own\n * admission, handed the plan, as the resume door hands it — is refused HERE,\n * before a member is shown a Confirm: the job fails with the door's sentence,\n * its plan is not kept, and it holds nothing.\n */\n\n/** The plan step's own instructions, cached after the doctrine. */\nexport const AI_JOB_PLAN_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text:\n 'You plan what a website builder job will build, before anything is built. You are given the kind of job and the brief from the person who owns the site. ' +\n 'Answer with submit_build_plan: what the site inventory already has that the job reuses, what it must create and why nothing listed will do, and each page it builds (the screens list) with its layout, template, slug, search title, search description and sections from top to bottom. ' +\n 'Refer to inventory records by id and to what the plan creates as new:<name>. Plan only what the brief asks for: a component, layout, template, form or email job plans no pages unless the brief asks for them, and a page job plans exactly one page.',\n },\n]\n\n/**\n * The plan step's time (AGL-3026, AGL-3036): its two inventory-lookup rounds,\n * its answer and its re-ask at the routing table's ceiling for `job.plan`, on\n * the tier that step kind is served from, with the step's reads and writes,\n * at the rates `ai-job-budget.ts` assumes — fitted to what a beat can give a\n * step, so the served tier asks a little under the routing ceiling.\n */\nexport const AI_JOB_PLAN_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.plan'],\n maxTokens: AI_ROUTING_TABLE['job.plan'].maxTokens,\n})\n\n/**\n * The least time a plan needs before it starts. Registered with the step, so\n * an inline door never starts a plan. A plan thinks before it answers and\n * routinely runs past an inline door's budget, and a provider call that\n * budget cuts off is still generated and billed upstream while the meter\n * records nothing. The beat starts a plan only with this much of its own\n * budget left, and a spec holds it inside that budget.\n */\nexport const AI_JOB_PLAN_STEP_MINIMUM_MS = AI_JOB_PLAN_STEP_BUDGET.minimumMs\n\n/**\n * The plan's answer ceiling on the model a job runs: the most whose worst\n * case fits the least time the step registered, and never more than the\n * routing table's. A model the catalog does not know is planned at the\n * slowest.\n */\nexport function aiJobPlanMaxTokens(model: string): number {\n return AI_JOB_PLAN_STEP_BUDGET.maxTokens(model)\n}\n\n/** What the member reads while a plan waits for them. */\nexport const AI_JOB_PLAN_REVIEW_COPY =\n 'The plan is ready. Review what the job will reuse and create, then confirm it to build.'\n\n/**\n * The job as the plan step's user turn: its kind, its brief, its scalar\n * inputs and, where the job read them, what it may create on its site\n * (AGL-3030). Per workspace and per site, which is why they ride here and\n * never in a system block the platform's cache entry is keyed on.\n */\nexport function aiJobPlanPrompt(\n job: Pick<AiJob, 'kind' | 'brief' | 'inputs'>,\n capabilities: AiPlanCapabilities | null = null,\n): string {\n const lines = [`Job kind: ${job.kind}`, `Brief: ${job.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`]\n for (const [key, value] of Object.entries(job.inputs ?? {})) {\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n lines.push(`${key}: ${String(value)}`)\n }\n }\n if (capabilities) lines.push(...aiPlanCapabilityLines(capabilities))\n lines.push(...aiPlanTemplateTokenLines(job))\n return lines.join('\\n')\n}\n\n/**\n * What a TEMPLATE job's plan is told its creation's `fields` are (AGL-3143\n * §11): the subject's binding tokens, listed so the planner promises only\n * what the page can fill and the build can be held to it\n * (`aiPlanTemplateTokenViolations`).\n *\n * ⛔ It rides the job's own turn and NOT the `fields` description in the plan\n * tool, which every kind's request caches. Written there it read better and\n * cost 40 tokens of the shared prefix — 2,980 to 3,020 — which is one credit\n * of the Free page's 300-credit wall, taking the room it keeps for a re-asked\n * section from 45 credits to 44 and breaking `ai-job-free-page.spec.ts`. A\n * page job must not pay for a sentence about templates.\n *\n * The list is the tokens a reader sees, never the whole catalog (AGL-3143\n * §16). Offered a slug and the publish timestamp, a live plan promised them,\n * and its build kept the promise the only way a slug can be kept: it printed\n * \"entry slug: {{entry.slug}}\" on every article.\n */\nexport function aiPlanTemplateTokenLines(job: Pick<AiJob, 'kind' | 'inputs'>): string[] {\n if (job.kind !== 'template') return []\n const subject = (job.inputs ?? {})['subject']\n const definition =\n typeof subject === 'string'\n ? AI_TEMPLATE_SUBJECT_DEFINITIONS[subject as AiTemplateSubject]\n : undefined\n if (!definition) return []\n return [\n `The template's fields are the binding tokens its page shows a reader, each one of: ${aiTemplateShownTokens(\n definition,\n ).join(', ')}. A link or picture token is shown by the link or picture holding it. Promise only what the page fills.`,\n ]\n}\n\n/** A kind that builds only some plans: the creations it makes, and the shapes it refuses. */\nexport interface AiJobPlanScope extends AiPlanJobScope {\n /** Why a job of the kind cannot build a plan of this shape; `null` when it can. Pure. */\n shapeRefusal: (plan: AiBuildPlan) => string | null\n}\n\n/**\n * The kinds whose confirm door builds only some plans (AGL-3030). A page job\n * builds one screen and a scaffold four to eight; each builds only the\n * creations its list names. A template, layout, component, form or email job\n * builds its own record and what that record places (AGL-3143 §15,\n * `AI_JOB_CREATE_KINDS`), and holds no plan to a shape.\n */\nexport const AI_JOB_PLAN_SCOPES: Readonly<Partial<Record<AiJobKind, AiJobPlanScope>>> = {\n page: { noun: 'a page job', creates: AI_PAGE_CREATE_KINDS, shapeRefusal: aiPagePlanShapeRefusal },\n site: { noun: 'a site scaffold', creates: AI_SITE_CREATE_KINDS, shapeRefusal: aiSitePlanShapeRefusal },\n ...Object.fromEntries(\n Object.entries(AI_JOB_CREATE_KINDS).map(([kind, creates]) => [\n kind,\n { noun: AI_JOB_CREATE_NOUNS[kind as AiJobKind] as string, creates, shapeRefusal: () => null },\n ]),\n ),\n}\n\n/**\n * What the plan step holds beyond the doctrine's plan rules, as the\n * violations the plan's one re-ask names: the players the plan lists against\n * the brief it answers (AGL-3433), which only this step reads beside the plan,\n * and a kind's shape refusal.\n */\nfunction planViolations(\n scope: AiJobPlanScope | null,\n brief: string,\n): (plan: AiBuildPlan) => AiDoctrineViolation[] {\n return (plan) => {\n const message = scope?.shapeRefusal(plan) ?? null\n return [\n ...aiPlanEmbedBriefViolations(plan, brief),\n ...(message ? [{ rule: null, code: 'plan-job-shape', message }] : []),\n ]\n }\n}\n\n/**\n * The inventory names of every id the plan references, so the proposal reads\n * \"the Services layout\" rather than an id — kept on the plan because the\n * inventory is not read again when the member opens it.\n */\nexport function aiPlanLabels(\n plan: AiBuildPlan,\n inventory: AiSiteInventory | null,\n): Record<string, string> {\n const names = new Map<string, string>()\n for (const rows of [\n inventory?.components,\n inventory?.layouts,\n inventory?.templates,\n inventory?.forms,\n inventory?.datasets,\n inventory?.collections,\n inventory?.screens,\n ]) {\n for (const row of rows ?? []) names.set(row.id, row.name)\n }\n const refs = [\n ...plan.reuse.map((entry) => entry.id),\n ...plan.create.map((entry) => entry.duplicateOf),\n ...plan.screens.flatMap((screen) => [\n screen.layout,\n screen.template,\n screen.duplicateOf,\n ...screen.sections.flatMap((section) => section.uses),\n ]),\n ]\n const labels: Record<string, string> = {}\n for (const ref of refs) {\n if (!ref || isAiPlanNewRef(ref)) continue\n const name = names.get(ref)\n if (name) labels[ref] = name\n }\n return labels\n}\n\n/* ------------------------------------------------------------------------ *\n * Reusing an identical brief's plan\n * ------------------------------------------------------------------------ */\n\n/**\n * How long a plan may be reused for (AGL-2937).\n *\n * The window is what makes reuse safe rather than merely cheap. The key\n * already covers everything the request said — the brief, the inputs, the\n * model and the prompt as rendered, site inventory included — so a plan is\n * reused only when nothing the model was shown has changed. What the key\n * cannot see is the world outside the request: a page published, a component\n * renamed after the inventory was read, a member who meant something\n * different the second time. Fifteen minutes is short enough that the answer\n * is still the one the site would give, and long enough to cover what reuse\n * is actually for — the same brief run twice in a sitting, a job resubmitted\n * after a refused reservation, two members starting the same work.\n */\nexport const AI_PLAN_REUSE_WINDOW_MS = 15 * 60 * 1_000\n\n/** Jobs one lookup reads. More than one can share a key; the newest plan wins. */\nexport const AI_PLAN_REUSE_CANDIDATES = 5\n\n/** A job whose plan might be reused, as the finder reports it. */\nexport interface AiJobPlanCandidate {\n jobId: string\n status: AiJobStatus\n plan: AiJobPlan\n}\n\n/**\n * The digest a plan is reused by: the whole request, never the answer.\n *\n * It hashes what the model was asked and what it was shown — the job's kind\n * and site, the user turn (which carries the trimmed brief and every scalar\n * input), the model that would answer, every rendered system block including\n * the site inventory, and the tool's schema. Two requests that hash the same\n * would have been sent the same bytes to the same model, so the second can\n * keep the first's answer.\n *\n * The version tag is the escape hatch: anything that changes what a key\n * MEANS, rather than what it covers, bumps it and strands every old key\n * harmlessly, since a key nothing matches simply asks the model.\n */\nexport function aiJobPlanKey(input: {\n job: Pick<AiJob, 'kind' | 'hostId'>\n prompt: string\n model: string\n system: readonly AiSystemBlock[]\n /** The plan tool the request offers (AGL-3433); the plain one when absent. */\n tool?: AiTool\n}): string {\n const digest = createHash('sha256')\n for (const part of [\n 'plan.v1',\n input.job.kind,\n input.job.hostId ?? '',\n input.model,\n input.prompt,\n ...input.system.map((block) => block.text),\n JSON.stringify(input.tool ?? AI_BUILD_PLAN_TOOL),\n ]) {\n // Length-prefixed, so two different splits of the same characters cannot\n // collide by running into one another.\n digest.update(`${part.length}:${part}\\u0000`)\n }\n return digest.digest('hex')\n}\n\n/** A job that could not have produced a reusable plan, whatever its key says. */\nconst UNREUSABLE: readonly AiJobStatus[] = ['canceled', 'failed']\n\nfunction planMillis(plan: AiJobPlan): number | null {\n const at = plan.proposedAt as unknown\n if (at instanceof Date) return at.getTime()\n if (at && typeof (at as { toMillis?: unknown }).toMillis === 'function') {\n return (at as { toMillis(): number }).toMillis()\n }\n return null\n}\n\n/**\n * The newest plan worth reusing out of what the finder returned: another\n * job's, still proposed or already confirmed, on a job that was neither\n * canceled nor failed, proposed inside the window.\n *\n * A canceled or failed job is excluded even though its plan may be perfectly\n * good: those are the jobs a member walked away from, and handing their plan\n * to the next one would make a rejected answer look like a fresh one.\n */\nexport function aiReusablePlan(\n candidates: readonly AiJobPlanCandidate[],\n context: { jobId: string; now: Date },\n): AiJobPlanCandidate | null {\n const floor = context.now.getTime() - AI_PLAN_REUSE_WINDOW_MS\n let best: AiJobPlanCandidate | null = null\n let bestAt = -1\n for (const candidate of candidates) {\n if (candidate.jobId === context.jobId) continue\n if (UNREUSABLE.includes(candidate.status)) continue\n if (candidate.plan.status !== 'proposed' && candidate.plan.status !== 'confirmed') continue\n const at = planMillis(candidate.plan)\n if (at === null || at < floor || at > context.now.getTime()) continue\n if (at > bestAt) {\n best = candidate\n bestAt = at\n }\n }\n return best\n}\n\nexport type AiJobPlanFinder = (\n orgId: string,\n key: string,\n firestore?: FirebaseFirestore.Firestore,\n) => Promise<AiJobPlanCandidate[]>\n\n/**\n * The finder the step uses in production: an equality on `plan.key` inside\n * the org's own jobs.\n *\n * One equality on one field, with no ordering beside it, so Firestore's\n * automatic single-field index answers it and no composite index is deployed.\n * The window and the statuses are applied in memory instead, which is what\n * keeps it that way.\n */\nexport const findAiJobsByPlanKey: AiJobPlanFinder = async (orgId, key, firestore) => {\n if (!firestore) return []\n const snapshot = await firestore\n .collection('orgs')\n .doc(orgId)\n .collection(AI_JOBS_COLLECTION)\n .where('plan.key', '==', key)\n .limit(AI_PLAN_REUSE_CANDIDATES)\n .get()\n return snapshot.docs.flatMap((doc) => {\n const data = doc.data() as { status?: AiJobStatus; plan?: AiJobPlan | null }\n return data.plan\n ? [{ jobId: doc.id, status: data.status ?? 'queued', plan: data.plan }]\n : []\n })\n}\n\n/** What a job may create on its site, read for the plan step; `null` for no site. */\nexport type AiPlanCapabilitiesReader = (input: {\n job: AiJob\n org: Partial<AglynOrgBilling> | null\n firestore: FirebaseFirestore.Firestore\n}) => Promise<AiPlanCapabilities | null>\n\n/** The reader the step uses in production: the draft bands' own arithmetic, for the job's site. */\nexport const readAiJobPlanCapabilities: AiPlanCapabilitiesReader = async ({ job, org, firestore }) =>\n job.hostId ? readAiPlanCapabilities(firestore, { hostId: job.hostId, org }) : null\n\nexport interface AiJobPlanStepDeps {\n /** The inventory reader; specs hand in a fake. */\n readInventory?: typeof readSiteInventory\n /** The reuse lookup; specs hand in a fake, and `null` turns reuse off. */\n findPlansByKey?: AiJobPlanFinder | null\n /** What the job may create on its site (AGL-3030); specs and the eval recorder hand in their own. */\n readCapabilities?: AiPlanCapabilitiesReader\n /** The kind's admission, asked of a plan before it is kept; the registry's otherwise. */\n admissionRefusal?: typeof aiJobAdmissionRefusal\n}\n\nexport function createAiJobPlanStep(deps: AiJobPlanStepDeps = {}): AiJobStepRunner {\n const readInventory = deps.readInventory ?? readSiteInventory\n const findPlansByKey =\n deps.findPlansByKey === undefined ? findAiJobsByPlanKey : deps.findPlansByKey\n const readCapabilities = deps.readCapabilities ?? readAiJobPlanCapabilities\n const admissionRefusal = deps.admissionRefusal ?? aiJobAdmissionRefusal\n return async ({ job, now, signal, firestore, modelFor, org: orgDocument }) => {\n const org = (orgDocument ?? null) as Partial<AglynOrgBilling> | null\n const [inventory, workspace] = await Promise.all([\n job.hostId ? readInventory(job.orgId, job.hostId, { firestore }) : Promise.resolve(null),\n readCapabilities({ job, org, firestore }),\n ])\n const scope = AI_JOB_PLAN_SCOPES[job.kind] ?? null\n const capabilities = workspace ? aiPlanCapabilitiesForJob(workspace, scope) : null\n // The model switch's answer for this job (AGL-2942): the creator's pick\n // where the plan, the org restriction and the allotment allowlists allow\n // it, and Auto held to those same lists otherwise. Without a resolver the\n // doctrine asks the routing table itself.\n const model = modelFor?.('job.plan')\n const route = AI_ROUTING_TABLE['job.plan']\n const prompt = aiJobPlanPrompt(job, capabilities)\n\n /**\n * The confirm door's answer for this plan, asked before the plan is kept\n * (AGL-3030): the kind's own admission, handed the plan as the resume\n * door hands it. A refusal fails the job with the door's sentence; a door\n * that could not answer keeps the plan, since the resume door asks again\n * before anything is built.\n */\n const refusalOnKeep = async (plan: AiJobPlan, outcome: AiJobStepOutcome): Promise<AiJobStepOutcome | null> => {\n let refusal: Awaited<ReturnType<typeof aiJobAdmissionRefusal>> = null\n try {\n refusal = await admissionRefusal(job.kind, {\n firestore,\n orgId: job.orgId,\n hostId: job.hostId ?? null,\n inputs: job.inputs ?? {},\n org,\n plan,\n uid: job.createdBy,\n })\n } catch (error) {\n console.error('ai plan admission failed', { orgId: job.orgId, jobId: job.$id, error })\n }\n return refusal ? { ...outcome, failure: refusal.error } : null\n }\n\n // Reuse before asking (AGL-2937). The key covers the whole request, so a\n // hit is a request that would have been sent the same bytes to the same\n // model; the window covers what the key cannot see. A reused plan spends\n // nothing, so the machine's spent-nothing branch releases the reservation\n // and meters no credit, and the job still stops for the member to confirm\n // — this job's plan is proposed to this member, whatever the last one did\n // with theirs.\n const resolved = model ?? aiModelForStep('job.plan')\n // A brief that asks for a video is offered the plan's list of players (AGL-3433).\n const tool = aiBuildPlanToolFor(job.brief)\n const key = aiJobPlanKey({\n job,\n prompt,\n model: resolved,\n system: aiDoctrineSystemBlocks(inventory, { instructions: AI_JOB_PLAN_INSTRUCTIONS }),\n tool,\n })\n const reused = findPlansByKey\n ? aiReusablePlan(await findPlansByKey(job.orgId, key, firestore), {\n jobId: job.$id,\n now,\n })\n : null\n if (reused) {\n // The reused plan's draft ids name the other job's drafts; this job's are its own.\n const plan: AiJobPlan = aiPlanWithDraftIds(job.kind, {\n ...reused.plan,\n status: 'proposed',\n labels: aiPlanLabels(reused.plan, inventory),\n proposedAt: now as unknown as AiJobPlan['proposedAt'],\n confirmedAt: null,\n confirmedBy: null,\n key,\n reusedFrom: reused.jobId,\n })\n const unspent = aiUnspentOutcome(resolved)\n return (\n (await refusalOnKeep(plan, unspent)) ?? {\n ...unspent,\n plan,\n review: { reason: 'plan', message: AI_JOB_PLAN_REVIEW_COPY, findings: [] },\n }\n )\n }\n\n const result = await runValidatedGeneration('plan', {\n step: 'job.plan',\n ...(model ? { model } : {}),\n instructions: AI_JOB_PLAN_INSTRUCTIONS,\n inventory,\n messages: [{ role: 'user', content: prompt }],\n tool,\n // The routing ceiling, lowered on a tier too slow to look records up,\n // answer and ask again inside the least time the step registered.\n maxTokens: aiJobPlanMaxTokens(resolved),\n ...(route.thinking ? { thinking: route.thinking } : {}),\n ...(route.effort ? { effort: route.effort } : {}),\n ...(signal ? { signal } : {}),\n capabilities,\n extend: planViolations(scope, job.brief),\n })\n const spent: AiJobStepOutcome = {\n outputs: [],\n usage: result.usage,\n estCostUsd: result.estCostUsd,\n model: result.model,\n stopReason: result.stopReason,\n ...(result.effort ? { effort: result.effort } : {}),\n }\n if (result.status === 'refused') return { ...spent, refused: true }\n if (result.status === 'needs_input') return { ...spent, review: aiDoctrineReview(result) }\n // Each draft the plan decides is named as the plan is kept, before anything is built (AGL-3079).\n const plan: AiJobPlan = aiPlanWithDraftIds(job.kind, {\n ...result.value,\n status: 'proposed',\n labels: aiPlanLabels(result.value, inventory),\n // A Date the Admin SDK stores as a timestamp, like every instant the machine writes.\n proposedAt: now as unknown as AiJobPlan['proposedAt'],\n confirmedAt: null,\n confirmedBy: null,\n key,\n })\n return (\n (await refusalOnKeep(plan, spent)) ?? {\n ...spent,\n plan,\n review: { reason: 'plan', message: AI_JOB_PLAN_REVIEW_COPY, findings: [] },\n }\n )\n }\n}\n\nexport const runAiJobPlanStep = createAiJobPlanStep()\n\n/**\n * Registers the plan step every planned kind runs first, with the least time\n * a plan needs; the plugin's console surface calls it.\n */\nexport function registerAiJobPlan(): void {\n registerAiJobPlanStep(runAiJobPlanStep, { minimumMs: AI_JOB_PLAN_STEP_MINIMUM_MS })\n}\n"],"names":["createHash","AI_BUILD_PLAN_TOOL","aiBuildPlanToolFor","isAiPlanNewRef","AI_TEMPLATE_SUBJECT_DEFINITIONS","aiTemplateShownTokens","AI_JOB_CREATE_KINDS","AI_JOB_CREATE_NOUNS","AI_PAGE_CREATE_KINDS","aiPagePlanShapeRefusal","aiPlanCapabilitiesForJob","aiPlanCapabilityLines","AI_SITE_CREATE_KINDS","aiSitePlanShapeRefusal","aiDoctrineSystemBlocks","runValidatedGeneration","AI_STEP_TIERS","aiPlanEmbedBriefViolations","AI_ROUTING_TABLE","aiModelForStep","readSiteInventory","aiJobAdmissionRefusal","readAiPlanCapabilities","AI_JOB_BRIEF_MAX_CHARS","aiJobStepBudget","aiDoctrineReview","aiUnspentOutcome","aiPlanWithDraftIds","AI_JOBS_COLLECTION","registerAiJobPlanStep","AI_JOB_PLAN_INSTRUCTIONS","text","AI_JOB_PLAN_STEP_BUDGET","tier","maxTokens","AI_JOB_PLAN_STEP_MINIMUM_MS","minimumMs","aiJobPlanMaxTokens","model","AI_JOB_PLAN_REVIEW_COPY","aiJobPlanPrompt","job","capabilities","lines","kind","brief","slice","key","value","Object","entries","inputs","push","String","aiPlanTemplateTokenLines","join","subject","definition","undefined","AI_JOB_PLAN_SCOPES","page","noun","creates","shapeRefusal","site","fromEntries","map","planViolations","scope","plan","message","rule","code","aiPlanLabels","inventory","names","Map","rows","components","layouts","templates","forms","datasets","collections","screens","row","set","id","name","refs","reuse","entry","create","duplicateOf","flatMap","screen","layout","template","sections","section","uses","labels","ref","get","AI_PLAN_REUSE_WINDOW_MS","AI_PLAN_REUSE_CANDIDATES","aiJobPlanKey","input","digest","part","hostId","prompt","system","block","JSON","stringify","tool","update","length","UNREUSABLE","planMillis","at","proposedAt","Date","getTime","toMillis","aiReusablePlan","candidates","context","floor","now","best","bestAt","candidate","jobId","includes","status","findAiJobsByPlanKey","orgId","firestore","snapshot","collection","doc","where","limit","docs","data","readAiJobPlanCapabilities","org","createAiJobPlanStep","deps","readInventory","findPlansByKey","readCapabilities","admissionRefusal","signal","modelFor","orgDocument","workspace","Promise","all","resolve","route","refusalOnKeep","outcome","refusal","uid","createdBy","error","console","$id","failure","resolved","instructions","reused","confirmedAt","confirmedBy","reusedFrom","unspent","review","reason","findings","result","step","messages","role","content","thinking","effort","extend","spent","outputs","usage","estCostUsd","stopReason","refused","runAiJobPlanStep","registerAiJobPlan"],"mappings":"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"}
|