@memberjunction/ng-ai-test-harness 2.59.0 → 2.61.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.
@@ -1,5 +1,4 @@
1
1
  import { Component, Input, ViewChild, ChangeDetectionStrategy, ViewContainerRef, Output, EventEmitter } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
2
  import { Subject, interval } from 'rxjs';
4
3
  import { takeUntil } from 'rxjs/operators';
5
4
  import { ExecutionNodeComponent } from './agent-execution-node.component';
@@ -18,22 +17,17 @@ function AgentExecutionMonitorComponent_Conditional_7_Template(rf, ctx) { if (rf
18
17
  i0.ɵɵelementEnd();
19
18
  i0.ɵɵelementStart(3, "span", 21);
20
19
  i0.ɵɵtext(4);
21
- i0.ɵɵelementEnd();
22
- i0.ɵɵelementStart(5, "span", 22);
23
- i0.ɵɵtext(6);
24
20
  i0.ɵɵelementEnd()();
25
21
  } if (rf & 2) {
26
22
  const ctx_r1 = i0.ɵɵnextContext();
27
23
  i0.ɵɵadvance(4);
28
- i0.ɵɵtextInterpolate(ctx_r1.formatAgentPath(ctx_r1.currentStep.agentPath));
29
- i0.ɵɵadvance(2);
30
- i0.ɵɵtextInterpolate(ctx_r1.currentStep.name);
24
+ i0.ɵɵtextInterpolate(ctx_r1.currentStep.StepName);
31
25
  } }
32
26
  function AgentExecutionMonitorComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
33
27
  const _r3 = i0.ɵɵgetCurrentView();
34
- i0.ɵɵelementStart(0, "button", 23);
28
+ i0.ɵɵelementStart(0, "button", 22);
35
29
  i0.ɵɵlistener("click", function AgentExecutionMonitorComponent_Conditional_8_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onViewRunClick()); });
36
- i0.ɵɵelement(1, "i", 24);
30
+ i0.ɵɵelement(1, "i", 23);
37
31
  i0.ɵɵtext(2);
38
32
  i0.ɵɵelementEnd();
39
33
  } if (rf & 2) {
@@ -43,7 +37,7 @@ function AgentExecutionMonitorComponent_Conditional_8_Template(rf, ctx) { if (rf
43
37
  } }
44
38
  function AgentExecutionMonitorComponent_Conditional_13_Template(rf, ctx) { if (rf & 1) {
45
39
  i0.ɵɵelementStart(0, "div", 11);
46
- i0.ɵɵelement(1, "i", 25);
40
+ i0.ɵɵelement(1, "i", 24);
47
41
  i0.ɵɵelementStart(2, "p");
48
42
  i0.ɵɵtext(3, "Waiting for execution to begin...");
49
43
  i0.ɵɵelementEnd()();
@@ -70,18 +64,18 @@ function AgentExecutionMonitorComponent_Conditional_37_Template(rf, ctx) { if (r
70
64
  i0.ɵɵtextInterpolate(ctx_r1.formatDuration(ctx_r1.stats.totalDuration));
71
65
  } }
72
66
  function AgentExecutionMonitorComponent_Conditional_38_For_2_Template(rf, ctx) { if (rf & 1) {
73
- i0.ɵɵelementStart(0, "span", 26);
67
+ i0.ɵɵelementStart(0, "span", 25);
74
68
  i0.ɵɵtext(1);
75
69
  i0.ɵɵelementEnd();
76
70
  } if (rf & 2) {
77
71
  const type_r4 = ctx.$implicit;
78
72
  const ctx_r1 = i0.ɵɵnextContext(2);
79
73
  i0.ɵɵadvance();
80
- i0.ɵɵtextInterpolate2(" ", ctx_r1.stats.stepsByType[type_r4], " ", type_r4, " ");
74
+ i0.ɵɵtextInterpolate2(" ", ctx_r1.stats.stepsByType[type_r4], " ", ctx_r1.pluralizeStepType(type_r4, ctx_r1.stats.stepsByType[type_r4]), " ");
81
75
  } }
82
76
  function AgentExecutionMonitorComponent_Conditional_38_Template(rf, ctx) { if (rf & 1) {
83
77
  i0.ɵɵelementStart(0, "div", 18);
84
- i0.ɵɵrepeaterCreate(1, AgentExecutionMonitorComponent_Conditional_38_For_2_Template, 2, 2, "span", 26, i0.ɵɵrepeaterTrackByIdentity);
78
+ i0.ɵɵrepeaterCreate(1, AgentExecutionMonitorComponent_Conditional_38_For_2_Template, 2, 2, "span", 25, i0.ɵɵrepeaterTrackByIdentity);
85
79
  i0.ɵɵelementEnd();
86
80
  } if (rf & 2) {
87
81
  const ctx_r1 = i0.ɵɵnextContext();
@@ -108,15 +102,19 @@ export class AgentExecutionMonitorComponent {
108
102
  constructor(cdr) {
109
103
  this.cdr = cdr;
110
104
  this.mode = 'historical';
111
- this.executionData = null; // Can be live updates or historical data
105
+ this.agentRun = null; // For historical mode
106
+ this.liveSteps = []; // For live mode streaming
112
107
  this.autoExpand = true; // Auto-expand nodes in live mode
113
108
  this.runId = null; // ID of the run (agent or prompt)
114
109
  this.runType = 'agent'; // Type of run
115
110
  this.viewRunClick = new EventEmitter();
116
- this.executionTree = [];
111
+ // Store the currently rendered steps for UI state management
117
112
  this.currentStep = null;
118
113
  // Track component references for dynamic components
119
114
  this.nodeComponentMap = new Map();
115
+ // UI state management - track expanded states separately from entities
116
+ this.expandedStates = new Map();
117
+ this.detailsExpandedStates = new Map();
120
118
  this.stats = {
121
119
  totalSteps: 0,
122
120
  completedSteps: 0,
@@ -145,84 +143,83 @@ export class AgentExecutionMonitorComponent {
145
143
  }
146
144
  ngOnChanges(changes) {
147
145
  console.log('🔄 Execution Monitor ngOnChanges:', {
148
- executionDataChanged: !!changes['executionData'],
146
+ agentRunChanged: !!changes['agentRun'],
147
+ liveStepsChanged: !!changes['liveSteps'],
149
148
  modeChanged: !!changes['mode'],
150
- hasExecutionData: !!this.executionData,
151
- currentTreeLength: this.executionTree.length
149
+ hasAgentRun: !!this.agentRun,
150
+ liveStepsCount: this.liveSteps?.length || 0
152
151
  });
153
- if (changes['executionData']) {
154
- // Log the change details
155
- const oldData = changes['executionData'].previousValue;
156
- const newData = changes['executionData'].currentValue;
157
- console.log('📊 Execution data changed:', {
158
- oldDataExists: !!oldData,
159
- newDataExists: !!newData,
160
- newDataKeys: newData ? Object.keys(newData) : [],
161
- executionTreeLength: newData?.executionTree?.length,
162
- forceRefresh: newData?._forceRefresh
152
+ // Handle agent run changes (historical mode)
153
+ if (changes['agentRun'] && this.mode === 'historical') {
154
+ const oldRun = changes['agentRun'].previousValue;
155
+ const newRun = changes['agentRun'].currentValue;
156
+ console.log('📊 Agent run changed:', {
157
+ oldRunExists: !!oldRun,
158
+ newRunExists: !!newRun,
159
+ oldRunId: oldRun?.ID,
160
+ newRunId: newRun?.ID,
161
+ stepsCount: newRun?.Steps?.length
163
162
  });
164
- // Check if this is a different execution by comparing run IDs
165
- const oldRunId = oldData?.agentRun?.ID || oldData?.promptRun?.ID;
166
- const newRunId = newData?.agentRun?.ID || newData?.promptRun?.ID;
167
- const isDifferentExecution = oldRunId && newRunId && oldRunId !== newRunId;
168
- const isStartingNewLiveExecution = this.mode === 'live' &&
169
- newData &&
170
- newData.liveSteps &&
171
- newData.liveSteps.length === 0 &&
172
- this.executionTree.length > 0;
173
- const isForceRefresh = newData && newData._forceRefresh;
174
- // Clear if it's a genuinely different execution, starting fresh live execution, or force refresh
175
- if (isDifferentExecution || isStartingNewLiveExecution || isForceRefresh) {
176
- console.log('🗑️ Clearing execution tree due to:', {
177
- isDifferentExecution,
178
- isStartingNewLiveExecution,
179
- isForceRefresh
180
- });
163
+ // Only clear if it's actually a different execution (different ID)
164
+ const isDifferentExecution = (!oldRun && newRun) ||
165
+ (oldRun && newRun && oldRun.ID !== newRun.ID) ||
166
+ (oldRun && !newRun);
167
+ if (isDifferentExecution) {
168
+ console.log('🗑️ Clearing for different agent run');
181
169
  this.processedStepIds.clear();
182
170
  this.newlyAddedNodeIds.clear();
171
+ this.expandedStates.clear();
172
+ this.detailsExpandedStates.clear();
183
173
  this.clearNodeComponents();
184
- this.executionTree = [];
185
174
  this.currentStep = null;
186
- this.calculateStats();
187
175
  }
188
- // Check for auto-expand flag
189
- if (this.executionData && this.executionData._autoExpandAll) {
190
- // Auto-expand all nodes after a short delay to ensure rendering is complete
191
- setTimeout(() => {
192
- this.expandAllNodes();
193
- }, 50);
176
+ // Always process the agent run data (will handle updates to existing run)
177
+ if (newRun) {
178
+ this.processAgentRun();
194
179
  }
195
- // Process data (this will preserve existing nodes if it's the same execution)
196
- this.processExecutionData();
197
180
  }
198
- // Handle mode changes - be more selective about when to clear
181
+ // Handle live steps changes (live mode)
182
+ if (changes['liveSteps'] && this.mode === 'live') {
183
+ const oldSteps = changes['liveSteps'].previousValue;
184
+ const newSteps = changes['liveSteps'].currentValue;
185
+ console.log('📊 Live steps changed:', {
186
+ oldCount: oldSteps?.length || 0,
187
+ newCount: newSteps?.length || 0
188
+ });
189
+ // Process live steps
190
+ this.processLiveSteps();
191
+ }
192
+ // Handle mode changes
199
193
  if (changes['mode'] && !changes['mode'].firstChange) {
200
194
  const previousMode = changes['mode'].previousValue;
201
195
  const currentMode = changes['mode'].currentValue;
202
196
  console.log('🔄 Mode changed:', { previousMode, currentMode });
203
- // Only clear when switching from live to historical AND we don't have historical data
204
- if (previousMode === 'live' && currentMode === 'historical') {
205
- // Stop live updates
206
- if (this.updateSubscription) {
207
- this.updateSubscription.unsubscribe();
208
- this.updateSubscription = undefined;
209
- }
210
- // Only clear if we don't have valid historical data to show
211
- if (!this.executionData || (!this.executionData.executionTree && !this.executionData.steps)) {
212
- this.processedStepIds.clear();
213
- this.newlyAddedNodeIds.clear();
214
- this.clearNodeComponents();
215
- this.executionTree = [];
216
- }
197
+ // Clear everything when switching modes
198
+ this.processedStepIds.clear();
199
+ this.newlyAddedNodeIds.clear();
200
+ this.expandedStates.clear();
201
+ this.detailsExpandedStates.clear();
202
+ this.clearNodeComponents();
203
+ this.currentStep = null;
204
+ // Stop live updates when switching away from live mode
205
+ if (previousMode === 'live' && this.updateSubscription) {
206
+ this.updateSubscription.unsubscribe();
207
+ this.updateSubscription = undefined;
208
+ }
209
+ // Process data for the new mode
210
+ if (currentMode === 'historical' && this.agentRun) {
211
+ this.processAgentRun();
212
+ }
213
+ else if (currentMode === 'live') {
214
+ this.setupLiveUpdates();
217
215
  }
218
- this.processExecutionData();
219
216
  }
220
217
  }
221
218
  ngAfterViewInit() {
222
219
  this.viewInitialized = true;
223
220
  console.log('🎯 View initialized, checking for pending data:', {
224
- hasExecutionData: !!this.executionData,
225
- treeLength: this.executionTree.length,
221
+ hasAgentRun: !!this.agentRun,
222
+ hasLiveSteps: this.liveSteps?.length > 0,
226
223
  hasContainer: !!this.executionNodesContainer
227
224
  });
228
225
  // Initial setup for scroll behavior
@@ -230,13 +227,13 @@ export class AgentExecutionMonitorComponent {
230
227
  this.checkIfUserAtBottom();
231
228
  }
232
229
  // If we have data waiting to be rendered, render it now
233
- if (this.executionData && this.executionTree.length === 0) {
234
- console.log('⚡ Processing pending execution data after view init');
235
- this.processExecutionData();
230
+ if (this.mode === 'historical' && this.agentRun && this.nodeComponentMap.size === 0) {
231
+ console.log('⚡ Processing agent run after view init');
232
+ this.processAgentRun();
236
233
  }
237
- else if (this.executionTree.length > 0 && this.nodeComponentMap.size === 0) {
238
- console.log('⚡ Re-rendering existing tree after view init');
239
- this.renderTree();
234
+ else if (this.mode === 'live' && this.liveSteps?.length > 0) {
235
+ console.log('⚡ Processing live steps after view init');
236
+ this.processLiveSteps();
240
237
  }
241
238
  }
242
239
  ngOnDestroy() {
@@ -259,288 +256,192 @@ export class AgentExecutionMonitorComponent {
259
256
  }
260
257
  }
261
258
  /**
262
- * Process execution data based on mode
259
+ * Process agent run for historical mode
263
260
  */
264
- processExecutionData() {
265
- console.log('⚙️ Processing execution data:', {
266
- mode: this.mode,
267
- hasExecutionData: !!this.executionData,
268
- currentTreeLength: this.executionTree.length,
261
+ processAgentRun() {
262
+ console.log('⚙️ Processing agent run:', {
263
+ hasAgentRun: !!this.agentRun,
264
+ stepsCount: this.agentRun?.Steps?.length || 0,
269
265
  viewInitialized: this.viewInitialized,
270
266
  hasContainer: !!this.executionNodesContainer
271
267
  });
272
- if (this.mode === 'historical') {
273
- // Only clear processed step IDs if we don't have existing data or it's different data
274
- if (this.executionTree.length === 0) {
275
- this.processedStepIds.clear();
276
- }
277
- // Process historical data
278
- this.buildTreeFromHistoricalData();
268
+ if (!this.agentRun || !this.viewInitialized || !this.executionNodesContainer) {
269
+ console.warn('⚠️ Cannot process agent run:', {
270
+ agentRun: !this.agentRun ? 'missing' : 'present',
271
+ viewInitialized: this.viewInitialized ? 'yes' : 'no',
272
+ container: !this.executionNodesContainer ? 'missing' : 'present'
273
+ });
274
+ return;
275
+ }
276
+ // Clear existing components
277
+ this.clearNodeComponents();
278
+ // Render the agent run steps
279
+ if (this.agentRun.Steps && this.agentRun.Steps.length > 0) {
280
+ console.log('🎨 Rendering', this.agentRun.Steps.length, 'steps');
281
+ this.renderSteps(this.agentRun.Steps, 0, []);
282
+ console.log('✅ Finished rendering, container now has', this.executionNodesContainer.length, 'components');
279
283
  }
280
284
  else {
281
- // For live mode, preserve existing data when possible
282
- if (this.executionTree.length === 0) {
283
- this.processedStepIds.clear();
284
- this.newlyAddedNodeIds.clear();
285
- }
286
- // Set up live updates
287
- this.setupLiveUpdates();
285
+ console.warn('⚠️ No steps to render');
288
286
  }
289
- // Always calculate stats when we have data or when processing new data
287
+ // Calculate statistics
290
288
  this.calculateStats();
291
- // Force change detection after processing
292
- if (this.viewInitialized) {
293
- this.cdr.markForCheck();
294
- this.cdr.detectChanges();
295
- }
289
+ // Trigger change detection after rendering all components
290
+ this.cdr.detectChanges();
296
291
  }
297
292
  /**
298
- * Build tree from historical execution data
293
+ * Process live steps for live mode
299
294
  */
300
- buildTreeFromHistoricalData() {
301
- console.log('🏗️ Building tree from historical data:', {
302
- hasExecutionData: !!this.executionData,
303
- executionTreeLength: this.executionData?.executionTree?.length,
304
- stepsLength: this.executionData?.steps?.length,
305
- currentTreeLength: this.executionTree.length
295
+ processLiveSteps() {
296
+ console.log('⚙️ Processing live steps:', {
297
+ stepsCount: this.liveSteps?.length || 0,
298
+ viewInitialized: this.viewInitialized,
299
+ hasContainer: !!this.executionNodesContainer
306
300
  });
307
- if (!this.executionData) {
308
- console.log('⚠️ No execution data available');
309
- this.clearNodeComponents();
310
- this.executionTree = [];
301
+ if (!this.liveSteps || !this.viewInitialized || !this.executionNodesContainer) {
311
302
  return;
312
303
  }
313
- // Clear existing components for full rebuild
314
- this.clearNodeComponents();
315
- // Handle executionTree format - historical mode should only use saved database data
316
- if (this.executionData.executionTree) {
317
- console.log('📊 Converting execution tree with', this.executionData.executionTree.length, 'nodes');
318
- const allNodes = this.convertExecutionTree(this.executionData.executionTree);
319
- console.log('🔄 Converted to', allNodes.length, 'tree nodes');
320
- // Filter out live-only nodes in historical mode
321
- this.executionTree = this.filterNodesForMode(allNodes, 'historical');
322
- console.log('✅ After filtering for historical mode:', this.executionTree.length, 'nodes');
323
- }
324
- else if (this.executionData.steps) {
325
- console.log('📊 Converting legacy steps with', this.executionData.steps.length, 'steps');
326
- // Handle legacy steps format
327
- const allNodes = this.convertLegacySteps(this.executionData.steps);
328
- this.executionTree = this.filterNodesForMode(allNodes, 'historical');
329
- console.log('✅ After converting legacy steps:', this.executionTree.length, 'nodes');
330
- }
331
- else {
332
- console.log('⚠️ No executionTree or steps found in data');
333
- this.executionTree = [];
334
- }
335
- // Always render the tree if we have a container
336
- if (this.executionNodesContainer) {
337
- console.log('🎨 Rendering tree with', this.executionTree.length, 'nodes');
338
- this.renderTree();
339
- }
340
- else {
341
- console.log('⚠️ No execution nodes container available for rendering');
304
+ // Append new steps without clearing existing ones
305
+ this.appendNewLiveSteps(this.liveSteps);
306
+ // Trigger change detection after appending live steps
307
+ this.cdr.detectChanges();
308
+ // Set up live update monitoring if not already set up
309
+ if (!this.updateSubscription) {
310
+ this.setupLiveUpdates();
342
311
  }
343
312
  }
344
313
  /**
345
- * Render the execution tree using dynamic components
314
+ * Render steps recursively with proper hierarchy
346
315
  */
347
- renderTree() {
348
- console.log('🎨 Rendering execution tree:', {
349
- hasContainer: !!this.executionNodesContainer,
350
- treeLength: this.executionTree.length,
351
- viewInitialized: this.viewInitialized
316
+ renderSteps(steps, depth, agentPath) {
317
+ console.log('🎨 Rendering steps:', {
318
+ count: steps.length,
319
+ depth,
320
+ agentPath
352
321
  });
353
- if (!this.executionNodesContainer) {
354
- console.warn('📊 Execution Monitor: No container available for rendering');
355
- return;
356
- }
357
- // Clear and recreate all components for historical view
358
- this.clearNodeComponents();
359
- console.log('🔄 Creating components for', this.executionTree.length, 'nodes');
360
- // Create components for each root node
361
- for (const node of this.executionTree) {
362
- const componentRef = this.createNodeComponent(node, this.executionNodesContainer);
363
- console.log('✅ Created component for node:', node.name, 'depth:', node.depth);
322
+ // Sort steps by StepNumber to ensure proper ordering
323
+ const sortedSteps = [...steps].sort((a, b) => (a.StepNumber || 0) - (b.StepNumber || 0));
324
+ for (const step of sortedSteps) {
325
+ // Skip if already processed in live mode
326
+ if (this.processedStepIds.has(step.ID)) {
327
+ continue;
328
+ }
329
+ // Create component for this step
330
+ this.createStepComponent(step, depth, agentPath);
331
+ // Mark as processed
332
+ this.processedStepIds.add(step.ID);
333
+ // Handle sub-agent recursion
334
+ if (step.StepType === 'Sub-Agent' && step.SubAgentRun?.Steps) {
335
+ const subAgentPath = [...agentPath, step.SubAgentRun.Agent || 'Sub-Agent'];
336
+ this.renderSteps(step.SubAgentRun.Steps, depth + 1, subAgentPath);
337
+ }
364
338
  }
365
- // Force change detection after rendering
366
- this.cdr.markForCheck();
367
- this.cdr.detectChanges();
368
- console.log('🎯 Tree rendering complete. Components created:', this.nodeComponentMap.size);
369
339
  }
370
340
  /**
371
- * Create a component for a node and its children
372
- * This now ensures proper depth is passed and used for indentation
341
+ * Create a component for a step
373
342
  */
374
- createNodeComponent(node, container) {
375
- const componentRef = container.createComponent(ExecutionNodeComponent);
343
+ createStepComponent(step, depth, agentPath) {
344
+ // Ensure container exists
345
+ if (!this.executionNodesContainer) {
346
+ console.error('❌ executionNodesContainer not available');
347
+ throw new Error('executionNodesContainer ViewContainerRef not initialized');
348
+ }
349
+ const componentRef = this.executionNodesContainer.createComponent(ExecutionNodeComponent);
376
350
  const instance = componentRef.instance;
377
- // IMPORTANT: Ensure the node has the correct depth
378
- // Create a new node object to ensure Angular detects the change
379
- const nodeWithDepth = {
380
- ...node,
381
- depth: node.depth || 0 // Ensure depth is always defined
382
- };
383
- // Set inputs
384
- instance.node = nodeWithDepth;
351
+ console.log('🔨 Creating component for step:', {
352
+ stepId: step.ID,
353
+ stepName: step.StepName,
354
+ depth,
355
+ containerLength: this.executionNodesContainer.length,
356
+ hostElement: componentRef.location.nativeElement
357
+ });
358
+ // Pass the step data with UI state information
359
+ instance.step = step;
360
+ instance.depth = depth;
361
+ instance.agentPath = agentPath;
362
+ instance.expanded = this.expandedStates.get(step.ID) || false;
363
+ instance.detailsExpanded = this.detailsExpandedStates.get(step.ID) || false;
385
364
  // Subscribe to outputs
386
- instance.toggleNode.subscribe((n) => this.toggleNode(n));
365
+ instance.toggleNode.subscribe(() => this.toggleStepExpansion(step));
366
+ instance.toggleDetails.subscribe(() => this.toggleStepDetails(step));
387
367
  instance.userInteracted.subscribe(() => this.onUserInteraction());
388
368
  // Store reference
389
- this.nodeComponentMap.set(node.id, componentRef);
390
- // Force immediate change detection to ensure styles are applied
391
- componentRef.changeDetectorRef.markForCheck();
392
- componentRef.changeDetectorRef.detectChanges();
369
+ this.nodeComponentMap.set(step.ID, componentRef);
370
+ // Log the component state after setup
371
+ console.log('✅ Component created and configured:', {
372
+ stepId: step.ID,
373
+ hasData: !!instance.step,
374
+ isAttached: componentRef.hostView.destroyed === false
375
+ });
393
376
  return componentRef;
394
377
  }
395
378
  /**
396
- * Convert new execution tree format to display nodes
397
- * FIXED: Maintain proper hierarchy for sub-agents instead of flattening
379
+ * Toggle step expansion
398
380
  */
399
- convertExecutionTree(nodes, depth = 0) {
400
- const treeNodes = [];
401
- // Sort nodes by StepNumber before processing
402
- const sortedNodes = [...nodes].sort((a, b) => {
403
- const aStepNum = a.step?.StepNumber || 0;
404
- const bStepNum = b.step?.StepNumber || 0;
405
- return aStepNum - bStepNum;
406
- });
407
- for (const node of sortedNodes) {
408
- // Parse the step name for markdown content
409
- const stepName = node.step?.StepName || 'Unknown Step';
410
- let name = stepName;
411
- let detailsMarkdown;
412
- // Check if the step name contains markdown
413
- if (stepName.includes('\n') || stepName.includes('**') || stepName.includes('##')) {
414
- const lines = stepName.split('\n');
415
- name = lines[0].trim();
416
- if (lines.length > 1) {
417
- detailsMarkdown = lines.slice(1).join('\n').trim();
418
- }
419
- }
420
- // Determine display mode based on the step content
421
- let displayMode = 'both';
422
- // Progress messages that should only show in live mode
423
- if (name.includes('Executing') && name.includes('action') && !name.startsWith('Execute Action:')) {
424
- displayMode = 'live';
425
- }
426
- else if (name.includes("Executing agent's initial prompt")) {
427
- displayMode = 'live';
428
- }
429
- else if (name.includes('Re-executing agent prompt with additional context')) {
430
- displayMode = 'live';
431
- }
432
- // Clear metadata storage before creating previews
433
- this.lastParsedAgentData = null;
434
- this.lastParsedActionData = null;
435
- // Create previews which will populate metadata if available
436
- const inputPreview = this.createPreview(node.inputData);
437
- const outputPreview = this.createPreview(node.outputData);
438
- const treeNode = {
439
- id: node.step?.ID || `node-${Date.now()}-${Math.random()}`,
440
- name: name,
441
- type: this.mapStepType(node.executionType || node.step?.StepType),
442
- status: this.mapStepStatus(node.step?.Status),
443
- startTime: node.startTime ? new Date(node.startTime) : undefined,
444
- endTime: node.endTime ? new Date(node.endTime) : undefined,
445
- duration: node.durationMs,
446
- agentPath: node.agentHierarchy || [],
447
- inputPreview: inputPreview,
448
- outputPreview: outputPreview,
449
- error: node.step?.ErrorMessage,
450
- expanded: false, // Start collapsed for sub-agents
451
- depth: depth, // Use actual depth for proper indentation
452
- tokensUsed: this.extractTokens(node.outputData),
453
- cost: this.extractCost(node.outputData),
454
- detailsMarkdown: detailsMarkdown,
455
- detailsExpanded: false,
456
- children: undefined, // Will be set below
457
- displayMode: displayMode
458
- };
459
- // Add agent metadata if this is a sub-agent node
460
- if (treeNode.type === 'sub-agent' && this.lastParsedAgentData) {
461
- treeNode.agentName = this.lastParsedAgentData.agentName;
462
- treeNode.agentId = this.lastParsedAgentData.agentId;
463
- treeNode.agentIconClass = this.lastParsedAgentData.agentIconClass;
464
- treeNode.agentLogoURL = this.lastParsedAgentData.agentLogoURL;
465
- }
466
- // Add action metadata if this is an action node
467
- if (treeNode.type === 'action' && this.lastParsedActionData) {
468
- treeNode.actionName = this.lastParsedActionData.actionName;
469
- treeNode.actionId = this.lastParsedActionData.actionId;
470
- treeNode.actionIconClass = this.lastParsedActionData.actionIconClass;
471
- }
472
- // IMPORTANT: Process children recursively to maintain hierarchy
473
- if (node.children && node.children.length > 0) {
474
- treeNode.children = this.convertExecutionTree(node.children, depth + 1);
475
- }
476
- treeNodes.push(treeNode);
381
+ toggleStepExpansion(step) {
382
+ const currentState = this.expandedStates.get(step.ID) || false;
383
+ this.expandedStates.set(step.ID, !currentState);
384
+ this.userHasInteracted = true;
385
+ // Update the component
386
+ const componentRef = this.nodeComponentMap.get(step.ID);
387
+ if (componentRef) {
388
+ componentRef.instance.expanded = !currentState;
389
+ componentRef.changeDetectorRef.detectChanges();
477
390
  }
478
- return treeNodes;
391
+ this.cdr.markForCheck();
479
392
  }
480
393
  /**
481
- * Convert legacy step format to tree nodes
394
+ * Toggle step details expansion
482
395
  */
483
- convertLegacySteps(steps) {
484
- return steps.map((step, index) => {
485
- // Parse the step name for markdown content
486
- const stepName = step.StepName || '';
487
- let name = stepName;
488
- let detailsMarkdown;
489
- // Check if the step name contains markdown
490
- if (stepName.includes('\n') || stepName.includes('**') || stepName.includes('##')) {
491
- const lines = stepName.split('\n');
492
- name = lines[0].trim();
493
- if (lines.length > 1) {
494
- detailsMarkdown = lines.slice(1).join('\n').trim();
495
- }
496
- }
497
- return {
498
- id: step.ID || `step-${index}`,
499
- name: name,
500
- type: this.mapStepType(step.StepType),
501
- status: this.mapStepStatus(step.Status),
502
- startTime: step.StartedAt ? new Date(step.StartedAt) : undefined,
503
- endTime: step.CompletedAt ? new Date(step.CompletedAt) : undefined,
504
- duration: this.calculateDuration(step.StartedAt, step.CompletedAt),
505
- agentPath: [],
506
- inputPreview: this.createPreview(step.InputData),
507
- outputPreview: this.createPreview(step.OutputData),
508
- error: step.ErrorMessage,
509
- expanded: false,
510
- depth: 0,
511
- detailsMarkdown: detailsMarkdown,
512
- detailsExpanded: false
513
- };
514
- });
396
+ toggleStepDetails(step) {
397
+ const currentState = this.detailsExpandedStates.get(step.ID) || false;
398
+ this.detailsExpandedStates.set(step.ID, !currentState);
399
+ // Update the component
400
+ const componentRef = this.nodeComponentMap.get(step.ID);
401
+ if (componentRef) {
402
+ componentRef.instance.detailsExpanded = !currentState;
403
+ componentRef.changeDetectorRef.detectChanges();
404
+ }
515
405
  }
516
406
  /**
517
- * Map step type strings to our type enum
407
+ * Get step type CSS class for styling
518
408
  */
519
- mapStepType(type) {
409
+ getStepTypeClass(stepType) {
520
410
  const typeMap = {
521
- 'validation': 'validation',
522
- 'prompt': 'prompt',
523
- 'action': 'action',
524
- 'action_execution': 'action',
525
- 'subagent': 'sub-agent',
526
- 'sub-agent': 'sub-agent',
527
- 'decision': 'decision',
528
- 'chat': 'chat'
411
+ 'Validation': 'validation',
412
+ 'Prompt': 'prompt',
413
+ 'Actions': 'action',
414
+ 'Sub-Agent': 'sub-agent',
415
+ 'Decision': 'decision',
416
+ 'Chat': 'chat'
529
417
  };
530
- return typeMap[type?.toLowerCase()] || 'prompt';
418
+ return typeMap[stepType] || 'prompt';
531
419
  }
532
420
  /**
533
- * Map status strings to our status enum
421
+ * Get status CSS class for styling
534
422
  */
535
- mapStepStatus(status) {
423
+ getStatusClass(status) {
536
424
  const statusMap = {
537
- 'pending': 'pending',
538
- 'running': 'running',
539
- 'completed': 'completed',
540
- 'failed': 'failed',
541
- 'error': 'failed'
425
+ 'Pending': 'pending',
426
+ 'Running': 'running',
427
+ 'Completed': 'completed',
428
+ 'Failed': 'failed',
429
+ 'Cancelled': 'failed',
430
+ 'Paused': 'pending'
542
431
  };
543
- return statusMap[status?.toLowerCase()] || 'pending';
432
+ return statusMap[status] || 'pending';
433
+ }
434
+ /**
435
+ * Helper function to safely convert a value to a preview string
436
+ */
437
+ valueToPreviewString(value) {
438
+ if (value === null || value === undefined) {
439
+ return '';
440
+ }
441
+ if (typeof value === 'object') {
442
+ return JSON.stringify(value, null, 2);
443
+ }
444
+ return String(value);
544
445
  }
545
446
  /**
546
447
  * Create a preview string from data
@@ -573,9 +474,9 @@ export class AgentExecutionMonitorComponent {
573
474
  return `Sub-agent: ${parsed.subAgentName}`;
574
475
  }
575
476
  if (parsed.message)
576
- return parsed.message;
477
+ return this.valueToPreviewString(parsed.message);
577
478
  if (parsed.userMessage)
578
- return parsed.userMessage;
479
+ return this.valueToPreviewString(parsed.userMessage);
579
480
  // Show action results clearly
580
481
  if (parsed.actionResult) {
581
482
  const result = parsed.actionResult;
@@ -587,7 +488,7 @@ export class AgentExecutionMonitorComponent {
587
488
  preview += `Result Code: ${result.resultCode}\n`;
588
489
  }
589
490
  if (result.message) {
590
- preview += `Message: ${result.message}\n`;
491
+ preview += `Message: ${this.valueToPreviewString(result.message)}\n`;
591
492
  }
592
493
  if (result.result) {
593
494
  preview += `Result: ${typeof result.result === 'object' ? JSON.stringify(result.result, null, 2) : result.result}`;
@@ -596,10 +497,7 @@ export class AgentExecutionMonitorComponent {
596
497
  }
597
498
  // Legacy action result format
598
499
  if (parsed.result) {
599
- if (typeof parsed.result === 'object') {
600
- return JSON.stringify(parsed.result, null, 2);
601
- }
602
- return String(parsed.result);
500
+ return this.valueToPreviewString(parsed.result);
603
501
  }
604
502
  // Show prompt results
605
503
  if (parsed.promptResult) {
@@ -609,7 +507,7 @@ export class AgentExecutionMonitorComponent {
609
507
  preview += `Success: ${result.success}\n`;
610
508
  }
611
509
  if (result.content) {
612
- preview += `Content: ${typeof result.content === 'string' ? result.content : JSON.stringify(result.content, null, 2)}`;
510
+ preview += `Content: ${this.valueToPreviewString(result.content)}`;
613
511
  }
614
512
  return preview;
615
513
  }
@@ -663,155 +561,83 @@ export class AgentExecutionMonitorComponent {
663
561
  * Set up live updates for real-time execution
664
562
  */
665
563
  setupLiveUpdates() {
666
- // Handle live streaming data format
667
- if (this.executionData && this.executionData.liveSteps) {
668
- // Only clear and rebuild if we don't have existing data
669
- if (this.executionTree.length === 0) {
670
- this.clearNodeComponents();
671
- this.executionTree = [];
672
- // Then append all steps
673
- this.appendNewLiveSteps(this.executionData.liveSteps);
674
- }
675
- else {
676
- // Just append new steps without clearing
677
- this.appendNewLiveSteps(this.executionData.liveSteps);
678
- }
564
+ console.log('⚡ Setting up live updates');
565
+ // Process initial live steps if any
566
+ if (this.liveSteps && this.liveSteps.length > 0) {
567
+ this.appendNewLiveSteps(this.liveSteps);
679
568
  }
680
- // Set up interval to monitor for changes (if not already set up)
569
+ // Set up interval to check for updates (if not already set up)
681
570
  if (this.mode === 'live' && !this.updateSubscription) {
682
571
  this.updateSubscription = interval(500)
683
572
  .pipe(takeUntil(this.destroy$))
684
573
  .subscribe(() => {
685
- // Check for new live steps and update tree
686
- if (this.executionData && this.executionData.liveSteps) {
687
- // Instead of replacing entire tree, append new nodes
688
- this.appendNewLiveSteps(this.executionData.liveSteps);
689
- this.calculateStats();
690
- }
574
+ // Update current step indicator
691
575
  this.updateCurrentStep();
576
+ // Recalculate stats periodically
577
+ this.calculateStats();
692
578
  });
693
579
  }
694
580
  }
695
- /**
696
- * Convert live steps format to execution tree format
697
- * FIXED: Ensure depth is properly preserved
698
- */
699
- convertLiveStepsToTree(liveSteps) {
700
- const treeNodes = [];
701
- const nodeMap = new Map();
702
- // Sort steps by StepNumber to ensure proper ordering
703
- const sortedSteps = [...liveSteps].sort((a, b) => {
704
- const aStepNum = a.step?.StepNumber || 0;
705
- const bStepNum = b.step?.StepNumber || 0;
706
- return aStepNum - bStepNum;
707
- });
708
- for (const step of sortedSteps) {
709
- // Parse the step name for markdown content
710
- const stepName = step.step?.StepName || 'Processing...';
711
- let name = stepName;
712
- let detailsMarkdown;
713
- // Check if the step name contains markdown (indicated by newlines or markdown syntax)
714
- if (stepName.includes('\n') || stepName.includes('**') || stepName.includes('##')) {
715
- // Split by newline and use first line as name
716
- const lines = stepName.split('\n');
717
- name = lines[0].trim();
718
- // Rest is markdown details
719
- if (lines.length > 1) {
720
- detailsMarkdown = lines.slice(1).join('\n').trim();
721
- }
722
- }
723
- const node = {
724
- id: step.step?.ID || `live-${Date.now()}-${Math.random()}`,
725
- name: name,
726
- type: this.mapStepType(step.executionType || step.step?.StepType),
727
- status: 'running', // Live steps are always running
728
- startTime: step.startTime ? new Date(step.startTime) : new Date(),
729
- agentPath: step.agentHierarchy || [],
730
- expanded: false,
731
- depth: step.depth !== undefined ? step.depth : 0, // Ensure depth is always defined
732
- detailsMarkdown: detailsMarkdown,
733
- detailsExpanded: false
734
- };
735
- // Store in map for hierarchy building
736
- nodeMap.set(node.id, node);
737
- // Handle hierarchy by depth
738
- if (step.depth === 0) {
739
- treeNodes.push(node);
740
- }
741
- else {
742
- // Find parent based on depth and agent hierarchy
743
- let parent;
744
- // Look for a node at depth-1 that could be the parent
745
- nodeMap.forEach((candidate) => {
746
- if (candidate.depth === step.depth - 1) {
747
- parent = candidate;
748
- }
749
- });
750
- if (parent) {
751
- if (!parent.children) {
752
- parent.children = [];
753
- }
754
- parent.children.push(node);
755
- }
756
- }
757
- nodeMap.set(node.id, node);
758
- }
759
- return treeNodes;
760
- }
761
581
  /**
762
582
  * Update current step indicator
763
583
  */
764
584
  updateCurrentStep() {
765
- // Find the first running step
766
- const findRunning = (nodes) => {
767
- for (const node of nodes) {
768
- if (node.status === 'running')
769
- return node;
770
- if (node.children) {
771
- const child = findRunning(node.children);
772
- if (child)
773
- return child;
774
- }
585
+ // Find the first running step from all rendered components
586
+ let runningStep = null;
587
+ this.nodeComponentMap.forEach((componentRef, stepId) => {
588
+ const step = componentRef.instance.step;
589
+ if (step && step.Status === 'Running' && !runningStep) {
590
+ runningStep = step;
775
591
  }
776
- return null;
777
- };
778
- this.currentStep = findRunning(this.executionTree);
592
+ });
593
+ this.currentStep = runningStep;
779
594
  this.cdr.markForCheck();
780
595
  }
781
596
  /**
782
597
  * Mark previously new nodes as completed
783
598
  */
784
599
  markPreviouslyNewNodesAsComplete() {
785
- // Only mark nodes that were previously marked as newly added
786
- for (const nodeId of this.newlyAddedNodeIds) {
787
- const node = this.findNodeById(this.executionTree, nodeId);
788
- if (node && node.status === 'running') {
789
- node.status = 'completed';
790
- // Update the component if it exists
791
- const componentRef = this.nodeComponentMap.get(nodeId);
792
- if (componentRef) {
793
- // Create a new object to ensure change detection
794
- componentRef.instance.node = { ...node };
795
- componentRef.changeDetectorRef.detectChanges();
796
- }
600
+ // Mark ALL running steps as completed (not just previously new ones)
601
+ // This ensures only the latest step shows as running
602
+ this.nodeComponentMap.forEach((componentRef, stepId) => {
603
+ const step = componentRef.instance.step;
604
+ if (step.Status === 'Running') {
605
+ // Create a copy with updated status
606
+ const updatedStep = Object.assign({}, step, { Status: 'Completed' });
607
+ componentRef.instance.step = updatedStep;
608
+ componentRef.changeDetectorRef.detectChanges();
797
609
  }
798
- }
610
+ });
799
611
  }
800
612
  /**
801
- * Find a node by ID in the tree
613
+ * Ensure only the last step in the execution order shows as running
802
614
  */
803
- findNodeById(nodes, id) {
804
- for (const node of nodes) {
805
- if (node.id === id) {
806
- return node;
615
+ ensureOnlyLastStepIsRunning() {
616
+ // Find the last step by step number
617
+ let lastRunningStep = null;
618
+ this.nodeComponentMap.forEach((componentRef, stepId) => {
619
+ const step = componentRef.instance.step;
620
+ if (step.Status === 'Running') {
621
+ const stepNumber = step.StepNumber || 0;
622
+ if (!lastRunningStep || stepNumber > lastRunningStep.stepNumber) {
623
+ lastRunningStep = { stepId, stepNumber, componentRef };
624
+ }
807
625
  }
808
- if (node.children) {
809
- const found = this.findNodeById(node.children, id);
810
- if (found)
811
- return found;
626
+ });
627
+ // Set override display status for all running steps
628
+ this.nodeComponentMap.forEach((componentRef, stepId) => {
629
+ const step = componentRef.instance.step;
630
+ if (step.Status === 'Running') {
631
+ // Override display to show as completed, except for the last one
632
+ if (lastRunningStep && stepId === lastRunningStep.stepId) {
633
+ componentRef.instance.overrideDisplayStatus = 'Running';
634
+ }
635
+ else {
636
+ componentRef.instance.overrideDisplayStatus = 'Completed';
637
+ }
638
+ componentRef.changeDetectorRef.detectChanges();
812
639
  }
813
- }
814
- return null;
640
+ });
815
641
  }
816
642
  /**
817
643
  * Calculate execution statistics
@@ -827,85 +653,47 @@ export class AgentExecutionMonitorComponent {
827
653
  totalDuration: 0,
828
654
  totalPrompts: 0
829
655
  };
830
- // Check if we have agent run data with token information
831
- if (this.executionData?.agentRun) {
832
- // Use token data from agent run if available
833
- this.stats.totalTokens = this.executionData.agentRun.TotalTokensUsed || 0;
834
- this.stats.totalCost = this.executionData.agentRun.TotalCost || 0;
835
- }
836
- // Calculate stats from execution tree nodes if we have them
837
- if (this.executionTree.length > 0) {
838
- const processNode = (node) => {
839
- this.stats.totalSteps++;
840
- if (node.status === 'completed')
841
- this.stats.completedSteps++;
842
- if (node.status === 'failed')
843
- this.stats.failedSteps++;
844
- // Only accumulate tokens if we don't have agent run data
845
- if (!this.executionData?.agentRun) {
846
- if (node.tokensUsed)
847
- this.stats.totalTokens += node.tokensUsed;
848
- if (node.cost)
849
- this.stats.totalCost += node.cost;
850
- }
851
- if (node.duration)
852
- this.stats.totalDuration += node.duration;
853
- this.stats.stepsByType[node.type] = (this.stats.stepsByType[node.type] || 0) + 1;
854
- // Count prompts
855
- if (node.type === 'prompt' || node.promptCount) {
856
- this.stats.totalPrompts += node.promptCount || 1;
857
- }
858
- if (node.children) {
859
- node.children.forEach(child => processNode(child));
860
- }
861
- };
862
- this.executionTree.forEach(node => processNode(node));
863
- }
864
- else if (this.executionData?.executionTree) {
865
- // If we don't have processed tree nodes but have raw execution data, calculate from raw data
866
- console.log('📈 Calculating stats from raw execution data since tree is empty');
867
- const processRawNode = (node) => {
868
- this.stats.totalSteps++;
869
- const status = this.mapStepStatus(node.step?.Status);
870
- if (status === 'completed')
871
- this.stats.completedSteps++;
872
- if (status === 'failed')
873
- this.stats.failedSteps++;
874
- if (node.durationMs)
875
- this.stats.totalDuration += node.durationMs;
876
- const nodeType = this.mapStepType(node.executionType || node.step?.StepType);
877
- this.stats.stepsByType[nodeType] = (this.stats.stepsByType[nodeType] || 0) + 1;
878
- if (nodeType === 'prompt') {
879
- this.stats.totalPrompts += 1;
880
- }
881
- if (node.children) {
882
- node.children.forEach((child) => processRawNode(child));
883
- }
884
- };
885
- this.executionData.executionTree.forEach((node) => processRawNode(node));
656
+ // Use token data from agent run if available
657
+ if (this.agentRun) {
658
+ this.stats.totalTokens = this.agentRun.TotalTokensUsed || 0;
659
+ this.stats.totalCost = this.agentRun.TotalCost || 0;
660
+ // Calculate duration
661
+ if (this.agentRun.StartedAt && this.agentRun.CompletedAt) {
662
+ this.stats.totalDuration = new Date(this.agentRun.CompletedAt).getTime() -
663
+ new Date(this.agentRun.StartedAt).getTime();
664
+ }
665
+ // Count steps recursively
666
+ if (this.agentRun.Steps) {
667
+ this.countSteps(this.agentRun.Steps);
668
+ }
886
669
  }
887
670
  console.log('📈 Stats calculated:', {
888
671
  totalSteps: this.stats.totalSteps,
889
- treeLength: this.executionTree.length,
890
- executionDataExists: !!this.executionData,
891
- hasRawExecutionTree: !!this.executionData?.executionTree
672
+ totalTokens: this.stats.totalTokens,
673
+ totalCost: this.stats.totalCost
892
674
  });
893
675
  }
894
676
  /**
895
- * Toggle node expansion
677
+ * Count steps recursively
896
678
  */
897
- toggleNode(node) {
898
- const nodeInTree = this.findNodeById(this.executionTree, node.id);
899
- if (nodeInTree && this.hasExpandableContent(nodeInTree)) {
900
- nodeInTree.expanded = !nodeInTree.expanded;
901
- this.userHasInteracted = true;
902
- // Update the component's node reference to trigger change detection
903
- const componentRef = this.nodeComponentMap.get(node.id);
904
- if (componentRef) {
905
- componentRef.instance.node = { ...nodeInTree };
906
- componentRef.changeDetectorRef.detectChanges();
679
+ countSteps(steps) {
680
+ for (const step of steps) {
681
+ this.stats.totalSteps++;
682
+ // Map to display types for consistency
683
+ const displayType = this.getStepTypeClass(step.StepType);
684
+ this.stats.stepsByType[displayType] = (this.stats.stepsByType[displayType] || 0) + 1;
685
+ if (step.Status === 'Completed')
686
+ this.stats.completedSteps++;
687
+ if (step.Status === 'Failed' || step.Status === 'Cancelled')
688
+ this.stats.failedSteps++;
689
+ // Count prompts
690
+ if (step.StepType === 'Prompt') {
691
+ this.stats.totalPrompts++;
692
+ }
693
+ // Recurse for sub-agents
694
+ if (step.StepType === 'Sub-Agent' && step.SubAgentRun?.Steps) {
695
+ this.countSteps(step.SubAgentRun.Steps);
907
696
  }
908
- this.cdr.markForCheck();
909
697
  }
910
698
  }
911
699
  /**
@@ -998,6 +786,24 @@ export class AgentExecutionMonitorComponent {
998
786
  getStepTypes() {
999
787
  return Object.keys(this.stats.stepsByType).sort();
1000
788
  }
789
+ /**
790
+ * Pluralize step type based on count
791
+ */
792
+ pluralizeStepType(type, count) {
793
+ if (count === 1) {
794
+ return type;
795
+ }
796
+ // Handle special cases
797
+ switch (type.toLowerCase()) {
798
+ case 'analysis':
799
+ return 'Analyses';
800
+ case 'summary':
801
+ return 'Summaries';
802
+ default:
803
+ // Default pluralization - just add 's'
804
+ return type + 's';
805
+ }
806
+ }
1001
807
  /**
1002
808
  * Format markdown content for display
1003
809
  */
@@ -1036,136 +842,53 @@ export class AgentExecutionMonitorComponent {
1036
842
  }
1037
843
  /**
1038
844
  * Append new live steps without re-rendering entire tree
1039
- * FIXED: Ensure depth is properly preserved when creating nodes
1040
845
  */
1041
- appendNewLiveSteps(liveSteps) {
1042
- if (!liveSteps || liveSteps.length === 0)
1043
- return;
1044
- // If view is not initialized yet, wait for it
1045
- if (!this.viewInitialized || !this.executionNodesContainer) {
846
+ appendNewLiveSteps(newSteps) {
847
+ if (!newSteps || newSteps.length === 0 || !this.viewInitialized || !this.executionNodesContainer) {
1046
848
  return;
1047
849
  }
1048
- // Sort live steps by stepNumber to ensure proper ordering
1049
- const sortedSteps = [...liveSteps].sort((a, b) => {
1050
- const aStepNum = a.step?.StepNumber || 0;
1051
- const bStepNum = b.step?.StepNumber || 0;
1052
- return aStepNum - bStepNum;
1053
- });
1054
- // Create a map of existing nodes by ID for quick lookup
1055
- const nodeMap = new Map();
1056
- this.buildNodeMap(this.executionTree, nodeMap);
1057
- // First pass: identify which steps are truly NEW (not in processedStepIds)
1058
- const trulyNewStepIds = new Set();
1059
- for (const step of sortedSteps) {
1060
- const stepId = step.step?.ID || this.generateStepId(step);
1061
- if (!this.processedStepIds.has(stepId)) {
1062
- trulyNewStepIds.add(stepId);
1063
- }
1064
- }
1065
- // Only mark previously new nodes as complete if we have truly new steps
1066
- if (trulyNewStepIds.size > 0 && this.newlyAddedNodeIds.size > 0) {
850
+ console.log('📝 Appending', newSteps.length, 'live steps');
851
+ // Sort steps by StepNumber
852
+ const sortedSteps = [...newSteps].sort((a, b) => (a.StepNumber || 0) - (b.StepNumber || 0));
853
+ // Mark previously new steps as completed
854
+ if (this.newlyAddedNodeIds.size > 0) {
1067
855
  this.markPreviouslyNewNodesAsComplete();
1068
- // Clear the set for new additions
1069
856
  this.newlyAddedNodeIds.clear();
1070
857
  }
1071
- let hasNewNodes = false;
858
+ let hasNewSteps = false;
1072
859
  for (const step of sortedSteps) {
1073
- // Generate a stable ID based on step content to avoid duplicates
1074
- const stepId = step.step?.ID || this.generateStepId(step);
1075
- // Skip if we've already processed this exact step
1076
- if (this.processedStepIds.has(stepId)) {
1077
- continue;
1078
- }
1079
- // Mark as processed
1080
- this.processedStepIds.add(stepId);
1081
- // Check if this node already exists
1082
- const existingNode = nodeMap.get(stepId);
1083
- if (existingNode) {
1084
- // Update existing node in the component
1085
- const componentRef = this.nodeComponentMap.get(stepId);
1086
- if (componentRef) {
1087
- // Preserve current UI state
1088
- const currentExpanded = existingNode.expanded;
1089
- const currentDetailsExpanded = existingNode.detailsExpanded;
1090
- const updatedStatus = step.step?.Status ?
1091
- this.mapStepStatus(step.step.Status) :
1092
- (step.endTime || step.outputData ? 'completed' : existingNode.status);
1093
- if (existingNode.status !== updatedStatus) {
1094
- existingNode.status = updatedStatus;
1095
- }
1096
- if (step.endTime && !existingNode.endTime) {
1097
- existingNode.endTime = new Date(step.endTime);
1098
- existingNode.duration = step.durationMs;
1099
- }
1100
- // Update output data if available
1101
- if (step.outputData && !existingNode.outputPreview) {
1102
- existingNode.outputPreview = this.createPreview(step.outputData);
1103
- existingNode.tokensUsed = this.extractTokens(step.outputData);
1104
- existingNode.cost = this.extractCost(step.outputData);
1105
- }
1106
- // Restore UI state
1107
- existingNode.expanded = currentExpanded;
1108
- existingNode.detailsExpanded = currentDetailsExpanded;
1109
- // Update the component's node reference
1110
- // Important: We need to update the node object directly to ensure
1111
- // Angular detects changes to style bindings that depend on node.depth
1112
- const nodeInstance = componentRef.instance.node;
1113
- Object.assign(nodeInstance, existingNode);
1114
- // Mark for check to ensure Angular updates the view
1115
- componentRef.changeDetectorRef.markForCheck();
860
+ // Skip if already processed
861
+ if (this.processedStepIds.has(step.ID)) {
862
+ // Update existing step if status changed
863
+ const componentRef = this.nodeComponentMap.get(step.ID);
864
+ if (componentRef && componentRef.instance.step.Status !== step.Status) {
865
+ componentRef.instance.step = step;
1116
866
  componentRef.changeDetectorRef.detectChanges();
1117
867
  }
868
+ continue;
1118
869
  }
1119
- else {
1120
- // Create new node with proper depth
1121
- const newNode = this.createNodeFromLiveStep(step);
1122
- // Ensure depth is set from the step data
1123
- if (step.depth !== undefined) {
1124
- newNode.depth = step.depth;
1125
- }
1126
- if (newNode.depth === 0) {
1127
- // Add to tree and create component
1128
- this.executionTree.push(newNode);
1129
- this.createNodeComponent(newNode, this.executionNodesContainer);
1130
- }
1131
- else {
1132
- // Find parent and add as child
1133
- const parentNode = this.findParentNode(this.executionTree, newNode);
1134
- if (parentNode) {
1135
- if (!parentNode.children) {
1136
- parentNode.children = [];
1137
- }
1138
- parentNode.children.push(newNode);
1139
- // Update parent component to show new child
1140
- const parentComponentRef = this.nodeComponentMap.get(parentNode.id);
1141
- if (parentComponentRef) {
1142
- // Update the parent node object directly
1143
- const parentNodeInstance = parentComponentRef.instance.node;
1144
- Object.assign(parentNodeInstance, parentNode);
1145
- parentComponentRef.changeDetectorRef.markForCheck();
1146
- parentComponentRef.changeDetectorRef.detectChanges();
1147
- }
1148
- }
1149
- else {
1150
- // If no parent found, add as root but preserve depth
1151
- this.executionTree.push(newNode);
1152
- this.createNodeComponent(newNode, this.executionNodesContainer);
1153
- }
1154
- }
1155
- hasNewNodes = true;
1156
- // Only track as newly added if it's truly new
1157
- if (trulyNewStepIds.has(stepId)) {
1158
- this.newlyAddedNodeIds.add(newNode.id);
1159
- }
870
+ // Mark as processed and newly added
871
+ this.processedStepIds.add(step.ID);
872
+ this.newlyAddedNodeIds.add(step.ID);
873
+ hasNewSteps = true;
874
+ // Determine depth based on parent hierarchy
875
+ const depth = this.calculateStepDepth(step);
876
+ const agentPath = this.buildAgentPath(step);
877
+ // Create component for new step
878
+ this.createStepComponent(step, depth, agentPath);
879
+ // Handle sub-agent steps recursively
880
+ if (step.StepType === 'Sub-Agent' && step.SubAgentRun?.Steps) {
881
+ const subAgentPath = [...agentPath, step.SubAgentRun.Agent || 'Sub-Agent'];
882
+ this.renderSteps(step.SubAgentRun.Steps, depth + 1, subAgentPath);
1160
883
  }
1161
884
  }
1162
- if (hasNewNodes) {
1163
- // Update current step
885
+ if (hasNewSteps) {
886
+ // After processing all new steps, ensure only the last one shows as running
887
+ this.ensureOnlyLastStepIsRunning();
1164
888
  this.updateCurrentStep();
1165
- // Recalculate stats
1166
889
  this.calculateStats();
1167
- // Trigger change detection
1168
- this.cdr.markForCheck();
890
+ // Force change detection to ensure new components are rendered
891
+ this.cdr.detectChanges();
1169
892
  // Auto-scroll if user hasn't interacted
1170
893
  setTimeout(() => {
1171
894
  this.autoScrollToBottom();
@@ -1173,220 +896,20 @@ export class AgentExecutionMonitorComponent {
1173
896
  }
1174
897
  }
1175
898
  /**
1176
- * Generate a stable ID for a step based on its content
1177
- */
1178
- generateStepId(step) {
1179
- // Create a stable ID based on step properties that should be unique
1180
- const parts = [
1181
- step.executionType || 'unknown',
1182
- step.depth || 0,
1183
- step.step?.StepName || '',
1184
- step.startTime || Date.now()
1185
- ];
1186
- return `live-${parts.join('-').replace(/[^a-zA-Z0-9-]/g, '')}`;
1187
- }
1188
- /**
1189
- * Find parent node based on depth
1190
- */
1191
- findParentNode(nodes, childNode) {
1192
- // We need to find the most recent node at the parent depth
1193
- // that could logically be this node's parent
1194
- let potentialParent = null;
1195
- const findParentRecursive = (searchNodes) => {
1196
- // Process nodes in reverse order to find the most recent one
1197
- for (let i = searchNodes.length - 1; i >= 0; i--) {
1198
- const node = searchNodes[i];
1199
- // Check if this node could be the parent based on depth
1200
- if (node.depth === childNode.depth - 1) {
1201
- // Special handling for sub-agent execution steps
1202
- if (childNode.agentPath && childNode.agentPath.length > node.agentPath.length) {
1203
- // This child belongs to a sub-agent
1204
- // Look for the most recent sub-agent delegation node
1205
- if (node.type === 'sub-agent' || node.name.includes('Delegating to')) {
1206
- return node;
1207
- }
1208
- }
1209
- // Keep track of the most recent node at the correct depth
1210
- if (!potentialParent) {
1211
- potentialParent = node;
1212
- }
1213
- }
1214
- // Search children recursively
1215
- if (node.children && node.children.length > 0) {
1216
- const foundInChildren = findParentRecursive(node.children);
1217
- if (foundInChildren)
1218
- return foundInChildren;
1219
- }
1220
- }
1221
- return null;
1222
- };
1223
- const found = findParentRecursive(nodes);
1224
- return found || potentialParent;
1225
- }
1226
- /**
1227
- * Filter nodes based on display mode
1228
- */
1229
- filterNodesForMode(nodes, mode) {
1230
- console.log('🔍 Filtering nodes for mode:', mode, 'from', nodes.length, 'nodes');
1231
- const filteredNodes = nodes.filter(node => {
1232
- // If no displayMode is set, default to showing in both modes
1233
- if (!node.displayMode) {
1234
- return true;
1235
- }
1236
- // Show if displayMode is 'both' or matches current mode
1237
- if (node.displayMode === 'both' || node.displayMode === mode) {
1238
- // Filter children recursively
1239
- if (node.children) {
1240
- node.children = this.filterNodesForMode(node.children, mode);
1241
- }
1242
- return true;
1243
- }
1244
- console.log('📄 Filtering out node in', mode, 'mode:', node.name, 'displayMode:', node.displayMode);
1245
- return false;
1246
- });
1247
- console.log('✅ Filtered to', filteredNodes.length, 'nodes for mode:', mode);
1248
- return filteredNodes;
1249
- }
1250
- /**
1251
- * Build a map of nodes by ID for quick lookup
1252
- */
1253
- buildNodeMap(nodes, map) {
1254
- for (const node of nodes) {
1255
- map.set(node.id, node);
1256
- if (node.children) {
1257
- this.buildNodeMap(node.children, map);
1258
- }
1259
- }
1260
- }
1261
- /**
1262
- * Create a single node from a live step
1263
- * FIXED: Ensure depth is properly set from step data
899
+ * Calculate step depth based on parent hierarchy
1264
900
  */
1265
- createNodeFromLiveStep(step) {
1266
- const stepName = step.step?.StepName || 'Processing...';
1267
- let name = stepName;
1268
- let detailsMarkdown;
1269
- // Check if the step name contains markdown
1270
- if (stepName.includes('\n') || stepName.includes('**') || stepName.includes('##')) {
1271
- const lines = stepName.split('\n');
1272
- name = lines[0].trim();
1273
- if (lines.length > 1) {
1274
- detailsMarkdown = lines.slice(1).join('\n').trim();
1275
- }
1276
- }
1277
- // For live steps, always start as running
1278
- // The status will be updated to completed in markPreviouslyNewNodesAsComplete
1279
- let status = 'running';
1280
- if (step.step?.Status) {
1281
- // Only use explicit status if it's failed
1282
- const mappedStatus = this.mapStepStatus(step.step.Status);
1283
- if (mappedStatus === 'failed') {
1284
- status = 'failed';
1285
- }
1286
- }
1287
- // Determine display mode based on the step content
1288
- let displayMode = 'both';
1289
- // Progress messages that should only show in live mode
1290
- if (name.includes('Executing') && name.includes('action') && !name.startsWith('Execute Action:')) {
1291
- displayMode = 'live';
1292
- }
1293
- else if (name.includes("Executing agent's initial prompt")) {
1294
- displayMode = 'live';
1295
- }
1296
- else if (name.includes('Re-executing agent prompt with additional context')) {
1297
- displayMode = 'live';
1298
- }
1299
- // Check displayMode from metadata if available
1300
- if (step.metadata?.displayMode) {
1301
- displayMode = step.metadata.displayMode;
1302
- }
1303
- // Clear metadata storage before creating previews
1304
- this.lastParsedAgentData = null;
1305
- this.lastParsedActionData = null;
1306
- // Create previews which will populate metadata if available
1307
- const inputPreview = this.createPreview(step.inputData);
1308
- const outputPreview = this.createPreview(step.outputData);
1309
- const nodeType = this.mapStepType(step.executionType || step.step?.StepType);
1310
- // IMPORTANT: Use the depth from step data, ensure it's always defined
1311
- const nodeDepth = step.depth !== undefined ? step.depth : 0;
1312
- const node = {
1313
- id: step.step?.ID || this.generateStepId(step),
1314
- name: name,
1315
- type: nodeType,
1316
- status: status,
1317
- startTime: step.startTime ? new Date(step.startTime) : new Date(),
1318
- endTime: step.endTime ? new Date(step.endTime) : undefined,
1319
- duration: step.durationMs,
1320
- agentPath: step.agentHierarchy || [],
1321
- expanded: false,
1322
- depth: nodeDepth, // Use the extracted depth
1323
- detailsMarkdown: detailsMarkdown,
1324
- detailsExpanded: false,
1325
- inputPreview: inputPreview,
1326
- outputPreview: outputPreview,
1327
- tokensUsed: this.extractTokens(step.outputData),
1328
- cost: this.extractCost(step.outputData),
1329
- displayMode: displayMode
1330
- };
1331
- // Add agent metadata if this is a sub-agent node
1332
- if (nodeType === 'sub-agent' && this.lastParsedAgentData) {
1333
- node.agentName = this.lastParsedAgentData.agentName;
1334
- node.agentId = this.lastParsedAgentData.agentId;
1335
- node.agentIconClass = this.lastParsedAgentData.agentIconClass;
1336
- node.agentLogoURL = this.lastParsedAgentData.agentLogoURL;
1337
- }
1338
- // Add action metadata if this is an action node
1339
- if (nodeType === 'action' && this.lastParsedActionData) {
1340
- node.actionName = this.lastParsedActionData.actionName;
1341
- node.actionId = this.lastParsedActionData.actionId;
1342
- node.actionIconClass = this.lastParsedActionData.actionIconClass;
1343
- }
1344
- return node;
1345
- }
1346
- /**
1347
- * Collect all node IDs in the tree
1348
- */
1349
- collectNodeIds(nodes, ids) {
1350
- for (const node of nodes) {
1351
- ids.add(node.id);
1352
- if (node.children) {
1353
- this.collectNodeIds(node.children, ids);
1354
- }
1355
- }
901
+ calculateStepDepth(step) {
902
+ // For now, return 0 for top-level steps
903
+ // In live mode, depth information should come from the streaming data
904
+ return 0;
1356
905
  }
1357
906
  /**
1358
- * Preserve expanded state of all nodes
907
+ * Build agent path for a step
1359
908
  */
1360
- preserveExpandedState(nodes, expandedState, detailsExpandedState) {
1361
- for (const node of nodes) {
1362
- if (node.expanded !== undefined) {
1363
- expandedState.set(node.id, node.expanded);
1364
- }
1365
- if (node.detailsExpanded !== undefined) {
1366
- detailsExpandedState.set(node.id, node.detailsExpanded);
1367
- }
1368
- if (node.children) {
1369
- this.preserveExpandedState(node.children, expandedState, detailsExpandedState);
1370
- }
1371
- }
1372
- }
1373
- /**
1374
- * Restore expanded state of all nodes
1375
- */
1376
- restoreExpandedState(nodes, expandedState, detailsExpandedState) {
1377
- for (const node of nodes) {
1378
- const expanded = expandedState.get(node.id);
1379
- if (expanded !== undefined) {
1380
- node.expanded = expanded;
1381
- }
1382
- const detailsExpanded = detailsExpandedState.get(node.id);
1383
- if (detailsExpanded !== undefined) {
1384
- node.detailsExpanded = detailsExpanded;
1385
- }
1386
- if (node.children) {
1387
- this.restoreExpandedState(node.children, expandedState, detailsExpandedState);
1388
- }
1389
- }
909
+ buildAgentPath(step) {
910
+ // In live mode, agent path should come from streaming data
911
+ // For now, return empty array
912
+ return [];
1390
913
  }
1391
914
  /**
1392
915
  * Check if execution is complete
@@ -1399,81 +922,29 @@ export class AgentExecutionMonitorComponent {
1399
922
  * Handle view run button click
1400
923
  */
1401
924
  onViewRunClick() {
1402
- if (this.runId) {
1403
- this.viewRunClick.emit({ runId: this.runId, runType: this.runType });
1404
- }
1405
- }
1406
- /**
1407
- * Append a node to its parent based on depth
1408
- */
1409
- appendToParent(nodes, newNode) {
1410
- for (const node of nodes) {
1411
- if (node.depth === newNode.depth - 1) {
1412
- if (!node.children) {
1413
- node.children = [];
1414
- }
1415
- node.children.push(newNode);
1416
- return true;
1417
- }
1418
- if (node.children && this.appendToParent(node.children, newNode)) {
1419
- return true;
1420
- }
925
+ if (this.agentRun) {
926
+ this.viewRunClick.emit({ runId: this.agentRun.ID, runType: 'agent' });
1421
927
  }
1422
- return false;
1423
928
  }
1424
929
  /**
1425
- * Expands nodes in the execution tree that have children
1426
- * Called when auto-expand flag is detected or execution completes
930
+ * Expands all nodes that have sub-agent children
1427
931
  */
1428
932
  expandAllNodes() {
1429
- if (!this.executionTree || this.executionTree.length === 0) {
1430
- return;
1431
- }
1432
- console.log('🔄 Starting auto-expansion of nodes with children', this.executionTree.length);
1433
- // Recursively expand only nodes that have children
1434
- const expandNodeRecursively = (nodes) => {
1435
- for (const node of nodes) {
1436
- // Only expand nodes that have children (sub-nodes)
1437
- const hasChildren = node.children && node.children.length > 0;
1438
- if (hasChildren) {
1439
- node.expanded = true;
1440
- console.log(`Expanding node with children: ${node.name}`, {
1441
- childCount: node.children?.length,
1442
- nodeType: node.type
1443
- });
1444
- // Update the corresponding component if it exists
1445
- const componentRef = this.nodeComponentMap.get(node.id);
1446
- if (componentRef) {
1447
- // Create a new object to ensure change detection
1448
- const updatedNode = { ...node, expanded: true };
1449
- componentRef.instance.node = updatedNode;
1450
- componentRef.changeDetectorRef.markForCheck();
1451
- componentRef.changeDetectorRef.detectChanges();
1452
- console.log(`Updated component for node: ${node.name}`);
1453
- }
1454
- else {
1455
- console.log(`No component found for node: ${node.name}`);
1456
- }
1457
- // Expand children recursively
1458
- expandNodeRecursively(node.children);
1459
- }
1460
- else {
1461
- console.log(`Skipping node without children: ${node.name}`, {
1462
- hasExpandableContent: this.hasExpandableContent(node),
1463
- hasInputPreview: !!node.inputPreview,
1464
- hasOutputPreview: !!node.outputPreview,
1465
- hasError: !!node.error,
1466
- hasDetailsMarkdown: !!node.detailsMarkdown
1467
- });
1468
- }
933
+ console.log('🔄 Auto-expanding nodes with sub-agents');
934
+ // Expand all steps that have sub-agent runs
935
+ this.nodeComponentMap.forEach((componentRef, stepId) => {
936
+ const step = componentRef.instance.step;
937
+ if (step.StepType === 'Sub-Agent' && step.SubAgentRun?.Steps?.length) {
938
+ this.expandedStates.set(stepId, true);
939
+ componentRef.instance.expanded = true;
940
+ componentRef.changeDetectorRef.detectChanges();
941
+ console.log(`Expanded sub-agent: ${step.StepName}`);
1469
942
  }
1470
- };
1471
- expandNodeRecursively(this.executionTree);
1472
- // Trigger change detection for the entire component
943
+ });
944
+ // Trigger change detection
1473
945
  this.cdr.markForCheck();
1474
946
  this.cdr.detectChanges();
1475
- console.log('✅ Completed auto-expansion of nodes with children');
1476
- // Scroll to top after expansion to show the full tree
947
+ // Scroll to top after expansion
1477
948
  setTimeout(() => {
1478
949
  this.scrollToTop();
1479
950
  }, 100);
@@ -1491,16 +962,10 @@ export class AgentExecutionMonitorComponent {
1491
962
  }
1492
963
  }
1493
964
  /**
1494
- * Check if node has expandable content
965
+ * Check if there is content to display
1495
966
  */
1496
- hasExpandableContent(node) {
1497
- return !!(node.children && node.children.length > 0) ||
1498
- node.type === 'sub-agent' ||
1499
- node.type === 'action' ||
1500
- !!node.inputPreview ||
1501
- !!node.outputPreview ||
1502
- !!node.error ||
1503
- !!node.detailsMarkdown;
967
+ hasContent() {
968
+ return this.nodeComponentMap.size > 0;
1504
969
  }
1505
970
  static { this.ɵfac = function AgentExecutionMonitorComponent_Factory(t) { return new (t || AgentExecutionMonitorComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); }; }
1506
971
  static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AgentExecutionMonitorComponent, selectors: [["mj-agent-execution-monitor"]], viewQuery: function AgentExecutionMonitorComponent_Query(rf, ctx) { if (rf & 1) {
@@ -1510,7 +975,7 @@ export class AgentExecutionMonitorComponent {
1510
975
  let _t;
1511
976
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.executionTreeContainer = _t.first);
1512
977
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.executionNodesContainer = _t.first);
1513
- } }, inputs: { mode: "mode", executionData: "executionData", autoExpand: "autoExpand", runId: "runId", runType: "runType" }, outputs: { viewRunClick: "viewRunClick" }, standalone: true, features: [i0.ɵɵNgOnChangesFeature, i0.ɵɵStandaloneFeature], decls: 39, vars: 16, consts: [["executionTreeContainer", ""], ["executionNodesContainer", ""], [1, "execution-monitor"], [1, "monitor-header"], [1, "header-title"], [1, "fa-solid", "fa-diagram-project"], [1, "live-indicator"], [1, "current-status"], [1, "view-run-btn"], [1, "execution-tree", 3, "scroll", "click"], [1, "nodes-container"], [1, "empty-state"], [1, "monitor-footer"], [1, "stats-grid"], [1, "stat-item"], [1, "stat-label"], [1, "stat-value"], [1, "failed-count"], [1, "step-types"], [1, "pulse"], [1, "status-label"], [1, "agent-path"], [1, "step-name"], [1, "view-run-btn", 3, "click"], [1, "fa-solid", "fa-external-link-alt"], [1, "fa-solid", "fa-hourglass-start"], [1, "type-badge"]], template: function AgentExecutionMonitorComponent_Template(rf, ctx) { if (rf & 1) {
978
+ } }, inputs: { mode: "mode", agentRun: "agentRun", liveSteps: "liveSteps", autoExpand: "autoExpand", runId: "runId", runType: "runType" }, outputs: { viewRunClick: "viewRunClick" }, features: [i0.ɵɵNgOnChangesFeature], decls: 39, vars: 16, consts: [["executionTreeContainer", ""], ["executionNodesContainer", ""], [1, "execution-monitor"], [1, "monitor-header"], [1, "header-title"], [1, "fa-solid", "fa-diagram-project"], [1, "live-indicator"], [1, "current-status"], [1, "view-run-btn"], [1, "execution-tree", 3, "scroll", "click"], [1, "nodes-container"], [1, "empty-state"], [1, "monitor-footer"], [1, "stats-grid"], [1, "stat-item"], [1, "stat-label"], [1, "stat-value"], [1, "failed-count"], [1, "step-types"], [1, "pulse"], [1, "status-label"], [1, "step-name"], [1, "view-run-btn", 3, "click"], [1, "fa-solid", "fa-external-link-alt"], [1, "fa-solid", "fa-hourglass-start"], [1, "type-badge"]], template: function AgentExecutionMonitorComponent_Template(rf, ctx) { if (rf & 1) {
1514
979
  const _r1 = i0.ɵɵgetCurrentView();
1515
980
  i0.ɵɵelementStart(0, "div", 2)(1, "div", 3)(2, "div", 4);
1516
981
  i0.ɵɵelement(3, "i", 5);
@@ -1519,13 +984,13 @@ export class AgentExecutionMonitorComponent {
1519
984
  i0.ɵɵelementEnd();
1520
985
  i0.ɵɵtemplate(6, AgentExecutionMonitorComponent_Conditional_6_Template, 3, 0, "span", 6);
1521
986
  i0.ɵɵelementEnd();
1522
- i0.ɵɵtemplate(7, AgentExecutionMonitorComponent_Conditional_7_Template, 7, 2, "div", 7)(8, AgentExecutionMonitorComponent_Conditional_8_Template, 3, 1, "button", 8);
987
+ i0.ɵɵtemplate(7, AgentExecutionMonitorComponent_Conditional_7_Template, 5, 1, "div", 7)(8, AgentExecutionMonitorComponent_Conditional_8_Template, 3, 1, "button", 8);
1523
988
  i0.ɵɵelementEnd();
1524
989
  i0.ɵɵelementStart(9, "div", 9, 0);
1525
990
  i0.ɵɵlistener("scroll", function AgentExecutionMonitorComponent_Template_div_scroll_9_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onScroll($event)); })("click", function AgentExecutionMonitorComponent_Template_div_click_9_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onUserInteraction()); });
1526
- i0.ɵɵelementStart(11, "div", 10, 1);
991
+ i0.ɵɵelement(11, "div", 10, 1);
1527
992
  i0.ɵɵtemplate(13, AgentExecutionMonitorComponent_Conditional_13_Template, 4, 0, "div", 11);
1528
- i0.ɵɵelementEnd()();
993
+ i0.ɵɵelementEnd();
1529
994
  i0.ɵɵelementStart(14, "div", 12)(15, "div", 13)(16, "div", 14)(17, "span", 15);
1530
995
  i0.ɵɵtext(18, "Steps");
1531
996
  i0.ɵɵelementEnd();
@@ -1562,11 +1027,11 @@ export class AgentExecutionMonitorComponent {
1562
1027
  i0.ɵɵadvance();
1563
1028
  i0.ɵɵconditional(ctx.currentStep && ctx.mode === "live" ? 7 : -1);
1564
1029
  i0.ɵɵadvance();
1565
- i0.ɵɵconditional(ctx.mode === "historical" && ctx.runId && ctx.isExecutionComplete() ? 8 : -1);
1030
+ i0.ɵɵconditional(ctx.mode === "historical" && ctx.agentRun && ctx.isExecutionComplete() ? 8 : -1);
1566
1031
  i0.ɵɵadvance();
1567
- i0.ɵɵclassProp("has-content", ctx.executionTree.length > 0);
1032
+ i0.ɵɵclassProp("has-content", ctx.hasContent());
1568
1033
  i0.ɵɵadvance(4);
1569
- i0.ɵɵconditional(ctx.executionTree.length === 0 ? 13 : -1);
1034
+ i0.ɵɵconditional(!ctx.agentRun && ctx.liveSteps.length === 0 ? 13 : -1);
1570
1035
  i0.ɵɵadvance(7);
1571
1036
  i0.ɵɵtextInterpolate2(" ", ctx.stats.completedSteps, "/", ctx.stats.totalSteps, " ");
1572
1037
  i0.ɵɵadvance();
@@ -1581,11 +1046,11 @@ export class AgentExecutionMonitorComponent {
1581
1046
  i0.ɵɵconditional(ctx.stats.totalDuration ? 37 : -1);
1582
1047
  i0.ɵɵadvance();
1583
1048
  i0.ɵɵconditional(ctx.getStepTypes().length > 0 ? 38 : -1);
1584
- } }, dependencies: [CommonModule], styles: ["[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n }\n \n .execution-monitor[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n flex: 1;\n background: #fff;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n \n\n .monitor-header[_ngcontent-%COMP%] {\n padding: 16px;\n background: #f8f9fa;\n border-bottom: 1px solid #e0e0e0;\n }\n\n .header-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 16px;\n font-weight: 600;\n color: #1a1a1a;\n margin-bottom: 8px;\n }\n\n .header-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #2196f3;\n }\n\n .live-indicator[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px;\n background: #ff4444;\n color: white;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.5px;\n }\n\n .pulse[_ngcontent-%COMP%] {\n display: inline-block;\n width: 8px;\n height: 8px;\n background: white;\n border-radius: 50%;\n animation: _ngcontent-%COMP%_pulse 2s infinite;\n }\n\n @keyframes _ngcontent-%COMP%_pulse {\n 0% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(0.8); }\n 100% { opacity: 1; transform: scale(1); }\n }\n\n .current-status[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #666;\n }\n\n .status-label[_ngcontent-%COMP%] {\n font-weight: 500;\n }\n\n .agent-path[_ngcontent-%COMP%] {\n color: #999;\n }\n\n .step-name[_ngcontent-%COMP%] {\n color: #1a1a1a;\n font-weight: 500;\n }\n\n \n\n .execution-tree[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n overflow-x: auto;\n padding: 16px;\n min-height: 0;\n }\n \n .nodes-container[_ngcontent-%COMP%] {\n \n\n }\n\n .empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #999;\n text-align: center;\n }\n\n .empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n }\n\n .empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n }\n\n \n\n .tree-node[_ngcontent-%COMP%] {\n margin-bottom: 4px;\n }\n\n \n\n .monitor-footer[_ngcontent-%COMP%] {\n padding: 16px;\n background: #f8f9fa;\n border-top: 1px solid #e0e0e0;\n }\n\n .stats-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));\n gap: 16px;\n margin-bottom: 12px;\n }\n\n .stat-item[_ngcontent-%COMP%] {\n text-align: center;\n }\n\n .stat-label[_ngcontent-%COMP%] {\n display: block;\n font-size: 11px;\n color: #666;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 4px;\n }\n\n .stat-value[_ngcontent-%COMP%] {\n display: block;\n font-size: 16px;\n font-weight: 600;\n color: #1a1a1a;\n }\n\n .failed-count[_ngcontent-%COMP%] {\n color: #f44336;\n font-size: 12px;\n font-weight: normal;\n }\n\n .step-types[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n justify-content: center;\n }\n\n .type-badge[_ngcontent-%COMP%] {\n display: inline-block;\n padding: 4px 8px;\n background: #e3f2fd;\n color: #1976d2;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n }\n \n \n\n .view-run-btn[_ngcontent-%COMP%] {\n background: #2196f3;\n color: white;\n border: none;\n padding: 6px 12px;\n border-radius: 4px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n transition: all 0.2s ease;\n margin-left: auto;\n }\n \n .view-run-btn[_ngcontent-%COMP%]:hover {\n background: #1976d2;\n transform: translateY(-1px);\n box-shadow: 0 2px 4px rgba(0,0,0,0.2);\n }\n \n .view-run-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n }\n\n \n\n @media (max-width: 768px) {\n .monitor-header[_ngcontent-%COMP%] {\n padding: 12px;\n }\n\n .execution-tree[_ngcontent-%COMP%] {\n padding: 12px;\n }\n }"], changeDetection: 0 }); }
1049
+ } }, styles: ["[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n }\n \n .execution-monitor[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n flex: 1;\n background: #fff;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n overflow: hidden;\n }\n\n \n\n .monitor-header[_ngcontent-%COMP%] {\n padding: 16px;\n background: #f8f9fa;\n border-bottom: 1px solid #e0e0e0;\n }\n\n .header-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 16px;\n font-weight: 600;\n color: #1a1a1a;\n margin-bottom: 8px;\n }\n\n .header-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #2196f3;\n }\n\n .live-indicator[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px;\n background: #ff4444;\n color: white;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.5px;\n }\n\n .pulse[_ngcontent-%COMP%] {\n display: inline-block;\n width: 8px;\n height: 8px;\n background: white;\n border-radius: 50%;\n animation: _ngcontent-%COMP%_pulse 2s infinite;\n }\n\n @keyframes _ngcontent-%COMP%_pulse {\n 0% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(0.8); }\n 100% { opacity: 1; transform: scale(1); }\n }\n\n .current-status[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #666;\n }\n\n .status-label[_ngcontent-%COMP%] {\n font-weight: 500;\n }\n\n .agent-path[_ngcontent-%COMP%] {\n color: #999;\n }\n\n .step-name[_ngcontent-%COMP%] {\n color: #1a1a1a;\n font-weight: 500;\n }\n\n \n\n .execution-tree[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n overflow-x: auto;\n padding: 16px;\n min-height: 0;\n }\n \n .nodes-container[_ngcontent-%COMP%] {\n \n\n }\n\n .empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #999;\n text-align: center;\n }\n\n .empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n }\n\n .empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n }\n\n \n\n .tree-node[_ngcontent-%COMP%] {\n margin-bottom: 4px;\n }\n\n \n\n .monitor-footer[_ngcontent-%COMP%] {\n padding: 16px;\n background: #f8f9fa;\n border-top: 1px solid #e0e0e0;\n }\n\n .stats-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));\n gap: 16px;\n margin-bottom: 12px;\n }\n\n .stat-item[_ngcontent-%COMP%] {\n text-align: center;\n }\n\n .stat-label[_ngcontent-%COMP%] {\n display: block;\n font-size: 11px;\n color: #666;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 4px;\n }\n\n .stat-value[_ngcontent-%COMP%] {\n display: block;\n font-size: 16px;\n font-weight: 600;\n color: #1a1a1a;\n }\n\n .failed-count[_ngcontent-%COMP%] {\n color: #f44336;\n font-size: 12px;\n font-weight: normal;\n }\n\n .step-types[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n justify-content: center;\n }\n\n .type-badge[_ngcontent-%COMP%] {\n display: inline-block;\n padding: 4px 8px;\n background: #e3f2fd;\n color: #1976d2;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n }\n \n \n\n .view-run-btn[_ngcontent-%COMP%] {\n background: #2196f3;\n color: white;\n border: none;\n padding: 6px 12px;\n border-radius: 4px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n transition: all 0.2s ease;\n margin-left: auto;\n }\n \n .view-run-btn[_ngcontent-%COMP%]:hover {\n background: #1976d2;\n transform: translateY(-1px);\n box-shadow: 0 2px 4px rgba(0,0,0,0.2);\n }\n \n .view-run-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n }\n\n \n\n @media (max-width: 768px) {\n .monitor-header[_ngcontent-%COMP%] {\n padding: 12px;\n }\n\n .execution-tree[_ngcontent-%COMP%] {\n padding: 12px;\n }\n }"], changeDetection: 0 }); }
1585
1050
  }
1586
1051
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AgentExecutionMonitorComponent, [{
1587
1052
  type: Component,
1588
- args: [{ selector: 'mj-agent-execution-monitor', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
1053
+ args: [{ selector: 'mj-agent-execution-monitor', changeDetection: ChangeDetectionStrategy.OnPush, template: `
1589
1054
  <div class="execution-monitor" [class.live-mode]="mode === 'live'">
1590
1055
  <!-- Header -->
1591
1056
  <div class="monitor-header">
@@ -1602,11 +1067,10 @@ export class AgentExecutionMonitorComponent {
1602
1067
  @if (currentStep && mode === 'live') {
1603
1068
  <div class="current-status">
1604
1069
  <span class="status-label">Current:</span>
1605
- <span class="agent-path">{{ formatAgentPath(currentStep.agentPath) }}</span>
1606
- <span class="step-name">{{ currentStep.name }}</span>
1070
+ <span class="step-name">{{ currentStep.StepName }}</span>
1607
1071
  </div>
1608
1072
  }
1609
- @if (mode === 'historical' && runId && isExecutionComplete()) {
1073
+ @if (mode === 'historical' && agentRun && isExecutionComplete()) {
1610
1074
  <button class="view-run-btn" (click)="onViewRunClick()">
1611
1075
  <i class="fa-solid fa-external-link-alt"></i>
1612
1076
  View {{ runType === 'agent' ? 'Agent' : 'Prompt' }} Run
@@ -1617,18 +1081,18 @@ export class AgentExecutionMonitorComponent {
1617
1081
  <!-- Execution Tree -->
1618
1082
  <div class="execution-tree"
1619
1083
  #executionTreeContainer
1620
- [class.has-content]="executionTree.length > 0"
1084
+ [class.has-content]="hasContent()"
1621
1085
  (scroll)="onScroll($event)"
1622
1086
  (click)="onUserInteraction()">
1623
1087
  <!-- Always render the container, but show empty state when no data -->
1624
1088
  <div #executionNodesContainer class="nodes-container">
1625
- @if (executionTree.length === 0) {
1626
- <div class="empty-state">
1627
- <i class="fa-solid fa-hourglass-start"></i>
1628
- <p>Waiting for execution to begin...</p>
1629
- </div>
1630
- }
1631
1089
  </div>
1090
+ @if (!agentRun && liveSteps.length === 0) {
1091
+ <div class="empty-state">
1092
+ <i class="fa-solid fa-hourglass-start"></i>
1093
+ <p>Waiting for execution to begin...</p>
1094
+ </div>
1095
+ }
1632
1096
  </div>
1633
1097
 
1634
1098
  <!-- Statistics Footer -->
@@ -1666,17 +1130,19 @@ export class AgentExecutionMonitorComponent {
1666
1130
  <div class="step-types">
1667
1131
  @for (type of getStepTypes(); track type) {
1668
1132
  <span class="type-badge">
1669
- {{ stats.stepsByType[type] }} {{ type }}
1133
+ {{ stats.stepsByType[type] }} {{ pluralizeStepType(type, stats.stepsByType[type]) }}
1670
1134
  </span>
1671
1135
  }
1672
1136
  </div>
1673
1137
  }
1674
1138
  </div>
1675
1139
  </div>
1676
- `, styles: ["\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n }\n \n .execution-monitor {\n display: flex;\n flex-direction: column;\n flex: 1;\n background: #fff;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n /* Header */\n .monitor-header {\n padding: 16px;\n background: #f8f9fa;\n border-bottom: 1px solid #e0e0e0;\n }\n\n .header-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 16px;\n font-weight: 600;\n color: #1a1a1a;\n margin-bottom: 8px;\n }\n\n .header-title i {\n color: #2196f3;\n }\n\n .live-indicator {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px;\n background: #ff4444;\n color: white;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.5px;\n }\n\n .pulse {\n display: inline-block;\n width: 8px;\n height: 8px;\n background: white;\n border-radius: 50%;\n animation: pulse 2s infinite;\n }\n\n @keyframes pulse {\n 0% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(0.8); }\n 100% { opacity: 1; transform: scale(1); }\n }\n\n .current-status {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #666;\n }\n\n .status-label {\n font-weight: 500;\n }\n\n .agent-path {\n color: #999;\n }\n\n .step-name {\n color: #1a1a1a;\n font-weight: 500;\n }\n\n /* Execution Tree */\n .execution-tree {\n flex: 1;\n overflow-y: auto;\n overflow-x: auto;\n padding: 16px;\n min-height: 0;\n }\n \n .nodes-container {\n /* No extra padding needed, indentation handled by node margins */\n }\n\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #999;\n text-align: center;\n }\n\n .empty-state i {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n }\n\n .empty-state p {\n margin: 0;\n font-size: 14px;\n }\n\n /* Tree Nodes - These styles are for reference only, actual rendering is done by ExecutionNodeComponent */\n .tree-node {\n margin-bottom: 4px;\n }\n\n /* Footer */\n .monitor-footer {\n padding: 16px;\n background: #f8f9fa;\n border-top: 1px solid #e0e0e0;\n }\n\n .stats-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));\n gap: 16px;\n margin-bottom: 12px;\n }\n\n .stat-item {\n text-align: center;\n }\n\n .stat-label {\n display: block;\n font-size: 11px;\n color: #666;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 4px;\n }\n\n .stat-value {\n display: block;\n font-size: 16px;\n font-weight: 600;\n color: #1a1a1a;\n }\n\n .failed-count {\n color: #f44336;\n font-size: 12px;\n font-weight: normal;\n }\n\n .step-types {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n justify-content: center;\n }\n\n .type-badge {\n display: inline-block;\n padding: 4px 8px;\n background: #e3f2fd;\n color: #1976d2;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n }\n \n /* View Run Button */\n .view-run-btn {\n background: #2196f3;\n color: white;\n border: none;\n padding: 6px 12px;\n border-radius: 4px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n transition: all 0.2s ease;\n margin-left: auto;\n }\n \n .view-run-btn:hover {\n background: #1976d2;\n transform: translateY(-1px);\n box-shadow: 0 2px 4px rgba(0,0,0,0.2);\n }\n \n .view-run-btn i {\n font-size: 12px;\n }\n\n /* Responsive */\n @media (max-width: 768px) {\n .monitor-header {\n padding: 12px;\n }\n\n .execution-tree {\n padding: 12px;\n }\n }\n "] }]
1140
+ `, styles: ["\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n }\n \n .execution-monitor {\n display: flex;\n flex-direction: column;\n flex: 1;\n background: #fff;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n overflow: hidden;\n }\n\n /* Header */\n .monitor-header {\n padding: 16px;\n background: #f8f9fa;\n border-bottom: 1px solid #e0e0e0;\n }\n\n .header-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 16px;\n font-weight: 600;\n color: #1a1a1a;\n margin-bottom: 8px;\n }\n\n .header-title i {\n color: #2196f3;\n }\n\n .live-indicator {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px;\n background: #ff4444;\n color: white;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.5px;\n }\n\n .pulse {\n display: inline-block;\n width: 8px;\n height: 8px;\n background: white;\n border-radius: 50%;\n animation: pulse 2s infinite;\n }\n\n @keyframes pulse {\n 0% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(0.8); }\n 100% { opacity: 1; transform: scale(1); }\n }\n\n .current-status {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #666;\n }\n\n .status-label {\n font-weight: 500;\n }\n\n .agent-path {\n color: #999;\n }\n\n .step-name {\n color: #1a1a1a;\n font-weight: 500;\n }\n\n /* Execution Tree */\n .execution-tree {\n flex: 1;\n overflow-y: auto;\n overflow-x: auto;\n padding: 16px;\n min-height: 0;\n }\n \n .nodes-container {\n /* No extra padding needed, indentation handled by node margins */\n }\n\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #999;\n text-align: center;\n }\n\n .empty-state i {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n }\n\n .empty-state p {\n margin: 0;\n font-size: 14px;\n }\n\n /* Tree Nodes - These styles are for reference only, actual rendering is done by ExecutionNodeComponent */\n .tree-node {\n margin-bottom: 4px;\n }\n\n /* Footer */\n .monitor-footer {\n padding: 16px;\n background: #f8f9fa;\n border-top: 1px solid #e0e0e0;\n }\n\n .stats-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));\n gap: 16px;\n margin-bottom: 12px;\n }\n\n .stat-item {\n text-align: center;\n }\n\n .stat-label {\n display: block;\n font-size: 11px;\n color: #666;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 4px;\n }\n\n .stat-value {\n display: block;\n font-size: 16px;\n font-weight: 600;\n color: #1a1a1a;\n }\n\n .failed-count {\n color: #f44336;\n font-size: 12px;\n font-weight: normal;\n }\n\n .step-types {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n justify-content: center;\n }\n\n .type-badge {\n display: inline-block;\n padding: 4px 8px;\n background: #e3f2fd;\n color: #1976d2;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n }\n \n /* View Run Button */\n .view-run-btn {\n background: #2196f3;\n color: white;\n border: none;\n padding: 6px 12px;\n border-radius: 4px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n transition: all 0.2s ease;\n margin-left: auto;\n }\n \n .view-run-btn:hover {\n background: #1976d2;\n transform: translateY(-1px);\n box-shadow: 0 2px 4px rgba(0,0,0,0.2);\n }\n \n .view-run-btn i {\n font-size: 12px;\n }\n\n /* Responsive */\n @media (max-width: 768px) {\n .monitor-header {\n padding: 12px;\n }\n\n .execution-tree {\n padding: 12px;\n }\n }\n "] }]
1677
1141
  }], () => [{ type: i0.ChangeDetectorRef }], { mode: [{
1678
1142
  type: Input
1679
- }], executionData: [{
1143
+ }], agentRun: [{
1144
+ type: Input
1145
+ }], liveSteps: [{
1680
1146
  type: Input
1681
1147
  }], autoExpand: [{
1682
1148
  type: Input
@@ -1693,5 +1159,5 @@ export class AgentExecutionMonitorComponent {
1693
1159
  type: ViewChild,
1694
1160
  args: ['executionNodesContainer', { read: ViewContainerRef }]
1695
1161
  }] }); })();
1696
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AgentExecutionMonitorComponent, { className: "AgentExecutionMonitorComponent", filePath: "lib/agent-execution-monitor.component.ts", lineNumber: 406 }); })();
1162
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AgentExecutionMonitorComponent, { className: "AgentExecutionMonitorComponent", filePath: "lib/agent-execution-monitor.component.ts", lineNumber: 367 }); })();
1697
1163
  //# sourceMappingURL=agent-execution-monitor.component.js.map