@lelouchhe/webagent 0.2.5 → 0.2.6

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/dist/index.html CHANGED
@@ -7,13 +7,14 @@
7
7
  <link rel="icon" href="/icons/icon.svg" type="image/svg+xml">
8
8
  <link rel="apple-touch-icon" href="/icons/icon-180.png">
9
9
  <link rel="manifest" href="/manifest.json">
10
- <meta name="theme-color" content="#0d1117">
10
+ <meta name="theme-color" content="#0d1117" media="(prefers-color-scheme: dark)">
11
+ <meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
11
12
  <meta name="apple-mobile-web-app-capable" content="yes">
12
- <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
13
+ <meta name="apple-mobile-web-app-status-bar-style" content="default">
13
14
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
14
15
  <script src="https://cdn.jsdelivr.net/npm/dompurify@3.3.2/dist/purify.min.js"></script>
15
16
  <script>document.documentElement.setAttribute('data-theme', localStorage.getItem('theme') || 'auto');</script>
16
- <link rel="stylesheet" href="/styles.01a6wdjv.css">
17
+ <link rel="stylesheet" href="/styles.008ve1hx.css">
17
18
  </head>
18
19
  <body>
19
20
 
@@ -41,6 +42,6 @@
41
42
  </div>
42
43
  <div id="status-bar"></div>
43
44
 
44
- <script src="/js/app.6BGATUZ7.js"></script>
45
+ <script src="/js/app.4FZ67UW4.js"></script>
45
46
  </body>
46
47
  </html>
@@ -0,0 +1,10 @@
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")})}}));
@@ -11,7 +11,7 @@
11
11
  --blue: #58a6ff;
12
12
  --radius: 4px;
13
13
  --user-bg: #1e3a5f;
14
- --code-bg: #161b22;
14
+ --code-bg: #0d2818;
15
15
  --permission-bg: #1c1305;
16
16
  --permission-title: #d29922;
17
17
  }
@@ -27,7 +27,7 @@
27
27
  --red: #cf222e;
28
28
  --blue: #0969da;
29
29
  --user-bg: #ddf4ff;
30
- --code-bg: #f6f8fa;
30
+ --code-bg: #f0faf0;
31
31
  --permission-bg: #fff4cc;
32
32
  --permission-title: #7a4f00;
33
33
  }
@@ -44,7 +44,7 @@
44
44
  --red: #cf222e;
45
45
  --blue: #0969da;
46
46
  --user-bg: #ddf4ff;
47
- --code-bg: #f6f8fa;
47
+ --code-bg: #f0faf0;
48
48
  --permission-bg: #fff4cc;
49
49
  --permission-title: #7a4f00;
50
50
  }
@@ -181,17 +181,79 @@ body {
181
181
  padding: 4px 14px;
182
182
  }
183
183
  .msg.assistant pre {
184
- background: var(--code-bg);
185
- padding: 10px;
186
- border-radius: 4px;
184
+ background: none;
185
+ padding: 8px 14px;
187
186
  overflow-x: auto;
188
- margin: 8px 0;
187
+ margin: 0;
189
188
  font-size: 15px;
189
+ border-radius: 0;
190
190
  }
191
191
  .msg.assistant code {
192
192
  font-family: "SF Mono", "Fira Code", monospace;
193
193
  font-size: 15px;
194
194
  }
195
+
196
+ /* Code block wrapper — aligned with prompt/permission/thinking style */
197
+ .code-block-wrapper {
198
+ margin: 8px -14px;
199
+ background: var(--code-bg);
200
+ border-left: 3px solid var(--green);
201
+ border-radius: 0 var(--radius) var(--radius) 0;
202
+ position: relative;
203
+ overflow: hidden;
204
+ }
205
+ .code-block-wrapper pre {
206
+ margin: 0;
207
+ padding: 8px 14px;
208
+ }
209
+ .code-block-wrapper pre code,
210
+ .code-block-wrapper pre code.hljs {
211
+ background: none;
212
+ padding: 0;
213
+ }
214
+ .code-toolbar {
215
+ display: flex;
216
+ align-items: center;
217
+ justify-content: flex-end;
218
+ padding: 2px 6px;
219
+ position: absolute;
220
+ top: 0;
221
+ right: 0;
222
+ z-index: 1;
223
+ font-size: 12px;
224
+ color: var(--text-dim);
225
+ gap: 8px;
226
+ opacity: 0;
227
+ transition: opacity 0.15s;
228
+ }
229
+ .code-block-wrapper:hover .code-toolbar {
230
+ opacity: 1;
231
+ }
232
+ .copy-btn {
233
+ background: transparent;
234
+ color: var(--text-dim);
235
+ border: 1px solid var(--border);
236
+ border-radius: var(--radius);
237
+ min-width: 28px;
238
+ height: 22px;
239
+ padding: 0 4px;
240
+ font-size: 12px;
241
+ font-family: inherit;
242
+ cursor: pointer;
243
+ display: inline-flex;
244
+ align-items: center;
245
+ justify-content: center;
246
+ line-height: 1;
247
+ transition: color 0.15s, border-color 0.15s;
248
+ }
249
+ .copy-btn:hover {
250
+ color: var(--text);
251
+ border-color: var(--text-dim);
252
+ }
253
+ .copy-btn.copied {
254
+ color: var(--green);
255
+ border-color: var(--green);
256
+ }
195
257
  .msg.assistant p { margin: 4px 0; }
196
258
  .msg.assistant ul, .msg.assistant ol { padding-left: 20px; margin: 4px 0; }
197
259
 
package/lib/bridge.js CHANGED
@@ -2,6 +2,7 @@ import { spawn, ChildProcess } from "node:child_process";
2
2
  import { Writable, Readable } from "node:stream";
3
3
  import { EventEmitter } from "node:events";
4
4
  import * as acp from "@agentclientprotocol/sdk";
5
+ import { interruptBashProc } from "./session-manager.js";
5
6
  export class AgentBridge extends EventEmitter {
6
7
  proc = null;
7
8
  conn = null;
@@ -10,6 +11,7 @@ export class AgentBridge extends EventEmitter {
10
11
  silentSessions = new Set(); // Sessions that don't emit events
11
12
  silentBuffers = new Map(); // Text buffers for silent sessions
12
13
  agentCmd;
14
+ reloading = false;
13
15
  constructor(agentCmd) {
14
16
  super();
15
17
  this.agentCmd = agentCmd;
@@ -161,6 +163,72 @@ export class AgentBridge extends EventEmitter {
161
163
  this.permissionRequestSessions.delete(requestId);
162
164
  }
163
165
  }
166
+ /**
167
+ * Restart the agent subprocess. Cancels all active work, cleans up state,
168
+ * shuts down the old process, and starts a new one. Sessions are restored
169
+ * lazily via ensureResumed() on next user interaction.
170
+ */
171
+ async restart(sessions, titleService) {
172
+ if (this.reloading)
173
+ throw new Error("Already reloading");
174
+ this.reloading = true;
175
+ this.emit("event", { type: "agent_reloading" });
176
+ console.log("[bridge] reloading agent...");
177
+ try {
178
+ // 1. Cancel all active prompts + kill bash procs
179
+ for (const sessionId of [...sessions.activePrompts]) {
180
+ const proc = sessions.runningBashProcs.get(sessionId);
181
+ if (proc) {
182
+ interruptBashProc(proc);
183
+ sessions.runningBashProcs.delete(sessionId);
184
+ }
185
+ try {
186
+ await this.cancel(sessionId);
187
+ }
188
+ catch { /* best-effort */ }
189
+ }
190
+ // 2. Flush buffers to persist partial content
191
+ for (const sessionId of sessions.liveSessions) {
192
+ sessions.flushBuffers(sessionId);
193
+ }
194
+ // 3. Clean up SessionManager state
195
+ sessions.pendingPermissions.clear();
196
+ sessions.activePrompts.clear();
197
+ // 4. Clean up bridge-side silent session state
198
+ this.silentSessions.clear();
199
+ this.silentBuffers.clear();
200
+ // 5. Invalidate title service session
201
+ titleService.invalidate();
202
+ // 5. Clear liveSessions so ensureResumed() will re-register on next access
203
+ sessions.liveSessions.clear();
204
+ // 6. Shutdown old process
205
+ await this.shutdown();
206
+ // 7. Start new process with retry (exponential backoff, max 3 attempts)
207
+ let lastError;
208
+ for (let i = 0; i < 3; i++) {
209
+ try {
210
+ await this.start();
211
+ lastError = null;
212
+ break;
213
+ }
214
+ catch (err) {
215
+ lastError = err;
216
+ console.error(`[bridge] start attempt ${i + 1} failed:`, err);
217
+ if (i < 2)
218
+ await new Promise((r) => setTimeout(r, 1000 * 2 ** i));
219
+ }
220
+ }
221
+ if (lastError || !this.conn) {
222
+ const msg = lastError instanceof Error ? lastError.message : String(lastError);
223
+ this.emit("event", { type: "agent_reloading_failed", error: msg });
224
+ throw lastError;
225
+ }
226
+ console.log("[bridge] agent reloaded successfully");
227
+ }
228
+ finally {
229
+ this.reloading = false;
230
+ }
231
+ }
164
232
  async shutdown() {
165
233
  // Reject all pending permissions
166
234
  for (const [id, resolve] of this.permissionResolvers) {
@@ -169,16 +237,17 @@ export class AgentBridge extends EventEmitter {
169
237
  this.permissionResolvers.clear();
170
238
  this.permissionRequestSessions.clear();
171
239
  if (this.proc && this.proc.exitCode === null) {
172
- this.proc.kill();
240
+ const proc = this.proc;
173
241
  await new Promise((resolve) => {
174
242
  const timer = setTimeout(() => {
175
- this.proc?.kill(process.platform === "win32" ? undefined : "SIGKILL");
243
+ proc.kill(process.platform === "win32" ? undefined : "SIGKILL");
176
244
  resolve();
177
245
  }, 5000);
178
- this.proc?.on("exit", () => {
246
+ proc.on("exit", () => {
179
247
  clearTimeout(timer);
180
248
  resolve();
181
249
  });
250
+ proc.kill();
182
251
  });
183
252
  }
184
253
  this.proc = null;
@@ -64,14 +64,13 @@ export function handleAgentEvent(event, sessions, store, bridge, config, sseMana
64
64
  });
65
65
  // Broadcast both so the frontend can render then collapse the permission card
66
66
  sseManager.broadcast(event);
67
- const resolvedEvent = {
68
- type: "permission_resolved",
67
+ sseManager.broadcast({
68
+ type: "permission_response",
69
69
  sessionId: event.sessionId,
70
70
  requestId: event.requestId,
71
71
  optionName,
72
72
  denied: false,
73
- };
74
- sseManager.broadcast(resolvedEvent);
73
+ });
75
74
  return;
76
75
  }
77
76
  }
package/lib/routes.js CHANGED
@@ -60,7 +60,7 @@ export function createRequestHandler(deps) {
60
60
  sessions: "/api/v1/sessions",
61
61
  config: "/api/v1/config",
62
62
  events_stream: "/api/v1/events/stream",
63
- prompt: "/api/v1/prompt",
63
+ prompt: "/api/beta/prompt",
64
64
  push: "/api/beta/push",
65
65
  clients: "/api/beta/clients",
66
66
  },
@@ -90,6 +90,26 @@ export function createRequestHandler(deps) {
90
90
  });
91
91
  return;
92
92
  }
93
+ // --- POST /api/v1/bridge/reload ---
94
+ if (url === "/api/v1/bridge/reload" && req.method === "POST") {
95
+ const bridge = getBridge?.();
96
+ if (!bridge) {
97
+ json(res, 503, { error: "Agent not ready yet" });
98
+ return;
99
+ }
100
+ if (bridge.reloading) {
101
+ json(res, 409, { error: "Already reloading" });
102
+ return;
103
+ }
104
+ try {
105
+ await bridge.restart(sessions, titleService);
106
+ json(res, 200, { ok: true });
107
+ }
108
+ catch (err) {
109
+ json(res, 500, { error: errorMessage(err) });
110
+ }
111
+ return;
112
+ }
93
113
  // --- Permissions (session-scoped) ---
94
114
  // GET /api/v1/sessions/:id/permissions
95
115
  const permListMatch = url.match(/^\/api\/v1\/sessions\/([^/]+)\/permissions\/?(\?.*)?$/);
@@ -140,18 +160,14 @@ export function createRequestHandler(deps) {
140
160
  await bridge.resolvePermission(requestId, optionId);
141
161
  }
142
162
  sessions.pendingPermissions.delete(requestId);
143
- // Store event and broadcast
144
- store.saveEvent(perm.sessionId, "permission_response", {
145
- requestId, optionId, optionName, denied,
146
- });
147
- const permEvent = {
148
- type: "permission_resolved",
163
+ // Store event and broadcast (same type so SSE drops are recoverable via sync)
164
+ const permEventData = { requestId, optionName, denied };
165
+ store.saveEvent(perm.sessionId, "permission_response", { ...permEventData, optionId });
166
+ sseManager.broadcast({
167
+ type: "permission_response",
149
168
  sessionId: perm.sessionId,
150
- requestId,
151
- optionName,
152
- denied,
153
- };
154
- sseManager.broadcast(permEvent);
169
+ ...permEventData,
170
+ });
155
171
  json(res, 200, { ok: true });
156
172
  return;
157
173
  }
@@ -619,48 +635,6 @@ export function createRequestHandler(deps) {
619
635
  json(res, 200, envelope, req);
620
636
  return;
621
637
  }
622
- // POST /api/v1/prompt — quick one-shot prompt (create temp session + send)
623
- if (url === "/api/v1/prompt" && req.method === "POST") {
624
- if (!sessions || !getBridge) {
625
- json(res, 503, { error: "Agent not available" });
626
- return;
627
- }
628
- const bridge = getBridge();
629
- if (!bridge) {
630
- json(res, 503, { error: "Agent not available" });
631
- return;
632
- }
633
- let body;
634
- try {
635
- body = JSON.parse(await readBody(req));
636
- }
637
- catch {
638
- json(res, 400, { error: "Invalid JSON" });
639
- return;
640
- }
641
- const text = body.text;
642
- if (!text || typeof text !== "string") {
643
- json(res, 400, { error: "Missing required field: text" });
644
- return;
645
- }
646
- const cwd = body.cwd || undefined;
647
- const { sessionId } = await sessions.createSession(bridge, cwd, undefined, "auto");
648
- const streamUrl = `/api/v1/sessions/${sessionId}/events/stream`;
649
- json(res, 202, { sessionId, streamUrl });
650
- // Fire-and-forget: send the prompt asynchronously, tracking busy state
651
- sessions.activePrompts.add(sessionId);
652
- // Generate title (fire-and-forget)
653
- if (titleService && !sessions.sessionHasTitle.has(sessionId)) {
654
- titleService.generate(bridge, text, sessionId, (title) => {
655
- const titleEvent = { type: "session_title_updated", sessionId, title };
656
- sseManager.broadcast(titleEvent);
657
- });
658
- }
659
- bridge.prompt(sessionId, text)
660
- .catch(() => { })
661
- .finally(() => sessions.activePrompts.delete(sessionId));
662
- return;
663
- }
664
638
  // --- SSE stream endpoints ---
665
639
  // GET /api/v1/events/stream — global SSE stream
666
640
  if (url.startsWith("/api/v1/events/stream") && req.method === "GET") {
@@ -800,6 +774,48 @@ export function createRequestHandler(deps) {
800
774
  // --- Beta API routes ---
801
775
  if (url.startsWith("/api/beta/")) {
802
776
  res.setHeader("Content-Type", "application/json");
777
+ // POST /api/beta/prompt — quick one-shot prompt (create temp session + send)
778
+ if (url === "/api/beta/prompt" && req.method === "POST") {
779
+ if (!sessions || !getBridge) {
780
+ json(res, 503, { error: "Agent not available" });
781
+ return;
782
+ }
783
+ const bridge = getBridge();
784
+ if (!bridge) {
785
+ json(res, 503, { error: "Agent not available" });
786
+ return;
787
+ }
788
+ let body;
789
+ try {
790
+ body = JSON.parse(await readBody(req));
791
+ }
792
+ catch {
793
+ json(res, 400, { error: "Invalid JSON" });
794
+ return;
795
+ }
796
+ const text = body.text;
797
+ if (!text || typeof text !== "string") {
798
+ json(res, 400, { error: "Missing required field: text" });
799
+ return;
800
+ }
801
+ const cwd = body.cwd || undefined;
802
+ const { sessionId } = await sessions.createSession(bridge, cwd, undefined, "auto");
803
+ const streamUrl = `/api/v1/sessions/${sessionId}/events/stream`;
804
+ json(res, 202, { sessionId, streamUrl });
805
+ // Fire-and-forget: send the prompt asynchronously, tracking busy state
806
+ sessions.activePrompts.add(sessionId);
807
+ // Generate title (fire-and-forget)
808
+ if (titleService && !sessions.sessionHasTitle.has(sessionId)) {
809
+ titleService.generate(bridge, text, sessionId, (title) => {
810
+ const titleEvent = { type: "session_title_updated", sessionId, title };
811
+ sseManager.broadcast(titleEvent);
812
+ });
813
+ }
814
+ bridge.prompt(sessionId, text)
815
+ .catch(() => { })
816
+ .finally(() => sessions.activePrompts.delete(sessionId));
817
+ return;
818
+ }
803
819
  // POST /api/beta/clients/:clientId/visibility
804
820
  const visMatch = url.match(/^\/api\/beta\/clients\/([^/]+)\/visibility$/);
805
821
  if (visMatch && req.method === "POST") {
@@ -56,6 +56,10 @@ export class TitleService {
56
56
  return;
57
57
  await bridge.cancel(this.titleSessionId);
58
58
  }
59
+ /** Clear the cached title session ID (e.g. after agent reload). */
60
+ invalidate() {
61
+ this.titleSessionId = null;
62
+ }
59
63
  /** Ensure the dedicated title session exists. Returns session ID or null. */
60
64
  async ensureTitleSession(bridge) {
61
65
  if (this.titleSessionId)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lelouchhe/webagent",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
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 ue=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 r=`HTTP ${s.status}`;try{let a=await s.json();a.error&&(r=String(a.error))}catch{}throw new ue(s.status,r)}let i=await s.text();if(i)return JSON.parse(i)}function q(e,t){return U(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})}function _e(e){let t={};return e?.cwd&&(t.cwd=e.cwd),e?.inheritFromSessionId&&(t.inheritFromSessionId=e.inheritFromSessionId),q("/api/v1/sessions",t)}function pe(e){return U("/api/v1/sessions/"+e,{method:"DELETE"})}function te(){return U("/api/v1/sessions")}function $(e){return U("/api/v1/sessions/"+e)}function fe(e,t,s){let i={text:t};return s?.length&&(i.images=s),q("/api/v1/sessions/"+e+"/prompt",i)}function Ne(e){return q("/api/v1/sessions/"+e+"/cancel",{})}function ne(e,t,s){return q("/api/v1/sessions/"+e+"/permissions/"+t,{optionId:s})}function se(e,t){return q("/api/v1/sessions/"+e+"/permissions/"+t,{denied:!0})}function K(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 qe(e,t){return U("/api/v1/sessions/"+e+"/title",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:t})})}function Be(e,t){return q("/api/v1/sessions/"+e+"/bash",{command:t})}function V(e,t,s){let i={visible:t};return s&&(i.sessionId=s),q("/api/beta/clients/"+e+"/visibility",i)}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},dt={disconnected:"is-disconnected",connecting:"is-connecting",connected:"is-connected"};function B(e,t=e){o.status.textContent="",o.status.className=`status-dot ${dt[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 ie(e){return x(e)?.currentValue??null}function Oe(e,t){let s=x(e);s&&(s.currentValue=t)}function me(e){n.configOptions=e,_(),O()}function _(){o.inputArea.classList.remove("plan-mode","autopilot-mode");let e=ie("mode")||"";e.includes("#plan")?o.inputArea.classList.add("plan-mode"):e.includes("#autopilot")&&o.inputArea.classList.add("autopilot-mode")}function O(){if(!o.statusBar)return;let e=ie("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 I(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 M({cwd:e,inheritFromSessionId:t=n.sessionId}={}){n.awaitingNewSession=!0,_e({cwd:e,inheritFromSessionId:t}).catch(()=>{})}var Ae=[];function Re(e){Ae.push(e)}function y(){for(let e of Ae)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 ?",I(!1),n.sessionTitle=null,n.sessionCwd=null,n.configOptions=[],oe(null,null),o.statusBar&&(o.statusBar.textContent="")}function G(){return!n.busy||!n.sessionId?!1:(Ne(n.sessionId).catch(()=>{}),J(),n.cancelTimeout>0&&(n._cancelTimerId=setTimeout(()=>{n._cancelTimerId=null,n.busy&&(n.turnEnded=!0,I(!1),n._onCancelTimeout?.())},n.cancelTimeout)),!0)}function J(){n._cancelTimerId!=null&&(clearTimeout(n._cancelTimerId),n._cancelTimerId=null)}function De(){return location.hash.slice(1)||null}function Fe(e){history.replaceState(null,"",`#${e}`)}function oe(e,t){o.sessionInfo.textContent=t||(e?e.slice(0,8)+"\u2026":""),document.title=t||">_"}B("disconnected");marked.setOptions({breaks:!0,gfm:!0});function re(e){return DOMPurify.sanitize(marked.parse(e))}function R(e,t){let s=document.createElement("div");return s.className=`msg ${e}`,s.innerHTML=e==="user"?p(t).replace(/\n/g,"<br>"):re(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(){n.currentAssistantEl=null,n.currentAssistantText=""}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 A=null,ut=80;function he(e){return e.scrollHeight-e.scrollTop-e.clientHeight<ut}function pt(){n.followMessages=he(o.messages)}o.messages.addEventListener("scroll",pt);function ft(){return n.followMessages||he(o.messages)}function E(e,t=!1){if(n.replayTarget)return n.replayTarget.appendChild(e),e;let s=t||ft();return o.messages.appendChild(e),S(s),e}function je(){D(),A=document.createElement("div"),A.id="waiting",A.innerHTML='<span class="cursor">\u258C</span>',E(A,!0)}function D(){A&&(A.remove(),A=null)}var ge=!1;function S(e){let t=o.messages;if(e||n.followMessages){typeof requestAnimationFrame=="function"?ge||(ge=!0,requestAnimationFrame(()=>{ge=!1,t.scrollTop=t.scrollHeight})):t.scrollTop=t.scrollHeight,n.followMessages=!0;return}n.followMessages=he(t)}function p(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}function We(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 ye(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 Q(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 Y(e,t,s){if(!e)return;let i=e.querySelector(".bash-cmd");i.classList.remove("running");let r="";if(s?r=`[signal: ${s}]`:t!==0&&t!=null&&(r=`[exit: ${t}]`),r){let a=document.createElement("span");a.className=`bash-exit ${t===0?"ok":"fail"}`,a.textContent=r,i.after(a)}e===n.currentBashEl&&(n.currentBashEl=null)}var mt={auto:"\u25D1",light:"\u2600",dark:"\u263E"},Ue=["auto","light","dark"];function Ke(){return localStorage.getItem("theme")||"auto"}function Ve(e){document.documentElement.setAttribute("data-theme",e),o.themeBtn.textContent=mt[e],o.themeBtn.title=`Theme: ${e}`,localStorage.setItem("theme",e)}o.themeBtn.onclick=()=>{let e=Ke();Ve(Ue[(Ue.indexOf(e)+1)%3])};Ve(Ke());var we={task_complete:"\u2714",read:"cat",edit:"edit",execute:"exec",search:"find",delete:"rm"},ve="run",Ie={pending:"\u25CB",in_progress:"\u25C9",completed:"\u25CF"};function Ge(e){return n.replayTarget?.querySelector(`[id="${e}"]`)??document.getElementById(e)}function gt(e){return n.replayTarget?.querySelector(e)??document.querySelector(e)}var Je="webagent_notify_tip_shown",Qe="webagent_notify_tip_denied_shown";function ht(){if(typeof Notification>"u"||n.replayInProgress)return;let e=Notification.permission;if(e!=="granted"){if(e==="denied"){if(localStorage.getItem(Qe))return;localStorage.setItem(Qe,"1"),c("tip: notifications are blocked \u2014 allow in browser site settings to enable");return}localStorage.getItem(Je)||(localStorage.setItem(Je,"1"),c("tip: use /notify to enable background notifications"))}}function ae(){n.pendingPromptDone&&(n.pendingToolCallIds.size>0||n.pendingPermissionRequestIds.size>0||(D(),k(),P(),I(!1),n.pendingPromptDone=!1,ht()))}function yt(){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 wt(){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 be(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 Ye=200;async function N(e){n.replayInProgress=!0,n.replayQueue=[];try{let t=await fetch(`/api/v1/sessions/${e}/events?limit=${Ye}`);if(!t.ok)return!1;let s=await t.json(),{events:i,streaming:r,hasMore:a}=be(s),l=document.createDocumentFragment(),d=Ee(i);n.replayTarget=l;for(let u=0;u<i.length;u++){let f=JSON.parse(i[u].data);ke(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=a===!0,n.hasMoreHistory&&vt(),ze(),Se(i,r),!0}catch{return!1}finally{n.replayTarget=null,n.replayInProgress=!1,Xe()}}function Se(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),r=o.messages.querySelectorAll(".thinking"),a=r[r.length-1];if(a){n.currentThinkingEl=a,n.currentThinkingText=i.text;let l=a.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),r=o.messages.querySelectorAll(".msg.assistant"),a=r[r.length-1];a&&(n.currentAssistantEl=a,n.currentAssistantText=i.text);break}}}function ze(){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 Te(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:r,streaming:a}=be(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,r.length===0)return Se(r,a),!0;let d=document.createDocumentFragment(),u=Ee(r);n.replayTarget=d;for(let f=0;f<r.length;f++){let g=JSON.parse(r[f].data);ke(r[f].type,g,r,f,u)}return n.replayTarget=null,o.messages.appendChild(d),n.lastEventSeq=r[r.length-1].seq,ze(),Se(r,a),!0}catch{return!1}finally{n.replayTarget=null,n.replayInProgress=!1,Xe()}}var z=null;function vt(){le();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"&&(z=new IntersectionObserver(t=>{t[0]?.isIntersecting&&!n.loadingOlderEvents&&n.hasMoreHistory&&n.sessionId&&It(n.sessionId)},{root:o.messages,rootMargin:"200px 0px 0px 0px"}),z.observe(e))}function le(){z&&(z.disconnect(),z=null),document.getElementById("history-sentinel")?.remove()}Re(le);async function It(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=${Ye}&before=${n.oldestLoadedSeq}`);if(!t.ok||e!==n.sessionId)return!1;let s=await t.json(),{events:i,hasMore:r}=be(s);if(i.length===0)return n.hasMoreHistory=!1,le(),!0;let a=document.createDocumentFragment(),l=Ee(i);n.replayTarget=a;for(let g=0;g<i.length;g++){let v=JSON.parse(i[g].data);ke(i[g].type,v,i,g,l)}n.replayTarget=null;let d=o.messages,u=d.scrollHeight,f=document.getElementById("history-sentinel");return f?f.after(a):d.prepend(a),d.scrollTop+=d.scrollHeight-u,n.oldestLoadedSeq=i[0].seq,n.hasMoreHistory=r===!0,n.hasMoreHistory||le(),!0}catch{return!1}finally{n.loadingOlderEvents=!1}}function Ze(){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?se(t.sessionId,e).catch(()=>{}):ne(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 Ee(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 ke(e,t,s,i,r){switch(e){case"user_message":{let a=R("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,a.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=re(f);break}R("assistant",t.text).setAttribute("data-raw",t.text);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 a=we[t.kind]||ve,l=document.createElement("div");l.className="tool-call",l.id=`tc-${t.id}`,l.dataset.kind=t.kind;let d=`<span class="icon">${a}</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"?ye(u):null;if(f){let v=document.createElement("details");v.innerHTML=`<summary>diff</summary><div class="diff-view">${f}</div>`,l.appendChild(v)}let g=l.querySelector(".tc-detail");g&&l.addEventListener("click",v=>{v.target.closest("details")||g.classList.toggle("expanded")}),E(l),r&&r.toolCalls.set(t.id,l);break}case"tool_call_update":{let a=r?r.toolCalls.get(t.id):Ge(`tc-${t.id}`);if(a){let l=t.status==="completed"?"\u2713":t.status==="failed"?"\u2717":"\u2026";a.className=`tool-call ${t.status}`;let d=a.querySelector(".icon");if(d&&(d.textContent=l),t.content&&t.content.length&&!a.querySelector("details")&&!a.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(a.dataset.kind==="task_complete"){let f=document.createElement("div");f.className="tc-summary",f.textContent=u,a.appendChild(f)}else{let f=document.createElement("details");f.innerHTML=`<summary>output</summary><div class="tc-content">${p(u)}</div>`,a.appendChild(f)}}}(t.status==="completed"||t.status==="failed")&&n.pendingToolCallIds.delete(t.id);break}case"plan":{let a=document.createElement("div");a.className="plan",a.innerHTML='<div class="plan-title">\u2015 plan</div>'+(t.entries||[]).map(l=>`<div class="plan-entry">${Ie[l.status]||"?"} ${p(l.content)}</div>`).join(""),E(a);break}case"permission_request":{let a=document.createElement("div");a.className="permission",a.dataset.requestId=t.requestId,a.dataset.title=t.title||"",a.innerHTML=`<span class="title" style="opacity:0.5">\u26BF ${p(t.title)}</span> `,!(r?r.resolvedPermissions.has(t.requestId):s&&s.slice(i+1).some(d=>d.type==="permission_response"&&JSON.parse(d.data).requestId===t.requestId))&&t.options&&(a.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")?se(n.sessionId,t.requestId).catch(()=>{}):ne(n.sessionId,t.requestId,d.optionId).catch(()=>{}),a.innerHTML=`<span style="opacity:0.5">\u26BF ${p(t.title)} \u2014 ${p(d.name)}</span>`},a.appendChild(u)})),E(a),r&&r.permissions.set(t.requestId,a);break}case"permission_response":{let a=r?r.permissions.get(t.requestId):gt(`.permission[data-request-id="${t.requestId}"]`);if(a){let l=a.dataset.title?`\u26BF ${a.dataset.title}`:"\u26BF",d=t.optionName||(t.denied?"denied":"allowed");a.innerHTML=`<span style="opacity:0.5">${p(l)} \u2014 ${p(d)}</span>`}break}case"bash_command":{let a=Q(t.command,!1);r?r.currentBashEl=a:a.id="bash-replay-pending";break}case"bash_result":{let a=r?r.currentBashEl:Ge("bash-replay-pending");if(a){if(r||a.removeAttribute("id"),t.output){let l=a.querySelector(".bash-output");l&&(l.textContent=t.output,l.classList.add("has-content"))}Y(a,t.code,t.signal),r&&(r.currentBashEl=null)}break}case"prompt_done":n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,I(!1);break}}function Xe(){let e=n.replayQueue;n.replayQueue=[];for(let t of e)St(t)||C(t)}function St(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":e.cancelTimeout!=null&&(n.cancelTimeout=e.cancelTimeout);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&&me(e.configOptions),Fe(n.sessionId),n.clientId&&V(n.clientId,!document.hidden,n.sessionId).catch(()=>{}),oe(n.sessionId,n.sessionTitle),B("connected","connected"),o.input.disabled=!1,o.sendBtn.disabled=!1,o.input.placeholder="Message or ?",I(!!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"}`),O();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=R("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;D(),k(),n.currentAssistantEl||(n.currentAssistantEl=R("assistant",""),n.currentAssistantText=""),n.currentAssistantText+=e.text,n.currentAssistantEl.innerHTML=re(n.currentAssistantText),S();break;case"thought_chunk":if(n.turnEnded)break;D(),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,S();break;case"tool_call":{if(n.turnEnded)break;n.pendingToolCallIds.add(e.id),I(!0),D(),k(),P();let t=we[e.kind]||ve,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)}`,r=e.rawInput;r&&r.command?i+=`<span class="tc-detail">$ ${p(r.command)}</span>`:r&&r.path&&(i+=`<span class="tc-detail">${p(r.path)}</span>`),s.innerHTML=i;let a=e.kind==="edit"?ye(r):null;if(a){let d=document.createElement("details");d.innerHTML=`<summary>diff</summary><div class="diff-view">${a}</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 r=e.content.map(a=>a.type==="terminal"?`[terminal ${a.terminalId}]`:a.content?.text?a.content.text:Array.isArray(a.content)?a.content.map(l=>l.text||"").join(""):"").filter(Boolean).join(`
10
- `);if(r)if(t.dataset.kind==="task_complete"){let a=document.createElement("div");a.className="tc-summary",a.textContent=r,t.appendChild(a)}else{let a=document.createElement("details");a.innerHTML=`<summary>output</summary><div class="tc-content">${p(r)}</div>`,t.appendChild(a)}}}ae(),S();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">${Ie[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),I(!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"),r=(s.kind||"").includes("allow");i.className=r?"allow":"deny",i.textContent=s.name,i.onclick=()=>{let a=(s.kind||"").includes("reject")||(s.kind||"").includes("deny");a?se(n.sessionId,e.requestId).catch(()=>{}):ne(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:a}),t.innerHTML=`<span style="opacity:0.5">\u26BF ${p(e.title)} \u2014 ${p(s.name)}</span>`,ae()},t.appendChild(i)}),E(t);break}case"permission_resolved":{n.pendingPermissionRequestIds.delete(e.requestId),n.unconfirmedPermissions.delete(e.requestId);let t=document.querySelector(`.permission[data-request-id="${e.requestId}"]`);if(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>`}ae();break}case"bash_command":{if(n.sentBashForSession===e.sessionId){n.sentBashForSession=null;break}e.sessionId===n.sessionId&&(Q(e.command,!0),I(!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,S()}break}case"bash_done":{if(e.sessionId!==n.sessionId)break;Y(n.currentBashEl,e.code,e.signal),e.error&&c(`err: ${e.error}`),I(!1);break}case"prompt_done":{if(J(),e.stopReason==="cancelled"&&n.newTurnStarted){n.newTurnStarted=!1,k(),P();break}n.newTurnStarted=!1,e.stopReason==="cancelled"?yt():wt(),n.turnEnded=!0,n.pendingPromptDone=!0,ae();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":y(),c("warn: Previous session expired, created new one."),M();break;case"config_set":{Oe(e.configId,e.value);let t=x(e.configId),s=t?.name||e.configId,i=t?.options.find(r=>r.value===e.value)?.name||e.value;c(`ok: ${s}: ${i}`),e.configId==="mode"&&_(),O();break}case"config_option_update":e.configOptions?.length&&me(e.configOptions);break;case"session_title_updated":e.sessionId===n.sessionId&&(n.sessionTitle=e.title,oe(n.sessionId,n.sessionTitle));break;case"error":n.awaitingNewSession=!1,n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,D(),k(),P(),c(`err: ${e.message}`),I(!1);break}}async function bt(){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(),r=(await e.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:Et(s)})).toJSON();await fetch("/api/beta/push/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({endpoint:r.endpoint,keys:r.keys,clientId:n.clientId})})}catch(e){console.error("[push] subscribe failed:",e)}}async function Tt(){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 Et(e){let t="=".repeat((4-e.length%4)%4),s=(e+t).replace(/-/g,"+").replace(/_/g,"/"),i=atob(s),r=new Uint8Array(i.length);for(let a=0;a<i.length;a++)r[a]=i.charCodeAt(a);return r}async function Ce(){try{let e=await navigator.serviceWorker?.ready;return e?await e.pushManager.getSubscription()!==null:!1}catch{return!1}}async function Me(e){let t=e.split(/\s+/),s=t[0].toLowerCase(),i=t.slice(1).join(" ").trim();switch(s){case"/new":return y(),c("Creating new session\u2026"),M({cwd:i||n.sessionCwd}),!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 qe(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 r=n.sessionId;try{let l=(await te()).find(d=>d.id!==r);if(n.busy&&G(),pe(r).catch(()=>{}),L=null,l){n.sessionSwitchGen++;let d=n.sessionSwitchGen;y(),n.sessionId=null;let[u]=await Promise.all([$(l.id),N(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}),S(!0)}else y(),n.sessionId=null,c("Creating new session\u2026"),M({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=>pe(d.id).catch(()=>{}))),L=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 a=await(await fetch("/api/v1/sessions")).json(),l=i.toLowerCase(),d=a.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;y(),n.sessionId=null;let[f]=await Promise.all([$(d.id),N(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}),S(!0)}catch{y(),n.sessionId=null,c("err: Failed to switch session")}return!0}case"/cancel":return n.busy?(G(),c("^C")):c("Nothing to cancel."),!0;case"/help":case"?":{let r=[];n.serverVersion&&r.push(`WebAgent ${n.serverVersion}`),n.agentName&&n.agentVersion&&r.push(`${n.agentName} ${n.agentVersion}`),r.length&&c(r.join(" \xB7 ")),c("? \u2014 Show help"),c("/help \u2014 Show help (alias)"),c("!<command> \u2014 Run bash command");for(let a of et){let l=a.args?`${a.cmd} ${a.args}`:a.cmd;c(`${l} \u2014 ${a.desc}`)}c("--- Shortcuts ---");for(let a of kt)c(`${a.key} \u2014 ${a.desc}`);c("--- Tips ---");for(let a of Ct)c(a.text);return!0}case"/model":case"/mode":case"/think":{let a={"/model":"model","/mode":"mode","/think":"reasoning_effort"}[s],l=x(a);if(!i){let v=l?.options.find(ee=>ee.value===l.currentValue)?.name||l?.currentValue||"unknown";return c(`${l?.name||a}: ${v}`),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=v=>v.toLowerCase().replace(/[\s_]+/g,"-"),f=u(d),g=l.options.find(v=>u(v.value)===f||u(v.name)===f);if(!g){let v=l.options.filter(ee=>u(ee.value).includes(f)||u(ee.name).includes(f));if(v.length===1)g=v[0];else if(v.length>1)return c(`err: Ambiguous "${i}". Type ${s} + space to see options.`),!0}return g?(l.currentValue=g.value,_(),O(),c(`${l.name} \u2192 ${g.name}`),await K(n.sessionId,a,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 r=i.toLowerCase();if(r==="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 Ce();return await bt(),c(l?"notify: already enabled":"notify: enabled"),!0}if(r==="off")return await Tt(),c("notify: disabled"),!0;let a=Notification.permission;return a==="denied"?c("notify: blocked \u2014 allow in browser site settings to enable"):a==="granted"&&await Ce()?c("notify: enabled"):c("notify: off \u2014 use /notify on to enable"),!0}default:return!1}}var et=[{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:"/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"}],Ct=[{text:"Tap \u276F prompt to cycle mode"}],b=-1,m=[],w="commands",Z=null,L=null,W=null,xe=!1;function Le(){let e=o.input.value;if(W!==null){if(e===W)return;W=null}let t=e.match(/^\/new /);if(t){let l=e.slice(t[0].length).toLowerCase();Mt(l);return}let s=e.match(/^\/switch /);if(s){let l=e.slice(s[0].length).toLowerCase();xt(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();Lt(d,u);return}let r=e.match(/^\/notify /);if(r){let l=e.slice(r[0].length).toLowerCase();Pt(l);return}if(!e.startsWith("/")||e.includes(" ")){h();return}w="commands";let a=e.toLowerCase();if(m=et.filter(l=>l.cmd.startsWith(a)),m.length===0){h();return}b=0,F(),o.slashMenu.classList.add("active")}async function xt(e,t="switch"){if(!L)try{L=await(await fetch("/api/v1/sessions")).json(),setTimeout(()=>{L=null},5e3)}catch{return}if(w=t,m=L.filter(i=>e?i.title&&i.title.toLowerCase().includes(e)||i.id.startsWith(e):!0),m.length===0){h();return}b=0,F(),o.slashMenu.classList.add("active")}async function Mt(e){if(!L)try{L=await(await fetch("/api/v1/sessions")).json(),setTimeout(()=>{L=null},5e3)}catch{return}w="new";let t=new Map;for(let i of L){let r=t.get(i.cwd);(!r||(i.last_active_at||i.created_at)>r.time)&&t.set(i.cwd,{cwd:i.cwd,time:i.last_active_at||i.created_at})}let s=[...t.values()].sort((i,r)=>r.time.localeCompare(i.time));if(e&&(s=s.filter(i=>i.cwd.toLowerCase().includes(e))),m=s,m.length===0){h();return}b=0,F(),o.slashMenu.classList.add("active")}function Lt(e,t){let s=x(e);if(!s){h();return}if(w="config",Z=e,m=s.options.filter(i=>t?i.value.toLowerCase().includes(t)||i.name.toLowerCase().includes(t):!0),m.length===0){h();return}b=0,F(),o.slashMenu.classList.add("active")}var $t=[{value:"on",name:"on",desc:"Enable background notifications"},{value:"off",name:"off",desc:"Disable background notifications"}];async function Pt(e){if(w="notify",m=$t.filter(i=>e?i.value.includes(e)||i.name.includes(e):!0),m.length===0){h();return}xe=await Ce();let t=xe?"on":"off",s=m.findIndex(i=>i.value===t);b=s>=0?s:0,F(),o.slashMenu.classList.add("active")}function F(){if(w==="new"){let t=(n.sessionCwd||"").toLowerCase();o.slashMenu.innerHTML=m.map((s,i)=>{let r=s.cwd.toLowerCase()===t;return`<div class="slash-item${i===b?" selected":""}" data-idx="${i}"><span class="slash-cmd"${r?' style="color:var(--green)"':""}>${p((r?"* ":" ")+s.cwd)}</span></div>`}).join("")}else if(w==="config"){let t=ie(Z)?.toLowerCase()||"";o.slashMenu.innerHTML=m.map((s,i)=>{let r=s.value.toLowerCase()===t;return`<div class="slash-item${i===b?" selected":""}" data-idx="${i}"><span class="slash-cmd"${r?' style="color:var(--green)"':""}>${p((r?"* ":" ")+s.name)}</span></div>`}).join("")}else if(w==="notify"){let t=xe?"on":"off";o.slashMenu.innerHTML=m.map((s,i)=>{let r=s.value===t;return`<div class="slash-item${i===b?" selected":""}" data-idx="${i}"><span class="slash-cmd"${r?' style="color:var(--green)"':""}>${p((r?"* ":" ")+s.name)}</span><span class="slash-desc">${p(s.desc)}</span></div>`}).join("")}else w==="switch"?o.slashMenu.innerHTML=m.map((t,s)=>{let i=t.id===n.sessionId,r=i?"* ":" ",a=t.title||t.id.slice(0,8)+"\u2026",l=We(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(r+a)}</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 h(){o.slashMenu.classList.remove("active"),b=-1,m=[],w="commands",W=o.input.value}function Ht(e){if(!(e<0||e>=m.length)){if(w==="commands"){let t=m[e];o.input.value=t.cmd+(t.args?" ":""),h(),o.input.focus(),["/new","/switch","/model","/mode","/think","/notify"].includes(t.cmd)&&(W=null,Le())}else if(w==="config"){let t=m[e],s={model:"/model",mode:"/mode",reasoning_effort:"/think"}[Z]||`/${Z}`;o.input.value=`${s} ${t.name}`,h(),o.input.focus()}else if(w==="notify"){let t=m[e];o.input.value=`/notify ${t.value}`,h(),o.input.focus()}else if(w==="new"){let t=m[e];o.input.value=`/new ${t.cwd}`,h(),o.input.focus()}else if(w==="switch"){let t=m[e];o.input.value=`/switch ${t.title||t.id}`,h(),o.input.focus()}}}async function _t(e){if(!(e<0||e>=m.length))if(w==="new"){let t=m[e];o.input.value="",h(),y(),c("Creating new session\u2026"),M({cwd:t.cwd})}else if(w==="config"){let t=m[e],s=Z,i=x(s);o.input.value="",h(),i&&(i.currentValue=t.value),_(),O(),c(`${i?.name||s} \u2192 ${t.name}`),await K(n.sessionId,s,t.value).catch(()=>{})}else if(w==="switch"){let t=m[e];o.input.value="",h(),y(),n.sessionId=null,c("Switching\u2026"),Promise.all([$(t.id),N(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&&S(!0)}).catch(()=>{y(),n.sessionId=null,c("err: Failed to switch session")})}else if(w==="notify"){let t=m[e];o.input.value=`/notify ${t.value}`,h(),Me(o.input.value),o.input.value=""}else{let t=m[e];o.input.value=t.cmd+(t.args?" ":""),h(),o.input.focus(),["/new","/switch","/model","/mode","/think","/notify"].includes(t.cmd)&&(W=null,Le())}}function tt(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"?(Ht(b),!0):!1:!1}o.slashMenu.addEventListener("mousedown",e=>{e.preventDefault();let t=e.target.closest(".slash-item");t&&_t(Number(t.dataset.idx))});o.input.addEventListener("input",()=>{Le(),o.inputArea.classList.toggle("bash-mode",o.input.value.startsWith("!"))});function nt(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 st(e){n.pendingImages.push(e),ce(),o.input.focus()}function ce(){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),ce()}),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/")&&st(await nt(e));o.fileInput.value=""};o.input.addEventListener("paste",async e=>{for(let t of e.clipboardData.items)t.type.startsWith("image/")&&(e.preventDefault(),st(await nt(t.getAsFile())))});var H=document.createElement("div");H.id="lightbox-overlay";var X=document.createElement("img");H.appendChild(X);document.body.appendChild(H);var de=1,Nt=.5,qt=5;function Bt(){return H.classList.contains("active")}function Ot(e){X.src=e,de=1,X.style.transform="",H.classList.add("active")}function it(){H.classList.remove("active"),X.src=""}document.getElementById("messages").addEventListener("click",e=>{let t=e.target;t.tagName==="IMG"&&t.classList.contains("user-image")&&Ot(t.src)});H.addEventListener("click",e=>{e.target===H&&it()});document.addEventListener("keydown",e=>{e.key==="Escape"&&Bt()&&it()});H.addEventListener("wheel",e=>{e.preventDefault();let t=e.deltaY>0?-.15:.15;de=Math.min(qt,Math.max(Nt,de+t)),X.style.transform=`scale(${de})`},{passive:!1});function $e(){return n.clientId!==null}n._onCancelTimeout=()=>c("warn: Agent not responding to cancel");function ot(){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",lt(),Me(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(!$e()){c("warn: Not connected, please retry");return}o.input.value="",o.input.style.height="auto",o.inputArea.classList.remove("bash-mode"),Q(i,!0),n.sentBashForSession=n.sessionId,Be(n.sessionId,i).catch(()=>{}),I(!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(!$e()){c("warn: Not connected, please retry");return}let t=R("user",e||"(image)");for(let i of n.pendingImages){let r=document.createElement("img");r.className="user-image",r.src=i.previewUrl,t.appendChild(r)}let s=n.pendingImages.slice();n.pendingImages.length=0,ce(),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(r=>r.json()).then(r=>({data:i.data,mimeType:i.mimeType,path:r.url})))).then(i=>{if(!$e()){t.remove(),c("warn: Not connected, please retry"),I(!1);return}fe(n.sessionId,e||"What is in this image?",i.map(r=>({data:r.data,mimeType:r.mimeType,path:r.path}))).catch(()=>{})}):fe(n.sessionId,e).catch(()=>{}),n.turnEnded=!1,n.sentMessageForSession=n.sessionId,I(!0),je()}function rt(){G()&&c("^C")}function at(){let e=o.input.value.trim();return e.startsWith("/")||e.startsWith("!")||e==="?"||e.startsWith("? ")}function lt(){n.busy&&(at()?(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&&!at()?rt():ot()};o.input.addEventListener("keydown",e=>{if(tt(e)){e.preventDefault();return}if(e.key==="Enter"&&!e.shiftKey){e.preventDefault(),h(),ot();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(),rt();return}e.key==="Escape"&&o.slashMenu.classList.contains("active")&&(e.preventDefault(),h(),o.input.focus())});function ct(){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,K(n.sessionId,"mode",s.value).catch(()=>{}),c(`Mode \u2192 ${s.name}`),_()}document.addEventListener("keydown",e=>{e.key==="m"&&(e.ctrlKey||e.metaKey)&&!e.shiftKey&&(e.preventDefault(),ct())});o.prompt.addEventListener("click",ct);o.input.addEventListener("input",lt);o.input.addEventListener("input",()=>{o.input.style.height="auto",o.input.style.height=Math.min(o.input.scrollHeight,200)+"px"});async function At(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 He(){B("connecting","connecting");let e=new EventSource("/api/v1/events/stream");n.eventSource=e,e.onmessage=async t=>{let s=JSON.parse(t.data);if(s.type==="connected"){n.clientId=s.clientId,V(s.clientId,!document.hidden,n.sessionId??void 0).catch(()=>{}),At(s.clientId);return}C(s)},e.onerror=()=>{e.close(),Dt(),setTimeout(He,3e3)},Rt()}async function Rt(){B("connecting","session loading");let e=n.sessionSwitchGen,t=De();if(t&&t===n.sessionId&&n.lastEventSeq>0){if(await Pe(t,!0,e),e!==n.sessionSwitchGen)return;Ze(),S(!1);return}if(t){if(y(),await Pe(t,!1,e),e!==n.sessionSwitchGen)return;S(!0);return}try{let s=await te();if(e!==n.sessionSwitchGen)return;if(s.length>0){if(y(),await Pe(s[0].id,!1,e),e!==n.sessionSwitchGen)return;S(!0);return}}catch{}e===n.sessionSwitchGen&&M()}async function Pe(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;y(),c("warn: Previous session expired, created new one."),M();return}if(s!==n.sessionSwitchGen)return;await Te(e)}else{n.sessionId=null;let i=N(e),r;try{let[a,l]=await Promise.all([$(e),i]);if(s!==n.sessionSwitchGen)return;r=a,l||c("warn: Failed to load history.")}catch{if(s!==n.sessionSwitchGen)return;y(),c("warn: Previous session expired, created new one."),M();return}C({type:"session_created",sessionId:r.id,cwd:r.cwd,title:r.title,configOptions:r.configOptions,busyKind:r.busyKind})}}function Dt(){B("disconnected","disconnected"),n.eventSource=null,n.clientId=null,k(),P(),n.currentBashEl&&Y(n.currentBashEl,null,"disconnected"),n.pendingToolCallIds.clear(),n.pendingPermissionRequestIds.clear(),n.pendingPromptDone=!1,n.turnEnded=!1,J(),I(!1)}document.addEventListener("visibilitychange",()=>{n.clientId&&V(n.clientId,!document.hidden,n.sessionId??void 0).catch(()=>{}),!document.hidden&&n.sessionId&&n.lastEventSeq>0&&!n.replayInProgress&&Te(n.sessionId).then(()=>S(!1))});He();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}`),y(),n.sessionId=null,c("Switching\u2026"),Promise.all([$(t),N(t)]).then(([i,r])=>{s===n.sessionSwitchGen&&(C({type:"session_created",sessionId:i.id,cwd:i.cwd,title:i.title,configOptions:i.configOptions,busyKind:i.busyKind}),r&&S(!0))}).catch(()=>{y(),n.sessionId=null,c("err: Failed to switch session")})}}));