@salesforce/afv-skills 1.47.0 → 1.48.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 (160) hide show
  1. package/package.json +1 -1
  2. package/skills/education-cloud-domain-configure/SKILL.md +234 -0
  3. package/skills/education-cloud-domain-configure/references/dependent-features.md +198 -0
  4. package/skills/education-cloud-domain-configure/references/domain-features/academic-operations.md +386 -0
  5. package/skills/education-cloud-domain-configure/references/domain-features/alumni-relations.md +155 -0
  6. package/skills/education-cloud-domain-configure/references/domain-features/cross-domain-dependencies.md +18 -0
  7. package/skills/education-cloud-domain-configure/references/domain-features/industriessettings-toggle-reference.md +43 -0
  8. package/skills/education-cloud-domain-configure/references/domain-features/mentoring.md +198 -0
  9. package/skills/education-cloud-domain-configure/references/domain-features/recruitment-admissions.md +315 -0
  10. package/skills/education-cloud-domain-configure/references/domain-features/student-management.md +155 -0
  11. package/skills/education-cloud-domain-configure/references/domain-features/student-success.md +249 -0
  12. package/skills/education-cloud-domain-configure/references/domain-features.md +27 -0
  13. package/skills/education-cloud-domain-configure/references/gotchas-extended.md +23 -0
  14. package/skills/education-cloud-domain-configure/references/toggle-write-path.md +67 -0
  15. package/skills/education-cloud-domain-configure/scripts/check_license_provisioned.py +62 -0
  16. package/skills/education-cloud-domain-configure/scripts/check_org_edition.py +44 -0
  17. package/skills/education-cloud-domain-configure/scripts/check_permission_assigned.py +39 -0
  18. package/skills/education-cloud-multi-campus-configure/SKILL.md +7 -0
  19. package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +4 -3
  20. package/skills/service-agentforce-channel-configure/SKILL.md +33 -28
  21. package/skills/service-agentforce-human-escalation-configure/SKILL.md +205 -0
  22. package/skills/service-agentforce-human-escalation-configure/assets/escalation-thresholds.instructions.md +37 -0
  23. package/skills/service-agentforce-human-escalation-configure/assets/force-app/main/default/flows/Human_Escalation_Outbound_Flow.flow-meta.xml +173 -0
  24. package/skills/service-agentforce-human-escalation-configure/assets/package.xml +8 -0
  25. package/skills/service-agentforce-human-escalation-configure/assets/sfdx-project.json +5 -0
  26. package/skills/service-agentforce-human-escalation-configure/references/classifier-contracts.md +103 -0
  27. package/skills/service-agentforce-human-escalation-configure/references/escalation-mechanism.md +63 -0
  28. package/skills/service-agentforce-human-escalation-configure/references/failure-policy.md +44 -0
  29. package/skills/service-agentforce-human-escalation-configure/references/nga-escalation.md +57 -0
  30. package/skills/service-agentforce-human-escalation-configure/references/runtime-verification.md +72 -0
  31. package/skills/service-agentforce-human-escalation-configure/scripts/classify-agent-active.mjs +49 -0
  32. package/skills/service-agentforce-human-escalation-configure/scripts/classify-nga-escalation.mjs +166 -0
  33. package/skills/service-agentforce-human-escalation-configure/scripts/extract-outbound-route.mjs +67 -0
  34. package/skills/service-agentforce-human-escalation-configure/scripts/patch-escalation-surfaces.mjs +131 -0
  35. package/skills/service-agentforce-human-escalation-configure/scripts/tests/__init__.py +0 -0
  36. package/skills/service-agentforce-human-escalation-configure/scripts/tests/_bootstrap.py +249 -0
  37. package/skills/service-agentforce-human-escalation-configure/scripts/tests/test_escalation_contracts.py +899 -0
  38. package/skills/service-agentforce-human-escalation-configure/scripts/verify-and-configure.sh +705 -0
  39. package/skills/service-agentforce-human-escalation-configure/scripts/verify-escalation-config.mjs +89 -0
  40. package/skills/service-de-channel-routing-configure/SKILL.md +13 -5
  41. package/skills/service-de-channel-routing-configure/scripts/tests/test_skill_contract.py +25 -0
  42. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +23 -10
  43. package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +63 -48
  44. package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +6 -2
  45. package/skills/service-omni-agent-users-create/SKILL.md +96 -0
  46. package/skills/service-omni-agent-users-create/assets/create-users.apex.template +80 -0
  47. package/skills/service-omni-agent-users-create/references/apex-patterns.md +135 -0
  48. package/skills/service-omni-agent-users-create/references/apex-template-notes.md +40 -0
  49. package/skills/service-omni-agent-users-create/scripts/detect-and-create.sh +206 -0
  50. package/skills/service-omni-agent-users-create/scripts/detect-existing.sh +122 -0
  51. package/skills/service-omni-agent-users-create/scripts/run-create.sh +298 -0
  52. package/skills/service-omni-agent-users-create/scripts/tests/test_user_create_security.py +171 -0
  53. package/skills/service-omni-agent-work-sharing-configure/SKILL.md +78 -0
  54. package/skills/service-omni-agent-work-sharing-configure/references/api-notes.md +31 -0
  55. package/skills/service-omni-agent-work-sharing-configure/scripts/configure-and-report.sh +186 -0
  56. package/skills/service-omni-agent-work-sharing-configure/scripts/tests/_bootstrap.py +120 -0
  57. package/skills/service-omni-agent-work-sharing-configure/scripts/tests/test_agent_work_sharing_contracts.py +93 -0
  58. package/skills/service-omni-base-settings-configure/SKILL.md +77 -0
  59. package/skills/service-omni-base-settings-configure/assets/force-app/main/default/settings/OmniChannel.settings-meta.xml +8 -0
  60. package/skills/service-omni-base-settings-configure/assets/package.xml +8 -0
  61. package/skills/service-omni-base-settings-configure/assets/sfdx-project.json +12 -0
  62. package/skills/service-omni-base-settings-configure/references/api-notes.md +121 -0
  63. package/skills/service-omni-base-settings-configure/scripts/configure-and-report.sh +164 -0
  64. package/skills/service-omni-base-settings-configure/scripts/detect-existing.sh +69 -0
  65. package/skills/service-omni-channel-setup-coordinate/SKILL.md +168 -0
  66. package/skills/service-omni-channel-setup-coordinate/examples/output-templates.md +161 -0
  67. package/skills/service-omni-channel-setup-coordinate/references/gap-catalog.md +99 -0
  68. package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +1542 -0
  69. package/skills/service-omni-channel-setup-coordinate/scripts/tests/__init__.py +0 -0
  70. package/skills/service-omni-channel-setup-coordinate/scripts/tests/_bootstrap.py +223 -0
  71. package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +804 -0
  72. package/skills/service-omni-command-center-analyze/SKILL.md +84 -0
  73. package/skills/service-omni-command-center-analyze/references/api-notes.md +54 -0
  74. package/skills/service-omni-command-center-analyze/scripts/analyze.sh +198 -0
  75. package/skills/service-omni-command-center-analyze/scripts/tests/_bootstrap.py +115 -0
  76. package/skills/service-omni-command-center-analyze/scripts/tests/test_command_center_analyze_contracts.py +165 -0
  77. package/skills/service-omni-permission-set-assign/SKILL.md +88 -0
  78. package/skills/service-omni-permission-set-assign/assets/package.xml +8 -0
  79. package/skills/service-omni-permission-set-assign/references/api-notes.md +143 -0
  80. package/skills/service-omni-permission-set-assign/scripts/verify-and-assign.sh +397 -0
  81. package/skills/service-omni-presence-status-deploy/SKILL.md +87 -0
  82. package/skills/service-omni-presence-status-deploy/assets/package.xml +12 -0
  83. package/skills/service-omni-presence-status-deploy/references/api-notes.md +115 -0
  84. package/skills/service-omni-presence-status-deploy/scripts/deploy-and-report.sh +422 -0
  85. package/skills/service-omni-presence-user-config-deploy/SKILL.md +80 -0
  86. package/skills/service-omni-presence-user-config-deploy/references/api-notes.md +60 -0
  87. package/skills/service-omni-presence-user-config-deploy/scripts/deploy-and-report.sh +261 -0
  88. package/skills/service-omni-presence-user-config-deploy/scripts/tests/_bootstrap.py +106 -0
  89. package/skills/service-omni-presence-user-config-deploy/scripts/tests/test_presence_user_config_contracts.py +142 -0
  90. package/skills/service-omni-queue-deploy/SKILL.md +91 -0
  91. package/skills/service-omni-queue-deploy/references/api-notes.md +140 -0
  92. package/skills/service-omni-queue-deploy/scripts/verify-and-align.sh +470 -0
  93. package/skills/service-omni-queue-members-assign/SKILL.md +93 -0
  94. package/skills/service-omni-queue-members-assign/references/api-notes.md +144 -0
  95. package/skills/service-omni-queue-members-assign/scripts/tests/_bootstrap.py +67 -0
  96. package/skills/service-omni-queue-members-assign/scripts/tests/test_queue_members_contracts.py +73 -0
  97. package/skills/service-omni-queue-members-assign/scripts/verify-and-bind.sh +362 -0
  98. package/skills/service-omni-queue-routing-config-deploy/SKILL.md +101 -0
  99. package/skills/service-omni-queue-routing-config-deploy/references/api-notes.md +127 -0
  100. package/skills/service-omni-queue-routing-config-deploy/scripts/tests/_bootstrap.py +90 -0
  101. package/skills/service-omni-queue-routing-config-deploy/scripts/tests/test_queue_routing_config_contracts.py +70 -0
  102. package/skills/service-omni-queue-routing-config-deploy/scripts/upsert-and-report.sh +538 -0
  103. package/skills/service-omni-routing-flow-deploy/SKILL.md +111 -0
  104. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Case_Trigger.SkillsBased.flow-meta.xml +94 -0
  105. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Case_Trigger.flow-meta.xml +88 -0
  106. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Cases.flow-meta.xml +144 -0
  107. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCall_Trigger.SkillsBased.flow-meta.xml +94 -0
  108. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCall_Trigger.flow-meta.xml +88 -0
  109. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCalls.flow-meta.xml +144 -0
  110. package/skills/service-omni-routing-flow-deploy/assets/package.xml +11 -0
  111. package/skills/service-omni-routing-flow-deploy/assets/sfdx-project.json +11 -0
  112. package/skills/service-omni-routing-flow-deploy/references/api-notes.md +265 -0
  113. package/skills/service-omni-routing-flow-deploy/scripts/deploy-and-report.sh +684 -0
  114. package/skills/service-omni-service-channel-configure/SKILL.md +83 -0
  115. package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/Cases.serviceChannel-meta.xml +9 -0
  116. package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/Incidents.serviceChannel-meta.xml +9 -0
  117. package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/MessagingSessions.serviceChannel-meta.xml +9 -0
  118. package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/VoiceCalls.serviceChannel-meta.xml +9 -0
  119. package/skills/service-omni-service-channel-configure/assets/package.xml +11 -0
  120. package/skills/service-omni-service-channel-configure/assets/sfdx-project.json +12 -0
  121. package/skills/service-omni-service-channel-configure/references/api-notes.md +84 -0
  122. package/skills/service-omni-service-channel-configure/scripts/deploy-and-report.sh +383 -0
  123. package/skills/service-omni-sidebar-configure/SKILL.md +77 -0
  124. package/skills/service-omni-sidebar-configure/references/api-notes.md +51 -0
  125. package/skills/service-omni-sidebar-configure/scripts/enable-and-report.sh +315 -0
  126. package/skills/service-omni-sidebar-configure/scripts/tests/_bootstrap.py +142 -0
  127. package/skills/service-omni-sidebar-configure/scripts/tests/test_sidebar_contracts.py +139 -0
  128. package/skills/service-omni-skills-based-routing-configure/SKILL.md +81 -0
  129. package/skills/service-omni-skills-based-routing-configure/references/api-notes.md +50 -0
  130. package/skills/service-omni-skills-based-routing-configure/scripts/configure-and-report.sh +270 -0
  131. package/skills/service-omni-skills-based-routing-configure/scripts/tests/_bootstrap.py +120 -0
  132. package/skills/service-omni-skills-based-routing-configure/scripts/tests/test_sbr_contracts.py +129 -0
  133. package/skills/service-omni-supervisor-config-deploy/SKILL.md +97 -0
  134. package/skills/service-omni-supervisor-config-deploy/assets/force-app/main/default/omniSupervisorConfigs/Omni_Supervisor.omniSupervisorConfig-meta.xml +9 -0
  135. package/skills/service-omni-supervisor-config-deploy/assets/package.xml +8 -0
  136. package/skills/service-omni-supervisor-config-deploy/assets/sfdx-project.json +12 -0
  137. package/skills/service-omni-supervisor-config-deploy/references/api-notes.md +205 -0
  138. package/skills/service-omni-supervisor-config-deploy/scripts/deploy-and-report.sh +586 -0
  139. package/skills/service-omni-supervisor-config-deploy/scripts/tests/_bootstrap.py +153 -0
  140. package/skills/service-omni-supervisor-config-deploy/scripts/tests/test_supervisor_config_contracts.py +116 -0
  141. package/skills/service-omni-supervisor-permset-assign/SKILL.md +88 -0
  142. package/skills/service-omni-supervisor-permset-assign/references/api-notes.md +48 -0
  143. package/skills/service-omni-supervisor-permset-assign/scripts/verify-and-assign.sh +316 -0
  144. package/skills/service-omni-supervisor-surface-deploy/SKILL.md +85 -0
  145. package/skills/service-omni-supervisor-surface-deploy/references/api-notes.md +47 -0
  146. package/skills/service-omni-supervisor-surface-deploy/scripts/deploy-and-report.sh +216 -0
  147. package/skills/service-omni-supervisor-surface-deploy/scripts/tests/_bootstrap.py +95 -0
  148. package/skills/service-omni-supervisor-surface-deploy/scripts/tests/test_supervisor_surface_contracts.py +154 -0
  149. package/skills/service-omni-supervisor-users-create/SKILL.md +92 -0
  150. package/skills/service-omni-supervisor-users-create/assets/create-supervisors.apex.template +72 -0
  151. package/skills/service-omni-supervisor-users-create/references/apex-patterns.md +73 -0
  152. package/skills/service-omni-supervisor-users-create/references/apex-template-notes.md +41 -0
  153. package/skills/service-omni-supervisor-users-create/scripts/detect-and-create.sh +197 -0
  154. package/skills/service-omni-supervisor-users-create/scripts/detect-existing.sh +112 -0
  155. package/skills/service-omni-supervisor-users-create/scripts/run-create.sh +296 -0
  156. package/skills/service-omni-work-skill-routing-configure/SKILL.md +80 -0
  157. package/skills/service-omni-work-skill-routing-configure/references/api-notes.md +67 -0
  158. package/skills/service-omni-work-skill-routing-configure/scripts/configure-and-report.sh +262 -0
  159. package/skills/service-omni-work-skill-routing-configure/scripts/tests/_bootstrap.py +121 -0
  160. package/skills/service-omni-work-skill-routing-configure/scripts/tests/test_work_skill_routing_contracts.py +161 -0
@@ -0,0 +1,223 @@
1
+ """Shared paths + fake-`sf` harness for the Omni contract suite (org-free, network-free): drives the bash skills via subprocess asserting exit codes + emitted JSON, with scrubbed-PATH (no sf) and production-org fake-sf fixtures for input-contract and safe_to_write checks."""
2
+ from __future__ import annotations
3
+
4
+ import os
5
+ import subprocess
6
+ import tempfile
7
+ from pathlib import Path
8
+
9
+ # skills/ root is three levels up from this file (.../<skill>/scripts/tests/_bootstrap.py).
10
+ SKILLS_ROOT = Path(__file__).resolve().parents[3]
11
+
12
+ # The 18 scripts under test, relative to skills/.
13
+ SCRIPTS = [
14
+ "service-omni-base-settings-configure/scripts/configure-and-report.sh",
15
+ "service-omni-base-settings-configure/scripts/detect-existing.sh",
16
+ "service-omni-channel-setup-coordinate/scripts/integration-driver.sh",
17
+ "service-omni-agent-users-create/scripts/detect-and-create.sh",
18
+ "service-omni-agent-users-create/scripts/detect-existing.sh",
19
+ "service-omni-agent-users-create/scripts/run-create.sh",
20
+ "service-omni-permission-set-assign/scripts/verify-and-assign.sh",
21
+ "service-omni-presence-status-deploy/scripts/deploy-and-report.sh",
22
+ "service-omni-queue-deploy/scripts/verify-and-align.sh",
23
+ "service-omni-queue-members-assign/scripts/verify-and-bind.sh",
24
+ "service-omni-queue-routing-config-deploy/scripts/upsert-and-report.sh",
25
+ "service-omni-routing-flow-deploy/scripts/deploy-and-report.sh",
26
+ "service-omni-service-channel-configure/scripts/deploy-and-report.sh",
27
+ "service-omni-supervisor-config-deploy/scripts/deploy-and-report.sh",
28
+ "service-omni-supervisor-permset-assign/scripts/verify-and-assign.sh",
29
+ "service-omni-supervisor-users-create/scripts/detect-and-create.sh",
30
+ "service-omni-supervisor-users-create/scripts/detect-existing.sh",
31
+ "service-omni-supervisor-users-create/scripts/run-create.sh",
32
+ ]
33
+
34
+ # A fake `sf` that reports a PRODUCTION customer org, so safe_to_write guards must refuse.
35
+ # Also fabricates the OmniChannel settings XML on `project retrieve start` (all toggles
36
+ # false) so base-settings' detector returns all_enabled=false and reaches its run guard.
37
+ _FAKE_SF = r"""#!/usr/bin/env bash
38
+ sub="${1:-} ${2:-}"
39
+ args="$*"
40
+ case "$sub" in
41
+ "org display")
42
+ echo '{"status":0,"result":{"username":"fake@prod.org","id":"00Dxx0000000000EAA"}}'; exit 0 ;;
43
+ "data query")
44
+ if printf '%s' "$args" | grep -q "FROM Organization"; then
45
+ echo '{"status":0,"result":{"records":[{"Id":"00Dxx0000000000EAA","IsSandbox":false,"TrialExpirationDate":null,"OrganizationType":"Enterprise Edition"}]}}'
46
+ elif printf '%s' "$args" | grep -q "FROM Profile"; then
47
+ echo '{"status":0,"result":{"records":[{"Id":"00exx0000000000AAA","Name":"Standard User"}]}}'
48
+ else
49
+ echo '{"status":0,"result":{"records":[],"totalSize":0}}'
50
+ fi
51
+ exit 0 ;;
52
+ "project retrieve")
53
+ mkdir -p force-app/main/default/settings
54
+ cat > force-app/main/default/settings/OmniChannel.settings-meta.xml <<'XML'
55
+ <?xml version="1.0" encoding="UTF-8"?>
56
+ <OmniChannelSettings xmlns="http://soap.sforce.com/2006/04/metadata">
57
+ <enableOmniChannel>false</enableOmniChannel>
58
+ <enableOmniSkillsRouting>false</enableOmniSkillsRouting>
59
+ <enableOmniSecondaryRoutingPriority>false</enableOmniSecondaryRoutingPriority>
60
+ <enableOmniStatusCapModel>false</enableOmniStatusCapModel>
61
+ <enableOmniAutoLoginPrompt>false</enableOmniAutoLoginPrompt>
62
+ </OmniChannelSettings>
63
+ XML
64
+ echo '{"status":0,"result":{"success":true}}'; exit 0 ;;
65
+ "project deploy")
66
+ echo '{"status":1,"result":{"success":false,"status":"Failed"},"message":"FAKE_SF_DEPLOY_SHOULD_NOT_BE_REACHED"}'; exit 1 ;;
67
+ *)
68
+ echo '{"status":0,"result":{}}'; exit 0 ;;
69
+ esac
70
+ """
71
+
72
+ # A fake `sf` that reports a SANDBOX org - used only by the negative-control test to prove
73
+ # the safe_to_write assertions are discriminating (a safe org must NOT trip the refusal).
74
+ _FAKE_SF_SANDBOX = r"""#!/usr/bin/env bash
75
+ sub="${1:-} ${2:-}"; args="$*"
76
+ case "$sub" in
77
+ "org display") echo '{"status":0,"result":{"id":"00Dxx0000000000EAA"}}'; exit 0;;
78
+ "data query")
79
+ if printf '%s' "$args" | grep -q "FROM Organization"; then
80
+ echo '{"status":0,"result":{"records":[{"Id":"00Dxx0000000000EAA","IsSandbox":true,"TrialExpirationDate":null,"OrganizationType":"Developer Edition"}]}}'
81
+ else echo '{"status":0,"result":{"records":[],"totalSize":0}}'; fi; exit 0;;
82
+ *) echo '{"status":0,"result":{}}'; exit 0;;
83
+ esac
84
+ """
85
+
86
+ # A parameterized fake `sf` for the Voice routing-flow steel-thread tests. It emulates a SANDBOX
87
+ # org where the VoiceCall queue + QueueRoutingConfig already resolve, and is tunable via env:
88
+ # FAKE_SC_PRESENT=1 → the ServiceChannel query returns a row (id 0N9...); 0 → empty (fail-closed path)
89
+ # FAKE_FLOW_ACTIVE=1 → an ACTIVE Flow version already exists (drives the reuse/stale-binding path)
90
+ # FAKE_ACTIVE_IDS="..." → the IDs embedded in the retrieved active flow XML (space-separated). When it
91
+ # contains the current queue+QRC+SC ids the binding "matches" (→ reused);
92
+ # otherwise it has drifted (→ redeploy).
93
+ # Canonical current ids: queue=00G000000000001AAA, qrc=0RC00000000001AAA, sc=0N9000000000001AAA.
94
+ _FAKE_SF_VOICE = r"""#!/usr/bin/env bash
95
+ sub="${1:-} ${2:-}"; args="$*"
96
+ SC_PRESENT="${FAKE_SC_PRESENT:-1}"
97
+ FLOW_ACTIVE="${FAKE_FLOW_ACTIVE:-0}"
98
+ ACTIVE_IDS="${FAKE_ACTIVE_IDS:-00G000000000001AAA 0RC00000000001AAA 0N9000000000001AAA}"
99
+ case "$sub" in
100
+ "org display") echo '{"status":0,"result":{"id":"00Dxx0000000000EAA","apiVersion":"66.0"}}'; exit 0;;
101
+ "data query")
102
+ if printf '%s' "$args" | grep -q "FROM Organization"; then
103
+ echo '{"status":0,"result":{"records":[{"Id":"00Dxx0000000000EAA","IsSandbox":true,"TrialExpirationDate":null,"OrganizationType":"Developer Edition"}]}}'
104
+ elif printf '%s' "$args" | grep -q "FROM ServiceChannel"; then
105
+ if [ "$SC_PRESENT" = "1" ]; then
106
+ echo '{"status":0,"result":{"records":[{"Id":"0N9000000000001AAA","MasterLabel":"Phone"}],"totalSize":1}}'
107
+ else
108
+ echo '{"status":0,"result":{"records":[],"totalSize":0}}'
109
+ fi
110
+ elif printf '%s' "$args" | grep -q "FROM QueueSobject"; then
111
+ echo '{"status":0,"result":{"records":[{"QueueId":"00G000000000001AAA","Queue":{"DeveloperName":"VoiceQueue"}}],"totalSize":1}}'
112
+ elif printf '%s' "$args" | grep -q "FROM Group"; then
113
+ echo '{"status":0,"result":{"records":[{"Id":"00G000000000001AAA","QueueRoutingConfigId":"0RC00000000001AAA"}],"totalSize":1}}'
114
+ elif printf '%s' "$args" | grep -q "FROM QueueRoutingConfig"; then
115
+ echo '{"status":0,"result":{"records":[{"Id":"0RC00000000001AAA","DeveloperName":"Voice_Routing_Config"}],"totalSize":1}}'
116
+ elif printf '%s' "$args" | grep -q "FROM PendingServiceRouting"; then
117
+ if [ "${FAKE_PROOF:-0}" = "1" ]; then
118
+ echo '{"status":0,"result":{"records":[{"Id":"0RQ00000000001AAA","IsReadyForRouting":true,"RoutingModel":"QueueBased"}],"totalSize":1}}'
119
+ else
120
+ echo '{"status":0,"result":{"records":[],"totalSize":0}}'
121
+ fi
122
+ elif printf '%s' "$args" | grep -q "FROM AgentWork"; then
123
+ echo '{"status":0,"result":{"records":[],"totalSize":0}}'
124
+ elif printf '%s' "$args" | grep -q "FROM SkillRequirement"; then
125
+ # SkillsBased acceptance signal: the rows the platform attached to the PSR (RelatedRecordId=PSR).
126
+ # FAKE_SKILLREQ=1 emulates a WorkSkillRouting rule that matched (RunSBRRules attached a SkillRequirement).
127
+ if [ "${FAKE_SKILLREQ:-0}" = "1" ]; then
128
+ echo '{"status":0,"result":{"records":[{"Id":"0RS00000000001AAA","SkillId":"0PS00000000001AAA","SkillLevel":5}],"totalSize":1}}'
129
+ else
130
+ echo '{"status":0,"result":{"records":[],"totalSize":0}}'
131
+ fi
132
+ elif printf '%s' "$args" | grep -q "FROM User"; then
133
+ echo '{"status":0,"result":{"records":[{"Id":"005000000000001AAA","Username":"agent1@example.com"},{"Id":"005000000000002AAA","Username":"agent2@example.com"}],"totalSize":2}}'
134
+ elif printf '%s' "$args" | grep -q "FROM FlowDefinitionView"; then
135
+ # Post-deploy round-trip active-version proof. Distinct tunable from FLOW_ACTIVE (which drives
136
+ # the PRE-deploy reuse detection): after a deploy the flow becomes active, so default active.
137
+ if [ "${FAKE_FLOW_VIEW_ERROR:-0}" = "1" ]; then
138
+ echo '{"status":1,"name":"EXTERNAL_OBJECT_EXCEPTION","message":"unrelated managed-package flow prevents FlowDefinitionView reads"}'
139
+ elif [ "${FAKE_FLOW_ACTIVE_RT:-1}" = "1" ]; then
140
+ echo '{"status":0,"result":{"records":[{"ApiName":"F","ActiveVersionId":"301000000000009AAA"}],"totalSize":1}}'
141
+ else
142
+ echo '{"status":0,"result":{"records":[{"ApiName":"F","ActiveVersionId":null}],"totalSize":1}}'
143
+ fi
144
+ elif printf '%s' "$args" | grep -q "FROM Flow"; then
145
+ if [ "$FLOW_ACTIVE" = "1" ]; then
146
+ echo '{"status":0,"result":{"records":[{"Id":"301000000000001AAA"}],"totalSize":1}}'
147
+ else
148
+ echo '{"status":0,"result":{"records":[],"totalSize":0}}'
149
+ fi
150
+ else
151
+ echo '{"status":0,"result":{"records":[],"totalSize":0}}'
152
+ fi
153
+ exit 0;;
154
+ "project retrieve")
155
+ # Called inside a temp DX project (cwd). Write the "active" flow with the tunable embedded ids
156
+ # so the stale-binding comparator can grep for them.
157
+ flow=$(printf '%s' "$args" | sed -n 's/.*Flow:\([A-Za-z0-9_]*\).*/\1/p'); flow="${flow:-Omni_Route_VoiceCall_Trigger}"
158
+ mkdir -p force-app/main/default/flows
159
+ { echo '<?xml version="1.0" encoding="UTF-8"?>'; echo '<Flow xmlns="http://soap.sforce.com/2006/04/metadata">'
160
+ for id in $ACTIVE_IDS; do echo " <elementReference>$id</elementReference>"; done
161
+ echo " <stringValue>${ACTIVE_ROUTING_TYPE:-QueueBased}</stringValue>"
162
+ [ "${ACTIVE_ROUTING_TYPE:-QueueBased}" != "SkillsBased" ] || echo " <stringValue>${ACTIVE_SKILL_OPTION:-RunSBRRules}</stringValue>"
163
+ echo '</Flow>'; } > "force-app/main/default/flows/${flow}.flow-meta.xml"
164
+ echo '{"status":0,"result":{"success":true}}'; exit 0;;
165
+ "project deploy")
166
+ fn=$(printf '%s' "$args" | sed -n 's/.*Flow:\([A-Za-z0-9_]*\).*/\1/p'); fn="${fn:-Omni_Route_VoiceCall_Trigger}"
167
+ echo "{\"status\":0,\"result\":{\"success\":true,\"files\":[{\"fullName\":\"$fn\",\"type\":\"Flow\",\"state\":\"Created\"}]}}"; exit 0;;
168
+ "data create")
169
+ echo '{"status":0,"result":{"id":"500000000000001AAA","success":true}}'; exit 0;;
170
+ "data delete")
171
+ # Log the deleted record-id so tests can prove runtime-proof cleanup happened.
172
+ [ -n "${FAKE_DELETE_LOG:-}" ] && printf '%s\n' "$args" >> "$FAKE_DELETE_LOG"
173
+ echo '{"status":0,"result":{"id":"500000000000001AAA","success":true}}'; exit 0;;
174
+ *) echo '{"status":0,"result":{}}'; exit 0;;
175
+ esac
176
+ """
177
+
178
+
179
+ def _run(script_rel, args, path_prefix=None, extra_path_scrub=False, extra_env=None):
180
+ """Run a skill script, return (returncode, merged stdout+stderr).
181
+
182
+ path_prefix: a dir to prepend to PATH (where a fake `sf` lives).
183
+ extra_path_scrub: when True, build a PATH that excludes any real `sf` so the
184
+ input-contract category can prove rejection happens before the first sf call.
185
+ extra_env: optional dict of env vars overlaid onto the child environment (fake-`sf`
186
+ tunables like FAKE_SC_PRESENT, and cross-skill handoff vars like
187
+ SERVICE_CHANNEL_DEVELOPER_NAME / QUEUE_DEVELOPER_NAME / ROUTING_CONFIG_DEVELOPER_NAME).
188
+ """
189
+ env = dict(os.environ)
190
+ if extra_path_scrub:
191
+ # Keep only dirs that do NOT contain an `sf` executable, so `sf` is absent.
192
+ kept = [d for d in env.get("PATH", "").split(os.pathsep)
193
+ if d and not os.path.exists(os.path.join(d, "sf"))]
194
+ env["PATH"] = os.pathsep.join(kept)
195
+ if path_prefix:
196
+ env["PATH"] = path_prefix + os.pathsep + env.get("PATH", "")
197
+ if extra_env:
198
+ env.update({k: str(v) for k, v in extra_env.items()})
199
+ proc = subprocess.run(
200
+ ["bash", str(SKILLS_ROOT / script_rel), *args],
201
+ capture_output=True, text=True, env=env, cwd=str(SKILLS_ROOT),
202
+ )
203
+ return proc.returncode, (proc.stdout or "") + (proc.stderr or "")
204
+
205
+
206
+ class _FakeSfDir:
207
+ """Context manager: a temp dir holding an executable fake `sf` on PATH."""
208
+
209
+ def __init__(self, body=_FAKE_SF):
210
+ self._body = body
211
+ self._tmp = None
212
+
213
+ def __enter__(self):
214
+ self._tmp = tempfile.mkdtemp(prefix="omni-fake-sf-")
215
+ sf = Path(self._tmp) / "sf"
216
+ sf.write_text(self._body)
217
+ sf.chmod(0o755)
218
+ return self._tmp
219
+
220
+ def __exit__(self, *exc):
221
+ import shutil
222
+ if self._tmp:
223
+ shutil.rmtree(self._tmp, ignore_errors=True)