@memberjunction/ng-ai-test-harness 2.58.0 → 2.60.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 +49 -94
- package/dist/lib/agent-execution-monitor.component.d.ts.map +1 -1
- package/dist/lib/agent-execution-monitor.component.js +448 -772
- package/dist/lib/agent-execution-monitor.component.js.map +1 -1
- package/dist/lib/agent-execution-node.component.d.ts +28 -6
- package/dist/lib/agent-execution-node.component.d.ts.map +1 -1
- package/dist/lib/agent-execution-node.component.js +399 -199
- 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 +33 -17
- package/dist/lib/ai-test-harness.component.d.ts.map +1 -1
- package/dist/lib/ai-test-harness.component.js +294 -200
- 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,
|
|
@@ -144,51 +142,98 @@ export class AgentExecutionMonitorComponent {
|
|
|
144
142
|
this.destroy$ = new Subject();
|
|
145
143
|
}
|
|
146
144
|
ngOnChanges(changes) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
145
|
+
console.log('🔄 Execution Monitor ngOnChanges:', {
|
|
146
|
+
agentRunChanged: !!changes['agentRun'],
|
|
147
|
+
liveStepsChanged: !!changes['liveSteps'],
|
|
148
|
+
modeChanged: !!changes['mode'],
|
|
149
|
+
hasAgentRun: !!this.agentRun,
|
|
150
|
+
liveStepsCount: this.liveSteps?.length || 0
|
|
151
|
+
});
|
|
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
|
|
162
|
+
});
|
|
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');
|
|
155
169
|
this.processedStepIds.clear();
|
|
156
170
|
this.newlyAddedNodeIds.clear();
|
|
171
|
+
this.expandedStates.clear();
|
|
172
|
+
this.detailsExpandedStates.clear();
|
|
157
173
|
this.clearNodeComponents();
|
|
158
|
-
this.executionTree = [];
|
|
159
174
|
this.currentStep = null;
|
|
160
|
-
this.calculateStats();
|
|
161
175
|
}
|
|
162
|
-
//
|
|
163
|
-
|
|
176
|
+
// Always process the agent run data (will handle updates to existing run)
|
|
177
|
+
if (newRun) {
|
|
178
|
+
this.processAgentRun();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
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();
|
|
164
191
|
}
|
|
165
192
|
// Handle mode changes
|
|
166
193
|
if (changes['mode'] && !changes['mode'].firstChange) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
194
|
+
const previousMode = changes['mode'].previousValue;
|
|
195
|
+
const currentMode = changes['mode'].currentValue;
|
|
196
|
+
console.log('🔄 Mode changed:', { previousMode, currentMode });
|
|
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();
|
|
179
215
|
}
|
|
180
|
-
this.processExecutionData();
|
|
181
216
|
}
|
|
182
217
|
}
|
|
183
218
|
ngAfterViewInit() {
|
|
184
219
|
this.viewInitialized = true;
|
|
220
|
+
console.log('🎯 View initialized, checking for pending data:', {
|
|
221
|
+
hasAgentRun: !!this.agentRun,
|
|
222
|
+
hasLiveSteps: this.liveSteps?.length > 0,
|
|
223
|
+
hasContainer: !!this.executionNodesContainer
|
|
224
|
+
});
|
|
185
225
|
// Initial setup for scroll behavior
|
|
186
226
|
if (this.mode === 'live') {
|
|
187
227
|
this.checkIfUserAtBottom();
|
|
188
228
|
}
|
|
189
229
|
// If we have data waiting to be rendered, render it now
|
|
190
|
-
if (this.
|
|
191
|
-
|
|
230
|
+
if (this.mode === 'historical' && this.agentRun && this.nodeComponentMap.size === 0) {
|
|
231
|
+
console.log('⚡ Processing agent run after view init');
|
|
232
|
+
this.processAgentRun();
|
|
233
|
+
}
|
|
234
|
+
else if (this.mode === 'live' && this.liveSteps?.length > 0) {
|
|
235
|
+
console.log('⚡ Processing live steps after view init');
|
|
236
|
+
this.processLiveSteps();
|
|
192
237
|
}
|
|
193
238
|
}
|
|
194
239
|
ngOnDestroy() {
|
|
@@ -211,237 +256,192 @@ export class AgentExecutionMonitorComponent {
|
|
|
211
256
|
}
|
|
212
257
|
}
|
|
213
258
|
/**
|
|
214
|
-
* Process
|
|
259
|
+
* Process agent run for historical mode
|
|
215
260
|
*/
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
this.
|
|
220
|
-
|
|
221
|
-
this.
|
|
261
|
+
processAgentRun() {
|
|
262
|
+
console.log('⚙️ Processing agent run:', {
|
|
263
|
+
hasAgentRun: !!this.agentRun,
|
|
264
|
+
stepsCount: this.agentRun?.Steps?.length || 0,
|
|
265
|
+
viewInitialized: this.viewInitialized,
|
|
266
|
+
hasContainer: !!this.executionNodesContainer
|
|
267
|
+
});
|
|
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;
|
|
222
275
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
// Set up live updates
|
|
231
|
-
this.setupLiveUpdates();
|
|
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');
|
|
232
283
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
this.calculateStats();
|
|
284
|
+
else {
|
|
285
|
+
console.warn('⚠️ No steps to render');
|
|
236
286
|
}
|
|
287
|
+
// Calculate statistics
|
|
288
|
+
this.calculateStats();
|
|
289
|
+
// Trigger change detection after rendering all components
|
|
290
|
+
this.cdr.detectChanges();
|
|
237
291
|
}
|
|
238
292
|
/**
|
|
239
|
-
*
|
|
293
|
+
* Process live steps for live mode
|
|
240
294
|
*/
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
this.
|
|
244
|
-
this.
|
|
295
|
+
processLiveSteps() {
|
|
296
|
+
console.log('⚙️ Processing live steps:', {
|
|
297
|
+
stepsCount: this.liveSteps?.length || 0,
|
|
298
|
+
viewInitialized: this.viewInitialized,
|
|
299
|
+
hasContainer: !!this.executionNodesContainer
|
|
300
|
+
});
|
|
301
|
+
if (!this.liveSteps || !this.viewInitialized || !this.executionNodesContainer) {
|
|
245
302
|
return;
|
|
246
303
|
}
|
|
247
|
-
//
|
|
248
|
-
this.
|
|
249
|
-
//
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
this.
|
|
254
|
-
}
|
|
255
|
-
else if (this.executionData.steps) {
|
|
256
|
-
// Handle legacy steps format
|
|
257
|
-
const allNodes = this.convertLegacySteps(this.executionData.steps);
|
|
258
|
-
this.executionTree = this.filterNodesForMode(allNodes, 'historical');
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
this.executionTree = [];
|
|
262
|
-
}
|
|
263
|
-
// Render the tree using dynamic components
|
|
264
|
-
if (this.executionNodesContainer) {
|
|
265
|
-
this.renderTree();
|
|
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();
|
|
266
311
|
}
|
|
267
312
|
}
|
|
268
313
|
/**
|
|
269
|
-
* Render
|
|
314
|
+
* Render steps recursively with proper hierarchy
|
|
270
315
|
*/
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
for (const
|
|
280
|
-
|
|
316
|
+
renderSteps(steps, depth, agentPath) {
|
|
317
|
+
console.log('🎨 Rendering steps:', {
|
|
318
|
+
count: steps.length,
|
|
319
|
+
depth,
|
|
320
|
+
agentPath
|
|
321
|
+
});
|
|
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
|
+
}
|
|
281
338
|
}
|
|
282
339
|
}
|
|
283
340
|
/**
|
|
284
|
-
* Create a component for a
|
|
341
|
+
* Create a component for a step
|
|
285
342
|
*/
|
|
286
|
-
|
|
287
|
-
|
|
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);
|
|
288
350
|
const instance = componentRef.instance;
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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;
|
|
292
364
|
// Subscribe to outputs
|
|
293
|
-
instance.toggleNode.subscribe((
|
|
365
|
+
instance.toggleNode.subscribe(() => this.toggleStepExpansion(step));
|
|
366
|
+
instance.toggleDetails.subscribe(() => this.toggleStepDetails(step));
|
|
294
367
|
instance.userInteracted.subscribe(() => this.onUserInteraction());
|
|
295
368
|
// Store reference
|
|
296
|
-
this.nodeComponentMap.set(
|
|
297
|
-
//
|
|
298
|
-
|
|
299
|
-
|
|
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
|
+
});
|
|
300
376
|
return componentRef;
|
|
301
377
|
}
|
|
302
378
|
/**
|
|
303
|
-
*
|
|
379
|
+
* Toggle step expansion
|
|
304
380
|
*/
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
let detailsMarkdown;
|
|
317
|
-
// Check if the step name contains markdown (indicated by newlines or markdown syntax)
|
|
318
|
-
if (stepName.includes('\n') || stepName.includes('**') || stepName.includes('##')) {
|
|
319
|
-
// Split by newline and use first line as name
|
|
320
|
-
const lines = stepName.split('\n');
|
|
321
|
-
name = lines[0].trim();
|
|
322
|
-
// Rest is markdown details
|
|
323
|
-
if (lines.length > 1) {
|
|
324
|
-
detailsMarkdown = lines.slice(1).join('\n').trim();
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
// Determine display mode based on the step content
|
|
328
|
-
let displayMode = 'both';
|
|
329
|
-
// Progress messages that should only show in live mode
|
|
330
|
-
if (name.includes('Executing') && name.includes('action') && !name.startsWith('Execute Action:')) {
|
|
331
|
-
displayMode = 'live';
|
|
332
|
-
}
|
|
333
|
-
else if (name.includes("Executing agent's initial prompt")) {
|
|
334
|
-
displayMode = 'live';
|
|
335
|
-
}
|
|
336
|
-
else if (name.includes('Re-executing agent prompt with additional context')) {
|
|
337
|
-
displayMode = 'live';
|
|
338
|
-
}
|
|
339
|
-
// Clear metadata storage before creating previews
|
|
340
|
-
this.lastParsedAgentData = null;
|
|
341
|
-
this.lastParsedActionData = null;
|
|
342
|
-
// Create previews which will populate metadata if available
|
|
343
|
-
const inputPreview = this.createPreview(node.inputData);
|
|
344
|
-
const outputPreview = this.createPreview(node.outputData);
|
|
345
|
-
const treeNode = {
|
|
346
|
-
id: node.step?.ID || `node-${Date.now()}-${index}`,
|
|
347
|
-
name: name,
|
|
348
|
-
type: this.mapStepType(node.executionType || node.step?.StepType),
|
|
349
|
-
status: this.mapStepStatus(node.step?.Status),
|
|
350
|
-
startTime: node.startTime ? new Date(node.startTime) : undefined,
|
|
351
|
-
endTime: node.endTime ? new Date(node.endTime) : undefined,
|
|
352
|
-
duration: node.durationMs,
|
|
353
|
-
agentPath: node.agentHierarchy || [],
|
|
354
|
-
inputPreview: inputPreview,
|
|
355
|
-
outputPreview: outputPreview,
|
|
356
|
-
error: node.step?.ErrorMessage,
|
|
357
|
-
expanded: false,
|
|
358
|
-
depth: node.depth !== undefined ? node.depth : depth, // Use node.depth if available, fallback to recursion depth
|
|
359
|
-
tokensUsed: this.extractTokens(node.outputData),
|
|
360
|
-
cost: this.extractCost(node.outputData),
|
|
361
|
-
detailsMarkdown: detailsMarkdown,
|
|
362
|
-
detailsExpanded: false,
|
|
363
|
-
children: node.children ? this.convertExecutionTree(node.children, node.depth !== undefined ? node.depth + 1 : depth + 1) : undefined,
|
|
364
|
-
displayMode: displayMode
|
|
365
|
-
};
|
|
366
|
-
// Add agent metadata if this is a sub-agent node
|
|
367
|
-
if (treeNode.type === 'sub-agent' && this.lastParsedAgentData) {
|
|
368
|
-
treeNode.agentName = this.lastParsedAgentData.agentName;
|
|
369
|
-
treeNode.agentId = this.lastParsedAgentData.agentId;
|
|
370
|
-
treeNode.agentIconClass = this.lastParsedAgentData.agentIconClass;
|
|
371
|
-
treeNode.agentLogoURL = this.lastParsedAgentData.agentLogoURL;
|
|
372
|
-
}
|
|
373
|
-
// Add action metadata if this is an action node
|
|
374
|
-
if (treeNode.type === 'action' && this.lastParsedActionData) {
|
|
375
|
-
treeNode.actionName = this.lastParsedActionData.actionName;
|
|
376
|
-
treeNode.actionId = this.lastParsedActionData.actionId;
|
|
377
|
-
treeNode.actionIconClass = this.lastParsedActionData.actionIconClass;
|
|
378
|
-
}
|
|
379
|
-
return treeNode;
|
|
380
|
-
});
|
|
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();
|
|
390
|
+
}
|
|
391
|
+
this.cdr.markForCheck();
|
|
381
392
|
}
|
|
382
393
|
/**
|
|
383
|
-
*
|
|
394
|
+
* Toggle step details expansion
|
|
384
395
|
*/
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
name = lines[0].trim();
|
|
395
|
-
if (lines.length > 1) {
|
|
396
|
-
detailsMarkdown = lines.slice(1).join('\n').trim();
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
return {
|
|
400
|
-
id: step.ID || `step-${index}`,
|
|
401
|
-
name: name,
|
|
402
|
-
type: this.mapStepType(step.StepType),
|
|
403
|
-
status: this.mapStepStatus(step.Status),
|
|
404
|
-
startTime: step.StartedAt ? new Date(step.StartedAt) : undefined,
|
|
405
|
-
endTime: step.CompletedAt ? new Date(step.CompletedAt) : undefined,
|
|
406
|
-
duration: this.calculateDuration(step.StartedAt, step.CompletedAt),
|
|
407
|
-
agentPath: [],
|
|
408
|
-
inputPreview: this.createPreview(step.InputData),
|
|
409
|
-
outputPreview: this.createPreview(step.OutputData),
|
|
410
|
-
error: step.ErrorMessage,
|
|
411
|
-
expanded: false,
|
|
412
|
-
depth: 0,
|
|
413
|
-
detailsMarkdown: detailsMarkdown,
|
|
414
|
-
detailsExpanded: false
|
|
415
|
-
};
|
|
416
|
-
});
|
|
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
|
+
}
|
|
417
405
|
}
|
|
418
406
|
/**
|
|
419
|
-
*
|
|
407
|
+
* Get step type CSS class for styling
|
|
420
408
|
*/
|
|
421
|
-
|
|
409
|
+
getStepTypeClass(stepType) {
|
|
422
410
|
const typeMap = {
|
|
423
|
-
'
|
|
424
|
-
'
|
|
425
|
-
'
|
|
426
|
-
'
|
|
427
|
-
'
|
|
428
|
-
'
|
|
429
|
-
'chat': 'chat'
|
|
411
|
+
'Validation': 'validation',
|
|
412
|
+
'Prompt': 'prompt',
|
|
413
|
+
'Actions': 'action',
|
|
414
|
+
'Sub-Agent': 'sub-agent',
|
|
415
|
+
'Decision': 'decision',
|
|
416
|
+
'Chat': 'chat'
|
|
430
417
|
};
|
|
431
|
-
return typeMap[
|
|
418
|
+
return typeMap[stepType] || 'prompt';
|
|
432
419
|
}
|
|
433
420
|
/**
|
|
434
|
-
*
|
|
421
|
+
* Get status CSS class for styling
|
|
435
422
|
*/
|
|
436
|
-
|
|
423
|
+
getStatusClass(status) {
|
|
437
424
|
const statusMap = {
|
|
438
|
-
'
|
|
439
|
-
'
|
|
440
|
-
'
|
|
441
|
-
'
|
|
442
|
-
'
|
|
425
|
+
'Pending': 'pending',
|
|
426
|
+
'Running': 'running',
|
|
427
|
+
'Completed': 'completed',
|
|
428
|
+
'Failed': 'failed',
|
|
429
|
+
'Cancelled': 'failed',
|
|
430
|
+
'Paused': 'pending'
|
|
443
431
|
};
|
|
444
|
-
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);
|
|
445
445
|
}
|
|
446
446
|
/**
|
|
447
447
|
* Create a preview string from data
|
|
@@ -474,9 +474,9 @@ export class AgentExecutionMonitorComponent {
|
|
|
474
474
|
return `Sub-agent: ${parsed.subAgentName}`;
|
|
475
475
|
}
|
|
476
476
|
if (parsed.message)
|
|
477
|
-
return parsed.message;
|
|
477
|
+
return this.valueToPreviewString(parsed.message);
|
|
478
478
|
if (parsed.userMessage)
|
|
479
|
-
return parsed.userMessage;
|
|
479
|
+
return this.valueToPreviewString(parsed.userMessage);
|
|
480
480
|
// Show action results clearly
|
|
481
481
|
if (parsed.actionResult) {
|
|
482
482
|
const result = parsed.actionResult;
|
|
@@ -488,7 +488,7 @@ export class AgentExecutionMonitorComponent {
|
|
|
488
488
|
preview += `Result Code: ${result.resultCode}\n`;
|
|
489
489
|
}
|
|
490
490
|
if (result.message) {
|
|
491
|
-
preview += `Message: ${result.message}\n`;
|
|
491
|
+
preview += `Message: ${this.valueToPreviewString(result.message)}\n`;
|
|
492
492
|
}
|
|
493
493
|
if (result.result) {
|
|
494
494
|
preview += `Result: ${typeof result.result === 'object' ? JSON.stringify(result.result, null, 2) : result.result}`;
|
|
@@ -497,10 +497,7 @@ export class AgentExecutionMonitorComponent {
|
|
|
497
497
|
}
|
|
498
498
|
// Legacy action result format
|
|
499
499
|
if (parsed.result) {
|
|
500
|
-
|
|
501
|
-
return JSON.stringify(parsed.result, null, 2);
|
|
502
|
-
}
|
|
503
|
-
return String(parsed.result);
|
|
500
|
+
return this.valueToPreviewString(parsed.result);
|
|
504
501
|
}
|
|
505
502
|
// Show prompt results
|
|
506
503
|
if (parsed.promptResult) {
|
|
@@ -510,7 +507,7 @@ export class AgentExecutionMonitorComponent {
|
|
|
510
507
|
preview += `Success: ${result.success}\n`;
|
|
511
508
|
}
|
|
512
509
|
if (result.content) {
|
|
513
|
-
preview += `Content: ${
|
|
510
|
+
preview += `Content: ${this.valueToPreviewString(result.content)}`;
|
|
514
511
|
}
|
|
515
512
|
return preview;
|
|
516
513
|
}
|
|
@@ -564,153 +561,83 @@ export class AgentExecutionMonitorComponent {
|
|
|
564
561
|
* Set up live updates for real-time execution
|
|
565
562
|
*/
|
|
566
563
|
setupLiveUpdates() {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
this.clearNodeComponents();
|
|
572
|
-
this.executionTree = [];
|
|
573
|
-
// Then append all steps
|
|
574
|
-
this.appendNewLiveSteps(this.executionData.liveSteps);
|
|
575
|
-
}
|
|
576
|
-
else {
|
|
577
|
-
// Just append new steps without clearing
|
|
578
|
-
this.appendNewLiveSteps(this.executionData.liveSteps);
|
|
579
|
-
}
|
|
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);
|
|
580
568
|
}
|
|
581
|
-
// Set up interval to
|
|
569
|
+
// Set up interval to check for updates (if not already set up)
|
|
582
570
|
if (this.mode === 'live' && !this.updateSubscription) {
|
|
583
571
|
this.updateSubscription = interval(500)
|
|
584
572
|
.pipe(takeUntil(this.destroy$))
|
|
585
573
|
.subscribe(() => {
|
|
586
|
-
//
|
|
587
|
-
if (this.executionData && this.executionData.liveSteps) {
|
|
588
|
-
// Instead of replacing entire tree, append new nodes
|
|
589
|
-
this.appendNewLiveSteps(this.executionData.liveSteps);
|
|
590
|
-
this.calculateStats();
|
|
591
|
-
}
|
|
574
|
+
// Update current step indicator
|
|
592
575
|
this.updateCurrentStep();
|
|
576
|
+
// Recalculate stats periodically
|
|
577
|
+
this.calculateStats();
|
|
593
578
|
});
|
|
594
579
|
}
|
|
595
580
|
}
|
|
596
|
-
/**
|
|
597
|
-
* Convert live steps format to execution tree format
|
|
598
|
-
*/
|
|
599
|
-
convertLiveStepsToTree(liveSteps) {
|
|
600
|
-
const treeNodes = [];
|
|
601
|
-
const nodeMap = new Map();
|
|
602
|
-
// Sort steps by StepNumber to ensure proper ordering
|
|
603
|
-
const sortedSteps = [...liveSteps].sort((a, b) => {
|
|
604
|
-
const aStepNum = a.step?.StepNumber || 0;
|
|
605
|
-
const bStepNum = b.step?.StepNumber || 0;
|
|
606
|
-
return aStepNum - bStepNum;
|
|
607
|
-
});
|
|
608
|
-
for (const step of sortedSteps) {
|
|
609
|
-
// Parse the step name for markdown content
|
|
610
|
-
const stepName = step.step?.StepName || 'Processing...';
|
|
611
|
-
let name = stepName;
|
|
612
|
-
let detailsMarkdown;
|
|
613
|
-
// Check if the step name contains markdown (indicated by newlines or markdown syntax)
|
|
614
|
-
if (stepName.includes('\n') || stepName.includes('**') || stepName.includes('##')) {
|
|
615
|
-
// Split by newline and use first line as name
|
|
616
|
-
const lines = stepName.split('\n');
|
|
617
|
-
name = lines[0].trim();
|
|
618
|
-
// Rest is markdown details
|
|
619
|
-
if (lines.length > 1) {
|
|
620
|
-
detailsMarkdown = lines.slice(1).join('\n').trim();
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
const node = {
|
|
624
|
-
id: step.step?.ID || `live-${Date.now()}-${Math.random()}`,
|
|
625
|
-
name: name,
|
|
626
|
-
type: this.mapStepType(step.executionType || step.step?.StepType),
|
|
627
|
-
status: 'running', // Live steps are always running
|
|
628
|
-
startTime: step.startTime ? new Date(step.startTime) : new Date(),
|
|
629
|
-
agentPath: step.agentHierarchy || [],
|
|
630
|
-
expanded: false,
|
|
631
|
-
depth: step.depth || 0,
|
|
632
|
-
detailsMarkdown: detailsMarkdown,
|
|
633
|
-
detailsExpanded: false
|
|
634
|
-
};
|
|
635
|
-
// Store in map for hierarchy building
|
|
636
|
-
nodeMap.set(node.id, node);
|
|
637
|
-
// Handle hierarchy by depth
|
|
638
|
-
if (step.depth === 0) {
|
|
639
|
-
treeNodes.push(node);
|
|
640
|
-
}
|
|
641
|
-
else {
|
|
642
|
-
// Find parent based on depth and agent hierarchy
|
|
643
|
-
let parent;
|
|
644
|
-
// Look for a node at depth-1 that could be the parent
|
|
645
|
-
nodeMap.forEach((candidate) => {
|
|
646
|
-
if (candidate.depth === step.depth - 1) {
|
|
647
|
-
parent = candidate;
|
|
648
|
-
}
|
|
649
|
-
});
|
|
650
|
-
if (parent) {
|
|
651
|
-
if (!parent.children) {
|
|
652
|
-
parent.children = [];
|
|
653
|
-
}
|
|
654
|
-
parent.children.push(node);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
nodeMap.set(node.id, node);
|
|
658
|
-
}
|
|
659
|
-
return treeNodes;
|
|
660
|
-
}
|
|
661
581
|
/**
|
|
662
582
|
* Update current step indicator
|
|
663
583
|
*/
|
|
664
584
|
updateCurrentStep() {
|
|
665
|
-
// Find the first running step
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
const child = findRunning(node.children);
|
|
672
|
-
if (child)
|
|
673
|
-
return child;
|
|
674
|
-
}
|
|
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;
|
|
675
591
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
this.currentStep = findRunning(this.executionTree);
|
|
592
|
+
});
|
|
593
|
+
this.currentStep = runningStep;
|
|
679
594
|
this.cdr.markForCheck();
|
|
680
595
|
}
|
|
681
596
|
/**
|
|
682
597
|
* Mark previously new nodes as completed
|
|
683
598
|
*/
|
|
684
599
|
markPreviouslyNewNodesAsComplete() {
|
|
685
|
-
//
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
//
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
componentRef.changeDetectorRef.detectChanges();
|
|
695
|
-
}
|
|
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();
|
|
696
609
|
}
|
|
697
|
-
}
|
|
610
|
+
});
|
|
698
611
|
}
|
|
699
612
|
/**
|
|
700
|
-
*
|
|
613
|
+
* Ensure only the last step in the execution order shows as running
|
|
701
614
|
*/
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
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
|
+
}
|
|
706
625
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
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();
|
|
711
639
|
}
|
|
712
|
-
}
|
|
713
|
-
return null;
|
|
640
|
+
});
|
|
714
641
|
}
|
|
715
642
|
/**
|
|
716
643
|
* Calculate execution statistics
|
|
@@ -726,67 +653,49 @@ export class AgentExecutionMonitorComponent {
|
|
|
726
653
|
totalDuration: 0,
|
|
727
654
|
totalPrompts: 0
|
|
728
655
|
};
|
|
729
|
-
//
|
|
730
|
-
if (this.
|
|
731
|
-
|
|
732
|
-
this.stats.
|
|
733
|
-
|
|
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
|
+
}
|
|
734
669
|
}
|
|
735
|
-
|
|
670
|
+
console.log('📈 Stats calculated:', {
|
|
671
|
+
totalSteps: this.stats.totalSteps,
|
|
672
|
+
totalTokens: this.stats.totalTokens,
|
|
673
|
+
totalCost: this.stats.totalCost
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Count steps recursively
|
|
678
|
+
*/
|
|
679
|
+
countSteps(steps) {
|
|
680
|
+
for (const step of steps) {
|
|
736
681
|
this.stats.totalSteps++;
|
|
737
|
-
|
|
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')
|
|
738
686
|
this.stats.completedSteps++;
|
|
739
|
-
if (
|
|
687
|
+
if (step.Status === 'Failed' || step.Status === 'Cancelled')
|
|
740
688
|
this.stats.failedSteps++;
|
|
741
|
-
// Only accumulate tokens if we don't have agent run data
|
|
742
|
-
if (!this.executionData?.agentRun) {
|
|
743
|
-
if (node.tokensUsed)
|
|
744
|
-
this.stats.totalTokens += node.tokensUsed;
|
|
745
|
-
if (node.cost)
|
|
746
|
-
this.stats.totalCost += node.cost;
|
|
747
|
-
}
|
|
748
|
-
if (node.duration)
|
|
749
|
-
this.stats.totalDuration += node.duration;
|
|
750
|
-
this.stats.stepsByType[node.type] = (this.stats.stepsByType[node.type] || 0) + 1;
|
|
751
689
|
// Count prompts
|
|
752
|
-
if (
|
|
753
|
-
this.stats.totalPrompts
|
|
690
|
+
if (step.StepType === 'Prompt') {
|
|
691
|
+
this.stats.totalPrompts++;
|
|
754
692
|
}
|
|
755
|
-
|
|
756
|
-
|
|
693
|
+
// Recurse for sub-agents
|
|
694
|
+
if (step.StepType === 'Sub-Agent' && step.SubAgentRun?.Steps) {
|
|
695
|
+
this.countSteps(step.SubAgentRun.Steps);
|
|
757
696
|
}
|
|
758
|
-
};
|
|
759
|
-
this.executionTree.forEach(node => processNode(node));
|
|
760
|
-
}
|
|
761
|
-
/**
|
|
762
|
-
* Toggle node expansion
|
|
763
|
-
*/
|
|
764
|
-
toggleNode(node) {
|
|
765
|
-
const nodeInTree = this.findNodeById(this.executionTree, node.id);
|
|
766
|
-
if (nodeInTree && this.hasExpandableContent(nodeInTree)) {
|
|
767
|
-
nodeInTree.expanded = !nodeInTree.expanded;
|
|
768
|
-
this.userHasInteracted = true;
|
|
769
|
-
// Update the component's node reference to trigger change detection
|
|
770
|
-
const componentRef = this.nodeComponentMap.get(node.id);
|
|
771
|
-
if (componentRef) {
|
|
772
|
-
componentRef.instance.node = { ...nodeInTree };
|
|
773
|
-
componentRef.changeDetectorRef.detectChanges();
|
|
774
|
-
}
|
|
775
|
-
this.cdr.markForCheck();
|
|
776
697
|
}
|
|
777
698
|
}
|
|
778
|
-
/**
|
|
779
|
-
* Check if node has expandable content
|
|
780
|
-
*/
|
|
781
|
-
hasExpandableContent(node) {
|
|
782
|
-
return !!(node.children && node.children.length > 0) ||
|
|
783
|
-
node.type === 'sub-agent' ||
|
|
784
|
-
node.type === 'action' ||
|
|
785
|
-
!!node.inputPreview ||
|
|
786
|
-
!!node.outputPreview ||
|
|
787
|
-
!!node.error ||
|
|
788
|
-
!!node.detailsMarkdown;
|
|
789
|
-
}
|
|
790
699
|
/**
|
|
791
700
|
* Handle scroll events
|
|
792
701
|
*/
|
|
@@ -877,6 +786,24 @@ export class AgentExecutionMonitorComponent {
|
|
|
877
786
|
getStepTypes() {
|
|
878
787
|
return Object.keys(this.stats.stepsByType).sort();
|
|
879
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
|
+
}
|
|
880
807
|
/**
|
|
881
808
|
* Format markdown content for display
|
|
882
809
|
*/
|
|
@@ -916,134 +843,52 @@ export class AgentExecutionMonitorComponent {
|
|
|
916
843
|
/**
|
|
917
844
|
* Append new live steps without re-rendering entire tree
|
|
918
845
|
*/
|
|
919
|
-
appendNewLiveSteps(
|
|
920
|
-
if (!
|
|
921
|
-
return;
|
|
922
|
-
// If view is not initialized yet, wait for it
|
|
923
|
-
if (!this.viewInitialized || !this.executionNodesContainer) {
|
|
846
|
+
appendNewLiveSteps(newSteps) {
|
|
847
|
+
if (!newSteps || newSteps.length === 0 || !this.viewInitialized || !this.executionNodesContainer) {
|
|
924
848
|
return;
|
|
925
849
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
});
|
|
932
|
-
// Create a map of existing nodes by ID for quick lookup
|
|
933
|
-
const nodeMap = new Map();
|
|
934
|
-
this.buildNodeMap(this.executionTree, nodeMap);
|
|
935
|
-
// First pass: identify which steps are truly NEW (not in processedStepIds)
|
|
936
|
-
const trulyNewStepIds = new Set();
|
|
937
|
-
for (const step of sortedSteps) {
|
|
938
|
-
const stepId = step.step?.ID || this.generateStepId(step);
|
|
939
|
-
if (!this.processedStepIds.has(stepId)) {
|
|
940
|
-
trulyNewStepIds.add(stepId);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
// Only mark previously new nodes as complete if we have truly new steps
|
|
944
|
-
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) {
|
|
945
855
|
this.markPreviouslyNewNodesAsComplete();
|
|
946
|
-
// Clear the set for new additions
|
|
947
856
|
this.newlyAddedNodeIds.clear();
|
|
948
857
|
}
|
|
949
|
-
let
|
|
858
|
+
let hasNewSteps = false;
|
|
950
859
|
for (const step of sortedSteps) {
|
|
951
|
-
//
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
// Mark as processed
|
|
958
|
-
this.processedStepIds.add(stepId);
|
|
959
|
-
// Check if this node already exists
|
|
960
|
-
const existingNode = nodeMap.get(stepId);
|
|
961
|
-
if (existingNode) {
|
|
962
|
-
// Update existing node in the component
|
|
963
|
-
const componentRef = this.nodeComponentMap.get(stepId);
|
|
964
|
-
if (componentRef) {
|
|
965
|
-
// Preserve current UI state
|
|
966
|
-
const currentExpanded = existingNode.expanded;
|
|
967
|
-
const currentDetailsExpanded = existingNode.detailsExpanded;
|
|
968
|
-
const updatedStatus = step.step?.Status ?
|
|
969
|
-
this.mapStepStatus(step.step.Status) :
|
|
970
|
-
(step.endTime || step.outputData ? 'completed' : existingNode.status);
|
|
971
|
-
if (existingNode.status !== updatedStatus) {
|
|
972
|
-
existingNode.status = updatedStatus;
|
|
973
|
-
}
|
|
974
|
-
if (step.endTime && !existingNode.endTime) {
|
|
975
|
-
existingNode.endTime = new Date(step.endTime);
|
|
976
|
-
existingNode.duration = step.durationMs;
|
|
977
|
-
}
|
|
978
|
-
// Update output data if available
|
|
979
|
-
if (step.outputData && !existingNode.outputPreview) {
|
|
980
|
-
existingNode.outputPreview = this.createPreview(step.outputData);
|
|
981
|
-
existingNode.tokensUsed = this.extractTokens(step.outputData);
|
|
982
|
-
existingNode.cost = this.extractCost(step.outputData);
|
|
983
|
-
}
|
|
984
|
-
// Restore UI state
|
|
985
|
-
existingNode.expanded = currentExpanded;
|
|
986
|
-
existingNode.detailsExpanded = currentDetailsExpanded;
|
|
987
|
-
// Update the component's node reference
|
|
988
|
-
// Important: We need to update the node object directly to ensure
|
|
989
|
-
// Angular detects changes to style bindings that depend on node.depth
|
|
990
|
-
const nodeInstance = componentRef.instance.node;
|
|
991
|
-
Object.assign(nodeInstance, existingNode);
|
|
992
|
-
// Mark for check to ensure Angular updates the view
|
|
993
|
-
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;
|
|
994
866
|
componentRef.changeDetectorRef.detectChanges();
|
|
995
867
|
}
|
|
868
|
+
continue;
|
|
996
869
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
// Find parent and add as child
|
|
1011
|
-
const parentNode = this.findParentNode(this.executionTree, newNode);
|
|
1012
|
-
if (parentNode) {
|
|
1013
|
-
if (!parentNode.children) {
|
|
1014
|
-
parentNode.children = [];
|
|
1015
|
-
}
|
|
1016
|
-
parentNode.children.push(newNode);
|
|
1017
|
-
// Update parent component to show new child
|
|
1018
|
-
const parentComponentRef = this.nodeComponentMap.get(parentNode.id);
|
|
1019
|
-
if (parentComponentRef) {
|
|
1020
|
-
// Update the parent node object directly
|
|
1021
|
-
const parentNodeInstance = parentComponentRef.instance.node;
|
|
1022
|
-
Object.assign(parentNodeInstance, parentNode);
|
|
1023
|
-
parentComponentRef.changeDetectorRef.markForCheck();
|
|
1024
|
-
parentComponentRef.changeDetectorRef.detectChanges();
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
else {
|
|
1028
|
-
// If no parent found, add as root
|
|
1029
|
-
this.executionTree.push(newNode);
|
|
1030
|
-
this.createNodeComponent(newNode, this.executionNodesContainer);
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
hasNewNodes = true;
|
|
1034
|
-
// Only track as newly added if it's truly new
|
|
1035
|
-
if (trulyNewStepIds.has(stepId)) {
|
|
1036
|
-
this.newlyAddedNodeIds.add(newNode.id);
|
|
1037
|
-
}
|
|
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);
|
|
1038
883
|
}
|
|
1039
884
|
}
|
|
1040
|
-
if (
|
|
1041
|
-
//
|
|
885
|
+
if (hasNewSteps) {
|
|
886
|
+
// After processing all new steps, ensure only the last one shows as running
|
|
887
|
+
this.ensureOnlyLastStepIsRunning();
|
|
1042
888
|
this.updateCurrentStep();
|
|
1043
|
-
// Recalculate stats
|
|
1044
889
|
this.calculateStats();
|
|
1045
|
-
//
|
|
1046
|
-
this.cdr.
|
|
890
|
+
// Force change detection to ensure new components are rendered
|
|
891
|
+
this.cdr.detectChanges();
|
|
1047
892
|
// Auto-scroll if user hasn't interacted
|
|
1048
893
|
setTimeout(() => {
|
|
1049
894
|
this.autoScrollToBottom();
|
|
@@ -1051,213 +896,20 @@ export class AgentExecutionMonitorComponent {
|
|
|
1051
896
|
}
|
|
1052
897
|
}
|
|
1053
898
|
/**
|
|
1054
|
-
*
|
|
899
|
+
* Calculate step depth based on parent hierarchy
|
|
1055
900
|
*/
|
|
1056
|
-
|
|
1057
|
-
//
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
step.depth || 0,
|
|
1061
|
-
step.step?.StepName || '',
|
|
1062
|
-
step.startTime || Date.now()
|
|
1063
|
-
];
|
|
1064
|
-
return `live-${parts.join('-').replace(/[^a-zA-Z0-9-]/g, '')}`;
|
|
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;
|
|
1065
905
|
}
|
|
1066
906
|
/**
|
|
1067
|
-
*
|
|
907
|
+
* Build agent path for a step
|
|
1068
908
|
*/
|
|
1069
|
-
|
|
1070
|
-
//
|
|
1071
|
-
//
|
|
1072
|
-
|
|
1073
|
-
const findParentRecursive = (searchNodes) => {
|
|
1074
|
-
// Process nodes in reverse order to find the most recent one
|
|
1075
|
-
for (let i = searchNodes.length - 1; i >= 0; i--) {
|
|
1076
|
-
const node = searchNodes[i];
|
|
1077
|
-
// Check if this node could be the parent based on depth
|
|
1078
|
-
if (node.depth === childNode.depth - 1) {
|
|
1079
|
-
// Special handling for sub-agent execution steps
|
|
1080
|
-
if (childNode.agentPath && childNode.agentPath.length > node.agentPath.length) {
|
|
1081
|
-
// This child belongs to a sub-agent
|
|
1082
|
-
// Look for the most recent sub-agent delegation node
|
|
1083
|
-
if (node.type === 'sub-agent' || node.name.includes('Delegating to')) {
|
|
1084
|
-
return node;
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
// Keep track of the most recent node at the correct depth
|
|
1088
|
-
if (!potentialParent) {
|
|
1089
|
-
potentialParent = node;
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
// Search children recursively
|
|
1093
|
-
if (node.children && node.children.length > 0) {
|
|
1094
|
-
const foundInChildren = findParentRecursive(node.children);
|
|
1095
|
-
if (foundInChildren)
|
|
1096
|
-
return foundInChildren;
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
return null;
|
|
1100
|
-
};
|
|
1101
|
-
const found = findParentRecursive(nodes);
|
|
1102
|
-
return found || potentialParent;
|
|
1103
|
-
}
|
|
1104
|
-
/**
|
|
1105
|
-
* Filter nodes based on display mode
|
|
1106
|
-
*/
|
|
1107
|
-
filterNodesForMode(nodes, mode) {
|
|
1108
|
-
return nodes.filter(node => {
|
|
1109
|
-
// If no displayMode is set, default to showing in both modes
|
|
1110
|
-
if (!node.displayMode) {
|
|
1111
|
-
return true;
|
|
1112
|
-
}
|
|
1113
|
-
// Show if displayMode is 'both' or matches current mode
|
|
1114
|
-
if (node.displayMode === 'both' || node.displayMode === mode) {
|
|
1115
|
-
// Filter children recursively
|
|
1116
|
-
if (node.children) {
|
|
1117
|
-
node.children = this.filterNodesForMode(node.children, mode);
|
|
1118
|
-
}
|
|
1119
|
-
return true;
|
|
1120
|
-
}
|
|
1121
|
-
return false;
|
|
1122
|
-
});
|
|
1123
|
-
}
|
|
1124
|
-
/**
|
|
1125
|
-
* Build a map of nodes by ID for quick lookup
|
|
1126
|
-
*/
|
|
1127
|
-
buildNodeMap(nodes, map) {
|
|
1128
|
-
for (const node of nodes) {
|
|
1129
|
-
map.set(node.id, node);
|
|
1130
|
-
if (node.children) {
|
|
1131
|
-
this.buildNodeMap(node.children, map);
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
/**
|
|
1136
|
-
* Create a single node from a live step
|
|
1137
|
-
*/
|
|
1138
|
-
createNodeFromLiveStep(step) {
|
|
1139
|
-
const stepName = step.step?.StepName || 'Processing...';
|
|
1140
|
-
let name = stepName;
|
|
1141
|
-
let detailsMarkdown;
|
|
1142
|
-
// Check if the step name contains markdown
|
|
1143
|
-
if (stepName.includes('\n') || stepName.includes('**') || stepName.includes('##')) {
|
|
1144
|
-
const lines = stepName.split('\n');
|
|
1145
|
-
name = lines[0].trim();
|
|
1146
|
-
if (lines.length > 1) {
|
|
1147
|
-
detailsMarkdown = lines.slice(1).join('\n').trim();
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
// For live steps, always start as running
|
|
1151
|
-
// The status will be updated to completed in markPreviouslyNewNodesAsComplete
|
|
1152
|
-
let status = 'running';
|
|
1153
|
-
if (step.step?.Status) {
|
|
1154
|
-
// Only use explicit status if it's failed
|
|
1155
|
-
const mappedStatus = this.mapStepStatus(step.step.Status);
|
|
1156
|
-
if (mappedStatus === 'failed') {
|
|
1157
|
-
status = 'failed';
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
// Determine display mode based on the step content
|
|
1161
|
-
let displayMode = 'both';
|
|
1162
|
-
// Progress messages that should only show in live mode
|
|
1163
|
-
if (name.includes('Executing') && name.includes('action') && !name.startsWith('Execute Action:')) {
|
|
1164
|
-
displayMode = 'live';
|
|
1165
|
-
}
|
|
1166
|
-
else if (name.includes("Executing agent's initial prompt")) {
|
|
1167
|
-
displayMode = 'live';
|
|
1168
|
-
}
|
|
1169
|
-
else if (name.includes('Re-executing agent prompt with additional context')) {
|
|
1170
|
-
displayMode = 'live';
|
|
1171
|
-
}
|
|
1172
|
-
// Check displayMode from metadata if available
|
|
1173
|
-
if (step.metadata?.displayMode) {
|
|
1174
|
-
displayMode = step.metadata.displayMode;
|
|
1175
|
-
}
|
|
1176
|
-
// Clear metadata storage before creating previews
|
|
1177
|
-
this.lastParsedAgentData = null;
|
|
1178
|
-
this.lastParsedActionData = null;
|
|
1179
|
-
// Create previews which will populate metadata if available
|
|
1180
|
-
const inputPreview = this.createPreview(step.inputData);
|
|
1181
|
-
const outputPreview = this.createPreview(step.outputData);
|
|
1182
|
-
const nodeType = this.mapStepType(step.executionType || step.step?.StepType);
|
|
1183
|
-
const node = {
|
|
1184
|
-
id: step.step?.ID || this.generateStepId(step),
|
|
1185
|
-
name: name,
|
|
1186
|
-
type: nodeType,
|
|
1187
|
-
status: status,
|
|
1188
|
-
startTime: step.startTime ? new Date(step.startTime) : new Date(),
|
|
1189
|
-
endTime: step.endTime ? new Date(step.endTime) : undefined,
|
|
1190
|
-
duration: step.durationMs,
|
|
1191
|
-
agentPath: step.agentHierarchy || [],
|
|
1192
|
-
expanded: false,
|
|
1193
|
-
depth: step.depth || 0,
|
|
1194
|
-
detailsMarkdown: detailsMarkdown,
|
|
1195
|
-
detailsExpanded: false,
|
|
1196
|
-
inputPreview: inputPreview,
|
|
1197
|
-
outputPreview: outputPreview,
|
|
1198
|
-
tokensUsed: this.extractTokens(step.outputData),
|
|
1199
|
-
cost: this.extractCost(step.outputData),
|
|
1200
|
-
displayMode: displayMode
|
|
1201
|
-
};
|
|
1202
|
-
// Add agent metadata if this is a sub-agent node
|
|
1203
|
-
if (nodeType === 'sub-agent' && this.lastParsedAgentData) {
|
|
1204
|
-
node.agentName = this.lastParsedAgentData.agentName;
|
|
1205
|
-
node.agentId = this.lastParsedAgentData.agentId;
|
|
1206
|
-
node.agentIconClass = this.lastParsedAgentData.agentIconClass;
|
|
1207
|
-
node.agentLogoURL = this.lastParsedAgentData.agentLogoURL;
|
|
1208
|
-
}
|
|
1209
|
-
// Add action metadata if this is an action node
|
|
1210
|
-
if (nodeType === 'action' && this.lastParsedActionData) {
|
|
1211
|
-
node.actionName = this.lastParsedActionData.actionName;
|
|
1212
|
-
node.actionId = this.lastParsedActionData.actionId;
|
|
1213
|
-
node.actionIconClass = this.lastParsedActionData.actionIconClass;
|
|
1214
|
-
}
|
|
1215
|
-
return node;
|
|
1216
|
-
}
|
|
1217
|
-
/**
|
|
1218
|
-
* Collect all node IDs in the tree
|
|
1219
|
-
*/
|
|
1220
|
-
collectNodeIds(nodes, ids) {
|
|
1221
|
-
for (const node of nodes) {
|
|
1222
|
-
ids.add(node.id);
|
|
1223
|
-
if (node.children) {
|
|
1224
|
-
this.collectNodeIds(node.children, ids);
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
/**
|
|
1229
|
-
* Preserve expanded state of all nodes
|
|
1230
|
-
*/
|
|
1231
|
-
preserveExpandedState(nodes, expandedState, detailsExpandedState) {
|
|
1232
|
-
for (const node of nodes) {
|
|
1233
|
-
if (node.expanded !== undefined) {
|
|
1234
|
-
expandedState.set(node.id, node.expanded);
|
|
1235
|
-
}
|
|
1236
|
-
if (node.detailsExpanded !== undefined) {
|
|
1237
|
-
detailsExpandedState.set(node.id, node.detailsExpanded);
|
|
1238
|
-
}
|
|
1239
|
-
if (node.children) {
|
|
1240
|
-
this.preserveExpandedState(node.children, expandedState, detailsExpandedState);
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
/**
|
|
1245
|
-
* Restore expanded state of all nodes
|
|
1246
|
-
*/
|
|
1247
|
-
restoreExpandedState(nodes, expandedState, detailsExpandedState) {
|
|
1248
|
-
for (const node of nodes) {
|
|
1249
|
-
const expanded = expandedState.get(node.id);
|
|
1250
|
-
if (expanded !== undefined) {
|
|
1251
|
-
node.expanded = expanded;
|
|
1252
|
-
}
|
|
1253
|
-
const detailsExpanded = detailsExpandedState.get(node.id);
|
|
1254
|
-
if (detailsExpanded !== undefined) {
|
|
1255
|
-
node.detailsExpanded = detailsExpanded;
|
|
1256
|
-
}
|
|
1257
|
-
if (node.children) {
|
|
1258
|
-
this.restoreExpandedState(node.children, expandedState, detailsExpandedState);
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
909
|
+
buildAgentPath(step) {
|
|
910
|
+
// In live mode, agent path should come from streaming data
|
|
911
|
+
// For now, return empty array
|
|
912
|
+
return [];
|
|
1261
913
|
}
|
|
1262
914
|
/**
|
|
1263
915
|
* Check if execution is complete
|
|
@@ -1270,27 +922,50 @@ export class AgentExecutionMonitorComponent {
|
|
|
1270
922
|
* Handle view run button click
|
|
1271
923
|
*/
|
|
1272
924
|
onViewRunClick() {
|
|
1273
|
-
if (this.
|
|
1274
|
-
this.viewRunClick.emit({ runId: this.
|
|
925
|
+
if (this.agentRun) {
|
|
926
|
+
this.viewRunClick.emit({ runId: this.agentRun.ID, runType: 'agent' });
|
|
1275
927
|
}
|
|
1276
928
|
}
|
|
1277
929
|
/**
|
|
1278
|
-
*
|
|
930
|
+
* Expands all nodes that have sub-agent children
|
|
1279
931
|
*/
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
return true;
|
|
932
|
+
expandAllNodes() {
|
|
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}`);
|
|
1291
942
|
}
|
|
943
|
+
});
|
|
944
|
+
// Trigger change detection
|
|
945
|
+
this.cdr.markForCheck();
|
|
946
|
+
this.cdr.detectChanges();
|
|
947
|
+
// Scroll to top after expansion
|
|
948
|
+
setTimeout(() => {
|
|
949
|
+
this.scrollToTop();
|
|
950
|
+
}, 100);
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Scroll the execution tree to the top
|
|
954
|
+
*/
|
|
955
|
+
scrollToTop() {
|
|
956
|
+
if (this.executionTreeContainer) {
|
|
957
|
+
const element = this.executionTreeContainer.nativeElement;
|
|
958
|
+
element.scrollTo({
|
|
959
|
+
top: 0,
|
|
960
|
+
behavior: 'smooth'
|
|
961
|
+
});
|
|
1292
962
|
}
|
|
1293
|
-
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Check if there is content to display
|
|
966
|
+
*/
|
|
967
|
+
hasContent() {
|
|
968
|
+
return this.nodeComponentMap.size > 0;
|
|
1294
969
|
}
|
|
1295
970
|
static { this.ɵfac = function AgentExecutionMonitorComponent_Factory(t) { return new (t || AgentExecutionMonitorComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); }; }
|
|
1296
971
|
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AgentExecutionMonitorComponent, selectors: [["mj-agent-execution-monitor"]], viewQuery: function AgentExecutionMonitorComponent_Query(rf, ctx) { if (rf & 1) {
|
|
@@ -1300,7 +975,7 @@ export class AgentExecutionMonitorComponent {
|
|
|
1300
975
|
let _t;
|
|
1301
976
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.executionTreeContainer = _t.first);
|
|
1302
977
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.executionNodesContainer = _t.first);
|
|
1303
|
-
} }, 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) {
|
|
1304
979
|
const _r1 = i0.ɵɵgetCurrentView();
|
|
1305
980
|
i0.ɵɵelementStart(0, "div", 2)(1, "div", 3)(2, "div", 4);
|
|
1306
981
|
i0.ɵɵelement(3, "i", 5);
|
|
@@ -1309,13 +984,13 @@ export class AgentExecutionMonitorComponent {
|
|
|
1309
984
|
i0.ɵɵelementEnd();
|
|
1310
985
|
i0.ɵɵtemplate(6, AgentExecutionMonitorComponent_Conditional_6_Template, 3, 0, "span", 6);
|
|
1311
986
|
i0.ɵɵelementEnd();
|
|
1312
|
-
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);
|
|
1313
988
|
i0.ɵɵelementEnd();
|
|
1314
989
|
i0.ɵɵelementStart(9, "div", 9, 0);
|
|
1315
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()); });
|
|
1316
|
-
i0.ɵɵ
|
|
991
|
+
i0.ɵɵelement(11, "div", 10, 1);
|
|
1317
992
|
i0.ɵɵtemplate(13, AgentExecutionMonitorComponent_Conditional_13_Template, 4, 0, "div", 11);
|
|
1318
|
-
i0.ɵɵelementEnd()
|
|
993
|
+
i0.ɵɵelementEnd();
|
|
1319
994
|
i0.ɵɵelementStart(14, "div", 12)(15, "div", 13)(16, "div", 14)(17, "span", 15);
|
|
1320
995
|
i0.ɵɵtext(18, "Steps");
|
|
1321
996
|
i0.ɵɵelementEnd();
|
|
@@ -1352,11 +1027,11 @@ export class AgentExecutionMonitorComponent {
|
|
|
1352
1027
|
i0.ɵɵadvance();
|
|
1353
1028
|
i0.ɵɵconditional(ctx.currentStep && ctx.mode === "live" ? 7 : -1);
|
|
1354
1029
|
i0.ɵɵadvance();
|
|
1355
|
-
i0.ɵɵconditional(ctx.mode === "historical" && ctx.
|
|
1030
|
+
i0.ɵɵconditional(ctx.mode === "historical" && ctx.agentRun && ctx.isExecutionComplete() ? 8 : -1);
|
|
1356
1031
|
i0.ɵɵadvance();
|
|
1357
|
-
i0.ɵɵclassProp("has-content", ctx.
|
|
1032
|
+
i0.ɵɵclassProp("has-content", ctx.hasContent());
|
|
1358
1033
|
i0.ɵɵadvance(4);
|
|
1359
|
-
i0.ɵɵconditional(ctx.
|
|
1034
|
+
i0.ɵɵconditional(!ctx.agentRun && ctx.liveSteps.length === 0 ? 13 : -1);
|
|
1360
1035
|
i0.ɵɵadvance(7);
|
|
1361
1036
|
i0.ɵɵtextInterpolate2(" ", ctx.stats.completedSteps, "/", ctx.stats.totalSteps, " ");
|
|
1362
1037
|
i0.ɵɵadvance();
|
|
@@ -1371,11 +1046,11 @@ export class AgentExecutionMonitorComponent {
|
|
|
1371
1046
|
i0.ɵɵconditional(ctx.stats.totalDuration ? 37 : -1);
|
|
1372
1047
|
i0.ɵɵadvance();
|
|
1373
1048
|
i0.ɵɵconditional(ctx.getStepTypes().length > 0 ? 38 : -1);
|
|
1374
|
-
} }, 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 .node-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n background: #f8f9fa;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.2s ease;\n user-select: none;\n }\n\n .node-header[_ngcontent-%COMP%]:hover {\n background: #e8f0fe;\n border-color: #2196f3;\n }\n\n .expand-icon[_ngcontent-%COMP%], .expand-spacer[_ngcontent-%COMP%] {\n width: 16px;\n text-align: center;\n color: #666;\n font-size: 10px;\n }\n\n .status-icon[_ngcontent-%COMP%] {\n width: 20px;\n text-align: center;\n font-size: 14px;\n }\n\n .status-pending[_ngcontent-%COMP%] { color: #999; }\n .status-running[_ngcontent-%COMP%] { color: #2196f3; }\n .status-completed[_ngcontent-%COMP%] { color: #4caf50; }\n .status-failed[_ngcontent-%COMP%] { color: #f44336; }\n\n .type-icon[_ngcontent-%COMP%] {\n width: 20px;\n text-align: center;\n font-size: 14px;\n color: #666;\n }\n\n .node-name[_ngcontent-%COMP%] {\n flex: 1;\n font-size: 13px;\n font-weight: 500;\n color: #1a1a1a;\n }\n\n .node-duration[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #666;\n font-weight: 500;\n }\n\n .node-metrics[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n font-size: 12px;\n }\n\n .tokens[_ngcontent-%COMP%] {\n color: #666;\n }\n\n .cost[_ngcontent-%COMP%] {\n color: #2196f3;\n font-weight: 500;\n }\n\n \n\n .node-details[_ngcontent-%COMP%] {\n margin: 8px 0 8px 44px;\n padding: 12px;\n background: #f5f5f5;\n border-radius: 4px;\n font-size: 12px;\n }\n\n .detail-section[_ngcontent-%COMP%] {\n margin-bottom: 8px;\n }\n\n .detail-section[_ngcontent-%COMP%]:last-child {\n margin-bottom: 0;\n }\n\n .detail-section.error[_ngcontent-%COMP%] {\n color: #d32f2f;\n }\n\n .detail-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-weight: 600;\n margin-bottom: 4px;\n color: #666;\n }\n\n .detail-section.error[_ngcontent-%COMP%] .detail-label[_ngcontent-%COMP%] {\n color: #d32f2f;\n }\n\n .detail-label[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n }\n\n .detail-content[_ngcontent-%COMP%] {\n padding-left: 18px;\n color: #333;\n line-height: 1.4;\n white-space: pre-wrap;\n word-break: break-word;\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 .details-toggle[_ngcontent-%COMP%] {\n margin-left: 8px;\n padding: 2px 6px;\n background: transparent;\n border: 1px solid #ddd;\n border-radius: 3px;\n cursor: pointer;\n font-size: 10px;\n color: #666;\n transition: all 0.2s ease;\n }\n \n .details-toggle[_ngcontent-%COMP%]:hover {\n background: #f0f0f0;\n border-color: #2196f3;\n color: #2196f3;\n }\n \n \n\n .markdown-details[_ngcontent-%COMP%] {\n margin: 8px 0 8px 44px;\n padding: 12px;\n background: #f9fafb;\n border-left: 3px solid #2196f3;\n border-radius: 0 4px 4px 0;\n font-size: 13px;\n line-height: 1.6;\n }\n \n .markdown-details[_ngcontent-%COMP%] h2[_ngcontent-%COMP%] {\n font-size: 16px;\n margin: 0 0 8px 0;\n color: #1a1a1a;\n }\n \n .markdown-details[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n font-size: 14px;\n margin: 8px 0 6px 0;\n color: #333;\n }\n \n .markdown-details[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n font-size: 13px;\n margin: 6px 0 4px 0;\n color: #555;\n }\n \n .markdown-details[_ngcontent-%COMP%] ul[_ngcontent-%COMP%] {\n margin: 4px 0;\n padding-left: 20px;\n }\n \n .markdown-details[_ngcontent-%COMP%] li[_ngcontent-%COMP%] {\n margin: 2px 0;\n }\n \n .markdown-details[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n background: #e8f0fe;\n padding: 1px 4px;\n border-radius: 3px;\n font-family: 'Consolas', 'Monaco', monospace;\n font-size: 12px;\n }\n \n .markdown-details[_ngcontent-%COMP%] pre[_ngcontent-%COMP%] {\n background: #f5f5f5;\n padding: 8px;\n border-radius: 4px;\n overflow-x: auto;\n margin: 8px 0;\n }\n \n .markdown-details[_ngcontent-%COMP%] pre[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n background: none;\n padding: 0;\n }\n \n .markdown-details[_ngcontent-%COMP%] strong[_ngcontent-%COMP%] {\n font-weight: 600;\n color: #1a1a1a;\n }\n \n .markdown-details[_ngcontent-%COMP%] em[_ngcontent-%COMP%] {\n font-style: italic;\n color: #555;\n }\n\n \n\n .execution-history-toggle[_ngcontent-%COMP%] {\n background: transparent;\n border: none;\n padding: 4px 8px;\n margin-left: 4px;\n cursor: pointer;\n color: #666;\n font-size: 12px;\n border-radius: 3px;\n transition: all 0.2s ease;\n }\n \n .execution-history-toggle[_ngcontent-%COMP%]:hover {\n background: #e3f2fd;\n color: #2196f3;\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\n .node-header[_ngcontent-%COMP%] {\n padding: 6px 8px;\n gap: 6px;\n }\n\n .node-metrics[_ngcontent-%COMP%] {\n display: none;\n }\n \n .markdown-details[_ngcontent-%COMP%] {\n margin-left: 20px;\n font-size: 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 }); }
|
|
1375
1050
|
}
|
|
1376
1051
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AgentExecutionMonitorComponent, [{
|
|
1377
1052
|
type: Component,
|
|
1378
|
-
args: [{ selector: 'mj-agent-execution-monitor',
|
|
1053
|
+
args: [{ selector: 'mj-agent-execution-monitor', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1379
1054
|
<div class="execution-monitor" [class.live-mode]="mode === 'live'">
|
|
1380
1055
|
<!-- Header -->
|
|
1381
1056
|
<div class="monitor-header">
|
|
@@ -1392,11 +1067,10 @@ export class AgentExecutionMonitorComponent {
|
|
|
1392
1067
|
@if (currentStep && mode === 'live') {
|
|
1393
1068
|
<div class="current-status">
|
|
1394
1069
|
<span class="status-label">Current:</span>
|
|
1395
|
-
<span class="
|
|
1396
|
-
<span class="step-name">{{ currentStep.name }}</span>
|
|
1070
|
+
<span class="step-name">{{ currentStep.StepName }}</span>
|
|
1397
1071
|
</div>
|
|
1398
1072
|
}
|
|
1399
|
-
@if (mode === 'historical' &&
|
|
1073
|
+
@if (mode === 'historical' && agentRun && isExecutionComplete()) {
|
|
1400
1074
|
<button class="view-run-btn" (click)="onViewRunClick()">
|
|
1401
1075
|
<i class="fa-solid fa-external-link-alt"></i>
|
|
1402
1076
|
View {{ runType === 'agent' ? 'Agent' : 'Prompt' }} Run
|
|
@@ -1407,18 +1081,18 @@ export class AgentExecutionMonitorComponent {
|
|
|
1407
1081
|
<!-- Execution Tree -->
|
|
1408
1082
|
<div class="execution-tree"
|
|
1409
1083
|
#executionTreeContainer
|
|
1410
|
-
[class.has-content]="
|
|
1084
|
+
[class.has-content]="hasContent()"
|
|
1411
1085
|
(scroll)="onScroll($event)"
|
|
1412
1086
|
(click)="onUserInteraction()">
|
|
1413
1087
|
<!-- Always render the container, but show empty state when no data -->
|
|
1414
1088
|
<div #executionNodesContainer class="nodes-container">
|
|
1415
|
-
@if (executionTree.length === 0) {
|
|
1416
|
-
<div class="empty-state">
|
|
1417
|
-
<i class="fa-solid fa-hourglass-start"></i>
|
|
1418
|
-
<p>Waiting for execution to begin...</p>
|
|
1419
|
-
</div>
|
|
1420
|
-
}
|
|
1421
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
|
+
}
|
|
1422
1096
|
</div>
|
|
1423
1097
|
|
|
1424
1098
|
<!-- Statistics Footer -->
|
|
@@ -1456,17 +1130,19 @@ export class AgentExecutionMonitorComponent {
|
|
|
1456
1130
|
<div class="step-types">
|
|
1457
1131
|
@for (type of getStepTypes(); track type) {
|
|
1458
1132
|
<span class="type-badge">
|
|
1459
|
-
{{ stats.stepsByType[type] }} {{ type }}
|
|
1133
|
+
{{ stats.stepsByType[type] }} {{ pluralizeStepType(type, stats.stepsByType[type]) }}
|
|
1460
1134
|
</span>
|
|
1461
1135
|
}
|
|
1462
1136
|
</div>
|
|
1463
1137
|
}
|
|
1464
1138
|
</div>
|
|
1465
1139
|
</div>
|
|
1466
|
-
`, 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 */\n .tree-node {\n margin-bottom: 4px;\n }\n\n .node-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n background: #f8f9fa;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.2s ease;\n user-select: none;\n }\n\n .node-header:hover {\n background: #e8f0fe;\n border-color: #2196f3;\n }\n\n .expand-icon, .expand-spacer {\n width: 16px;\n text-align: center;\n color: #666;\n font-size: 10px;\n }\n\n .status-icon {\n width: 20px;\n text-align: center;\n font-size: 14px;\n }\n\n .status-pending { color: #999; }\n .status-running { color: #2196f3; }\n .status-completed { color: #4caf50; }\n .status-failed { color: #f44336; }\n\n .type-icon {\n width: 20px;\n text-align: center;\n font-size: 14px;\n color: #666;\n }\n\n .node-name {\n flex: 1;\n font-size: 13px;\n font-weight: 500;\n color: #1a1a1a;\n }\n\n .node-duration {\n font-size: 12px;\n color: #666;\n font-weight: 500;\n }\n\n .node-metrics {\n display: flex;\n gap: 12px;\n font-size: 12px;\n }\n\n .tokens {\n color: #666;\n }\n\n .cost {\n color: #2196f3;\n font-weight: 500;\n }\n\n /* Node Details */\n .node-details {\n margin: 8px 0 8px 44px;\n padding: 12px;\n background: #f5f5f5;\n border-radius: 4px;\n font-size: 12px;\n }\n\n .detail-section {\n margin-bottom: 8px;\n }\n\n .detail-section:last-child {\n margin-bottom: 0;\n }\n\n .detail-section.error {\n color: #d32f2f;\n }\n\n .detail-label {\n display: flex;\n align-items: center;\n gap: 6px;\n font-weight: 600;\n margin-bottom: 4px;\n color: #666;\n }\n\n .detail-section.error .detail-label {\n color: #d32f2f;\n }\n\n .detail-label i {\n font-size: 12px;\n }\n\n .detail-content {\n padding-left: 18px;\n color: #333;\n line-height: 1.4;\n white-space: pre-wrap;\n word-break: break-word;\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 /* Details Toggle Button */\n .details-toggle {\n margin-left: 8px;\n padding: 2px 6px;\n background: transparent;\n border: 1px solid #ddd;\n border-radius: 3px;\n cursor: pointer;\n font-size: 10px;\n color: #666;\n transition: all 0.2s ease;\n }\n \n .details-toggle:hover {\n background: #f0f0f0;\n border-color: #2196f3;\n color: #2196f3;\n }\n \n /* Markdown Details Section */\n .markdown-details {\n margin: 8px 0 8px 44px;\n padding: 12px;\n background: #f9fafb;\n border-left: 3px solid #2196f3;\n border-radius: 0 4px 4px 0;\n font-size: 13px;\n line-height: 1.6;\n }\n \n .markdown-details h2 {\n font-size: 16px;\n margin: 0 0 8px 0;\n color: #1a1a1a;\n }\n \n .markdown-details h3 {\n font-size: 14px;\n margin: 8px 0 6px 0;\n color: #333;\n }\n \n .markdown-details h4 {\n font-size: 13px;\n margin: 6px 0 4px 0;\n color: #555;\n }\n \n .markdown-details ul {\n margin: 4px 0;\n padding-left: 20px;\n }\n \n .markdown-details li {\n margin: 2px 0;\n }\n \n .markdown-details code {\n background: #e8f0fe;\n padding: 1px 4px;\n border-radius: 3px;\n font-family: 'Consolas', 'Monaco', monospace;\n font-size: 12px;\n }\n \n .markdown-details pre {\n background: #f5f5f5;\n padding: 8px;\n border-radius: 4px;\n overflow-x: auto;\n margin: 8px 0;\n }\n \n .markdown-details pre code {\n background: none;\n padding: 0;\n }\n \n .markdown-details strong {\n font-weight: 600;\n color: #1a1a1a;\n }\n \n .markdown-details em {\n font-style: italic;\n color: #555;\n }\n\n /* Button styling for execution history toggle */\n .execution-history-toggle {\n background: transparent;\n border: none;\n padding: 4px 8px;\n margin-left: 4px;\n cursor: pointer;\n color: #666;\n font-size: 12px;\n border-radius: 3px;\n transition: all 0.2s ease;\n }\n \n .execution-history-toggle:hover {\n background: #e3f2fd;\n color: #2196f3;\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 .node-header {\n padding: 6px 8px;\n gap: 6px;\n }\n\n .node-metrics {\n display: none;\n }\n \n .markdown-details {\n margin-left: 20px;\n font-size: 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 "] }]
|
|
1467
1141
|
}], () => [{ type: i0.ChangeDetectorRef }], { mode: [{
|
|
1468
1142
|
type: Input
|
|
1469
|
-
}],
|
|
1143
|
+
}], agentRun: [{
|
|
1144
|
+
type: Input
|
|
1145
|
+
}], liveSteps: [{
|
|
1470
1146
|
type: Input
|
|
1471
1147
|
}], autoExpand: [{
|
|
1472
1148
|
type: Input
|
|
@@ -1483,5 +1159,5 @@ export class AgentExecutionMonitorComponent {
|
|
|
1483
1159
|
type: ViewChild,
|
|
1484
1160
|
args: ['executionNodesContainer', { read: ViewContainerRef }]
|
|
1485
1161
|
}] }); })();
|
|
1486
|
-
(() => { (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 }); })();
|
|
1487
1163
|
//# sourceMappingURL=agent-execution-monitor.component.js.map
|