@aglyn/plugins-ai 1.0.0-beta.220 → 1.0.0-beta.222
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/src/lib/components/ai-job-plan.component.js +1 -4
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/jobs/ai-job-crm-step.d.ts +44 -3
- package/src/lib/jobs/ai-job-crm-step.js +298 -8
- package/src/lib/jobs/ai-job-crm-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.d.ts +33 -1
- package/src/lib/jobs/ai-job-page-sections.js +55 -1
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +20 -11
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.js +6 -2
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-seo-step.js +52 -15
- package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.js +6 -0
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.js +8 -7
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-workflow-records.d.ts +2 -2
- package/src/lib/jobs/ai-workflow-records.js +2 -2
- package/src/lib/jobs/ai-workflow-records.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-free-page-built-recording.js +1 -0
- package/src/lib/jobs/fixtures/ai-free-page-built-recording.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-free-page-recording.js +2 -0
- package/src/lib/jobs/fixtures/ai-free-page-recording.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-page-briefs.js +2 -0
- package/src/lib/jobs/fixtures/ai-page-briefs.js.map +1 -1
- package/src/lib/model/ai-automation-draft.d.ts +4 -4
- package/src/lib/model/ai-automation-draft.js +15 -10
- package/src/lib/model/ai-automation-draft.js.map +1 -1
- package/src/lib/model/ai-automation-format.d.ts +139 -0
- package/src/lib/model/ai-automation-format.js +42 -0
- package/src/lib/model/ai-automation-format.js.map +1 -0
- package/src/lib/model/ai-automation-outline.d.ts +4 -4
- package/src/lib/model/ai-automation-outline.js +10 -8
- package/src/lib/model/ai-automation-outline.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +39 -2
- package/src/lib/model/ai-build-plan.js +80 -3
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-site-inventory.d.ts +5 -0
- package/src/lib/model/ai-site-inventory.js.map +1 -1
- package/src/lib/model/ai-workflow-job.js +2 -2
- package/src/lib/model/ai-workflow-job.js.map +1 -1
- package/src/lib/providers/catalog.d.ts +23 -0
- package/src/lib/providers/catalog.js +29 -1
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +8 -0
- package/src/lib/runtime/ai-doctrine-validators.js +34 -3
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +1 -1
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +2 -2
- package/src/lib/runtime/ai-eval.js +3 -3
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +8 -1
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/seo-audit.d.ts +5 -2
- package/src/lib/runtime/seo-audit.js +16 -7
- package/src/lib/runtime/seo-audit.js.map +1 -1
- package/src/lib/runtime/site-inventory.js +10 -3
- package/src/lib/runtime/site-inventory.js.map +1 -1
- package/src/lib/testing/stand-in-dataset-index.js +31 -5
- package/src/lib/testing/stand-in-dataset-index.js.map +1 -1
- package/src/lib/tools/ai-seo-tool.d.ts +5 -1
- package/src/lib/tools/ai-seo-tool.js +6 -2
- package/src/lib/tools/ai-seo-tool.js.map +1 -1
- package/src/lib/tools/ai-workflow-tool.d.ts +15 -4
- package/src/lib/tools/ai-workflow-tool.js +70 -12
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
- package/src/lib/usage-axes.js +14 -1
- package/src/lib/usage-axes.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-automation-draft.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n HostAction,\n HostActionStep,\n HostActionStepGuard,\n HostActionTriggerCondition,\n} from '@aglyn/aglyn/app-utils/actions'\nimport {\n automationPlaceholder,\n automationPlaceholders,\n describeAutomationPlaceholder,\n type AutomationPlaceholder,\n} from '@aglyn/aglyn/app-utils/automation-placeholders'\nimport type {\n AiAutomationAnswer,\n AiAutomationAnswerStep,\n AiAutomationCondition,\n} from '../tools/ai-workflow-tool'\n\n/**\n * An answer made into the automation the workflows plugin stores (AGL-2919).\n *\n * The answer names each record in WORDS. Here, in code and after the answer,\n * each is looked up among the site's own records: a list, a campaign, a\n * workflow, a webhook, a dataset, a form or a deal stage the words name\n * without doubt becomes that record's id; words that name none, or name more\n * than one, become a placeholder — the words in square brackets where the\n * record belongs — for the person to pick in the Actions editor. Nothing is\n * guessed, and the model never saw the records it was matched against beyond\n * what the site inventory already sends.\n */\n\n/** One record a description may name. */\nexport interface AiAutomationNamedRecord {\n id: string\n name: string\n}\n\n/** A form, with the fields its submissions carry. */\nexport interface AiAutomationForm extends AiAutomationNamedRecord {\n fields: string[]\n}\n\n/** The site's records an automation's words are looked up among. */\nexport interface AiAutomationRecords {\n forms: AiAutomationForm[]\n datasets: AiAutomationNamedRecord[]\n lists: AiAutomationNamedRecord[]\n campaigns: AiAutomationNamedRecord[]\n workflows: AiAutomationNamedRecord[]\n webhooks: AiAutomationNamedRecord[]\n /** Every stage of every pipeline the site may see. */\n stages: AiAutomationNamedRecord[]\n}\n\nexport function emptyAiAutomationRecords(): AiAutomationRecords {\n return { forms: [], datasets: [], lists: [], campaigns: [], workflows: [], webhooks: [], stages: [] }\n}\n\n/** Words that say nothing about which record is meant. */\nconst FILLER = new Set(['the', 'a', 'an', 'our', 'my', 'your', 'this', 'that', 'new', 'called', 'named'])\n\nfunction tokens(value: string, noun: string): string[] {\n const nouns = new Set([noun, `${noun}s`])\n return value\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, ' ')\n .split(' ')\n .filter(Boolean)\n .filter((word) => !FILLER.has(word) && !nouns.has(word))\n .map((word) => (word.length > 3 && word.endsWith('s') && !word.endsWith('ss') ? word.slice(0, -1) : word))\n}\n\n/**\n * The one record `words` name, or `null`: its id written whole; else the one\n * record whose name is the same words; else the one record whose name holds\n * every word, or whose every word the words hold. Two records that fit\n * equally well name neither — a placeholder is better than a coin toss.\n */\nexport function aiMatchNamedRecord<T extends AiAutomationNamedRecord>(\n words: string,\n records: readonly T[],\n noun: string,\n): T | null {\n const wanted = words.trim()\n if (!wanted) return null\n const byId = records.filter((record) => record.id === wanted)\n if (byId.length === 1) return byId[0]\n const asked = tokens(wanted, noun)\n if (!asked.length) return null\n const same = (record: T) => {\n const name = tokens(record.name, noun)\n return name.length === asked.length && asked.every((word) => name.includes(word))\n }\n const exact = records.filter(same)\n if (exact.length) return exact.length === 1 ? exact[0] : null\n const within = records.filter((record) => {\n const name = tokens(record.name, noun)\n if (!name.length) return false\n const [small, large] = name.length <= asked.length ? [name, asked] : [asked, name]\n return small.every((word) => large.includes(word))\n })\n return within.length === 1 ? within[0] : null\n}\n\n/** A form by its id or its name, for a condition that names one. */\nfunction formOf(value: string, forms: readonly AiAutomationForm[]): AiAutomationForm | null {\n return aiMatchNamedRecord(value, forms, 'form')\n}\n\nfunction conditionOf(\n condition: AiAutomationCondition,\n records: AiAutomationRecords,\n): HostActionTriggerCondition {\n const { field, op, value } = condition\n if (op === 'notEmpty' || value === null) return { field, op }\n if (field === 'formId') {\n const form = formOf(value, records.forms)\n return { field, op, value: form ? form.id : automationPlaceholder(value) }\n }\n if (field === 'formName') {\n const form = formOf(value, records.forms)\n return { field, op, value: form ? form.name : automationPlaceholder(value) }\n }\n if (field === 'stageId' || field === 'previousStageId') {\n const stage = aiMatchNamedRecord(value, records.stages, 'stage')\n return { field, op, value: stage ? stage.id : automationPlaceholder(value) }\n }\n return { field, op, value }\n}\n\nfunction guardOf(\n when: AiAutomationCondition | null,\n records: AiAutomationRecords,\n): { when?: HostActionStepGuard } {\n return when ? { when: { conditions: [conditionOf(when, records)] } } : {}\n}\n\n/** A named record as a step stores it: the id and the name, or a placeholder name and no id. */\nfunction referenceOf(\n words: string | null,\n records: readonly AiAutomationNamedRecord[],\n noun: string,\n): { id: string | null; name: string } {\n const record = aiMatchNamedRecord(words ?? '', records, noun)\n return record ? { id: record.id, name: record.name } : { id: null, name: automationPlaceholder(words ?? '') }\n}\n\nfunction stepOf(step: AiAutomationAnswerStep, records: AiAutomationRecords): HostActionStep {\n const guard = guardOf(step.when, records)\n switch (step.type) {\n case 'sendEmail':\n return {\n type: 'sendEmail',\n subject: step.subject ?? '',\n body: step.body ?? '',\n ...(step.toField ? { toField: step.toField } : {}),\n ...guard,\n }\n case 'notifyAdmins':\n return { type: 'notifyAdmins', title: step.title ?? '', ...guard }\n case 'enrollList': {\n const { id, name } = referenceOf(step.list, records.lists, 'list')\n return { type: 'enrollList', ...(id ? { listId: id } : {}), listName: name, ...guard }\n }\n case 'assignCampaign': {\n const { id, name } = referenceOf(step.campaign, records.campaigns, 'campaign')\n return { type: 'assignCampaign', ...(id ? { campaignId: id } : {}), campaignName: name, ...guard }\n }\n case 'runWorkflow': {\n const { id, name } = referenceOf(step.workflow, records.workflows, 'workflow')\n return { type: 'runWorkflow', ...(id ? { workflowId: id } : {}), workflowName: name, ...guard }\n }\n case 'webhookPost': {\n const { id, name } = referenceOf(step.webhook, records.webhooks, 'webhook')\n return { type: 'webhookPost', ...(id ? { webhookId: id } : {}), webhookName: name, ...guard }\n }\n case 'datasetAppend':\n case 'updateDataset': {\n const { id, name } = referenceOf(step.dataset, records.datasets, 'dataset')\n return { type: step.type, ...(id ? { datasetId: id } : {}), datasetName: name, ...guard }\n }\n case 'siteAlert':\n return { type: 'siteAlert', message: step.message ?? '', severity: step.severity ?? 'info', ...guard }\n case 'wait':\n return { type: 'wait', delayMinutes: step.minutes ?? 0, ...guard }\n case 'waitForEvent':\n return {\n type: 'waitForEvent',\n eventName: step.event ?? '',\n timeoutMinutes: step.minutes ?? 0,\n ...guard,\n }\n case 'exitFlow':\n return { type: 'exitFlow', ...guard }\n case 'setContactStage':\n return { type: 'setContactStage', lifecycleStage: step.stage ?? 'lead', ...guard }\n case 'addContactTag':\n return { type: 'addContactTag', tag: step.tag ?? '', ...guard }\n case 'assignContactOwner':\n return step.owner === 'round robin'\n ? { type: 'assignContactOwner', roundRobin: true, ...guard }\n : { type: 'assignContactOwner', ownerEmail: step.owner ?? '', ...guard }\n case 'createCrmTask':\n return {\n type: 'createCrmTask',\n title: step.title ?? '',\n kind: step.taskKind ?? 'todo',\n dueInDays: step.dueInDays ?? 0,\n ...guard,\n }\n case 'logCrmActivity':\n return { type: 'logCrmActivity', kind: step.activityKind ?? 'note', body: step.body ?? '', ...guard }\n }\n}\n\n/** The name an automation is drafted under when the answer gave none. */\nexport const AI_AUTOMATION_DEFAULT_NAME = 'New automation'\n\nexport interface AiAutomationDraft {\n /** The automation as the workflows plugin stores it: OFF. */\n action: HostAction\n /** What a person still has to supply before switching it on. */\n placeholders: AutomationPlaceholder[]\n}\n\n/** An answer as the automation to store, with every record it names looked up. */\nexport function aiAutomationDraft(\n answer: AiAutomationAnswer,\n records: AiAutomationRecords,\n): AiAutomationDraft {\n const conditions = answer.trigger.conditions.map((condition) => conditionOf(condition, records))\n const action: HostAction = {\n name: answer.name || AI_AUTOMATION_DEFAULT_NAME,\n trigger: {\n event: answer.trigger.event,\n ...(conditions.length ? { conditions, combinator: answer.trigger.combinator } : {}),\n },\n steps: answer.steps.map((step) => stepOf(step, records)),\n enabled: false,\n }\n return { action, placeholders: automationPlaceholders(action) }\n}\n\n/**\n * What a drafted automation's output tells a person: that it is off, what to\n * fill in before switching it on, and what the answer said needs deciding.\n */\nexport function aiAutomationDraftNote(draft: AiAutomationDraft, notes: readonly string[]): string {\n const parts = ['It is off until you switch it on.']\n if (draft.placeholders.length) {\n parts.push(\n `Fill in ${draft.placeholders.length === 1 ? 'its placeholder' : `its ${draft.placeholders.length} placeholders`} first — ${draft.placeholders\n .map(describeAutomationPlaceholder)\n .join('; ')}.`,\n )\n } else {\n parts.push('Review its steps in the Actions editor, then switch it on from the list.')\n }\n parts.push(...notes)\n return parts.join(' ')\n}\n"],"names":["automationPlaceholder","automationPlaceholders","describeAutomationPlaceholder","emptyAiAutomationRecords","forms","datasets","lists","campaigns","workflows","webhooks","stages","FILLER","Set","tokens","value","noun","nouns","toLowerCase","replace","split","filter","Boolean","word","has","map","length","endsWith","slice","aiMatchNamedRecord","words","records","wanted","trim","byId","record","id","asked","same","name","every","includes","exact","within","small","large","formOf","conditionOf","condition","field","op","form","stage","guardOf","when","conditions","referenceOf","stepOf","step","guard","type","subject","body","toField","title","list","listId","listName","campaign","campaignId","campaignName","workflow","workflowId","workflowName","webhook","webhookId","webhookName","dataset","datasetId","datasetName","message","severity","delayMinutes","minutes","eventName","event","timeoutMinutes","lifecycleStage","tag","owner","roundRobin","ownerEmail","kind","taskKind","dueInDays","activityKind","AI_AUTOMATION_DEFAULT_NAME","aiAutomationDraft","answer","trigger","action","combinator","steps","enabled","placeholders","aiAutomationDraftNote","draft","notes","parts","push","join"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-automation-draft.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 {\n describeInteractionPlaceholder,\n draftPlaceholder,\n type InteractionPlaceholder,\n interactionPlaceholders,\n} from '@aglyn/aglyn/app-utils/draft-placeholders'\nimport type { InteractionCondition, InteractionStepGuard } from '@aglyn/aglyn/app-utils/site-interactions'\nimport type { AiAutomation, AiAutomationStep } from './ai-automation-format'\nimport type {\n AiAutomationAnswer,\n AiAutomationAnswerStep,\n AiAutomationCondition,\n} from '../tools/ai-workflow-tool'\n\n/**\n * An answer made into the automation the workflows plugin stores (AGL-2919).\n *\n * The answer names each record in WORDS. Here, in code and after the answer,\n * each is looked up among the site's own records: a list, a campaign, a\n * workflow, a webhook, a dataset, a form or a deal stage the words name\n * without doubt becomes that record's id; words that name none, or name more\n * than one, become a placeholder — the words in square brackets where the\n * record belongs — for the person to pick in the Actions editor. Nothing is\n * guessed, and the model never saw the records it was matched against beyond\n * what the site inventory already sends.\n */\n\n/** One record a description may name. */\nexport interface AiAutomationNamedRecord {\n id: string\n name: string\n}\n\n/** A form, with the fields its submissions carry. */\nexport interface AiAutomationForm extends AiAutomationNamedRecord {\n fields: string[]\n}\n\n/** The site's records an automation's words are looked up among. */\nexport interface AiAutomationRecords {\n forms: AiAutomationForm[]\n datasets: AiAutomationNamedRecord[]\n lists: AiAutomationNamedRecord[]\n campaigns: AiAutomationNamedRecord[]\n workflows: AiAutomationNamedRecord[]\n webhooks: AiAutomationNamedRecord[]\n /** Every stage of every pipeline the site may see. */\n stages: AiAutomationNamedRecord[]\n}\n\nexport function emptyAiAutomationRecords(): AiAutomationRecords {\n return { forms: [], datasets: [], lists: [], campaigns: [], workflows: [], webhooks: [], stages: [] }\n}\n\n/** Words that say nothing about which record is meant. */\nconst FILLER = new Set(['the', 'a', 'an', 'our', 'my', 'your', 'this', 'that', 'new', 'called', 'named'])\n\nfunction tokens(value: string, noun: string): string[] {\n const nouns = new Set([noun, `${noun}s`])\n return value\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, ' ')\n .split(' ')\n .filter(Boolean)\n .filter((word) => !FILLER.has(word) && !nouns.has(word))\n .map((word) => (word.length > 3 && word.endsWith('s') && !word.endsWith('ss') ? word.slice(0, -1) : word))\n}\n\n/**\n * The one record `words` name, or `null`: its id written whole; else the one\n * record whose name is the same words; else the one record whose name holds\n * every word, or whose every word the words hold. Two records that fit\n * equally well name neither — a placeholder is better than a coin toss.\n */\nexport function aiMatchNamedRecord<T extends AiAutomationNamedRecord>(\n words: string,\n records: readonly T[],\n noun: string,\n): T | null {\n const wanted = words.trim()\n if (!wanted) return null\n const byId = records.filter((record) => record.id === wanted)\n if (byId.length === 1) return byId[0]\n const asked = tokens(wanted, noun)\n if (!asked.length) return null\n const same = (record: T) => {\n const name = tokens(record.name, noun)\n return name.length === asked.length && asked.every((word) => name.includes(word))\n }\n const exact = records.filter(same)\n if (exact.length) return exact.length === 1 ? exact[0] : null\n const within = records.filter((record) => {\n const name = tokens(record.name, noun)\n if (!name.length) return false\n const [small, large] = name.length <= asked.length ? [name, asked] : [asked, name]\n return small.every((word) => large.includes(word))\n })\n return within.length === 1 ? within[0] : null\n}\n\n/** A form by its id or its name, for a condition that names one. */\nfunction formOf(value: string, forms: readonly AiAutomationForm[]): AiAutomationForm | null {\n return aiMatchNamedRecord(value, forms, 'form')\n}\n\nfunction conditionOf(\n condition: AiAutomationCondition,\n records: AiAutomationRecords,\n): InteractionCondition {\n const { field, op, value } = condition\n if (op === 'notEmpty' || value === null) return { field, op }\n if (field === 'formId') {\n const form = formOf(value, records.forms)\n return { field, op, value: form ? form.id : draftPlaceholder(value) }\n }\n if (field === 'formName') {\n const form = formOf(value, records.forms)\n return { field, op, value: form ? form.name : draftPlaceholder(value) }\n }\n if (field === 'stageId' || field === 'previousStageId') {\n const stage = aiMatchNamedRecord(value, records.stages, 'stage')\n return { field, op, value: stage ? stage.id : draftPlaceholder(value) }\n }\n return { field, op, value }\n}\n\nfunction guardOf(\n when: AiAutomationCondition | null,\n records: AiAutomationRecords,\n): { when?: InteractionStepGuard } {\n return when ? { when: { conditions: [conditionOf(when, records)] } } : {}\n}\n\n/** A named record as a step stores it: the id and the name, or a placeholder name and no id. */\nfunction referenceOf(\n words: string | null,\n records: readonly AiAutomationNamedRecord[],\n noun: string,\n): { id: string | null; name: string } {\n const record = aiMatchNamedRecord(words ?? '', records, noun)\n return record ? { id: record.id, name: record.name } : { id: null, name: draftPlaceholder(words ?? '') }\n}\n\nfunction stepOf(step: AiAutomationAnswerStep, records: AiAutomationRecords): AiAutomationStep {\n const guard = guardOf(step.when, records)\n switch (step.type) {\n case 'sendEmail':\n return {\n type: 'sendEmail',\n subject: step.subject ?? '',\n body: step.body ?? '',\n ...(step.toField ? { toField: step.toField } : {}),\n ...guard,\n }\n case 'notifyAdmins':\n return { type: 'notifyAdmins', title: step.title ?? '', ...guard }\n case 'enrollList': {\n const { id, name } = referenceOf(step.list, records.lists, 'list')\n return { type: 'enrollList', ...(id ? { listId: id } : {}), listName: name, ...guard }\n }\n case 'assignCampaign': {\n const { id, name } = referenceOf(step.campaign, records.campaigns, 'campaign')\n return { type: 'assignCampaign', ...(id ? { campaignId: id } : {}), campaignName: name, ...guard }\n }\n case 'runWorkflow': {\n const { id, name } = referenceOf(step.workflow, records.workflows, 'workflow')\n return { type: 'runWorkflow', ...(id ? { workflowId: id } : {}), workflowName: name, ...guard }\n }\n case 'webhookPost': {\n const { id, name } = referenceOf(step.webhook, records.webhooks, 'webhook')\n return { type: 'webhookPost', ...(id ? { webhookId: id } : {}), webhookName: name, ...guard }\n }\n case 'datasetAppend':\n case 'updateDataset': {\n const { id, name } = referenceOf(step.dataset, records.datasets, 'dataset')\n return { type: step.type, ...(id ? { datasetId: id } : {}), datasetName: name, ...guard }\n }\n case 'siteAlert':\n return { type: 'siteAlert', message: step.message ?? '', severity: step.severity ?? 'info', ...guard }\n case 'wait':\n return { type: 'wait', delayMinutes: step.minutes ?? 0, ...guard }\n case 'waitForEvent':\n return {\n type: 'waitForEvent',\n eventName: step.event ?? '',\n timeoutMinutes: step.minutes ?? 0,\n ...guard,\n }\n case 'exitFlow':\n return { type: 'exitFlow', ...guard }\n case 'setContactStage':\n return { type: 'setContactStage', lifecycleStage: step.stage ?? 'lead', ...guard }\n case 'addContactTag':\n return { type: 'addContactTag', tag: step.tag ?? '', ...guard }\n case 'assignContactOwner':\n return step.owner === 'round robin'\n ? { type: 'assignContactOwner', roundRobin: true, ...guard }\n : { type: 'assignContactOwner', ownerEmail: step.owner ?? '', ...guard }\n case 'createCrmTask':\n return {\n type: 'createCrmTask',\n title: step.title ?? '',\n kind: step.taskKind ?? 'todo',\n // The runner stores the org's label for the meaning (AGL-3538).\n ...(step.priority ? { priority: step.priority } : {}),\n dueInDays: step.dueInDays ?? 0,\n ...guard,\n }\n case 'logCrmActivity':\n return {\n type: 'logCrmActivity',\n kind: step.activityKind ?? 'note',\n body: step.body ?? '',\n ...(step.direction ? { direction: step.direction } : {}),\n ...guard,\n }\n }\n}\n\n/** The name an automation is drafted under when the answer gave none. */\nexport const AI_AUTOMATION_DEFAULT_NAME = 'New automation'\n\nexport interface AiAutomationDraft {\n /** The automation as the workflows plugin stores it: OFF. */\n action: AiAutomation\n /** What a person still has to supply before switching it on. */\n placeholders: InteractionPlaceholder[]\n}\n\n/** An answer as the automation to store, with every record it names looked up. */\nexport function aiAutomationDraft(\n answer: AiAutomationAnswer,\n records: AiAutomationRecords,\n): AiAutomationDraft {\n const conditions = answer.trigger.conditions.map((condition) => conditionOf(condition, records))\n const action: AiAutomation = {\n name: answer.name || AI_AUTOMATION_DEFAULT_NAME,\n trigger: {\n event: answer.trigger.event,\n ...(conditions.length ? { conditions, combinator: answer.trigger.combinator } : {}),\n },\n steps: answer.steps.map((step) => stepOf(step, records)),\n enabled: false,\n }\n return { action, placeholders: interactionPlaceholders(action) }\n}\n\n/**\n * What a drafted automation's output tells a person: that it is off, what to\n * fill in before switching it on, and what the answer said needs deciding.\n */\nexport function aiAutomationDraftNote(draft: AiAutomationDraft, notes: readonly string[]): string {\n const parts = ['It is off until you switch it on.']\n if (draft.placeholders.length) {\n parts.push(\n `Fill in ${draft.placeholders.length === 1 ? 'its placeholder' : `its ${draft.placeholders.length} placeholders`} first — ${draft.placeholders\n .map(describeInteractionPlaceholder)\n .join('; ')}.`,\n )\n } else {\n parts.push('Review its steps in the Actions editor, then switch it on from the list.')\n }\n parts.push(...notes)\n return parts.join(' ')\n}\n"],"names":["describeInteractionPlaceholder","draftPlaceholder","interactionPlaceholders","emptyAiAutomationRecords","forms","datasets","lists","campaigns","workflows","webhooks","stages","FILLER","Set","tokens","value","noun","nouns","toLowerCase","replace","split","filter","Boolean","word","has","map","length","endsWith","slice","aiMatchNamedRecord","words","records","wanted","trim","byId","record","id","asked","same","name","every","includes","exact","within","small","large","formOf","conditionOf","condition","field","op","form","stage","guardOf","when","conditions","referenceOf","stepOf","step","guard","type","subject","body","toField","title","list","listId","listName","campaign","campaignId","campaignName","workflow","workflowId","workflowName","webhook","webhookId","webhookName","dataset","datasetId","datasetName","message","severity","delayMinutes","minutes","eventName","event","timeoutMinutes","lifecycleStage","tag","owner","roundRobin","ownerEmail","kind","taskKind","priority","dueInDays","activityKind","direction","AI_AUTOMATION_DEFAULT_NAME","aiAutomationDraft","answer","trigger","action","combinator","steps","enabled","placeholders","aiAutomationDraftNote","draft","notes","parts","push","join"],"mappings":"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"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { ContactLifecycleStage, CrmActivityDirection, CrmActivityKind, CrmTaskKind, CrmTaskPriority } from '@aglyn/aglyn/app-utils/crm';
|
|
18
|
+
import { type ClientInteractionStep, type InteractionStepGuard, type SiteInteraction } from '@aglyn/aglyn/app-utils/site-interactions';
|
|
19
|
+
/**
|
|
20
|
+
* THE AUTOMATION FORMAT, AS THIS PLUGIN DRAFTS AND READS IT (AGL-2919,
|
|
21
|
+
* AGL-3080).
|
|
22
|
+
*
|
|
23
|
+
* An automation is a site interaction whose steps include the server's — the
|
|
24
|
+
* stored document at `hosts/{hostId}/actions/{id}`. The automation editor is
|
|
25
|
+
* the workflows plugin's, and this plugin never loads it. It drafts in the
|
|
26
|
+
* terms every plugin can read:
|
|
27
|
+
*
|
|
28
|
+
* - WHAT A STEP IS CALLED, which step HOLDS a run and for how long, and the
|
|
29
|
+
* field a hold that ran out of time leaves in scope, are each step's
|
|
30
|
+
* declaration (`interactionSteps` in `plugins.config.json`), compiled into
|
|
31
|
+
* core and read here through `site-interactions`;
|
|
32
|
+
* - WHAT IT HANDS OVER is written through the `automation` resource's draft
|
|
33
|
+
* writer (`plugin-resource-drafts`), which the workflows plugin registers
|
|
34
|
+
* and which refuses anything its editor would not save;
|
|
35
|
+
* - WHAT IT GRADES a drafted automation against is the platform's validator
|
|
36
|
+
* with every step check the owners registered (`interaction-step-checks`).
|
|
37
|
+
*
|
|
38
|
+
* The shapes below are the steps this plugin drafts and explains, as the
|
|
39
|
+
* stored document holds them: its own reading of the format, typed so a
|
|
40
|
+
* drafted step cannot miss a field it means to write. The writer is the judge
|
|
41
|
+
* of the document. In this plugin's specs no editor registers its checks, so a
|
|
42
|
+
* draft is graded by the platform's checks and this plugin's own reader;
|
|
43
|
+
* `apps/console/specs/ai-automation-drafting.spec.ts` boots both plugins and
|
|
44
|
+
* holds every server step named, checked and held as drafted here.
|
|
45
|
+
*/
|
|
46
|
+
/** A server step, as this plugin writes and reads it. */
|
|
47
|
+
export type AiAutomationServerStep = ({
|
|
48
|
+
type: 'runWorkflow';
|
|
49
|
+
workflowId?: string;
|
|
50
|
+
workflowName?: string;
|
|
51
|
+
} | {
|
|
52
|
+
type: 'customEvent';
|
|
53
|
+
eventName: string;
|
|
54
|
+
} | {
|
|
55
|
+
type: 'datasetAppend';
|
|
56
|
+
datasetId?: string;
|
|
57
|
+
datasetName?: string;
|
|
58
|
+
} | {
|
|
59
|
+
type: 'updateDataset';
|
|
60
|
+
datasetId?: string;
|
|
61
|
+
datasetName?: string;
|
|
62
|
+
} | {
|
|
63
|
+
type: 'webhookPost';
|
|
64
|
+
webhookId?: string;
|
|
65
|
+
webhookName?: string;
|
|
66
|
+
} | {
|
|
67
|
+
type: 'sendEmail';
|
|
68
|
+
subject: string;
|
|
69
|
+
body: string;
|
|
70
|
+
toField?: string;
|
|
71
|
+
topicId?: string;
|
|
72
|
+
} | {
|
|
73
|
+
type: 'notifyAdmins';
|
|
74
|
+
title: string;
|
|
75
|
+
body?: string;
|
|
76
|
+
} | {
|
|
77
|
+
type: 'enrollList';
|
|
78
|
+
listId?: string;
|
|
79
|
+
listName?: string;
|
|
80
|
+
} | {
|
|
81
|
+
type: 'assignCampaign';
|
|
82
|
+
campaignId?: string;
|
|
83
|
+
campaignName?: string;
|
|
84
|
+
} | {
|
|
85
|
+
type: 'wait';
|
|
86
|
+
delayMinutes: number;
|
|
87
|
+
} | {
|
|
88
|
+
type: 'waitForEvent';
|
|
89
|
+
eventName: string;
|
|
90
|
+
timeoutMinutes: number;
|
|
91
|
+
} | {
|
|
92
|
+
type: 'exitFlow';
|
|
93
|
+
} | {
|
|
94
|
+
type: 'setContactStage';
|
|
95
|
+
lifecycleStage: ContactLifecycleStage;
|
|
96
|
+
} | {
|
|
97
|
+
type: 'addContactTag';
|
|
98
|
+
tag: string;
|
|
99
|
+
} | {
|
|
100
|
+
type: 'assignContactOwner';
|
|
101
|
+
ownerUid?: string;
|
|
102
|
+
ownerEmail?: string;
|
|
103
|
+
roundRobin?: boolean;
|
|
104
|
+
} | {
|
|
105
|
+
type: 'createCrmTask';
|
|
106
|
+
title: string;
|
|
107
|
+
kind: CrmTaskKind;
|
|
108
|
+
/** By meaning; the runner stores the org's label (AGL-3538). */
|
|
109
|
+
priority?: CrmTaskPriority;
|
|
110
|
+
dueInDays: number;
|
|
111
|
+
assigneeUid?: string;
|
|
112
|
+
assigneeEmail?: string;
|
|
113
|
+
} | {
|
|
114
|
+
type: 'logCrmActivity';
|
|
115
|
+
kind: CrmActivityKind;
|
|
116
|
+
body: string;
|
|
117
|
+
/** Which way a call or an email went (AGL-3538). */
|
|
118
|
+
direction?: CrmActivityDirection;
|
|
119
|
+
}) & {
|
|
120
|
+
when?: InteractionStepGuard | null;
|
|
121
|
+
};
|
|
122
|
+
/** Any step of an automation: a platform client step, or a server step. */
|
|
123
|
+
export type AiAutomationStep = ClientInteractionStep | AiAutomationServerStep;
|
|
124
|
+
/** A step type of the format. */
|
|
125
|
+
export type AiAutomationFormatStepType = AiAutomationStep['type'];
|
|
126
|
+
/** An automation, as the stored document holds it. */
|
|
127
|
+
export type AiAutomation = SiteInteraction<AiAutomationStep>;
|
|
128
|
+
/** How a step type reads in a sentence: its declared label, or its raw type. */
|
|
129
|
+
export declare function aiStepLabel(type: string): string;
|
|
130
|
+
/** The whole minutes a `wait` or a `waitForEvent` may hold, as their declarations give them. */
|
|
131
|
+
export declare const AI_AUTOMATION_WAIT_MINUTES: Readonly<{
|
|
132
|
+
min: number;
|
|
133
|
+
max: number;
|
|
134
|
+
}>;
|
|
135
|
+
/**
|
|
136
|
+
* The field a `waitForEvent` that ran out of time leaves in scope, which a
|
|
137
|
+
* later step's guard names to take the timeout branch.
|
|
138
|
+
*/
|
|
139
|
+
export declare const AI_AUTOMATION_TIMED_OUT_FIELD: string;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { interactionStepHolds, interactionStepLabel } from "@aglyn/aglyn/app-utils/site-interactions";
|
|
17
|
+
/** How a step type reads in a sentence: its declared label, or its raw type. */ export function aiStepLabel(type) {
|
|
18
|
+
var _interactionStepLabel;
|
|
19
|
+
return (_interactionStepLabel = interactionStepLabel(type)) != null ? _interactionStepLabel : type;
|
|
20
|
+
}
|
|
21
|
+
/** The hold a step declares; a step this plugin drafts as a hold must declare one. */ function declaredHolds(type) {
|
|
22
|
+
const holds = interactionStepHolds(type);
|
|
23
|
+
if (!holds) throw new Error(`the "${type}" step declares no hold, and the automation drafter writes it as one`);
|
|
24
|
+
return holds;
|
|
25
|
+
}
|
|
26
|
+
/** The whole minutes a `wait` or a `waitForEvent` may hold, as their declarations give them. */ export const AI_AUTOMATION_WAIT_MINUTES = (()=>{
|
|
27
|
+
const wait = declaredHolds('wait');
|
|
28
|
+
return {
|
|
29
|
+
min: wait.minMinutes,
|
|
30
|
+
max: wait.maxMinutes
|
|
31
|
+
};
|
|
32
|
+
})();
|
|
33
|
+
/**
|
|
34
|
+
* The field a `waitForEvent` that ran out of time leaves in scope, which a
|
|
35
|
+
* later step's guard names to take the timeout branch.
|
|
36
|
+
*/ export const AI_AUTOMATION_TIMED_OUT_FIELD = (()=>{
|
|
37
|
+
const field = declaredHolds('waitForEvent').timeoutField;
|
|
38
|
+
if (!field) throw new Error('the "waitForEvent" step declares no timeout field');
|
|
39
|
+
return field;
|
|
40
|
+
})();
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=ai-automation-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-automation-format.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n ContactLifecycleStage,\n CrmActivityDirection,\n CrmActivityKind,\n CrmTaskKind,\n CrmTaskPriority,\n} from '@aglyn/aglyn/app-utils/crm'\nimport {\n type ClientInteractionStep,\n type InteractionStepGuard,\n interactionStepHolds,\n type InteractionStepHolds,\n interactionStepLabel,\n type SiteInteraction,\n} from '@aglyn/aglyn/app-utils/site-interactions'\n\n/**\n * THE AUTOMATION FORMAT, AS THIS PLUGIN DRAFTS AND READS IT (AGL-2919,\n * AGL-3080).\n *\n * An automation is a site interaction whose steps include the server's — the\n * stored document at `hosts/{hostId}/actions/{id}`. The automation editor is\n * the workflows plugin's, and this plugin never loads it. It drafts in the\n * terms every plugin can read:\n *\n * - WHAT A STEP IS CALLED, which step HOLDS a run and for how long, and the\n * field a hold that ran out of time leaves in scope, are each step's\n * declaration (`interactionSteps` in `plugins.config.json`), compiled into\n * core and read here through `site-interactions`;\n * - WHAT IT HANDS OVER is written through the `automation` resource's draft\n * writer (`plugin-resource-drafts`), which the workflows plugin registers\n * and which refuses anything its editor would not save;\n * - WHAT IT GRADES a drafted automation against is the platform's validator\n * with every step check the owners registered (`interaction-step-checks`).\n *\n * The shapes below are the steps this plugin drafts and explains, as the\n * stored document holds them: its own reading of the format, typed so a\n * drafted step cannot miss a field it means to write. The writer is the judge\n * of the document. In this plugin's specs no editor registers its checks, so a\n * draft is graded by the platform's checks and this plugin's own reader;\n * `apps/console/specs/ai-automation-drafting.spec.ts` boots both plugins and\n * holds every server step named, checked and held as drafted here.\n */\n\n/** A server step, as this plugin writes and reads it. */\nexport type AiAutomationServerStep = (\n | { type: 'runWorkflow'; workflowId?: string; workflowName?: string }\n | { type: 'customEvent'; eventName: string }\n | { type: 'datasetAppend'; datasetId?: string; datasetName?: string }\n | { type: 'updateDataset'; datasetId?: string; datasetName?: string }\n | { type: 'webhookPost'; webhookId?: string; webhookName?: string }\n | { type: 'sendEmail'; subject: string; body: string; toField?: string; topicId?: string }\n | { type: 'notifyAdmins'; title: string; body?: string }\n | { type: 'enrollList'; listId?: string; listName?: string }\n | { type: 'assignCampaign'; campaignId?: string; campaignName?: string }\n | { type: 'wait'; delayMinutes: number }\n | { type: 'waitForEvent'; eventName: string; timeoutMinutes: number }\n | { type: 'exitFlow' }\n | { type: 'setContactStage'; lifecycleStage: ContactLifecycleStage }\n | { type: 'addContactTag'; tag: string }\n | { type: 'assignContactOwner'; ownerUid?: string; ownerEmail?: string; roundRobin?: boolean }\n | {\n type: 'createCrmTask'\n title: string\n kind: CrmTaskKind\n /** By meaning; the runner stores the org's label (AGL-3538). */\n priority?: CrmTaskPriority\n dueInDays: number\n assigneeUid?: string\n assigneeEmail?: string\n }\n | {\n type: 'logCrmActivity'\n kind: CrmActivityKind\n body: string\n /** Which way a call or an email went (AGL-3538). */\n direction?: CrmActivityDirection\n }\n) & {\n when?: InteractionStepGuard | null\n}\n\n/** Any step of an automation: a platform client step, or a server step. */\nexport type AiAutomationStep = ClientInteractionStep | AiAutomationServerStep\n\n/** A step type of the format. */\nexport type AiAutomationFormatStepType = AiAutomationStep['type']\n\n/** An automation, as the stored document holds it. */\nexport type AiAutomation = SiteInteraction<AiAutomationStep>\n\n/** How a step type reads in a sentence: its declared label, or its raw type. */\nexport function aiStepLabel(type: string): string {\n return interactionStepLabel(type) ?? type\n}\n\n/** The hold a step declares; a step this plugin drafts as a hold must declare one. */\nfunction declaredHolds(type: string): InteractionStepHolds {\n const holds = interactionStepHolds(type)\n if (!holds) throw new Error(`the \"${type}\" step declares no hold, and the automation drafter writes it as one`)\n return holds\n}\n\n/** The whole minutes a `wait` or a `waitForEvent` may hold, as their declarations give them. */\nexport const AI_AUTOMATION_WAIT_MINUTES: Readonly<{ min: number; max: number }> = (() => {\n const wait = declaredHolds('wait')\n return { min: wait.minMinutes, max: wait.maxMinutes }\n})()\n\n/**\n * The field a `waitForEvent` that ran out of time leaves in scope, which a\n * later step's guard names to take the timeout branch.\n */\nexport const AI_AUTOMATION_TIMED_OUT_FIELD: string = (() => {\n const field = declaredHolds('waitForEvent').timeoutField\n if (!field) throw new Error('the \"waitForEvent\" step declares no timeout field')\n return field\n})()\n"],"names":["interactionStepHolds","interactionStepLabel","aiStepLabel","type","declaredHolds","holds","Error","AI_AUTOMATION_WAIT_MINUTES","wait","min","minMinutes","max","maxMinutes","AI_AUTOMATION_TIMED_OUT_FIELD","field","timeoutField"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GASD,SAGEA,oBAAoB,EAEpBC,oBAAoB,QAEf,2CAA0C;AA6EjD,8EAA8E,GAC9E,OAAO,SAASC,YAAYC,IAAY;QAC/BF;IAAP,QAAOA,wBAAAA,qBAAqBE,iBAArBF,wBAA8BE;AACvC;AAEA,oFAAoF,GACpF,SAASC,cAAcD,IAAY;IACjC,MAAME,QAAQL,qBAAqBG;IACnC,IAAI,CAACE,OAAO,MAAM,IAAIC,MAAM,CAAC,KAAK,EAAEH,KAAK,oEAAoE,CAAC;IAC9G,OAAOE;AACT;AAEA,8FAA8F,GAC9F,OAAO,MAAME,6BAAqE,AAAC,CAAA;IACjF,MAAMC,OAAOJ,cAAc;IAC3B,OAAO;QAAEK,KAAKD,KAAKE,UAAU;QAAEC,KAAKH,KAAKI,UAAU;IAAC;AACtD,CAAA,IAAI;AAEJ;;;CAGC,GACD,OAAO,MAAMC,gCAAwC,AAAC,CAAA;IACpD,MAAMC,QAAQV,cAAc,gBAAgBW,YAAY;IACxD,IAAI,CAACD,OAAO,MAAM,IAAIR,MAAM;IAC5B,OAAOQ;AACT,CAAA,IAAI"}
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
-
import { type HostAction, type HostActionStepType } from '@aglyn/aglyn/app-utils/actions';
|
|
18
17
|
import type { AiAutomationNamedRecord, AiAutomationRecords } from './ai-automation-draft';
|
|
19
18
|
import type { AiWorkflowExplanation } from '../tools/ai-workflow-tool';
|
|
19
|
+
import { type AiAutomation, type AiAutomationFormatStepType } from './ai-automation-format';
|
|
20
20
|
/** How much of an email's text an outline quotes. */
|
|
21
21
|
export declare const AI_OUTLINE_BODY_MAX_CHARS = 300;
|
|
22
22
|
/** How much of any other value an outline quotes. */
|
|
@@ -26,11 +26,11 @@ export declare const AI_OUTLINE_RUN_ERRORS_MAX = 10;
|
|
|
26
26
|
/** A line with every email address replaced by what it is. */
|
|
27
27
|
export declare function aiRedactPersonal(text: string): string;
|
|
28
28
|
/** The kind of site record each step that names one names. */
|
|
29
|
-
export declare const AI_OUTLINE_RECORD_KINDS: Readonly<Partial<Record<
|
|
29
|
+
export declare const AI_OUTLINE_RECORD_KINDS: Readonly<Partial<Record<AiAutomationFormatStepType, 'lists' | 'campaigns' | 'workflows' | 'webhooks' | 'datasets'>>>;
|
|
30
30
|
/** The kinds of site record an action's steps name, each once. */
|
|
31
|
-
export declare function aiActionRecordKinds(action: Pick<
|
|
31
|
+
export declare function aiActionRecordKinds(action: Pick<AiAutomation, 'steps'>): Array<keyof AiAutomationRecords>;
|
|
32
32
|
/** A saved action as an outline. */
|
|
33
|
-
export declare function aiActionOutline(action:
|
|
33
|
+
export declare function aiActionOutline(action: AiAutomation, records: AiAutomationRecords | null): string;
|
|
34
34
|
/**
|
|
35
35
|
* A saved workflow as the workflows plugin's `workflow` record index shares
|
|
36
36
|
* it: its name, and the facts that index documents — the stored step list
|
|
@@ -13,10 +13,11 @@
|
|
|
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 {
|
|
17
|
-
import {
|
|
16
|
+
*/ import { draftPlaceholderIn } from "@aglyn/aglyn/app-utils/draft-placeholders";
|
|
17
|
+
import { CLIENT_ACTION_STEP_TYPES, normalizeTriggerConditions } from "@aglyn/aglyn/app-utils/site-interactions";
|
|
18
18
|
import { CONTACT_LIFECYCLE_STAGE_LABELS } from "@aglyn/aglyn/app-utils/crm";
|
|
19
19
|
import { aiAutomationTriggerLabel } from "./ai-workflow-job.js";
|
|
20
|
+
import { aiStepLabel } from "./ai-automation-format.js";
|
|
20
21
|
/**
|
|
21
22
|
* What an explanation is asked from (AGL-2919): a saved automation, and one of
|
|
22
23
|
* its failed runs, as plain lines a model reads — never the stored documents.
|
|
@@ -50,7 +51,7 @@ function conditionLine(condition) {
|
|
|
50
51
|
if (condition.op === 'notEmpty') return `${field} is not empty`;
|
|
51
52
|
const value = String((_condition_value = condition.value) != null ? _condition_value : '');
|
|
52
53
|
const stage = CONTACT_LIFECYCLE_STAGE_LABELS[value];
|
|
53
|
-
const placeholder =
|
|
54
|
+
const placeholder = draftPlaceholderIn(value) ? ' (a placeholder nobody has filled in)' : '';
|
|
54
55
|
return `${field} ${condition.op} ${stage ? `"${stage}"` : quoted(value)}${placeholder}`;
|
|
55
56
|
}
|
|
56
57
|
/** The kind of site record each step that names one names. */ export const AI_OUTLINE_RECORD_KINDS = {
|
|
@@ -69,7 +70,7 @@ function conditionLine(condition) {
|
|
|
69
70
|
];
|
|
70
71
|
}
|
|
71
72
|
/** Whether the site holds a record by the id or the name a step stores. */ function standing(id, name, records) {
|
|
72
|
-
if (
|
|
73
|
+
if (draftPlaceholderIn(name) && !String(id != null ? id : '').trim()) {
|
|
73
74
|
return ' — a placeholder nobody has filled in';
|
|
74
75
|
}
|
|
75
76
|
if (!records) return '';
|
|
@@ -77,9 +78,9 @@ function conditionLine(condition) {
|
|
|
77
78
|
return found ? ' — the site has it' : ' — the site has none by that name';
|
|
78
79
|
}
|
|
79
80
|
function stepLine(step, records) {
|
|
80
|
-
var
|
|
81
|
+
var _ref;
|
|
81
82
|
var _step_when, _step_when1;
|
|
82
|
-
const label = (
|
|
83
|
+
const label = aiStepLabel(step.type);
|
|
83
84
|
const detail = (()=>{
|
|
84
85
|
switch(step.type){
|
|
85
86
|
case 'sendEmail':
|
|
@@ -118,9 +119,10 @@ function stepLine(step, records) {
|
|
|
118
119
|
case 'assignContactOwner':
|
|
119
120
|
return step.roundRobin ? 'by round robin' : 'a named teammate';
|
|
120
121
|
case 'createCrmTask':
|
|
121
|
-
return `${quoted(step.title)}, a ${step.kind}, due in ${step.dueInDays} ${step.dueInDays === 1 ? 'day' : 'days'}${step.assigneeUid || step.assigneeEmail ? ', for a named teammate' : ''}`;
|
|
122
|
+
return `${quoted(step.title)}, a ${step.priority && step.priority !== 'normal' ? `${step.priority}-priority ` : ''}${step.kind}, due in ${step.dueInDays} ${step.dueInDays === 1 ? 'day' : 'days'}${step.assigneeUid || step.assigneeEmail ? ', for a named teammate' : ''}`;
|
|
122
123
|
case 'logCrmActivity':
|
|
123
|
-
|
|
124
|
+
// Every direction starts with a vowel: an outbound, inbound or internal call.
|
|
125
|
+
return `${step.direction ? `an ${step.direction}` : 'a'} ${step.kind}: ${quoted(step.body)}`;
|
|
124
126
|
case 'customEvent':
|
|
125
127
|
return quoted(step.eventName);
|
|
126
128
|
default:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-automation-outline.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 {\n CLIENT_ACTION_STEP_TYPES,\n HOST_ACTION_STEP_LABELS,\n type HostAction,\n type HostActionStep,\n type HostActionStepType,\n type HostActionTriggerCondition,\n normalizeTriggerConditions,\n} from '@aglyn/aglyn/app-utils/actions'\nimport { automationPlaceholderIn } from '@aglyn/aglyn/app-utils/automation-placeholders'\nimport { CONTACT_LIFECYCLE_STAGE_LABELS, type ContactLifecycleStage } from '@aglyn/aglyn/app-utils/crm'\nimport { aiAutomationTriggerLabel } from './ai-workflow-job'\nimport type { AiAutomationNamedRecord, AiAutomationRecords } from './ai-automation-draft'\nimport type { AiWorkflowExplanation } from '../tools/ai-workflow-tool'\n\n/**\n * What an explanation is asked from (AGL-2919): a saved automation, and one of\n * its failed runs, as plain lines a model reads — never the stored documents.\n *\n * WHAT IS LEFT OUT. An email address anywhere — a condition's value, an\n * email's text, a run's error naming the contact it could not find — becomes\n * `[email address]`. A teammate named by account id or address is \"a named\n * teammate\". A selector, a script or HTML an on-page step holds is not shown\n * at all; the step is named by its label. A run's event payload, which holds\n * what a visitor submitted, is never read. What is shown is how the automation\n * is set up, and what the run history recorded about the run.\n *\n * WHAT IS ADDED. Whether each list, campaign, workflow, webhook or dataset a\n * step names still exists on the site, looked up in code, because \"that list\n * was deleted\" is the answer to the most common failure and a model cannot\n * find it out.\n */\n\n/** An email address, wherever it appears in a line. */\nconst EMAIL_ADDRESS = /[^\\s@<>()[\\]\"',;:]+@[^\\s@<>()[\\]\"',;:]+\\.[A-Za-z]{2,}/g\n\n/** How much of an email's text an outline quotes. */\nexport const AI_OUTLINE_BODY_MAX_CHARS = 300\n/** How much of any other value an outline quotes. */\nexport const AI_OUTLINE_VALUE_MAX_CHARS = 120\n/** The most errors of one run an outline lists. */\nexport const AI_OUTLINE_RUN_ERRORS_MAX = 10\n\n/** A line with every email address replaced by what it is. */\nexport function aiRedactPersonal(text: string): string {\n return text.replace(EMAIL_ADDRESS, '[email address]')\n}\n\nfunction quoted(value: unknown, max = AI_OUTLINE_VALUE_MAX_CHARS): string {\n const text = aiRedactPersonal(String(value ?? '').replace(/\\s+/g, ' ').trim())\n return `\"${text.length > max ? `${text.slice(0, max)}…` : text}\"`\n}\n\nfunction conditionLine(condition: HostActionTriggerCondition): string {\n const field = String(condition.field ?? '')\n if (condition.op === 'notEmpty') return `${field} is not empty`\n const value = String(condition.value ?? '')\n const stage = CONTACT_LIFECYCLE_STAGE_LABELS[value as ContactLifecycleStage]\n const placeholder = automationPlaceholderIn(value) ? ' (a placeholder nobody has filled in)' : ''\n return `${field} ${condition.op} ${stage ? `\"${stage}\"` : quoted(value)}${placeholder}`\n}\n\n/** The kind of site record each step that names one names. */\nexport const AI_OUTLINE_RECORD_KINDS: Readonly<\n Partial<Record<HostActionStepType, 'lists' | 'campaigns' | 'workflows' | 'webhooks' | 'datasets'>>\n> = {\n enrollList: 'lists',\n assignCampaign: 'campaigns',\n runWorkflow: 'workflows',\n webhookPost: 'webhooks',\n datasetAppend: 'datasets',\n updateDataset: 'datasets',\n}\n\n/** The kinds of site record an action's steps name, each once. */\nexport function aiActionRecordKinds(action: Pick<HostAction, 'steps'>): Array<keyof AiAutomationRecords> {\n const kinds = (action.steps ?? []).map((step) => AI_OUTLINE_RECORD_KINDS[step?.type as HostActionStepType])\n return [...new Set(kinds.filter((kind): kind is NonNullable<typeof kind> => Boolean(kind)))]\n}\n\n/** Whether the site holds a record by the id or the name a step stores. */\nfunction standing(\n id: unknown,\n name: unknown,\n records: readonly AiAutomationNamedRecord[] | null,\n): string {\n if (automationPlaceholderIn(name) && !String(id ?? '').trim()) {\n return ' — a placeholder nobody has filled in'\n }\n if (!records) return ''\n const found = records.some(\n (record) =>\n (typeof id === 'string' && id && record.id === id) ||\n (typeof name === 'string' && name && record.name.trim() === name.trim()),\n )\n return found ? ' — the site has it' : ' — the site has none by that name'\n}\n\nfunction stepLine(\n step: HostActionStep,\n records: AiAutomationRecords | null,\n): string {\n const label = HOST_ACTION_STEP_LABELS[step.type as HostActionStepType] ?? step.type\n const detail = (() => {\n switch (step.type) {\n case 'sendEmail':\n return `subject ${quoted(step.subject)}, text ${quoted(step.body, AI_OUTLINE_BODY_MAX_CHARS)}, to the event's ${step.toField || 'email'} field`\n case 'notifyAdmins':\n return quoted(step.title)\n case 'enrollList':\n return `${quoted(step.listName || step.listId)}${standing(step.listId, step.listName, records?.lists ?? null)}`\n case 'assignCampaign':\n return `${quoted(step.campaignName || step.campaignId)}${standing(step.campaignId, step.campaignName, records?.campaigns ?? null)}`\n case 'runWorkflow':\n return `${quoted(step.workflowName || step.workflowId)}${standing(step.workflowId, step.workflowName, records?.workflows ?? null)}`\n case 'webhookPost':\n return `${quoted(step.webhookName || step.webhookId)}${standing(step.webhookId, step.webhookName, records?.webhooks ?? null)}`\n case 'datasetAppend':\n case 'updateDataset':\n return `${quoted(step.datasetName || step.datasetId)}${standing(step.datasetId, step.datasetName, records?.datasets ?? null)}`\n case 'siteAlert':\n return quoted(step.message)\n case 'wait':\n return durationOf(step.delayMinutes)\n case 'waitForEvent':\n return `${aiAutomationTriggerLabel(step.eventName)}, giving up after ${durationOf(step.timeoutMinutes)}`\n case 'exitFlow':\n return ''\n case 'setContactStage':\n return `\"${CONTACT_LIFECYCLE_STAGE_LABELS[step.lifecycleStage] ?? step.lifecycleStage}\"`\n case 'addContactTag':\n return quoted(step.tag)\n case 'assignContactOwner':\n return step.roundRobin ? 'by round robin' : 'a named teammate'\n case 'createCrmTask':\n return `${quoted(step.title)}, a ${step.kind}, due in ${step.dueInDays} ${step.dueInDays === 1 ? 'day' : 'days'}${\n step.assigneeUid || step.assigneeEmail ? ', for a named teammate' : ''\n }`\n case 'logCrmActivity':\n return `a ${step.kind}: ${quoted(step.body)}`\n case 'customEvent':\n return quoted(step.eventName)\n default:\n return CLIENT_ACTION_STEP_TYPES.has(step.type) ? 'runs in the visitor’s browser on the page' : ''\n }\n })()\n const guard = (step.when?.conditions ?? []).filter(Boolean)\n const only = guard.length\n ? ` Only if ${guard.map(conditionLine).join(step.when?.combinator === 'or' ? ' or ' : ' and ')}.`\n : ''\n return `${label}${detail ? `: ${detail}` : ''}.${only}`\n}\n\n/** Minutes as a person says them. */\nfunction durationOf(minutes: unknown): string {\n const value = Number(minutes)\n if (!Number.isFinite(value) || value <= 0) return 'no time'\n const units: Array<[number, string]> = [\n [60 * 24 * 7, 'week'],\n [60 * 24, 'day'],\n [60, 'hour'],\n [1, 'minute'],\n ]\n for (const [size, unit] of units) {\n if (value % size === 0) {\n const count = value / size\n return `${count} ${unit}${count === 1 ? '' : 's'}`\n }\n }\n return `${value} minutes`\n}\n\n/** A saved action as an outline. */\nexport function aiActionOutline(\n action: HostAction,\n records: AiAutomationRecords | null,\n): string {\n const lines = [\n `Automation: ${quoted(action.name)} — an action, switched ${action.enabled === false ? 'off' : 'on'}.`,\n `Starts when: ${aiAutomationTriggerLabel(String(action.trigger?.event ?? ''))}.`,\n ]\n const conditions = normalizeTriggerConditions(action.trigger)\n if (conditions.length) {\n lines.push(\n `Only when ${action.trigger?.combinator === 'or' ? 'any' : 'all'} of these hold: ${conditions\n .map(conditionLine)\n .join('; ')}.`,\n )\n }\n if (action.trigger?.filter?.trim()) {\n lines.push(`And only when this expression is true: ${quoted(action.trigger.filter)}.`)\n }\n lines.push('Steps, in order:')\n ;(action.steps ?? []).forEach((step, index) => {\n lines.push(`${index + 1}. ${stepLine(step, records)}`)\n })\n if (!(action.steps ?? []).length) lines.push('(none)')\n return lines.join('\\n')\n}\n\n/**\n * A saved workflow as the workflows plugin's `workflow` record index shares\n * it: its name, and the facts that index documents — the stored step list\n * (each a function call by `functionId`, `functionName`, `args` and\n * `resultName`), the scope name it returns, and its trigger.\n */\nexport interface AiIndexedWorkflow {\n name: string\n steps?: Array<{\n functionId?: string\n functionName?: string\n args?: string[]\n resultName?: string\n }>\n returnValue?: string | null\n trigger?: { event?: string; filter?: string } | null\n}\n\n/** A saved workflow — a pipeline of function calls — as an outline. */\nexport function aiWorkflowOutline(\n workflow: AiIndexedWorkflow,\n functions: readonly AiAutomationNamedRecord[] | null,\n): string {\n const lines = [`Workflow: ${quoted(workflow.name)} — a workflow, a list of function calls.`]\n if (workflow.trigger?.event) {\n const filter = workflow.trigger.filter?.trim()\n lines.push(\n `Starts when: ${aiAutomationTriggerLabel(workflow.trigger.event)}${\n filter ? `, only when this expression is true: ${quoted(filter)}` : ''\n }.`,\n )\n } else {\n lines.push('Starts when: it is called — by an automation, an inbound webhook or a computed variable.')\n }\n lines.push('Steps, in order:')\n ;(workflow.steps ?? []).forEach((step, index) => {\n const name = step.functionName || step.functionId || ''\n const exists = standing(step.functionId, step.functionName, functions)\n const args = (step.args ?? []).filter((arg) => String(arg ?? '').trim())\n lines.push(\n `${index + 1}. Runs the function ${quoted(name)}${exists}${\n args.length ? `, given ${args.map((arg) => quoted(arg)).join(', ')}` : ''\n }; its result is kept as ${quoted(step.resultName || `step${index + 1}`)}.`,\n )\n })\n if (!(workflow.steps ?? []).length) lines.push('(none)')\n if (workflow.returnValue?.trim()) lines.push(`Returns: ${quoted(workflow.returnValue)}.`)\n return lines.join('\\n')\n}\n\n/** What the run history recorded about one run, as far as an outline reads it. */\nexport interface AiRunRecord {\n result?: unknown\n trigger?: unknown\n summary?: unknown\n action?: unknown\n createdAt?: unknown\n}\n\nfunction whenOf(value: unknown): string | null {\n const toDate = (value as { toDate?: () => Date } | null)?.toDate\n const date =\n typeof toDate === 'function'\n ? toDate.call(value)\n : value instanceof Date\n ? value\n : null\n return date && Number.isFinite(date.getTime()) ? `${date.toISOString().slice(0, 16).replace('T', ' ')} UTC` : null\n}\n\n/** The errors a run recorded: the prose after \"with errors:\", or a workflow's failure. */\nexport function aiRunErrors(run: AiRunRecord): string[] {\n const action = String(run.action ?? '')\n const listed = action.match(/with errors:\\s*(.+)$/)?.[1]\n if (listed) {\n return listed\n .split(/;\\s+/)\n .map((error) => error.trim())\n .filter(Boolean)\n .slice(0, AI_OUTLINE_RUN_ERRORS_MAX)\n }\n const failed = action.match(/failed on [^:]+:\\s*(.+)$/)?.[1] ?? (run.result === 'failed' ? String(run.summary ?? '') : '')\n return failed.trim() ? [failed.trim()] : []\n}\n\n/** A failed run as an outline: when, on what, what it did, and what went wrong. */\nexport function aiRunOutline(run: AiRunRecord): string {\n const when = whenOf(run.createdAt)\n const lines = [\n `The run that failed: on ${aiAutomationTriggerLabel(String(run.trigger ?? ''))}${when ? `, ${when}` : ''}.`,\n ]\n const summary = String(run.summary ?? '').trim()\n const errors = aiRunErrors(run)\n if (summary && !errors.includes(summary)) lines.push(`What it did: ${aiRedactPersonal(summary)}.`)\n lines.push('What went wrong:')\n lines.push(...(errors.length ? errors.map((error) => `- ${aiRedactPersonal(error)}`) : ['- (the run recorded no error)']))\n return lines.join('\\n')\n}\n\n/** An explanation as the text a person reads. */\nexport function aiWorkflowExplanationText(\n explanation: AiWorkflowExplanation,\n mode: 'explain' | 'diagnose',\n): string {\n const parts = [explanation.summary]\n if (explanation.points.length) {\n parts.push(\n [\n mode === 'diagnose' ? 'What happened:' : 'What it does:',\n ...explanation.points.map((point, index) => `${index + 1}. ${point}`),\n ].join('\\n'),\n )\n }\n if (explanation.suggestions.length) {\n parts.push(\n [\n mode === 'diagnose' ? 'How to fix it:' : 'Worth checking:',\n ...explanation.suggestions.map((suggestion) => `- ${suggestion}`),\n ].join('\\n'),\n )\n }\n return parts.join('\\n\\n')\n}\n"],"names":["CLIENT_ACTION_STEP_TYPES","HOST_ACTION_STEP_LABELS","normalizeTriggerConditions","automationPlaceholderIn","CONTACT_LIFECYCLE_STAGE_LABELS","aiAutomationTriggerLabel","EMAIL_ADDRESS","AI_OUTLINE_BODY_MAX_CHARS","AI_OUTLINE_VALUE_MAX_CHARS","AI_OUTLINE_RUN_ERRORS_MAX","aiRedactPersonal","text","replace","quoted","value","max","String","trim","length","slice","conditionLine","condition","field","op","stage","placeholder","AI_OUTLINE_RECORD_KINDS","enrollList","assignCampaign","runWorkflow","webhookPost","datasetAppend","updateDataset","aiActionRecordKinds","action","kinds","steps","map","step","type","Set","filter","kind","Boolean","standing","id","name","records","found","some","record","stepLine","label","detail","subject","body","toField","title","listName","listId","lists","campaignName","campaignId","campaigns","workflowName","workflowId","workflows","webhookName","webhookId","webhooks","datasetName","datasetId","datasets","message","durationOf","delayMinutes","eventName","timeoutMinutes","lifecycleStage","tag","roundRobin","dueInDays","assigneeUid","assigneeEmail","has","guard","when","conditions","only","join","combinator","minutes","Number","isFinite","units","size","unit","count","aiActionOutline","lines","enabled","trigger","event","push","forEach","index","aiWorkflowOutline","workflow","functions","functionName","functionId","exists","args","arg","resultName","returnValue","whenOf","toDate","date","call","Date","getTime","toISOString","aiRunErrors","run","listed","match","split","error","failed","result","summary","aiRunOutline","createdAt","errors","includes","aiWorkflowExplanationText","explanation","mode","parts","points","point","suggestions","suggestion"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,wBAAwB,EACxBC,uBAAuB,EAKvBC,0BAA0B,QACrB,iCAAgC;AACvC,SAASC,uBAAuB,QAAQ,iDAAgD;AACxF,SAASC,8BAA8B,QAAoC,6BAA4B;AACvG,SAASC,wBAAwB,QAAQ,uBAAmB;AAI5D;;;;;;;;;;;;;;;;CAgBC,GAED,qDAAqD,GACrD,MAAMC,gBAAgB;AAEtB,mDAAmD,GACnD,OAAO,MAAMC,4BAA4B,IAAG;AAC5C,mDAAmD,GACnD,OAAO,MAAMC,6BAA6B,IAAG;AAC7C,iDAAiD,GACjD,OAAO,MAAMC,4BAA4B,GAAE;AAE3C,4DAA4D,GAC5D,OAAO,SAASC,iBAAiBC,IAAY;IAC3C,OAAOA,KAAKC,OAAO,CAACN,eAAe;AACrC;AAEA,SAASO,OAAOC,KAAc,EAAEC,MAAMP,0BAA0B;IAC9D,MAAMG,OAAOD,iBAAiBM,OAAOF,gBAAAA,QAAS,IAAIF,OAAO,CAAC,QAAQ,KAAKK,IAAI;IAC3E,OAAO,CAAC,CAAC,EAAEN,KAAKO,MAAM,GAAGH,MAAM,GAAGJ,KAAKQ,KAAK,CAAC,GAAGJ,KAAK,CAAC,CAAC,GAAGJ,KAAK,CAAC,CAAC;AACnE;AAEA,SAASS,cAAcC,SAAqC;QACrCA,kBAEAA;IAFrB,MAAMC,QAAQN,QAAOK,mBAAAA,UAAUC,KAAK,YAAfD,mBAAmB;IACxC,IAAIA,UAAUE,EAAE,KAAK,YAAY,OAAO,GAAGD,MAAM,aAAa,CAAC;IAC/D,MAAMR,QAAQE,QAAOK,mBAAAA,UAAUP,KAAK,YAAfO,mBAAmB;IACxC,MAAMG,QAAQpB,8BAA8B,CAACU,MAA+B;IAC5E,MAAMW,cAActB,wBAAwBW,SAAS,0CAA0C;IAC/F,OAAO,GAAGQ,MAAM,CAAC,EAAED,UAAUE,EAAE,CAAC,CAAC,EAAEC,QAAQ,CAAC,CAAC,EAAEA,MAAM,CAAC,CAAC,GAAGX,OAAOC,SAASW,aAAa;AACzF;AAEA,4DAA4D,GAC5D,OAAO,MAAMC,0BAET;IACFC,YAAY;IACZC,gBAAgB;IAChBC,aAAa;IACbC,aAAa;IACbC,eAAe;IACfC,eAAe;AACjB,EAAC;AAED,gEAAgE,GAChE,OAAO,SAASC,oBAAoBC,MAAiC;QACpDA;IAAf,MAAMC,QAAQ,EAACD,gBAAAA,OAAOE,KAAK,YAAZF,gBAAgB,EAAE,EAAEG,GAAG,CAAC,CAACC,OAASZ,uBAAuB,CAACY,wBAAAA,KAAMC,IAAI,CAAuB;IAC1G,OAAO;WAAI,IAAIC,IAAIL,MAAMM,MAAM,CAAC,CAACC,OAA2CC,QAAQD;KAAQ;AAC9F;AAEA,yEAAyE,GACzE,SAASE,SACPC,EAAW,EACXC,IAAa,EACbC,OAAkD;IAElD,IAAI5C,wBAAwB2C,SAAS,CAAC9B,OAAO6B,aAAAA,KAAM,IAAI5B,IAAI,IAAI;QAC7D,OAAO;IACT;IACA,IAAI,CAAC8B,SAAS,OAAO;IACrB,MAAMC,QAAQD,QAAQE,IAAI,CACxB,CAACC,SACC,AAAC,OAAOL,OAAO,YAAYA,MAAMK,OAAOL,EAAE,KAAKA,MAC9C,OAAOC,SAAS,YAAYA,QAAQI,OAAOJ,IAAI,CAAC7B,IAAI,OAAO6B,KAAK7B,IAAI;IAEzE,OAAO+B,QAAQ,uBAAuB;AACxC;AAEA,SAASG,SACPb,IAAoB,EACpBS,OAAmC;QAErB9C;QA4CCqC,YAE+BA;IA9C9C,MAAMc,SAAQnD,qCAAAA,uBAAuB,CAACqC,KAAKC,IAAI,CAAuB,YAAxDtC,qCAA4DqC,KAAKC,IAAI;IACnF,MAAMc,SAAS,AAAC,CAAA;QACd,OAAQf,KAAKC,IAAI;YACf,KAAK;gBACH,OAAO,CAAC,QAAQ,EAAE1B,OAAOyB,KAAKgB,OAAO,EAAE,OAAO,EAAEzC,OAAOyB,KAAKiB,IAAI,EAAEhD,2BAA2B,iBAAiB,EAAE+B,KAAKkB,OAAO,IAAI,QAAQ,MAAM,CAAC;YACjJ,KAAK;gBACH,OAAO3C,OAAOyB,KAAKmB,KAAK;YAC1B,KAAK;;gBACH,OAAO,GAAG5C,OAAOyB,KAAKoB,QAAQ,IAAIpB,KAAKqB,MAAM,IAAIf,SAASN,KAAKqB,MAAM,EAAErB,KAAKoB,QAAQ,UAAEX,2BAAAA,QAASa,KAAK,mBAAI,OAAO;YACjH,KAAK;;gBACH,OAAO,GAAG/C,OAAOyB,KAAKuB,YAAY,IAAIvB,KAAKwB,UAAU,IAAIlB,SAASN,KAAKwB,UAAU,EAAExB,KAAKuB,YAAY,WAAEd,2BAAAA,QAASgB,SAAS,oBAAI,OAAO;YACrI,KAAK;;gBACH,OAAO,GAAGlD,OAAOyB,KAAK0B,YAAY,IAAI1B,KAAK2B,UAAU,IAAIrB,SAASN,KAAK2B,UAAU,EAAE3B,KAAK0B,YAAY,WAAEjB,2BAAAA,QAASmB,SAAS,oBAAI,OAAO;YACrI,KAAK;;gBACH,OAAO,GAAGrD,OAAOyB,KAAK6B,WAAW,IAAI7B,KAAK8B,SAAS,IAAIxB,SAASN,KAAK8B,SAAS,EAAE9B,KAAK6B,WAAW,WAAEpB,2BAAAA,QAASsB,QAAQ,oBAAI,OAAO;YAChI,KAAK;YACL,KAAK;;gBACH,OAAO,GAAGxD,OAAOyB,KAAKgC,WAAW,IAAIhC,KAAKiC,SAAS,IAAI3B,SAASN,KAAKiC,SAAS,EAAEjC,KAAKgC,WAAW,WAAEvB,2BAAAA,QAASyB,QAAQ,oBAAI,OAAO;YAChI,KAAK;gBACH,OAAO3D,OAAOyB,KAAKmC,OAAO;YAC5B,KAAK;gBACH,OAAOC,WAAWpC,KAAKqC,YAAY;YACrC,KAAK;gBACH,OAAO,GAAGtE,yBAAyBiC,KAAKsC,SAAS,EAAE,kBAAkB,EAAEF,WAAWpC,KAAKuC,cAAc,GAAG;YAC1G,KAAK;gBACH,OAAO;YACT,KAAK;oBACQzE;gBAAX,OAAO,CAAC,CAAC,GAAEA,sDAAAA,8BAA8B,CAACkC,KAAKwC,cAAc,CAAC,YAAnD1E,sDAAuDkC,KAAKwC,cAAc,CAAC,CAAC,CAAC;YAC1F,KAAK;gBACH,OAAOjE,OAAOyB,KAAKyC,GAAG;YACxB,KAAK;gBACH,OAAOzC,KAAK0C,UAAU,GAAG,mBAAmB;YAC9C,KAAK;gBACH,OAAO,GAAGnE,OAAOyB,KAAKmB,KAAK,EAAE,IAAI,EAAEnB,KAAKI,IAAI,CAAC,SAAS,EAAEJ,KAAK2C,SAAS,CAAC,CAAC,EAAE3C,KAAK2C,SAAS,KAAK,IAAI,QAAQ,SACvG3C,KAAK4C,WAAW,IAAI5C,KAAK6C,aAAa,GAAG,2BAA2B,IACpE;YACJ,KAAK;gBACH,OAAO,CAAC,EAAE,EAAE7C,KAAKI,IAAI,CAAC,EAAE,EAAE7B,OAAOyB,KAAKiB,IAAI,GAAG;YAC/C,KAAK;gBACH,OAAO1C,OAAOyB,KAAKsC,SAAS;YAC9B;gBACE,OAAO5E,yBAAyBoF,GAAG,CAAC9C,KAAKC,IAAI,IAAI,8CAA8C;QACnG;IACF,CAAA;IACA,MAAM8C,QAAQ,UAAC/C,aAAAA,KAAKgD,IAAI,qBAAThD,WAAWiD,UAAU,mBAAI,EAAE,EAAE9C,MAAM,CAACE;IACnD,MAAM6C,OAAOH,MAAMnE,MAAM,GACrB,CAAC,SAAS,EAAEmE,MAAMhD,GAAG,CAACjB,eAAeqE,IAAI,CAACnD,EAAAA,cAAAA,KAAKgD,IAAI,qBAAThD,YAAWoD,UAAU,MAAK,OAAO,SAAS,SAAS,CAAC,CAAC,GAC/F;IACJ,OAAO,GAAGtC,QAAQC,SAAS,CAAC,EAAE,EAAEA,QAAQ,GAAG,GAAG,CAAC,EAAEmC,MAAM;AACzD;AAEA,mCAAmC,GACnC,SAASd,WAAWiB,OAAgB;IAClC,MAAM7E,QAAQ8E,OAAOD;IACrB,IAAI,CAACC,OAAOC,QAAQ,CAAC/E,UAAUA,SAAS,GAAG,OAAO;IAClD,MAAMgF,QAAiC;QACrC;YAAC,KAAK,KAAK;YAAG;SAAO;QACrB;YAAC,KAAK;YAAI;SAAM;QAChB;YAAC;YAAI;SAAO;QACZ;YAAC;YAAG;SAAS;KACd;IACD,KAAK,MAAM,CAACC,MAAMC,KAAK,IAAIF,MAAO;QAChC,IAAIhF,QAAQiF,SAAS,GAAG;YACtB,MAAME,QAAQnF,QAAQiF;YACtB,OAAO,GAAGE,MAAM,CAAC,EAAED,OAAOC,UAAU,IAAI,KAAK,KAAK;QACpD;IACF;IACA,OAAO,GAAGnF,MAAM,QAAQ,CAAC;AAC3B;AAEA,kCAAkC,GAClC,OAAO,SAASoF,gBACdhE,MAAkB,EAClBa,OAAmC;cAkBjCb,eAGIA;QAjB4CA,iBAU9CA,wBAAAA;IAZJ,MAAMiE,QAAQ;QACZ,CAAC,YAAY,EAAEtF,OAAOqB,OAAOY,IAAI,EAAE,uBAAuB,EAAEZ,OAAOkE,OAAO,KAAK,QAAQ,QAAQ,KAAK,CAAC,CAAC;QACtG,CAAC,aAAa,EAAE/F,yBAAyBW,gBAAOkB,kBAAAA,OAAOmE,OAAO,qBAAdnE,gBAAgBoE,KAAK,mBAAI,KAAK,CAAC,CAAC;KACjF;IACD,MAAMf,aAAarF,2BAA2BgC,OAAOmE,OAAO;IAC5D,IAAId,WAAWrE,MAAM,EAAE;YAENgB;QADfiE,MAAMI,IAAI,CACR,CAAC,UAAU,EAAErE,EAAAA,mBAAAA,OAAOmE,OAAO,qBAAdnE,iBAAgBwD,UAAU,MAAK,OAAO,QAAQ,MAAM,gBAAgB,EAAEH,WAChFlD,GAAG,CAACjB,eACJqE,IAAI,CAAC,MAAM,CAAC,CAAC;IAEpB;IACA,KAAIvD,mBAAAA,OAAOmE,OAAO,sBAAdnE,yBAAAA,iBAAgBO,MAAM,qBAAtBP,uBAAwBjB,IAAI,IAAI;QAClCkF,MAAMI,IAAI,CAAC,CAAC,uCAAuC,EAAE1F,OAAOqB,OAAOmE,OAAO,CAAC5D,MAAM,EAAE,CAAC,CAAC;IACvF;IACA0D,MAAMI,IAAI,CAAC;IACV,EAACrE,gBAAAA,OAAOE,KAAK,YAAZF,gBAAgB,EAAE,EAAEsE,OAAO,CAAC,CAAClE,MAAMmE;QACnCN,MAAMI,IAAI,CAAC,GAAGE,QAAQ,EAAE,EAAE,EAAEtD,SAASb,MAAMS,UAAU;IACvD;IACA,IAAI,CAAC,EAACb,iBAAAA,OAAOE,KAAK,YAAZF,iBAAgB,EAAE,EAAEhB,MAAM,EAAEiF,MAAMI,IAAI,CAAC;IAC7C,OAAOJ,MAAMV,IAAI,CAAC;AACpB;AAoBA,qEAAqE,GACrE,OAAO,SAASiB,kBACdC,QAA2B,EAC3BC,SAAoD;QAclDD,iBAUIA;QArBFA,mBAsBAA;IAvBJ,MAAMR,QAAQ;QAAC,CAAC,UAAU,EAAEtF,OAAO8F,SAAS7D,IAAI,EAAE,wCAAwC,CAAC;KAAC;IAC5F,KAAI6D,oBAAAA,SAASN,OAAO,qBAAhBM,kBAAkBL,KAAK,EAAE;YACZK;QAAf,MAAMlE,UAASkE,2BAAAA,SAASN,OAAO,CAAC5D,MAAM,qBAAvBkE,yBAAyB1F,IAAI;QAC5CkF,MAAMI,IAAI,CACR,CAAC,aAAa,EAAElG,yBAAyBsG,SAASN,OAAO,CAACC,KAAK,IAC7D7D,SAAS,CAAC,qCAAqC,EAAE5B,OAAO4B,SAAS,GAAG,GACrE,CAAC,CAAC;IAEP,OAAO;QACL0D,MAAMI,IAAI,CAAC;IACb;IACAJ,MAAMI,IAAI,CAAC;IACV,EAACI,kBAAAA,SAASvE,KAAK,YAAduE,kBAAkB,EAAE,EAAEH,OAAO,CAAC,CAAClE,MAAMmE;YAGvBnE;QAFd,MAAMQ,OAAOR,KAAKuE,YAAY,IAAIvE,KAAKwE,UAAU,IAAI;QACrD,MAAMC,SAASnE,SAASN,KAAKwE,UAAU,EAAExE,KAAKuE,YAAY,EAAED;QAC5D,MAAMI,OAAO,EAAC1E,aAAAA,KAAK0E,IAAI,YAAT1E,aAAa,EAAE,EAAEG,MAAM,CAAC,CAACwE,MAAQjG,OAAOiG,cAAAA,MAAO,IAAIhG,IAAI;QACrEkF,MAAMI,IAAI,CACR,GAAGE,QAAQ,EAAE,oBAAoB,EAAE5F,OAAOiC,QAAQiE,SAChDC,KAAK9F,MAAM,GAAG,CAAC,QAAQ,EAAE8F,KAAK3E,GAAG,CAAC,CAAC4E,MAAQpG,OAAOoG,MAAMxB,IAAI,CAAC,OAAO,GAAG,GACxE,wBAAwB,EAAE5E,OAAOyB,KAAK4E,UAAU,IAAI,CAAC,IAAI,EAAET,QAAQ,GAAG,EAAE,CAAC,CAAC;IAE/E;IACA,IAAI,CAAC,EAACE,mBAAAA,SAASvE,KAAK,YAAduE,mBAAkB,EAAE,EAAEzF,MAAM,EAAEiF,MAAMI,IAAI,CAAC;IAC/C,KAAII,wBAAAA,SAASQ,WAAW,qBAApBR,sBAAsB1F,IAAI,IAAIkF,MAAMI,IAAI,CAAC,CAAC,SAAS,EAAE1F,OAAO8F,SAASQ,WAAW,EAAE,CAAC,CAAC;IACxF,OAAOhB,MAAMV,IAAI,CAAC;AACpB;AAWA,SAAS2B,OAAOtG,KAAc;IAC5B,MAAMuG,SAAUvG,yBAAD,AAACA,MAA0CuG,MAAM;IAChE,MAAMC,OACJ,OAAOD,WAAW,aACdA,OAAOE,IAAI,CAACzG,SACZA,iBAAiB0G,OACf1G,QACA;IACR,OAAOwG,QAAQ1B,OAAOC,QAAQ,CAACyB,KAAKG,OAAO,MAAM,GAAGH,KAAKI,WAAW,GAAGvG,KAAK,CAAC,GAAG,IAAIP,OAAO,CAAC,KAAK,KAAK,IAAI,CAAC,GAAG;AAChH;AAEA,wFAAwF,GACxF,OAAO,SAAS+G,YAAYC,GAAgB;QACpBA,mBAS4EA;QARnF1F,eAQAA;IATf,MAAMA,SAASlB,QAAO4G,cAAAA,IAAI1F,MAAM,YAAV0F,cAAc;IACpC,MAAMC,UAAS3F,gBAAAA,OAAO4F,KAAK,CAAC,4CAAb5F,aAAsC,CAAC,EAAE;IACxD,IAAI2F,QAAQ;QACV,OAAOA,OACJE,KAAK,CAAC,QACN1F,GAAG,CAAC,CAAC2F,QAAUA,MAAM/G,IAAI,IACzBwB,MAAM,CAACE,SACPxB,KAAK,CAAC,GAAGV;IACd;IACA,MAAMwH,kBAAS/F,iBAAAA,OAAO4F,KAAK,CAAC,gDAAb5F,cAA0C,CAAC,EAAE,mBAAK0F,IAAIM,MAAM,KAAK,WAAWlH,QAAO4G,eAAAA,IAAIO,OAAO,YAAXP,eAAe,MAAM;IACvH,OAAOK,OAAOhH,IAAI,KAAK;QAACgH,OAAOhH,IAAI;KAAG,GAAG,EAAE;AAC7C;AAEA,iFAAiF,GACjF,OAAO,SAASmH,aAAaR,GAAgB;QAGkBA,cAEtCA;IAJvB,MAAMtC,OAAO8B,OAAOQ,IAAIS,SAAS;IACjC,MAAMlC,QAAQ;QACZ,CAAC,wBAAwB,EAAE9F,yBAAyBW,QAAO4G,eAAAA,IAAIvB,OAAO,YAAXuB,eAAe,OAAOtC,OAAO,CAAC,EAAE,EAAEA,MAAM,GAAG,GAAG,CAAC,CAAC;KAC5G;IACD,MAAM6C,UAAUnH,QAAO4G,eAAAA,IAAIO,OAAO,YAAXP,eAAe,IAAI3G,IAAI;IAC9C,MAAMqH,SAASX,YAAYC;IAC3B,IAAIO,WAAW,CAACG,OAAOC,QAAQ,CAACJ,UAAUhC,MAAMI,IAAI,CAAC,CAAC,aAAa,EAAE7F,iBAAiByH,SAAS,CAAC,CAAC;IACjGhC,MAAMI,IAAI,CAAC;IACXJ,MAAMI,IAAI,IAAK+B,OAAOpH,MAAM,GAAGoH,OAAOjG,GAAG,CAAC,CAAC2F,QAAU,CAAC,EAAE,EAAEtH,iBAAiBsH,QAAQ,IAAI;QAAC;KAAgC;IACxH,OAAO7B,MAAMV,IAAI,CAAC;AACpB;AAEA,+CAA+C,GAC/C,OAAO,SAAS+C,0BACdC,WAAkC,EAClCC,IAA4B;IAE5B,MAAMC,QAAQ;QAACF,YAAYN,OAAO;KAAC;IACnC,IAAIM,YAAYG,MAAM,CAAC1H,MAAM,EAAE;QAC7ByH,MAAMpC,IAAI,CACR;YACEmC,SAAS,aAAa,mBAAmB;eACtCD,YAAYG,MAAM,CAACvG,GAAG,CAAC,CAACwG,OAAOpC,QAAU,GAAGA,QAAQ,EAAE,EAAE,EAAEoC,OAAO;SACrE,CAACpD,IAAI,CAAC;IAEX;IACA,IAAIgD,YAAYK,WAAW,CAAC5H,MAAM,EAAE;QAClCyH,MAAMpC,IAAI,CACR;YACEmC,SAAS,aAAa,mBAAmB;eACtCD,YAAYK,WAAW,CAACzG,GAAG,CAAC,CAAC0G,aAAe,CAAC,EAAE,EAAEA,YAAY;SACjE,CAACtD,IAAI,CAAC;IAEX;IACA,OAAOkD,MAAMlD,IAAI,CAAC;AACpB"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-automation-outline.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 { draftPlaceholderIn } from '@aglyn/aglyn/app-utils/draft-placeholders'\nimport {\n CLIENT_ACTION_STEP_TYPES,\n type InteractionCondition,\n normalizeTriggerConditions,\n} from '@aglyn/aglyn/app-utils/site-interactions'\nimport { CONTACT_LIFECYCLE_STAGE_LABELS, type ContactLifecycleStage } from '@aglyn/aglyn/app-utils/crm'\nimport { aiAutomationTriggerLabel } from './ai-workflow-job'\nimport type { AiAutomationNamedRecord, AiAutomationRecords } from './ai-automation-draft'\nimport type { AiWorkflowExplanation } from '../tools/ai-workflow-tool'\nimport {\n type AiAutomation,\n type AiAutomationFormatStepType,\n type AiAutomationStep,\n aiStepLabel,\n} from './ai-automation-format'\n\n/**\n * What an explanation is asked from (AGL-2919): a saved automation, and one of\n * its failed runs, as plain lines a model reads — never the stored documents.\n *\n * WHAT IS LEFT OUT. An email address anywhere — a condition's value, an\n * email's text, a run's error naming the contact it could not find — becomes\n * `[email address]`. A teammate named by account id or address is \"a named\n * teammate\". A selector, a script or HTML an on-page step holds is not shown\n * at all; the step is named by its label. A run's event payload, which holds\n * what a visitor submitted, is never read. What is shown is how the automation\n * is set up, and what the run history recorded about the run.\n *\n * WHAT IS ADDED. Whether each list, campaign, workflow, webhook or dataset a\n * step names still exists on the site, looked up in code, because \"that list\n * was deleted\" is the answer to the most common failure and a model cannot\n * find it out.\n */\n\n/** An email address, wherever it appears in a line. */\nconst EMAIL_ADDRESS = /[^\\s@<>()[\\]\"',;:]+@[^\\s@<>()[\\]\"',;:]+\\.[A-Za-z]{2,}/g\n\n/** How much of an email's text an outline quotes. */\nexport const AI_OUTLINE_BODY_MAX_CHARS = 300\n/** How much of any other value an outline quotes. */\nexport const AI_OUTLINE_VALUE_MAX_CHARS = 120\n/** The most errors of one run an outline lists. */\nexport const AI_OUTLINE_RUN_ERRORS_MAX = 10\n\n/** A line with every email address replaced by what it is. */\nexport function aiRedactPersonal(text: string): string {\n return text.replace(EMAIL_ADDRESS, '[email address]')\n}\n\nfunction quoted(value: unknown, max = AI_OUTLINE_VALUE_MAX_CHARS): string {\n const text = aiRedactPersonal(String(value ?? '').replace(/\\s+/g, ' ').trim())\n return `\"${text.length > max ? `${text.slice(0, max)}…` : text}\"`\n}\n\nfunction conditionLine(condition: InteractionCondition): string {\n const field = String(condition.field ?? '')\n if (condition.op === 'notEmpty') return `${field} is not empty`\n const value = String(condition.value ?? '')\n const stage = CONTACT_LIFECYCLE_STAGE_LABELS[value as ContactLifecycleStage]\n const placeholder = draftPlaceholderIn(value) ? ' (a placeholder nobody has filled in)' : ''\n return `${field} ${condition.op} ${stage ? `\"${stage}\"` : quoted(value)}${placeholder}`\n}\n\n/** The kind of site record each step that names one names. */\nexport const AI_OUTLINE_RECORD_KINDS: Readonly<\n Partial<Record<AiAutomationFormatStepType, 'lists' | 'campaigns' | 'workflows' | 'webhooks' | 'datasets'>>\n> = {\n enrollList: 'lists',\n assignCampaign: 'campaigns',\n runWorkflow: 'workflows',\n webhookPost: 'webhooks',\n datasetAppend: 'datasets',\n updateDataset: 'datasets',\n}\n\n/** The kinds of site record an action's steps name, each once. */\nexport function aiActionRecordKinds(action: Pick<AiAutomation, 'steps'>): Array<keyof AiAutomationRecords> {\n const kinds = (action.steps ?? []).map((step) => AI_OUTLINE_RECORD_KINDS[step?.type as AiAutomationFormatStepType])\n return [...new Set(kinds.filter((kind): kind is NonNullable<typeof kind> => Boolean(kind)))]\n}\n\n/** Whether the site holds a record by the id or the name a step stores. */\nfunction standing(\n id: unknown,\n name: unknown,\n records: readonly AiAutomationNamedRecord[] | null,\n): string {\n if (draftPlaceholderIn(name) && !String(id ?? '').trim()) {\n return ' — a placeholder nobody has filled in'\n }\n if (!records) return ''\n const found = records.some(\n (record) =>\n (typeof id === 'string' && id && record.id === id) ||\n (typeof name === 'string' && name && record.name.trim() === name.trim()),\n )\n return found ? ' — the site has it' : ' — the site has none by that name'\n}\n\nfunction stepLine(\n step: AiAutomationStep,\n records: AiAutomationRecords | null,\n): string {\n const label = aiStepLabel(step.type)\n const detail = (() => {\n switch (step.type) {\n case 'sendEmail':\n return `subject ${quoted(step.subject)}, text ${quoted(step.body, AI_OUTLINE_BODY_MAX_CHARS)}, to the event's ${step.toField || 'email'} field`\n case 'notifyAdmins':\n return quoted(step.title)\n case 'enrollList':\n return `${quoted(step.listName || step.listId)}${standing(step.listId, step.listName, records?.lists ?? null)}`\n case 'assignCampaign':\n return `${quoted(step.campaignName || step.campaignId)}${standing(step.campaignId, step.campaignName, records?.campaigns ?? null)}`\n case 'runWorkflow':\n return `${quoted(step.workflowName || step.workflowId)}${standing(step.workflowId, step.workflowName, records?.workflows ?? null)}`\n case 'webhookPost':\n return `${quoted(step.webhookName || step.webhookId)}${standing(step.webhookId, step.webhookName, records?.webhooks ?? null)}`\n case 'datasetAppend':\n case 'updateDataset':\n return `${quoted(step.datasetName || step.datasetId)}${standing(step.datasetId, step.datasetName, records?.datasets ?? null)}`\n case 'siteAlert':\n return quoted(step.message)\n case 'wait':\n return durationOf(step.delayMinutes)\n case 'waitForEvent':\n return `${aiAutomationTriggerLabel(step.eventName)}, giving up after ${durationOf(step.timeoutMinutes)}`\n case 'exitFlow':\n return ''\n case 'setContactStage':\n return `\"${CONTACT_LIFECYCLE_STAGE_LABELS[step.lifecycleStage] ?? step.lifecycleStage}\"`\n case 'addContactTag':\n return quoted(step.tag)\n case 'assignContactOwner':\n return step.roundRobin ? 'by round robin' : 'a named teammate'\n case 'createCrmTask':\n return `${quoted(step.title)}, a ${step.priority && step.priority !== 'normal' ? `${step.priority}-priority ` : ''}${step.kind}, due in ${step.dueInDays} ${step.dueInDays === 1 ? 'day' : 'days'}${\n step.assigneeUid || step.assigneeEmail ? ', for a named teammate' : ''\n }`\n case 'logCrmActivity':\n // Every direction starts with a vowel: an outbound, inbound or internal call.\n return `${step.direction ? `an ${step.direction}` : 'a'} ${step.kind}: ${quoted(step.body)}`\n case 'customEvent':\n return quoted(step.eventName)\n default:\n return CLIENT_ACTION_STEP_TYPES.has(step.type) ? 'runs in the visitor’s browser on the page' : ''\n }\n })()\n const guard = (step.when?.conditions ?? []).filter(Boolean)\n const only = guard.length\n ? ` Only if ${guard.map(conditionLine).join(step.when?.combinator === 'or' ? ' or ' : ' and ')}.`\n : ''\n return `${label}${detail ? `: ${detail}` : ''}.${only}`\n}\n\n/** Minutes as a person says them. */\nfunction durationOf(minutes: unknown): string {\n const value = Number(minutes)\n if (!Number.isFinite(value) || value <= 0) return 'no time'\n const units: Array<[number, string]> = [\n [60 * 24 * 7, 'week'],\n [60 * 24, 'day'],\n [60, 'hour'],\n [1, 'minute'],\n ]\n for (const [size, unit] of units) {\n if (value % size === 0) {\n const count = value / size\n return `${count} ${unit}${count === 1 ? '' : 's'}`\n }\n }\n return `${value} minutes`\n}\n\n/** A saved action as an outline. */\nexport function aiActionOutline(\n action: AiAutomation,\n records: AiAutomationRecords | null,\n): string {\n const lines = [\n `Automation: ${quoted(action.name)} — an action, switched ${action.enabled === false ? 'off' : 'on'}.`,\n `Starts when: ${aiAutomationTriggerLabel(String(action.trigger?.event ?? ''))}.`,\n ]\n const conditions = normalizeTriggerConditions(action.trigger)\n if (conditions.length) {\n lines.push(\n `Only when ${action.trigger?.combinator === 'or' ? 'any' : 'all'} of these hold: ${conditions\n .map(conditionLine)\n .join('; ')}.`,\n )\n }\n if (action.trigger?.filter?.trim()) {\n lines.push(`And only when this expression is true: ${quoted(action.trigger.filter)}.`)\n }\n lines.push('Steps, in order:')\n ;(action.steps ?? []).forEach((step, index) => {\n lines.push(`${index + 1}. ${stepLine(step, records)}`)\n })\n if (!(action.steps ?? []).length) lines.push('(none)')\n return lines.join('\\n')\n}\n\n/**\n * A saved workflow as the workflows plugin's `workflow` record index shares\n * it: its name, and the facts that index documents — the stored step list\n * (each a function call by `functionId`, `functionName`, `args` and\n * `resultName`), the scope name it returns, and its trigger.\n */\nexport interface AiIndexedWorkflow {\n name: string\n steps?: Array<{\n functionId?: string\n functionName?: string\n args?: string[]\n resultName?: string\n }>\n returnValue?: string | null\n trigger?: { event?: string; filter?: string } | null\n}\n\n/** A saved workflow — a pipeline of function calls — as an outline. */\nexport function aiWorkflowOutline(\n workflow: AiIndexedWorkflow,\n functions: readonly AiAutomationNamedRecord[] | null,\n): string {\n const lines = [`Workflow: ${quoted(workflow.name)} — a workflow, a list of function calls.`]\n if (workflow.trigger?.event) {\n const filter = workflow.trigger.filter?.trim()\n lines.push(\n `Starts when: ${aiAutomationTriggerLabel(workflow.trigger.event)}${\n filter ? `, only when this expression is true: ${quoted(filter)}` : ''\n }.`,\n )\n } else {\n lines.push('Starts when: it is called — by an automation, an inbound webhook or a computed variable.')\n }\n lines.push('Steps, in order:')\n ;(workflow.steps ?? []).forEach((step, index) => {\n const name = step.functionName || step.functionId || ''\n const exists = standing(step.functionId, step.functionName, functions)\n const args = (step.args ?? []).filter((arg) => String(arg ?? '').trim())\n lines.push(\n `${index + 1}. Runs the function ${quoted(name)}${exists}${\n args.length ? `, given ${args.map((arg) => quoted(arg)).join(', ')}` : ''\n }; its result is kept as ${quoted(step.resultName || `step${index + 1}`)}.`,\n )\n })\n if (!(workflow.steps ?? []).length) lines.push('(none)')\n if (workflow.returnValue?.trim()) lines.push(`Returns: ${quoted(workflow.returnValue)}.`)\n return lines.join('\\n')\n}\n\n/** What the run history recorded about one run, as far as an outline reads it. */\nexport interface AiRunRecord {\n result?: unknown\n trigger?: unknown\n summary?: unknown\n action?: unknown\n createdAt?: unknown\n}\n\nfunction whenOf(value: unknown): string | null {\n const toDate = (value as { toDate?: () => Date } | null)?.toDate\n const date =\n typeof toDate === 'function'\n ? toDate.call(value)\n : value instanceof Date\n ? value\n : null\n return date && Number.isFinite(date.getTime()) ? `${date.toISOString().slice(0, 16).replace('T', ' ')} UTC` : null\n}\n\n/** The errors a run recorded: the prose after \"with errors:\", or a workflow's failure. */\nexport function aiRunErrors(run: AiRunRecord): string[] {\n const action = String(run.action ?? '')\n const listed = action.match(/with errors:\\s*(.+)$/)?.[1]\n if (listed) {\n return listed\n .split(/;\\s+/)\n .map((error) => error.trim())\n .filter(Boolean)\n .slice(0, AI_OUTLINE_RUN_ERRORS_MAX)\n }\n const failed = action.match(/failed on [^:]+:\\s*(.+)$/)?.[1] ?? (run.result === 'failed' ? String(run.summary ?? '') : '')\n return failed.trim() ? [failed.trim()] : []\n}\n\n/** A failed run as an outline: when, on what, what it did, and what went wrong. */\nexport function aiRunOutline(run: AiRunRecord): string {\n const when = whenOf(run.createdAt)\n const lines = [\n `The run that failed: on ${aiAutomationTriggerLabel(String(run.trigger ?? ''))}${when ? `, ${when}` : ''}.`,\n ]\n const summary = String(run.summary ?? '').trim()\n const errors = aiRunErrors(run)\n if (summary && !errors.includes(summary)) lines.push(`What it did: ${aiRedactPersonal(summary)}.`)\n lines.push('What went wrong:')\n lines.push(...(errors.length ? errors.map((error) => `- ${aiRedactPersonal(error)}`) : ['- (the run recorded no error)']))\n return lines.join('\\n')\n}\n\n/** An explanation as the text a person reads. */\nexport function aiWorkflowExplanationText(\n explanation: AiWorkflowExplanation,\n mode: 'explain' | 'diagnose',\n): string {\n const parts = [explanation.summary]\n if (explanation.points.length) {\n parts.push(\n [\n mode === 'diagnose' ? 'What happened:' : 'What it does:',\n ...explanation.points.map((point, index) => `${index + 1}. ${point}`),\n ].join('\\n'),\n )\n }\n if (explanation.suggestions.length) {\n parts.push(\n [\n mode === 'diagnose' ? 'How to fix it:' : 'Worth checking:',\n ...explanation.suggestions.map((suggestion) => `- ${suggestion}`),\n ].join('\\n'),\n )\n }\n return parts.join('\\n\\n')\n}\n"],"names":["draftPlaceholderIn","CLIENT_ACTION_STEP_TYPES","normalizeTriggerConditions","CONTACT_LIFECYCLE_STAGE_LABELS","aiAutomationTriggerLabel","aiStepLabel","EMAIL_ADDRESS","AI_OUTLINE_BODY_MAX_CHARS","AI_OUTLINE_VALUE_MAX_CHARS","AI_OUTLINE_RUN_ERRORS_MAX","aiRedactPersonal","text","replace","quoted","value","max","String","trim","length","slice","conditionLine","condition","field","op","stage","placeholder","AI_OUTLINE_RECORD_KINDS","enrollList","assignCampaign","runWorkflow","webhookPost","datasetAppend","updateDataset","aiActionRecordKinds","action","kinds","steps","map","step","type","Set","filter","kind","Boolean","standing","id","name","records","found","some","record","stepLine","label","detail","subject","body","toField","title","listName","listId","lists","campaignName","campaignId","campaigns","workflowName","workflowId","workflows","webhookName","webhookId","webhooks","datasetName","datasetId","datasets","message","durationOf","delayMinutes","eventName","timeoutMinutes","lifecycleStage","tag","roundRobin","priority","dueInDays","assigneeUid","assigneeEmail","direction","has","guard","when","conditions","only","join","combinator","minutes","Number","isFinite","units","size","unit","count","aiActionOutline","lines","enabled","trigger","event","push","forEach","index","aiWorkflowOutline","workflow","functions","functionName","functionId","exists","args","arg","resultName","returnValue","whenOf","toDate","date","call","Date","getTime","toISOString","aiRunErrors","run","listed","match","split","error","failed","result","summary","aiRunOutline","createdAt","errors","includes","aiWorkflowExplanationText","explanation","mode","parts","points","point","suggestions","suggestion"],"mappings":"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"}
|
|
@@ -94,6 +94,19 @@ export interface AiBuildPlanSection {
|
|
|
94
94
|
/** How many repeated items it shows (cards, rows, tiles); 0 when none. */
|
|
95
95
|
items: number;
|
|
96
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* A screen that is one dataset's record template (AGL-3475): the data plugin
|
|
99
|
+
* serves it once per record at `/{base}/{slug}`, its copy bound with
|
|
100
|
+
* `{{item.<field>}}`. The plan names the binding and the build drafts the
|
|
101
|
+
* page; the member saves the binding in Page Properties → Record pages, since
|
|
102
|
+
* a binding is a route and a job writes drafts only (rule 13).
|
|
103
|
+
*/
|
|
104
|
+
export interface AiBuildPlanRecord {
|
|
105
|
+
/** The dataset: an inventory id or `new:<name>`. */
|
|
106
|
+
dataset: string;
|
|
107
|
+
/** The path its record pages live under, one or more segments: `services`, `services/residential`. */
|
|
108
|
+
base: string;
|
|
109
|
+
}
|
|
97
110
|
export interface AiBuildPlanScreen {
|
|
98
111
|
title: string;
|
|
99
112
|
slug: string;
|
|
@@ -108,6 +121,12 @@ export interface AiBuildPlanScreen {
|
|
|
108
121
|
seoTitle: string;
|
|
109
122
|
seoDescription: string;
|
|
110
123
|
sections: AiBuildPlanSection[];
|
|
124
|
+
/**
|
|
125
|
+
* The dataset this page is the record template of, or null for a page that
|
|
126
|
+
* is one page. A plan kept before record templates carries none, which
|
|
127
|
+
* reads as null.
|
|
128
|
+
*/
|
|
129
|
+
record: AiBuildPlanRecord | null;
|
|
111
130
|
/**
|
|
112
131
|
* The id the draft screen is written under, where a unit of the job builds
|
|
113
132
|
* it (AGL-3079). Minted when the job keeps the plan, never part of the
|
|
@@ -186,8 +205,19 @@ export declare const AI_PLAN_ASKS_FOR_VIDEO: RegExp;
|
|
|
186
205
|
* player it was never asked for.
|
|
187
206
|
*/
|
|
188
207
|
export declare const AI_BUILD_PLAN_EMBEDS_TOOL: AiTool;
|
|
189
|
-
/**
|
|
190
|
-
|
|
208
|
+
/**
|
|
209
|
+
* The plan tools with a record template on every screen (AGL-3475), offered
|
|
210
|
+
* only where the job may bind a dataset: a site with a dataset, or a job that
|
|
211
|
+
* may create one. Record pages are a Starter feature, and every other plan's
|
|
212
|
+
* request is the plain tool, byte for byte, so the field costs a Free taste
|
|
213
|
+
* nothing and a model planning a site with no dataset has none to bind.
|
|
214
|
+
*/
|
|
215
|
+
export declare const AI_BUILD_PLAN_RECORDS_TOOL: AiTool;
|
|
216
|
+
export declare const AI_BUILD_PLAN_EMBEDS_RECORDS_TOOL: AiTool;
|
|
217
|
+
/** The plan tool a brief is answered with, on a job that may or may not bind a dataset. */
|
|
218
|
+
export declare function aiBuildPlanToolFor(brief: string, options?: {
|
|
219
|
+
records?: boolean;
|
|
220
|
+
}): AiTool;
|
|
191
221
|
export type AiBuildPlanParse = {
|
|
192
222
|
ok: true;
|
|
193
223
|
plan: AiBuildPlan;
|
|
@@ -205,6 +235,13 @@ export type AiBuildPlanParse = {
|
|
|
205
235
|
* repaired: copy past a ceiling is cut and the cut is listed.
|
|
206
236
|
*/
|
|
207
237
|
export declare function parseAiBuildPlan(input: unknown): AiBuildPlanParse;
|
|
238
|
+
/**
|
|
239
|
+
* A record template's base as the data plugin's save route stores it
|
|
240
|
+
* (`normalizeRecordPageBase`, which this plugin may not import): one to four
|
|
241
|
+
* lowercase segments of letters, digits and hyphens, with no leading or
|
|
242
|
+
* trailing slash; null for a base that is no such path.
|
|
243
|
+
*/
|
|
244
|
+
export declare function aiPlanRecordBase(base: string): string | null;
|
|
208
245
|
/** Whether a reference names something the plan creates. */
|
|
209
246
|
export declare function isAiPlanNewRef(ref: string | null | undefined): ref is string;
|
|
210
247
|
/** The creation a `new:<name>` reference names, case-insensitively. */
|