@qiaolei81/copilot-session-viewer 0.3.5 → 0.3.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qiaolei81/copilot-session-viewer",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "Web UI for viewing GitHub Copilot CLI session logs",
5
5
  "author": "Lei Qiao <qiaolei81@gmail.com>",
6
6
  "license": "MIT",
@@ -33,14 +33,14 @@
33
33
  "scripts": {
34
34
  "build": "node scripts/build.mjs",
35
35
  "build:watch": "node scripts/build.mjs --watch",
36
- "start": "node server.js",
37
- "dev": "nodemon server.js",
36
+ "start": "cross-env DISABLE_TELEMETRY=true node server.js",
37
+ "dev": "cross-env DISABLE_TELEMETRY=true nodemon server.js",
38
38
  "test": "jest",
39
39
  "test:watch": "jest --watch",
40
40
  "test:coverage": "jest --coverage",
41
- "test:e2e": "playwright test",
42
- "test:e2e:headed": "playwright test --headed",
43
- "test:e2e:debug": "playwright test --debug",
41
+ "test:e2e": "cross-env DISABLE_TELEMETRY=true playwright test",
42
+ "test:e2e:headed": "cross-env DISABLE_TELEMETRY=true playwright test --headed",
43
+ "test:e2e:debug": "cross-env DISABLE_TELEMETRY=true playwright test --debug",
44
44
  "test:all": "npm test && npm run test:e2e",
45
45
  "test:coverage:all": "npm run test:coverage && npm run test:e2e",
46
46
  "lint": "eslint .",
@@ -54,6 +54,7 @@
54
54
  "prepublishOnly": "npm run build:prod"
55
55
  },
56
56
  "dependencies": {
57
+ "adm-zip": "^0.5.16",
57
58
  "applicationinsights": "^2.9.8",
58
59
  "compression": "^1.8.1",
59
60
  "dompurify": "^3.3.1",
@@ -61,9 +62,9 @@
61
62
  "express": "^4.18.2",
62
63
  "express-rate-limit": "^8.2.1",
63
64
  "helmet": "^8.1.0",
65
+ "js-yaml": "^4.1.1",
64
66
  "multer": "^2.0.2",
65
- "zod": "^4.3.6",
66
- "adm-zip": "^0.5.16"
67
+ "zod": "^4.3.6"
67
68
  },
68
69
  "devDependencies": {
69
70
  "@eslint/css": "^0.14.1",
@@ -73,6 +74,7 @@
73
74
  "@istanbuljs/nyc-config-typescript": "^1.0.2",
74
75
  "@playwright/test": "^1.58.2",
75
76
  "@types/jest": "^30.0.0",
77
+ "cross-env": "^10.1.0",
76
78
  "esbuild": "^0.27.3",
77
79
  "eslint": "^10.0.0",
78
80
  "eslint-plugin-vue": "^10.8.0",
@@ -1,21 +1,21 @@
1
- (()=>{var v=window.__PAGE_DATA||{},L=v.sessions||[],V=v.totalSessions||0,x=v.hasMore||!1,E=v.sourceHints||{},m=[...L],u={};u.copilot={offset:L.length,hasMore:x};var p=!1,i="copilot";function h(){return u[i]||(u[i]={offset:0,hasMore:!0}),u[i]}async function A(){let e=h();if(p||!e.hasMore)return;p=!0;let t=document.getElementById("loading-indicator");t.style.display="block",window.trackClick("LoadMoreClicked",{currentPage:Math.floor(e.offset/20)+1,offset:e.offset,source:i});try{let s=await fetch(`/api/sessions/load-more?offset=${h().offset}&limit=20&source=${encodeURIComponent(i)}`);if(!s.ok)throw new Error("Failed to load more sessions");let o=await s.json(),n=new Set(m.map(r=>r.id)),a=[];for(let r of o.sessions)n.has(r.id)||(m.push(r),a.push(r));h().offset+=o.sessions.length,h().hasMore=o.hasMore,await $(a),S()}catch(s){console.error("Error loading more sessions:",s)}finally{p=!1,t.style.display="none"}}function D(){return m.filter(e=>e.source===i)}function S(){let e=document.getElementById("sessions-container");e.innerHTML="";let t=D();if(t.length===0){e.innerHTML='<div style="text-align: center; color: #6e7681; padding: 40px; font-size: 14px;">No sessions found for this filter.</div>';return}let s=z(t);Object.keys(s).sort((n,a)=>a.localeCompare(n)).forEach(n=>{let a=document.createElement("div");a.className="date-group-header",a.textContent=Z(s[n][0].createdAt),e.appendChild(a);let r=document.createElement("div");r.className="recent-list",s[n].forEach(l=>{r.innerHTML+=j(l)}),e.appendChild(r)})}function T(){let e=window.pageYOffset||document.documentElement.scrollTop,t=window.innerHeight,s=document.documentElement.scrollHeight;e+t>=s-500&&h().hasMore&&!p&&A()}var y;function B(){y||(y=setTimeout(()=>{T(),y=null},100))}function F(e){e.preventDefault();let t=document.getElementById("sessionInput").value.trim();t&&(window.location.href=`/session/${t}`)}document.getElementById("sessionForm").addEventListener("submit",F);var w=document.getElementById("fileInput"),d=document.getElementById("importLink"),I=document.getElementById("importStatus");d.addEventListener("click",e=>{e.preventDefault(),w.click()});w.addEventListener("change",async e=>{let t=e.target.files[0];if(t){if(!t.name.endsWith(".zip")){g("error","\u274C Please select a .zip file");return}d.style.pointerEvents="none",d.style.opacity="0.5",d.textContent="Importing...",g("loading","Uploading and extracting session...");try{let s=new FormData;s.append("sessionZip",t);let o=await fetch("/session/import",{method:"POST",body:s}),n=await o.json();o.ok?(g("success",`\u2705 Session ${n.sessionId} imported successfully!`),setTimeout(()=>{window.location.reload()},1500)):(g("error",`\u274C Import failed: ${n.error}`),d.style.pointerEvents="auto",d.style.opacity="1",d.textContent="Import session from zip")}catch(s){g("error",`\u274C Import failed: ${s.message}`),d.style.pointerEvents="auto",d.style.opacity="1",d.textContent="Import session from zip"}finally{w.value=""}}});function g(e,t){I.className=`import-status ${e}`,I.textContent=t}function P(e){if(!e||e<0)return"\u2014";let t=Math.floor(e/1e3),s=Math.floor(t/60),o=Math.floor(s/60);if(o>0){let n=s%60;return`${o}h ${n}m`}else if(s>0){let n=t%60;return`${s}m ${n}s`}else return`${t}s`}function Z(e){let t=new Date(e),s=t.getFullYear(),o=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0");return`${s}/${o}/${n}`}function N(e){if(!e)return"Unknown";let t=new Date(e),s=t.getFullYear(),o=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0");return`${s}-${o}-${n}`}function z(e){let t={};return e.forEach(s=>{let o=N(s.createdAt);t[o]||(t[o]=[]),t[o].push(s)}),t}function j(e){let t="",s=e.sourceBadgeClass||"source-copilot",o=e.sourceName||"Copilot";if(t+=`<span class="status-badge ${s}" title="${o}">${o}</span>`,e.sessionStatus==="wip"&&(t+='<span class="status-badge wip" title="Session in progress">\u{1F504} WIP</span>'),e.isImported&&(t+='<span class="status-badge imported" title="Imported session">\u{1F4E5}</span>'),e.hasInsight&&(t+='<span class="status-badge insight" title="Has Agent Review">\u{1F4A1}</span>'),e.selectedModel){let C=e.selectedModel.replace("claude-","").replace("gpt-","").replace("gemini-",""),f="model-other";e.selectedModel.includes("claude")?f="model-claude":e.selectedModel.includes("gpt")?f="model-gpt":e.selectedModel.includes("gemini")&&(f="model-gemini"),t+=`<span class="status-badge model ${f}" title="Model: ${c(e.selectedModel)}">${c(C)}</span>`}e.copilotVersion&&(t+=`<span class="status-badge version" title="CLI version">${c(e.copilotVersion)}</span>`);let n="";e.summary&&e.summary!=="No summary"&&e.summary!=="Legacy session"?n=`<div class="session-summary">${c(e.summary)}</div>`:n='<div class="session-summary" style="color: #6e7681; font-style: italic;">No summary available</div>';let a="";e.workspace&&e.workspace.cwd&&(a=`
1
+ (()=>{var y=window.__PAGE_DATA||{},A=y.sessions||[],G=y.totalSessions||0,D=y.hasMore||!1,k=y.sourceHints||{},m=[...A],u={};u.copilot={offset:A.length,hasMore:D};var f=!1,S="sessionViewer.sourceFilter",$;try{$=localStorage.getItem(S)}catch{$=null}var i=$||"copilot";function g(){return u[i]||(u[i]={offset:0,hasMore:!0}),u[i]}async function B(){let e=g();if(f||!e.hasMore)return;f=!0;let t=document.getElementById("loading-indicator");t.style.display="block",window.trackClick("LoadMoreClicked",{currentPage:Math.floor(e.offset/20)+1,offset:e.offset,source:i});try{let s=await fetch(`/api/sessions/load-more?offset=${g().offset}&limit=20&source=${encodeURIComponent(i)}`);if(!s.ok)throw new Error("Failed to load more sessions");let o=await s.json(),n=new Set(m.map(r=>r.id)),a=[];for(let r of o.sessions)n.has(r.id)||(m.push(r),a.push(r));g().offset+=o.sessions.length,g().hasMore=o.hasMore,await C(a),E()}catch(s){console.error("Error loading more sessions:",s)}finally{f=!1,t.style.display="none"}}function F(){return m.filter(e=>e.source===i)}function E(){let e=document.getElementById("sessions-container");e.innerHTML="";let t=F();if(t.length===0){e.innerHTML='<div style="text-align: center; color: #6e7681; padding: 40px; font-size: 14px;">No sessions found for this filter.</div>';return}let s=O(t);Object.keys(s).sort((n,a)=>a.localeCompare(n)).forEach(n=>{let a=document.createElement("div");a.className="date-group-header",a.textContent=z(s[n][0].createdAt),e.appendChild(a);let r=document.createElement("div");r.className="recent-list",s[n].forEach(h=>{r.innerHTML+=R(h)}),e.appendChild(r)})}function P(){let e=window.pageYOffset||document.documentElement.scrollTop,t=window.innerHeight,s=document.documentElement.scrollHeight;e+t>=s-500&&g().hasMore&&!f&&B()}var w;function Z(){w||(w=setTimeout(()=>{P(),w=null},100))}function _(e){e.preventDefault();let t=document.getElementById("sessionInput").value.trim();t&&(window.location.href=`/session/${t}`)}document.getElementById("sessionForm").addEventListener("submit",_);var M=document.getElementById("fileInput"),d=document.getElementById("importLink"),L=document.getElementById("importStatus");d.addEventListener("click",e=>{e.preventDefault(),M.click()});M.addEventListener("change",async e=>{let t=e.target.files[0];if(t){if(!t.name.endsWith(".zip")){p("error","\u274C Please select a .zip file");return}d.style.pointerEvents="none",d.style.opacity="0.5",d.textContent="Importing...",p("loading","Uploading and extracting session...");try{let s=new FormData;s.append("sessionZip",t);let o=await fetch("/session/import",{method:"POST",body:s}),n=await o.json();o.ok?(p("success",`\u2705 Session ${n.sessionId} imported successfully!`),setTimeout(()=>{window.location.reload()},1500)):(p("error",`\u274C Import failed: ${n.error}`),d.style.pointerEvents="auto",d.style.opacity="1",d.textContent="Import session from zip")}catch(s){p("error",`\u274C Import failed: ${s.message}`),d.style.pointerEvents="auto",d.style.opacity="1",d.textContent="Import session from zip"}finally{M.value=""}}});function p(e,t){L.className=`import-status ${e}`,L.textContent=t}function N(e){if(!e||e<0)return"\u2014";let t=Math.floor(e/1e3),s=Math.floor(t/60),o=Math.floor(s/60);if(o>0){let n=s%60;return`${o}h ${n}m`}else if(s>0){let n=t%60;return`${s}m ${n}s`}else return`${t}s`}function z(e){let t=new Date(e),s=t.getFullYear(),o=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0");return`${s}/${o}/${n}`}function j(e){if(!e)return"Unknown";let t=new Date(e),s=t.getFullYear(),o=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0");return`${s}-${o}-${n}`}function O(e){let t={};return e.forEach(s=>{let o=j(s.createdAt);t[o]||(t[o]=[]),t[o].push(s)}),t}function R(e){let t="",s=e.sourceBadgeClass||"source-copilot",o=e.sourceName||"Copilot";if(t+=`<span class="status-badge ${s}" title="${o}">${o}</span>`,e.sessionStatus==="wip"&&(t+='<span class="status-badge wip" title="Session in progress">\u{1F504} WIP</span>'),e.isImported&&(t+='<span class="status-badge imported" title="Imported session">\u{1F4E5}</span>'),e.hasInsight&&(t+='<span class="status-badge insight" title="Has Agent Review">\u{1F4A1}</span>'),e.selectedModel){let v=e.selectedModel.replace("claude-","").replace("gpt-","").replace("gemini-",""),l="model-other";e.selectedModel.includes("claude")?l="model-claude":e.selectedModel.includes("gpt")?l="model-gpt":e.selectedModel.includes("gemini")&&(l="model-gemini"),t+=`<span class="status-badge model ${l}" title="Model: ${c(e.selectedModel)}">${c(v)}</span>`}e.copilotVersion&&(t+=`<span class="status-badge version" title="CLI version">${c(e.copilotVersion)}</span>`);let n="";if(e.summary&&e.summary!=="No summary"&&e.summary!=="Legacy session"){let v=e.summary.replace(/"/g,"&quot;"),l=c(e.summary).replace(/\n+/g," ");n=`<div class="session-summary" title="${v}">${l}</div>`}else n='<div class="session-summary" style="color: #6e7681; font-style: italic;">No summary available</div>';let a="";e.workspace&&e.workspace.cwd&&(a=`
2
2
  <div class="session-info-item workspace" title="${c(e.workspace.cwd)}">
3
3
  <svg viewBox="0 0 16 16" fill="currentColor"><path d="M1.75 1A1.75 1.75 0 000 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0016 13.25v-8.5A1.75 1.75 0 0014.25 3H7.5a.25.25 0 01-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75z"></path></svg>
4
- <span class="session-info-value">${c(e.workspace.cwd.replace(/^\/Users\/[^/]+/,"~"))}</span>
4
+ <span class="session-info-value">${c(e.workspace.cwd)}</span>
5
5
  </div>
6
- `);let r=e.createdAt?new Date(e.createdAt).toLocaleString("en-US",{month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hour12:!1}):"unknown",l="";e.duration&&(l=`
6
+ `);let r=e.createdAt?new Date(e.createdAt).toLocaleString("en-US",{month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hour12:!1}):"unknown",h="";e.duration&&(h=`
7
7
  <div class="session-info-item">
8
8
  <svg viewBox="0 0 16 16" fill="currentColor"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM7.25 12.5v-5A.75.75 0 0 1 8 6.75h2.5a.75.75 0 0 1 0 1.5H8.75v4.25a.75.75 0 0 1-1.5 0Z"></path></svg>
9
- <span class="session-info-value">${P(e.duration)}</span>
9
+ <span class="session-info-value">${N(e.duration)}</span>
10
10
  </div>
11
- `);let b=e.sessionStatus==="wip"?" recent-item-wip":"",M="";return e.tags&&e.tags.length>0&&(M=`<div class="session-tags">${e.tags.map(f=>`<span class="session-tag" style="background-color: ${U(f)}" title="${c(f)}">${c(f)}</span>`).join("")}</div>`),`
12
- <a href="/session/${e.id}" class="recent-item${b}" onclick="trackClick('SessionCardClicked', { sessionId: '${c(e.id)}', source: '${c(e.source||"unknown")}' })">
11
+ `);let T=e.sessionStatus==="wip"?" recent-item-wip":"",I="";return e.tags&&e.tags.length>0&&(I=`<div class="session-tags">${e.tags.map(l=>`<span class="session-tag" style="background-color: ${U(l)}" title="${c(l)}">${c(l)}</span>`).join("")}</div>`),`
12
+ <a href="/session/${e.id}" class="recent-item${T}" onclick="trackClick('SessionCardClicked', { sessionId: '${c(e.id)}', source: '${c(e.source||"unknown")}' })">
13
13
  <div class="session-id">
14
14
  <span class="session-id-text" title="${c(e.id)}">${c(e.id)}</span>
15
15
  </div>
16
16
  <div class="session-badges-tags">
17
17
  <div class="session-badges">${t}</div>
18
- ${M}
18
+ ${I}
19
19
  </div>
20
20
  ${n}
21
21
  <div class="session-divider"></div>
@@ -25,11 +25,11 @@
25
25
  <svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm7-3.25v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5a.75.75 0 0 1 1.5 0Z"></path></svg>
26
26
  <span class="session-info-value">${r}</span>
27
27
  </div>
28
- ${l}
28
+ ${h}
29
29
  <div class="session-info-item">
30
30
  <svg viewBox="0 0 16 16" fill="currentColor"><path d="M7.72.72a.75.75 0 0 1 1.06 0l1.5 1.5a.75.75 0 0 1-1.06 1.06l-.22-.22v1.69a.75.75 0 0 1-1.5 0V3.06l-.22.22a.75.75 0 0 1-1.06-1.06ZM2 7a.75.75 0 0 0 0 1.5h3.69l-.22.22a.75.75 0 1 0 1.06 1.06l1.5-1.5a.75.75 0 0 0 0-1.06l-1.5-1.5a.75.75 0 0 0-1.06 1.06l.22.22Zm8.53-.28a.75.75 0 0 0 0 1.06l1.5 1.5a.75.75 0 1 0 1.06-1.06l-.22-.22H16a.75.75 0 0 0 0-1.5h-3.13l.22-.22a.75.75 0 0 0-1.06-1.06ZM7.72 12.22a.75.75 0 0 1 1.06 0l1.5 1.5a.75.75 0 1 1-1.06 1.06l-.22-.22v1.69a.75.75 0 0 1-1.5 0v-1.69l-.22.22a.75.75 0 0 1-1.06-1.06Z"></path></svg>
31
31
  <span class="session-info-value">${e.eventCount||0} events</span>
32
32
  </div>
33
33
  </div>
34
34
  </a>
35
- `}function c(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}var k=["#3b82f6","#10b981","#f59e0b","#ef4444","#8b5cf6","#ec4899","#06b6d4","#f97316"];function U(e){let t=0;for(let s=0;s<e.length;s++)t=e.charCodeAt(s)+((t<<5)-t);return k[Math.abs(t)%k.length]}async function O(e){try{let t=e.map(n=>fetch(`/api/sessions/${n}/tags`).then(a=>a.ok?a.json():{tags:[]}).then(a=>({id:n,tags:a.tags||[]})).catch(()=>({id:n,tags:[]}))),s=await Promise.all(t),o={};return s.forEach(({id:n,tags:a})=>{o[n]=a}),o}catch(t){return console.error("Error loading session tags:",t),{}}}async function $(e){let t=e.map(o=>o.id),s=await O(t);e.forEach(o=>{o.tags=s[o.id]||[]})}function H(e){let t=document.getElementById("sourceHint");t&&E[e]?t.innerHTML='Sessions from <span class="hint-code">'+E[e]+"</span>":t&&(t.textContent="")}function _(){let e=document.querySelectorAll(".filter-pill");H(i),e.forEach(t=>{t.addEventListener("click",async()=>{if(e.forEach(s=>s.classList.remove("active")),t.classList.add("active"),i=t.getAttribute("data-source"),H(i),window.trackClick("FilterPillClicked",{pillName:t.textContent.trim(),dataSource:i}),u[i]||(u[i]={offset:0,hasMore:!0}),u[i].offset===0&&!p){p=!0;let s=document.getElementById("sessions-container");s.innerHTML='<div style="text-align: center; color: #6e7681; padding: 40px; font-size: 14px;">\u23F3 Loading...</div>',document.getElementById("loading-indicator").style.display="none";try{let o=await fetch(`/api/sessions/load-more?offset=0&limit=20&source=${encodeURIComponent(i)}`);if(o.ok){let n=await o.json(),a=new Set(m.map(l=>l.id)),r=[];for(let l of n.sessions||[])a.has(l.id)||(m.push(l),r.push(l));u[i].offset=(n.sessions||[]).length,u[i].hasMore=n.hasMore,await $(r)}}catch(o){console.error("Failed to load sessions for source:",i,o)}finally{p=!1}}S()})})}document.addEventListener("DOMContentLoaded",async function(){await $(m),S(),window.addEventListener("scroll",B),_()});})();
35
+ `}function c(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}var H=["#3b82f6","#10b981","#f59e0b","#ef4444","#8b5cf6","#ec4899","#06b6d4","#f97316"];function U(e){let t=0;for(let s=0;s<e.length;s++)t=e.charCodeAt(s)+((t<<5)-t);return H[Math.abs(t)%H.length]}async function V(e){try{let t=e.map(n=>fetch(`/api/sessions/${n}/tags`).then(a=>a.ok?a.json():{tags:[]}).then(a=>({id:n,tags:a.tags||[]})).catch(()=>({id:n,tags:[]}))),s=await Promise.all(t),o={};return s.forEach(({id:n,tags:a})=>{o[n]=a}),o}catch(t){return console.error("Error loading session tags:",t),{}}}async function C(e){let t=e.map(o=>o.id),s=await V(t);e.forEach(o=>{o.tags=s[o.id]||[]})}function b(e){let t=document.getElementById("sourceHint");t&&k[e]?t.innerHTML='Sessions from <span class="hint-code">'+k[e]+"</span>":t&&(t.textContent="")}async function x(e){if(u[e]||(u[e]={offset:0,hasMore:!0}),u[e].offset===0&&!f){f=!0;let t=document.getElementById("sessions-container");t.innerHTML='<div style="text-align: center; color: #6e7681; padding: 40px; font-size: 14px;">\u23F3 Loading...</div>',document.getElementById("loading-indicator").style.display="none";try{let s=await fetch(`/api/sessions/load-more?offset=0&limit=20&source=${encodeURIComponent(e)}`);if(s.ok){let o=await s.json(),n=new Set(m.map(r=>r.id)),a=[];for(let r of o.sessions||[])n.has(r.id)||(m.push(r),a.push(r));u[e].offset=(o.sessions||[]).length,u[e].hasMore=o.hasMore,await C(a)}}catch(s){console.error("Failed to load sessions for source:",e,s)}finally{f=!1}}E()}function Y(){let e=document.querySelectorAll(".filter-pill");if(!new Set([...e].map(s=>s.getAttribute("data-source"))).has(i)){i="copilot";try{localStorage.setItem(S,i)}catch{}}e.forEach(s=>{s.classList.toggle("active",s.getAttribute("data-source")===i)}),b(i),e.forEach(s=>{s.addEventListener("click",async()=>{e.forEach(o=>o.classList.remove("active")),s.classList.add("active"),i=s.getAttribute("data-source");try{localStorage.setItem(S,i)}catch{}b(i),window.trackClick("FilterPillClicked",{pillName:s.textContent.trim(),dataSource:i}),await x(i)})})}document.addEventListener("DOMContentLoaded",async function(){await C(m),window.addEventListener("scroll",Z),Y(),i==="copilot"?E():await x(i)});})();
@@ -1,12 +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:ue,ref:u,computed:T,onMounted:F,onBeforeUnmount:me,watch:$}=Vue,{DynamicScroller:ge,DynamicScrollerItem:pe}=window.VueVirtualScroller,G=ue({components:{DynamicScroller:ge,DynamicScrollerItem:pe},setup(){let m=u(window.__PAGE_DATA.sessionId),W=u(window.__PAGE_DATA.metadata),z=u(!1),K=()=>window.innerWidth<=640,q=u(K()?!0:localStorage.getItem("sidebarCollapsed")==="true");$(q,e=>{K()||localStorage.setItem("sidebarCollapsed",e.toString())});let x=u({}),w=u({}),U=50,J=()=>{let e=Object.keys(x.value);e.length>U&&e.slice(0,e.length-U).forEach(s=>delete x.value[s]);let t=Object.keys(w.value);t.length>U&&t.slice(0,t.length-U).forEach(s=>delete w.value[s])},S=u("all"),V=u(""),E=u(""),X=u(0),g=u(null),Y=u({start:0,end:0}),L=null;$(V,e=>{clearTimeout(L),L=setTimeout(()=>{E.value=e,e.trim()&&window.trackClick&&window.trackClick("SearchUsed",{query:e.substring(0,50),resultCount:M.value.length,sessionId:m.value})},300)}),$(S,()=>{J()}),$(E,()=>{J()});let I=u([]),Z=u(!0),Q=u(null),v=T(()=>I.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,n=a.timestamp?new Date(a.timestamp).getTime():0;return s!==n?s-n:(t._fileIndex??0)-(a._fileIndex??0)}).map((t,a)=>({...t,virtualIndex:a,stableId:t.id||`${t.timestamp}-${t.type}-${a}`}))),ve=e=>{if(!E.value.trim())return!0;let t=E.value.toLowerCase();return[e.data?.message,e.data?.text,e.data?.content,e.data?.reason,e.data?.errorType,e.data?.previousModel,e.data?.newModel].filter(Boolean).join(" ").toLowerCase().includes(t)},M=T(()=>{let e=a=>{let s=a.type||"";return s!=="tool.execution_start"&&s!=="tool.execution_complete"},t=v.value.filter(e);return E.value.trim()&&(t=t.filter(ve)),t}),A=T(()=>{let e=M.value;S.value!=="all"&&(e=e.filter(s=>s.type===S.value));let t=["assistant.turn_start","subagent.started","subagent.completed","subagent.failed"],a=e.length;return e.map((s,n)=>{let r=e[n+1],o=n===a-1,i=r&&t.includes(r.type);return{...s,filteredIndex:n,filteredTotal:a,isLastEvent:o||i}})}),fe=T(()=>{let e={};return M.value.forEach(t=>{t.type&&(e[t.type]=(e[t.type]||0)+1)}),e}),be=T(()=>{if(!E.value.trim())return null;let e=M.value.length;return e>0?`${e} result${e!==1?"s":""}`:"No matches"}),he=T(()=>{let e=Object.keys(x.value).filter(a=>x.value[a]).length,t=Object.keys(w.value).filter(a=>w.value[a]).length;return e+t}),ee=T(()=>{let e=M.value.length,t=[{type:"all",label:`All (${e})`,count:e}],a={};M.value.forEach(n=>{n.type&&(a[n.type]=(a[n.type]||0)+1)});let s=Object.entries(a).sort((n,r)=>r[1]-n[1]).map(([n,r])=>({type:n,label:`${n} (${r})`,count:r,disabled:!1}));return[...t,...s]}),R=T(()=>{let e=v.value.filter(a=>a.type==="assistant.turn_start"),t=v.value.filter(a=>a.type==="user.message");return e.map((a,s)=>{let n=s,r=new Date(a.timestamp).getTime(),o,i=e.indexOf(a)+1;i<e.length?o=new Date(e[i].timestamp).getTime():o=Date.now();let c=o-r,l=Math.floor(c/1e3),d=Math.floor(l/60),y=l%60,P=d>0?`${d}m ${y}s`:`${y}s`,p=v.value.slice(0,v.value.indexOf(a)).reverse().find(_=>_.type==="user.message"),N=p?t.indexOf(p)+1:0;return{id:n,index:a.virtualIndex,originalTurnId:a.data?.turnId,timestamp:a.timestamp,duration:P,message:p?.data?.content||p?.data?.transformedContent||"",userReqNumber:N}})}),Te=T(()=>{let e=[],t=new Map;return R.value.forEach(a=>{let s=a.userReqNumber||0;if(!t.has(s)){let n={reqNumber:s,message:a.message,turns:[]};t.set(s,n),e.push(n)}t.get(s).turns.push(a)}),e}),ye=(e,t)=>e?e.length<=t?e:e.substring(0,t)+"\u2026":"",xe=T(()=>{let e=v.value,t=new Map,a=new Map,s=0;for(let o of e)if(o.type==="subagent.started"){let i=o.data?.toolCallId;i&&a.set(i,{name:o.data?.agentDisplayName||o.data?.agentName||"SubAgent",colorIndex:s++})}for(let o of e)if(o.type==="assistant.message"&&o.data?.subAgentName&&o.data?.subAgentId){let i=o.data.subAgentId;a.has(i)||a.set(i,{name:o.data.subAgentName,colorIndex:s++}),t.set(o.stableId,i)}if(a.size===0)return{ownerMap:t,subagentInfo:a};let n=new Map;for(let o of e)o.id&&n.set(o.id,o);for(let o of e)if(o.type==="assistant.message"){let i=o.data?.parentToolCallId;i&&a.has(i)&&t.set(o.stableId,i)}for(let o of e){if(o.type!=="reasoning")continue;let i=o.parentId,c=0;for(;i&&c<10;){let l=n.get(i);if(!l)break;if(l.type==="assistant.message"){let d=l.data?.parentToolCallId;d&&a.has(d)&&t.set(o.stableId,d);break}i=l.parentId,c++}}let r=new Map;for(let o of e){if(o.type!=="tool.execution_start")continue;let i=o.parentId,c=0;for(;i&&c<10;){let l=n.get(i);if(!l)break;if(l.type==="assistant.message"){let d=l.data?.parentToolCallId;if(d&&a.has(d)){t.set(o.stableId,d);let y=o.data?.toolCallId;y&&r.set(y,d)}break}i=l.parentId,c++}}for(let o of e){if(o.type!=="tool.execution_complete")continue;let i=o.data?.toolCallId;i&&r.has(i)&&t.set(o.stableId,r.get(i))}for(let o of e){if(o.type!=="tool.invocation")continue;let i=o.data?.parentToolCallId;i&&a.has(i)&&t.set(o.stableId,i)}return{ownerMap:t,subagentInfo:a}}),we=e=>{if(!e)return"";let t=new Date(e),a=String(t.getHours()).padStart(2,"0"),s=String(t.getMinutes()).padStart(2,"0"),n=String(t.getSeconds()).padStart(2,"0");return`${a}:${s}:${n}`},f=new Map,te=200,Ie=e=>{if(!e)return"";if(f.has(e))return f.get(e);try{let t=e.replace(/\\r\\n/g,`
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:fe,ref:u,computed:v,onMounted:X,onBeforeUnmount:be,watch:$}=Vue,{DynamicScroller:Te,DynamicScrollerItem:he}=window.VueVirtualScroller,Y=fe({components:{DynamicScroller:Te,DynamicScrollerItem:he},setup(){let m=u(window.__PAGE_DATA.sessionId),b=u(window.__PAGE_DATA.metadata),j=u(!1),Z=()=>window.innerWidth<=640,q=u(Z()?!0:localStorage.getItem("sidebarCollapsed")==="true");$(q,e=>{Z()||localStorage.setItem("sidebarCollapsed",e.toString())});let k=u({}),w=u({}),U=50,Q=()=>{let e=Object.keys(k.value);e.length>U&&e.slice(0,e.length-U).forEach(s=>delete k.value[s]);let t=Object.keys(w.value);t.length>U&&t.slice(0,t.length-U).forEach(s=>delete w.value[s])},E=u("all"),F=u(""),M=u(""),ee=u(0),g=u(null),te=u({start:0,end:0}),L=null;$(F,e=>{clearTimeout(L),L=setTimeout(()=>{M.value=e,e.trim()&&window.trackClick&&window.trackClick("SearchUsed",{query:e.substring(0,50),resultCount:N.value.length,sessionId:m.value})},300)}),$(E,()=>{Q()}),$(M,()=>{Q()});let C=u([]),ae=u(!0),se=u(null),T=v(()=>C.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,n=a.timestamp?new Date(a.timestamp).getTime():0;return s!==n?s-n:(t._fileIndex??0)-(a._fileIndex??0)}).map((t,a)=>({...t,virtualIndex:a,stableId:t.id||`${t.timestamp}-${t.type}-${a}`}))),ye=e=>{if(!M.value.trim())return!0;let t=M.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)},N=v(()=>{let e=a=>{let s=a.type||"";return s!=="tool.execution_start"&&s!=="tool.execution_complete"},t=T.value.filter(e);return M.value.trim()&&(t=t.filter(ye)),t}),A=v(()=>{let e=N.value;E.value!=="all"&&(e=e.filter(s=>s.type===E.value));let t=["assistant.turn_start","subagent.started","subagent.completed","subagent.failed"],a=e.length;return e.map((s,n)=>{let i=e[n+1],o=n===a-1,r=i&&t.includes(i.type);return{...s,filteredIndex:n,filteredTotal:a,isLastEvent:o||r}})}),xe=v(()=>{let e={};return N.value.forEach(t=>{t.type&&(e[t.type]=(e[t.type]||0)+1)}),e}),ke=v(()=>{if(!M.value.trim())return null;let e=N.value.length;return e>0?`${e} result${e!==1?"s":""}`:"No matches"}),we=v(()=>{let e=Object.keys(k.value).filter(a=>k.value[a]).length,t=Object.keys(w.value).filter(a=>w.value[a]).length;return e+t}),oe=v(()=>{let e=N.value.length,t=[{type:"all",label:`All (${e})`,count:e}],a={};N.value.forEach(n=>{n.type&&(a[n.type]=(a[n.type]||0)+1)});let s=Object.entries(a).sort((n,i)=>i[1]-n[1]).map(([n,i])=>({type:n,label:`${n} (${i})`,count:i,disabled:!1}));return[...t,...s]}),D=v(()=>{let e=T.value.filter(a=>a.type==="assistant.turn_start"),t=T.value.filter(a=>a.type==="user.message");return e.map((a,s)=>{let n=s,i=new Date(a.timestamp).getTime(),o,r=e.indexOf(a)+1;r<e.length?o=new Date(e[r].timestamp).getTime():o=Date.now();let c=o-i,l=Math.floor(c/1e3),d=Math.floor(l/60),p=l%60,J=d>0?`${d}m ${p}s`:`${p}s`,R=T.value.slice(0,T.value.indexOf(a)).reverse().find(f=>f.type==="user.message"),B=R?t.indexOf(R)+1:0;return{id:n,index:a.virtualIndex,originalTurnId:a.data?.turnId,timestamp:a.timestamp,duration:J,message:R?.data?.content||R?.data?.transformedContent||"",userReqNumber:B}})}),Ce=v(()=>{let e=[],t=new Map;return D.value.forEach(a=>{let s=a.userReqNumber||0;if(!t.has(s)){let n={reqNumber:s,message:a.message,turns:[]};t.set(s,n),e.push(n)}t.get(s).turns.push(a)}),e}),Se=(e,t)=>e?e.length<=t?e:e.substring(0,t)+"\u2026":"",Ie=v(()=>{let e=T.value,t=new Map,a=new Map,s=0;for(let o of e)if(o.type==="subagent.started"){let r=o.data?.toolCallId;r&&a.set(r,{name:o.data?.agentDisplayName||o.data?.agentName||"SubAgent",colorIndex:s++})}for(let o of e)if(o.type==="assistant.message"&&o.data?.subAgentName&&o.data?.subAgentId){let r=o.data.subAgentId;a.has(r)||a.set(r,{name:o.data.subAgentName,colorIndex:s++}),t.set(o.stableId,r)}if(a.size===0)return{ownerMap:t,subagentInfo:a};let n=new Map;for(let o of e)o.id&&n.set(o.id,o);for(let o of e)if(o.type==="assistant.message"){let r=o.data?.parentToolCallId;r&&a.has(r)&&t.set(o.stableId,r)}for(let o of e){if(o.type!=="reasoning")continue;let r=o.parentId,c=0;for(;r&&c<10;){let l=n.get(r);if(!l)break;if(l.type==="assistant.message"){let d=l.data?.parentToolCallId;d&&a.has(d)&&t.set(o.stableId,d);break}r=l.parentId,c++}}let i=new Map;for(let o of e){if(o.type!=="tool.execution_start")continue;let r=o.parentId,c=0;for(;r&&c<10;){let l=n.get(r);if(!l)break;if(l.type==="assistant.message"){let d=l.data?.parentToolCallId;if(d&&a.has(d)){t.set(o.stableId,d);let p=o.data?.toolCallId;p&&i.set(p,d)}break}r=l.parentId,c++}}for(let o of e){if(o.type!=="tool.execution_complete")continue;let r=o.data?.toolCallId;r&&i.has(r)&&t.set(o.stableId,i.get(r))}for(let o of e){if(o.type!=="tool.invocation")continue;let r=o.data?.parentToolCallId;r&&a.has(r)&&t.set(o.stableId,r)}return{ownerMap:t,subagentInfo:a}}),Ee=e=>{if(!e)return"";let t=new Date(e),a=String(t.getHours()).padStart(2,"0"),s=String(t.getMinutes()).padStart(2,"0"),n=String(t.getSeconds()).padStart(2,"0");return`${a}:${s}:${n}`},Me=e=>{if(!e)return"";let t=new Date(e),a=String(t.getHours()).padStart(2,"0"),s=String(t.getMinutes()).padStart(2,"0"),n=String(t.getSeconds()).padStart(2,"0"),i=String(t.getMilliseconds()).padStart(3,"0");return`${a}:${s}:${n}.${i}`},h=new Map,ne=200,Ne=e=>{if(!e)return"";if(h.has(e))return h.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 o=s[1],i=s[2],c=o.split(`
4
- `).filter(p=>p.trim()&&p.includes(":")).map(p=>{let N=p.indexOf(":"),_=p.substring(0,N).trim(),tt=p.substring(N+1).trim();return{key:_,value:tt}}),l='<table style="margin-bottom: 16px; border-collapse: collapse; width: 100%;"><tbody>';c.forEach(p=>{let N=DOMPurify.sanitize(p.key,{ALLOWED_TAGS:[]}),_=DOMPurify.sanitize(p.value,{ALLOWED_TAGS:[]});l+=`<tr><td style="padding: 4px 12px; border: 1px solid #30363d; font-weight: 600; color: #7d8590;">${N}</td><td style="padding: 4px 12px; border: 1px solid #30363d;">${_}</td></tr>`}),l+="</tbody></table>";let d=marked.parse(i),y=DOMPurify.sanitize(d,a),P=l+y;if(f.size>=te){let p=f.keys().next().value;f.delete(p)}return f.set(e,P),P}let n=marked.parse(t),r=DOMPurify.sanitize(n,a);if(f.size>=te){let o=f.keys().next().value;f.delete(o)}return f.set(e,r),r}catch{return e}},Ce=e=>{let t={...x.value},a=!!t[e];t[e]?delete t[e]:t[e]=!0,x.value=t,window.trackClick&&window.trackClick("EventExpanded",{eventType:"tool",action:a?"collapse":"expand",sessionId:m.value})},ke=(e,t)=>{if(!t||!t.trim()||!e)return e;let a=t.trim(),s=re(a).replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),n=document.createElement("div");n.innerHTML=e;let r=o=>{if(o.nodeType===Node.TEXT_NODE){let i=o.textContent,c=new RegExp(`(${s})`,"gi");if(c.test(i)){let l=i.replace(c,'<mark class="search-highlight">$1</mark>'),d=document.createElement("span");d.innerHTML=l,o.parentNode.replaceChild(d,o)}}else o.nodeType===Node.ELEMENT_NODE&&o.tagName!=="SCRIPT"&&o.tagName!=="STYLE"&&Array.from(o.childNodes).forEach(r)};return Array.from(n.childNodes).forEach(r),n.innerHTML},Se=e=>{let t={...w.value},a=!!t[e];t[e]?delete t[e]:t[e]=!0,w.value=t,window.trackClick&&window.trackClick("EventExpanded",{eventType:"content",action:a?"collapse":"expand",sessionId:m.value})},Ee=e=>e?e.split(`
5
- `).length>20||e.length>2e3:!1,Me=e=>{let t=e.split(`
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 o=s[1],r=s[2],c=o.split(`
4
+ `),l=[],d=0;for(;d<c.length;){let f=c[d];if(!f.trim()||!f.includes(":")){d++;continue}let z=f.indexOf(":"),H=f.substring(0,z).trim(),V=f.substring(z+1).trim();if(V==="|"||V===">"){let pe=[];for(d++;d<c.length&&(c[d].startsWith(" ")||c[d].startsWith(" ")||c[d].trim()==="");)pe.push(c[d].trim()),d++;let vt=V===">"?" ":`
5
+ `;l.push({key:H,value:pe.filter(pt=>pt).join(vt)})}else l.push({key:H,value:V}),d++}let p='<table style="margin-bottom: 16px; border-collapse: collapse; width: 100%;"><tbody>';l.forEach(f=>{let z=DOMPurify.sanitize(f.key,{ALLOWED_TAGS:[]}),H=DOMPurify.sanitize(f.value,{ALLOWED_TAGS:[]});p+=`<tr><td style="padding: 4px 12px; border: 1px solid #30363d; font-weight: 600; color: #7d8590;">${z}</td><td style="padding: 4px 12px; border: 1px solid #30363d;">${H}</td></tr>`}),p+="</tbody></table>";let J=marked.parse(r),R=DOMPurify.sanitize(J,a),B=p+R;if(h.size>=ne){let f=h.keys().next().value;h.delete(f)}return h.set(e,B),B}let n=marked.parse(t),i=DOMPurify.sanitize(n,a);if(h.size>=ne){let o=h.keys().next().value;h.delete(o)}return h.set(e,i),i}catch{return e}},Re=e=>{let t={...k.value},a=!!t[e];t[e]?delete t[e]:t[e]=!0,k.value=t,window.trackClick&&window.trackClick("EventExpanded",{eventType:"tool",action:a?"collapse":"expand",sessionId:m.value})},Le=(e,t)=>{if(!t||!t.trim()||!e)return e;let a=t.trim(),s=ce(a).replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),n=document.createElement("div");n.innerHTML=e;let i=o=>{if(o.nodeType===Node.TEXT_NODE){let r=o.textContent,c=new RegExp(`(${s})`,"gi");if(c.test(r)){let l=r.replace(c,'<mark class="search-highlight">$1</mark>'),d=document.createElement("span");d.innerHTML=l,o.parentNode.replaceChild(d,o)}}else o.nodeType===Node.ELEMENT_NODE&&o.tagName!=="SCRIPT"&&o.tagName!=="STYLE"&&Array.from(o.childNodes).forEach(i)};return Array.from(n.childNodes).forEach(i),n.innerHTML},Ae=e=>{let t={...w.value},a=!!t[e];t[e]?delete t[e]:t[e]=!0,w.value=t,window.trackClick&&window.trackClick("EventExpanded",{eventType:"content",action:a?"collapse":"expand",sessionId:m.value})},De=e=>e?e.split(`
6
+ `).length>20||e.length>2e3:!1,Oe=e=>{let t=e.split(`
6
7
  `);return t.length<=20?e:t.slice(0,20).join(`
7
8
  `)+`
8
9
 
9
- ...`},Ne=(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"}},Le=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:""}},Ae=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)},Re=e=>{if(!e.complete)return"";let t=new Date(e.start.timestamp).getTime(),s=new Date(e.complete.timestamp).getTime()-t;return s>=100?`${(s/1e3).toFixed(1)}s`:""},De=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||"",r=t.targetUrl||t.url||"";s=r?`${n} ${r}`: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},Oe=e=>e.data?.tools&&e.data.tools.length>0,_e=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";return{tool:t.name,start:{data:{toolName:t.name,arguments:t.input||t.arguments||{}}},complete:a?{data:{result:t.result,error:t.status==="error"?t.error:null}}:null}}):[],ae=["#58a6ff","#f0883e","#a371f7","#3fb950","#f778ba","#79c0ff","#d29922","#56d4dd"],$e=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}=xe.value;if(e.type==="subagent.started"||e.type==="subagent.completed"||e.type==="subagent.failed"){let r=e.data?.toolCallId;if(r&&a.has(r)){let o=a.get(r);return{name:o.name,toolCallId:r,colorIndex:o.colorIndex}}return null}if(e._subagent){let r=e._subagent.id,o=e._subagent.name;if(a.has(r)){let i=a.get(r);return{name:i.name,toolCallId:r,colorIndex:i.colorIndex}}return{name:o,toolCallId:r,colorIndex:Math.abs($e(r))}}if(e.data?.subAgentId){let r=e.data.subAgentId,o=a.get(r);if(o)return{name:o.name,toolCallId:r,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},qe=e=>{let t=se(e);return t?ae[t.colorIndex%ae.length]:null},Ue=e=>{if(window.trackClick){let t=ee.value.find(a=>a.type===e);window.trackClick("EventFilterClicked",{filterType:e,filterLabel:t?t.label:e,sessionId:m.value})}S.value=e},oe=e=>{V.value="",S.value="all",X.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)}}})},Be=()=>{if(!g.value)return;let e=t=>{t<=0||!g.value||(g.value.scrollToItem(0),setTimeout(()=>e(t-1),100))};e(3)},Pe=()=>{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)},ne=e=>{window.trackClick&&window.trackClick("TurnClicked",{turnNumber:e,sessionId:m.value});let t=R.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),oe(t)}},ze=e=>{if(!e)return"";let t=e.replace(/\/$/,"").split("/");return t[t.length-1]||e},Ve=e=>{let t=R.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}`},He=e=>R.value.find(a=>a.index===e)?.duration||null,re=e=>{let t=document.createElement("div");return t.textContent=e,t.innerHTML},je=e=>e?new Date(e).toLocaleString():"N/A",Fe=async()=>{console.log("[Export] exportSession called"),window.trackClick&&window.trackClick("ExportClicked",{sessionId:m.value}),z.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",r="\u2713 Downloaded!",o=document.querySelector(".export-btn");o&&(o.textContent=r,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{z.value=!1,console.log("[Export] Export complete")}};F(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))I.value=s;else if(s.events&&Array.isArray(s.events))I.value=s.events,console.log("[Navigation] Pagination:",s.pagination);else throw new Error("Invalid response format");if(console.log("[Navigation] Events loaded:",I.value.length),I.value.length>0){let c=I.value[I.value.length-1],l=c.timestamp||c.time||c.data?.timestamp;l&&(W.value.updated=new Date(l))}let n=new URLSearchParams(window.location.search),r=n.get("eventType"),o=n.get("eventName"),i=n.get("eventTimestamp");console.log("[Navigation] URL params:",r,o,i),r&&o&&(console.log("[Navigation] Waiting for Vue to render..."),Vue.nextTick(()=>{console.log("[Navigation] nextTick - flatEvents count:",v.value?.length);let c=null;if(r==="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),ne(d);return}}}else r==="subagent.started"?(console.log("[Navigation] Searching for subagent:",o,"timestamp:",i),i&&(c=v.value.find(l=>l.type==="subagent.started"&&l.timestamp===i)),c||(c=v.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=v.value.find(l=>l.type===r);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=y=>{y<=0||!g.value||(g.value.scrollToItem(l),setTimeout(()=>d(y-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),Q.value=a.message}finally{Z.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,r=80,o=Math.floor(s/r),i=Math.ceil(n/r),c=Math.min(o+i,A.value.length),l=Math.max(1,o+1),d=Math.max(1,c);Y.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),me(()=>{L&&(clearTimeout(L),L=null),t&&t(),x.value={},w.value={},f.clear()})});let D=u([]),H=u([]),O=u(!1),b=u([]),C=u(""),j=u(null),h=u(""),k=u(!1),B=u([]),ie=u(0),le=["#3b82f6","#10b981","#f59e0b","#ef4444","#8b5cf6","#ec4899","#06b6d4","#f97316"],Ge=e=>{let t=0;for(let a=0;a<e.length;a++)t=e.charCodeAt(a)+((t<<5)-t);return le[Math.abs(t)%le.length]},We=async()=>{try{let e=await fetch(`/api/sessions/${m.value}/tags`);if(e.ok){let t=await e.json();D.value=t.tags||[]}}catch(e){console.error("Error loading tags:",e)}},de=async()=>{try{let e=await fetch("/api/tags");if(e.ok){let t=await e.json();H.value=t.tags||[]}}catch(e){console.error("Error loading all tags:",e)}},Ke=async e=>{try{window.trackClick&&e.filter(s=>!D.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 D.value=a.tags||[],h.value="",!0}else{let a=await t.json();return h.value=a.error||"Failed to save tags",!1}}catch(t){return console.error("Error saving tags:",t),h.value="Network error",!1}},Je=()=>{b.value=[...D.value],O.value=!0,h.value="",setTimeout(()=>{j.value&&j.value.focus()},10)},Xe=()=>{O.value=!1,b.value=[],C.value="",k.value=!1,h.value=""},ce=()=>{let e=C.value.trim().toLowerCase();if(e){if(e.length>30){h.value="Tag must be 30 characters or less";return}if(b.value.length>=10){h.value="Maximum 10 tags per session";return}if(b.value.includes(e)){h.value="Tag already added",C.value="";return}b.value.push(e),C.value="",k.value=!1,h.value=""}},Ye=e=>{b.value=b.value.filter(t=>t!==e),h.value=""},Ze=()=>{let e=C.value.trim().toLowerCase();if(!e){k.value=!1,B.value=[];return}let t=H.value.filter(a=>a.toLowerCase().includes(e)&&!b.value.includes(a)).slice(0,5);t.length>0?(k.value=!0,B.value=t,ie.value=0):(k.value=!1,B.value=[])},Qe=e=>{C.value=e,ce()},et=async()=>{setTimeout(async()=>{if(!O.value)return;await Ke(b.value)&&(O.value=!1,b.value=[],C.value="",k.value=!1,await de())},200)};return F(async()=>{await We(),await de()}),{sessionId:m,metadata:W,exporting:z,sidebarCollapsed:q,expandedTools:x,expandedContent:w,expansionCount:he,currentFilter:S,searchText:V,currentTurnIndex:X,scrollerRef:g,visibleRange:Y,loadedEvents:I,eventsLoading:Z,eventsError:Q,flatEvents:v,filteredEvents:A,eventCounts:fe,filters:ee,turns:R,userReqs:Te,truncateText:ye,formatTime:we,formatDateTime:je,renderMarkdown:Ie,highlightSearchText:ke,toggleTool:Ce,toggleContent:Se,isContentTooLong:Ee,truncateContent:Me,getBadgeInfo:Ne,getToolStatus:Le,getToolErrorMessage:Ae,getToolDuration:Re,getToolCommand:De,hasTools:Oe,getToolGroups:_e,getSubagentInfo:se,getSubagentColor:qe,setFilter:Ue,scrollToTurn:oe,scrollToTop:Be,scrollToBottom:Pe,jumpToTurn:ne,getTurnNumber:Ve,getTurnDuration:He,repoBasename:ze,escapeHtml:re,exportSession:Fe,searchResultCount:be,sessionTags:D,allTags:H,tagsEditing:O,editingTags:b,tagInputValue:C,tagInputRef:j,tagsError:h,showAutocomplete:k,autocompleteOptions:B,autocompleteSelectedIndex:ie,getTagColor:Ge,startEditTags:Je,cancelEditTags:Xe,addTag:ce,removeTagFromEdit:Ye,updateAutocomplete:Ze,selectAutocompleteOption:Qe,saveTagsOnBlur:et}},template:`
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
11
  <div class="container">
11
12
  <div class="header">
12
13
  <a href="/" class="home-btn">\u2190 Back to Home</a>
@@ -77,6 +78,73 @@
77
78
  </div>
78
79
  </div>
79
80
 
81
+ <!-- Usage Section -->
82
+ <div v-if="metadata.usage" class="sidebar-section">
83
+ <div class="sidebar-section-title">Usage</div>
84
+ <div class="usage-container">
85
+ <!-- Compact view (always visible) -->
86
+ <div class="usage-compact" @click="toggleUsage">
87
+ {{ totalRequests }} reqs \xB7 {{ formatTokens(totalTokens) }} tokens \xB7 {{ formatDuration(metadata.usage.totalApiDurationMs) }}
88
+ <span class="usage-expand-icon">{{ usageExpanded ? '\u25B2' : '\u25BC' }}</span>
89
+ </div>
90
+
91
+ <!-- Expanded view -->
92
+ <div v-if="usageExpanded" class="usage-expanded">
93
+ <!-- Model breakdown -->
94
+ <div v-if="Object.keys(metadata.usage.modelMetrics).length > 0" class="usage-section">
95
+ <div class="usage-section-title">Models</div>
96
+ <div v-for="(metrics, model) in metadata.usage.modelMetrics" :key="model" class="usage-model">
97
+ <div class="usage-model-name">{{ model }}</div>
98
+ <div class="usage-model-details">
99
+ <div>{{ metrics.requests?.count || 0 }} reqs</div>
100
+ <div v-if="metrics.requests?.cost">Cost: {{ formatCost(metrics.requests.cost) }}</div>
101
+ <div v-if="metrics.usage">
102
+ <span class="usage-token-label">In:</span> {{ formatTokens(metrics.usage.inputTokens || 0) }}
103
+ <span class="usage-token-label">Out:</span> {{ formatTokens(metrics.usage.outputTokens || 0) }}
104
+ </div>
105
+ <div v-if="metrics.usage?.cacheReadTokens">
106
+ <span class="usage-token-label">Cache Read:</span> {{ formatTokens(metrics.usage.cacheReadTokens) }}
107
+ <span v-if="getCacheHitRatio(model)" class="usage-cache-ratio">({{ getCacheHitRatio(model) }}%)</span>
108
+ </div>
109
+ <div v-if="metrics.usage?.cacheWriteTokens">
110
+ <span class="usage-token-label">Cache Write:</span> {{ formatTokens(metrics.usage.cacheWriteTokens) }}
111
+ </div>
112
+ </div>
113
+ </div>
114
+ </div>
115
+
116
+ <!-- Context window breakdown -->
117
+ <div v-if="metadata.usage.currentTokens || metadata.usage.systemTokens || metadata.usage.conversationTokens || metadata.usage.toolDefinitionsTokens" class="usage-section">
118
+ <div class="usage-section-title">Context Window</div>
119
+ <div class="usage-context">
120
+ <div v-if="metadata.usage.currentTokens">
121
+ <span class="usage-token-label">Current:</span> {{ formatTokens(metadata.usage.currentTokens) }}
122
+ </div>
123
+ <div v-if="metadata.usage.systemTokens">
124
+ <span class="usage-token-label">System:</span> {{ formatTokens(metadata.usage.systemTokens) }}
125
+ </div>
126
+ <div v-if="metadata.usage.conversationTokens">
127
+ <span class="usage-token-label">Conversation:</span> {{ formatTokens(metadata.usage.conversationTokens) }}
128
+ </div>
129
+ <div v-if="metadata.usage.toolDefinitionsTokens">
130
+ <span class="usage-token-label">Tools:</span> {{ formatTokens(metadata.usage.toolDefinitionsTokens) }}
131
+ </div>
132
+ </div>
133
+ </div>
134
+
135
+ <!-- Code changes -->
136
+ <div v-if="metadata.usage.codeChanges && (metadata.usage.codeChanges.linesAdded > 0 || metadata.usage.codeChanges.linesRemoved > 0)" class="usage-section">
137
+ <div class="usage-section-title">Code Changes</div>
138
+ <div class="usage-code-changes">
139
+ <span class="usage-code-added">+{{ metadata.usage.codeChanges.linesAdded }}</span>
140
+ <span class="usage-code-removed">-{{ metadata.usage.codeChanges.linesRemoved }}</span>
141
+ <span class="usage-code-files">({{ metadata.usage.codeChanges.filesModified?.length || 0 }} files)</span>
142
+ </div>
143
+ </div>
144
+ </div>
145
+ </div>
146
+ </div>
147
+
80
148
  <div class="sidebar-section">
81
149
  <div class="sidebar-section-title">Event Filters</div>
82
150
  <div class="event-filters">
@@ -211,6 +279,7 @@
211
279
  ref="scrollerRef"
212
280
  :items="filteredEvents"
213
281
  :min-item-size="80"
282
+ :prerender="10"
214
283
  key-field="stableId"
215
284
  class="scroller"
216
285
  >
@@ -393,10 +462,32 @@
393
462
  </div>
394
463
 
395
464
  <!-- No content at all (no message and no tools) -->
396
- <div v-else-if="!hasTools(item)" class="event-content" style="color: #7d8590; font-style: italic;">
465
+ <div v-else-if="!hasTools(item) && !item.data?.reasoningText" class="event-content" style="color: #7d8590; font-style: italic;">
397
466
  No available message
398
467
  </div>
399
468
 
469
+ <!-- Reasoning text (shown after main content, before tool calls) -->
470
+ <div v-if="item.data?.reasoningText" class="event-content reasoning-text-content">
471
+ <div
472
+ v-html="highlightSearchText(
473
+ renderMarkdown(
474
+ (expandedContent[item.stableId + '-reasoning'] || !isContentTooLong(item.data.reasoningText))
475
+ ? item.data.reasoningText
476
+ : truncateContent(item.data.reasoningText)
477
+ ),
478
+ searchText
479
+ )"
480
+ ></div>
481
+ <div v-if="isContentTooLong(item.data.reasoningText)" style="margin-top: 8px;">
482
+ <button
483
+ @click="toggleContent(item.stableId + '-reasoning')"
484
+ style="background: none; border: 1px solid #30363d; color: #58a6ff; padding: 4px 12px; border-radius: 4px; cursor: pointer; font-size: 13px;"
485
+ >
486
+ {{ expandedContent[item.stableId + '-reasoning'] ? 'Show less \u25B2' : 'Show more \u25BC' }}
487
+ </button>
488
+ </div>
489
+ </div>
490
+
400
491
  <!-- Tool calls section (independent of message content, but don't need "No available message" if tools exist) -->
401
492
  <div v-if="hasTools(item)" class="tool-list">
402
493
  <div
@@ -417,6 +508,13 @@
417
508
  </div>
418
509
 
419
510
  <div v-if="expandedTools[item.stableId + '-' + idx]" class="tool-detail">
511
+ <div v-if="group.timing.startTime || group.timing.endTime || group.timing.duration" class="tool-detail-section">
512
+ <div class="tool-detail-content tool-timing-line">
513
+ <span v-if="group.timing.startTime"><span class="tool-timing-label">Start</span> {{ formatToolTime(group.timing.startTime) }}</span>
514
+ <span v-if="group.timing.endTime"><span class="tool-timing-label">Complete</span> {{ formatToolTime(group.timing.endTime) }}</span>
515
+ <span v-if="group.timing.duration"><span class="tool-timing-label">Duration</span> {{ group.timing.duration }}</span>
516
+ </div>
517
+ </div>
420
518
  <div v-if="group.start?.data?.arguments" class="tool-detail-section">
421
519
  <div class="tool-detail-title">Arguments:</div>
422
520
  <div class="tool-detail-content">
@@ -458,4 +556,4 @@
458
556
  </div>
459
557
 
460
558
  </div>
461
- `});console.log("Mounting Vue app to #app..."),console.log("App config:",G.config),console.log("Target element:",document.getElementById("app"));try{let m=G.mount("#app");console.log("Vue app mounted successfully!",m?"Instance created":"No instance"),console.log("VM type:",typeof m,"Has exportSession:",typeof m?.exportSession),console.log("VM keys:",m?Object.keys(m).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(m){console.error("Mount failed:",m),console.error("Error stack:",m.stack)}})();})();
559
+ `});console.log("Mounting Vue app to #app..."),console.log("App config:",Y.config),console.log("Target element:",document.getElementById("app"));try{let m=Y.mount("#app");console.log("Vue app mounted successfully!",m?"Instance created":"No instance"),console.log("VM type:",typeof m,"Has exportSession:",typeof m?.exportSession),console.log("VM keys:",m?Object.keys(m).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(m){console.error("Mount failed:",m),console.error("Error stack:",m.stack)}})();})();