@baselineos/persona 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +14 -0
- package/.turbo/turbo-test.log +250 -0
- package/LICENSE +17 -0
- package/README.md +19 -0
- package/dist/index.d.ts +530 -0
- package/dist/index.js +2445 -0
- package/package.json +34 -0
- package/src/__tests__/persona-ui.test.ts +336 -0
- package/src/__tests__/smoke.test.ts +16 -0
- package/src/engine.ts +1196 -0
- package/src/index.ts +33 -0
- package/src/personas/agile-pm.ts +399 -0
- package/src/personas/dev-lead.ts +654 -0
- package/src/ui.ts +1042 -0
- package/tsconfig.json +8 -0
package/src/engine.ts
ADDED
|
@@ -0,0 +1,1196 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
|
|
4
|
+
// ─── Type Definitions ────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
export interface StudioConfig {
|
|
7
|
+
interface: {
|
|
8
|
+
layout: string;
|
|
9
|
+
colorScheme: string;
|
|
10
|
+
typography: string;
|
|
11
|
+
visualElements: string[];
|
|
12
|
+
};
|
|
13
|
+
lexicon: {
|
|
14
|
+
terminology: string[];
|
|
15
|
+
language: string;
|
|
16
|
+
abbreviations: string[];
|
|
17
|
+
};
|
|
18
|
+
frames: Record<string, string[]>;
|
|
19
|
+
templates: Record<string, string[]>;
|
|
20
|
+
workflows: Record<string, string[]>;
|
|
21
|
+
aiModels: Record<string, string[]>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface AgilePMConfig {
|
|
25
|
+
sprints: {
|
|
26
|
+
methodology: string;
|
|
27
|
+
cycles: string[];
|
|
28
|
+
duration: string;
|
|
29
|
+
};
|
|
30
|
+
userStories: Record<string, string[]>;
|
|
31
|
+
backlog: Record<string, string[]>;
|
|
32
|
+
analytics: Record<string, string[]>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface PersonaConfig {
|
|
36
|
+
name: string;
|
|
37
|
+
description: string;
|
|
38
|
+
focus: string[];
|
|
39
|
+
studio: StudioConfig;
|
|
40
|
+
agilePM: AgilePMConfig;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface Persona extends PersonaConfig {
|
|
44
|
+
id: string;
|
|
45
|
+
createdAt: Date;
|
|
46
|
+
lastModified: Date;
|
|
47
|
+
usageCount: number;
|
|
48
|
+
adaptationScore: number;
|
|
49
|
+
customizations: Map<string, Record<string, unknown>>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface ActivePersona extends Persona {
|
|
53
|
+
userId: string;
|
|
54
|
+
activatedAt: Date;
|
|
55
|
+
sessionId: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface BehaviorData {
|
|
59
|
+
type: string;
|
|
60
|
+
timestamp?: Date;
|
|
61
|
+
[key: string]: unknown;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface BehaviorPattern {
|
|
65
|
+
type: string;
|
|
66
|
+
confidence: number;
|
|
67
|
+
data: BehaviorData[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface PersonaEngineConfig {
|
|
71
|
+
enableAutonomousLearning?: boolean;
|
|
72
|
+
enableBehavioralAnalysis?: boolean;
|
|
73
|
+
enablePredictiveCustomization?: boolean;
|
|
74
|
+
enableCrossPersonaSharing?: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface PersonaAdaptation {
|
|
78
|
+
id: string;
|
|
79
|
+
personaId: string;
|
|
80
|
+
userId: string;
|
|
81
|
+
type: string;
|
|
82
|
+
changes: Record<string, unknown>;
|
|
83
|
+
confidence: number;
|
|
84
|
+
appliedAt: Date;
|
|
85
|
+
source: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface PersonaRecommendation {
|
|
89
|
+
personaId: string;
|
|
90
|
+
score: number;
|
|
91
|
+
reasoning: string;
|
|
92
|
+
fitScores: {
|
|
93
|
+
pattern: number;
|
|
94
|
+
context: number;
|
|
95
|
+
persona: number;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface PersonaAnalytics {
|
|
100
|
+
totalPersonas: number;
|
|
101
|
+
activePersonas: number;
|
|
102
|
+
totalBehaviors: number;
|
|
103
|
+
behaviorsByType: Record<string, number>;
|
|
104
|
+
totalAdaptations: number;
|
|
105
|
+
adaptationsByPersona: Record<string, number>;
|
|
106
|
+
adaptationsByUser: Record<string, number>;
|
|
107
|
+
averageAdaptationScore: number;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface PersonaStatus {
|
|
111
|
+
initialized: boolean;
|
|
112
|
+
totalPersonas: number;
|
|
113
|
+
activePersonas: number;
|
|
114
|
+
behaviorDataPoints: number;
|
|
115
|
+
adaptationsApplied: number;
|
|
116
|
+
config: PersonaEngineConfig;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ─── Core Persona Engine ─────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
export class BaselinePersonaEngine extends EventEmitter {
|
|
122
|
+
private personas: Map<string, Persona>;
|
|
123
|
+
private activePersonas: Map<string, ActivePersona>;
|
|
124
|
+
private behaviorData: Map<string, BehaviorData[]>;
|
|
125
|
+
private adaptations: PersonaAdaptation[];
|
|
126
|
+
private config: PersonaEngineConfig;
|
|
127
|
+
private initialized: boolean;
|
|
128
|
+
|
|
129
|
+
constructor(config: PersonaEngineConfig = {}) {
|
|
130
|
+
super();
|
|
131
|
+
this.personas = new Map();
|
|
132
|
+
this.activePersonas = new Map();
|
|
133
|
+
this.behaviorData = new Map();
|
|
134
|
+
this.adaptations = [];
|
|
135
|
+
this.config = {
|
|
136
|
+
enableAutonomousLearning: config.enableAutonomousLearning ?? true,
|
|
137
|
+
enableBehavioralAnalysis: config.enableBehavioralAnalysis ?? true,
|
|
138
|
+
enablePredictiveCustomization: config.enablePredictiveCustomization ?? true,
|
|
139
|
+
enableCrossPersonaSharing: config.enableCrossPersonaSharing ?? false,
|
|
140
|
+
};
|
|
141
|
+
this.initialized = false;
|
|
142
|
+
this.initializeCorePersonas();
|
|
143
|
+
this.initializeEventListeners();
|
|
144
|
+
this.initialized = true;
|
|
145
|
+
console.log('[BaselinePersonaEngine] Persona engine initialized with', this.personas.size, 'core personas');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ─── Persona Initialization ──────────────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
private initializeCorePersonas(): void {
|
|
151
|
+
const corePersonas: Record<string, PersonaConfig> = {
|
|
152
|
+
designer: {
|
|
153
|
+
name: 'Designer',
|
|
154
|
+
description: 'Visual and UX designer focused on creating beautiful, intuitive interfaces and experiences',
|
|
155
|
+
focus: ['visual-design', 'ux-research', 'prototyping', 'design-systems', 'accessibility'],
|
|
156
|
+
studio: {
|
|
157
|
+
interface: {
|
|
158
|
+
layout: 'canvas-centric',
|
|
159
|
+
colorScheme: 'creative-vibrant',
|
|
160
|
+
typography: 'modern-sans',
|
|
161
|
+
visualElements: ['color-picker', 'layer-panel', 'asset-library', 'component-inspector', 'design-tokens'],
|
|
162
|
+
},
|
|
163
|
+
lexicon: {
|
|
164
|
+
terminology: ['wireframe', 'mockup', 'prototype', 'design-system', 'component-library', 'style-guide', 'user-flow', 'information-architecture'],
|
|
165
|
+
language: 'design-focused',
|
|
166
|
+
abbreviations: ['UX', 'UI', 'IA', 'A11Y', 'DS', 'DT'],
|
|
167
|
+
},
|
|
168
|
+
frames: {
|
|
169
|
+
primary: ['canvas-frame', 'component-library-frame', 'prototype-frame'],
|
|
170
|
+
secondary: ['color-palette-frame', 'typography-frame', 'spacing-frame'],
|
|
171
|
+
utility: ['export-frame', 'handoff-frame', 'feedback-frame'],
|
|
172
|
+
},
|
|
173
|
+
templates: {
|
|
174
|
+
layouts: ['responsive-grid', 'mobile-first', 'desktop-canvas', 'tablet-adaptive'],
|
|
175
|
+
components: ['button-system', 'form-elements', 'navigation-patterns', 'card-layouts'],
|
|
176
|
+
pages: ['landing-page', 'dashboard', 'settings-page', 'onboarding-flow'],
|
|
177
|
+
},
|
|
178
|
+
workflows: {
|
|
179
|
+
design: ['research-ideate-design-test', 'sketch-wireframe-mockup-prototype'],
|
|
180
|
+
review: ['design-critique', 'stakeholder-review', 'usability-testing'],
|
|
181
|
+
handoff: ['spec-generation', 'asset-export', 'developer-handoff'],
|
|
182
|
+
},
|
|
183
|
+
aiModels: {
|
|
184
|
+
generation: ['layout-generator', 'color-palette-ai', 'icon-generator'],
|
|
185
|
+
analysis: ['design-consistency-checker', 'accessibility-auditor', 'visual-hierarchy-analyzer'],
|
|
186
|
+
suggestion: ['component-recommender', 'layout-optimizer', 'design-pattern-matcher'],
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
agilePM: {
|
|
190
|
+
sprints: {
|
|
191
|
+
methodology: 'design-sprint',
|
|
192
|
+
cycles: ['understand', 'diverge', 'converge', 'prototype', 'test'],
|
|
193
|
+
duration: '1-week',
|
|
194
|
+
},
|
|
195
|
+
userStories: {
|
|
196
|
+
format: ['As a user, I want...', 'Design requirement:', 'Visual specification:'],
|
|
197
|
+
priorities: ['must-have', 'should-have', 'nice-to-have'],
|
|
198
|
+
categories: ['ux-improvement', 'visual-design', 'accessibility', 'responsive'],
|
|
199
|
+
},
|
|
200
|
+
backlog: {
|
|
201
|
+
categories: ['design-tasks', 'research-tasks', 'prototype-tasks', 'review-tasks'],
|
|
202
|
+
priorities: ['critical', 'high', 'medium', 'low'],
|
|
203
|
+
estimations: ['story-points', 'design-complexity', 'research-hours'],
|
|
204
|
+
},
|
|
205
|
+
analytics: {
|
|
206
|
+
metrics: ['design-velocity', 'iteration-count', 'user-satisfaction', 'accessibility-score'],
|
|
207
|
+
reports: ['sprint-design-report', 'ux-metrics-dashboard', 'design-debt-tracker'],
|
|
208
|
+
tracking: ['task-completion', 'review-cycles', 'design-quality'],
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
developer: {
|
|
214
|
+
name: 'Developer',
|
|
215
|
+
description: 'Software developer focused on building robust, scalable applications with clean code',
|
|
216
|
+
focus: ['coding', 'architecture', 'testing', 'deployment', 'performance'],
|
|
217
|
+
studio: {
|
|
218
|
+
interface: {
|
|
219
|
+
layout: 'code-centric',
|
|
220
|
+
colorScheme: 'dark-professional',
|
|
221
|
+
typography: 'monospace-primary',
|
|
222
|
+
visualElements: ['code-editor', 'terminal-panel', 'file-explorer', 'git-panel', 'debugger'],
|
|
223
|
+
},
|
|
224
|
+
lexicon: {
|
|
225
|
+
terminology: ['refactor', 'deploy', 'CI/CD', 'microservice', 'API', 'SDK', 'middleware', 'ORM'],
|
|
226
|
+
language: 'technical',
|
|
227
|
+
abbreviations: ['API', 'SDK', 'CI', 'CD', 'PR', 'MR', 'TDD', 'BDD'],
|
|
228
|
+
},
|
|
229
|
+
frames: {
|
|
230
|
+
primary: ['code-editor-frame', 'terminal-frame', 'debug-frame'],
|
|
231
|
+
secondary: ['git-history-frame', 'dependency-frame', 'test-runner-frame'],
|
|
232
|
+
utility: ['documentation-frame', 'api-explorer-frame', 'performance-frame'],
|
|
233
|
+
},
|
|
234
|
+
templates: {
|
|
235
|
+
code: ['microservice-template', 'api-gateway', 'worker-service', 'cli-tool'],
|
|
236
|
+
testing: ['unit-test-suite', 'integration-test', 'e2e-test', 'load-test'],
|
|
237
|
+
deployment: ['docker-compose', 'kubernetes-manifest', 'serverless-config', 'ci-pipeline'],
|
|
238
|
+
},
|
|
239
|
+
workflows: {
|
|
240
|
+
development: ['branch-develop-test-merge', 'trunk-based-development'],
|
|
241
|
+
review: ['code-review', 'pair-programming', 'mob-programming'],
|
|
242
|
+
deployment: ['blue-green', 'canary', 'rolling-update', 'feature-flags'],
|
|
243
|
+
},
|
|
244
|
+
aiModels: {
|
|
245
|
+
generation: ['code-generator', 'test-generator', 'documentation-generator'],
|
|
246
|
+
analysis: ['code-quality-analyzer', 'security-scanner', 'performance-profiler'],
|
|
247
|
+
suggestion: ['refactoring-advisor', 'architecture-recommender', 'dependency-checker'],
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
agilePM: {
|
|
251
|
+
sprints: {
|
|
252
|
+
methodology: 'scrum',
|
|
253
|
+
cycles: ['planning', 'daily-standup', 'development', 'review', 'retrospective'],
|
|
254
|
+
duration: '2-weeks',
|
|
255
|
+
},
|
|
256
|
+
userStories: {
|
|
257
|
+
format: ['As a developer, I want...', 'Technical requirement:', 'Bug fix:'],
|
|
258
|
+
priorities: ['blocker', 'critical', 'major', 'minor'],
|
|
259
|
+
categories: ['feature', 'bug-fix', 'tech-debt', 'infrastructure'],
|
|
260
|
+
},
|
|
261
|
+
backlog: {
|
|
262
|
+
categories: ['features', 'bugs', 'tech-debt', 'infrastructure', 'documentation'],
|
|
263
|
+
priorities: ['critical', 'high', 'medium', 'low'],
|
|
264
|
+
estimations: ['story-points', 'fibonacci', 't-shirt-sizing'],
|
|
265
|
+
},
|
|
266
|
+
analytics: {
|
|
267
|
+
metrics: ['velocity', 'bug-rate', 'code-coverage', 'deployment-frequency'],
|
|
268
|
+
reports: ['sprint-burndown', 'velocity-chart', 'cumulative-flow', 'defect-density'],
|
|
269
|
+
tracking: ['task-hours', 'story-completion', 'sprint-goal-achievement'],
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
business_analyst: {
|
|
275
|
+
name: 'Business Analyst',
|
|
276
|
+
description: 'Business analyst focused on requirements gathering, process optimization, and stakeholder management',
|
|
277
|
+
focus: ['requirements', 'process-analysis', 'stakeholder-management', 'data-analysis', 'documentation'],
|
|
278
|
+
studio: {
|
|
279
|
+
interface: {
|
|
280
|
+
layout: 'document-centric',
|
|
281
|
+
colorScheme: 'professional-neutral',
|
|
282
|
+
typography: 'serif-readable',
|
|
283
|
+
visualElements: ['document-editor', 'diagram-tool', 'spreadsheet', 'stakeholder-map', 'process-modeler'],
|
|
284
|
+
},
|
|
285
|
+
lexicon: {
|
|
286
|
+
terminology: ['requirement', 'stakeholder', 'process-flow', 'use-case', 'acceptance-criteria', 'business-rule', 'gap-analysis', 'feasibility'],
|
|
287
|
+
language: 'business-professional',
|
|
288
|
+
abbreviations: ['BA', 'BRD', 'SRS', 'UAT', 'KPI', 'ROI', 'SWOT'],
|
|
289
|
+
},
|
|
290
|
+
frames: {
|
|
291
|
+
primary: ['requirements-frame', 'process-modeler-frame', 'stakeholder-frame'],
|
|
292
|
+
secondary: ['data-analysis-frame', 'reporting-frame', 'documentation-frame'],
|
|
293
|
+
utility: ['meeting-notes-frame', 'decision-log-frame', 'risk-register-frame'],
|
|
294
|
+
},
|
|
295
|
+
templates: {
|
|
296
|
+
documents: ['business-requirements-doc', 'functional-spec', 'use-case-doc', 'process-map'],
|
|
297
|
+
analysis: ['gap-analysis-template', 'swot-analysis', 'cost-benefit-analysis', 'feasibility-study'],
|
|
298
|
+
presentations: ['stakeholder-presentation', 'project-proposal', 'status-report', 'executive-summary'],
|
|
299
|
+
},
|
|
300
|
+
workflows: {
|
|
301
|
+
requirements: ['elicit-analyze-validate-manage', 'interview-workshop-prototype-review'],
|
|
302
|
+
analysis: ['as-is-to-be-gap-recommendation', 'data-collection-analysis-insight'],
|
|
303
|
+
management: ['stakeholder-identification-analysis-engagement', 'change-impact-assessment'],
|
|
304
|
+
},
|
|
305
|
+
aiModels: {
|
|
306
|
+
generation: ['requirements-generator', 'process-flow-generator', 'report-generator'],
|
|
307
|
+
analysis: ['requirements-conflict-detector', 'process-bottleneck-analyzer', 'stakeholder-sentiment-analyzer'],
|
|
308
|
+
suggestion: ['requirement-prioritizer', 'process-optimizer', 'risk-identifier'],
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
agilePM: {
|
|
312
|
+
sprints: {
|
|
313
|
+
methodology: 'kanban',
|
|
314
|
+
cycles: ['intake', 'analysis', 'validation', 'documentation', 'handoff'],
|
|
315
|
+
duration: 'continuous',
|
|
316
|
+
},
|
|
317
|
+
userStories: {
|
|
318
|
+
format: ['As a business user, I want...', 'Business requirement:', 'Process improvement:'],
|
|
319
|
+
priorities: ['business-critical', 'high-value', 'medium-value', 'low-value'],
|
|
320
|
+
categories: ['new-requirement', 'change-request', 'process-improvement', 'compliance'],
|
|
321
|
+
},
|
|
322
|
+
backlog: {
|
|
323
|
+
categories: ['requirements', 'analysis-tasks', 'documentation', 'reviews', 'meetings'],
|
|
324
|
+
priorities: ['urgent', 'high', 'medium', 'low'],
|
|
325
|
+
estimations: ['effort-hours', 'complexity-rating', 'business-value-score'],
|
|
326
|
+
},
|
|
327
|
+
analytics: {
|
|
328
|
+
metrics: ['requirements-throughput', 'stakeholder-satisfaction', 'defect-leakage', 'requirement-stability'],
|
|
329
|
+
reports: ['requirements-status', 'stakeholder-engagement', 'process-efficiency', 'change-impact'],
|
|
330
|
+
tracking: ['requirement-lifecycle', 'approval-cycle-time', 'rework-rate'],
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
data_scientist: {
|
|
336
|
+
name: 'Data Scientist',
|
|
337
|
+
description: 'Data scientist focused on extracting insights from data through analysis, modeling, and visualization',
|
|
338
|
+
focus: ['data-analysis', 'machine-learning', 'statistical-modeling', 'data-visualization', 'feature-engineering'],
|
|
339
|
+
studio: {
|
|
340
|
+
interface: {
|
|
341
|
+
layout: 'notebook-centric',
|
|
342
|
+
colorScheme: 'data-analytical',
|
|
343
|
+
typography: 'technical-readable',
|
|
344
|
+
visualElements: ['notebook-editor', 'data-explorer', 'chart-builder', 'model-inspector', 'pipeline-viewer'],
|
|
345
|
+
},
|
|
346
|
+
lexicon: {
|
|
347
|
+
terminology: ['feature-engineering', 'model-training', 'hyperparameter', 'cross-validation', 'dimensionality-reduction', 'ensemble', 'gradient-descent', 'overfitting'],
|
|
348
|
+
language: 'scientific-technical',
|
|
349
|
+
abbreviations: ['ML', 'DL', 'NLP', 'CNN', 'RNN', 'GAN', 'PCA', 'AUC'],
|
|
350
|
+
},
|
|
351
|
+
frames: {
|
|
352
|
+
primary: ['notebook-frame', 'data-explorer-frame', 'visualization-frame'],
|
|
353
|
+
secondary: ['model-training-frame', 'experiment-tracker-frame', 'pipeline-frame'],
|
|
354
|
+
utility: ['dataset-catalog-frame', 'feature-store-frame', 'model-registry-frame'],
|
|
355
|
+
},
|
|
356
|
+
templates: {
|
|
357
|
+
notebooks: ['eda-notebook', 'model-training-notebook', 'feature-engineering-notebook', 'evaluation-notebook'],
|
|
358
|
+
pipelines: ['data-ingestion-pipeline', 'feature-pipeline', 'training-pipeline', 'inference-pipeline'],
|
|
359
|
+
reports: ['analysis-report', 'model-performance-report', 'data-quality-report', 'experiment-summary'],
|
|
360
|
+
},
|
|
361
|
+
workflows: {
|
|
362
|
+
analysis: ['collect-clean-explore-model-evaluate', 'hypothesis-experiment-analyze-report'],
|
|
363
|
+
modeling: ['feature-select-train-validate-deploy', 'experiment-track-compare-select'],
|
|
364
|
+
deployment: ['model-package-test-deploy-monitor', 'ab-test-evaluate-rollout'],
|
|
365
|
+
},
|
|
366
|
+
aiModels: {
|
|
367
|
+
generation: ['feature-generator', 'model-architecture-generator', 'data-augmentation'],
|
|
368
|
+
analysis: ['data-quality-analyzer', 'model-explainer', 'drift-detector'],
|
|
369
|
+
suggestion: ['algorithm-recommender', 'hyperparameter-tuner', 'feature-importance-ranker'],
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
agilePM: {
|
|
373
|
+
sprints: {
|
|
374
|
+
methodology: 'crisp-dm',
|
|
375
|
+
cycles: ['business-understanding', 'data-understanding', 'data-preparation', 'modeling', 'evaluation', 'deployment'],
|
|
376
|
+
duration: '3-weeks',
|
|
377
|
+
},
|
|
378
|
+
userStories: {
|
|
379
|
+
format: ['As a data scientist, I want...', 'Analysis requirement:', 'Model specification:'],
|
|
380
|
+
priorities: ['high-impact', 'medium-impact', 'low-impact', 'experimental'],
|
|
381
|
+
categories: ['data-exploration', 'feature-engineering', 'model-development', 'deployment'],
|
|
382
|
+
},
|
|
383
|
+
backlog: {
|
|
384
|
+
categories: ['data-tasks', 'modeling-tasks', 'evaluation-tasks', 'deployment-tasks', 'research'],
|
|
385
|
+
priorities: ['critical', 'high', 'medium', 'low'],
|
|
386
|
+
estimations: ['complexity-points', 'computation-hours', 'research-days'],
|
|
387
|
+
},
|
|
388
|
+
analytics: {
|
|
389
|
+
metrics: ['model-accuracy', 'experiment-throughput', 'data-pipeline-reliability', 'model-latency'],
|
|
390
|
+
reports: ['experiment-dashboard', 'model-comparison', 'data-quality-dashboard', 'pipeline-health'],
|
|
391
|
+
tracking: ['experiment-progress', 'model-performance-over-time', 'data-freshness'],
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
|
|
396
|
+
product_manager: {
|
|
397
|
+
name: 'Product Manager',
|
|
398
|
+
description: 'Product manager focused on strategy, roadmapping, and delivering value to users and the business',
|
|
399
|
+
focus: ['product-strategy', 'roadmapping', 'user-research', 'prioritization', 'go-to-market'],
|
|
400
|
+
studio: {
|
|
401
|
+
interface: {
|
|
402
|
+
layout: 'strategic-overview',
|
|
403
|
+
colorScheme: 'executive-clean',
|
|
404
|
+
typography: 'professional-modern',
|
|
405
|
+
visualElements: ['roadmap-board', 'metrics-dashboard', 'user-feedback-panel', 'competitive-landscape', 'okr-tracker'],
|
|
406
|
+
},
|
|
407
|
+
lexicon: {
|
|
408
|
+
terminology: ['product-market-fit', 'north-star-metric', 'user-persona', 'value-proposition', 'product-backlog', 'release-train', 'feature-flag', 'a-b-test'],
|
|
409
|
+
language: 'product-strategic',
|
|
410
|
+
abbreviations: ['PM', 'PRD', 'OKR', 'KPI', 'MRR', 'ARR', 'NPS', 'DAU', 'MAU'],
|
|
411
|
+
},
|
|
412
|
+
frames: {
|
|
413
|
+
primary: ['roadmap-frame', 'metrics-frame', 'feedback-frame'],
|
|
414
|
+
secondary: ['competitive-analysis-frame', 'user-research-frame', 'release-frame'],
|
|
415
|
+
utility: ['stakeholder-frame', 'experiment-frame', 'documentation-frame'],
|
|
416
|
+
},
|
|
417
|
+
templates: {
|
|
418
|
+
documents: ['product-requirements-doc', 'product-brief', 'go-to-market-plan', 'competitive-analysis'],
|
|
419
|
+
planning: ['quarterly-roadmap', 'release-plan', 'experiment-plan', 'launch-checklist'],
|
|
420
|
+
communication: ['stakeholder-update', 'sprint-review-deck', 'product-newsletter', 'feature-announcement'],
|
|
421
|
+
},
|
|
422
|
+
workflows: {
|
|
423
|
+
discovery: ['research-ideate-validate-build', 'opportunity-solution-tree'],
|
|
424
|
+
delivery: ['define-design-develop-deliver', 'dual-track-agile'],
|
|
425
|
+
analysis: ['measure-analyze-learn-iterate', 'hypothesis-driven-development'],
|
|
426
|
+
},
|
|
427
|
+
aiModels: {
|
|
428
|
+
generation: ['prd-generator', 'roadmap-generator', 'release-notes-generator'],
|
|
429
|
+
analysis: ['user-feedback-analyzer', 'competitive-intelligence', 'churn-predictor'],
|
|
430
|
+
suggestion: ['feature-prioritizer', 'experiment-recommender', 'pricing-optimizer'],
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
agilePM: {
|
|
434
|
+
sprints: {
|
|
435
|
+
methodology: 'dual-track-agile',
|
|
436
|
+
cycles: ['discovery', 'definition', 'delivery', 'measurement', 'iteration'],
|
|
437
|
+
duration: '2-weeks',
|
|
438
|
+
},
|
|
439
|
+
userStories: {
|
|
440
|
+
format: ['As a product user, I want...', 'Product requirement:', 'Feature specification:'],
|
|
441
|
+
priorities: ['must-have', 'should-have', 'could-have', 'wont-have'],
|
|
442
|
+
categories: ['new-feature', 'enhancement', 'experiment', 'tech-enabler'],
|
|
443
|
+
},
|
|
444
|
+
backlog: {
|
|
445
|
+
categories: ['features', 'experiments', 'tech-enablers', 'bugs', 'improvements'],
|
|
446
|
+
priorities: ['p0-critical', 'p1-high', 'p2-medium', 'p3-low'],
|
|
447
|
+
estimations: ['impact-effort-matrix', 'rice-score', 'weighted-scoring'],
|
|
448
|
+
},
|
|
449
|
+
analytics: {
|
|
450
|
+
metrics: ['feature-adoption', 'time-to-value', 'customer-satisfaction', 'revenue-impact'],
|
|
451
|
+
reports: ['product-health-dashboard', 'feature-performance', 'okr-progress', 'release-metrics'],
|
|
452
|
+
tracking: ['feature-usage', 'experiment-results', 'customer-feedback-trends'],
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
for (const [id, config] of Object.entries(corePersonas)) {
|
|
459
|
+
const persona: Persona = {
|
|
460
|
+
...config,
|
|
461
|
+
id,
|
|
462
|
+
createdAt: new Date(),
|
|
463
|
+
lastModified: new Date(),
|
|
464
|
+
usageCount: 0,
|
|
465
|
+
adaptationScore: 0,
|
|
466
|
+
customizations: new Map(),
|
|
467
|
+
};
|
|
468
|
+
this.personas.set(id, persona);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ─── Event Listeners ──────────────────────────────────────────────────────
|
|
473
|
+
|
|
474
|
+
private initializeEventListeners(): void {
|
|
475
|
+
this.on('persona:activated', (data: { personaId: string; userId: string }) => {
|
|
476
|
+
console.log(`[BaselinePersonaEngine] Persona activated: ${data.personaId} for user ${data.userId}`);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
this.on('persona:switched', (data: { fromPersonaId: string; toPersonaId: string; userId: string }) => {
|
|
480
|
+
console.log(`[BaselinePersonaEngine] Persona switched from ${data.fromPersonaId} to ${data.toPersonaId} for user ${data.userId}`);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
this.on('persona:customized', (data: { personaId: string; userId: string }) => {
|
|
484
|
+
console.log(`[BaselinePersonaEngine] Persona customized: ${data.personaId} for user ${data.userId}`);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
this.on('behavior:recorded', (data: { userId: string; type: string }) => {
|
|
488
|
+
if (this.config.enableBehavioralAnalysis) {
|
|
489
|
+
const patterns = this.analyzeBehaviorPatterns(data.userId);
|
|
490
|
+
if (patterns.length > 0 && this.config.enableAutonomousLearning) {
|
|
491
|
+
this.adaptPersonaAutonomously(data.userId, patterns);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
this.on('adaptation:applied', (data: { personaId: string; userId: string; type: string }) => {
|
|
497
|
+
console.log(`[BaselinePersonaEngine] Adaptation applied: ${data.type} to persona ${data.personaId} for user ${data.userId}`);
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// ─── Persona Management ───────────────────────────────────────────────────
|
|
502
|
+
|
|
503
|
+
addPersona(id: string, config: PersonaConfig): Persona {
|
|
504
|
+
if (this.personas.has(id)) {
|
|
505
|
+
throw new Error(`Persona with id '${id}' already exists`);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const persona: Persona = {
|
|
509
|
+
...config,
|
|
510
|
+
id,
|
|
511
|
+
createdAt: new Date(),
|
|
512
|
+
lastModified: new Date(),
|
|
513
|
+
usageCount: 0,
|
|
514
|
+
adaptationScore: 0,
|
|
515
|
+
customizations: new Map(),
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
this.personas.set(id, persona);
|
|
519
|
+
this.emit('persona:added', { personaId: id });
|
|
520
|
+
console.log(`[BaselinePersonaEngine] Persona added: ${id}`);
|
|
521
|
+
return persona;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
setActivePersona(personaId: string, userId: string): ActivePersona {
|
|
525
|
+
const persona = this.personas.get(personaId);
|
|
526
|
+
if (!persona) {
|
|
527
|
+
throw new Error(`Persona '${personaId}' not found`);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const activePersona: ActivePersona = {
|
|
531
|
+
...persona,
|
|
532
|
+
userId,
|
|
533
|
+
activatedAt: new Date(),
|
|
534
|
+
sessionId: randomUUID(),
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
persona.usageCount++;
|
|
538
|
+
persona.lastModified = new Date();
|
|
539
|
+
|
|
540
|
+
this.activePersonas.set(userId, activePersona);
|
|
541
|
+
this.emit('persona:activated', { personaId, userId });
|
|
542
|
+
return activePersona;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
getActivePersona(userId: string): ActivePersona | undefined {
|
|
546
|
+
return this.activePersonas.get(userId);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
getStudioConfig(userId: string): StudioConfig | null {
|
|
550
|
+
const activePersona = this.activePersonas.get(userId);
|
|
551
|
+
if (!activePersona) {
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
return activePersona.studio;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
getAgilePMConfig(userId: string): AgilePMConfig | null {
|
|
558
|
+
const activePersona = this.activePersonas.get(userId);
|
|
559
|
+
if (!activePersona) {
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
return activePersona.agilePM;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
customizePersona(personaId: string, userId: string, customizations: Record<string, unknown>): Persona {
|
|
566
|
+
const persona = this.personas.get(personaId);
|
|
567
|
+
if (!persona) {
|
|
568
|
+
throw new Error(`Persona '${personaId}' not found`);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
persona.customizations.set(userId, {
|
|
572
|
+
...(persona.customizations.get(userId) || {}),
|
|
573
|
+
...customizations,
|
|
574
|
+
});
|
|
575
|
+
persona.lastModified = new Date();
|
|
576
|
+
|
|
577
|
+
const activePersona = this.activePersonas.get(userId);
|
|
578
|
+
if (activePersona && activePersona.id === personaId) {
|
|
579
|
+
Object.assign(activePersona, this.deepMerge(activePersona as unknown as Record<string, unknown>, customizations));
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
this.emit('persona:customized', { personaId, userId, customizations });
|
|
583
|
+
return persona;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
getPersonalizedPersona(personaId: string, userId: string): Persona | null {
|
|
587
|
+
const persona = this.personas.get(personaId);
|
|
588
|
+
if (!persona) {
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const userCustomizations = persona.customizations.get(userId);
|
|
593
|
+
if (!userCustomizations) {
|
|
594
|
+
return persona;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return this.deepMerge({ ...persona } as Record<string, unknown>, userCustomizations) as unknown as Persona;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
switchPersona(userId: string, newPersonaId: string): ActivePersona {
|
|
601
|
+
const currentPersona = this.activePersonas.get(userId);
|
|
602
|
+
const fromPersonaId = currentPersona ? currentPersona.id : 'none';
|
|
603
|
+
|
|
604
|
+
const newActivePersona = this.setActivePersona(newPersonaId, userId);
|
|
605
|
+
this.emit('persona:switched', { fromPersonaId, toPersonaId: newPersonaId, userId });
|
|
606
|
+
return newActivePersona;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// ─── Behavioral Learning ──────────────────────────────────────────────────
|
|
610
|
+
|
|
611
|
+
learnFromBehavior(userId: string, behavior: BehaviorData): void {
|
|
612
|
+
if (!this.behaviorData.has(userId)) {
|
|
613
|
+
this.behaviorData.set(userId, []);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const entry: BehaviorData = {
|
|
617
|
+
...behavior,
|
|
618
|
+
timestamp: behavior.timestamp || new Date(),
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
this.behaviorData.get(userId)!.push(entry);
|
|
622
|
+
this.emit('behavior:recorded', { userId, type: behavior.type });
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
analyzeBehaviorPatterns(userId: string): BehaviorPattern[] {
|
|
626
|
+
const data = this.behaviorData.get(userId);
|
|
627
|
+
if (!data || data.length < 3) {
|
|
628
|
+
return [];
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const patterns: BehaviorPattern[] = [];
|
|
632
|
+
|
|
633
|
+
const workflowPattern = this.identifyWorkflowPattern(data);
|
|
634
|
+
if (workflowPattern) {
|
|
635
|
+
patterns.push(workflowPattern);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const toolPattern = this.identifyToolPreferencePattern(data);
|
|
639
|
+
if (toolPattern) {
|
|
640
|
+
patterns.push(toolPattern);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const timePattern = this.identifyTimePattern(data);
|
|
644
|
+
if (timePattern) {
|
|
645
|
+
patterns.push(timePattern);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const collaborationPattern = this.identifyCollaborationPattern(data);
|
|
649
|
+
if (collaborationPattern) {
|
|
650
|
+
patterns.push(collaborationPattern);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
return patterns;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
private identifyPattern(data: BehaviorData[], type: string): BehaviorPattern | null {
|
|
657
|
+
const relevantData = data.filter((d) => d.type === type);
|
|
658
|
+
if (relevantData.length < 2) {
|
|
659
|
+
return null;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const confidence = Math.min(relevantData.length / 10, 1.0);
|
|
663
|
+
return {
|
|
664
|
+
type,
|
|
665
|
+
confidence,
|
|
666
|
+
data: relevantData,
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
private identifyWorkflowPattern(data: BehaviorData[]): BehaviorPattern | null {
|
|
671
|
+
const workflowData = data.filter(
|
|
672
|
+
(d) => d.type === 'workflow_start' || d.type === 'workflow_complete' || d.type === 'workflow_step'
|
|
673
|
+
);
|
|
674
|
+
if (workflowData.length < 3) {
|
|
675
|
+
return null;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
const workflowTypes = new Map<string, number>();
|
|
679
|
+
for (const d of workflowData) {
|
|
680
|
+
const wType = (d.workflowType as string) || 'unknown';
|
|
681
|
+
workflowTypes.set(wType, (workflowTypes.get(wType) || 0) + 1);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
const confidence = Math.min(workflowData.length / 15, 1.0);
|
|
685
|
+
return {
|
|
686
|
+
type: 'workflow_preference',
|
|
687
|
+
confidence,
|
|
688
|
+
data: workflowData,
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
private identifyToolPreferencePattern(data: BehaviorData[]): BehaviorPattern | null {
|
|
693
|
+
const toolData = data.filter(
|
|
694
|
+
(d) => d.type === 'tool_used' || d.type === 'tool_selected' || d.type === 'tool_configured'
|
|
695
|
+
);
|
|
696
|
+
if (toolData.length < 2) {
|
|
697
|
+
return null;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const confidence = Math.min(toolData.length / 10, 1.0);
|
|
701
|
+
return {
|
|
702
|
+
type: 'tool_preference',
|
|
703
|
+
confidence,
|
|
704
|
+
data: toolData,
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
private identifyTimePattern(data: BehaviorData[]): BehaviorPattern | null {
|
|
709
|
+
const timedData = data.filter((d) => d.timestamp instanceof Date);
|
|
710
|
+
if (timedData.length < 5) {
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
const hours = timedData.map((d) => (d.timestamp as Date).getHours());
|
|
715
|
+
const avgHour = hours.reduce((a, b) => a + b, 0) / hours.length;
|
|
716
|
+
const variance = hours.reduce((sum, h) => sum + Math.pow(h - avgHour, 2), 0) / hours.length;
|
|
717
|
+
|
|
718
|
+
if (variance < 16) {
|
|
719
|
+
return {
|
|
720
|
+
type: 'time_preference',
|
|
721
|
+
confidence: Math.min(1.0 - variance / 16, 1.0),
|
|
722
|
+
data: timedData,
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
private identifyCollaborationPattern(data: BehaviorData[]): BehaviorPattern | null {
|
|
730
|
+
const collabData = data.filter(
|
|
731
|
+
(d) => d.type === 'collaboration' || d.type === 'shared' || d.type === 'commented' || d.type === 'reviewed'
|
|
732
|
+
);
|
|
733
|
+
if (collabData.length < 2) {
|
|
734
|
+
return null;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
const confidence = Math.min(collabData.length / 8, 1.0);
|
|
738
|
+
return {
|
|
739
|
+
type: 'collaboration_preference',
|
|
740
|
+
confidence,
|
|
741
|
+
data: collabData,
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// ─── Autonomous Adaptation ────────────────────────────────────────────────
|
|
746
|
+
|
|
747
|
+
private adaptPersonaAutonomously(userId: string, patterns: BehaviorPattern[]): void {
|
|
748
|
+
const activePersona = this.activePersonas.get(userId);
|
|
749
|
+
if (!activePersona) {
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
for (const pattern of patterns) {
|
|
754
|
+
if (pattern.confidence < 0.5) {
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const adaptation = this.generateAdaptation(activePersona.id, userId, pattern);
|
|
759
|
+
if (adaptation) {
|
|
760
|
+
this.applyPersonaAdaptation(adaptation);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
private generateAdaptation(personaId: string, userId: string, pattern: BehaviorPattern): PersonaAdaptation | null {
|
|
766
|
+
let changes: Record<string, unknown> = {};
|
|
767
|
+
|
|
768
|
+
switch (pattern.type) {
|
|
769
|
+
case 'workflow_preference':
|
|
770
|
+
changes = this.generateWorkflowAdaptation(pattern);
|
|
771
|
+
break;
|
|
772
|
+
case 'tool_preference':
|
|
773
|
+
changes = this.generateToolAdaptation(pattern);
|
|
774
|
+
break;
|
|
775
|
+
case 'time_preference':
|
|
776
|
+
changes = this.generateTimeAdaptation(pattern);
|
|
777
|
+
break;
|
|
778
|
+
case 'collaboration_preference':
|
|
779
|
+
changes = this.generateCollaborationAdaptation(pattern);
|
|
780
|
+
break;
|
|
781
|
+
default:
|
|
782
|
+
return null;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (Object.keys(changes).length === 0) {
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
return {
|
|
790
|
+
id: randomUUID(),
|
|
791
|
+
personaId,
|
|
792
|
+
userId,
|
|
793
|
+
type: pattern.type,
|
|
794
|
+
changes,
|
|
795
|
+
confidence: pattern.confidence,
|
|
796
|
+
appliedAt: new Date(),
|
|
797
|
+
source: 'autonomous-learning',
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
private generateWorkflowAdaptation(pattern: BehaviorPattern): Record<string, unknown> {
|
|
802
|
+
const workflowCounts = new Map<string, number>();
|
|
803
|
+
for (const d of pattern.data) {
|
|
804
|
+
const wType = (d.workflowType as string) || 'default';
|
|
805
|
+
workflowCounts.set(wType, (workflowCounts.get(wType) || 0) + 1);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
let preferredWorkflow = 'default';
|
|
809
|
+
let maxCount = 0;
|
|
810
|
+
for (const [wType, count] of workflowCounts) {
|
|
811
|
+
if (count > maxCount) {
|
|
812
|
+
maxCount = count;
|
|
813
|
+
preferredWorkflow = wType;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
return {
|
|
818
|
+
studio: {
|
|
819
|
+
workflows: {
|
|
820
|
+
preferred: preferredWorkflow,
|
|
821
|
+
frequencyMap: Object.fromEntries(workflowCounts),
|
|
822
|
+
},
|
|
823
|
+
},
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
private generateToolAdaptation(pattern: BehaviorPattern): Record<string, unknown> {
|
|
828
|
+
const toolCounts = new Map<string, number>();
|
|
829
|
+
for (const d of pattern.data) {
|
|
830
|
+
const tool = (d.tool as string) || (d.toolName as string) || 'unknown';
|
|
831
|
+
toolCounts.set(tool, (toolCounts.get(tool) || 0) + 1);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const preferredTools: string[] = [];
|
|
835
|
+
for (const [tool, count] of toolCounts) {
|
|
836
|
+
if (count >= 2) {
|
|
837
|
+
preferredTools.push(tool);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
return {
|
|
842
|
+
studio: {
|
|
843
|
+
interface: {
|
|
844
|
+
preferredTools,
|
|
845
|
+
toolUsageMap: Object.fromEntries(toolCounts),
|
|
846
|
+
},
|
|
847
|
+
},
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
private generateTimeAdaptation(pattern: BehaviorPattern): Record<string, unknown> {
|
|
852
|
+
const hours = pattern.data
|
|
853
|
+
.filter((d) => d.timestamp instanceof Date)
|
|
854
|
+
.map((d) => (d.timestamp as Date).getHours());
|
|
855
|
+
|
|
856
|
+
if (hours.length === 0) {
|
|
857
|
+
return {};
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
const avgHour = Math.round(hours.reduce((a, b) => a + b, 0) / hours.length);
|
|
861
|
+
let timePreference = 'flexible';
|
|
862
|
+
|
|
863
|
+
if (avgHour >= 5 && avgHour < 12) {
|
|
864
|
+
timePreference = 'morning';
|
|
865
|
+
} else if (avgHour >= 12 && avgHour < 17) {
|
|
866
|
+
timePreference = 'afternoon';
|
|
867
|
+
} else if (avgHour >= 17 && avgHour < 21) {
|
|
868
|
+
timePreference = 'evening';
|
|
869
|
+
} else {
|
|
870
|
+
timePreference = 'night';
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
return {
|
|
874
|
+
preferences: {
|
|
875
|
+
activeHours: { average: avgHour, preference: timePreference },
|
|
876
|
+
},
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
private generateCollaborationAdaptation(pattern: BehaviorPattern): Record<string, unknown> {
|
|
881
|
+
const collabTypes = new Map<string, number>();
|
|
882
|
+
for (const d of pattern.data) {
|
|
883
|
+
collabTypes.set(d.type, (collabTypes.get(d.type) || 0) + 1);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
const collaborators = new Set<string>();
|
|
887
|
+
for (const d of pattern.data) {
|
|
888
|
+
if (d.collaborator) {
|
|
889
|
+
collaborators.add(d.collaborator as string);
|
|
890
|
+
}
|
|
891
|
+
if (d.reviewer) {
|
|
892
|
+
collaborators.add(d.reviewer as string);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
return {
|
|
897
|
+
preferences: {
|
|
898
|
+
collaboration: {
|
|
899
|
+
style: collabTypes.size > 2 ? 'highly-collaborative' : 'selective',
|
|
900
|
+
frequentCollaborators: Array.from(collaborators),
|
|
901
|
+
preferredMethods: Object.fromEntries(collabTypes),
|
|
902
|
+
},
|
|
903
|
+
},
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
private applyPersonaAdaptation(adaptation: PersonaAdaptation): void {
|
|
908
|
+
const persona = this.personas.get(adaptation.personaId);
|
|
909
|
+
if (!persona) {
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
const existingCustomizations = persona.customizations.get(adaptation.userId) || {};
|
|
914
|
+
persona.customizations.set(
|
|
915
|
+
adaptation.userId,
|
|
916
|
+
this.deepMerge(existingCustomizations, adaptation.changes) as Record<string, unknown>
|
|
917
|
+
);
|
|
918
|
+
|
|
919
|
+
persona.adaptationScore = Math.min(persona.adaptationScore + adaptation.confidence * 0.1, 1.0);
|
|
920
|
+
persona.lastModified = new Date();
|
|
921
|
+
|
|
922
|
+
const activePersona = this.activePersonas.get(adaptation.userId);
|
|
923
|
+
if (activePersona && activePersona.id === adaptation.personaId) {
|
|
924
|
+
Object.assign(activePersona, this.deepMerge(activePersona as unknown as Record<string, unknown>, adaptation.changes));
|
|
925
|
+
activePersona.adaptationScore = persona.adaptationScore;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
this.adaptations.push(adaptation);
|
|
929
|
+
this.emit('adaptation:applied', {
|
|
930
|
+
personaId: adaptation.personaId,
|
|
931
|
+
userId: adaptation.userId,
|
|
932
|
+
type: adaptation.type,
|
|
933
|
+
confidence: adaptation.confidence,
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// ─── Recommendations ──────────────────────────────────────────────────────
|
|
938
|
+
|
|
939
|
+
getPersonaRecommendations(userId: string, context: Record<string, unknown> = {}): PersonaRecommendation[] {
|
|
940
|
+
const behaviors = this.behaviorData.get(userId) || [];
|
|
941
|
+
const patterns = this.analyzeBehaviorPatterns(userId);
|
|
942
|
+
|
|
943
|
+
const recommendations: PersonaRecommendation[] = [];
|
|
944
|
+
|
|
945
|
+
for (const [personaId, persona] of this.personas) {
|
|
946
|
+
const patternScore = this.calculatePatternFitScore(persona, patterns);
|
|
947
|
+
const contextScore = this.calculateContextFitScore(persona, context);
|
|
948
|
+
const personaScore = this.calculatePersonaFitScore(persona, behaviors);
|
|
949
|
+
|
|
950
|
+
const totalScore = patternScore * 0.4 + contextScore * 0.3 + personaScore * 0.3;
|
|
951
|
+
|
|
952
|
+
recommendations.push({
|
|
953
|
+
personaId,
|
|
954
|
+
score: Math.round(totalScore * 100) / 100,
|
|
955
|
+
reasoning: this.generateRecommendationReasoning(persona, patternScore, contextScore, personaScore),
|
|
956
|
+
fitScores: {
|
|
957
|
+
pattern: Math.round(patternScore * 100) / 100,
|
|
958
|
+
context: Math.round(contextScore * 100) / 100,
|
|
959
|
+
persona: Math.round(personaScore * 100) / 100,
|
|
960
|
+
},
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
recommendations.sort((a, b) => b.score - a.score);
|
|
965
|
+
return recommendations;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
calculatePersonaFitScore(persona: Persona, behaviors: BehaviorData[]): number {
|
|
969
|
+
if (behaviors.length === 0) {
|
|
970
|
+
return 0.5;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
let score = 0;
|
|
974
|
+
const focusAreas = persona.focus;
|
|
975
|
+
|
|
976
|
+
for (const behavior of behaviors) {
|
|
977
|
+
const behaviorType = behavior.type.toLowerCase();
|
|
978
|
+
for (const focus of focusAreas) {
|
|
979
|
+
if (behaviorType.includes(focus) || focus.includes(behaviorType)) {
|
|
980
|
+
score += this.getPatternWeight(behavior.type);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
return Math.min(score / behaviors.length, 1.0);
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
private calculatePatternFitScore(persona: Persona, patterns: BehaviorPattern[]): number {
|
|
989
|
+
if (patterns.length === 0) {
|
|
990
|
+
return 0.5;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
let totalScore = 0;
|
|
994
|
+
for (const pattern of patterns) {
|
|
995
|
+
const weight = this.getPatternWeight(pattern.type);
|
|
996
|
+
totalScore += pattern.confidence * weight;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
return Math.min(totalScore / patterns.length, 1.0);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
private calculateContextFitScore(persona: Persona, context: Record<string, unknown>): number {
|
|
1003
|
+
if (Object.keys(context).length === 0) {
|
|
1004
|
+
return 0.5;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
let matchCount = 0;
|
|
1008
|
+
let totalChecks = 0;
|
|
1009
|
+
|
|
1010
|
+
const contextData = this.extractContextData(context);
|
|
1011
|
+
const focusAreas = persona.focus;
|
|
1012
|
+
|
|
1013
|
+
for (const item of contextData) {
|
|
1014
|
+
totalChecks++;
|
|
1015
|
+
for (const focus of focusAreas) {
|
|
1016
|
+
if (item.toLowerCase().includes(focus) || focus.includes(item.toLowerCase())) {
|
|
1017
|
+
matchCount++;
|
|
1018
|
+
break;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
return totalChecks > 0 ? matchCount / totalChecks : 0.5;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
private generateRecommendationReasoning(
|
|
1027
|
+
persona: Persona,
|
|
1028
|
+
patternScore: number,
|
|
1029
|
+
contextScore: number,
|
|
1030
|
+
personaScore: number
|
|
1031
|
+
): string {
|
|
1032
|
+
const reasons: string[] = [];
|
|
1033
|
+
|
|
1034
|
+
if (patternScore > 0.7) {
|
|
1035
|
+
reasons.push(`Strong behavioral pattern match with ${persona.name} workflows`);
|
|
1036
|
+
} else if (patternScore > 0.4) {
|
|
1037
|
+
reasons.push(`Moderate behavioral alignment with ${persona.name} focus areas`);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
if (contextScore > 0.7) {
|
|
1041
|
+
reasons.push(`Current context strongly aligns with ${persona.name} capabilities`);
|
|
1042
|
+
} else if (contextScore > 0.4) {
|
|
1043
|
+
reasons.push(`Some contextual relevance to ${persona.name} tools and processes`);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
if (personaScore > 0.7) {
|
|
1047
|
+
reasons.push(`Historical usage patterns strongly favor ${persona.name} persona`);
|
|
1048
|
+
} else if (personaScore > 0.4) {
|
|
1049
|
+
reasons.push(`Some historical affinity with ${persona.name} persona`);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
if (reasons.length === 0) {
|
|
1053
|
+
reasons.push(`${persona.name} is available as a general-purpose persona option`);
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
return reasons.join('. ') + '.';
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// ─── Utility Methods ──────────────────────────────────────────────────────
|
|
1060
|
+
|
|
1061
|
+
private deepMerge(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> {
|
|
1062
|
+
const output: Record<string, unknown> = { ...target };
|
|
1063
|
+
|
|
1064
|
+
for (const key of Object.keys(source)) {
|
|
1065
|
+
if (
|
|
1066
|
+
source[key] &&
|
|
1067
|
+
typeof source[key] === 'object' &&
|
|
1068
|
+
!Array.isArray(source[key]) &&
|
|
1069
|
+
!(source[key] instanceof Date) &&
|
|
1070
|
+
!(source[key] instanceof Map) &&
|
|
1071
|
+
!(source[key] instanceof Set)
|
|
1072
|
+
) {
|
|
1073
|
+
if (target[key] && typeof target[key] === 'object' && !Array.isArray(target[key])) {
|
|
1074
|
+
output[key] = this.deepMerge(target[key] as Record<string, unknown>, source[key] as Record<string, unknown>);
|
|
1075
|
+
} else {
|
|
1076
|
+
output[key] = { ...(source[key] as Record<string, unknown>) };
|
|
1077
|
+
}
|
|
1078
|
+
} else {
|
|
1079
|
+
output[key] = source[key];
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
return output;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
private calculateCustomizationLevel(persona: Persona): number {
|
|
1087
|
+
let level = 0;
|
|
1088
|
+
for (const [, customizations] of persona.customizations) {
|
|
1089
|
+
level += Object.keys(customizations).length;
|
|
1090
|
+
}
|
|
1091
|
+
return Math.min(level / 10, 1.0);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
private extractContextData(context: Record<string, unknown>): string[] {
|
|
1095
|
+
const data: string[] = [];
|
|
1096
|
+
|
|
1097
|
+
for (const [key, value] of Object.entries(context)) {
|
|
1098
|
+
data.push(key);
|
|
1099
|
+
if (typeof value === 'string') {
|
|
1100
|
+
data.push(value);
|
|
1101
|
+
} else if (Array.isArray(value)) {
|
|
1102
|
+
for (const item of value) {
|
|
1103
|
+
if (typeof item === 'string') {
|
|
1104
|
+
data.push(item);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
return data;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
private getPatternWeight(type: string): number {
|
|
1114
|
+
const weights: Record<string, number> = {
|
|
1115
|
+
workflow_preference: 0.9,
|
|
1116
|
+
tool_preference: 0.8,
|
|
1117
|
+
time_preference: 0.3,
|
|
1118
|
+
collaboration_preference: 0.7,
|
|
1119
|
+
workflow_start: 0.6,
|
|
1120
|
+
workflow_complete: 0.8,
|
|
1121
|
+
workflow_step: 0.4,
|
|
1122
|
+
tool_used: 0.7,
|
|
1123
|
+
tool_selected: 0.5,
|
|
1124
|
+
tool_configured: 0.6,
|
|
1125
|
+
collaboration: 0.5,
|
|
1126
|
+
shared: 0.4,
|
|
1127
|
+
commented: 0.3,
|
|
1128
|
+
reviewed: 0.6,
|
|
1129
|
+
};
|
|
1130
|
+
return weights[type] || 0.5;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// ─── Analytics & Status ────────────────────────────────────────────────────
|
|
1134
|
+
|
|
1135
|
+
getStatus(): PersonaStatus {
|
|
1136
|
+
return {
|
|
1137
|
+
initialized: this.initialized,
|
|
1138
|
+
totalPersonas: this.personas.size,
|
|
1139
|
+
activePersonas: this.activePersonas.size,
|
|
1140
|
+
behaviorDataPoints: this.countBehaviorTypes(),
|
|
1141
|
+
adaptationsApplied: this.adaptations.length,
|
|
1142
|
+
config: { ...this.config },
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
getAnalytics(): PersonaAnalytics {
|
|
1147
|
+
const behaviorsByType: Record<string, number> = {};
|
|
1148
|
+
for (const [, behaviors] of this.behaviorData) {
|
|
1149
|
+
for (const b of behaviors) {
|
|
1150
|
+
behaviorsByType[b.type] = (behaviorsByType[b.type] || 0) + 1;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
let totalAdaptationScore = 0;
|
|
1155
|
+
let personaCount = 0;
|
|
1156
|
+
for (const [, persona] of this.personas) {
|
|
1157
|
+
totalAdaptationScore += persona.adaptationScore;
|
|
1158
|
+
personaCount++;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
return {
|
|
1162
|
+
totalPersonas: this.personas.size,
|
|
1163
|
+
activePersonas: this.activePersonas.size,
|
|
1164
|
+
totalBehaviors: this.countBehaviorTypes(),
|
|
1165
|
+
behaviorsByType,
|
|
1166
|
+
totalAdaptations: this.adaptations.length,
|
|
1167
|
+
adaptationsByPersona: this.groupAdaptationsByPersona(),
|
|
1168
|
+
adaptationsByUser: this.groupAdaptationsByUser(),
|
|
1169
|
+
averageAdaptationScore: personaCount > 0 ? totalAdaptationScore / personaCount : 0,
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
private countBehaviorTypes(): number {
|
|
1174
|
+
let total = 0;
|
|
1175
|
+
for (const [, behaviors] of this.behaviorData) {
|
|
1176
|
+
total += behaviors.length;
|
|
1177
|
+
}
|
|
1178
|
+
return total;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
private groupAdaptationsByPersona(): Record<string, number> {
|
|
1182
|
+
const groups: Record<string, number> = {};
|
|
1183
|
+
for (const adaptation of this.adaptations) {
|
|
1184
|
+
groups[adaptation.personaId] = (groups[adaptation.personaId] || 0) + 1;
|
|
1185
|
+
}
|
|
1186
|
+
return groups;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
private groupAdaptationsByUser(): Record<string, number> {
|
|
1190
|
+
const groups: Record<string, number> = {};
|
|
1191
|
+
for (const adaptation of this.adaptations) {
|
|
1192
|
+
groups[adaptation.userId] = (groups[adaptation.userId] || 0) + 1;
|
|
1193
|
+
}
|
|
1194
|
+
return groups;
|
|
1195
|
+
}
|
|
1196
|
+
}
|