ai-dispatch 8.68.0

Multi-AI CLI team orchestrator
(function(){ "use strict";
function el(t,a){var n=document.createElement(t);if(a)Object.keys(a).forEach(function(k){var v=a[k];if(k==="class")n.className=v;else if(k==="dataset")Object.keys(v||{}).forEach(function(dk){n.dataset[dk]=v[dk];});else if(k==="style")Object.assign(n.style,v||{});else if(k in n)n[k]=v;else n.setAttribute(k,v);});for(var i=2;i<arguments.length;i++){var c=arguments[i];if(c==null)continue;n.appendChild(typeof c==="string"?document.createTextNode(c):c);}return n;}
function fmtDur(ms){if(ms==null)return"—";var s=Math.floor(Number(ms)/1000);if(!isFinite(s)||s<=0)return"—";if(s<60)return s+"s";var m=Math.floor(s/60);return m+"m "+(s%60)+"s";}
function fmtTok(n){if(n==null)return"—";var x=Number(n);if(!isFinite(x)||x<=0)return"—";if(x>=1e6)return(x/1e6).toFixed(1)+"M";if(x>=1e3)return(x/1e3).toFixed(1)+"k";return String(Math.round(x));}
function fmtCost(u){if(u==null)return"free";var x=Number(u);if(!isFinite(x)||x<=0)return"free";return"$"+x.toFixed(2);}
function timeAgo(iso){if(!iso)return"—";var d=new Date(iso),s=Math.round((Date.now()-d.getTime())/1000);if(!isFinite(s)||s<0)return"—";if(s<60)return s+"s ago";if(s<3600)return Math.floor(s/60)+"m ago";if(s<86400)return Math.floor(s/3600)+"h ago";return Math.floor(s/86400)+"d ago";}
function badgeCls(st){var s=String(st||"").toLowerCase(),c="badge";if(s==="running")c+=" running";else if(s==="done")c+=" done";else if(s==="merged")c+=" merged";else if(s==="failed")c+=" failed";else if(s==="stopped")c+=" stopped";else if(s==="pending")c+=" pending";return c;}
function trunc(x,n){x=x==null?"":String(x);return x.length<=n?x:x.slice(0,n)+"…";}
function api(url){return fetch(url,{headers:{Accept:"application/json"}}).then(function(r){if(!r.ok)throw new Error("HTTP "+r.status);return r.json();});}
function route(){var h=window.location.hash||"#/";if(h==="#/usage")return{p:"usage"};var m=h.match(/^#\/task\/([^/?#]+)$/);if(m)return{p:"task",id:decodeURIComponent(m[1])};return{p:"dash"};}
function setHash(h){window.location.hash=h;}
var S={filter:"today",tasks:[],rows:Object.create(null),sse:{es:null,t:null,st:"disconnected"},poll:null,timeRefresh:null,detailEvents:[]},R=null;
function shell(){document.body.textContent="";var nav=el("nav",{class:"nav"}),logo=el("a",{class:"nav-logo",href:"#/"},"aid"),dot=el("span",{class:"sse-dot disconnected",title:"Live updates"}),aDash=el("a",{class:"nav-link",href:"#/"},"Dashboard"),aUsage=el("a",{class:"nav-link",href:"#/usage"},"Usage");nav.appendChild(logo);nav.appendChild(dot);nav.appendChild(aDash);nav.appendChild(aUsage);var main=el("main",{class:"main-content",id:"app"}),foot=el("footer",{class:"footer"},"aid ",el("span",{id:"version"})),wrap=el("div",{class:"app-shell"},nav,main,foot);document.body.appendChild(wrap);return{main:main,dot:dot,aDash:aDash,aUsage:aUsage};}
function navActive(rt){if(!R)return;R.aDash.classList.toggle("active",rt.p==="dash");R.aUsage.classList.toggle("active",rt.p==="usage");}
function sseDot(st){S.sse.st=st;if(!R)return;R.dot.className="sse-dot"+(st==="disconnected"?" disconnected":st==="reconnecting"?" reconnecting":"");}
function sseStop(){if(S.sse.t){clearTimeout(S.sse.t);S.sse.t=null;}if(S.sse.es){S.sse.es.close();S.sse.es=null;}sseDot("disconnected");}
function sseStart(onUpd){sseStop();sseDot("reconnecting");var es=new EventSource("/api/events");S.sse.es=es;es.addEventListener("task_update",function(e){try{onUpd(JSON.parse(e.data));}catch(_){}});es.addEventListener("heartbeat",function(){});es.onopen=function(){sseDot("connected");};es.onerror=function(){sseStop();sseDot("reconnecting");S.sse.t=setTimeout(function(){sseStart(onUpd);},3000);};}
function pollStop(){if(S.poll){clearInterval(S.poll);S.poll=null;}}
function timeRefreshStop(){if(S.timeRefresh){clearInterval(S.timeRefresh);S.timeRefresh=null;}}
function loading(root){root.textContent="";root.appendChild(el("div",{class:"loading"},"Loading…"));}
function empty(root,msg){root.textContent="";root.appendChild(el("div",{class:"empty-state"},msg||"No tasks found"));}
function summary(tasks){var total=tasks.length,running=0,term=0,ok=0,cost=0;for(var i=0;i<tasks.length;i++){var t=tasks[i]||{},s=String(t.status||"").toLowerCase();if(s==="running")running++;if(s==="done"||s==="merged"||s==="failed"||s==="stopped"){term++;if(s==="done"||s==="merged")ok++;}var c=Number(t.cost_usd);if(isFinite(c)&&c>0)cost+=c;}var rate=term?Math.round(ok/term*100):0;function card(v,l){return el("div",{class:"summary-stat"},el("div",{class:"summary-value"},v),el("div",{class:"summary-label"},l));}return el("div",{class:"summary-bar"},card(String(total),"Total Tasks"),card(String(running),"Running"),card(rate+"%","Success Rate"),card("$"+cost.toFixed(2),"Total Cost"));}
function filterCounts(tasks){var today=0,running=0,cut=Date.now()-86400000;for(var i=0;i<tasks.length;i++){var t=tasks[i]||{},s=String(t.status||"").toLowerCase();if(s==="running")running++;var created=new Date(t.created_at||0).getTime();if(isFinite(created)&&created>=cut)today++;}return{today:today,running:running,all:tasks.length};}
function filterBar(onPick,counts){counts=counts||{today:0,running:0,all:0};function b(id,txt,count){var label=count!=null?txt+" ("+count+")":txt;var x=el("button",{class:"filter-btn",type:"button",dataset:{filterId:id}},label);x.classList.toggle("active",S.filter===id);x.addEventListener("click",function(){onPick(id);});return x;}return el("div",{class:"filter-bar"},b("today","Today",counts.today),b("running","Running",counts.running),b("all","All",counts.all));}
function sortTasks(ts){return (ts||[]).slice().sort(function(a,b){return new Date((b&&b.created_at)||0).getTime()-new Date((a&&a.created_at)||0).getTime();});}
function filteredTasks(tasks,filter){if(filter==="all")return tasks;if(filter==="running")return tasks.filter(function(t){return String(t.status||"").toLowerCase()==="running";});var cut=Date.now()-86400000;return tasks.filter(function(t){var created=new Date(t.created_at||0).getTime();return isFinite(created)&&created>=cut;});}
function table(tasks,onClick){S.rows=Object.create(null);var wrap=el("div",{class:"task-table-wrap"}),tbl=el("table",{class:"task-table"}),thead=el("thead",null,el("tr",null,el("th",null,"ID"),el("th",null,"Agent"),el("th",null,"Status"),el("th",null,"Duration"),el("th",null,"Tokens"),el("th",null,"Cost"),el("th",null,"Model"),el("th",null,"Prompt"))),tbody=el("tbody");tbl.appendChild(thead);tbl.appendChild(tbody);wrap.appendChild(tbl);
for(var i=0;i<tasks.length;i++){var t=tasks[i]||{},id=t.id==null?"":String(t.id),row=el("tr",{class:"task-row",dataset:{id:id},tabindex:"0"});row.addEventListener("click",function(ev){var rid=ev.currentTarget&&ev.currentTarget.dataset?ev.currentTarget.dataset.id:null;if(rid)onClick(rid);});row.addEventListener("keydown",function(ev){if(ev.key==="Enter"&&ev.currentTarget&&ev.currentTarget.dataset){var rid=ev.currentTarget.dataset.id;if(rid)onClick(rid);}});var badge=el("span",{class:badgeCls(t.status)},String(t.status||"—"));if(t.latest_milestone)badge.title=String(t.latest_milestone);var prompt=el("td",{class:"prompt-cell"},trunc(t.prompt||"—",60));if(String(t.status||"").toLowerCase()==="running"&&t.latest_milestone)prompt.appendChild(el("div",{class:"milestone-text"},trunc(t.latest_milestone,80)));if(String(t.status||"").toLowerCase()==="failed"&&t.latest_error)prompt.appendChild(el("div",{style:{color:"var(--error)"}},trunc(t.latest_error,120)));
row.appendChild(el("td",null,id||"—"));row.appendChild(el("td",null,t.custom_agent_name||t.agent||"—"));row.appendChild(el("td",null,badge));row.appendChild(el("td",null,fmtDur(t.duration_ms)));row.appendChild(el("td",null,fmtTok(t.tokens)));row.appendChild(el("td",null,fmtCost(t.cost_usd)));row.appendChild(el("td",null,t.model||"—"));row.appendChild(prompt);tbody.appendChild(row);S.rows[id]=row;}
return wrap;}
function dashUpdate(id,patch){var row=S.rows[id];if(!row)return;var t=null;for(var i=0;i<S.tasks.length;i++)if(String(S.tasks[i].id)===String(id)){t=S.tasks[i];break;}if(!t)return;Object.assign(t,patch||{});var c=row.children;if(!c||c.length<8)return;c[1].textContent=t.custom_agent_name||t.agent||"—";var b=c[2].querySelector(".badge");if(b){b.className=badgeCls(t.status);b.textContent=t.status||"—";b.title=t.latest_milestone?String(t.latest_milestone):"";}c[3].textContent=fmtDur(t.duration_ms);c[4].textContent=fmtTok(t.tokens);c[5].textContent=fmtCost(t.cost_usd);c[6].textContent=t.model||"—";var p=c[7];if(p){p.textContent="";p.appendChild(document.createTextNode(trunc(t.prompt||"—",60)));var run=String(t.status||"").toLowerCase()==="running";if(run&&t.latest_milestone)p.appendChild(el("div",{class:"milestone-text"},trunc(t.latest_milestone,80)));if(!run&&String(t.status||"").toLowerCase()==="failed"&&t.latest_error)p.appendChild(el("div",{style:{color:"var(--error)"}},trunc(t.latest_error,120)));}}
function renderDash(root){loading(root);api("/api/tasks?filter=all").then(function(ts){S.tasks=sortTasks(ts);var display=filteredTasks(S.tasks,S.filter);display=sortTasks(display);var counts=filterCounts(S.tasks);root.textContent="";root.appendChild(summary(display));root.appendChild(filterBar(function(n){S.filter=n;renderDash(root);},counts));if(!display.length)root.appendChild(el("div",{class:"empty-state"},"No tasks found"));else root.appendChild(table(display,function(id){setHash("#/task/"+encodeURIComponent(id));}));
sseStart(function(e){var id=e&&(e.id||e.task_id);if(id==null)return;dashUpdate(String(id),{status:e.status,agent:e.agent,tokens:e.tokens,cost_usd:e.cost_usd,duration_ms:e.duration_ms,latest_milestone:e.milestone});var bar=root.querySelector(".summary-bar");if(bar)bar.replaceWith(summary(filteredTasks(S.tasks,S.filter)));});}).catch(function(){sseStop();empty(root,"Failed to load tasks");});}
function meta(task){var pairs=[];function add(l,v){if(v!=null&&v!=="")pairs.push([l,String(v)]);}add("Model",task.model);add("Duration",fmtDur(task.duration_ms));add("Tokens",task.tokens==null?null:fmtTok(task.tokens));add("Prompt tokens",task.prompt_tokens==null?null:fmtTok(task.prompt_tokens));add("Cost",fmtCost(task.cost_usd));add("Exit code",task.exit_code);add("Created",task.created_at);add("Completed",task.completed_at);add("Workgroup",task.workgroup_id);add("Verify",task.verify_status);add("Worktree branch",task.worktree_branch);add("Budget",task.budget);add("Read only",task.read_only==null?null:String(!!task.read_only));return pairs;}
function renderDiff(text){var pre=el("pre",{class:"diff-display"}),lines=text.split("\n");for(var i=0;i<lines.length;i++){var line=lines[i],cls="diff-line";if(line.startsWith("+++")||line.startsWith("---"))cls+=" diff-file";else if(line.startsWith("@@"))cls+=" diff-hunk";else if(line.startsWith("+"))cls+=" diff-add";else if(line.startsWith("-"))cls+=" diff-del";pre.appendChild(el("span",{class:cls},line));pre.appendChild(document.createTextNode("\n"));}return pre;}
function renderTask(root,taskId){loading(root);pollStop();timeRefreshStop();api("/api/tasks/"+encodeURIComponent(taskId)).then(function(task){
var idSpan=el("span",{class:"detail-id detail-id-copy",title:"Copy ID"},String(task.id||taskId));idSpan.addEventListener("click",function(){var t=String(task.id||taskId);navigator.clipboard&&navigator.clipboard.writeText(t).then(function(){var old=idSpan.title;idSpan.title="Copied!";setTimeout(function(){idSpan.title=old;},1500);});});var header=el("div",{class:"detail-header"},el("button",{class:"back-btn",type:"button"},"← Dashboard"),idSpan,el("span",{class:badgeCls(task.status)},String(task.status||"—")),el("span",{class:"detail-meta-item"},String(task.custom_agent_name||task.agent||"—")));header.querySelector(".back-btn").addEventListener("click",function(){setHash("#/");});
var actions=el("div",{class:"detail-actions"}),st=String(task.status||"").toLowerCase();
if(st==="running"){var stopBtn=el("button",{class:"action-btn action-stop",type:"button"},"Stop");stopBtn.addEventListener("click",function(){if(!confirm("Stop task "+task.id+"?"))return;fetch("/api/tasks/"+encodeURIComponent(taskId)+"/stop",{method:"POST"}).then(function(r){return r.json();}).then(function(d){if(d.ok)renderTask(root,taskId);else alert(d.error||"Failed");}).catch(function(){alert("Network error");});});actions.appendChild(stopBtn);}
if(st==="done"||st==="failed"||st==="stopped"){var retryBtn=el("button",{class:"action-btn action-retry",type:"button"},"Retry");retryBtn.addEventListener("click",function(){var fb=prompt("Feedback for retry (optional):","");if(fb===null)return;fetch("/api/tasks/"+encodeURIComponent(taskId)+"/retry",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({feedback:fb||null})}).then(function(r){return r.json();}).then(function(d){if(d.ok&&d.new_task_id)setHash("#/task/"+encodeURIComponent(d.new_task_id));else if(d.ok)renderTask(root,taskId);else alert(d.error||"Failed");}).catch(function(){alert("Network error");});});actions.appendChild(retryBtn);}
if(st==="done"){var mergeBtn=el("button",{class:"action-btn action-merge",type:"button"},"Merge");mergeBtn.addEventListener("click",function(){if(!confirm("Merge task "+task.id+"?"))return;fetch("/api/tasks/"+encodeURIComponent(taskId)+"/merge",{method:"POST"}).then(function(r){return r.json();}).then(function(d){if(d.ok)renderTask(root,taskId);else alert(d.error||"Failed");}).catch(function(){alert("Network error");});});actions.appendChild(mergeBtn);}
var metaBox=el("div",{class:"detail-meta"});meta(task).forEach(function(p){metaBox.appendChild(el("div",{class:"detail-meta-item"},el("div",{class:"detail-meta-label"},p[0]),el("div",{class:"detail-meta-value"},p[1])));});
var tabs=el("div",{class:"detail-tabs"}),content=el("div",{class:"detail-content"}),cur="events";
function tab(id,txt){var b=el("button",{class:"detail-tab",type:"button",dataset:{tabId:id}},txt);b.classList.toggle("active",cur===id);b.addEventListener("click",function(){cur=id;paint();});return b;}
tabs.appendChild(tab("events","Events"));tabs.appendChild(tab("diff","Diff"));tabs.appendChild(tab("prompt","Prompt"));tabs.appendChild(tab("output","Output"));
function setTabActive(){Array.prototype.slice.call(tabs.querySelectorAll(".detail-tab")).forEach(function(b){b.classList.toggle("active",(b.dataset&&b.dataset.tabId)===cur);});}
function paint(){setTabActive();content.textContent="";if(cur==="prompt"){content.appendChild(el("pre",{class:"prompt-display"},String(task.prompt||"—")));return;}if(cur==="output"){content.appendChild(el("pre",{class:"output-display"},"Loading…"));api("/api/tasks/"+encodeURIComponent(taskId)+"/output").then(function(o){content.textContent="";content.appendChild(el("pre",{class:"output-display"},String((o&&o.output)||"—")));}).catch(function(){content.textContent="";content.appendChild(el("pre",{class:"output-display"},"Failed to load output."));});return;}if(cur==="diff"){content.appendChild(el("pre",{class:"diff-display"},"Loading…"));api("/api/tasks/"+encodeURIComponent(taskId)+"/diff").then(function(d){content.textContent="";content.appendChild(renderDiff(String((d&&d.diff)||"No diff available")));}).catch(function(){content.textContent="";content.appendChild(el("pre",{class:"diff-display"},"Failed to load diff."));});return;}
var list=el("div",{class:"event-list"});content.appendChild(list);
function fillEvents(evts){list.textContent="";S.detailEvents=evts||[];S.detailEvents.forEach(function(ev){var kindEl=el("div",{class:"event-kind",dataset:{kind:String(ev.event_kind||"")}},String(ev.event_kind||"—"));list.appendChild(el("div",{class:"event-item"},el("div",{class:"event-time",dataset:{timestamp:ev.timestamp||""}},timeAgo(ev.timestamp)),kindEl,el("div",{class:"event-detail"},String(ev.detail||""))));});list.scrollTop=list.scrollHeight;}
function refreshEventTimes(){list.querySelectorAll(".event-time").forEach(function(node){var ts=node.dataset&&node.dataset.timestamp;if(ts)node.textContent=timeAgo(ts);});}
function load(){api("/api/tasks/"+encodeURIComponent(taskId)+"/events").then(function(evts){fillEvents(evts);timeRefreshStop();S.timeRefresh=setInterval(refreshEventTimes,30000);}).catch(function(){list.textContent="";list.appendChild(el("div",{class:"empty-state"},"Failed to load events."));});}
load();if(String(task.status||"").toLowerCase()==="running")S.poll=setInterval(load,5000);}
root.textContent="";root.appendChild(el("div",{class:"detail-page"},header,actions,metaBox,tabs,content));paint();
}).catch(function(){empty(root,"Task not found");});}
document.addEventListener("keydown",function(e){if(e.key==="Escape"&&route().p==="task")setHash("#/");});
function renderUsage(root){loading(root);api("/api/usage").then(function(d){var agents=(d&&d.agents)?d.agents.slice():[];agents.sort(function(a,b){return Number(b.task_count||0)-Number(a.task_count||0);});root.textContent="";if(!agents.length)return empty(root,"No usage data found");var grid=el("div",{class:"usage-grid"});agents.forEach(function(a){var r=a&&a.success_rate!=null?Number(a.success_rate):0;if(!isFinite(r)||r<0)r=0;if(r>1)r=1;var pct=Math.round(r*100);grid.appendChild(el("div",{class:"usage-card"},el("div",{class:"usage-agent"},String(a.agent||"—")),el("div",{class:"usage-stat"},pct+"% success"),el("div",{class:"usage-bar-wrap"},el("div",{class:"usage-bar",style:{width:pct+"%"}})),el("div",{class:"usage-stat"},"Tasks: "+String(a.task_count==null?0:a.task_count)),el("div",{class:"usage-stat"},"Avg cost: "+fmtCost(a.avg_cost))));});root.appendChild(grid);}).catch(function(){empty(root,"Failed to load usage");});}
function render(){if(!R)R=shell();var rt=route();navActive(rt);sseStop();pollStop();timeRefreshStop();var root=R.main;if(!root)return;if(rt.p==="dash")return renderDash(root);if(rt.p==="usage")return renderUsage(root);if(rt.p==="task"&&rt.id)return renderTask(root,rt.id);setHash("#/");}
window.addEventListener("hashchange",render);if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",render);else render();
})();