diff --git a/periscope/panes.py b/periscope/panes.py index ccfb278a..e58a0ea3 100644 --- a/periscope/panes.py +++ b/periscope/panes.py @@ -368,18 +368,13 @@ def recency_stamps_for(target: str) -> dict: # active-op / prose by the no-space-before-`(` pattern. TOOL_CALL_RE = re.compile(r"^[^\x00-\x7f]\s+\w+\(") -# Tool-result lines start with `⎿`. Claude Code renders rate-limit and -# transport failures as a normal tool result whose body begins with -# "API Error:" and then STOPS — it does not auto-retry; the turn just -# ends with the error sitting there until the user types something -# (`keep going`, etc.) to restart. When the most recent `⎿` line is an -# API Error the pane is silently blocked waiting on a human nudge. A -# later non-error `⎿` means a subsequent turn succeeded and the flag -# should clear. A stream cut mid-response ("The response stopped -# arriving") renders as an assistant `⏺` line instead (THI-436), so both -# glyphs count as the error. +# A tool result (`⎿ …`). An API error is a tool result (rate limit) or an +# assistant `⏺` line (stream cut, THI-436). A collapsed tool group +# (` Called fdy 6 times (ctrl+o to expand)`) prints no `⎿` (THI-455); its +# capital excludes a result's `… +3 lines` row and `› Message from @…`. TOOL_RESULT_RE = re.compile(r"^\s*⎿\s+") API_ERROR_RE = re.compile(r"^\s*[⎿⏺]\s+API Error:") +COLLAPSED_TOOL_GROUP_RE = re.compile(r"^\s+[A-Z]\w* .*\(ctrl\+o to expand\)\s*$") def all_pane_ids() -> set[str]: @@ -1046,15 +1041,13 @@ def _detect_asked_question(lines: list[str]) -> bool: def _detect_api_error(lines: list[str]) -> bool: - """True when the most recent `⎿` tool-result line is an API Error, - or a `⏺ API Error:` line sits below it. + """True when Claude has written nothing since the newest API Error + line: no `⎿` tool result, `⏺` line or collapsed tool group below it. - Claude Code renders rate-limit / transport failures as a normal tool - result whose body begins "API Error:" (or, for a stream cut, as an - assistant `⏺ API Error:` line) and then STOPS — no auto-retry; - the turn just ends until the user nudges it (`keep going`, etc.). The - flag tracks "pane is silently blocked". A later non-error `⎿` means a - subsequent turn succeeded and the flag clears. + A rate-limit error ends the turn until the user nudges it (`keep + going`); after a stream cut Claude Code may carry on by itself. Either + way, output below the error means the API answered and the pane isn't + blocked. Caller invokes this only for Claude panes — prose mentioning "API Error" in a shell pane (logs, grep output) must not trip it. @@ -1062,7 +1055,7 @@ def _detect_api_error(lines: list[str]) -> bool: for line in reversed(lines): if API_ERROR_RE.match(line): return True - if TOOL_RESULT_RE.match(line): + if TOOL_RESULT_RE.match(line) or COLLAPSED_TOOL_GROUP_RE.match(line) or line.lstrip().startswith("⏺"): return False return False diff --git a/static/dist/app.js b/static/dist/app.js index 3bbcee6a..43c99170 100644 --- a/static/dist/app.js +++ b/static/dist/app.js @@ -34,7 +34,7 @@ ${n}`:w?"updating — periscope will restart itself":$?`${g}, but ${v}`:`${g} `),n=String(e.new_string||"").split(` `);return r("div",{class:"tc-diff",children:[t.map((i,l)=>r("div",{class:"tc-diff-del",children:i||" "},`d${l}`)),n.map((i,l)=>r("div",{class:"tc-diff-add",children:i||" "},`a${l}`))]})}function Rw(e){const t=e.input||{};switch(e.name){case"Bash":return t.command||"";case"Write":return t.content||"";default:return""}}function Fw({input:e}){const t=e.questions||[];return r("div",{class:"tc-ask",children:t.map((n,i)=>r("div",{class:"tc-ask-q",children:[r("div",{class:"tc-ask-qhead",children:[n.header&&r("span",{class:"tc-ask-chip",children:n.header}),n.multiSelect&&r("span",{class:"tc-ask-multi",children:"multi-select"})]}),r("div",{class:"tc-ask-question",children:n.question}),r("div",{class:"tc-ask-options",children:(n.options||[]).map((l,a)=>r("div",{class:"tc-ask-opt",children:[r("span",{class:"tc-ask-opt-label",children:l.label}),l.description&&r("span",{class:"tc-ask-opt-desc",children:l.description})]},a))})]},i))})}function Nw({t:e}){var f;const[t,n]=B(!1),i=e.result==null,l=e.name==="Edit",a=e.name==="AskUserQuestion",s=e.name==="Agent"||e.name==="Task",o=Rw(e),u=!i&&e.result!=null&&e.result!=="",c=l||a||!!o||u,p=Ow(e),d=s&&((f=e.input)==null?void 0:f.subagent_type)||"";return r("div",{class:`tc${s?" tc-agent":""}`,children:[r("button",{class:`tc-head${c?" expandable":""}`,onClick:c?()=>n(!t):void 0,children:[r("span",{class:"tc-dot",children:s?"⚇":"⏺"}),r("span",{class:"tc-name",children:e.name}),d&&r("span",{class:"tc-agent-type",children:d}),p&&(e.name==="Read"||e.name==="Edit"||e.name==="Write"||e.name==="MultiEdit"||e.name==="NotebookEdit"?r("span",{class:"tc-arg tc-arg-clickable",title:"Cmd+click to preview",onClick:m=>{!m.metaKey&&!m.ctrlKey||(m.stopPropagation(),kr({path:p,line:null}))},children:p}):r("span",{class:"tc-arg",children:p})),i&&r("span",{class:"tc-running",children:"running…"}),c&&r("span",{class:"tc-caret",children:t?"▾":"▸"})]}),t&&r("div",{class:"tc-detail",children:[l?r(Dw,{input:e.input||{}}):a?r(Fw,{input:e.input||{}}):o&&r("pre",{class:"tc-cmd",children:o}),u&&r("pre",{class:"tc-out",children:e.result})]})]})}function Bw({m:e,current:t}){if(e.role==="user")return r("div",{class:`turn turn-user${t?" turn-current":""}`,children:[r("span",{class:"turn-user-mark",children:"›"}),r("div",{class:"turn-user-body",children:mr(e.text)})]});const n=e.tool_uses||[];return r("div",{class:`turn turn-assistant${t?" turn-current":""}`,children:[e.text&&r("div",{class:"turn-prose",children:mr(e.text)}),n.length>0&&r("div",{class:"turn-tools",children:n.map((i,l)=>r(Nw,{t:i},i.id||l))})]})}function zw({target:e,composerRef:t}){const[n,i]=B(""),[l,a]=B(!1),s=te(null);async function o(){const d=n;if(!d.trim()||l)return;a(!0),await ie("send",`/api/send?${tt(e)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paste:d})})&&(await new Promise(h=>setTimeout(h,250)),await ie("send",`/api/send?${tt(e)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({keys:["Enter"]})}),i(""),s.current&&(s.current.style.height="")),a(!1)}function u(d){d.key==="Enter"&&!d.shiftKey&&(d.preventDefault(),o())}function c(d){const f=d.currentTarget;i(f.value),f.style.height="0px",f.style.height=`${Math.min(f.scrollHeight,140)}px`}async function p(d){var h;const f=((h=d.clipboardData)==null?void 0:h.items)||[];for(const m of f){if(m.kind!=="file"||!m.type.startsWith("image/"))continue;const b=m.getAsFile();if(!b)continue;d.preventDefault();const w=await(await fetch(`/api/paste-image?${tt(e)}&deliver=false`,{method:"POST",headers:{"Content-Type":b.type||"image/png"},body:b})).json().catch(()=>({}));w.ok&&i(C=>`${C}${C&&!C.endsWith(" ")?" ":""}@${w.path} `);return}}return r("div",{class:"transcript-composer",ref:t,children:r("textarea",{ref:s,class:"transcript-composer-input",placeholder:"Message Claude… (Enter to send · Shift+Enter for newline · paste screenshots)",value:n,rows:1,onInput:c,onKeyDown:u,onPaste:p})})}function jw({target:e,pid:t,selected:n,state:i,waitingFor:l,spinner:a}){var m;Iw(e,t,n);const s=((m=Ft.value[t])==null?void 0:m.messages)||[],o=te(null),u=te(null),c=te(!0),p=te(""),d=()=>{const b=o.current;b&&c.current&&(b.scrollTop=b.scrollHeight)};j(()=>{const b=s[s.length-1],x=`${s.length}:${b?b.uuid:""}:${b?(b.tool_uses||[]).length:0}`;x!==p.current&&(p.current=x,d())},[s]),j(()=>{const b=u.current,x=o.current;if(!b||!x)return;const w=()=>{x.style.paddingBottom=`${b.offsetHeight+14}px`,requestAnimationFrame(d)};w();const C=new ResizeObserver(w);return C.observe(b),()=>C.disconnect()},[]),j(()=>{(i==="needs-input"||i==="working")&&d()},[i]);function f(b){const x=b.currentTarget;c.current=x.scrollHeight-x.scrollTop-x.clientHeight<80}let h="";for(let b=s.length-1;b>=0;b--)if(s[b].role!=="system"){h=s[b].uuid;break}return r(K,{children:[r("div",{class:"transcript",ref:o,onScroll:f,children:[s.length===0?r("div",{class:"transcript-empty",children:"No transcript yet."}):s.map(b=>b.role==="system"&&b.kind==="compact"?r("div",{class:"transcript-compact",children:r("span",{children:"context compacted"})},b.uuid):r(Bw,{m:b,current:b.uuid===h},b.uuid)),i==="needs-input"&&r("div",{class:"transcript-status transcript-status-waiting",children:[r("span",{class:"transcript-status-dot"}),r("span",{class:"transcript-status-text",children:["Waiting for your input",l?` — ${l}`:""]})]}),i==="working"&&r("div",{class:"transcript-working",children:[r("span",{class:"transcript-working-dot"}),r("span",{class:"transcript-working-verb",children:a?`${a.toLowerCase()}…`:"working…"})]})]}),r(zw,{target:e,composerRef:u})]})}const Hw=1500;function qw(e){const t=e.replace(/\/+$/,""),n=t.lastIndexOf("/");return n>=0?t.slice(n+1):t}function Uw({mode:e,onMode:t}){return r("span",{class:"detail-mode-toggle",children:[r("button",{class:e==="transcript"?"is-active":"",onClick:()=>t("transcript"),children:"Transcript"}),r("button",{class:e==="terminal"?"is-active":"",onClick:()=>t("terminal"),children:"Terminal"})]})}function Ww({pid:e,paneLabel:t}){const n=Ne.value[e]||[],i=Re.value[e]||"pane";return r("div",{class:"detail-tab-strip",role:"tablist",children:[r("button",{type:"button",role:"tab",class:`detail-tab${i==="pane"?" is-active":""}`,"aria-selected":i==="pane",onClick:()=>jr(e,"pane"),title:t,children:r("span",{class:"detail-tab-label",children:t})}),r("button",{type:"button",role:"tab",class:`detail-tab${i==="changes"?" is-active":""}`,"aria-selected":i==="changes",onClick:()=>jr(e,"changes"),title:"Changes in this worktree",children:r("span",{class:"detail-tab-label",children:"Changes"})}),n.map(l=>{const a=`file:${l.path}`,s=i===a;return r("button",{type:"button",role:"tab",class:`detail-tab${s?" is-active":""}`,"aria-selected":s,onClick:()=>jr(e,a),title:l.path,children:[r("span",{class:"detail-tab-label",children:qw(l.path)}),r("span",{class:"detail-tab-close",title:"Close tab",onClick:o=>{o.stopPropagation(),Mc(e,l.path)},children:"×"})]},a)})]})}function Go(e){return(ce.value||[]).find(t=>t.pid===e)||null}function Qd(e){return(e==null?void 0:e.agent)!=="claude"?"terminal":yu(e.pid)||"terminal"}async function Kw(e){var n;if(!rr.value)return;const t=((n=e.clipboardData)==null?void 0:n.items)||[];for(const i of t){if(i.kind!=="file"||!i.type.startsWith("image/"))continue;const l=i.getAsFile();if(l){e.preventDefault(),e.stopPropagation();try{const s=await(await fetch(`/api/paste-image?${tt(rr.value)}`,{method:"POST",headers:{"Content-Type":l.type||"image/png"},body:l})).json();s.ok||Vs(`\r \x1B[31m[periscope: image paste failed: ${s.error}]\x1B[0m`)}catch(a){Vs(`\r -\x1B[31m[periscope: image paste error: ${a.message}]\x1B[0m`)}return}}}function Vw({w:e}){const t=!!e.spawned_by&&(ce.value||[]).some(l=>l.pid===e.spawned_by),n=[];if(e.session&&e.session!==e.branch&&n.push(r("span",{children:r("b",{children:e.session})})),e.cwd){const l=e.cwd.split("/").filter(Boolean),a=l[l.length-1]||"",s=e.branch?e.branch.replace(/[/\\]/g,"-"):null;let o;s&&a===s&&l.length>=2?o=l[l.length-2]:o=l.length>=2?l.slice(-2).join("/"):l[0]||e.cwd,o&&n.push(r(K,{children:[n.length>0&&r("span",{class:"hsep",children:"·"}),r("span",{class:"header-cwd-reveal",title:`Reveal ${e.cwd} in Finder`,onClick:async()=>{try{await fetch(`/api/fs/open?session=${encodeURIComponent(e.session)}&index=${e.index}&path=.&action=reveal`,{method:"POST"})}catch{}},children:o})]}))}if(e.branch&&n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("span",{children:e.branch})]})),e.pr){const l=lr(e.repo_slug,e.pr),a=Ui(e.pr_state),s=e.ci==="✓"?"ci-ok":e.ci==="✗"?"ci-bad":e.ci==="⟳"?"ci-running":"",o=e.ci&&!a.cls?r(K,{children:[" ",r("span",{class:`header-ci ${s}`,children:e.ci})]}):null,u=r(K,{children:["#",String(e.pr),a.suffix?r("span",{class:"header-pr-state",children:a.suffix}):null,o]}),c=l?r("a",{class:`header-pr ${a.cls}`,href:l,target:"_blank",rel:"noopener",children:u}):r("span",{class:`header-pr ${a.cls}`,children:u});n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),c]}))}const i=vr(e);if(i&&n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("a",{class:"header-linear",href:i.href,target:"_blank",rel:"noopener",title:i.tip,children:i.id})]})),e.git&&e.git!=="clean"&&n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("span",{class:"header-git",children:e.git})]})),e.agent==="claude"?n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r(Mw,{w:e})]})):e.agent&&e.model&&n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("span",{children:e.model.replace(/\s*\(.*\)/,"")})]})),e.agent&&e.context_pct!=null&&n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("span",{children:[e.context_pct,"%"]})]})),e.agent==="claude"&&e.mem){const l=fu(e.mem);n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("span",{class:`header-mem ${l.cls}`,title:l.title,children:["↻ ",l.label]})]}))}return e.api_error&&n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("span",{class:"header-api-error",title:"last tool result was an API error",children:"⚠ API error"})]})),e.res&&n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("span",{class:"header-res",title:"this pane's whole process tree: memory footprint, and its share of the machine's CPU (100% = every core busy)",children:`${cn(e.res.mem_mb)} mem · ${Au(e.res.cpu_pct)} cpu`})]})),r("header",{id:"detail-pane-header",class:"detail-pane-header","data-agent":e.agent||void 0,children:[n.map((l,a)=>r(K,{children:l})),e.spawner_name&&r("span",{class:`header-lineage${t?"":" lineage-gone"}`,title:t?`spawned by ${e.spawner_name} — click to reveal`:`spawned by ${e.spawner_name}, which is no longer running`,onClick:t?()=>{ke.value=`pane:${e.spawned_by}`,yt({kind:"pane",pid:e.spawned_by})}:void 0,children:["↳ launched by ",e.spawner_name]}),e.status_line&&r("span",{class:"header-status",title:e.status_at?`as of ${Pe(e.status_at)} ago`:void 0,children:e.status_line})]})}function Gw({target:e}){const[t,n]=B(null);return j(()=>{if(!e)return;let i=!0;async function l(){try{const s=await fetch(`/api/pane?${tt(e)}&lines=80`);if(!s.ok)return;const o=await s.json();i&&n(o)}catch{}}l();const a=setInterval(l,Hw);return()=>{i=!1,clearInterval(a)}},[e]),t?r(dw,{data:t,onRefresh:()=>{},containerId:"detail-side",containerClass:"detail-side",idPrefix:"detail"}):r("aside",{id:"detail-side",class:"detail-side"})}function Jw({w:e}){const t=Qd(e);j(()=>{rr.value=e.target},[e.target]),j(()=>{e.pane_id&&(e.channel_unread||0)>0&&fetch(`/api/channel/clear-unread?pane_id=${encodeURIComponent(e.pane_id)}`,{method:"POST"})},[e.pane_id,e.channel_unread]),j(()=>(qs(c=>{let p=c,d=null;const f=p.match(/^(.*?):(\d+)$/);f&&(p=f[1],d=f[2]),kr({path:p,line:d})}),()=>qs(null)),[e.target]),j(()=>{const c=document.getElementById("detail-pane-header"),p=document.getElementById("detail-tab-bar"),d=document.getElementById("detail");if(!c||!d)return;const f=()=>d.style.setProperty("--detail-header-h",`${c.offsetHeight+((p==null?void 0:p.offsetHeight)||0)}px`);f();const h=new ResizeObserver(f);return h.observe(c),p&&h.observe(p),()=>h.disconnect()},[e.target]),j(()=>{if(t!=="terminal")return;const c=requestAnimationFrame(()=>requestAnimationFrame(vi));return()=>cancelAnimationFrame(c)},[t]);const[n,i]=B(!0);j(()=>{if(t!=="terminal")return;const c=()=>i(D1());c();const p=setInterval(c,250);return()=>clearInterval(p)},[t,e.pid]);const l=te(e.state);j(()=>{const c=l.current==="working",p=e.state==="idle"||e.state==="done";if(c&&p&&e.agent){const d=document.getElementById("detail-xterm");d&&(d.classList.add("bell-pulse"),setTimeout(()=>d.classList.remove("bell-pulse"),400))}l.current=e.state},[e.state,e.agent]);const a=Ne.value[e.pid]||[],s=Re.value[e.pid]||"pane",o=s==="pane",u=e.track_name||_e(e);return r("div",{id:"detail-pane",class:"detail-pane",children:[r(Vw,{w:e}),r("div",{id:"detail-tab-bar",class:"detail-tab-bar",children:[r(Ww,{pid:e.pid,paneLabel:u}),e.agent==="claude"&&r(Uw,{mode:t,onMode:c=>{ye("view.switch",{view:c}),Ul(e.pid,c)}})]}),r("div",{class:"detail-pane-body",children:[r("div",{class:"detail-term-host",style:o&&t==="terminal"?"display:contents":"display:none",children:r(_w,{id:"detail-xterm",class:"detail-xterm",target:e.pane_id,agent:e.agent,onPaste:Kw},e.pid)}),r("div",{class:"detail-changes-host",style:s==="changes"?"display:contents":"display:none",children:r(Kk,{target:e.target,active:s==="changes",gitSig:e.git||""})}),a.map(c=>{const p=`file:${c.path}`,d=s===p;return r("div",{class:"detail-preview-host",style:d?"display:contents":"display:none",children:r(fw,{entry:c,active:d})},p)}),!bi.value&&r(Gw,{target:e.target},e.target),!bi.value&&r(Jd,{which:"side"})]}),o&&t==="terminal"&&!n&&r("button",{class:"term-scroll-bottom",title:"Scroll to latest",onClick:vi,children:"⤓"}),o&&t==="terminal"&&r(xw,{})]})}function $l(){return r("div",{id:"detail-empty",class:"detail-empty",children:r("div",{class:"detail-empty-card",children:r("p",{children:["Select a tab on the left, or"," ",r("button",{id:"detail-empty-add",onClick:()=>{var e;return(e=window.__periscopeOpenOmnibox)==null?void 0:e.call(window)},children:"+ open"})," ","to add one."]})})})}function Qw(){var d,f;const e=ke.value,t=ce.value||[],n=!!e&&e.startsWith("pane:"),i=!!e&&e.startsWith("review:"),l=e==="reviews";let a=null;if(i){const[h,m]=(()=>{const b=e.slice(7),x=b.indexOf("/");return x<0?[b,""]:[b.slice(0,x),b.slice(x+1)]})();a=(gt.value||[]).find(b=>b.repo===h&&b.slug===m)||null}j(()=>{(i||l)&&(rr.value=null)},[e]);const s=n?Go(e.slice(5)):null,o=te(new Set);n&&(s==null?void 0:s.agent)==="claude"&&o.current.add(s.pid);const u=new Set(t.map(h=>h.pid)),c=Qd(s);for(const h of[...o.current])if(!(n&&(s==null?void 0:s.pid)===h)&&!u.has(h)&&(o.current.delete(h),Ft.value[h])){const{[h]:b,...x}=Ft.value;Ft.value=x}const p=[...o.current];return r("section",{id:"detail",children:[!e&&r($l,{}),n&&(s?r(Jw,{w:s}):r($l,{})),l&&r(yw,{}),i&&(gt.value===null?r("div",{class:"wt-empty",children:"loading…"}):a?r(vw,{repo:a.repo,branch:a.branch,pr:a.pr,sig:`${(d=a.walkthrough)==null?void 0:d.head}|${(f=a.walkthrough)==null?void 0:f.stale_by}|${a.unresolved}|${a.comments}`},`${a.repo}/${a.branch}`):r($l,{})),p.map(h=>{const m=Go(h),b=n&&(s==null?void 0:s.pid)===h,x=(Re.value[h]||"pane")==="pane";return r("div",{class:"detail-transcript-host",style:b&&x&&c==="transcript"?"display:flex":"display:none",children:r(jw,{target:m==null?void 0:m.target,pid:h,selected:b,state:m==null?void 0:m.state,waitingFor:m==null?void 0:m.waiting_for,spinner:m==null?void 0:m.spinner})},`tr:${h}`)})]})}function Cl(e,t){return t==="all"?!0:t==="needs-input"?e.state==="needs-input":t==="working"?e.state==="working":t==="done"?e.state==="done":t==="idle"?e.state==="idle":t==="agents"?!!e.agent:t==="shell"?e.state==="shell":t==="ci-bad"?e.ci==="✗":!0}const ia=H(!1);function Tl(e,t,n,i=l=>l.key){const l=e||[];if(!n||!t)return l;const a=new Map(l.map(s=>[i(s),s]));return t.map(s=>a.get(i(s))||s)}function El(e,t,n,i=l=>l.key){if(!n||!t)return!1;const l=(e||[]).map(i),a=t.map(i);if(l.length!==a.length)return!0;const s=new Set(a);return l.some(o=>!s.has(o))}function Yw(e,t){return!!t&&!!(e!=null&&e.branch)}function Zw(e,t){return`open ${e!=null&&e.branch?`${e.branch} `:""}worktree in ${t}`}const Xw=900;function Xa(e){return e==="needs-input"?"dot dot-alert dot-pulse":e==="working"?"dot dot-green":e==="done"?"dot dot-blue dot-pulse-done":e==="idle"?"dot dot-grey":"dot dot-none"}function ev(e){return e==="✓"?"ok":e==="✗"?"bad":e==="⟳"?"running":"neutral"}function tv(e){return e?e.replace(/\s*\(.*$/,"").trim():null}function nv(e){return`${Math.round(e/1e3)}k`}function Nn(e){return e.context_pct!=null?`${e.context_pct}%`:e.ctx_tokens!=null?nv(e.ctx_tokens):null}function Yd({label:e,kind:t,renameable:n,onCommit:i}){const[l,a]=B(!1),s=te(null),o=te(!1);j(()=>{l&&s.current&&(s.current.focus(),s.current.select())},[l]);function u(d){n&&(d.preventDefault(),d.stopPropagation(),o.current=!1,a(!0))}function c(d){if(o.current)return;o.current=!0,a(!1);const f=(d||"").trim();f&&f!==e&&i(f)}function p(){o.current||(o.current=!0,a(!1))}return l?r("span",{class:"rail-label",children:r("input",{ref:s,type:"text",class:"rail-rename-input",defaultValue:e,spellcheck:!1,onClick:d=>d.stopPropagation(),onKeyDown:d=>{d.stopPropagation(),d.key==="Enter"?(d.preventDefault(),c(d.currentTarget.value)):d.key==="Escape"&&(d.preventDefault(),p())},onBlur:d=>c(d.currentTarget.value)})}):r("span",{class:"rail-label",onDblClick:n?u:void 0,children:t==="pane"?e:r("b",{children:e})})}function rv({w:e,chip:t,selectedKey:n,onSelect:i,onClose:l,onRename:a,dim:s,dragProps:o,dropPos:u,pinned:c,onTogglePin:p,awaitingSince:d,onMoveAccount:f,onToggleNamePin:h,editor:m,onOpenInEditor:b}){const x=`pane:${e.pid}`,w=x===n,C=w||e.state==="needs-input",y=e.agent?e.state||"idle":"shell",g=s?"":" rail-dim",$=u?" drop-target":"",v=_e(e),E=e.agent||(e.is_claude?"claude":null),M=ql[E],T=e.status_rail||e.status_line,S=e.status_at&&Math.floor(Date.now()/1e3)-e.status_at>Xw,R=tv(e.model),F=e.pr?lr(e.repo_slug,e.pr):null,q=fu(e.mem),L=d?vu(d,Math.floor(Date.now()/1e3)):null,I=Ch(e),O=Ui(e.pr_state),Q=`PR #${e.pr}${O.suffix}`,A=r(K,{children:["#",e.pr,e.ci&&!O.cls?r("span",{class:`pane-ci pane-ci-${ev(e.ci)}`,children:[" ",e.ci]}):null]}),z=e.pr&&(F?r("a",{class:`pane-pill pane-pill-pr ${O.cls}`,href:F,target:"_blank",rel:"noopener",onClick:X=>X.stopPropagation(),title:Q,children:A}):r("span",{class:`pane-pill pane-pill-pr ${O.cls}`,title:Q,children:A})),k=vr(e),W=k&&r("a",{class:"pane-pill pane-pill-linear",href:k.href,target:"_blank",rel:"noopener",onClick:X=>X.stopPropagation(),title:k.tip,children:k.id}),Z=e.account&&e.account!=="default"&&($n()||e.account==="unknown")&&r("span",{class:"pane-pill pane-pill-acct",title:`running on Claude account ${yn(e.account)} — its usage bills that subscription`,children:["@",yn(e.account)]}),_=e.profile&&e.profile!=="default"&&r("span",{class:"rail-profile",title:`running the ${To(e.profile)} profile — a different plugin set and system prompt than a normal pane`,children:To(e.profile)});return r("div",{class:`rail-row child-row pane-row pane-state-${y}${C?" pane-expanded":""}${w?" selected":""}${g}${$}`,"data-drop-pos":u||void 0,draggable:!0,onClick:()=>i(x),...o,children:[r("span",{class:"pane-stripe","aria-hidden":"true"}),r("div",{class:"pane-row-main",children:[M?r("span",{class:`rail-icon ${M.className}`,title:M.label,children:M.glyph}):r("span",{class:"rail-icon icon-shell",children:"$"}),r(Yd,{label:v,kind:"pane",renameable:!0,onCommit:a}),C?R&&r("span",{class:"pane-model",title:e.model,children:R}):Nn(e)!=null&&r("span",{class:`pane-mini-ctx${Vl(e)}`,title:e.ctx_hint||"context window used",children:Nn(e)}),q&&r("span",{class:`rail-mem ${q.cls}`,title:q.title,children:["↻",q.label]}),L&&r("span",{class:`rail-await wait-${L}`,title:`asked you something ${Pe(d)} ago and has had no reply since`,children:["⚠",Pe(d)]}),c&&r("span",{class:"pane-pin-flag",title:"pinned",children:"★"})]}),(t||T||!C&&(z||W||Z||_))&&r("div",{class:`rail-meta${S?" stale":""}`,children:[t&&r("span",{class:"rail-chip",title:e.cwd,children:["⧉ ",t]}),T&&r("span",{class:"rail-status",title:e.status_line,children:T}),!C&&(z||W||Z||_)&&r("span",{class:"rail-chips",children:[z,W,Z,_]})]}),C&&(e.pr||k||Nn(e)!=null||Z||_)&&r("div",{class:"pane-foot",children:[z,W,r("span",{class:"pane-foot-spacer"}),Z,_,Nn(e)!=null&&r("span",{class:`pane-pill pane-pill-ctx${Vl(e)}`,title:e.ctx_hint||"context window used",children:Nn(e)}),e.status_at?r("span",{class:"pane-time",children:Pe(e.status_at)}):null]}),r("div",{class:"pane-actions",children:[I&&r("button",{class:"rail-move-acct",title:`re-open this session on Claude account ${I.label} — spawns a resumed pane there and leaves this one running`,onClick:X=>{X.stopPropagation(),f==null||f(I.id)},children:["→ ",I.label]}),r("button",{class:`rail-pin${c?" pinned":""}`,title:c?"unpin":"pin",onClick:X=>{X.stopPropagation(),p==null||p()},children:c?"★":"☆"}),r("button",{class:`rail-name-pin${e.name_pinned?" pinned":""}`,title:e.name_pinned?"name pinned — click to let periscope rename this tab again":"freeze this name — periscope won't rename this tab",onClick:X=>{X.stopPropagation(),h==null||h()},children:e.name_pinned?"🔒":"🔓"}),Yw(e,m)&&r("button",{class:"rail-editor",title:Zw(e,m),onClick:X=>{X.stopPropagation(),b==null||b()},children:"↗"}),r("button",{class:"rail-close",title:"kill this tab",onClick:X=>{X.stopPropagation(),l()},children:"×"})]})]})}function iv({worktreeKey:e,onOpen:t}){return r("div",{class:"rail-row child-row newtab-row last-in-worktree",onClick:()=>t(e),children:[r("span",{class:"rail-icon",children:"+"}),r("span",{class:"rail-label",children:"New tab"})]})}function lv({label:e,collapsed:t,childCount:n,rolledUp:i,dim:l,onToggle:a,dragProps:s,dropPos:o}){return r("div",{class:`rail-row wt-row${l?"":" rail-dim"}${o?" drop-target":""}`,"data-drop-pos":o||void 0,draggable:!0,onClick:d=>{d.target.closest("button")||d.target.closest("input")||a()},...s,children:[r("span",{class:"rail-chev",children:t?"▸":"▾"}),r("span",{class:"rail-icon icon-worktree",children:"⎇"}),r("span",{class:"rail-label",children:r("b",{children:e})}),t&&n>0?r("span",{class:"rail-count",children:n}):null,r("span",{class:Xa(i)})]})}function Jo({row:e,selectedKey:t,onSelect:n}){var s;const i=`review:${e.repo}/${e.slug}`,l=i===t?" selected":"",a=((s=e.walkthrough)==null?void 0:s.stale_by)>0;return r("div",{class:`rail-row child-row rail-review${l}`,onClick:()=>n(i),children:[r("span",{class:"rail-icon icon-review",children:"◉"}),r("span",{class:"rail-label",children:["review · ",e.walkthrough.stops," stop",e.walkthrough.stops===1?"":"s",a?" · stale":""]}),e.unresolved?r("span",{class:"rail-review-badge",children:e.unresolved}):null]})}function av({kind:e,label:t,collapsed:n,rolledUp:i,dim:l,onToggle:a,onRename:s,onDissolve:o,onTeardown:u,dragProps:c,dropPos:p}){const d=n?"▸":"▾",f=l?"":" rail-dim",h=p?" drop-target":"",[m,b]=B(!1),x=e==="loose"||e==="repo",w=e==="goal"?"⧉":"🗀";return j(()=>{if(!m)return;const C=g=>{g.target.closest(".track-menu")||b(!1)},y=g=>{g.key==="Escape"&&b(!1)};return document.addEventListener("click",C,!0),document.addEventListener("keydown",y),()=>{document.removeEventListener("click",C,!0),document.removeEventListener("keydown",y)}},[m]),r("div",{class:`rail-row repo-row${f}${h}`,"data-drop-pos":p||void 0,draggable:!0,onClick:C=>{C.target.closest("button")||C.target.closest("input")||a()},...c,children:[r("span",{class:"rail-chev",children:d}),r("span",{class:"rail-icon icon-effort",title:e==="goal"?"track":"project",children:w}),r(Yd,{label:t,kind:"track",renameable:e!=="loose",onCommit:s}),r("span",{class:"repo-rule","aria-hidden":"true"}),r("span",{class:Xa(i)}),!x&&r("span",{class:"track-menu",children:[r("button",{class:"rail-track-menu-btn",title:"track actions",onClick:C=>{C.stopPropagation(),b(y=>!y)},children:"⋯"}),m&&r("div",{class:"track-menu-pop",onClick:C=>C.stopPropagation(),children:[r("button",{class:"track-menu-item",onClick:()=>{b(!1),o()},children:["Dissolve ",r("span",{class:"track-menu-hint",children:"tabs survive"})]}),r("button",{class:"track-menu-item track-menu-danger",onClick:()=>{b(!1),u()},children:["Tear down ",r("span",{class:"track-menu-hint",children:"kills tabs"})]})]})]})]})}function an({icon:e,label:t,count:n,collapsed:i,onToggle:l,tone:a}){return r("div",{class:`rail-row section-header${a==="alert"?" section-header-alert":a==="ready"?" section-header-ready":a==="working"?" section-header-working":""}`,onClick:l,children:[r("span",{class:"rail-chev",children:i?"▸":"▾"}),e?r("span",{class:"section-header-icon",children:e}):null,r("span",{class:"section-header-label",children:t}),n!=null&&n>0?r("span",{class:"section-header-count",children:n}):null]})}function $t(e,t,n,i){const l=(e==null?void 0:e.session)||t||"",a=_e(e)||n||"";return`${i?i(l):l} · ${a}`}function Zd(e){const t=[...new Set((e||[]).map(n=>n.session).filter(Boolean))];return n=>hu(n,t)}function Hn(e,t){mu(e,!t)}function Zt(e){return`${e.kind}:${e.pid??e.id}`}function sv(){var M,T,S,R,F,q;me.value;const e=ce.value||[],t=Da.value||[],n=ir.value,i=ft.value,l=sr(),a=Zd(e),s=La(e,t,n,i),o=l["sec:needs"]===!0,u=wu(e,t,n,pt.value),c=l["sec:ready"]===!0,p=Su(e),d=l["sec:running"]===!0,f=c1(Ea(),e),h=l["sec:pinned"]===!0,m=ia.value,b=te(null),x=Tl(s,(M=b.current)==null?void 0:M.needs,m,Zt),w=Tl(u,(T=b.current)==null?void 0:T.ready,m,Zt),C=Tl(p,(S=b.current)==null?void 0:S.running,m,Zt),y=El(s,(R=b.current)==null?void 0:R.needs,m,Zt)||El(u,(F=b.current)==null?void 0:F.ready,m,Zt)||El(p,(q=b.current)==null?void 0:q.running,m,Zt);j(()=>{m||(b.current={needs:s,ready:u,running:p})});function g(){b.current=null}j(()=>{const L=Bs(ft.value,e,"needs-input");L.size!==ft.value.size&&(ft.value=L);const I=Bs(pt.value,e,"done");I.size!==pt.value.size&&(pt.value=I)},[ce.value]);function $(L){g();const I=new Set(n);I.add(L),ir.value=I}function v(L){g(),$u(L)}function E(L){g(),Tu(L)}return r(K,{children:[y&&r("div",{class:"attn-frozen",title:"Move the pointer out of the rail to apply",children:"updates paused"}),x.length>0&&r(K,{children:[r(an,{icon:"⚠",label:"NEEDS YOU",tone:"alert",count:a1(x),collapsed:o,onToggle:()=>Hn("sec:needs",o)}),!o&&x.map(L=>{var I;return L.kind==="live"?r("div",{class:"rail-row attn-row attn-needs",onClick:()=>v(L.w),children:[r("span",{class:"attn-dot dot dot-alert dot-pulse"}),r("span",{class:"attn-label",children:$t(L.w,null,null,a)}),r("span",{class:"attn-reason",children:Mg((I=L.w)==null?void 0:I.waiting_for)})]},`live:${L.pid}`):r("div",{class:"rail-row attn-row attn-needs attn-event",onClick:()=>jt(L.target),children:[r("span",{class:"attn-ico",children:"⚠"}),r("span",{class:"attn-label",children:$t(L.w,L.session,L.name,a)}),r("span",{class:"attn-reason",children:["need_human · ",Pe(L.ts)]}),r("button",{class:"attn-x",title:"dismiss",onClick:O=>{O.stopPropagation(),$(L.id)},children:"×"})]},`evt:${L.id}`)})]}),w.length>0&&r(K,{children:[r(an,{icon:"✓",label:"READY",tone:"ready",count:w.length,collapsed:c,onToggle:()=>Hn("sec:ready",c)}),!c&&w.map(L=>{var I;return L.kind==="live"?r("div",{class:"rail-row attn-row attn-ready",onClick:()=>E(L.w),children:[r("span",{class:"attn-dot dot dot-blue dot-pulse-done"}),r("span",{class:"attn-label",children:$t(L.w,null,null,a)}),r("span",{class:"attn-reason",children:Pe((I=L.w)==null?void 0:I.completed_at)})]},`rlive:${L.pid}`):r("div",{class:"rail-row attn-row attn-ready attn-event",onClick:()=>jt(L.target),children:[r("span",{class:"attn-ico",children:"✓"}),r("span",{class:"attn-label",children:$t(L.w,L.session,L.name,a)}),r("span",{class:"attn-reason",children:["done · ",Pe(L.ts)]}),r("button",{class:"attn-x",title:"dismiss",onClick:O=>{O.stopPropagation(),$(L.id)},children:"×"})]},`revt:${L.id}`)})]}),C.length>0&&r(K,{children:[r(an,{icon:"⟳",label:"RUNNING",tone:"working",count:C.length,collapsed:d,onToggle:()=>Hn("sec:running",d)}),!d&&C.map(L=>{var I;return r("div",{class:"rail-row attn-row attn-running",onClick:()=>at(L.w),children:[r("span",{class:"attn-dot dot dot-green"}),r("span",{class:"attn-label",children:$t(L.w,null,null,a)}),r("span",{class:"attn-reason",children:`${((I=L.w)==null?void 0:I.spinner)||"working"}…`})]},`run:${L.pid}`)})]}),f.length>0&&r(K,{children:[r(an,{icon:"★",label:"PINNED",count:f.length,collapsed:h,onToggle:()=>Hn("sec:pinned",h)}),!h&&f.map(L=>{var I,O;return r("div",{class:"rail-row attn-row attn-pinned",onClick:()=>at(L),children:[r("span",{class:`attn-ico ${((I=ql[L.agent])==null?void 0:I.className)||""}`,children:((O=ql[L.agent])==null?void 0:O.glyph)||"$"}),r("span",{class:"attn-label",children:$t(L,null,null,a)}),r("span",{class:Xa(L.state),style:"margin-left:auto"})]},`pin:${L.pid}`)})]})]})}function ov(){me.value;const e=Da.value||[],t=sr(),n=Zd(ce.value||[]),i=u1(e),l=t["sec:activity"]!==!1;return r(K,{children:[r(an,{label:"ACTIVITY",count:i.length||null,collapsed:l,onToggle:()=>Hn("sec:activity",l)}),!l&&i.map(a=>r("div",{class:`rail-row attn-row attn-activity attn-${a.kind}`,onClick:()=>jt(a.target),children:[r("span",{class:"attn-ico",children:a.kind==="done"?"✓":"•"}),r("span",{class:"attn-label",children:$t(null,a.session,a.name,n)}),r("span",{class:"attn-reason",children:Pe(a.ts)})]},`act:${a.id}`))]})}const cv="";function uv(e){const t=window.__periscopeOpenLauncher;typeof t=="function"&&t(e)}function dv(e,t){return!t||t==="all"?e:e.filter(n=>n.track_id===t)}function hv({live:e,scope:t,onScope:n}){const i=[],l=new Set;for(const a of e)a.track_id&&!l.has(a.track_id)&&(l.add(a.track_id),i.push(a.track_id));return i.length<2?null:r("div",{class:"rail-filter-chips",role:"tablist","aria-label":"filter by track",children:[r("button",{class:`rail-filter-chip${!t||t==="all"?" is-active":""}`,onClick:()=>n("all"),children:"all"}),i.map(a=>r("button",{class:`rail-filter-chip${t===a?" is-active":""}`,onClick:()=>n(a),title:Ci(a,e),children:Ci(a,e)},a))]})}let Qo=0;function fv(){if(Date.now()-Qo<5e3)return;const e=ce.value||[];if(e.length===0)return;Qo=Date.now();const t=Ya(e,Oi.value,Di.value,{trackOrder:gi(),tabsByTrack:ar(),branchOrderByTrack:Ta()},Bt.value),n=gi(),i=ar(),l=t.trackOrder,a={};for(const u of l)a[u]=t.tabsByTrack[u]||[];if(JSON.stringify(l)===JSON.stringify(n)&&JSON.stringify(a)===JSON.stringify(i))return;const s=[...new Set(e.map(u=>u.track_id))],o=(Bt.value||[]).map(u=>u.id);$e({track_order:l,tabs_by_track:a},`sync live=${JSON.stringify(s)} registry=${JSON.stringify(o)} cached=${JSON.stringify(n)}`)}function Xd(e,t,n,i){if(t<0||n<0)return e;const[l]=e.splice(t,1);let a=n-(tA.id===i),{trackOrder:b,tabsByTrack:x,branchesByTrack:w,tabsByBranch:C}=Ya(u,Oi.value,Di.value,{trackOrder:gi(),tabsByTrack:ar(),branchOrderByTrack:Ta()},m),y=sb(b,x,d,gt.value||[]);function g(A){if(A==="reviews"){Ra();return}if(A.startsWith("review:")){const z=A.slice(7),k=z.indexOf("/"),W=(gt.value||[]).find(Z=>Z.repo===z.slice(0,k)&&Z.slug===z.slice(k+1));W&&Eu(W.repo,W.branch);return}ke.value=A,ye("pane.focus",{key:A}),A.startsWith("pane:")&&yt({kind:"pane",pid:A.slice(5)})}function $(A){const z=sr()[A]===!0;mu(A,!z)}async function v(A){await Yn(`Close tab "${_e(A)}"? +\x1B[31m[periscope: image paste error: ${a.message}]\x1B[0m`)}return}}}function Vw({w:e}){const t=!!e.spawned_by&&(ce.value||[]).some(l=>l.pid===e.spawned_by),n=[];if(e.session&&e.session!==e.branch&&n.push(r("span",{children:r("b",{children:e.session})})),e.cwd){const l=e.cwd.split("/").filter(Boolean),a=l[l.length-1]||"",s=e.branch?e.branch.replace(/[/\\]/g,"-"):null;let o;s&&a===s&&l.length>=2?o=l[l.length-2]:o=l.length>=2?l.slice(-2).join("/"):l[0]||e.cwd,o&&n.push(r(K,{children:[n.length>0&&r("span",{class:"hsep",children:"·"}),r("span",{class:"header-cwd-reveal",title:`Reveal ${e.cwd} in Finder`,onClick:async()=>{try{await fetch(`/api/fs/open?session=${encodeURIComponent(e.session)}&index=${e.index}&path=.&action=reveal`,{method:"POST"})}catch{}},children:o})]}))}if(e.branch&&n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("span",{children:e.branch})]})),e.pr){const l=lr(e.repo_slug,e.pr),a=Ui(e.pr_state),s=e.ci==="✓"?"ci-ok":e.ci==="✗"?"ci-bad":e.ci==="⟳"?"ci-running":"",o=e.ci&&!a.cls?r(K,{children:[" ",r("span",{class:`header-ci ${s}`,children:e.ci})]}):null,u=r(K,{children:["#",String(e.pr),a.suffix?r("span",{class:"header-pr-state",children:a.suffix}):null,o]}),c=l?r("a",{class:`header-pr ${a.cls}`,href:l,target:"_blank",rel:"noopener",children:u}):r("span",{class:`header-pr ${a.cls}`,children:u});n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),c]}))}const i=vr(e);if(i&&n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("a",{class:"header-linear",href:i.href,target:"_blank",rel:"noopener",title:i.tip,children:i.id})]})),e.git&&e.git!=="clean"&&n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("span",{class:"header-git",children:e.git})]})),e.agent==="claude"?n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r(Mw,{w:e})]})):e.agent&&e.model&&n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("span",{children:e.model.replace(/\s*\(.*\)/,"")})]})),e.agent&&e.context_pct!=null&&n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("span",{children:[e.context_pct,"%"]})]})),e.agent==="claude"&&e.mem){const l=fu(e.mem);n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("span",{class:`header-mem ${l.cls}`,title:l.title,children:["↻ ",l.label]})]}))}return e.api_error&&n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("span",{class:"header-api-error",title:"Claude hit an API error and has written nothing since",children:"⚠ API error"})]})),e.res&&n.push(r(K,{children:[r("span",{class:"hsep",children:"·"}),r("span",{class:"header-res",title:"this pane's whole process tree: memory footprint, and its share of the machine's CPU (100% = every core busy)",children:`${cn(e.res.mem_mb)} mem · ${Au(e.res.cpu_pct)} cpu`})]})),r("header",{id:"detail-pane-header",class:"detail-pane-header","data-agent":e.agent||void 0,children:[n.map((l,a)=>r(K,{children:l})),e.spawner_name&&r("span",{class:`header-lineage${t?"":" lineage-gone"}`,title:t?`spawned by ${e.spawner_name} — click to reveal`:`spawned by ${e.spawner_name}, which is no longer running`,onClick:t?()=>{ke.value=`pane:${e.spawned_by}`,yt({kind:"pane",pid:e.spawned_by})}:void 0,children:["↳ launched by ",e.spawner_name]}),e.status_line&&r("span",{class:"header-status",title:e.status_at?`as of ${Pe(e.status_at)} ago`:void 0,children:e.status_line})]})}function Gw({target:e}){const[t,n]=B(null);return j(()=>{if(!e)return;let i=!0;async function l(){try{const s=await fetch(`/api/pane?${tt(e)}&lines=80`);if(!s.ok)return;const o=await s.json();i&&n(o)}catch{}}l();const a=setInterval(l,Hw);return()=>{i=!1,clearInterval(a)}},[e]),t?r(dw,{data:t,onRefresh:()=>{},containerId:"detail-side",containerClass:"detail-side",idPrefix:"detail"}):r("aside",{id:"detail-side",class:"detail-side"})}function Jw({w:e}){const t=Qd(e);j(()=>{rr.value=e.target},[e.target]),j(()=>{e.pane_id&&(e.channel_unread||0)>0&&fetch(`/api/channel/clear-unread?pane_id=${encodeURIComponent(e.pane_id)}`,{method:"POST"})},[e.pane_id,e.channel_unread]),j(()=>(qs(c=>{let p=c,d=null;const f=p.match(/^(.*?):(\d+)$/);f&&(p=f[1],d=f[2]),kr({path:p,line:d})}),()=>qs(null)),[e.target]),j(()=>{const c=document.getElementById("detail-pane-header"),p=document.getElementById("detail-tab-bar"),d=document.getElementById("detail");if(!c||!d)return;const f=()=>d.style.setProperty("--detail-header-h",`${c.offsetHeight+((p==null?void 0:p.offsetHeight)||0)}px`);f();const h=new ResizeObserver(f);return h.observe(c),p&&h.observe(p),()=>h.disconnect()},[e.target]),j(()=>{if(t!=="terminal")return;const c=requestAnimationFrame(()=>requestAnimationFrame(vi));return()=>cancelAnimationFrame(c)},[t]);const[n,i]=B(!0);j(()=>{if(t!=="terminal")return;const c=()=>i(D1());c();const p=setInterval(c,250);return()=>clearInterval(p)},[t,e.pid]);const l=te(e.state);j(()=>{const c=l.current==="working",p=e.state==="idle"||e.state==="done";if(c&&p&&e.agent){const d=document.getElementById("detail-xterm");d&&(d.classList.add("bell-pulse"),setTimeout(()=>d.classList.remove("bell-pulse"),400))}l.current=e.state},[e.state,e.agent]);const a=Ne.value[e.pid]||[],s=Re.value[e.pid]||"pane",o=s==="pane",u=e.track_name||_e(e);return r("div",{id:"detail-pane",class:"detail-pane",children:[r(Vw,{w:e}),r("div",{id:"detail-tab-bar",class:"detail-tab-bar",children:[r(Ww,{pid:e.pid,paneLabel:u}),e.agent==="claude"&&r(Uw,{mode:t,onMode:c=>{ye("view.switch",{view:c}),Ul(e.pid,c)}})]}),r("div",{class:"detail-pane-body",children:[r("div",{class:"detail-term-host",style:o&&t==="terminal"?"display:contents":"display:none",children:r(_w,{id:"detail-xterm",class:"detail-xterm",target:e.pane_id,agent:e.agent,onPaste:Kw},e.pid)}),r("div",{class:"detail-changes-host",style:s==="changes"?"display:contents":"display:none",children:r(Kk,{target:e.target,active:s==="changes",gitSig:e.git||""})}),a.map(c=>{const p=`file:${c.path}`,d=s===p;return r("div",{class:"detail-preview-host",style:d?"display:contents":"display:none",children:r(fw,{entry:c,active:d})},p)}),!bi.value&&r(Gw,{target:e.target},e.target),!bi.value&&r(Jd,{which:"side"})]}),o&&t==="terminal"&&!n&&r("button",{class:"term-scroll-bottom",title:"Scroll to latest",onClick:vi,children:"⤓"}),o&&t==="terminal"&&r(xw,{})]})}function $l(){return r("div",{id:"detail-empty",class:"detail-empty",children:r("div",{class:"detail-empty-card",children:r("p",{children:["Select a tab on the left, or"," ",r("button",{id:"detail-empty-add",onClick:()=>{var e;return(e=window.__periscopeOpenOmnibox)==null?void 0:e.call(window)},children:"+ open"})," ","to add one."]})})})}function Qw(){var d,f;const e=ke.value,t=ce.value||[],n=!!e&&e.startsWith("pane:"),i=!!e&&e.startsWith("review:"),l=e==="reviews";let a=null;if(i){const[h,m]=(()=>{const b=e.slice(7),x=b.indexOf("/");return x<0?[b,""]:[b.slice(0,x),b.slice(x+1)]})();a=(gt.value||[]).find(b=>b.repo===h&&b.slug===m)||null}j(()=>{(i||l)&&(rr.value=null)},[e]);const s=n?Go(e.slice(5)):null,o=te(new Set);n&&(s==null?void 0:s.agent)==="claude"&&o.current.add(s.pid);const u=new Set(t.map(h=>h.pid)),c=Qd(s);for(const h of[...o.current])if(!(n&&(s==null?void 0:s.pid)===h)&&!u.has(h)&&(o.current.delete(h),Ft.value[h])){const{[h]:b,...x}=Ft.value;Ft.value=x}const p=[...o.current];return r("section",{id:"detail",children:[!e&&r($l,{}),n&&(s?r(Jw,{w:s}):r($l,{})),l&&r(yw,{}),i&&(gt.value===null?r("div",{class:"wt-empty",children:"loading…"}):a?r(vw,{repo:a.repo,branch:a.branch,pr:a.pr,sig:`${(d=a.walkthrough)==null?void 0:d.head}|${(f=a.walkthrough)==null?void 0:f.stale_by}|${a.unresolved}|${a.comments}`},`${a.repo}/${a.branch}`):r($l,{})),p.map(h=>{const m=Go(h),b=n&&(s==null?void 0:s.pid)===h,x=(Re.value[h]||"pane")==="pane";return r("div",{class:"detail-transcript-host",style:b&&x&&c==="transcript"?"display:flex":"display:none",children:r(jw,{target:m==null?void 0:m.target,pid:h,selected:b,state:m==null?void 0:m.state,waitingFor:m==null?void 0:m.waiting_for,spinner:m==null?void 0:m.spinner})},`tr:${h}`)})]})}function Cl(e,t){return t==="all"?!0:t==="needs-input"?e.state==="needs-input":t==="working"?e.state==="working":t==="done"?e.state==="done":t==="idle"?e.state==="idle":t==="agents"?!!e.agent:t==="shell"?e.state==="shell":t==="ci-bad"?e.ci==="✗":!0}const ia=H(!1);function Tl(e,t,n,i=l=>l.key){const l=e||[];if(!n||!t)return l;const a=new Map(l.map(s=>[i(s),s]));return t.map(s=>a.get(i(s))||s)}function El(e,t,n,i=l=>l.key){if(!n||!t)return!1;const l=(e||[]).map(i),a=t.map(i);if(l.length!==a.length)return!0;const s=new Set(a);return l.some(o=>!s.has(o))}function Yw(e,t){return!!t&&!!(e!=null&&e.branch)}function Zw(e,t){return`open ${e!=null&&e.branch?`${e.branch} `:""}worktree in ${t}`}const Xw=900;function Xa(e){return e==="needs-input"?"dot dot-alert dot-pulse":e==="working"?"dot dot-green":e==="done"?"dot dot-blue dot-pulse-done":e==="idle"?"dot dot-grey":"dot dot-none"}function ev(e){return e==="✓"?"ok":e==="✗"?"bad":e==="⟳"?"running":"neutral"}function tv(e){return e?e.replace(/\s*\(.*$/,"").trim():null}function nv(e){return`${Math.round(e/1e3)}k`}function Nn(e){return e.context_pct!=null?`${e.context_pct}%`:e.ctx_tokens!=null?nv(e.ctx_tokens):null}function Yd({label:e,kind:t,renameable:n,onCommit:i}){const[l,a]=B(!1),s=te(null),o=te(!1);j(()=>{l&&s.current&&(s.current.focus(),s.current.select())},[l]);function u(d){n&&(d.preventDefault(),d.stopPropagation(),o.current=!1,a(!0))}function c(d){if(o.current)return;o.current=!0,a(!1);const f=(d||"").trim();f&&f!==e&&i(f)}function p(){o.current||(o.current=!0,a(!1))}return l?r("span",{class:"rail-label",children:r("input",{ref:s,type:"text",class:"rail-rename-input",defaultValue:e,spellcheck:!1,onClick:d=>d.stopPropagation(),onKeyDown:d=>{d.stopPropagation(),d.key==="Enter"?(d.preventDefault(),c(d.currentTarget.value)):d.key==="Escape"&&(d.preventDefault(),p())},onBlur:d=>c(d.currentTarget.value)})}):r("span",{class:"rail-label",onDblClick:n?u:void 0,children:t==="pane"?e:r("b",{children:e})})}function rv({w:e,chip:t,selectedKey:n,onSelect:i,onClose:l,onRename:a,dim:s,dragProps:o,dropPos:u,pinned:c,onTogglePin:p,awaitingSince:d,onMoveAccount:f,onToggleNamePin:h,editor:m,onOpenInEditor:b}){const x=`pane:${e.pid}`,w=x===n,C=w||e.state==="needs-input",y=e.agent?e.state||"idle":"shell",g=s?"":" rail-dim",$=u?" drop-target":"",v=_e(e),E=e.agent||(e.is_claude?"claude":null),M=ql[E],T=e.status_rail||e.status_line,S=e.status_at&&Math.floor(Date.now()/1e3)-e.status_at>Xw,R=tv(e.model),F=e.pr?lr(e.repo_slug,e.pr):null,q=fu(e.mem),L=d?vu(d,Math.floor(Date.now()/1e3)):null,I=Ch(e),O=Ui(e.pr_state),Q=`PR #${e.pr}${O.suffix}`,A=r(K,{children:["#",e.pr,e.ci&&!O.cls?r("span",{class:`pane-ci pane-ci-${ev(e.ci)}`,children:[" ",e.ci]}):null]}),z=e.pr&&(F?r("a",{class:`pane-pill pane-pill-pr ${O.cls}`,href:F,target:"_blank",rel:"noopener",onClick:X=>X.stopPropagation(),title:Q,children:A}):r("span",{class:`pane-pill pane-pill-pr ${O.cls}`,title:Q,children:A})),k=vr(e),W=k&&r("a",{class:"pane-pill pane-pill-linear",href:k.href,target:"_blank",rel:"noopener",onClick:X=>X.stopPropagation(),title:k.tip,children:k.id}),Z=e.account&&e.account!=="default"&&($n()||e.account==="unknown")&&r("span",{class:"pane-pill pane-pill-acct",title:`running on Claude account ${yn(e.account)} — its usage bills that subscription`,children:["@",yn(e.account)]}),_=e.profile&&e.profile!=="default"&&r("span",{class:"rail-profile",title:`running the ${To(e.profile)} profile — a different plugin set and system prompt than a normal pane`,children:To(e.profile)});return r("div",{class:`rail-row child-row pane-row pane-state-${y}${C?" pane-expanded":""}${w?" selected":""}${g}${$}`,"data-drop-pos":u||void 0,draggable:!0,onClick:()=>i(x),...o,children:[r("span",{class:"pane-stripe","aria-hidden":"true"}),r("div",{class:"pane-row-main",children:[M?r("span",{class:`rail-icon ${M.className}`,title:M.label,children:M.glyph}):r("span",{class:"rail-icon icon-shell",children:"$"}),r(Yd,{label:v,kind:"pane",renameable:!0,onCommit:a}),C?R&&r("span",{class:"pane-model",title:e.model,children:R}):Nn(e)!=null&&r("span",{class:`pane-mini-ctx${Vl(e)}`,title:e.ctx_hint||"context window used",children:Nn(e)}),q&&r("span",{class:`rail-mem ${q.cls}`,title:q.title,children:["↻",q.label]}),L&&r("span",{class:`rail-await wait-${L}`,title:`asked you something ${Pe(d)} ago and has had no reply since`,children:["⚠",Pe(d)]}),c&&r("span",{class:"pane-pin-flag",title:"pinned",children:"★"})]}),(t||T||!C&&(z||W||Z||_))&&r("div",{class:`rail-meta${S?" stale":""}`,children:[t&&r("span",{class:"rail-chip",title:e.cwd,children:["⧉ ",t]}),T&&r("span",{class:"rail-status",title:e.status_line,children:T}),!C&&(z||W||Z||_)&&r("span",{class:"rail-chips",children:[z,W,Z,_]})]}),C&&(e.pr||k||Nn(e)!=null||Z||_)&&r("div",{class:"pane-foot",children:[z,W,r("span",{class:"pane-foot-spacer"}),Z,_,Nn(e)!=null&&r("span",{class:`pane-pill pane-pill-ctx${Vl(e)}`,title:e.ctx_hint||"context window used",children:Nn(e)}),e.status_at?r("span",{class:"pane-time",children:Pe(e.status_at)}):null]}),r("div",{class:"pane-actions",children:[I&&r("button",{class:"rail-move-acct",title:`re-open this session on Claude account ${I.label} — spawns a resumed pane there and leaves this one running`,onClick:X=>{X.stopPropagation(),f==null||f(I.id)},children:["→ ",I.label]}),r("button",{class:`rail-pin${c?" pinned":""}`,title:c?"unpin":"pin",onClick:X=>{X.stopPropagation(),p==null||p()},children:c?"★":"☆"}),r("button",{class:`rail-name-pin${e.name_pinned?" pinned":""}`,title:e.name_pinned?"name pinned — click to let periscope rename this tab again":"freeze this name — periscope won't rename this tab",onClick:X=>{X.stopPropagation(),h==null||h()},children:e.name_pinned?"🔒":"🔓"}),Yw(e,m)&&r("button",{class:"rail-editor",title:Zw(e,m),onClick:X=>{X.stopPropagation(),b==null||b()},children:"↗"}),r("button",{class:"rail-close",title:"kill this tab",onClick:X=>{X.stopPropagation(),l()},children:"×"})]})]})}function iv({worktreeKey:e,onOpen:t}){return r("div",{class:"rail-row child-row newtab-row last-in-worktree",onClick:()=>t(e),children:[r("span",{class:"rail-icon",children:"+"}),r("span",{class:"rail-label",children:"New tab"})]})}function lv({label:e,collapsed:t,childCount:n,rolledUp:i,dim:l,onToggle:a,dragProps:s,dropPos:o}){return r("div",{class:`rail-row wt-row${l?"":" rail-dim"}${o?" drop-target":""}`,"data-drop-pos":o||void 0,draggable:!0,onClick:d=>{d.target.closest("button")||d.target.closest("input")||a()},...s,children:[r("span",{class:"rail-chev",children:t?"▸":"▾"}),r("span",{class:"rail-icon icon-worktree",children:"⎇"}),r("span",{class:"rail-label",children:r("b",{children:e})}),t&&n>0?r("span",{class:"rail-count",children:n}):null,r("span",{class:Xa(i)})]})}function Jo({row:e,selectedKey:t,onSelect:n}){var s;const i=`review:${e.repo}/${e.slug}`,l=i===t?" selected":"",a=((s=e.walkthrough)==null?void 0:s.stale_by)>0;return r("div",{class:`rail-row child-row rail-review${l}`,onClick:()=>n(i),children:[r("span",{class:"rail-icon icon-review",children:"◉"}),r("span",{class:"rail-label",children:["review · ",e.walkthrough.stops," stop",e.walkthrough.stops===1?"":"s",a?" · stale":""]}),e.unresolved?r("span",{class:"rail-review-badge",children:e.unresolved}):null]})}function av({kind:e,label:t,collapsed:n,rolledUp:i,dim:l,onToggle:a,onRename:s,onDissolve:o,onTeardown:u,dragProps:c,dropPos:p}){const d=n?"▸":"▾",f=l?"":" rail-dim",h=p?" drop-target":"",[m,b]=B(!1),x=e==="loose"||e==="repo",w=e==="goal"?"⧉":"🗀";return j(()=>{if(!m)return;const C=g=>{g.target.closest(".track-menu")||b(!1)},y=g=>{g.key==="Escape"&&b(!1)};return document.addEventListener("click",C,!0),document.addEventListener("keydown",y),()=>{document.removeEventListener("click",C,!0),document.removeEventListener("keydown",y)}},[m]),r("div",{class:`rail-row repo-row${f}${h}`,"data-drop-pos":p||void 0,draggable:!0,onClick:C=>{C.target.closest("button")||C.target.closest("input")||a()},...c,children:[r("span",{class:"rail-chev",children:d}),r("span",{class:"rail-icon icon-effort",title:e==="goal"?"track":"project",children:w}),r(Yd,{label:t,kind:"track",renameable:e!=="loose",onCommit:s}),r("span",{class:"repo-rule","aria-hidden":"true"}),r("span",{class:Xa(i)}),!x&&r("span",{class:"track-menu",children:[r("button",{class:"rail-track-menu-btn",title:"track actions",onClick:C=>{C.stopPropagation(),b(y=>!y)},children:"⋯"}),m&&r("div",{class:"track-menu-pop",onClick:C=>C.stopPropagation(),children:[r("button",{class:"track-menu-item",onClick:()=>{b(!1),o()},children:["Dissolve ",r("span",{class:"track-menu-hint",children:"tabs survive"})]}),r("button",{class:"track-menu-item track-menu-danger",onClick:()=>{b(!1),u()},children:["Tear down ",r("span",{class:"track-menu-hint",children:"kills tabs"})]})]})]})]})}function an({icon:e,label:t,count:n,collapsed:i,onToggle:l,tone:a}){return r("div",{class:`rail-row section-header${a==="alert"?" section-header-alert":a==="ready"?" section-header-ready":a==="working"?" section-header-working":""}`,onClick:l,children:[r("span",{class:"rail-chev",children:i?"▸":"▾"}),e?r("span",{class:"section-header-icon",children:e}):null,r("span",{class:"section-header-label",children:t}),n!=null&&n>0?r("span",{class:"section-header-count",children:n}):null]})}function $t(e,t,n,i){const l=(e==null?void 0:e.session)||t||"",a=_e(e)||n||"";return`${i?i(l):l} · ${a}`}function Zd(e){const t=[...new Set((e||[]).map(n=>n.session).filter(Boolean))];return n=>hu(n,t)}function Hn(e,t){mu(e,!t)}function Zt(e){return`${e.kind}:${e.pid??e.id}`}function sv(){var M,T,S,R,F,q;me.value;const e=ce.value||[],t=Da.value||[],n=ir.value,i=ft.value,l=sr(),a=Zd(e),s=La(e,t,n,i),o=l["sec:needs"]===!0,u=wu(e,t,n,pt.value),c=l["sec:ready"]===!0,p=Su(e),d=l["sec:running"]===!0,f=c1(Ea(),e),h=l["sec:pinned"]===!0,m=ia.value,b=te(null),x=Tl(s,(M=b.current)==null?void 0:M.needs,m,Zt),w=Tl(u,(T=b.current)==null?void 0:T.ready,m,Zt),C=Tl(p,(S=b.current)==null?void 0:S.running,m,Zt),y=El(s,(R=b.current)==null?void 0:R.needs,m,Zt)||El(u,(F=b.current)==null?void 0:F.ready,m,Zt)||El(p,(q=b.current)==null?void 0:q.running,m,Zt);j(()=>{m||(b.current={needs:s,ready:u,running:p})});function g(){b.current=null}j(()=>{const L=Bs(ft.value,e,"needs-input");L.size!==ft.value.size&&(ft.value=L);const I=Bs(pt.value,e,"done");I.size!==pt.value.size&&(pt.value=I)},[ce.value]);function $(L){g();const I=new Set(n);I.add(L),ir.value=I}function v(L){g(),$u(L)}function E(L){g(),Tu(L)}return r(K,{children:[y&&r("div",{class:"attn-frozen",title:"Move the pointer out of the rail to apply",children:"updates paused"}),x.length>0&&r(K,{children:[r(an,{icon:"⚠",label:"NEEDS YOU",tone:"alert",count:a1(x),collapsed:o,onToggle:()=>Hn("sec:needs",o)}),!o&&x.map(L=>{var I;return L.kind==="live"?r("div",{class:"rail-row attn-row attn-needs",onClick:()=>v(L.w),children:[r("span",{class:"attn-dot dot dot-alert dot-pulse"}),r("span",{class:"attn-label",children:$t(L.w,null,null,a)}),r("span",{class:"attn-reason",children:Mg((I=L.w)==null?void 0:I.waiting_for)})]},`live:${L.pid}`):r("div",{class:"rail-row attn-row attn-needs attn-event",onClick:()=>jt(L.target),children:[r("span",{class:"attn-ico",children:"⚠"}),r("span",{class:"attn-label",children:$t(L.w,L.session,L.name,a)}),r("span",{class:"attn-reason",children:["need_human · ",Pe(L.ts)]}),r("button",{class:"attn-x",title:"dismiss",onClick:O=>{O.stopPropagation(),$(L.id)},children:"×"})]},`evt:${L.id}`)})]}),w.length>0&&r(K,{children:[r(an,{icon:"✓",label:"READY",tone:"ready",count:w.length,collapsed:c,onToggle:()=>Hn("sec:ready",c)}),!c&&w.map(L=>{var I;return L.kind==="live"?r("div",{class:"rail-row attn-row attn-ready",onClick:()=>E(L.w),children:[r("span",{class:"attn-dot dot dot-blue dot-pulse-done"}),r("span",{class:"attn-label",children:$t(L.w,null,null,a)}),r("span",{class:"attn-reason",children:Pe((I=L.w)==null?void 0:I.completed_at)})]},`rlive:${L.pid}`):r("div",{class:"rail-row attn-row attn-ready attn-event",onClick:()=>jt(L.target),children:[r("span",{class:"attn-ico",children:"✓"}),r("span",{class:"attn-label",children:$t(L.w,L.session,L.name,a)}),r("span",{class:"attn-reason",children:["done · ",Pe(L.ts)]}),r("button",{class:"attn-x",title:"dismiss",onClick:O=>{O.stopPropagation(),$(L.id)},children:"×"})]},`revt:${L.id}`)})]}),C.length>0&&r(K,{children:[r(an,{icon:"⟳",label:"RUNNING",tone:"working",count:C.length,collapsed:d,onToggle:()=>Hn("sec:running",d)}),!d&&C.map(L=>{var I;return r("div",{class:"rail-row attn-row attn-running",onClick:()=>at(L.w),children:[r("span",{class:"attn-dot dot dot-green"}),r("span",{class:"attn-label",children:$t(L.w,null,null,a)}),r("span",{class:"attn-reason",children:`${((I=L.w)==null?void 0:I.spinner)||"working"}…`})]},`run:${L.pid}`)})]}),f.length>0&&r(K,{children:[r(an,{icon:"★",label:"PINNED",count:f.length,collapsed:h,onToggle:()=>Hn("sec:pinned",h)}),!h&&f.map(L=>{var I,O;return r("div",{class:"rail-row attn-row attn-pinned",onClick:()=>at(L),children:[r("span",{class:`attn-ico ${((I=ql[L.agent])==null?void 0:I.className)||""}`,children:((O=ql[L.agent])==null?void 0:O.glyph)||"$"}),r("span",{class:"attn-label",children:$t(L,null,null,a)}),r("span",{class:Xa(L.state),style:"margin-left:auto"})]},`pin:${L.pid}`)})]})]})}function ov(){me.value;const e=Da.value||[],t=sr(),n=Zd(ce.value||[]),i=u1(e),l=t["sec:activity"]!==!1;return r(K,{children:[r(an,{label:"ACTIVITY",count:i.length||null,collapsed:l,onToggle:()=>Hn("sec:activity",l)}),!l&&i.map(a=>r("div",{class:`rail-row attn-row attn-activity attn-${a.kind}`,onClick:()=>jt(a.target),children:[r("span",{class:"attn-ico",children:a.kind==="done"?"✓":"•"}),r("span",{class:"attn-label",children:$t(null,a.session,a.name,n)}),r("span",{class:"attn-reason",children:Pe(a.ts)})]},`act:${a.id}`))]})}const cv="";function uv(e){const t=window.__periscopeOpenLauncher;typeof t=="function"&&t(e)}function dv(e,t){return!t||t==="all"?e:e.filter(n=>n.track_id===t)}function hv({live:e,scope:t,onScope:n}){const i=[],l=new Set;for(const a of e)a.track_id&&!l.has(a.track_id)&&(l.add(a.track_id),i.push(a.track_id));return i.length<2?null:r("div",{class:"rail-filter-chips",role:"tablist","aria-label":"filter by track",children:[r("button",{class:`rail-filter-chip${!t||t==="all"?" is-active":""}`,onClick:()=>n("all"),children:"all"}),i.map(a=>r("button",{class:`rail-filter-chip${t===a?" is-active":""}`,onClick:()=>n(a),title:Ci(a,e),children:Ci(a,e)},a))]})}let Qo=0;function fv(){if(Date.now()-Qo<5e3)return;const e=ce.value||[];if(e.length===0)return;Qo=Date.now();const t=Ya(e,Oi.value,Di.value,{trackOrder:gi(),tabsByTrack:ar(),branchOrderByTrack:Ta()},Bt.value),n=gi(),i=ar(),l=t.trackOrder,a={};for(const u of l)a[u]=t.tabsByTrack[u]||[];if(JSON.stringify(l)===JSON.stringify(n)&&JSON.stringify(a)===JSON.stringify(i))return;const s=[...new Set(e.map(u=>u.track_id))],o=(Bt.value||[]).map(u=>u.id);$e({track_order:l,tabs_by_track:a},`sync live=${JSON.stringify(s)} registry=${JSON.stringify(o)} cached=${JSON.stringify(n)}`)}function Xd(e,t,n,i){if(t<0||n<0)return e;const[l]=e.splice(t,1);let a=n-(tA.id===i),{trackOrder:b,tabsByTrack:x,branchesByTrack:w,tabsByBranch:C}=Ya(u,Oi.value,Di.value,{trackOrder:gi(),tabsByTrack:ar(),branchOrderByTrack:Ta()},m),y=sb(b,x,d,gt.value||[]);function g(A){if(A==="reviews"){Ra();return}if(A.startsWith("review:")){const z=A.slice(7),k=z.indexOf("/"),W=(gt.value||[]).find(Z=>Z.repo===z.slice(0,k)&&Z.slug===z.slice(k+1));W&&Eu(W.repo,W.branch);return}ke.value=A,ye("pane.focus",{key:A}),A.startsWith("pane:")&&yt({kind:"pane",pid:A.slice(5)})}function $(A){const z=sr()[A]===!0;mu(A,!z)}async function v(A){await Yn(`Close tab "${_e(A)}"? This kills its tmux window.`,{okLabel:"Close",danger:!0})&&await ie("close tab",`/api/window?${tt(A.target)}`,{method:"DELETE"})}async function E(A,z,k=!1){const W=`/api/pane/move-account?pid=${encodeURIComponent(A.pid)}&account=${encodeURIComponent(z)}`+(k?"&force=1":""),{data:Z,error:_,reason:X,status:he}=await gr("move account",W,{method:"POST"});if(ye("api:move account",{path:W,method:"POST",ok:!!Z}),Z!=null&&Z.pid){ke.value=`pane:${Z.pid}`,yt({kind:"pane",pid:Z.pid});return}if(he===409&&!k&&(X!=null&&X.startsWith("session looks live"))){await Yn(`${_} diff --git a/static/src/split/Detail.jsx b/static/src/split/Detail.jsx index c7be3752..f9d31890 100644 --- a/static/src/split/Detail.jsx +++ b/static/src/split/Detail.jsx @@ -264,7 +264,7 @@ export function PaneHeader({ w }) { parts.push(<>·↻ {mh.label}); } if (w.api_error) { - parts.push(<>·⚠ API error); + parts.push(<>·⚠ API error); } // After ↻ and API-error: its fixed width (styles.css .header-res) can't // shift either chip. diff --git a/tests/test_panes.py b/tests/test_panes.py index d2f16b1c..05d884af 100644 --- a/tests/test_panes.py +++ b/tests/test_panes.py @@ -738,15 +738,16 @@ # Claude Code renders rate-limit / transport failures as a normal `⎿` tool # result whose body starts with "API Error:". We flag panes whose MOST # RECENT tool result is an error so the dashboard can show a stuck-on- -# retry pane at a glance. A subsequent successful tool result clears it. +# retry pane at a glance. A subsequent successful tool result clears it, +# and so does a `⏺` reply to a prompt sent after the error. API_ERROR_CASES: list[tuple[str, str, bool]] = [ # The screenshot Tom hit: three consecutive rate-limit hits, the user # repeatedly typed `keep going`, and the bottom shows Claude resuming. - # The latest tool result above the resumed turn is still the API - # error → flag should be on so the card surfaces the stuck retry. + # The `⏺` under the last `keep going` is a reply → the errors belong to + # earlier turns, flag off (THI-455). ( - "rate-limited-stuck", + "rate-limited-resumed", textwrap.dedent(f"""\ Reshaping the query — payload columns are huge. @@ -773,6 +774,57 @@ ⏺ Skipping reminder. One last lookup — there's a closest_store_by_address variant I haven't traced yet. {STATUS_LINE} + """), + False, + ), + # The same pane before anything answers the nudge: `keep going` alone + # doesn't clear it. + ( + "rate-limited-nudged", + textwrap.dedent(f"""\ + ❯ keep going + ⎿ API Error: Server is temporarily limiting requests (not your usage limit) · Rate limited + + ✻ Brewed for 0s + + ❯ keep going + + ✻ Crunching… + {STATUS_LINE} + """), + True, + ), + # The nudged turn has so far printed only a collapsed tool group: the + # API answered → off. + ( + "rate-limited-resuming-calls", + textwrap.dedent(f"""\ + ❯ keep going + ⎿ API Error: Server is temporarily limiting requests (not your usage limit) · Rate limited + + ✻ Brewed for 0s + + ❯ keep going + + Called fdy 6 times (ctrl+o to expand) + + ✻ Crunching… + {STATUS_LINE} + """), + False, + ), + # A long error's `… +N lines` row is part of the error, not output after it. + ( + "error-truncated-result", + textwrap.dedent(f"""\ + Called fdy (ctrl+o to expand) + ⎿ API Error: 500 {{"type":"error","error":{{"type":"api_error","message":"Internal server error"}}}} + … +3 lines (ctrl+o to expand) + + ✻ Brewed for 2s + + ❯ + {STATUS_LINE} """), True, ), @@ -818,8 +870,7 @@ """), False, ), - # API error mid-buffer with no later `⎿` tool results — Claude is - # presumably retrying / waiting. Flag on. + # API error, then a `⏺` reply in the same turn: the API answered → off. ( "error-then-only-text", textwrap.dedent(f"""\ @@ -832,7 +883,7 @@ ❯ {STATUS_LINE} """), - True, + False, ), # THI-436: a stream cut mid-response renders as an assistant `⏺` line, # not a `⎿` tool result — and a successful tool result sits above it, @@ -889,6 +940,61 @@ """), False, ), + # THI-455, from a live pane: collapsed tool calls leave no `⎿` to clear + # the flag, and a later prompt got a full reply → off. + ( + "collapsed-calls-later-reply", + textwrap.dedent(f"""\ + ❯ compare the clinic rankings from both sheets + + Called faraday_internal 2 times (ctrl+o to expand) + + ⏺ API Error: The response stopped arriving. The response above may be incomplete. + + Called faraday_internal 2 times (ctrl+o to expand) + + ⏺ Rankings by headroom count are nearly identical and track catchment size. + + ❯ add the clinic names to these codes + + Called faraday_internal (ctrl+o to expand) + + ⏺ Here are the clinic codes from the comparison with their names. + + ✻ Churned for 34s · done 8:33 AM + + {RULE} + ❯ add this comparison to the handoff + {RULE} + {CUSTOM_STATUS_LINE} + ⏵⏵ auto mode on (shift+tab to cycle) · ← 2 agents + """), + False, + ), + # The same pane when that turn ended: Claude Code carried on after the + # stream cut with no prompt in between → off. + ( + "stream-cut-recovered-in-turn", + textwrap.dedent(f"""\ + ❯ compare the clinic rankings from both sheets + + Called faraday_internal 2 times (ctrl+o to expand) + + ⏺ API Error: The response stopped arriving. The response above may be incomplete. + + Called faraday_internal 2 times (ctrl+o to expand) + + ⏺ Rankings by headroom count are nearly identical and track catchment size. + + ✻ Brewed for 2m 10s · done 8:07 AM + + {RULE} + ❯ + {RULE} + {CUSTOM_STATUS_LINE} + """), + False, + ), ]