@aglyn/plugins-ai 1.0.0-beta.225 → 1.0.0-beta.227
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 -1
- package/package.json +17 -17
- package/src/lib/billing/ai-month-meter.js +5 -3
- package/src/lib/billing/ai-month-meter.js.map +1 -1
- package/src/lib/billing/ai-overage-ledger.js +3 -1
- package/src/lib/billing/ai-overage-ledger.js.map +1 -1
- package/src/lib/components/ai-brief-dialog.component.d.ts +6 -2
- package/src/lib/components/ai-brief-dialog.component.js +35 -17
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-describe-automation.component.js +17 -2
- package/src/lib/components/ai-describe-automation.component.js.map +1 -1
- package/src/lib/components/ai-insight-dialog.component.js +24 -2
- package/src/lib/components/ai-insight-dialog.component.js.map +1 -1
- package/src/lib/components/ai-job-events.d.ts +14 -0
- package/src/lib/components/ai-job-events.js +76 -0
- package/src/lib/components/ai-job-events.js.map +1 -0
- package/src/lib/components/ai-job-follow.component.d.ts +49 -0
- package/src/lib/components/ai-job-follow.component.js +210 -0
- package/src/lib/components/ai-job-follow.component.js.map +1 -0
- package/src/lib/components/ai-job-links.d.ts +42 -0
- package/src/lib/components/ai-job-links.js +140 -0
- package/src/lib/components/ai-job-links.js.map +1 -0
- package/src/lib/components/ai-job-plan.component.d.ts +13 -6
- package/src/lib/components/ai-job-plan.component.js +29 -3
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-job-requests.d.ts +24 -0
- package/src/lib/components/ai-job-requests.js +76 -0
- package/src/lib/components/ai-job-requests.js.map +1 -0
- package/src/lib/components/ai-jobs-indicator.component.d.ts +25 -0
- package/src/lib/components/ai-jobs-indicator.component.js +80 -0
- package/src/lib/components/ai-jobs-indicator.component.js.map +1 -0
- package/src/lib/components/ai-jobs-store.d.ts +81 -0
- package/src/lib/components/ai-jobs-store.js +218 -0
- package/src/lib/components/ai-jobs-store.js.map +1 -0
- package/src/lib/components/ai-page-brief-dialog.component.d.ts +3 -3
- package/src/lib/components/ai-page-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.d.ts +23 -0
- package/src/lib/components/ai-site-build-page.component.js +377 -0
- package/src/lib/components/ai-site-build-page.component.js.map +1 -0
- package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
- package/src/lib/components/ai-site-start-card.component.js +522 -130
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-site-starter-fallback.component.d.ts +24 -0
- package/src/lib/components/ai-site-starter-fallback.component.js +112 -0
- package/src/lib/components/ai-site-starter-fallback.component.js.map +1 -0
- package/src/lib/components/assist-jobs-drawer.component.d.ts +18 -20
- package/src/lib/components/assist-jobs-drawer.component.js +152 -172
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +62 -6
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/staff-ai-credit-return.component.d.ts +55 -0
- package/src/lib/components/staff-ai-credit-return.component.js +346 -0
- package/src/lib/components/staff-ai-credit-return.component.js.map +1 -0
- package/src/lib/components/staff-org-ai-card.component.js +364 -279
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
- package/src/lib/components/staff-user-ai-usage-card.component.d.ts +4 -0
- package/src/lib/components/staff-user-ai-usage-card.component.js +83 -28
- package/src/lib/components/staff-user-ai-usage-card.component.js.map +1 -1
- package/src/lib/components/use-ai-job-run.js +5 -18
- package/src/lib/components/use-ai-job-run.js.map +1 -1
- package/src/lib/jobs/ai-job-auto-confirm.d.ts +52 -0
- package/src/lib/jobs/ai-job-auto-confirm.js +62 -0
- package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -0
- package/src/lib/jobs/ai-job-plan-step.d.ts +33 -2
- package/src/lib/jobs/ai-job-plan-step.js +161 -17
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +7 -0
- package/src/lib/jobs/ai-job-site-step.js +34 -4
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +7 -0
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs-notify.d.ts +37 -0
- package/src/lib/jobs/ai-jobs-notify.js +51 -0
- package/src/lib/jobs/ai-jobs-notify.js.map +1 -0
- package/src/lib/jobs/ai-jobs.d.ts +31 -4
- package/src/lib/jobs/ai-jobs.js +196 -32
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/model/ai-job-activity.d.ts +112 -0
- package/src/lib/model/ai-job-activity.js +144 -0
- package/src/lib/model/ai-job-activity.js.map +1 -0
- package/src/lib/model/ai-job-failure-copy.d.ts +69 -0
- package/src/lib/model/ai-job-failure-copy.js +78 -0
- package/src/lib/model/ai-job-failure-copy.js.map +1 -0
- package/src/lib/model/ai-job-notice.d.ts +61 -0
- package/src/lib/model/ai-job-notice.js +112 -0
- package/src/lib/model/ai-job-notice.js.map +1 -0
- package/src/lib/model/ai-jobs.types.d.ts +26 -0
- package/src/lib/model/ai-jobs.types.js +1 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-plan-capabilities.d.ts +7 -0
- package/src/lib/model/ai-plan-capabilities.js.map +1 -1
- package/src/lib/model/ai-site-build-progress.d.ts +45 -0
- package/src/lib/model/ai-site-build-progress.js +76 -0
- package/src/lib/model/ai-site-build-progress.js.map +1 -0
- package/src/lib/model/ai-site-inventory.d.ts +9 -0
- package/src/lib/model/ai-site-inventory.js +7 -2
- package/src/lib/model/ai-site-inventory.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +93 -2
- package/src/lib/model/ai-site-job.js +88 -7
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-start.d.ts +3 -1
- package/src/lib/model/ai-site-start.js +6 -4
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/ai-template-subjects.d.ts +4 -3
- package/src/lib/model/ai-template-subjects.js +5 -4
- package/src/lib/model/ai-template-subjects.js.map +1 -1
- package/src/lib/plan-entitlements.d.ts +3 -3
- package/src/lib/plan-entitlements.js +3 -3
- package/src/lib/plan-entitlements.js.map +1 -1
- package/src/lib/plugin.js +28 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/runtime/ai-abuse-guards.d.ts +9 -6
- package/src/lib/runtime/ai-abuse-guards.js +10 -7
- package/src/lib/runtime/ai-abuse-guards.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.js +31 -4
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-gate.d.ts +3 -2
- package/src/lib/runtime/ai-gate.js +3 -2
- package/src/lib/runtime/ai-gate.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +12 -4
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/site-inventory.d.ts +16 -1
- package/src/lib/runtime/site-inventory.js +43 -3
- package/src/lib/runtime/site-inventory.js.map +1 -1
- package/src/lib/server/ai-admin-credits.d.ts +21 -0
- package/src/lib/server/ai-admin-credits.js +213 -0
- package/src/lib/server/ai-admin-credits.js.map +1 -0
- package/src/lib/server/ai-admin-org.js +12 -5
- package/src/lib/server/ai-admin-org.js.map +1 -1
- package/src/lib/server/ai-admin-signals.js +3 -1
- package/src/lib/server/ai-admin-signals.js.map +1 -1
- package/src/lib/server/ai-admin-user.js +12 -1
- package/src/lib/server/ai-admin-user.js.map +1 -1
- package/src/lib/server/ai-allotments.js +3 -2
- package/src/lib/server/ai-allotments.js.map +1 -1
- package/src/lib/server/ai-jobs-route.js +20 -2
- package/src/lib/server/ai-jobs-route.js.map +1 -1
- package/src/lib/server/assist-chat.js +3 -2
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/billing-credits.js +4 -3
- package/src/lib/server/billing-credits.js.map +1 -1
- package/src/lib/server/billing-overage.js +3 -2
- package/src/lib/server/billing-overage.js.map +1 -1
- package/src/lib/server.js +11 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/usage/assist-credit-returns-write.d.ts +75 -0
- package/src/lib/usage/assist-credit-returns-write.js +150 -0
- package/src/lib/usage/assist-credit-returns-write.js.map +1 -0
- package/src/lib/usage/assist-credit-returns.d.ts +68 -0
- package/src/lib/usage/assist-credit-returns.js +79 -0
- package/src/lib/usage/assist-credit-returns.js.map +1 -0
- package/src/lib/usage/assist-free-taste.js +6 -3
- package/src/lib/usage/assist-free-taste.js.map +1 -1
- package/src/lib/usage/assist-job-refund.d.ts +100 -0
- package/src/lib/usage/assist-job-refund.js +109 -0
- package/src/lib/usage/assist-job-refund.js.map +1 -0
- package/src/lib/usage/assist-usage.js +5 -2
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/staff-org-ai.d.ts +31 -1
- package/src/lib/usage/staff-org-ai.js +19 -2
- package/src/lib/usage/staff-org-ai.js.map +1 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { AiJob } from '../model/ai-jobs.types';
|
|
18
|
+
import { AI_JOB_AUTO_CONFIRM_INPUT } from '../model/ai-site-job';
|
|
19
|
+
/**
|
|
20
|
+
* A guided site start confirms its own plan (AGL-3594).
|
|
21
|
+
*
|
|
22
|
+
* Every planned job stops after its plan for a person to read and confirm it.
|
|
23
|
+
* For the guided start on a new site that stop read as a step nobody knew they
|
|
24
|
+
* had to take: a person who had just answered "what is your site?" was shown a
|
|
25
|
+
* plan to approve, and a site nobody approved was a site never built. So the
|
|
26
|
+
* guided start asks for its plan to be confirmed for it, with this input, and
|
|
27
|
+
* the machine confirms it the moment the plan step keeps a plan — the same
|
|
28
|
+
* confirmation the resume door makes, by the job's creator, with the same
|
|
29
|
+
* hold on what the plan is estimated to cost.
|
|
30
|
+
*
|
|
31
|
+
* Honored only where it is safe to skip the read: a `site` job, on a site
|
|
32
|
+
* that has published nothing of its own yet — the site the guided start is
|
|
33
|
+
* offered on. Everywhere else the create door drops it, so a page, a form or
|
|
34
|
+
* a site job on an established site still waits for a person. What bounds the
|
|
35
|
+
* spend is unchanged: the plan step's own checks (the Free page cap and the
|
|
36
|
+
* Free wall among them), the plan's admission, and the reservation each step
|
|
37
|
+
* takes.
|
|
38
|
+
*/
|
|
39
|
+
/** The job input the guided start sets. */
|
|
40
|
+
export { AI_JOB_AUTO_CONFIRM_INPUT };
|
|
41
|
+
/** Whether a job's plan is confirmed for it when the plan step keeps one. */
|
|
42
|
+
export declare function aiJobAutoConfirms(job: Pick<AiJob, 'kind' | 'inputs'>): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* A job's inputs as the create door stores them: `autoConfirm` kept only on a
|
|
45
|
+
* `site` job for a site that publishes nothing of the owner's yet, and dropped
|
|
46
|
+
* from every other request, which then waits for a person as it always has.
|
|
47
|
+
*/
|
|
48
|
+
export declare function aiJobAdmittedInputs(firestore: FirebaseFirestore.Firestore, request: {
|
|
49
|
+
kind: string;
|
|
50
|
+
hostId: string | null;
|
|
51
|
+
inputs: Record<string, unknown>;
|
|
52
|
+
}): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
3
|
+
import { _ as _to_property_key } from "@swc/helpers/_/_to_property_key";
|
|
4
|
+
/**
|
|
5
|
+
* @license
|
|
6
|
+
* Copyright 2026 Aglyn LLC
|
|
7
|
+
*
|
|
8
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9
|
+
* you may not use this file except in compliance with the License.
|
|
10
|
+
* You may obtain a copy of the License at
|
|
11
|
+
*
|
|
12
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
13
|
+
*
|
|
14
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
15
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
16
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
|
+
* See the License for the specific language governing permissions and
|
|
18
|
+
* limitations under the License.
|
|
19
|
+
*/ import { isFirstPublishedRoute } from "@aglyn/aglyn/app-utils/analytics-events";
|
|
20
|
+
import { AI_JOB_AUTO_CONFIRM_INPUT } from "../model/ai-site-job.js";
|
|
21
|
+
/**
|
|
22
|
+
* A guided site start confirms its own plan (AGL-3594).
|
|
23
|
+
*
|
|
24
|
+
* Every planned job stops after its plan for a person to read and confirm it.
|
|
25
|
+
* For the guided start on a new site that stop read as a step nobody knew they
|
|
26
|
+
* had to take: a person who had just answered "what is your site?" was shown a
|
|
27
|
+
* plan to approve, and a site nobody approved was a site never built. So the
|
|
28
|
+
* guided start asks for its plan to be confirmed for it, with this input, and
|
|
29
|
+
* the machine confirms it the moment the plan step keeps a plan — the same
|
|
30
|
+
* confirmation the resume door makes, by the job's creator, with the same
|
|
31
|
+
* hold on what the plan is estimated to cost.
|
|
32
|
+
*
|
|
33
|
+
* Honored only where it is safe to skip the read: a `site` job, on a site
|
|
34
|
+
* that has published nothing of its own yet — the site the guided start is
|
|
35
|
+
* offered on. Everywhere else the create door drops it, so a page, a form or
|
|
36
|
+
* a site job on an established site still waits for a person. What bounds the
|
|
37
|
+
* spend is unchanged: the plan step's own checks (the Free page cap and the
|
|
38
|
+
* Free wall among them), the plan's admission, and the reservation each step
|
|
39
|
+
* takes.
|
|
40
|
+
*/ /** The job input the guided start sets. */ export { AI_JOB_AUTO_CONFIRM_INPUT };
|
|
41
|
+
/** Whether a job's plan is confirmed for it when the plan step keeps one. */ export function aiJobAutoConfirms(job) {
|
|
42
|
+
var _job_inputs;
|
|
43
|
+
return job.kind === 'site' && ((_job_inputs = job.inputs) != null ? _job_inputs : {})[AI_JOB_AUTO_CONFIRM_INPUT] === true;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A job's inputs as the create door stores them: `autoConfirm` kept only on a
|
|
47
|
+
* `site` job for a site that publishes nothing of the owner's yet, and dropped
|
|
48
|
+
* from every other request, which then waits for a person as it always has.
|
|
49
|
+
*/ export async function aiJobAdmittedInputs(firestore, request) {
|
|
50
|
+
if (!(AI_JOB_AUTO_CONFIRM_INPUT in request.inputs)) return request.inputs;
|
|
51
|
+
const _request_inputs = request.inputs, { [AI_JOB_AUTO_CONFIRM_INPUT]: asked } = _request_inputs, rest = _object_without_properties_loose(_request_inputs, [
|
|
52
|
+
_to_property_key(AI_JOB_AUTO_CONFIRM_INPUT)
|
|
53
|
+
]);
|
|
54
|
+
if (request.kind !== 'site' || asked !== true || !request.hostId) return rest;
|
|
55
|
+
const host = await firestore.collection('hosts').doc(request.hostId).get();
|
|
56
|
+
const blank = host.exists && isFirstPublishedRoute(host.get('screens'), host.get('defaultHomeScreenId'));
|
|
57
|
+
return blank ? _extends({}, rest, {
|
|
58
|
+
[AI_JOB_AUTO_CONFIRM_INPUT]: true
|
|
59
|
+
}) : rest;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=ai-job-auto-confirm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-auto-confirm.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 { isFirstPublishedRoute } from '@aglyn/aglyn/app-utils/analytics-events'\nimport type { AiJob } from '../model/ai-jobs.types'\nimport { AI_JOB_AUTO_CONFIRM_INPUT } from '../model/ai-site-job'\n\n/**\n * A guided site start confirms its own plan (AGL-3594).\n *\n * Every planned job stops after its plan for a person to read and confirm it.\n * For the guided start on a new site that stop read as a step nobody knew they\n * had to take: a person who had just answered \"what is your site?\" was shown a\n * plan to approve, and a site nobody approved was a site never built. So the\n * guided start asks for its plan to be confirmed for it, with this input, and\n * the machine confirms it the moment the plan step keeps a plan — the same\n * confirmation the resume door makes, by the job's creator, with the same\n * hold on what the plan is estimated to cost.\n *\n * Honored only where it is safe to skip the read: a `site` job, on a site\n * that has published nothing of its own yet — the site the guided start is\n * offered on. Everywhere else the create door drops it, so a page, a form or\n * a site job on an established site still waits for a person. What bounds the\n * spend is unchanged: the plan step's own checks (the Free page cap and the\n * Free wall among them), the plan's admission, and the reservation each step\n * takes.\n */\n\n/** The job input the guided start sets. */\nexport { AI_JOB_AUTO_CONFIRM_INPUT }\n\n/** Whether a job's plan is confirmed for it when the plan step keeps one. */\nexport function aiJobAutoConfirms(job: Pick<AiJob, 'kind' | 'inputs'>): boolean {\n return job.kind === 'site' && (job.inputs ?? {})[AI_JOB_AUTO_CONFIRM_INPUT] === true\n}\n\n/**\n * A job's inputs as the create door stores them: `autoConfirm` kept only on a\n * `site` job for a site that publishes nothing of the owner's yet, and dropped\n * from every other request, which then waits for a person as it always has.\n */\nexport async function aiJobAdmittedInputs(\n firestore: FirebaseFirestore.Firestore,\n request: { kind: string; hostId: string | null; inputs: Record<string, unknown> },\n): Promise<Record<string, unknown>> {\n if (!(AI_JOB_AUTO_CONFIRM_INPUT in request.inputs)) return request.inputs\n const { [AI_JOB_AUTO_CONFIRM_INPUT]: asked, ...rest } = request.inputs\n if (request.kind !== 'site' || asked !== true || !request.hostId) return rest\n const host = await firestore.collection('hosts').doc(request.hostId).get()\n const blank =\n host.exists &&\n isFirstPublishedRoute(\n host.get('screens') as Record<string, unknown> | undefined,\n host.get('defaultHomeScreenId') as string | undefined,\n )\n return blank ? { ...rest, [AI_JOB_AUTO_CONFIRM_INPUT]: true } : rest\n}\n"],"names":["isFirstPublishedRoute","AI_JOB_AUTO_CONFIRM_INPUT","aiJobAutoConfirms","job","kind","inputs","aiJobAdmittedInputs","firestore","request","asked","rest","hostId","host","collection","doc","get","blank","exists"],"mappings":";;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,qBAAqB,QAAQ,0CAAyC;AAE/E,SAASC,yBAAyB,QAAQ,0BAAsB;AAEhE;;;;;;;;;;;;;;;;;;;CAmBC,GAED,yCAAyC,GACzC,SAASA,yBAAyB,GAAE;AAEpC,2EAA2E,GAC3E,OAAO,SAASC,kBAAkBC,GAAmC;QACpCA;IAA/B,OAAOA,IAAIC,IAAI,KAAK,UAAU,EAACD,cAAAA,IAAIE,MAAM,YAAVF,cAAc,CAAC,EAAE,CAACF,0BAA0B,KAAK;AAClF;AAEA;;;;CAIC,GACD,OAAO,eAAeK,oBACpBC,SAAsC,EACtCC,OAAiF;IAEjF,IAAI,CAAEP,CAAAA,6BAA6BO,QAAQH,MAAM,AAAD,GAAI,OAAOG,QAAQH,MAAM;IACzE,MAAwDG,kBAAAA,QAAQH,MAAM,EAAhE,EAAE,CAACJ,0BAA0B,EAAEQ,KAAK,EAAW,GAAGD,iBAATE,wCAASF;yBAA/CP;;IACT,IAAIO,QAAQJ,IAAI,KAAK,UAAUK,UAAU,QAAQ,CAACD,QAAQG,MAAM,EAAE,OAAOD;IACzE,MAAME,OAAO,MAAML,UAAUM,UAAU,CAAC,SAASC,GAAG,CAACN,QAAQG,MAAM,EAAEI,GAAG;IACxE,MAAMC,QACJJ,KAAKK,MAAM,IACXjB,sBACEY,KAAKG,GAAG,CAAC,YACTH,KAAKG,GAAG,CAAC;IAEb,OAAOC,QAAQ,aAAKN;QAAM,CAACT,0BAA0B,EAAE;SAASS;AAClE"}
|
|
@@ -84,7 +84,19 @@ export declare const AI_JOB_PLAN_REVIEW_COPY = "The plan is ready. Review what t
|
|
|
84
84
|
* (AGL-3030). Per workspace and per site, which is why they ride here and
|
|
85
85
|
* never in a system block the platform's cache entry is keyed on.
|
|
86
86
|
*/
|
|
87
|
-
export declare function aiJobPlanPrompt(job: Pick<AiJob, 'kind' | 'brief' | 'inputs'>, capabilities?: AiPlanCapabilities | null): string;
|
|
87
|
+
export declare function aiJobPlanPrompt(job: Pick<AiJob, 'kind' | 'brief' | 'inputs'>, capabilities?: AiPlanCapabilities | null, inventory?: AiSiteInventory | null): string;
|
|
88
|
+
/**
|
|
89
|
+
* What a SITE job's plan is told beside the inventory (AGL-3594): the pages
|
|
90
|
+
* the site already has and that their addresses are taken, which one — the
|
|
91
|
+
* untouched starter home — a planned home page at `/` replaces, the Free page
|
|
92
|
+
* cap and its guidance, and that a plan is an outline.
|
|
93
|
+
*
|
|
94
|
+
* A site job only. A new site is born with a published home page at `/`
|
|
95
|
+
* (AGL-3497), and a plan that did not know it could replace it planned its own
|
|
96
|
+
* home there and was refused for the clash on every new site. Kept off a page
|
|
97
|
+
* job's turn, whose length the Free page's wall is proven at.
|
|
98
|
+
*/
|
|
99
|
+
export declare function aiPlanSiteLines(job: Pick<AiJob, 'kind'>, inventory: AiSiteInventory | null, capabilities: AiPlanCapabilities | null): string[];
|
|
88
100
|
/**
|
|
89
101
|
* What a TEMPLATE job's plan is told its creation's `fields` are (AGL-3143
|
|
90
102
|
* §11): the subject's binding tokens, listed so the planner promises only
|
|
@@ -107,7 +119,9 @@ export declare function aiPlanTemplateTokenLines(job: Pick<AiJob, 'kind' | 'inpu
|
|
|
107
119
|
/** A kind that builds only some plans: the creations it makes, and the shapes it refuses. */
|
|
108
120
|
export interface AiJobPlanScope extends AiPlanJobScope {
|
|
109
121
|
/** Why a job of the kind cannot build a plan of this shape; `null` when it can. Pure. */
|
|
110
|
-
shapeRefusal: (plan: AiBuildPlan
|
|
122
|
+
shapeRefusal: (plan: AiBuildPlan, options?: {
|
|
123
|
+
freeTaste?: boolean;
|
|
124
|
+
}) => string | null;
|
|
111
125
|
}
|
|
112
126
|
/**
|
|
113
127
|
* The kinds whose confirm door builds only some plans (AGL-3030). A page job
|
|
@@ -211,6 +225,23 @@ export interface AiJobPlanStepDeps {
|
|
|
211
225
|
admissionRefusal?: typeof aiJobAdmissionRefusal;
|
|
212
226
|
}
|
|
213
227
|
export declare function createAiJobPlanStep(deps?: AiJobPlanStepDeps): AiJobStepRunner;
|
|
228
|
+
/**
|
|
229
|
+
* A site job's capabilities on the Free taste (AGL-3594): its plan holds at
|
|
230
|
+
* most the pages the member asked for within the Free band, which the Free
|
|
231
|
+
* wall then holds it to, and it changes no theme — the site was born with
|
|
232
|
+
* one (AGL-3497), and a palette pass is credits the two pages need. Every
|
|
233
|
+
* other job's capabilities pass through.
|
|
234
|
+
*/
|
|
235
|
+
export declare function aiSitePlanCapabilities(job: Pick<AiJob, 'kind' | 'inputs'>, capabilities: AiPlanCapabilities | null): AiPlanCapabilities | null;
|
|
236
|
+
/**
|
|
237
|
+
* The model a plan runs on (AGL-3594): the resolved one, except a Free site
|
|
238
|
+
* plans on its provider's fast tier — an outline the plan rules hold and
|
|
239
|
+
* re-ask, at a fifth of the balanced tier's output rate. A model the creator
|
|
240
|
+
* picked by name stays theirs.
|
|
241
|
+
*/
|
|
242
|
+
export declare function aiSitePlanModel(job: Pick<AiJob, 'kind' | 'model'>, resolved: string | undefined, freeTaste: boolean): string | undefined;
|
|
243
|
+
/** The plan's answer ceiling: the step's own, and a scaffold's outline ceiling under it (AGL-3594). */
|
|
244
|
+
export declare function aiSitePlanMaxTokens(job: Pick<AiJob, 'kind'>, model: string, freeTaste: boolean): number;
|
|
214
245
|
export declare const runAiJobPlanStep: AiJobStepRunner;
|
|
215
246
|
/**
|
|
216
247
|
* Registers the plan step every planned kind runs first, with the least time
|
|
@@ -20,10 +20,15 @@ import { AI_TEMPLATE_SUBJECT_DEFINITIONS, aiTemplateShownTokens } from "../model
|
|
|
20
20
|
import { AI_JOB_CREATE_KINDS, AI_JOB_CREATE_NOUNS } from "../model/ai-job-creations.js";
|
|
21
21
|
import { AI_PAGE_CREATE_KINDS, aiPagePlanShapeRefusal } from "../model/ai-page-job.js";
|
|
22
22
|
import { aiPlanCapabilitiesForJob, aiPlanCapabilityLines } from "../model/ai-plan-capabilities.js";
|
|
23
|
-
import { AI_SITE_CREATE_KINDS, aiSitePlanShapeRefusal } from "../model/ai-site-job.js";
|
|
23
|
+
import { AI_FREE_SITE_WORST_CASE_CREDITS, AI_SITE_CREATE_KINDS, AI_SITE_FREE_PAGES, AI_SITE_PLAN_MAX_TOKENS, aiFreeSiteSectionsWithin, aiSitePlanShapeRefusal } from "../model/ai-site-job.js";
|
|
24
|
+
import { aiPlanFailureCopy, aiPlanRetryRefusal } from "../model/ai-job-failure-copy.js";
|
|
25
|
+
import { FREE_AI_TASTE_CREDITS_PER_MONTH } from "../plan-entitlements.js";
|
|
24
26
|
import { aiDoctrineSystemBlocks, runValidatedGeneration } from "../runtime/ai-doctrine.js";
|
|
25
|
-
import { AI_STEP_TIERS } from "../providers/catalog.js";
|
|
26
|
-
import { aiPlanEmbedBriefViolations } from "../runtime/ai-doctrine-validators.js";
|
|
27
|
+
import { AI_STEP_TIERS, aiCatalogEntry, aiDefaultModelFor } from "../providers/catalog.js";
|
|
28
|
+
import { AI_FREE_PAGE_WORST_CASE_CREDITS, aiPlanEmbedBriefViolations } from "../runtime/ai-doctrine-validators.js";
|
|
29
|
+
import { assistCreditsFromUsd } from "../usage/assist-credits.js";
|
|
30
|
+
import { ASSIST_RETURNED_USD_FIELD, assistSpendAfterReturnsUsd } from "../usage/assist-credit-returns.js";
|
|
31
|
+
import { freeAccountUsageRef, freeAssistAccount } from "../usage/assist-free-taste.js";
|
|
27
32
|
import { AI_ROUTING_TABLE, aiModelForStep } from "../providers/routing.js";
|
|
28
33
|
import { readSiteInventory } from "../runtime/site-inventory.js";
|
|
29
34
|
import { aiJobAdmissionRefusal } from "./ai-job-admission.js";
|
|
@@ -95,7 +100,7 @@ import { AI_JOBS_COLLECTION, registerAiJobPlanStep } from "./ai-jobs.js";
|
|
|
95
100
|
* inputs and, where the job read them, what it may create on its site
|
|
96
101
|
* (AGL-3030). Per workspace and per site, which is why they ride here and
|
|
97
102
|
* never in a system block the platform's cache entry is keyed on.
|
|
98
|
-
*/ export function aiJobPlanPrompt(job, capabilities = null) {
|
|
103
|
+
*/ export function aiJobPlanPrompt(job, capabilities = null, inventory = null) {
|
|
99
104
|
var _job_inputs;
|
|
100
105
|
const lines = [
|
|
101
106
|
`Job kind: ${job.kind}`,
|
|
@@ -108,8 +113,46 @@ import { AI_JOBS_COLLECTION, registerAiJobPlanStep } from "./ai-jobs.js";
|
|
|
108
113
|
}
|
|
109
114
|
if (capabilities) lines.push(...aiPlanCapabilityLines(capabilities));
|
|
110
115
|
lines.push(...aiPlanTemplateTokenLines(job));
|
|
116
|
+
lines.push(...aiPlanSiteLines(job, inventory, capabilities));
|
|
111
117
|
return lines.join('\n');
|
|
112
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* What a SITE job's plan is told beside the inventory (AGL-3594): the pages
|
|
121
|
+
* the site already has and that their addresses are taken, which one — the
|
|
122
|
+
* untouched starter home — a planned home page at `/` replaces, the Free page
|
|
123
|
+
* cap and its guidance, and that a plan is an outline.
|
|
124
|
+
*
|
|
125
|
+
* A site job only. A new site is born with a published home page at `/`
|
|
126
|
+
* (AGL-3497), and a plan that did not know it could replace it planned its own
|
|
127
|
+
* home there and was refused for the clash on every new site. Kept off a page
|
|
128
|
+
* job's turn, whose length the Free page's wall is proven at.
|
|
129
|
+
*/ export function aiPlanSiteLines(job, inventory, capabilities) {
|
|
130
|
+
var _ref;
|
|
131
|
+
if (job.kind !== 'site') return [];
|
|
132
|
+
const lines = [];
|
|
133
|
+
const pages = ((_ref = inventory == null ? void 0 : inventory.screens) != null ? _ref : []).filter((screen)=>!screen.template);
|
|
134
|
+
const starter = pages.find((screen)=>screen.replaceable);
|
|
135
|
+
if (pages.length) {
|
|
136
|
+
lines.push('Pages the site already has; their addresses are taken unless a line says otherwise:');
|
|
137
|
+
for (const page of pages){
|
|
138
|
+
lines.push(page.replaceable ? `- ${page.name} at ${page.slug}: the starter home page the site was created with. Plan this site's home page at / and it replaces this one.` : `- ${page.name} at ${page.slug}`);
|
|
139
|
+
}
|
|
140
|
+
if (!starter && pages.some((page)=>page.slug.trim() === '/' || page.slug.trim() === '')) {
|
|
141
|
+
lines.push("The home page at / is the owner's own and stays: give every page you plan another address.");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const cap = capabilities == null ? void 0 : capabilities.freeSitePages;
|
|
145
|
+
if (cap !== undefined) {
|
|
146
|
+
var _ref1;
|
|
147
|
+
const sections = aiFreeSiteSectionsWithin({
|
|
148
|
+
layouts: ((_ref1 = inventory == null ? void 0 : inventory.layouts.length) != null ? _ref1 : 0) ? 0 : 1,
|
|
149
|
+
pages: cap
|
|
150
|
+
}, FREE_AI_TASTE_CREDITS_PER_MONTH);
|
|
151
|
+
lines.push(`This is a Free workspace: plan at most ${cap} ${cap === 1 ? 'page' : 'pages'} — the home page at / and the one page the brief most needs, such as services, booking or contact — with at most ${sections} sections across them. Put the contact form on one of them.`);
|
|
152
|
+
}
|
|
153
|
+
lines.push("Keep the plan an outline: each page's title, address, a short search title and description, and its sections named in a few words. The build writes the copy.");
|
|
154
|
+
return lines;
|
|
155
|
+
}
|
|
113
156
|
/**
|
|
114
157
|
* What a TEMPLATE job's plan is told its creation's `fields` are (AGL-3143
|
|
115
158
|
* §11): the subject's binding tokens, listed so the planner promises only
|
|
@@ -167,10 +210,12 @@ import { AI_JOBS_COLLECTION, registerAiJobPlanStep } from "./ai-jobs.js";
|
|
|
167
210
|
* violations the plan's one re-ask names: the players the plan lists against
|
|
168
211
|
* the brief it answers (AGL-3433), which only this step reads beside the plan,
|
|
169
212
|
* and a kind's shape refusal.
|
|
170
|
-
*/ function planViolations(scope, brief) {
|
|
213
|
+
*/ function planViolations(scope, brief, freeTaste = false) {
|
|
171
214
|
return (plan)=>{
|
|
172
215
|
var _ref;
|
|
173
|
-
const message = (_ref = scope == null ? void 0 : scope.shapeRefusal(plan
|
|
216
|
+
const message = (_ref = scope == null ? void 0 : scope.shapeRefusal(plan, {
|
|
217
|
+
freeTaste
|
|
218
|
+
})) != null ? _ref : null;
|
|
174
219
|
return [
|
|
175
220
|
...aiPlanEmbedBriefViolations(plan, brief),
|
|
176
221
|
...message ? [
|
|
@@ -350,14 +395,17 @@ export function createAiJobPlanStep(deps = {}) {
|
|
|
350
395
|
})
|
|
351
396
|
]);
|
|
352
397
|
const scope = (_AI_JOB_PLAN_SCOPES_job_kind = AI_JOB_PLAN_SCOPES[job.kind]) != null ? _AI_JOB_PLAN_SCOPES_job_kind : null;
|
|
353
|
-
const capabilities = workspace ? aiPlanCapabilitiesForJob(workspace, scope) : null;
|
|
398
|
+
const capabilities = aiSitePlanCapabilities(job, workspace ? aiPlanCapabilitiesForJob(workspace, scope) : null);
|
|
399
|
+
const freeTaste = (capabilities == null ? void 0 : capabilities.freeTaste) === true;
|
|
400
|
+
const site = job.kind === 'site';
|
|
354
401
|
// The model switch's answer for this job (AGL-2942): the creator's pick
|
|
355
402
|
// where the plan, the org restriction and the allotment allowlists allow
|
|
356
403
|
// it, and Auto held to those same lists otherwise. Without a resolver the
|
|
357
|
-
// doctrine asks the routing table itself.
|
|
358
|
-
|
|
404
|
+
// doctrine asks the routing table itself. A Free site plans on its
|
|
405
|
+
// provider's fast tier (AGL-3594), unless the creator picked a model.
|
|
406
|
+
const model = aiSitePlanModel(job, modelFor == null ? void 0 : modelFor('job.plan'), freeTaste);
|
|
359
407
|
const route = AI_ROUTING_TABLE['job.plan'];
|
|
360
|
-
const prompt = aiJobPlanPrompt(job, capabilities);
|
|
408
|
+
const prompt = aiJobPlanPrompt(job, capabilities, inventory);
|
|
361
409
|
/**
|
|
362
410
|
* The confirm door's answer for this plan, asked before the plan is kept
|
|
363
411
|
* (AGL-3030): the kind's own admission, handed the plan as the resume
|
|
@@ -452,9 +500,12 @@ export function createAiJobPlanStep(deps = {}) {
|
|
|
452
500
|
],
|
|
453
501
|
tool,
|
|
454
502
|
// The routing ceiling, lowered on a tier too slow to look records up,
|
|
455
|
-
// answer and ask again inside the least time the step registered
|
|
456
|
-
|
|
457
|
-
|
|
503
|
+
// answer and ask again inside the least time the step registered; a
|
|
504
|
+
// scaffold's outline is held to its own, far lower ceiling (AGL-3594).
|
|
505
|
+
maxTokens: aiSitePlanMaxTokens(job, resolved, freeTaste)
|
|
506
|
+
}, site ? {
|
|
507
|
+
thinking: 'off'
|
|
508
|
+
} : route.thinking ? {
|
|
458
509
|
thinking: route.thinking
|
|
459
510
|
} : {}, route.effort ? {
|
|
460
511
|
effort: route.effort
|
|
@@ -462,7 +513,7 @@ export function createAiJobPlanStep(deps = {}) {
|
|
|
462
513
|
signal
|
|
463
514
|
} : {}, {
|
|
464
515
|
capabilities,
|
|
465
|
-
extend: planViolations(scope, job.brief)
|
|
516
|
+
extend: planViolations(scope, job.brief, freeTaste)
|
|
466
517
|
}));
|
|
467
518
|
const spent = _extends({
|
|
468
519
|
outputs: [],
|
|
@@ -476,9 +527,34 @@ export function createAiJobPlanStep(deps = {}) {
|
|
|
476
527
|
if (result.status === 'refused') return _extends({}, spent, {
|
|
477
528
|
refused: true
|
|
478
529
|
});
|
|
479
|
-
if (result.status === 'needs_input')
|
|
480
|
-
|
|
481
|
-
|
|
530
|
+
if (result.status === 'needs_input') {
|
|
531
|
+
// Refused by our own checks, after the one re-ask (AGL-3594): the member
|
|
532
|
+
// reads a sentence and an action, staff the rules; the machine gives the
|
|
533
|
+
// credits back (`uncredited`), and on the Free taste Try again says when
|
|
534
|
+
// it cannot work. Whether the give-back was made is the job's to say
|
|
535
|
+
// (`refundedCredits`), so this sentence does not promise it.
|
|
536
|
+
const review = aiDoctrineReview(result);
|
|
537
|
+
const retryRefusal = freeTaste ? await aiFreePlanRetryRefusal({
|
|
538
|
+
job,
|
|
539
|
+
org,
|
|
540
|
+
firestore,
|
|
541
|
+
now,
|
|
542
|
+
site
|
|
543
|
+
}) : null;
|
|
544
|
+
return _extends({}, spent, {
|
|
545
|
+
uncredited: true,
|
|
546
|
+
review: _extends({}, review, {
|
|
547
|
+
message: aiPlanFailureCopy({
|
|
548
|
+
kind: job.kind,
|
|
549
|
+
codes: result.violations.map((violation)=>violation.code),
|
|
550
|
+
refunded: false
|
|
551
|
+
}),
|
|
552
|
+
detail: review.message
|
|
553
|
+
}, retryRefusal ? {
|
|
554
|
+
retryRefusal
|
|
555
|
+
} : {})
|
|
556
|
+
});
|
|
557
|
+
}
|
|
482
558
|
// Each draft the plan decides is named as the plan is kept, before anything is built (AGL-3079).
|
|
483
559
|
const plan = aiPlanWithDraftIds(job.kind, _extends({}, result.value, {
|
|
484
560
|
status: 'proposed',
|
|
@@ -499,6 +575,74 @@ export function createAiJobPlanStep(deps = {}) {
|
|
|
499
575
|
});
|
|
500
576
|
};
|
|
501
577
|
}
|
|
578
|
+
/**
|
|
579
|
+
* A site job's capabilities on the Free taste (AGL-3594): its plan holds at
|
|
580
|
+
* most the pages the member asked for within the Free band, which the Free
|
|
581
|
+
* wall then holds it to, and it changes no theme — the site was born with
|
|
582
|
+
* one (AGL-3497), and a palette pass is credits the two pages need. Every
|
|
583
|
+
* other job's capabilities pass through.
|
|
584
|
+
*/ export function aiSitePlanCapabilities(job, capabilities) {
|
|
585
|
+
var _job_inputs;
|
|
586
|
+
if (job.kind !== 'site' || !(capabilities == null ? void 0 : capabilities.freeTaste)) return capabilities;
|
|
587
|
+
const asked = Number(((_job_inputs = job.inputs) != null ? _job_inputs : {})['pages']);
|
|
588
|
+
const pages = Number.isInteger(asked) && asked >= AI_SITE_FREE_PAGES.min ? Math.min(asked, AI_SITE_FREE_PAGES.max) : AI_SITE_FREE_PAGES.max;
|
|
589
|
+
return _extends({}, capabilities, {
|
|
590
|
+
freeSitePages: pages,
|
|
591
|
+
create: _extends({}, capabilities.create, {
|
|
592
|
+
'theme-change': {
|
|
593
|
+
allowed: false,
|
|
594
|
+
left: null,
|
|
595
|
+
reason: "a Free workspace's site start keeps the theme the site was created with"
|
|
596
|
+
}
|
|
597
|
+
})
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* The model a plan runs on (AGL-3594): the resolved one, except a Free site
|
|
602
|
+
* plans on its provider's fast tier — an outline the plan rules hold and
|
|
603
|
+
* re-ask, at a fifth of the balanced tier's output rate. A model the creator
|
|
604
|
+
* picked by name stays theirs.
|
|
605
|
+
*/ export function aiSitePlanModel(job, resolved, freeTaste) {
|
|
606
|
+
var _ref;
|
|
607
|
+
if (job.kind !== 'site' || !freeTaste) return resolved;
|
|
608
|
+
const picked = typeof job.model === 'string' ? job.model.trim() : '';
|
|
609
|
+
if (picked && picked !== 'auto') return resolved;
|
|
610
|
+
const entry = aiCatalogEntry(resolved != null ? resolved : aiModelForStep('job.plan'));
|
|
611
|
+
return (_ref = entry ? aiDefaultModelFor(entry.provider, 'fast') : undefined) != null ? _ref : resolved;
|
|
612
|
+
}
|
|
613
|
+
/** The plan's answer ceiling: the step's own, and a scaffold's outline ceiling under it (AGL-3594). */ export function aiSitePlanMaxTokens(job, model, freeTaste) {
|
|
614
|
+
const ceiling = aiJobPlanMaxTokens(model);
|
|
615
|
+
if (job.kind !== 'site') return ceiling;
|
|
616
|
+
return Math.min(ceiling, freeTaste ? AI_SITE_PLAN_MAX_TOKENS.free : AI_SITE_PLAN_MAX_TOKENS.paid);
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Why trying a Free plan again cannot work this month, or `null` (AGL-3594):
|
|
620
|
+
* the owner's Free allowance left, read off the account month the meter
|
|
621
|
+
* writes net of give-backs, against what a plan of this kind costs at its
|
|
622
|
+
* worst. Read before this step is metered, and the machine gives a refused
|
|
623
|
+
* plan's credits back, so the figure read is the figure left. A read that fails
|
|
624
|
+
* leaves the button on: the reservation still refuses at the wall.
|
|
625
|
+
*/ async function aiFreePlanRetryRefusal(input) {
|
|
626
|
+
const account = freeAssistAccount(input.org);
|
|
627
|
+
if (!(account == null ? void 0 : account.accountUid)) return null;
|
|
628
|
+
try {
|
|
629
|
+
// The meter's month key, `YYYY-MM` in UTC (`assistUsageMonth`).
|
|
630
|
+
const month = input.now.toISOString().slice(0, 7);
|
|
631
|
+
const snapshot = await freeAccountUsageRef(input.firestore, account.accountUid, month).get();
|
|
632
|
+
const spent = assistCreditsFromUsd(assistSpendAfterReturnsUsd(snapshot.get('estCostUsd'), snapshot.get(ASSIST_RETURNED_USD_FIELD)));
|
|
633
|
+
return aiPlanRetryRefusal({
|
|
634
|
+
creditsLeft: FREE_AI_TASTE_CREDITS_PER_MONTH - spent,
|
|
635
|
+
planCredits: input.site ? AI_FREE_SITE_WORST_CASE_CREDITS.plan : AI_FREE_PAGE_WORST_CASE_CREDITS.plan
|
|
636
|
+
});
|
|
637
|
+
} catch (error) {
|
|
638
|
+
console.error('ai plan retry allowance read failed', {
|
|
639
|
+
orgId: input.job.orgId,
|
|
640
|
+
jobId: input.job.$id,
|
|
641
|
+
error
|
|
642
|
+
});
|
|
643
|
+
return null;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
502
646
|
export const runAiJobPlanStep = createAiJobPlanStep();
|
|
503
647
|
/**
|
|
504
648
|
* Registers the plan step every planned kind runs first, with the least time
|