@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.
Files changed (136) hide show
  1. package/package.json +1 -1
  2. package/skills/automation-flow-generate/SKILL.md +32 -43
  3. package/skills/experience-portal-create/SKILL.md +497 -0
  4. package/skills/experience-portal-create/assets/report-template.md +30 -0
  5. package/skills/experience-portal-create/references/mcp-invocation.md +288 -0
  6. package/skills/experience-portal-create/references/post-creation-activate-publish.md +165 -0
  7. package/skills/experience-portal-create/references/templates.md +253 -0
  8. package/skills/experience-ui-bundle-features-generate/SKILL.md +5 -1
  9. package/skills/experience-ui-bundle-frontend-generate/SKILL.md +2 -0
  10. package/skills/experience-ui-bundle-frontend-generate/references/page.md +1 -0
  11. package/skills/platform-datamask-run/SKILL.md +345 -0
  12. package/skills/platform-datamask-run/references/api-surface.md +130 -0
  13. package/skills/platform-datamask-run/references/policy-authoring.md +185 -0
  14. package/skills/platform-datamask-run/references/run-and-abort.md +116 -0
  15. package/skills/platform-datamask-run/scripts/poll-job.sh +115 -0
  16. package/skills/platform-dataspace-access-configure/SKILL.md +51 -3
  17. package/skills/platform-dataspace-access-configure/scripts/inspect-dataspace-scopes.sh +56 -0
  18. package/skills/platform-lightning-type-widget-coordinate/references/build-plan-format.md +1 -0
  19. package/skills/platform-sandbox-configure/SKILL.md +17 -2
  20. package/skills/platform-trial-org-create/SKILL.md +175 -0
  21. package/skills/platform-trial-org-create/examples/create_request.json +9 -0
  22. package/skills/platform-trial-org-create/examples/error_response.json +41 -0
  23. package/skills/platform-trial-org-create/examples/success_response.json +27 -0
  24. package/skills/platform-trial-org-create/references/error_codes.md +42 -0
  25. package/skills/platform-trial-org-create/references/signup_request_fields.md +69 -0
  26. package/skills/platform-trial-org-create/scripts/create_signup_request.sh +175 -0
  27. package/skills/platform-trial-org-create/scripts/get_signup_request.sh +155 -0
  28. package/skills/platform-widget-generate/SKILL.md +47 -6
  29. package/skills/platform-widget-generate/examples/conditional.json +3 -3
  30. package/skills/platform-widget-generate/examples/list-with-foreach.json +2 -2
  31. package/skills/platform-widget-generate/examples/single-object.json +2 -2
  32. package/skills/platform-widget-generate/references/widget-bundle-layout.md +1 -1
  33. package/skills/service-agentforce-channel-configure/SKILL.md +271 -0
  34. package/skills/service-agentforce-channel-configure/references/agent-wiring.md +97 -0
  35. package/skills/service-agentforce-channel-configure/references/channel-branch-email.md +145 -0
  36. package/skills/service-agentforce-channel-configure/references/channel-branch-voice.md +69 -0
  37. package/skills/service-agentforce-channel-configure/references/channel-types.md +61 -0
  38. package/skills/service-agentforce-channel-configure/references/live-traffic-gate.md +86 -0
  39. package/skills/service-agentforce-channel-configure/references/queue-resolution.md +135 -0
  40. package/skills/service-agentforce-channel-configure/references/routing-flow.md +384 -0
  41. package/skills/service-catalog-template-deploy/SKILL.md +310 -0
  42. package/skills/service-catalog-template-deploy/references/cli-invocation.md +258 -0
  43. package/skills/service-catalog-template-deploy/scripts/activate-verify.mjs +164 -0
  44. package/skills/service-catalog-template-deploy/scripts/build-deploy-payload.mjs +94 -0
  45. package/skills/service-catalog-template-deploy/scripts/resolve-template.mjs +331 -0
  46. package/skills/service-catalog-template-search/SKILL.md +212 -0
  47. package/skills/service-catalog-template-search/references/cli-invocation.md +128 -0
  48. package/skills/service-catalog-template-search/scripts/classify-catalog.mjs +205 -0
  49. package/skills/service-concierge-portal-generate/SKILL.md +126 -0
  50. package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +1428 -0
  51. package/skills/service-digital-engagement-channel-configure/SKILL.md +46 -6
  52. package/skills/service-digital-engagement-channel-configure/assets/messaging_channel_template.xml +2 -1
  53. package/skills/service-digital-engagement-channel-configure/examples/asa_agent_channel.xml +4 -1
  54. package/skills/service-helpagent-coordinate/README.md +8 -2
  55. package/skills/service-helpagent-coordinate/SKILL.md +126 -130
  56. package/skills/service-helpagent-coordinate/assets/help-agent-spec.md +70 -53
  57. package/skills/service-helpagent-coordinate/references/agent-script.md +571 -457
  58. package/skills/service-helpagent-coordinate/references/channel-voice.md +38 -9
  59. package/skills/service-helpagent-coordinate/references/channel-web-chat.md +173 -49
  60. package/skills/service-helpagent-coordinate/references/output-report-format.md +126 -0
  61. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +153 -0
  62. package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +79 -0
  63. package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +35 -0
  64. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/SKILL.md +271 -0
  65. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/references/cli-invocation.md +265 -0
  66. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/classify-enable-plan.mjs +220 -0
  67. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/classify-final-report.mjs +102 -0
  68. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/record-enable-result.mjs +73 -0
  69. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/SKILL.md +206 -0
  70. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/references/cli-invocation.md +194 -0
  71. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/scripts/classify-readiness.mjs +223 -0
  72. package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +45 -7
  73. package/skills/service-itsm-agentic-setup-cmdb-configure/references/mcp-invocation.md +45 -5
  74. package/skills/service-itsm-agentic-setup-configure/SKILL.md +116 -0
  75. package/skills/service-itsm-agentic-setup-configure/examples/output-templates.md +64 -0
  76. package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +158 -0
  77. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/cli-invocation.md +361 -0
  78. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/error-taxonomy.md +44 -0
  79. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/reactivation.md +66 -0
  80. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +66 -0
  81. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/specialized-templates.md +148 -0
  82. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/workflow-detail.md +169 -0
  83. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/build-create-body.mjs +116 -0
  84. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/classify-agent-existence.mjs +185 -0
  85. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/classify-preflight.mjs +168 -0
  86. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/create-scratch-dir.mjs +58 -0
  87. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +197 -0
  88. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +186 -0
  89. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/action-availability.md +51 -0
  90. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/cli-invocation.md +345 -0
  91. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/error-taxonomy.md +44 -0
  92. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/reactivation.md +63 -0
  93. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +44 -0
  94. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md +149 -0
  95. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/build-create-body.mjs +110 -0
  96. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-action-availability.mjs +201 -0
  97. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-activate-result.mjs +135 -0
  98. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-agent-existence.mjs +194 -0
  99. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-preflight.mjs +158 -0
  100. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/create-scratch-dir.mjs +58 -0
  101. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +191 -0
  102. package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +133 -0
  103. package/skills/service-itsm-agentic-setup-incident-management/examples/output-templates.md +71 -0
  104. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +308 -0
  105. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +23 -0
  106. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +193 -0
  107. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +57 -0
  108. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +394 -0
  109. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/SKILL.md +266 -0
  110. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/cli-invocation.md +106 -0
  111. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/helper-contracts.md +142 -0
  112. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/permset-topology.md +82 -0
  113. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-action-surface.mjs +137 -0
  114. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-assignment-state.mjs +99 -0
  115. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-permset-availability.mjs +120 -0
  116. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/resolve-target-user.mjs +86 -0
  117. package/skills/service-itsm-agentic-setup-uel-user-create/SKILL.md +284 -0
  118. package/skills/service-itsm-agentic-setup-uel-user-create/references/mcp-invocation.md +302 -0
  119. package/skills/service-itsm-channels-coordinate/SKILL.md +472 -0
  120. package/skills/service-itsm-incident-mgmt-configure/SKILL.md +212 -0
  121. package/skills/service-itsm-incident-mgmt-configure/references/mcp-invocation.md +225 -0
  122. package/skills/service-itsm-incident-priority-configure/SKILL.md +53 -12
  123. package/skills/service-itsm-swarming-configure/SKILL.md +212 -0
  124. package/skills/service-itsm-teams-configure/SKILL.md +395 -0
  125. package/skills/service-itsm-teams-configure/references/azure-credential-population.md +213 -0
  126. package/skills/service-itsm-teams-configure/references/gotchas.md +23 -0
  127. package/skills/service-itsm-teams-coordinate/SKILL.md +175 -0
  128. package/skills/service-itsm-teams-coordinate/examples/output-templates.md +85 -0
  129. package/skills/service-itsm-teams-debug/SKILL.md +144 -0
  130. package/skills/service-itsm-teams-debug/references/configuration-checklists.md +277 -0
  131. package/skills/service-itsm-teams-debug/references/report-generation.md +95 -0
  132. package/skills/service-itsm-teams-employee-agent-configure/SKILL.md +139 -0
  133. package/skills/service-itsm-teams-employee-agent-configure/assets/Teams_AgentForce.EmbeddedServiceConfig-meta.xml +43 -0
  134. package/skills/service-itsm-teams-employee-agent-configure/references/teams-embedded-employee-agent.md +480 -0
  135. package/skills/service-itsm-teams-itdesk-configure/SKILL.md +232 -0
  136. package/skills/service-itsm-teams-itservice-configure/SKILL.md +391 -0
@@ -0,0 +1,51 @@
1
+ # Action-availability preflight + activate-result classification
2
+
3
+ The Fulfiller template's Agent Script references `svc_itsm_intelligence__*` invocable actions via `source:` and `target: generatePromptResponse://...`. `activate` returns **HTTP 200** with `{success:false, messages:[{... "does not exist"}]}` (silent-failure body) when any referenced action isn't surfaced for the running user by `/actions/custom/generatePromptResponse`. Two helpers catch this — one before the write, one after.
4
+
5
+ ## Phase 2c — `scripts/classify-action-availability.mjs`
6
+
7
+ Called on the **create path only** — after Phase 2 (idempotency) returns `exists:false`, and before Phase 3 (confirm-to-write). An already-existing **Active** agent (ALREADY-CREATED) skips this gate entirely: its actions are already wired, so re-checking availability would wrongly park a no-op run. A reactivation (Phase 2b) relies on the activate-result classifier below rather than this preflight. Reads:
8
+ - `/tmp/agent-templates.json` (Phase-1 capture — the decoded template `agentScript` is the source of truth for the referenced actions).
9
+ - `/tmp/generate-prompt-response.json` — a fresh GET of `/services/data/v67.0/actions/custom/generatePromptResponse` (the two response shapes are `{actions:[{name,...}]}` and `{actions:{<name>:{...}}}`; the classifier handles both).
10
+
11
+ Invocation:
12
+
13
+ ```bash
14
+ sf api request rest "/services/data/v67.0/actions/custom/generatePromptResponse" \
15
+ --method GET --target-org <alias> > /tmp/generate-prompt-response.json 2>/tmp/generate-prompt-response.err || true
16
+ node "<skill_dir>/scripts/classify-action-availability.mjs" \
17
+ /tmp/agent-templates.json "IT Service Fulfiller" /tmp/generate-prompt-response.json
18
+ ```
19
+
20
+ Emits `{ referenced, present, missing, verdict, reasons }`. Branching:
21
+
22
+ | verdict | Meaning | Caller action |
23
+ |---|---|---|
24
+ | `READY` | `missing.length === 0` | Continue to Phase 3 (confirm-to-write) |
25
+ | `NOT-READY` | ≥1 referenced action absent from `generatePromptResponse` | Offer permset hand-off (below) — no writes |
26
+ | `CANNOT-CONFIRM` | `generatePromptResponse` returned an unexpected shape | Surface reasons; proceed with caution — Phase 6 activate-result will catch a silent-failure body |
27
+
28
+ **Hand-off wording on `NOT-READY`** (via `AskUserQuestion`):
29
+
30
+ > "`missing.length` invocable action(s) referenced by the Fulfiller template are not available on this org for your user (e.g. `<first 3 of missing>`). Run `service-itsm-agentic-setup-itsm-agentforce-permset-assign` to assign the ITSM Intelligence permission set (if installed) or to route you to the content-bundle step (if the package isn't installed)?" (options: **Yes, resolve action availability** / **No, stop here**)
31
+
32
+ - On **Yes**: delegate to `service-itsm-agentic-setup-itsm-agentforce-permset-assign`, then re-run Phase 2c.
33
+ - On **No**: stop and report missing actions verbatim — no writes.
34
+
35
+ ## Phase 2b / Phase 6 — `scripts/classify-activate-result.mjs`
36
+
37
+ Called on both the create-path activate (`POST /nextgen-authoring/bundle-versions/<id>/activate`) and the reactivation activate (`POST /connect/bot-versions/<id>/activation`). Reads the response body captured to a file. Emits `{ verdict, success, isActivated, messages, reasons }`.
38
+
39
+ | Body shape | verdict | Handling |
40
+ |---|---|---|
41
+ | empty stdout | `PASS` | Documented success — fall through to Phase 7 verify |
42
+ | `{"success":true, ...}` | `PASS` | Fall through to Phase 7 verify |
43
+ | `{"success":false, "messages":[...]}` | `FAIL` | Do NOT report CREATED — surface `messages[]` verbatim; if a message names a missing invocable action, offer the same Phase-2c hand-off (`service-itsm-agentic-setup-itsm-agentforce-permset-assign`) |
44
+ | Connect error array `[{errorCode, message}]` | `FAIL` | Surface verbatim and stop |
45
+ | Unparseable body | `CANNOT-CONFIRM` | Fall through to Phase 7 SOQL verify — let it decide |
46
+
47
+ The classifier's "does the message name a missing invocable action" test matches `/does not exist|not found|no such action|invocable action/i`.
48
+
49
+ ## Why both phases exist
50
+
51
+ Phase 2c catches the failure **before** create+publish+activate — expensive to unwind after the bundle exists. Phase 6/2b is the belt-and-braces: on rare org state changes between Phase 2c and the activate call (permset revoked mid-flow), the activate body is the only surviving signal that Phase 2c's verdict is stale. Never trust HTTP 200 alone on activate.
@@ -0,0 +1,345 @@
1
+ # CLI invocation reference — Create the IT Service Fulfiller Agent
2
+
3
+ This skill uses the **Salesforce CLI (`sf`)** as its only transport:
4
+ `sf api request rest` for the NGA Connect API reads/writes, `sf data query` for the
5
+ `BotDefinition` idempotency + verify SOQL, and two Node helper scripts for the
6
+ deterministic decisions. It extracts no access tokens.
7
+
8
+ The agent is created as a **Next-Gen Authoring (NGA) native agent** via the
9
+ `/nextgen-authoring/*` Connect API — **not** via the legacy
10
+ `/connect/service-itsm/createAgent` route. That legacy route creates a
11
+ Setup-page bot that shows an external-link icon in Agentforce Studio and is
12
+ also listed on the old Setup > Agentforce Agents page; the NGA bundle pipeline
13
+ below produces an agent that is native to Agentforce Studio's Agents list with
14
+ no external-link icon, matching the platform's own "IT Service Employee Agent".
15
+
16
+ ## Why `sf api request rest` / `sf data query`, never curl + token
17
+
18
+ Both commands authenticate using the CLI's stored session for the
19
+ `--target-org` alias — the CLI mints/refreshes the token internally and never
20
+ exposes it. **Do not** do:
21
+
22
+ <!-- skill-validate: ignore -->
23
+ ```bash
24
+ # FORBIDDEN — leaks a bearer token into shell context, bypasses the CLI session
25
+ TOKEN=$(sf org display --json -o <alias> | jq -r '.result.accessToken')
26
+ curl -X POST -H "Authorization: Bearer $TOKEN" .../nextgen-authoring/bundles
27
+ ```
28
+
29
+ Every call this skill makes is a plain `/services/data/v67.0/...` Connect API or
30
+ `/query` path — exactly what `sf api request rest` and `sf data query` proxy.
31
+ There is no reason to fall back to curl.
32
+
33
+ ## Target org, API version, and the `--json` split
34
+
35
+ - **Target org**: always `--target-org <alias>` (or `-o <alias>`). Resolve the
36
+ alias from the user or the default org (`sf config get target-org`).
37
+ - **API version**: pinned in the URL path (`/services/data/v67.0/...`). Do not
38
+ hand-edit it below `metadata.minApiVersion` (`67.0`).
39
+ - **`--json` rule** — the two commands differ:
40
+ - `sf api request rest` prints the **raw** Connect response body to stdout;
41
+ do **not** add `--json` (it is unsupported on some Connect endpoints and
42
+ errors). The stdout body is already JSON.
43
+ - `sf data query --json` **does** wrap results in a `{status, result:{records[]}}`
44
+ envelope — that envelope is exactly what `scripts/classify-agent-existence.mjs`
45
+ expects. Always pass `--json` to `sf data query`.
46
+
47
+ ## Thread the collected developerName / label through the create body
48
+
49
+ The `<developerName>` and `<label>` are collected from the user (defaults
50
+ `IT_Service_Fulfiller_Agent` / `IT Service Fulfiller Agent`). The **same**
51
+ `<developerName>` must appear in the `createBundleWithVersion` body's outer
52
+ `apiName` AND its `resourceContent`'s internal `config.developer_name` —
53
+ otherwise the bundle's outer identity diverges from the script's internal
54
+ identity. The idempotency and verify reads do **not** key on `<developerName>`:
55
+ they query `BotDefinition` by the template's `botDefinitionId` (idempotency) or
56
+ the publish response's `publishedBotId` (verify) — see the Enumerate/Verify
57
+ sections below for why an Id-keyed read is the only reliable guard.
58
+
59
+ ## The source content — the legacy template's `agentScript` field
60
+
61
+ The legacy `agent-templates` read (still used, read-only) returns each
62
+ template's full **Agent Script** (AFScript) in an `agentScript` field,
63
+ HTML-entity-encoded (sometimes double-encoded). This is the SAME content
64
+ format an NGA bundle version's `resourceContent` expects — there is no
65
+ separate "NGA template catalog" endpoint; the fix simply routes this existing
66
+ read's content into the NGA bundle pipeline instead of the legacy
67
+ `createAgent` call.
68
+
69
+ ```text
70
+ GET /services/data/v67.0/connect/service-itsm/agent-templates?agentType=AgentforceEmployeeAgent
71
+ ```
72
+
73
+ Response: `{data:[{id, masterLabel, agentScript, isInstalled, isActivated, botDefinitionId, ...}]}`.
74
+ The classifier (`scripts/classify-preflight.mjs`) finds the item whose
75
+ `masterLabel` matches `"IT Service Fulfiller"` and confirms `agentScript` is a
76
+ non-empty string. `scripts/build-create-body.mjs` re-reads the same captured
77
+ file, re-locates the match, and does the actual decode + substitution (see
78
+ below) — the classifier only confirms presence, it does not re-emit the ~70KB
79
+ script content on stdout.
80
+
81
+ ## The NGA Connect API
82
+
83
+ All three calls live under `/nextgen-authoring/`, owning team **Agentforce
84
+ Platform**. Org access check: `NextGenAuthoring.orgHasNextGenAgentAuthoringEnabled
85
+ && NextGenAuthoring.userCanAccessNextGenAgentAuthoring`. User access check:
86
+ `NextGenAuthoring.userCanAccessAuthoringBundle` (create) /
87
+ `NextGenAuthoring.userCanEditAuthoringBundle` (publish/activate).
88
+
89
+ | Route | Method | Purpose |
90
+ |-------|--------|---------|
91
+ | `/services/data/v67.0/nextgen-authoring/bundles` | POST | `createBundleWithVersion` — create the bundle + its first DRAFT version from an Agent Script |
92
+ | `/services/data/v67.0/nextgen-authoring/bundle-versions/{bundleVersionId}/publish` | POST | `publishBundleVersion` — publish the version, creating the underlying `BotDefinition`/`BotVersion` |
93
+ | `/services/data/v67.0/nextgen-authoring/bundle-versions/{bundleVersionId}/activate` | POST | `activateBundleVersion` — activate the published version |
94
+ | `/services/data/v67.0/nextgen-authoring/bundles` | GET | List bundles (optional post-hoc check for `isLegacy:false`) |
95
+
96
+ > **The legacy `createAgent` / `create-agents` / `activate-agents` routes under
97
+ > `/connect/service-itsm/` are NOT part of this flow.** They create a
98
+ > Setup-page bot with an external-link icon in Agentforce Studio — the wrong
99
+ > kind of agent. Do not fall back to them even on an NGA-route error; surface
100
+ > the error and stop instead.
101
+
102
+ ## Preflight — Studio access + template's Agent Script presence (one classifier)
103
+
104
+ Capture both reads, then let `scripts/classify-preflight.mjs` make the
105
+ deterministic decisions (do not read `hasAccess` or search `data[]` in prose — A9):
106
+
107
+ ```bash
108
+ sf api request rest "/services/data/v67.0/agentforce-studio/access/Agents" \
109
+ --method GET --target-org <alias> > ${SCRATCH_DIR}/studio-access.json 2>${SCRATCH_DIR}/studio-access.err || true
110
+ sf api request rest "/services/data/v67.0/connect/service-itsm/agent-templates?agentType=AgentforceEmployeeAgent" \
111
+ --method GET --target-org <alias> > ${SCRATCH_DIR}/agent-templates.json 2>${SCRATCH_DIR}/agent-templates.err || true
112
+ node "<skill_dir>/scripts/classify-preflight.mjs" ${SCRATCH_DIR}/studio-access.json ${SCRATCH_DIR}/agent-templates.json "IT Service Fulfiller"
113
+ ```
114
+
115
+ **Studio access** body: `{ "hasAccess": true, "productName": "Agents" }`. The
116
+ classifier maps `hasAccess=true`→PASS, `false`→FAIL (offer the hand-off to
117
+ `service-itsm-agentic-setup-agentforce-studio-validate`), a **confirmed** `404`
118
+ (gate not wired, expected on scratch orgs)→CANNOT-CONFIRM (does not block — a
119
+ successful create/publish/activate is authoritative), and any other parseable
120
+ error body (`401`/`403`/unexpected)→**ERROR** (surface the raw response and
121
+ stop — do not treat an auth/permission failure as an unwired gate). The
122
+ product in the path must be `Agents` (any other value ⇒ `400 Invalid product name`).
123
+
124
+ **agent-templates**: the `agentType` query param is **required** — omitting it
125
+ returns `400 MISSING_ARGUMENT: agentType`; the value is `AgentforceEmployeeAgent`
126
+ (a wrong value returns an empty `data[]`). The classifier finds the item whose
127
+ `masterLabel` matches the label arg ("IT Service Fulfiller") and confirms its
128
+ `agentScript` field is a non-empty string — that field, not `id`, is what
129
+ Phase 4 consumes. Empty/no-match `data[]` ⇒ `template.signal=FAIL`; a `404` ⇒
130
+ CANNOT-CONFIRM — hand off to the readiness check; a match with no/empty
131
+ `agentScript` ⇒ CANNOT-CONFIRM (nothing to build the NGA bundle from).
132
+
133
+ Classifier output:
134
+
135
+ ```json
136
+ {
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": "..." },
139
+ "verdict": "READY | NOT-READY | CANNOT-CONFIRM | ERROR",
140
+ "reasons": ["..."]
141
+ }
142
+ ```
143
+
144
+ `template.botDefinitionId` is 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 its template `botDefinitionId` is `null` on every run — the collected `<developerName>` is therefore the guard that actually protects repeat runs. Carry `<developerName>` into the Phase-2 read as the fallback 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
+
146
+ `verdict=ERROR` (`studio.signal="ERROR"` — a parseable non-404 Studio-access
147
+ error, e.g. `401`/`403`) ⇒ surface the raw error and stop; takes priority over
148
+ template state so a present template cannot outrun a failed prerequisite read.
149
+ `verdict=NOT-READY` (studio FAIL or template FAIL) ⇒ hand off / stop.
150
+ `verdict=READY` ⇒ proceed to Phase 2. It exits `0` on usable args.
151
+
152
+ ## Enumerate the existing agent — SOQL on `BotDefinition` BY Id (falling back to DeveloperName)
153
+
154
+ Idempotency is keyed **PRIMARILY** on the template's `botDefinitionId` (from the
155
+ Phase-1 row) and **FALLS BACK** to the collected `<developerName>`. If the
156
+ Fulfiller agent already exists under a `DeveloperName` that differs from this
157
+ skill's default guess `IT_Service_Fulfiller_Agent`, a name-only read
158
+ false-negatives (`exists:false`) and the create then collides on `apiName` with
159
+ `DUPLICATE_VALUE`. `botDefinitionId` is the platform's authoritative link from
160
+ the template to the `BotDefinition` it was instantiated into — but it is
161
+ back-filled onto the template row **only** for pre-provisioned agents, and the
162
+ Fulfiller is never pre-provisioned: this skill's create path never stamps
163
+ `templateName`, so its template `botDefinitionId` stays `null` on the first run
164
+ and every run after. The `DeveloperName`-keyed fallback is therefore the guard
165
+ that actually protects repeat runs here — never short-circuit straight to create
166
+ when `botDefinitionId` is absent.
167
+
168
+ - **`botDefinitionId` empty/null** (the normal Fulfiller case — template row
169
+ never joined) ⇒ fall back to a DeveloperName-keyed read and let the classifier
170
+ match on it:
171
+ ```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>'" \
173
+ --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>"
175
+ ```
176
+ - **`botDefinitionId` present** (a pre-provisioned instantiation, if any — never
177
+ the normal Fulfiller case) ⇒ read the `BotDefinition` by Id **`OR` by the
178
+ collected `<developerName>`** in one query, then classify. The `OR DeveloperName=`
179
+ clause catches a **dangling** link — a `botDefinitionId` whose target row was
180
+ since deleted: the by-Id half returns nothing, the live same-name agent still
181
+ surfaces, and the classifier falls back to it (`matchedBy:"developerName"`)
182
+ rather than concluding `exists:false` and colliding with `DUPLICATE_VALUE`:
183
+ ```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>'" \
185
+ --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>"
187
+ ```
188
+
189
+ Only when **both** `botDefinitionId` and `<developerName>` are absent does the
190
+ classifier return `exists:false` without reading a query file — in practice the
191
+ collected `<developerName>` is always present, so the fallback read always runs.
192
+
193
+ The `BotVersions` subquery (child relationship on `BotDefinition`) is what lets
194
+ the classifier see the latest version's `Status` — omit it and
195
+ `latestVersionStatus`/`needsActivation` come back `null`/`false` even when the
196
+ existing agent is actually inactive. The classifier prints
197
+ `{ exists, count, matchedBy, agentId, botDefinitionId, developerName, latestVersionId, latestVersionStatus, needsActivation }`
198
+ (`matchedBy` is `"botDefinitionId"` | `"developerName"` | `null`; `developerName`
199
+ is the ACTUAL live agent's DeveloperName read from the record — surface it in the
200
+ report instead of the collected guess):
201
+ - `exists:false` ⇒ proceed to create.
202
+ - `exists:true` and `needsActivation:false` (latest version `Active`) ⇒
203
+ **ALREADY-CREATED** (skip the entire create/publish/activate sequence).
204
+ - `exists:true` and `needsActivation:true` (latest version `Inactive`) ⇒ do
205
+ **not** create a duplicate — take the reactivation path documented in
206
+ `references/reactivation.md` (direct `BotVersion` activation, skips
207
+ create + publish).
208
+ - **Exit 3** ⇒ the query itself failed (auth error, malformed SOQL) — surface
209
+ the raw CLI error and stop; do **not** assume the agent is absent.
210
+
211
+ Any existing-agent hit here is a `matchedBy:"developerName"` fallback (the
212
+ Fulfiller's template `botDefinitionId` is always `null`), so Phase 7 verifies the
213
+ agent using the classifier's returned live `agentId` / `botDefinitionId` — the
214
+ actual matched `BotDefinition.Id` — **never** the Phase-1 template
215
+ `botDefinitionId`, so the verify read never degrades to `WHERE Id=''` and never
216
+ false-fails after a successful skip or reactivation.
217
+
218
+ Use the skill's **absolute** `<skill_dir>` in the `node` invocation — a bare
219
+ `./scripts/...` resolves against the shell CWD, not the skill dir.
220
+
221
+ ## Create the NGA bundle — `createBundleWithVersion`
222
+
223
+ ```bash
224
+ node "<skill_dir>/scripts/build-create-body.mjs" ${SCRATCH_DIR}/agent-templates.json "IT Service Fulfiller" "<developerName>" "<label>" ${SCRATCH_DIR}/create-bundle-body.json
225
+ sf api request rest "/services/data/v67.0/nextgen-authoring/bundles" \
226
+ --method POST \
227
+ --body @${SCRATCH_DIR}/create-bundle-body.json \
228
+ --target-org <alias> > ${SCRATCH_DIR}/create-bundle.json 2>${SCRATCH_DIR}/create-bundle.err || true
229
+ ```
230
+
231
+ `build-create-body.mjs` re-reads `${SCRATCH_DIR}/agent-templates.json` (the SAME file
232
+ captured in Phase 1), re-locates the item whose `masterLabel` matches
233
+ `"IT Service Fulfiller"`, and:
234
+ 1. Fully HTML-decodes its `agentScript` — named entities (`&amp;`, `&quot;`,
235
+ `&#39;`, `&lt;`, `&gt;`) AND numeric entities (`&#(\d+);` →
236
+ `String.fromCharCode`), applied in a loop (up to 4 passes) to fully unwind
237
+ double-encoding. A naive single-pass decode leaves artifacts (e.g.
238
+ `&amp;quot;`, an undecoded `&#92;` for a literal backslash) that break
239
+ AFScript parsing.
240
+ 2. Substitutes the script's `config.developer_name` / `config.agent_label`
241
+ with the collected `<developerName>` / `<label>` via a regex replace on the
242
+ `developer_name: "..."` / `agent_label: "..."` lines. If either
243
+ substitution doesn't land (pattern not found), the script exits 3 rather
244
+ than silently building a body with the wrong internal identity.
245
+ 3. Writes `{ apiName: <developerName>, label: <label>, assets: [{ resourceName:
246
+ "agentDefinition", resourceType: "agentDefinition", sections: [],
247
+ resourceContent: <decoded+substituted script> }] }` to the output path.
248
+
249
+ Response on success — a bundle-version detail:
250
+
251
+ ```json
252
+ {
253
+ "apiName": "IT_Service_Fulfiller_Agent",
254
+ "label": "IT Service Fulfiller Agent",
255
+ "bundleId": "1bY...",
256
+ "id": "1bZ...",
257
+ "versionStatus": "DRAFT",
258
+ "assets": [ { "resourceName": "agentDefinition", "resourceType": "agentDefinition", "resourceContent": "...", "sections": [] } ],
259
+ "publishedBotId": null,
260
+ "publishedBotVersionId": null
261
+ }
262
+ ```
263
+
264
+ **Capture `id` — that is the `bundleVersionId`** used in the publish/activate
265
+ calls below. `bundleId` is the bundle's own Id, not the version's; passing it
266
+ to `/bundle-versions/{...}` returns a 404.
267
+
268
+ Common errors:
269
+
270
+ | HTTP | Error code | Meaning | Action |
271
+ |------|-----------|---------|--------|
272
+ | 403 / 404 | `FUNCTIONALITY_NOT_ENABLED` / not found | NGA authoring namespace not provisioned on the org | Trigger the Phase-1 hand-off — `AskUserQuestion` → on Yes delegate to `service-itsm-agentic-setup-agentforce-studio-validate` |
273
+ | (script exit 3) | — | `build-create-body.mjs` couldn't find the template / its `agentScript` / couldn't substitute developer_name or agent_label in `${SCRATCH_DIR}/agent-templates.json` | Surface the script's stderr verbatim; re-verify Phase 1's `template.hasAgentScript:true` rather than hand-typing a body |
274
+
275
+ ## Publish the bundle version — `publishBundleVersion`
276
+
277
+ ```bash
278
+ sf api request rest "/services/data/v67.0/nextgen-authoring/bundle-versions/<bundleVersionId>/publish" \
279
+ --method POST --body '{}' \
280
+ --target-org <alias> > ${SCRATCH_DIR}/publish-bundle.json 2>${SCRATCH_DIR}/publish-bundle.err || true
281
+ ```
282
+
283
+ Path param only — the body is ignored by the endpoint but `--body '{}'` is
284
+ still required (see Gotchas). Response on success:
285
+
286
+ ```json
287
+ { "lastPublishedOn": "2026-08-05T01:22:54.278Z", "publishedBotId": "0Xx...", "publishedBotVersionId": "0X9..." }
288
+ ```
289
+
290
+ This is the call that creates the underlying `BotDefinition`/`BotVersion`. An
291
+ error here means the DRAFT version failed platform-side validation.
292
+
293
+ ## Activate the bundle version — `activateBundleVersion`
294
+
295
+ ```bash
296
+ sf api request rest "/services/data/v67.0/nextgen-authoring/bundle-versions/<bundleVersionId>/activate" \
297
+ --method POST --body '{}' \
298
+ --target-org <alias> > ${SCRATCH_DIR}/activate-bundle.json 2>${SCRATCH_DIR}/activate-bundle.err || true
299
+ ```
300
+
301
+ Success returns an **empty response body** (`EmptyRepresentation`) — do not
302
+ treat empty stdout as a failure signal. Check the CLI exit code, then confirm
303
+ success via the Phase-7 `BotDefinition`/`BotVersion` verify read, not by
304
+ parsing this call's output.
305
+
306
+ ## Verify the agent is live — SOQL on `BotDefinition` BY Id
307
+
308
+ ```bash
309
+ sf data query -q "SELECT Id,DeveloperName,MasterLabel,(SELECT Id,Status FROM BotVersions ORDER BY VersionNumber DESC LIMIT 1) FROM BotDefinition WHERE Id='<verifyId>'" \
310
+ --target-org <alias> --json > ${SCRATCH_DIR}/bot-verify.json 2>${SCRATCH_DIR}/bot-verify.err || true
311
+ node "<skill_dir>/scripts/classify-agent-existence.mjs" ${SCRATCH_DIR}/bot-verify.json "<verifyId>"
312
+ ```
313
+
314
+ The verify `<verifyId>` is the create path's **`publishedBotId`** (captured from
315
+ the Phase-5 publish response) or, on the ALREADY-CREATED / reactivation path, the
316
+ **live matched Id the Phase-2 classifier returned** (its `botDefinitionId` /
317
+ `agentId` output — the actual `BotDefinition.Id` of the matched record) — **not**
318
+ the Phase-1 template `botDefinitionId`, which is always `null` for the Fulfiller,
319
+ so on any existing-agent hit (always a `matchedBy:"developerName"` fallback)
320
+ using it would run the verify as `WHERE Id=''` and falsely report failure after a
321
+ successful skip/activation. Never the collected `<developerName>`.
322
+ Confirm `exists:true` with `count:1`, and — whether the create or the
323
+ reactivation path was taken — `latestVersionStatus:"Active"`. `exists:false`
324
+ after a successful activate ⇒ report the discrepancy verbatim, do not fabricate
325
+ success. Optionally cross-check
326
+ `GET /services/data/v67.0/nextgen-authoring/bundles` for an entry with
327
+ `apiName=<developerName>` and `isLegacy:false` — this is the same shape as the
328
+ platform's own "IT Service Employee Agent" entry and confirms the created
329
+ agent is NGA-native (no external-link icon in Agentforce Studio).
330
+
331
+ ## Idempotency semantics
332
+
333
+ The full `ALREADY-CREATED` / `ACTIVATED` / `CREATED` verdict table (Phase-2
334
+ classifier signal → verdict) — plus the note on how the Phase-2 SOQL read turns
335
+ the server's `DeveloperName`-keyed duplicate rejection into a graceful skip —
336
+ lives in `references/reactivation.md`.
337
+
338
+ ## Errors and gotchas
339
+
340
+ The response-body error codes each phase can return (auth,
341
+ `FUNCTIONALITY_NOT_ENABLED`, missing `agentType`, build-script exit codes,
342
+ empty-response semantics) and the recurring foot-guns (legacy-`createAgent`,
343
+ `--body '{}'`, `bundleId` vs `bundleVersionId`, multi-pass HTML decode,
344
+ threading the collected `<developerName>`, token+curl, …) live in
345
+ `references/error-taxonomy.md`.
@@ -0,0 +1,44 @@
1
+ # Error taxonomy and gotchas — Create the IT Service Fulfiller Agent
2
+
3
+ Split from `references/cli-invocation.md` — that file documents the per-phase
4
+ CLI call shapes and the classifier contracts; this file collects the errors
5
+ those calls can return and the recurring foot-guns.
6
+
7
+ ## Error taxonomy
8
+
9
+ - **Auth error / `401 Unauthorized`** — the org session is expired or the alias
10
+ is wrong. Re-run `sf org login web` for the target org; there is no token to
11
+ refresh by hand.
12
+ - **`401`/`403`/unexpected error body on the Phase-1 Studio-access read** — the
13
+ preflight classifier maps this to `studio.signal="ERROR"` / `verdict="ERROR"`
14
+ (distinct from a confirmed 404 gate). Surface the raw response and stop —
15
+ do not proceed to Phase 2 even if the template read succeeded.
16
+ - **`403`/`404` on `createBundleWithVersion` / `publish` / `activate`** — the
17
+ NGA authoring namespace isn't provisioned or enabled on the org. Trigger the
18
+ Phase-1 hand-off offer.
19
+ - **`400 MISSING_ARGUMENT: agentType`** on `agent-templates` — the required
20
+ `agentType` query param was omitted; pass `AgentforceEmployeeAgent`.
21
+ - **Build-script exit 3** — the template, its `agentScript`, or a successful
22
+ `developer_name`/`agent_label` substitution wasn't found in the captured
23
+ `agent-templates.json`; surface stderr, don't hand-author a body.
24
+ - **Classifier exit 3** on a `sf data query` read — the SOQL query failed
25
+ (auth/malformed); surface the raw CLI error, do not treat as NOT-EXISTS.
26
+ - **Empty stdout from `activate`** — this is the SUCCESS response
27
+ (`EmptyRepresentation`), not a failure; confirm via the Phase-7 verify SOQL.
28
+
29
+ ## Gotchas
30
+
31
+ | Issue | Resolution |
32
+ |-------|------------|
33
+ | Legacy `createAgent` produces the wrong kind of agent | Never call `/connect/service-itsm/createAgent` — it creates a Setup-page bot with an external-link icon in Agentforce Studio. Use the three `/nextgen-authoring/*` calls in `references/cli-invocation.md` |
34
+ | `sf api request rest --method POST` with no `--body` flag | Fails with `No 'mode' found in 'body' entry`, even for `publish`/`activate` which ignore the body — always pass `--body '{}'` explicitly |
35
+ | `sf api request rest --json` errors | Don't pass `--json` there — the raw stdout body is already JSON. `--json` is for `sf data query` (which needs the `.result.records[]` envelope) |
36
+ | Confusing `bundleId` with `bundleVersionId` | The `createBundleWithVersion` response's `id` field is the bundle **version** Id used in publish/activate; `bundleId` is the parent bundle's Id and 404s if passed to `/bundle-versions/{...}` |
37
+ | Naive single-pass HTML-entity decode leaves compile errors | `&amp;quot;` (double-encoded) and `&#92;` (numeric, backslash) require a multi-pass decode covering both named and numeric entities — see `build-create-body.mjs` |
38
+ | Bundle's outer `apiName` diverging from the script's internal `config.developer_name` | Always substitute both via `build-create-body.mjs` before the create call — a divergence is exactly the kind of mismatch that produces confusing platform behavior |
39
+ | Hardcoding `IT_Service_Fulfiller_Agent` in one call but collecting it in another | Thread the collected `<developerName>` through the idempotency SOQL, the bundle body (outer + internal), AND the verify SOQL — a mismatch creates the wrong agent |
40
+ | Bare `./scripts/classify-agent-existence.mjs` "not found" | Use the skill's absolute `<skill_dir>` in the `node` invocation |
41
+ | `agent-templates` → `400 MISSING_ARGUMENT: agentType` | The `agentType` query param is required — pass `AgentforceEmployeeAgent` |
42
+ | `agent-templates` returns empty `data[]` | Wrong `agentType` value — use `AgentforceEmployeeAgent`; empty results are not the same as "not provisioned" |
43
+ | Treating `activate`'s empty response as a failure | It's the documented success response (`EmptyRepresentation`) — verify via the Phase-7 SOQL instead of parsing this call's stdout |
44
+ | Tempted to token+curl for any of the three writes | Never — `sf api request rest --method POST --body @<file>` (or `--body '{}'`) handles auth for `/services/data/...` writes; token extraction is forbidden |
@@ -0,0 +1,63 @@
1
+ # Reactivation path — activating an existing inactive IT Service Fulfiller Agent
2
+
3
+ Split from `references/cli-invocation.md` — this file documents the direct
4
+ `BotVersion` activation call used when the Phase-2 classifier reports the agent
5
+ already exists but its latest version is `Inactive`. `cli-invocation.md` covers
6
+ the create/publish/activate happy path; this file covers "skip create/publish,
7
+ just reactivate what's already there".
8
+
9
+ ## When this path fires
10
+
11
+ `scripts/classify-agent-existence.mjs` (called with the template's
12
+ `botDefinitionId` as the PRIMARY idempotency key and the collected
13
+ `DeveloperName` as the FALLBACK — see `cli-invocation.md` → "Enumerate the
14
+ existing agent") returns
15
+ `{ exists:true, needsActivation:true, latestVersionId, latestVersionStatus:"Inactive" }`.
16
+ That means a `BotDefinition` matching the collected `DeveloperName` (the
17
+ Fulfiller's template `botDefinitionId` is null, so the fallback is what matches)
18
+ already exists from a prior run of this skill but its most-recent `BotVersion` is
19
+ inactive; creating a new bundle would produce a duplicate. Instead, flip the
20
+ existing version to `Active`.
21
+
22
+ ## Activate an existing inactive version — `POST /connect/bot-versions/{id}/activation`
23
+
24
+ ```bash
25
+ sf api request rest "/services/data/v67.0/connect/bot-versions/<latestVersionId>/activation" \
26
+ --method POST --body '{"status":"Active"}' \
27
+ --target-org <alias> > ${SCRATCH_DIR}/activate-existing.json 2>${SCRATCH_DIR}/activate-existing.err || true
28
+ ```
29
+
30
+ `<latestVersionId>` is the captured `latestVersionId` from the Phase-2
31
+ classifier output — **not** a bundle version id. This is a direct
32
+ `BotVersion` activation toggle that bypasses the NGA bundle pipeline entirely,
33
+ because the `BotDefinition`/`BotVersion` records already exist. Skips create
34
+ (Phase 4) and publish (Phase 5) — go straight to activate, then verify.
35
+
36
+ ## Verify — same SOQL as the happy path
37
+
38
+ Run the Phase-7 verify read (see `cli-invocation.md` → "Verify the agent is
39
+ live"). Confirm `exists:true` with `count:1` **and**
40
+ `latestVersionStatus:"Active"`. If `latestVersionStatus` is still `Inactive`
41
+ after a successful activation call, report the discrepancy verbatim — do not
42
+ fabricate success.
43
+
44
+ ## Idempotency semantics
45
+
46
+ | Signal | Verdict |
47
+ |--------|---------|
48
+ | Neither the template's `botDefinitionId` nor the collected `DeveloperName` matches a live `BotDefinition` — classifier returns `exists:false` | proceed to CREATE |
49
+ | Phase-2 classifier returns `exists:true` and `needsActivation:false` (latest version `Active`), matched by `botDefinitionId` or the `DeveloperName` fallback | ALREADY-CREATED (skip create/publish/activate entirely) |
50
+ | Phase-2 classifier returns `exists:true` and `needsActivation:true` (latest version `Inactive`), user confirms reactivation, and the bot-version activation call succeeds | ACTIVATED (skip create/publish entirely — direct `BotVersion` activation only) |
51
+ | `createBundleWithVersion` → `publish` → `activate` all succeed and Phase-7 classifier (keyed on the publish `publishedBotId`) confirms `exists:true` with `latestVersionStatus:"Active"` | CREATED |
52
+
53
+ The server-side create path **does** reject a duplicate, but keyed on
54
+ `DeveloperName`, not `apiName`: a pre-validation lookup
55
+ (`lookupBotDefinitionIdByDeveloperName`) plus a publish-time `BotDefinition`
56
+ DeveloperName unique-constraint catch that cleans up the orphaned bundle version.
57
+ That guard fires when the `DeveloperName` being created already exists, so it
58
+ catches a repeat run of this skill — but only as a hard error. The Phase-2 read
59
+ is what makes the repeat graceful: because the Fulfiller is never pre-provisioned
60
+ and this skill's create path never stamps `templateName`, the template's
61
+ `botDefinitionId` is always `null`, so the read falls back to the collected
62
+ `DeveloperName` to detect the existing agent and offer ALREADY-CREATED /
63
+ reactivation instead of letting the create call fail with `DUPLICATE_VALUE`.
@@ -0,0 +1,44 @@
1
+ # Report Format — Fulfiller Agent Create & Activate
2
+
3
+ The report layout is generated deterministically by `scripts/render-report.mjs` — the single source of report text for both the chat-turn response and the harness's `${outputDir}/report.md` file, so the two never diverge. Never hand-compose the layout, field placement, stage rows, or next-step wording in prose (authoring standard A9); always shell out to the helper.
4
+
5
+ ## Rendered shape
6
+
7
+ ```text
8
+ # Fulfiller Agent — Create & Activate
9
+
10
+ IT Service Fulfiller Agent Creation (via service-itsm-agentic-setup-fulfiller-agent-configure)
11
+
12
+ Org: <org-alias> (API v67.0)
13
+ Agent: <developerName> ("<label>") — NGA-native bundle
14
+
15
+ Preflight ......................... Studio hasAccess=<true|false|cannot-confirm>; template agentScript present=<yes|no>
16
+ Enumerate ......................... target agent exists before write=<yes|no>; latest version status=<Active|Inactive|n/a>
17
+ Confirm-to-write ................... user-confirmed=<true|false>
18
+ Create bundle ...................... <bundleVersionId=... | ALREADY-CREATED | skipped (reactivation path) | pending confirmation | skipped | FAILED>
19
+ Publish ............................ <publishedBotId=... | skipped | pending confirmation | FAILED>
20
+ Activate ........................... <succeeded (created) | succeeded (reactivated existing) | skipped | pending confirmation | FAILED>
21
+ Verify ............................. <BotDefinition present: yes|no; latest version Active: yes|no | skipped | pending>
22
+
23
+ Verdict: CREATED | ALREADY-CREATED | ACTIVATED | PENDING CONFIRMATION | DECLINED | FAILED
24
+ Reason: <plain-language explanation naming the atomicity constraint, the idempotency decision, and any decline/skip reasoning — the caller populates this before invoking the helper, so it can be as rubric-facing as the situation warrants without repeating raw API calls>
25
+
26
+ Next steps:
27
+ - <helper-generated line keyed off the verdict>
28
+ ```
29
+
30
+ The helper enforces a validated verdict set and picks the `Next steps` line from the verdict, so the file never drifts from that shape.
31
+
32
+ ## Checkpoint writes (harness / non-interactive runs)
33
+
34
+ When `${outputDir}` is provided (via the harness's generated-file location directive), invoke the helper at three checkpoints so a report always exists even when a run parks at a confirmation gate:
35
+
36
+ 1. **Before Phase 3's confirmation gate** — verdict `PENDING CONFIRMATION` with Preflight + Enumerate rows populated; the helper marks Confirm-to-write / Create / Publish / Activate as `pending confirmation` and Verify as `skipped`. On the user's "no" at the gate, re-render with verdict `DECLINED` and a one-line `reason` naming the decline (the helper flips write rows to `skipped`).
37
+ 2. **After Phase 6 (or the Phase-2b reactivation activation)** — verdict is still not the final verdict yet; the helper is re-invoked with the create-succeeded (or reactivation-succeeded) state and Verify `pending`.
38
+ 3. **After Phase 8** — verdict `CREATED` / `ALREADY-CREATED` / `ACTIVATED` / `FAILED` with all rows filled in.
39
+
40
+ Each invocation overwrites `${outputDir}/report.md`, so the last state on disk is always the most complete. Skip these writes when running interactively for a user in a chat surface — write only when `${outputDir}` was passed as an explicit destination.
41
+
42
+ ## Interactive runs
43
+
44
+ When talking to the user in a chat surface, the assistant may still add turn-side narrative context above or below the helper output (raw command traces, error diagnostics, remediation walkthroughs, "why this is blocking" prose) — the *report* is what the helper emits, the *turn* can be as rich as the situation warrants. Only the report file has the strict shape.