openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,102 lines • 107 kB
JavaScript
import{n as e}from"./rolldown-runtime-DkW27tQK.js";import{Bi as t,Cn as n,Fn as r,Qi as i,R as a,Yi as o,Zn as s,bn as c,jn as l,wr as u,z as d}from"./control-ui-foundation-Cydpkyqa.js";import{Bn as f,C as p,E as m,Eo as h,Oo as g,Sc as _,b as v,bc as y,cr as b,k as x,lr as S,m as C,oa as w,sa as T,sr as E,wn as D,y as ee,zn as O}from"./control-ui-core-Rwzhj6nO.js";import{$ as k,K as A,L as j,Q as M,X as N,at as P,q as F,st as I,z as L}from"./lit-runtime-CV_MH99K.js";import{An as R,_ as z,_n as B,g as te,jn as V}from"./control-ui-core-YphAl4aM.js";import{Ht as H,Yt as U,Z as ne,en as W}from"./control-ui-core-vvcQQV2m.js";import{B as re,U as ie,V as ae,z as oe}from"./control-ui-boot-shared-BYq7Cchl.js";import{$n as se,Dr as ce,Er as le,Or as ue,Tr as de,Uo as fe,bt as pe,er as me,kr as he,wr as ge}from"./control-ui-boot-shared-DfZ39Yj9.js";import{K as _e,L as ve,X as G,q as ye}from"./control-ui-boot-chat-_QSl8fbH.js";import{Is as be,Ls as xe}from"./control-ui-boot-shared-CXVvK2AW.js";import{n as Se,t as Ce}from"./settings-workspace-Bseic8Tf.js";import{n as we,t as Te}from"./agent-scope-control-6x2VZZ2e.js";import{a as Ee,i as De,o as Oe,r as ke}from"./usage-y60JDuyp.js";import{a as Ae,i as je,n as Me,o as Ne,r as Pe,s as Fe}from"./usage-BoX28Uwf.js";import{n as Ie,r as Le,t as Re}from"./request-usage-snapshot-8Lzw48R1.js";function ze(e,t){return[e,t].some(e=>e&&e.status!==`fresh`)}function Be(){let e=new Date;return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,`0`)}-${String(e.getDate()).padStart(2,`0`)}`}function Ve(e){return h(e,`request failed`)}function He(e,t,n,r,i){if(r&&e.length>0)for(let r of e.slice(-1)){let i=n.indexOf(r),a=n.indexOf(t);if(i!==-1&&a!==-1){let[t,r]=i<a?[i,a]:[a,i];return[...new Set([...e,...n.slice(t,r+1)])]}}return e.includes(t)?e.filter(e=>e!==t):i?[...e,t]:[t]}function Ue(e,t,n,r){if(r&&e.length>0){let r=n.indexOf(e.at(-1)??``),i=n.indexOf(t);if(r!==-1&&i!==-1){let[t,a]=r<i?[r,i]:[i,r];return[...new Set([...e,...n.slice(t,a+1)])]}}return e.length===1&&e[0]===t?[]:[t]}function We(e){let t=e.split(`
`),n=new Map,r=[];for(let e of t){let t=/^\[Tool:\s*([^\]]+)\]/.exec(e.trim())?.[1];if(t){n.set(t,(n.get(t)??0)+1);continue}e.trim().startsWith(`[Tool Result]`)||r.push(e)}let i=Array.from(n.entries()).toSorted((e,t)=>t[1]-e[1]),a=i.reduce((e,[,t])=>e+t,0);return{tools:i,summary:i.length>0?`Tools: ${i.map(([e,t])=>`${e}×${t}`).join(`, `)} (${a} calls)`:``,cleanContent:r.join(`
`).trim()}}var Ge,Ke,qe,K,Je,Ye,Xe,Ze,Qe,$e,et,tt,nt,rt,it,at,ot;function q(){return(q=e((()=>{g(),Ge=e=>i(e),Ke=e=>{let t=e.replace(/[.+^${}()|[\]\\]/g,`\\$&`).replace(/\*/g,`.*`).replace(/\?/g,`.`);return RegExp(`^${t}$`,`i`)},qe=e=>{let t=i(e);if(!t)return null;t.startsWith(`$`)&&(t=t.slice(1));let n=1;if(t.endsWith(`k`)?(n=1e3,t=t.slice(0,-1)):t.endsWith(`m`)&&(n=1e6,t=t.slice(0,-1)),!/^\d+(?:\.\d+)?$/.test(t))return null;let r=Number(t)*n;return!Number.isFinite(r)||!Number.isSafeInteger(Math.round(r))?null:r},K=e=>(e.match(/(?:[^\s"]|"[^"]*")+/g)??[]).map(e=>{let t=e.replace(/^"(.*)"$/u,`$1`),n=t.indexOf(`:`);return n>0?{key:t.slice(0,n),value:t.slice(n+1).replace(/^"(.*)"$/u,`$1`),raw:t}:{value:t,raw:e}}),Je=e=>[e.label,e.key,e.sessionId].filter(e=>!!e).map(e=>i(e)),Ye=e=>{let t=new Set;e.modelProvider&&t.add(i(e.modelProvider)),e.providerOverride&&t.add(i(e.providerOverride)),e.origin?.provider&&t.add(i(e.origin.provider));for(let n of e.usage?.modelUsage??[])n.provider&&t.add(i(n.provider));return Array.from(t)},Xe=e=>{let t=new Set;e.model&&t.add(i(e.model));for(let n of e.usage?.modelUsage??[])n.model&&t.add(i(n.model));return Array.from(t)},Ze=e=>(e.usage?.toolUsage?.tools??[]).map(e=>i(e.name)),Qe={tools:e=>(e.usage?.toolUsage?.totalCalls??0)>0,errors:e=>(e.usage?.messageCounts?.errors??0)>0,context:e=>e.hasContextWeight===!0,usage:e=>!!e.usage,model:e=>Xe(e).length>0,provider:e=>Ye(e).length>0},$e=(e,t)=>e>=t,et=(e,t)=>e<=t,tt={mintokens:[e=>e.usage?.totalTokens??0,$e],maxtokens:[e=>e.usage?.totalTokens??0,et],mincost:[e=>e.usage?.totalCost??0,$e],maxcost:[e=>e.usage?.totalCost??0,et],minmessages:[e=>e.usage?.messageCounts?.total??0,$e],maxmessages:[e=>e.usage?.messageCounts?.total??0,et]},nt=new Set([`agent`,`channel`,`chat`,`provider`,`model`,`tool`,`label`,`key`,`session`,`id`,`has`,...Object.keys(tt)]),rt=new Set([`channel`,`provider`,`model`,`tool`]),it=()=>!0,at=(e,t,n)=>{if(e.key&&!nt.has(t))return n.push(`Unknown filter: ${e.key}`),it;e.key&&e.value===``&&n.push(`Missing value for ${e.key}`);let r=Ge(e.value??``),a=Object.hasOwn(tt,t)?tt[t]:void 0,o=a&&e.value?qe(e.value):null;if(a&&e.value&&o===null&&n.push(`Invalid number for ${e.key}`),t===`has`){let t=Object.hasOwn(Qe,r)?Qe[r]:void 0;return e.value&&!t&&n.push(`Unknown has:${e.value}`),t??it}if(!r)return it;if(!e.key)return e=>Je(e).some(e=>e.includes(r));switch(t){case`agent`:return e=>i(e.agentId).includes(r);case`channel`:return e=>i(e.channel).includes(r);case`chat`:return e=>i(e.chatType).includes(r);case`provider`:return e=>Ye(e).some(e=>e.includes(r));case`model`:return e=>Xe(e).some(e=>e.includes(r));case`tool`:return e=>Ze(e).some(e=>e.includes(r));case`label`:return e=>i(e.label).includes(r);case`key`:case`session`:case`id`:if(r.includes(`*`)||r.includes(`?`)){let e;return t=>(e??=Ke(r),e.test(t.key)||(t.sessionId?e.test(t.sessionId):!1))}return e=>i(e.key).includes(r)||i(e.sessionId).includes(r)}if(!a||o===null)return it;let[s,c]=a;return e=>c(s(e),o)},ot=(e,t)=>{let n=K(t);if(n.length===0)return{sessions:e,warnings:[]};let r=[],i=new Map,a=n.map(e=>{let t=Ge(e.key??``),n=at(e,t,r);if(!rt.has(t))return n;let a=i.get(t)??[];return e.value&&a.push(n),i.set(t,a),e=>a.length===0||a.some(t=>t(e))});return{sessions:e.filter(e=>a.every(t=>t(e))),warnings:r}}})))()}function st(e,t){return S(t)?{clearData:!0,status:ce(le(),E(`usage details`))}:{clearData:!1,status:ce(e,Ve(t))}}function ct(){return(ct=e((()=>{ue(),b(),q()})))()}function lt(e,t,n,r){let i=null,a=le(),o=new re(e,{autoRun:!1,args:()=>[null,``],task:async([e,t],{signal:r})=>e&&t?{sessionKey:t,data:await n(e,t,r)}:ae,onComplete:e=>{i=e,a=de()},onError:e=>{let t=st(a,e);t.clearData&&(i=null),a=t.status}}),s=()=>{o.run([null,``])};return{get data(){return i?.data??null},get status(){return a},get loading(){return o.status===ie.PENDING},load(e){let n=t.client;if(!n||!t.connected)return Promise.resolve();let c=r?.(e)!==!1;return(i?.sessionKey!==e||!c)&&(i=null,a=le()),c?(a=ge(a),o.run([n,e])):(s(),Promise.resolve())},cancel:s,clear(){i=null,a=le(),s()}}}var ut;function dt(){return(dt=e((()=>{oe(),ue(),ct(),ut=class{constructor(e,t,n,r){this.timeSeries=lt(e,t,Oe),this.sessionLogs=lt(e,t,async(e,t)=>{let n=await Ee(e,t);return Array.isArray(n.logs)?n.logs:null}),this.contextWeight=lt(e,t,(e,t,i)=>{let a=n(),o=r().find(e=>e.key===t)?.agentId??a.agentId;return De(e,{...a,agentId:o},t,i)},e=>r().some(t=>t.key===e&&t.hasContextWeight))}load(e){this.timeSeries.load(e),this.sessionLogs.load(e),this.contextWeight.load(e)}cancel(){this.timeSeries.cancel(),this.sessionLogs.cancel(),this.contextWeight.cancel()}clear(){this.timeSeries.clear(),this.sessionLogs.clear(),this.contextWeight.clear()}}})))()}function ft({agentId:e,key:t}){return JSON.stringify([e,t])}function pt(e,t,n){return new re(e,{autoRun:!1,args:()=>[null],task:async([e],{signal:r})=>{if(!e)return ae;let i=t.capture();if(!i)throw Error(W(`common.offline`));let a=`openclaw-usage-${Be()}.json`,o=new Map;if(e.sessions.some(e=>e.hasContextWeight)){let t=await ke(i.client,n(),{includeContextWeight:!0,signal:r});if(o=new Map(t.sessions.map(e=>[ft(e),e.contextWeight])),e.sessions.some(e=>e.hasContextWeight&&!o.get(ft(e))))throw Error(W(`usage.export.changed`))}return{connection:i,filename:a,data:{...e,sessions:e.sessions.map(e=>({...e,contextWeight:o.get(ft(e))??null}))}}},onComplete:({connection:e,filename:n,data:r})=>{t.isCurrent(e)&&pe(n,JSON.stringify(r,null,2),`application/json;charset=utf-8`)},onError:e=>{T({message:`${W(`usage.export.label`)}: ${Ve(e)}`})}})}function mt(){return(mt=e((()=>{oe(),U(),w(),q()})))()}function ht(e,t,n){let r=t?.sessions.map(e=>e.agentId).filter(e=>!!e?.trim())??[];return N`
${ye({title:V(`usage`),subtitle:R(`usage`),actions:we({agents:e.agents.state.agentsList?.agents??[],additionalAgentIds:r,selection:e.agentSelection})})}
${Se(n)}
`}function gt(){return(gt=e((()=>{A(),B(),Te(),ve(),Ce()})))()}var _t;function vt(){return(vt=e((()=>{_t=[`channel`,`agent`,`provider`,`model`,`messages`,`tools`,`errors`,`duration`]})))()}function yt(){return{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,totalCost:0,inputCost:0,outputCost:0,cacheReadCost:0,cacheWriteCost:0,missingCostEntries:0}}function bt(e,t){if(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheWrite+=t.cacheWrite,e.totalTokens+=t.totalTokens,e.totalCost+=t.totalCost,e.inputCost+=t.inputCost,e.outputCost+=t.outputCost,e.cacheReadCost+=t.cacheReadCost,e.cacheWriteCost+=t.cacheWriteCost,e.missingCostEntries+=t.missingCostEntries,t.missingCostByModel){e.missingCostByModel??={};for(let[n,r]of Object.entries(t.missingCostByModel))e.missingCostByModel[n]=(e.missingCostByModel[n]??0)+r}}function xt(e,t){return JSON.stringify([e??`unknown`,t??`unknown`])}function St(e,t,n){return JSON.stringify([e,t??`unknown`,n??`unknown`])}function Ct(){return{count:0,sum:0,min:1/0,max:0,p95Max:0}}function wt(e,t){e.count+=t.count,e.sum+=t.avgMs*t.count,e.min=Math.min(e.min,t.minMs),e.max=Math.max(e.max,t.maxMs),e.p95Max=Math.max(e.p95Max,t.p95Ms)}function Tt(e){return{count:e.count,avgMs:e.count?e.sum/e.count:0,minMs:e.min===1/0?0:e.min,maxMs:e.max,p95Ms:e.p95Max}}function Et(e,t,n){let r=e.get(t)??{provider:n.provider,model:n.model,count:0,totals:yt()};r.count+=n.count,bt(r.totals,n.totals),e.set(t,r)}function Dt(e,t,n){if(!t)return;let r=e.get(t)??yt();bt(r,n),e.set(t,r)}function Ot(e,t){return t.totals.totalCost-e.totals.totalCost||t.totals.totalTokens-e.totals.totalTokens}function kt(){let e=yt(),t={total:0,user:0,assistant:0,toolCalls:0,toolResults:0,errors:0},n=new Map,r=new Map,i=new Map,a=new Map,o=new Map,s=new Map,c=new Map,l=new Map,u=Ct(),d=0,f=0;function p(e){let t=s.get(e);return t||(t={date:e,tokens:0,cost:0,messages:0,toolCalls:0,errors:0},s.set(e,t)),t}function m({usage:s,agentId:m,channel:h}){if(s){bt(e,s),f=Math.max(f,s.durationMs??0),(s.firstActivity!==void 0||(s.messageCounts?.total??0)>0)&&(d+=1),s.messageCounts&&(t.total+=s.messageCounts.total,t.user+=s.messageCounts.user,t.assistant+=s.messageCounts.assistant,t.toolCalls+=s.messageCounts.toolCalls,t.toolResults+=s.messageCounts.toolResults,t.errors+=s.messageCounts.errors);for(let e of s.toolUsage?.tools??[])n.set(e.name,(n.get(e.name)??0)+e.count);for(let e of s.modelUsage??[])Et(r,xt(e.provider,e.model),e),Et(i,e.provider??`unknown`,{...e,model:void 0});Dt(a,m,s),Dt(o,h,s),s.latency&&s.latency.count>0&&wt(u,s.latency);for(let e of s.dailyLatency??[]){let t=c.get(e.date)??Ct();wt(t,e),c.set(e.date,t)}for(let e of s.dailyBreakdown??[]){let t=p(e.date);t.tokens+=e.tokens,t.cost+=e.cost}for(let e of s.dailyMessageCounts??[]){let t=p(e.date);t.messages+=e.total,t.toolCalls+=e.toolCalls,t.errors+=e.errors}for(let e of s.dailyModelUsage??[]){let t=St(e.date,e.provider,e.model),n=l.get(t)??{date:e.date,provider:e.provider,model:e.model,tokens:0,cost:0,count:0};n.tokens+=e.tokens,n.cost+=e.cost,n.count+=e.count,l.set(t,n)}}}function h(){let e=Array.from(n,([e,t])=>({name:e,count:t})).toSorted((e,t)=>t.count-e.count);return{sessionCount:d,...f>0?{longestSessionDurationMs:f}:{},messages:t,tools:{totalCalls:e.reduce((e,{count:t})=>e+t,0),uniqueTools:n.size,tools:e},byModel:Array.from(r.values()).toSorted(Ot),byProvider:Array.from(i.values()).toSorted(Ot),byAgent:Array.from(a,([e,t])=>({agentId:e,totals:t})).toSorted((e,t)=>t.totals.totalCost-e.totals.totalCost),byChannel:Array.from(o,([e,t])=>({channel:e,totals:t})).toSorted((e,t)=>t.totals.totalCost-e.totals.totalCost),latency:u.count>0?Tt(u):void 0,dailyLatency:Array.from(c,([e,t])=>({date:e,...Tt(t)})).toSorted((e,t)=>e.date.localeCompare(t.date)),modelDaily:Array.from(l.values()).toSorted((e,t)=>e.date.localeCompare(t.date)||t.cost-e.cost),daily:Array.from(s.values()).toSorted((e,t)=>e.date.localeCompare(t.date))}}return{totals:e,add:m,finish:h}}function At(){return(At=e((()=>{})))()}function jt(e){return Math.round(e/en)}function J(e){return C(e,{thousandsSuffix:`K`,trimTrailingZero:!1})}function Mt(e,t=2){return`$${e.toFixed(t)}`}function Nt(e){return new Date(Date.UTC(1970,0,1,e)).toLocaleTimeString(void 0,{hour:`numeric`,timeZone:`UTC`})}function Pt(e,t,n){let r=e.usage;if(!r)return!1;let i=r.firstActivity??e.updatedAt,a=r.lastActivity??e.updatedAt;if(!i||!a)return!1;let o=Math.min(i,a),s=Math.max(i,a);if(o===s){let e=new Date(o);return n({usage:r,hour:It(e,t),weekday:Lt(e,t),share:1}),!0}let c=(s-o)/6e4,l=o;for(;l<s;){let e=new Date(l),i=Bt(e,t),a=Math.min(i.getTime(),s),o=Math.max((a-l)/6e4,0);n({usage:r,hour:It(e,t),weekday:Lt(e,t),share:o/c}),l=a+1}return!0}function Ft(e,t){let n=Array.from({length:24},()=>0),r=Array.from({length:24},()=>0);for(let i of e){let e=i.usage;if(!e?.messageCounts||e.messageCounts.total===0)continue;let a=e.messageCounts;if(e.utcQuarterHourMessageCounts&&e.utcQuarterHourMessageCounts.length>0){for(let i of e.utcQuarterHourMessageCounts){let e=zt(i.date,i.quarterIndex,t);e&&(n[e.hour]=(n[e.hour]??0)+i.errors,r[e.hour]=(r[e.hour]??0)+i.total)}continue}Pt(i,t,({hour:e,share:t})=>{n[e]=(n[e]??0)+(a.errors??0)*t,r[e]=(r[e]??0)+a.total*t})}return r.map((e,t)=>{let r=n[t]??0;return{hour:t,rate:e>0?r/e:0,errors:r,msgs:e}}).filter(e=>e.msgs>0&&e.errors>0).toSorted((e,t)=>t.rate-e.rate).slice(0,5).map(e=>({label:Nt(e.hour),value:`${(e.rate*100).toFixed(2)}%`,sub:`${Math.round(e.errors)} ${i(W(`usage.overview.errors`))} · ${Math.round(e.msgs)} ${W(`usage.overview.messagesAbbrev`)}`}))}function It(e,t){return t===`utc`?e.getUTCHours():e.getHours()}function Lt(e,t){return t===`utc`?e.getUTCDay():e.getDay()}function Rt(e,t){let n=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);if(!n||!Number.isInteger(t)||t<0||t>95)return null;let[,r,i,a]=n,o=Number(r),s=Number(i),c=Number(a),l=new Date(Date.UTC(o,s-1,c,0,t*15));return Number.isNaN(l.valueOf())||l.getUTCFullYear()!==o||l.getUTCMonth()!==s-1||l.getUTCDate()!==c?null:l}function zt(e,t,n){let r=Rt(e,t);return r?{hour:It(r,n),weekday:Lt(r,n)}:null}function Bt(e,t){let n=new Date(e);return t===`utc`?n.setUTCMinutes(59,59,999):n.setMinutes(59,59,999),n}function Vt(e,t,n){let r=e.usage?.utcQuarterHourTokenUsage;if(!r||r.length===0)return!1;let i=!1;for(let e of r){if(e.totalTokens<=0)continue;let r=zt(e.date,e.quarterIndex,t);r&&(i=!0,n({hour:r.hour,weekday:r.weekday,tokens:e.totalTokens}))}return i}function Ht(e,t,n){let r=e.usage,i=r?.firstActivity??e.updatedAt,a=r?.lastActivity??e.updatedAt;if(!i||!a)return!1;let o=Math.min(i,a),s=Math.max(i,a),c=o;for(;c<=s;){let e=new Date(c),r=It(e,n);if(t.includes(r))return!0;let i=Bt(e,n);c=Math.min(i.getTime(),s)+1}return!1}function Ut(e,t,n){if(t.length===0)return!0;let r=!1;return Vt(e,n,({hour:e})=>{t.includes(e)&&(r=!0)})?r:Ht(e,t,n)}function Wt(e,t){let n=Array.from({length:24},()=>0),r=Array.from({length:7},()=>0),i=0,a=!1;for(let o of e){let e=o.usage;if(!(!e||!e.totalTokens||e.totalTokens<=0)){if(i+=e.totalTokens,Vt(o,t,({hour:e,weekday:t,tokens:i})=>{n[e]=(n[e]??0)+i,r[t]=(r[t]??0)+i})){a=!0;continue}Pt(o,t,({usage:e,hour:t,weekday:i,share:a})=>{n[t]=(n[t]??0)+e.totalTokens*a,r[i]=(r[i]??0)+e.totalTokens*a})&&(a=!0)}}let o=[W(`usage.mosaic.sun`),W(`usage.mosaic.mon`),W(`usage.mosaic.tue`),W(`usage.mosaic.wed`),W(`usage.mosaic.thu`),W(`usage.mosaic.fri`),W(`usage.mosaic.sat`)].map((e,t)=>({label:e,tokens:r[t]??0}));return{hasData:a,totalTokens:i,hourTotals:n,weekdayTotals:o}}function Gt(e,t,n,r){let a=Wt(e,t);if(!a.hasData)return G({title:W(`usage.mosaic.title`),description:W(`usage.mosaic.subtitleEmpty`),actions:N`
<div class="usage-mosaic-total">
${J(0)} ${i(W(`usage.metrics.tokens`))}
</div>
`},N`
<div class="usage-panel usage-mosaic">
<div class="usage-empty-block usage-empty-block--compact">
${W(`usage.mosaic.noTimelineData`)}
</div>
</div>
`);let o=Math.max(...a.hourTotals,1),s=Math.max(...a.weekdayTotals.map(e=>e.tokens),1);return G({title:W(`usage.mosaic.title`),description:W(`usage.mosaic.subtitle`,{zone:W(t===`utc`?`usage.filters.timeZoneUtc`:`usage.filters.timeZoneLocal`)}),actions:N`
<div class="usage-mosaic-total">
${J(a.totalTokens)}
${i(W(`usage.metrics.tokens`))}
</div>
`},N`
<div class="usage-panel usage-mosaic">
<div class="usage-mosaic-grid">
<div class="usage-mosaic-section">
<div class="usage-mosaic-section-title">${W(`usage.mosaic.dayOfWeek`)}</div>
<div class="usage-daypart-grid">
${a.weekdayTotals.map(e=>{let t=Math.min(e.tokens/s,1),n=e.tokens>0?`color-mix(in srgb, var(--accent) ${(12+t*60).toFixed(1)}%, transparent)`:`transparent`;return N`
<div class="usage-daypart-cell" style="background: ${n};">
<div class="usage-daypart-label">${e.label}</div>
<div class="usage-daypart-value">${J(e.tokens)}</div>
</div>
`})}
</div>
</div>
<div class="usage-mosaic-section">
<div class="usage-mosaic-section-title">
<span>${W(`usage.filters.hours`)}</span>
<span class="usage-mosaic-sub">0 → 23</span>
</div>
<div class="usage-hour-grid">
${a.hourTotals.map((e,t)=>{let a=Math.min(e/o,1),s=e>0?`color-mix(in srgb, var(--accent) ${(8+a*70).toFixed(1)}%, transparent)`:`transparent`,c=`${t}:00 · ${J(e)} ${i(W(`usage.metrics.tokens`))}`,l=a>.7?`color-mix(in srgb, var(--accent) 60%, transparent)`:`color-mix(in srgb, var(--accent) 24%, transparent)`,u=n.includes(t);return N`
<button
type="button"
class="usage-hour-cell ${u?`selected`:``}"
style="background: ${s}; border-color: ${l};"
title="${c}"
aria-label=${c}
aria-pressed=${u?`true`:`false`}
@click=${e=>r(t,e.shiftKey)}
></button>
`})}
</div>
<div class="usage-hour-labels">
<span>${W(`usage.mosaic.midnight`)}</span>
<span>${W(`usage.mosaic.fourAm`)}</span>
<span>${W(`usage.mosaic.eightAm`)}</span>
<span>${W(`usage.mosaic.noon`)}</span>
<span>${W(`usage.mosaic.fourPm`)}</span>
<span>${W(`usage.mosaic.eightPm`)}</span>
</div>
<div class="usage-hour-legend">
<span></span>
${W(`usage.mosaic.legend`)}
</div>
</div>
</div>
</div>
`)}function Kt(e){return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,`0`)}-${String(e.getDate()).padStart(2,`0`)}`}function qt(e){let t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);if(!t)return null;let[,n,r,i]=t,a=Number(n),o=Number(r)-1,s=Number(i),c=new Date(a,o,s);return Number.isNaN(c.valueOf())||c.getFullYear()!==a||c.getMonth()!==o||c.getDate()!==s?null:c}function Jt(e){let t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);if(!t)return null;let n=Number(t[1]),r=Number(t[2]),i=Number(t[3]),a=Date.UTC(n,r-1,i),o=new Date(a);return o.getUTCFullYear()!==n||o.getUTCMonth()!==r-1||o.getUTCDate()!==i?null:a/tn}function Yt(e){return new Date(e*tn).toISOString().slice(0,10)}function Xt(e){let t=qt(e);return t?t.toLocaleDateString(void 0,{month:`short`,day:`numeric`}):e}function Zt(e){let t=qt(e);return t?t.toLocaleDateString(void 0,{month:`long`,day:`numeric`,year:`numeric`}):e}function Qt(e,t,n){let r=Jt(t),i=Jt(n);if(r===null||i===null||r>i)return null;let a=yt();for(let t of e){let e=Jt(t.date);e!==null&&e>=r&&e<=i&&bt(a,t)}return{days:i-r+1,startDate:t,endDate:n,totals:a}}function $t(e,t,n,r=[1,7,30,90]){let i=Jt(t),a=Jt(n);if(i===null||a===null||i>a)return[];let o=a-i+1;return Array.from(new Set(r.map(e=>Math.max(1,Math.trunc(e))))).filter(e=>e<o).toSorted((e,t)=>e-t).map(t=>Qt(e,Yt(a-t+1),n)).filter(e=>e!==null)}var en,tn,nn,rn;function an(){return(an=e((()=>{A(),At(),ve(),U(),x(),en=4,tn=864e5,nn=(e,t)=>{if(e.length===0)return t??{messages:{total:0,user:0,assistant:0,toolCalls:0,toolResults:0,errors:0},tools:{totalCalls:0,uniqueTools:0,tools:[]},byModel:[],byProvider:[],byAgent:[],byChannel:[],daily:[]};let n=kt();for(let t of e)n.add(t);return n.finish()},rn=(e,t,n)=>{let r=0,i=0;for(let t of e){let e=t.usage?.durationMs??0;e>0&&(r+=e,i+=1)}let a=i?r/i:0,o=t&&r>0?t.totalTokens/(r/6e4):void 0,s=t&&r>0?t.totalCost/(r/6e4):void 0,c=n.messages.total?n.messages.errors/n.messages.total:0,l;for(let e of n.daily){if(e.messages<=0||e.errors<=0)continue;let t={date:e.date,errors:e.errors,messages:e.messages,rate:e.errors/e.messages};(!l||t.rate>l.rate||t.rate===l.rate&&t.errors>l.errors)&&(l=t)}return{durationSumMs:r,durationCount:i,avgDurationMs:a,throughputTokensPerMin:o,throughputCostPerMin:s,errorRate:c,peakErrorDay:l}}})))()}function on(e){return/^[ \t\r\n]*[=+\-@\uFF0B\uFF0D\uFF1D\uFF20]/u.test(e)?`'${e}`:e}function sn(e,t=!0){let n=t?on(e):e;return/[",\r\n]/.test(n)?`"${n.replaceAll(`"`,`""`)}"`:n}function cn(e){return e.map(e=>e==null?``:sn(String(e),typeof e==`string`)).join(`,`)}var ln,un,dn,fn,Y,pn,mn;function hn(){return(hn=e((()=>{l(),t(),q(),ln=e=>{let t=[cn([`key`,`label`,`agentId`,`channel`,`provider`,`model`,`updatedAt`,`durationMs`,`messages`,`errors`,`toolCalls`,`inputTokens`,`outputTokens`,`cacheReadTokens`,`cacheWriteTokens`,`totalTokens`,`totalCost`])];for(let n of e){let e=n.usage;t.push(cn([n.key,n.label??``,n.agentId??``,n.channel??``,n.modelProvider??n.providerOverride??``,n.model??n.modelOverride??``,r(n.updatedAt)??``,e?.durationMs??``,e?.messageCounts?.total??``,e?.messageCounts?.errors??``,e?.messageCounts?.toolCalls??``,e?.input??``,e?.output??``,e?.cacheRead??``,e?.cacheWrite??``,e?.totalTokens??``,e?.totalCost??``]))}return t.join(`
`)},un=e=>{let t=[cn([`date`,`inputTokens`,`outputTokens`,`cacheReadTokens`,`cacheWriteTokens`,`totalTokens`,`inputCost`,`outputCost`,`cacheReadCost`,`cacheWriteCost`,`totalCost`])];for(let n of e)t.push(cn([n.date,n.input,n.output,n.cacheRead,n.cacheWrite,n.totalTokens,n.inputCost??``,n.outputCost??``,n.cacheReadCost??``,n.cacheWriteCost??``,n.totalCost]));return t.join(`
`)},dn=(e,t,n)=>{let r=e.trim();if(!r)return[];let a=K(r).map(e=>e.raw).at(-1)??``,[s,c]=a.includes(`:`)?[a.slice(0,a.indexOf(`:`)),a.slice(a.indexOf(`:`)+1)]:[``,``],l=i(s),u=i(c),d=e=>o(e.filter(e=>!!e)),f=d(t.map(e=>e.agentId)).slice(0,6),p=d(t.map(e=>e.channel)).slice(0,6),m=d([...t.map(e=>e.modelProvider),...t.map(e=>e.providerOverride),...n?.byProvider.map(e=>e.provider)??[]]).slice(0,6),h=d([...t.map(e=>e.model),...n?.byModel.map(e=>e.model)??[]]).slice(0,6),g=d(n?.tools.tools.map(e=>e.name)??[]).slice(0,6);if(!l)return[{label:`agent:`,value:`agent:`},{label:`channel:`,value:`channel:`},{label:`provider:`,value:`provider:`},{label:`model:`,value:`model:`},{label:`tool:`,value:`tool:`},{label:`has:errors`,value:`has:errors`},{label:`has:tools`,value:`has:tools`},{label:`minTokens:`,value:`minTokens:`},{label:`maxCost:`,value:`maxCost:`}];let _=[],v=(e,t)=>{for(let n of t)(!u||i(n).includes(u))&&_.push({label:`${e}:${n}`,value:`${e}:${n}`})};switch(l){case`agent`:v(`agent`,f);break;case`channel`:v(`channel`,p);break;case`provider`:v(`provider`,m);break;case`model`:v(`model`,h);break;case`tool`:v(`tool`,g);break;case`has`:[`errors`,`tools`,`context`,`usage`,`model`,`provider`].forEach(e=>{(!u||e.includes(u))&&_.push({label:`has:${e}`,value:`has:${e}`})})}return _},fn=(e,t)=>{let n=e.trim();if(!n)return`${t} `;let r=K(n).map(e=>e.raw);return r[r.length-1]=t,`${r.join(` `)} `},Y=e=>i(e),pn=(e,t)=>{let n=K(e).map(e=>e.raw).filter(e=>e!==t);return n.length?`${n.join(` `)} `:``},mn=(e,t,n)=>{let r=Y(t),i=new Map(n.map(e=>[Y(e),e])),a=[];for(let t of K(e))(Y(t.key??``)!==r||i.delete(Y(t.value)))&&a.push(t.raw);let o=[...a,...Array.from(i.values(),e=>`${t}:${e}`)];return o.length?`${o.join(` `)} `:``}})))()}function gn(e,t,n){return{key:e,className:e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),labelKey:`usage.breakdown.${e}`,hintKey:t,short:n}}function _n(e,t){return t===0?0:e/t*100}function X(e){let t=Math.abs(e);return Mt(e,t===0||t>=.01?2:t>=1e-4?4:6)}function vn(e,t,n){(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),n(t,e.shiftKey))}function yn(e,t,n,r=`chart-toggle small`){return N`
<div class=${r}>
${n.map(({value:n,labelKey:r})=>N`
<button
class="btn btn--sm toggle-btn ${e===n?`active`:``}"
@click=${()=>t(n)}
>
${W(r)}
</button>
`)}
</div>
`}function bn(e,t,r,i,a,o,s,c){if(!(e.length>0||t.length>0||r.length>0))return F;let l=r.at(0)??``,u=r.length===1?i.find(e=>e.key===l):null,d=u?n(u.label||u.key,20)+((u.label||u.key).length>20?`…`:``):r.length===1?n(l,8)+`…`:W(`usage.filters.sessionsCount`,{count:String(r.length)}),f=u?u.label||u.key:r.length===1?l:r.join(`, `),p=e.length===1?e[0]:W(`usage.filters.daysCount`,{count:String(e.length)}),m=t.length===1?`${t[0]}:00`:W(`usage.filters.hoursCount`,{count:String(t.length)}),h=[{active:e.length>0,labelKey:`usage.filters.days`,value:p,removeKey:`usage.filters.removeDays`,onClear:a},{active:t.length>0,labelKey:`usage.filters.hours`,value:m,removeKey:`usage.filters.removeHours`,onClear:o},{active:r.length>0,labelKey:`usage.filters.session`,value:d,removeKey:`usage.filters.removeSession`,onClear:s,title:f}];return N`
<div class="active-filters">
${h.filter(({active:e})=>e).map(({labelKey:e,value:t,removeKey:n,onClear:r,title:i})=>N`
<div class="filter-chip" title=${L(i)}>
<span class="filter-chip-label">${W(e)}: ${t}</span>
<openclaw-tooltip .content=${W(`usage.filters.remove`)}>
<button class="filter-chip-remove" @click=${r} aria-label=${W(n)}>
×
</button>
</openclaw-tooltip>
</div>
`)}
${(e.length>0||t.length>0)&&r.length>0?N`
<button class="btn btn--sm" @click=${c}>
${W(`usage.filters.clearAll`)}
</button>
`:F}
</div>
`}function xn(e,t,n){let r=Qt(e,t,n);if(!r||e.length===0)return F;let i=$t(e,t,n),a=Kt(new Date),o=(e,t)=>e===1?t===a?W(`usage.presets.today`):Xt(t):W(`usage.costWindows.lastDays`,{count:String(e)}),s=[{label:W(`usage.costWindows.selectedRange`),summary:r,range:!0},...i.map(e=>({label:o(e.days,e.endDate),summary:e,range:!1}))];return N`
<section class="cost-window-analysis">
<div class="cost-window-header">
<div>
<div class="card-title usage-section-title">${W(`usage.costWindows.title`)}</div>
<div class="card-sub">
${W(`usage.costWindows.subtitle`,{date:Zt(n)})}
</div>
</div>
<div class="cost-window-range-label">
${Xt(t)} – ${Xt(n)}
</div>
</div>
<div class="cost-window-grid">
${s.map(({label:e,summary:t,range:n})=>{let r=t.totals.totalCost/t.days;return N`
<div class="cost-window-card ${n?`cost-window-card--range`:``}">
<div class="cost-window-card__label">${e}</div>
<div class="cost-window-card__value">
${X(t.totals.totalCost)}
</div>
<div class="cost-window-card__meta">
${J(t.totals.totalTokens)} ${W(`usage.metrics.tokens`)} ·
${X(r)} ${W(`usage.costWindows.perDay`)}
</div>
</div>
`})}
</div>
</section>
`}function Sn(e,t,n,r,a,o){if(!e.length)return N`
<div class="daily-chart-compact">
<div class="card-title usage-section-title">${W(`usage.daily.title`)}</div>
<div class="usage-empty-block">${W(`usage.empty.noData`)}</div>
</div>
`;let c=n===`tokens`,l=e.map(e=>c?e.totalTokens:e.totalCost),u=Math.max(...l,0),d=u>0?u:c?1:1e-4,f=l.filter(e=>e>0),p=d/(f.length>0?Math.min(...f):d)>50,m=l.map(e=>{if(e<=0)return 0;let t=p?Math.sqrt(e/d):e/d;return Math.max(6,t*200)}),h=e.length>30?12:e.length>20?18:e.length>14?24:32,g=e.length<=14,_=new Set(t);return N`
<div class="daily-chart-compact">
<div class="daily-chart-header">
${yn(r,a,[{value:`total`,labelKey:`usage.daily.total`},{value:`by-type`,labelKey:`usage.daily.byType`}],`chart-toggle small sessions-toggle`)}
<div class="card-title">
${W(c?`usage.daily.tokensTitle`:`usage.daily.costTitle`)}
${p?N`<span
class="daily-chart-scale-badge"
title=${W(`usage.daily.compressedScaleHint`)}
aria-label=${W(`usage.daily.compressedScaleHint`)}
>√</span
>`:F}
</div>
</div>
<div class="daily-chart">
<div class="daily-chart-plot">
<div class="daily-chart-scale" aria-hidden="true">
${(u>0?[u,u/(p?4:2),0]:[0]).map(e=>N`<span
>${c?J(e):e===0?Mt(0):X(e)}</span
>`)}
</div>
<div class="daily-chart-bars" style="--bar-max-width: ${h}px">
${e.map((t,n)=>{let a=s(m[n],`daily usage bar height`),l=_.has(t.date),u=Xt(t.date),d=e.length>20?String(Number.parseInt(t.date.slice(8),10)):u,f=e.length>20?`daily-bar-label daily-bar-label--compact`:`daily-bar-label`,p=r===`by-type`?Q.map(({key:e,className:n,labelKey:r})=>({value:c?t[e]:t[`${e}Cost`]??0,className:n,labelKey:r})):[],h=p.map(({value:e,labelKey:t})=>`${W(t)} ${c?J(e):X(e)}`),v=c?J(t.totalTokens):X(t.totalCost),y=Zt(t.date),b=`${J(t.totalTokens)} ${i(W(`usage.metrics.tokens`))}`.trim(),x=X(t.totalCost),S=p.reduce((e,t)=>e+t.value,0)||1;return N`
<openclaw-tooltip
.content=${[y,b,x,...h].join(`
`)}
>
<div
class="daily-bar-wrapper ${l?`selected`:``}"
role="button"
tabindex="0"
aria-pressed=${l?`true`:`false`}
aria-label=${`${y}: ${b}, ${x}`}
@keydown=${e=>vn(e,t.date,o)}
@click=${e=>o(t.date,e.shiftKey)}
>
${r===`by-type`?N`
<div
class="daily-bar daily-bar--stacked"
style="height: ${a.toFixed(0)}px;"
>
${p.map(({className:e,value:t})=>N`
<div
class="cost-segment ${e}"
style="height: ${t/S*100}%"
></div>
`)}
</div>
`:N`
<div class="daily-bar" style="height: ${a.toFixed(0)}px"></div>
`}
${g?N`<div class="daily-bar-total">${v}</div>`:N`<div
class="daily-bar-total daily-bar-total--placeholder"
aria-hidden="true"
></div>`}
<div class="${f}">${d}</div>
</div>
</openclaw-tooltip>
`})}
</div>
</div>
</div>
</div>
`}function Cn(e,t){let n=t===`tokens`,r=n?e.totalTokens||1:e.totalCost||0,i=Q.map(({key:t,className:i,labelKey:a})=>{let o=n?e[t]:e[`${t}Cost`]||0;return{className:i,labelKey:a,percentage:_n(o,r),formatted:n?J(o):X(o)}});return N`
<div class="cost-breakdown cost-breakdown-compact">
<div class="cost-breakdown-header">
${W(n?`usage.breakdown.tokensByType`:`usage.breakdown.costByType`)}
</div>
<div class="cost-breakdown-bar">
${i.map(({className:e,labelKey:t,percentage:n,formatted:r})=>N`
<div
class="cost-segment ${e}"
style="width: ${n.toFixed(1)}%"
title="${W(t)}: ${r}"
></div>
`)}
</div>
<div class="cost-breakdown-legend">
${i.map(({className:e,labelKey:t,formatted:n})=>N`
<span class="legend-item"
><span class="legend-dot ${e}"></span>${W(t)} ${n}</span
>
`)}
</div>
<div class="cost-breakdown-total">
${W(`usage.breakdown.total`)}:
${n?J(e.totalTokens):X(e.totalCost)}
</div>
</div>
`}function wn(e,t,n,r){let i=[`usage-insight-card`,r?.className].filter(Boolean).join(` `),a=[r?.error?`usage-error-list`:`usage-list`,r?.listClassName].filter(Boolean).join(` `);return N`
<div class=${i}>
<div class="usage-insight-title">${e}</div>
${t.length===0?N`<div class="muted">${n}</div>`:N`
<div class=${a}>
${t.map(e=>r?.error?N`
<div class="usage-error-row">
<div class="usage-error-date">${e.label}</div>
<div class="usage-error-rate">${e.value}</div>
${e.sub?N`<div class="usage-error-sub">${e.sub}</div>`:F}
</div>
`:N`
<div class="usage-list-item">
<span>${e.label}</span>
<span class="usage-list-value">
<span>${e.value}</span>
${e.sub?N`<span class="usage-list-sub">${e.sub}</span>`:F}
</span>
</div>
`)}
</div>
`}
</div>
`}function Tn(e){let t=e.currentTarget;t instanceof HTMLElement&&t.focus()}function Z(e){let t=`usage-summary-hint-${e.hintId}`,n=[`stat`,`usage-summary-card`,e.className,e.tone?`usage-summary-card--${e.tone}`:``].filter(Boolean).join(` `),r=[`stat-value`,`usage-summary-value`,e.tone??``,e.compactValue?`usage-summary-value--compact`:``].filter(Boolean).join(` `);return N`
<div class=${n}>
<div class="usage-summary-title">
${e.title}
<openclaw-tooltip open-on-click>
<button
id=${t}
type="button"
class="usage-summary-hint"
aria-label=${e.title}
@click=${Tn}
>
?
</button>
<!-- Shared tooltips dismiss pointer activation so action buttons never
strand one open. This hint exists only to be read, so it opts in to
click-to-open; the click handler still normalizes browsers that do
not focus buttons on pointer activation. -->
<span slot="content">${e.hint}</span>
</openclaw-tooltip>
</div>
<div class=${r}>${e.value}</div>
<div class="usage-summary-sub">${e.sub}</div>
</div>
`}function En(e,t,n,r,a,o,s,c){if(!e)return F;let l=t.messages.total?Math.round(e.totalTokens/t.messages.total):0,u=t.messages.total?e.totalCost/t.messages.total:0,d=e.input+e.cacheRead+e.cacheWrite,f=d>0?e.cacheRead/d:0,p=d>0?`${(f*100).toFixed(1)}%`:W(`usage.common.emptyValue`),m=n.errorRate*100,h=n.throughputTokensPerMin===void 0?W(`usage.common.emptyValue`):`${J(Math.round(n.throughputTokensPerMin))} ${W(`usage.overview.tokensPerMinute`)}`,g=n.throughputCostPerMin===void 0?W(`usage.common.emptyValue`):`${X(n.throughputCostPerMin)} ${W(`usage.overview.perMinute`)}`,_=n.durationCount>0?v(n.avgDurationMs)??W(`usage.common.emptyValue`):W(`usage.common.emptyValue`),y=t.daily.filter(e=>e.messages>0&&e.errors>0).map(e=>{let t=e.errors/e.messages;return{label:Xt(e.date),value:`${(t*100).toFixed(2)}%`,sub:`${e.errors} ${i(W(`usage.overview.errors`))} · ${e.messages} ${W(`usage.overview.messagesAbbrev`)} · ${J(e.tokens)}`,rate:t}}).toSorted((e,t)=>t.rate-e.rate).slice(0,5).map(({rate:e,...t})=>t),b=t=>a&&e.totalCost>0?W(`usage.overview.costShare`,{percent:(t/e.totalCost*100).toFixed(1)}):null,x=(e,t,n)=>[b(e),J(t),n===void 0?null:`${n} ${W(`usage.overview.messagesAbbrev`)}`].filter(e=>e!==null).join(` · `),S=t.byModel.slice(0,5).map(e=>({label:e.model??W(`usage.common.unknown`),value:X(e.totals.totalCost),sub:x(e.totals.totalCost,e.totals.totalTokens,e.count)})),C=t.byProvider.slice(0,5).map(e=>({label:e.provider??W(`usage.common.unknown`),value:X(e.totals.totalCost),sub:x(e.totals.totalCost,e.totals.totalTokens,e.count)})),w=t.tools.tools.slice(0,6).map(e=>({label:e.name,value:`${e.count}`,sub:W(`usage.overview.calls`)})),T=t.byAgent.slice(0,5).map(e=>({label:e.agentId,value:X(e.totals.totalCost),sub:x(e.totals.totalCost,e.totals.totalTokens)})),E=t.byChannel.slice(0,5).map(e=>({label:e.channel,value:X(e.totals.totalCost),sub:x(e.totals.totalCost,e.totals.totalTokens)})),D=[[`usage.overview.topModels`,S,`usage.overview.noModelData`],[`usage.overview.topProviders`,C,`usage.overview.noProviderData`],[`usage.overview.topTools`,w,`usage.overview.noToolCalls`],[`usage.overview.topAgents`,T,`usage.overview.noAgentData`],[`usage.overview.topChannels`,E,`usage.overview.noChannelData`]];return G({title:W(`usage.overview.title`)},N`
<section class="usage-panel usage-overview-card">
<div class="usage-overview-layout">
<div class="usage-summary-grid">
${Z({hintId:`messages`,title:W(`usage.overview.messages`),hint:W(`usage.overview.messagesHint`),value:t.messages.total,sub:`${t.messages.user} ${i(W(`usage.overview.user`))} · ${t.messages.assistant} ${i(W(`usage.overview.assistant`))}`,className:`usage-summary-card--hero`})}
${Z({hintId:`throughput`,title:W(`usage.overview.throughput`),hint:W(`usage.overview.throughputHint`),value:h,sub:g,className:`usage-summary-card--hero usage-summary-card--throughput`,compactValue:!0})}
${Z({hintId:`tool-calls`,title:W(`usage.overview.toolCalls`),hint:W(`usage.overview.toolCallsHint`),value:t.tools.totalCalls,sub:`${t.tools.uniqueTools} ${W(`usage.overview.toolsUsed`)}`,className:`usage-summary-card--half`})}
${Z({hintId:`average-tokens`,title:W(`usage.overview.avgTokens`),hint:W(`usage.overview.avgTokensHint`),value:J(l),sub:W(`usage.overview.acrossMessages`,{count:String(t.messages.total||0)}),className:`usage-summary-card--half`})}
${Z({hintId:`cache-hit-rate`,title:W(`usage.overview.cacheHitRate`),hint:W(`usage.overview.cacheHint`),value:p,sub:`${J(e.cacheRead)} ${W(`usage.overview.cached`)} · ${J(d)} ${W(`usage.overview.prompt`)}`,tone:f>.6?`good`:f>.3?`warn`:`bad`,className:`usage-summary-card--medium`})}
${Z({hintId:`error-rate`,title:W(`usage.overview.errorRate`),hint:W(`usage.overview.errorHint`),value:`${m.toFixed(2)}%`,sub:`${t.messages.errors} ${i(W(`usage.overview.errors`))} · ${_} ${W(`usage.overview.avgSession`)}`,tone:m>5?`bad`:m>1?`warn`:`good`,className:`usage-summary-card--medium`})}
${Z({hintId:`average-cost`,title:W(`usage.overview.avgCost`),hint:W(r?`usage.overview.avgCostHintMissing`:`usage.overview.avgCostHint`),value:X(u),sub:`${X(e.totalCost)} ${i(W(`usage.breakdown.total`))}`,className:`usage-summary-card--compact`})}
${Z({hintId:`sessions`,title:W(`usage.overview.sessions`),hint:W(`usage.overview.sessionsHint`),value:s,sub:W(`usage.overview.sessionsInRange`,{count:String(c)}),className:`usage-summary-card--compact`})}
${Z({hintId:`errors`,title:W(`usage.overview.errors`),hint:W(`usage.overview.errorsHint`),value:t.messages.errors,sub:`${t.messages.toolResults} ${W(`usage.overview.toolResults`)}`,className:`usage-summary-card--compact`})}
</div>
<div class="usage-insights-grid">
${D.map(([e,t,n])=>wn(W(e),t,W(n)))}
${wn(W(`usage.overview.peakErrorDays`),y,W(`usage.overview.noErrorData`),{error:!0})}
${wn(W(`usage.overview.peakErrorHours`),o,W(`usage.overview.noErrorData`),{error:!0,className:`usage-insight-card--wide`,listClassName:`usage-error-list--hours`})}
</div>
</div>
</section>
`)}function Dn(e,t,n,r,a,o,s,c,l,u,d,f,p,m,h){let g=e=>p.includes(e),_=e=>{let t=e.label||e.key;return t.startsWith(`agent:`)&&t.includes(`?token=`)?t.slice(0,t.indexOf(`?token=`)):t},y=e=>[g(`channel`)&&e.channel&&`channel:${e.channel}`,g(`agent`)&&e.agentId&&`agent:${e.agentId}`,g(`provider`)&&(e.modelProvider||e.providerOverride)&&`provider:${e.modelProvider??e.providerOverride}`,g(`model`)&&e.model&&`model:${e.model}`,g(`messages`)&&e.usage?.messageCounts&&`msgs:${e.usage.messageCounts.total}`,g(`tools`)&&e.usage?.toolUsage&&`tools:${e.usage.toolUsage.totalCalls}`,g(`errors`)&&e.usage?.messageCounts&&`errors:${e.usage.messageCounts.errors}`,g(`duration`)&&e.usage?.durationMs&&`dur:${v(e.usage.durationMs)??`—`}`].filter(e=>typeof e==`string`&&e.length>0),b=new Set(n),x=(e,t)=>{let n=e.usage;return n?b.size>0&&n.dailyBreakdown&&n.dailyBreakdown.length>0?n.dailyBreakdown.reduce((e,n)=>b.has(n.date)?e+(t===`tokens`?n.tokens:n.cost):e,0):t===`tokens`?n.totalTokens??0:n.totalCost??0:0},S=e=>x(e,r?`tokens`:`cost`),C=e=>{switch(a){case`recent`:return e.updatedAt??0;case`messages`:return e.usage?.messageCounts?.total??0;case`errors`:return e.usage?.messageCounts?.errors??0;case`cost`:return x(e,`cost`);case`tokens`:return x(e,`tokens`)}return a},w=[...e].toSorted((e,t)=>{let n=C(t)-C(e);if(n!==0)return n;let r=(t.updatedAt??0)-(e.updatedAt??0);return r===0?_(e).localeCompare(_(t)):r}),T=o===`asc`?w.toReversed():w,E=T.reduce((e,t)=>e+S(t),0),D=T.length?E/T.length:0,ee=T.reduce((e,t)=>e+(t.usage?.messageCounts?.errors??0),0),O=(e,t,n)=>{let i=S(e),a=_(e),o=y(e);return N`
<div
class="session-bar-row ${t?`selected`:``}"
@click=${t=>{t.target?.closest(`button`)||l(e.key,t.shiftKey,n)}}
title="${e.key}"
>
<button
type="button"
class="session-bar-selection"
aria-label=${a}
aria-pressed=${t?`true`:`false`}
@click=${t=>l(e.key,t.shiftKey,n)}
>
<span class="session-bar-label">
<span class="session-bar-title">${a}</span>
${o.length>0?N`<span class="session-bar-meta">${o.join(` · `)}</span>`:F}
</span>
</button>
<div class="session-bar-actions">
<button
type="button"
class="btn btn--sm btn--ghost"
@click=${t=>{t.stopPropagation(),se(t,_(e),W(`usage.sessions.copy`))}}
>
<span data-copy-label>${W(`usage.sessions.copy`)}</span>
</button>
<div class="session-bar-value">
${r?J(i):X(i)}
</div>
</div>
</div>
`},k=new Set(t),A=T.filter(e=>k.has(e.key)),j=A.length,M=new Map(T.map(e=>[e.key,e])),P=s.map(e=>M.get(e)).filter(e=>!!e),I=e=>{let t=e.map(e=>e.key);return e.map(e=>O(e,k.has(e.key),t))};return G({title:W(`usage.sessions.title`)},N`
<div class="usage-panel sessions-card">
<div class="sessions-card-header">
<div class="sessions-card-count">
${W(`usage.sessions.shown`,{count:String(e.length)})}
${m===e.length?``:` · ${W(`usage.sessions.total`,{count:String(m)})}`}
</div>
</div>
<div class="sessions-card-meta">
<div class="sessions-card-stats">
<span>
${r?J(D):X(D)}
${W(`usage.sessions.avg`)}
</span>
<span
>${ee} ${i(W(`usage.overview.errors`))}</span
>
</div>
${yn(c,f,[{value:`all`,labelKey:`usage.sessions.all`},{value:`recent`,labelKey:`usage.sessions.recent`}])}
<label class="sessions-sort">
<span>${W(`usage.sessions.sort`)}</span>
<select
class="settings-select"
@change=${e=>u(e.target.value)}
>
${Object.entries({cost:`usage.metrics.cost`,errors:`usage.overview.errors`,messages:`usage.overview.messages`,recent:`usage.sessions.recentShort`,tokens:`usage.metrics.tokens`}).map(([e,t])=>N`<option value=${e} ?selected=${a===e}>
${W(t)}
</option>`)}
</select>
</label>
<openclaw-tooltip
.content=${W(o===`desc`?`usage.sessions.descending`:`usage.sessions.ascending`)}
>
<button
class="btn btn--sm"
aria-label=${W(o===`desc`?`usage.sessions.descending`:`usage.sessions.ascending`)}
@click=${()=>d(o===`desc`?`asc`:`desc`)}
>
${o===`desc`?`↓`:`↑`}
</button>
</openclaw-tooltip>
${j>0?N`
<button class="btn btn--sm" @click=${h}>
${W(`usage.sessions.clearSelection`)}
</button>
`:F}
</div>
${c===`recent`?P.length===0?N` <div class="usage-empty-block">${W(`usage.sessions.noRecent`)}</div> `:N`
<div class="session-bars session-bars--recent">
${I(P)}
</div>
`:e.length===0?N` <div class="usage-empty-block">${W(`usage.sessions.noneInRange`)}</div> `:N`
<div class="session-bars">
${I(T.slice(0,50))}
${e.length>50?N`
<div class="usage-more-sessions">
${W(`usage.sessions.more`,{count:String(e.length-50)})}
</div>
`:F}
</div>
`}
${j>1?N`
<div class="sessions-selected-group">
<div class="sessions-card-count">
${W(`usage.sessions.selected`,{count:String(j)})}
</div>
<div class="session-bars session-bars--selected">
${I(A)}
</div>
</div>
`:F}
</div>
`)}var Q;function On(){return(On=e((()=>{c(),A(),j(),me(),ve(),U(),H(),x(),an(),Q=[gn(`output`,`usage.details.assistantOutputTokens`,`Out`),gn(`input`,`usage.details.userToolInputTokens`,`In`),gn(`cacheWrite`,`usage.details.tokensWrittenToCache`,`CW`),gn(`cacheRead`,`usage.details.tokensReadFromCache`,`CR`)]})))()}function kn(e,t){return t>0?e/t*100:0}function An(e){return e<0xe8d4a51000?e*1e3:e}function jn(e,t,n){let r=Number(e.slice(0,4)),i=Number(e.slice(5,7))-1,a=Number(e.slice(8,10))+n;return t===`utc`?Date.UTC(r,i,a):new Date(r,i,a).getTime()}function Mn(e,t){let n=new Date(e),r=t===`utc`?n.getUTCFullYear():n.getFullYear(),i=(t===`utc`?n.getUTCMonth():n.getMonth())+1,a=t===`utc`?n.getUTCDate():n.getDate();return`${r}-${String(i).padStart(2,`0`)}-${String(a).padStart(2,`0`)}`}function Nn(e,t,n){let r=Math.min(t,n),i=Math.max(t,n);return e.filter(e=>{if(e.timestamp<=0)return!0;let t=An(e.timestamp);return t>=r&&t<=i})}function Pn(e,t,n,r){return he({status:e,errorMessage:e.error?W(`usage.details.loadFailed`,{detail:i(W(n)),error:e.error}):void 0,onRetry:t,className:`usage-callout usage-detail-error--${r}`})}function Fn(e,t,n){let r=t||e.usage;if(!r)return N` <div class="usage-empty-block">${W(`usage.details.noUsageData`)}</div> `;let a=e=>e?p(e):W(`usage.common.emptyValue`),o=[e.channel&&`channel:${e.channel}`,e.agentId&&`agent:${e.agentId}`,(e.modelProvider||e.providerOverride)&&`provider:${e.modelProvider??e.providerOverride}`,e.model&&`model:${e.model}`].filter(Boolean),s=r.toolUsage?.tools.slice(0,6)??[],c;if(n){c=new Map;for(let e of n){let{tools:t}=We(e.content);for(let[e]of t)c.set(e,(c.get(e)||0)+1)}}let l=s.map(e=>({label:e.name,value:`${c?c.get(e.name)??0:e.count}`,sub:W(`usage.overview.calls`)})),u=c?[...c.values()].reduce((e,t)=>e+t,0):r.toolUsage?.totalCalls??0,d=c?c.size:r.toolUsage?.uniqueTools??0,f=r.modelUsage?.slice(0,6).map(e=>({label:e.model??W(`usage.common.unknown`),value:Mt(e.totals.totalCost),sub:J(e.totals.totalTokens)}))??[],m=[{labelKey:`usage.overview.messages`,value:r.messageCounts?.total??0,meta:N`${r.messageCounts?.user??0}
${i(W(`usage.overview.user`))} ·
${r.messageCounts?.assistant??0}
${i(W(`usage.overview.assistant`))}`},{labelKey:`usage.overview.toolCalls`,value:u,meta:N`${d} ${W(`usage.overview.toolsUsed`)}`},{labelKey:`usage.overview.errors`,value:r.messageCounts?.errors??0,meta:N`${r.messageCounts?.toolResults??0} ${W(`usage.overview.toolResults`)}`},{labelKey:`usage.details.duration`,value:v(r.durationMs)??W(`usage.common.emptyValue`),meta:N`${a(r.firstActivity)} → ${a(r.lastActivity)}`}];return N`
${o.length>0?N`<div class="usage-badges">
${o.map(e=>N`<span class="settings-row__value">${e}</span>`)}
</div>`:F}
<div class="session-summary-grid">
${m.map(({labelKey:e,value:t,meta:n})=>N`
<div class="stat session-summary-card">
<div class="session-summary-title">${W(e)}</div>
<div class="stat-value session-summary-value">${t}</div>
<div class="session-summary-meta">${n}</div>
</div>
`)}
</div>
<div class="usage-insights-grid usage-insights-grid--tight">
${wn(W(`usage.overview.topTools`),l,W(`usage.overview.noToolCalls`))}
${wn(W(`usage.details.modelMix`),f,W(`usage.overview.noModelData`))}
</div>
`}function In(e,t,n,r){let i=Math.min(n,r),a=Math.max(n,r),o=t.filter(e=>e.timestamp>=i&&e.timestamp<=a);if(o.length===0)return;let c=0,l=0,u=0,d=0,f={output:0,input:0,cacheWrite:0,cacheRead:0};for(let e of o){c+=e.totalTokens||0,l+=e.cost||0;for(let{key:t}of Q)f[t]+=e[t]||0;d+=+(e.output>0),u+=+(e.input>0)}let p=s(o[0],`filtered usage first point`),m=s(o.at(-1),`filtered usage last point`);return{...e,...f,totalTokens:c,totalCost:l,durationMs:m.timestamp-p.timestamp,firstActivity:p.timestamp,lastActivity:m.timestamp,messageCounts:{total:o.length,user:u,assistant:d,toolCalls:0,toolResults:0,errors:0}}}function Ln(e,t,r,a,o,s,c,l,u,d,f,p,m,h,g,_,v,y,b,x,S,C,w,T,E,D,ee,O,k,A,j,M,P){let I=e.label||e.key,L=I.length>50?n(I,50)+`…`:I,R=e.usage,z=d!==null&&f!==null,B=d!==null&&f!==null&&t?.points&&R?In(R,t.points,d,f):void 0,te=B?{totalTokens:B.totalTokens,totalCost:B.totalCost}:{totalTokens:R?.totalTokens??0,totalCost:R?.totalCost??0},V=B?W(`usage.details.filtered`):``;return N`
<div class="settings-group usage-panel session-detail-panel">
<div class="session-detail-header">
<div class="session-detail-header-left">
<div class="session-detail-title">
${L}
${V?N`<span class="session-detail-indicator">${V}</span>`:F}
</div>
</div>
<div class="session-detail-stats">
${R?N`
<span
><strong>${J(te.totalTokens)}</strong>
${i(W(`usage.metrics.tokens`))}${V}</span
>
<span
><strong>${Mt(te.totalCost)}</strong
>${V}</span
>
`:F}
</div>
<openclaw-tooltip .content=${W(`usage.details.close`)}>
<button
class="btn btn--sm btn--ghost"
@click=${P}
aria-label=${W(`usage.details.close`)}
>
×
</button>
</openclaw-tooltip>
</div>
${e.scope===`family`&&e.includedSessionIds?.length?N`
<div class="usage-lineage-note">
${W(`usage.scope.familyIncluded`,{count:String(e.includedSessionIds.length)})}
</div>
`:F}
<div class="session-detail-content">
${Fn(e,B,d!=null&&f!=null&&v?Nn(v,d,f):void 0)}
<div class="session-detail-row">
${Rn(t,r,a,o,s,c,l,u,m,h,g,_,d,f,p)}
</div>
<div class="session-detail-bottom">
${Bn(v,y,b,x,S,C,w,T,E,D,ee,O,z?d:null,z?f:null)}
${zn(k,A,R,j,M)}
</div>
</div>
</div>
`}function Rn(e,t,n,r,a,o,c,l,u,d,f,p=`local`,h,g,_){if(t&&!n.hasLoaded)return N`
<div class="session-timeseries-compact">
<div class="usage-empty-block">${W(`usage.loading.badge`)}</div>
</div>
`;let v=Pn(n,r,`usage.details.usageOverTime`,`timeline`);if(n.error&&!n.hasLoaded)return N`
<div class="session-timeseries-compact">
<div class="card-title usage-section-title">${W(`usage.details.usageOverTime`)}</div>
${v}
</div>
`;if(!e||e.points.length<2)return N`
<div class="session-timeseries-compact">
${v}
<div class="usage-empty-block">${W(`usage.details.noTimeline`)}</div>
</div>
`;let y=e.points;if(u||d||f&&f.length>0){let t=u?jn(u,p,0):0,n=d?jn(d,p,1):1/0,r=f?.length?new Set(f):void 0;y=e.points.filter(e=>e.timestamp<t||e.timestamp>=n?!1:!r||r.has(Mn(e.timestamp,p)))}if(y.length<2)return N`
<div class="session-timeseries-compact">
${v}
<div class="usage-empty-block">${W(`usage.details.noDataInRange`)}</div>
</div>
`;let b=0,x=0;y=y.map(e=>(b+=e.totalTokens,x+=e.cost,{...e,cumulativeTokens:b,cumulativeCost:x}));let S=h!=null&&g!=null,C=S?Math.min(h,g):0,w=S?Math.max(h,g):1/0,T=0,E=y.length;if(S){T=y.findIndex(e=>e.timestamp>=C),T===-1&&(T=y.length);let e=y.findIndex(e=>e.timestamp>w);E=e===-1?y.length:e}let D=S?y.slice(T,E):y,O={output:0,input:0,cacheRead:0,cacheWrite:0};for(let e of D)for(let{key:t}of Q)O[t]+=e[t];let k={top:8,right:4,bottom:14,left:30},A=400-k.left-k.right,j=100-k.top-k.bottom,P=a===`cumulative`,I=a===`per-turn`&&c===`by-type`,L=p===`utc`?{timeZone:`UTC`}:{},R=Object.values(O).reduce((e,t)=>e+t,0),z=y.map(e=>P?e.cumulativeTokens:I?e.input+e.output+e.cacheRead+e.cacheWrite:e.totalTokens),B=Math.max(...z,1),te=A/y.length,V=Math.min(Hn,Math.max(1,te*Vn)),H=te-V,U=k.left+T*(V+H),ne=E>=y.length?k.left+(y.length-1)*(V+H)+V:k.left+(E-1)*(V+H)+V;return N`
<div class="session-timeseries-compact">
<div class="timeseries-header-row">
<div class="card-title usage-section-title">${W(`usage.details.usageOverTime`)}</div>
<div class="timeseries-controls">
${S?N`
<div class="chart-toggle small">
<button
class="btn btn--sm toggle-btn active"
@click=${()=>_?.(null,null)}
>
${W(`usage.details.reset`)}
</button>
</div>
`:F}
${yn(a,o,[{value:`per-turn`,labelKey:`usage.details.perTurn`},{value:`cumulative`,labelKey:`usage.details.cumulative`}])}
${P?F:yn(c,l,[{value:`total`,labelKey:`usage.daily.total`},{value:`by-type`,labelKey:`usage.daily.byType`}])}
</div>
</div>
${v}
<div class="timeseries-chart-wrapper">
<svg viewBox="0 0 ${400} ${118}" class="timeseries-svg">
${[{x1:k.left,y1:k.top,x2:k.left,y2:k.top+j},{x1:k.left,y1:k.top+j,x2:400-k.right,y2:k.top+j}].map(({x1:e,y1:t,x2:n,y2:r})=>M`<line x1="${e}" y1="${t}" x2="${n}" y2="${r}" stroke="var(--border)" />`)}
${[{y:k.top+5,text:J(B)},{y:k.top+j,text:`0`}].map(({y:e,text:t})=>M`<text x="${k.left-4}" y="${e}" text-anchor="end" class="ts-axis-label">${t}</text>`)}
<!-- X axis labels (first and last) -->
${y.length>0?M`
<text x="${k.left}" y="${k.top+j+10}" text-anchor="start" class="ts-axis-label">${m(s(y[0],`time series first point`).timestamp,{hour:`2-digit`,minute:`2-digit`,...L},``)}</text>
<text x="${400-k.right}" y="${k.top+j+10}" text-anchor="end" class="ts-axis-label">${m(s(y.at(-1),`time series last point`).timestamp,{hour:`2-digit`,minute:`2-digit`,...L},``)}</text>
`:F}
<!-- Bars -->
${y.map((e,t)=>{let n=s(z[t],`time series bar total`),r=k.left+t*(V+H),a=n/B*j,o=k.top+j-a,c=[ee(e.timestamp,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`,...L},``),`${J(n)} ${i(W(`usage.metrics.tokens`))}`];I&&c.push(...Q.map(({key:t,short:n})=>`${n} ${J(e[t])}`));let l=c.join(` · `),u=S&&(t<T||t>=E);if(!I)return M`<rect x="${r}" y="${o}" width="${V}" height="${a}" class="ts-bar${u?` dimmed`:``}" rx="1" data-tooltip=${l} aria-label=${l}></rect>`;let d=k.top+j,f=u?` dimmed`:``;return M`
${Q.map(({key:t,className:i})=>{let o=e[t];if(o<=0||n<=0)return F;let s=o/n*a;return d-=s,M`<rect x="${r}" y="${d}" width="${V}" height="${s}" class="ts-bar ${i}${f}" rx="1" data-tooltip=${l} aria-label=${l}></rect>`})}
`})}
<!-- Selection highlight overlay (always visible between handles) -->
${M`
<rect
x="${U}"
y="${k.top}"
width="${Math.max(1,ne-U)}"
height="${j}"
fill="var(--accent)"
opacity="${Un}"
pointer-events="none"
/>
`}
${[U,ne].map(e=>M`
<line x1="${e}" y1="${k.top}" x2="${e}" y2="${k.top+j}" stroke="var(--accent)" stroke-width="0.8" opacity="0.7" />
<rect x="${e-Wn/2}" y="${k.top+j/2-Gn/2}" width="${Wn}" height="${Gn}" rx="1.5" fill="var(--accent)" class="cursor-handle" />
${[-.7,Kn].map(t=>M`<line x1="${e+t}" y1="${k.top+j/2-Gn/5}" x2="${e+t}" y2="${k.top+j/2+Gn/5}" stroke="var(--bg)" stroke-width="0.4" pointer-events="none" />`)}
`)}
</svg>
<!-- Handle drag zones (only on handles, not full chart) -->
${(()=>{let e=e=>t=>{if(!_)return;t.preventDefault(),t.stopPropagation();let n=t.currentTarget.closest(`.timeseries-chart-wrapper`)?.querySelector(`svg`);if(!n)return;let r=n.getBoundingClientRect(),i=r.width,a=k.left/400*i,o=(400-k.right)/400*i-a,c=e=>{let t=Math.max(0,Math.min(1,(e-r.left-a)/o));return Math.min(Math.floor(t*y.length),y.length-1)},l=e===`left`?U:ne,u=r.left+l/400*i,d=t.clientX-u;document.body.style.cursor=`col-resize`;let f=t=>{let n=t.clientX-d,r=c(n),i=y[r];if(!i)return;let a=e===`left`,o=a?g??s(y.at(-1),`time series right cursor point`).timestamp:h??s(y[0],`time series left cursor point`).timestamp;_(a?Math.min(i.timestamp,o):o,a?o:Math.max(i.timestamp,o))},p=()=>{document.body.style.cursor=``,document.removeEventListener(`mousemove`,f),document.removeEventListener(`mouseup`,p)};document.addEventListener(`mousemove`,f),document.addEventListener(`mouseup`,p)};return N`
${[`left`,`right`].map(t=>N`<div
class="chart-handle-zone chart-handle-${t}"
style="left: ${((t===`left`?U:ne)/400*100).toFixed(1)}%;"
@mousedown=${e(t)}
></div>`)}
`})()}
</div>
<div class="timeseries-summary">
${S?N`
<span class="timeseries-summary__range">
${W(`usage.details.turnRange`,{start:String(T+1),end:String(E),total:String(y.length)})}
</span>
·
${m(C,{hour:`2-digit`,minute:`2-digit`,...L},``)}–${m(w,{hour:`2-digit`,minute:`2-digit`,...L},``)}
· ${J(R)} ·
${Mt(D.reduce((e,t)=>e+(t.cost||0),0))}
`:N`${y.length} ${W(`usage.overview.messagesAbbrev`)} ·
${J(b)} · ${Mt(x)}`}
</div>
${I?N`
<div class="timeseries-breakdown">
<div class="card-title usage-section-title">
${W(`usage.breakdown.tokensByType`)}
</div>
<div class="cost-breakdown-bar cost-breakdown-bar--compact">
${Q.map(({key:e,className:t})=>N`
<div
class="cost-segment ${t}"
style="width: ${kn(O[e],R).toFixed(1)}%"
></div>
`)}
</div>
<div class="cost-breakdown-legend">
${Q.map(({key:e,className:t,labelKey:n,hintKey:r})=>N`
<div class="legend-item" title=${W(r)}>
<span class="legend-dot ${t}"></span>${W(n)}
${J(O[e])}
</div>
`)}
</div>
<div class="cost-breakdown-total">
${W(`usage.breakdown.total`)}: ${J(R)}
</div>
</div>
`:F}
</div>
`}function zn({weight:e,loading:t,status:n},r,i,a,o){let s=Pn(n,r,`usage.details.systemPromptBreakdown`,`context`);if(!e)return N`
<div class="context-details-panel">
${s}
${n.error?F:N`<div class="usage-empty-block">
${W(t?`usage.loading.badge`:`usage.details.noContextData`)}
</div>`}
</div>
`;let c=[{className:`skills`,labelKey:`usage.details.skills`,tokens:jt(e.skills.promptChars),entries:e.skills.entries.map(({name:e,blockChars:t})=>({name:e,chars:t}))},{className:`tools`,labelKey:`usage.details.tools`,tokens:jt(e.tools.listChars+e.tools.schemaChars),entries:e.tools.entries.map(({name:e,summaryChars:t,schemaChars:n})=>({name:e,chars:t+n}))},{className:`files`,labelKey:`usage.details.files`,tokens:jt(e.injectedWorkspaceFiles.reduce((e,t)=>t.injectionStatus===`native_unverified`?e:e+t.injectedChars,0)),entries:e.injectedWorkspaceFiles.map(({name:e,injectedChars:t})=>({name:e,chars:t}))}].map(({className:e,labelKey:t,tokens:n,entries:r})=>({className:e,labelKey:t,tokens:n,entries:r.toSorted((e,t)=>e.chars===null?t.chars===null?0:1:t.chars===null?-1:t.chars-e.chars)})),l=[{className:`system`,labelKey:`usage.details.system`,tokens:jt(e.systemPrompt.chars)},...c],u=l.reduce((e,{tokens:t})=>e+t,0),d=i&&i.totalTokens>0?i.input+i.cacheRead:0,f=d>0?`~${Math.min(u/d*100,100).toFixed(0)}% ${W(`usage.details.ofInput`)}`:W(`usage.details.baseContextPerMessage`),p=c.some(({entries:e})=>e.length>4);return N`
<div class="context-details-panel">
${s}
<div class="context-breakdown-header">
<div class="card-title usage-section-title">
${W(`usage.details.systemPromptBreakdown`)}
</div>
${p?N`<button class="btn btn--sm" @click=${o}>
${W(a?`usage.details.collapse`:`usage.details.expandAll`)}
</button>`:F}
</div>
<p class="context-weight-desc">${f}</p>
<div class="context-stacked-bar">
${l.map(({className:e,labelKey:t,tokens:n})=>N`
<div
class="context-segment ${e}"
style="width: ${kn(n,u).toFixed(1)}%"
title="${W(t)}: ~${J(n)}"
></div>
`)}
</div>
<div class="context-legend">
${l.map(({className:e,labelKey:t,tokens:n})=>N`
<span class="legend-item"
><span class="legend-dot ${e}"></span>${W(e===`system`?`usage.details.systemShort`:t)}
~${J(n)}</span
>
`)}
</div>
<div class="context-total">
${W(`usage.breakdown.total`)}: ~${J(u)}
</div>
<div class="context-breakdown-grid">
${c.filter(({entries:e})=>e.length>0).map(({labelKey:e,entries:t})=>{let n=a?t:t.slice(0,4),r=t.length-n.length;return N`
<div class="context-breakdown-card">
<div class="context-breakdown-title">${W(e)} (${t.length})</div>
<div class="context-breakdown-list">
${n.map(({name:e,chars:t})=>N`
<div class="context-breakdown-item">
<span class="mono" title=${e}>${e}</span>
<span class="muted"
>${t===null?W(`usage.common.unknown`):`~${J(jt(t))}`}</span
>
</div>
`)}
</div>
${r>0?N`
<div class="context-breakdown-more">
${W(`usage.sessions.more`,{count:String(r)})}
</div>
`:F}
</div>
`})}
</div>
</div>
`}function Bn(e,t,n,r,a,o,s,c,l,u,d,f,m,h){if(t&&!n.hasLoaded)return N`
<div class="session-logs-compact">
<div class="session-logs-header">${W(`usage.details.conversation`)}</div>
<div class="usage-empty-block">${W(`usage.loading.badge`)}</div>
</div>
`;let g=Pn(n,r,`usage.details.conversation`,`conversation`);if(n.error&&!n.hasLoaded)return N`
<div class="session-logs-compact">
<div class="session-logs-header">${W(`usage.details.conversation`)}</div>
${g}
</div>
`;if(!e||e.length===0)return N`
<div class="session-logs-compact">
<div class="session-logs-header">${W(`usage.details.conversation`)}</div>
${g}
<div class="usage-empty-block">${W(`usage.details.noMessages`)}</div>
</div>
`;let _=i(s.query),v=e.map(e=>{let t=We(e.content);return{log:e,toolInfo:t,cleanContent:t.cleanContent||e.content}}),y=Array.from(new Set(v.flatMap(e=>e.toolInfo.tools.map(([e])=>e)))).toSorted((e,t)=>e.localeCompare(t)),b=m!=null&&h!=null,x=b?Math.min(m,h):0,S=b?Math.max(m,h):1/0,C=v.filter(e=>{if(b&&e.log.timestamp>0){let t=An(e.log.timestamp);if(t<x||t>S)return!1}return(s.roles.length===0||s.roles.includes(e.log.role))&&(!s.hasTools||e.toolInfo.tools.length>0)&&(s.tools.length===0||e.toolInfo.tools.some(([e])=>s.tools.includes(e)))&&(!_||i(e.cleanContent).includes(_))}),w=s.roles.length>0||s.tools.length>0||s.hasTools||_||b?`${C.length} ${W(`usage.details.of`)} ${e.length}${b?` (${W(`usage.details.timelineFiltered`)})`:``}`:`${e.length}`,T=new Set(s.roles),E=new Set(s.tools);return N`
<div class="session-logs-compact">
<div class="session-logs-header">
<span>
${W(`usage.details.conversation`)}
<span class="session-logs-header-count">
(${w} ${i(W(`usage.overview.messages`))})
</span>
</span>
<button class="btn btn--sm" @click=${o}>
${W(a?`usage.details.collapseAll`:`usage.details.expandAll`)}
</button>
</div>
${g}
<div class="usage-filters-inline session-log-filters">
<select
multiple
size="4"
aria-label=${W(`usage.details.filterByRole`)}
@change=${e=>c(Array.from(e.target.selectedOptions).map(e=>e.value))}
>
${[[`user`,`usage.overview.user`],[`assistant`,`usage.overview.assistant`],[`tool`,`usage.details.tool`],[`toolResult`,`usage.details.toolResult`]].map(([e,t])=>N`<option value=${e} ?selected=${T.has(e)}>
${W(t)}
</option>`)}
</select>
<select
multiple
size="4"
aria-label=${W(`usage.details.filterByTool`)}
@change=${e=>l(Array.from(e.target.selectedOptions).map(e=>e.value))}
>
${y.map(e=>N`<option value=${e} ?selected=${E.has(e)}>${e}</option>`)}
</select>
<label class="usage-filters-inline session-log-has-tools">
<input
type="checkbox"
.checked=${s.hasTools}
@change=${e=>u(e.target.checked)}
/>
${W(`usage.details.hasTools`)}
</label>
<input
type="text"
placeholder=${W(`usage.details.searchConversation`)}
aria-label=${W(`usage.details.searchConversation`)}
.value=${s.query}
@input=${e=>d(e.target.value)}
/>
<button class="btn btn--sm" @click=${f}>${W(`usage.filters.clear`)}</button>
</div>
<div class="session-logs-list">
${C.map(e=>{let{log:t,toolInfo:n,cleanContent:r}=e,i=t.role===`user`?`user`:`assistant`,o=t.role===`user`?W(`usage.details.you`):t.role===`assistant`?W(`usage.overview.assistant`):W(`usage.details.tool`);return N`
<div class="session-log-entry ${i}">
<div class="session-log-meta">
<span class="session-log-role">${o}</span>
<span>${p(t.timestamp)}</span>
${t.tokens?N`<span>${J(t.tokens)}</span>`:F}
</div>
<div class="session-log-content">${r}</div>
${n.tools.length>0?N`
<details class="session-log-tools" ?open=${a}>
<summary>${n.summary}</summary>
<div class="session-log-tools-list">
${n.tools.map(([e,t])=>N`
<span class="session-log-tools-pill">${e} × ${t}</span>
`)}
</div>
</details>
`:F}
</div>
`})}
${C.length===0?N`
<div class="usage-empty-block usage-empty-block--compact">
${W(`usage.details.noMessagesMatch`)}
</div>
`:F}
</div>
</div>
`}var Vn,Hn,Un,Wn,Gn,Kn;function qn(){return(qn=e((()=>{c(),A(),ue(),U(),H(),x(),q(),an(),On(),Vn=.75,Hn=8,Un=.06,Wn=5,Gn=12,Kn=.7})))()}function Jn(e){return new Date(`${e}T12:00:00Z`).getTime()}function Yn(e){return new Date(e).toISOString().slice(0,10)}function Xn(e){let t=e.toSorted((e,t)=>e-t),n=e=>t[Math.min(t.length-1,Math.floor(t.length*e))]??0;return[n(.25),n(.5),n(.75)]}function Zn(e,t){return e<=0?0:e<t[0]?1:e<t[1]?2:e<t[2]?3:4}function Qn(e,t,n,r){let i=Jn(n),a=Math.max(Jn(t),i-363*$n),o=new Map(e.map(e=>[e.date,e.totalTokens])),s=e.filter(e=>{let t=Jn(e.date);return e.totalTokens>0&&t>=a&&t<=i}).map(e=>e.totalTokens),c=s.length>0?Xn(s):[0,0,0],l=a-new Date(a).getUTCDay()*$n,u=new Intl.DateTimeFormat(r,{month:`short`,timeZone:`UTC`}),d=[],f=[],p=-1;for(let e=l;e<=i;e+=7*$n){let t=[];for(let n=0;n<7;n+=1){let r=e+n*$n;if(r<a||r>i){t.push(null);continue}let s=Yn(r),l=o.get(s)??0;t.push({date:s,tokens:l,level:Zn(l,c)})}d.push({days:t});let r=Jn(t.find(e=>e!==null)?.date??n),s=new Date(r).getUTCMonth();f.push(s===p?``:u.format(new Date(r))),p=s}return{weeks:d,monthLabels:f}}var $n;function er(){return(er=e((()=>{$n=864e5})))()}function tr(e){let t=ar+e.weeks.length*ir,n=new Intl.NumberFormat(void 0,{maximumFractionDigits:0}),r=new Intl.DateTimeFormat(void 0,{weekday:`short`,timeZone:`UTC`});return N`
<svg
class="usage-heatmap__svg"
viewBox="0 0 ${t} ${116}"
style="--usage-heatmap-width: ${t}px"
role="img"
aria-label=${W(`usage.heatmap.title`)}
>
${e.monthLabels.map((e,t)=>e?M`<text class="usage-heatmap__month" x=${ar+t*ir} y="10">${e}</text>`:F)}
${sr.map(({row:e,utcDay:t})=>M`<text class="usage-heatmap__weekday" x=${24} y=${or+e*ir+rr-2}>${r.format(new Date(t))}</text>`)}
${e.weeks.map((e,t)=>e.days.map((e,r)=>{if(!e)return F;let i=`${Zt(e.date)} · ${W(`usage.heatmap.cellTokens`,{tokens:n.format(e.tokens)})}`;return M`
<rect
class="usage-heatmap__cell usage-heatmap__cell--l${e.level}"
x=${ar+t*ir}
y=${or+r*ir}
width=${rr}
height=${rr}
rx="2.5"
data-tooltip=${i}
aria-label=${i}
></rect>
`}))}
</svg>
`}function nr(e,t,n){if(e.length===0)return F;let r=Qn(e,t,n),i=N`
<div class="usage-heatmap__legend" aria-hidden="true">
<span>${W(`usage.heatmap.less`)}</span>
${[0,1,2,3,4].map(e=>N`<span class="usage-heatmap__swatch usage-heatmap__cell--l${e}"></span>`)}
<span>${W(`usage.heatmap.more`)}</span>
</div>
`;return G({title:W(`usage.heatmap.title`),description:W(`usage.heatmap.subtitle`),actions:i},N`<div class="usage-panel usage-heatmap">${tr(r)}</div>`)}var rr,ir,ar,or,sr;function cr(){return(cr=e((()=>{A(),ve(),U(),er(),an(),rr=11,ir=14,ar=30,or=18,sr=[{row:1,utcDay:Date.UTC(2024,0,1)},{row:3,utcDay:Date.UTC(2024,0,3)},{row:5,utcDay:Date.UTC(2024,0,5)}]})))()}function lr(e){return N`
<span class="settings-status settings-status--accent">
<span class="usage-loading-spinner" aria-hidden="true"></span>
${e}
</span>
`}function ur(e){return G({title:W(`usage.loading.title`),actions:lr(W(`usage.loading.badge`))},N`
<div class="usage-panel usage-loading-card">
<div class="usage-loading-header">
<div class="usage-loading-controls">
<div class="usage-date-range usage-date-range--loading">
<input class="usage-date-input" type="date" .value=${e.startDate} disabled />
<span class="usage-separator">${W(`usage.filters.to`)}</span>
<input class="usage-date-input" type="date" .value=${e.endDate} disabled />
</div>
</div>
</div>
<div class="usage-loading-grid">
<div class="skeleton usage-skeleton-block usage-skeleton-block--tall"></div>
<div class="skeleton usage-skeleton-block"></div>
<div class="skeleton usage-skeleton-block"></div>
</div>
</div>
`)}function dr(e){return N`
<section class="settings-group usage-panel usage-empty-state">
<div class="usage-empty-state__title">${W(`usage.empty.title`)}</div>
<div class="card-sub usage-empty-state__subtitle">${W(`usage.empty.subtitle`)}</div>
<div class="usage-empty-state__features">
<span class="usage-empty-state__feature">${W(`usage.empty.featureOverview`)}</span>
<span class="usage-empty-state__feature">${W(`usage.empty.featureSessions`)}</span>
<span class="usage-empty-state__feature">${W(`usage.empty.featureTimeline`)}</span>
</div>
<div class="usage-empty-state__actions">
<button class="btn primary" @click=${e}>${W(`common.refresh`)}</button>
</div>
</section>
`}function fr(e,t,n){let r=n?N`<div class="callout warning usage-callout">${W(`usage.providerUsage.stalled`)}</div>`:t?N`<div class="callout warning usage-callout">
${W(`usage.providerUsage.unavailable`)}
</div>`:F;return e.length===0?r:G({title:W(`usage.providerUsage.title`),count:e.length,description:W(`usage.providerUsage.subtitle`)},N`
${r}
<div class="usage-panel provider-usage-section">
<div class="provider-usage-grid">
${e.map(e=>N`
<article class="provider-usage-card">
<div class="provider-usage-card__header">
<div>
<div class="provider-usage-card__name">${e.displayName}</div>
<div class="provider-usage-card__id">${e.provider}</div>
</div>
${e.plan?N`<span class="provider-usage-plan">${e.plan}</span>`:F}
</div>
${Pe(e)}
</article>
`)}
</div>
</div>
`)}function pr(e){let{data:t,filters:n,display:r,detail:i,callbacks:a}=e,o=a.filters,s=a.display,c=a.details;if(t.loading&&!t.totals)return _e(N`<div class="usage-page">${ur(n)}</div>`,{wide:!0});let l=r.chartMode===`tokens`,u=n.query.trim().length>0,d=n.queryDraft.trim().length>0,f=new Set(n.selectedDays),p=new Set(n.selectedSessions),m=[...t.sessions].toSorted((e,t)=>{let n=l?e.usage?.totalTokens??0:e.usage?.totalCost??0;return(l?t.usage?.totalTokens??0:t.usage?.totalCost??0)-n}),h=n.agentId?m.filter(e=>Y(e.agentId??``)===Y(n.agentId??``)):m,g=n.selectedHours.length>0?h.filter(e=>Ut(e,n.selectedHours,n.timeZone)):h,_=ot(g,n.query),v=e=>f.size===0?!0:e.usage?.activityDates?.length?e.usage.activityDates.some(e=>f.has(e)):!!(e.updatedAt&&f.has(Mn(e.updatedAt,n.timeZone))),y=_.sessions.filter(v),b=_.warnings,x=dn(n.queryDraft,h,t.aggregates),S=K(n.queryDraft),C=e=>{let t=Y(e);return S.filter(e=>Y(e.key??``)===t).map(e=>e.value).filter(Boolean)},w=e=>{let t=new Set;for(let n of e)n&&t.add(n);return Array.from(t)},T=w(h.map(e=>e.channel)).slice(0,12),E=w([...h.map(e=>e.modelProvider),...h.map(e=>e.providerOverride),...t.aggregates?.byProvider.map(e=>e.provider)??[]]).slice(0,12),D=w([...h.map(e=>e.model),...t.aggregates?.byModel.map(e=>e.model)??[]]).slice(0,12),ee=w(t.aggregates?.tools.tools.map(e=>e.name)??[]).slice(0,12),O=n.selectedSessions.length===1?t.sessions.find(e=>e.key===n.selectedSessions[0])??y.find(e=>e.key===n.selectedSessions[0]):null,k=p.size?_.sessions.filter(e=>p.has(e.key)):_.sessions,A=k.filter(v),j=p.size>0||u||n.selectedHours.length>0||!!n.agentId,M=j||f.size>0,P=e=>{let t=yt();for(let n of e)n&&bt(t,n);return t},I=j?(()=>{let e=new Map;for(let t of k)for(let n of t.usage?.dailyBreakdown??[]){let t=e.get(n.date)??yt();bt(t,n),e.set(n.date,t)}return Array.from(e,([e,t])=>({date:e,...t})).toSorted((e,t)=>e.date.localeCompare(t.date))})():t.costDaily,L=f.size?P(I.filter(e=>f.has(e.date))):j?P(A.map(e=>e.usage)):t.totals,R=A.length,z=h.length,B=M?nn(A):nn([],t.aggregates),te=t.sessionsLimitReached&&!M,V=te?P(A.map(e=>e.usage)):L,H=te?nn(A):B,U=M?F:xn(t.costDaily,n.startDate,n.endDate),ne=rn(A,V,H),re=!t.loading&&!t.error&&t.sessions.length===0&&(t.totals?.totalTokens??0)===0,ie=(V?.missingCostEntries??0)>0||(V?V.totalTokens>0&&V.totalCost===0&&V.input+V.output+V.cacheRead+V.cacheWrite>0:!1),ae=[{label:W(`usage.presets.today`),days:1},{label:W(`usage.presets.last7d`),days:7},{label:W(`usage.presets.last30d`),days:30},{label:W(`usage.presets.last90d`),days:90},{label:W(`usage.presets.last1y`),days:365}],oe=e=>{let t=new Date,n=new Date;n.setDate(n.getDate()-(e-1)),o.onStartDateChange(Kt(n)),o.onEndDateChange(Kt(t))},se=()=>{o.onStartDateChange(`1970-01-01`),o.onEndDateChange(Kt(new Date))},ce=(e,t,r)=>{if(r.length===0)return F;let i=C(e),a=new Set(i.map(e=>Y(e))),s=r.length>0&&r.every(e=>a.has(Y(e))),c=i.length;return N`
<wa-dropdown
class="usage-filter-select"
placement="bottom-start"
@wa-select=${t=>{t.preventDefault();let a=t.detail.item.value;if(a===`command:select-all`){o.onQueryDraftChange(mn(n.queryDraft,e,r));return}if(a===`command:clear`){o.onQueryDraftChange(mn(n.queryDraft,e,[]));return}if(a?.startsWith(`option:`)){let r=decodeURIComponent(a.slice(7));o.onQueryDraftChange(mn(n.queryDraft,e,t.detail.item.checked?[...i,r]:i.filter(e=>Y(e)!==Y(r))))}}}
>
<button slot="trigger" type="button" class="usage-filter-trigger">
<span>${t}</span>
${c>0?N`<span class="settings-count">${c}</span>`:N` <span class="settings-count">${W(`usage.filters.all`)}</span> `}
</button>
<wa-dropdown-item value="command:select-all" ?disabled=${s}>
${W(`usage.filters.selectAll`)}
</wa-dropdown-item>
<wa-dropdown-item value="command:clear" ?disabled=${c===0}>
${W(`usage.filters.clear`)}
</wa-dropdown-item>
<div class="session-menu__separator" role="separator"></div>
${r.map(e=>{let t=a.has(Y(e));return N`
<wa-dropdown-item
class="usage-filter-option"
type="checkbox"
value=${`option:${encodeURIComponent(e)}`}
.checked=${t}
>
${e}
</wa-dropdown-item>
`})}
</wa-dropdown>
`},le=Kt(new Date);return _e(N`
<div class="usage-page">
<section class="settings-section">
<div class="settings-section__header">
<h2 class="settings-section__heading">${W(`usage.filters.title`)}</h2>
<div class="settings-section__actions">
${t.loading?lr(W(`usage.loading.badge`)):F}
${re?N`<span class="usage-query-hint">${W(`usage.empty.hint`)}</span>`:F}
</div>
</div>
<div
class="settings-group usage-panel usage-header ${r.headerPinned?`pinned`:``}"
>
<div class="usage-header-row">
<div class="usage-header-metrics">
${L?N`
<span class="usage-metric-badge">
<strong>${J(L.totalTokens)}</strong>
${W(`usage.metrics.tokens`)}
</span>
<span class="usage-metric-badge">
<strong>${Mt(L.totalCost)}</strong>
${W(`usage.metrics.cost`)}
</span>
<span class="usage-metric-badge">
<strong>${R}</strong>
${W(R===1?`usage.metrics.session`:`usage.metrics.sessions`)}
</span>
`:F}
<button
class="btn btn--sm usage-pin-btn ${r.headerPinned?`active`:``}"
@click=${o.onToggleHeaderPinned}
>
${r.headerPinned?W(`usage.filters.pinned`):W(`usage.filters.pin`)}
</button>
<wa-dropdown
class="usage-export-menu"
placement="bottom-end"
@wa-select=${e=>{switch(e.detail.item.value){case`sessions-csv`:pe(`openclaw-usage-sessions-${le}.csv`,ln(y),`text/csv;charset=utf-8`);break;case`daily-csv`:pe(`openclaw-usage-daily-${le}.csv`,un(I),`text/csv;charset=utf-8`);break;case`json`:s.onExportJson({totals:L,sessions:y,daily:I,aggregates:B});break;case void 0:}}}
>
<button
slot="trigger"
type="button"
class="btn btn--sm"
aria-busy=${t.exporting}
>
${t.exporting?W(`common.loading`):W(`usage.export.label`)} ▾
</button>
<wa-dropdown-item value="sessions-csv" ?disabled=${y.length===0}>
${W(`usage.export.sessionsCsv`)}
</wa-dropdown-item>
<wa-dropdown-item value="daily-csv" ?disabled=${I.length===0}>
${W(`usage.export.dailyCsv`)}
</wa-dropdown-item>
<wa-dropdown-item
value="json"
?disabled=${t.exporting||t.loading||y.length===0&&I.length===0}
>
${W(`usage.export.json`)}
</wa-dropdown-item>
</wa-dropdown>
</div>
</div>
<div class="usage-header-row">
<div class="usage-controls">
${bn(n.selectedDays,n.selectedHours,n.selectedSessions,t.sessions,o.onClearDays,o.onClearHours,o.onClearSessions,o.onClearFilters)}
<div class="usage-presets">
${ae.map(e=>N`
<button class="btn btn--sm" @click=${()=>oe(e.days)}>
${e.label}
</button>
`)}
<button class="btn btn--sm" @click=${se}>
${W(`usage.presets.all`)}
</button>
</div>
<div class="usage-date-range">
<input
class="usage-date-input"
type="date"
.value=${n.startDate}
title=${W(`usage.filters.startDate`)}
aria-label=${W(`usage.filters.startDate`)}
@change=${e=>o.onStartDateChange(e.target.value)}
/>
<span class="usage-separator">${W(`usage.filters.to`)}</span>
<input
class="usage-date-input"
type="date"
.value=${n.endDate}
title=${W(`usage.filters.endDate`)}
aria-label=${W(`usage.filters.endDate`)}
@change=${e=>o.onEndDateChange(e.target.value)}
/>
</div>
<select
class="usage-select"
title=${W(`usage.filters.timeZone`)}
aria-label=${W(`usage.filters.timeZone`)}
.value=${n.timeZone}
@change=${e=>o.onTimeZoneChange(e.target.value)}
>
<option value="local">${W(`usage.filters.timeZoneLocal`)}</option>
<option value="utc">${W(`usage.filters.timeZoneUtc`)}</option>
</select>
<div class="chart-toggle">
<button
class="btn btn--sm toggle-btn ${n.scope===`instance`?`active`:``}"
title=${W(`usage.scope.instanceHint`)}
@click=${()=>o.onScopeChange(`instance`)}
>
${W(`usage.scope.instance`)}
</button>
<button
class="btn btn--sm toggle-btn ${n.scope===`family`?`active`:``}"
title=${W(`usage.scope.familyHint`)}
@click=${()=>o.onScopeChange(`family`)}
>
${W(`usage.scope.family`)}
</button>
</div>
<div class="chart-toggle">
<button
class="btn btn--sm toggle-btn ${l?`active`:``}"
@click=${()=>s.onChartModeChange(`tokens`)}
>
${W(`usage.metrics.tokens`)}
</button>
<button
class="btn btn--sm toggle-btn ${l?``:`active`}"
@click=${()=>s.onChartModeChange(`cost`)}
>
${W(`usage.metrics.cost`)}
</button>
</div>
<button
class="btn btn--sm primary"
@click=${o.onRefresh}
?disabled=${t.loading}
>
${W(`common.refresh`)}
</button>
</div>
</div>
<div class="usage-query-section">
<div class="usage-query-bar">
<input
class="usage-query-input"
type="text"
.value=${n.queryDraft}
placeholder=${W(`usage.query.placeholder`)}
@input=${e=>o.onQueryDraftChange(e.target.value)}
@keydown=${e=>{e.key===`Enter`&&(e.preventDefault(),o.onApplyQuery())}}
/>
<div class="usage-query-actions">
<button
class="btn btn--sm"
@click=${o.onApplyQuery}
?disabled=${t.loading||!d&&!u}
>
${W(`usage.query.apply`)}
</button>
${d||u?N`
<button class="btn btn--sm" @click=${o.onClearQuery}>
${W(`usage.filters.clear`)}
</button>
`:F}
<span class="usage-query-hint">
${u?W(`usage.query.matching`,{shown:String(y.length),total:String(z)}):W(`usage.query.inRange`,{total:String(z)})}
</span>
</div>
</div>
<div class="usage-filter-row">
${ce(`channel`,W(`usage.filters.channel`),T)}
${ce(`provider`,W(`usage.filters.provider`),E)}
${ce(`model`,W(`usage.filters.model`),D)}
${ce(`tool`,W(`usage.filters.tool`),ee)}
<span class="usage-query-hint">${W(`usage.query.tip`)}</span>
</div>
${S.length>0?N`
<div class="usage-query-chips">
${S.map(e=>{let t=e.raw;return N`
<span class="usage-query-chip">
${t}
<openclaw-tooltip .content=${W(`usage.filters.remove`)}>
<button
aria-label=${W(`usage.filters.remove`)}
@click=${()=>o.onQueryDraftChange(pn(n.queryDraft,t))}
>
×
</button>
</openclaw-tooltip>
</span>
`})}
</div>
`:F}
${x.length>0?N`
<div class="usage-query-suggestions">
${x.map(e=>N`
<button
class="usage-query-suggestion"
@click=${()=>o.onQueryDraftChange(fn(n.queryDraft,e.value))}
>
${e.label}
</button>
`)}
</div>
`:F}
${b.length>0?N`
<div class="callout warning usage-callout usage-callout--tight">
${b.join(` · `)}
</div>
`:F}
</div>
${t.error?N`<div class="callout danger usage-callout">${t.error}</div>`:F}
${t.cacheRefresh===`complete`?F:N`
<div
class="callout warning usage-callout usage-cache-warning"
role="status"
aria-live="polite"
>
${W(t.cacheRefresh===`exhausted`?`usage.cacheStatus.paused`:`usage.cacheStatus.warning`)}
</div>
`}
${t.sessionsLimitReached?N`
<div class="callout warning usage-callout">
${W(`usage.sessions.limitReached`)}
</div>
`:F}
</div>
</section>
${fr(t.providerUsage,t.providerUsageUnavailable,t.providerUsageStalled)}
${re?dr(o.onRefresh):N`
${En(V,H,ne,ie,n.selectedDays.length===0,Ft(A,n.timeZone),R,z)}
${nr(I,n.startDate,n.endDate)}
${Gt(A,n.timeZone,n.selectedHours,o.onSelectHour)}
<div class="usage-grid">
<div class="usage-grid-column">
<div class="settings-group usage-panel usage-left-card">
${U}
${Sn(I,n.selectedDays,r.chartMode,r.dailyChartMode,s.onDailyChartModeChange,o.onSelectDay)}
${L?Cn(L,r.chartMode):F}
</div>
${Dn(y,n.selectedSessions,n.selectedDays,l,r.sessionSort,r.sessionSortDir,r.recentSessions,r.sessionsTab,c.onSelectSession,s.onSessionSortChange,s.onSessionSortDirChange,s.onSessionsTabChange,r.visibleColumns,z,o.onClearSessions)}
</div>
${O?N`<div class="usage-grid-column">
${Ln(O,i.timeSeries,i.timeSeriesLoading,i.timeSeriesStatus,c.onRetryTimeSeries,i.timeSeriesMode,c.onTimeSeriesModeChange,i.timeSeriesBreakdownMode,c.onTimeSeriesBreakdownChange,i.timeSeriesCursorStart,i.timeSeriesCursorEnd,c.onTimeSeriesCursorRangeChange,n.startDate,n.endDate,n.selectedDays,n.timeZone,i.sessionLogs,i.sessionLogsLoading,i.sessionLogsStatus,c.onRetrySessionLogs,i.sessionLogsExpanded,c.onToggleSessionLogsExpanded,i.logFilters,c.onLogFilterRolesChange,c.onLogFilterToolsChange,c.onLogFilterHasToolsChange,c.onLogFilterQueryChange,c.onLogFilterClear,i.context,c.onRetryContextWeight,r.contextExpanded,c.onToggleContextExpanded,o.onClearSessions)}
</div>`:F}
</div>
`}
</div>
`,{wide:!0})}function mr(){return(mr=e((()=>{A(),Me(),ve(),H(),ne(),U(),q(),an(),hn(),qn(),cr(),On()})))()}var $;function hr(){return(hr=e((()=>{d(),oe(),k(),z(),D(),b(),Ne(),xe(),_(),f(),dt(),mt(),q(),gt(),Ae(),Re(),vt(),mr(),$=class extends y{constructor(...e){super(...e),this.usageResult=null,this.usageCostSummary=null,this.providerUsageSummary=null,this.providerUsageUnavailable=!1,this.providerUsageIncomplete=!1,this.usageError=null,this.usageStartDate=Be(),this.usageEndDate=Be(),this.usageLoadStartDate=this.usageStartDate,this.usageLoadEndDate=this.usageEndDate,this.usageScope=`family`,this.usageAgentId=null,this.usageSelectedSessions=[],this.usageSelectedDays=[],this.usageSelectedHours=[],this.usageChartMode=`tokens`,this.usageDailyChartMode=`by-type`,this.usageTimeSeriesMode=`per-turn`,this.usageTimeSeriesBreakdownMode=`by-type`,this.usageTimeSeriesCursorStart=null,this.usageTimeSeriesCursorEnd=null,this.usageSessionLogsExpanded=!1,this.usageQuery=``,this.usageQueryDraft=``,this.usageSessionSort=`recent`,this.usageSessionSortDir=`desc`,this.usageRecentSessions=[],this.usageTimeZone=`local`,this.usageContextExpanded=!1,this.usageHeaderPinned=!1,this.usageSessionsTab=`all`,this.usageVisibleColumns=[..._t],this.usageLogFilterRoles=[],this.usageLogFilterTools=[],this.usageLogFilterHasTools=!1,this.usageLogFilterQuery=``,this.dateDebounceTimer=null,this.queryDebounceTimer=null,this.usageTaskActiveClient=null,this.connectionEpoch={},this.routeDataInitialized=!1,this.routeDataEnabled=!0,this.refreshPolicy=new je({isLoading:()=>this.usageLoading,reload:()=>(this.clearDateDebounce(),this.loadUsage()),onIncompleteUsageExhausted:()=>this.requestUpdate()}),this.gateway=new be(this,{getGateway:()=>this.context?.gateway,onIdentityChange:()=>this.resetForClientChange(),invalidateRequests:e=>{e.snapshot.phase!==`connected`&&(this.refreshPolicy.interrupt(),this.usageTaskActiveClient=null,this.usageTask.run(this.usageTaskArgs(null)),this.details.cancel(),this.usageExportTask.run([null]))},onSnapshot:e=>this.handleGatewaySnapshot(e),onPageActivation:()=>this.refreshPolicy.request(`focus`)}),this.observeAgentScope=fe(e=>{this.routeDataInitialized&&this.usageAgentId!==e&&(this.usageAgentId=e,this.clearSelectionsAndDetails(),this.resetProviderUsage(),this.refreshPolicy.request(`manual`)),this.requestUpdate()}),this.usageTask=new re(this,{autoRun:!1,args:()=>this.usageTaskArgs(),task:async([e,t,n,r,i,a],{signal:o})=>!e||this.routeDataEnabled?ae:(this.refreshPolicy.beginLoad(),{epoch:this.connectionEpoch,snapshot:await Le(e,{startDate:t,endDate:n,agentId:a||void 0,scope:r,timeZone:i},o)}),onComplete:e=>{this.usageTaskActiveClient=null;let t=e.snapshot;if(t.ok){this.usageResult=t.value.result,this.usageCostSummary=t.value.costSummary,this.usageError=null;let e=this.usageSelectedSessions.length===1?this.usageSelectedSessions[0]:void 0;e&&this.details.contextWeight.load(e)}else this.applyUsageError(t.error.cause);this.applyUsageLoadState(Ie(t),e.epoch,t.ok?void 0:null),this.refreshPolicy.flushPending()},onError:e=>{this.usageTaskActiveClient=null,this.applyUsageError(e),this.applyUsageLoadState({state:`pending`},this.connectionEpoch,null),this.refreshPolicy.flushPending()}}),this.usageExportTask=pt(this,this.gateway,()=>({startDate:this.usageLoadStartDate,endDate:this.usageLoadEndDate,scope:this.usageScope,timeZone:this.usageTimeZone,agentId:this.usageAgentId??void 0})),this.details=new ut(this,this.gateway,()=>({startDate:this.usageStartDate,endDate:this.usageEndDate,scope:this.usageScope,timeZone:this.usageTimeZone,agentId:this.usageAgentId??void 0}),()=>this.usageResult?.sessions??[]),this.subscriptions=new O(this).effect(()=>this.context?.agentSelection,e=>this.observeAgentScope(e)).watch(()=>this.context?.agents,(e,t)=>e.subscribe(t))}usageTaskArgs(e=this.gateway.connected?this.gateway.client:null){return[e,this.usageLoadStartDate,this.usageLoadEndDate,this.usageScope,this.usageTimeZone,i(this.usageAgentId??``)||null]}willUpdate(e){e.has(`routeData`)&&(this.applyRouteData(),this.ensureInitialData())}disconnectedCallback(){this.subscriptions.clear(),this.clearDateDebounce(),this.clearQueryDebounce(),this.refreshPolicy.dispose(),this.usageTaskActiveClient=null,this.usageTask.run(this.usageTaskArgs(null)),this.details.cancel(),this.usageExportTask.run([null]),super.disconnectedCallback()}applyRouteData(){let e=this.routeData;if(!e||(this.routeDataInitialized=!0,!this.routeDataEnabled))return;if(!this.gateway.isRouteDataCurrent(e)){this.routeDataEnabled=!1;return}let t=this.context.agentSelection.state.scopeId;if(e.query.agentId!==t){this.usageAgentId=t,this.clearSelectionsAndDetails(),this.resetProviderUsage(),this.refreshPolicy.request(`manual`);return}this.usageStartDate=e.query.startDate,this.usageEndDate=e.query.endDate,this.usageLoadStartDate=e.query.startDate,this.usageLoadEndDate=e.query.endDate,this.usageScope=e.query.scope,this.usageTimeZone=e.query.timeZone,this.usageAgentId=e.query.agentId,this.usageResult=e.result,this.usageCostSummary=e.costSummary,this.applyUsageLoadState(e.providerUsage,this.connectionEpoch,e.loadedAtMs),this.usageError=e.error}ensureInitialData(){this.routeDataEnabled||!this.routeDataInitialized||!this.gateway.client||!this.gateway.connected||this.usageLoading||this.loadUsage()}resetForClientChange(){this.clearDateDebounce(),this.usageTaskActiveClient=null,this.usageTask.run(this.usageTaskArgs(null)),this.routeDataInitialized&&(this.routeDataEnabled=!1),this.usageResult=null,this.usageCostSummary=null,this.resetProviderUsage(),this.usageError=null,this.usageAgentId=this.context.agentSelection.state.scopeId,this.clearSelectionsAndDetails()}resetProviderUsage(){this.providerUsageSummary=null,this.providerUsageUnavailable=!1,this.providerUsageIncomplete=!1,this.refreshPolicy.resetPayload()}applyUsageLoadState(e,t,n=Date.now()){if(e.state===`settled`){let t=e.result;this.providerUsageUnavailable=!t.ok,this.providerUsageIncomplete=!t.ok||Fe(t.value),t.ok&&!this.providerUsageIncomplete&&(this.providerUsageSummary=t.value)}let r=this.providerUsageIncomplete||this.usageCacheIncomplete;this.refreshPolicy.setLastLoadedAtMs(e.state===`pending`?null:n,{incomplete:r,connection:t})}get usageCacheIncomplete(){return ze(this.usageResult?.cacheStatus,this.usageCostSummary?.cacheStatus)}get providerUsageStalled(){return this.providerUsageIncomplete&&this.refreshPolicy.incompleteUsageExhausted}applyUsageError(e){let t=S(e);this.usageError=t?E(`usage`):Ve(e),t&&(this.usageResult=this.usageCostSummary=null)}get usageLoading(){return!this.routeDataInitialized||this.usageTaskActiveClient!==null}loadUsage(){let e=this.gateway.client;return!e||!this.gateway.connected?(this.refreshPolicy.markLoadDeferred(),Promise.resolve()):(this.routeDataEnabled=!1,this.usageLoadStartDate=this.usageStartDate,this.usageLoadEndDate=this.usageEndDate,this.usageError=null,this.usageTaskActiveClient=e,this.usageTask.run())}clearSelections(){this.usageSelectedDays=[],this.usageSelectedHours=[],this.usageSelectedSessions=[]}clearDetails(){this.details.clear(),this.usageTimeSeriesCursorStart=null,this.usageTimeSeriesCursorEnd=null}clearSelectionsAndDetails(){this.usageExportTask.run([null]),this.clearSelections(),this.clearDetails()}clearDateDebounce(){this.dateDebounceTimer!==null&&(window.clearTimeout(this.dateDebounceTimer),this.dateDebounceTimer=null)}scheduleUsageLoad(){this.clearDateDebounce(),this.refreshPolicy.resetPayload(),this.routeDataEnabled=!1,this.dateDebounceTimer=window.setTimeout(()=>{this.dateDebounceTimer=null,this.refreshPolicy.request(`manual`)},400)}handleGatewaySnapshot(e){if(!(!this.gateway.connected||!this.gateway.client)&&(this.context.agents.ensureList(),(e.identityChanged||e.becameConnected)&&(this.connectionEpoch={},this.routeDataInitialized))){this.refreshPolicy.request(`reconnect`);let e=this.usageSelectedSessions.length===1?this.usageSelectedSessions[0]:void 0;e&&!this.details.contextWeight.status.hasLoaded&&this.details.contextWeight.load(e)}}clearQueryDebounce(){this.queryDebounceTimer!==null&&(window.clearTimeout(this.queryDebounceTimer),this.queryDebounceTimer=null)}selectSession(e,t,n){if(this.clearDetails(),this.usageRecentSessions=[e,...this.usageRecentSessions.filter(t=>t!==e)].slice(0,8),this.usageSelectedSessions=Ue(this.usageSelectedSessions,e,n,t),this.usageSelectedSessions.length===1){let e=this.usageSelectedSessions[0];e&&this.details.load(e)}}render(){let e={data:{loading:this.usageLoading,exporting:this.usageExportTask.status===ie.PENDING,error:this.usageError,sessions:this.usageResult?.sessions??[],agents:this.context.agents.state.agentsList?.agents.map(e=>e.id).filter(Boolean)??[],sessionsLimitReached:(this.usageResult?.sessions.length??0)>=1e3,totals:this.usageResult?.totals??null,aggregates:this.usageResult?.aggregates??null,costDaily:this.usageCostSummary?.daily??[],cacheRefresh:this.usageCacheIncomplete?this.refreshPolicy.incompleteUsageExhausted?`exhausted`:`retrying`:`complete`,providerUsage:this.providerUsageSummary?.providers??[],providerUsageStalled:this.providerUsageStalled,providerUsageUnavailable:this.providerUsageUnavailable},filters:{startDate:this.usageStartDate,endDate:this.usageEndDate,scope:this.usageScope,selectedSessions:this.usageSelectedSessions,selectedDays:this.usageSelectedDays,selectedHours:this.usageSelectedHours,agentId:this.usageAgentId,query:this.usageQuery,queryDraft:this.usageQueryDraft,timeZone:this.usageTimeZone},display:{chartMode:this.usageChartMode,dailyChartMode:this.usageDailyChartMode,sessionSort:this.usageSessionSort,sessionSortDir:this.usageSessionSortDir,recentSessions:this.usageRecentSessions,sessionsTab:this.usageSessionsTab,visibleColumns:this.usageVisibleColumns,contextExpanded:this.usageContextExpanded,headerPinned:this.usageHeaderPinned},detail:{context:{weight:this.details.contextWeight.data,loading:this.details.contextWeight.loading,status:this.details.contextWeight.status},timeSeriesMode:this.usageTimeSeriesMode,timeSeriesBreakdownMode:this.usageTimeSeriesBreakdownMode,timeSeries:this.details.timeSeries.data,timeSeriesLoading:this.details.timeSeries.loading,timeSeriesStatus:this.details.timeSeries.status,timeSeriesCursorStart:this.usageTimeSeriesCursorStart,timeSeriesCursorEnd:this.usageTimeSeriesCursorEnd,sessionLogs:this.details.sessionLogs.data,sessionLogsLoading:this.details.sessionLogs.loading,sessionLogsStatus:this.details.sessionLogs.status,sessionLogsExpanded:this.usageSessionLogsExpanded,logFilters:{roles:this.usageLogFilterRoles,tools:this.usageLogFilterTools,hasTools:this.usageLogFilterHasTools,query:this.usageLogFilterQuery}},callbacks:{filters:{onStartDateChange:e=>{this.usageStartDate=e,this.clearSelectionsAndDetails(),this.scheduleUsageLoad()},onEndDateChange:e=>{this.usageEndDate=e,this.clearSelectionsAndDetails(),this.scheduleUsageLoad()},onScopeChange:e=>{this.usageScope=e,this.clearSelectionsAndDetails(),this.refreshPolicy.request(`manual`)},onAgentChange:e=>{this.context.agentSelection.setScope(e)},onRefresh:()=>this.refreshPolicy.request(`manual`),onTimeZoneChange:e=>{this.usageTimeZone=e,this.clearSelectionsAndDetails(),this.refreshPolicy.request(`manual`)},onToggleHeaderPinned:()=>this.usageHeaderPinned=!this.usageHeaderPinned,onSelectHour:(e,t)=>{this.usageSelectedHours=He(this.usageSelectedHours,e,Array.from({length:24},(e,t)=>t),t,!0)},onQueryDraftChange:e=>{this.usageQueryDraft=e,this.clearQueryDebounce(),this.queryDebounceTimer=window.setTimeout(()=>{this.usageQuery=this.usageQueryDraft,this.queryDebounceTimer=null},250)},onApplyQuery:()=>{this.clearQueryDebounce(),this.usageQuery=this.usageQueryDraft},onClearQuery:()=>{this.clearQueryDebounce(),this.usageQueryDraft=``,this.usageQuery=``},onSelectDay:(e,t)=>{this.usageSelectedDays=He(this.usageSelectedDays,e,(this.usageCostSummary?.daily??[]).map(e=>e.date),t,!1)},onClearDays:()=>this.usageSelectedDays=[],onClearHours:()=>this.usageSelectedHours=[],onClearSessions:()=>{this.usageSelectedSessions=[],this.clearDetails()},onClearFilters:()=>this.clearSelectionsAndDetails()},display:{onExportJson:e=>{this.usageExportTask.run([e])},onChartModeChange:e=>this.usageChartMode=e,onDailyChartModeChange:e=>this.usageDailyChartMode=e,onSessionSortChange:e=>this.usageSessionSort=e,onSessionSortDirChange:e=>this.usageSessionSortDir=e,onSessionsTabChange:e=>this.usageSessionsTab=e,onToggleColumn:e=>{this.usageVisibleColumns=this.usageVisibleColumns.includes(e)?this.usageVisibleColumns.filter(t=>t!==e):[...this.usageVisibleColumns,e]}},details:{onToggleContextExpanded:()=>this.usageContextExpanded=!this.usageContextExpanded,onToggleSessionLogsExpanded:()=>this.usageSessionLogsExpanded=!this.usageSessionLogsExpanded,onLogFilterRolesChange:e=>{this.usageLogFilterRoles=e},onLogFilterToolsChange:e=>{this.usageLogFilterTools=e},onLogFilterHasToolsChange:e=>{this.usageLogFilterHasTools=e},onLogFilterQueryChange:e=>{this.usageLogFilterQuery=e},onLogFilterClear:()=>{this.usageLogFilterRoles=[],this.usageLogFilterTools=[],this.usageLogFilterHasTools=!1,this.usageLogFilterQuery=``},onSelectSession:(e,t,n)=>this.selectSession(e,t,n),onTimeSeriesModeChange:e=>{this.usageTimeSeriesMode=e},onTimeSeriesBreakdownChange:e=>{this.usageTimeSeriesBreakdownMode=e},onTimeSeriesCursorRangeChange:(e,t)=>{this.usageTimeSeriesCursorStart=e,this.usageTimeSeriesCursorEnd=t},onRetryTimeSeries:()=>{let e=this.usageSelectedSessions[0];e&&this.details.timeSeries.load(e)},onRetrySessionLogs:()=>{let e=this.usageSelectedSessions[0];e&&this.details.sessionLogs.load(e)},onRetryContextWeight:()=>{let e=this.usageSelectedSessions[0];e&&this.details.contextWeight.load(e)}}}};return ht(this.context,this.usageResult,pr(e))}},u([a({context:te,subscribe:!0})],$.prototype,`context`,void 0),u([I({attribute:!1})],$.prototype,`routeData`,void 0),u([P()],$.prototype,`usageResult`,void 0),u([P()],$.prototype,`usageCostSummary`,void 0),u([P()],$.prototype,`providerUsageSummary`,void 0),u([P()],$.prototype,`providerUsageUnavailable`,void 0),u([P()],$.prototype,`providerUsageIncomplete`,void 0),u([P()],$.prototype,`usageError`,void 0),u([P()],$.prototype,`usageStartDate`,void 0),u([P()],$.prototype,`usageEndDate`,void 0),u([P()],$.prototype,`usageLoadStartDate`,void 0),u([P()],$.prototype,`usageLoadEndDate`,void 0),u([P()],$.prototype,`usageScope`,void 0),u([P()],$.prototype,`usageAgentId`,void 0),u([P()],$.prototype,`usageSelectedSessions`,void 0),u([P()],$.prototype,`usageSelectedDays`,void 0),u([P()],$.prototype,`usageSelectedHours`,void 0),u([P()],$.prototype,`usageChartMode`,void 0),u([P()],$.prototype,`usageDailyChartMode`,void 0),u([P()],$.prototype,`usageTimeSeriesMode`,void 0),u([P()],$.prototype,`usageTimeSeriesBreakdownMode`,void 0),u([P()],$.prototype,`usageTimeSeriesCursorStart`,void 0),u([P()],$.prototype,`usageTimeSeriesCursorEnd`,void 0),u([P()],$.prototype,`usageSessionLogsExpanded`,void 0),u([P()],$.prototype,`usageQuery`,void 0),u([P()],$.prototype,`usageQueryDraft`,void 0),u([P()],$.prototype,`usageSessionSort`,void 0),u([P()],$.prototype,`usageSessionSortDir`,void 0),u([P()],$.prototype,`usageRecentSessions`,void 0),u([P()],$.prototype,`usageTimeZone`,void 0),u([P()],$.prototype,`usageContextExpanded`,void 0),u([P()],$.prototype,`usageHeaderPinned`,void 0),u([P()],$.prototype,`usageSessionsTab`,void 0),u([P()],$.prototype,`usageVisibleColumns`,void 0),u([P()],$.prototype,`usageLogFilterRoles`,void 0),u([P()],$.prototype,`usageLogFilterTools`,void 0),u([P()],$.prototype,`usageLogFilterHasTools`,void 0),u([P()],$.prototype,`usageLogFilterQuery`,void 0),customElements.get(`openclaw-usage-page`)||customElements.define(`openclaw-usage-page`,$)})))()}hr();
//# sourceMappingURL=usage-page-BEvzd9y-.js.map