@agentstats/sdk 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- var AgentStatsSDK=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});let t=`agentstats:locationchange`,n=Symbol.for(`agentstats.patched`),r=`pdf.xlsx.docx.txt.rtf.csv.exe.key.pps.ppt.pptx.7z.pkg.rar.gz.zip.avi.mov.mp4.mpeg.wmv.midi.mp3.wav.wma.dmg.iso.msi`.split(`.`);function i(e){try{return document.cookie.match(RegExp(`(?:^|; )${e}=([\\w-]{8,64})`))?.[1]}catch{return}}function a(){if(typeof document>`u`||typeof navigator>`u`)return null;if(document.querySelector?.(`#claude-agent-stop-container`))return{name:`claude-in-chrome`,via:`dom`};let e=String(navigator.userAgent??``);return/\bClaude\/\d/.test(e)?{name:`claude-browser`,via:`user-agent`}:i(`_al_v`)?{name:`verified-visit`,via:`visit`}:navigator.webdriver===!0||/HeadlessChrome/.test(e)?{name:`automation`,via:`webdriver`}:null}function o(){if(!(typeof history>`u`))for(let e of[`pushState`,`replaceState`]){let r=history[e];if(typeof r!=`function`||r[n])continue;let i=function(...e){let n=r.apply(this,e);return window.dispatchEvent(new Event(t)),n};Object.defineProperty(i,n,{value:!0}),history[e]=i}}function s(e){let n={pageview(){},track(){},stop(){}};if(typeof window>`u`||typeof document>`u`)return n;let i=(...t)=>e.debug&&console.debug(`[agentstats]`,...t),s=/^localhost$|^127(\.\d+){3}$|^\[::1?\]$|\.local$|^$/.test(location.hostname)||location.protocol===`file:`,c=!1;try{c=localStorage.getItem(`agentstats_ignore`)===`true`}catch{}let l=(t,n)=>{if(c)return i(`ignored: agentstats_ignore is set`);if(s&&!e.allowLocalhost)return i(`ignored on localhost:`,t);let r=JSON.stringify({k:e.key,n:t,u:location.href,r:document.referrer||null,w:window.innerWidth,p:n,a:a()?.name,h:e.hashRouting?1:void 0});i(`send`,t,n??``);try{if(navigator.sendBeacon?.(e.endpoint,r))return}catch{}fetch(e.endpoint,{method:`POST`,body:r,keepalive:!0,headers:{"Content-Type":`text/plain`}}).catch(()=>{})},u=new AbortController,d={signal:u.signal},f=``,p=()=>{if(u.signal.aborted)return;let t=location.pathname+(e.hashRouting?location.hash:``);t!==f&&(f=t,l(`pageview`))};(()=>{if(document.visibilityState!==`prerender`)return p();document.addEventListener(`visibilitychange`,function e(){document.visibilityState!==`prerender`&&(document.removeEventListener(`visibilitychange`,e),p())},d)})(),o();let m=()=>setTimeout(p,0);if(window.addEventListener(t,m,d),window.addEventListener(`popstate`,m,d),e.hashRouting&&window.addEventListener(`hashchange`,m,d),e.outboundLinks||e.fileDownloads){let t=Array.isArray(e.fileDownloads)?e.fileDownloads:e.fileDownloads?r:[],n=n=>{if(n.type===`auxclick`&&n.button!==1)return;let r=n.target?.closest?.(`a[href]`);if(!r)return;let i;try{i=new URL(r.href,location.href)}catch{return}let a=i.pathname.split(`.`).pop()?.toLowerCase()??``;t.includes(a)?l(`File Download`,{url:i.href.split(`?`)[0]}):e.outboundLinks&&/^https?:$/.test(i.protocol)&&i.host!==location.host&&l(`Outbound Link: Click`,{url:i.href.split(`?`)[0]})};document.addEventListener(`click`,n,{capture:!0,signal:u.signal}),document.addEventListener(`auxclick`,n,{capture:!0,signal:u.signal})}return{pageview:p,track:(e,t)=>l(String(e),t),stop:()=>u.abort()}}let c=`0.4.1`;function l(){let e=globalThis.crypto;return e?.randomUUID?e.randomUUID():`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e===`x`?t:t&3|8).toString(16)})}function u(){return Date.now()}function d(){return typeof performance<`u`&&performance.now?performance.now():Date.now()}function f(e){return!!e&&typeof e==`object`&&e.name===`AbortError`}function p(e,t,n=0){if(n>6)return`[depth]`;if(Array.isArray(e))return e.slice(0,100).map(e=>p(e,t,n+1));if(e&&typeof e==`object`){let r={};for(let[i,a]of Object.entries(e)){let e=i.toLowerCase();r[i]=t.some(t=>e.includes(t))?`[redacted]`:p(a,t,n+1)}return r}return typeof e==`string`&&e.length>1e3?e.slice(0,1e3)+`…`:e}function m(e,t){let n;try{n=typeof e==`string`?e:JSON.stringify(e)}catch{n=String(e)}return n===void 0&&(n=``),n.length>t?n.slice(0,t)+`…`:n}function h(e){try{return JSON.stringify(e)?.length??0}catch{return 0}}function g(e){let t={name:String(e.name??``)};return e.title&&(t.title=String(e.title)),e.description&&(t.description=String(e.description)),e.inputSchema!==void 0&&(t.inputSchema=e.inputSchema),e.annotations&&(t.annotations={...e.annotations}),e.origin&&(t.origin=String(e.origin)),t}function _(){if(typeof document<`u`){let e=document.currentScript;if(e?.src)try{return new URL(`/api/ingest`,e.src).toString()}catch{}}return`https://agentstats.io/api/ingest`}let v=/(^|\.)(chatgpt\.com|chat\.openai\.com|perplexity\.ai|claude\.ai|gemini\.google\.com|bard\.google\.com|copilot\.microsoft\.com|copilot\.cloud\.microsoft|copilot\.com|meta\.ai|deepseek\.com|grok\.com|chat\.mistral\.ai|you\.com|phind\.com|poe\.com|duck\.ai|chat\.qwen\.ai|kimi\.com|kimi\.ai|doubao\.com)$/i,y=/[?&]utm_source=(chatgpt(\.com)?|openai|perplexity(\.ai)?|claude(\.ai)?|gemini|copilot(\.com)?|meta\.?ai|deepseek|grok|mistral|le-?chat)(&|$)/i,b=Symbol.for(`agentstats.patched`),x=Symbol.for(`agentstats.wrapped`);var S=class{constructor(e){if(this.queue=[],this.timer=null,this.snapshotTimer=null,this.pollTimer=null,this.sessionId=``,this.lastActivity=0,this.lastSnapshotHash=``,this.inflight=Promise.resolve(),this.web=null,this.agentPage=``,this.controlStarted=0,!e||!e.key)throw Error("[agentstats] `key` is required");if(this.options={endpoint:_(),captureArgs:!0,captureResults:!1,captureErrors:!0,redactKeys:[],sessionTimeoutMs:18e5,flushIntervalMs:2e3,maxBatchSize:25,autoInstrument:!0,trackAgentBrowsers:!0,trackPageviews:!0,debug:!1,disabled:!1,...Object.fromEntries(Object.entries(e).filter(([,e])=>e!==void 0))},this._fetch=e.fetch??(typeof fetch<`u`?fetch.bind(globalThis):(()=>Promise.reject(Error(`no fetch`)))),this.sessionId=this.loadSession(),!this.options.disabled&&(this.options.autoInstrument&&this.instrument(),this.bindLifecycle(),this.reportReferral(),this.options.trackAgentBrowsers&&this.watchAgentBrowsers(),this.options.trackPageviews)){let e=this.options;this.web=s({key:e.key,endpoint:`${e.endpoint.replace(/\/+$/,``)}/web`,hashRouting:e.hashRouting,outboundLinks:e.outboundLinks,fileDownloads:e.fileDownloads,allowLocalhost:e.allowLocalhost,debug:e.debug})}this.log(`initialised`,{endpoint:this.options.endpoint,session:this.sessionId})}wrap(e){if(!e||typeof e.execute!=`function`||e[x])return e;let t=e.execute,n=String(e.name),r=this,i=async function(e,i){let a=d(),o=i?.signal;try{let s=await t.call(this,e,i);return r.recordCall(n,`success`,d()-a,e,s,void 0,o),s}catch(t){let i=o?.aborted||f(t);throw r.recordCall(n,i?`cancelled`:`error`,d()-a,e,void 0,t,o),t}};Object.defineProperty(i,x,{value:!0});let a=Object.assign({},e,{execute:i});return Object.defineProperty(a,x,{value:!0}),a}track(e,t){let n=t?p(t,this.redactKeys()):void 0;this.enqueue({type:`custom`,id:l(),ts:u(),name:e,props:n,url:this.url()}),this.web?.track(e,n)}pageview(){this.web?.pageview()}async snapshot(){let e=this.modelContext();if(!e?.getTools)return[];let t=[];try{t=(await e.getTools()??[]).map(e=>g(e))}catch(e){return this.log(`getTools failed`,e),[]}let n=m(t.map(e=>[e.name,e.description,e.inputSchema,e.annotations]),1e7);return n!==this.lastSnapshotHash&&(this.lastSnapshotHash=n,this.enqueue({type:`tools_snapshot`,id:l(),ts:u(),tools:t,url:this.url()})),t}flush(e=!1){if(this.timer&&=(clearTimeout(this.timer),null),!this.queue.length)return this.inflight;let t=this.queue.splice(0,this.queue.length),n={key:this.options.key,sdk:`js/${c}`,session_id:this.sessionId,sent_at:u(),metadata:this.options.metadata,visit:typeof document<`u`?i(`_al_v`):void 0,client:this.clientHints(),events:t},r=JSON.stringify(n);if(this.log(`flush`,t.length,`events`),e&&typeof navigator<`u`&&navigator.sendBeacon)try{if(navigator.sendBeacon(this.options.endpoint,new Blob([r],{type:`text/plain`})))return Promise.resolve()}catch{}let a=this._fetch(this.options.endpoint,{method:`POST`,body:r,keepalive:!0,headers:{"Content-Type":`text/plain`}}).then(async e=>{e.ok||this.log(`ingest responded`,e.status)}).catch(e=>{this.log(`ingest failed`,e)});return this.inflight=a,a}destroy(){this.timer&&clearTimeout(this.timer),this.snapshotTimer&&clearTimeout(this.snapshotTimer),this.pollTimer&&clearInterval(this.pollTimer),this.timer=this.snapshotTimer=this.pollTimer=null,this.web?.stop(),this.web=null}get session(){return this.sessionId}log(...e){this.options.debug&&console.debug(`[agentstats]`,...e)}url(){return typeof location<`u`?location.href:void 0}redactKeys(){return(this.options.redactKeys??[]).map(e=>e.toLowerCase()).filter(Boolean)}modelContext(){let e=globalThis;return e.document?.modelContext??e.navigator?.modelContext}captureArgs(e,t){let n=this.options.captureArgs;if(n===!1)return;let r=typeof n==`function`?this.callOption(`captureArgs`,()=>n(e,t)):p(t,this.redactKeys());return r!==void 0&&h(r)>8192&&(r={_truncated:!0,preview:m(r,1e3)}),r}captureResult(e,t){let n=this.options.captureResults;if(!n||t===void 0)return;let r=typeof n==`function`?this.callOption(`captureResults`,()=>n(e,t)):t;return r===void 0?void 0:m(r,2048)}captureError(e,t){let n=this.options.captureErrors;if(n===!1)return;let r=t,i=typeof r?.message==`string`?r.message:m(t,1e3);if(typeof n!=`function`)return m(i,1e3);let a=typeof r?.code==`string`||typeof r?.code==`number`?r.code:null,o=this.callOption(`captureErrors`,()=>n({tool:e,message:i,name:typeof r?.name==`string`?r.name:null,code:a,error:t}));return typeof o==`string`&&o?m(o,1e3):void 0}callOption(e,t){try{return t()}catch(t){this.log(`${e} threw`,t);return}}recordCall(e,t,n,r,i,a,o){let s={type:`tool_call`,id:l(),ts:u(),tool:e,status:t,duration_ms:Math.round(n*100)/100,url:this.url()},c=this.captureArgs(e,r);c!==void 0&&(s.args=c);let d=this.captureResult(e,i);if(d!==void 0&&(s.result=d),a!==void 0){let t=this.captureError(e,a);t!==void 0&&(s.error=t);let n=a?.name;n&&(s.error_name=String(n).slice(0,100))}this.enqueue(s)}enqueue(e){this.options.disabled||(this.touchSession(),this.queue.push(e),this.log(`event`,e.type,e.tool??e.name??``),this.queue.length>=this.options.maxBatchSize?this.flush():this.timer||=setTimeout(()=>void this.flush(),this.options.flushIntervalMs))}scheduleSnapshot(e=250){this.snapshotTimer&&clearTimeout(this.snapshotTimer),this.snapshotTimer=setTimeout(()=>{this.snapshotTimer=null,this.snapshot()},e)}instrument(){let e=()=>{let e=this.modelContext();return e?(this.patch(e),!0):!1};if(e())return;let t=0;this.pollTimer=setInterval(()=>{t++,(e()||t>40)&&(this.pollTimer&&clearInterval(this.pollTimer),this.pollTimer=null)},250)}patch(e){if(e[b]){this.log(`modelContext already patched`),this.scheduleSnapshot(0);return}Object.defineProperty(e,b,{value:!0});let t=this;if(typeof e.registerTool==`function`){let n=e.registerTool;e.registerTool=function(e,r){let i=t.wrap(e);return t.enqueue({type:`tool_registered`,id:l(),ts:u(),tool:String(e?.name),descriptor:g(e),url:t.url()}),t.scheduleSnapshot(),n.call(this,i,r)}}if(typeof e.provideContext==`function`){let n=e.provideContext;e.provideContext=function(e){let r=(e?.tools??[]).map(e=>(t.enqueue({type:`tool_registered`,id:l(),ts:u(),tool:String(e?.name),descriptor:g(e),url:t.url()}),t.wrap(e)));return t.scheduleSnapshot(),n.call(this,{...e,tools:r})}}if(typeof e.unregisterTool==`function`){let n=e.unregisterTool;e.unregisterTool=function(e){return t.enqueue({type:`tool_unregistered`,id:l(),ts:u(),tool:String(e),url:t.url()}),t.scheduleSnapshot(),n.call(this,e)}}if(typeof e.clearContext==`function`){let n=e.clearContext;e.clearContext=function(){return t.scheduleSnapshot(),n.call(this)}}if(typeof e.addEventListener==`function`)try{e.addEventListener(`toolchange`,()=>t.scheduleSnapshot())}catch{}this.log(`modelContext patched`),this.scheduleSnapshot(0)}bindLifecycle(){if(typeof window>`u`||typeof window.addEventListener!=`function`)return;let e=()=>void this.flush(!0);window.addEventListener(`pagehide`,e),document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&e()})}clientHints(){if(typeof navigator>`u`)return;let e=String(navigator.userAgent??``).slice(0,1e3),t=navigator.webdriver===!0;return e||t?{ua:e||void 0,webdriver:t||void 0}:void 0}detectAgentBrowser(){return a()}watchAgentBrowsers(){if(typeof document>`u`||typeof window>`u`)return;let e=()=>{let e=this.detectAgentBrowser(),t=this.url();e&&t!==this.agentPage&&(this.agentPage=t??``,this.enqueue({type:`agent_pageview`,id:l(),ts:u(),name:e.name,props:{via:e.via},url:t}))},n=()=>{e(),!(typeof MutationObserver>`u`||!document.body)&&new MutationObserver(()=>{let t=!!document.querySelector(`#claude-agent-stop-container`);if(t&&!this.controlStarted)this.controlStarted=d(),this.enqueue({type:`agent_control`,id:l(),ts:u(),name:`claude-in-chrome`,props:{via:`dom`,phase:`start`},url:this.url()}),e();else if(!t&&this.controlStarted){let e=Math.round(d()-this.controlStarted);this.controlStarted=0,this.enqueue({type:`agent_control`,id:l(),ts:u(),name:`claude-in-chrome`,props:{via:`dom`,phase:`stop`,duration_ms:e},url:this.url()})}}).observe(document.body,{childList:!0})};document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,n,{once:!0}):n();let r=()=>setTimeout(e,0);window.addEventListener(`popstate`,r),window.addEventListener(t,r),o()}reportReferral(){if(typeof document>`u`||typeof location>`u`)return;let e=``;try{e=document.referrer?new URL(document.referrer).hostname.replace(/^www\./,``):``}catch{}if(!(e&&v.test(e))&&!y.test(location.search))return;let t=`__agentstats_ref_${this.sessionId}`;try{if(sessionStorage.getItem(t))return;sessionStorage.setItem(t,`1`)}catch{}this.enqueue({type:`referral`,id:l(),ts:u(),url:this.url()??location.href,referrer:e?`https://${e}/`:null})}loadSession(){try{let e=sessionStorage.getItem(`__agentstats_session`);if(e){let t=JSON.parse(e);if(t?.id&&u()-t.last<this.options.sessionTimeoutMs)return this.lastActivity=t.last,t.id}}catch{}return l()}touchSession(){let e=u();this.lastActivity&&e-this.lastActivity>this.options.sessionTimeoutMs&&(this.sessionId=l(),this.lastSnapshotHash=``,this.log(`new session`,this.sessionId)),this.lastActivity=e;try{sessionStorage.setItem(`__agentstats_session`,JSON.stringify({id:this.sessionId,last:e}))}catch{}}};let C=null;function w(e){return C&&C.destroy(),C=new S(e),C}function T(){return C}function E(e,t){C?.track(e,t)}function D(e){return C?C.wrap(e):e}function O(){return C?.flush()??Promise.resolve()}function k(){return C?.snapshot()??Promise.resolve([])}function A(){C?.pageview()}let j={init:w,track:E,pageview:A,wrap:D,flush:O,snapshot:k,getClient:T,version:c};window.AgentStats=j;let M=document.currentScript,N=M?.dataset.key,P=e=>e!==void 0&&e!==`false`,F=M?.dataset.fileDownloads;return N&&j.init({key:N,endpoint:M?.dataset.endpoint,debug:M?.dataset.debug===`true`,captureArgs:M?.dataset.captureArgs!==`false`,captureErrors:M?.dataset.captureErrors!==`false`,captureResults:M?.dataset.captureResults===`true`,redactKeys:M?.dataset.redactKeys?.split(`,`).map(e=>e.trim()).filter(Boolean),trackPageviews:M?.dataset.pageviews!==`false`,hashRouting:P(M?.dataset.hashRouting),outboundLinks:P(M?.dataset.outboundLinks),fileDownloads:F&&![``,`true`,`false`].includes(F)?F.split(`,`).map(e=>e.trim()):P(F),allowLocalhost:P(M?.dataset.allowLocalhost)}),e.AgentStats=j,e})({});
1
+ var AgentStatsSDK=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});let t=[10,40,100,250],n=[1,2,6,21],r=(e,t)=>{let n=0;for(;n<t.length&&e>=t[n];)n++;return n},i=e=>!!e&&(e.isContentEditable||e.tagName===`TEXTAREA`||e.tagName===`INPUT`&&/^(text|search|email|url|tel|password|number|)$/.test(e.type)),a=e=>!!e&&(e.isContentEditable||/^(INPUT|TEXTAREA|SELECT)$/.test(e.tagName)),o=e=>{try{return e.matches(`:autofill`)}catch{return!1}};function s(){let e={take:()=>null,stop(){}};if(typeof document>`u`||typeof performance>`u`)return e;let s=()=>performance.now(),c=()=>document.visibilityState===`hidden`||!document.hasFocus(),l=[],u=0,d=0,f=-1e9,p=-1e9,m=-1e9,h=-1e9,g=/* @__PURE__ */ new Map,_=()=>{l=Array.from({length:29},()=>0),l[0]=1,u=s(),d=0};_();let v=e=>{l[2]||(l[2]=Math.max(1,Math.round(e-u)))},y=new AbortController,b=(e,t)=>document.addEventListener(e,t,{capture:!0,passive:!0,signal:y.signal});b(`pointermove`,e=>{e.isTrusted&&(l[3]++,d++)}),b(`pointerdown`,e=>{e.isTrusted&&(m=s(),v(m))}),b(`click`,e=>{if(!e.isTrusted)return void l[26]++;v(s()),c()&&l[27]++,e.detail===0?l[10]++:e.pointerType===`touch`||e.pointerType===`pen`?l[9]++:e.detail===1&&l[4+r(d,n)]++,d=0}),b(`keydown`,e=>{if(!e.isTrusted)return void l[26]++;let n=s();v(n),m=p=n,c()&&l[27]++,e.repeat||e.target?.type===`password`||(n-f<2e3&&l[16+r(n-f,t)]++,f=n,g.set(e.code||e.key,n))}),b(`keyup`,e=>{let n=g.get(e.code||e.key);e.isTrusted&&n!==void 0&&(g.delete(e.code||e.key),l[11+r(s()-n,t)]++)});for(let e of[`paste`,`drop`,`compositionend`])b(e,()=>{p=s()});for(let e of[`wheel`,`touchstart`])b(e,()=>{m=s()});return b(`change`,e=>{!e.isTrusted&&a(e.target)&&l[28]++}),b(`input`,e=>{if(!e.isTrusted)return void(a(e.target)&&l[28]++);if(!i(e.target)||o(e.target))return;let t=s();v(t),c()&&l[27]++;let n=t-p;l[n<100?21:n<1e3?22:23]++}),b(`scroll`,()=>{let e=s();e-h>150&&(l[24]++,e-m>500&&l[25]++),h=e}),{take(){let e=l.slice(3).some(Boolean)?l:null;return e&&(e[1]=Math.min(2**31,Math.round(s()-u))),_(),e},stop:()=>y.abort()}}let c=`agentstats:locationchange`,l=Symbol.for(`agentstats.patched`),u=/* @__PURE__ */ `pdf.xlsx.docx.txt.rtf.csv.exe.key.pps.ppt.pptx.7z.pkg.rar.gz.zip.avi.mov.mp4.mpeg.wmv.midi.mp3.wav.wma.dmg.iso.msi`.split(`.`);function d(e){try{return document.cookie.match(RegExp(`(?:^|; )${e}=([\\w-]{8,64})`))?.[1]}catch{return}}function f(){if(typeof document>`u`||typeof navigator>`u`)return null;if(document.querySelector?.(`#claude-agent-stop-container`))return{name:`claude-in-chrome`,via:`dom`};let e=String(navigator.userAgent??``);return/\bClaude\/\d/.test(e)?{name:`claude-browser`,via:`user-agent`}:d(`_al_v`)?{name:`verified-visit`,via:`visit`}:navigator.webdriver===!0||/HeadlessChrome/.test(e)?{name:`automation`,via:`webdriver`}:null}function p(){if(typeof history<`u`)for(let e of[`pushState`,`replaceState`]){let t=history[e];if(typeof t!=`function`||t[l])continue;let n=function(...e){let n=t.apply(this,e);return window.dispatchEvent(new Event(c)),n};Object.defineProperty(n,l,{value:!0}),history[e]=n}}function m(e){let t={pageview(){},track(){},stop(){}};if(typeof window>`u`||typeof document>`u`)return t;let n=(...t)=>e.debug&&console.debug(`[agentstats]`,...t),r=/^localhost$|^127(\.\d+){3}$|^\[::1?\]$|\.local$|^$/.test(location.hostname)||location.protocol===`file:`,i=!1;try{i=localStorage.getItem(`agentstats_ignore`)===`true`}catch{}let a=(t,a)=>{if(i)return n(`ignored: agentstats_ignore is set`);if(r&&!e.allowLocalhost)return n(`ignored on localhost:`,t);let o=JSON.stringify({k:e.key,...a,a:f()?.name});n(`send`,t);try{if(navigator.sendBeacon?.(e.endpoint,o))return}catch{}fetch(e.endpoint,{method:`POST`,body:o,keepalive:!0,headers:{"Content-Type":`text/plain`}}).catch(()=>{})},o=(t,n)=>a(t,{n:t,u:location.href,r:document.referrer||null,w:window.innerWidth,p:n,h:e.hashRouting?1:void 0}),l=e.agentSignals===!1?null:s(),d=location.href,m=()=>{let e=l?.take();e&&a(`signals`,{t:`s`,u:d,s:e})},h=new AbortController,g={signal:h.signal},_=``,v=()=>{if(h.signal.aborted)return;let t=location.pathname+(e.hashRouting?location.hash:``);t!==_&&(_&&m(),_=t,d=location.href,o(`pageview`))};(()=>{if(document.visibilityState!==`prerender`)return v();document.addEventListener(`visibilitychange`,function e(){document.visibilityState!==`prerender`&&(document.removeEventListener(`visibilitychange`,e),v())},g)})(),p();let y=()=>setTimeout(v,0);if(window.addEventListener(c,y,g),window.addEventListener(`popstate`,y,g),e.hashRouting&&window.addEventListener(`hashchange`,y,g),e.outboundLinks||e.fileDownloads){let t=Array.isArray(e.fileDownloads)?e.fileDownloads:e.fileDownloads?u:[],n=n=>{if(n.type===`auxclick`&&n.button!==1)return;let r=n.target?.closest?.(`a[href]`);if(!r)return;let i;try{i=new URL(r.href,location.href)}catch{return}let a=i.pathname.split(`.`).pop()?.toLowerCase()??``;t.includes(a)?o(`File Download`,{url:i.href.split(`?`)[0]}):e.outboundLinks&&/^https?:$/.test(i.protocol)&&i.host!==location.host&&o(`Outbound Link: Click`,{url:i.href.split(`?`)[0]})};document.addEventListener(`click`,n,{capture:!0,signal:h.signal}),document.addEventListener(`auxclick`,n,{capture:!0,signal:h.signal})}return l&&(window.addEventListener(`pagehide`,m,g),document.addEventListener(`visibilitychange`,()=>document.visibilityState===`hidden`&&m(),g)),{pageview:v,track:(e,t)=>o(String(e),t),stop:()=>{h.signal.aborted||(m(),l?.stop(),h.abort())}}}let h=`0.5.1`;function g(){let e=globalThis.crypto;return e?.randomUUID?e.randomUUID():`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e===`x`?t:t&3|8).toString(16)})}function _(){return Date.now()}function v(){return typeof performance<`u`&&performance.now?performance.now():Date.now()}function y(e){return!!e&&typeof e==`object`&&e.name===`AbortError`}function b(e,t,n=0){if(n>6)return`[depth]`;if(Array.isArray(e))return e.slice(0,100).map(e=>b(e,t,n+1));if(e&&typeof e==`object`){let r={};for(let[i,a]of Object.entries(e)){let e=i.toLowerCase();r[i]=t.some(t=>e.includes(t))?`[redacted]`:b(a,t,n+1)}return r}return typeof e==`string`&&e.length>1e3?e.slice(0,1e3)+`…`:e}function x(e,t){let n;try{n=typeof e==`string`?e:JSON.stringify(e)}catch{n=String(e)}return n===void 0&&(n=``),n.length>t?n.slice(0,t)+`…`:n}function S(e){try{return JSON.stringify(e)?.length??0}catch{return 0}}function C(e){let t={name:String(e.name??``)};return e.title&&(t.title=String(e.title)),e.description&&(t.description=String(e.description)),e.inputSchema!==void 0&&(t.inputSchema=e.inputSchema),e.annotations&&(t.annotations={...e.annotations}),e.origin&&(t.origin=String(e.origin)),t}function w(){if(typeof document<`u`){let e=document.currentScript;if(e?.src)try{return new URL(`/api/ingest`,e.src).toString()}catch{}}return`https://agentstats.io/api/ingest`}let T=/(^|\.)(chatgpt\.com|chat\.openai\.com|perplexity\.ai|claude\.ai|gemini\.google\.com|bard\.google\.com|copilot\.microsoft\.com|copilot\.cloud\.microsoft|copilot\.com|meta\.ai|deepseek\.com|grok\.com|chat\.mistral\.ai|you\.com|phind\.com|poe\.com|duck\.ai|chat\.qwen\.ai|kimi\.com|kimi\.ai|doubao\.com)$/i,E=/[?&]utm_source=(chatgpt(\.com)?|openai|perplexity(\.ai)?|claude(\.ai)?|gemini|copilot(\.com)?|meta\.?ai|deepseek|grok|mistral|le-?chat)(&|$)/i,D=Symbol.for(`agentstats.patched`),O=Symbol.for(`agentstats.wrapped`);var k=class{constructor(e){if(this.queue=[],this.timer=null,this.snapshotTimer=null,this.pollTimer=null,this.sessionId=``,this.lastActivity=0,this.lastSnapshotHash=``,this.inflight=Promise.resolve(),this.web=null,this.agentPage=``,this.controlStarted=0,!e||!e.key)throw Error("[agentstats] `key` is required");if(this.options={endpoint:w(),captureArgs:!0,captureResults:!1,captureErrors:!0,redactKeys:[],sessionTimeoutMs:18e5,flushIntervalMs:2e3,maxBatchSize:25,autoInstrument:!0,trackAgentBrowsers:!0,trackPageviews:!0,debug:!1,disabled:!1,...Object.fromEntries(Object.entries(e).filter(([,e])=>e!==void 0))},this._fetch=e.fetch??(typeof fetch<`u`?fetch.bind(globalThis):(()=>Promise.reject(/* @__PURE__ */ Error(`no fetch`)))),this.sessionId=this.loadSession(),!this.options.disabled&&(this.options.autoInstrument&&this.instrument(),this.bindLifecycle(),this.reportReferral(),this.options.trackAgentBrowsers&&this.watchAgentBrowsers(),this.options.trackPageviews)){let e=this.options;this.web=m({key:e.key,endpoint:`${e.endpoint.replace(/\/+$/,``)}/web`,hashRouting:e.hashRouting,outboundLinks:e.outboundLinks,fileDownloads:e.fileDownloads,allowLocalhost:e.allowLocalhost,agentSignals:e.agentSignals,debug:e.debug})}this.log(`initialised`,{endpoint:this.options.endpoint,session:this.sessionId})}wrap(e){if(!e||typeof e.execute!=`function`||e[O])return e;let t=e.execute,n=String(e.name),r=this,i=async function(e,i){let a=v(),o=i?.signal;try{let s=await t.call(this,e,i);return r.recordCall(n,`success`,v()-a,e,s,void 0,o),s}catch(t){let i=o?.aborted||y(t);throw r.recordCall(n,i?`cancelled`:`error`,v()-a,e,void 0,t,o),t}};Object.defineProperty(i,O,{value:!0});let a=Object.assign({},e,{execute:i});return Object.defineProperty(a,O,{value:!0}),a}track(e,t){let n=t?b(t,this.redactKeys()):void 0;this.enqueue({type:`custom`,id:g(),ts:_(),name:e,props:n,url:this.url()}),this.web?.track(e,n)}pageview(){this.web?.pageview()}async snapshot(){let e=this.modelContext();if(!e?.getTools)return[];let t=[];try{t=(await e.getTools()??[]).map(e=>C(e))}catch(e){return this.log(`getTools failed`,e),[]}let n=x(t.map(e=>[e.name,e.description,e.inputSchema,e.annotations]),1e7);return n!==this.lastSnapshotHash&&(this.lastSnapshotHash=n,this.enqueue({type:`tools_snapshot`,id:g(),ts:_(),tools:t,url:this.url()})),t}flush(e=!1){if(this.timer&&=(clearTimeout(this.timer),null),!this.queue.length)return this.inflight;let t=this.queue.splice(0,this.queue.length),n={key:this.options.key,sdk:`js/${h}`,session_id:this.sessionId,sent_at:_(),metadata:this.options.metadata,visit:typeof document<`u`?d(`_al_v`):void 0,client:this.clientHints(),events:t},r=JSON.stringify(n);if(this.log(`flush`,t.length,`events`),e&&typeof navigator<`u`&&navigator.sendBeacon)try{if(navigator.sendBeacon(this.options.endpoint,new Blob([r],{type:`text/plain`})))return Promise.resolve()}catch{}let i=this._fetch(this.options.endpoint,{method:`POST`,body:r,keepalive:!0,headers:{"Content-Type":`text/plain`}}).then(async e=>{e.ok||this.log(`ingest responded`,e.status)}).catch(e=>{this.log(`ingest failed`,e)});return this.inflight=i,i}destroy(){this.timer&&clearTimeout(this.timer),this.snapshotTimer&&clearTimeout(this.snapshotTimer),this.pollTimer&&clearInterval(this.pollTimer),this.timer=this.snapshotTimer=this.pollTimer=null,this.web?.stop(),this.web=null}get session(){return this.sessionId}log(...e){this.options.debug&&console.debug(`[agentstats]`,...e)}url(){return typeof location<`u`?location.href:void 0}redactKeys(){return(this.options.redactKeys??[]).map(e=>e.toLowerCase()).filter(Boolean)}modelContext(){let e=globalThis;return e.document?.modelContext??e.navigator?.modelContext}captureArgs(e,t){let n=this.options.captureArgs;if(n===!1)return;let r=typeof n==`function`?this.callOption(`captureArgs`,()=>n(e,t)):b(t,this.redactKeys());return r!==void 0&&S(r)>8192&&(r={_truncated:!0,preview:x(r,1e3)}),r}captureResult(e,t){let n=this.options.captureResults;if(!n||t===void 0)return;let r=typeof n==`function`?this.callOption(`captureResults`,()=>n(e,t)):t;return r===void 0?void 0:x(r,2048)}captureError(e,t){let n=this.options.captureErrors;if(n===!1)return;let r=t,i=typeof r?.message==`string`?r.message:x(t,1e3);if(typeof n!=`function`)return x(i,1e3);let a=typeof r?.code==`string`||typeof r?.code==`number`?r.code:null,o=this.callOption(`captureErrors`,()=>n({tool:e,message:i,name:typeof r?.name==`string`?r.name:null,code:a,error:t}));return typeof o==`string`&&o?x(o,1e3):void 0}callOption(e,t){try{return t()}catch(t){this.log(`${e} threw`,t);return}}recordCall(e,t,n,r,i,a,o){let s={type:`tool_call`,id:g(),ts:_(),tool:e,status:t,duration_ms:Math.round(n*100)/100,url:this.url()},c=this.captureArgs(e,r);c!==void 0&&(s.args=c);let l=this.captureResult(e,i);if(l!==void 0&&(s.result=l),a!==void 0){let t=this.captureError(e,a);t!==void 0&&(s.error=t);let n=a?.name;n&&(s.error_name=String(n).slice(0,100))}this.enqueue(s)}enqueue(e){this.options.disabled||(this.touchSession(),this.queue.push(e),this.log(`event`,e.type,e.tool??e.name??``),this.queue.length>=this.options.maxBatchSize?this.flush():this.timer||=setTimeout(()=>void this.flush(),this.options.flushIntervalMs))}scheduleSnapshot(e=250){this.snapshotTimer&&clearTimeout(this.snapshotTimer),this.snapshotTimer=setTimeout(()=>{this.snapshotTimer=null,this.snapshot()},e)}instrument(){let e=()=>{let e=this.modelContext();return e?(this.patch(e),!0):!1};if(e())return;let t=0;this.pollTimer=setInterval(()=>{t++,(e()||t>40)&&(this.pollTimer&&clearInterval(this.pollTimer),this.pollTimer=null)},250)}patch(e){if(e[D]){this.log(`modelContext already patched`),this.scheduleSnapshot(0);return}Object.defineProperty(e,D,{value:!0});let t=this;if(typeof e.registerTool==`function`){let n=e.registerTool;e.registerTool=function(e,r){let i=t.wrap(e);return t.enqueue({type:`tool_registered`,id:g(),ts:_(),tool:String(e?.name),descriptor:C(e),url:t.url()}),t.scheduleSnapshot(),n.call(this,i,r)}}if(typeof e.provideContext==`function`){let n=e.provideContext;e.provideContext=function(e){let r=(e?.tools??[]).map(e=>(t.enqueue({type:`tool_registered`,id:g(),ts:_(),tool:String(e?.name),descriptor:C(e),url:t.url()}),t.wrap(e)));return t.scheduleSnapshot(),n.call(this,{...e,tools:r})}}if(typeof e.unregisterTool==`function`){let n=e.unregisterTool;e.unregisterTool=function(e){return t.enqueue({type:`tool_unregistered`,id:g(),ts:_(),tool:String(e),url:t.url()}),t.scheduleSnapshot(),n.call(this,e)}}if(typeof e.clearContext==`function`){let n=e.clearContext;e.clearContext=function(){return t.scheduleSnapshot(),n.call(this)}}if(typeof e.addEventListener==`function`)try{e.addEventListener(`toolchange`,()=>t.scheduleSnapshot())}catch{}this.log(`modelContext patched`),this.scheduleSnapshot(0)}bindLifecycle(){if(typeof window>`u`||typeof window.addEventListener!=`function`)return;let e=()=>void this.flush(!0);window.addEventListener(`pagehide`,e),document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&e()})}clientHints(){if(typeof navigator>`u`)return;let e=String(navigator.userAgent??``).slice(0,1e3),t=navigator.webdriver===!0;return e||t?{ua:e||void 0,webdriver:t||void 0}:void 0}detectAgentBrowser(){return f()}watchAgentBrowsers(){if(typeof document>`u`||typeof window>`u`)return;let e=()=>{let e=this.detectAgentBrowser(),t=this.url();e&&t!==this.agentPage&&(this.agentPage=t??``,this.enqueue({type:`agent_pageview`,id:g(),ts:_(),name:e.name,props:{via:e.via},url:t}))},t=()=>{e(),typeof MutationObserver<`u`&&document.body&&new MutationObserver(()=>{let t=!!document.querySelector(`#claude-agent-stop-container`);if(t&&!this.controlStarted)this.controlStarted=v(),this.enqueue({type:`agent_control`,id:g(),ts:_(),name:`claude-in-chrome`,props:{via:`dom`,phase:`start`},url:this.url()}),e();else if(!t&&this.controlStarted){let e=Math.round(v()-this.controlStarted);this.controlStarted=0,this.enqueue({type:`agent_control`,id:g(),ts:_(),name:`claude-in-chrome`,props:{via:`dom`,phase:`stop`,duration_ms:e},url:this.url()})}}).observe(document.body,{childList:!0})};document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,t,{once:!0}):t();let n=()=>setTimeout(e,0);window.addEventListener(`popstate`,n),window.addEventListener(c,n),p()}reportReferral(){if(typeof document>`u`||typeof location>`u`)return;let e=``;try{e=document.referrer?new URL(document.referrer).hostname.replace(/^www\./,``):``}catch{}if(!(e&&T.test(e))&&!E.test(location.search))return;let t=`__agentstats_ref_${this.sessionId}`;try{if(sessionStorage.getItem(t))return;sessionStorage.setItem(t,`1`)}catch{}this.enqueue({type:`referral`,id:g(),ts:_(),url:this.url()??location.href,referrer:e?`https://${e}/`:null})}loadSession(){try{let e=sessionStorage.getItem(`__agentstats_session`);if(e){let t=JSON.parse(e);if(t?.id&&_()-t.last<this.options.sessionTimeoutMs)return this.lastActivity=t.last,t.id}}catch{}return g()}touchSession(){let e=_();this.lastActivity&&e-this.lastActivity>this.options.sessionTimeoutMs&&(this.sessionId=g(),this.lastSnapshotHash=``,this.log(`new session`,this.sessionId)),this.lastActivity=e;try{sessionStorage.setItem(`__agentstats_session`,JSON.stringify({id:this.sessionId,last:e}))}catch{}}};let A=null;function j(e){return A&&A.destroy(),A=new k(e),A}function M(){return A}function N(e,t){A?.track(e,t)}function P(e){return A?A.wrap(e):e}function F(){return A?.flush()??Promise.resolve()}function I(){return A?.snapshot()??Promise.resolve([])}function L(){A?.pageview()}let R={init:j,track:N,pageview:L,wrap:P,flush:F,snapshot:I,getClient:M,version:h};window.AgentStats=R;let z=document.currentScript,B=z?.dataset.key,V=e=>e!==void 0&&e!==`false`,H=z?.dataset.fileDownloads;return B&&R.init({key:B,endpoint:z?.dataset.endpoint,debug:z?.dataset.debug===`true`,captureArgs:z?.dataset.captureArgs!==`false`,captureErrors:z?.dataset.captureErrors!==`false`,captureResults:z?.dataset.captureResults===`true`,redactKeys:z?.dataset.redactKeys?.split(`,`).map(e=>e.trim()).filter(Boolean),trackPageviews:z?.dataset.pageviews!==`false`,hashRouting:V(z?.dataset.hashRouting),outboundLinks:V(z?.dataset.outboundLinks),fileDownloads:H&&![``,`true`,`false`].includes(H)?H.split(`,`).map(e=>e.trim()):V(H),allowLocalhost:V(z?.dataset.allowLocalhost),agentSignals:z?.dataset.agentSignals!==`false`}),e.AgentStats=R,e})({});
package/dist/index.cjs CHANGED
@@ -2,7 +2,150 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- //#region src/web.ts
5
+ const SIZE = 29;
6
+ const TIMES = [
7
+ 10,
8
+ 40,
9
+ 100,
10
+ 250
11
+ ];
12
+ const MOVES = [
13
+ 1,
14
+ 2,
15
+ 6,
16
+ 21
17
+ ];
18
+ const bucket = (x, bounds) => {
19
+ let i = 0;
20
+ while (i < bounds.length && x >= bounds[i]) i++;
21
+ return i;
22
+ };
23
+ const textEntry = (t) => !!t && (t.isContentEditable || t.tagName === "TEXTAREA" || t.tagName === "INPUT" && /^(text|search|email|url|tel|password|number|)$/.test(t.type));
24
+ const formField = (t) => !!t && (t.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName));
25
+ const autofilled = (t) => {
26
+ try {
27
+ return t.matches(":autofill");
28
+ } catch {
29
+ return false;
30
+ }
31
+ };
32
+ function watchSignals() {
33
+ const noop = {
34
+ take: () => null,
35
+ stop() {}
36
+ };
37
+ if (typeof document === "undefined" || typeof performance === "undefined") return noop;
38
+ const now = () => performance.now();
39
+ const away = () => document.visibilityState === "hidden" || !document.hasFocus();
40
+ let v = [];
41
+ let start = 0;
42
+ let moves = 0;
43
+ let lastKey = -1e9;
44
+ let editCause = -1e9;
45
+ let intent = -1e9;
46
+ let lastScroll = -1e9;
47
+ const holds = /* @__PURE__ */ new Map();
48
+ const reset = () => {
49
+ v = Array.from({ length: SIZE }, () => 0);
50
+ v[0] = 1;
51
+ start = now();
52
+ moves = 0;
53
+ };
54
+ reset();
55
+ const acted = (t) => {
56
+ if (!v[2]) v[2] = Math.max(1, Math.round(t - start));
57
+ };
58
+ const ctl = new AbortController();
59
+ const on = (type, f) => document.addEventListener(type, f, {
60
+ capture: true,
61
+ passive: true,
62
+ signal: ctl.signal
63
+ });
64
+ on("pointermove", (e) => {
65
+ if (!e.isTrusted) return;
66
+ v[3]++;
67
+ moves++;
68
+ });
69
+ on("pointerdown", (e) => {
70
+ if (!e.isTrusted) return;
71
+ intent = now();
72
+ acted(intent);
73
+ });
74
+ on("click", (e) => {
75
+ if (!e.isTrusted) return void v[26]++;
76
+ acted(now());
77
+ if (away()) v[27]++;
78
+ if (e.detail === 0) v[10]++;
79
+ else if (e.pointerType === "touch" || e.pointerType === "pen") v[9]++;
80
+ else if (e.detail === 1) v[4 + bucket(moves, MOVES)]++;
81
+ moves = 0;
82
+ });
83
+ on("keydown", (e) => {
84
+ if (!e.isTrusted) return void v[26]++;
85
+ const t = now();
86
+ acted(t);
87
+ intent = editCause = t;
88
+ if (away()) v[27]++;
89
+ if (e.repeat || e.target?.type === "password") return;
90
+ if (t - lastKey < 2e3) v[16 + bucket(t - lastKey, TIMES)]++;
91
+ lastKey = t;
92
+ holds.set(e.code || e.key, t);
93
+ });
94
+ on("keyup", (e) => {
95
+ const down = holds.get(e.code || e.key);
96
+ if (!e.isTrusted || down === void 0) return;
97
+ holds.delete(e.code || e.key);
98
+ v[11 + bucket(now() - down, TIMES)]++;
99
+ });
100
+ for (const type of [
101
+ "paste",
102
+ "drop",
103
+ "compositionend"
104
+ ]) on(type, () => {
105
+ editCause = now();
106
+ });
107
+ for (const type of ["wheel", "touchstart"]) on(type, () => {
108
+ intent = now();
109
+ });
110
+ on("change", (e) => {
111
+ if (!e.isTrusted && formField(e.target)) v[28]++;
112
+ });
113
+ on("input", (e) => {
114
+ if (!e.isTrusted) return void (formField(e.target) && v[28]++);
115
+ if (!textEntry(e.target) || autofilled(e.target)) return;
116
+ const t = now();
117
+ acted(t);
118
+ if (away()) v[27]++;
119
+ const since = t - editCause;
120
+ v[since < 100 ? 21 : since < 1e3 ? 22 : 23]++;
121
+ });
122
+ on("scroll", () => {
123
+ const t = now();
124
+ if (t - lastScroll > 150) {
125
+ v[24]++;
126
+ if (t - intent > 500) v[25]++;
127
+ }
128
+ lastScroll = t;
129
+ });
130
+ return {
131
+ take() {
132
+ const out = v.slice(3).some(Boolean) ? v : null;
133
+ if (out) out[1] = Math.min(2 ** 31, Math.round(now() - start));
134
+ reset();
135
+ return out;
136
+ },
137
+ stop: () => ctl.abort()
138
+ };
139
+ }
140
+ // src/web.ts
141
+ /**
142
+ * Visitors: privacy-friendly web analytics for people. One small request per pageview or custom
143
+ * event, no cookies, nothing stored in the browser. The server counts unique visitors from a
144
+ * daily-salted hash of the IP and user agent, the way Plausible does, and keeps neither.
145
+ *
146
+ * Used by the full SDK (on by default, `trackPageviews: false` turns it off) and on its own by
147
+ * the lite script (`/sdk/lite.js`) for sites without WebMCP tools.
148
+ */
6
149
  const VISIT_COOKIE = "_al_v";
7
150
  /** What Claude in Chrome puts on the page while it's in control (its stop button). */
8
151
  const AGENT_CONTROL_MARKER = "#claude-agent-stop-container";
@@ -96,20 +239,15 @@ function webAnalytics(o) {
96
239
  try {
97
240
  ignored = localStorage.getItem("agentstats_ignore") === "true";
98
241
  } catch {}
99
- const send = (name, props) => {
242
+ const post = (what, data) => {
100
243
  if (ignored) return log("ignored: agentstats_ignore is set");
101
- if (local && !o.allowLocalhost) return log("ignored on localhost:", name);
244
+ if (local && !o.allowLocalhost) return log("ignored on localhost:", what);
102
245
  const body = JSON.stringify({
103
246
  k: o.key,
104
- n: name,
105
- u: location.href,
106
- r: document.referrer || null,
107
- w: window.innerWidth,
108
- p: props,
109
- a: agentInBrowser()?.name,
110
- h: o.hashRouting ? 1 : void 0
247
+ ...data,
248
+ a: agentInBrowser()?.name
111
249
  });
112
- log("send", name, props ?? "");
250
+ log("send", what);
113
251
  try {
114
252
  if (navigator.sendBeacon?.(o.endpoint, body)) return;
115
253
  } catch {}
@@ -120,6 +258,24 @@ function webAnalytics(o) {
120
258
  headers: { "Content-Type": "text/plain" }
121
259
  }).catch(() => {});
122
260
  };
261
+ const send = (name, props) => post(name, {
262
+ n: name,
263
+ u: location.href,
264
+ r: document.referrer || null,
265
+ w: window.innerWidth,
266
+ p: props,
267
+ h: o.hashRouting ? 1 : void 0
268
+ });
269
+ const signals = o.agentSignals === false ? null : watchSignals();
270
+ let signalsUrl = location.href;
271
+ const sendSignals = () => {
272
+ const s = signals?.take();
273
+ if (s) post("signals", {
274
+ t: "s",
275
+ u: signalsUrl,
276
+ s
277
+ });
278
+ };
123
279
  const ctl = new AbortController();
124
280
  const on = { signal: ctl.signal };
125
281
  let last = "";
@@ -127,7 +283,9 @@ function webAnalytics(o) {
127
283
  if (ctl.signal.aborted) return;
128
284
  const page = location.pathname + (o.hashRouting ? location.hash : "");
129
285
  if (page === last) return;
286
+ if (last) sendSignals();
130
287
  last = page;
288
+ signalsUrl = location.href;
131
289
  send("pageview");
132
290
  };
133
291
  const start = () => {
@@ -169,14 +327,22 @@ function webAnalytics(o) {
169
327
  signal: ctl.signal
170
328
  });
171
329
  }
330
+ if (signals) {
331
+ window.addEventListener("pagehide", sendSignals, on);
332
+ document.addEventListener("visibilitychange", () => document.visibilityState === "hidden" && sendSignals(), on);
333
+ }
172
334
  return {
173
335
  pageview,
174
336
  track: (name, props) => send(String(name), props),
175
- stop: () => ctl.abort()
337
+ stop: () => {
338
+ if (ctl.signal.aborted) return;
339
+ sendSignals();
340
+ signals?.stop();
341
+ ctl.abort();
342
+ }
176
343
  };
177
344
  }
178
- //#endregion
179
- //#region src/index.ts
345
+ // src/index.ts
180
346
  /**
181
347
  * AgentStats SDK — analytics for WebMCP tool calls, and for the people visiting the site.
182
348
  *
@@ -188,7 +354,7 @@ function webAnalytics(o) {
188
354
  * It also records pageviews (Visitors, see ./web.ts): no cookies, no personal data. Turn that
189
355
  * off with `trackPageviews: false`.
190
356
  */
191
- const SDK_VERSION = "0.4.1";
357
+ const SDK_VERSION = "0.5.1";
192
358
  const MAX_ARG_BYTES = 8192;
193
359
  const MAX_RESULT_CHARS = 2048;
194
360
  function uuid() {
@@ -307,6 +473,7 @@ var AgentStatsClient = class {
307
473
  outboundLinks: o.outboundLinks,
308
474
  fileDownloads: o.fileDownloads,
309
475
  allowLocalhost: o.allowLocalhost,
476
+ agentSignals: o.agentSignals,
310
477
  debug: o.debug
311
478
  });
312
479
  }
@@ -786,7 +953,6 @@ const AgentStats = {
786
953
  getClient,
787
954
  version: SDK_VERSION
788
955
  };
789
- //#endregion
790
956
  exports.AgentStats = AgentStats;
791
957
  exports.default = AgentStats;
792
958
  exports.AgentStatsClient = AgentStatsClient;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,3 @@
1
- //#region src/web.d.ts
2
1
  /**
3
2
  * Visitors: privacy-friendly web analytics for people. One small request per pageview or custom
4
3
  * event, no cookies, nothing stored in the browser. The server counts unique visitors from a
@@ -8,9 +7,7 @@
8
7
  * the lite script (`/sdk/lite.js`) for sites without WebMCP tools.
9
8
  */
10
9
  type EventProps = Record<string, string | number | boolean>;
11
- //#endregion
12
- //#region src/index.d.ts
13
- export declare const SDK_VERSION = "0.4.1";
10
+ export declare const SDK_VERSION = "0.5.1";
14
11
  export type ToolStatus = 'success' | 'error' | 'cancelled';
15
12
  export interface ToolAnnotations {
16
13
  readOnlyHint?: boolean;
@@ -103,6 +100,8 @@ export interface AgentStatsOptions {
103
100
  fileDownloads?: boolean | string[];
104
101
  /** Record pageviews on localhost too. Default `false`. */
105
102
  allowLocalhost?: boolean;
103
+ /** With Visitors, send page interaction counts and timings (no content, positions or elements). Default `true`. */
104
+ agentSignals?: boolean;
106
105
  /** Log to console. */
107
106
  debug?: boolean;
108
107
  /** Do nothing (useful in dev/test). */
@@ -265,5 +264,4 @@ export declare const AgentStats: {
265
264
  getClient: typeof getClient;
266
265
  version: string;
267
266
  };
268
- //#endregion
269
267
  export { AgentStats as default, type EventProps };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- //#region src/web.d.ts
2
1
  /**
3
2
  * Visitors: privacy-friendly web analytics for people. One small request per pageview or custom
4
3
  * event, no cookies, nothing stored in the browser. The server counts unique visitors from a
@@ -8,9 +7,7 @@
8
7
  * the lite script (`/sdk/lite.js`) for sites without WebMCP tools.
9
8
  */
10
9
  type EventProps = Record<string, string | number | boolean>;
11
- //#endregion
12
- //#region src/index.d.ts
13
- export declare const SDK_VERSION = "0.4.1";
10
+ export declare const SDK_VERSION = "0.5.1";
14
11
  export type ToolStatus = 'success' | 'error' | 'cancelled';
15
12
  export interface ToolAnnotations {
16
13
  readOnlyHint?: boolean;
@@ -103,6 +100,8 @@ export interface AgentStatsOptions {
103
100
  fileDownloads?: boolean | string[];
104
101
  /** Record pageviews on localhost too. Default `false`. */
105
102
  allowLocalhost?: boolean;
103
+ /** With Visitors, send page interaction counts and timings (no content, positions or elements). Default `true`. */
104
+ agentSignals?: boolean;
106
105
  /** Log to console. */
107
106
  debug?: boolean;
108
107
  /** Do nothing (useful in dev/test). */
@@ -265,5 +264,4 @@ export declare const AgentStats: {
265
264
  getClient: typeof getClient;
266
265
  version: string;
267
266
  };
268
- //#endregion
269
267
  export { AgentStats as default, type EventProps };
package/dist/index.js CHANGED
@@ -1,4 +1,147 @@
1
- //#region src/web.ts
1
+ const SIZE = 29;
2
+ const TIMES = [
3
+ 10,
4
+ 40,
5
+ 100,
6
+ 250
7
+ ];
8
+ const MOVES = [
9
+ 1,
10
+ 2,
11
+ 6,
12
+ 21
13
+ ];
14
+ const bucket = (x, bounds) => {
15
+ let i = 0;
16
+ while (i < bounds.length && x >= bounds[i]) i++;
17
+ return i;
18
+ };
19
+ const textEntry = (t) => !!t && (t.isContentEditable || t.tagName === "TEXTAREA" || t.tagName === "INPUT" && /^(text|search|email|url|tel|password|number|)$/.test(t.type));
20
+ const formField = (t) => !!t && (t.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName));
21
+ const autofilled = (t) => {
22
+ try {
23
+ return t.matches(":autofill");
24
+ } catch {
25
+ return false;
26
+ }
27
+ };
28
+ function watchSignals() {
29
+ const noop = {
30
+ take: () => null,
31
+ stop() {}
32
+ };
33
+ if (typeof document === "undefined" || typeof performance === "undefined") return noop;
34
+ const now = () => performance.now();
35
+ const away = () => document.visibilityState === "hidden" || !document.hasFocus();
36
+ let v = [];
37
+ let start = 0;
38
+ let moves = 0;
39
+ let lastKey = -1e9;
40
+ let editCause = -1e9;
41
+ let intent = -1e9;
42
+ let lastScroll = -1e9;
43
+ const holds = /* @__PURE__ */ new Map();
44
+ const reset = () => {
45
+ v = Array.from({ length: SIZE }, () => 0);
46
+ v[0] = 1;
47
+ start = now();
48
+ moves = 0;
49
+ };
50
+ reset();
51
+ const acted = (t) => {
52
+ if (!v[2]) v[2] = Math.max(1, Math.round(t - start));
53
+ };
54
+ const ctl = new AbortController();
55
+ const on = (type, f) => document.addEventListener(type, f, {
56
+ capture: true,
57
+ passive: true,
58
+ signal: ctl.signal
59
+ });
60
+ on("pointermove", (e) => {
61
+ if (!e.isTrusted) return;
62
+ v[3]++;
63
+ moves++;
64
+ });
65
+ on("pointerdown", (e) => {
66
+ if (!e.isTrusted) return;
67
+ intent = now();
68
+ acted(intent);
69
+ });
70
+ on("click", (e) => {
71
+ if (!e.isTrusted) return void v[26]++;
72
+ acted(now());
73
+ if (away()) v[27]++;
74
+ if (e.detail === 0) v[10]++;
75
+ else if (e.pointerType === "touch" || e.pointerType === "pen") v[9]++;
76
+ else if (e.detail === 1) v[4 + bucket(moves, MOVES)]++;
77
+ moves = 0;
78
+ });
79
+ on("keydown", (e) => {
80
+ if (!e.isTrusted) return void v[26]++;
81
+ const t = now();
82
+ acted(t);
83
+ intent = editCause = t;
84
+ if (away()) v[27]++;
85
+ if (e.repeat || e.target?.type === "password") return;
86
+ if (t - lastKey < 2e3) v[16 + bucket(t - lastKey, TIMES)]++;
87
+ lastKey = t;
88
+ holds.set(e.code || e.key, t);
89
+ });
90
+ on("keyup", (e) => {
91
+ const down = holds.get(e.code || e.key);
92
+ if (!e.isTrusted || down === void 0) return;
93
+ holds.delete(e.code || e.key);
94
+ v[11 + bucket(now() - down, TIMES)]++;
95
+ });
96
+ for (const type of [
97
+ "paste",
98
+ "drop",
99
+ "compositionend"
100
+ ]) on(type, () => {
101
+ editCause = now();
102
+ });
103
+ for (const type of ["wheel", "touchstart"]) on(type, () => {
104
+ intent = now();
105
+ });
106
+ on("change", (e) => {
107
+ if (!e.isTrusted && formField(e.target)) v[28]++;
108
+ });
109
+ on("input", (e) => {
110
+ if (!e.isTrusted) return void (formField(e.target) && v[28]++);
111
+ if (!textEntry(e.target) || autofilled(e.target)) return;
112
+ const t = now();
113
+ acted(t);
114
+ if (away()) v[27]++;
115
+ const since = t - editCause;
116
+ v[since < 100 ? 21 : since < 1e3 ? 22 : 23]++;
117
+ });
118
+ on("scroll", () => {
119
+ const t = now();
120
+ if (t - lastScroll > 150) {
121
+ v[24]++;
122
+ if (t - intent > 500) v[25]++;
123
+ }
124
+ lastScroll = t;
125
+ });
126
+ return {
127
+ take() {
128
+ const out = v.slice(3).some(Boolean) ? v : null;
129
+ if (out) out[1] = Math.min(2 ** 31, Math.round(now() - start));
130
+ reset();
131
+ return out;
132
+ },
133
+ stop: () => ctl.abort()
134
+ };
135
+ }
136
+ // src/web.ts
137
+ /**
138
+ * Visitors: privacy-friendly web analytics for people. One small request per pageview or custom
139
+ * event, no cookies, nothing stored in the browser. The server counts unique visitors from a
140
+ * daily-salted hash of the IP and user agent, the way Plausible does, and keeps neither.
141
+ *
142
+ * Used by the full SDK (on by default, `trackPageviews: false` turns it off) and on its own by
143
+ * the lite script (`/sdk/lite.js`) for sites without WebMCP tools.
144
+ */
2
145
  const VISIT_COOKIE = "_al_v";
3
146
  /** What Claude in Chrome puts on the page while it's in control (its stop button). */
4
147
  const AGENT_CONTROL_MARKER = "#claude-agent-stop-container";
@@ -92,20 +235,15 @@ function webAnalytics(o) {
92
235
  try {
93
236
  ignored = localStorage.getItem("agentstats_ignore") === "true";
94
237
  } catch {}
95
- const send = (name, props) => {
238
+ const post = (what, data) => {
96
239
  if (ignored) return log("ignored: agentstats_ignore is set");
97
- if (local && !o.allowLocalhost) return log("ignored on localhost:", name);
240
+ if (local && !o.allowLocalhost) return log("ignored on localhost:", what);
98
241
  const body = JSON.stringify({
99
242
  k: o.key,
100
- n: name,
101
- u: location.href,
102
- r: document.referrer || null,
103
- w: window.innerWidth,
104
- p: props,
105
- a: agentInBrowser()?.name,
106
- h: o.hashRouting ? 1 : void 0
243
+ ...data,
244
+ a: agentInBrowser()?.name
107
245
  });
108
- log("send", name, props ?? "");
246
+ log("send", what);
109
247
  try {
110
248
  if (navigator.sendBeacon?.(o.endpoint, body)) return;
111
249
  } catch {}
@@ -116,6 +254,24 @@ function webAnalytics(o) {
116
254
  headers: { "Content-Type": "text/plain" }
117
255
  }).catch(() => {});
118
256
  };
257
+ const send = (name, props) => post(name, {
258
+ n: name,
259
+ u: location.href,
260
+ r: document.referrer || null,
261
+ w: window.innerWidth,
262
+ p: props,
263
+ h: o.hashRouting ? 1 : void 0
264
+ });
265
+ const signals = o.agentSignals === false ? null : watchSignals();
266
+ let signalsUrl = location.href;
267
+ const sendSignals = () => {
268
+ const s = signals?.take();
269
+ if (s) post("signals", {
270
+ t: "s",
271
+ u: signalsUrl,
272
+ s
273
+ });
274
+ };
119
275
  const ctl = new AbortController();
120
276
  const on = { signal: ctl.signal };
121
277
  let last = "";
@@ -123,7 +279,9 @@ function webAnalytics(o) {
123
279
  if (ctl.signal.aborted) return;
124
280
  const page = location.pathname + (o.hashRouting ? location.hash : "");
125
281
  if (page === last) return;
282
+ if (last) sendSignals();
126
283
  last = page;
284
+ signalsUrl = location.href;
127
285
  send("pageview");
128
286
  };
129
287
  const start = () => {
@@ -165,14 +323,22 @@ function webAnalytics(o) {
165
323
  signal: ctl.signal
166
324
  });
167
325
  }
326
+ if (signals) {
327
+ window.addEventListener("pagehide", sendSignals, on);
328
+ document.addEventListener("visibilitychange", () => document.visibilityState === "hidden" && sendSignals(), on);
329
+ }
168
330
  return {
169
331
  pageview,
170
332
  track: (name, props) => send(String(name), props),
171
- stop: () => ctl.abort()
333
+ stop: () => {
334
+ if (ctl.signal.aborted) return;
335
+ sendSignals();
336
+ signals?.stop();
337
+ ctl.abort();
338
+ }
172
339
  };
173
340
  }
174
- //#endregion
175
- //#region src/index.ts
341
+ // src/index.ts
176
342
  /**
177
343
  * AgentStats SDK — analytics for WebMCP tool calls, and for the people visiting the site.
178
344
  *
@@ -184,7 +350,7 @@ function webAnalytics(o) {
184
350
  * It also records pageviews (Visitors, see ./web.ts): no cookies, no personal data. Turn that
185
351
  * off with `trackPageviews: false`.
186
352
  */
187
- const SDK_VERSION = "0.4.1";
353
+ const SDK_VERSION = "0.5.1";
188
354
  const MAX_ARG_BYTES = 8192;
189
355
  const MAX_RESULT_CHARS = 2048;
190
356
  function uuid() {
@@ -303,6 +469,7 @@ var AgentStatsClient = class {
303
469
  outboundLinks: o.outboundLinks,
304
470
  fileDownloads: o.fileDownloads,
305
471
  allowLocalhost: o.allowLocalhost,
472
+ agentSignals: o.agentSignals,
306
473
  debug: o.debug
307
474
  });
308
475
  }
@@ -782,5 +949,4 @@ const AgentStats = {
782
949
  getClient,
783
950
  version: SDK_VERSION
784
951
  };
785
- //#endregion
786
952
  export { AgentStats, AgentStats as default, AgentStatsClient, SDK_VERSION, flush, getClient, init, pageview, snapshot, track, wrap };