@lelouchhe/webagent 0.2.6 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/config.toml CHANGED
@@ -27,5 +27,11 @@ image_upload = 10_485_760
27
27
  # does not respond within this time the UI resets to idle. Set to 0 to disable.
28
28
  cancel_timeout = 10_000
29
29
 
30
+ # Max recent paths shown in /new menu (default 10, 0 = show all)
31
+ recent_paths = 10
32
+
33
+ # Days to keep unused paths before auto-cleanup (default 30, 0 = keep forever)
34
+ recent_paths_ttl = 30
35
+
30
36
  [push]
31
37
  vapid_subject = "mailto:noreply@example.com"
package/dist/index.html CHANGED
@@ -42,6 +42,6 @@
42
42
  </div>
43
43
  <div id="status-bar"></div>
44
44
 
45
- <script src="/js/app.4FZ67UW4.js"></script>
45
+ <script src="/js/app.2562YGRO.js"></script>
46
46
  </body>
47
47
  </html>
@@ -0,0 +1,10 @@
1
+ var he=class extends Error{name="ApiError";status;constructor(t,s){super(s),this.status=t}};async function U(e,t){let s=await fetch(e,t);if(!s.ok){let a=`HTTP ${s.status}`;try{let r=await s.json();r.error&&(a=String(r.error))}catch{}throw new he(s.status,a)}let i=await s.text();if(i)return JSON.parse(i)}function _(e,t){return U(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})}function je(e){let t={};return e?.cwd&&(t.cwd=e.cwd),e?.inheritFromSessionId&&(t.inheritFromSessionId=e.inheritFromSessionId),_("/api/v1/sessions",t)}function ye(e){return U("/api/v1/sessions/"+e,{method:"DELETE"})}function ie(){return U("/api/v1/sessions")}function M(e){return U("/api/v1/sessions/"+e)}function we(e,t,s){let i={text:t};return s?.length&&(i.images=s),_("/api/v1/sessions/"+e+"/prompt",i)}function Fe(e){return _("/api/v1/sessions/"+e+"/cancel",{})}function oe(e,t,s){return _("/api/v1/sessions/"+e+"/permissions/"+t,{optionId:s})}function re(e,t){return _("/api/v1/sessions/"+e+"/permissions/"+t,{denied:!0})}function G(e,t,s){let i=t.replace(/_/g,"-");return U("/api/v1/sessions/"+e+"/"+i,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:s})})}function Ue(e,t){return U("/api/v1/sessions/"+e+"/title",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:t})})}function We(e,t){return _("/api/v1/sessions/"+e+"/bash",{command:t})}function J(e,t,s){let i={visible:t};return s&&(i.sessionId=s),_("/api/beta/clients/"+e+"/visibility",i)}function Ke(){return _("/api/v1/bridge/reload",{})}var T=e=>document.querySelector(e),o={messages:T("#messages"),input:T("#input"),sendBtn:T("#send-btn"),prompt:T("#input-prompt"),status:T("#status"),sessionInfo:T("#session-info"),attachBtn:T("#attach-btn"),fileInput:T("#file-input"),attachPreview:T("#attach-preview"),themeBtn:T("#theme-btn"),slashMenu:T("#slash-menu"),inputArea:T("#input-area"),statusBar:T("#status-bar")},n={eventSource:null,clientId:null,sessionId:null,sessionSwitchGen:0,sessionCwd:null,sessionTitle:null,awaitingNewSession:!1,configOptions:[],currentAssistantEl:null,currentAssistantText:"",currentThinkingEl:null,currentThinkingText:"",busy:!1,pendingImages:[],currentBashEl:null,followMessages:!0,pendingToolCallIds:new Set,pendingPermissionRequestIds:new Set,pendingPromptDone:!1,turnEnded:!1,newTurnStarted:!1,sentMessageForSession:null,sentBashForSession:null,cancelTimeout:1e4,serverVersion:null,agentName:null,agentVersion:null,_cancelTimerId:null,_onCancelTimeout:null,lastEventSeq:0,oldestLoadedSeq:0,hasMoreHistory:!1,loadingOlderEvents:!1,replayInProgress:!1,replayTarget:null,replayQueue:[],unconfirmedPermissions:new Map,agentReloading:!1,recentPathsLimit:10},Tt={disconnected:"is-disconnected",connecting:"is-connecting",connected:"is-connected"};function O(e,t=e){o.status.textContent="",o.status.className=`status-dot ${Tt[e]}`,o.status.dataset.state=e,o.status.setAttribute("aria-label",t),o.status.setAttribute("title",t)}function x(e){return n.configOptions.find(t=>t.id===e)}function ae(e){return x(e)?.currentValue??null}function Ve(e,t){let s=x(e);s&&(s.currentValue=t)}function ve(e){n.configOptions=e,N(),A()}function N(){o.inputArea.classList.remove("plan-mode","autopilot-mode");let e=ae("mode")||"";e.includes("#plan")?o.inputArea.classList.add("plan-mode"):e.includes("#autopilot")&&o.inputArea.classList.add("autopilot-mode")}function A(){if(!o.statusBar)return;let e=ae("model"),t=n.sessionCwd||"";if(o.statusBar.textContent="",t){e&&o.statusBar.appendChild(document.createTextNode(e+" \xB7 "));let s=document.createElement("span");s.className="status-cwd",s.textContent=t,o.statusBar.appendChild(s)}else e&&(o.statusBar.textContent=e)}function h(e){n.busy=e,e?(o.sendBtn.textContent="^C",o.sendBtn.title="Cancel (Ctrl+C)",o.sendBtn.classList.add("cancel"),o.prompt.classList.add("busy")):(o.sendBtn.textContent="\u21B5",o.sendBtn.title="Send (Enter)",o.sendBtn.classList.remove("cancel"),o.prompt.classList.remove("busy"))}function L({cwd:e,inheritFromSessionId:t=n.sessionId}={}){n.awaitingNewSession=!0,je({cwd:e,inheritFromSessionId:t}).catch(()=>{})}var Ge=[];function Je(e){Ge.push(e)}function w(){for(let e of Ge)e();o.messages.innerHTML="",n.currentAssistantEl=null,n.currentAssistantText="",n.currentThinkingEl=null,n.currentThinkingText="",n.pendingImages.length=0,n.followMessages=!0,n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.unconfirmedPermissions.clear(),n.pendingPromptDone=!1,n.turnEnded=!1,n.newTurnStarted=!1,n._cancelTimerId=null,n.lastEventSeq=0,n.oldestLoadedSeq=0,n.hasMoreHistory=!1,n.loadingOlderEvents=!1,n.replayInProgress=!1,n.replayQueue=[],o.attachPreview.innerHTML="",o.attachPreview.classList.remove("active"),o.input.disabled=!1,o.sendBtn.disabled=!1,o.input.placeholder="Message or ?",h(!1),n.sessionTitle=null,n.sessionCwd=null,n.configOptions=[],ce(null,null),o.statusBar&&(o.statusBar.textContent="")}function Q(){return!n.busy||!n.sessionId?!1:(Fe(n.sessionId).catch(()=>{}),Y(),n.cancelTimeout>0&&(n._cancelTimerId=setTimeout(()=>{n._cancelTimerId=null,n.busy&&(n.turnEnded=!0,h(!1),n._onCancelTimeout?.())},n.cancelTimeout)),!0)}function Y(){n._cancelTimerId!=null&&(clearTimeout(n._cancelTimerId),n._cancelTimerId=null)}function Qe(){return location.hash.slice(1)||null}function le(e){history.replaceState(null,"",`#${e}`)}function ce(e,t){o.sessionInfo.textContent=t||(e?e.slice(0,8)+"\u2026":""),document.title=t||">_"}O("disconnected");var q="idle",Se=[],Et="11.11.1",Ye=`https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@${Et}/build`,kt={dark:"github-dark.min.css",light:"github.min.css",auto:""},$=null;function Ct(){let e=document.documentElement.getAttribute("data-theme")||"auto";return e==="light"?"light":e==="dark"?"dark":window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark"}function be(){let e=Ct(),t=kt[e],s=`${Ye}/styles/${t}`;if($){if($.href===s)return;$.href=s;return}$=document.createElement("link"),$.rel="stylesheet",$.href=s,document.head.appendChild($)}function xt(){return new Promise((e,t)=>{let s=document.createElement("script");s.src=`${Ye}/highlight.min.js`,s.onload=()=>e(),s.onerror=()=>t(new Error("Failed to load highlight.js")),document.head.appendChild(s)})}async function Lt(){if(q==="ready")return!0;if(q==="loading")return new Promise(e=>{Se.push(()=>e(q==="ready"))});q="loading";try{return be(),await xt(),q="ready",Se.splice(0).forEach(t=>t()),!0}catch{return q="idle",Se.splice(0).forEach(t=>t()),!1}}function Mt(e){let t=globalThis.hljs;if(t)for(let s of e.querySelectorAll("pre code"))s.dataset.highlighted||t.highlightElement(s)}function $t(e){for(let t of e.classList)if(t.startsWith("language-"))return t.slice(9);return""}function Pt(e){let t=e.querySelectorAll("pre");for(let s of t){let i=s.querySelector("code");if(!i||s.parentElement?.classList.contains("code-block-wrapper"))continue;let a=$t(i),r=document.createElement("div");r.className="code-block-wrapper";let l=document.createElement("div");l.className="code-toolbar";let d=document.createElement("button");d.className="copy-btn",d.textContent="cp",d.type="button",l.appendChild(d),s.replaceWith(r),r.appendChild(l),r.appendChild(s)}}var Ie=new WeakMap;function ze(e){let t=e.target.closest(".copy-btn");if(!t)return;let i=t.closest(".code-block-wrapper")?.querySelector("code");if(!i)return;let a=i.textContent||"";navigator.clipboard.writeText(a).then(()=>{let r=Ie.get(t);r&&clearTimeout(r),t.textContent="\u2713",t.classList.add("copied");let l=setTimeout(()=>{t.textContent="cp",t.classList.remove("copied"),Ie.delete(t)},1500);Ie.set(t,l)}).catch(()=>{let r=document.createRange();r.selectNodeContents(i);let l=window.getSelection();l?.removeAllRanges(),l?.addRange(r)})}async function z(e){if(!e.querySelector("pre code"))return;Pt(e),await Lt()&&Mt(e)}function Ze(){q==="ready"&&$&&be()}typeof window<"u"&&window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{q==="ready"&&$&&be()});marked.setOptions({breaks:!0,gfm:!0});function de(e){return DOMPurify.sanitize(marked.parse(e))}function D(e,t){let s=document.createElement("div");return s.className=`msg ${e}`,s.innerHTML=e==="user"?p(t).replace(/\n/g,"<br>"):de(t),E(s),s}function c(e){let t=document.createElement("div");return t.className="system-msg",t.textContent=e,E(t),t}function P(){let e=n.currentAssistantEl;n.currentAssistantEl=null,n.currentAssistantText="",e&&typeof e.querySelector=="function"&&z(e)}function k(){if(n.currentThinkingEl){let e=n.currentThinkingEl.querySelector("summary");e.textContent="\u283F thought",e.classList.remove("active"),e.style.animation="none",n.currentThinkingEl=null,n.currentThinkingText=""}}var R=null,Ht=80;function Ee(e){return e.scrollHeight-e.scrollTop-e.clientHeight<Ht}function _t(){n.followMessages=Ee(o.messages)}o.messages.addEventListener("scroll",_t);function Nt(){return n.followMessages||Ee(o.messages)}function E(e,t=!1){if(n.replayTarget)return n.replayTarget.appendChild(e),e;let s=t||Nt();return o.messages.appendChild(e),I(s),e}function et(){j(),R=document.createElement("div"),R.id="waiting",R.innerHTML='<span class="cursor">\u258C</span>',E(R,!0)}function j(){R&&(R.remove(),R=null)}var Te=!1;function I(e){let t=o.messages;if(e||n.followMessages){typeof requestAnimationFrame=="function"?Te||(Te=!0,requestAnimationFrame(()=>{Te=!1,t.scrollTop=t.scrollHeight})):t.scrollTop=t.scrollHeight,n.followMessages=!0;return}n.followMessages=Ee(t)}function p(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}function tt(e){if(!e)return"";let t=new Date(e.endsWith("Z")?e:e+"Z"),s=i=>String(i).padStart(2,"0");return`${t.getFullYear()}-${s(t.getMonth()+1)}-${s(t.getDate())} ${s(t.getHours())}:${s(t.getMinutes())}`}function ke(e){if(typeof e=="string"&&e.includes("*** Begin Patch")){let t=e.split(`
2
+ `),s=[];for(let i of t)i.startsWith("*** Begin Patch")||i.startsWith("*** End Patch")||(i.startsWith("*** Update File:")||i.startsWith("*** Add File:")||i.startsWith("*** Delete File:")?s.push(`<span class="diff-file">${p(i)}</span>`):i.startsWith("@@")?s.push(`<span class="diff-hunk">${p(i)}</span>`):i.startsWith("-")?s.push(`<span class="diff-del">${p(i)}</span>`):i.startsWith("+")?s.push(`<span class="diff-add">${p(i)}</span>`):s.push(p(i)));return s.join(`
3
+ `)}if(e&&typeof e=="object"){let t=[];if(e.path&&t.push(`<span class="diff-file">*** ${p(e.path)}</span>`),e.old_str!=null)for(let s of String(e.old_str).split(`
4
+ `))t.push(`<span class="diff-del">- ${p(s)}</span>`);if(e.new_str!=null)for(let s of String(e.new_str).split(`
5
+ `))t.push(`<span class="diff-add">+ ${p(s)}</span>`);if(e.file_text!=null)for(let s of String(e.file_text).split(`
6
+ `))t.push(`<span class="diff-add">+ ${p(s)}</span>`);return t.length>(e.path?1:0)?t.join(`
7
+ `):null}return null}function Z(e,t=!1){let s=document.createElement("div");return s.className="bash-block",s.innerHTML=`<span class="bash-cmd${t?" running":""}">${p(e)}</span><div class="bash-output"></div>`,s.querySelector(".bash-cmd").addEventListener("click",()=>{let i=s.querySelector(".bash-output");i.style.display==="none"?i.style.display="block":i.classList.contains("has-content")&&(i.style.display="none")}),E(s),t&&(n.currentBashEl=s),s}function X(e,t,s){if(!e)return;let i=e.querySelector(".bash-cmd");i.classList.remove("running");let a="";if(s?a=`[signal: ${s}]`:t!==0&&t!=null&&(a=`[exit: ${t}]`),a){let r=document.createElement("span");r.className=`bash-exit ${t===0?"ok":"fail"}`,r.textContent=a,i.after(r)}e===n.currentBashEl&&(n.currentBashEl=null)}var qt={auto:"\u25D1",light:"\u2600",dark:"\u263E"},Xe=["auto","light","dark"];function nt(){return localStorage.getItem("theme")||"auto"}function st(e){document.documentElement.setAttribute("data-theme",e),o.themeBtn.textContent=qt[e],o.themeBtn.title=`Theme: ${e}`,localStorage.setItem("theme",e);for(let t of it)t()}var it=[];function ot(e){it.push(e)}o.themeBtn.onclick=()=>{let e=nt();st(Xe[(Xe.indexOf(e)+1)%3])};st(nt());var Ce={task_complete:"\u2714",read:"cat",edit:"edit",execute:"exec",search:"find",delete:"rm"},xe="run",Le={pending:"\u25CB",in_progress:"\u25C9",completed:"\u25CF"};function rt(e){return n.replayTarget?.querySelector(`[id="${e}"]`)??document.getElementById(e)}function Bt(e){return n.replayTarget?.querySelector(e)??document.querySelector(e)}var at="webagent_notify_tip_shown",lt="webagent_notify_tip_denied_shown";function Ot(){if(typeof Notification>"u"||n.replayInProgress)return;let e=Notification.permission;if(e!=="granted"){if(e==="denied"){if(localStorage.getItem(lt))return;localStorage.setItem(lt,"1"),c("tip: notifications are blocked \u2014 allow in browser site settings to enable");return}localStorage.getItem(at)||(localStorage.setItem(at,"1"),c("tip: use /notify to enable background notifications"))}}function ue(){n.pendingPromptDone&&(n.pendingToolCallIds.size>0||n.pendingPermissionRequestIds.size>0||(j(),k(),P(),h(!1),n.pendingPromptDone=!1,Ot()))}function At(){for(let e of n.pendingToolCallIds){let t=document.getElementById(`tc-${e}`);if(!t)continue;t.className="tool-call failed";let s=t.querySelector(".icon");s&&(s.textContent="\u2717")}for(let e of n.pendingPermissionRequestIds){let t=document.querySelector(`.permission[data-request-id="${e}"]`);if(!t||!t.querySelector("button"))continue;let i=t.querySelector(".title")?.textContent||"\u26BF";t.innerHTML=`<span style="opacity:0.5">${p(i)} \u2014 cancelled</span>`}n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear()}function Rt(){for(let e of n.pendingToolCallIds){let t=document.getElementById(`tc-${e}`);if(!t)continue;t.className="tool-call completed";let s=t.querySelector(".icon");s&&(s.textContent="\u2713")}n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear()}function $e(e){if(Array.isArray(e))return{events:e,streaming:{thinking:!1,assistant:!1}};let t=e;return{events:t.events??[],streaming:t.streaming??{thinking:!1,assistant:!1},total:typeof t.total=="number"?t.total:void 0,hasMore:typeof t.hasMore=="boolean"?t.hasMore:void 0}}var ct=200;async function B(e){n.replayInProgress=!0,n.replayQueue=[];try{let t=await fetch(`/api/v1/sessions/${e}/events?limit=${ct}`);if(!t.ok)return!1;let s=await t.json(),{events:i,streaming:a,hasMore:r}=$e(s),l=document.createDocumentFragment(),d=He(i);n.replayTarget=l;for(let u=0;u<i.length;u++){let f=JSON.parse(i[u].data);_e(i[u].type,f,i,u,d)}return n.replayTarget=null,o.messages.style.display="none",o.messages.appendChild(l),o.messages.style.display="",i.length&&(n.lastEventSeq=i[i.length-1].seq,n.oldestLoadedSeq=i[0].seq),n.hasMoreHistory=r===!0,n.hasMoreHistory&&Dt(),dt(),Me(i,a),!0}catch{return!1}finally{n.replayTarget=null,n.replayInProgress=!1,pt()}}function Me(e,t){if(t.thinking&&e.length){for(let s=e.length-1;s>=0;s--)if(e[s].type==="thinking"){let i=JSON.parse(e[s].data),a=o.messages.querySelectorAll(".thinking"),r=a[a.length-1];if(r){n.currentThinkingEl=r,n.currentThinkingText=i.text;let l=r.querySelector("summary");l&&(l.textContent="\u283F thinking...",l.classList.add("active"))}break}}if(t.assistant&&e.length){for(let s=e.length-1;s>=0;s--)if(e[s].type==="assistant_message"){let i=JSON.parse(e[s].data),a=o.messages.querySelectorAll(".msg.assistant"),r=a[a.length-1];r&&(n.currentAssistantEl=r,n.currentAssistantText=i.text);break}}}function dt(){let e=o.messages.querySelector("[data-sync-boundary]");e&&e.removeAttribute("data-sync-boundary");let t=o.messages.lastElementChild;t&&t.setAttribute("data-sync-boundary","")}async function Pe(e){n.replayInProgress=!0,n.replayQueue=[];try{let t=`/api/v1/sessions/${e}/events?after=${n.lastEventSeq}`,s=await fetch(t);if(!s.ok)return!1;let i=await s.json(),{events:a,streaming:r}=$e(i),l=o.messages.querySelector("[data-sync-boundary]");if(l)for(;l.nextElementSibling;)l.nextElementSibling.remove();if(n.currentAssistantEl=null,n.currentAssistantText="",n.currentThinkingEl=null,n.currentThinkingText="",n.currentBashEl=null,a.length===0)return Me(a,r),!0;let d=document.createDocumentFragment(),u=He(a);n.replayTarget=d;for(let f=0;f<a.length;f++){let g=JSON.parse(a[f].data);_e(a[f].type,g,a,f,u)}return n.replayTarget=null,o.messages.appendChild(d),n.lastEventSeq=a[a.length-1].seq,dt(),Me(a,r),!0}catch{return!1}finally{n.replayTarget=null,n.replayInProgress=!1,pt()}}var ee=null;function Dt(){pe();let e=document.createElement("div");e.id="history-sentinel",e.className="history-sentinel",e.textContent="\u2191 loading\u2026",o.messages.prepend(e),typeof IntersectionObserver=="function"&&(ee=new IntersectionObserver(t=>{t[0]?.isIntersecting&&!n.loadingOlderEvents&&n.hasMoreHistory&&n.sessionId&&jt(n.sessionId)},{root:o.messages,rootMargin:"200px 0px 0px 0px"}),ee.observe(e))}function pe(){ee&&(ee.disconnect(),ee=null),document.getElementById("history-sentinel")?.remove()}Je(pe);async function jt(e){if(n.loadingOlderEvents||!n.hasMoreHistory||n.oldestLoadedSeq<=0)return!1;n.loadingOlderEvents=!0;try{let t=await fetch(`/api/v1/sessions/${e}/events?limit=${ct}&before=${n.oldestLoadedSeq}`);if(!t.ok||e!==n.sessionId)return!1;let s=await t.json(),{events:i,hasMore:a}=$e(s);if(i.length===0)return n.hasMoreHistory=!1,pe(),!0;let r=document.createDocumentFragment(),l=He(i);n.replayTarget=r;for(let g=0;g<i.length;g++){let S=JSON.parse(i[g].data);_e(i[g].type,S,i,g,l)}n.replayTarget=null;let d=o.messages,u=d.scrollHeight,f=document.getElementById("history-sentinel");return f?f.after(r):d.prepend(r),d.scrollTop+=d.scrollHeight-u,n.oldestLoadedSeq=i[0].seq,n.hasMoreHistory=a===!0,n.hasMoreHistory||pe(),!0}catch{return!1}finally{n.loadingOlderEvents=!1}}function ut(){for(let[e,t]of n.unconfirmedPermissions){let s=document.querySelector(`.permission[data-request-id="${e}"]`);if(!s||!s.querySelector("button")){n.unconfirmedPermissions.delete(e);continue}t.denied?re(t.sessionId,e).catch(()=>{}):oe(t.sessionId,e,t.optionId).catch(()=>{});let i=s.dataset.title?`\u26BF ${p(s.dataset.title)}`:"\u26BF";s.innerHTML=`<span style="opacity:0.5">${i} \u2014 ${p(t.optionName)}</span>`,n.unconfirmedPermissions.delete(e)}}function He(e){let t=new Set;for(let s of e)s.type==="permission_response"&&t.add(JSON.parse(s.data).requestId);return{toolCalls:new Map,permissions:new Map,resolvedPermissions:t,currentBashEl:null}}function _e(e,t,s,i,a){switch(e){case"user_message":{let r=D("user",t.text);if(t.images)for(let l of t.images){let d=document.createElement("img");d.className="user-image",d.src=l.path,r.appendChild(d)}break}case"assistant_message":{let l=(n.replayTarget||o.messages).lastElementChild;if(l&&l.classList.contains("msg")&&l.classList.contains("assistant")){let f=(l.getAttribute("data-raw")||"")+t.text;l.setAttribute("data-raw",f),l.innerHTML=de(f),z(l);break}let d=D("assistant",t.text);d.setAttribute("data-raw",t.text),z(d);break}case"thinking":{let l=(n.replayTarget||o.messages).lastElementChild;if(l&&l.classList.contains("thinking")){let u=l.querySelector(".thinking-content");if(u){u.textContent+=`
8
+ `+t.text;break}}let d=document.createElement("details");d.className="thinking",d.innerHTML=`<summary>\u283F thought</summary><div class="thinking-content">${p(t.text)}</div>`,E(d);break}case"tool_call":{let r=Ce[t.kind]||xe,l=document.createElement("div");l.className="tool-call",l.id=`tc-${t.id}`,l.dataset.kind=t.kind;let d=`<span class="icon">${r}</span> ${p(t.title)}`,u=t.rawInput;u&&u.command?d+=`<span class="tc-detail">$ ${p(u.command)}</span>`:u&&u.path&&(d+=`<span class="tc-detail">${p(u.path)}</span>`),l.innerHTML=d;let f=t.kind==="edit"?ke(u):null;if(f){let S=document.createElement("details");S.innerHTML=`<summary>diff</summary><div class="diff-view">${f}</div>`,l.appendChild(S)}let g=l.querySelector(".tc-detail");g&&l.addEventListener("click",S=>{S.target.closest("details")||g.classList.toggle("expanded")}),E(l),a&&a.toolCalls.set(t.id,l);break}case"tool_call_update":{let r=a?a.toolCalls.get(t.id):rt(`tc-${t.id}`);if(r){let l=t.status==="completed"?"\u2713":t.status==="failed"?"\u2717":"\u2026";r.className=`tool-call ${t.status}`;let d=r.querySelector(".icon");if(d&&(d.textContent=l),t.content&&t.content.length&&!r.querySelector("details")&&!r.querySelector(".tc-summary")){let u=t.content.map(f=>f.type==="terminal"?`[terminal ${f.terminalId}]`:f.content?.text?f.content.text:Array.isArray(f.content)?f.content.map(g=>g.text||"").join(""):"").filter(Boolean).join(`
9
+ `);if(u)if(r.dataset.kind==="task_complete"){let f=document.createElement("div");f.className="tc-summary",f.textContent=u,r.appendChild(f)}else{let f=document.createElement("details");f.innerHTML=`<summary>output</summary><div class="tc-content">${p(u)}</div>`,r.appendChild(f)}}}(t.status==="completed"||t.status==="failed")&&n.pendingToolCallIds.delete(t.id);break}case"plan":{let r=document.createElement("div");r.className="plan",r.innerHTML='<div class="plan-title">\u2015 plan</div>'+(t.entries||[]).map(l=>`<div class="plan-entry">${Le[l.status]||"?"} ${p(l.content)}</div>`).join(""),E(r);break}case"permission_request":{let r=document.createElement("div");r.className="permission",r.dataset.requestId=t.requestId,r.dataset.title=t.title||"",r.innerHTML=`<span class="title" style="opacity:0.5">\u26BF ${p(t.title)}</span> `,!(a?a.resolvedPermissions.has(t.requestId):s&&s.slice(i+1).some(d=>d.type==="permission_response"&&JSON.parse(d.data).requestId===t.requestId))&&t.options&&(r.querySelector(".title").style.opacity="1",t.options.forEach(d=>{let u=document.createElement("button"),f=(d.kind||"").includes("allow");u.className=f?"allow":"deny",u.textContent=d.name,u.onclick=()=>{(d.kind||"").includes("reject")||(d.kind||"").includes("deny")?re(n.sessionId,t.requestId).catch(()=>{}):oe(n.sessionId,t.requestId,d.optionId).catch(()=>{}),r.innerHTML=`<span style="opacity:0.5">\u26BF ${p(t.title)} \u2014 ${p(d.name)}</span>`},r.appendChild(u)})),E(r),a&&a.permissions.set(t.requestId,r);break}case"permission_response":{let r=a?a.permissions.get(t.requestId):Bt(`.permission[data-request-id="${t.requestId}"]`);if(r){let l=r.dataset.title?`\u26BF ${r.dataset.title}`:"\u26BF",d=t.optionName||(t.denied?"denied":"allowed");r.innerHTML=`<span style="opacity:0.5">${p(l)} \u2014 ${p(d)}</span>`}break}case"bash_command":{let r=Z(t.command,!1);a?a.currentBashEl=r:r.id="bash-replay-pending";break}case"bash_result":{let r=a?a.currentBashEl:rt("bash-replay-pending");if(r){if(a||r.removeAttribute("id"),t.output){let l=r.querySelector(".bash-output");l&&(l.textContent=t.output,l.classList.add("has-content"))}X(r,t.code,t.signal),a&&(a.currentBashEl=null)}break}case"prompt_done":n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,h(!1);break}}function pt(){let e=n.replayQueue;n.replayQueue=[];for(let t of e)Ft(t)||C(t)}function Ft(e){switch(e.type){case"tool_call":return!!document.getElementById(`tc-${e.id}`);case"permission_request":return!!document.querySelector(`.permission[data-request-id="${e.requestId}"]`);case"thought_chunk":return!!n.currentThinkingEl;case"message_chunk":return!!n.currentAssistantEl;default:return!1}}function C(e){if(n.replayInProgress){console.log("[handleEvent-DEBUG] QUEUED (replayInProgress):",e.type),n.replayQueue.push(e);return}if(!(e.sessionId&&e.type!=="session_created"&&e.type!=="session_deleted"&&(!n.sessionId||e.sessionId!==n.sessionId)))switch(e.type){case"connected":if(e.cancelTimeout!=null&&(n.cancelTimeout=e.cancelTimeout),e.recentPathsLimit!=null&&(n.recentPathsLimit=e.recentPathsLimit),n.agentReloading){n.agentReloading=!1;let t=e.agent?.name??"",s=e.agent?.version??"",i=t&&s?`${t} ${s}`:"Agent";c(`${i} reloaded.`),h(!1)}break;case"session_created":if(!n.awaitingNewSession&&n.sessionId&&e.sessionId!==n.sessionId)break;if(n.awaitingNewSession=!1,n.sessionId=e.sessionId,n.sessionCwd=e.cwd||n.sessionCwd,n.sessionTitle=e.title||null,e.configOptions?.length&&ve(e.configOptions),le(n.sessionId),n.clientId&&J(n.clientId,!document.hidden,n.sessionId).catch(()=>{}),ce(n.sessionId,n.sessionTitle),O("connected","connected"),o.input.disabled=!1,o.sendBtn.disabled=!1,o.input.placeholder="Message or ?",h(!!e.busyKind),n.newTurnStarted=!1,e.busyKind==="bash"){let t=document.getElementById("bash-replay-pending");t&&(t.removeAttribute("id"),t.querySelector(".bash-cmd")?.classList.add("running"),n.currentBashEl=t)}else n.currentBashEl=null;o.messages.children.length===0&&c(`Session created: ${n.sessionTitle||e.sessionId.slice(0,8)+"\u2026"}`),A();break;case"user_message":{if(n.sentMessageForSession===e.sessionId){n.sentMessageForSession=null;break}if(k(),P(),n.newTurnStarted=!0,n.turnEnded=!1,e.sessionId===n.sessionId){let t=D("user",e.text);if(e.images)for(let s of e.images){let i=document.createElement("img");i.className="user-image",i.src=s.path,t.appendChild(i)}}break}case"message_chunk":if(n.turnEnded)break;j(),k(),n.currentAssistantEl||(n.currentAssistantEl=D("assistant",""),n.currentAssistantText=""),n.currentAssistantText+=e.text,n.currentAssistantEl.innerHTML=de(n.currentAssistantText),I();break;case"thought_chunk":if(n.turnEnded)break;j(),n.currentThinkingEl||(n.currentThinkingEl=document.createElement("details"),n.currentThinkingEl.className="thinking",n.currentThinkingEl.innerHTML='<summary class="active">\u283F thinking...</summary><div class="thinking-content"></div>',n.currentThinkingText="",E(n.currentThinkingEl)),n.currentThinkingText+=e.text,n.currentThinkingEl.querySelector(".thinking-content").textContent=n.currentThinkingText,I();break;case"tool_call":{if(n.turnEnded)break;n.pendingToolCallIds.add(e.id),h(!0),j(),k(),P();let t=Ce[e.kind]||xe,s=document.createElement("div");s.className="tool-call",s.id=`tc-${e.id}`,s.dataset.kind=e.kind;let i=`<span class="icon">${t}</span> ${p(e.title)}`,a=e.rawInput;a&&a.command?i+=`<span class="tc-detail">$ ${p(a.command)}</span>`:a&&a.path&&(i+=`<span class="tc-detail">${p(a.path)}</span>`),s.innerHTML=i;let r=e.kind==="edit"?ke(a):null;if(r){let d=document.createElement("details");d.innerHTML=`<summary>diff</summary><div class="diff-view">${r}</div>`,s.appendChild(d)}let l=s.querySelector(".tc-detail");l&&s.addEventListener("click",d=>{d.target.closest("details")||l.classList.toggle("expanded")}),E(s);break}case"tool_call_update":{let t=document.getElementById(`tc-${e.id}`);if((e.status==="completed"||e.status==="failed")&&n.pendingToolCallIds.delete(e.id),t){let s=e.status==="completed"?"\u2713":e.status==="failed"?"\u2717":"\u2026";t.className=`tool-call ${e.status}`;let i=t.querySelector(".icon");if(i&&(i.textContent=s),e.content&&e.content.length&&!t.querySelector("details")&&!t.querySelector(".tc-summary")){let a=e.content.map(r=>r.type==="terminal"?`[terminal ${r.terminalId}]`:r.content?.text?r.content.text:Array.isArray(r.content)?r.content.map(l=>l.text||"").join(""):"").filter(Boolean).join(`
10
+ `);if(a)if(t.dataset.kind==="task_complete"){let r=document.createElement("div");r.className="tc-summary",r.textContent=a,t.appendChild(r)}else{let r=document.createElement("details");r.innerHTML=`<summary>output</summary><div class="tc-content">${p(a)}</div>`,t.appendChild(r)}}}ue(),I();break}case"plan":{k(),P();let t=document.createElement("div");t.className="plan",t.innerHTML='<div class="plan-title">\u2015 plan</div>'+e.entries.map(s=>`<div class="plan-entry">${Le[s.status]||"?"} ${p(s.content)}</div>`).join(""),E(t);break}case"permission_request":{if(n.turnEnded||document.querySelector(`.permission[data-request-id="${e.requestId}"]`))break;n.pendingPermissionRequestIds.add(e.requestId),h(!0),k();let t=document.createElement("div");t.className="permission",t.dataset.requestId=e.requestId,t.dataset.title=e.title||"",t.innerHTML=`<span class="title">\u26BF ${p(e.title)}</span> `,e.options.forEach(s=>{let i=document.createElement("button"),a=(s.kind||"").includes("allow");i.className=a?"allow":"deny",i.textContent=s.name,i.onclick=()=>{let r=(s.kind||"").includes("reject")||(s.kind||"").includes("deny");r?re(n.sessionId,e.requestId).catch(()=>{}):oe(n.sessionId,e.requestId,s.optionId).catch(()=>{}),n.pendingPermissionRequestIds.delete(e.requestId),n.unconfirmedPermissions.set(e.requestId,{sessionId:n.sessionId,optionId:s.optionId,optionName:s.name,denied:r}),t.innerHTML=`<span style="opacity:0.5">\u26BF ${p(e.title)} \u2014 ${p(s.name)}</span>`,ue()},t.appendChild(i)}),E(t);break}case"permission_response":{console.log("[PERM-DEBUG] permission_response received:",e.requestId,"sessionId:",e.sessionId,"state.sessionId:",n.sessionId),n.pendingPermissionRequestIds.delete(e.requestId),n.unconfirmedPermissions.delete(e.requestId);let t=document.querySelector(`.permission[data-request-id="${e.requestId}"]`);if(console.log("[PERM-DEBUG] permTarget found:",!!t,"sessionMatch:",e.sessionId===n.sessionId),e.sessionId===n.sessionId&&t){let s=t.dataset.title?`\u26BF ${t.dataset.title}`:"\u26BF",i=e.optionName||(e.denied?"denied":"allowed");t.innerHTML=`<span style="opacity:0.5">${p(s)} \u2014 ${p(i)}</span>`,console.log("[PERM-DEBUG] DOM updated successfully")}else console.log("[PERM-DEBUG] DOM NOT updated \u2014 check failed");ue();break}case"bash_command":{if(n.sentBashForSession===e.sessionId){n.sentBashForSession=null;break}e.sessionId===n.sessionId&&(Z(e.command,!0),h(!0));break}case"bash_output":{if(e.sessionId!==n.sessionId)break;if(n.currentBashEl){let t=n.currentBashEl.querySelector(".bash-output");if(e.stream==="stderr"){let s=document.createElement("span");s.className="stderr",s.textContent=e.text,t.appendChild(s)}else t.appendChild(document.createTextNode(e.text));t.classList.add("has-content"),t.scrollTop=t.scrollHeight,I()}break}case"bash_done":{if(e.sessionId!==n.sessionId)break;X(n.currentBashEl,e.code,e.signal),e.error&&c(`err: ${e.error}`),h(!1);break}case"prompt_done":{if(Y(),e.stopReason==="cancelled"&&n.newTurnStarted){n.newTurnStarted=!1,k(),P();break}n.newTurnStarted=!1,e.stopReason==="cancelled"?At():Rt(),n.turnEnded=!0,n.pendingPromptDone=!0,ue();break}case"session_deleted":e.sessionId===n.sessionId&&(c("warn: This session has been deleted."),o.input.disabled=!0,o.sendBtn.disabled=!0,o.input.placeholder="Session deleted");break;case"session_expired":{let t=n.sessionCwd;w(),c("warn: Previous session expired, created new one."),L({cwd:t||void 0});break}case"config_set":{Ve(e.configId,e.value);let t=x(e.configId),s=t?.name||e.configId,i=t?.options.find(a=>a.value===e.value)?.name||e.value;c(`ok: ${s}: ${i}`),e.configId==="mode"&&N(),A();break}case"config_option_update":e.configOptions?.length&&ve(e.configOptions);break;case"session_title_updated":e.sessionId===n.sessionId&&(n.sessionTitle=e.title,ce(n.sessionId,n.sessionTitle));break;case"agent_reloading":n.agentReloading=!0,c("Agent reloading\u2026"),h(!0);break;case"agent_reloading_failed":c(`err: Agent reload failed: ${e.error}`),h(!1);break;case"error":n.awaitingNewSession=!1,n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,j(),k(),P(),c(`err: ${e.message}`),h(!1);break}}async function Ut(){try{let e=await navigator.serviceWorker?.ready;if(!e)return;let t=await fetch("/api/beta/push/vapid-key");if(!t.ok)return;let{publicKey:s}=await t.json(),a=(await e.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:Kt(s)})).toJSON();await fetch("/api/beta/push/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({endpoint:a.endpoint,keys:a.keys,clientId:n.clientId})})}catch(e){console.error("[push] subscribe failed:",e)}}async function Wt(){try{let e=await navigator.serviceWorker?.ready;if(!e)return;let t=await e.pushManager.getSubscription();if(!t)return;let s=t.endpoint;await t.unsubscribe(),await fetch("/api/beta/push/unsubscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({endpoint:s})})}catch(e){console.error("[push] unsubscribe failed:",e)}}function Kt(e){let t="=".repeat((4-e.length%4)%4),s=(e+t).replace(/-/g,"+").replace(/_/g,"/"),i=atob(s),a=new Uint8Array(i.length);for(let r=0;r<i.length;r++)a[r]=i.charCodeAt(r);return a}async function Ne(){try{let e=await navigator.serviceWorker?.ready;return e?await e.pushManager.getSubscription()!==null:!1}catch{return!1}}async function Be(e){let t=e.split(/\s+/),s=t[0].toLowerCase(),i=t.slice(1).join(" ").trim();switch(s){case"/new":{let a=i||n.sessionCwd;return w(),c("Creating new session\u2026"),L({cwd:a||void 0}),!0}case"/pwd":return c(`\u{1F4C1} ${n.sessionCwd||"unknown"}`),!0;case"/rename":{if(!n.sessionId)return c("err: No active session"),!0;if(!i)return c(`Current: ${n.sessionTitle||"(untitled)"}`),c("Usage: /rename <new title>"),!0;try{await Ue(n.sessionId,i),c(`Renamed \u2192 ${i}`)}catch{c("err: Failed to rename session")}return!0}case"/sessions":return c("Removed. Use /switch to see all sessions."),!0;case"/exit":{if(!n.sessionId)return c("warn: No active session"),!0;let a=n.sessionId;try{let l=(await ie()).find(d=>d.id!==a);if(n.busy&&Q(),ye(a).catch(()=>{}),K=null,l){n.sessionSwitchGen++;let d=n.sessionSwitchGen;w(),n.sessionId=null,le(l.id);let[u]=await Promise.all([M(l.id),B(l.id)]);if(d!==n.sessionSwitchGen)return!0;C({type:"session_created",sessionId:u.id,cwd:u.cwd,title:u.title,configOptions:u.configOptions,busyKind:u.busyKind}),I(!0)}else{let d=n.sessionCwd;w(),n.sessionId=null,c("Creating new session\u2026"),L({cwd:d||void 0,inheritFromSessionId:null})}}catch{c("err: Failed to exit session")}return!0}case"/prune":{try{let l=(await(await fetch("/api/v1/sessions")).json()).filter(d=>d.id!==n.sessionId);if(l.length===0)return c("No other sessions to prune."),!0;await Promise.all(l.map(d=>ye(d.id).catch(()=>{}))),K=null,c(`Pruned ${l.length} session(s).`)}catch{c("err: Failed to prune sessions")}return!0}case"/switch":{if(!i)return c("Usage: /switch <title or id prefix>"),!0;try{let r=await(await fetch("/api/v1/sessions")).json(),l=i.toLowerCase(),d=r.find(g=>g.id.startsWith(i)||g.title&&g.title.toLowerCase().includes(l));if(!d)return c(`err: No session matching "${i}"`),!0;n.sessionSwitchGen++;let u=n.sessionSwitchGen;w(),n.sessionId=null;let[f]=await Promise.all([M(d.id),B(d.id)]);if(u!==n.sessionSwitchGen)return!0;C({type:"session_created",sessionId:f.id,cwd:f.cwd,title:f.title,configOptions:f.configOptions,busyKind:f.busyKind}),I(!0)}catch{w(),n.sessionId=null,c("err: Failed to switch session")}return!0}case"/cancel":return n.busy?(Q(),c("^C")):c("Nothing to cancel."),!0;case"/reload":return c("Reloading agent\u2026"),Ke().then(()=>{}).catch(a=>{c(`err: ${a instanceof Error?a.message:"Failed to reload agent"}`)}),!0;case"/help":case"?":{let a=[];n.serverVersion&&a.push(`WebAgent ${n.serverVersion}`),n.agentName&&n.agentVersion&&a.push(`${n.agentName} ${n.agentVersion}`),a.length&&c(a.join(" \xB7 ")),c("? \u2014 Show help"),c("!<command> \u2014 Run bash command");for(let r of ft){let l=r.args?`${r.cmd} ${r.args}`:r.cmd;c(`${l} \u2014 ${r.desc}`)}c("--- Shortcuts ---");for(let r of Vt)c(`${r.key} \u2014 ${r.desc}`);c("--- Tips ---");for(let r of Gt)c(r.text);return!0}case"/model":case"/mode":case"/think":{let r={"/model":"model","/mode":"mode","/think":"reasoning_effort"}[s],l=x(r);if(!i){let S=l?.options.find(se=>se.value===l.currentValue)?.name||l?.currentValue||"unknown";return c(`${l?.name||r}: ${S}`),c(`Type ${s} + space to pick from list`),!0}if(!l)return c(`err: ${s.slice(1)} is not available.`),!0;let d=i.trim(),u=S=>S.toLowerCase().replace(/[\s_]+/g,"-"),f=u(d),g=l.options.find(S=>u(S.value)===f||u(S.name)===f);if(!g){let S=l.options.filter(se=>u(se.value).includes(f)||u(se.name).includes(f));if(S.length===1)g=S[0];else if(S.length>1)return c(`err: Ambiguous "${i}". Type ${s} + space to see options.`),!0}return g?(l.currentValue=g.value,N(),A(),c(`${l.name} \u2192 ${g.name}`),await G(n.sessionId,r,g.value).catch(()=>{}),!0):(c(`err: Unknown "${i}". Type ${s} + space to see options.`),!0)}case"/notify":{if(typeof Notification>"u")return c("err: notifications not supported in this browser"),!0;let a=i.toLowerCase();if(a==="on"){if(Notification.permission==="denied")return c("notify: blocked \u2014 allow in browser site settings to enable"),!0;if(Notification.permission!=="granted"&&await Notification.requestPermission()!=="granted")return c("notify: blocked \u2014 allow in browser site settings to enable"),!0;let l=await Ne();return await Ut(),c(l?"notify: already enabled":"notify: enabled"),!0}if(a==="off")return await Wt(),c("notify: disabled"),!0;let r=Notification.permission;return r==="denied"?c("notify: blocked \u2014 allow in browser site settings to enable"):r==="granted"&&await Ne()?c("notify: enabled"):c("notify: off \u2014 use /notify on to enable"),!0}default:return!1}}var ft=[{cmd:"/cancel",args:"",desc:"Cancel current response"},{cmd:"/exit",args:"",desc:"Close current session"},{cmd:"/help",args:"",desc:"Show help (or type ?)"},{cmd:"/mode",args:"[name]",desc:"Pick or switch mode"},{cmd:"/model",args:"[name]",desc:"Pick or switch model"},{cmd:"/new",args:"[cwd]",desc:"New session"},{cmd:"/notify",args:"[on|off]",desc:"Toggle background notifications"},{cmd:"/prune",args:"",desc:"Delete all sessions except current"},{cmd:"/pwd",args:"",desc:"Show working directory"},{cmd:"/reload",args:"",desc:"Reload agent CLI"},{cmd:"/rename",args:"<new title>",desc:"Rename current session"},{cmd:"/switch",args:"<title|id>",desc:"Switch to session"},{cmd:"/think",args:"[level]",desc:"Pick or switch reasoning effort"}],Vt=[{key:"Enter",desc:"Send message"},{key:"Shift+Enter",desc:"New line"},{key:"^C",desc:"Cancel current response"},{key:"^M",desc:"Cycle mode (Agent \u2192 Plan \u2192 Autopilot)"},{key:"^U",desc:"Upload image"}],Gt=[{text:"Tap \u276F prompt to cycle mode"}],b=-1,m=[],v="commands",te=null,K=null,V=null,qe=!1;function Oe(){let e=o.input.value;if(V!==null){if(e===V)return;V=null}let t=e.match(/^\/new /);if(t){let l=e.slice(t[0].length).toLowerCase();Qt(l);return}let s=e.match(/^\/switch /);if(s){let l=e.slice(s[0].length).toLowerCase();Jt(l,"switch");return}let i=e.match(/^\/(model|mode|think) /);if(i){let d={model:"model",mode:"mode",think:"reasoning_effort"}[i[1]],u=e.slice(i[0].length).toLowerCase();Yt(d,u);return}let a=e.match(/^\/notify /);if(a){let l=e.slice(a[0].length).toLowerCase();Zt(l);return}if(!e.startsWith("/")||e.includes(" ")){y();return}v="commands";let r=e.toLowerCase();if(m=ft.filter(l=>l.cmd.startsWith(r)),m.length===0){y();return}b=0,F(),o.slashMenu.classList.add("active")}async function Jt(e,t="switch"){if(!K)try{K=await(await fetch("/api/v1/sessions")).json(),setTimeout(()=>{K=null},5e3)}catch{return}if(v=t,m=K.filter(i=>e?i.title&&i.title.toLowerCase().includes(e)||i.id.startsWith(e):!0),m.length===0){y();return}b=0,F(),o.slashMenu.classList.add("active")}var fe=null;async function Qt(e){if(!fe)try{let s=n.recentPathsLimit||0,i=s>0?`/api/v1/recent-paths?limit=${s}`:"/api/v1/recent-paths";fe=(await(await fetch(i)).json()).map(r=>({cwd:r.cwd,time:r.last_used_at})),setTimeout(()=>{fe=null},5e3)}catch{return}v="new";let t=fe;if(e&&(t=t.filter(s=>s.cwd.toLowerCase().includes(e))),m=t,m.length===0){y();return}b=0,F(),o.slashMenu.classList.add("active")}function Yt(e,t){let s=x(e);if(!s){y();return}if(v="config",te=e,m=s.options.filter(i=>t?i.value.toLowerCase().includes(t)||i.name.toLowerCase().includes(t):!0),m.length===0){y();return}b=0,F(),o.slashMenu.classList.add("active")}var zt=[{value:"on",name:"on",desc:"Enable background notifications"},{value:"off",name:"off",desc:"Disable background notifications"}];async function Zt(e){if(v="notify",m=zt.filter(i=>e?i.value.includes(e)||i.name.includes(e):!0),m.length===0){y();return}qe=await Ne();let t=qe?"on":"off",s=m.findIndex(i=>i.value===t);b=s>=0?s:0,F(),o.slashMenu.classList.add("active")}function F(){if(v==="new"){let t=(n.sessionCwd||"").toLowerCase();o.slashMenu.innerHTML=m.map((s,i)=>{let a=s.cwd.toLowerCase()===t;return`<div class="slash-item${i===b?" selected":""}" data-idx="${i}"><span class="slash-cmd"${a?' style="color:var(--green)"':""}>${p((a?"* ":" ")+s.cwd)}</span></div>`}).join("")}else if(v==="config"){let t=ae(te)?.toLowerCase()||"";o.slashMenu.innerHTML=m.map((s,i)=>{let a=s.value.toLowerCase()===t;return`<div class="slash-item${i===b?" selected":""}" data-idx="${i}"><span class="slash-cmd"${a?' style="color:var(--green)"':""}>${p((a?"* ":" ")+s.name)}</span></div>`}).join("")}else if(v==="notify"){let t=qe?"on":"off";o.slashMenu.innerHTML=m.map((s,i)=>{let a=s.value===t;return`<div class="slash-item${i===b?" selected":""}" data-idx="${i}"><span class="slash-cmd"${a?' style="color:var(--green)"':""}>${p((a?"* ":" ")+s.name)}</span><span class="slash-desc">${p(s.desc)}</span></div>`}).join("")}else v==="switch"?o.slashMenu.innerHTML=m.map((t,s)=>{let i=t.id===n.sessionId,a=i?"* ":" ",r=t.title||t.id.slice(0,8)+"\u2026",l=tt(t.last_active_at||t.created_at);return`<div class="slash-item${s===b?" selected":""}" data-idx="${s}"><span class="slash-cmd"${i?' style="color:var(--green)"':""}>${p(a+r)}</span><span class="slash-desc">${p(t.cwd)} (${p(l)})</span></div>`}).join(""):o.slashMenu.innerHTML=m.map((t,s)=>{let i=t.args?`${t.cmd} ${t.args}`:t.cmd;return`<div class="slash-item${s===b?" selected":""}" data-idx="${s}"><span class="slash-cmd">${p(i)}</span><span class="slash-desc">${p(t.desc)}</span></div>`}).join("");let e=o.slashMenu.querySelector(".selected");e&&e.scrollIntoView({block:"nearest"})}function y(){o.slashMenu.classList.remove("active"),b=-1,m=[],v="commands",V=o.input.value}function Xt(e){if(!(e<0||e>=m.length)){if(v==="commands"){let t=m[e];o.input.value=t.cmd+(t.args?" ":""),y(),o.input.focus(),["/new","/switch","/model","/mode","/think","/notify"].includes(t.cmd)&&(V=null,Oe())}else if(v==="config"){let t=m[e],s={model:"/model",mode:"/mode",reasoning_effort:"/think"}[te]||`/${te}`;o.input.value=`${s} ${t.name}`,y(),o.input.focus()}else if(v==="notify"){let t=m[e];o.input.value=`/notify ${t.value}`,y(),o.input.focus()}else if(v==="new"){let t=m[e];o.input.value=`/new ${t.cwd}`,y(),o.input.focus()}else if(v==="switch"){let t=m[e];o.input.value=`/switch ${t.title||t.id}`,y(),o.input.focus()}}}async function en(e){if(!(e<0||e>=m.length))if(v==="new"){let t=m[e];o.input.value="",y(),w(),c("Creating new session\u2026"),L({cwd:t.cwd})}else if(v==="config"){let t=m[e],s=te,i=x(s);o.input.value="",y(),i&&(i.currentValue=t.value),N(),A(),c(`${i?.name||s} \u2192 ${t.name}`),await G(n.sessionId,s,t.value).catch(()=>{})}else if(v==="switch"){let t=m[e];o.input.value="",y(),w(),n.sessionId=null,c("Switching\u2026"),Promise.all([M(t.id),B(t.id)]).then(([s,i])=>{C({type:"session_created",sessionId:s.id,cwd:s.cwd,title:s.title,configOptions:s.configOptions,busyKind:s.busyKind}),i&&I(!0)}).catch(()=>{w(),n.sessionId=null,c("err: Failed to switch session")})}else if(v==="notify"){let t=m[e];o.input.value=`/notify ${t.value}`,y(),Be(o.input.value),o.input.value=""}else{let t=m[e];o.input.value=t.cmd+(t.args?" ":""),y(),o.input.focus(),["/new","/switch","/model","/mode","/think","/notify"].includes(t.cmd)&&(V=null,Oe())}}function mt(e){return o.slashMenu.classList.contains("active")?e.key==="ArrowDown"?(b=(b+1)%m.length,F(),!0):e.key==="ArrowUp"?(b=(b-1+m.length)%m.length,F(),!0):e.key==="Tab"?(Xt(b),!0):!1:!1}o.slashMenu.addEventListener("mousedown",e=>{e.preventDefault();let t=e.target.closest(".slash-item");t&&en(Number(t.dataset.idx))});o.input.addEventListener("input",()=>{Oe(),o.inputArea.classList.toggle("bash-mode",o.input.value.startsWith("!"))});function gt(e){return new Promise(t=>{let s=new FileReader;s.onload=()=>{let i=s.result.split(",")[1];t({data:i,mimeType:e.type,previewUrl:s.result})},s.readAsDataURL(e)})}function ht(e){n.pendingImages.push(e),me(),o.input.focus()}function me(){if(o.attachPreview.innerHTML="",n.pendingImages.length===0){o.attachPreview.classList.remove("active");return}o.attachPreview.classList.add("active"),n.pendingImages.forEach((e,t)=>{let s=document.createElement("span");s.className="attach-thumb",s.innerHTML=`<img src="${e.previewUrl}"><button class="remove">\xD7</button>`,s.querySelector(".remove").addEventListener("click",()=>{n.pendingImages.splice(t,1),me()}),o.attachPreview.appendChild(s)})}o.attachBtn.onclick=()=>o.fileInput.click();o.fileInput.onchange=async()=>{for(let e of o.fileInput.files)e.type.startsWith("image/")&&ht(await gt(e));o.fileInput.value=""};o.input.addEventListener("paste",async e=>{for(let t of e.clipboardData.items)t.type.startsWith("image/")&&(e.preventDefault(),ht(await gt(t.getAsFile())))});var H=document.createElement("div");H.id="lightbox-overlay";var ne=document.createElement("img");H.appendChild(ne);document.body.appendChild(H);var ge=1,tn=.5,nn=5;function sn(){return H.classList.contains("active")}function on(e){ne.src=e,ge=1,ne.style.transform="",H.classList.add("active")}function yt(){H.classList.remove("active"),ne.src=""}document.getElementById("messages").addEventListener("click",e=>{let t=e.target;t.tagName==="IMG"&&t.classList.contains("user-image")&&on(t.src)});H.addEventListener("click",e=>{e.target===H&&yt()});document.addEventListener("keydown",e=>{e.key==="Escape"&&sn()&&yt()});H.addEventListener("wheel",e=>{e.preventDefault();let t=e.deltaY>0?-.15:.15;ge=Math.min(nn,Math.max(tn,ge+t)),ne.style.transform=`scale(${ge})`},{passive:!1});function Ae(){return n.clientId!==null}n._onCancelTimeout=()=>c("warn: Agent not responding to cancel");function wt(){let e=o.input.value.trim();if(!e&&n.pendingImages.length===0)return;if((e.startsWith("/")||e==="?"||e.startsWith("? "))&&n.pendingImages.length===0){o.input.value="",o.input.style.height="auto",It(),Be(e);return}if(e.startsWith("!")&&n.pendingImages.length===0){let i=e.slice(1).trim();if(!i)return;if(!n.sessionId){c("warn: Session not ready yet, please wait\u2026");return}if(!Ae()){c("warn: Not connected, please retry");return}o.input.value="",o.input.style.height="auto",o.inputArea.classList.remove("bash-mode"),Z(i,!0),n.sentBashForSession=n.sessionId,We(n.sessionId,i).catch(()=>{}),h(!0);return}if(n.busy)return;if(o.input.value="",o.input.style.height="auto",o.inputArea.classList.remove("bash-mode"),!n.sessionId){c("warn: Session not ready yet, please wait\u2026");return}if(!Ae()){c("warn: Not connected, please retry");return}let t=D("user",e||"(image)");for(let i of n.pendingImages){let a=document.createElement("img");a.className="user-image",a.src=i.previewUrl,t.appendChild(a)}let s=n.pendingImages.slice();n.pendingImages.length=0,me(),s.length>0?Promise.all(s.map(i=>fetch(`/api/v1/sessions/${n.sessionId}/images`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({data:i.data,mimeType:i.mimeType})}).then(a=>a.json()).then(a=>({data:i.data,mimeType:i.mimeType,path:a.url})))).then(i=>{if(!Ae()){t.remove(),c("warn: Not connected, please retry"),h(!1);return}we(n.sessionId,e||"What is in this image?",i.map(a=>({data:a.data,mimeType:a.mimeType,path:a.path}))).catch(()=>{})}):we(n.sessionId,e).catch(()=>{}),n.turnEnded=!1,n.sentMessageForSession=n.sessionId,h(!0),et()}function vt(){Q()&&c("^C")}function St(){let e=o.input.value.trim();return e.startsWith("/")||e.startsWith("!")||e==="?"||e.startsWith("? ")}function It(){n.busy&&(St()?(o.sendBtn.textContent="\u21B5",o.sendBtn.title="Send (Enter)",o.sendBtn.classList.remove("cancel")):(o.sendBtn.textContent="^C",o.sendBtn.title="Cancel (Ctrl+C)",o.sendBtn.classList.add("cancel")))}o.sendBtn.onclick=()=>{n.busy&&!St()?vt():wt()};o.input.addEventListener("keydown",e=>{if(mt(e)){e.preventDefault();return}if(e.key==="Enter"&&!e.shiftKey){e.preventDefault(),y(),wt();return}if(e.key==="u"&&(e.ctrlKey||e.metaKey)&&!e.shiftKey){e.preventDefault(),o.fileInput.click();return}});document.addEventListener("keydown",e=>{if(e.key==="c"&&(e.ctrlKey||e.metaKey)&&!e.shiftKey&&n.busy&&!(window.getSelection()?.toString()||o.input.selectionStart!==o.input.selectionEnd)){e.preventDefault(),vt();return}e.key==="Escape"&&o.slashMenu.classList.contains("active")&&(e.preventDefault(),y(),o.input.focus())});function bt(){let e=x("mode");if(!e||!e.options.length)return;let t=e.options.findIndex(i=>i.value===e.currentValue),s=e.options[(t+1)%e.options.length];e.currentValue=s.value,G(n.sessionId,"mode",s.value).catch(()=>{}),c(`Mode \u2192 ${s.name}`),N()}document.addEventListener("keydown",e=>{e.key==="m"&&(e.ctrlKey||e.metaKey)&&!e.shiftKey&&(e.preventDefault(),bt())});o.prompt.addEventListener("click",bt);o.input.addEventListener("input",It);o.input.addEventListener("input",()=>{o.input.style.height="auto",o.input.style.height=Math.min(o.input.scrollHeight,200)+"px"});async function rn(e){try{let t=await navigator.serviceWorker?.ready;if(!t)return;let s=await t.pushManager.getSubscription();if(!s)return;await fetch("/api/beta/push/register-client",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({clientId:e,endpoint:s.endpoint})})}catch{}}function De(){O("connecting","connecting");let e=new EventSource("/api/v1/events/stream");n.eventSource=e,e.onmessage=async t=>{let s=JSON.parse(t.data);s.type==="connected"&&s.clientId&&(n.clientId=s.clientId,J(s.clientId,!document.hidden,n.sessionId??void 0).catch(()=>{}),rn(s.clientId),!s.agent)||C(s)},e.onerror=()=>{e.close(),ln(),setTimeout(De,3e3)},an()}async function an(){O("connecting","session loading");let e=n.sessionSwitchGen,t=Qe();if(t&&t===n.sessionId&&n.lastEventSeq>0){if(await Re(t,!0,e),e!==n.sessionSwitchGen)return;ut(),I(!1);return}if(t){if(w(),await Re(t,!1,e),e!==n.sessionSwitchGen)return;I(!0);return}try{let s=await ie();if(e!==n.sessionSwitchGen)return;if(s.length>0){if(w(),await Re(s[0].id,!1,e),e!==n.sessionSwitchGen)return;I(!0);return}}catch{}e===n.sessionSwitchGen&&L()}async function Re(e,t,s){if(t){try{let i=await M(e);if(s!==n.sessionSwitchGen)return;C({type:"session_created",sessionId:i.id,cwd:i.cwd,title:i.title,configOptions:i.configOptions,busyKind:i.busyKind})}catch{if(s!==n.sessionSwitchGen)return;let i=n.sessionCwd;w(),c("warn: Previous session expired, created new one."),L({cwd:i||void 0});return}if(s!==n.sessionSwitchGen)return;await Pe(e)}else{n.sessionId=null;let i=B(e),a;try{let[r,l]=await Promise.all([M(e),i]);if(s!==n.sessionSwitchGen)return;a=r,l||c("warn: Failed to load history.")}catch{if(s!==n.sessionSwitchGen)return;let r=n.sessionCwd;w(),c("warn: Previous session expired, created new one."),L({cwd:r||void 0});return}C({type:"session_created",sessionId:a.id,cwd:a.cwd,title:a.title,configOptions:a.configOptions,busyKind:a.busyKind})}}function ln(){O("disconnected","disconnected"),n.eventSource=null,n.clientId=null,k(),P(),n.currentBashEl&&X(n.currentBashEl,null,"disconnected"),n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,n.turnEnded=!1,Y(),h(!1)}document.addEventListener("visibilitychange",()=>{n.clientId&&J(n.clientId,!document.hidden,n.sessionId??void 0).catch(()=>{}),!document.hidden&&n.sessionId&&n.lastEventSeq>0&&!n.replayInProgress&&Pe(n.sessionId).then(()=>I(!1))});o.messages.addEventListener("click",ze);ot(Ze);De();fetch("/api/v1/version").then(e=>e.json()).then(e=>{typeof e.server=="string"&&(n.serverVersion=e.server);let t=e.agent;t&&(n.agentName=t.name??null,n.agentVersion=t.version??null)}).catch(()=>{});"serviceWorker"in navigator&&(navigator.serviceWorker.register("/sw.js"),navigator.serviceWorker.addEventListener("message",e=>{if(e.data?.type==="navigate"&&e.data.sessionId){let t=e.data.sessionId;if(n.sessionId===t)return;n.sessionSwitchGen++;let s=n.sessionSwitchGen;history.replaceState(null,"",`#${t}`),w(),n.sessionId=null,c("Switching\u2026"),Promise.all([M(t),B(t)]).then(([i,a])=>{s===n.sessionSwitchGen&&(C({type:"session_created",sessionId:i.id,cwd:i.cwd,title:i.title,configOptions:i.configOptions,busyKind:i.busyKind}),a&&I(!0))}).catch(()=>{w(),n.sessionId=null,c("err: Failed to switch session")})}}));
package/lib/config.js CHANGED
@@ -11,10 +11,14 @@ const ConfigSchema = z.object({
11
11
  bash_output: z.number().int().positive().default(1_048_576), // 1 MB
12
12
  image_upload: z.number().int().positive().default(10_485_760), // 10 MB
13
13
  cancel_timeout: z.number().int().nonnegative().default(10_000), // 10s; 0 disables
14
+ recent_paths: z.number().int().nonnegative().default(10), // /new menu display limit; 0 = show all
15
+ recent_paths_ttl: z.number().int().nonnegative().default(30), // days before auto-cleanup; 0 = keep forever
14
16
  }).default({
15
17
  bash_output: 1_048_576,
16
18
  image_upload: 10_485_760,
17
19
  cancel_timeout: 10_000,
20
+ recent_paths: 10,
21
+ recent_paths_ttl: 30,
18
22
  }),
19
23
  push: z.object({
20
24
  vapid_subject: z.string().default("mailto:webagent@localhost"),
package/lib/daemon.js CHANGED
@@ -8,6 +8,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
8
8
  // ---------------------------------------------------------------------------
9
9
  const PID_FILE = "webagent.pid";
10
10
  const LOG_FILE = "webagent.log";
11
+ const LOG_MAX_LINES = 5_000;
11
12
  const RESTART_DELAY_INITIAL = 1_000;
12
13
  const RESTART_DELAY_MAX = 30_000;
13
14
  const STABLE_THRESHOLD_MS = 60_000;
@@ -85,6 +86,16 @@ async function cmdStart(pidFile, logFile, args) {
85
86
  }
86
87
  const resolved = resolveArgs(args);
87
88
  const daemonJs = join(__dirname, "daemon.js");
89
+ // Truncate log to last LOG_MAX_LINES before starting
90
+ if (existsSync(logFile)) {
91
+ try {
92
+ const lines = readFileSync(logFile, "utf-8").split("\n");
93
+ if (lines.length > LOG_MAX_LINES) {
94
+ writeFileSync(logFile, lines.slice(-LOG_MAX_LINES).join("\n"));
95
+ }
96
+ }
97
+ catch { /* best-effort */ }
98
+ }
88
99
  const log = openSync(logFile, "a");
89
100
  const child = spawn(process.execPath, [daemonJs, "__supervisor", ...resolved], { detached: true, stdio: ["ignore", log, log], cwd: process.cwd(), windowsHide: true });
90
101
  child.unref();
@@ -4,6 +4,7 @@ export function handleAgentEvent(event, sessions, store, bridge, config, sseMana
4
4
  switch (event.type) {
5
5
  case "connected":
6
6
  event.cancelTimeout = config.cancelTimeout;
7
+ event.recentPathsLimit = config.recentPathsLimit;
7
8
  if (event.agent)
8
9
  sessions.agentInfo = event.agent;
9
10
  break;
package/lib/routes.js CHANGED
@@ -58,6 +58,7 @@ export function createRequestHandler(deps) {
58
58
  version: "v1",
59
59
  endpoints: {
60
60
  sessions: "/api/v1/sessions",
61
+ paths: "/api/v1/recent-paths",
61
62
  config: "/api/v1/config",
62
63
  events_stream: "/api/v1/events/stream",
63
64
  prompt: "/api/beta/prompt",
@@ -79,9 +80,23 @@ export function createRequestHandler(deps) {
79
80
  json(res, 200, {
80
81
  configOptions: sessions?.cachedConfigOptions ?? [],
81
82
  cancelTimeout: deps.limits.cancel_timeout ?? 0,
83
+ recentPathsLimit: deps.limits.recent_paths ?? 10,
82
84
  });
83
85
  return;
84
86
  }
87
+ // GET /api/v1/recent-paths
88
+ if (url.startsWith("/api/v1/recent-paths") && req.method === "GET") {
89
+ const params = new URLSearchParams(url.split("?")[1] ?? "");
90
+ const limitParam = params.get("limit");
91
+ const limit = limitParam != null ? Math.max(0, parseInt(limitParam, 10)) : 0;
92
+ const ttlDays = deps.limits.recent_paths_ttl ?? 30;
93
+ const paths = store.listRecentPaths({
94
+ limit: isNaN(limit) ? 0 : limit,
95
+ ttlDays,
96
+ });
97
+ json(res, 200, paths);
98
+ return;
99
+ }
85
100
  // GET /api/v1/version
86
101
  if (url === "/api/v1/version" && req.method === "GET") {
87
102
  json(res, 200, {
@@ -265,6 +280,7 @@ export function createRequestHandler(deps) {
265
280
  const storedImages = body.images?.map(i => ({ path: i.path, mimeType: i.mimeType }));
266
281
  store.saveEvent(sessionId, "user_message", { text: body.text, ...(storedImages?.length && { images: storedImages }) });
267
282
  store.updateSessionLastActive(sessionId);
283
+ store.touchRecentPath(session.cwd);
268
284
  const userMsgEvent = { type: "user_message", sessionId, text: body.text, images: storedImages };
269
285
  sseManager.broadcast(userMsgEvent);
270
286
  // Generate title (fire-and-forget)
package/lib/server.js CHANGED
@@ -11,6 +11,14 @@ import { createRequestHandler } from "./routes.js";
11
11
  import { handleAgentEvent } from "./event-handler.js";
12
12
  import { PushService } from "./push-service.js";
13
13
  import { SseManager } from "./sse-manager.js";
14
+ // Prefix all console output with ISO-ish timestamps (YYYY-MM-DD HH:MM:SS)
15
+ for (const method of ["log", "error", "warn"]) {
16
+ const orig = console[method].bind(console);
17
+ console[method] = (...args) => {
18
+ const ts = new Date().toLocaleString("sv-SE", { hour12: false }).replace(",", "");
19
+ orig(ts, ...args);
20
+ };
21
+ }
14
22
  const config = loadConfig();
15
23
  const __dirname = fileURLToPath(new URL(".", import.meta.url));
16
24
  const PUBLIC_DIR = join(__dirname, "..", config.public_dir);
@@ -49,7 +57,10 @@ const server = createServer(createRequestHandler({
49
57
  async function initBridge() {
50
58
  const b = new AgentBridge(config.agent_cmd);
51
59
  b.on("event", (event) => {
52
- handleAgentEvent(event, sessions, store, b, { cancelTimeout: config.limits.cancel_timeout }, sseManager, pushService);
60
+ handleAgentEvent(event, sessions, store, b, {
61
+ cancelTimeout: config.limits.cancel_timeout,
62
+ recentPathsLimit: config.limits.recent_paths,
63
+ }, sseManager, pushService);
53
64
  });
54
65
  await b.start();
55
66
  bridge = b;
package/lib/store.js CHANGED
@@ -58,6 +58,22 @@ export class Store {
58
58
  if (!colNames.has("source")) {
59
59
  this.db.exec("ALTER TABLE sessions ADD COLUMN source TEXT NOT NULL DEFAULT 'auto'");
60
60
  }
61
+ // recent_paths: LRU path list for /new menu
62
+ const rpExists = this.db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='recent_paths'").get();
63
+ if (!rpExists) {
64
+ this.db.exec(`
65
+ CREATE TABLE recent_paths (
66
+ cwd TEXT PRIMARY KEY,
67
+ last_used_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%d %H:%M:%f', 'now'))
68
+ );
69
+ `);
70
+ // Backfill from existing sessions
71
+ this.db.exec(`
72
+ INSERT OR IGNORE INTO recent_paths (cwd, last_used_at)
73
+ SELECT cwd, MAX(COALESCE(last_active_at, created_at))
74
+ FROM sessions GROUP BY cwd;
75
+ `);
76
+ }
61
77
  }
62
78
  createSession(id, cwd, source = "auto") {
63
79
  this.db.prepare("INSERT INTO sessions (id, cwd, source) VALUES (?, ?, ?)").run(id, cwd, source);
@@ -168,6 +184,26 @@ export class Store {
168
184
  getAllSubscriptions() {
169
185
  return this.db.prepare("SELECT * FROM push_subscriptions").all();
170
186
  }
187
+ // --- Recent paths ---
188
+ touchRecentPath(cwd) {
189
+ this.db.prepare(`INSERT INTO recent_paths (cwd, last_used_at)
190
+ VALUES (?, strftime('%Y-%m-%d %H:%M:%f', 'now'))
191
+ ON CONFLICT(cwd) DO UPDATE SET last_used_at = strftime('%Y-%m-%d %H:%M:%f', 'now')`).run(cwd);
192
+ }
193
+ listRecentPaths(opts) {
194
+ const ttl = opts?.ttlDays ?? 0;
195
+ if (ttl > 0) {
196
+ this.db.prepare("DELETE FROM recent_paths WHERE last_used_at < strftime('%Y-%m-%d %H:%M:%f', 'now', ?)").run(`-${ttl} days`);
197
+ }
198
+ const limit = opts?.limit;
199
+ if (limit && limit > 0) {
200
+ return this.db.prepare("SELECT cwd, last_used_at FROM recent_paths ORDER BY last_used_at DESC LIMIT ?").all(limit);
201
+ }
202
+ return this.db.prepare("SELECT cwd, last_used_at FROM recent_paths ORDER BY last_used_at DESC").all();
203
+ }
204
+ deleteRecentPath(cwd) {
205
+ this.db.prepare("DELETE FROM recent_paths WHERE cwd = ?").run(cwd);
206
+ }
171
207
  close() {
172
208
  this.db.close();
173
209
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lelouchhe/webagent",
3
- "version": "0.2.6",
3
+ "version": "0.3.0",
4
4
  "description": "A terminal-style web UI for ACP-compatible agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,10 +0,0 @@
1
- var me=class extends Error{name="ApiError";status;constructor(t,s){super(s),this.status=t}};async function W(e,t){let s=await fetch(e,t);if(!s.ok){let a=`HTTP ${s.status}`;try{let r=await s.json();r.error&&(a=String(r.error))}catch{}throw new me(s.status,a)}let i=await s.text();if(i)return JSON.parse(i)}function N(e,t){return W(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})}function Re(e){let t={};return e?.cwd&&(t.cwd=e.cwd),e?.inheritFromSessionId&&(t.inheritFromSessionId=e.inheritFromSessionId),N("/api/v1/sessions",t)}function ge(e){return W("/api/v1/sessions/"+e,{method:"DELETE"})}function ie(){return W("/api/v1/sessions")}function $(e){return W("/api/v1/sessions/"+e)}function he(e,t,s){let i={text:t};return s?.length&&(i.images=s),N("/api/v1/sessions/"+e+"/prompt",i)}function De(e){return N("/api/v1/sessions/"+e+"/cancel",{})}function oe(e,t,s){return N("/api/v1/sessions/"+e+"/permissions/"+t,{optionId:s})}function re(e,t){return N("/api/v1/sessions/"+e+"/permissions/"+t,{denied:!0})}function G(e,t,s){let i=t.replace(/_/g,"-");return W("/api/v1/sessions/"+e+"/"+i,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:s})})}function je(e,t){return W("/api/v1/sessions/"+e+"/title",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:t})})}function Fe(e,t){return N("/api/v1/sessions/"+e+"/bash",{command:t})}function J(e,t,s){let i={visible:t};return s&&(i.sessionId=s),N("/api/beta/clients/"+e+"/visibility",i)}function Ue(){return N("/api/v1/bridge/reload",{})}var T=e=>document.querySelector(e),o={messages:T("#messages"),input:T("#input"),sendBtn:T("#send-btn"),prompt:T("#input-prompt"),status:T("#status"),sessionInfo:T("#session-info"),attachBtn:T("#attach-btn"),fileInput:T("#file-input"),attachPreview:T("#attach-preview"),themeBtn:T("#theme-btn"),slashMenu:T("#slash-menu"),inputArea:T("#input-area"),statusBar:T("#status-bar")},n={eventSource:null,clientId:null,sessionId:null,sessionSwitchGen:0,sessionCwd:null,sessionTitle:null,awaitingNewSession:!1,configOptions:[],currentAssistantEl:null,currentAssistantText:"",currentThinkingEl:null,currentThinkingText:"",busy:!1,pendingImages:[],currentBashEl:null,followMessages:!0,pendingToolCallIds:new Set,pendingPermissionRequestIds:new Set,pendingPromptDone:!1,turnEnded:!1,newTurnStarted:!1,sentMessageForSession:null,sentBashForSession:null,cancelTimeout:1e4,serverVersion:null,agentName:null,agentVersion:null,_cancelTimerId:null,_onCancelTimeout:null,lastEventSeq:0,oldestLoadedSeq:0,hasMoreHistory:!1,loadingOlderEvents:!1,replayInProgress:!1,replayTarget:null,replayQueue:[],unconfirmedPermissions:new Map,agentReloading:!1},bt={disconnected:"is-disconnected",connecting:"is-connecting",connected:"is-connected"};function A(e,t=e){o.status.textContent="",o.status.className=`status-dot ${bt[e]}`,o.status.dataset.state=e,o.status.setAttribute("aria-label",t),o.status.setAttribute("title",t)}function x(e){return n.configOptions.find(t=>t.id===e)}function ae(e){return x(e)?.currentValue??null}function We(e,t){let s=x(e);s&&(s.currentValue=t)}function ye(e){n.configOptions=e,q(),R()}function q(){o.inputArea.classList.remove("plan-mode","autopilot-mode");let e=ae("mode")||"";e.includes("#plan")?o.inputArea.classList.add("plan-mode"):e.includes("#autopilot")&&o.inputArea.classList.add("autopilot-mode")}function R(){if(!o.statusBar)return;let e=ae("model"),t=n.sessionCwd||"";if(o.statusBar.textContent="",t){e&&o.statusBar.appendChild(document.createTextNode(e+" \xB7 "));let s=document.createElement("span");s.className="status-cwd",s.textContent=t,o.statusBar.appendChild(s)}else e&&(o.statusBar.textContent=e)}function h(e){n.busy=e,e?(o.sendBtn.textContent="^C",o.sendBtn.title="Cancel (Ctrl+C)",o.sendBtn.classList.add("cancel"),o.prompt.classList.add("busy")):(o.sendBtn.textContent="\u21B5",o.sendBtn.title="Send (Enter)",o.sendBtn.classList.remove("cancel"),o.prompt.classList.remove("busy"))}function L({cwd:e,inheritFromSessionId:t=n.sessionId}={}){n.awaitingNewSession=!0,Re({cwd:e,inheritFromSessionId:t}).catch(()=>{})}var Ke=[];function Ve(e){Ke.push(e)}function w(){for(let e of Ke)e();o.messages.innerHTML="",n.currentAssistantEl=null,n.currentAssistantText="",n.currentThinkingEl=null,n.currentThinkingText="",n.pendingImages.length=0,n.followMessages=!0,n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.unconfirmedPermissions.clear(),n.pendingPromptDone=!1,n.turnEnded=!1,n.newTurnStarted=!1,n._cancelTimerId=null,n.lastEventSeq=0,n.oldestLoadedSeq=0,n.hasMoreHistory=!1,n.loadingOlderEvents=!1,n.replayInProgress=!1,n.replayQueue=[],o.attachPreview.innerHTML="",o.attachPreview.classList.remove("active"),o.input.disabled=!1,o.sendBtn.disabled=!1,o.input.placeholder="Message or ?",h(!1),n.sessionTitle=null,n.sessionCwd=null,n.configOptions=[],le(null,null),o.statusBar&&(o.statusBar.textContent="")}function Q(){return!n.busy||!n.sessionId?!1:(De(n.sessionId).catch(()=>{}),Y(),n.cancelTimeout>0&&(n._cancelTimerId=setTimeout(()=>{n._cancelTimerId=null,n.busy&&(n.turnEnded=!0,h(!1),n._onCancelTimeout?.())},n.cancelTimeout)),!0)}function Y(){n._cancelTimerId!=null&&(clearTimeout(n._cancelTimerId),n._cancelTimerId=null)}function Ge(){return location.hash.slice(1)||null}function Je(e){history.replaceState(null,"",`#${e}`)}function le(e,t){o.sessionInfo.textContent=t||(e?e.slice(0,8)+"\u2026":""),document.title=t||">_"}A("disconnected");var B="idle",we=[],Tt="11.11.1",Qe=`https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@${Tt}/build`,Et={dark:"github-dark.min.css",light:"github.min.css",auto:""},P=null;function kt(){let e=document.documentElement.getAttribute("data-theme")||"auto";return e==="light"?"light":e==="dark"?"dark":window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark"}function Se(){let e=kt(),t=Et[e],s=`${Qe}/styles/${t}`;if(P){if(P.href===s)return;P.href=s;return}P=document.createElement("link"),P.rel="stylesheet",P.href=s,document.head.appendChild(P)}function Ct(){return new Promise((e,t)=>{let s=document.createElement("script");s.src=`${Qe}/highlight.min.js`,s.onload=()=>e(),s.onerror=()=>t(new Error("Failed to load highlight.js")),document.head.appendChild(s)})}async function xt(){if(B==="ready")return!0;if(B==="loading")return new Promise(e=>{we.push(()=>e(B==="ready"))});B="loading";try{return Se(),await Ct(),B="ready",we.splice(0).forEach(t=>t()),!0}catch{return B="idle",we.splice(0).forEach(t=>t()),!1}}function Lt(e){let t=globalThis.hljs;if(t)for(let s of e.querySelectorAll("pre code"))s.dataset.highlighted||t.highlightElement(s)}function Mt(e){for(let t of e.classList)if(t.startsWith("language-"))return t.slice(9);return""}function $t(e){let t=e.querySelectorAll("pre");for(let s of t){let i=s.querySelector("code");if(!i||s.parentElement?.classList.contains("code-block-wrapper"))continue;let a=Mt(i),r=document.createElement("div");r.className="code-block-wrapper";let l=document.createElement("div");l.className="code-toolbar";let d=document.createElement("button");d.className="copy-btn",d.textContent="cp",d.type="button",l.appendChild(d),s.replaceWith(r),r.appendChild(l),r.appendChild(s)}}var ve=new WeakMap;function Ye(e){let t=e.target.closest(".copy-btn");if(!t)return;let i=t.closest(".code-block-wrapper")?.querySelector("code");if(!i)return;let a=i.textContent||"";navigator.clipboard.writeText(a).then(()=>{let r=ve.get(t);r&&clearTimeout(r),t.textContent="\u2713",t.classList.add("copied");let l=setTimeout(()=>{t.textContent="cp",t.classList.remove("copied"),ve.delete(t)},1500);ve.set(t,l)}).catch(()=>{let r=document.createRange();r.selectNodeContents(i);let l=window.getSelection();l?.removeAllRanges(),l?.addRange(r)})}async function z(e){if(!e.querySelector("pre code"))return;$t(e),await xt()&&Lt(e)}function ze(){B==="ready"&&P&&Se()}typeof window<"u"&&window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{B==="ready"&&P&&Se()});marked.setOptions({breaks:!0,gfm:!0});function ce(e){return DOMPurify.sanitize(marked.parse(e))}function j(e,t){let s=document.createElement("div");return s.className=`msg ${e}`,s.innerHTML=e==="user"?p(t).replace(/\n/g,"<br>"):ce(t),E(s),s}function c(e){let t=document.createElement("div");return t.className="system-msg",t.textContent=e,E(t),t}function H(){let e=n.currentAssistantEl;n.currentAssistantEl=null,n.currentAssistantText="",e&&typeof e.querySelector=="function"&&z(e)}function k(){if(n.currentThinkingEl){let e=n.currentThinkingEl.querySelector("summary");e.textContent="\u283F thought",e.classList.remove("active"),e.style.animation="none",n.currentThinkingEl=null,n.currentThinkingText=""}}var D=null,Pt=80;function be(e){return e.scrollHeight-e.scrollTop-e.clientHeight<Pt}function Ht(){n.followMessages=be(o.messages)}o.messages.addEventListener("scroll",Ht);function _t(){return n.followMessages||be(o.messages)}function E(e,t=!1){if(n.replayTarget)return n.replayTarget.appendChild(e),e;let s=t||_t();return o.messages.appendChild(e),I(s),e}function Xe(){F(),D=document.createElement("div"),D.id="waiting",D.innerHTML='<span class="cursor">\u258C</span>',E(D,!0)}function F(){D&&(D.remove(),D=null)}var Ie=!1;function I(e){let t=o.messages;if(e||n.followMessages){typeof requestAnimationFrame=="function"?Ie||(Ie=!0,requestAnimationFrame(()=>{Ie=!1,t.scrollTop=t.scrollHeight})):t.scrollTop=t.scrollHeight,n.followMessages=!0;return}n.followMessages=be(t)}function p(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}function et(e){if(!e)return"";let t=new Date(e.endsWith("Z")?e:e+"Z"),s=i=>String(i).padStart(2,"0");return`${t.getFullYear()}-${s(t.getMonth()+1)}-${s(t.getDate())} ${s(t.getHours())}:${s(t.getMinutes())}`}function Te(e){if(typeof e=="string"&&e.includes("*** Begin Patch")){let t=e.split(`
2
- `),s=[];for(let i of t)i.startsWith("*** Begin Patch")||i.startsWith("*** End Patch")||(i.startsWith("*** Update File:")||i.startsWith("*** Add File:")||i.startsWith("*** Delete File:")?s.push(`<span class="diff-file">${p(i)}</span>`):i.startsWith("@@")?s.push(`<span class="diff-hunk">${p(i)}</span>`):i.startsWith("-")?s.push(`<span class="diff-del">${p(i)}</span>`):i.startsWith("+")?s.push(`<span class="diff-add">${p(i)}</span>`):s.push(p(i)));return s.join(`
3
- `)}if(e&&typeof e=="object"){let t=[];if(e.path&&t.push(`<span class="diff-file">*** ${p(e.path)}</span>`),e.old_str!=null)for(let s of String(e.old_str).split(`
4
- `))t.push(`<span class="diff-del">- ${p(s)}</span>`);if(e.new_str!=null)for(let s of String(e.new_str).split(`
5
- `))t.push(`<span class="diff-add">+ ${p(s)}</span>`);if(e.file_text!=null)for(let s of String(e.file_text).split(`
6
- `))t.push(`<span class="diff-add">+ ${p(s)}</span>`);return t.length>(e.path?1:0)?t.join(`
7
- `):null}return null}function Z(e,t=!1){let s=document.createElement("div");return s.className="bash-block",s.innerHTML=`<span class="bash-cmd${t?" running":""}">${p(e)}</span><div class="bash-output"></div>`,s.querySelector(".bash-cmd").addEventListener("click",()=>{let i=s.querySelector(".bash-output");i.style.display==="none"?i.style.display="block":i.classList.contains("has-content")&&(i.style.display="none")}),E(s),t&&(n.currentBashEl=s),s}function X(e,t,s){if(!e)return;let i=e.querySelector(".bash-cmd");i.classList.remove("running");let a="";if(s?a=`[signal: ${s}]`:t!==0&&t!=null&&(a=`[exit: ${t}]`),a){let r=document.createElement("span");r.className=`bash-exit ${t===0?"ok":"fail"}`,r.textContent=a,i.after(r)}e===n.currentBashEl&&(n.currentBashEl=null)}var Nt={auto:"\u25D1",light:"\u2600",dark:"\u263E"},Ze=["auto","light","dark"];function tt(){return localStorage.getItem("theme")||"auto"}function nt(e){document.documentElement.setAttribute("data-theme",e),o.themeBtn.textContent=Nt[e],o.themeBtn.title=`Theme: ${e}`,localStorage.setItem("theme",e);for(let t of st)t()}var st=[];function it(e){st.push(e)}o.themeBtn.onclick=()=>{let e=tt();nt(Ze[(Ze.indexOf(e)+1)%3])};nt(tt());var Ee={task_complete:"\u2714",read:"cat",edit:"edit",execute:"exec",search:"find",delete:"rm"},ke="run",Ce={pending:"\u25CB",in_progress:"\u25C9",completed:"\u25CF"};function ot(e){return n.replayTarget?.querySelector(`[id="${e}"]`)??document.getElementById(e)}function qt(e){return n.replayTarget?.querySelector(e)??document.querySelector(e)}var rt="webagent_notify_tip_shown",at="webagent_notify_tip_denied_shown";function Bt(){if(typeof Notification>"u"||n.replayInProgress)return;let e=Notification.permission;if(e!=="granted"){if(e==="denied"){if(localStorage.getItem(at))return;localStorage.setItem(at,"1"),c("tip: notifications are blocked \u2014 allow in browser site settings to enable");return}localStorage.getItem(rt)||(localStorage.setItem(rt,"1"),c("tip: use /notify to enable background notifications"))}}function de(){n.pendingPromptDone&&(n.pendingToolCallIds.size>0||n.pendingPermissionRequestIds.size>0||(F(),k(),H(),h(!1),n.pendingPromptDone=!1,Bt()))}function Ot(){for(let e of n.pendingToolCallIds){let t=document.getElementById(`tc-${e}`);if(!t)continue;t.className="tool-call failed";let s=t.querySelector(".icon");s&&(s.textContent="\u2717")}for(let e of n.pendingPermissionRequestIds){let t=document.querySelector(`.permission[data-request-id="${e}"]`);if(!t||!t.querySelector("button"))continue;let i=t.querySelector(".title")?.textContent||"\u26BF";t.innerHTML=`<span style="opacity:0.5">${p(i)} \u2014 cancelled</span>`}n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear()}function At(){for(let e of n.pendingToolCallIds){let t=document.getElementById(`tc-${e}`);if(!t)continue;t.className="tool-call completed";let s=t.querySelector(".icon");s&&(s.textContent="\u2713")}n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear()}function Le(e){if(Array.isArray(e))return{events:e,streaming:{thinking:!1,assistant:!1}};let t=e;return{events:t.events??[],streaming:t.streaming??{thinking:!1,assistant:!1},total:typeof t.total=="number"?t.total:void 0,hasMore:typeof t.hasMore=="boolean"?t.hasMore:void 0}}var lt=200;async function O(e){n.replayInProgress=!0,n.replayQueue=[];try{let t=await fetch(`/api/v1/sessions/${e}/events?limit=${lt}`);if(!t.ok)return!1;let s=await t.json(),{events:i,streaming:a,hasMore:r}=Le(s),l=document.createDocumentFragment(),d=$e(i);n.replayTarget=l;for(let u=0;u<i.length;u++){let f=JSON.parse(i[u].data);Pe(i[u].type,f,i,u,d)}return n.replayTarget=null,o.messages.style.display="none",o.messages.appendChild(l),o.messages.style.display="",i.length&&(n.lastEventSeq=i[i.length-1].seq,n.oldestLoadedSeq=i[0].seq),n.hasMoreHistory=r===!0,n.hasMoreHistory&&Rt(),ct(),xe(i,a),!0}catch{return!1}finally{n.replayTarget=null,n.replayInProgress=!1,ut()}}function xe(e,t){if(t.thinking&&e.length){for(let s=e.length-1;s>=0;s--)if(e[s].type==="thinking"){let i=JSON.parse(e[s].data),a=o.messages.querySelectorAll(".thinking"),r=a[a.length-1];if(r){n.currentThinkingEl=r,n.currentThinkingText=i.text;let l=r.querySelector("summary");l&&(l.textContent="\u283F thinking...",l.classList.add("active"))}break}}if(t.assistant&&e.length){for(let s=e.length-1;s>=0;s--)if(e[s].type==="assistant_message"){let i=JSON.parse(e[s].data),a=o.messages.querySelectorAll(".msg.assistant"),r=a[a.length-1];r&&(n.currentAssistantEl=r,n.currentAssistantText=i.text);break}}}function ct(){let e=o.messages.querySelector("[data-sync-boundary]");e&&e.removeAttribute("data-sync-boundary");let t=o.messages.lastElementChild;t&&t.setAttribute("data-sync-boundary","")}async function Me(e){n.replayInProgress=!0,n.replayQueue=[];try{let t=`/api/v1/sessions/${e}/events?after=${n.lastEventSeq}`,s=await fetch(t);if(!s.ok)return!1;let i=await s.json(),{events:a,streaming:r}=Le(i),l=o.messages.querySelector("[data-sync-boundary]");if(l)for(;l.nextElementSibling;)l.nextElementSibling.remove();if(n.currentAssistantEl=null,n.currentAssistantText="",n.currentThinkingEl=null,n.currentThinkingText="",n.currentBashEl=null,a.length===0)return xe(a,r),!0;let d=document.createDocumentFragment(),u=$e(a);n.replayTarget=d;for(let f=0;f<a.length;f++){let g=JSON.parse(a[f].data);Pe(a[f].type,g,a,f,u)}return n.replayTarget=null,o.messages.appendChild(d),n.lastEventSeq=a[a.length-1].seq,ct(),xe(a,r),!0}catch{return!1}finally{n.replayTarget=null,n.replayInProgress=!1,ut()}}var ee=null;function Rt(){ue();let e=document.createElement("div");e.id="history-sentinel",e.className="history-sentinel",e.textContent="\u2191 loading\u2026",o.messages.prepend(e),typeof IntersectionObserver=="function"&&(ee=new IntersectionObserver(t=>{t[0]?.isIntersecting&&!n.loadingOlderEvents&&n.hasMoreHistory&&n.sessionId&&Dt(n.sessionId)},{root:o.messages,rootMargin:"200px 0px 0px 0px"}),ee.observe(e))}function ue(){ee&&(ee.disconnect(),ee=null),document.getElementById("history-sentinel")?.remove()}Ve(ue);async function Dt(e){if(n.loadingOlderEvents||!n.hasMoreHistory||n.oldestLoadedSeq<=0)return!1;n.loadingOlderEvents=!0;try{let t=await fetch(`/api/v1/sessions/${e}/events?limit=${lt}&before=${n.oldestLoadedSeq}`);if(!t.ok||e!==n.sessionId)return!1;let s=await t.json(),{events:i,hasMore:a}=Le(s);if(i.length===0)return n.hasMoreHistory=!1,ue(),!0;let r=document.createDocumentFragment(),l=$e(i);n.replayTarget=r;for(let g=0;g<i.length;g++){let S=JSON.parse(i[g].data);Pe(i[g].type,S,i,g,l)}n.replayTarget=null;let d=o.messages,u=d.scrollHeight,f=document.getElementById("history-sentinel");return f?f.after(r):d.prepend(r),d.scrollTop+=d.scrollHeight-u,n.oldestLoadedSeq=i[0].seq,n.hasMoreHistory=a===!0,n.hasMoreHistory||ue(),!0}catch{return!1}finally{n.loadingOlderEvents=!1}}function dt(){for(let[e,t]of n.unconfirmedPermissions){let s=document.querySelector(`.permission[data-request-id="${e}"]`);if(!s||!s.querySelector("button")){n.unconfirmedPermissions.delete(e);continue}t.denied?re(t.sessionId,e).catch(()=>{}):oe(t.sessionId,e,t.optionId).catch(()=>{});let i=s.dataset.title?`\u26BF ${p(s.dataset.title)}`:"\u26BF";s.innerHTML=`<span style="opacity:0.5">${i} \u2014 ${p(t.optionName)}</span>`,n.unconfirmedPermissions.delete(e)}}function $e(e){let t=new Set;for(let s of e)s.type==="permission_response"&&t.add(JSON.parse(s.data).requestId);return{toolCalls:new Map,permissions:new Map,resolvedPermissions:t,currentBashEl:null}}function Pe(e,t,s,i,a){switch(e){case"user_message":{let r=j("user",t.text);if(t.images)for(let l of t.images){let d=document.createElement("img");d.className="user-image",d.src=l.path,r.appendChild(d)}break}case"assistant_message":{let l=(n.replayTarget||o.messages).lastElementChild;if(l&&l.classList.contains("msg")&&l.classList.contains("assistant")){let f=(l.getAttribute("data-raw")||"")+t.text;l.setAttribute("data-raw",f),l.innerHTML=ce(f),z(l);break}let d=j("assistant",t.text);d.setAttribute("data-raw",t.text),z(d);break}case"thinking":{let l=(n.replayTarget||o.messages).lastElementChild;if(l&&l.classList.contains("thinking")){let u=l.querySelector(".thinking-content");if(u){u.textContent+=`
8
- `+t.text;break}}let d=document.createElement("details");d.className="thinking",d.innerHTML=`<summary>\u283F thought</summary><div class="thinking-content">${p(t.text)}</div>`,E(d);break}case"tool_call":{let r=Ee[t.kind]||ke,l=document.createElement("div");l.className="tool-call",l.id=`tc-${t.id}`,l.dataset.kind=t.kind;let d=`<span class="icon">${r}</span> ${p(t.title)}`,u=t.rawInput;u&&u.command?d+=`<span class="tc-detail">$ ${p(u.command)}</span>`:u&&u.path&&(d+=`<span class="tc-detail">${p(u.path)}</span>`),l.innerHTML=d;let f=t.kind==="edit"?Te(u):null;if(f){let S=document.createElement("details");S.innerHTML=`<summary>diff</summary><div class="diff-view">${f}</div>`,l.appendChild(S)}let g=l.querySelector(".tc-detail");g&&l.addEventListener("click",S=>{S.target.closest("details")||g.classList.toggle("expanded")}),E(l),a&&a.toolCalls.set(t.id,l);break}case"tool_call_update":{let r=a?a.toolCalls.get(t.id):ot(`tc-${t.id}`);if(r){let l=t.status==="completed"?"\u2713":t.status==="failed"?"\u2717":"\u2026";r.className=`tool-call ${t.status}`;let d=r.querySelector(".icon");if(d&&(d.textContent=l),t.content&&t.content.length&&!r.querySelector("details")&&!r.querySelector(".tc-summary")){let u=t.content.map(f=>f.type==="terminal"?`[terminal ${f.terminalId}]`:f.content?.text?f.content.text:Array.isArray(f.content)?f.content.map(g=>g.text||"").join(""):"").filter(Boolean).join(`
9
- `);if(u)if(r.dataset.kind==="task_complete"){let f=document.createElement("div");f.className="tc-summary",f.textContent=u,r.appendChild(f)}else{let f=document.createElement("details");f.innerHTML=`<summary>output</summary><div class="tc-content">${p(u)}</div>`,r.appendChild(f)}}}(t.status==="completed"||t.status==="failed")&&n.pendingToolCallIds.delete(t.id);break}case"plan":{let r=document.createElement("div");r.className="plan",r.innerHTML='<div class="plan-title">\u2015 plan</div>'+(t.entries||[]).map(l=>`<div class="plan-entry">${Ce[l.status]||"?"} ${p(l.content)}</div>`).join(""),E(r);break}case"permission_request":{let r=document.createElement("div");r.className="permission",r.dataset.requestId=t.requestId,r.dataset.title=t.title||"",r.innerHTML=`<span class="title" style="opacity:0.5">\u26BF ${p(t.title)}</span> `,!(a?a.resolvedPermissions.has(t.requestId):s&&s.slice(i+1).some(d=>d.type==="permission_response"&&JSON.parse(d.data).requestId===t.requestId))&&t.options&&(r.querySelector(".title").style.opacity="1",t.options.forEach(d=>{let u=document.createElement("button"),f=(d.kind||"").includes("allow");u.className=f?"allow":"deny",u.textContent=d.name,u.onclick=()=>{(d.kind||"").includes("reject")||(d.kind||"").includes("deny")?re(n.sessionId,t.requestId).catch(()=>{}):oe(n.sessionId,t.requestId,d.optionId).catch(()=>{}),r.innerHTML=`<span style="opacity:0.5">\u26BF ${p(t.title)} \u2014 ${p(d.name)}</span>`},r.appendChild(u)})),E(r),a&&a.permissions.set(t.requestId,r);break}case"permission_response":{let r=a?a.permissions.get(t.requestId):qt(`.permission[data-request-id="${t.requestId}"]`);if(r){let l=r.dataset.title?`\u26BF ${r.dataset.title}`:"\u26BF",d=t.optionName||(t.denied?"denied":"allowed");r.innerHTML=`<span style="opacity:0.5">${p(l)} \u2014 ${p(d)}</span>`}break}case"bash_command":{let r=Z(t.command,!1);a?a.currentBashEl=r:r.id="bash-replay-pending";break}case"bash_result":{let r=a?a.currentBashEl:ot("bash-replay-pending");if(r){if(a||r.removeAttribute("id"),t.output){let l=r.querySelector(".bash-output");l&&(l.textContent=t.output,l.classList.add("has-content"))}X(r,t.code,t.signal),a&&(a.currentBashEl=null)}break}case"prompt_done":n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,h(!1);break}}function ut(){let e=n.replayQueue;n.replayQueue=[];for(let t of e)jt(t)||C(t)}function jt(e){switch(e.type){case"tool_call":return!!document.getElementById(`tc-${e.id}`);case"permission_request":return!!document.querySelector(`.permission[data-request-id="${e.requestId}"]`);case"thought_chunk":return!!n.currentThinkingEl;case"message_chunk":return!!n.currentAssistantEl;default:return!1}}function C(e){if(n.replayInProgress){console.log("[handleEvent-DEBUG] QUEUED (replayInProgress):",e.type),n.replayQueue.push(e);return}if(!(e.sessionId&&e.type!=="session_created"&&e.type!=="session_deleted"&&(!n.sessionId||e.sessionId!==n.sessionId)))switch(e.type){case"connected":if(e.cancelTimeout!=null&&(n.cancelTimeout=e.cancelTimeout),n.agentReloading){n.agentReloading=!1;let t=e.agent?.name??"",s=e.agent?.version??"",i=t&&s?`${t} ${s}`:"Agent";c(`${i} reloaded.`),h(!1)}break;case"session_created":if(!n.awaitingNewSession&&n.sessionId&&e.sessionId!==n.sessionId)break;if(n.awaitingNewSession=!1,n.sessionId=e.sessionId,n.sessionCwd=e.cwd||n.sessionCwd,n.sessionTitle=e.title||null,e.configOptions?.length&&ye(e.configOptions),Je(n.sessionId),n.clientId&&J(n.clientId,!document.hidden,n.sessionId).catch(()=>{}),le(n.sessionId,n.sessionTitle),A("connected","connected"),o.input.disabled=!1,o.sendBtn.disabled=!1,o.input.placeholder="Message or ?",h(!!e.busyKind),n.newTurnStarted=!1,e.busyKind==="bash"){let t=document.getElementById("bash-replay-pending");t&&(t.removeAttribute("id"),t.querySelector(".bash-cmd")?.classList.add("running"),n.currentBashEl=t)}else n.currentBashEl=null;o.messages.children.length===0&&c(`Session created: ${n.sessionTitle||e.sessionId.slice(0,8)+"\u2026"}`),R();break;case"user_message":{if(n.sentMessageForSession===e.sessionId){n.sentMessageForSession=null;break}if(k(),H(),n.newTurnStarted=!0,n.turnEnded=!1,e.sessionId===n.sessionId){let t=j("user",e.text);if(e.images)for(let s of e.images){let i=document.createElement("img");i.className="user-image",i.src=s.path,t.appendChild(i)}}break}case"message_chunk":if(n.turnEnded)break;F(),k(),n.currentAssistantEl||(n.currentAssistantEl=j("assistant",""),n.currentAssistantText=""),n.currentAssistantText+=e.text,n.currentAssistantEl.innerHTML=ce(n.currentAssistantText),I();break;case"thought_chunk":if(n.turnEnded)break;F(),n.currentThinkingEl||(n.currentThinkingEl=document.createElement("details"),n.currentThinkingEl.className="thinking",n.currentThinkingEl.innerHTML='<summary class="active">\u283F thinking...</summary><div class="thinking-content"></div>',n.currentThinkingText="",E(n.currentThinkingEl)),n.currentThinkingText+=e.text,n.currentThinkingEl.querySelector(".thinking-content").textContent=n.currentThinkingText,I();break;case"tool_call":{if(n.turnEnded)break;n.pendingToolCallIds.add(e.id),h(!0),F(),k(),H();let t=Ee[e.kind]||ke,s=document.createElement("div");s.className="tool-call",s.id=`tc-${e.id}`,s.dataset.kind=e.kind;let i=`<span class="icon">${t}</span> ${p(e.title)}`,a=e.rawInput;a&&a.command?i+=`<span class="tc-detail">$ ${p(a.command)}</span>`:a&&a.path&&(i+=`<span class="tc-detail">${p(a.path)}</span>`),s.innerHTML=i;let r=e.kind==="edit"?Te(a):null;if(r){let d=document.createElement("details");d.innerHTML=`<summary>diff</summary><div class="diff-view">${r}</div>`,s.appendChild(d)}let l=s.querySelector(".tc-detail");l&&s.addEventListener("click",d=>{d.target.closest("details")||l.classList.toggle("expanded")}),E(s);break}case"tool_call_update":{let t=document.getElementById(`tc-${e.id}`);if((e.status==="completed"||e.status==="failed")&&n.pendingToolCallIds.delete(e.id),t){let s=e.status==="completed"?"\u2713":e.status==="failed"?"\u2717":"\u2026";t.className=`tool-call ${e.status}`;let i=t.querySelector(".icon");if(i&&(i.textContent=s),e.content&&e.content.length&&!t.querySelector("details")&&!t.querySelector(".tc-summary")){let a=e.content.map(r=>r.type==="terminal"?`[terminal ${r.terminalId}]`:r.content?.text?r.content.text:Array.isArray(r.content)?r.content.map(l=>l.text||"").join(""):"").filter(Boolean).join(`
10
- `);if(a)if(t.dataset.kind==="task_complete"){let r=document.createElement("div");r.className="tc-summary",r.textContent=a,t.appendChild(r)}else{let r=document.createElement("details");r.innerHTML=`<summary>output</summary><div class="tc-content">${p(a)}</div>`,t.appendChild(r)}}}de(),I();break}case"plan":{k(),H();let t=document.createElement("div");t.className="plan",t.innerHTML='<div class="plan-title">\u2015 plan</div>'+e.entries.map(s=>`<div class="plan-entry">${Ce[s.status]||"?"} ${p(s.content)}</div>`).join(""),E(t);break}case"permission_request":{if(n.turnEnded||document.querySelector(`.permission[data-request-id="${e.requestId}"]`))break;n.pendingPermissionRequestIds.add(e.requestId),h(!0),k();let t=document.createElement("div");t.className="permission",t.dataset.requestId=e.requestId,t.dataset.title=e.title||"",t.innerHTML=`<span class="title">\u26BF ${p(e.title)}</span> `,e.options.forEach(s=>{let i=document.createElement("button"),a=(s.kind||"").includes("allow");i.className=a?"allow":"deny",i.textContent=s.name,i.onclick=()=>{let r=(s.kind||"").includes("reject")||(s.kind||"").includes("deny");r?re(n.sessionId,e.requestId).catch(()=>{}):oe(n.sessionId,e.requestId,s.optionId).catch(()=>{}),n.pendingPermissionRequestIds.delete(e.requestId),n.unconfirmedPermissions.set(e.requestId,{sessionId:n.sessionId,optionId:s.optionId,optionName:s.name,denied:r}),t.innerHTML=`<span style="opacity:0.5">\u26BF ${p(e.title)} \u2014 ${p(s.name)}</span>`,de()},t.appendChild(i)}),E(t);break}case"permission_response":{console.log("[PERM-DEBUG] permission_response received:",e.requestId,"sessionId:",e.sessionId,"state.sessionId:",n.sessionId),n.pendingPermissionRequestIds.delete(e.requestId),n.unconfirmedPermissions.delete(e.requestId);let t=document.querySelector(`.permission[data-request-id="${e.requestId}"]`);if(console.log("[PERM-DEBUG] permTarget found:",!!t,"sessionMatch:",e.sessionId===n.sessionId),e.sessionId===n.sessionId&&t){let s=t.dataset.title?`\u26BF ${t.dataset.title}`:"\u26BF",i=e.optionName||(e.denied?"denied":"allowed");t.innerHTML=`<span style="opacity:0.5">${p(s)} \u2014 ${p(i)}</span>`,console.log("[PERM-DEBUG] DOM updated successfully")}else console.log("[PERM-DEBUG] DOM NOT updated \u2014 check failed");de();break}case"bash_command":{if(n.sentBashForSession===e.sessionId){n.sentBashForSession=null;break}e.sessionId===n.sessionId&&(Z(e.command,!0),h(!0));break}case"bash_output":{if(e.sessionId!==n.sessionId)break;if(n.currentBashEl){let t=n.currentBashEl.querySelector(".bash-output");if(e.stream==="stderr"){let s=document.createElement("span");s.className="stderr",s.textContent=e.text,t.appendChild(s)}else t.appendChild(document.createTextNode(e.text));t.classList.add("has-content"),t.scrollTop=t.scrollHeight,I()}break}case"bash_done":{if(e.sessionId!==n.sessionId)break;X(n.currentBashEl,e.code,e.signal),e.error&&c(`err: ${e.error}`),h(!1);break}case"prompt_done":{if(Y(),e.stopReason==="cancelled"&&n.newTurnStarted){n.newTurnStarted=!1,k(),H();break}n.newTurnStarted=!1,e.stopReason==="cancelled"?Ot():At(),n.turnEnded=!0,n.pendingPromptDone=!0,de();break}case"session_deleted":e.sessionId===n.sessionId&&(c("warn: This session has been deleted."),o.input.disabled=!0,o.sendBtn.disabled=!0,o.input.placeholder="Session deleted");break;case"session_expired":{let t=n.sessionCwd;w(),c("warn: Previous session expired, created new one."),L({cwd:t||void 0});break}case"config_set":{We(e.configId,e.value);let t=x(e.configId),s=t?.name||e.configId,i=t?.options.find(a=>a.value===e.value)?.name||e.value;c(`ok: ${s}: ${i}`),e.configId==="mode"&&q(),R();break}case"config_option_update":e.configOptions?.length&&ye(e.configOptions);break;case"session_title_updated":e.sessionId===n.sessionId&&(n.sessionTitle=e.title,le(n.sessionId,n.sessionTitle));break;case"agent_reloading":n.agentReloading=!0,c("Agent reloading\u2026"),h(!0);break;case"agent_reloading_failed":c(`err: Agent reload failed: ${e.error}`),h(!1);break;case"error":n.awaitingNewSession=!1,n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,F(),k(),H(),c(`err: ${e.message}`),h(!1);break}}async function Ft(){try{let e=await navigator.serviceWorker?.ready;if(!e)return;let t=await fetch("/api/beta/push/vapid-key");if(!t.ok)return;let{publicKey:s}=await t.json(),a=(await e.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:Wt(s)})).toJSON();await fetch("/api/beta/push/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({endpoint:a.endpoint,keys:a.keys,clientId:n.clientId})})}catch(e){console.error("[push] subscribe failed:",e)}}async function Ut(){try{let e=await navigator.serviceWorker?.ready;if(!e)return;let t=await e.pushManager.getSubscription();if(!t)return;let s=t.endpoint;await t.unsubscribe(),await fetch("/api/beta/push/unsubscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({endpoint:s})})}catch(e){console.error("[push] unsubscribe failed:",e)}}function Wt(e){let t="=".repeat((4-e.length%4)%4),s=(e+t).replace(/-/g,"+").replace(/_/g,"/"),i=atob(s),a=new Uint8Array(i.length);for(let r=0;r<i.length;r++)a[r]=i.charCodeAt(r);return a}async function He(){try{let e=await navigator.serviceWorker?.ready;return e?await e.pushManager.getSubscription()!==null:!1}catch{return!1}}async function Ne(e){let t=e.split(/\s+/),s=t[0].toLowerCase(),i=t.slice(1).join(" ").trim();switch(s){case"/new":{let a=i||n.sessionCwd;return w(),c("Creating new session\u2026"),L({cwd:a||void 0}),!0}case"/pwd":return c(`\u{1F4C1} ${n.sessionCwd||"unknown"}`),!0;case"/rename":{if(!n.sessionId)return c("err: No active session"),!0;if(!i)return c(`Current: ${n.sessionTitle||"(untitled)"}`),c("Usage: /rename <new title>"),!0;try{await je(n.sessionId,i),c(`Renamed \u2192 ${i}`)}catch{c("err: Failed to rename session")}return!0}case"/sessions":return c("Removed. Use /switch to see all sessions."),!0;case"/exit":{if(!n.sessionId)return c("warn: No active session"),!0;let a=n.sessionId;try{let l=(await ie()).find(d=>d.id!==a);if(n.busy&&Q(),ge(a).catch(()=>{}),M=null,l){n.sessionSwitchGen++;let d=n.sessionSwitchGen;w(),n.sessionId=null;let[u]=await Promise.all([$(l.id),O(l.id)]);if(d!==n.sessionSwitchGen)return!0;C({type:"session_created",sessionId:u.id,cwd:u.cwd,title:u.title,configOptions:u.configOptions,busyKind:u.busyKind}),I(!0)}else{let d=n.sessionCwd;w(),n.sessionId=null,c("Creating new session\u2026"),L({cwd:d||void 0,inheritFromSessionId:null})}}catch{c("err: Failed to exit session")}return!0}case"/prune":{try{let l=(await(await fetch("/api/v1/sessions")).json()).filter(d=>d.id!==n.sessionId);if(l.length===0)return c("No other sessions to prune."),!0;await Promise.all(l.map(d=>ge(d.id).catch(()=>{}))),M=null,c(`Pruned ${l.length} session(s).`)}catch{c("err: Failed to prune sessions")}return!0}case"/switch":{if(!i)return c("Usage: /switch <title or id prefix>"),!0;try{let r=await(await fetch("/api/v1/sessions")).json(),l=i.toLowerCase(),d=r.find(g=>g.id.startsWith(i)||g.title&&g.title.toLowerCase().includes(l));if(!d)return c(`err: No session matching "${i}"`),!0;n.sessionSwitchGen++;let u=n.sessionSwitchGen;w(),n.sessionId=null;let[f]=await Promise.all([$(d.id),O(d.id)]);if(u!==n.sessionSwitchGen)return!0;C({type:"session_created",sessionId:f.id,cwd:f.cwd,title:f.title,configOptions:f.configOptions,busyKind:f.busyKind}),I(!0)}catch{w(),n.sessionId=null,c("err: Failed to switch session")}return!0}case"/cancel":return n.busy?(Q(),c("^C")):c("Nothing to cancel."),!0;case"/reload":return c("Reloading agent\u2026"),Ue().then(()=>{}).catch(a=>{c(`err: ${a instanceof Error?a.message:"Failed to reload agent"}`)}),!0;case"/help":case"?":{let a=[];n.serverVersion&&a.push(`WebAgent ${n.serverVersion}`),n.agentName&&n.agentVersion&&a.push(`${n.agentName} ${n.agentVersion}`),a.length&&c(a.join(" \xB7 ")),c("? \u2014 Show help"),c("/help \u2014 Show help (alias)"),c("!<command> \u2014 Run bash command");for(let r of pt){let l=r.args?`${r.cmd} ${r.args}`:r.cmd;c(`${l} \u2014 ${r.desc}`)}c("--- Shortcuts ---");for(let r of Kt)c(`${r.key} \u2014 ${r.desc}`);c("--- Tips ---");for(let r of Vt)c(r.text);return!0}case"/model":case"/mode":case"/think":{let r={"/model":"model","/mode":"mode","/think":"reasoning_effort"}[s],l=x(r);if(!i){let S=l?.options.find(se=>se.value===l.currentValue)?.name||l?.currentValue||"unknown";return c(`${l?.name||r}: ${S}`),c(`Type ${s} + space to pick from list`),!0}if(!l)return c(`err: ${s.slice(1)} is not available.`),!0;let d=i.trim(),u=S=>S.toLowerCase().replace(/[\s_]+/g,"-"),f=u(d),g=l.options.find(S=>u(S.value)===f||u(S.name)===f);if(!g){let S=l.options.filter(se=>u(se.value).includes(f)||u(se.name).includes(f));if(S.length===1)g=S[0];else if(S.length>1)return c(`err: Ambiguous "${i}". Type ${s} + space to see options.`),!0}return g?(l.currentValue=g.value,q(),R(),c(`${l.name} \u2192 ${g.name}`),await G(n.sessionId,r,g.value).catch(()=>{}),!0):(c(`err: Unknown "${i}". Type ${s} + space to see options.`),!0)}case"/notify":{if(typeof Notification>"u")return c("err: notifications not supported in this browser"),!0;let a=i.toLowerCase();if(a==="on"){if(Notification.permission==="denied")return c("notify: blocked \u2014 allow in browser site settings to enable"),!0;if(Notification.permission!=="granted"&&await Notification.requestPermission()!=="granted")return c("notify: blocked \u2014 allow in browser site settings to enable"),!0;let l=await He();return await Ft(),c(l?"notify: already enabled":"notify: enabled"),!0}if(a==="off")return await Ut(),c("notify: disabled"),!0;let r=Notification.permission;return r==="denied"?c("notify: blocked \u2014 allow in browser site settings to enable"):r==="granted"&&await He()?c("notify: enabled"):c("notify: off \u2014 use /notify on to enable"),!0}default:return!1}}var pt=[{cmd:"/cancel",args:"",desc:"Cancel current response"},{cmd:"/exit",args:"",desc:"Close current session"},{cmd:"/mode",args:"[name]",desc:"Pick or switch mode"},{cmd:"/model",args:"[name]",desc:"Pick or switch model"},{cmd:"/new",args:"[cwd]",desc:"New session"},{cmd:"/notify",args:"[on|off]",desc:"Toggle background notifications"},{cmd:"/prune",args:"",desc:"Delete all sessions except current"},{cmd:"/pwd",args:"",desc:"Show working directory"},{cmd:"/reload",args:"",desc:"Reload agent CLI"},{cmd:"/rename",args:"<new title>",desc:"Rename current session"},{cmd:"/switch",args:"<title|id>",desc:"Switch to session"},{cmd:"/think",args:"[level]",desc:"Pick or switch reasoning effort"}],Kt=[{key:"Enter",desc:"Send message"},{key:"Shift+Enter",desc:"New line"},{key:"^C",desc:"Cancel current response"},{key:"^M",desc:"Cycle mode (Agent \u2192 Plan \u2192 Autopilot)"},{key:"^U",desc:"Upload image"}],Vt=[{text:"Tap \u276F prompt to cycle mode"}],b=-1,m=[],v="commands",te=null,M=null,V=null,_e=!1;function qe(){let e=o.input.value;if(V!==null){if(e===V)return;V=null}let t=e.match(/^\/new /);if(t){let l=e.slice(t[0].length).toLowerCase();Jt(l);return}let s=e.match(/^\/switch /);if(s){let l=e.slice(s[0].length).toLowerCase();Gt(l,"switch");return}let i=e.match(/^\/(model|mode|think) /);if(i){let d={model:"model",mode:"mode",think:"reasoning_effort"}[i[1]],u=e.slice(i[0].length).toLowerCase();Qt(d,u);return}let a=e.match(/^\/notify /);if(a){let l=e.slice(a[0].length).toLowerCase();zt(l);return}if(!e.startsWith("/")||e.includes(" ")){y();return}v="commands";let r=e.toLowerCase();if(m=pt.filter(l=>l.cmd.startsWith(r)),m.length===0){y();return}b=0,U(),o.slashMenu.classList.add("active")}async function Gt(e,t="switch"){if(!M)try{M=await(await fetch("/api/v1/sessions")).json(),setTimeout(()=>{M=null},5e3)}catch{return}if(v=t,m=M.filter(i=>e?i.title&&i.title.toLowerCase().includes(e)||i.id.startsWith(e):!0),m.length===0){y();return}b=0,U(),o.slashMenu.classList.add("active")}async function Jt(e){if(!M)try{M=await(await fetch("/api/v1/sessions")).json(),setTimeout(()=>{M=null},5e3)}catch{return}v="new";let t=new Map;for(let i of M){let a=t.get(i.cwd);(!a||(i.last_active_at||i.created_at)>a.time)&&t.set(i.cwd,{cwd:i.cwd,time:i.last_active_at||i.created_at})}let s=[...t.values()].sort((i,a)=>a.time.localeCompare(i.time));if(e&&(s=s.filter(i=>i.cwd.toLowerCase().includes(e))),m=s,m.length===0){y();return}b=0,U(),o.slashMenu.classList.add("active")}function Qt(e,t){let s=x(e);if(!s){y();return}if(v="config",te=e,m=s.options.filter(i=>t?i.value.toLowerCase().includes(t)||i.name.toLowerCase().includes(t):!0),m.length===0){y();return}b=0,U(),o.slashMenu.classList.add("active")}var Yt=[{value:"on",name:"on",desc:"Enable background notifications"},{value:"off",name:"off",desc:"Disable background notifications"}];async function zt(e){if(v="notify",m=Yt.filter(i=>e?i.value.includes(e)||i.name.includes(e):!0),m.length===0){y();return}_e=await He();let t=_e?"on":"off",s=m.findIndex(i=>i.value===t);b=s>=0?s:0,U(),o.slashMenu.classList.add("active")}function U(){if(v==="new"){let t=(n.sessionCwd||"").toLowerCase();o.slashMenu.innerHTML=m.map((s,i)=>{let a=s.cwd.toLowerCase()===t;return`<div class="slash-item${i===b?" selected":""}" data-idx="${i}"><span class="slash-cmd"${a?' style="color:var(--green)"':""}>${p((a?"* ":" ")+s.cwd)}</span></div>`}).join("")}else if(v==="config"){let t=ae(te)?.toLowerCase()||"";o.slashMenu.innerHTML=m.map((s,i)=>{let a=s.value.toLowerCase()===t;return`<div class="slash-item${i===b?" selected":""}" data-idx="${i}"><span class="slash-cmd"${a?' style="color:var(--green)"':""}>${p((a?"* ":" ")+s.name)}</span></div>`}).join("")}else if(v==="notify"){let t=_e?"on":"off";o.slashMenu.innerHTML=m.map((s,i)=>{let a=s.value===t;return`<div class="slash-item${i===b?" selected":""}" data-idx="${i}"><span class="slash-cmd"${a?' style="color:var(--green)"':""}>${p((a?"* ":" ")+s.name)}</span><span class="slash-desc">${p(s.desc)}</span></div>`}).join("")}else v==="switch"?o.slashMenu.innerHTML=m.map((t,s)=>{let i=t.id===n.sessionId,a=i?"* ":" ",r=t.title||t.id.slice(0,8)+"\u2026",l=et(t.last_active_at||t.created_at);return`<div class="slash-item${s===b?" selected":""}" data-idx="${s}"><span class="slash-cmd"${i?' style="color:var(--green)"':""}>${p(a+r)}</span><span class="slash-desc">${p(t.cwd)} (${p(l)})</span></div>`}).join(""):o.slashMenu.innerHTML=m.map((t,s)=>{let i=t.args?`${t.cmd} ${t.args}`:t.cmd;return`<div class="slash-item${s===b?" selected":""}" data-idx="${s}"><span class="slash-cmd">${p(i)}</span><span class="slash-desc">${p(t.desc)}</span></div>`}).join("");let e=o.slashMenu.querySelector(".selected");e&&e.scrollIntoView({block:"nearest"})}function y(){o.slashMenu.classList.remove("active"),b=-1,m=[],v="commands",V=o.input.value}function Zt(e){if(!(e<0||e>=m.length)){if(v==="commands"){let t=m[e];o.input.value=t.cmd+(t.args?" ":""),y(),o.input.focus(),["/new","/switch","/model","/mode","/think","/notify"].includes(t.cmd)&&(V=null,qe())}else if(v==="config"){let t=m[e],s={model:"/model",mode:"/mode",reasoning_effort:"/think"}[te]||`/${te}`;o.input.value=`${s} ${t.name}`,y(),o.input.focus()}else if(v==="notify"){let t=m[e];o.input.value=`/notify ${t.value}`,y(),o.input.focus()}else if(v==="new"){let t=m[e];o.input.value=`/new ${t.cwd}`,y(),o.input.focus()}else if(v==="switch"){let t=m[e];o.input.value=`/switch ${t.title||t.id}`,y(),o.input.focus()}}}async function Xt(e){if(!(e<0||e>=m.length))if(v==="new"){let t=m[e];o.input.value="",y(),w(),c("Creating new session\u2026"),L({cwd:t.cwd})}else if(v==="config"){let t=m[e],s=te,i=x(s);o.input.value="",y(),i&&(i.currentValue=t.value),q(),R(),c(`${i?.name||s} \u2192 ${t.name}`),await G(n.sessionId,s,t.value).catch(()=>{})}else if(v==="switch"){let t=m[e];o.input.value="",y(),w(),n.sessionId=null,c("Switching\u2026"),Promise.all([$(t.id),O(t.id)]).then(([s,i])=>{C({type:"session_created",sessionId:s.id,cwd:s.cwd,title:s.title,configOptions:s.configOptions,busyKind:s.busyKind}),i&&I(!0)}).catch(()=>{w(),n.sessionId=null,c("err: Failed to switch session")})}else if(v==="notify"){let t=m[e];o.input.value=`/notify ${t.value}`,y(),Ne(o.input.value),o.input.value=""}else{let t=m[e];o.input.value=t.cmd+(t.args?" ":""),y(),o.input.focus(),["/new","/switch","/model","/mode","/think","/notify"].includes(t.cmd)&&(V=null,qe())}}function ft(e){return o.slashMenu.classList.contains("active")?e.key==="ArrowDown"?(b=(b+1)%m.length,U(),!0):e.key==="ArrowUp"?(b=(b-1+m.length)%m.length,U(),!0):e.key==="Tab"?(Zt(b),!0):!1:!1}o.slashMenu.addEventListener("mousedown",e=>{e.preventDefault();let t=e.target.closest(".slash-item");t&&Xt(Number(t.dataset.idx))});o.input.addEventListener("input",()=>{qe(),o.inputArea.classList.toggle("bash-mode",o.input.value.startsWith("!"))});function mt(e){return new Promise(t=>{let s=new FileReader;s.onload=()=>{let i=s.result.split(",")[1];t({data:i,mimeType:e.type,previewUrl:s.result})},s.readAsDataURL(e)})}function gt(e){n.pendingImages.push(e),pe(),o.input.focus()}function pe(){if(o.attachPreview.innerHTML="",n.pendingImages.length===0){o.attachPreview.classList.remove("active");return}o.attachPreview.classList.add("active"),n.pendingImages.forEach((e,t)=>{let s=document.createElement("span");s.className="attach-thumb",s.innerHTML=`<img src="${e.previewUrl}"><button class="remove">\xD7</button>`,s.querySelector(".remove").addEventListener("click",()=>{n.pendingImages.splice(t,1),pe()}),o.attachPreview.appendChild(s)})}o.attachBtn.onclick=()=>o.fileInput.click();o.fileInput.onchange=async()=>{for(let e of o.fileInput.files)e.type.startsWith("image/")&&gt(await mt(e));o.fileInput.value=""};o.input.addEventListener("paste",async e=>{for(let t of e.clipboardData.items)t.type.startsWith("image/")&&(e.preventDefault(),gt(await mt(t.getAsFile())))});var _=document.createElement("div");_.id="lightbox-overlay";var ne=document.createElement("img");_.appendChild(ne);document.body.appendChild(_);var fe=1,en=.5,tn=5;function nn(){return _.classList.contains("active")}function sn(e){ne.src=e,fe=1,ne.style.transform="",_.classList.add("active")}function ht(){_.classList.remove("active"),ne.src=""}document.getElementById("messages").addEventListener("click",e=>{let t=e.target;t.tagName==="IMG"&&t.classList.contains("user-image")&&sn(t.src)});_.addEventListener("click",e=>{e.target===_&&ht()});document.addEventListener("keydown",e=>{e.key==="Escape"&&nn()&&ht()});_.addEventListener("wheel",e=>{e.preventDefault();let t=e.deltaY>0?-.15:.15;fe=Math.min(tn,Math.max(en,fe+t)),ne.style.transform=`scale(${fe})`},{passive:!1});function Be(){return n.clientId!==null}n._onCancelTimeout=()=>c("warn: Agent not responding to cancel");function yt(){let e=o.input.value.trim();if(!e&&n.pendingImages.length===0)return;if((e.startsWith("/")||e==="?"||e.startsWith("? "))&&n.pendingImages.length===0){o.input.value="",o.input.style.height="auto",St(),Ne(e);return}if(e.startsWith("!")&&n.pendingImages.length===0){let i=e.slice(1).trim();if(!i)return;if(!n.sessionId){c("warn: Session not ready yet, please wait\u2026");return}if(!Be()){c("warn: Not connected, please retry");return}o.input.value="",o.input.style.height="auto",o.inputArea.classList.remove("bash-mode"),Z(i,!0),n.sentBashForSession=n.sessionId,Fe(n.sessionId,i).catch(()=>{}),h(!0);return}if(n.busy)return;if(o.input.value="",o.input.style.height="auto",o.inputArea.classList.remove("bash-mode"),!n.sessionId){c("warn: Session not ready yet, please wait\u2026");return}if(!Be()){c("warn: Not connected, please retry");return}let t=j("user",e||"(image)");for(let i of n.pendingImages){let a=document.createElement("img");a.className="user-image",a.src=i.previewUrl,t.appendChild(a)}let s=n.pendingImages.slice();n.pendingImages.length=0,pe(),s.length>0?Promise.all(s.map(i=>fetch(`/api/v1/sessions/${n.sessionId}/images`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({data:i.data,mimeType:i.mimeType})}).then(a=>a.json()).then(a=>({data:i.data,mimeType:i.mimeType,path:a.url})))).then(i=>{if(!Be()){t.remove(),c("warn: Not connected, please retry"),h(!1);return}he(n.sessionId,e||"What is in this image?",i.map(a=>({data:a.data,mimeType:a.mimeType,path:a.path}))).catch(()=>{})}):he(n.sessionId,e).catch(()=>{}),n.turnEnded=!1,n.sentMessageForSession=n.sessionId,h(!0),Xe()}function wt(){Q()&&c("^C")}function vt(){let e=o.input.value.trim();return e.startsWith("/")||e.startsWith("!")||e==="?"||e.startsWith("? ")}function St(){n.busy&&(vt()?(o.sendBtn.textContent="\u21B5",o.sendBtn.title="Send (Enter)",o.sendBtn.classList.remove("cancel")):(o.sendBtn.textContent="^C",o.sendBtn.title="Cancel (Ctrl+C)",o.sendBtn.classList.add("cancel")))}o.sendBtn.onclick=()=>{n.busy&&!vt()?wt():yt()};o.input.addEventListener("keydown",e=>{if(ft(e)){e.preventDefault();return}if(e.key==="Enter"&&!e.shiftKey){e.preventDefault(),y(),yt();return}if(e.key==="u"&&(e.ctrlKey||e.metaKey)&&!e.shiftKey){e.preventDefault(),o.fileInput.click();return}});document.addEventListener("keydown",e=>{if(e.key==="c"&&(e.ctrlKey||e.metaKey)&&!e.shiftKey&&n.busy&&!(window.getSelection()?.toString()||o.input.selectionStart!==o.input.selectionEnd)){e.preventDefault(),wt();return}e.key==="Escape"&&o.slashMenu.classList.contains("active")&&(e.preventDefault(),y(),o.input.focus())});function It(){let e=x("mode");if(!e||!e.options.length)return;let t=e.options.findIndex(i=>i.value===e.currentValue),s=e.options[(t+1)%e.options.length];e.currentValue=s.value,G(n.sessionId,"mode",s.value).catch(()=>{}),c(`Mode \u2192 ${s.name}`),q()}document.addEventListener("keydown",e=>{e.key==="m"&&(e.ctrlKey||e.metaKey)&&!e.shiftKey&&(e.preventDefault(),It())});o.prompt.addEventListener("click",It);o.input.addEventListener("input",St);o.input.addEventListener("input",()=>{o.input.style.height="auto",o.input.style.height=Math.min(o.input.scrollHeight,200)+"px"});async function on(e){try{let t=await navigator.serviceWorker?.ready;if(!t)return;let s=await t.pushManager.getSubscription();if(!s)return;await fetch("/api/beta/push/register-client",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({clientId:e,endpoint:s.endpoint})})}catch{}}function Ae(){A("connecting","connecting");let e=new EventSource("/api/v1/events/stream");n.eventSource=e,e.onmessage=async t=>{let s=JSON.parse(t.data);s.type==="connected"&&s.clientId&&(n.clientId=s.clientId,J(s.clientId,!document.hidden,n.sessionId??void 0).catch(()=>{}),on(s.clientId),!s.agent)||C(s)},e.onerror=()=>{e.close(),an(),setTimeout(Ae,3e3)},rn()}async function rn(){A("connecting","session loading");let e=n.sessionSwitchGen,t=Ge();if(t&&t===n.sessionId&&n.lastEventSeq>0){if(await Oe(t,!0,e),e!==n.sessionSwitchGen)return;dt(),I(!1);return}if(t){if(w(),await Oe(t,!1,e),e!==n.sessionSwitchGen)return;I(!0);return}try{let s=await ie();if(e!==n.sessionSwitchGen)return;if(s.length>0){if(w(),await Oe(s[0].id,!1,e),e!==n.sessionSwitchGen)return;I(!0);return}}catch{}e===n.sessionSwitchGen&&L()}async function Oe(e,t,s){if(t){try{let i=await $(e);if(s!==n.sessionSwitchGen)return;C({type:"session_created",sessionId:i.id,cwd:i.cwd,title:i.title,configOptions:i.configOptions,busyKind:i.busyKind})}catch{if(s!==n.sessionSwitchGen)return;let i=n.sessionCwd;w(),c("warn: Previous session expired, created new one."),L({cwd:i||void 0});return}if(s!==n.sessionSwitchGen)return;await Me(e)}else{n.sessionId=null;let i=O(e),a;try{let[r,l]=await Promise.all([$(e),i]);if(s!==n.sessionSwitchGen)return;a=r,l||c("warn: Failed to load history.")}catch{if(s!==n.sessionSwitchGen)return;let r=n.sessionCwd;w(),c("warn: Previous session expired, created new one."),L({cwd:r||void 0});return}C({type:"session_created",sessionId:a.id,cwd:a.cwd,title:a.title,configOptions:a.configOptions,busyKind:a.busyKind})}}function an(){A("disconnected","disconnected"),n.eventSource=null,n.clientId=null,k(),H(),n.currentBashEl&&X(n.currentBashEl,null,"disconnected"),n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,n.turnEnded=!1,Y(),h(!1)}document.addEventListener("visibilitychange",()=>{n.clientId&&J(n.clientId,!document.hidden,n.sessionId??void 0).catch(()=>{}),!document.hidden&&n.sessionId&&n.lastEventSeq>0&&!n.replayInProgress&&Me(n.sessionId).then(()=>I(!1))});o.messages.addEventListener("click",Ye);it(ze);Ae();fetch("/api/v1/version").then(e=>e.json()).then(e=>{typeof e.server=="string"&&(n.serverVersion=e.server);let t=e.agent;t&&(n.agentName=t.name??null,n.agentVersion=t.version??null)}).catch(()=>{});"serviceWorker"in navigator&&(navigator.serviceWorker.register("/sw.js"),navigator.serviceWorker.addEventListener("message",e=>{if(e.data?.type==="navigate"&&e.data.sessionId){let t=e.data.sessionId;if(n.sessionId===t)return;n.sessionSwitchGen++;let s=n.sessionSwitchGen;history.replaceState(null,"",`#${t}`),w(),n.sessionId=null,c("Switching\u2026"),Promise.all([$(t),O(t)]).then(([i,a])=>{s===n.sessionSwitchGen&&(C({type:"session_created",sessionId:i.id,cwd:i.cwd,title:i.title,configOptions:i.configOptions,busyKind:i.busyKind}),a&&I(!0))}).catch(()=>{w(),n.sessionId=null,c("err: Failed to switch session")})}}));