@qiaolei81/copilot-session-viewer 0.3.7 → 0.3.9
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.
- package/dist/server.min.js +96 -71
- package/package.json +1 -1
- package/public/js/homepage.min.js +8 -8
- package/public/js/session-detail.min.js +267 -75
- package/public/js/time-analyze.min.js +18 -40
- package/views/index.ejs +18 -1
- package/views/session-vue.ejs +579 -252
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
(()=>{(function(){if(typeof Vue>"u"){console.error("Vue is not loaded");return}if(typeof window.VueVirtualScroller>"u"){console.error("VueVirtualScroller is not loaded");return}console.log("Initializing Vue app...");let{createApp:
|
|
1
|
+
(()=>{var $e=(u,g)=>()=>(g||u((g={exports:{}}).exports,g),g.exports);var qe=$e((Gt,ie)=>{function Vt(u){let g=new Map,f=new Map,x=0;for(let n of u)if(n.type==="subagent.started"){let l=n.data?.toolCallId;l&&f.set(l,{name:n.data?.agentDisplayName||n.data?.agentName||"SubAgent",colorIndex:x++,meta:{agentName:n.data?.agentName||"",agentDisplayName:n.data?.agentDisplayName||"",agentDescription:n.data?.agentDescription||""}})}for(let n of u){if(n.type!=="tool.execution_start")continue;let l=n.data?.toolCallId;if(!l||!f.has(l))continue;let v=f.get(l),p=n.data?.arguments;if(typeof p=="string")try{p=JSON.parse(p)}catch{continue}!p||typeof p!="object"||(p.description&&(v.meta.taskDescription=p.description),p.name&&(v.meta.taskName=p.name),p.agent_type&&(v.meta.agentType=p.agent_type),p.mode&&(v.meta.taskMode=p.mode),p.description&&v.name===(v.meta.agentDisplayName||v.meta.agentName)&&(v.name=p.description))}for(let n of u)if(n.type==="assistant.message"&&n.data?.subAgentName&&n.data?.subAgentId){let l=n.data.subAgentId;f.has(l)||f.set(l,{name:n.data.subAgentName,colorIndex:x++,meta:{agentName:n.data.subAgentName}}),g.set(n.stableId,l)}for(let n of u)if(n._subagent?.id){let l=n._subagent.id;f.has(l)||f.set(l,{name:n._subagent.name||"SubAgent",colorIndex:x++,meta:{agentName:n._subagent.name||""}}),g.set(n.stableId,l)}if(f.size===0)return{ownerMap:g,subagentInfo:f};let V=new Map;for(let n of u)n.id&&V.set(n.id,n);for(let n of u)if(n.type==="assistant.message"){let l=n.data?.parentToolCallId;l&&f.has(l)&&g.set(n.stableId,l)}for(let n of u){if(n.type!=="reasoning")continue;let l=n.parentId,v=0;for(;l&&v<10;){let p=V.get(l);if(!p)break;if(p.type==="assistant.message"){let C=p.data?.parentToolCallId;C&&f.has(C)&&g.set(n.stableId,C);break}l=p.parentId,v++}}let m=new Map;for(let n of u){if(n.type!=="tool.execution_start")continue;let l=n.parentId,v=0;for(;l&&v<10;){let p=V.get(l);if(!p)break;if(p.type==="assistant.message"){let C=p.data?.parentToolCallId;if(C&&f.has(C)){g.set(n.stableId,C);let X=n.data?.toolCallId;X&&m.set(X,C)}break}l=p.parentId,v++}}for(let n of u){if(n.type!=="tool.execution_complete")continue;let l=n.data?.toolCallId;l&&m.has(l)&&g.set(n.stableId,m.get(l))}for(let n of u){if(n.type!=="tool.invocation")continue;let l=n.data?.parentToolCallId;l&&f.has(l)&&g.set(n.stableId,l)}let b=null;for(let n of u)n.type==="subagent.started"&&n.data?.toolCallId&&f.has(n.data.toolCallId)?b=n.data.toolCallId:(n.type==="subagent.completed"||n.type==="subagent.failed")&&n.data?.toolCallId===b?b=null:b&&!g.has(n.stableId)&&g.set(n.stableId,b);for(let[n,l]of f)for(let v of u){if(!(g.get(v.stableId)===n||v._subagent?.id===n||v.data?.subAgentId===n))continue;let C=v.model||v.data?.model;if(C){l.meta.model=C;break}}return{ownerMap:g,subagentInfo:f}}function Pt(u,g,f){return g?u.filter(x=>(x.type==="subagent.started"||x.type==="subagent.completed"||x.type==="subagent.failed")&&x.data?.toolCallId===g||f.get(x.stableId)===g||x._subagent?.id===g||x.data?.subAgentId===g):u}typeof ie<"u"&&ie.exports&&(ie.exports={computeSubagentOwnership:Vt,filterBySubagent:Pt})});var Fe=$e((Kt,re)=>{function Ue(u){if(!u||typeof u!="object")return 0;let g=Number.isFinite(u.inputTokens)?u.inputTokens:0,f=Number.isFinite(u.cacheReadTokens)?u.cacheReadTokens:0,x=Number.isFinite(u.cacheWriteTokens)?u.cacheWriteTokens:0;return Math.max(g-f-x,0)}function jt(u){if(!u||typeof u!="object")return null;let g=Number.isFinite(u.cacheReadTokens)?u.cacheReadTokens:0,f=Ue(u)+g;return g===0||f===0?null:Math.round(g/f*100)}typeof re<"u"&&re.exports&&(re.exports={getDisplayInputTokens:Ue,getCacheHitRatio:jt})});(function(){let{computeSubagentOwnership:u,filterBySubagent:g}=qe(),{getDisplayInputTokens:f,getCacheHitRatio:x}=Fe();if(typeof Vue>"u"){console.error("Vue is not loaded");return}if(typeof window.VueVirtualScroller>"u"){console.error("VueVirtualScroller is not loaded");return}console.log("Initializing Vue app...");let{createApp:V,ref:m,computed:b,onMounted:n,onBeforeUnmount:l,watch:v,nextTick:p}=Vue,{DynamicScroller:C,DynamicScrollerItem:X}=window.VueVirtualScroller,ve=V({components:{DynamicScroller:C,DynamicScrollerItem:X},setup(){let h=m(window.__PAGE_DATA.sessionId),w=m(window.__PAGE_DATA.metadata),le=m(!1),fe=()=>window.innerWidth<=640,Y=m(fe()?!0:localStorage.getItem("sidebarCollapsed")==="true");v(Y,e=>{fe()||localStorage.setItem("sidebarCollapsed",e.toString())});let D=m({}),L=m({}),Q=50,be=()=>{let e=Object.keys(D.value);e.length>Q&&e.slice(0,e.length-Q).forEach(s=>delete D.value[s]);let t=Object.keys(L.value);t.length>Q&&t.slice(0,t.length-Q).forEach(s=>delete L.value[s])},R=m("all"),P=m(""),U=m(""),he=m(0),k=m(null),Te=m({start:0,end:0}),A=m(null),Z=m(!1),de=m(""),ye=m(null),ce=m(!1),Be=b(()=>{let e=0;return R.value!=="all"&&e++,A.value&&e++,P.value.trim()&&e++,e}),ze=()=>{R.value="all",A.value=null,P.value="",U.value="",ce.value=!1},j=null,ee=null;v(P,e=>{clearTimeout(j),j=setTimeout(()=>{U.value=e,e.trim()&&window.trackClick&&window.trackClick("SearchUsed",{query:e.substring(0,50),resultCount:B.value.length,sessionId:h.value})},300)}),v(R,()=>{be()}),v(U,()=>{be()}),v(Z,e=>{e&&p(()=>{ye.value?.focus()})});let O=m([]),ke=m(!0),xe=m(null),S=b(()=>O.value.filter(t=>t.type!=="assistant.turn_end"&&t.type!=="assistant.turn_complete").sort((t,a)=>{let s=t.timestamp?new Date(t.timestamp).getTime():0,o=a.timestamp?new Date(a.timestamp).getTime():0;return s!==o?s-o:(t._fileIndex??0)-(a._fileIndex??0)}).map((t,a)=>({...t,virtualIndex:a,stableId:t.id||`${t.timestamp}-${t.type}-${a}`}))),Ve=e=>{if(!U.value.trim())return!0;let t=U.value.toLowerCase();return[e.data?.message,e.data?.text,e.data?.content,e.data?.reason,e.data?.reasoningText,e.data?.errorType,e.data?.previousModel,e.data?.newModel].filter(Boolean).join(" ").toLowerCase().includes(t)},B=b(()=>{let e=a=>{let s=a.type||"";return s!=="tool.execution_start"&&s!=="tool.execution_complete"},t=S.value.filter(e);return U.value.trim()&&(t=t.filter(Ve)),t}),H=b(()=>{let e=B.value;if(A.value){let{ownerMap:s}=G.value;e=g(e,A.value,s)}R.value!=="all"&&(e=e.filter(s=>s.type===R.value));let t=["assistant.turn_start","subagent.started","subagent.completed","subagent.failed"],a=e.length;return e.map((s,o)=>{let i=e[o+1],r=o===a-1,d=i&&t.includes(i.type);return{...s,filteredIndex:o,filteredTotal:a,isLastEvent:r||d}})}),Pe=b(()=>{let e={};return B.value.forEach(t=>{t.type&&(e[t.type]=(e[t.type]||0)+1)}),e}),je=b(()=>{if(!U.value.trim())return null;let e=B.value.length;return e>0?`${e} result${e!==1?"s":""}`:"No matches"}),He=b(()=>{let e=Object.keys(D.value).filter(a=>D.value[a]).length,t=Object.keys(L.value).filter(a=>L.value[a]).length;return e+t}),we=b(()=>{let e=B.value.length,t=[{type:"all",label:`All (${e})`,count:e}],a={};B.value.forEach(o=>{o.type&&(a[o.type]=(a[o.type]||0)+1)});let s=Object.entries(a).sort((o,i)=>i[1]-o[1]).map(([o,i])=>({type:o,label:`${o} (${i})`,count:i,disabled:!1}));return[...t,...s]}),W=b(()=>{let e=S.value.filter(a=>a.type==="assistant.turn_start"),t=S.value.filter(a=>a.type==="user.message");return e.map((a,s)=>{let o=s,i=new Date(a.timestamp).getTime(),r,d=e.indexOf(a)+1;d<e.length?r=new Date(e[d].timestamp).getTime():r=Date.now();let c=r-i,T=Math.floor(c/1e3),y=Math.floor(T/60),$=T%60,q=y>0?`${y}m ${$}s`:`${$}s`,z=S.value.slice(0,S.value.indexOf(a)).reverse().find(I=>I.type==="user.message"),ae=z?t.indexOf(z)+1:0;return{id:o,index:a.virtualIndex,originalTurnId:a.data?.turnId,timestamp:a.timestamp,duration:q,message:z?.data?.content||z?.data?.transformedContent||"",userReqNumber:ae}})}),We=b(()=>{let e=[],t=new Map;return W.value.forEach(a=>{let s=a.userReqNumber||0;if(!t.has(s)){let o={reqNumber:s,message:a.message,turns:[]};t.set(s,o),e.push(o)}t.get(s).turns.push(a)}),e}),Ge=(e,t)=>e?e.length<=t?e:e.substring(0,t)+"\u2026":"",G=b(()=>u(S.value)),ue=b(()=>{let{subagentInfo:e}=G.value;if(e.size===0)return[];let t=[];for(let[a,s]of e)t.push({toolCallId:a,name:s.name,colorIndex:s.colorIndex,meta:s.meta||{}});return t}),Ke=b(()=>{let e=de.value.toLowerCase().trim();return e?ue.value.filter(t=>{let a=t.meta||{};return[t.name,a.taskName,a.taskDescription,a.agentName,a.agentType,a.agentDescription,a.model].filter(Boolean).join(" ").toLowerCase().includes(e)}):ue.value}),Je=b(()=>{if(!A.value)return null;let{ownerMap:e,subagentInfo:t}=G.value,a=A.value;if(!t.has(a))return null;let s=0,o=null,i=null;for(let d of S.value){let c=(d.type==="subagent.started"||d.type==="subagent.completed"||d.type==="subagent.failed")&&d.data?.toolCallId===a,T=e.get(d.stableId)===a,y=d._subagent?.id===a,$=d.data?.subAgentId===a;if((c||T||y||$)&&(s++,d.timestamp!==null&&d.timestamp!==void 0)){let q=new Date(d.timestamp).getTime();(o===null||q<o)&&(o=q),(i===null||q>i)&&(i=q)}}let r=o===null||i===null?0:i-o;return{eventCount:s,durationMs:r}}),Xe=e=>{if(!e)return"";let t=new Date(e),a=String(t.getHours()).padStart(2,"0"),s=String(t.getMinutes()).padStart(2,"0"),o=String(t.getSeconds()).padStart(2,"0");return`${a}:${s}:${o}`},Ye=e=>{if(!e)return"";let t=new Date(e),a=String(t.getHours()).padStart(2,"0"),s=String(t.getMinutes()).padStart(2,"0"),o=String(t.getSeconds()).padStart(2,"0"),i=String(t.getMilliseconds()).padStart(3,"0");return`${a}:${s}:${o}.${i}`},E=new Map,Ce=200,Qe=e=>{if(!e)return"";if(E.has(e))return E.get(e);try{let t=e.replace(/\\r\\n/g,`
|
|
2
2
|
`).replace(/\\n/g,`
|
|
3
|
-
`).replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\"),a={ALLOWED_TAGS:["p","br","strong","em","code","pre","a","ul","ol","li","h1","h2","h3","h4","h5","h6","blockquote","table","thead","tbody","tr","th","td","hr","del","span","div","mark"],ALLOWED_ATTR:["href","style","class"],ALLOW_DATA_ATTR:!1,ALLOWED_URI_REGEXP:/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i},s=t.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);if(s){let
|
|
4
|
-
`),
|
|
5
|
-
`;
|
|
6
|
-
`).length>20||e.length>2e3:!1,
|
|
3
|
+
`).replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\"),a={ALLOWED_TAGS:["p","br","strong","em","code","pre","a","ul","ol","li","h1","h2","h3","h4","h5","h6","blockquote","table","thead","tbody","tr","th","td","hr","del","span","div","mark"],ALLOWED_ATTR:["href","style","class"],ALLOW_DATA_ATTR:!1,ALLOWED_URI_REGEXP:/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i},s=t.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);if(s){let r=s[1],d=s[2],c=r.split(`
|
|
4
|
+
`),T=[],y=0;for(;y<c.length;){let I=c[y];if(!I.trim()||!I.includes(":")){y++;continue}let se=I.indexOf(":"),ne=I.substring(0,se).trim(),oe=I.substring(se+1).trim();if(oe==="|"||oe===">"){let _e=[];for(y++;y<c.length&&(c[y].startsWith(" ")||c[y].startsWith(" ")||c[y].trim()==="");)_e.push(c[y].trim()),y++;let Bt=oe===">"?" ":`
|
|
5
|
+
`;T.push({key:ne,value:_e.filter(zt=>zt).join(Bt)})}else T.push({key:ne,value:oe}),y++}let $='<table style="margin-bottom: 16px; border-collapse: collapse; width: 100%;"><tbody>';T.forEach(I=>{let se=DOMPurify.sanitize(I.key,{ALLOWED_TAGS:[]}),ne=DOMPurify.sanitize(I.value,{ALLOWED_TAGS:[]});$+=`<tr><td style="padding: 4px 12px; border: 1px solid #30363d; font-weight: 600; color: #7d8590;">${se}</td><td style="padding: 4px 12px; border: 1px solid #30363d;">${ne}</td></tr>`}),$+="</tbody></table>";let q=marked.parse(d),z=DOMPurify.sanitize(q,a),ae=$+z;if(E.size>=Ce){let I=E.keys().next().value;E.delete(I)}return E.set(e,ae),ae}let o=marked.parse(t),i=DOMPurify.sanitize(o,a);if(E.size>=Ce){let r=E.keys().next().value;E.delete(r)}return E.set(e,i),i}catch{return e}},Ze=e=>{let t={...D.value},a=!!t[e];t[e]?delete t[e]:t[e]=!0,D.value=t,window.trackClick&&window.trackClick("EventExpanded",{eventType:"tool",action:a?"collapse":"expand",sessionId:h.value})},et=(e,t)=>{if(!t||!t.trim()||!e)return e;let a=t.trim(),s=Me(a).replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=document.createElement("div");o.innerHTML=e;let i=r=>{if(r.nodeType===Node.TEXT_NODE){let d=r.textContent,c=new RegExp(`(${s})`,"gi");if(c.test(d)){let T=d.replace(c,'<mark class="search-highlight">$1</mark>'),y=document.createElement("span");y.innerHTML=T,r.parentNode.replaceChild(y,r)}}else r.nodeType===Node.ELEMENT_NODE&&r.tagName!=="SCRIPT"&&r.tagName!=="STYLE"&&Array.from(r.childNodes).forEach(i)};return Array.from(o.childNodes).forEach(i),o.innerHTML},tt=e=>{let t={...L.value},a=!!t[e];t[e]?delete t[e]:t[e]=!0,L.value=t,window.trackClick&&window.trackClick("EventExpanded",{eventType:"content",action:a?"collapse":"expand",sessionId:h.value})},at=e=>e?e.split(`
|
|
6
|
+
`).length>20||e.length>2e3:!1,st=e=>{let t=e.split(`
|
|
7
7
|
`);return t.length<=20?e:t.slice(0,20).join(`
|
|
8
8
|
`)+`
|
|
9
9
|
|
|
10
|
-
...`},_e=(e,t)=>{if(t?.data?.badgeLabel&&t?.data?.badgeClass)return{label:t.data.badgeLabel,class:t.data.badgeClass};if(e==="message"&&t?.data?.role==="toolResult")return{label:"TOOL RESULT",class:"badge-tool"};if(e==="session.model_change")return{label:"MODEL CHANGE",class:"badge-session"};if(e==="session.truncation")return{label:"TRUNCATION",class:"badge-truncation"};if(e==="session.compaction_start"||e==="session.compaction_complete")return{label:"COMPACTION",class:"badge-compaction"};if(e==="system.notification")return{label:"SYSTEM",class:"badge-system"};let s=(e||"").split(".")[0]||"unknown";return{user:{label:"USER",class:"badge-user"},assistant:{label:"ASSISTANT",class:"badge-assistant"},reasoning:{label:"REASONING",class:"badge-reasoning"},turn:{label:"TURN",class:"badge-turn"},tool:{label:"TOOL",class:"badge-tool"},subagent:{label:"SUBAGENT",class:"badge-subagent"},skill:{label:"SKILL",class:"badge-skill"},session:{label:"SESSION",class:"badge-session"},error:{label:"ERROR",class:"badge-error"},abort:{label:"ABORT",class:"badge-error"}}[s]||{label:s.toUpperCase(),class:"badge-info"}},$e=e=>{if(!e.complete)return{icon:"\u23F3",color:"tool-status-running",text:""};let t=e.complete.data||{};return t.error||t.isError?{icon:"\u274C",color:"tool-status-error",text:""}:{icon:"\u2713",color:"tool-status-success",text:""}},qe=e=>{if(!e.complete?.data?.error)return"";let t=e.complete.data.error;if(typeof t=="object"&&t.message)return t.message;if(typeof t=="string"){try{let a=JSON.parse(t);if(a.message)return a.message}catch{}return t}return String(t)},Ue=e=>{if(!e.complete)return"";let t=new Date(e.start.timestamp).getTime(),s=new Date(e.complete.timestamp).getTime()-t;return s>=100?`${parseFloat((s/1e3).toPrecision(3))}s`:""},ft=e=>{let t={};if(e.start?.timestamp&&(t.startTime=e.start.timestamp),e.complete?.timestamp&&(t.endTime=e.complete.timestamp),t.startTime&&t.endTime){let a=new Date(t.endTime).getTime()-new Date(t.startTime).getTime();a>=0&&(t.duration=`${parseFloat((a/1e3).toPrecision(3))}s (${a}ms)`)}return t},Pe=e=>{if(!e.start)return"";let t=e.start.data?.arguments||{},a=e.start.data?.toolName||e.tool||"",s="";if(a==="bash"||a==="exec")s=t.command||t.description||"";else if(a==="ask_user")s=t.question||t.message||"";else if(a==="read"||a==="write"||a==="edit")s=t.file_path||t.path||"";else if(a==="view")s=t.path||t.file||"";else if(a==="create")s=t.path||t.name||"";else if(a==="report_intent")s=t.intent||t.message||"";else if(a==="web_search")s=t.query||"";else if(a==="web_fetch")s=t.url||"";else if(a==="browser"){let n=t.action||"",i=t.targetUrl||t.url||"";s=i?`${n} ${i}`:n}else s=t.description||t.command||t.message||t.path||t.file_path||t.query||"";return s&&s.length>200&&(s=s.substring(0,200)+"..."),s},Be=e=>e.data?.tools&&e.data.tools.length>0,ze=e=>e.data?.tools&&Array.isArray(e.data.tools)?e.data.tools.filter(t=>t&&typeof t=="object"&&t.name).map(t=>{let a=t.result!==void 0||t.status==="completed"||t.status==="error",s={};if(t.startTime&&(s.startTime=t.startTime),t.endTime&&(s.endTime=t.endTime),s.startTime&&s.endTime){let n=new Date(s.endTime).getTime()-new Date(s.startTime).getTime();n>=0&&(s.duration=`${parseFloat((n/1e3).toPrecision(3))}s (${n}ms)`)}return{tool:t.name,timing:s,start:{timestamp:t.startTime,data:{toolName:t.name,arguments:t.input||t.arguments||{}}},complete:a?{timestamp:t.endTime,data:{result:t.result,error:t.status==="error"?t.error:null}}:null}}):[],ie=["#58a6ff","#f0883e","#a371f7","#3fb950","#f778ba","#79c0ff","#d29922","#56d4dd"],He=e=>{let t=0;for(let a=0;a<e.length;a++){let s=e.charCodeAt(a);t=(t<<5)-t+s,t=t&t}return t},re=e=>{let{ownerMap:t,subagentInfo:a}=Ie.value;if(e.type==="subagent.started"||e.type==="subagent.completed"||e.type==="subagent.failed"){let i=e.data?.toolCallId;if(i&&a.has(i)){let o=a.get(i);return{name:o.name,toolCallId:i,colorIndex:o.colorIndex}}return null}if(e._subagent){let i=e._subagent.id,o=e._subagent.name;if(a.has(i)){let r=a.get(i);return{name:r.name,toolCallId:i,colorIndex:r.colorIndex}}return{name:o,toolCallId:i,colorIndex:Math.abs(He(i))}}if(e.data?.subAgentId){let i=e.data.subAgentId,o=a.get(i);if(o)return{name:o.name,toolCallId:i,colorIndex:o.colorIndex}}let s=t.get(e.stableId);if(!s)return null;let n=a.get(s);return n?{name:n.name,toolCallId:s,colorIndex:n.colorIndex}:null},Ve=e=>{let t=re(e);return t?ie[t.colorIndex%ie.length]:null},je=e=>{if(window.trackClick){let t=oe.value.find(a=>a.type===e);window.trackClick("EventFilterClicked",{filterType:e,filterLabel:t?t.label:e,sessionId:m.value})}E.value=e},le=e=>{F.value="",E.value="all",ee.value=e.id,Vue.nextTick(()=>{if(g.value){let t=A.value.findIndex(a=>a.virtualIndex===e.index);if(t>=0){let a=s=>{s<=0||!g.value||(g.value.scrollToItem(t),setTimeout(()=>a(s-1),100))};setTimeout(()=>a(3),50)}}})},Fe=()=>{if(!g.value)return;let e=t=>{t<=0||!g.value||(g.value.scrollToItem(0),setTimeout(()=>e(t-1),100))};e(3)},We=()=>{if(!g.value)return;let e=A.value.length-1,t=a=>{a<=0||!g.value||(g.value.scrollToItem(e),setTimeout(()=>t(a-1),100))};t(5)},de=e=>{window.trackClick&&window.trackClick("TurnClicked",{turnNumber:e,sessionId:m.value});let t=D.value.find(a=>a.id===e);if(t){let a=`UserReq${t.userReqNumber}_Turn${t.id}`,s=`${window.location.pathname}?eventType=assistant.turn_start&eventName=${a}`;window.history.pushState({},"",s),le(t)}},Ge=e=>{if(!e)return"";let t=e.replace(/\/$/,"").split("/");return t[t.length-1]||e},Ke=e=>{let t=D.value.find(s=>s.index===e);if(!t)return"?";let a=t.originalTurnId??t.id;return t.userReqNumber>0?`${t.userReqNumber} - Turn ${a}`:`Turn ${a}`},Je=e=>D.value.find(a=>a.index===e)?.duration||null,ce=e=>{let t=document.createElement("div");return t.textContent=e,t.innerHTML},Xe=e=>e?new Date(e).toLocaleString():"N/A",Ye=async()=>{console.log("[Export] exportSession called"),window.trackClick&&window.trackClick("ExportClicked",{sessionId:m.value}),j.value=!0;try{console.log("[Export] Fetching:",`/session/${m.value}/export`);let e=await fetch(`/session/${m.value}/export`);if(console.log("[Export] Response received:",e.status,e.ok),console.log("[Export] Response received:",e.status,e.ok),!e.ok)throw new Error("Share failed");console.log("[Export] Creating blob...");let t=await e.blob();console.log("[Export] Blob size:",t.size,"type:",t.type);let a=window.URL.createObjectURL(t);console.log("[Export] Creating download link...");let s=document.createElement("a");s.href=a,s.download=`session-${m.value}.zip`,document.body.appendChild(s),s.click(),console.log("[Export] Download triggered"),window.URL.revokeObjectURL(a),document.body.removeChild(s),console.log("[Export] Showing success feedback...");let n="\u{1F4E4} Share Session",i="\u2713 Downloaded!",o=document.querySelector(".export-btn");o&&(o.textContent=i,o.style.background="#238636",console.log("[Export] Button text updated to:",o.textContent),setTimeout(()=>{o.textContent=n,o.style.background="",console.log("[Export] Button text restored")},2e3))}catch(e){console.error("[Export] Share session error:",e),alert("Failed to share session: "+e.message)}finally{j.value=!1,console.log("[Export] Export complete")}};X(async()=>{try{console.log("[Navigation] Starting event loading...");let a=await fetch(`/api/sessions/${m.value}/events`);if(!a.ok)throw new Error(`Failed to load events: ${a.statusText}`);let s=await a.json();if(Array.isArray(s))C.value=s;else if(s.events&&Array.isArray(s.events))C.value=s.events,console.log("[Navigation] Pagination:",s.pagination);else throw new Error("Invalid response format");if(console.log("[Navigation] Events loaded:",C.value.length),C.value.length>0){let c=C.value[C.value.length-1],l=c.timestamp||c.time||c.data?.timestamp;l&&(b.value.updated=new Date(l))}let n=new URLSearchParams(window.location.search),i=n.get("eventType"),o=n.get("eventName"),r=n.get("eventTimestamp");console.log("[Navigation] URL params:",i,o,r),i&&o&&(console.log("[Navigation] Waiting for Vue to render..."),Vue.nextTick(()=>{console.log("[Navigation] nextTick - flatEvents count:",T.value?.length);let c=null;if(i==="assistant.turn_start"){let l=o.match(/UserReq(\d+)_Turn(\d+)/);if(l){let d=parseInt(l[2],10);if(!isNaN(d)){console.log("[Navigation] Jumping to turn:",d),de(d);return}}}else i==="subagent.started"?(console.log("[Navigation] Searching for subagent:",o,"timestamp:",r),r&&(c=T.value.find(l=>l.type==="subagent.started"&&l.timestamp===r)),c||(c=T.value.find(l=>l.type==="subagent.started"&&(l.data?.agentDisplayName===o||l.data?.agentName===o||l.data?.label===o))),console.log("[Navigation] Target event found:",c?"YES":"NO","virtualIndex:",c?.virtualIndex)):c=T.value.find(l=>l.type===i);if(c){let l=A.value.findIndex(d=>d.virtualIndex===c.virtualIndex);if(console.log("[Navigation] Target in filteredEvents at index:",l),l>=0&&g.value){console.log("[Navigation] Scrolling to index:",l);let d=p=>{p<=0||!g.value||(g.value.scrollToItem(l),setTimeout(()=>d(p-1),100))};setTimeout(()=>d(3),50)}else console.log("[Navigation] Failed - targetIndex:",l,"scrollerRef:",!!g.value)}else console.log("[Navigation] Target event not found")}))}catch(a){console.error("Error loading events:",a),se.value=a.message}finally{ae.value=!1}window.addEventListener("keydown",a=>{a.ctrlKey&&a.key==="b"&&(a.preventDefault(),q.value=!q.value)}),window.marked&&marked.setOptions({breaks:!0,gfm:!0});let e=()=>{if(!g.value)return;let a=null;if(g.value.$el&&typeof g.value.$el.querySelector=="function"?a=g.value.$el.querySelector(".vue-recycle-scroller"):g.value.querySelector&&typeof g.value.querySelector=="function"&&(a=g.value.querySelector(".vue-recycle-scroller")),a||(a=document.querySelector(".vue-recycle-scroller")),a){let s=a.scrollTop,n=a.clientHeight,i=80,o=Math.floor(s/i),r=Math.ceil(n/i),c=Math.min(o+r,A.value.length),l=Math.max(1,o+1),d=Math.max(1,c);te.value={start:Math.min(l,d),end:d}}},t=null;setTimeout(()=>{e();let a=document.querySelector(".vue-recycle-scroller");a&&(a.addEventListener("scroll",e),t=()=>{a.removeEventListener("scroll",e)})},500),be(()=>{L&&(clearTimeout(L),L=null),t&&t(),k.value={},w.value={},h.clear()})});let O=u([]),W=u([]),_=u(!1),y=u([]),S=u(""),G=u(null),x=u(""),I=u(!1),P=u([]),ue=u(0),K=u(!1),Ze=e=>!e||e===0?"0":e<1e3?e.toString():Math.floor(e/1e3)+"K",Qe=e=>{if(!e||e===0)return"0s";let t=Math.floor(e/1e3);if(t<60)return(e/1e3).toFixed(1)+"s";let a=Math.floor(t/60),s=t%60;return`${a}m ${s}s`},et=v(()=>{if(!b.value.usage||!b.value.usage.modelMetrics)return 0;let e=0;for(let t in b.value.usage.modelMetrics){let a=b.value.usage.modelMetrics[t].usage;a&&(e+=(a.inputTokens||0)+(a.outputTokens||0))}return e}),tt=v(()=>{if(!b.value.usage||!b.value.usage.modelMetrics)return 0;let e=0;for(let t in b.value.usage.modelMetrics)e+=b.value.usage.modelMetrics[t].requests?.count||0;return e}),at=e=>{let t=b.value.usage?.modelMetrics[e];if(!t||!t.usage)return null;let a=t.usage.cacheReadTokens||0,s=t.usage.inputTokens||0;return s===0?null:Math.round(a/s*100)},st=e=>e==null?"":e+" premium",ot=()=>{K.value=!K.value},me=["#3b82f6","#10b981","#f59e0b","#ef4444","#8b5cf6","#ec4899","#06b6d4","#f97316"],nt=e=>{let t=0;for(let a=0;a<e.length;a++)t=e.charCodeAt(a)+((t<<5)-t);return me[Math.abs(t)%me.length]},it=async()=>{try{let e=await fetch(`/api/sessions/${m.value}/tags`);if(e.ok){let t=await e.json();O.value=t.tags||[]}}catch(e){console.error("Error loading tags:",e)}},ge=async()=>{try{let e=await fetch("/api/tags");if(e.ok){let t=await e.json();W.value=t.tags||[]}}catch(e){console.error("Error loading all tags:",e)}},rt=async e=>{try{window.trackClick&&e.filter(s=>!O.value.includes(s)).forEach(s=>{window.trackClick("TagAdded",{sessionId:m.value,tag:s})});let t=await fetch(`/api/sessions/${m.value}/tags`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({tags:e})});if(t.ok){let a=await t.json();return O.value=a.tags||[],x.value="",!0}else{let a=await t.json();return x.value=a.error||"Failed to save tags",!1}}catch(t){return console.error("Error saving tags:",t),x.value="Network error",!1}},lt=()=>{y.value=[...O.value],_.value=!0,x.value="",setTimeout(()=>{G.value&&G.value.focus()},10)},dt=()=>{_.value=!1,y.value=[],S.value="",I.value=!1,x.value=""},ve=()=>{let e=S.value.trim().toLowerCase();if(e){if(e.length>30){x.value="Tag must be 30 characters or less";return}if(y.value.length>=10){x.value="Maximum 10 tags per session";return}if(y.value.includes(e)){x.value="Tag already added",S.value="";return}y.value.push(e),S.value="",I.value=!1,x.value=""}},ct=e=>{y.value=y.value.filter(t=>t!==e),x.value=""},ut=()=>{let e=S.value.trim().toLowerCase();if(!e){I.value=!1,P.value=[];return}let t=W.value.filter(a=>a.toLowerCase().includes(e)&&!y.value.includes(a)).slice(0,5);t.length>0?(I.value=!0,P.value=t,ue.value=0):(I.value=!1,P.value=[])},mt=e=>{S.value=e,ve()},gt=async()=>{setTimeout(async()=>{if(!_.value)return;await rt(y.value)&&(_.value=!1,y.value=[],S.value="",I.value=!1,await ge())},200)};return X(async()=>{await it(),await ge()}),{sessionId:m,metadata:b,exporting:j,sidebarCollapsed:q,expandedTools:k,expandedContent:w,expansionCount:we,currentFilter:E,searchText:F,currentTurnIndex:ee,scrollerRef:g,visibleRange:te,loadedEvents:C,eventsLoading:ae,eventsError:se,flatEvents:T,filteredEvents:A,eventCounts:xe,filters:oe,turns:D,userReqs:Ce,truncateText:Se,formatTime:Ee,formatToolTime:Me,formatDateTime:Xe,renderMarkdown:Ne,highlightSearchText:Le,toggleTool:Re,toggleContent:Ae,isContentTooLong:De,truncateContent:Oe,getBadgeInfo:_e,getToolStatus:$e,getToolErrorMessage:qe,getToolDuration:Ue,getToolCommand:Pe,hasTools:Be,getToolGroups:ze,getSubagentInfo:re,getSubagentColor:Ve,setFilter:je,scrollToTurn:le,scrollToTop:Fe,scrollToBottom:We,jumpToTurn:de,getTurnNumber:Ke,getTurnDuration:Je,repoBasename:Ge,escapeHtml:ce,exportSession:Ye,searchResultCount:ke,sessionTags:O,allTags:W,tagsEditing:_,editingTags:y,tagInputValue:S,tagInputRef:G,tagsError:x,showAutocomplete:I,autocompleteOptions:P,autocompleteSelectedIndex:ue,getTagColor:nt,startEditTags:lt,cancelEditTags:dt,addTag:ve,removeTagFromEdit:ct,updateAutocomplete:ut,selectAutocompleteOption:mt,saveTagsOnBlur:gt,usageExpanded:K,toggleUsage:ot,formatTokens:Ze,formatDuration:Qe,formatCost:st,totalTokens:et,totalRequests:tt,getCacheHitRatio:at}},template:`
|
|
10
|
+
...`},nt=(e,t)=>{if(t?.data?.badgeLabel&&t?.data?.badgeClass)return{label:t.data.badgeLabel,class:t.data.badgeClass};if(e==="message"&&t?.data?.role==="toolResult")return{label:"TOOL RESULT",class:"badge-tool"};if(e==="session.model_change")return{label:"MODEL CHANGE",class:"badge-session"};if(e==="session.truncation")return{label:"TRUNCATION",class:"badge-truncation"};if(e==="session.compaction_start"||e==="session.compaction_complete")return{label:"COMPACTION",class:"badge-compaction"};if(e==="system.notification")return{label:"SYSTEM",class:"badge-system"};let s=(e||"").split(".")[0]||"unknown";return{user:{label:"USER",class:"badge-user"},assistant:{label:"ASSISTANT",class:"badge-assistant"},reasoning:{label:"REASONING",class:"badge-reasoning"},turn:{label:"TURN",class:"badge-turn"},tool:{label:"TOOL",class:"badge-tool"},subagent:{label:"SUBAGENT",class:"badge-subagent"},skill:{label:"SKILL",class:"badge-skill"},session:{label:"SESSION",class:"badge-session"},error:{label:"ERROR",class:"badge-error"},abort:{label:"ABORT",class:"badge-error"}}[s]||{label:s.toUpperCase(),class:"badge-info"}},ot=e=>{if(!e.complete)return{icon:"\u23F3",color:"tool-status-running",text:""};let t=e.complete.data||{};return t.error||t.isError?{icon:"\u274C",color:"tool-status-error",text:""}:{icon:"\u2713",color:"tool-status-success",text:""}},it=e=>{if(!e.complete?.data?.error)return"";let t=e.complete.data.error;if(typeof t=="object"&&t.message)return t.message;if(typeof t=="string"){try{let a=JSON.parse(t);if(a.message)return a.message}catch{}return t}return String(t)},rt=e=>{if(!e.complete)return"";let t=new Date(e.start.timestamp).getTime(),s=new Date(e.complete.timestamp).getTime()-t;return s>=100?`${parseFloat((s/1e3).toPrecision(3))}s`:""},Ht=e=>{let t={};if(e.start?.timestamp&&(t.startTime=e.start.timestamp),e.complete?.timestamp&&(t.endTime=e.complete.timestamp),t.startTime&&t.endTime){let a=new Date(t.endTime).getTime()-new Date(t.startTime).getTime();a>=0&&(t.duration=`${parseFloat((a/1e3).toPrecision(3))}s (${a}ms)`)}return t},lt=e=>{if(!e.start)return"";let t=e.start.data?.arguments||{},a=e.start.data?.toolName||e.tool||"",s="";if(a==="bash"||a==="exec")s=t.command||t.description||"";else if(a==="ask_user")s=t.question||t.message||"";else if(a==="read"||a==="write"||a==="edit")s=t.file_path||t.path||"";else if(a==="view")s=t.path||t.file||"";else if(a==="create")s=t.path||t.name||"";else if(a==="report_intent")s=t.intent||t.message||"";else if(a==="web_search")s=t.query||"";else if(a==="web_fetch")s=t.url||"";else if(a==="browser"){let o=t.action||"",i=t.targetUrl||t.url||"";s=i?`${o} ${i}`:o}else s=t.description||t.command||t.message||t.path||t.file_path||t.query||"";return s&&s.length>200&&(s=s.substring(0,200)+"..."),s},dt=e=>e.data?.tools&&e.data.tools.length>0,ct=e=>e.data?.tools&&Array.isArray(e.data.tools)?e.data.tools.filter(t=>t&&typeof t=="object"&&t.name).map(t=>{let a=t.result!==void 0||t.status==="completed"||t.status==="error",s={};if(t.startTime&&(s.startTime=t.startTime),t.endTime&&(s.endTime=t.endTime),s.startTime&&s.endTime){let o=new Date(s.endTime).getTime()-new Date(s.startTime).getTime();o>=0&&(s.duration=`${parseFloat((o/1e3).toPrecision(3))}s (${o}ms)`)}return{tool:t.name,timing:s,start:{timestamp:t.startTime,data:{toolName:t.name,arguments:t.input||t.arguments||{}}},complete:a?{timestamp:t.endTime,data:{result:t.result,error:t.status==="error"?t.error:null}}:null}}):[],me=["#58a6ff","#f0883e","#a371f7","#3fb950","#f778ba","#79c0ff","#d29922","#56d4dd"],ut=e=>{let t=0;for(let a=0;a<e.length;a++){let s=e.charCodeAt(a);t=(t<<5)-t+s,t=t&t}return t},Se=e=>{let{ownerMap:t,subagentInfo:a}=G.value;if(e.type==="subagent.started"||e.type==="subagent.completed"||e.type==="subagent.failed"){let i=e.data?.toolCallId;if(i&&a.has(i)){let r=a.get(i);return{name:r.name,toolCallId:i,colorIndex:r.colorIndex}}return null}if(e._subagent){let i=e._subagent.id,r=e._subagent.name;if(a.has(i)){let d=a.get(i);return{name:d.name,toolCallId:i,colorIndex:d.colorIndex}}return{name:r,toolCallId:i,colorIndex:Math.abs(ut(i))}}if(e.data?.subAgentId){let i=e.data.subAgentId,r=a.get(i);if(r)return{name:r.name,toolCallId:i,colorIndex:r.colorIndex}}let s=t.get(e.stableId);if(!s)return null;let o=a.get(s);return o?{name:o.name,toolCallId:s,colorIndex:o.colorIndex}:null},mt=e=>{let t=Se(e);return t?me[t.colorIndex%me.length]:null},gt=e=>{if(window.trackClick){let t=we.value.find(a=>a.type===e);window.trackClick("EventFilterClicked",{filterType:e,filterLabel:t?t.label:e,sessionId:h.value})}R.value=e},pt=e=>{A.value=e,Z.value=!1,de.value="",e&&(R.value="all"),window.trackClick&&window.trackClick("SubagentSelected",{subagent:e,sessionId:h.value})},Ie=e=>{P.value="",R.value="all",A.value=null,he.value=e.id,Vue.nextTick(()=>{if(k.value){let t=H.value.findIndex(a=>a.virtualIndex===e.index);if(t>=0){let a=s=>{s<=0||!k.value||(k.value.scrollToItem(t),setTimeout(()=>a(s-1),100))};setTimeout(()=>a(3),50)}}})},vt=()=>{if(!k.value)return;let e=t=>{t<=0||!k.value||(k.value.scrollToItem(0),setTimeout(()=>e(t-1),100))};e(3)},ft=()=>{if(!k.value)return;let e=H.value.length-1,t=a=>{a<=0||!k.value||(k.value.scrollToItem(e),setTimeout(()=>t(a-1),100))};t(5)},Ee=e=>{window.trackClick&&window.trackClick("TurnClicked",{turnNumber:e,sessionId:h.value});let t=W.value.find(a=>a.id===e);if(t){let a=`UserReq${t.userReqNumber}_Turn${t.id}`,s=`${window.location.pathname}?eventType=assistant.turn_start&eventName=${a}`;window.history.pushState({},"",s),Ie(t)}},bt=e=>{if(!e)return"";let t=e.replace(/\/$/,"").split("/");return t[t.length-1]||e},ht=e=>{let t=W.value.find(s=>s.index===e);if(!t)return"?";let a=t.originalTurnId??t.id;return t.userReqNumber>0?`${t.userReqNumber} - Turn ${a}`:`Turn ${a}`},Tt=e=>W.value.find(a=>a.index===e)?.duration||null,Me=e=>{let t=document.createElement("div");return t.textContent=e,t.innerHTML},yt=e=>e?new Date(e).toLocaleString():"N/A",kt=async()=>{console.log("[Export] exportSession called"),window.trackClick&&window.trackClick("ExportClicked",{sessionId:h.value}),le.value=!0;try{console.log("[Export] Fetching:",`/session/${h.value}/export`);let e=await fetch(`/session/${h.value}/export`);if(console.log("[Export] Response received:",e.status,e.ok),console.log("[Export] Response received:",e.status,e.ok),!e.ok)throw new Error("Share failed");console.log("[Export] Creating blob...");let t=await e.blob();console.log("[Export] Blob size:",t.size,"type:",t.type);let a=window.URL.createObjectURL(t);console.log("[Export] Creating download link...");let s=document.createElement("a");s.href=a,s.download=`session-${h.value}.zip`,document.body.appendChild(s),s.click(),console.log("[Export] Download triggered"),window.URL.revokeObjectURL(a),document.body.removeChild(s),console.log("[Export] Showing success feedback...");let o="\u{1F4E4} Share Session",i="\u2713 Downloaded!",r=document.querySelector(".export-btn");r&&(r.textContent=i,r.style.background="#238636",console.log("[Export] Button text updated to:",r.textContent),setTimeout(()=>{r.textContent=o,r.style.background="",console.log("[Export] Button text restored")},2e3))}catch(e){console.error("[Export] Share session error:",e),alert("Failed to share session: "+e.message)}finally{le.value=!1,console.log("[Export] Export complete")}},Ne=e=>{let t=document.querySelector(".filter-type-wrapper");t&&!t.contains(e.target)&&(ce.value=!1)},Re=e=>{e.ctrlKey&&e.key==="b"&&(e.preventDefault(),Y.value=!Y.value)};l(()=>{document.removeEventListener("click",Ne),window.removeEventListener("keydown",Re),j&&(clearTimeout(j),j=null),ee&&(ee(),ee=null),D.value={},L.value={},E.clear()}),n(async()=>{document.addEventListener("click",Ne),document.addEventListener("click",()=>{Z.value=!1});try{console.log("[Navigation] Starting event loading...");let t=await fetch(`/api/sessions/${h.value}/events`);if(!t.ok)throw new Error(`Failed to load events: ${t.statusText}`);let a=await t.json();if(Array.isArray(a))O.value=a;else if(a.events&&Array.isArray(a.events))O.value=a.events,console.log("[Navigation] Pagination:",a.pagination);else throw new Error("Invalid response format");if(console.log("[Navigation] Events loaded:",O.value.length),O.value.length>0){let d=O.value[O.value.length-1],c=d.timestamp||d.time||d.data?.timestamp;c&&(w.value.updated=new Date(c))}let s=new URLSearchParams(window.location.search),o=s.get("eventType"),i=s.get("eventName"),r=s.get("eventTimestamp");console.log("[Navigation] URL params:",o,i,r),o&&i&&(console.log("[Navigation] Waiting for Vue to render..."),Vue.nextTick(()=>{console.log("[Navigation] nextTick - flatEvents count:",S.value?.length);let d=null;if(o==="assistant.turn_start"){let c=i.match(/UserReq(\d+)_Turn(\d+)/);if(c){let T=parseInt(c[2],10);if(!isNaN(T)){console.log("[Navigation] Jumping to turn:",T),Ee(T);return}}}else o==="subagent.started"?(console.log("[Navigation] Searching for subagent:",i,"timestamp:",r),r&&(d=S.value.find(c=>c.type==="subagent.started"&&c.timestamp===r)),d||(d=S.value.find(c=>c.type==="subagent.started"&&(c.data?.agentDisplayName===i||c.data?.agentName===i||c.data?.label===i))),console.log("[Navigation] Target event found:",d?"YES":"NO","virtualIndex:",d?.virtualIndex)):d=S.value.find(c=>c.type===o);if(d){let c=H.value.findIndex(T=>T.virtualIndex===d.virtualIndex);if(console.log("[Navigation] Target in filteredEvents at index:",c),c>=0&&k.value){console.log("[Navigation] Scrolling to index:",c);let T=y=>{y<=0||!k.value||(k.value.scrollToItem(c),setTimeout(()=>T(y-1),100))};setTimeout(()=>T(3),50)}else console.log("[Navigation] Failed - targetIndex:",c,"scrollerRef:",!!k.value)}else console.log("[Navigation] Target event not found")}))}catch(t){console.error("Error loading events:",t),xe.value=t.message}finally{ke.value=!1}window.addEventListener("keydown",Re),window.marked&&marked.setOptions({breaks:!0,gfm:!0});let e=()=>{if(!k.value)return;let t=null;if(k.value.$el&&typeof k.value.$el.querySelector=="function"?t=k.value.$el.querySelector(".vue-recycle-scroller"):k.value.querySelector&&typeof k.value.querySelector=="function"&&(t=k.value.querySelector(".vue-recycle-scroller")),t||(t=document.querySelector(".vue-recycle-scroller")),t){let a=t.scrollTop,s=t.clientHeight,o=80,i=Math.floor(a/o),r=Math.ceil(s/o),d=Math.min(i+r,H.value.length),c=Math.max(1,i+1),T=Math.max(1,d);Te.value={start:Math.min(c,T),end:T}}};setTimeout(()=>{e();let t=document.querySelector(".vue-recycle-scroller");t&&(t.addEventListener("scroll",e),ee=()=>{t.removeEventListener("scroll",e)})},500)});let K=m([]),ge=m([]),J=m(!1),M=m([]),_=m(""),pe=m(null),N=m(""),F=m(!1),te=m([]),Ae=m(0),xt=e=>!e||e===0?"0":e<1e3?e.toString():Math.floor(e/1e3)+"K",wt=e=>{if(!e||e===0)return"0s";let t=Math.floor(e/1e3);if(t<60)return(e/1e3).toFixed(1)+"s";let a=Math.floor(t/60),s=t%60;return`${a}m ${s}s`},Ct=b(()=>{if(!w.value.usage||!w.value.usage.modelMetrics)return 0;let e=0;for(let t in w.value.usage.modelMetrics){let a=w.value.usage.modelMetrics[t].usage;a&&(e+=(a.inputTokens||0)+(a.outputTokens||0))}return e}),St=b(()=>{if(!w.value.usage||!w.value.usage.modelMetrics)return 0;let e=0;for(let t in w.value.usage.modelMetrics)e+=w.value.usage.modelMetrics[t].requests?.count||0;return e}),It=b(()=>!w.value.usage||!w.value.usage.modelMetrics?0:Object.keys(w.value.usage.modelMetrics).length),Et=e=>{let t=w.value.usage?.modelMetrics[e];return!t||!t.usage?null:x(t.usage)},Mt=e=>{let t=w.value.usage?.modelMetrics[e];return!t||!t.usage?0:f(t.usage)},Nt=b(()=>{let e=new Map;for(let t of S.value)if(t.data?.tools&&Array.isArray(t.data.tools))for(let a of t.data.tools)a&&a.name&&e.set(a.name,(e.get(a.name)||0)+1);return Array.from(e,([t,a])=>({name:t,count:a})).sort((t,a)=>a.count-t.count)}),Rt=e=>e==null?"":e+" premium",De=["#3b82f6","#10b981","#f59e0b","#ef4444","#8b5cf6","#ec4899","#06b6d4","#f97316"],At=e=>{let t=0;for(let a=0;a<e.length;a++)t=e.charCodeAt(a)+((t<<5)-t);return De[Math.abs(t)%De.length]},Dt=async()=>{try{let e=await fetch(`/api/sessions/${h.value}/tags`);if(e.ok){let t=await e.json();K.value=t.tags||[]}}catch(e){console.error("Error loading tags:",e)}},Le=async()=>{try{let e=await fetch("/api/tags");if(e.ok){let t=await e.json();ge.value=t.tags||[]}}catch(e){console.error("Error loading all tags:",e)}},Lt=async e=>{try{window.trackClick&&e.filter(s=>!K.value.includes(s)).forEach(s=>{window.trackClick("TagAdded",{sessionId:h.value,tag:s})});let t=await fetch(`/api/sessions/${h.value}/tags`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({tags:e})});if(t.ok){let a=await t.json();return K.value=a.tags||[],N.value="",!0}else{let a=await t.json();return N.value=a.error||"Failed to save tags",!1}}catch(t){return console.error("Error saving tags:",t),N.value="Network error",!1}},Ot=()=>{M.value=[...K.value],J.value=!0,N.value="",setTimeout(()=>{pe.value&&pe.value.focus()},10)},_t=()=>{J.value=!1,M.value=[],_.value="",F.value=!1,N.value=""},Oe=()=>{let e=_.value.trim().toLowerCase();if(e){if(e.length>30){N.value="Tag must be 30 characters or less";return}if(M.value.length>=10){N.value="Maximum 10 tags per session";return}if(M.value.includes(e)){N.value="Tag already added",_.value="";return}M.value.push(e),_.value="",F.value=!1,N.value=""}},$t=e=>{M.value=M.value.filter(t=>t!==e),N.value=""},qt=()=>{let e=_.value.trim().toLowerCase();if(!e){F.value=!1,te.value=[];return}let t=ge.value.filter(a=>a.toLowerCase().includes(e)&&!M.value.includes(a)).slice(0,5);t.length>0?(F.value=!0,te.value=t,Ae.value=0):(F.value=!1,te.value=[])},Ut=e=>{_.value=e,Oe()},Ft=async()=>{setTimeout(async()=>{if(!J.value)return;await Lt(M.value)&&(J.value=!1,M.value=[],_.value="",F.value=!1,await Le())},200)};return n(async()=>{await Dt(),await Le()}),{sessionId:h,metadata:w,exporting:le,sidebarCollapsed:Y,expandedTools:D,expandedContent:L,expansionCount:He,currentFilter:R,searchText:P,currentTurnIndex:he,scrollerRef:k,visibleRange:Te,loadedEvents:O,eventsLoading:ke,eventsError:xe,flatEvents:S,filteredEvents:H,eventCounts:Pe,filters:we,turns:W,userReqs:We,truncateText:Ge,formatTime:Xe,formatToolTime:Ye,formatDateTime:yt,renderMarkdown:Qe,highlightSearchText:et,toggleTool:Ze,toggleContent:tt,isContentTooLong:at,truncateContent:st,getBadgeInfo:nt,getToolStatus:ot,getToolErrorMessage:it,getToolDuration:rt,getToolCommand:lt,hasTools:dt,getToolGroups:ct,getSubagentInfo:Se,getSubagentColor:mt,subagentOwnership:G,setFilter:gt,selectSubagent:pt,selectedSubagent:A,subagentList:ue,filteredSubagentList:Ke,subagentDropdownOpen:Z,subagentSearchQuery:de,subagentSearchRef:ye,subagentTokenUsage:Je,SUBAGENT_COLORS:me,typeFilterOpen:ce,activeFilterCount:Be,clearAllFilters:ze,scrollToTurn:Ie,scrollToTop:vt,scrollToBottom:ft,jumpToTurn:Ee,getTurnNumber:ht,getTurnDuration:Tt,repoBasename:bt,escapeHtml:Me,exportSession:kt,searchResultCount:je,sessionTags:K,allTags:ge,tagsEditing:J,editingTags:M,tagInputValue:_,tagInputRef:pe,tagsError:N,showAutocomplete:F,autocompleteOptions:te,autocompleteSelectedIndex:Ae,getTagColor:At,startEditTags:Ot,cancelEditTags:_t,addTag:Oe,removeTagFromEdit:$t,updateAutocomplete:qt,selectAutocompleteOption:Ut,saveTagsOnBlur:Ft,formatTokens:xt,formatDuration:wt,formatCost:Rt,totalTokens:Ct,totalRequests:St,totalModels:It,getDisplayUsageInputTokens:Mt,getModelCacheHitRatio:Et,toolCallingSummary:Nt}},template:`
|
|
11
11
|
<div class="container">
|
|
12
12
|
<div class="header">
|
|
13
13
|
<a href="/" class="home-btn">\u2190 Back to Home</a>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
</h1>
|
|
17
17
|
<div style="display: flex; gap: 10px;">
|
|
18
18
|
<a :href="'/session/' + sessionId + '/time-analyze'" class="time-analyze-btn" @click="trackClick && trackClick('TimeAnalyzeClicked', { sessionId: sessionId })">\u23F1 Analysis</a>
|
|
19
|
-
<button @click="exportSession" class="export-btn" :disabled="exporting">
|
|
19
|
+
<button @click="exportSession" class="export-btn" :disabled="exporting" v-if="!metadata.source || !['vscode', 'modernize'].includes(metadata.source)">
|
|
20
20
|
{{ exporting ? '\u23F3 Sharing...' : '\u{1F4E4} Share Session' }}
|
|
21
21
|
</button>
|
|
22
22
|
</div>
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
<div class="sidebar-section">
|
|
34
34
|
<div class="sidebar-section-title">Session Info</div>
|
|
35
35
|
<div class="session-info">
|
|
36
|
-
<div v-if="metadata.summary" class="session-summary-block">{{ metadata.summary }}</div>
|
|
37
36
|
<table class="session-info-table">
|
|
38
37
|
<tbody>
|
|
39
38
|
<tr v-if="metadata.source">
|
|
@@ -45,7 +44,15 @@
|
|
|
45
44
|
</span>
|
|
46
45
|
</td>
|
|
47
46
|
</tr>
|
|
48
|
-
<tr v-if="metadata.
|
|
47
|
+
<tr v-if="metadata.modernizeVersion">
|
|
48
|
+
<td>Version</td>
|
|
49
|
+
<td>{{ metadata.modernizeVersion }}</td>
|
|
50
|
+
</tr>
|
|
51
|
+
<tr v-if="metadata.source === 'modernize' && metadata.copilotVersion">
|
|
52
|
+
<td>Copilot SDK</td>
|
|
53
|
+
<td>{{ metadata.copilotVersion }}</td>
|
|
54
|
+
</tr>
|
|
55
|
+
<tr v-if="metadata.copilotVersion && metadata.source !== 'modernize'">
|
|
49
56
|
<td>Version</td>
|
|
50
57
|
<td>{{ metadata.copilotVersion }}</td>
|
|
51
58
|
</tr>
|
|
@@ -53,6 +60,10 @@
|
|
|
53
60
|
<td>Model</td>
|
|
54
61
|
<td>{{ metadata.model }}</td>
|
|
55
62
|
</tr>
|
|
63
|
+
<tr v-if="metadata.agentName">
|
|
64
|
+
<td>Agent</td>
|
|
65
|
+
<td>\u{1F916} {{ metadata.agentName }}</td>
|
|
66
|
+
</tr>
|
|
56
67
|
<tr v-if="metadata.repo">
|
|
57
68
|
<td>Repo</td>
|
|
58
69
|
<td>{{ metadata.repo }}</td>
|
|
@@ -80,34 +91,66 @@
|
|
|
80
91
|
|
|
81
92
|
<!-- Usage Section -->
|
|
82
93
|
<div v-if="metadata.usage" class="sidebar-section">
|
|
83
|
-
<div class="sidebar-section-title">Usage</div>
|
|
94
|
+
<div class="sidebar-section-title">Token Usage</div>
|
|
84
95
|
<div class="usage-container">
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
96
|
+
<div class="usage-summary">
|
|
97
|
+
<div class="usage-summary-eyebrow">Overview</div>
|
|
98
|
+
<div class="usage-summary-total">
|
|
99
|
+
{{ formatTokens(totalTokens) }} <span class="usage-summary-total-unit">tokens</span>
|
|
100
|
+
</div>
|
|
101
|
+
<div class="usage-summary-caption">
|
|
102
|
+
Usage captured across {{ totalModels }} model{{ totalModels === 1 ? '' : 's' }}
|
|
103
|
+
</div>
|
|
104
|
+
<div class="usage-summary-metrics">
|
|
105
|
+
<div class="usage-metric-card usage-metric-card-summary">
|
|
106
|
+
<span class="usage-metric-label">Requests</span>
|
|
107
|
+
<span class="usage-metric-value">{{ totalRequests }} reqs</span>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="usage-metric-card usage-metric-card-summary">
|
|
110
|
+
<span class="usage-metric-label">Models</span>
|
|
111
|
+
<span class="usage-metric-value">{{ totalModels }}</span>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="usage-metric-card usage-metric-card-summary">
|
|
114
|
+
<span class="usage-metric-label">API Time</span>
|
|
115
|
+
<span class="usage-metric-value">{{ formatDuration(metadata.usage.totalApiDurationMs) }}</span>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
89
118
|
</div>
|
|
90
119
|
|
|
91
|
-
|
|
92
|
-
<div v-if="usageExpanded" class="usage-expanded">
|
|
120
|
+
<div class="usage-expanded">
|
|
93
121
|
<!-- Model breakdown -->
|
|
94
122
|
<div v-if="Object.keys(metadata.usage.modelMetrics).length > 0" class="usage-section">
|
|
95
|
-
<div class="usage-section-
|
|
96
|
-
|
|
97
|
-
<div class="usage-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
<div
|
|
102
|
-
<
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
123
|
+
<div class="usage-section-header">
|
|
124
|
+
<div class="usage-section-title">Models</div>
|
|
125
|
+
<div class="usage-section-badge">{{ totalModels }}</div>
|
|
126
|
+
</div>
|
|
127
|
+
<div class="usage-model-list">
|
|
128
|
+
<div v-for="(metrics, model) in metadata.usage.modelMetrics" :key="model" class="usage-model">
|
|
129
|
+
<div class="usage-model-header">
|
|
130
|
+
<div class="usage-model-name" :title="model">{{ model }}</div>
|
|
131
|
+
<div class="usage-model-meta">
|
|
132
|
+
<span class="usage-meta-pill">{{ metrics.requests?.count || 0 }} reqs</span>
|
|
133
|
+
<span v-if="metrics.requests?.cost" class="usage-meta-pill usage-meta-pill-premium">{{ formatCost(metrics.requests.cost) }}</span>
|
|
134
|
+
<span v-if="getModelCacheHitRatio(model) !== null" class="usage-meta-pill usage-meta-pill-cache">{{ getModelCacheHitRatio(model) }}% cache</span>
|
|
135
|
+
</div>
|
|
108
136
|
</div>
|
|
109
|
-
<div v-if="metrics.usage
|
|
110
|
-
<
|
|
137
|
+
<div v-if="metrics.usage" class="usage-metric-grid">
|
|
138
|
+
<div class="usage-metric-card">
|
|
139
|
+
<span class="usage-metric-label">Input</span>
|
|
140
|
+
<span class="usage-metric-value">{{ formatTokens(getDisplayUsageInputTokens(model)) }}</span>
|
|
141
|
+
</div>
|
|
142
|
+
<div class="usage-metric-card">
|
|
143
|
+
<span class="usage-metric-label">Output</span>
|
|
144
|
+
<span class="usage-metric-value">{{ formatTokens(metrics.usage.outputTokens || 0) }}</span>
|
|
145
|
+
</div>
|
|
146
|
+
<div v-if="metrics.usage?.cacheReadTokens" class="usage-metric-card">
|
|
147
|
+
<span class="usage-metric-label">Cache Read</span>
|
|
148
|
+
<span class="usage-metric-value">{{ formatTokens(metrics.usage.cacheReadTokens) }}</span>
|
|
149
|
+
</div>
|
|
150
|
+
<div v-if="metrics.usage?.cacheWriteTokens" class="usage-metric-card">
|
|
151
|
+
<span class="usage-metric-label">Cache Write</span>
|
|
152
|
+
<span class="usage-metric-value">{{ formatTokens(metrics.usage.cacheWriteTokens) }}</span>
|
|
153
|
+
</div>
|
|
111
154
|
</div>
|
|
112
155
|
</div>
|
|
113
156
|
</div>
|
|
@@ -115,48 +158,62 @@
|
|
|
115
158
|
|
|
116
159
|
<!-- Context window breakdown -->
|
|
117
160
|
<div v-if="metadata.usage.currentTokens || metadata.usage.systemTokens || metadata.usage.conversationTokens || metadata.usage.toolDefinitionsTokens" class="usage-section">
|
|
118
|
-
<div class="usage-section-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
161
|
+
<div class="usage-section-header">
|
|
162
|
+
<div class="usage-section-title">Context Window</div>
|
|
163
|
+
</div>
|
|
164
|
+
<div class="usage-metric-grid">
|
|
165
|
+
<div v-if="metadata.usage.currentTokens" class="usage-metric-card">
|
|
166
|
+
<span class="usage-metric-label">Current</span>
|
|
167
|
+
<span class="usage-metric-value">{{ formatTokens(metadata.usage.currentTokens) }}</span>
|
|
122
168
|
</div>
|
|
123
|
-
<div v-if="metadata.usage.systemTokens">
|
|
124
|
-
<span class="usage-
|
|
169
|
+
<div v-if="metadata.usage.systemTokens" class="usage-metric-card">
|
|
170
|
+
<span class="usage-metric-label">System</span>
|
|
171
|
+
<span class="usage-metric-value">{{ formatTokens(metadata.usage.systemTokens) }}</span>
|
|
125
172
|
</div>
|
|
126
|
-
<div v-if="metadata.usage.conversationTokens">
|
|
127
|
-
<span class="usage-
|
|
173
|
+
<div v-if="metadata.usage.conversationTokens" class="usage-metric-card">
|
|
174
|
+
<span class="usage-metric-label">Conversation</span>
|
|
175
|
+
<span class="usage-metric-value">{{ formatTokens(metadata.usage.conversationTokens) }}</span>
|
|
128
176
|
</div>
|
|
129
|
-
<div v-if="metadata.usage.toolDefinitionsTokens">
|
|
130
|
-
<span class="usage-
|
|
177
|
+
<div v-if="metadata.usage.toolDefinitionsTokens" class="usage-metric-card">
|
|
178
|
+
<span class="usage-metric-label">Tools</span>
|
|
179
|
+
<span class="usage-metric-value">{{ formatTokens(metadata.usage.toolDefinitionsTokens) }}</span>
|
|
131
180
|
</div>
|
|
132
181
|
</div>
|
|
133
182
|
</div>
|
|
134
183
|
|
|
135
184
|
<!-- Code changes -->
|
|
136
185
|
<div v-if="metadata.usage.codeChanges && (metadata.usage.codeChanges.linesAdded > 0 || metadata.usage.codeChanges.linesRemoved > 0)" class="usage-section">
|
|
137
|
-
<div class="usage-section-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
<
|
|
186
|
+
<div class="usage-section-header">
|
|
187
|
+
<div class="usage-section-title">Code Changes</div>
|
|
188
|
+
</div>
|
|
189
|
+
<div class="usage-metric-grid usage-metric-grid-compact">
|
|
190
|
+
<div class="usage-metric-card">
|
|
191
|
+
<span class="usage-metric-label">Added</span>
|
|
192
|
+
<span class="usage-metric-value usage-metric-value-added">+{{ metadata.usage.codeChanges.linesAdded }}</span>
|
|
193
|
+
</div>
|
|
194
|
+
<div class="usage-metric-card">
|
|
195
|
+
<span class="usage-metric-label">Removed</span>
|
|
196
|
+
<span class="usage-metric-value usage-metric-value-removed">-{{ metadata.usage.codeChanges.linesRemoved }}</span>
|
|
197
|
+
</div>
|
|
198
|
+
<div class="usage-metric-card">
|
|
199
|
+
<span class="usage-metric-label">Files</span>
|
|
200
|
+
<span class="usage-metric-value">{{ metadata.usage.codeChanges.filesModified?.length || 0 }}</span>
|
|
201
|
+
</div>
|
|
142
202
|
</div>
|
|
143
203
|
</div>
|
|
144
204
|
</div>
|
|
145
205
|
</div>
|
|
146
206
|
</div>
|
|
147
207
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
<div class="
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
:
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
>
|
|
158
|
-
{{ filter.label }}
|
|
159
|
-
</button>
|
|
208
|
+
<!-- Tool Calling Summary -->
|
|
209
|
+
<div v-if="toolCallingSummary.length" class="sidebar-section">
|
|
210
|
+
<div class="sidebar-section-title">Tool Calls</div>
|
|
211
|
+
<div class="tool-summary-list">
|
|
212
|
+
<div v-for="item in toolCallingSummary" :key="item.name" class="tool-summary-item">
|
|
213
|
+
<div class="tool-summary-bar" :style="{ width: (item.count / toolCallingSummary[0].count * 100) + '%' }"></div>
|
|
214
|
+
<span class="tool-summary-name" :title="item.name">{{ item.name }}</span>
|
|
215
|
+
<span class="tool-summary-count">{{ item.count }}</span>
|
|
216
|
+
</div>
|
|
160
217
|
</div>
|
|
161
218
|
</div>
|
|
162
219
|
|
|
@@ -216,8 +273,8 @@
|
|
|
216
273
|
</div>
|
|
217
274
|
|
|
218
275
|
<div class="content">
|
|
219
|
-
<div class="
|
|
220
|
-
<div class="
|
|
276
|
+
<div class="unified-filter-bar">
|
|
277
|
+
<div class="filter-bar-row">
|
|
221
278
|
<button
|
|
222
279
|
class="sidebar-toggle"
|
|
223
280
|
@click="() => { sidebarCollapsed = !sidebarCollapsed; trackClick && trackClick('SidebarToggled', { state: sidebarCollapsed ? 'open' : 'collapsed', sessionId: sessionId }); }"
|
|
@@ -226,6 +283,20 @@
|
|
|
226
283
|
\u2630
|
|
227
284
|
</button>
|
|
228
285
|
|
|
286
|
+
<div class="filter-bar-search">
|
|
287
|
+
<input
|
|
288
|
+
v-model="searchText"
|
|
289
|
+
type="text"
|
|
290
|
+
placeholder="\u{1F50D} Search events..."
|
|
291
|
+
class="search-input"
|
|
292
|
+
/>
|
|
293
|
+
<span v-if="searchResultCount" class="search-result-count">
|
|
294
|
+
{{ searchResultCount }}
|
|
295
|
+
</span>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<div class="filter-bar-divider"></div>
|
|
299
|
+
|
|
229
300
|
<!-- Turn dropdown with optgroup -->
|
|
230
301
|
<select
|
|
231
302
|
v-if="turns.length > 0"
|
|
@@ -243,19 +314,104 @@
|
|
|
243
314
|
</option>
|
|
244
315
|
</optgroup>
|
|
245
316
|
</select>
|
|
317
|
+
|
|
318
|
+
<div class="filter-bar-divider"></div>
|
|
319
|
+
|
|
320
|
+
<!-- Subagent selector (rich dropdown with search) -->
|
|
321
|
+
<div v-if="subagentList.length > 0" class="subagent-selector" style="position:relative">
|
|
322
|
+
<button
|
|
323
|
+
class="subagent-dropdown-trigger"
|
|
324
|
+
@click.stop="subagentDropdownOpen = !subagentDropdownOpen"
|
|
325
|
+
>
|
|
326
|
+
<span class="subagent-trigger-icon">\u{1F916}</span>
|
|
327
|
+
<span class="subagent-trigger-label">{{ selectedSubagent ? (subagentList.find(s => s.toolCallId === selectedSubagent)?.name || 'Agent') : 'All Agents' }}</span>
|
|
328
|
+
<span class="subagent-trigger-arrow">\u25BE</span>
|
|
329
|
+
</button>
|
|
330
|
+
<div v-if="subagentDropdownOpen" class="subagent-dropdown-panel" @click.stop>
|
|
331
|
+
<input
|
|
332
|
+
class="subagent-search-input"
|
|
333
|
+
v-model="subagentSearchQuery"
|
|
334
|
+
placeholder="Search agents..."
|
|
335
|
+
ref="subagentSearchRef"
|
|
336
|
+
@keydown.escape="subagentDropdownOpen = false"
|
|
337
|
+
/>
|
|
338
|
+
<div class="subagent-dropdown-list">
|
|
339
|
+
<div
|
|
340
|
+
class="subagent-dropdown-item"
|
|
341
|
+
:class="{ active: !selectedSubagent }"
|
|
342
|
+
@click="selectSubagent(null)"
|
|
343
|
+
>
|
|
344
|
+
<div class="subagent-item-name">\u{1F916} All Agents</div>
|
|
345
|
+
</div>
|
|
346
|
+
<div
|
|
347
|
+
v-for="sa in filteredSubagentList"
|
|
348
|
+
:key="sa.toolCallId"
|
|
349
|
+
class="subagent-dropdown-item"
|
|
350
|
+
:class="{ active: selectedSubagent === sa.toolCallId }"
|
|
351
|
+
@click="selectSubagent(sa.toolCallId)"
|
|
352
|
+
>
|
|
353
|
+
<div class="subagent-item-color" :style="{ background: SUBAGENT_COLORS[sa.colorIndex % SUBAGENT_COLORS.length] }"></div>
|
|
354
|
+
<div class="subagent-item-body">
|
|
355
|
+
<div class="subagent-item-name">{{ sa.name }}</div>
|
|
356
|
+
<div v-if="sa.meta.taskName || sa.meta.agentType || sa.meta.model" class="subagent-item-meta">
|
|
357
|
+
<span v-if="sa.meta.taskName" class="subagent-meta-tag">{{ sa.meta.taskName }}</span>
|
|
358
|
+
<span v-if="sa.meta.agentType" class="subagent-meta-tag dim">{{ sa.meta.agentType }}</span>
|
|
359
|
+
<span v-if="sa.meta.model" class="subagent-meta-tag dim">{{ sa.meta.model }}</span>
|
|
360
|
+
</div>
|
|
361
|
+
<div v-if="sa.meta.agentDescription" class="subagent-item-desc">{{ sa.meta.agentDescription }}</div>
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
<div v-if="filteredSubagentList.length === 0" class="subagent-dropdown-empty">No matches</div>
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
<span v-if="subagentTokenUsage" class="subagent-usage-badge">
|
|
368
|
+
{{ subagentTokenUsage.eventCount }} events \xB7 {{ formatDuration(subagentTokenUsage.durationMs) }}
|
|
369
|
+
</span>
|
|
370
|
+
</div>
|
|
371
|
+
|
|
372
|
+
<div class="filter-bar-divider"></div>
|
|
373
|
+
|
|
374
|
+
<!-- Event type dropdown -->
|
|
375
|
+
<div class="filter-type-wrapper">
|
|
376
|
+
<button
|
|
377
|
+
class="filter-type-toggle"
|
|
378
|
+
:class="{ active: currentFilter !== 'all' }"
|
|
379
|
+
@click.stop="typeFilterOpen = !typeFilterOpen"
|
|
380
|
+
>
|
|
381
|
+
\u26A1 {{ currentFilter === 'all' ? 'All Types' : currentFilter }} \u25BE
|
|
382
|
+
</button>
|
|
383
|
+
<div v-if="typeFilterOpen" class="filter-type-menu">
|
|
384
|
+
<div class="filter-type-menu-header">Event Types</div>
|
|
385
|
+
<div class="filter-type-menu-options">
|
|
386
|
+
<div
|
|
387
|
+
v-for="filter in filters"
|
|
388
|
+
:key="filter.type"
|
|
389
|
+
:class="['filter-type-menu-item', { active: currentFilter === filter.type }]"
|
|
390
|
+
@click="setFilter(filter.type); typeFilterOpen = false"
|
|
391
|
+
>
|
|
392
|
+
<span class="filter-type-menu-label">{{ filter.type === 'all' ? 'All' : filter.type }}</span>
|
|
393
|
+
<span class="filter-type-menu-count">{{ filter.count }}</span>
|
|
394
|
+
</div>
|
|
395
|
+
</div>
|
|
396
|
+
</div>
|
|
397
|
+
</div>
|
|
246
398
|
</div>
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
<div class="
|
|
250
|
-
<
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
{{ searchResultCount }}
|
|
399
|
+
|
|
400
|
+
<!-- Active filter chips -->
|
|
401
|
+
<div v-if="activeFilterCount > 0" class="active-filters-bar">
|
|
402
|
+
<span v-if="currentFilter !== 'all'" class="filter-chip">
|
|
403
|
+
Type: {{ currentFilter }}
|
|
404
|
+
<button @click="setFilter('all')" class="filter-chip-remove" title="Remove filter">\xD7</button>
|
|
405
|
+
</span>
|
|
406
|
+
<span v-if="selectedSubagent" class="filter-chip">
|
|
407
|
+
Agent: {{ subagentList.find(s => s.toolCallId === selectedSubagent)?.name || selectedSubagent }}
|
|
408
|
+
<button @click="selectSubagent(null)" class="filter-chip-remove" title="Remove filter">\xD7</button>
|
|
258
409
|
</span>
|
|
410
|
+
<span v-if="searchText.trim()" class="filter-chip">
|
|
411
|
+
Search: "{{ searchText.length > 20 ? searchText.substring(0, 20) + '\u2026' : searchText }}"
|
|
412
|
+
<button @click="searchText = ''" class="filter-chip-remove" title="Remove filter">\xD7</button>
|
|
413
|
+
</span>
|
|
414
|
+
<button class="clear-all-filters-btn" @click="clearAllFilters">Clear all</button>
|
|
259
415
|
</div>
|
|
260
416
|
</div>
|
|
261
417
|
|
|
@@ -323,6 +479,7 @@
|
|
|
323
479
|
<div class="subagent-divider-line-left" :style="{ background: getSubagentColor(item) || '#58a6ff' }"></div>
|
|
324
480
|
<span class="subagent-divider-text" :style="{ color: getSubagentColor(item) || '#58a6ff', borderColor: getSubagentColor(item) || '#58a6ff', background: (getSubagentColor(item) || '#58a6ff') + '1a' }">
|
|
325
481
|
\u{1F916} {{ item.data?.agentDisplayName || item.data?.agentName || 'SubAgent' }}
|
|
482
|
+
<span v-if="subagentOwnership.subagentInfo.get(item.data?.toolCallId)?.meta?.model" class="subagent-divider-model">\xB7 {{ subagentOwnership.subagentInfo.get(item.data?.toolCallId).meta.model }}</span>
|
|
326
483
|
{{ item.type === 'subagent.started' ? 'Start \u25B6' : item.type === 'subagent.completed' ? 'Complete \u2713' : 'Failed \u2717' }}
|
|
327
484
|
</span>
|
|
328
485
|
<div class="subagent-divider-line-right" :style="{ background: getSubagentColor(item) || '#58a6ff' }"></div>
|
|
@@ -344,9 +501,11 @@
|
|
|
344
501
|
<span
|
|
345
502
|
v-if="getSubagentInfo(item)"
|
|
346
503
|
class="subagent-owner-tag"
|
|
347
|
-
:style="{ color: getSubagentColor(item),
|
|
348
|
-
|
|
349
|
-
|
|
504
|
+
:style="{ '--subagent-color': getSubagentColor(item) || '#58a6ff', '--subagent-hover-bg': ((getSubagentColor(item) || '#58a6ff') + '26') }"
|
|
505
|
+
:title="'Filter to ' + getSubagentInfo(item).name"
|
|
506
|
+
@click.stop="selectSubagent(getSubagentInfo(item).toolCallId)"
|
|
507
|
+
>\u{1F916} {{ getSubagentInfo(item).name }}</span>
|
|
508
|
+
<span class="event-timestamp">{{ formatTime(item.timestamp) }}</span>
|
|
350
509
|
</div>
|
|
351
510
|
|
|
352
511
|
<!-- Abort event: show reason -->
|
|
@@ -434,6 +593,39 @@
|
|
|
434
593
|
</div>
|
|
435
594
|
</div>
|
|
436
595
|
|
|
596
|
+
<!-- Hook event: compact summary with collapsible args/result -->
|
|
597
|
+
<div v-else-if="item.data?.hookType" class="hook-content">
|
|
598
|
+
<div class="hook-summary">
|
|
599
|
+
<span style="color: #8b949e;">{{ item.data.hookType }}</span>
|
|
600
|
+
<span v-if="item.data.hookToolName" style="color: #8b949e;"> \u2192 </span>
|
|
601
|
+
<span v-if="item.data.hookToolName" style="color: #c9d1d9;">{{ item.data.hookToolName }}</span>
|
|
602
|
+
<span v-if="item.data.hookDurationMs != null" style="color: #7d8590; margin-left: 8px;">{{ item.data.hookDurationMs }}ms</span>
|
|
603
|
+
<span v-if="item.data.hookSuccess === true" style="color: #3fb950; margin-left: 4px;">\u2713</span>
|
|
604
|
+
<span v-if="item.data.hookSuccess === false" style="color: #ff7b72; margin-left: 4px;">\u2717</span>
|
|
605
|
+
</div>
|
|
606
|
+
<div v-if="item.data.hookArgs && Object.keys(item.data.hookArgs).length > 0" class="hook-section">
|
|
607
|
+
<div class="hook-section-header" @click="toggleContent('hook-args-' + item.stableId)">
|
|
608
|
+
<span class="tool-expand-icon">{{ expandedContent['hook-args-' + item.stableId] ? '\u25BC' : '\u25B6' }}</span>
|
|
609
|
+
<span style="color: #8b949e;">Arguments</span>
|
|
610
|
+
</div>
|
|
611
|
+
<div v-if="expandedContent['hook-args-' + item.stableId]" class="hook-section-body">
|
|
612
|
+
<pre>{{ JSON.stringify(item.data.hookArgs, null, 2) }}</pre>
|
|
613
|
+
</div>
|
|
614
|
+
</div>
|
|
615
|
+
<div v-if="item.data.hookResult" class="hook-section">
|
|
616
|
+
<div class="hook-section-header" @click="toggleContent('hook-result-' + item.stableId)">
|
|
617
|
+
<span class="tool-expand-icon">{{ expandedContent['hook-result-' + item.stableId] ? '\u25BC' : '\u25B6' }}</span>
|
|
618
|
+
<span style="color: #8b949e;">Result</span>
|
|
619
|
+
</div>
|
|
620
|
+
<div v-if="expandedContent['hook-result-' + item.stableId]" class="hook-section-body">
|
|
621
|
+
<pre>{{ item.data.hookResult }}</pre>
|
|
622
|
+
</div>
|
|
623
|
+
</div>
|
|
624
|
+
<div v-if="item.data.hookError" style="color: #ff7b72; margin-top: 4px;">
|
|
625
|
+
Error: {{ item.data.hookError }}
|
|
626
|
+
</div>
|
|
627
|
+
</div>
|
|
628
|
+
|
|
437
629
|
<!-- Regular content (unified format from server) -->
|
|
438
630
|
<div v-else-if="item.data?.message || item.data?.text || item.data?.content || item.data?.transformedContent">
|
|
439
631
|
<div
|
|
@@ -556,4 +748,4 @@
|
|
|
556
748
|
</div>
|
|
557
749
|
|
|
558
750
|
</div>
|
|
559
|
-
`});console.log("Mounting Vue app to #app..."),console.log("App config:",
|
|
751
|
+
`});console.log("Mounting Vue app to #app..."),console.log("App config:",ve.config),console.log("Target element:",document.getElementById("app"));try{let h=ve.mount("#app");console.log("Vue app mounted successfully!",h?"Instance created":"No instance"),console.log("VM type:",typeof h,"Has exportSession:",typeof h?.exportSession),console.log("VM keys:",h?Object.keys(h).slice(0,10):"NO_VM"),console.log("#app innerHTML length:",document.getElementById("app").innerHTML.length),console.log("#app first 100 chars:",document.getElementById("app").innerHTML.substring(0,100))}catch(h){console.error("Mount failed:",h),console.error("Error stack:",h.stack)}})();})();
|