@aifabrix/builder 2.50.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/.cursor/plans/Done/157.1-security-quality-violations-by-type.csv +374 -0
- package/.cursor/rules/assistant-driven-integration-build.mdc +40 -0
- package/integration/hubspot-test/create-hubspot.js +1 -16
- 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-kinds.api.js +128 -0
- package/lib/api/evidence.api.js +57 -1
- package/lib/api/index.js +24 -7
- package/lib/api/pipeline.api.js +47 -3
- package/lib/api/records-bulk.api.js +28 -3
- package/lib/api/resource-types.api.js +4 -4
- package/lib/api/roles.api.js +121 -0
- package/lib/api/types/evidence-kinds.types.js +42 -0
- package/lib/api/types/evidence.types.js +156 -8
- package/lib/api/types/roles.types.js +36 -0
- package/lib/api/work.api.js +283 -0
- package/lib/app/certification-verify-rows.js +1 -1
- 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/app/show.js +2 -2
- package/lib/certification/merge-certification-from-artifact.js +1 -1
- 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 +16 -3
- 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 +6 -0
- package/lib/cli/setup-app.js +26 -5
- package/lib/cli/setup-dev.js +23 -7
- package/lib/cli/setup-external-system-help.js +8 -2
- package/lib/cli/setup-external-system.js +12 -0
- package/lib/cli/setup-utility-help-after.js +12 -0
- package/lib/cli/setup-utility-validate.js +41 -2
- 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 +23 -0
- package/lib/commands/deploy-gate-orchestrator.js +64 -18
- package/lib/commands/dev-cli-handlers.js +6 -3
- package/lib/commands/dev-down.js +2 -6
- package/lib/commands/dev-init.js +7 -109
- package/lib/commands/dev-refresh.js +197 -0
- package/lib/commands/evidence-kind.js +478 -0
- package/lib/commands/governance-command-action.js +4 -2
- 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-export.js +1 -1
- package/lib/commands/repair-openapi-sync.js +43 -18
- package/lib/commands/resource-type.js +101 -18
- package/lib/commands/role-assistant.js +313 -0
- package/lib/commands/role.js +452 -0
- package/lib/commands/setup.js +1 -1
- 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/validate-json.js +230 -0
- 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/verify-operations-step-plan.js +78 -40
- package/lib/commands/wizard-core-helpers.js +7 -3
- package/lib/commands/wizard-core.js +78 -9
- package/lib/core/audit-logger.js +1 -1
- package/lib/core/secrets-load.js +1 -3
- package/lib/datasource/bulk-loader-run-batches.js +4 -2
- package/lib/datasource/bulk-loader-service.js +5 -1
- package/lib/datasource/capability/relate-operations.js +1 -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/helpers.js +13 -5
- package/lib/generator/split-variables.js +9 -3
- package/lib/generator/wizard.js +51 -4
- package/lib/infrastructure/helpers-docker-check.js +6 -0
- package/lib/infrastructure/helpers.js +1 -1
- 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/auth-config.js +3 -2
- package/lib/programmatic/bearer-auth.js +1 -1
- package/lib/programmatic/capability-registry.js +20 -1
- package/lib/programmatic/command-options.js +1 -0
- package/lib/programmatic/enterprise-mcp-client.js +18 -2
- 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/manifest-section-registry.js +18 -1
- 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 +29 -12
- package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
- package/lib/programmatic/openapi-schema-descriptions-core.js +6 -3
- 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-schema-helpers.js +16 -1
- package/lib/programmatic/openapi-spec.js +48 -29
- 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-repository-emcp.js +11 -3
- package/lib/programmatic/run-template-workspace.js +42 -2
- 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/resolvers/evidence-kind-file.js +230 -0
- package/lib/resolvers/resource-type-file.js +36 -1
- package/lib/resolvers/role-file.js +227 -0
- 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 +36 -2
- package/lib/schema/deployment-rules.yaml +1 -0
- package/lib/schema/external-datasource.schema.json +121 -4
- package/lib/schema/external-system.schema.json +20 -6
- package/lib/schema/infra.parameter.yaml +45 -1
- package/lib/schema/role-assistant-settings.schema.json +108 -0
- package/lib/schema/type/credential-field-registry.json +19 -3
- package/lib/utils/api.js +52 -21
- package/lib/utils/app-path-resolve.js +72 -24
- package/lib/utils/app-register-config.js +4 -3
- package/lib/utils/authentication-shape.js +3 -1
- package/lib/utils/cli-sync-options.js +59 -3
- package/lib/utils/cli-utils.js +11 -3
- package/lib/utils/deployment-validation.js +28 -3
- package/lib/utils/dev-ca-install.js +5 -6
- package/lib/utils/dev-config.js +2 -2
- package/lib/utils/dev-list-sort.js +145 -0
- package/lib/utils/dev-platform-tls.js +0 -63
- package/lib/utils/docker-build.js +46 -27
- package/lib/utils/docker-daemon-tls-ca.js +1 -1
- package/lib/utils/docker-exec.js +91 -6
- 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 +35 -4
- package/lib/utils/error-formatters/error-parser.js +0 -3
- package/lib/utils/external-env-template.js +1 -1
- package/lib/utils/external-system-readiness-compact-blocks.js +28 -1
- package/lib/utils/external-system-readiness-compact-display.js +3 -0
- package/lib/utils/health-check-request.js +2 -3
- package/lib/utils/help-builder.js +4 -0
- package/lib/utils/image-name.js +22 -4
- package/lib/utils/logger.js +37 -4
- package/lib/utils/manifest-source-emit.js +1 -1
- package/lib/utils/paths.js +98 -26
- package/lib/utils/pipeline-upload-auth-retry.js +19 -3
- package/lib/utils/postgres-platform-bootstrap.js +24 -17
- package/lib/utils/postgres-wipe.js +5 -2
- package/lib/utils/remote-dev-auth.js +39 -4
- package/lib/utils/resolve-docker-image-ref.js +36 -6
- 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 +47 -22
- package/lib/utils/upload-sync-options.js +17 -3
- package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
- package/lib/validation/external-manifest-validator.js +31 -8
- package/lib/validation/openapi-contract-surface-validator.js +4 -0
- package/lib/validation/validate-external-file.js +8 -1
- package/lib/validation/validator.js +6 -5
- package/package.json +35 -12
- package/scripts/export-builder-api-evidence.js +3 -3
- package/scripts/lib/plan-actor.js +2 -2
- package/scripts/lib/plan-meta.js +1 -1
- package/scripts/lib/plan-parse.js +1 -1
- package/scripts/lib/plan-prevalidate-gates.js +12 -1
- package/scripts/lib/plan-run-report.js +13 -2
- package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +10 -10
- package/templates/applications/dataplane/application.yaml +47 -17
- package/templates/applications/dataplane/env.template +28 -9
- package/templates/applications/dataplane/rbac.yaml +78 -2
- package/templates/applications/miso-controller/application.yaml +10 -1
- package/templates/applications/miso-controller/env.template +13 -8
- package/templates/applications/miso-controller/rbac.yaml +72 -0
- 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/templates/channels/slack/integration/slack-system.json +60 -2
- package/templates/python/requirements.txt +1 -1
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role assistant case runtime runner using dataplane work APIs.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Execute role-assistant test case input/answers/approvals over work.start/status/respond/continue
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const crypto = require('crypto');
|
|
12
|
+
const { VERDICT } = require('../lifecycle/product-model');
|
|
13
|
+
const {
|
|
14
|
+
startWorkIntent,
|
|
15
|
+
getWorkExecutionStatus,
|
|
16
|
+
respondWorkExecution,
|
|
17
|
+
continueWorkExecution,
|
|
18
|
+
getWorkExecutionResult
|
|
19
|
+
} = require('../api/work.api');
|
|
20
|
+
const { wantsWorkHubPath, runWorkHubCaseLifecycle } = require('./test-runner-workhub');
|
|
21
|
+
const TERMINAL_STATUSES = new Set(['completed', 'stopped']);
|
|
22
|
+
const WAITING_STATUS = 'waiting';
|
|
23
|
+
|
|
24
|
+
function delayMs(ms) {
|
|
25
|
+
const wait = Number(ms);
|
|
26
|
+
if (!Number.isFinite(wait) || wait <= 0) {
|
|
27
|
+
return Promise.resolve();
|
|
28
|
+
}
|
|
29
|
+
return new Promise((resolve) => setTimeout(resolve, wait));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function normalizeToken(value) {
|
|
33
|
+
if (typeof value !== 'string') {
|
|
34
|
+
return '';
|
|
35
|
+
}
|
|
36
|
+
return value
|
|
37
|
+
.trim()
|
|
38
|
+
.toLowerCase()
|
|
39
|
+
.replace(/[^a-z0-9]+/g, '');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function buildRuntimeId(prefix) {
|
|
43
|
+
if (typeof crypto.randomUUID === 'function') {
|
|
44
|
+
return `${prefix}-${crypto.randomUUID()}`;
|
|
45
|
+
}
|
|
46
|
+
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function applyOptionalStringField(payload, value, key) {
|
|
50
|
+
if (typeof value === 'string' && value.trim()) {
|
|
51
|
+
payload[key] = value.trim();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
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
|
+
function isExecutableRoleAssistantCase(testCase) {
|
|
72
|
+
const input = resolveInputObject(testCase);
|
|
73
|
+
if (typeof input.content !== 'undefined') {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
if (resolveInputMessage(testCase)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
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
|
+
function extractProcessExecutionId(startResponse) {
|
|
130
|
+
const candidates = [
|
|
131
|
+
startResponse?.intent?.processExecutionId,
|
|
132
|
+
startResponse?.processExecutionId,
|
|
133
|
+
startResponse?.status?.processExecutionId
|
|
134
|
+
];
|
|
135
|
+
for (const row of candidates) {
|
|
136
|
+
if (typeof row === 'string' && row.trim()) {
|
|
137
|
+
return row.trim();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return '';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function isTerminalStatus(statusValue) {
|
|
144
|
+
return TERMINAL_STATUSES.has(normalizeToken(statusValue));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function getWaitDescriptor(statusRow) {
|
|
148
|
+
return statusRow && typeof statusRow.wait === 'object' && statusRow.wait ? statusRow.wait : {};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function collectWaitMatchTokens(wait) {
|
|
152
|
+
const out = [];
|
|
153
|
+
const add = (value) => {
|
|
154
|
+
if (typeof value === 'string' && value.trim()) {
|
|
155
|
+
out.push(value.trim());
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
add(wait.answerType);
|
|
159
|
+
add(wait.waitReason);
|
|
160
|
+
add(wait.interactionKind);
|
|
161
|
+
add(wait.questionKey);
|
|
162
|
+
add(wait.requestKey);
|
|
163
|
+
if (wait.objectRef && typeof wait.objectRef === 'object') {
|
|
164
|
+
add(wait.objectRef.key);
|
|
165
|
+
add(wait.objectRef.name);
|
|
166
|
+
add(wait.objectRef.type);
|
|
167
|
+
add(wait.objectRef.id);
|
|
168
|
+
}
|
|
169
|
+
if (wait.safeProjection && typeof wait.safeProjection === 'object') {
|
|
170
|
+
add(wait.safeProjection.capabilityKey);
|
|
171
|
+
add(wait.safeProjection.capability);
|
|
172
|
+
add(wait.safeProjection.key);
|
|
173
|
+
}
|
|
174
|
+
return out.map((row) => normalizeToken(row)).filter(Boolean);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function matchesWhenSpec(wait, when = {}) {
|
|
178
|
+
if (!when || typeof when !== 'object' || Array.isArray(when)) {
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
const tokens = collectWaitMatchTokens(wait);
|
|
182
|
+
const includesToken = (value) => {
|
|
183
|
+
const normalized = normalizeToken(value);
|
|
184
|
+
if (!normalized) {
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
return tokens.includes(normalized);
|
|
188
|
+
};
|
|
189
|
+
if (!includesToken(when.type)) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
if (!includesToken(when.key)) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
if (!includesToken(when.questionKey)) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
if (!includesToken(when.requestKey)) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
if (!includesToken(when.capability)) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
if (!includesToken(when.interactionKind)) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
return includesToken(when.waitReason);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function findMatchingEntry(entries, usedIndexes, wait) {
|
|
211
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
212
|
+
if (usedIndexes.has(index)) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const candidate = entries[index];
|
|
216
|
+
const when = candidate && typeof candidate === 'object' ? candidate.when : null;
|
|
217
|
+
if (matchesWhenSpec(wait, when)) {
|
|
218
|
+
return { index, entry: candidate || {} };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function normalizeApprovalDecision(value) {
|
|
225
|
+
const token = normalizeToken(value);
|
|
226
|
+
if (token === 'approve' || token === 'approved' || token === 'accept') {
|
|
227
|
+
return 'approved';
|
|
228
|
+
}
|
|
229
|
+
if (token === 'reject' || token === 'rejected' || token === 'decline' || token === 'declined') {
|
|
230
|
+
return 'rejected';
|
|
231
|
+
}
|
|
232
|
+
return '';
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function resolveAskRespondValue(respond) {
|
|
236
|
+
if (typeof respond.value !== 'undefined') {
|
|
237
|
+
return respond.value;
|
|
238
|
+
}
|
|
239
|
+
if (typeof respond.message !== 'undefined') {
|
|
240
|
+
return respond.message;
|
|
241
|
+
}
|
|
242
|
+
if (typeof respond.text !== 'undefined') {
|
|
243
|
+
return respond.text;
|
|
244
|
+
}
|
|
245
|
+
return respond;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function applyWaitVersioningFields(payload, wait) {
|
|
249
|
+
applyOptionalStringField(payload, wait.requestVersion, 'requestVersion');
|
|
250
|
+
applyOptionalStringField(payload, wait.stateVersionRef, 'stateVersionRef');
|
|
251
|
+
applyOptionalStringField(payload, wait.requestFingerprint, 'requestFingerprint');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function buildAskRespondPayload(answerEntry, wait) {
|
|
255
|
+
const respond = answerEntry && typeof answerEntry.respond === 'object' && answerEntry.respond
|
|
256
|
+
? answerEntry.respond
|
|
257
|
+
: {};
|
|
258
|
+
const payload = {
|
|
259
|
+
respondIdempotencyKey: buildRuntimeId('ra-answer'),
|
|
260
|
+
answerType: 'ask',
|
|
261
|
+
value: resolveAskRespondValue(respond)
|
|
262
|
+
};
|
|
263
|
+
applyOptionalStringField(payload, respond.selectedOptionId, 'selectedOptionId');
|
|
264
|
+
applyOptionalStringField(payload, respond.evidenceRef, 'evidenceRef');
|
|
265
|
+
if (respond.declined === true) {
|
|
266
|
+
payload.declined = true;
|
|
267
|
+
}
|
|
268
|
+
applyWaitVersioningFields(payload, wait);
|
|
269
|
+
return payload;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function buildApprovalRespondPayload(approvalEntry, wait) {
|
|
273
|
+
const decision = normalizeApprovalDecision(
|
|
274
|
+
approvalEntry?.decision || approvalEntry?.respond?.decision || approvalEntry?.respond?.approvalDecision
|
|
275
|
+
);
|
|
276
|
+
if (!decision) {
|
|
277
|
+
throw new Error('Approval entry in case requires decision "approve" or "reject".');
|
|
278
|
+
}
|
|
279
|
+
const payload = {
|
|
280
|
+
respondIdempotencyKey: buildRuntimeId('ra-approval'),
|
|
281
|
+
answerType: 'approval',
|
|
282
|
+
approvalDecision: decision
|
|
283
|
+
};
|
|
284
|
+
applyWaitVersioningFields(payload, wait);
|
|
285
|
+
return payload;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function buildStatusSnapshot(statusRow) {
|
|
289
|
+
return {
|
|
290
|
+
processExecutionId: statusRow?.processExecutionId || null,
|
|
291
|
+
status: statusRow?.status || null,
|
|
292
|
+
message: statusRow?.message || null,
|
|
293
|
+
wait: statusRow?.wait || null,
|
|
294
|
+
correlationId: statusRow?.correlationId || null
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function buildRunnerOptions(options = {}) {
|
|
299
|
+
const maxStatusPolls = Number(options.maxStatusPolls);
|
|
300
|
+
const statusPollIntervalMs = Number(options.statusPollIntervalMs);
|
|
301
|
+
return {
|
|
302
|
+
maxStatusPolls: Number.isFinite(maxStatusPolls) && maxStatusPolls > 0 ? maxStatusPolls : 20,
|
|
303
|
+
statusPollIntervalMs:
|
|
304
|
+
Number.isFinite(statusPollIntervalMs) && statusPollIntervalMs >= 0 ? statusPollIntervalMs : 750
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async function fetchWorkResultSafe(dataplaneUrl, authConfig, processExecutionId, testCase) {
|
|
309
|
+
const input = resolveInputObject(testCase);
|
|
310
|
+
const requestedDepth = input.requestedDepth || 'details';
|
|
311
|
+
const format = input.format || 'json';
|
|
312
|
+
return getWorkExecutionResult(dataplaneUrl, authConfig, processExecutionId, {
|
|
313
|
+
requestedDepth,
|
|
314
|
+
format
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function finalizeWaitingAdvanceStatus(respondResult, continueResult) {
|
|
319
|
+
const afterRespondStatus = respondResult && typeof respondResult.status === 'object'
|
|
320
|
+
? respondResult.status
|
|
321
|
+
: null;
|
|
322
|
+
return continueResult && typeof continueResult === 'object'
|
|
323
|
+
? continueResult
|
|
324
|
+
: afterRespondStatus;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async function advanceApprovalWaitingExecution(context, approvals, wait) {
|
|
328
|
+
const { dataplaneUrl, authConfig, processExecutionId, usedApprovalIndexes } = context;
|
|
329
|
+
const match = findMatchingEntry(approvals, usedApprovalIndexes, wait);
|
|
330
|
+
if (!match) {
|
|
331
|
+
return { status: null, matched: false, interactionType: 'approval' };
|
|
332
|
+
}
|
|
333
|
+
usedApprovalIndexes.add(match.index);
|
|
334
|
+
const payload = buildApprovalRespondPayload(match.entry, wait);
|
|
335
|
+
const respondResult = await respondWorkExecution(dataplaneUrl, authConfig, processExecutionId, payload);
|
|
336
|
+
const continueResult = await continueWorkExecution(dataplaneUrl, authConfig, processExecutionId, {});
|
|
337
|
+
return {
|
|
338
|
+
status: finalizeWaitingAdvanceStatus(respondResult, continueResult),
|
|
339
|
+
matched: true,
|
|
340
|
+
interactionType: 'approval',
|
|
341
|
+
respondPayload: payload
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async function advanceAskWaitingExecution(context, answers, wait) {
|
|
346
|
+
const { dataplaneUrl, authConfig, processExecutionId, usedAnswerIndexes } = context;
|
|
347
|
+
const match = findMatchingEntry(answers, usedAnswerIndexes, wait);
|
|
348
|
+
if (!match) {
|
|
349
|
+
return { status: null, matched: false, interactionType: 'ask' };
|
|
350
|
+
}
|
|
351
|
+
usedAnswerIndexes.add(match.index);
|
|
352
|
+
const payload = buildAskRespondPayload(match.entry, wait);
|
|
353
|
+
const respondResult = await respondWorkExecution(dataplaneUrl, authConfig, processExecutionId, payload);
|
|
354
|
+
const continueResult = await continueWorkExecution(dataplaneUrl, authConfig, processExecutionId, {});
|
|
355
|
+
return {
|
|
356
|
+
status: finalizeWaitingAdvanceStatus(respondResult, continueResult),
|
|
357
|
+
matched: true,
|
|
358
|
+
interactionType: 'ask',
|
|
359
|
+
respondPayload: payload
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function advanceWaitingExecution(context) {
|
|
364
|
+
const { wait, testCase } = context;
|
|
365
|
+
const waitReason = normalizeToken(wait.waitReason || wait.answerType || wait.interactionKind);
|
|
366
|
+
const approvals = Array.isArray(testCase.approvals) ? testCase.approvals : [];
|
|
367
|
+
const answers = Array.isArray(testCase.answers) ? testCase.answers : [];
|
|
368
|
+
if (waitReason === 'approval') {
|
|
369
|
+
return advanceApprovalWaitingExecution(context, approvals, wait);
|
|
370
|
+
}
|
|
371
|
+
return advanceAskWaitingExecution(context, answers, wait);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async function pollCaseExecutionStatus(dataplaneUrl, authConfig, processExecutionId, testCase, options) {
|
|
375
|
+
const usedAnswerIndexes = new Set();
|
|
376
|
+
const usedApprovalIndexes = new Set();
|
|
377
|
+
const timeline = [];
|
|
378
|
+
const interactions = [];
|
|
379
|
+
let statusRow = await getWorkExecutionStatus(dataplaneUrl, authConfig, processExecutionId);
|
|
380
|
+
for (let pollIndex = 0; pollIndex < options.maxStatusPolls; pollIndex += 1) {
|
|
381
|
+
timeline.push(buildStatusSnapshot(statusRow));
|
|
382
|
+
if (isTerminalStatus(statusRow?.status)) {
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
if (normalizeToken(statusRow?.status) === WAITING_STATUS) {
|
|
386
|
+
const wait = getWaitDescriptor(statusRow);
|
|
387
|
+
const advanced = await advanceWaitingExecution({
|
|
388
|
+
dataplaneUrl,
|
|
389
|
+
authConfig,
|
|
390
|
+
processExecutionId,
|
|
391
|
+
wait,
|
|
392
|
+
testCase,
|
|
393
|
+
usedAnswerIndexes,
|
|
394
|
+
usedApprovalIndexes
|
|
395
|
+
});
|
|
396
|
+
interactions.push({
|
|
397
|
+
pollIndex,
|
|
398
|
+
type: advanced.interactionType,
|
|
399
|
+
waitReason: wait.waitReason || wait.answerType || wait.interactionKind || null,
|
|
400
|
+
matched: advanced.matched === true,
|
|
401
|
+
questionKey: wait.questionKey || wait.requestKey || null
|
|
402
|
+
});
|
|
403
|
+
if (!advanced.matched || !advanced.status) {
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
statusRow = advanced.status;
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
await delayMs(options.statusPollIntervalMs);
|
|
410
|
+
statusRow = await getWorkExecutionStatus(dataplaneUrl, authConfig, processExecutionId);
|
|
411
|
+
}
|
|
412
|
+
return { finalStatus: statusRow, timeline, interactions };
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
async function safeLoadWorkResult(dataplaneUrl, authConfig, processExecutionId, testCase) {
|
|
416
|
+
try {
|
|
417
|
+
const workResult = await fetchWorkResultSafe(dataplaneUrl, authConfig, processExecutionId, testCase);
|
|
418
|
+
return { workResult, workResultError: null };
|
|
419
|
+
} catch (error) {
|
|
420
|
+
return { workResult: null, workResultError: error.message || String(error) };
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async function runRoleAssistantCaseLifecycle(roleKey, testCase, runtimeContext, options = {}) {
|
|
425
|
+
const dataplaneUrl = runtimeContext?.dataplaneUrl;
|
|
426
|
+
const authConfig = runtimeContext?.authConfig;
|
|
427
|
+
if (!dataplaneUrl || !authConfig) {
|
|
428
|
+
throw new Error('Role assistant case runtime requires dataplane auth context.');
|
|
429
|
+
}
|
|
430
|
+
const runnerOptions = buildRunnerOptions(options);
|
|
431
|
+
if (wantsWorkHubPath(testCase)) {
|
|
432
|
+
return runWorkHubCaseLifecycle(roleKey, testCase, runtimeContext, runnerOptions);
|
|
433
|
+
}
|
|
434
|
+
const startPayload = buildStartIntentPayload(roleKey, testCase);
|
|
435
|
+
const startResponse = await startWorkIntent(dataplaneUrl, authConfig, startPayload);
|
|
436
|
+
const processExecutionId = extractProcessExecutionId(startResponse);
|
|
437
|
+
if (!processExecutionId) {
|
|
438
|
+
throw new Error(
|
|
439
|
+
'work.start missing processExecutionId; use input.executionPath: workHub for free-text RA cases.'
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
const pollState = await pollCaseExecutionStatus(
|
|
443
|
+
dataplaneUrl,
|
|
444
|
+
authConfig,
|
|
445
|
+
processExecutionId,
|
|
446
|
+
testCase,
|
|
447
|
+
runnerOptions
|
|
448
|
+
);
|
|
449
|
+
const { workResult, workResultError } = await safeLoadWorkResult(
|
|
450
|
+
dataplaneUrl,
|
|
451
|
+
authConfig,
|
|
452
|
+
processExecutionId,
|
|
453
|
+
testCase
|
|
454
|
+
);
|
|
455
|
+
return {
|
|
456
|
+
roleKey,
|
|
457
|
+
caseId: testCase.caseId,
|
|
458
|
+
processExecutionId,
|
|
459
|
+
startResponse,
|
|
460
|
+
finalStatus: pollState.finalStatus,
|
|
461
|
+
timeline: pollState.timeline,
|
|
462
|
+
interactions: pollState.interactions,
|
|
463
|
+
workResult,
|
|
464
|
+
workResultError
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function mergeCaseExecutionIntoLifecycleResult(lifecycleResult, executionResult) {
|
|
469
|
+
const base = lifecycleResult && typeof lifecycleResult === 'object' ? lifecycleResult : {};
|
|
470
|
+
return {
|
|
471
|
+
...base,
|
|
472
|
+
status: executionResult?.finalStatus?.status || base.status,
|
|
473
|
+
wait: executionResult?.finalStatus?.wait || base.wait,
|
|
474
|
+
processExecutionId: executionResult?.processExecutionId || base.processExecutionId,
|
|
475
|
+
workResult: executionResult?.workResult || null,
|
|
476
|
+
task: executionResult?.workResult?.task || null,
|
|
477
|
+
steps: Array.isArray(executionResult?.workResult?.steps) ? executionResult.workResult.steps : [],
|
|
478
|
+
workTimeline: executionResult?.timeline || [],
|
|
479
|
+
workInteractions: executionResult?.interactions || []
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function lifecycleVerdictFromCaseExecution(executionResult, fallbackVerdict) {
|
|
484
|
+
const statusToken = normalizeToken(executionResult?.finalStatus?.status);
|
|
485
|
+
if (statusToken === 'completed') {
|
|
486
|
+
return VERDICT.VERIFIED;
|
|
487
|
+
}
|
|
488
|
+
if (statusToken === 'stopped' || statusToken === 'waiting' || statusToken === 'running') {
|
|
489
|
+
return VERDICT.NOT_VERIFIED;
|
|
490
|
+
}
|
|
491
|
+
return fallbackVerdict || VERDICT.FAILED;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
module.exports = {
|
|
495
|
+
isExecutableRoleAssistantCase,
|
|
496
|
+
buildStartIntentPayload,
|
|
497
|
+
runRoleAssistantCaseLifecycle,
|
|
498
|
+
mergeCaseExecutionIntoLifecycleResult,
|
|
499
|
+
lifecycleVerdictFromCaseExecution
|
|
500
|
+
};
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"key": "application-schema",
|
|
8
8
|
"name": "Application Configuration Schema",
|
|
9
9
|
"description": "JSON schema for validating AI Fabrix application configuration files",
|
|
10
|
-
"version": "1.
|
|
10
|
+
"version": "1.6.0",
|
|
11
11
|
"type": "schema",
|
|
12
12
|
"category": "infrastructure",
|
|
13
13
|
"author": "AI Fabrix Team",
|
|
14
14
|
"createdAt": "2024-01-01T00:00:00Z",
|
|
15
|
-
"updatedAt": "2026-
|
|
15
|
+
"updatedAt": "2026-07-28T00:00:00Z",
|
|
16
16
|
"compatibility": {
|
|
17
17
|
"minVersion": "1.0.0",
|
|
18
18
|
"maxVersion": "2.0.0",
|
|
@@ -30,6 +30,22 @@
|
|
|
30
30
|
"external-datasource.schema.json"
|
|
31
31
|
],
|
|
32
32
|
"changelog": [
|
|
33
|
+
{
|
|
34
|
+
"version": "1.6.0",
|
|
35
|
+
"date": "2026-07-28T00:00:00Z",
|
|
36
|
+
"changes": [
|
|
37
|
+
"Added optional build.deployJob (string): one-shot command run once before any long-running role start (api/worker/beat). Language-agnostic (python, node, pnpm, etc.). Not an array; no phase/once/id fields."
|
|
38
|
+
],
|
|
39
|
+
"breaking": false
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"version": "1.5.0",
|
|
43
|
+
"date": "2026-07-21T00:00:00Z",
|
|
44
|
+
"changes": [
|
|
45
|
+
"Added optional configuration[].category (and conditionalConfiguration item category) for portal UI grouping labels in Miso Configuration tab."
|
|
46
|
+
],
|
|
47
|
+
"breaking": false
|
|
48
|
+
},
|
|
33
49
|
{
|
|
34
50
|
"version": "1.4.0",
|
|
35
51
|
"date": "2026-04-06T00:00:00Z",
|
|
@@ -203,6 +219,12 @@
|
|
|
203
219
|
"type": "boolean",
|
|
204
220
|
"description": "Whether this configuration is required"
|
|
205
221
|
},
|
|
222
|
+
"category": {
|
|
223
|
+
"type": "string",
|
|
224
|
+
"description": "Optional UI grouping label for portal/deployment wizard Configuration tab",
|
|
225
|
+
"minLength": 1,
|
|
226
|
+
"maxLength": 100
|
|
227
|
+
},
|
|
206
228
|
"portalInput": {
|
|
207
229
|
"type": "object",
|
|
208
230
|
"description": "Portal input configuration for user-provided values",
|
|
@@ -315,6 +337,12 @@
|
|
|
315
337
|
"type": "boolean",
|
|
316
338
|
"description": "Whether this configuration is required"
|
|
317
339
|
},
|
|
340
|
+
"category": {
|
|
341
|
+
"type": "string",
|
|
342
|
+
"description": "Optional UI grouping label for portal/deployment wizard Configuration tab",
|
|
343
|
+
"minLength": 1,
|
|
344
|
+
"maxLength": 100
|
|
345
|
+
},
|
|
318
346
|
"portalInput": {
|
|
319
347
|
"type": "object",
|
|
320
348
|
"description": "Portal input configuration for user-provided values",
|
|
@@ -817,6 +845,12 @@
|
|
|
817
845
|
"type": "string",
|
|
818
846
|
"description": "Compose/Azure startup command for the beat sidecar (<appKey>-beat) when build.worker is true. Omit or set to empty string to skip the beat role."
|
|
819
847
|
},
|
|
848
|
+
"deployJob": {
|
|
849
|
+
"type": "string",
|
|
850
|
+
"description": "Optional one-shot command run once before any long-running role (imageRun / imageRunWorker / imageRunBeat). Local aifabrix run and remote Deployment execute it once per start/deploy (app-scoped, never once-per-sidecar). Language-agnostic command string (e.g. python -m app.db.migrate upgrade, node dist/migrate.js).",
|
|
851
|
+
"minLength": 1,
|
|
852
|
+
"maxLength": 500
|
|
853
|
+
},
|
|
820
854
|
"scripts": {
|
|
821
855
|
"type": "object",
|
|
822
856
|
"description": "Override CLI script commands (aifabrix test, install, lint, test-e2e, test-integration). When a key is missing, language default is used (TypeScript: pnpm test/install/lint/test:e2e/test:integration, Python: make test/install/lint/test:e2e/test-integration).",
|