aicodeman 1.31.0 → 1.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/README.md +7 -6
  2. package/dist/web/public/admin-ui.js.gz +0 -0
  3. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  4. package/dist/web/public/{app.8df358cd.js → app.a792bf71.js} +2 -2
  5. package/dist/web/public/app.a792bf71.js.br +0 -0
  6. package/dist/web/public/{app.8df358cd.js.gz → app.a792bf71.js.gz} +0 -0
  7. package/dist/web/public/approvals-ui.js.gz +0 -0
  8. package/dist/web/public/{constants.85ed12f3.js → constants.b26748e9.js} +49 -0
  9. package/dist/web/public/constants.b26748e9.js.br +0 -0
  10. package/dist/web/public/constants.b26748e9.js.gz +0 -0
  11. package/dist/web/public/cron-ui.js.gz +0 -0
  12. package/dist/web/public/entrance-animations.js.gz +0 -0
  13. package/dist/web/public/home-sessions.js.gz +0 -0
  14. package/dist/web/public/host-wake-ui.js.gz +0 -0
  15. package/dist/web/public/i18n.d22be5c4.js +1 -0
  16. package/dist/web/public/i18n.d22be5c4.js.br +0 -0
  17. package/dist/web/public/i18n.d22be5c4.js.gz +0 -0
  18. package/dist/web/public/{image-input.cd4b97c4.js → image-input.a216cf9e.js} +8 -4
  19. package/dist/web/public/image-input.a216cf9e.js.br +0 -0
  20. package/dist/web/public/image-input.a216cf9e.js.gz +0 -0
  21. package/dist/web/public/index.html +14 -11
  22. package/dist/web/public/index.html.br +0 -0
  23. package/dist/web/public/index.html.gz +0 -0
  24. package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
  25. package/dist/web/public/{keyboard-accessory.2cf04f17.js → keyboard-accessory.c1e7c7b7.js} +280 -15
  26. package/dist/web/public/keyboard-accessory.c1e7c7b7.js.br +0 -0
  27. package/dist/web/public/keyboard-accessory.c1e7c7b7.js.gz +0 -0
  28. package/dist/web/public/mobile-handlers.6f354a87.js.gz +0 -0
  29. package/dist/web/public/mobile-overview.js.gz +0 -0
  30. package/dist/web/public/mobile.6caaa28a.css.gz +0 -0
  31. package/dist/web/public/notification-manager.36ea4624.js.gz +0 -0
  32. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  33. package/dist/web/public/panels-ui.28df5053.js.gz +0 -0
  34. package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
  35. package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
  36. package/dist/web/public/readmymind-ui.js.gz +0 -0
  37. package/dist/web/public/reboot-restore-ui.js.gz +0 -0
  38. package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
  39. package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
  40. package/dist/web/public/session-lineage.js.gz +0 -0
  41. package/dist/web/public/session-ui.7df17fd3.js +88 -0
  42. package/dist/web/public/session-ui.7df17fd3.js.br +0 -0
  43. package/dist/web/public/session-ui.7df17fd3.js.gz +0 -0
  44. package/dist/web/public/settings-ui.df5ffb68.js +78 -0
  45. package/dist/web/public/settings-ui.df5ffb68.js.br +0 -0
  46. package/dist/web/public/settings-ui.df5ffb68.js.gz +0 -0
  47. package/dist/web/public/styles.74f312e5.css +1 -0
  48. package/dist/web/public/styles.74f312e5.css.br +0 -0
  49. package/dist/web/public/styles.74f312e5.css.gz +0 -0
  50. package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
  51. package/dist/web/public/sw.js.gz +0 -0
  52. package/dist/web/public/tab-rail-resize.42c24949.js.gz +0 -0
  53. package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js.gz +0 -0
  54. package/dist/web/public/terminal-split.js +804 -0
  55. package/dist/web/public/terminal-split.js.br +0 -0
  56. package/dist/web/public/terminal-split.js.gz +0 -0
  57. package/dist/web/public/terminal-ui.32fbeaf6.js +2 -0
  58. package/dist/web/public/terminal-ui.32fbeaf6.js.br +0 -0
  59. package/dist/web/public/terminal-ui.32fbeaf6.js.gz +0 -0
  60. package/dist/web/public/ultracode-panel.js.gz +0 -0
  61. package/dist/web/public/ultracode-windows.js.gz +0 -0
  62. package/dist/web/public/upload.html.gz +0 -0
  63. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  64. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  65. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  66. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  67. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  68. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  69. package/dist/web/public/vendor/xterm-predictive-echo.bd6882b8.js.gz +0 -0
  70. package/dist/web/public/vendor/xterm-zerolag-input.6fee72f2.js.gz +0 -0
  71. package/dist/web/public/vendor/xterm.css.gz +0 -0
  72. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  73. package/dist/web/public/voice-input.c4b51eb6.js.gz +0 -0
  74. package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
  75. package/dist/web/public/webview-tabs.js.gz +0 -0
  76. package/dist/web/server.d.ts.map +1 -1
  77. package/dist/web/server.js +13 -5
  78. package/dist/web/server.js.map +1 -1
  79. package/package.json +1 -1
  80. package/dist/web/public/app.8df358cd.js.br +0 -0
  81. package/dist/web/public/constants.85ed12f3.js.br +0 -0
  82. package/dist/web/public/constants.85ed12f3.js.gz +0 -0
  83. package/dist/web/public/i18n.83924614.js +0 -1
  84. package/dist/web/public/i18n.83924614.js.br +0 -0
  85. package/dist/web/public/i18n.83924614.js.gz +0 -0
  86. package/dist/web/public/image-input.cd4b97c4.js.br +0 -0
  87. package/dist/web/public/image-input.cd4b97c4.js.gz +0 -0
  88. package/dist/web/public/keyboard-accessory.2cf04f17.js.br +0 -0
  89. package/dist/web/public/keyboard-accessory.2cf04f17.js.gz +0 -0
  90. package/dist/web/public/session-ui.d4aea71a.js +0 -88
  91. package/dist/web/public/session-ui.d4aea71a.js.br +0 -0
  92. package/dist/web/public/session-ui.d4aea71a.js.gz +0 -0
  93. package/dist/web/public/settings-ui.4762f480.js +0 -78
  94. package/dist/web/public/settings-ui.4762f480.js.br +0 -0
  95. package/dist/web/public/settings-ui.4762f480.js.gz +0 -0
  96. package/dist/web/public/styles.e34fea04.css +0 -1
  97. package/dist/web/public/styles.e34fea04.css.br +0 -0
  98. package/dist/web/public/styles.e34fea04.css.gz +0 -0
  99. package/dist/web/public/terminal-ui.35812aa2.js +0 -2
  100. package/dist/web/public/terminal-ui.35812aa2.js.br +0 -0
  101. package/dist/web/public/terminal-ui.35812aa2.js.gz +0 -0
package/README.md CHANGED
@@ -61,12 +61,13 @@ The installer asks before every system change, and re-running the same line upda
61
61
  curl -fsSL https://getcodeman.com/install | bash
62
62
  ```
63
63
 
64
- This installs Node.js, tmux and a build toolchain if missing (node-pty ships no Linux prebuilds, so it compiles from source), clones Codeman to `~/.codeman/app`, and builds it. A few things worth knowing:
64
+ This installs Node.js, tmux and a build toolchain if missing (node-pty ships no Linux prebuilds, so it compiles from source), clones Codeman to `~/.codeman/app`, and builds it. It looks at what is already on the machine, asks at most three questions, then does all the work unattended and ends on the URL with a QR code for your phone. A few things worth knowing:
65
65
 
66
- - **It asks first.** Every system change (package installs, AI CLI download) is prompted, and a menu at the end lets you choose: run Codeman in this terminal, install it as a background service (systemd/launchd, auto-start on boot), or don't start yet. Nothing runs in the background unless you pick it.
67
- - **How it's reachable, your choice.** The installer offers three ways to reach the dashboard: **Tailscale** (loopback bind fronted by `tailscale serve`, so you get `https://<machine>.<tailnet>.ts.net` with a real certificate and your tailnet as the login, no password needed), **any device on your network** (`0.0.0.0`, with a strongly recommended password prompt), or **this machine only** (`127.0.0.1`, safest). Skipping the password on a network bind requires an explicit confirmation and ends with a loud warning. The highlighted default reflects what is already on the machine (Tailscale when it is already in use, your existing binding on a re-run), and a bare Enter never pulls in new software. A bare `codeman web` started by hand still defaults to loopback.
68
- - **Re-run to update.** The same one-liner updates a finished install in place: local changes in `~/.codeman/app` are stashed (never discarded), and a running service is restarted and verified. If a first install was interrupted, re-running resumes the full setup instead. `install.sh update` and `install.sh uninstall` also exist.
69
- - **CI / headless:** without a terminal attached, steps that would change your system abort with instructions instead of running silently. Set `CODEMAN_NONINTERACTIVE=1` to approve them for automation.
66
+ - **Three questions, all up front.** How the dashboard is reached, optionally what to call this machine on your tailnet, and whether to run Codeman as a background service (systemd/launchd, auto-start on boot; Enter says yes). Everything that needs you, including one consent for all missing packages, one sudo password, and the Tailscale login, happens before the build, so you can walk away while it compiles.
67
+ - **How it's reachable, your choice.** **Tailscale** (loopback bind fronted by `tailscale serve`, so you get `https://<machine>.<tailnet>.ts.net` with a real certificate and your tailnet as the login, no password needed), **any device on your network** (`0.0.0.0`, with a strongly recommended password prompt), or **this machine only** (`127.0.0.1`, safest). Skipping the password on a network bind requires an explicit confirmation and ends with a loud warning. The highlighted default reflects what is already on the machine (Tailscale when it is already connected, your existing binding on a re-run), and a bare Enter never pulls in new software. If another app already owns `:443` on your node, Codeman goes under `https://<machine>.<tailnet>.ts.net/codeman` or on a second port instead of replacing it. A bare `codeman web` started by hand still defaults to loopback.
68
+ - **The name is yours to choose.** By default the URL uses the machine's existing tailnet name. Answering yes to the second question renames the machine to `codeman-<hostname>` (which also renames it for SSH, so the default is no); `install.sh name` does it later.
69
+ - **Re-run to update.** The same one-liner updates a finished install in place: local changes in `~/.codeman/app` are stashed (never discarded), and a running service is restarted and verified. If a first install was interrupted, re-running resumes the full setup instead. `install.sh status` prints the URLs and the QR code again; `install.sh update`, `install.sh tailscale` and `install.sh uninstall` also exist.
70
+ - **Flags for the impatient.** `curl -fsSL https://getcodeman.com/install | bash -s -- --tailscale --service` answers the questions from the command line (`--lan`, `--local`, `--run`, `--no-start`, `--name <n>`, `--port <n>`, `--yes` too). **CI / headless:** without a terminal attached, steps that would change your system abort with instructions instead of running silently; set `CODEMAN_NONINTERACTIVE=1` to approve them for automation.
70
71
 
71
72
  You'll need at least one AI coding CLI installed — [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [OpenCode](https://opencode.ai), [Codex](https://developers.openai.com/codex/cli), [Antigravity](https://antigravity.google), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Pi](https://pi.dev), [Grok Build](https://github.com/xai-org/grok-build), [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness), or [OMP](https://github.com/can1357/oh-my-pi) (any combination works; Gemini CLI is enterprise-only since Google's consumer cutover, and Antigravity is its successor). The installer detects whichever of the nine is present; if none is found, it offers to install any of them from a menu (DeepSeek excepted, since its npm package installs only a launcher with no runnable profile), or you can skip and install one yourself later. After install:
72
73
 
@@ -219,7 +220,7 @@ codeman web --https
219
220
  # Open on your phone: https://<your-ip>:3000
220
221
  ```
221
222
 
222
- > `localhost` works over plain HTTP. Use `--https` when accessing from another device, or use [Tailscale](https://tailscale.com/) (recommended): the installer can set it up for you (choose **Tailscale** at the network-access prompt, or run `bash ~/.codeman/app/install.sh tailscale` on an existing install). That gives you `https://<your-machine>.<tailnet>.ts.net` with a real certificate: private to your tailnet, no password required, and PWA install + push notifications work on your phone.
223
+ > `localhost` works over plain HTTP. Use `--https` when accessing from another device, or use [Tailscale](https://tailscale.com/) (recommended): the installer can set it up for you (choose **Tailscale** at the network-access prompt, or run `bash ~/.codeman/app/install.sh tailscale` on an existing install). That gives you `https://<your-machine>.<tailnet>.ts.net` with a real certificate: private to your tailnet, no password required, and PWA install + push notifications work on your phone. The installer ends on that URL with a QR code to scan, and `bash ~/.codeman/app/install.sh status` prints it again any time.
223
224
 
224
225
  ### Secure QR Code Authentication
225
226
 
Binary file
@@ -15,7 +15,7 @@
15
15
  ${p.join(`
16
16
  `)}`:""};t.title=[h("Claude",e,!0),h("Codex",e.codex,!1)].filter(Boolean).join(`
17
17
 
18
- `)||"Plan usage limits"}_onScheduledCreated(e){this.currentRun=e,this.showTimer()}_onScheduledUpdated(e){this.currentRun=e,this.updateTimer()}_onScheduledCompleted(e){this.currentRun=e,this.hideTimer(),this.showToast("Scheduled run completed!","success")}_onScheduledStopped(){this.currentRun=null,this.hideTimer()}setConnectionStatus(e){this._connectionStatus=e,e==="connected"?(this._connDownSince=null,this._nextSseRetryAt=null,this._offlineOverlayDismissed=!1):this._connDownSince===null&&(this._connDownSince=Date.now()),this._updateConnectionIndicator(),this._updateConnectionLossUi(),e==="connected"&&this._redeliverSweep()}_connectWs(e){this._disconnectWs(),this._wsState="connecting",this._updateConnectionIndicator();const t=location.protocol==="https:"?"wss:":"ws:",s=this._clientId?`${this._clientId}:${this._wsTabNonce}`:"",i=s?`?cid=${encodeURIComponent(s)}`:"",n=`${t}//${location.host}${CodemanBase.base}/ws/sessions/${e}/terminal${i}`,a=new WebSocket(n);this._ws=a,this._wsSessionId=e,a.onopen=()=>{this._ws===a&&(this._wsReady=!0,this._wsState="connected",this._wsReconnectAttempts=0,this._updateConnectionIndicator(),this.sendResize(e)?.catch?.(()=>{}),this._startMobileResizeRetry(e),this._onWsReady(e))},a.onmessage=o=>{if(this._ws===a){this._wsLastRecvAt=Date.now();try{const r=JSON.parse(o.data);r.t==="o"?this._onSessionTerminal({id:e,data:r.d}):r.t==="c"?this._onSessionClearTerminal({id:e}):r.t==="r"?this._onSessionNeedsRefresh({id:e}):r.t==="ia"&&this._onWsInputAck(r.seq,r)}catch{}}},a.onclose=o=>{if(this._ws!==a)return;this._ws=null,this._wsSessionId=null,this._wsReady=!1,this._stopMobileResizeRetry();const r=window.CodemanWsReconnect.plan(o.code,this._wsReconnectAttempts||0);_crashDiag.log(`WS CLOSE code=${o.code} reason=${o.reason||""} action=${r.action} attempts=${this._wsReconnectAttempts||0}`);const l=this.activeSessionId===e;if(r.action==="give-up")this._wsState=l?"fallback":"disconnected",this._updateConnectionIndicator();else if(r.action==="reconnect")if(l){this._wsState="reconnecting",this._updateConnectionIndicator();const c=r.delayMs+Math.floor(Math.random()*250);this._wsReconnectAttempts=(this._wsReconnectAttempts||0)+1,this._wsReconnectTimer=setTimeout(()=>{this._wsReconnectTimer=null,this.activeSessionId===e&&this._connectWs(e)},c)}else this._wsState="disconnected",this._updateConnectionIndicator();else this._wsState=l?"fallback":"disconnected",this._updateConnectionIndicator(),l&&(this._wsReconnectAttempts=(this._wsReconnectAttempts||0)+1,this._wsReconnectTimer=setTimeout(()=>{this._wsReconnectTimer=null,this.activeSessionId===e&&this._connectWs(e)},r.delayMs))},a.onerror=()=>{}}_disconnectWs(){this._clearTimer("_wsReconnectTimer"),this._wsState="disconnected",this._stopMobileResizeRetry(),this._ws&&(this._ws.onclose=null,this._ws.close(),this._ws=null,this._wsSessionId=null,this._wsReady=!1)}_startMobileResizeRetry(e){this._stopMobileResizeRetry(),(typeof MobileDetection<"u"&&MobileDetection.getDeviceType?MobileDetection.getDeviceType():"desktop")!=="desktop"&&(this._mobileResizeRetryTimer=setInterval(()=>{document.visibilityState==="visible"&&(!this._wsReady||this._wsSessionId!==e||typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible||this.sendResize(e)?.catch?.(()=>{}))},MOBILE_RESIZE_RETRY_MS))}_stopMobileResizeRetry(){this._mobileResizeRetryTimer&&(clearInterval(this._mobileResizeRetryTimer),this._mobileResizeRetryTimer=null)}_sendInputAsync(e,t,s){!e||!t||this._reliableSend(e,t,s?.useMux===!0)}_sendInputEphemeral(e,t){if(!(!e||!t)){if(this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e)try{this._ws.send(JSON.stringify({t:"i",d:t}));return}catch{}try{fetch(`/api/sessions/${e}/input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({input:t}),keepalive:!0}).catch(()=>{})}catch{}}}_reliableSend(e,t,s){const n={seq:this._nextSeq(e),data:t,useMux:!!s,ts:Date.now(),tries:0,sentAt:0};let a=this._pendingDeliveries.get(e);a||(a=[],this._pendingDeliveries.set(e,a)),a.push(n),this._persistReliableNow(),this._updateConnectionIndicator(),this._drainSession(e)}_nextSeq(e){const t=(this._seqCounters.get(e)||0)+1;return this._seqCounters.set(e,t),t}_drainSession(e){const t=this._pendingDeliveries.get(e);if(!(!t||t.length===0)){if(this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e){for(const s of t)if(s.sentAt===0)try{this._ws.send(JSON.stringify({t:"i",d:s.data,seq:s.seq,cid:this._clientId})),s.sentAt=Date.now(),s.tries++}catch{break}return}this._postDraining.has(e)||(this._postDraining.add(e),(async()=>{try{for(;;){const s=this._pendingDeliveries.get(e);if(!s||s.length===0||this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e)break;const i=s[0];i.tries++,i.sentAt=Date.now();let n=null;try{const a={input:i.data,seq:i.seq,clientId:this._clientId};i.useMux&&(a.useMux=!0),n=await fetch(`/api/sessions/${e}/input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a),keepalive:i.data.length<65536})}catch{n=null}if(n&&n.ok)this._ackDelivery(e,i.seq);else if(n&&(n.status===404||n.status===410))this._ackDelivery(e,i.seq);else break}}finally{this._postDraining.delete(e)}})())}}_ackDelivery(e,t){const s=this._pendingDeliveries.get(e);if(s){const i=s.findIndex(n=>n.seq===t);i!==-1&&(s.splice(i,1),s.length===0&&this._pendingDeliveries.delete(e),this._pendingDeliveries.size===0?this._persistReliableNow():this._persistReliableState(),this._updateConnectionIndicator())}this.markIdleAlertSeen?.(e)}_onWsInputAck(e,t){const s=this._wsSessionId;if(!(!s||!Number.isInteger(e))){if(t&&t.dup){const i=this._pendingDeliveries.get(s),n=i&&i.find(r=>r.seq===e),a=Number.isInteger(t.last)?t.last:e;(this._seqCounters.get(s)||0)<=a&&(this._seqCounters.set(s,a),this._persistReliableNow());const o=n&&n.tries<=1?n.data:null;this._ackDelivery(s,e),o!==null&&this._reliableSend(s,o,n.useMux);return}this._ackDelivery(s,e)}}_onWsReady(e){const t=this._pendingDeliveries.get(e);if(t)for(const s of t)s.sentAt=0;this._drainSession(e)}_redeliverSweep(){if(this._pendingDeliveries.size!==0)for(const e of[...this._pendingDeliveries.keys()]){const t=this._pendingDeliveries.get(e);if(!t||t.length===0)continue;if(this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e){const i=t[0],n=i&&i.sentAt&&Date.now()-i.sentAt>this._reliableAckTimeoutMs,a=Date.now()-this._wsLastRecvAt>this._reliableAckTimeoutMs;if(n&&a){try{this._ws.close()}catch{}continue}if(n)for(const o of t)o.sentAt&&Date.now()-o.sentAt>this._reliableAckTimeoutMs&&(o.sentAt=0)}this._drainSession(e)}}_pendingBytes(){let e=0,t=0;for(const s of this._pendingDeliveries.values())for(const i of s)e+=i.data.length,t++;return{bytes:e,count:t}}_loadReliableState(){try{this._clientId=localStorage.getItem("codeman:clientId")||""}catch{this._clientId=""}if(!this._clientId){this._clientId="c-"+Math.random().toString(36).slice(2)+"-"+Date.now().toString(36);try{localStorage.setItem("codeman:clientId",this._clientId)}catch{}}try{const e=localStorage.getItem("codeman:pendingInput");if(!e)return;const t=JSON.parse(e);if(t&&t.seqs)for(const[s,i]of Object.entries(t.seqs))Number.isFinite(i)&&this._seqCounters.set(s,i);if(t&&t.pending)for(const[s,i]of Object.entries(t.pending))Array.isArray(i)&&i.length&&this._pendingDeliveries.set(s,i.filter(n=>n&&typeof n.data=="string"&&Number.isInteger(n.seq)).map(n=>({seq:n.seq,data:n.data,useMux:!!n.useMux,ts:n.ts||Date.now(),tries:0,sentAt:0})))}catch{}}_persistReliableState(){this._persistReliableTimer||(this._persistReliableTimer=setTimeout(()=>{this._persistReliableTimer=null,this._persistReliableNow()},250))}_persistReliableNow(){this._persistReliableTimer&&(clearTimeout(this._persistReliableTimer),this._persistReliableTimer=null);try{const e={};for(const[n,a]of this._seqCounters)e[n]=a;const t={};let s=0;for(const[n,a]of this._pendingDeliveries)if(a.length){t[n]=a.map(o=>({seq:o.seq,data:o.data,useMux:o.useMux,ts:o.ts,tries:o.tries}));for(const o of a)s+=o.data.length}const i=s>this._reliableMaxBytes?{seqs:e}:{seqs:e,pending:t};localStorage.setItem("codeman:pendingInput",JSON.stringify(i))}catch{}}_computeConnectionDescriptor(){const{bytes:e,count:t}=this._pendingBytes(),s=t>0,n=e>4,a=r=>r<1024?`${r}B`:`${(r/1024).toFixed(1)}KB`,o=n?` \xB7 ${a(e)} queued`:"";if(!this.isOnline||this._connectionStatus==="offline")return{display:"flex",dotClass:"connection-dot offline",text:n?`Offline (${a(e)} queued)`:"Offline",title:"No network connection"};if(this.activeSessionId){let r,l,c;switch(this._wsState){case"connected":r="connected",l="WS",c="Terminal connected over WebSocket";break;case"fallback":r="fallback",l="HTTP",c="WebSocket unavailable \u2014 input sent over HTTP";break;case"reconnecting":r="reconnecting",l="WS\u2026",c="Reconnecting WebSocket";break;default:r="reconnecting",l="WS\u2026",c="Connecting WebSocket";break}return{display:"flex",dotClass:`connection-dot ${r}`,text:`${l}${o}`,title:c}}return this._connectionStatus==="reconnecting"||this._connectionStatus==="disconnected"?{display:"flex",dotClass:"connection-dot reconnecting",text:n?`Reconnecting (${a(e)} queued)`:"Reconnecting...",title:"Reconnecting to server"}:s?{display:"flex",dotClass:"connection-dot draining",text:n?`Sending ${a(e)}...`:"Sending...",title:"Delivering queued input"}:{display:"none",dotClass:"",text:"",title:""}}_updateConnectionIndicator(){const e=this.$("connectionIndicator"),t=this.$("connectionDot"),s=this.$("connectionText");if(!e||!t||!s)return;const i=this._computeConnectionDescriptor(),n=this._lastIndicatorDescriptor;n&&n.display===i.display&&n.dotClass===i.dotClass&&n.text===i.text&&n.title===i.title||(this._lastIndicatorDescriptor=i,e.style.display=i.display,i.display!=="none"&&(t.className=i.dotClass,s.textContent=i.text,e.title=i.title))}setupOnlineDetection(){window.addEventListener("online",()=>{this.isOnline=!0,this.reconnectAttempts=0,this._connDownSince=Date.now(),this.connectSSE(),this._redeliverSweep()}),window.addEventListener("offline",()=>{this.isOnline=!1,this.setConnectionStatus("offline")})}_updateConnectionLossUi(){const e=window.CodemanConnectionLoss,t=this.$("offlineBanner"),s=this.$("offlineOverlay");if(!e||!t||!s)return;const i=e.compute({isOnline:this.isOnline,status:this._connectionStatus,everLoaded:this._initGeneration>0,downSince:this._connDownSince,now:Date.now(),nextRetryAt:this._nextSseRetryAt,overlayDismissed:this._offlineOverlayDismissed,retryPending:this._offlineRetryPending});this._connDownSince===null?this._stopOfflineTicker():this._startOfflineTicker();const n=this._offlineRetryPending?"Reconnecting\u2026":i.retryInSec!=null&&i.retryInSec>0?`Retrying in ${i.retryInSec}s`:"Retrying\u2026",a=`${i.mode}|${i.kind}|${n}`;if(a!==this._lastOfflineUiKey){if(this._lastOfflineUiKey=a,t.hidden=i.mode!=="banner",s.hidden=i.mode!=="overlay",document.body.classList.toggle("connection-lost",i.mode!=="hidden"),i.mode==="banner"){const o=this.$("offlineBannerText"),r=this.$("offlineBannerDetail");o&&(o.textContent=i.title),r&&(r.textContent=n)}else if(i.mode==="overlay"){const o=this.$("offlineOverlayTitle"),r=this.$("offlineOverlayBody"),l=this.$("offlineOverlayHost"),c=this.$("offlineOverlayStatus");o&&(o.textContent=i.title),r&&(r.textContent=i.detail),l&&(l.textContent=location.host),c&&(c.textContent=n)}}}_startOfflineTicker(){this._offlineUiTicker||(this._offlineUiTicker=setInterval(()=>this._updateConnectionLossUi(),1e3))}_stopOfflineTicker(){this._offlineUiTicker&&(clearInterval(this._offlineUiTicker),this._offlineUiTicker=null)}retryConnection(){this._offlineRetryPending=!0,this._nextSseRetryAt=null,this.reconnectAttempts=0,this._clearTimer("sseReconnectTimeout"),this.isOnline=navigator.onLine,this._lastOfflineUiKey="",this._updateConnectionLossUi(),this.connectSSE(),this.activeSessionId&&this._wsState!=="connected"&&(this._wsReconnectAttempts=0,this._connectWs(this.activeSessionId)),this._clearTimer("_offlineRetryTimer"),this._offlineRetryTimer=setTimeout(()=>{this._offlineRetryPending=!1,this._lastOfflineUiKey="",this._updateConnectionLossUi()},1500)}dismissOfflineOverlay(){this._offlineOverlayDismissed=!0,this._lastOfflineUiKey="",this._updateConnectionLossUi()}_updateCjkInputState(){const e=document.getElementById("cjkInput");if(!e)return;const t=this.loadAppSettingsFromStorage(),s=this.getDefaultSettings?.()||{},n=(this._serverCjkOverride||(t.cjkInputEnabled??s.cjkInputEnabled??!1))&&!!this.activeSessionId;e.classList.toggle("cjk-input-visible",!!n),document.body.classList.toggle("cjk-input-visible",!!n),e.style.display=n?"block":"none",e.setAttribute("aria-hidden",n?"false":"true"),n||(window.cjkActive=!1),typeof KeyboardHandler<"u"&&KeyboardHandler.updateLayoutForKeyboard()}_resetAllAppState(e=!1){this.sessions.clear(),this.ralphStates.clear(),e||(this.terminalBuffers.clear(),this.terminalBufferCache.clear(),this._xtermSnapshots?.clear()),this.projectInsights.clear(),this.teams.clear(),this.teamTasks.clear();for(const t of this.idleTimers.values())clearTimeout(t);if(this.idleTimers.clear(),this._clearTimer("flickerFilterTimeout"),this.flickerFilterBuffer="",this.flickerFilterActive=!1,this._clearTimer("syncWaitTimeout"),this._clearTimer("_clientDropRecoveryTimer"),this.pendingWrites=[],this.writeFrameScheduled=!1,this._terminalWriteInFlight=!1,this._terminalWriteInFlightBytes=0,this._isLoadingBuffer=!1,this._loadBufferQueue=null,this._bufferLoadOwner=null,this._terminalRefreshOwner=null,this._chunkedWriteGen=(this._chunkedWriteGen||0)+1,this._localEchoOverlay?.rerender(),this._predictiveEcho?.clearPredictions(),this.pendingHooks.clear(),this.approvals?.clear(),this._parentNameCache&&this._parentNameCache.clear(),this.subagentActivity.clear(),this.subagentToolResults.clear(),this.workflowRuns&&this.workflowRuns.clear(),this.workflowRunDetails&&this.workflowRunDetails.clear(),this.activeWorkflowRunId=null,this.activeWorkflowPhaseIndex=null,MobileDetection.cleanup(),KeyboardHandler.cleanup(),MobileDetection.init(),KeyboardHandler.init(),this.tabAlerts.clear(),this.attachmentHistoryCounts.clear(),this._shownCompletions&&this._shownCompletions.clear(),this.notificationManager?.titleFlashInterval&&(clearInterval(this.notificationManager.titleFlashInterval),this.notificationManager.titleFlashInterval=null),this.notificationManager?.groupingMap){for(const{timeout:t}of this.notificationManager.groupingMap.values())clearTimeout(t);this.notificationManager.groupingMap.clear()}this.terminalResizeObserver&&(this.terminalResizeObserver.disconnect(),this.terminalResizeObserver=null),this.planLoadingTimer&&(clearInterval(this.planLoadingTimer),this.planLoadingTimer=null),this.timerCountdownInterval&&(clearInterval(this.timerCountdownInterval),this.timerCountdownInterval=null),this.runSummaryAutoRefreshTimer&&(clearInterval(this.runSummaryAutoRefreshTimer),this.runSummaryAutoRefreshTimer=null)}handleInit(e){this._clearTimer("_initFallbackTimer");const t=++this._initGeneration;if(this._serverCjkOverride=e.inputCjkForm||!1,this._updateCjkInputState(),e.planUsage&&this.updatePlanUsageChip(e.planUsage),this.refreshRebootRestoreBanner?.(),e.version){const o=this.$("versionDisplay"),r=this.$("headerVersion");o&&(o.textContent=`v${e.version}`,o.title=`Codeman v${e.version}`),r&&(r.textContent=`v${e.version}`,r.title=`Codeman v${e.version}`)}VoiceInput.cleanup();const s=this.activeSessionId,i=t>1&&!!s&&Array.isArray(e.sessions)&&e.sessions.some(o=>o.id===s);this._resetAllAppState(i),e.sessions.forEach(o=>{this.sessions.set(o.id,o),(o.ralphLoop||o.ralphTodos)&&!this.ralphClosedSessions.has(o.id)&&this.ralphStates.set(o.id,{loop:o.ralphLoop||null,todos:o.ralphTodos||[]})});try{localStorage.removeItem("codeman-tab-meta")}catch{}if(Array.isArray(e.sessionOrder)&&e.sessionOrder.length)try{localStorage.setItem("codeman-session-order",JSON.stringify(e.sessionOrder))}catch{}this.syncSessionOrder(),e.respawnStatus?this.respawnStatus=e.respawnStatus:this.respawnStatus={},this.respawnTimers={},this.respawnCountdownTimers={},this.respawnActionLogs={},e.globalStats&&(this.globalStats=e.globalStats),this.totalCost=e.sessions.reduce((o,r)=>o+(r.totalCost||0),0),this.totalCost+=e.scheduledRuns.reduce((o,r)=>o+(r.totalCost||0),0);const n=e.scheduledRuns.find(o=>o.status==="running");if(n&&(this.currentRun=n,this.showTimer()),this.updateCost(),this.renderSessionTabs(),this.seedApprovals?.(),this.sessions.size>0?this.startSystemStatsPolling():this.stopSystemStatsPolling(),this.cleanupAllFloatingWindows(),e.subagents&&(this.subagents.clear(),this.subagentActivity.clear(),this.subagentToolResults.clear(),e.subagents.forEach(o=>{this.subagents.set(o.agentId,o)}),this.renderSubagentPanel(),this.subagentParentMap.clear(),this.loadSubagentParentMap().then(()=>{for(const[o,r]of this.subagentParentMap){const l=this.subagents.get(o);if(l&&this.sessions.has(r)){l.parentSessionId=r;const c=this.sessions.get(r);c&&(l.parentSessionName=this.getSessionName(c)),this.subagents.set(o,l)}}for(const[o]of this.subagents)this.subagentParentMap.has(o)||this.findParentSessionForSubagent(o);this.restoreSubagentWindowStates()})),e.workflowRuns&&this.seedWorkflowRuns(e.workflowRuns),t!==this._initGeneration)return;if(this.isSoloWindow){this._applySoloMode();return}const a=this.activeSessionId;if(this.sessionOrder.length===0)this.activeSessionId=null;else{let o=a;if(!o||!this.sessions.has(o))try{o=localStorage.getItem("codeman-active-session")}catch{}i&&o===a&&this.sessions.has(o)?(this._wsSessionId!==o&&this._connectWs(o),this._onSessionNeedsRefresh({id:o})):(this.activeSessionId=null,o&&this.sessions.has(o)?this.selectSession(o,{auto:!0}):this.selectSession(this.sessionOrder[0],{auto:!0}))}}async loadState(){try{const t=await(await fetch("/api/status")).json();this.handleInit(t?.data??{})}catch(e){console.error("Failed to load state:",e)}}_debouncedCall(e,t,s=100){this._debounceTimers[e]&&clearTimeout(this._debounceTimers[e]),this._debounceTimers[e]=setTimeout(()=>{this._debounceTimers[e]=null,t.call(this)},s)}getSessionListLayout(){if(this.soloSessionId)return"header";const e=this.loadAppSettingsFromStorage(),t=this.getDefaultSettings(),s=e.sessionListLayout??t.sessionListLayout??"header";return s==="sidebar"||s==="sidebar-rich"?s:"header"}resolveSessionSidebarFontSize(e){const t=Number(e);return Number.isInteger(t)&&t>=11&&t<=18?t:12}applySessionSidebarFontSize(e=null){const t=e??this.loadAppSettingsFromStorage(),s=this.resolveSessionSidebarFontSize(t?.sessionSidebarFontSize);return document.documentElement.style.setProperty("--session-sidebar-name-font-size",`${s}px`),s}isSessionSidebarActive(){return document.documentElement.dataset.sessionList==="sidebar"}_tabOrientation(){return document.documentElement.getAttribute("data-tab-orientation")==="vertical"?"vertical":"horizontal"}_isVerticalTabList(){return this.isSessionSidebarActive()||this._tabOrientation()==="vertical"}shouldInlineSessionActions(){return this.isSessionSidebarActive()?!this.isSessionSidebarCollapsed():this._tabOrientation()==="vertical"&&!document.documentElement.classList.contains("tab-rail-compact")}isSessionSidebarRich(){const e=document.documentElement;return e.dataset.sessionList==="sidebar"&&e.dataset.sidebarDetail==="rich"}isTabRailRich(){const e=document.documentElement;return e.getAttribute("data-tab-orientation")==="vertical"&&e.dataset.tabRailDetail==="rich"&&!e.classList.contains("tab-rail-compact")}isRichTabRows(){return this.isSessionSidebarRich()||this.isTabRailRich()}isTabRailSorted(){const e=document.documentElement;return e.getAttribute("data-tab-orientation")==="vertical"&&e.dataset.tabRailSort==="activity"}_tabRailSortOrder(e){if(!this.isTabRailSorted()||!window.CodemanSessionOrder||typeof this._mobileOverviewState!="function")return null;const t=[];for(let n=0;n<e.length;n++){const a=this.sessions.get(e[n]);a&&t.push({id:e[n],state:this._mobileOverviewState(a,this.pendingHooks?.get(e[n])),lastActivityAt:Number(a.lastActivityAt)||0,lastSubmitAt:Number(a.lastSubmitAt)||0,orderIndex:n})}const s=window.CodemanSessionOrder.sort(t),i=new Map;for(let n=0;n<s.length;n++)i.set(s[n].id,n);return i}_isSessionSidebarOverlay(){return window.innerWidth<1024}isSessionSidebarCollapsed(){if(this._sidebarCollapsedOverride!==void 0)return this._sidebarCollapsedOverride;let e=null;try{e=localStorage.getItem("codeman-sidebar-collapsed")}catch{}return e===null?this._isSessionSidebarOverlay():e==="1"}shouldToggleSessionSidebarFromShortcut(e){if(!e||!e.ctrlKey&&!e.metaKey&&!e.altKey||!this.isSessionSidebarActive()||typeof this.getShortcutRegistry!="function"||typeof this.matchesShortcutEvent!="function")return!1;const t=this.getShortcutRegistry().find(s=>s.id==="toggle-session-sidebar");return!t||t.disabled?!1:this.matchesShortcutEvent(e,t)}applySessionListLayout(){const e=this.getSessionListLayout();this.applySessionSidebarFontSize();const t=e==="sidebar"||e==="sidebar-rich",s=this.isSessionSidebarCollapsed(),i=document.documentElement.dataset.sessionList,n=document.documentElement.dataset.sidebarDetail,a=document.documentElement.dataset.sidebar,o=document.getElementById("sessionTabs"),r=document.getElementById("sessionTabsHost"),l=document.getElementById("sessionSidebarList");if(!o||!r||!l)return;const c=document.getElementById("tabRail"),h=!t&&document.documentElement.getAttribute("data-tab-orientation")==="vertical",u=t?l:h&&c?c:r;o.parentElement!==u&&u.appendChild(o),document.documentElement.dataset.sessionList=t?"sidebar":"header",document.documentElement.dataset.sidebarDetail=e==="sidebar-rich"?"rich":"simple",document.documentElement.dataset.sidebar=s?"collapsed":"expanded",o.setAttribute("aria-orientation",u===r?"horizontal":"vertical");const d=document.getElementById("sidebarToggleBtn");if(d){d.classList.toggle("btn-sidebar-toggle--hidden",!t);const w=s?"Expand session sidebar":"Collapse session sidebar";d.setAttribute("aria-expanded",s?"false":"true"),d.setAttribute("aria-label",w),d.setAttribute("title",w)}const S=document.getElementById("sessionSidebar");if(S){S.classList.toggle("open",t&&!s);const w=t&&s&&this._isSessionSidebarOverlay();S.toggleAttribute("inert",w),w?S.setAttribute("aria-hidden","true"):S.removeAttribute("aria-hidden")}if(!t){this._sidebarFilter="";const w=document.getElementById("sessionSidebarFilter");w&&(w.value="")}const p=this._tallTabsEnabled;this.applyTabWrapSettings();const b=i!==document.documentElement.dataset.sessionList||n!==document.documentElement.dataset.sidebarDetail,g=a!==document.documentElement.dataset.sidebar;(b||g)&&p===this._tallTabsEnabled&&this._fullRenderSessionTabs(),t&&o.classList.remove("tabs-auto-wrap"),this.applySidebarFilter(this._sidebarFilter),this.updateConnectionLines(),document.getElementById("welcomeOverlay")?.classList.contains("visible")&&this.showHomeSessions?.(),this.isRichTabRows()?this._startSidebarRichClock():this._stopSidebarRichClock()}toggleSessionSidebar(){if(!this.isSessionSidebarActive())return;const e=!this.isSessionSidebarCollapsed();this._sidebarCollapsedOverride=e;try{localStorage.setItem("codeman-sidebar-collapsed",e?"1":"0")}catch{}e&&this.$("sessionSidebar")?.contains(document.activeElement)&&document.getElementById("sidebarToggleBtn")?.focus(),this.applySessionListLayout(),!e&&this._isSessionSidebarOverlay()&&this.$("sessionTabs")?.querySelector(".session-tab.active")?.focus()}closeSessionSidebarOnHandheld(){this._isSessionSidebarOverlay()&&(!this.isSessionSidebarActive()||this.isSessionSidebarCollapsed()||this.toggleSessionSidebar())}updateSidebarCount(){const e=document.getElementById("sessionSidebarCount");if(!e)return;const t=this.$("sessionTabs"),s=t?t.querySelectorAll(".session-tab:not(.tab-filtered-out)").length:this.sessions?.size??0;e.textContent=String(s)}applySidebarFilter(e){this._sidebarFilter=(e??"").trim().toLowerCase();const t=this.$("sessionTabs");if(!t)return;const i=this.isSessionSidebarActive()&&document.documentElement.dataset.sidebar!=="collapsed"?this._sidebarFilter:"";for(const n of t.querySelectorAll(".session-tab")){if(!i){n.classList.remove("tab-filtered-out");continue}const a=`${n.getAttribute("aria-label")||""} ${n.getAttribute("title")||""}`.toLowerCase();n.classList.toggle("tab-filtered-out",!a.includes(i))}this.updateSidebarCount()}_sidebarRichPillLabel(e){return{needs:"needs you",error:"error",waiting:"waiting",working:"working",idle:"idle",done:"done"}[e]||e}_sidebarRichRow(e,t){if(typeof this._mobileOverviewState!="function")return null;const s=this._mobileOverviewState(t,this.pendingHooks?.get(e));return{state:s,pill:this._sidebarRichPillLabel(s),createdAt:Number(t.createdAt)||0,since:this._mobileOverviewSince?this._mobileOverviewSince(s,t):null}}_sidebarRichMetaHTML(e){if(!e)return"";const t=(a,o,r,l)=>{const c=this._sidebarRichStampText(o,r),h=o?` title="${escapeHtml(`${a==="created"?"First created":a}: ${new Date(o).toLocaleString()}`)}"`:"";return`<span class="tab-meta-item ${l}"${h}><span class="tab-meta-key">${escapeHtml(a)}</span><span data-tab-ts="${o||0}" data-tab-fmt="${r}">${escapeHtml(c)}</span></span>`},s=[t("created",e.createdAt,"ago","tab-meta-created")];e.since&&(s.push('<span class="tab-meta-sep" aria-hidden="true">\xB7</span>'),s.push(t(e.since.key,e.since.at,"for","tab-meta-since"))),s.push(`<span class="tab-pill tab-pill--${escapeHtml(e.state)}">${escapeHtml(e.pill)}</span>`);const i=[e.createdAt?`First created: ${new Date(e.createdAt).toLocaleString()}`:"",e.since&&e.since.at?`${e.since.key}: ${new Date(e.since.at).toLocaleString()}`:""].filter(Boolean).join(" \xB7 ");return`<span class="tab-meta"${i?` title="${escapeHtml(i)}"`:""} data-i18n-skip>${s.join("")}</span>`}_sidebarRichStampText(e,t){return this._mobileOverviewStampText?this._mobileOverviewStampText(e,t):"\u2014"}_updateSidebarRichRow(e,t,s){const i=this._sidebarRichRow(t,s);if(!i)return;const n=e.dataset.tabState,a=`${i.state}:${i.since?i.since.at:0}:${i.createdAt}`;if(e.dataset.tabMetaSig===a)return;e.dataset.tabMetaSig=a,e.dataset.tabState=i.state,n&&e.classList.remove(`tab-state-${n}`),e.classList.add(`tab-state-${i.state}`);const o=e.querySelector(".tab-info");if(!o)return;const r=this._sidebarRichMetaHTML(i),l=o.querySelector(".tab-meta");l?l.outerHTML=r:o.insertAdjacentHTML("beforeend",r)}_startSidebarRichClock(){this._sidebarRichClock||(this._sidebarRichClock=setInterval(()=>{if(!this.isRichTabRows()){this._stopSidebarRichClock();return}this._tickSidebarRichTimes()},SIDEBAR_RICH_CLOCK_MS))}_stopSidebarRichClock(){this._sidebarRichClock&&(clearInterval(this._sidebarRichClock),this._sidebarRichClock=null)}_tickSidebarRichTimes(){const e=this.$("sessionTabs");if(e)for(const t of e.querySelectorAll("[data-tab-ts]")){const s=this._sidebarRichStampText(Number(t.dataset.tabTs)||0,t.dataset.tabFmt);t.textContent!==s&&(t.textContent=s)}}renderSessionTabs(){this._inlineRenameActive||this._debouncedCall("sessionTabs",this._renderSessionTabsImmediate)}_updateActiveTabImmediate(e){const t=this.$("sessionTabs");if(!t)return;const s=t.querySelectorAll(".session-tab[data-id]");for(const i of s)i.dataset.id===e?i.classList.add("active"):i.classList.remove("active");this._scrollActiveTabIntoView(e)}_scrollActiveTabIntoView(e,t="smooth"){const s=this.$("sessionTabs");if(!s)return;const i=e&&s.querySelector(`.session-tab[data-id="${e}"]`)||s.querySelector(".session-tab.active");if(!i)return;if(this._isVerticalTabList()){i.scrollIntoView({block:"nearest"});return}const n=window.CodemanTabOverflow?.computeTabScrollLeft;if(!n)return;const a=s.getBoundingClientRect(),o=i.getBoundingClientRect(),r=n({scrollLeft:s.scrollLeft,clientWidth:s.clientWidth,scrollWidth:s.scrollWidth,tabLeft:o.left-a.left+s.scrollLeft,tabWidth:o.width});if(Math.abs(r-s.scrollLeft)<1)return;const l=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;typeof s.scrollTo=="function"?s.scrollTo({left:r,behavior:l?"auto":t}):s.scrollLeft=r}_tabAnchor(e){return this._isVerticalTabList()?{x:e.right,y:e.top+e.height/2,spawnLeft:e.right+14,spawnTop:e.top,vertical:!1}:{x:e.left+e.width/2,y:e.bottom,spawnLeft:e.left,spawnTop:e.bottom,vertical:!0}}_tabConnectorPath(e,t){if(e.vertical){const a=t.left+t.width/2,o=t.top,r=(e.y+o)/2;return`M ${e.x} ${e.y} C ${e.x} ${r}, ${a} ${r}, ${a} ${o}`}const s=t.left,i=t.top+t.height/2,n=(e.x+s)/2;return`M ${e.x} ${e.y} C ${n} ${e.y}, ${n} ${i}, ${s} ${i}`}_setTerminalLoadState(e,t,s){this.terminalLoadStates.set(e,{generation:t,phase:s}),this._updateTerminalLoadTab(e)}_clearTerminalLoadState(e,t){const s=this.terminalLoadStates.get(e);s&&s.generation!==t||(this.terminalLoadStates.delete(e),this._updateTerminalLoadTab(e))}_updateTerminalLoadTab(e){const t=this.$("sessionTabs")?.querySelector(`.session-tab[data-id="${e}"]`);if(!t)return;const s=this.terminalLoadStates.get(e);if(t.classList.toggle("tab-loading",!!s),s){if(t.setAttribute("aria-busy","true"),t.dataset.loadPhase=s.phase,!t.querySelector(".tab-load-spinner")){const i=document.createElement("span");i.className="tab-load-spinner",i.setAttribute("aria-hidden","true");const n=t.querySelector(".tab-number");n?n.insertAdjacentElement("afterend",i):t.insertBefore(i,t.firstChild)}}else t.setAttribute("aria-busy","false"),delete t.dataset.loadPhase,t.querySelector(".tab-load-spinner")?.remove()}_renderSessionTabsImmediate(){if(this._inlineRenameActive)return;const e=this.$("sessionTabs"),t=e.querySelectorAll(".session-tab[data-id]"),s=new Set([...t].map(l=>l.dataset.id)),i=new Set(this.sessions.keys()),n=[...e.querySelectorAll(".session-tab[data-webview-id]")].map(l=>l.dataset.webviewId),a=(this.webviewOrder||[]).filter(l=>this.webviews?.has(l)),o=n.length===a.length&&n.every((l,c)=>l===a[c]);if(s.size===i.size&&[...s].every(l=>i.has(l))&&o){const l=this.isRichTabRows(),c=this._tabRailSortOrder(this.sessionOrder.filter(h=>this.sessions.has(h)));for(const[h,u]of this.sessions){const d=e.querySelector(`.session-tab[data-id="${h}"]`);if(!d)continue;const S=c?.has(h)?String(c.get(h)):"";d.style.order!==S&&(d.style.order=S);const p=h===this.activeSessionId&&!this.activeWebviewId,b=u.status||"idle",g=this.getSessionName(u),w=u.taskStats||{running:0,total:0},A=w.running>0,I=this.terminalLoadStates.get(h);if(p&&!d.classList.contains("active")?d.classList.add("active"):!p&&d.classList.contains("active")&&d.classList.remove("active"),d.classList.toggle("tab-loading",!!I),I){if(d.setAttribute("aria-busy","true"),d.dataset.loadPhase=I.phase,!d.querySelector(".tab-load-spinner")){const m=document.createElement("span");m.className="tab-load-spinner",m.setAttribute("aria-hidden","true");const T=d.querySelector(".tab-number");T?T.insertAdjacentElement("afterend",m):d.insertBefore(m,d.firstChild)}}else d.setAttribute("aria-busy","false"),delete d.dataset.loadPhase,d.querySelector(".tab-load-spinner")?.remove();const C=this.tabAlerts.get(h),P=C==="action",N=C==="idle",x=d.classList.contains("tab-alert-action"),_=d.classList.contains("tab-alert-idle");if(P&&!x?(d.classList.add("tab-alert-action"),d.classList.remove("tab-alert-idle")):N&&!_?(d.classList.add("tab-alert-idle"),d.classList.remove("tab-alert-action")):!C&&(x||_)&&d.classList.remove("tab-alert-action","tab-alert-idle"),!d.querySelector(".tab-number")){const m=this.sessionOrder.indexOf(h);if(m>=0&&m<9){const T=document.createElement("span");T.className="tab-number",T.textContent=String(m+1),d.insertBefore(T,d.firstChild)}}const $=d.querySelector(".tab-status");$&&!$.classList.contains(b)&&($.className=`tab-status ${b}`),l?this._updateSidebarRichRow(d,h,u):d.dataset.tabState&&(d.querySelector(".tab-meta")?.remove(),d.classList.remove(`tab-state-${d.dataset.tabState}`),delete d.dataset.tabState,delete d.dataset.tabMetaSig);const O=d.querySelector(".tab-name");if(O){const m=parseSessionPrefix(g);if(O.dataset.fullName!==g){if(O.replaceChildren(),m&&m.suffix){const T=document.createElement("span");T.className="tab-name-prefix",T.textContent=`${m.prefix}: `,O.append(T,document.createTextNode(m.suffix))}else O.textContent=g;O.dataset.fullName=g,d.title=m&&m.suffix?u.workingDir?`${m.prefix} (${u.workingDir})`:m.prefix:u.workingDir||""}}const L=d.querySelector(".tab-badge");if(A)if(L)L.textContent!==String(w.running)&&(L.textContent=w.running);else{this._fullRenderSessionTabs();return}else if(L){this._fullRenderSessionTabs();return}const D=d.querySelector(".tab-subagent-badge"),R=this.minimizedSubagents.get(h),y=R?.size||0;if(y>0&&D){const m=D.querySelector(".subagent-label"),T=y===1?"AGENT":`AGENTS (${y})`;m&&m.textContent!==T&&(m.textContent=T);const v=D.querySelector(".subagent-dropdown");if(v){const H=this.renderSubagentTabBadge(h,R),W=document.createElement("div");W.innerHTML=H;const B=W.querySelector(".subagent-dropdown");B&&(v.innerHTML=B.innerHTML)}}else if(y>0&&!D){const m=this.renderSubagentTabBadge(h,R),T=d.querySelector(":scope > .tab-actions");T?T.insertAdjacentHTML("beforebegin",m):d.insertAdjacentHTML("beforeend",m)}else y===0&&D&&D.remove()}}else this._fullRenderSessionTabs();this._lastRenderedActiveTabId=this.activeSessionId,this.updateTabOverflowMode(),this._applyTabEntrances?.(),this._refreshMobileOverviewIfVisible?.(),this._refreshHomeSessionsIfVisible?.(),(this._lineageEdgeCount>0||this._isVerticalTabList())&&this.updateConnectionLines(),this.applySidebarFilter(this._sidebarFilter)}updateTabOverflowMode(){const e=this.$("sessionTabs");if(!e)return;if(this.isSessionSidebarActive()){e.classList.remove("tabs-auto-wrap");return}const t=MobileDetection.getDeviceType(),s=this.loadAppSettingsFromStorage(),i=this.getDefaultSettings(),n=t==="desktop"?s.tabTwoRows??i.tabTwoRows??!1:!1;if((window.CodemanTabOverflow?.resolveTabOrientation?window.CodemanTabOverflow.resolveTabOrientation({deviceType:t,setting:s.tabOrientation??i.tabOrientation??"horizontal"}):"horizontal")==="vertical"){e.classList.remove("tabs-auto-wrap");return}if(n||t!=="desktop"){e.classList.remove("tabs-auto-wrap");return}e.classList.remove("tabs-auto-wrap");const o=window.CodemanTabOverflow?.shouldAutoWrapTabs?window.CodemanTabOverflow.shouldAutoWrapTabs({deviceType:t,manualTwoRows:n,tabCount:this.sessions.size,scrollWidth:e.scrollWidth,clientWidth:e.clientWidth}):e.scrollWidth>e.clientWidth+1;e.classList.toggle("tabs-auto-wrap",o)}_setupTabMiddleClickClose(){const e=this.$("sessionTabs");!e||this._tabAuxClickBound||(this._tabAuxClickBound=!0,e.addEventListener("auxclick",t=>{if(t.button!==1)return;const s=t.target.closest?.(".session-tab");s&&(t.preventDefault(),t.stopPropagation(),s.dataset.id?this.requestCloseSession(s.dataset.id):s.dataset.webviewId&&this.closeWebviewTab?.(s.dataset.webviewId))}))}_fullRenderSessionTabs(){if(this.closeTabRailActionMenu?.(),this._inlineRenameActive)return;const e=this.$("sessionTabs");this.isSessionSidebarActive()&&(this._tallTabsEnabled=!0,e.classList.add("tabs-show-folder"),e.classList.remove("tabs-two-rows","tabs-auto-wrap")),document.querySelectorAll("body > .subagent-dropdown").forEach(h=>h.remove()),this.cancelHideSubagentDropdown();const t=e.scrollLeft,s=e.scrollTop,i=this._lastRenderedActiveTabId,n=!e.querySelector(".session-tab"),a=[],o=this.sessionOrder,r=this.isRichTabRows(),l=this._tabRailSortOrder(o.filter(h=>this.sessions.has(h)));let c=0;for(const h of o){const u=this.sessions.get(h);if(!u)continue;const d=l?.has(h)?` style="order:${l.get(h)}"`:"",S=h===this.activeSessionId&&!this.activeWebviewId,p=u.status||"idle",b=this.getSessionName(u),g=u.mode||"claude",w=u.color||"default",A=u.taskStats||{running:0,total:0},I=A.running>0,C=this.tabAlerts.get(h),P=C==="action"?" tab-alert-action":C==="idle"?" tab-alert-idle":"",N=this.terminalLoadStates.get(h),x=this.minimizedSubagents.get(h),$=(x?.size||0)>0?this.renderSubagentTabBadge(h,x):"",O=this.renderUltracodeTabBadge?this.renderUltracodeTabBadge(h):"",L=u.workingDir&&u.workingDir.split("/").pop()||"",R=(this._tallTabsEnabled??!1)&&u.name&&L&&L!==b,y=parseSessionPrefix(b),m=y&&y.suffix?`<span class="tab-name-prefix">${escapeHtml(y.prefix)}: </span>${escapeHtml(y.suffix)}`:escapeHtml(b),T=y&&y.suffix?u.workingDir?`${y.prefix} (${u.workingDir})`:y.prefix:u.workingDir||"",v=r?this._sidebarRichRow(h,u):null,H=this._sidebarRichMetaHTML(v),W=v?` tab-state-${v.state}`:"",B=v?` data-tab-state="${v.state}" data-tab-meta-sig="${v.state}:${v.since?v.since.at:0}:${v.createdAt}"`:"",V=this.shouldInlineSessionActions(),E=`<span class="tab-actions"><span class="tab-gear" onclick="event.stopPropagation(); app.openSessionOptions(${escapeHtml(JSON.stringify(h))})" title="Session options" aria-label="Session options" tabindex="0">&#x2699;</span><span class="tab-detach" onclick="event.stopPropagation(); app.detachSession(${escapeHtml(JSON.stringify(h))})" title="Open in a new window" aria-label="Open session in a new window" tabindex="0">&#x29C9;</span><span class="tab-close" onclick="event.stopPropagation(); app.requestCloseSession(${escapeHtml(JSON.stringify(h))})" title="Close session" aria-label="Close session" tabindex="0">&times;</span><button type="button" class="tab-more" onclick="event.stopPropagation(); app.openTabRailActionMenu(event, ${escapeHtml(JSON.stringify(h))})" title="Session actions" aria-label="Session actions">&#x22EF;</button></span>`;a.push(`<div class="session-tab ${S?"active":""}${P}${W}${N?" tab-loading":""}${this.hasTabDetachOverride(h)?" tab-show-detach":""}"${B}${d} data-id="${h}" data-color="${w}" ${N?`data-load-phase="${escapeHtml(N.phase)}"`:""} onclick="app.handleSessionTabClick(event, ${escapeHtml(JSON.stringify(h))})" oncontextmenu="event.preventDefault(); app.startInlineRename(${escapeHtml(JSON.stringify(h))})" tabindex="0" role="tab" aria-selected="${S?"true":"false"}" aria-busy="${N?"true":"false"}" aria-label="${escapeHtml(b)} session" ${T?`title="${escapeHtml(T)}"`:""}>
18
+ `)||"Plan usage limits"}_onScheduledCreated(e){this.currentRun=e,this.showTimer()}_onScheduledUpdated(e){this.currentRun=e,this.updateTimer()}_onScheduledCompleted(e){this.currentRun=e,this.hideTimer(),this.showToast("Scheduled run completed!","success")}_onScheduledStopped(){this.currentRun=null,this.hideTimer()}setConnectionStatus(e){this._connectionStatus=e,e==="connected"?(this._connDownSince=null,this._nextSseRetryAt=null,this._offlineOverlayDismissed=!1):this._connDownSince===null&&(this._connDownSince=Date.now()),this._updateConnectionIndicator(),this._updateConnectionLossUi(),e==="connected"&&this._redeliverSweep()}_connectWs(e){this._disconnectWs(),this._wsState="connecting",this._updateConnectionIndicator();const t=location.protocol==="https:"?"wss:":"ws:",s=this._clientId?`${this._clientId}:${this._wsTabNonce}`:"",i=s?`?cid=${encodeURIComponent(s)}`:"",n=`${t}//${location.host}${CodemanBase.base}/ws/sessions/${e}/terminal${i}`,a=new WebSocket(n);this._ws=a,this._wsSessionId=e,a.onopen=()=>{this._ws===a&&(this._wsReady=!0,this._wsState="connected",this._wsReconnectAttempts=0,this._updateConnectionIndicator(),this.sendResize(e)?.catch?.(()=>{}),this._startMobileResizeRetry(e),this._onWsReady(e))},a.onmessage=o=>{if(this._ws===a){this._wsLastRecvAt=Date.now();try{const r=JSON.parse(o.data);r.t==="o"?this._onSessionTerminal({id:e,data:r.d}):r.t==="c"?this._onSessionClearTerminal({id:e}):r.t==="r"?this._onSessionNeedsRefresh({id:e}):r.t==="ia"&&this._onWsInputAck(r.seq,r)}catch{}}},a.onclose=o=>{if(this._ws!==a)return;this._ws=null,this._wsSessionId=null,this._wsReady=!1,this._stopMobileResizeRetry();const r=window.CodemanWsReconnect.plan(o.code,this._wsReconnectAttempts||0);_crashDiag.log(`WS CLOSE code=${o.code} reason=${o.reason||""} action=${r.action} attempts=${this._wsReconnectAttempts||0}`);const l=this.activeSessionId===e;if(r.action==="give-up")this._wsState=l?"fallback":"disconnected",this._updateConnectionIndicator();else if(r.action==="reconnect")if(l){this._wsState="reconnecting",this._updateConnectionIndicator();const c=r.delayMs+Math.floor(Math.random()*250);this._wsReconnectAttempts=(this._wsReconnectAttempts||0)+1,this._wsReconnectTimer=setTimeout(()=>{this._wsReconnectTimer=null,this.activeSessionId===e&&this._connectWs(e)},c)}else this._wsState="disconnected",this._updateConnectionIndicator();else this._wsState=l?"fallback":"disconnected",this._updateConnectionIndicator(),l&&(this._wsReconnectAttempts=(this._wsReconnectAttempts||0)+1,this._wsReconnectTimer=setTimeout(()=>{this._wsReconnectTimer=null,this.activeSessionId===e&&this._connectWs(e)},r.delayMs))},a.onerror=()=>{}}_disconnectWs(){this._clearTimer("_wsReconnectTimer"),this._wsState="disconnected",this._stopMobileResizeRetry(),this._ws&&(this._ws.onclose=null,this._ws.close(),this._ws=null,this._wsSessionId=null,this._wsReady=!1)}_startMobileResizeRetry(e){this._stopMobileResizeRetry(),(typeof MobileDetection<"u"&&MobileDetection.getDeviceType?MobileDetection.getDeviceType():"desktop")!=="desktop"&&(this._mobileResizeRetryTimer=setInterval(()=>{document.visibilityState==="visible"&&(!this._wsReady||this._wsSessionId!==e||typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible||this.sendResize(e)?.catch?.(()=>{}))},MOBILE_RESIZE_RETRY_MS))}_stopMobileResizeRetry(){this._mobileResizeRetryTimer&&(clearInterval(this._mobileResizeRetryTimer),this._mobileResizeRetryTimer=null)}_sendInputAsync(e,t,s){!e||!t||this._reliableSend(e,t,s?.useMux===!0)}_sendInputEphemeral(e,t){if(!(!e||!t)){if(this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e)try{this._ws.send(JSON.stringify({t:"i",d:t}));return}catch{}try{fetch(`/api/sessions/${e}/input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({input:t}),keepalive:!0}).catch(()=>{})}catch{}}}_reliableSend(e,t,s){const n={seq:this._nextSeq(e),data:t,useMux:!!s,ts:Date.now(),tries:0,sentAt:0};let a=this._pendingDeliveries.get(e);a||(a=[],this._pendingDeliveries.set(e,a)),a.push(n),this._persistReliableNow(),this._updateConnectionIndicator(),this._drainSession(e)}_nextSeq(e){const t=(this._seqCounters.get(e)||0)+1;return this._seqCounters.set(e,t),t}_drainSession(e){const t=this._pendingDeliveries.get(e);if(!(!t||t.length===0)){if(this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e){for(const s of t)if(s.sentAt===0)try{this._ws.send(JSON.stringify({t:"i",d:s.data,seq:s.seq,cid:this._clientId})),s.sentAt=Date.now(),s.tries++}catch{break}return}this._postDraining.has(e)||(this._postDraining.add(e),(async()=>{try{for(;;){const s=this._pendingDeliveries.get(e);if(!s||s.length===0||this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e)break;const i=s[0];i.tries++,i.sentAt=Date.now();let n=null;try{const a={input:i.data,seq:i.seq,clientId:this._clientId};i.useMux&&(a.useMux=!0),n=await fetch(`/api/sessions/${e}/input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a),keepalive:i.data.length<65536})}catch{n=null}if(n&&n.ok)this._ackDelivery(e,i.seq);else if(n&&(n.status===404||n.status===410))this._ackDelivery(e,i.seq);else break}}finally{this._postDraining.delete(e)}})())}}_ackDelivery(e,t){const s=this._pendingDeliveries.get(e);if(s){const i=s.findIndex(n=>n.seq===t);i!==-1&&(s.splice(i,1),s.length===0&&this._pendingDeliveries.delete(e),this._pendingDeliveries.size===0?this._persistReliableNow():this._persistReliableState(),this._updateConnectionIndicator())}this.markIdleAlertSeen?.(e)}_onWsInputAck(e,t){const s=this._wsSessionId;if(!(!s||!Number.isInteger(e))){if(t&&t.dup){const i=this._pendingDeliveries.get(s),n=i&&i.find(r=>r.seq===e),a=Number.isInteger(t.last)?t.last:e;(this._seqCounters.get(s)||0)<=a&&(this._seqCounters.set(s,a),this._persistReliableNow());const o=n&&n.tries<=1?n.data:null;this._ackDelivery(s,e),o!==null&&this._reliableSend(s,o,n.useMux);return}this._ackDelivery(s,e)}}_onWsReady(e){const t=this._pendingDeliveries.get(e);if(t)for(const s of t)s.sentAt=0;this._drainSession(e)}_redeliverSweep(){if(this._pendingDeliveries.size!==0)for(const e of[...this._pendingDeliveries.keys()]){const t=this._pendingDeliveries.get(e);if(!t||t.length===0)continue;if(this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e){const i=t[0],n=i&&i.sentAt&&Date.now()-i.sentAt>this._reliableAckTimeoutMs,a=Date.now()-this._wsLastRecvAt>this._reliableAckTimeoutMs;if(n&&a){try{this._ws.close()}catch{}continue}if(n)for(const o of t)o.sentAt&&Date.now()-o.sentAt>this._reliableAckTimeoutMs&&(o.sentAt=0)}this._drainSession(e)}}_pendingBytes(){let e=0,t=0;for(const s of this._pendingDeliveries.values())for(const i of s)e+=i.data.length,t++;return{bytes:e,count:t}}_loadReliableState(){try{this._clientId=localStorage.getItem("codeman:clientId")||""}catch{this._clientId=""}if(!this._clientId){this._clientId="c-"+Math.random().toString(36).slice(2)+"-"+Date.now().toString(36);try{localStorage.setItem("codeman:clientId",this._clientId)}catch{}}try{const e=localStorage.getItem("codeman:pendingInput");if(!e)return;const t=JSON.parse(e);if(t&&t.seqs)for(const[s,i]of Object.entries(t.seqs))Number.isFinite(i)&&this._seqCounters.set(s,i);if(t&&t.pending)for(const[s,i]of Object.entries(t.pending))Array.isArray(i)&&i.length&&this._pendingDeliveries.set(s,i.filter(n=>n&&typeof n.data=="string"&&Number.isInteger(n.seq)).map(n=>({seq:n.seq,data:n.data,useMux:!!n.useMux,ts:n.ts||Date.now(),tries:0,sentAt:0})))}catch{}}_persistReliableState(){this._persistReliableTimer||(this._persistReliableTimer=setTimeout(()=>{this._persistReliableTimer=null,this._persistReliableNow()},250))}_persistReliableNow(){this._persistReliableTimer&&(clearTimeout(this._persistReliableTimer),this._persistReliableTimer=null);try{const e={};for(const[n,a]of this._seqCounters)e[n]=a;const t={};let s=0;for(const[n,a]of this._pendingDeliveries)if(a.length){t[n]=a.map(o=>({seq:o.seq,data:o.data,useMux:o.useMux,ts:o.ts,tries:o.tries}));for(const o of a)s+=o.data.length}const i=s>this._reliableMaxBytes?{seqs:e}:{seqs:e,pending:t};localStorage.setItem("codeman:pendingInput",JSON.stringify(i))}catch{}}_computeConnectionDescriptor(){const{bytes:e,count:t}=this._pendingBytes(),s=t>0,n=e>4,a=r=>r<1024?`${r}B`:`${(r/1024).toFixed(1)}KB`,o=n?` \xB7 ${a(e)} queued`:"";if(!this.isOnline||this._connectionStatus==="offline")return{display:"flex",dotClass:"connection-dot offline",text:n?`Offline (${a(e)} queued)`:"Offline",title:"No network connection"};if(this.activeSessionId){let r,l,c;switch(this._wsState){case"connected":r="connected",l="WS",c="Terminal connected over WebSocket";break;case"fallback":r="fallback",l="HTTP",c="WebSocket unavailable \u2014 input sent over HTTP";break;case"reconnecting":r="reconnecting",l="WS\u2026",c="Reconnecting WebSocket";break;default:r="reconnecting",l="WS\u2026",c="Connecting WebSocket";break}return{display:"flex",dotClass:`connection-dot ${r}`,text:`${l}${o}`,title:c}}return this._connectionStatus==="reconnecting"||this._connectionStatus==="disconnected"?{display:"flex",dotClass:"connection-dot reconnecting",text:n?`Reconnecting (${a(e)} queued)`:"Reconnecting...",title:"Reconnecting to server"}:s?{display:"flex",dotClass:"connection-dot draining",text:n?`Sending ${a(e)}...`:"Sending...",title:"Delivering queued input"}:{display:"none",dotClass:"",text:"",title:""}}_updateConnectionIndicator(){const e=this.$("connectionIndicator"),t=this.$("connectionDot"),s=this.$("connectionText");if(!e||!t||!s)return;const i=this._computeConnectionDescriptor(),n=this._lastIndicatorDescriptor;n&&n.display===i.display&&n.dotClass===i.dotClass&&n.text===i.text&&n.title===i.title||(this._lastIndicatorDescriptor=i,e.style.display=i.display,i.display!=="none"&&(t.className=i.dotClass,s.textContent=i.text,e.title=i.title))}setupOnlineDetection(){window.addEventListener("online",()=>{this.isOnline=!0,this.reconnectAttempts=0,this._connDownSince=Date.now(),this.connectSSE(),this._redeliverSweep()}),window.addEventListener("offline",()=>{this.isOnline=!1,this.setConnectionStatus("offline")})}_updateConnectionLossUi(){const e=window.CodemanConnectionLoss,t=this.$("offlineBanner"),s=this.$("offlineOverlay");if(!e||!t||!s)return;const i=e.compute({isOnline:this.isOnline,status:this._connectionStatus,everLoaded:this._initGeneration>0,downSince:this._connDownSince,now:Date.now(),nextRetryAt:this._nextSseRetryAt,overlayDismissed:this._offlineOverlayDismissed,retryPending:this._offlineRetryPending});this._connDownSince===null?this._stopOfflineTicker():this._startOfflineTicker();const n=this._offlineRetryPending?"Reconnecting\u2026":i.retryInSec!=null&&i.retryInSec>0?`Retrying in ${i.retryInSec}s`:"Retrying\u2026",a=`${i.mode}|${i.kind}|${n}`;if(a!==this._lastOfflineUiKey){if(this._lastOfflineUiKey=a,t.hidden=i.mode!=="banner",s.hidden=i.mode!=="overlay",document.body.classList.toggle("connection-lost",i.mode!=="hidden"),i.mode==="banner"){const o=this.$("offlineBannerText"),r=this.$("offlineBannerDetail");o&&(o.textContent=i.title),r&&(r.textContent=n)}else if(i.mode==="overlay"){const o=this.$("offlineOverlayTitle"),r=this.$("offlineOverlayBody"),l=this.$("offlineOverlayHost"),c=this.$("offlineOverlayStatus");o&&(o.textContent=i.title),r&&(r.textContent=i.detail),l&&(l.textContent=location.host),c&&(c.textContent=n)}}}_startOfflineTicker(){this._offlineUiTicker||(this._offlineUiTicker=setInterval(()=>this._updateConnectionLossUi(),1e3))}_stopOfflineTicker(){this._offlineUiTicker&&(clearInterval(this._offlineUiTicker),this._offlineUiTicker=null)}retryConnection(){this._offlineRetryPending=!0,this._nextSseRetryAt=null,this.reconnectAttempts=0,this._clearTimer("sseReconnectTimeout"),this.isOnline=navigator.onLine,this._lastOfflineUiKey="",this._updateConnectionLossUi(),this.connectSSE(),this.activeSessionId&&this._wsState!=="connected"&&(this._wsReconnectAttempts=0,this._connectWs(this.activeSessionId)),this._clearTimer("_offlineRetryTimer"),this._offlineRetryTimer=setTimeout(()=>{this._offlineRetryPending=!1,this._lastOfflineUiKey="",this._updateConnectionLossUi()},1500)}dismissOfflineOverlay(){this._offlineOverlayDismissed=!0,this._lastOfflineUiKey="",this._updateConnectionLossUi()}_updateCjkInputState(){const e=document.getElementById("cjkInput");if(!e)return;const t=this.loadAppSettingsFromStorage(),s=this.getDefaultSettings?.()||{},n=(this._serverCjkOverride||(t.cjkInputEnabled??s.cjkInputEnabled??!1))&&!!this.activeSessionId;e.classList.toggle("cjk-input-visible",!!n),document.body.classList.toggle("cjk-input-visible",!!n),e.style.display=n?"block":"none",e.setAttribute("aria-hidden",n?"false":"true"),n||(window.cjkActive=!1),typeof KeyboardHandler<"u"&&KeyboardHandler.updateLayoutForKeyboard()}_resetAllAppState(e=!1){this.sessions.clear(),this.ralphStates.clear(),e||(this.terminalBuffers.clear(),this.terminalBufferCache.clear(),this._xtermSnapshots?.clear()),this.projectInsights.clear(),this.teams.clear(),this.teamTasks.clear();for(const t of this.idleTimers.values())clearTimeout(t);if(this.idleTimers.clear(),this._clearTimer("flickerFilterTimeout"),this.flickerFilterBuffer="",this.flickerFilterActive=!1,this._clearTimer("syncWaitTimeout"),this._clearTimer("_clientDropRecoveryTimer"),this.pendingWrites=[],this.writeFrameScheduled=!1,this._terminalWriteInFlight=!1,this._terminalWriteInFlightBytes=0,this._isLoadingBuffer=!1,this._loadBufferQueue=null,this._bufferLoadOwner=null,this._terminalRefreshOwner=null,this._chunkedWriteGen=(this._chunkedWriteGen||0)+1,this._localEchoOverlay?.rerender(),this._predictiveEcho?.clearPredictions(),this.pendingHooks.clear(),this.approvals?.clear(),this._parentNameCache&&this._parentNameCache.clear(),this.subagentActivity.clear(),this.subagentToolResults.clear(),this.workflowRuns&&this.workflowRuns.clear(),this.workflowRunDetails&&this.workflowRunDetails.clear(),this.activeWorkflowRunId=null,this.activeWorkflowPhaseIndex=null,MobileDetection.cleanup(),KeyboardHandler.cleanup(),MobileDetection.init(),KeyboardHandler.init(),this.tabAlerts.clear(),this.attachmentHistoryCounts.clear(),this._shownCompletions&&this._shownCompletions.clear(),this.notificationManager?.titleFlashInterval&&(clearInterval(this.notificationManager.titleFlashInterval),this.notificationManager.titleFlashInterval=null),this.notificationManager?.groupingMap){for(const{timeout:t}of this.notificationManager.groupingMap.values())clearTimeout(t);this.notificationManager.groupingMap.clear()}this.terminalResizeObserver&&(this.terminalResizeObserver.disconnect(),this.terminalResizeObserver=null),this.planLoadingTimer&&(clearInterval(this.planLoadingTimer),this.planLoadingTimer=null),this.timerCountdownInterval&&(clearInterval(this.timerCountdownInterval),this.timerCountdownInterval=null),this.runSummaryAutoRefreshTimer&&(clearInterval(this.runSummaryAutoRefreshTimer),this.runSummaryAutoRefreshTimer=null)}handleInit(e){this._clearTimer("_initFallbackTimer");const t=++this._initGeneration;if(this._serverCjkOverride=e.inputCjkForm||!1,this._updateCjkInputState(),e.planUsage&&this.updatePlanUsageChip(e.planUsage),this.refreshRebootRestoreBanner?.(),e.version){const o=this.$("versionDisplay"),r=this.$("headerVersion");o&&(o.textContent=`v${e.version}`,o.title=`Codeman v${e.version}`),r&&(r.textContent=`v${e.version}`,r.title=`Codeman v${e.version}`)}VoiceInput.cleanup();const s=this.activeSessionId,i=t>1&&!!s&&Array.isArray(e.sessions)&&e.sessions.some(o=>o.id===s);this._resetAllAppState(i),e.sessions.forEach(o=>{this.sessions.set(o.id,o),(o.ralphLoop||o.ralphTodos)&&!this.ralphClosedSessions.has(o.id)&&this.ralphStates.set(o.id,{loop:o.ralphLoop||null,todos:o.ralphTodos||[]})});try{localStorage.removeItem("codeman-tab-meta")}catch{}if(Array.isArray(e.sessionOrder)&&e.sessionOrder.length)try{localStorage.setItem("codeman-session-order",JSON.stringify(e.sessionOrder))}catch{}this.syncSessionOrder(),e.respawnStatus?this.respawnStatus=e.respawnStatus:this.respawnStatus={},this.respawnTimers={},this.respawnCountdownTimers={},this.respawnActionLogs={},e.globalStats&&(this.globalStats=e.globalStats),this.totalCost=e.sessions.reduce((o,r)=>o+(r.totalCost||0),0),this.totalCost+=e.scheduledRuns.reduce((o,r)=>o+(r.totalCost||0),0);const n=e.scheduledRuns.find(o=>o.status==="running");if(n&&(this.currentRun=n,this.showTimer()),this.updateCost(),this.renderSessionTabs(),this.seedApprovals?.(),this.sessions.size>0?this.startSystemStatsPolling():this.stopSystemStatsPolling(),this.cleanupAllFloatingWindows(),e.subagents&&(this.subagents.clear(),this.subagentActivity.clear(),this.subagentToolResults.clear(),e.subagents.forEach(o=>{this.subagents.set(o.agentId,o)}),this.renderSubagentPanel(),this.subagentParentMap.clear(),this.loadSubagentParentMap().then(()=>{for(const[o,r]of this.subagentParentMap){const l=this.subagents.get(o);if(l&&this.sessions.has(r)){l.parentSessionId=r;const c=this.sessions.get(r);c&&(l.parentSessionName=this.getSessionName(c)),this.subagents.set(o,l)}}for(const[o]of this.subagents)this.subagentParentMap.has(o)||this.findParentSessionForSubagent(o);this.restoreSubagentWindowStates()})),e.workflowRuns&&this.seedWorkflowRuns(e.workflowRuns),t!==this._initGeneration)return;if(this.isSoloWindow){this._applySoloMode();return}const a=this.activeSessionId;if(this.sessionOrder.length===0)this.activeSessionId=null;else{let o=a;if(!o||!this.sessions.has(o))try{o=localStorage.getItem("codeman-active-session")}catch{}i&&o===a&&this.sessions.has(o)?(this._wsSessionId!==o&&this._connectWs(o),this._onSessionNeedsRefresh({id:o})):(this.activeSessionId=null,o&&this.sessions.has(o)?this.selectSession(o,{auto:!0}):this.selectSession(this.sessionOrder[0],{auto:!0}))}}async loadState(){try{const t=await(await fetch("/api/status")).json();this.handleInit(t?.data??{})}catch(e){console.error("Failed to load state:",e)}}_debouncedCall(e,t,s=100){this._debounceTimers[e]&&clearTimeout(this._debounceTimers[e]),this._debounceTimers[e]=setTimeout(()=>{this._debounceTimers[e]=null,t.call(this)},s)}getSessionListLayout(){if(this.soloSessionId)return"header";const e=this.loadAppSettingsFromStorage(),t=this.getDefaultSettings(),s=e.sessionListLayout??t.sessionListLayout??"header";return s==="sidebar"||s==="sidebar-rich"?s:"header"}resolveSessionSidebarFontSize(e){const t=Number(e);return Number.isInteger(t)&&t>=11&&t<=18?t:12}applySessionSidebarFontSize(e=null){const t=e??this.loadAppSettingsFromStorage(),s=this.resolveSessionSidebarFontSize(t?.sessionSidebarFontSize);return document.documentElement.style.setProperty("--session-sidebar-name-font-size",`${s}px`),s}isSessionSidebarActive(){return document.documentElement.dataset.sessionList==="sidebar"}_tabOrientation(){return document.documentElement.getAttribute("data-tab-orientation")==="vertical"?"vertical":"horizontal"}_isVerticalTabList(){return this.isSessionSidebarActive()||this._tabOrientation()==="vertical"}shouldInlineSessionActions(){return this.isSessionSidebarActive()?!this.isSessionSidebarCollapsed():this._tabOrientation()==="vertical"&&!document.documentElement.classList.contains("tab-rail-compact")}isSessionSidebarRich(){const e=document.documentElement;return e.dataset.sessionList==="sidebar"&&e.dataset.sidebarDetail==="rich"}isTabRailRich(){const e=document.documentElement;return e.getAttribute("data-tab-orientation")==="vertical"&&e.dataset.tabRailDetail==="rich"&&!e.classList.contains("tab-rail-compact")}isRichTabRows(){return this.isSessionSidebarRich()||this.isTabRailRich()}isTabRailSorted(){const e=document.documentElement;return e.getAttribute("data-tab-orientation")==="vertical"&&e.dataset.tabRailSort==="activity"}_tabRailSortOrder(e){if(!this.isTabRailSorted()||!window.CodemanSessionOrder||typeof this._mobileOverviewState!="function")return null;const t=[];for(let n=0;n<e.length;n++){const a=this.sessions.get(e[n]);a&&t.push({id:e[n],state:this._mobileOverviewState(a,this.pendingHooks?.get(e[n])),lastActivityAt:Number(a.lastActivityAt)||0,lastSubmitAt:Number(a.lastSubmitAt)||0,orderIndex:n})}const s=window.CodemanSessionOrder.sort(t),i=new Map;for(let n=0;n<s.length;n++)i.set(s[n].id,n);return i}_isSessionSidebarOverlay(){return window.innerWidth<1024}isSessionSidebarCollapsed(){if(this._sidebarCollapsedOverride!==void 0)return this._sidebarCollapsedOverride;let e=null;try{e=localStorage.getItem("codeman-sidebar-collapsed")}catch{}return e===null?this._isSessionSidebarOverlay():e==="1"}shouldToggleSessionSidebarFromShortcut(e){if(!e||!e.ctrlKey&&!e.metaKey&&!e.altKey||!this.isSessionSidebarActive()||typeof this.getShortcutRegistry!="function"||typeof this.matchesShortcutEvent!="function")return!1;const t=this.getShortcutRegistry().find(s=>s.id==="toggle-session-sidebar");return!t||t.disabled?!1:this.matchesShortcutEvent(e,t)}applySessionListLayout(){const e=this.getSessionListLayout();this.applySessionSidebarFontSize();const t=e==="sidebar"||e==="sidebar-rich",s=this.isSessionSidebarCollapsed(),i=document.documentElement.dataset.sessionList,n=document.documentElement.dataset.sidebarDetail,a=document.documentElement.dataset.sidebar,o=document.getElementById("sessionTabs"),r=document.getElementById("sessionTabsHost"),l=document.getElementById("sessionSidebarList");if(!o||!r||!l)return;const c=document.getElementById("tabRail"),h=!t&&document.documentElement.getAttribute("data-tab-orientation")==="vertical",u=t?l:h&&c?c:r;o.parentElement!==u&&u.appendChild(o),document.documentElement.dataset.sessionList=t?"sidebar":"header",document.documentElement.dataset.sidebarDetail=e==="sidebar-rich"?"rich":"simple",document.documentElement.dataset.sidebar=s?"collapsed":"expanded",o.setAttribute("aria-orientation",u===r?"horizontal":"vertical");const d=document.getElementById("sidebarToggleBtn");if(d){d.classList.toggle("btn-sidebar-toggle--hidden",!t);const y=s?"Expand session sidebar":"Collapse session sidebar";d.setAttribute("aria-expanded",s?"false":"true"),d.setAttribute("aria-label",y),d.setAttribute("title",y)}const S=document.getElementById("sessionSidebar");if(S){S.classList.toggle("open",t&&!s);const y=t&&s&&this._isSessionSidebarOverlay();S.toggleAttribute("inert",y),y?S.setAttribute("aria-hidden","true"):S.removeAttribute("aria-hidden")}if(!t){this._sidebarFilter="";const y=document.getElementById("sessionSidebarFilter");y&&(y.value="")}const p=this._tallTabsEnabled;this.applyTabWrapSettings();const b=i!==document.documentElement.dataset.sessionList||n!==document.documentElement.dataset.sidebarDetail,g=a!==document.documentElement.dataset.sidebar;(b||g)&&p===this._tallTabsEnabled&&this._fullRenderSessionTabs(),t&&o.classList.remove("tabs-auto-wrap"),this.applySidebarFilter(this._sidebarFilter),this.updateConnectionLines(),document.getElementById("welcomeOverlay")?.classList.contains("visible")&&this.showHomeSessions?.(),this.isRichTabRows()?this._startSidebarRichClock():this._stopSidebarRichClock()}toggleSessionSidebar(){if(!this.isSessionSidebarActive())return;const e=!this.isSessionSidebarCollapsed();this._sidebarCollapsedOverride=e;try{localStorage.setItem("codeman-sidebar-collapsed",e?"1":"0")}catch{}e&&this.$("sessionSidebar")?.contains(document.activeElement)&&document.getElementById("sidebarToggleBtn")?.focus(),this.applySessionListLayout(),!e&&this._isSessionSidebarOverlay()&&this.$("sessionTabs")?.querySelector(".session-tab.active")?.focus()}closeSessionSidebarOnHandheld(){this._isSessionSidebarOverlay()&&(!this.isSessionSidebarActive()||this.isSessionSidebarCollapsed()||this.toggleSessionSidebar())}updateSidebarCount(){const e=document.getElementById("sessionSidebarCount");if(!e)return;const t=this.$("sessionTabs"),s=t?t.querySelectorAll(".session-tab:not(.tab-filtered-out)").length:this.sessions?.size??0;e.textContent=String(s)}applySidebarFilter(e){this._sidebarFilter=(e??"").trim().toLowerCase();const t=this.$("sessionTabs");if(!t)return;const i=this.isSessionSidebarActive()&&document.documentElement.dataset.sidebar!=="collapsed"?this._sidebarFilter:"";for(const n of t.querySelectorAll(".session-tab")){if(!i){n.classList.remove("tab-filtered-out");continue}const a=`${n.getAttribute("aria-label")||""} ${n.getAttribute("title")||""}`.toLowerCase();n.classList.toggle("tab-filtered-out",!a.includes(i))}this.updateSidebarCount()}_sidebarRichPillLabel(e){return{needs:"needs you",error:"error",waiting:"waiting",working:"working",idle:"idle",done:"done"}[e]||e}_sidebarRichRow(e,t){if(typeof this._mobileOverviewState!="function")return null;const s=this._mobileOverviewState(t,this.pendingHooks?.get(e));return{state:s,pill:this._sidebarRichPillLabel(s),createdAt:Number(t.createdAt)||0,since:this._mobileOverviewSince?this._mobileOverviewSince(s,t):null}}_sidebarRichMetaHTML(e){if(!e)return"";const t=(a,o,r,l)=>{const c=this._sidebarRichStampText(o,r),h=o?` title="${escapeHtml(`${a==="created"?"First created":a}: ${new Date(o).toLocaleString()}`)}"`:"";return`<span class="tab-meta-item ${l}"${h}><span class="tab-meta-key">${escapeHtml(a)}</span><span data-tab-ts="${o||0}" data-tab-fmt="${r}">${escapeHtml(c)}</span></span>`},s=[t("created",e.createdAt,"ago","tab-meta-created")];e.since&&(s.push('<span class="tab-meta-sep" aria-hidden="true">\xB7</span>'),s.push(t(e.since.key,e.since.at,"for","tab-meta-since"))),s.push(`<span class="tab-pill tab-pill--${escapeHtml(e.state)}">${escapeHtml(e.pill)}</span>`);const i=[e.createdAt?`First created: ${new Date(e.createdAt).toLocaleString()}`:"",e.since&&e.since.at?`${e.since.key}: ${new Date(e.since.at).toLocaleString()}`:""].filter(Boolean).join(" \xB7 ");return`<span class="tab-meta"${i?` title="${escapeHtml(i)}"`:""} data-i18n-skip>${s.join("")}</span>`}_sidebarRichStampText(e,t){return this._mobileOverviewStampText?this._mobileOverviewStampText(e,t):"\u2014"}_updateSidebarRichRow(e,t,s){const i=this._sidebarRichRow(t,s);if(!i)return;const n=e.dataset.tabState,a=`${i.state}:${i.since?i.since.at:0}:${i.createdAt}`;if(e.dataset.tabMetaSig===a)return;e.dataset.tabMetaSig=a,e.dataset.tabState=i.state,n&&e.classList.remove(`tab-state-${n}`),e.classList.add(`tab-state-${i.state}`);const o=e.querySelector(".tab-info");if(!o)return;const r=this._sidebarRichMetaHTML(i),l=o.querySelector(".tab-meta");l?l.outerHTML=r:o.insertAdjacentHTML("beforeend",r)}_startSidebarRichClock(){this._sidebarRichClock||(this._sidebarRichClock=setInterval(()=>{if(!this.isRichTabRows()){this._stopSidebarRichClock();return}this._tickSidebarRichTimes()},SIDEBAR_RICH_CLOCK_MS))}_stopSidebarRichClock(){this._sidebarRichClock&&(clearInterval(this._sidebarRichClock),this._sidebarRichClock=null)}_tickSidebarRichTimes(){const e=this.$("sessionTabs");if(e)for(const t of e.querySelectorAll("[data-tab-ts]")){const s=this._sidebarRichStampText(Number(t.dataset.tabTs)||0,t.dataset.tabFmt);t.textContent!==s&&(t.textContent=s)}}renderSessionTabs(){this._inlineRenameActive||this._debouncedCall("sessionTabs",this._renderSessionTabsImmediate)}_updateActiveTabImmediate(e){const t=this.$("sessionTabs");if(!t)return;const s=t.querySelectorAll(".session-tab[data-id]");for(const i of s)i.dataset.id===e?i.classList.add("active"):i.classList.remove("active");this._scrollActiveTabIntoView(e)}_scrollActiveTabIntoView(e,t="smooth"){const s=this.$("sessionTabs");if(!s)return;const i=e&&s.querySelector(`.session-tab[data-id="${e}"]`)||s.querySelector(".session-tab.active");if(!i)return;if(this._isVerticalTabList()){i.scrollIntoView({block:"nearest"});return}const n=window.CodemanTabOverflow?.computeTabScrollLeft;if(!n)return;const a=s.getBoundingClientRect(),o=i.getBoundingClientRect(),r=n({scrollLeft:s.scrollLeft,clientWidth:s.clientWidth,scrollWidth:s.scrollWidth,tabLeft:o.left-a.left+s.scrollLeft,tabWidth:o.width});if(Math.abs(r-s.scrollLeft)<1)return;const l=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;typeof s.scrollTo=="function"?s.scrollTo({left:r,behavior:l?"auto":t}):s.scrollLeft=r}_tabAnchor(e){return this._isVerticalTabList()?{x:e.right,y:e.top+e.height/2,spawnLeft:e.right+14,spawnTop:e.top,vertical:!1}:{x:e.left+e.width/2,y:e.bottom,spawnLeft:e.left,spawnTop:e.bottom,vertical:!0}}_tabConnectorPath(e,t){if(e.vertical){const a=t.left+t.width/2,o=t.top,r=(e.y+o)/2;return`M ${e.x} ${e.y} C ${e.x} ${r}, ${a} ${r}, ${a} ${o}`}const s=t.left,i=t.top+t.height/2,n=(e.x+s)/2;return`M ${e.x} ${e.y} C ${n} ${e.y}, ${n} ${i}, ${s} ${i}`}_setTerminalLoadState(e,t,s){this.terminalLoadStates.set(e,{generation:t,phase:s}),this._updateTerminalLoadTab(e)}_clearTerminalLoadState(e,t){const s=this.terminalLoadStates.get(e);s&&s.generation!==t||(this.terminalLoadStates.delete(e),this._updateTerminalLoadTab(e))}_updateTerminalLoadTab(e){const t=this.$("sessionTabs")?.querySelector(`.session-tab[data-id="${e}"]`);if(!t)return;const s=this.terminalLoadStates.get(e);if(t.classList.toggle("tab-loading",!!s),s){if(t.setAttribute("aria-busy","true"),t.dataset.loadPhase=s.phase,!t.querySelector(".tab-load-spinner")){const i=document.createElement("span");i.className="tab-load-spinner",i.setAttribute("aria-hidden","true");const n=t.querySelector(".tab-number");n?n.insertAdjacentElement("afterend",i):t.insertBefore(i,t.firstChild)}}else t.setAttribute("aria-busy","false"),delete t.dataset.loadPhase,t.querySelector(".tab-load-spinner")?.remove()}_renderSessionTabsImmediate(){if(this._inlineRenameActive)return;const e=this.$("sessionTabs"),t=e.querySelectorAll(".session-tab[data-id]"),s=new Set([...t].map(l=>l.dataset.id)),i=new Set(this.sessions.keys()),n=[...e.querySelectorAll(".session-tab[data-webview-id]")].map(l=>l.dataset.webviewId),a=(this.webviewOrder||[]).filter(l=>this.webviews?.has(l)),o=n.length===a.length&&n.every((l,c)=>l===a[c]);if(s.size===i.size&&[...s].every(l=>i.has(l))&&o){const l=this.isRichTabRows(),c=this._tabRailSortOrder(this.sessionOrder.filter(h=>this.sessions.has(h)));for(const[h,u]of this.sessions){const d=e.querySelector(`.session-tab[data-id="${h}"]`);if(!d)continue;const S=c?.has(h)?String(c.get(h)):"";d.style.order!==S&&(d.style.order=S);const p=h===this.activeSessionId&&!this.activeWebviewId,b=u.status||"idle",g=this.getSessionName(u),y=u.taskStats||{running:0,total:0},A=y.running>0,I=this.terminalLoadStates.get(h);if(p&&!d.classList.contains("active")?d.classList.add("active"):!p&&d.classList.contains("active")&&d.classList.remove("active"),d.classList.toggle("tab-loading",!!I),I){if(d.setAttribute("aria-busy","true"),d.dataset.loadPhase=I.phase,!d.querySelector(".tab-load-spinner")){const m=document.createElement("span");m.className="tab-load-spinner",m.setAttribute("aria-hidden","true");const T=d.querySelector(".tab-number");T?T.insertAdjacentElement("afterend",m):d.insertBefore(m,d.firstChild)}}else d.setAttribute("aria-busy","false"),delete d.dataset.loadPhase,d.querySelector(".tab-load-spinner")?.remove();const C=this.tabAlerts.get(h),P=C==="action",N=C==="idle",x=d.classList.contains("tab-alert-action"),_=d.classList.contains("tab-alert-idle");if(P&&!x?(d.classList.add("tab-alert-action"),d.classList.remove("tab-alert-idle")):N&&!_?(d.classList.add("tab-alert-idle"),d.classList.remove("tab-alert-action")):!C&&(x||_)&&d.classList.remove("tab-alert-action","tab-alert-idle"),!d.querySelector(".tab-number")){const m=this.sessionOrder.indexOf(h);if(m>=0&&m<9){const T=document.createElement("span");T.className="tab-number",T.textContent=String(m+1),d.insertBefore(T,d.firstChild)}}const $=d.querySelector(".tab-status");$&&!$.classList.contains(b)&&($.className=`tab-status ${b}`),l?this._updateSidebarRichRow(d,h,u):d.dataset.tabState&&(d.querySelector(".tab-meta")?.remove(),d.classList.remove(`tab-state-${d.dataset.tabState}`),delete d.dataset.tabState,delete d.dataset.tabMetaSig);const O=d.querySelector(".tab-name");if(O){const m=parseSessionPrefix(g);if(O.dataset.fullName!==g){if(O.replaceChildren(),m&&m.suffix){const T=document.createElement("span");T.className="tab-name-prefix",T.textContent=`${m.prefix}: `,O.append(T,document.createTextNode(m.suffix))}else O.textContent=g;O.dataset.fullName=g,d.title=m&&m.suffix?u.workingDir?`${m.prefix} (${u.workingDir})`:m.prefix:u.workingDir||""}}const L=d.querySelector(".tab-badge");if(A)if(L)L.textContent!==String(y.running)&&(L.textContent=y.running);else{this._fullRenderSessionTabs();return}else if(L){this._fullRenderSessionTabs();return}const D=d.querySelector(".tab-subagent-badge"),R=this.minimizedSubagents.get(h),w=R?.size||0;if(w>0&&D){const m=D.querySelector(".subagent-label"),T=w===1?"AGENT":`AGENTS (${w})`;m&&m.textContent!==T&&(m.textContent=T);const v=D.querySelector(".subagent-dropdown");if(v){const H=this.renderSubagentTabBadge(h,R),W=document.createElement("div");W.innerHTML=H;const B=W.querySelector(".subagent-dropdown");B&&(v.innerHTML=B.innerHTML)}}else if(w>0&&!D){const m=this.renderSubagentTabBadge(h,R),T=d.querySelector(":scope > .tab-actions");T?T.insertAdjacentHTML("beforebegin",m):d.insertAdjacentHTML("beforeend",m)}else w===0&&D&&D.remove()}}else this._fullRenderSessionTabs();this._lastRenderedActiveTabId=this.activeSessionId,this.updateTabOverflowMode(),this._applyTabEntrances?.(),this._refreshMobileOverviewIfVisible?.(),this._refreshHomeSessionsIfVisible?.(),(this._lineageEdgeCount>0||this._isVerticalTabList())&&this.updateConnectionLines(),this.applySidebarFilter(this._sidebarFilter)}updateTabOverflowMode(){const e=this.$("sessionTabs");if(!e)return;if(this.isSessionSidebarActive()){e.classList.remove("tabs-auto-wrap");return}const t=MobileDetection.getDeviceType(),s=this.loadAppSettingsFromStorage(),i=this.getDefaultSettings(),n=t==="desktop"?s.tabTwoRows??i.tabTwoRows??!1:!1;if((window.CodemanTabOverflow?.resolveTabOrientation?window.CodemanTabOverflow.resolveTabOrientation({deviceType:t,setting:s.tabOrientation??i.tabOrientation??"horizontal"}):"horizontal")==="vertical"){e.classList.remove("tabs-auto-wrap");return}if(n||t!=="desktop"){e.classList.remove("tabs-auto-wrap");return}e.classList.remove("tabs-auto-wrap");const o=window.CodemanTabOverflow?.shouldAutoWrapTabs?window.CodemanTabOverflow.shouldAutoWrapTabs({deviceType:t,manualTwoRows:n,tabCount:this.sessions.size,scrollWidth:e.scrollWidth,clientWidth:e.clientWidth}):e.scrollWidth>e.clientWidth+1;e.classList.toggle("tabs-auto-wrap",o)}_setupTabMiddleClickClose(){const e=this.$("sessionTabs");!e||this._tabAuxClickBound||(this._tabAuxClickBound=!0,e.addEventListener("auxclick",t=>{if(t.button!==1)return;const s=t.target.closest?.(".session-tab");s&&(t.preventDefault(),t.stopPropagation(),s.dataset.id?this.requestCloseSession(s.dataset.id):s.dataset.webviewId&&this.closeWebviewTab?.(s.dataset.webviewId))}))}_fullRenderSessionTabs(){if(this.closeTabRailActionMenu?.(),this._inlineRenameActive)return;const e=this.$("sessionTabs");this.isSessionSidebarActive()&&(this._tallTabsEnabled=!0,e.classList.add("tabs-show-folder"),e.classList.remove("tabs-two-rows","tabs-auto-wrap")),document.querySelectorAll("body > .subagent-dropdown").forEach(h=>h.remove()),this.cancelHideSubagentDropdown();const t=e.scrollLeft,s=e.scrollTop,i=this._lastRenderedActiveTabId,n=!e.querySelector(".session-tab"),a=[],o=this.sessionOrder,r=this.isRichTabRows(),l=this._tabRailSortOrder(o.filter(h=>this.sessions.has(h)));let c=0;for(const h of o){const u=this.sessions.get(h);if(!u)continue;const d=l?.has(h)?` style="order:${l.get(h)}"`:"",S=h===this.activeSessionId&&!this.activeWebviewId,p=u.status||"idle",b=this.getSessionName(u),g=u.mode||"claude",y=u.color||"default",A=u.taskStats||{running:0,total:0},I=A.running>0,C=this.tabAlerts.get(h),P=C==="action"?" tab-alert-action":C==="idle"?" tab-alert-idle":"",N=this.terminalLoadStates.get(h),x=this.minimizedSubagents.get(h),$=(x?.size||0)>0?this.renderSubagentTabBadge(h,x):"",O=this.renderUltracodeTabBadge?this.renderUltracodeTabBadge(h):"",L=u.workingDir&&u.workingDir.split("/").pop()||"",R=(this._tallTabsEnabled??!1)&&u.name&&L&&L!==b,w=parseSessionPrefix(b),m=w&&w.suffix?`<span class="tab-name-prefix">${escapeHtml(w.prefix)}: </span>${escapeHtml(w.suffix)}`:escapeHtml(b),T=w&&w.suffix?u.workingDir?`${w.prefix} (${u.workingDir})`:w.prefix:u.workingDir||"",v=r?this._sidebarRichRow(h,u):null,H=this._sidebarRichMetaHTML(v),W=v?` tab-state-${v.state}`:"",B=v?` data-tab-state="${v.state}" data-tab-meta-sig="${v.state}:${v.since?v.since.at:0}:${v.createdAt}"`:"",V=this.shouldInlineSessionActions(),E=`<span class="tab-actions"><span class="tab-gear" onclick="event.stopPropagation(); app.openSessionOptions(${escapeHtml(JSON.stringify(h))})" title="Session options" aria-label="Session options" tabindex="0">&#x2699;</span><span class="tab-detach" onclick="event.stopPropagation(); app.detachSession(${escapeHtml(JSON.stringify(h))})" title="Open in a new window" aria-label="Open session in a new window" tabindex="0">&#x29C9;</span><span class="tab-close" onclick="event.stopPropagation(); app.requestCloseSession(${escapeHtml(JSON.stringify(h))})" title="Close session" aria-label="Close session" tabindex="0">&times;</span><button type="button" class="tab-more" onclick="event.stopPropagation(); app.openTabRailActionMenu(event, ${escapeHtml(JSON.stringify(h))})" title="Session actions" aria-label="Session actions">&#x22EF;</button></span>`;a.push(`<div class="session-tab ${S?"active":""}${P}${W}${N?" tab-loading":""}${this.hasTabDetachOverride(h)?" tab-show-detach":""}"${B}${d} data-id="${h}" data-color="${y}" ${N?`data-load-phase="${escapeHtml(N.phase)}"`:""} onclick="app.handleSessionTabClick(event, ${escapeHtml(JSON.stringify(h))})" oncontextmenu="event.preventDefault(); app.startInlineRename(${escapeHtml(JSON.stringify(h))})" tabindex="0" role="tab" aria-selected="${S?"true":"false"}" aria-busy="${N?"true":"false"}" aria-label="${escapeHtml(b)} session" ${T?`title="${escapeHtml(T)}"`:""}>
19
19
  ${c<9?'<span class="tab-number">'+(c+1)+"</span>":""}
20
20
  ${N?'<span class="tab-load-spinner" aria-hidden="true"></span>':""}
21
21
  <span class="tab-status ${p}" aria-hidden="true"></span>
@@ -33,6 +33,6 @@ ${p.join(`
33
33
  ${$}
34
34
  ${O}
35
35
  ${V?"":E}
36
- </div>`),c++}a.push(this.renderWebviewTabs?this.renderWebviewTabs(c):""),e.innerHTML=a.join(""),e.scrollLeft=t,e.scrollTop=s,this._lastRenderedActiveTabId=this.activeSessionId,(n||i!==this.activeSessionId)&&this._scrollActiveTabIntoView(this.activeSessionId,n?"auto":"smooth"),this.setupTabDragHandlers(),this.setupTabKeyboardNavigation(e),this.updateConnectionLines(),this.updateTabOverflowMode(),this._applyTabEntrances?.(),this.applySidebarFilter(this._sidebarFilter),r?this._startSidebarRichClock():this._stopSidebarRichClock()}setupTabKeyboardNavigation(e){this._tabKeydownHandler&&e.removeEventListener("keydown",this._tabKeydownHandler),this._tabKeydownHandler=t=>{if(!["ArrowLeft","ArrowRight","ArrowUp","ArrowDown","Home","End","Enter"," "].includes(t.key))return;const s=[...e.querySelectorAll(".session-tab:not(.tab-filtered-out)")];if(this.isTabRailSorted()){const a=o=>Number(getComputedStyle(o).order)||0;s.sort((o,r)=>a(o)-a(r))}const i=s.indexOf(document.activeElement);if((t.key==="Enter"||t.key===" ")&&i>=0){t.preventDefault();const a=s[i].dataset.id;this.selectSession(a,{forceReload:!0});return}if(i<0)return;let n;switch(t.key){case"ArrowLeft":case"ArrowUp":n=i>0?i-1:s.length-1;break;case"ArrowRight":case"ArrowDown":n=i<s.length-1?i+1:0;break;case"Home":n=0;break;case"End":n=s.length-1;break;default:return}t.preventDefault(),s[n]?.focus()},e.addEventListener("keydown",this._tabKeydownHandler)}handleSessionTabClick(e,t){return e?.preventDefault?.(),!(typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible===!0)&&MobileDetection.isTouchDevice()&&document.activeElement?.blur?.(),this.selectSession(t,{forceReload:!0})}syncSessionOrder(){const e=new Set(this.sessions.keys()),s=this.loadSessionOrder().filter(a=>e.has(a)),i=new Set(s),n=[...e].filter(a=>!i.has(a));this.sessionOrder=[...s,...n]}loadSessionOrder(){try{const e=localStorage.getItem("codeman-session-order");return e?JSON.parse(e):[]}catch{return[]}}saveSessionOrder(){try{localStorage.setItem("codeman-session-order",JSON.stringify(this.sessionOrder))}catch{}const e=[...this.sessionOrder];this._debouncedCall("saveSessionOrderServer",()=>{fetch("/api/session-order",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({order:e})}).catch(()=>{})},400)}_onSessionOrderChanged(e){if(!e||!Array.isArray(e.order))return;try{localStorage.setItem("codeman-session-order",JSON.stringify(e.order))}catch{}const t=JSON.stringify(this.sessionOrder);this.syncSessionOrder(),JSON.stringify(this.sessionOrder)!==t&&this._fullRenderSessionTabs()}setupTabDragHandlers(){const e=this.$("sessionTabs"),t=e.querySelectorAll(".session-tab[data-id]");if(this.isTabRailSorted()){t.forEach(s=>s.setAttribute("draggable","false"));return}t.forEach(s=>{s.setAttribute("draggable","true"),s.addEventListener("dragstart",i=>{this.draggedTabId=s.dataset.id,s.classList.add("dragging"),i.dataTransfer.effectAllowed="move",i.dataTransfer.setData("text/plain",s.dataset.id)}),s.addEventListener("dragend",()=>{s.classList.remove("dragging"),this.draggedTabId=null,e.querySelectorAll(".session-tab").forEach(i=>{i.classList.remove("drag-over-left","drag-over-right")})}),s.addEventListener("dragover",i=>{if(i.preventDefault(),!this.draggedTabId||this.draggedTabId===s.dataset.id)return;i.dataTransfer.dropEffect="move";const n=s.getBoundingClientRect(),a=this._isVerticalTabList()?i.clientY<n.top+n.height/2:i.clientX<n.left+n.width/2;s.classList.toggle("drag-over-left",a),s.classList.toggle("drag-over-right",!a)}),s.addEventListener("dragleave",()=>{s.classList.remove("drag-over-left","drag-over-right")}),s.addEventListener("drop",i=>{if(i.preventDefault(),s.classList.remove("drag-over-left","drag-over-right"),!this.draggedTabId||this.draggedTabId===s.dataset.id)return;const n=s.dataset.id,a=this.draggedTabId,o=s.getBoundingClientRect(),r=this._isVerticalTabList()?i.clientY<o.top+o.height/2:i.clientX<o.left+o.width/2,l=this.sessionOrder.indexOf(a);let c=this.sessionOrder.indexOf(n);l===-1||c===-1||(this.sessionOrder.splice(l,1),c=this.sessionOrder.indexOf(n),c!==-1&&(r?this.sessionOrder.splice(c,0,a):this.sessionOrder.splice(c+1,0,a),this.saveSessionOrder(),this._fullRenderSessionTabs()))})})}moveActiveTabLeft(){if(!this.activeSessionId)return;const e=this.sessionOrder.indexOf(this.activeSessionId);e<=0||([this.sessionOrder[e-1],this.sessionOrder[e]]=[this.sessionOrder[e],this.sessionOrder[e-1]],this.saveSessionOrder(),this._fullRenderSessionTabs())}moveActiveTabRight(){if(!this.activeSessionId)return;const e=this.sessionOrder.indexOf(this.activeSessionId);e===-1||e>=this.sessionOrder.length-1||([this.sessionOrder[e],this.sessionOrder[e+1]]=[this.sessionOrder[e+1],this.sessionOrder[e]],this.saveSessionOrder(),this._fullRenderSessionTabs())}getShortId(e){if(!e)return"";let t=this._shortIdCache.get(e);return t||(t=e.slice(0,8),this._shortIdCache.set(e,t)),t}getSessionName(e){return e.name?e.name:e.workingDir?e.workingDir.split("/").pop()||e.workingDir:this.getShortId(e.id)}_notifySession(e,t,s,i,n){const a=this.sessions.get(e);this.notificationManager?.notify({urgency:t,category:s,sessionId:e,sessionName:a?.name||this.getShortId(e),title:i,message:n})}_isUsableXtermSnapshot(e){return!e||typeof e!="string"||e.length<8?!1:e.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g,"").replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g,"").replace(/\x1b[()][0-2A-Z]/g,"").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g,"").trim().length>=3}_persistXtermSnapshot(e,t){const s="codeman-xs-",n=()=>Object.keys(localStorage).filter(a=>a.startsWith(s)&&a!==e);try{if(localStorage.getItem(e)===null){const a=new Set(Array.from(this.sessions?.keys?.()||[])),o=n().sort((r,l)=>Number(a.has(r.slice(s.length)))-Number(a.has(l.slice(s.length))));for(;o.length>=10;)localStorage.removeItem(o.shift())}try{localStorage.setItem(e,t)}catch{for(const o of n()){localStorage.removeItem(o);try{localStorage.setItem(e,t);return}catch{}}try{localStorage.removeItem(e)}catch{}}}catch{}}_cleanupPreviousSession(e){const t=this.activeSessionId?this.sessions?.get?.(this.activeSessionId):null;if(this.activeSessionId&&t?.mode!=="shell"&&this._serializeAddon&&this._xtermSnapshots)try{const s=this._serializeAddon.serialize({scrollback:1e3});if(this._isUsableXtermSnapshot(s)){if(this._xtermSnapshots.delete(this.activeSessionId),this._xtermSnapshots.set(this.activeSessionId,s),this._xtermSnapshots.size>20){const i=this._xtermSnapshots.keys().next().value;this._xtermSnapshots.delete(i)}s.length<256*1024&&this._persistXtermSnapshot(`codeman-xs-${this.activeSessionId}`,s)}else{this._xtermSnapshots.delete(this.activeSessionId);try{localStorage.removeItem(`codeman-xs-${this.activeSessionId}`)}catch{}}}catch{}if(this._disconnectWs(),typeof CjkInput<"u")CjkInput.clear();else{const s=document.getElementById("cjkInput");s&&(s.value="")}this._clearTimer("flickerFilterTimeout"),this.flickerFilterBuffer="",this.flickerFilterActive=!1,this._tabCompletionSessionId=null,this._tabCompletionRetries=0,this._tabCompletionBaseText=null,this._clearTimer("_tabCompletionFallback"),this._clearTimer("_clientDropRecoveryTimer"),this._terminalRefreshOwner=null,this._clearTimer("syncWaitTimeout"),this.pendingWrites=[],this.writeFrameScheduled=!1,this._terminalWriteInFlight=!1,this._terminalWriteInFlightBytes=0,this._isLoadingBuffer=!1,this._loadBufferQueue=null,this._bufferLoadOwner=null,this._chunkedWriteGen=(this._chunkedWriteGen||0)+1;try{const s=this.terminal?._core?._compositionHelper;if(s?._isComposing){s._isComposing=!1;const i=this.terminal?.element?.querySelector(".xterm-helper-textarea");i&&i.dispatchEvent(new CompositionEvent("compositionend",{data:""}))}}catch{}this._flushLocalEchoTo(this.activeSessionId),this._localEchoOverlay?.clear(),this._predictiveEcho?.clearPredictions(),this._localEchoOverlay&&!this._flushedOffsets?.has(e)&&this._localEchoOverlay.suppressBufferDetection()}_flushLocalEchoTo(e){if(!e)return;const t=this._localEchoOverlay?.pendingText||"",s=this._localEchoOverlay?.getFlushed()?.count||0,i=this._localEchoOverlay?.getFlushed()?.text||"";t&&this._sendInputAsync(e,t);const n=s+t.length;n>0&&(this._flushedOffsets||(this._flushedOffsets=new Map),this._flushedTexts||(this._flushedTexts=new Map),this._flushedOffsets.set(e,n),this._flushedTexts.set(e,i+t))}_resetTerminalForReplay(){this.terminal.reset(),this.terminal.write("\x1B[3J\x1B[H\x1B[2J")}_recordTerminalLoadTiming(e){this._lastTerminalLoadTiming=e,console.info("[TERMINAL-PERF]",e);const t=(e.cacheResetAndParseMs||0)+(e.freshResetAndParseMs||0)+(e.resetAndParseMs||0),s=e.selectDoneMs??e.totalMs??e.selectToReplayCompleteMs??0;_crashDiag.log(`TERMINAL_LOAD: ${e.trigger} ${e.full?"full":"tail"} ${e.chars} chars ttfb=${e.ttfbMs.toFixed(0)}ms body+json=${e.bodyAndJsonMs.toFixed(0)}ms reset+parse=${t.toFixed(0)}ms total=${s.toFixed(0)}ms server="${e.serverTiming}"${e.refused?" refused-downgrade":""}`)}async _maybeRefetchFullHistory({force:e=!1}={}){const t=this.activeSessionId;if(!t||this._fullHistoryRepullInFlight||this._isLoadingBuffer||this.detachedSessions?.has(t))return;const s=this.sessions.get(t);if(!e&&s?.mode==="shell")return;const i=Date.now(),n=this._fullHistoryRepullUseless?.has(t)?6e4:4e3;if(!(!e&&i-(this._fullHistoryRepullAt.get(t)||0)<n)){this._fullHistoryRepullAt.set(t,i),this._fullHistoryRepullInFlight=!0;try{const a=performance.now(),o=await fetch(`/api/sessions/${t}/terminal?full=1`),r=performance.now(),l=(await o.json())?.data??{},c=performance.now(),h=l.terminalBuffer,u={trigger:e?"full-history-button":"full-history-scroll",mode:s?.mode||"unknown",full:!0,source:l.source||"unknown",chars:h?.length||0,ttfbMs:r-a,bodyAndJsonMs:c-r,resetAndParseMs:0,totalMs:0,serverTiming:o.headers?.get?.("server-timing")||"",refused:!1};if(!h||this.activeSessionId!==t)return;if(this._replayWouldShrinkBuffer(h)){u.refused=!0,u.totalMs=performance.now()-a,this._recordTerminalLoadTiming(u),(this._fullHistoryRepullUseless||=new Set).add(t),this._logScrollRouting?.("repull-refused-downgrade"),this._setHistoryTruncation(t,{...l,exhausted:!0});return}this._setHistoryTruncation(t,l),this._fullHistoryRepullUseless?.delete(t);const d=this.terminal.buffer.active.length,S=performance.now();this._resetTerminalForReplay();const{parsedAt:p,bufferLength:b,completed:g}=await this.chunkedTerminalWrite(h,TERMINAL_CHUNK_SIZE,t,this._bufferLoadFinishOpts(l,r));if(u.resetAndParseMs=p-S,!g||this.activeSessionId!==t)return;this.sessions.get(t)?.mode!=="shell"?this.terminalBufferCache.set(t,h):this.terminalBufferCache.delete(t);const w=b-d;w>0?this.terminal.scrollToLine(w):this.terminal.scrollToTop(),this._syncStickyScrollBaseline(),u.totalMs=performance.now()-a,this._recordTerminalLoadTiming(u)}catch{}finally{this._fullHistoryRepullInFlight=!1}}}_setHistoryTruncation(e,t={}){e&&((this._historyTruncation||=new Map).set(e,{truncated:!!t.truncated,reason:t.truncationReason??null,source:t.source??null,fullSize:t.fullSize??0,retainedBytes:t.retainedBytes??0,exhausted:!!t.exhausted}),e===this.activeSessionId&&this._renderHistoryTruncationBanner())}_clearHistoryTruncation(e){this._historyTruncation?.delete(e),e===this.activeSessionId&&this._renderHistoryTruncationBanner()}_renderHistoryTruncationBanner(){const e=document.getElementById("historyTruncationBar");if(!e)return;const t=this.activeSessionId?this._historyTruncation?.get(this.activeSessionId):null,s=computeHistoryTruncationNotice(t||{});if(!s.visible){e.hidden=!0;return}e.textContent="";const i=document.createElement("span");if(i.className="history-trunc-text",i.textContent=s.message,e.appendChild(i),s.canLoadMore){const a=document.createElement("button");a.type="button",a.className="history-trunc-load",a.textContent="Load full history",a.onclick=()=>{a.disabled=!0,a.textContent="Loading\u2026",this._maybeRefetchFullHistory({force:!0}).finally(()=>{this._renderHistoryTruncationBanner()})},e.appendChild(a)}const n=document.createElement("button");n.type="button",n.className="history-trunc-dismiss",n.setAttribute("aria-label","Dismiss history notice"),n.textContent="\xD7",n.onclick=()=>{e.hidden=!0},e.appendChild(n),e.hidden=!1}_shouldFocusTerminalForTabSwitch(){return typeof MobileDetection>"u"||!MobileDetection.isTouchDevice()?!0:typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible}async selectSession(e,t={}){if(!this.isSoloWindow&&this.detachedSessions.has(e)&&this._raiseDetached(e))return;const s=t?.forceReload===!0,i=t?.auto!==!0;if(this.activeSessionId===e&&!s){i&&this.markIdleAlertSeen(e);return}if(this.activeSessionId===e&&s){this.terminalBufferCache?.delete(e),this._xtermSnapshots?.delete(e);try{localStorage.removeItem(`codeman-xs-${e}`)}catch{}this._clearTimer("syncWaitTimeout"),this.pendingWrites=[],this.writeFrameScheduled=!1,this._terminalWriteInFlight=!1,this._terminalWriteInFlightBytes=0,this._isLoadingBuffer=!1,this._loadBufferQueue=null,this._terminalRefreshOwner=null,this._chunkedWriteGen=(this._chunkedWriteGen||0)+1,this._flushLocalEchoTo(e),this.activeSessionId=null}const n=this._shouldFocusTerminalForTabSwitch();n&&this.terminal&&this.terminal.focus();const a=performance.now(),o=this.sessions.get(e)?.name||e.slice(0,8);_crashDiag.log(`SELECT: ${o}`),console.log(`[CRASH-DIAG] selectSession START: ${e.slice(0,8)}`);const r=++this._selectGeneration;if(this._setTerminalLoadState(e,r,"resizing"),r!==this._selectGeneration){this._clearTerminalLoadState(e,r);return}this._hideWebviewLayer?.(),this._cleanupPreviousSession(e),this.activeSessionId=e,this._activateFileBrowserSession?.(e),this._renderHistoryTruncationBanner();try{localStorage.setItem("codeman-active-session",e)}catch{}this._updateSseSubscription(e),this.hideWelcome(),this.playTerminalEntrance?.(e),i&&this.markIdleAlertSeen(e),this._updateActiveTabImmediate(e),this.closeSessionSidebarOnHandheld(),this.renderSessionTabs(),this.updateAttachmentHistoryBadge?.(),this.attachmentHistoryDrawerOpen&&this.loadAttachmentHistory?.(e),this._updateLocalEchoState(),typeof KeyboardAccessoryBar<"u"&&KeyboardAccessoryBar.refreshForActiveSession(),this.refreshHostWakeBanner?.(e),this._flushedOffsets?.has(e)&&this._localEchoOverlay&&this._localEchoOverlay.setFlushed(this._flushedOffsets.get(e),this._flushedTexts?.get(e)||"",!1);const l=document.querySelector(`.session-tab.active[data-id="${e}"]`);l&&(l.classList.add("tab-glow"),l.addEventListener("animationend",()=>l.classList.remove("tab-glow"),{once:!0}));const c=this.sessions.get(e);if(this.currentSessionWorkingDir=c?.workingDir||null,c&&c.pid===null)if(c.respawnBlocked){const S=c.name||"Session";if(window.confirm(`${S} was stopped after crashing repeatedly. Restart it?`))try{await fetch(`/api/sessions/${e}/interactive`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({clearBreaker:!0})}),c.respawnBlocked=!1,c.status="busy"}catch(p){console.error("Failed to restart crash-looped session:",p)}}else try{const S=c.mode==="shell"?`/api/sessions/${e}/shell`:`/api/sessions/${e}/interactive`;await fetch(S,{method:"POST"}),c.status="busy"}catch(S){console.error("Failed to attach to restored session:",S)}if(this._terminalFontReady&&(await this._terminalFontReady,this._isStaleSelect(r))){this._clearTerminalLoadState(e,r);return}this._restoringFlushedState=!0;const h=this._beginBufferLoad(r);let u=!1,d=0;try{this.fitAddon&&this.fitAddon.fit();const S=await this.sendResize(e,{forceHttp:!0}).catch(()=>!1),p=this.getTerminalDimensions?.();if(this._isStaleSelect(r)){this._clearTerminalLoadState(e,r);return}let b=this._xtermSnapshots?.get(e);if(b&&!this._isUsableXtermSnapshot(b)&&(this._xtermSnapshots?.delete(e),b=null),!b)try{const E=localStorage.getItem(`codeman-xs-${e}`);E&&this._isUsableXtermSnapshot(E)?(b=E,this._xtermSnapshots?.delete(e),this._xtermSnapshots?.set(e,E)):E&&localStorage.removeItem(`codeman-xs-${e}`)}catch{}const g=c&&(c.status==="busy"||c.status==="working");let w=!1;if(b&&!g&&c?.mode!=="shell"){if(_crashDiag.log(`SNAPSHOT_RESTORE: ${(b.length/1024).toFixed(0)}KB`),this._setTerminalLoadState(e,r,"replaying"),this._resetTerminalForReplay(),await new Promise(E=>this.terminal.write(b,E)),this._isStaleSelect(r)){this._clearTerminalLoadState(e,r);return}this.scrollToLastNonEmptyLine(),_crashDiag.log("SNAPSHOT_RESTORE_DONE"),w=!0}const A=this.terminalBufferCache.get(e);let I=!1;if(A&&!g&&!w&&c?.mode!=="shell"){_crashDiag.log(`CACHE_WRITE: ${(A.length/1024).toFixed(0)}KB`),this._setTerminalLoadState(e,r,"replaying");const E=performance.now();this._resetTerminalForReplay();const{parsedAt:k}=await this.chunkedTerminalWrite(A,TERMINAL_CHUNK_SIZE,h);if(d=k-E,this._isStaleSelect(r)){this._clearTerminalLoadState(e,r);return}this.terminal.scrollToBottom(),_crashDiag.log("CACHE_DONE")}else(g||c?.mode==="shell")&&(this._resetTerminalForReplay(),I=!0,_crashDiag.log(c?.mode==="shell"?"CACHE_SKIP_SHELL":"CACHE_SKIP_BUSY"));if(c?.mode!=="shell"&&S&&(await new Promise(E=>setTimeout(E,TUI_REDRAW_SETTLE_MS)),this._isStaleSelect(r))){this._clearTerminalLoadState(e,r);return}this._setTerminalLoadState(e,r,"fetching"),_crashDiag.log("FETCH_START");const C=c?.mode!=="shell"&&!this._fullHistoryLoaded.has(e);C&&this._fullHistoryLoaded.add(e);const P=performance.now(),N=await fetch(C?`/api/sessions/${e}/terminal?full=1`:`/api/sessions/${e}/terminal?tail=${TERMINAL_TAIL_SIZE}`),x=performance.now();if(this._isStaleSelect(r)){this._clearTerminalLoadState(e,r);return}const _=(await N.json())?.data??{},$=performance.now(),O=this._bufferLoadFinishOpts(_,x);_crashDiag.log(`FETCH_DONE: ${_.terminalBuffer?(_.terminalBuffer.length/1024).toFixed(0)+"KB":"empty"} truncated=${_.truncated}`);let L=0;if(_.terminalBuffer){if(w||I||_.terminalBuffer!==A){_crashDiag.log(`REWRITE: ${(_.terminalBuffer.length/1024).toFixed(0)}KB`),this._setTerminalLoadState(e,r,"replaying");const k=performance.now();this._resetTerminalForReplay(),this._setHistoryTruncation(e,_);const{parsedAt:K}=await this.chunkedTerminalWrite(_.terminalBuffer,TERMINAL_CHUNK_SIZE,h,O);if(L=K-k,this._isStaleSelect(r)){this._clearTerminalLoadState(e,r);return}this.terminal.scrollToBottom()}if(c?.mode==="shell")this.terminalBufferCache.delete(e);else if(this.terminalBufferCache.set(e,_.terminalBuffer),this.terminalBufferCache.size>20){const k=this.terminalBufferCache.keys().next().value;this.terminalBufferCache.delete(k)}}else(!A||I)&&(I||this._resetTerminalForReplay(),u=!0);const D={trigger:"session-select",mode:c?.mode||"unknown",full:C,source:_.source||"unknown",chars:_.terminalBuffer?.length||0,ttfbMs:x-P,bodyAndJsonMs:$-x,cacheResetAndParseMs:d,freshResetAndParseMs:L,selectToReplayCompleteMs:performance.now()-a,serverTiming:N.headers?.get?.("server-timing")||""};if(this._isLoadingBuffer&&this._finishBufferLoad(h,u?{flushQueued:!0,since:0}:O),this._restoringFlushedState=!1,this._flushedOffsets?.has(e)&&this._localEchoOverlay){this._localEchoOverlay.setFlushed(this._flushedOffsets.get(e),this._flushedTexts?.get(e)||"",!1);const E=this._localEchoOverlay;this.terminal.write("",()=>{E.hasPending&&E.rerender()})}this.sendResize(e);const R=this.getTerminalDimensions?.(),y=_.source==="mux-visible",m=y&&Number.isFinite(_.captureRows)&&!!p&&!!R&&(R.cols!==p.cols||R.rows!==p.rows),T=y&&Number.isFinite(_.captureRows)&&_.captureRows>(this.terminal?.rows||0),v=y&&Number.isFinite(_.captureCols)&&_.captureCols>(this.terminal?.cols||0),H=!!R&&_.captureCols===R.cols&&_.captureRows===R.rows;(typeof requestIdleCallback=="function"?requestIdleCallback:E=>setTimeout(E,16))(()=>{if(r!==this._selectGeneration)return;this.respawnStatus[e]?(this.showRespawnBanner(),this.updateRespawnBanner(this.respawnStatus[e].state),document.getElementById("respawnCycleCount").textContent=this.respawnStatus[e].cycleCount||0,this.updateCountdownTimerDisplay(),this.updateActionLogDisplay(),Object.keys(this.respawnCountdownTimers[e]||{}).length>0&&this.startCountdownInterval()):(this.hideRespawnBanner(),this.stopCountdownInterval());const E=document.getElementById("taskPanel");E&&E.classList.contains("open")&&this.renderTaskPanel();const k=this.sessions.get(e);if(k&&(k.ralphLoop||k.ralphTodos)&&this.updateRalphState(e,{loop:k.ralphLoop,todos:k.ralphTodos}),this.renderRalphStatePanel(),this.updateCliInfoDisplay(),this.renderProjectInsightsPanel(),this.updateSubagentWindowVisibility(),this.loadAppSettingsFromStorage().showFileBrowser){const M=this.$("fileBrowserPanel");if(M&&(M.classList.add("visible"),this.loadFileBrowser(e),!this.fileBrowserDragListeners)){const F=M.querySelector(".file-browser-header");if(F){const U=()=>{if(!M.style.left){const G=M.getBoundingClientRect();M.style.left=`${G.left}px`,M.style.top=`${G.top}px`,M.style.right="auto"}};F.addEventListener("mousedown",U),F.addEventListener("touchstart",U,{passive:!0}),this.fileBrowserDragListeners=this.makeWindowDraggable(M,F),this.fileBrowserDragListeners._onFirstDrag=U}}}}),this._connectWs(e),_crashDiag.log("FOCUS"),n&&this.terminal&&this.terminal.focus(),this.scrollToLastNonEmptyLine(),typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible&&KeyboardHandler.onKeyboardShow();const B=performance.now()-a;D.selectDoneMs=B,this._recordTerminalLoadTiming(D),this._clearTerminalLoadState(e,r),_crashDiag.log(`SELECT_DONE: ${B.toFixed(0)}ms`),console.log(`[CRASH-DIAG] selectSession DONE: ${e.slice(0,8)} in ${B.toFixed(0)}ms`),y&&Number.isFinite(_.captureRows)&&!T&&!v?this._geometryRetryUseless?.delete(e):t?.resizeRetry&&(T||v)&&(this._geometryRetryUseless||=new Set).add(e),(m||T||v)&&!H&&!this._geometryRetryUseless?.has(e)&&!t?.resizeRetry&&!this._isStaleSelect(r)&&(_crashDiag.log(`RESIZE_RETRY: capture ${_.captureCols}x${_.captureRows} vs terminal ${this.terminal?.cols}x${this.terminal?.rows}`+(m?" (size moved under load)":"")),C&&this._fullHistoryLoaded.delete(e),await this.selectSession(e,{auto:!0,forceReload:!0,resizeRetry:!0}))}catch(S){this._isLoadingBuffer&&this._finishBufferLoad(h),this._restoringFlushedState=!1,this._setTerminalLoadState(e,r,"failed"),console.error("Failed to load session terminal:",S)}}_cleanupSessionData(e){this.closeTabRailActionMenu?.(),this._activeRename?.sessionId===e&&this._activeRename.cancel(),this.sessions.delete(e);const t=this.sessionOrder.indexOf(e);t!==-1&&(this.sessionOrder.splice(t,1),this.saveSessionOrder()),this.terminalBuffers.delete(e),this.terminalBufferCache.delete(e),this._clearHistoryTruncation(e),this._xtermSnapshots?.delete(e);try{localStorage.removeItem(`codeman-xs-${e}`)}catch{}this._flushedOffsets?.delete(e),this._flushedTexts?.delete(e),this._pendingDeliveries?.delete(e),this._seqCounters?.delete(e),this._postDraining?.delete(e),this._persistReliableState(),this.ralphStates.delete(e),this.ralphClosedSessions.delete(e),this.projectInsights.delete(e),this.pendingHooks.delete(e),this.tabAlerts.delete(e),this.attachmentHistoryCounts.delete(e),this.attachmentHistoryDrawerOpen&&this.activeSessionId===e&&this.closeAttachmentHistory?.(),this.terminalLoadStates.delete(e),this.clearCountdownTimers(e),this.closeSessionLogViewerWindows(e),this.closeSessionImagePopups(e),this.closeSessionAttachmentCards(e),this.closeSessionSubagentWindows(e,!0);const s=this.idleTimers.get(e);s&&(clearTimeout(s),this.idleTimers.delete(e)),delete this.respawnStatus[e],delete this.respawnTimers[e],delete this.respawnCountdownTimers[e],delete this.respawnActionLogs[e]}async closeSession(e,t=!0){const s=this.activeSessionId===e;this._closingSessions.add(e);try{if(await this._apiDelete(`/api/sessions/${e}?killMux=${t}`),this._cleanupSessionData(e),s){this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}const i=this.sessionOrder.find(n=>n!==e&&this.sessions.has(n));i?this.selectSession(i,{auto:!0}):(this.terminal.clear(),this.showWelcome(),this.renderRalphStatePanel())}this.renderSessionTabs(),t?this.showToast("Session closed and tmux killed","success"):this.showToast("Tab hidden, tmux still running","info")}catch{this.showToast("Failed to close session","error")}finally{this._closingSessions.delete(e)}}requestCloseSession(e){const t=this.sessions.get(e);if(!t)return;this.pendingCloseSessionId=e;const s=this.getSessionName(t),i=document.getElementById("closeConfirmSessionName");i.textContent=s;const n=document.getElementById("closeConfirmKillTitle");n&&(n.textContent=t.mode==="opencode"?"Kill Tmux & OpenCode":t.mode==="codex"?"Kill Tmux & Codex":t.mode==="gemini"?"Kill Tmux & Gemini":t.mode==="antigravity"?"Kill Tmux & Antigravity":t.mode==="pi"?"Kill Tmux & Pi":t.mode==="grok"?"Kill Tmux & Grok":t.mode==="deepseek"?"Kill Tmux & DeepSeek":t.mode==="omp"?"Kill Tmux & OMP":"Kill Tmux & Claude Code"),document.getElementById("closeConfirmModal").classList.add("active")}cancelCloseSession(){this.pendingCloseSessionId=null,document.getElementById("closeConfirmModal").classList.remove("active")}async confirmCloseSession(e=!0){const t=this.pendingCloseSessionId;this.cancelCloseSession(),t&&await this.closeSession(t,e)}nextSession(){if(this.sessionOrder.length<=1)return;const t=(this.sessionOrder.indexOf(this.activeSessionId)+1)%this.sessionOrder.length;this.selectSession(this.sessionOrder[t])}prevSession(){if(this.sessionOrder.length<=1)return;const t=(this.sessionOrder.indexOf(this.activeSessionId)-1+this.sessionOrder.length)%this.sessionOrder.length;this.selectSession(this.sessionOrder[t])}goHome(){this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.terminal.clear(),this.showWelcome(),this.renderSessionTabs(),this.renderRalphStatePanel()}ralphWizardStep=1;ralphWizardConfig={taskDescription:"",completionPhrase:"COMPLETE",maxIterations:10,caseName:"testcase",enableRespawn:!1,generatedPlan:null,planGenerated:!1,skipPlanGeneration:!1,planDetailLevel:"detailed",existingPlan:null,useExistingPlan:!1};planLoadingTimer=null;planLoadingStartTime=null;async killActiveSession(){if(!this.activeSessionId){this.showToast("No active session","warning");return}await this.closeSession(this.activeSessionId)}async killAllSessions(){if(this.sessions.size!==0&&confirm(`Kill all ${this.sessions.size} session(s)?`))try{await this._apiDelete("/api/sessions"),this.sessions.clear(),this.terminalBuffers.clear(),this.terminalBufferCache.clear(),this.terminalLoadStates.clear(),this._xtermSnapshots?.clear();try{for(const e of Object.keys(localStorage))e.startsWith("codeman-xs-")&&localStorage.removeItem(e)}catch{}this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.respawnStatus={},this.respawnCountdownTimers={},this.respawnActionLogs={},this.stopCountdownInterval(),this.hideRespawnBanner(),this.renderSessionTabs(),this.terminal.clear(),this.showWelcome(),this.showToast("All sessions killed","success")}catch{this.showToast("Failed to kill sessions","error")}}showTimer(){document.getElementById("timerBanner").style.display="flex",this.updateTimer(),this.timerInterval=setInterval(()=>this.updateTimer(),1e3)}hideTimer(){document.getElementById("timerBanner").style.display="none",this.timerInterval&&(clearInterval(this.timerInterval),this.timerInterval=null)}updateTimer(){if(!this.currentRun||this.currentRun.status!=="running")return;const e=Date.now(),t=Math.max(0,this.currentRun.endAt-e),s=this.currentRun.endAt-this.currentRun.startedAt,i=e-this.currentRun.startedAt,n=Math.min(100,i/s*100);document.getElementById("timerValue").textContent=this.formatTime(t),document.getElementById("timerProgress").style.width=`${n}%`,document.getElementById("timerMeta").textContent=`${this.currentRun.completedTasks} tasks | $${this.currentRun.totalCost.toFixed(2)}`}async stopCurrentRun(){if(this.currentRun)try{await fetch(`/api/scheduled/${this.currentRun.id}`,{method:"DELETE"})}catch{this.showToast("Failed to stop run","error")}}formatTime(e){const t=Math.floor(e/1e3),s=Math.floor(t/3600),i=Math.floor(t%3600/60),n=t%60;return`${s.toString().padStart(2,"0")}:${i.toString().padStart(2,"0")}:${n.toString().padStart(2,"0")}`}updateCost(){this.updateTokens()}updateTokens(){this._clearTimer("_updateTokensTimeout"),this._updateTokensTimeout=setTimeout(()=>{this._updateTokensTimeout=null,this._updateTokensImmediate()},200)}_updateTokensImmediate(){let e=0,t=0;this.globalStats?(e=this.globalStats.totalInputTokens||0,t=this.globalStats.totalOutputTokens||0):this.sessions.forEach(o=>{o.tokens&&(e+=o.tokens.input||0,t+=o.tokens.output||0)});const s=e+t;this.totalTokens=s;const i=this.formatTokens(s),n=this.estimateCost(e,t),a=this.$("headerTokens");if(a){const r=this.loadAppSettingsFromStorage().showCost??!1;a.textContent=s>0?r?`${i} tokens \xB7 $${n.toFixed(2)}`:`${i} tokens`:"0 tokens",a.title=this.globalStats?`Lifetime: ${this.globalStats.totalSessionsCreated} sessions created${r?`
36
+ </div>`),c++}a.push(this.renderWebviewTabs?this.renderWebviewTabs(c):""),e.innerHTML=a.join(""),e.scrollLeft=t,e.scrollTop=s,this._lastRenderedActiveTabId=this.activeSessionId,(n||i!==this.activeSessionId)&&this._scrollActiveTabIntoView(this.activeSessionId,n?"auto":"smooth"),this.setupTabDragHandlers(),this.setupTabKeyboardNavigation(e),this.updateConnectionLines(),this.updateTabOverflowMode(),this._applyTabEntrances?.(),this.applySidebarFilter(this._sidebarFilter),r?this._startSidebarRichClock():this._stopSidebarRichClock()}setupTabKeyboardNavigation(e){this._tabKeydownHandler&&e.removeEventListener("keydown",this._tabKeydownHandler),this._tabKeydownHandler=t=>{if(!["ArrowLeft","ArrowRight","ArrowUp","ArrowDown","Home","End","Enter"," "].includes(t.key))return;const s=[...e.querySelectorAll(".session-tab:not(.tab-filtered-out)")];if(this.isTabRailSorted()){const a=o=>Number(getComputedStyle(o).order)||0;s.sort((o,r)=>a(o)-a(r))}const i=s.indexOf(document.activeElement);if((t.key==="Enter"||t.key===" ")&&i>=0){t.preventDefault();const a=s[i].dataset.id;this.selectSession(a,{forceReload:!0});return}if(i<0)return;let n;switch(t.key){case"ArrowLeft":case"ArrowUp":n=i>0?i-1:s.length-1;break;case"ArrowRight":case"ArrowDown":n=i<s.length-1?i+1:0;break;case"Home":n=0;break;case"End":n=s.length-1;break;default:return}t.preventDefault(),s[n]?.focus()},e.addEventListener("keydown",this._tabKeydownHandler)}handleSessionTabClick(e,t){return e?.preventDefault?.(),!(typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible===!0)&&MobileDetection.isTouchDevice()&&document.activeElement?.blur?.(),this.selectSession(t,{forceReload:!0})}syncSessionOrder(){const e=new Set(this.sessions.keys()),s=this.loadSessionOrder().filter(a=>e.has(a)),i=new Set(s),n=[...e].filter(a=>!i.has(a));this.sessionOrder=[...s,...n]}loadSessionOrder(){try{const e=localStorage.getItem("codeman-session-order");return e?JSON.parse(e):[]}catch{return[]}}saveSessionOrder(){try{localStorage.setItem("codeman-session-order",JSON.stringify(this.sessionOrder))}catch{}const e=[...this.sessionOrder];this._debouncedCall("saveSessionOrderServer",()=>{fetch("/api/session-order",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({order:e})}).catch(()=>{})},400)}_onSessionOrderChanged(e){if(!e||!Array.isArray(e.order))return;try{localStorage.setItem("codeman-session-order",JSON.stringify(e.order))}catch{}const t=JSON.stringify(this.sessionOrder);this.syncSessionOrder(),JSON.stringify(this.sessionOrder)!==t&&this._fullRenderSessionTabs()}setupTabDragHandlers(){const e=this.$("sessionTabs"),t=e.querySelectorAll(".session-tab[data-id]");if(this.isTabRailSorted()){t.forEach(s=>s.setAttribute("draggable","false"));return}t.forEach(s=>{s.setAttribute("draggable","true"),s.addEventListener("dragstart",i=>{this.draggedTabId=s.dataset.id,s.classList.add("dragging"),i.dataTransfer.effectAllowed="move",i.dataTransfer.setData("text/plain",s.dataset.id)}),s.addEventListener("dragend",()=>{s.classList.remove("dragging"),this.draggedTabId=null,e.querySelectorAll(".session-tab").forEach(i=>{i.classList.remove("drag-over-left","drag-over-right")})}),s.addEventListener("dragover",i=>{if(i.preventDefault(),!this.draggedTabId||this.draggedTabId===s.dataset.id)return;i.dataTransfer.dropEffect="move";const n=s.getBoundingClientRect(),a=this._isVerticalTabList()?i.clientY<n.top+n.height/2:i.clientX<n.left+n.width/2;s.classList.toggle("drag-over-left",a),s.classList.toggle("drag-over-right",!a)}),s.addEventListener("dragleave",()=>{s.classList.remove("drag-over-left","drag-over-right")}),s.addEventListener("drop",i=>{if(i.preventDefault(),s.classList.remove("drag-over-left","drag-over-right"),!this.draggedTabId||this.draggedTabId===s.dataset.id)return;const n=s.dataset.id,a=this.draggedTabId,o=s.getBoundingClientRect(),r=this._isVerticalTabList()?i.clientY<o.top+o.height/2:i.clientX<o.left+o.width/2,l=this.sessionOrder.indexOf(a);let c=this.sessionOrder.indexOf(n);l===-1||c===-1||(this.sessionOrder.splice(l,1),c=this.sessionOrder.indexOf(n),c!==-1&&(r?this.sessionOrder.splice(c,0,a):this.sessionOrder.splice(c+1,0,a),this.saveSessionOrder(),this._fullRenderSessionTabs()))})})}moveActiveTabLeft(){if(!this.activeSessionId)return;const e=this.sessionOrder.indexOf(this.activeSessionId);e<=0||([this.sessionOrder[e-1],this.sessionOrder[e]]=[this.sessionOrder[e],this.sessionOrder[e-1]],this.saveSessionOrder(),this._fullRenderSessionTabs())}moveActiveTabRight(){if(!this.activeSessionId)return;const e=this.sessionOrder.indexOf(this.activeSessionId);e===-1||e>=this.sessionOrder.length-1||([this.sessionOrder[e],this.sessionOrder[e+1]]=[this.sessionOrder[e+1],this.sessionOrder[e]],this.saveSessionOrder(),this._fullRenderSessionTabs())}getShortId(e){if(!e)return"";let t=this._shortIdCache.get(e);return t||(t=e.slice(0,8),this._shortIdCache.set(e,t)),t}getSessionName(e){return e.name?e.name:e.workingDir?e.workingDir.split("/").pop()||e.workingDir:this.getShortId(e.id)}_notifySession(e,t,s,i,n){const a=this.sessions.get(e);this.notificationManager?.notify({urgency:t,category:s,sessionId:e,sessionName:a?.name||this.getShortId(e),title:i,message:n})}_isUsableXtermSnapshot(e){return!e||typeof e!="string"||e.length<8?!1:e.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g,"").replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g,"").replace(/\x1b[()][0-2A-Z]/g,"").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g,"").trim().length>=3}_persistXtermSnapshot(e,t){const s="codeman-xs-",n=()=>Object.keys(localStorage).filter(a=>a.startsWith(s)&&a!==e);try{if(localStorage.getItem(e)===null){const a=new Set(Array.from(this.sessions?.keys?.()||[])),o=n().sort((r,l)=>Number(a.has(r.slice(s.length)))-Number(a.has(l.slice(s.length))));for(;o.length>=10;)localStorage.removeItem(o.shift())}try{localStorage.setItem(e,t)}catch{for(const o of n()){localStorage.removeItem(o);try{localStorage.setItem(e,t);return}catch{}}try{localStorage.removeItem(e)}catch{}}}catch{}}_cleanupPreviousSession(e){const t=this.activeSessionId?this.sessions?.get?.(this.activeSessionId):null;if(this.activeSessionId&&t?.mode!=="shell"&&this._serializeAddon&&this._xtermSnapshots)try{const s=this._serializeAddon.serialize({scrollback:1e3});if(this._isUsableXtermSnapshot(s)){if(this._xtermSnapshots.delete(this.activeSessionId),this._xtermSnapshots.set(this.activeSessionId,s),this._xtermSnapshots.size>20){const i=this._xtermSnapshots.keys().next().value;this._xtermSnapshots.delete(i)}s.length<256*1024&&this._persistXtermSnapshot(`codeman-xs-${this.activeSessionId}`,s)}else{this._xtermSnapshots.delete(this.activeSessionId);try{localStorage.removeItem(`codeman-xs-${this.activeSessionId}`)}catch{}}}catch{}if(this._disconnectWs(),typeof CjkInput<"u")CjkInput.clear();else{const s=document.getElementById("cjkInput");s&&(s.value="")}this._clearTimer("flickerFilterTimeout"),this.flickerFilterBuffer="",this.flickerFilterActive=!1,this._tabCompletionSessionId=null,this._tabCompletionRetries=0,this._tabCompletionBaseText=null,this._clearTimer("_tabCompletionFallback"),this._clearTimer("_clientDropRecoveryTimer"),this._terminalRefreshOwner=null,this._clearTimer("syncWaitTimeout"),this.pendingWrites=[],this.writeFrameScheduled=!1,this._terminalWriteInFlight=!1,this._terminalWriteInFlightBytes=0,this._isLoadingBuffer=!1,this._loadBufferQueue=null,this._bufferLoadOwner=null,this._chunkedWriteGen=(this._chunkedWriteGen||0)+1;try{const s=this.terminal?._core?._compositionHelper;if(s?._isComposing){s._isComposing=!1;const i=this.terminal?.element?.querySelector(".xterm-helper-textarea");i&&i.dispatchEvent(new CompositionEvent("compositionend",{data:""}))}}catch{}this._flushLocalEchoTo(this.activeSessionId),this._localEchoOverlay?.clear(),this._predictiveEcho?.clearPredictions(),this._localEchoOverlay&&!this._flushedOffsets?.has(e)&&this._localEchoOverlay.suppressBufferDetection()}_flushLocalEchoTo(e){if(!e)return;const t=this._localEchoOverlay?.pendingText||"",s=this._localEchoOverlay?.getFlushed()?.count||0,i=this._localEchoOverlay?.getFlushed()?.text||"";t&&this._sendInputAsync(e,t);const n=s+t.length;n>0&&(this._flushedOffsets||(this._flushedOffsets=new Map),this._flushedTexts||(this._flushedTexts=new Map),this._flushedOffsets.set(e,n),this._flushedTexts.set(e,i+t))}_resetTerminalForReplay(){this.terminal.reset(),this.terminal.write("\x1B[3J\x1B[H\x1B[2J")}_recordTerminalLoadTiming(e){this._lastTerminalLoadTiming=e,console.info("[TERMINAL-PERF]",e);const t=(e.cacheResetAndParseMs||0)+(e.freshResetAndParseMs||0)+(e.resetAndParseMs||0),s=e.selectDoneMs??e.totalMs??e.selectToReplayCompleteMs??0;_crashDiag.log(`TERMINAL_LOAD: ${e.trigger} ${e.full?"full":"tail"} ${e.chars} chars ttfb=${e.ttfbMs.toFixed(0)}ms body+json=${e.bodyAndJsonMs.toFixed(0)}ms reset+parse=${t.toFixed(0)}ms total=${s.toFixed(0)}ms server="${e.serverTiming}"${e.refused?" refused-downgrade":""}`)}async _maybeRefetchFullHistory({force:e=!1}={}){const t=this.activeSessionId;if(!t||this._fullHistoryRepullInFlight||this._isLoadingBuffer||this.detachedSessions?.has(t))return;const s=this.sessions.get(t);if(!e&&s?.mode==="shell")return;const i=Date.now(),n=this._fullHistoryRepullUseless?.has(t)?6e4:4e3;if(!(!e&&i-(this._fullHistoryRepullAt.get(t)||0)<n)){this._fullHistoryRepullAt.set(t,i),this._fullHistoryRepullInFlight=!0;try{const a=performance.now(),o=await fetch(`/api/sessions/${t}/terminal?full=1`),r=performance.now(),l=(await o.json())?.data??{},c=performance.now(),h=l.terminalBuffer,u={trigger:e?"full-history-button":"full-history-scroll",mode:s?.mode||"unknown",full:!0,source:l.source||"unknown",chars:h?.length||0,ttfbMs:r-a,bodyAndJsonMs:c-r,resetAndParseMs:0,totalMs:0,serverTiming:o.headers?.get?.("server-timing")||"",refused:!1};if(!h||this.activeSessionId!==t)return;if(this._replayWouldShrinkBuffer(h)){u.refused=!0,u.totalMs=performance.now()-a,this._recordTerminalLoadTiming(u),(this._fullHistoryRepullUseless||=new Set).add(t),this._logScrollRouting?.("repull-refused-downgrade"),this._setHistoryTruncation(t,{...l,exhausted:!0});return}this._setHistoryTruncation(t,l),this._fullHistoryRepullUseless?.delete(t);const d=this.terminal.buffer.active.length,S=performance.now();this._resetTerminalForReplay();const{parsedAt:p,bufferLength:b,completed:g}=await this.chunkedTerminalWrite(h,TERMINAL_CHUNK_SIZE,t,this._bufferLoadFinishOpts(l,r));if(u.resetAndParseMs=p-S,!g||this.activeSessionId!==t)return;this.sessions.get(t)?.mode!=="shell"?this.terminalBufferCache.set(t,h):this.terminalBufferCache.delete(t);const y=b-d;y>0?this.terminal.scrollToLine(y):this.terminal.scrollToTop(),this._syncStickyScrollBaseline(),u.totalMs=performance.now()-a,this._recordTerminalLoadTiming(u)}catch{}finally{this._fullHistoryRepullInFlight=!1}}}_setHistoryTruncation(e,t={}){e&&((this._historyTruncation||=new Map).set(e,{truncated:!!t.truncated,reason:t.truncationReason??null,source:t.source??null,fullSize:t.fullSize??0,retainedBytes:t.retainedBytes??0,exhausted:!!t.exhausted}),e===this.activeSessionId&&this._renderHistoryTruncationBanner())}_clearHistoryTruncation(e){this._historyTruncation?.delete(e),e===this.activeSessionId&&this._renderHistoryTruncationBanner()}_renderHistoryTruncationBanner(){const e=document.getElementById("historyTruncationBar");if(!e)return;const t=this.activeSessionId?this._historyTruncation?.get(this.activeSessionId):null,s=computeHistoryTruncationNotice(t||{});if(!s.visible){e.hidden=!0;return}e.textContent="";const i=document.createElement("span");if(i.className="history-trunc-text",i.textContent=s.message,e.appendChild(i),s.canLoadMore){const a=document.createElement("button");a.type="button",a.className="history-trunc-load",a.textContent="Load full history",a.onclick=()=>{a.disabled=!0,a.textContent="Loading\u2026",this._maybeRefetchFullHistory({force:!0}).finally(()=>{this._renderHistoryTruncationBanner()})},e.appendChild(a)}const n=document.createElement("button");n.type="button",n.className="history-trunc-dismiss",n.setAttribute("aria-label","Dismiss history notice"),n.textContent="\xD7",n.onclick=()=>{e.hidden=!0},e.appendChild(n),e.hidden=!1}_shouldFocusTerminalForTabSwitch(){return typeof MobileDetection>"u"||!MobileDetection.isTouchDevice()?!0:typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible}async selectSession(e,t={}){if(!this.isSoloWindow&&this.detachedSessions.has(e)&&this._raiseDetached(e))return;const s=t?.forceReload===!0,i=t?.auto!==!0;if(this.activeSessionId===e&&!s){i&&this.markIdleAlertSeen(e);return}if(this.activeSessionId===e&&s){this.terminalBufferCache?.delete(e),this._xtermSnapshots?.delete(e);try{localStorage.removeItem(`codeman-xs-${e}`)}catch{}this._clearTimer("syncWaitTimeout"),this.pendingWrites=[],this.writeFrameScheduled=!1,this._terminalWriteInFlight=!1,this._terminalWriteInFlightBytes=0,this._isLoadingBuffer=!1,this._loadBufferQueue=null,this._terminalRefreshOwner=null,this._chunkedWriteGen=(this._chunkedWriteGen||0)+1,this._flushLocalEchoTo(e),this.activeSessionId=null}const n=this._shouldFocusTerminalForTabSwitch();n&&this.terminal&&this.terminal.focus();const a=performance.now(),o=this.sessions.get(e)?.name||e.slice(0,8);_crashDiag.log(`SELECT: ${o}`),console.log(`[CRASH-DIAG] selectSession START: ${e.slice(0,8)}`);const r=++this._selectGeneration;if(this._setTerminalLoadState(e,r,"resizing"),r!==this._selectGeneration){this._clearTerminalLoadState(e,r);return}this._hideWebviewLayer?.(),this._cleanupPreviousSession(e),this.activeSessionId=e,this._activateFileBrowserSession?.(e),this._renderHistoryTruncationBanner();try{localStorage.setItem("codeman-active-session",e)}catch{}this._updateSseSubscription(e),this.hideWelcome(),this.playTerminalEntrance?.(e),i&&this.markIdleAlertSeen(e),this._updateActiveTabImmediate(e),this.closeSessionSidebarOnHandheld(),this.renderSessionTabs(),this.updateAttachmentHistoryBadge?.(),this.attachmentHistoryDrawerOpen&&this.loadAttachmentHistory?.(e),this._updateLocalEchoState(),typeof KeyboardAccessoryBar<"u"&&KeyboardAccessoryBar.refreshForActiveSession(),this.refreshHostWakeBanner?.(e),this._flushedOffsets?.has(e)&&this._localEchoOverlay&&this._localEchoOverlay.setFlushed(this._flushedOffsets.get(e),this._flushedTexts?.get(e)||"",!1);const l=document.querySelector(`.session-tab.active[data-id="${e}"]`);l&&(l.classList.add("tab-glow"),l.addEventListener("animationend",()=>l.classList.remove("tab-glow"),{once:!0}));const c=this.sessions.get(e);if(this.currentSessionWorkingDir=c?.workingDir||null,c&&c.pid===null)if(c.respawnBlocked){const S=c.name||"Session";if(window.confirm(`${S} was stopped after crashing repeatedly. Restart it?`))try{await fetch(`/api/sessions/${e}/interactive`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({clearBreaker:!0})}),c.respawnBlocked=!1,c.status="busy"}catch(p){console.error("Failed to restart crash-looped session:",p)}}else try{const S=c.mode==="shell"?`/api/sessions/${e}/shell`:`/api/sessions/${e}/interactive`;await fetch(S,{method:"POST"}),c.status="busy"}catch(S){console.error("Failed to attach to restored session:",S)}if(this._terminalFontReady&&(await this._terminalFontReady,this._isStaleSelect(r))){this._clearTerminalLoadState(e,r);return}this._restoringFlushedState=!0;const h=this._beginBufferLoad(r);let u=!1,d=0;try{this.fitAddon&&this.fitAddon.fit();const S=await this.sendResize(e,{forceHttp:!0}).catch(()=>!1),p=this.getTerminalDimensions?.();if(this._isStaleSelect(r)){this._clearTerminalLoadState(e,r);return}let b=this._xtermSnapshots?.get(e);if(b&&!this._isUsableXtermSnapshot(b)&&(this._xtermSnapshots?.delete(e),b=null),!b)try{const E=localStorage.getItem(`codeman-xs-${e}`);E&&this._isUsableXtermSnapshot(E)?(b=E,this._xtermSnapshots?.delete(e),this._xtermSnapshots?.set(e,E)):E&&localStorage.removeItem(`codeman-xs-${e}`)}catch{}const g=c&&(c.status==="busy"||c.status==="working");let y=!1;if(b&&!g&&c?.mode!=="shell"){if(_crashDiag.log(`SNAPSHOT_RESTORE: ${(b.length/1024).toFixed(0)}KB`),this._setTerminalLoadState(e,r,"replaying"),this._resetTerminalForReplay(),await new Promise(E=>this.terminal.write(b,E)),this._isStaleSelect(r)){this._clearTerminalLoadState(e,r);return}this.scrollToLastNonEmptyLine(),_crashDiag.log("SNAPSHOT_RESTORE_DONE"),y=!0}const A=this.terminalBufferCache.get(e);let I=!1;if(A&&!g&&!y&&c?.mode!=="shell"){_crashDiag.log(`CACHE_WRITE: ${(A.length/1024).toFixed(0)}KB`),this._setTerminalLoadState(e,r,"replaying");const E=performance.now();this._resetTerminalForReplay();const{parsedAt:k}=await this.chunkedTerminalWrite(A,TERMINAL_CHUNK_SIZE,h);if(d=k-E,this._isStaleSelect(r)){this._clearTerminalLoadState(e,r);return}this.terminal.scrollToBottom(),_crashDiag.log("CACHE_DONE")}else(g||c?.mode==="shell")&&(this._resetTerminalForReplay(),I=!0,_crashDiag.log(c?.mode==="shell"?"CACHE_SKIP_SHELL":"CACHE_SKIP_BUSY"));if(c?.mode!=="shell"&&S&&(await new Promise(E=>setTimeout(E,TUI_REDRAW_SETTLE_MS)),this._isStaleSelect(r))){this._clearTerminalLoadState(e,r);return}this._setTerminalLoadState(e,r,"fetching"),_crashDiag.log("FETCH_START");const C=c?.mode!=="shell"&&!this._fullHistoryLoaded.has(e);C&&this._fullHistoryLoaded.add(e);const P=performance.now(),N=await fetch(C?`/api/sessions/${e}/terminal?full=1`:`/api/sessions/${e}/terminal?tail=${TERMINAL_TAIL_SIZE}`),x=performance.now();if(this._isStaleSelect(r)){this._clearTerminalLoadState(e,r);return}const _=(await N.json())?.data??{},$=performance.now(),O=this._bufferLoadFinishOpts(_,x);_crashDiag.log(`FETCH_DONE: ${_.terminalBuffer?(_.terminalBuffer.length/1024).toFixed(0)+"KB":"empty"} truncated=${_.truncated}`);let L=0;if(_.terminalBuffer){if(y||I||_.terminalBuffer!==A){_crashDiag.log(`REWRITE: ${(_.terminalBuffer.length/1024).toFixed(0)}KB`),this._setTerminalLoadState(e,r,"replaying");const k=performance.now();this._resetTerminalForReplay(),this._setHistoryTruncation(e,_);const{parsedAt:K}=await this.chunkedTerminalWrite(_.terminalBuffer,TERMINAL_CHUNK_SIZE,h,O);if(L=K-k,this._isStaleSelect(r)){this._clearTerminalLoadState(e,r);return}this.terminal.scrollToBottom()}if(c?.mode==="shell")this.terminalBufferCache.delete(e);else if(this.terminalBufferCache.set(e,_.terminalBuffer),this.terminalBufferCache.size>20){const k=this.terminalBufferCache.keys().next().value;this.terminalBufferCache.delete(k)}}else(!A||I)&&(I||this._resetTerminalForReplay(),u=!0);const D={trigger:"session-select",mode:c?.mode||"unknown",full:C,source:_.source||"unknown",chars:_.terminalBuffer?.length||0,ttfbMs:x-P,bodyAndJsonMs:$-x,cacheResetAndParseMs:d,freshResetAndParseMs:L,selectToReplayCompleteMs:performance.now()-a,serverTiming:N.headers?.get?.("server-timing")||""};if(this._isLoadingBuffer&&this._finishBufferLoad(h,u?{flushQueued:!0,since:0}:O),this._restoringFlushedState=!1,this._flushedOffsets?.has(e)&&this._localEchoOverlay){this._localEchoOverlay.setFlushed(this._flushedOffsets.get(e),this._flushedTexts?.get(e)||"",!1);const E=this._localEchoOverlay;this.terminal.write("",()=>{E.hasPending&&E.rerender()})}this.sendResize(e);const R=this.getTerminalDimensions?.(),w=_.source==="mux-visible",m=w&&Number.isFinite(_.captureRows)&&!!p&&!!R&&(R.cols!==p.cols||R.rows!==p.rows),T=w&&Number.isFinite(_.captureRows)&&_.captureRows>(this.terminal?.rows||0),v=w&&Number.isFinite(_.captureCols)&&_.captureCols>(this.terminal?.cols||0),H=!!R&&_.captureCols===R.cols&&_.captureRows===R.rows;(typeof requestIdleCallback=="function"?requestIdleCallback:E=>setTimeout(E,16))(()=>{if(r!==this._selectGeneration)return;this.respawnStatus[e]?(this.showRespawnBanner(),this.updateRespawnBanner(this.respawnStatus[e].state),document.getElementById("respawnCycleCount").textContent=this.respawnStatus[e].cycleCount||0,this.updateCountdownTimerDisplay(),this.updateActionLogDisplay(),Object.keys(this.respawnCountdownTimers[e]||{}).length>0&&this.startCountdownInterval()):(this.hideRespawnBanner(),this.stopCountdownInterval());const E=document.getElementById("taskPanel");E&&E.classList.contains("open")&&this.renderTaskPanel();const k=this.sessions.get(e);if(k&&(k.ralphLoop||k.ralphTodos)&&this.updateRalphState(e,{loop:k.ralphLoop,todos:k.ralphTodos}),this.renderRalphStatePanel(),this.updateCliInfoDisplay(),this.renderProjectInsightsPanel(),this.updateSubagentWindowVisibility(),this.loadAppSettingsFromStorage().showFileBrowser){const M=this.$("fileBrowserPanel");if(M&&(M.classList.add("visible"),this.loadFileBrowser(e),!this.fileBrowserDragListeners)){const F=M.querySelector(".file-browser-header");if(F){const U=()=>{if(!M.style.left){const G=M.getBoundingClientRect();M.style.left=`${G.left}px`,M.style.top=`${G.top}px`,M.style.right="auto"}};F.addEventListener("mousedown",U),F.addEventListener("touchstart",U,{passive:!0}),this.fileBrowserDragListeners=this.makeWindowDraggable(M,F),this.fileBrowserDragListeners._onFirstDrag=U}}}}),this._connectWs(e),_crashDiag.log("FOCUS"),n&&this.terminal&&this.terminal.focus(),this.scrollToLastNonEmptyLine(),typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible&&KeyboardHandler.onKeyboardShow();const B=performance.now()-a;D.selectDoneMs=B,this._recordTerminalLoadTiming(D),this._clearTerminalLoadState(e,r),_crashDiag.log(`SELECT_DONE: ${B.toFixed(0)}ms`),console.log(`[CRASH-DIAG] selectSession DONE: ${e.slice(0,8)} in ${B.toFixed(0)}ms`),w&&Number.isFinite(_.captureRows)&&!T&&!v?this._geometryRetryUseless?.delete(e):t?.resizeRetry&&(T||v)&&(this._geometryRetryUseless||=new Set).add(e),(m||T||v)&&!H&&!this._geometryRetryUseless?.has(e)&&!t?.resizeRetry&&!this._isStaleSelect(r)&&(_crashDiag.log(`RESIZE_RETRY: capture ${_.captureCols}x${_.captureRows} vs terminal ${this.terminal?.cols}x${this.terminal?.rows}`+(m?" (size moved under load)":"")),C&&this._fullHistoryLoaded.delete(e),await this.selectSession(e,{auto:!0,forceReload:!0,resizeRetry:!0}))}catch(S){this._isLoadingBuffer&&this._finishBufferLoad(h),this._restoringFlushedState=!1,this._setTerminalLoadState(e,r,"failed"),console.error("Failed to load session terminal:",S)}}_cleanupSessionData(e){this.closeTabRailActionMenu?.(),this._activeRename?.sessionId===e&&this._activeRename.cancel(),this.sessions.delete(e);const t=this.sessionOrder.indexOf(e);t!==-1&&(this.sessionOrder.splice(t,1),this.saveSessionOrder()),this.terminalBuffers.delete(e),this.terminalBufferCache.delete(e),this._clearHistoryTruncation(e),this._xtermSnapshots?.delete(e);try{localStorage.removeItem(`codeman-xs-${e}`)}catch{}this._flushedOffsets?.delete(e),this._flushedTexts?.delete(e),typeof KeyboardAccessoryBar<"u"&&KeyboardAccessoryBar.discardComposerDraft?.(e),this._pendingDeliveries?.delete(e),this._seqCounters?.delete(e),this._postDraining?.delete(e),this._persistReliableState(),this.ralphStates.delete(e),this.ralphClosedSessions.delete(e),this.projectInsights.delete(e),this.pendingHooks.delete(e),this.tabAlerts.delete(e),this.attachmentHistoryCounts.delete(e),this.attachmentHistoryDrawerOpen&&this.activeSessionId===e&&this.closeAttachmentHistory?.(),this.terminalLoadStates.delete(e),this.clearCountdownTimers(e),this.closeSessionLogViewerWindows(e),this.closeSessionImagePopups(e),this.closeSessionAttachmentCards(e),this.closeSessionSubagentWindows(e,!0);const s=this.idleTimers.get(e);s&&(clearTimeout(s),this.idleTimers.delete(e)),delete this.respawnStatus[e],delete this.respawnTimers[e],delete this.respawnCountdownTimers[e],delete this.respawnActionLogs[e]}async closeSession(e,t=!0){const s=this.activeSessionId===e;this._closingSessions.add(e);try{if(await this._apiDelete(`/api/sessions/${e}?killMux=${t}`),this._cleanupSessionData(e),s){this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}const i=this.sessionOrder.find(n=>n!==e&&this.sessions.has(n));i?this.selectSession(i,{auto:!0}):(this.terminal.clear(),this.showWelcome(),this.renderRalphStatePanel())}this.renderSessionTabs(),t?this.showToast("Session closed and tmux killed","success"):this.showToast("Tab hidden, tmux still running","info")}catch{this.showToast("Failed to close session","error")}finally{this._closingSessions.delete(e)}}requestCloseSession(e){const t=this.sessions.get(e);if(!t)return;this.pendingCloseSessionId=e;const s=this.getSessionName(t),i=document.getElementById("closeConfirmSessionName");i.textContent=s;const n=document.getElementById("closeConfirmKillTitle");n&&(n.textContent=t.mode==="opencode"?"Kill Tmux & OpenCode":t.mode==="codex"?"Kill Tmux & Codex":t.mode==="gemini"?"Kill Tmux & Gemini":t.mode==="antigravity"?"Kill Tmux & Antigravity":t.mode==="pi"?"Kill Tmux & Pi":t.mode==="grok"?"Kill Tmux & Grok":t.mode==="deepseek"?"Kill Tmux & DeepSeek":t.mode==="omp"?"Kill Tmux & OMP":"Kill Tmux & Claude Code"),document.getElementById("closeConfirmModal").classList.add("active")}cancelCloseSession(){this.pendingCloseSessionId=null,document.getElementById("closeConfirmModal").classList.remove("active")}async confirmCloseSession(e=!0){const t=this.pendingCloseSessionId;this.cancelCloseSession(),t&&await this.closeSession(t,e)}nextSession(){if(this.sessionOrder.length<=1)return;const t=(this.sessionOrder.indexOf(this.activeSessionId)+1)%this.sessionOrder.length;this.selectSession(this.sessionOrder[t])}prevSession(){if(this.sessionOrder.length<=1)return;const t=(this.sessionOrder.indexOf(this.activeSessionId)-1+this.sessionOrder.length)%this.sessionOrder.length;this.selectSession(this.sessionOrder[t])}goHome(){this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.terminal.clear(),this.showWelcome(),this.renderSessionTabs(),this.renderRalphStatePanel()}ralphWizardStep=1;ralphWizardConfig={taskDescription:"",completionPhrase:"COMPLETE",maxIterations:10,caseName:"testcase",enableRespawn:!1,generatedPlan:null,planGenerated:!1,skipPlanGeneration:!1,planDetailLevel:"detailed",existingPlan:null,useExistingPlan:!1};planLoadingTimer=null;planLoadingStartTime=null;async killActiveSession(){if(!this.activeSessionId){this.showToast("No active session","warning");return}await this.closeSession(this.activeSessionId)}async killAllSessions(){if(this.sessions.size!==0&&confirm(`Kill all ${this.sessions.size} session(s)?`))try{await this._apiDelete("/api/sessions"),this.sessions.clear(),this.terminalBuffers.clear(),this.terminalBufferCache.clear(),this.terminalLoadStates.clear(),this._xtermSnapshots?.clear();try{for(const e of Object.keys(localStorage))e.startsWith("codeman-xs-")&&localStorage.removeItem(e)}catch{}this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.respawnStatus={},this.respawnCountdownTimers={},this.respawnActionLogs={},this.stopCountdownInterval(),this.hideRespawnBanner(),this.renderSessionTabs(),this.terminal.clear(),this.showWelcome(),this.showToast("All sessions killed","success")}catch{this.showToast("Failed to kill sessions","error")}}showTimer(){document.getElementById("timerBanner").style.display="flex",this.updateTimer(),this.timerInterval=setInterval(()=>this.updateTimer(),1e3)}hideTimer(){document.getElementById("timerBanner").style.display="none",this.timerInterval&&(clearInterval(this.timerInterval),this.timerInterval=null)}updateTimer(){if(!this.currentRun||this.currentRun.status!=="running")return;const e=Date.now(),t=Math.max(0,this.currentRun.endAt-e),s=this.currentRun.endAt-this.currentRun.startedAt,i=e-this.currentRun.startedAt,n=Math.min(100,i/s*100);document.getElementById("timerValue").textContent=this.formatTime(t),document.getElementById("timerProgress").style.width=`${n}%`,document.getElementById("timerMeta").textContent=`${this.currentRun.completedTasks} tasks | $${this.currentRun.totalCost.toFixed(2)}`}async stopCurrentRun(){if(this.currentRun)try{await fetch(`/api/scheduled/${this.currentRun.id}`,{method:"DELETE"})}catch{this.showToast("Failed to stop run","error")}}formatTime(e){const t=Math.floor(e/1e3),s=Math.floor(t/3600),i=Math.floor(t%3600/60),n=t%60;return`${s.toString().padStart(2,"0")}:${i.toString().padStart(2,"0")}:${n.toString().padStart(2,"0")}`}updateCost(){this.updateTokens()}updateTokens(){this._clearTimer("_updateTokensTimeout"),this._updateTokensTimeout=setTimeout(()=>{this._updateTokensTimeout=null,this._updateTokensImmediate()},200)}_updateTokensImmediate(){let e=0,t=0;this.globalStats?(e=this.globalStats.totalInputTokens||0,t=this.globalStats.totalOutputTokens||0):this.sessions.forEach(o=>{o.tokens&&(e+=o.tokens.input||0,t+=o.tokens.output||0)});const s=e+t;this.totalTokens=s;const i=this.formatTokens(s),n=this.estimateCost(e,t),a=this.$("headerTokens");if(a){const r=this.loadAppSettingsFromStorage().showCost??!1;a.textContent=s>0?r?`${i} tokens \xB7 $${n.toFixed(2)}`:`${i} tokens`:"0 tokens",a.title=this.globalStats?`Lifetime: ${this.globalStats.totalSessionsCreated} sessions created${r?`
37
37
  Estimated cost based on Claude Opus pricing`:""}`:`Token usage across active sessions${r?`
38
38
  Estimated cost based on Claude Opus pricing`:""}`}}getShortcutRegistry(){const t=this.loadAppSettingsFromStorage().shortcutOverrides||{};return DEFAULT_SHORTCUTS.map(s=>{const i=t[s.id];if(!i)return s;const n={...s};return Array.isArray(i.bindings)&&(n.bindings=i.bindings,delete n.displayBindings),typeof i.disabled=="boolean"&&(n.disabled=i.disabled),n})}matchesShortcutEvent(e,t){return!t||!Array.isArray(t.bindings)?!1:t.bindings.some(s=>{const i=s.modifiers||[];return(i.includes("ctrl")||i.includes("meta"))!==!!(e.ctrlKey||e.metaKey)||i.includes("shift")!==!!e.shiftKey||i.includes("alt")!==!!e.altKey?!1:!!(s.code&&e.code===s.code||s.key&&typeof e.key=="string"&&e.key.toLowerCase()===s.key.toLowerCase())})}showShortcutOverlay(){const e=document.getElementById("shortcutOverlayModal");e&&(this.renderShortcutOverlay(),e.classList.add("active"),e.focus?.())}renderShortcutOverlay(){const e=document.getElementById("shortcutOverlayList");if(!e)return;const t=this.getShortcutRegistry(),s={};for(const n of t){const a=n.group||"General";s[a]||(s[a]=[]),s[a].push(n)}const i=n=>n.displayBindings?n.displayBindings.map(a=>`<kbd>${escapeHtml(a)}</kbd>`).join(" / "):n.bindings?n.bindings.map(a=>{const o=[...(a.modifiers||[]).map(r=>r.charAt(0).toUpperCase()+r.slice(1)),a.key||a.code||""];return`<kbd>${escapeHtml(o.join("+"))}</kbd>`}).join(" / "):"";e.innerHTML=Object.entries(s).map(([n,a])=>`<div class="shortcut-overlay-group"><div class="shortcut-overlay-group-label">${escapeHtml(n)}</div>`+a.map(o=>`<div class="shortcut-overlay-row"><span class="shortcut-overlay-label">${escapeHtml(o.label)}</span><span class="shortcut-overlay-keys">${i(o)}</span></div>`).join("")+"</div>").join("")}closeShortcutOverlay(){const e=document.getElementById("shortcutOverlayModal");e&&e.classList.remove("active")}}try{for(let f=0;f<localStorage.length;f++){const e=localStorage.key(f);if(e&&(e.startsWith("claudeman-")||e.startsWith("claudeman_"))){const t=e.replace(/^claudeman[-_]/,s=>"codeman"+s.charAt(s.length-1));localStorage.getItem(t)===null&&localStorage.setItem(t,localStorage.getItem(e))}}}catch{}let app;document.addEventListener("DOMContentLoaded",()=>{app=new CodemanApp,window.app=app}),window.MobileDetection=MobileDetection;
Binary file
@@ -1516,8 +1516,57 @@ function terminalLogicalLine(buffer, row, cols, maxRows) {
1516
1516
  return { startRow, endRow, text, offsetToCell, cellToOffset };
1517
1517
  }
1518
1518
 
1519
+ // ═══════════════════════════════════════════════════════════════
1520
+ // Split-Pane Sessions — pure helpers (divider math, picker list)
1521
+ // ═══════════════════════════════════════════════════════════════
1522
+
1523
+ // Desktop-only, same reasoning and same threshold as HOME_SESSIONS_MIN_WIDTH
1524
+ // (home-sessions.js): two 240px min-width panes plus the divider need ~486px,
1525
+ // which a phone or narrow tablet cannot give them, and the divider has no
1526
+ // touch handlers. A dedicated constant rather than reusing
1527
+ // HOME_SESSIONS_MIN_WIDTH directly — that name lives in home-sessions.js,
1528
+ // which loads AFTER this file (load order 12.56 vs 7.5), so referencing it
1529
+ // from module-evaluation-time code here would be a ReferenceError.
1530
+ const SPLIT_PANE_MIN_WIDTH = 1180;
1531
+
1532
+ function clampDividerPercent(rawPercent, min = 20, max = 80) {
1533
+ if (rawPercent < min) return min;
1534
+ if (rawPercent > max) return max;
1535
+ return rawPercent;
1536
+ }
1537
+
1538
+ function buildSplitPickerSessions(sessions, sessionOrder, excludeId, detachedIds) {
1539
+ const result = [];
1540
+ for (const id of sessionOrder) {
1541
+ if (id === excludeId) continue;
1542
+ // A detached (popped-out) session's own window already yields its PTY
1543
+ // size (see sendResize's detachedElsewhere guard in terminal-ui.js) —
1544
+ // Pane B's SplitTerminalPane._sendResize() has no such check, so letting
1545
+ // one into the picker put its detached window and Pane B in a fight over
1546
+ // the same PTY's dimensions.
1547
+ if (detachedIds?.has?.(id)) continue;
1548
+ const session = sessions.get(id);
1549
+ if (!session) continue;
1550
+ // A session with no PTY attached (exited CLI, a crash-looped session
1551
+ // whose breaker tripped, a restore that failed to re-attach) has nothing
1552
+ // reading its tmux pane. SplitTerminalPane never does selectSession()'s
1553
+ // re-attach POST, so its socket would open onto a pane nothing feeds:
1554
+ // no terminal events, and Session.write() silently drops every keystroke
1555
+ // with no ack either way (Pane B sends no `seq`), so the loss is
1556
+ // invisible — the healthy socket never trips the disconnect banner.
1557
+ if (session.pid === null) continue;
1558
+ result.push({ id, label: session.name || 'Session' });
1559
+ }
1560
+ return result;
1561
+ }
1562
+
1519
1563
  if (typeof window !== 'undefined') {
1520
1564
  window.CodemanHistoryFormat = { formatHistoryBytes, computeHistoryTruncationNotice, computeRewriteScrollLine };
1521
1565
  window.CodemanFilePaths = { absoluteFilePathPattern, previewsInFileViewer, FILE_PREVIEW_EXTENSIONS };
1522
1566
  window.CodemanTerminalLines = { terminalLogicalLine };
1567
+ window.CodemanSplitPane = {
1568
+ clampDividerPercent,
1569
+ buildSplitPickerSessions,
1570
+ SPLIT_PANE_MIN_WIDTH,
1571
+ };
1523
1572
  }
Binary file
Binary file
Binary file