@way_marks/server 0.9.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/server.js +598 -0
- package/dist/approval/manager.js +249 -0
- package/dist/db/database.js +754 -185
- package/dist/escalation/manager.js +205 -0
- package/dist/notifications/service.js +457 -0
- package/dist/policies/engine.js +11 -5
- package/dist/policy/engine.js +420 -0
- package/dist/remediation/recommender.js +309 -0
- package/dist/risk/analyzer.js +453 -0
- package/dist/rollback/blocker.js +222 -0
- package/dist/rollback/manager.js +245 -0
- package/package.json +1 -1
- package/src/ui/index.html +862 -0
- package/dist/approvals/handler.test.js +0 -172
- package/dist/policies/engine.test.js +0 -241
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Remediation Recommender — Phase 4C
|
|
4
|
+
*
|
|
5
|
+
* Suggests safe rollback strategies:
|
|
6
|
+
* - Partial rollback (safe operations only)
|
|
7
|
+
* - Staged rollback (phases with verification)
|
|
8
|
+
* - Retry strategy (don't rollback, retry)
|
|
9
|
+
* - Workaround strategy (data fixes instead)
|
|
10
|
+
* - Escalation (manual expert review)
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.getRemediations = getRemediations;
|
|
14
|
+
/**
|
|
15
|
+
* Get remediation recommendations based on risk assessment and policy violations
|
|
16
|
+
*/
|
|
17
|
+
function getRemediations(session, actions, riskAssessment, policyViolations = []) {
|
|
18
|
+
const strategies = [];
|
|
19
|
+
// Analyze action types
|
|
20
|
+
const writeActions = actions.filter(a => a.tool_name === 'write_file');
|
|
21
|
+
const deleteActions = actions.filter(a => a.tool_name === 'delete_file');
|
|
22
|
+
const bashActions = actions.filter(a => a.tool_name === 'bash');
|
|
23
|
+
const readActions = actions.filter(a => a.tool_name === 'read_file' || a.tool_name === 'grep');
|
|
24
|
+
const errorActions = actions.filter(a => a.status === 'error');
|
|
25
|
+
// Strategy 1: Partial Rollback (safest for mixed operations)
|
|
26
|
+
const partialStrategy = createPartialRollbackStrategy(actions, riskAssessment);
|
|
27
|
+
strategies.push(partialStrategy);
|
|
28
|
+
// Strategy 2: Staged Rollback (safe for large scales)
|
|
29
|
+
if (actions.length > 10) {
|
|
30
|
+
const stagedStrategy = createStagedRollbackStrategy(actions, riskAssessment);
|
|
31
|
+
strategies.push(stagedStrategy);
|
|
32
|
+
}
|
|
33
|
+
// Strategy 3: Retry (if transient errors)
|
|
34
|
+
if (errorActions.length > 0 && hasTransientErrors(errorActions)) {
|
|
35
|
+
const retryStrategy = createRetryStrategy(actions, errorActions);
|
|
36
|
+
strategies.push(retryStrategy);
|
|
37
|
+
}
|
|
38
|
+
// Strategy 4: Workaround (for data issues)
|
|
39
|
+
if (writeActions.length > 0 && deleteActions.length === 0) {
|
|
40
|
+
const workaroundStrategy = createWorkaroundStrategy(actions);
|
|
41
|
+
strategies.push(workaroundStrategy);
|
|
42
|
+
}
|
|
43
|
+
// Strategy 5: Escalation (high risk or policy violations)
|
|
44
|
+
if (riskAssessment.score > 6 || policyViolations.length > 0) {
|
|
45
|
+
const escalationStrategy = createEscalationStrategy(actions, riskAssessment, policyViolations);
|
|
46
|
+
strategies.push(escalationStrategy);
|
|
47
|
+
}
|
|
48
|
+
// Determine primary strategy
|
|
49
|
+
const primary = selectPrimaryStrategy(strategies, riskAssessment, errorActions.length);
|
|
50
|
+
const alternatives = strategies.filter(s => s.name !== primary.name);
|
|
51
|
+
// Determine required approvals
|
|
52
|
+
const requiredApprovals = determineRequiredApprovals(riskAssessment, policyViolations, actions);
|
|
53
|
+
// Calculate overall metrics
|
|
54
|
+
const estimatedSafety = Math.round((100 - riskAssessment.score * 10) + primary.success_probability);
|
|
55
|
+
const estimatedDowntime = calculateDowntime(primary);
|
|
56
|
+
const reasoning = generateReasoning(riskAssessment, errorActions, deleteActions, policyViolations);
|
|
57
|
+
return {
|
|
58
|
+
primary_strategy: primary,
|
|
59
|
+
alternative_strategies: alternatives.slice(0, 2), // Top 2 alternatives
|
|
60
|
+
estimated_safety: Math.max(0, Math.min(100, estimatedSafety)),
|
|
61
|
+
estimated_downtime: estimatedDowntime,
|
|
62
|
+
requires_manual_review: primary.name === 'escalation' || riskAssessment.score > 7,
|
|
63
|
+
required_approvals: requiredApprovals,
|
|
64
|
+
reasoning,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create partial rollback strategy (safe operations only)
|
|
69
|
+
*/
|
|
70
|
+
function createPartialRollbackStrategy(actions, riskAssessment) {
|
|
71
|
+
// Safe operations: read, info gathering
|
|
72
|
+
const safeOps = actions.filter(a => ['read_file', 'grep', 'find_files'].includes(a.tool_name));
|
|
73
|
+
// Risky operations: write, delete, bash
|
|
74
|
+
const riskyOps = actions.filter(a => a.status !== 'success' || ['write_file', 'delete_file', 'bash'].includes(a.tool_name));
|
|
75
|
+
const successProb = Math.max(50, 100 - riskAssessment.score * 5);
|
|
76
|
+
return {
|
|
77
|
+
name: 'partial_rollback',
|
|
78
|
+
description: 'Rollback safe read-only and information gathering operations only; exclude write/delete/bash',
|
|
79
|
+
safe_operations: safeOps.map(a => a.action_id),
|
|
80
|
+
risky_operations: riskyOps.map(a => a.action_id),
|
|
81
|
+
retry_instead: false,
|
|
82
|
+
manual_steps: [
|
|
83
|
+
'Review list of safe operations to rollback',
|
|
84
|
+
'Verify no dependencies on risky operations',
|
|
85
|
+
'Execute rollback of safe operations',
|
|
86
|
+
'Manually address risky operations as needed',
|
|
87
|
+
],
|
|
88
|
+
estimated_time: `${Math.ceil(safeOps.length / 5)} minutes`,
|
|
89
|
+
success_probability: successProb,
|
|
90
|
+
estimated_downtime: '5-15 minutes',
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Create staged rollback strategy (phases with verification)
|
|
95
|
+
*/
|
|
96
|
+
function createStagedRollbackStrategy(actions, riskAssessment) {
|
|
97
|
+
const stageSize = Math.ceil(actions.length / 3); // 3 stages
|
|
98
|
+
const stages = [];
|
|
99
|
+
for (let i = 0; i < 3; i++) {
|
|
100
|
+
const start = i * stageSize;
|
|
101
|
+
const end = Math.min(start + stageSize, actions.length);
|
|
102
|
+
stages.push(`${i + 1}. Rollback actions ${start + 1}-${end}`);
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
name: 'staged_rollback',
|
|
106
|
+
description: `Rollback in ${stages.length} phases with health verification between stages`,
|
|
107
|
+
safe_operations: actions.map(a => a.action_id),
|
|
108
|
+
risky_operations: [],
|
|
109
|
+
retry_instead: false,
|
|
110
|
+
manual_steps: [
|
|
111
|
+
...stages,
|
|
112
|
+
'Verify system health after each stage',
|
|
113
|
+
'Check error rates and key metrics',
|
|
114
|
+
'Proceed to next stage or rollback if issues detected',
|
|
115
|
+
],
|
|
116
|
+
estimated_time: `${stages.length * 30} minutes`,
|
|
117
|
+
success_probability: Math.max(65, 100 - riskAssessment.score * 3),
|
|
118
|
+
estimated_downtime: '1-2 hours',
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Create retry strategy (don't rollback, try again)
|
|
123
|
+
*/
|
|
124
|
+
function createRetryStrategy(actions, errorActions) {
|
|
125
|
+
const failedTools = [...new Set(errorActions.map(a => a.tool_name))];
|
|
126
|
+
return {
|
|
127
|
+
name: 'retry',
|
|
128
|
+
description: 'Instead of rolling back, retry failed operations with same or adjusted parameters',
|
|
129
|
+
safe_operations: [],
|
|
130
|
+
risky_operations: [],
|
|
131
|
+
retry_instead: true,
|
|
132
|
+
manual_steps: [
|
|
133
|
+
'Review errors from failed operations',
|
|
134
|
+
`Failed tools: ${failedTools.join(', ')}`,
|
|
135
|
+
'Identify root cause (timeout, resource, permissions)',
|
|
136
|
+
'Retry with same parameters or adjusted timeout',
|
|
137
|
+
'Monitor retry execution',
|
|
138
|
+
],
|
|
139
|
+
estimated_time: '5-15 minutes',
|
|
140
|
+
success_probability: 60,
|
|
141
|
+
estimated_downtime: 'Minimal (0-5 minutes)',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Create workaround strategy (data fixes instead of rollback)
|
|
146
|
+
*/
|
|
147
|
+
function createWorkaroundStrategy(actions) {
|
|
148
|
+
return {
|
|
149
|
+
name: 'workaround',
|
|
150
|
+
description: 'Apply targeted data fixes instead of full rollback',
|
|
151
|
+
safe_operations: [],
|
|
152
|
+
risky_operations: [],
|
|
153
|
+
retry_instead: false,
|
|
154
|
+
manual_steps: [
|
|
155
|
+
'Identify specific data that needs correction',
|
|
156
|
+
'Create targeted fix script (minimal scope)',
|
|
157
|
+
'Test fix on staging environment',
|
|
158
|
+
'Apply fix to production with DBA oversight',
|
|
159
|
+
'Verify data consistency post-fix',
|
|
160
|
+
],
|
|
161
|
+
estimated_time: '30-60 minutes',
|
|
162
|
+
success_probability: 75,
|
|
163
|
+
estimated_downtime: '5-20 minutes',
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Create escalation strategy (manual expert review)
|
|
168
|
+
*/
|
|
169
|
+
function createEscalationStrategy(actions, riskAssessment, policyViolations) {
|
|
170
|
+
const reasons = [];
|
|
171
|
+
if (riskAssessment.score > 7) {
|
|
172
|
+
reasons.push(`High risk score (${riskAssessment.score.toFixed(1)}/10)`);
|
|
173
|
+
}
|
|
174
|
+
if (policyViolations.length > 0) {
|
|
175
|
+
const categories = [...new Set(policyViolations.map(v => v.category))];
|
|
176
|
+
reasons.push(`Policy violations in: ${categories.join(', ')}`);
|
|
177
|
+
}
|
|
178
|
+
if (actions.some(a => a.tool_name === 'delete_file')) {
|
|
179
|
+
reasons.push('Delete operations involved (data loss risk)');
|
|
180
|
+
}
|
|
181
|
+
if (actions.length > 20) {
|
|
182
|
+
reasons.push('Large number of actions (complex scenario)');
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
name: 'escalation',
|
|
186
|
+
description: 'Request expert manual review and guidance on safe remediation approach',
|
|
187
|
+
safe_operations: [],
|
|
188
|
+
risky_operations: [],
|
|
189
|
+
retry_instead: false,
|
|
190
|
+
manual_steps: [
|
|
191
|
+
'Escalate to on-call expert/DBA',
|
|
192
|
+
'Provide risk assessment and policy violations',
|
|
193
|
+
'Discuss safe remediation options',
|
|
194
|
+
'Expert recommends specific approach',
|
|
195
|
+
'Execute under expert guidance',
|
|
196
|
+
],
|
|
197
|
+
estimated_time: '30+ minutes (waiting for expert)',
|
|
198
|
+
success_probability: 95,
|
|
199
|
+
estimated_downtime: 'Depends on expert recommendation',
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Select primary strategy based on context
|
|
204
|
+
*/
|
|
205
|
+
function selectPrimaryStrategy(strategies, riskAssessment, errorCount) {
|
|
206
|
+
// Escalation if critical risk
|
|
207
|
+
if (riskAssessment.score > 8) {
|
|
208
|
+
const esc = strategies.find(s => s.name === 'escalation');
|
|
209
|
+
if (esc)
|
|
210
|
+
return esc;
|
|
211
|
+
}
|
|
212
|
+
// Retry if many transient errors and no risky operations
|
|
213
|
+
if (errorCount > 0) {
|
|
214
|
+
const retry = strategies.find(s => s.name === 'retry');
|
|
215
|
+
if (retry)
|
|
216
|
+
return retry;
|
|
217
|
+
}
|
|
218
|
+
// Partial rollback if medium risk
|
|
219
|
+
if (riskAssessment.score < 5) {
|
|
220
|
+
const partial = strategies.find(s => s.name === 'partial_rollback');
|
|
221
|
+
if (partial && partial.safe_operations.length > 0)
|
|
222
|
+
return partial;
|
|
223
|
+
}
|
|
224
|
+
// Staged rollback if large scale
|
|
225
|
+
const staged = strategies.find(s => s.name === 'staged_rollback');
|
|
226
|
+
if (staged)
|
|
227
|
+
return staged;
|
|
228
|
+
// Partial as fallback
|
|
229
|
+
const partial = strategies.find(s => s.name === 'partial_rollback');
|
|
230
|
+
if (partial)
|
|
231
|
+
return partial;
|
|
232
|
+
// Escalation as last resort
|
|
233
|
+
return strategies.find(s => s.name === 'escalation') || strategies[0];
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Determine required approvals
|
|
237
|
+
*/
|
|
238
|
+
function determineRequiredApprovals(riskAssessment, policyViolations, actions) {
|
|
239
|
+
const approvals = [];
|
|
240
|
+
if (riskAssessment.score > 7) {
|
|
241
|
+
approvals.push('cto');
|
|
242
|
+
}
|
|
243
|
+
else if (riskAssessment.score > 5) {
|
|
244
|
+
approvals.push('engineering_lead');
|
|
245
|
+
}
|
|
246
|
+
const categories = new Set(policyViolations.map(v => v.category));
|
|
247
|
+
if (categories.has('data')) {
|
|
248
|
+
approvals.push('dba');
|
|
249
|
+
}
|
|
250
|
+
if (categories.has('security')) {
|
|
251
|
+
approvals.push('security_lead');
|
|
252
|
+
}
|
|
253
|
+
if (categories.has('compliance')) {
|
|
254
|
+
approvals.push('compliance_officer');
|
|
255
|
+
}
|
|
256
|
+
if (actions.some(a => a.tool_name === 'delete_file')) {
|
|
257
|
+
approvals.push('data_steward');
|
|
258
|
+
}
|
|
259
|
+
return [...new Set(approvals)]; // Deduplicate
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Check if errors are transient (retryable)
|
|
263
|
+
*/
|
|
264
|
+
function hasTransientErrors(errorActions) {
|
|
265
|
+
const transientPatterns = ['timeout', 'network', 'connection', 'temporary', 'unavailable'];
|
|
266
|
+
return errorActions.some(a => {
|
|
267
|
+
const errorMsg = (a.error_message || '').toLowerCase();
|
|
268
|
+
return transientPatterns.some(p => errorMsg.includes(p));
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Calculate estimated downtime
|
|
273
|
+
*/
|
|
274
|
+
function calculateDowntime(strategy) {
|
|
275
|
+
const timeMap = {
|
|
276
|
+
partial_rollback: '5-15 minutes',
|
|
277
|
+
staged_rollback: '1-2 hours',
|
|
278
|
+
retry: '5-15 minutes',
|
|
279
|
+
workaround: '15-45 minutes',
|
|
280
|
+
escalation: '30+ minutes',
|
|
281
|
+
};
|
|
282
|
+
return timeMap[strategy.name];
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Generate reasoning for recommendations
|
|
286
|
+
*/
|
|
287
|
+
function generateReasoning(riskAssessment, errorActions, deleteActions, policyViolations) {
|
|
288
|
+
const parts = [];
|
|
289
|
+
parts.push(`Risk Level: ${riskAssessment.level.toUpperCase()} (${riskAssessment.score.toFixed(1)}/10)`);
|
|
290
|
+
if (deleteActions.length > 0) {
|
|
291
|
+
parts.push(`⚠️ Data Risk: ${deleteActions.length} delete operation(s) - data loss possible`);
|
|
292
|
+
}
|
|
293
|
+
if (errorActions.length > 0) {
|
|
294
|
+
parts.push(`Errors: ${errorActions.length} operation(s) failed - review error messages`);
|
|
295
|
+
}
|
|
296
|
+
if (policyViolations.length > 0) {
|
|
297
|
+
parts.push(`Compliance: ${policyViolations.length} policy violation(s)`);
|
|
298
|
+
}
|
|
299
|
+
parts.push(`Recommendation: Use primary strategy, alternatives available`);
|
|
300
|
+
return parts.join('\n');
|
|
301
|
+
}
|
|
302
|
+
exports.default = {
|
|
303
|
+
getRemediations,
|
|
304
|
+
createPartialRollbackStrategy,
|
|
305
|
+
createStagedRollbackStrategy,
|
|
306
|
+
createRetryStrategy,
|
|
307
|
+
createWorkaroundStrategy,
|
|
308
|
+
createEscalationStrategy,
|
|
309
|
+
};
|