aiwf 0.3.21 → 0.3.23
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/package.json +3 -2
- package/src/cli/index.js +39 -336
- package/src/lib/installer.js +310 -90
- package/src/utils/messages.js +24 -0
- package/src/DEPENDENCY_MAP.md +0 -90
- package/src/cli/cache-cli.js +0 -459
- package/src/cli/checkpoint-cli.js +0 -417
- package/src/cli/language-cli.js +0 -286
- package/src/cli/sprint-cli.js +0 -287
- package/src/commands/ai-tool.js +0 -383
- package/src/commands/compress.js +0 -60
- package/src/commands/create-project.js +0 -160
- package/src/commands/evaluate.js +0 -459
- package/src/commands/persona.js +0 -309
- package/src/commands/sprint-task.js +0 -262
- package/src/commands/token.js +0 -312
- package/src/lib/ai-persona-manager.js +0 -711
- package/src/lib/cache-system.js +0 -332
- package/src/lib/context-engine.js +0 -568
- package/src/lib/github-integration.js +0 -402
- package/src/lib/memory-profiler.js +0 -471
- package/src/lib/metrics-collector.js +0 -885
- package/src/lib/offline-detector.js +0 -386
- package/src/lib/resource-loader-enhanced.js +0 -399
- package/src/lib/resource-loader.js +0 -250
- package/src/lib/resources/commands/ai-persona.js +0 -602
- package/src/lib/resources/commands/compress-context.js +0 -389
- package/src/lib/resources/commands/evaluate.js +0 -246
- package/src/lib/resources/commands/persona-context-apply.js +0 -562
- package/src/lib/resources/commands/token-tracking.js +0 -443
- package/src/lib/resources/config/commit-patterns.js +0 -103
- package/src/lib/resources/config/language.json +0 -6
- package/src/lib/resources/personas/PERSONA_INDEX.md +0 -57
- package/src/lib/resources/personas/analyst.json +0 -44
- package/src/lib/resources/personas/architect/best_practices.md +0 -136
- package/src/lib/resources/personas/architect/knowledge_base.md +0 -377
- package/src/lib/resources/personas/architect.json +0 -44
- package/src/lib/resources/personas/backend/best_practices.md +0 -766
- package/src/lib/resources/personas/backend/knowledge_base.md +0 -1070
- package/src/lib/resources/personas/data_analyst/best_practices.md +0 -594
- package/src/lib/resources/personas/data_analyst/knowledge_base.md +0 -1057
- package/src/lib/resources/personas/developer.json +0 -44
- package/src/lib/resources/personas/developer.md +0 -37
- package/src/lib/resources/personas/evaluation_criteria.json +0 -137
- package/src/lib/resources/personas/frontend/best_practices.md +0 -445
- package/src/lib/resources/personas/frontend/knowledge_base.md +0 -729
- package/src/lib/resources/personas/persona-index.json +0 -31
- package/src/lib/resources/personas/reviewer.json +0 -44
- package/src/lib/resources/personas/security/best_practices.md +0 -307
- package/src/lib/resources/personas/security/knowledge_base.md +0 -498
- package/src/lib/resources/personas/tester.json +0 -44
- package/src/lib/resources/templates/README.md +0 -78
- package/src/lib/resources/templates/api-server/config.json +0 -64
- package/src/lib/resources/templates/api-server/template/.aiwf/config.json +0 -56
- package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +0 -42
- package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +0 -40
- package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +0 -111
- package/src/lib/resources/templates/api-server/template/.env.example +0 -29
- package/src/lib/resources/templates/api-server/template/.eslintrc.json +0 -23
- package/src/lib/resources/templates/api-server/template/README.md +0 -171
- package/src/lib/resources/templates/api-server/template/jest.config.js +0 -26
- package/src/lib/resources/templates/api-server/template/nodemon.json +0 -9
- package/src/lib/resources/templates/api-server/template/package.json +0 -61
- package/src/lib/resources/templates/api-server/template/src/app.ts +0 -60
- package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +0 -38
- package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +0 -54
- package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +0 -128
- package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +0 -28
- package/src/lib/resources/templates/api-server/template/src/index.ts +0 -28
- package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +0 -50
- package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +0 -41
- package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +0 -42
- package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +0 -14
- package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +0 -20
- package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +0 -31
- package/src/lib/resources/templates/api-server/template/src/routes/index.ts +0 -13
- package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +0 -67
- package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +0 -37
- package/src/lib/resources/templates/api-server/template/tests/app.test.ts +0 -40
- package/src/lib/resources/templates/api-server/template/tsconfig.json +0 -43
- package/src/lib/resources/templates/npm-library/config.json +0 -61
- package/src/lib/resources/templates/npm-library/template/LICENSE +0 -21
- package/src/lib/resources/templates/npm-library/template/README.md +0 -201
- package/src/lib/resources/templates/npm-library/template/package.json +0 -78
- package/src/lib/resources/templates/web-app/config.json +0 -53
- package/src/lib/resources/templates/web-app/template/.aiwf/config.json +0 -54
- package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +0 -37
- package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +0 -40
- package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +0 -110
- package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +0 -20
- package/src/lib/resources/templates/web-app/template/README.md +0 -151
- package/src/lib/resources/templates/web-app/template/index.html +0 -14
- package/src/lib/resources/templates/web-app/template/package.json +0 -44
- package/src/lib/resources/templates/web-app/template/postcss.config.js +0 -6
- package/src/lib/resources/templates/web-app/template/public/vite.svg +0 -1
- package/src/lib/resources/templates/web-app/template/src/App.tsx +0 -21
- package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +0 -57
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +0 -107
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +0 -71
- package/src/lib/resources/templates/web-app/template/src/index.css +0 -60
- package/src/lib/resources/templates/web-app/template/src/main.tsx +0 -10
- package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +0 -57
- package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +0 -89
- package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +0 -25
- package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +0 -126
- package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +0 -9
- package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +0 -1
- package/src/lib/resources/templates/web-app/template/tailwind.config.js +0 -30
- package/src/lib/resources/templates/web-app/template/tsconfig.json +0 -37
- package/src/lib/resources/templates/web-app/template/tsconfig.node.json +0 -10
- package/src/lib/resources/templates/web-app/template/vite.config.ts +0 -23
- package/src/lib/resources/utils/background-monitor.js +0 -223
- package/src/lib/resources/utils/compression-metrics.js +0 -1031
- package/src/lib/resources/utils/compression-strategies.js +0 -557
- package/src/lib/resources/utils/content-normalizer.js +0 -455
- package/src/lib/resources/utils/context-compressor.js +0 -583
- package/src/lib/resources/utils/context-rule-parser.js +0 -180
- package/src/lib/resources/utils/context-token-monitor.js +0 -463
- package/src/lib/resources/utils/context-update-manager.js +0 -502
- package/src/lib/resources/utils/git-utils.js +0 -220
- package/src/lib/resources/utils/importance-classifier.js +0 -645
- package/src/lib/resources/utils/information-filter.js +0 -710
- package/src/lib/resources/utils/persona-aware-compressor.js +0 -598
- package/src/lib/resources/utils/prompt-injector.js +0 -242
- package/src/lib/resources/utils/simplified-evaluator.js +0 -119
- package/src/lib/resources/utils/text-summarizer.js +0 -492
- package/src/lib/resources/utils/token-counter.js +0 -177
- package/src/lib/resources/utils/token-monitor.js +0 -484
- package/src/lib/resources/utils/token-reporter.js +0 -452
- package/src/lib/resources/utils/token-storage.js +0 -433
- package/src/lib/resources/utils/token-tracker.js +0 -283
- package/src/lib/state/priority-calculator.js +0 -217
- package/src/lib/state/state-index.js +0 -155
- package/src/lib/state/task-scanner.js +0 -328
- package/src/lib/task-analyzer.js +0 -599
- package/src/lib/template-cache-system.js +0 -559
- package/src/lib/template-downloader.js +0 -467
- package/src/lib/template-version-manager.js +0 -491
- package/src/lib/token-optimizer.js +0 -517
- package/src/utils/engineering-guard.js +0 -399
|
@@ -1,885 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Metrics Collector
|
|
3
|
-
* Collects and analyzes metrics for AI persona performance
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import fs from 'fs/promises';
|
|
7
|
-
import path from 'path';
|
|
8
|
-
import { EventEmitter } from 'events';
|
|
9
|
-
|
|
10
|
-
class MetricsCollector extends EventEmitter {
|
|
11
|
-
constructor(options = {}) {
|
|
12
|
-
super();
|
|
13
|
-
|
|
14
|
-
this.options = {
|
|
15
|
-
metricsPath: options.metricsPath ||
|
|
16
|
-
path.join(process.cwd(), '.aiwf', 'metrics'),
|
|
17
|
-
maxHistorySize: options.maxHistorySize || 1000,
|
|
18
|
-
aggregationInterval: options.aggregationInterval || 3600000, // 1 hour
|
|
19
|
-
persistInterval: options.persistInterval || 300000, // 5 minutes
|
|
20
|
-
...options
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
this.metrics = {
|
|
24
|
-
sessions: [],
|
|
25
|
-
personaPerformance: {},
|
|
26
|
-
taskOutcomes: [],
|
|
27
|
-
aggregated: {}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
this.currentSession = null;
|
|
31
|
-
this.startTime = Date.now();
|
|
32
|
-
this.persistTimer = null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Initialize metrics collector
|
|
37
|
-
*/
|
|
38
|
-
async init() {
|
|
39
|
-
try {
|
|
40
|
-
// Ensure metrics directory exists
|
|
41
|
-
await fs.mkdir(this.options.metricsPath, { recursive: true });
|
|
42
|
-
|
|
43
|
-
// Load existing metrics
|
|
44
|
-
await this.loadMetrics();
|
|
45
|
-
|
|
46
|
-
// Start persist timer
|
|
47
|
-
this.startPersistTimer();
|
|
48
|
-
|
|
49
|
-
this.emit('initialized', {
|
|
50
|
-
sessionsLoaded: this.metrics.sessions.length,
|
|
51
|
-
startTime: this.startTime
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
return true;
|
|
55
|
-
} catch (error) {
|
|
56
|
-
this.emit('error', { error, phase: 'initialization' });
|
|
57
|
-
throw error;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Start a new metrics session
|
|
63
|
-
*/
|
|
64
|
-
startSession(persona, task) {
|
|
65
|
-
// End current session if exists
|
|
66
|
-
if (this.currentSession) {
|
|
67
|
-
this.endSession();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
this.currentSession = {
|
|
71
|
-
id: this.generateSessionId(),
|
|
72
|
-
persona: persona.name || persona,
|
|
73
|
-
task: {
|
|
74
|
-
type: task.type || 'general',
|
|
75
|
-
description: task.description || task.command || '',
|
|
76
|
-
complexity: this.estimateTaskComplexity(task)
|
|
77
|
-
},
|
|
78
|
-
startTime: Date.now(),
|
|
79
|
-
events: [],
|
|
80
|
-
metrics: {
|
|
81
|
-
tokenUsage: {
|
|
82
|
-
start: this.getCurrentTokenCount(),
|
|
83
|
-
end: null,
|
|
84
|
-
contextSwitches: 0
|
|
85
|
-
},
|
|
86
|
-
interactions: 0,
|
|
87
|
-
errors: 0,
|
|
88
|
-
completionStatus: null
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
this.emit('sessionStarted', {
|
|
93
|
-
sessionId: this.currentSession.id,
|
|
94
|
-
persona: this.currentSession.persona
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
return this.currentSession.id;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* End current session
|
|
102
|
-
*/
|
|
103
|
-
endSession(status = 'completed') {
|
|
104
|
-
if (!this.currentSession) return;
|
|
105
|
-
|
|
106
|
-
this.currentSession.endTime = Date.now();
|
|
107
|
-
this.currentSession.duration = this.currentSession.endTime - this.currentSession.startTime;
|
|
108
|
-
this.currentSession.metrics.completionStatus = status;
|
|
109
|
-
this.currentSession.metrics.tokenUsage.end = this.getCurrentTokenCount();
|
|
110
|
-
|
|
111
|
-
// Calculate quality metrics
|
|
112
|
-
this.currentSession.qualityMetrics = this.calculateQualityMetrics(this.currentSession);
|
|
113
|
-
|
|
114
|
-
// Store session
|
|
115
|
-
this.metrics.sessions.push(this.currentSession);
|
|
116
|
-
this.updatePersonaPerformance(this.currentSession);
|
|
117
|
-
|
|
118
|
-
// Limit history size
|
|
119
|
-
if (this.metrics.sessions.length > this.options.maxHistorySize) {
|
|
120
|
-
this.metrics.sessions = this.metrics.sessions.slice(-this.options.maxHistorySize);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
this.emit('sessionEnded', {
|
|
124
|
-
sessionId: this.currentSession.id,
|
|
125
|
-
duration: this.currentSession.duration,
|
|
126
|
-
status
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
const session = this.currentSession;
|
|
130
|
-
this.currentSession = null;
|
|
131
|
-
|
|
132
|
-
return session;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Track an event
|
|
137
|
-
*/
|
|
138
|
-
trackEvent(eventType, data = {}) {
|
|
139
|
-
const event = {
|
|
140
|
-
type: eventType,
|
|
141
|
-
timestamp: Date.now(),
|
|
142
|
-
data
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
// Add to current session if active
|
|
146
|
-
if (this.currentSession) {
|
|
147
|
-
this.currentSession.events.push(event);
|
|
148
|
-
|
|
149
|
-
// Update session metrics based on event
|
|
150
|
-
this.updateSessionMetrics(eventType, data);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Track global events
|
|
154
|
-
this.trackGlobalEvent(eventType, data);
|
|
155
|
-
|
|
156
|
-
this.emit('eventTracked', event);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Update session metrics based on event
|
|
161
|
-
*/
|
|
162
|
-
updateSessionMetrics(eventType, data) {
|
|
163
|
-
if (!this.currentSession) return;
|
|
164
|
-
|
|
165
|
-
switch (eventType) {
|
|
166
|
-
case 'interaction':
|
|
167
|
-
this.currentSession.metrics.interactions++;
|
|
168
|
-
break;
|
|
169
|
-
|
|
170
|
-
case 'error':
|
|
171
|
-
this.currentSession.metrics.errors++;
|
|
172
|
-
break;
|
|
173
|
-
|
|
174
|
-
case 'context_switch':
|
|
175
|
-
this.currentSession.metrics.tokenUsage.contextSwitches++;
|
|
176
|
-
break;
|
|
177
|
-
|
|
178
|
-
case 'task_completed':
|
|
179
|
-
this.currentSession.metrics.completionStatus = 'completed';
|
|
180
|
-
break;
|
|
181
|
-
|
|
182
|
-
case 'task_failed':
|
|
183
|
-
this.currentSession.metrics.completionStatus = 'failed';
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Track global events (not session-specific)
|
|
190
|
-
*/
|
|
191
|
-
trackGlobalEvent(eventType, data) {
|
|
192
|
-
// Aggregate event counts
|
|
193
|
-
if (!this.metrics.aggregated.eventCounts) {
|
|
194
|
-
this.metrics.aggregated.eventCounts = {};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
this.metrics.aggregated.eventCounts[eventType] =
|
|
198
|
-
(this.metrics.aggregated.eventCounts[eventType] || 0) + 1;
|
|
199
|
-
|
|
200
|
-
// Track persona switches
|
|
201
|
-
if (eventType === 'persona_switch') {
|
|
202
|
-
if (!this.metrics.aggregated.personaSwitches) {
|
|
203
|
-
this.metrics.aggregated.personaSwitches = [];
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
this.metrics.aggregated.personaSwitches.push({
|
|
207
|
-
from: data.from,
|
|
208
|
-
to: data.to,
|
|
209
|
-
trigger: data.trigger,
|
|
210
|
-
timestamp: Date.now()
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Calculate quality metrics for a session
|
|
217
|
-
*/
|
|
218
|
-
calculateQualityMetrics(session) {
|
|
219
|
-
const metrics = {
|
|
220
|
-
completionTime: session.duration,
|
|
221
|
-
tokenEfficiency: this.calculateTokenEfficiency(session),
|
|
222
|
-
errorRate: this.calculateErrorRate(session),
|
|
223
|
-
interactionEfficiency: this.calculateInteractionEfficiency(session),
|
|
224
|
-
overallScore: 0
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
// Calculate overall score (0-1)
|
|
228
|
-
const weights = {
|
|
229
|
-
completion: 0.3,
|
|
230
|
-
tokenEfficiency: 0.3,
|
|
231
|
-
errorRate: 0.2,
|
|
232
|
-
interactionEfficiency: 0.2
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
let score = 0;
|
|
236
|
-
|
|
237
|
-
// Completion score
|
|
238
|
-
if (session.metrics.completionStatus === 'completed') {
|
|
239
|
-
score += weights.completion;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Token efficiency score
|
|
243
|
-
score += metrics.tokenEfficiency * weights.tokenEfficiency;
|
|
244
|
-
|
|
245
|
-
// Error rate score (inverse - lower is better)
|
|
246
|
-
score += (1 - metrics.errorRate) * weights.errorRate;
|
|
247
|
-
|
|
248
|
-
// Interaction efficiency score
|
|
249
|
-
score += metrics.interactionEfficiency * weights.interactionEfficiency;
|
|
250
|
-
|
|
251
|
-
metrics.overallScore = score;
|
|
252
|
-
|
|
253
|
-
return metrics;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Calculate token efficiency
|
|
258
|
-
*/
|
|
259
|
-
calculateTokenEfficiency(session) {
|
|
260
|
-
const tokensUsed = (session.metrics.tokenUsage.end || 0) -
|
|
261
|
-
(session.metrics.tokenUsage.start || 0);
|
|
262
|
-
|
|
263
|
-
if (tokensUsed === 0) return 1;
|
|
264
|
-
|
|
265
|
-
// Estimate expected tokens based on task complexity
|
|
266
|
-
const expectedTokens = session.task.complexity * 1000;
|
|
267
|
-
|
|
268
|
-
// Efficiency is inverse of usage ratio (lower usage = higher efficiency)
|
|
269
|
-
const efficiency = Math.min(expectedTokens / tokensUsed, 1);
|
|
270
|
-
|
|
271
|
-
return efficiency;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Calculate error rate
|
|
276
|
-
*/
|
|
277
|
-
calculateErrorRate(session) {
|
|
278
|
-
if (session.metrics.interactions === 0) return 0;
|
|
279
|
-
|
|
280
|
-
return Math.min(session.metrics.errors / session.metrics.interactions, 1);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Calculate interaction efficiency
|
|
285
|
-
*/
|
|
286
|
-
calculateInteractionEfficiency(session) {
|
|
287
|
-
if (session.metrics.interactions === 0) return 0;
|
|
288
|
-
|
|
289
|
-
// Estimate expected interactions based on task complexity
|
|
290
|
-
const expectedInteractions = Math.max(session.task.complexity * 2, 1);
|
|
291
|
-
|
|
292
|
-
// Efficiency is inverse of interaction ratio
|
|
293
|
-
const efficiency = Math.min(expectedInteractions / session.metrics.interactions, 1);
|
|
294
|
-
|
|
295
|
-
return efficiency;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Estimate task complexity
|
|
300
|
-
*/
|
|
301
|
-
estimateTaskComplexity(task) {
|
|
302
|
-
let complexity = 1; // Base complexity
|
|
303
|
-
|
|
304
|
-
// Increase based on task type
|
|
305
|
-
const complexTypes = ['debug', 'optimize', 'architect', 'refactor'];
|
|
306
|
-
if (complexTypes.includes(task.type)) {
|
|
307
|
-
complexity += 1;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Increase based on keywords
|
|
311
|
-
const complexKeywords = ['complex', 'difficult', 'large', 'entire', 'all'];
|
|
312
|
-
const description = (task.description || '').toLowerCase();
|
|
313
|
-
complexKeywords.forEach(keyword => {
|
|
314
|
-
if (description.includes(keyword)) {
|
|
315
|
-
complexity += 0.5;
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
// Cap at 5
|
|
320
|
-
return Math.min(complexity, 5);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Update persona performance metrics
|
|
325
|
-
*/
|
|
326
|
-
updatePersonaPerformance(session) {
|
|
327
|
-
const personaName = session.persona;
|
|
328
|
-
|
|
329
|
-
if (!this.metrics.personaPerformance[personaName]) {
|
|
330
|
-
this.metrics.personaPerformance[personaName] = {
|
|
331
|
-
totalSessions: 0,
|
|
332
|
-
completedSessions: 0,
|
|
333
|
-
totalDuration: 0,
|
|
334
|
-
totalTokens: 0,
|
|
335
|
-
totalErrors: 0,
|
|
336
|
-
qualityScores: [],
|
|
337
|
-
taskTypes: {}
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
const perf = this.metrics.personaPerformance[personaName];
|
|
342
|
-
|
|
343
|
-
perf.totalSessions++;
|
|
344
|
-
if (session.metrics.completionStatus === 'completed') {
|
|
345
|
-
perf.completedSessions++;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
perf.totalDuration += session.duration;
|
|
349
|
-
|
|
350
|
-
const tokensUsed = (session.metrics.tokenUsage.end || 0) -
|
|
351
|
-
(session.metrics.tokenUsage.start || 0);
|
|
352
|
-
perf.totalTokens += tokensUsed;
|
|
353
|
-
|
|
354
|
-
perf.totalErrors += session.metrics.errors;
|
|
355
|
-
|
|
356
|
-
if (session.qualityMetrics) {
|
|
357
|
-
perf.qualityScores.push(session.qualityMetrics.overallScore);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Track task types
|
|
361
|
-
const taskType = session.task.type;
|
|
362
|
-
perf.taskTypes[taskType] = (perf.taskTypes[taskType] || 0) + 1;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Get current token count (simulated)
|
|
367
|
-
*/
|
|
368
|
-
getCurrentTokenCount() {
|
|
369
|
-
// In a real implementation, this would interface with the token tracking system
|
|
370
|
-
// For now, return a simulated increasing value
|
|
371
|
-
return Math.floor((Date.now() - this.startTime) / 100);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Collect current metrics
|
|
376
|
-
*/
|
|
377
|
-
async collectCurrentMetrics() {
|
|
378
|
-
if (!this.currentSession) return null;
|
|
379
|
-
|
|
380
|
-
return {
|
|
381
|
-
duration: Date.now() - this.currentSession.startTime,
|
|
382
|
-
interactions: this.currentSession.metrics.interactions,
|
|
383
|
-
errors: this.currentSession.metrics.errors,
|
|
384
|
-
tokenUsage: {
|
|
385
|
-
current: this.getCurrentTokenCount() - this.currentSession.metrics.tokenUsage.start,
|
|
386
|
-
contextSwitches: this.currentSession.metrics.tokenUsage.contextSwitches
|
|
387
|
-
}
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* Get persona history
|
|
393
|
-
*/
|
|
394
|
-
async getPersonaHistory(personaName) {
|
|
395
|
-
return this.metrics.sessions.filter(
|
|
396
|
-
session => session.persona === personaName
|
|
397
|
-
);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* Get persona statistics
|
|
402
|
-
*/
|
|
403
|
-
async getPersonaStats(personaName) {
|
|
404
|
-
const perf = this.metrics.personaPerformance[personaName];
|
|
405
|
-
|
|
406
|
-
if (!perf || perf.totalSessions === 0) {
|
|
407
|
-
return {
|
|
408
|
-
message: `No data available for persona: ${personaName}`
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
const avgQualityScore = perf.qualityScores.length > 0 ?
|
|
413
|
-
perf.qualityScores.reduce((a, b) => a + b, 0) / perf.qualityScores.length : 0;
|
|
414
|
-
|
|
415
|
-
return {
|
|
416
|
-
totalSessions: perf.totalSessions,
|
|
417
|
-
completionRate: perf.completedSessions / perf.totalSessions,
|
|
418
|
-
averageDuration: perf.totalDuration / perf.totalSessions,
|
|
419
|
-
averageTokens: perf.totalTokens / perf.totalSessions,
|
|
420
|
-
errorRate: perf.totalErrors / perf.totalSessions,
|
|
421
|
-
averageQualityScore: avgQualityScore,
|
|
422
|
-
taskDistribution: perf.taskTypes
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Generate comprehensive report
|
|
428
|
-
*/
|
|
429
|
-
async generateReport(options = {}) {
|
|
430
|
-
const timeRange = options.timeRange || 'all';
|
|
431
|
-
const startTime = this.getStartTime(timeRange);
|
|
432
|
-
|
|
433
|
-
// Filter sessions by time range
|
|
434
|
-
const sessions = this.metrics.sessions.filter(
|
|
435
|
-
session => session.startTime >= startTime
|
|
436
|
-
);
|
|
437
|
-
|
|
438
|
-
const report = {
|
|
439
|
-
generatedAt: new Date().toISOString(),
|
|
440
|
-
timeRange,
|
|
441
|
-
summary: {
|
|
442
|
-
totalSessions: sessions.length,
|
|
443
|
-
averageCompletionTime: this.calculateAverageTime(sessions),
|
|
444
|
-
personaUsage: this.getPersonaUsageStats(sessions),
|
|
445
|
-
qualityTrend: this.calculateQualityTrend(sessions)
|
|
446
|
-
},
|
|
447
|
-
personaAnalysis: this.analyzePersonaEffectiveness(sessions),
|
|
448
|
-
taskAnalysis: this.analyzeTaskTypes(sessions),
|
|
449
|
-
recommendations: this.generateRecommendations(sessions)
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
return report;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* Get start time for time range
|
|
457
|
-
*/
|
|
458
|
-
getStartTime(timeRange) {
|
|
459
|
-
const now = Date.now();
|
|
460
|
-
|
|
461
|
-
switch (timeRange) {
|
|
462
|
-
case 'hour':
|
|
463
|
-
return now - 3600000;
|
|
464
|
-
case 'day':
|
|
465
|
-
return now - 86400000;
|
|
466
|
-
case 'week':
|
|
467
|
-
return now - 604800000;
|
|
468
|
-
case 'month':
|
|
469
|
-
return now - 2592000000;
|
|
470
|
-
default:
|
|
471
|
-
return 0;
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* Calculate average completion time
|
|
477
|
-
*/
|
|
478
|
-
calculateAverageTime(sessions) {
|
|
479
|
-
if (sessions.length === 0) return 0;
|
|
480
|
-
|
|
481
|
-
const totalTime = sessions.reduce((sum, session) => sum + session.duration, 0);
|
|
482
|
-
return totalTime / sessions.length;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* Get persona usage statistics
|
|
487
|
-
*/
|
|
488
|
-
getPersonaUsageStats(sessions) {
|
|
489
|
-
const usage = {};
|
|
490
|
-
|
|
491
|
-
sessions.forEach(session => {
|
|
492
|
-
usage[session.persona] = (usage[session.persona] || 0) + 1;
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
// Convert to percentages
|
|
496
|
-
const total = sessions.length;
|
|
497
|
-
const stats = {};
|
|
498
|
-
|
|
499
|
-
for (const [persona, count] of Object.entries(usage)) {
|
|
500
|
-
stats[persona] = {
|
|
501
|
-
count,
|
|
502
|
-
percentage: (count / total) * 100
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
return stats;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
/**
|
|
510
|
-
* Calculate quality trend
|
|
511
|
-
*/
|
|
512
|
-
calculateQualityTrend(sessions) {
|
|
513
|
-
if (sessions.length < 2) {
|
|
514
|
-
return { trend: 'insufficient_data' };
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
// Get quality scores over time
|
|
518
|
-
const scores = sessions
|
|
519
|
-
.filter(s => s.qualityMetrics)
|
|
520
|
-
.map(s => ({
|
|
521
|
-
time: s.startTime,
|
|
522
|
-
score: s.qualityMetrics.overallScore
|
|
523
|
-
}))
|
|
524
|
-
.sort((a, b) => a.time - b.time);
|
|
525
|
-
|
|
526
|
-
if (scores.length < 2) {
|
|
527
|
-
return { trend: 'insufficient_data' };
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
// Calculate trend (simple linear regression)
|
|
531
|
-
const n = scores.length;
|
|
532
|
-
const sumX = scores.reduce((sum, s, i) => sum + i, 0);
|
|
533
|
-
const sumY = scores.reduce((sum, s) => sum + s.score, 0);
|
|
534
|
-
const sumXY = scores.reduce((sum, s, i) => sum + i * s.score, 0);
|
|
535
|
-
const sumX2 = scores.reduce((sum, s, i) => sum + i * i, 0);
|
|
536
|
-
|
|
537
|
-
const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
|
|
538
|
-
|
|
539
|
-
return {
|
|
540
|
-
trend: slope > 0.01 ? 'improving' : slope < -0.01 ? 'declining' : 'stable',
|
|
541
|
-
slope,
|
|
542
|
-
latestScore: scores[scores.length - 1].score,
|
|
543
|
-
averageScore: sumY / n
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
/**
|
|
548
|
-
* Analyze persona effectiveness
|
|
549
|
-
*/
|
|
550
|
-
analyzePersonaEffectiveness(sessions) {
|
|
551
|
-
const analysis = {};
|
|
552
|
-
|
|
553
|
-
// Group sessions by persona
|
|
554
|
-
const personaSessions = {};
|
|
555
|
-
sessions.forEach(session => {
|
|
556
|
-
if (!personaSessions[session.persona]) {
|
|
557
|
-
personaSessions[session.persona] = [];
|
|
558
|
-
}
|
|
559
|
-
personaSessions[session.persona].push(session);
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
// Analyze each persona
|
|
563
|
-
for (const [persona, personaSessions] of Object.entries(personaSessions)) {
|
|
564
|
-
const completed = personaSessions.filter(s => s.metrics.completionStatus === 'completed');
|
|
565
|
-
const qualityScores = personaSessions
|
|
566
|
-
.filter(s => s.qualityMetrics)
|
|
567
|
-
.map(s => s.qualityMetrics.overallScore);
|
|
568
|
-
|
|
569
|
-
analysis[persona] = {
|
|
570
|
-
sessionCount: personaSessions.length,
|
|
571
|
-
completionRate: completed.length / personaSessions.length,
|
|
572
|
-
averageCompletionTime: this.calculateAverageTime(personaSessions),
|
|
573
|
-
averageQualityScore: qualityScores.length > 0 ?
|
|
574
|
-
qualityScores.reduce((a, b) => a + b, 0) / qualityScores.length : 0,
|
|
575
|
-
tokenEfficiency: this.calculateAverageTokenEfficiency(personaSessions),
|
|
576
|
-
bestUseCases: this.identifyBestUseCases(persona, personaSessions),
|
|
577
|
-
improvementAreas: this.identifyImprovementAreas(persona, personaSessions)
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
return analysis;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
/**
|
|
585
|
-
* Calculate average token efficiency
|
|
586
|
-
*/
|
|
587
|
-
calculateAverageTokenEfficiency(sessions) {
|
|
588
|
-
const efficiencies = sessions
|
|
589
|
-
.filter(s => s.qualityMetrics)
|
|
590
|
-
.map(s => s.qualityMetrics.tokenEfficiency);
|
|
591
|
-
|
|
592
|
-
if (efficiencies.length === 0) return 0;
|
|
593
|
-
|
|
594
|
-
return efficiencies.reduce((a, b) => a + b, 0) / efficiencies.length;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
/**
|
|
598
|
-
* Identify best use cases for persona
|
|
599
|
-
*/
|
|
600
|
-
identifyBestUseCases(persona, sessions) {
|
|
601
|
-
// Group by task type and calculate success rate
|
|
602
|
-
const taskStats = {};
|
|
603
|
-
|
|
604
|
-
sessions.forEach(session => {
|
|
605
|
-
const taskType = session.task.type;
|
|
606
|
-
if (!taskStats[taskType]) {
|
|
607
|
-
taskStats[taskType] = {
|
|
608
|
-
total: 0,
|
|
609
|
-
successful: 0,
|
|
610
|
-
avgQuality: 0,
|
|
611
|
-
qualityCount: 0
|
|
612
|
-
};
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
taskStats[taskType].total++;
|
|
616
|
-
|
|
617
|
-
if (session.metrics.completionStatus === 'completed') {
|
|
618
|
-
taskStats[taskType].successful++;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
if (session.qualityMetrics) {
|
|
622
|
-
taskStats[taskType].avgQuality += session.qualityMetrics.overallScore;
|
|
623
|
-
taskStats[taskType].qualityCount++;
|
|
624
|
-
}
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
// Calculate success rates and find best
|
|
628
|
-
const taskScores = Object.entries(taskStats).map(([type, stats]) => ({
|
|
629
|
-
type,
|
|
630
|
-
successRate: stats.successful / stats.total,
|
|
631
|
-
avgQuality: stats.qualityCount > 0 ? stats.avgQuality / stats.qualityCount : 0,
|
|
632
|
-
count: stats.total
|
|
633
|
-
}));
|
|
634
|
-
|
|
635
|
-
// Return top 3 task types
|
|
636
|
-
return taskScores
|
|
637
|
-
.filter(t => t.count >= 2) // Minimum sample size
|
|
638
|
-
.sort((a, b) => (b.successRate + b.avgQuality) - (a.successRate + a.avgQuality))
|
|
639
|
-
.slice(0, 3)
|
|
640
|
-
.map(t => t.type);
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
/**
|
|
644
|
-
* Identify improvement areas
|
|
645
|
-
*/
|
|
646
|
-
identifyImprovementAreas(persona, sessions) {
|
|
647
|
-
const areas = [];
|
|
648
|
-
|
|
649
|
-
// Check completion rate
|
|
650
|
-
const completionRate = sessions.filter(s =>
|
|
651
|
-
s.metrics.completionStatus === 'completed'
|
|
652
|
-
).length / sessions.length;
|
|
653
|
-
|
|
654
|
-
if (completionRate < 0.8) {
|
|
655
|
-
areas.push('completion_rate');
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
// Check error rate
|
|
659
|
-
const avgErrors = sessions.reduce((sum, s) =>
|
|
660
|
-
sum + s.metrics.errors, 0
|
|
661
|
-
) / sessions.length;
|
|
662
|
-
|
|
663
|
-
if (avgErrors > 1) {
|
|
664
|
-
areas.push('error_rate');
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
// Check token efficiency
|
|
668
|
-
const avgTokenEfficiency = this.calculateAverageTokenEfficiency(sessions);
|
|
669
|
-
if (avgTokenEfficiency < 0.5) {
|
|
670
|
-
areas.push('token_efficiency');
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
return areas;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
/**
|
|
677
|
-
* Analyze task types
|
|
678
|
-
*/
|
|
679
|
-
analyzeTaskTypes(sessions) {
|
|
680
|
-
const taskAnalysis = {};
|
|
681
|
-
|
|
682
|
-
sessions.forEach(session => {
|
|
683
|
-
const taskType = session.task.type;
|
|
684
|
-
|
|
685
|
-
if (!taskAnalysis[taskType]) {
|
|
686
|
-
taskAnalysis[taskType] = {
|
|
687
|
-
count: 0,
|
|
688
|
-
totalDuration: 0,
|
|
689
|
-
completedCount: 0,
|
|
690
|
-
totalQuality: 0,
|
|
691
|
-
qualityCount: 0,
|
|
692
|
-
personas: {}
|
|
693
|
-
};
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
const analysis = taskAnalysis[taskType];
|
|
697
|
-
analysis.count++;
|
|
698
|
-
analysis.totalDuration += session.duration;
|
|
699
|
-
|
|
700
|
-
if (session.metrics.completionStatus === 'completed') {
|
|
701
|
-
analysis.completedCount++;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
if (session.qualityMetrics) {
|
|
705
|
-
analysis.totalQuality += session.qualityMetrics.overallScore;
|
|
706
|
-
analysis.qualityCount++;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// Track which personas handle this task type
|
|
710
|
-
analysis.personas[session.persona] = (analysis.personas[session.persona] || 0) + 1;
|
|
711
|
-
});
|
|
712
|
-
|
|
713
|
-
// Calculate averages
|
|
714
|
-
for (const [taskType, analysis] of Object.entries(taskAnalysis)) {
|
|
715
|
-
analysis.averageDuration = analysis.totalDuration / analysis.count;
|
|
716
|
-
analysis.completionRate = analysis.completedCount / analysis.count;
|
|
717
|
-
analysis.averageQuality = analysis.qualityCount > 0 ?
|
|
718
|
-
analysis.totalQuality / analysis.qualityCount : 0;
|
|
719
|
-
|
|
720
|
-
// Find best persona for this task type
|
|
721
|
-
analysis.bestPersona = Object.entries(analysis.personas)
|
|
722
|
-
.sort(([,a], [,b]) => b - a)[0]?.[0];
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
return taskAnalysis;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
/**
|
|
729
|
-
* Generate recommendations
|
|
730
|
-
*/
|
|
731
|
-
generateRecommendations(sessions) {
|
|
732
|
-
const recommendations = [];
|
|
733
|
-
|
|
734
|
-
// Analyze overall patterns
|
|
735
|
-
const personaAnalysis = this.analyzePersonaEffectiveness(sessions);
|
|
736
|
-
const taskAnalysis = this.analyzeTaskTypes(sessions);
|
|
737
|
-
|
|
738
|
-
// Recommendation 1: Underutilized personas
|
|
739
|
-
for (const [persona, stats] of Object.entries(personaAnalysis)) {
|
|
740
|
-
if (stats.sessionCount < sessions.length * 0.1) {
|
|
741
|
-
recommendations.push({
|
|
742
|
-
type: 'persona_usage',
|
|
743
|
-
priority: 'medium',
|
|
744
|
-
message: `Consider using ${persona} persona more often for ${stats.bestUseCases.join(', ')} tasks`,
|
|
745
|
-
data: { persona, usage: stats.sessionCount }
|
|
746
|
-
});
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
// Recommendation 2: Task-persona mismatches
|
|
751
|
-
for (const [taskType, analysis] of Object.entries(taskAnalysis)) {
|
|
752
|
-
if (analysis.averageQuality < 0.6 && analysis.count > 3) {
|
|
753
|
-
recommendations.push({
|
|
754
|
-
type: 'task_persona_match',
|
|
755
|
-
priority: 'high',
|
|
756
|
-
message: `${taskType} tasks have low quality scores. Try using ${analysis.bestPersona} persona`,
|
|
757
|
-
data: { taskType, currentQuality: analysis.averageQuality }
|
|
758
|
-
});
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
// Recommendation 3: High error rates
|
|
763
|
-
for (const [persona, stats] of Object.entries(personaAnalysis)) {
|
|
764
|
-
if (stats.improvementAreas.includes('error_rate')) {
|
|
765
|
-
recommendations.push({
|
|
766
|
-
type: 'error_rate',
|
|
767
|
-
priority: 'high',
|
|
768
|
-
message: `${persona} persona has high error rate. Review context rules and tool recommendations`,
|
|
769
|
-
data: { persona, areas: stats.improvementAreas }
|
|
770
|
-
});
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
return recommendations;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
/**
|
|
778
|
-
* Load metrics from disk
|
|
779
|
-
*/
|
|
780
|
-
async loadMetrics() {
|
|
781
|
-
try {
|
|
782
|
-
const metricsFile = path.join(this.options.metricsPath, 'metrics.json');
|
|
783
|
-
const data = await fs.readFile(metricsFile, 'utf-8');
|
|
784
|
-
const loaded = JSON.parse(data);
|
|
785
|
-
|
|
786
|
-
// Merge with current metrics
|
|
787
|
-
this.metrics = {
|
|
788
|
-
...this.metrics,
|
|
789
|
-
...loaded,
|
|
790
|
-
sessions: [...(loaded.sessions || []), ...this.metrics.sessions]
|
|
791
|
-
};
|
|
792
|
-
|
|
793
|
-
// Limit to max history size
|
|
794
|
-
if (this.metrics.sessions.length > this.options.maxHistorySize) {
|
|
795
|
-
this.metrics.sessions = this.metrics.sessions.slice(-this.options.maxHistorySize);
|
|
796
|
-
}
|
|
797
|
-
} catch (error) {
|
|
798
|
-
// No existing metrics or error loading - start fresh
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
/**
|
|
803
|
-
* Persist metrics to disk
|
|
804
|
-
*/
|
|
805
|
-
async persistMetrics() {
|
|
806
|
-
try {
|
|
807
|
-
const metricsFile = path.join(this.options.metricsPath, 'metrics.json');
|
|
808
|
-
await fs.writeFile(
|
|
809
|
-
metricsFile,
|
|
810
|
-
JSON.stringify(this.metrics, null, 2),
|
|
811
|
-
'utf-8'
|
|
812
|
-
);
|
|
813
|
-
|
|
814
|
-
this.emit('metricsPersisted', {
|
|
815
|
-
sessionCount: this.metrics.sessions.length
|
|
816
|
-
});
|
|
817
|
-
} catch (error) {
|
|
818
|
-
this.emit('error', { error, phase: 'persist' });
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
/**
|
|
823
|
-
* Start persist timer
|
|
824
|
-
*/
|
|
825
|
-
startPersistTimer() {
|
|
826
|
-
this.persistTimer = setInterval(
|
|
827
|
-
() => this.persistMetrics(),
|
|
828
|
-
this.options.persistInterval
|
|
829
|
-
);
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
/**
|
|
833
|
-
* Generate session ID
|
|
834
|
-
*/
|
|
835
|
-
generateSessionId() {
|
|
836
|
-
return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
/**
|
|
840
|
-
* Get current session metrics
|
|
841
|
-
*/
|
|
842
|
-
async getCurrentSessionMetrics() {
|
|
843
|
-
if (!this.currentSession) {
|
|
844
|
-
return { message: 'No active session' };
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
return await this.collectCurrentMetrics();
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
/**
|
|
851
|
-
* Reset metrics
|
|
852
|
-
*/
|
|
853
|
-
async reset() {
|
|
854
|
-
this.metrics = {
|
|
855
|
-
sessions: [],
|
|
856
|
-
personaPerformance: {},
|
|
857
|
-
taskOutcomes: [],
|
|
858
|
-
aggregated: {}
|
|
859
|
-
};
|
|
860
|
-
|
|
861
|
-
this.currentSession = null;
|
|
862
|
-
|
|
863
|
-
await this.persistMetrics();
|
|
864
|
-
|
|
865
|
-
this.emit('reset');
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
/**
|
|
869
|
-
* Cleanup resources
|
|
870
|
-
*/
|
|
871
|
-
async cleanup() {
|
|
872
|
-
if (this.persistTimer) {
|
|
873
|
-
clearInterval(this.persistTimer);
|
|
874
|
-
this.persistTimer = null;
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
// Persist final metrics
|
|
878
|
-
await this.persistMetrics();
|
|
879
|
-
|
|
880
|
-
this.removeAllListeners();
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
export default MetricsCollector;
|
|
885
|
-
export { MetricsCollector };
|