@aifabrix/builder 2.52.0 → 2.53.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 (310) hide show
  1. package/lib/api/datasources-extended.api.js +8 -8
  2. package/lib/api/keycloak.api.js +92 -0
  3. package/lib/api/token-providers.api.js +77 -0
  4. package/lib/api/types/keycloak.types.js +23 -0
  5. package/lib/api/types/token-providers.types.js +65 -0
  6. package/lib/app/deploy-config.js +3 -2
  7. package/lib/app/down.js +16 -1
  8. package/lib/channels/add-channel.js +134 -57
  9. package/lib/channels/approval-guides/chatgpt.js +1 -1
  10. package/lib/channels/approval-guides/index.js +3 -0
  11. package/lib/channels/approval-guides/microsoft-copilot.js +27 -14
  12. package/lib/channels/channel-add-ensure.js +128 -0
  13. package/lib/channels/channel-add-existing.js +72 -0
  14. package/lib/channels/channel-artifact-generator.js +11 -1
  15. package/lib/channels/channel-branding.js +94 -0
  16. package/lib/channels/channel-credential.js +8 -0
  17. package/lib/channels/channel-discovery.js +330 -0
  18. package/lib/channels/channel-external-system.js +1 -1
  19. package/lib/channels/channel-runtime-resolver.js +107 -14
  20. package/lib/channels/channel-status-display.js +36 -3
  21. package/lib/channels/microsoft-copilot-channel-add.js +138 -0
  22. package/lib/channels/microsoft-copilot-entra-sso-form.js +251 -0
  23. package/lib/channels/microsoft-copilot-entra-sso.js +285 -0
  24. package/lib/channels/microsoft-copilot-icons.js +74 -0
  25. package/lib/channels/microsoft-copilot-limits.js +103 -0
  26. package/lib/channels/microsoft-copilot-package.js +178 -52
  27. package/lib/channels/microsoft-copilot-validate.js +227 -0
  28. package/lib/cli/attach-default-help-examples.js +102 -0
  29. package/lib/cli/index.js +4 -0
  30. package/lib/cli/infra-guided.js +13 -6
  31. package/lib/cli/setup-credential-deployment.js +7 -5
  32. package/lib/cli/setup-infra.js +31 -4
  33. package/lib/cli/setup-utility-validate.js +1 -0
  34. package/lib/commands/channel-add-setup.js +28 -9
  35. package/lib/commands/channel.js +14 -10
  36. package/lib/commands/credential-push.js +37 -5
  37. package/lib/commands/integration-definition.js +158 -0
  38. package/lib/commands/package-score.js +72 -0
  39. package/lib/commands/repair-auth-apply.js +19 -5
  40. package/lib/commands/repair-datasource-sync-operations.js +4 -1
  41. package/lib/commands/role-assistant-test-output.js +200 -0
  42. package/lib/commands/role-assistant.js +163 -166
  43. package/lib/datasource/capability/capability-resolve.js +15 -5
  44. package/lib/datasource/capability/copy-operations.js +14 -4
  45. package/lib/deployment/deployer-poll-run.js +2 -2
  46. package/lib/deployment/deployer.js +1 -1
  47. package/lib/deployment/environment.js +2 -2
  48. package/lib/external-system/deploy.js +11 -21
  49. package/lib/infrastructure/index.js +19 -4
  50. package/lib/integration-definition/apply.js +476 -0
  51. package/lib/integration-definition/canonical-json.js +51 -0
  52. package/lib/integration-definition/emit-suite-bid.js +94 -0
  53. package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
  54. package/lib/integration-definition/enrich-openapi-operations.js +433 -0
  55. package/lib/integration-definition/from-discovery.js +339 -0
  56. package/lib/integration-definition/hash.js +37 -0
  57. package/lib/integration-definition/index.js +37 -0
  58. package/lib/integration-definition/resource-type-catalog.js +59 -0
  59. package/lib/integration-definition/system-kind-enum.js +40 -0
  60. package/lib/integration-definition/validate.js +148 -0
  61. package/lib/lab/package-parity-score.js +417 -0
  62. package/lib/programmatic/builder-api-request.js +5 -1
  63. package/lib/programmatic/builder-help-governance.js +3 -2
  64. package/lib/programmatic/capability-registry.js +20 -0
  65. package/lib/programmatic/help-content/channel-onboarding.md +35 -0
  66. package/lib/programmatic/help-content/cip-overview.md +27 -0
  67. package/lib/programmatic/help-content/connected-system-ui.md +70 -0
  68. package/lib/programmatic/help-content/dimensions-guide.md +23 -0
  69. package/lib/programmatic/help-content/enterprise-sync-fabrix.md +123 -0
  70. package/lib/programmatic/help-content/overview.md +25 -0
  71. package/lib/programmatic/help-content/subscription-guide.md +27 -0
  72. package/lib/programmatic/help-content/workflow.md +50 -0
  73. package/lib/programmatic/openapi-component-schemas.js +7 -1
  74. package/lib/programmatic/openapi-descriptions.js +46 -0
  75. package/lib/programmatic/openapi-envelope-examples.js +280 -0
  76. package/lib/programmatic/openapi-operation-bindings.js +50 -0
  77. package/lib/programmatic/openapi-request-schemas.js +40 -0
  78. package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
  79. package/lib/programmatic/openapi-result-schemas.js +76 -99
  80. package/lib/programmatic/openapi-schema-descriptions-core.js +25 -4
  81. package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
  82. package/lib/programmatic/openapi-schema-descriptions-operations.js +43 -0
  83. package/lib/programmatic/openapi-schema-helpers.js +9 -9
  84. package/lib/programmatic/openapi-spec.js +11 -1
  85. package/lib/programmatic/orchestration-openapi-meta.js +10 -0
  86. package/lib/programmatic/persist-supplied-openapi.js +180 -0
  87. package/lib/programmatic/route-handler-map.js +35 -1
  88. package/lib/programmatic/run-channel-package.js +236 -0
  89. package/lib/programmatic/run-integration-definition.js +77 -0
  90. package/lib/role-assistant/evidence-sync.js +55 -7
  91. package/lib/role-assistant/knowledge-sync-logging.js +58 -0
  92. package/lib/role-assistant/knowledge-sync.js +88 -93
  93. package/lib/role-assistant/test-cases-bid.js +155 -0
  94. package/lib/role-assistant/test-cases-evaluate.js +9 -1
  95. package/lib/role-assistant/test-cases-honesty.js +24 -10
  96. package/lib/role-assistant/test-cases.js +28 -0
  97. package/lib/role-assistant/test-runner-attachments.js +118 -0
  98. package/lib/role-assistant/test-runner-bid-apply.js +201 -0
  99. package/lib/role-assistant/test-runner-bid-document.js +278 -0
  100. package/lib/role-assistant/test-runner-start-payload.js +96 -0
  101. package/lib/role-assistant/test-runner-workhub-answers.js +249 -20
  102. package/lib/role-assistant/test-runner-workhub-helpers.js +100 -0
  103. package/lib/role-assistant/test-runner-workhub-soft.js +86 -0
  104. package/lib/role-assistant/test-runner-workhub.js +152 -107
  105. package/lib/role-assistant/test-runner.js +5 -63
  106. package/lib/role-assistant/test-suite.js +112 -0
  107. package/lib/schema/application-schema.json +56 -6
  108. package/lib/schema/external-datasource.schema.json +103 -17
  109. package/lib/schema/external-system.schema.json +54 -3
  110. package/lib/schema/integration-definition.schema.json +195 -0
  111. package/lib/utils/authentication-shape.js +6 -0
  112. package/lib/utils/compose-generator.js +10 -1
  113. package/lib/utils/compose-traefik-extra-routes.js +107 -0
  114. package/lib/utils/docker-build.js +2 -2
  115. package/lib/utils/help-builder.js +3 -1
  116. package/lib/utils/promote-dev-docker-image.js +102 -18
  117. package/lib/utils/transient-developer-id.js +28 -0
  118. package/package.json +14 -16
  119. package/templates/README.md +12 -1
  120. package/templates/applications/dataplane/application.yaml +10 -2
  121. package/templates/applications/dataplane/env.template +7 -6
  122. package/templates/applications/miso-controller/application.yaml +23 -8
  123. package/templates/applications/miso-controller/env.template +8 -2
  124. package/templates/applications/miso-controller/rbac.yaml +1 -0
  125. package/templates/channels/README.md +11 -1
  126. package/templates/channels/brand/color-192.png +0 -0
  127. package/templates/channels/brand/dark/128.png +0 -0
  128. package/templates/channels/brand/dark/192.png +0 -0
  129. package/templates/channels/brand/dark/256.png +0 -0
  130. package/templates/channels/brand/dark/32.png +0 -0
  131. package/templates/channels/brand/dark/512.png +0 -0
  132. package/templates/channels/brand/dark/64.png +0 -0
  133. package/templates/channels/brand/light/128.png +0 -0
  134. package/templates/channels/brand/light/192.png +0 -0
  135. package/templates/channels/brand/light/256.png +0 -0
  136. package/templates/channels/brand/light/32.png +0 -0
  137. package/templates/channels/brand/light/512.png +0 -0
  138. package/templates/channels/brand/light/64.png +0 -0
  139. package/templates/channels/brand/mono-white/128.png +0 -0
  140. package/templates/channels/brand/mono-white/192.png +0 -0
  141. package/templates/channels/brand/mono-white/256.png +0 -0
  142. package/templates/channels/brand/mono-white/32.png +0 -0
  143. package/templates/channels/brand/mono-white/512.png +0 -0
  144. package/templates/channels/brand/mono-white/64.png +0 -0
  145. package/templates/channels/brand/outline-32.png +0 -0
  146. package/templates/channels/chatgpt/install.template.md.hbs +6 -4
  147. package/templates/channels/chatgpt/integration/README.md +4 -5
  148. package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
  149. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +14 -16
  150. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
  151. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +30 -0
  152. package/templates/channels/microsoft-copilot/fi-fi.template.json.hbs +7 -0
  153. package/templates/channels/microsoft-copilot/install.template.md.hbs +93 -22
  154. package/templates/channels/microsoft-copilot/integration/README.md +2 -3
  155. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
  156. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -19
  157. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +15 -0
  158. package/templates/channels/slack/integration/README.md +2 -4
  159. package/templates/channels/teams-bot/install.template.md.hbs +4 -0
  160. package/templates/channels/teams-bot/integration/README.md +3 -5
  161. package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
  162. package/templates/python/docker-compose.hbs +36 -0
  163. package/templates/typescript/docker-compose.hbs +36 -0
  164. package/.cursor/plans/Done/157.1-security-quality-violations-by-type.csv +0 -374
  165. package/.cursor/process/README.md +0 -9
  166. package/.cursor/rules/anchor-docs.mdc +0 -15
  167. package/.cursor/rules/assistant-driven-integration-build.mdc +0 -40
  168. package/.cursor/rules/cli-layout.mdc +0 -81
  169. package/.cursor/rules/cli-product-neutral.mdc +0 -43
  170. package/.cursor/rules/deploy-rules/docker-image-ip-policy.mdc +0 -49
  171. package/.cursor/rules/docs-rules.mdc +0 -32
  172. package/.cursor/rules/plan-rules/application-lifecycle.mdc +0 -30
  173. package/.cursor/rules/plan-rules/plan-frontmatter.mdc +0 -35
  174. package/.cursor/rules/plan-rules/plan-lifecycle.mdc +0 -31
  175. package/.cursor/rules/plan-rules/plan-validation-gate.mdc +0 -34
  176. package/.cursor/rules/project-rules.mdc +0 -913
  177. package/.cursor/skills/README.md +0 -15
  178. package/.dockerignore +0 -52
  179. package/.eslintignore +0 -22
  180. package/.markdownlint.json +0 -14
  181. package/anchor-docs/README.md +0 -10
  182. package/anchor-docs/_TEMPLATE +0 -24
  183. package/babel.config.js +0 -6
  184. package/bin/builder-api-cleanup.js +0 -26
  185. package/bin/builder-api-nest.js +0 -10
  186. package/bin/builder-api-spike.js +0 -19
  187. package/enterprise-knowledge/README.md +0 -50
  188. package/enterprise-knowledge/enterprise-integration-lifecycle/README.md +0 -22
  189. package/enterprise-knowledge/enterprise-integration-lifecycle/application.json +0 -15
  190. package/enterprise-knowledge/enterprise-integration-lifecycle/deploy.js +0 -69
  191. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-deploy.json +0 -52
  192. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-system.json +0 -29
  193. package/enterprise-knowledge/enterprise-integration-lifecycle/env.template +0 -2
  194. package/integration/README.md +0 -81
  195. package/integration/github/README.md +0 -53
  196. package/integration/github/application.json +0 -21
  197. package/integration/github/deploy.js +0 -72
  198. package/integration/github/env.template +0 -11
  199. package/integration/github/github-datasource-issue-comments.json +0 -239
  200. package/integration/github/github-datasource-issues.json +0 -399
  201. package/integration/github/github-datasource-repository.json +0 -354
  202. package/integration/github/github-deploy.json +0 -1202
  203. package/integration/github/github-system.json +0 -82
  204. package/integration/hubspot-test/README.md +0 -159
  205. package/integration/hubspot-test/application.json +0 -54
  206. package/integration/hubspot-test/companies.json +0 -2048
  207. package/integration/hubspot-test/create-hubspot.js +0 -523
  208. package/integration/hubspot-test/env.template +0 -4
  209. package/integration/hubspot-test/hubspot-test-datasource-company.json +0 -138
  210. package/integration/hubspot-test/hubspot-test-datasource-contact.json +0 -146
  211. package/integration/hubspot-test/hubspot-test-datasource-deal.json +0 -146
  212. package/integration/hubspot-test/hubspot-test-datasource-users.json +0 -76
  213. package/integration/hubspot-test/hubspot-test-deploy.json +0 -2160
  214. package/integration/hubspot-test/hubspot-test-system.json +0 -74
  215. package/integration/hubspot-test/rbac.json +0 -166
  216. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +0 -20
  217. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +0 -9
  218. package/integration/hubspot-test/test-artifacts/wizard-invalid-add-datasource.yaml +0 -5
  219. package/integration/hubspot-test/test-artifacts/wizard-invalid-app-name.yaml +0 -5
  220. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-create.yaml +0 -7
  221. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-select.yaml +0 -7
  222. package/integration/hubspot-test/test-artifacts/wizard-invalid-known-platform.yaml +0 -4
  223. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-app.yaml +0 -4
  224. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  225. package/integration/hubspot-test/test-artifacts/wizard-invalid-mode.yaml +0 -5
  226. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-file.yaml +0 -5
  227. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-url.yaml +0 -4
  228. package/integration/hubspot-test/test-artifacts/wizard-invalid-source.yaml +0 -4
  229. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-array-test.yaml +0 -5
  230. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  231. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  232. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-test.yaml +0 -5
  233. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-test.yaml +0 -5
  234. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +0 -5
  235. package/integration/hubspot-test/test-dataplane-down-helpers.js +0 -249
  236. package/integration/hubspot-test/test-dataplane-down-tests.js +0 -386
  237. package/integration/hubspot-test/test-dataplane-down.js +0 -209
  238. package/integration/hubspot-test/test.js +0 -1588
  239. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +0 -16
  240. package/integration/hubspot-test/wizard-hubspot-platform.yaml +0 -8
  241. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +0 -23
  242. package/integration/roundtrip-test-local/README.md +0 -143
  243. package/integration/roundtrip-test-local/application.yaml +0 -13
  244. package/integration/roundtrip-test-local/env.template +0 -15
  245. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +0 -14
  246. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +0 -61
  247. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +0 -25
  248. package/integration/roundtrip-test-local2/README.md +0 -143
  249. package/integration/roundtrip-test-local2/application.yaml +0 -13
  250. package/integration/roundtrip-test-local2/env.template +0 -15
  251. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +0 -31
  252. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +0 -86
  253. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +0 -25
  254. package/integration/test/wizard.yaml +0 -8
  255. package/jest.config.coverage.js +0 -37
  256. package/jest.config.default.js +0 -11
  257. package/jest.config.integration.fixtures.js +0 -22
  258. package/jest.config.integration.js +0 -33
  259. package/jest.config.isolated.js +0 -11
  260. package/jest.config.manual.js +0 -30
  261. package/jest.global-live-fabrix-hooks.js +0 -36
  262. package/jest.isolated-projects.js +0 -260
  263. package/jest.projects.js +0 -76
  264. package/packages/builder-api/README.md +0 -120
  265. package/packages/builder-api/package.json +0 -30
  266. package/packages/builder-api/src/app.module.ts +0 -7
  267. package/packages/builder-api/src/auth/auth.module.ts +0 -5
  268. package/packages/builder-api/src/external-system/external-system.module.ts +0 -5
  269. package/packages/builder-api/src/main.ts +0 -16
  270. package/packages/builder-api/tsconfig.json +0 -15
  271. package/scripts/check-builder-api-architecture-grep.js +0 -79
  272. package/scripts/check-datasource-test-run-schema-sync.js +0 -34
  273. package/scripts/ci-fix.sh +0 -19
  274. package/scripts/ci-simulate.sh +0 -19
  275. package/scripts/diagnose-cli.js +0 -150
  276. package/scripts/docker_image_content_gate.js +0 -47
  277. package/scripts/docker_image_content_gate.sh +0 -5
  278. package/scripts/export-builder-api-evidence.js +0 -262
  279. package/scripts/install-local.js +0 -490
  280. package/scripts/lib/builder-api-docker-staging-copy.js +0 -60
  281. package/scripts/lib/builder-api-docker-staging-core.js +0 -183
  282. package/scripts/lib/docker-image-content-gate-core.js +0 -166
  283. package/scripts/lib/plan-actor.js +0 -109
  284. package/scripts/lib/plan-config.js +0 -20
  285. package/scripts/lib/plan-gates.js +0 -154
  286. package/scripts/lib/plan-meta.js +0 -104
  287. package/scripts/lib/plan-parse.js +0 -162
  288. package/scripts/lib/plan-prevalidate-gates.js +0 -337
  289. package/scripts/lib/plan-run-core.js +0 -125
  290. package/scripts/lib/plan-run-report.js +0 -119
  291. package/scripts/plan-approve.js +0 -24
  292. package/scripts/plan-archive.js +0 -49
  293. package/scripts/plan-run.js +0 -13
  294. package/scripts/plan-set-actor.js +0 -21
  295. package/scripts/plan-validate.js +0 -80
  296. package/scripts/pnpm/af.mjs +0 -110
  297. package/scripts/pnpm/check-quiet.mjs +0 -32
  298. package/scripts/pnpm/dev-install.mjs +0 -29
  299. package/scripts/pnpm/dev-reload.mjs +0 -18
  300. package/scripts/pnpm/help.mjs +0 -55
  301. package/scripts/pnpm/lib/dev-reload-core.mjs +0 -189
  302. package/scripts/pnpm/lib/dev-reload-log-capture.mjs +0 -67
  303. package/scripts/pnpm/lib/dev-reload-log-utils.mjs +0 -32
  304. package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +0 -124
  305. package/scripts/pnpm/lib/resolve-app-url.mjs +0 -183
  306. package/scripts/pnpm/lib/spinner.mjs +0 -42
  307. package/scripts/pnpm-global-remove.js +0 -48
  308. package/scripts/stage_builder_api_for_docker.js +0 -65
  309. package/scripts/sync-builder-api-template.js +0 -44
  310. package/scripts/test-dataplane-bootstrap.js +0 -331
@@ -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 path = require('path');
9
+ const fs = require('../internal/fs-real-sync');
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
+ };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Build work.start Intent payload for role-assistant cases.
3
+ * @fileoverview Extracted from test-runner.js (file-size) + 517.1 attachments
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const crypto = require('crypto');
9
+ const { buildStartAttachments } = require('./test-runner-attachments');
10
+
11
+ function buildRuntimeId(prefix) {
12
+ if (typeof crypto.randomUUID === 'function') {
13
+ return `${prefix}-${crypto.randomUUID()}`;
14
+ }
15
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
16
+ }
17
+
18
+ function applyOptionalStringField(payload, value, key) {
19
+ if (typeof value === 'string' && value.trim()) {
20
+ payload[key] = value.trim();
21
+ }
22
+ }
23
+
24
+ function resolveInputObject(testCase) {
25
+ const input = testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
26
+ return Array.isArray(input) ? {} : input;
27
+ }
28
+
29
+ function resolveInputMessage(testCase) {
30
+ const input = resolveInputObject(testCase);
31
+ const candidates = [input.message, input.text, input.prompt];
32
+ for (const row of candidates) {
33
+ if (typeof row === 'string' && row.trim()) {
34
+ return row.trim();
35
+ }
36
+ }
37
+ return '';
38
+ }
39
+
40
+ function applyStartPayloadOptionalFields(payload, input) {
41
+ const mappedFields = [
42
+ ['activationId', 'activationId'],
43
+ ['workerTaskId', 'workerTaskId'],
44
+ ['principalRef', 'principalRef'],
45
+ ['businessRoleRef', 'businessRoleRef']
46
+ ];
47
+ for (const [inputKey, payloadKey] of mappedFields) {
48
+ applyOptionalStringField(payload, input[inputKey], payloadKey);
49
+ }
50
+ }
51
+
52
+ function buildStartPayloadSourceMetadata(roleKey, testCase, input) {
53
+ return {
54
+ roleKey,
55
+ caseId: testCase.caseId,
56
+ runner: 'aifabrix-role-assistant-test',
57
+ ...(input.sourceMetadata && typeof input.sourceMetadata === 'object' ? input.sourceMetadata : {})
58
+ };
59
+ }
60
+
61
+ function buildStartIntentPayload(roleKey, testCase) {
62
+ const input = resolveInputObject(testCase);
63
+ const message = resolveInputMessage(testCase);
64
+ const hasContent = typeof input.content !== 'undefined';
65
+ if (!hasContent && !message) {
66
+ throw new Error(
67
+ `Role assistant test case "${testCase.caseId}" is missing input.message or input.content for runtime execution.`
68
+ );
69
+ }
70
+ const payload = {
71
+ submissionId: String(input.submissionId || buildRuntimeId(`ra-${roleKey}`)),
72
+ content: hasContent ? input.content : message,
73
+ comModelRef: String(input.comModelRef || 'aifabrix.com/intent/default'),
74
+ comModelVersion: String(input.comModelVersion || '1.0'),
75
+ comVocabulary:
76
+ Array.isArray(input.comVocabulary) && input.comVocabulary.length > 0
77
+ ? input.comVocabulary.map((row) => String(row))
78
+ : ['objective', 'entity', 'action'],
79
+ sourceType: String(input.sourceType || 'api'),
80
+ sourceLanguage: String(input.sourceLanguage || 'en'),
81
+ responseLanguage: String(input.responseLanguage || input.sourceLanguage || 'en'),
82
+ sourceMetadata: buildStartPayloadSourceMetadata(roleKey, testCase, input)
83
+ };
84
+ applyStartPayloadOptionalFields(payload, input);
85
+ const attachments = buildStartAttachments(testCase);
86
+ if (attachments && attachments.length > 0) {
87
+ payload.attachments = attachments;
88
+ }
89
+ return payload;
90
+ }
91
+
92
+ module.exports = {
93
+ buildStartIntentPayload,
94
+ resolveInputObject,
95
+ resolveInputMessage
96
+ };
@@ -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,14 +285,87 @@ 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
+ );
153
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
+ // Always attach the full BID for apply waits (path or body). Path-only responds
326
+ // that send only systemKey let CIP POST an empty definition → Invalid BID.
327
+ if (!bid || (!wantsBody && !wantsPath)) {
328
+ return undefined;
329
+ }
330
+ const out = { integrationDefinition: bid };
331
+ const targetSystemKey = resolveTargetSystemKey(testCase);
332
+ if (targetSystemKey) {
333
+ out.systemKey = targetSystemKey;
334
+ }
335
+ return out;
336
+ }
337
+
338
+ function synthesizeTargetSystemKeyAnswer(wait, prompt, kind, testCase) {
339
+ const targetSystemKey = resolveTargetSystemKey(testCase);
340
+ const needsBody = kind === 'missingRequestBody' || /missing required request body/i.test(prompt);
341
+ if (!targetSystemKey || (!needsBody && !/systemKey is required/i.test(prompt))) {
342
+ return undefined;
343
+ }
344
+ const capability = resolveWaitCapabilityText(wait);
345
+ const supportsTarget =
346
+ capability.includes('workspace') ||
347
+ capability.includes('createsystem') ||
348
+ (capability.includes('template') && !isBidApplyWait(wait)) ||
349
+ capability.includes('wizard') ||
350
+ capability.includes('upload') ||
351
+ capability.includes('project.') ||
352
+ /systemkey is required/i.test(prompt);
353
+ return supportsTarget ? { systemKey: targetSystemKey } : undefined;
354
+ }
355
+
356
+ function synthesizeWaitAnswer(wait, statusRow, objective, testCase) {
357
+ const prompt = String(wait.prompt || statusRow?.message || '').trim();
154
358
  const kind = String(wait.objectRef?.kind || '').trim();
359
+ // BID apply before field/path synthesis — "(fields: systemKey)" must not
360
+ // drop integrationDefinition and let CIP POST an empty BID.
361
+ const bidAnswer = synthesizeBidApplyAnswer(wait, prompt, kind, testCase);
362
+ if (bidAnswer !== undefined) {
363
+ return bidAnswer;
364
+ }
365
+ const fieldAnswer = synthesizeFieldAnswer(wait, statusRow, objective, testCase);
366
+ if (fieldAnswer !== undefined) {
367
+ return fieldAnswer;
368
+ }
155
369
  const isMissingInput =
156
370
  kind === 'missingRequestBody' ||
157
371
  kind === 'missingPathParams' ||
@@ -159,18 +373,25 @@ function synthesizeWaitAnswer(wait, statusRow, objective) {
159
373
  if (!isMissingInput) {
160
374
  return undefined;
161
375
  }
162
- const capability = String(wait.questionKey || wait.objectRef?.capabilityKey || '').toLowerCase();
163
- return synthesizeFromCapability(capability, hints);
376
+ const pathAnswer = synthesizePathParameterAnswer(wait, prompt, kind, testCase);
377
+ if (pathAnswer !== undefined) {
378
+ return pathAnswer;
379
+ }
380
+ const targetAnswer = synthesizeTargetSystemKeyAnswer(wait, prompt, kind, testCase);
381
+ return targetAnswer || synthesizeFromCapability(resolveWaitCapabilityText(wait), extractObjectiveHints(objective));
164
382
  }
165
383
 
166
384
  function resolveRespondValue(testCase, wait, statusRow, objective) {
167
385
  const answers = Array.isArray(testCase.answers) ? testCase.answers : [];
168
- const matched = pickAnswerForWait(answers, wait) || answers[0] || null;
386
+ // Only use an answer whose when.questionKey matches this wait.
387
+ // Never fall back to answers[0] — that poisons capability waits (e.g. task.runtest)
388
+ // with unrelated discovery payloads (system.apiGroups).
389
+ const matched = pickAnswerForWait(answers, wait);
169
390
  const fromCase = matched ? resolveWorkHubAnswerValue(matched) : undefined;
170
391
  if (fromCase !== undefined) {
171
392
  return fromCase;
172
393
  }
173
- return synthesizeWaitAnswer(wait, statusRow, objective);
394
+ return synthesizeWaitAnswer(wait, statusRow, objective, testCase);
174
395
  }
175
396
 
176
397
  function waitRespondFingerprint(wait) {
@@ -181,7 +402,10 @@ function waitRespondFingerprint(wait) {
181
402
 
182
403
  function alreadyResponded(interactions, fingerprint) {
183
404
  return interactions.some(
184
- (row) => row && row.type === 'respond' && row.fingerprint === fingerprint
405
+ (row) =>
406
+ row &&
407
+ (row.type === 'respond' || row.type === 'respond-skipped') &&
408
+ row.fingerprint === fingerprint
185
409
  );
186
410
  }
187
411
 
@@ -214,6 +438,11 @@ module.exports = {
214
438
  pickAnswerForWait,
215
439
  synthesizeWaitAnswer,
216
440
  extractObjectiveHints,
441
+ resolveCaseSystemKey,
442
+ resolveTargetSystemKey,
443
+ shouldStampSuiteBid,
444
+ resolveSuiteRunId,
445
+ loadCaseBidDocument,
217
446
  resolveRespondValue,
218
447
  waitRespondFingerprint,
219
448
  alreadyResponded,