claude-mpm 4.1.23__py3-none-any.whl → 4.1.24__py3-none-any.whl
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.
- claude_mpm/VERSION +1 -1
- claude_mpm/dashboard/static/css/activity.css +20 -0
- claude_mpm/dashboard/static/dist/chunks/unified-data-viewer.B7wmm2bD.js +2 -0
- claude_mpm/dashboard/static/dist/components/activity-tree.js +1 -1
- claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/dashboard.js +1 -1
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +5 -17
- {claude_mpm-4.1.23.dist-info → claude_mpm-4.1.24.dist-info}/METADATA +1 -1
- {claude_mpm-4.1.23.dist-info → claude_mpm-4.1.24.dist-info}/RECORD +13 -13
- claude_mpm/dashboard/static/dist/chunks/unified-data-viewer.CvAmgM3l.js +0 -2
- {claude_mpm-4.1.23.dist-info → claude_mpm-4.1.24.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.23.dist-info → claude_mpm-4.1.24.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.23.dist-info → claude_mpm-4.1.24.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.23.dist-info → claude_mpm-4.1.24.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.1.
|
|
1
|
+
4.1.24
|
|
@@ -1741,6 +1741,26 @@ body .activity-tree-container .linear-tree .tree-node.tool.privileged-tool .tree
|
|
|
1741
1741
|
font-size: 0.9em;
|
|
1742
1742
|
}
|
|
1743
1743
|
|
|
1744
|
+
/* Horizontal status line for TodoWrite */
|
|
1745
|
+
.todo-status-line {
|
|
1746
|
+
display: flex;
|
|
1747
|
+
gap: 20px;
|
|
1748
|
+
padding: 8px 12px;
|
|
1749
|
+
background: #f8fafc;
|
|
1750
|
+
border-radius: 6px;
|
|
1751
|
+
margin-bottom: 12px;
|
|
1752
|
+
font-size: 0.95em;
|
|
1753
|
+
border: 1px solid #e2e8f0;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
.todo-status-line .status-inline {
|
|
1757
|
+
display: inline-flex;
|
|
1758
|
+
align-items: center;
|
|
1759
|
+
gap: 4px;
|
|
1760
|
+
font-weight: 500;
|
|
1761
|
+
color: #475569;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1744
1764
|
/* Todo list primary display */
|
|
1745
1765
|
.todo-list-primary {
|
|
1746
1766
|
padding: 10px 0;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
class s{constructor(s="module-data-content"){this.container=document.getElementById(s),this.currentData=null,this.currentType=null}display(s,a=null){if(this.container)switch(this.currentData=s,this.currentType=a,a||(a=this.detectType(s)),this.container.innerHTML="",a){case"event":this.displayEvent(s);break;case"agent":this.displayAgent(s);break;case"tool":this.displayTool(s);break;case"todo":this.displayTodo(s);break;case"instruction":this.displayInstruction(s);break;case"session":this.displaySession(s);break;case"file_operation":this.displayFileOperation(s);break;case"hook":this.displayHook(s);break;default:this.displayGeneric(s)}else console.warn("UnifiedDataViewer: Container not found")}detectType(s){return s&&"object"==typeof s?s.hook_event_name||s.event_type||s.type&&s.timestamp?"event":s.agent_name||s.agentName||s.name&&("active"===s.status||"completed"===s.status)?"agent":s.tool_name||"TodoWrite"===s.name||"Read"===s.name||s.tool_parameters||s.params&&s.icon||s.name&&"tool"===s.type?"tool":!s.todos||s.name||s.params?s.content&&s.activeForm&&s.status&&!s.name&&!s.params?"todo":s.text&&s.preview&&"user_instruction"===s.type?"instruction":s.session_id&&(s.startTime||s.lastActivity)?"session":s.file_path&&(s.operations||s.operation)?"file_operation":s.event_type&&(s.hook_name||s.subtype)?"hook":"generic":"todo":"generic"}displayEvent(s){let a=`\n <div class="unified-viewer-header">\n <h6>${this.formatEventType(s)}</h6>\n <span class="unified-viewer-timestamp">${this.formatTimestamp(s.timestamp)}</span>\n </div>\n <div class="unified-viewer-content">\n `;if(a+='<div class="primary-data">',a+=this.formatEventDetails(s),s.tool_name||s.data?.tool_name){const n=s.tool_name||s.data.tool_name;a+=`\n <div class="detail-row highlight">\n <span class="detail-label">Tool:</span>\n <span class="detail-value">${this.getToolIcon(n)} ${n}</span>\n </div>\n `;const t=s.tool_parameters||s.data?.tool_parameters;t&&(t.file_path&&(a+=`\n <div class="detail-row">\n <span class="detail-label">File:</span>\n <span class="detail-value code">${t.file_path}</span>\n </div>\n `),t.command&&(a+=`\n <div class="detail-row">\n <span class="detail-label">Command:</span>\n <pre class="code-snippet">${this.escapeHtml(t.command)}</pre>\n </div>\n `))}a+="</div>",a+=this.createCollapsibleJSON(s,"Full Event Data"),a+="</div>",this.container.innerHTML=a}displayAgent(s){const a=this.getAgentIcon(s.name||s.agentName),n=s.name||s.agentName||"Unknown Agent",t=this.formatStatus(s.status);let e=`\n <div class="unified-viewer-header">\n <h6>${a} ${n}</h6>\n <span class="unified-viewer-status">${t}</span>\n </div>\n <div class="unified-viewer-content">\n `;if(e+='<div class="primary-data">',e+=`\n <div class="detail-row highlight">\n <span class="detail-label">Status:</span>\n <span class="detail-value ${this.formatStatusClass(t)}">${t}</span>\n </div>\n `,s.tools&&s.tools.length>0){const a=s.tools.filter(s=>"in_progress"===s.status),n=s.tools.filter(s=>"completed"===s.status);a.length>0&&(e+='\n <div class="active-tools-section">\n <span class="section-label">🔄 Active Tools:</span>\n <div class="tools-grid">\n ',a.forEach(s=>{e+=`\n <div class="tool-chip active">\n ${this.getToolIcon(s.name)} ${s.name}\n </div>\n `}),e+="</div></div>"),e+=`\n <div class="detail-row">\n <span class="detail-label">Tools Summary:</span>\n <span class="detail-value">\n ${a.length} active, ${n.length} completed, ${s.tools.length} total\n </span>\n </div>\n `}(s.currentTask||s.description)&&(e+=`\n <div class="detail-row">\n <span class="detail-label">Current Task:</span>\n <span class="detail-value">${s.currentTask||s.description}</span>\n </div>\n `),e+="</div>",e+=this.createCollapsibleJSON(s,"Full Agent Details"),e+="</div>",this.container.innerHTML=e}displayTool(s){const a=s.name||s.tool_name||"Unknown Tool",n=this.getToolIcon(a),t=this.formatStatus(s.status);if("TodoWrite"===a)return void this.displayTodoWriteTool(s);let e=`\n <div class="unified-viewer-header">\n <h6>${n} ${a}</h6>\n <span class="unified-viewer-status">${t}</span>\n </div>\n <div class="unified-viewer-content">\n `;const i=s.params||s.tool_parameters||{};"Read"===a||"Edit"===a||"Write"===a?i.file_path&&(e+=`\n <div class="primary-data">\n <div class="detail-row highlight">\n <span class="detail-label">📁 File:</span>\n <span class="detail-value code">${i.file_path}</span>\n </div>\n `,i.old_string&&(e+=`\n <div class="detail-row">\n <span class="detail-label">Old Text:</span>\n <pre class="code-snippet">${this.escapeHtml(i.old_string.substring(0,200))}${i.old_string.length>200?"...":""}</pre>\n </div>\n `),i.new_string&&(e+=`\n <div class="detail-row">\n <span class="detail-label">New Text:</span>\n <pre class="code-snippet">${this.escapeHtml(i.new_string.substring(0,200))}${i.new_string.length>200?"...":""}</pre>\n </div>\n `),e+="</div>"):"Bash"===a?i.command&&(e+=`\n <div class="primary-data">\n <div class="detail-row highlight">\n <span class="detail-label">💻 Command:</span>\n <pre class="code-snippet">${this.escapeHtml(i.command)}</pre>\n </div>\n </div>\n `):"Grep"===a||"Glob"===a?i.pattern&&(e+=`\n <div class="primary-data">\n <div class="detail-row highlight">\n <span class="detail-label">🔍 Pattern:</span>\n <span class="detail-value code">${this.escapeHtml(i.pattern)}</span>\n </div>\n `,i.path&&(e+=`\n <div class="detail-row">\n <span class="detail-label">Path:</span>\n <span class="detail-value">${i.path}</span>\n </div>\n `),e+="</div>"):"Task"===a&&i.subagent_type&&(e+=`\n <div class="primary-data">\n <div class="detail-row highlight">\n <span class="detail-label">🤖 Delegating to:</span>\n <span class="detail-value">${i.subagent_type} agent</span>\n </div>\n `,i.description&&(e+=`\n <div class="detail-row">\n <span class="detail-label">Task:</span>\n <span class="detail-value">${i.description}</span>\n </div>\n `),e+="</div>"),e+=`\n <div class="detail-row">\n <span class="detail-label">Status:</span>\n <span class="detail-value">${t}</span>\n </div>\n `,s.callCount&&(e+=`\n <div class="detail-row">\n <span class="detail-label">Call Count:</span>\n <span class="detail-value">${s.callCount}</span>\n </div>\n `),e+=this.createCollapsibleJSON(s,"Full Tool Details"),e+="</div>",this.container.innerHTML=e}displayTodoWriteTool(s){const a=this.formatStatus(s.status),n=(s.params||s.tool_parameters||{}).todos||[];let t=`\n <div class="unified-viewer-header">\n <h6>📝 TodoWrite</h6>\n <span class="unified-viewer-status">${a}</span>\n </div>\n <div class="unified-viewer-content">\n `;if(n.length>0){const s=this.getTodoStatusCounts(n);t+=`\n <div class="todo-status-line">\n <span class="status-inline">✅ ${s.completed} Done</span>\n <span class="status-inline">🔄 ${s.in_progress} Active</span>\n <span class="status-inline">⏳ ${s.pending} Pending</span>\n </div>\n `,t+='\n <div class="todo-list-primary">\n ',n.forEach((s,a)=>{const n=this.getCheckboxIcon(s.status),e="in_progress"===s.status&&s.activeForm||s.content,i=this.formatStatusClass(s.status);t+=`\n <div class="todo-item ${s.status}">\n <span class="todo-icon ${i}">${n}</span>\n <span class="todo-text">${this.escapeHtml(e)}</span>\n ${"in_progress"===s.status?'<span class="todo-badge active">ACTIVE</span>':""}\n </div>\n `}),t+="\n </div>\n "}else t+='\n <div class="detail-row">\n <span class="detail-value">No todos in list</span>\n </div>\n ';s.callCount&&s.callCount>1&&(t+=`\n <div class="detail-row">\n <span class="detail-label">Updates:</span>\n <span class="detail-value">${s.callCount}</span>\n </div>\n `),t+=this.createCollapsibleJSON(s,"Full Details"),t+="</div>",this.container.innerHTML=t}displayTodo(s){let a;a=s.todos&&Array.isArray(s.todos)?s.todos:Array.isArray(s)?s:s.content&&s.activeForm&&s.status?[s]:[];let n='\n <div class="unified-viewer-header">\n <h6>📋 Todo List</h6>\n </div>\n <div class="unified-viewer-content">\n ';a.length>0?(n+='\n <div class="todo-list-primary">\n ',a.forEach(s=>{const a=this.getCheckboxIcon(s.status),t="in_progress"===s.status&&s.activeForm||s.content,e=this.formatStatusClass(s.status);n+=`\n <div class="todo-item ${s.status}">\n <span class="todo-icon ${e}">${a}</span>\n <span class="todo-text">${this.escapeHtml(t)}</span>\n <span class="todo-status-text ${e}">${s.status.replace("_"," ")}</span>\n </div>\n `}),n+="\n </div>\n "):n+='\n <div class="detail-section">\n <div class="no-todos">No todo items found</div>\n </div>\n ',n+="</div>",this.container.innerHTML=n}displayInstruction(s){let a=`\n <div class="unified-viewer-header">\n <h6>💬 User Instruction</h6>\n <span class="unified-viewer-timestamp">${this.formatTimestamp(s.timestamp)}</span>\n </div>\n <div class="unified-viewer-content">\n `;a+=`\n <div class="primary-data">\n <div class="instruction-content">\n ${this.escapeHtml(s.text)}\n </div>\n <div class="instruction-meta">\n <span class="meta-item">📏 ${s.text.length} characters</span>\n <span class="meta-item">🕐 ${this.formatTimestamp(s.timestamp)}</span>\n </div>\n </div>\n `,Object.keys(s).length>3&&(a+=this.createCollapsibleJSON(s,"Full Instruction Data")),a+="</div>",this.container.innerHTML=a}displaySession(s){let a=`\n <div class="unified-viewer-header">\n <h6>🎯 Session: ${s.session_id||s.id}</h6>\n <span class="unified-viewer-status">${this.formatStatus(s.status||"active")}</span>\n </div>\n <div class="unified-viewer-content">\n <div class="detail-row">\n <span class="detail-label">Session ID:</span>\n <span class="detail-value">${s.session_id||s.id}</span>\n </div>\n <div class="detail-row">\n <span class="detail-label">Start Time:</span>\n <span class="detail-value">${this.formatTimestamp(s.startTime||s.timestamp)}</span>\n </div>\n `;s.working_directory&&(a+=`\n <div class="detail-row">\n <span class="detail-label">Working Directory:</span>\n <span class="detail-value">${s.working_directory}</span>\n </div>\n `),s.git_branch&&(a+=`\n <div class="detail-row">\n <span class="detail-label">Git Branch:</span>\n <span class="detail-value">${s.git_branch}</span>\n </div>\n `),void 0!==s.eventCount&&(a+=`\n <div class="detail-row">\n <span class="detail-label">Events:</span>\n <span class="detail-value">${s.eventCount}</span>\n </div>\n `),a+="</div>",this.container.innerHTML=a}displayFileOperation(s){let a=`\n <div class="unified-viewer-header">\n <h6>📄 File: ${s.file_path}</h6>\n <span class="unified-viewer-count">${s.operations?s.operations.length:1} operation${s.operations&&1!==s.operations.length?"s":""}</span>\n </div>\n <div class="unified-viewer-content">\n <div class="detail-row">\n <span class="detail-label">File Path:</span>\n <span class="detail-value">${s.file_path}</span>\n </div>\n `;s.operations&&Array.isArray(s.operations)&&(a+=`\n <div class="detail-section">\n <span class="detail-section-title">Operations:</span>\n <div class="operations-list">\n ${s.operations.map(s=>`\n <div class="operation-item">\n <span class="operation-type">${s.operation}</span>\n <span class="operation-timestamp">${this.formatTimestamp(s.timestamp)}</span>\n </div>\n `).join("")}\n </div>\n </div>\n `),a+="</div>",this.container.innerHTML=a}displayHook(s){let a=`\n <div class="unified-viewer-header">\n <h6>🔗 Hook: ${s.event_type||s.subtype||"unknown"}</h6>\n <span class="unified-viewer-timestamp">${this.formatTimestamp(s.timestamp)}</span>\n </div>\n <div class="unified-viewer-content">\n `;a+=this.formatHookDetails(s),a+="</div>",this.container.innerHTML=a}displayGeneric(s){let a=`\n <div class="unified-viewer-header">\n <h6>📊 Data Details</h6>\n ${s.timestamp?`<span class="unified-viewer-timestamp">${this.formatTimestamp(s.timestamp)}</span>`:""}\n </div>\n <div class="unified-viewer-content">\n `;if("object"==typeof s&&null!==s){const n=["id","name","type","status","timestamp","text","content","message"];for(let t of n)if(void 0!==s[t]){let n=s[t];"string"==typeof n&&n.length>200&&(n=n.substring(0,200)+"..."),a+=`\n <div class="detail-row">\n <span class="detail-label">${t}:</span>\n <span class="detail-value">${this.escapeHtml(String(n))}</span>\n </div>\n `}}else a+=`<div class="simple-value">${this.escapeHtml(String(s))}</div>`;a+="</div>",this.container.innerHTML=a}formatEventType(s){return s.type&&s.subtype?s.type===s.subtype||"generic"===s.subtype?s.type:`${s.type}.${s.subtype}`:s.type?s.type:s.hook_event_name?s.hook_event_name:"unknown"}formatEventDetails(s){switch(s.data,s.type){case"hook":return this.formatHookDetails(s);case"agent":return this.formatAgentEventDetails(s);case"todo":return this.formatTodoEventDetails(s);case"session":return this.formatSessionEventDetails(s);default:return this.formatGenericEventDetails(s)}}formatHookDetails(s){const a=s.data||{},n=s.subtype||s.event_type||"unknown";let t=`\n <div class="detail-row">\n <span class="detail-label">Hook Type:</span>\n <span class="detail-value">${n}</span>\n </div>\n `;switch(n){case"user_prompt":const s=a.prompt_text||a.prompt_preview||"";t+=`\n <div class="detail-row">\n <span class="detail-label">Prompt:</span>\n <div class="detail-value prompt-text">${this.escapeHtml(s)}</div>\n </div>\n `;break;case"pre_tool":case"post_tool":t+=`\n <div class="detail-row">\n <span class="detail-label">Tool:</span>\n <span class="detail-value">${a.tool_name||"Unknown tool"}</span>\n </div>\n `,a.operation_type&&(t+=`\n <div class="detail-row">\n <span class="detail-label">Operation:</span>\n <span class="detail-value">${a.operation_type}</span>\n </div>\n `),"post_tool"===n&&a.duration_ms&&(t+=`\n <div class="detail-row">\n <span class="detail-label">Duration:</span>\n <span class="detail-value">${a.duration_ms}ms</span>\n </div>\n `);break;case"subagent_start":case"subagent_stop":t+=`\n <div class="detail-row">\n <span class="detail-label">Agent:</span>\n <span class="detail-value">${a.agent_type||a.agent||"Unknown"}</span>\n </div>\n `,"subagent_start"===n&&a.prompt&&(t+=`\n <div class="detail-row">\n <span class="detail-label">Task:</span>\n <div class="detail-value">${this.escapeHtml(a.prompt)}</div>\n </div>\n `),"subagent_stop"===n&&a.reason&&(t+=`\n <div class="detail-row">\n <span class="detail-label">Reason:</span>\n <span class="detail-value">${a.reason}</span>\n </div>\n `)}return t}formatAgentEventDetails(s){const a=s.data||{};let n="";return(a.agent_type||a.name)&&(n+=`\n <div class="detail-row">\n <span class="detail-label">Agent Type:</span>\n <span class="detail-value">${a.agent_type||a.name}</span>\n </div>\n `),s.subtype&&(n+=`\n <div class="detail-row">\n <span class="detail-label">Action:</span>\n <span class="detail-value">${s.subtype}</span>\n </div>\n `),n}formatTodoEventDetails(s){const a=s.data||{};let n="";if(a.todos&&Array.isArray(a.todos)){const s=this.getTodoStatusCounts(a.todos);n+=`\n <div class="detail-row">\n <span class="detail-label">Todo Items:</span>\n <span class="detail-value">${a.todos.length} total</span>\n </div>\n <div class="detail-row">\n <span class="detail-label">Status:</span>\n <span class="detail-value">${s.completed} completed, ${s.in_progress} in progress</span>\n </div>\n `}return n}formatSessionEventDetails(s){const a=s.data||{};let n="";return a.session_id&&(n+=`\n <div class="detail-row">\n <span class="detail-label">Session ID:</span>\n <span class="detail-value">${a.session_id}</span>\n </div>\n `),s.subtype&&(n+=`\n <div class="detail-row">\n <span class="detail-label">Action:</span>\n <span class="detail-value">${s.subtype}</span>\n </div>\n `),n}formatGenericEventDetails(s){const a=s.data||{};let n="";const t=["message","description","value","result"];for(let e of t)if(void 0!==a[e]){let s=a[e];"string"==typeof s&&s.length>200&&(s=s.substring(0,200)+"..."),n+=`\n <div class="detail-row">\n <span class="detail-label">${e}:</span>\n <span class="detail-value">${this.escapeHtml(String(s))}</span>\n </div>\n `}return n}formatEventData(s){const a=s.data;return a&&0!==Object.keys(a).length?`\n <div class="detail-section">\n <span class="detail-section-title">Event Data:</span>\n <pre class="event-data-json">${this.escapeHtml(JSON.stringify(a,null,2))}</pre>\n </div>\n `:""}formatParameters(s,a="Parameters"){if(!s||0===Object.keys(s).length)return`\n <div class="detail-section">\n <span class="detail-section-title">${a}:</span>\n <div class="no-params">No parameters</div>\n </div>\n `;const n=Object.keys(s);return`\n <div class="detail-section">\n <span class="detail-section-title">${a} (${n.length}):</span>\n <div class="params-list">\n ${n.map(a=>{const n=s[a];return`\n <div class="param-item">\n <div class="param-key">${a}:</div>\n <div class="param-value">${this.formatParameterValue(n)}</div>\n </div>\n `}).join("")}\n </div>\n </div>\n `}formatParameterValue(s){if("string"==typeof s)return s.length>500?`<pre class="param-text-long">${this.escapeHtml(s.substring(0,500)+"...\n\n[Content truncated - "+s.length+" total characters]")}</pre>`:s.length>100?`<pre class="param-text">${this.escapeHtml(s)}</pre>`:`<span class="param-text-short">${this.escapeHtml(s)}</span>`;if("object"!=typeof s||null===s)return`<span class="param-primitive">${this.escapeHtml(String(s))}</span>`;if(Array.isArray(s)&&s.length>0&&s[0].hasOwnProperty("content")&&s[0].hasOwnProperty("status"))return this.formatTodosAsParameter(s);try{return`<pre class="param-json">${this.escapeHtml(JSON.stringify(s,null,2))}</pre>`}catch(a){return'<span class="param-error">Error displaying object</span>'}}formatTodosAsParameter(s){const a=this.getTodoStatusCounts(s);let n=`\n <div class="param-todos">\n <div class="param-todos-header">\n Array of todo objects (${s.length} items)\n </div>\n <div class="param-todos-summary">\n ${a.completed} completed • ${a.in_progress} in progress • ${a.pending} pending\n </div>\n <div class="param-todos-list">\n `;return s.forEach((s,a)=>{const t=this.getCheckboxIcon(s.status),e="in_progress"===s.status&&s.activeForm||s.content,i=this.formatStatusClass(s.status);n+=`\n <div class="param-todo-item ${s.status}">\n <div class="param-todo-checkbox">\n <span class="param-checkbox-icon ${i}">${t}</span>\n </div>\n <div class="param-todo-text">\n <span class="param-todo-content">${this.escapeHtml(e)}</span>\n <span class="param-todo-status-badge ${i}">${s.status.replace("_"," ")}</span>\n </div>\n </div>\n `}),n+="\n </div>\n </div>\n ",n}formatTimestamp(s){if(!s)return"Unknown time";try{const a=new Date(s);return isNaN(a.getTime())?"Invalid date":a.toLocaleString()}catch(a){return"Invalid date"}}formatStatus(s){if(!s)return"unknown";return{active:"🟢 Active",completed:"✅ Completed",in_progress:"🔄 In Progress",pending:"⏳ Pending",error:"❌ Error",failed:"❌ Failed"}[s]||s}formatStatusClass(s){return`status-${s}`}getAgentIcon(s){return{PM:"🎯",Engineer:"🔧","Engineer Agent":"🔧",Research:"🔍","Research Agent":"🔍",QA:"✅","QA Agent":"✅",Architect:"🏗️","Architect Agent":"🏗️",Ops:"⚙️","Ops Agent":"⚙️"}[s]||"🤖"}getToolIcon(s){return{Read:"👁️",Write:"✍️",Edit:"✏️",MultiEdit:"📝",Bash:"💻",Grep:"🔍",Glob:"📂",LS:"📁",TodoWrite:"📝",Task:"📋",WebFetch:"🌐"}[s]||"🔧"}getCheckboxIcon(s){return{pending:"⏳",in_progress:"🔄",completed:"✅"}[s]||"❓"}getTodoStatusCounts(s){const a={completed:0,in_progress:0,pending:0};return s.forEach(s=>{a.hasOwnProperty(s.status)&&a[s.status]++}),a}escapeHtml(s){if("string"!=typeof s)return"";const a=document.createElement("div");return a.textContent=s,a.innerHTML}createCollapsibleJSON(s,a="Full Details"){const n=`json-details-${Date.now()}-${Math.random().toString(36).substr(2,9)}`,t=this.cleanDataForDisplay(s);return`\n <div class="collapsible-json-section">\n <button class="collapsible-json-toggle" onclick="\n const content = document.getElementById('${n}');\n const button = this;\n if (content.style.display === 'none' || content.style.display === '') {\n content.style.display = 'block';\n button.classList.add('expanded');\n button.innerHTML = '▼ ${a}';\n } else {\n content.style.display = 'none';\n button.classList.remove('expanded');\n button.innerHTML = '▶ ${a}';\n }\n ">▶ ${a}</button>\n <div id="${n}" class="collapsible-json-content" style="display: none;">\n <pre class="json-viewer">${this.escapeHtml(JSON.stringify(t,null,2))}</pre>\n </div>\n </div>\n `}cleanDataForDisplay(s){const a=new WeakSet;return JSON.parse(JSON.stringify(s,(s,n)=>{if("object"==typeof n&&null!==n){if(a.has(n))return"[Circular Reference]";a.add(n)}return"string"==typeof n&&n.length>1e3?n.substring(0,1e3)+"... [truncated]":"function"==typeof n?"[Function]":n}))}clear(){this.container&&(this.container.innerHTML=""),this.currentData=null,this.currentType=null}getCurrentData(){return this.currentData}getCurrentType(){return this.currentType}hasData(){return null!==this.currentData}}window.UnifiedDataViewer=s;export{s as U};
|
|
2
|
+
//# sourceMappingURL=unified-data-viewer.B7wmm2bD.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{U as e}from"../chunks/unified-data-viewer.CvAmgM3l.js";class t{constructor(){this.container=null,this.events=[],this.processedEventIds=new Set,this.sessions=new Map,this.currentSession=null,this.selectedSessionFilter="all",this.timeRange="30min",this.searchTerm="",this.initialized=!1,this.expandedSessions=new Set,this.expandedAgents=new Set,this.expandedTools=new Set,this.selectedItem=null,this.renderTreeDebounced=this.debounce(()=>this.renderTree(),100)}debounce(e,t){let s;return function(...n){clearTimeout(s),s=setTimeout(()=>{clearTimeout(s),e(...n)},t)}}initialize(){if(console.log("ActivityTree.initialize() called, initialized:",this.initialized),this.initialized)return void console.log("Activity tree already initialized, skipping");if(this.container=document.getElementById("activity-tree-container"),!this.container&&(this.container=document.getElementById("activity-tree"),!this.container))return void console.error("Activity tree container not found in DOM");const e=document.getElementById("activity-tab");if(e){if(!e.classList.contains("active"))return console.log("Activity tab not active, initializing but deferring render"),this.setupControls(),this.subscribeToEvents(),void(this.initialized=!0);this.setupControls(),this.createLinearTreeView(),this.subscribeToEvents(),this.initialized=!0,console.log("Activity tree initialization complete")}else console.error("Activity tab panel (#activity-tab) not found in DOM")}forceShow(){console.log("ActivityTree.forceShow() called"),this.container||(this.container=document.getElementById("activity-tree-container")||document.getElementById("activity-tree"),this.container)?(this.createLinearTreeView(),this.renderTree()):console.error("Cannot find activity tree container")}renderWhenVisible(){if(console.log("ActivityTree.renderWhenVisible() called"),!this.initialized)return console.log("Not initialized yet, calling initialize..."),void this.initialize();this.createLinearTreeView(),this.renderTree()}setupControls(){const e=document.getElementById("time-range");e&&e.addEventListener("change",e=>{this.timeRange=e.target.value,console.log(`ActivityTree: Time range changed to: ${this.timeRange}`),this.renderTree()}),document.addEventListener("sessionFilterChanged",e=>{this.selectedSessionFilter=e.detail.sessionId||"all",console.log(`ActivityTree: Session filter changed to: ${this.selectedSessionFilter} (from SessionManager)`),this.renderTree()}),document.addEventListener("sessionChanged",e=>{this.selectedSessionFilter=e.detail.sessionId||"all",console.log(`ActivityTree: Session changed to: ${this.selectedSessionFilter} (from SessionManager - backward compat)`),this.renderTree()}),setTimeout(()=>{if(window.sessionManager){const e=window.sessionManager.getCurrentFilter();e!==this.selectedSessionFilter&&(this.selectedSessionFilter=e||"all",console.log(`ActivityTree: Initialized with current session filter: ${this.selectedSessionFilter}`),this.renderTree())}},100);const t=document.getElementById("expand-all");t&&t.addEventListener("click",()=>this.expandAllSessions());const s=document.getElementById("collapse-all");s&&s.addEventListener("click",()=>this.collapseAllSessions());const n=document.getElementById("reset-zoom");n&&(n.style.display="inline-block",n.addEventListener("click",()=>this.resetZoom()));const o=document.getElementById("activity-search");o&&o.addEventListener("input",e=>{this.searchTerm=e.target.value.toLowerCase(),this.renderTree()})}createLinearTreeView(){console.log("Creating linear tree view"),this.container.innerHTML="";const e=document.createElement("div");e.id="linear-tree",e.className="linear-tree",this.container.appendChild(e),console.log("Linear tree view created")}subscribeToEvents(){if(!window.socketClient)return console.warn("Socket client not available for activity tree"),void setTimeout(()=>this.subscribeToEvents(),1e3);console.log("ActivityTree: Setting up event subscription"),window.socketClient.onEventUpdate((e,t)=>{console.log(`ActivityTree: onEventUpdate called with ${e.length} total events and ${t.size} sessions`);for(const[n,o]of t.entries())if(this.sessions.has(n)){const e=this.sessions.get(n);e.timestamp=new Date(o.lastActivity||o.startTime||e.timestamp),e.eventCount=o.eventCount,e.status=o.status||e.status,e.working_directory=o.working_directory||e.working_directory,e.git_branch=o.git_branch||e.git_branch}else{const e={id:n,timestamp:new Date(o.lastActivity||o.startTime||new Date),expanded:this.expandedSessions.has(n)||!0,agents:new Map,todos:[],userInstructions:[],tools:[],toolsMap:new Map,status:"active",currentTodoTool:null,working_directory:o.working_directory,git_branch:o.git_branch,eventCount:o.eventCount};this.sessions.set(n,e)}const s=e.filter(e=>{const t=e.id||`${e.type}-${e.timestamp}-${Math.random()}`;return!this.processedEventIds.has(t)});s.length>0&&(console.log(`ActivityTree: Processing ${s.length} new events`,s),s.forEach(e=>{const t=e.id||`${e.type}-${e.timestamp}-${Math.random()}`;this.processedEventIds.add(t),this.processEvent(e)})),this.events=[...e],this.renderTreeDebounced(),console.log("ActivityTree: Sessions after sync with socket client:",Array.from(this.sessions.entries()))});const e=window.socketClient?.getState();if(e&&e.events.length>0){console.log(`ActivityTree: Loading existing data - ${e.events.length} events, ${e.sessions.size} sessions`);for(const[s,n]of e.sessions.entries())if(!this.sessions.has(s)){const e={id:s,timestamp:new Date(n.lastActivity||n.startTime||new Date),expanded:this.expandedSessions.has(s)||!0,agents:new Map,todos:[],userInstructions:[],tools:[],toolsMap:new Map,status:"active",currentTodoTool:null,working_directory:n.working_directory,git_branch:n.git_branch,eventCount:n.eventCount};this.sessions.set(s,e)}const t=e.events.filter(e=>{const t=e.id||`${e.type}-${e.timestamp}-${Math.random()}`;return!this.processedEventIds.has(t)});t.length>0&&(console.log(`ActivityTree: Processing ${t.length} unprocessed events from initial load`),t.forEach(e=>{const t=e.id||`${e.type}-${e.timestamp}-${Math.random()}`;this.processedEventIds.add(t),this.processEvent(e)})),this.events=[...e.events],this.renderTree(),console.log("ActivityTree: Initial sessions state:",Array.from(this.sessions.entries()))}else console.log("ActivityTree: No existing events found"),this.events=[],this.sessions.clear(),this.renderTree()}processEvent(e){if(!e)return void console.log("ActivityTree: Ignoring null event");let t,s=this.getEventType(e);if(!s)return;console.log(`ActivityTree: Processing event: ${s}`,e),e.timestamp?(t=new Date(e.timestamp),isNaN(t.getTime())&&(console.warn("ActivityTree: Invalid timestamp, using current time:",e.timestamp),t=new Date)):(console.warn("ActivityTree: No timestamp found, using current time"),t=new Date);const n=e.session_id||e.data?.session_id;if(!n)return void console.log(`ActivityTree: Skipping event without session_id: ${s}`);if(!this.sessions.has(n))return void console.warn(`ActivityTree: Session ${n} not found in authoritative sessions - skipping event`);const o=this.sessions.get(n);switch(s){case"Start":this.currentSession=o;break;case"user_prompt":this.processUserInstruction(e,o);break;case"TodoWrite":break;case"SubagentStart":this.processSubagentStart(e,o);break;case"SubagentStop":this.processSubagentStop(e,o);break;case"PreToolUse":this.processToolUse(e,o);break;case"PostToolUse":this.updateToolStatus(e,o,"completed")}this.updateStats()}getEventType(e){if(e.hook_event_name)return e.hook_event_name;if("hook"===e.type&&e.subtype){return{pre_tool:"PreToolUse",post_tool:"PostToolUse",subagent_start:"SubagentStart",subagent_stop:"SubagentStop",todo_write:"TodoWrite"}[e.subtype]}if("todo"===e.type&&"updated"===e.subtype)return"TodoWrite";if("subagent"===e.type){if("started"===e.subtype)return"SubagentStart";if("stopped"===e.subtype)return"SubagentStop"}return"start"===e.type?"Start":"user_prompt"===e.type||"user_prompt"===e.subtype?"user_prompt":null}processUserInstruction(e,t){const s=e.prompt_text||e.data?.prompt_text||e.prompt||"";if(!s)return;const n={id:`instruction-${t.id}-${Date.now()}`,text:s,preview:s.length>100?s.substring(0,100)+"...":s,timestamp:e.timestamp||(new Date).toISOString(),type:"user_instruction"};if(t.agents.size>0){console.log("ActivityTree: New user prompt detected, collapsing previous agents");for(let e of t.agents.values())"active"===e.status&&(e.status="completed"),this.expandedAgents.delete(e.id)}t.currentActiveAgent=null,t.userInstructions.push(n),t.userInstructions.length>5&&(t.userInstructions=t.userInstructions.slice(-5))}processTodoWrite(e,t){let s=e.todos||e.data?.todos||e.data||[];if(s&&"object"==typeof s&&s.todos&&(s=s.todos),!Array.isArray(s)||0===s.length)return;t.currentTodos=s.map(t=>({content:t.content,activeForm:t.activeForm,status:t.status,timestamp:e.timestamp}));let n=t.currentActiveAgent;if(!n){const e=this.getAllAgents(t).filter(e=>"active"===e.status||"in_progress"===e.status).sort((e,t)=>new Date(t.timestamp)-new Date(e.timestamp));if(e.length>0)n=e[0];else{const e=this.getAllAgents(t),s=e.find(e=>e.isPM);s?n=s:e.length>0&&(n=e[0])}}if(n){n.todoWritesMap||(n.todoWritesMap=new Map),n.todoWrites||(n.todoWrites=[]);const t=n.todoWritesMap.get("TodoWrite");if(t)t.todos=s,t.timestamp=e.timestamp,t.updateCount=(t.updateCount||1)+1;else{const t={id:`todowrite-${n.id}-${Date.now()}`,name:"TodoWrite",type:"todowrite",icon:"📝",timestamp:e.timestamp,status:"completed",todos:s,params:{todos:s},updateCount:1};n.todoWritesMap.set("TodoWrite",t),n.todoWrites=[t]}n.currentTodos=s}else{t.todoWrites||(t.todoWrites=[]),t.todoWritesMap||(t.todoWritesMap=new Map);const n=t.todoWritesMap.get("TodoWrite");if(n)n.todos=s,n.timestamp=e.timestamp,n.updateCount=(n.updateCount||1)+1;else{const n={id:`todowrite-session-${Date.now()}`,name:"TodoWrite",type:"todowrite",icon:"📝",timestamp:e.timestamp,status:"completed",todos:s,updateCount:1};t.todoWritesMap.set("TodoWrite",n),t.todoWrites=[n]}}}processSubagentStart(e,t){const s=e.agent_name||e.data?.agent_name||e.data?.agent_type||e.agent_type||e.agent||"unknown",n=e.session_id||e.data?.session_id,o=e.parent_agent||e.data?.parent_agent,i=`${s}-${n||"no-session"}`;let a=null;let r;if(a=this.getAllAgents(t).find(e=>e.name===s&&e.sessionId===n&&"active"===e.status),a)r=a,r.timestamp=e.timestamp,r.instanceCount=(r.instanceCount||1)+1,this.expandedAgents.add(r.id);else{if(r={id:`agent-${i}-${Date.now()}`,name:s,type:"agent",icon:this.getAgentIcon(s),timestamp:e.timestamp,status:"active",tools:[],subagents:new Map,sessionId:n,parentAgent:o,isPM:"pm"===s.toLowerCase()||s.toLowerCase().includes("project manager"),instanceCount:1,toolsMap:new Map},o){let e=null;for(let[s,n]of t.agents.entries())if(n.sessionId===o||n.name===o){e=n;break}e?(e.subagents||(e.subagents=new Map),e.subagents.set(r.id,r)):t.agents.set(r.id,r)}else t.agents.set(r.id,r);this.expandedAgents.add(r.id)}t.currentActiveAgent=r}processSubagentStop(e,t){const s=e.session_id||e.data?.session_id;if(s&&t.agents.has(s)){t.agents.get(s).status="completed"}}processToolUse(e,t){const s=e.tool_name||e.data?.tool_name||e.tool||e.data?.tool||"unknown",n=e.tool_parameters||e.data?.tool_parameters||e.parameters||e.data?.parameters||{},o=e.session_id||e.data?.session_id;let i=t.currentActiveAgent;if(!i){const e=this.getAllAgents(t);i=e.find(e=>e.sessionId===o)||e.find(e=>"active"===e.status)||e[0]}if(i){i.toolsMap||(i.toolsMap=new Map),i.tools||(i.tools=[]);const t=this.getToolKey(s,n);let o=i.toolsMap.get(t);if(o)o.params=n,o.timestamp=e.timestamp,o.status="in_progress",o.eventId=e.id,o.callCount=(o.callCount||1)+1,i.currentTool=o;else{const o={id:`tool-${i.id}-${s}-${Date.now()}`,name:s,type:"tool",icon:this.getToolIcon(s),timestamp:e.timestamp,status:"in_progress",params:n,eventId:e.id,callCount:1,createdAt:e.timestamp};"Task"===s&&n.subagent_type&&(o.isSubagentTask=!0,o.subagentType=n.subagent_type),i.toolsMap.set(t,o),"TodoWrite"===s?i.tools.unshift(o):i.tools.push(o),i.currentTool=o}}else{t.tools||(t.tools=[]),t.toolsMap||(t.toolsMap=new Map);const o=this.getToolKey(s,n);let i=t.toolsMap.get(o);if(i)i.params=n,i.timestamp=e.timestamp,i.status="in_progress",i.eventId=e.id,i.callCount=(i.callCount||1)+1,t.currentTool=i;else{const i={id:`tool-session-${s}-${Date.now()}`,name:s,type:"tool",icon:this.getToolIcon(s),timestamp:e.timestamp,status:"in_progress",params:n,eventId:e.id,callCount:1,createdAt:e.timestamp};t.toolsMap.set(o,i),"TodoWrite"===s?t.tools.unshift(i):t.tools.push(i),t.currentTool=i}}}getToolKey(e,t){if("TodoWrite"===e)return"TodoWrite";let s=e;return"Edit"!==e&&"Write"!==e&&"Read"!==e||t.file_path&&(s+=`-${t.file_path}`),"Grep"!==e&&"Glob"!==e||!t.pattern||(s+=`-${t.pattern.substring(0,20)}`),s}updateToolStatus(e,t,s){const n=e.tool_name||e.data?.tool_name||e.tool||"unknown",o=e.tool_parameters||e.data?.tool_parameters||e.parameters||e.data?.parameters||{},i=e.session_id||e.data?.session_id,a=this.getToolKey(n,o);let r=t.currentActiveAgent;if(!r){const e=this.getAllAgents(t);r=e.find(e=>e.sessionId===i)||e.find(e=>"active"===e.status)}if(r&&r.toolsMap){const t=r.toolsMap.get(a);if(t)return t.status=s,t.completedAt=e.timestamp,(e.data?.result||e.result)&&(t.result=e.data?.result||e.result),void(e.data?.duration_ms&&(t.duration=e.data.duration_ms))}if(t.toolsMap){const n=t.toolsMap.get(a);if(n)return n.status=s,n.completedAt=e.timestamp,(e.data?.result||e.result)&&(n.result=e.data?.result||e.result),void(e.data?.duration_ms&&(n.duration=e.data.duration_ms))}console.log(`ActivityTree: Could not find tool to update status for ${n} with key ${a} (event ${e.id})`)}renderTree(){const e=document.getElementById("linear-tree");if(!e)return;e.innerHTML="";const t=Array.from(this.sessions.values()).sort((e,t)=>new Date(t.timestamp)-new Date(e.timestamp));for(let s of t){if("all"!==this.selectedSessionFilter&&this.selectedSessionFilter!==s.id)continue;const t=this.createSessionElement(s);e.appendChild(t)}}createSessionElement(e){const t=this.expandedSessions.has(e.id)||e.expanded;let s;try{const t=e.timestamp instanceof Date?e.timestamp:new Date(e.timestamp);isNaN(t.getTime())?(s="Invalid Date",console.warn("ActivityTree: Invalid session timestamp:",e.timestamp)):s=t.toLocaleString()}catch(l){s="Invalid Date",console.error("ActivityTree: Error formatting session timestamp:",l,e.timestamp)}const n=document.createElement("div");n.className="tree-node session",n.dataset.sessionId=e.id;const o=t?"▼":"▶",i=this.getAllAgents(e).length,a=e.currentTodos?e.currentTodos.length:0,r=e.userInstructions?e.userInstructions.length:0;return console.log(`ActivityTree: Rendering session ${e.id}: ${i} agents, ${r} instructions, ${a} todos at ${s}`),n.innerHTML=`\n <div class="tree-node-content" onclick="window.activityTreeInstance.toggleSession('${e.id}')">\n <span class="tree-expand-icon">${o}</span>\n <span class="tree-icon">🎯</span>\n <span class="tree-label">PM Session</span>\n <span class="tree-meta">${s} • ${i} agent(s) • ${r} instruction(s) • ${a} todo(s)</span>\n </div>\n <div class="tree-children" style="display: ${t?"block":"none"}">\n ${this.renderSessionContent(e)}\n </div>\n `,n}renderSessionContent(e){let t="";if(e.userInstructions&&e.userInstructions.length>0)for(let n of e.userInstructions.slice(-3))t+=this.renderUserInstructionElement(n,1);if(e.tools&&e.tools.length>0)for(let n of e.tools)t+=this.renderToolElement(n,1);const s=Array.from(e.agents.values()).sort((e,t)=>new Date(t.timestamp)-new Date(e.timestamp));for(let n of s)t+=this.renderAgentElement(n,1);return t}renderUserInstructionElement(e,t){return`\n <div class="tree-node user-instruction ${this.selectedItem&&"instruction"===this.selectedItem.type&&this.selectedItem.data.id===e.id?"selected":""}" data-level="${t}">\n <div class="tree-node-content">\n <span class="tree-expand-icon"></span>\n <span class="tree-icon">💬</span>\n <span class="tree-label clickable" onclick="window.activityTreeInstance.selectItem(${this.escapeJson(e)}, 'instruction', event)">User: "${this.escapeHtml(e.preview)}"</span>\n <span class="tree-status status-active">instruction</span>\n </div>\n </div>\n `}renderTodoChecklistElement(e,t){const s=`checklist-${Date.now()}`,n=!1!==this.expandedTools.has(s),o=n?"▼":"▶";let i=0,a=0;e.forEach(e=>{"completed"===e.status?i++:"in_progress"===e.status&&a++});let r="";r=a>0?`${a} in progress, ${i} completed`:i===e.length&&e.length>0?`All ${e.length} completed`:`${e.length} todo(s)`;let l=`\n <div class="tree-node todo-checklist" data-level="${t}">\n <div class="tree-node-content">\n <span class="tree-expand-icon" onclick="window.activityTreeInstance.toggleTodoChecklist('${s}'); event.stopPropagation();">${o}</span>\n <span class="tree-icon">☑️</span>\n <span class="tree-label">TODOs</span>\n <span class="tree-params">${r}</span>\n <span class="tree-status status-active">checklist</span>\n </div>\n `;if(n){l+='<div class="tree-children">';for(let s of e){const e=this.getCheckboxIcon(s.status),n=`status-${s.status}`,o="in_progress"===s.status?s.activeForm:s.content;l+=`\n <div class="tree-node todo-item ${n}" data-level="${t+1}">\n <div class="tree-node-content">\n <span class="tree-expand-icon"></span>\n <span class="tree-icon">${e}</span>\n <span class="tree-label">${this.escapeHtml(o)}</span>\n <span class="tree-status ${n}">${s.status.replace("_"," ")}</span>\n </div>\n </div>\n `}l+="</div>"}return l+="</div>",l}renderAgentElement(e,t){const s="active"===e.status?"status-active":"status-completed",n=this.expandedAgents.has(e.id),o=e.tools&&e.tools.length>0,i=e.subagents&&e.subagents.size>0,a=o||i,r=a?n?"▼":"▶":"",l=this.selectedItem&&"agent"===this.selectedItem.type&&this.selectedItem.data.id===e.id?"selected":"",c=e.instanceCount>1?` (${e.instanceCount}x)`:"";let d="";if(!n&&a){const t=[];if(e.currentTodos&&e.currentTodos.length>0){const s=e.currentTodos.find(e=>"in_progress"===e.status);s&&t.push(`📝 ${s.activeForm||s.content}`)}e.currentTool&&t.push(`${e.currentTool.icon} ${e.currentTool.name}`),t.length>0&&(d=` • ${t.join(" • ")}`)}let p=`\n <div class="tree-node agent ${s} ${l}" data-level="${t}">\n <div class="tree-node-content">\n ${r?`<span class="tree-expand-icon" onclick="window.activityTreeInstance.toggleAgent('${e.id}'); event.stopPropagation();">${r}</span>`:'<span class="tree-expand-icon"></span>'}\n <span class="tree-icon">${e.icon}</span>\n <span class="tree-label clickable" onclick="window.activityTreeInstance.selectItem(${this.escapeJson(e)}, 'agent', event)">${e.name}${c}${d}</span>\n <span class="tree-status ${s}">${e.status}</span>\n </div>\n `;if(a&&n){if(p+='<div class="tree-children">',o)for(let s of e.tools)p+=this.renderToolElement(s,t+1);if(i){const s=Array.from(e.subagents.values());for(let e of s)p+=this.renderAgentElement(e,t+1)}p+="</div>"}return p+="</div>",p}renderToolElement(e,t){const s=`status-${e.status}`,n=this.getToolParams(e),o=this.selectedItem&&"tool"===this.selectedItem.type&&this.selectedItem.data.id===e.id?"selected":"",i=this.getToolStatusIcon(e.status),a=this.getToolStatusLabel(e.status),r=e.callCount>1?` (${e.callCount} calls)`:"";return`\n <div class="tree-node tool ${s} ${o}" data-level="${t}">\n <div class="tree-node-content">\n <span class="tree-expand-icon"></span>\n <span class="tree-icon">${e.icon}</span>\n <span class="tree-status-icon">${i}</span>\n <span class="tree-label clickable" onclick="window.activityTreeInstance.selectItem(${this.escapeJson(e)}, 'tool', event)">${e.name}${r}</span>\n <span class="tree-params">${n}</span>\n <span class="tree-status ${s}">${a}</span>\n </div>\n </div>\n `}getToolParams(e){if(!e.params)return"";if("Read"===e.name&&e.params.file_path)return e.params.file_path;if("Edit"===e.name&&e.params.file_path)return e.params.file_path;if("Write"===e.name&&e.params.file_path)return e.params.file_path;if("Bash"===e.name&&e.params.command){const t=e.params.command;return t.length>50?t.substring(0,50)+"...":t}return"WebFetch"===e.name&&e.params.url?e.params.url:""}getStatusIcon(e){return{pending:"⏸️",in_progress:"🔄",completed:"✅"}[e]||"❓"}getCheckboxIcon(e){return{pending:"⏳",in_progress:"🔄",completed:"✅"}[e]||"❓"}getAgentIcon(e){return{engineer:"👷",research:"🔬",qa:"🧪",ops:"⚙️",pm:"📊",architect:"🏗️","project manager":"📊"}[e.toLowerCase()]||"🤖"}getAllAgents(e){const t=[],s=e=>{if(e)for(let n of e.values())t.push(n),n.subagents&&n.subagents.size>0&&s(n.subagents)};return s(e.agents),t}renderTodoWriteElement(e,t){const s=e.id,n=this.expandedTools.has(s),o=n?"▼":"▶",i=e.todos||[];let a=0,r=0;i.forEach(e=>{"completed"===e.status?a++:"in_progress"===e.status&&r++});const l=i.find(e=>"in_progress"===e.status),c=l?` • 🔄 ${l.activeForm||l.content}`:"";let d="";d=r>0?`${r} in progress, ${a}/${i.length} done`:a===i.length&&i.length>0?`All ${i.length} completed ✅`:`${a}/${i.length} done`;let p=`\n <div class="tree-node todowrite ${l?"has-active":""}" data-level="${t}">\n <div class="tree-node-content">\n <span class="tree-expand-icon" onclick="window.activityTreeInstance.toggleTodoWrite('${s}'); event.stopPropagation();">${o}</span>\n <span class="tree-icon">📝</span>\n <span class="tree-label">TodoWrite${e.updateCount>1?` (${e.updateCount} updates)`:""}${n?"":c}</span>\n <span class="tree-params">${d}</span>\n <span class="tree-status status-active">todos</span>\n </div>\n `;if(n&&i.length>0){p+='<div class="tree-children">';for(let e of i){const s=this.getCheckboxIcon(e.status),n=`status-${e.status}`,o="in_progress"===e.status?e.activeForm:e.content;p+=`\n <div class="tree-node todo-item ${n} ${e===l?"current-active":""}" data-level="${t+1}">\n <div class="tree-node-content">\n <span class="tree-expand-icon"></span>\n <span class="tree-icon">${s}</span>\n <span class="tree-label">${this.escapeHtml(o)}</span>\n <span class="tree-status ${n}">${e.status.replace("_"," ")}</span>\n </div>\n </div>\n `}p+="</div>"}return p+="</div>",p}toggleTodoWrite(e){this.expandedTools.has(e)?this.expandedTools.delete(e):this.expandedTools.add(e),this.renderTree()}getToolIcon(e){return{read:"👁️",write:"✍️",edit:"✏️",bash:"💻",webfetch:"🌐",grep:"🔍",glob:"📂",todowrite:"📝"}[e.toLowerCase()]||"🔧"}getToolStatusIcon(e){return{in_progress:"⏳",completed:"✅",failed:"❌",error:"❌",pending:"⏸️",active:"🔄"}[e]||"❓"}getToolStatusLabel(e){return{in_progress:"in progress",completed:"completed",failed:"failed",error:"error",pending:"pending",active:"active"}[e]||e}toggleSession(e){this.expandedSessions.has(e)?this.expandedSessions.delete(e):this.expandedSessions.add(e);const t=this.sessions.get(e);t&&(t.expanded=this.expandedSessions.has(e)),this.renderTree()}expandAllSessions(){for(let e of this.sessions.keys()){this.expandedSessions.add(e);const t=this.sessions.get(e);t&&(t.expanded=!0)}this.renderTree()}collapseAllSessions(){this.expandedSessions.clear();for(let e of this.sessions.values())e.expanded=!1;this.renderTree()}updateStats(){const e=this.countTotalNodes(),t=this.countActiveNodes(),s=this.calculateMaxDepth(),n=document.getElementById("node-count"),o=document.getElementById("active-count"),i=document.getElementById("tree-depth");n&&(n.textContent=e),o&&(o.textContent=t),i&&(i.textContent=s),console.log(`ActivityTree: Stats updated - Nodes: ${e}, Active: ${t}, Depth: ${s}`)}countTotalNodes(){let e=0;for(let t of this.sessions.values()){e+=1,e+=t.agents.size,t.userInstructions&&(e+=t.userInstructions.length),t.todos&&(e+=t.todos.length),t.tools&&(e+=t.tools.length);for(let s of t.agents.values())s.tools&&(e+=s.tools.length)}return e}countActiveNodes(){let e=0;for(let t of this.sessions.values()){if("active"===t.status&&e++,t.todos)for(let s of t.todos)"in_progress"===s.status&&e++;if(t.tools)for(let s of t.tools)"in_progress"===s.status&&e++;for(let s of t.agents.values())if("active"===s.status&&e++,s.tools)for(let t of s.tools)"in_progress"===t.status&&e++}return e}calculateMaxDepth(){let e=0;for(let t of this.sessions.values()){let s=1;t.userInstructions&&t.userInstructions.length>0&&(s=Math.max(s,2)),t.todos&&t.todos.length>0&&(s=Math.max(s,3)),t.tools&&t.tools.length>0&&(s=Math.max(s,2));for(let e of t.agents.values())e.tools&&e.tools.length>0&&(s=Math.max(s,3));e=Math.max(e,s)}return e}toggleAgent(e){this.expandedAgents.has(e)?this.expandedAgents.delete(e):this.expandedAgents.add(e),this.renderTree()}toggleTool(e){console.log("Tool expansion is disabled. Tools now show data in the left pane when clicked.")}toggleTodoChecklist(e){this.expandedTools.has(e)?this.expandedTools.delete(e):this.expandedTools.add(e),this.renderTree()}renderPinnedTodosElement(e,t){const s=`pinned-todos-${Date.now()}`,n=!1!==this.expandedTools.has(s),o=n?"▼":"▶",i=e.todos||[];let a=0,r=0;i.forEach(e=>{"completed"===e.status?a++:"in_progress"===e.status&&r++});let l="";l=r>0?`${r} in progress, ${a} completed`:a===i.length&&i.length>0?`All ${i.length} completed`:`${i.length} todo(s)`;let c=`\n <div class="tree-node pinned-todos" data-level="${t}">\n <div class="tree-node-content">\n <span class="tree-expand-icon" onclick="window.activityTreeInstance.toggleTodoChecklist('${s}'); event.stopPropagation();">${o}</span>\n <span class="tree-icon">📌</span>\n <span class="tree-label">Pinned TODOs</span>\n <span class="tree-params">${l}</span>\n <span class="tree-status status-active">pinned</span>\n </div>\n `;if(n){c+='<div class="tree-children">';for(let e of i){const s=this.getCheckboxIcon(e.status),n=`status-${e.status}`,o="in_progress"===e.status?e.activeForm:e.content;c+=`\n <div class="tree-node todo-item ${n}" data-level="${t+1}">\n <div class="tree-node-content">\n <span class="tree-expand-icon"></span>\n <span class="tree-icon">${s}</span>\n <span class="tree-label">${this.escapeHtml(o)}</span>\n <span class="tree-status ${n}">${e.status.replace("_"," ")}</span>\n </div>\n </div>\n `}c+="</div>"}return c+="</div>",c}selectItem(e,t,s){s&&s.stopPropagation(),this.selectedItem={data:e,type:t},this.displayItemData(e,t),this.renderTree()}displayItemData(t,s){this.unifiedViewer||(this.unifiedViewer=new e("module-data-content")),this.unifiedViewer.display(t,s);const n=document.querySelector(".module-data-header h5");if(n){const e={agent:"🤖",tool:"🔧",instruction:"💬",session:"🎯",todo:"📝"}[s]||"📊",o=t.name||t.agentName||t.tool_name||"Item";n.textContent=`${e} ${s}: ${o}`}}escapeHtml(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}resetZoom(){this.svg&&this.zoom&&this.svg.transition().duration(this.duration).call(this.zoom.transform,d3.zoomIdentity)}escapeJson(e){return JSON.stringify(e).replace(/'/g,"'").replace(/"/g,""")}}window.ActivityTree=t;const s=()=>{let e=null;const s=()=>{e||(console.log("Creating new Activity Tree instance..."),e=new t,window.activityTreeInstance=e,window.activityTree=()=>e),setTimeout(()=>{console.log("Attempting to initialize Activity Tree visualization..."),e.initialize()},100)};document.querySelectorAll(".tab-button").forEach(t=>{t.addEventListener("click",t=>{"activity"===t.target.getAttribute("data-tab")&&(console.log("Activity tab button clicked, initializing tree..."),s(),e&&setTimeout(()=>{e.renderWhenVisible(),e.forceShow()},150))})}),document.addEventListener("tabChanged",t=>{t.detail&&"activity"===t.detail.newTab&&(console.log("Tab changed to activity, initializing tree..."),s(),e&&setTimeout(()=>{e.renderWhenVisible(),e.forceShow()},150))});const n=document.querySelector(".tab-button.active");n&&"activity"===n.getAttribute("data-tab")&&(console.log("Activity tab is active on load, initializing tree..."),s());const o=document.getElementById("activity-tab");o&&o.classList.contains("active")&&(console.log("Activity panel is active on load, initializing tree..."),e||s())};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",s):s();
|
|
1
|
+
import{U as e}from"../chunks/unified-data-viewer.B7wmm2bD.js";class t{constructor(){this.container=null,this.events=[],this.processedEventIds=new Set,this.sessions=new Map,this.currentSession=null,this.selectedSessionFilter="all",this.timeRange="30min",this.searchTerm="",this.initialized=!1,this.expandedSessions=new Set,this.expandedAgents=new Set,this.expandedTools=new Set,this.selectedItem=null,this.renderTreeDebounced=this.debounce(()=>this.renderTree(),100)}debounce(e,t){let s;return function(...n){clearTimeout(s),s=setTimeout(()=>{clearTimeout(s),e(...n)},t)}}initialize(){if(console.log("ActivityTree.initialize() called, initialized:",this.initialized),this.initialized)return void console.log("Activity tree already initialized, skipping");if(this.container=document.getElementById("activity-tree-container"),!this.container&&(this.container=document.getElementById("activity-tree"),!this.container))return void console.error("Activity tree container not found in DOM");const e=document.getElementById("activity-tab");if(e){if(!e.classList.contains("active"))return console.log("Activity tab not active, initializing but deferring render"),this.setupControls(),this.subscribeToEvents(),void(this.initialized=!0);this.setupControls(),this.createLinearTreeView(),this.subscribeToEvents(),this.initialized=!0,console.log("Activity tree initialization complete")}else console.error("Activity tab panel (#activity-tab) not found in DOM")}forceShow(){console.log("ActivityTree.forceShow() called"),this.container||(this.container=document.getElementById("activity-tree-container")||document.getElementById("activity-tree"),this.container)?(this.createLinearTreeView(),this.renderTree()):console.error("Cannot find activity tree container")}renderWhenVisible(){if(console.log("ActivityTree.renderWhenVisible() called"),!this.initialized)return console.log("Not initialized yet, calling initialize..."),void this.initialize();this.createLinearTreeView(),this.renderTree()}setupControls(){const e=document.getElementById("time-range");e&&e.addEventListener("change",e=>{this.timeRange=e.target.value,console.log(`ActivityTree: Time range changed to: ${this.timeRange}`),this.renderTree()}),document.addEventListener("sessionFilterChanged",e=>{this.selectedSessionFilter=e.detail.sessionId||"all",console.log(`ActivityTree: Session filter changed to: ${this.selectedSessionFilter} (from SessionManager)`),this.renderTree()}),document.addEventListener("sessionChanged",e=>{this.selectedSessionFilter=e.detail.sessionId||"all",console.log(`ActivityTree: Session changed to: ${this.selectedSessionFilter} (from SessionManager - backward compat)`),this.renderTree()}),setTimeout(()=>{if(window.sessionManager){const e=window.sessionManager.getCurrentFilter();e!==this.selectedSessionFilter&&(this.selectedSessionFilter=e||"all",console.log(`ActivityTree: Initialized with current session filter: ${this.selectedSessionFilter}`),this.renderTree())}},100);const t=document.getElementById("expand-all");t&&t.addEventListener("click",()=>this.expandAllSessions());const s=document.getElementById("collapse-all");s&&s.addEventListener("click",()=>this.collapseAllSessions());const n=document.getElementById("reset-zoom");n&&(n.style.display="inline-block",n.addEventListener("click",()=>this.resetZoom()));const o=document.getElementById("activity-search");o&&o.addEventListener("input",e=>{this.searchTerm=e.target.value.toLowerCase(),this.renderTree()})}createLinearTreeView(){console.log("Creating linear tree view"),this.container.innerHTML="";const e=document.createElement("div");e.id="linear-tree",e.className="linear-tree",this.container.appendChild(e),console.log("Linear tree view created")}subscribeToEvents(){if(!window.socketClient)return console.warn("Socket client not available for activity tree"),void setTimeout(()=>this.subscribeToEvents(),1e3);console.log("ActivityTree: Setting up event subscription"),window.socketClient.onEventUpdate((e,t)=>{console.log(`ActivityTree: onEventUpdate called with ${e.length} total events and ${t.size} sessions`);for(const[n,o]of t.entries())if(this.sessions.has(n)){const e=this.sessions.get(n);e.timestamp=new Date(o.lastActivity||o.startTime||e.timestamp),e.eventCount=o.eventCount,e.status=o.status||e.status,e.working_directory=o.working_directory||e.working_directory,e.git_branch=o.git_branch||e.git_branch}else{const e={id:n,timestamp:new Date(o.lastActivity||o.startTime||new Date),expanded:this.expandedSessions.has(n)||!0,agents:new Map,todos:[],userInstructions:[],tools:[],toolsMap:new Map,status:"active",currentTodoTool:null,working_directory:o.working_directory,git_branch:o.git_branch,eventCount:o.eventCount};this.sessions.set(n,e)}const s=e.filter(e=>{const t=e.id||`${e.type}-${e.timestamp}-${Math.random()}`;return!this.processedEventIds.has(t)});s.length>0&&(console.log(`ActivityTree: Processing ${s.length} new events`,s),s.forEach(e=>{const t=e.id||`${e.type}-${e.timestamp}-${Math.random()}`;this.processedEventIds.add(t),this.processEvent(e)})),this.events=[...e],this.renderTreeDebounced(),console.log("ActivityTree: Sessions after sync with socket client:",Array.from(this.sessions.entries()))});const e=window.socketClient?.getState();if(e&&e.events.length>0){console.log(`ActivityTree: Loading existing data - ${e.events.length} events, ${e.sessions.size} sessions`);for(const[s,n]of e.sessions.entries())if(!this.sessions.has(s)){const e={id:s,timestamp:new Date(n.lastActivity||n.startTime||new Date),expanded:this.expandedSessions.has(s)||!0,agents:new Map,todos:[],userInstructions:[],tools:[],toolsMap:new Map,status:"active",currentTodoTool:null,working_directory:n.working_directory,git_branch:n.git_branch,eventCount:n.eventCount};this.sessions.set(s,e)}const t=e.events.filter(e=>{const t=e.id||`${e.type}-${e.timestamp}-${Math.random()}`;return!this.processedEventIds.has(t)});t.length>0&&(console.log(`ActivityTree: Processing ${t.length} unprocessed events from initial load`),t.forEach(e=>{const t=e.id||`${e.type}-${e.timestamp}-${Math.random()}`;this.processedEventIds.add(t),this.processEvent(e)})),this.events=[...e.events],this.renderTree(),console.log("ActivityTree: Initial sessions state:",Array.from(this.sessions.entries()))}else console.log("ActivityTree: No existing events found"),this.events=[],this.sessions.clear(),this.renderTree()}processEvent(e){if(!e)return void console.log("ActivityTree: Ignoring null event");let t,s=this.getEventType(e);if(!s)return;console.log(`ActivityTree: Processing event: ${s}`,e),e.timestamp?(t=new Date(e.timestamp),isNaN(t.getTime())&&(console.warn("ActivityTree: Invalid timestamp, using current time:",e.timestamp),t=new Date)):(console.warn("ActivityTree: No timestamp found, using current time"),t=new Date);const n=e.session_id||e.data?.session_id;if(!n)return void console.log(`ActivityTree: Skipping event without session_id: ${s}`);if(!this.sessions.has(n))return void console.warn(`ActivityTree: Session ${n} not found in authoritative sessions - skipping event`);const o=this.sessions.get(n);switch(s){case"Start":this.currentSession=o;break;case"user_prompt":this.processUserInstruction(e,o);break;case"TodoWrite":break;case"SubagentStart":this.processSubagentStart(e,o);break;case"SubagentStop":this.processSubagentStop(e,o);break;case"PreToolUse":this.processToolUse(e,o);break;case"PostToolUse":this.updateToolStatus(e,o,"completed")}this.updateStats()}getEventType(e){if(e.hook_event_name)return e.hook_event_name;if("hook"===e.type&&e.subtype){return{pre_tool:"PreToolUse",post_tool:"PostToolUse",subagent_start:"SubagentStart",subagent_stop:"SubagentStop",todo_write:"TodoWrite"}[e.subtype]}if("todo"===e.type&&"updated"===e.subtype)return"TodoWrite";if("subagent"===e.type){if("started"===e.subtype)return"SubagentStart";if("stopped"===e.subtype)return"SubagentStop"}return"start"===e.type?"Start":"user_prompt"===e.type||"user_prompt"===e.subtype?"user_prompt":null}processUserInstruction(e,t){const s=e.prompt_text||e.data?.prompt_text||e.prompt||"";if(!s)return;const n={id:`instruction-${t.id}-${Date.now()}`,text:s,preview:s.length>100?s.substring(0,100)+"...":s,timestamp:e.timestamp||(new Date).toISOString(),type:"user_instruction"};if(t.agents.size>0){console.log("ActivityTree: New user prompt detected, collapsing previous agents");for(let e of t.agents.values())"active"===e.status&&(e.status="completed"),this.expandedAgents.delete(e.id)}t.currentActiveAgent=null,t.userInstructions.push(n),t.userInstructions.length>5&&(t.userInstructions=t.userInstructions.slice(-5))}processTodoWrite(e,t){let s=e.todos||e.data?.todos||e.data||[];if(s&&"object"==typeof s&&s.todos&&(s=s.todos),!Array.isArray(s)||0===s.length)return;t.currentTodos=s.map(t=>({content:t.content,activeForm:t.activeForm,status:t.status,timestamp:e.timestamp}));let n=t.currentActiveAgent;if(!n){const e=this.getAllAgents(t).filter(e=>"active"===e.status||"in_progress"===e.status).sort((e,t)=>new Date(t.timestamp)-new Date(e.timestamp));if(e.length>0)n=e[0];else{const e=this.getAllAgents(t),s=e.find(e=>e.isPM);s?n=s:e.length>0&&(n=e[0])}}if(n){n.todoWritesMap||(n.todoWritesMap=new Map),n.todoWrites||(n.todoWrites=[]);const t=n.todoWritesMap.get("TodoWrite");if(t)t.todos=s,t.timestamp=e.timestamp,t.updateCount=(t.updateCount||1)+1;else{const t={id:`todowrite-${n.id}-${Date.now()}`,name:"TodoWrite",type:"todowrite",icon:"📝",timestamp:e.timestamp,status:"completed",todos:s,params:{todos:s},updateCount:1};n.todoWritesMap.set("TodoWrite",t),n.todoWrites=[t]}n.currentTodos=s}else{t.todoWrites||(t.todoWrites=[]),t.todoWritesMap||(t.todoWritesMap=new Map);const n=t.todoWritesMap.get("TodoWrite");if(n)n.todos=s,n.timestamp=e.timestamp,n.updateCount=(n.updateCount||1)+1;else{const n={id:`todowrite-session-${Date.now()}`,name:"TodoWrite",type:"todowrite",icon:"📝",timestamp:e.timestamp,status:"completed",todos:s,updateCount:1};t.todoWritesMap.set("TodoWrite",n),t.todoWrites=[n]}}}processSubagentStart(e,t){const s=e.agent_name||e.data?.agent_name||e.data?.agent_type||e.agent_type||e.agent||"unknown",n=e.session_id||e.data?.session_id,o=e.parent_agent||e.data?.parent_agent,i=`${s}-${n||"no-session"}`;let a=null;let r;if(a=this.getAllAgents(t).find(e=>e.name===s&&e.sessionId===n&&"active"===e.status),a)r=a,r.timestamp=e.timestamp,r.instanceCount=(r.instanceCount||1)+1,this.expandedAgents.add(r.id);else{if(r={id:`agent-${i}-${Date.now()}`,name:s,type:"agent",icon:this.getAgentIcon(s),timestamp:e.timestamp,status:"active",tools:[],subagents:new Map,sessionId:n,parentAgent:o,isPM:"pm"===s.toLowerCase()||s.toLowerCase().includes("project manager"),instanceCount:1,toolsMap:new Map},o){let e=null;for(let[s,n]of t.agents.entries())if(n.sessionId===o||n.name===o){e=n;break}e?(e.subagents||(e.subagents=new Map),e.subagents.set(r.id,r)):t.agents.set(r.id,r)}else t.agents.set(r.id,r);this.expandedAgents.add(r.id)}t.currentActiveAgent=r}processSubagentStop(e,t){const s=e.session_id||e.data?.session_id;if(s&&t.agents.has(s)){t.agents.get(s).status="completed"}}processToolUse(e,t){const s=e.tool_name||e.data?.tool_name||e.tool||e.data?.tool||"unknown",n=e.tool_parameters||e.data?.tool_parameters||e.parameters||e.data?.parameters||{},o=e.session_id||e.data?.session_id;let i=t.currentActiveAgent;if(!i){const e=this.getAllAgents(t);i=e.find(e=>e.sessionId===o)||e.find(e=>"active"===e.status)||e[0]}if(i){i.toolsMap||(i.toolsMap=new Map),i.tools||(i.tools=[]);const t=this.getToolKey(s,n);let o=i.toolsMap.get(t);if(o)o.params=n,o.timestamp=e.timestamp,o.status="in_progress",o.eventId=e.id,o.callCount=(o.callCount||1)+1,i.currentTool=o;else{const o={id:`tool-${i.id}-${s}-${Date.now()}`,name:s,type:"tool",icon:this.getToolIcon(s),timestamp:e.timestamp,status:"in_progress",params:n,eventId:e.id,callCount:1,createdAt:e.timestamp};"Task"===s&&n.subagent_type&&(o.isSubagentTask=!0,o.subagentType=n.subagent_type),i.toolsMap.set(t,o),"TodoWrite"===s?i.tools.unshift(o):i.tools.push(o),i.currentTool=o}}else{t.tools||(t.tools=[]),t.toolsMap||(t.toolsMap=new Map);const o=this.getToolKey(s,n);let i=t.toolsMap.get(o);if(i)i.params=n,i.timestamp=e.timestamp,i.status="in_progress",i.eventId=e.id,i.callCount=(i.callCount||1)+1,t.currentTool=i;else{const i={id:`tool-session-${s}-${Date.now()}`,name:s,type:"tool",icon:this.getToolIcon(s),timestamp:e.timestamp,status:"in_progress",params:n,eventId:e.id,callCount:1,createdAt:e.timestamp};t.toolsMap.set(o,i),"TodoWrite"===s?t.tools.unshift(i):t.tools.push(i),t.currentTool=i}}}getToolKey(e,t){if("TodoWrite"===e)return"TodoWrite";let s=e;return"Edit"!==e&&"Write"!==e&&"Read"!==e||t.file_path&&(s+=`-${t.file_path}`),"Grep"!==e&&"Glob"!==e||!t.pattern||(s+=`-${t.pattern.substring(0,20)}`),s}updateToolStatus(e,t,s){const n=e.tool_name||e.data?.tool_name||e.tool||"unknown",o=e.tool_parameters||e.data?.tool_parameters||e.parameters||e.data?.parameters||{},i=e.session_id||e.data?.session_id,a=this.getToolKey(n,o);let r=t.currentActiveAgent;if(!r){const e=this.getAllAgents(t);r=e.find(e=>e.sessionId===i)||e.find(e=>"active"===e.status)}if(r&&r.toolsMap){const t=r.toolsMap.get(a);if(t)return t.status=s,t.completedAt=e.timestamp,(e.data?.result||e.result)&&(t.result=e.data?.result||e.result),void(e.data?.duration_ms&&(t.duration=e.data.duration_ms))}if(t.toolsMap){const n=t.toolsMap.get(a);if(n)return n.status=s,n.completedAt=e.timestamp,(e.data?.result||e.result)&&(n.result=e.data?.result||e.result),void(e.data?.duration_ms&&(n.duration=e.data.duration_ms))}console.log(`ActivityTree: Could not find tool to update status for ${n} with key ${a} (event ${e.id})`)}renderTree(){const e=document.getElementById("linear-tree");if(!e)return;e.innerHTML="";const t=Array.from(this.sessions.values()).sort((e,t)=>new Date(t.timestamp)-new Date(e.timestamp));for(let s of t){if("all"!==this.selectedSessionFilter&&this.selectedSessionFilter!==s.id)continue;const t=this.createSessionElement(s);e.appendChild(t)}}createSessionElement(e){const t=this.expandedSessions.has(e.id)||e.expanded;let s;try{const t=e.timestamp instanceof Date?e.timestamp:new Date(e.timestamp);isNaN(t.getTime())?(s="Invalid Date",console.warn("ActivityTree: Invalid session timestamp:",e.timestamp)):s=t.toLocaleString()}catch(l){s="Invalid Date",console.error("ActivityTree: Error formatting session timestamp:",l,e.timestamp)}const n=document.createElement("div");n.className="tree-node session",n.dataset.sessionId=e.id;const o=t?"▼":"▶",i=this.getAllAgents(e).length,a=e.currentTodos?e.currentTodos.length:0,r=e.userInstructions?e.userInstructions.length:0;return console.log(`ActivityTree: Rendering session ${e.id}: ${i} agents, ${r} instructions, ${a} todos at ${s}`),n.innerHTML=`\n <div class="tree-node-content" onclick="window.activityTreeInstance.toggleSession('${e.id}')">\n <span class="tree-expand-icon">${o}</span>\n <span class="tree-icon">🎯</span>\n <span class="tree-label">PM Session</span>\n <span class="tree-meta">${s} • ${i} agent(s) • ${r} instruction(s) • ${a} todo(s)</span>\n </div>\n <div class="tree-children" style="display: ${t?"block":"none"}">\n ${this.renderSessionContent(e)}\n </div>\n `,n}renderSessionContent(e){let t="";if(e.userInstructions&&e.userInstructions.length>0)for(let n of e.userInstructions.slice(-3))t+=this.renderUserInstructionElement(n,1);if(e.tools&&e.tools.length>0)for(let n of e.tools)t+=this.renderToolElement(n,1);const s=Array.from(e.agents.values()).sort((e,t)=>new Date(t.timestamp)-new Date(e.timestamp));for(let n of s)t+=this.renderAgentElement(n,1);return t}renderUserInstructionElement(e,t){return`\n <div class="tree-node user-instruction ${this.selectedItem&&"instruction"===this.selectedItem.type&&this.selectedItem.data.id===e.id?"selected":""}" data-level="${t}">\n <div class="tree-node-content">\n <span class="tree-expand-icon"></span>\n <span class="tree-icon">💬</span>\n <span class="tree-label clickable" onclick="window.activityTreeInstance.selectItem(${this.escapeJson(e)}, 'instruction', event)">User: "${this.escapeHtml(e.preview)}"</span>\n <span class="tree-status status-active">instruction</span>\n </div>\n </div>\n `}renderTodoChecklistElement(e,t){const s=`checklist-${Date.now()}`,n=!1!==this.expandedTools.has(s),o=n?"▼":"▶";let i=0,a=0;e.forEach(e=>{"completed"===e.status?i++:"in_progress"===e.status&&a++});let r="";r=a>0?`${a} in progress, ${i} completed`:i===e.length&&e.length>0?`All ${e.length} completed`:`${e.length} todo(s)`;let l=`\n <div class="tree-node todo-checklist" data-level="${t}">\n <div class="tree-node-content">\n <span class="tree-expand-icon" onclick="window.activityTreeInstance.toggleTodoChecklist('${s}'); event.stopPropagation();">${o}</span>\n <span class="tree-icon">☑️</span>\n <span class="tree-label">TODOs</span>\n <span class="tree-params">${r}</span>\n <span class="tree-status status-active">checklist</span>\n </div>\n `;if(n){l+='<div class="tree-children">';for(let s of e){const e=this.getCheckboxIcon(s.status),n=`status-${s.status}`,o="in_progress"===s.status?s.activeForm:s.content;l+=`\n <div class="tree-node todo-item ${n}" data-level="${t+1}">\n <div class="tree-node-content">\n <span class="tree-expand-icon"></span>\n <span class="tree-icon">${e}</span>\n <span class="tree-label">${this.escapeHtml(o)}</span>\n <span class="tree-status ${n}">${s.status.replace("_"," ")}</span>\n </div>\n </div>\n `}l+="</div>"}return l+="</div>",l}renderAgentElement(e,t){const s="active"===e.status?"status-active":"status-completed",n=this.expandedAgents.has(e.id),o=e.tools&&e.tools.length>0,i=e.subagents&&e.subagents.size>0,a=o||i,r=a?n?"▼":"▶":"",l=this.selectedItem&&"agent"===this.selectedItem.type&&this.selectedItem.data.id===e.id?"selected":"",c=e.instanceCount>1?` (${e.instanceCount}x)`:"";let d="";if(!n&&a){const t=[];if(e.currentTodos&&e.currentTodos.length>0){const s=e.currentTodos.find(e=>"in_progress"===e.status);s&&t.push(`📝 ${s.activeForm||s.content}`)}e.currentTool&&t.push(`${e.currentTool.icon} ${e.currentTool.name}`),t.length>0&&(d=` • ${t.join(" • ")}`)}let p=`\n <div class="tree-node agent ${s} ${l}" data-level="${t}">\n <div class="tree-node-content">\n ${r?`<span class="tree-expand-icon" onclick="window.activityTreeInstance.toggleAgent('${e.id}'); event.stopPropagation();">${r}</span>`:'<span class="tree-expand-icon"></span>'}\n <span class="tree-icon">${e.icon}</span>\n <span class="tree-label clickable" onclick="window.activityTreeInstance.selectItem(${this.escapeJson(e)}, 'agent', event)">${e.name}${c}${d}</span>\n <span class="tree-status ${s}">${e.status}</span>\n </div>\n `;if(a&&n){if(p+='<div class="tree-children">',o)for(let s of e.tools)p+=this.renderToolElement(s,t+1);if(i){const s=Array.from(e.subagents.values());for(let e of s)p+=this.renderAgentElement(e,t+1)}p+="</div>"}return p+="</div>",p}renderToolElement(e,t){const s=`status-${e.status}`,n=this.getToolParams(e),o=this.selectedItem&&"tool"===this.selectedItem.type&&this.selectedItem.data.id===e.id?"selected":"",i=this.getToolStatusIcon(e.status),a=this.getToolStatusLabel(e.status),r=e.callCount>1?` (${e.callCount} calls)`:"";return`\n <div class="tree-node tool ${s} ${o}" data-level="${t}">\n <div class="tree-node-content">\n <span class="tree-expand-icon"></span>\n <span class="tree-icon">${e.icon}</span>\n <span class="tree-status-icon">${i}</span>\n <span class="tree-label clickable" onclick="window.activityTreeInstance.selectItem(${this.escapeJson(e)}, 'tool', event)">${e.name}${r}</span>\n <span class="tree-params">${n}</span>\n <span class="tree-status ${s}">${a}</span>\n </div>\n </div>\n `}getToolParams(e){if(!e.params)return"";if("Read"===e.name&&e.params.file_path)return e.params.file_path;if("Edit"===e.name&&e.params.file_path)return e.params.file_path;if("Write"===e.name&&e.params.file_path)return e.params.file_path;if("Bash"===e.name&&e.params.command){const t=e.params.command;return t.length>50?t.substring(0,50)+"...":t}return"WebFetch"===e.name&&e.params.url?e.params.url:""}getStatusIcon(e){return{pending:"⏸️",in_progress:"🔄",completed:"✅"}[e]||"❓"}getCheckboxIcon(e){return{pending:"⏳",in_progress:"🔄",completed:"✅"}[e]||"❓"}getAgentIcon(e){return{engineer:"👷",research:"🔬",qa:"🧪",ops:"⚙️",pm:"📊",architect:"🏗️","project manager":"📊"}[e.toLowerCase()]||"🤖"}getAllAgents(e){const t=[],s=e=>{if(e)for(let n of e.values())t.push(n),n.subagents&&n.subagents.size>0&&s(n.subagents)};return s(e.agents),t}renderTodoWriteElement(e,t){const s=e.id,n=this.expandedTools.has(s),o=n?"▼":"▶",i=e.todos||[];let a=0,r=0;i.forEach(e=>{"completed"===e.status?a++:"in_progress"===e.status&&r++});const l=i.find(e=>"in_progress"===e.status),c=l?` • 🔄 ${l.activeForm||l.content}`:"";let d="";d=r>0?`${r} in progress, ${a}/${i.length} done`:a===i.length&&i.length>0?`All ${i.length} completed ✅`:`${a}/${i.length} done`;let p=`\n <div class="tree-node todowrite ${l?"has-active":""}" data-level="${t}">\n <div class="tree-node-content">\n <span class="tree-expand-icon" onclick="window.activityTreeInstance.toggleTodoWrite('${s}'); event.stopPropagation();">${o}</span>\n <span class="tree-icon">📝</span>\n <span class="tree-label">TodoWrite${e.updateCount>1?` (${e.updateCount} updates)`:""}${n?"":c}</span>\n <span class="tree-params">${d}</span>\n <span class="tree-status status-active">todos</span>\n </div>\n `;if(n&&i.length>0){p+='<div class="tree-children">';for(let e of i){const s=this.getCheckboxIcon(e.status),n=`status-${e.status}`,o="in_progress"===e.status?e.activeForm:e.content;p+=`\n <div class="tree-node todo-item ${n} ${e===l?"current-active":""}" data-level="${t+1}">\n <div class="tree-node-content">\n <span class="tree-expand-icon"></span>\n <span class="tree-icon">${s}</span>\n <span class="tree-label">${this.escapeHtml(o)}</span>\n <span class="tree-status ${n}">${e.status.replace("_"," ")}</span>\n </div>\n </div>\n `}p+="</div>"}return p+="</div>",p}toggleTodoWrite(e){this.expandedTools.has(e)?this.expandedTools.delete(e):this.expandedTools.add(e),this.renderTree()}getToolIcon(e){return{read:"👁️",write:"✍️",edit:"✏️",bash:"💻",webfetch:"🌐",grep:"🔍",glob:"📂",todowrite:"📝"}[e.toLowerCase()]||"🔧"}getToolStatusIcon(e){return{in_progress:"⏳",completed:"✅",failed:"❌",error:"❌",pending:"⏸️",active:"🔄"}[e]||"❓"}getToolStatusLabel(e){return{in_progress:"in progress",completed:"completed",failed:"failed",error:"error",pending:"pending",active:"active"}[e]||e}toggleSession(e){this.expandedSessions.has(e)?this.expandedSessions.delete(e):this.expandedSessions.add(e);const t=this.sessions.get(e);t&&(t.expanded=this.expandedSessions.has(e)),this.renderTree()}expandAllSessions(){for(let e of this.sessions.keys()){this.expandedSessions.add(e);const t=this.sessions.get(e);t&&(t.expanded=!0)}this.renderTree()}collapseAllSessions(){this.expandedSessions.clear();for(let e of this.sessions.values())e.expanded=!1;this.renderTree()}updateStats(){const e=this.countTotalNodes(),t=this.countActiveNodes(),s=this.calculateMaxDepth(),n=document.getElementById("node-count"),o=document.getElementById("active-count"),i=document.getElementById("tree-depth");n&&(n.textContent=e),o&&(o.textContent=t),i&&(i.textContent=s),console.log(`ActivityTree: Stats updated - Nodes: ${e}, Active: ${t}, Depth: ${s}`)}countTotalNodes(){let e=0;for(let t of this.sessions.values()){e+=1,e+=t.agents.size,t.userInstructions&&(e+=t.userInstructions.length),t.todos&&(e+=t.todos.length),t.tools&&(e+=t.tools.length);for(let s of t.agents.values())s.tools&&(e+=s.tools.length)}return e}countActiveNodes(){let e=0;for(let t of this.sessions.values()){if("active"===t.status&&e++,t.todos)for(let s of t.todos)"in_progress"===s.status&&e++;if(t.tools)for(let s of t.tools)"in_progress"===s.status&&e++;for(let s of t.agents.values())if("active"===s.status&&e++,s.tools)for(let t of s.tools)"in_progress"===t.status&&e++}return e}calculateMaxDepth(){let e=0;for(let t of this.sessions.values()){let s=1;t.userInstructions&&t.userInstructions.length>0&&(s=Math.max(s,2)),t.todos&&t.todos.length>0&&(s=Math.max(s,3)),t.tools&&t.tools.length>0&&(s=Math.max(s,2));for(let e of t.agents.values())e.tools&&e.tools.length>0&&(s=Math.max(s,3));e=Math.max(e,s)}return e}toggleAgent(e){this.expandedAgents.has(e)?this.expandedAgents.delete(e):this.expandedAgents.add(e),this.renderTree()}toggleTool(e){console.log("Tool expansion is disabled. Tools now show data in the left pane when clicked.")}toggleTodoChecklist(e){this.expandedTools.has(e)?this.expandedTools.delete(e):this.expandedTools.add(e),this.renderTree()}renderPinnedTodosElement(e,t){const s=`pinned-todos-${Date.now()}`,n=!1!==this.expandedTools.has(s),o=n?"▼":"▶",i=e.todos||[];let a=0,r=0;i.forEach(e=>{"completed"===e.status?a++:"in_progress"===e.status&&r++});let l="";l=r>0?`${r} in progress, ${a} completed`:a===i.length&&i.length>0?`All ${i.length} completed`:`${i.length} todo(s)`;let c=`\n <div class="tree-node pinned-todos" data-level="${t}">\n <div class="tree-node-content">\n <span class="tree-expand-icon" onclick="window.activityTreeInstance.toggleTodoChecklist('${s}'); event.stopPropagation();">${o}</span>\n <span class="tree-icon">📌</span>\n <span class="tree-label">Pinned TODOs</span>\n <span class="tree-params">${l}</span>\n <span class="tree-status status-active">pinned</span>\n </div>\n `;if(n){c+='<div class="tree-children">';for(let e of i){const s=this.getCheckboxIcon(e.status),n=`status-${e.status}`,o="in_progress"===e.status?e.activeForm:e.content;c+=`\n <div class="tree-node todo-item ${n}" data-level="${t+1}">\n <div class="tree-node-content">\n <span class="tree-expand-icon"></span>\n <span class="tree-icon">${s}</span>\n <span class="tree-label">${this.escapeHtml(o)}</span>\n <span class="tree-status ${n}">${e.status.replace("_"," ")}</span>\n </div>\n </div>\n `}c+="</div>"}return c+="</div>",c}selectItem(e,t,s){s&&s.stopPropagation(),this.selectedItem={data:e,type:t},this.displayItemData(e,t),this.renderTree()}displayItemData(t,s){this.unifiedViewer||(this.unifiedViewer=new e("module-data-content")),this.unifiedViewer.display(t,s);const n=document.querySelector(".module-data-header h5");if(n){const e={agent:"🤖",tool:"🔧",instruction:"💬",session:"🎯",todo:"📝"}[s]||"📊",o=t.name||t.agentName||t.tool_name||"Item";n.textContent=`${e} ${s}: ${o}`}}escapeHtml(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}resetZoom(){this.svg&&this.zoom&&this.svg.transition().duration(this.duration).call(this.zoom.transform,d3.zoomIdentity)}escapeJson(e){return JSON.stringify(e).replace(/'/g,"'").replace(/"/g,""")}}window.ActivityTree=t;const s=()=>{let e=null;const s=()=>{e||(console.log("Creating new Activity Tree instance..."),e=new t,window.activityTreeInstance=e,window.activityTree=()=>e),setTimeout(()=>{console.log("Attempting to initialize Activity Tree visualization..."),e.initialize()},100)};document.querySelectorAll(".tab-button").forEach(t=>{t.addEventListener("click",t=>{"activity"===t.target.getAttribute("data-tab")&&(console.log("Activity tab button clicked, initializing tree..."),s(),e&&setTimeout(()=>{e.renderWhenVisible(),e.forceShow()},150))})}),document.addEventListener("tabChanged",t=>{t.detail&&"activity"===t.detail.newTab&&(console.log("Tab changed to activity, initializing tree..."),s(),e&&setTimeout(()=>{e.renderWhenVisible(),e.forceShow()},150))});const n=document.querySelector(".tab-button.active");n&&"activity"===n.getAttribute("data-tab")&&(console.log("Activity tab is active on load, initializing tree..."),s());const o=document.getElementById("activity-tab");o&&o.classList.contains("active")&&(console.log("Activity panel is active on load, initializing tree..."),e||s())};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",s):s();
|
|
2
2
|
//# sourceMappingURL=activity-tree.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{U as t}from"../chunks/unified-data-viewer.CvAmgM3l.js";class e{constructor(e){this.container=document.getElementById(e),this.dataContainer=null,this.jsonContainer=null,this.currentEvent=null,this.eventsByClass=new Map,this.globalJsonExpanded="true"===localStorage.getItem("dashboard-json-expanded"),this.keyboardListenerAdded=!1,this.unifiedViewer=new t("module-data-content"),this.init()}init(){this.setupContainers(),this.setupEventHandlers(),this.showEmptyState()}setupContainers(){this.dataContainer=document.getElementById("module-data-content"),this.jsonContainer=null,this.dataContainer||console.error("Module viewer data container not found")}setupEventHandlers(){document.addEventListener("eventSelected",t=>{this.showEventDetails(t.detail.event)}),document.addEventListener("eventSelectionCleared",()=>{this.showEmptyState()}),document.addEventListener("socketEventUpdate",t=>{this.updateEventsByClass(t.detail.events)})}showEmptyState(){this.dataContainer&&(this.dataContainer.innerHTML='\n <div class="module-empty">\n <p>Click on an event to view structured data</p>\n <p class="module-hint">Data is organized by event type</p>\n </div>\n '),this.currentEvent=null}showEventDetails(t){if(this.currentEvent=t,!this.unifiedViewer)return console.warn("ModuleViewer: UnifiedDataViewer not available"),this.renderStructuredData(t),void this.renderJsonData(t);this.unifiedViewer.display(t,"event")}renderStructuredData(t){if(!this.dataContainer)return;const e=this.createContextualHeader(t),n=this.createEventStructuredView(t),s=this.createCollapsibleJsonSection(t);this.dataContainer.innerHTML=e+n+s,this.initializeJsonToggle()}renderJsonData(t){}ingest(t){Array.isArray(t)?t.length>0?this.showEventDetails(t[0]):this.showEmptyState():t&&"object"==typeof t?this.showEventDetails(t):this.showEmptyState()}updateEventsByClass(t){this.eventsByClass.clear(),t.forEach(t=>{const e=this.getEventClass(t);this.eventsByClass.has(e)||this.eventsByClass.set(e,[]),this.eventsByClass.get(e).push(t)})}getEventClass(t){if(!t.type)return"unknown";switch(t.type){case"session":return"Session Management";case"claude":return"Claude Interactions";case"agent":return"Agent Operations";case"hook":return"Hook System";case"todo":return"Task Management";case"memory":return"Memory Operations";case"log":return"System Logs";case"connection":return"Connection Events";default:return"Other Events"}}createContextualHeader(t){const e=this.formatTimestamp(t.timestamp),n=t.data||{};let s="";switch(t.type){case"hook":const o=this.extractToolName(n),i=this.extractAgent(t)||"Unknown";if(o)s=`${o}: ${i} ${e}`;else{s=`${this.getHookDisplayName(t,n)}: ${i} ${e}`}break;case"agent":s=`Agent: ${n.agent_type||n.name||"Unknown"} ${e}`;break;case"todo":s=`TodoWrite: ${this.extractAgent(t)||"PM"} ${e}`;break;case"memory":s=`Memory: ${n.operation||"Unknown"} ${e}`;break;case"session":case"claude":case"log":case"connection":s=`Event: ${t.type}.${t.subtype||"default"} ${e}`;break;default:const a=this.extractFileName(n);if(a)s=`File: ${a} ${e}`;else{s=`Event: ${t.type||"Unknown"}.${t.subtype||"default"} ${e}`}}return`\n <div class="contextual-header">\n <h3 class="contextual-header-text">${s}</h3>\n </div>\n `}createEventStructuredView(t){const e=this.getEventClass(t),n=(this.eventsByClass.get(e)||[]).length;let s=`\n <div class="structured-view-section">\n ${this.createEventDetailCard(t.type,t,n)}\n </div>\n `;switch(t.type){case"agent":s+=this.createAgentStructuredView(t);break;case"hook":"Task"===t.data?.tool_name&&t.data?.tool_parameters?.subagent_type?s+=this.createAgentStructuredView(t):s+=this.createHookStructuredView(t);break;case"todo":s+=this.createTodoStructuredView(t);break;case"memory":s+=this.createMemoryStructuredView(t);break;case"claude":s+=this.createClaudeStructuredView(t);break;case"session":s+=this.createSessionStructuredView(t);break;default:s+=this.createGenericStructuredView(t)}return s}createEventDetailCard(t,e,n){const s=new Date(e.timestamp).toLocaleString();return`\n <div class="event-detail-card">\n <div class="event-detail-header">\n <div class="event-detail-title">\n ${this.getEventIcon(t)} ${t||"Unknown"}.${e.subtype||"default"}\n </div>\n <div class="event-detail-time">${s}</div>\n </div>\n <div class="event-detail-content">\n ${this.createProperty("Event ID",e.id||"N/A")}\n ${this.createProperty("Type",`${t}.${e.subtype||"default"}`)}\n ${this.createProperty("Class Events",n)}\n ${e.data&&e.data.session_id?this.createProperty("Session",e.data.session_id):""}\n </div>\n </div>\n `}createAgentStructuredView(t){const e=t.data||{};if("hook"===t.type&&"Task"===e.tool_name&&e.tool_parameters?.subagent_type){const n=e.tool_parameters;return`\n <div class="structured-view-section">\n <div class="structured-data">\n ${this.createProperty("Agent Type",n.subagent_type)}\n ${this.createProperty("Task Type","Subagent Delegation")}\n ${this.createProperty("Phase",t.subtype||"pre_tool")}\n ${n.description?this.createProperty("Description",n.description):""}\n ${n.prompt?this.createProperty("Prompt Preview",this.truncateText(n.prompt,200)):""}\n ${e.session_id?this.createProperty("Session ID",e.session_id):""}\n ${e.working_directory?this.createProperty("Working Directory",e.working_directory):""}\n </div>\n ${n.prompt?`\n <div class="prompt-section">\n <div class="contextual-header">\n <h3 class="contextual-header-text">📝 Task Prompt</h3>\n </div>\n <div class="structured-data">\n <div class="task-prompt" style="white-space: pre-wrap; max-height: 300px; overflow-y: auto; padding: 10px; background: #f8fafc; border-radius: 6px; font-family: monospace; font-size: 12px; line-height: 1.4;">\n ${n.prompt}\n </div>\n </div>\n </div>\n `:""}\n </div>\n `}return`\n <div class="structured-view-section">\n <div class="structured-data">\n ${this.createProperty("Agent Type",e.agent_type||e.subagent_type||"Unknown")}\n ${this.createProperty("Name",e.name||"N/A")}\n ${this.createProperty("Phase",t.subtype||"N/A")}\n ${e.config?this.createProperty("Config","object"==typeof e.config?Object.keys(e.config).join(", "):String(e.config)):""}\n ${e.capabilities?this.createProperty("Capabilities",e.capabilities.join(", ")):""}\n ${e.result?this.createProperty("Result","object"==typeof e.result?"[Object]":String(e.result)):""}\n </div>\n </div>\n `}createHookStructuredView(t){const e=t.data||{},n=this.extractFilePathFromHook(e),s=this.extractToolInfoFromHook(e),o=this.createInlineToolResultContent(e,t);return`\n <div class="structured-view-section">\n <div class="structured-data">\n ${this.createProperty("Hook Name",this.getHookDisplayName(t,e))}\n ${this.createProperty("Event Type",e.event_type||t.subtype||"N/A")}\n ${n?this.createProperty("File Path",n):""}\n ${s.tool_name?this.createProperty("Tool",s.tool_name):""}\n ${s.operation_type?this.createProperty("Operation",s.operation_type):""}\n ${e.session_id?this.createProperty("Session ID",e.session_id):""}\n ${e.working_directory?this.createProperty("Working Directory",e.working_directory):""}\n ${e.duration_ms?this.createProperty("Duration",`${e.duration_ms}ms`):""}\n ${o}\n </div>\n </div>\n `}createInlineToolResultContent(t,e=null){const n=t.result_summary,s=e?.subtype||t.event_type||t.phase,o="post_tool"===s||s?.includes("post");if(window.DEBUG_TOOL_RESULTS&&console.log("🔧 createInlineToolResultContent debug:",{hasResultSummary:!!n,eventPhase:s,isPostTool:o,eventSubtype:e?.subtype,dataEventType:t.event_type,dataPhase:t.phase,toolName:t.tool_name,resultSummaryKeys:n?Object.keys(n):[]}),!n)return"";if("pre_tool"===s||s?.includes("pre")&&!s?.includes("post"))return"";let i="";if(n.has_output&&n.output_preview&&(i+=`\n ${this.createProperty("Output",this.truncateText(n.output_preview,200))}\n ${n.output_lines?this.createProperty("Output Lines",n.output_lines):""}\n `),n.has_error&&n.error_preview&&(i+=`\n ${this.createProperty("Error",this.truncateText(n.error_preview,200))}\n `),!n.has_output&&!n.has_error&&Object.keys(n).length>3){i+=Object.entries(n).filter(([t,e])=>!["has_output","has_error","exit_code"].includes(t)&&void 0!==e).map(([t,e])=>this.createProperty(this.formatFieldName(t),String(e))).join("")}return i}createToolResultSection(t,e=null){const n=t.result_summary,s=e?.subtype||t.event_type||t.phase,o="post_tool"===s||s?.includes("post");if(window.DEBUG_TOOL_RESULTS&&console.log("🔧 createToolResultSection debug:",{hasResultSummary:!!n,eventPhase:s,isPostTool:o,eventSubtype:e?.subtype,dataEventType:t.event_type,dataPhase:t.phase,toolName:t.tool_name,resultSummaryKeys:n?Object.keys(n):[]}),!n)return"";if("pre_tool"===s||s?.includes("pre")&&!s?.includes("post"))return"";let i="⏳",a="tool-running",r="Unknown";!0===t.success?(i="✅",a="tool-success",r="Success"):!1===t.success?(i="❌",a="tool-failure",r="Failed"):0===t.exit_code?(i="✅",a="tool-success",r="Completed"):2===t.exit_code?(i="⚠️",a="tool-blocked",r="Blocked"):void 0!==t.exit_code&&0!==t.exit_code&&(i="❌",a="tool-failure",r="Error");let l="";if(l+=`\n <div class="tool-result-status ${a}">\n <span class="tool-result-icon">${i}</span>\n <span class="tool-result-text">${r}</span>\n ${void 0!==t.exit_code?`<span class="tool-exit-code">Exit Code: ${t.exit_code}</span>`:""}\n </div>\n `,n.has_output&&n.output_preview&&(l+=`\n <div class="tool-result-output">\n <div class="tool-result-label">📄 Output:</div>\n <div class="tool-result-preview">\n <pre>${this.escapeHtml(n.output_preview)}</pre>\n </div>\n ${n.output_lines?`<div class="tool-result-meta">Lines: ${n.output_lines}</div>`:""}\n </div>\n `),n.has_error&&n.error_preview&&(l+=`\n <div class="tool-result-error">\n <div class="tool-result-label">⚠️ Error:</div>\n <div class="tool-result-preview error-preview">\n <pre>${this.escapeHtml(n.error_preview)}</pre>\n </div>\n </div>\n `),!n.has_output&&!n.has_error&&Object.keys(n).length>3){const t=Object.entries(n).filter(([t,e])=>!["has_output","has_error","exit_code"].includes(t)&&void 0!==e).map(([t,e])=>this.createProperty(this.formatFieldName(t),String(e))).join("");t&&(l+=`\n <div class="tool-result-other">\n <div class="tool-result-label">📊 Result Details:</div>\n <div class="structured-data">\n ${t}\n </div>\n </div>\n `)}return l.trim()?`\n <div class="tool-result-section">\n <div class="contextual-header">\n <h3 class="contextual-header-text">🔧 Tool Result</h3>\n </div>\n <div class="tool-result-content">\n ${l}\n </div>\n </div>\n `:""}isWriteOperation(t,e){if(["Write","Edit","MultiEdit","NotebookEdit"].includes(t))return!0;if(e.tool_parameters){const t=e.tool_parameters;if(t.content||t.new_string||t.edits)return!0;if(t.edit_mode&&"read"!==t.edit_mode)return!0}return!("post_tool"!==e.event_type&&"pre_tool"!==e.event_type||!t||!(t.toLowerCase().includes("write")||t.toLowerCase().includes("edit")||t.toLowerCase().includes("modify")))}isReadOnlyOperation(t){if(!t)return!0;const e=t.toLowerCase();return!!["read"].includes(e)||!["write","edit","multiedit","create","delete","move","copy"].includes(e)}createTodoStructuredView(t){const e=t.data||{};let n="";return e.todos&&Array.isArray(e.todos)&&(n+=`\n <div class="todo-checklist">\n ${e.todos.map(t=>`\n <div class="todo-item todo-${t.status||"pending"}">\n <span class="todo-status">${this.getTodoStatusIcon(t.status)}</span>\n <span class="todo-content">${t.content||"No content"}</span>\n <span class="todo-priority priority-${t.priority||"medium"}">${this.getTodoPriorityIcon(t.priority)}</span>\n </div>\n `).join("")}\n </div>\n `),n}createMemoryStructuredView(t){const e=t.data||{};return`\n <div class="structured-view-section">\n <div class="structured-data">\n ${this.createProperty("Operation",e.operation||"Unknown")}\n ${this.createProperty("Key",e.key||"N/A")}\n ${e.value?this.createProperty("Value","object"==typeof e.value?"[Object]":String(e.value)):""}\n ${e.namespace?this.createProperty("Namespace",e.namespace):""}\n ${e.metadata?this.createProperty("Metadata","object"==typeof e.metadata?"[Object]":String(e.metadata)):""}\n </div>\n </div>\n `}createClaudeStructuredView(t){const e=t.data||{};return`\n <div class="structured-view-section">\n <div class="structured-data">\n ${this.createProperty("Type",t.subtype||"N/A")}\n ${e.prompt?this.createProperty("Prompt",this.truncateText(e.prompt,200)):""}\n ${e.message?this.createProperty("Message",this.truncateText(e.message,200)):""}\n ${e.response?this.createProperty("Response",this.truncateText(e.response,200)):""}\n ${e.content?this.createProperty("Content",this.truncateText(e.content,200)):""}\n ${e.tokens?this.createProperty("Tokens",e.tokens):""}\n ${e.model?this.createProperty("Model",e.model):""}\n </div>\n </div>\n `}createSessionStructuredView(t){const e=t.data||{};return`\n <div class="structured-view-section">\n <div class="structured-data">\n ${this.createProperty("Action",t.subtype||"N/A")}\n ${this.createProperty("Session ID",e.session_id||"N/A")}\n ${e.working_directory?this.createProperty("Working Dir",e.working_directory):""}\n ${e.git_branch?this.createProperty("Git Branch",e.git_branch):""}\n ${e.agent_type?this.createProperty("Agent Type",e.agent_type):""}\n </div>\n </div>\n `}createGenericStructuredView(t){const e=t.data||{},n=Object.keys(e);return 0===n.length?"":`\n <div class="structured-view-section">\n <div class="structured-data">\n ${n.map(t=>this.createProperty(t,"object"==typeof e[t]?"[Object]":String(e[t]))).join("")}\n </div>\n </div>\n `}createCollapsibleJsonSection(t){const e="json-section-"+Math.random().toString(36).substr(2,9),n=this.formatJSON(t),s=this.globalJsonExpanded;return`\n <div class="collapsible-json-section" id="${e}">\n <div class="json-toggle-header"\n onclick="window.moduleViewer.toggleJsonSection()"\n role="button"\n tabindex="0"\n aria-expanded="${s?"true":"false"}"\n onkeydown="if(event.key==='Enter'||event.key===' '){window.moduleViewer.toggleJsonSection();event.preventDefault();}">\n <span class="json-toggle-text">Raw JSON</span>\n <span class="json-toggle-arrow">${s?"▲":"▼"}</span>\n </div>\n <div class="json-content-collapsible" style="display: ${s?"block":"none"};" aria-hidden="${!s}">\n <div class="json-display" onclick="window.moduleViewer.copyJsonToClipboard(event)">\n <pre>${n}</pre>\n </div>\n </div>\n </div>\n `}async copyJsonToClipboard(t){const e=t.currentTarget.getBoundingClientRect(),n=t.clientX-e.left,s=t.clientY-e.top;if(n>e.width-50&&s<30){const e=t.currentTarget.querySelector("pre");if(e)try{await navigator.clipboard.writeText(e.textContent),this.showNotification("JSON copied to clipboard","success")}catch(o){console.error("Failed to copy JSON:",o),this.showNotification("Failed to copy JSON","error")}t.stopPropagation()}}initializeJsonToggle(){window.moduleViewer=this,this.globalJsonExpanded&&setTimeout(()=>{this.updateAllJsonSections()},0),this.keyboardListenerAdded||(this.keyboardListenerAdded=!0,document.addEventListener("keydown",t=>{t.target.classList.contains("json-toggle-header")&&("Enter"!==t.key&&" "!==t.key||(this.toggleJsonSection(),t.preventDefault()))}))}toggleJsonSection(){this.globalJsonExpanded=!this.globalJsonExpanded,localStorage.setItem("dashboard-json-expanded",this.globalJsonExpanded.toString()),this.updateAllJsonSections(),document.dispatchEvent(new CustomEvent("jsonToggleChanged",{detail:{expanded:this.globalJsonExpanded}}))}updateAllJsonSections(){const t=document.querySelectorAll(".json-content-collapsible"),e=document.querySelectorAll(".json-toggle-arrow"),n=document.querySelectorAll(".json-toggle-header");t.forEach((t,s)=>{this.globalJsonExpanded?(t.style.display="block",t.setAttribute("aria-hidden","false"),e[s]&&(e[s].textContent="▲"),n[s]&&n[s].setAttribute("aria-expanded","true")):(t.style.display="none",t.setAttribute("aria-hidden","true"),e[s]&&(e[s].textContent="▼"),n[s]&&n[s].setAttribute("aria-expanded","false"))}),this.globalJsonExpanded&&t.length>0&&setTimeout(()=>{const e=t[0];e&&e.scrollIntoView({behavior:"smooth",block:"nearest"})},100)}createProperty(t,e){const n=this.truncateText(String(e),300);return this.isFilePathProperty(t,e)?`\n <div class="event-property">\n <span class="event-property-key">${t}:</span>\n <span class="event-property-value">\n ${this.createClickableFilePath(e)}\n </span>\n </div>\n `:`\n <div class="event-property">\n <span class="event-property-key">${t}:</span>\n <span class="event-property-value">${n}</span>\n </div>\n `}isFilePathProperty(t,e){if(["File Path","file_path","notebook_path","Full Path","Working Directory","working_directory"].some(e=>t.toLowerCase().includes(e.toLowerCase()))){const t=String(e);return t.length>0&&(t.includes("/")||t.includes("\\"))&&t.length<500}return!1}createClickableFilePath(t){const e=this.truncateText(String(t),300);return`\n <span class="clickable-file-path"\n onclick="showFileViewerModal('${t.replace(/'/g,"\\'")}')"\n title="Click to view file contents with syntax highlighting Path: ${t}">\n ${e}\n </span>\n `}getEventIcon(t){const e={session:"📱",claude:"🤖",agent:"🎯",hook:"🔗",todo:"✅",memory:"🧠",log:"📝",connection:"🔌",unknown:"❓"};return e[t]||e.unknown}getTodoStatusIcon(t){const e={completed:"✅",in_progress:"🔄",pending:"⏳",cancelled:"❌"};return e[t]||e.pending}getTodoPriorityIcon(t){const e={high:"🔴",medium:"🟡",low:"🟢"};return e[t]||e.medium}getHookDisplayName(t,e){if(e.hook_name)return e.hook_name;if(e.name)return e.name;const n=t.subtype||e.event_type,s={user_prompt:"User Prompt",pre_tool:"Tool Execution (Pre)",post_tool:"Tool Execution (Post)",notification:"Notification",stop:"Session Stop",subagent_stop:"Subagent Stop"};if(s[n])return s[n];if("string"==typeof t.type&&t.type.startsWith("hook.")){const e=t.type.replace("hook.","");if(s[e])return s[e]}return n?n.split("_").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" "):"Unknown Hook"}extractFilePathFromHook(t){return t.tool_parameters&&t.tool_parameters.file_path?t.tool_parameters.file_path:t.file_path?t.file_path:t.tool_input&&t.tool_input.file_path?t.tool_input.file_path:t.tool_parameters&&t.tool_parameters.notebook_path?t.tool_parameters.notebook_path:null}extractToolInfoFromHook(t){return{tool_name:t.tool_name||t.tool_parameters&&t.tool_parameters.tool_name,operation_type:t.operation_type||t.tool_parameters&&t.tool_parameters.operation_type}}truncateText(t,e){return!t||t.length<=e?t:t.substring(0,e)+"..."}formatOperationDetails(t){if(!t||"object"!=typeof t)return"";let e="";if(t.parameters&&t.parameters.command&&(e+=`<br><strong>Command:</strong> <code>${this.escapeHtml(t.parameters.command)}</code>`),void 0!==t.success&&(e+="<br><strong>Status:</strong> "+(t.success?"✅ Success":"❌ Failed")),void 0!==t.exit_code&&null!==t.exit_code&&(e+=`<br><strong>Exit Code:</strong> ${t.exit_code}`),void 0!==t.duration_ms&&null!==t.duration_ms){e+=`<br><strong>Duration:</strong> ${t.duration_ms>1e3?`${(t.duration_ms/1e3).toFixed(2)}s`:`${t.duration_ms}ms`}`}return t.error&&(e+=`<br><strong>Error:</strong> ${this.escapeHtml(this.truncateText(t.error,200))}`),e}escapeHtml(t){if(!t)return"";const e=document.createElement("div");return e.textContent=t,e.innerHTML}formatJSON(t){try{return JSON.stringify(t,null,2)}catch(e){return String(t)}}formatTimestamp(t){if(!t)return"Unknown time";try{return new Date(t).toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",second:"2-digit",hour12:!0})}catch(e){return"Invalid time"}}escapeHtml(t){if(!t)return"";const e=document.createElement("div");return e.textContent=t,e.innerHTML}formatFieldName(t){return t.split("_").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}extractToolName(t){if(t.tool_name)return t.tool_name;if(t.tool_parameters&&t.tool_parameters.tool_name)return t.tool_parameters.tool_name;if(t.tool_input&&t.tool_input.tool_name)return t.tool_input.tool_name;if(t.tool_parameters){if(t.tool_parameters.file_path||t.tool_parameters.notebook_path)return"FileOperation";if(t.tool_parameters.pattern)return"Search";if(t.tool_parameters.command)return"Bash";if(t.tool_parameters.todos)return"TodoWrite"}return null}extractAgent(t){if(t._agentName&&"Unknown Agent"!==t._agentName)return t._agentName;if(t._inference&&t._inference.agentName&&"Unknown"!==t._inference.agentName)return t._inference.agentName;if(t.agent)return t.agent;if(t.agent_type)return t.agent_type;if(t.agent_name)return t.agent_name;if(t.session_id&&"string"==typeof t.session_id){const e=t.session_id.split("_");if(e.length>1)return e[0].toUpperCase()}return t.todos||"TodoWrite"===t.tool_name?"PM":null}extractFileName(t){const e=this.extractFilePathFromHook(t);if(e){const t=e.split("/");return t[t.length-1]}return t.filename?t.filename:t.file?t.file:null}clear(){this.unifiedViewer&&this.unifiedViewer.clear(),this.showEmptyState()}showToolCall(t,e){if(!t)return void this.showEmptyState();const n=t.tool_name||"Unknown Tool",s=t.agent_type||"PM",o=this.formatTimestamp(t.timestamp),i=t.pre_event,a=t.post_event,r=i?.tool_parameters||{},l=i?this.extractToolTarget(n,r):"Unknown target",c=t.duration_ms?`${t.duration_ms}ms`:"-",d=void 0!==t.success?t.success:null;void 0!==t.exit_code&&t.exit_code;let p=t.result_summary||"No summary available";if("object"==typeof p&&null!==p){const t=[];void 0!==p.exit_code&&t.push(`Exit Code: ${p.exit_code}`),void 0!==p.has_output&&t.push("Has Output: "+(p.has_output?"Yes":"No")),void 0!==p.has_error&&t.push("Has Error: "+(p.has_error?"Yes":"No")),void 0!==p.output_lines&&t.push(`Output Lines: ${p.output_lines}`),p.output_preview&&t.push(`Output Preview: ${p.output_preview}`),p.error_preview&&t.push(`Error Preview: ${p.error_preview}`)}let u="⏳",h="Running...",m="tool-running";a&&(!0===d?(u="✅",h="Success",m="tool-success"):!1===d?(u="❌",h="Failed",m="tool-failure"):(u="⏳",h="Completed",m="tool-completed"));const g=`\n <div class="contextual-header">\n <h3 class="contextual-header-text">${n}: ${s} ${o}</h3>\n </div>\n `;if("TodoWrite"===n&&r.todos){const e=`\n <div class="todo-checklist">\n ${r.todos.map(t=>{const e=this.getTodoStatusIcon(t.status),n=this.getTodoPriorityIcon(t.priority);return`\n <div class="todo-item todo-${t.status||"pending"}">\n <span class="todo-status">${e}</span>\n <span class="todo-content">${t.content||"No content"}</span>\n <span class="todo-priority priority-${t.priority||"medium"}">${n}</span>\n </div>\n `}).join("")}\n </div>\n `,n={toolCall:t,preEvent:i,postEvent:a},s=this.createCollapsibleJsonSection(n);this.dataContainer&&(this.dataContainer.innerHTML=g+e+s),this.initializeJsonToggle()}else if("Grep"===n||"Search"===n||r&&r.pattern&&!r.file_path){const e=r.pattern||"No pattern specified",o=r.path||r.directory||".",l=r.type||r.glob||"all files";let d="";t.result_summary&&(d="string"==typeof t.result_summary?t.result_summary:t.result_summary.output_preview?t.result_summary.output_preview:JSON.stringify(t.result_summary,null,2));const p=`\n <div class="structured-view-section">\n <div class="tool-call-details">\n <div class="tool-call-info ${m}">\n <div class="structured-field">\n <strong>Tool Name:</strong> ${n}\n </div>\n <div class="structured-field">\n <strong>Agent:</strong> ${s}\n </div>\n <div class="structured-field">\n <strong>Status:</strong> ${u} ${h}\n </div>\n <div class="structured-field">\n <strong>Search Pattern:</strong> <code>${e}</code>\n </div>\n <div class="structured-field">\n <strong>Search Path:</strong> ${o}\n </div>\n <div class="structured-field">\n <strong>File Type:</strong> ${l}\n </div>\n <div class="structured-field">\n <strong>Started:</strong> ${new Date(t.timestamp).toLocaleString()}\n </div>\n ${c&&"-"!==c?`\n <div class="structured-field">\n <strong>Duration:</strong> ${c}\n </div>\n `:""}\n </div>\n\n <div class="search-view-action" style="margin-top: 20px;">\n <button class="btn-view-search" data-search-params='${JSON.stringify(r)}' data-search-results='${JSON.stringify(d).replace(/'/g,"'")}' onclick="window.showSearchViewerModal(JSON.parse(this.getAttribute('data-search-params')), JSON.parse(this.getAttribute('data-search-results')))">\n 🔍 View Search Details\n </button>\n </div>\n\n ${this.createToolResultFromToolCall(t)}\n </div>\n </div>\n `,v={toolCall:t,preEvent:i,postEvent:a},f=this.createCollapsibleJsonSection(v);this.dataContainer&&(this.dataContainer.innerHTML=g+p+f),this.initializeJsonToggle()}else{const e=`\n <div class="structured-view-section">\n <div class="tool-call-details">\n <div class="tool-call-info ${m}">\n <div class="structured-field">\n <strong>Tool Name:</strong> ${n}\n </div>\n <div class="structured-field">\n <strong>Agent:</strong> ${s}\n </div>\n <div class="structured-field">\n <strong>Status:</strong> ${u} ${h}\n </div>\n <div class="structured-field">\n <strong>Target:</strong> ${l}\n </div>\n <div class="structured-field">\n <strong>Started:</strong> ${new Date(t.timestamp).toLocaleString()}\n </div>\n ${c&&"-"!==c?`\n <div class="structured-field">\n <strong>Duration:</strong> ${c}\n </div>\n `:""}\n ${t.session_id?`\n <div class="structured-field">\n <strong>Session ID:</strong> ${t.session_id}\n </div>\n `:""}\n </div>\n\n ${this.createToolResultFromToolCall(t)}\n </div>\n </div>\n `,o={toolCall:t,preEvent:i,postEvent:a},r=this.createCollapsibleJsonSection(o);this.dataContainer&&(this.dataContainer.innerHTML=g+e+r),this.initializeJsonToggle()}}showFileOperations(t,e){if(!t||!e)return void this.showEmptyState();const n=e.split("/").pop()||e,s=t.operations||[],o=s[s.length-1],i=`\n <div class="contextual-header">\n <h3 class="contextual-header-text">File: ${n} ${o?this.formatTimestamp(o.timestamp):""}</h3>\n </div>\n `,a=`\n <div class="structured-view-section">\n <div class="file-details">\n <div class="file-path-display">\n <strong>Full Path:</strong> ${this.createClickableFilePath(e)}\n <div id="git-track-status-${e.replace(/[^a-zA-Z0-9]/g,"-")}" class="git-track-status" style="margin-top: 8px;">\n \x3c!-- Git tracking status will be populated here --\x3e\n </div>\n </div>\n <div class="operations-list">\n ${s.map(t=>`\n <div class="operation-item">\n <div class="operation-header">\n <span class="operation-icon">${this.getOperationIcon(t.operation)}</span>\n <span class="operation-type">${t.operation}</span>\n <span class="operation-timestamp">${new Date(t.timestamp).toLocaleString()}</span>\n ${this.isReadOnlyOperation(t.operation)?`\n \x3c!-- Read-only operation: show only file viewer --\x3e\n <span class="file-viewer-icon"\n onclick="showFileViewerModal('${e}')"\n title="View file contents with syntax highlighting"\n style="margin-left: 8px; cursor: pointer; font-size: 16px;">\n 👁️\n </span>\n `:`\n \x3c!-- Edit operation: show both file viewer and git diff --\x3e\n <span class="file-viewer-icon"\n onclick="showFileViewerModal('${e}')"\n title="View file contents with syntax highlighting"\n style="margin-left: 8px; cursor: pointer; font-size: 16px;">\n 👁️\n </span>\n <span class="git-diff-icon"\n onclick="showGitDiffModal('${e}', '${t.timestamp}')"\n title="View git diff for this file operation"\n style="margin-left: 8px; cursor: pointer; font-size: 16px; display: none;"\n data-file-path="${e}"\n data-operation-timestamp="${t.timestamp}">\n 📋\n </span>\n `}\n </div>\n <div class="operation-details">\n <strong>Agent:</strong> ${t.agent}<br>\n <strong>Session:</strong> ${t.sessionId?t.sessionId.substring(0,8)+"...":"Unknown"}\n ${t.details?this.formatOperationDetails(t.details):""}\n </div>\n </div>\n `).join("")}\n </div>\n </div>\n </div>\n `;this.checkAndShowTrackControl(e),this.checkAndShowGitDiffIcons(e);const r=this.createCollapsibleJsonSection(t);this.dataContainer&&(this.dataContainer.innerHTML=i+a+r),this.initializeJsonToggle()}showErrorMessage(t,e){const n=`\n <div class="module-error">\n <div class="error-header">\n <h3>❌ ${t}</h3>\n </div>\n <div class="error-message">\n <p>${e}</p>\n </div>\n </div>\n `,s={title:t,message:e},o=this.createCollapsibleJsonSection(s);this.dataContainer&&(this.dataContainer.innerHTML=n+o),this.initializeJsonToggle()}showAgentEvent(t,e){this.showAgentSpecificDetails(t,e)}showAgentSpecificDetails(t,e){if(!t)return void this.showEmptyState();const n=window.dashboard?.agentInference,s=window.dashboard?.eventViewer;if(!n||!s)return console.warn("AgentInference or EventViewer not available, falling back to single event view"),void this.showEventDetails(t);const o=n.getInferredAgentForEvent(t),i=o?.agentName||this.extractAgent(t)||"Unknown",a=s.events||[],r=this.getAgentSpecificEvents(a,i,n);console.log(`Showing details for agent: ${i}, found ${r.length} related events`);const l=this.extractAgentSpecificData(i,r);this.renderAgentSpecificView(i,l,t)}getAgentSpecificEvents(t,e,n){return t.filter(t=>{const s=n.getInferredAgentForEvent(t);return(s?.agentName||this.extractAgent(t)||"Unknown").toLowerCase()===e.toLowerCase()})}extractAgentSpecificData(t,e){const n={agentName:t,totalEvents:e.length,prompt:null,todos:[],toolsCalled:[],sessions:new Set,firstSeen:null,lastSeen:null,eventTypes:new Set};return e.forEach(e=>{const s=e.data||{},o=new Date(e.timestamp);(!n.firstSeen||o<n.firstSeen)&&(n.firstSeen=o),(!n.lastSeen||o>n.lastSeen)&&(n.lastSeen=o),(e.session_id||s.session_id)&&n.sessions.add(e.session_id||s.session_id);const i=e.hook_event_name||e.type||"unknown";if(n.eventTypes.add(i),"hook"===e.type&&"Task"===s.tool_name&&s.tool_parameters){const e=s.tool_parameters;e.prompt&&!n.prompt&&(n.prompt=e.prompt),e.description&&!n.description&&(n.description=e.description),e.subagent_type===t&&e.prompt&&(n.prompt=e.prompt)}if(!s.prompt||s.agent_type!==t&&s.subagent_type!==t||(n.prompt=s.prompt),"todo"===e.type||"hook"===e.type&&"TodoWrite"===s.tool_name){const t=s.todos||s.tool_parameters?.todos;t&&Array.isArray(t)&&t.forEach(t=>{const e=n.todos.findIndex(e=>e.id===t.id||e.content===t.content);e>=0?n.todos[e]={...n.todos[e],...t,timestamp:o}:n.todos.push({...t,timestamp:o})})}if("hook"===e.type&&s.tool_name){const t=e.subtype||s.event_type,i=this.generateToolCallId(s.tool_name,s.tool_parameters,o);"pre_tool"===t?(n._preToolEvents||(n._preToolEvents=new Map),n._preToolEvents.set(i,{toolName:s.tool_name,timestamp:o,target:this.extractToolTarget(s.tool_name,s.tool_parameters,null),parameters:s.tool_parameters})):"post_tool"===t&&(n._postToolEvents||(n._postToolEvents=new Map),n._postToolEvents.set(i,{toolName:s.tool_name,timestamp:o,success:s.success,duration:s.duration_ms,resultSummary:s.result_summary,exitCode:s.exit_code}))}}),n.todos.sort((t,e)=>(e.timestamp||0)-(t.timestamp||0)),n.toolsCalled=this.consolidateToolCalls(n._preToolEvents,n._postToolEvents),delete n._preToolEvents,delete n._postToolEvents,n.toolsCalled.sort((t,e)=>e.timestamp-t.timestamp),n}generateToolCallId(t,e,n){const s=Math.floor(n.getTime()/5e3);let o="";if(e){const t=[];e.file_path&&t.push(e.file_path),e.command&&t.push(e.command.substring(0,50)),e.pattern&&t.push(e.pattern),e.subagent_type&&t.push(e.subagent_type),e.notebook_path&&t.push(e.notebook_path),e.url&&t.push(e.url),e.prompt&&t.push(e.prompt.substring(0,30)),o=t.join("|")}return o||(o="default"),`${t}:${s}:${o}`}consolidateToolCalls(t,e){const n=[],s=new Set;t||(t=new Map),e||(e=new Map);for(const[o,i]of t){if(s.has(o))continue;const t=e.get(o),a={toolName:i.toolName,timestamp:i.timestamp,target:i.target,parameters:i.parameters,status:this.determineToolCallStatus(i,t),statusIcon:this.getToolCallStatusIcon(i,t),phase:t?"completed":"running"};t&&(a.success=t.success,a.duration=t.duration,a.resultSummary=t.resultSummary,a.exitCode=t.exitCode,a.completedAt=t.timestamp),n.push(a),s.add(o)}for(const[o,i]of e){if(s.has(o))continue;const t={toolName:i.toolName,timestamp:i.timestamp,target:"Unknown target",parameters:null,status:this.determineToolCallStatus(null,i),statusIcon:this.getToolCallStatusIcon(null,i),phase:"completed",success:i.success,duration:i.duration,resultSummary:i.resultSummary,exitCode:i.exitCode,completedAt:i.timestamp};n.push(t),s.add(o)}return n}determineToolCallStatus(t,e){return e?!0===e.success?"Success":!1===e.success?"Failed":0===e.exitCode?"Completed":2===e.exitCode?"Blocked":void 0!==e.exitCode&&0!==e.exitCode?"Error":"Completed":"Running..."}getToolCallStatusIcon(t,e){return e?!0===e.success?"✅":!1===e.success?"❌":0===e.exitCode?"✅":2===e.exitCode?"⚠️":void 0!==e.exitCode&&0!==e.exitCode?"❌":"✅":"⏳"}estimateTokenCount(t){if(!t||"string"!=typeof t)return 0;const e=t.trim().split(/\s+/).length,n=Math.ceil(t.length/4);return Math.max(1.3*e,n)}trimPromptWhitespace(t){return t&&"string"==typeof t?t=(t=(t=t.trim()).replace(/\n\s*\n\s*\n+/g,"\n\n")).split("\n").map(t=>t.replace(/\s+$/,"")).join("\n"):""}renderAgentSpecificView(t,e,n){const s=this.formatTimestamp(n.timestamp),o=`\n <div class="contextual-header">\n <h3 class="contextual-header-text">🤖 ${t} Agent Details ${s}</h3>\n </div>\n `;let i=`\n <div class="agent-overview-section">\n <div class="structured-data">\n ${this.createProperty("Agent Name",t)}\n ${this.createProperty("Total Events",e.totalEvents)}\n ${this.createProperty("Active Sessions",e.sessions.size)}\n ${this.createProperty("Event Types",Array.from(e.eventTypes).join(", "))}\n ${e.firstSeen?this.createProperty("First Seen",e.firstSeen.toLocaleString()):""}\n ${e.lastSeen?this.createProperty("Last Seen",e.lastSeen.toLocaleString()):""}\n </div>\n </div>\n `;if(e.prompt){const t=this.trimPromptWhitespace(e.prompt);i+=`\n <div class="agent-prompt-section">\n <div class="contextual-header">\n <h3 class="contextual-header-text">📝 Agent Task Prompt</h3>\n <div class="prompt-stats" style="font-size: 11px; color: #64748b; margin-top: 4px;">\n ~${Math.round(this.estimateTokenCount(t))} tokens • ${t.trim().split(/\s+/).length} words • ${t.length} characters\n </div>\n </div>\n <div class="structured-data">\n <div class="agent-prompt" style="white-space: pre-wrap; max-height: 300px; overflow-y: auto; padding: 10px; background: #f8fafc; border-radius: 6px; font-family: monospace; font-size: 12px; line-height: 1.4; border: 1px solid #e2e8f0;">\n ${this.escapeHtml(t)}\n </div>\n </div>\n </div>\n `}e.todos.length>0&&(i+=`\n <div class="agent-todos-section">\n <div class="contextual-header">\n <h3 class="contextual-header-text">✅ Agent Todo List (${e.todos.length} items)</h3>\n </div>\n <div class="todo-checklist">\n ${e.todos.map(t=>`\n <div class="todo-item todo-${t.status||"pending"}">\n <span class="todo-status">${this.getTodoStatusIcon(t.status)}</span>\n <span class="todo-content">${t.content||"No content"}</span>\n <span class="todo-priority priority-${t.priority||"medium"}">${this.getTodoPriorityIcon(t.priority)}</span>\n ${t.timestamp?`<span class="todo-timestamp">${new Date(t.timestamp).toLocaleTimeString()}</span>`:""}\n </div>\n `).join("")}\n </div>\n </div>\n `),e.toolsCalled.length>0&&(i+=`\n <div class="agent-tools-section">\n <div class="contextual-header">\n <h3 class="contextual-header-text">🔧 Tools Called by Agent (${e.toolsCalled.length} calls)</h3>\n </div>\n <div class="tools-list">\n ${e.toolsCalled.map(e=>{let n="";return"✅"===e.statusIcon?n="status-success":"❌"===e.statusIcon?n="status-failed":"⚠️"===e.statusIcon?n="status-blocked":"⏳"===e.statusIcon&&(n="status-running"),`\n <div class="tool-call-item">\n <div class="tool-call-header">\n <div style="display: flex; align-items: center; gap: 12px; flex: 1;">\n <span class="tool-name">🔧 ${e.toolName}</span>\n <span class="tool-agent">${t}</span>\n <span class="tool-status-indicator ${n}">${e.statusIcon} ${e.status}</span>\n </div>\n <span class="tool-timestamp" style="margin-left: auto;">${e.timestamp.toLocaleTimeString()}</span>\n </div>\n <div class="tool-call-details">\n ${e.target?`<span class="tool-target">Target: ${e.target}</span>`:""}\n ${e.duration?`<span class="tool-duration">Duration: ${e.duration}ms</span>`:""}\n ${e.completedAt&&e.completedAt!==e.timestamp?`<span class="tool-completed">Completed: ${e.completedAt.toLocaleTimeString()}</span>`:""}\n </div>\n </div>\n `}).join("")}\n </div>\n </div>\n `);const a={agentName:t,agentData:e,originalEvent:n},r=this.createCollapsibleJsonSection(a);this.dataContainer&&(this.dataContainer.innerHTML=o+i+r),this.initializeJsonToggle()}createToolResultFromToolCall(t){if(!t.result_summary)return"";const e={event_type:"post_tool",result_summary:t.result_summary,success:t.success,exit_code:t.exit_code},n=this.createInlineToolResultContent(e,{subtype:"post_tool"});return n.trim()?`\n <div class="tool-result-inline">\n <div class="structured-data">\n ${n}\n </div>\n </div>\n `:""}extractToolTarget(t,e,n){const s=e||n||{};switch(t?.toLowerCase()){case"write":case"read":case"edit":case"multiedit":return s.file_path||"Unknown file";case"bash":return s.command?`${s.command.substring(0,50)}${s.command.length>50?"...":""}`:"Unknown command";case"grep":return s.pattern?`Pattern: ${s.pattern}`:"Unknown pattern";case"glob":return s.pattern?`Pattern: ${s.pattern}`:"Unknown glob";case"todowrite":return`${s.todos?.length||0} todos`;case"task":return s.subagent_type||s.agent_type||"Subagent delegation";default:return s.file_path?s.file_path:s.pattern?`Pattern: ${s.pattern}`:s.command?`Command: ${s.command.substring(0,30)}...`:s.path?s.path:"Unknown target"}}getOperationIcon(t){return{read:"👁️",write:"✏️",edit:"📝",multiedit:"📝",create:"🆕",delete:"🗑️",move:"📦",copy:"📋"}[t?.toLowerCase()]||"📄"}getCurrentEvent(){return this.currentEvent}async checkAndShowTrackControl(t){if(t)try{const e=window.socket||window.dashboard?.socketClient?.socket;if(!e)return void console.warn("No socket connection available for git tracking check");let n=window.dashboard?.currentWorkingDir;if(!n||"Unknown"===n||""===n.trim()){const t=document.getElementById("footer-working-dir");n=t?.textContent?.trim()&&"Unknown"!==t.textContent.trim()?t.textContent.trim():".",console.log("[MODULE-VIEWER-DEBUG] Working directory fallback used:",n)}const s=new Promise((n,s)=>{const o=s=>{s.file_path===t&&(e.off("file_tracked_response",o),n(s))};e.on("file_tracked_response",o),setTimeout(()=>{e.off("file_tracked_response",o),s(new Error("Request timeout"))},5e3)});e.emit("check_file_tracked",{file_path:t,working_dir:n});const o=await s;this.displayTrackingStatus(t,o)}catch(e){console.error("Error checking file tracking status:",e),this.displayTrackingStatus(t,{success:!1,error:e.message,file_path:t})}}displayTrackingStatus(t,e){const n=`git-track-status-${t.replace(/[^a-zA-Z0-9]/g,"-")}`,s=document.getElementById(n);s&&(e.success&&!1===e.is_tracked?s.innerHTML=`\n <div class="untracked-file-notice">\n <span class="untracked-icon">⚠️</span>\n <span class="untracked-text">This file is not tracked by git</span>\n <button class="track-file-button"\n onclick="window.moduleViewer.trackFile('${t}')"\n title="Add this file to git tracking">\n <span class="git-icon">📁</span> Track File\n </button>\n </div>\n `:e.success&&!0===e.is_tracked?s.innerHTML='\n <div class="tracked-file-notice">\n <span class="tracked-icon">✅</span>\n <span class="tracked-text">This file is tracked by git</span>\n </div>\n ':e.success||(s.innerHTML=`\n <div class="tracking-error-notice">\n <span class="error-icon">❌</span>\n <span class="error-text">Could not check git status: ${e.error||"Unknown error"}</span>\n </div>\n `))}async trackFile(t){if(t)try{const e=window.socket||window.dashboard?.socketClient?.socket;if(!e)return void console.warn("No socket connection available for git add");let n=window.dashboard?.currentWorkingDir;if(!n||"Unknown"===n||""===n.trim()){const t=document.getElementById("footer-working-dir");n=t?.textContent?.trim()&&"Unknown"!==t.textContent.trim()?t.textContent.trim():".",console.log("[MODULE-VIEWER-DEBUG] Working directory fallback used:",n)}const s=`git-track-status-${t.replace(/[^a-zA-Z0-9]/g,"-")}`,o=document.getElementById(s);o&&(o.innerHTML='\n <div class="tracking-file-notice">\n <span class="loading-icon">⏳</span>\n <span class="loading-text">Adding file to git tracking...</span>\n </div>\n ');const i=new Promise((n,s)=>{const o=s=>{s.file_path===t&&(e.off("git_add_response",o),n(s))};e.on("git_add_response",o),setTimeout(()=>{e.off("git_add_response",o),s(new Error("Request timeout"))},1e4)});e.emit("git_add_file",{file_path:t,working_dir:n}),console.log("📁 Git add request sent:",{filePath:t,workingDir:n});const a=await i;console.log("📦 Git add result:",a),a.success?(o&&(o.innerHTML='\n <div class="tracked-file-notice">\n <span class="tracked-icon">✅</span>\n <span class="tracked-text">File successfully added to git tracking</span>\n </div>\n '),this.showNotification("File tracked successfully","success")):(o&&(o.innerHTML=`\n <div class="tracking-error-notice">\n <span class="error-icon">❌</span>\n <span class="error-text">Failed to track file: ${a.error||"Unknown error"}</span>\n <button class="track-file-button"\n onclick="window.moduleViewer.trackFile('${t}')"\n title="Try again">\n <span class="git-icon">📁</span> Retry\n </button>\n </div>\n `),this.showNotification(`Failed to track file: ${a.error}`,"error"))}catch(e){console.error("❌ Failed to track file:",e);const n=`git-track-status-${t.replace(/[^a-zA-Z0-9]/g,"-")}`,s=document.getElementById(n);s&&(s.innerHTML=`\n <div class="tracking-error-notice">\n <span class="error-icon">❌</span>\n <span class="error-text">Error: ${e.message}</span>\n <button class="track-file-button"\n onclick="window.moduleViewer.trackFile('${t}')"\n title="Try again">\n <span class="git-icon">📁</span> Retry\n </button>\n </div>\n `),this.showNotification(`Error tracking file: ${e.message}`,"error")}}async checkAndShowGitDiffIcons(t){if(t){console.debug("[GIT-DIFF-ICONS] Checking git diff icons for file:",t);try{const e=window.socket||window.dashboard?.socketClient?.socket;if(!e)return void console.warn("[GIT-DIFF-ICONS] No socket connection available for git status check");console.debug("[GIT-DIFF-ICONS] Socket connection available, proceeding");let n=window.dashboard?.currentWorkingDir;if(n&&"Unknown"!==n&&""!==n.trim())console.debug("[GIT-DIFF-ICONS] Using working directory:",n);else{const t=document.getElementById("footer-working-dir");n=t?.textContent?.trim()&&"Unknown"!==t.textContent.trim()?t.textContent.trim():".",console.log("[GIT-DIFF-ICONS] Working directory fallback used:",n)}const s=new Promise((n,s)=>{const o=s=>{console.debug("[GIT-DIFF-ICONS] Received git status response:",s),s.file_path===t?(e.off("git_status_response",o),n(s)):console.debug("[GIT-DIFF-ICONS] Response for different file, ignoring:",s.file_path)};e.on("git_status_response",o),setTimeout(()=>{e.off("git_status_response",o),console.warn("[GIT-DIFF-ICONS] Timeout waiting for git status response"),s(new Error("Request timeout"))},3e3)});console.debug("[GIT-DIFF-ICONS] Sending check_git_status event"),e.emit("check_git_status",{file_path:t,working_dir:n});const o=await s;console.debug("[GIT-DIFF-ICONS] Git status check result:",o),o.success?(console.debug("[GIT-DIFF-ICONS] Git status check successful, showing icons for:",t),this.showGitDiffIconsForFile(t)):console.debug("[GIT-DIFF-ICONS] Git status check failed, icons will remain hidden:",o.error)}catch(e){console.warn("[GIT-DIFF-ICONS] Git status check failed, hiding git diff icons:",e.message)}}else console.debug("[GIT-DIFF-ICONS] No filePath provided, skipping git diff icon check")}showGitDiffIconsForFile(t){console.debug("[GIT-DIFF-ICONS] Showing git diff icons for file:",t);const e=document.querySelectorAll(`[data-file-path="${t}"]`);console.debug("[GIT-DIFF-ICONS] Found",e.length,"elements with matching file path");let n=0;e.forEach((t,e)=>{console.debug("[GIT-DIFF-ICONS] Processing element",e,":",t),console.debug("[GIT-DIFF-ICONS] Element classes:",t.classList.toString()),t.classList.contains("git-diff-icon")?(console.debug("[GIT-DIFF-ICONS] Setting display to inline for git-diff-icon"),t.style.display="inline",n++):console.debug("[GIT-DIFF-ICONS] Element is not a git-diff-icon, skipping")}),console.debug("[GIT-DIFF-ICONS] Showed",n,"git diff icons for file:",t)}showNotification(t,e="info"){const n=document.createElement("div");n.className=`notification notification-${e}`,n.innerHTML=`\n <span class="notification-icon">${"success"===e?"✅":"error"===e?"❌":"ℹ️"}</span>\n <span class="notification-message">${t}</span>\n `,n.style.cssText=`\n position: fixed;\n top: 20px;\n right: 20px;\n background: ${"success"===e?"#d4edda":"error"===e?"#f8d7da":"#d1ecf1"};\n color: ${"success"===e?"#155724":"error"===e?"#721c24":"#0c5460"};\n border: 1px solid ${"success"===e?"#c3e6cb":"error"===e?"#f5c6cb":"#bee5eb"};\n border-radius: 6px;\n padding: 12px 16px;\n font-size: 14px;\n font-weight: 500;\n z-index: 2000;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n display: flex;\n align-items: center;\n gap: 8px;\n max-width: 400px;\n animation: slideIn 0.3s ease-out;\n `;const s=document.createElement("style");s.textContent="\n @keyframes slideIn {\n from { transform: translateX(100%); opacity: 0; }\n to { transform: translateX(0); opacity: 1; }\n }\n @keyframes slideOut {\n from { transform: translateX(0); opacity: 1; }\n to { transform: translateX(100%); opacity: 0; }\n }\n ",document.head.appendChild(s),document.body.appendChild(n),setTimeout(()=>{n.style.animation="slideOut 0.3s ease-in",setTimeout(()=>{n.parentNode&&n.parentNode.removeChild(n),s.parentNode&&s.parentNode.removeChild(s)},300)},5e3)}showAgentInstance(t){if(!t)return void this.showEmptyState();const e={type:"pm_delegation",subtype:t.agentName,agent_type:t.agentName,timestamp:t.timestamp,session_id:t.sessionId,metadata:{delegation_type:"explicit",event_count:t.agentEvents.length,pm_call:t.pmCall||null,agent_events:t.agentEvents}};console.log("Showing PM delegation details:",t),this.showAgentSpecificDetails(e,0)}showImpliedAgent(t){if(!t)return void this.showEmptyState();const e={type:"implied_delegation",subtype:t.agentName,agent_type:t.agentName,timestamp:t.timestamp,session_id:t.sessionId,metadata:{delegation_type:"implied",event_count:t.eventCount,pm_call:null,note:"No explicit PM call found - inferred from agent activity"}};console.log("Showing implied agent details:",t),this.showAgentSpecificDetails(e,0)}}window.ModuleViewer=e,window.enableToolResultDebugging=function(){window.DEBUG_TOOL_RESULTS=!0,console.log("🔧 Tool result debugging enabled. Click on tool events to see debug info.")},window.disableToolResultDebugging=function(){window.DEBUG_TOOL_RESULTS=!1,console.log("🔧 Tool result debugging disabled.")};export{e as M};
|
|
1
|
+
import{U as t}from"../chunks/unified-data-viewer.B7wmm2bD.js";class e{constructor(e){this.container=document.getElementById(e),this.dataContainer=null,this.jsonContainer=null,this.currentEvent=null,this.eventsByClass=new Map,this.globalJsonExpanded="true"===localStorage.getItem("dashboard-json-expanded"),this.keyboardListenerAdded=!1,this.unifiedViewer=new t("module-data-content"),this.init()}init(){this.setupContainers(),this.setupEventHandlers(),this.showEmptyState()}setupContainers(){this.dataContainer=document.getElementById("module-data-content"),this.jsonContainer=null,this.dataContainer||console.error("Module viewer data container not found")}setupEventHandlers(){document.addEventListener("eventSelected",t=>{this.showEventDetails(t.detail.event)}),document.addEventListener("eventSelectionCleared",()=>{this.showEmptyState()}),document.addEventListener("socketEventUpdate",t=>{this.updateEventsByClass(t.detail.events)})}showEmptyState(){this.dataContainer&&(this.dataContainer.innerHTML='\n <div class="module-empty">\n <p>Click on an event to view structured data</p>\n <p class="module-hint">Data is organized by event type</p>\n </div>\n '),this.currentEvent=null}showEventDetails(t){if(this.currentEvent=t,!this.unifiedViewer)return console.warn("ModuleViewer: UnifiedDataViewer not available"),this.renderStructuredData(t),void this.renderJsonData(t);this.unifiedViewer.display(t,"event")}renderStructuredData(t){if(!this.dataContainer)return;const e=this.createContextualHeader(t),n=this.createEventStructuredView(t),s=this.createCollapsibleJsonSection(t);this.dataContainer.innerHTML=e+n+s,this.initializeJsonToggle()}renderJsonData(t){}ingest(t){Array.isArray(t)?t.length>0?this.showEventDetails(t[0]):this.showEmptyState():t&&"object"==typeof t?this.showEventDetails(t):this.showEmptyState()}updateEventsByClass(t){this.eventsByClass.clear(),t.forEach(t=>{const e=this.getEventClass(t);this.eventsByClass.has(e)||this.eventsByClass.set(e,[]),this.eventsByClass.get(e).push(t)})}getEventClass(t){if(!t.type)return"unknown";switch(t.type){case"session":return"Session Management";case"claude":return"Claude Interactions";case"agent":return"Agent Operations";case"hook":return"Hook System";case"todo":return"Task Management";case"memory":return"Memory Operations";case"log":return"System Logs";case"connection":return"Connection Events";default:return"Other Events"}}createContextualHeader(t){const e=this.formatTimestamp(t.timestamp),n=t.data||{};let s="";switch(t.type){case"hook":const o=this.extractToolName(n),i=this.extractAgent(t)||"Unknown";if(o)s=`${o}: ${i} ${e}`;else{s=`${this.getHookDisplayName(t,n)}: ${i} ${e}`}break;case"agent":s=`Agent: ${n.agent_type||n.name||"Unknown"} ${e}`;break;case"todo":s=`TodoWrite: ${this.extractAgent(t)||"PM"} ${e}`;break;case"memory":s=`Memory: ${n.operation||"Unknown"} ${e}`;break;case"session":case"claude":case"log":case"connection":s=`Event: ${t.type}.${t.subtype||"default"} ${e}`;break;default:const a=this.extractFileName(n);if(a)s=`File: ${a} ${e}`;else{s=`Event: ${t.type||"Unknown"}.${t.subtype||"default"} ${e}`}}return`\n <div class="contextual-header">\n <h3 class="contextual-header-text">${s}</h3>\n </div>\n `}createEventStructuredView(t){const e=this.getEventClass(t),n=(this.eventsByClass.get(e)||[]).length;let s=`\n <div class="structured-view-section">\n ${this.createEventDetailCard(t.type,t,n)}\n </div>\n `;switch(t.type){case"agent":s+=this.createAgentStructuredView(t);break;case"hook":"Task"===t.data?.tool_name&&t.data?.tool_parameters?.subagent_type?s+=this.createAgentStructuredView(t):s+=this.createHookStructuredView(t);break;case"todo":s+=this.createTodoStructuredView(t);break;case"memory":s+=this.createMemoryStructuredView(t);break;case"claude":s+=this.createClaudeStructuredView(t);break;case"session":s+=this.createSessionStructuredView(t);break;default:s+=this.createGenericStructuredView(t)}return s}createEventDetailCard(t,e,n){const s=new Date(e.timestamp).toLocaleString();return`\n <div class="event-detail-card">\n <div class="event-detail-header">\n <div class="event-detail-title">\n ${this.getEventIcon(t)} ${t||"Unknown"}.${e.subtype||"default"}\n </div>\n <div class="event-detail-time">${s}</div>\n </div>\n <div class="event-detail-content">\n ${this.createProperty("Event ID",e.id||"N/A")}\n ${this.createProperty("Type",`${t}.${e.subtype||"default"}`)}\n ${this.createProperty("Class Events",n)}\n ${e.data&&e.data.session_id?this.createProperty("Session",e.data.session_id):""}\n </div>\n </div>\n `}createAgentStructuredView(t){const e=t.data||{};if("hook"===t.type&&"Task"===e.tool_name&&e.tool_parameters?.subagent_type){const n=e.tool_parameters;return`\n <div class="structured-view-section">\n <div class="structured-data">\n ${this.createProperty("Agent Type",n.subagent_type)}\n ${this.createProperty("Task Type","Subagent Delegation")}\n ${this.createProperty("Phase",t.subtype||"pre_tool")}\n ${n.description?this.createProperty("Description",n.description):""}\n ${n.prompt?this.createProperty("Prompt Preview",this.truncateText(n.prompt,200)):""}\n ${e.session_id?this.createProperty("Session ID",e.session_id):""}\n ${e.working_directory?this.createProperty("Working Directory",e.working_directory):""}\n </div>\n ${n.prompt?`\n <div class="prompt-section">\n <div class="contextual-header">\n <h3 class="contextual-header-text">📝 Task Prompt</h3>\n </div>\n <div class="structured-data">\n <div class="task-prompt" style="white-space: pre-wrap; max-height: 300px; overflow-y: auto; padding: 10px; background: #f8fafc; border-radius: 6px; font-family: monospace; font-size: 12px; line-height: 1.4;">\n ${n.prompt}\n </div>\n </div>\n </div>\n `:""}\n </div>\n `}return`\n <div class="structured-view-section">\n <div class="structured-data">\n ${this.createProperty("Agent Type",e.agent_type||e.subagent_type||"Unknown")}\n ${this.createProperty("Name",e.name||"N/A")}\n ${this.createProperty("Phase",t.subtype||"N/A")}\n ${e.config?this.createProperty("Config","object"==typeof e.config?Object.keys(e.config).join(", "):String(e.config)):""}\n ${e.capabilities?this.createProperty("Capabilities",e.capabilities.join(", ")):""}\n ${e.result?this.createProperty("Result","object"==typeof e.result?"[Object]":String(e.result)):""}\n </div>\n </div>\n `}createHookStructuredView(t){const e=t.data||{},n=this.extractFilePathFromHook(e),s=this.extractToolInfoFromHook(e),o=this.createInlineToolResultContent(e,t);return`\n <div class="structured-view-section">\n <div class="structured-data">\n ${this.createProperty("Hook Name",this.getHookDisplayName(t,e))}\n ${this.createProperty("Event Type",e.event_type||t.subtype||"N/A")}\n ${n?this.createProperty("File Path",n):""}\n ${s.tool_name?this.createProperty("Tool",s.tool_name):""}\n ${s.operation_type?this.createProperty("Operation",s.operation_type):""}\n ${e.session_id?this.createProperty("Session ID",e.session_id):""}\n ${e.working_directory?this.createProperty("Working Directory",e.working_directory):""}\n ${e.duration_ms?this.createProperty("Duration",`${e.duration_ms}ms`):""}\n ${o}\n </div>\n </div>\n `}createInlineToolResultContent(t,e=null){const n=t.result_summary,s=e?.subtype||t.event_type||t.phase,o="post_tool"===s||s?.includes("post");if(window.DEBUG_TOOL_RESULTS&&console.log("🔧 createInlineToolResultContent debug:",{hasResultSummary:!!n,eventPhase:s,isPostTool:o,eventSubtype:e?.subtype,dataEventType:t.event_type,dataPhase:t.phase,toolName:t.tool_name,resultSummaryKeys:n?Object.keys(n):[]}),!n)return"";if("pre_tool"===s||s?.includes("pre")&&!s?.includes("post"))return"";let i="";if(n.has_output&&n.output_preview&&(i+=`\n ${this.createProperty("Output",this.truncateText(n.output_preview,200))}\n ${n.output_lines?this.createProperty("Output Lines",n.output_lines):""}\n `),n.has_error&&n.error_preview&&(i+=`\n ${this.createProperty("Error",this.truncateText(n.error_preview,200))}\n `),!n.has_output&&!n.has_error&&Object.keys(n).length>3){i+=Object.entries(n).filter(([t,e])=>!["has_output","has_error","exit_code"].includes(t)&&void 0!==e).map(([t,e])=>this.createProperty(this.formatFieldName(t),String(e))).join("")}return i}createToolResultSection(t,e=null){const n=t.result_summary,s=e?.subtype||t.event_type||t.phase,o="post_tool"===s||s?.includes("post");if(window.DEBUG_TOOL_RESULTS&&console.log("🔧 createToolResultSection debug:",{hasResultSummary:!!n,eventPhase:s,isPostTool:o,eventSubtype:e?.subtype,dataEventType:t.event_type,dataPhase:t.phase,toolName:t.tool_name,resultSummaryKeys:n?Object.keys(n):[]}),!n)return"";if("pre_tool"===s||s?.includes("pre")&&!s?.includes("post"))return"";let i="⏳",a="tool-running",r="Unknown";!0===t.success?(i="✅",a="tool-success",r="Success"):!1===t.success?(i="❌",a="tool-failure",r="Failed"):0===t.exit_code?(i="✅",a="tool-success",r="Completed"):2===t.exit_code?(i="⚠️",a="tool-blocked",r="Blocked"):void 0!==t.exit_code&&0!==t.exit_code&&(i="❌",a="tool-failure",r="Error");let l="";if(l+=`\n <div class="tool-result-status ${a}">\n <span class="tool-result-icon">${i}</span>\n <span class="tool-result-text">${r}</span>\n ${void 0!==t.exit_code?`<span class="tool-exit-code">Exit Code: ${t.exit_code}</span>`:""}\n </div>\n `,n.has_output&&n.output_preview&&(l+=`\n <div class="tool-result-output">\n <div class="tool-result-label">📄 Output:</div>\n <div class="tool-result-preview">\n <pre>${this.escapeHtml(n.output_preview)}</pre>\n </div>\n ${n.output_lines?`<div class="tool-result-meta">Lines: ${n.output_lines}</div>`:""}\n </div>\n `),n.has_error&&n.error_preview&&(l+=`\n <div class="tool-result-error">\n <div class="tool-result-label">⚠️ Error:</div>\n <div class="tool-result-preview error-preview">\n <pre>${this.escapeHtml(n.error_preview)}</pre>\n </div>\n </div>\n `),!n.has_output&&!n.has_error&&Object.keys(n).length>3){const t=Object.entries(n).filter(([t,e])=>!["has_output","has_error","exit_code"].includes(t)&&void 0!==e).map(([t,e])=>this.createProperty(this.formatFieldName(t),String(e))).join("");t&&(l+=`\n <div class="tool-result-other">\n <div class="tool-result-label">📊 Result Details:</div>\n <div class="structured-data">\n ${t}\n </div>\n </div>\n `)}return l.trim()?`\n <div class="tool-result-section">\n <div class="contextual-header">\n <h3 class="contextual-header-text">🔧 Tool Result</h3>\n </div>\n <div class="tool-result-content">\n ${l}\n </div>\n </div>\n `:""}isWriteOperation(t,e){if(["Write","Edit","MultiEdit","NotebookEdit"].includes(t))return!0;if(e.tool_parameters){const t=e.tool_parameters;if(t.content||t.new_string||t.edits)return!0;if(t.edit_mode&&"read"!==t.edit_mode)return!0}return!("post_tool"!==e.event_type&&"pre_tool"!==e.event_type||!t||!(t.toLowerCase().includes("write")||t.toLowerCase().includes("edit")||t.toLowerCase().includes("modify")))}isReadOnlyOperation(t){if(!t)return!0;const e=t.toLowerCase();return!!["read"].includes(e)||!["write","edit","multiedit","create","delete","move","copy"].includes(e)}createTodoStructuredView(t){const e=t.data||{};let n="";return e.todos&&Array.isArray(e.todos)&&(n+=`\n <div class="todo-checklist">\n ${e.todos.map(t=>`\n <div class="todo-item todo-${t.status||"pending"}">\n <span class="todo-status">${this.getTodoStatusIcon(t.status)}</span>\n <span class="todo-content">${t.content||"No content"}</span>\n <span class="todo-priority priority-${t.priority||"medium"}">${this.getTodoPriorityIcon(t.priority)}</span>\n </div>\n `).join("")}\n </div>\n `),n}createMemoryStructuredView(t){const e=t.data||{};return`\n <div class="structured-view-section">\n <div class="structured-data">\n ${this.createProperty("Operation",e.operation||"Unknown")}\n ${this.createProperty("Key",e.key||"N/A")}\n ${e.value?this.createProperty("Value","object"==typeof e.value?"[Object]":String(e.value)):""}\n ${e.namespace?this.createProperty("Namespace",e.namespace):""}\n ${e.metadata?this.createProperty("Metadata","object"==typeof e.metadata?"[Object]":String(e.metadata)):""}\n </div>\n </div>\n `}createClaudeStructuredView(t){const e=t.data||{};return`\n <div class="structured-view-section">\n <div class="structured-data">\n ${this.createProperty("Type",t.subtype||"N/A")}\n ${e.prompt?this.createProperty("Prompt",this.truncateText(e.prompt,200)):""}\n ${e.message?this.createProperty("Message",this.truncateText(e.message,200)):""}\n ${e.response?this.createProperty("Response",this.truncateText(e.response,200)):""}\n ${e.content?this.createProperty("Content",this.truncateText(e.content,200)):""}\n ${e.tokens?this.createProperty("Tokens",e.tokens):""}\n ${e.model?this.createProperty("Model",e.model):""}\n </div>\n </div>\n `}createSessionStructuredView(t){const e=t.data||{};return`\n <div class="structured-view-section">\n <div class="structured-data">\n ${this.createProperty("Action",t.subtype||"N/A")}\n ${this.createProperty("Session ID",e.session_id||"N/A")}\n ${e.working_directory?this.createProperty("Working Dir",e.working_directory):""}\n ${e.git_branch?this.createProperty("Git Branch",e.git_branch):""}\n ${e.agent_type?this.createProperty("Agent Type",e.agent_type):""}\n </div>\n </div>\n `}createGenericStructuredView(t){const e=t.data||{},n=Object.keys(e);return 0===n.length?"":`\n <div class="structured-view-section">\n <div class="structured-data">\n ${n.map(t=>this.createProperty(t,"object"==typeof e[t]?"[Object]":String(e[t]))).join("")}\n </div>\n </div>\n `}createCollapsibleJsonSection(t){const e="json-section-"+Math.random().toString(36).substr(2,9),n=this.formatJSON(t),s=this.globalJsonExpanded;return`\n <div class="collapsible-json-section" id="${e}">\n <div class="json-toggle-header"\n onclick="window.moduleViewer.toggleJsonSection()"\n role="button"\n tabindex="0"\n aria-expanded="${s?"true":"false"}"\n onkeydown="if(event.key==='Enter'||event.key===' '){window.moduleViewer.toggleJsonSection();event.preventDefault();}">\n <span class="json-toggle-text">Raw JSON</span>\n <span class="json-toggle-arrow">${s?"▲":"▼"}</span>\n </div>\n <div class="json-content-collapsible" style="display: ${s?"block":"none"};" aria-hidden="${!s}">\n <div class="json-display" onclick="window.moduleViewer.copyJsonToClipboard(event)">\n <pre>${n}</pre>\n </div>\n </div>\n </div>\n `}async copyJsonToClipboard(t){const e=t.currentTarget.getBoundingClientRect(),n=t.clientX-e.left,s=t.clientY-e.top;if(n>e.width-50&&s<30){const e=t.currentTarget.querySelector("pre");if(e)try{await navigator.clipboard.writeText(e.textContent),this.showNotification("JSON copied to clipboard","success")}catch(o){console.error("Failed to copy JSON:",o),this.showNotification("Failed to copy JSON","error")}t.stopPropagation()}}initializeJsonToggle(){window.moduleViewer=this,this.globalJsonExpanded&&setTimeout(()=>{this.updateAllJsonSections()},0),this.keyboardListenerAdded||(this.keyboardListenerAdded=!0,document.addEventListener("keydown",t=>{t.target.classList.contains("json-toggle-header")&&("Enter"!==t.key&&" "!==t.key||(this.toggleJsonSection(),t.preventDefault()))}))}toggleJsonSection(){this.globalJsonExpanded=!this.globalJsonExpanded,localStorage.setItem("dashboard-json-expanded",this.globalJsonExpanded.toString()),this.updateAllJsonSections(),document.dispatchEvent(new CustomEvent("jsonToggleChanged",{detail:{expanded:this.globalJsonExpanded}}))}updateAllJsonSections(){const t=document.querySelectorAll(".json-content-collapsible"),e=document.querySelectorAll(".json-toggle-arrow"),n=document.querySelectorAll(".json-toggle-header");t.forEach((t,s)=>{this.globalJsonExpanded?(t.style.display="block",t.setAttribute("aria-hidden","false"),e[s]&&(e[s].textContent="▲"),n[s]&&n[s].setAttribute("aria-expanded","true")):(t.style.display="none",t.setAttribute("aria-hidden","true"),e[s]&&(e[s].textContent="▼"),n[s]&&n[s].setAttribute("aria-expanded","false"))}),this.globalJsonExpanded&&t.length>0&&setTimeout(()=>{const e=t[0];e&&e.scrollIntoView({behavior:"smooth",block:"nearest"})},100)}createProperty(t,e){const n=this.truncateText(String(e),300);return this.isFilePathProperty(t,e)?`\n <div class="event-property">\n <span class="event-property-key">${t}:</span>\n <span class="event-property-value">\n ${this.createClickableFilePath(e)}\n </span>\n </div>\n `:`\n <div class="event-property">\n <span class="event-property-key">${t}:</span>\n <span class="event-property-value">${n}</span>\n </div>\n `}isFilePathProperty(t,e){if(["File Path","file_path","notebook_path","Full Path","Working Directory","working_directory"].some(e=>t.toLowerCase().includes(e.toLowerCase()))){const t=String(e);return t.length>0&&(t.includes("/")||t.includes("\\"))&&t.length<500}return!1}createClickableFilePath(t){const e=this.truncateText(String(t),300);return`\n <span class="clickable-file-path"\n onclick="showFileViewerModal('${t.replace(/'/g,"\\'")}')"\n title="Click to view file contents with syntax highlighting Path: ${t}">\n ${e}\n </span>\n `}getEventIcon(t){const e={session:"📱",claude:"🤖",agent:"🎯",hook:"🔗",todo:"✅",memory:"🧠",log:"📝",connection:"🔌",unknown:"❓"};return e[t]||e.unknown}getTodoStatusIcon(t){const e={completed:"✅",in_progress:"🔄",pending:"⏳",cancelled:"❌"};return e[t]||e.pending}getTodoPriorityIcon(t){const e={high:"🔴",medium:"🟡",low:"🟢"};return e[t]||e.medium}getHookDisplayName(t,e){if(e.hook_name)return e.hook_name;if(e.name)return e.name;const n=t.subtype||e.event_type,s={user_prompt:"User Prompt",pre_tool:"Tool Execution (Pre)",post_tool:"Tool Execution (Post)",notification:"Notification",stop:"Session Stop",subagent_stop:"Subagent Stop"};if(s[n])return s[n];if("string"==typeof t.type&&t.type.startsWith("hook.")){const e=t.type.replace("hook.","");if(s[e])return s[e]}return n?n.split("_").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" "):"Unknown Hook"}extractFilePathFromHook(t){return t.tool_parameters&&t.tool_parameters.file_path?t.tool_parameters.file_path:t.file_path?t.file_path:t.tool_input&&t.tool_input.file_path?t.tool_input.file_path:t.tool_parameters&&t.tool_parameters.notebook_path?t.tool_parameters.notebook_path:null}extractToolInfoFromHook(t){return{tool_name:t.tool_name||t.tool_parameters&&t.tool_parameters.tool_name,operation_type:t.operation_type||t.tool_parameters&&t.tool_parameters.operation_type}}truncateText(t,e){return!t||t.length<=e?t:t.substring(0,e)+"..."}formatOperationDetails(t){if(!t||"object"!=typeof t)return"";let e="";if(t.parameters&&t.parameters.command&&(e+=`<br><strong>Command:</strong> <code>${this.escapeHtml(t.parameters.command)}</code>`),void 0!==t.success&&(e+="<br><strong>Status:</strong> "+(t.success?"✅ Success":"❌ Failed")),void 0!==t.exit_code&&null!==t.exit_code&&(e+=`<br><strong>Exit Code:</strong> ${t.exit_code}`),void 0!==t.duration_ms&&null!==t.duration_ms){e+=`<br><strong>Duration:</strong> ${t.duration_ms>1e3?`${(t.duration_ms/1e3).toFixed(2)}s`:`${t.duration_ms}ms`}`}return t.error&&(e+=`<br><strong>Error:</strong> ${this.escapeHtml(this.truncateText(t.error,200))}`),e}escapeHtml(t){if(!t)return"";const e=document.createElement("div");return e.textContent=t,e.innerHTML}formatJSON(t){try{return JSON.stringify(t,null,2)}catch(e){return String(t)}}formatTimestamp(t){if(!t)return"Unknown time";try{return new Date(t).toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",second:"2-digit",hour12:!0})}catch(e){return"Invalid time"}}escapeHtml(t){if(!t)return"";const e=document.createElement("div");return e.textContent=t,e.innerHTML}formatFieldName(t){return t.split("_").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}extractToolName(t){if(t.tool_name)return t.tool_name;if(t.tool_parameters&&t.tool_parameters.tool_name)return t.tool_parameters.tool_name;if(t.tool_input&&t.tool_input.tool_name)return t.tool_input.tool_name;if(t.tool_parameters){if(t.tool_parameters.file_path||t.tool_parameters.notebook_path)return"FileOperation";if(t.tool_parameters.pattern)return"Search";if(t.tool_parameters.command)return"Bash";if(t.tool_parameters.todos)return"TodoWrite"}return null}extractAgent(t){if(t._agentName&&"Unknown Agent"!==t._agentName)return t._agentName;if(t._inference&&t._inference.agentName&&"Unknown"!==t._inference.agentName)return t._inference.agentName;if(t.agent)return t.agent;if(t.agent_type)return t.agent_type;if(t.agent_name)return t.agent_name;if(t.session_id&&"string"==typeof t.session_id){const e=t.session_id.split("_");if(e.length>1)return e[0].toUpperCase()}return t.todos||"TodoWrite"===t.tool_name?"PM":null}extractFileName(t){const e=this.extractFilePathFromHook(t);if(e){const t=e.split("/");return t[t.length-1]}return t.filename?t.filename:t.file?t.file:null}clear(){this.unifiedViewer&&this.unifiedViewer.clear(),this.showEmptyState()}showToolCall(t,e){if(!t)return void this.showEmptyState();const n=t.tool_name||"Unknown Tool",s=t.agent_type||"PM",o=this.formatTimestamp(t.timestamp),i=t.pre_event,a=t.post_event,r=i?.tool_parameters||{},l=i?this.extractToolTarget(n,r):"Unknown target",c=t.duration_ms?`${t.duration_ms}ms`:"-",d=void 0!==t.success?t.success:null;void 0!==t.exit_code&&t.exit_code;let p=t.result_summary||"No summary available";if("object"==typeof p&&null!==p){const t=[];void 0!==p.exit_code&&t.push(`Exit Code: ${p.exit_code}`),void 0!==p.has_output&&t.push("Has Output: "+(p.has_output?"Yes":"No")),void 0!==p.has_error&&t.push("Has Error: "+(p.has_error?"Yes":"No")),void 0!==p.output_lines&&t.push(`Output Lines: ${p.output_lines}`),p.output_preview&&t.push(`Output Preview: ${p.output_preview}`),p.error_preview&&t.push(`Error Preview: ${p.error_preview}`)}let u="⏳",h="Running...",m="tool-running";a&&(!0===d?(u="✅",h="Success",m="tool-success"):!1===d?(u="❌",h="Failed",m="tool-failure"):(u="⏳",h="Completed",m="tool-completed"));const g=`\n <div class="contextual-header">\n <h3 class="contextual-header-text">${n}: ${s} ${o}</h3>\n </div>\n `;if("TodoWrite"===n&&r.todos){const e=`\n <div class="todo-checklist">\n ${r.todos.map(t=>{const e=this.getTodoStatusIcon(t.status),n=this.getTodoPriorityIcon(t.priority);return`\n <div class="todo-item todo-${t.status||"pending"}">\n <span class="todo-status">${e}</span>\n <span class="todo-content">${t.content||"No content"}</span>\n <span class="todo-priority priority-${t.priority||"medium"}">${n}</span>\n </div>\n `}).join("")}\n </div>\n `,n={toolCall:t,preEvent:i,postEvent:a},s=this.createCollapsibleJsonSection(n);this.dataContainer&&(this.dataContainer.innerHTML=g+e+s),this.initializeJsonToggle()}else if("Grep"===n||"Search"===n||r&&r.pattern&&!r.file_path){const e=r.pattern||"No pattern specified",o=r.path||r.directory||".",l=r.type||r.glob||"all files";let d="";t.result_summary&&(d="string"==typeof t.result_summary?t.result_summary:t.result_summary.output_preview?t.result_summary.output_preview:JSON.stringify(t.result_summary,null,2));const p=`\n <div class="structured-view-section">\n <div class="tool-call-details">\n <div class="tool-call-info ${m}">\n <div class="structured-field">\n <strong>Tool Name:</strong> ${n}\n </div>\n <div class="structured-field">\n <strong>Agent:</strong> ${s}\n </div>\n <div class="structured-field">\n <strong>Status:</strong> ${u} ${h}\n </div>\n <div class="structured-field">\n <strong>Search Pattern:</strong> <code>${e}</code>\n </div>\n <div class="structured-field">\n <strong>Search Path:</strong> ${o}\n </div>\n <div class="structured-field">\n <strong>File Type:</strong> ${l}\n </div>\n <div class="structured-field">\n <strong>Started:</strong> ${new Date(t.timestamp).toLocaleString()}\n </div>\n ${c&&"-"!==c?`\n <div class="structured-field">\n <strong>Duration:</strong> ${c}\n </div>\n `:""}\n </div>\n\n <div class="search-view-action" style="margin-top: 20px;">\n <button class="btn-view-search" data-search-params='${JSON.stringify(r)}' data-search-results='${JSON.stringify(d).replace(/'/g,"'")}' onclick="window.showSearchViewerModal(JSON.parse(this.getAttribute('data-search-params')), JSON.parse(this.getAttribute('data-search-results')))">\n 🔍 View Search Details\n </button>\n </div>\n\n ${this.createToolResultFromToolCall(t)}\n </div>\n </div>\n `,v={toolCall:t,preEvent:i,postEvent:a},f=this.createCollapsibleJsonSection(v);this.dataContainer&&(this.dataContainer.innerHTML=g+p+f),this.initializeJsonToggle()}else{const e=`\n <div class="structured-view-section">\n <div class="tool-call-details">\n <div class="tool-call-info ${m}">\n <div class="structured-field">\n <strong>Tool Name:</strong> ${n}\n </div>\n <div class="structured-field">\n <strong>Agent:</strong> ${s}\n </div>\n <div class="structured-field">\n <strong>Status:</strong> ${u} ${h}\n </div>\n <div class="structured-field">\n <strong>Target:</strong> ${l}\n </div>\n <div class="structured-field">\n <strong>Started:</strong> ${new Date(t.timestamp).toLocaleString()}\n </div>\n ${c&&"-"!==c?`\n <div class="structured-field">\n <strong>Duration:</strong> ${c}\n </div>\n `:""}\n ${t.session_id?`\n <div class="structured-field">\n <strong>Session ID:</strong> ${t.session_id}\n </div>\n `:""}\n </div>\n\n ${this.createToolResultFromToolCall(t)}\n </div>\n </div>\n `,o={toolCall:t,preEvent:i,postEvent:a},r=this.createCollapsibleJsonSection(o);this.dataContainer&&(this.dataContainer.innerHTML=g+e+r),this.initializeJsonToggle()}}showFileOperations(t,e){if(!t||!e)return void this.showEmptyState();const n=e.split("/").pop()||e,s=t.operations||[],o=s[s.length-1],i=`\n <div class="contextual-header">\n <h3 class="contextual-header-text">File: ${n} ${o?this.formatTimestamp(o.timestamp):""}</h3>\n </div>\n `,a=`\n <div class="structured-view-section">\n <div class="file-details">\n <div class="file-path-display">\n <strong>Full Path:</strong> ${this.createClickableFilePath(e)}\n <div id="git-track-status-${e.replace(/[^a-zA-Z0-9]/g,"-")}" class="git-track-status" style="margin-top: 8px;">\n \x3c!-- Git tracking status will be populated here --\x3e\n </div>\n </div>\n <div class="operations-list">\n ${s.map(t=>`\n <div class="operation-item">\n <div class="operation-header">\n <span class="operation-icon">${this.getOperationIcon(t.operation)}</span>\n <span class="operation-type">${t.operation}</span>\n <span class="operation-timestamp">${new Date(t.timestamp).toLocaleString()}</span>\n ${this.isReadOnlyOperation(t.operation)?`\n \x3c!-- Read-only operation: show only file viewer --\x3e\n <span class="file-viewer-icon"\n onclick="showFileViewerModal('${e}')"\n title="View file contents with syntax highlighting"\n style="margin-left: 8px; cursor: pointer; font-size: 16px;">\n 👁️\n </span>\n `:`\n \x3c!-- Edit operation: show both file viewer and git diff --\x3e\n <span class="file-viewer-icon"\n onclick="showFileViewerModal('${e}')"\n title="View file contents with syntax highlighting"\n style="margin-left: 8px; cursor: pointer; font-size: 16px;">\n 👁️\n </span>\n <span class="git-diff-icon"\n onclick="showGitDiffModal('${e}', '${t.timestamp}')"\n title="View git diff for this file operation"\n style="margin-left: 8px; cursor: pointer; font-size: 16px; display: none;"\n data-file-path="${e}"\n data-operation-timestamp="${t.timestamp}">\n 📋\n </span>\n `}\n </div>\n <div class="operation-details">\n <strong>Agent:</strong> ${t.agent}<br>\n <strong>Session:</strong> ${t.sessionId?t.sessionId.substring(0,8)+"...":"Unknown"}\n ${t.details?this.formatOperationDetails(t.details):""}\n </div>\n </div>\n `).join("")}\n </div>\n </div>\n </div>\n `;this.checkAndShowTrackControl(e),this.checkAndShowGitDiffIcons(e);const r=this.createCollapsibleJsonSection(t);this.dataContainer&&(this.dataContainer.innerHTML=i+a+r),this.initializeJsonToggle()}showErrorMessage(t,e){const n=`\n <div class="module-error">\n <div class="error-header">\n <h3>❌ ${t}</h3>\n </div>\n <div class="error-message">\n <p>${e}</p>\n </div>\n </div>\n `,s={title:t,message:e},o=this.createCollapsibleJsonSection(s);this.dataContainer&&(this.dataContainer.innerHTML=n+o),this.initializeJsonToggle()}showAgentEvent(t,e){this.showAgentSpecificDetails(t,e)}showAgentSpecificDetails(t,e){if(!t)return void this.showEmptyState();const n=window.dashboard?.agentInference,s=window.dashboard?.eventViewer;if(!n||!s)return console.warn("AgentInference or EventViewer not available, falling back to single event view"),void this.showEventDetails(t);const o=n.getInferredAgentForEvent(t),i=o?.agentName||this.extractAgent(t)||"Unknown",a=s.events||[],r=this.getAgentSpecificEvents(a,i,n);console.log(`Showing details for agent: ${i}, found ${r.length} related events`);const l=this.extractAgentSpecificData(i,r);this.renderAgentSpecificView(i,l,t)}getAgentSpecificEvents(t,e,n){return t.filter(t=>{const s=n.getInferredAgentForEvent(t);return(s?.agentName||this.extractAgent(t)||"Unknown").toLowerCase()===e.toLowerCase()})}extractAgentSpecificData(t,e){const n={agentName:t,totalEvents:e.length,prompt:null,todos:[],toolsCalled:[],sessions:new Set,firstSeen:null,lastSeen:null,eventTypes:new Set};return e.forEach(e=>{const s=e.data||{},o=new Date(e.timestamp);(!n.firstSeen||o<n.firstSeen)&&(n.firstSeen=o),(!n.lastSeen||o>n.lastSeen)&&(n.lastSeen=o),(e.session_id||s.session_id)&&n.sessions.add(e.session_id||s.session_id);const i=e.hook_event_name||e.type||"unknown";if(n.eventTypes.add(i),"hook"===e.type&&"Task"===s.tool_name&&s.tool_parameters){const e=s.tool_parameters;e.prompt&&!n.prompt&&(n.prompt=e.prompt),e.description&&!n.description&&(n.description=e.description),e.subagent_type===t&&e.prompt&&(n.prompt=e.prompt)}if(!s.prompt||s.agent_type!==t&&s.subagent_type!==t||(n.prompt=s.prompt),"todo"===e.type||"hook"===e.type&&"TodoWrite"===s.tool_name){const t=s.todos||s.tool_parameters?.todos;t&&Array.isArray(t)&&t.forEach(t=>{const e=n.todos.findIndex(e=>e.id===t.id||e.content===t.content);e>=0?n.todos[e]={...n.todos[e],...t,timestamp:o}:n.todos.push({...t,timestamp:o})})}if("hook"===e.type&&s.tool_name){const t=e.subtype||s.event_type,i=this.generateToolCallId(s.tool_name,s.tool_parameters,o);"pre_tool"===t?(n._preToolEvents||(n._preToolEvents=new Map),n._preToolEvents.set(i,{toolName:s.tool_name,timestamp:o,target:this.extractToolTarget(s.tool_name,s.tool_parameters,null),parameters:s.tool_parameters})):"post_tool"===t&&(n._postToolEvents||(n._postToolEvents=new Map),n._postToolEvents.set(i,{toolName:s.tool_name,timestamp:o,success:s.success,duration:s.duration_ms,resultSummary:s.result_summary,exitCode:s.exit_code}))}}),n.todos.sort((t,e)=>(e.timestamp||0)-(t.timestamp||0)),n.toolsCalled=this.consolidateToolCalls(n._preToolEvents,n._postToolEvents),delete n._preToolEvents,delete n._postToolEvents,n.toolsCalled.sort((t,e)=>e.timestamp-t.timestamp),n}generateToolCallId(t,e,n){const s=Math.floor(n.getTime()/5e3);let o="";if(e){const t=[];e.file_path&&t.push(e.file_path),e.command&&t.push(e.command.substring(0,50)),e.pattern&&t.push(e.pattern),e.subagent_type&&t.push(e.subagent_type),e.notebook_path&&t.push(e.notebook_path),e.url&&t.push(e.url),e.prompt&&t.push(e.prompt.substring(0,30)),o=t.join("|")}return o||(o="default"),`${t}:${s}:${o}`}consolidateToolCalls(t,e){const n=[],s=new Set;t||(t=new Map),e||(e=new Map);for(const[o,i]of t){if(s.has(o))continue;const t=e.get(o),a={toolName:i.toolName,timestamp:i.timestamp,target:i.target,parameters:i.parameters,status:this.determineToolCallStatus(i,t),statusIcon:this.getToolCallStatusIcon(i,t),phase:t?"completed":"running"};t&&(a.success=t.success,a.duration=t.duration,a.resultSummary=t.resultSummary,a.exitCode=t.exitCode,a.completedAt=t.timestamp),n.push(a),s.add(o)}for(const[o,i]of e){if(s.has(o))continue;const t={toolName:i.toolName,timestamp:i.timestamp,target:"Unknown target",parameters:null,status:this.determineToolCallStatus(null,i),statusIcon:this.getToolCallStatusIcon(null,i),phase:"completed",success:i.success,duration:i.duration,resultSummary:i.resultSummary,exitCode:i.exitCode,completedAt:i.timestamp};n.push(t),s.add(o)}return n}determineToolCallStatus(t,e){return e?!0===e.success?"Success":!1===e.success?"Failed":0===e.exitCode?"Completed":2===e.exitCode?"Blocked":void 0!==e.exitCode&&0!==e.exitCode?"Error":"Completed":"Running..."}getToolCallStatusIcon(t,e){return e?!0===e.success?"✅":!1===e.success?"❌":0===e.exitCode?"✅":2===e.exitCode?"⚠️":void 0!==e.exitCode&&0!==e.exitCode?"❌":"✅":"⏳"}estimateTokenCount(t){if(!t||"string"!=typeof t)return 0;const e=t.trim().split(/\s+/).length,n=Math.ceil(t.length/4);return Math.max(1.3*e,n)}trimPromptWhitespace(t){return t&&"string"==typeof t?t=(t=(t=t.trim()).replace(/\n\s*\n\s*\n+/g,"\n\n")).split("\n").map(t=>t.replace(/\s+$/,"")).join("\n"):""}renderAgentSpecificView(t,e,n){const s=this.formatTimestamp(n.timestamp),o=`\n <div class="contextual-header">\n <h3 class="contextual-header-text">🤖 ${t} Agent Details ${s}</h3>\n </div>\n `;let i=`\n <div class="agent-overview-section">\n <div class="structured-data">\n ${this.createProperty("Agent Name",t)}\n ${this.createProperty("Total Events",e.totalEvents)}\n ${this.createProperty("Active Sessions",e.sessions.size)}\n ${this.createProperty("Event Types",Array.from(e.eventTypes).join(", "))}\n ${e.firstSeen?this.createProperty("First Seen",e.firstSeen.toLocaleString()):""}\n ${e.lastSeen?this.createProperty("Last Seen",e.lastSeen.toLocaleString()):""}\n </div>\n </div>\n `;if(e.prompt){const t=this.trimPromptWhitespace(e.prompt);i+=`\n <div class="agent-prompt-section">\n <div class="contextual-header">\n <h3 class="contextual-header-text">📝 Agent Task Prompt</h3>\n <div class="prompt-stats" style="font-size: 11px; color: #64748b; margin-top: 4px;">\n ~${Math.round(this.estimateTokenCount(t))} tokens • ${t.trim().split(/\s+/).length} words • ${t.length} characters\n </div>\n </div>\n <div class="structured-data">\n <div class="agent-prompt" style="white-space: pre-wrap; max-height: 300px; overflow-y: auto; padding: 10px; background: #f8fafc; border-radius: 6px; font-family: monospace; font-size: 12px; line-height: 1.4; border: 1px solid #e2e8f0;">\n ${this.escapeHtml(t)}\n </div>\n </div>\n </div>\n `}e.todos.length>0&&(i+=`\n <div class="agent-todos-section">\n <div class="contextual-header">\n <h3 class="contextual-header-text">✅ Agent Todo List (${e.todos.length} items)</h3>\n </div>\n <div class="todo-checklist">\n ${e.todos.map(t=>`\n <div class="todo-item todo-${t.status||"pending"}">\n <span class="todo-status">${this.getTodoStatusIcon(t.status)}</span>\n <span class="todo-content">${t.content||"No content"}</span>\n <span class="todo-priority priority-${t.priority||"medium"}">${this.getTodoPriorityIcon(t.priority)}</span>\n ${t.timestamp?`<span class="todo-timestamp">${new Date(t.timestamp).toLocaleTimeString()}</span>`:""}\n </div>\n `).join("")}\n </div>\n </div>\n `),e.toolsCalled.length>0&&(i+=`\n <div class="agent-tools-section">\n <div class="contextual-header">\n <h3 class="contextual-header-text">🔧 Tools Called by Agent (${e.toolsCalled.length} calls)</h3>\n </div>\n <div class="tools-list">\n ${e.toolsCalled.map(e=>{let n="";return"✅"===e.statusIcon?n="status-success":"❌"===e.statusIcon?n="status-failed":"⚠️"===e.statusIcon?n="status-blocked":"⏳"===e.statusIcon&&(n="status-running"),`\n <div class="tool-call-item">\n <div class="tool-call-header">\n <div style="display: flex; align-items: center; gap: 12px; flex: 1;">\n <span class="tool-name">🔧 ${e.toolName}</span>\n <span class="tool-agent">${t}</span>\n <span class="tool-status-indicator ${n}">${e.statusIcon} ${e.status}</span>\n </div>\n <span class="tool-timestamp" style="margin-left: auto;">${e.timestamp.toLocaleTimeString()}</span>\n </div>\n <div class="tool-call-details">\n ${e.target?`<span class="tool-target">Target: ${e.target}</span>`:""}\n ${e.duration?`<span class="tool-duration">Duration: ${e.duration}ms</span>`:""}\n ${e.completedAt&&e.completedAt!==e.timestamp?`<span class="tool-completed">Completed: ${e.completedAt.toLocaleTimeString()}</span>`:""}\n </div>\n </div>\n `}).join("")}\n </div>\n </div>\n `);const a={agentName:t,agentData:e,originalEvent:n},r=this.createCollapsibleJsonSection(a);this.dataContainer&&(this.dataContainer.innerHTML=o+i+r),this.initializeJsonToggle()}createToolResultFromToolCall(t){if(!t.result_summary)return"";const e={event_type:"post_tool",result_summary:t.result_summary,success:t.success,exit_code:t.exit_code},n=this.createInlineToolResultContent(e,{subtype:"post_tool"});return n.trim()?`\n <div class="tool-result-inline">\n <div class="structured-data">\n ${n}\n </div>\n </div>\n `:""}extractToolTarget(t,e,n){const s=e||n||{};switch(t?.toLowerCase()){case"write":case"read":case"edit":case"multiedit":return s.file_path||"Unknown file";case"bash":return s.command?`${s.command.substring(0,50)}${s.command.length>50?"...":""}`:"Unknown command";case"grep":return s.pattern?`Pattern: ${s.pattern}`:"Unknown pattern";case"glob":return s.pattern?`Pattern: ${s.pattern}`:"Unknown glob";case"todowrite":return`${s.todos?.length||0} todos`;case"task":return s.subagent_type||s.agent_type||"Subagent delegation";default:return s.file_path?s.file_path:s.pattern?`Pattern: ${s.pattern}`:s.command?`Command: ${s.command.substring(0,30)}...`:s.path?s.path:"Unknown target"}}getOperationIcon(t){return{read:"👁️",write:"✏️",edit:"📝",multiedit:"📝",create:"🆕",delete:"🗑️",move:"📦",copy:"📋"}[t?.toLowerCase()]||"📄"}getCurrentEvent(){return this.currentEvent}async checkAndShowTrackControl(t){if(t)try{const e=window.socket||window.dashboard?.socketClient?.socket;if(!e)return void console.warn("No socket connection available for git tracking check");let n=window.dashboard?.currentWorkingDir;if(!n||"Unknown"===n||""===n.trim()){const t=document.getElementById("footer-working-dir");n=t?.textContent?.trim()&&"Unknown"!==t.textContent.trim()?t.textContent.trim():".",console.log("[MODULE-VIEWER-DEBUG] Working directory fallback used:",n)}const s=new Promise((n,s)=>{const o=s=>{s.file_path===t&&(e.off("file_tracked_response",o),n(s))};e.on("file_tracked_response",o),setTimeout(()=>{e.off("file_tracked_response",o),s(new Error("Request timeout"))},5e3)});e.emit("check_file_tracked",{file_path:t,working_dir:n});const o=await s;this.displayTrackingStatus(t,o)}catch(e){console.error("Error checking file tracking status:",e),this.displayTrackingStatus(t,{success:!1,error:e.message,file_path:t})}}displayTrackingStatus(t,e){const n=`git-track-status-${t.replace(/[^a-zA-Z0-9]/g,"-")}`,s=document.getElementById(n);s&&(e.success&&!1===e.is_tracked?s.innerHTML=`\n <div class="untracked-file-notice">\n <span class="untracked-icon">⚠️</span>\n <span class="untracked-text">This file is not tracked by git</span>\n <button class="track-file-button"\n onclick="window.moduleViewer.trackFile('${t}')"\n title="Add this file to git tracking">\n <span class="git-icon">📁</span> Track File\n </button>\n </div>\n `:e.success&&!0===e.is_tracked?s.innerHTML='\n <div class="tracked-file-notice">\n <span class="tracked-icon">✅</span>\n <span class="tracked-text">This file is tracked by git</span>\n </div>\n ':e.success||(s.innerHTML=`\n <div class="tracking-error-notice">\n <span class="error-icon">❌</span>\n <span class="error-text">Could not check git status: ${e.error||"Unknown error"}</span>\n </div>\n `))}async trackFile(t){if(t)try{const e=window.socket||window.dashboard?.socketClient?.socket;if(!e)return void console.warn("No socket connection available for git add");let n=window.dashboard?.currentWorkingDir;if(!n||"Unknown"===n||""===n.trim()){const t=document.getElementById("footer-working-dir");n=t?.textContent?.trim()&&"Unknown"!==t.textContent.trim()?t.textContent.trim():".",console.log("[MODULE-VIEWER-DEBUG] Working directory fallback used:",n)}const s=`git-track-status-${t.replace(/[^a-zA-Z0-9]/g,"-")}`,o=document.getElementById(s);o&&(o.innerHTML='\n <div class="tracking-file-notice">\n <span class="loading-icon">⏳</span>\n <span class="loading-text">Adding file to git tracking...</span>\n </div>\n ');const i=new Promise((n,s)=>{const o=s=>{s.file_path===t&&(e.off("git_add_response",o),n(s))};e.on("git_add_response",o),setTimeout(()=>{e.off("git_add_response",o),s(new Error("Request timeout"))},1e4)});e.emit("git_add_file",{file_path:t,working_dir:n}),console.log("📁 Git add request sent:",{filePath:t,workingDir:n});const a=await i;console.log("📦 Git add result:",a),a.success?(o&&(o.innerHTML='\n <div class="tracked-file-notice">\n <span class="tracked-icon">✅</span>\n <span class="tracked-text">File successfully added to git tracking</span>\n </div>\n '),this.showNotification("File tracked successfully","success")):(o&&(o.innerHTML=`\n <div class="tracking-error-notice">\n <span class="error-icon">❌</span>\n <span class="error-text">Failed to track file: ${a.error||"Unknown error"}</span>\n <button class="track-file-button"\n onclick="window.moduleViewer.trackFile('${t}')"\n title="Try again">\n <span class="git-icon">📁</span> Retry\n </button>\n </div>\n `),this.showNotification(`Failed to track file: ${a.error}`,"error"))}catch(e){console.error("❌ Failed to track file:",e);const n=`git-track-status-${t.replace(/[^a-zA-Z0-9]/g,"-")}`,s=document.getElementById(n);s&&(s.innerHTML=`\n <div class="tracking-error-notice">\n <span class="error-icon">❌</span>\n <span class="error-text">Error: ${e.message}</span>\n <button class="track-file-button"\n onclick="window.moduleViewer.trackFile('${t}')"\n title="Try again">\n <span class="git-icon">📁</span> Retry\n </button>\n </div>\n `),this.showNotification(`Error tracking file: ${e.message}`,"error")}}async checkAndShowGitDiffIcons(t){if(t){console.debug("[GIT-DIFF-ICONS] Checking git diff icons for file:",t);try{const e=window.socket||window.dashboard?.socketClient?.socket;if(!e)return void console.warn("[GIT-DIFF-ICONS] No socket connection available for git status check");console.debug("[GIT-DIFF-ICONS] Socket connection available, proceeding");let n=window.dashboard?.currentWorkingDir;if(n&&"Unknown"!==n&&""!==n.trim())console.debug("[GIT-DIFF-ICONS] Using working directory:",n);else{const t=document.getElementById("footer-working-dir");n=t?.textContent?.trim()&&"Unknown"!==t.textContent.trim()?t.textContent.trim():".",console.log("[GIT-DIFF-ICONS] Working directory fallback used:",n)}const s=new Promise((n,s)=>{const o=s=>{console.debug("[GIT-DIFF-ICONS] Received git status response:",s),s.file_path===t?(e.off("git_status_response",o),n(s)):console.debug("[GIT-DIFF-ICONS] Response for different file, ignoring:",s.file_path)};e.on("git_status_response",o),setTimeout(()=>{e.off("git_status_response",o),console.warn("[GIT-DIFF-ICONS] Timeout waiting for git status response"),s(new Error("Request timeout"))},3e3)});console.debug("[GIT-DIFF-ICONS] Sending check_git_status event"),e.emit("check_git_status",{file_path:t,working_dir:n});const o=await s;console.debug("[GIT-DIFF-ICONS] Git status check result:",o),o.success?(console.debug("[GIT-DIFF-ICONS] Git status check successful, showing icons for:",t),this.showGitDiffIconsForFile(t)):console.debug("[GIT-DIFF-ICONS] Git status check failed, icons will remain hidden:",o.error)}catch(e){console.warn("[GIT-DIFF-ICONS] Git status check failed, hiding git diff icons:",e.message)}}else console.debug("[GIT-DIFF-ICONS] No filePath provided, skipping git diff icon check")}showGitDiffIconsForFile(t){console.debug("[GIT-DIFF-ICONS] Showing git diff icons for file:",t);const e=document.querySelectorAll(`[data-file-path="${t}"]`);console.debug("[GIT-DIFF-ICONS] Found",e.length,"elements with matching file path");let n=0;e.forEach((t,e)=>{console.debug("[GIT-DIFF-ICONS] Processing element",e,":",t),console.debug("[GIT-DIFF-ICONS] Element classes:",t.classList.toString()),t.classList.contains("git-diff-icon")?(console.debug("[GIT-DIFF-ICONS] Setting display to inline for git-diff-icon"),t.style.display="inline",n++):console.debug("[GIT-DIFF-ICONS] Element is not a git-diff-icon, skipping")}),console.debug("[GIT-DIFF-ICONS] Showed",n,"git diff icons for file:",t)}showNotification(t,e="info"){const n=document.createElement("div");n.className=`notification notification-${e}`,n.innerHTML=`\n <span class="notification-icon">${"success"===e?"✅":"error"===e?"❌":"ℹ️"}</span>\n <span class="notification-message">${t}</span>\n `,n.style.cssText=`\n position: fixed;\n top: 20px;\n right: 20px;\n background: ${"success"===e?"#d4edda":"error"===e?"#f8d7da":"#d1ecf1"};\n color: ${"success"===e?"#155724":"error"===e?"#721c24":"#0c5460"};\n border: 1px solid ${"success"===e?"#c3e6cb":"error"===e?"#f5c6cb":"#bee5eb"};\n border-radius: 6px;\n padding: 12px 16px;\n font-size: 14px;\n font-weight: 500;\n z-index: 2000;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n display: flex;\n align-items: center;\n gap: 8px;\n max-width: 400px;\n animation: slideIn 0.3s ease-out;\n `;const s=document.createElement("style");s.textContent="\n @keyframes slideIn {\n from { transform: translateX(100%); opacity: 0; }\n to { transform: translateX(0); opacity: 1; }\n }\n @keyframes slideOut {\n from { transform: translateX(0); opacity: 1; }\n to { transform: translateX(100%); opacity: 0; }\n }\n ",document.head.appendChild(s),document.body.appendChild(n),setTimeout(()=>{n.style.animation="slideOut 0.3s ease-in",setTimeout(()=>{n.parentNode&&n.parentNode.removeChild(n),s.parentNode&&s.parentNode.removeChild(s)},300)},5e3)}showAgentInstance(t){if(!t)return void this.showEmptyState();const e={type:"pm_delegation",subtype:t.agentName,agent_type:t.agentName,timestamp:t.timestamp,session_id:t.sessionId,metadata:{delegation_type:"explicit",event_count:t.agentEvents.length,pm_call:t.pmCall||null,agent_events:t.agentEvents}};console.log("Showing PM delegation details:",t),this.showAgentSpecificDetails(e,0)}showImpliedAgent(t){if(!t)return void this.showEmptyState();const e={type:"implied_delegation",subtype:t.agentName,agent_type:t.agentName,timestamp:t.timestamp,session_id:t.sessionId,metadata:{delegation_type:"implied",event_count:t.eventCount,pm_call:null,note:"No explicit PM call found - inferred from agent activity"}};console.log("Showing implied agent details:",t),this.showAgentSpecificDetails(e,0)}}window.ModuleViewer=e,window.enableToolResultDebugging=function(){window.DEBUG_TOOL_RESULTS=!0,console.log("🔧 Tool result debugging enabled. Click on tool events to see debug info.")},window.disableToolResultDebugging=function(){window.DEBUG_TOOL_RESULTS=!1,console.log("🔧 Tool result debugging disabled.")};export{e as M};
|
|
2
2
|
//# sourceMappingURL=module-viewer.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{S as e,U as t}from"./socket-client.js";import{E as n,a as i}from"./components/event-viewer.js";import{M as s}from"./components/module-viewer.js";import{S as o}from"./components/session-manager.js";import{A as a}from"./components/agent-inference.js";import{E as r}from"./components/export-manager.js";import{W as l}from"./components/working-directory.js";import{F as d}from"./components/file-tool-tracker.js";import"./chunks/unified-data-viewer.CvAmgM3l.js";class c{constructor(e,t){this.agentInference=e,this.eventViewer=t,this.state={hierarchyTree:null,nodeMap:new Map,expandedNodes:new Set,selectedNode:null},this.expandAll=!0,this.setupEventListeners(),console.log("Agent hierarchy component initialized")}setupEventListeners(){document.addEventListener("click",e=>{const t=e.target.closest("[data-toggle-node]");if(t&&window.dashboard&&window.dashboard.agentHierarchy){const e=t.dataset.toggleNode;window.dashboard.agentHierarchy.toggleNode(e)}})}buildHierarchy(){this.agentInference.processAgentInference();const e=this.agentInference.getPMDelegations(),t=this.eventViewer.events,n=this.agentInference.getEventAgentMap(),i={id:"pm_main",type:"pm",name:"PM (Main Session)",children:[],events:[],eventCount:0,status:"active",startTime:null,endTime:null,expanded:!0},s=new Map;this.state.nodeMap.clear(),this.state.nodeMap.set(i.id,i);const o=new Set;for(const[h,u]of e){const e={id:h,type:"subagent",name:u.agentName,delegationContext:this.extractDelegationContext(u.pmCall),children:[],events:u.agentEvents,eventCount:u.agentEvents.length,status:u.endIndex?"completed":"active",startTime:u.timestamp,endTime:u.endIndex?t[u.endIndex]?.timestamp:null,startIndex:u.startIndex,endIndex:u.endIndex,expanded:this.expandAll||this.state.expandedNodes.has(h)};if(i.children.push(e),this.state.nodeMap.set(h,e),o.add(u.agentName),i.eventCount++,(!i.startTime||new Date(u.timestamp)<new Date(i.startTime))&&(i.startTime=u.timestamp),u.endIndex&&t[u.endIndex]){const e=t[u.endIndex].timestamp;(!i.endTime||new Date(e)>new Date(i.endTime))&&(i.endTime=e)}}const a=this.agentInference.getOrphanGroups();let r=1;for(const[h,u]of a){const i={id:`pm_implied_${h}`,type:"pm",name:`PM (Implied #${r})`,children:[],events:[],eventCount:0,status:"inferred",startTime:null,endTime:null,expanded:!0,isImplied:!0,tooltip:"Inferred PM - Subagents started without explicit PM delegation"};s.set(h,i),this.state.nodeMap.set(i.id,i),r++;const o=new Map;for(const s of u){const i=[];t.forEach((t,o)=>{const a=n.get(o);if(a&&a.agentName===s.agentName){let n=!0;for(const[t,i]of e)if(i.agentEvents.some(e=>e.eventIndex===o)){n=!1;break}n&&i.push({eventIndex:o,event:t,inference:a})}}),i.length>0&&(o.has(s.agentName)||o.set(s.agentName,[]),o.get(s.agentName).push(...i))}for(const[e,t]of o){if(0===t.length)continue;const n=t[0].event,s=t[t.length-1].event,o={id:`implied_agent_${h}_${e}`,type:"subagent",name:e,delegationContext:"Orphan agent - no explicit PM delegation found",children:[],events:t,eventCount:t.length,status:"inferred",startTime:n.timestamp,endTime:s.timestamp,startIndex:t[0].eventIndex,endIndex:t[t.length-1].eventIndex,expanded:this.expandAll,isImplied:!0,tooltip:"This agent was spawned without an explicit PM Task delegation"};i.children.push(o),this.state.nodeMap.set(o.id,o),i.eventCount+=t.length,(!i.startTime||new Date(n.timestamp)<new Date(i.startTime))&&(i.startTime=n.timestamp),(!i.endTime||new Date(s.timestamp)>new Date(i.endTime))&&(i.endTime=s.timestamp)}}const l=[];if(t.forEach((t,i)=>{const o=n.get(i);if(o&&"subagent"===o.type){let n=!0;for(const[t,s]of e)if(s.agentEvents.some(e=>e.eventIndex===i)){n=!1;break}if(n)for(const[e,t]of s){for(const e of t.children)if(e.events.some(e=>e.eventIndex===i)){n=!1;break}if(!n)break}n&&l.push({eventIndex:i,event:t,inference:o})}}),l.length>0){const e={id:"pm_implied_generic",type:"pm",name:"PM (Implied - Uncategorized)",children:[],events:[],eventCount:0,status:"inferred",startTime:null,endTime:null,expanded:!0,isImplied:!0,tooltip:"Orphan agents without clear grouping"},t=new Map;for(const n of l){const e=n.inference.agentName;t.has(e)||t.set(e,[]),t.get(e).push(n)}for(const[n,i]of t){const t=i[0].event,s=i[i.length-1].event,o={id:`implied_generic_${n}`,type:"subagent",name:n,delegationContext:"Uncategorized orphan agent",children:[],events:i,eventCount:i.length,status:"inferred",startTime:t.timestamp,endTime:s.timestamp,startIndex:i[0].eventIndex,endIndex:i[i.length-1].eventIndex,expanded:this.expandAll,isImplied:!0};e.children.push(o),this.state.nodeMap.set(o.id,o),e.eventCount+=i.length,(!e.startTime||new Date(t.timestamp)<new Date(e.startTime))&&(e.startTime=t.timestamp),(!e.endTime||new Date(s.timestamp)>new Date(e.endTime))&&(e.endTime=s.timestamp)}e.children.length>0&&(s.set("generic",e),this.state.nodeMap.set(e.id,e))}let d=0;if(t.forEach((e,t)=>{const s=n.get(t);s&&"main_agent"===s.type&&(d++,i.events.push({eventIndex:t,event:e,inference:s}))}),i.eventCount+=d,i.children.length>0){const e=i.children.some(e=>"active"===e.status);i.status=e?"active":"completed"}const c={roots:[]};(i.eventCount>0||i.children.length>0)&&c.roots.push(i);for(const[h,u]of s)u.children.length>0&&c.roots.push(u);return this.state.hierarchyTree=c,console.log("Hierarchy built:",{mainPM:{children:i.children.length,events:i.eventCount,ownEvents:d},impliedPMGroups:s.size,totalImpliedAgents:Array.from(s.values()).reduce((e,t)=>e+t.children.length,0)}),c}extractDelegationContext(e){if(!e)return"Unknown delegation";const t=e.tool_parameters||e.data?.tool_parameters||{},n=t.task||t.request||t.description;if(n){const e=100;return n.length>e?n.substring(0,e)+"...":n}const i=e.tool_input||e.data?.tool_input;if(i&&"string"==typeof i){const e=100;return i.length>e?i.substring(0,e)+"...":i}return"Task delegation"}render(e={}){const t=this.state.hierarchyTree||this.buildHierarchy();if(!t.roots||0===t.roots.length)return'<div class="agent-hierarchy-empty">No agent activity detected</div>';return`<div class="agent-hierarchy">${this.applyFilters(t,e).roots.map(e=>this.renderNode(e,0)).join("")}</div>`}renderNode(e,t){const n=e.expanded||this.state.expandedNodes.has(e.id),i=e.children&&e.children.length>0,s=this.state.selectedNode===e.id,o=this.getNodeIcon(e),a=i?n?"▼":"▶":" ",r=this.getStatusClass(e.status),l=e.isImplied?"agent-node-implied":"",d=e.tooltip?`title="${this.escapeHtml(e.tooltip)}"`:"";let c=`\n <div class="agent-node agent-node-level-${t} ${s?"agent-node-selected":""} ${l}" \n data-node-id="${e.id}" ${d}>\n <div class="agent-node-header ${r}" \n data-toggle-node="${e.id}" style="cursor: pointer">\n <span class="agent-node-expand">${a}</span>\n <span class="agent-node-icon">${o}</span>\n <span class="agent-node-name">${this.escapeHtml(e.name)}</span>\n <span class="agent-node-stats">\n <span class="agent-event-count">${e.eventCount} events</span>\n ${e.status?`<span class="agent-status">${e.status}</span>`:""}\n </span>\n </div>\n `;if(n&&(e.delegationContext||e.startTime)){if(c+='<div class="agent-node-details">',e.delegationContext&&"Unknown delegation"!==e.delegationContext&&(c+=`\n <div class="agent-delegation-context">\n <strong>Task:</strong> ${this.escapeHtml(e.delegationContext)}\n </div>\n `),e.startTime){const t=this.calculateDuration(e.startTime,e.endTime);c+=`\n <div class="agent-timing">\n <span class="agent-time-start">${this.formatTime(e.startTime)}</span>\n ${t?`<span class="agent-duration">(${t})</span>`:""}\n </div>\n `}c+="</div>"}return n&&i&&(c+='<div class="agent-node-children">',c+=e.children.map(e=>this.renderNode(e,t+1)).join(""),c+="</div>"),c+="</div>",c}getNodeIcon(e){if("pm"===e.type)return e.isImplied?"🔍":"👔";return{"Engineer Agent":"🔧","Research Agent":"🔍","QA Agent":"✅","Documentation Agent":"📝","Security Agent":"🔒","Ops Agent":"⚙️","Version Control Agent":"📦","Data Engineer Agent":"💾","Test Integration Agent":"🧪"}[e.name]||"🤖"}getStatusClass(e){switch(e){case"active":return"agent-status-active";case"completed":return"agent-status-completed";case"pending":return"agent-status-pending";case"inferred":return"agent-status-inferred";default:return"agent-status-unknown"}}toggleNode(e){const t=this.state.nodeMap.get(e);t&&(this.state.expandedNodes.has(e)?(this.state.expandedNodes.delete(e),t.expanded=!1):(this.state.expandedNodes.add(e),t.expanded=!0),window.dashboard&&window.dashboard.renderCurrentTab())}selectNode(e){this.state.selectedNode=e;const t=this.state.nodeMap.get(e);if(t){const e=new CustomEvent("agentNodeSelected",{detail:{node:t}});document.dispatchEvent(e)}}applyFilters(e,t){if(!t||0===Object.keys(t).length)return e;const n={roots:[]};for(const i of e.roots){const e=this.filterNode(i,t);e&&n.roots.push(e)}return n}filterNode(e,t){let n=!0;if(t.searchText){const i=t.searchText.toLowerCase();n=n&&(e.name.toLowerCase().includes(i)||e.delegationContext&&e.delegationContext.toLowerCase().includes(i))}t.agentType&&(n=n&&e.name.includes(t.agentType)),t.status&&(n=n&&e.status===t.status);let i=[];if(e.children)for(const s of e.children){const e=this.filterNode(s,t);e&&i.push(e)}return n||i.length>0?{...e,children:i}:null}formatTime(e){if(!e)return"";return new Date(e).toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1})}calculateDuration(e,t){if(!e||!t)return"";const n=new Date(e).getTime(),i=new Date(t).getTime()-n;if(i<1e3)return`${i}ms`;if(i<6e4)return`${(i/1e3).toFixed(1)}s`;return`${Math.floor(i/6e4)}m ${Math.floor(i%6e4/1e3)}s`}escapeHtml(e){if(!e)return"";const t=document.createElement("div");return t.textContent=e,t.innerHTML}updateWithNewEvents(e){this.buildHierarchy()}clear(){this.state.hierarchyTree=null,this.state.nodeMap.clear(),this.state.expandedNodes.clear(),this.state.selectedNode=null}expandAllNodes(){for(const[e,t]of this.state.nodeMap)this.state.expandedNodes.add(e),t.expanded=!0;this.expandAll=!0}collapseAllNodes(){this.state.expandedNodes.clear();for(const[e,t]of this.state.nodeMap)t.expanded=!1;this.expandAll=!1}}class h{constructor(){this.element=null,this.buildInfo={monitor:{version:"1.0.0",build:1,formatted_build:"0001",full_version:"v1.0.0-0001"},mpm:{version:"unknown",build:"unknown",full_version:"v0.0.0"}},this.socketClient=null,this.init()}async init(){await this.loadDashboardVersion(),this.createElements(),this.setupEventListeners()}async loadDashboardVersion(){try{const e=await fetch("/version.json");if(e.ok){const t=await e.json();this.buildInfo.monitor={version:t.version||"1.0.0",build:t.build||1,formatted_build:t.formatted_build||"0001",full_version:t.full_version||"v1.0.0-0001"}}}catch(e){}}createElements(){this.element=document.createElement("div"),this.element.className="version-display",this.element.id="version-display";const e=document.createElement("span");e.className="version-item mpm-version",e.id="mpm-version",e.innerHTML='\n <span class="version-label">MPM</span>\n <span class="version-value">v0.0.0</span>\n ';const t=document.createElement("span");t.className="version-separator",t.textContent="|";const n=document.createElement("span");n.className="version-item monitor-version",n.id="monitor-version",n.innerHTML='\n <span class="version-label">Monitor</span>\n <span class="version-value">v1.0.0-0001</span>\n ',this.element.appendChild(e),this.element.appendChild(t),this.element.appendChild(n),this.element.title="Click for detailed version information"}setSocketClient(e){this.socketClient=e,this.socketClient&&this.socketClient.socket&&(this.socketClient.socket.on("welcome",e=>{const t=e.build_info||e.data&&e.data.build_info;t&&this.updateBuildInfo(t)}),this.socketClient.socket.on("status",e=>{const t=e.build_info||e.data&&e.data.build_info;t&&this.updateBuildInfo(t)}),this.socketClient.socket.on("build_info",e=>{this.updateBuildInfo(e)}))}updateBuildInfo(e){this.buildInfo=e,this.updateDisplay()}updateDisplay(){const e=this.element.querySelector(".mpm-version .version-value");if(e&&this.buildInfo.mpm){const t=this.buildInfo.mpm.full_version||`v${this.buildInfo.mpm.version}`;e.textContent=t,this.buildInfo.mpm.build&&"unknown"!==this.buildInfo.mpm.build&&(e.parentElement.title=`MPM Build: ${this.buildInfo.mpm.build}`)}const t=this.element.querySelector(".monitor-version .version-value");if(t&&this.buildInfo.monitor){const e=this.buildInfo.monitor.full_version||`v${this.buildInfo.monitor.version}-${this.buildInfo.monitor.formatted_build}`;if(t.textContent=e,this.buildInfo.monitor.last_updated){const e=new Date(this.buildInfo.monitor.last_updated).toLocaleString();t.parentElement.title=`Monitor Build: ${this.buildInfo.monitor.formatted_build}\nLast Updated: ${e}`}}}setupEventListeners(){this.element.addEventListener("click",()=>{this.showDetailedInfo()})}showDetailedInfo(){const e=[];if(this.buildInfo.mpm&&(e.push("=== MPM Framework ==="),e.push(`Version: ${this.buildInfo.mpm.version}`),this.buildInfo.mpm.build&&"unknown"!==this.buildInfo.mpm.build&&e.push(`Build: ${this.buildInfo.mpm.build}`),e.push(`Full: ${this.buildInfo.mpm.full_version}`)),e.push(""),this.buildInfo.monitor&&(e.push("=== Monitor UI ==="),e.push(`Version: ${this.buildInfo.monitor.version}`),e.push(`Build: ${this.buildInfo.monitor.formatted_build} (${this.buildInfo.monitor.build})`),e.push(`Full: ${this.buildInfo.monitor.full_version}`),this.buildInfo.monitor.last_updated)){const t=new Date(this.buildInfo.monitor.last_updated).toLocaleString();e.push(`Updated: ${t}`)}const t=document.createElement("div");t.className="version-modal",t.innerHTML=`\n <div class="version-modal-content">\n <h3>Version Information</h3>\n <pre>${e.join("\n")}</pre>\n <button onclick="this.parentElement.parentElement.remove()">Close</button>\n </div>\n `,document.body.appendChild(t),setTimeout(()=>{t.remove()},1e4)}mount(e){const t="string"==typeof e?document.querySelector(e):e;this.element&&t&&this.element.parentNode!==t&&t.appendChild(this.element)}getElement(){return this.element}destroy(){this.element&&this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.socketClient&&this.socketClient.socket&&(this.socketClient.socket.off("welcome"),this.socketClient.socket.off("status"),this.socketClient.socket.off("build_info")),this.element=null,this.socketClient=null}}class u{constructor(){this.eventViewer=null,this.moduleViewer=null,this.sessionManager=null,this.socketManager=null,this.agentInference=null,this.agentHierarchy=null,this.uiStateManager=null,this.eventProcessor=null,this.exportManager=null,this.workingDirectoryManager=null,this.fileToolTracker=null,this.buildTracker=null,this.init()}init(){console.log("Initializing refactored Claude MPM Dashboard...");try{this.initializeSocketManager(),this.initializeCoreComponents(),this.initializeBuildTracker(),this.initializeAgentInference(),this.initializeAgentHierarchy(),this.initializeUIStateManager(),this.initializeWorkingDirectoryManager(),this.initializeFileToolTracker(),this.initializeEventProcessor(),this.initializeExportManager(),this.setupModuleInteractions(),this.initializeFromURL(),console.log("Claude MPM Dashboard initialized successfully")}catch(e){throw console.error("Error during dashboard initialization:",e),e}}validateInitialization(){const e=[{name:"socketManager",component:this.socketManager},{name:"eventViewer",component:this.eventViewer},{name:"agentHierarchy",component:this.agentHierarchy}].filter(e=>!e.component);e.length>0?console.warn("Missing critical components:",e.map(e=>e.name)):console.log("All critical components initialized")}postInit(){try{this.agentHierarchy&&(window.dashboard.agentHierarchy=this.agentHierarchy,console.log("Agent hierarchy global reference set")),this.validateInitialization()}catch(e){console.error("Error in dashboard postInit:",e)}}initializeSocketManager(){this.socketManager=new e,this.socketManager.setupConnectionControls(),this.socketClient=this.socketManager.getSocketClient(),window.socketClient=this.socketClient}initializeCoreComponents(){this.eventViewer=new n("events-list",this.socketClient),this.moduleViewer=new s,this.sessionManager=new o(this.socketClient),window.eventViewer=this.eventViewer,window.moduleViewer=this.moduleViewer,window.sessionManager=this.sessionManager}initializeBuildTracker(){this.buildTracker=new h,this.buildTracker.setSocketClient(this.socketClient);const e=()=>{const t=document.querySelector(".header-title");t?(this.buildTracker.mount(t),console.log("BuildTracker mounted successfully")):(console.warn("Header-title element not found for build tracker, will retry"),setTimeout(e,100))};e(),window.buildTracker=this.buildTracker}initializeAgentInference(){this.agentInference=new a(this.eventViewer),this.agentInference.initialize()}initializeAgentHierarchy(){try{this.agentHierarchy=new c(this.agentInference,this.eventViewer),console.log("Agent hierarchy component created")}catch(e){console.error("Failed to initialize agent hierarchy:",e),this.agentHierarchy={render:()=>'<div class="error">Agent hierarchy unavailable</div>',expandAllNodes:()=>{},collapseAllNodes:()=>{},updateWithNewEvents:()=>{}}}}initializeUIStateManager(){this.uiStateManager=new t,this.setupTabFilters()}initializeWorkingDirectoryManager(){this.workingDirectoryManager=new l(this.socketManager)}initializeFileToolTracker(){this.fileToolTracker=new d(this.agentInference,this.workingDirectoryManager)}initializeEventProcessor(){this.eventProcessor=new i(this.eventViewer,this.agentInference)}initializeExportManager(){this.exportManager=new r(this.eventViewer)}setupModuleInteractions(){this.socketManager.onEventUpdate(e=>{this.fileToolTracker.updateFileOperations(e),this.fileToolTracker.updateToolCalls(e),this.agentInference.processAgentInference(),this.agentHierarchy.updateWithNewEvents(e),"events"===this.uiStateManager.getCurrentTab()&&this.exportManager.scrollListToBottom("events-list"),this.renderCurrentTab()}),this.socketManager.onConnectionStatusChange((e,t)=>{"connected"===t&&this.workingDirectoryManager.updateGitBranch(this.workingDirectoryManager.getCurrentWorkingDir())}),document.addEventListener("tabChanged",e=>{this.renderCurrentTab(),this.uiStateManager.updateTabNavigationItems()}),document.addEventListener("eventsClearing",()=>{this.fileToolTracker.clear(),this.agentInference.initialize()}),document.addEventListener("showCardDetails",e=>{this.showCardDetails(e.detail.tabName,e.detail.index)}),document.addEventListener("sessionFilterChanged",e=>{console.log("Session filter changed, re-rendering current tab:",this.uiStateManager.getCurrentTab()),this.renderCurrentTab()})}setupTabFilters(){const e=document.getElementById("agents-search-input"),t=document.getElementById("agents-type-filter");e&&e.addEventListener("input",()=>{"agents"===this.uiStateManager.getCurrentTab()&&this.renderCurrentTab()}),t&&t.addEventListener("change",()=>{"agents"===this.uiStateManager.getCurrentTab()&&this.renderCurrentTab()});const n=document.getElementById("tools-search-input"),i=document.getElementById("tools-type-filter");n&&n.addEventListener("input",()=>{"tools"===this.uiStateManager.getCurrentTab()&&this.renderCurrentTab()}),i&&i.addEventListener("change",()=>{"tools"===this.uiStateManager.getCurrentTab()&&this.renderCurrentTab()});const s=document.getElementById("files-search-input"),o=document.getElementById("files-type-filter");s&&s.addEventListener("input",()=>{"files"===this.uiStateManager.getCurrentTab()&&this.renderCurrentTab()}),o&&o.addEventListener("change",()=>{"files"===this.uiStateManager.getCurrentTab()&&this.renderCurrentTab()})}initializeFromURL(){const e=new URLSearchParams(window.location.search);this.socketManager.initializeFromURL(e)}renderCurrentTab(){const e=this.uiStateManager.getCurrentTab();switch(e){case"events":break;case"activity":if(window.ActivityTree&&"function"==typeof window.ActivityTree)window.activityTreeInstance||(console.log("Creating new ActivityTree instance..."),window.activityTreeInstance=new window.ActivityTree),window.activityTreeInstance&&(window.activityTreeInstance.initialized||(console.log("Initializing ActivityTree..."),window.activityTreeInstance.initialize()),"function"==typeof window.activityTreeInstance.renderWhenVisible&&(console.log("Dashboard triggering activity tree render..."),window.activityTreeInstance.renderWhenVisible()),"function"==typeof window.activityTreeInstance.forceShow&&(console.log("Dashboard forcing activity tree to show..."),window.activityTreeInstance.forceShow()));else if(window.activityTree&&"function"==typeof window.activityTree){const e=window.activityTree();e&&("function"==typeof e.renderWhenVisible&&(console.log("Dashboard triggering activity tree render (legacy)..."),e.renderWhenVisible()),"function"==typeof e.forceShow&&(console.log("Dashboard forcing activity tree to show (legacy)..."),e.forceShow()))}else console.warn("Activity tree component not available, retrying in 100ms..."),setTimeout(()=>{"activity"===this.currentTab&&this.renderCurrentTab()},100);break;case"agents":this.renderAgents();break;case"tools":this.renderTools();break;case"files":this.renderFiles()}this.uiStateManager.getSelectedCard().tab===e&&this.uiStateManager.updateCardSelectionUI(),this.uiStateManager.updateUnifiedSelectionUI()}renderAgents(){const e=document.getElementById("agents-list");if(!e)return;const t=document.getElementById("agents-search-input")?.value||"",n=document.getElementById("agents-type-filter")?.value||"",i=this.renderAgentsFlat(t,n);e.innerHTML=i,this.removeHierarchyControls();const s=this.agentInference.getUniqueAgentInstances();this.updateAgentsFilterDropdowns(s)}removeHierarchyControls(){const e=document.getElementById("hierarchy-controls");e&&e.remove()}renderAgentsFlat(e,t){const n=this.eventViewer.events;if(!n||0===n.length)return'<div class="no-events">No agent events found...</div>';this.agentInference.processAgentInference();const i=this.agentInference.getEventAgentMap(),s=[];if(n.forEach((n,o)=>{const a=i.get(o);if(a&&("subagent"===a.type||"main_agent"===a.type)){let i=!0;if(e){const t=e.toLowerCase();i=i&&(a.agentName.toLowerCase().includes(t)||n.tool_name&&n.tool_name.toLowerCase().includes(t)||n.data&&JSON.stringify(n.data).toLowerCase().includes(t))}t&&(i=i&&a.agentName.includes(t)),i&&s.push({event:n,inference:a,index:o,timestamp:new Date(n.timestamp)})}}),0===s.length)return'<div class="no-events">No agent events match the current filters...</div>';return`<div class="agent-events-flat">${s.map((e,t)=>{const{event:n,inference:i,index:s,timestamp:o}=e;let a="Activity",r="📋",l="";if("SubagentStart"===n.event_type)a="Started",r="🟢",l="Agent session began";else if("SubagentStop"===n.event_type)a="Stopped",r="🔴",l="Agent session ended";else if(n.tool_name&&(a=`Tool: ${n.tool_name}`,r=this.getToolIcon(n.tool_name),n.data&&n.data.tool_parameters)){const e=n.data.tool_parameters;e.file_path?l=e.file_path:e.command?l=e.command.substring(0,50)+(e.command.length>50?"...":""):e.pattern?l=`pattern="${e.pattern}"`:e.query&&(l=`query="${e.query}"`)}let d="completed";return"SubagentStart"===n.event_type?d="active":n.data&&n.data.error&&(d="error"),`\n <div class="agent-event-item" data-index="${t}" onclick="window.dashboard.showCardDetails('agents', ${s})">\n <div class="agent-event-header">\n <div class="agent-event-time">${this.formatTimestamp(o)}</div>\n <div class="agent-event-agent">\n ${this.getAgentIcon(i.agentName)} ${i.agentName}\n </div>\n <div class="agent-event-action">\n ${r} ${a}\n </div>\n <div class="agent-event-status status-${d}">\n ${this.getStatusIcon(d)}\n </div>\n </div>\n ${l?`<div class="agent-event-details">${this.escapeHtml(l)}</div>`:""}\n </div>\n `}).join("")}</div>`}getAgentIcon(e){return{PM:"🎯","Engineer Agent":"🔧","Research Agent":"🔍","QA Agent":"✅","Documentation Agent":"📝","Security Agent":"🔒","Ops Agent":"⚙️","Version Control Agent":"📦","Data Engineer Agent":"💾","Test Integration Agent":"🧪"}[e]||"🤖"}getToolIcon(e){return{Read:"📖",Write:"✏️",Edit:"📝",Bash:"💻",Grep:"🔍",Glob:"📂",LS:"📁",Task:"📋"}[e]||"🔧"}getStatusIcon(e){return{active:"🟢",completed:"✅",error:"❌",pending:"🟡"}[e]||"❓"}formatTimestamp(e){return e.toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1})}escapeHtml(e){if(!e)return"";const t=document.createElement("div");return t.textContent=e,t.innerHTML}renderTools(){const e=document.getElementById("tools-list");if(!e)return;const t=this.fileToolTracker.getToolCalls(),n=Array.from(t.entries()),i=this.eventProcessor.getUniqueToolInstances(n),s=this.eventProcessor.generateToolHTML(i);e.innerHTML=s,this.exportManager.scrollListToBottom("tools-list"),this.updateToolsFilterDropdowns(i)}renderFiles(){const e=document.getElementById("files-list");if(!e)return;const t=this.fileToolTracker.getFileOperations(),n=Array.from(t.entries()),i=this.eventProcessor.getUniqueFileInstances(n),s=this.eventProcessor.generateFileHTML(i);e.innerHTML=s,this.exportManager.scrollListToBottom("files-list"),this.updateFilesFilterDropdowns(n)}updateAgentsFilterDropdowns(e){const t=new Set;e.forEach(e=>{e.agentName&&"Unknown"!==e.agentName&&t.add(e.agentName)});const n=Array.from(t).filter(e=>e&&""!==e.trim());this.populateFilterDropdown("agents-type-filter",n,"All Agent Types"),n.length>0?console.log("Agent types found for filter:",n):console.log("No agent types found for filter. Instances:",e.length)}updateToolsFilterDropdowns(e){const t=[...new Set(e.map(([e,t])=>t.tool_name))].filter(e=>e);this.populateFilterDropdown("tools-type-filter",t,"All Tools")}updateFilesFilterDropdowns(e){const t=[...new Set(e.flatMap(([e,t])=>t.operations.map(e=>e.operation)))].filter(e=>e);this.populateFilterDropdown("files-type-filter",t,"All Operations")}populateFilterDropdown(e,t,n="All"){const i=document.getElementById(e);if(!i)return;const s=i.value,o=t.sort((e,t)=>e.localeCompare(t));i.innerHTML=`<option value="">${n}</option>`,o.forEach(e=>{const t=document.createElement("option");t.value=e,t.textContent=e,i.appendChild(t)}),s&&o.includes(s)&&(i.value=s)}showCardDetails(e,t){switch(e){case"events":this.eventViewer&&this.eventViewer.showEventDetails(t);break;case"agents":this.showAgentDetailsByIndex(t);break;case"tools":this.showToolDetailsByIndex(t);break;case"files":this.showFileDetailsByIndex(t)}}showAgentDetailsByIndex(e){const t=this.eventProcessor.getFilteredEventsForTab("agents");if(!t||!Array.isArray(t)||e<0||e>=t.length)return void console.warn("Dashboard: Invalid agent index or events array");const n=this.eventProcessor.applyAgentsFilters([t[e]]);if(n.length>0&&this.moduleViewer&&"function"==typeof this.moduleViewer.showAgentEvent){const t=n[0];this.moduleViewer.showAgentEvent(t,e)}}showAgentInstanceDetails(e){const t=this.agentInference.getPMDelegations().get(e);if(!t){const t=this.agentInference.getUniqueAgentInstances().find(t=>t.id===e);return t?void this.showImpliedAgentDetails(t):void console.error("Agent instance not found:",e)}this.moduleViewer&&"function"==typeof this.moduleViewer.showAgentInstance?this.moduleViewer.showAgentInstance(t):console.log("Agent Instance Details:",{id:e,agentName:t.agentName,type:"PM Delegation",eventCount:t.agentEvents.length,startTime:t.timestamp,pmCall:t.pmCall})}showImpliedAgentDetails(e){this.moduleViewer&&"function"==typeof this.moduleViewer.showImpliedAgent?this.moduleViewer.showImpliedAgent(e):console.log("Implied Agent Details:",{id:e.id,agentName:e.agentName,type:"Implied PM Delegation",eventCount:e.eventCount,startTime:e.timestamp,note:"No explicit PM call found - inferred from agent activity"})}showToolDetailsByIndex(e){const t=this.fileToolTracker.getToolCalls(),n=Array.from(t.entries()),i=this.eventProcessor.applyToolCallFilters(n);if(e>=0&&e<i.length){const[t]=i[e];this.showToolCallDetails(t)}}showFileDetailsByIndex(e){const t=this.fileToolTracker.getFileOperations();let n=Array.from(t.entries());if(n=this.eventProcessor.applyFilesFilters(n),e>=0&&e<n.length){const[t]=n[e];this.showFileDetails(t)}}showToolCallDetails(e){const t=this.fileToolTracker.getToolCall(e);t&&this.moduleViewer&&this.moduleViewer.showToolCall(t,e)}showFileDetails(e){const t=this.fileToolTracker.getFileOperationsForFile(e);t&&this.moduleViewer&&this.moduleViewer.showFileOperations(t,e)}switchTab(e){this.uiStateManager.switchTab(e)}selectCard(e,t,n,i){this.uiStateManager.selectCard(e,t,n,i)}clearEvents(){this.exportManager.clearEvents()}exportEvents(){this.exportManager.exportEvents()}clearSelection(){this.uiStateManager.clearSelection(),this.eventViewer&&this.eventViewer.clearSelection(),this.moduleViewer&&this.moduleViewer.clear()}get currentWorkingDir(){return this.workingDirectoryManager.getCurrentWorkingDir()}set currentWorkingDir(e){this.workingDirectoryManager.setWorkingDirectory(e)}get currentTab(){return this.uiStateManager.getCurrentTab()}get selectedCard(){return this.uiStateManager.getSelectedCard()}get fileOperations(){return this.fileToolTracker.getFileOperations()}get toolCalls(){return this.fileToolTracker.getToolCalls()}get tabNavigation(){return this.uiStateManager?this.uiStateManager.tabNavigation:null}}function p(){const e=document.createElement("div");return e.id="file-viewer-modal",e.className="modal file-viewer-modal",e.innerHTML='\n <div class="modal-content file-viewer-content">\n <div class="file-viewer-header">\n <h2 class="file-viewer-title">\n <span class="file-viewer-icon">📄</span>\n <span class="file-viewer-title-text">File Viewer</span>\n </h2>\n <div class="file-viewer-meta">\n <span class="file-viewer-file-path"></span>\n <span class="file-viewer-file-size"></span>\n </div>\n <button class="file-viewer-close" onclick="hideFileViewerModal()">\n <span>×</span>\n </button>\n </div>\n <div class="file-viewer-body">\n <div class="file-viewer-loading">\n <div class="loading-spinner"></div>\n <span>Loading file content...</span>\n </div>\n <div class="file-viewer-error" style="display: none;">\n <div class="error-icon">⚠️</div>\n <div class="error-message"></div>\n <div class="error-suggestions"></div>\n </div>\n <div class="file-viewer-content-area" style="display: none;">\n <div class="file-viewer-toolbar">\n <div class="file-viewer-info">\n <span class="file-extension"></span>\n <span class="file-encoding"></span>\n </div>\n <div class="file-viewer-actions">\n <button class="file-content-copy" onclick="copyFileContent()">\n 📋 Copy\n </button>\n </div>\n </div>\n <div class="file-viewer-scroll-wrapper">\n <pre class="file-content-display"><code class="file-content-code"></code></pre>\n </div>\n </div>\n </div>\n </div>\n ',e.addEventListener("click",t=>{t.target===e&&hideFileViewerModal()}),document.addEventListener("keydown",t=>{"Escape"===t.key&&"flex"===e.style.display&&hideFileViewerModal()}),e}async function g(e,t,n){const i=e.querySelector(".file-viewer-file-path"),s=e.querySelector(".file-viewer-file-size");i.textContent=t,s.textContent="",e.querySelector(".file-viewer-loading").style.display="flex",e.querySelector(".file-viewer-error").style.display="none",e.querySelector(".file-viewer-content-area").style.display="none";try{const i=window.socket||window.dashboard?.socketClient?.socket;if(!i)throw new Error("No socket connection available");const s=new Promise((e,n)=>{const s=o=>{o.file_path===t&&(i.off("file_content_response",s),o.success?e(o):n(new Error(o.error||"Failed to read file")))};i.on("file_content_response",s),setTimeout(()=>{i.off("file_content_response",s),n(new Error("Request timeout"))},1e4)});i.emit("read_file",{file_path:t,working_dir:n});const o=await s;e.querySelector(".file-viewer-loading").style.display="none",function(e,t){const n=e.querySelector(".file-viewer-content-area"),i=e.querySelector(".file-extension"),s=e.querySelector(".file-encoding"),o=e.querySelector(".file-viewer-file-size"),a=e.querySelector(".file-content-code");i&&(i.textContent=`Type: ${t.extension||"unknown"}`);s&&(s.textContent=`Encoding: ${t.encoding||"unknown"}`);o&&(o.textContent=`Size: ${function(e){if(!e)return"0 B";const t=1024,n=["B","KB","MB","GB"],i=Math.floor(Math.log(e)/Math.log(t));return parseFloat((e/Math.pow(t,i)).toFixed(2))+" "+n[i]}(t.file_size)}`);if(a&&t.content){a.innerHTML=function(e,t){const n=e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");switch(t){case".js":case".jsx":case".ts":case".tsx":return function(e){return m(e.replace(/\b(function|const|let|var|if|else|for|while|return|import|export|class|extends)\b/g,'<span class="keyword">$1</span>').replace(/(\/\*[\s\S]*?\*\/|\/\/.*)/g,'<span class="comment">$1</span>').replace(/('[^']*'|"[^"]*"|`[^`]*`)/g,'<span class="string">$1</span>').replace(/\b(\d+)\b/g,'<span class="number">$1</span>'))}(n);case".py":return function(e){return m(e.replace(/\b(def|class|if|elif|else|for|while|return|import|from|as|try|except|finally|with)\b/g,'<span class="keyword">$1</span>').replace(/(#.*)/g,'<span class="comment">$1</span>').replace(/('[^']*'|"[^"]*"|"""[\s\S]*?""")/g,'<span class="string">$1</span>').replace(/\b(\d+)\b/g,'<span class="number">$1</span>'))}(n);case".json":return function(e){return m(e.replace(/("[\w\s]*")\s*:/g,'<span class="property">$1</span>:').replace(/:\s*(".*?")/g,': <span class="string">$1</span>').replace(/:\s*(\d+)/g,': <span class="number">$1</span>').replace(/:\s*(true|false|null)/g,': <span class="keyword">$1</span>'))}(n);case".css":return function(e){return m(e.replace(/([.#]?[\w-]+)\s*\{/g,'<span class="selector">$1</span> {').replace(/([\w-]+)\s*:/g,'<span class="property">$1</span>:').replace(/:\s*([^;]+);/g,': <span class="value">$1</span>;').replace(/(\/\*[\s\S]*?\*\/)/g,'<span class="comment">$1</span>'))}(n);case".html":case".htm":return function(e){return m(e.replace(/(<\/?[\w-]+)/g,'<span class="tag">$1</span>').replace(/([\w-]+)=(['"][^'"]*['"])/g,'<span class="attribute">$1</span>=<span class="string">$2</span>').replace(/(<!--[\s\S]*?-->)/g,'<span class="comment">$1</span>'))}(n);case".md":case".markdown":return function(e){return m(e.replace(/^(#{1,6})\s+(.*)$/gm,'<span class="header">$1</span> <span class="header-text">$2</span>').replace(/\*\*(.*?)\*\*/g,'<span class="bold">**$1**</span>').replace(/\*(.*?)\*/g,'<span class="italic">*$1*</span>').replace(/`([^`]+)`/g,'<span class="code">`$1`</span>').replace(/^\s*[-*+]\s+(.*)$/gm,'<span class="list-marker">•</span> $1'))}(n);default:return m(n)}}(t.content,t.extension);const n=e.querySelector(".file-viewer-scroll-wrapper");n&&setTimeout(()=>{const t=e.querySelector(".modal-content"),i=e.querySelector(".file-viewer-header"),s=e.querySelector(".file-viewer-toolbar"),o=(t?.offsetHeight||0)-(i?.offsetHeight||0)-(s?.offsetHeight||0)-40;n.style.maxHeight=`${o}px`,n.style.overflowY="auto"},50)}else console.warn("⚠️ Missing codeElement or file content");n&&(n.style.display="block")}(e,o)}catch(o){console.error("❌ Failed to fetch file content:",o),e.querySelector(".file-viewer-loading").style.display="none";let i=o.message||"Unknown error occurred",s=[];o.message.includes("No socket connection")?(i="Failed to connect to the monitoring server",s=["Check if the monitoring server is running","Verify the socket connection in the dashboard","Try refreshing the page and reconnecting"]):o.message.includes("timeout")?(i="Request timed out",s=["The file may be too large to load quickly","Check your network connection","Try again in a few moments"]):o.message.includes("File does not exist")?(i="File not found",s=["The file may have been moved or deleted","Check the file path spelling","Refresh the file list to see current files"]):o.message.includes("Access denied")&&(i="Access denied",s=["The file is outside the allowed directories","File access is restricted for security reasons"]),function(e,t){const n=e.querySelector(".file-viewer-error"),i=e.querySelector(".error-message"),s=e.querySelector(".error-suggestions");let o=t.error||"Unknown error occurred";i.innerHTML=`\n <div class="error-main">${o}</div>\n ${t.file_path?`<div class="error-file">File: ${t.file_path}</div>`:""}\n ${t.working_dir?`<div class="error-dir">Working directory: ${t.working_dir}</div>`:""}\n `,t.suggestions&&t.suggestions.length>0?s.innerHTML=`\n <h4>Suggestions:</h4>\n <ul>\n ${t.suggestions.map(e=>`<li>${e}</li>`).join("")}\n </ul>\n `:s.innerHTML="";console.log("📋 Displaying file viewer error:",{originalError:t.error,processedMessage:o,suggestions:t.suggestions}),n.style.display="block"}(e,{error:i,file_path:t,working_dir:n,suggestions:s})}}function m(e){return e.split("\n").map((e,t)=>`<span class="line-number">${String(t+1).padStart(3," ")}</span> ${e||" "}`).join("\n")}function f(e,t){const n=e.querySelector(".git-diff-error"),i=e.querySelector(".error-message"),s=e.querySelector(".error-suggestions");let o=t.error||"Unknown error occurred",a=!1;if(o.includes("not tracked by git")?(o="📝 This file is not tracked by git yet",a=!0):o.includes("No git history found")&&(o="📋 No git history available for this file"),i.innerHTML=`\n <div class="error-main">${o}</div>\n ${t.file_path?`<div class="error-file">File: ${t.file_path}</div>`:""}\n ${t.working_dir?`<div class="error-dir">Working directory: ${t.working_dir}</div>`:""}\n `,t.suggestions&&t.suggestions.length>0){const e=a?"How to track this file:":"Suggestions:";s.innerHTML=`\n <h4>${e}</h4>\n <ul>\n ${t.suggestions.map(e=>`<li>${e}</li>`).join("")}\n </ul>\n `}else s.innerHTML="";console.log("📋 Displaying git diff error:",{originalError:t.error,processedMessage:o,isUntracked:a,suggestions:t.suggestions}),n.style.display="block"}function v(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}window.clearEvents=function(){window.dashboard&&window.dashboard.clearEvents()},window.exportEvents=function(){window.dashboard&&window.dashboard.exportEvents()},window.clearSelection=function(){window.dashboard&&window.dashboard.clearSelection()},window.switchTab=function(e){window.dashboard&&window.dashboard.switchTab(e)},window.showFileViewerModal=function(e,t){!t&&window.dashboard&&window.dashboard.currentWorkingDir&&(t=window.dashboard.currentWorkingDir);let n=document.getElementById("file-viewer-modal");n||(n=p(),document.body.appendChild(n)),g(n,e,t),n.style.display="flex",document.body.style.overflow="hidden"},window.hideFileViewerModal=function(){const e=document.getElementById("file-viewer-modal");e&&(e.style.display="none",document.body.style.overflow="")},window.copyFileContent=function(){const e=document.getElementById("file-viewer-modal");if(!e)return;const t=e.querySelector(".file-content-code");if(!t)return;const n=t.textContent;if(navigator.clipboard&&navigator.clipboard.writeText)navigator.clipboard.writeText(n).then(()=>{const t=e.querySelector(".file-content-copy"),n=t.textContent;t.textContent="✅ Copied!",setTimeout(()=>{t.textContent=n},2e3)}).catch(e=>{console.error("Failed to copy text:",e)});else{const t=document.createElement("textarea");t.value=n,document.body.appendChild(t),t.select(),document.execCommand("copy"),document.body.removeChild(t);const i=e.querySelector(".file-content-copy"),s=i.textContent;i.textContent="✅ Copied!",setTimeout(()=>{i.textContent=s},2e3)}},window.showGitDiffModal=function(e,t,n){!n&&window.dashboard&&window.dashboard.currentWorkingDir&&(n=window.dashboard.currentWorkingDir);let i=document.getElementById("git-diff-modal");i||(i=function(){const e=document.createElement("div");return e.id="git-diff-modal",e.className="modal git-diff-modal",e.innerHTML='\n <div class="modal-content git-diff-content">\n <div class="git-diff-header">\n <h2 class="git-diff-title">\n <span class="git-diff-icon">📋</span>\n <span class="git-diff-title-text">Git Diff</span>\n </h2>\n <div class="git-diff-meta">\n <span class="git-diff-file-path"></span>\n <span class="git-diff-timestamp"></span>\n </div>\n <button class="git-diff-close" onclick="hideGitDiffModal()">\n <span>×</span>\n </button>\n </div>\n <div class="git-diff-body">\n <div class="git-diff-loading">\n <div class="loading-spinner"></div>\n <span>Loading git diff...</span>\n </div>\n <div class="git-diff-error" style="display: none;">\n <div class="error-icon">⚠️</div>\n <div class="error-message"></div>\n <div class="error-suggestions"></div>\n </div>\n <div class="git-diff-content-area" style="display: none;">\n <div class="git-diff-toolbar">\n <div class="git-diff-info">\n <span class="commit-hash"></span>\n <span class="diff-method"></span>\n </div>\n <div class="git-diff-actions">\n <button class="git-diff-copy" onclick="copyGitDiff()">\n 📋 Copy\n </button>\n </div>\n </div>\n <div class="git-diff-scroll-wrapper">\n <pre class="git-diff-display"><code class="git-diff-code"></code></pre>\n </div>\n </div>\n </div>\n </div>\n ',e.addEventListener("click",t=>{t.target===e&&hideGitDiffModal()}),document.addEventListener("keydown",t=>{"Escape"===t.key&&"flex"===e.style.display&&hideGitDiffModal()}),e}(),document.body.appendChild(i)),async function(e,t,n,i){const s=e.querySelector(".git-diff-file-path"),o=e.querySelector(".git-diff-timestamp");s.textContent=t,o.textContent=n?new Date(n).toLocaleString():"Latest",e.querySelector(".git-diff-loading").style.display="flex",e.querySelector(".git-diff-error").style.display="none",e.querySelector(".git-diff-content-area").style.display="none";try{let s=8765;if(window.dashboard&&window.dashboard.socketClient&&window.dashboard.socketClient.port)s=window.dashboard.socketClient.port;else{const e=document.getElementById("port-input");e&&e.value&&(s=e.value)}const o=new URLSearchParams({file:t});n&&o.append("timestamp",n),i&&o.append("working_dir",i);const r=`http://localhost:${s}/api/git-diff?${o}`;console.log("🌐 Making git diff request to:",r),console.log("📋 Git diff request parameters:",{filePath:t,timestamp:n,workingDir:i,urlParams:o.toString()});try{const e=await fetch(`http://localhost:${s}/health`,{method:"GET",headers:{Accept:"application/json","Content-Type":"application/json"},mode:"cors"});if(!e.ok)throw new Error(`Server health check failed: ${e.status} ${e.statusText}`)}catch(a){throw new Error(`Cannot reach server at localhost:${s}. Health check failed: ${a.message}`)}const l=await fetch(r,{method:"GET",headers:{Accept:"application/json","Content-Type":"application/json"},mode:"cors"});if(!l.ok)throw new Error(`HTTP ${l.status}: ${l.statusText}`);const d=await l.json();e.querySelector(".git-diff-loading").style.display="none",d.success?function(e,t){const n=e.querySelector(".git-diff-content-area"),i=e.querySelector(".commit-hash"),s=e.querySelector(".diff-method"),o=e.querySelector(".git-diff-code");i&&(i.textContent=`Commit: ${t.commit_hash}`);s&&(s.textContent=`Method: ${t.method}`);if(o&&t.diff){o.innerHTML=t.diff.split("\n").map(e=>{const t=e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");return e.startsWith("+++")||e.startsWith("---")?`<span class="diff-header">${t}</span>`:e.startsWith("@@")?`<span class="diff-meta">${t}</span>`:e.startsWith("+")?`<span class="diff-addition">${t}</span>`:e.startsWith("-")?`<span class="diff-deletion">${t}</span>`:e.startsWith("commit ")||e.startsWith("Author:")||e.startsWith("Date:")?`<span class="diff-header">${t}</span>`:`<span class="diff-context">${t}</span>`}).join("\n");const n=e.querySelector(".git-diff-scroll-wrapper");n&&setTimeout(()=>{const t=e.querySelector(".modal-content"),i=e.querySelector(".git-diff-header"),s=e.querySelector(".git-diff-toolbar"),o=(t?.offsetHeight||0)-(i?.offsetHeight||0)-(s?.offsetHeight||0)-40;n.style.maxHeight=`${o}px`,n.style.overflowY="auto"},50)}else console.warn("⚠️ Missing codeElement or diff data");n&&(n.style.display="block")}(e,d):f(e,d)}catch(r){console.error("❌ Failed to fetch git diff:",r),console.error("Error details:",{name:r.name,message:r.message,stack:r.stack,filePath:t,timestamp:n,workingDir:i}),e.querySelector(".git-diff-loading").style.display="none";let s=`Network error: ${r.message}`,o=[];r.message.includes("Failed to fetch")?(s="Failed to connect to the monitoring server",o=["Check if the monitoring server is running on port 8765","Verify the port configuration in the dashboard","Check browser console for CORS or network errors","Try refreshing the page and reconnecting"]):r.message.includes("health check failed")?(s=r.message,o=["The server may be starting up - try again in a few seconds","Check if another process is using port 8765","Restart the claude-mpm monitoring server"]):r.message.includes("HTTP")&&(s=`Server error: ${r.message}`,o=["The server encountered an internal error","Check the server logs for more details","Try with a different file or working directory"]),f(e,{error:s,file_path:t,working_dir:i,suggestions:o,debug_info:{error_type:r.name,original_message:r.message,port:window.dashboard?.socketClient?.port||document.getElementById("port-input")?.value||"8765",timestamp:(new Date).toISOString()}})}}(i,e,t,n),i.style.display="flex",document.body.style.overflow="hidden"},window.hideGitDiffModal=function(){const e=document.getElementById("git-diff-modal");e&&(e.style.display="none",document.body.style.overflow="")},window.copyGitDiff=function(){const e=document.getElementById("git-diff-modal");if(!e)return;const t=e.querySelector(".git-diff-code");if(!t)return;const n=t.textContent;if(navigator.clipboard&&navigator.clipboard.writeText)navigator.clipboard.writeText(n).then(()=>{const t=e.querySelector(".git-diff-copy"),n=t.textContent;t.textContent="✅ Copied!",setTimeout(()=>{t.textContent=n},2e3)}).catch(e=>{console.error("Failed to copy text:",e)});else{const t=document.createElement("textarea");t.value=n,document.body.appendChild(t),t.select(),document.execCommand("copy"),document.body.removeChild(t);const i=e.querySelector(".git-diff-copy"),s=i.textContent;i.textContent="✅ Copied!",setTimeout(()=>{i.textContent=s},2e3)}},window.showFileViewerModal=function(e){let t="";window.dashboard&&window.dashboard.currentWorkingDir&&(t=window.dashboard.currentWorkingDir);let n=document.getElementById("file-viewer-modal");n||(n=p(),document.body.appendChild(n)),g(n,e,t),n.style.display="flex",document.body.style.overflow="hidden"},window.hideFileViewerModal=function(){const e=document.getElementById("file-viewer-modal");e&&(e.style.display="none",document.body.style.overflow="")},window.copyFileContent=function(){const e=document.getElementById("file-viewer-modal");if(!e)return;const t=e.querySelector(".file-content-code");if(!t)return;const n=t.textContent;if(navigator.clipboard&&navigator.clipboard.writeText)navigator.clipboard.writeText(n).then(()=>{const t=e.querySelector(".file-content-copy"),n=t.textContent;t.textContent="✅ Copied!",setTimeout(()=>{t.textContent=n},2e3)}).catch(e=>{console.error("Failed to copy text:",e)});else{const t=document.createElement("textarea");t.value=n,document.body.appendChild(t),t.select(),document.execCommand("copy"),document.body.removeChild(t);const i=e.querySelector(".file-content-copy"),s=i.textContent;i.textContent="✅ Copied!",setTimeout(()=>{i.textContent=s},2e3)}},window.showSearchViewerModal=function(e,t){let n=document.getElementById("search-viewer-modal");n||(n=function(){const e=document.createElement("div");return e.id="search-viewer-modal",e.className="modal search-viewer-modal",e.innerHTML='\n <div class="modal-content search-viewer-content">\n <div class="search-viewer-header">\n <h2 class="search-viewer-title">\n <span class="search-viewer-icon">🔍</span>\n <span class="search-viewer-title-text">Search Results</span>\n </h2>\n <button class="search-viewer-close" onclick="hideSearchViewerModal()">\n <span>×</span>\n </button>\n </div>\n <div class="search-viewer-body">\n <div class="search-params-section">\n <h3>Search Parameters</h3>\n <pre class="search-params-display"></pre>\n </div>\n <div class="search-results-section">\n <h3>Search Results</h3>\n <div class="search-results-display"></div>\n </div>\n </div>\n </div>\n ',e.addEventListener("click",t=>{t.target===e&&hideSearchViewerModal()}),document.addEventListener("keydown",t=>{"Escape"===t.key&&"flex"===e.style.display&&hideSearchViewerModal()}),e}(),document.body.appendChild(n)),function(e,t,n){const i=e.querySelector(".search-params-display"),s=e.querySelector(".search-results-display");i&&t&&(i.textContent=JSON.stringify(t,null,2));if(s&&n){let e="";"string"==typeof n?e=`<pre class="search-results-text">${v(n)}</pre>`:Array.isArray(n)?(e='<ul class="search-results-list">',n.forEach(t=>{e+="object"==typeof t?`<li><pre>${JSON.stringify(t,null,2)}</pre></li>`:`<li>${v(String(t))}</li>`}),e+="</ul>"):e="object"==typeof n?`<pre class="search-results-json">${JSON.stringify(n,null,2)}</pre>`:`<div class="search-results-text">${v(String(n))}</div>`,s.innerHTML=e}}(n,e,t),n.style.display="flex",document.body.style.overflow="hidden"},window.hideSearchViewerModal=function(){const e=document.getElementById("search-viewer-modal");e&&(e.style.display="none",document.body.style.overflow="")},window.showAgentInstanceDetails=function(e){window.dashboard&&"function"==typeof window.dashboard.showAgentInstanceDetails?window.dashboard.showAgentInstanceDetails(e):console.error("Dashboard not available or method not found")},document.addEventListener("DOMContentLoaded",function(){try{window.dashboard=new u,window.dashboard&&"function"==typeof window.dashboard.postInit&&window.dashboard.postInit(),console.log("Dashboard loaded and initialized successfully"),document.dispatchEvent(new CustomEvent("dashboardReady",{detail:{dashboard:window.dashboard}}))}catch(e){console.error("Failed to initialize dashboard:",e),document.body.innerHTML=`\n <div style="padding: 20px; font-family: sans-serif;">\n <h1>Dashboard Initialization Error</h1>\n <p>The dashboard failed to load properly. Please refresh the page or check the console for details.</p>\n <pre style="background: #f5f5f5; padding: 10px; border-radius: 4px;">${e.message}</pre>\n </div>\n `}});
|
|
1
|
+
import{S as e,U as t}from"./socket-client.js";import{E as n,a as i}from"./components/event-viewer.js";import{M as s}from"./components/module-viewer.js";import{S as o}from"./components/session-manager.js";import{A as a}from"./components/agent-inference.js";import{E as r}from"./components/export-manager.js";import{W as l}from"./components/working-directory.js";import{F as d}from"./components/file-tool-tracker.js";import"./chunks/unified-data-viewer.B7wmm2bD.js";class c{constructor(e,t){this.agentInference=e,this.eventViewer=t,this.state={hierarchyTree:null,nodeMap:new Map,expandedNodes:new Set,selectedNode:null},this.expandAll=!0,this.setupEventListeners(),console.log("Agent hierarchy component initialized")}setupEventListeners(){document.addEventListener("click",e=>{const t=e.target.closest("[data-toggle-node]");if(t&&window.dashboard&&window.dashboard.agentHierarchy){const e=t.dataset.toggleNode;window.dashboard.agentHierarchy.toggleNode(e)}})}buildHierarchy(){this.agentInference.processAgentInference();const e=this.agentInference.getPMDelegations(),t=this.eventViewer.events,n=this.agentInference.getEventAgentMap(),i={id:"pm_main",type:"pm",name:"PM (Main Session)",children:[],events:[],eventCount:0,status:"active",startTime:null,endTime:null,expanded:!0},s=new Map;this.state.nodeMap.clear(),this.state.nodeMap.set(i.id,i);const o=new Set;for(const[h,u]of e){const e={id:h,type:"subagent",name:u.agentName,delegationContext:this.extractDelegationContext(u.pmCall),children:[],events:u.agentEvents,eventCount:u.agentEvents.length,status:u.endIndex?"completed":"active",startTime:u.timestamp,endTime:u.endIndex?t[u.endIndex]?.timestamp:null,startIndex:u.startIndex,endIndex:u.endIndex,expanded:this.expandAll||this.state.expandedNodes.has(h)};if(i.children.push(e),this.state.nodeMap.set(h,e),o.add(u.agentName),i.eventCount++,(!i.startTime||new Date(u.timestamp)<new Date(i.startTime))&&(i.startTime=u.timestamp),u.endIndex&&t[u.endIndex]){const e=t[u.endIndex].timestamp;(!i.endTime||new Date(e)>new Date(i.endTime))&&(i.endTime=e)}}const a=this.agentInference.getOrphanGroups();let r=1;for(const[h,u]of a){const i={id:`pm_implied_${h}`,type:"pm",name:`PM (Implied #${r})`,children:[],events:[],eventCount:0,status:"inferred",startTime:null,endTime:null,expanded:!0,isImplied:!0,tooltip:"Inferred PM - Subagents started without explicit PM delegation"};s.set(h,i),this.state.nodeMap.set(i.id,i),r++;const o=new Map;for(const s of u){const i=[];t.forEach((t,o)=>{const a=n.get(o);if(a&&a.agentName===s.agentName){let n=!0;for(const[t,i]of e)if(i.agentEvents.some(e=>e.eventIndex===o)){n=!1;break}n&&i.push({eventIndex:o,event:t,inference:a})}}),i.length>0&&(o.has(s.agentName)||o.set(s.agentName,[]),o.get(s.agentName).push(...i))}for(const[e,t]of o){if(0===t.length)continue;const n=t[0].event,s=t[t.length-1].event,o={id:`implied_agent_${h}_${e}`,type:"subagent",name:e,delegationContext:"Orphan agent - no explicit PM delegation found",children:[],events:t,eventCount:t.length,status:"inferred",startTime:n.timestamp,endTime:s.timestamp,startIndex:t[0].eventIndex,endIndex:t[t.length-1].eventIndex,expanded:this.expandAll,isImplied:!0,tooltip:"This agent was spawned without an explicit PM Task delegation"};i.children.push(o),this.state.nodeMap.set(o.id,o),i.eventCount+=t.length,(!i.startTime||new Date(n.timestamp)<new Date(i.startTime))&&(i.startTime=n.timestamp),(!i.endTime||new Date(s.timestamp)>new Date(i.endTime))&&(i.endTime=s.timestamp)}}const l=[];if(t.forEach((t,i)=>{const o=n.get(i);if(o&&"subagent"===o.type){let n=!0;for(const[t,s]of e)if(s.agentEvents.some(e=>e.eventIndex===i)){n=!1;break}if(n)for(const[e,t]of s){for(const e of t.children)if(e.events.some(e=>e.eventIndex===i)){n=!1;break}if(!n)break}n&&l.push({eventIndex:i,event:t,inference:o})}}),l.length>0){const e={id:"pm_implied_generic",type:"pm",name:"PM (Implied - Uncategorized)",children:[],events:[],eventCount:0,status:"inferred",startTime:null,endTime:null,expanded:!0,isImplied:!0,tooltip:"Orphan agents without clear grouping"},t=new Map;for(const n of l){const e=n.inference.agentName;t.has(e)||t.set(e,[]),t.get(e).push(n)}for(const[n,i]of t){const t=i[0].event,s=i[i.length-1].event,o={id:`implied_generic_${n}`,type:"subagent",name:n,delegationContext:"Uncategorized orphan agent",children:[],events:i,eventCount:i.length,status:"inferred",startTime:t.timestamp,endTime:s.timestamp,startIndex:i[0].eventIndex,endIndex:i[i.length-1].eventIndex,expanded:this.expandAll,isImplied:!0};e.children.push(o),this.state.nodeMap.set(o.id,o),e.eventCount+=i.length,(!e.startTime||new Date(t.timestamp)<new Date(e.startTime))&&(e.startTime=t.timestamp),(!e.endTime||new Date(s.timestamp)>new Date(e.endTime))&&(e.endTime=s.timestamp)}e.children.length>0&&(s.set("generic",e),this.state.nodeMap.set(e.id,e))}let d=0;if(t.forEach((e,t)=>{const s=n.get(t);s&&"main_agent"===s.type&&(d++,i.events.push({eventIndex:t,event:e,inference:s}))}),i.eventCount+=d,i.children.length>0){const e=i.children.some(e=>"active"===e.status);i.status=e?"active":"completed"}const c={roots:[]};(i.eventCount>0||i.children.length>0)&&c.roots.push(i);for(const[h,u]of s)u.children.length>0&&c.roots.push(u);return this.state.hierarchyTree=c,console.log("Hierarchy built:",{mainPM:{children:i.children.length,events:i.eventCount,ownEvents:d},impliedPMGroups:s.size,totalImpliedAgents:Array.from(s.values()).reduce((e,t)=>e+t.children.length,0)}),c}extractDelegationContext(e){if(!e)return"Unknown delegation";const t=e.tool_parameters||e.data?.tool_parameters||{},n=t.task||t.request||t.description;if(n){const e=100;return n.length>e?n.substring(0,e)+"...":n}const i=e.tool_input||e.data?.tool_input;if(i&&"string"==typeof i){const e=100;return i.length>e?i.substring(0,e)+"...":i}return"Task delegation"}render(e={}){const t=this.state.hierarchyTree||this.buildHierarchy();if(!t.roots||0===t.roots.length)return'<div class="agent-hierarchy-empty">No agent activity detected</div>';return`<div class="agent-hierarchy">${this.applyFilters(t,e).roots.map(e=>this.renderNode(e,0)).join("")}</div>`}renderNode(e,t){const n=e.expanded||this.state.expandedNodes.has(e.id),i=e.children&&e.children.length>0,s=this.state.selectedNode===e.id,o=this.getNodeIcon(e),a=i?n?"▼":"▶":" ",r=this.getStatusClass(e.status),l=e.isImplied?"agent-node-implied":"",d=e.tooltip?`title="${this.escapeHtml(e.tooltip)}"`:"";let c=`\n <div class="agent-node agent-node-level-${t} ${s?"agent-node-selected":""} ${l}" \n data-node-id="${e.id}" ${d}>\n <div class="agent-node-header ${r}" \n data-toggle-node="${e.id}" style="cursor: pointer">\n <span class="agent-node-expand">${a}</span>\n <span class="agent-node-icon">${o}</span>\n <span class="agent-node-name">${this.escapeHtml(e.name)}</span>\n <span class="agent-node-stats">\n <span class="agent-event-count">${e.eventCount} events</span>\n ${e.status?`<span class="agent-status">${e.status}</span>`:""}\n </span>\n </div>\n `;if(n&&(e.delegationContext||e.startTime)){if(c+='<div class="agent-node-details">',e.delegationContext&&"Unknown delegation"!==e.delegationContext&&(c+=`\n <div class="agent-delegation-context">\n <strong>Task:</strong> ${this.escapeHtml(e.delegationContext)}\n </div>\n `),e.startTime){const t=this.calculateDuration(e.startTime,e.endTime);c+=`\n <div class="agent-timing">\n <span class="agent-time-start">${this.formatTime(e.startTime)}</span>\n ${t?`<span class="agent-duration">(${t})</span>`:""}\n </div>\n `}c+="</div>"}return n&&i&&(c+='<div class="agent-node-children">',c+=e.children.map(e=>this.renderNode(e,t+1)).join(""),c+="</div>"),c+="</div>",c}getNodeIcon(e){if("pm"===e.type)return e.isImplied?"🔍":"👔";return{"Engineer Agent":"🔧","Research Agent":"🔍","QA Agent":"✅","Documentation Agent":"📝","Security Agent":"🔒","Ops Agent":"⚙️","Version Control Agent":"📦","Data Engineer Agent":"💾","Test Integration Agent":"🧪"}[e.name]||"🤖"}getStatusClass(e){switch(e){case"active":return"agent-status-active";case"completed":return"agent-status-completed";case"pending":return"agent-status-pending";case"inferred":return"agent-status-inferred";default:return"agent-status-unknown"}}toggleNode(e){const t=this.state.nodeMap.get(e);t&&(this.state.expandedNodes.has(e)?(this.state.expandedNodes.delete(e),t.expanded=!1):(this.state.expandedNodes.add(e),t.expanded=!0),window.dashboard&&window.dashboard.renderCurrentTab())}selectNode(e){this.state.selectedNode=e;const t=this.state.nodeMap.get(e);if(t){const e=new CustomEvent("agentNodeSelected",{detail:{node:t}});document.dispatchEvent(e)}}applyFilters(e,t){if(!t||0===Object.keys(t).length)return e;const n={roots:[]};for(const i of e.roots){const e=this.filterNode(i,t);e&&n.roots.push(e)}return n}filterNode(e,t){let n=!0;if(t.searchText){const i=t.searchText.toLowerCase();n=n&&(e.name.toLowerCase().includes(i)||e.delegationContext&&e.delegationContext.toLowerCase().includes(i))}t.agentType&&(n=n&&e.name.includes(t.agentType)),t.status&&(n=n&&e.status===t.status);let i=[];if(e.children)for(const s of e.children){const e=this.filterNode(s,t);e&&i.push(e)}return n||i.length>0?{...e,children:i}:null}formatTime(e){if(!e)return"";return new Date(e).toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1})}calculateDuration(e,t){if(!e||!t)return"";const n=new Date(e).getTime(),i=new Date(t).getTime()-n;if(i<1e3)return`${i}ms`;if(i<6e4)return`${(i/1e3).toFixed(1)}s`;return`${Math.floor(i/6e4)}m ${Math.floor(i%6e4/1e3)}s`}escapeHtml(e){if(!e)return"";const t=document.createElement("div");return t.textContent=e,t.innerHTML}updateWithNewEvents(e){this.buildHierarchy()}clear(){this.state.hierarchyTree=null,this.state.nodeMap.clear(),this.state.expandedNodes.clear(),this.state.selectedNode=null}expandAllNodes(){for(const[e,t]of this.state.nodeMap)this.state.expandedNodes.add(e),t.expanded=!0;this.expandAll=!0}collapseAllNodes(){this.state.expandedNodes.clear();for(const[e,t]of this.state.nodeMap)t.expanded=!1;this.expandAll=!1}}class h{constructor(){this.element=null,this.buildInfo={monitor:{version:"1.0.0",build:1,formatted_build:"0001",full_version:"v1.0.0-0001"},mpm:{version:"unknown",build:"unknown",full_version:"v0.0.0"}},this.socketClient=null,this.init()}async init(){await this.loadDashboardVersion(),this.createElements(),this.setupEventListeners()}async loadDashboardVersion(){try{const e=await fetch("/version.json");if(e.ok){const t=await e.json();this.buildInfo.monitor={version:t.version||"1.0.0",build:t.build||1,formatted_build:t.formatted_build||"0001",full_version:t.full_version||"v1.0.0-0001"}}}catch(e){}}createElements(){this.element=document.createElement("div"),this.element.className="version-display",this.element.id="version-display";const e=document.createElement("span");e.className="version-item mpm-version",e.id="mpm-version",e.innerHTML='\n <span class="version-label">MPM</span>\n <span class="version-value">v0.0.0</span>\n ';const t=document.createElement("span");t.className="version-separator",t.textContent="|";const n=document.createElement("span");n.className="version-item monitor-version",n.id="monitor-version",n.innerHTML='\n <span class="version-label">Monitor</span>\n <span class="version-value">v1.0.0-0001</span>\n ',this.element.appendChild(e),this.element.appendChild(t),this.element.appendChild(n),this.element.title="Click for detailed version information"}setSocketClient(e){this.socketClient=e,this.socketClient&&this.socketClient.socket&&(this.socketClient.socket.on("welcome",e=>{const t=e.build_info||e.data&&e.data.build_info;t&&this.updateBuildInfo(t)}),this.socketClient.socket.on("status",e=>{const t=e.build_info||e.data&&e.data.build_info;t&&this.updateBuildInfo(t)}),this.socketClient.socket.on("build_info",e=>{this.updateBuildInfo(e)}))}updateBuildInfo(e){this.buildInfo=e,this.updateDisplay()}updateDisplay(){const e=this.element.querySelector(".mpm-version .version-value");if(e&&this.buildInfo.mpm){const t=this.buildInfo.mpm.full_version||`v${this.buildInfo.mpm.version}`;e.textContent=t,this.buildInfo.mpm.build&&"unknown"!==this.buildInfo.mpm.build&&(e.parentElement.title=`MPM Build: ${this.buildInfo.mpm.build}`)}const t=this.element.querySelector(".monitor-version .version-value");if(t&&this.buildInfo.monitor){const e=this.buildInfo.monitor.full_version||`v${this.buildInfo.monitor.version}-${this.buildInfo.monitor.formatted_build}`;if(t.textContent=e,this.buildInfo.monitor.last_updated){const e=new Date(this.buildInfo.monitor.last_updated).toLocaleString();t.parentElement.title=`Monitor Build: ${this.buildInfo.monitor.formatted_build}\nLast Updated: ${e}`}}}setupEventListeners(){this.element.addEventListener("click",()=>{this.showDetailedInfo()})}showDetailedInfo(){const e=[];if(this.buildInfo.mpm&&(e.push("=== MPM Framework ==="),e.push(`Version: ${this.buildInfo.mpm.version}`),this.buildInfo.mpm.build&&"unknown"!==this.buildInfo.mpm.build&&e.push(`Build: ${this.buildInfo.mpm.build}`),e.push(`Full: ${this.buildInfo.mpm.full_version}`)),e.push(""),this.buildInfo.monitor&&(e.push("=== Monitor UI ==="),e.push(`Version: ${this.buildInfo.monitor.version}`),e.push(`Build: ${this.buildInfo.monitor.formatted_build} (${this.buildInfo.monitor.build})`),e.push(`Full: ${this.buildInfo.monitor.full_version}`),this.buildInfo.monitor.last_updated)){const t=new Date(this.buildInfo.monitor.last_updated).toLocaleString();e.push(`Updated: ${t}`)}const t=document.createElement("div");t.className="version-modal",t.innerHTML=`\n <div class="version-modal-content">\n <h3>Version Information</h3>\n <pre>${e.join("\n")}</pre>\n <button onclick="this.parentElement.parentElement.remove()">Close</button>\n </div>\n `,document.body.appendChild(t),setTimeout(()=>{t.remove()},1e4)}mount(e){const t="string"==typeof e?document.querySelector(e):e;this.element&&t&&this.element.parentNode!==t&&t.appendChild(this.element)}getElement(){return this.element}destroy(){this.element&&this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.socketClient&&this.socketClient.socket&&(this.socketClient.socket.off("welcome"),this.socketClient.socket.off("status"),this.socketClient.socket.off("build_info")),this.element=null,this.socketClient=null}}class u{constructor(){this.eventViewer=null,this.moduleViewer=null,this.sessionManager=null,this.socketManager=null,this.agentInference=null,this.agentHierarchy=null,this.uiStateManager=null,this.eventProcessor=null,this.exportManager=null,this.workingDirectoryManager=null,this.fileToolTracker=null,this.buildTracker=null,this.init()}init(){console.log("Initializing refactored Claude MPM Dashboard...");try{this.initializeSocketManager(),this.initializeCoreComponents(),this.initializeBuildTracker(),this.initializeAgentInference(),this.initializeAgentHierarchy(),this.initializeUIStateManager(),this.initializeWorkingDirectoryManager(),this.initializeFileToolTracker(),this.initializeEventProcessor(),this.initializeExportManager(),this.setupModuleInteractions(),this.initializeFromURL(),console.log("Claude MPM Dashboard initialized successfully")}catch(e){throw console.error("Error during dashboard initialization:",e),e}}validateInitialization(){const e=[{name:"socketManager",component:this.socketManager},{name:"eventViewer",component:this.eventViewer},{name:"agentHierarchy",component:this.agentHierarchy}].filter(e=>!e.component);e.length>0?console.warn("Missing critical components:",e.map(e=>e.name)):console.log("All critical components initialized")}postInit(){try{this.agentHierarchy&&(window.dashboard.agentHierarchy=this.agentHierarchy,console.log("Agent hierarchy global reference set")),this.validateInitialization()}catch(e){console.error("Error in dashboard postInit:",e)}}initializeSocketManager(){this.socketManager=new e,this.socketManager.setupConnectionControls(),this.socketClient=this.socketManager.getSocketClient(),window.socketClient=this.socketClient}initializeCoreComponents(){this.eventViewer=new n("events-list",this.socketClient),this.moduleViewer=new s,this.sessionManager=new o(this.socketClient),window.eventViewer=this.eventViewer,window.moduleViewer=this.moduleViewer,window.sessionManager=this.sessionManager}initializeBuildTracker(){this.buildTracker=new h,this.buildTracker.setSocketClient(this.socketClient);const e=()=>{const t=document.querySelector(".header-title");t?(this.buildTracker.mount(t),console.log("BuildTracker mounted successfully")):(console.warn("Header-title element not found for build tracker, will retry"),setTimeout(e,100))};e(),window.buildTracker=this.buildTracker}initializeAgentInference(){this.agentInference=new a(this.eventViewer),this.agentInference.initialize()}initializeAgentHierarchy(){try{this.agentHierarchy=new c(this.agentInference,this.eventViewer),console.log("Agent hierarchy component created")}catch(e){console.error("Failed to initialize agent hierarchy:",e),this.agentHierarchy={render:()=>'<div class="error">Agent hierarchy unavailable</div>',expandAllNodes:()=>{},collapseAllNodes:()=>{},updateWithNewEvents:()=>{}}}}initializeUIStateManager(){this.uiStateManager=new t,this.setupTabFilters()}initializeWorkingDirectoryManager(){this.workingDirectoryManager=new l(this.socketManager)}initializeFileToolTracker(){this.fileToolTracker=new d(this.agentInference,this.workingDirectoryManager)}initializeEventProcessor(){this.eventProcessor=new i(this.eventViewer,this.agentInference)}initializeExportManager(){this.exportManager=new r(this.eventViewer)}setupModuleInteractions(){this.socketManager.onEventUpdate(e=>{this.fileToolTracker.updateFileOperations(e),this.fileToolTracker.updateToolCalls(e),this.agentInference.processAgentInference(),this.agentHierarchy.updateWithNewEvents(e),"events"===this.uiStateManager.getCurrentTab()&&this.exportManager.scrollListToBottom("events-list"),this.renderCurrentTab()}),this.socketManager.onConnectionStatusChange((e,t)=>{"connected"===t&&this.workingDirectoryManager.updateGitBranch(this.workingDirectoryManager.getCurrentWorkingDir())}),document.addEventListener("tabChanged",e=>{this.renderCurrentTab(),this.uiStateManager.updateTabNavigationItems()}),document.addEventListener("eventsClearing",()=>{this.fileToolTracker.clear(),this.agentInference.initialize()}),document.addEventListener("showCardDetails",e=>{this.showCardDetails(e.detail.tabName,e.detail.index)}),document.addEventListener("sessionFilterChanged",e=>{console.log("Session filter changed, re-rendering current tab:",this.uiStateManager.getCurrentTab()),this.renderCurrentTab()})}setupTabFilters(){const e=document.getElementById("agents-search-input"),t=document.getElementById("agents-type-filter");e&&e.addEventListener("input",()=>{"agents"===this.uiStateManager.getCurrentTab()&&this.renderCurrentTab()}),t&&t.addEventListener("change",()=>{"agents"===this.uiStateManager.getCurrentTab()&&this.renderCurrentTab()});const n=document.getElementById("tools-search-input"),i=document.getElementById("tools-type-filter");n&&n.addEventListener("input",()=>{"tools"===this.uiStateManager.getCurrentTab()&&this.renderCurrentTab()}),i&&i.addEventListener("change",()=>{"tools"===this.uiStateManager.getCurrentTab()&&this.renderCurrentTab()});const s=document.getElementById("files-search-input"),o=document.getElementById("files-type-filter");s&&s.addEventListener("input",()=>{"files"===this.uiStateManager.getCurrentTab()&&this.renderCurrentTab()}),o&&o.addEventListener("change",()=>{"files"===this.uiStateManager.getCurrentTab()&&this.renderCurrentTab()})}initializeFromURL(){const e=new URLSearchParams(window.location.search);this.socketManager.initializeFromURL(e)}renderCurrentTab(){const e=this.uiStateManager.getCurrentTab();switch(e){case"events":break;case"activity":if(window.ActivityTree&&"function"==typeof window.ActivityTree)window.activityTreeInstance||(console.log("Creating new ActivityTree instance..."),window.activityTreeInstance=new window.ActivityTree),window.activityTreeInstance&&(window.activityTreeInstance.initialized||(console.log("Initializing ActivityTree..."),window.activityTreeInstance.initialize()),"function"==typeof window.activityTreeInstance.renderWhenVisible&&(console.log("Dashboard triggering activity tree render..."),window.activityTreeInstance.renderWhenVisible()),"function"==typeof window.activityTreeInstance.forceShow&&(console.log("Dashboard forcing activity tree to show..."),window.activityTreeInstance.forceShow()));else if(window.activityTree&&"function"==typeof window.activityTree){const e=window.activityTree();e&&("function"==typeof e.renderWhenVisible&&(console.log("Dashboard triggering activity tree render (legacy)..."),e.renderWhenVisible()),"function"==typeof e.forceShow&&(console.log("Dashboard forcing activity tree to show (legacy)..."),e.forceShow()))}else console.warn("Activity tree component not available, retrying in 100ms..."),setTimeout(()=>{"activity"===this.currentTab&&this.renderCurrentTab()},100);break;case"agents":this.renderAgents();break;case"tools":this.renderTools();break;case"files":this.renderFiles()}this.uiStateManager.getSelectedCard().tab===e&&this.uiStateManager.updateCardSelectionUI(),this.uiStateManager.updateUnifiedSelectionUI()}renderAgents(){const e=document.getElementById("agents-list");if(!e)return;const t=document.getElementById("agents-search-input")?.value||"",n=document.getElementById("agents-type-filter")?.value||"",i=this.renderAgentsFlat(t,n);e.innerHTML=i,this.removeHierarchyControls();const s=this.agentInference.getUniqueAgentInstances();this.updateAgentsFilterDropdowns(s)}removeHierarchyControls(){const e=document.getElementById("hierarchy-controls");e&&e.remove()}renderAgentsFlat(e,t){const n=this.eventViewer.events;if(!n||0===n.length)return'<div class="no-events">No agent events found...</div>';this.agentInference.processAgentInference();const i=this.agentInference.getEventAgentMap(),s=[];if(n.forEach((n,o)=>{const a=i.get(o);if(a&&("subagent"===a.type||"main_agent"===a.type)){let i=!0;if(e){const t=e.toLowerCase();i=i&&(a.agentName.toLowerCase().includes(t)||n.tool_name&&n.tool_name.toLowerCase().includes(t)||n.data&&JSON.stringify(n.data).toLowerCase().includes(t))}t&&(i=i&&a.agentName.includes(t)),i&&s.push({event:n,inference:a,index:o,timestamp:new Date(n.timestamp)})}}),0===s.length)return'<div class="no-events">No agent events match the current filters...</div>';return`<div class="agent-events-flat">${s.map((e,t)=>{const{event:n,inference:i,index:s,timestamp:o}=e;let a="Activity",r="📋",l="";if("SubagentStart"===n.event_type)a="Started",r="🟢",l="Agent session began";else if("SubagentStop"===n.event_type)a="Stopped",r="🔴",l="Agent session ended";else if(n.tool_name&&(a=`Tool: ${n.tool_name}`,r=this.getToolIcon(n.tool_name),n.data&&n.data.tool_parameters)){const e=n.data.tool_parameters;e.file_path?l=e.file_path:e.command?l=e.command.substring(0,50)+(e.command.length>50?"...":""):e.pattern?l=`pattern="${e.pattern}"`:e.query&&(l=`query="${e.query}"`)}let d="completed";return"SubagentStart"===n.event_type?d="active":n.data&&n.data.error&&(d="error"),`\n <div class="agent-event-item" data-index="${t}" onclick="window.dashboard.showCardDetails('agents', ${s})">\n <div class="agent-event-header">\n <div class="agent-event-time">${this.formatTimestamp(o)}</div>\n <div class="agent-event-agent">\n ${this.getAgentIcon(i.agentName)} ${i.agentName}\n </div>\n <div class="agent-event-action">\n ${r} ${a}\n </div>\n <div class="agent-event-status status-${d}">\n ${this.getStatusIcon(d)}\n </div>\n </div>\n ${l?`<div class="agent-event-details">${this.escapeHtml(l)}</div>`:""}\n </div>\n `}).join("")}</div>`}getAgentIcon(e){return{PM:"🎯","Engineer Agent":"🔧","Research Agent":"🔍","QA Agent":"✅","Documentation Agent":"📝","Security Agent":"🔒","Ops Agent":"⚙️","Version Control Agent":"📦","Data Engineer Agent":"💾","Test Integration Agent":"🧪"}[e]||"🤖"}getToolIcon(e){return{Read:"📖",Write:"✏️",Edit:"📝",Bash:"💻",Grep:"🔍",Glob:"📂",LS:"📁",Task:"📋"}[e]||"🔧"}getStatusIcon(e){return{active:"🟢",completed:"✅",error:"❌",pending:"🟡"}[e]||"❓"}formatTimestamp(e){return e.toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1})}escapeHtml(e){if(!e)return"";const t=document.createElement("div");return t.textContent=e,t.innerHTML}renderTools(){const e=document.getElementById("tools-list");if(!e)return;const t=this.fileToolTracker.getToolCalls(),n=Array.from(t.entries()),i=this.eventProcessor.getUniqueToolInstances(n),s=this.eventProcessor.generateToolHTML(i);e.innerHTML=s,this.exportManager.scrollListToBottom("tools-list"),this.updateToolsFilterDropdowns(i)}renderFiles(){const e=document.getElementById("files-list");if(!e)return;const t=this.fileToolTracker.getFileOperations(),n=Array.from(t.entries()),i=this.eventProcessor.getUniqueFileInstances(n),s=this.eventProcessor.generateFileHTML(i);e.innerHTML=s,this.exportManager.scrollListToBottom("files-list"),this.updateFilesFilterDropdowns(n)}updateAgentsFilterDropdowns(e){const t=new Set;e.forEach(e=>{e.agentName&&"Unknown"!==e.agentName&&t.add(e.agentName)});const n=Array.from(t).filter(e=>e&&""!==e.trim());this.populateFilterDropdown("agents-type-filter",n,"All Agent Types"),n.length>0?console.log("Agent types found for filter:",n):console.log("No agent types found for filter. Instances:",e.length)}updateToolsFilterDropdowns(e){const t=[...new Set(e.map(([e,t])=>t.tool_name))].filter(e=>e);this.populateFilterDropdown("tools-type-filter",t,"All Tools")}updateFilesFilterDropdowns(e){const t=[...new Set(e.flatMap(([e,t])=>t.operations.map(e=>e.operation)))].filter(e=>e);this.populateFilterDropdown("files-type-filter",t,"All Operations")}populateFilterDropdown(e,t,n="All"){const i=document.getElementById(e);if(!i)return;const s=i.value,o=t.sort((e,t)=>e.localeCompare(t));i.innerHTML=`<option value="">${n}</option>`,o.forEach(e=>{const t=document.createElement("option");t.value=e,t.textContent=e,i.appendChild(t)}),s&&o.includes(s)&&(i.value=s)}showCardDetails(e,t){switch(e){case"events":this.eventViewer&&this.eventViewer.showEventDetails(t);break;case"agents":this.showAgentDetailsByIndex(t);break;case"tools":this.showToolDetailsByIndex(t);break;case"files":this.showFileDetailsByIndex(t)}}showAgentDetailsByIndex(e){const t=this.eventProcessor.getFilteredEventsForTab("agents");if(!t||!Array.isArray(t)||e<0||e>=t.length)return void console.warn("Dashboard: Invalid agent index or events array");const n=this.eventProcessor.applyAgentsFilters([t[e]]);if(n.length>0&&this.moduleViewer&&"function"==typeof this.moduleViewer.showAgentEvent){const t=n[0];this.moduleViewer.showAgentEvent(t,e)}}showAgentInstanceDetails(e){const t=this.agentInference.getPMDelegations().get(e);if(!t){const t=this.agentInference.getUniqueAgentInstances().find(t=>t.id===e);return t?void this.showImpliedAgentDetails(t):void console.error("Agent instance not found:",e)}this.moduleViewer&&"function"==typeof this.moduleViewer.showAgentInstance?this.moduleViewer.showAgentInstance(t):console.log("Agent Instance Details:",{id:e,agentName:t.agentName,type:"PM Delegation",eventCount:t.agentEvents.length,startTime:t.timestamp,pmCall:t.pmCall})}showImpliedAgentDetails(e){this.moduleViewer&&"function"==typeof this.moduleViewer.showImpliedAgent?this.moduleViewer.showImpliedAgent(e):console.log("Implied Agent Details:",{id:e.id,agentName:e.agentName,type:"Implied PM Delegation",eventCount:e.eventCount,startTime:e.timestamp,note:"No explicit PM call found - inferred from agent activity"})}showToolDetailsByIndex(e){const t=this.fileToolTracker.getToolCalls(),n=Array.from(t.entries()),i=this.eventProcessor.applyToolCallFilters(n);if(e>=0&&e<i.length){const[t]=i[e];this.showToolCallDetails(t)}}showFileDetailsByIndex(e){const t=this.fileToolTracker.getFileOperations();let n=Array.from(t.entries());if(n=this.eventProcessor.applyFilesFilters(n),e>=0&&e<n.length){const[t]=n[e];this.showFileDetails(t)}}showToolCallDetails(e){const t=this.fileToolTracker.getToolCall(e);t&&this.moduleViewer&&this.moduleViewer.showToolCall(t,e)}showFileDetails(e){const t=this.fileToolTracker.getFileOperationsForFile(e);t&&this.moduleViewer&&this.moduleViewer.showFileOperations(t,e)}switchTab(e){this.uiStateManager.switchTab(e)}selectCard(e,t,n,i){this.uiStateManager.selectCard(e,t,n,i)}clearEvents(){this.exportManager.clearEvents()}exportEvents(){this.exportManager.exportEvents()}clearSelection(){this.uiStateManager.clearSelection(),this.eventViewer&&this.eventViewer.clearSelection(),this.moduleViewer&&this.moduleViewer.clear()}get currentWorkingDir(){return this.workingDirectoryManager.getCurrentWorkingDir()}set currentWorkingDir(e){this.workingDirectoryManager.setWorkingDirectory(e)}get currentTab(){return this.uiStateManager.getCurrentTab()}get selectedCard(){return this.uiStateManager.getSelectedCard()}get fileOperations(){return this.fileToolTracker.getFileOperations()}get toolCalls(){return this.fileToolTracker.getToolCalls()}get tabNavigation(){return this.uiStateManager?this.uiStateManager.tabNavigation:null}}function p(){const e=document.createElement("div");return e.id="file-viewer-modal",e.className="modal file-viewer-modal",e.innerHTML='\n <div class="modal-content file-viewer-content">\n <div class="file-viewer-header">\n <h2 class="file-viewer-title">\n <span class="file-viewer-icon">📄</span>\n <span class="file-viewer-title-text">File Viewer</span>\n </h2>\n <div class="file-viewer-meta">\n <span class="file-viewer-file-path"></span>\n <span class="file-viewer-file-size"></span>\n </div>\n <button class="file-viewer-close" onclick="hideFileViewerModal()">\n <span>×</span>\n </button>\n </div>\n <div class="file-viewer-body">\n <div class="file-viewer-loading">\n <div class="loading-spinner"></div>\n <span>Loading file content...</span>\n </div>\n <div class="file-viewer-error" style="display: none;">\n <div class="error-icon">⚠️</div>\n <div class="error-message"></div>\n <div class="error-suggestions"></div>\n </div>\n <div class="file-viewer-content-area" style="display: none;">\n <div class="file-viewer-toolbar">\n <div class="file-viewer-info">\n <span class="file-extension"></span>\n <span class="file-encoding"></span>\n </div>\n <div class="file-viewer-actions">\n <button class="file-content-copy" onclick="copyFileContent()">\n 📋 Copy\n </button>\n </div>\n </div>\n <div class="file-viewer-scroll-wrapper">\n <pre class="file-content-display"><code class="file-content-code"></code></pre>\n </div>\n </div>\n </div>\n </div>\n ',e.addEventListener("click",t=>{t.target===e&&hideFileViewerModal()}),document.addEventListener("keydown",t=>{"Escape"===t.key&&"flex"===e.style.display&&hideFileViewerModal()}),e}async function g(e,t,n){const i=e.querySelector(".file-viewer-file-path"),s=e.querySelector(".file-viewer-file-size");i.textContent=t,s.textContent="",e.querySelector(".file-viewer-loading").style.display="flex",e.querySelector(".file-viewer-error").style.display="none",e.querySelector(".file-viewer-content-area").style.display="none";try{const i=window.socket||window.dashboard?.socketClient?.socket;if(!i)throw new Error("No socket connection available");const s=new Promise((e,n)=>{const s=o=>{o.file_path===t&&(i.off("file_content_response",s),o.success?e(o):n(new Error(o.error||"Failed to read file")))};i.on("file_content_response",s),setTimeout(()=>{i.off("file_content_response",s),n(new Error("Request timeout"))},1e4)});i.emit("read_file",{file_path:t,working_dir:n});const o=await s;e.querySelector(".file-viewer-loading").style.display="none",function(e,t){const n=e.querySelector(".file-viewer-content-area"),i=e.querySelector(".file-extension"),s=e.querySelector(".file-encoding"),o=e.querySelector(".file-viewer-file-size"),a=e.querySelector(".file-content-code");i&&(i.textContent=`Type: ${t.extension||"unknown"}`);s&&(s.textContent=`Encoding: ${t.encoding||"unknown"}`);o&&(o.textContent=`Size: ${function(e){if(!e)return"0 B";const t=1024,n=["B","KB","MB","GB"],i=Math.floor(Math.log(e)/Math.log(t));return parseFloat((e/Math.pow(t,i)).toFixed(2))+" "+n[i]}(t.file_size)}`);if(a&&t.content){a.innerHTML=function(e,t){const n=e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");switch(t){case".js":case".jsx":case".ts":case".tsx":return function(e){return m(e.replace(/\b(function|const|let|var|if|else|for|while|return|import|export|class|extends)\b/g,'<span class="keyword">$1</span>').replace(/(\/\*[\s\S]*?\*\/|\/\/.*)/g,'<span class="comment">$1</span>').replace(/('[^']*'|"[^"]*"|`[^`]*`)/g,'<span class="string">$1</span>').replace(/\b(\d+)\b/g,'<span class="number">$1</span>'))}(n);case".py":return function(e){return m(e.replace(/\b(def|class|if|elif|else|for|while|return|import|from|as|try|except|finally|with)\b/g,'<span class="keyword">$1</span>').replace(/(#.*)/g,'<span class="comment">$1</span>').replace(/('[^']*'|"[^"]*"|"""[\s\S]*?""")/g,'<span class="string">$1</span>').replace(/\b(\d+)\b/g,'<span class="number">$1</span>'))}(n);case".json":return function(e){return m(e.replace(/("[\w\s]*")\s*:/g,'<span class="property">$1</span>:').replace(/:\s*(".*?")/g,': <span class="string">$1</span>').replace(/:\s*(\d+)/g,': <span class="number">$1</span>').replace(/:\s*(true|false|null)/g,': <span class="keyword">$1</span>'))}(n);case".css":return function(e){return m(e.replace(/([.#]?[\w-]+)\s*\{/g,'<span class="selector">$1</span> {').replace(/([\w-]+)\s*:/g,'<span class="property">$1</span>:').replace(/:\s*([^;]+);/g,': <span class="value">$1</span>;').replace(/(\/\*[\s\S]*?\*\/)/g,'<span class="comment">$1</span>'))}(n);case".html":case".htm":return function(e){return m(e.replace(/(<\/?[\w-]+)/g,'<span class="tag">$1</span>').replace(/([\w-]+)=(['"][^'"]*['"])/g,'<span class="attribute">$1</span>=<span class="string">$2</span>').replace(/(<!--[\s\S]*?-->)/g,'<span class="comment">$1</span>'))}(n);case".md":case".markdown":return function(e){return m(e.replace(/^(#{1,6})\s+(.*)$/gm,'<span class="header">$1</span> <span class="header-text">$2</span>').replace(/\*\*(.*?)\*\*/g,'<span class="bold">**$1**</span>').replace(/\*(.*?)\*/g,'<span class="italic">*$1*</span>').replace(/`([^`]+)`/g,'<span class="code">`$1`</span>').replace(/^\s*[-*+]\s+(.*)$/gm,'<span class="list-marker">•</span> $1'))}(n);default:return m(n)}}(t.content,t.extension);const n=e.querySelector(".file-viewer-scroll-wrapper");n&&setTimeout(()=>{const t=e.querySelector(".modal-content"),i=e.querySelector(".file-viewer-header"),s=e.querySelector(".file-viewer-toolbar"),o=(t?.offsetHeight||0)-(i?.offsetHeight||0)-(s?.offsetHeight||0)-40;n.style.maxHeight=`${o}px`,n.style.overflowY="auto"},50)}else console.warn("⚠️ Missing codeElement or file content");n&&(n.style.display="block")}(e,o)}catch(o){console.error("❌ Failed to fetch file content:",o),e.querySelector(".file-viewer-loading").style.display="none";let i=o.message||"Unknown error occurred",s=[];o.message.includes("No socket connection")?(i="Failed to connect to the monitoring server",s=["Check if the monitoring server is running","Verify the socket connection in the dashboard","Try refreshing the page and reconnecting"]):o.message.includes("timeout")?(i="Request timed out",s=["The file may be too large to load quickly","Check your network connection","Try again in a few moments"]):o.message.includes("File does not exist")?(i="File not found",s=["The file may have been moved or deleted","Check the file path spelling","Refresh the file list to see current files"]):o.message.includes("Access denied")&&(i="Access denied",s=["The file is outside the allowed directories","File access is restricted for security reasons"]),function(e,t){const n=e.querySelector(".file-viewer-error"),i=e.querySelector(".error-message"),s=e.querySelector(".error-suggestions");let o=t.error||"Unknown error occurred";i.innerHTML=`\n <div class="error-main">${o}</div>\n ${t.file_path?`<div class="error-file">File: ${t.file_path}</div>`:""}\n ${t.working_dir?`<div class="error-dir">Working directory: ${t.working_dir}</div>`:""}\n `,t.suggestions&&t.suggestions.length>0?s.innerHTML=`\n <h4>Suggestions:</h4>\n <ul>\n ${t.suggestions.map(e=>`<li>${e}</li>`).join("")}\n </ul>\n `:s.innerHTML="";console.log("📋 Displaying file viewer error:",{originalError:t.error,processedMessage:o,suggestions:t.suggestions}),n.style.display="block"}(e,{error:i,file_path:t,working_dir:n,suggestions:s})}}function m(e){return e.split("\n").map((e,t)=>`<span class="line-number">${String(t+1).padStart(3," ")}</span> ${e||" "}`).join("\n")}function f(e,t){const n=e.querySelector(".git-diff-error"),i=e.querySelector(".error-message"),s=e.querySelector(".error-suggestions");let o=t.error||"Unknown error occurred",a=!1;if(o.includes("not tracked by git")?(o="📝 This file is not tracked by git yet",a=!0):o.includes("No git history found")&&(o="📋 No git history available for this file"),i.innerHTML=`\n <div class="error-main">${o}</div>\n ${t.file_path?`<div class="error-file">File: ${t.file_path}</div>`:""}\n ${t.working_dir?`<div class="error-dir">Working directory: ${t.working_dir}</div>`:""}\n `,t.suggestions&&t.suggestions.length>0){const e=a?"How to track this file:":"Suggestions:";s.innerHTML=`\n <h4>${e}</h4>\n <ul>\n ${t.suggestions.map(e=>`<li>${e}</li>`).join("")}\n </ul>\n `}else s.innerHTML="";console.log("📋 Displaying git diff error:",{originalError:t.error,processedMessage:o,isUntracked:a,suggestions:t.suggestions}),n.style.display="block"}function v(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}window.clearEvents=function(){window.dashboard&&window.dashboard.clearEvents()},window.exportEvents=function(){window.dashboard&&window.dashboard.exportEvents()},window.clearSelection=function(){window.dashboard&&window.dashboard.clearSelection()},window.switchTab=function(e){window.dashboard&&window.dashboard.switchTab(e)},window.showFileViewerModal=function(e,t){!t&&window.dashboard&&window.dashboard.currentWorkingDir&&(t=window.dashboard.currentWorkingDir);let n=document.getElementById("file-viewer-modal");n||(n=p(),document.body.appendChild(n)),g(n,e,t),n.style.display="flex",document.body.style.overflow="hidden"},window.hideFileViewerModal=function(){const e=document.getElementById("file-viewer-modal");e&&(e.style.display="none",document.body.style.overflow="")},window.copyFileContent=function(){const e=document.getElementById("file-viewer-modal");if(!e)return;const t=e.querySelector(".file-content-code");if(!t)return;const n=t.textContent;if(navigator.clipboard&&navigator.clipboard.writeText)navigator.clipboard.writeText(n).then(()=>{const t=e.querySelector(".file-content-copy"),n=t.textContent;t.textContent="✅ Copied!",setTimeout(()=>{t.textContent=n},2e3)}).catch(e=>{console.error("Failed to copy text:",e)});else{const t=document.createElement("textarea");t.value=n,document.body.appendChild(t),t.select(),document.execCommand("copy"),document.body.removeChild(t);const i=e.querySelector(".file-content-copy"),s=i.textContent;i.textContent="✅ Copied!",setTimeout(()=>{i.textContent=s},2e3)}},window.showGitDiffModal=function(e,t,n){!n&&window.dashboard&&window.dashboard.currentWorkingDir&&(n=window.dashboard.currentWorkingDir);let i=document.getElementById("git-diff-modal");i||(i=function(){const e=document.createElement("div");return e.id="git-diff-modal",e.className="modal git-diff-modal",e.innerHTML='\n <div class="modal-content git-diff-content">\n <div class="git-diff-header">\n <h2 class="git-diff-title">\n <span class="git-diff-icon">📋</span>\n <span class="git-diff-title-text">Git Diff</span>\n </h2>\n <div class="git-diff-meta">\n <span class="git-diff-file-path"></span>\n <span class="git-diff-timestamp"></span>\n </div>\n <button class="git-diff-close" onclick="hideGitDiffModal()">\n <span>×</span>\n </button>\n </div>\n <div class="git-diff-body">\n <div class="git-diff-loading">\n <div class="loading-spinner"></div>\n <span>Loading git diff...</span>\n </div>\n <div class="git-diff-error" style="display: none;">\n <div class="error-icon">⚠️</div>\n <div class="error-message"></div>\n <div class="error-suggestions"></div>\n </div>\n <div class="git-diff-content-area" style="display: none;">\n <div class="git-diff-toolbar">\n <div class="git-diff-info">\n <span class="commit-hash"></span>\n <span class="diff-method"></span>\n </div>\n <div class="git-diff-actions">\n <button class="git-diff-copy" onclick="copyGitDiff()">\n 📋 Copy\n </button>\n </div>\n </div>\n <div class="git-diff-scroll-wrapper">\n <pre class="git-diff-display"><code class="git-diff-code"></code></pre>\n </div>\n </div>\n </div>\n </div>\n ',e.addEventListener("click",t=>{t.target===e&&hideGitDiffModal()}),document.addEventListener("keydown",t=>{"Escape"===t.key&&"flex"===e.style.display&&hideGitDiffModal()}),e}(),document.body.appendChild(i)),async function(e,t,n,i){const s=e.querySelector(".git-diff-file-path"),o=e.querySelector(".git-diff-timestamp");s.textContent=t,o.textContent=n?new Date(n).toLocaleString():"Latest",e.querySelector(".git-diff-loading").style.display="flex",e.querySelector(".git-diff-error").style.display="none",e.querySelector(".git-diff-content-area").style.display="none";try{let s=8765;if(window.dashboard&&window.dashboard.socketClient&&window.dashboard.socketClient.port)s=window.dashboard.socketClient.port;else{const e=document.getElementById("port-input");e&&e.value&&(s=e.value)}const o=new URLSearchParams({file:t});n&&o.append("timestamp",n),i&&o.append("working_dir",i);const r=`http://localhost:${s}/api/git-diff?${o}`;console.log("🌐 Making git diff request to:",r),console.log("📋 Git diff request parameters:",{filePath:t,timestamp:n,workingDir:i,urlParams:o.toString()});try{const e=await fetch(`http://localhost:${s}/health`,{method:"GET",headers:{Accept:"application/json","Content-Type":"application/json"},mode:"cors"});if(!e.ok)throw new Error(`Server health check failed: ${e.status} ${e.statusText}`)}catch(a){throw new Error(`Cannot reach server at localhost:${s}. Health check failed: ${a.message}`)}const l=await fetch(r,{method:"GET",headers:{Accept:"application/json","Content-Type":"application/json"},mode:"cors"});if(!l.ok)throw new Error(`HTTP ${l.status}: ${l.statusText}`);const d=await l.json();e.querySelector(".git-diff-loading").style.display="none",d.success?function(e,t){const n=e.querySelector(".git-diff-content-area"),i=e.querySelector(".commit-hash"),s=e.querySelector(".diff-method"),o=e.querySelector(".git-diff-code");i&&(i.textContent=`Commit: ${t.commit_hash}`);s&&(s.textContent=`Method: ${t.method}`);if(o&&t.diff){o.innerHTML=t.diff.split("\n").map(e=>{const t=e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");return e.startsWith("+++")||e.startsWith("---")?`<span class="diff-header">${t}</span>`:e.startsWith("@@")?`<span class="diff-meta">${t}</span>`:e.startsWith("+")?`<span class="diff-addition">${t}</span>`:e.startsWith("-")?`<span class="diff-deletion">${t}</span>`:e.startsWith("commit ")||e.startsWith("Author:")||e.startsWith("Date:")?`<span class="diff-header">${t}</span>`:`<span class="diff-context">${t}</span>`}).join("\n");const n=e.querySelector(".git-diff-scroll-wrapper");n&&setTimeout(()=>{const t=e.querySelector(".modal-content"),i=e.querySelector(".git-diff-header"),s=e.querySelector(".git-diff-toolbar"),o=(t?.offsetHeight||0)-(i?.offsetHeight||0)-(s?.offsetHeight||0)-40;n.style.maxHeight=`${o}px`,n.style.overflowY="auto"},50)}else console.warn("⚠️ Missing codeElement or diff data");n&&(n.style.display="block")}(e,d):f(e,d)}catch(r){console.error("❌ Failed to fetch git diff:",r),console.error("Error details:",{name:r.name,message:r.message,stack:r.stack,filePath:t,timestamp:n,workingDir:i}),e.querySelector(".git-diff-loading").style.display="none";let s=`Network error: ${r.message}`,o=[];r.message.includes("Failed to fetch")?(s="Failed to connect to the monitoring server",o=["Check if the monitoring server is running on port 8765","Verify the port configuration in the dashboard","Check browser console for CORS or network errors","Try refreshing the page and reconnecting"]):r.message.includes("health check failed")?(s=r.message,o=["The server may be starting up - try again in a few seconds","Check if another process is using port 8765","Restart the claude-mpm monitoring server"]):r.message.includes("HTTP")&&(s=`Server error: ${r.message}`,o=["The server encountered an internal error","Check the server logs for more details","Try with a different file or working directory"]),f(e,{error:s,file_path:t,working_dir:i,suggestions:o,debug_info:{error_type:r.name,original_message:r.message,port:window.dashboard?.socketClient?.port||document.getElementById("port-input")?.value||"8765",timestamp:(new Date).toISOString()}})}}(i,e,t,n),i.style.display="flex",document.body.style.overflow="hidden"},window.hideGitDiffModal=function(){const e=document.getElementById("git-diff-modal");e&&(e.style.display="none",document.body.style.overflow="")},window.copyGitDiff=function(){const e=document.getElementById("git-diff-modal");if(!e)return;const t=e.querySelector(".git-diff-code");if(!t)return;const n=t.textContent;if(navigator.clipboard&&navigator.clipboard.writeText)navigator.clipboard.writeText(n).then(()=>{const t=e.querySelector(".git-diff-copy"),n=t.textContent;t.textContent="✅ Copied!",setTimeout(()=>{t.textContent=n},2e3)}).catch(e=>{console.error("Failed to copy text:",e)});else{const t=document.createElement("textarea");t.value=n,document.body.appendChild(t),t.select(),document.execCommand("copy"),document.body.removeChild(t);const i=e.querySelector(".git-diff-copy"),s=i.textContent;i.textContent="✅ Copied!",setTimeout(()=>{i.textContent=s},2e3)}},window.showFileViewerModal=function(e){let t="";window.dashboard&&window.dashboard.currentWorkingDir&&(t=window.dashboard.currentWorkingDir);let n=document.getElementById("file-viewer-modal");n||(n=p(),document.body.appendChild(n)),g(n,e,t),n.style.display="flex",document.body.style.overflow="hidden"},window.hideFileViewerModal=function(){const e=document.getElementById("file-viewer-modal");e&&(e.style.display="none",document.body.style.overflow="")},window.copyFileContent=function(){const e=document.getElementById("file-viewer-modal");if(!e)return;const t=e.querySelector(".file-content-code");if(!t)return;const n=t.textContent;if(navigator.clipboard&&navigator.clipboard.writeText)navigator.clipboard.writeText(n).then(()=>{const t=e.querySelector(".file-content-copy"),n=t.textContent;t.textContent="✅ Copied!",setTimeout(()=>{t.textContent=n},2e3)}).catch(e=>{console.error("Failed to copy text:",e)});else{const t=document.createElement("textarea");t.value=n,document.body.appendChild(t),t.select(),document.execCommand("copy"),document.body.removeChild(t);const i=e.querySelector(".file-content-copy"),s=i.textContent;i.textContent="✅ Copied!",setTimeout(()=>{i.textContent=s},2e3)}},window.showSearchViewerModal=function(e,t){let n=document.getElementById("search-viewer-modal");n||(n=function(){const e=document.createElement("div");return e.id="search-viewer-modal",e.className="modal search-viewer-modal",e.innerHTML='\n <div class="modal-content search-viewer-content">\n <div class="search-viewer-header">\n <h2 class="search-viewer-title">\n <span class="search-viewer-icon">🔍</span>\n <span class="search-viewer-title-text">Search Results</span>\n </h2>\n <button class="search-viewer-close" onclick="hideSearchViewerModal()">\n <span>×</span>\n </button>\n </div>\n <div class="search-viewer-body">\n <div class="search-params-section">\n <h3>Search Parameters</h3>\n <pre class="search-params-display"></pre>\n </div>\n <div class="search-results-section">\n <h3>Search Results</h3>\n <div class="search-results-display"></div>\n </div>\n </div>\n </div>\n ',e.addEventListener("click",t=>{t.target===e&&hideSearchViewerModal()}),document.addEventListener("keydown",t=>{"Escape"===t.key&&"flex"===e.style.display&&hideSearchViewerModal()}),e}(),document.body.appendChild(n)),function(e,t,n){const i=e.querySelector(".search-params-display"),s=e.querySelector(".search-results-display");i&&t&&(i.textContent=JSON.stringify(t,null,2));if(s&&n){let e="";"string"==typeof n?e=`<pre class="search-results-text">${v(n)}</pre>`:Array.isArray(n)?(e='<ul class="search-results-list">',n.forEach(t=>{e+="object"==typeof t?`<li><pre>${JSON.stringify(t,null,2)}</pre></li>`:`<li>${v(String(t))}</li>`}),e+="</ul>"):e="object"==typeof n?`<pre class="search-results-json">${JSON.stringify(n,null,2)}</pre>`:`<div class="search-results-text">${v(String(n))}</div>`,s.innerHTML=e}}(n,e,t),n.style.display="flex",document.body.style.overflow="hidden"},window.hideSearchViewerModal=function(){const e=document.getElementById("search-viewer-modal");e&&(e.style.display="none",document.body.style.overflow="")},window.showAgentInstanceDetails=function(e){window.dashboard&&"function"==typeof window.dashboard.showAgentInstanceDetails?window.dashboard.showAgentInstanceDetails(e):console.error("Dashboard not available or method not found")},document.addEventListener("DOMContentLoaded",function(){try{window.dashboard=new u,window.dashboard&&"function"==typeof window.dashboard.postInit&&window.dashboard.postInit(),console.log("Dashboard loaded and initialized successfully"),document.dispatchEvent(new CustomEvent("dashboardReady",{detail:{dashboard:window.dashboard}}))}catch(e){console.error("Failed to initialize dashboard:",e),document.body.innerHTML=`\n <div style="padding: 20px; font-family: sans-serif;">\n <h1>Dashboard Initialization Error</h1>\n <p>The dashboard failed to load properly. Please refresh the page or check the console for details.</p>\n <pre style="background: #f5f5f5; padding: 10px; border-radius: 4px;">${e.message}</pre>\n </div>\n `}});
|
|
2
2
|
//# sourceMappingURL=dashboard.js.map
|
|
@@ -426,24 +426,12 @@ class UnifiedDataViewer {
|
|
|
426
426
|
if (todos.length > 0) {
|
|
427
427
|
const statusCounts = this.getTodoStatusCounts(todos);
|
|
428
428
|
|
|
429
|
-
// Status summary
|
|
429
|
+
// Status summary - horizontal single line format
|
|
430
430
|
html += `
|
|
431
|
-
<div class="todo-status-
|
|
432
|
-
<
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
<span class="status-label">Done</span>
|
|
436
|
-
</div>
|
|
437
|
-
<div class="status-item in_progress">
|
|
438
|
-
<span class="status-icon">🔄</span>
|
|
439
|
-
<span class="status-count">${statusCounts.in_progress}</span>
|
|
440
|
-
<span class="status-label">Active</span>
|
|
441
|
-
</div>
|
|
442
|
-
<div class="status-item pending">
|
|
443
|
-
<span class="status-icon">⏳</span>
|
|
444
|
-
<span class="status-count">${statusCounts.pending}</span>
|
|
445
|
-
<span class="status-label">Pending</span>
|
|
446
|
-
</div>
|
|
431
|
+
<div class="todo-status-line">
|
|
432
|
+
<span class="status-inline">✅ ${statusCounts.completed} Done</span>
|
|
433
|
+
<span class="status-inline">🔄 ${statusCounts.in_progress} Active</span>
|
|
434
|
+
<span class="status-inline">⏳ ${statusCounts.pending} Pending</span>
|
|
447
435
|
</div>
|
|
448
436
|
`;
|
|
449
437
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=toytnNjkIKPgQaGwDqQdC1rpNTAdSEc6Vja50d7Ovug,4
|
|
2
|
-
claude_mpm/VERSION,sha256=
|
|
2
|
+
claude_mpm/VERSION,sha256=6LRtTefrGK5zvl6gbr7jlidPvfCnlzPAt-1I1YdYbEQ,7
|
|
3
3
|
claude_mpm/__init__.py,sha256=lyTZAYGH4DTaFGLRNWJKk5Q5oTjzN5I6AXmfVX-Jff0,1512
|
|
4
4
|
claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
|
|
5
5
|
claude_mpm/constants.py,sha256=I946iCQzIIPRZVVJ8aO7lA4euiyDnNw2IX7EelAOkIE,5915
|
|
@@ -189,14 +189,14 @@ claude_mpm/dashboard/static/built/components/session-manager.js,sha256=ZG__csNeB
|
|
|
189
189
|
claude_mpm/dashboard/static/built/components/socket-manager.js,sha256=LEqCS0_EABAsQVUfb6WQk152mSicK2k039zne5cKR8A,131
|
|
190
190
|
claude_mpm/dashboard/static/built/components/ui-state-manager.js,sha256=PtZs6sxNhqMMFwdlr04kDKQbgOvxtMHdjSh56KR2ZBo,134
|
|
191
191
|
claude_mpm/dashboard/static/built/components/working-directory.js,sha256=xE1ydpKDzRWrzHzfjrCiq5wbf3vDhmgQTGKS7myQcPM,15790
|
|
192
|
-
claude_mpm/dashboard/static/css/activity.css,sha256=
|
|
192
|
+
claude_mpm/dashboard/static/css/activity.css,sha256=0SJUwGB6fr4OlVqMwhvVKrPndg2R3QTZPnN6DkAo4aU,37987
|
|
193
193
|
claude_mpm/dashboard/static/css/code-tree.css,sha256=x0MlVKrBBYr7XU8ndhxx7cjCe35GouhthIM32Zc9SA8,25599
|
|
194
194
|
claude_mpm/dashboard/static/css/connection-status.css,sha256=nQiMLxIFjY5MDgrVCG58VR5H1PpE1vLlgXmnn01-P64,6729
|
|
195
195
|
claude_mpm/dashboard/static/css/dashboard.css,sha256=k_tHegUSiIFHAD2Ez1qX4PJCh4Ut-Tk0BgJ4EANqT54,75294
|
|
196
|
-
claude_mpm/dashboard/static/dist/dashboard.js,sha256=
|
|
196
|
+
claude_mpm/dashboard/static/dist/dashboard.js,sha256=BlDQDaezsu4Qqa6XBoCaWT041FggkyGrfWlz4dxdCUA,52841
|
|
197
197
|
claude_mpm/dashboard/static/dist/socket-client.js,sha256=z9HU1HePow8Yvh-7QMcjChwMYsR_p-ZvJVu8HU6d5Xg,29110
|
|
198
|
-
claude_mpm/dashboard/static/dist/chunks/unified-data-viewer.
|
|
199
|
-
claude_mpm/dashboard/static/dist/components/activity-tree.js,sha256=
|
|
198
|
+
claude_mpm/dashboard/static/dist/chunks/unified-data-viewer.B7wmm2bD.js,sha256=fyPbOJ6Q1_Sf5m2yVwf5OXpta8YR_sa4wIU9uaCI-TY,27500
|
|
199
|
+
claude_mpm/dashboard/static/dist/components/activity-tree.js,sha256=lfm--V2-UCjPkfoIuxCc14LNZdskeV1bxXZuPj17LEk,29513
|
|
200
200
|
claude_mpm/dashboard/static/dist/components/agent-inference.js,sha256=fXK7kzvAXzYzOGdLw_APbDiEN51OX1I4-InnN2iZlu0,12978
|
|
201
201
|
claude_mpm/dashboard/static/dist/components/code-tree.js,sha256=cM_S8PObjhYpj0CcMgtv5BAn-h1IBSiFtQWmwR2KYrY,46609
|
|
202
202
|
claude_mpm/dashboard/static/dist/components/code-viewer.js,sha256=wMggEwMt2RrBuXJO-WYZ5_HHDODfWLm_IS9lS5LLR7M,8495
|
|
@@ -207,7 +207,7 @@ claude_mpm/dashboard/static/dist/components/file-tool-tracker.js,sha256=fZgUsg_u
|
|
|
207
207
|
claude_mpm/dashboard/static/dist/components/hud-library-loader.js,sha256=v08Xntd0Lwf-q6Nww1X2r5AWokYLgg9xUWe6ExHiNAg,2964
|
|
208
208
|
claude_mpm/dashboard/static/dist/components/hud-manager.js,sha256=CvgzgqprROjl0DP4yQx4aAypONuNlzU2EP8woGk-8Sc,27392
|
|
209
209
|
claude_mpm/dashboard/static/dist/components/hud-visualizer.js,sha256=igUUu1OLKOgwrdsfCEb-bKOsbi61bMLlpKa3hR2wQdI,69
|
|
210
|
-
claude_mpm/dashboard/static/dist/components/module-viewer.js,sha256=
|
|
210
|
+
claude_mpm/dashboard/static/dist/components/module-viewer.js,sha256=wlPT2g_q0zijyVxik1fJ78Va4wehQETQTIXE_VS7Yto,57056
|
|
211
211
|
claude_mpm/dashboard/static/dist/components/session-manager.js,sha256=ZG__csNeB8-cG1VhRNoojD-DSmNHijXI4To410hGf2E,8970
|
|
212
212
|
claude_mpm/dashboard/static/dist/components/socket-manager.js,sha256=LEqCS0_EABAsQVUfb6WQk152mSicK2k039zne5cKR8A,131
|
|
213
213
|
claude_mpm/dashboard/static/dist/components/ui-state-manager.js,sha256=PtZs6sxNhqMMFwdlr04kDKQbgOvxtMHdjSh56KR2ZBo,134
|
|
@@ -234,7 +234,7 @@ claude_mpm/dashboard/static/js/components/module-viewer.js,sha256=5OQpPfBr0AmNF4
|
|
|
234
234
|
claude_mpm/dashboard/static/js/components/session-manager.js,sha256=pJrlMcSBNTXoK5nDLWLcIesOosk0FQtkTEM6X7eSI_I,22807
|
|
235
235
|
claude_mpm/dashboard/static/js/components/socket-manager.js,sha256=KC-aOJDEPSiMPYk0ABgvCC1xmO7TVQ4SVqohi2GNsWQ,13303
|
|
236
236
|
claude_mpm/dashboard/static/js/components/ui-state-manager.js,sha256=EWm0HpPI9TShChitRCyZvZCiUk5rIBVyw10BoHdnTzI,13178
|
|
237
|
-
claude_mpm/dashboard/static/js/components/unified-data-viewer.js,sha256=
|
|
237
|
+
claude_mpm/dashboard/static/js/components/unified-data-viewer.js,sha256=JLlmoC5c6BySNikdC4MpusfzI-W53xR_mNsGXfrD92o,44900
|
|
238
238
|
claude_mpm/dashboard/static/js/components/working-directory.js,sha256=BRsPYwVnhbhFl0mC0Gvb-Ev1zxAu6yZpZcl1T1aJWWA,33558
|
|
239
239
|
claude_mpm/dashboard/templates/index.html,sha256=dGb4Z2vcboxvZdNS8zv41mOM5VtHkD6gt7dlvc3tb2U,30667
|
|
240
240
|
claude_mpm/experimental/__init__.py,sha256=R_aclOvWpvSTHWAx9QXyg9OIPVK2dXT5tQJhxLQN11Y,369
|
|
@@ -597,9 +597,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=zgiwLqh_17WxHpySvUPH65pb4bzIeUGOAYUJ
|
|
|
597
597
|
claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
|
|
598
598
|
claude_mpm/validation/agent_validator.py,sha256=3Lo6LK-Mw9IdnL_bd3zl_R6FkgSVDYKUUM7EeVVD3jc,20865
|
|
599
599
|
claude_mpm/validation/frontmatter_validator.py,sha256=u8g4Eyd_9O6ugj7Un47oSGh3kqv4wMkuks2i_CtWRvM,7028
|
|
600
|
-
claude_mpm-4.1.
|
|
601
|
-
claude_mpm-4.1.
|
|
602
|
-
claude_mpm-4.1.
|
|
603
|
-
claude_mpm-4.1.
|
|
604
|
-
claude_mpm-4.1.
|
|
605
|
-
claude_mpm-4.1.
|
|
600
|
+
claude_mpm-4.1.24.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
|
|
601
|
+
claude_mpm-4.1.24.dist-info/METADATA,sha256=9bT1nP2mYJ349jHYJVNLd594TLafVdPKODLp9fIWuyo,13777
|
|
602
|
+
claude_mpm-4.1.24.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
603
|
+
claude_mpm-4.1.24.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
|
|
604
|
+
claude_mpm-4.1.24.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
|
605
|
+
claude_mpm-4.1.24.dist-info/RECORD,,
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
class s{constructor(s="module-data-content"){this.container=document.getElementById(s),this.currentData=null,this.currentType=null}display(s,a=null){if(this.container)switch(this.currentData=s,this.currentType=a,a||(a=this.detectType(s)),this.container.innerHTML="",a){case"event":this.displayEvent(s);break;case"agent":this.displayAgent(s);break;case"tool":this.displayTool(s);break;case"todo":this.displayTodo(s);break;case"instruction":this.displayInstruction(s);break;case"session":this.displaySession(s);break;case"file_operation":this.displayFileOperation(s);break;case"hook":this.displayHook(s);break;default:this.displayGeneric(s)}else console.warn("UnifiedDataViewer: Container not found")}detectType(s){return s&&"object"==typeof s?s.hook_event_name||s.event_type||s.type&&s.timestamp?"event":s.agent_name||s.agentName||s.name&&("active"===s.status||"completed"===s.status)?"agent":s.tool_name||"TodoWrite"===s.name||"Read"===s.name||s.tool_parameters||s.params&&s.icon||s.name&&"tool"===s.type?"tool":!s.todos||s.name||s.params?s.content&&s.activeForm&&s.status&&!s.name&&!s.params?"todo":s.text&&s.preview&&"user_instruction"===s.type?"instruction":s.session_id&&(s.startTime||s.lastActivity)?"session":s.file_path&&(s.operations||s.operation)?"file_operation":s.event_type&&(s.hook_name||s.subtype)?"hook":"generic":"todo":"generic"}displayEvent(s){let a=`\n <div class="unified-viewer-header">\n <h6>${this.formatEventType(s)}</h6>\n <span class="unified-viewer-timestamp">${this.formatTimestamp(s.timestamp)}</span>\n </div>\n <div class="unified-viewer-content">\n `;if(a+='<div class="primary-data">',a+=this.formatEventDetails(s),s.tool_name||s.data?.tool_name){const n=s.tool_name||s.data.tool_name;a+=`\n <div class="detail-row highlight">\n <span class="detail-label">Tool:</span>\n <span class="detail-value">${this.getToolIcon(n)} ${n}</span>\n </div>\n `;const t=s.tool_parameters||s.data?.tool_parameters;t&&(t.file_path&&(a+=`\n <div class="detail-row">\n <span class="detail-label">File:</span>\n <span class="detail-value code">${t.file_path}</span>\n </div>\n `),t.command&&(a+=`\n <div class="detail-row">\n <span class="detail-label">Command:</span>\n <pre class="code-snippet">${this.escapeHtml(t.command)}</pre>\n </div>\n `))}a+="</div>",a+=this.createCollapsibleJSON(s,"Full Event Data"),a+="</div>",this.container.innerHTML=a}displayAgent(s){const a=this.getAgentIcon(s.name||s.agentName),n=s.name||s.agentName||"Unknown Agent",t=this.formatStatus(s.status);let e=`\n <div class="unified-viewer-header">\n <h6>${a} ${n}</h6>\n <span class="unified-viewer-status">${t}</span>\n </div>\n <div class="unified-viewer-content">\n `;if(e+='<div class="primary-data">',e+=`\n <div class="detail-row highlight">\n <span class="detail-label">Status:</span>\n <span class="detail-value ${this.formatStatusClass(t)}">${t}</span>\n </div>\n `,s.tools&&s.tools.length>0){const a=s.tools.filter(s=>"in_progress"===s.status),n=s.tools.filter(s=>"completed"===s.status);a.length>0&&(e+='\n <div class="active-tools-section">\n <span class="section-label">🔄 Active Tools:</span>\n <div class="tools-grid">\n ',a.forEach(s=>{e+=`\n <div class="tool-chip active">\n ${this.getToolIcon(s.name)} ${s.name}\n </div>\n `}),e+="</div></div>"),e+=`\n <div class="detail-row">\n <span class="detail-label">Tools Summary:</span>\n <span class="detail-value">\n ${a.length} active, ${n.length} completed, ${s.tools.length} total\n </span>\n </div>\n `}(s.currentTask||s.description)&&(e+=`\n <div class="detail-row">\n <span class="detail-label">Current Task:</span>\n <span class="detail-value">${s.currentTask||s.description}</span>\n </div>\n `),e+="</div>",e+=this.createCollapsibleJSON(s,"Full Agent Details"),e+="</div>",this.container.innerHTML=e}displayTool(s){const a=s.name||s.tool_name||"Unknown Tool",n=this.getToolIcon(a),t=this.formatStatus(s.status);if("TodoWrite"===a)return void this.displayTodoWriteTool(s);let e=`\n <div class="unified-viewer-header">\n <h6>${n} ${a}</h6>\n <span class="unified-viewer-status">${t}</span>\n </div>\n <div class="unified-viewer-content">\n `;const i=s.params||s.tool_parameters||{};"Read"===a||"Edit"===a||"Write"===a?i.file_path&&(e+=`\n <div class="primary-data">\n <div class="detail-row highlight">\n <span class="detail-label">📁 File:</span>\n <span class="detail-value code">${i.file_path}</span>\n </div>\n `,i.old_string&&(e+=`\n <div class="detail-row">\n <span class="detail-label">Old Text:</span>\n <pre class="code-snippet">${this.escapeHtml(i.old_string.substring(0,200))}${i.old_string.length>200?"...":""}</pre>\n </div>\n `),i.new_string&&(e+=`\n <div class="detail-row">\n <span class="detail-label">New Text:</span>\n <pre class="code-snippet">${this.escapeHtml(i.new_string.substring(0,200))}${i.new_string.length>200?"...":""}</pre>\n </div>\n `),e+="</div>"):"Bash"===a?i.command&&(e+=`\n <div class="primary-data">\n <div class="detail-row highlight">\n <span class="detail-label">💻 Command:</span>\n <pre class="code-snippet">${this.escapeHtml(i.command)}</pre>\n </div>\n </div>\n `):"Grep"===a||"Glob"===a?i.pattern&&(e+=`\n <div class="primary-data">\n <div class="detail-row highlight">\n <span class="detail-label">🔍 Pattern:</span>\n <span class="detail-value code">${this.escapeHtml(i.pattern)}</span>\n </div>\n `,i.path&&(e+=`\n <div class="detail-row">\n <span class="detail-label">Path:</span>\n <span class="detail-value">${i.path}</span>\n </div>\n `),e+="</div>"):"Task"===a&&i.subagent_type&&(e+=`\n <div class="primary-data">\n <div class="detail-row highlight">\n <span class="detail-label">🤖 Delegating to:</span>\n <span class="detail-value">${i.subagent_type} agent</span>\n </div>\n `,i.description&&(e+=`\n <div class="detail-row">\n <span class="detail-label">Task:</span>\n <span class="detail-value">${i.description}</span>\n </div>\n `),e+="</div>"),e+=`\n <div class="detail-row">\n <span class="detail-label">Status:</span>\n <span class="detail-value">${t}</span>\n </div>\n `,s.callCount&&(e+=`\n <div class="detail-row">\n <span class="detail-label">Call Count:</span>\n <span class="detail-value">${s.callCount}</span>\n </div>\n `),e+=this.createCollapsibleJSON(s,"Full Tool Details"),e+="</div>",this.container.innerHTML=e}displayTodoWriteTool(s){const a=this.formatStatus(s.status),n=(s.params||s.tool_parameters||{}).todos||[];let t=`\n <div class="unified-viewer-header">\n <h6>📝 TodoWrite</h6>\n <span class="unified-viewer-status">${a}</span>\n </div>\n <div class="unified-viewer-content">\n `;if(n.length>0){const s=this.getTodoStatusCounts(n);t+=`\n <div class="todo-status-bar">\n <div class="status-item completed">\n <span class="status-icon">✅</span>\n <span class="status-count">${s.completed}</span>\n <span class="status-label">Done</span>\n </div>\n <div class="status-item in_progress">\n <span class="status-icon">🔄</span>\n <span class="status-count">${s.in_progress}</span>\n <span class="status-label">Active</span>\n </div>\n <div class="status-item pending">\n <span class="status-icon">⏳</span>\n <span class="status-count">${s.pending}</span>\n <span class="status-label">Pending</span>\n </div>\n </div>\n `,t+='\n <div class="todo-list-primary">\n ',n.forEach((s,a)=>{const n=this.getCheckboxIcon(s.status),e="in_progress"===s.status&&s.activeForm||s.content,i=this.formatStatusClass(s.status);t+=`\n <div class="todo-item ${s.status}">\n <span class="todo-icon ${i}">${n}</span>\n <span class="todo-text">${this.escapeHtml(e)}</span>\n ${"in_progress"===s.status?'<span class="todo-badge active">ACTIVE</span>':""}\n </div>\n `}),t+="\n </div>\n "}else t+='\n <div class="detail-row">\n <span class="detail-value">No todos in list</span>\n </div>\n ';s.callCount&&s.callCount>1&&(t+=`\n <div class="detail-row">\n <span class="detail-label">Updates:</span>\n <span class="detail-value">${s.callCount}</span>\n </div>\n `),t+=this.createCollapsibleJSON(s,"Full Details"),t+="</div>",this.container.innerHTML=t}displayTodo(s){let a;a=s.todos&&Array.isArray(s.todos)?s.todos:Array.isArray(s)?s:s.content&&s.activeForm&&s.status?[s]:[];let n='\n <div class="unified-viewer-header">\n <h6>📋 Todo List</h6>\n </div>\n <div class="unified-viewer-content">\n ';a.length>0?(n+='\n <div class="todo-list-primary">\n ',a.forEach(s=>{const a=this.getCheckboxIcon(s.status),t="in_progress"===s.status&&s.activeForm||s.content,e=this.formatStatusClass(s.status);n+=`\n <div class="todo-item ${s.status}">\n <span class="todo-icon ${e}">${a}</span>\n <span class="todo-text">${this.escapeHtml(t)}</span>\n <span class="todo-status-text ${e}">${s.status.replace("_"," ")}</span>\n </div>\n `}),n+="\n </div>\n "):n+='\n <div class="detail-section">\n <div class="no-todos">No todo items found</div>\n </div>\n ',n+="</div>",this.container.innerHTML=n}displayInstruction(s){let a=`\n <div class="unified-viewer-header">\n <h6>💬 User Instruction</h6>\n <span class="unified-viewer-timestamp">${this.formatTimestamp(s.timestamp)}</span>\n </div>\n <div class="unified-viewer-content">\n `;a+=`\n <div class="primary-data">\n <div class="instruction-content">\n ${this.escapeHtml(s.text)}\n </div>\n <div class="instruction-meta">\n <span class="meta-item">📏 ${s.text.length} characters</span>\n <span class="meta-item">🕐 ${this.formatTimestamp(s.timestamp)}</span>\n </div>\n </div>\n `,Object.keys(s).length>3&&(a+=this.createCollapsibleJSON(s,"Full Instruction Data")),a+="</div>",this.container.innerHTML=a}displaySession(s){let a=`\n <div class="unified-viewer-header">\n <h6>🎯 Session: ${s.session_id||s.id}</h6>\n <span class="unified-viewer-status">${this.formatStatus(s.status||"active")}</span>\n </div>\n <div class="unified-viewer-content">\n <div class="detail-row">\n <span class="detail-label">Session ID:</span>\n <span class="detail-value">${s.session_id||s.id}</span>\n </div>\n <div class="detail-row">\n <span class="detail-label">Start Time:</span>\n <span class="detail-value">${this.formatTimestamp(s.startTime||s.timestamp)}</span>\n </div>\n `;s.working_directory&&(a+=`\n <div class="detail-row">\n <span class="detail-label">Working Directory:</span>\n <span class="detail-value">${s.working_directory}</span>\n </div>\n `),s.git_branch&&(a+=`\n <div class="detail-row">\n <span class="detail-label">Git Branch:</span>\n <span class="detail-value">${s.git_branch}</span>\n </div>\n `),void 0!==s.eventCount&&(a+=`\n <div class="detail-row">\n <span class="detail-label">Events:</span>\n <span class="detail-value">${s.eventCount}</span>\n </div>\n `),a+="</div>",this.container.innerHTML=a}displayFileOperation(s){let a=`\n <div class="unified-viewer-header">\n <h6>📄 File: ${s.file_path}</h6>\n <span class="unified-viewer-count">${s.operations?s.operations.length:1} operation${s.operations&&1!==s.operations.length?"s":""}</span>\n </div>\n <div class="unified-viewer-content">\n <div class="detail-row">\n <span class="detail-label">File Path:</span>\n <span class="detail-value">${s.file_path}</span>\n </div>\n `;s.operations&&Array.isArray(s.operations)&&(a+=`\n <div class="detail-section">\n <span class="detail-section-title">Operations:</span>\n <div class="operations-list">\n ${s.operations.map(s=>`\n <div class="operation-item">\n <span class="operation-type">${s.operation}</span>\n <span class="operation-timestamp">${this.formatTimestamp(s.timestamp)}</span>\n </div>\n `).join("")}\n </div>\n </div>\n `),a+="</div>",this.container.innerHTML=a}displayHook(s){let a=`\n <div class="unified-viewer-header">\n <h6>🔗 Hook: ${s.event_type||s.subtype||"unknown"}</h6>\n <span class="unified-viewer-timestamp">${this.formatTimestamp(s.timestamp)}</span>\n </div>\n <div class="unified-viewer-content">\n `;a+=this.formatHookDetails(s),a+="</div>",this.container.innerHTML=a}displayGeneric(s){let a=`\n <div class="unified-viewer-header">\n <h6>📊 Data Details</h6>\n ${s.timestamp?`<span class="unified-viewer-timestamp">${this.formatTimestamp(s.timestamp)}</span>`:""}\n </div>\n <div class="unified-viewer-content">\n `;if("object"==typeof s&&null!==s){const n=["id","name","type","status","timestamp","text","content","message"];for(let t of n)if(void 0!==s[t]){let n=s[t];"string"==typeof n&&n.length>200&&(n=n.substring(0,200)+"..."),a+=`\n <div class="detail-row">\n <span class="detail-label">${t}:</span>\n <span class="detail-value">${this.escapeHtml(String(n))}</span>\n </div>\n `}}else a+=`<div class="simple-value">${this.escapeHtml(String(s))}</div>`;a+="</div>",this.container.innerHTML=a}formatEventType(s){return s.type&&s.subtype?s.type===s.subtype||"generic"===s.subtype?s.type:`${s.type}.${s.subtype}`:s.type?s.type:s.hook_event_name?s.hook_event_name:"unknown"}formatEventDetails(s){switch(s.data,s.type){case"hook":return this.formatHookDetails(s);case"agent":return this.formatAgentEventDetails(s);case"todo":return this.formatTodoEventDetails(s);case"session":return this.formatSessionEventDetails(s);default:return this.formatGenericEventDetails(s)}}formatHookDetails(s){const a=s.data||{},n=s.subtype||s.event_type||"unknown";let t=`\n <div class="detail-row">\n <span class="detail-label">Hook Type:</span>\n <span class="detail-value">${n}</span>\n </div>\n `;switch(n){case"user_prompt":const s=a.prompt_text||a.prompt_preview||"";t+=`\n <div class="detail-row">\n <span class="detail-label">Prompt:</span>\n <div class="detail-value prompt-text">${this.escapeHtml(s)}</div>\n </div>\n `;break;case"pre_tool":case"post_tool":t+=`\n <div class="detail-row">\n <span class="detail-label">Tool:</span>\n <span class="detail-value">${a.tool_name||"Unknown tool"}</span>\n </div>\n `,a.operation_type&&(t+=`\n <div class="detail-row">\n <span class="detail-label">Operation:</span>\n <span class="detail-value">${a.operation_type}</span>\n </div>\n `),"post_tool"===n&&a.duration_ms&&(t+=`\n <div class="detail-row">\n <span class="detail-label">Duration:</span>\n <span class="detail-value">${a.duration_ms}ms</span>\n </div>\n `);break;case"subagent_start":case"subagent_stop":t+=`\n <div class="detail-row">\n <span class="detail-label">Agent:</span>\n <span class="detail-value">${a.agent_type||a.agent||"Unknown"}</span>\n </div>\n `,"subagent_start"===n&&a.prompt&&(t+=`\n <div class="detail-row">\n <span class="detail-label">Task:</span>\n <div class="detail-value">${this.escapeHtml(a.prompt)}</div>\n </div>\n `),"subagent_stop"===n&&a.reason&&(t+=`\n <div class="detail-row">\n <span class="detail-label">Reason:</span>\n <span class="detail-value">${a.reason}</span>\n </div>\n `)}return t}formatAgentEventDetails(s){const a=s.data||{};let n="";return(a.agent_type||a.name)&&(n+=`\n <div class="detail-row">\n <span class="detail-label">Agent Type:</span>\n <span class="detail-value">${a.agent_type||a.name}</span>\n </div>\n `),s.subtype&&(n+=`\n <div class="detail-row">\n <span class="detail-label">Action:</span>\n <span class="detail-value">${s.subtype}</span>\n </div>\n `),n}formatTodoEventDetails(s){const a=s.data||{};let n="";if(a.todos&&Array.isArray(a.todos)){const s=this.getTodoStatusCounts(a.todos);n+=`\n <div class="detail-row">\n <span class="detail-label">Todo Items:</span>\n <span class="detail-value">${a.todos.length} total</span>\n </div>\n <div class="detail-row">\n <span class="detail-label">Status:</span>\n <span class="detail-value">${s.completed} completed, ${s.in_progress} in progress</span>\n </div>\n `}return n}formatSessionEventDetails(s){const a=s.data||{};let n="";return a.session_id&&(n+=`\n <div class="detail-row">\n <span class="detail-label">Session ID:</span>\n <span class="detail-value">${a.session_id}</span>\n </div>\n `),s.subtype&&(n+=`\n <div class="detail-row">\n <span class="detail-label">Action:</span>\n <span class="detail-value">${s.subtype}</span>\n </div>\n `),n}formatGenericEventDetails(s){const a=s.data||{};let n="";const t=["message","description","value","result"];for(let e of t)if(void 0!==a[e]){let s=a[e];"string"==typeof s&&s.length>200&&(s=s.substring(0,200)+"..."),n+=`\n <div class="detail-row">\n <span class="detail-label">${e}:</span>\n <span class="detail-value">${this.escapeHtml(String(s))}</span>\n </div>\n `}return n}formatEventData(s){const a=s.data;return a&&0!==Object.keys(a).length?`\n <div class="detail-section">\n <span class="detail-section-title">Event Data:</span>\n <pre class="event-data-json">${this.escapeHtml(JSON.stringify(a,null,2))}</pre>\n </div>\n `:""}formatParameters(s,a="Parameters"){if(!s||0===Object.keys(s).length)return`\n <div class="detail-section">\n <span class="detail-section-title">${a}:</span>\n <div class="no-params">No parameters</div>\n </div>\n `;const n=Object.keys(s);return`\n <div class="detail-section">\n <span class="detail-section-title">${a} (${n.length}):</span>\n <div class="params-list">\n ${n.map(a=>{const n=s[a];return`\n <div class="param-item">\n <div class="param-key">${a}:</div>\n <div class="param-value">${this.formatParameterValue(n)}</div>\n </div>\n `}).join("")}\n </div>\n </div>\n `}formatParameterValue(s){if("string"==typeof s)return s.length>500?`<pre class="param-text-long">${this.escapeHtml(s.substring(0,500)+"...\n\n[Content truncated - "+s.length+" total characters]")}</pre>`:s.length>100?`<pre class="param-text">${this.escapeHtml(s)}</pre>`:`<span class="param-text-short">${this.escapeHtml(s)}</span>`;if("object"!=typeof s||null===s)return`<span class="param-primitive">${this.escapeHtml(String(s))}</span>`;if(Array.isArray(s)&&s.length>0&&s[0].hasOwnProperty("content")&&s[0].hasOwnProperty("status"))return this.formatTodosAsParameter(s);try{return`<pre class="param-json">${this.escapeHtml(JSON.stringify(s,null,2))}</pre>`}catch(a){return'<span class="param-error">Error displaying object</span>'}}formatTodosAsParameter(s){const a=this.getTodoStatusCounts(s);let n=`\n <div class="param-todos">\n <div class="param-todos-header">\n Array of todo objects (${s.length} items)\n </div>\n <div class="param-todos-summary">\n ${a.completed} completed • ${a.in_progress} in progress • ${a.pending} pending\n </div>\n <div class="param-todos-list">\n `;return s.forEach((s,a)=>{const t=this.getCheckboxIcon(s.status),e="in_progress"===s.status&&s.activeForm||s.content,i=this.formatStatusClass(s.status);n+=`\n <div class="param-todo-item ${s.status}">\n <div class="param-todo-checkbox">\n <span class="param-checkbox-icon ${i}">${t}</span>\n </div>\n <div class="param-todo-text">\n <span class="param-todo-content">${this.escapeHtml(e)}</span>\n <span class="param-todo-status-badge ${i}">${s.status.replace("_"," ")}</span>\n </div>\n </div>\n `}),n+="\n </div>\n </div>\n ",n}formatTimestamp(s){if(!s)return"Unknown time";try{const a=new Date(s);return isNaN(a.getTime())?"Invalid date":a.toLocaleString()}catch(a){return"Invalid date"}}formatStatus(s){if(!s)return"unknown";return{active:"🟢 Active",completed:"✅ Completed",in_progress:"🔄 In Progress",pending:"⏳ Pending",error:"❌ Error",failed:"❌ Failed"}[s]||s}formatStatusClass(s){return`status-${s}`}getAgentIcon(s){return{PM:"🎯",Engineer:"🔧","Engineer Agent":"🔧",Research:"🔍","Research Agent":"🔍",QA:"✅","QA Agent":"✅",Architect:"🏗️","Architect Agent":"🏗️",Ops:"⚙️","Ops Agent":"⚙️"}[s]||"🤖"}getToolIcon(s){return{Read:"👁️",Write:"✍️",Edit:"✏️",MultiEdit:"📝",Bash:"💻",Grep:"🔍",Glob:"📂",LS:"📁",TodoWrite:"📝",Task:"📋",WebFetch:"🌐"}[s]||"🔧"}getCheckboxIcon(s){return{pending:"⏳",in_progress:"🔄",completed:"✅"}[s]||"❓"}getTodoStatusCounts(s){const a={completed:0,in_progress:0,pending:0};return s.forEach(s=>{a.hasOwnProperty(s.status)&&a[s.status]++}),a}escapeHtml(s){if("string"!=typeof s)return"";const a=document.createElement("div");return a.textContent=s,a.innerHTML}createCollapsibleJSON(s,a="Full Details"){const n=`json-details-${Date.now()}-${Math.random().toString(36).substr(2,9)}`,t=this.cleanDataForDisplay(s);return`\n <div class="collapsible-json-section">\n <button class="collapsible-json-toggle" onclick="\n const content = document.getElementById('${n}');\n const button = this;\n if (content.style.display === 'none' || content.style.display === '') {\n content.style.display = 'block';\n button.classList.add('expanded');\n button.innerHTML = '▼ ${a}';\n } else {\n content.style.display = 'none';\n button.classList.remove('expanded');\n button.innerHTML = '▶ ${a}';\n }\n ">▶ ${a}</button>\n <div id="${n}" class="collapsible-json-content" style="display: none;">\n <pre class="json-viewer">${this.escapeHtml(JSON.stringify(t,null,2))}</pre>\n </div>\n </div>\n `}cleanDataForDisplay(s){const a=new WeakSet;return JSON.parse(JSON.stringify(s,(s,n)=>{if("object"==typeof n&&null!==n){if(a.has(n))return"[Circular Reference]";a.add(n)}return"string"==typeof n&&n.length>1e3?n.substring(0,1e3)+"... [truncated]":"function"==typeof n?"[Function]":n}))}clear(){this.container&&(this.container.innerHTML=""),this.currentData=null,this.currentType=null}getCurrentData(){return this.currentData}getCurrentType(){return this.currentType}hasData(){return null!==this.currentData}}window.UnifiedDataViewer=s;export{s as U};
|
|
2
|
-
//# sourceMappingURL=unified-data-viewer.CvAmgM3l.js.map
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|