@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.
- package/jest.isolated-projects.js +77 -1
- package/jest.projects.js +5 -426
- package/lib/api/assistants.api.js +67 -0
- package/lib/api/datasources-extended.api.js +6 -6
- package/lib/api/evidence-authoring-helpers.js +267 -0
- package/lib/api/evidence-authoring.api.js +337 -0
- package/lib/api/evidence.api.js +57 -1
- package/lib/api/index.js +24 -7
- package/lib/api/pipeline.api.js +43 -3
- package/lib/api/records-bulk.api.js +28 -3
- package/lib/api/types/evidence.types.js +156 -8
- package/lib/api/work.api.js +283 -0
- package/lib/app/run-deploy-job.js +108 -0
- package/lib/app/run-helpers.js +10 -3
- package/lib/app/run.js +8 -0
- package/lib/channels/add-channel.js +54 -23
- package/lib/channels/approval-guides/chatgpt.js +8 -7
- package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
- package/lib/channels/channel-artifact-generator.js +14 -2
- package/lib/channels/channel-credential.js +3 -1
- package/lib/channels/channel-probe-evidence.js +79 -0
- package/lib/channels/channel-probe.js +265 -13
- package/lib/channels/channel-runtime-resolver.js +120 -29
- package/lib/channels/channel-status-display.js +44 -0
- package/lib/channels/channel-targets.js +7 -0
- package/lib/channels/chatgpt-runtime-contract.js +29 -0
- package/lib/channels/microsoft-copilot-package.js +280 -0
- package/lib/channels/runtime-tool-catalog.js +165 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/setup-app.js +4 -0
- package/lib/cli/setup-external-system-help.js +8 -2
- package/lib/cli/setup-external-system.js +12 -0
- package/lib/commands/app-test.js +2 -14
- package/lib/commands/datasource-load-export-cli.js +6 -1
- package/lib/commands/deploy-gate-failure-details.js +19 -17
- package/lib/commands/dev-down.js +2 -6
- package/lib/commands/evidence-kind.js +65 -15
- package/lib/commands/governance-generate-external.js +56 -21
- package/lib/commands/lifecycle-external.js +10 -4
- package/lib/commands/repair-datasource-apply.js +10 -1
- package/lib/commands/repair-datasource-run.js +4 -2
- package/lib/commands/repair-datasource-test-payload.js +80 -19
- package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
- package/lib/commands/repair-internal.js +9 -1
- package/lib/commands/repair-openapi-sync.js +34 -17
- package/lib/commands/role-assistant.js +313 -0
- package/lib/commands/upload-data-sync-finalize.js +5 -4
- package/lib/commands/upload-sidecar-followups.js +144 -47
- package/lib/commands/upload.js +43 -6
- package/lib/commands/verify-governance-combined-external.js +73 -9
- package/lib/commands/verify-governance-db.js +80 -25
- package/lib/commands/verify-governance-external.js +29 -5
- package/lib/commands/wizard-core-helpers.js +7 -3
- package/lib/commands/wizard-core.js +78 -9
- package/lib/datasource/bulk-loader-run-batches.js +4 -2
- package/lib/datasource/bulk-loader-service.js +5 -1
- package/lib/datasource/datasource-validate-summary.js +2 -2
- package/lib/datasource/validate.js +13 -6
- package/lib/external-system/deploy-helpers.js +35 -1
- package/lib/external-system/deploy.js +23 -10
- package/lib/external-system/download-helpers.js +111 -2
- package/lib/external-system/download-role-assistant.js +55 -0
- package/lib/external-system/download.js +45 -26
- package/lib/external-system/test-helpers.js +1 -1
- package/lib/external-system/test.js +39 -3
- package/lib/generator/builders.js +3 -0
- package/lib/generator/wizard.js +51 -4
- package/lib/lifecycle/access-control-abac.js +121 -0
- package/lib/lifecycle/lifecycle-governance-options.js +4 -1
- package/lib/lifecycle/product-model.js +3 -2
- package/lib/lifecycle/report-display.js +14 -2
- package/lib/programmatic/capability-registry.js +20 -1
- package/lib/programmatic/command-options.js +1 -0
- package/lib/programmatic/index.js +4 -0
- package/lib/programmatic/manifest-patch-section.js +48 -9
- package/lib/programmatic/manifest-section-registry-data.js +3 -2
- package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
- package/lib/programmatic/openapi-component-schemas.js +16 -1
- package/lib/programmatic/openapi-description-enricher.js +3 -1
- package/lib/programmatic/openapi-descriptions.js +55 -5
- package/lib/programmatic/openapi-evidence-schemas.js +161 -0
- package/lib/programmatic/openapi-manifest-schemas.js +6 -2
- package/lib/programmatic/openapi-operation-bindings.js +99 -0
- package/lib/programmatic/openapi-request-schemas.js +36 -1
- package/lib/programmatic/openapi-result-schemas.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
- package/lib/programmatic/openapi-schema-descriptions-core.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
- package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
- package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
- package/lib/programmatic/openapi-spec.js +47 -28
- package/lib/programmatic/orchestration-openapi-meta.js +5 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +89 -2
- package/lib/programmatic/run-certify.js +15 -2
- package/lib/programmatic/run-deploy.js +74 -0
- package/lib/programmatic/run-evidence-authoring.js +80 -0
- package/lib/programmatic/run-protection.js +124 -0
- package/lib/programmatic/run-template-workspace.js +28 -1
- package/lib/programmatic/workspace-context.js +6 -0
- package/lib/protection/auth-context.js +48 -6
- package/lib/protection/paths.js +22 -0
- package/lib/protection/run-protection-create-helpers.js +22 -1
- package/lib/protection/run-protection-create.js +1 -1
- package/lib/protection/sync-after-upload.js +1 -1
- package/lib/role-assistant/evidence-sync.js +450 -0
- package/lib/role-assistant/github-workflow.js +96 -0
- package/lib/role-assistant/knowledge-sync.js +465 -0
- package/lib/role-assistant/settings.js +126 -0
- package/lib/role-assistant/test-cases-evaluate.js +170 -0
- package/lib/role-assistant/test-cases-honesty.js +184 -0
- package/lib/role-assistant/test-cases.js +364 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +203 -0
- package/lib/role-assistant/test-runner-workhub.js +385 -0
- package/lib/role-assistant/test-runner.js +500 -0
- package/lib/schema/application-schema.json +16 -2
- package/lib/schema/deployment-rules.yaml +1 -0
- package/lib/schema/external-datasource.schema.json +38 -4
- package/lib/schema/external-system.schema.json +4 -4
- package/lib/schema/infra.parameter.yaml +20 -0
- package/lib/schema/role-assistant-settings.schema.json +108 -0
- package/lib/utils/api.js +52 -27
- package/lib/utils/app-path-resolve.js +72 -24
- package/lib/utils/authentication-shape.js +3 -1
- package/lib/utils/deployment-validation.js +28 -3
- package/lib/utils/docker-run-ephemeral.js +55 -0
- package/lib/utils/enterprise-knowledge-paths.js +29 -12
- package/lib/utils/env-copy-keycloak.js +196 -0
- package/lib/utils/env-copy.js +34 -1
- package/lib/utils/external-system-readiness-compact-display.js +3 -0
- package/lib/utils/help-builder.js +1 -0
- package/lib/utils/paths.js +67 -23
- package/lib/utils/pipeline-upload-auth-retry.js +15 -3
- package/lib/utils/role-assistant-paths.js +98 -0
- package/lib/utils/schema-loader.js +41 -0
- package/lib/utils/schema-resolver.js +36 -4
- package/lib/utils/token-manager.js +38 -29
- package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
- package/lib/validation/external-manifest-validator.js +31 -8
- package/lib/validation/validate-external-file.js +8 -1
- package/package.json +2 -1
- package/scripts/export-builder-api-evidence.js +1 -1
- package/templates/applications/dataplane/application.yaml +23 -15
- package/templates/applications/dataplane/env.template +1 -1
- package/templates/applications/dataplane/rbac.yaml +14 -0
- package/templates/applications/miso-controller/application.yaml +1 -1
- package/templates/applications/miso-controller/env.template +3 -1
- package/templates/channels/chatgpt/install.template.md.hbs +42 -24
- package/templates/channels/chatgpt/integration/README.md +10 -7
- package/templates/channels/chatgpt/integration/application.json +2 -2
- package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
- package/templates/channels/chatgpt/integration/env.template +1 -1
- package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
- package/templates/channels/microsoft-copilot/integration/README.md +9 -7
- package/templates/channels/microsoft-copilot/integration/application.json +2 -2
- package/templates/channels/microsoft-copilot/integration/env.template +1 -1
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
- /package/.cursor/plans/{157.1-security-quality-violations-by-type.csv → Done/157.1-security-quality-violations-by-type.csv} +0 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Work Hub plan → activate → accept path for role-assistant test cases.
|
|
3
|
+
* Retries create→accept when Docker reload wipes in-process plans.
|
|
4
|
+
* @fileoverview Work Hub execution for RA cases
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
createWorkTaskPlan,
|
|
11
|
+
activateWorkPlanAssistant,
|
|
12
|
+
acceptWorkTaskPlan,
|
|
13
|
+
getWorkTask,
|
|
14
|
+
approveWorkTask,
|
|
15
|
+
getWorkExecutionStatus,
|
|
16
|
+
respondWorkExecution,
|
|
17
|
+
continueWorkExecution,
|
|
18
|
+
getWorkExecutionResult
|
|
19
|
+
} = require('../api/work.api');
|
|
20
|
+
const {
|
|
21
|
+
synthesizeWaitAnswer,
|
|
22
|
+
waitRespondFingerprint,
|
|
23
|
+
alreadyResponded,
|
|
24
|
+
buildRespondPayload
|
|
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
|
+
}
|
|
107
|
+
|
|
108
|
+
async function waitDataplaneHealthy(dataplaneUrl, attempts = 12) {
|
|
109
|
+
const base = String(dataplaneUrl || '')
|
|
110
|
+
.replace(/\/+$/, '')
|
|
111
|
+
.replace(/\/api\/v1$/, '');
|
|
112
|
+
if (!base) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
for (let i = 0; i < attempts; i += 1) {
|
|
116
|
+
try {
|
|
117
|
+
const res = await fetch(`${base}/health`);
|
|
118
|
+
if (res.ok) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
} catch (_err) {
|
|
122
|
+
// Reload in progress.
|
|
123
|
+
}
|
|
124
|
+
await delayMs(500 + i * 250);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function activateIfNeeded(dataplaneUrl, authConfig, plan, assistantName) {
|
|
129
|
+
const planId = String(plan.planId || '').trim();
|
|
130
|
+
if (!planId) {
|
|
131
|
+
throw new Error('work.tasks.plan did not return planId.');
|
|
132
|
+
}
|
|
133
|
+
if (!planAlreadyActivated(plan)) {
|
|
134
|
+
await activateWorkPlanAssistant(dataplaneUrl, authConfig, planId, {
|
|
135
|
+
planId,
|
|
136
|
+
assistantName
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return planId;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function acceptPlanForTask(dataplaneUrl, authConfig, planId) {
|
|
143
|
+
const accepted = await acceptWorkTaskPlan(dataplaneUrl, authConfig, planId, { planId });
|
|
144
|
+
const taskId = String(accepted.taskId || unwrapId(accepted.id) || '').trim();
|
|
145
|
+
if (!taskId) {
|
|
146
|
+
throw new Error('work.tasks.plan.accept did not return taskId.');
|
|
147
|
+
}
|
|
148
|
+
return taskId;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function createActivateAcceptOnce(dataplaneUrl, authConfig, planBody, assistantName) {
|
|
152
|
+
await waitDataplaneHealthy(dataplaneUrl);
|
|
153
|
+
const plan = await createWorkTaskPlan(dataplaneUrl, authConfig, planBody);
|
|
154
|
+
const planId = await activateIfNeeded(dataplaneUrl, authConfig, plan, assistantName);
|
|
155
|
+
const taskId = await acceptPlanForTask(dataplaneUrl, authConfig, planId);
|
|
156
|
+
return { planId, taskId };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function createActivateAcceptPlan(dataplaneUrl, authConfig, planBody, assistantName, options) {
|
|
160
|
+
const maxAttempts = Number(options.planRetryAttempts) || 5;
|
|
161
|
+
let lastError = null;
|
|
162
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
163
|
+
try {
|
|
164
|
+
const started = await createActivateAcceptOnce(
|
|
165
|
+
dataplaneUrl,
|
|
166
|
+
authConfig,
|
|
167
|
+
planBody,
|
|
168
|
+
assistantName
|
|
169
|
+
);
|
|
170
|
+
return { ...started, attempt };
|
|
171
|
+
} catch (error) {
|
|
172
|
+
lastError = error;
|
|
173
|
+
if (!isPlanLostError(error) || attempt >= maxAttempts) {
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
await waitDataplaneHealthy(dataplaneUrl);
|
|
177
|
+
await delayMs(400 * attempt);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
throw lastError || new Error('Work Hub plan create/activate/accept failed.');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function maybeApproveTask(dataplaneUrl, authConfig, taskId, ctx) {
|
|
184
|
+
if (ctx.approveStopped) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const status = String(ctx.status || '').trim().toLowerCase();
|
|
188
|
+
if (status !== 'pendingapproval' && status !== 'pending_approval') {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const approvalState = String(ctx.taskRow?.approvalState || '').trim().toLowerCase();
|
|
192
|
+
if (approvalState && approvalState !== 'pending') {
|
|
193
|
+
ctx.approveStopped = true;
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if ((Number(ctx.approveAttempts) || 0) >= 8) {
|
|
197
|
+
ctx.approveStopped = true;
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
await approveWorkTask(dataplaneUrl, authConfig, taskId, {});
|
|
202
|
+
ctx.interactions.push({ type: 'approve', taskId, attempt: ctx.approveAttempts });
|
|
203
|
+
ctx.approveAttempts += 1;
|
|
204
|
+
} catch (err) {
|
|
205
|
+
if (isApprovalNotPendingError(err)) {
|
|
206
|
+
ctx.approveStopped = true;
|
|
207
|
+
ctx.interactions.push({ type: 'approveStopped', reason: 'notPending', taskId });
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
ctx.approveAttempts += 1;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function maybeRespondWaiting(dataplaneUrl, authConfig, taskRow, testCase, interactions) {
|
|
215
|
+
const peId = extractPeFromTask(taskRow);
|
|
216
|
+
if (!peId) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
const statusRow = await getWorkExecutionStatus(dataplaneUrl, authConfig, peId);
|
|
221
|
+
if (String(statusRow?.status || '').toLowerCase() !== 'waiting') {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const wait = statusRow.wait && typeof statusRow.wait === 'object' ? statusRow.wait : {};
|
|
225
|
+
const fingerprint = waitRespondFingerprint(wait);
|
|
226
|
+
if (alreadyResponded(interactions, fingerprint)) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const payload = buildRespondPayload(
|
|
230
|
+
peId,
|
|
231
|
+
wait,
|
|
232
|
+
testCase,
|
|
233
|
+
statusRow,
|
|
234
|
+
interactions,
|
|
235
|
+
resolveObjective(testCase)
|
|
236
|
+
);
|
|
237
|
+
if (!payload) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
await respondWorkExecution(dataplaneUrl, authConfig, peId, payload);
|
|
241
|
+
await continueWorkExecution(dataplaneUrl, authConfig, peId, {});
|
|
242
|
+
interactions.push({ type: 'respond', processExecutionId: peId, fingerprint });
|
|
243
|
+
} catch (_err) {
|
|
244
|
+
// Soft: continue polling task.
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function assertTerminalWorkHubTask(last, taskId, timeoutMs) {
|
|
249
|
+
if (!last) {
|
|
250
|
+
throw new Error(`Work Hub task ${taskId} produced no status rows.`);
|
|
251
|
+
}
|
|
252
|
+
const finalStatusToken = String(last?.status || last?.taskStatus || '').trim().toLowerCase();
|
|
253
|
+
if (!TERMINAL_TASK.has(finalStatusToken)) {
|
|
254
|
+
throw new Error(
|
|
255
|
+
`Work Hub task ${taskId} did not reach a terminal status within ${timeoutMs}ms (last=${finalStatusToken || 'unknown'}).`
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
return last;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function pollWorkHubTask(dataplaneUrl, authConfig, taskId, testCase, options) {
|
|
262
|
+
const timeoutMs = Number(options.pollTimeoutMs) || 180000;
|
|
263
|
+
const intervalMs = Number(options.pollIntervalMs) || 3000;
|
|
264
|
+
const started = Date.now();
|
|
265
|
+
let last = null;
|
|
266
|
+
const timeline = [];
|
|
267
|
+
const ctx = {
|
|
268
|
+
status: '',
|
|
269
|
+
approveAttempts: 0,
|
|
270
|
+
approveStopped: false,
|
|
271
|
+
interactions: [],
|
|
272
|
+
taskRow: null
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
while (Date.now() - started < timeoutMs) {
|
|
276
|
+
last = await getWorkTask(dataplaneUrl, authConfig, taskId);
|
|
277
|
+
const status = String(last?.status || last?.taskStatus || '').trim().toLowerCase();
|
|
278
|
+
timeline.push({ at: new Date().toISOString(), status, taskId });
|
|
279
|
+
if (TERMINAL_TASK.has(status)) {
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
ctx.status = status;
|
|
283
|
+
ctx.taskRow = last;
|
|
284
|
+
await maybeApproveTask(dataplaneUrl, authConfig, taskId, ctx);
|
|
285
|
+
await maybeRespondWaiting(dataplaneUrl, authConfig, last, testCase, ctx.interactions);
|
|
286
|
+
await delayMs(intervalMs);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
finalTask: assertTerminalWorkHubTask(last, taskId, timeoutMs),
|
|
291
|
+
timeline,
|
|
292
|
+
interactions: ctx.interactions
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function fetchBusinessResult(dataplaneUrl, authConfig, processExecutionId) {
|
|
297
|
+
try {
|
|
298
|
+
const workResult = await getWorkExecutionResult(dataplaneUrl, authConfig, processExecutionId, {
|
|
299
|
+
requestedDepth: 'business',
|
|
300
|
+
format: 'json'
|
|
301
|
+
});
|
|
302
|
+
return { workResult, workResultError: null };
|
|
303
|
+
} catch (error) {
|
|
304
|
+
return { workResult: null, workResultError: error.message || String(error) };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function buildWorkHubLifecycleResult(roleKey, testCase, started, pollState, peBundle) {
|
|
309
|
+
const taskStatus = String(
|
|
310
|
+
pollState.finalTask?.status || pollState.finalTask?.taskStatus || ''
|
|
311
|
+
)
|
|
312
|
+
.trim()
|
|
313
|
+
.toLowerCase();
|
|
314
|
+
return {
|
|
315
|
+
roleKey,
|
|
316
|
+
caseId: testCase.caseId,
|
|
317
|
+
processExecutionId: peBundle.processExecutionId,
|
|
318
|
+
startResponse: {
|
|
319
|
+
planId: started.planId,
|
|
320
|
+
taskId: started.taskId,
|
|
321
|
+
path: 'workHub',
|
|
322
|
+
planAttempts: started.attempt
|
|
323
|
+
},
|
|
324
|
+
finalStatus: {
|
|
325
|
+
status: taskStatus === 'completed' ? 'completed' : taskStatus || 'stopped',
|
|
326
|
+
wait: null
|
|
327
|
+
},
|
|
328
|
+
timeline: pollState.timeline,
|
|
329
|
+
interactions: pollState.interactions,
|
|
330
|
+
workResult: peBundle.workResult,
|
|
331
|
+
workResultError: peBundle.workResultError
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async function runWorkHubCaseLifecycle(roleKey, testCase, runtimeContext, options = {}) {
|
|
336
|
+
const dataplaneUrl = runtimeContext?.dataplaneUrl;
|
|
337
|
+
const authConfig = runtimeContext?.authConfig;
|
|
338
|
+
const objective = resolveObjective(testCase);
|
|
339
|
+
if (!objective) {
|
|
340
|
+
throw new Error(
|
|
341
|
+
`Role assistant Work Hub case "${testCase.caseId}" is missing input.message/objective.`
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
const input = resolveInput(testCase);
|
|
345
|
+
const assistantName = String(
|
|
346
|
+
input.assistantName || input.digitalWorkerKey || roleKey
|
|
347
|
+
).trim();
|
|
348
|
+
const planBody = {
|
|
349
|
+
objective,
|
|
350
|
+
digitalWorkerKey: String(input.digitalWorkerKey || roleKey).trim()
|
|
351
|
+
};
|
|
352
|
+
const started = await createActivateAcceptPlan(
|
|
353
|
+
dataplaneUrl,
|
|
354
|
+
authConfig,
|
|
355
|
+
planBody,
|
|
356
|
+
assistantName,
|
|
357
|
+
options
|
|
358
|
+
);
|
|
359
|
+
const pollState = await pollWorkHubTask(
|
|
360
|
+
dataplaneUrl,
|
|
361
|
+
authConfig,
|
|
362
|
+
started.taskId,
|
|
363
|
+
testCase,
|
|
364
|
+
options
|
|
365
|
+
);
|
|
366
|
+
const processExecutionId = extractPeFromTask(pollState.finalTask);
|
|
367
|
+
if (!processExecutionId) {
|
|
368
|
+
throw new Error(`Work Hub task ${started.taskId} finished without processExecutionId.`);
|
|
369
|
+
}
|
|
370
|
+
const peBundle = {
|
|
371
|
+
processExecutionId,
|
|
372
|
+
...(await fetchBusinessResult(dataplaneUrl, authConfig, processExecutionId))
|
|
373
|
+
};
|
|
374
|
+
return buildWorkHubLifecycleResult(roleKey, testCase, started, pollState, peBundle);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
module.exports = {
|
|
378
|
+
wantsWorkHubPath,
|
|
379
|
+
runWorkHubCaseLifecycle,
|
|
380
|
+
planAlreadyActivated,
|
|
381
|
+
isPlanLostError,
|
|
382
|
+
extractPeFromTask,
|
|
383
|
+
synthesizeWaitAnswer,
|
|
384
|
+
isApprovalNotPendingError
|
|
385
|
+
};
|