@memberjunction/ng-core-entity-forms 2.54.0 → 2.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +1 -206
  2. package/dist/lib/custom/AIAgents/ai-agent-form.component.d.ts +8 -5
  3. package/dist/lib/custom/AIAgents/ai-agent-form.component.d.ts.map +1 -1
  4. package/dist/lib/custom/AIAgents/ai-agent-form.component.js +202 -220
  5. package/dist/lib/custom/AIAgents/ai-agent-form.component.js.map +1 -1
  6. package/dist/lib/custom/AIAgents/new-agent-dialog.component.d.ts +36 -0
  7. package/dist/lib/custom/AIAgents/new-agent-dialog.component.d.ts.map +1 -0
  8. package/dist/lib/custom/AIAgents/new-agent-dialog.component.js +325 -0
  9. package/dist/lib/custom/AIAgents/new-agent-dialog.component.js.map +1 -0
  10. package/dist/lib/custom/AIAgents/new-agent-dialog.service.d.ts +41 -0
  11. package/dist/lib/custom/AIAgents/new-agent-dialog.service.d.ts.map +1 -0
  12. package/dist/lib/custom/AIAgents/new-agent-dialog.service.js +87 -0
  13. package/dist/lib/custom/AIAgents/new-agent-dialog.service.js.map +1 -0
  14. package/dist/lib/custom/AIPrompts/ai-prompt-form.component.d.ts +5 -2
  15. package/dist/lib/custom/AIPrompts/ai-prompt-form.component.d.ts.map +1 -1
  16. package/dist/lib/custom/AIPrompts/ai-prompt-form.component.js +501 -485
  17. package/dist/lib/custom/AIPrompts/ai-prompt-form.component.js.map +1 -1
  18. package/dist/lib/custom/Actions/action-execution-log-form.component.d.ts +1 -0
  19. package/dist/lib/custom/Actions/action-execution-log-form.component.d.ts.map +1 -1
  20. package/dist/lib/custom/Actions/action-execution-log-form.component.js +4 -0
  21. package/dist/lib/custom/Actions/action-execution-log-form.component.js.map +1 -1
  22. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts +5 -4
  23. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts.map +1 -1
  24. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js +150 -72
  25. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js.map +1 -1
  26. package/dist/lib/custom/custom-forms.module.d.ts +31 -35
  27. package/dist/lib/custom/custom-forms.module.d.ts.map +1 -1
  28. package/dist/lib/custom/custom-forms.module.js +23 -43
  29. package/dist/lib/custom/custom-forms.module.js.map +1 -1
  30. package/dist/lib/generated/Entities/AIAgent/aiagent.form.component.js +11 -12
  31. package/dist/lib/generated/Entities/AIAgent/aiagent.form.component.js.map +1 -1
  32. package/dist/lib/generated/Entities/AIAgentRun/aiagentrun.form.component.js +7 -8
  33. package/dist/lib/generated/Entities/AIAgentRun/aiagentrun.form.component.js.map +1 -1
  34. package/dist/lib/generated/Entities/AIConfiguration/aiconfiguration.form.component.js +7 -8
  35. package/dist/lib/generated/Entities/AIConfiguration/aiconfiguration.form.component.js.map +1 -1
  36. package/dist/lib/generated/Entities/AIModel/aimodel.form.component.js +7 -8
  37. package/dist/lib/generated/Entities/AIModel/aimodel.form.component.js.map +1 -1
  38. package/dist/lib/generated/Entities/AIModelType/aimodeltype.form.component.js +11 -12
  39. package/dist/lib/generated/Entities/AIModelType/aimodeltype.form.component.js.map +1 -1
  40. package/dist/lib/generated/Entities/AIPrompt/aiprompt.form.component.js +11 -12
  41. package/dist/lib/generated/Entities/AIPrompt/aiprompt.form.component.js.map +1 -1
  42. package/dist/lib/generated/Entities/AIPromptRun/aipromptrun.form.component.js +11 -12
  43. package/dist/lib/generated/Entities/AIPromptRun/aipromptrun.form.component.js.map +1 -1
  44. package/dist/lib/generated/Entities/AIPromptRun/sections/details.component.d.ts.map +1 -1
  45. package/dist/lib/generated/Entities/AIPromptRun/sections/details.component.js +13 -4
  46. package/dist/lib/generated/Entities/AIPromptRun/sections/details.component.js.map +1 -1
  47. package/dist/lib/generated/Entities/AIVendor/aivendor.form.component.js +11 -12
  48. package/dist/lib/generated/Entities/AIVendor/aivendor.form.component.js.map +1 -1
  49. package/dist/public-api.d.ts +2 -3
  50. package/dist/public-api.d.ts.map +1 -1
  51. package/dist/public-api.js +3 -16
  52. package/dist/public-api.js.map +1 -1
  53. package/package.json +13 -12
  54. package/dist/lib/custom/ai-test-harness/agent-execution-monitor.component.d.ts +0 -273
  55. package/dist/lib/custom/ai-test-harness/agent-execution-monitor.component.d.ts.map +0 -1
  56. package/dist/lib/custom/ai-test-harness/agent-execution-monitor.component.js +0 -1429
  57. package/dist/lib/custom/ai-test-harness/agent-execution-monitor.component.js.map +0 -1
  58. package/dist/lib/custom/ai-test-harness/agent-execution-node.component.d.ts +0 -19
  59. package/dist/lib/custom/ai-test-harness/agent-execution-node.component.d.ts.map +0 -1
  60. package/dist/lib/custom/ai-test-harness/agent-execution-node.component.js +0 -458
  61. package/dist/lib/custom/ai-test-harness/agent-execution-node.component.js.map +0 -1
  62. package/dist/lib/custom/ai-test-harness/ai-test-harness-dialog.component.d.ts +0 -78
  63. package/dist/lib/custom/ai-test-harness/ai-test-harness-dialog.component.d.ts.map +0 -1
  64. package/dist/lib/custom/ai-test-harness/ai-test-harness-dialog.component.js +0 -157
  65. package/dist/lib/custom/ai-test-harness/ai-test-harness-dialog.component.js.map +0 -1
  66. package/dist/lib/custom/ai-test-harness/ai-test-harness.component.d.ts +0 -631
  67. package/dist/lib/custom/ai-test-harness/ai-test-harness.component.d.ts.map +0 -1
  68. package/dist/lib/custom/ai-test-harness/ai-test-harness.component.js +0 -3380
  69. package/dist/lib/custom/ai-test-harness/ai-test-harness.component.js.map +0 -1
  70. package/dist/lib/custom/ai-test-harness/json-viewer-window.component.d.ts +0 -8
  71. package/dist/lib/custom/ai-test-harness/json-viewer-window.component.d.ts.map +0 -1
  72. package/dist/lib/custom/ai-test-harness/json-viewer-window.component.js +0 -57
  73. package/dist/lib/custom/ai-test-harness/json-viewer-window.component.js.map +0 -1
  74. package/dist/lib/custom/test-harness-dialog.service.d.ts +0 -111
  75. package/dist/lib/custom/test-harness-dialog.service.d.ts.map +0 -1
  76. package/dist/lib/custom/test-harness-dialog.service.js +0 -211
  77. package/dist/lib/custom/test-harness-dialog.service.js.map +0 -1
@@ -1,1429 +0,0 @@
1
- import { Component, Input, ViewChild, ChangeDetectionStrategy, ViewContainerRef, Output, EventEmitter } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { Subject, interval } from 'rxjs';
4
- import { takeUntil } from 'rxjs/operators';
5
- import { ExecutionNodeComponent } from './agent-execution-node.component';
6
- import * as i0 from "@angular/core";
7
- const _c0 = ["executionTreeContainer"];
8
- const _c1 = ["executionNodesContainer"];
9
- function AgentExecutionMonitorComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
10
- i0.ɵɵelementStart(0, "span", 6);
11
- i0.ɵɵelement(1, "span", 19);
12
- i0.ɵɵtext(2, " LIVE ");
13
- i0.ɵɵelementEnd();
14
- } }
15
- function AgentExecutionMonitorComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
16
- i0.ɵɵelementStart(0, "div", 7)(1, "span", 20);
17
- i0.ɵɵtext(2, "Current:");
18
- i0.ɵɵelementEnd();
19
- i0.ɵɵelementStart(3, "span", 21);
20
- i0.ɵɵtext(4);
21
- i0.ɵɵelementEnd();
22
- i0.ɵɵelementStart(5, "span", 22);
23
- i0.ɵɵtext(6);
24
- i0.ɵɵelementEnd()();
25
- } if (rf & 2) {
26
- const ctx_r1 = i0.ɵɵnextContext();
27
- i0.ɵɵadvance(4);
28
- i0.ɵɵtextInterpolate(ctx_r1.formatAgentPath(ctx_r1.currentStep.agentPath));
29
- i0.ɵɵadvance(2);
30
- i0.ɵɵtextInterpolate(ctx_r1.currentStep.name);
31
- } }
32
- function AgentExecutionMonitorComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
33
- const _r3 = i0.ɵɵgetCurrentView();
34
- i0.ɵɵelementStart(0, "button", 23);
35
- i0.ɵɵlistener("click", function AgentExecutionMonitorComponent_Conditional_8_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onViewRunClick()); });
36
- i0.ɵɵelement(1, "i", 24);
37
- i0.ɵɵtext(2);
38
- i0.ɵɵelementEnd();
39
- } if (rf & 2) {
40
- const ctx_r1 = i0.ɵɵnextContext();
41
- i0.ɵɵadvance(2);
42
- i0.ɵɵtextInterpolate1(" View ", ctx_r1.runType === "agent" ? "Agent" : "Prompt", " Run ");
43
- } }
44
- function AgentExecutionMonitorComponent_Conditional_13_Template(rf, ctx) { if (rf & 1) {
45
- i0.ɵɵelementStart(0, "div", 11);
46
- i0.ɵɵelement(1, "i", 25);
47
- i0.ɵɵelementStart(2, "p");
48
- i0.ɵɵtext(3, "Waiting for execution to begin...");
49
- i0.ɵɵelementEnd()();
50
- } }
51
- function AgentExecutionMonitorComponent_Conditional_21_Template(rf, ctx) { if (rf & 1) {
52
- i0.ɵɵelementStart(0, "span", 17);
53
- i0.ɵɵtext(1);
54
- i0.ɵɵelementEnd();
55
- } if (rf & 2) {
56
- const ctx_r1 = i0.ɵɵnextContext();
57
- i0.ɵɵadvance();
58
- i0.ɵɵtextInterpolate1("(", ctx_r1.stats.failedSteps, " failed)");
59
- } }
60
- function AgentExecutionMonitorComponent_Conditional_37_Template(rf, ctx) { if (rf & 1) {
61
- i0.ɵɵelementStart(0, "div", 14)(1, "span", 15);
62
- i0.ɵɵtext(2, "Duration");
63
- i0.ɵɵelementEnd();
64
- i0.ɵɵelementStart(3, "span", 16);
65
- i0.ɵɵtext(4);
66
- i0.ɵɵelementEnd()();
67
- } if (rf & 2) {
68
- const ctx_r1 = i0.ɵɵnextContext();
69
- i0.ɵɵadvance(4);
70
- i0.ɵɵtextInterpolate(ctx_r1.formatDuration(ctx_r1.stats.totalDuration));
71
- } }
72
- function AgentExecutionMonitorComponent_Conditional_38_For_2_Template(rf, ctx) { if (rf & 1) {
73
- i0.ɵɵelementStart(0, "span", 26);
74
- i0.ɵɵtext(1);
75
- i0.ɵɵelementEnd();
76
- } if (rf & 2) {
77
- const type_r4 = ctx.$implicit;
78
- const ctx_r1 = i0.ɵɵnextContext(2);
79
- i0.ɵɵadvance();
80
- i0.ɵɵtextInterpolate2(" ", ctx_r1.stats.stepsByType[type_r4], " ", type_r4, " ");
81
- } }
82
- function AgentExecutionMonitorComponent_Conditional_38_Template(rf, ctx) { if (rf & 1) {
83
- i0.ɵɵelementStart(0, "div", 18);
84
- i0.ɵɵrepeaterCreate(1, AgentExecutionMonitorComponent_Conditional_38_For_2_Template, 2, 2, "span", 26, i0.ɵɵrepeaterTrackByIdentity);
85
- i0.ɵɵelementEnd();
86
- } if (rf & 2) {
87
- const ctx_r1 = i0.ɵɵnextContext();
88
- i0.ɵɵadvance();
89
- i0.ɵɵrepeater(ctx_r1.getStepTypes());
90
- } }
91
- /**
92
- * AgentExecutionMonitor Component
93
- *
94
- * A reusable component for visualizing AI agent execution flow in real-time or historically.
95
- * Displays a hierarchical tree of execution steps with status, timing, and preview information.
96
- *
97
- * Features:
98
- * - Real-time updates for live executions
99
- * - Historical playback for completed executions
100
- * - Collapsible tree structure
101
- * - Step status indicators with animations
102
- * - Execution statistics
103
- * - Token usage and cost tracking
104
- * - Error display
105
- * - Input/output preview
106
- */
107
- export class AgentExecutionMonitorComponent {
108
- constructor(cdr) {
109
- this.cdr = cdr;
110
- this.mode = 'historical';
111
- this.executionData = null; // Can be live updates or historical data
112
- this.autoExpand = true; // Auto-expand nodes in live mode
113
- this.runId = null; // ID of the run (agent or prompt)
114
- this.runType = 'agent'; // Type of run
115
- this.viewRunClick = new EventEmitter();
116
- this.executionTree = [];
117
- this.currentStep = null;
118
- // Track component references for dynamic components
119
- this.nodeComponentMap = new Map();
120
- this.stats = {
121
- totalSteps: 0,
122
- completedSteps: 0,
123
- failedSteps: 0,
124
- totalTokens: 0,
125
- totalCost: 0,
126
- stepsByType: {},
127
- totalPrompts: 0
128
- };
129
- // User interaction tracking
130
- this.userHasInteracted = false;
131
- this.userHasScrolled = false;
132
- this.isAutoScrolling = false;
133
- this.lastScrollPosition = 0;
134
- this.scrollThreshold = 50; // pixels from bottom to consider "at bottom"
135
- // View initialization flag
136
- this.viewInitialized = false;
137
- // Track processed step IDs to avoid duplication
138
- this.processedStepIds = new Set();
139
- // Track which nodes were added in the current update
140
- this.newlyAddedNodeIds = new Set();
141
- this.destroy$ = new Subject();
142
- }
143
- ngOnChanges(changes) {
144
- if (changes['executionData']) {
145
- // Check if we're starting a new live execution (empty liveSteps array)
146
- if (this.mode === 'live' &&
147
- this.executionData &&
148
- this.executionData.liveSteps &&
149
- this.executionData.liveSteps.length === 0 &&
150
- this.executionTree.length > 0) {
151
- // Clear everything for new run
152
- this.processedStepIds.clear();
153
- this.newlyAddedNodeIds.clear();
154
- this.clearNodeComponents();
155
- this.executionTree = [];
156
- this.currentStep = null;
157
- this.calculateStats();
158
- }
159
- // Process data even if it's empty/null (for initial setup)
160
- this.processExecutionData();
161
- }
162
- // Handle mode changes
163
- if (changes['mode'] && !changes['mode'].firstChange) {
164
- // Clear live nodes when switching from live to historical
165
- if (changes['mode'].previousValue === 'live' && changes['mode'].currentValue === 'historical') {
166
- // Stop live updates
167
- if (this.updateSubscription) {
168
- this.updateSubscription.unsubscribe();
169
- this.updateSubscription = undefined;
170
- }
171
- // Clear all tracking data from live mode
172
- this.processedStepIds.clear();
173
- this.newlyAddedNodeIds.clear();
174
- this.clearNodeComponents();
175
- this.executionTree = [];
176
- }
177
- this.processExecutionData();
178
- }
179
- }
180
- ngAfterViewInit() {
181
- this.viewInitialized = true;
182
- // Initial setup for scroll behavior
183
- if (this.mode === 'live') {
184
- this.checkIfUserAtBottom();
185
- }
186
- // If we have data waiting to be rendered, render it now
187
- if (this.executionData && this.executionTree.length === 0) {
188
- this.processExecutionData();
189
- }
190
- }
191
- ngOnDestroy() {
192
- this.destroy$.next();
193
- this.destroy$.complete();
194
- if (this.updateSubscription) {
195
- this.updateSubscription.unsubscribe();
196
- this.updateSubscription = undefined;
197
- }
198
- this.clearNodeComponents();
199
- }
200
- /**
201
- * Clear all dynamically created components
202
- */
203
- clearNodeComponents() {
204
- this.nodeComponentMap.forEach(ref => ref.destroy());
205
- this.nodeComponentMap.clear();
206
- if (this.executionNodesContainer) {
207
- this.executionNodesContainer.clear();
208
- }
209
- }
210
- /**
211
- * Process execution data based on mode
212
- */
213
- processExecutionData() {
214
- if (this.mode === 'historical') {
215
- // Clear processed step IDs when processing historical data
216
- this.processedStepIds.clear();
217
- // Process historical data
218
- this.buildTreeFromHistoricalData();
219
- }
220
- else {
221
- // For live mode, only clear if we don't have existing data
222
- // This prevents losing state when switching tabs
223
- if (this.executionTree.length === 0) {
224
- this.processedStepIds.clear();
225
- this.newlyAddedNodeIds.clear();
226
- }
227
- // Set up live updates
228
- this.setupLiveUpdates();
229
- }
230
- // Only calculate stats if we have data
231
- if (this.executionTree.length > 0) {
232
- this.calculateStats();
233
- }
234
- }
235
- /**
236
- * Build tree from historical execution data
237
- */
238
- buildTreeFromHistoricalData() {
239
- if (!this.executionData) {
240
- this.clearNodeComponents();
241
- this.executionTree = [];
242
- return;
243
- }
244
- // Clear existing components for full rebuild
245
- this.clearNodeComponents();
246
- // Handle executionTree format - historical mode should only use saved database data
247
- if (this.executionData.executionTree) {
248
- const allNodes = this.convertExecutionTree(this.executionData.executionTree);
249
- // Filter out live-only nodes in historical mode
250
- this.executionTree = this.filterNodesForMode(allNodes, 'historical');
251
- }
252
- else if (this.executionData.steps) {
253
- // Handle legacy steps format
254
- const allNodes = this.convertLegacySteps(this.executionData.steps);
255
- this.executionTree = this.filterNodesForMode(allNodes, 'historical');
256
- }
257
- else {
258
- this.executionTree = [];
259
- }
260
- // Render the tree using dynamic components
261
- if (this.executionNodesContainer) {
262
- this.renderTree();
263
- }
264
- }
265
- /**
266
- * Render the execution tree using dynamic components
267
- */
268
- renderTree() {
269
- if (!this.executionNodesContainer) {
270
- console.warn('📊 Execution Monitor: No container available for rendering');
271
- return;
272
- }
273
- // Clear and recreate all components for historical view
274
- this.clearNodeComponents();
275
- // Create components for each root node
276
- for (const node of this.executionTree) {
277
- this.createNodeComponent(node, this.executionNodesContainer);
278
- }
279
- }
280
- /**
281
- * Create a component for a node and its children
282
- */
283
- createNodeComponent(node, container) {
284
- const componentRef = container.createComponent(ExecutionNodeComponent);
285
- const instance = componentRef.instance;
286
- // Set inputs
287
- instance.node = node;
288
- // No need to apply margin here - it's handled in the component template
289
- // Subscribe to outputs
290
- instance.toggleNode.subscribe((n) => this.toggleNode(n));
291
- instance.userInteracted.subscribe(() => this.onUserInteraction());
292
- // Store reference
293
- this.nodeComponentMap.set(node.id, componentRef);
294
- // Force immediate change detection to ensure styles are applied
295
- componentRef.changeDetectorRef.markForCheck();
296
- componentRef.changeDetectorRef.detectChanges();
297
- return componentRef;
298
- }
299
- /**
300
- * Convert new execution tree format to display nodes
301
- */
302
- convertExecutionTree(nodes, depth = 0) {
303
- // Sort nodes by StepNumber before processing
304
- const sortedNodes = [...nodes].sort((a, b) => {
305
- const aStepNum = a.step?.StepNumber || 0;
306
- const bStepNum = b.step?.StepNumber || 0;
307
- return aStepNum - bStepNum;
308
- });
309
- return sortedNodes.map((node, index) => {
310
- // Parse the step name for markdown content (same as live steps)
311
- const stepName = node.step?.StepName || 'Unknown Step';
312
- let name = stepName;
313
- let detailsMarkdown;
314
- // Check if the step name contains markdown (indicated by newlines or markdown syntax)
315
- if (stepName.includes('\n') || stepName.includes('**') || stepName.includes('##')) {
316
- // Split by newline and use first line as name
317
- const lines = stepName.split('\n');
318
- name = lines[0].trim();
319
- // Rest is markdown details
320
- if (lines.length > 1) {
321
- detailsMarkdown = lines.slice(1).join('\n').trim();
322
- }
323
- }
324
- // Determine display mode based on the step content
325
- let displayMode = 'both';
326
- // Progress messages that should only show in live mode
327
- if (name.includes('Executing') && name.includes('action') && !name.startsWith('Execute Action:')) {
328
- displayMode = 'live';
329
- }
330
- else if (name.includes("Executing agent's initial prompt")) {
331
- displayMode = 'live';
332
- }
333
- else if (name.includes('Re-executing agent prompt with additional context')) {
334
- displayMode = 'live';
335
- }
336
- const treeNode = {
337
- id: node.step?.ID || `node-${Date.now()}-${index}`,
338
- name: name,
339
- type: this.mapStepType(node.executionType || node.step?.StepType),
340
- status: this.mapStepStatus(node.step?.Status),
341
- startTime: node.startTime ? new Date(node.startTime) : undefined,
342
- endTime: node.endTime ? new Date(node.endTime) : undefined,
343
- duration: node.durationMs,
344
- agentPath: node.agentHierarchy || [],
345
- inputPreview: this.createPreview(node.inputData),
346
- outputPreview: this.createPreview(node.outputData),
347
- error: node.step?.ErrorMessage,
348
- expanded: false,
349
- depth: node.depth !== undefined ? node.depth : depth, // Use node.depth if available, fallback to recursion depth
350
- tokensUsed: this.extractTokens(node.outputData),
351
- cost: this.extractCost(node.outputData),
352
- detailsMarkdown: detailsMarkdown,
353
- detailsExpanded: false,
354
- children: node.children ? this.convertExecutionTree(node.children, node.depth !== undefined ? node.depth + 1 : depth + 1) : undefined,
355
- displayMode: displayMode
356
- };
357
- return treeNode;
358
- });
359
- }
360
- /**
361
- * Convert legacy step format to tree nodes
362
- */
363
- convertLegacySteps(steps) {
364
- return steps.map((step, index) => {
365
- // Parse the step name for markdown content
366
- const stepName = step.StepName || '';
367
- let name = stepName;
368
- let detailsMarkdown;
369
- // Check if the step name contains markdown
370
- if (stepName.includes('\n') || stepName.includes('**') || stepName.includes('##')) {
371
- const lines = stepName.split('\n');
372
- name = lines[0].trim();
373
- if (lines.length > 1) {
374
- detailsMarkdown = lines.slice(1).join('\n').trim();
375
- }
376
- }
377
- return {
378
- id: step.ID || `step-${index}`,
379
- name: name,
380
- type: this.mapStepType(step.StepType),
381
- status: this.mapStepStatus(step.Status),
382
- startTime: step.StartedAt ? new Date(step.StartedAt) : undefined,
383
- endTime: step.CompletedAt ? new Date(step.CompletedAt) : undefined,
384
- duration: this.calculateDuration(step.StartedAt, step.CompletedAt),
385
- agentPath: [],
386
- inputPreview: this.createPreview(step.InputData),
387
- outputPreview: this.createPreview(step.OutputData),
388
- error: step.ErrorMessage,
389
- expanded: false,
390
- depth: 0,
391
- detailsMarkdown: detailsMarkdown,
392
- detailsExpanded: false
393
- };
394
- });
395
- }
396
- /**
397
- * Map step type strings to our type enum
398
- */
399
- mapStepType(type) {
400
- const typeMap = {
401
- 'validation': 'validation',
402
- 'prompt': 'prompt',
403
- 'action': 'action',
404
- 'subagent': 'sub-agent',
405
- 'sub-agent': 'sub-agent',
406
- 'decision': 'decision',
407
- 'chat': 'chat'
408
- };
409
- return typeMap[type?.toLowerCase()] || 'prompt';
410
- }
411
- /**
412
- * Map status strings to our status enum
413
- */
414
- mapStepStatus(status) {
415
- const statusMap = {
416
- 'pending': 'pending',
417
- 'running': 'running',
418
- 'completed': 'completed',
419
- 'failed': 'failed',
420
- 'error': 'failed'
421
- };
422
- return statusMap[status?.toLowerCase()] || 'pending';
423
- }
424
- /**
425
- * Create a preview string from data
426
- */
427
- createPreview(data) {
428
- if (!data)
429
- return undefined;
430
- try {
431
- const parsed = typeof data === 'string' ? JSON.parse(data) : data;
432
- // Extract meaningful preview
433
- if (parsed.promptName)
434
- return `Prompt: ${parsed.promptName}`;
435
- if (parsed.actionName)
436
- return `Action: ${parsed.actionName}`;
437
- if (parsed.subAgentName)
438
- return `Sub-agent: ${parsed.subAgentName}`;
439
- if (parsed.message)
440
- return parsed.message;
441
- if (parsed.userMessage)
442
- return parsed.userMessage;
443
- // Show action results clearly
444
- if (parsed.actionResult) {
445
- const result = parsed.actionResult;
446
- let preview = '';
447
- if (result.success !== undefined) {
448
- preview += `Success: ${result.success}\n`;
449
- }
450
- if (result.resultCode) {
451
- preview += `Result Code: ${result.resultCode}\n`;
452
- }
453
- if (result.message) {
454
- preview += `Message: ${result.message}\n`;
455
- }
456
- if (result.result) {
457
- preview += `Result: ${typeof result.result === 'object' ? JSON.stringify(result.result, null, 2) : result.result}`;
458
- }
459
- return preview.trim();
460
- }
461
- // Legacy action result format
462
- if (parsed.result) {
463
- if (typeof parsed.result === 'object') {
464
- return JSON.stringify(parsed.result, null, 2);
465
- }
466
- return String(parsed.result);
467
- }
468
- // Show prompt results
469
- if (parsed.promptResult) {
470
- const result = parsed.promptResult;
471
- let preview = '';
472
- if (result.success !== undefined) {
473
- preview += `Success: ${result.success}\n`;
474
- }
475
- if (result.content) {
476
- preview += `Content: ${typeof result.content === 'string' ? result.content : JSON.stringify(result.content, null, 2)}`;
477
- }
478
- return preview;
479
- }
480
- // Fallback to stringified preview
481
- const str = JSON.stringify(parsed, null, 2);
482
- return str.length > 500 ? str.substring(0, 500) + '...' : str;
483
- }
484
- catch {
485
- return typeof data === 'string' ? data : JSON.stringify(data);
486
- }
487
- }
488
- /**
489
- * Extract token count from output data
490
- */
491
- extractTokens(data) {
492
- if (!data)
493
- return undefined;
494
- try {
495
- const parsed = typeof data === 'string' ? JSON.parse(data) : data;
496
- return parsed.promptResult?.tokensUsed || parsed.tokensUsed;
497
- }
498
- catch {
499
- return undefined;
500
- }
501
- }
502
- /**
503
- * Extract cost from output data
504
- */
505
- extractCost(data) {
506
- if (!data)
507
- return undefined;
508
- try {
509
- const parsed = typeof data === 'string' ? JSON.parse(data) : data;
510
- return parsed.promptResult?.totalCost || parsed.totalCost;
511
- }
512
- catch {
513
- return undefined;
514
- }
515
- }
516
- /**
517
- * Calculate duration from timestamps
518
- */
519
- calculateDuration(start, end) {
520
- if (!start || !end)
521
- return undefined;
522
- const startTime = new Date(start).getTime();
523
- const endTime = new Date(end).getTime();
524
- return endTime - startTime;
525
- }
526
- /**
527
- * Set up live updates for real-time execution
528
- */
529
- setupLiveUpdates() {
530
- // Handle live streaming data format
531
- if (this.executionData && this.executionData.liveSteps) {
532
- // Only clear and rebuild if we don't have existing data
533
- if (this.executionTree.length === 0) {
534
- this.clearNodeComponents();
535
- this.executionTree = [];
536
- // Then append all steps
537
- this.appendNewLiveSteps(this.executionData.liveSteps);
538
- }
539
- else {
540
- // Just append new steps without clearing
541
- this.appendNewLiveSteps(this.executionData.liveSteps);
542
- }
543
- }
544
- // Set up interval to monitor for changes (if not already set up)
545
- if (this.mode === 'live' && !this.updateSubscription) {
546
- this.updateSubscription = interval(500)
547
- .pipe(takeUntil(this.destroy$))
548
- .subscribe(() => {
549
- // Check for new live steps and update tree
550
- if (this.executionData && this.executionData.liveSteps) {
551
- // Instead of replacing entire tree, append new nodes
552
- this.appendNewLiveSteps(this.executionData.liveSteps);
553
- this.calculateStats();
554
- }
555
- this.updateCurrentStep();
556
- });
557
- }
558
- }
559
- /**
560
- * Convert live steps format to execution tree format
561
- */
562
- convertLiveStepsToTree(liveSteps) {
563
- const treeNodes = [];
564
- const nodeMap = new Map();
565
- // Sort steps by StepNumber to ensure proper ordering
566
- const sortedSteps = [...liveSteps].sort((a, b) => {
567
- const aStepNum = a.step?.StepNumber || 0;
568
- const bStepNum = b.step?.StepNumber || 0;
569
- return aStepNum - bStepNum;
570
- });
571
- for (const step of sortedSteps) {
572
- // Parse the step name for markdown content
573
- const stepName = step.step?.StepName || 'Processing...';
574
- let name = stepName;
575
- let detailsMarkdown;
576
- // Check if the step name contains markdown (indicated by newlines or markdown syntax)
577
- if (stepName.includes('\n') || stepName.includes('**') || stepName.includes('##')) {
578
- // Split by newline and use first line as name
579
- const lines = stepName.split('\n');
580
- name = lines[0].trim();
581
- // Rest is markdown details
582
- if (lines.length > 1) {
583
- detailsMarkdown = lines.slice(1).join('\n').trim();
584
- }
585
- }
586
- const node = {
587
- id: step.step?.ID || `live-${Date.now()}-${Math.random()}`,
588
- name: name,
589
- type: this.mapStepType(step.executionType || step.step?.StepType),
590
- status: 'running', // Live steps are always running
591
- startTime: step.startTime ? new Date(step.startTime) : new Date(),
592
- agentPath: step.agentHierarchy || [],
593
- expanded: false,
594
- depth: step.depth || 0,
595
- detailsMarkdown: detailsMarkdown,
596
- detailsExpanded: false
597
- };
598
- // Store in map for hierarchy building
599
- nodeMap.set(node.id, node);
600
- // Handle hierarchy by depth
601
- if (step.depth === 0) {
602
- treeNodes.push(node);
603
- }
604
- else {
605
- // Find parent based on depth and agent hierarchy
606
- let parent;
607
- // Look for a node at depth-1 that could be the parent
608
- nodeMap.forEach((candidate) => {
609
- if (candidate.depth === step.depth - 1) {
610
- parent = candidate;
611
- }
612
- });
613
- if (parent) {
614
- if (!parent.children) {
615
- parent.children = [];
616
- }
617
- parent.children.push(node);
618
- }
619
- }
620
- nodeMap.set(node.id, node);
621
- }
622
- return treeNodes;
623
- }
624
- /**
625
- * Update current step indicator
626
- */
627
- updateCurrentStep() {
628
- // Find the first running step
629
- const findRunning = (nodes) => {
630
- for (const node of nodes) {
631
- if (node.status === 'running')
632
- return node;
633
- if (node.children) {
634
- const child = findRunning(node.children);
635
- if (child)
636
- return child;
637
- }
638
- }
639
- return null;
640
- };
641
- this.currentStep = findRunning(this.executionTree);
642
- this.cdr.markForCheck();
643
- }
644
- /**
645
- * Mark previously new nodes as completed
646
- */
647
- markPreviouslyNewNodesAsComplete() {
648
- // Only mark nodes that were previously marked as newly added
649
- for (const nodeId of this.newlyAddedNodeIds) {
650
- const node = this.findNodeById(this.executionTree, nodeId);
651
- if (node && node.status === 'running') {
652
- node.status = 'completed';
653
- // Update the component if it exists
654
- const componentRef = this.nodeComponentMap.get(nodeId);
655
- if (componentRef) {
656
- componentRef.instance.node = { ...node };
657
- componentRef.changeDetectorRef.detectChanges();
658
- }
659
- }
660
- }
661
- }
662
- /**
663
- * Find a node by ID in the tree
664
- */
665
- findNodeById(nodes, id) {
666
- for (const node of nodes) {
667
- if (node.id === id) {
668
- return node;
669
- }
670
- if (node.children) {
671
- const found = this.findNodeById(node.children, id);
672
- if (found)
673
- return found;
674
- }
675
- }
676
- return null;
677
- }
678
- /**
679
- * Calculate execution statistics
680
- */
681
- calculateStats() {
682
- this.stats = {
683
- totalSteps: 0,
684
- completedSteps: 0,
685
- failedSteps: 0,
686
- totalTokens: 0,
687
- totalCost: 0,
688
- stepsByType: {},
689
- totalDuration: 0,
690
- totalPrompts: 0
691
- };
692
- // Check if we have agent run data with token information
693
- if (this.executionData?.agentRun) {
694
- // Use token data from agent run if available
695
- this.stats.totalTokens = this.executionData.agentRun.TotalTokensUsed || 0;
696
- this.stats.totalCost = this.executionData.agentRun.TotalCost || 0;
697
- }
698
- const processNode = (node) => {
699
- this.stats.totalSteps++;
700
- if (node.status === 'completed')
701
- this.stats.completedSteps++;
702
- if (node.status === 'failed')
703
- this.stats.failedSteps++;
704
- // Only accumulate tokens if we don't have agent run data
705
- if (!this.executionData?.agentRun) {
706
- if (node.tokensUsed)
707
- this.stats.totalTokens += node.tokensUsed;
708
- if (node.cost)
709
- this.stats.totalCost += node.cost;
710
- }
711
- if (node.duration)
712
- this.stats.totalDuration += node.duration;
713
- this.stats.stepsByType[node.type] = (this.stats.stepsByType[node.type] || 0) + 1;
714
- // Count prompts
715
- if (node.type === 'prompt' || node.promptCount) {
716
- this.stats.totalPrompts += node.promptCount || 1;
717
- }
718
- if (node.children) {
719
- node.children.forEach(child => processNode(child));
720
- }
721
- };
722
- this.executionTree.forEach(node => processNode(node));
723
- }
724
- /**
725
- * Toggle node expansion
726
- */
727
- toggleNode(node) {
728
- const nodeInTree = this.findNodeById(this.executionTree, node.id);
729
- if (nodeInTree && this.hasExpandableContent(nodeInTree)) {
730
- nodeInTree.expanded = !nodeInTree.expanded;
731
- this.userHasInteracted = true;
732
- // Update the component's node reference to trigger change detection
733
- const componentRef = this.nodeComponentMap.get(node.id);
734
- if (componentRef) {
735
- componentRef.instance.node = { ...nodeInTree };
736
- componentRef.changeDetectorRef.detectChanges();
737
- }
738
- this.cdr.markForCheck();
739
- }
740
- }
741
- /**
742
- * Check if node has expandable content
743
- */
744
- hasExpandableContent(node) {
745
- return !!(node.children && node.children.length > 0) ||
746
- node.type === 'sub-agent' ||
747
- node.type === 'action' ||
748
- !!node.inputPreview ||
749
- !!node.outputPreview ||
750
- !!node.error ||
751
- !!node.detailsMarkdown;
752
- }
753
- /**
754
- * Handle scroll events
755
- */
756
- onScroll(event) {
757
- if (this.isAutoScrolling) {
758
- // Ignore scroll events triggered by auto-scrolling
759
- return;
760
- }
761
- const element = event.target;
762
- const isAtBottom = this.isScrolledToBottom(element);
763
- // If user scrolled up from bottom, mark as user interaction
764
- if (!isAtBottom && this.lastScrollPosition !== element.scrollTop) {
765
- this.userHasScrolled = true;
766
- this.userHasInteracted = true;
767
- }
768
- // If user scrolled back to bottom, reset interaction flags
769
- if (isAtBottom) {
770
- this.userHasScrolled = false;
771
- this.userHasInteracted = false;
772
- }
773
- this.lastScrollPosition = element.scrollTop;
774
- }
775
- /**
776
- * Handle user clicks (interaction detection)
777
- */
778
- onUserInteraction() {
779
- // This is called when user clicks anywhere in the execution tree
780
- // The toggleNode method will set userHasInteracted for specific interactions
781
- }
782
- /**
783
- * Check if scroll is at bottom
784
- */
785
- isScrolledToBottom(element) {
786
- const threshold = this.scrollThreshold;
787
- return element.scrollHeight - element.scrollTop - element.clientHeight < threshold;
788
- }
789
- /**
790
- * Check if user is at bottom (for initial setup)
791
- */
792
- checkIfUserAtBottom() {
793
- if (!this.executionTreeContainer)
794
- return;
795
- const element = this.executionTreeContainer.nativeElement;
796
- if (this.isScrolledToBottom(element)) {
797
- this.userHasScrolled = false;
798
- }
799
- }
800
- /**
801
- * Auto-scroll to bottom if user hasn't interacted
802
- */
803
- autoScrollToBottom() {
804
- if (!this.executionTreeContainer || this.userHasInteracted || this.userHasScrolled) {
805
- return;
806
- }
807
- const element = this.executionTreeContainer.nativeElement;
808
- this.isAutoScrolling = true;
809
- // Use smooth scrolling for better UX
810
- element.scrollTo({
811
- top: element.scrollHeight,
812
- behavior: 'smooth'
813
- });
814
- // Reset auto-scrolling flag after animation
815
- setTimeout(() => {
816
- this.isAutoScrolling = false;
817
- }, 300);
818
- }
819
- /**
820
- * Format agent path for display
821
- */
822
- formatAgentPath(path) {
823
- return path.join(' → ');
824
- }
825
- /**
826
- * Format duration for display
827
- */
828
- formatDuration(ms) {
829
- if (ms < 1000)
830
- return `${ms}ms`;
831
- if (ms < 60000)
832
- return `${(ms / 1000).toFixed(1)}s`;
833
- const minutes = Math.floor(ms / 60000);
834
- const seconds = Math.floor((ms % 60000) / 1000);
835
- return `${minutes}m ${seconds}s`;
836
- }
837
- /**
838
- * Get step types for display
839
- */
840
- getStepTypes() {
841
- return Object.keys(this.stats.stepsByType).sort();
842
- }
843
- /**
844
- * Format markdown content for display
845
- */
846
- formatMarkdown(markdown) {
847
- // Basic markdown formatting
848
- // This is a simple implementation - you might want to use a proper markdown library
849
- let html = markdown;
850
- // Headers
851
- html = html.replace(/^### (.*$)/gim, '<h4>$1</h4>');
852
- html = html.replace(/^## (.*$)/gim, '<h3>$1</h3>');
853
- html = html.replace(/^# (.*$)/gim, '<h2>$1</h2>');
854
- // Bold
855
- html = html.replace(/\*\*(.*)\*\*/g, '<strong>$1</strong>');
856
- // Italic
857
- html = html.replace(/\*(.*)\*/g, '<em>$1</em>');
858
- // Detect and linkify URLs
859
- // This regex matches URLs starting with http://, https://, or www.
860
- const urlRegex = /(?:https?:\/\/|www\.)[^\s<]+/gi;
861
- html = html.replace(urlRegex, (url) => {
862
- // Ensure the URL has a protocol
863
- const href = url.startsWith('http') ? url : `https://${url}`;
864
- // Truncate long URLs for display
865
- const displayUrl = url.length > 50 ? url.substring(0, 50) + '...' : url;
866
- return `<a href="${href}" target="_blank" rel="noopener noreferrer" style="color: #2196f3; text-decoration: underline; font-size: inherit;">${displayUrl}</a>`;
867
- });
868
- // Line breaks
869
- html = html.replace(/\n/g, '<br>');
870
- // Lists
871
- html = html.replace(/^\* (.+)$/gim, '<li>$1</li>');
872
- html = html.replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>');
873
- // Code blocks
874
- html = html.replace(/```(.*?)```/gs, '<pre><code>$1</code></pre>');
875
- // Inline code
876
- html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
877
- return html;
878
- }
879
- /**
880
- * Append new live steps without re-rendering entire tree
881
- */
882
- appendNewLiveSteps(liveSteps) {
883
- if (!liveSteps || liveSteps.length === 0)
884
- return;
885
- // If view is not initialized yet, wait for it
886
- if (!this.viewInitialized || !this.executionNodesContainer) {
887
- return;
888
- }
889
- // Sort live steps by stepNumber to ensure proper ordering
890
- const sortedSteps = [...liveSteps].sort((a, b) => {
891
- const aStepNum = a.step?.StepNumber || 0;
892
- const bStepNum = b.step?.StepNumber || 0;
893
- return aStepNum - bStepNum;
894
- });
895
- // Create a map of existing nodes by ID for quick lookup
896
- const nodeMap = new Map();
897
- this.buildNodeMap(this.executionTree, nodeMap);
898
- // First pass: identify which steps are truly NEW (not in processedStepIds)
899
- const trulyNewStepIds = new Set();
900
- for (const step of sortedSteps) {
901
- const stepId = step.step?.ID || this.generateStepId(step);
902
- if (!this.processedStepIds.has(stepId)) {
903
- trulyNewStepIds.add(stepId);
904
- }
905
- }
906
- // Only mark previously new nodes as complete if we have truly new steps
907
- if (trulyNewStepIds.size > 0 && this.newlyAddedNodeIds.size > 0) {
908
- this.markPreviouslyNewNodesAsComplete();
909
- // Clear the set for new additions
910
- this.newlyAddedNodeIds.clear();
911
- }
912
- let hasNewNodes = false;
913
- for (const step of sortedSteps) {
914
- // Generate a stable ID based on step content to avoid duplicates
915
- const stepId = step.step?.ID || this.generateStepId(step);
916
- // Skip if we've already processed this exact step
917
- if (this.processedStepIds.has(stepId)) {
918
- continue;
919
- }
920
- // Mark as processed
921
- this.processedStepIds.add(stepId);
922
- // Check if this node already exists
923
- const existingNode = nodeMap.get(stepId);
924
- if (existingNode) {
925
- // Update existing node in the component
926
- const componentRef = this.nodeComponentMap.get(stepId);
927
- if (componentRef) {
928
- // Preserve current UI state
929
- const currentExpanded = existingNode.expanded;
930
- const currentDetailsExpanded = existingNode.detailsExpanded;
931
- const updatedStatus = step.step?.Status ?
932
- this.mapStepStatus(step.step.Status) :
933
- (step.endTime || step.outputData ? 'completed' : existingNode.status);
934
- if (existingNode.status !== updatedStatus) {
935
- existingNode.status = updatedStatus;
936
- }
937
- if (step.endTime && !existingNode.endTime) {
938
- existingNode.endTime = new Date(step.endTime);
939
- existingNode.duration = step.durationMs;
940
- }
941
- // Update output data if available
942
- if (step.outputData && !existingNode.outputPreview) {
943
- existingNode.outputPreview = this.createPreview(step.outputData);
944
- existingNode.tokensUsed = this.extractTokens(step.outputData);
945
- existingNode.cost = this.extractCost(step.outputData);
946
- }
947
- // Restore UI state
948
- existingNode.expanded = currentExpanded;
949
- existingNode.detailsExpanded = currentDetailsExpanded;
950
- // Update the component's node reference
951
- // Important: We need to update the node object directly to ensure
952
- // Angular detects changes to style bindings that depend on node.depth
953
- const nodeInstance = componentRef.instance.node;
954
- Object.assign(nodeInstance, existingNode);
955
- // Mark for check to ensure Angular updates the view
956
- componentRef.changeDetectorRef.markForCheck();
957
- componentRef.changeDetectorRef.detectChanges();
958
- }
959
- }
960
- else {
961
- // Create new node
962
- const newNode = this.createNodeFromLiveStep(step);
963
- // Debug: Log if depth is missing
964
- if (newNode.depth === 0 && step.depth !== 0) {
965
- console.warn('Node depth was reset to 0 for step:', step);
966
- }
967
- if (step.depth === 0) {
968
- // Add to tree and create component
969
- this.executionTree.push(newNode);
970
- this.createNodeComponent(newNode, this.executionNodesContainer);
971
- }
972
- else {
973
- // Find parent and add as child
974
- const parentNode = this.findParentNode(this.executionTree, newNode);
975
- if (parentNode) {
976
- if (!parentNode.children) {
977
- parentNode.children = [];
978
- }
979
- parentNode.children.push(newNode);
980
- // Update parent component to show new child
981
- const parentComponentRef = this.nodeComponentMap.get(parentNode.id);
982
- if (parentComponentRef) {
983
- // Update the parent node object directly
984
- const parentNodeInstance = parentComponentRef.instance.node;
985
- Object.assign(parentNodeInstance, parentNode);
986
- parentComponentRef.changeDetectorRef.markForCheck();
987
- parentComponentRef.changeDetectorRef.detectChanges();
988
- }
989
- }
990
- else {
991
- // If no parent found, add as root
992
- this.executionTree.push(newNode);
993
- this.createNodeComponent(newNode, this.executionNodesContainer);
994
- }
995
- }
996
- hasNewNodes = true;
997
- // Only track as newly added if it's truly new
998
- if (trulyNewStepIds.has(stepId)) {
999
- this.newlyAddedNodeIds.add(newNode.id);
1000
- }
1001
- }
1002
- }
1003
- if (hasNewNodes) {
1004
- // Update current step
1005
- this.updateCurrentStep();
1006
- // Recalculate stats
1007
- this.calculateStats();
1008
- // Trigger change detection
1009
- this.cdr.markForCheck();
1010
- // Auto-scroll if user hasn't interacted
1011
- setTimeout(() => {
1012
- this.autoScrollToBottom();
1013
- }, 100);
1014
- }
1015
- }
1016
- /**
1017
- * Generate a stable ID for a step based on its content
1018
- */
1019
- generateStepId(step) {
1020
- // Create a stable ID based on step properties that should be unique
1021
- const parts = [
1022
- step.executionType || 'unknown',
1023
- step.depth || 0,
1024
- step.step?.StepName || '',
1025
- step.startTime || Date.now()
1026
- ];
1027
- return `live-${parts.join('-').replace(/[^a-zA-Z0-9-]/g, '')}`;
1028
- }
1029
- /**
1030
- * Find parent node based on depth
1031
- */
1032
- findParentNode(nodes, childNode) {
1033
- // We need to find the most recent node at the parent depth
1034
- // that could logically be this node's parent
1035
- let potentialParent = null;
1036
- const findParentRecursive = (searchNodes) => {
1037
- // Process nodes in reverse order to find the most recent one
1038
- for (let i = searchNodes.length - 1; i >= 0; i--) {
1039
- const node = searchNodes[i];
1040
- // Check if this node could be the parent based on depth
1041
- if (node.depth === childNode.depth - 1) {
1042
- // Special handling for sub-agent execution steps
1043
- if (childNode.agentPath && childNode.agentPath.length > node.agentPath.length) {
1044
- // This child belongs to a sub-agent
1045
- // Look for the most recent sub-agent delegation node
1046
- if (node.type === 'sub-agent' || node.name.includes('Delegating to')) {
1047
- return node;
1048
- }
1049
- }
1050
- // Keep track of the most recent node at the correct depth
1051
- if (!potentialParent) {
1052
- potentialParent = node;
1053
- }
1054
- }
1055
- // Search children recursively
1056
- if (node.children && node.children.length > 0) {
1057
- const foundInChildren = findParentRecursive(node.children);
1058
- if (foundInChildren)
1059
- return foundInChildren;
1060
- }
1061
- }
1062
- return null;
1063
- };
1064
- const found = findParentRecursive(nodes);
1065
- return found || potentialParent;
1066
- }
1067
- /**
1068
- * Filter nodes based on display mode
1069
- */
1070
- filterNodesForMode(nodes, mode) {
1071
- return nodes.filter(node => {
1072
- // If no displayMode is set, default to showing in both modes
1073
- if (!node.displayMode) {
1074
- return true;
1075
- }
1076
- // Show if displayMode is 'both' or matches current mode
1077
- if (node.displayMode === 'both' || node.displayMode === mode) {
1078
- // Filter children recursively
1079
- if (node.children) {
1080
- node.children = this.filterNodesForMode(node.children, mode);
1081
- }
1082
- return true;
1083
- }
1084
- return false;
1085
- });
1086
- }
1087
- /**
1088
- * Build a map of nodes by ID for quick lookup
1089
- */
1090
- buildNodeMap(nodes, map) {
1091
- for (const node of nodes) {
1092
- map.set(node.id, node);
1093
- if (node.children) {
1094
- this.buildNodeMap(node.children, map);
1095
- }
1096
- }
1097
- }
1098
- /**
1099
- * Create a single node from a live step
1100
- */
1101
- createNodeFromLiveStep(step) {
1102
- const stepName = step.step?.StepName || 'Processing...';
1103
- let name = stepName;
1104
- let detailsMarkdown;
1105
- // Check if the step name contains markdown
1106
- if (stepName.includes('\n') || stepName.includes('**') || stepName.includes('##')) {
1107
- const lines = stepName.split('\n');
1108
- name = lines[0].trim();
1109
- if (lines.length > 1) {
1110
- detailsMarkdown = lines.slice(1).join('\n').trim();
1111
- }
1112
- }
1113
- // For live steps, always start as running
1114
- // The status will be updated to completed in markPreviouslyNewNodesAsComplete
1115
- let status = 'running';
1116
- if (step.step?.Status) {
1117
- // Only use explicit status if it's failed
1118
- const mappedStatus = this.mapStepStatus(step.step.Status);
1119
- if (mappedStatus === 'failed') {
1120
- status = 'failed';
1121
- }
1122
- }
1123
- // Determine display mode based on the step content
1124
- let displayMode = 'both';
1125
- // Progress messages that should only show in live mode
1126
- if (name.includes('Executing') && name.includes('action') && !name.startsWith('Execute Action:')) {
1127
- displayMode = 'live';
1128
- }
1129
- else if (name.includes("Executing agent's initial prompt")) {
1130
- displayMode = 'live';
1131
- }
1132
- else if (name.includes('Re-executing agent prompt with additional context')) {
1133
- displayMode = 'live';
1134
- }
1135
- // Check displayMode from metadata if available
1136
- if (step.metadata?.displayMode) {
1137
- displayMode = step.metadata.displayMode;
1138
- }
1139
- return {
1140
- id: step.step?.ID || this.generateStepId(step),
1141
- name: name,
1142
- type: this.mapStepType(step.executionType || step.step?.StepType),
1143
- status: status,
1144
- startTime: step.startTime ? new Date(step.startTime) : new Date(),
1145
- endTime: step.endTime ? new Date(step.endTime) : undefined,
1146
- duration: step.durationMs,
1147
- agentPath: step.agentHierarchy || [],
1148
- expanded: false,
1149
- depth: step.depth || 0,
1150
- detailsMarkdown: detailsMarkdown,
1151
- detailsExpanded: false,
1152
- inputPreview: this.createPreview(step.inputData),
1153
- outputPreview: this.createPreview(step.outputData),
1154
- tokensUsed: this.extractTokens(step.outputData),
1155
- cost: this.extractCost(step.outputData),
1156
- displayMode: displayMode
1157
- };
1158
- }
1159
- /**
1160
- * Collect all node IDs in the tree
1161
- */
1162
- collectNodeIds(nodes, ids) {
1163
- for (const node of nodes) {
1164
- ids.add(node.id);
1165
- if (node.children) {
1166
- this.collectNodeIds(node.children, ids);
1167
- }
1168
- }
1169
- }
1170
- /**
1171
- * Preserve expanded state of all nodes
1172
- */
1173
- preserveExpandedState(nodes, expandedState, detailsExpandedState) {
1174
- for (const node of nodes) {
1175
- if (node.expanded !== undefined) {
1176
- expandedState.set(node.id, node.expanded);
1177
- }
1178
- if (node.detailsExpanded !== undefined) {
1179
- detailsExpandedState.set(node.id, node.detailsExpanded);
1180
- }
1181
- if (node.children) {
1182
- this.preserveExpandedState(node.children, expandedState, detailsExpandedState);
1183
- }
1184
- }
1185
- }
1186
- /**
1187
- * Restore expanded state of all nodes
1188
- */
1189
- restoreExpandedState(nodes, expandedState, detailsExpandedState) {
1190
- for (const node of nodes) {
1191
- const expanded = expandedState.get(node.id);
1192
- if (expanded !== undefined) {
1193
- node.expanded = expanded;
1194
- }
1195
- const detailsExpanded = detailsExpandedState.get(node.id);
1196
- if (detailsExpanded !== undefined) {
1197
- node.detailsExpanded = detailsExpanded;
1198
- }
1199
- if (node.children) {
1200
- this.restoreExpandedState(node.children, expandedState, detailsExpandedState);
1201
- }
1202
- }
1203
- }
1204
- /**
1205
- * Check if execution is complete
1206
- */
1207
- isExecutionComplete() {
1208
- return this.stats.completedSteps > 0 &&
1209
- this.stats.completedSteps === this.stats.totalSteps - this.stats.failedSteps;
1210
- }
1211
- /**
1212
- * Handle view run button click
1213
- */
1214
- onViewRunClick() {
1215
- if (this.runId) {
1216
- this.viewRunClick.emit({ runId: this.runId, runType: this.runType });
1217
- }
1218
- }
1219
- /**
1220
- * Append a node to its parent based on depth
1221
- */
1222
- appendToParent(nodes, newNode) {
1223
- for (const node of nodes) {
1224
- if (node.depth === newNode.depth - 1) {
1225
- if (!node.children) {
1226
- node.children = [];
1227
- }
1228
- node.children.push(newNode);
1229
- return true;
1230
- }
1231
- if (node.children && this.appendToParent(node.children, newNode)) {
1232
- return true;
1233
- }
1234
- }
1235
- return false;
1236
- }
1237
- static { this.ɵfac = function AgentExecutionMonitorComponent_Factory(t) { return new (t || AgentExecutionMonitorComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); }; }
1238
- static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AgentExecutionMonitorComponent, selectors: [["mj-agent-execution-monitor"]], viewQuery: function AgentExecutionMonitorComponent_Query(rf, ctx) { if (rf & 1) {
1239
- i0.ɵɵviewQuery(_c0, 5);
1240
- i0.ɵɵviewQuery(_c1, 5, ViewContainerRef);
1241
- } if (rf & 2) {
1242
- let _t;
1243
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.executionTreeContainer = _t.first);
1244
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.executionNodesContainer = _t.first);
1245
- } }, inputs: { mode: "mode", executionData: "executionData", autoExpand: "autoExpand", runId: "runId", runType: "runType" }, outputs: { viewRunClick: "viewRunClick" }, standalone: true, features: [i0.ɵɵNgOnChangesFeature, i0.ɵɵStandaloneFeature], decls: 39, vars: 16, consts: [["executionTreeContainer", ""], ["executionNodesContainer", ""], [1, "execution-monitor"], [1, "monitor-header"], [1, "header-title"], [1, "fa-solid", "fa-diagram-project"], [1, "live-indicator"], [1, "current-status"], [1, "view-run-btn"], [1, "execution-tree", 3, "scroll", "click"], [1, "nodes-container"], [1, "empty-state"], [1, "monitor-footer"], [1, "stats-grid"], [1, "stat-item"], [1, "stat-label"], [1, "stat-value"], [1, "failed-count"], [1, "step-types"], [1, "pulse"], [1, "status-label"], [1, "agent-path"], [1, "step-name"], [1, "view-run-btn", 3, "click"], [1, "fa-solid", "fa-external-link-alt"], [1, "fa-solid", "fa-hourglass-start"], [1, "type-badge"]], template: function AgentExecutionMonitorComponent_Template(rf, ctx) { if (rf & 1) {
1246
- const _r1 = i0.ɵɵgetCurrentView();
1247
- i0.ɵɵelementStart(0, "div", 2)(1, "div", 3)(2, "div", 4);
1248
- i0.ɵɵelement(3, "i", 5);
1249
- i0.ɵɵelementStart(4, "span");
1250
- i0.ɵɵtext(5, "Execution Monitor");
1251
- i0.ɵɵelementEnd();
1252
- i0.ɵɵtemplate(6, AgentExecutionMonitorComponent_Conditional_6_Template, 3, 0, "span", 6);
1253
- i0.ɵɵelementEnd();
1254
- i0.ɵɵtemplate(7, AgentExecutionMonitorComponent_Conditional_7_Template, 7, 2, "div", 7)(8, AgentExecutionMonitorComponent_Conditional_8_Template, 3, 1, "button", 8);
1255
- i0.ɵɵelementEnd();
1256
- i0.ɵɵelementStart(9, "div", 9, 0);
1257
- 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()); });
1258
- i0.ɵɵelementStart(11, "div", 10, 1);
1259
- i0.ɵɵtemplate(13, AgentExecutionMonitorComponent_Conditional_13_Template, 4, 0, "div", 11);
1260
- i0.ɵɵelementEnd()();
1261
- i0.ɵɵelementStart(14, "div", 12)(15, "div", 13)(16, "div", 14)(17, "span", 15);
1262
- i0.ɵɵtext(18, "Steps");
1263
- i0.ɵɵelementEnd();
1264
- i0.ɵɵelementStart(19, "span", 16);
1265
- i0.ɵɵtext(20);
1266
- i0.ɵɵtemplate(21, AgentExecutionMonitorComponent_Conditional_21_Template, 2, 1, "span", 17);
1267
- i0.ɵɵelementEnd()();
1268
- i0.ɵɵelementStart(22, "div", 14)(23, "span", 15);
1269
- i0.ɵɵtext(24, "Prompts");
1270
- i0.ɵɵelementEnd();
1271
- i0.ɵɵelementStart(25, "span", 16);
1272
- i0.ɵɵtext(26);
1273
- i0.ɵɵelementEnd()();
1274
- i0.ɵɵelementStart(27, "div", 14)(28, "span", 15);
1275
- i0.ɵɵtext(29, "Tokens");
1276
- i0.ɵɵelementEnd();
1277
- i0.ɵɵelementStart(30, "span", 16);
1278
- i0.ɵɵtext(31);
1279
- i0.ɵɵelementEnd()();
1280
- i0.ɵɵelementStart(32, "div", 14)(33, "span", 15);
1281
- i0.ɵɵtext(34, "Cost");
1282
- i0.ɵɵelementEnd();
1283
- i0.ɵɵelementStart(35, "span", 16);
1284
- i0.ɵɵtext(36);
1285
- i0.ɵɵelementEnd()();
1286
- i0.ɵɵtemplate(37, AgentExecutionMonitorComponent_Conditional_37_Template, 5, 1, "div", 14);
1287
- i0.ɵɵelementEnd();
1288
- i0.ɵɵtemplate(38, AgentExecutionMonitorComponent_Conditional_38_Template, 3, 0, "div", 18);
1289
- i0.ɵɵelementEnd()();
1290
- } if (rf & 2) {
1291
- i0.ɵɵclassProp("live-mode", ctx.mode === "live");
1292
- i0.ɵɵadvance(6);
1293
- i0.ɵɵconditional(ctx.mode === "live" ? 6 : -1);
1294
- i0.ɵɵadvance();
1295
- i0.ɵɵconditional(ctx.currentStep && ctx.mode === "live" ? 7 : -1);
1296
- i0.ɵɵadvance();
1297
- i0.ɵɵconditional(ctx.mode === "historical" && ctx.runId && ctx.isExecutionComplete() ? 8 : -1);
1298
- i0.ɵɵadvance();
1299
- i0.ɵɵclassProp("has-content", ctx.executionTree.length > 0);
1300
- i0.ɵɵadvance(4);
1301
- i0.ɵɵconditional(ctx.executionTree.length === 0 ? 13 : -1);
1302
- i0.ɵɵadvance(7);
1303
- i0.ɵɵtextInterpolate2(" ", ctx.stats.completedSteps, "/", ctx.stats.totalSteps, " ");
1304
- i0.ɵɵadvance();
1305
- i0.ɵɵconditional(ctx.stats.failedSteps > 0 ? 21 : -1);
1306
- i0.ɵɵadvance(5);
1307
- i0.ɵɵtextInterpolate(ctx.stats.totalPrompts);
1308
- i0.ɵɵadvance(5);
1309
- i0.ɵɵtextInterpolate(ctx.stats.totalTokens.toLocaleString());
1310
- i0.ɵɵadvance(5);
1311
- i0.ɵɵtextInterpolate1("$", ctx.stats.totalCost.toFixed(4), "");
1312
- i0.ɵɵadvance();
1313
- i0.ɵɵconditional(ctx.stats.totalDuration ? 37 : -1);
1314
- i0.ɵɵadvance();
1315
- i0.ɵɵconditional(ctx.getStepTypes().length > 0 ? 38 : -1);
1316
- } }, 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 }); }
1317
- }
1318
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AgentExecutionMonitorComponent, [{
1319
- type: Component,
1320
- args: [{ selector: 'mj-agent-execution-monitor', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
1321
- <div class="execution-monitor" [class.live-mode]="mode === 'live'">
1322
- <!-- Header -->
1323
- <div class="monitor-header">
1324
- <div class="header-title">
1325
- <i class="fa-solid fa-diagram-project"></i>
1326
- <span>Execution Monitor</span>
1327
- @if (mode === 'live') {
1328
- <span class="live-indicator">
1329
- <span class="pulse"></span>
1330
- LIVE
1331
- </span>
1332
- }
1333
- </div>
1334
- @if (currentStep && mode === 'live') {
1335
- <div class="current-status">
1336
- <span class="status-label">Current:</span>
1337
- <span class="agent-path">{{ formatAgentPath(currentStep.agentPath) }}</span>
1338
- <span class="step-name">{{ currentStep.name }}</span>
1339
- </div>
1340
- }
1341
- @if (mode === 'historical' && runId && isExecutionComplete()) {
1342
- <button class="view-run-btn" (click)="onViewRunClick()">
1343
- <i class="fa-solid fa-external-link-alt"></i>
1344
- View {{ runType === 'agent' ? 'Agent' : 'Prompt' }} Run
1345
- </button>
1346
- }
1347
- </div>
1348
-
1349
- <!-- Execution Tree -->
1350
- <div class="execution-tree"
1351
- #executionTreeContainer
1352
- [class.has-content]="executionTree.length > 0"
1353
- (scroll)="onScroll($event)"
1354
- (click)="onUserInteraction()">
1355
- <!-- Always render the container, but show empty state when no data -->
1356
- <div #executionNodesContainer class="nodes-container">
1357
- @if (executionTree.length === 0) {
1358
- <div class="empty-state">
1359
- <i class="fa-solid fa-hourglass-start"></i>
1360
- <p>Waiting for execution to begin...</p>
1361
- </div>
1362
- }
1363
- </div>
1364
- </div>
1365
-
1366
- <!-- Statistics Footer -->
1367
- <div class="monitor-footer">
1368
- <div class="stats-grid">
1369
- <div class="stat-item">
1370
- <span class="stat-label">Steps</span>
1371
- <span class="stat-value">
1372
- {{ stats.completedSteps }}/{{ stats.totalSteps }}
1373
- @if (stats.failedSteps > 0) {
1374
- <span class="failed-count">({{ stats.failedSteps }} failed)</span>
1375
- }
1376
- </span>
1377
- </div>
1378
- <div class="stat-item">
1379
- <span class="stat-label">Prompts</span>
1380
- <span class="stat-value">{{ stats.totalPrompts }}</span>
1381
- </div>
1382
- <div class="stat-item">
1383
- <span class="stat-label">Tokens</span>
1384
- <span class="stat-value">{{ stats.totalTokens.toLocaleString() }}</span>
1385
- </div>
1386
- <div class="stat-item">
1387
- <span class="stat-label">Cost</span>
1388
- <span class="stat-value">\${{ stats.totalCost.toFixed(4) }}</span>
1389
- </div>
1390
- @if (stats.totalDuration) {
1391
- <div class="stat-item">
1392
- <span class="stat-label">Duration</span>
1393
- <span class="stat-value">{{ formatDuration(stats.totalDuration) }}</span>
1394
- </div>
1395
- }
1396
- </div>
1397
- @if (getStepTypes().length > 0) {
1398
- <div class="step-types">
1399
- @for (type of getStepTypes(); track type) {
1400
- <span class="type-badge">
1401
- {{ stats.stepsByType[type] }} {{ type }}
1402
- </span>
1403
- }
1404
- </div>
1405
- }
1406
- </div>
1407
- </div>
1408
- `, 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 "] }]
1409
- }], () => [{ type: i0.ChangeDetectorRef }], { mode: [{
1410
- type: Input
1411
- }], executionData: [{
1412
- type: Input
1413
- }], autoExpand: [{
1414
- type: Input
1415
- }], runId: [{
1416
- type: Input
1417
- }], runType: [{
1418
- type: Input
1419
- }], viewRunClick: [{
1420
- type: Output
1421
- }], executionTreeContainer: [{
1422
- type: ViewChild,
1423
- args: ['executionTreeContainer']
1424
- }], executionNodesContainer: [{
1425
- type: ViewChild,
1426
- args: ['executionNodesContainer', { read: ViewContainerRef }]
1427
- }] }); })();
1428
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AgentExecutionMonitorComponent, { className: "AgentExecutionMonitorComponent", filePath: "src/lib/custom/ai-test-harness/agent-execution-monitor.component.ts", lineNumber: 634 }); })();
1429
- //# sourceMappingURL=agent-execution-monitor.component.js.map