elsabro 2.3.0 → 3.7.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.
Files changed (67) hide show
  1. package/README.md +668 -20
  2. package/bin/install.js +0 -0
  3. package/flows/development-flow.json +452 -0
  4. package/flows/quick-flow.json +118 -0
  5. package/package.json +3 -2
  6. package/references/SYSTEM_INDEX.md +379 -5
  7. package/references/agent-marketplace.md +2274 -0
  8. package/references/agent-protocol.md +1126 -0
  9. package/references/ai-code-suggestions.md +2413 -0
  10. package/references/checkpointing.md +595 -0
  11. package/references/collaboration-patterns.md +851 -0
  12. package/references/collaborative-sessions.md +1081 -0
  13. package/references/configuration-management.md +1810 -0
  14. package/references/cost-tracking.md +1095 -0
  15. package/references/enterprise-sso.md +2001 -0
  16. package/references/error-contracts-v2.md +968 -0
  17. package/references/event-driven.md +1031 -0
  18. package/references/flow-orchestration.md +940 -0
  19. package/references/flow-visualization.md +1557 -0
  20. package/references/ide-integrations.md +3513 -0
  21. package/references/interrupt-system.md +681 -0
  22. package/references/kubernetes-deployment.md +3099 -0
  23. package/references/memory-system.md +683 -0
  24. package/references/mobile-companion.md +3236 -0
  25. package/references/multi-llm-providers.md +2494 -0
  26. package/references/multi-project-memory.md +1182 -0
  27. package/references/observability.md +793 -0
  28. package/references/output-schemas.md +858 -0
  29. package/references/performance-profiler.md +955 -0
  30. package/references/plugin-system.md +1526 -0
  31. package/references/prompt-management.md +292 -0
  32. package/references/sandbox-execution.md +303 -0
  33. package/references/security-system.md +1253 -0
  34. package/references/streaming.md +696 -0
  35. package/references/testing-framework.md +1151 -0
  36. package/references/time-travel.md +802 -0
  37. package/references/tool-registry.md +886 -0
  38. package/references/voice-commands.md +3296 -0
  39. package/templates/agent-marketplace-config.json +220 -0
  40. package/templates/agent-protocol-config.json +136 -0
  41. package/templates/ai-suggestions-config.json +100 -0
  42. package/templates/checkpoint-state.json +61 -0
  43. package/templates/collaboration-config.json +157 -0
  44. package/templates/collaborative-sessions-config.json +153 -0
  45. package/templates/configuration-config.json +245 -0
  46. package/templates/cost-tracking-config.json +148 -0
  47. package/templates/enterprise-sso-config.json +438 -0
  48. package/templates/events-config.json +148 -0
  49. package/templates/flow-visualization-config.json +196 -0
  50. package/templates/ide-integrations-config.json +442 -0
  51. package/templates/kubernetes-config.json +764 -0
  52. package/templates/memory-state.json +84 -0
  53. package/templates/mobile-companion-config.json +600 -0
  54. package/templates/multi-llm-config.json +544 -0
  55. package/templates/multi-project-memory-config.json +145 -0
  56. package/templates/observability-config.json +109 -0
  57. package/templates/performance-profiler-config.json +125 -0
  58. package/templates/plugin-config.json +170 -0
  59. package/templates/prompt-management-config.json +86 -0
  60. package/templates/sandbox-config.json +185 -0
  61. package/templates/schemas-config.json +65 -0
  62. package/templates/security-config.json +120 -0
  63. package/templates/streaming-config.json +72 -0
  64. package/templates/testing-config.json +81 -0
  65. package/templates/timetravel-config.json +62 -0
  66. package/templates/tool-registry-config.json +109 -0
  67. package/templates/voice-commands-config.json +658 -0
@@ -0,0 +1,681 @@
1
+ ---
2
+ name: interrupt-system
3
+ description: Sistema de interrupts para intervención humana
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # ELSABRO Interrupt System
8
+
9
+ ## Vision General
10
+
11
+ El sistema de Interrupts permite pausar la ejecución en puntos críticos para obtener input del usuario, aprobar decisiones, o manejar situaciones excepcionales.
12
+
13
+ ```
14
+ ┌──────────────────────────────────────────────────────────────────────────┐
15
+ │ INTERRUPT FLOW │
16
+ ├──────────────────────────────────────────────────────────────────────────┤
17
+ │ │
18
+ │ EXECUTION │
19
+ │ │ │
20
+ │ ▼ │
21
+ │ ┌──────────┐ │
22
+ │ │ NODE A │ │
23
+ │ └────┬─────┘ │
24
+ │ │ │
25
+ │ ▼ │
26
+ │ ┌──────────────────────────────────────────┐ │
27
+ │ │ INTERRUPT TRIGGERED │ │
28
+ │ │ ┌────────────────────────────────────┐ │ │
29
+ │ │ │ Reason: Approval required │ │ │
30
+ │ │ │ Data: {...plan details...} │ │ │
31
+ │ │ │ Options: [approve, modify, reject] │ │ │
32
+ │ │ └────────────────────────────────────┘ │ │
33
+ │ └─────────────────┬────────────────────────┘ │
34
+ │ │ │
35
+ │ SAVE CHECKPOINT │
36
+ │ │ │
37
+ │ WAIT FOR USER │
38
+ │ │ │
39
+ │ ▼ │
40
+ │ ┌──────────────┐ │
41
+ │ │ USER RESPONDS │ │
42
+ │ └──────┬───────┘ │
43
+ │ │ │
44
+ │ ┌──────────┼──────────┐ │
45
+ │ ▼ ▼ ▼ │
46
+ │ [approve] [modify] [reject] │
47
+ │ │ │ │ │
48
+ │ ▼ ▼ ▼ │
49
+ │ NODE B NODE A END │
50
+ │ (retry) │
51
+ │ │
52
+ └──────────────────────────────────────────────────────────────────────────┘
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Tipos de Interrupts
58
+
59
+ ### 1. Approval Gates
60
+
61
+ Requiere aprobación antes de continuar.
62
+
63
+ ```json
64
+ {
65
+ "type": "approval",
66
+ "trigger": "before",
67
+ "nodeId": "deploy",
68
+ "display": {
69
+ "title": "🚀 Deployment Approval",
70
+ "content": "Ready to deploy to production",
71
+ "data": {
72
+ "environment": "production",
73
+ "changes": 15,
74
+ "testsPassed": true
75
+ }
76
+ },
77
+ "options": [
78
+ { "id": "approve", "label": "Deploy Now", "next": "deploy" },
79
+ { "id": "delay", "label": "Schedule Later", "next": "schedule" },
80
+ { "id": "reject", "label": "Cancel", "next": "end" }
81
+ ],
82
+ "timeout": 86400000,
83
+ "default": "reject"
84
+ }
85
+ ```
86
+
87
+ ### 2. Decision Points
88
+
89
+ Usuario debe elegir entre opciones.
90
+
91
+ ```json
92
+ {
93
+ "type": "decision",
94
+ "nodeId": "choose_approach",
95
+ "display": {
96
+ "title": "💡 Choose Implementation Approach",
97
+ "content": "Multiple valid approaches found",
98
+ "options": [
99
+ {
100
+ "id": "approach_a",
101
+ "label": "Server Components",
102
+ "description": "Better performance, simpler code",
103
+ "recommended": true
104
+ },
105
+ {
106
+ "id": "approach_b",
107
+ "label": "Client Components + API",
108
+ "description": "More flexible, easier testing"
109
+ }
110
+ ]
111
+ },
112
+ "routes": {
113
+ "approach_a": "implement_server",
114
+ "approach_b": "implement_client"
115
+ }
116
+ }
117
+ ```
118
+
119
+ ### 3. Error Recovery
120
+
121
+ Intervención cuando auto-recovery falla.
122
+
123
+ ```json
124
+ {
125
+ "type": "error_recovery",
126
+ "nodeId": "fix_failed",
127
+ "trigger": "on_max_iterations",
128
+ "display": {
129
+ "title": "⚠️ Auto-Fix Failed",
130
+ "content": "Unable to automatically resolve issues",
131
+ "errors": ["TypeScript error in auth.ts:45", "Test failure in login.test.ts"],
132
+ "attempts": 3
133
+ },
134
+ "options": [
135
+ { "id": "retry", "label": "Retry Auto-Fix" },
136
+ { "id": "manual", "label": "I'll Fix Manually" },
137
+ { "id": "skip", "label": "Skip and Continue" },
138
+ { "id": "abort", "label": "Abort Execution" }
139
+ ]
140
+ }
141
+ ```
142
+
143
+ ### 4. Information Request
144
+
145
+ Necesita input del usuario para continuar.
146
+
147
+ ```json
148
+ {
149
+ "type": "input",
150
+ "nodeId": "get_config",
151
+ "display": {
152
+ "title": "🔧 Configuration Required",
153
+ "content": "Need some information to proceed",
154
+ "fields": [
155
+ {
156
+ "id": "api_key",
157
+ "type": "secret",
158
+ "label": "API Key",
159
+ "required": true
160
+ },
161
+ {
162
+ "id": "environment",
163
+ "type": "select",
164
+ "label": "Environment",
165
+ "options": ["development", "staging", "production"],
166
+ "default": "development"
167
+ }
168
+ ]
169
+ },
170
+ "next": "continue_with_config"
171
+ }
172
+ ```
173
+
174
+ ### 5. Confirmation
175
+
176
+ Confirmar acción potencialmente destructiva.
177
+
178
+ ```json
179
+ {
180
+ "type": "confirmation",
181
+ "nodeId": "before_delete",
182
+ "display": {
183
+ "title": "⚠️ Confirm Deletion",
184
+ "content": "This action cannot be undone",
185
+ "warning": "You are about to delete 5 files",
186
+ "files": ["src/old/auth.ts", "src/old/user.ts", "..."]
187
+ },
188
+ "options": [
189
+ { "id": "confirm", "label": "Yes, Delete", "style": "danger" },
190
+ { "id": "cancel", "label": "Cancel", "style": "default" }
191
+ ],
192
+ "default": "cancel"
193
+ }
194
+ ```
195
+
196
+ ### 6. Progress Checkpoint
197
+
198
+ Checkpoint manual durante operación larga.
199
+
200
+ ```json
201
+ {
202
+ "type": "checkpoint",
203
+ "nodeId": "midpoint",
204
+ "display": {
205
+ "title": "📍 Progress Checkpoint",
206
+ "content": "50% complete - Want to continue?",
207
+ "progress": {
208
+ "completed": ["Phase 1", "Phase 2"],
209
+ "remaining": ["Phase 3", "Phase 4"]
210
+ }
211
+ },
212
+ "options": [
213
+ { "id": "continue", "label": "Continue" },
214
+ { "id": "pause", "label": "Pause Here" },
215
+ { "id": "stop", "label": "Stop Execution" }
216
+ ]
217
+ }
218
+ ```
219
+
220
+ ---
221
+
222
+ ## API del Sistema de Interrupts
223
+
224
+ ### InterruptManager
225
+
226
+ ```javascript
227
+ /**
228
+ * InterruptManager
229
+ * Gestiona interrupts para intervención humana
230
+ */
231
+ class InterruptManager {
232
+ constructor(options = {}) {
233
+ this.checkpointManager = options.checkpointManager;
234
+ this.notificationService = options.notificationService;
235
+
236
+ this.config = {
237
+ defaultTimeout: options.defaultTimeout || 86400000, // 24 horas
238
+ persistInterrupts: options.persistInterrupts !== false
239
+ };
240
+
241
+ this.activeInterrupts = new Map();
242
+ this.history = [];
243
+ }
244
+
245
+ /**
246
+ * Crea y dispara un interrupt
247
+ */
248
+ async trigger(interruptDef, context = {}) {
249
+ const interrupt = {
250
+ id: this.generateId(),
251
+ type: interruptDef.type,
252
+ nodeId: interruptDef.nodeId,
253
+ display: this.resolveTemplate(interruptDef.display, context),
254
+ options: interruptDef.options,
255
+ routes: interruptDef.routes,
256
+ timeout: interruptDef.timeout || this.config.defaultTimeout,
257
+ default: interruptDef.default,
258
+ createdAt: new Date().toISOString(),
259
+ context: context,
260
+ status: 'pending'
261
+ };
262
+
263
+ // Guardar interrupt
264
+ this.activeInterrupts.set(interrupt.id, interrupt);
265
+
266
+ // Persistir en checkpoint si está habilitado
267
+ if (this.config.persistInterrupts && this.checkpointManager) {
268
+ await this.checkpointManager.saveInterrupt(interrupt.type, {
269
+ interruptId: interrupt.id,
270
+ display: interrupt.display,
271
+ options: interrupt.options
272
+ });
273
+ }
274
+
275
+ // Notificar al usuario
276
+ await this.notifyUser(interrupt);
277
+
278
+ // Configurar timeout
279
+ this.setupTimeout(interrupt);
280
+
281
+ return interrupt;
282
+ }
283
+
284
+ /**
285
+ * Espera respuesta del usuario
286
+ */
287
+ async waitForResponse(interruptId) {
288
+ const interrupt = this.activeInterrupts.get(interruptId);
289
+
290
+ if (!interrupt) {
291
+ throw new Error(`Interrupt ${interruptId} not found`);
292
+ }
293
+
294
+ return new Promise((resolve, reject) => {
295
+ interrupt.resolver = resolve;
296
+ interrupt.rejecter = reject;
297
+ });
298
+ }
299
+
300
+ /**
301
+ * Procesa respuesta del usuario
302
+ */
303
+ async respond(interruptId, response, additionalData = {}) {
304
+ const interrupt = this.activeInterrupts.get(interruptId);
305
+
306
+ if (!interrupt) {
307
+ throw new Error(`Interrupt ${interruptId} not found`);
308
+ }
309
+
310
+ if (interrupt.status !== 'pending') {
311
+ throw new Error(`Interrupt ${interruptId} already resolved`);
312
+ }
313
+
314
+ // Validar respuesta
315
+ const validOptions = interrupt.options.map(o => o.id);
316
+ if (!validOptions.includes(response)) {
317
+ throw new Error(`Invalid response: ${response}. Valid: ${validOptions.join(', ')}`);
318
+ }
319
+
320
+ // Actualizar interrupt
321
+ interrupt.status = 'resolved';
322
+ interrupt.response = response;
323
+ interrupt.responseData = additionalData;
324
+ interrupt.resolvedAt = new Date().toISOString();
325
+
326
+ // Determinar siguiente nodo
327
+ const nextNode = interrupt.routes
328
+ ? interrupt.routes[response]
329
+ : interrupt.options.find(o => o.id === response)?.next;
330
+
331
+ // Mover a historial
332
+ this.history.push(interrupt);
333
+ this.activeInterrupts.delete(interruptId);
334
+
335
+ // Resolver promise si existe
336
+ if (interrupt.resolver) {
337
+ interrupt.resolver({
338
+ response,
339
+ nextNode,
340
+ data: additionalData
341
+ });
342
+ }
343
+
344
+ return {
345
+ response,
346
+ nextNode,
347
+ interrupt
348
+ };
349
+ }
350
+
351
+ /**
352
+ * Cancela un interrupt
353
+ */
354
+ async cancel(interruptId, reason = 'cancelled') {
355
+ const interrupt = this.activeInterrupts.get(interruptId);
356
+
357
+ if (interrupt) {
358
+ interrupt.status = 'cancelled';
359
+ interrupt.cancelReason = reason;
360
+ interrupt.cancelledAt = new Date().toISOString();
361
+
362
+ this.history.push(interrupt);
363
+ this.activeInterrupts.delete(interruptId);
364
+
365
+ if (interrupt.rejecter) {
366
+ interrupt.rejecter(new Error(`Interrupt cancelled: ${reason}`));
367
+ }
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Configura timeout para interrupt
373
+ */
374
+ setupTimeout(interrupt) {
375
+ if (interrupt.timeout > 0) {
376
+ setTimeout(() => {
377
+ if (this.activeInterrupts.has(interrupt.id)) {
378
+ const defaultResponse = interrupt.default;
379
+
380
+ if (defaultResponse) {
381
+ this.respond(interrupt.id, defaultResponse, { timedOut: true });
382
+ } else {
383
+ this.cancel(interrupt.id, 'timeout');
384
+ }
385
+ }
386
+ }, interrupt.timeout);
387
+ }
388
+ }
389
+
390
+ /**
391
+ * Notifica al usuario sobre el interrupt
392
+ */
393
+ async notifyUser(interrupt) {
394
+ const display = interrupt.display;
395
+
396
+ // Formato de consola
397
+ const notification = `
398
+ ╔════════════════════════════════════════════════════════════════╗
399
+ ║ ${this.getIcon(interrupt.type)} ${display.title.padEnd(54)}║
400
+ ╠════════════════════════════════════════════════════════════════╣
401
+ ${this.formatContent(display.content)}
402
+ ${display.data ? this.formatData(display.data) : ''}
403
+ ${display.warning ? `║ ⚠️ ${display.warning.padEnd(55)}║\n` : ''}
404
+ ╠════════════════════════════════════════════════════════════════╣
405
+ ${this.formatOptions(interrupt.options)}
406
+ ╚════════════════════════════════════════════════════════════════╝
407
+ `;
408
+
409
+ console.log(notification);
410
+
411
+ // También notificar via servicio si está configurado
412
+ if (this.notificationService) {
413
+ await this.notificationService.send({
414
+ type: 'interrupt',
415
+ id: interrupt.id,
416
+ display: display
417
+ });
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Lista interrupts pendientes
423
+ */
424
+ listPending() {
425
+ return Array.from(this.activeInterrupts.values()).map(i => ({
426
+ id: i.id,
427
+ type: i.type,
428
+ title: i.display.title,
429
+ createdAt: i.createdAt,
430
+ timeout: i.timeout
431
+ }));
432
+ }
433
+
434
+ /**
435
+ * Obtiene historial de interrupts
436
+ */
437
+ getHistory(options = {}) {
438
+ let history = [...this.history];
439
+
440
+ if (options.type) {
441
+ history = history.filter(i => i.type === options.type);
442
+ }
443
+
444
+ if (options.limit) {
445
+ history = history.slice(-options.limit);
446
+ }
447
+
448
+ return history;
449
+ }
450
+
451
+ // ==================== Helpers ====================
452
+
453
+ generateId() {
454
+ return `int_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
455
+ }
456
+
457
+ resolveTemplate(template, context) {
458
+ if (typeof template === 'string') {
459
+ return template.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
460
+ return this.getNestedValue(context, path.trim()) || match;
461
+ });
462
+ }
463
+
464
+ if (typeof template === 'object' && template !== null) {
465
+ const resolved = Array.isArray(template) ? [] : {};
466
+ for (const [key, value] of Object.entries(template)) {
467
+ resolved[key] = this.resolveTemplate(value, context);
468
+ }
469
+ return resolved;
470
+ }
471
+
472
+ return template;
473
+ }
474
+
475
+ getNestedValue(obj, path) {
476
+ return path.split('.').reduce((current, key) => current?.[key], obj);
477
+ }
478
+
479
+ getIcon(type) {
480
+ const icons = {
481
+ approval: '✅',
482
+ decision: '💡',
483
+ error_recovery: '⚠️',
484
+ input: '🔧',
485
+ confirmation: '❓',
486
+ checkpoint: '📍'
487
+ };
488
+ return icons[type] || '📢';
489
+ }
490
+
491
+ formatContent(content) {
492
+ const lines = content.split('\n');
493
+ return lines.map(line => `║ ${line.padEnd(60)}║`).join('\n');
494
+ }
495
+
496
+ formatData(data) {
497
+ let output = '';
498
+ for (const [key, value] of Object.entries(data)) {
499
+ output += `║ • ${key}: ${JSON.stringify(value).substring(0, 50).padEnd(52)}║\n`;
500
+ }
501
+ return output;
502
+ }
503
+
504
+ formatOptions(options) {
505
+ return options.map(opt => {
506
+ const style = opt.style === 'danger' ? '🔴' : opt.recommended ? '⭐' : ' ';
507
+ return `║ ${style} [${opt.id}] ${opt.label.padEnd(50)}║`;
508
+ }).join('\n');
509
+ }
510
+ }
511
+ ```
512
+
513
+ ---
514
+
515
+ ## Integración con Flow Engine
516
+
517
+ ### Declaración en Flows
518
+
519
+ ```json
520
+ {
521
+ "id": "deploy_node",
522
+ "type": "agent",
523
+ "agent": "deploy-agent",
524
+ "interrupt": {
525
+ "before": {
526
+ "type": "approval",
527
+ "condition": "{{inputs.environment}} === 'production'",
528
+ "display": {
529
+ "title": "Production Deployment Approval",
530
+ "content": "Review changes before deploying to production"
531
+ }
532
+ }
533
+ }
534
+ }
535
+ ```
536
+
537
+ ### Interrupts Automáticos
538
+
539
+ Configurar interrupts que se disparan automáticamente:
540
+
541
+ ```json
542
+ {
543
+ "autoInterrupts": {
544
+ "onCriticalError": {
545
+ "type": "error_recovery",
546
+ "display": {
547
+ "title": "Critical Error",
548
+ "content": "{{error.message}}"
549
+ }
550
+ },
551
+ "onHighRisk": {
552
+ "type": "confirmation",
553
+ "condition": "{{riskScore}} > 0.8",
554
+ "display": {
555
+ "title": "High Risk Operation",
556
+ "warning": "This operation has been flagged as high risk"
557
+ }
558
+ },
559
+ "beforeDestructive": {
560
+ "type": "confirmation",
561
+ "trigger": "destructive_action",
562
+ "display": {
563
+ "title": "Confirm Destructive Action"
564
+ }
565
+ }
566
+ }
567
+ }
568
+ ```
569
+
570
+ ---
571
+
572
+ ## Comandos de Usuario
573
+
574
+ ### /elsabro:interrupt
575
+
576
+ ```bash
577
+ /elsabro:interrupt list # Ver interrupts pendientes
578
+ /elsabro:interrupt respond <id> <opt> # Responder a interrupt
579
+ /elsabro:interrupt cancel <id> # Cancelar interrupt
580
+ /elsabro:interrupt history # Ver historial de interrupts
581
+ ```
582
+
583
+ ---
584
+
585
+ ## Configuración
586
+
587
+ ### .planning/interrupt-config.json
588
+
589
+ ```json
590
+ {
591
+ "interrupts": {
592
+ "enabled": true,
593
+ "defaultTimeout": 86400000,
594
+ "persistToCheckpoint": true,
595
+ "notifications": {
596
+ "console": true,
597
+ "sound": false
598
+ },
599
+ "autoInterrupts": {
600
+ "onCriticalError": true,
601
+ "beforeDestructive": true,
602
+ "onHighRisk": true,
603
+ "progressCheckpoints": false
604
+ },
605
+ "defaults": {
606
+ "approval": "reject",
607
+ "confirmation": "cancel",
608
+ "error_recovery": "manual"
609
+ }
610
+ }
611
+ }
612
+ ```
613
+
614
+ ---
615
+
616
+ ## Ejemplos de Uso
617
+
618
+ ### Interrupt en Fase de Deploy
619
+
620
+ ```javascript
621
+ // En el flow de deployment
622
+ const deployInterrupt = await interruptManager.trigger({
623
+ type: 'approval',
624
+ nodeId: 'deploy',
625
+ display: {
626
+ title: '🚀 Production Deployment',
627
+ content: 'Ready to deploy v2.3.0 to production',
628
+ data: {
629
+ version: '2.3.0',
630
+ changes: 15,
631
+ tests: 'All passed',
632
+ coverage: '87%'
633
+ }
634
+ },
635
+ options: [
636
+ { id: 'deploy', label: 'Deploy Now', next: 'execute_deploy' },
637
+ { id: 'staging', label: 'Deploy to Staging First', next: 'staging_deploy' },
638
+ { id: 'cancel', label: 'Cancel', next: 'end' }
639
+ ],
640
+ timeout: 3600000 // 1 hora
641
+ });
642
+
643
+ const response = await interruptManager.waitForResponse(deployInterrupt.id);
644
+ console.log(`User chose: ${response.response}`);
645
+ ```
646
+
647
+ ### Interrupt en Auto-Fix Loop
648
+
649
+ ```javascript
650
+ // Después de N intentos de auto-fix
651
+ if (attempts >= maxAttempts) {
652
+ const fixInterrupt = await interruptManager.trigger({
653
+ type: 'error_recovery',
654
+ nodeId: 'fix_loop',
655
+ display: {
656
+ title: '⚠️ Auto-Fix Exhausted',
657
+ content: `Failed to fix after ${attempts} attempts`,
658
+ errors: collectedErrors
659
+ },
660
+ options: [
661
+ { id: 'retry', label: 'Retry with Different Approach' },
662
+ { id: 'manual', label: 'I\'ll Fix Manually' },
663
+ { id: 'skip', label: 'Skip and Continue' },
664
+ { id: 'abort', label: 'Abort' }
665
+ ]
666
+ });
667
+
668
+ const response = await interruptManager.waitForResponse(fixInterrupt.id);
669
+
670
+ switch (response.response) {
671
+ case 'retry':
672
+ return retryWithDifferentApproach();
673
+ case 'manual':
674
+ return waitForManualFix();
675
+ case 'skip':
676
+ return continueWithWarning();
677
+ case 'abort':
678
+ throw new Error('Aborted by user');
679
+ }
680
+ }
681
+ ```