@aifabrix/builder 2.51.0 → 2.51.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 (162) hide show
  1. package/jest.isolated-projects.js +77 -1
  2. package/jest.projects.js +5 -426
  3. package/lib/api/assistants.api.js +67 -0
  4. package/lib/api/datasources-extended.api.js +6 -6
  5. package/lib/api/evidence-authoring-helpers.js +267 -0
  6. package/lib/api/evidence-authoring.api.js +337 -0
  7. package/lib/api/evidence.api.js +57 -1
  8. package/lib/api/index.js +24 -7
  9. package/lib/api/pipeline.api.js +43 -3
  10. package/lib/api/records-bulk.api.js +28 -3
  11. package/lib/api/types/evidence.types.js +156 -8
  12. package/lib/api/work.api.js +283 -0
  13. package/lib/app/run-deploy-job.js +108 -0
  14. package/lib/app/run-helpers.js +10 -3
  15. package/lib/app/run.js +8 -0
  16. package/lib/channels/add-channel.js +54 -23
  17. package/lib/channels/approval-guides/chatgpt.js +8 -7
  18. package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
  19. package/lib/channels/channel-artifact-generator.js +14 -2
  20. package/lib/channels/channel-credential.js +3 -1
  21. package/lib/channels/channel-probe-evidence.js +79 -0
  22. package/lib/channels/channel-probe.js +265 -13
  23. package/lib/channels/channel-runtime-resolver.js +120 -29
  24. package/lib/channels/channel-status-display.js +44 -0
  25. package/lib/channels/channel-targets.js +7 -0
  26. package/lib/channels/chatgpt-runtime-contract.js +29 -0
  27. package/lib/channels/microsoft-copilot-package.js +280 -0
  28. package/lib/channels/runtime-tool-catalog.js +165 -0
  29. package/lib/cli/index.js +2 -0
  30. package/lib/cli/setup-app.js +4 -0
  31. package/lib/cli/setup-external-system-help.js +8 -2
  32. package/lib/cli/setup-external-system.js +12 -0
  33. package/lib/commands/app-test.js +2 -14
  34. package/lib/commands/datasource-load-export-cli.js +6 -1
  35. package/lib/commands/deploy-gate-failure-details.js +19 -17
  36. package/lib/commands/dev-down.js +2 -6
  37. package/lib/commands/evidence-kind.js +65 -15
  38. package/lib/commands/governance-generate-external.js +56 -21
  39. package/lib/commands/lifecycle-external.js +10 -4
  40. package/lib/commands/repair-datasource-apply.js +10 -1
  41. package/lib/commands/repair-datasource-run.js +4 -2
  42. package/lib/commands/repair-datasource-test-payload.js +80 -19
  43. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
  44. package/lib/commands/repair-internal.js +9 -1
  45. package/lib/commands/repair-openapi-sync.js +34 -17
  46. package/lib/commands/role-assistant.js +313 -0
  47. package/lib/commands/upload-data-sync-finalize.js +5 -4
  48. package/lib/commands/upload-sidecar-followups.js +144 -47
  49. package/lib/commands/upload.js +43 -6
  50. package/lib/commands/verify-governance-combined-external.js +73 -9
  51. package/lib/commands/verify-governance-db.js +80 -25
  52. package/lib/commands/verify-governance-external.js +29 -5
  53. package/lib/commands/wizard-core-helpers.js +7 -3
  54. package/lib/commands/wizard-core.js +78 -9
  55. package/lib/datasource/bulk-loader-run-batches.js +4 -2
  56. package/lib/datasource/bulk-loader-service.js +5 -1
  57. package/lib/datasource/datasource-validate-summary.js +2 -2
  58. package/lib/datasource/validate.js +13 -6
  59. package/lib/external-system/deploy-helpers.js +35 -1
  60. package/lib/external-system/deploy.js +23 -10
  61. package/lib/external-system/download-helpers.js +111 -2
  62. package/lib/external-system/download-role-assistant.js +55 -0
  63. package/lib/external-system/download.js +45 -26
  64. package/lib/external-system/test-helpers.js +1 -1
  65. package/lib/external-system/test.js +39 -3
  66. package/lib/generator/builders.js +3 -0
  67. package/lib/generator/wizard.js +51 -4
  68. package/lib/lifecycle/access-control-abac.js +121 -0
  69. package/lib/lifecycle/lifecycle-governance-options.js +4 -1
  70. package/lib/lifecycle/product-model.js +3 -2
  71. package/lib/lifecycle/report-display.js +14 -2
  72. package/lib/programmatic/capability-registry.js +20 -1
  73. package/lib/programmatic/command-options.js +1 -0
  74. package/lib/programmatic/index.js +4 -0
  75. package/lib/programmatic/manifest-patch-section.js +48 -9
  76. package/lib/programmatic/manifest-section-registry-data.js +3 -2
  77. package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
  78. package/lib/programmatic/openapi-component-schemas.js +16 -1
  79. package/lib/programmatic/openapi-description-enricher.js +3 -1
  80. package/lib/programmatic/openapi-descriptions.js +55 -5
  81. package/lib/programmatic/openapi-evidence-schemas.js +161 -0
  82. package/lib/programmatic/openapi-manifest-schemas.js +6 -2
  83. package/lib/programmatic/openapi-operation-bindings.js +99 -0
  84. package/lib/programmatic/openapi-request-schemas.js +36 -1
  85. package/lib/programmatic/openapi-result-schemas.js +1 -1
  86. package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
  87. package/lib/programmatic/openapi-schema-descriptions-core.js +1 -1
  88. package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
  89. package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
  90. package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
  91. package/lib/programmatic/openapi-spec.js +47 -28
  92. package/lib/programmatic/orchestration-openapi-meta.js +5 -0
  93. package/lib/programmatic/request-timeout.js +3 -0
  94. package/lib/programmatic/route-handler-map.js +89 -2
  95. package/lib/programmatic/run-certify.js +15 -2
  96. package/lib/programmatic/run-deploy.js +74 -0
  97. package/lib/programmatic/run-evidence-authoring.js +80 -0
  98. package/lib/programmatic/run-protection.js +124 -0
  99. package/lib/programmatic/run-template-workspace.js +28 -1
  100. package/lib/programmatic/workspace-context.js +6 -0
  101. package/lib/protection/auth-context.js +48 -6
  102. package/lib/protection/paths.js +22 -0
  103. package/lib/protection/run-protection-create-helpers.js +22 -1
  104. package/lib/protection/run-protection-create.js +1 -1
  105. package/lib/protection/sync-after-upload.js +1 -1
  106. package/lib/role-assistant/evidence-sync.js +450 -0
  107. package/lib/role-assistant/github-workflow.js +96 -0
  108. package/lib/role-assistant/knowledge-sync.js +465 -0
  109. package/lib/role-assistant/settings.js +126 -0
  110. package/lib/role-assistant/test-cases-evaluate.js +170 -0
  111. package/lib/role-assistant/test-cases-honesty.js +184 -0
  112. package/lib/role-assistant/test-cases.js +364 -0
  113. package/lib/role-assistant/test-runner-workhub-answers.js +203 -0
  114. package/lib/role-assistant/test-runner-workhub.js +385 -0
  115. package/lib/role-assistant/test-runner.js +500 -0
  116. package/lib/schema/application-schema.json +16 -2
  117. package/lib/schema/deployment-rules.yaml +1 -0
  118. package/lib/schema/external-datasource.schema.json +38 -4
  119. package/lib/schema/external-system.schema.json +4 -4
  120. package/lib/schema/infra.parameter.yaml +20 -0
  121. package/lib/schema/role-assistant-settings.schema.json +108 -0
  122. package/lib/utils/api.js +52 -27
  123. package/lib/utils/app-path-resolve.js +72 -24
  124. package/lib/utils/authentication-shape.js +3 -1
  125. package/lib/utils/deployment-validation.js +28 -3
  126. package/lib/utils/docker-run-ephemeral.js +55 -0
  127. package/lib/utils/enterprise-knowledge-paths.js +29 -12
  128. package/lib/utils/env-copy-keycloak.js +196 -0
  129. package/lib/utils/env-copy.js +34 -1
  130. package/lib/utils/external-system-readiness-compact-display.js +3 -0
  131. package/lib/utils/help-builder.js +1 -0
  132. package/lib/utils/paths.js +67 -23
  133. package/lib/utils/pipeline-upload-auth-retry.js +15 -3
  134. package/lib/utils/role-assistant-paths.js +98 -0
  135. package/lib/utils/schema-loader.js +41 -0
  136. package/lib/utils/schema-resolver.js +36 -4
  137. package/lib/utils/token-manager.js +38 -29
  138. package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
  139. package/lib/validation/external-manifest-validator.js +31 -8
  140. package/lib/validation/validate-external-file.js +8 -1
  141. package/package.json +2 -1
  142. package/scripts/export-builder-api-evidence.js +1 -1
  143. package/templates/applications/dataplane/application.yaml +23 -15
  144. package/templates/applications/dataplane/env.template +1 -1
  145. package/templates/applications/dataplane/rbac.yaml +14 -0
  146. package/templates/applications/miso-controller/application.yaml +1 -1
  147. package/templates/applications/miso-controller/env.template +3 -1
  148. package/templates/channels/chatgpt/install.template.md.hbs +42 -24
  149. package/templates/channels/chatgpt/integration/README.md +10 -7
  150. package/templates/channels/chatgpt/integration/application.json +2 -2
  151. package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
  152. package/templates/channels/chatgpt/integration/env.template +1 -1
  153. package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
  154. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
  155. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
  156. package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
  157. package/templates/channels/microsoft-copilot/integration/README.md +9 -7
  158. package/templates/channels/microsoft-copilot/integration/application.json +2 -2
  159. package/templates/channels/microsoft-copilot/integration/env.template +1 -1
  160. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
  161. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
  162. /package/.cursor/plans/{157.1-security-quality-violations-by-type.csv → Done/157.1-security-quality-violations-by-type.csv} +0 -0
package/lib/api/index.js CHANGED
@@ -79,7 +79,7 @@ class ApiClient {
79
79
  headers['Content-Type'] = 'application/json';
80
80
  }
81
81
 
82
- // User token (bearer) → Authorization: Bearer; application token (client-token) → x-client-token
82
+ // User token (bearer) → Authorization: Bearer; app token (client-token) → x-client-token.
83
83
  if (this.authConfig.type === 'bootstrap' && this.authConfig.encryptionKey) {
84
84
  headers['x-encryption-key'] = String(this.authConfig.encryptionKey);
85
85
  } else if (this.authConfig.token) {
@@ -160,7 +160,10 @@ class ApiClient {
160
160
  transport.timeoutMs = options.timeoutMs;
161
161
  }
162
162
 
163
- if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
163
+ if (
164
+ this.authConfig.type === 'bearer' ||
165
+ this.authConfig.type === 'client-token'
166
+ ) {
164
167
  // Pass full authConfig to enable proper token refresh using controller URL
165
168
  return await authenticatedApiCall(url, transport, this.authConfig);
166
169
  }
@@ -213,7 +216,10 @@ class ApiClient {
213
216
  requestOptions.timeoutMs = options.timeoutMs;
214
217
  }
215
218
 
216
- if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
219
+ if (
220
+ this.authConfig.type === 'bearer' ||
221
+ this.authConfig.type === 'client-token'
222
+ ) {
217
223
  // Pass full authConfig to enable proper token refresh using controller URL
218
224
  return await authenticatedApiCall(url, requestOptions, this.authConfig);
219
225
  }
@@ -241,7 +247,9 @@ class ApiClient {
241
247
  body: formData
242
248
  };
243
249
 
244
- const hasToken = this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token';
250
+ const hasToken =
251
+ this.authConfig.type === 'bearer' ||
252
+ this.authConfig.type === 'client-token';
245
253
  if (hasToken && this.authConfig.token) {
246
254
  return await authenticatedApiCall(url, requestOptions, this.authConfig);
247
255
  }
@@ -272,7 +280,10 @@ class ApiClient {
272
280
  requestOptions.body = JSON.stringify(options.body);
273
281
  }
274
282
 
275
- if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
283
+ if (
284
+ this.authConfig.type === 'bearer' ||
285
+ this.authConfig.type === 'client-token'
286
+ ) {
276
287
  // Pass full authConfig to enable proper token refresh using controller URL
277
288
  return await authenticatedApiCall(url, requestOptions, this.authConfig);
278
289
  }
@@ -303,7 +314,10 @@ class ApiClient {
303
314
  requestOptions.body = JSON.stringify(options.body);
304
315
  }
305
316
 
306
- if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
317
+ if (
318
+ this.authConfig.type === 'bearer' ||
319
+ this.authConfig.type === 'client-token'
320
+ ) {
307
321
  // Pass full authConfig to enable proper token refresh using controller URL
308
322
  return await authenticatedApiCall(url, requestOptions, this.authConfig);
309
323
  }
@@ -329,7 +343,10 @@ class ApiClient {
329
343
  headers
330
344
  };
331
345
 
332
- if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
346
+ if (
347
+ this.authConfig.type === 'bearer' ||
348
+ this.authConfig.type === 'client-token'
349
+ ) {
333
350
  // Pass full authConfig to enable proper token refresh using controller URL
334
351
  return await authenticatedApiCall(url, requestOptions, this.authConfig);
335
352
  }
@@ -249,13 +249,51 @@ async function testDatasourceViaPipeline({ dataplaneUrl, systemKey, datasourceKe
249
249
  */
250
250
  /** Multi-datasource publish often exceeds the default 30s HTTP timeout. */
251
251
  const PIPELINE_UPLOAD_TIMEOUT_MS = 300000;
252
+ const PIPELINE_UPLOAD_TIMEOUT_MIN_MS = 5000;
253
+ const PIPELINE_UPLOAD_TIMEOUT_MAX_MS = 45 * 60 * 1000;
252
254
 
253
- async function uploadApplicationViaPipeline(dataplaneUrl, authConfig, payload) {
255
+ /**
256
+ * Resolve pipeline upload timeout (ms) for HTTP + dataplane budget.
257
+ * @param {number|string|undefined|null} raw
258
+ * @returns {number}
259
+ */
260
+ function resolvePipelineUploadTimeoutMs(raw) {
261
+ if (raw === undefined || raw === null || raw === '') {
262
+ return PIPELINE_UPLOAD_TIMEOUT_MS;
263
+ }
264
+ const n = typeof raw === 'number' ? raw : Number(raw);
265
+ if (!Number.isFinite(n) || n <= 0) {
266
+ throw new Error(
267
+ `--timeout must be a positive number of milliseconds (got ${String(raw)})`
268
+ );
269
+ }
270
+ return Math.min(
271
+ PIPELINE_UPLOAD_TIMEOUT_MAX_MS,
272
+ Math.max(PIPELINE_UPLOAD_TIMEOUT_MIN_MS, Math.floor(n))
273
+ );
274
+ }
275
+
276
+ /**
277
+ * @param {string} dataplaneUrl
278
+ * @param {Object} authConfig
279
+ * @param {Object} payload
280
+ * @param {Object} [options]
281
+ * @param {number|string} [options.timeoutMs] - HTTP + dataplane publish budget (default 300000)
282
+ */
283
+ async function uploadApplicationViaPipeline(
284
+ dataplaneUrl,
285
+ authConfig,
286
+ payload,
287
+ options = {}
288
+ ) {
254
289
  const body = { ...payload, status: payload.status ?? 'draft' };
290
+ const timeoutMs = resolvePipelineUploadTimeoutMs(options.timeoutMs);
255
291
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
256
292
  return await client.post('/api/v1/pipeline/upload', {
257
293
  body,
258
- timeoutMs: PIPELINE_UPLOAD_TIMEOUT_MS
294
+ timeoutMs,
295
+ params: { timeoutMs: String(timeoutMs) },
296
+ headers: { 'X-Aifabrix-Timeout-Ms': String(timeoutMs) }
259
297
  });
260
298
  }
261
299
 
@@ -268,6 +306,8 @@ module.exports = {
268
306
  validatePipelineConfig,
269
307
  testSystemViaPipeline,
270
308
  testDatasourceViaPipeline,
271
- uploadApplicationViaPipeline
309
+ uploadApplicationViaPipeline,
310
+ resolvePipelineUploadTimeoutMs,
311
+ PIPELINE_UPLOAD_TIMEOUT_MS
272
312
  };
273
313
 
@@ -7,6 +7,26 @@
7
7
  const { createDataplaneApiClient } = require('./index');
8
8
  const { unwrapApiData } = require('../utils/external-system-readiness-core');
9
9
 
10
+ /** Default HTTP budget for records/bulk (ABAC projection can exceed 30s under load). */
11
+ const DEFAULT_BULK_SYNC_TIMEOUT_MS = 120000;
12
+
13
+ /**
14
+ * Resolve per-request timeout for bulk sync.
15
+ * @param {Object} [transportOpts]
16
+ * @returns {number}
17
+ */
18
+ function resolveBulkSyncTimeoutMs(transportOpts = {}) {
19
+ const raw = transportOpts.timeoutMs ?? transportOpts.timeout;
20
+ if (raw === undefined || raw === null || raw === '') {
21
+ return DEFAULT_BULK_SYNC_TIMEOUT_MS;
22
+ }
23
+ const n = typeof raw === 'number' ? raw : parseInt(String(raw), 10);
24
+ if (!Number.isFinite(n) || n <= 0) {
25
+ return DEFAULT_BULK_SYNC_TIMEOUT_MS;
26
+ }
27
+ return n;
28
+ }
29
+
10
30
  /**
11
31
  * Bulk sync external records for a datasource.
12
32
  * POST /api/v1/data-storage/{sourceIdOrKey}/records/bulk
@@ -16,13 +36,16 @@ const { unwrapApiData } = require('../utils/external-system-readiness-core');
16
36
  * @param {string} sourceIdOrKey
17
37
  * @param {Object} authConfig
18
38
  * @param {import('./types/records-bulk.types').ExternalRecordBulkRequest} body
39
+ * @param {Object} [transportOpts]
40
+ * @param {number} [transportOpts.timeoutMs] - Per-request HTTP timeout (default 120000)
19
41
  * @returns {Promise<import('./types/records-bulk.types').ExternalRecordBulkResponse>}
20
42
  */
21
- async function bulkSyncRecords(dataplaneUrl, sourceIdOrKey, authConfig, body) {
43
+ async function bulkSyncRecords(dataplaneUrl, sourceIdOrKey, authConfig, body, transportOpts = {}) {
22
44
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
45
+ const timeoutMs = resolveBulkSyncTimeoutMs(transportOpts);
23
46
  const res = await client.post(
24
47
  `/api/v1/data-storage/${encodeURIComponent(sourceIdOrKey)}/records/bulk`,
25
- { body }
48
+ { body, timeoutMs }
26
49
  );
27
50
  if (res && res.success === false) {
28
51
  const msg =
@@ -39,5 +62,7 @@ async function bulkSyncRecords(dataplaneUrl, sourceIdOrKey, authConfig, body) {
39
62
  }
40
63
 
41
64
  module.exports = {
42
- bulkSyncRecords
65
+ bulkSyncRecords,
66
+ resolveBulkSyncTimeoutMs,
67
+ DEFAULT_BULK_SYNC_TIMEOUT_MS
43
68
  };
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * @fileoverview JSDoc types for Evidence Fabrix dataplane API.
3
3
  * @author AI Fabrix Team
4
- * @version 1.0.0
4
+ * @version 1.1.0
5
5
  */
6
6
 
7
7
  /**
8
- * @typedef {'active' | 'disabled'} EvidenceStatus
8
+ * @typedef {'candidate' | 'active' | 'disabled' | 'archived'} EvidenceStatus
9
9
  */
10
10
 
11
11
  /**
@@ -19,11 +19,44 @@
19
19
  * @property {string|null} [roleKey]
20
20
  */
21
21
 
22
+ /**
23
+ * @typedef {Object} EvidenceContributionOverride
24
+ * @property {string} contributionKey
25
+ * @property {number} referenceValue
26
+ */
27
+
28
+ /**
29
+ * @typedef {Object} EvidenceValueProposal
30
+ * @property {number|null} [primaryReferenceValue]
31
+ * @property {string|null} [primaryContributionKey]
32
+ * @property {string|null} [unit]
33
+ * @property {string|null} [rationale]
34
+ * @property {EvidenceContributionOverride[]} [overrides]
35
+ */
36
+
37
+ /**
38
+ * @typedef {Object} EvidenceContributionProjectionItem
39
+ * @property {string} contributionKey
40
+ * @property {string|null} [typeRef]
41
+ * @property {string|null} [unit]
42
+ * @property {boolean} [primary]
43
+ * @property {number|null} [inheritedReferenceValue]
44
+ * @property {number|null} [effectiveReferenceValue]
45
+ * @property {boolean} [overridden]
46
+ * @property {string|null} [attributionKey]
47
+ * @property {string|null} [attributionBoundary]
48
+ * @property {string|null} [kindKey]
49
+ * @property {string|null} [kindVersion]
50
+ */
51
+
22
52
  /**
23
53
  * @typedef {Object} EvidenceRow
24
54
  * @property {string} id
25
- * @property {string} resourceType
55
+ * @property {string|null} [key]
56
+ * @property {string|null} [resourceType]
26
57
  * @property {string|null} [roleKey]
58
+ * @property {string|null} [kindKey]
59
+ * @property {string|null} [kindVersion]
27
60
  * @property {string} title
28
61
  * @property {string|null} [description]
29
62
  * @property {string} when
@@ -32,23 +65,34 @@
32
65
  * @property {EvidenceStatus} status
33
66
  * @property {number|null} [generationConfidence]
34
67
  * @property {string|null} [suggestionExplanation]
68
+ * @property {EvidenceContributionOverride[]|null} [contributionOverrides]
69
+ * @property {EvidenceValueProposal|null} [valueProposal]
70
+ * @property {EvidenceContributionProjectionItem[]|null} [contributionProjection]
71
+ * @property {string|null} [fingerprint]
72
+ * @property {Object|null} [provenanceSummary]
73
+ * @property {Object|null} [trustProjections]
35
74
  * @property {EvidenceApprovalPolicy} approval
36
- * @property {'human' | 'assistant'} createdByType
37
- * @property {string} createdById
38
- * @property {string} createdAt
39
- * @property {string} updatedAt
75
+ * @property {'human' | 'assistant'} [createdByType]
76
+ * @property {string|Object} [createdById]
77
+ * @property {string} [createdAt]
78
+ * @property {string} [updatedAt]
40
79
  */
41
80
 
42
81
  /**
43
82
  * @typedef {Object} EvidenceCreatePayload
44
- * @property {string} resourceType
83
+ * @property {string|null} [key]
84
+ * @property {string|null} [resourceType]
45
85
  * @property {string|null} [roleKey]
86
+ * @property {string|null} [kindKey]
87
+ * @property {string|null} [kindVersion]
46
88
  * @property {string} title
47
89
  * @property {string|null} [description]
48
90
  * @property {string} when
49
91
  * @property {string} expectedBehavior
50
92
  * @property {number|null} [order]
51
93
  * @property {EvidenceStatus} [status]
94
+ * @property {EvidenceContributionOverride[]} [contributionOverrides]
95
+ * @property {string|null} [suggestionExplanation]
52
96
  * @property {EvidenceApprovalPolicy} [approval]
53
97
  */
54
98
 
@@ -62,6 +106,110 @@
62
106
  * @property {string} [roleKey]
63
107
  * @property {boolean} [genericOnly]
64
108
  * @property {EvidenceStatus} [status]
109
+ * @property {string} [kindKey]
110
+ * @property {string} [aspect]
111
+ * @property {string} [search]
112
+ * @property {number} [limit]
113
+ * @property {number} [offset]
114
+ */
115
+
116
+ /**
117
+ * @typedef {Object} EvidenceMetadataKind
118
+ * @property {string|null} key
119
+ * @property {string|null} version
120
+ * @property {string|null} [displayName]
121
+ * @property {string|null} [purpose]
122
+ */
123
+
124
+ /**
125
+ * @typedef {Object} EvidenceMetadataResourceType
126
+ * @property {string|null} key
127
+ * @property {string|null} [displayName]
128
+ * @property {string|null} [category]
129
+ */
130
+
131
+ /**
132
+ * @typedef {Object} EvidenceAuthoringMetadata
133
+ * @property {string} roleKey
134
+ * @property {EvidenceMetadataKind[]} evidenceKinds
135
+ * @property {string[]} aspects
136
+ * @property {EvidenceMetadataResourceType[]} resourceTypes
137
+ * @property {string[]} assistantCapabilities
138
+ * @property {string[]} writableFields
139
+ * @property {string[]} requiredFields
140
+ * @property {Object} constraints
141
+ * @property {Object} constraints.certification
142
+ * @property {string} constraints.certification.validationStatus
143
+ * @property {string} constraints.certification.candidateStatus
144
+ * @property {Object} constraints.activation
145
+ * @property {boolean} constraints.activation.requiresCertified
146
+ * @property {boolean} constraints.activation.requiresAuthority
147
+ */
148
+
149
+ /**
150
+ * @typedef {EvidenceListParams & { roleKey: string }} EvidenceDownloadParams
151
+ */
152
+
153
+ /**
154
+ * @typedef {Object} EvidenceDownloadResponse
155
+ * @property {string} roleKey
156
+ * @property {number} count
157
+ * @property {EvidenceRow[]} data
158
+ */
159
+
160
+ /**
161
+ * @typedef {Object} EvidenceUploadRequest
162
+ * @property {string} roleKey
163
+ * @property {EvidenceCreatePayload[]} rows
164
+ * @property {string} [comment]
165
+ */
166
+
167
+ /**
168
+ * @typedef {Object} EvidenceUploadAcceptedItem
169
+ * @property {number} index
170
+ * @property {string} evidenceRef
171
+ * @property {string} status
172
+ */
173
+
174
+ /**
175
+ * @typedef {Object} EvidenceUploadRejectedItem
176
+ * @property {number} index
177
+ * @property {string} error
178
+ * @property {string} [evidenceRef]
179
+ * @property {Object[]} [issues]
180
+ */
181
+
182
+ /**
183
+ * @typedef {Object} EvidenceUploadCertifiedItem
184
+ * @property {number} index
185
+ * @property {string} evidenceRef
186
+ * @property {string} status
187
+ * @property {Object} data
188
+ */
189
+
190
+ /**
191
+ * @typedef {Object} EvidenceUploadResponse
192
+ * @property {string} roleKey
193
+ * @property {number} inserted
194
+ * @property {number} updated
195
+ * @property {EvidenceUploadAcceptedItem[]} accepted
196
+ * @property {EvidenceUploadRejectedItem[]} rejected
197
+ * @property {Object[]} warnings
198
+ * @property {EvidenceUploadCertifiedItem[]} certified
199
+ * @property {boolean} readyToActivate
200
+ */
201
+
202
+ /**
203
+ * @typedef {Object} EvidenceActivateRequest
204
+ * @property {string[]} evidenceIds
205
+ */
206
+
207
+ /**
208
+ * @typedef {Object} EvidenceActivateResponse
209
+ * @property {number} requested
210
+ * @property {Array<{ evidenceId: string, result: Object }>} activated
211
+ * @property {Array<{ evidenceId: string, error: string, status: number|null }>} failed
212
+ * @property {boolean} success
65
213
  */
66
214
 
67
215
  module.exports = {};
@@ -0,0 +1,283 @@
1
+ /**
2
+ * @fileoverview Dataplane work runtime API client for role-assistant test flows.
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { createDataplaneApiClient } = require('./index');
10
+ const { unwrapApiData } = require('../utils/external-system-readiness-core');
11
+
12
+ /** Work Hub plan create runs LLM planning; 30s default is too short under load/reload. */
13
+ const WORK_PLAN_TIMEOUT_MS = 120000;
14
+ /** Task poll / result fetch can wait on Celery + CIP. */
15
+ const WORK_TASK_TIMEOUT_MS = 60000;
16
+
17
+ /**
18
+ * Some dataplane work routes return `{ data: { … } }` while ApiClient already
19
+ * wraps once — peel a nested `data` envelope when top-level identity is missing.
20
+ */
21
+ function unwrapWorkPayload(res) {
22
+ const cur = unwrapApiData(res);
23
+ if (!cur || typeof cur !== 'object' || Array.isArray(cur)) {
24
+ return cur;
25
+ }
26
+ if (cur.planId || cur.taskId || cur.processExecutionId || cur.status || cur.schemaVersion) {
27
+ return cur;
28
+ }
29
+ if (cur.data && typeof cur.data === 'object' && !Array.isArray(cur.data)) {
30
+ return cur.data;
31
+ }
32
+ return cur;
33
+ }
34
+
35
+ function extractApiErrorMessage(res, fallbackMessage) {
36
+ return (
37
+ (res &&
38
+ res.error &&
39
+ (res.error.formattedError || res.error.error || res.error.message || res.error.detail)) ||
40
+ (res && (res.formattedError || res.error || res.message)) ||
41
+ fallbackMessage
42
+ );
43
+ }
44
+
45
+ function assertObjectShape(value, fallbackMessage) {
46
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
47
+ throw new Error(fallbackMessage);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * POST /api/v1/work/start
53
+ * @requiresPermission {Dataplane} worker-task:create
54
+ * @async
55
+ * @param {string} dataplaneUrl
56
+ * @param {Object} authConfig
57
+ * @param {Object} body
58
+ * @returns {Promise<Object>}
59
+ */
60
+ async function startWorkIntent(dataplaneUrl, authConfig, body) {
61
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
62
+ const res = await client.post('/api/v1/work/start', { body });
63
+ if (res && res.success === false) {
64
+ throw new Error(extractApiErrorMessage(res, 'work.start request failed'));
65
+ }
66
+ const unwrapped = unwrapWorkPayload(res);
67
+ assertObjectShape(unwrapped, 'work.start returned an unexpected response shape');
68
+ return unwrapped;
69
+ }
70
+
71
+ /**
72
+ * GET /api/v1/work/executions/{processExecutionId}
73
+ * @requiresPermission {Dataplane} worker-task:read
74
+ * @async
75
+ * @param {string} dataplaneUrl
76
+ * @param {Object} authConfig
77
+ * @param {string} processExecutionId
78
+ * @returns {Promise<Object>}
79
+ */
80
+ async function getWorkExecutionStatus(dataplaneUrl, authConfig, processExecutionId) {
81
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
82
+ const encodedId = encodeURIComponent(String(processExecutionId));
83
+ const res = await client.get(`/api/v1/work/executions/${encodedId}`, {
84
+ timeoutMs: WORK_TASK_TIMEOUT_MS
85
+ });
86
+ if (res && res.success === false) {
87
+ throw new Error(extractApiErrorMessage(res, 'work.status request failed'));
88
+ }
89
+ const unwrapped = unwrapWorkPayload(res);
90
+ assertObjectShape(unwrapped, 'work.status returned an unexpected response shape');
91
+ return unwrapped;
92
+ }
93
+
94
+ /**
95
+ * POST /api/v1/work/executions/{processExecutionId}/respond
96
+ * @requiresPermission {Dataplane} worker-task:update
97
+ * @async
98
+ * @param {string} dataplaneUrl
99
+ * @param {Object} authConfig
100
+ * @param {string} processExecutionId
101
+ * @param {Object} body
102
+ * @returns {Promise<Object>}
103
+ */
104
+ async function respondWorkExecution(dataplaneUrl, authConfig, processExecutionId, body) {
105
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
106
+ const encodedId = encodeURIComponent(String(processExecutionId));
107
+ const res = await client.post(`/api/v1/work/executions/${encodedId}/respond`, {
108
+ body,
109
+ timeoutMs: WORK_TASK_TIMEOUT_MS
110
+ });
111
+ if (res && res.success === false) {
112
+ throw new Error(extractApiErrorMessage(res, 'work.respond request failed'));
113
+ }
114
+ const unwrapped = unwrapWorkPayload(res);
115
+ assertObjectShape(unwrapped, 'work.respond returned an unexpected response shape');
116
+ return unwrapped;
117
+ }
118
+
119
+ /**
120
+ * POST /api/v1/work/executions/{processExecutionId}/continue
121
+ * @requiresPermission {Dataplane} worker-task:update
122
+ * @async
123
+ * @param {string} dataplaneUrl
124
+ * @param {Object} authConfig
125
+ * @param {string} processExecutionId
126
+ * @param {Object} [body]
127
+ * @returns {Promise<Object>}
128
+ */
129
+ async function continueWorkExecution(dataplaneUrl, authConfig, processExecutionId, body = {}) {
130
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
131
+ const encodedId = encodeURIComponent(String(processExecutionId));
132
+ const res = await client.post(`/api/v1/work/executions/${encodedId}/continue`, {
133
+ body,
134
+ timeoutMs: WORK_TASK_TIMEOUT_MS
135
+ });
136
+ if (res && res.success === false) {
137
+ throw new Error(extractApiErrorMessage(res, 'work.continue request failed'));
138
+ }
139
+ const unwrapped = unwrapWorkPayload(res);
140
+ assertObjectShape(unwrapped, 'work.continue returned an unexpected response shape');
141
+ return unwrapped;
142
+ }
143
+
144
+ /**
145
+ * GET /api/v1/work/executions/{processExecutionId}/result
146
+ * @requiresPermission {Dataplane} worker-task:read
147
+ * @async
148
+ * @param {string} dataplaneUrl
149
+ * @param {Object} authConfig
150
+ * @param {string} processExecutionId
151
+ * @param {Object} [query]
152
+ * @returns {Promise<Object>}
153
+ */
154
+ async function getWorkExecutionResult(dataplaneUrl, authConfig, processExecutionId, query = {}) {
155
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
156
+ const encodedId = encodeURIComponent(String(processExecutionId));
157
+ const params = {};
158
+ if (query && typeof query === 'object') {
159
+ if (query.requestedDepth) {
160
+ params.requestedDepth = String(query.requestedDepth);
161
+ }
162
+ if (query.format) {
163
+ params.format = String(query.format);
164
+ }
165
+ if (query.projection) {
166
+ params.projection = String(query.projection);
167
+ }
168
+ }
169
+ const res = await client.get(`/api/v1/work/executions/${encodedId}/result`, {
170
+ params,
171
+ timeoutMs: WORK_TASK_TIMEOUT_MS
172
+ });
173
+ if (res && res.success === false) {
174
+ throw new Error(extractApiErrorMessage(res, 'work.result request failed'));
175
+ }
176
+ const unwrapped = unwrapWorkPayload(res);
177
+ assertObjectShape(unwrapped, 'work.result returned an unexpected response shape');
178
+ return unwrapped;
179
+ }
180
+
181
+ /**
182
+ * POST /api/v1/work/tasks/plan — Work Hub plan (objective → Role Assistant).
183
+ * @requiresPermission {Dataplane} worker-task:create
184
+ */
185
+ async function createWorkTaskPlan(dataplaneUrl, authConfig, body) {
186
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
187
+ const res = await client.post('/api/v1/work/tasks/plan', {
188
+ body,
189
+ timeoutMs: WORK_PLAN_TIMEOUT_MS
190
+ });
191
+ if (res && res.success === false) {
192
+ throw new Error(extractApiErrorMessage(res, 'work.tasks.plan request failed'));
193
+ }
194
+ const unwrapped = unwrapWorkPayload(res);
195
+ assertObjectShape(unwrapped, 'work.tasks.plan returned an unexpected response shape');
196
+ return unwrapped;
197
+ }
198
+
199
+ /**
200
+ * POST /api/v1/work/tasks/plan/{planId}/activate-assistant
201
+ * @requiresPermission {Dataplane} worker-task:update
202
+ */
203
+ async function activateWorkPlanAssistant(dataplaneUrl, authConfig, planId, body) {
204
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
205
+ const encodedId = encodeURIComponent(String(planId));
206
+ const res = await client.post(`/api/v1/work/tasks/plan/${encodedId}/activate-assistant`, {
207
+ body,
208
+ timeoutMs: WORK_TASK_TIMEOUT_MS
209
+ });
210
+ if (res && res.success === false) {
211
+ throw new Error(extractApiErrorMessage(res, 'work.tasks.plan.activate-assistant failed'));
212
+ }
213
+ const unwrapped = unwrapWorkPayload(res);
214
+ assertObjectShape(unwrapped, 'activate-assistant returned an unexpected response shape');
215
+ return unwrapped;
216
+ }
217
+
218
+ /**
219
+ * POST /api/v1/work/tasks/plan/{planId}/accept
220
+ * @requiresPermission {Dataplane} worker-task:update
221
+ */
222
+ async function acceptWorkTaskPlan(dataplaneUrl, authConfig, planId, body = {}) {
223
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
224
+ const encodedId = encodeURIComponent(String(planId));
225
+ const res = await client.post(`/api/v1/work/tasks/plan/${encodedId}/accept`, {
226
+ body: { planId, ...body },
227
+ timeoutMs: WORK_PLAN_TIMEOUT_MS
228
+ });
229
+ if (res && res.success === false) {
230
+ throw new Error(extractApiErrorMessage(res, 'work.tasks.plan.accept failed'));
231
+ }
232
+ const unwrapped = unwrapWorkPayload(res);
233
+ assertObjectShape(unwrapped, 'work.tasks.plan.accept returned an unexpected response shape');
234
+ return unwrapped;
235
+ }
236
+
237
+ /**
238
+ * GET /api/v1/work/tasks/{taskId}
239
+ * @requiresPermission {Dataplane} worker-task:read
240
+ */
241
+ async function getWorkTask(dataplaneUrl, authConfig, taskId) {
242
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
243
+ const encodedId = encodeURIComponent(String(taskId));
244
+ const res = await client.get(`/api/v1/work/tasks/${encodedId}`, {
245
+ timeoutMs: WORK_TASK_TIMEOUT_MS
246
+ });
247
+ if (res && res.success === false) {
248
+ throw new Error(extractApiErrorMessage(res, 'work.tasks.get failed'));
249
+ }
250
+ const unwrapped = unwrapWorkPayload(res);
251
+ assertObjectShape(unwrapped, 'work.tasks.get returned an unexpected response shape');
252
+ return unwrapped;
253
+ }
254
+
255
+ /**
256
+ * POST /api/v1/work/tasks/{taskId}/approve
257
+ * @requiresPermission {Dataplane} worker-task:update
258
+ */
259
+ async function approveWorkTask(dataplaneUrl, authConfig, taskId, body = {}) {
260
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
261
+ const encodedId = encodeURIComponent(String(taskId));
262
+ const res = await client.post(`/api/v1/work/tasks/${encodedId}/approve`, {
263
+ body: { taskId, ...body },
264
+ timeoutMs: WORK_TASK_TIMEOUT_MS
265
+ });
266
+ if (res && res.success === false) {
267
+ throw new Error(extractApiErrorMessage(res, 'work.tasks.approve failed'));
268
+ }
269
+ return unwrapWorkPayload(res);
270
+ }
271
+
272
+ module.exports = {
273
+ startWorkIntent,
274
+ getWorkExecutionStatus,
275
+ respondWorkExecution,
276
+ continueWorkExecution,
277
+ getWorkExecutionResult,
278
+ createWorkTaskPlan,
279
+ activateWorkPlanAssistant,
280
+ acceptWorkTaskPlan,
281
+ getWorkTask,
282
+ approveWorkTask
283
+ };