@salesforce/afv-skills 1.40.0 → 1.41.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 +32 -43
- package/skills/experience-portal-create/SKILL.md +497 -0
- package/skills/experience-portal-create/assets/report-template.md +30 -0
- package/skills/experience-portal-create/references/mcp-invocation.md +288 -0
- package/skills/experience-portal-create/references/post-creation-activate-publish.md +165 -0
- package/skills/experience-portal-create/references/templates.md +253 -0
- package/skills/experience-ui-bundle-features-generate/SKILL.md +5 -1
- package/skills/experience-ui-bundle-frontend-generate/SKILL.md +2 -0
- package/skills/experience-ui-bundle-frontend-generate/references/page.md +1 -0
- package/skills/platform-datamask-run/SKILL.md +345 -0
- package/skills/platform-datamask-run/references/api-surface.md +130 -0
- package/skills/platform-datamask-run/references/policy-authoring.md +185 -0
- package/skills/platform-datamask-run/references/run-and-abort.md +116 -0
- package/skills/platform-datamask-run/scripts/poll-job.sh +115 -0
- package/skills/platform-dataspace-access-configure/SKILL.md +51 -3
- package/skills/platform-dataspace-access-configure/scripts/inspect-dataspace-scopes.sh +56 -0
- package/skills/platform-lightning-type-widget-coordinate/references/build-plan-format.md +1 -0
- package/skills/platform-sandbox-configure/SKILL.md +17 -2
- package/skills/platform-trial-org-create/SKILL.md +175 -0
- package/skills/platform-trial-org-create/examples/create_request.json +9 -0
- package/skills/platform-trial-org-create/examples/error_response.json +41 -0
- package/skills/platform-trial-org-create/examples/success_response.json +27 -0
- package/skills/platform-trial-org-create/references/error_codes.md +42 -0
- package/skills/platform-trial-org-create/references/signup_request_fields.md +69 -0
- package/skills/platform-trial-org-create/scripts/create_signup_request.sh +175 -0
- package/skills/platform-trial-org-create/scripts/get_signup_request.sh +155 -0
- package/skills/platform-widget-generate/SKILL.md +47 -6
- package/skills/platform-widget-generate/examples/conditional.json +3 -3
- package/skills/platform-widget-generate/examples/list-with-foreach.json +2 -2
- package/skills/platform-widget-generate/examples/single-object.json +2 -2
- package/skills/platform-widget-generate/references/widget-bundle-layout.md +1 -1
- package/skills/service-agentforce-channel-configure/SKILL.md +271 -0
- package/skills/service-agentforce-channel-configure/references/agent-wiring.md +97 -0
- package/skills/service-agentforce-channel-configure/references/channel-branch-email.md +145 -0
- package/skills/service-agentforce-channel-configure/references/channel-branch-voice.md +69 -0
- package/skills/service-agentforce-channel-configure/references/channel-types.md +61 -0
- package/skills/service-agentforce-channel-configure/references/live-traffic-gate.md +86 -0
- package/skills/service-agentforce-channel-configure/references/queue-resolution.md +135 -0
- package/skills/service-agentforce-channel-configure/references/routing-flow.md +384 -0
- package/skills/service-catalog-template-deploy/SKILL.md +310 -0
- package/skills/service-catalog-template-deploy/references/cli-invocation.md +258 -0
- package/skills/service-catalog-template-deploy/scripts/activate-verify.mjs +164 -0
- package/skills/service-catalog-template-deploy/scripts/build-deploy-payload.mjs +94 -0
- package/skills/service-catalog-template-deploy/scripts/resolve-template.mjs +331 -0
- package/skills/service-catalog-template-search/SKILL.md +212 -0
- package/skills/service-catalog-template-search/references/cli-invocation.md +128 -0
- package/skills/service-catalog-template-search/scripts/classify-catalog.mjs +205 -0
- package/skills/service-concierge-portal-generate/SKILL.md +126 -0
- package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +1428 -0
- package/skills/service-digital-engagement-channel-configure/SKILL.md +46 -6
- package/skills/service-digital-engagement-channel-configure/assets/messaging_channel_template.xml +2 -1
- package/skills/service-digital-engagement-channel-configure/examples/asa_agent_channel.xml +4 -1
- package/skills/service-helpagent-coordinate/README.md +8 -2
- package/skills/service-helpagent-coordinate/SKILL.md +126 -130
- package/skills/service-helpagent-coordinate/assets/help-agent-spec.md +70 -53
- package/skills/service-helpagent-coordinate/references/agent-script.md +571 -457
- package/skills/service-helpagent-coordinate/references/channel-voice.md +38 -9
- package/skills/service-helpagent-coordinate/references/channel-web-chat.md +173 -49
- package/skills/service-helpagent-coordinate/references/output-report-format.md +126 -0
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +153 -0
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +79 -0
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +35 -0
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/SKILL.md +271 -0
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/references/cli-invocation.md +265 -0
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/classify-enable-plan.mjs +220 -0
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/classify-final-report.mjs +102 -0
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/record-enable-result.mjs +73 -0
- package/skills/service-itsm-agentic-setup-agentforce-studio-validate/SKILL.md +206 -0
- package/skills/service-itsm-agentic-setup-agentforce-studio-validate/references/cli-invocation.md +194 -0
- package/skills/service-itsm-agentic-setup-agentforce-studio-validate/scripts/classify-readiness.mjs +223 -0
- package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +45 -7
- package/skills/service-itsm-agentic-setup-cmdb-configure/references/mcp-invocation.md +45 -5
- package/skills/service-itsm-agentic-setup-configure/SKILL.md +116 -0
- package/skills/service-itsm-agentic-setup-configure/examples/output-templates.md +64 -0
- package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +158 -0
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/cli-invocation.md +361 -0
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/error-taxonomy.md +44 -0
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/reactivation.md +66 -0
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +66 -0
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/specialized-templates.md +148 -0
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/workflow-detail.md +169 -0
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/build-create-body.mjs +116 -0
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/classify-agent-existence.mjs +185 -0
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/classify-preflight.mjs +168 -0
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/create-scratch-dir.mjs +58 -0
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +197 -0
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +186 -0
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/action-availability.md +51 -0
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/cli-invocation.md +345 -0
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/error-taxonomy.md +44 -0
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/reactivation.md +63 -0
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +44 -0
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md +149 -0
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/build-create-body.mjs +110 -0
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-action-availability.mjs +201 -0
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-activate-result.mjs +135 -0
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-agent-existence.mjs +194 -0
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-preflight.mjs +158 -0
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/create-scratch-dir.mjs +58 -0
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +191 -0
- package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +133 -0
- package/skills/service-itsm-agentic-setup-incident-management/examples/output-templates.md +71 -0
- package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +308 -0
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +23 -0
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +193 -0
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +57 -0
- package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +394 -0
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/SKILL.md +266 -0
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/cli-invocation.md +106 -0
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/helper-contracts.md +142 -0
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/permset-topology.md +82 -0
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-action-surface.mjs +137 -0
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-assignment-state.mjs +99 -0
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-permset-availability.mjs +120 -0
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/resolve-target-user.mjs +86 -0
- package/skills/service-itsm-agentic-setup-uel-user-create/SKILL.md +284 -0
- package/skills/service-itsm-agentic-setup-uel-user-create/references/mcp-invocation.md +302 -0
- package/skills/service-itsm-channels-coordinate/SKILL.md +472 -0
- package/skills/service-itsm-incident-mgmt-configure/SKILL.md +212 -0
- package/skills/service-itsm-incident-mgmt-configure/references/mcp-invocation.md +225 -0
- package/skills/service-itsm-incident-priority-configure/SKILL.md +53 -12
- package/skills/service-itsm-swarming-configure/SKILL.md +212 -0
- package/skills/service-itsm-teams-configure/SKILL.md +395 -0
- package/skills/service-itsm-teams-configure/references/azure-credential-population.md +213 -0
- package/skills/service-itsm-teams-configure/references/gotchas.md +23 -0
- package/skills/service-itsm-teams-coordinate/SKILL.md +175 -0
- package/skills/service-itsm-teams-coordinate/examples/output-templates.md +85 -0
- package/skills/service-itsm-teams-debug/SKILL.md +144 -0
- package/skills/service-itsm-teams-debug/references/configuration-checklists.md +277 -0
- package/skills/service-itsm-teams-debug/references/report-generation.md +95 -0
- package/skills/service-itsm-teams-employee-agent-configure/SKILL.md +139 -0
- package/skills/service-itsm-teams-employee-agent-configure/assets/Teams_AgentForce.EmbeddedServiceConfig-meta.xml +43 -0
- package/skills/service-itsm-teams-employee-agent-configure/references/teams-embedded-employee-agent.md +480 -0
- package/skills/service-itsm-teams-itdesk-configure/SKILL.md +232 -0
- package/skills/service-itsm-teams-itservice-configure/SKILL.md +391 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: service-itsm-agentic-setup-agentforce-studio-validate
|
|
3
|
+
description: "Validate the Agentforce for IT Service prerequisites for a Fulfiller or Employee agent, using the Salesforce CLI (sf). The READ-ONLY prerequisite check: it reads the Salesforce Go feature toggles behind the Agentforce for IT Service setup page (Agentforce Studio, the IT Service Fulfiller / IT Service Employee templates, Specialized Agent Templates for Employee) via one Connect API call, then a helper script classifies the chosen path into a READY / NOT-READY verdict. It never enables anything; when a toggle is off it hands off to the agentforce-studio-configure skill to turn it on. Use when asked to check Agentforce prerequisites, validate the org is ready for an ITSM agent, or verify Agentforce Studio is enabled. Triggers: check agentforce prerequisites, validate agentforce readiness, is my org ready for the fulfiller agent, verify agentforce studio enabled. DO NOT TRIGGER: enabling or turning on the toggles, creating the fulfiller agent (service-itsm-agentic-setup-fulfiller-agent-configure), or CMDB CRUD."
|
|
4
|
+
metadata:
|
|
5
|
+
version: "2.1"
|
|
6
|
+
domains: ["Service", "Agentforce"]
|
|
7
|
+
minApiVersion: "67.0"
|
|
8
|
+
relatedSkills:
|
|
9
|
+
- "service-itsm-agentic-setup-agentforce-studio-configure"
|
|
10
|
+
- "service-itsm-agentic-setup-employee-agent-configure"
|
|
11
|
+
- "service-itsm-agentic-setup-fulfiller-agent-configure"
|
|
12
|
+
cliTools:
|
|
13
|
+
- tool: ["node"]
|
|
14
|
+
semver: ">=18.0.0"
|
|
15
|
+
- tool: ["sf"]
|
|
16
|
+
semver: ">=2.0.0"
|
|
17
|
+
accessCheck:
|
|
18
|
+
- type: "license"
|
|
19
|
+
value: "Agentforce"
|
|
20
|
+
allowed-tools: |
|
|
21
|
+
Bash
|
|
22
|
+
Read
|
|
23
|
+
AskUserQuestion
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# Validate Agentforce for IT Service Prerequisites
|
|
27
|
+
|
|
28
|
+
Readiness check for **Agentforce for IT Service**, for either the **Fulfiller** or the **Employee** agent path. Before an ITSM agent can be created, the org must have the right **Salesforce Go feature toggles** turned on (the ones on the *Agentforce for IT Service* setup page). This skill reads those toggles through the **Salesforce CLI (`sf`)** — a single authenticated Connect API call via `sf api request rest` — and a helper script classifies the chosen agent path into a **READY / NOT-READY** verdict.
|
|
29
|
+
|
|
30
|
+
This is the **read-only** step in a three-skill flow. It **never enables anything**; turning toggles on is a separate write-capable skill:
|
|
31
|
+
|
|
32
|
+
| Step | Skill | What it does |
|
|
33
|
+
|------|-------|--------------|
|
|
34
|
+
| 1. Validate (this skill) | `service-itsm-agentic-setup-agentforce-studio-validate` | Read the toggles → READY / NOT-READY (no writes) |
|
|
35
|
+
| 2. Configure | `service-itsm-agentic-setup-agentforce-studio-configure` | Turn the disabled toggles ON |
|
|
36
|
+
| 3. Create agent | `service-itsm-agentic-setup-fulfiller-agent-configure` | Create + activate the Fulfiller agent |
|
|
37
|
+
|
|
38
|
+
On NOT-READY this skill names each disabled toggle and **hands off** to `service-itsm-agentic-setup-agentforce-studio-configure` to enable them — it does not POST an enable itself.
|
|
39
|
+
|
|
40
|
+
The feature toggles and their real `featureApiName`s:
|
|
41
|
+
|
|
42
|
+
| Go-page toggle | featureApiName | Required for |
|
|
43
|
+
|----------------|----------------|--------------|
|
|
44
|
+
| Turn on Agentforce Studio | `sales-cloud-agent-studio` | both paths (shared) |
|
|
45
|
+
| Agentforce for IT Service (parent umbrella) | `service-cloud-agentforce-for-itsm` | both paths (shared) |
|
|
46
|
+
| IT Service Fulfiller | `service-cloud-it-fulfiller-agent` | **fulfiller** |
|
|
47
|
+
| IT Service Employee | `service-cloud-requestor-agent` | **employee** |
|
|
48
|
+
| Specialized Agent Templates for Employee | `service-cloud-it-service-employee-agent` | **employee** |
|
|
49
|
+
|
|
50
|
+
All are **Connect API** features (`/connect/setup/discovery/...`) — the read goes through `sf api request rest`, no Headless360 dispatcher required. The classification is **deterministic** and lives in `scripts/classify-readiness.mjs` (invoked via `Bash`), not in prose.
|
|
51
|
+
|
|
52
|
+
## Scope
|
|
53
|
+
|
|
54
|
+
- **In scope**: Reading the Agentforce-for-IT-Service Go feature toggles via `connect/setup/discovery/features/status`; classifying the **fulfiller** or **employee** path into a per-feature + overall READY / NOT-READY verdict via a helper script; on NOT-READY, naming each disabled toggle and handing off to `service-itsm-agentic-setup-agentforce-studio-configure` to enable it.
|
|
55
|
+
- **Out of scope**: **Enabling / turning on any toggle** — that write is owned by `service-itsm-agentic-setup-agentforce-studio-configure` (this skill is read-only and never POSTs an enable); creating, committing, or activating an agent (handled by `service-itsm-agentic-setup-fulfiller-agent-configure`); the org-wide multi-agent orchestration toggle (that is a Headless360-only pref, not an ITSM Connect feature, and is not one of the setup-page toggles); assigning permission sets; CMDB CRUD.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Which path?
|
|
60
|
+
|
|
61
|
+
Determine whether the user is setting up the **fulfiller** agent (the IT-fulfiller-facing agent) or an **employee** agent (the employee-facing / NGA agent). If it is not clear from the request, ask (`AskUserQuestion`). The path selects which toggles are required:
|
|
62
|
+
|
|
63
|
+
- **fulfiller** → `sales-cloud-agent-studio` + `service-cloud-agentforce-for-itsm` + `service-cloud-it-fulfiller-agent`
|
|
64
|
+
- **employee** → `sales-cloud-agent-studio` + `service-cloud-agentforce-for-itsm` + `service-cloud-requestor-agent` + `service-cloud-it-service-employee-agent`
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Preconditions
|
|
69
|
+
|
|
70
|
+
Before the skill can read anything, the CLI and target org must be configured. If any of these are unmet, `sf` surfaces an auth error or a `401`/`403`/`404`; **do not fabricate state — surface the raw error and stop**.
|
|
71
|
+
|
|
72
|
+
1. **`sf` CLI installed and authenticated to the target org** (`sf org display -o <alias>` shows Connected). All calls use `--target-org <alias>`; never extract or pass the access token by hand.
|
|
73
|
+
2. **API v67.0+**: the `connect/setup/discovery` feature APIs are available at v67.0. The version is pinned in the URL path; do not hand-edit it below the minimum.
|
|
74
|
+
3. **`node` ≥ 18** on PATH (runs the classifier script).
|
|
75
|
+
|
|
76
|
+
If a precondition fails, `sf` returns one of:
|
|
77
|
+
- Auth error / `401 Unauthorized` → session expired or wrong alias; re-run `sf org login web`.
|
|
78
|
+
- `403 Forbidden` → the user/org lacks the required access (missing Agentforce license).
|
|
79
|
+
- `404 Not Found` → the feature-discovery surface is not wired on this org tier — the classifier maps a missing/error body to CANNOT-CONFIRM, not a hard failure.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Operations at a glance
|
|
84
|
+
|
|
85
|
+
| Operation | Command | Returns |
|
|
86
|
+
|-----------|---------|---------|
|
|
87
|
+
| Read feature toggles | `sf api request rest "/services/data/v67.0/connect/setup/discovery/features/status" --method POST --body '{"featureApiNames":[...]}' --target-org <alias>` | `{items:[{apiName,status,enableBlockedReasons[],dependencyStatuses[]}]}` — `status` is `ENABLED` / `NOT_ENABLED` per toggle. |
|
|
88
|
+
|
|
89
|
+
This skill makes **only** the read above — it is read-only. Enabling a toggle (`.../feature/{apiName}/enable`) is out of scope: hand off to `service-itsm-agentic-setup-agentforce-studio-configure`. `sf api request rest` prints the raw response body (JSON) to stdout — capture it to a file and hand it to the classifier. Full command shapes and the error taxonomy live in `references/cli-invocation.md`.
|
|
90
|
+
|
|
91
|
+
> **Never extract the access token.** Use `sf api request rest` directly — it uses the CLI's stored session for the target org. Do **not** pull the `accessToken` out of `sf org display` and hand-build an HTTP request with it; that bypasses the CLI session and leaks a bearer token into shell context.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Architecture — How the check works
|
|
96
|
+
|
|
97
|
+
| Step | What happens | Tool used |
|
|
98
|
+
|------|--------------|-----------|
|
|
99
|
+
| Pick path | Determine fulfiller vs employee (ask if unclear) | `AskUserQuestion` |
|
|
100
|
+
| Read toggles | POST the feature-status batch for all toggles the path needs, capture to a file | `Bash` (`sf api request rest`) |
|
|
101
|
+
| Classify | Run `scripts/classify-readiness.mjs <file> <agentType> [exitStatus]` → per-feature + overall verdict | `Bash` (`node`) |
|
|
102
|
+
| Report | Render the classifier's verdict into the Output Format | — |
|
|
103
|
+
| Hand off | On NOT-READY, name each disabled toggle and hand off to `service-itsm-agentic-setup-agentforce-studio-configure` to enable it | — |
|
|
104
|
+
|
|
105
|
+
The skill is read-only — it never enables a toggle.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Workflow
|
|
110
|
+
|
|
111
|
+
Substitute `<alias>` with the target org alias (ask the user, or use the default org from `sf config`). `<agentType>` is `fulfiller` or `employee`.
|
|
112
|
+
|
|
113
|
+
### Phase 1 — Read the feature toggles
|
|
114
|
+
|
|
115
|
+
1. POST the feature-status batch for every toggle the chosen path needs (safe to always request all five — the classifier only judges the ones the path requires), capturing stdout to a file. Do **not** add `--json`:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
sf api request rest "/services/data/v67.0/connect/setup/discovery/features/status" \
|
|
119
|
+
--method POST \
|
|
120
|
+
--body '{"featureApiNames":["sales-cloud-agent-studio","service-cloud-agentforce-for-itsm","service-cloud-it-fulfiller-agent","service-cloud-requestor-agent","service-cloud-it-service-employee-agent"]}' \
|
|
121
|
+
--target-org <alias> > /tmp/features-status.json 2>/tmp/features-status.err
|
|
122
|
+
echo $? > /tmp/features-status.exit
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Capture the command's exit status (`$?`) so the classifier can tell a confirmed 404 (gate not wired) apart from an auth/permission/transport failure. Do not swallow the exit with `|| true` — pass it to the classifier in Phase 2.
|
|
126
|
+
|
|
127
|
+
### Phase 2 — Classify (helper script)
|
|
128
|
+
|
|
129
|
+
2. Run the classifier over the captured file, the chosen agent type, **and the captured exit status**. **Use the skill's absolute directory** for the script path:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
node "<skill_dir>/scripts/classify-readiness.mjs" /tmp/features-status.json <agentType> "$(cat /tmp/features-status.exit)"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
It prints `{ agentType, readState, features, verdict, notEnabled, enableable, reasons, rawError }` where each feature is `PASS | FAIL | CANNOT-CONFIRM | ERROR` and `verdict` is `READY | NOT-READY | CANNOT-CONFIRM | ERROR`. This is the authoritative verdict — do not re-derive it from the raw response in prose. `notEnabled` lists every disabled required toggle; `enableable` is the subset with **no** `enableBlockedReasons` — the ones the configure skill can turn on straight away (the rest are blocked by an unmet dependency or a purchase/licensing gate).
|
|
136
|
+
|
|
137
|
+
### Phase 3 — Report + hand off
|
|
138
|
+
|
|
139
|
+
3. Render the classifier output into the Output Format below.
|
|
140
|
+
- **READY** → point the user at `service-itsm-agentic-setup-fulfiller-agent-configure` to create the agent (fulfiller path), or `service-itsm-agentic-setup-employee-agent-configure` (employee path).
|
|
141
|
+
- **NOT-READY** → name each disabled toggle (from `notEnabled`). For the `enableable` subset, hand off to `service-itsm-agentic-setup-agentforce-studio-configure` to turn them on (offer via `AskUserQuestion`: _"N prerequisite toggle(s) are off. Run service-itsm-agentic-setup-agentforce-studio-configure to enable them?"_ → on Yes, delegate; on No, stop and report). For any disabled-but-blocked toggle (in `notEnabled` but not `enableable`), report its `enableBlockedReasons` verbatim — it cannot be enabled until the blocker clears. **This skill does not POST an enable itself.**
|
|
142
|
+
- **CANNOT-CONFIRM** → state which toggle(s) had no status and that the feature-discovery surface may not be wired on this org tier.
|
|
143
|
+
- **ERROR** → the read failed (auth / permission / transport, or an unexpected body — see `rawError`). Surface the raw response and **stop**; do not treat it as a mere wiring gap.
|
|
144
|
+
|
|
145
|
+
### Phase 4 — (After enable) re-validate
|
|
146
|
+
|
|
147
|
+
4. If the user ran `service-itsm-agentic-setup-agentforce-studio-configure` to enable the toggles, re-run Phase 1 + Phase 2 here to confirm the verdict flips to READY, then report. This skill's own role remains read-only throughout.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Rules / Constraints
|
|
152
|
+
|
|
153
|
+
| Constraint | Rationale |
|
|
154
|
+
|-----------|-----------|
|
|
155
|
+
| **This skill is read-only** — it never POSTs a feature enable | Turning toggles on is a separate write-capable skill (`service-itsm-agentic-setup-agentforce-studio-configure`); keeping the two apart keeps the `validate` verb honest and avoids two skills owning the same write |
|
|
156
|
+
| On NOT-READY, **hand off** to `service-itsm-agentic-setup-agentforce-studio-configure` for enablement | That skill owns the `.../feature/{apiName}/enable` write path (idempotent, dependency-ordered); this skill only names what is off |
|
|
157
|
+
| The read goes through `sf api request rest`; **never extract the access token** or hand-build a raw HTTP request | `sf api request rest` uses the CLI's stored session for `--target-org`; extracting the token leaks a bearer token into shell context |
|
|
158
|
+
| Classification lives in `scripts/classify-readiness.mjs`, invoked via `Bash` — not in prose | The verdict is a deterministic decision table over fixed feature statuses; a script is reliable, prose interpretation is not (authoring standard A9) |
|
|
159
|
+
| The required-toggle set depends on the agent path | Fulfiller and Employee agents gate on different template toggles; the classifier encodes both paths |
|
|
160
|
+
| These are **Connect API** features — use SF CLI, not Headless360 | A Connect/Tooling equivalent exists, so SF CLI is preferred (avoids the Headless360HostedMcpServer org-perm gate) |
|
|
161
|
+
| `enableBlockedReasons` non-empty ⇒ toggle is not in `enableable` — report the blocker | A blocked toggle can't be turned on even by the configure skill until the unmet dependency / purchase gate clears |
|
|
162
|
+
| Do **not** pass `--json` to `sf api request rest` | It is unsupported on some Connect endpoints; the raw stdout body is already JSON |
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Verification Checklist
|
|
167
|
+
|
|
168
|
+
- [ ] The agent path (fulfiller / employee) was determined (asked if unclear).
|
|
169
|
+
- [ ] The feature-status POST was run via `sf api request rest` and its body captured to a file.
|
|
170
|
+
- [ ] `scripts/classify-readiness.mjs` was invoked with the file, the agent type, **and the captured exit status**, and its `{verdict, notEnabled, enableable, rawError}` output recorded.
|
|
171
|
+
- [ ] The verdict (READY / NOT-READY / CANNOT-CONFIRM / ERROR) was reported verbatim from the classifier; on NOT-READY each disabled toggle was named, with the enable route only for the `enableable` subset and `enableBlockedReasons` reported for the rest; on ERROR the raw response was surfaced and the skill stopped.
|
|
172
|
+
- [ ] No feature was enabled by this skill (it is read-only); on NOT-READY the disabled toggles were named and the hand-off to `service-itsm-agentic-setup-agentforce-studio-configure` was offered, and the access token was never extracted.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Output Format
|
|
177
|
+
|
|
178
|
+
Present the readiness report as:
|
|
179
|
+
|
|
180
|
+
```text
|
|
181
|
+
Agentforce for IT Service — Prerequisite Check (via service-itsm-agentic-setup-agentforce-studio-validate)
|
|
182
|
+
|
|
183
|
+
Org: <org-alias> (API v67.0)
|
|
184
|
+
Agent path: fulfiller | employee
|
|
185
|
+
|
|
186
|
+
[PASS|FAIL] Agentforce Studio ......................... ENABLED | NOT_ENABLED (sales-cloud-agent-studio)
|
|
187
|
+
[PASS|FAIL] Agentforce for IT Service (parent) ........ ENABLED | NOT_ENABLED (service-cloud-agentforce-for-itsm)
|
|
188
|
+
[PASS|FAIL] <path-specific toggle(s)> ................. ENABLED | NOT_ENABLED (<featureApiName>)
|
|
189
|
+
|
|
190
|
+
Verdict: READY | NOT-READY | CANNOT-CONFIRM | ERROR
|
|
191
|
+
|
|
192
|
+
Next steps:
|
|
193
|
+
- <If READY: "Org satisfies the prerequisites for the <path> agent. Create it via service-itsm-agentic-setup-fulfiller-agent-configure (fulfiller) / the employee-agent skill.">
|
|
194
|
+
- <If NOT-READY: list each disabled toggle; for the enableable ones, hand off to service-itsm-agentic-setup-agentforce-studio-configure to turn them on; for blocked ones, report the enableBlockedReasons.>
|
|
195
|
+
- <If CANNOT-CONFIRM: state which toggle(s) returned no status and that the feature-discovery surface may not be wired on this org.>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
This skill is **read-only** — no org state is written. The only file it produces is the temporary response capture handed to the classifier.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Reference File Index
|
|
203
|
+
|
|
204
|
+
| File | When to read |
|
|
205
|
+
|------|--------------|
|
|
206
|
+
| `references/cli-invocation.md` | Every phase — exact `sf api request rest` read call shape, the feature-status route, the read-only / hand-off rule, the never-extract-token rule, the classifier contract, and the error taxonomy |
|
package/skills/service-itsm-agentic-setup-agentforce-studio-validate/references/cli-invocation.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# CLI invocation reference — service-itsm-agentic-setup-agentforce-studio-validate
|
|
2
|
+
|
|
3
|
+
This skill uses the **Salesforce CLI (`sf`)** as its only transport, and it is
|
|
4
|
+
**read-only**: it reads the *Agentforce for IT Service* Salesforce Go feature
|
|
5
|
+
toggles via one Connect API POST (`sf api request rest`), and a Node helper
|
|
6
|
+
script classifies the chosen agent path into a verdict. It **never enables** a
|
|
7
|
+
toggle — turning toggles on is owned by the separate write-capable skill
|
|
8
|
+
`service-itsm-agentic-setup-agentforce-studio-configure`, which this skill hands
|
|
9
|
+
off to on NOT-READY. It extracts no access tokens.
|
|
10
|
+
|
|
11
|
+
## Why `sf api request rest`, never curl + token
|
|
12
|
+
|
|
13
|
+
`sf api request rest` authenticates using the CLI's stored session for the
|
|
14
|
+
`--target-org` alias — the CLI mints/refreshes the token internally and never
|
|
15
|
+
exposes it. **Do not** do:
|
|
16
|
+
|
|
17
|
+
<!-- skill-validate: ignore -->
|
|
18
|
+
```bash
|
|
19
|
+
# FORBIDDEN — leaks a bearer token into shell context, bypasses the CLI session
|
|
20
|
+
TOKEN=$(sf org display --json -o <alias> | jq -r '.result.accessToken')
|
|
21
|
+
curl -H "Authorization: Bearer $TOKEN" https://.../connect/setup/discovery/features/status
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Every call this skill makes is a plain `/services/data/v67.0/connect/...` Connect
|
|
25
|
+
API path, exactly what `sf api request rest` proxies. There is no reason to fall
|
|
26
|
+
back to curl.
|
|
27
|
+
|
|
28
|
+
## Why SF CLI (Connect API), not Headless360
|
|
29
|
+
|
|
30
|
+
The *Agentforce for IT Service* Go toggles are backed by real Connect API
|
|
31
|
+
features under `/connect/setup/discovery/`. Because a Connect equivalent exists,
|
|
32
|
+
the transport decision prefers SF CLI to avoid the `Headless360HostedMcpServer`
|
|
33
|
+
org-perm gate. (The only ITSM-adjacent pref that has *no* Connect equivalent —
|
|
34
|
+
the org-wide multi-agent orchestrator toggle — is out of this skill's scope and
|
|
35
|
+
would require Headless360 dispatch.)
|
|
36
|
+
|
|
37
|
+
## The feature toggles
|
|
38
|
+
|
|
39
|
+
The *Agentforce for IT Service* setup page (feature key
|
|
40
|
+
`service-cloud-agentforce-for-itsm`) has these child toggles. The `featureApiName`
|
|
41
|
+
column is what the API uses:
|
|
42
|
+
|
|
43
|
+
| UI toggle | featureApiName | Required for |
|
|
44
|
+
|-----------|----------------|--------------|
|
|
45
|
+
| Turn on Agentforce Studio | `sales-cloud-agent-studio` | both paths |
|
|
46
|
+
| Agentforce for IT Service (parent umbrella) | `service-cloud-agentforce-for-itsm` | both paths |
|
|
47
|
+
| IT Service Fulfiller | `service-cloud-it-fulfiller-agent` | fulfiller |
|
|
48
|
+
| IT Service Employee | `service-cloud-requestor-agent` | employee |
|
|
49
|
+
| Specialized Agent Templates for Employee | `service-cloud-it-service-employee-agent` | employee |
|
|
50
|
+
|
|
51
|
+
Dependency chain (enable order): `sales-cloud-einstein-generative-ai` →
|
|
52
|
+
`sales-cloud-agent-studio` → `service-cloud-agentforce-for-itsm` (parent) → the
|
|
53
|
+
child template toggles. The read response echoes this in `dependencyStatuses[]`.
|
|
54
|
+
|
|
55
|
+
## Target org, API version, and the `--json` rule
|
|
56
|
+
|
|
57
|
+
- **Target org**: always `--target-org <alias>` (or `-o <alias>`). Resolve from
|
|
58
|
+
the user or the default org (`sf config get target-org`).
|
|
59
|
+
- **API version**: pinned in the URL path (`/services/data/v67.0/...`). Do not
|
|
60
|
+
hand-edit below `metadata.minApiVersion` (`67.0`).
|
|
61
|
+
- **`--json`**: do **not** add `--json` to `sf api request rest` — it is
|
|
62
|
+
unsupported on some Connect endpoints and errors; the raw stdout body is
|
|
63
|
+
already JSON.
|
|
64
|
+
|
|
65
|
+
## Read — feature status (the one read this skill makes)
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
sf api request rest "/services/data/v67.0/connect/setup/discovery/features/status" \
|
|
69
|
+
--method POST \
|
|
70
|
+
--body '{"featureApiNames":["sales-cloud-agent-studio","service-cloud-agentforce-for-itsm","service-cloud-it-fulfiller-agent","service-cloud-requestor-agent","service-cloud-it-service-employee-agent"]}' \
|
|
71
|
+
--target-org <alias> > /tmp/features-status.json 2>/tmp/features-status.err
|
|
72
|
+
echo $? > /tmp/features-status.exit
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Capture the exit status (`$?`) rather than swallowing it with `|| true` — the
|
|
76
|
+
classifier uses it to separate a confirmed 404 (gate not wired → CANNOT-CONFIRM)
|
|
77
|
+
from an empty-body auth/permission/transport failure (→ ERROR, surface and stop).
|
|
78
|
+
It is safe to always request all five apiNames — the classifier only judges the
|
|
79
|
+
subset the chosen path requires. Response shape (abridged):
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"items": [
|
|
84
|
+
{
|
|
85
|
+
"apiName": "service-cloud-it-fulfiller-agent",
|
|
86
|
+
"status": "ENABLED",
|
|
87
|
+
"enableBlockedReasons": [],
|
|
88
|
+
"dependencyStatuses": [
|
|
89
|
+
{ "apiName": "sales-cloud-agent-studio", "status": "ENABLED", "enableBlockedReasons": [] }
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
{ "apiName": "service-cloud-it-service-employee-agent", "status": "NOT_ENABLED", "enableBlockedReasons": [] }
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- `status: "ENABLED"` ⇒ toggle on ⇒ **PASS**.
|
|
98
|
+
- `status: "NOT_ENABLED"` with empty `enableBlockedReasons` ⇒ toggle off ⇒ **FAIL**;
|
|
99
|
+
the classifier lists it under `enableable` and attaches the enable route (which the
|
|
100
|
+
hand-off skill uses — this skill only reports it, it does not call it).
|
|
101
|
+
- `status: "NOT_ENABLED"` with non-empty `enableBlockedReasons` ⇒ **FAIL** but
|
|
102
|
+
**not** enableable — the classifier keeps it in `notEnabled`, omits it from
|
|
103
|
+
`enableable`, and carries the blockers so the hand-off never queues a doomed enable.
|
|
104
|
+
- A missing item ⇒ **CANNOT-CONFIRM** for that toggle.
|
|
105
|
+
- A confirmed `404`/NOT_FOUND error body ⇒ whole read is **CANNOT-CONFIRM** (gate not wired).
|
|
106
|
+
- An empty body (CLI exited non-zero) or an auth/permission/unexpected error body ⇒
|
|
107
|
+
whole read is **ERROR** — surface `rawError` and stop.
|
|
108
|
+
|
|
109
|
+
The single-feature GET `.../connect/setup/discovery/feature/{apiName}/summary`
|
|
110
|
+
returns the same status for one feature; the batch POST above is preferred so one
|
|
111
|
+
call covers the whole path.
|
|
112
|
+
|
|
113
|
+
## Enablement is a hand-off — this skill never POSTs an enable
|
|
114
|
+
|
|
115
|
+
This skill is read-only. When the verdict is NOT-READY, it names each disabled
|
|
116
|
+
toggle (the `notEnabled` list; `enableable` is the subset with no blockers) and
|
|
117
|
+
hands off to **`service-itsm-agentic-setup-agentforce-studio-configure`**, which
|
|
118
|
+
owns the write path `POST /connect/setup/discovery/feature/{apiName}/enable`
|
|
119
|
+
(idempotent, dependency-ordered — Einstein GenAI → Studio → the ITSM child
|
|
120
|
+
toggles). After the user runs that skill, re-read `features/status` here and
|
|
121
|
+
re-run the classifier to confirm the verdict flips to READY. Do **not** POST the
|
|
122
|
+
enable route from this skill — that would duplicate the configure skill's
|
|
123
|
+
responsibility and break the read-only contract the `validate` name promises.
|
|
124
|
+
|
|
125
|
+
## The classifier — `scripts/classify-readiness.mjs`
|
|
126
|
+
|
|
127
|
+
The deterministic verdict logic lives in the script, not in the workflow prose
|
|
128
|
+
(authoring standard A9). Invoke it with the captured file **and** the agent type,
|
|
129
|
+
using the skill's **absolute** directory (a bare `./scripts/...` won't resolve
|
|
130
|
+
against the CWD):
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
node "<skill_dir>/scripts/classify-readiness.mjs" /tmp/features-status.json <fulfiller|employee> "$(cat /tmp/features-status.exit)"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The third arg (the captured `$?`) is optional but recommended — it lets the
|
|
137
|
+
classifier distinguish an empty-body failure (ERROR) from a confirmed 404
|
|
138
|
+
(CANNOT-CONFIRM). Output (to stdout):
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"agentType": "fulfiller | employee",
|
|
143
|
+
"readState": "ok | not-wired | error",
|
|
144
|
+
"features": { "<apiName>": { "status": "ENABLED|NOT_ENABLED|UNKNOWN", "signal": "PASS|FAIL|CANNOT-CONFIRM|ERROR", "enableBlockedReasons": [], "enableable": true, "enableRoute": "POST .../enable | null" } },
|
|
145
|
+
"verdict": "READY | NOT-READY | CANNOT-CONFIRM | ERROR",
|
|
146
|
+
"notEnabled": ["<apiName>", "..."],
|
|
147
|
+
"enableable": ["<apiName>", "..."],
|
|
148
|
+
"reasons": ["..."],
|
|
149
|
+
"rawError": "null | <error snippet>"
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Verdict rule (encoded in the script):
|
|
154
|
+
- A read failure that is **not** a confirmed 404 (empty body / auth / permission /
|
|
155
|
+
unexpected shape) ⇒ `ERROR` — surface `rawError` and stop.
|
|
156
|
+
- Otherwise: any required toggle `FAIL` ⇒ `NOT-READY`; all required `PASS` ⇒
|
|
157
|
+
`READY`; else (a `CANNOT-CONFIRM` with no `FAIL`) ⇒ `CANNOT-CONFIRM`.
|
|
158
|
+
|
|
159
|
+
`notEnabled` lists every `NOT_ENABLED` required toggle; `enableable` is the subset
|
|
160
|
+
with **no** `enableBlockedReasons` — the toggles the hand-off configure skill can
|
|
161
|
+
turn on straight away (the rest are blocked). `enableRoute` is informational only.
|
|
162
|
+
The script always exits `0` on a usable agent type; the verdict is in the payload.
|
|
163
|
+
A confirmed `404`/NOT_FOUND maps every feature to CANNOT-CONFIRM (never a throw);
|
|
164
|
+
an empty/auth/unexpected body maps to ERROR.
|
|
165
|
+
|
|
166
|
+
## Error taxonomy
|
|
167
|
+
|
|
168
|
+
- **Auth error / `401 Unauthorized`** — session expired or wrong alias. Re-run
|
|
169
|
+
`sf org login web`; there is no token to refresh by hand.
|
|
170
|
+
- **`403 Forbidden`** — the user/org lacks the Agentforce license (`accessCheck`
|
|
171
|
+
gate). Surface verbatim.
|
|
172
|
+
- **`404 Not Found`** on `features/status` — the feature-discovery surface is not
|
|
173
|
+
wired on the org tier (expected on some scratch orgs). The classifier maps a
|
|
174
|
+
confirmed 404/NOT_FOUND error body to CANNOT-CONFIRM.
|
|
175
|
+
- **`401 Unauthorized` / `403 Forbidden` / empty body / unexpected shape** —
|
|
176
|
+
the classifier maps these to **ERROR** (not CANNOT-CONFIRM): surface `rawError`
|
|
177
|
+
and stop. An unreadable org must not masquerade as a mere wiring gap.
|
|
178
|
+
- **`enableBlockedReasons` non-empty** on a toggle — an unmet dependency or a
|
|
179
|
+
purchase/licensing gate; the classifier keeps the toggle in `notEnabled`,
|
|
180
|
+
omits it from `enableable`, and reports the reason. Do not POST enable.
|
|
181
|
+
|
|
182
|
+
## Gotchas
|
|
183
|
+
|
|
184
|
+
| Issue | Resolution |
|
|
185
|
+
|-------|------------|
|
|
186
|
+
| `sf api request rest --json` errors | Don't pass `--json` — the raw stdout body is already JSON |
|
|
187
|
+
| Bare `./scripts/classify-readiness.mjs` "not found" | Use the skill's absolute `<skill_dir>` in the `node` invocation |
|
|
188
|
+
| Passing the file but no agent type | The classifier needs `<fulfiller\|employee>` as its 2nd arg — it exits 2 (usage) otherwise |
|
|
189
|
+
| Treating a 404 as a hard fail | A confirmed 404 is CANNOT-CONFIRM — report which toggle had no status, not a hard NOT-READY |
|
|
190
|
+
| Treating an auth/permission failure or empty body as CANNOT-CONFIRM | Pass `$?` as the 3rd classifier arg — an empty/auth/unexpected body is ERROR (surface `rawError`, stop); only a confirmed 404 is CANNOT-CONFIRM |
|
|
191
|
+
| Handing off a blocked toggle for enable | Hand off only the `enableable` toggles; a `notEnabled` toggle with `enableBlockedReasons` is excluded — report the blocker instead |
|
|
192
|
+
| POSTing the enable route from this skill | Never — this skill is read-only; enablement is owned by `service-itsm-agentic-setup-agentforce-studio-configure`. Hand off instead |
|
|
193
|
+
| Tempted to token+curl for "just a POST read" | Never — `sf api request rest` handles auth for `/services/data/...` paths |
|
|
194
|
+
| Checking `agentforce-studio/access/{product}` as the studio toggle | That endpoint is a product-entitlement check, not the feature toggle — use `sales-cloud-agent-studio` feature status instead |
|
package/skills/service-itsm-agentic-setup-agentforce-studio-validate/scripts/classify-readiness.mjs
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Deterministic readiness classifier for the Agentforce for IT Service prerequisite check.
|
|
3
|
+
//
|
|
4
|
+
// Consumes the raw JSON body of a single `sf api request rest` read of the
|
|
5
|
+
// Salesforce Go feature-status endpoint and, for a chosen agent path
|
|
6
|
+
// (fulfiller | employee), decides whether every required feature toggle is ON.
|
|
7
|
+
// This is the deterministic decision table that MUST NOT be interpreted by the
|
|
8
|
+
// model in prose (authoring standard A9).
|
|
9
|
+
//
|
|
10
|
+
// Usage:
|
|
11
|
+
// node classify-readiness.mjs <features-status.json> <agentType> [exitStatus]
|
|
12
|
+
//
|
|
13
|
+
// <features-status.json> is a FILE PATH to the raw stdout captured from:
|
|
14
|
+
// sf api request rest "/services/data/vXX.0/connect/setup/discovery/features/status" \
|
|
15
|
+
// --method POST --body '{"featureApiNames":[...]}' --target-org <org> > file.json
|
|
16
|
+
// The body shape is { items: [ { apiName, status, enableBlockedReasons[],
|
|
17
|
+
// dependencyStatuses[] } ] } where status is "ENABLED" | "NOT_ENABLED".
|
|
18
|
+
// The file may instead hold the CLI error payload (a JSON array like
|
|
19
|
+
// [{"errorCode":"NOT_FOUND",...}]) or be empty when the CLI exited non-zero.
|
|
20
|
+
//
|
|
21
|
+
// <exitStatus> is the OPTIONAL numeric exit code of the `sf` command ($? in the
|
|
22
|
+
// shell). Pass it so the classifier can tell a *confirmed* 404-style "gate not
|
|
23
|
+
// wired" (→ CANNOT-CONFIRM) apart from an auth / permission / transport failure
|
|
24
|
+
// (→ ERROR, surface the raw response and stop). An empty or unparseable body is
|
|
25
|
+
// treated as ERROR, never CANNOT-CONFIRM.
|
|
26
|
+
//
|
|
27
|
+
// <agentType> is one of: fulfiller | employee. It selects which feature toggles
|
|
28
|
+
// are required — fulfiller checks the fulfiller template pref, employee checks
|
|
29
|
+
// the requestor + specialized-employee template prefs. Both paths also require
|
|
30
|
+
// the shared Agentforce Studio pref and the ITSM parent umbrella.
|
|
31
|
+
//
|
|
32
|
+
// Emits a single JSON object to stdout:
|
|
33
|
+
// { agentType, readState,
|
|
34
|
+
// features: { <apiName>: { status, signal, enableBlockedReasons, enableable, enableRoute } },
|
|
35
|
+
// verdict, notEnabled: [<apiName>...], enableable: [<apiName>...], reasons: [...], rawError }
|
|
36
|
+
// where each signal is PASS | FAIL | CANNOT-CONFIRM | ERROR and verdict is
|
|
37
|
+
// READY | NOT-READY | CANNOT-CONFIRM | ERROR. Exit code is always 0 on a usable
|
|
38
|
+
// agentType; the verdict is carried in the payload, not the exit status.
|
|
39
|
+
|
|
40
|
+
import { readFileSync } from 'node:fs';
|
|
41
|
+
|
|
42
|
+
// Required feature toggles per agent path. The apiNames are the real Salesforce
|
|
43
|
+
// Go featureApiNames behind the "Agentforce for IT Service" page toggles.
|
|
44
|
+
const STUDIO = 'sales-cloud-agent-studio'; // "Turn on Agentforce Studio"
|
|
45
|
+
const PARENT = 'service-cloud-agentforce-for-itsm'; // "Agentforce for IT Service" umbrella
|
|
46
|
+
const FULFILLER = 'service-cloud-it-fulfiller-agent'; // "IT Service Fulfiller"
|
|
47
|
+
const REQUESTOR = 'service-cloud-requestor-agent'; // "IT Service Employee"
|
|
48
|
+
const SPECIALIZED = 'service-cloud-it-service-employee-agent'; // "Specialized Agent Templates for Employee"
|
|
49
|
+
|
|
50
|
+
const REQUIRED = {
|
|
51
|
+
fulfiller: [STUDIO, PARENT, FULFILLER],
|
|
52
|
+
employee: [STUDIO, PARENT, REQUESTOR, SPECIALIZED],
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const LABEL = {
|
|
56
|
+
[STUDIO]: 'Agentforce Studio',
|
|
57
|
+
[PARENT]: 'Agentforce for IT Service (parent)',
|
|
58
|
+
[FULFILLER]: 'IT Service Fulfiller template',
|
|
59
|
+
[REQUESTOR]: 'IT Service Employee template',
|
|
60
|
+
[SPECIALIZED]: 'Specialized Agent Templates for Employee',
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
function enableRoute(apiName) {
|
|
64
|
+
return `POST /connect/setup/discovery/feature/${apiName}/enable`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function readJson(path) {
|
|
68
|
+
// { ok:true, data } on a parseable body; { ok:false, empty:true } when the file
|
|
69
|
+
// is missing/empty; { ok:false, empty:false } when it holds non-JSON garbage.
|
|
70
|
+
try {
|
|
71
|
+
const text = readFileSync(path, 'utf8').trim();
|
|
72
|
+
if (!text) return { ok: false, empty: true };
|
|
73
|
+
return { ok: true, data: JSON.parse(text) };
|
|
74
|
+
} catch {
|
|
75
|
+
return { ok: false, empty: false };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function asErrorArray(data) {
|
|
80
|
+
// sf api request rest surfaces Connect API errors as an array of
|
|
81
|
+
// {errorCode,message} (or a single such object). Returns [] if not an error body.
|
|
82
|
+
if (Array.isArray(data)) return data.filter((e) => e && (e.errorCode || e.message));
|
|
83
|
+
if (data && (data.errorCode || data.message)) return [data];
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isNotFoundError(errs) {
|
|
88
|
+
// A 404 / NOT_FOUND / gate-not-wired is the ONLY error shape that maps to
|
|
89
|
+
// "cannot confirm". Auth (401), permission (403), and everything else = ERROR.
|
|
90
|
+
return errs.some((e) => {
|
|
91
|
+
const code = String(e?.errorCode ?? '').toUpperCase();
|
|
92
|
+
const msg = String(e?.message ?? '').toUpperCase();
|
|
93
|
+
return code.includes('NOT_FOUND') || code.includes('NOT_EXIST') ||
|
|
94
|
+
msg.includes('NOT FOUND') || msg.includes('DOES NOT EXIST') ||
|
|
95
|
+
msg.includes('COULD NOT BE FOUND');
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function errorSnippet(errs) {
|
|
100
|
+
return errs.map((e) => `${e.errorCode ?? 'ERROR'}: ${e.message ?? ''}`.trim()).join(' | ').slice(0, 500);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Returns { readState, statusMap, rawError }:
|
|
104
|
+
// readState 'ok' → statusMap has per-apiName { status, enableBlockedReasons }
|
|
105
|
+
// readState 'not-wired' → a confirmed 404 (feature-discovery surface not wired)
|
|
106
|
+
// readState 'error' → empty / auth / permission / transport / unexpected body
|
|
107
|
+
function readFeatureStatus(res, exitStatus) {
|
|
108
|
+
if (!res.ok) {
|
|
109
|
+
// Empty or non-JSON body. Never a confirmed 404 → ERROR (surface it, stop).
|
|
110
|
+
const why = res.empty
|
|
111
|
+
? (Number.isFinite(exitStatus) && exitStatus !== 0
|
|
112
|
+
? `The read command exited ${exitStatus} with an empty body (auth / permission / transport failure).`
|
|
113
|
+
: 'The read produced an empty body — the response could not be read.')
|
|
114
|
+
: 'The read produced a non-JSON body that could not be parsed.';
|
|
115
|
+
return { readState: 'error', statusMap: null, rawError: why };
|
|
116
|
+
}
|
|
117
|
+
const d = res.data;
|
|
118
|
+
const errs = asErrorArray(d);
|
|
119
|
+
if (errs.length) {
|
|
120
|
+
if (isNotFoundError(errs)) return { readState: 'not-wired', statusMap: null, rawError: null };
|
|
121
|
+
return { readState: 'error', statusMap: null, rawError: errorSnippet(errs) };
|
|
122
|
+
}
|
|
123
|
+
const items = Array.isArray(d?.items) ? d.items : null;
|
|
124
|
+
if (!items) {
|
|
125
|
+
return { readState: 'error', statusMap: null, rawError: 'The response had no "items" array (unexpected shape).' };
|
|
126
|
+
}
|
|
127
|
+
const map = new Map();
|
|
128
|
+
for (const it of items) {
|
|
129
|
+
if (it?.apiName) {
|
|
130
|
+
map.set(it.apiName, {
|
|
131
|
+
status: String(it.status ?? '').toUpperCase(),
|
|
132
|
+
enableBlockedReasons: Array.isArray(it.enableBlockedReasons) ? it.enableBlockedReasons : [],
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { readState: 'ok', statusMap: map, rawError: null };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function classifyFeature(apiName, statusMap) {
|
|
140
|
+
const label = LABEL[apiName] ?? apiName;
|
|
141
|
+
if (!statusMap || !statusMap.has(apiName)) {
|
|
142
|
+
return {
|
|
143
|
+
status: 'UNKNOWN', signal: 'CANNOT-CONFIRM', enableBlockedReasons: [], enableable: false, enableRoute: null,
|
|
144
|
+
reason: `${label} (${apiName}) — no status returned (endpoint not wired / not in response).`,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const { status, enableBlockedReasons } = statusMap.get(apiName);
|
|
148
|
+
if (status === 'ENABLED') {
|
|
149
|
+
return { status, signal: 'PASS', enableBlockedReasons: [], enableable: false, enableRoute: null, reason: `${label} (${apiName}) → ENABLED.` };
|
|
150
|
+
}
|
|
151
|
+
if (status === 'NOT_ENABLED') {
|
|
152
|
+
const blocked = enableBlockedReasons.length > 0;
|
|
153
|
+
return {
|
|
154
|
+
status, signal: 'FAIL', enableBlockedReasons,
|
|
155
|
+
enableable: !blocked,
|
|
156
|
+
enableRoute: blocked ? null : enableRoute(apiName),
|
|
157
|
+
reason: blocked
|
|
158
|
+
? `${label} (${apiName}) → NOT_ENABLED, but blocked: ${enableBlockedReasons.map((r) => (typeof r === 'string' ? r : (r?.message ?? JSON.stringify(r)))).join('; ')}.`
|
|
159
|
+
: `${label} (${apiName}) → NOT_ENABLED (enableable).`,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
status, signal: 'CANNOT-CONFIRM', enableBlockedReasons: [], enableable: false, enableRoute: null,
|
|
164
|
+
reason: `${label} (${apiName}) → unexpected status "${status}".`,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const [statusPath, rawAgentType, rawExit] = process.argv.slice(2);
|
|
169
|
+
const agentType = String(rawAgentType ?? '').toLowerCase();
|
|
170
|
+
if (!statusPath || !REQUIRED[agentType]) {
|
|
171
|
+
process.stderr.write('usage: node classify-readiness.mjs <features-status.json> <fulfiller|employee> [exitStatus]\n');
|
|
172
|
+
process.exit(2);
|
|
173
|
+
}
|
|
174
|
+
const exitStatus = rawExit === undefined ? NaN : Number(rawExit);
|
|
175
|
+
|
|
176
|
+
const { readState, statusMap, rawError } = readFeatureStatus(readJson(statusPath), exitStatus);
|
|
177
|
+
|
|
178
|
+
const features = {};
|
|
179
|
+
const reasons = [];
|
|
180
|
+
const notEnabled = [];
|
|
181
|
+
const enableable = [];
|
|
182
|
+
|
|
183
|
+
if (readState === 'error') {
|
|
184
|
+
// Hard read failure (auth / permission / transport / unexpected). Do NOT
|
|
185
|
+
// pretend the gate is merely un-wired — surface it and stop.
|
|
186
|
+
for (const apiName of REQUIRED[agentType]) {
|
|
187
|
+
features[apiName] = { status: 'UNKNOWN', signal: 'ERROR', enableBlockedReasons: [], enableable: false, enableRoute: null };
|
|
188
|
+
}
|
|
189
|
+
reasons.push(`Feature status could not be read: ${rawError} Surface the raw response and stop — do not treat as a wiring gap.`);
|
|
190
|
+
process.stdout.write(JSON.stringify({
|
|
191
|
+
agentType, readState, features, verdict: 'ERROR', notEnabled, enableable, reasons, rawError,
|
|
192
|
+
}, null, 2) + '\n');
|
|
193
|
+
process.exit(0);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const signals = [];
|
|
197
|
+
for (const apiName of REQUIRED[agentType]) {
|
|
198
|
+
const c = classifyFeature(apiName, statusMap);
|
|
199
|
+
features[apiName] = {
|
|
200
|
+
status: c.status, signal: c.signal,
|
|
201
|
+
enableBlockedReasons: c.enableBlockedReasons, enableable: c.enableable, enableRoute: c.enableRoute,
|
|
202
|
+
};
|
|
203
|
+
reasons.push(c.reason);
|
|
204
|
+
signals.push(c.signal);
|
|
205
|
+
if (c.signal === 'FAIL') notEnabled.push(apiName);
|
|
206
|
+
if (c.enableable) enableable.push(apiName);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let verdict;
|
|
210
|
+
if (signals.includes('FAIL')) verdict = 'NOT-READY';
|
|
211
|
+
else if (signals.every((s) => s === 'PASS')) verdict = 'READY';
|
|
212
|
+
else verdict = 'CANNOT-CONFIRM';
|
|
213
|
+
|
|
214
|
+
process.stdout.write(JSON.stringify({
|
|
215
|
+
agentType,
|
|
216
|
+
readState,
|
|
217
|
+
features,
|
|
218
|
+
verdict,
|
|
219
|
+
notEnabled,
|
|
220
|
+
enableable,
|
|
221
|
+
reasons,
|
|
222
|
+
rawError,
|
|
223
|
+
}, null, 2) + '\n');
|