@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,100 @@
1
+ /**
2
+ * Shared Work Hub test-runner helpers (ids, delays, path detection).
3
+ * @fileoverview Pure helpers for role-assistant Work Hub harness
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const TERMINAL_TASK = new Set(['completed', 'failed', 'cancelled', 'canceled']);
9
+ const PLAN_LOST_RE =
10
+ /work plan not found|plan not found|404|ephemeral|reload|timed out|timeout|network error|econnreset|socket|fetch failed/i;
11
+ const APPROVAL_NOT_PENDING_RE = /approval state is not pending|not pending/i;
12
+
13
+ function delayMs(ms) {
14
+ const wait = Number(ms);
15
+ if (!Number.isFinite(wait) || wait <= 0) {
16
+ return Promise.resolve();
17
+ }
18
+ return new Promise((resolve) => setTimeout(resolve, wait));
19
+ }
20
+
21
+ function resolveInput(testCase) {
22
+ const input = testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
23
+ return Array.isArray(input) ? {} : input;
24
+ }
25
+
26
+ function resolveObjective(testCase) {
27
+ const input = resolveInput(testCase);
28
+ const candidates = [input.message, input.text, input.prompt, input.objective];
29
+ for (const row of candidates) {
30
+ if (typeof row === 'string' && row.trim()) {
31
+ return row.trim();
32
+ }
33
+ }
34
+ return '';
35
+ }
36
+
37
+ function wantsWorkHubPath(testCase) {
38
+ const input = resolveInput(testCase);
39
+ const pathToken = String(input.executionPath || input.mode || '').trim().toLowerCase();
40
+ if (pathToken === 'workhub' || pathToken === 'work-hub' || pathToken === 'plan') {
41
+ return true;
42
+ }
43
+ if (pathToken === 'work.start' || pathToken === 'start' || pathToken === 'intent') {
44
+ return false;
45
+ }
46
+ return Boolean(resolveObjective(testCase));
47
+ }
48
+
49
+ function unwrapId(value) {
50
+ if (typeof value === 'string' && value.trim()) {
51
+ return value.trim();
52
+ }
53
+ if (value && typeof value === 'object') {
54
+ const nested = value.id || value.value || value.ref;
55
+ if (typeof nested === 'string' && nested.trim()) {
56
+ return nested.trim();
57
+ }
58
+ }
59
+ return '';
60
+ }
61
+
62
+ function extractPeFromTask(task) {
63
+ const candidates = [task?.processExecutionId, task?.processExecutionRef];
64
+ for (const row of candidates) {
65
+ const id = unwrapId(row);
66
+ if (id) {
67
+ return id;
68
+ }
69
+ }
70
+ return '';
71
+ }
72
+
73
+ function planAlreadyActivated(plan) {
74
+ const assistant = plan?.recommendedAssistant;
75
+ if (!assistant || typeof assistant !== 'object' || assistant.isActivated !== true) {
76
+ return false;
77
+ }
78
+ return Boolean(unwrapId(assistant.activationId));
79
+ }
80
+
81
+ function isPlanLostError(error) {
82
+ return PLAN_LOST_RE.test(String((error && error.message) || error || ''));
83
+ }
84
+
85
+ function isApprovalNotPendingError(error) {
86
+ return APPROVAL_NOT_PENDING_RE.test(String((error && error.message) || error || ''));
87
+ }
88
+
89
+ module.exports = {
90
+ TERMINAL_TASK,
91
+ delayMs,
92
+ resolveInput,
93
+ resolveObjective,
94
+ wantsWorkHubPath,
95
+ unwrapId,
96
+ extractPeFromTask,
97
+ planAlreadyActivated,
98
+ isPlanLostError,
99
+ isApprovalNotPendingError
100
+ };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Soft Work Hub outcomes for insufficient-info / clarify-first Intent.
3
+ * Soft cases may stop before accept when Intent is not Act-ready — that is a
4
+ * governed fail-closed outcome, not a harness failure.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ function isIntentNotActReadyError(error) {
10
+ const msg = String((error && error.message) || error || '');
11
+ return /not Act-ready|intentNotActReady/i.test(msg);
12
+ }
13
+
14
+ function attachPlanIdToError(error, planId) {
15
+ if (error && typeof error === 'object' && planId) {
16
+ error.planId = planId;
17
+ }
18
+ return error;
19
+ }
20
+
21
+ function buildSoftClarifyLifecycleResult(roleKey, testCase, planId, error) {
22
+ const message = String((error && error.message) || error || 'intentNotActReady');
23
+ return {
24
+ roleKey,
25
+ caseId: testCase.caseId,
26
+ processExecutionId: null,
27
+ startResponse: {
28
+ planId: planId || null,
29
+ path: 'workHub',
30
+ softClarifyStop: true
31
+ },
32
+ finalStatus: {
33
+ status: 'stopped',
34
+ wait: null,
35
+ reason: 'intentNotActReady'
36
+ },
37
+ timeline: [],
38
+ interactions: [{ type: 'softClarifyStop', message }],
39
+ workResult: null,
40
+ workResultError: null
41
+ };
42
+ }
43
+
44
+ /**
45
+ * @param {Function} createActivateAcceptPlan
46
+ * @param {Object} startArgs
47
+ * @param {string} startArgs.dataplaneUrl
48
+ * @param {Object} startArgs.authConfig
49
+ * @param {Object} startArgs.planBody
50
+ * @param {string} startArgs.assistantName
51
+ * @param {Object} [startArgs.options]
52
+ * @param {Object} softCtx
53
+ * @param {string} softCtx.roleKey
54
+ * @param {Object} softCtx.testCase
55
+ */
56
+ async function startWorkHubOrSoftClarify(createActivateAcceptPlan, startArgs, softCtx) {
57
+ const { dataplaneUrl, authConfig, planBody, assistantName, options } = startArgs;
58
+ const { roleKey, testCase } = softCtx;
59
+ try {
60
+ return {
61
+ soft: false,
62
+ started: await createActivateAcceptPlan(
63
+ dataplaneUrl,
64
+ authConfig,
65
+ planBody,
66
+ assistantName,
67
+ options
68
+ )
69
+ };
70
+ } catch (error) {
71
+ if (testCase?.expected?.soft === true && isIntentNotActReadyError(error)) {
72
+ return {
73
+ soft: true,
74
+ result: buildSoftClarifyLifecycleResult(roleKey, testCase, error.planId, error)
75
+ };
76
+ }
77
+ throw error;
78
+ }
79
+ }
80
+
81
+ module.exports = {
82
+ isIntentNotActReadyError,
83
+ attachPlanIdToError,
84
+ buildSoftClarifyLifecycleResult,
85
+ startWorkHubOrSoftClarify
86
+ };
@@ -23,87 +23,25 @@ const {
23
23
  alreadyResponded,
24
24
  buildRespondPayload
25
25
  } = require('./test-runner-workhub-answers');
26
-
27
- const TERMINAL_TASK = new Set(['completed', 'failed', 'cancelled', 'canceled']);
28
- const PLAN_LOST_RE =
29
- /work plan not found|plan not found|404|ephemeral|reload|timed out|timeout|network error|econnreset|socket|fetch failed/i;
30
- const APPROVAL_NOT_PENDING_RE = /approval state is not pending|not pending/i;
31
-
32
- function delayMs(ms) {
33
- const wait = Number(ms);
34
- if (!Number.isFinite(wait) || wait <= 0) {
35
- return Promise.resolve();
36
- }
37
- return new Promise((resolve) => setTimeout(resolve, wait));
38
- }
39
-
40
- function resolveInput(testCase) {
41
- const input = testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
42
- return Array.isArray(input) ? {} : input;
43
- }
44
-
45
- function resolveObjective(testCase) {
46
- const input = resolveInput(testCase);
47
- const candidates = [input.message, input.text, input.prompt, input.objective];
48
- for (const row of candidates) {
49
- if (typeof row === 'string' && row.trim()) {
50
- return row.trim();
51
- }
52
- }
53
- return '';
54
- }
55
-
56
- function wantsWorkHubPath(testCase) {
57
- const input = resolveInput(testCase);
58
- const pathToken = String(input.executionPath || input.mode || '').trim().toLowerCase();
59
- if (pathToken === 'workhub' || pathToken === 'work-hub' || pathToken === 'plan') {
60
- return true;
61
- }
62
- if (pathToken === 'work.start' || pathToken === 'start' || pathToken === 'intent') {
63
- return false;
64
- }
65
- return Boolean(resolveObjective(testCase));
66
- }
67
-
68
- function unwrapId(value) {
69
- if (typeof value === 'string' && value.trim()) {
70
- return value.trim();
71
- }
72
- if (value && typeof value === 'object') {
73
- const nested = value.id || value.value || value.ref;
74
- if (typeof nested === 'string' && nested.trim()) {
75
- return nested.trim();
76
- }
77
- }
78
- return '';
79
- }
80
-
81
- function extractPeFromTask(task) {
82
- const candidates = [task?.processExecutionId, task?.processExecutionRef];
83
- for (const row of candidates) {
84
- const id = unwrapId(row);
85
- if (id) {
86
- return id;
87
- }
88
- }
89
- return '';
90
- }
91
-
92
- function planAlreadyActivated(plan) {
93
- const assistant = plan?.recommendedAssistant;
94
- if (!assistant || typeof assistant !== 'object' || assistant.isActivated !== true) {
95
- return false;
96
- }
97
- return Boolean(unwrapId(assistant.activationId));
98
- }
99
-
100
- function isPlanLostError(error) {
101
- return PLAN_LOST_RE.test(String((error && error.message) || error || ''));
102
- }
103
-
104
- function isApprovalNotPendingError(error) {
105
- return APPROVAL_NOT_PENDING_RE.test(String((error && error.message) || error || ''));
106
- }
26
+ const {
27
+ shouldApplyBidAfterWait,
28
+ shouldSoftHttpBidApply,
29
+ applyCaseBidViaBuilderApi,
30
+ persistCaseOpenApiViaBuilderApi
31
+ } = require('./test-runner-bid-apply');
32
+ const { resolveTargetSystemKey } = require('./test-runner-bid-document');
33
+ const {
34
+ TERMINAL_TASK,
35
+ delayMs,
36
+ resolveInput,
37
+ resolveObjective,
38
+ wantsWorkHubPath,
39
+ unwrapId,
40
+ extractPeFromTask,
41
+ planAlreadyActivated,
42
+ isPlanLostError,
43
+ isApprovalNotPendingError
44
+ } = require('./test-runner-workhub-helpers');
107
45
 
108
46
  async function waitDataplaneHealthy(dataplaneUrl, attempts = 12) {
109
47
  const base = String(dataplaneUrl || '')
@@ -149,11 +87,18 @@ async function acceptPlanForTask(dataplaneUrl, authConfig, planId) {
149
87
  }
150
88
 
151
89
  async function createActivateAcceptOnce(dataplaneUrl, authConfig, planBody, assistantName) {
90
+ const {
91
+ attachPlanIdToError
92
+ } = require('./test-runner-workhub-soft');
152
93
  await waitDataplaneHealthy(dataplaneUrl);
153
94
  const plan = await createWorkTaskPlan(dataplaneUrl, authConfig, planBody);
154
95
  const planId = await activateIfNeeded(dataplaneUrl, authConfig, plan, assistantName);
155
- const taskId = await acceptPlanForTask(dataplaneUrl, authConfig, planId);
156
- return { planId, taskId };
96
+ try {
97
+ const taskId = await acceptPlanForTask(dataplaneUrl, authConfig, planId);
98
+ return { planId, taskId };
99
+ } catch (error) {
100
+ throw attachPlanIdToError(error, planId);
101
+ }
157
102
  }
158
103
 
159
104
  async function createActivateAcceptPlan(dataplaneUrl, authConfig, planBody, assistantName, options) {
@@ -189,7 +134,16 @@ function _approveGateDecision(ctx) {
189
134
  return 'skip';
190
135
  }
191
136
  const approvalState = String(ctx.taskRow?.approvalState || '').trim().toLowerCase();
192
- if (approvalState && approvalState !== 'pending') {
137
+ // Treat empty / pending* as still actionable. Stop only after a terminal decision.
138
+ const terminalDecision = new Set([
139
+ 'approved',
140
+ 'rejected',
141
+ 'cancelled',
142
+ 'canceled',
143
+ 'notrequired',
144
+ 'not_required'
145
+ ]);
146
+ if (approvalState && terminalDecision.has(approvalState)) {
193
147
  return 'stop';
194
148
  }
195
149
  if ((Number(ctx.approveAttempts) || 0) >= 8) {
@@ -225,46 +179,133 @@ async function maybeApproveTask(dataplaneUrl, authConfig, taskId, ctx) {
225
179
  await _attemptApproveTask(dataplaneUrl, authConfig, taskId, ctx);
226
180
  }
227
181
 
182
+ function recordSkippedResponse(interactions, peId, fingerprint, wait) {
183
+ interactions.push({
184
+ type: 'respond-skipped',
185
+ processExecutionId: peId,
186
+ fingerprint,
187
+ questionKey: wait.questionKey || wait.requestKey || null,
188
+ objectRefKind: wait.objectRef?.kind || null,
189
+ reason: 'no-matching-answer-or-synthesis'
190
+ });
191
+ }
192
+
193
+ function recordBidEmit(testCase, interactions, peId) {
194
+ const { loadCaseBidDocument } = require('./test-runner-workhub-answers');
195
+ const emittedBid = loadCaseBidDocument(testCase);
196
+ if (!(emittedBid && emittedBid.meta && emittedBid.meta.suiteRunId)) {
197
+ return;
198
+ }
199
+ interactions.push({
200
+ type: 'bid-emit',
201
+ processExecutionId: peId,
202
+ suiteRunId: emittedBid.meta.suiteRunId,
203
+ definitionId: emittedBid.definitionId,
204
+ definitionHash: emittedBid.definitionHash
205
+ });
206
+ }
207
+
208
+ async function softHttpBidApplyAfterWait(authConfig, testCase, interactions, peId) {
209
+ try {
210
+ const applied = await applyCaseBidViaBuilderApi(authConfig, testCase);
211
+ interactions.push({
212
+ type: 'bid-apply',
213
+ processExecutionId: peId,
214
+ systemKey: applied && applied.systemKey,
215
+ definitionHash: applied && applied.definitionHash,
216
+ suiteRunId: applied && applied.suiteRunId,
217
+ status: applied && applied.status,
218
+ softHttp: true
219
+ });
220
+ } catch (applyErr) {
221
+ interactions.push({
222
+ type: 'bid-apply-error',
223
+ processExecutionId: peId,
224
+ error: applyErr.message || String(applyErr),
225
+ softHttp: true
226
+ });
227
+ }
228
+ }
229
+
230
+ async function respondToWaitingExecution(
231
+ dataplaneUrl,
232
+ authConfig,
233
+ peId,
234
+ testCase,
235
+ interactions,
236
+ statusRow
237
+ ) {
238
+ const wait = statusRow.wait && typeof statusRow.wait === 'object' ? statusRow.wait : {};
239
+ const fingerprint = waitRespondFingerprint(wait);
240
+ if (alreadyResponded(interactions, fingerprint)) {
241
+ return;
242
+ }
243
+ const payload = buildRespondPayload(
244
+ peId,
245
+ wait,
246
+ testCase,
247
+ statusRow,
248
+ interactions,
249
+ resolveObjective(testCase)
250
+ );
251
+ if (!payload) {
252
+ recordSkippedResponse(interactions, peId, fingerprint, wait);
253
+ return;
254
+ }
255
+ // Stage suite OpenAPI into workspace before CIP apply (517.2 layout).
256
+ // Product path: DE admits persistsuppliedopenapi; this is lab debt until then.
257
+ if (shouldApplyBidAfterWait(testCase, wait)) {
258
+ const systemKey = resolveTargetSystemKey(testCase);
259
+ if (systemKey) {
260
+ await persistCaseOpenApiViaBuilderApi(authConfig, testCase, systemKey);
261
+ }
262
+ }
263
+ await respondWorkExecution(dataplaneUrl, authConfig, peId, payload);
264
+ await continueWorkExecution(dataplaneUrl, authConfig, peId, {});
265
+ interactions.push({ type: 'respond', processExecutionId: peId, fingerprint });
266
+ if (shouldApplyBidAfterWait(testCase, wait)) {
267
+ recordBidEmit(testCase, interactions, peId);
268
+ }
269
+ // Soft HTTP apply is lab debt only. Hard cases (expected.soft !== true) rely on
270
+ // DE → capability.execute(template.applyintegrationdefinition) after continue.
271
+ if (shouldSoftHttpBidApply(testCase, wait)) {
272
+ await softHttpBidApplyAfterWait(authConfig, testCase, interactions, peId);
273
+ }
274
+ }
275
+
228
276
  async function maybeRespondWaiting(dataplaneUrl, authConfig, taskRow, testCase, interactions) {
229
277
  const peId = extractPeFromTask(taskRow);
230
278
  if (!peId) {
231
279
  return;
232
280
  }
233
281
  try {
282
+ testCase._activeProcessExecutionId = peId;
234
283
  const statusRow = await getWorkExecutionStatus(dataplaneUrl, authConfig, peId);
235
284
  if (String(statusRow?.status || '').toLowerCase() !== 'waiting') {
236
285
  return;
237
286
  }
238
- const wait = statusRow.wait && typeof statusRow.wait === 'object' ? statusRow.wait : {};
239
- const fingerprint = waitRespondFingerprint(wait);
240
- if (alreadyResponded(interactions, fingerprint)) {
241
- return;
242
- }
243
- const payload = buildRespondPayload(
287
+ await respondToWaitingExecution(
288
+ dataplaneUrl,
289
+ authConfig,
244
290
  peId,
245
- wait,
246
291
  testCase,
247
- statusRow,
248
292
  interactions,
249
- resolveObjective(testCase)
293
+ statusRow
250
294
  );
251
- if (!payload) {
252
- return;
253
- }
254
- await respondWorkExecution(dataplaneUrl, authConfig, peId, payload);
255
- await continueWorkExecution(dataplaneUrl, authConfig, peId, {});
256
- interactions.push({ type: 'respond', processExecutionId: peId, fingerprint });
257
295
  } catch (_err) {
258
296
  // Soft: continue polling task.
259
297
  }
260
298
  }
261
299
 
262
- function assertTerminalWorkHubTask(last, taskId, timeoutMs) {
300
+ function assertTerminalWorkHubTask(last, taskId, timeoutMs, options = {}) {
263
301
  if (!last) {
264
302
  throw new Error(`Work Hub task ${taskId} produced no status rows.`);
265
303
  }
266
304
  const finalStatusToken = String(last?.status || last?.taskStatus || '').trim().toLowerCase();
267
305
  if (!TERMINAL_TASK.has(finalStatusToken)) {
306
+ if (options.soft === true) {
307
+ return last;
308
+ }
268
309
  throw new Error(
269
310
  `Work Hub task ${taskId} did not reach a terminal status within ${timeoutMs}ms (last=${finalStatusToken || 'unknown'}).`
270
311
  );
@@ -275,6 +316,7 @@ function assertTerminalWorkHubTask(last, taskId, timeoutMs) {
275
316
  async function pollWorkHubTask(dataplaneUrl, authConfig, taskId, testCase, options) {
276
317
  const timeoutMs = Number(options.pollTimeoutMs) || 180000;
277
318
  const intervalMs = Number(options.pollIntervalMs) || 3000;
319
+ const soft = testCase?.expected?.soft === true;
278
320
  const started = Date.now();
279
321
  let last = null;
280
322
  const timeline = [];
@@ -301,7 +343,7 @@ async function pollWorkHubTask(dataplaneUrl, authConfig, taskId, testCase, optio
301
343
  }
302
344
 
303
345
  return {
304
- finalTask: assertTerminalWorkHubTask(last, taskId, timeoutMs),
346
+ finalTask: assertTerminalWorkHubTask(last, taskId, timeoutMs, { soft }),
305
347
  timeline,
306
348
  interactions: ctx.interactions
307
349
  };
@@ -347,6 +389,7 @@ function buildWorkHubLifecycleResult(roleKey, testCase, started, pollState, peBu
347
389
  }
348
390
 
349
391
  async function runWorkHubCaseLifecycle(roleKey, testCase, runtimeContext, options = {}) {
392
+ const { startWorkHubOrSoftClarify } = require('./test-runner-workhub-soft');
350
393
  const dataplaneUrl = runtimeContext?.dataplaneUrl;
351
394
  const authConfig = runtimeContext?.authConfig;
352
395
  const objective = resolveObjective(testCase);
@@ -363,13 +406,15 @@ async function runWorkHubCaseLifecycle(roleKey, testCase, runtimeContext, option
363
406
  objective,
364
407
  digitalWorkerKey: String(input.digitalWorkerKey || roleKey).trim()
365
408
  };
366
- const started = await createActivateAcceptPlan(
367
- dataplaneUrl,
368
- authConfig,
369
- planBody,
370
- assistantName,
371
- options
409
+ const gate = await startWorkHubOrSoftClarify(
410
+ createActivateAcceptPlan,
411
+ { dataplaneUrl, authConfig, planBody, assistantName, options },
412
+ { roleKey, testCase }
372
413
  );
414
+ if (gate.soft) {
415
+ return gate.result;
416
+ }
417
+ const started = gate.started;
373
418
  const pollState = await pollWorkHubTask(
374
419
  dataplaneUrl,
375
420
  authConfig,
@@ -18,6 +18,11 @@ const {
18
18
  getWorkExecutionResult
19
19
  } = require('../api/work.api');
20
20
  const { wantsWorkHubPath, runWorkHubCaseLifecycle } = require('./test-runner-workhub');
21
+ const {
22
+ buildStartIntentPayload,
23
+ resolveInputObject,
24
+ resolveInputMessage
25
+ } = require('./test-runner-start-payload');
21
26
  const TERMINAL_STATUSES = new Set(['completed', 'stopped']);
22
27
  const WAITING_STATUS = 'waiting';
23
28
 
@@ -52,22 +57,6 @@ function applyOptionalStringField(payload, value, key) {
52
57
  }
53
58
  }
54
59
 
55
- function resolveInputObject(testCase) {
56
- const input = testCase && typeof testCase.input === 'object' && testCase.input ? testCase.input : {};
57
- return Array.isArray(input) ? {} : input;
58
- }
59
-
60
- function resolveInputMessage(testCase) {
61
- const input = resolveInputObject(testCase);
62
- const candidates = [input.message, input.text, input.prompt];
63
- for (const row of candidates) {
64
- if (typeof row === 'string' && row.trim()) {
65
- return row.trim();
66
- }
67
- }
68
- return '';
69
- }
70
-
71
60
  function isExecutableRoleAssistantCase(testCase) {
72
61
  const input = resolveInputObject(testCase);
73
62
  if (typeof input.content !== 'undefined') {
@@ -79,53 +68,6 @@ function isExecutableRoleAssistantCase(testCase) {
79
68
  return false;
80
69
  }
81
70
 
82
- function buildStartPayloadSourceMetadata(roleKey, testCase, input) {
83
- return {
84
- roleKey,
85
- caseId: testCase.caseId,
86
- runner: 'aifabrix-role-assistant-test',
87
- ...(input.sourceMetadata && typeof input.sourceMetadata === 'object' ? input.sourceMetadata : {})
88
- };
89
- }
90
-
91
- function applyStartPayloadOptionalFields(payload, input) {
92
- const mappedFields = [
93
- ['activationId', 'activationId'],
94
- ['workerTaskId', 'workerTaskId'],
95
- ['principalRef', 'principalRef'],
96
- ['businessRoleRef', 'businessRoleRef']
97
- ];
98
- for (const [inputKey, payloadKey] of mappedFields) {
99
- applyOptionalStringField(payload, input[inputKey], payloadKey);
100
- }
101
- }
102
-
103
- function buildStartIntentPayload(roleKey, testCase) {
104
- const input = resolveInputObject(testCase);
105
- const message = resolveInputMessage(testCase);
106
- const hasContent = typeof input.content !== 'undefined';
107
- if (!hasContent && !message) {
108
- throw new Error(
109
- `Role assistant test case "${testCase.caseId}" is missing input.message or input.content for runtime execution.`
110
- );
111
- }
112
- const payload = {
113
- submissionId: String(input.submissionId || buildRuntimeId(`ra-${roleKey}`)),
114
- content: hasContent ? input.content : message,
115
- comModelRef: String(input.comModelRef || 'aifabrix.com/intent/default'),
116
- comModelVersion: String(input.comModelVersion || '1.0'),
117
- comVocabulary: Array.isArray(input.comVocabulary) && input.comVocabulary.length > 0
118
- ? input.comVocabulary.map((row) => String(row))
119
- : ['objective', 'entity', 'action'],
120
- sourceType: String(input.sourceType || 'api'),
121
- sourceLanguage: String(input.sourceLanguage || 'en'),
122
- responseLanguage: String(input.responseLanguage || input.sourceLanguage || 'en'),
123
- sourceMetadata: buildStartPayloadSourceMetadata(roleKey, testCase, input)
124
- };
125
- applyStartPayloadOptionalFields(payload, input);
126
- return payload;
127
- }
128
-
129
71
  function extractProcessExecutionId(startResponse) {
130
72
  const candidates = [
131
73
  startResponse?.intent?.processExecutionId,