@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
|
@@ -145,7 +145,12 @@ export interface AiInsightDigestReport {
|
|
|
145
145
|
}
|
|
146
146
|
/** The week a Monday digest covers: the week the run falls in. */
|
|
147
147
|
export declare function aiInsightDigestWeek(now: Date): string;
|
|
148
|
-
/**
|
|
148
|
+
/**
|
|
149
|
+
* A digest email's words: the insights, the site's Analytics page, and where
|
|
150
|
+
* to turn it off. The digest is an AI job metered against the workspace, so
|
|
151
|
+
* it says what it drew, and it names the workspace a member of several asked
|
|
152
|
+
* for it in.
|
|
153
|
+
*/
|
|
149
154
|
export declare function aiInsightDigestEmailText(input: {
|
|
150
155
|
siteName: string;
|
|
151
156
|
productName: string;
|
|
@@ -155,6 +160,10 @@ export declare function aiInsightDigestEmailText(input: {
|
|
|
155
160
|
/** Absolute links, or `null` where no console origin is configured to build one from. */
|
|
156
161
|
analyticsUrl: string | null;
|
|
157
162
|
settingsUrl: string | null;
|
|
163
|
+
/** The workspace the digest was asked for in; `''` falls back to "this workspace". */
|
|
164
|
+
workspaceName?: string;
|
|
165
|
+
/** The AI credits the digest's job drew; 0 or absent says nothing. */
|
|
166
|
+
creditsSpent?: number;
|
|
158
167
|
}): string;
|
|
159
168
|
/**
|
|
160
169
|
* One call of the sweep: delivery for this week and the last on the first
|
|
@@ -44,15 +44,25 @@ const str = (value)=>typeof value === 'string' ? value.trim() : '';
|
|
|
44
44
|
/** The week before, whose late digests a run still delivers. */ function previousWeek(now) {
|
|
45
45
|
return aiInsightIsoWeek(new Date(now.getTime() - 7 * 86400000));
|
|
46
46
|
}
|
|
47
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* A digest email's words: the insights, the site's Analytics page, and where
|
|
49
|
+
* to turn it off. The digest is an AI job metered against the workspace, so
|
|
50
|
+
* it says what it drew, and it names the workspace a member of several asked
|
|
51
|
+
* for it in.
|
|
52
|
+
*/ export function aiInsightDigestEmailText(input) {
|
|
53
|
+
var _input_workspaceName;
|
|
54
|
+
const workspace = ((_input_workspaceName = input.workspaceName) == null ? void 0 : _input_workspaceName.trim()) || '';
|
|
55
|
+
const credits = Math.max(0, Math.round(Number(input.creditsSpent) || 0));
|
|
56
|
+
const drew = credits ? ` Writing it used ${credits.toLocaleString('en-US')} of ` + `${workspace ? `${workspace}’s` : 'the workspace’s'} AI credits.` : '';
|
|
57
|
+
const forWhom = workspace || 'this workspace';
|
|
48
58
|
return [
|
|
49
|
-
`Here is what ${input.siteName}'s figures showed this week
|
|
59
|
+
`Here is what ${input.siteName}'s figures showed this week.${drew}`,
|
|
50
60
|
'',
|
|
51
61
|
...input.insights.map((insight, index)=>`${index + 1}. ${insight.text}`),
|
|
52
62
|
'',
|
|
53
63
|
input.analyticsUrl ? `Each of these is traced to the figures it cites. See them in ${input.productName}: ${input.analyticsUrl}` : `Each of these is traced to the figures it cites. See them on the site's Analytics page in ${input.productName}.`,
|
|
54
64
|
'',
|
|
55
|
-
input.settingsUrl ? `You asked for weekly insights for
|
|
65
|
+
input.settingsUrl ? `You asked for weekly insights for ${forWhom}. Turn them off in Notifications: ${input.settingsUrl}` : `You asked for weekly insights for ${forWhom}. Turn them off in your Notifications settings.`
|
|
56
66
|
].join('\n');
|
|
57
67
|
}
|
|
58
68
|
async function readUsers(firestore, uids) {
|
|
@@ -228,7 +238,9 @@ async function readUsers(firestore, uids) {
|
|
|
228
238
|
productName: branding.productName,
|
|
229
239
|
insights: record.insights,
|
|
230
240
|
analyticsUrl,
|
|
231
|
-
settingsUrl: origin ? `${origin}/manage/notifications` : null
|
|
241
|
+
settingsUrl: origin ? `${origin}/manage/notifications` : null,
|
|
242
|
+
workspaceName: str(org['name']),
|
|
243
|
+
creditsSpent: job.creditsSpent
|
|
232
244
|
}),
|
|
233
245
|
fromName: branding.fromName,
|
|
234
246
|
siteName: site.name,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/insights/ai-insight-digest.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 { FieldPath } from 'firebase-admin/firestore'\nimport { INSIGHT_DIGESTS_FIELD, insightDigestSubscribed, notificationMuted } from '@aglyn/aglyn/app-utils/notifications'\nimport { hostRoleFor } from '@aglyn/aglyn/app-utils/organizations'\nimport { checkEntitlement, resolveBrandingProfile, resolveEffectivePlan } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { isReleaseFlagOnForOrg, parseOrgReleaseFlagOverrides, type ReleaseFlagValue } from '@aglyn/aglyn/app-utils/release-flags'\nimport type { AglynOrgMember } from '@aglyn/aglyn/foundation/definitions/organization.types'\nimport {\n AI_INSIGHTS_COLLECTION,\n aiInsightIsoWeek,\n type AiInsightRecord,\n} from '../model/ai-insight'\nimport { isAiOnForSite } from '../model/ai-site-switch'\nimport { cancelAiJob, createAiJob, getAiJob } from '../jobs/ai-jobs'\n\n/**\n * THE WEEKLY INSIGHTS (AGL-2915): `POST /api/admin/ai-insights-digest`, twice\n * a day on the console's scheduler.\n *\n * ## Who asked for it\n *\n * A person turns weekly insights on for a workspace beside the answers\n * themselves (`users/{uid}.insightDigests.{orgId}`), and off there or in\n * Notifications. It is opt-in, because each digest is a generation the\n * workspace pays for. At the sweep, every rung is asked again rather than\n * trusted from the switch: the workspace's plan includes `aiGenerative`,\n * `release_ai_generative` is on for it, its AI is not paused, the site has AI\n * on, and the person still holds `ai.generate` on the site.\n *\n * ## Monday: one job per site\n *\n * On a Monday run, for each workspace with someone who asked, the sites those\n * people reach — the busiest `AI_INSIGHT_DIGEST_MAX_SITES` by yesterday's page\n * views — each get one `insight` job with the `digest` surface, created by the\n * first of them who may generate there. The job runs on the AI jobs beat like\n * any other: it reserves, meters and records its credits, and a workspace at\n * its band — a Free one that has used its credits — has its job parked by the\n * reservation like any other. `orgs/{orgId}/aiInsightDigests/{week}` records\n * the jobs; `aiInsightDigestQueue/{week}` lists the workspaces still owed a\n * delivery, so a run that delivers reads one document rather than every\n * workspace.\n *\n * ## Every run: deliver what is written\n *\n * A digest whose job is done and kept at least one insight is delivered once:\n * a `content.insightsDigest` notification (unless the person muted the\n * category) and an email through the platform's own sending path, to each\n * person who asked for the workspace's insights and reaches the site. A job\n * that failed, kept nothing, or parked for credits is SKIPPED SILENTLY: a\n * parked job is canceled rather than left to spend later, and nobody is told,\n * because nobody asked about that week in particular. A job still running\n * past `AI_INSIGHT_DIGEST_DEADLINE_MS` is canceled and skipped the same way.\n *\n * The email says only what the answer says: the insights, each traced to the\n * figures it cites, and a link to the site's Analytics page. It is sent by the\n * platform to a person who asked for it, never to a contact.\n */\n\ntype Firestore = FirebaseFirestore.Firestore\n\nexport const AI_INSIGHTS_DIGEST_CRON_ID = 'ai-insights-digest'\nexport const AI_INSIGHTS_DIGEST_PATH = 'admin/ai-insights-digest'\n\n/** Orgs one call reads; a chunk that times out never advances the cursor, so the safe size is one that finishes. */\nexport const AI_INSIGHT_DIGEST_ORG_CHUNK = 25\n\n/** Sites one workspace's digest covers a week. */\nexport const AI_INSIGHT_DIGEST_MAX_SITES = 5\n\n/** Sites a workspace's sweep reads before it ranks them. */\nexport const AI_INSIGHT_DIGEST_HOST_READ_LIMIT = 50\n\n/** Workspaces one run delivers to, per week. */\nexport const AI_INSIGHT_DIGEST_DELIVER_LIMIT = 100\n\n/** How long a digest's job may take before its week is given up. */\nexport const AI_INSIGHT_DIGEST_DEADLINE_MS = 48 * 60 * 60_000\n\n/**\n * The time one call works before it stops and leaves the rest to the next:\n * inside the plugin dispatcher's 60 s function ceiling, with room for the\n * reads before the first check and the reply after the last. A call killed\n * mid-send could send one person a site's digest twice, because a send is\n * stamped after it goes.\n */\nexport const AI_INSIGHT_DIGEST_RUN_BUDGET_MS = 45_000\n\nexport const AI_INSIGHT_DIGEST_MARKERS = 'aiInsightDigests'\nexport const AI_INSIGHT_DIGEST_QUEUE = 'aiInsightDigestQueue'\n\nexport type AiInsightDigestSiteState = 'pending' | 'delivered' | 'skipped'\n\nexport interface AiInsightDigestSite {\n jobId: string\n name: string\n state: AiInsightDigestSiteState\n /**\n * The people already sent this site's digest, so a run the send rate\n * stopped never sends one twice. Dropped when the site settles, so a\n * settled marker names no person.\n */\n sent?: Record<string, boolean>\n}\n\nexport interface AiInsightDigestMarker {\n week: string\n createdAtMs: number\n sites: Record<string, AiInsightDigestSite>\n}\n\n/** What a send answered: whether it went, and whether the platform's send rate refused it for now. */\nexport interface AiInsightDigestSendResult {\n sent: boolean\n rateLimited: boolean\n}\n\n/** Everything the sweep reaches beyond Firestore, so a spec can hold each one. */\nexport interface AiInsightDigestDeps {\n firestore: Firestore\n now: Date\n flagValue: ReleaseFlagValue\n listMembers: (orgId: string) => Promise<AglynOrgMember[]>\n mayGenerate: (orgId: string, hostId: string, member: AglynOrgMember) => Promise<boolean>\n paused: (orgId: string) => Promise<boolean>\n notify: (uid: string, payload: { title: string; body: string; link: string; orgId: string; hostId: string }) => Promise<void>\n send: (email: {\n to: string\n subject: string\n text: string\n fromName: string\n /** The site the digest is about, and the org whose brand it wears (AGL-3367). */\n siteName: string\n org: Record<string, unknown>\n }) => Promise<AiInsightDigestSendResult>\n /** The console's absolute origin for an email link, or `''` when none is configured. */\n consoleOrigin: string\n /** The call's working time; `AI_INSIGHT_DIGEST_RUN_BUDGET_MS` unless a spec holds it. */\n budgetMs?: number\n}\n\nexport interface AiInsightDigestReport {\n week: string\n swept: number\n created: number\n delivered: number\n skipped: number\n /** The send rate refused or the call's time ran out; the run stops, and the next one delivers the rest. */\n deferred: boolean\n nextCursor: string | null\n done: boolean\n}\n\nconst str = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\n/** The week a Monday digest covers: the week the run falls in. */\nexport function aiInsightDigestWeek(now: Date): string {\n return aiInsightIsoWeek(now)\n}\n\n/** The week before, whose late digests a run still delivers. */\nfunction previousWeek(now: Date): string {\n return aiInsightIsoWeek(new Date(now.getTime() - 7 * 86_400_000))\n}\n\n/** A digest email's words: the insights, the site's Analytics page, and where to turn it off. */\nexport function aiInsightDigestEmailText(input: {\n siteName: string\n productName: string\n insights: ReadonlyArray<{ text: string }>\n /** Absolute links, or `null` where no console origin is configured to build one from. */\n analyticsUrl: string | null\n settingsUrl: string | null\n}): string {\n return [\n `Here is what ${input.siteName}'s figures showed this week.`,\n '',\n ...input.insights.map((insight, index) => `${index + 1}. ${insight.text}`),\n '',\n input.analyticsUrl\n ? `Each of these is traced to the figures it cites. See them in ${input.productName}: ${input.analyticsUrl}`\n : `Each of these is traced to the figures it cites. See them on the site's Analytics page in ${input.productName}.`,\n '',\n input.settingsUrl\n ? `You asked for weekly insights for this workspace. Turn them off in Notifications: ${input.settingsUrl}`\n : `You asked for weekly insights for this workspace. Turn them off in your Notifications settings.`,\n ].join('\\n')\n}\n\nasync function readUsers(firestore: Firestore, uids: readonly string[]) {\n if (!uids.length) return new Map<string, FirebaseFirestore.DocumentSnapshot>()\n const docs = await firestore.getAll(...uids.map((uid) => firestore.collection('users').doc(uid)))\n return new Map(docs.map((doc) => [doc.id, doc]))\n}\n\n/** Creates one workspace's digests for the week; answers how many jobs it created. */\nasync function createOrgDigest(\n deps: AiInsightDigestDeps,\n orgDoc: FirebaseFirestore.QueryDocumentSnapshot,\n week: string,\n): Promise<number> {\n const { firestore, now } = deps\n const orgId = orgDoc.id\n const org = (orgDoc.data() ?? {}) as Record<string, unknown>\n if (!checkEntitlement(org as never, 'aiGenerative')) return 0\n const flagOn = isReleaseFlagOnForOrg(\n 'release_ai_generative',\n deps.flagValue,\n orgId,\n parseOrgReleaseFlagOverrides(org['releaseFlags']),\n resolveEffectivePlan(org as never),\n )\n if (!flagOn || (await deps.paused(orgId))) return 0\n const markerRef = orgDoc.ref.collection(AI_INSIGHT_DIGEST_MARKERS).doc(week)\n if ((await markerRef.get()).exists) return 0\n\n const members = (await deps.listMembers(orgId)).filter((member) => member.orgSuspended !== true)\n const users = await readUsers(firestore, members.map((member) => member.$id).sort())\n const subscribers = members\n .filter((member) => insightDigestSubscribed(users.get(member.$id)?.get(INSIGHT_DIGESTS_FIELD), orgId))\n .sort((a, b) => a.$id.localeCompare(b.$id))\n if (!subscribers.length) return 0\n\n const hosts = await firestore\n .collection('hosts')\n .where('orgId', '==', orgId)\n .limit(AI_INSIGHT_DIGEST_HOST_READ_LIMIT)\n .get()\n const yesterday = new Date(now.getTime() - 86_400_000).toISOString().slice(0, 10)\n const candidates: Array<{ hostId: string; name: string; creator: string; views: number }> = []\n for (const host of hosts.docs) {\n const data = host.data() ?? {}\n if (!isAiOnForSite(org, data)) continue\n let creator: string | null = null\n for (const member of subscribers) {\n if (hostRoleFor(member, host.id) !== null && (await deps.mayGenerate(orgId, host.id, member))) {\n creator = member.$id\n break\n }\n }\n if (!creator) continue\n const day = await host.ref.collection('analytics').doc(yesterday).get()\n candidates.push({\n hostId: host.id,\n name: str(data['displayName']) || str(data['name']) || str(data['subdomain']) || host.id,\n creator,\n views: Number(day.get('total') ?? 0) || 0,\n })\n }\n const chosen = candidates\n .sort((a, b) => b.views - a.views || a.hostId.localeCompare(b.hostId))\n .slice(0, AI_INSIGHT_DIGEST_MAX_SITES)\n if (!chosen.length) return 0\n\n const sites: Record<string, AiInsightDigestSite> = {}\n for (const site of chosen) {\n const job = await createAiJob(\n firestore,\n {\n orgId,\n hostId: site.hostId,\n kind: 'insight',\n brief: `Weekly insights for ${site.name}`,\n inputs: { surface: 'digest', week },\n createdBy: site.creator,\n },\n now,\n )\n sites[site.hostId] = { jobId: job.$id, name: site.name, state: 'pending' }\n }\n const marker: AiInsightDigestMarker = { week, createdAtMs: now.getTime(), sites }\n await markerRef.set(marker)\n await firestore\n .collection(AI_INSIGHT_DIGEST_QUEUE)\n .doc(week)\n .set({ week, pending: { [orgId]: true } }, { merge: true })\n return chosen.length\n}\n\n/** Delivers or settles one workspace's digests for a week; `deferred` when the send rate refused. */\nasync function deliverOrgDigest(\n deps: AiInsightDigestDeps,\n orgId: string,\n week: string,\n overBudget: () => boolean,\n): Promise<{ delivered: number; skipped: number; settled: boolean; deferred: boolean }> {\n const { firestore, now } = deps\n const orgRef = firestore.collection('orgs').doc(orgId)\n const markerRef = orgRef.collection(AI_INSIGHT_DIGEST_MARKERS).doc(week)\n const markerSnapshot = await markerRef.get()\n const result = { delivered: 0, skipped: 0, settled: true, deferred: false }\n if (!markerSnapshot.exists) return result\n const marker = markerSnapshot.data() as AiInsightDigestMarker\n const org = ((await orgRef.get()).data() ?? {}) as Record<string, unknown>\n let members: AglynOrgMember[] | null = null\n const branding = resolveBrandingProfile(org as never)\n\n for (const [hostId, site] of Object.entries(marker.sites ?? {})) {\n if (site.state !== 'pending') continue\n const settle = async (state: AiInsightDigestSiteState) => {\n // Replaced whole rather than merged, which drops `sent`.\n const settled: AiInsightDigestSite = { jobId: site.jobId, name: site.name, state }\n await markerRef.update({ [`sites.${hostId}`]: settled })\n if (state === 'delivered') result.delivered += 1\n else result.skipped += 1\n }\n const job = await getAiJob(firestore, orgId, site.jobId)\n if (!job || job.status === 'failed' || job.status === 'canceled') {\n await settle('skipped')\n continue\n }\n if (job.status !== 'done') {\n const late = now.getTime() - Number(marker.createdAtMs ?? 0) > AI_INSIGHT_DIGEST_DEADLINE_MS\n // A job parked for credits — a Free workspace at its band — is given up\n // rather than left to spend on a week that is already over.\n if (job.status === 'needs_input' || late) {\n await cancelAiJob(firestore, orgId, site.jobId, now)\n await settle('skipped')\n } else {\n result.settled = false\n }\n continue\n }\n const answer = await orgRef.collection(AI_INSIGHTS_COLLECTION).doc(site.jobId).get()\n const record = answer.exists ? (answer.data() as AiInsightRecord) : null\n if (!record?.insights?.length) {\n await settle('skipped')\n continue\n }\n\n members ??= (await deps.listMembers(orgId)).filter((member) => member.orgSuspended !== true)\n const reaching = members.filter((member) => hostRoleFor(member, hostId) !== null)\n const users = await readUsers(firestore, reaching.map((member) => member.$id).sort())\n const host = (await firestore.collection('hosts').doc(hostId).get()).data() ?? {}\n const orgSlug = str(org['slug'])\n const subdomain = str(host['subdomain'])\n const origin = deps.consoleOrigin\n const analyticsUrl = origin && orgSlug && subdomain ? `${origin}/${orgSlug}/hosts/${subdomain}/analytics` : null\n for (const member of reaching.sort((a, b) => a.$id.localeCompare(b.$id))) {\n const user = users.get(member.$id)\n if (!insightDigestSubscribed(user?.get(INSIGHT_DIGESTS_FIELD), orgId)) continue\n if (site.sent?.[member.$id]) continue\n if (overBudget()) {\n // Out of time: what was sent is stamped, and the next run sends the rest.\n result.deferred = true\n result.settled = false\n return result\n }\n const address = str(member.email ?? user?.get('email')).toLowerCase()\n if (address.includes('@')) {\n const sent = await deps.send({\n to: address,\n subject: `Your weekly insights for ${site.name}`,\n text: aiInsightDigestEmailText({\n siteName: site.name,\n productName: branding.productName,\n insights: record.insights,\n analyticsUrl,\n settingsUrl: origin ? `${origin}/manage/notifications` : null,\n }),\n fromName: branding.fromName,\n siteName: site.name,\n org,\n })\n if (sent.rateLimited) {\n // Nothing is marked, so the next run delivers this site whole.\n result.deferred = true\n result.settled = false\n return result\n }\n }\n if (!notificationMuted(user?.get('notificationPrefs'), 'content.insightsDigest')) {\n await deps.notify(member.$id, {\n title: `Weekly insights · ${site.name}`,\n body: record.insights[0].text,\n link: `/${hostId}/analytics`,\n orgId,\n hostId,\n })\n }\n await markerRef.set({ sites: { [hostId]: { sent: { [member.$id]: true } } } }, { merge: true })\n }\n await settle('delivered')\n }\n return result\n}\n\n/**\n * One call of the sweep: delivery for this week and the last on the first\n * call, then — on a Monday — one chunk of workspaces' creation, resumed by\n * `cursor`. The response shape is the console crons' (`nextCursor`, `done`),\n * so the scheduler's loop carries a platform of any size across calls.\n */\nexport async function runAiInsightDigestSweep(\n deps: AiInsightDigestDeps,\n options: { cursor: string | null },\n): Promise<AiInsightDigestReport> {\n const { firestore, now } = deps\n const startedAtMs = Date.now()\n const budgetMs = deps.budgetMs ?? AI_INSIGHT_DIGEST_RUN_BUDGET_MS\n const overBudget = () => Date.now() - startedAtMs > budgetMs\n const week = aiInsightDigestWeek(now)\n const report: AiInsightDigestReport = {\n week,\n swept: 0,\n created: 0,\n delivered: 0,\n skipped: 0,\n deferred: false,\n nextCursor: null,\n done: true,\n }\n\n if (!options.cursor) {\n for (const owed of [previousWeek(now), week]) {\n const queueRef = firestore.collection(AI_INSIGHT_DIGEST_QUEUE).doc(owed)\n const pending = Object.entries(((await queueRef.get()).get('pending') ?? {}) as Record<string, unknown>)\n .filter(([, owed]) => owed === true)\n .map(([orgId]) => orgId)\n .sort()\n .slice(0, AI_INSIGHT_DIGEST_DELIVER_LIMIT)\n for (const orgId of pending) {\n if (overBudget()) {\n report.deferred = true\n return report\n }\n try {\n const delivered = await deliverOrgDigest(deps, orgId, owed, overBudget)\n report.delivered += delivered.delivered\n report.skipped += delivered.skipped\n if (delivered.settled) {\n await queueRef.set({ pending: { [orgId]: false } }, { merge: true })\n }\n if (delivered.deferred) {\n report.deferred = true\n return report\n }\n } catch (error) {\n console.error('ai insight digest delivery failed', { orgId, week: owed, error })\n }\n }\n }\n }\n\n // Monday is the week's first day by ISO 8601, and the day a digest is made.\n if (now.getUTCDay() !== 1) return report\n\n let query = firestore.collection('orgs').orderBy(FieldPath.documentId()).limit(AI_INSIGHT_DIGEST_ORG_CHUNK + 1)\n if (options.cursor) query = query.startAfter(firestore.collection('orgs').doc(options.cursor))\n const page = await query.get()\n const orgs = page.docs.slice(0, AI_INSIGHT_DIGEST_ORG_CHUNK)\n for (const [index, orgDoc] of orgs.entries()) {\n if (overBudget()) {\n // Out of time: the next call resumes after the last workspace read. A\n // call that read none has no cursor to hand on, and the next scheduled\n // run starts the week's workspaces again, which the markers make safe.\n report.nextCursor = index ? orgs[index - 1].id : (options.cursor ?? null)\n report.done = report.nextCursor === null\n return report\n }\n report.swept += 1\n try {\n report.created += await createOrgDigest(deps, orgDoc, week)\n } catch (error) {\n console.error('ai insight digest creation failed', { orgId: orgDoc.id, week, error })\n }\n }\n const more = page.docs.length > AI_INSIGHT_DIGEST_ORG_CHUNK\n report.nextCursor = more ? (orgs[orgs.length - 1]?.id ?? null) : null\n report.done = !more\n return report\n}\n"],"names":["FieldPath","INSIGHT_DIGESTS_FIELD","insightDigestSubscribed","notificationMuted","hostRoleFor","checkEntitlement","resolveBrandingProfile","resolveEffectivePlan","isReleaseFlagOnForOrg","parseOrgReleaseFlagOverrides","AI_INSIGHTS_COLLECTION","aiInsightIsoWeek","isAiOnForSite","cancelAiJob","createAiJob","getAiJob","AI_INSIGHTS_DIGEST_CRON_ID","AI_INSIGHTS_DIGEST_PATH","AI_INSIGHT_DIGEST_ORG_CHUNK","AI_INSIGHT_DIGEST_MAX_SITES","AI_INSIGHT_DIGEST_HOST_READ_LIMIT","AI_INSIGHT_DIGEST_DELIVER_LIMIT","AI_INSIGHT_DIGEST_DEADLINE_MS","AI_INSIGHT_DIGEST_RUN_BUDGET_MS","AI_INSIGHT_DIGEST_MARKERS","AI_INSIGHT_DIGEST_QUEUE","str","value","trim","aiInsightDigestWeek","now","previousWeek","Date","getTime","aiInsightDigestEmailText","input","siteName","insights","map","insight","index","text","analyticsUrl","productName","settingsUrl","join","readUsers","firestore","uids","length","Map","docs","getAll","uid","collection","doc","id","createOrgDigest","deps","orgDoc","week","orgId","org","data","flagOn","flagValue","paused","markerRef","ref","get","exists","members","listMembers","filter","member","orgSuspended","users","$id","sort","subscribers","a","b","localeCompare","hosts","where","limit","yesterday","toISOString","slice","candidates","host","day","creator","mayGenerate","push","hostId","name","views","Number","chosen","sites","site","job","kind","brief","inputs","surface","createdBy","jobId","state","marker","createdAtMs","set","pending","merge","deliverOrgDigest","overBudget","orgRef","markerSnapshot","result","delivered","skipped","settled","deferred","branding","Object","entries","record","settle","update","status","late","answer","reaching","orgSlug","subdomain","origin","consoleOrigin","user","sent","address","email","toLowerCase","includes","send","to","subject","fromName","rateLimited","notify","title","body","link","runAiInsightDigestSweep","options","orgs","startedAtMs","budgetMs","report","swept","created","nextCursor","done","cursor","owed","queueRef","error","console","getUTCDay","query","orderBy","documentId","startAfter","page","more"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/insights/ai-insight-digest.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 { FieldPath } from 'firebase-admin/firestore'\nimport { INSIGHT_DIGESTS_FIELD, insightDigestSubscribed, notificationMuted } from '@aglyn/aglyn/app-utils/notifications'\nimport { hostRoleFor } from '@aglyn/aglyn/app-utils/organizations'\nimport { checkEntitlement, resolveBrandingProfile, resolveEffectivePlan } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { isReleaseFlagOnForOrg, parseOrgReleaseFlagOverrides, type ReleaseFlagValue } from '@aglyn/aglyn/app-utils/release-flags'\nimport type { AglynOrgMember } from '@aglyn/aglyn/foundation/definitions/organization.types'\nimport {\n AI_INSIGHTS_COLLECTION,\n aiInsightIsoWeek,\n type AiInsightRecord,\n} from '../model/ai-insight'\nimport { isAiOnForSite } from '../model/ai-site-switch'\nimport { cancelAiJob, createAiJob, getAiJob } from '../jobs/ai-jobs'\n\n/**\n * THE WEEKLY INSIGHTS (AGL-2915): `POST /api/admin/ai-insights-digest`, twice\n * a day on the console's scheduler.\n *\n * ## Who asked for it\n *\n * A person turns weekly insights on for a workspace beside the answers\n * themselves (`users/{uid}.insightDigests.{orgId}`), and off there or in\n * Notifications. It is opt-in, because each digest is a generation the\n * workspace pays for. At the sweep, every rung is asked again rather than\n * trusted from the switch: the workspace's plan includes `aiGenerative`,\n * `release_ai_generative` is on for it, its AI is not paused, the site has AI\n * on, and the person still holds `ai.generate` on the site.\n *\n * ## Monday: one job per site\n *\n * On a Monday run, for each workspace with someone who asked, the sites those\n * people reach — the busiest `AI_INSIGHT_DIGEST_MAX_SITES` by yesterday's page\n * views — each get one `insight` job with the `digest` surface, created by the\n * first of them who may generate there. The job runs on the AI jobs beat like\n * any other: it reserves, meters and records its credits, and a workspace at\n * its band — a Free one that has used its credits — has its job parked by the\n * reservation like any other. `orgs/{orgId}/aiInsightDigests/{week}` records\n * the jobs; `aiInsightDigestQueue/{week}` lists the workspaces still owed a\n * delivery, so a run that delivers reads one document rather than every\n * workspace.\n *\n * ## Every run: deliver what is written\n *\n * A digest whose job is done and kept at least one insight is delivered once:\n * a `content.insightsDigest` notification (unless the person muted the\n * category) and an email through the platform's own sending path, to each\n * person who asked for the workspace's insights and reaches the site. A job\n * that failed, kept nothing, or parked for credits is SKIPPED SILENTLY: a\n * parked job is canceled rather than left to spend later, and nobody is told,\n * because nobody asked about that week in particular. A job still running\n * past `AI_INSIGHT_DIGEST_DEADLINE_MS` is canceled and skipped the same way.\n *\n * The email says only what the answer says: the insights, each traced to the\n * figures it cites, and a link to the site's Analytics page. It is sent by the\n * platform to a person who asked for it, never to a contact.\n */\n\ntype Firestore = FirebaseFirestore.Firestore\n\nexport const AI_INSIGHTS_DIGEST_CRON_ID = 'ai-insights-digest'\nexport const AI_INSIGHTS_DIGEST_PATH = 'admin/ai-insights-digest'\n\n/** Orgs one call reads; a chunk that times out never advances the cursor, so the safe size is one that finishes. */\nexport const AI_INSIGHT_DIGEST_ORG_CHUNK = 25\n\n/** Sites one workspace's digest covers a week. */\nexport const AI_INSIGHT_DIGEST_MAX_SITES = 5\n\n/** Sites a workspace's sweep reads before it ranks them. */\nexport const AI_INSIGHT_DIGEST_HOST_READ_LIMIT = 50\n\n/** Workspaces one run delivers to, per week. */\nexport const AI_INSIGHT_DIGEST_DELIVER_LIMIT = 100\n\n/** How long a digest's job may take before its week is given up. */\nexport const AI_INSIGHT_DIGEST_DEADLINE_MS = 48 * 60 * 60_000\n\n/**\n * The time one call works before it stops and leaves the rest to the next:\n * inside the plugin dispatcher's 60 s function ceiling, with room for the\n * reads before the first check and the reply after the last. A call killed\n * mid-send could send one person a site's digest twice, because a send is\n * stamped after it goes.\n */\nexport const AI_INSIGHT_DIGEST_RUN_BUDGET_MS = 45_000\n\nexport const AI_INSIGHT_DIGEST_MARKERS = 'aiInsightDigests'\nexport const AI_INSIGHT_DIGEST_QUEUE = 'aiInsightDigestQueue'\n\nexport type AiInsightDigestSiteState = 'pending' | 'delivered' | 'skipped'\n\nexport interface AiInsightDigestSite {\n jobId: string\n name: string\n state: AiInsightDigestSiteState\n /**\n * The people already sent this site's digest, so a run the send rate\n * stopped never sends one twice. Dropped when the site settles, so a\n * settled marker names no person.\n */\n sent?: Record<string, boolean>\n}\n\nexport interface AiInsightDigestMarker {\n week: string\n createdAtMs: number\n sites: Record<string, AiInsightDigestSite>\n}\n\n/** What a send answered: whether it went, and whether the platform's send rate refused it for now. */\nexport interface AiInsightDigestSendResult {\n sent: boolean\n rateLimited: boolean\n}\n\n/** Everything the sweep reaches beyond Firestore, so a spec can hold each one. */\nexport interface AiInsightDigestDeps {\n firestore: Firestore\n now: Date\n flagValue: ReleaseFlagValue\n listMembers: (orgId: string) => Promise<AglynOrgMember[]>\n mayGenerate: (orgId: string, hostId: string, member: AglynOrgMember) => Promise<boolean>\n paused: (orgId: string) => Promise<boolean>\n notify: (uid: string, payload: { title: string; body: string; link: string; orgId: string; hostId: string }) => Promise<void>\n send: (email: {\n to: string\n subject: string\n text: string\n fromName: string\n /** The site the digest is about, and the org whose brand it wears (AGL-3367). */\n siteName: string\n org: Record<string, unknown>\n }) => Promise<AiInsightDigestSendResult>\n /** The console's absolute origin for an email link, or `''` when none is configured. */\n consoleOrigin: string\n /** The call's working time; `AI_INSIGHT_DIGEST_RUN_BUDGET_MS` unless a spec holds it. */\n budgetMs?: number\n}\n\nexport interface AiInsightDigestReport {\n week: string\n swept: number\n created: number\n delivered: number\n skipped: number\n /** The send rate refused or the call's time ran out; the run stops, and the next one delivers the rest. */\n deferred: boolean\n nextCursor: string | null\n done: boolean\n}\n\nconst str = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\n/** The week a Monday digest covers: the week the run falls in. */\nexport function aiInsightDigestWeek(now: Date): string {\n return aiInsightIsoWeek(now)\n}\n\n/** The week before, whose late digests a run still delivers. */\nfunction previousWeek(now: Date): string {\n return aiInsightIsoWeek(new Date(now.getTime() - 7 * 86_400_000))\n}\n\n/**\n * A digest email's words: the insights, the site's Analytics page, and where\n * to turn it off. The digest is an AI job metered against the workspace, so\n * it says what it drew, and it names the workspace a member of several asked\n * for it in.\n */\nexport function aiInsightDigestEmailText(input: {\n siteName: string\n productName: string\n insights: ReadonlyArray<{ text: string }>\n /** Absolute links, or `null` where no console origin is configured to build one from. */\n analyticsUrl: string | null\n settingsUrl: string | null\n /** The workspace the digest was asked for in; `''` falls back to \"this workspace\". */\n workspaceName?: string\n /** The AI credits the digest's job drew; 0 or absent says nothing. */\n creditsSpent?: number\n}): string {\n const workspace = input.workspaceName?.trim() || ''\n const credits = Math.max(0, Math.round(Number(input.creditsSpent) || 0))\n const drew = credits\n ? ` Writing it used ${credits.toLocaleString('en-US')} of ` +\n `${workspace ? `${workspace}’s` : 'the workspace’s'} AI credits.`\n : ''\n const forWhom = workspace || 'this workspace'\n return [\n `Here is what ${input.siteName}'s figures showed this week.${drew}`,\n '',\n ...input.insights.map((insight, index) => `${index + 1}. ${insight.text}`),\n '',\n input.analyticsUrl\n ? `Each of these is traced to the figures it cites. See them in ${input.productName}: ${input.analyticsUrl}`\n : `Each of these is traced to the figures it cites. See them on the site's Analytics page in ${input.productName}.`,\n '',\n input.settingsUrl\n ? `You asked for weekly insights for ${forWhom}. Turn them off in Notifications: ${input.settingsUrl}`\n : `You asked for weekly insights for ${forWhom}. Turn them off in your Notifications settings.`,\n ].join('\\n')\n}\n\nasync function readUsers(firestore: Firestore, uids: readonly string[]) {\n if (!uids.length) return new Map<string, FirebaseFirestore.DocumentSnapshot>()\n const docs = await firestore.getAll(...uids.map((uid) => firestore.collection('users').doc(uid)))\n return new Map(docs.map((doc) => [doc.id, doc]))\n}\n\n/** Creates one workspace's digests for the week; answers how many jobs it created. */\nasync function createOrgDigest(\n deps: AiInsightDigestDeps,\n orgDoc: FirebaseFirestore.QueryDocumentSnapshot,\n week: string,\n): Promise<number> {\n const { firestore, now } = deps\n const orgId = orgDoc.id\n const org = (orgDoc.data() ?? {}) as Record<string, unknown>\n if (!checkEntitlement(org as never, 'aiGenerative')) return 0\n const flagOn = isReleaseFlagOnForOrg(\n 'release_ai_generative',\n deps.flagValue,\n orgId,\n parseOrgReleaseFlagOverrides(org['releaseFlags']),\n resolveEffectivePlan(org as never),\n )\n if (!flagOn || (await deps.paused(orgId))) return 0\n const markerRef = orgDoc.ref.collection(AI_INSIGHT_DIGEST_MARKERS).doc(week)\n if ((await markerRef.get()).exists) return 0\n\n const members = (await deps.listMembers(orgId)).filter((member) => member.orgSuspended !== true)\n const users = await readUsers(firestore, members.map((member) => member.$id).sort())\n const subscribers = members\n .filter((member) => insightDigestSubscribed(users.get(member.$id)?.get(INSIGHT_DIGESTS_FIELD), orgId))\n .sort((a, b) => a.$id.localeCompare(b.$id))\n if (!subscribers.length) return 0\n\n const hosts = await firestore\n .collection('hosts')\n .where('orgId', '==', orgId)\n .limit(AI_INSIGHT_DIGEST_HOST_READ_LIMIT)\n .get()\n const yesterday = new Date(now.getTime() - 86_400_000).toISOString().slice(0, 10)\n const candidates: Array<{ hostId: string; name: string; creator: string; views: number }> = []\n for (const host of hosts.docs) {\n const data = host.data() ?? {}\n if (!isAiOnForSite(org, data)) continue\n let creator: string | null = null\n for (const member of subscribers) {\n if (hostRoleFor(member, host.id) !== null && (await deps.mayGenerate(orgId, host.id, member))) {\n creator = member.$id\n break\n }\n }\n if (!creator) continue\n const day = await host.ref.collection('analytics').doc(yesterday).get()\n candidates.push({\n hostId: host.id,\n name: str(data['displayName']) || str(data['name']) || str(data['subdomain']) || host.id,\n creator,\n views: Number(day.get('total') ?? 0) || 0,\n })\n }\n const chosen = candidates\n .sort((a, b) => b.views - a.views || a.hostId.localeCompare(b.hostId))\n .slice(0, AI_INSIGHT_DIGEST_MAX_SITES)\n if (!chosen.length) return 0\n\n const sites: Record<string, AiInsightDigestSite> = {}\n for (const site of chosen) {\n const job = await createAiJob(\n firestore,\n {\n orgId,\n hostId: site.hostId,\n kind: 'insight',\n brief: `Weekly insights for ${site.name}`,\n inputs: { surface: 'digest', week },\n createdBy: site.creator,\n },\n now,\n )\n sites[site.hostId] = { jobId: job.$id, name: site.name, state: 'pending' }\n }\n const marker: AiInsightDigestMarker = { week, createdAtMs: now.getTime(), sites }\n await markerRef.set(marker)\n await firestore\n .collection(AI_INSIGHT_DIGEST_QUEUE)\n .doc(week)\n .set({ week, pending: { [orgId]: true } }, { merge: true })\n return chosen.length\n}\n\n/** Delivers or settles one workspace's digests for a week; `deferred` when the send rate refused. */\nasync function deliverOrgDigest(\n deps: AiInsightDigestDeps,\n orgId: string,\n week: string,\n overBudget: () => boolean,\n): Promise<{ delivered: number; skipped: number; settled: boolean; deferred: boolean }> {\n const { firestore, now } = deps\n const orgRef = firestore.collection('orgs').doc(orgId)\n const markerRef = orgRef.collection(AI_INSIGHT_DIGEST_MARKERS).doc(week)\n const markerSnapshot = await markerRef.get()\n const result = { delivered: 0, skipped: 0, settled: true, deferred: false }\n if (!markerSnapshot.exists) return result\n const marker = markerSnapshot.data() as AiInsightDigestMarker\n const org = ((await orgRef.get()).data() ?? {}) as Record<string, unknown>\n let members: AglynOrgMember[] | null = null\n const branding = resolveBrandingProfile(org as never)\n\n for (const [hostId, site] of Object.entries(marker.sites ?? {})) {\n if (site.state !== 'pending') continue\n const settle = async (state: AiInsightDigestSiteState) => {\n // Replaced whole rather than merged, which drops `sent`.\n const settled: AiInsightDigestSite = { jobId: site.jobId, name: site.name, state }\n await markerRef.update({ [`sites.${hostId}`]: settled })\n if (state === 'delivered') result.delivered += 1\n else result.skipped += 1\n }\n const job = await getAiJob(firestore, orgId, site.jobId)\n if (!job || job.status === 'failed' || job.status === 'canceled') {\n await settle('skipped')\n continue\n }\n if (job.status !== 'done') {\n const late = now.getTime() - Number(marker.createdAtMs ?? 0) > AI_INSIGHT_DIGEST_DEADLINE_MS\n // A job parked for credits — a Free workspace at its band — is given up\n // rather than left to spend on a week that is already over.\n if (job.status === 'needs_input' || late) {\n await cancelAiJob(firestore, orgId, site.jobId, now)\n await settle('skipped')\n } else {\n result.settled = false\n }\n continue\n }\n const answer = await orgRef.collection(AI_INSIGHTS_COLLECTION).doc(site.jobId).get()\n const record = answer.exists ? (answer.data() as AiInsightRecord) : null\n if (!record?.insights?.length) {\n await settle('skipped')\n continue\n }\n\n members ??= (await deps.listMembers(orgId)).filter((member) => member.orgSuspended !== true)\n const reaching = members.filter((member) => hostRoleFor(member, hostId) !== null)\n const users = await readUsers(firestore, reaching.map((member) => member.$id).sort())\n const host = (await firestore.collection('hosts').doc(hostId).get()).data() ?? {}\n const orgSlug = str(org['slug'])\n const subdomain = str(host['subdomain'])\n const origin = deps.consoleOrigin\n const analyticsUrl = origin && orgSlug && subdomain ? `${origin}/${orgSlug}/hosts/${subdomain}/analytics` : null\n for (const member of reaching.sort((a, b) => a.$id.localeCompare(b.$id))) {\n const user = users.get(member.$id)\n if (!insightDigestSubscribed(user?.get(INSIGHT_DIGESTS_FIELD), orgId)) continue\n if (site.sent?.[member.$id]) continue\n if (overBudget()) {\n // Out of time: what was sent is stamped, and the next run sends the rest.\n result.deferred = true\n result.settled = false\n return result\n }\n const address = str(member.email ?? user?.get('email')).toLowerCase()\n if (address.includes('@')) {\n const sent = await deps.send({\n to: address,\n subject: `Your weekly insights for ${site.name}`,\n text: aiInsightDigestEmailText({\n siteName: site.name,\n productName: branding.productName,\n insights: record.insights,\n analyticsUrl,\n settingsUrl: origin ? `${origin}/manage/notifications` : null,\n workspaceName: str(org['name']),\n creditsSpent: job.creditsSpent,\n }),\n fromName: branding.fromName,\n siteName: site.name,\n org,\n })\n if (sent.rateLimited) {\n // Nothing is marked, so the next run delivers this site whole.\n result.deferred = true\n result.settled = false\n return result\n }\n }\n if (!notificationMuted(user?.get('notificationPrefs'), 'content.insightsDigest')) {\n await deps.notify(member.$id, {\n title: `Weekly insights · ${site.name}`,\n body: record.insights[0].text,\n link: `/${hostId}/analytics`,\n orgId,\n hostId,\n })\n }\n await markerRef.set({ sites: { [hostId]: { sent: { [member.$id]: true } } } }, { merge: true })\n }\n await settle('delivered')\n }\n return result\n}\n\n/**\n * One call of the sweep: delivery for this week and the last on the first\n * call, then — on a Monday — one chunk of workspaces' creation, resumed by\n * `cursor`. The response shape is the console crons' (`nextCursor`, `done`),\n * so the scheduler's loop carries a platform of any size across calls.\n */\nexport async function runAiInsightDigestSweep(\n deps: AiInsightDigestDeps,\n options: { cursor: string | null },\n): Promise<AiInsightDigestReport> {\n const { firestore, now } = deps\n const startedAtMs = Date.now()\n const budgetMs = deps.budgetMs ?? AI_INSIGHT_DIGEST_RUN_BUDGET_MS\n const overBudget = () => Date.now() - startedAtMs > budgetMs\n const week = aiInsightDigestWeek(now)\n const report: AiInsightDigestReport = {\n week,\n swept: 0,\n created: 0,\n delivered: 0,\n skipped: 0,\n deferred: false,\n nextCursor: null,\n done: true,\n }\n\n if (!options.cursor) {\n for (const owed of [previousWeek(now), week]) {\n const queueRef = firestore.collection(AI_INSIGHT_DIGEST_QUEUE).doc(owed)\n const pending = Object.entries(((await queueRef.get()).get('pending') ?? {}) as Record<string, unknown>)\n .filter(([, owed]) => owed === true)\n .map(([orgId]) => orgId)\n .sort()\n .slice(0, AI_INSIGHT_DIGEST_DELIVER_LIMIT)\n for (const orgId of pending) {\n if (overBudget()) {\n report.deferred = true\n return report\n }\n try {\n const delivered = await deliverOrgDigest(deps, orgId, owed, overBudget)\n report.delivered += delivered.delivered\n report.skipped += delivered.skipped\n if (delivered.settled) {\n await queueRef.set({ pending: { [orgId]: false } }, { merge: true })\n }\n if (delivered.deferred) {\n report.deferred = true\n return report\n }\n } catch (error) {\n console.error('ai insight digest delivery failed', { orgId, week: owed, error })\n }\n }\n }\n }\n\n // Monday is the week's first day by ISO 8601, and the day a digest is made.\n if (now.getUTCDay() !== 1) return report\n\n let query = firestore.collection('orgs').orderBy(FieldPath.documentId()).limit(AI_INSIGHT_DIGEST_ORG_CHUNK + 1)\n if (options.cursor) query = query.startAfter(firestore.collection('orgs').doc(options.cursor))\n const page = await query.get()\n const orgs = page.docs.slice(0, AI_INSIGHT_DIGEST_ORG_CHUNK)\n for (const [index, orgDoc] of orgs.entries()) {\n if (overBudget()) {\n // Out of time: the next call resumes after the last workspace read. A\n // call that read none has no cursor to hand on, and the next scheduled\n // run starts the week's workspaces again, which the markers make safe.\n report.nextCursor = index ? orgs[index - 1].id : (options.cursor ?? null)\n report.done = report.nextCursor === null\n return report\n }\n report.swept += 1\n try {\n report.created += await createOrgDigest(deps, orgDoc, week)\n } catch (error) {\n console.error('ai insight digest creation failed', { orgId: orgDoc.id, week, error })\n }\n }\n const more = page.docs.length > AI_INSIGHT_DIGEST_ORG_CHUNK\n report.nextCursor = more ? (orgs[orgs.length - 1]?.id ?? null) : null\n report.done = !more\n return report\n}\n"],"names":["FieldPath","INSIGHT_DIGESTS_FIELD","insightDigestSubscribed","notificationMuted","hostRoleFor","checkEntitlement","resolveBrandingProfile","resolveEffectivePlan","isReleaseFlagOnForOrg","parseOrgReleaseFlagOverrides","AI_INSIGHTS_COLLECTION","aiInsightIsoWeek","isAiOnForSite","cancelAiJob","createAiJob","getAiJob","AI_INSIGHTS_DIGEST_CRON_ID","AI_INSIGHTS_DIGEST_PATH","AI_INSIGHT_DIGEST_ORG_CHUNK","AI_INSIGHT_DIGEST_MAX_SITES","AI_INSIGHT_DIGEST_HOST_READ_LIMIT","AI_INSIGHT_DIGEST_DELIVER_LIMIT","AI_INSIGHT_DIGEST_DEADLINE_MS","AI_INSIGHT_DIGEST_RUN_BUDGET_MS","AI_INSIGHT_DIGEST_MARKERS","AI_INSIGHT_DIGEST_QUEUE","str","value","trim","aiInsightDigestWeek","now","previousWeek","Date","getTime","aiInsightDigestEmailText","input","workspace","workspaceName","credits","Math","max","round","Number","creditsSpent","drew","toLocaleString","forWhom","siteName","insights","map","insight","index","text","analyticsUrl","productName","settingsUrl","join","readUsers","firestore","uids","length","Map","docs","getAll","uid","collection","doc","id","createOrgDigest","deps","orgDoc","week","orgId","org","data","flagOn","flagValue","paused","markerRef","ref","get","exists","members","listMembers","filter","member","orgSuspended","users","$id","sort","subscribers","a","b","localeCompare","hosts","where","limit","yesterday","toISOString","slice","candidates","host","day","creator","mayGenerate","push","hostId","name","views","chosen","sites","site","job","kind","brief","inputs","surface","createdBy","jobId","state","marker","createdAtMs","set","pending","merge","deliverOrgDigest","overBudget","orgRef","markerSnapshot","result","delivered","skipped","settled","deferred","branding","Object","entries","record","settle","update","status","late","answer","reaching","orgSlug","subdomain","origin","consoleOrigin","user","sent","address","email","toLowerCase","includes","send","to","subject","fromName","rateLimited","notify","title","body","link","runAiInsightDigestSweep","options","orgs","startedAtMs","budgetMs","report","swept","created","nextCursor","done","cursor","owed","queueRef","error","console","getUTCDay","query","orderBy","documentId","startAfter","page","more"],"mappings":"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"}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { type ResolvedPluginFigureReader } from '@aglyn/aglyn/plugin-manager/plugin-figures';
|
|
18
18
|
import { type AiInsightSurface, type AiInsightTable } from '../model/ai-insight';
|
|
19
19
|
import type { AiInsightReadRequest } from '../tools/ai-insight-tool';
|
|
20
|
-
import type { AiDatasetCatalogEntry } from './ai-
|
|
20
|
+
import type { AiDatasetCatalogEntry } from './ai-dataset-catalog';
|
|
21
21
|
/**
|
|
22
22
|
* Which figure readers an insight job may read, and reading them (AGL-2915).
|
|
23
23
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/insights/ai-insight-readers.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { isHostPluginEnabled, isPluginEnabled } from '@aglyn/aglyn/plugin-manager/enabled-plugins'\nimport {\n listPluginFigureReaders,\n normalizePluginFigureTable,\n pluginFigureParams,\n pluginFigureReaderPlugin,\n type ResolvedPluginFigureReader,\n} from '@aglyn/aglyn/plugin-manager/plugin-figures'\nimport {\n AI_INSIGHT_MAX_READS,\n AI_INSIGHT_SURFACE_READERS,\n aiInsightCellText,\n type AiInsightSurface,\n type AiInsightTable,\n} from '../model/ai-insight'\nimport { filterEnabledPluginsByReleaseFlags } from '@aglyn/tenant-data-admin/server/release-flags'\nimport type { AiInsightReadRequest } from '../tools/ai-insight-tool'\nimport type { AiDatasetCatalogEntry } from './ai-figure-readers'\n\n/**\n * Which figure readers an insight job may read, and reading them (AGL-2915).\n *\n * The caller's half of the `plugin-figures` contract: the seam carries no\n * authorization, so before a reader is offered to the model — let alone read\n * — this establishes that the surface asks about its kind of figures, that\n * the workspace's plan includes the feature the figures are sold under, and\n * that the plugin whose records they are runs here: past its release flag,\n * on for the workspace and not switched off for the site. The site belonging\n * to the job's workspace, and the member's reach, are the job's own checks.\n */\n\n/** Which of a set of plugins are past their release flags for an org; the platform's flags by default. */\nexport type AiReleasedPlugins = (pluginIds: string[], orgId: string) => Promise<string[]>\n\nconst releasedByFlags: AiReleasedPlugins = (pluginIds, orgId) =>\n filterEnabledPluginsByReleaseFlags(pluginIds, { orgId, authorization: null })\n\nexport interface AiInsightReaderContext {\n orgId: string\n /** The site the figures are about; `null` for a workspace-level question. */\n hostId: string | null\n /** The org document the machine or the door read. */\n org: object | null\n /** The site's host document, already checked to belong to the org; `null` with no site. */\n host: Record<string, unknown> | null\n surface: AiInsightSurface\n released?: AiReleasedPlugins\n}\n\n/** The readers a job on this surface may read, ordered by id so a prompt lists them in the same order every time. */\nexport async function aiInsightReaders(context: AiInsightReaderContext): Promise<ResolvedPluginFigureReader[]> {\n const prefixes = AI_INSIGHT_SURFACE_READERS[context.surface]\n const org = context.org as { enabledPlugins?: string[] } | null\n const candidates = listPluginFigureReaders().filter(({ reader }) => {\n if (!prefixes.includes(reader.id.split('.')[0])) return false\n // A site reader needs a site; a workspace reader reads with or without one.\n if (reader.scope === 'site' && !context.hostId) return false\n return !reader.feature || checkEntitlement(org as never, reader.feature)\n })\n const plugins = [...new Set(candidates.map(pluginFigureReaderPlugin).filter((id): id is string => Boolean(id)))]\n const released = new Set(plugins.length ? await (context.released ?? releasedByFlags)(plugins, context.orgId) : [])\n return candidates.filter((resolved) => {\n const plugin = pluginFigureReaderPlugin(resolved)\n if (!plugin) return true\n if (!released.has(plugin)) return false\n return context.hostId\n ? isHostPluginEnabled(org, context.host as { disabledPlugins?: string[] } | null, plugin)\n : isPluginEnabled(org, plugin)\n })\n}\n\n/** The window a reader is read over: the one asked for when it covers it, else the nearest it does. */\nexport function aiInsightReaderDays(windows: readonly number[], asked: number): number {\n if (!windows.length) return 0\n if (windows.includes(asked)) return asked\n return [...windows].sort((a, b) => Math.abs(a - asked) - Math.abs(b - asked) || a - b)[0]\n}\n\n/** The readers as the read prompt lists them: id, what each answers, its windows and its parameters. */\nexport function aiInsightReaderCatalog(readers: readonly ResolvedPluginFigureReader[]): string {\n return readers\n .map(({ reader }) => {\n const windows = reader.windows.length\n ? ` Windows: ${reader.windows.join(', ')} days.`\n : ' Current totals, with no window: days 0.'\n const params = (reader.params ?? []).length\n ? ` Parameters: ${(reader.params ?? [])\n .map((param) => `${param.name} (${param.required ? 'required' : 'optional'}) — ${param.description}`)\n .join('; ')}`\n : ''\n return `- ${reader.id} — ${reader.label}: ${reader.description}${windows}${params}`\n })\n .join('\\n')\n}\n\n/** The datasets a datasets question may name, as the read prompt lists them. */\nexport function aiInsightDatasetCatalog(datasets: readonly AiDatasetCatalogEntry[]): string {\n if (!datasets.length) return 'There are no datasets you may read here.'\n return datasets\n .map(\n (dataset) =>\n `- ${dataset.name} (id ${dataset.id}): ${dataset.fields.map((field) => `${field.name} [${field.type}]`).join(', ') || 'no fields'}`,\n )\n .join('\\n')\n}\n\nexport interface AiInsightReadResult {\n tables: AiInsightTable[]\n /** Why a request that named a real reader was not read, in its owner's words, for the log. */\n refusals: string[]\n}\n\n/**\n * The tables for the reads a job asked for: each request held to the readers\n * this job may read, its window to that reader's, its parameters to those it\n * declares; each table held to the contract by `normalizePluginFigureTable`.\n * A reader asked for twice with the same window and parameters is read once.\n */\nexport async function readAiInsightTables(\n readers: readonly ResolvedPluginFigureReader[],\n requests: readonly AiInsightReadRequest[],\n base: { orgId: string; hostId: string | null; uid: string | null; now: Date },\n maxTables: number = AI_INSIGHT_MAX_READS,\n): Promise<AiInsightReadResult> {\n const tables: AiInsightTable[] = []\n const refusals: string[] = []\n const seen = new Set<string>()\n for (const request of requests) {\n if (tables.length === maxTables) break\n const resolved = readers.find(({ reader }) => reader.id === request.reader)\n if (!resolved) continue\n const { reader } = resolved\n const days = aiInsightReaderDays(reader.windows, request.days)\n const params = pluginFigureParams(reader, request.params)\n if (params.ok === false) {\n refusals.push(`${reader.id}: missing ${params.missing}`)\n continue\n }\n const key = JSON.stringify([reader.id, days, params.params])\n if (seen.has(key)) continue\n seen.add(key)\n try {\n const read = await reader.read({ ...base, days, params: params.params })\n if (read.ok === false) {\n refusals.push(`${reader.id}: ${read.error}`)\n continue\n }\n const table = normalizePluginFigureTable(read.table)\n if (!table) {\n refusals.push(`${reader.id}: not a table`)\n continue\n }\n tables.push({ ...table, ref: `t${tables.length + 1}`, reader: reader.id, days, scope: reader.scope })\n } catch (error) {\n // One reader's fault is not the answer's: the rest are still read.\n console.error('ai insight reader failed', { reader: reader.id, orgId: base.orgId, error })\n refusals.push(`${reader.id}: failed`)\n }\n }\n return { tables, refusals }\n}\n\n/** One table as the answer prompt shows it: a heading, the columns, numbered rows, and its notes. */\nexport function aiInsightTableText(table: AiInsightTable): string {\n const period = table.period ? ` · ${table.period.from} to ${table.period.to} (${table.period.days} days)` : ''\n const lines = [\n `${table.ref} · ${table.title} (${table.source.label})${period}`,\n `Columns: ${table.columns.map((column) => column.label).join(' | ')}`,\n ...table.rows.map(\n (row, index) =>\n `${index}: ${table.columns.map((column) => aiInsightCellText(table, column.key, row[column.key])).join(' | ')}`,\n ),\n ]\n if (!table.rows.length) lines.push('(no rows)')\n if (table.omitted) lines.push(`${table.omitted} more rows were left out of this table.`)\n for (const note of table.notes) lines.push(`Note: ${note}`)\n return lines.join('\\n')\n}\n"],"names":["checkEntitlement","isHostPluginEnabled","isPluginEnabled","listPluginFigureReaders","normalizePluginFigureTable","pluginFigureParams","pluginFigureReaderPlugin","AI_INSIGHT_MAX_READS","AI_INSIGHT_SURFACE_READERS","aiInsightCellText","filterEnabledPluginsByReleaseFlags","releasedByFlags","pluginIds","orgId","authorization","aiInsightReaders","context","prefixes","surface","org","candidates","filter","reader","includes","id","split","scope","hostId","feature","plugins","Set","map","Boolean","released","length","resolved","plugin","has","host","aiInsightReaderDays","windows","asked","sort","a","b","Math","abs","aiInsightReaderCatalog","readers","join","params","param","name","required","description","label","aiInsightDatasetCatalog","datasets","dataset","fields","field","type","readAiInsightTables","requests","base","maxTables","tables","refusals","seen","request","find","days","ok","push","missing","key","JSON","stringify","add","read","error","table","ref","console","aiInsightTableText","period","from","to","lines","title","source","columns","column","rows","row","index","omitted","note","notes"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/insights/ai-insight-readers.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { isHostPluginEnabled, isPluginEnabled } from '@aglyn/aglyn/plugin-manager/enabled-plugins'\nimport {\n listPluginFigureReaders,\n normalizePluginFigureTable,\n pluginFigureParams,\n pluginFigureReaderPlugin,\n type ResolvedPluginFigureReader,\n} from '@aglyn/aglyn/plugin-manager/plugin-figures'\nimport {\n AI_INSIGHT_MAX_READS,\n AI_INSIGHT_SURFACE_READERS,\n aiInsightCellText,\n type AiInsightSurface,\n type AiInsightTable,\n} from '../model/ai-insight'\nimport { filterEnabledPluginsByReleaseFlags } from '@aglyn/tenant-data-admin/server/release-flags'\nimport type { AiInsightReadRequest } from '../tools/ai-insight-tool'\nimport type { AiDatasetCatalogEntry } from './ai-dataset-catalog'\n\n/**\n * Which figure readers an insight job may read, and reading them (AGL-2915).\n *\n * The caller's half of the `plugin-figures` contract: the seam carries no\n * authorization, so before a reader is offered to the model — let alone read\n * — this establishes that the surface asks about its kind of figures, that\n * the workspace's plan includes the feature the figures are sold under, and\n * that the plugin whose records they are runs here: past its release flag,\n * on for the workspace and not switched off for the site. The site belonging\n * to the job's workspace, and the member's reach, are the job's own checks.\n */\n\n/** Which of a set of plugins are past their release flags for an org; the platform's flags by default. */\nexport type AiReleasedPlugins = (pluginIds: string[], orgId: string) => Promise<string[]>\n\nconst releasedByFlags: AiReleasedPlugins = (pluginIds, orgId) =>\n filterEnabledPluginsByReleaseFlags(pluginIds, { orgId, authorization: null })\n\nexport interface AiInsightReaderContext {\n orgId: string\n /** The site the figures are about; `null` for a workspace-level question. */\n hostId: string | null\n /** The org document the machine or the door read. */\n org: object | null\n /** The site's host document, already checked to belong to the org; `null` with no site. */\n host: Record<string, unknown> | null\n surface: AiInsightSurface\n released?: AiReleasedPlugins\n}\n\n/** The readers a job on this surface may read, ordered by id so a prompt lists them in the same order every time. */\nexport async function aiInsightReaders(context: AiInsightReaderContext): Promise<ResolvedPluginFigureReader[]> {\n const prefixes = AI_INSIGHT_SURFACE_READERS[context.surface]\n const org = context.org as { enabledPlugins?: string[] } | null\n const candidates = listPluginFigureReaders().filter(({ reader }) => {\n if (!prefixes.includes(reader.id.split('.')[0])) return false\n // A site reader needs a site; a workspace reader reads with or without one.\n if (reader.scope === 'site' && !context.hostId) return false\n return !reader.feature || checkEntitlement(org as never, reader.feature)\n })\n const plugins = [...new Set(candidates.map(pluginFigureReaderPlugin).filter((id): id is string => Boolean(id)))]\n const released = new Set(plugins.length ? await (context.released ?? releasedByFlags)(plugins, context.orgId) : [])\n return candidates.filter((resolved) => {\n const plugin = pluginFigureReaderPlugin(resolved)\n if (!plugin) return true\n if (!released.has(plugin)) return false\n return context.hostId\n ? isHostPluginEnabled(org, context.host as { disabledPlugins?: string[] } | null, plugin)\n : isPluginEnabled(org, plugin)\n })\n}\n\n/** The window a reader is read over: the one asked for when it covers it, else the nearest it does. */\nexport function aiInsightReaderDays(windows: readonly number[], asked: number): number {\n if (!windows.length) return 0\n if (windows.includes(asked)) return asked\n return [...windows].sort((a, b) => Math.abs(a - asked) - Math.abs(b - asked) || a - b)[0]\n}\n\n/** The readers as the read prompt lists them: id, what each answers, its windows and its parameters. */\nexport function aiInsightReaderCatalog(readers: readonly ResolvedPluginFigureReader[]): string {\n return readers\n .map(({ reader }) => {\n const windows = reader.windows.length\n ? ` Windows: ${reader.windows.join(', ')} days.`\n : ' Current totals, with no window: days 0.'\n const params = (reader.params ?? []).length\n ? ` Parameters: ${(reader.params ?? [])\n .map((param) => `${param.name} (${param.required ? 'required' : 'optional'}) — ${param.description}`)\n .join('; ')}`\n : ''\n return `- ${reader.id} — ${reader.label}: ${reader.description}${windows}${params}`\n })\n .join('\\n')\n}\n\n/** The datasets a datasets question may name, as the read prompt lists them. */\nexport function aiInsightDatasetCatalog(datasets: readonly AiDatasetCatalogEntry[]): string {\n if (!datasets.length) return 'There are no datasets you may read here.'\n return datasets\n .map(\n (dataset) =>\n `- ${dataset.name} (id ${dataset.id}): ${dataset.fields.map((field) => `${field.name} [${field.type}]`).join(', ') || 'no fields'}`,\n )\n .join('\\n')\n}\n\nexport interface AiInsightReadResult {\n tables: AiInsightTable[]\n /** Why a request that named a real reader was not read, in its owner's words, for the log. */\n refusals: string[]\n}\n\n/**\n * The tables for the reads a job asked for: each request held to the readers\n * this job may read, its window to that reader's, its parameters to those it\n * declares; each table held to the contract by `normalizePluginFigureTable`.\n * A reader asked for twice with the same window and parameters is read once.\n */\nexport async function readAiInsightTables(\n readers: readonly ResolvedPluginFigureReader[],\n requests: readonly AiInsightReadRequest[],\n base: { orgId: string; hostId: string | null; uid: string | null; now: Date },\n maxTables: number = AI_INSIGHT_MAX_READS,\n): Promise<AiInsightReadResult> {\n const tables: AiInsightTable[] = []\n const refusals: string[] = []\n const seen = new Set<string>()\n for (const request of requests) {\n if (tables.length === maxTables) break\n const resolved = readers.find(({ reader }) => reader.id === request.reader)\n if (!resolved) continue\n const { reader } = resolved\n const days = aiInsightReaderDays(reader.windows, request.days)\n const params = pluginFigureParams(reader, request.params)\n if (params.ok === false) {\n refusals.push(`${reader.id}: missing ${params.missing}`)\n continue\n }\n const key = JSON.stringify([reader.id, days, params.params])\n if (seen.has(key)) continue\n seen.add(key)\n try {\n const read = await reader.read({ ...base, days, params: params.params })\n if (read.ok === false) {\n refusals.push(`${reader.id}: ${read.error}`)\n continue\n }\n const table = normalizePluginFigureTable(read.table)\n if (!table) {\n refusals.push(`${reader.id}: not a table`)\n continue\n }\n tables.push({ ...table, ref: `t${tables.length + 1}`, reader: reader.id, days, scope: reader.scope })\n } catch (error) {\n // One reader's fault is not the answer's: the rest are still read.\n console.error('ai insight reader failed', { reader: reader.id, orgId: base.orgId, error })\n refusals.push(`${reader.id}: failed`)\n }\n }\n return { tables, refusals }\n}\n\n/** One table as the answer prompt shows it: a heading, the columns, numbered rows, and its notes. */\nexport function aiInsightTableText(table: AiInsightTable): string {\n const period = table.period ? ` · ${table.period.from} to ${table.period.to} (${table.period.days} days)` : ''\n const lines = [\n `${table.ref} · ${table.title} (${table.source.label})${period}`,\n `Columns: ${table.columns.map((column) => column.label).join(' | ')}`,\n ...table.rows.map(\n (row, index) =>\n `${index}: ${table.columns.map((column) => aiInsightCellText(table, column.key, row[column.key])).join(' | ')}`,\n ),\n ]\n if (!table.rows.length) lines.push('(no rows)')\n if (table.omitted) lines.push(`${table.omitted} more rows were left out of this table.`)\n for (const note of table.notes) lines.push(`Note: ${note}`)\n return lines.join('\\n')\n}\n"],"names":["checkEntitlement","isHostPluginEnabled","isPluginEnabled","listPluginFigureReaders","normalizePluginFigureTable","pluginFigureParams","pluginFigureReaderPlugin","AI_INSIGHT_MAX_READS","AI_INSIGHT_SURFACE_READERS","aiInsightCellText","filterEnabledPluginsByReleaseFlags","releasedByFlags","pluginIds","orgId","authorization","aiInsightReaders","context","prefixes","surface","org","candidates","filter","reader","includes","id","split","scope","hostId","feature","plugins","Set","map","Boolean","released","length","resolved","plugin","has","host","aiInsightReaderDays","windows","asked","sort","a","b","Math","abs","aiInsightReaderCatalog","readers","join","params","param","name","required","description","label","aiInsightDatasetCatalog","datasets","dataset","fields","field","type","readAiInsightTables","requests","base","maxTables","tables","refusals","seen","request","find","days","ok","push","missing","key","JSON","stringify","add","read","error","table","ref","console","aiInsightTableText","period","from","to","lines","title","source","columns","column","rows","row","index","omitted","note","notes"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,gBAAgB,QAAQ,2CAA0C;AAC3E,SAASC,mBAAmB,EAAEC,eAAe,QAAQ,8CAA6C;AAClG,SACEC,uBAAuB,EACvBC,0BAA0B,EAC1BC,kBAAkB,EAClBC,wBAAwB,QAEnB,6CAA4C;AACnD,SACEC,oBAAoB,EACpBC,0BAA0B,EAC1BC,iBAAiB,QAGZ,yBAAqB;AAC5B,SAASC,kCAAkC,QAAQ,gDAA+C;AAmBlG,MAAMC,kBAAqC,CAACC,WAAWC,QACrDH,mCAAmCE,WAAW;QAAEC;QAAOC,eAAe;IAAK;AAc7E,mHAAmH,GACnH,OAAO,eAAeC,iBAAiBC,OAA+B;QAUnBA;IATjD,MAAMC,WAAWT,0BAA0B,CAACQ,QAAQE,OAAO,CAAC;IAC5D,MAAMC,MAAMH,QAAQG,GAAG;IACvB,MAAMC,aAAajB,0BAA0BkB,MAAM,CAAC,CAAC,EAAEC,MAAM,EAAE;QAC7D,IAAI,CAACL,SAASM,QAAQ,CAACD,OAAOE,EAAE,CAACC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO;QACxD,4EAA4E;QAC5E,IAAIH,OAAOI,KAAK,KAAK,UAAU,CAACV,QAAQW,MAAM,EAAE,OAAO;QACvD,OAAO,CAACL,OAAOM,OAAO,IAAI5B,iBAAiBmB,KAAcG,OAAOM,OAAO;IACzE;IACA,MAAMC,UAAU;WAAI,IAAIC,IAAIV,WAAWW,GAAG,CAACzB,0BAA0Be,MAAM,CAAC,CAACG,KAAqBQ,QAAQR;KAAM;IAChH,MAAMS,WAAW,IAAIH,IAAID,QAAQK,MAAM,GAAG,MAAM,EAAClB,oBAAAA,QAAQiB,QAAQ,YAAhBjB,oBAAoBL,iBAAiBkB,SAASb,QAAQH,KAAK,IAAI,EAAE;IAClH,OAAOO,WAAWC,MAAM,CAAC,CAACc;QACxB,MAAMC,SAAS9B,yBAAyB6B;QACxC,IAAI,CAACC,QAAQ,OAAO;QACpB,IAAI,CAACH,SAASI,GAAG,CAACD,SAAS,OAAO;QAClC,OAAOpB,QAAQW,MAAM,GACjB1B,oBAAoBkB,KAAKH,QAAQsB,IAAI,EAA2CF,UAChFlC,gBAAgBiB,KAAKiB;IAC3B;AACF;AAEA,qGAAqG,GACrG,OAAO,SAASG,oBAAoBC,OAA0B,EAAEC,KAAa;IAC3E,IAAI,CAACD,QAAQN,MAAM,EAAE,OAAO;IAC5B,IAAIM,QAAQjB,QAAQ,CAACkB,QAAQ,OAAOA;IACpC,OAAO;WAAID;KAAQ,CAACE,IAAI,CAAC,CAACC,GAAGC,IAAMC,KAAKC,GAAG,CAACH,IAAIF,SAASI,KAAKC,GAAG,CAACF,IAAIH,UAAUE,IAAIC,EAAE,CAAC,EAAE;AAC3F;AAEA,sGAAsG,GACtG,OAAO,SAASG,uBAAuBC,OAA8C;IACnF,OAAOA,QACJjB,GAAG,CAAC,CAAC,EAAET,MAAM,EAAE;YAIEA,gBACKA;QAJrB,MAAMkB,UAAUlB,OAAOkB,OAAO,CAACN,MAAM,GACjC,CAAC,UAAU,EAAEZ,OAAOkB,OAAO,CAACS,IAAI,CAAC,MAAM,MAAM,CAAC,GAC9C;QACJ,MAAMC,SAAS,EAAC5B,iBAAAA,OAAO4B,MAAM,YAAb5B,iBAAiB,EAAE,EAAEY,MAAM,GACvC,CAAC,aAAa,EAAE,EAACZ,kBAAAA,OAAO4B,MAAM,YAAb5B,kBAAiB,EAAE,EACjCS,GAAG,CAAC,CAACoB,QAAU,GAAGA,MAAMC,IAAI,CAAC,EAAE,EAAED,MAAME,QAAQ,GAAG,aAAa,WAAW,IAAI,EAAEF,MAAMG,WAAW,EAAE,EACnGL,IAAI,CAAC,OAAO,GACf;QACJ,OAAO,CAAC,EAAE,EAAE3B,OAAOE,EAAE,CAAC,GAAG,EAAEF,OAAOiC,KAAK,CAAC,EAAE,EAAEjC,OAAOgC,WAAW,GAAGd,UAAUU,QAAQ;IACrF,GACCD,IAAI,CAAC;AACV;AAEA,8EAA8E,GAC9E,OAAO,SAASO,wBAAwBC,QAA0C;IAChF,IAAI,CAACA,SAASvB,MAAM,EAAE,OAAO;IAC7B,OAAOuB,SACJ1B,GAAG,CACF,CAAC2B,UACC,CAAC,EAAE,EAAEA,QAAQN,IAAI,CAAC,KAAK,EAAEM,QAAQlC,EAAE,CAAC,GAAG,EAAEkC,QAAQC,MAAM,CAAC5B,GAAG,CAAC,CAAC6B,QAAU,GAAGA,MAAMR,IAAI,CAAC,EAAE,EAAEQ,MAAMC,IAAI,CAAC,CAAC,CAAC,EAAEZ,IAAI,CAAC,SAAS,aAAa,EAEtIA,IAAI,CAAC;AACV;AAQA;;;;;CAKC,GACD,OAAO,eAAea,oBACpBd,OAA8C,EAC9Ce,QAAyC,EACzCC,IAA6E,EAC7EC,YAAoB1D,oBAAoB;IAExC,MAAM2D,SAA2B,EAAE;IACnC,MAAMC,WAAqB,EAAE;IAC7B,MAAMC,OAAO,IAAItC;IACjB,KAAK,MAAMuC,WAAWN,SAAU;QAC9B,IAAIG,OAAOhC,MAAM,KAAK+B,WAAW;QACjC,MAAM9B,WAAWa,QAAQsB,IAAI,CAAC,CAAC,EAAEhD,MAAM,EAAE,GAAKA,OAAOE,EAAE,KAAK6C,QAAQ/C,MAAM;QAC1E,IAAI,CAACa,UAAU;QACf,MAAM,EAAEb,MAAM,EAAE,GAAGa;QACnB,MAAMoC,OAAOhC,oBAAoBjB,OAAOkB,OAAO,EAAE6B,QAAQE,IAAI;QAC7D,MAAMrB,SAAS7C,mBAAmBiB,QAAQ+C,QAAQnB,MAAM;QACxD,IAAIA,OAAOsB,EAAE,KAAK,OAAO;YACvBL,SAASM,IAAI,CAAC,GAAGnD,OAAOE,EAAE,CAAC,UAAU,EAAE0B,OAAOwB,OAAO,EAAE;YACvD;QACF;QACA,MAAMC,MAAMC,KAAKC,SAAS,CAAC;YAACvD,OAAOE,EAAE;YAAE+C;YAAMrB,OAAOA,MAAM;SAAC;QAC3D,IAAIkB,KAAK/B,GAAG,CAACsC,MAAM;QACnBP,KAAKU,GAAG,CAACH;QACT,IAAI;YACF,MAAMI,OAAO,MAAMzD,OAAOyD,IAAI,CAAC,aAAKf;gBAAMO;gBAAMrB,QAAQA,OAAOA,MAAM;;YACrE,IAAI6B,KAAKP,EAAE,KAAK,OAAO;gBACrBL,SAASM,IAAI,CAAC,GAAGnD,OAAOE,EAAE,CAAC,EAAE,EAAEuD,KAAKC,KAAK,EAAE;gBAC3C;YACF;YACA,MAAMC,QAAQ7E,2BAA2B2E,KAAKE,KAAK;YACnD,IAAI,CAACA,OAAO;gBACVd,SAASM,IAAI,CAAC,GAAGnD,OAAOE,EAAE,CAAC,aAAa,CAAC;gBACzC;YACF;YACA0C,OAAOO,IAAI,CAAC,aAAKQ;gBAAOC,KAAK,CAAC,CAAC,EAAEhB,OAAOhC,MAAM,GAAG,GAAG;gBAAEZ,QAAQA,OAAOE,EAAE;gBAAE+C;gBAAM7C,OAAOJ,OAAOI,KAAK;;QACpG,EAAE,OAAOsD,OAAO;YACd,mEAAmE;YACnEG,QAAQH,KAAK,CAAC,4BAA4B;gBAAE1D,QAAQA,OAAOE,EAAE;gBAAEX,OAAOmD,KAAKnD,KAAK;gBAAEmE;YAAM;YACxFb,SAASM,IAAI,CAAC,GAAGnD,OAAOE,EAAE,CAAC,QAAQ,CAAC;QACtC;IACF;IACA,OAAO;QAAE0C;QAAQC;IAAS;AAC5B;AAEA,mGAAmG,GACnG,OAAO,SAASiB,mBAAmBH,KAAqB;IACtD,MAAMI,SAASJ,MAAMI,MAAM,GAAG,CAAC,GAAG,EAAEJ,MAAMI,MAAM,CAACC,IAAI,CAAC,IAAI,EAAEL,MAAMI,MAAM,CAACE,EAAE,CAAC,EAAE,EAAEN,MAAMI,MAAM,CAACd,IAAI,CAAC,MAAM,CAAC,GAAG;IAC5G,MAAMiB,QAAQ;QACZ,GAAGP,MAAMC,GAAG,CAAC,GAAG,EAAED,MAAMQ,KAAK,CAAC,EAAE,EAAER,MAAMS,MAAM,CAACnC,KAAK,CAAC,CAAC,EAAE8B,QAAQ;QAChE,CAAC,SAAS,EAAEJ,MAAMU,OAAO,CAAC5D,GAAG,CAAC,CAAC6D,SAAWA,OAAOrC,KAAK,EAAEN,IAAI,CAAC,QAAQ;WAClEgC,MAAMY,IAAI,CAAC9D,GAAG,CACf,CAAC+D,KAAKC,QACJ,GAAGA,MAAM,EAAE,EAAEd,MAAMU,OAAO,CAAC5D,GAAG,CAAC,CAAC6D,SAAWnF,kBAAkBwE,OAAOW,OAAOjB,GAAG,EAAEmB,GAAG,CAACF,OAAOjB,GAAG,CAAC,GAAG1B,IAAI,CAAC,QAAQ;KAEpH;IACD,IAAI,CAACgC,MAAMY,IAAI,CAAC3D,MAAM,EAAEsD,MAAMf,IAAI,CAAC;IACnC,IAAIQ,MAAMe,OAAO,EAAER,MAAMf,IAAI,CAAC,GAAGQ,MAAMe,OAAO,CAAC,uCAAuC,CAAC;IACvF,KAAK,MAAMC,QAAQhB,MAAMiB,KAAK,CAAEV,MAAMf,IAAI,CAAC,CAAC,MAAM,EAAEwB,MAAM;IAC1D,OAAOT,MAAMvC,IAAI,CAAC;AACpB"}
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
-
import { type CampaignSendTimeSuggestion } from '@aglyn/shared-ui-email-campaigns/model/campaign-send-time';
|
|
18
17
|
/**
|
|
19
18
|
* What an email or campaign job binds and suggests WITHOUT the model
|
|
20
19
|
* (AGL-2912).
|
|
@@ -32,19 +31,28 @@ import { type CampaignSendTimeSuggestion } from '@aglyn/shared-ui-email-campaign
|
|
|
32
31
|
* - THE LIST a campaign is for is suggested when the brief names one of the
|
|
33
32
|
* org's lists by name, and is set on nothing: the drafted email is aimed at
|
|
34
33
|
* nobody until a person chooses.
|
|
35
|
-
* - THE SEND TIME is
|
|
36
|
-
*
|
|
37
|
-
*
|
|
34
|
+
* - THE SEND TIME is asked of the plugin that keeps the list's past sends,
|
|
35
|
+
* through the core's record-facts seam, which answers the slot and how
|
|
36
|
+
* many sends it was chosen among — and is said only in the output's note.
|
|
38
37
|
*/
|
|
39
|
-
type Firestore = FirebaseFirestore.Firestore;
|
|
40
38
|
/** The most products one email binds. */
|
|
41
39
|
export declare const AI_EMAIL_MAX_PRODUCTS = 6;
|
|
42
40
|
/** How many of a site's products are read for the names a brief might use. */
|
|
43
41
|
export declare const AI_EMAIL_PRODUCT_SCAN = 200;
|
|
44
42
|
/** How many of an org's lists are read: the composer's list picker window. */
|
|
45
43
|
export declare const AI_CAMPAIGN_LIST_SCAN = 50;
|
|
46
|
-
/**
|
|
47
|
-
|
|
44
|
+
/**
|
|
45
|
+
* The resource a list's send time is read as, by the list's id, through
|
|
46
|
+
* `plugin-record-facts`. Its owner answers `{ label, measured }`: the slot as
|
|
47
|
+
* a person reads it (or `null` with too little history) and the sends it was
|
|
48
|
+
* chosen among.
|
|
49
|
+
*/
|
|
50
|
+
export declare const AI_LIST_SEND_TIME_RESOURCE = "listSendTime";
|
|
51
|
+
/** A list's suggested send time, as the note says it. */
|
|
52
|
+
export interface AiListSendTime {
|
|
53
|
+
label: string;
|
|
54
|
+
measured: number;
|
|
55
|
+
}
|
|
48
56
|
/** A record a brief may name, by its id and its name. */
|
|
49
57
|
export interface AiNamedRecord {
|
|
50
58
|
id: string;
|
|
@@ -70,9 +78,11 @@ export interface AiEmailProductBinding {
|
|
|
70
78
|
}
|
|
71
79
|
/**
|
|
72
80
|
* The products an email binds: the member's picks that still exist, else the
|
|
73
|
-
* ones the brief names. Reads ids and names
|
|
81
|
+
* ones the brief names. Reads ids and names through the `product` index the
|
|
82
|
+
* plugin that keeps products publishes (AGL-3080), and returns only ids; none
|
|
83
|
+
* where no plugin keeps products here.
|
|
74
84
|
*/
|
|
75
|
-
export declare function resolveAiEmailProducts(
|
|
85
|
+
export declare function resolveAiEmailProducts(input: {
|
|
76
86
|
hostId: string;
|
|
77
87
|
brief: string;
|
|
78
88
|
inputs: Readonly<Record<string, unknown>> | undefined;
|
|
@@ -86,22 +96,19 @@ export declare function suggestAiCampaignList(input: {
|
|
|
86
96
|
brief: string;
|
|
87
97
|
}): Promise<AiNamedRecord | null>;
|
|
88
98
|
/**
|
|
89
|
-
* The send time a list's past sends on this site suggest,
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* Sends are the organization's (`orgs/{orgId}/campaigns`), and a list is too,
|
|
93
|
-
* so the org's sends to the list are narrowed to the ones sent AS this site
|
|
94
|
-
* (`hostId`): another site's audience opening at another hour is not this
|
|
95
|
-
* site's history. Three equality filters, which Firestore serves by merging
|
|
96
|
-
* single-field indexes — no composite index.
|
|
99
|
+
* The send time a list's past sends on this site suggest, asked of the plugin
|
|
100
|
+
* that keeps the sends: `null` when nothing keeps them here, when the owner
|
|
101
|
+
* refuses, or when there is too little history to suggest one.
|
|
97
102
|
*/
|
|
98
|
-
export declare function readAiListSendTime(
|
|
103
|
+
export declare function readAiListSendTime(input: {
|
|
99
104
|
orgId: string;
|
|
100
105
|
hostId: string;
|
|
101
106
|
listId: string;
|
|
102
|
-
|
|
107
|
+
uid: string;
|
|
108
|
+
org: Readonly<Record<string, unknown>> | null;
|
|
109
|
+
now: Date;
|
|
110
|
+
}): Promise<AiListSendTime | null>;
|
|
103
111
|
/** What an email's output says about its products; `null` when it binds none and none were picked. */
|
|
104
112
|
export declare function aiEmailProductNote(binding: AiEmailProductBinding): string | null;
|
|
105
113
|
/** What a campaign's output says about who it is for and when to send it. */
|
|
106
|
-
export declare function aiCampaignAudienceNote(list: AiNamedRecord | null, sendTime:
|
|
107
|
-
export {};
|
|
114
|
+
export declare function aiCampaignAudienceNote(list: AiNamedRecord | null, sendTime: AiListSendTime | null): string;
|
|
@@ -13,12 +13,38 @@
|
|
|
13
13
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
|
-
*/ import {
|
|
16
|
+
*/ import { pluginRecordIndex } from "@aglyn/aglyn/plugin-manager/plugin-record-index";
|
|
17
|
+
import { pluginRecordFactsReader } from "@aglyn/aglyn/plugin-manager/plugin-record-facts";
|
|
17
18
|
import { orgDataCollectionForHost } from "@aglyn/tenant-data-admin/server/organizations";
|
|
18
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* What an email or campaign job binds and suggests WITHOUT the model
|
|
21
|
+
* (AGL-2912).
|
|
22
|
+
*
|
|
23
|
+
* What AI generation is published to send is the brief, a summary of the
|
|
24
|
+
* site and the content being worked on — not email lists, contacts,
|
|
25
|
+
* recipients, CRM records, product records or engagement statistics. So
|
|
26
|
+
* everything here that touches one of those runs in code, on the server, and
|
|
27
|
+
* hands the model at most a count:
|
|
28
|
+
*
|
|
29
|
+
* - PRODUCTS are bound by id: the ones the member picked for the job, else
|
|
30
|
+
* the ones the brief names by their exact name. The model learns only how
|
|
31
|
+
* many product blocks to place; the ids are set on those blocks after it
|
|
32
|
+
* answers, and the send fills each block's name, price and picture.
|
|
33
|
+
* - THE LIST a campaign is for is suggested when the brief names one of the
|
|
34
|
+
* org's lists by name, and is set on nothing: the drafted email is aimed at
|
|
35
|
+
* nobody until a person chooses.
|
|
36
|
+
* - THE SEND TIME is asked of the plugin that keeps the list's past sends,
|
|
37
|
+
* through the core's record-facts seam, which answers the slot and how
|
|
38
|
+
* many sends it was chosen among — and is said only in the output's note.
|
|
39
|
+
*/ /** The most products one email binds. */ export const AI_EMAIL_MAX_PRODUCTS = 6;
|
|
19
40
|
/** How many of a site's products are read for the names a brief might use. */ export const AI_EMAIL_PRODUCT_SCAN = 200;
|
|
20
41
|
/** How many of an org's lists are read: the composer's list picker window. */ export const AI_CAMPAIGN_LIST_SCAN = 50;
|
|
21
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* The resource a list's send time is read as, by the list's id, through
|
|
44
|
+
* `plugin-record-facts`. Its owner answers `{ label, measured }`: the slot as
|
|
45
|
+
* a person reads it (or `null` with too little history) and the sends it was
|
|
46
|
+
* chosen among.
|
|
47
|
+
*/ export const AI_LIST_SEND_TIME_RESOURCE = 'listSendTime';
|
|
22
48
|
/** Lowercase words joined by single spaces, for matching a name inside text. */ function wordsOf(value) {
|
|
23
49
|
return value.toLowerCase().replace(RegExp("[^\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376-\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u064A\\u066E-\\u066F\\u0671-\\u06D3\\u06D5\\u06E5-\\u06E6\\u06EE-\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4-\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086A\\u0870-\\u0887\\u0889-\\u088F\\u08A0-\\u08C9\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F-\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC-\\u09DD\\u09DF-\\u09E1\\u09F0-\\u09F1\\u09FC\\u0A05-\\u0A0A\\u0A0F-\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32-\\u0A33\\u0A35-\\u0A36\\u0A38-\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2-\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0-\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F-\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32-\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C-\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99-\\u0B9A\\u0B9C\\u0B9E-\\u0B9F\\u0BA3-\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C5C-\\u0C5D\\u0C60-\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDC-\\u0CDE\\u0CE0-\\u0CE1\\u0CF1-\\u0CF2\\u0D04-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32-\\u0E33\\u0E40-\\u0E46\\u0E81-\\u0E82\\u0E84\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EB0\\u0EB2-\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065-\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u1711\\u171F-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1878\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4C\\u1B83-\\u1BA0\\u1BAE-\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C8A\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1CE9-\\u1CEC\\u1CEE-\\u1CF3\\u1CF5-\\u1CF6\\u1CFA\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183-\\u2184\\u2C00-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3006\\u3031-\\u3035\\u303B-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312F\\u3131-\\u318E\\u31A0-\\u31BF\\u31F0-\\u31FF\\u3400-\\u4DBF\\u4E00-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A-\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7DC\\uA7F1-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD-\\uA8FE\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5-\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB69\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40-\\uFB41\\uFB43-\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC\\u{10000}-\\u{1000B}\\u{1000D}-\\u{10026}\\u{10028}-\\u{1003A}\\u{1003C}-\\u{1003D}\\u{1003F}-\\u{1004D}\\u{10050}-\\u{1005D}\\u{10080}-\\u{100FA}\\u{10280}-\\u{1029C}\\u{102A0}-\\u{102D0}\\u{10300}-\\u{1031F}\\u{1032D}-\\u{10340}\\u{10342}-\\u{10349}\\u{10350}-\\u{10375}\\u{10380}-\\u{1039D}\\u{103A0}-\\u{103C3}\\u{103C8}-\\u{103CF}\\u{10400}-\\u{1049D}\\u{104B0}-\\u{104D3}\\u{104D8}-\\u{104FB}\\u{10500}-\\u{10527}\\u{10530}-\\u{10563}\\u{10570}-\\u{1057A}\\u{1057C}-\\u{1058A}\\u{1058C}-\\u{10592}\\u{10594}-\\u{10595}\\u{10597}-\\u{105A1}\\u{105A3}-\\u{105B1}\\u{105B3}-\\u{105B9}\\u{105BB}-\\u{105BC}\\u{105C0}-\\u{105F3}\\u{10600}-\\u{10736}\\u{10740}-\\u{10755}\\u{10760}-\\u{10767}\\u{10780}-\\u{10785}\\u{10787}-\\u{107B0}\\u{107B2}-\\u{107BA}\\u{10800}-\\u{10805}\\u{10808}\\u{1080A}-\\u{10835}\\u{10837}-\\u{10838}\\u{1083C}\\u{1083F}-\\u{10855}\\u{10860}-\\u{10876}\\u{10880}-\\u{1089E}\\u{108E0}-\\u{108F2}\\u{108F4}-\\u{108F5}\\u{10900}-\\u{10915}\\u{10920}-\\u{10939}\\u{10940}-\\u{10959}\\u{10980}-\\u{109B7}\\u{109BE}-\\u{109BF}\\u{10A00}\\u{10A10}-\\u{10A13}\\u{10A15}-\\u{10A17}\\u{10A19}-\\u{10A35}\\u{10A60}-\\u{10A7C}\\u{10A80}-\\u{10A9C}\\u{10AC0}-\\u{10AC7}\\u{10AC9}-\\u{10AE4}\\u{10B00}-\\u{10B35}\\u{10B40}-\\u{10B55}\\u{10B60}-\\u{10B72}\\u{10B80}-\\u{10B91}\\u{10C00}-\\u{10C48}\\u{10C80}-\\u{10CB2}\\u{10CC0}-\\u{10CF2}\\u{10D00}-\\u{10D23}\\u{10D4A}-\\u{10D65}\\u{10D6F}-\\u{10D85}\\u{10E80}-\\u{10EA9}\\u{10EB0}-\\u{10EB1}\\u{10EC2}-\\u{10EC7}\\u{10F00}-\\u{10F1C}\\u{10F27}\\u{10F30}-\\u{10F45}\\u{10F70}-\\u{10F81}\\u{10FB0}-\\u{10FC4}\\u{10FE0}-\\u{10FF6}\\u{11003}-\\u{11037}\\u{11071}-\\u{11072}\\u{11075}\\u{11083}-\\u{110AF}\\u{110D0}-\\u{110E8}\\u{11103}-\\u{11126}\\u{11144}\\u{11147}\\u{11150}-\\u{11172}\\u{11176}\\u{11183}-\\u{111B2}\\u{111C1}-\\u{111C4}\\u{111DA}\\u{111DC}\\u{11200}-\\u{11211}\\u{11213}-\\u{1122B}\\u{1123F}-\\u{11240}\\u{11280}-\\u{11286}\\u{11288}\\u{1128A}-\\u{1128D}\\u{1128F}-\\u{1129D}\\u{1129F}-\\u{112A8}\\u{112B0}-\\u{112DE}\\u{11305}-\\u{1130C}\\u{1130F}-\\u{11310}\\u{11313}-\\u{11328}\\u{1132A}-\\u{11330}\\u{11332}-\\u{11333}\\u{11335}-\\u{11339}\\u{1133D}\\u{11350}\\u{1135D}-\\u{11361}\\u{11380}-\\u{11389}\\u{1138B}\\u{1138E}\\u{11390}-\\u{113B5}\\u{113B7}\\u{113D1}\\u{113D3}\\u{11400}-\\u{11434}\\u{11447}-\\u{1144A}\\u{1145F}-\\u{11461}\\u{11480}-\\u{114AF}\\u{114C4}-\\u{114C5}\\u{114C7}\\u{11580}-\\u{115AE}\\u{115D8}-\\u{115DB}\\u{11600}-\\u{1162F}\\u{11644}\\u{11680}-\\u{116AA}\\u{116B8}\\u{11700}-\\u{1171A}\\u{11740}-\\u{11746}\\u{11800}-\\u{1182B}\\u{118A0}-\\u{118DF}\\u{118FF}-\\u{11906}\\u{11909}\\u{1190C}-\\u{11913}\\u{11915}-\\u{11916}\\u{11918}-\\u{1192F}\\u{1193F}\\u{11941}\\u{119A0}-\\u{119A7}\\u{119AA}-\\u{119D0}\\u{119E1}\\u{119E3}\\u{11A00}\\u{11A0B}-\\u{11A32}\\u{11A3A}\\u{11A50}\\u{11A5C}-\\u{11A89}\\u{11A9D}\\u{11AB0}-\\u{11AF8}\\u{11BC0}-\\u{11BE0}\\u{11C00}-\\u{11C08}\\u{11C0A}-\\u{11C2E}\\u{11C40}\\u{11C72}-\\u{11C8F}\\u{11D00}-\\u{11D06}\\u{11D08}-\\u{11D09}\\u{11D0B}-\\u{11D30}\\u{11D46}\\u{11D60}-\\u{11D65}\\u{11D67}-\\u{11D68}\\u{11D6A}-\\u{11D89}\\u{11D98}\\u{11DB0}-\\u{11DDB}\\u{11EE0}-\\u{11EF2}\\u{11F02}\\u{11F04}-\\u{11F10}\\u{11F12}-\\u{11F33}\\u{11FB0}\\u{12000}-\\u{12399}\\u{12480}-\\u{12543}\\u{12F90}-\\u{12FF0}\\u{13000}-\\u{1342F}\\u{13441}-\\u{13446}\\u{13460}-\\u{143FA}\\u{14400}-\\u{14646}\\u{16100}-\\u{1611D}\\u{16800}-\\u{16A38}\\u{16A40}-\\u{16A5E}\\u{16A70}-\\u{16ABE}\\u{16AD0}-\\u{16AED}\\u{16B00}-\\u{16B2F}\\u{16B40}-\\u{16B43}\\u{16B63}-\\u{16B77}\\u{16B7D}-\\u{16B8F}\\u{16D40}-\\u{16D6C}\\u{16E40}-\\u{16E7F}\\u{16EA0}-\\u{16EB8}\\u{16EBB}-\\u{16ED3}\\u{16F00}-\\u{16F4A}\\u{16F50}\\u{16F93}-\\u{16F9F}\\u{16FE0}-\\u{16FE1}\\u{16FE3}\\u{16FF2}-\\u{16FF3}\\u{17000}-\\u{18CD5}\\u{18CFF}-\\u{18D1E}\\u{18D80}-\\u{18DF2}\\u{1AFF0}-\\u{1AFF3}\\u{1AFF5}-\\u{1AFFB}\\u{1AFFD}-\\u{1AFFE}\\u{1B000}-\\u{1B122}\\u{1B132}\\u{1B150}-\\u{1B152}\\u{1B155}\\u{1B164}-\\u{1B167}\\u{1B170}-\\u{1B2FB}\\u{1BC00}-\\u{1BC6A}\\u{1BC70}-\\u{1BC7C}\\u{1BC80}-\\u{1BC88}\\u{1BC90}-\\u{1BC99}\\u{1D400}-\\u{1D454}\\u{1D456}-\\u{1D49C}\\u{1D49E}-\\u{1D49F}\\u{1D4A2}\\u{1D4A5}-\\u{1D4A6}\\u{1D4A9}-\\u{1D4AC}\\u{1D4AE}-\\u{1D4B9}\\u{1D4BB}\\u{1D4BD}-\\u{1D4C3}\\u{1D4C5}-\\u{1D505}\\u{1D507}-\\u{1D50A}\\u{1D50D}-\\u{1D514}\\u{1D516}-\\u{1D51C}\\u{1D51E}-\\u{1D539}\\u{1D53B}-\\u{1D53E}\\u{1D540}-\\u{1D544}\\u{1D546}\\u{1D54A}-\\u{1D550}\\u{1D552}-\\u{1D6A5}\\u{1D6A8}-\\u{1D6C0}\\u{1D6C2}-\\u{1D6DA}\\u{1D6DC}-\\u{1D6FA}\\u{1D6FC}-\\u{1D714}\\u{1D716}-\\u{1D734}\\u{1D736}-\\u{1D74E}\\u{1D750}-\\u{1D76E}\\u{1D770}-\\u{1D788}\\u{1D78A}-\\u{1D7A8}\\u{1D7AA}-\\u{1D7C2}\\u{1D7C4}-\\u{1D7CB}\\u{1DF00}-\\u{1DF1E}\\u{1DF25}-\\u{1DF2A}\\u{1E030}-\\u{1E06D}\\u{1E100}-\\u{1E12C}\\u{1E137}-\\u{1E13D}\\u{1E14E}\\u{1E290}-\\u{1E2AD}\\u{1E2C0}-\\u{1E2EB}\\u{1E4D0}-\\u{1E4EB}\\u{1E5D0}-\\u{1E5ED}\\u{1E5F0}\\u{1E6C0}-\\u{1E6DE}\\u{1E6E0}-\\u{1E6E2}\\u{1E6E4}-\\u{1E6E5}\\u{1E6E7}-\\u{1E6ED}\\u{1E6F0}-\\u{1E6F4}\\u{1E6FE}-\\u{1E6FF}\\u{1E7E0}-\\u{1E7E6}\\u{1E7E8}-\\u{1E7EB}\\u{1E7ED}-\\u{1E7EE}\\u{1E7F0}-\\u{1E7FE}\\u{1E800}-\\u{1E8C4}\\u{1E900}-\\u{1E943}\\u{1E94B}\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u{20000}-\\u{2A6DF}\\u{2A700}-\\u{2B81D}\\u{2B820}-\\u{2CEAD}\\u{2CEB0}-\\u{2EBE0}\\u{2EBF0}-\\u{2EE5D}\\u{2F800}-\\u{2FA1D}\\u{30000}-\\u{3134A}\\u{31350}-\\u{33479}\\u0030-\\u0039\\u00B2-\\u00B3\\u00B9\\u00BC-\\u00BE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D58-\\u0D5E\\u0D66-\\u0D78\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19\\u{10107}-\\u{10133}\\u{10140}-\\u{10178}\\u{1018A}-\\u{1018B}\\u{102E1}-\\u{102FB}\\u{10320}-\\u{10323}\\u{10341}\\u{1034A}\\u{103D1}-\\u{103D5}\\u{104A0}-\\u{104A9}\\u{10858}-\\u{1085F}\\u{10879}-\\u{1087F}\\u{108A7}-\\u{108AF}\\u{108FB}-\\u{108FF}\\u{10916}-\\u{1091B}\\u{109BC}-\\u{109BD}\\u{109C0}-\\u{109CF}\\u{109D2}-\\u{109FF}\\u{10A40}-\\u{10A48}\\u{10A7D}-\\u{10A7E}\\u{10A9D}-\\u{10A9F}\\u{10AEB}-\\u{10AEF}\\u{10B58}-\\u{10B5F}\\u{10B78}-\\u{10B7F}\\u{10BA9}-\\u{10BAF}\\u{10CFA}-\\u{10CFF}\\u{10D30}-\\u{10D39}\\u{10D40}-\\u{10D49}\\u{10E60}-\\u{10E7E}\\u{10F1D}-\\u{10F26}\\u{10F51}-\\u{10F54}\\u{10FC5}-\\u{10FCB}\\u{11052}-\\u{1106F}\\u{110F0}-\\u{110F9}\\u{11136}-\\u{1113F}\\u{111D0}-\\u{111D9}\\u{111E1}-\\u{111F4}\\u{112F0}-\\u{112F9}\\u{11450}-\\u{11459}\\u{114D0}-\\u{114D9}\\u{11650}-\\u{11659}\\u{116C0}-\\u{116C9}\\u{116D0}-\\u{116E3}\\u{11730}-\\u{1173B}\\u{118E0}-\\u{118F2}\\u{11950}-\\u{11959}\\u{11BF0}-\\u{11BF9}\\u{11C50}-\\u{11C6C}\\u{11D50}-\\u{11D59}\\u{11DA0}-\\u{11DA9}\\u{11DE0}-\\u{11DE9}\\u{11F50}-\\u{11F59}\\u{11FC0}-\\u{11FD4}\\u{12400}-\\u{1246E}\\u{16130}-\\u{16139}\\u{16A60}-\\u{16A69}\\u{16AC0}-\\u{16AC9}\\u{16B50}-\\u{16B59}\\u{16B5B}-\\u{16B61}\\u{16D70}-\\u{16D79}\\u{16E80}-\\u{16E96}\\u{16FF4}-\\u{16FF6}\\u{1CCF0}-\\u{1CCF9}\\u{1D2C0}-\\u{1D2D3}\\u{1D2E0}-\\u{1D2F3}\\u{1D360}-\\u{1D378}\\u{1D7CE}-\\u{1D7FF}\\u{1E140}-\\u{1E149}\\u{1E2F0}-\\u{1E2F9}\\u{1E4F0}-\\u{1E4F9}\\u{1E5F1}-\\u{1E5FA}\\u{1E8C7}-\\u{1E8CF}\\u{1E950}-\\u{1E959}\\u{1EC71}-\\u{1ECAB}\\u{1ECAD}-\\u{1ECAF}\\u{1ECB1}-\\u{1ECB4}\\u{1ED01}-\\u{1ED2D}\\u{1ED2F}-\\u{1ED3D}\\u{1F100}-\\u{1F10C}\\u{1FBF0}-\\u{1FBF9}]+", "gu"), ' ').trim();
|
|
24
50
|
}
|
|
@@ -62,27 +88,36 @@ import { orgDataCollectionForHost } from "@aglyn/tenant-data-admin/server/organi
|
|
|
62
88
|
}
|
|
63
89
|
/**
|
|
64
90
|
* The products an email binds: the member's picks that still exist, else the
|
|
65
|
-
* ones the brief names. Reads ids and names
|
|
66
|
-
|
|
67
|
-
|
|
91
|
+
* ones the brief names. Reads ids and names through the `product` index the
|
|
92
|
+
* plugin that keeps products publishes (AGL-3080), and returns only ids; none
|
|
93
|
+
* where no plugin keeps products here.
|
|
94
|
+
*/ export async function resolveAiEmailProducts(input) {
|
|
95
|
+
var _pluginRecordIndex;
|
|
68
96
|
const picked = aiEmailPickedProductIds(input.inputs);
|
|
97
|
+
const index = (_pluginRecordIndex = pluginRecordIndex('product')) == null ? void 0 : _pluginRecordIndex.index;
|
|
98
|
+
if (!index) return {
|
|
99
|
+
ids: [],
|
|
100
|
+
picked: picked.length
|
|
101
|
+
};
|
|
69
102
|
if (picked.length) {
|
|
70
|
-
const
|
|
103
|
+
const records = await Promise.all(picked.map((id)=>index.get({
|
|
104
|
+
hostId: input.hostId,
|
|
105
|
+
id
|
|
106
|
+
})));
|
|
71
107
|
return {
|
|
72
|
-
ids:
|
|
108
|
+
ids: records.filter((record)=>record !== null).map((record)=>record.id),
|
|
73
109
|
picked: picked.length
|
|
74
110
|
};
|
|
75
111
|
}
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
id: doc.id,
|
|
81
|
-
name: String((_doc_get = doc.get('name')) != null ? _doc_get : '')
|
|
82
|
-
};
|
|
112
|
+
const { records } = await index.list({
|
|
113
|
+
hostId: input.hostId,
|
|
114
|
+
limit: AI_EMAIL_PRODUCT_SCAN
|
|
83
115
|
});
|
|
84
116
|
return {
|
|
85
|
-
ids: aiRecordsNamedIn(input.brief,
|
|
117
|
+
ids: aiRecordsNamedIn(input.brief, records.map((record)=>({
|
|
118
|
+
id: record.id,
|
|
119
|
+
name: record.name
|
|
120
|
+
})), AI_EMAIL_MAX_PRODUCTS).map((row)=>row.id),
|
|
86
121
|
picked: 0
|
|
87
122
|
};
|
|
88
123
|
}
|
|
@@ -105,32 +140,30 @@ import { orgDataCollectionForHost } from "@aglyn/tenant-data-admin/server/organi
|
|
|
105
140
|
});
|
|
106
141
|
return (_aiRecordsNamedIn_ = aiRecordsNamedIn(input.brief, rows, 1)[0]) != null ? _aiRecordsNamedIn_ : null;
|
|
107
142
|
}
|
|
108
|
-
function millisOf(value) {
|
|
109
|
-
if (value instanceof Date) return value.getTime();
|
|
110
|
-
if (typeof value === 'number') return value;
|
|
111
|
-
const toMillis = value == null ? void 0 : value.toMillis;
|
|
112
|
-
return typeof toMillis === 'function' ? toMillis.call(value) : 0;
|
|
113
|
-
}
|
|
114
143
|
/**
|
|
115
|
-
* The send time a list's past sends on this site suggest,
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
144
|
+
* The send time a list's past sends on this site suggest, asked of the plugin
|
|
145
|
+
* that keeps the sends: `null` when nothing keeps them here, when the owner
|
|
146
|
+
* refuses, or when there is too little history to suggest one.
|
|
147
|
+
*/ export async function readAiListSendTime(input) {
|
|
148
|
+
var _read_facts_measured;
|
|
149
|
+
if (!input.orgId || !input.hostId || !input.listId) return null;
|
|
150
|
+
const owner = pluginRecordFactsReader(AI_LIST_SEND_TIME_RESOURCE);
|
|
151
|
+
if (!owner) return null;
|
|
152
|
+
const read = await owner.reader.read({
|
|
153
|
+
orgId: input.orgId,
|
|
154
|
+
hostId: input.hostId,
|
|
155
|
+
id: input.listId,
|
|
156
|
+
uid: input.uid,
|
|
157
|
+
org: input.org,
|
|
158
|
+
now: input.now
|
|
159
|
+
});
|
|
160
|
+
if (!read.ok) return null;
|
|
161
|
+
const label = read.facts['label'];
|
|
162
|
+
const measured = Number((_read_facts_measured = read.facts['measured']) != null ? _read_facts_measured : 0);
|
|
163
|
+
return typeof label === 'string' && label && measured > 0 ? {
|
|
164
|
+
label,
|
|
165
|
+
measured
|
|
166
|
+
} : null;
|
|
134
167
|
}
|
|
135
168
|
/** What an email's output says about its products; `null` when it binds none and none were picked. */ export function aiEmailProductNote(binding) {
|
|
136
169
|
const bound = binding.ids.length;
|
|
@@ -150,7 +183,7 @@ function millisOf(value) {
|
|
|
150
183
|
if (!list) {
|
|
151
184
|
return 'The email is aimed at nobody yet: choose who receives it before you schedule it.';
|
|
152
185
|
}
|
|
153
|
-
const when = sendTime ? ` Past campaigns to that list on this site were opened most when sent on ${
|
|
186
|
+
const when = sendTime ? ` Past campaigns to that list on this site were opened most when sent on ${sendTime.label}, across ${sendTime.measured} sends.` : ' That list has too little send history on this site to suggest a send time.';
|
|
154
187
|
return `The brief names the list "${list.name}". The email is aimed at nobody yet: choose "${list.name}" as its audience before you schedule it.${when}`;
|
|
155
188
|
}
|
|
156
189
|
|