@aifabrix/builder 2.52.0 → 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 (134) 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/channels/add-channel.js +126 -19
  10. package/lib/channels/approval-guides/chatgpt.js +1 -1
  11. package/lib/channels/approval-guides/microsoft-copilot.js +16 -12
  12. package/lib/channels/channel-add-ensure.js +128 -0
  13. package/lib/channels/channel-artifact-generator.js +11 -1
  14. package/lib/channels/channel-branding.js +94 -0
  15. package/lib/channels/channel-credential.js +8 -0
  16. package/lib/channels/channel-discovery.js +330 -0
  17. package/lib/channels/channel-external-system.js +1 -1
  18. package/lib/channels/channel-runtime-resolver.js +103 -14
  19. package/lib/channels/channel-status-display.js +7 -1
  20. package/lib/channels/microsoft-copilot-channel-add.js +138 -0
  21. package/lib/channels/microsoft-copilot-entra-sso-form.js +180 -0
  22. package/lib/channels/microsoft-copilot-entra-sso.js +264 -0
  23. package/lib/channels/microsoft-copilot-icons.js +74 -0
  24. package/lib/channels/microsoft-copilot-limits.js +84 -0
  25. package/lib/channels/microsoft-copilot-package.js +130 -49
  26. package/lib/channels/microsoft-copilot-validate.js +197 -0
  27. package/lib/cli/index.js +2 -0
  28. package/lib/cli/infra-guided.js +13 -6
  29. package/lib/cli/setup-infra.js +31 -4
  30. package/lib/commands/channel-add-setup.js +28 -9
  31. package/lib/commands/channel.js +14 -10
  32. package/lib/commands/integration-definition.js +158 -0
  33. package/lib/commands/repair-auth-apply.js +19 -5
  34. package/lib/commands/repair-datasource-sync-operations.js +4 -1
  35. package/lib/commands/role-assistant-test-output.js +200 -0
  36. package/lib/commands/role-assistant.js +125 -166
  37. package/lib/datasource/capability/capability-resolve.js +15 -5
  38. package/lib/datasource/capability/copy-operations.js +14 -4
  39. package/lib/infrastructure/index.js +19 -4
  40. package/lib/integration-definition/apply.js +478 -0
  41. package/lib/integration-definition/canonical-json.js +51 -0
  42. package/lib/integration-definition/emit-suite-bid.js +94 -0
  43. package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
  44. package/lib/integration-definition/enrich-openapi-operations.js +434 -0
  45. package/lib/integration-definition/from-discovery.js +339 -0
  46. package/lib/integration-definition/hash.js +37 -0
  47. package/lib/integration-definition/index.js +37 -0
  48. package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +205 -0
  49. package/lib/integration-definition/resource-type-catalog.js +59 -0
  50. package/lib/integration-definition/system-kind-enum.js +40 -0
  51. package/lib/integration-definition/validate.js +148 -0
  52. package/lib/programmatic/builder-api-request.js +5 -1
  53. package/lib/programmatic/builder-help-governance.js +3 -2
  54. package/lib/programmatic/capability-registry.js +19 -0
  55. package/lib/programmatic/openapi-component-schemas.js +6 -1
  56. package/lib/programmatic/openapi-descriptions.js +40 -0
  57. package/lib/programmatic/openapi-envelope-examples.js +269 -0
  58. package/lib/programmatic/openapi-operation-bindings.js +45 -0
  59. package/lib/programmatic/openapi-request-schemas.js +14 -0
  60. package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
  61. package/lib/programmatic/openapi-result-schemas.js +46 -99
  62. package/lib/programmatic/openapi-schema-descriptions-core.js +7 -4
  63. package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
  64. package/lib/programmatic/openapi-schema-descriptions-operations.js +24 -0
  65. package/lib/programmatic/openapi-schema-helpers.js +9 -9
  66. package/lib/programmatic/openapi-spec.js +11 -1
  67. package/lib/programmatic/orchestration-openapi-meta.js +10 -0
  68. package/lib/programmatic/route-handler-map.js +29 -1
  69. package/lib/programmatic/run-channel-package.js +236 -0
  70. package/lib/programmatic/run-integration-definition.js +69 -0
  71. package/lib/role-assistant/knowledge-sync-logging.js +58 -0
  72. package/lib/role-assistant/knowledge-sync.js +88 -93
  73. package/lib/role-assistant/test-cases-bid.js +155 -0
  74. package/lib/role-assistant/test-cases-evaluate.js +9 -1
  75. package/lib/role-assistant/test-cases-honesty.js +24 -10
  76. package/lib/role-assistant/test-cases.js +28 -0
  77. package/lib/role-assistant/test-runner-bid-apply.js +122 -0
  78. package/lib/role-assistant/test-runner-bid-document.js +278 -0
  79. package/lib/role-assistant/test-runner-workhub-answers.js +245 -20
  80. package/lib/role-assistant/test-runner-workhub.js +100 -18
  81. package/lib/role-assistant/test-suite.js +119 -0
  82. package/lib/schema/application-schema.json +56 -6
  83. package/lib/schema/external-datasource.schema.json +103 -17
  84. package/lib/schema/external-system.schema.json +54 -3
  85. package/lib/schema/integration-definition.schema.json +195 -0
  86. package/lib/utils/authentication-shape.js +6 -0
  87. package/lib/utils/docker-build.js +3 -3
  88. package/lib/utils/help-builder.js +1 -0
  89. package/lib/utils/promote-dev-docker-image.js +97 -18
  90. package/lib/utils/transient-developer-id.js +28 -0
  91. package/package.json +1 -1
  92. package/scripts/pnpm/af.mjs +24 -9
  93. package/scripts/pnpm/help.mjs +1 -1
  94. package/templates/applications/dataplane/application.yaml +3 -2
  95. package/templates/applications/dataplane/env.template +7 -6
  96. package/templates/applications/miso-controller/application.yaml +23 -9
  97. package/templates/applications/miso-controller/env.template +8 -2
  98. package/templates/channels/README.md +11 -1
  99. package/templates/channels/brand/color-192.png +0 -0
  100. package/templates/channels/brand/dark/128.png +0 -0
  101. package/templates/channels/brand/dark/192.png +0 -0
  102. package/templates/channels/brand/dark/256.png +0 -0
  103. package/templates/channels/brand/dark/32.png +0 -0
  104. package/templates/channels/brand/dark/512.png +0 -0
  105. package/templates/channels/brand/dark/64.png +0 -0
  106. package/templates/channels/brand/light/128.png +0 -0
  107. package/templates/channels/brand/light/192.png +0 -0
  108. package/templates/channels/brand/light/256.png +0 -0
  109. package/templates/channels/brand/light/32.png +0 -0
  110. package/templates/channels/brand/light/512.png +0 -0
  111. package/templates/channels/brand/light/64.png +0 -0
  112. package/templates/channels/brand/mono-white/128.png +0 -0
  113. package/templates/channels/brand/mono-white/192.png +0 -0
  114. package/templates/channels/brand/mono-white/256.png +0 -0
  115. package/templates/channels/brand/mono-white/32.png +0 -0
  116. package/templates/channels/brand/mono-white/512.png +0 -0
  117. package/templates/channels/brand/mono-white/64.png +0 -0
  118. package/templates/channels/brand/outline-32.png +0 -0
  119. package/templates/channels/chatgpt/install.template.md.hbs +6 -4
  120. package/templates/channels/chatgpt/integration/README.md +4 -5
  121. package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
  122. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +10 -16
  123. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
  124. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +27 -0
  125. package/templates/channels/microsoft-copilot/install.template.md.hbs +39 -20
  126. package/templates/channels/microsoft-copilot/integration/README.md +2 -3
  127. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
  128. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +7 -7
  129. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +11 -0
  130. package/templates/channels/slack/integration/README.md +2 -4
  131. package/templates/channels/teams-bot/install.template.md.hbs +4 -0
  132. package/templates/channels/teams-bot/integration/README.md +3 -5
  133. package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
  134. /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,25 +76,159 @@ 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
 
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
+
91
232
  function _synthContactFields(hints) {
92
233
  const out = {};
93
234
  if (hints.email) {
@@ -144,12 +285,77 @@ function synthesizeFromCapability(capability, hints) {
144
285
  return Object.keys(out).length > 0 ? out : undefined;
145
286
  }
146
287
 
147
- function synthesizeWaitAnswer(wait, statusRow, objective) {
288
+ function synthesizeFieldAnswer(wait, statusRow, objective, testCase) {
148
289
  const prompt = String(wait.prompt || statusRow?.message || '').trim();
149
290
  const hints = extractObjectiveHints(objective);
150
291
  const fields = parseFieldsFromPrompt(prompt);
151
- if (fields.length > 0) {
152
- 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;
153
359
  }
154
360
  const kind = String(wait.objectRef?.kind || '').trim();
155
361
  const isMissingInput =
@@ -159,18 +365,29 @@ function synthesizeWaitAnswer(wait, statusRow, objective) {
159
365
  if (!isMissingInput) {
160
366
  return undefined;
161
367
  }
162
- const capability = String(wait.questionKey || wait.objectRef?.capabilityKey || '').toLowerCase();
163
- 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));
164
378
  }
165
379
 
166
380
  function resolveRespondValue(testCase, wait, statusRow, objective) {
167
381
  const answers = Array.isArray(testCase.answers) ? testCase.answers : [];
168
- 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);
169
386
  const fromCase = matched ? resolveWorkHubAnswerValue(matched) : undefined;
170
387
  if (fromCase !== undefined) {
171
388
  return fromCase;
172
389
  }
173
- return synthesizeWaitAnswer(wait, statusRow, objective);
390
+ return synthesizeWaitAnswer(wait, statusRow, objective, testCase);
174
391
  }
175
392
 
176
393
  function waitRespondFingerprint(wait) {
@@ -181,7 +398,10 @@ function waitRespondFingerprint(wait) {
181
398
 
182
399
  function alreadyResponded(interactions, fingerprint) {
183
400
  return interactions.some(
184
- (row) => row && row.type === 'respond' && row.fingerprint === fingerprint
401
+ (row) =>
402
+ row &&
403
+ (row.type === 'respond' || row.type === 'respond-skipped') &&
404
+ row.fingerprint === fingerprint
185
405
  );
186
406
  }
187
407
 
@@ -214,6 +434,11 @@ module.exports = {
214
434
  pickAnswerForWait,
215
435
  synthesizeWaitAnswer,
216
436
  extractObjectiveHints,
437
+ resolveCaseSystemKey,
438
+ resolveTargetSystemKey,
439
+ shouldStampSuiteBid,
440
+ resolveSuiteRunId,
441
+ loadCaseBidDocument,
217
442
  resolveRespondValue,
218
443
  waitRespondFingerprint,
219
444
  alreadyResponded,