@wundr.io/cli 1.0.1 → 1.0.4
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/ai/ai-service.d.ts +152 -0
- package/dist/ai/ai-service.d.ts.map +1 -0
- package/dist/ai/ai-service.js +430 -0
- package/dist/ai/ai-service.js.map +1 -0
- package/dist/ai/claude-client.d.ts +130 -0
- package/dist/ai/claude-client.d.ts.map +1 -0
- package/dist/ai/claude-client.js +340 -0
- package/dist/ai/claude-client.js.map +1 -0
- package/dist/ai/conversation-manager.d.ts +164 -0
- package/dist/ai/conversation-manager.d.ts.map +1 -0
- package/dist/ai/conversation-manager.js +614 -0
- package/dist/ai/conversation-manager.js.map +1 -0
- package/dist/ai/index.d.ts +5 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +8 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/cli.d.ts +36 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +192 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/ai.d.ts +89 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +799 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/alignment.d.ts +78 -0
- package/dist/commands/alignment.d.ts.map +1 -0
- package/dist/commands/alignment.js +817 -0
- package/dist/commands/alignment.js.map +1 -0
- package/dist/commands/analyze-optimized.d.ts +14 -0
- package/dist/commands/analyze-optimized.d.ts.map +1 -0
- package/dist/commands/analyze-optimized.js +600 -0
- package/dist/commands/analyze-optimized.js.map +1 -0
- package/dist/commands/analyze.d.ts +65 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +435 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/batch.d.ts +71 -0
- package/dist/commands/batch.d.ts.map +1 -0
- package/dist/commands/batch.js +738 -0
- package/dist/commands/batch.js.map +1 -0
- package/dist/commands/chat.d.ts +71 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +674 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/claude-init.d.ts +28 -0
- package/dist/commands/claude-init.d.ts.map +1 -0
- package/dist/commands/claude-init.js +591 -0
- package/dist/commands/claude-init.js.map +1 -0
- package/dist/commands/claude-setup.d.ts +119 -0
- package/dist/commands/claude-setup.d.ts.map +1 -0
- package/dist/commands/claude-setup.js +1073 -0
- package/dist/commands/claude-setup.js.map +1 -0
- package/dist/commands/computer-setup-commands.d.ts +53 -0
- package/dist/commands/computer-setup-commands.d.ts.map +1 -0
- package/dist/commands/computer-setup-commands.js +705 -0
- package/dist/commands/computer-setup-commands.js.map +1 -0
- package/dist/commands/computer-setup.d.ts +7 -0
- package/dist/commands/computer-setup.d.ts.map +1 -0
- package/dist/commands/computer-setup.js +849 -0
- package/dist/commands/computer-setup.js.map +1 -0
- package/dist/commands/create-command.d.ts +7 -0
- package/dist/commands/create-command.d.ts.map +1 -0
- package/dist/commands/create-command.js +158 -0
- package/dist/commands/create-command.js.map +1 -0
- package/dist/commands/create.d.ts +74 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +556 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dashboard.d.ts +91 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +538 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/govern.d.ts +70 -0
- package/dist/commands/govern.d.ts.map +1 -0
- package/dist/commands/govern.js +481 -0
- package/dist/commands/govern.js.map +1 -0
- package/dist/commands/governance.d.ts +17 -0
- package/dist/commands/governance.d.ts.map +1 -0
- package/dist/commands/governance.js +703 -0
- package/dist/commands/governance.js.map +1 -0
- package/dist/commands/guardian.d.ts +20 -0
- package/dist/commands/guardian.d.ts.map +1 -0
- package/dist/commands/guardian.js +597 -0
- package/dist/commands/guardian.js.map +1 -0
- package/dist/commands/init.d.ts +59 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +650 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/performance-optimizer.d.ts +30 -0
- package/dist/commands/performance-optimizer.d.ts.map +1 -0
- package/dist/commands/performance-optimizer.js +650 -0
- package/dist/commands/performance-optimizer.js.map +1 -0
- package/dist/commands/plugins.d.ts +87 -0
- package/dist/commands/plugins.d.ts.map +1 -0
- package/dist/commands/plugins.js +685 -0
- package/dist/commands/plugins.js.map +1 -0
- package/dist/commands/rag.d.ts +7 -0
- package/dist/commands/rag.d.ts.map +1 -0
- package/dist/commands/rag.js +748 -0
- package/dist/commands/rag.js.map +1 -0
- package/dist/commands/session.d.ts +41 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +441 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/setup.d.ts +29 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +397 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/test-init.d.ts +9 -0
- package/dist/commands/test-init.d.ts.map +1 -0
- package/dist/commands/test-init.js +222 -0
- package/dist/commands/test-init.js.map +1 -0
- package/dist/commands/test.d.ts +25 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +217 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/vp.d.ts +7 -0
- package/dist/commands/vp.d.ts.map +1 -0
- package/dist/commands/vp.js +571 -0
- package/dist/commands/vp.js.map +1 -0
- package/dist/commands/watch.d.ts +76 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +613 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/commands/worktree.d.ts +63 -0
- package/dist/commands/worktree.d.ts.map +1 -0
- package/dist/commands/worktree.js +774 -0
- package/dist/commands/worktree.js.map +1 -0
- package/dist/context/context-manager.d.ts +155 -0
- package/dist/context/context-manager.d.ts.map +1 -0
- package/dist/context/context-manager.js +383 -0
- package/dist/context/context-manager.js.map +1 -0
- package/dist/context/index.d.ts +3 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +6 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/session-manager.d.ts +207 -0
- package/dist/context/session-manager.d.ts.map +1 -0
- package/dist/context/session-manager.js +686 -0
- package/dist/context/session-manager.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/interactive/interactive-mode.d.ts +76 -0
- package/dist/interactive/interactive-mode.d.ts.map +1 -0
- package/dist/interactive/interactive-mode.js +732 -0
- package/dist/interactive/interactive-mode.js.map +1 -0
- package/dist/nlp/command-mapper.d.ts +174 -0
- package/dist/nlp/command-mapper.d.ts.map +1 -0
- package/dist/nlp/command-mapper.js +624 -0
- package/dist/nlp/command-mapper.js.map +1 -0
- package/dist/nlp/command-parser.d.ts +106 -0
- package/dist/nlp/command-parser.d.ts.map +1 -0
- package/dist/nlp/command-parser.js +417 -0
- package/dist/nlp/command-parser.js.map +1 -0
- package/dist/nlp/index.d.ts +5 -0
- package/dist/nlp/index.d.ts.map +1 -0
- package/dist/nlp/index.js +8 -0
- package/dist/nlp/index.js.map +1 -0
- package/dist/nlp/intent-classifier.d.ts +59 -0
- package/dist/nlp/intent-classifier.d.ts.map +1 -0
- package/dist/nlp/intent-classifier.js +384 -0
- package/dist/nlp/intent-classifier.js.map +1 -0
- package/dist/nlp/intent-parser.d.ts +152 -0
- package/dist/nlp/intent-parser.d.ts.map +1 -0
- package/dist/nlp/intent-parser.js +744 -0
- package/dist/nlp/intent-parser.js.map +1 -0
- package/dist/plugins/plugin-manager.d.ts +120 -0
- package/dist/plugins/plugin-manager.d.ts.map +1 -0
- package/dist/plugins/plugin-manager.js +595 -0
- package/dist/plugins/plugin-manager.js.map +1 -0
- package/dist/types/index.d.ts +224 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/backup-rollback-manager.d.ts +72 -0
- package/dist/utils/backup-rollback-manager.d.ts.map +1 -0
- package/dist/utils/backup-rollback-manager.js +289 -0
- package/dist/utils/backup-rollback-manager.js.map +1 -0
- package/dist/utils/claude-config-installer.d.ts +94 -0
- package/dist/utils/claude-config-installer.d.ts.map +1 -0
- package/dist/utils/claude-config-installer.js +628 -0
- package/dist/utils/claude-config-installer.js.map +1 -0
- package/dist/utils/config-manager.d.ts +73 -0
- package/dist/utils/config-manager.d.ts.map +1 -0
- package/dist/utils/config-manager.js +339 -0
- package/dist/utils/config-manager.js.map +1 -0
- package/dist/utils/error-handler.d.ts +46 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +169 -0
- package/dist/utils/error-handler.js.map +1 -0
- package/dist/utils/logger.d.ts +25 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +105 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +6 -4
- package/src/ai/ai-service.ts +22 -19
- package/src/ai/claude-client.ts +20 -16
- package/src/ai/conversation-manager.ts +37 -30
- package/src/cli.ts +46 -17
- package/src/commands/ai.ts +196 -88
- package/src/commands/alignment.ts +1212 -0
- package/src/commands/analyze-optimized.ts +394 -89
- package/src/commands/analyze.ts +22 -20
- package/src/commands/batch.ts +41 -38
- package/src/commands/chat.ts +37 -34
- package/src/commands/claude-init.ts +38 -30
- package/src/commands/claude-setup.ts +692 -97
- package/src/commands/computer-setup-commands.ts +45 -39
- package/src/commands/computer-setup.ts +490 -20
- package/src/commands/create-command.ts +7 -7
- package/src/commands/create.ts +36 -33
- package/src/commands/dashboard.ts +33 -28
- package/src/commands/govern.ts +34 -29
- package/src/commands/governance.ts +1005 -0
- package/src/commands/guardian.ts +887 -0
- package/src/commands/init.ts +112 -22
- package/src/commands/performance-optimizer.ts +48 -42
- package/src/commands/plugins.ts +35 -32
- package/src/commands/project-update.ts +1053 -0
- package/src/commands/rag.ts +904 -0
- package/src/commands/session.ts +631 -0
- package/src/commands/setup.ts +35 -31
- package/src/commands/test-init.ts +6 -5
- package/src/commands/test.ts +7 -6
- package/src/commands/vp.ts +762 -0
- package/src/commands/watch.ts +44 -33
- package/src/commands/worktree.ts +1057 -0
- package/src/context/context-manager.ts +15 -12
- package/src/context/session-manager.ts +51 -40
- package/src/index.ts +7 -6
- package/src/interactive/interactive-mode.ts +25 -18
- package/src/lib/conflict-resolution.ts +28 -0
- package/src/lib/merge-strategy.ts +28 -0
- package/src/lib/safety-mechanisms.ts +47 -0
- package/src/lib/state-detection.ts +28 -0
- package/src/nlp/command-mapper.ts +35 -30
- package/src/nlp/command-parser.ts +20 -17
- package/src/nlp/intent-classifier.ts +7 -7
- package/src/nlp/intent-parser.ts +61 -49
- package/src/plugins/plugin-manager.ts +27 -23
- package/src/types/index.ts +1 -1
- package/src/types/modules.d.ts +1 -0
- package/src/utils/backup-rollback-manager.ts +13 -11
- package/src/utils/claude-config-installer.ts +18 -16
- package/src/utils/config-manager.ts +12 -9
- package/src/utils/error-handler.ts +5 -3
- package/src/utils/logger.ts +35 -12
|
@@ -0,0 +1,1212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alignment Monitoring CLI Commands
|
|
3
|
+
*
|
|
4
|
+
* Provides commands for monitoring AI agent alignment drift across five dimensions:
|
|
5
|
+
* - Policy Violation Rate
|
|
6
|
+
* - Intent-Outcome Gap
|
|
7
|
+
* - Evaluator Disagreement
|
|
8
|
+
* - Escalation Suppression
|
|
9
|
+
* - Reward Hacking
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as os from 'os';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
import { Command } from 'commander';
|
|
17
|
+
import fs from 'fs-extra';
|
|
18
|
+
import ora from 'ora';
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Types
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
export interface AlignmentDriftMetrics {
|
|
25
|
+
policyViolationRate: number; // % of actions violating constraints (0-1)
|
|
26
|
+
intentOutcomeGap: number; // Divergence from stated goals (0-1)
|
|
27
|
+
evaluatorDisagreement: number; // Human override rate (0-1)
|
|
28
|
+
escalationSuppression: number; // Agents avoiding triggers (0-1)
|
|
29
|
+
rewardHacking: number; // Gaming metrics instances count
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface AlignmentDimension {
|
|
33
|
+
name: string;
|
|
34
|
+
key: keyof AlignmentDriftMetrics;
|
|
35
|
+
threshold: number;
|
|
36
|
+
description: string;
|
|
37
|
+
currentValue: number;
|
|
38
|
+
status: 'healthy' | 'warning' | 'critical';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface AlignmentReport {
|
|
42
|
+
timestamp: string;
|
|
43
|
+
sessionId?: string;
|
|
44
|
+
overallScore: number; // 0-100 (100 = perfect alignment)
|
|
45
|
+
status: 'healthy' | 'warning' | 'critical';
|
|
46
|
+
metrics: AlignmentDriftMetrics;
|
|
47
|
+
dimensions: AlignmentDimension[];
|
|
48
|
+
recommendations: string[];
|
|
49
|
+
history?: AlignmentHistoryEntry[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface AlignmentHistoryEntry {
|
|
53
|
+
timestamp: string;
|
|
54
|
+
sessionId?: string;
|
|
55
|
+
score: number;
|
|
56
|
+
status: 'healthy' | 'warning' | 'critical';
|
|
57
|
+
metrics: AlignmentDriftMetrics;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface AlignmentDebtReport {
|
|
61
|
+
generatedAt: string;
|
|
62
|
+
period: {
|
|
63
|
+
start: string;
|
|
64
|
+
end: string;
|
|
65
|
+
days: number;
|
|
66
|
+
};
|
|
67
|
+
summary: {
|
|
68
|
+
averageScore: number;
|
|
69
|
+
lowestScore: number;
|
|
70
|
+
highestScore: number;
|
|
71
|
+
trend: 'improving' | 'stable' | 'degrading';
|
|
72
|
+
totalViolations: number;
|
|
73
|
+
};
|
|
74
|
+
dimensionAnalysis: {
|
|
75
|
+
[key: string]: {
|
|
76
|
+
average: number;
|
|
77
|
+
max: number;
|
|
78
|
+
exceedances: number;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
criticalEvents: Array<{
|
|
82
|
+
timestamp: string;
|
|
83
|
+
dimension: string;
|
|
84
|
+
value: number;
|
|
85
|
+
threshold: number;
|
|
86
|
+
}>;
|
|
87
|
+
recommendations: string[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface DimensionAnalysisEntry {
|
|
91
|
+
average: number;
|
|
92
|
+
max: number;
|
|
93
|
+
exceedances: number;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// AlignmentDriftDetector - Embedded Implementation
|
|
98
|
+
// ============================================================================
|
|
99
|
+
|
|
100
|
+
class AlignmentDriftDetector {
|
|
101
|
+
private readonly dataDir: string;
|
|
102
|
+
private readonly historyFile: string;
|
|
103
|
+
|
|
104
|
+
// Thresholds from Three-Tier Architecture spec
|
|
105
|
+
private readonly thresholds = {
|
|
106
|
+
policyViolation: 0.005, // >0.5% daily violations triggers alert
|
|
107
|
+
intentOutcomeGap: 0.15, // >15% divergence is concerning
|
|
108
|
+
evaluatorDisagreement: 0.2, // >20% monthly overrides
|
|
109
|
+
escalationSuppression: 0.4, // >40% drop from baseline
|
|
110
|
+
rewardHacking: 5, // >5 instances/month
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
constructor(dataDir?: string) {
|
|
114
|
+
this.dataDir = dataDir || path.join(os.homedir(), '.wundr', 'alignment');
|
|
115
|
+
this.historyFile = path.join(this.dataDir, 'history.json');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async initialize(): Promise<void> {
|
|
119
|
+
await fs.ensureDir(this.dataDir);
|
|
120
|
+
|
|
121
|
+
if (!(await fs.pathExists(this.historyFile))) {
|
|
122
|
+
await fs.writeJson(this.historyFile, { entries: [] }, { spaces: 2 });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async calculateAlignmentDebt(sessionId?: string): Promise<number> {
|
|
127
|
+
const metrics = await this.collectMetrics(sessionId);
|
|
128
|
+
return this.calculateScore(metrics);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async getAlignmentReport(sessionId?: string): Promise<AlignmentReport> {
|
|
132
|
+
await this.initialize();
|
|
133
|
+
|
|
134
|
+
const metrics = await this.collectMetrics(sessionId);
|
|
135
|
+
const score = this.calculateScore(metrics);
|
|
136
|
+
const status = this.determineStatus(score);
|
|
137
|
+
const dimensions = this.analyzeDimensions(metrics);
|
|
138
|
+
const recommendations = this.generateRecommendations(dimensions, status);
|
|
139
|
+
|
|
140
|
+
const report: AlignmentReport = {
|
|
141
|
+
timestamp: new Date().toISOString(),
|
|
142
|
+
sessionId,
|
|
143
|
+
overallScore: score,
|
|
144
|
+
status,
|
|
145
|
+
metrics,
|
|
146
|
+
dimensions,
|
|
147
|
+
recommendations,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Store in history
|
|
151
|
+
await this.storeHistoryEntry({
|
|
152
|
+
timestamp: report.timestamp,
|
|
153
|
+
sessionId,
|
|
154
|
+
score,
|
|
155
|
+
status,
|
|
156
|
+
metrics,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return report;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async getAlignmentScore(sessionId?: string): Promise<{
|
|
163
|
+
score: number;
|
|
164
|
+
status: 'healthy' | 'warning' | 'critical';
|
|
165
|
+
color: 'green' | 'yellow' | 'red';
|
|
166
|
+
}> {
|
|
167
|
+
const score = await this.calculateAlignmentDebt(sessionId);
|
|
168
|
+
const status = this.determineStatus(score);
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
score,
|
|
172
|
+
status,
|
|
173
|
+
color:
|
|
174
|
+
status === 'healthy'
|
|
175
|
+
? 'green'
|
|
176
|
+
: status === 'warning'
|
|
177
|
+
? 'yellow'
|
|
178
|
+
: 'red',
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async getHistory(days: number = 30): Promise<AlignmentHistoryEntry[]> {
|
|
183
|
+
await this.initialize();
|
|
184
|
+
|
|
185
|
+
const history = await this.loadHistory();
|
|
186
|
+
const cutoffDate = new Date();
|
|
187
|
+
cutoffDate.setDate(cutoffDate.getDate() - days);
|
|
188
|
+
|
|
189
|
+
return history.entries
|
|
190
|
+
.filter(
|
|
191
|
+
(entry: AlignmentHistoryEntry) =>
|
|
192
|
+
new Date(entry.timestamp) >= cutoffDate,
|
|
193
|
+
)
|
|
194
|
+
.sort(
|
|
195
|
+
(a: AlignmentHistoryEntry, b: AlignmentHistoryEntry) =>
|
|
196
|
+
new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async getDimensionBreakdown(): Promise<AlignmentDimension[]> {
|
|
201
|
+
const metrics = await this.collectMetrics();
|
|
202
|
+
return this.analyzeDimensions(metrics);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async generateDebtReport(
|
|
206
|
+
days: number = 7,
|
|
207
|
+
sessionId?: string,
|
|
208
|
+
outputFile?: string,
|
|
209
|
+
): Promise<AlignmentDebtReport> {
|
|
210
|
+
await this.initialize();
|
|
211
|
+
|
|
212
|
+
const history = await this.getHistory(days);
|
|
213
|
+
const filteredHistory = sessionId
|
|
214
|
+
? history.filter(h => h.sessionId === sessionId)
|
|
215
|
+
: history;
|
|
216
|
+
|
|
217
|
+
const endDate = new Date();
|
|
218
|
+
const startDate = new Date();
|
|
219
|
+
startDate.setDate(startDate.getDate() - days);
|
|
220
|
+
|
|
221
|
+
// Calculate summary statistics
|
|
222
|
+
const scores = filteredHistory.map(h => h.score);
|
|
223
|
+
const averageScore =
|
|
224
|
+
scores.length > 0
|
|
225
|
+
? scores.reduce((a, b) => a + b, 0) / scores.length
|
|
226
|
+
: 100;
|
|
227
|
+
const lowestScore = scores.length > 0 ? Math.min(...scores) : 100;
|
|
228
|
+
const highestScore = scores.length > 0 ? Math.max(...scores) : 100;
|
|
229
|
+
|
|
230
|
+
// Calculate trend
|
|
231
|
+
let trend: 'improving' | 'stable' | 'degrading' = 'stable';
|
|
232
|
+
if (scores.length >= 2) {
|
|
233
|
+
const firstHalf = scores.slice(0, Math.floor(scores.length / 2));
|
|
234
|
+
const secondHalf = scores.slice(Math.floor(scores.length / 2));
|
|
235
|
+
const firstAvg = firstHalf.reduce((a, b) => a + b, 0) / firstHalf.length;
|
|
236
|
+
const secondAvg =
|
|
237
|
+
secondHalf.reduce((a, b) => a + b, 0) / secondHalf.length;
|
|
238
|
+
|
|
239
|
+
if (secondAvg > firstAvg + 5) {
|
|
240
|
+
trend = 'improving';
|
|
241
|
+
} else if (secondAvg < firstAvg - 5) {
|
|
242
|
+
trend = 'degrading';
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Analyze dimensions across history
|
|
247
|
+
const dimensionAnalysis: AlignmentDebtReport['dimensionAnalysis'] = {};
|
|
248
|
+
const dimensionKeys: (keyof AlignmentDriftMetrics)[] = [
|
|
249
|
+
'policyViolationRate',
|
|
250
|
+
'intentOutcomeGap',
|
|
251
|
+
'evaluatorDisagreement',
|
|
252
|
+
'escalationSuppression',
|
|
253
|
+
'rewardHacking',
|
|
254
|
+
];
|
|
255
|
+
|
|
256
|
+
for (const key of dimensionKeys) {
|
|
257
|
+
const values = filteredHistory.map(h => h.metrics[key]);
|
|
258
|
+
const threshold = this.getThresholdForKey(key);
|
|
259
|
+
|
|
260
|
+
dimensionAnalysis[key] = {
|
|
261
|
+
average:
|
|
262
|
+
values.length > 0
|
|
263
|
+
? values.reduce((a, b) => a + b, 0) / values.length
|
|
264
|
+
: 0,
|
|
265
|
+
max: values.length > 0 ? Math.max(...values) : 0,
|
|
266
|
+
exceedances: values.filter(v => v > threshold).length,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Find critical events
|
|
271
|
+
const criticalEvents: AlignmentDebtReport['criticalEvents'] = [];
|
|
272
|
+
for (const entry of filteredHistory) {
|
|
273
|
+
for (const key of dimensionKeys) {
|
|
274
|
+
const threshold = this.getThresholdForKey(key);
|
|
275
|
+
if (entry.metrics[key] > threshold * 2) {
|
|
276
|
+
criticalEvents.push({
|
|
277
|
+
timestamp: entry.timestamp,
|
|
278
|
+
dimension: key,
|
|
279
|
+
value: entry.metrics[key],
|
|
280
|
+
threshold,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Calculate total violations
|
|
287
|
+
const totalViolations = Object.values(dimensionAnalysis).reduce(
|
|
288
|
+
(sum, dim) => sum + dim.exceedances,
|
|
289
|
+
0,
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// Generate recommendations
|
|
293
|
+
const recommendations = this.generateDebtRecommendations(
|
|
294
|
+
dimensionAnalysis,
|
|
295
|
+
trend,
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
const report: AlignmentDebtReport = {
|
|
299
|
+
generatedAt: new Date().toISOString(),
|
|
300
|
+
period: {
|
|
301
|
+
start: startDate.toISOString(),
|
|
302
|
+
end: endDate.toISOString(),
|
|
303
|
+
days,
|
|
304
|
+
},
|
|
305
|
+
summary: {
|
|
306
|
+
averageScore,
|
|
307
|
+
lowestScore,
|
|
308
|
+
highestScore,
|
|
309
|
+
trend,
|
|
310
|
+
totalViolations,
|
|
311
|
+
},
|
|
312
|
+
dimensionAnalysis,
|
|
313
|
+
criticalEvents: criticalEvents.slice(0, 20),
|
|
314
|
+
recommendations,
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
if (outputFile) {
|
|
318
|
+
await fs.writeJson(outputFile, report, { spaces: 2 });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return report;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
getThresholds(): typeof this.thresholds {
|
|
325
|
+
return { ...this.thresholds };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private async collectMetrics(
|
|
329
|
+
sessionId?: string,
|
|
330
|
+
): Promise<AlignmentDriftMetrics> {
|
|
331
|
+
const sessionMetricsFile = sessionId
|
|
332
|
+
? path.join(this.dataDir, 'sessions', `${sessionId}.json`)
|
|
333
|
+
: path.join(this.dataDir, 'current-metrics.json');
|
|
334
|
+
|
|
335
|
+
if (await fs.pathExists(sessionMetricsFile)) {
|
|
336
|
+
try {
|
|
337
|
+
const data = await fs.readJson(sessionMetricsFile);
|
|
338
|
+
return {
|
|
339
|
+
policyViolationRate: data.policyViolationRate ?? 0,
|
|
340
|
+
intentOutcomeGap: data.intentOutcomeGap ?? 0,
|
|
341
|
+
evaluatorDisagreement: data.evaluatorDisagreement ?? 0,
|
|
342
|
+
escalationSuppression: data.escalationSuppression ?? 0,
|
|
343
|
+
rewardHacking: data.rewardHacking ?? 0,
|
|
344
|
+
};
|
|
345
|
+
} catch {
|
|
346
|
+
// Fall through to defaults
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
policyViolationRate: 0,
|
|
352
|
+
intentOutcomeGap: 0,
|
|
353
|
+
evaluatorDisagreement: 0,
|
|
354
|
+
escalationSuppression: 0,
|
|
355
|
+
rewardHacking: 0,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
private calculateScore(metrics: AlignmentDriftMetrics): number {
|
|
360
|
+
const normalizedPolicyViolation = Math.min(
|
|
361
|
+
metrics.policyViolationRate / this.thresholds.policyViolation,
|
|
362
|
+
2,
|
|
363
|
+
);
|
|
364
|
+
const normalizedIntentGap = Math.min(
|
|
365
|
+
metrics.intentOutcomeGap / this.thresholds.intentOutcomeGap,
|
|
366
|
+
2,
|
|
367
|
+
);
|
|
368
|
+
const normalizedEvaluatorDisagreement = Math.min(
|
|
369
|
+
metrics.evaluatorDisagreement / this.thresholds.evaluatorDisagreement,
|
|
370
|
+
2,
|
|
371
|
+
);
|
|
372
|
+
const normalizedEscalationSuppression = Math.min(
|
|
373
|
+
metrics.escalationSuppression / this.thresholds.escalationSuppression,
|
|
374
|
+
2,
|
|
375
|
+
);
|
|
376
|
+
const normalizedRewardHacking = Math.min(
|
|
377
|
+
metrics.rewardHacking / this.thresholds.rewardHacking,
|
|
378
|
+
2,
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
const weights = {
|
|
382
|
+
policyViolation: 0.25,
|
|
383
|
+
intentOutcomeGap: 0.25,
|
|
384
|
+
evaluatorDisagreement: 0.2,
|
|
385
|
+
escalationSuppression: 0.15,
|
|
386
|
+
rewardHacking: 0.15,
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
const penalty =
|
|
390
|
+
normalizedPolicyViolation * weights.policyViolation +
|
|
391
|
+
normalizedIntentGap * weights.intentOutcomeGap +
|
|
392
|
+
normalizedEvaluatorDisagreement * weights.evaluatorDisagreement +
|
|
393
|
+
normalizedEscalationSuppression * weights.escalationSuppression +
|
|
394
|
+
normalizedRewardHacking * weights.rewardHacking;
|
|
395
|
+
|
|
396
|
+
const score = Math.max(0, Math.min(100, 100 - penalty * 50));
|
|
397
|
+
return Math.round(score * 10) / 10;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private determineStatus(score: number): 'healthy' | 'warning' | 'critical' {
|
|
401
|
+
if (score >= 80) {
|
|
402
|
+
return 'healthy';
|
|
403
|
+
}
|
|
404
|
+
if (score >= 50) {
|
|
405
|
+
return 'warning';
|
|
406
|
+
}
|
|
407
|
+
return 'critical';
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
private analyzeDimensions(
|
|
411
|
+
metrics: AlignmentDriftMetrics,
|
|
412
|
+
): AlignmentDimension[] {
|
|
413
|
+
return [
|
|
414
|
+
{
|
|
415
|
+
name: 'Policy Violation Rate',
|
|
416
|
+
key: 'policyViolationRate',
|
|
417
|
+
threshold: this.thresholds.policyViolation,
|
|
418
|
+
description: 'Percentage of actions violating defined constraints',
|
|
419
|
+
currentValue: metrics.policyViolationRate,
|
|
420
|
+
status: this.getDimensionStatus(
|
|
421
|
+
metrics.policyViolationRate,
|
|
422
|
+
this.thresholds.policyViolation,
|
|
423
|
+
),
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
name: 'Intent-Outcome Gap',
|
|
427
|
+
key: 'intentOutcomeGap',
|
|
428
|
+
threshold: this.thresholds.intentOutcomeGap,
|
|
429
|
+
description: 'Divergence between stated goals and actual outcomes',
|
|
430
|
+
currentValue: metrics.intentOutcomeGap,
|
|
431
|
+
status: this.getDimensionStatus(
|
|
432
|
+
metrics.intentOutcomeGap,
|
|
433
|
+
this.thresholds.intentOutcomeGap,
|
|
434
|
+
),
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
name: 'Evaluator Disagreement',
|
|
438
|
+
key: 'evaluatorDisagreement',
|
|
439
|
+
threshold: this.thresholds.evaluatorDisagreement,
|
|
440
|
+
description: 'Rate of human overrides on agent decisions',
|
|
441
|
+
currentValue: metrics.evaluatorDisagreement,
|
|
442
|
+
status: this.getDimensionStatus(
|
|
443
|
+
metrics.evaluatorDisagreement,
|
|
444
|
+
this.thresholds.evaluatorDisagreement,
|
|
445
|
+
),
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
name: 'Escalation Suppression',
|
|
449
|
+
key: 'escalationSuppression',
|
|
450
|
+
threshold: this.thresholds.escalationSuppression,
|
|
451
|
+
description: 'Drop in escalation triggers compared to baseline',
|
|
452
|
+
currentValue: metrics.escalationSuppression,
|
|
453
|
+
status: this.getDimensionStatus(
|
|
454
|
+
metrics.escalationSuppression,
|
|
455
|
+
this.thresholds.escalationSuppression,
|
|
456
|
+
),
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
name: 'Reward Hacking',
|
|
460
|
+
key: 'rewardHacking',
|
|
461
|
+
threshold: this.thresholds.rewardHacking,
|
|
462
|
+
description: 'Instances of gaming metrics without achieving intent',
|
|
463
|
+
currentValue: metrics.rewardHacking,
|
|
464
|
+
status: this.getDimensionStatus(
|
|
465
|
+
metrics.rewardHacking,
|
|
466
|
+
this.thresholds.rewardHacking,
|
|
467
|
+
),
|
|
468
|
+
},
|
|
469
|
+
];
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
private getDimensionStatus(
|
|
473
|
+
value: number,
|
|
474
|
+
threshold: number,
|
|
475
|
+
): 'healthy' | 'warning' | 'critical' {
|
|
476
|
+
if (value <= threshold) {
|
|
477
|
+
return 'healthy';
|
|
478
|
+
}
|
|
479
|
+
if (value <= threshold * 2) {
|
|
480
|
+
return 'warning';
|
|
481
|
+
}
|
|
482
|
+
return 'critical';
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
private getThresholdForKey(key: keyof AlignmentDriftMetrics): number {
|
|
486
|
+
const thresholdMap: Record<keyof AlignmentDriftMetrics, number> = {
|
|
487
|
+
policyViolationRate: this.thresholds.policyViolation,
|
|
488
|
+
intentOutcomeGap: this.thresholds.intentOutcomeGap,
|
|
489
|
+
evaluatorDisagreement: this.thresholds.evaluatorDisagreement,
|
|
490
|
+
escalationSuppression: this.thresholds.escalationSuppression,
|
|
491
|
+
rewardHacking: this.thresholds.rewardHacking,
|
|
492
|
+
};
|
|
493
|
+
return thresholdMap[key];
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
private generateRecommendations(
|
|
497
|
+
dimensions: AlignmentDimension[],
|
|
498
|
+
status: string,
|
|
499
|
+
): string[] {
|
|
500
|
+
const recommendations: string[] = [];
|
|
501
|
+
|
|
502
|
+
for (const dim of dimensions) {
|
|
503
|
+
if (dim.status === 'critical') {
|
|
504
|
+
recommendations.push(
|
|
505
|
+
`CRITICAL: ${dim.name} at ${this.formatValue(dim.currentValue, dim.key)} exceeds threshold (${this.formatValue(dim.threshold, dim.key)}). Immediate action required.`,
|
|
506
|
+
);
|
|
507
|
+
} else if (dim.status === 'warning') {
|
|
508
|
+
recommendations.push(
|
|
509
|
+
`WARNING: ${dim.name} at ${this.formatValue(dim.currentValue, dim.key)} approaching threshold (${this.formatValue(dim.threshold, dim.key)}). Monitor closely.`,
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (status === 'critical') {
|
|
515
|
+
recommendations.push(
|
|
516
|
+
'Consider pausing autonomous operations until alignment issues are resolved.',
|
|
517
|
+
);
|
|
518
|
+
recommendations.push('Schedule immediate review with Guardian role.');
|
|
519
|
+
} else if (status === 'warning') {
|
|
520
|
+
recommendations.push(
|
|
521
|
+
'Review recent agent decisions for potential alignment drift.',
|
|
522
|
+
);
|
|
523
|
+
recommendations.push(
|
|
524
|
+
'Consider tightening constraints or adding checkpoints.',
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (recommendations.length === 0) {
|
|
529
|
+
recommendations.push(
|
|
530
|
+
'All alignment metrics within acceptable thresholds.',
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return recommendations;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
private generateDebtRecommendations(
|
|
538
|
+
dimensionAnalysis: AlignmentDebtReport['dimensionAnalysis'],
|
|
539
|
+
trend: 'improving' | 'stable' | 'degrading',
|
|
540
|
+
): string[] {
|
|
541
|
+
const recommendations: string[] = [];
|
|
542
|
+
|
|
543
|
+
if (trend === 'degrading') {
|
|
544
|
+
recommendations.push(
|
|
545
|
+
'Alignment score is degrading over time. Review recent changes to agent configurations.',
|
|
546
|
+
);
|
|
547
|
+
} else if (trend === 'improving') {
|
|
548
|
+
recommendations.push(
|
|
549
|
+
'Alignment score is improving. Continue current governance practices.',
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
for (const [key, analysis] of Object.entries(dimensionAnalysis)) {
|
|
554
|
+
if ((analysis as DimensionAnalysisEntry).exceedances > 0) {
|
|
555
|
+
const friendlyName = this.getFriendlyDimensionName(key);
|
|
556
|
+
const entry = analysis as DimensionAnalysisEntry;
|
|
557
|
+
recommendations.push(
|
|
558
|
+
`${friendlyName}: ${entry.exceedances} threshold exceedance(s) detected. Average: ${entry.average.toFixed(3)}`,
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
return recommendations;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
private getFriendlyDimensionName(key: string): string {
|
|
567
|
+
const names: Record<string, string> = {
|
|
568
|
+
policyViolationRate: 'Policy Violation Rate',
|
|
569
|
+
intentOutcomeGap: 'Intent-Outcome Gap',
|
|
570
|
+
evaluatorDisagreement: 'Evaluator Disagreement',
|
|
571
|
+
escalationSuppression: 'Escalation Suppression',
|
|
572
|
+
rewardHacking: 'Reward Hacking',
|
|
573
|
+
};
|
|
574
|
+
return names[key] || key;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
private formatValue(value: number, key: keyof AlignmentDriftMetrics): string {
|
|
578
|
+
if (key === 'rewardHacking') {
|
|
579
|
+
return `${value} instances`;
|
|
580
|
+
}
|
|
581
|
+
return `${(value * 100).toFixed(1)}%`;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
private async storeHistoryEntry(entry: AlignmentHistoryEntry): Promise<void> {
|
|
585
|
+
const history = await this.loadHistory();
|
|
586
|
+
history.entries.push(entry);
|
|
587
|
+
|
|
588
|
+
if (history.entries.length > 1000) {
|
|
589
|
+
history.entries = history.entries.slice(-1000);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
await fs.writeJson(this.historyFile, history, { spaces: 2 });
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
private async loadHistory(): Promise<{ entries: AlignmentHistoryEntry[] }> {
|
|
596
|
+
try {
|
|
597
|
+
if (await fs.pathExists(this.historyFile)) {
|
|
598
|
+
return await fs.readJson(this.historyFile);
|
|
599
|
+
}
|
|
600
|
+
} catch {
|
|
601
|
+
// Fall through to default
|
|
602
|
+
}
|
|
603
|
+
return { entries: [] };
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// ============================================================================
|
|
608
|
+
// Utility Functions
|
|
609
|
+
// ============================================================================
|
|
610
|
+
|
|
611
|
+
function padRight(str: string, length: number): string {
|
|
612
|
+
return str.length >= length
|
|
613
|
+
? str.substring(0, length)
|
|
614
|
+
: str + ' '.repeat(length - str.length);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function getStatusColor(
|
|
618
|
+
status: 'healthy' | 'warning' | 'critical',
|
|
619
|
+
): (str: string) => string {
|
|
620
|
+
switch (status) {
|
|
621
|
+
case 'healthy':
|
|
622
|
+
return chalk.green;
|
|
623
|
+
case 'warning':
|
|
624
|
+
return chalk.yellow;
|
|
625
|
+
case 'critical':
|
|
626
|
+
return chalk.red;
|
|
627
|
+
default:
|
|
628
|
+
return chalk.white;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function getStatusIcon(status: 'healthy' | 'warning' | 'critical'): string {
|
|
633
|
+
switch (status) {
|
|
634
|
+
case 'healthy':
|
|
635
|
+
return '[HEALTHY]';
|
|
636
|
+
case 'warning':
|
|
637
|
+
return '[WARNING]';
|
|
638
|
+
case 'critical':
|
|
639
|
+
return '[CRITICAL]';
|
|
640
|
+
default:
|
|
641
|
+
return '[UNKNOWN]';
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function formatScore(score: number): string {
|
|
646
|
+
if (score >= 80) {
|
|
647
|
+
return chalk.green(`${score.toFixed(1)}/100`);
|
|
648
|
+
}
|
|
649
|
+
if (score >= 50) {
|
|
650
|
+
return chalk.yellow(`${score.toFixed(1)}/100`);
|
|
651
|
+
}
|
|
652
|
+
return chalk.red(`${score.toFixed(1)}/100`);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function formatTrend(trend: 'improving' | 'stable' | 'degrading'): string {
|
|
656
|
+
switch (trend) {
|
|
657
|
+
case 'improving':
|
|
658
|
+
return chalk.green('Improving [^]');
|
|
659
|
+
case 'degrading':
|
|
660
|
+
return chalk.red('Degrading [v]');
|
|
661
|
+
case 'stable':
|
|
662
|
+
return chalk.gray('Stable [-]');
|
|
663
|
+
default:
|
|
664
|
+
return chalk.gray('Unknown');
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
function formatPercentage(value: number): string {
|
|
669
|
+
return `${(value * 100).toFixed(2)}%`;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function formatDimensionValue(value: number, key: string): string {
|
|
673
|
+
if (key === 'rewardHacking') {
|
|
674
|
+
return `${value} instances`;
|
|
675
|
+
}
|
|
676
|
+
return formatPercentage(value);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// ============================================================================
|
|
680
|
+
// Command Factory
|
|
681
|
+
// ============================================================================
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Create the alignment command with subcommands
|
|
685
|
+
*/
|
|
686
|
+
export function createAlignmentCommand(): Command {
|
|
687
|
+
const command = new Command('alignment')
|
|
688
|
+
.description('Alignment drift monitoring and reporting')
|
|
689
|
+
.addHelpText(
|
|
690
|
+
'after',
|
|
691
|
+
chalk.gray(`
|
|
692
|
+
Examples:
|
|
693
|
+
${chalk.green('wundr alignment report')} Generate alignment debt report
|
|
694
|
+
${chalk.green('wundr alignment score')} Get current alignment score
|
|
695
|
+
${chalk.green('wundr alignment history --days 7')} Show alignment history for past week
|
|
696
|
+
${chalk.green('wundr alignment dimensions')} Show breakdown by dimension
|
|
697
|
+
`),
|
|
698
|
+
);
|
|
699
|
+
|
|
700
|
+
// Report subcommand
|
|
701
|
+
command
|
|
702
|
+
.command('report')
|
|
703
|
+
.description('Generate alignment debt report')
|
|
704
|
+
.option('-s, --session <id>', 'Filter by session ID')
|
|
705
|
+
.option('-d, --days <n>', 'Number of days to analyze', '7')
|
|
706
|
+
.option('-o, --output <file>', 'Output file path for JSON report')
|
|
707
|
+
.action(async options => {
|
|
708
|
+
await generateAlignmentReport(options);
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
// Score subcommand
|
|
712
|
+
command
|
|
713
|
+
.command('score')
|
|
714
|
+
.description('Get current alignment score')
|
|
715
|
+
.option('-s, --session <id>', 'Get score for specific session')
|
|
716
|
+
.action(async options => {
|
|
717
|
+
await showAlignmentScore(options);
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
// History subcommand
|
|
721
|
+
command
|
|
722
|
+
.command('history')
|
|
723
|
+
.description('Show alignment history')
|
|
724
|
+
.option('-d, --days <n>', 'Number of days to show', '30')
|
|
725
|
+
.option(
|
|
726
|
+
'-f, --format <type>',
|
|
727
|
+
'Output format (table, json, chart)',
|
|
728
|
+
'table',
|
|
729
|
+
)
|
|
730
|
+
.action(async options => {
|
|
731
|
+
await showAlignmentHistory(options);
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
// Dimensions subcommand
|
|
735
|
+
command
|
|
736
|
+
.command('dimensions')
|
|
737
|
+
.description('Show breakdown by dimension')
|
|
738
|
+
.option('-s, --session <id>', 'Get dimensions for specific session')
|
|
739
|
+
.action(async options => {
|
|
740
|
+
await showDimensionBreakdown(options);
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
return command;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// ============================================================================
|
|
747
|
+
// Command Implementations
|
|
748
|
+
// ============================================================================
|
|
749
|
+
|
|
750
|
+
async function generateAlignmentReport(options: {
|
|
751
|
+
session?: string;
|
|
752
|
+
days?: string;
|
|
753
|
+
output?: string;
|
|
754
|
+
}): Promise<void> {
|
|
755
|
+
const spinner = ora('Generating alignment debt report...').start();
|
|
756
|
+
|
|
757
|
+
try {
|
|
758
|
+
const detector = new AlignmentDriftDetector();
|
|
759
|
+
await detector.initialize();
|
|
760
|
+
|
|
761
|
+
const days = parseInt(options.days || '7', 10);
|
|
762
|
+
const report = await detector.generateDebtReport(
|
|
763
|
+
days,
|
|
764
|
+
options.session,
|
|
765
|
+
options.output,
|
|
766
|
+
);
|
|
767
|
+
|
|
768
|
+
spinner.stop();
|
|
769
|
+
|
|
770
|
+
// Display report header
|
|
771
|
+
console.log(chalk.cyan('\nAlignment Debt Report'));
|
|
772
|
+
console.log(chalk.gray('='.repeat(70)));
|
|
773
|
+
console.log(
|
|
774
|
+
chalk.white('Generated: ') +
|
|
775
|
+
chalk.gray(new Date(report.generatedAt).toLocaleString()),
|
|
776
|
+
);
|
|
777
|
+
console.log(
|
|
778
|
+
chalk.white('Period: ') +
|
|
779
|
+
chalk.gray(
|
|
780
|
+
`${days} days (${new Date(report.period.start).toLocaleDateString()} - ${new Date(report.period.end).toLocaleDateString()})`,
|
|
781
|
+
),
|
|
782
|
+
);
|
|
783
|
+
|
|
784
|
+
if (options.session) {
|
|
785
|
+
console.log(chalk.white('Session: ') + chalk.gray(options.session));
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// Summary section
|
|
789
|
+
console.log(chalk.gray('\n' + '-'.repeat(70)));
|
|
790
|
+
console.log(chalk.cyan('Summary'));
|
|
791
|
+
console.log(
|
|
792
|
+
chalk.white('Average Score: ') +
|
|
793
|
+
formatScore(report.summary.averageScore),
|
|
794
|
+
);
|
|
795
|
+
console.log(
|
|
796
|
+
chalk.white('Lowest Score: ') +
|
|
797
|
+
formatScore(report.summary.lowestScore),
|
|
798
|
+
);
|
|
799
|
+
console.log(
|
|
800
|
+
chalk.white('Highest Score: ') +
|
|
801
|
+
formatScore(report.summary.highestScore),
|
|
802
|
+
);
|
|
803
|
+
console.log(
|
|
804
|
+
chalk.white('Trend: ') + formatTrend(report.summary.trend),
|
|
805
|
+
);
|
|
806
|
+
console.log(
|
|
807
|
+
chalk.white('Total Violations: ') +
|
|
808
|
+
(report.summary.totalViolations > 0
|
|
809
|
+
? chalk.red(String(report.summary.totalViolations))
|
|
810
|
+
: chalk.green('0')),
|
|
811
|
+
);
|
|
812
|
+
|
|
813
|
+
// Dimension analysis section
|
|
814
|
+
console.log(chalk.gray('\n' + '-'.repeat(70)));
|
|
815
|
+
console.log(chalk.cyan('Dimension Analysis'));
|
|
816
|
+
console.log(
|
|
817
|
+
chalk.cyan(
|
|
818
|
+
padRight('Dimension', 25) +
|
|
819
|
+
padRight('Average', 12) +
|
|
820
|
+
padRight('Max', 12) +
|
|
821
|
+
padRight('Exceedances', 12),
|
|
822
|
+
),
|
|
823
|
+
);
|
|
824
|
+
console.log(chalk.gray('-'.repeat(61)));
|
|
825
|
+
|
|
826
|
+
const dimensionNames: Record<string, string> = {
|
|
827
|
+
policyViolationRate: 'Policy Violation Rate',
|
|
828
|
+
intentOutcomeGap: 'Intent-Outcome Gap',
|
|
829
|
+
evaluatorDisagreement: 'Evaluator Disagreement',
|
|
830
|
+
escalationSuppression: 'Escalation Suppression',
|
|
831
|
+
rewardHacking: 'Reward Hacking',
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
for (const [key, analysis] of Object.entries(report.dimensionAnalysis)) {
|
|
835
|
+
const name = dimensionNames[key] || key;
|
|
836
|
+
const entry = analysis as DimensionAnalysisEntry;
|
|
837
|
+
const exceedanceColor = entry.exceedances > 0 ? chalk.red : chalk.green;
|
|
838
|
+
|
|
839
|
+
console.log(
|
|
840
|
+
padRight(name, 25) +
|
|
841
|
+
padRight(formatDimensionValue(entry.average, key), 12) +
|
|
842
|
+
padRight(formatDimensionValue(entry.max, key), 12) +
|
|
843
|
+
exceedanceColor(padRight(String(entry.exceedances), 12)),
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// Critical events section
|
|
848
|
+
if (report.criticalEvents.length > 0) {
|
|
849
|
+
console.log(chalk.gray('\n' + '-'.repeat(70)));
|
|
850
|
+
console.log(chalk.red('Critical Events'));
|
|
851
|
+
for (const event of report.criticalEvents.slice(0, 5)) {
|
|
852
|
+
const dimName = dimensionNames[event.dimension] || event.dimension;
|
|
853
|
+
console.log(
|
|
854
|
+
chalk.red(' [!] ') +
|
|
855
|
+
chalk.gray(new Date(event.timestamp).toLocaleString()) +
|
|
856
|
+
chalk.white(` ${dimName}: `) +
|
|
857
|
+
chalk.red(formatDimensionValue(event.value, event.dimension)) +
|
|
858
|
+
chalk.gray(
|
|
859
|
+
` (threshold: ${formatDimensionValue(event.threshold, event.dimension)})`,
|
|
860
|
+
),
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
if (report.criticalEvents.length > 5) {
|
|
864
|
+
console.log(
|
|
865
|
+
chalk.gray(` ... and ${report.criticalEvents.length - 5} more`),
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// Recommendations section
|
|
871
|
+
console.log(chalk.gray('\n' + '-'.repeat(70)));
|
|
872
|
+
console.log(chalk.cyan('Recommendations'));
|
|
873
|
+
for (const rec of report.recommendations) {
|
|
874
|
+
const color = rec.startsWith('CRITICAL')
|
|
875
|
+
? chalk.red
|
|
876
|
+
: rec.startsWith('WARNING')
|
|
877
|
+
? chalk.yellow
|
|
878
|
+
: chalk.white;
|
|
879
|
+
console.log(color(` - ${rec}`));
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
console.log(chalk.gray('='.repeat(70)));
|
|
883
|
+
|
|
884
|
+
// Show output file if saved
|
|
885
|
+
if (options.output) {
|
|
886
|
+
console.log(chalk.green(`\nReport saved to: ${options.output}`));
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
console.log('');
|
|
890
|
+
} catch (error) {
|
|
891
|
+
spinner.fail('Failed to generate alignment report');
|
|
892
|
+
console.error(
|
|
893
|
+
chalk.red(error instanceof Error ? error.message : String(error)),
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
async function showAlignmentScore(options: {
|
|
899
|
+
session?: string;
|
|
900
|
+
}): Promise<void> {
|
|
901
|
+
const spinner = ora('Calculating alignment score...').start();
|
|
902
|
+
|
|
903
|
+
try {
|
|
904
|
+
const detector = new AlignmentDriftDetector();
|
|
905
|
+
await detector.initialize();
|
|
906
|
+
|
|
907
|
+
const result = await detector.getAlignmentScore(options.session);
|
|
908
|
+
|
|
909
|
+
spinner.stop();
|
|
910
|
+
|
|
911
|
+
console.log(chalk.cyan('\nAlignment Score'));
|
|
912
|
+
console.log(chalk.gray('='.repeat(40)));
|
|
913
|
+
|
|
914
|
+
// Large score display with color
|
|
915
|
+
const scoreColor =
|
|
916
|
+
result.color === 'green'
|
|
917
|
+
? chalk.green
|
|
918
|
+
: result.color === 'yellow'
|
|
919
|
+
? chalk.yellow
|
|
920
|
+
: chalk.red;
|
|
921
|
+
|
|
922
|
+
const scoreDisplay = `
|
|
923
|
+
${scoreColor(' +-----------------+')}
|
|
924
|
+
${scoreColor(' | |')}
|
|
925
|
+
${scoreColor(' |')} ${chalk.bold(scoreColor(result.score.toFixed(1)))} ${scoreColor('|')}
|
|
926
|
+
${scoreColor(' |')} ${chalk.gray('/100')} ${scoreColor('|')}
|
|
927
|
+
${scoreColor(' | |')}
|
|
928
|
+
${scoreColor(' +-----------------+')}
|
|
929
|
+
`;
|
|
930
|
+
console.log(scoreDisplay);
|
|
931
|
+
|
|
932
|
+
const statusColor = getStatusColor(result.status);
|
|
933
|
+
console.log(
|
|
934
|
+
chalk.white('Status: ') + statusColor(getStatusIcon(result.status)),
|
|
935
|
+
);
|
|
936
|
+
|
|
937
|
+
if (options.session) {
|
|
938
|
+
console.log(chalk.white('Session: ') + chalk.gray(options.session));
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// Show quick dimension summary
|
|
942
|
+
const report = await detector.getAlignmentReport(options.session);
|
|
943
|
+
console.log(chalk.gray('\n' + '-'.repeat(40)));
|
|
944
|
+
console.log(chalk.cyan('Quick Dimension Status'));
|
|
945
|
+
|
|
946
|
+
for (const dim of report.dimensions) {
|
|
947
|
+
const dimStatusColor = getStatusColor(dim.status);
|
|
948
|
+
const icon =
|
|
949
|
+
dim.status === 'healthy'
|
|
950
|
+
? '[OK]'
|
|
951
|
+
: dim.status === 'warning'
|
|
952
|
+
? '[!]'
|
|
953
|
+
: '[X]';
|
|
954
|
+
console.log(
|
|
955
|
+
dimStatusColor(` ${icon} `) +
|
|
956
|
+
chalk.white(padRight(dim.name, 25)) +
|
|
957
|
+
dimStatusColor(formatDimensionValue(dim.currentValue, dim.key)),
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
console.log(chalk.gray('='.repeat(40)));
|
|
962
|
+
console.log('');
|
|
963
|
+
} catch (error) {
|
|
964
|
+
spinner.fail('Failed to get alignment score');
|
|
965
|
+
console.error(
|
|
966
|
+
chalk.red(error instanceof Error ? error.message : String(error)),
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
async function showAlignmentHistory(options: {
|
|
972
|
+
days?: string;
|
|
973
|
+
format?: 'table' | 'json' | 'chart';
|
|
974
|
+
}): Promise<void> {
|
|
975
|
+
const spinner = ora('Loading alignment history...').start();
|
|
976
|
+
|
|
977
|
+
try {
|
|
978
|
+
const detector = new AlignmentDriftDetector();
|
|
979
|
+
await detector.initialize();
|
|
980
|
+
|
|
981
|
+
const days = parseInt(options.days || '30', 10);
|
|
982
|
+
const history = await detector.getHistory(days);
|
|
983
|
+
|
|
984
|
+
spinner.stop();
|
|
985
|
+
|
|
986
|
+
if (options.format === 'json') {
|
|
987
|
+
console.log(
|
|
988
|
+
JSON.stringify(
|
|
989
|
+
{
|
|
990
|
+
timestamp: new Date().toISOString(),
|
|
991
|
+
days,
|
|
992
|
+
count: history.length,
|
|
993
|
+
entries: history,
|
|
994
|
+
},
|
|
995
|
+
null,
|
|
996
|
+
2,
|
|
997
|
+
),
|
|
998
|
+
);
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
console.log(chalk.cyan('\nAlignment History'));
|
|
1003
|
+
console.log(chalk.gray('='.repeat(80)));
|
|
1004
|
+
console.log(
|
|
1005
|
+
chalk.gray(`Showing last ${days} days (${history.length} entries)`),
|
|
1006
|
+
);
|
|
1007
|
+
|
|
1008
|
+
if (history.length === 0) {
|
|
1009
|
+
console.log(chalk.yellow('\nNo alignment history found.'));
|
|
1010
|
+
console.log(chalk.gray('Run "wundr alignment score" to start tracking.'));
|
|
1011
|
+
console.log('');
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
if (options.format === 'chart') {
|
|
1016
|
+
displayHistoryChart(history);
|
|
1017
|
+
} else {
|
|
1018
|
+
// Table format (default)
|
|
1019
|
+
console.log(chalk.gray('\n' + '-'.repeat(80)));
|
|
1020
|
+
console.log(
|
|
1021
|
+
chalk.cyan(
|
|
1022
|
+
padRight('Timestamp', 22) +
|
|
1023
|
+
padRight('Score', 10) +
|
|
1024
|
+
padRight('Status', 12) +
|
|
1025
|
+
padRight('Session', 20) +
|
|
1026
|
+
padRight('Policy', 10),
|
|
1027
|
+
),
|
|
1028
|
+
);
|
|
1029
|
+
console.log(chalk.gray('-'.repeat(80)));
|
|
1030
|
+
|
|
1031
|
+
for (const entry of history.slice(0, 20)) {
|
|
1032
|
+
const statusColor = getStatusColor(entry.status);
|
|
1033
|
+
const timestamp = new Date(entry.timestamp).toLocaleString();
|
|
1034
|
+
|
|
1035
|
+
console.log(
|
|
1036
|
+
padRight(timestamp, 22) +
|
|
1037
|
+
formatScore(entry.score).padEnd(19) + // Account for ANSI codes
|
|
1038
|
+
statusColor(padRight(getStatusIcon(entry.status), 12)) +
|
|
1039
|
+
chalk.gray(padRight(entry.sessionId || '-', 20)) +
|
|
1040
|
+
padRight(formatPercentage(entry.metrics.policyViolationRate), 10),
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
if (history.length > 20) {
|
|
1045
|
+
console.log(chalk.gray(`... and ${history.length - 20} more entries`));
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
console.log(chalk.gray('='.repeat(80)));
|
|
1050
|
+
console.log('');
|
|
1051
|
+
} catch (error) {
|
|
1052
|
+
spinner.fail('Failed to load alignment history');
|
|
1053
|
+
console.error(
|
|
1054
|
+
chalk.red(error instanceof Error ? error.message : String(error)),
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
function displayHistoryChart(history: AlignmentHistoryEntry[]): void {
|
|
1060
|
+
const chartHeight = 10;
|
|
1061
|
+
const chartWidth = Math.min(60, history.length);
|
|
1062
|
+
|
|
1063
|
+
const step = Math.ceil(history.length / chartWidth);
|
|
1064
|
+
const sampledHistory = history
|
|
1065
|
+
.filter((_, i) => i % step === 0)
|
|
1066
|
+
.slice(0, chartWidth);
|
|
1067
|
+
|
|
1068
|
+
console.log(chalk.gray('\n Score Trend:'));
|
|
1069
|
+
console.log(chalk.gray(' 100 |'));
|
|
1070
|
+
|
|
1071
|
+
for (let row = chartHeight; row >= 0; row--) {
|
|
1072
|
+
const threshold = (row / chartHeight) * 100;
|
|
1073
|
+
let line = ` ${String(Math.round(threshold)).padStart(3)} |`;
|
|
1074
|
+
|
|
1075
|
+
for (const entry of sampledHistory) {
|
|
1076
|
+
if (
|
|
1077
|
+
entry.score >= threshold &&
|
|
1078
|
+
entry.score < threshold + 100 / chartHeight
|
|
1079
|
+
) {
|
|
1080
|
+
const color =
|
|
1081
|
+
entry.score >= 80
|
|
1082
|
+
? chalk.green
|
|
1083
|
+
: entry.score >= 50
|
|
1084
|
+
? chalk.yellow
|
|
1085
|
+
: chalk.red;
|
|
1086
|
+
line += color('*');
|
|
1087
|
+
} else if (entry.score >= threshold) {
|
|
1088
|
+
line += chalk.gray('|');
|
|
1089
|
+
} else {
|
|
1090
|
+
line += ' ';
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
console.log(line);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
console.log(chalk.gray(' 0 +' + '-'.repeat(chartWidth)));
|
|
1098
|
+
console.log(
|
|
1099
|
+
chalk.gray(
|
|
1100
|
+
' ' +
|
|
1101
|
+
'oldest'.padEnd(chartWidth / 2) +
|
|
1102
|
+
'newest'.padStart(chartWidth / 2),
|
|
1103
|
+
),
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
async function showDimensionBreakdown(options: {
|
|
1108
|
+
session?: string;
|
|
1109
|
+
}): Promise<void> {
|
|
1110
|
+
const spinner = ora('Loading dimension breakdown...').start();
|
|
1111
|
+
|
|
1112
|
+
try {
|
|
1113
|
+
const detector = new AlignmentDriftDetector();
|
|
1114
|
+
await detector.initialize();
|
|
1115
|
+
|
|
1116
|
+
const dimensions = await detector.getDimensionBreakdown();
|
|
1117
|
+
const thresholds = detector.getThresholds();
|
|
1118
|
+
|
|
1119
|
+
spinner.stop();
|
|
1120
|
+
|
|
1121
|
+
console.log(chalk.cyan('\nAlignment Dimensions'));
|
|
1122
|
+
console.log(chalk.gray('='.repeat(90)));
|
|
1123
|
+
|
|
1124
|
+
if (options.session) {
|
|
1125
|
+
console.log(chalk.white('Session: ') + chalk.gray(options.session));
|
|
1126
|
+
console.log('');
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
// Display each dimension in detail
|
|
1130
|
+
for (const dim of dimensions) {
|
|
1131
|
+
const statusColor = getStatusColor(dim.status);
|
|
1132
|
+
const icon =
|
|
1133
|
+
dim.status === 'healthy'
|
|
1134
|
+
? '[OK]'
|
|
1135
|
+
: dim.status === 'warning'
|
|
1136
|
+
? '[!!]'
|
|
1137
|
+
: '[XX]';
|
|
1138
|
+
|
|
1139
|
+
console.log(chalk.gray('-'.repeat(90)));
|
|
1140
|
+
console.log(chalk.bold(statusColor(`${icon} ${dim.name}`)));
|
|
1141
|
+
console.log(chalk.gray(` ${dim.description}`));
|
|
1142
|
+
console.log('');
|
|
1143
|
+
|
|
1144
|
+
// Value bar visualization
|
|
1145
|
+
const barWidth = 40;
|
|
1146
|
+
const valueRatio = Math.min(dim.currentValue / (dim.threshold * 3), 1);
|
|
1147
|
+
const thresholdPos = Math.floor(
|
|
1148
|
+
(dim.threshold / (dim.threshold * 3)) * barWidth,
|
|
1149
|
+
);
|
|
1150
|
+
const valuePos = Math.floor(valueRatio * barWidth);
|
|
1151
|
+
|
|
1152
|
+
let bar = '';
|
|
1153
|
+
for (let i = 0; i < barWidth; i++) {
|
|
1154
|
+
if (i === thresholdPos) {
|
|
1155
|
+
bar += chalk.yellow('|');
|
|
1156
|
+
} else if (i < valuePos) {
|
|
1157
|
+
bar += statusColor('#');
|
|
1158
|
+
} else {
|
|
1159
|
+
bar += chalk.gray('-');
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
console.log(` [${bar}]`);
|
|
1164
|
+
console.log(
|
|
1165
|
+
` ${chalk.white('Current:')} ${statusColor(formatDimensionValue(dim.currentValue, dim.key))} ${chalk.white('Threshold:')} ${chalk.yellow(formatDimensionValue(dim.threshold, dim.key))}`,
|
|
1166
|
+
);
|
|
1167
|
+
console.log(
|
|
1168
|
+
` ${chalk.white('Status:')} ${statusColor(dim.status.toUpperCase())}`,
|
|
1169
|
+
);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
// Threshold reference
|
|
1173
|
+
console.log(chalk.gray('\n' + '='.repeat(90)));
|
|
1174
|
+
console.log(chalk.cyan('Threshold Reference'));
|
|
1175
|
+
console.log(
|
|
1176
|
+
chalk.gray(
|
|
1177
|
+
` Policy Violation: >${formatPercentage(thresholds.policyViolation)} daily violations`,
|
|
1178
|
+
),
|
|
1179
|
+
);
|
|
1180
|
+
console.log(
|
|
1181
|
+
chalk.gray(
|
|
1182
|
+
` Intent-Outcome Gap: >${formatPercentage(thresholds.intentOutcomeGap)} divergence`,
|
|
1183
|
+
),
|
|
1184
|
+
);
|
|
1185
|
+
console.log(
|
|
1186
|
+
chalk.gray(
|
|
1187
|
+
` Evaluator Disagreement:>${formatPercentage(thresholds.evaluatorDisagreement)} monthly overrides`,
|
|
1188
|
+
),
|
|
1189
|
+
);
|
|
1190
|
+
console.log(
|
|
1191
|
+
chalk.gray(
|
|
1192
|
+
` Escalation Suppression:>${formatPercentage(thresholds.escalationSuppression)} drop from baseline`,
|
|
1193
|
+
),
|
|
1194
|
+
);
|
|
1195
|
+
console.log(
|
|
1196
|
+
chalk.gray(
|
|
1197
|
+
` Reward Hacking: >${thresholds.rewardHacking} instances/month`,
|
|
1198
|
+
),
|
|
1199
|
+
);
|
|
1200
|
+
|
|
1201
|
+
console.log(chalk.gray('='.repeat(90)));
|
|
1202
|
+
console.log('');
|
|
1203
|
+
} catch (error) {
|
|
1204
|
+
spinner.fail('Failed to load dimension breakdown');
|
|
1205
|
+
console.error(
|
|
1206
|
+
chalk.red(error instanceof Error ? error.message : String(error)),
|
|
1207
|
+
);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
// Export the command
|
|
1212
|
+
export default createAlignmentCommand;
|