elsabro 2.3.0 → 3.8.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/README.md +698 -20
- package/bin/install.js +0 -0
- package/flows/development-flow.json +452 -0
- package/flows/quick-flow.json +118 -0
- package/hooks/hooks-config-updated.json +285 -0
- package/hooks/skill-discovery.sh +539 -0
- package/package.json +3 -2
- package/references/SYSTEM_INDEX.md +400 -5
- package/references/agent-marketplace.md +2274 -0
- package/references/agent-protocol.md +1126 -0
- package/references/ai-code-suggestions.md +2413 -0
- package/references/checkpointing.md +595 -0
- package/references/collaboration-patterns.md +851 -0
- package/references/collaborative-sessions.md +1081 -0
- package/references/configuration-management.md +1810 -0
- package/references/cost-tracking.md +1095 -0
- package/references/enterprise-sso.md +2001 -0
- package/references/error-contracts-v2.md +968 -0
- package/references/event-driven.md +1031 -0
- package/references/flow-orchestration.md +940 -0
- package/references/flow-visualization.md +1557 -0
- package/references/ide-integrations.md +3513 -0
- package/references/interrupt-system.md +681 -0
- package/references/kubernetes-deployment.md +3099 -0
- package/references/memory-system.md +683 -0
- package/references/mobile-companion.md +3236 -0
- package/references/multi-llm-providers.md +2494 -0
- package/references/multi-project-memory.md +1182 -0
- package/references/observability.md +793 -0
- package/references/output-schemas.md +858 -0
- package/references/performance-profiler.md +955 -0
- package/references/plugin-system.md +1526 -0
- package/references/prompt-management.md +292 -0
- package/references/sandbox-execution.md +303 -0
- package/references/security-system.md +1253 -0
- package/references/skill-marketplace-integration.md +3901 -0
- package/references/streaming.md +696 -0
- package/references/testing-framework.md +1151 -0
- package/references/time-travel.md +802 -0
- package/references/tool-registry.md +886 -0
- package/references/voice-commands.md +3296 -0
- package/templates/agent-marketplace-config.json +220 -0
- package/templates/agent-protocol-config.json +136 -0
- package/templates/ai-suggestions-config.json +100 -0
- package/templates/checkpoint-state.json +61 -0
- package/templates/collaboration-config.json +157 -0
- package/templates/collaborative-sessions-config.json +153 -0
- package/templates/configuration-config.json +245 -0
- package/templates/cost-tracking-config.json +148 -0
- package/templates/enterprise-sso-config.json +438 -0
- package/templates/events-config.json +148 -0
- package/templates/flow-visualization-config.json +196 -0
- package/templates/ide-integrations-config.json +442 -0
- package/templates/kubernetes-config.json +764 -0
- package/templates/memory-state.json +84 -0
- package/templates/mobile-companion-config.json +600 -0
- package/templates/multi-llm-config.json +544 -0
- package/templates/multi-project-memory-config.json +145 -0
- package/templates/observability-config.json +109 -0
- package/templates/performance-profiler-config.json +125 -0
- package/templates/plugin-config.json +170 -0
- package/templates/prompt-management-config.json +86 -0
- package/templates/sandbox-config.json +185 -0
- package/templates/schemas-config.json +65 -0
- package/templates/security-config.json +120 -0
- package/templates/skill-marketplace-config.json +441 -0
- package/templates/streaming-config.json +72 -0
- package/templates/testing-config.json +81 -0
- package/templates/timetravel-config.json +62 -0
- package/templates/tool-registry-config.json +109 -0
- package/templates/voice-commands-config.json +658 -0
|
@@ -0,0 +1,802 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: time-travel
|
|
3
|
+
description: Sistema de time-travel debugging para análisis y replay de sesiones
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# ELSABRO Time-Travel Debugging System
|
|
8
|
+
|
|
9
|
+
## Vision General
|
|
10
|
+
|
|
11
|
+
El sistema de Time-Travel Debugging permite navegar por la historia de ejecución, analizar decisiones pasadas, y explorar caminos alternativos ("what-if" analysis).
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
┌──────────────────────────────────────────────────────────────────────────┐
|
|
15
|
+
│ TIME-TRAVEL ARCHITECTURE │
|
|
16
|
+
├──────────────────────────────────────────────────────────────────────────┤
|
|
17
|
+
│ │
|
|
18
|
+
│ TIMELINE │
|
|
19
|
+
│ ═══════════════════════════════════════════════════════════════ │
|
|
20
|
+
│ │ │
|
|
21
|
+
│ ▼ │
|
|
22
|
+
│ [CP1]────[CP2]────[CP3]────[CP4]────[CP5]────[CURRENT] │
|
|
23
|
+
│ │ │ │ │ │ │
|
|
24
|
+
│ │ │ │ │ └── Review complete │
|
|
25
|
+
│ │ │ │ └── Implementation done │
|
|
26
|
+
│ │ │ └── Plan approved │
|
|
27
|
+
│ │ └── Analysis complete │
|
|
28
|
+
│ └── Session started │
|
|
29
|
+
│ │
|
|
30
|
+
│ OPERATIONS │
|
|
31
|
+
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
|
32
|
+
│ │ REPLAY │ │ JUMP │ │ BRANCH │ │ COMPARE │ │
|
|
33
|
+
│ │ (watch) │ │ (goto) │ │ (fork) │ │ (diff) │ │
|
|
34
|
+
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
|
35
|
+
│ │
|
|
36
|
+
│ WHAT-IF ANALYSIS │
|
|
37
|
+
│ ┌──────────────────────────────────────────────────────────────┐ │
|
|
38
|
+
│ │ Original: CP3 ─── [Decision A] ─── CP4 ─── CP5 │ │
|
|
39
|
+
│ │ │ │ │
|
|
40
|
+
│ │ Branch: CP3 ─── [Decision B] ─── CP4' ─── ? │ │
|
|
41
|
+
│ │ (alternative) │ │
|
|
42
|
+
│ └──────────────────────────────────────────────────────────────┘ │
|
|
43
|
+
│ │
|
|
44
|
+
└──────────────────────────────────────────────────────────────────────────┘
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Componentes del Sistema
|
|
50
|
+
|
|
51
|
+
### 1. Timeline Manager
|
|
52
|
+
|
|
53
|
+
Gestiona la línea de tiempo de ejecución.
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
/**
|
|
57
|
+
* TimelineManager
|
|
58
|
+
* Gestiona la línea de tiempo de ejecución para time-travel
|
|
59
|
+
*/
|
|
60
|
+
class TimelineManager {
|
|
61
|
+
constructor(checkpointManager) {
|
|
62
|
+
this.checkpointManager = checkpointManager;
|
|
63
|
+
this.timeline = [];
|
|
64
|
+
this.currentPosition = -1;
|
|
65
|
+
this.branches = new Map();
|
|
66
|
+
this.mainBranch = 'main';
|
|
67
|
+
this.activeBranch = 'main';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Construye timeline desde checkpoints
|
|
72
|
+
*/
|
|
73
|
+
async buildTimeline(sessionId) {
|
|
74
|
+
const checkpoints = await this.checkpointManager.listCheckpoints(sessionId, {
|
|
75
|
+
limit: 1000
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
this.timeline = checkpoints.map((cp, index) => ({
|
|
79
|
+
index,
|
|
80
|
+
checkpointId: cp.checkpointId,
|
|
81
|
+
timestamp: cp.createdAt,
|
|
82
|
+
event: cp.event,
|
|
83
|
+
phase: cp.phase,
|
|
84
|
+
summary: this.generateSummary(cp),
|
|
85
|
+
branch: 'main'
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
this.currentPosition = this.timeline.length - 1;
|
|
89
|
+
|
|
90
|
+
return this.timeline;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Genera resumen legible de un checkpoint
|
|
95
|
+
*/
|
|
96
|
+
generateSummary(checkpoint) {
|
|
97
|
+
const summaries = {
|
|
98
|
+
'FLOW_START': 'Session started',
|
|
99
|
+
'PHASE_START': `Started phase: ${checkpoint.phase}`,
|
|
100
|
+
'PHASE_COMPLETE': `Completed phase: ${checkpoint.phase}`,
|
|
101
|
+
'NODE_COMPLETE': `Completed node: ${checkpoint.nodeId}`,
|
|
102
|
+
'AGENT_COMPLETE': `Agent finished: ${checkpoint.agentId}`,
|
|
103
|
+
'INTERRUPT': `Waiting for input: ${checkpoint.reason}`,
|
|
104
|
+
'DECISION': `Decision made: ${checkpoint.decision}`,
|
|
105
|
+
'ERROR': `Error occurred: ${checkpoint.error}`,
|
|
106
|
+
'ROLLBACK': `Rolled back to: ${checkpoint.targetId}`
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return summaries[checkpoint.event] || checkpoint.event;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Obtiene posición actual en timeline
|
|
114
|
+
*/
|
|
115
|
+
getCurrentPosition() {
|
|
116
|
+
return {
|
|
117
|
+
index: this.currentPosition,
|
|
118
|
+
checkpoint: this.timeline[this.currentPosition],
|
|
119
|
+
total: this.timeline.length,
|
|
120
|
+
isLatest: this.currentPosition === this.timeline.length - 1,
|
|
121
|
+
branch: this.activeBranch
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Salta a una posición específica
|
|
127
|
+
*/
|
|
128
|
+
async jumpTo(position) {
|
|
129
|
+
if (position < 0 || position >= this.timeline.length) {
|
|
130
|
+
throw new Error(`Invalid position: ${position}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const checkpoint = this.timeline[position];
|
|
134
|
+
await this.checkpointManager.loadCheckpoint(checkpoint.checkpointId);
|
|
135
|
+
|
|
136
|
+
this.currentPosition = position;
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
position: this.currentPosition,
|
|
140
|
+
checkpoint,
|
|
141
|
+
state: await this.checkpointManager.getStateAt(checkpoint.checkpointId)
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Avanza un paso
|
|
147
|
+
*/
|
|
148
|
+
async stepForward() {
|
|
149
|
+
if (this.currentPosition >= this.timeline.length - 1) {
|
|
150
|
+
return { atEnd: true };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return this.jumpTo(this.currentPosition + 1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Retrocede un paso
|
|
158
|
+
*/
|
|
159
|
+
async stepBackward() {
|
|
160
|
+
if (this.currentPosition <= 0) {
|
|
161
|
+
return { atBeginning: true };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return this.jumpTo(this.currentPosition - 1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Crea una rama alternativa desde la posición actual
|
|
169
|
+
*/
|
|
170
|
+
async createBranch(branchName) {
|
|
171
|
+
if (this.branches.has(branchName)) {
|
|
172
|
+
throw new Error(`Branch ${branchName} already exists`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const branchPoint = this.timeline[this.currentPosition];
|
|
176
|
+
|
|
177
|
+
this.branches.set(branchName, {
|
|
178
|
+
name: branchName,
|
|
179
|
+
createdAt: new Date().toISOString(),
|
|
180
|
+
branchPoint: branchPoint.checkpointId,
|
|
181
|
+
branchPosition: this.currentPosition,
|
|
182
|
+
timeline: []
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
this.activeBranch = branchName;
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
branch: branchName,
|
|
189
|
+
branchPoint: branchPoint,
|
|
190
|
+
position: this.currentPosition
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Cambia a una rama
|
|
196
|
+
*/
|
|
197
|
+
async switchBranch(branchName) {
|
|
198
|
+
if (branchName === 'main') {
|
|
199
|
+
this.activeBranch = 'main';
|
|
200
|
+
return { branch: 'main', timeline: this.timeline };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (!this.branches.has(branchName)) {
|
|
204
|
+
throw new Error(`Branch ${branchName} not found`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const branch = this.branches.get(branchName);
|
|
208
|
+
this.activeBranch = branchName;
|
|
209
|
+
|
|
210
|
+
// Combinar timeline principal hasta branch point + branch timeline
|
|
211
|
+
const combinedTimeline = [
|
|
212
|
+
...this.timeline.slice(0, branch.branchPosition + 1),
|
|
213
|
+
...branch.timeline
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
branch: branchName,
|
|
218
|
+
timeline: combinedTimeline,
|
|
219
|
+
branchPoint: branch.branchPosition
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Lista todas las ramas
|
|
225
|
+
*/
|
|
226
|
+
listBranches() {
|
|
227
|
+
const branches = [
|
|
228
|
+
{
|
|
229
|
+
name: 'main',
|
|
230
|
+
isActive: this.activeBranch === 'main',
|
|
231
|
+
length: this.timeline.length,
|
|
232
|
+
branchPoint: null
|
|
233
|
+
}
|
|
234
|
+
];
|
|
235
|
+
|
|
236
|
+
for (const [name, branch] of this.branches) {
|
|
237
|
+
branches.push({
|
|
238
|
+
name,
|
|
239
|
+
isActive: this.activeBranch === name,
|
|
240
|
+
length: branch.timeline.length,
|
|
241
|
+
branchPoint: branch.branchPosition,
|
|
242
|
+
createdAt: branch.createdAt
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return branches;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### 2. Replay Engine
|
|
252
|
+
|
|
253
|
+
Motor para reproducir ejecuciones pasadas.
|
|
254
|
+
|
|
255
|
+
```javascript
|
|
256
|
+
/**
|
|
257
|
+
* ReplayEngine
|
|
258
|
+
* Reproduce ejecuciones pasadas step-by-step
|
|
259
|
+
*/
|
|
260
|
+
class ReplayEngine {
|
|
261
|
+
constructor(timelineManager, options = {}) {
|
|
262
|
+
this.timeline = timelineManager;
|
|
263
|
+
this.config = {
|
|
264
|
+
speed: options.speed || 1.0, // 1.0 = tiempo real
|
|
265
|
+
autoPlay: options.autoPlay || false,
|
|
266
|
+
pauseOnDecision: options.pauseOnDecision || true,
|
|
267
|
+
pauseOnError: options.pauseOnError || true
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
this.state = {
|
|
271
|
+
isPlaying: false,
|
|
272
|
+
isPaused: false,
|
|
273
|
+
currentStep: 0,
|
|
274
|
+
totalSteps: 0
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
this.listeners = new Set();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Inicia replay desde posición actual
|
|
282
|
+
*/
|
|
283
|
+
async startReplay(options = {}) {
|
|
284
|
+
const startPosition = options.from || 0;
|
|
285
|
+
const endPosition = options.to || this.timeline.timeline.length - 1;
|
|
286
|
+
|
|
287
|
+
this.state = {
|
|
288
|
+
isPlaying: true,
|
|
289
|
+
isPaused: false,
|
|
290
|
+
currentStep: startPosition,
|
|
291
|
+
totalSteps: endPosition - startPosition + 1,
|
|
292
|
+
startPosition,
|
|
293
|
+
endPosition
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
await this.timeline.jumpTo(startPosition);
|
|
297
|
+
this.emit('replay_started', { position: startPosition });
|
|
298
|
+
|
|
299
|
+
if (this.config.autoPlay) {
|
|
300
|
+
await this.play();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return this.state;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Reproduce automáticamente
|
|
308
|
+
*/
|
|
309
|
+
async play() {
|
|
310
|
+
while (this.state.isPlaying && !this.state.isPaused) {
|
|
311
|
+
const result = await this.step();
|
|
312
|
+
|
|
313
|
+
if (result.atEnd) {
|
|
314
|
+
this.state.isPlaying = false;
|
|
315
|
+
this.emit('replay_complete', { steps: this.state.currentStep });
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Pausa en decisiones si está configurado
|
|
320
|
+
if (this.config.pauseOnDecision && result.checkpoint.event === 'DECISION') {
|
|
321
|
+
this.pause();
|
|
322
|
+
this.emit('paused_on_decision', result);
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Pausa en errores si está configurado
|
|
327
|
+
if (this.config.pauseOnError && result.checkpoint.event === 'ERROR') {
|
|
328
|
+
this.pause();
|
|
329
|
+
this.emit('paused_on_error', result);
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Delay basado en velocidad
|
|
334
|
+
const delay = this.calculateDelay(result);
|
|
335
|
+
await this.sleep(delay);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Avanza un paso en el replay
|
|
341
|
+
*/
|
|
342
|
+
async step() {
|
|
343
|
+
if (this.state.currentStep >= this.state.endPosition) {
|
|
344
|
+
return { atEnd: true };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const result = await this.timeline.stepForward();
|
|
348
|
+
this.state.currentStep++;
|
|
349
|
+
|
|
350
|
+
this.emit('step', {
|
|
351
|
+
step: this.state.currentStep,
|
|
352
|
+
total: this.state.totalSteps,
|
|
353
|
+
checkpoint: result.checkpoint,
|
|
354
|
+
state: result.state
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
return result;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Retrocede un paso
|
|
362
|
+
*/
|
|
363
|
+
async stepBack() {
|
|
364
|
+
if (this.state.currentStep <= this.state.startPosition) {
|
|
365
|
+
return { atBeginning: true };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const result = await this.timeline.stepBackward();
|
|
369
|
+
this.state.currentStep--;
|
|
370
|
+
|
|
371
|
+
this.emit('step_back', {
|
|
372
|
+
step: this.state.currentStep,
|
|
373
|
+
checkpoint: result.checkpoint
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Pausa el replay
|
|
381
|
+
*/
|
|
382
|
+
pause() {
|
|
383
|
+
this.state.isPaused = true;
|
|
384
|
+
this.emit('paused', { step: this.state.currentStep });
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Reanuda el replay
|
|
389
|
+
*/
|
|
390
|
+
async resume() {
|
|
391
|
+
this.state.isPaused = false;
|
|
392
|
+
this.emit('resumed', { step: this.state.currentStep });
|
|
393
|
+
await this.play();
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Detiene el replay
|
|
398
|
+
*/
|
|
399
|
+
stop() {
|
|
400
|
+
this.state.isPlaying = false;
|
|
401
|
+
this.state.isPaused = false;
|
|
402
|
+
this.emit('stopped', { step: this.state.currentStep });
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Cambia velocidad de replay
|
|
407
|
+
*/
|
|
408
|
+
setSpeed(speed) {
|
|
409
|
+
this.config.speed = speed;
|
|
410
|
+
this.emit('speed_changed', { speed });
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Calcula delay entre pasos
|
|
415
|
+
*/
|
|
416
|
+
calculateDelay(result) {
|
|
417
|
+
// Base delay de 500ms, ajustado por velocidad
|
|
418
|
+
const baseDelay = 500;
|
|
419
|
+
return baseDelay / this.config.speed;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Event emitter
|
|
424
|
+
*/
|
|
425
|
+
on(event, callback) {
|
|
426
|
+
this.listeners.add({ event, callback });
|
|
427
|
+
return () => this.listeners.delete({ event, callback });
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
emit(event, data) {
|
|
431
|
+
for (const listener of this.listeners) {
|
|
432
|
+
if (listener.event === event) {
|
|
433
|
+
listener.callback(data);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
sleep(ms) {
|
|
439
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### 3. What-If Analyzer
|
|
445
|
+
|
|
446
|
+
Analizador de escenarios alternativos.
|
|
447
|
+
|
|
448
|
+
```javascript
|
|
449
|
+
/**
|
|
450
|
+
* WhatIfAnalyzer
|
|
451
|
+
* Analiza escenarios alternativos ("what-if")
|
|
452
|
+
*/
|
|
453
|
+
class WhatIfAnalyzer {
|
|
454
|
+
constructor(timelineManager, flowEngine) {
|
|
455
|
+
this.timeline = timelineManager;
|
|
456
|
+
this.flowEngine = flowEngine;
|
|
457
|
+
this.scenarios = new Map();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Crea un escenario what-if desde un punto
|
|
462
|
+
*/
|
|
463
|
+
async createScenario(name, options = {}) {
|
|
464
|
+
const { fromPosition, alternativeDecision, alternativeInputs } = options;
|
|
465
|
+
|
|
466
|
+
// Crear branch
|
|
467
|
+
await this.timeline.jumpTo(fromPosition);
|
|
468
|
+
await this.timeline.createBranch(`whatif_${name}`);
|
|
469
|
+
|
|
470
|
+
const scenario = {
|
|
471
|
+
id: `scenario_${Date.now()}`,
|
|
472
|
+
name,
|
|
473
|
+
createdAt: new Date().toISOString(),
|
|
474
|
+
branchPoint: fromPosition,
|
|
475
|
+
originalDecision: this.timeline.timeline[fromPosition],
|
|
476
|
+
alternativeDecision,
|
|
477
|
+
alternativeInputs,
|
|
478
|
+
status: 'created',
|
|
479
|
+
results: null
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
this.scenarios.set(scenario.id, scenario);
|
|
483
|
+
|
|
484
|
+
return scenario;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Ejecuta un escenario what-if
|
|
489
|
+
*/
|
|
490
|
+
async runScenario(scenarioId) {
|
|
491
|
+
const scenario = this.scenarios.get(scenarioId);
|
|
492
|
+
|
|
493
|
+
if (!scenario) {
|
|
494
|
+
throw new Error(`Scenario ${scenarioId} not found`);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
scenario.status = 'running';
|
|
498
|
+
scenario.startedAt = new Date().toISOString();
|
|
499
|
+
|
|
500
|
+
try {
|
|
501
|
+
// Restaurar estado en branch point
|
|
502
|
+
const state = await this.timeline.checkpointManager.getStateAt(
|
|
503
|
+
this.timeline.timeline[scenario.branchPoint].checkpointId
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
// Aplicar decisión/inputs alternativos
|
|
507
|
+
const modifiedState = this.applyAlternatives(state, scenario);
|
|
508
|
+
|
|
509
|
+
// Ejecutar flow desde ese punto
|
|
510
|
+
const result = await this.flowEngine.executeFromState(modifiedState, {
|
|
511
|
+
branchName: `whatif_${scenario.name}`,
|
|
512
|
+
dryRun: false
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
scenario.status = 'completed';
|
|
516
|
+
scenario.completedAt = new Date().toISOString();
|
|
517
|
+
scenario.results = result;
|
|
518
|
+
|
|
519
|
+
return scenario;
|
|
520
|
+
|
|
521
|
+
} catch (error) {
|
|
522
|
+
scenario.status = 'failed';
|
|
523
|
+
scenario.error = error.message;
|
|
524
|
+
throw error;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Aplica alternativas al estado
|
|
530
|
+
*/
|
|
531
|
+
applyAlternatives(state, scenario) {
|
|
532
|
+
const modified = JSON.parse(JSON.stringify(state));
|
|
533
|
+
|
|
534
|
+
if (scenario.alternativeDecision) {
|
|
535
|
+
// Reemplazar decisión
|
|
536
|
+
modified.decisions = modified.decisions || [];
|
|
537
|
+
modified.decisions.push({
|
|
538
|
+
original: scenario.originalDecision,
|
|
539
|
+
alternative: scenario.alternativeDecision,
|
|
540
|
+
appliedAt: new Date().toISOString()
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (scenario.alternativeInputs) {
|
|
545
|
+
// Merge inputs alternativos
|
|
546
|
+
modified.variables = {
|
|
547
|
+
...modified.variables,
|
|
548
|
+
...scenario.alternativeInputs
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
return modified;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Compara dos escenarios
|
|
557
|
+
*/
|
|
558
|
+
async compareScenarios(scenarioId1, scenarioId2) {
|
|
559
|
+
const s1 = this.scenarios.get(scenarioId1);
|
|
560
|
+
const s2 = this.scenarios.get(scenarioId2);
|
|
561
|
+
|
|
562
|
+
if (!s1 || !s2) {
|
|
563
|
+
throw new Error('One or both scenarios not found');
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (s1.status !== 'completed' || s2.status !== 'completed') {
|
|
567
|
+
throw new Error('Both scenarios must be completed');
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
return {
|
|
571
|
+
scenario1: {
|
|
572
|
+
id: s1.id,
|
|
573
|
+
name: s1.name,
|
|
574
|
+
decision: s1.alternativeDecision || 'original'
|
|
575
|
+
},
|
|
576
|
+
scenario2: {
|
|
577
|
+
id: s2.id,
|
|
578
|
+
name: s2.name,
|
|
579
|
+
decision: s2.alternativeDecision || 'original'
|
|
580
|
+
},
|
|
581
|
+
comparison: {
|
|
582
|
+
filesCreated: this.compareLists(
|
|
583
|
+
s1.results.filesCreated,
|
|
584
|
+
s2.results.filesCreated
|
|
585
|
+
),
|
|
586
|
+
filesModified: this.compareLists(
|
|
587
|
+
s1.results.filesModified,
|
|
588
|
+
s2.results.filesModified
|
|
589
|
+
),
|
|
590
|
+
duration: {
|
|
591
|
+
scenario1: s1.results.duration,
|
|
592
|
+
scenario2: s2.results.duration,
|
|
593
|
+
difference: s2.results.duration - s1.results.duration
|
|
594
|
+
},
|
|
595
|
+
success: {
|
|
596
|
+
scenario1: s1.results.success,
|
|
597
|
+
scenario2: s2.results.success
|
|
598
|
+
},
|
|
599
|
+
errors: {
|
|
600
|
+
scenario1: s1.results.errors?.length || 0,
|
|
601
|
+
scenario2: s2.results.errors?.length || 0
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Compara dos listas
|
|
609
|
+
*/
|
|
610
|
+
compareLists(list1, list2) {
|
|
611
|
+
const set1 = new Set(list1?.map(f => f.path || f) || []);
|
|
612
|
+
const set2 = new Set(list2?.map(f => f.path || f) || []);
|
|
613
|
+
|
|
614
|
+
return {
|
|
615
|
+
onlyIn1: [...set1].filter(x => !set2.has(x)),
|
|
616
|
+
onlyIn2: [...set2].filter(x => !set1.has(x)),
|
|
617
|
+
inBoth: [...set1].filter(x => set2.has(x))
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Lista todos los escenarios
|
|
623
|
+
*/
|
|
624
|
+
listScenarios() {
|
|
625
|
+
return Array.from(this.scenarios.values()).map(s => ({
|
|
626
|
+
id: s.id,
|
|
627
|
+
name: s.name,
|
|
628
|
+
status: s.status,
|
|
629
|
+
branchPoint: s.branchPoint,
|
|
630
|
+
createdAt: s.createdAt
|
|
631
|
+
}));
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
---
|
|
637
|
+
|
|
638
|
+
## Interfaz de Usuario
|
|
639
|
+
|
|
640
|
+
### Timeline Visualization
|
|
641
|
+
|
|
642
|
+
```
|
|
643
|
+
╔══════════════════════════════════════════════════════════════════════════╗
|
|
644
|
+
║ TIME-TRAVEL DEBUGGER ║
|
|
645
|
+
╠══════════════════════════════════════════════════════════════════════════╣
|
|
646
|
+
║ ║
|
|
647
|
+
║ TIMELINE (Branch: main) ║
|
|
648
|
+
║ ═══════════════════════════════════════════════════════════════════ ║
|
|
649
|
+
║ ║
|
|
650
|
+
║ [1]──[2]──[3]──[4]──[5]──[6]──[7]──►[8] ║
|
|
651
|
+
║ │ │ │ │ │ │ │ │ ║
|
|
652
|
+
║ │ │ │ │ │ │ │ └─ Current: Review complete ║
|
|
653
|
+
║ │ │ │ │ │ │ └─────── Implementation done ║
|
|
654
|
+
║ │ │ │ │ │ └──────────── Tests written ║
|
|
655
|
+
║ │ │ │ │ └───────────────── Code generated ║
|
|
656
|
+
║ │ │ │ └────────────────────── Plan approved ║
|
|
657
|
+
║ │ │ └─────────────────────────── Analysis complete ║
|
|
658
|
+
║ │ └──────────────────────────────── Started analysis ║
|
|
659
|
+
║ └───────────────────────────────────── Session started ║
|
|
660
|
+
║ ║
|
|
661
|
+
║ Position: 8/8 (latest) ║
|
|
662
|
+
║ ║
|
|
663
|
+
╠══════════════════════════════════════════════════════════════════════════╣
|
|
664
|
+
║ CONTROLS ║
|
|
665
|
+
║ [◄◄] First [◄] Back [▶] Forward [►►] Last [▶▶] Play [⏸] Pause ║
|
|
666
|
+
║ ║
|
|
667
|
+
║ BRANCHES ║
|
|
668
|
+
║ • main (active) - 8 checkpoints ║
|
|
669
|
+
║ • whatif_different_auth - 5 checkpoints (branched from #4) ║
|
|
670
|
+
║ ║
|
|
671
|
+
╠══════════════════════════════════════════════════════════════════════════╣
|
|
672
|
+
║ CHECKPOINT #8 DETAILS ║
|
|
673
|
+
║ ┌────────────────────────────────────────────────────────────────────┐ ║
|
|
674
|
+
║ │ Event: PHASE_COMPLETE │ ║
|
|
675
|
+
║ │ Phase: review │ ║
|
|
676
|
+
║ │ Timestamp: 2024-01-20T15:45:32.123Z │ ║
|
|
677
|
+
║ │ Duration since start: 2m 34s │ ║
|
|
678
|
+
║ │ │ ║
|
|
679
|
+
║ │ State: │ ║
|
|
680
|
+
║ │ • Files created: 3 │ ║
|
|
681
|
+
║ │ • Files modified: 2 │ ║
|
|
682
|
+
║ │ • Tests: 12 passing │ ║
|
|
683
|
+
║ │ • Review: approved │ ║
|
|
684
|
+
║ └────────────────────────────────────────────────────────────────────┘ ║
|
|
685
|
+
║ ║
|
|
686
|
+
╚══════════════════════════════════════════════════════════════════════════╝
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### Comparison View
|
|
690
|
+
|
|
691
|
+
```
|
|
692
|
+
╔══════════════════════════════════════════════════════════════════════════╗
|
|
693
|
+
║ SCENARIO COMPARISON ║
|
|
694
|
+
╠══════════════════════════════════════════════════════════════════════════╣
|
|
695
|
+
║ ║
|
|
696
|
+
║ Scenario A: Original (NextAuth) │ Scenario B: Alternative (Clerk) ║
|
|
697
|
+
║ ───────────────────────────────── │ ─────────────────────────────────║
|
|
698
|
+
║ │ ║
|
|
699
|
+
║ Decision at checkpoint #4: │ Decision at checkpoint #4: ║
|
|
700
|
+
║ "Use NextAuth.js for auth" │ "Use Clerk for auth" ║
|
|
701
|
+
║ │ ║
|
|
702
|
+
║ Files Created: 5 │ Files Created: 3 ║
|
|
703
|
+
║ • src/auth/[...nextauth].ts │ • src/middleware.ts ║
|
|
704
|
+
║ • src/auth/providers.ts │ • src/lib/clerk.ts ║
|
|
705
|
+
║ • src/auth/callbacks.ts │ • src/components/ClerkProvider.tsx║
|
|
706
|
+
║ • src/lib/auth.ts │ ║
|
|
707
|
+
║ • src/types/next-auth.d.ts │ ║
|
|
708
|
+
║ │ ║
|
|
709
|
+
║ Duration: 45s │ Duration: 28s ║
|
|
710
|
+
║ Tests: 8 passing │ Tests: 5 passing ║
|
|
711
|
+
║ Complexity: Medium │ Complexity: Low ║
|
|
712
|
+
║ │ ║
|
|
713
|
+
╠══════════════════════════════════════════════════════════════════════════╣
|
|
714
|
+
║ ANALYSIS ║
|
|
715
|
+
║ • Clerk approach is 38% faster to implement ║
|
|
716
|
+
║ • NextAuth has more flexibility but more boilerplate ║
|
|
717
|
+
║ • Clerk has better DX but vendor lock-in ║
|
|
718
|
+
╚══════════════════════════════════════════════════════════════════════════╝
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
---
|
|
722
|
+
|
|
723
|
+
## Comandos de Usuario
|
|
724
|
+
|
|
725
|
+
### /elsabro:timetravel
|
|
726
|
+
|
|
727
|
+
```bash
|
|
728
|
+
/elsabro:timetravel show # Mostrar timeline actual
|
|
729
|
+
/elsabro:timetravel goto <position> # Saltar a posición
|
|
730
|
+
/elsabro:timetravel back # Retroceder un paso
|
|
731
|
+
/elsabro:timetravel forward # Avanzar un paso
|
|
732
|
+
/elsabro:timetravel replay [from] [to] # Replay de rango
|
|
733
|
+
/elsabro:timetravel branch <name> # Crear branch alternativo
|
|
734
|
+
/elsabro:timetravel branches # Listar branches
|
|
735
|
+
/elsabro:timetravel switch <branch> # Cambiar de branch
|
|
736
|
+
/elsabro:timetravel whatif <name> # Crear escenario what-if
|
|
737
|
+
/elsabro:timetravel compare <s1> <s2> # Comparar escenarios
|
|
738
|
+
/elsabro:timetravel diff <cp1> <cp2> # Diff entre checkpoints
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
---
|
|
742
|
+
|
|
743
|
+
## Integración con Checkpointing
|
|
744
|
+
|
|
745
|
+
El sistema de time-travel se construye sobre el sistema de checkpointing existente:
|
|
746
|
+
|
|
747
|
+
```javascript
|
|
748
|
+
// Inicialización
|
|
749
|
+
const checkpointManager = new CheckpointManager();
|
|
750
|
+
const timelineManager = new TimelineManager(checkpointManager);
|
|
751
|
+
const replayEngine = new ReplayEngine(timelineManager);
|
|
752
|
+
const whatIfAnalyzer = new WhatIfAnalyzer(timelineManager, flowEngine);
|
|
753
|
+
|
|
754
|
+
// Construir timeline desde sesión
|
|
755
|
+
await timelineManager.buildTimeline(sessionId);
|
|
756
|
+
|
|
757
|
+
// Navegar
|
|
758
|
+
await timelineManager.jumpTo(5);
|
|
759
|
+
await timelineManager.stepBackward();
|
|
760
|
+
|
|
761
|
+
// Replay
|
|
762
|
+
await replayEngine.startReplay({ from: 0, to: 10 });
|
|
763
|
+
|
|
764
|
+
// What-if analysis
|
|
765
|
+
const scenario = await whatIfAnalyzer.createScenario('try_clerk', {
|
|
766
|
+
fromPosition: 4,
|
|
767
|
+
alternativeDecision: 'Use Clerk instead of NextAuth'
|
|
768
|
+
});
|
|
769
|
+
await whatIfAnalyzer.runScenario(scenario.id);
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
---
|
|
773
|
+
|
|
774
|
+
## Configuración
|
|
775
|
+
|
|
776
|
+
### .planning/timetravel-config.json
|
|
777
|
+
|
|
778
|
+
```json
|
|
779
|
+
{
|
|
780
|
+
"timeTravel": {
|
|
781
|
+
"enabled": true,
|
|
782
|
+
"maxTimelineLength": 1000,
|
|
783
|
+
"maxBranches": 10,
|
|
784
|
+
"replay": {
|
|
785
|
+
"defaultSpeed": 1.0,
|
|
786
|
+
"pauseOnDecision": true,
|
|
787
|
+
"pauseOnError": true,
|
|
788
|
+
"autoPlay": false
|
|
789
|
+
},
|
|
790
|
+
"whatIf": {
|
|
791
|
+
"enabled": true,
|
|
792
|
+
"maxScenarios": 20,
|
|
793
|
+
"autoCompare": true
|
|
794
|
+
},
|
|
795
|
+
"visualization": {
|
|
796
|
+
"showTimestamps": true,
|
|
797
|
+
"showDurations": true,
|
|
798
|
+
"compactMode": false
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
```
|