@ranker/raxflow 2.0.5 → 2.1.1

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 (160) hide show
  1. package/dist/hub/components/ChatInput.d.ts +14 -0
  2. package/dist/hub/components/ChatInput.d.ts.map +1 -0
  3. package/dist/hub/components/ChatInput.js +14 -0
  4. package/dist/hub/components/ChatInput.js.map +1 -0
  5. package/dist/hub/components/ChatMessage.d.ts +14 -0
  6. package/dist/hub/components/ChatMessage.d.ts.map +1 -0
  7. package/dist/hub/components/ChatMessage.js +23 -0
  8. package/dist/hub/components/ChatMessage.js.map +1 -0
  9. package/dist/hub/components/HubHeader.d.ts +12 -0
  10. package/dist/hub/components/HubHeader.d.ts.map +1 -0
  11. package/dist/hub/components/HubHeader.js +12 -0
  12. package/dist/hub/components/HubHeader.js.map +1 -0
  13. package/dist/hub/components/index.d.ts +7 -0
  14. package/dist/hub/components/index.d.ts.map +1 -0
  15. package/dist/hub/components/index.js +7 -0
  16. package/dist/hub/components/index.js.map +1 -0
  17. package/dist/tui/App.d.ts.map +1 -1
  18. package/dist/tui/App.js +10 -10
  19. package/dist/tui/App.js.map +1 -1
  20. package/dist/tui/components/ChatPanel.d.ts +1 -1
  21. package/dist/tui/components/ChatPanel.d.ts.map +1 -1
  22. package/dist/tui/components/ChatPanel.js +37 -26
  23. package/dist/tui/components/ChatPanel.js.map +1 -1
  24. package/dist/tui/components/DAGPanel.d.ts +3 -4
  25. package/dist/tui/components/DAGPanel.d.ts.map +1 -1
  26. package/dist/tui/components/DAGPanel.js +44 -36
  27. package/dist/tui/components/DAGPanel.js.map +1 -1
  28. package/dist/tui/components/Header.d.ts.map +1 -1
  29. package/dist/tui/components/Header.js +54 -36
  30. package/dist/tui/components/Header.js.map +1 -1
  31. package/dist/tui/components/HelpOverlay.d.ts.map +1 -1
  32. package/dist/tui/components/HelpOverlay.js +7 -5
  33. package/dist/tui/components/HelpOverlay.js.map +1 -1
  34. package/dist/tui/components/InputBar.d.ts.map +1 -1
  35. package/dist/tui/components/InputBar.js +25 -13
  36. package/dist/tui/components/InputBar.js.map +1 -1
  37. package/dist/tui/components/LogsPanel.d.ts.map +1 -1
  38. package/dist/tui/components/LogsPanel.js +23 -17
  39. package/dist/tui/components/LogsPanel.js.map +1 -1
  40. package/dist/tui/components/MemoryPanel.d.ts +1 -1
  41. package/dist/tui/components/MemoryPanel.d.ts.map +1 -1
  42. package/dist/tui/components/MemoryPanel.js +24 -31
  43. package/dist/tui/components/MemoryPanel.js.map +1 -1
  44. package/dist/tui/components/MetricsPanel.d.ts +1 -1
  45. package/dist/tui/components/MetricsPanel.d.ts.map +1 -1
  46. package/dist/tui/components/MetricsPanel.js +10 -13
  47. package/dist/tui/components/MetricsPanel.js.map +1 -1
  48. package/dist/tui/components/SelectMenu.d.ts +20 -0
  49. package/dist/tui/components/SelectMenu.d.ts.map +1 -0
  50. package/dist/tui/components/SelectMenu.js +19 -0
  51. package/dist/tui/components/SelectMenu.js.map +1 -0
  52. package/dist/tui/components/StatusPanel.d.ts +4 -3
  53. package/dist/tui/components/StatusPanel.d.ts.map +1 -1
  54. package/dist/tui/components/StatusPanel.js +26 -18
  55. package/dist/tui/components/StatusPanel.js.map +1 -1
  56. package/dist/tui/components/animations/ProgressBar.d.ts +8 -1
  57. package/dist/tui/components/animations/ProgressBar.d.ts.map +1 -1
  58. package/dist/tui/components/animations/ProgressBar.js +52 -26
  59. package/dist/tui/components/animations/ProgressBar.js.map +1 -1
  60. package/dist/tui/components/animations/Pulse.d.ts +11 -3
  61. package/dist/tui/components/animations/Pulse.d.ts.map +1 -1
  62. package/dist/tui/components/animations/Pulse.js +23 -14
  63. package/dist/tui/components/animations/Pulse.js.map +1 -1
  64. package/dist/tui/components/animations/Spinner.d.ts +2 -1
  65. package/dist/tui/components/animations/Spinner.d.ts.map +1 -1
  66. package/dist/tui/components/animations/Spinner.js +31 -16
  67. package/dist/tui/components/animations/Spinner.js.map +1 -1
  68. package/dist/tui/components/animations/StatusAnimator.d.ts +3 -0
  69. package/dist/tui/components/animations/StatusAnimator.d.ts.map +1 -1
  70. package/dist/tui/components/animations/StatusAnimator.js +43 -28
  71. package/dist/tui/components/animations/StatusAnimator.js.map +1 -1
  72. package/dist/tui/components/animations/TypingEffect.d.ts +1 -0
  73. package/dist/tui/components/animations/TypingEffect.d.ts.map +1 -1
  74. package/dist/tui/components/animations/TypingEffect.js +13 -7
  75. package/dist/tui/components/animations/TypingEffect.js.map +1 -1
  76. package/dist/tui/components/animations/index.d.ts +3 -2
  77. package/dist/tui/components/animations/index.d.ts.map +1 -1
  78. package/dist/tui/components/animations/index.js +2 -2
  79. package/dist/tui/components/animations/index.js.map +1 -1
  80. package/dist/tui/components/composed/Button.d.ts +18 -0
  81. package/dist/tui/components/composed/Button.d.ts.map +1 -0
  82. package/dist/tui/components/composed/Button.js +22 -0
  83. package/dist/tui/components/composed/Button.js.map +1 -0
  84. package/dist/tui/components/composed/Divider.d.ts +13 -0
  85. package/dist/tui/components/composed/Divider.d.ts.map +1 -0
  86. package/dist/tui/components/composed/Divider.js +18 -0
  87. package/dist/tui/components/composed/Divider.js.map +1 -0
  88. package/dist/tui/components/composed/StatusBadge.d.ts +14 -0
  89. package/dist/tui/components/composed/StatusBadge.d.ts.map +1 -0
  90. package/dist/tui/components/composed/StatusBadge.js +28 -0
  91. package/dist/tui/components/composed/StatusBadge.js.map +1 -0
  92. package/dist/tui/components/composed/index.d.ts +7 -0
  93. package/dist/tui/components/composed/index.d.ts.map +1 -0
  94. package/dist/tui/components/composed/index.js +7 -0
  95. package/dist/tui/components/composed/index.js.map +1 -0
  96. package/dist/tui/components/layouts/Container.d.ts +14 -0
  97. package/dist/tui/components/layouts/Container.d.ts.map +1 -0
  98. package/dist/tui/components/layouts/Container.js +10 -0
  99. package/dist/tui/components/layouts/Container.js.map +1 -0
  100. package/dist/tui/components/layouts/Grid.d.ts +14 -0
  101. package/dist/tui/components/layouts/Grid.d.ts.map +1 -0
  102. package/dist/tui/components/layouts/Grid.js +13 -0
  103. package/dist/tui/components/layouts/Grid.js.map +1 -0
  104. package/dist/tui/components/layouts/Stack.d.ts +32 -0
  105. package/dist/tui/components/layouts/Stack.d.ts.map +1 -0
  106. package/dist/tui/components/layouts/Stack.js +17 -0
  107. package/dist/tui/components/layouts/Stack.js.map +1 -0
  108. package/dist/tui/components/layouts/index.d.ts +7 -0
  109. package/dist/tui/components/layouts/index.d.ts.map +1 -0
  110. package/dist/tui/components/layouts/index.js +7 -0
  111. package/dist/tui/components/layouts/index.js.map +1 -0
  112. package/dist/tui/components/primitives/Box.d.ts +28 -0
  113. package/dist/tui/components/primitives/Box.d.ts.map +1 -0
  114. package/dist/tui/components/primitives/Box.js +38 -0
  115. package/dist/tui/components/primitives/Box.js.map +1 -0
  116. package/dist/tui/components/primitives/Panel.d.ts +17 -0
  117. package/dist/tui/components/primitives/Panel.d.ts.map +1 -0
  118. package/dist/tui/components/primitives/Panel.js +12 -0
  119. package/dist/tui/components/primitives/Panel.js.map +1 -0
  120. package/dist/tui/components/primitives/Text.d.ts +18 -0
  121. package/dist/tui/components/primitives/Text.d.ts.map +1 -0
  122. package/dist/tui/components/primitives/Text.js +21 -0
  123. package/dist/tui/components/primitives/Text.js.map +1 -0
  124. package/dist/tui/components/primitives/index.d.ts +7 -0
  125. package/dist/tui/components/primitives/index.d.ts.map +1 -0
  126. package/dist/tui/components/primitives/index.js +7 -0
  127. package/dist/tui/components/primitives/index.js.map +1 -0
  128. package/dist/tui/hooks/useAppState.d.ts +30 -2
  129. package/dist/tui/hooks/useAppState.d.ts.map +1 -1
  130. package/dist/tui/hooks/useAppState.js +333 -186
  131. package/dist/tui/hooks/useAppState.js.map +1 -1
  132. package/dist/tui/hooks/useTerminalSize.d.ts +3 -0
  133. package/dist/tui/hooks/useTerminalSize.d.ts.map +1 -1
  134. package/dist/tui/hooks/useTerminalSize.js +36 -10
  135. package/dist/tui/hooks/useTerminalSize.js.map +1 -1
  136. package/dist/tui/services/orchestrator.d.ts +6 -2
  137. package/dist/tui/services/orchestrator.d.ts.map +1 -1
  138. package/dist/tui/services/orchestrator.js +22 -5
  139. package/dist/tui/services/orchestrator.js.map +1 -1
  140. package/dist/tui/styles/colors.d.ts +4 -0
  141. package/dist/tui/styles/colors.d.ts.map +1 -1
  142. package/dist/tui/styles/colors.js +54 -14
  143. package/dist/tui/styles/colors.js.map +1 -1
  144. package/dist/tui/styles/design-system.d.ts +107 -0
  145. package/dist/tui/styles/design-system.d.ts.map +1 -0
  146. package/dist/tui/styles/design-system.js +140 -0
  147. package/dist/tui/styles/design-system.js.map +1 -0
  148. package/dist/tui/styles/index.d.ts +4 -2
  149. package/dist/tui/styles/index.d.ts.map +1 -1
  150. package/dist/tui/styles/index.js +3 -2
  151. package/dist/tui/styles/index.js.map +1 -1
  152. package/dist/tui/styles/themes.d.ts +10 -0
  153. package/dist/tui/styles/themes.d.ts.map +1 -1
  154. package/dist/tui/styles/themes.js +137 -47
  155. package/dist/tui/styles/themes.js.map +1 -1
  156. package/dist/tui/styles/tokens.d.ts +262 -0
  157. package/dist/tui/styles/tokens.d.ts.map +1 -0
  158. package/dist/tui/styles/tokens.js +230 -0
  159. package/dist/tui/styles/tokens.js.map +1 -0
  160. package/package.json +1 -1
@@ -3,99 +3,111 @@ import { createOrchestrator } from "../services/orchestrator.js";
3
3
  import { setTheme } from "../styles/colors.js";
4
4
  const COMMAND_SUGGESTIONS = [
5
5
  "stop", "retry", "clear", "session", "evolve",
6
- "theme", "toggle", "history", "config", "help", "exit",
6
+ "theme", "toggle", "history", "config", "providers", "help", "exit",
7
7
  ];
8
- const DEFAULT_AGENTS = [
9
- { name: "IntentClassifier", role: "Classify intent", status: "idle", provider: "H" },
10
- { name: "TaskPlanner", role: "Decompose tasks", status: "idle", provider: "H" },
11
- { name: "SpecAgent", role: "Write specs", status: "idle", provider: "H" },
12
- { name: "CodeGenerator", role: "Generate code", status: "idle", provider: "H" },
13
- { name: "TestAgent", role: "Run tests", status: "idle", provider: "H" },
14
- { name: "FixAgent", role: "Fix issues", status: "idle", provider: "H" },
15
- { name: "MutationAgent", role: "Evolve workflow", status: "idle", provider: "H" },
16
- { name: "ValidatorAgent", role: "Validate outputs", status: "idle", provider: "H" },
8
+ const AVAILABLE_PROVIDERS = [
9
+ { name: "Host-Native", env: "HOST_BRIDGE", model: "claude-sonnet-4" },
10
+ { name: "OpenAI", env: "OPENAI_API_KEY", model: "gpt-4o" },
11
+ { name: "Anthropic", env: "ANTHROPIC_API_KEY", model: "claude-3-opus" },
12
+ { name: "Gemini", env: "GOOGLE_API_KEY", model: "gemini-pro" },
13
+ { name: "Groq", env: "GROQ_API_KEY", model: "llama-3" },
17
14
  ];
18
- const DEFAULT_PROVIDERS = [
19
- { name: "Host-Native", status: "active", latency: 12, model: "claude-sonnet-4" },
20
- { name: "Claude Code", status: "active", latency: 8, model: "claude-sonnet-4" },
21
- { name: "OpenCode", status: "idle", latency: 0, model: "gpt-4o" },
22
- { name: "Anthropic", status: "idle", latency: 0, model: "claude-3-opus" },
23
- ];
24
- const DEFAULT_WORKFLOW_STATE = {
25
- levels: [
26
- {
27
- name: "L1: SPEC",
28
- progress: 0,
29
- nodes: [
30
- { id: "intent", name: "IntentClassifier", status: "pending", agent: "H" },
31
- { id: "spec", name: "SpecAgent", status: "pending", agent: "H" },
32
- { id: "arch", name: "ArchitectureAgent", status: "pending", agent: "H" },
33
- ],
34
- },
35
- {
36
- name: "L2: CODE",
37
- progress: 0,
38
- nodes: [
39
- { id: "task", name: "TaskPlanner", status: "pending", agent: "H" },
40
- { id: "codegen", name: "CodeGenerator", status: "pending", agent: "H" },
41
- ],
42
- },
43
- {
44
- name: "L3: TEST",
45
- progress: 0,
46
- nodes: [
47
- { id: "test", name: "TestAgent", status: "pending", agent: "H" },
48
- { id: "fix", name: "FixAgent", status: "pending", agent: "H" },
49
- ],
50
- },
51
- ],
52
- currentLevel: 0,
53
- totalProgress: 0,
54
- };
55
15
  function generateId() {
56
16
  return Math.random().toString(36).slice(2, 9);
57
17
  }
18
+ function detectProviders() {
19
+ return AVAILABLE_PROVIDERS.map((p) => {
20
+ const hasKey = process.env[p.env] || (p.name === "Host-Native");
21
+ return {
22
+ name: p.name,
23
+ status: hasKey ? "active" : "idle",
24
+ latency: hasKey ? Math.floor(Math.random() * 50) + 10 : 0,
25
+ model: p.model,
26
+ requests: 0,
27
+ };
28
+ });
29
+ }
30
+ function detectProjectName() {
31
+ try {
32
+ const cwd = process.cwd();
33
+ return cwd.split("/").pop() || "project";
34
+ }
35
+ catch {
36
+ return "project";
37
+ }
38
+ }
39
+ function detectModel() {
40
+ if (process.env.ANTHROPIC_API_KEY)
41
+ return "claude-3-opus";
42
+ if (process.env.OPENAI_API_KEY)
43
+ return "gpt-4o";
44
+ if (process.env.GOOGLE_API_KEY)
45
+ return "gemini-pro";
46
+ if (process.env.GROQ_API_KEY)
47
+ return "llama-3";
48
+ return "host-native";
49
+ }
50
+ const EMPTY_WORKFLOW_STATE = {
51
+ levels: [],
52
+ currentLevel: -1,
53
+ totalProgress: 0,
54
+ nodeCount: 0,
55
+ };
56
+ const EMPTY_MEMORY_STATE = {
57
+ nodes: [],
58
+ edges: [],
59
+ nodeCount: 0,
60
+ cacheHitRate: 0,
61
+ };
58
62
  export function useAppState() {
59
63
  const orchestratorRef = useRef(createOrchestrator());
60
- const [state, setState] = useState({
61
- projectName: process.cwd().split("/").pop() || "project",
62
- agentCount: 12,
63
- provider: "HOST-NATIVE",
64
- model: "claude-sonnet-4",
65
- status: "ready",
66
- messages: [
67
- {
68
- id: generateId(),
69
- type: "system",
70
- content: "RAXFLOW HUB prêt. Tapez votre prompt ou 'help' pour les commandes.",
71
- timestamp: new Date(),
72
- model: "claude-sonnet-4",
64
+ const nodeTimingsRef = useRef(new Map());
65
+ const [state, setState] = useState(() => {
66
+ const providers = detectProviders();
67
+ const activeProvider = providers.find((p) => p.status === "active")?.name || "Host-Native";
68
+ return {
69
+ projectName: detectProjectName(),
70
+ agentCount: 0,
71
+ provider: activeProvider,
72
+ model: detectModel(),
73
+ status: "ready",
74
+ messages: [
75
+ {
76
+ id: generateId(),
77
+ type: "system",
78
+ content: "RAXFLOW HUB ready. Enter a prompt or type 'help' for commands.",
79
+ timestamp: new Date(),
80
+ model: detectModel(),
81
+ },
82
+ ],
83
+ agents: [],
84
+ providers,
85
+ fitness: 0.85,
86
+ currentWorkflow: null,
87
+ suggestions: COMMAND_SUGGESTIONS,
88
+ isProcessing: false,
89
+ workflowState: EMPTY_WORKFLOW_STATE,
90
+ logs: [],
91
+ metrics: {
92
+ sessions: 0,
93
+ avgDuration: 0,
94
+ successRate: 100,
95
+ totalCost: 0,
96
+ totalRetries: 0,
97
+ totalEscalations: 0,
98
+ totalTokens: 0,
99
+ confidenceAvg: 0,
100
+ },
101
+ memory: EMPTY_MEMORY_STATE,
102
+ theme: "default",
103
+ sessions: [],
104
+ viewMode: "full",
105
+ interactiveCommand: {
106
+ type: null,
107
+ options: [],
108
+ selectedIndex: 0,
73
109
  },
74
- ],
75
- agents: DEFAULT_AGENTS,
76
- providers: DEFAULT_PROVIDERS,
77
- fitness: 0.87,
78
- currentWorkflow: null,
79
- suggestions: COMMAND_SUGGESTIONS,
80
- isProcessing: false,
81
- workflowState: DEFAULT_WORKFLOW_STATE,
82
- logs: [],
83
- metrics: {
84
- sessions: 0,
85
- avgDuration: 0,
86
- successRate: 100,
87
- totalCost: 0,
88
- totalRetries: 0,
89
- totalEscalations: 0,
90
- },
91
- theme: "default",
92
- sessions: [],
93
- viewMode: "full",
94
- interactiveCommand: {
95
- type: null,
96
- options: [],
97
- selectedIndex: 0,
98
- },
110
+ };
99
111
  });
100
112
  useEffect(() => {
101
113
  const unsubscribe = orchestratorRef.current.onEvent((event) => {
@@ -103,49 +115,118 @@ export function useAppState() {
103
115
  });
104
116
  return unsubscribe;
105
117
  }, []);
118
+ const addLog = useCallback((log) => {
119
+ setState((prev) => ({
120
+ ...prev,
121
+ logs: [...prev.logs.slice(-200), log],
122
+ }));
123
+ }, []);
124
+ const addMessage = useCallback((type, content, agent, model) => {
125
+ setState((prev) => ({
126
+ ...prev,
127
+ messages: [
128
+ ...prev.messages,
129
+ { id: generateId(), type, content, timestamp: new Date(), agent, model: model || prev.model },
130
+ ],
131
+ }));
132
+ }, []);
106
133
  const handleOrchestratorEvent = useCallback((event) => {
107
134
  const timestamp = new Date();
108
- const timeStr = timestamp.toLocaleTimeString("fr-FR", {
135
+ const timeStr = timestamp.toLocaleTimeString("en-US", {
109
136
  hour: "2-digit",
110
137
  minute: "2-digit",
111
138
  second: "2-digit",
139
+ hour12: false,
112
140
  });
113
141
  switch (event.type) {
114
142
  case "run_start":
115
- addLog(`[${timeStr}] [START] Task ${event.taskId}`);
116
- setState((prev) => ({ ...prev, status: "running", isProcessing: true }));
143
+ addLog(`[${timeStr}] START Task ${event.taskId}`);
144
+ nodeTimingsRef.current.clear();
145
+ setState((prev) => ({
146
+ ...prev,
147
+ status: "running",
148
+ isProcessing: true,
149
+ currentWorkflow: event.taskId,
150
+ workflowState: event.workflow ? buildWorkflowFromGraph(event.workflow) : prev.workflowState,
151
+ }));
117
152
  break;
118
153
  case "graph_ready":
119
- addLog(`[${timeStr}] [GRAPH] Workflow ready with ${event.workflow.nodes.length} nodes`);
154
+ addLog(`[${timeStr}] GRAPH Ready with ${event.workflow.nodes.length} nodes`);
155
+ setState((prev) => ({
156
+ ...prev,
157
+ workflowState: buildWorkflowFromGraph(event.workflow),
158
+ agentCount: event.workflow.nodes.length,
159
+ }));
120
160
  break;
121
161
  case "node_start":
122
- addLog(`[${timeStr}] [RUN] ${event.agent} starting...`);
123
- updateAgentStatus(event.agent, "running");
124
- updateDAGNode(event.nodeId, "running");
162
+ addLog(`[${timeStr}] RUN ${event.agent} (node: ${event.nodeId})`);
163
+ nodeTimingsRef.current.set(event.nodeId, Date.now());
164
+ setState((prev) => {
165
+ const updatedAgents = updateAgentInList(prev.agents, event.agent, "running");
166
+ const updatedWorkflow = updateNodeStatus(prev.workflowState, event.nodeId, "running");
167
+ return {
168
+ ...prev,
169
+ agents: updatedAgents,
170
+ workflowState: updatedWorkflow,
171
+ };
172
+ });
125
173
  break;
126
174
  case "node_end":
175
+ const nodeDuration = nodeTimingsRef.current.has(event.nodeId)
176
+ ? Date.now() - nodeTimingsRef.current.get(event.nodeId)
177
+ : 0;
127
178
  if (event.success) {
128
- addLog(`[${timeStr}] [OK] ${event.agent} done (confidence: ${event.confidence.toFixed(2)})`);
129
- updateAgentStatus(event.agent, "done");
130
- updateDAGNode(event.nodeId, "done");
179
+ addLog(`[${timeStr}] OK ${event.agent} (${nodeDuration}ms, confidence: ${event.confidence.toFixed(2)})`);
131
180
  }
132
181
  else {
133
- addLog(`[${timeStr}] [FAIL] ${event.agent} failed`);
134
- updateAgentStatus(event.agent, "idle");
135
- updateDAGNode(event.nodeId, "error");
182
+ addLog(`[${timeStr}] FAIL ${event.agent} (retry: ${event.retry})`);
136
183
  }
184
+ setState((prev) => {
185
+ const status = event.success ? "done" : "error";
186
+ const updatedAgents = updateAgentInList(prev.agents, event.agent, status, event.confidence);
187
+ const updatedWorkflow = updateNodeStatus(prev.workflowState, event.nodeId, status, event.confidence);
188
+ const updatedProviders = incrementProviderRequests(prev.providers, prev.provider);
189
+ const newTokens = prev.metrics.totalTokens + (event.usage?.totalTokens || 0);
190
+ const newCost = prev.metrics.totalCost + (event.costUsd || 0);
191
+ return {
192
+ ...prev,
193
+ agents: updatedAgents,
194
+ workflowState: updatedWorkflow,
195
+ providers: updatedProviders,
196
+ metrics: {
197
+ ...prev.metrics,
198
+ totalTokens: newTokens,
199
+ totalCost: newCost,
200
+ },
201
+ };
202
+ });
137
203
  break;
138
204
  case "node_error":
139
- addLog(`[${timeStr}] [ERR] ${event.agent}: ${event.message}`);
140
- updateDAGNode(event.nodeId, "error");
205
+ addLog(`[${timeStr}] ERR ${event.agent}: ${event.message}`);
206
+ setState((prev) => {
207
+ const updatedAgents = updateAgentInList(prev.agents, event.agent, "idle");
208
+ const updatedWorkflow = updateNodeStatus(prev.workflowState, event.nodeId, "error");
209
+ return {
210
+ ...prev,
211
+ agents: updatedAgents,
212
+ workflowState: updatedWorkflow,
213
+ metrics: {
214
+ ...prev.metrics,
215
+ totalRetries: prev.metrics.totalRetries + 1,
216
+ },
217
+ };
218
+ });
141
219
  break;
142
220
  case "run_end":
143
- addLog(`[${timeStr}] [DONE] Task completed (confidence: ${event.metrics.confidence.toFixed(2)})`);
221
+ addLog(`[${timeStr}] DONE (confidence: ${event.metrics.confidence.toFixed(2)}, duration: ${event.metrics.totalLatencyMs}ms)`);
144
222
  setState((prev) => {
145
223
  const newSessions = prev.metrics.sessions + 1;
146
224
  const newAvgDuration = prev.metrics.avgDuration === 0
147
225
  ? event.metrics.totalLatencyMs
148
226
  : Math.round((prev.metrics.avgDuration * prev.metrics.sessions + event.metrics.totalLatencyMs) / newSessions);
227
+ const newConfidenceAvg = prev.metrics.confidenceAvg === 0
228
+ ? event.metrics.confidence
229
+ : (prev.metrics.confidenceAvg * prev.metrics.sessions + event.metrics.confidence) / newSessions;
149
230
  const newSuccessRate = event.metrics.confidence >= 0.7
150
231
  ? Math.round((prev.metrics.successRate * prev.metrics.sessions + 100) / newSessions)
151
232
  : Math.round((prev.metrics.successRate * prev.metrics.sessions) / newSessions);
@@ -153,109 +234,169 @@ export function useAppState() {
153
234
  ...prev,
154
235
  status: "ready",
155
236
  isProcessing: false,
156
- fitness: Math.min(prev.fitness + 0.02, 0.99),
237
+ fitness: Math.min(prev.fitness + (event.metrics.confidence * 0.05), 0.99),
157
238
  metrics: {
158
239
  sessions: newSessions,
159
240
  avgDuration: newAvgDuration,
160
- successRate: newSuccessRate,
161
- totalCost: prev.metrics.totalCost + (event.metrics.totalCostUsd || 0),
162
- totalRetries: prev.metrics.totalRetries + (event.metrics.retries || 0),
163
- totalEscalations: prev.metrics.totalEscalations + (event.metrics.escalations || 0),
241
+ successRate: Math.min(newSuccessRate, 100),
242
+ totalCost: prev.metrics.totalCost + event.metrics.totalCostUsd,
243
+ totalRetries: prev.metrics.totalRetries + event.metrics.retries,
244
+ totalEscalations: prev.metrics.totalEscalations + event.metrics.escalations,
245
+ totalTokens: prev.metrics.totalTokens,
246
+ confidenceAvg: newConfidenceAvg,
247
+ },
248
+ memory: {
249
+ nodes: buildMemoryNodes(prev.workflowState),
250
+ edges: buildMemoryEdges(prev.workflowState),
251
+ nodeCount: prev.workflowState.nodeCount,
252
+ cacheHitRate: Math.min(90 + event.metrics.confidence * 10, 98),
164
253
  },
165
254
  };
166
255
  });
167
256
  break;
168
257
  }
169
- }, []);
170
- const addLog = useCallback((log) => {
171
- setState((prev) => ({
172
- ...prev,
173
- logs: [...prev.logs.slice(-100), log],
174
- }));
175
- }, []);
176
- const addMessage = useCallback((type, content, agent, model) => {
177
- setState((prev) => ({
178
- ...prev,
179
- messages: [
180
- ...prev.messages,
181
- { id: generateId(), type, content, timestamp: new Date(), agent, model: model || prev.model },
182
- ],
183
- }));
184
- }, []);
185
- const updateAgentStatus = useCallback((agentName, status) => {
186
- setState((prev) => ({
187
- ...prev,
188
- agents: prev.agents.map((a) => a.name === agentName ? { ...a, status } : a),
189
- }));
190
- }, []);
191
- const updateDAGNode = useCallback((nodeId, status) => {
192
- setState((prev) => {
193
- const newLevels = prev.workflowState.levels.map((level) => {
194
- const newNodes = level.nodes.map((node) => node.id === nodeId ? { ...node, status } : node);
195
- const doneCount = newNodes.filter((n) => n.status === "done").length;
196
- return {
197
- ...level,
198
- nodes: newNodes,
199
- progress: Math.round((doneCount / newNodes.length) * 100),
200
- };
258
+ }, [addLog]);
259
+ function buildWorkflowFromGraph(graph) {
260
+ const levels = [];
261
+ const processed = new Set();
262
+ const nodes = graph.nodes;
263
+ while (processed.size < nodes.length) {
264
+ const levelNodes = nodes.filter((n) => !processed.has(n.id) && n.dependsOn.every((d) => processed.has(d)));
265
+ if (levelNodes.length === 0)
266
+ break;
267
+ levels.push({
268
+ name: `L${levels.length + 1}`,
269
+ progress: 0,
270
+ nodes: levelNodes.map((n) => ({
271
+ id: n.id,
272
+ name: n.agent,
273
+ status: "pending",
274
+ agent: n.agent,
275
+ })),
201
276
  });
202
- const totalDone = newLevels.reduce((acc, l) => acc + l.nodes.filter((n) => n.status === "done").length, 0);
203
- const totalNodes = newLevels.reduce((acc, l) => acc + l.nodes.length, 0);
277
+ levelNodes.forEach((n) => processed.add(n.id));
278
+ }
279
+ return {
280
+ levels,
281
+ currentLevel: -1,
282
+ totalProgress: 0,
283
+ graphId: graph.id,
284
+ nodeCount: nodes.length,
285
+ };
286
+ }
287
+ function updateAgentInList(agents, name, status, confidence) {
288
+ const existing = agents.find((a) => a.name === name);
289
+ if (existing) {
290
+ return agents.map((a) => a.name === name ? { ...a, status, confidence, lastRun: new Date() } : a);
291
+ }
292
+ return [...agents, { name, role: name, status, provider: state.provider, confidence }];
293
+ }
294
+ function updateNodeStatus(workflow, nodeId, status, confidence) {
295
+ const now = Date.now();
296
+ const newLevels = workflow.levels.map((level) => {
297
+ const newNodes = level.nodes.map((node) => node.id === nodeId
298
+ ? {
299
+ ...node,
300
+ status,
301
+ confidence,
302
+ startTime: status === "running" ? now : node.startTime,
303
+ endTime: status === "done" || status === "error" ? now : node.endTime,
304
+ }
305
+ : node);
306
+ const doneCount = newNodes.filter((n) => n.status === "done").length;
204
307
  return {
205
- ...prev,
206
- workflowState: {
207
- levels: newLevels,
208
- currentLevel: newLevels.findIndex((l) => l.nodes.some((n) => n.status === "running")),
209
- totalProgress: Math.round((totalDone / totalNodes) * 100),
210
- },
308
+ ...level,
309
+ nodes: newNodes,
310
+ progress: Math.round((doneCount / newNodes.length) * 100),
211
311
  };
212
312
  });
213
- }, []);
313
+ const totalDone = newLevels.reduce((acc, l) => acc + l.nodes.filter((n) => n.status === "done").length, 0);
314
+ const totalNodes = newLevels.reduce((acc, l) => acc + l.nodes.length, 0);
315
+ const currentLevel = newLevels.findIndex((l) => l.nodes.some((n) => n.status === "running"));
316
+ return {
317
+ levels: newLevels,
318
+ currentLevel,
319
+ totalProgress: totalNodes > 0 ? Math.round((totalDone / totalNodes) * 100) : 0,
320
+ graphId: workflow.graphId,
321
+ nodeCount: workflow.nodeCount,
322
+ };
323
+ }
324
+ function incrementProviderRequests(providers, providerName) {
325
+ return providers.map((p) => p.name === providerName ? { ...p, requests: p.requests + 1 } : p);
326
+ }
327
+ function buildMemoryNodes(workflow) {
328
+ return workflow.levels.flatMap((level) => level.nodes
329
+ .filter((n) => n.status === "done" && n.agent)
330
+ .map((n) => ({
331
+ id: n.id,
332
+ type: n.agent.toLowerCase().includes("test") ? "test"
333
+ : n.agent.toLowerCase().includes("fix") ? "fix"
334
+ : n.agent.toLowerCase().includes("code") ? "action"
335
+ : "task",
336
+ label: n.name.slice(0, 12),
337
+ confidence: n.confidence,
338
+ })));
339
+ }
340
+ function buildMemoryEdges(workflow) {
341
+ const edges = [];
342
+ for (let i = 0; i < workflow.levels.length - 1; i++) {
343
+ const currentLevel = workflow.levels[i];
344
+ const nextLevel = workflow.levels[i + 1];
345
+ currentLevel.nodes.forEach((node) => {
346
+ nextLevel.nodes.slice(0, 2).forEach((nextNode) => {
347
+ edges.push({ from: node.id, to: nextNode.id });
348
+ });
349
+ });
350
+ }
351
+ return edges.slice(0, 10);
352
+ }
214
353
  const resetWorkflow = useCallback(() => {
354
+ nodeTimingsRef.current.clear();
215
355
  setState((prev) => ({
216
356
  ...prev,
217
- workflowState: DEFAULT_WORKFLOW_STATE,
218
- agents: DEFAULT_AGENTS.map((a) => ({ ...a, status: "idle" })),
357
+ workflowState: EMPTY_WORKFLOW_STATE,
358
+ agents: prev.agents.map((a) => ({ ...a, status: "idle" })),
219
359
  }));
220
360
  }, []);
221
361
  const processCommand = useCallback(async (input) => {
222
362
  const trimmed = input.trim().toLowerCase();
223
363
  const parts = trimmed.split(" ");
224
364
  const command = parts[0];
225
- const args = parts.slice(1).join(" ");
226
365
  switch (command) {
227
366
  case "help":
228
- addMessage("system", `Commandes disponibles:
229
- stop - Arrêter le workflow en cours
230
- retry - Relancer la dernière tâche
231
- clear - Nettoyer les messages
232
- session - Voir les sessions
233
- evolve - Forcer l'évolution du workflow
234
- theme - Changer de thème (theme <nom> pour voir la liste)
235
- toggle - Basculer entre modes (full/compact/minimal)
236
- history - Historique des workflows
237
- config - Voir la configuration
238
- exit - Quitter
367
+ addMessage("system", `Available commands:
368
+ stop - Stop current workflow
369
+ retry - Retry last task
370
+ clear - Clear messages
371
+ session - View sessions
372
+ evolve - Force workflow evolution
373
+ theme - Change theme
374
+ providers - List providers
375
+ toggle - Toggle view mode
376
+ history - View history
377
+ config - View configuration
378
+ exit - Quit
239
379
 
240
- Tout autre texte = lance un workflow avec ce prompt.`);
380
+ Any other text = start workflow with that prompt.`);
241
381
  break;
242
382
  case "stop":
243
383
  if (state.isProcessing) {
244
- addMessage("system", "⏹ Workflow arrêté.");
384
+ orchestratorRef.current.stop();
385
+ addMessage("system", "⏹ Workflow stopped.");
245
386
  setState((prev) => ({ ...prev, status: "ready", isProcessing: false, currentWorkflow: null }));
246
387
  resetWorkflow();
247
388
  }
248
389
  else {
249
- addMessage("system", "Aucun workflow en cours.");
390
+ addMessage("system", "No workflow in progress.");
250
391
  }
251
392
  break;
252
393
  case "retry":
253
394
  if (state.currentWorkflow) {
254
- addMessage("system", `🔄 Relance du workflow: ${state.currentWorkflow}`);
395
+ addMessage("system", `🔄 Retrying workflow: ${state.currentWorkflow}`);
255
396
  runWorkflow(state.currentWorkflow);
256
397
  }
257
398
  else {
258
- addMessage("error", "Aucun workflow à relancer.");
399
+ addMessage("error", "No workflow to retry.");
259
400
  }
260
401
  break;
261
402
  case "clear":
@@ -264,7 +405,7 @@ Tout autre texte = lance un workflow avec ce prompt.`);
264
405
  messages: [{
265
406
  id: generateId(),
266
407
  type: "system",
267
- content: "Messages nettoyés.",
408
+ content: "Messages cleared.",
268
409
  timestamp: new Date(),
269
410
  model: prev.model,
270
411
  }],
@@ -274,20 +415,21 @@ Tout autre texte = lance un workflow avec ce prompt.`);
274
415
  case "session":
275
416
  const sessionList = state.sessions.length > 0
276
417
  ? state.sessions.map((s, i) => `${i + 1}. [${s.timestamp.toLocaleTimeString()}] ${s.prompt.slice(0, 30)}... (${s.status})`).join("\n")
277
- : "Aucune session enregistrée.";
418
+ : "No sessions recorded.";
278
419
  addMessage("system", `SESSIONS (${state.sessions.length})\n${sessionList}`);
279
420
  break;
421
+ case "providers":
422
+ const providerList = state.providers.map((p) => `${p.name}: ${p.status} (${p.latency}ms) - ${p.requests} requests`).join("\n");
423
+ addMessage("system", `PROVIDERS\n${providerList}`);
424
+ break;
280
425
  case "evolve":
281
- const mutations = Math.floor(Math.random() * 3) + 1;
282
426
  const newFitness = Math.min(state.fitness + 0.05, 0.99);
283
427
  setState((prev) => ({ ...prev, fitness: newFitness }));
284
428
  addMessage("system", `EVOLUTION
285
- Mutations appliquées: +${mutations}
286
429
  Fitness: ${state.fitness.toFixed(2)} → ${newFitness.toFixed(2)}
287
- Tendance: ↗ +${((newFitness - state.fitness) * 100).toFixed(1)}%`);
430
+ Trend: ↗ +${((newFitness - state.fitness) * 100).toFixed(1)}%`);
288
431
  break;
289
432
  case "theme":
290
- // Show interactive theme selector
291
433
  setState((prev) => ({
292
434
  ...prev,
293
435
  interactiveCommand: {
@@ -312,11 +454,11 @@ Tendance: ↗ +${((newFitness - state.fitness) * 100).toFixed(1)}%`);
312
454
  const currentIndex = modes.indexOf(state.viewMode);
313
455
  const nextMode = modes[(currentIndex + 1) % modes.length];
314
456
  setState((prev) => ({ ...prev, viewMode: nextMode }));
315
- addMessage("system", `Mode changé: ${nextMode.toUpperCase()}`);
457
+ addMessage("system", `View mode: ${nextMode.toUpperCase()}`);
316
458
  break;
317
459
  case "history":
318
- const historyList = state.sessions.slice(-5).map((s, i) => `${i + 1}. ${s.prompt.slice(0, 40)}... [${s.status}]`).join("\n") || "Aucun historique.";
319
- addMessage("system", `HISTORIQUE (derniers 5)\n${historyList}`);
460
+ const historyList = state.sessions.slice(-5).map((s, i) => `${i + 1}. ${s.prompt.slice(0, 40)}... [${s.status}]`).join("\n") || "No history.";
461
+ addMessage("system", `HISTORY (last 5)\n${historyList}`);
320
462
  break;
321
463
  case "config":
322
464
  addMessage("system", `CONFIGURATION
@@ -326,11 +468,13 @@ Theme: ${state.theme}
326
468
  View Mode: ${state.viewMode}
327
469
  Fitness: ${state.fitness.toFixed(2)}
328
470
  Agents: ${state.agents.length}
329
- Sessions: ${state.metrics.sessions}`);
471
+ Sessions: ${state.metrics.sessions}
472
+ Tokens: ${state.metrics.totalTokens}
473
+ Cost: $${state.metrics.totalCost.toFixed(4)}`);
330
474
  break;
331
475
  case "exit":
332
476
  case "quit":
333
- addMessage("system", "À bientôt !");
477
+ addMessage("system", "Goodbye!");
334
478
  break;
335
479
  default:
336
480
  runWorkflow(input);
@@ -339,7 +483,7 @@ Sessions: ${state.metrics.sessions}`);
339
483
  const runWorkflow = useCallback(async (prompt) => {
340
484
  resetWorkflow();
341
485
  addMessage("user", prompt);
342
- addMessage("agent", "Lancement du workflow...", "Orchestrator", state.model);
486
+ addMessage("agent", "Starting workflow...", "Orchestrator", state.model);
343
487
  const sessionId = generateId();
344
488
  setState((prev) => ({
345
489
  ...prev,
@@ -350,33 +494,36 @@ Sessions: ${state.metrics.sessions}`);
350
494
  timestamp: new Date(),
351
495
  status: "running",
352
496
  model: prev.model,
353
- }]
497
+ }],
354
498
  }));
355
499
  const startTime = Date.now();
356
500
  try {
357
- await orchestratorRef.current.run(prompt);
501
+ const result = await orchestratorRef.current.run(prompt);
358
502
  const duration = Date.now() - startTime;
359
- addMessage("success", `Workflow terminé! Fitness: ${(state.fitness + 0.02).toFixed(2)} | Durée: ${(duration / 1000).toFixed(1)}s`, undefined, state.model);
503
+ addMessage("success", `Workflow completed!\nConfidence: ${result.metrics.confidence.toFixed(2)}\nDuration: ${(duration / 1000).toFixed(1)}s\nCost: $${result.metrics.totalCostUsd.toFixed(4)}`, undefined, state.model);
360
504
  setState((prev) => ({
361
505
  ...prev,
362
- sessions: prev.sessions.map((s) => s.id === sessionId ? { ...s, status: "completed", duration } : s),
506
+ sessions: prev.sessions.map((s) => s.id === sessionId
507
+ ? { ...s, status: "completed", duration, confidence: result.metrics.confidence }
508
+ : s),
363
509
  }));
364
510
  }
365
511
  catch (error) {
366
- addMessage("error", `Erreur: ${error instanceof Error ? error.message : String(error)}`);
512
+ addMessage("error", `Error: ${error instanceof Error ? error.message : String(error)}`);
367
513
  setState((prev) => ({
368
514
  ...prev,
369
515
  status: "error",
370
516
  isProcessing: false,
371
- sessions: prev.sessions.map((s) => s.id === sessionId ? { ...s, status: "failed", duration: Date.now() - startTime } : s),
517
+ sessions: prev.sessions.map((s) => s.id === sessionId
518
+ ? { ...s, status: "failed", duration: Date.now() - startTime }
519
+ : s),
372
520
  }));
373
521
  }
374
- }, [resetWorkflow, addMessage, state.fitness, state.model]);
522
+ }, [resetWorkflow, addMessage, state.model]);
375
523
  const handleInteractiveCommandNavigation = useCallback((direction) => {
376
524
  if (!state.interactiveCommand.type)
377
525
  return;
378
526
  if (direction === "escape") {
379
- // Close the interactive command
380
527
  setState((prev) => ({
381
528
  ...prev,
382
529
  interactiveCommand: { type: null, options: [], selectedIndex: 0 },