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