@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.
- package/dist/lib/agent-execution-monitor.component.d.ts +42 -101
- package/dist/lib/agent-execution-monitor.component.d.ts.map +1 -1
- package/dist/lib/agent-execution-monitor.component.js +409 -943
- package/dist/lib/agent-execution-monitor.component.js.map +1 -1
- package/dist/lib/agent-execution-node.component.d.ts +24 -5
- package/dist/lib/agent-execution-node.component.d.ts.map +1 -1
- package/dist/lib/agent-execution-node.component.js +275 -140
- package/dist/lib/agent-execution-node.component.js.map +1 -1
- package/dist/lib/ai-test-harness-window.component.d.ts +1 -0
- package/dist/lib/ai-test-harness-window.component.d.ts.map +1 -1
- package/dist/lib/ai-test-harness-window.component.js +13 -4
- package/dist/lib/ai-test-harness-window.component.js.map +1 -1
- package/dist/lib/ai-test-harness.component.d.ts +25 -17
- package/dist/lib/ai-test-harness.component.d.ts.map +1 -1
- package/dist/lib/ai-test-harness.component.js +194 -216
- package/dist/lib/ai-test-harness.component.js.map +1 -1
- package/dist/lib/test-harness-window.service.d.ts.map +1 -1
- package/dist/lib/test-harness-window.service.js +10 -6
- package/dist/lib/test-harness-window.service.js.map +1 -1
- package/dist/module.d.ts +16 -16
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +9 -16
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +1 -1
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -1
- package/dist/public-api.js.map +1 -1
- package/package.json +8 -8
|
@@ -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.
|
|
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",
|
|
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",
|
|
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",
|
|
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",
|
|
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",
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
146
|
+
agentRunChanged: !!changes['agentRun'],
|
|
147
|
+
liveStepsChanged: !!changes['liveSteps'],
|
|
149
148
|
modeChanged: !!changes['mode'],
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
hasAgentRun: !!this.agentRun,
|
|
150
|
+
liveStepsCount: this.liveSteps?.length || 0
|
|
152
151
|
});
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
console.log('📊
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
//
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
//
|
|
189
|
-
if (
|
|
190
|
-
|
|
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
|
|
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
|
-
//
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
225
|
-
|
|
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.
|
|
234
|
-
console.log('⚡ Processing
|
|
235
|
-
this.
|
|
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.
|
|
238
|
-
console.log('⚡
|
|
239
|
-
this.
|
|
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
|
|
259
|
+
* Process agent run for historical mode
|
|
263
260
|
*/
|
|
264
|
-
|
|
265
|
-
console.log('⚙️ Processing
|
|
266
|
-
|
|
267
|
-
|
|
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.
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
this.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
287
|
+
// Calculate statistics
|
|
290
288
|
this.calculateStats();
|
|
291
|
-
//
|
|
292
|
-
|
|
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
|
-
*
|
|
293
|
+
* Process live steps for live mode
|
|
299
294
|
*/
|
|
300
|
-
|
|
301
|
-
console.log('
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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.
|
|
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
|
-
//
|
|
314
|
-
this.
|
|
315
|
-
//
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
|
314
|
+
* Render steps recursively with proper hierarchy
|
|
346
315
|
*/
|
|
347
|
-
|
|
348
|
-
console.log('🎨 Rendering
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
316
|
+
renderSteps(steps, depth, agentPath) {
|
|
317
|
+
console.log('🎨 Rendering steps:', {
|
|
318
|
+
count: steps.length,
|
|
319
|
+
depth,
|
|
320
|
+
agentPath
|
|
352
321
|
});
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
|
372
|
-
* This now ensures proper depth is passed and used for indentation
|
|
341
|
+
* Create a component for a step
|
|
373
342
|
*/
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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((
|
|
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(
|
|
390
|
-
//
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
*
|
|
397
|
-
* FIXED: Maintain proper hierarchy for sub-agents instead of flattening
|
|
379
|
+
* Toggle step expansion
|
|
398
380
|
*/
|
|
399
|
-
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
|
|
391
|
+
this.cdr.markForCheck();
|
|
479
392
|
}
|
|
480
393
|
/**
|
|
481
|
-
*
|
|
394
|
+
* Toggle step details expansion
|
|
482
395
|
*/
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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
|
-
*
|
|
407
|
+
* Get step type CSS class for styling
|
|
518
408
|
*/
|
|
519
|
-
|
|
409
|
+
getStepTypeClass(stepType) {
|
|
520
410
|
const typeMap = {
|
|
521
|
-
'
|
|
522
|
-
'
|
|
523
|
-
'
|
|
524
|
-
'
|
|
525
|
-
'
|
|
526
|
-
'
|
|
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[
|
|
418
|
+
return typeMap[stepType] || 'prompt';
|
|
531
419
|
}
|
|
532
420
|
/**
|
|
533
|
-
*
|
|
421
|
+
* Get status CSS class for styling
|
|
534
422
|
*/
|
|
535
|
-
|
|
423
|
+
getStatusClass(status) {
|
|
536
424
|
const statusMap = {
|
|
537
|
-
'
|
|
538
|
-
'
|
|
539
|
-
'
|
|
540
|
-
'
|
|
541
|
-
'
|
|
425
|
+
'Pending': 'pending',
|
|
426
|
+
'Running': 'running',
|
|
427
|
+
'Completed': 'completed',
|
|
428
|
+
'Failed': 'failed',
|
|
429
|
+
'Cancelled': 'failed',
|
|
430
|
+
'Paused': 'pending'
|
|
542
431
|
};
|
|
543
|
-
return statusMap[status
|
|
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
|
-
|
|
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: ${
|
|
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
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
//
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
|
|
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
|
-
*
|
|
613
|
+
* Ensure only the last step in the execution order shows as running
|
|
802
614
|
*/
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
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
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
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
|
-
//
|
|
831
|
-
if (this.
|
|
832
|
-
|
|
833
|
-
this.stats.
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
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
|
-
|
|
890
|
-
|
|
891
|
-
hasRawExecutionTree: !!this.executionData?.executionTree
|
|
672
|
+
totalTokens: this.stats.totalTokens,
|
|
673
|
+
totalCost: this.stats.totalCost
|
|
892
674
|
});
|
|
893
675
|
}
|
|
894
676
|
/**
|
|
895
|
-
*
|
|
677
|
+
* Count steps recursively
|
|
896
678
|
*/
|
|
897
|
-
|
|
898
|
-
const
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
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(
|
|
1042
|
-
if (!
|
|
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
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
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
|
|
858
|
+
let hasNewSteps = false;
|
|
1072
859
|
for (const step of sortedSteps) {
|
|
1073
|
-
//
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
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
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
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 (
|
|
1163
|
-
//
|
|
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
|
-
//
|
|
1168
|
-
this.cdr.
|
|
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
|
-
*
|
|
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
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
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
|
-
*
|
|
907
|
+
* Build agent path for a step
|
|
1359
908
|
*/
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
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.
|
|
1403
|
-
this.viewRunClick.emit({ runId: this.
|
|
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
|
|
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
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
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
|
-
|
|
1472
|
-
// Trigger change detection for the entire component
|
|
943
|
+
});
|
|
944
|
+
// Trigger change detection
|
|
1473
945
|
this.cdr.markForCheck();
|
|
1474
946
|
this.cdr.detectChanges();
|
|
1475
|
-
|
|
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
|
|
965
|
+
* Check if there is content to display
|
|
1495
966
|
*/
|
|
1496
|
-
|
|
1497
|
-
return
|
|
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",
|
|
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,
|
|
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.ɵɵ
|
|
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.
|
|
1030
|
+
i0.ɵɵconditional(ctx.mode === "historical" && ctx.agentRun && ctx.isExecutionComplete() ? 8 : -1);
|
|
1566
1031
|
i0.ɵɵadvance();
|
|
1567
|
-
i0.ɵɵclassProp("has-content", ctx.
|
|
1032
|
+
i0.ɵɵclassProp("has-content", ctx.hasContent());
|
|
1568
1033
|
i0.ɵɵadvance(4);
|
|
1569
|
-
i0.ɵɵconditional(ctx.
|
|
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
|
-
} },
|
|
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',
|
|
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="
|
|
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' &&
|
|
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]="
|
|
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
|
|
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
|
-
}],
|
|
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:
|
|
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
|