@aglyn/plugins-ai 1.0.0-beta.237 → 1.0.0-beta.239
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/src/lib/components/ai-credits-prompt.component.d.ts +11 -4
- package/src/lib/components/ai-credits-prompt.component.js +84 -34
- package/src/lib/components/ai-credits-prompt.component.js.map +1 -1
- package/src/lib/components/ai-job-cancel.component.d.ts +38 -0
- package/src/lib/components/ai-job-cancel.component.js +156 -0
- package/src/lib/components/ai-job-cancel.component.js.map +1 -0
- package/src/lib/components/ai-job-links.js +8 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.js +12 -1
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-jobs-list-page.component.js +141 -72
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
- package/src/lib/components/ai-jobs-store.d.ts +12 -0
- package/src/lib/components/ai-jobs-store.js +15 -0
- package/src/lib/components/ai-jobs-store.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +22 -2
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-usage-meter-refresh.d.ts +35 -0
- package/src/lib/components/ai-usage-meter-refresh.js +63 -0
- package/src/lib/components/ai-usage-meter-refresh.js.map +1 -0
- package/src/lib/components/ai-usage-strip.component.d.ts +19 -2
- package/src/lib/components/ai-usage-strip.component.js +40 -9
- package/src/lib/components/ai-usage-strip.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +12 -16
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +91 -51
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/use-ai-usage-meter.d.ts +2 -0
- package/src/lib/components/use-ai-usage-meter.js +8 -3
- package/src/lib/components/use-ai-usage-meter.js.map +1 -1
- package/src/lib/jobs/ai-build-unit-outcome.d.ts +3 -1
- package/src/lib/jobs/ai-build-unit-outcome.js +19 -3
- package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +33 -3
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-cancel.d.ts +71 -0
- package/src/lib/jobs/ai-job-cancel.js +102 -0
- package/src/lib/jobs/ai-job-cancel.js.map +1 -0
- package/src/lib/jobs/ai-job-component-step.js +4 -1
- package/src/lib/jobs/ai-job-component-step.js.map +1 -1
- package/src/lib/jobs/ai-job-draft-ids.js +7 -2
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.js +2 -0
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-form-dataset.d.ts +100 -0
- package/src/lib/jobs/ai-job-form-dataset.js +161 -0
- package/src/lib/jobs/ai-job-form-dataset.js.map +1 -0
- package/src/lib/jobs/ai-job-form-step.d.ts +15 -3
- package/src/lib/jobs/ai-job-form-step.js +41 -10
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +2 -0
- package/src/lib/jobs/ai-job-layout-language.js +13 -1
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +6 -1
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +9 -0
- package/src/lib/jobs/ai-job-page-language.js +45 -2
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +12 -1
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +38 -1
- package/src/lib/jobs/ai-job-plan-step.js +90 -13
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +15 -0
- package/src/lib/jobs/ai-job-site-content.js +66 -7
- package/src/lib/jobs/ai-job-site-content.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +170 -0
- package/src/lib/jobs/ai-job-site-datasets.js +499 -0
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +15 -8
- package/src/lib/jobs/ai-job-site-step.js +152 -30
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-template-step.js +4 -1
- package/src/lib/jobs/ai-job-template-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +37 -12
- package/src/lib/jobs/ai-jobs.js +184 -28
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +143 -70
- package/src/lib/jobs/ai-layout-stock-photos.js +649 -176
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.d.ts +30 -0
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js +220 -0
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.d.ts +30 -0
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js +637 -0
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.d.ts +27 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js +151 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +16 -0
- package/src/lib/layout-language/ai-layout-compiler.js +397 -9
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-design.d.ts +6 -3
- package/src/lib/layout-language/ai-layout-design.js +5 -3
- package/src/lib/layout-language/ai-layout-design.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.d.ts +12 -0
- package/src/lib/layout-language/ai-layout-frame.js +58 -3
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-gaps.js +17 -1
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
- package/src/lib/layout-language/ai-layout-listings.d.ts +44 -3
- package/src/lib/layout-language/ai-layout-listings.js +118 -50
- package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +8 -0
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/layout-language/ai-layout-storefront.d.ts +82 -0
- package/src/lib/layout-language/ai-layout-storefront.js +223 -0
- package/src/lib/layout-language/ai-layout-storefront.js.map +1 -0
- package/src/lib/model/ai-build-job.d.ts +11 -1
- package/src/lib/model/ai-build-job.js +35 -6
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +23 -3
- package/src/lib/model/ai-build-plan.js +47 -6
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +2 -2
- package/src/lib/model/ai-build-progress.js +6 -4
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +69 -2
- package/src/lib/model/ai-credit-estimate.js +91 -3
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-job-cancel-copy.d.ts +44 -0
- package/src/lib/model/ai-job-cancel-copy.js +53 -0
- package/src/lib/model/ai-job-cancel-copy.js.map +1 -0
- package/src/lib/model/ai-job-failure-copy.d.ts +4 -1
- package/src/lib/model/ai-job-failure-copy.js +8 -3
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +20 -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.js +7 -4
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +38 -4
- package/src/lib/model/ai-site-job.js +50 -14
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.d.ts +4 -1
- package/src/lib/model/ai-site-kinds.js +34 -6
- package/src/lib/model/ai-site-kinds.js.map +1 -1
- package/src/lib/runtime/ai-dataset-generation.d.ts +77 -0
- package/src/lib/runtime/ai-dataset-generation.js +127 -0
- package/src/lib/runtime/ai-dataset-generation.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +77 -1
- package/src/lib/runtime/ai-doctrine-validators.js +280 -6
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +10 -4
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.d.ts +2 -0
- package/src/lib/runtime/ai-node-tree.js +16 -0
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +53 -7
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/server/ai-jobs-cancel.d.ts +13 -5
- package/src/lib/server/ai-jobs-cancel.js +27 -9
- package/src/lib/server/ai-jobs-cancel.js.map +1 -1
- package/src/lib/server/ai-jobs-gate.d.ts +12 -2
- package/src/lib/server/ai-jobs-gate.js +20 -9
- package/src/lib/server/ai-jobs-gate.js.map +1 -1
- package/src/lib/tools/ai-dataset-tool.d.ts +68 -0
- package/src/lib/tools/ai-dataset-tool.js +231 -0
- package/src/lib/tools/ai-dataset-tool.js.map +1 -0
- package/src/lib/usage/ai-usage-meter.d.ts +2 -1
- package/src/lib/usage/ai-usage-meter.js +17 -1
- package/src/lib/usage/ai-usage-meter.js.map +1 -1
- package/src/lib/usage/ai-usage-wire.d.ts +6 -0
- package/src/lib/usage/ai-usage-wire.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +7 -0
- package/src/lib/usage/assist-usage.js +14 -4
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/free-ai-credits-left.d.ts +7 -0
- package/src/lib/usage/free-ai-credits-left.js +30 -4
- package/src/lib/usage/free-ai-credits-left.js.map +1 -1
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
*/ import { aiCatalogEntry } from "../providers/catalog.js";
|
|
17
17
|
import { publicAssistQuota } from "./assist-usage.js";
|
|
18
18
|
import { aiUsageMeterState } from "./ai-usage-wire.js";
|
|
19
|
+
import { assistCreditsFromUsd } from "./assist-credits.js";
|
|
20
|
+
import { FREE_AI_TASTE_CREDITS_PER_MONTH } from "../plan-entitlements.js";
|
|
19
21
|
/**
|
|
20
22
|
* The usage strip's envelope from what a door already holds (AGL-2942): the
|
|
21
23
|
* reservation it took and, once the provider answered, what the exchange
|
|
@@ -25,7 +27,8 @@ import { aiUsageMeterState } from "./ai-usage-wire.js";
|
|
|
25
27
|
* Every figure is credits. The pool comes through `publicAssistQuota`, the
|
|
26
28
|
* one projection allowed to turn our provider bill into a customer figure;
|
|
27
29
|
* the caller's month comes off the allotment gate, which the meter wrote in
|
|
28
|
-
* credits in the first place
|
|
30
|
+
* credits in the first place — or, on the Free plan, off the person's Free
|
|
31
|
+
* allowance the reservation read, net of give-backs (AGL-3722).
|
|
29
32
|
*
|
|
30
33
|
* `last` is added to both months, because the reservation measured them
|
|
31
34
|
* BEFORE this exchange and the strip is read after it.
|
|
@@ -50,11 +53,24 @@ import { aiUsageMeterState } from "./ai-usage-wire.js";
|
|
|
50
53
|
}
|
|
51
54
|
const gate = (_reservation_allotment = reservation.allotment) != null ? _reservation_allotment : null;
|
|
52
55
|
const binding = (_ref = gate == null ? void 0 : gate.binding) != null ? _ref : null;
|
|
56
|
+
// On the Free plan the person's own line is their Free allowance (AGL-3722):
|
|
57
|
+
// 300 credits a month across every Free workspace they hold, net of
|
|
58
|
+
// give-backs — the wall the reservation refuses at. The workspace's
|
|
59
|
+
// per-member row it used to read is gross (a refund or a staff reset never
|
|
60
|
+
// reaches it) and this workspace's alone, so it said "You: 401 credits this
|
|
61
|
+
// month" to a person with 48 of their 300 left (2026-10-10).
|
|
62
|
+
const freeAccountUsd = reservation.free ? reservation.freeAccountCostUsd : null;
|
|
53
63
|
const mine = binding ? {
|
|
54
64
|
used: binding.used + spent,
|
|
55
65
|
limit: binding.credits,
|
|
56
66
|
mode: binding.mode,
|
|
57
67
|
scope: binding.scope
|
|
68
|
+
} : typeof freeAccountUsd === 'number' && Number.isFinite(freeAccountUsd) ? {
|
|
69
|
+
used: assistCreditsFromUsd(freeAccountUsd) + spent,
|
|
70
|
+
limit: FREE_AI_TASTE_CREDITS_PER_MONTH,
|
|
71
|
+
mode: 'hard',
|
|
72
|
+
scope: null,
|
|
73
|
+
free: true
|
|
58
74
|
} : {
|
|
59
75
|
used: ((_ref1 = gate == null ? void 0 : gate.personalCredits) != null ? _ref1 : 0) + spent,
|
|
60
76
|
limit: null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/usage/ai-usage-meter.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 { aiCatalogEntry } from '../providers/catalog'\nimport { publicAssistQuota, type AssistReservation } from './assist-usage'\nimport { aiUsageMeterState, type AiUsageMeterWire } from './ai-usage-wire'\n\n/**\n * The usage strip's envelope from what a door already holds (AGL-2942): the\n * reservation it took and, once the provider answered, what the exchange\n * cost. No read — the reservation measured the pool and the caller's month\n * inside the transaction it already runs.\n *\n * Every figure is credits. The pool comes through `publicAssistQuota`, the\n * one projection allowed to turn our provider bill into a customer figure;\n * the caller's month comes off the allotment gate, which the meter wrote in\n * credits in the first place.\n *\n * `last` is added to both months, because the reservation measured them\n * BEFORE this exchange and the strip is read after it.\n */\nexport function aiUsageMeter(\n reservation: Pick<AssistReservation, 'monthKey' | 'allowed'> &\n Partial<AssistReservation>,\n extra: {\n lastCredits?: number | null\n model?: { id: string; auto: boolean } | null\n } = {},\n): AiUsageMeterWire {\n const last =\n typeof extra.lastCredits === 'number' && Number.isFinite(extra.lastCredits)\n ? Math.max(0, Math.ceil(extra.lastCredits))\n : null\n const spent = last ?? 0\n let pool: AiUsageMeterWire['pool'] = { used: spent, limit: null }\n try {\n const credits = publicAssistQuota(reservation as AssistReservation)?.credits\n if (credits) pool = { used: credits.used + spent, limit: credits.limit }\n } catch {\n // A reservation without its dollar figures (a double, a refusal taken\n // before the month was read) reports the exchange alone.\n }\n const gate = reservation.allotment ?? null\n const binding = gate?.binding ?? null\n const mine: AiUsageMeterWire['mine'] = binding\n ? {\n used: binding.used + spent,\n limit: binding.credits,\n mode: binding.mode,\n scope: binding.scope,\n }\n : {\n used: (gate?.personalCredits ?? 0) + spent,\n
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/usage/ai-usage-meter.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 { aiCatalogEntry } from '../providers/catalog'\nimport { publicAssistQuota, type AssistReservation } from './assist-usage'\nimport { aiUsageMeterState, type AiUsageMeterWire } from './ai-usage-wire'\nimport { assistCreditsFromUsd } from './assist-credits'\nimport { FREE_AI_TASTE_CREDITS_PER_MONTH } from '../plan-entitlements'\n\n/**\n * The usage strip's envelope from what a door already holds (AGL-2942): the\n * reservation it took and, once the provider answered, what the exchange\n * cost. No read — the reservation measured the pool and the caller's month\n * inside the transaction it already runs.\n *\n * Every figure is credits. The pool comes through `publicAssistQuota`, the\n * one projection allowed to turn our provider bill into a customer figure;\n * the caller's month comes off the allotment gate, which the meter wrote in\n * credits in the first place — or, on the Free plan, off the person's Free\n * allowance the reservation read, net of give-backs (AGL-3722).\n *\n * `last` is added to both months, because the reservation measured them\n * BEFORE this exchange and the strip is read after it.\n */\nexport function aiUsageMeter(\n reservation: Pick<AssistReservation, 'monthKey' | 'allowed'> &\n Partial<AssistReservation>,\n extra: {\n lastCredits?: number | null\n model?: { id: string; auto: boolean } | null\n } = {},\n): AiUsageMeterWire {\n const last =\n typeof extra.lastCredits === 'number' && Number.isFinite(extra.lastCredits)\n ? Math.max(0, Math.ceil(extra.lastCredits))\n : null\n const spent = last ?? 0\n let pool: AiUsageMeterWire['pool'] = { used: spent, limit: null }\n try {\n const credits = publicAssistQuota(reservation as AssistReservation)?.credits\n if (credits) pool = { used: credits.used + spent, limit: credits.limit }\n } catch {\n // A reservation without its dollar figures (a double, a refusal taken\n // before the month was read) reports the exchange alone.\n }\n const gate = reservation.allotment ?? null\n const binding = gate?.binding ?? null\n // On the Free plan the person's own line is their Free allowance (AGL-3722):\n // 300 credits a month across every Free workspace they hold, net of\n // give-backs — the wall the reservation refuses at. The workspace's\n // per-member row it used to read is gross (a refund or a staff reset never\n // reaches it) and this workspace's alone, so it said \"You: 401 credits this\n // month\" to a person with 48 of their 300 left (2026-10-10).\n const freeAccountUsd = reservation.free ? reservation.freeAccountCostUsd : null\n const mine: AiUsageMeterWire['mine'] = binding\n ? {\n used: binding.used + spent,\n limit: binding.credits,\n mode: binding.mode,\n scope: binding.scope,\n }\n : typeof freeAccountUsd === 'number' && Number.isFinite(freeAccountUsd)\n ? {\n used: assistCreditsFromUsd(freeAccountUsd) + spent,\n limit: FREE_AI_TASTE_CREDITS_PER_MONTH,\n mode: 'hard',\n scope: null,\n free: true,\n }\n : {\n used: (gate?.personalCredits ?? 0) + spent,\n limit: null,\n mode: null,\n scope: null,\n }\n const refused = !reservation.allowed\n const entry = extra.model ? aiCatalogEntry(extra.model.id) : undefined\n return {\n month: reservation.monthKey,\n pool,\n mine,\n last,\n refused,\n state: aiUsageMeterState({ pool, mine, refused }),\n model:\n extra.model && entry\n ? { id: entry.id, label: entry.label, auto: extra.model.auto }\n : null,\n }\n}\n"],"names":["aiCatalogEntry","publicAssistQuota","aiUsageMeterState","assistCreditsFromUsd","FREE_AI_TASTE_CREDITS_PER_MONTH","aiUsageMeter","reservation","extra","last","lastCredits","Number","isFinite","Math","max","ceil","spent","pool","used","limit","credits","gate","allotment","binding","freeAccountUsd","free","freeAccountCostUsd","mine","mode","scope","personalCredits","refused","allowed","entry","model","id","undefined","month","monthKey","state","label","auto"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,cAAc,QAAQ,0BAAsB;AACrD,SAASC,iBAAiB,QAAgC,oBAAgB;AAC1E,SAASC,iBAAiB,QAA+B,qBAAiB;AAC1E,SAASC,oBAAoB,QAAQ,sBAAkB;AACvD,SAASC,+BAA+B,QAAQ,0BAAsB;AAEtE;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASC,aACdC,WAC4B,EAC5BC,QAGI,CAAC,CAAC;QAeOD;IAbb,MAAME,OACJ,OAAOD,MAAME,WAAW,KAAK,YAAYC,OAAOC,QAAQ,CAACJ,MAAME,WAAW,IACtEG,KAAKC,GAAG,CAAC,GAAGD,KAAKE,IAAI,CAACP,MAAME,WAAW,KACvC;IACN,MAAMM,QAAQP,eAAAA,OAAQ;IACtB,IAAIQ,OAAiC;QAAEC,MAAMF;QAAOG,OAAO;IAAK;IAChE,IAAI;YACcjB;QAAhB,MAAMkB,WAAUlB,qBAAAA,kBAAkBK,iCAAlBL,mBAAqDkB,OAAO;QAC5E,IAAIA,SAASH,OAAO;YAAEC,MAAME,QAAQF,IAAI,GAAGF;YAAOG,OAAOC,QAAQD,KAAK;QAAC;IACzE,EAAE,eAAM;IACN,sEAAsE;IACtE,yDAAyD;IAC3D;IACA,MAAME,QAAOd,yBAAAA,YAAYe,SAAS,YAArBf,yBAAyB;IACtC,MAAMgB,kBAAUF,wBAAAA,KAAME,OAAO,mBAAI;IACjC,6EAA6E;IAC7E,oEAAoE;IACpE,oEAAoE;IACpE,2EAA2E;IAC3E,4EAA4E;IAC5E,6DAA6D;IAC7D,MAAMC,iBAAiBjB,YAAYkB,IAAI,GAAGlB,YAAYmB,kBAAkB,GAAG;IAC3E,MAAMC,OAAiCJ,UACnC;QACEL,MAAMK,QAAQL,IAAI,GAAGF;QACrBG,OAAOI,QAAQH,OAAO;QACtBQ,MAAML,QAAQK,IAAI;QAClBC,OAAON,QAAQM,KAAK;IACtB,IACA,OAAOL,mBAAmB,YAAYb,OAAOC,QAAQ,CAACY,kBACpD;QACEN,MAAMd,qBAAqBoB,kBAAkBR;QAC7CG,OAAOd;QACPuB,MAAM;QACNC,OAAO;QACPJ,MAAM;IACR,IACA;QACEP,MAAM,UAACG,wBAAAA,KAAMS,eAAe,oBAAI,KAAKd;QACrCG,OAAO;QACPS,MAAM;QACNC,OAAO;IACT;IACN,MAAME,UAAU,CAACxB,YAAYyB,OAAO;IACpC,MAAMC,QAAQzB,MAAM0B,KAAK,GAAGjC,eAAeO,MAAM0B,KAAK,CAACC,EAAE,IAAIC;IAC7D,OAAO;QACLC,OAAO9B,YAAY+B,QAAQ;QAC3BrB;QACAU;QACAlB;QACAsB;QACAQ,OAAOpC,kBAAkB;YAAEc;YAAMU;YAAMI;QAAQ;QAC/CG,OACE1B,MAAM0B,KAAK,IAAID,QACX;YAAEE,IAAIF,MAAME,EAAE;YAAEK,OAAOP,MAAMO,KAAK;YAAEC,MAAMjC,MAAM0B,KAAK,CAACO,IAAI;QAAC,IAC3D;IACR;AACF"}
|
|
@@ -100,6 +100,12 @@ export interface AiUsageMeterWire {
|
|
|
100
100
|
limit: number | null;
|
|
101
101
|
mode: AiAllotmentMode | null;
|
|
102
102
|
scope: AiCreditAllotmentScope | null;
|
|
103
|
+
/**
|
|
104
|
+
* `mine` is the person's Free allowance (AGL-3722): `used` of the 300
|
|
105
|
+
* free credits a month every Free workspace they hold draws on, net of
|
|
106
|
+
* give-backs. Absent on a paid workspace.
|
|
107
|
+
*/
|
|
108
|
+
free?: boolean;
|
|
103
109
|
};
|
|
104
110
|
/** Credits the exchange this envelope answers cost; `null` when no model ran. */
|
|
105
111
|
last: number | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/usage/ai-usage-wire.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// By their own entry points rather than the barrel: the route that serves\n// this shape imports it on the server, where the whole library is dead weight.\nimport {\n topAiUsageKinds,\n type AiUsageByUserMonth,\n} from '../model/ai-usage-by-user'\nimport {\n AI_ALLOTMENT_WARN_SHARE,\n type AiAllotmentMode,\n type AiCreditAllotmentScope,\n} from '../model/ai-allotments'\nimport { csvCell } from '@aglyn/aglyn/app-utils/csv-import'\n\n/**\n * What `/api/ai/usage` answers (AGL-2928), shared by the route and the\n * console surfaces that read it — the Usage table, the roster column, the\n * site collaborators card and the member card. Dependency-free on purpose:\n * the route imports the Admin SDK and a client component may not.\n */\n\n/** Header naming the row count the export undertook to send. */\nexport const AI_USAGE_EXPORT_ROWS_HEADER = 'X-Aglyn-Export-Rows'\n\n/** A row as a customer sees it — credits and a share, never `estCostUsd`. */\nexport interface OrgAiUsageRowWire {\n uid: string\n /** The roster's name — display name, else email, else the uid. */\n name: string\n email: string | null\n role: string | null\n credits: number\n /** Of the workspace's measured spend for the month, in `[0, 1]`. */\n share: number\n requests: number\n refusals: number\n byKind: Record<string, number>\n byHost: Record<string, number>\n /** Credits on the site the request named, when it named one. */\n hostCredits?: number\n}\n\nexport interface OrgAiUsageTableWire {\n month: string\n /** The months a reader may ask for, newest first. */\n months: string[]\n /** The workspace's own credits for the month — the pool the shares are of. */\n orgCredits: number\n rows: OrgAiUsageRowWire[]\n}\n\n/** One person's month, as the member card reads it. */\nexport interface UserAiUsageMonthWire {\n month: string\n credits: number\n share: number\n requests: number\n refusals: number\n byKind: Record<string, number>\n byHost: Record<string, number>\n}\n\nexport interface UserAiUsageWire {\n uid: string\n /** Newest first. */\n months: UserAiUsageMonthWire[]\n}\n\n/** The roster's month, joined and shared, projected onto the wire. */\nexport function toAiUsageWireRow(\n row: AiUsageByUserMonth & {\n name: string\n email: string | null\n role: string | null\n share: number\n },\n hostId: string,\n): OrgAiUsageRowWire {\n const wire: OrgAiUsageRowWire = {\n uid: row.uid,\n name: row.name,\n email: row.email,\n role: row.role,\n credits: row.credits,\n share: row.share,\n requests: row.requests,\n refusals: row.refusals,\n byKind: row.byKind,\n byHost: row.byHost,\n }\n if (hostId) wire.hostCredits = row.byHost[hostId] ?? 0\n return wire\n}\n\n/** The CSV's columns, in order. */\nexport const AI_USAGE_CSV_HEADER = [\n 'Member',\n 'Email',\n 'Role',\n 'Credits',\n 'Share',\n 'Requests',\n 'Refusals',\n 'Top kinds',\n 'Sites',\n] as const\n\n/** A share as the table and the file print it: one decimal of a percent. */\nexport function formatAiUsageShare(share: number): string {\n return `${Math.round(share * 1000) / 10}%`\n}\n\nexport function aiUsageCsvLine(row: OrgAiUsageRowWire): string {\n const kinds = topAiUsageKinds(row.byKind, 3)\n .map((entry) => `${entry.kind} ${entry.credits}`)\n .join('; ')\n const sites = Object.entries(row.byHost)\n .sort((a, b) => b[1] - a[1])\n .map(([hostId, credits]) => `${hostId} ${credits}`)\n .join('; ')\n return [\n row.name,\n row.email ?? '',\n row.role ?? '',\n row.credits,\n formatAiUsageShare(row.share),\n row.requests,\n row.refusals,\n kinds,\n sites,\n ]\n .map(csvCell)\n .join(',')\n}\n\n/**\n * THE USAGE STRIP'S ENVELOPE (AGL-2942): what every AI door adds to the\n * answer it already sends — the chat door's `done` event, the copy\n * assistant's JSON, a job's create response, and each door's quota refusal —\n * so the strip in the panel and the generation dialogs updates with no read\n * of its own.\n *\n * Credits only. `pool` is the workspace's month against its band; `mine` is\n * the caller's own month, measured against the allotment that binds them\n * when one does — for a site allotment, the site's month, because that is\n * the line the next request meets.\n */\nexport type AiUsageMeterState = 'ok' | 'warn' | 'capped'\n\nexport interface AiUsageMeterWire {\n month: string\n pool: { used: number; limit: number | null }\n mine: {\n used: number\n limit: number | null\n mode: AiAllotmentMode | null\n scope: AiCreditAllotmentScope | null\n }\n /** Credits the exchange this envelope answers cost; `null` when no model ran. */\n last: number | null\n /** True when the envelope rides a refusal. */\n refused: boolean\n state: AiUsageMeterState\n /** The model that answered, as the catalog names it. */\n model: { id: string; label: string; auto: boolean } | null\n}\n\n/**\n * `capped` when a hard allotment is spent or the request was refused, `warn`\n * from 80% of the allotment that binds or of the pool, `ok` otherwise.\n */\nexport function aiUsageMeterState(\n meter: Pick<AiUsageMeterWire, 'pool' | 'mine' | 'refused'>,\n): AiUsageMeterState {\n const { mine, pool } = meter\n if (meter.refused) return 'capped'\n if (mine.limit !== null && mine.mode === 'hard' && mine.used >= mine.limit) return 'capped'\n const past = (used: number, limit: number | null) =>\n limit !== null && limit > 0 && used >= limit * AI_ALLOTMENT_WARN_SHARE\n return past(mine.used, mine.limit) || past(pool.used, pool.limit) ? 'warn' : 'ok'\n}\n\n/**\n * What `/api/ai/allotments` answers (AGL-2942): the allotments a reader may\n * see, each with what its subject drew this month, and the roster, sites\n * and models the editors pick from. Credits only.\n */\nexport interface AiAllotmentRowWire {\n subject: string\n scope: 'member' | 'collab' | 'host' | 'org'\n uid: string | null\n hostId: string | null\n credits: number | null\n mode: AiAllotmentMode\n models: string[] | null\n /** Credits the subject drew this month — the figure the gate measures. */\n used: number\n}\n\nexport interface AiAllotmentsMemberWire {\n uid: string\n name: string\n email: string | null\n role: string | null\n /** False for a site collaborator, whose allotments are per site. */\n orgWide: boolean\n /** Credits this month, every site. */\n credits: number\n /** Credits this month by site. */\n byHost: Record<string, number>\n}\n\nexport interface AiAllotmentsHostWire {\n hostId: string\n name: string\n /** The site's credits this month, everyone on it. */\n credits: number\n}\n\nexport interface AiAllotmentsWire {\n /** The org the answer is about — resolved from the site when only one was named. */\n orgId: string\n month: string\n pool: { used: number; limit: number | null }\n allotments: AiAllotmentRowWire[]\n members: AiAllotmentsMemberWire[]\n hosts: AiAllotmentsHostWire[]\n /** The catalog models this deployment serves, for an allowlist. */\n models: Array<{ id: string; label: string; tier: string }>\n /** Whether the plan lets the workspace restrict models org-wide. */\n restrictionAvailable: boolean\n canEdit: {\n /** Members, sites and the restriction: `billing.manage`. */\n billing: boolean\n /** Collaborators on the site asked about: `billing.manage`, or its admin. */\n collaborators: boolean\n }\n /** Who is reading, so an editor can refuse to offer a self-raise. */\n callerUid: string\n}\n\n/** `2026-09` as a reader says it, on the UTC calendar the key is cut from. */\nexport function aiUsageMonthLabel(month: string): string {\n const [year, monthOfYear] = month.split('-').map(Number)\n if (!year || !monthOfYear) return month\n try {\n return new Date(Date.UTC(year, monthOfYear - 1, 1)).toLocaleDateString(undefined, {\n month: 'long',\n year: 'numeric',\n timeZone: 'UTC',\n })\n } catch {\n return month\n }\n}\n"],"names":["topAiUsageKinds","AI_ALLOTMENT_WARN_SHARE","csvCell","AI_USAGE_EXPORT_ROWS_HEADER","toAiUsageWireRow","row","hostId","wire","uid","name","email","role","credits","share","requests","refusals","byKind","byHost","hostCredits","AI_USAGE_CSV_HEADER","formatAiUsageShare","Math","round","aiUsageCsvLine","kinds","map","entry","kind","join","sites","Object","entries","sort","a","b","aiUsageMeterState","meter","mine","pool","refused","limit","mode","used","past","aiUsageMonthLabel","month","year","monthOfYear","split","Number","Date","UTC","toLocaleDateString","undefined","timeZone"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/usage/ai-usage-wire.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// By their own entry points rather than the barrel: the route that serves\n// this shape imports it on the server, where the whole library is dead weight.\nimport {\n topAiUsageKinds,\n type AiUsageByUserMonth,\n} from '../model/ai-usage-by-user'\nimport {\n AI_ALLOTMENT_WARN_SHARE,\n type AiAllotmentMode,\n type AiCreditAllotmentScope,\n} from '../model/ai-allotments'\nimport { csvCell } from '@aglyn/aglyn/app-utils/csv-import'\n\n/**\n * What `/api/ai/usage` answers (AGL-2928), shared by the route and the\n * console surfaces that read it — the Usage table, the roster column, the\n * site collaborators card and the member card. Dependency-free on purpose:\n * the route imports the Admin SDK and a client component may not.\n */\n\n/** Header naming the row count the export undertook to send. */\nexport const AI_USAGE_EXPORT_ROWS_HEADER = 'X-Aglyn-Export-Rows'\n\n/** A row as a customer sees it — credits and a share, never `estCostUsd`. */\nexport interface OrgAiUsageRowWire {\n uid: string\n /** The roster's name — display name, else email, else the uid. */\n name: string\n email: string | null\n role: string | null\n credits: number\n /** Of the workspace's measured spend for the month, in `[0, 1]`. */\n share: number\n requests: number\n refusals: number\n byKind: Record<string, number>\n byHost: Record<string, number>\n /** Credits on the site the request named, when it named one. */\n hostCredits?: number\n}\n\nexport interface OrgAiUsageTableWire {\n month: string\n /** The months a reader may ask for, newest first. */\n months: string[]\n /** The workspace's own credits for the month — the pool the shares are of. */\n orgCredits: number\n rows: OrgAiUsageRowWire[]\n}\n\n/** One person's month, as the member card reads it. */\nexport interface UserAiUsageMonthWire {\n month: string\n credits: number\n share: number\n requests: number\n refusals: number\n byKind: Record<string, number>\n byHost: Record<string, number>\n}\n\nexport interface UserAiUsageWire {\n uid: string\n /** Newest first. */\n months: UserAiUsageMonthWire[]\n}\n\n/** The roster's month, joined and shared, projected onto the wire. */\nexport function toAiUsageWireRow(\n row: AiUsageByUserMonth & {\n name: string\n email: string | null\n role: string | null\n share: number\n },\n hostId: string,\n): OrgAiUsageRowWire {\n const wire: OrgAiUsageRowWire = {\n uid: row.uid,\n name: row.name,\n email: row.email,\n role: row.role,\n credits: row.credits,\n share: row.share,\n requests: row.requests,\n refusals: row.refusals,\n byKind: row.byKind,\n byHost: row.byHost,\n }\n if (hostId) wire.hostCredits = row.byHost[hostId] ?? 0\n return wire\n}\n\n/** The CSV's columns, in order. */\nexport const AI_USAGE_CSV_HEADER = [\n 'Member',\n 'Email',\n 'Role',\n 'Credits',\n 'Share',\n 'Requests',\n 'Refusals',\n 'Top kinds',\n 'Sites',\n] as const\n\n/** A share as the table and the file print it: one decimal of a percent. */\nexport function formatAiUsageShare(share: number): string {\n return `${Math.round(share * 1000) / 10}%`\n}\n\nexport function aiUsageCsvLine(row: OrgAiUsageRowWire): string {\n const kinds = topAiUsageKinds(row.byKind, 3)\n .map((entry) => `${entry.kind} ${entry.credits}`)\n .join('; ')\n const sites = Object.entries(row.byHost)\n .sort((a, b) => b[1] - a[1])\n .map(([hostId, credits]) => `${hostId} ${credits}`)\n .join('; ')\n return [\n row.name,\n row.email ?? '',\n row.role ?? '',\n row.credits,\n formatAiUsageShare(row.share),\n row.requests,\n row.refusals,\n kinds,\n sites,\n ]\n .map(csvCell)\n .join(',')\n}\n\n/**\n * THE USAGE STRIP'S ENVELOPE (AGL-2942): what every AI door adds to the\n * answer it already sends — the chat door's `done` event, the copy\n * assistant's JSON, a job's create response, and each door's quota refusal —\n * so the strip in the panel and the generation dialogs updates with no read\n * of its own.\n *\n * Credits only. `pool` is the workspace's month against its band; `mine` is\n * the caller's own month, measured against the allotment that binds them\n * when one does — for a site allotment, the site's month, because that is\n * the line the next request meets.\n */\nexport type AiUsageMeterState = 'ok' | 'warn' | 'capped'\n\nexport interface AiUsageMeterWire {\n month: string\n pool: { used: number; limit: number | null }\n mine: {\n used: number\n limit: number | null\n mode: AiAllotmentMode | null\n scope: AiCreditAllotmentScope | null\n /**\n * `mine` is the person's Free allowance (AGL-3722): `used` of the 300\n * free credits a month every Free workspace they hold draws on, net of\n * give-backs. Absent on a paid workspace.\n */\n free?: boolean\n }\n /** Credits the exchange this envelope answers cost; `null` when no model ran. */\n last: number | null\n /** True when the envelope rides a refusal. */\n refused: boolean\n state: AiUsageMeterState\n /** The model that answered, as the catalog names it. */\n model: { id: string; label: string; auto: boolean } | null\n}\n\n/**\n * `capped` when a hard allotment is spent or the request was refused, `warn`\n * from 80% of the allotment that binds or of the pool, `ok` otherwise.\n */\nexport function aiUsageMeterState(\n meter: Pick<AiUsageMeterWire, 'pool' | 'mine' | 'refused'>,\n): AiUsageMeterState {\n const { mine, pool } = meter\n if (meter.refused) return 'capped'\n if (mine.limit !== null && mine.mode === 'hard' && mine.used >= mine.limit) return 'capped'\n const past = (used: number, limit: number | null) =>\n limit !== null && limit > 0 && used >= limit * AI_ALLOTMENT_WARN_SHARE\n return past(mine.used, mine.limit) || past(pool.used, pool.limit) ? 'warn' : 'ok'\n}\n\n/**\n * What `/api/ai/allotments` answers (AGL-2942): the allotments a reader may\n * see, each with what its subject drew this month, and the roster, sites\n * and models the editors pick from. Credits only.\n */\nexport interface AiAllotmentRowWire {\n subject: string\n scope: 'member' | 'collab' | 'host' | 'org'\n uid: string | null\n hostId: string | null\n credits: number | null\n mode: AiAllotmentMode\n models: string[] | null\n /** Credits the subject drew this month — the figure the gate measures. */\n used: number\n}\n\nexport interface AiAllotmentsMemberWire {\n uid: string\n name: string\n email: string | null\n role: string | null\n /** False for a site collaborator, whose allotments are per site. */\n orgWide: boolean\n /** Credits this month, every site. */\n credits: number\n /** Credits this month by site. */\n byHost: Record<string, number>\n}\n\nexport interface AiAllotmentsHostWire {\n hostId: string\n name: string\n /** The site's credits this month, everyone on it. */\n credits: number\n}\n\nexport interface AiAllotmentsWire {\n /** The org the answer is about — resolved from the site when only one was named. */\n orgId: string\n month: string\n pool: { used: number; limit: number | null }\n allotments: AiAllotmentRowWire[]\n members: AiAllotmentsMemberWire[]\n hosts: AiAllotmentsHostWire[]\n /** The catalog models this deployment serves, for an allowlist. */\n models: Array<{ id: string; label: string; tier: string }>\n /** Whether the plan lets the workspace restrict models org-wide. */\n restrictionAvailable: boolean\n canEdit: {\n /** Members, sites and the restriction: `billing.manage`. */\n billing: boolean\n /** Collaborators on the site asked about: `billing.manage`, or its admin. */\n collaborators: boolean\n }\n /** Who is reading, so an editor can refuse to offer a self-raise. */\n callerUid: string\n}\n\n/** `2026-09` as a reader says it, on the UTC calendar the key is cut from. */\nexport function aiUsageMonthLabel(month: string): string {\n const [year, monthOfYear] = month.split('-').map(Number)\n if (!year || !monthOfYear) return month\n try {\n return new Date(Date.UTC(year, monthOfYear - 1, 1)).toLocaleDateString(undefined, {\n month: 'long',\n year: 'numeric',\n timeZone: 'UTC',\n })\n } catch {\n return month\n }\n}\n"],"names":["topAiUsageKinds","AI_ALLOTMENT_WARN_SHARE","csvCell","AI_USAGE_EXPORT_ROWS_HEADER","toAiUsageWireRow","row","hostId","wire","uid","name","email","role","credits","share","requests","refusals","byKind","byHost","hostCredits","AI_USAGE_CSV_HEADER","formatAiUsageShare","Math","round","aiUsageCsvLine","kinds","map","entry","kind","join","sites","Object","entries","sort","a","b","aiUsageMeterState","meter","mine","pool","refused","limit","mode","used","past","aiUsageMonthLabel","month","year","monthOfYear","split","Number","Date","UTC","toLocaleDateString","undefined","timeZone"],"mappings":"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"}
|
|
@@ -269,6 +269,13 @@ export interface AssistReservation extends AssistQuotaVerdict {
|
|
|
269
269
|
* on, and the record must charge that account and no other.
|
|
270
270
|
*/
|
|
271
271
|
free: FreeAssistAccount | null;
|
|
272
|
+
/**
|
|
273
|
+
* The Free account's month in billed USD, net of give-backs, as read inside
|
|
274
|
+
* the transaction (AGL-3722) — the allowance every Free workspace of the
|
|
275
|
+
* person draws on. `null` for a paid workspace and for a reservation that
|
|
276
|
+
* did not read it. Optional so a double without it reads as not read.
|
|
277
|
+
*/
|
|
278
|
+
freeAccountCostUsd?: number | null;
|
|
272
279
|
/**
|
|
273
280
|
* Measured provider spend for `monthKey` in USD as read inside the
|
|
274
281
|
* transaction, or `null` when the transaction did not consult it.
|
|
@@ -414,6 +414,9 @@ async function reserveInTransaction(firestore, orgId, entitled, now, org, subjec
|
|
|
414
414
|
// Net of any credits given back this month (AGL-3595), so a give-back
|
|
415
415
|
// re-opens the band it was meant to re-open.
|
|
416
416
|
const costUsd = monthlySnapshot ? assistSpendAfterReturnsUsd(monthlySnapshot.get('estCostUsd'), monthlySnapshot.get(ASSIST_RETURNED_USD_FIELD)) : null;
|
|
417
|
+
// The person's Free allowance, net of give-backs (AGL-3722): what the
|
|
418
|
+
// usage strip says is theirs, never the workspace's gross per-member row.
|
|
419
|
+
const freeAccountCostUsd = accountSnapshot ? assistSpendAfterReturnsUsd(accountSnapshot.get('estCostUsd'), accountSnapshot.get(ASSIST_RETURNED_USD_FIELD)) : null;
|
|
417
420
|
if (!(used < limit)) {
|
|
418
421
|
recordAssistRefusal(firestore, orgId, month, 'messages');
|
|
419
422
|
return {
|
|
@@ -428,7 +431,8 @@ async function reserveInTransaction(firestore, orgId, entitled, now, org, subjec
|
|
|
428
431
|
costUsd,
|
|
429
432
|
costLimitUsd,
|
|
430
433
|
budgetUsd,
|
|
431
|
-
free
|
|
434
|
+
free,
|
|
435
|
+
freeAccountCostUsd
|
|
432
436
|
};
|
|
433
437
|
}
|
|
434
438
|
// The dollar ceiling, checked AFTER the message cap so the cheaper and
|
|
@@ -449,7 +453,8 @@ async function reserveInTransaction(firestore, orgId, entitled, now, org, subjec
|
|
|
449
453
|
costUsd,
|
|
450
454
|
costLimitUsd,
|
|
451
455
|
budgetUsd,
|
|
452
|
-
free
|
|
456
|
+
free,
|
|
457
|
+
freeAccountCostUsd
|
|
453
458
|
};
|
|
454
459
|
}
|
|
455
460
|
// The org's ceiling on its OVERAGE (AGL-2898), checked after the band
|
|
@@ -478,7 +483,8 @@ async function reserveInTransaction(firestore, orgId, entitled, now, org, subjec
|
|
|
478
483
|
costUsd,
|
|
479
484
|
costLimitUsd,
|
|
480
485
|
budgetUsd,
|
|
481
|
-
free
|
|
486
|
+
free,
|
|
487
|
+
freeAccountCostUsd
|
|
482
488
|
};
|
|
483
489
|
}
|
|
484
490
|
// AGLYN'S OWN OVERAGE GUARDS (AGL-3011), after the workspace's own
|
|
@@ -518,7 +524,8 @@ async function reserveInTransaction(firestore, orgId, entitled, now, org, subjec
|
|
|
518
524
|
costUsd,
|
|
519
525
|
costLimitUsd,
|
|
520
526
|
budgetUsd,
|
|
521
|
-
free
|
|
527
|
+
free,
|
|
528
|
+
freeAccountCostUsd
|
|
522
529
|
};
|
|
523
530
|
}
|
|
524
531
|
// The allotments that apply to the person asking (AGL-2942): read only
|
|
@@ -541,6 +548,7 @@ async function reserveInTransaction(firestore, orgId, entitled, now, org, subjec
|
|
|
541
548
|
costLimitUsd,
|
|
542
549
|
budgetUsd,
|
|
543
550
|
free,
|
|
551
|
+
freeAccountCostUsd,
|
|
544
552
|
allotment
|
|
545
553
|
};
|
|
546
554
|
}
|
|
@@ -563,6 +571,7 @@ async function reserveInTransaction(firestore, orgId, entitled, now, org, subjec
|
|
|
563
571
|
costLimitUsd,
|
|
564
572
|
budgetUsd,
|
|
565
573
|
free,
|
|
574
|
+
freeAccountCostUsd,
|
|
566
575
|
allotment
|
|
567
576
|
};
|
|
568
577
|
}
|
|
@@ -602,6 +611,7 @@ async function reserveInTransaction(firestore, orgId, entitled, now, org, subjec
|
|
|
602
611
|
costLimitUsd,
|
|
603
612
|
budgetUsd,
|
|
604
613
|
free,
|
|
614
|
+
freeAccountCostUsd,
|
|
605
615
|
allotment
|
|
606
616
|
};
|
|
607
617
|
});
|