scene-capability-engine 3.6.45 → 3.6.46
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/CHANGELOG.md +11 -0
- package/docs/releases/README.md +1 -0
- package/docs/releases/v3.6.46.md +23 -0
- package/docs/zh/releases/README.md +1 -0
- package/docs/zh/releases/v3.6.46.md +23 -0
- package/package.json +4 -2
- package/scripts/auto-strategy-router.js +231 -0
- package/scripts/capability-mapping-report.js +339 -0
- package/scripts/check-branding-consistency.js +140 -0
- package/scripts/check-sce-tracking.js +54 -0
- package/scripts/check-skip-allowlist.js +94 -0
- package/scripts/errorbook-registry-health-gate.js +172 -0
- package/scripts/errorbook-release-gate.js +132 -0
- package/scripts/failure-attribution-repair.js +317 -0
- package/scripts/git-managed-gate.js +464 -0
- package/scripts/interactive-approval-event-projection.js +400 -0
- package/scripts/interactive-approval-workflow.js +829 -0
- package/scripts/interactive-authorization-tier-evaluate.js +413 -0
- package/scripts/interactive-change-plan-gate.js +225 -0
- package/scripts/interactive-context-bridge.js +617 -0
- package/scripts/interactive-customization-loop.js +1690 -0
- package/scripts/interactive-dialogue-governance.js +842 -0
- package/scripts/interactive-feedback-log.js +253 -0
- package/scripts/interactive-flow-smoke.js +238 -0
- package/scripts/interactive-flow.js +1059 -0
- package/scripts/interactive-governance-report.js +1112 -0
- package/scripts/interactive-intent-build.js +707 -0
- package/scripts/interactive-loop-smoke.js +215 -0
- package/scripts/interactive-moqui-adapter.js +304 -0
- package/scripts/interactive-plan-build.js +426 -0
- package/scripts/interactive-runtime-policy-evaluate.js +495 -0
- package/scripts/interactive-work-order-build.js +552 -0
- package/scripts/matrix-regression-gate.js +167 -0
- package/scripts/moqui-core-regression-suite.js +397 -0
- package/scripts/moqui-lexicon-audit.js +651 -0
- package/scripts/moqui-matrix-remediation-phased-runner.js +865 -0
- package/scripts/moqui-matrix-remediation-queue.js +852 -0
- package/scripts/moqui-metadata-extract.js +1340 -0
- package/scripts/moqui-rebuild-gate.js +167 -0
- package/scripts/moqui-release-summary.js +729 -0
- package/scripts/moqui-standard-rebuild.js +1370 -0
- package/scripts/moqui-template-baseline-report.js +682 -0
- package/scripts/npm-package-runtime-asset-check.js +221 -0
- package/scripts/problem-closure-gate.js +441 -0
- package/scripts/release-asset-integrity-check.js +216 -0
- package/scripts/release-asset-nonempty-normalize.js +166 -0
- package/scripts/release-drift-evaluate.js +223 -0
- package/scripts/release-drift-signals.js +255 -0
- package/scripts/release-governance-snapshot-export.js +132 -0
- package/scripts/release-ops-weekly-summary.js +934 -0
- package/scripts/release-risk-remediation-bundle.js +315 -0
- package/scripts/release-weekly-ops-gate.js +423 -0
- package/scripts/state-migration-reconciliation-gate.js +110 -0
- package/scripts/state-storage-tiering-audit.js +337 -0
- package/scripts/steering-content-audit.js +393 -0
- package/scripts/symbol-evidence-locate.js +366 -0
|
@@ -0,0 +1,1690 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs-extra');
|
|
6
|
+
const crypto = require('crypto');
|
|
7
|
+
const { spawnSync } = require('child_process');
|
|
8
|
+
const {
|
|
9
|
+
DEFAULT_BUSINESS_MODE_POLICY,
|
|
10
|
+
BUSINESS_MODES,
|
|
11
|
+
normalizeBusinessMode,
|
|
12
|
+
applyBusinessModePolicy
|
|
13
|
+
} = require('../lib/runtime/business-mode-resolver');
|
|
14
|
+
|
|
15
|
+
const DEFAULT_OUT_DIR = '.sce/reports/interactive-loop';
|
|
16
|
+
const DEFAULT_USER_ID = 'anonymous-user';
|
|
17
|
+
const DEFAULT_APPROVAL_ACTOR = 'workflow-operator';
|
|
18
|
+
const DEFAULT_FEEDBACK_CHANNEL = 'ui';
|
|
19
|
+
const DEFAULT_AUTH_PASSWORD_HASH_ENV = 'SCE_INTERACTIVE_AUTH_PASSWORD_SHA256';
|
|
20
|
+
const DEFAULT_RUNTIME_MODE = 'ops-fix';
|
|
21
|
+
const DEFAULT_RUNTIME_ENVIRONMENT = 'staging';
|
|
22
|
+
const DEFAULT_DIALOGUE_PROFILE = 'business-user';
|
|
23
|
+
const FEEDBACK_CHANNELS = new Set(['ui', 'cli', 'api', 'other']);
|
|
24
|
+
const RUNTIME_MODES = new Set(['user-assist', 'ops-fix', 'feature-dev']);
|
|
25
|
+
const RUNTIME_ENVIRONMENTS = new Set(['dev', 'staging', 'prod']);
|
|
26
|
+
const DIALOGUE_PROFILES = new Set(['business-user', 'system-maintainer']);
|
|
27
|
+
const UI_MODES = new Set(['user-app', 'ops-console']);
|
|
28
|
+
|
|
29
|
+
const SCRIPT_DIALOGUE = path.resolve(__dirname, 'interactive-dialogue-governance.js');
|
|
30
|
+
const SCRIPT_INTENT = path.resolve(__dirname, 'interactive-intent-build.js');
|
|
31
|
+
const SCRIPT_PLAN = path.resolve(__dirname, 'interactive-plan-build.js');
|
|
32
|
+
const SCRIPT_GATE = path.resolve(__dirname, 'interactive-change-plan-gate.js');
|
|
33
|
+
const SCRIPT_RUNTIME = path.resolve(__dirname, 'interactive-runtime-policy-evaluate.js');
|
|
34
|
+
const SCRIPT_AUTHORIZATION_TIER = path.resolve(__dirname, 'interactive-authorization-tier-evaluate.js');
|
|
35
|
+
const SCRIPT_APPROVAL = path.resolve(__dirname, 'interactive-approval-workflow.js');
|
|
36
|
+
const SCRIPT_ADAPTER = path.resolve(__dirname, 'interactive-moqui-adapter.js');
|
|
37
|
+
const SCRIPT_FEEDBACK = path.resolve(__dirname, 'interactive-feedback-log.js');
|
|
38
|
+
const SCRIPT_WORK_ORDER = path.resolve(__dirname, 'interactive-work-order-build.js');
|
|
39
|
+
|
|
40
|
+
function parseArgs(argv) {
|
|
41
|
+
const explicitKeys = new Set();
|
|
42
|
+
const options = {
|
|
43
|
+
context: null,
|
|
44
|
+
goal: null,
|
|
45
|
+
goalFile: null,
|
|
46
|
+
userId: DEFAULT_USER_ID,
|
|
47
|
+
sessionId: null,
|
|
48
|
+
executionMode: 'suggestion',
|
|
49
|
+
businessMode: null,
|
|
50
|
+
businessModePolicy: DEFAULT_BUSINESS_MODE_POLICY,
|
|
51
|
+
allowModeOverride: false,
|
|
52
|
+
policy: null,
|
|
53
|
+
catalog: null,
|
|
54
|
+
dialoguePolicy: null,
|
|
55
|
+
dialogueProfile: DEFAULT_DIALOGUE_PROFILE,
|
|
56
|
+
uiMode: null,
|
|
57
|
+
dialogueOut: null,
|
|
58
|
+
runtimeMode: DEFAULT_RUNTIME_MODE,
|
|
59
|
+
runtimeEnvironment: DEFAULT_RUNTIME_ENVIRONMENT,
|
|
60
|
+
runtimePolicy: null,
|
|
61
|
+
runtimeOut: null,
|
|
62
|
+
authorizationTierPolicy: null,
|
|
63
|
+
authorizationTierOut: null,
|
|
64
|
+
contextContract: null,
|
|
65
|
+
strictContract: true,
|
|
66
|
+
moquiConfig: null,
|
|
67
|
+
outDir: DEFAULT_OUT_DIR,
|
|
68
|
+
out: null,
|
|
69
|
+
workOrderOut: null,
|
|
70
|
+
workOrderMarkdownOut: null,
|
|
71
|
+
approvalActor: DEFAULT_APPROVAL_ACTOR,
|
|
72
|
+
approvalActorRole: null,
|
|
73
|
+
approverActor: null,
|
|
74
|
+
approverActorRole: null,
|
|
75
|
+
approvalRolePolicy: null,
|
|
76
|
+
skipSubmit: false,
|
|
77
|
+
autoApproveLowRisk: false,
|
|
78
|
+
autoExecuteLowRisk: false,
|
|
79
|
+
allowSuggestionApply: false,
|
|
80
|
+
liveApply: false,
|
|
81
|
+
dryRun: true,
|
|
82
|
+
feedbackScore: null,
|
|
83
|
+
feedbackComment: null,
|
|
84
|
+
feedbackTags: [],
|
|
85
|
+
feedbackChannel: DEFAULT_FEEDBACK_CHANNEL,
|
|
86
|
+
authPassword: null,
|
|
87
|
+
authPasswordHash: null,
|
|
88
|
+
authPasswordEnv: null,
|
|
89
|
+
failOnDialogueDeny: false,
|
|
90
|
+
failOnGateDeny: false,
|
|
91
|
+
failOnGateNonAllow: false,
|
|
92
|
+
failOnRuntimeNonAllow: false,
|
|
93
|
+
failOnExecuteBlocked: false,
|
|
94
|
+
json: false
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
98
|
+
const token = argv[index];
|
|
99
|
+
const next = argv[index + 1];
|
|
100
|
+
|
|
101
|
+
if (token === '--context' && next) {
|
|
102
|
+
options.context = next;
|
|
103
|
+
index += 1;
|
|
104
|
+
} else if (token === '--goal' && next) {
|
|
105
|
+
options.goal = next;
|
|
106
|
+
index += 1;
|
|
107
|
+
} else if (token === '--goal-file' && next) {
|
|
108
|
+
options.goalFile = next;
|
|
109
|
+
index += 1;
|
|
110
|
+
} else if (token === '--user-id' && next) {
|
|
111
|
+
options.userId = next;
|
|
112
|
+
index += 1;
|
|
113
|
+
} else if (token === '--session-id' && next) {
|
|
114
|
+
options.sessionId = next;
|
|
115
|
+
index += 1;
|
|
116
|
+
} else if (token === '--execution-mode' && next) {
|
|
117
|
+
options.executionMode = next;
|
|
118
|
+
explicitKeys.add('executionMode');
|
|
119
|
+
index += 1;
|
|
120
|
+
} else if (token === '--business-mode' && next) {
|
|
121
|
+
options.businessMode = next;
|
|
122
|
+
index += 1;
|
|
123
|
+
} else if (token === '--business-mode-policy' && next) {
|
|
124
|
+
options.businessModePolicy = next;
|
|
125
|
+
index += 1;
|
|
126
|
+
} else if (token === '--allow-mode-override') {
|
|
127
|
+
options.allowModeOverride = true;
|
|
128
|
+
} else if (token === '--policy' && next) {
|
|
129
|
+
options.policy = next;
|
|
130
|
+
index += 1;
|
|
131
|
+
} else if (token === '--catalog' && next) {
|
|
132
|
+
options.catalog = next;
|
|
133
|
+
index += 1;
|
|
134
|
+
} else if (token === '--dialogue-policy' && next) {
|
|
135
|
+
options.dialoguePolicy = next;
|
|
136
|
+
index += 1;
|
|
137
|
+
} else if (token === '--dialogue-profile' && next) {
|
|
138
|
+
options.dialogueProfile = next;
|
|
139
|
+
explicitKeys.add('dialogueProfile');
|
|
140
|
+
index += 1;
|
|
141
|
+
} else if (token === '--ui-mode' && next) {
|
|
142
|
+
options.uiMode = next;
|
|
143
|
+
explicitKeys.add('uiMode');
|
|
144
|
+
index += 1;
|
|
145
|
+
} else if (token === '--dialogue-out' && next) {
|
|
146
|
+
options.dialogueOut = next;
|
|
147
|
+
index += 1;
|
|
148
|
+
} else if (token === '--runtime-mode' && next) {
|
|
149
|
+
options.runtimeMode = next;
|
|
150
|
+
explicitKeys.add('runtimeMode');
|
|
151
|
+
index += 1;
|
|
152
|
+
} else if (token === '--runtime-environment' && next) {
|
|
153
|
+
options.runtimeEnvironment = next;
|
|
154
|
+
explicitKeys.add('runtimeEnvironment');
|
|
155
|
+
index += 1;
|
|
156
|
+
} else if (token === '--runtime-policy' && next) {
|
|
157
|
+
options.runtimePolicy = next;
|
|
158
|
+
index += 1;
|
|
159
|
+
} else if (token === '--runtime-out' && next) {
|
|
160
|
+
options.runtimeOut = next;
|
|
161
|
+
index += 1;
|
|
162
|
+
} else if (token === '--authorization-tier-policy' && next) {
|
|
163
|
+
options.authorizationTierPolicy = next;
|
|
164
|
+
index += 1;
|
|
165
|
+
} else if (token === '--authorization-tier-out' && next) {
|
|
166
|
+
options.authorizationTierOut = next;
|
|
167
|
+
index += 1;
|
|
168
|
+
} else if (token === '--context-contract' && next) {
|
|
169
|
+
options.contextContract = next;
|
|
170
|
+
index += 1;
|
|
171
|
+
} else if (token === '--no-strict-contract') {
|
|
172
|
+
options.strictContract = false;
|
|
173
|
+
} else if (token === '--moqui-config' && next) {
|
|
174
|
+
options.moquiConfig = next;
|
|
175
|
+
index += 1;
|
|
176
|
+
} else if (token === '--out-dir' && next) {
|
|
177
|
+
options.outDir = next;
|
|
178
|
+
index += 1;
|
|
179
|
+
} else if (token === '--out' && next) {
|
|
180
|
+
options.out = next;
|
|
181
|
+
index += 1;
|
|
182
|
+
} else if (token === '--work-order-out' && next) {
|
|
183
|
+
options.workOrderOut = next;
|
|
184
|
+
index += 1;
|
|
185
|
+
} else if (token === '--work-order-markdown-out' && next) {
|
|
186
|
+
options.workOrderMarkdownOut = next;
|
|
187
|
+
index += 1;
|
|
188
|
+
} else if (token === '--approval-actor' && next) {
|
|
189
|
+
options.approvalActor = next;
|
|
190
|
+
index += 1;
|
|
191
|
+
} else if (token === '--approval-actor-role' && next) {
|
|
192
|
+
options.approvalActorRole = next;
|
|
193
|
+
index += 1;
|
|
194
|
+
} else if (token === '--approver-actor' && next) {
|
|
195
|
+
options.approverActor = next;
|
|
196
|
+
index += 1;
|
|
197
|
+
} else if (token === '--approver-actor-role' && next) {
|
|
198
|
+
options.approverActorRole = next;
|
|
199
|
+
index += 1;
|
|
200
|
+
} else if (token === '--approval-role-policy' && next) {
|
|
201
|
+
options.approvalRolePolicy = next;
|
|
202
|
+
index += 1;
|
|
203
|
+
} else if (token === '--skip-submit') {
|
|
204
|
+
options.skipSubmit = true;
|
|
205
|
+
} else if (token === '--auto-approve-low-risk') {
|
|
206
|
+
options.autoApproveLowRisk = true;
|
|
207
|
+
} else if (token === '--auto-execute-low-risk') {
|
|
208
|
+
options.autoExecuteLowRisk = true;
|
|
209
|
+
explicitKeys.add('autoExecuteLowRisk');
|
|
210
|
+
} else if (token === '--allow-suggestion-apply') {
|
|
211
|
+
options.allowSuggestionApply = true;
|
|
212
|
+
} else if (token === '--live-apply') {
|
|
213
|
+
options.liveApply = true;
|
|
214
|
+
} else if (token === '--no-dry-run') {
|
|
215
|
+
options.dryRun = false;
|
|
216
|
+
} else if (token === '--feedback-score' && next) {
|
|
217
|
+
options.feedbackScore = Number(next);
|
|
218
|
+
index += 1;
|
|
219
|
+
} else if (token === '--feedback-comment' && next) {
|
|
220
|
+
options.feedbackComment = next;
|
|
221
|
+
index += 1;
|
|
222
|
+
} else if (token === '--feedback-tags' && next) {
|
|
223
|
+
options.feedbackTags = next.split(',').map(item => item.trim()).filter(Boolean);
|
|
224
|
+
index += 1;
|
|
225
|
+
} else if (token === '--feedback-channel' && next) {
|
|
226
|
+
options.feedbackChannel = next;
|
|
227
|
+
index += 1;
|
|
228
|
+
} else if (token === '--auth-password' && next) {
|
|
229
|
+
options.authPassword = next;
|
|
230
|
+
index += 1;
|
|
231
|
+
} else if (token === '--auth-password-hash' && next) {
|
|
232
|
+
options.authPasswordHash = next;
|
|
233
|
+
index += 1;
|
|
234
|
+
} else if (token === '--auth-password-env' && next) {
|
|
235
|
+
options.authPasswordEnv = next;
|
|
236
|
+
index += 1;
|
|
237
|
+
} else if (token === '--fail-on-dialogue-deny') {
|
|
238
|
+
options.failOnDialogueDeny = true;
|
|
239
|
+
} else if (token === '--fail-on-gate-deny') {
|
|
240
|
+
options.failOnGateDeny = true;
|
|
241
|
+
} else if (token === '--fail-on-gate-non-allow') {
|
|
242
|
+
options.failOnGateNonAllow = true;
|
|
243
|
+
} else if (token === '--fail-on-runtime-non-allow') {
|
|
244
|
+
options.failOnRuntimeNonAllow = true;
|
|
245
|
+
} else if (token === '--fail-on-execute-blocked') {
|
|
246
|
+
options.failOnExecuteBlocked = true;
|
|
247
|
+
} else if (token === '--json') {
|
|
248
|
+
options.json = true;
|
|
249
|
+
} else if (token === '--help' || token === '-h') {
|
|
250
|
+
printHelpAndExit(0);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!options.context) {
|
|
255
|
+
throw new Error('--context is required.');
|
|
256
|
+
}
|
|
257
|
+
if (!options.goal && !options.goalFile) {
|
|
258
|
+
throw new Error('either --goal or --goal-file is required.');
|
|
259
|
+
}
|
|
260
|
+
if (!['suggestion', 'apply'].includes(`${options.executionMode || ''}`.trim().toLowerCase())) {
|
|
261
|
+
throw new Error('--execution-mode must be one of: suggestion, apply');
|
|
262
|
+
}
|
|
263
|
+
if (!RUNTIME_MODES.has(`${options.runtimeMode || ''}`.trim().toLowerCase())) {
|
|
264
|
+
throw new Error(`--runtime-mode must be one of: ${Array.from(RUNTIME_MODES).join(', ')}`);
|
|
265
|
+
}
|
|
266
|
+
if (!RUNTIME_ENVIRONMENTS.has(`${options.runtimeEnvironment || ''}`.trim().toLowerCase())) {
|
|
267
|
+
throw new Error(`--runtime-environment must be one of: ${Array.from(RUNTIME_ENVIRONMENTS).join(', ')}`);
|
|
268
|
+
}
|
|
269
|
+
if (options.feedbackScore != null) {
|
|
270
|
+
if (!Number.isFinite(options.feedbackScore) || options.feedbackScore < 0 || options.feedbackScore > 5) {
|
|
271
|
+
throw new Error('--feedback-score must be between 0 and 5.');
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (options.authPasswordHash != null) {
|
|
275
|
+
const normalizedHash = `${options.authPasswordHash || ''}`.trim();
|
|
276
|
+
if (!/^[a-fA-F0-9]{64}$/.test(normalizedHash)) {
|
|
277
|
+
throw new Error('--auth-password-hash must be a sha256 hex string (64 chars).');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (options.authPasswordEnv != null && `${options.authPasswordEnv || ''}`.trim().length === 0) {
|
|
281
|
+
throw new Error('--auth-password-env cannot be empty.');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
options.userId = `${options.userId || ''}`.trim() || DEFAULT_USER_ID;
|
|
285
|
+
options.approvalActor = `${options.approvalActor || ''}`.trim() || DEFAULT_APPROVAL_ACTOR;
|
|
286
|
+
options.approvalActorRole = `${options.approvalActorRole || ''}`.trim().toLowerCase() || null;
|
|
287
|
+
options.approverActor = `${options.approverActor || ''}`.trim() || options.approvalActor;
|
|
288
|
+
options.approverActorRole = `${options.approverActorRole || ''}`.trim().toLowerCase() || options.approvalActorRole;
|
|
289
|
+
options.approvalRolePolicy = `${options.approvalRolePolicy || ''}`.trim() || null;
|
|
290
|
+
options.feedbackComment = `${options.feedbackComment || ''}`.trim() || null;
|
|
291
|
+
options.dialoguePolicy = `${options.dialoguePolicy || ''}`.trim() || null;
|
|
292
|
+
options.executionMode = `${options.executionMode || ''}`.trim().toLowerCase() || 'suggestion';
|
|
293
|
+
options.businessMode = normalizeBusinessMode(options.businessMode);
|
|
294
|
+
options.businessModePolicy = `${options.businessModePolicy || ''}`.trim() || DEFAULT_BUSINESS_MODE_POLICY;
|
|
295
|
+
options.dialogueProfile = `${options.dialogueProfile || ''}`.trim().toLowerCase() || DEFAULT_DIALOGUE_PROFILE;
|
|
296
|
+
options.uiMode = `${options.uiMode || ''}`.trim().toLowerCase()
|
|
297
|
+
|| (options.dialogueProfile === 'system-maintainer' ? 'ops-console' : 'user-app');
|
|
298
|
+
options.dialogueOut = `${options.dialogueOut || ''}`.trim() || null;
|
|
299
|
+
options.runtimeMode = `${options.runtimeMode || ''}`.trim().toLowerCase() || DEFAULT_RUNTIME_MODE;
|
|
300
|
+
options.runtimeEnvironment = `${options.runtimeEnvironment || ''}`.trim().toLowerCase() || DEFAULT_RUNTIME_ENVIRONMENT;
|
|
301
|
+
options.runtimePolicy = `${options.runtimePolicy || ''}`.trim() || null;
|
|
302
|
+
options.runtimeOut = `${options.runtimeOut || ''}`.trim() || null;
|
|
303
|
+
options.authorizationTierPolicy = `${options.authorizationTierPolicy || ''}`.trim() || null;
|
|
304
|
+
options.authorizationTierOut = `${options.authorizationTierOut || ''}`.trim() || null;
|
|
305
|
+
options.feedbackChannel = `${options.feedbackChannel || ''}`.trim().toLowerCase() || DEFAULT_FEEDBACK_CHANNEL;
|
|
306
|
+
options.authPassword = options.authPassword == null ? null : `${options.authPassword}`;
|
|
307
|
+
options.authPasswordHash = options.authPasswordHash == null
|
|
308
|
+
? null
|
|
309
|
+
: `${options.authPasswordHash}`.trim().toLowerCase();
|
|
310
|
+
options.authPasswordEnv = `${options.authPasswordEnv || ''}`.trim() || null;
|
|
311
|
+
options.workOrderOut = `${options.workOrderOut || ''}`.trim() || null;
|
|
312
|
+
options.workOrderMarkdownOut = `${options.workOrderMarkdownOut || ''}`.trim() || null;
|
|
313
|
+
if (!FEEDBACK_CHANNELS.has(options.feedbackChannel)) {
|
|
314
|
+
throw new Error(`--feedback-channel must be one of: ${Array.from(FEEDBACK_CHANNELS).join(', ')}`);
|
|
315
|
+
}
|
|
316
|
+
options.feedbackTags = Array.from(new Set(options.feedbackTags.map(item => item.toLowerCase())));
|
|
317
|
+
if (!DIALOGUE_PROFILES.has(options.dialogueProfile)) {
|
|
318
|
+
throw new Error(`--dialogue-profile must be one of: ${Array.from(DIALOGUE_PROFILES).join(', ')}`);
|
|
319
|
+
}
|
|
320
|
+
if (!UI_MODES.has(options.uiMode)) {
|
|
321
|
+
throw new Error(`--ui-mode must be one of: ${Array.from(UI_MODES).join(', ')}`);
|
|
322
|
+
}
|
|
323
|
+
if (options.businessMode && !BUSINESS_MODES.has(options.businessMode)) {
|
|
324
|
+
throw new Error(`--business-mode must be one of: ${Array.from(BUSINESS_MODES).join(', ')}`);
|
|
325
|
+
}
|
|
326
|
+
options.businessModeState = applyBusinessModePolicy(options, explicitKeys, process.cwd());
|
|
327
|
+
if (!['suggestion', 'apply'].includes(options.executionMode)) {
|
|
328
|
+
throw new Error('--execution-mode must be one of: suggestion, apply');
|
|
329
|
+
}
|
|
330
|
+
if (!DIALOGUE_PROFILES.has(options.dialogueProfile)) {
|
|
331
|
+
throw new Error(`--dialogue-profile must be one of: ${Array.from(DIALOGUE_PROFILES).join(', ')}`);
|
|
332
|
+
}
|
|
333
|
+
if (!UI_MODES.has(options.uiMode)) {
|
|
334
|
+
throw new Error(`--ui-mode must be one of: ${Array.from(UI_MODES).join(', ')}`);
|
|
335
|
+
}
|
|
336
|
+
if (!RUNTIME_MODES.has(options.runtimeMode)) {
|
|
337
|
+
throw new Error(`--runtime-mode must be one of: ${Array.from(RUNTIME_MODES).join(', ')}`);
|
|
338
|
+
}
|
|
339
|
+
if (!RUNTIME_ENVIRONMENTS.has(options.runtimeEnvironment)) {
|
|
340
|
+
throw new Error(`--runtime-environment must be one of: ${Array.from(RUNTIME_ENVIRONMENTS).join(', ')}`);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return options;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function printHelpAndExit(code) {
|
|
347
|
+
const lines = [
|
|
348
|
+
'Usage: node scripts/interactive-customization-loop.js --context <path> (--goal <text> | --goal-file <path>) [options]',
|
|
349
|
+
'',
|
|
350
|
+
'Pipeline:',
|
|
351
|
+
' dialogue -> intent -> plan -> gate -> runtime -> authorization-tier -> approval(init/submit) -> optional low-risk apply',
|
|
352
|
+
'',
|
|
353
|
+
'Options:',
|
|
354
|
+
' --context <path> Page context JSON file (required)',
|
|
355
|
+
' --goal <text> Business goal text',
|
|
356
|
+
' --goal-file <path> File containing business goal text',
|
|
357
|
+
` --user-id <id> User identifier (default: ${DEFAULT_USER_ID})`,
|
|
358
|
+
' --session-id <id> Session id (default: auto-generated)',
|
|
359
|
+
' --execution-mode <mode> suggestion|apply (default: suggestion)',
|
|
360
|
+
' --business-mode <mode> user-mode|ops-mode|dev-mode (preset routing profile)',
|
|
361
|
+
` --business-mode-policy <path> Business mode policy override (default: ${DEFAULT_BUSINESS_MODE_POLICY})`,
|
|
362
|
+
' --allow-mode-override Allow option overrides that conflict with business mode preset',
|
|
363
|
+
' --policy <path> Guardrail policy override',
|
|
364
|
+
' --catalog <path> High-risk catalog override',
|
|
365
|
+
' --dialogue-policy <path> Dialogue governance policy override',
|
|
366
|
+
` --dialogue-profile <name> business-user|system-maintainer (default: ${DEFAULT_DIALOGUE_PROFILE})`,
|
|
367
|
+
` --ui-mode <name> user-app|ops-console (default by dialogue profile)`,
|
|
368
|
+
' --dialogue-out <path> Dialogue governance report output path',
|
|
369
|
+
` --runtime-mode <name> user-assist|ops-fix|feature-dev (default: ${DEFAULT_RUNTIME_MODE})`,
|
|
370
|
+
` --runtime-environment <name> dev|staging|prod (default: ${DEFAULT_RUNTIME_ENVIRONMENT})`,
|
|
371
|
+
' --runtime-policy <path> Runtime mode/environment policy override',
|
|
372
|
+
' --runtime-out <path> Runtime policy evaluation report output path',
|
|
373
|
+
' --authorization-tier-policy <path> Authorization tier policy override',
|
|
374
|
+
' --authorization-tier-out <path> Authorization tier evaluation report output path',
|
|
375
|
+
' --context-contract <path> Context contract override for intent build',
|
|
376
|
+
' --no-strict-contract Do not fail when context contract validation has issues',
|
|
377
|
+
' --moqui-config <path> Moqui adapter runtime config',
|
|
378
|
+
` --out-dir <path> Loop artifact root (default: ${DEFAULT_OUT_DIR})`,
|
|
379
|
+
' --out <path> Loop summary JSON output path',
|
|
380
|
+
' --work-order-out <path> Work-order JSON output path',
|
|
381
|
+
' --work-order-markdown-out <path> Work-order markdown output path',
|
|
382
|
+
` --approval-actor <id> Approval workflow actor (default: ${DEFAULT_APPROVAL_ACTOR})`,
|
|
383
|
+
' --approval-actor-role <name> Approval actor role for role-policy checks',
|
|
384
|
+
' --approver-actor <id> Auto-approve actor (default: approval actor)',
|
|
385
|
+
' --approver-actor-role <name> Auto-approve actor role (default: approval actor role)',
|
|
386
|
+
' --approval-role-policy <path> Approval role policy JSON path',
|
|
387
|
+
' --skip-submit Skip approval submit step',
|
|
388
|
+
' --auto-approve-low-risk Auto-approve when gate=allow and risk=low',
|
|
389
|
+
' --auto-execute-low-risk Auto-run adapter low-risk-apply when gate=allow and risk=low',
|
|
390
|
+
' --allow-suggestion-apply Allow applying plans with execution_mode=suggestion',
|
|
391
|
+
' --live-apply Enable live apply mode for adapter',
|
|
392
|
+
' --no-dry-run Disable dry-run simulation when live apply is enabled',
|
|
393
|
+
' --feedback-score <0..5> Optional user feedback score to append into feedback JSONL',
|
|
394
|
+
' --feedback-comment <text> Optional user feedback comment',
|
|
395
|
+
' --feedback-tags <csv> Optional feedback tags (comma-separated)',
|
|
396
|
+
` --feedback-channel <name> ui|cli|api|other (default: ${DEFAULT_FEEDBACK_CHANNEL})`,
|
|
397
|
+
' --auth-password <text> One-time password for protected execute action',
|
|
398
|
+
' --auth-password-hash <sha256> Password verifier hash override',
|
|
399
|
+
` --auth-password-env <name> Password hash env name (default: ${DEFAULT_AUTH_PASSWORD_HASH_ENV})`,
|
|
400
|
+
' --fail-on-dialogue-deny Exit code 2 if dialogue decision is deny',
|
|
401
|
+
' --fail-on-gate-deny Exit code 2 if gate decision is deny',
|
|
402
|
+
' --fail-on-gate-non-allow Exit code 2 if gate decision is deny/review-required',
|
|
403
|
+
' --fail-on-runtime-non-allow Exit code 2 if runtime decision is deny/review-required',
|
|
404
|
+
' --fail-on-execute-blocked Exit code 2 if auto execute result is blocked/non-success',
|
|
405
|
+
' --json Print loop summary JSON',
|
|
406
|
+
' -h, --help Show this help'
|
|
407
|
+
];
|
|
408
|
+
console.log(lines.join('\n'));
|
|
409
|
+
process.exit(code);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function resolvePath(cwd, value) {
|
|
413
|
+
return path.isAbsolute(value) ? value : path.resolve(cwd, value);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function normalizeSessionId(value) {
|
|
417
|
+
const raw = `${value || ''}`.trim();
|
|
418
|
+
if (raw.length > 0) {
|
|
419
|
+
return raw.replace(/[^\w.-]/g, '-');
|
|
420
|
+
}
|
|
421
|
+
return `session-${crypto.randomUUID()}`;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function parseJsonOutput(text, label) {
|
|
425
|
+
const raw = `${text || ''}`.trim();
|
|
426
|
+
if (!raw) {
|
|
427
|
+
throw new Error(`${label} produced empty stdout`);
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
return JSON.parse(raw);
|
|
431
|
+
} catch (error) {
|
|
432
|
+
throw new Error(`${label} returned invalid JSON: ${error.message}`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function buildCommandString(scriptPath, args, redactFlags = []) {
|
|
437
|
+
const redacted = new Set(redactFlags);
|
|
438
|
+
const maskedArgs = [];
|
|
439
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
440
|
+
const token = args[index];
|
|
441
|
+
maskedArgs.push(token);
|
|
442
|
+
if (redacted.has(token)) {
|
|
443
|
+
if (index + 1 < args.length) {
|
|
444
|
+
maskedArgs.push('***');
|
|
445
|
+
index += 1;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return [process.execPath, scriptPath, ...maskedArgs].join(' ');
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function runScript({
|
|
453
|
+
label,
|
|
454
|
+
scriptPath,
|
|
455
|
+
args,
|
|
456
|
+
cwd,
|
|
457
|
+
allowedExitCodes
|
|
458
|
+
}) {
|
|
459
|
+
const result = spawnSync(process.execPath, [scriptPath, ...args], {
|
|
460
|
+
cwd,
|
|
461
|
+
encoding: 'utf8'
|
|
462
|
+
});
|
|
463
|
+
const exitCode = typeof result.status === 'number' ? result.status : 1;
|
|
464
|
+
|
|
465
|
+
if (!allowedExitCodes.includes(exitCode)) {
|
|
466
|
+
const stderr = `${result.stderr || ''}`.trim();
|
|
467
|
+
throw new Error(
|
|
468
|
+
`${label} failed with exit code ${exitCode}${stderr ? `: ${stderr}` : ''}`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return {
|
|
473
|
+
exit_code: exitCode,
|
|
474
|
+
stdout: `${result.stdout || ''}`,
|
|
475
|
+
stderr: `${result.stderr || ''}`
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function toRelative(cwd, absolutePath) {
|
|
480
|
+
return path.relative(cwd, absolutePath) || '.';
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function buildStep(name, payload, command, exitCode) {
|
|
484
|
+
return {
|
|
485
|
+
name,
|
|
486
|
+
command,
|
|
487
|
+
exit_code: exitCode,
|
|
488
|
+
payload
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function shouldAutoLowRisk({
|
|
493
|
+
gateDecision,
|
|
494
|
+
riskLevel,
|
|
495
|
+
dialogueDecision,
|
|
496
|
+
runtimeDecision,
|
|
497
|
+
runtimeAutoExecuteAllowed,
|
|
498
|
+
authorizationTierDecision,
|
|
499
|
+
authorizationTierAutoExecuteAllowed
|
|
500
|
+
}) {
|
|
501
|
+
return `${dialogueDecision || ''}`.trim().toLowerCase() !== 'deny' &&
|
|
502
|
+
`${gateDecision || ''}`.trim().toLowerCase() === 'allow' &&
|
|
503
|
+
`${riskLevel || ''}`.trim().toLowerCase() === 'low' &&
|
|
504
|
+
`${runtimeDecision || ''}`.trim().toLowerCase() === 'allow' &&
|
|
505
|
+
runtimeAutoExecuteAllowed === true &&
|
|
506
|
+
`${authorizationTierDecision || ''}`.trim().toLowerCase() === 'allow' &&
|
|
507
|
+
authorizationTierAutoExecuteAllowed === true;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function buildSummaryStatus({
|
|
511
|
+
dialogueDecision,
|
|
512
|
+
gateDecision,
|
|
513
|
+
runtimeDecision,
|
|
514
|
+
authorizationTierDecision,
|
|
515
|
+
executionAttempted,
|
|
516
|
+
executionBlocked,
|
|
517
|
+
executionResult
|
|
518
|
+
}) {
|
|
519
|
+
const normalizedDialogueDecision = `${dialogueDecision || ''}`.trim().toLowerCase();
|
|
520
|
+
const decision = `${gateDecision || ''}`.trim().toLowerCase();
|
|
521
|
+
const runtime = `${runtimeDecision || ''}`.trim().toLowerCase();
|
|
522
|
+
const authorization = `${authorizationTierDecision || ''}`.trim().toLowerCase();
|
|
523
|
+
if (normalizedDialogueDecision === 'deny') {
|
|
524
|
+
return 'blocked';
|
|
525
|
+
}
|
|
526
|
+
if (authorization === 'deny') {
|
|
527
|
+
return 'blocked';
|
|
528
|
+
}
|
|
529
|
+
if (authorization === 'review-required') {
|
|
530
|
+
return 'requires-review';
|
|
531
|
+
}
|
|
532
|
+
if (runtime === 'deny') {
|
|
533
|
+
return 'blocked';
|
|
534
|
+
}
|
|
535
|
+
if (runtime === 'review-required') {
|
|
536
|
+
return 'requires-review';
|
|
537
|
+
}
|
|
538
|
+
if (normalizedDialogueDecision === 'clarify' && decision === 'allow' && !executionAttempted) {
|
|
539
|
+
return 'requires-clarification';
|
|
540
|
+
}
|
|
541
|
+
if (decision === 'deny') {
|
|
542
|
+
return 'blocked';
|
|
543
|
+
}
|
|
544
|
+
if (decision === 'review-required') {
|
|
545
|
+
return 'requires-review';
|
|
546
|
+
}
|
|
547
|
+
if (!executionAttempted) {
|
|
548
|
+
return 'ready-for-apply';
|
|
549
|
+
}
|
|
550
|
+
if (executionBlocked) {
|
|
551
|
+
return 'apply-blocked';
|
|
552
|
+
}
|
|
553
|
+
if (`${executionResult || ''}`.trim().toLowerCase() === 'success') {
|
|
554
|
+
return 'completed';
|
|
555
|
+
}
|
|
556
|
+
return 'apply-finished-with-risk';
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function detectExecutionBlockReasonCategory(execution) {
|
|
560
|
+
if (!execution || execution.blocked !== true) {
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
const reason = `${execution.reason || ''}`.trim().toLowerCase();
|
|
564
|
+
const decision = `${execution.decision || ''}`.trim().toLowerCase();
|
|
565
|
+
|
|
566
|
+
if (reason.includes('password authorization')) {
|
|
567
|
+
return 'password-authorization';
|
|
568
|
+
}
|
|
569
|
+
if (reason.includes('authorization tier')) {
|
|
570
|
+
return 'authorization-tier';
|
|
571
|
+
}
|
|
572
|
+
if (reason.includes('actor role') || reason.includes('allowed roles')) {
|
|
573
|
+
return 'role-policy';
|
|
574
|
+
}
|
|
575
|
+
if (decision === 'runtime-blocked' || reason.includes('runtime environment')) {
|
|
576
|
+
return 'runtime-policy';
|
|
577
|
+
}
|
|
578
|
+
if (decision === 'approval-blocked') {
|
|
579
|
+
return 'approval-policy';
|
|
580
|
+
}
|
|
581
|
+
return 'unknown';
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function buildExecutionBlockRemediationHint({
|
|
585
|
+
blockReasonCategory,
|
|
586
|
+
executionReason
|
|
587
|
+
}) {
|
|
588
|
+
if (!blockReasonCategory) {
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
if (blockReasonCategory === 'password-authorization') {
|
|
592
|
+
return 'Provide one-time password authorization and rerun apply.';
|
|
593
|
+
}
|
|
594
|
+
if (blockReasonCategory === 'role-policy') {
|
|
595
|
+
return 'Use an actor role allowed by approval role policy for this action and rerun apply.';
|
|
596
|
+
}
|
|
597
|
+
if (blockReasonCategory === 'authorization-tier') {
|
|
598
|
+
return 'Switch to an authorized dialogue profile/environment and satisfy secondary authorization requirements.';
|
|
599
|
+
}
|
|
600
|
+
if (blockReasonCategory === 'runtime-policy') {
|
|
601
|
+
return 'Adjust runtime mode/environment policy or run under an environment that permits the action.';
|
|
602
|
+
}
|
|
603
|
+
if (blockReasonCategory === 'approval-policy') {
|
|
604
|
+
return 'Complete required approval workflow transitions before execute.';
|
|
605
|
+
}
|
|
606
|
+
if (`${executionReason || ''}`.trim().length > 0) {
|
|
607
|
+
return `Review execution block reason: ${executionReason}`;
|
|
608
|
+
}
|
|
609
|
+
return 'Review execution block reason and approval/runtime policies.';
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function buildNextActions({
|
|
613
|
+
dialogueDecision,
|
|
614
|
+
gateDecision,
|
|
615
|
+
runtimeDecision,
|
|
616
|
+
authorizationTierDecision,
|
|
617
|
+
riskLevel,
|
|
618
|
+
runtime,
|
|
619
|
+
authorizationTier,
|
|
620
|
+
autoExecuteTriggered,
|
|
621
|
+
executionPayload,
|
|
622
|
+
executionBlockReasonCategory,
|
|
623
|
+
executionReason,
|
|
624
|
+
approvalStatus,
|
|
625
|
+
feedbackLogged,
|
|
626
|
+
artifacts
|
|
627
|
+
}) {
|
|
628
|
+
const actions = [];
|
|
629
|
+
const dialogue = `${dialogueDecision || ''}`.trim().toLowerCase();
|
|
630
|
+
const decision = `${gateDecision || ''}`.trim().toLowerCase();
|
|
631
|
+
const runtimeDecisionNormalized = `${runtimeDecision || ''}`.trim().toLowerCase();
|
|
632
|
+
const authorizationDecision = `${authorizationTierDecision || ''}`.trim().toLowerCase();
|
|
633
|
+
const risk = `${riskLevel || ''}`.trim().toLowerCase();
|
|
634
|
+
|
|
635
|
+
if (dialogue === 'deny') {
|
|
636
|
+
actions.push('Rewrite the goal to remove sensitive/forbidden requests and rerun dialogue governance.');
|
|
637
|
+
actions.push(`node scripts/interactive-dialogue-governance.js --goal "..." --context ${artifacts.context_json} --json`);
|
|
638
|
+
} else if (authorizationDecision === 'deny') {
|
|
639
|
+
actions.push('Authorization tier denied apply for current profile/environment.');
|
|
640
|
+
actions.push(`node scripts/interactive-authorization-tier-evaluate.js --execution-mode apply --dialogue-profile ${authorizationTier.profile || DEFAULT_DIALOGUE_PROFILE} --runtime-environment ${runtime.environment || DEFAULT_RUNTIME_ENVIRONMENT} --json`);
|
|
641
|
+
actions.push('Use suggestion mode for business-user, or switch to system-maintainer with explicit approval controls.');
|
|
642
|
+
} else if (authorizationDecision === 'review-required') {
|
|
643
|
+
actions.push('Authorization tier requires manual review before apply.');
|
|
644
|
+
actions.push('Complete secondary authorization checks (password/role separation/change ticket) before execution.');
|
|
645
|
+
} else if (runtimeDecisionNormalized === 'deny') {
|
|
646
|
+
actions.push('Runtime policy denied the plan. Switch runtime mode/environment or reduce risky actions.');
|
|
647
|
+
actions.push(`node scripts/interactive-runtime-policy-evaluate.js --plan ${artifacts.plan_json} --ui-mode ${runtime.ui_mode || 'user-app'} --runtime-mode ${runtime.mode || DEFAULT_RUNTIME_MODE} --runtime-environment ${runtime.environment || DEFAULT_RUNTIME_ENVIRONMENT} --json`);
|
|
648
|
+
} else if (runtimeDecisionNormalized === 'review-required') {
|
|
649
|
+
actions.push('Runtime policy requires manual review before apply.');
|
|
650
|
+
actions.push('Complete review ticket and approval workflow before any execution step.');
|
|
651
|
+
} else if (decision === 'deny') {
|
|
652
|
+
actions.push('Revise business goal wording and regenerate intent/plan before retrying.');
|
|
653
|
+
actions.push('Review gate failure reasons in the generated gate markdown report.');
|
|
654
|
+
} else if (decision === 'review-required') {
|
|
655
|
+
actions.push('Complete manual approval review before any apply step.');
|
|
656
|
+
actions.push('Tune plan actions to reduce sensitive or high-risk operations.');
|
|
657
|
+
} else if (!autoExecuteTriggered) {
|
|
658
|
+
actions.push('Review plan and gate outputs, then run adapter low-risk apply when ready.');
|
|
659
|
+
actions.push(`node scripts/interactive-moqui-adapter.js --action low-risk-apply --plan ${artifacts.plan_json} --json`);
|
|
660
|
+
} else if (executionPayload && executionPayload.execution_record) {
|
|
661
|
+
const record = executionPayload.execution_record;
|
|
662
|
+
if (`${record.result || ''}`.trim().toLowerCase() === 'success') {
|
|
663
|
+
if (feedbackLogged) {
|
|
664
|
+
actions.push(`Execution succeeded (execution_id=${record.execution_id || 'n/a'}) and feedback was logged.`);
|
|
665
|
+
actions.push('node scripts/interactive-governance-report.js --period weekly --json');
|
|
666
|
+
} else {
|
|
667
|
+
actions.push(`Execution succeeded (execution_id=${record.execution_id || 'n/a'}). Collect user feedback for governance.`);
|
|
668
|
+
actions.push('node scripts/interactive-feedback-log.js --score 5 --comment "business flow improved" --json');
|
|
669
|
+
}
|
|
670
|
+
} else {
|
|
671
|
+
actions.push('Execution was blocked or failed. Inspect adapter output and adjust plan/policy.');
|
|
672
|
+
actions.push(`node scripts/interactive-moqui-adapter.js --action validate --plan ${artifacts.plan_json} --json`);
|
|
673
|
+
}
|
|
674
|
+
} else if (executionBlockReasonCategory === 'password-authorization') {
|
|
675
|
+
actions.push('Provide one-time password authorization and rerun low-risk apply.');
|
|
676
|
+
actions.push(`node scripts/interactive-customization-loop.js --context ${artifacts.context_json} --goal "..." --execution-mode apply --auto-execute-low-risk --auth-password "<password>" --json`);
|
|
677
|
+
} else if (executionBlockReasonCategory === 'authorization-tier') {
|
|
678
|
+
actions.push('Satisfy authorization tier step-up requirements (password/role policy/role separation) and rerun apply.');
|
|
679
|
+
actions.push(`node scripts/interactive-customization-loop.js --context ${artifacts.context_json} --goal "..." --dialogue-profile system-maintainer --execution-mode apply --auto-execute-low-risk --auth-password "<password>" --json`);
|
|
680
|
+
} else if (executionBlockReasonCategory === 'role-policy') {
|
|
681
|
+
actions.push('Use an approver actor role allowed by approval role policy, then rerun low-risk apply.');
|
|
682
|
+
actions.push(`node scripts/interactive-approval-workflow.js --action status --state-file ${artifacts.approval_state_json} --json`);
|
|
683
|
+
actions.push(`node scripts/interactive-customization-loop.js --context ${artifacts.context_json} --goal "..." --execution-mode apply --auto-execute-low-risk --approval-role-policy "<role-policy.json>" --approver-actor-role "<allowed-role>" --json`);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if (risk === 'low' && approvalStatus === 'submitted') {
|
|
687
|
+
actions.push('Optional: approve workflow to keep an auditable close-loop trail.');
|
|
688
|
+
}
|
|
689
|
+
return actions;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
async function main() {
|
|
693
|
+
const options = parseArgs(process.argv.slice(2));
|
|
694
|
+
const cwd = process.cwd();
|
|
695
|
+
const sessionId = normalizeSessionId(options.sessionId);
|
|
696
|
+
|
|
697
|
+
const contextPath = resolvePath(cwd, options.context);
|
|
698
|
+
const globalFeedbackPath = resolvePath(cwd, '.sce/reports/interactive-user-feedback.jsonl');
|
|
699
|
+
const globalDialogueAuthorizationSignalsPath = resolvePath(cwd, '.sce/reports/interactive-dialogue-authorization-signals.jsonl');
|
|
700
|
+
const globalRuntimeSignalsPath = resolvePath(cwd, '.sce/reports/interactive-runtime-signals.jsonl');
|
|
701
|
+
const globalAuthorizationTierSignalsPath = resolvePath(cwd, '.sce/reports/interactive-authorization-tier-signals.jsonl');
|
|
702
|
+
const outRoot = resolvePath(cwd, options.outDir);
|
|
703
|
+
const sessionDir = path.join(outRoot, sessionId);
|
|
704
|
+
const summaryOutPath = options.out
|
|
705
|
+
? resolvePath(cwd, options.out)
|
|
706
|
+
: path.join(sessionDir, 'interactive-customization-loop.summary.json');
|
|
707
|
+
|
|
708
|
+
const artifacts = {
|
|
709
|
+
session_dir: toRelative(cwd, sessionDir),
|
|
710
|
+
context_json: toRelative(cwd, contextPath),
|
|
711
|
+
dialogue_json: toRelative(cwd, options.dialogueOut
|
|
712
|
+
? resolvePath(cwd, options.dialogueOut)
|
|
713
|
+
: path.join(sessionDir, 'interactive-dialogue-governance.json')),
|
|
714
|
+
runtime_json: toRelative(cwd, options.runtimeOut
|
|
715
|
+
? resolvePath(cwd, options.runtimeOut)
|
|
716
|
+
: path.join(sessionDir, 'interactive-runtime-policy.json')),
|
|
717
|
+
authorization_tier_json: toRelative(cwd, options.authorizationTierOut
|
|
718
|
+
? resolvePath(cwd, options.authorizationTierOut)
|
|
719
|
+
: path.join(sessionDir, 'interactive-authorization-tier.json')),
|
|
720
|
+
intent_json: toRelative(cwd, path.join(sessionDir, 'interactive-change-intent.json')),
|
|
721
|
+
explain_md: toRelative(cwd, path.join(sessionDir, 'interactive-page-explain.md')),
|
|
722
|
+
intent_audit_jsonl: toRelative(cwd, path.join(sessionDir, 'interactive-copilot-audit.jsonl')),
|
|
723
|
+
plan_json: toRelative(cwd, path.join(sessionDir, 'interactive-change-plan.generated.json')),
|
|
724
|
+
plan_md: toRelative(cwd, path.join(sessionDir, 'interactive-change-plan.generated.md')),
|
|
725
|
+
gate_json: toRelative(cwd, path.join(sessionDir, 'interactive-change-plan-gate.json')),
|
|
726
|
+
gate_md: toRelative(cwd, path.join(sessionDir, 'interactive-change-plan-gate.md')),
|
|
727
|
+
approval_state_json: toRelative(cwd, path.join(sessionDir, 'interactive-approval-state.json')),
|
|
728
|
+
approval_audit_jsonl: toRelative(cwd, path.join(sessionDir, 'interactive-approval-events.jsonl')),
|
|
729
|
+
adapter_json: toRelative(cwd, path.join(sessionDir, 'interactive-moqui-adapter.json')),
|
|
730
|
+
work_order_json: toRelative(cwd, options.workOrderOut
|
|
731
|
+
? resolvePath(cwd, options.workOrderOut)
|
|
732
|
+
: path.join(sessionDir, 'interactive-work-order.json')),
|
|
733
|
+
work_order_md: toRelative(cwd, options.workOrderMarkdownOut
|
|
734
|
+
? resolvePath(cwd, options.workOrderMarkdownOut)
|
|
735
|
+
: path.join(sessionDir, 'interactive-work-order.md')),
|
|
736
|
+
dialogue_authorization_signals_global_jsonl: toRelative(cwd, globalDialogueAuthorizationSignalsPath),
|
|
737
|
+
dialogue_authorization_signals_jsonl: toRelative(cwd, path.join(sessionDir, 'interactive-dialogue-authorization-signals.jsonl')),
|
|
738
|
+
runtime_signals_global_jsonl: toRelative(cwd, globalRuntimeSignalsPath),
|
|
739
|
+
runtime_signals_jsonl: toRelative(cwd, path.join(sessionDir, 'interactive-runtime-signals.jsonl')),
|
|
740
|
+
authorization_tier_signals_global_jsonl: toRelative(cwd, globalAuthorizationTierSignalsPath),
|
|
741
|
+
authorization_tier_signals_jsonl: toRelative(cwd, path.join(sessionDir, 'interactive-authorization-tier-signals.jsonl')),
|
|
742
|
+
feedback_global_jsonl: toRelative(cwd, globalFeedbackPath),
|
|
743
|
+
feedback_jsonl: toRelative(cwd, path.join(sessionDir, 'interactive-user-feedback.jsonl')),
|
|
744
|
+
summary_json: toRelative(cwd, summaryOutPath)
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
await fs.ensureDir(sessionDir);
|
|
748
|
+
|
|
749
|
+
const steps = [];
|
|
750
|
+
|
|
751
|
+
const dialogueArgs = [
|
|
752
|
+
'--context', contextPath,
|
|
753
|
+
'--ui-mode', options.uiMode,
|
|
754
|
+
'--execution-mode', options.executionMode,
|
|
755
|
+
'--runtime-environment', options.runtimeEnvironment,
|
|
756
|
+
'--out', resolvePath(cwd, artifacts.dialogue_json),
|
|
757
|
+
'--json'
|
|
758
|
+
];
|
|
759
|
+
if (options.goal) {
|
|
760
|
+
dialogueArgs.push('--goal', options.goal);
|
|
761
|
+
} else {
|
|
762
|
+
dialogueArgs.push('--goal-file', resolvePath(cwd, options.goalFile));
|
|
763
|
+
}
|
|
764
|
+
if (options.dialoguePolicy) {
|
|
765
|
+
dialogueArgs.push('--policy', resolvePath(cwd, options.dialoguePolicy));
|
|
766
|
+
}
|
|
767
|
+
if (options.dialogueProfile) {
|
|
768
|
+
dialogueArgs.push('--profile', options.dialogueProfile);
|
|
769
|
+
}
|
|
770
|
+
const dialogueResult = runScript({
|
|
771
|
+
label: 'interactive-dialogue-governance',
|
|
772
|
+
scriptPath: SCRIPT_DIALOGUE,
|
|
773
|
+
args: dialogueArgs,
|
|
774
|
+
cwd,
|
|
775
|
+
allowedExitCodes: [0]
|
|
776
|
+
});
|
|
777
|
+
const dialoguePayload = parseJsonOutput(dialogueResult.stdout, 'interactive-dialogue-governance');
|
|
778
|
+
const dialogueDecision = dialoguePayload && dialoguePayload.decision ? dialoguePayload.decision : 'allow';
|
|
779
|
+
const dialogueAuthorization = dialoguePayload &&
|
|
780
|
+
dialoguePayload.authorization_dialogue &&
|
|
781
|
+
typeof dialoguePayload.authorization_dialogue === 'object'
|
|
782
|
+
? dialoguePayload.authorization_dialogue
|
|
783
|
+
: {};
|
|
784
|
+
const dialogueAuthorizationDecision = `${dialogueAuthorization.decision || 'allow'}`.trim().toLowerCase() || 'allow';
|
|
785
|
+
const dialogueAuthorizationSignalRecord = {
|
|
786
|
+
event_type: 'interactive.dialogue.authorization.evaluated',
|
|
787
|
+
timestamp: new Date().toISOString(),
|
|
788
|
+
session_id: sessionId,
|
|
789
|
+
user_id: options.userId,
|
|
790
|
+
business_mode: options.businessMode,
|
|
791
|
+
ui_mode: options.uiMode,
|
|
792
|
+
dialogue_profile: options.dialogueProfile,
|
|
793
|
+
execution_mode: options.executionMode,
|
|
794
|
+
runtime_environment: options.runtimeEnvironment,
|
|
795
|
+
decision: dialogueAuthorizationDecision,
|
|
796
|
+
reasons: Array.isArray(dialogueAuthorization.reasons) ? dialogueAuthorization.reasons : [],
|
|
797
|
+
required_inputs: Array.isArray(dialogueAuthorization.required_inputs) ? dialogueAuthorization.required_inputs : [],
|
|
798
|
+
required_confirmation_steps: Array.isArray(dialogueAuthorization.required_confirmation_steps)
|
|
799
|
+
? dialogueAuthorization.required_confirmation_steps
|
|
800
|
+
: []
|
|
801
|
+
};
|
|
802
|
+
const dialogueAuthorizationSignalsGlobalPath = resolvePath(cwd, artifacts.dialogue_authorization_signals_global_jsonl);
|
|
803
|
+
await fs.ensureDir(path.dirname(dialogueAuthorizationSignalsGlobalPath));
|
|
804
|
+
await fs.appendFile(dialogueAuthorizationSignalsGlobalPath, `${JSON.stringify(dialogueAuthorizationSignalRecord)}\n`, 'utf8');
|
|
805
|
+
const dialogueAuthorizationSignalsSessionPath = resolvePath(cwd, artifacts.dialogue_authorization_signals_jsonl);
|
|
806
|
+
await fs.ensureDir(path.dirname(dialogueAuthorizationSignalsSessionPath));
|
|
807
|
+
await fs.appendFile(dialogueAuthorizationSignalsSessionPath, `${JSON.stringify(dialogueAuthorizationSignalRecord)}\n`, 'utf8');
|
|
808
|
+
steps.push(buildStep(
|
|
809
|
+
'dialogue',
|
|
810
|
+
dialoguePayload,
|
|
811
|
+
buildCommandString(SCRIPT_DIALOGUE, dialogueArgs),
|
|
812
|
+
dialogueResult.exit_code
|
|
813
|
+
));
|
|
814
|
+
|
|
815
|
+
const intentArgs = [
|
|
816
|
+
'--context', contextPath,
|
|
817
|
+
'--user-id', options.userId,
|
|
818
|
+
'--session-id', sessionId,
|
|
819
|
+
'--out-intent', resolvePath(cwd, artifacts.intent_json),
|
|
820
|
+
'--out-explain', resolvePath(cwd, artifacts.explain_md),
|
|
821
|
+
'--audit-file', resolvePath(cwd, artifacts.intent_audit_jsonl),
|
|
822
|
+
'--json'
|
|
823
|
+
];
|
|
824
|
+
if (options.goal) {
|
|
825
|
+
intentArgs.push('--goal', options.goal);
|
|
826
|
+
} else {
|
|
827
|
+
intentArgs.push('--goal-file', resolvePath(cwd, options.goalFile));
|
|
828
|
+
}
|
|
829
|
+
if (options.contextContract) {
|
|
830
|
+
intentArgs.push('--context-contract', resolvePath(cwd, options.contextContract));
|
|
831
|
+
}
|
|
832
|
+
if (!options.strictContract) {
|
|
833
|
+
intentArgs.push('--no-strict-contract');
|
|
834
|
+
}
|
|
835
|
+
const intentResult = runScript({
|
|
836
|
+
label: 'interactive-intent-build',
|
|
837
|
+
scriptPath: SCRIPT_INTENT,
|
|
838
|
+
args: intentArgs,
|
|
839
|
+
cwd,
|
|
840
|
+
allowedExitCodes: [0]
|
|
841
|
+
});
|
|
842
|
+
const intentPayload = parseJsonOutput(intentResult.stdout, 'interactive-intent-build');
|
|
843
|
+
steps.push(buildStep('intent', intentPayload, buildCommandString(SCRIPT_INTENT, intentArgs), intentResult.exit_code));
|
|
844
|
+
|
|
845
|
+
const planArgs = [
|
|
846
|
+
'--intent', resolvePath(cwd, artifacts.intent_json),
|
|
847
|
+
'--context', contextPath,
|
|
848
|
+
'--execution-mode', options.executionMode,
|
|
849
|
+
'--out-plan', resolvePath(cwd, artifacts.plan_json),
|
|
850
|
+
'--out-markdown', resolvePath(cwd, artifacts.plan_md),
|
|
851
|
+
'--json'
|
|
852
|
+
];
|
|
853
|
+
const planResult = runScript({
|
|
854
|
+
label: 'interactive-plan-build',
|
|
855
|
+
scriptPath: SCRIPT_PLAN,
|
|
856
|
+
args: planArgs,
|
|
857
|
+
cwd,
|
|
858
|
+
allowedExitCodes: [0]
|
|
859
|
+
});
|
|
860
|
+
const planPayload = parseJsonOutput(planResult.stdout, 'interactive-plan-build');
|
|
861
|
+
steps.push(buildStep('plan', planPayload, buildCommandString(SCRIPT_PLAN, planArgs), planResult.exit_code));
|
|
862
|
+
|
|
863
|
+
const gateArgs = [
|
|
864
|
+
'--plan', resolvePath(cwd, artifacts.plan_json),
|
|
865
|
+
'--out', resolvePath(cwd, artifacts.gate_json),
|
|
866
|
+
'--markdown-out', resolvePath(cwd, artifacts.gate_md),
|
|
867
|
+
'--json'
|
|
868
|
+
];
|
|
869
|
+
if (options.policy) {
|
|
870
|
+
gateArgs.push('--policy', resolvePath(cwd, options.policy));
|
|
871
|
+
}
|
|
872
|
+
if (options.catalog) {
|
|
873
|
+
gateArgs.push('--catalog', resolvePath(cwd, options.catalog));
|
|
874
|
+
}
|
|
875
|
+
const gateResult = runScript({
|
|
876
|
+
label: 'interactive-change-plan-gate',
|
|
877
|
+
scriptPath: SCRIPT_GATE,
|
|
878
|
+
args: gateArgs,
|
|
879
|
+
cwd,
|
|
880
|
+
allowedExitCodes: [0]
|
|
881
|
+
});
|
|
882
|
+
const gatePayload = parseJsonOutput(gateResult.stdout, 'interactive-change-plan-gate');
|
|
883
|
+
steps.push(buildStep('gate', gatePayload, buildCommandString(SCRIPT_GATE, gateArgs), gateResult.exit_code));
|
|
884
|
+
|
|
885
|
+
const gateDecision = gatePayload && gatePayload.decision ? gatePayload.decision : 'deny';
|
|
886
|
+
const riskLevel = planPayload && planPayload.plan ? planPayload.plan.risk_level : null;
|
|
887
|
+
|
|
888
|
+
const runtimeArgs = [
|
|
889
|
+
'--plan', resolvePath(cwd, artifacts.plan_json),
|
|
890
|
+
'--ui-mode', options.uiMode,
|
|
891
|
+
'--runtime-mode', options.runtimeMode,
|
|
892
|
+
'--runtime-environment', options.runtimeEnvironment,
|
|
893
|
+
'--out', resolvePath(cwd, artifacts.runtime_json),
|
|
894
|
+
'--json'
|
|
895
|
+
];
|
|
896
|
+
if (options.runtimePolicy) {
|
|
897
|
+
runtimeArgs.push('--policy', resolvePath(cwd, options.runtimePolicy));
|
|
898
|
+
}
|
|
899
|
+
const runtimeResult = runScript({
|
|
900
|
+
label: 'interactive-runtime-policy-evaluate',
|
|
901
|
+
scriptPath: SCRIPT_RUNTIME,
|
|
902
|
+
args: runtimeArgs,
|
|
903
|
+
cwd,
|
|
904
|
+
allowedExitCodes: [0]
|
|
905
|
+
});
|
|
906
|
+
const runtimePayload = parseJsonOutput(runtimeResult.stdout, 'interactive-runtime-policy-evaluate');
|
|
907
|
+
steps.push(buildStep(
|
|
908
|
+
'runtime',
|
|
909
|
+
runtimePayload,
|
|
910
|
+
buildCommandString(SCRIPT_RUNTIME, runtimeArgs),
|
|
911
|
+
runtimeResult.exit_code
|
|
912
|
+
));
|
|
913
|
+
const runtimeDecision = runtimePayload && runtimePayload.decision ? runtimePayload.decision : 'deny';
|
|
914
|
+
const runtimeRequirements = runtimePayload && runtimePayload.requirements && typeof runtimePayload.requirements === 'object'
|
|
915
|
+
? runtimePayload.requirements
|
|
916
|
+
: {};
|
|
917
|
+
const runtimeViolationCodes = Array.isArray(runtimePayload && runtimePayload.violations)
|
|
918
|
+
? runtimePayload.violations
|
|
919
|
+
.map(item => `${item && item.code ? item.code : ''}`.trim().toLowerCase())
|
|
920
|
+
.filter(Boolean)
|
|
921
|
+
: [];
|
|
922
|
+
const runtimeUiModeViolationCodes = runtimeViolationCodes
|
|
923
|
+
.filter(code => code.startsWith('ui-mode-'));
|
|
924
|
+
const runtimeSignalRecord = {
|
|
925
|
+
event_type: 'interactive.runtime.policy.evaluated',
|
|
926
|
+
timestamp: new Date().toISOString(),
|
|
927
|
+
session_id: sessionId,
|
|
928
|
+
user_id: options.userId,
|
|
929
|
+
business_mode: options.businessMode,
|
|
930
|
+
ui_mode: runtimePayload && runtimePayload.ui_mode ? runtimePayload.ui_mode : options.uiMode,
|
|
931
|
+
dialogue_profile: options.dialogueProfile,
|
|
932
|
+
execution_mode: options.executionMode,
|
|
933
|
+
runtime_mode: runtimePayload && runtimePayload.runtime_mode ? runtimePayload.runtime_mode : options.runtimeMode,
|
|
934
|
+
runtime_environment: runtimePayload && runtimePayload.runtime_environment
|
|
935
|
+
? runtimePayload.runtime_environment
|
|
936
|
+
: options.runtimeEnvironment,
|
|
937
|
+
decision: runtimeDecision,
|
|
938
|
+
reasons: Array.isArray(runtimePayload && runtimePayload.reasons)
|
|
939
|
+
? runtimePayload.reasons
|
|
940
|
+
: [],
|
|
941
|
+
violation_codes: runtimeViolationCodes,
|
|
942
|
+
ui_mode_violation: runtimeUiModeViolationCodes.length > 0,
|
|
943
|
+
ui_mode_violation_codes: runtimeUiModeViolationCodes
|
|
944
|
+
};
|
|
945
|
+
const runtimeSignalsGlobalPath = resolvePath(cwd, artifacts.runtime_signals_global_jsonl);
|
|
946
|
+
await fs.ensureDir(path.dirname(runtimeSignalsGlobalPath));
|
|
947
|
+
await fs.appendFile(runtimeSignalsGlobalPath, `${JSON.stringify(runtimeSignalRecord)}\n`, 'utf8');
|
|
948
|
+
const runtimeSignalsSessionPath = resolvePath(cwd, artifacts.runtime_signals_jsonl);
|
|
949
|
+
await fs.ensureDir(path.dirname(runtimeSignalsSessionPath));
|
|
950
|
+
await fs.appendFile(runtimeSignalsSessionPath, `${JSON.stringify(runtimeSignalRecord)}\n`, 'utf8');
|
|
951
|
+
|
|
952
|
+
const authorizationTierArgs = [
|
|
953
|
+
'--execution-mode', options.executionMode,
|
|
954
|
+
'--dialogue-profile', options.dialogueProfile,
|
|
955
|
+
'--runtime-mode', options.runtimeMode,
|
|
956
|
+
'--runtime-environment', options.runtimeEnvironment,
|
|
957
|
+
'--out', resolvePath(cwd, artifacts.authorization_tier_json),
|
|
958
|
+
'--json'
|
|
959
|
+
];
|
|
960
|
+
if (options.autoExecuteLowRisk) {
|
|
961
|
+
authorizationTierArgs.push('--auto-execute-low-risk');
|
|
962
|
+
}
|
|
963
|
+
if (options.liveApply) {
|
|
964
|
+
authorizationTierArgs.push('--live-apply');
|
|
965
|
+
}
|
|
966
|
+
if (options.authorizationTierPolicy) {
|
|
967
|
+
authorizationTierArgs.push('--policy', resolvePath(cwd, options.authorizationTierPolicy));
|
|
968
|
+
}
|
|
969
|
+
const authorizationTierResult = runScript({
|
|
970
|
+
label: 'interactive-authorization-tier-evaluate',
|
|
971
|
+
scriptPath: SCRIPT_AUTHORIZATION_TIER,
|
|
972
|
+
args: authorizationTierArgs,
|
|
973
|
+
cwd,
|
|
974
|
+
allowedExitCodes: [0]
|
|
975
|
+
});
|
|
976
|
+
const authorizationTierPayload = parseJsonOutput(authorizationTierResult.stdout, 'interactive-authorization-tier-evaluate');
|
|
977
|
+
steps.push(buildStep(
|
|
978
|
+
'authorization_tier',
|
|
979
|
+
authorizationTierPayload,
|
|
980
|
+
buildCommandString(SCRIPT_AUTHORIZATION_TIER, authorizationTierArgs),
|
|
981
|
+
authorizationTierResult.exit_code
|
|
982
|
+
));
|
|
983
|
+
const authorizationTierDecision = authorizationTierPayload && authorizationTierPayload.decision
|
|
984
|
+
? authorizationTierPayload.decision
|
|
985
|
+
: 'deny';
|
|
986
|
+
const authorizationTierRequirements = authorizationTierPayload &&
|
|
987
|
+
authorizationTierPayload.requirements &&
|
|
988
|
+
typeof authorizationTierPayload.requirements === 'object'
|
|
989
|
+
? authorizationTierPayload.requirements
|
|
990
|
+
: {};
|
|
991
|
+
const authorizationTierSignalRecord = {
|
|
992
|
+
event_type: 'interactive.authorization_tier.evaluated',
|
|
993
|
+
timestamp: new Date().toISOString(),
|
|
994
|
+
session_id: sessionId,
|
|
995
|
+
user_id: options.userId,
|
|
996
|
+
business_mode: options.businessMode,
|
|
997
|
+
execution_mode: options.executionMode,
|
|
998
|
+
runtime_mode: options.runtimeMode,
|
|
999
|
+
runtime_environment: options.runtimeEnvironment,
|
|
1000
|
+
dialogue_profile: options.dialogueProfile,
|
|
1001
|
+
decision: authorizationTierDecision,
|
|
1002
|
+
reasons: Array.isArray(authorizationTierPayload && authorizationTierPayload.reasons)
|
|
1003
|
+
? authorizationTierPayload.reasons
|
|
1004
|
+
: [],
|
|
1005
|
+
requirements: authorizationTierRequirements,
|
|
1006
|
+
policy_source: authorizationTierPayload &&
|
|
1007
|
+
authorizationTierPayload.policy &&
|
|
1008
|
+
authorizationTierPayload.policy.source
|
|
1009
|
+
? authorizationTierPayload.policy.source
|
|
1010
|
+
: null
|
|
1011
|
+
};
|
|
1012
|
+
const authorizationTierSignalsGlobalPath = resolvePath(cwd, artifacts.authorization_tier_signals_global_jsonl);
|
|
1013
|
+
await fs.ensureDir(path.dirname(authorizationTierSignalsGlobalPath));
|
|
1014
|
+
await fs.appendFile(authorizationTierSignalsGlobalPath, `${JSON.stringify(authorizationTierSignalRecord)}\n`, 'utf8');
|
|
1015
|
+
const authorizationTierSignalsSessionPath = resolvePath(cwd, artifacts.authorization_tier_signals_jsonl);
|
|
1016
|
+
await fs.ensureDir(path.dirname(authorizationTierSignalsSessionPath));
|
|
1017
|
+
await fs.appendFile(authorizationTierSignalsSessionPath, `${JSON.stringify(authorizationTierSignalRecord)}\n`, 'utf8');
|
|
1018
|
+
|
|
1019
|
+
const approvalInitArgs = [
|
|
1020
|
+
'--action', 'init',
|
|
1021
|
+
'--plan', resolvePath(cwd, artifacts.plan_json),
|
|
1022
|
+
'--state-file', resolvePath(cwd, artifacts.approval_state_json),
|
|
1023
|
+
'--audit-file', resolvePath(cwd, artifacts.approval_audit_jsonl),
|
|
1024
|
+
'--actor', options.approvalActor,
|
|
1025
|
+
'--comment', 'interactive loop init',
|
|
1026
|
+
'--force',
|
|
1027
|
+
'--json'
|
|
1028
|
+
];
|
|
1029
|
+
if (options.approvalActorRole) {
|
|
1030
|
+
approvalInitArgs.push('--actor-role', options.approvalActorRole);
|
|
1031
|
+
}
|
|
1032
|
+
if (options.approvalRolePolicy) {
|
|
1033
|
+
approvalInitArgs.push('--role-policy', resolvePath(cwd, options.approvalRolePolicy));
|
|
1034
|
+
}
|
|
1035
|
+
if (options.authPasswordHash) {
|
|
1036
|
+
approvalInitArgs.push('--password-hash', options.authPasswordHash);
|
|
1037
|
+
}
|
|
1038
|
+
if (options.authPasswordEnv) {
|
|
1039
|
+
approvalInitArgs.push('--password-hash-env', options.authPasswordEnv);
|
|
1040
|
+
}
|
|
1041
|
+
const approvalInitResult = runScript({
|
|
1042
|
+
label: 'interactive-approval-workflow:init',
|
|
1043
|
+
scriptPath: SCRIPT_APPROVAL,
|
|
1044
|
+
args: approvalInitArgs,
|
|
1045
|
+
cwd,
|
|
1046
|
+
allowedExitCodes: [0]
|
|
1047
|
+
});
|
|
1048
|
+
const approvalInitPayload = parseJsonOutput(approvalInitResult.stdout, 'interactive-approval-workflow:init');
|
|
1049
|
+
steps.push(buildStep(
|
|
1050
|
+
'approval_init',
|
|
1051
|
+
approvalInitPayload,
|
|
1052
|
+
buildCommandString(SCRIPT_APPROVAL, approvalInitArgs, ['--password-hash']),
|
|
1053
|
+
approvalInitResult.exit_code
|
|
1054
|
+
));
|
|
1055
|
+
|
|
1056
|
+
if (!options.skipSubmit) {
|
|
1057
|
+
const approvalSubmitArgs = [
|
|
1058
|
+
'--action', 'submit',
|
|
1059
|
+
'--state-file', resolvePath(cwd, artifacts.approval_state_json),
|
|
1060
|
+
'--audit-file', resolvePath(cwd, artifacts.approval_audit_jsonl),
|
|
1061
|
+
'--actor', options.approvalActor,
|
|
1062
|
+
'--comment', 'interactive loop submit',
|
|
1063
|
+
'--json'
|
|
1064
|
+
];
|
|
1065
|
+
if (options.approvalActorRole) {
|
|
1066
|
+
approvalSubmitArgs.push('--actor-role', options.approvalActorRole);
|
|
1067
|
+
}
|
|
1068
|
+
const approvalSubmitResult = runScript({
|
|
1069
|
+
label: 'interactive-approval-workflow:submit',
|
|
1070
|
+
scriptPath: SCRIPT_APPROVAL,
|
|
1071
|
+
args: approvalSubmitArgs,
|
|
1072
|
+
cwd,
|
|
1073
|
+
allowedExitCodes: [0]
|
|
1074
|
+
});
|
|
1075
|
+
const approvalSubmitPayload = parseJsonOutput(approvalSubmitResult.stdout, 'interactive-approval-workflow:submit');
|
|
1076
|
+
steps.push(buildStep('approval_submit', approvalSubmitPayload, buildCommandString(SCRIPT_APPROVAL, approvalSubmitArgs), approvalSubmitResult.exit_code));
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
const canAutoLowRisk = shouldAutoLowRisk({
|
|
1080
|
+
gateDecision,
|
|
1081
|
+
riskLevel,
|
|
1082
|
+
dialogueDecision,
|
|
1083
|
+
runtimeDecision,
|
|
1084
|
+
runtimeAutoExecuteAllowed: runtimeRequirements.auto_execute_allowed === true,
|
|
1085
|
+
authorizationTierDecision,
|
|
1086
|
+
authorizationTierAutoExecuteAllowed: authorizationTierRequirements.auto_execute_allowed === true
|
|
1087
|
+
});
|
|
1088
|
+
if (options.autoExecuteLowRisk && !canAutoLowRisk && `${authorizationTierDecision || ''}`.trim().toLowerCase() !== 'allow') {
|
|
1089
|
+
steps.push(buildStep(
|
|
1090
|
+
'authorization_apply_guard',
|
|
1091
|
+
{
|
|
1092
|
+
decision: 'blocked',
|
|
1093
|
+
reason: `authorization tier decision is "${authorizationTierDecision}"`,
|
|
1094
|
+
dialogue_profile: options.dialogueProfile,
|
|
1095
|
+
runtime_environment: options.runtimeEnvironment
|
|
1096
|
+
},
|
|
1097
|
+
'authorization-tier-guard',
|
|
1098
|
+
2
|
|
1099
|
+
));
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
if (options.autoApproveLowRisk && canAutoLowRisk) {
|
|
1103
|
+
const approvalApproveArgs = [
|
|
1104
|
+
'--action', 'approve',
|
|
1105
|
+
'--state-file', resolvePath(cwd, artifacts.approval_state_json),
|
|
1106
|
+
'--audit-file', resolvePath(cwd, artifacts.approval_audit_jsonl),
|
|
1107
|
+
'--actor', options.approverActor,
|
|
1108
|
+
'--comment', 'auto approve low-risk allow plan',
|
|
1109
|
+
'--json'
|
|
1110
|
+
];
|
|
1111
|
+
if (options.approverActorRole) {
|
|
1112
|
+
approvalApproveArgs.push('--actor-role', options.approverActorRole);
|
|
1113
|
+
}
|
|
1114
|
+
const approvalApproveResult = runScript({
|
|
1115
|
+
label: 'interactive-approval-workflow:approve',
|
|
1116
|
+
scriptPath: SCRIPT_APPROVAL,
|
|
1117
|
+
args: approvalApproveArgs,
|
|
1118
|
+
cwd,
|
|
1119
|
+
allowedExitCodes: [0]
|
|
1120
|
+
});
|
|
1121
|
+
const approvalApprovePayload = parseJsonOutput(approvalApproveResult.stdout, 'interactive-approval-workflow:approve');
|
|
1122
|
+
steps.push(buildStep('approval_approve_auto', approvalApprovePayload, buildCommandString(SCRIPT_APPROVAL, approvalApproveArgs), approvalApproveResult.exit_code));
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
let execution = {
|
|
1126
|
+
attempted: false,
|
|
1127
|
+
auto_triggered: false,
|
|
1128
|
+
blocked: false,
|
|
1129
|
+
result: null,
|
|
1130
|
+
decision: null,
|
|
1131
|
+
reason: null,
|
|
1132
|
+
execution_id: null,
|
|
1133
|
+
payload: null,
|
|
1134
|
+
exit_code: null
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
if (options.autoExecuteLowRisk && canAutoLowRisk) {
|
|
1138
|
+
let runtimeExecutionBlockReason = null;
|
|
1139
|
+
if (options.liveApply && runtimeRequirements.allow_live_apply !== true) {
|
|
1140
|
+
runtimeExecutionBlockReason = `runtime environment "${options.runtimeEnvironment}" does not allow live apply`;
|
|
1141
|
+
} else if (
|
|
1142
|
+
options.liveApply &&
|
|
1143
|
+
!options.dryRun &&
|
|
1144
|
+
runtimeRequirements.require_dry_run_before_live_apply === true
|
|
1145
|
+
) {
|
|
1146
|
+
runtimeExecutionBlockReason = `runtime environment "${options.runtimeEnvironment}" requires dry-run before live apply`;
|
|
1147
|
+
} else if (authorizationTierRequirements.require_password_for_apply === true && !options.authPassword) {
|
|
1148
|
+
runtimeExecutionBlockReason = `authorization tier requires one-time password for apply in "${options.runtimeEnvironment}"`;
|
|
1149
|
+
} else if (authorizationTierRequirements.require_role_policy === true && !options.approvalRolePolicy) {
|
|
1150
|
+
runtimeExecutionBlockReason = `authorization tier requires approval role policy for apply in "${options.runtimeEnvironment}"`;
|
|
1151
|
+
} else if (
|
|
1152
|
+
authorizationTierRequirements.require_distinct_actor_roles === true &&
|
|
1153
|
+
options.approvalActorRole &&
|
|
1154
|
+
options.approverActorRole &&
|
|
1155
|
+
options.approvalActorRole === options.approverActorRole
|
|
1156
|
+
) {
|
|
1157
|
+
runtimeExecutionBlockReason = 'authorization tier requires distinct approval actor role and approver actor role';
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
if (runtimeExecutionBlockReason) {
|
|
1161
|
+
execution = {
|
|
1162
|
+
attempted: true,
|
|
1163
|
+
auto_triggered: true,
|
|
1164
|
+
blocked: true,
|
|
1165
|
+
result: 'blocked',
|
|
1166
|
+
decision: 'runtime-blocked',
|
|
1167
|
+
reason: runtimeExecutionBlockReason,
|
|
1168
|
+
execution_id: null,
|
|
1169
|
+
payload: {
|
|
1170
|
+
mode: 'interactive-runtime-policy-evaluate',
|
|
1171
|
+
decision: 'blocked',
|
|
1172
|
+
reason: runtimeExecutionBlockReason
|
|
1173
|
+
},
|
|
1174
|
+
exit_code: 2
|
|
1175
|
+
};
|
|
1176
|
+
steps.push(buildStep(
|
|
1177
|
+
'runtime_apply_guard',
|
|
1178
|
+
{
|
|
1179
|
+
decision: 'blocked',
|
|
1180
|
+
reason: runtimeExecutionBlockReason,
|
|
1181
|
+
runtime_mode: options.runtimeMode,
|
|
1182
|
+
runtime_environment: options.runtimeEnvironment
|
|
1183
|
+
},
|
|
1184
|
+
'runtime-guard',
|
|
1185
|
+
2
|
|
1186
|
+
));
|
|
1187
|
+
} else {
|
|
1188
|
+
const approvalExecuteArgs = [
|
|
1189
|
+
'--action', 'execute',
|
|
1190
|
+
'--state-file', resolvePath(cwd, artifacts.approval_state_json),
|
|
1191
|
+
'--audit-file', resolvePath(cwd, artifacts.approval_audit_jsonl),
|
|
1192
|
+
'--actor', options.approverActor,
|
|
1193
|
+
'--comment', 'auto execute low-risk allow plan',
|
|
1194
|
+
'--json'
|
|
1195
|
+
];
|
|
1196
|
+
if (options.approverActorRole) {
|
|
1197
|
+
approvalExecuteArgs.push('--actor-role', options.approverActorRole);
|
|
1198
|
+
}
|
|
1199
|
+
if (options.authPassword) {
|
|
1200
|
+
approvalExecuteArgs.push('--password', options.authPassword);
|
|
1201
|
+
}
|
|
1202
|
+
const approvalExecuteResult = runScript({
|
|
1203
|
+
label: 'interactive-approval-workflow:execute',
|
|
1204
|
+
scriptPath: SCRIPT_APPROVAL,
|
|
1205
|
+
args: approvalExecuteArgs,
|
|
1206
|
+
cwd,
|
|
1207
|
+
allowedExitCodes: [0, 2]
|
|
1208
|
+
});
|
|
1209
|
+
const approvalExecutePayload = parseJsonOutput(approvalExecuteResult.stdout, 'interactive-approval-workflow:execute');
|
|
1210
|
+
steps.push(buildStep(
|
|
1211
|
+
'approval_execute_auto',
|
|
1212
|
+
approvalExecutePayload,
|
|
1213
|
+
buildCommandString(SCRIPT_APPROVAL, approvalExecuteArgs, ['--password']),
|
|
1214
|
+
approvalExecuteResult.exit_code
|
|
1215
|
+
));
|
|
1216
|
+
|
|
1217
|
+
if (approvalExecuteResult.exit_code === 2 || approvalExecutePayload.decision === 'blocked') {
|
|
1218
|
+
execution = {
|
|
1219
|
+
attempted: true,
|
|
1220
|
+
auto_triggered: true,
|
|
1221
|
+
blocked: true,
|
|
1222
|
+
result: 'blocked',
|
|
1223
|
+
decision: 'approval-blocked',
|
|
1224
|
+
reason: approvalExecutePayload.reason || 'approval execute blocked',
|
|
1225
|
+
execution_id: null,
|
|
1226
|
+
payload: approvalExecutePayload,
|
|
1227
|
+
exit_code: approvalExecuteResult.exit_code
|
|
1228
|
+
};
|
|
1229
|
+
} else {
|
|
1230
|
+
const adapterArgs = [
|
|
1231
|
+
'--action', 'low-risk-apply',
|
|
1232
|
+
'--plan', resolvePath(cwd, artifacts.plan_json),
|
|
1233
|
+
'--out', resolvePath(cwd, artifacts.adapter_json),
|
|
1234
|
+
'--json'
|
|
1235
|
+
];
|
|
1236
|
+
if (options.policy) {
|
|
1237
|
+
adapterArgs.push('--policy', resolvePath(cwd, options.policy));
|
|
1238
|
+
}
|
|
1239
|
+
if (options.catalog) {
|
|
1240
|
+
adapterArgs.push('--catalog', resolvePath(cwd, options.catalog));
|
|
1241
|
+
}
|
|
1242
|
+
if (options.moquiConfig) {
|
|
1243
|
+
adapterArgs.push('--moqui-config', resolvePath(cwd, options.moquiConfig));
|
|
1244
|
+
}
|
|
1245
|
+
if (options.liveApply) {
|
|
1246
|
+
adapterArgs.push('--live-apply');
|
|
1247
|
+
}
|
|
1248
|
+
if (!options.dryRun) {
|
|
1249
|
+
adapterArgs.push('--no-dry-run');
|
|
1250
|
+
}
|
|
1251
|
+
if (options.allowSuggestionApply) {
|
|
1252
|
+
adapterArgs.push('--allow-suggestion-apply');
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
const adapterResult = runScript({
|
|
1256
|
+
label: 'interactive-moqui-adapter:low-risk-apply',
|
|
1257
|
+
scriptPath: SCRIPT_ADAPTER,
|
|
1258
|
+
args: adapterArgs,
|
|
1259
|
+
cwd,
|
|
1260
|
+
allowedExitCodes: [0, 2]
|
|
1261
|
+
});
|
|
1262
|
+
const adapterPayload = parseJsonOutput(adapterResult.stdout, 'interactive-moqui-adapter:low-risk-apply');
|
|
1263
|
+
const record = adapterPayload &&
|
|
1264
|
+
adapterPayload.payload &&
|
|
1265
|
+
adapterPayload.payload.execution_record &&
|
|
1266
|
+
typeof adapterPayload.payload.execution_record === 'object'
|
|
1267
|
+
? adapterPayload.payload.execution_record
|
|
1268
|
+
: null;
|
|
1269
|
+
|
|
1270
|
+
execution = {
|
|
1271
|
+
attempted: true,
|
|
1272
|
+
auto_triggered: true,
|
|
1273
|
+
blocked: adapterResult.exit_code === 2,
|
|
1274
|
+
result: record ? record.result : null,
|
|
1275
|
+
decision: adapterPayload && adapterPayload.payload ? adapterPayload.payload.decision : null,
|
|
1276
|
+
reason: adapterPayload && adapterPayload.payload ? adapterPayload.payload.reason || null : null,
|
|
1277
|
+
execution_id: record ? record.execution_id : null,
|
|
1278
|
+
payload: adapterPayload,
|
|
1279
|
+
exit_code: adapterResult.exit_code
|
|
1280
|
+
};
|
|
1281
|
+
steps.push(buildStep('adapter_low_risk_apply_auto', adapterPayload, buildCommandString(SCRIPT_ADAPTER, adapterArgs), adapterResult.exit_code));
|
|
1282
|
+
|
|
1283
|
+
if (adapterResult.exit_code === 0) {
|
|
1284
|
+
const approvalVerifyArgs = [
|
|
1285
|
+
'--action', 'verify',
|
|
1286
|
+
'--state-file', resolvePath(cwd, artifacts.approval_state_json),
|
|
1287
|
+
'--audit-file', resolvePath(cwd, artifacts.approval_audit_jsonl),
|
|
1288
|
+
'--actor', options.approverActor,
|
|
1289
|
+
'--comment', 'auto verify after successful low-risk apply',
|
|
1290
|
+
'--json'
|
|
1291
|
+
];
|
|
1292
|
+
if (options.approverActorRole) {
|
|
1293
|
+
approvalVerifyArgs.push('--actor-role', options.approverActorRole);
|
|
1294
|
+
}
|
|
1295
|
+
const approvalVerifyResult = runScript({
|
|
1296
|
+
label: 'interactive-approval-workflow:verify',
|
|
1297
|
+
scriptPath: SCRIPT_APPROVAL,
|
|
1298
|
+
args: approvalVerifyArgs,
|
|
1299
|
+
cwd,
|
|
1300
|
+
allowedExitCodes: [0, 2]
|
|
1301
|
+
});
|
|
1302
|
+
const approvalVerifyPayload = parseJsonOutput(approvalVerifyResult.stdout, 'interactive-approval-workflow:verify');
|
|
1303
|
+
steps.push(buildStep('approval_verify_auto', approvalVerifyPayload, buildCommandString(SCRIPT_APPROVAL, approvalVerifyArgs), approvalVerifyResult.exit_code));
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
const approvalStatusArgs = [
|
|
1310
|
+
'--action', 'status',
|
|
1311
|
+
'--state-file', resolvePath(cwd, artifacts.approval_state_json),
|
|
1312
|
+
'--audit-file', resolvePath(cwd, artifacts.approval_audit_jsonl),
|
|
1313
|
+
'--json'
|
|
1314
|
+
];
|
|
1315
|
+
const approvalStatusResult = runScript({
|
|
1316
|
+
label: 'interactive-approval-workflow:status',
|
|
1317
|
+
scriptPath: SCRIPT_APPROVAL,
|
|
1318
|
+
args: approvalStatusArgs,
|
|
1319
|
+
cwd,
|
|
1320
|
+
allowedExitCodes: [0]
|
|
1321
|
+
});
|
|
1322
|
+
const approvalStatusPayload = parseJsonOutput(approvalStatusResult.stdout, 'interactive-approval-workflow:status');
|
|
1323
|
+
steps.push(buildStep('approval_status', approvalStatusPayload, buildCommandString(SCRIPT_APPROVAL, approvalStatusArgs), approvalStatusResult.exit_code));
|
|
1324
|
+
|
|
1325
|
+
const approvalState = approvalStatusPayload && approvalStatusPayload.state ? approvalStatusPayload.state : {};
|
|
1326
|
+
const approvalStatus = approvalState && approvalState.status ? approvalState.status : null;
|
|
1327
|
+
const intentRecord = intentPayload && intentPayload.intent && typeof intentPayload.intent === 'object'
|
|
1328
|
+
? intentPayload.intent
|
|
1329
|
+
: {};
|
|
1330
|
+
const planRecord = planPayload && planPayload.plan && typeof planPayload.plan === 'object'
|
|
1331
|
+
? planPayload.plan
|
|
1332
|
+
: {};
|
|
1333
|
+
const contextRef = intentRecord.context_ref && typeof intentRecord.context_ref === 'object'
|
|
1334
|
+
? intentRecord.context_ref
|
|
1335
|
+
: {};
|
|
1336
|
+
|
|
1337
|
+
const feedback = {
|
|
1338
|
+
requested: options.feedbackScore != null,
|
|
1339
|
+
logged: false,
|
|
1340
|
+
score: options.feedbackScore,
|
|
1341
|
+
feedback_id: null,
|
|
1342
|
+
global_file: artifacts.feedback_global_jsonl,
|
|
1343
|
+
session_file: artifacts.feedback_jsonl,
|
|
1344
|
+
payload: null,
|
|
1345
|
+
exit_code: null
|
|
1346
|
+
};
|
|
1347
|
+
|
|
1348
|
+
if (options.feedbackScore != null) {
|
|
1349
|
+
const feedbackArgs = [
|
|
1350
|
+
'--score', String(options.feedbackScore),
|
|
1351
|
+
'--user-id', options.userId,
|
|
1352
|
+
'--session-id', sessionId,
|
|
1353
|
+
'--feedback-file', globalFeedbackPath,
|
|
1354
|
+
'--channel', options.feedbackChannel,
|
|
1355
|
+
'--json'
|
|
1356
|
+
];
|
|
1357
|
+
if (options.feedbackComment) {
|
|
1358
|
+
feedbackArgs.push('--comment', options.feedbackComment);
|
|
1359
|
+
}
|
|
1360
|
+
if (options.feedbackTags.length > 0) {
|
|
1361
|
+
feedbackArgs.push('--tags', options.feedbackTags.join(','));
|
|
1362
|
+
}
|
|
1363
|
+
if (intentRecord.intent_id) {
|
|
1364
|
+
feedbackArgs.push('--intent-id', intentRecord.intent_id);
|
|
1365
|
+
}
|
|
1366
|
+
if (planRecord.plan_id) {
|
|
1367
|
+
feedbackArgs.push('--plan-id', planRecord.plan_id);
|
|
1368
|
+
}
|
|
1369
|
+
if (execution.execution_id) {
|
|
1370
|
+
feedbackArgs.push('--execution-id', execution.execution_id);
|
|
1371
|
+
}
|
|
1372
|
+
if (contextRef.product) {
|
|
1373
|
+
feedbackArgs.push('--product', `${contextRef.product}`);
|
|
1374
|
+
}
|
|
1375
|
+
if (contextRef.module) {
|
|
1376
|
+
feedbackArgs.push('--module', `${contextRef.module}`);
|
|
1377
|
+
}
|
|
1378
|
+
if (contextRef.page) {
|
|
1379
|
+
feedbackArgs.push('--page', `${contextRef.page}`);
|
|
1380
|
+
}
|
|
1381
|
+
if (contextRef.scene_id) {
|
|
1382
|
+
feedbackArgs.push('--scene-id', `${contextRef.scene_id}`);
|
|
1383
|
+
}
|
|
1384
|
+
const feedbackResult = runScript({
|
|
1385
|
+
label: 'interactive-feedback-log',
|
|
1386
|
+
scriptPath: SCRIPT_FEEDBACK,
|
|
1387
|
+
args: feedbackArgs,
|
|
1388
|
+
cwd,
|
|
1389
|
+
allowedExitCodes: [0]
|
|
1390
|
+
});
|
|
1391
|
+
const feedbackPayload = parseJsonOutput(feedbackResult.stdout, 'interactive-feedback-log');
|
|
1392
|
+
const feedbackRecord = feedbackPayload && feedbackPayload.record && typeof feedbackPayload.record === 'object'
|
|
1393
|
+
? feedbackPayload.record
|
|
1394
|
+
: null;
|
|
1395
|
+
if (feedbackRecord) {
|
|
1396
|
+
await fs.ensureDir(path.dirname(resolvePath(cwd, artifacts.feedback_jsonl)));
|
|
1397
|
+
await fs.appendFile(
|
|
1398
|
+
resolvePath(cwd, artifacts.feedback_jsonl),
|
|
1399
|
+
`${JSON.stringify(feedbackRecord)}\n`,
|
|
1400
|
+
'utf8'
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
steps.push(buildStep('feedback_log', feedbackPayload, buildCommandString(SCRIPT_FEEDBACK, feedbackArgs), feedbackResult.exit_code));
|
|
1404
|
+
feedback.logged = true;
|
|
1405
|
+
feedback.payload = feedbackPayload;
|
|
1406
|
+
feedback.exit_code = feedbackResult.exit_code;
|
|
1407
|
+
feedback.feedback_id = feedbackRecord
|
|
1408
|
+
? feedbackRecord.feedback_id || null
|
|
1409
|
+
: null;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
const workOrderArgs = [
|
|
1413
|
+
'--plan', resolvePath(cwd, artifacts.plan_json),
|
|
1414
|
+
'--dialogue', resolvePath(cwd, artifacts.dialogue_json),
|
|
1415
|
+
'--intent', resolvePath(cwd, artifacts.intent_json),
|
|
1416
|
+
'--gate', resolvePath(cwd, artifacts.gate_json),
|
|
1417
|
+
'--runtime', resolvePath(cwd, artifacts.runtime_json),
|
|
1418
|
+
'--authorization-tier', resolvePath(cwd, artifacts.authorization_tier_json),
|
|
1419
|
+
'--approval-state', resolvePath(cwd, artifacts.approval_state_json),
|
|
1420
|
+
'--session-id', sessionId,
|
|
1421
|
+
'--runtime-mode', options.runtimeMode,
|
|
1422
|
+
'--runtime-environment', options.runtimeEnvironment,
|
|
1423
|
+
'--out', resolvePath(cwd, artifacts.work_order_json),
|
|
1424
|
+
'--markdown-out', resolvePath(cwd, artifacts.work_order_md),
|
|
1425
|
+
'--json'
|
|
1426
|
+
];
|
|
1427
|
+
if (options.goal) {
|
|
1428
|
+
workOrderArgs.push('--goal', options.goal);
|
|
1429
|
+
}
|
|
1430
|
+
if (execution.attempted) {
|
|
1431
|
+
workOrderArgs.push('--execution-attempted');
|
|
1432
|
+
}
|
|
1433
|
+
if (execution.blocked) {
|
|
1434
|
+
workOrderArgs.push('--execution-blocked');
|
|
1435
|
+
}
|
|
1436
|
+
if (execution.result) {
|
|
1437
|
+
workOrderArgs.push('--execution-result', execution.result);
|
|
1438
|
+
}
|
|
1439
|
+
if (execution.reason) {
|
|
1440
|
+
workOrderArgs.push('--execution-reason', execution.reason);
|
|
1441
|
+
}
|
|
1442
|
+
if (execution.execution_id) {
|
|
1443
|
+
workOrderArgs.push('--execution-id', execution.execution_id);
|
|
1444
|
+
}
|
|
1445
|
+
const workOrderResult = runScript({
|
|
1446
|
+
label: 'interactive-work-order-build',
|
|
1447
|
+
scriptPath: SCRIPT_WORK_ORDER,
|
|
1448
|
+
args: workOrderArgs,
|
|
1449
|
+
cwd,
|
|
1450
|
+
allowedExitCodes: [0]
|
|
1451
|
+
});
|
|
1452
|
+
const workOrderPayload = parseJsonOutput(workOrderResult.stdout, 'interactive-work-order-build');
|
|
1453
|
+
steps.push(buildStep(
|
|
1454
|
+
'work_order',
|
|
1455
|
+
workOrderPayload,
|
|
1456
|
+
buildCommandString(SCRIPT_WORK_ORDER, workOrderArgs),
|
|
1457
|
+
workOrderResult.exit_code
|
|
1458
|
+
));
|
|
1459
|
+
|
|
1460
|
+
const summaryStatus = buildSummaryStatus({
|
|
1461
|
+
dialogueDecision,
|
|
1462
|
+
gateDecision,
|
|
1463
|
+
runtimeDecision,
|
|
1464
|
+
authorizationTierDecision,
|
|
1465
|
+
executionAttempted: execution.attempted,
|
|
1466
|
+
executionBlocked: execution.blocked,
|
|
1467
|
+
executionResult: execution.result
|
|
1468
|
+
});
|
|
1469
|
+
const executionBlockReasonCategory = detectExecutionBlockReasonCategory(execution);
|
|
1470
|
+
const executionBlockRemediationHint = buildExecutionBlockRemediationHint({
|
|
1471
|
+
blockReasonCategory: executionBlockReasonCategory,
|
|
1472
|
+
executionReason: execution.reason
|
|
1473
|
+
});
|
|
1474
|
+
|
|
1475
|
+
const payload = {
|
|
1476
|
+
mode: 'interactive-customization-loop',
|
|
1477
|
+
generated_at: new Date().toISOString(),
|
|
1478
|
+
session_id: sessionId,
|
|
1479
|
+
input: {
|
|
1480
|
+
context: toRelative(cwd, contextPath),
|
|
1481
|
+
goal: options.goal || null,
|
|
1482
|
+
goal_file: options.goalFile ? toRelative(cwd, resolvePath(cwd, options.goalFile)) : null,
|
|
1483
|
+
user_id: options.userId,
|
|
1484
|
+
execution_mode: options.executionMode
|
|
1485
|
+
},
|
|
1486
|
+
options: {
|
|
1487
|
+
business_mode: options.businessMode,
|
|
1488
|
+
business_mode_policy: options.businessModeState
|
|
1489
|
+
? toRelative(cwd, options.businessModeState.policy_path)
|
|
1490
|
+
: toRelative(cwd, resolvePath(cwd, options.businessModePolicy)),
|
|
1491
|
+
business_mode_policy_source: options.businessModeState ? options.businessModeState.policy_source : 'unknown',
|
|
1492
|
+
allow_mode_override: options.allowModeOverride === true,
|
|
1493
|
+
policy: options.policy ? toRelative(cwd, resolvePath(cwd, options.policy)) : null,
|
|
1494
|
+
catalog: options.catalog ? toRelative(cwd, resolvePath(cwd, options.catalog)) : null,
|
|
1495
|
+
moqui_config: options.moquiConfig ? toRelative(cwd, resolvePath(cwd, options.moquiConfig)) : null,
|
|
1496
|
+
approval_actor: options.approvalActor,
|
|
1497
|
+
approval_actor_role: options.approvalActorRole,
|
|
1498
|
+
approver_actor: options.approverActor,
|
|
1499
|
+
approver_actor_role: options.approverActorRole,
|
|
1500
|
+
approval_role_policy: options.approvalRolePolicy ? toRelative(cwd, resolvePath(cwd, options.approvalRolePolicy)) : null,
|
|
1501
|
+
skip_submit: options.skipSubmit,
|
|
1502
|
+
auto_approve_low_risk: options.autoApproveLowRisk,
|
|
1503
|
+
auto_execute_low_risk: options.autoExecuteLowRisk,
|
|
1504
|
+
allow_suggestion_apply: options.allowSuggestionApply,
|
|
1505
|
+
live_apply: options.liveApply,
|
|
1506
|
+
dry_run: options.dryRun,
|
|
1507
|
+
feedback_score: options.feedbackScore,
|
|
1508
|
+
feedback_comment: options.feedbackComment,
|
|
1509
|
+
feedback_tags: options.feedbackTags,
|
|
1510
|
+
feedback_channel: options.feedbackChannel,
|
|
1511
|
+
dialogue_policy: options.dialoguePolicy ? toRelative(cwd, resolvePath(cwd, options.dialoguePolicy)) : null,
|
|
1512
|
+
dialogue_profile: options.dialogueProfile,
|
|
1513
|
+
ui_mode: options.uiMode,
|
|
1514
|
+
runtime_mode: options.runtimeMode,
|
|
1515
|
+
runtime_environment: options.runtimeEnvironment,
|
|
1516
|
+
runtime_policy: options.runtimePolicy ? toRelative(cwd, resolvePath(cwd, options.runtimePolicy)) : null,
|
|
1517
|
+
authorization_tier_policy: options.authorizationTierPolicy
|
|
1518
|
+
? toRelative(cwd, resolvePath(cwd, options.authorizationTierPolicy))
|
|
1519
|
+
: null,
|
|
1520
|
+
auth_password_hash: options.authPasswordHash ? '***' : null,
|
|
1521
|
+
auth_password_env: options.authPasswordEnv || DEFAULT_AUTH_PASSWORD_HASH_ENV
|
|
1522
|
+
},
|
|
1523
|
+
dialogue: {
|
|
1524
|
+
decision: dialogueDecision,
|
|
1525
|
+
profile: dialoguePayload && dialoguePayload.policy ? dialoguePayload.policy.active_profile || null : null,
|
|
1526
|
+
ui_mode: dialoguePayload &&
|
|
1527
|
+
dialoguePayload.authorization_dialogue &&
|
|
1528
|
+
dialoguePayload.authorization_dialogue.context &&
|
|
1529
|
+
dialoguePayload.authorization_dialogue.context.ui_mode
|
|
1530
|
+
? dialoguePayload.authorization_dialogue.context.ui_mode
|
|
1531
|
+
: options.uiMode,
|
|
1532
|
+
reasons: Array.isArray(dialoguePayload && dialoguePayload.reasons) ? dialoguePayload.reasons : [],
|
|
1533
|
+
clarification_questions: Array.isArray(dialoguePayload && dialoguePayload.clarification_questions)
|
|
1534
|
+
? dialoguePayload.clarification_questions
|
|
1535
|
+
: [],
|
|
1536
|
+
authorization_dialogue: dialoguePayload &&
|
|
1537
|
+
dialoguePayload.authorization_dialogue &&
|
|
1538
|
+
typeof dialoguePayload.authorization_dialogue === 'object'
|
|
1539
|
+
? dialoguePayload.authorization_dialogue
|
|
1540
|
+
: null
|
|
1541
|
+
},
|
|
1542
|
+
gate: {
|
|
1543
|
+
decision: gateDecision,
|
|
1544
|
+
risk_level: riskLevel,
|
|
1545
|
+
reasons: Array.isArray(gatePayload && gatePayload.reasons) ? gatePayload.reasons : []
|
|
1546
|
+
},
|
|
1547
|
+
runtime: {
|
|
1548
|
+
decision: runtimeDecision,
|
|
1549
|
+
ui_mode: runtimePayload && runtimePayload.ui_mode ? runtimePayload.ui_mode : options.uiMode,
|
|
1550
|
+
mode: runtimePayload && runtimePayload.runtime_mode ? runtimePayload.runtime_mode : options.runtimeMode,
|
|
1551
|
+
environment: runtimePayload && runtimePayload.runtime_environment ? runtimePayload.runtime_environment : options.runtimeEnvironment,
|
|
1552
|
+
reasons: Array.isArray(runtimePayload && runtimePayload.reasons) ? runtimePayload.reasons : [],
|
|
1553
|
+
violations: Array.isArray(runtimePayload && runtimePayload.violations) ? runtimePayload.violations : [],
|
|
1554
|
+
requirements: runtimeRequirements
|
|
1555
|
+
},
|
|
1556
|
+
authorization_tier: {
|
|
1557
|
+
decision: authorizationTierDecision,
|
|
1558
|
+
profile: authorizationTierPayload &&
|
|
1559
|
+
authorizationTierPayload.context &&
|
|
1560
|
+
authorizationTierPayload.context.dialogue_profile
|
|
1561
|
+
? authorizationTierPayload.context.dialogue_profile
|
|
1562
|
+
: options.dialogueProfile,
|
|
1563
|
+
runtime_environment: authorizationTierPayload &&
|
|
1564
|
+
authorizationTierPayload.context &&
|
|
1565
|
+
authorizationTierPayload.context.runtime_environment
|
|
1566
|
+
? authorizationTierPayload.context.runtime_environment
|
|
1567
|
+
: options.runtimeEnvironment,
|
|
1568
|
+
reasons: Array.isArray(authorizationTierPayload && authorizationTierPayload.reasons)
|
|
1569
|
+
? authorizationTierPayload.reasons
|
|
1570
|
+
: [],
|
|
1571
|
+
requirements: authorizationTierRequirements
|
|
1572
|
+
},
|
|
1573
|
+
approval: {
|
|
1574
|
+
workflow_id: approvalState.workflow_id || null,
|
|
1575
|
+
status: approvalStatus,
|
|
1576
|
+
approval_required: approvalState.approval_required === true,
|
|
1577
|
+
approvals: approvalState.approvals || {},
|
|
1578
|
+
authorization: approvalStatusPayload && approvalStatusPayload.authorization
|
|
1579
|
+
? approvalStatusPayload.authorization
|
|
1580
|
+
: {}
|
|
1581
|
+
},
|
|
1582
|
+
execution,
|
|
1583
|
+
summary: {
|
|
1584
|
+
status: summaryStatus,
|
|
1585
|
+
business_mode: options.businessMode,
|
|
1586
|
+
ui_mode: options.uiMode,
|
|
1587
|
+
dialogue_authorization_decision: dialoguePayload &&
|
|
1588
|
+
dialoguePayload.authorization_dialogue &&
|
|
1589
|
+
dialoguePayload.authorization_dialogue.decision
|
|
1590
|
+
? dialoguePayload.authorization_dialogue.decision
|
|
1591
|
+
: null,
|
|
1592
|
+
authorization_tier_decision: authorizationTierDecision,
|
|
1593
|
+
execution_block_reason_category: executionBlockReasonCategory,
|
|
1594
|
+
execution_block_remediation_hint: executionBlockRemediationHint,
|
|
1595
|
+
next_actions: buildNextActions({
|
|
1596
|
+
dialogueDecision,
|
|
1597
|
+
gateDecision,
|
|
1598
|
+
runtimeDecision,
|
|
1599
|
+
authorizationTierDecision,
|
|
1600
|
+
riskLevel,
|
|
1601
|
+
runtime: {
|
|
1602
|
+
ui_mode: runtimePayload && runtimePayload.ui_mode ? runtimePayload.ui_mode : options.uiMode,
|
|
1603
|
+
mode: runtimePayload && runtimePayload.runtime_mode ? runtimePayload.runtime_mode : options.runtimeMode,
|
|
1604
|
+
environment: runtimePayload && runtimePayload.runtime_environment ? runtimePayload.runtime_environment : options.runtimeEnvironment
|
|
1605
|
+
},
|
|
1606
|
+
authorizationTier: {
|
|
1607
|
+
profile: authorizationTierPayload &&
|
|
1608
|
+
authorizationTierPayload.context &&
|
|
1609
|
+
authorizationTierPayload.context.dialogue_profile
|
|
1610
|
+
? authorizationTierPayload.context.dialogue_profile
|
|
1611
|
+
: options.dialogueProfile,
|
|
1612
|
+
requirements: authorizationTierRequirements
|
|
1613
|
+
},
|
|
1614
|
+
autoExecuteTriggered: execution.auto_triggered === true,
|
|
1615
|
+
executionPayload: execution.payload && execution.payload.payload ? execution.payload.payload : null,
|
|
1616
|
+
executionBlockReasonCategory,
|
|
1617
|
+
executionReason: execution.reason,
|
|
1618
|
+
approvalStatus,
|
|
1619
|
+
feedbackLogged: feedback.logged,
|
|
1620
|
+
artifacts
|
|
1621
|
+
})
|
|
1622
|
+
},
|
|
1623
|
+
work_order: workOrderPayload && workOrderPayload.work_order
|
|
1624
|
+
? workOrderPayload.work_order
|
|
1625
|
+
: null,
|
|
1626
|
+
feedback,
|
|
1627
|
+
artifacts,
|
|
1628
|
+
steps
|
|
1629
|
+
};
|
|
1630
|
+
|
|
1631
|
+
await fs.ensureDir(path.dirname(summaryOutPath));
|
|
1632
|
+
await fs.writeJson(summaryOutPath, payload, { spaces: 2 });
|
|
1633
|
+
|
|
1634
|
+
if (options.json) {
|
|
1635
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
1636
|
+
} else {
|
|
1637
|
+
process.stdout.write(`Interactive customization loop completed: ${payload.summary.status}\n`);
|
|
1638
|
+
process.stdout.write(`- Session: ${sessionId}\n`);
|
|
1639
|
+
process.stdout.write(`- Gate: ${gateDecision}\n`);
|
|
1640
|
+
process.stdout.write(`- Runtime: ${runtimeDecision}\n`);
|
|
1641
|
+
process.stdout.write(`- Summary: ${toRelative(cwd, summaryOutPath)}\n`);
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
if (options.failOnGateDeny && `${gateDecision}`.trim().toLowerCase() === 'deny') {
|
|
1645
|
+
process.exitCode = 2;
|
|
1646
|
+
} else if (options.failOnDialogueDeny && `${dialogueDecision}`.trim().toLowerCase() === 'deny') {
|
|
1647
|
+
process.exitCode = 2;
|
|
1648
|
+
} else if (options.failOnGateNonAllow && `${gateDecision}`.trim().toLowerCase() !== 'allow') {
|
|
1649
|
+
process.exitCode = 2;
|
|
1650
|
+
} else if (options.failOnRuntimeNonAllow && `${runtimeDecision}`.trim().toLowerCase() !== 'allow') {
|
|
1651
|
+
process.exitCode = 2;
|
|
1652
|
+
} else if (options.failOnExecuteBlocked && execution.attempted && execution.blocked) {
|
|
1653
|
+
process.exitCode = 2;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
if (require.main === module) {
|
|
1658
|
+
main().catch((error) => {
|
|
1659
|
+
console.error(`Interactive customization loop failed: ${error.message}`);
|
|
1660
|
+
process.exit(1);
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
module.exports = {
|
|
1665
|
+
DEFAULT_OUT_DIR,
|
|
1666
|
+
DEFAULT_USER_ID,
|
|
1667
|
+
DEFAULT_APPROVAL_ACTOR,
|
|
1668
|
+
DEFAULT_FEEDBACK_CHANNEL,
|
|
1669
|
+
DEFAULT_RUNTIME_MODE,
|
|
1670
|
+
DEFAULT_RUNTIME_ENVIRONMENT,
|
|
1671
|
+
DEFAULT_DIALOGUE_PROFILE,
|
|
1672
|
+
FEEDBACK_CHANNELS,
|
|
1673
|
+
RUNTIME_MODES,
|
|
1674
|
+
RUNTIME_ENVIRONMENTS,
|
|
1675
|
+
DIALOGUE_PROFILES,
|
|
1676
|
+
parseArgs,
|
|
1677
|
+
resolvePath,
|
|
1678
|
+
normalizeSessionId,
|
|
1679
|
+
parseJsonOutput,
|
|
1680
|
+
buildCommandString,
|
|
1681
|
+
runScript,
|
|
1682
|
+
toRelative,
|
|
1683
|
+
buildStep,
|
|
1684
|
+
shouldAutoLowRisk,
|
|
1685
|
+
buildSummaryStatus,
|
|
1686
|
+
detectExecutionBlockReasonCategory,
|
|
1687
|
+
buildExecutionBlockRemediationHint,
|
|
1688
|
+
buildNextActions,
|
|
1689
|
+
main
|
|
1690
|
+
};
|