UNPKG

@memberjunction/ng-ai-test-harness

Version:

MemberJunction AI Test Harness - A reusable component for testing AI agents and prompts with beautiful UX

693 lines (683 loc) 56.7 kB
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core'; import * as i0 from "@angular/core"; function ExecutionNodeComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) { const _r1 = i0.ɵɵgetCurrentView(); i0.ɵɵelementStart(0, "i", 18); i0.ɵɵlistener("click", function ExecutionNodeComponent_Conditional_2_Template_i_click_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onToggleChildren($event)); }); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵclassProp("fa-chevron-down", ctx_r1.expanded)("fa-chevron-right", !ctx_r1.expanded); } } function ExecutionNodeComponent_Case_4_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "i", 4); } } function ExecutionNodeComponent_Case_5_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "i", 5); } } function ExecutionNodeComponent_Case_6_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "i", 6); } } function ExecutionNodeComponent_Case_7_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "i", 7); } } function ExecutionNodeComponent_Case_9_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "i", 9); } } function ExecutionNodeComponent_Case_10_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "i", 10); } } function ExecutionNodeComponent_Case_11_Conditional_0_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "i"); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(2); i0.ɵɵclassMap(ctx_r1.getActionIconClass()); } } function ExecutionNodeComponent_Case_11_Conditional_1_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "i", 20); } } function ExecutionNodeComponent_Case_11_Template(rf, ctx) { if (rf & 1) { i0.ɵɵconditionalCreate(0, ExecutionNodeComponent_Case_11_Conditional_0_Template, 1, 2, "i", 19)(1, ExecutionNodeComponent_Case_11_Conditional_1_Template, 1, 0, "i", 20); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵconditional(ctx_r1.getActionIconClass() ? 0 : 1); } } function ExecutionNodeComponent_Case_12_Conditional_0_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "img", 21); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(2); i0.ɵɵproperty("src", ctx_r1.getAgentLogoURL(), i0.ɵɵsanitizeUrl)("alt", ctx_r1.getAgentName() || "Agent"); } } function ExecutionNodeComponent_Case_12_Conditional_1_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "i"); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(2); i0.ɵɵclassMap(ctx_r1.getAgentIconClass()); } } function ExecutionNodeComponent_Case_12_Conditional_2_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "i", 22); } } function ExecutionNodeComponent_Case_12_Template(rf, ctx) { if (rf & 1) { i0.ɵɵconditionalCreate(0, ExecutionNodeComponent_Case_12_Conditional_0_Template, 1, 2, "img", 21)(1, ExecutionNodeComponent_Case_12_Conditional_1_Template, 1, 2, "i", 19)(2, ExecutionNodeComponent_Case_12_Conditional_2_Template, 1, 0, "i", 22); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵconditional(ctx_r1.getAgentLogoURL() ? 0 : ctx_r1.getAgentIconClass() ? 1 : 2); } } function ExecutionNodeComponent_Case_13_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "i", 11); } } function ExecutionNodeComponent_Case_14_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "i", 12); } } function ExecutionNodeComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "small", 14); i0.ɵɵtext(1); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵadvance(); i0.ɵɵtextInterpolate1("[Level ", ctx_r1.depth, "]"); } } function ExecutionNodeComponent_Conditional_18_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "span", 15); i0.ɵɵtext(1); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵadvance(); i0.ɵɵtextInterpolate(ctx_r1.formatDuration(ctx_r1.getDuration())); } } function ExecutionNodeComponent_Conditional_19_Conditional_1_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "span", 23); i0.ɵɵtext(1); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(2); i0.ɵɵadvance(); i0.ɵɵtextInterpolate1("", ctx_r1.getTokensUsed(), " tokens"); } } function ExecutionNodeComponent_Conditional_19_Conditional_2_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "span", 24); i0.ɵɵtext(1); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(2); i0.ɵɵadvance(); i0.ɵɵtextInterpolate1("$", ctx_r1.getCost().toFixed(4)); } } function ExecutionNodeComponent_Conditional_19_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "span", 16); i0.ɵɵconditionalCreate(1, ExecutionNodeComponent_Conditional_19_Conditional_1_Template, 2, 1, "span", 23); i0.ɵɵconditionalCreate(2, ExecutionNodeComponent_Conditional_19_Conditional_2_Template, 2, 1, "span", 24); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵadvance(); i0.ɵɵconditional(ctx_r1.getTokensUsed() ? 1 : -1); i0.ɵɵadvance(); i0.ɵɵconditional(ctx_r1.getCost() ? 2 : -1); } } function ExecutionNodeComponent_Conditional_20_Template(rf, ctx) { if (rf & 1) { const _r3 = i0.ɵɵgetCurrentView(); i0.ɵɵelementStart(0, "button", 25); i0.ɵɵlistener("click", function ExecutionNodeComponent_Conditional_20_Template_button_click_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onToggleDetails($event)); }); i0.ɵɵelement(1, "i", 26); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵproperty("title", ctx_r1.detailsExpanded ? "Hide details" : "Show details"); i0.ɵɵadvance(); i0.ɵɵclassProp("fa-info", !ctx_r1.detailsExpanded)("fa-times", ctx_r1.detailsExpanded); } } function ExecutionNodeComponent_Conditional_21_Conditional_0_Conditional_1_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div", 31); i0.ɵɵtext(1); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(3); i0.ɵɵadvance(); i0.ɵɵtextInterpolate(ctx_r1.step.StepName); } } function ExecutionNodeComponent_Conditional_21_Conditional_0_Conditional_2_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "div", 32); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(3); i0.ɵɵproperty("innerHTML", ctx_r1.formatMarkdown(ctx_r1.getDetailsMarkdown()), i0.ɵɵsanitizeHtml); } } function ExecutionNodeComponent_Conditional_21_Conditional_0_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div", 27); i0.ɵɵconditionalCreate(1, ExecutionNodeComponent_Conditional_21_Conditional_0_Conditional_1_Template, 2, 1, "div", 31); i0.ɵɵconditionalCreate(2, ExecutionNodeComponent_Conditional_21_Conditional_0_Conditional_2_Template, 1, 1, "div", 32); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(2); i0.ɵɵadvance(); i0.ɵɵconditional(ctx_r1.isNameTruncated() ? 1 : -1); i0.ɵɵadvance(); i0.ɵɵconditional(ctx_r1.getDetailsMarkdown() ? 2 : -1); } } function ExecutionNodeComponent_Conditional_21_Conditional_2_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div", 29)(1, "div", 33); i0.ɵɵelement(2, "i", 34); i0.ɵɵtext(3, " Error "); i0.ɵɵelementEnd(); i0.ɵɵelementStart(4, "div", 35); i0.ɵɵtext(5); i0.ɵɵelementEnd()(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(2); i0.ɵɵadvance(5); i0.ɵɵtextInterpolate(ctx_r1.step.ErrorMessage); } } function ExecutionNodeComponent_Conditional_21_Conditional_3_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div", 30)(1, "div", 33); i0.ɵɵelement(2, "i", 36); i0.ɵɵtext(3, " Input "); i0.ɵɵelementEnd(); i0.ɵɵelementStart(4, "div", 35); i0.ɵɵtext(5); i0.ɵɵelementEnd()(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(2); i0.ɵɵadvance(5); i0.ɵɵtextInterpolate(ctx_r1.getInputPreview()); } } function ExecutionNodeComponent_Conditional_21_Conditional_4_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div", 30)(1, "div", 33); i0.ɵɵelement(2, "i", 37); i0.ɵɵtext(3, " Output "); i0.ɵɵelementEnd(); i0.ɵɵelementStart(4, "div", 35); i0.ɵɵtext(5); i0.ɵɵelementEnd()(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(2); i0.ɵɵadvance(5); i0.ɵɵtextInterpolate(ctx_r1.getOutputPreview()); } } function ExecutionNodeComponent_Conditional_21_Conditional_5_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div", 30)(1, "div", 35); i0.ɵɵtext(2, "No additional details available for this step."); i0.ɵɵelementEnd()(); } } function ExecutionNodeComponent_Conditional_21_Template(rf, ctx) { if (rf & 1) { i0.ɵɵconditionalCreate(0, ExecutionNodeComponent_Conditional_21_Conditional_0_Template, 3, 2, "div", 27); i0.ɵɵelementStart(1, "div", 28); i0.ɵɵconditionalCreate(2, ExecutionNodeComponent_Conditional_21_Conditional_2_Template, 6, 1, "div", 29); i0.ɵɵconditionalCreate(3, ExecutionNodeComponent_Conditional_21_Conditional_3_Template, 6, 1, "div", 30); i0.ɵɵconditionalCreate(4, ExecutionNodeComponent_Conditional_21_Conditional_4_Template, 6, 1, "div", 30); i0.ɵɵconditionalCreate(5, ExecutionNodeComponent_Conditional_21_Conditional_5_Template, 3, 0, "div", 30); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵconditional(ctx_r1.getDetailsMarkdown() || ctx_r1.isNameTruncated() ? 0 : -1); i0.ɵɵadvance(2); i0.ɵɵconditional(ctx_r1.step.ErrorMessage ? 2 : -1); i0.ɵɵadvance(); i0.ɵɵconditional(ctx_r1.getInputPreview() ? 3 : -1); i0.ɵɵadvance(); i0.ɵɵconditional(ctx_r1.getOutputPreview() ? 4 : -1); i0.ɵɵadvance(); i0.ɵɵconditional(!ctx_r1.step.ErrorMessage && !ctx_r1.getInputPreview() && !ctx_r1.getOutputPreview() && !ctx_r1.getDetailsMarkdown() && !ctx_r1.isNameTruncated() ? 5 : -1); } } export class ExecutionNodeComponent { constructor() { this.depth = 0; this.agentPath = []; this.expanded = false; this.detailsExpanded = false; this.toggleNode = new EventEmitter(); this.toggleDetails = new EventEmitter(); this.userInteracted = new EventEmitter(); } hasChildren() { return this.step.StepType === 'Sub-Agent' && !!this.step.SubAgentRun?.Steps && this.step.SubAgentRun.Steps.length > 0; } onToggleChildren(event) { if (event) { event.stopPropagation(); } if (this.hasChildren()) { this.toggleNode.emit(); this.userInteracted.emit(); } } onToggleDetails(event) { if (event) { event.stopPropagation(); } if (this.hasNodeDetails()) { this.toggleDetails.emit(); this.userInteracted.emit(); } } onDoubleClick() { if (this.hasChildren()) { this.toggleNode.emit(); this.userInteracted.emit(); } } hasNodeDetails() { return !!this.step.InputData || !!this.step.OutputData || !!this.step.ErrorMessage || !!this.getDetailsMarkdown() || this.isNameTruncated(); } getTruncatedName() { const maxLength = 120; const name = this.getStepName(); if (name.length <= maxLength) { return name; } return name.substring(0, maxLength) + '...'; } isNameTruncated() { return this.step.StepName.length > 120; } formatDuration(ms) { if (ms < 1000) return `${ms}ms`; if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`; const minutes = Math.floor(ms / 60000); const seconds = Math.floor((ms % 60000) / 1000); return `${minutes}m ${seconds}s`; } getNodeTitle() { if (this.step.StepType === 'Sub-Agent' && this.getAgentName()) { return `Sub-agent: ${this.getAgentName()}`; } if (this.step.StepType === 'Actions' && this.getActionName()) { return `Action: ${this.getActionName()}`; } return this.step.StepType; } // Getter methods for step data getStepName() { // Extract just the first line if the name contains markdown const lines = this.step.StepName.split('\n'); return lines[0].trim(); } getStepTypeClass() { const typeMap = { 'Validation': 'validation', 'Prompt': 'prompt', 'Actions': 'action', 'Sub-Agent': 'sub-agent', 'Decision': 'decision', 'Chat': 'chat' }; return typeMap[this.step.StepType] || 'prompt'; } getStatusClass() { const statusMap = { 'Pending': 'pending', 'Running': 'running', 'Completed': 'completed', 'Failed': 'failed', 'Cancelled': 'failed', 'Paused': 'pending' }; // Use override if provided, otherwise use actual status const status = this.overrideDisplayStatus || this.step.Status; return statusMap[status] || 'pending'; } getDuration() { if (!this.step.StartedAt || !this.step.CompletedAt) return 0; return new Date(this.step.CompletedAt).getTime() - new Date(this.step.StartedAt).getTime(); } getTokensUsed() { // Check if this is a prompt step with token data if (this.step.StepType === 'Prompt' && this.step.PromptRun) { return this.step.PromptRun.TokensUsed || undefined; } return undefined; } getCost() { // Check if this is a prompt step with cost data if (this.step.StepType === 'Prompt' && this.step.PromptRun) { return this.step.PromptRun.TotalCost || undefined; } return undefined; } getDetailsMarkdown() { // Check if the step name contains markdown details after the first line const lines = this.step.StepName.split('\n'); if (lines.length > 1) { return lines.slice(1).join('\n').trim(); } return undefined; } getInputPreview() { if (!this.step.InputData) return undefined; try { const parsed = JSON.parse(this.step.InputData); // Extract meaningful preview if (parsed.promptName) return `Prompt: ${parsed.promptName}`; if (parsed.actionName) return `Action: ${parsed.actionName}`; if (parsed.subAgentName) return `Sub-agent: ${parsed.subAgentName}`; if (parsed.message) return parsed.message; if (parsed.userMessage) return parsed.userMessage; // Fallback to stringified preview const str = JSON.stringify(parsed, null, 2); return str.length > 500 ? str.substring(0, 500) + '...' : str; } catch { return typeof this.step.InputData === 'string' ? this.step.InputData : JSON.stringify(this.step.InputData); } } getOutputPreview() { if (!this.step.OutputData) return undefined; try { const parsed = JSON.parse(this.step.OutputData); // Show action results clearly if (parsed.actionResult) { const result = parsed.actionResult; let preview = ''; if (result.success !== undefined) { preview += `Success: ${result.success}\n`; } if (result.resultCode) { preview += `Result Code: ${result.resultCode}\n`; } if (result.message) { preview += `Message: ${result.message}\n`; } if (result.result) { preview += `Result: ${typeof result.result === 'object' ? JSON.stringify(result.result, null, 2) : result.result}`; } return preview.trim(); } // Show prompt results if (parsed.promptResult) { const result = parsed.promptResult; let preview = ''; if (result.success !== undefined) { preview += `Success: ${result.success}\n`; } if (result.content) { preview += `Content: ${result.content}`; } return preview; } // Fallback const str = JSON.stringify(parsed, null, 2); return str.length > 500 ? str.substring(0, 500) + '...' : str; } catch { return typeof this.step.OutputData === 'string' ? this.step.OutputData : JSON.stringify(this.step.OutputData); } } // Methods to extract metadata from input/output data getAgentName() { if (this.step.StepType === 'Sub-Agent' && this.step.SubAgentRun) { return this.step.SubAgentRun.Agent || undefined; } return this.parseMetadata('subAgentName'); } getAgentIconClass() { return this.parseMetadata('subAgentIconClass') || this.parseMetadata('agentIconClass'); } getAgentLogoURL() { return this.parseMetadata('subAgentLogoURL') || this.parseMetadata('agentLogoURL'); } getActionName() { if (this.step.StepType === 'Actions' && this.step.ActionExecutionLog) { return this.step.ActionExecutionLog.Action; } return this.parseMetadata('actionName'); } getActionIconClass() { return this.parseMetadata('actionIconClass'); } parseMetadata(key) { if (!this.step.InputData) return undefined; try { const parsed = JSON.parse(this.step.InputData); return parsed[key]; } catch { return undefined; } } formatMarkdown(markdown) { // Basic markdown formatting let html = markdown; // Headers html = html.replace(/^### (.*$)/gim, '<h4>$1</h4>'); html = html.replace(/^## (.*$)/gim, '<h3>$1</h3>'); html = html.replace(/^# (.*$)/gim, '<h2>$1</h2>'); // Bold html = html.replace(/\*\*(.*)\*\*/g, '<strong>$1</strong>'); // Italic html = html.replace(/\*(.*)\*/g, '<em>$1</em>'); // Detect and linkify URLs const urlRegex = /(?:https?:\/\/|www\.)[^\s<]+/gi; html = html.replace(urlRegex, (url) => { const href = url.startsWith('http') ? url : `https://${url}`; return `<a href="${href}" target="_blank" rel="noopener noreferrer" style="color: #2196f3; text-decoration: underline;">${url}</a>`; }); // Line breaks html = html.replace(/\n/g, '<br>'); // Lists html = html.replace(/^\* (.+)$/gim, '<li>$1</li>'); html = html.replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>'); // Code blocks html = html.replace(/```(.*?)```/gs, '<pre><code>$1</code></pre>'); // Inline code html = html.replace(/`([^`]+)`/g, '<code>$1</code>'); return html; } static { this.ɵfac = function ExecutionNodeComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ExecutionNodeComponent)(); }; } static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ExecutionNodeComponent, selectors: [["mj-execution-node"]], inputs: { step: "step", depth: "depth", agentPath: "agentPath", expanded: "expanded", detailsExpanded: "detailsExpanded", overrideDisplayStatus: "overrideDisplayStatus" }, outputs: { toggleNode: "toggleNode", toggleDetails: "toggleDetails", userInteracted: "userInteracted" }, standalone: false, decls: 22, vars: 20, consts: [[1, "tree-node"], [1, "node-header", 3, "dblclick"], ["title", "Toggle children", 1, "expand-icon", "fa-solid", 3, "fa-chevron-down", "fa-chevron-right"], [1, "status-icon"], [1, "fa-regular", "fa-circle"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-check-circle"], [1, "fa-solid", "fa-times-circle"], [1, "type-icon", 3, "title"], [1, "fa-solid", "fa-shield-halved"], [1, "fa-solid", "fa-brain"], [1, "fa-solid", "fa-code-branch"], [1, "fa-solid", "fa-comments"], [1, "node-name"], [2, "color", "#666", "margin-right", "8px"], [1, "node-duration"], [1, "node-metrics"], [1, "details-toggle-btn", 3, "title"], ["title", "Toggle children", 1, "expand-icon", "fa-solid", 3, "click"], [3, "class"], [1, "fa-solid", "fa-bolt"], [1, "agent-logo-icon", 3, "src", "alt"], [1, "fa-solid", "fa-sitemap"], [1, "tokens"], [1, "cost"], [1, "details-toggle-btn", 3, "click", "title"], [1, "fa-solid"], [1, "markdown-details"], [1, "node-details"], [1, "detail-section", "error"], [1, "detail-section"], [1, "full-name"], [1, "detail-content", "markdown", 3, "innerHTML"], [1, "detail-label"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "detail-content"], [1, "fa-solid", "fa-sign-in-alt"], [1, "fa-solid", "fa-sign-out-alt"]], template: function ExecutionNodeComponent_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div", 0)(1, "div", 1); i0.ɵɵlistener("dblclick", function ExecutionNodeComponent_Template_div_dblclick_1_listener() { return ctx.onDoubleClick(); }); i0.ɵɵconditionalCreate(2, ExecutionNodeComponent_Conditional_2_Template, 1, 4, "i", 2); i0.ɵɵelementStart(3, "span", 3); i0.ɵɵconditionalCreate(4, ExecutionNodeComponent_Case_4_Template, 1, 0, "i", 4)(5, ExecutionNodeComponent_Case_5_Template, 1, 0, "i", 5)(6, ExecutionNodeComponent_Case_6_Template, 1, 0, "i", 6)(7, ExecutionNodeComponent_Case_7_Template, 1, 0, "i", 7); i0.ɵɵelementEnd(); i0.ɵɵelementStart(8, "span", 8); i0.ɵɵconditionalCreate(9, ExecutionNodeComponent_Case_9_Template, 1, 0, "i", 9)(10, ExecutionNodeComponent_Case_10_Template, 1, 0, "i", 10)(11, ExecutionNodeComponent_Case_11_Template, 2, 1)(12, ExecutionNodeComponent_Case_12_Template, 3, 1)(13, ExecutionNodeComponent_Case_13_Template, 1, 0, "i", 11)(14, ExecutionNodeComponent_Case_14_Template, 1, 0, "i", 12); i0.ɵɵelementEnd(); i0.ɵɵelementStart(15, "span", 13); i0.ɵɵconditionalCreate(16, ExecutionNodeComponent_Conditional_16_Template, 2, 1, "small", 14); i0.ɵɵtext(17); i0.ɵɵelementEnd(); i0.ɵɵconditionalCreate(18, ExecutionNodeComponent_Conditional_18_Template, 2, 1, "span", 15); i0.ɵɵconditionalCreate(19, ExecutionNodeComponent_Conditional_19_Template, 3, 2, "span", 16); i0.ɵɵconditionalCreate(20, ExecutionNodeComponent_Conditional_20_Template, 2, 5, "button", 17); i0.ɵɵelementEnd(); i0.ɵɵconditionalCreate(21, ExecutionNodeComponent_Conditional_21_Template, 6, 5); i0.ɵɵelementEnd(); } if (rf & 2) { let tmp_6_0; let tmp_8_0; i0.ɵɵclassMap("depth-" + ctx.depth + " type-" + ctx.getStepTypeClass()); i0.ɵɵclassProp("expanded", ctx.expanded)("has-children", ctx.hasChildren())("details-expanded", ctx.detailsExpanded); i0.ɵɵadvance(2); i0.ɵɵconditional(ctx.hasChildren() ? 2 : -1); i0.ɵɵadvance(); i0.ɵɵclassMap("status-" + ctx.getStatusClass()); i0.ɵɵadvance(); i0.ɵɵconditional((tmp_6_0 = ctx.getStatusClass()) === "pending" ? 4 : tmp_6_0 === "running" ? 5 : tmp_6_0 === "completed" ? 6 : tmp_6_0 === "failed" ? 7 : -1); i0.ɵɵadvance(4); i0.ɵɵproperty("title", ctx.getNodeTitle()); i0.ɵɵadvance(); i0.ɵɵconditional((tmp_8_0 = ctx.getStepTypeClass()) === "validation" ? 9 : tmp_8_0 === "prompt" ? 10 : tmp_8_0 === "action" ? 11 : tmp_8_0 === "sub-agent" ? 12 : tmp_8_0 === "decision" ? 13 : tmp_8_0 === "chat" ? 14 : -1); i0.ɵɵadvance(7); i0.ɵɵconditional(ctx.step.StepType === "Sub-Agent" && ctx.depth > 0 ? 16 : -1); i0.ɵɵadvance(); i0.ɵɵtextInterpolate1(" ", ctx.getTruncatedName(), " "); i0.ɵɵadvance(); i0.ɵɵconditional(ctx.getDuration() ? 18 : -1); i0.ɵɵadvance(); i0.ɵɵconditional(ctx.getTokensUsed() || ctx.getCost() ? 19 : -1); i0.ɵɵadvance(); i0.ɵɵconditional(ctx.hasNodeDetails() ? 20 : -1); i0.ɵɵadvance(); i0.ɵɵconditional(ctx.detailsExpanded ? 21 : -1); } }, styles: ["[_nghost-%COMP%] {\n display: block;\n width: 100%;\n }\n \n \n\n .tree-node[_ngcontent-%COMP%] {\n margin: 4px 0;\n position: relative;\n }\n \n \n\n .depth-0[_ngcontent-%COMP%] { \n margin-left: 0;\n position: relative;\n z-index: 2;\n }\n .depth-0[_ngcontent-%COMP%]::after {\n display: none;\n }\n .depth-0[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n border: 2px solid var(--mj-border-default);\n font-weight: 600;\n position: relative;\n z-index: 10;\n }\n\n \n\n .depth-1[_ngcontent-%COMP%] {\n margin-left: 24px;\n }\n .depth-1[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n border-color: var(--mj-border-default);\n }\n\n .depth-2[_ngcontent-%COMP%] {\n margin-left: 48px;\n }\n .depth-2[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-color: var(--mj-border-default);\n }\n\n .depth-3[_ngcontent-%COMP%] {\n margin-left: 72px;\n }\n .depth-3[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-color: var(--mj-border-strong);\n }\n\n .depth-4[_ngcontent-%COMP%] {\n margin-left: 96px;\n }\n .depth-4[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-color: var(--mj-border-strong);\n }\n\n .depth-5[_ngcontent-%COMP%] {\n margin-left: 120px;\n }\n .depth-5[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-color: var(--mj-border-strong);\n }\n\n .depth-6[_ngcontent-%COMP%] {\n margin-left: 144px;\n }\n .depth-6[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-color: var(--mj-border-strong);\n }\n\n \n\n .depth-0[_ngcontent-%COMP%] { \n position: relative;\n overflow: hidden;\n z-index: 2;\n }\n .depth-0[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n position: relative;\n z-index: 2;\n }\n\n \n\n .depth-0.has-children[_ngcontent-%COMP%]::before {\n content: '';\n position: absolute;\n left: 12px;\n top: 0;\n width: 2px;\n height: 100%;\n border-left: 2px dotted var(--mj-border-strong);\n z-index: 0; \n\n }\n\n \n\n .depth-1[_ngcontent-%COMP%] {\n margin-left: 30px;\n padding-left: 12px;\n position: relative;\n z-index: 1;\n }\n .depth-1[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n position: relative;\n z-index: 1;\n }\n\n \n\n .depth-1[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n left: -15px;\n top: 12px;\n width: 25px;\n height: 2px;\n border-bottom: 2px dotted var(--mj-border-strong);\n z-index: 1;\n }\n\n \n\n .tree-node.details-expanded[_ngcontent-%COMP%] > .node-header[_ngcontent-%COMP%] {\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n border-bottom: 1px solid var(--mj-brand-primary);\n }\n\n .node-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n transition: all 0.2s ease;\n user-select: none;\n position: relative;\n z-index: 5;\n background: var(--mj-bg-surface);\n }\n\n .node-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-brand-primary) !important;\n }\n\n \n\n .tree-node.type-sub-agent[_ngcontent-%COMP%] > .node-header[_ngcontent-%COMP%] {\n border-left: 4px solid var(--mj-brand-primary);\n }\n\n \n\n .tree-node.type-action[_ngcontent-%COMP%] > .node-header[_ngcontent-%COMP%] {\n border-left: 4px solid var(--mj-status-success);\n }\n \n .expand-icon[_ngcontent-%COMP%] {\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n color: var(--mj-text-secondary);\n font-size: 10px;\n cursor: pointer;\n border-radius: 3px;\n transition: all 0.2s ease;\n z-index: 10; \n\n position: relative;\n }\n\n .expand-icon[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\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: var(--mj-text-muted); }\n .status-running[_ngcontent-%COMP%] { color: var(--mj-brand-primary); }\n .status-completed[_ngcontent-%COMP%] { color: var(--mj-status-success); }\n .status-failed[_ngcontent-%COMP%] { color: var(--mj-status-error); }\n\n .type-icon[_ngcontent-%COMP%] {\n width: 20px;\n text-align: center;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n color: var(--mj-text-secondary);\n }\n \n .agent-logo-icon[_ngcontent-%COMP%] {\n width: 16px;\n height: 16px;\n object-fit: cover;\n border-radius: 3px;\n }\n \n .node-name[_ngcontent-%COMP%] {\n flex: 1;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n }\n\n\n .node-duration[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-secondary);\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: var(--mj-text-secondary);\n }\n\n .cost[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-weight: 500;\n }\n\n \n\n .details-toggle-btn[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n cursor: pointer;\n transition: all 0.2s ease;\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-left: 4px;\n }\n\n .details-toggle-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n }\n\n .details-toggle-btn[_ngcontent-%COMP%]:active {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n }\n\n \n\n .tree-node.details-expanded[_ngcontent-%COMP%] .details-toggle-btn[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .tree-node.details-expanded[_ngcontent-%COMP%] .details-toggle-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n }\n\n .node-details[_ngcontent-%COMP%] {\n margin: 0 5px;\n padding: 16px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-top: none;\n border-radius: 0 0 6px 6px;\n font-size: 12px;\n position: relative;\n z-index: 4;\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: var(--mj-status-error-text);\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: var(--mj-text-secondary);\n }\n\n .detail-content[_ngcontent-%COMP%] {\n white-space: pre-wrap;\n word-break: break-word;\n color: var(--mj-text-primary);\n line-height: 1.4;\n }\n\n .markdown-details[_ngcontent-%COMP%] {\n padding: 16px 16px 0 16px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-bottom: none;\n border-top: none;\n margin: 0 5px;\n }\n\n .markdown[_ngcontent-%COMP%] h3[_ngcontent-%COMP%], .markdown[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 8px 0 4px 0;\n color: var(--mj-text-primary);\n }\n \n .markdown[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n font-size: 14px;\n }\n \n .markdown[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n font-size: 13px;\n }\n \n .markdown[_ngcontent-%COMP%] ul[_ngcontent-%COMP%] {\n margin: 4px 0;\n padding-left: 20px;\n }\n \n .markdown[_ngcontent-%COMP%] li[_ngcontent-%COMP%] {\n margin: 2px 0;\n }\n \n .markdown[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n padding: 1px 4px;\n border-radius: 3px;\n font-size: 12px;\n }\n\n .markdown[_ngcontent-%COMP%] pre[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n padding: 8px;\n border-radius: 4px;\n overflow-x: auto;\n margin: 4px 0;\n }\n\n .markdown[_ngcontent-%COMP%] pre[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n background: none;\n padding: 0;\n }\n\n .markdown[_ngcontent-%COMP%] strong[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n\n .markdown[_ngcontent-%COMP%] em[_ngcontent-%COMP%] {\n font-style: italic;\n color: var(--mj-text-secondary);\n }\n\n .full-name[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--mj-text-primary);\n padding-bottom: 8px;\n border-bottom: 1px solid var(--mj-border-default);\n word-wrap: break-word;\n }"], changeDetection: 0 }); } } (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ExecutionNodeComponent, [{ type: Component, args: [{ standalone: false, selector: 'mj-execution-node', changeDetection: ChangeDetectionStrategy.OnPush, template: ` <div class="tree-node" [class.expanded]="expanded" [class.has-children]="hasChildren()" [class.details-expanded]="detailsExpanded" [class]="'depth-' + depth + ' type-' + getStepTypeClass()"> <!-- Node Header --> <div class="node-header" (dblclick)="onDoubleClick()"> <!-- Expand/Collapse Icon - Only show if node has children --> @if (hasChildren()) { <i class="expand-icon fa-solid" [class.fa-chevron-down]="expanded" [class.fa-chevron-right]="!expanded" (click)="onToggleChildren($event)" title="Toggle children"></i> } <!-- Status Icon --> <span class="status-icon" [class]="'status-' + getStatusClass()"> @switch (getStatusClass()) { @case ('pending') { <i class="fa-regular fa-circle"></i> } @case ('running') { <i class="fa-solid fa-spinner fa-spin"></i> } @case ('completed') { <i class="fa-solid fa-check-circle"></i> } @case ('failed') { <i class="fa-solid fa-times-circle"></i> } } </span> <!-- Type Icon --> <span class="type-icon" [title]="getNodeTitle()"> @switch (getStepTypeClass()) { @case ('validation') { <i class="fa-solid fa-shield-halved"></i> } @case ('prompt') { <i class="fa-solid fa-brain"></i> } @case ('action') { @if (getActionIconClass()) { <i [class]="getActionIconClass()"></i> } @else { <i class="fa-solid fa-bolt"></i> } } @case ('sub-agent') { @if (getAgentLogoURL()) { <img [src]="getAgentLogoURL()" [alt]="getAgentName() || 'Agent'" class="agent-logo-icon"> } @else if (getAgentIconClass()) { <i [class]="getAgentIconClass()"></i> } @else { <i class="fa-solid fa-sitemap"></i> } } @case ('decision') { <i class="fa-solid fa-code-branch"></i> } @case ('chat') { <i class="fa-solid fa-comments"></i> } } </span> <!-- Node Name with depth indicator for sub-agents --> <span class="node-name"> @if (step.StepType === 'Sub-Agent' && depth > 0) { <small style="color: #666; margin-right: 8px;">[Level {{ depth }}]</small> } {{ getTruncatedName() }} </span> <!-- Duration --> @if (getDuration()) { <span class="node-duration">{{ formatDuration(getDuration()) }}</span> } <!-- Tokens/Cost --> @if (getTokensUsed() || getCost()) { <span class="node-metrics"> @if (getTokensUsed()) { <span class="tokens">{{ getTokensUsed() }} tokens</span> } @if (getCost()) { <span class="cost">\${{ getCost()!.toFixed(4) }}</span> } </span> } <!-- Details Toggle Button - Only show if node has details --> @if (hasNodeDetails()) { <button class="details-toggle-btn" (click)="onToggleDetails($event)" [title]="detailsExpanded ? 'Hide details' : 'Show details'"> <i class="fa-solid" [class.fa-info]="!detailsExpanded" [class.fa-times]="detailsExpanded"></i> </button> } </div> <!-- Node Details (when details are expanded) --> @if (detailsExpanded) { <!-- Show markdown details first if available --> @if (getDetailsMarkdown() || isNameTruncated()) { <div class="markdown-details"> @if (isNameTruncated()) { <div class="full-name">{{ step.StepName }}</div> } @if (getDetailsMarkdown()) { <div class="detail-content markdown" [innerHTML]="formatMarkdown(getDetailsMarkdown()!)"></div> } </div> } <!-- Always show details section if node is expanded, even if some content is empty --> <div class="node-details"> @if (step.ErrorMessage) { <div class="detail-section error"> <div class="detail-label"> <i class="fa-solid fa-exclamation-triangle"></i> Error </div> <div class="detail-content">{{ step.ErrorMessage }}</div> </div> } @if (getInputPreview()) { <div class="detail-section"> <div class="detail-label"> <i class="fa-solid fa-sign-in-alt"></i> Input </div> <div class="detail-content">{{ getInputPreview() }}</div> </div> } @if (getOutputPreview()) { <div class="detail-section"> <div class="detail-label"> <i class="fa-solid fa-sign-out-alt"></i> Output </div> <div class="detail-content">{{ getOutputPreview() }}</div> </div> } @if (!step.ErrorMessage && !getInputPreview() && !getOutputPreview() && !getDetailsMarkdown() && !isNameTruncated()) { <div class="detail-section"> <div class="detail-content">No additional details available for this step.</div> </div> } </div> } <!-- Note: Sub-agent children are rendered by the parent component to maintain proper depth tracking --> </div> `, styles: ["\n :host {\n display: block;\n width: 100%;\n }\n \n /* Depth-based indentation - each level indents by 24px */\n .tree-node {\n margin: 4px 0;\n position: relative;\n }\n \n /* Root level nodes (depth 0) */\n .depth-0 { \n margin-left: 0;\n position: relative;\n z-index: 2;\n }\n .depth-0::after {\n display: none;\n }\n .depth-0 .node-header {\n background: var(--mj-bg-surface-card);\n border: 2px solid var(--mj-border-default);\n font-weight: 600;\n position: relative;\n z-index: 10;\n }\n\n /* Sub-level nodes with increasing indentation */\n .depth-1 {\n margin-left: 24px;\n }\n .depth-1 .node-header {\n background: var(--mj-bg-surface-card);\n border-color: var(--mj-border-default);\n }\n\n .depth-2 {\n margin-left: 48px;\n }\n .depth-2 .node-header {\n background: var(--mj-bg-surface);\n border-color: var(--mj-border-default);\n }\n\n .depth-3 {\n margin-left: 72px;\n }\n .depth-3 .node-header {\n background: var(--mj-bg-surface);\n border-color: var(--mj-border-strong);\n }\n\n .depth-4 {\n margin-left: 96px;\n }\n .depth-4 .node-header {\n background: var(--mj-bg-surface);\n border-color: var(--mj-border-strong);\n }\n\n .depth-5 {\n margin-left: 120px;\n }\n .depth-5 .node-header {\n background: var(--mj-bg-surface);\n border-color: var(--mj-border-strong);\n }\n\n .depth-6 {\n margin-left: 144px;\n }\n .depth-6 .node-header {\n background: var(--mj-bg-surface);\n border-color: var(--mj-border-strong);\n }\n\n /* Root level - higher z-index to hide lines behind it */\n .depth-0 { \n position: relative;\n overflow: hidden;\n z-index: 2;\n }\n .depth-0 .node-header {\n background: var(--mj-bg-surface-card);\n position: relative;\n z-index: 2;\n }\n\n /* Only add left padding for nodes with children (that show chevrons) */\n .depth-0.has-children::before {\n content: '';\n position: absolute;\n left: 12px;\n top: 0;\n width: 2px;\n height: 100%;\n border-left: 2px dotted var(--mj-border-strong);\n z-index: 0; /* Behind everything */\n }\n\n /* Child level - only for nodes that actually have parent chevrons */\n .depth-1 {\n margin-left: 30px;\n padding-left: 12px;\n position: relative;\n z-index: 1;\n }\n .depth-1 .node-header {\n background: var(--mj-bg-surface-card);\n position: relative;\n z-index: 1;\n }\n\n /* Horizontal line connecting to each child node - only when parent has children */\n .depth-1::after {\n content: '';\n position: absolute;\n left: -15px;\n top: 12px;\n width: 25px;\n height: 2px;\n border-bottom: 2px dotted var(--mj-border-strong);\n z-index: 1;\n }\n\n /* Visual indicator when details are expanded */\n .tree-node.details-expanded > .node-header {\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n border-bottom: 1px solid var(--mj-brand-primary);\n }\n\n .node-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n transition: all 0.2s ease;\n user-select: none;\n position: relative;\n z-index: 5;\n background: var(--mj-bg-surface);\n }\n\n .node-header:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-brand-primary) !important;\n }\n\n /* Sub-agent specific styling */\n .tree-node.type-sub-agent > .node-header {\n border-left: 4px solid var(--mj-brand-primary);\n }\n\n /* Action specific styling */\n .tree-node.type-action > .node-header {\n border-left: 4px solid var(--mj-status-success);\n }\n \n .expand-icon {\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n color: var(--mj-text-secondary);\n font-size: 10px;\n cursor: pointer;\n border-radius: 3px;\n transition: all 0.2s ease;\n z-index: 10; /* Ensure expand icon is clickable */\n position: relative;\n }\n\n .expand-icon:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n\n .status-icon {\n