@salesforce/afv-skills 1.44.0 → 1.46.0
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 +1 -1
- package/skills/automation-flow-generate/SKILL.md +11 -5
- package/skills/consumer-goods-rtr-datacloud-export-configure/SKILL.md +72 -0
- package/skills/consumer-goods-rtr-datacloud-export-configure/references/inputs-and-namespace.md +38 -0
- package/skills/consumer-goods-rtr-datacloud-export-configure/references/procedure.md +158 -0
- package/skills/consumer-goods-rtr-datacloud-export-configure/scripts/detect-namespace.js +86 -0
- package/skills/consumer-goods-rtr-datacloud-export-configure/scripts/render-apex.js +64 -0
- package/skills/consumer-goods-rtr-datacloud-export-configure/scripts/resolve-id-by-name.js +47 -0
- package/skills/consumer-goods-rtr-datacloud-export-configure/scripts/sf-rest.js +171 -0
- package/skills/consumer-goods-rtr-datacloud-export-configure/scripts/soql-escape.js +26 -0
- package/skills/consumer-goods-rtr-datacloud-export-configure/scripts/upsert-report-config.apex +51 -0
- package/skills/consumer-goods-rtr-datacloud-export-configure/scripts/upsert-system-setting.apex +21 -0
- package/skills/consumer-goods-tpe-dashboard-configure/SKILL.md +74 -0
- package/skills/consumer-goods-tpe-dashboard-configure/references/phases-1-6.md +112 -0
- package/skills/consumer-goods-tpe-dashboard-configure/references/phases-7-12.md +157 -0
- package/skills/consumer-goods-tpe-dashboard-configure/scripts/find-failure-reason.js +132 -0
- package/skills/consumer-goods-tpe-dashboard-configure/scripts/poll-status.js +116 -0
- package/skills/consumer-goods-tpe-dashboard-configure/scripts/render-apex.js +64 -0
- package/skills/consumer-goods-tpe-dashboard-configure/scripts/run-data-transform.js +121 -0
- package/skills/consumer-goods-tpe-dashboard-configure/scripts/schedule-business-period-export.apex +27 -0
- package/skills/consumer-goods-tpe-dashboard-configure/scripts/sf-rest.js +171 -0
- package/skills/consumer-goods-tpe-dashboard-configure/scripts/soql-escape.js +25 -0
- package/skills/consumer-goods-tpe-dashboard-custom-kpi-configure/SKILL.md +141 -0
- package/skills/consumer-goods-tpe-dashboard-custom-kpi-configure/references/payload-shapes.md +447 -0
- package/skills/consumer-goods-tpe-dashboard-custom-kpi-configure/references/procedure.md +263 -0
- package/skills/consumer-goods-tpe-dashboard-custom-kpi-configure/scripts/clone-tpe-dashboards.js +537 -0
- package/skills/consumer-goods-tpe-dashboard-custom-kpi-configure/scripts/sf-rest.js +195 -0
- package/skills/consumer-goods-tpe-datakit-deploy/SKILL.md +157 -0
- package/skills/consumer-goods-tpe-datakit-deploy/scripts/detect-namespace.js +86 -0
- package/skills/consumer-goods-tpe-datakit-deploy/scripts/download-static-resource.js +151 -0
- package/skills/consumer-goods-tpe-datakit-deploy/scripts/extract-crm-field-permissions.js +115 -0
- package/skills/consumer-goods-tpe-datakit-deploy/scripts/sf-rest.js +109 -0
- package/skills/consumer-goods-tpe-datakit-deploy/scripts/update-field-permissions.js +433 -0
- package/skills/dx-code-analyzer-configure/scripts/validate-config.sh +14 -10
- package/skills/dx-code-analyzer-run/scripts/apply-fixes.js +45 -4
- package/skills/dx-code-analyzer-run/scripts/describe-rule.js +52 -32
- package/skills/platform-apex-logs-debug/SKILL.md +7 -7
- package/skills/platform-custom-application-generate/SKILL.md +4 -4
- package/skills/platform-custom-object-generate/SKILL.md +7 -7
- package/skills/platform-custom-tab-generate/SKILL.md +1 -1
- package/skills/platform-flexipage-generate/SKILL.md +4 -0
- package/skills/platform-list-view-generate/SKILL.md +1 -0
- package/skills/platform-soql-query/SKILL.md +8 -8
- package/skills/platform-value-set-generate/SKILL.md +2 -2
- package/skills/service-catalog-template-coordinate/SKILL.md +263 -0
- package/skills/service-catalog-template-coordinate/examples/output-templates.md +44 -0
- package/skills/service-catalog-template-coordinate/references/mcp-invocation.md +183 -0
- package/skills/service-catalog-template-coordinate/references/operations.md +230 -0
- package/skills/service-itsm-agentic-setup-agent-runtime-access-assign/SKILL.md +243 -0
- package/skills/service-itsm-agentic-setup-agent-runtime-access-assign/references/cli-invocation.md +205 -0
- package/skills/service-itsm-agentic-setup-agent-runtime-access-assign/references/helper-contracts.md +236 -0
- package/skills/service-itsm-agentic-setup-agent-runtime-access-assign/references/permset-topology.md +132 -0
- package/skills/service-itsm-agentic-setup-agent-runtime-access-assign/scripts/classify-activated-agents.mjs +106 -0
- package/skills/service-itsm-agentic-setup-agent-runtime-access-assign/scripts/classify-agent-access-state.mjs +113 -0
- package/skills/service-itsm-agentic-setup-agent-runtime-access-assign/scripts/classify-assignment-state.mjs +99 -0
- package/skills/service-itsm-agentic-setup-agent-runtime-access-assign/scripts/classify-platform-permset-availability.mjs +155 -0
- package/skills/service-itsm-agentic-setup-agent-runtime-access-assign/scripts/gate-unified-catalog-tiers.mjs +100 -0
- package/skills/service-itsm-agentic-setup-agent-runtime-access-assign/scripts/rank-candidate-users.mjs +95 -0
- package/skills/service-itsm-agentic-setup-agent-runtime-access-assign/scripts/resolve-target-user.mjs +86 -0
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +44 -23
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +33 -9
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/SKILL.md +17 -18
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +3 -1
- package/skills/service-itsm-agentic-setup-configure/SKILL.md +20 -12
- package/skills/service-itsm-agentic-setup-configure/examples/output-templates.md +73 -5
- package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +8 -7
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/cli-invocation.md +45 -33
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/reactivation.md +8 -6
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/workflow-detail.md +10 -10
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/classify-agent-existence.mjs +114 -56
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/classify-preflight.mjs +33 -17
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +9 -3
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +8 -7
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/cli-invocation.md +43 -32
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/reactivation.md +6 -4
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md +10 -10
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-agent-existence.mjs +106 -55
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-preflight.mjs +27 -13
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +9 -3
- package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +159 -161
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone-action.json +51 -0
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +1 -1
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +120 -0
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +28 -5
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +19 -1
- package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +350 -30
- package/skills/service-itsm-channels-coordinate/SKILL.md +80 -213
- package/skills/service-itsm-slack-itservice-configure/SKILL.md +363 -0
- package/skills/service-itsm-slack-itservice-configure/references/connect-agentforce-to-slack.md +159 -0
- package/skills/service-itsm-slack-itservice-configure/references/manage-slack-connection.md +88 -0
- package/skills/service-itsm-slack-itservice-configure/references/manage-user-access.md +117 -0
- package/skills/service-itsm-slack-itservice-configure/references/record-visibility.md +78 -0
- package/skills/service-itsm-slack-itservice-configure/references/site-membership-verification.md +126 -0
- package/skills/service-itsm-slack-itservice-configure/scripts/classify-user-access.mjs +167 -0
- package/skills/service-itsm-teams-configure/SKILL.md +50 -47
- package/skills/service-itsm-teams-configure/references/azure-credential-population.md +42 -28
- package/skills/service-itsm-teams-configure/references/gotchas.md +1 -2
- package/skills/service-itsm-teams-coordinate/SKILL.md +22 -18
- package/skills/service-itsm-teams-coordinate/examples/output-templates.md +12 -9
- package/skills/service-itsm-teams-itdesk-configure/SKILL.md +60 -44
- package/skills/service-itsm-teams-itservice-configure/SKILL.md +56 -70
- package/skills/service-catalog-template-deploy/SKILL.md +0 -310
- package/skills/service-catalog-template-deploy/references/cli-invocation.md +0 -258
- package/skills/service-catalog-template-deploy/scripts/activate-verify.mjs +0 -164
- package/skills/service-catalog-template-deploy/scripts/build-deploy-payload.mjs +0 -94
- package/skills/service-catalog-template-deploy/scripts/resolve-template.mjs +0 -331
- package/skills/service-catalog-template-search/SKILL.md +0 -212
- package/skills/service-catalog-template-search/references/cli-invocation.md +0 -128
- package/skills/service-catalog-template-search/scripts/classify-catalog.mjs +0 -205
package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/classify-preflight.mjs
CHANGED
|
@@ -20,16 +20,21 @@
|
|
|
20
20
|
//
|
|
21
21
|
// Emits a single JSON object to stdout:
|
|
22
22
|
// { studio: { hasAccess, signal, reason },
|
|
23
|
-
// template: { present, id, hasAgentScript, botDefinitionId, signal, reason },
|
|
23
|
+
// template: { present, id, hasAgentScript, botDefinitionId, masterLabel, signal, reason },
|
|
24
24
|
// verdict, reasons: [...] }
|
|
25
25
|
// `botDefinitionId` is copied from the matched agent-templates row and passed to
|
|
26
26
|
// the Phase-2 idempotency read as the PRIMARY key (the platform's authoritative
|
|
27
|
-
// template→BotDefinition link); the
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
27
|
+
// template→BotDefinition link); `id` (the template's OOTB namespaced API name) is
|
|
28
|
+
// passed as the AgentTemplate fallback key — it matches `BotDefinition.AgentTemplate`
|
|
29
|
+
// on the pre-provisioned broad `IT_Service_Employee` agent regardless of its
|
|
30
|
+
// DeveloperName, so it is THE key that catches it — and the collected developerName
|
|
31
|
+
// is the last fallback (the guard for self-created agents, which carry a null
|
|
32
|
+
// AgentTemplate). `masterLabel` is echoed only for the report's display label; it
|
|
33
|
+
// is NOT an idempotency key (a display name is renamable and non-unique). See
|
|
34
|
+
// classify-agent-existence.mjs for why. (The template row's `isInstalled`/`isActivated`
|
|
35
|
+
// flags are NOT emitted: they ride the same AgentTemplate join as botDefinitionId,
|
|
36
|
+
// so for agents this skill creates — which never stamp `templateName` — they read
|
|
37
|
+
// false even after the agent exists, and nothing downstream consumes them.)
|
|
33
38
|
// where signal is PASS | FAIL | CANNOT-CONFIRM | ERROR and verdict is
|
|
34
39
|
// READY | NOT-READY | CANNOT-CONFIRM | ERROR. Exit is always 0 on usable args.
|
|
35
40
|
// ERROR (studio only) means the Studio-access read returned a parseable but
|
|
@@ -117,16 +122,19 @@ function classifyTemplate(res, masterLabel) {
|
|
|
117
122
|
}
|
|
118
123
|
// Idempotency identity carried by the matched row — the platform's own link
|
|
119
124
|
// from this template to the BotDefinition it was instantiated into. A populated
|
|
120
|
-
// botDefinitionId is
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
+
// botDefinitionId is a direct link for PRE-PROVISIONED agents; for the broad
|
|
126
|
+
// Employee agent it is null, so the template's own `id` (matched against
|
|
127
|
+
// `BotDefinition.AgentTemplate` in Phase 2) is what catches the pre-provisioned
|
|
128
|
+
// `IT_Service_Employee` regardless of what DeveloperName this skill would have
|
|
129
|
+
// guessed (see classify-agent-existence.mjs). Agents this skill creates never
|
|
130
|
+
// stamp `templateName`, so they carry BOTH a null row botDefinitionId AND a null
|
|
131
|
+
// AgentTemplate — the Phase-2 read falls back to the collected DeveloperName key
|
|
132
|
+
// for that case.
|
|
125
133
|
const botDefinitionId = typeof match.botDefinitionId === 'string' && match.botDefinitionId.trim() ? match.botDefinitionId : null;
|
|
126
134
|
if (!match.agentScript || typeof match.agentScript !== 'string' || !match.agentScript.trim()) {
|
|
127
135
|
return { present: true, id: match.id ?? null, hasAgentScript: false, botDefinitionId, signal: 'CANNOT-CONFIRM', reason: `Matched "${masterLabel}" (id=${match.id}) but it had no agentScript field — the NGA bundle create needs the template's Agent Script content.` };
|
|
128
136
|
}
|
|
129
|
-
return { present: true, id: match.id ?? null, hasAgentScript: true, botDefinitionId, signal: 'PASS', reason: `Template "${masterLabel}" present (id=${match.id}) with a non-empty agentScript${botDefinitionId ? `; already instantiated (botDefinitionId=${botDefinitionId})` : '; not yet instantiated (no botDefinitionId — the Phase-2 read
|
|
137
|
+
return { present: true, id: match.id ?? null, hasAgentScript: true, botDefinitionId, signal: 'PASS', reason: `Template "${masterLabel}" present (id=${match.id}) with a non-empty agentScript${botDefinitionId ? `; already instantiated (botDefinitionId=${botDefinitionId})` : '; not yet instantiated (no botDefinitionId — the Phase-2 read keys on AgentTemplate=id, then DeveloperName)'} — feed it to scripts/build-create-body.mjs for the NGA bundle create.` };
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
const [studioPath, templatePath, rawLabel] = process.argv.slice(2);
|
|
@@ -155,11 +163,19 @@ process.stdout.write(JSON.stringify({
|
|
|
155
163
|
id: template.id,
|
|
156
164
|
hasAgentScript: template.hasAgentScript,
|
|
157
165
|
// Idempotency identity (from the matched agent-templates row): the Phase-2
|
|
158
|
-
// read uses botDefinitionId as the PRIMARY key
|
|
159
|
-
// as the
|
|
160
|
-
//
|
|
161
|
-
// template row was never joined (e.g. a self-created
|
|
166
|
+
// read uses botDefinitionId as the PRIMARY key, `id` (the template's namespaced
|
|
167
|
+
// API name) as the AgentTemplate key, and the collected developerName as the
|
|
168
|
+
// last fallback. A populated botDefinitionId is a direct "agent already exists"
|
|
169
|
+
// link; null just means this template row was never joined (e.g. a self-created
|
|
170
|
+
// agent) — not "no agent". `id` is the load-bearing catcher for the
|
|
171
|
+
// pre-provisioned broad `IT_Service_Employee`, whose DeveloperName differs from
|
|
172
|
+
// this skill's collected guess: it matches `BotDefinition.AgentTemplate`, the
|
|
173
|
+
// platform-stamped source template. `masterLabel` is echoed only for the
|
|
174
|
+
// report's display label (NOT an idempotency key). The workflow passes
|
|
175
|
+
// botDefinitionId, `id`, and the collected developerName to
|
|
176
|
+
// classify-agent-existence.mjs from this single source.
|
|
162
177
|
botDefinitionId: template.botDefinitionId ?? null,
|
|
178
|
+
masterLabel,
|
|
163
179
|
signal: template.signal,
|
|
164
180
|
reason: template.reason,
|
|
165
181
|
},
|
package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs
CHANGED
|
@@ -148,13 +148,19 @@ if (
|
|
|
148
148
|
|
|
149
149
|
const reason = String(state.reason ?? '').trim();
|
|
150
150
|
|
|
151
|
+
// The "set up access" next step below is the handoff to the
|
|
152
|
+
// `service-itsm-agentic-setup-agent-runtime-access-assign` skill (declared in
|
|
153
|
+
// this skill's metadata.relatedSkills). We deliberately surface it in plain
|
|
154
|
+
// admin language ("grant this agent's runtime access") rather than by raw skill
|
|
155
|
+
// name — the routing metadata carries the linkage, so the user-facing report
|
|
156
|
+
// stays friendly while the relationship remains discoverable.
|
|
151
157
|
const NEXT_STEPS = {
|
|
152
158
|
'CREATED':
|
|
153
|
-
'The IT Service Employee agent is live as an NGA-native agent (no external-link icon in Agentforce Studio). No new agent was provisioned beyond the one just created. Verify end-to-end in Agentforce Studio\'s Agents list.',
|
|
159
|
+
'The IT Service Employee agent is live as an NGA-native agent (no external-link icon in Agentforce Studio). No new agent was provisioned beyond the one just created. Verify end-to-end in Agentforce Studio\'s Agents list. **Next — set up access:** before anyone can use the agent, the user needs the runtime permissions the agent\'s actions use (Prompt Templates, Data Cloud, or Unified Catalog, depending on what is provisioned). Just ask to **grant this agent\'s runtime access** and those feature permissions — plus an "Agent Access" permission set covering the agent — will be set up for the user(s) you choose.',
|
|
154
160
|
'ALREADY-CREATED':
|
|
155
|
-
'The IT Service Employee agent was already present and active — no new agent was provisioned. Verify end-to-end in Agentforce Studio\'s Agents list.',
|
|
161
|
+
'The IT Service Employee agent was already present and active — no new agent was provisioned. Verify end-to-end in Agentforce Studio\'s Agents list. **Next — set up access:** before anyone can use the agent, the user needs the runtime permissions the agent\'s actions use (Prompt Templates, Data Cloud, or Unified Catalog, depending on what is provisioned). Just ask to **grant this agent\'s runtime access** and those feature permissions — plus an "Agent Access" permission set covering the agent — will be set up for the user(s) you choose.',
|
|
156
162
|
'ACTIVATED':
|
|
157
|
-
'The existing IT Service Employee agent was found inactive and has been activated — no new agent was provisioned. Verify end-to-end in Agentforce Studio\'s Agents list.',
|
|
163
|
+
'The existing IT Service Employee agent was found inactive and has been activated — no new agent was provisioned. Verify end-to-end in Agentforce Studio\'s Agents list. **Next — set up access:** before anyone can use the agent, the user needs the runtime permissions the agent\'s actions use (Prompt Templates, Data Cloud, or Unified Catalog, depending on what is provisioned). Just ask to **grant this agent\'s runtime access** and those feature permissions — plus an "Agent Access" permission set covering the agent — will be set up for the user(s) you choose.',
|
|
158
164
|
'PENDING CONFIRMATION':
|
|
159
165
|
'The run is paused at the confirm-to-write gate. In this skill, create + publish + activate happen as one atomic sequence gated by a single confirmation — there is no supported create-only or publish-without-activate mode, and no partial "create-only" mode of any kind. Re-run and reply "yes" when you are ready for the agent to go live.',
|
|
160
166
|
'DECLINED':
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
name: service-itsm-agentic-setup-fulfiller-agent-configure
|
|
3
3
|
description: "Create and activate the IT Service Fulfiller agent as a Next-Gen Authoring (NGA) native agent from the shipped ITSM Fulfiller template's Agent Script, using the Salesforce CLI (sf): read the template, check idempotency, create the NGA bundle then publish and activate it, then verify it is live. Idempotent per developer name. The Fulfiller agent is the IT-technician-facing assistant for incident triage, case summarization, field updates, and related-record automations. Use when asked to create the Fulfiller agent, set up the IT Service Fulfiller agent, provision the fulfiller assistant, or activate the Fulfiller agent. Triggers: create fulfiller agent, set up fulfiller agent, provision fulfiller agent, activate fulfiller agent. DO NOT TRIGGER: checking org prerequisites (service-itsm-agentic-setup-agentforce-studio-validate), or CMDB CRUD."
|
|
4
4
|
metadata:
|
|
5
|
-
version: "3.
|
|
5
|
+
version: "3.7"
|
|
6
6
|
domains: ["Service", "Agentforce"]
|
|
7
7
|
minApiVersion: "67.0"
|
|
8
8
|
relatedSkills:
|
|
9
|
+
- "service-itsm-agentic-setup-agent-runtime-access-assign"
|
|
9
10
|
- "service-itsm-agentic-setup-agentforce-studio-configure"
|
|
10
11
|
- "service-itsm-agentic-setup-agentforce-studio-validate"
|
|
11
12
|
- "service-itsm-agentic-setup-employee-agent-configure"
|
|
@@ -63,7 +64,7 @@ If any of these are unmet, `sf` surfaces an auth error or a `401`/`403`/`404`; *
|
|
|
63
64
|
|---------|---------|-------|
|
|
64
65
|
| Studio access (precondition read) | `sf api request rest "/services/data/v67.0/agentforce-studio/access/Agents" --method GET -o <alias>` | `hasAccess=false` ⇒ prerequisite hand-off |
|
|
65
66
|
| List agent templates + Agent Script (read) | `sf api request rest "/services/data/v67.0/connect/service-itsm/agent-templates?agentType=AgentforceEmployeeAgent" --method GET -o <alias>` | `agentType=AgentforceEmployeeAgent` required; confirms Fulfiller template + non-empty `agentScript` |
|
|
66
|
-
| Enumerate the existing agent + latest version status (read) | `sf data query -q "SELECT Id,DeveloperName,MasterLabel,(SELECT Id,Status FROM BotVersions ORDER BY VersionNumber DESC LIMIT 1) FROM BotDefinition WHERE Id='<botDefinitionId>' OR DeveloperName='<developerName>'" -o <alias> --json` | Keyed PRIMARILY on the template's `botDefinitionId` (Phase-1 row); the `OR DeveloperName=`
|
|
67
|
+
| Enumerate the existing agent + latest version status (read) | `sf data query -q "SELECT Id,DeveloperName,MasterLabel,AgentTemplate,(SELECT Id,Status FROM BotVersions ORDER BY VersionNumber DESC LIMIT 1) FROM BotDefinition WHERE Id='<botDefinitionId>' OR AgentTemplate='<agentTemplate>' OR DeveloperName='<developerName>'" -o <alias> --json` | Keyed PRIMARILY on the template's `botDefinitionId` (Phase-1 row); `OR AgentTemplate=` is a defensive fallback that would catch a live agent instantiated from the OOTB source template (`svc_itsm_intelligence__ITSrvcMgmtFulfiller` = Phase-1 `template.id`) regardless of its DeveloperName; `OR DeveloperName=` is the real guard here — the normal Fulfiller case (never pre-provisioned; null `AgentTemplate`) and the guard for a dangling Id link (deleted target). Classified by `scripts/classify-agent-existence.mjs`; Active latest ⇒ ALREADY-CREATED; Inactive latest ⇒ offer reactivation |
|
|
67
68
|
| **Create the NGA bundle** (write) | `sf api request rest "/services/data/v67.0/nextgen-authoring/bundles" --method POST --body @<body-file> -o <alias>` | Body built by `scripts/build-create-body.mjs`; response `id` = the bundle **version** Id |
|
|
68
69
|
| **Publish the bundle version** (write) | `sf api request rest "/services/data/v67.0/nextgen-authoring/bundle-versions/<bundleVersionId>/publish" --method POST --body '{}' -o <alias>` | Returns `publishedBotId`/`publishedBotVersionId` — creates the underlying `BotDefinition`/`BotVersion` |
|
|
69
70
|
| **Activate the bundle version** (write) | `sf api request rest "/services/data/v67.0/nextgen-authoring/bundle-versions/<bundleVersionId>/activate" --method POST --body '{}' -o <alias>` | Empty response on success; agent is now live and NGA-native |
|
|
@@ -102,7 +103,7 @@ Full command shapes and the ITSM Connect API reference live in `references/cli-i
|
|
|
102
103
|
| Activate | POST `.../bundle-versions/<id>/activate` (create path) OR POST `.../connect/bot-versions/<latestVersionId>/activation` with `{"status":"Active"}` (reactivation path — skips create/publish) | `Bash` (`sf api request rest`) |
|
|
103
104
|
| Verify | SOQL-read `BotDefinition`/`BotVersions` and confirm the agent exists with an Active latest version | `Bash` (`sf data query`) |
|
|
104
105
|
|
|
105
|
-
**Idempotency**: keyed PRIMARILY on the **template's `botDefinitionId`** (Phase-1 `agent-templates` row — the platform's authoritative template→`BotDefinition` link)
|
|
106
|
+
**Idempotency**: keyed PRIMARILY on the **template's `botDefinitionId`** (Phase-1 `agent-templates` row — the platform's authoritative template→`BotDefinition` link), FALLING BACK first to the **BotDefinition's `AgentTemplate`** (the OOTB namespaced source template = Phase-1 `template.id`) and then to the collected `<developerName>`. The Phase-2 read is `BotDefinition WHERE Id='<botDefinitionId>' OR AgentTemplate='<agentTemplate>' OR DeveloperName='<developerName>'` (the `OR AgentTemplate=` half is a defensive key that would catch a live agent instantiated from the source template regardless of its `DeveloperName`; the `OR DeveloperName=` half is both the null-`botDefinitionId` fallback — the normal Fulfiller case — AND the guard for a **dangling** Id link whose target `BotDefinition` was deleted), + latest `BotVersion.Status` (classified by the helper script). Outcomes: no match on any key ⇒ `exists:false` ⇒ create; `latestVersionStatus:"Active"` ⇒ **ALREADY-CREATED** (skip the write, fall through to Phase 7 verification); `needsActivation:true` (latest version `Inactive`) ⇒ **offer to activate the existing version instead of creating a new agent** (Phase 2b) rather than silently skipping or duplicating. **Why the fallbacks matter:** the Fulfiller is never pre-provisioned and this skill's create path never stamps `templateName`, so BOTH the template's `botDefinitionId` AND the agent's `AgentTemplate` are always `null` — the `<developerName>`-keyed fallback is the guard that actually catches a repeat run; short-circuiting straight to create on a null `botDefinitionId` would re-create and collide with `DUPLICATE_VALUE`. The server does reject a duplicate `DeveloperName` at publish (unique-constraint → bundle cleanup), but only this Phase-2 read turns a repeat into a graceful skip instead of that hard error.
|
|
106
107
|
|
|
107
108
|
---
|
|
108
109
|
|
|
@@ -117,7 +118,7 @@ Collect from the user (ask only what is not already in conversation context):
|
|
|
117
118
|
| Label | User-facing label for the agent | `IT Service Fulfiller Agent` |
|
|
118
119
|
| Confirm the write | Explicit confirmation before the create/publish/activate sequence | **REQUIRED** — present the developerName + label and require "yes" via `AskUserQuestion` |
|
|
119
120
|
|
|
120
|
-
The **idempotency read keys PRIMARILY on the template's `botDefinitionId`** (Phase-1 row) and FALLS BACK to the collected `<developerName
|
|
121
|
+
The **idempotency read keys PRIMARILY on the template's `botDefinitionId`** (Phase-1 row) and FALLS BACK to the BotDefinition's `AgentTemplate` (defensive — null on the never-pre-provisioned Fulfiller) and then to the collected `<developerName>`, the guard that actually catches a repeat here, when that is null; the verify read keys on the publish response's `publishedBotId` (create path) or the template's `botDefinitionId` (ALREADY-CREATED / reactivation). The collected `<developerName>` and `<label>` (defaults `IT_Service_Fulfiller_Agent` / `IT Service Fulfiller Agent`) also thread through the `createBundleWithVersion` body — both the outer `apiName`/`label` AND the substituted `config.developer_name`/`config.agent_label` inside the Agent Script. Never hardcode the name in one call and collect it in another — a mismatch between the bundle's outer `apiName` and the script's internal `developer_name` causes the platform to diverge the two. Creating an agent provisions a live, activated agent on the org; the user must explicitly approve the write.
|
|
121
122
|
|
|
122
123
|
---
|
|
123
124
|
|
|
@@ -126,8 +127,8 @@ The **idempotency read keys PRIMARILY on the template's `botDefinitionId`** (Pha
|
|
|
126
127
|
Substitute `<alias>` with the collected target org and `<developerName>` / `<label>` with the collected values. Full command shapes + per-phase verdict-branch handling live in `references/workflow-detail.md` — the phase summary below names each step and its load-bearing rule; the reference file holds the exact `sf` / `node` invocations to copy.
|
|
127
128
|
|
|
128
129
|
0. **Phase 0 — Establish `${SCRATCH_DIR}`.** Invoke the deterministic helper (path is skill-root-qualified so it resolves regardless of the shell's CWD): `SCRATCH_DIR="$(node "<skill_dir>/scripts/create-scratch-dir.mjs" "${outputDir:-}")"`. Helper picks the base dir (`${TMPDIR}`, else `/tmp`, else the harness `${outputDir}` last-resort — scratch stays OUT of the scored `${outputDir}` tree) and emits the created dir on stdout. All transient JSON lands under `${SCRATCH_DIR}`; the durable `${outputDir}/report.md` stays under the harness dir.
|
|
129
|
-
1. **Phase 1 — Preflight.** Capture the Studio-access read
|
|
130
|
-
2. **Phase 2 — Idempotency (primary key `botDefinitionId`,
|
|
130
|
+
1. **Phase 1 — Preflight.** Capture the Studio-access read into `${SCRATCH_DIR}/studio-access.json` and the `agent-templates` read (with the **required** `agentType=AgentforceEmployeeAgent` query param) into `${SCRATCH_DIR}/agent-templates.json`, then classify by passing **both file paths, then the label** (that arg order): `node "<skill_dir>/scripts/classify-preflight.mjs" ${SCRATCH_DIR}/studio-access.json ${SCRATCH_DIR}/agent-templates.json "IT Service Fulfiller"`. The classifier emits `template.botDefinitionId`, `template.id`, and `template.masterLabel` from the matched row — **capture all three; `botDefinitionId` is the primary Phase-2 idempotency key, `template.id` (the BotDefinition's `AgentTemplate`) the first fallback, and `<developerName>` the last fallback; `masterLabel` is the report's display label, not a key.** Branch on `verdict`: `READY` ⇒ Phase 2; `NOT-READY` ⇒ prerequisite hand-off via `AskUserQuestion` (delegate to `service-itsm-agentic-setup-agentforce-studio-validate` on "yes"); `ERROR` ⇒ surface + stop; `studio.signal="CANNOT-CONFIRM"` (confirmed 404) does not block.
|
|
131
|
+
2. **Phase 2 — Idempotency (primary key `botDefinitionId`, fallbacks `AgentTemplate` then `<developerName>`).** Take `template.botDefinitionId` and `template.id` from Phase 1. **Present `botDefinitionId`** ⇒ SOQL `BotDefinition WHERE Id='<botDefinitionId>' OR AgentTemplate='<agentTemplate>' OR DeveloperName='<developerName>'` with the `BotVersions` subquery (subquery is required — otherwise `needsActivation` is permanently false; the `OR` clauses make a **dangling** Id link — deleted target — fall back to the live agent instead of a false `exists:false` → duplicate create). **Empty/null `botDefinitionId`** (the normal Fulfiller case — the template row is never back-filled) ⇒ do NOT skip to create; read `BotDefinition WHERE AgentTemplate='<agentTemplate>' OR DeveloperName='<developerName>'` (a self-created agent from a prior run carries a null `AgentTemplate`, so `DeveloperName` is what recovers it; `AgentTemplate` would catch a hypothetical instantiated-from-template agent under any DeveloperName). `<agentTemplate>` is Phase-1 `template.id`. Either way classify via `node "<skill_dir>/scripts/classify-agent-existence.mjs" ${SCRATCH_DIR}/bot-existing.json "<botDefinitionId-or-empty>" "<developerName>" "<agentTemplate>"`. Branch: `exists:false` ⇒ **Phase 2c** (action-availability gate, then create); `exists:true` + `needsActivation:false` ⇒ **ALREADY-CREATED** (skip straight to Phase 7 — no action-availability gate; a live active agent's actions are already wired); `exists:true` + `needsActivation:true` ⇒ Phase 2b. Non-zero exit ⇒ surface CLI error; never assume absent. **Why the fallbacks:** the Fulfiller is never pre-provisioned, so `botDefinitionId` and `AgentTemplate` are always null — a missing developerName check would re-create and hit `DUPLICATE_VALUE`.
|
|
131
132
|
3. **Phase 2b — Reactivation offer.** `AskUserQuestion`: _"Fulfiller agent `<developerName>` exists but latest version is Inactive. Activate it?"_. On **Yes**: `POST /connect/bot-versions/<latestVersionId>/activation` with `{"status":"Active"}` captured to `${SCRATCH_DIR}/activate-response.json`, then `node "<skill_dir>/scripts/classify-activate-result.mjs" ${SCRATCH_DIR}/activate-response.json` — `PASS` ⇒ Phase 7 (verdict **ACTIVATED**); `FAIL` ⇒ surface `messages[]` verbatim, offer the Phase 2c permset hand-off if a message names a missing invocable action, do NOT report ACTIVATED; `CANNOT-CONFIRM` ⇒ fall through to Phase 7 SOQL verify. On **No**: stop, no writes.
|
|
132
133
|
4. **Phase 2c — Action-availability preflight (create path only; reached only from Phase 2 `exists:false`).** Capture `sf api request rest "/services/data/v67.0/actions/custom/generatePromptResponse" --method GET` to `${SCRATCH_DIR}/generate-prompt-response.json`, then `node "<skill_dir>/scripts/classify-action-availability.mjs" ${SCRATCH_DIR}/agent-templates.json "IT Service Fulfiller" ${SCRATCH_DIR}/generate-prompt-response.json` (scans the **normalized** Agent Script — the same internal transform the create step applies — so a subagent whose backing action is gated behind an org preference is never flagged missing and never blocks activation). Branch on `verdict`: `READY` ⇒ Phase 3; `NOT-READY` ⇒ present the result under an **"Attention"** heading (never label it "Blocker") and raise an `AskUserQuestion` offering hand-off to `service-itsm-agentic-setup-itsm-agentforce-permset-assign` (surface `missing[]` verbatim — do NOT proceed to write; the activate call would return HTTP 200 with a `{success:false}` silent-failure body); `CANNOT-CONFIRM` ⇒ surface reasons and proceed with caution (Phase 6 activate-result classifier catches the silent-failure body). Full contract in `references/action-availability.md`.
|
|
133
134
|
5. **Phase 3 — Confirm-to-Write (REQUIRED, create path only).** If `${outputDir}` was provided, first render the checkpoint file via `render-report.mjs` with `verdict:"PENDING CONFIRMATION"` (skip for interactive runs). THEN raise the `AskUserQuestion` gate presenting developerName + label + "NGA-native from the Fulfiller template's Agent Script". Proceed **only** on explicit "yes"; on "no", re-render with `verdict:"DECLINED"`.
|
|
@@ -158,7 +159,7 @@ Substitute `<alias>` with the collected target org and `<developerName>` / `<lab
|
|
|
158
159
|
## Verification Checklist
|
|
159
160
|
|
|
160
161
|
- [ ] Preflight classified by `classify-preflight.mjs` (PASS or documented CANNOT-CONFIRM); hand-off offered on FAIL; raw error surfaced on ERROR.
|
|
161
|
-
- [ ] Idempotency keyed on the template's `botDefinitionId` (Phase-1 row) with the collected developerName as
|
|
162
|
+
- [ ] Idempotency keyed on the template's `botDefinitionId` (Phase-1 row) with the BotDefinition's `AgentTemplate` (Phase-1 `template.id`) as a defensive first fallback and the collected developerName as the real guard; `BotDefinition WHERE Id='<botDefinitionId>' OR AgentTemplate='<agentTemplate>' OR DeveloperName='<developerName>'` (the `OR`s cover a null/dangling `botDefinitionId` and — for the never-pre-provisioned Fulfiller with a null `AgentTemplate` — a self-created repeat by DeveloperName) + latest `BotVersion.Status` (subquery present) read + classified before any write.
|
|
162
163
|
- [ ] If `needsActivation:true`, Phase-2b reactivation offer presented — no silent skip, no duplicate create.
|
|
163
164
|
- [ ] Explicit user confirmation at Phase 3 (create) or Phase 2b (reactivation) before any write.
|
|
164
165
|
- [ ] Bundle body built by `build-create-body.mjs`, POSTed via `--body @<file>` with the collected `developerName`/`label`; or write correctly skipped.
|
package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/cli-invocation.md
CHANGED
|
@@ -135,13 +135,13 @@ Classifier output:
|
|
|
135
135
|
```json
|
|
136
136
|
{
|
|
137
137
|
"studio": { "hasAccess": true, "signal": "PASS|FAIL|CANNOT-CONFIRM|ERROR", "reason": "..." },
|
|
138
|
-
"template": { "present": true, "id": "svc_itsm_intelligence__ITSrvcMgmtFulfiller", "hasAgentScript": true, "botDefinitionId": "0Xx...", "signal": "PASS|FAIL|CANNOT-CONFIRM", "reason": "..." },
|
|
138
|
+
"template": { "present": true, "id": "svc_itsm_intelligence__ITSrvcMgmtFulfiller", "hasAgentScript": true, "botDefinitionId": "0Xx...", "masterLabel": "IT Service Fulfiller", "signal": "PASS|FAIL|CANNOT-CONFIRM", "reason": "..." },
|
|
139
139
|
"verdict": "READY | NOT-READY | CANNOT-CONFIRM | ERROR",
|
|
140
140
|
"reasons": ["..."]
|
|
141
141
|
}
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
-
`template.botDefinitionId`
|
|
144
|
+
`template.botDefinitionId`, `template.id`, and `template.masterLabel` are copied from the matched `agent-templates` row. **`botDefinitionId` is the PRIMARY Phase-2 idempotency key**, but the Fulfiller is never pre-provisioned and this skill's create path never stamps `templateName`, so BOTH its template `botDefinitionId` AND the agent's `AgentTemplate` are `null` on every run — the collected `<developerName>` (the last fallback) is therefore the guard that actually protects repeat runs; `template.id` (the BotDefinition's `AgentTemplate`) is carried as a defensive first fallback. Carry `template.id` and `<developerName>` into the Phase-2 read as the fallback keys; `masterLabel` is the report's display label, not an idempotency key. (`isInstalled`/`isActivated` are no longer emitted: they ride the same AgentTemplate join as `botDefinitionId`, so they read false for self-created agents and nothing consumes them.)
|
|
145
145
|
|
|
146
146
|
`verdict=ERROR` (`studio.signal="ERROR"` — a parseable non-404 Studio-access
|
|
147
147
|
error, e.g. `401`/`403`) ⇒ surface the raw error and stop; takes priority over
|
|
@@ -149,55 +149,66 @@ template state so a present template cannot outrun a failed prerequisite read.
|
|
|
149
149
|
`verdict=NOT-READY` (studio FAIL or template FAIL) ⇒ hand off / stop.
|
|
150
150
|
`verdict=READY` ⇒ proceed to Phase 2. It exits `0` on usable args.
|
|
151
151
|
|
|
152
|
-
## Enumerate the existing agent — SOQL on `BotDefinition` BY Id (falling back to DeveloperName)
|
|
152
|
+
## Enumerate the existing agent — SOQL on `BotDefinition` BY Id (falling back to AgentTemplate, then DeveloperName)
|
|
153
153
|
|
|
154
154
|
Idempotency is keyed **PRIMARILY** on the template's `botDefinitionId` (from the
|
|
155
|
-
Phase-1 row)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
`
|
|
160
|
-
the
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
155
|
+
Phase-1 row), **FALLS BACK** first to the BotDefinition's `AgentTemplate` (the
|
|
156
|
+
OOTB namespaced source template = Phase-1 `template.id`), and then to the
|
|
157
|
+
collected `<developerName>`. If the Fulfiller agent already exists under a
|
|
158
|
+
`DeveloperName` that differs from this skill's default guess
|
|
159
|
+
`IT_Service_Fulfiller_Agent`, a name-only read false-negatives (`exists:false`)
|
|
160
|
+
and the create then collides on `apiName` with `DUPLICATE_VALUE` — the
|
|
161
|
+
`DeveloperName`-keyed fallback recovers that live agent. `botDefinitionId` is the
|
|
162
|
+
platform's authoritative link from the template to the `BotDefinition` it was
|
|
163
|
+
instantiated into — but it is back-filled onto the template row **only** for
|
|
164
|
+
pre-provisioned agents, and the Fulfiller is never pre-provisioned: this skill's
|
|
165
|
+
create path never stamps `templateName`, so BOTH its template `botDefinitionId`
|
|
166
|
+
AND the agent's `AgentTemplate` stay `null` on the first run and every run after.
|
|
167
|
+
The `DeveloperName`-keyed fallback is therefore the guard that actually protects
|
|
168
|
+
repeat runs here; the `AgentTemplate` key — which would catch a live agent
|
|
169
|
+
instantiated from the source template regardless of its `DeveloperName`, far more
|
|
170
|
+
reliably than a display name — is carried as a defensive/symmetric key that keeps
|
|
171
|
+
this classifier identical to the Employee one (where `AgentTemplate` IS the
|
|
172
|
+
load-bearing catcher). Never short-circuit straight to create when
|
|
173
|
+
`botDefinitionId` is absent.
|
|
167
174
|
|
|
168
175
|
- **`botDefinitionId` empty/null** (the normal Fulfiller case — template row
|
|
169
|
-
never joined) ⇒ fall back to
|
|
170
|
-
match on
|
|
176
|
+
never joined) ⇒ fall back to an `AgentTemplate` **`OR` `DeveloperName`** read and
|
|
177
|
+
let the classifier match on either:
|
|
171
178
|
```bash
|
|
172
|
-
sf data query -q "SELECT Id,DeveloperName,MasterLabel,(SELECT Id,Status FROM BotVersions ORDER BY VersionNumber DESC LIMIT 1) FROM BotDefinition WHERE DeveloperName='<developerName>'" \
|
|
179
|
+
sf data query -q "SELECT Id,DeveloperName,MasterLabel,AgentTemplate,(SELECT Id,Status FROM BotVersions ORDER BY VersionNumber DESC LIMIT 1) FROM BotDefinition WHERE AgentTemplate='<agentTemplate>' OR DeveloperName='<developerName>'" \
|
|
173
180
|
--target-org <alias> --json > ${SCRATCH_DIR}/bot-existing.json 2>${SCRATCH_DIR}/bot-existing.err || true
|
|
174
|
-
node "<skill_dir>/scripts/classify-agent-existence.mjs" ${SCRATCH_DIR}/bot-existing.json "" "<developerName>"
|
|
181
|
+
node "<skill_dir>/scripts/classify-agent-existence.mjs" ${SCRATCH_DIR}/bot-existing.json "" "<developerName>" "<agentTemplate>"
|
|
175
182
|
```
|
|
176
183
|
- **`botDefinitionId` present** (a pre-provisioned instantiation, if any — never
|
|
177
184
|
the normal Fulfiller case) ⇒ read the `BotDefinition` by Id **`OR` by the
|
|
178
|
-
collected `<developerName>`** in one
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
185
|
+
template's `AgentTemplate` `OR` by the collected `<developerName>`** in one
|
|
186
|
+
query, then classify. The `OR AgentTemplate=` clause would catch a live agent by
|
|
187
|
+
its platform-stamped source template (`matchedBy:"agentTemplate"`) regardless of
|
|
188
|
+
its DeveloperName. The `OR DeveloperName=` clause catches a **dangling**
|
|
189
|
+
link — a `botDefinitionId` whose target row was since deleted: the by-Id half
|
|
190
|
+
returns nothing, the live same-name agent still surfaces, and the classifier
|
|
191
|
+
falls back to it (`matchedBy:"developerName"`) rather than concluding
|
|
192
|
+
`exists:false` and colliding with `DUPLICATE_VALUE`:
|
|
183
193
|
```bash
|
|
184
|
-
sf data query -q "SELECT Id,DeveloperName,MasterLabel,(SELECT Id,Status FROM BotVersions ORDER BY VersionNumber DESC LIMIT 1) FROM BotDefinition WHERE Id='<botDefinitionId>' OR DeveloperName='<developerName>'" \
|
|
194
|
+
sf data query -q "SELECT Id,DeveloperName,MasterLabel,AgentTemplate,(SELECT Id,Status FROM BotVersions ORDER BY VersionNumber DESC LIMIT 1) FROM BotDefinition WHERE Id='<botDefinitionId>' OR AgentTemplate='<agentTemplate>' OR DeveloperName='<developerName>'" \
|
|
185
195
|
--target-org <alias> --json > ${SCRATCH_DIR}/bot-existing.json 2>${SCRATCH_DIR}/bot-existing.err || true
|
|
186
|
-
node "<skill_dir>/scripts/classify-agent-existence.mjs" ${SCRATCH_DIR}/bot-existing.json "<botDefinitionId>" "<developerName>"
|
|
196
|
+
node "<skill_dir>/scripts/classify-agent-existence.mjs" ${SCRATCH_DIR}/bot-existing.json "<botDefinitionId>" "<developerName>" "<agentTemplate>"
|
|
187
197
|
```
|
|
188
198
|
|
|
189
|
-
Only when **
|
|
190
|
-
classifier return `exists:false` without reading a query
|
|
191
|
-
|
|
199
|
+
Only when **all** of `botDefinitionId`, `<agentTemplate>`, and `<developerName>`
|
|
200
|
+
are absent does the classifier return `exists:false` without reading a query
|
|
201
|
+
file — in practice the collected `<developerName>` and `template.id` (AgentTemplate)
|
|
202
|
+
are always present, so the fallback read always runs.
|
|
192
203
|
|
|
193
204
|
The `BotVersions` subquery (child relationship on `BotDefinition`) is what lets
|
|
194
205
|
the classifier see the latest version's `Status` — omit it and
|
|
195
206
|
`latestVersionStatus`/`needsActivation` come back `null`/`false` even when the
|
|
196
207
|
existing agent is actually inactive. The classifier prints
|
|
197
208
|
`{ exists, count, matchedBy, agentId, botDefinitionId, developerName, latestVersionId, latestVersionStatus, needsActivation }`
|
|
198
|
-
(`matchedBy` is `"botDefinitionId"` | `"developerName"` | `null`;
|
|
199
|
-
is the ACTUAL live agent's DeveloperName read from the record —
|
|
200
|
-
report instead of the collected guess):
|
|
209
|
+
(`matchedBy` is `"botDefinitionId"` | `"agentTemplate"` | `"developerName"` | `null`;
|
|
210
|
+
`developerName` is the ACTUAL live agent's DeveloperName read from the record —
|
|
211
|
+
surface it in the report instead of the collected guess):
|
|
201
212
|
- `exists:false` ⇒ proceed to create.
|
|
202
213
|
- `exists:true` and `needsActivation:false` (latest version `Active`) ⇒
|
|
203
214
|
**ALREADY-CREATED** (skip the entire create/publish/activate sequence).
|
package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/reactivation.md
CHANGED
|
@@ -9,12 +9,14 @@ just reactivate what's already there".
|
|
|
9
9
|
## When this path fires
|
|
10
10
|
|
|
11
11
|
`scripts/classify-agent-existence.mjs` (called with the template's
|
|
12
|
-
`botDefinitionId` as the PRIMARY idempotency key
|
|
13
|
-
`
|
|
14
|
-
|
|
12
|
+
`botDefinitionId` as the PRIMARY idempotency key, the BotDefinition's
|
|
13
|
+
`AgentTemplate` — the template's OOTB source id — as the first FALLBACK, and the
|
|
14
|
+
collected `DeveloperName` as the last FALLBACK —
|
|
15
|
+
see `cli-invocation.md` → "Enumerate the existing agent") returns
|
|
15
16
|
`{ exists:true, needsActivation:true, latestVersionId, latestVersionStatus:"Inactive" }`.
|
|
16
17
|
That means a `BotDefinition` matching the collected `DeveloperName` (the
|
|
17
|
-
Fulfiller's template `botDefinitionId`
|
|
18
|
+
Fulfiller's template `botDefinitionId` AND `AgentTemplate` are both null, so the
|
|
19
|
+
`DeveloperName` fallback is what matches)
|
|
18
20
|
already exists from a prior run of this skill but its most-recent `BotVersion` is
|
|
19
21
|
inactive; creating a new bundle would produce a duplicate. Instead, flip the
|
|
20
22
|
existing version to `Active`.
|
package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md
CHANGED
|
@@ -26,10 +26,10 @@ sf api request rest "/services/data/v67.0/connect/service-itsm/agent-templates?a
|
|
|
26
26
|
node "<skill_dir>/scripts/classify-preflight.mjs" ${SCRATCH_DIR}/studio-access.json ${SCRATCH_DIR}/agent-templates.json "IT Service Fulfiller"
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
The classifier prints `{ studio:{hasAccess,signal}, template:{present,id,hasAgentScript,botDefinitionId,signal}, verdict, reasons }`:
|
|
29
|
+
The classifier prints `{ studio:{hasAccess,signal}, template:{present,id,hasAgentScript,botDefinitionId,masterLabel,signal}, verdict, reasons }`:
|
|
30
30
|
|
|
31
31
|
- `template.hasAgentScript:true` confirms the template carries the Agent Script content Phase 4 needs; capture the raw `agent-templates.json` file path — Phase 4 re-reads it directly (via `scripts/build-create-body.mjs`) rather than the classifier re-emitting the full script content.
|
|
32
|
-
- **`template.botDefinitionId` is the PRIMARY Phase-2 idempotency key**, but the Fulfiller is never pre-provisioned and this skill's create path never stamps `templateName`, so its template `botDefinitionId` is `null` on every run — the collected `<developerName>`
|
|
32
|
+
- **`template.botDefinitionId` is the PRIMARY Phase-2 idempotency key**, but the Fulfiller is never pre-provisioned and this skill's create path never stamps `templateName`, so its template `botDefinitionId` is `null` on every run — the collected `<developerName>` and the template's `masterLabel` are the guards that actually fire. Capture `botDefinitionId` for Phase 2 and carry the collected `<developerName>` (first fallback) and `template.masterLabel` (second fallback) as the fallback keys.
|
|
33
33
|
- `verdict:"READY"` ⇒ continue to Phase 2.
|
|
34
34
|
- `verdict:"ERROR"` because `studio.signal="ERROR"` (the Studio-access read returned a parseable but non-404 error body — e.g. `401`/`403`) ⇒ do NOT proceed — surface the raw error and stop. This is a failed prerequisite read, not an unwired gate; do not let a present template push the verdict to READY.
|
|
35
35
|
- `verdict:"NOT-READY"` because `studio.signal="FAIL"` (`hasAccess=false`) ⇒ do NOT proceed — **offer the prerequisite hand-off**:
|
|
@@ -42,22 +42,22 @@ The classifier prints `{ studio:{hasAccess,signal}, template:{present,id,hasAgen
|
|
|
42
42
|
|
|
43
43
|
## Phase 2 — Idempotency: does the target agent already exist, and is it active?
|
|
44
44
|
|
|
45
|
-
Idempotency is keyed **PRIMARILY** on the template's `botDefinitionId` (captured in Phase 1)
|
|
45
|
+
Idempotency is keyed **PRIMARILY** on the template's `botDefinitionId` (captured in Phase 1), **FALLS BACK** first to the BotDefinition's `AgentTemplate` (the OOTB namespaced source template = Phase-1 `template.id`), and then to the collected `<developerName>`. The `botDefinitionId` is the platform's authoritative link from the `agent-templates` row to the `BotDefinition` it was instantiated into, but it is back-filled **only** for pre-provisioned agents — and the Fulfiller is never pre-provisioned: this skill's create path never stamps `templateName`, so BOTH its template `botDefinitionId` AND the agent's `AgentTemplate` are `null` on the first run and every run after. The `DeveloperName`-keyed fallback is therefore the guard that actually catches repeat runs (if the agent already exists under a `DeveloperName` matching the collected `<developerName>`, a name-only miss would let the create fail with `DUPLICATE_VALUE`); the `AgentTemplate` key — which would recover a live agent instantiated from the source template regardless of its `DeveloperName`, far more reliably than a display name — is carried as a defensive/symmetric key that keeps this classifier identical to the Employee one (where `AgentTemplate` IS the load-bearing catcher). Never short-circuit to create when `botDefinitionId` is absent.
|
|
46
46
|
|
|
47
|
-
- **`template.botDefinitionId` is empty/null** (the normal Fulfiller case) ⇒ fall back to
|
|
47
|
+
- **`template.botDefinitionId` is empty/null** (the normal Fulfiller case) ⇒ fall back to an `AgentTemplate` **`OR` `DeveloperName`** read (do **not** short-circuit to create — a self-created agent from a prior run carries a null `AgentTemplate`, so `DeveloperName` is what recovers it). The `BotVersions` subquery is required — omitting it leaves `needsActivation` permanently false and hides the reactivation path:
|
|
48
48
|
```bash
|
|
49
|
-
sf data query -q "SELECT Id,DeveloperName,MasterLabel,(SELECT Id,Status FROM BotVersions ORDER BY VersionNumber DESC LIMIT 1) FROM BotDefinition WHERE DeveloperName='<developerName>'" \
|
|
49
|
+
sf data query -q "SELECT Id,DeveloperName,MasterLabel,AgentTemplate,(SELECT Id,Status FROM BotVersions ORDER BY VersionNumber DESC LIMIT 1) FROM BotDefinition WHERE AgentTemplate='<agentTemplate>' OR DeveloperName='<developerName>'" \
|
|
50
50
|
--target-org <alias> --json > ${SCRATCH_DIR}/bot-existing.json 2>${SCRATCH_DIR}/bot-existing.err || true
|
|
51
|
-
node "<skill_dir>/scripts/classify-agent-existence.mjs" ${SCRATCH_DIR}/bot-existing.json "" "<developerName>"
|
|
51
|
+
node "<skill_dir>/scripts/classify-agent-existence.mjs" ${SCRATCH_DIR}/bot-existing.json "" "<developerName>" "<agentTemplate>"
|
|
52
52
|
```
|
|
53
|
-
- **`template.botDefinitionId` is present** (a pre-provisioned instantiation, if any — never the normal Fulfiller case) ⇒ read the `BotDefinition` by Id **`OR` by the collected `<developerName>`** in one query, INCLUDING its latest version's status, then classify. The `OR
|
|
53
|
+
- **`template.botDefinitionId` is present** (a pre-provisioned instantiation, if any — never the normal Fulfiller case) ⇒ read the `BotDefinition` by Id **`OR` by the template's `AgentTemplate` `OR` by the collected `<developerName>`** in one query, INCLUDING its latest version's status, then classify. The `OR AgentTemplate=` clause would catch a live agent by its platform-stamped source template (`matchedBy:"agentTemplate"`) regardless of its DeveloperName. The `OR DeveloperName=` clause catches a **dangling** template→BotDefinition link — a `botDefinitionId` whose target row was since deleted: the by-Id half returns nothing, but the live same-name agent still surfaces, so the classifier falls back to it (`matchedBy:"developerName"`) instead of concluding `exists:false` and letting the create collide with `DUPLICATE_VALUE`:
|
|
54
54
|
```bash
|
|
55
|
-
sf data query -q "SELECT Id,DeveloperName,MasterLabel,(SELECT Id,Status FROM BotVersions ORDER BY VersionNumber DESC LIMIT 1) FROM BotDefinition WHERE Id='<botDefinitionId>' OR DeveloperName='<developerName>'" \
|
|
55
|
+
sf data query -q "SELECT Id,DeveloperName,MasterLabel,AgentTemplate,(SELECT Id,Status FROM BotVersions ORDER BY VersionNumber DESC LIMIT 1) FROM BotDefinition WHERE Id='<botDefinitionId>' OR AgentTemplate='<agentTemplate>' OR DeveloperName='<developerName>'" \
|
|
56
56
|
--target-org <alias> --json > ${SCRATCH_DIR}/bot-existing.json 2>${SCRATCH_DIR}/bot-existing.err || true
|
|
57
|
-
node "<skill_dir>/scripts/classify-agent-existence.mjs" ${SCRATCH_DIR}/bot-existing.json "<botDefinitionId>" "<developerName>"
|
|
57
|
+
node "<skill_dir>/scripts/classify-agent-existence.mjs" ${SCRATCH_DIR}/bot-existing.json "<botDefinitionId>" "<developerName>" "<agentTemplate>"
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
The classifier prints `{ exists, count, matchedBy, agentId, botDefinitionId, developerName, latestVersionId, latestVersionStatus, needsActivation }` — `matchedBy` is `"botDefinitionId"` | `"developerName"` | `null`, and `developerName` is the ACTUAL live agent's DeveloperName read from the record, surface it in the report rather than the collected guess:
|
|
60
|
+
The classifier prints `{ exists, count, matchedBy, agentId, botDefinitionId, developerName, latestVersionId, latestVersionStatus, needsActivation }` — `matchedBy` is `"botDefinitionId"` | `"agentTemplate"` | `"developerName"` | `null`, and `developerName` is the ACTUAL live agent's DeveloperName read from the record, surface it in the report rather than the collected guess:
|
|
61
61
|
|
|
62
62
|
- `exists:false` ⇒ proceed to Phase 3 (create path).
|
|
63
63
|
- `exists:true` and `needsActivation:false` (latest version `Active`) ⇒ **ALREADY-CREATED** — skip the write, fall through to Phase 7 verification.
|