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