@aglyn/plugins-ai 1.0.0-beta.261 → 1.0.0-beta.263

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.
Files changed (90) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-brief-dialog.component.d.ts +21 -58
  3. package/src/lib/components/ai-brief-dialog.component.js +8 -132
  4. package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
  5. package/src/lib/components/ai-job-follow.component.js +2 -35
  6. package/src/lib/components/ai-job-follow.component.js.map +1 -1
  7. package/src/lib/components/ai-job-plan.component.d.ts +2 -8
  8. package/src/lib/components/ai-job-plan.component.js +13 -74
  9. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  10. package/src/lib/components/ai-template-collection-field.component.d.ts +2 -12
  11. package/src/lib/components/ai-template-collection-field.component.js +13 -10
  12. package/src/lib/components/ai-template-collection-field.component.js.map +1 -1
  13. package/src/lib/components/ai-upsell-dialog.component.d.ts +3 -25
  14. package/src/lib/components/ai-upsell-dialog.component.js +9 -66
  15. package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
  16. package/src/lib/components/use-ai-job-run.d.ts +3 -8
  17. package/src/lib/components/use-ai-job-run.js +4 -15
  18. package/src/lib/components/use-ai-job-run.js.map +1 -1
  19. package/src/lib/insights/ai-figure-readers.js +15 -5
  20. package/src/lib/insights/ai-figure-readers.js.map +1 -1
  21. package/src/lib/insights/ai-figure-summaries.d.ts +27 -0
  22. package/src/lib/insights/ai-figure-summaries.js +96 -0
  23. package/src/lib/insights/ai-figure-summaries.js.map +1 -0
  24. package/src/lib/insights/ai-insight-digest.d.ts +2 -0
  25. package/src/lib/insights/ai-insight-digest.js +7 -4
  26. package/src/lib/insights/ai-insight-digest.js.map +1 -1
  27. package/src/lib/insights/ai-insight-readers.d.ts +2 -1
  28. package/src/lib/insights/ai-insight-readers.js +11 -26
  29. package/src/lib/insights/ai-insight-readers.js.map +1 -1
  30. package/src/lib/jobs/ai-job-insight-step.d.ts +1 -1
  31. package/src/lib/jobs/ai-job-insight-step.js +5 -3
  32. package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
  33. package/src/lib/jobs/ai-job-page-language.d.ts +2 -0
  34. package/src/lib/jobs/ai-job-page-language.js +23 -2
  35. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  36. package/src/lib/jobs/ai-job-plan-step.d.ts +14 -1
  37. package/src/lib/jobs/ai-job-plan-step.js +44 -9
  38. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  39. package/src/lib/jobs/ai-jobs-notify.d.ts +8 -1
  40. package/src/lib/jobs/ai-jobs-notify.js +44 -2
  41. package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
  42. package/src/lib/jobs/ai-layout-stock-photos.js +11 -1
  43. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  44. package/src/lib/layout-language/ai-layout-compiler.js +18 -7
  45. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  46. package/src/lib/layout-language/ai-layout-promised-copy.d.ts +54 -0
  47. package/src/lib/layout-language/ai-layout-promised-copy.js +104 -0
  48. package/src/lib/layout-language/ai-layout-promised-copy.js.map +1 -0
  49. package/src/lib/model/ai-brief-copy.d.ts +85 -0
  50. package/src/lib/model/ai-brief-copy.js +152 -0
  51. package/src/lib/model/ai-brief-copy.js.map +1 -0
  52. package/src/lib/model/ai-create-view.d.ts +200 -0
  53. package/src/lib/model/ai-create-view.js +263 -0
  54. package/src/lib/model/ai-create-view.js.map +1 -0
  55. package/src/lib/model/ai-insight.d.ts +10 -1
  56. package/src/lib/model/ai-insight.js +16 -39
  57. package/src/lib/model/ai-insight.js.map +1 -1
  58. package/src/lib/model/ai-job-activity.d.ts +24 -0
  59. package/src/lib/model/ai-job-activity.js +52 -1
  60. package/src/lib/model/ai-job-activity.js.map +1 -1
  61. package/src/lib/model/ai-job-notice.d.ts +7 -1
  62. package/src/lib/model/ai-job-notice.js +10 -9
  63. package/src/lib/model/ai-job-notice.js.map +1 -1
  64. package/src/lib/model/ai-job-plan-lines.d.ts +46 -0
  65. package/src/lib/model/ai-job-plan-lines.js +70 -0
  66. package/src/lib/model/ai-job-plan-lines.js.map +1 -0
  67. package/src/lib/model/ai-plan-said-sections.d.ts +81 -0
  68. package/src/lib/model/ai-plan-said-sections.js +168 -0
  69. package/src/lib/model/ai-plan-said-sections.js.map +1 -0
  70. package/src/lib/model/ai-site-job.d.ts +8 -2
  71. package/src/lib/model/ai-site-job.js +7 -1
  72. package/src/lib/model/ai-site-job.js.map +1 -1
  73. package/src/lib/model/ai-template-collections.d.ts +28 -0
  74. package/src/lib/model/ai-template-collections.js +28 -0
  75. package/src/lib/model/ai-template-collections.js.map +1 -0
  76. package/src/lib/model/ai-upsell-copy.d.ts +41 -0
  77. package/src/lib/model/ai-upsell-copy.js +85 -0
  78. package/src/lib/model/ai-upsell-copy.js.map +1 -0
  79. package/src/lib/runtime/ai-doctrine-validators.d.ts +15 -1
  80. package/src/lib/runtime/ai-doctrine-validators.js +96 -3
  81. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  82. package/src/lib/runtime/ai-eval-memory-firestore.js +2 -0
  83. package/src/lib/runtime/ai-eval-memory-firestore.js.map +1 -1
  84. package/src/lib/runtime/ai-palette.generated.js +11 -3
  85. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  86. package/src/lib/server/ai-create-route.d.ts +32 -0
  87. package/src/lib/server/ai-create-route.js +295 -0
  88. package/src/lib/server/ai-create-route.js.map +1 -0
  89. package/src/lib/server.js +14 -0
  90. package/src/lib/server.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-jobs-notify.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 { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { notifyUsers } from '@aglyn/tenant-data-admin/server/notifications'\nimport { raiseOperatorAlert } from '@aglyn/tenant-data-admin/server/operator-alerts'\nimport { AI_BUILD_PARTLY_FAILED, AI_JOB_FAILED } from '../operator-alerts'\nimport { aiJobNotice } from '../model/ai-job-notice'\nimport { aiBusinessProfilePrefiller } from './ai-business-profile-prefill'\nimport { aiJobAutoConfirms } from './ai-job-auto-confirm'\nimport { registerAiJobTransitionListener, type AiJobTransitionListener } from './ai-jobs'\n\n/**\n * The person who started an AI job is told when it needs them, when it\n * finishes and when it stops (AGL-3593), through the console's notifications\n * — the bell, and the generic notification email for a person who switched\n * that channel on for the category. No other channel: there is no push.\n *\n * The machine calls this where the job's state changes, once per change\n * (`registerAiJobTransitionListener`): the request that ran a step inline,\n * or the beat that ran it with nobody watching — which is exactly when a\n * person needs telling. The recipient is the job's creator, whose job it is\n * and who alone confirms its plan; a teammate is not told about someone\n * else's draft.\n *\n * `notifyUsers` never throws, and honors the person's per-category,\n * per-workspace and per-site preferences.\n */\nexport function aiJobTransitionNotifier(\n notify: typeof notifyUsers = notifyUsers,\n): AiJobTransitionListener {\n return async ({ job, to }) => {\n if (!job.createdBy) return\n // A guided site start's plan is confirmed for it the moment it is kept\n // (AGL-3594): there is nothing for the person to confirm.\n if (to === 'needs-review' && job.review?.reason === 'plan' && aiJobAutoConfirms(job)) return\n await notify([job.createdBy], aiJobNotice(job, to))\n }\n}\n\n/** The longest runner error an alert quotes; the log has the rest. */\nconst ALERT_ERROR_MAX = 300\n\n/** At most this many failed parts are named in a partial-build alert. */\nconst ALERT_ITEMS_MAX = 5\n\nconst capped = (text: string): string =>\n text.length > ALERT_ERROR_MAX ? `${text.slice(0, ALERT_ERROR_MAX)}…` : text\n\n/**\n * Staff are told when a job fails on our side (`ai.jobFailed`), and when a\n * build finishes `done` with parts that failed on our side\n * (`ai.buildPartlyFailed`) — each deduped per job kind, so a broken provider\n * or a broken step is heard about the hour it starts. A failure that is the\n * customer's — the model declined the brief or a part of it, the site\n * switched AI off — raises nothing. `raiseOperatorAlert` never throws.\n */\nexport function aiJobFailureAlerter(\n raise: typeof raiseOperatorAlert = raiseOperatorAlert,\n): AiJobTransitionListener {\n return async ({ job, to, failure }) => {\n if (to === 'done') {\n const items = job.items ?? []\n const ours = items.filter((row) => row.status === 'failed' && row.failure?.ours)\n if (!ours.length) return\n const named = ours\n .slice(0, ALERT_ITEMS_MAX)\n .map((row) => `${row.label} (${row.failure?.reason}${row.failure?.detail ? `: ${row.failure.detail}` : ''})`)\n .join('; ')\n await raise(AI_BUILD_PARTLY_FAILED, {\n dedupeKey: job.kind,\n orgId: job.orgId,\n ...(job.hostId ? { hostId: job.hostId } : {}),\n context: {\n kind: job.kind,\n jobId: job.$id,\n orgId: job.orgId,\n failed: ours.length,\n total: items.length,\n items: capped(ours.length > ALERT_ITEMS_MAX ? `${named}; and ${ours.length - ALERT_ITEMS_MAX} more` : named),\n },\n })\n return\n }\n if (to !== 'failed' || !failure?.ours) return\n const error = failure.error ?? job.error ?? 'no error recorded'\n await raise(AI_JOB_FAILED, {\n dedupeKey: job.kind,\n orgId: job.orgId,\n ...(job.hostId ? { hostId: job.hostId } : {}),\n context: {\n kind: job.kind,\n jobId: job.$id,\n orgId: job.orgId,\n step: failure.stepIndex === null ? 'n/a' : job.steps[failure.stepIndex]?.name ?? failure.stepIndex,\n error: capped(error),\n },\n })\n }\n}\n\n/**\n * Registers what the jobs machine tells about each change: the notifier, the\n * staff alert for a failure on our side, and the business profile's prefill\n * from a site job (AGL-3661). One listener, each in turn; the alert and the\n * prefill swallow their own failures, so neither costs the person their notice.\n */\nexport function registerAiJobsNotify(): void {\n const notify = aiJobTransitionNotifier()\n const alert = aiJobFailureAlerter()\n const prefill = aiBusinessProfilePrefiller(() => firebaseAdmin.app().firestore())\n registerAiJobTransitionListener(async (change) => {\n await notify(change)\n await alert(change)\n await prefill(change)\n })\n}\n"],"names":["firebaseAdmin","notifyUsers","raiseOperatorAlert","AI_BUILD_PARTLY_FAILED","AI_JOB_FAILED","aiJobNotice","aiBusinessProfilePrefiller","aiJobAutoConfirms","registerAiJobTransitionListener","aiJobTransitionNotifier","notify","job","to","createdBy","review","reason","ALERT_ERROR_MAX","ALERT_ITEMS_MAX","capped","text","length","slice","aiJobFailureAlerter","raise","failure","items","ours","filter","row","status","named","map","label","detail","join","dedupeKey","kind","orgId","hostId","context","jobId","$id","failed","total","error","step","stepIndex","steps","name","registerAiJobsNotify","alert","prefill","app","firestore","change"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-jobs-notify.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 { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { notifyUsers } from '@aglyn/tenant-data-admin/server/notifications'\nimport { raiseOperatorAlert } from '@aglyn/tenant-data-admin/server/operator-alerts'\nimport { AI_BUILD_PARTLY_FAILED, AI_JOB_FAILED } from '../operator-alerts'\nimport { liveCustomDomain } from '@aglyn/aglyn/app-utils/host-naming'\nimport { TENANT_APEX } from '@aglyn/aglyn/app-utils/tenant-apex'\nimport { aiJobNotice, type AiJobNoticeSite } from '../model/ai-job-notice'\nimport { aiBusinessProfilePrefiller } from './ai-business-profile-prefill'\nimport { aiJobAutoConfirms } from './ai-job-auto-confirm'\nimport { registerAiJobTransitionListener, type AiJobTransitionListener } from './ai-jobs'\n\n/**\n * The person who started an AI job is told when it needs them, when it\n * finishes and when it stops (AGL-3593), through the console's notifications\n * — the bell, and the generic notification email for a person who switched\n * that channel on for the category. No other channel: there is no push.\n *\n * The machine calls this where the job's state changes, once per change\n * (`registerAiJobTransitionListener`): the request that ran a step inline,\n * or the beat that ran it with nobody watching — which is exactly when a\n * person needs telling. The recipient is the job's creator, whose job it is\n * and who alone confirms its plan; a teammate is not told about someone\n * else's draft.\n *\n * `notifyUsers` never throws, and honors the person's per-category,\n * per-workspace and per-site preferences.\n */\nexport function aiJobTransitionNotifier(\n notify: typeof notifyUsers = notifyUsers,\n readSite: (hostId: string) => Promise<AiJobNoticeSite | null> = readJobSite,\n): AiJobTransitionListener {\n return async ({ job, to }) => {\n if (!job.createdBy) return\n // A guided site start's plan is confirmed for it the moment it is kept\n // (AGL-3594): there is nothing for the person to confirm.\n if (to === 'needs-review' && job.review?.reason === 'plan' && aiJobAutoConfirms(job)) return\n // The site a job worked on, by name and address (AGL-3889): the email is\n // read away from the console, often by someone with several sites.\n const site = job.hostId ? await readSite(job.hostId).catch(() => null) : null\n const notice = aiJobNotice(job, to, site)\n if (!site) {\n await notify([job.createdBy], notice)\n return\n }\n await notify([job.createdBy], notice, {\n emailDetails: [\n { label: 'Site', value: site.name },\n ...(site.url ? [{ label: 'Address', value: site.url }] : []),\n ],\n })\n }\n}\n\n/**\n * A job's site as its notice names it: the name its managers gave it, else\n * its address, and where it is live — its custom domain once that serves,\n * else its platform subdomain. `null` for a site that is gone.\n */\nexport async function readJobSite(hostId: string): Promise<AiJobNoticeSite | null> {\n const host = await firebaseAdmin.app().firestore().collection('hosts').doc(hostId).get()\n if (!host.exists) return null\n const data = (host.data() ?? {}) as Record<string, unknown>\n const text = (value: unknown) => (typeof value === 'string' ? value.trim() : '')\n const subdomain = text(data['subdomain'])\n const domain = liveCustomDomain(data as never)\n return {\n name: text(data['displayName']) || text(data['name']) || subdomain || hostId,\n url: domain ? `https://${domain}` : subdomain ? `https://${subdomain}.${TENANT_APEX}` : null,\n }\n}\n\n/** The longest runner error an alert quotes; the log has the rest. */\nconst ALERT_ERROR_MAX = 300\n\n/** At most this many failed parts are named in a partial-build alert. */\nconst ALERT_ITEMS_MAX = 5\n\nconst capped = (text: string): string =>\n text.length > ALERT_ERROR_MAX ? `${text.slice(0, ALERT_ERROR_MAX)}…` : text\n\n/**\n * Staff are told when a job fails on our side (`ai.jobFailed`), and when a\n * build finishes `done` with parts that failed on our side\n * (`ai.buildPartlyFailed`) — each deduped per job kind, so a broken provider\n * or a broken step is heard about the hour it starts. A failure that is the\n * customer's — the model declined the brief or a part of it, the site\n * switched AI off — raises nothing. `raiseOperatorAlert` never throws.\n */\nexport function aiJobFailureAlerter(\n raise: typeof raiseOperatorAlert = raiseOperatorAlert,\n): AiJobTransitionListener {\n return async ({ job, to, failure }) => {\n if (to === 'done') {\n const items = job.items ?? []\n const ours = items.filter((row) => row.status === 'failed' && row.failure?.ours)\n if (!ours.length) return\n const named = ours\n .slice(0, ALERT_ITEMS_MAX)\n .map((row) => `${row.label} (${row.failure?.reason}${row.failure?.detail ? `: ${row.failure.detail}` : ''})`)\n .join('; ')\n await raise(AI_BUILD_PARTLY_FAILED, {\n dedupeKey: job.kind,\n orgId: job.orgId,\n ...(job.hostId ? { hostId: job.hostId } : {}),\n context: {\n kind: job.kind,\n jobId: job.$id,\n orgId: job.orgId,\n failed: ours.length,\n total: items.length,\n items: capped(ours.length > ALERT_ITEMS_MAX ? `${named}; and ${ours.length - ALERT_ITEMS_MAX} more` : named),\n },\n })\n return\n }\n if (to !== 'failed' || !failure?.ours) return\n const error = failure.error ?? job.error ?? 'no error recorded'\n await raise(AI_JOB_FAILED, {\n dedupeKey: job.kind,\n orgId: job.orgId,\n ...(job.hostId ? { hostId: job.hostId } : {}),\n context: {\n kind: job.kind,\n jobId: job.$id,\n orgId: job.orgId,\n step: failure.stepIndex === null ? 'n/a' : job.steps[failure.stepIndex]?.name ?? failure.stepIndex,\n error: capped(error),\n },\n })\n }\n}\n\n/**\n * Registers what the jobs machine tells about each change: the notifier, the\n * staff alert for a failure on our side, and the business profile's prefill\n * from a site job (AGL-3661). One listener, each in turn; the alert and the\n * prefill swallow their own failures, so neither costs the person their notice.\n */\nexport function registerAiJobsNotify(): void {\n const notify = aiJobTransitionNotifier()\n const alert = aiJobFailureAlerter()\n const prefill = aiBusinessProfilePrefiller(() => firebaseAdmin.app().firestore())\n registerAiJobTransitionListener(async (change) => {\n await notify(change)\n await alert(change)\n await prefill(change)\n })\n}\n"],"names":["firebaseAdmin","notifyUsers","raiseOperatorAlert","AI_BUILD_PARTLY_FAILED","AI_JOB_FAILED","liveCustomDomain","TENANT_APEX","aiJobNotice","aiBusinessProfilePrefiller","aiJobAutoConfirms","registerAiJobTransitionListener","aiJobTransitionNotifier","notify","readSite","readJobSite","job","to","createdBy","review","reason","site","hostId","catch","notice","emailDetails","label","value","name","url","host","app","firestore","collection","doc","get","exists","data","text","trim","subdomain","domain","ALERT_ERROR_MAX","ALERT_ITEMS_MAX","capped","length","slice","aiJobFailureAlerter","raise","failure","items","ours","filter","row","status","named","map","detail","join","dedupeKey","kind","orgId","context","jobId","$id","failed","total","error","step","stepIndex","steps","registerAiJobsNotify","alert","prefill","change"],"mappings":"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"}
@@ -1207,7 +1207,17 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
1207
1207
  'boxing',
1208
1208
  'boxer',
1209
1209
  'beach',
1210
- 'circus'
1210
+ 'circus',
1211
+ // Another sport's ball, never "medicine ball": a basketball court covered a deadlift night (AGL-3864).
1212
+ 'basketball',
1213
+ 'football',
1214
+ 'soccer',
1215
+ 'baseball',
1216
+ 'volleyball',
1217
+ 'tennis',
1218
+ 'golf',
1219
+ 'rugby',
1220
+ 'hockey'
1211
1221
  ]
1212
1222
  },
1213
1223
  {