elsabro 2.2.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 (88) hide show
  1. package/README.md +668 -20
  2. package/agents/elsabro-orchestrator.md +113 -0
  3. package/bin/install.js +0 -0
  4. package/commands/elsabro/execute.md +223 -46
  5. package/commands/elsabro/start.md +34 -0
  6. package/commands/elsabro/verify-work.md +29 -0
  7. package/flows/development-flow.json +452 -0
  8. package/flows/quick-flow.json +118 -0
  9. package/hooks/confirm-destructive.sh +145 -0
  10. package/hooks/hooks-config.json +81 -0
  11. package/hooks/lint-check.sh +238 -0
  12. package/hooks/post-edit-test.sh +189 -0
  13. package/package.json +5 -3
  14. package/references/SYSTEM_INDEX.md +379 -5
  15. package/references/agent-marketplace.md +2274 -0
  16. package/references/agent-protocol.md +1126 -0
  17. package/references/ai-code-suggestions.md +2413 -0
  18. package/references/checkpointing.md +595 -0
  19. package/references/collaboration-patterns.md +851 -0
  20. package/references/collaborative-sessions.md +1081 -0
  21. package/references/configuration-management.md +1810 -0
  22. package/references/cost-tracking.md +1095 -0
  23. package/references/enterprise-sso.md +2001 -0
  24. package/references/error-contracts-tests.md +1171 -0
  25. package/references/error-contracts-v2.md +968 -0
  26. package/references/error-contracts.md +3102 -0
  27. package/references/event-driven.md +1031 -0
  28. package/references/flow-orchestration.md +940 -0
  29. package/references/flow-visualization.md +1557 -0
  30. package/references/ide-integrations.md +3513 -0
  31. package/references/interrupt-system.md +681 -0
  32. package/references/kubernetes-deployment.md +3099 -0
  33. package/references/memory-system.md +683 -0
  34. package/references/mobile-companion.md +3236 -0
  35. package/references/multi-llm-providers.md +2494 -0
  36. package/references/multi-project-memory.md +1182 -0
  37. package/references/observability.md +793 -0
  38. package/references/output-schemas.md +858 -0
  39. package/references/parallel-worktrees.md +293 -0
  40. package/references/performance-profiler.md +955 -0
  41. package/references/plugin-system.md +1526 -0
  42. package/references/prompt-management.md +292 -0
  43. package/references/sandbox-execution.md +303 -0
  44. package/references/security-system.md +1253 -0
  45. package/references/streaming.md +696 -0
  46. package/references/testing-framework.md +1151 -0
  47. package/references/time-travel.md +802 -0
  48. package/references/tool-registry.md +886 -0
  49. package/references/voice-commands.md +3296 -0
  50. package/scripts/setup-parallel-worktrees.sh +319 -0
  51. package/skills/memory-update.md +207 -0
  52. package/skills/review.md +331 -0
  53. package/skills/techdebt.md +289 -0
  54. package/skills/tutor.md +219 -0
  55. package/templates/.planning/notes/.gitkeep +0 -0
  56. package/templates/CLAUDE.md.template +48 -0
  57. package/templates/agent-marketplace-config.json +220 -0
  58. package/templates/agent-protocol-config.json +136 -0
  59. package/templates/ai-suggestions-config.json +100 -0
  60. package/templates/checkpoint-state.json +61 -0
  61. package/templates/collaboration-config.json +157 -0
  62. package/templates/collaborative-sessions-config.json +153 -0
  63. package/templates/configuration-config.json +245 -0
  64. package/templates/cost-tracking-config.json +148 -0
  65. package/templates/enterprise-sso-config.json +438 -0
  66. package/templates/error-handling-config.json +79 -2
  67. package/templates/events-config.json +148 -0
  68. package/templates/flow-visualization-config.json +196 -0
  69. package/templates/ide-integrations-config.json +442 -0
  70. package/templates/kubernetes-config.json +764 -0
  71. package/templates/memory-state.json +84 -0
  72. package/templates/mistakes.md.template +52 -0
  73. package/templates/mobile-companion-config.json +600 -0
  74. package/templates/multi-llm-config.json +544 -0
  75. package/templates/multi-project-memory-config.json +145 -0
  76. package/templates/observability-config.json +109 -0
  77. package/templates/patterns.md.template +114 -0
  78. package/templates/performance-profiler-config.json +125 -0
  79. package/templates/plugin-config.json +170 -0
  80. package/templates/prompt-management-config.json +86 -0
  81. package/templates/sandbox-config.json +185 -0
  82. package/templates/schemas-config.json +65 -0
  83. package/templates/security-config.json +120 -0
  84. package/templates/streaming-config.json +72 -0
  85. package/templates/testing-config.json +81 -0
  86. package/templates/timetravel-config.json +62 -0
  87. package/templates/tool-registry-config.json +109 -0
  88. package/templates/voice-commands-config.json +658 -0
@@ -0,0 +1,940 @@
1
+ ---
2
+ name: flow-orchestration
3
+ description: Sistema de orquestacion basado en grafos inspirado en LangGraph
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # ELSABRO Flow-Based Orchestration
8
+
9
+ ## Vision General
10
+
11
+ El sistema de Flow Orchestration permite definir workflows complejos como grafos dirigidos, con soporte para:
12
+ - **Nodos**: Agentes, funciones, decisiones
13
+ - **Edges**: Transiciones condicionales o incondicionales
14
+ - **Subflows**: Workflows reutilizables anidados
15
+ - **Parallel branches**: Ejecución paralela de ramas independientes
16
+ - **Interrupts**: Pausas programáticas para intervención humana
17
+
18
+ ```
19
+ ┌──────────────────────────────────────────────────────────────────────────┐
20
+ │ FLOW ARCHITECTURE │
21
+ ├──────────────────────────────────────────────────────────────────────────┤
22
+ │ │
23
+ │ ┌───────────┐ │
24
+ │ │ START │ │
25
+ │ └─────┬─────┘ │
26
+ │ │ │
27
+ │ ▼ │
28
+ │ ┌───────────┐ condition=true ┌───────────┐ │
29
+ │ │ Analyst │ ──────────────────────► │ Planner │ │
30
+ │ └───────────┘ └─────┬─────┘ │
31
+ │ │ │ │
32
+ │ │ condition=false │ │
33
+ │ ▼ ▼ │
34
+ │ ┌───────────┐ ┌─────────────────────┐ │
35
+ │ │ Quick │ │ PARALLEL BRANCH │ │
36
+ │ │ Mode │ ├─────────────────────┤ │
37
+ │ └─────┬─────┘ │ ┌─────┐ ┌─────┐ │ │
38
+ │ │ │ │Exec │ │ QA │ │ │
39
+ │ │ │ └──┬──┘ └──┬──┘ │ │
40
+ │ │ │ │ │ │ │
41
+ │ │ │ └────┬────┘ │ │
42
+ │ │ │ │ │ │
43
+ │ │ └─────────┼──────────┘ │
44
+ │ │ │ │
45
+ │ │ ▼ │
46
+ │ │ ┌───────────────┐ │
47
+ │ │ │ Verifier │ │
48
+ │ │ └───────┬───────┘ │
49
+ │ │ │ │
50
+ │ └──────────────────────────────────┤ │
51
+ │ │ │
52
+ │ ▼ │
53
+ │ ┌───────────┐ │
54
+ │ │ END │ │
55
+ │ └───────────┘ │
56
+ │ │
57
+ └──────────────────────────────────────────────────────────────────────────┘
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Definición de Flows
63
+
64
+ ### Flow Definition Schema
65
+
66
+ ```json
67
+ {
68
+ "id": "flow_feature_dev",
69
+ "name": "Feature Development Flow",
70
+ "version": "1.0.0",
71
+ "description": "Workflow completo para desarrollar una feature",
72
+
73
+ "config": {
74
+ "timeout": 3600000,
75
+ "maxRetries": 3,
76
+ "checkpointEnabled": true,
77
+ "interruptEnabled": true
78
+ },
79
+
80
+ "inputs": {
81
+ "featureDescription": { "type": "string", "required": true },
82
+ "complexity": { "type": "enum", "values": ["low", "medium", "high"], "default": "medium" }
83
+ },
84
+
85
+ "outputs": {
86
+ "filesCreated": { "type": "array" },
87
+ "testsCreated": { "type": "array" },
88
+ "success": { "type": "boolean" }
89
+ },
90
+
91
+ "nodes": [
92
+ {
93
+ "id": "start",
94
+ "type": "entry",
95
+ "next": "analyze"
96
+ },
97
+ {
98
+ "id": "analyze",
99
+ "type": "agent",
100
+ "agent": "elsabro-analyst",
101
+ "config": {
102
+ "model": "opus",
103
+ "timeout": 300000
104
+ },
105
+ "inputs": {
106
+ "task": "{{inputs.featureDescription}}"
107
+ },
108
+ "outputs": ["analysis", "complexity", "scope"]
109
+ },
110
+ {
111
+ "id": "route_complexity",
112
+ "type": "router",
113
+ "condition": "{{nodes.analyze.outputs.complexity}}",
114
+ "routes": {
115
+ "low": "quick_implement",
116
+ "medium": "plan_implement",
117
+ "high": "detailed_plan"
118
+ },
119
+ "default": "plan_implement"
120
+ },
121
+ {
122
+ "id": "quick_implement",
123
+ "type": "agent",
124
+ "agent": "elsabro-quick-dev",
125
+ "next": "verify"
126
+ },
127
+ {
128
+ "id": "plan_implement",
129
+ "type": "subflow",
130
+ "flow": "planning_subflow",
131
+ "next": "parallel_dev"
132
+ },
133
+ {
134
+ "id": "detailed_plan",
135
+ "type": "sequence",
136
+ "steps": [
137
+ { "agent": "elsabro-analyst", "as": "deep_analysis" },
138
+ { "agent": "elsabro-planner", "as": "architecture" }
139
+ ],
140
+ "next": "interrupt_approval"
141
+ },
142
+ {
143
+ "id": "interrupt_approval",
144
+ "type": "interrupt",
145
+ "reason": "Plan requires approval for high complexity feature",
146
+ "display": {
147
+ "title": "Plan Approval Required",
148
+ "content": "{{nodes.detailed_plan.outputs.architecture}}",
149
+ "options": ["approve", "modify", "reject"]
150
+ },
151
+ "routes": {
152
+ "approve": "parallel_dev",
153
+ "modify": "detailed_plan",
154
+ "reject": "end_rejected"
155
+ }
156
+ },
157
+ {
158
+ "id": "parallel_dev",
159
+ "type": "parallel",
160
+ "branches": [
161
+ {
162
+ "id": "implementation",
163
+ "agent": "elsabro-executor",
164
+ "config": { "model": "opus" }
165
+ },
166
+ {
167
+ "id": "testing",
168
+ "agent": "elsabro-qa",
169
+ "config": { "model": "opus" }
170
+ }
171
+ ],
172
+ "joinType": "all",
173
+ "next": "verify"
174
+ },
175
+ {
176
+ "id": "verify",
177
+ "type": "agent",
178
+ "agent": "elsabro-verifier",
179
+ "config": { "model": "opus" }
180
+ },
181
+ {
182
+ "id": "verify_check",
183
+ "type": "condition",
184
+ "condition": "{{nodes.verify.outputs.passed}}",
185
+ "true": "end_success",
186
+ "false": "fix_issues"
187
+ },
188
+ {
189
+ "id": "fix_issues",
190
+ "type": "agent",
191
+ "agent": "elsabro-debugger",
192
+ "next": "verify",
193
+ "maxIterations": 3,
194
+ "onMaxIterations": "interrupt_manual"
195
+ },
196
+ {
197
+ "id": "interrupt_manual",
198
+ "type": "interrupt",
199
+ "reason": "Auto-fix failed after 3 attempts",
200
+ "display": {
201
+ "title": "Manual Intervention Required",
202
+ "errors": "{{nodes.verify.outputs.errors}}"
203
+ }
204
+ },
205
+ {
206
+ "id": "end_success",
207
+ "type": "exit",
208
+ "status": "success",
209
+ "outputs": {
210
+ "filesCreated": "{{collectOutputs('filesCreated')}}",
211
+ "testsCreated": "{{collectOutputs('testsCreated')}}",
212
+ "success": true
213
+ }
214
+ },
215
+ {
216
+ "id": "end_rejected",
217
+ "type": "exit",
218
+ "status": "rejected",
219
+ "outputs": {
220
+ "success": false,
221
+ "reason": "Plan rejected by user"
222
+ }
223
+ }
224
+ ],
225
+
226
+ "edges": [
227
+ { "from": "start", "to": "analyze" },
228
+ { "from": "analyze", "to": "route_complexity" },
229
+ { "from": "verify", "to": "verify_check" }
230
+ ]
231
+ }
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Tipos de Nodos
237
+
238
+ ### 1. Entry Node
239
+
240
+ Punto de entrada del flow.
241
+
242
+ ```json
243
+ {
244
+ "id": "start",
245
+ "type": "entry",
246
+ "next": "first_node"
247
+ }
248
+ ```
249
+
250
+ ### 2. Agent Node
251
+
252
+ Ejecuta un agente ELSABRO.
253
+
254
+ ```json
255
+ {
256
+ "id": "analyze",
257
+ "type": "agent",
258
+ "agent": "elsabro-analyst",
259
+ "config": {
260
+ "model": "opus",
261
+ "timeout": 300000,
262
+ "retries": 2
263
+ },
264
+ "inputs": {
265
+ "task": "{{inputs.description}}",
266
+ "context": "{{state.previousAnalysis}}"
267
+ },
268
+ "outputs": ["result", "decisions"],
269
+ "next": "next_node"
270
+ }
271
+ ```
272
+
273
+ ### 3. Router Node
274
+
275
+ Enruta basado en condiciones.
276
+
277
+ ```json
278
+ {
279
+ "id": "complexity_router",
280
+ "type": "router",
281
+ "condition": "{{nodes.analyze.outputs.complexity}}",
282
+ "routes": {
283
+ "low": "quick_path",
284
+ "medium": "normal_path",
285
+ "high": "detailed_path"
286
+ },
287
+ "default": "normal_path"
288
+ }
289
+ ```
290
+
291
+ ### 4. Condition Node
292
+
293
+ Bifurcación binaria simple.
294
+
295
+ ```json
296
+ {
297
+ "id": "check_tests",
298
+ "type": "condition",
299
+ "condition": "{{nodes.test.outputs.passed}} === true",
300
+ "true": "deploy",
301
+ "false": "fix_tests"
302
+ }
303
+ ```
304
+
305
+ ### 5. Parallel Node
306
+
307
+ Ejecuta múltiples ramas en paralelo.
308
+
309
+ ```json
310
+ {
311
+ "id": "parallel_work",
312
+ "type": "parallel",
313
+ "branches": [
314
+ { "id": "code", "agent": "elsabro-executor" },
315
+ { "id": "tests", "agent": "elsabro-qa" },
316
+ { "id": "docs", "agent": "elsabro-tech-writer" }
317
+ ],
318
+ "joinType": "all",
319
+ "timeout": 600000,
320
+ "next": "merge_results"
321
+ }
322
+ ```
323
+
324
+ **Join Types**:
325
+ - `all`: Espera que todas terminen
326
+ - `any`: Continúa cuando cualquiera termine
327
+ - `quorum`: Continúa cuando >50% termine
328
+ - `first_success`: Continúa con el primer éxito
329
+
330
+ ### 6. Sequence Node
331
+
332
+ Ejecuta pasos en secuencia.
333
+
334
+ ```json
335
+ {
336
+ "id": "analysis_sequence",
337
+ "type": "sequence",
338
+ "steps": [
339
+ { "agent": "elsabro-analyst", "as": "market" },
340
+ { "agent": "elsabro-analyst", "as": "tech" },
341
+ { "agent": "elsabro-planner", "as": "plan" }
342
+ ],
343
+ "next": "implementation"
344
+ }
345
+ ```
346
+
347
+ ### 7. Subflow Node
348
+
349
+ Ejecuta otro flow como subrutina.
350
+
351
+ ```json
352
+ {
353
+ "id": "auth_feature",
354
+ "type": "subflow",
355
+ "flow": "authentication_flow",
356
+ "inputs": {
357
+ "provider": "nextauth",
358
+ "features": ["email", "oauth"]
359
+ },
360
+ "next": "continue"
361
+ }
362
+ ```
363
+
364
+ ### 8. Interrupt Node
365
+
366
+ Pausa para intervención humana.
367
+
368
+ ```json
369
+ {
370
+ "id": "approval_gate",
371
+ "type": "interrupt",
372
+ "reason": "Approval required before deployment",
373
+ "display": {
374
+ "title": "Deployment Approval",
375
+ "content": "Review changes before proceeding",
376
+ "data": "{{nodes.review.outputs.summary}}",
377
+ "options": [
378
+ { "id": "approve", "label": "Approve & Deploy" },
379
+ { "id": "reject", "label": "Reject" },
380
+ { "id": "modify", "label": "Request Changes" }
381
+ ]
382
+ },
383
+ "routes": {
384
+ "approve": "deploy",
385
+ "reject": "end_rejected",
386
+ "modify": "make_changes"
387
+ },
388
+ "timeout": 86400000
389
+ }
390
+ ```
391
+
392
+ ### 9. Loop Node
393
+
394
+ Repite hasta condición.
395
+
396
+ ```json
397
+ {
398
+ "id": "fix_loop",
399
+ "type": "loop",
400
+ "body": [
401
+ { "agent": "elsabro-debugger" },
402
+ { "agent": "elsabro-verifier" }
403
+ ],
404
+ "condition": "{{nodes.verifier.outputs.passed}} === false",
405
+ "maxIterations": 5,
406
+ "onMaxIterations": "escalate"
407
+ }
408
+ ```
409
+
410
+ ### 10. Exit Node
411
+
412
+ Punto de salida del flow.
413
+
414
+ ```json
415
+ {
416
+ "id": "end_success",
417
+ "type": "exit",
418
+ "status": "success",
419
+ "outputs": {
420
+ "files": "{{collectOutputs('files')}}",
421
+ "duration": "{{duration()}}"
422
+ }
423
+ }
424
+ ```
425
+
426
+ ---
427
+
428
+ ## API del Flow Engine
429
+
430
+ ### FlowEngine
431
+
432
+ ```javascript
433
+ /**
434
+ * FlowEngine
435
+ * Motor de ejecución de flows basados en grafos
436
+ */
437
+ class FlowEngine {
438
+ constructor(options = {}) {
439
+ this.checkpointManager = options.checkpointManager || new CheckpointManager();
440
+ this.memoryManager = options.memoryManager || new MemoryManager();
441
+ this.agentRegistry = options.agentRegistry || new AgentRegistry();
442
+
443
+ this.state = {
444
+ currentNode: null,
445
+ variables: {},
446
+ nodeOutputs: {},
447
+ history: [],
448
+ interrupts: []
449
+ };
450
+ }
451
+
452
+ /**
453
+ * Carga y valida una definición de flow
454
+ */
455
+ async loadFlow(flowDefinition) {
456
+ // Validar schema
457
+ this.validateFlowSchema(flowDefinition);
458
+
459
+ // Construir grafo
460
+ this.flow = flowDefinition;
461
+ this.graph = this.buildGraph(flowDefinition);
462
+
463
+ // Validar grafo (ciclos, nodos huérfanos, etc.)
464
+ this.validateGraph();
465
+
466
+ return this;
467
+ }
468
+
469
+ /**
470
+ * Ejecuta el flow desde el inicio
471
+ */
472
+ async execute(inputs = {}) {
473
+ // Inicializar estado
474
+ this.state.variables = { ...inputs };
475
+ this.state.startedAt = new Date().toISOString();
476
+
477
+ // Crear checkpoint inicial
478
+ await this.checkpointManager.createCheckpoint({
479
+ flowId: this.flow.id,
480
+ execution: { status: 'ACTIVE', currentNode: 'start' },
481
+ state: this.state
482
+ }, 'FLOW_START');
483
+
484
+ // Encontrar nodo de entrada
485
+ const entryNode = this.findEntryNode();
486
+
487
+ // Ejecutar desde entrada
488
+ return this.executeNode(entryNode);
489
+ }
490
+
491
+ /**
492
+ * Resume un flow desde un checkpoint
493
+ */
494
+ async resume(checkpointId) {
495
+ const checkpoint = await this.checkpointManager.loadCheckpoint(checkpointId);
496
+
497
+ // Restaurar estado
498
+ this.state = checkpoint.state;
499
+
500
+ // Encontrar nodo donde quedó
501
+ const currentNode = this.graph.getNode(checkpoint.execution.currentNode);
502
+
503
+ // Continuar ejecución
504
+ return this.executeNode(currentNode);
505
+ }
506
+
507
+ /**
508
+ * Resume desde un interrupt
509
+ */
510
+ async resumeFromInterrupt(interruptId, response) {
511
+ const interrupt = this.state.interrupts.find(i => i.id === interruptId);
512
+
513
+ if (!interrupt) {
514
+ throw new Error(`Interrupt ${interruptId} not found`);
515
+ }
516
+
517
+ // Determinar siguiente nodo basado en respuesta
518
+ const nextNode = interrupt.routes[response];
519
+
520
+ if (!nextNode) {
521
+ throw new Error(`Invalid response: ${response}`);
522
+ }
523
+
524
+ // Marcar interrupt como resuelto
525
+ interrupt.resolvedAt = new Date().toISOString();
526
+ interrupt.response = response;
527
+
528
+ // Continuar ejecución
529
+ return this.executeNode(this.graph.getNode(nextNode));
530
+ }
531
+
532
+ /**
533
+ * Ejecuta un nodo específico
534
+ */
535
+ async executeNode(node) {
536
+ this.state.currentNode = node.id;
537
+ this.state.history.push({
538
+ nodeId: node.id,
539
+ startedAt: new Date().toISOString()
540
+ });
541
+
542
+ // Checkpoint antes de ejecutar
543
+ await this.checkpointManager.updateCheckpoint({
544
+ execution: { currentNode: node.id },
545
+ state: this.state
546
+ }, 'NODE_START');
547
+
548
+ try {
549
+ // Ejecutar según tipo de nodo
550
+ const result = await this.executeByType(node);
551
+
552
+ // Guardar output
553
+ this.state.nodeOutputs[node.id] = result;
554
+
555
+ // Actualizar historial
556
+ const historyEntry = this.state.history[this.state.history.length - 1];
557
+ historyEntry.completedAt = new Date().toISOString();
558
+ historyEntry.status = 'completed';
559
+ historyEntry.output = result;
560
+
561
+ // Checkpoint después de ejecutar
562
+ await this.checkpointManager.updateCheckpoint({
563
+ state: this.state
564
+ }, 'NODE_COMPLETE');
565
+
566
+ // Determinar siguiente nodo
567
+ const nextNode = await this.determineNextNode(node, result);
568
+
569
+ if (nextNode) {
570
+ return this.executeNode(nextNode);
571
+ }
572
+
573
+ // Si no hay siguiente, terminamos
574
+ return this.finalize(result);
575
+
576
+ } catch (error) {
577
+ return this.handleNodeError(node, error);
578
+ }
579
+ }
580
+
581
+ /**
582
+ * Ejecuta nodo según su tipo
583
+ */
584
+ async executeByType(node) {
585
+ switch (node.type) {
586
+ case 'entry':
587
+ return this.executeEntryNode(node);
588
+
589
+ case 'agent':
590
+ return this.executeAgentNode(node);
591
+
592
+ case 'router':
593
+ return this.executeRouterNode(node);
594
+
595
+ case 'condition':
596
+ return this.executeConditionNode(node);
597
+
598
+ case 'parallel':
599
+ return this.executeParallelNode(node);
600
+
601
+ case 'sequence':
602
+ return this.executeSequenceNode(node);
603
+
604
+ case 'subflow':
605
+ return this.executeSubflowNode(node);
606
+
607
+ case 'interrupt':
608
+ return this.executeInterruptNode(node);
609
+
610
+ case 'loop':
611
+ return this.executeLoopNode(node);
612
+
613
+ case 'exit':
614
+ return this.executeExitNode(node);
615
+
616
+ default:
617
+ throw new Error(`Unknown node type: ${node.type}`);
618
+ }
619
+ }
620
+
621
+ /**
622
+ * Ejecuta nodo de agente
623
+ */
624
+ async executeAgentNode(node) {
625
+ // Resolver inputs con template
626
+ const inputs = this.resolveTemplate(node.inputs);
627
+
628
+ // Obtener contexto de memoria
629
+ const context = await this.memoryManager.getFullContext({
630
+ description: inputs.task || inputs.description,
631
+ context: node.context || []
632
+ });
633
+
634
+ // Ejecutar agente
635
+ const result = await this.agentRegistry.execute(node.agent, {
636
+ ...inputs,
637
+ memoryContext: context,
638
+ config: node.config
639
+ });
640
+
641
+ // Procesar resultado con memoria
642
+ await this.memoryManager.processAgentResult(node.agent, result);
643
+
644
+ return result;
645
+ }
646
+
647
+ /**
648
+ * Ejecuta nodo paralelo
649
+ */
650
+ async executeParallelNode(node) {
651
+ const branches = node.branches;
652
+ const joinType = node.joinType || 'all';
653
+
654
+ // Lanzar todas las ramas en paralelo
655
+ const promises = branches.map(branch =>
656
+ this.executeBranch(branch).catch(error => ({
657
+ branchId: branch.id,
658
+ error: error.message,
659
+ status: 'failed'
660
+ }))
661
+ );
662
+
663
+ // Esperar según joinType
664
+ let results;
665
+
666
+ switch (joinType) {
667
+ case 'all':
668
+ results = await Promise.all(promises);
669
+ break;
670
+
671
+ case 'any':
672
+ results = [await Promise.race(promises)];
673
+ break;
674
+
675
+ case 'quorum':
676
+ results = await this.waitForQuorum(promises);
677
+ break;
678
+
679
+ case 'first_success':
680
+ results = [await this.waitForFirstSuccess(promises)];
681
+ break;
682
+ }
683
+
684
+ // Combinar resultados
685
+ return {
686
+ branches: results.reduce((acc, r, i) => {
687
+ acc[branches[i].id] = r;
688
+ return acc;
689
+ }, {}),
690
+ completedCount: results.filter(r => !r.error).length,
691
+ totalCount: branches.length
692
+ };
693
+ }
694
+
695
+ /**
696
+ * Ejecuta nodo de interrupt
697
+ */
698
+ async executeInterruptNode(node) {
699
+ const interrupt = {
700
+ id: `int_${Date.now()}`,
701
+ nodeId: node.id,
702
+ reason: node.reason,
703
+ display: this.resolveTemplate(node.display),
704
+ routes: node.routes,
705
+ createdAt: new Date().toISOString()
706
+ };
707
+
708
+ // Guardar interrupt
709
+ this.state.interrupts.push(interrupt);
710
+
711
+ // Guardar checkpoint especial
712
+ await this.checkpointManager.saveInterrupt(node.reason, {
713
+ nodeId: node.id,
714
+ interruptId: interrupt.id,
715
+ display: interrupt.display
716
+ });
717
+
718
+ // Retornar para que el caller maneje
719
+ return {
720
+ type: 'interrupt',
721
+ interrupt: interrupt
722
+ };
723
+ }
724
+
725
+ /**
726
+ * Ejecuta subflow
727
+ */
728
+ async executeSubflowNode(node) {
729
+ // Cargar definición del subflow
730
+ const subflowDef = await this.loadSubflowDefinition(node.flow);
731
+
732
+ // Crear engine para subflow
733
+ const subEngine = new FlowEngine({
734
+ checkpointManager: this.checkpointManager,
735
+ memoryManager: this.memoryManager,
736
+ agentRegistry: this.agentRegistry
737
+ });
738
+
739
+ await subEngine.loadFlow(subflowDef);
740
+
741
+ // Ejecutar subflow con inputs
742
+ const inputs = this.resolveTemplate(node.inputs);
743
+ return subEngine.execute(inputs);
744
+ }
745
+
746
+ // ==================== Helpers ====================
747
+
748
+ resolveTemplate(template) {
749
+ if (typeof template === 'string') {
750
+ return template.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
751
+ return this.resolvePath(path.trim());
752
+ });
753
+ }
754
+
755
+ if (Array.isArray(template)) {
756
+ return template.map(item => this.resolveTemplate(item));
757
+ }
758
+
759
+ if (typeof template === 'object' && template !== null) {
760
+ const resolved = {};
761
+ for (const [key, value] of Object.entries(template)) {
762
+ resolved[key] = this.resolveTemplate(value);
763
+ }
764
+ return resolved;
765
+ }
766
+
767
+ return template;
768
+ }
769
+
770
+ resolvePath(path) {
771
+ const parts = path.split('.');
772
+ let current;
773
+
774
+ if (parts[0] === 'inputs') {
775
+ current = this.state.variables;
776
+ parts.shift();
777
+ } else if (parts[0] === 'nodes') {
778
+ current = this.state.nodeOutputs;
779
+ parts.shift();
780
+ } else if (parts[0] === 'state') {
781
+ current = this.state;
782
+ parts.shift();
783
+ }
784
+
785
+ for (const part of parts) {
786
+ if (current === undefined) return undefined;
787
+ current = current[part];
788
+ }
789
+
790
+ return current;
791
+ }
792
+
793
+ buildGraph(flowDefinition) {
794
+ const graph = new Map();
795
+
796
+ for (const node of flowDefinition.nodes) {
797
+ graph.set(node.id, {
798
+ ...node,
799
+ inEdges: [],
800
+ outEdges: []
801
+ });
802
+ }
803
+
804
+ // Procesar edges explícitos
805
+ for (const edge of flowDefinition.edges || []) {
806
+ const fromNode = graph.get(edge.from);
807
+ const toNode = graph.get(edge.to);
808
+
809
+ if (fromNode && toNode) {
810
+ fromNode.outEdges.push(edge);
811
+ toNode.inEdges.push(edge);
812
+ }
813
+ }
814
+
815
+ // Procesar edges implícitos (next)
816
+ for (const [id, node] of graph) {
817
+ if (node.next && !node.outEdges.find(e => e.to === node.next)) {
818
+ node.outEdges.push({ from: id, to: node.next });
819
+ const nextNode = graph.get(node.next);
820
+ if (nextNode) {
821
+ nextNode.inEdges.push({ from: id, to: node.next });
822
+ }
823
+ }
824
+ }
825
+
826
+ return {
827
+ nodes: graph,
828
+ getNode: (id) => graph.get(id),
829
+ getEdges: (from) => graph.get(from)?.outEdges || []
830
+ };
831
+ }
832
+
833
+ findEntryNode() {
834
+ for (const [id, node] of this.graph.nodes) {
835
+ if (node.type === 'entry') {
836
+ return node;
837
+ }
838
+ }
839
+ throw new Error('No entry node found in flow');
840
+ }
841
+
842
+ async determineNextNode(currentNode, result) {
843
+ // Si el resultado es un interrupt, no hay siguiente
844
+ if (result?.type === 'interrupt') {
845
+ return null;
846
+ }
847
+
848
+ // Router determina siguiente basado en condición
849
+ if (currentNode.type === 'router') {
850
+ const route = currentNode.routes[result.route];
851
+ return route ? this.graph.getNode(route) : this.graph.getNode(currentNode.default);
852
+ }
853
+
854
+ // Condition determina siguiente basado en true/false
855
+ if (currentNode.type === 'condition') {
856
+ const nextId = result.value ? currentNode.true : currentNode.false;
857
+ return this.graph.getNode(nextId);
858
+ }
859
+
860
+ // Exit no tiene siguiente
861
+ if (currentNode.type === 'exit') {
862
+ return null;
863
+ }
864
+
865
+ // Default: usar next o primer outEdge
866
+ if (currentNode.next) {
867
+ return this.graph.getNode(currentNode.next);
868
+ }
869
+
870
+ const outEdges = this.graph.getEdges(currentNode.id);
871
+ if (outEdges.length > 0) {
872
+ return this.graph.getNode(outEdges[0].to);
873
+ }
874
+
875
+ return null;
876
+ }
877
+ }
878
+ ```
879
+
880
+ ---
881
+
882
+ ## Flows Predefinidos
883
+
884
+ ### development_flow.json
885
+
886
+ Flow estándar para desarrollo de features.
887
+
888
+ ### quick_flow.json
889
+
890
+ Flow simplificado para tareas rápidas.
891
+
892
+ ### review_flow.json
893
+
894
+ Flow para code review multi-agente.
895
+
896
+ ### debug_flow.json
897
+
898
+ Flow para debugging sistemático.
899
+
900
+ ---
901
+
902
+ ## Comandos de Usuario
903
+
904
+ ### /elsabro:flow
905
+
906
+ ```bash
907
+ /elsabro:flow list # Listar flows disponibles
908
+ /elsabro:flow run <name> [inputs] # Ejecutar flow
909
+ /elsabro:flow status # Ver estado del flow actual
910
+ /elsabro:flow resume [checkpoint] # Resumir flow pausado
911
+ /elsabro:flow cancel # Cancelar flow actual
912
+ /elsabro:flow create # Crear nuevo flow (interactivo)
913
+ ```
914
+
915
+ ---
916
+
917
+ ## Configuración
918
+
919
+ ### .planning/flows/config.json
920
+
921
+ ```json
922
+ {
923
+ "flows": {
924
+ "defaultFlow": "development_flow",
925
+ "timeout": 3600000,
926
+ "checkpointing": {
927
+ "enabled": true,
928
+ "interval": "node"
929
+ },
930
+ "parallelism": {
931
+ "maxConcurrent": 4,
932
+ "defaultJoinType": "all"
933
+ },
934
+ "interrupts": {
935
+ "timeout": 86400000,
936
+ "defaultAction": "pause"
937
+ }
938
+ }
939
+ }
940
+ ```