@aifabrix/builder 2.51.1 → 2.52.1

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 (187) hide show
  1. package/jest.isolated-projects.js +25 -2
  2. package/jest.projects.js +4 -1
  3. package/lib/api/datasources-extended.api.js +8 -8
  4. package/lib/api/keycloak.api.js +92 -0
  5. package/lib/api/token-providers.api.js +77 -0
  6. package/lib/api/types/keycloak.types.js +23 -0
  7. package/lib/api/types/token-providers.types.js +65 -0
  8. package/lib/app/down.js +16 -1
  9. package/lib/app/run-container-start.js +4 -0
  10. package/lib/app/run-deploy-job.js +135 -27
  11. package/lib/app/run-env-compose.js +32 -0
  12. package/lib/app/run-env-recovery.js +35 -0
  13. package/lib/app/run-helpers.js +5 -6
  14. package/lib/app/run.js +11 -3
  15. package/lib/channels/add-channel.js +126 -19
  16. package/lib/channels/approval-guides/chatgpt.js +12 -9
  17. package/lib/channels/approval-guides/microsoft-copilot.js +16 -12
  18. package/lib/channels/channel-add-ensure.js +128 -0
  19. package/lib/channels/channel-artifact-generator.js +11 -1
  20. package/lib/channels/channel-branding.js +94 -0
  21. package/lib/channels/channel-credential.js +8 -0
  22. package/lib/channels/channel-discovery.js +330 -0
  23. package/lib/channels/channel-external-system.js +1 -1
  24. package/lib/channels/channel-runtime-resolver.js +103 -14
  25. package/lib/channels/channel-status-display.js +7 -1
  26. package/lib/channels/channel-targets.js +18 -1
  27. package/lib/channels/chatgpt-runtime-contract.js +1 -1
  28. package/lib/channels/microsoft-copilot-channel-add.js +138 -0
  29. package/lib/channels/microsoft-copilot-entra-sso-form.js +180 -0
  30. package/lib/channels/microsoft-copilot-entra-sso.js +264 -0
  31. package/lib/channels/microsoft-copilot-icons.js +74 -0
  32. package/lib/channels/microsoft-copilot-limits.js +84 -0
  33. package/lib/channels/microsoft-copilot-package.js +130 -49
  34. package/lib/channels/microsoft-copilot-validate.js +197 -0
  35. package/lib/cli/index.js +2 -0
  36. package/lib/cli/infra-guided-footers.js +1 -1
  37. package/lib/cli/infra-guided-public-url-warn.js +55 -0
  38. package/lib/cli/infra-guided.js +78 -62
  39. package/lib/cli/setup-app.help.js +4 -0
  40. package/lib/cli/setup-app.test-commands.js +59 -28
  41. package/lib/cli/setup-external-system.js +50 -12
  42. package/lib/cli/setup-infra.js +31 -4
  43. package/lib/commands/channel-add-setup.js +35 -10
  44. package/lib/commands/channel.js +27 -12
  45. package/lib/commands/deploy-gate-orchestrator.js +3 -2
  46. package/lib/commands/governance-verify-external.js +2 -5
  47. package/lib/commands/integration-definition.js +158 -0
  48. package/lib/commands/lifecycle-external.js +14 -7
  49. package/lib/commands/repair-auth-apply.js +19 -5
  50. package/lib/commands/repair-datasource-sync-operations.js +4 -1
  51. package/lib/commands/role-assistant-test-output.js +200 -0
  52. package/lib/commands/role-assistant.js +131 -114
  53. package/lib/commands/test-governance-external.js +2 -5
  54. package/lib/commands/up-miso.js +61 -24
  55. package/lib/commands/upload-data-sync-finalize.js +20 -3
  56. package/lib/commands/verify-governance-external.js +2 -5
  57. package/lib/commands/verify-operations-external.js +2 -5
  58. package/lib/commands/verify-operations-step-plan.js +31 -0
  59. package/lib/commands/verify-trust-command-action.js +2 -5
  60. package/lib/datasource/capability/capability-resolve.js +15 -5
  61. package/lib/datasource/capability/copy-operations.js +14 -4
  62. package/lib/datasource/datasource-identifier.js +4 -3
  63. package/lib/datasource/integration-context.js +3 -3
  64. package/lib/datasource/resolve-app.js +90 -8
  65. package/lib/datasource/test-e2e.js +2 -2
  66. package/lib/datasource/unified-validation-run-body.js +15 -1
  67. package/lib/datasource/unified-validation-run-resolve.js +2 -2
  68. package/lib/external-system/deploy-helpers.js +9 -5
  69. package/lib/external-system/deploy.js +2 -1
  70. package/lib/external-system/test-system-level.js +21 -3
  71. package/lib/external-system/test.js +4 -4
  72. package/lib/generator/builders.js +9 -15
  73. package/lib/infrastructure/index.js +19 -4
  74. package/lib/integration-definition/apply.js +478 -0
  75. package/lib/integration-definition/canonical-json.js +51 -0
  76. package/lib/integration-definition/emit-suite-bid.js +94 -0
  77. package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
  78. package/lib/integration-definition/enrich-openapi-operations.js +434 -0
  79. package/lib/integration-definition/from-discovery.js +339 -0
  80. package/lib/integration-definition/hash.js +37 -0
  81. package/lib/integration-definition/index.js +37 -0
  82. package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +205 -0
  83. package/lib/integration-definition/resource-type-catalog.js +59 -0
  84. package/lib/integration-definition/system-kind-enum.js +40 -0
  85. package/lib/integration-definition/validate.js +148 -0
  86. package/lib/programmatic/builder-api-request.js +5 -1
  87. package/lib/programmatic/builder-help-governance.js +3 -2
  88. package/lib/programmatic/capability-registry.js +19 -0
  89. package/lib/programmatic/openapi-component-schemas.js +6 -1
  90. package/lib/programmatic/openapi-descriptions.js +40 -0
  91. package/lib/programmatic/openapi-envelope-examples.js +269 -0
  92. package/lib/programmatic/openapi-operation-bindings.js +45 -0
  93. package/lib/programmatic/openapi-request-schemas.js +14 -0
  94. package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
  95. package/lib/programmatic/openapi-result-schemas.js +46 -99
  96. package/lib/programmatic/openapi-schema-descriptions-core.js +7 -4
  97. package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
  98. package/lib/programmatic/openapi-schema-descriptions-operations.js +24 -0
  99. package/lib/programmatic/openapi-schema-helpers.js +9 -9
  100. package/lib/programmatic/openapi-spec.js +11 -1
  101. package/lib/programmatic/orchestration-openapi-meta.js +10 -0
  102. package/lib/programmatic/route-handler-map.js +29 -1
  103. package/lib/programmatic/run-channel-package.js +236 -0
  104. package/lib/programmatic/run-integration-definition.js +69 -0
  105. package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
  106. package/lib/role-assistant/knowledge-sync-logging.js +58 -0
  107. package/lib/role-assistant/knowledge-sync.js +104 -89
  108. package/lib/role-assistant/test-cases-bid.js +155 -0
  109. package/lib/role-assistant/test-cases-evaluate.js +9 -1
  110. package/lib/role-assistant/test-cases-honesty.js +24 -10
  111. package/lib/role-assistant/test-cases.js +28 -0
  112. package/lib/role-assistant/test-runner-bid-apply.js +122 -0
  113. package/lib/role-assistant/test-runner-bid-document.js +278 -0
  114. package/lib/role-assistant/test-runner-workhub-answers.js +289 -46
  115. package/lib/role-assistant/test-runner-workhub.js +121 -25
  116. package/lib/role-assistant/test-suite.js +119 -0
  117. package/lib/schema/application-schema.json +64 -32
  118. package/lib/schema/external-datasource.schema.json +103 -17
  119. package/lib/schema/external-system.schema.json +54 -3
  120. package/lib/schema/infra.parameter.yaml +0 -20
  121. package/lib/schema/integration-definition.schema.json +195 -0
  122. package/lib/utils/authentication-shape.js +6 -0
  123. package/lib/utils/certify-external-workspace.js +40 -0
  124. package/lib/utils/docker-build.js +3 -3
  125. package/lib/utils/docker-run-ephemeral.js +77 -17
  126. package/lib/utils/health-check-public-warn.js +31 -13
  127. package/lib/utils/health-check.js +11 -1
  128. package/lib/utils/help-builder.js +1 -0
  129. package/lib/utils/paths.js +13 -15
  130. package/lib/utils/promote-dev-docker-image.js +97 -18
  131. package/lib/utils/role-assistant-paths.js +76 -14
  132. package/lib/utils/transient-developer-id.js +28 -0
  133. package/lib/utils/upload-sync-options.js +7 -4
  134. package/lib/validation/reserved-system-key-prefixes.js +56 -0
  135. package/lib/validation/validate.js +54 -19
  136. package/package.json +1 -1
  137. package/scripts/pnpm/af.mjs +24 -9
  138. package/scripts/pnpm/help.mjs +1 -1
  139. package/templates/applications/builder-api/application.yaml +3 -3
  140. package/templates/applications/dataplane/application.yaml +5 -5
  141. package/templates/applications/dataplane/env.template +8 -7
  142. package/templates/applications/dataplane/rbac.yaml +10 -10
  143. package/templates/applications/keycloak/application.yaml +2 -2
  144. package/templates/applications/keycloak/env.template +5 -2
  145. package/templates/applications/miso-controller/application.yaml +25 -11
  146. package/templates/applications/miso-controller/env.template +24 -5
  147. package/templates/channels/README.md +11 -1
  148. package/templates/channels/brand/color-192.png +0 -0
  149. package/templates/channels/brand/dark/128.png +0 -0
  150. package/templates/channels/brand/dark/192.png +0 -0
  151. package/templates/channels/brand/dark/256.png +0 -0
  152. package/templates/channels/brand/dark/32.png +0 -0
  153. package/templates/channels/brand/dark/512.png +0 -0
  154. package/templates/channels/brand/dark/64.png +0 -0
  155. package/templates/channels/brand/light/128.png +0 -0
  156. package/templates/channels/brand/light/192.png +0 -0
  157. package/templates/channels/brand/light/256.png +0 -0
  158. package/templates/channels/brand/light/32.png +0 -0
  159. package/templates/channels/brand/light/512.png +0 -0
  160. package/templates/channels/brand/light/64.png +0 -0
  161. package/templates/channels/brand/mono-white/128.png +0 -0
  162. package/templates/channels/brand/mono-white/192.png +0 -0
  163. package/templates/channels/brand/mono-white/256.png +0 -0
  164. package/templates/channels/brand/mono-white/32.png +0 -0
  165. package/templates/channels/brand/mono-white/512.png +0 -0
  166. package/templates/channels/brand/mono-white/64.png +0 -0
  167. package/templates/channels/brand/outline-32.png +0 -0
  168. package/templates/channels/chatgpt/install.template.md.hbs +76 -36
  169. package/templates/channels/chatgpt/integration/README.md +4 -4
  170. package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
  171. package/templates/channels/chatgpt/manifest.template.json.hbs +3 -3
  172. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +10 -16
  173. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
  174. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +27 -0
  175. package/templates/channels/microsoft-copilot/install.template.md.hbs +39 -20
  176. package/templates/channels/microsoft-copilot/integration/README.md +2 -3
  177. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
  178. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +7 -7
  179. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +11 -0
  180. package/templates/channels/slack/install.template.md.hbs +6 -1
  181. package/templates/channels/slack/integration/README.md +2 -4
  182. package/templates/channels/teams-bot/install.template.md.hbs +4 -0
  183. package/templates/channels/teams-bot/integration/README.md +3 -5
  184. package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
  185. package/templates/python/docker-compose.hbs +10 -0
  186. package/templates/typescript/docker-compose.hbs +10 -0
  187. /package/.cursor/plans/{Done → Archive}/157.1-security-quality-violations-by-type.csv +0 -0
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Suite BID load / discovery emit for Work Hub apply waits (512.4 GP).
3
+ * @fileoverview Case BID template + discovery compile + suite stamp
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const fs = require('../internal/fs-real-sync');
9
+ const path = require('path');
10
+
11
+ /**
12
+ * @param {Object} testCase
13
+ * @returns {string}
14
+ */
15
+ function resolveTargetSystemKey(testCase) {
16
+ const input =
17
+ testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
18
+ for (const key of ['targetSystemKey', 'integrationSystemKey', 'systemKey']) {
19
+ const value = input[key];
20
+ if (typeof value === 'string' && value.trim()) {
21
+ return value.trim();
22
+ }
23
+ }
24
+ return '';
25
+ }
26
+
27
+ /**
28
+ * @param {Object} testCase
29
+ * @returns {boolean}
30
+ */
31
+ function shouldStampSuiteBid(testCase) {
32
+ const input =
33
+ testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
34
+ if (input.stampSuiteBid === true) {
35
+ return true;
36
+ }
37
+ if (input.stampSuiteBid === false) {
38
+ return false;
39
+ }
40
+ const expected =
41
+ testCase && typeof testCase.expected === 'object' && testCase.expected
42
+ ? testCase.expected
43
+ : {};
44
+ return expected.soft === false;
45
+ }
46
+
47
+ /**
48
+ * @param {Object} testCase
49
+ * @returns {string}
50
+ */
51
+ function resolveSuiteRunId(testCase) {
52
+ const fromActive = String(testCase?._activeProcessExecutionId || '').trim();
53
+ if (fromActive) {
54
+ return fromActive;
55
+ }
56
+ const input =
57
+ testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
58
+ return String(input.suiteRunId || '').trim();
59
+ }
60
+
61
+ /**
62
+ * @param {Object} testCase
63
+ * @param {Object} bid
64
+ */
65
+ function maybePersistEmittedBid(testCase, bid) {
66
+ const input =
67
+ testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
68
+ const outPath = typeof input.emittedBidPath === 'string' ? input.emittedBidPath.trim() : '';
69
+ if (!outPath || !bid) {
70
+ return;
71
+ }
72
+ const baseDir =
73
+ typeof testCase.sourceFile === 'string' && testCase.sourceFile.trim()
74
+ ? path.dirname(testCase.sourceFile)
75
+ : process.cwd();
76
+ const abs = path.isAbsolute(outPath) ? outPath : path.resolve(baseDir, outPath);
77
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
78
+ fs.writeFileSync(abs, `${JSON.stringify(bid, null, 2)}\n`, 'utf8');
79
+ }
80
+
81
+ /**
82
+ * @param {Object} testCase
83
+ * @returns {Object|undefined}
84
+ */
85
+ function loadBidTemplate(testCase) {
86
+ const input =
87
+ testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
88
+ const bidPath = typeof input.bidPath === 'string' ? input.bidPath.trim() : '';
89
+ if (!bidPath) {
90
+ return undefined;
91
+ }
92
+ const baseDir =
93
+ typeof testCase.sourceFile === 'string' && testCase.sourceFile.trim()
94
+ ? path.dirname(testCase.sourceFile)
95
+ : process.cwd();
96
+ const abs = path.isAbsolute(bidPath) ? bidPath : path.resolve(baseDir, bidPath);
97
+ if (!fs.existsSync(abs)) {
98
+ return undefined;
99
+ }
100
+ try {
101
+ const parsed = JSON.parse(fs.readFileSync(abs, 'utf8'));
102
+ return parsed && typeof parsed === 'object' ? parsed : undefined;
103
+ } catch (_err) {
104
+ return undefined;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * @param {Object} testCase
110
+ * @returns {boolean}
111
+ */
112
+ function shouldEmitBidFromAnswers(testCase) {
113
+ const input =
114
+ testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
115
+ if (input.emitBidFromAnswers === false) {
116
+ return false;
117
+ }
118
+ if (!shouldStampSuiteBid(testCase) && input.emitBidFromAnswers !== true) {
119
+ return false;
120
+ }
121
+ const answers = Array.isArray(testCase.answers) ? testCase.answers : [];
122
+ return answers.some((entry) => {
123
+ const key = String(entry?.when?.questionKey || '')
124
+ .trim()
125
+ .toLowerCase();
126
+ return key === 'system.apigroups';
127
+ });
128
+ }
129
+
130
+ /**
131
+ * @param {Object} testCase
132
+ * @returns {string|undefined}
133
+ */
134
+ function resolveAssistantKey(testCase) {
135
+ const input =
136
+ testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
137
+ if (typeof input.digitalWorkerKey === 'string' && input.digitalWorkerKey.trim()) {
138
+ return input.digitalWorkerKey.trim();
139
+ }
140
+ if (typeof input.businessRoleRef === 'string' && input.businessRoleRef.trim()) {
141
+ return input.businessRoleRef.trim();
142
+ }
143
+ return undefined;
144
+ }
145
+
146
+ function resolveConnectionRefs(input, template) {
147
+ if (input.connectionRefs && typeof input.connectionRefs === 'object') {
148
+ return input.connectionRefs;
149
+ }
150
+ if (template && template.system && template.system.connectionRefs) {
151
+ return template.system.connectionRefs;
152
+ }
153
+ return undefined;
154
+ }
155
+
156
+ function packageField(template, field, fallback) {
157
+ const identity = template && template.packageIdentity;
158
+ if (identity && identity[field]) {
159
+ return identity[field];
160
+ }
161
+ return fallback;
162
+ }
163
+
164
+ function buildDiscoveryEmitOptions(testCase, suiteRunId, template, systemKey) {
165
+ const input =
166
+ testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
167
+ const openapiSourceRefs =
168
+ input.openapiSourceRefs && typeof input.openapiSourceRefs === 'object'
169
+ ? input.openapiSourceRefs
170
+ : undefined;
171
+ const knowledgePackRefs =
172
+ template && template.meta && Array.isArray(template.meta.knowledgePackRefs)
173
+ ? template.meta.knowledgePackRefs
174
+ : undefined;
175
+ return {
176
+ answers: testCase.answers,
177
+ systemKey,
178
+ displayName: packageField(template, 'displayName', systemKey),
179
+ packageVersion: packageField(template, 'packageVersion', '0.1.0'),
180
+ systemKind: (template && template.system && template.system.systemKind) || 'crm',
181
+ connectionRefs: resolveConnectionRefs(input, template),
182
+ openapiSourceRefs,
183
+ suiteRunId,
184
+ definitionId: template && template.definitionId,
185
+ btAssistantKey: resolveAssistantKey(testCase),
186
+ knowledgePackRefs
187
+ };
188
+ }
189
+
190
+ /**
191
+ * @param {Object} testCase
192
+ * @param {string} suiteRunId
193
+ * @param {Object|undefined} template
194
+ * @returns {Object|undefined}
195
+ */
196
+ function emitBidFromCaseAnswers(testCase, suiteRunId, template) {
197
+ const systemKey = resolveTargetSystemKey(testCase);
198
+ if (!systemKey) {
199
+ return undefined;
200
+ }
201
+ const { bidFromDiscovery } = require('../integration-definition/from-discovery');
202
+ return bidFromDiscovery(buildDiscoveryEmitOptions(testCase, suiteRunId, template, systemKey));
203
+ }
204
+
205
+ function cachedEmittedBid(testCase, suiteRunId) {
206
+ if (
207
+ testCase._emittedBid &&
208
+ typeof testCase._emittedBid === 'object' &&
209
+ testCase._emittedBid.meta &&
210
+ testCase._emittedBid.meta.suiteRunId === suiteRunId
211
+ ) {
212
+ return testCase._emittedBid;
213
+ }
214
+ return null;
215
+ }
216
+
217
+ function tryEmitFromAnswers(testCase, suiteRunId, template) {
218
+ if (!shouldEmitBidFromAnswers(testCase)) {
219
+ return null;
220
+ }
221
+ try {
222
+ const emitted = emitBidFromCaseAnswers(testCase, suiteRunId, template);
223
+ if (emitted) {
224
+ testCase._emittedBid = emitted;
225
+ maybePersistEmittedBid(testCase, emitted);
226
+ return emitted;
227
+ }
228
+ } catch (_err) {
229
+ // Fall through to template stamp.
230
+ }
231
+ return null;
232
+ }
233
+
234
+ /**
235
+ * Load BID for apply: discovery-compiled (preferred) or stamped template.
236
+ * @param {Object} testCase
237
+ * @returns {Object|undefined}
238
+ */
239
+ function loadCaseBidDocument(testCase) {
240
+ const input =
241
+ testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
242
+ const template = loadBidTemplate(testCase);
243
+ if (!shouldStampSuiteBid(testCase) && input.emitBidFromAnswers !== true) {
244
+ return template;
245
+ }
246
+ const suiteRunId = resolveSuiteRunId(testCase);
247
+ if (!suiteRunId) {
248
+ return template;
249
+ }
250
+ const cached = cachedEmittedBid(testCase, suiteRunId);
251
+ if (cached) {
252
+ return cached;
253
+ }
254
+ const fromAnswers = tryEmitFromAnswers(testCase, suiteRunId, template);
255
+ if (fromAnswers) {
256
+ return fromAnswers;
257
+ }
258
+ if (!template) {
259
+ return undefined;
260
+ }
261
+ const { emitSuiteBid } = require('../integration-definition/emit-suite-bid');
262
+ const emitted = emitSuiteBid(template, {
263
+ suiteRunId,
264
+ btAssistantKey: resolveAssistantKey(testCase),
265
+ answers: testCase.answers
266
+ });
267
+ testCase._emittedBid = emitted;
268
+ maybePersistEmittedBid(testCase, emitted);
269
+ return emitted;
270
+ }
271
+
272
+ module.exports = {
273
+ resolveTargetSystemKey,
274
+ shouldStampSuiteBid,
275
+ resolveSuiteRunId,
276
+ loadCaseBidDocument,
277
+ shouldEmitBidFromAnswers
278
+ };
@@ -5,6 +5,13 @@
5
5
 
6
6
  'use strict';
7
7
 
8
+ const {
9
+ resolveTargetSystemKey,
10
+ shouldStampSuiteBid,
11
+ resolveSuiteRunId,
12
+ loadCaseBidDocument
13
+ } = require('./test-runner-bid-document');
14
+
8
15
  function resolveWorkHubAnswerValue(answer) {
9
16
  if (answer === null || answer === undefined) {
10
17
  return undefined;
@@ -69,69 +76,286 @@ function extractObjectiveHints(objective) {
69
76
  return { email, domain, name, address };
70
77
  }
71
78
 
72
- function fillFieldsFromHints(fields, hints) {
79
+ function resolveCaseSystemKey(testCase) {
80
+ const input =
81
+ testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
82
+ for (const key of ['digitalWorkerKey', 'systemKey', 'businessRoleRef']) {
83
+ const value = input[key];
84
+ if (typeof value === 'string' && value.trim()) {
85
+ return value.trim();
86
+ }
87
+ }
88
+ return '';
89
+ }
90
+
91
+ /**
92
+ * Target Connected System key for workspace/template lifecycle (not the RA package key).
93
+ */
94
+ /**
95
+ * Capability identity for a wait. Prefer structured keys; fall back to prompt
96
+ * text so "Missing … for project.runupload (fields: systemKey)" still routes
97
+ * to the target integration key (not the Role Assistant package key).
98
+ * @param {Object} wait
99
+ * @returns {string}
100
+ */
101
+ function resolveWaitCapabilityText(wait) {
102
+ return String(
103
+ wait?.questionKey ||
104
+ wait?.objectRef?.capabilityKey ||
105
+ wait?.requestKey ||
106
+ wait?.prompt ||
107
+ ''
108
+ ).toLowerCase();
109
+ }
110
+
111
+ function isTargetIntegrationLifecycleCapability(capability) {
112
+ const text = String(capability || '').toLowerCase();
113
+ return (
114
+ text.includes('workspace') ||
115
+ text.includes('createsystem') ||
116
+ text.includes('template') ||
117
+ text.includes('applyintegrationdefinition') ||
118
+ text.includes('wizard') ||
119
+ text.includes('repair') ||
120
+ text.includes('validate') ||
121
+ text.includes('upload') ||
122
+ text.includes('project.') ||
123
+ text.includes('document.') ||
124
+ text.includes('certifypillar') ||
125
+ text.includes('approval.')
126
+ );
127
+ }
128
+
129
+ function resolveCaseCertifyPillar(testCase) {
130
+ const input =
131
+ testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
132
+ const raw = input.certifyPillar || input.pillar;
133
+ if (typeof raw === 'string' && raw.trim()) {
134
+ return raw.trim();
135
+ }
136
+ return 'operations';
137
+ }
138
+
139
+ /**
140
+ * @param {Object} wait
141
+ * @param {Object} testCase
142
+ * @param {string} systemKey
143
+ * @returns {Object|undefined}
144
+ */
145
+ function synthesizeCertifyAnswer(wait, testCase, systemKey) {
146
+ if (!systemKey) {
147
+ return undefined;
148
+ }
149
+ const capability = resolveWaitCapabilityText(wait);
150
+ if (!capability.includes('certifypillar') && !capability.includes('certify')) {
151
+ return undefined;
152
+ }
153
+ return { systemKey, pillar: resolveCaseCertifyPillar(testCase) };
154
+ }
155
+
156
+ function isBidApplyWait(wait) {
157
+ const capability = resolveWaitCapabilityText(wait);
158
+ return (
159
+ capability.includes('applyintegrationdefinition') ||
160
+ capability.includes('integrationdefinition') ||
161
+ capability.includes('integration-definition')
162
+ );
163
+ }
164
+
165
+ function resolveSystemKeyForWait(testCase, wait) {
166
+ const capability = resolveWaitCapabilityText(wait);
167
+ const target = resolveTargetSystemKey(testCase);
168
+ if (target && isTargetIntegrationLifecycleCapability(capability)) {
169
+ return target;
170
+ }
171
+ return resolveCaseSystemKey(testCase);
172
+ }
173
+
174
+ function valueFromHintField(lower, hints, testCase, systemKey) {
175
+ if (hints[lower]) {
176
+ return hints[lower];
177
+ }
178
+ if (lower === 'systemkey' && systemKey) {
179
+ return systemKey;
180
+ }
181
+ if (lower === 'pillar') {
182
+ return resolveCaseCertifyPillar(testCase);
183
+ }
184
+ if (lower === 'force') {
185
+ return true;
186
+ }
187
+ if (lower.includes('mail') && hints.email) {
188
+ return hints.email;
189
+ }
190
+ if (lower.includes('name') && hints.name) {
191
+ return hints.name;
192
+ }
193
+ if (lower.includes('domain') && hints.domain) {
194
+ return hints.domain;
195
+ }
196
+ if (lower.includes('address') && hints.address) {
197
+ return hints.address;
198
+ }
199
+ return undefined;
200
+ }
201
+
202
+ function fillFieldsFromHints(fields, hints, testCase, wait) {
73
203
  const out = {};
204
+ const systemKey = resolveSystemKeyForWait(testCase, wait);
74
205
  for (const key of fields) {
75
- const lower = key.toLowerCase();
76
- if (hints[lower]) {
77
- out[key] = hints[lower];
78
- } else if (lower.includes('mail') && hints.email) {
79
- out[key] = hints.email;
80
- } else if (lower.includes('name') && hints.name) {
81
- out[key] = hints.name;
82
- } else if (lower.includes('domain') && hints.domain) {
83
- out[key] = hints.domain;
84
- } else if (lower.includes('address') && hints.address) {
85
- out[key] = hints.address;
206
+ const value = valueFromHintField(key.toLowerCase(), hints, testCase, systemKey);
207
+ if (value !== undefined) {
208
+ out[key] = value;
86
209
  }
87
210
  }
88
211
  return Object.keys(out).length > 0 ? out : undefined;
89
212
  }
90
213
 
91
- function synthesizeFromCapability(capability, hints) {
214
+ /**
215
+ * Soft-loop upload often rewrites BID-shaped schemas over prior packages.
216
+ * Include force so project.runupload can replace non-overlapping attributes.
217
+ * @param {Object} wait
218
+ * @param {string} systemKey
219
+ * @returns {Object|undefined}
220
+ */
221
+ function synthesizeUploadAnswer(wait, systemKey) {
222
+ if (!systemKey || !isTargetIntegrationLifecycleCapability(resolveWaitCapabilityText(wait))) {
223
+ return undefined;
224
+ }
225
+ const capability = resolveWaitCapabilityText(wait);
226
+ if (!capability.includes('upload') && !capability.includes('project.runupload')) {
227
+ return undefined;
228
+ }
229
+ return { systemKey, force: true };
230
+ }
231
+
232
+ function _synthContactFields(hints) {
92
233
  const out = {};
234
+ if (hints.email) {
235
+ out.email = hints.email;
236
+ }
237
+ if (hints.name || hints.email) {
238
+ out.firstname = String(hints.email || 'user').split('@')[0];
239
+ out.lastname = hints.name || 'Contact';
240
+ }
241
+ return out;
242
+ }
243
+
244
+ function _synthCustomerFields(hints) {
245
+ const out = {};
246
+ if (hints.name) {
247
+ out.name = hints.name;
248
+ }
249
+ if (hints.domain) {
250
+ out.domain = hints.domain;
251
+ }
252
+ if (hints.address) {
253
+ out.address = hints.address;
254
+ }
255
+ return out;
256
+ }
257
+
258
+ function _synthDefaultFields(hints) {
259
+ const out = {};
260
+ if (hints.name) {
261
+ out.name = hints.name;
262
+ }
263
+ if (hints.email) {
264
+ out.email = hints.email;
265
+ }
266
+ if (hints.domain) {
267
+ out.domain = hints.domain;
268
+ }
269
+ return out;
270
+ }
271
+
272
+ function synthesizeFromCapability(capability, hints) {
273
+ let out = {};
93
274
  if (capability.includes('contact')) {
94
- if (hints.email) {
95
- out.email = hints.email;
96
- }
97
- if (hints.name || hints.email) {
98
- out.firstname = String(hints.email || 'user').split('@')[0];
99
- out.lastname = hints.name || 'Contact';
100
- }
275
+ out = _synthContactFields(hints);
101
276
  } else if (capability.includes('deal')) {
102
277
  if (hints.name) {
103
278
  out.dealname = hints.name;
104
279
  }
105
280
  } else if (capability.includes('customer') || capability.includes('compan')) {
106
- if (hints.name) {
107
- out.name = hints.name;
108
- }
109
- if (hints.domain) {
110
- out.domain = hints.domain;
111
- }
112
- if (hints.address) {
113
- out.address = hints.address;
114
- }
281
+ out = _synthCustomerFields(hints);
115
282
  } else {
116
- if (hints.name) {
117
- out.name = hints.name;
118
- }
119
- if (hints.email) {
120
- out.email = hints.email;
121
- }
122
- if (hints.domain) {
123
- out.domain = hints.domain;
124
- }
283
+ out = _synthDefaultFields(hints);
125
284
  }
126
285
  return Object.keys(out).length > 0 ? out : undefined;
127
286
  }
128
287
 
129
- function synthesizeWaitAnswer(wait, statusRow, objective) {
288
+ function synthesizeFieldAnswer(wait, statusRow, objective, testCase) {
130
289
  const prompt = String(wait.prompt || statusRow?.message || '').trim();
131
290
  const hints = extractObjectiveHints(objective);
132
291
  const fields = parseFieldsFromPrompt(prompt);
133
- if (fields.length > 0) {
134
- return fillFieldsFromHints(fields, hints);
292
+ const filled = fields.length > 0 ? fillFieldsFromHints(fields, hints, testCase, wait) : undefined;
293
+ if (filled === undefined) {
294
+ return undefined;
295
+ }
296
+ const targetKey = filled.systemKey || resolveTargetSystemKey(testCase);
297
+ const uploadExtra = synthesizeUploadAnswer(wait, targetKey);
298
+ const certifyExtra = synthesizeCertifyAnswer(wait, testCase, targetKey);
299
+ return uploadExtra || certifyExtra ? { ...filled, ...(uploadExtra || {}), ...(certifyExtra || {}) } : filled;
300
+ }
301
+
302
+ function synthesizePathParameterAnswer(wait, prompt, kind, testCase) {
303
+ if (kind !== 'missingPathParams' && !/missing required path params/i.test(prompt)) {
304
+ return undefined;
305
+ }
306
+ const targetSystemKey = resolveTargetSystemKey(testCase);
307
+ if (targetSystemKey && isTargetIntegrationLifecycleCapability(resolveWaitCapabilityText(wait))) {
308
+ return (
309
+ synthesizeUploadAnswer(wait, targetSystemKey) ||
310
+ synthesizeCertifyAnswer(wait, testCase, targetSystemKey) ||
311
+ { systemKey: targetSystemKey }
312
+ );
313
+ }
314
+ const pathSystemKey = resolveCaseSystemKey(testCase);
315
+ return pathSystemKey ? { systemKey: pathSystemKey } : undefined;
316
+ }
317
+
318
+ function synthesizeBidApplyAnswer(wait, prompt, kind, testCase) {
319
+ if (!isBidApplyWait(wait)) {
320
+ return undefined;
321
+ }
322
+ const wantsBody = kind === 'missingRequestBody' || /missing required request body/i.test(prompt);
323
+ const wantsPath = kind === 'missingPathParams' || /missing required path params/i.test(prompt);
324
+ const bid = loadCaseBidDocument(testCase);
325
+ if (!bid || (!wantsBody && !wantsPath)) {
326
+ return undefined;
327
+ }
328
+ const out = wantsBody ? { integrationDefinition: bid } : {};
329
+ const targetSystemKey = resolveTargetSystemKey(testCase);
330
+ if (targetSystemKey) {
331
+ out.systemKey = targetSystemKey;
332
+ }
333
+ return Object.keys(out).length > 0 ? out : undefined;
334
+ }
335
+
336
+ function synthesizeTargetSystemKeyAnswer(wait, prompt, kind, testCase) {
337
+ const targetSystemKey = resolveTargetSystemKey(testCase);
338
+ const needsBody = kind === 'missingRequestBody' || /missing required request body/i.test(prompt);
339
+ if (!targetSystemKey || (!needsBody && !/systemKey is required/i.test(prompt))) {
340
+ return undefined;
341
+ }
342
+ const capability = resolveWaitCapabilityText(wait);
343
+ const supportsTarget =
344
+ capability.includes('workspace') ||
345
+ capability.includes('createsystem') ||
346
+ (capability.includes('template') && !isBidApplyWait(wait)) ||
347
+ capability.includes('wizard') ||
348
+ capability.includes('upload') ||
349
+ capability.includes('project.') ||
350
+ /systemkey is required/i.test(prompt);
351
+ return supportsTarget ? { systemKey: targetSystemKey } : undefined;
352
+ }
353
+
354
+ function synthesizeWaitAnswer(wait, statusRow, objective, testCase) {
355
+ const prompt = String(wait.prompt || statusRow?.message || '').trim();
356
+ const fieldAnswer = synthesizeFieldAnswer(wait, statusRow, objective, testCase);
357
+ if (fieldAnswer !== undefined) {
358
+ return fieldAnswer;
135
359
  }
136
360
  const kind = String(wait.objectRef?.kind || '').trim();
137
361
  const isMissingInput =
@@ -141,18 +365,29 @@ function synthesizeWaitAnswer(wait, statusRow, objective) {
141
365
  if (!isMissingInput) {
142
366
  return undefined;
143
367
  }
144
- const capability = String(wait.questionKey || wait.objectRef?.capabilityKey || '').toLowerCase();
145
- return synthesizeFromCapability(capability, hints);
368
+ const pathAnswer = synthesizePathParameterAnswer(wait, prompt, kind, testCase);
369
+ if (pathAnswer !== undefined) {
370
+ return pathAnswer;
371
+ }
372
+ const bidAnswer = synthesizeBidApplyAnswer(wait, prompt, kind, testCase);
373
+ if (bidAnswer !== undefined) {
374
+ return bidAnswer;
375
+ }
376
+ const targetAnswer = synthesizeTargetSystemKeyAnswer(wait, prompt, kind, testCase);
377
+ return targetAnswer || synthesizeFromCapability(resolveWaitCapabilityText(wait), extractObjectiveHints(objective));
146
378
  }
147
379
 
148
380
  function resolveRespondValue(testCase, wait, statusRow, objective) {
149
381
  const answers = Array.isArray(testCase.answers) ? testCase.answers : [];
150
- const matched = pickAnswerForWait(answers, wait) || answers[0] || null;
382
+ // Only use an answer whose when.questionKey matches this wait.
383
+ // Never fall back to answers[0] — that poisons capability waits (e.g. task.runtest)
384
+ // with unrelated discovery payloads (system.apiGroups).
385
+ const matched = pickAnswerForWait(answers, wait);
151
386
  const fromCase = matched ? resolveWorkHubAnswerValue(matched) : undefined;
152
387
  if (fromCase !== undefined) {
153
388
  return fromCase;
154
389
  }
155
- return synthesizeWaitAnswer(wait, statusRow, objective);
390
+ return synthesizeWaitAnswer(wait, statusRow, objective, testCase);
156
391
  }
157
392
 
158
393
  function waitRespondFingerprint(wait) {
@@ -163,7 +398,10 @@ function waitRespondFingerprint(wait) {
163
398
 
164
399
  function alreadyResponded(interactions, fingerprint) {
165
400
  return interactions.some(
166
- (row) => row && row.type === 'respond' && row.fingerprint === fingerprint
401
+ (row) =>
402
+ row &&
403
+ (row.type === 'respond' || row.type === 'respond-skipped') &&
404
+ row.fingerprint === fingerprint
167
405
  );
168
406
  }
169
407
 
@@ -196,6 +434,11 @@ module.exports = {
196
434
  pickAnswerForWait,
197
435
  synthesizeWaitAnswer,
198
436
  extractObjectiveHints,
437
+ resolveCaseSystemKey,
438
+ resolveTargetSystemKey,
439
+ shouldStampSuiteBid,
440
+ resolveSuiteRunId,
441
+ loadCaseBidDocument,
199
442
  resolveRespondValue,
200
443
  waitRespondFingerprint,
201
444
  alreadyResponded,