herm-tui 1.11.0-dev.1 → 1.11.0-dev.11

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.
Files changed (4) hide show
  1. package/README.md +14 -0
  2. package/db.worker.js +49 -22
  3. package/index.js +249 -219
  4. package/package.json +2 -2
package/README.md CHANGED
@@ -56,6 +56,20 @@ herm # fresh session
56
56
  herm -c # resume last session
57
57
  ```
58
58
 
59
+ Attach to an already-running Hermes dashboard instead of spawning a local
60
+ gateway subprocess:
61
+
62
+ ```bash
63
+ herm --gateway-url 'ws://127.0.0.1:9119/api/ws?token=...'
64
+ ```
65
+
66
+ HTTP(S) dashboard URLs are normalized to WS(S) and `/api/ws` is appended after
67
+ any path prefix. The URL must already carry a reusable `token` or `internal`
68
+ credential. OAuth-gated dashboards use single-use tickets and require an
69
+ authenticated client that can mint a fresh ticket for every reconnect. For
70
+ persistent configuration, set `HERM_GATEWAY_URL`. Herm also accepts the
71
+ upstream-injected `HERMES_TUI_GATEWAY_URL`.
72
+
59
73
  Or run from source:
60
74
 
61
75
  ```bash
package/db.worker.js CHANGED
@@ -5,24 +5,35 @@ var __create=Object.create;var{getPrototypeOf:__getProtoOf,defineProperty:__defP
5
5
  `))},data=()=>{if(!enabled)return null;let m=process.memoryUsage();return{boot:Object.fromEntries(stages.map(([l,ms])=>[l,+ms.toFixed(1)])),memory:{rss:Math.round(m.rss/1024/1024),heap:Math.round(m.heapUsed/1024/1024),heapTotal:Math.round(m.heapTotal/1024/1024),external:Math.round(m.external/1024/1024)},snapshots:snapshots.map((s)=>({label:s.label,rss:Math.round(s.rss/1024/1024),heap:Math.round(s.heap/1024/1024),external:Math.round(s.external/1024/1024)})),timings:Object.fromEntries([...timings.entries()].sort((a,b)=>b[1].total-a[1].total).map(([k,v])=>[k,{count:v.count,avg:+(v.total/v.count).toFixed(2),min:+v.min.toFixed(2),max:+v.max.toFixed(2),total:Math.round(v.total)}])),renders:Object.fromEntries([...renders.entries()].sort((a,b)=>b[1].count-a[1].count).map(([k,v])=>[k,{count:v.count,avg:+(v.total/v.count).toFixed(2),max:+v.max.toFixed(2),total:Math.round(v.total)}])),counters:Object.fromEntries([...counters.entries()].sort((a,b)=>b[1]-a[1]))}},log=(msg)=>process.stderr.write(msg+`
6
6
  `);var init_perf=__esm(()=>{level=process.env.PERF??"",enabled=level==="1"||level==="verbose",verbose=level==="verbose",timings=new Map,mark=enabled?(label)=>{let start=Bun.nanoseconds();return()=>{let ms=(Bun.nanoseconds()-start)/1e6,t=timings.get(label);if(t){if(t.count++,t.total+=ms,ms<t.min)t.min=ms;if(ms>t.max)t.max=ms;t.last=ms}else timings.set(label,{count:1,total:ms,min:ms,max:ms,last:ms});if(verbose)log(`\u23F1 ${label}: ${ms.toFixed(2)}ms`);return ms}}:(_)=>noop,stages=[],counters=new Map,count=enabled?(label,n=1)=>{counters.set(label,(counters.get(label)??0)+n)}:(_label,_n)=>{},snapshots=[],mem=enabled?(label)=>{let m=process.memoryUsage();snapshots.push({label,rss:m.rss,heap:m.heapUsed,external:m.external,ts:Date.now()}),log(`\uD83D\uDCCA [${label}] RSS=${mb(m.rss)}MB heap=${mb(m.heapUsed)}MB ext=${mb(m.external)}MB`)}:(_)=>{},monitor=enabled?(ms=1e4)=>{let id=setInterval(()=>{let m=process.memoryUsage(),gc=Bun.gc(!1);log(`\x1B[90m[mem] RSS=${mb(m.rss)}MB heap=${mb(m.heapUsed)}/${mb(m.heapTotal)}MB ext=${mb(m.external)}MB gcRuns=${gc?.eden_collections??"?"}/${gc?.full_collections??"?"}\x1B[0m`)},ms);return()=>clearInterval(id)}:(_ms)=>noop,renders=new Map,onRender=enabled?(id,phase,actual)=>{let r=renders.get(id);if(r){if(r.count++,r.total+=actual,actual>r.max)r.max=actual;r.last=actual}else renders.set(id,{count:1,total:actual,max:actual,last:actual});if(verbose&&actual>1)log(`\uD83D\uDD04 [${id}] ${phase}: ${actual.toFixed(2)}ms`)}:(_id,_phase,_actual)=>{};if(enabled)process.on("exit",report),process.on("SIGINT",()=>{report(),process.exit(0)})});var exports_sessions_db={};__export(exports_sessions_db,{systemPrompt:()=>systemPrompt,stateDb:()=>stateDb,setHome:()=>setHome,search:()=>search,sdb:()=>exports_sessions_db,roots:()=>roots,resetDb:()=>resetDb,rename:()=>rename,remove:()=>remove,peek:()=>peek,lineage:()=>lineage,lastReal:()=>lastReal,kind:()=>kind,goalState:()=>goalState,children:()=>children,chainTip:()=>chainTip,byId:()=>byId});import{Database}from"bun:sqlite";import{homedir}from"os";function walkUp(sid){let step=q(`SELECT p.id FROM sessions c
7
7
  JOIN sessions p ON p.id = c.parent_session_id
8
- WHERE c.id = ? AND ${CONT("c")}`),cur=sid;for(let i=0;i<100;i++){let prev=step?.get(cur);if(!prev)return cur;cur=prev.id}return cur}function roots(limit=30){let end=mark("io:sessions.roots");try{return(q(`SELECT ${COLS} FROM sessions s
9
- WHERE s.parent_session_id IS NULL
10
- OR EXISTS (SELECT 1 FROM sessions p
11
- WHERE p.id = s.parent_session_id
12
- AND ${BR("s")})
13
- ORDER BY s.started_at DESC
8
+ WHERE c.id = ? AND c.id = ${next("p")}`),cur=sid;for(let i=0;i<100;i++){let prev=step?.get(cur);if(!prev)return cur;cur=prev.id}return cur}function roots(limit=30){let end=mark("io:sessions.roots");try{return(q(`WITH RECURSIVE chain(root_id, cur_id) AS (
9
+ SELECT s.id, s.id FROM sessions s
10
+ WHERE ${listable("s")}
11
+ UNION ALL
12
+ SELECT c.root_id, child.id
13
+ FROM chain c
14
+ JOIN sessions parent ON parent.id = c.cur_id
15
+ JOIN sessions child ON child.parent_session_id = c.cur_id
16
+ WHERE ${cont("child","parent")}
17
+ ), stats AS (
18
+ SELECT c.root_id, MAX(${active("s")}) AS tick
19
+ FROM chain c
20
+ JOIN sessions s ON s.id = c.cur_id
21
+ GROUP BY c.root_id
22
+ )
23
+ SELECT ${COLS} FROM sessions s
24
+ LEFT JOIN stats st ON st.root_id = s.id
25
+ WHERE ${listable("s")}
26
+ ORDER BY COALESCE(st.tick, s.started_at) DESC, s.started_at DESC, s.id DESC
14
27
  LIMIT ?`)?.all(limit)??[]).map((r)=>{if(r.end_reason!=="compression")return toRow(r);let tid=tip(r.id);if(tid===r.id)return toRow(r);let t=one(tid);return t?{...toRow(t,r.id),started_at:r.started_at}:toRow(r)})}finally{end()}}function children(pid){let end=mark("io:sessions.children");try{return(q(`SELECT ${COLS} FROM sessions s
15
28
  JOIN sessions p ON p.id = s.parent_session_id
16
- WHERE s.parent_session_id = ? AND ${SUB("s")}
29
+ WHERE s.parent_session_id = ? AND ${sub("s","p")}
17
30
  ORDER BY s.started_at ASC`)?.all(pid)??[]).map((r)=>toRow(r))}finally{end()}}function lineage(sid){let end=mark("io:sessions.lineage");try{let pred=q(`SELECT p.id, p.title FROM sessions c
18
31
  JOIN sessions p ON p.id = c.parent_session_id
19
- WHERE c.id = ? AND ${CONT("c")}`)?.get(sid),succ=q(`SELECT c.id, c.title FROM sessions c
32
+ WHERE c.id = ? AND c.id = ${next("p")}`)?.get(sid),succ=q(`SELECT c.id, c.title FROM sessions c
20
33
  JOIN sessions p ON p.id = c.parent_session_id
21
- WHERE p.id = ? AND ${CONT("c")}
22
- ORDER BY c.started_at DESC LIMIT 1`)?.get(sid);return{...pred&&{continuesFrom:pred},...succ&&{compressedTo:succ}}}finally{end()}}function tip(sid){let step=q(`SELECT c.id FROM sessions c
34
+ WHERE p.id = ? AND c.id = ${next("p")}`)?.get(sid);return{...pred&&{continuesFrom:pred},...succ&&{compressedTo:succ}}}finally{end()}}function tip(sid){let step=q(`SELECT c.id FROM sessions c
23
35
  JOIN sessions p ON p.id = c.parent_session_id
24
- WHERE p.id = ? AND ${CONT("c")}
25
- ORDER BY c.started_at DESC LIMIT 1`),cur=sid;for(let i=0;i<100;i++){let next=step?.get(cur);if(!next)return cur;cur=next.id}return cur}function peek(sid,_n=4){let end=mark("io:sessions.peek");try{let ext=[hasMsgCol("platform_message_id")?"platform_message_id":"NULL AS platform_message_id",hasMsgCol("observed")?"observed":"NULL AS observed"];return(q(`SELECT role, SUBSTR(content,1,400) AS content, tool_name,
36
+ WHERE p.id = ? AND c.id = ${next("p")}`),cur=sid;for(let i=0;i<100;i++){let next2=step?.get(cur);if(!next2)return cur;cur=next2.id}return cur}function peek(sid,_n=4){let end=mark("io:sessions.peek");try{let ext=[hasMsgCol("platform_message_id")?"platform_message_id":"NULL AS platform_message_id",hasMsgCol("observed")?"observed":"NULL AS observed"];return(q(`SELECT role, SUBSTR(content,1,400) AS content, tool_name,
26
37
  SUBSTR(tool_calls,1,400) AS tool_calls, timestamp AS at,
27
38
  ${ext.join(", ")}
28
39
  FROM (
@@ -38,7 +49,31 @@ var __create=Object.create;var{getPrototypeOf:__getProtoOf,defineProperty:__defP
38
49
  JOIN messages m ON m.id = messages_fts.rowid
39
50
  JOIN sessions s ON s.id = m.session_id
40
51
  WHERE messages_fts MATCH ?
41
- ORDER BY rank LIMIT ?`)?.all(m,limit*4)??[],seen=new Set;return raw.filter((r)=>!seen.has(r.session_id)&&(seen.add(r.session_id),!0)).slice(0,limit)}finally{end()}}function rename(sid,title){let db=new Database(conn.path);try{return db.run("UPDATE sessions SET title = ? WHERE id = ?",[title,sid]),db.query("SELECT changes() AS c").get().c>0}finally{db.close()}}function remove(sid){let db=new Database(conn.path);try{if(!db.query("SELECT 1 FROM sessions WHERE id = ?").get(sid))return!1;return db.run("UPDATE sessions SET parent_session_id = NULL WHERE parent_session_id = ?",[sid]),db.run("DELETE FROM messages WHERE session_id = ?",[sid]),db.run("DELETE FROM sessions WHERE id = ?",[sid]),!0}finally{db.close()}}var HERMES,SRC,conn,setHome=(h)=>{let next=`${h}/state.db`;if(conn.path===next)return;conn.path=SRC.file=next,resetDb()},stateDb=()=>{if(conn.ro)return conn.ro;try{return conn.ro=new Database(conn.path,{readwrite:!0,create:!1})}catch{return null}},resetDb=()=>{for(let s of stmts.values())s.finalize();stmts.clear(),msgCols.clear(),conn.ro?.close(),conn.ro=null},stmts,msgCols,q=(sql)=>{let db=stateDb();if(!db)return null;let s=stmts.get(sql);if(!s)stmts.set(sql,s=db.query(sql));return s},hasMsgCol=(name)=>{let hit=msgCols.get(name);if(hit!==void 0)return hit;let db=stateDb(),ok=db?db.query("PRAGMA table_info(messages)").all().some((r)=>r.name===name):!1;return msgCols.set(name,ok),ok},SUB=(c)=>`(p.ended_at IS NULL OR ${c}.started_at < p.ended_at)`,CONT=(c)=>`(p.end_reason = 'compression' AND ${c}.started_at >= p.ended_at)`,BR=(c)=>`(p.end_reason = 'branched' AND ${c}.started_at >= p.ended_at)`,kind=(parent,child)=>{if(!parent)return"root";if(parent.ended_at==null||child.started_at<parent.ended_at)return"subagent";if(parent.end_reason==="compression")return"continuation";if(parent.end_reason==="branched")return"branch";return"subagent"},COLS=`
52
+ ORDER BY rank LIMIT ?`)?.all(m,limit*4)??[],seen=new Set;return raw.filter((r)=>!seen.has(r.session_id)&&(seen.add(r.session_id),!0)).slice(0,limit)}finally{end()}}function rename(sid,title){let db=new Database(conn.path);try{return db.run("UPDATE sessions SET title = ? WHERE id = ?",[title,sid]),db.query("SELECT changes() AS c").get().c>0}finally{db.close()}}function remove(sid){let db=new Database(conn.path);try{return db.transaction((id)=>{if(!db.query("SELECT 1 FROM sessions WHERE id = ?").get(id))return!1;return db.run("UPDATE sessions SET parent_session_id = NULL WHERE parent_session_id = ?",[id]),db.run("DELETE FROM messages WHERE session_id = ?",[id]),db.run("DELETE FROM sessions WHERE id = ?",[id]),!0})(sid)}finally{db.close()}}var HERMES,SRC,conn,setHome=(h)=>{let next=`${h}/state.db`;if(conn.path===next)return;conn.path=SRC.file=next,resetDb()},stateDb=()=>{if(conn.ro)return conn.ro;try{return conn.ro=new Database(conn.path,{readwrite:!0,create:!1})}catch{return null}},resetDb=()=>{for(let s of stmts.values())s.finalize();stmts.clear(),msgCols.clear(),conn.ro?.close(),conn.ro=null},stmts,msgCols,q=(sql)=>{let db=stateDb();if(!db)return null;let s=stmts.get(sql);if(!s)stmts.set(sql,s=db.query(sql));return s},hasMsgCol=(name)=>{let hit=msgCols.get(name);if(hit!==void 0)return hit;let db=stateDb(),ok=db?db.query("PRAGMA table_info(messages)").all().some((r)=>r.name===name):!1;return msgCols.set(name,ok),ok},cfg=(c,key)=>`json_extract(COALESCE(${c}.model_config, '{}'), '$.${key}')`,branched=(c)=>`${cfg(c,"_branched_from")} IS NOT NULL`,delegated=(c)=>`${cfg(c,"_delegate_from")} IS NOT NULL`,clean=(c)=>`COALESCE(${c}.source, '') != 'tool' AND NOT (${branched(c)}) AND NOT (${delegated(c)})`,cont=(c,p)=>`(${p}.ended_at IS NOT NULL AND COALESCE(${p}.end_reason, '') = 'compression' AND ${clean(c)})`,branch=(c,p)=>`(NOT (${delegated(c)}) AND (${branched(c)} OR (${p}.ended_at IS NOT NULL AND COALESCE(${p}.end_reason, '') = 'branched' AND ${c}.started_at >= ${p}.ended_at)))`,sub=(c,p)=>`(NOT (${branch(c,p)}) AND NOT (${cont(c,p)}) AND (${p}.ended_at IS NULL OR ${c}.started_at < ${p}.ended_at OR COALESCE(${p}.end_reason, '') NOT IN ('compression', 'branched')))`,top=(s)=>`(${s}.parent_session_id IS NULL OR EXISTS (SELECT 1 FROM sessions p WHERE p.id = ${s}.parent_session_id AND ${branch(s,"p")}))`,listable=(s)=>`(NOT (${delegated(s)}) AND ${top(s)})`,active=(s)=>`COALESCE((SELECT MAX(m.timestamp) FROM messages m WHERE m.session_id = ${s}.id), ${s}.started_at)`,order=(c)=>`CASE WHEN ${c}.end_reason = 'compression' THEN 0 WHEN ${c}.ended_at IS NULL THEN 1 ELSE 2 END ASC, ${active(c)} DESC, ${c}.started_at DESC, ${c}.id DESC`,next=(p,c="n")=>`(SELECT ${c}.id FROM sessions ${c} WHERE ${c}.parent_session_id = ${p}.id AND ${cont(c,p)} ORDER BY ${order(c)} LIMIT 1)`,marker=(raw,key)=>{if(!raw)return!1;try{return Object.prototype.hasOwnProperty.call(JSON.parse(raw),key)}catch{return!1}},kind=(parent,child)=>{if(!parent)return"root";if(marker(child.model_config,"_branched_from"))return"branch";if(parent.end_reason==="compression"&&parent.ended_at!=null&&child.source!=="tool"&&!marker(child.model_config,"_delegate_from"))return"continuation";if(parent.ended_at==null||child.started_at<parent.ended_at)return"subagent";if(parent.end_reason==="branched")return"branch";return"subagent"},COLS,toRow=(r,lineage=null)=>({source:SRC,id:r.id,sessionSource:r.source,model:r.model,billing_provider:r.billing_provider,started_at:r.started_at,ended_at:r.ended_at,end_reason:r.end_reason,message_count:r.message_count,tool_call_count:r.tool_call_count,input_tokens:r.input_tokens,output_tokens:r.output_tokens,cache_read_tokens:r.cache_read_tokens,cache_write_tokens:r.cache_write_tokens,reasoning_tokens:r.reasoning_tokens,estimated_cost_usd:r.estimated_cost_usd,title:r.title,lastMessage:r.lastMessage,last_active:r.last_active,parent_session_id:r.parent_session_id,subagent_count:r.subagent_count,lineage_root_id:lineage}),one=(id)=>q(`SELECT ${COLS} FROM sessions s WHERE s.id = ?`)?.get(id)??null,byId=(id)=>{let r=one(id);return r?toRow(r):null},lastReal=()=>{let hit=q(`
53
+ WITH RECURSIVE chain(root_id, cur_id) AS (
54
+ SELECT s.id, s.id FROM sessions s
55
+ WHERE s.source IN ('tui', 'cli') AND ${listable("s")}
56
+ UNION ALL
57
+ SELECT c.root_id, child.id
58
+ FROM chain c
59
+ JOIN sessions parent ON parent.id = c.cur_id
60
+ JOIN sessions child ON child.parent_session_id = c.cur_id
61
+ WHERE ${cont("child","parent")}
62
+ ), stats AS (
63
+ SELECT c.root_id,
64
+ MAX(${active("s")}) AS tick,
65
+ MAX(CASE WHEN s.source IN ('tui', 'cli') AND s.message_count > 0 THEN 1 ELSE 0 END) AS real
66
+ FROM chain c
67
+ JOIN sessions s ON s.id = c.cur_id
68
+ GROUP BY c.root_id
69
+ )
70
+ SELECT s.id FROM sessions s
71
+ JOIN stats st ON st.root_id = s.id
72
+ WHERE st.real = 1
73
+ ORDER BY st.tick DESC, s.started_at DESC, s.id DESC LIMIT 1
74
+ `)?.get();if(!hit)return;return byId(chainTip(hit.id))??void 0},chainTip=(sid)=>tip(walkUp(sid)),systemPrompt=()=>q(`SELECT id, system_prompt AS text FROM sessions
75
+ WHERE system_prompt IS NOT NULL AND length(system_prompt) > 1000
76
+ ORDER BY started_at DESC LIMIT 1`)?.get()??null,VALID_ITEM,parseItem=(raw)=>{if(!raw||typeof raw!=="object")return null;let o=raw,text=typeof o.text==="string"?o.text:"";if(!text.trim())return null;let s=typeof o.status==="string"?o.status:"pending",status=VALID_ITEM.has(s)?s:"pending",by=typeof o.added_by==="string"?o.added_by:void 0;return{text,status,addedBy:by==="judge"||by==="user"?by:void 0}},fts=(s)=>s.trim().split(/\s+/).filter(Boolean).map((w)=>/^\w+$/.test(w)?`${w}*`:`"${w.replace(/"/g,'""')}"`).join(" ");var init_sessions_db=__esm(()=>{init_perf();init_sessions_db();HERMES=process.env.HERMES_HOME||`${process.env.HOME||homedir()}/.hermes`,SRC={file:`${HERMES}/state.db`,relative:"state.db",label:"state.db"},conn={path:SRC.file,ro:null},stmts=new Map,msgCols=new Map,COLS=`
42
77
  s.id, s.source, s.model, s.billing_provider, s.started_at, s.ended_at, s.end_reason,
43
78
  s.message_count, s.tool_call_count,
44
79
  s.input_tokens, s.output_tokens,
@@ -52,15 +87,7 @@ var __create=Object.create;var{getPrototypeOf:__getProtoOf,defineProperty:__defP
52
87
  (SELECT MAX(timestamp) FROM messages WHERE session_id = s.id) AS last_active,
53
88
  (SELECT COUNT(*) FROM sessions c
54
89
  WHERE c.parent_session_id = s.id
55
- AND (s.ended_at IS NULL OR c.started_at < s.ended_at)) AS subagent_count`,toRow=(r,lineage=null)=>({source:SRC,id:r.id,sessionSource:r.source,model:r.model,billing_provider:r.billing_provider,started_at:r.started_at,ended_at:r.ended_at,end_reason:r.end_reason,message_count:r.message_count,tool_call_count:r.tool_call_count,input_tokens:r.input_tokens,output_tokens:r.output_tokens,cache_read_tokens:r.cache_read_tokens,cache_write_tokens:r.cache_write_tokens,reasoning_tokens:r.reasoning_tokens,estimated_cost_usd:r.estimated_cost_usd,title:r.title,lastMessage:r.lastMessage,last_active:r.last_active,parent_session_id:r.parent_session_id,subagent_count:r.subagent_count,lineage_root_id:lineage}),one=(id)=>q(`SELECT ${COLS} FROM sessions s WHERE s.id = ?`)?.get(id)??null,byId=(id)=>{let r=one(id);return r?toRow(r):null},lastReal=()=>{let hit=q(`
56
- SELECT s.id FROM sessions s
57
- LEFT JOIN sessions p ON p.id = s.parent_session_id
58
- WHERE s.source IN ('tui', 'cli') AND s.message_count > 0
59
- AND (s.parent_session_id IS NULL OR ${CONT("s")})
60
- ORDER BY s.started_at DESC LIMIT 1
61
- `)?.get();if(!hit)return;return byId(chainTip(hit.id))??void 0},chainTip=(sid)=>tip(walkUp(sid)),systemPrompt=()=>q(`SELECT id, system_prompt AS text FROM sessions
62
- WHERE system_prompt IS NOT NULL AND length(system_prompt) > 1000
63
- ORDER BY started_at DESC LIMIT 1`)?.get()??null,VALID_ITEM,parseItem=(raw)=>{if(!raw||typeof raw!=="object")return null;let o=raw,text=typeof o.text==="string"?o.text:"";if(!text.trim())return null;let s=typeof o.status==="string"?o.status:"pending",status=VALID_ITEM.has(s)?s:"pending",by=typeof o.added_by==="string"?o.added_by:void 0;return{text,status,addedBy:by==="judge"||by==="user"?by:void 0}},fts=(s)=>s.trim().split(/\s+/).filter(Boolean).map((w)=>/^\w+$/.test(w)?`${w}*`:`"${w.replace(/"/g,'""')}"`).join(" ");var init_sessions_db=__esm(()=>{init_perf();init_sessions_db();HERMES=process.env.HERMES_HOME||`${process.env.HOME||homedir()}/.hermes`,SRC={file:`${HERMES}/state.db`,relative:"state.db",label:"state.db"},conn={path:SRC.file,ro:null},stmts=new Map,msgCols=new Map;VALID_ITEM=new Set(["pending","completed","impossible"])});function analytics(days,opts){let since=Math.floor(Date.now()/1000)-days*86400,db=stateDb();if(!db)return ZERO;let q2=db.query.bind(db),tot=q2(`SELECT COUNT(*) n,
90
+ AND ${sub("c","s")}) AS subagent_count`;VALID_ITEM=new Set(["pending","completed","impossible"])});function analytics(days,opts){let since=Math.floor(Date.now()/1000)-days*86400,db=stateDb();if(!db)return ZERO;let q2=db.query.bind(db),tot=q2(`SELECT COUNT(*) n,
64
91
  COALESCE(SUM(message_count),0) msgs,
65
92
  COALESCE(SUM(input_tokens),0) i,
66
93
  COALESCE(SUM(output_tokens),0) o,