pi-straw-boss 0.5.0 → 0.5.2

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.
@@ -0,0 +1 @@
1
+ import{n as e}from"./post-B39ywXKd.js";var t=e=>e.multiple?`It takes several choices.`:e.freeform&&e.options.length?`It also takes your own words.`:``,n=(n,r)=>e(`div`,{class:`ask-one`},...n.header?[e(`b`,{class:`ask-head`},n.header)]:[],e(`p`,{class:`ask-q`},n.question),...n.context?[e(`div`,{class:`ask-ctx`},n.context)]:[],...n.options.length?[e(`ol`,{class:`ask-opts`,"aria-label":`Options`},...n.options.map(t=>e(`li`,{class:`ask-opt`},e(`b`,null,t.title),...t.description?[e(`small`,null,t.description)]:[])))]:[],...r&&t(n)?[e(`small`,{class:`ask-how`},t(n))]:[]);function r(r,i){let a,o=!1,s=!1,c=e(`p`,{class:`ask-note`,role:`status`,"aria-live":`polite`}),l=e(`button`,{type:`button`,class:`cmp-link`},`Focus terminal`),u=()=>l.disabled=!o||s;l.onclick=async()=>{s=!0,u();try{let e=await i();c.dataset.tone=e.ok?``:`error`,c.textContent=e.ok?``:e.body.reason??`Refused (${e.status}).`}catch{c.dataset.tone=`error`,c.textContent=`Herdr could not be reached.`}finally{s=!1,u()}};function d(i){c.textContent=``;let a=i.asks??(i.ask?[i.ask]:[]),o=a.length>1,s=a.length?a.map(e=>n(e,o)):[e(`p`,{class:`ask-why`},i.why??`It waits on a dialog.`)];r.replaceChildren(e(`section`,{class:`ask`},...s,e(`div`,{class:`ask-acts`},e(`p`,{class:`ask-term`},`${o?`Answer them`:`Answer it`} in the terminal. ${o||!a[0]?``:t(a[0])}`.trim()),l),c))}return{update(e,t){o=t,e&&e.callId!==a?d(e):!e&&a&&r.replaceChildren(),a=e?.callId,u()}}}export{r as t};
@@ -1,4 +1,4 @@
1
- import{l as e,n as t}from"./index-Cn7_VOw9.js";import{n}from"./post-B39ywXKd.js";function r(){let e=!0,t=0;return{get pinned(){return e},scrolled(n){e=n,n&&(t=0)},hold:()=>void(e=!1),arrived(n){return e||(t+=n),e},jump:()=>void(e=!0,t=0),button:n=>e&&!n?void 0:t&&!n?`${t} new`:`jump to latest`}}async function i(e,t,n,r){let i=()=>e.items.some(e=>e.id===n);for(;!i()&&e.before;)r(e.prepend(await t.older(e.before)));return i()?!0:(r(e.reset(await t.latest())),!1)}var a=(e,t,n)=>({tag:e,kids:t,attrs:n}),o=/^(?:https?:\/\/|mailto:)[^\s\u0000-\u001f]+$/i,s=(e,t)=>a(`a`,t,{href:e,rel:`noopener noreferrer`,target:`_blank`}),c=/(`+)([^`]|[^`][^]{0,1000}?[^`])\1(?!`)|\*\*([^*][^]{0,1000}?)\*\*|~~([^~][^]{0,1000}?)~~|\*([^\s*][^*]{0,500}?)\*|(?<![\w])_([^\s_][^_]{0,500}?)_(?![\w])|\[([^\]]{1,500})\]\(([^)\s]{1,2000})(?:\s+"[^"]*")?\)|(https?:\/\/[^\s<>)\]]+)/g;function l(e){let t=[],n=0;for(let r of e.matchAll(c))r.index>n&&t.push(e.slice(n,r.index)),n=r.index+r[0].length,r[2]===void 0?r[3]===void 0?r[4]===void 0?r[5]!==void 0||r[6]!==void 0?t.push(a(`em`,l(r[5]??r[6]))):r[7]===void 0?t.push(s(r[9],[r[9]])):t.push(o.test(r[8])?s(r[8],l(r[7])):r[0]):t.push(a(`del`,l(r[4]))):t.push(a(`strong`,l(r[3]))):t.push(a(`code`,[r[2]]));return n<e.length&&t.push(e.slice(n)),t}var u=/^(\s*)([-*+]|\d+[.)])\s+(.*)$/,d=/^\s*([-*_])(\s*\1){2,}\s*$/,f=/^\s*\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)*\|?\s*$/,p=e=>e.trim().replace(/^\|/,``).replace(/\|$/,``).split(`|`).map(e=>e.trim()),m=(e,t)=>/^\s*(`{3,}|~{3,}|#{1,6}\s|>)/.test(e)||d.test(e)||u.test(e)||e.includes(`|`)&&t!==void 0&&f.test(t)&&t.includes(`-`);function h(e,t=0){if(t>=16)return[a(`p`,[e])];let n=e.replace(/\r\n?/g,`
1
+ import{l as e,n as t}from"./index-C_WFzbRO.js";import{n}from"./post-B39ywXKd.js";function r(){let e=!0,t=0;return{get pinned(){return e},scrolled(n){e=n,n&&(t=0)},hold:()=>void(e=!1),arrived(n){return e||(t+=n),e},jump:()=>void(e=!0,t=0),button:n=>e&&!n?void 0:t&&!n?`${t} new`:`jump to latest`}}async function i(e,t,n,r){let i=()=>e.items.some(e=>e.id===n);for(;!i()&&e.before;)r(e.prepend(await t.older(e.before)));return i()?!0:(r(e.reset(await t.latest())),!1)}var a=(e,t,n)=>({tag:e,kids:t,attrs:n}),o=/^(?:https?:\/\/|mailto:)[^\s\u0000-\u001f]+$/i,s=(e,t)=>a(`a`,t,{href:e,rel:`noopener noreferrer`,target:`_blank`}),c=/(`+)([^`]|[^`][^]{0,1000}?[^`])\1(?!`)|\*\*([^*][^]{0,1000}?)\*\*|~~([^~][^]{0,1000}?)~~|\*([^\s*][^*]{0,500}?)\*|(?<![\w])_([^\s_][^_]{0,500}?)_(?![\w])|\[([^\]]{1,500})\]\(([^)\s]{1,2000})(?:\s+"[^"]*")?\)|(https?:\/\/[^\s<>)\]]+)/g;function l(e){let t=[],n=0;for(let r of e.matchAll(c))r.index>n&&t.push(e.slice(n,r.index)),n=r.index+r[0].length,r[2]===void 0?r[3]===void 0?r[4]===void 0?r[5]!==void 0||r[6]!==void 0?t.push(a(`em`,l(r[5]??r[6]))):r[7]===void 0?t.push(s(r[9],[r[9]])):t.push(o.test(r[8])?s(r[8],l(r[7])):r[0]):t.push(a(`del`,l(r[4]))):t.push(a(`strong`,l(r[3]))):t.push(a(`code`,[r[2]]));return n<e.length&&t.push(e.slice(n)),t}var u=/^(\s*)([-*+]|\d+[.)])\s+(.*)$/,d=/^\s*([-*_])(\s*\1){2,}\s*$/,f=/^\s*\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)*\|?\s*$/,p=e=>e.trim().replace(/^\|/,``).replace(/\|$/,``).split(`|`).map(e=>e.trim()),m=(e,t)=>/^\s*(`{3,}|~{3,}|#{1,6}\s|>)/.test(e)||d.test(e)||u.test(e)||e.includes(`|`)&&t!==void 0&&f.test(t)&&t.includes(`-`);function h(e,t=0){if(t>=16)return[a(`p`,[e])];let n=e.replace(/\r\n?/g,`
2
2
  `).split(`
3
3
  `),r=[];for(let e=0;e<n.length;){let i=n[e],o=n[e+1],s=i.match(/^\s*(`{3,}|~{3,})\s*([\w+-]*)/),c=i.match(/^\s*(#{1,6})\s+(.*?)\s*#*\s*$/),g=i.match(u);if(!i.trim())e++;else if(s){let t=[];for(e++;e<n.length&&!n[e].trim().startsWith(s[1]);e++)t.push(n[e]);e++,r.push(a(`pre`,[a(`code`,[t.join(`
4
4
  `)],s[2]?{"data-lang":s[2]}:void 0)]))}else if(c)r.push(a(`h${c[1].length}`,l(c[2]))),e++;else if(d.test(i))r.push(a(`hr`,[])),e++;else if(/^\s*>/.test(i)){let i=[];for(;e<n.length&&/^\s*>/.test(n[e]);e++)i.push(n[e].replace(/^\s*> ?/,``));r.push(a(`blockquote`,h(i.join(`
@@ -1 +1 @@
1
- :root{--bg:#060a14;--panel:#0e1628e0;--line:#7a98e033;--text:#dbe6ff;--dim:#8b99ba;--gold:#ffd166;--grid:linear-gradient(#82a0ff0d 1px, transparent 1px) 0 0 / 44px 44px, linear-gradient(90deg, #82a0ff0d 1px, transparent 1px) 0 0 / 44px 44px;--mono:ui-monospace, "SF Mono", Menlo, Consolas, monospace;--sans:ui-rounded, "Avenir Next", "Trebuchet MS", system-ui, -apple-system, "Segoe UI", sans-serif}*{box-sizing:border-box;margin:0}html{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}body{min-height:100vh;color:var(--text);font:14px/1.4 var(--sans);background:radial-gradient(1100px 640px at 12% -10%, #38e0ff1c, transparent 62%), radial-gradient(900px 600px at 98% 8%, #966eff1c, transparent 60%), linear-gradient(var(--bg), #0a1020);background-attachment:fixed}svg{display:block}[data-mark=working]{--c:#38e0ff}[data-mark=waiting]{--c:#ffb02e}[data-mark=idle]{--c:#7184ad}[data-mark=done]{--c:#3ddc97}[data-mark=error]{--c:#ff5470}[data-mark=unknown]{--c:#4f5c7c}[data-mark]{--c1:color-mix(in srgb, var(--c) 13%, transparent);--c4:color-mix(in srgb, var(--c) 45%, transparent)}:focus-visible{outline-offset:2px;outline:2px solid #38e0ff}.shell{max-width:1480px;margin:0 auto;padding:18px 22px 40px}.hud{border:1px solid var(--line);background:var(--panel);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);z-index:5;border-radius:18px;align-items:center;gap:24px;padding:12px 18px;display:flex;position:sticky;top:12px}.brand{align-items:center;gap:12px;margin-right:auto;display:flex}.logo{color:#38e0ff;border:1px solid var(--line);background:radial-gradient(circle at 30% 25%,#38e0ff4d,#0a1020e6);border-radius:14px;place-items:center;width:44px;height:44px;display:grid}.logo svg{filter:drop-shadow(0 0 6px #38e0ff);width:28px;height:28px}.brand h1{letter-spacing:.14em;text-transform:uppercase;font-size:20px}.brand p{color:var(--dim);font-size:12px}.stats{gap:10px;display:flex}.stats div,.tally div,.alerts{background:#ffffff0a;border:1px solid #ffffff0f;border-radius:12px;min-width:78px;padding:6px 12px}.stats dt,.tally dt{color:var(--dim);letter-spacing:.08em;text-transform:uppercase;font-size:11px}.stats dd,.tally dd{font:600 20px/1.1 var(--mono)}.stats [data-mark] dd,.tally [data-mark] dd,.count b{color:var(--c)}.pair-btn{border:1px solid var(--line);color:var(--dim);font:600 12px var(--sans);letter-spacing:.08em;text-transform:uppercase;cursor:pointer;background:0 0;border-radius:12px;padding:6px 12px}.pair{text-align:center;color:var(--dim);justify-items:center;gap:12px;font-size:13px;display:grid}.qr{shape-rendering:crispedges;border-radius:12px;width:min(100%,300px)}.link{color:var(--dim);text-transform:uppercase;letter-spacing:.08em;align-items:center;gap:8px;font-size:12px;display:flex}.link i{background:#4f5c7c;border-radius:50%;width:9px;height:9px}.link[data-state=live] i{background:#3ddc97;animation:2.4s ease-in-out infinite blink}.link[data-state=live]{color:#3ddc97}.link[data-state=reconnecting] i,.link[data-state=connecting] i{background:#ffb02e;animation:1s ease-in-out infinite blink}.link[data-state=offline] i{background:#ff5470}.banner{color:#c9d6f5;background:#7184ad29;border-radius:14px;margin:14px 0 0;padding:10px 16px;font-size:13px}.banner[hidden]{display:none}.banner b{font-family:var(--mono);font-weight:600}.stage{grid-template-columns:minmax(0,1fr) 300px;align-items:start;gap:20px;margin-top:18px;transition:opacity .4s;display:grid}.stage[data-stale=true] .map,body[data-stale=true] .stats{opacity:.4}.stage[data-stale=true] *{animation-play-state:paused!important}.map{border:1px solid var(--line);background:var(--grid), #080d1a8c;border-radius:22px;min-height:420px;padding:26px;transition:opacity .4s;position:relative}.links{pointer-events:none;width:100%;height:100%;position:absolute;inset:0}.wire{fill:none;stroke:#38e0ff4d;stroke-width:1.5px;stroke-dasharray:3 7;stroke-linecap:round}.packet{fill:#38e0ff;opacity:.9}.bases{grid-template-columns:repeat(auto-fill,minmax(min(100%,300px),1fr));align-items:start;gap:30px;display:grid;position:relative}.empty{color:var(--dim);text-align:center;padding:120px 0;position:relative}.base{border:1px solid var(--line);background:linear-gradient(#16213c,#0c1324);border-radius:18px;padding:16px;position:relative;box-shadow:0 18px 40px #00000059}.base.head{background:linear-gradient(#ffd1661a,#0000 38%),linear-gradient(#16213c,#0c1324)}.base[data-mark=waiting]{box-shadow:0 18px 40px #00000059,inset 0 0 0 999px #ffb02e0b}.base>header{align-items:center;gap:14px;margin-bottom:14px;display:flex}.title{justify-items:start;min-width:0;margin-right:auto;display:grid}.title h2{white-space:nowrap;text-overflow:ellipsis;max-width:100%;font-size:17px;overflow:hidden}.title p{max-width:100%;color:var(--dim);white-space:nowrap;text-overflow:ellipsis;font-size:12px;overflow:hidden}.crown{letter-spacing:.12em;text-transform:uppercase;color:var(--gold);background:#ffd1661f;border-radius:999px;align-items:center;gap:5px;margin-bottom:3px;padding:2px 9px 2px 6px;font-size:10px;display:inline-flex}.crown svg{width:14px;height:14px}.base:not(.head) .crown{display:none}.tower{width:80px;height:80px;color:var(--c);flex:none;place-items:center;display:grid;position:relative}.tower:before{content:"";background:radial-gradient(circle at 35% 30%, color-mix(in srgb, var(--c) 24%, #101a2e), #0b1222 70%);border:1px solid color-mix(in srgb, var(--c) 45%, transparent);border-radius:50%;position:absolute;inset:0}.tower svg{width:48px;height:48px;filter:drop-shadow(0 0 6px color-mix(in srgb, var(--c) 70%, transparent));position:relative}.tower.ghost{opacity:.45}.tower.ghost:before{border-style:dashed}.sweep,.pulse{opacity:0;pointer-events:none;border-radius:50%;position:absolute;inset:0}.sweep{background:conic-gradient(from 0deg, transparent 0 70%, color-mix(in srgb, var(--c) 55%, transparent) 100%)}.pulse{border:2px solid var(--c)}.tower[data-mark=working] .sweep{opacity:1;animation:3.2s linear infinite spin}.tower[data-mark=waiting] .pulse{animation:1.5s ease-out infinite ping}.tower>.badge{--s:40px;position:absolute;bottom:-2px;right:-2px}.tabs{gap:12px;display:grid}.tab{background:#ffffff06;border:1px solid #ffffff0d;border-radius:14px;padding:10px}.tab h3{color:var(--dim);letter-spacing:.12em;text-transform:uppercase;margin-bottom:8px;font-size:11px;font-weight:600}.units,.subs{gap:8px;display:grid}.outposts{gap:8px;margin-top:12px;display:grid}.outposts:empty{display:none}.outposts:before{content:"Outposts";color:var(--dim);letter-spacing:.12em;text-transform:uppercase;font-size:11px}.card{background:#ffffff09;border:1px solid #ffffff0f;border-radius:12px;align-items:center;gap:10px;padding:8px 10px;transition:background-color .4s;display:flex;position:relative}.unit[data-mark=waiting]>.card{background:#ffb02e1c}.unit[data-mark=error]>.card{background:#ff54701c}.unit[data-mark=working]>.card{background:#38e0ff12}.plate{width:var(--s,40px);height:var(--s,40px);color:var(--c);background:radial-gradient(circle at 35% 30%, color-mix(in srgb, var(--c) 26%, #101a2e), #0b1222 72%);border-radius:50%;flex:none;place-items:center;transition:color .4s;display:grid;position:relative}.plate svg{width:calc(var(--s,40px) * .55);height:calc(var(--s,40px) * .55)}.ring,.plate:after{pointer-events:none;border-radius:50%;position:absolute;inset:-3px}.ring{opacity:0;background:conic-gradient(from 0deg, transparent 0 55%, var(--c));-webkit-mask:radial-gradient(farthest-side,#0000 calc(100% - 3px),#000 calc(100% - 2px));mask:radial-gradient(farthest-side,#0000 calc(100% - 3px),#000 calc(100% - 2px))}.plate[data-mark=working]>.ring{opacity:1;animation:1.1s linear infinite spin}.plate:after{content:"";border:2px solid var(--c);opacity:0}.plate[data-mark=waiting]:after{animation:1.5s ease-out infinite ping}.badge{width:clamp(16px, calc(var(--s,40px) * .3), 24px);height:clamp(16px, calc(var(--s,40px) * .3), 24px);font:700 10px/1 var(--mono);color:#06101e;background:var(--c);border-radius:50%;flex:none;place-items:center;transition:background-color .4s;display:grid}.plate>.badge{position:absolute;bottom:-4px;right:-4px}[data-mark=waiting]>.badge:after{content:"?"}[data-mark=idle]>.badge:after{content:"z"}[data-mark=done]>.badge:after{content:"✓"}[data-mark=error]>.badge:after{content:"!"}[data-mark=unknown]>.badge:after{content:"·"}[data-mark=working]>.badge:after{content:"";border:2px solid #06101e;border-right-color:#0000;border-radius:50%;width:8px;height:8px;animation:.8s linear infinite spin}[data-mark=waiting]>.badge{animation:1.2s ease-in-out infinite hop}.meta{min-width:0;display:grid}.name{white-space:nowrap;text-overflow:ellipsis;font-weight:600;overflow:hidden}.line{color:var(--dim);align-items:baseline;gap:8px;min-width:0;font-size:12px;display:flex}.role{letter-spacing:.1em;text-transform:uppercase;color:color-mix(in srgb, var(--c) 70%, var(--dim));font-size:10px;font-style:normal}.note{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.unit[data-mark=idle]>.card{opacity:.85}.subs{margin:6px 0 0 22px;padding-left:18px;position:relative}.subs:empty{display:none}.subs:before{content:"";background:repeating-linear-gradient(#7a98e080 0 4px,#0000 4px 8px);width:1px;position:absolute;top:-4px;bottom:22px;left:3px}.subs>.unit>.card:before{content:"";background:repeating-linear-gradient(90deg,#7a98e080 0 4px,#0000 4px 8px);width:15px;height:1px;position:absolute;top:50%;left:-15px}.subs>.unit{position:relative}.subs .plate{--s:30px}.subs .name{font-size:13px}.side{gap:16px;display:grid;position:sticky;top:96px}.panel{border:1px solid var(--line);background:var(--panel);border-radius:18px;padding:14px 16px}.panel h2{color:var(--dim);letter-spacing:.14em;text-transform:uppercase;margin-bottom:10px;font-size:11px}.net-log summary{cursor:pointer;justify-content:space-between;align-items:center;list-style:none;display:flex}.net-log summary::-webkit-details-marker{display:none}.net-log summary:after{content:"▾";color:var(--dim);transition:transform .2s}.net-log:not([open]) summary:after{transform:rotate(-90deg)}.net-log:not([open]) h2{margin-bottom:0}.log{gap:7px;max-height:360px;padding:0;list-style:none;display:grid;overflow:hidden}.log:empty:before{content:"Waiting for changes…";color:var(--dim);font-size:12px}.log li{grid-template-columns:auto 1fr;gap:2px 8px;font-size:12px;animation:.4s ease-out both slide;display:grid}.log li:before{content:"";background:var(--c);border-radius:50%;width:8px;height:8px;margin-top:5px}.log time{font:11px var(--mono);color:var(--dim);grid-column:2}.credit{color:var(--dim);padding:0 6px;font-size:11px}.legend{gap:8px;padding:0;font-size:12px;list-style:none;display:grid}.legend li{align-items:center;gap:9px;display:flex}.enter{animation:.5s cubic-bezier(.2,.9,.3,1.15) both pop}.leaving{pointer-events:none;animation:.35s ease-in both out}.flash>.card>.plate,.flash.tower{animation:.6s ease-out flash}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{0%{opacity:.6;transform:scale(1)}to{opacity:0;transform:scale(1.7)}}@keyframes hop{50%{transform:translateY(-3px)}}@keyframes blink{50%{opacity:.35}}@keyframes pop{0%{opacity:0;transform:scale(.92)translateY(8px)}}@keyframes out{to{opacity:0;transform:scale(.94)}}@keyframes flash{40%{transform:scale(1.12)}}@keyframes slide{0%{opacity:0;transform:translate(10px)}}@keyframes zoom{0%{opacity:0;transform:scale(.45)}}[hidden]{display:none!important}a{color:inherit}.crumbs,.tally dt,.comms-head small,.tx.you>header,.notice em,.rule summary,.dossier-in>.tag,.d-drones h4,.specs dt,.stop,.dialog .tag,.intel h4,.pending b{color:var(--dim);letter-spacing:.14em;text-transform:uppercase;font-size:11px;font-style:normal}.token .name,.drone .note,.count small,.sum,.where,.tile b{white-space:nowrap;text-overflow:ellipsis;max-width:100%;overflow:hidden}.md a,.bubble a,.notice a,.d-note a,.specs a,.crumbs a{color:#38e0ff}.crumbs{gap:10px;margin:14px 6px 0;display:flex}.crumbs>*+:before{content:"›";opacity:.5;margin-right:10px}.crumbs a,.brand{text-decoration:none}.work{align-items:flex-start;gap:20px;min-width:0;display:flex}.view{flex:1 1 0;min-width:0}body[data-stale=true] .work{opacity:.45}.enter-zoom{transform-origin:var(--ox,50%) var(--oy,0);animation:.5s ease-out both zoom}.enter-slide,.dossier{animation:.4s ease-out both slide}.enter-back{animation:.4s ease-out both pop}.hit,.enter-base:after{z-index:1;border-radius:inherit;position:absolute;inset:0}.enter-base:after{content:"";z-index:0}.hit:hover{box-shadow:inset 0 0 0 1px var(--c)}.base>header{position:relative}.enter-base{text-decoration:none}.base>header:hover h2{color:#38e0ff}.alerts{width:230px;color:inherit;font:inherit;text-align:left;cursor:pointer;align-items:center;gap:10px;display:flex;position:relative}.alerts:disabled{cursor:default;opacity:.6}.alerts:not(:disabled){border-color:var(--c4);background:var(--c1)}.faces{flex:none;padding-left:10px;display:flex}.faces .plate{--s:28px;margin-left:-10px;box-shadow:0 0 0 2px #0e1628}.faces .badge{display:none}.count{min-width:0;display:grid}.count b{font:600 18px/1.1 var(--mono)}.count small,.comms-head p,.cmp-note,.where,.queued,.d-line,.who p{color:var(--dim);font-size:12px}.token{border:1px solid #0000;border-radius:16px;justify-items:center;gap:3px;width:100%;padding:10px 8px 9px;text-decoration:none;display:grid;position:relative}a.token:hover{border-color:var(--c4)}.token[data-mark=waiting]{background:#ffb02e1c}.token[data-mark=error]{background:#ff54701c}.token .plate{--s:64px;margin-bottom:4px}.token .name{font-weight:600}.token .line{justify-items:center;max-width:100%;display:grid}.drone{width:94px;padding:6px;font-size:12px}.drone .plate{--s:44px}.drone .role{display:none}.flash.token>.plate{animation:.6s ease-out flash}.zoom-in,.comms-in{border:1px solid var(--line);background:var(--grid), #080d1a8c;border-radius:22px;position:relative}.zoom-in{min-height:420px;padding:24px}.zoom-in.void>:not(.lost){display:none}.lost,.chat-empty{color:var(--dim);text-align:center;padding:60px 0}.keep{align-items:center;gap:20px;padding:18px 22px;display:flex}.keep h2{font-size:24px}.tower.big{width:120px;height:120px}.tower.big svg{width:72px;height:72px}.tower.big>.badge{--s:96px;bottom:4px;right:4px}a.tower:hover:before{border-color:var(--c)}.tally{gap:10px;display:flex}.squads,.camps{grid-template-columns:repeat(auto-fit,minmax(min(100%,430px),1fr));align-items:start;gap:18px;margin-top:20px;display:grid}.squad{background:#0c1324c7;padding:14px 16px 18px}.squad[data-mark=waiting]{background:#262121d9}.squad h3{align-items:center;gap:10px;margin-bottom:14px;display:flex}.squad h3 b,.crumbs b{color:var(--text)}.squad h3 small{margin-left:auto}.squad h3 svg{width:18px;height:18px}.flag{background:var(--c);width:10px;height:10px;box-shadow:0 0 8px var(--c);border-radius:2px;transform:rotate(45deg)}.formation{flex-wrap:wrap;align-items:flex-start;gap:16px 20px;display:flex}.col{justify-items:center;gap:8px;width:196px;display:grid}.say{width:100%;color:var(--dim);background:#111a2f;border-radius:12px;margin-top:4px;padding:7px 10px;font-size:12px;line-height:1.35}.say p{overflow-wrap:anywhere;max-height:4.05em;overflow:hidden}.drones{flex-wrap:wrap;justify-content:center;gap:8px;width:100%;padding-top:16px;display:flex;position:relative}.drones:empty{display:none}.drones:before{content:"";background:repeating-linear-gradient(#7a98e099 0 4px,#0000 4px 8px);width:1px;height:14px;position:absolute;top:0;left:50%}.camp .token{width:156px}[data-group=outside] .plate{filter:saturate(.3);outline-offset:4px;outline:1px dashed #8b99ba8c}.comms-in,.tile{height:calc(var(--vvh,100vh) - 190px);grid-template-rows:auto minmax(0,1fr) auto;min-height:380px;display:grid;overflow:hidden}.comms-head,.tile>header{background:var(--panel);align-items:center;gap:12px;padding:12px 18px;display:flex}.ava{color:#38e0ff;background:radial-gradient(circle at 35% 30%,#38e0ff47,#0b1222 72%);border-radius:50%;flex:none;place-items:center;width:30px;height:30px;display:grid}.ava svg{width:58%;height:58%}.comms-head h2{font-size:16px}.chan{margin-left:auto}.chat-root{min-height:0;display:grid;position:relative}.chat-scroll{overflow-anchor:none;padding:16px 20px;overflow-y:auto}.chat-list{gap:14px;display:grid}.chat-top{height:1px}.chat-live{color:var(--c);align-items:center;gap:10px;margin-top:14px;padding-left:44px;font-size:12px;display:flex}.chat-pill{--c:#38e0ff;border:0;position:absolute;bottom:14px;left:50%;transform:translate(-50%)}.lit:after{content:"";background:#38e0ff2e;border-radius:14px;animation:1s 1s forwards out;position:absolute;inset:0}.composer{border-top:1px solid var(--line);background:var(--panel);gap:8px;padding:12px 18px 14px;display:grid}.cmp-row,.cmp-acts,.cmp-foot,.cmp-note{align-items:center;gap:8px;display:flex}.cmp-row{align-items:flex-end;gap:10px}.composer textarea{resize:none;border:1px solid var(--line);min-width:0;color:var(--text);font:14px/1.4 var(--sans);background:#070c18;border-radius:12px;flex:1;padding:9px 12px}.composer textarea:focus{border-color:#38e0ff80;outline:none;box-shadow:0 0 0 3px #38e0ff1f}.composer button:disabled{opacity:.45;cursor:default}.cmp-note{flex-wrap:wrap;flex:1;min-height:18px}.cmp-note[data-tone=error],.cmp-alt.cmp-stop{color:#ff8a9c}.cmp-note[data-tone=ok]{color:#3ddc97}.cmp-note[data-tone=ask]{color:var(--gold)}.cmp-link{color:#38e0ff;font:12px var(--sans);cursor:pointer;background:0 0;border:0;padding:0}.cmp-yes{background:var(--gold);color:#06101e;font:600 12px var(--sans);cursor:pointer;border:0;border-radius:8px;padding:3px 10px}.tx{content-visibility:auto;contain-intrinsic-size:auto 90px;min-width:0}.tx time,.tx.note{color:var(--dim);font:11px var(--mono)}.tx.you{justify-items:end;gap:5px;display:grid}.tx.you>header{color:var(--gold);align-items:center;gap:8px;display:flex}.tx.you .ava{color:var(--gold)}.bubble{white-space:pre-wrap;overflow-wrap:anywhere;background:#ffd16614;border:1px solid #ffd16638;border-radius:14px;max-width:80%;padding:9px 13px}.tx.in{grid-template-columns:34px minmax(0,1fr);align-items:start;gap:10px;display:grid}.tx-body{border:1px solid var(--line);background:#0e1628d9;border-radius:14px;gap:8px;padding:10px 14px;display:grid}.tx-body,.tx-body>*{min-width:0}.tx-body>header{color:var(--dim);font:11px var(--mono);align-items:baseline;gap:10px;display:flex}.tx-body>header b{color:#38e0ff;font:600 13px var(--sans)}.stop,.dialog .tag{background:var(--c1);color:var(--c);border-radius:999px;padding:1px 8px;font-size:10px}.fault{color:#ff8a9c;font-size:13px}.thought,.more,.tab-btn{border:1px solid var(--line);color:var(--dim);font:11px var(--sans);cursor:pointer;background:0 0;border-radius:999px;justify-self:start;padding:2px 10px}.thought-text:empty{display:none}.thought-text,.out,.args,.notice pre,.rule pre{white-space:pre-wrap;overflow-wrap:anywhere;color:var(--dim);font:12px/1.4 var(--mono)}.act,.notice{background:#ffffff08;border:1px solid #ffffff12;border-radius:10px}.act>summary,.notice>summary,.rule summary{cursor:pointer;align-items:center;gap:9px;min-width:0;padding:6px 10px;font-size:12.5px;list-style:none;display:flex}summary::-webkit-details-marker{display:none}.act code{color:var(--c);font:600 11px var(--mono);letter-spacing:.06em;text-transform:uppercase}.sum{min-width:0;color:var(--dim)}.act[open] .sum{white-space:normal}.open{flex:none;margin-left:auto}.act>pre,.result{margin:0 10px 8px}.result{gap:6px;display:grid}.tx.sys{padding-left:44px}.notice{background:#966eff12;border-color:#966eff33}.notice .ava{width:22px;height:22px}.notice pre,.rule pre{padding:0 10px 8px}.rule summary{justify-content:center}.rule summary:before,.rule summary:after{content:"";background:var(--line);flex:1;height:1px}.tx.note{text-align:center}.dossier-in{text-align:center;justify-items:center;gap:6px;display:grid}.dossier-in>.tag{text-align:left;justify-self:stretch}.portrait{background:radial-gradient(var(--c1), transparent 70%);border-radius:50%;padding:8px}.portrait .plate{--s:96px}.dossier-in h3{overflow-wrap:anywhere;margin-top:4px;font-size:18px}.d-line,.who p{align-items:baseline;gap:8px;display:flex}.d-note{background:#ffb02e1f;border-radius:10px;padding:8px 10px;font-size:12px}.specs{text-align:left;justify-self:stretch;gap:2px;margin-top:10px;display:grid}.specs div{background:#ffffff09;grid-template-columns:76px minmax(0,1fr);gap:8px;padding:7px 10px;display:grid}.specs dt{font-size:10px}.specs dd{overflow-wrap:anywhere;font-size:12.5px}.d-drones{margin-top:8px}.d-drones .formation{justify-content:center;gap:6px}.md{overflow-wrap:anywhere;line-height:1.5}.md p,.md ul,.md ol,.md pre,.md table,.md blockquote{margin:0 0 10px}.md>:last-child{margin-bottom:0}.md :is(h1,h2,h3,h4,h5,h6){margin:14px 0 6px;font-size:15px}.md>:first-child{margin-top:0}.md h1{font-size:19px}.md ul,.md ol{padding-left:22px}.md li>p{margin:0}.md li.task{margin-left:-18px;list-style:none}.check{vertical-align:-2px;text-align:center;width:14px;height:14px;font:700 10px/12px var(--mono);border:1px solid;border-radius:4px;margin-right:7px;display:inline-block}.check[data-done=true]{color:#06101e;background:#3ddc97}.md code{font:12.5px var(--mono);background:#ffffff14;border-radius:5px;padding:1px 5px}.md pre{border:1px solid var(--line);background:#070c18;border-radius:10px;padding:10px 12px;overflow-x:auto}.md pre code{background:0 0;padding:0}.md blockquote{color:var(--dim);background:#ffffff0a;padding:6px 12px}.md table{border-collapse:collapse;display:block;overflow-x:auto}.md th,.md td{border:1px solid var(--line);padding:4px 10px}.tiles{flex:1 1 0;gap:20px;min-width:0;display:flex}.tile-tabs{display:none}.tile{border:1px solid var(--line);background:var(--panel);border-radius:22px;flex:1 1 0;min-width:0;position:sticky;top:96px}.tile>header svg{width:22px;height:22px;color:var(--gold)}.tile-body{padding:16px 20px;overflow:auto}.tiles.tabbed{flex-direction:column;gap:8px}.tiles.tabbed .tile-tabs{gap:6px;display:flex}.tiles.tabbed .tile:not(.active){display:none}.tab-btn.active,.open{color:var(--gold);border-color:#ffd16666}.dock{z-index:20;pointer-events:none;width:min(300px,100vw - 44px);display:grid;position:fixed;top:108px;right:max(22px,50vw - 718px)}.dialog{pointer-events:auto;border:1px solid var(--c4);background:linear-gradient(var(--c1), #0c1324 70%), #121a30;transform-origin:top;border-radius:18px;grid-area:1/1;padding:14px 16px 16px;transition:transform .3s,opacity .3s;position:relative;box-shadow:0 24px 60px #000c}.dialog:focus{outline:none}.dialog>header{align-items:center;gap:8px;min-width:0;margin-bottom:12px;display:flex}.where{min-width:0}.x{color:var(--dim);cursor:pointer;background:0 0;border:0;margin-left:auto;font-size:20px}.who{align-items:center;gap:14px;display:flex}.who .plate{--s:56px}.who h3{font-size:17px}.intel{background:#060a148c;border-radius:12px;margin-top:12px;padding:10px 12px}.intel h4{margin-bottom:4px}.said{overflow-wrap:anywhere;font-size:13px;line-height:1.45}.pending{color:var(--c);font:12px var(--mono);overflow-wrap:anywhere;margin-top:8px}.dialog footer{align-items:center;gap:10px;margin-top:14px;display:flex}.ask{background:#ffb02e0f;border:1px solid #ffb02e59;border-radius:14px;gap:8px;margin:10px 18px 0;padding:12px 14px;display:grid}.dialog .ask{margin:10px 0 0}.ask-q{white-space:pre-wrap;overflow-wrap:anywhere;font-weight:600}.ask-ctx,.ask-why{color:var(--dim);white-space:pre-wrap;overflow-wrap:anywhere;max-height:9em;font-size:13px;overflow:auto}.ask-opts{counter-reset:opt;gap:6px;max-height:16em;margin:0;padding:0;list-style:none;display:grid;overflow:auto}.ask-opt{border:1px solid var(--line);overflow-wrap:anywhere;counter-increment:opt;background:#070c18;border-radius:10px;gap:2px;padding:7px 12px;font-size:13px;display:grid}.ask-opt b:before{content:counter(opt) ". ";color:var(--dim)}.ask-opt small{color:var(--dim);font-size:12px}.ask-acts{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:6px 12px;display:flex}.ask-term{color:var(--gold);font-size:12px}.ask button:disabled{opacity:.45;cursor:default}.ask-note:empty{display:none}.ask-note{color:var(--dim);font-size:12px}.ask-note[data-tone=error]{color:#ff8a9c}.go,.later,.chat-pill,.cmp-send,.cmp-alt{font:600 13px var(--sans);cursor:pointer;border-radius:10px;padding:7px 13px;text-decoration:none}.go,.chat-pill{background:var(--c);color:#06101e}.cmp-send{color:#06101e;background:#38e0ff;border:0}.later,.cmp-alt{border:1px solid var(--line);color:var(--text);background:0 0}.queued{margin-left:auto}html.paused *,html.paused :before,html.paused :after{animation-play-state:paused!important}@media (width<=1100px){.stage{grid-template-columns:1fr}.side{position:static}.stats{display:none}}@media (width<=900px){.work{flex-direction:column;align-items:stretch}.view,.tiles{flex:none}.tile{position:static}.keep{flex-wrap:wrap}}@media (width<=640px){.shell{padding:max(8px, env(safe-area-inset-top)) max(8px, env(safe-area-inset-right)) calc(24px + env(safe-area-inset-bottom)) max(8px, env(safe-area-inset-left))}.hud{flex-wrap:wrap;gap:10px;padding:10px 12px;position:static}.brand p,.link span{display:none}.alerts{flex:100%;order:3;width:auto}.map,.zoom-in{padding:14px}.bases{gap:18px}.keep{gap:12px;padding:6px}.tower.big{width:84px;height:84px}.tally{flex-wrap:wrap}.col{width:100%}.comms-in,.tile{height:calc(var(--vvh,100dvh) - 16px);border-radius:16px;min-height:300px}.chat-scroll{padding:12px}.bubble{max-width:92%}.composer{padding:10px 12px calc(10px + env(safe-area-inset-bottom))}.cmp-row{flex-wrap:wrap}.composer textarea{flex:100%;min-height:64px}.cmp-acts{margin-left:auto}.log{max-height:none}.log li:nth-child(n+3){display:none}.dock{top:auto;right:10px;bottom:calc(10px + env(safe-area-inset-bottom));width:calc(100vw - 20px)}}@media (pointer:coarse){.composer textarea{font-size:16px}.composer button,.go,.later,.x,.chat-pill,.pair-btn,.alerts,.ask button{min-height:40px}}@media (prefers-reduced-motion:reduce){*,:before,:after{transition:none!important;animation:none!important}}
1
+ :root{--bg:#060a14;--panel:#0e1628e0;--line:#7a98e033;--text:#dbe6ff;--dim:#8b99ba;--gold:#ffd166;--grid:linear-gradient(#82a0ff0d 1px, transparent 1px) 0 0 / 44px 44px, linear-gradient(90deg, #82a0ff0d 1px, transparent 1px) 0 0 / 44px 44px;--mono:ui-monospace, "SF Mono", Menlo, Consolas, monospace;--sans:ui-rounded, "Avenir Next", "Trebuchet MS", system-ui, -apple-system, "Segoe UI", sans-serif}*{box-sizing:border-box;margin:0}html{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}body{min-height:100vh;color:var(--text);font:14px/1.4 var(--sans);background:radial-gradient(1100px 640px at 12% -10%, #38e0ff1c, transparent 62%), radial-gradient(900px 600px at 98% 8%, #966eff1c, transparent 60%), linear-gradient(var(--bg), #0a1020);background-attachment:fixed}svg{display:block}[data-mark=working]{--c:#38e0ff}[data-mark=waiting]{--c:#ffb02e}[data-mark=idle]{--c:#7184ad}[data-mark=done]{--c:#3ddc97}[data-mark=error]{--c:#ff5470}[data-mark=unknown]{--c:#4f5c7c}[data-mark]{--c1:color-mix(in srgb, var(--c) 13%, transparent);--c4:color-mix(in srgb, var(--c) 45%, transparent)}:focus-visible{outline-offset:2px;outline:2px solid #38e0ff}.shell{max-width:1480px;margin:0 auto;padding:18px 22px 40px}.hud{border:1px solid var(--line);background:var(--panel);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);z-index:5;border-radius:18px;align-items:center;gap:24px;padding:12px 18px;display:flex;position:sticky;top:12px}.brand{align-items:center;gap:12px;margin-right:auto;display:flex}.logo{color:#38e0ff;border:1px solid var(--line);background:radial-gradient(circle at 30% 25%,#38e0ff4d,#0a1020e6);border-radius:14px;place-items:center;width:44px;height:44px;display:grid}.logo svg{filter:drop-shadow(0 0 6px #38e0ff);width:28px;height:28px}.brand h1{letter-spacing:.14em;text-transform:uppercase;font-size:20px}.brand p{color:var(--dim);font-size:12px}.stats{gap:10px;display:flex}.stats div,.tally div,.alerts{background:#ffffff0a;border:1px solid #ffffff0f;border-radius:12px;min-width:78px;padding:6px 12px}.stats dt,.tally dt{color:var(--dim);letter-spacing:.08em;text-transform:uppercase;font-size:11px}.stats dd,.tally dd{font:600 20px/1.1 var(--mono)}.stats [data-mark] dd,.tally [data-mark] dd,.count b{color:var(--c)}.pair-btn{border:1px solid var(--line);color:var(--dim);font:600 12px var(--sans);letter-spacing:.08em;text-transform:uppercase;cursor:pointer;background:0 0;border-radius:12px;padding:6px 12px}.pair{text-align:center;color:var(--dim);justify-items:center;gap:12px;font-size:13px;display:grid}.qr{shape-rendering:crispedges;border-radius:12px;width:min(100%,300px)}.link{color:var(--dim);text-transform:uppercase;letter-spacing:.08em;align-items:center;gap:8px;font-size:12px;display:flex}.link i{background:#4f5c7c;border-radius:50%;width:9px;height:9px}.link[data-state=live] i{background:#3ddc97;animation:2.4s ease-in-out infinite blink}.link[data-state=live]{color:#3ddc97}.link[data-state=reconnecting] i,.link[data-state=connecting] i{background:#ffb02e;animation:1s ease-in-out infinite blink}.link[data-state=offline] i{background:#ff5470}.banner{color:#c9d6f5;background:#7184ad29;border-radius:14px;margin:14px 0 0;padding:10px 16px;font-size:13px}.banner[hidden]{display:none}.banner b{font-family:var(--mono);font-weight:600}.stage{grid-template-columns:minmax(0,1fr) 300px;align-items:start;gap:20px;margin-top:18px;transition:opacity .4s;display:grid}.stage[data-stale=true] .map,body[data-stale=true] .stats{opacity:.4}.stage[data-stale=true] *{animation-play-state:paused!important}.map{border:1px solid var(--line);background:var(--grid), #080d1a8c;border-radius:22px;min-height:420px;padding:26px;transition:opacity .4s;position:relative}.links{pointer-events:none;width:100%;height:100%;position:absolute;inset:0}.wire{fill:none;stroke:#38e0ff4d;stroke-width:1.5px;stroke-dasharray:3 7;stroke-linecap:round}.packet{fill:#38e0ff;opacity:.9}.bases{grid-template-columns:repeat(auto-fill,minmax(min(100%,300px),1fr));align-items:start;gap:30px;display:grid;position:relative}.empty{color:var(--dim);text-align:center;padding:120px 0;position:relative}.base{border:1px solid var(--line);background:linear-gradient(#16213c,#0c1324);border-radius:18px;padding:16px;position:relative;box-shadow:0 18px 40px #00000059}.base.head{background:linear-gradient(#ffd1661a,#0000 38%),linear-gradient(#16213c,#0c1324)}.base[data-mark=waiting]{box-shadow:0 18px 40px #00000059,inset 0 0 0 999px #ffb02e0b}.base>header{align-items:center;gap:14px;margin-bottom:14px;display:flex}.title{justify-items:start;min-width:0;margin-right:auto;display:grid}.title h2{white-space:nowrap;text-overflow:ellipsis;max-width:100%;font-size:17px;overflow:hidden}.title p{max-width:100%;color:var(--dim);white-space:nowrap;text-overflow:ellipsis;font-size:12px;overflow:hidden}.crown{letter-spacing:.12em;text-transform:uppercase;color:var(--gold);background:#ffd1661f;border-radius:999px;align-items:center;gap:5px;margin-bottom:3px;padding:2px 9px 2px 6px;font-size:10px;display:inline-flex}.crown svg{width:14px;height:14px}.base:not(.head) .crown{display:none}.tower{width:80px;height:80px;color:var(--c);flex:none;place-items:center;display:grid;position:relative}.tower:before{content:"";background:radial-gradient(circle at 35% 30%, color-mix(in srgb, var(--c) 24%, #101a2e), #0b1222 70%);border:1px solid color-mix(in srgb, var(--c) 45%, transparent);border-radius:50%;position:absolute;inset:0}.tower svg{width:48px;height:48px;filter:drop-shadow(0 0 6px color-mix(in srgb, var(--c) 70%, transparent));position:relative}.tower.ghost{opacity:.45}.tower.ghost:before{border-style:dashed}.sweep,.pulse{opacity:0;pointer-events:none;border-radius:50%;position:absolute;inset:0}.sweep{background:conic-gradient(from 0deg, transparent 0 70%, color-mix(in srgb, var(--c) 55%, transparent) 100%)}.pulse{border:2px solid var(--c)}.tower[data-mark=working] .sweep{opacity:1;animation:3.2s linear infinite spin}.tower[data-mark=waiting] .pulse{animation:1.5s ease-out infinite ping}.tower>.badge{--s:40px;position:absolute;bottom:-2px;right:-2px}.tabs{gap:12px;display:grid}.tab{background:#ffffff06;border:1px solid #ffffff0d;border-radius:14px;padding:10px}.tab h3{color:var(--dim);letter-spacing:.12em;text-transform:uppercase;margin-bottom:8px;font-size:11px;font-weight:600}.units,.subs{gap:8px;display:grid}.outposts{gap:8px;margin-top:12px;display:grid}.outposts:empty{display:none}.outposts:before{content:"Outposts";color:var(--dim);letter-spacing:.12em;text-transform:uppercase;font-size:11px}.card{background:#ffffff09;border:1px solid #ffffff0f;border-radius:12px;align-items:center;gap:10px;padding:8px 10px;transition:background-color .4s;display:flex;position:relative}.unit[data-mark=waiting]>.card{background:#ffb02e1c}.unit[data-mark=error]>.card{background:#ff54701c}.unit[data-mark=working]>.card{background:#38e0ff12}.plate{width:var(--s,40px);height:var(--s,40px);color:var(--c);background:radial-gradient(circle at 35% 30%, color-mix(in srgb, var(--c) 26%, #101a2e), #0b1222 72%);border-radius:50%;flex:none;place-items:center;transition:color .4s;display:grid;position:relative}.plate svg{width:calc(var(--s,40px) * .55);height:calc(var(--s,40px) * .55)}.ring,.plate:after{pointer-events:none;border-radius:50%;position:absolute;inset:-3px}.ring{opacity:0;background:conic-gradient(from 0deg, transparent 0 55%, var(--c));-webkit-mask:radial-gradient(farthest-side,#0000 calc(100% - 3px),#000 calc(100% - 2px));mask:radial-gradient(farthest-side,#0000 calc(100% - 3px),#000 calc(100% - 2px))}.plate[data-mark=working]>.ring{opacity:1;animation:1.1s linear infinite spin}.plate:after{content:"";border:2px solid var(--c);opacity:0}.plate[data-mark=waiting]:after{animation:1.5s ease-out infinite ping}.badge{width:clamp(16px, calc(var(--s,40px) * .3), 24px);height:clamp(16px, calc(var(--s,40px) * .3), 24px);font:700 10px/1 var(--mono);color:#06101e;background:var(--c);border-radius:50%;flex:none;place-items:center;transition:background-color .4s;display:grid}.plate>.badge{position:absolute;bottom:-4px;right:-4px}[data-mark=waiting]>.badge:after{content:"?"}[data-mark=idle]>.badge:after{content:"z"}[data-mark=done]>.badge:after{content:"✓"}[data-mark=error]>.badge:after{content:"!"}[data-mark=unknown]>.badge:after{content:"·"}[data-mark=working]>.badge:after{content:"";border:2px solid #06101e;border-right-color:#0000;border-radius:50%;width:8px;height:8px;animation:.8s linear infinite spin}[data-mark=waiting]>.badge{animation:1.2s ease-in-out infinite hop}.meta{min-width:0;display:grid}.name{white-space:nowrap;text-overflow:ellipsis;font-weight:600;overflow:hidden}.line{color:var(--dim);align-items:baseline;gap:8px;min-width:0;font-size:12px;display:flex}.role{letter-spacing:.1em;text-transform:uppercase;color:color-mix(in srgb, var(--c) 70%, var(--dim));font-size:10px;font-style:normal}.note{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.unit[data-mark=idle]>.card{opacity:.85}.subs{margin:6px 0 0 22px;padding-left:18px;position:relative}.subs:empty{display:none}.subs:before{content:"";background:repeating-linear-gradient(#7a98e080 0 4px,#0000 4px 8px);width:1px;position:absolute;top:-4px;bottom:22px;left:3px}.subs>.unit>.card:before{content:"";background:repeating-linear-gradient(90deg,#7a98e080 0 4px,#0000 4px 8px);width:15px;height:1px;position:absolute;top:50%;left:-15px}.subs>.unit{position:relative}.subs .plate{--s:30px}.subs .name{font-size:13px}.side{gap:16px;display:grid;position:sticky;top:96px}.panel{border:1px solid var(--line);background:var(--panel);border-radius:18px;padding:14px 16px}.panel h2{color:var(--dim);letter-spacing:.14em;text-transform:uppercase;margin-bottom:10px;font-size:11px}.net-log summary{cursor:pointer;justify-content:space-between;align-items:center;list-style:none;display:flex}.net-log summary::-webkit-details-marker{display:none}.net-log summary:after{content:"▾";color:var(--dim);transition:transform .2s}.net-log:not([open]) summary:after{transform:rotate(-90deg)}.net-log:not([open]) h2{margin-bottom:0}.log{gap:7px;max-height:360px;padding:0;list-style:none;display:grid;overflow:hidden}.log:empty:before{content:"Waiting for changes…";color:var(--dim);font-size:12px}.log li{grid-template-columns:auto 1fr;gap:2px 8px;font-size:12px;animation:.4s ease-out both slide;display:grid}.log li:before{content:"";background:var(--c);border-radius:50%;width:8px;height:8px;margin-top:5px}.log time{font:11px var(--mono);color:var(--dim);grid-column:2}.credit{color:var(--dim);padding:0 6px;font-size:11px}.legend{gap:8px;padding:0;font-size:12px;list-style:none;display:grid}.legend li{align-items:center;gap:9px;display:flex}.enter{animation:.5s cubic-bezier(.2,.9,.3,1.15) both pop}.leaving{pointer-events:none;animation:.35s ease-in both out}.flash>.card>.plate,.flash.tower{animation:.6s ease-out flash}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{0%{opacity:.6;transform:scale(1)}to{opacity:0;transform:scale(1.7)}}@keyframes hop{50%{transform:translateY(-3px)}}@keyframes blink{50%{opacity:.35}}@keyframes pop{0%{opacity:0;transform:scale(.92)translateY(8px)}}@keyframes out{to{opacity:0;transform:scale(.94)}}@keyframes flash{40%{transform:scale(1.12)}}@keyframes slide{0%{opacity:0;transform:translate(10px)}}@keyframes zoom{0%{opacity:0;transform:scale(.45)}}[hidden]{display:none!important}a{color:inherit}.crumbs,.tally dt,.comms-head small,.tx.you>header,.notice em,.rule summary,.dossier-in>.tag,.d-drones h4,.specs dt,.stop,.dialog .tag,.intel h4,.pending b{color:var(--dim);letter-spacing:.14em;text-transform:uppercase;font-size:11px;font-style:normal}.token .name,.drone .note,.count small,.sum,.where,.tile b{white-space:nowrap;text-overflow:ellipsis;max-width:100%;overflow:hidden}.md a,.bubble a,.notice a,.d-note a,.specs a,.crumbs a{color:#38e0ff}.crumbs{gap:10px;margin:14px 6px 0;display:flex}.crumbs>*+:before{content:"›";opacity:.5;margin-right:10px}.crumbs a,.brand{text-decoration:none}.work{align-items:flex-start;gap:20px;min-width:0;display:flex}.view{flex:1 1 0;min-width:0}body[data-stale=true] .work{opacity:.45}.enter-zoom{transform-origin:var(--ox,50%) var(--oy,0);animation:.5s ease-out both zoom}.enter-slide,.dossier{animation:.4s ease-out both slide}.enter-back{animation:.4s ease-out both pop}.hit,.enter-base:after{z-index:1;border-radius:inherit;position:absolute;inset:0}.enter-base:after{content:"";z-index:0}.hit:hover{box-shadow:inset 0 0 0 1px var(--c)}.base>header{position:relative}.enter-base{text-decoration:none}.base>header:hover h2{color:#38e0ff}.alerts{width:230px;color:inherit;font:inherit;text-align:left;cursor:pointer;align-items:center;gap:10px;display:flex;position:relative}.alerts:disabled{cursor:default;opacity:.6}.alerts:not(:disabled){border-color:var(--c4);background:var(--c1)}.faces{flex:none;padding-left:10px;display:flex}.faces .plate{--s:28px;margin-left:-10px;box-shadow:0 0 0 2px #0e1628}.faces .badge{display:none}.count{min-width:0;display:grid}.count b{font:600 18px/1.1 var(--mono)}.count small,.comms-head p,.cmp-note,.where,.queued,.d-line,.who p{color:var(--dim);font-size:12px}.token{border:1px solid #0000;border-radius:16px;justify-items:center;gap:3px;width:100%;padding:10px 8px 9px;text-decoration:none;display:grid;position:relative}a.token:hover{border-color:var(--c4)}.token[data-mark=waiting]{background:#ffb02e1c}.token[data-mark=error]{background:#ff54701c}.token .plate{--s:64px;margin-bottom:4px}.token .name{font-weight:600}.token .line{justify-items:center;max-width:100%;display:grid}.drone{width:94px;padding:6px;font-size:12px}.drone .plate{--s:44px}.drone .role{display:none}.flash.token>.plate{animation:.6s ease-out flash}.zoom-in,.comms-in{border:1px solid var(--line);background:var(--grid), #080d1a8c;border-radius:22px;position:relative}.zoom-in{min-height:420px;padding:24px}.zoom-in.void>:not(.lost){display:none}.lost,.chat-empty{color:var(--dim);text-align:center;padding:60px 0}.keep{align-items:center;gap:20px;padding:18px 22px;display:flex}.keep h2{font-size:24px}.tower.big{width:120px;height:120px}.tower.big svg{width:72px;height:72px}.tower.big>.badge{--s:96px;bottom:4px;right:4px}a.tower:hover:before{border-color:var(--c)}.tally{gap:10px;display:flex}.squads,.camps{grid-template-columns:repeat(auto-fit,minmax(min(100%,430px),1fr));align-items:start;gap:18px;margin-top:20px;display:grid}.squad{background:#0c1324c7;padding:14px 16px 18px}.squad[data-mark=waiting]{background:#262121d9}.squad h3{align-items:center;gap:10px;margin-bottom:14px;display:flex}.squad h3 b,.crumbs b{color:var(--text)}.squad h3 small{margin-left:auto}.squad h3 svg{width:18px;height:18px}.flag{background:var(--c);width:10px;height:10px;box-shadow:0 0 8px var(--c);border-radius:2px;transform:rotate(45deg)}.formation{flex-wrap:wrap;align-items:flex-start;gap:16px 20px;display:flex}.col{justify-items:center;gap:8px;width:196px;display:grid}.say{width:100%;color:var(--dim);background:#111a2f;border-radius:12px;margin-top:4px;padding:7px 10px;font-size:12px;line-height:1.35}.say p{overflow-wrap:anywhere;max-height:4.05em;overflow:hidden}.drones{flex-wrap:wrap;justify-content:center;gap:8px;width:100%;padding-top:16px;display:flex;position:relative}.drones:empty{display:none}.drones:before{content:"";background:repeating-linear-gradient(#7a98e099 0 4px,#0000 4px 8px);width:1px;height:14px;position:absolute;top:0;left:50%}.camp .token{width:156px}[data-group=outside] .plate{filter:saturate(.3);outline-offset:4px;outline:1px dashed #8b99ba8c}.comms-in,.tile{height:calc(var(--vvh,100vh) - 190px);grid-template-rows:auto minmax(0,1fr) auto;min-height:380px;display:grid;overflow:hidden}.comms-head,.tile>header{background:var(--panel);align-items:center;gap:12px;padding:12px 18px;display:flex}.ava{color:#38e0ff;background:radial-gradient(circle at 35% 30%,#38e0ff47,#0b1222 72%);border-radius:50%;flex:none;place-items:center;width:30px;height:30px;display:grid}.ava svg{width:58%;height:58%}.comms-head h2{font-size:16px}.chan{margin-left:auto}.chat-root{min-height:0;display:grid;position:relative}.chat-scroll{overflow-anchor:none;padding:16px 20px;overflow-y:auto}.chat-list{gap:14px;display:grid}.chat-top{height:1px}.chat-live{color:var(--c);align-items:center;gap:10px;margin-top:14px;padding-left:44px;font-size:12px;display:flex}.chat-pill{--c:#38e0ff;border:0;position:absolute;bottom:14px;left:50%;transform:translate(-50%)}.lit:after{content:"";background:#38e0ff2e;border-radius:14px;animation:1s 1s forwards out;position:absolute;inset:0}.composer{border-top:1px solid var(--line);background:var(--panel);gap:8px;padding:12px 18px 14px;display:grid}.cmp-row,.cmp-acts,.cmp-foot,.cmp-note{align-items:center;gap:8px;display:flex}.cmp-row{align-items:flex-end;gap:10px}.composer textarea{resize:none;border:1px solid var(--line);min-width:0;color:var(--text);font:14px/1.4 var(--sans);background:#070c18;border-radius:12px;flex:1;padding:9px 12px}.composer textarea:focus{border-color:#38e0ff80;outline:none;box-shadow:0 0 0 3px #38e0ff1f}.composer button:disabled{opacity:.45;cursor:default}.cmp-note{flex-wrap:wrap;flex:1;min-height:18px}.cmp-note[data-tone=error],.cmp-alt.cmp-stop{color:#ff8a9c}.cmp-note[data-tone=ok]{color:#3ddc97}.cmp-note[data-tone=ask]{color:var(--gold)}.cmp-link{color:#38e0ff;font:12px var(--sans);cursor:pointer;background:0 0;border:0;padding:0}.cmp-yes{background:var(--gold);color:#06101e;font:600 12px var(--sans);cursor:pointer;border:0;border-radius:8px;padding:3px 10px}.tx{content-visibility:auto;contain-intrinsic-size:auto 90px;min-width:0}.tx time,.tx.note{color:var(--dim);font:11px var(--mono)}.tx.you{justify-items:end;gap:5px;display:grid}.tx.you>header{color:var(--gold);align-items:center;gap:8px;display:flex}.tx.you .ava{color:var(--gold)}.bubble{white-space:pre-wrap;overflow-wrap:anywhere;background:#ffd16614;border:1px solid #ffd16638;border-radius:14px;max-width:80%;padding:9px 13px}.tx.in{grid-template-columns:34px minmax(0,1fr);align-items:start;gap:10px;display:grid}.tx-body{border:1px solid var(--line);background:#0e1628d9;border-radius:14px;gap:8px;padding:10px 14px;display:grid}.tx-body,.tx-body>*{min-width:0}.tx-body>header{color:var(--dim);font:11px var(--mono);align-items:baseline;gap:10px;display:flex}.tx-body>header b{color:#38e0ff;font:600 13px var(--sans)}.stop,.dialog .tag{background:var(--c1);color:var(--c);border-radius:999px;padding:1px 8px;font-size:10px}.fault{color:#ff8a9c;font-size:13px}.thought,.more,.tab-btn{border:1px solid var(--line);color:var(--dim);font:11px var(--sans);cursor:pointer;background:0 0;border-radius:999px;justify-self:start;padding:2px 10px}.thought-text:empty{display:none}.thought-text,.out,.args,.notice pre,.rule pre{white-space:pre-wrap;overflow-wrap:anywhere;color:var(--dim);font:12px/1.4 var(--mono)}.act,.notice{background:#ffffff08;border:1px solid #ffffff12;border-radius:10px}.act>summary,.notice>summary,.rule summary{cursor:pointer;align-items:center;gap:9px;min-width:0;padding:6px 10px;font-size:12.5px;list-style:none;display:flex}summary::-webkit-details-marker{display:none}.act code{color:var(--c);font:600 11px var(--mono);letter-spacing:.06em;text-transform:uppercase}.sum{min-width:0;color:var(--dim)}.act[open] .sum{white-space:normal}.open{flex:none;margin-left:auto}.act>pre,.result{margin:0 10px 8px}.result{gap:6px;display:grid}.tx.sys{padding-left:44px}.notice{background:#966eff12;border-color:#966eff33}.notice .ava{width:22px;height:22px}.notice pre,.rule pre{padding:0 10px 8px}.rule summary{justify-content:center}.rule summary:before,.rule summary:after{content:"";background:var(--line);flex:1;height:1px}.tx.note{text-align:center}.dossier-in{text-align:center;justify-items:center;gap:6px;display:grid}.dossier-in>.tag{text-align:left;justify-self:stretch}.portrait{background:radial-gradient(var(--c1), transparent 70%);border-radius:50%;padding:8px}.portrait .plate{--s:96px}.dossier-in h3{overflow-wrap:anywhere;margin-top:4px;font-size:18px}.d-line,.who p{align-items:baseline;gap:8px;display:flex}.d-note{background:#ffb02e1f;border-radius:10px;padding:8px 10px;font-size:12px}.specs{text-align:left;justify-self:stretch;gap:2px;margin-top:10px;display:grid}.specs div{background:#ffffff09;grid-template-columns:76px minmax(0,1fr);gap:8px;padding:7px 10px;display:grid}.specs dt{font-size:10px}.specs dd{overflow-wrap:anywhere;font-size:12.5px}.d-drones{margin-top:8px}.d-drones .formation{justify-content:center;gap:6px}.md{overflow-wrap:anywhere;line-height:1.5}.md p,.md ul,.md ol,.md pre,.md table,.md blockquote{margin:0 0 10px}.md>:last-child{margin-bottom:0}.md :is(h1,h2,h3,h4,h5,h6){margin:14px 0 6px;font-size:15px}.md>:first-child{margin-top:0}.md h1{font-size:19px}.md ul,.md ol{padding-left:22px}.md li>p{margin:0}.md li.task{margin-left:-18px;list-style:none}.check{vertical-align:-2px;text-align:center;width:14px;height:14px;font:700 10px/12px var(--mono);border:1px solid;border-radius:4px;margin-right:7px;display:inline-block}.check[data-done=true]{color:#06101e;background:#3ddc97}.md code{font:12.5px var(--mono);background:#ffffff14;border-radius:5px;padding:1px 5px}.md pre{border:1px solid var(--line);background:#070c18;border-radius:10px;padding:10px 12px;overflow-x:auto}.md pre code{background:0 0;padding:0}.md blockquote{color:var(--dim);background:#ffffff0a;padding:6px 12px}.md table{border-collapse:collapse;display:block;overflow-x:auto}.md th,.md td{border:1px solid var(--line);padding:4px 10px}.tiles{flex:1 1 0;gap:20px;min-width:0;display:flex}.tile-tabs{display:none}.tile{border:1px solid var(--line);background:var(--panel);border-radius:22px;flex:1 1 0;min-width:0;position:sticky;top:96px}.tile>header svg{width:22px;height:22px;color:var(--gold)}.tile-body{padding:16px 20px;overflow:auto}.tiles.tabbed{flex-direction:column;gap:8px}.tiles.tabbed .tile-tabs{gap:6px;display:flex}.tiles.tabbed .tile:not(.active){display:none}.tab-btn.active,.open{color:var(--gold);border-color:#ffd16666}.dock{z-index:20;pointer-events:none;width:min(300px,100vw - 44px);display:grid;position:fixed;top:108px;right:max(22px,50vw - 718px)}.dialog{pointer-events:auto;border:1px solid var(--c4);background:linear-gradient(var(--c1), #0c1324 70%), #121a30;transform-origin:top;border-radius:18px;grid-area:1/1;padding:14px 16px 16px;transition:transform .3s,opacity .3s;position:relative;box-shadow:0 24px 60px #000c}.dialog:focus{outline:none}.dialog>header{align-items:center;gap:8px;min-width:0;margin-bottom:12px;display:flex}.where{min-width:0}.x{color:var(--dim);cursor:pointer;background:0 0;border:0;margin-left:auto;font-size:20px}.who{align-items:center;gap:14px;display:flex}.who .plate{--s:56px}.who h3{font-size:17px}.intel{background:#060a148c;border-radius:12px;margin-top:12px;padding:10px 12px}.intel h4{margin-bottom:4px}.said{overflow-wrap:anywhere;font-size:13px;line-height:1.45}.pending{color:var(--c);font:12px var(--mono);overflow-wrap:anywhere;margin-top:8px}.dialog footer{flex-wrap:wrap;align-items:center;gap:10px;margin-top:14px;display:flex}.ask{background:#ffb02e0f;border:1px solid #ffb02e59;border-radius:14px;gap:8px;margin:10px 18px 0;padding:12px 14px;display:grid}.dialog .ask{margin:10px 0 0}.ask-one{gap:8px;display:grid}.ask-one+.ask-one{margin-top:8px}.ask-head{color:var(--gold);letter-spacing:.06em;text-transform:uppercase;background:#ffb02e24;border-radius:999px;justify-self:start;padding:1px 9px;font-size:11px}.ask-how{color:var(--dim);font-size:12px}.ask-q{white-space:pre-wrap;overflow-wrap:anywhere;font-weight:600}.ask-ctx,.ask-why{color:var(--dim);white-space:pre-wrap;overflow-wrap:anywhere;max-height:9em;font-size:13px;overflow:auto}.ask-opts{counter-reset:opt;gap:6px;max-height:16em;margin:0;padding:0;list-style:none;display:grid;overflow:auto}.ask-opt{border:1px solid var(--line);overflow-wrap:anywhere;counter-increment:opt;background:#070c18;border-radius:10px;gap:2px;padding:7px 12px;font-size:13px;display:grid}.ask-opt b:before{content:counter(opt) ". ";color:var(--dim)}.ask-opt small{color:var(--dim);font-size:12px}.ask-acts{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:6px 12px;display:flex}.ask-term{color:var(--gold);font-size:12px}.ask button:disabled{opacity:.45;cursor:default}.ask-note:empty{display:none}.ask-note{color:var(--dim);font-size:12px}.ask-note[data-tone=error]{color:#ff8a9c}.go,.later,.chat-pill,.cmp-send,.cmp-alt{font:600 13px var(--sans);cursor:pointer;border-radius:10px;padding:7px 13px;text-decoration:none}.go,.chat-pill{background:var(--c);color:#06101e}.cmp-send{color:#06101e;background:#38e0ff;border:0}.later,.cmp-alt{border:1px solid var(--line);color:var(--text);background:0 0}.queued{color:var(--dim);flex:1 0 100%;font-size:12px}.queued:empty{display:none}html.paused *,html.paused :before,html.paused :after{animation-play-state:paused!important}@media (width<=1100px){.stage{grid-template-columns:1fr}.side{position:static}.stats{display:none}}@media (width<=900px){.work{flex-direction:column;align-items:stretch}.view,.tiles{flex:none}.tile{position:static}.keep{flex-wrap:wrap}}@media (width<=640px){.shell{padding:max(8px, env(safe-area-inset-top)) max(8px, env(safe-area-inset-right)) calc(24px + env(safe-area-inset-bottom)) max(8px, env(safe-area-inset-left))}.hud{flex-wrap:wrap;gap:10px;padding:10px 12px;position:static}.brand p,.link span{display:none}.alerts{flex:100%;order:3;width:auto}.map,.zoom-in{padding:14px}.bases{gap:18px}.keep{gap:12px;padding:6px}.tower.big{width:84px;height:84px}.tally{flex-wrap:wrap}.col{width:100%}.comms-in,.tile{height:calc(var(--vvh,100dvh) - 16px);border-radius:16px;min-height:300px}.chat-scroll{padding:12px}.bubble{max-width:92%}.composer{padding:10px 12px calc(10px + env(safe-area-inset-bottom))}.cmp-row{flex-wrap:wrap}.composer textarea{flex:100%;min-height:64px}.cmp-acts{margin-left:auto}.log{max-height:none}.log li:nth-child(n+3){display:none}.dock{top:auto;right:10px;bottom:calc(10px + env(safe-area-inset-bottom));width:calc(100vw - 20px)}}@media (pointer:coarse){.composer textarea{font-size:16px}.composer button,.go,.later,.x,.chat-pill,.pair-btn,.alerts,.ask button{min-height:40px}}@media (prefers-reduced-motion:reduce){*,:before,:after{transition:none!important;animation:none!important}}
@@ -1 +1 @@
1
- var e={working:`started working`,waiting:`is waiting on a question`,idle:`went idle`,done:`finished`,error:`hit an error`,unknown:`lost its status`};function t(e){let t=new Map;for(let n of e.bases){n.tower&&t.set(`tower:${n.id}`,{name:`${n.label} tower`,mark:n.tower.mark});for(let e of n.tabs.flatMap(e=>e.units))for(let n of[e,...e.subs])t.set(n.id,{name:n.name,mark:n.mark});for(let e of n.outposts)t.set(e.id,{name:e.label,mark:e.mark})}return t}function n(n,r){if(!n)return[];let[i,a]=[t(n),t(r)],o=[];for(let[t,n]of a){let r=i.get(t);r?r.mark!==n.mark&&o.push({text:`${n.name} ${e[n.mark]}`,mark:n.mark}):o.push({text:`${n.name} joined`,mark:n.mark})}for(let[e,t]of i)a.has(e)||o.push({text:`${t.name} left`,mark:`idle`});return o}var r=e=>e===`waiting`||e===`error`?0:1;function i(e,t){let n=new Map;for(let r of e.bases)for(let e of[...r.tower?[{...r.tower,role:`tower`}]:[],...[...r.tabs.flatMap(e=>e.units),...r.outside].flatMap(e=>[e,...e.subs])])n.set(e.key,{key:e.key,ws:r.id,name:e.name,role:e.role,mark:e.mark,note:e.note,at:t,pending:e.pending});return n}function a(e,t){return[...t.values()].filter(t=>e?.get(t.key)?.mark!==t.mark&&(r(t.mark)===0||e!==void 0&&t.mark===`done`))}var o=e=>{try{return decodeURIComponent(e)}catch{return e}},s=e=>/^\.+$/.test(e)?e.replaceAll(`.`,`%2E`):encodeURIComponent(e);function c(e){let[t,n]=e.split(`#`)[0].split(`?`),[r,i,a,s]=t.replace(/^\/+|\/+$/g,``).split(`/`).map(o);if(r!==`w`||!i)return{view:`map`};if(a!==`s`||!s)return{view:`workspace`,ws:i};let c=new URLSearchParams(n).get(`at`);return c?{view:`session`,ws:i,key:s,at:c}:{view:`session`,ws:i,key:s}}var l=e=>e.view===`map`?``:e.view===`workspace`?`w/${s(e.ws)}`:`w/${s(e.ws)}/s/${s(e.key)}${e.at?`?at=${encodeURIComponent(e.at)}`:``}`,u=e=>e.view===`session`?{view:`workspace`,ws:e.ws}:{view:`map`},d=e=>e.view===`session`?e.key:e.view===`workspace`?`w:${e.ws}`:null;function ee(e,t=new URL(document.baseURI).pathname){let n=()=>c(location.pathname.slice(t.length)+location.search),r={route:n,href:e=>l(e)||`./`,go(n){let r=t+l(n);location.pathname+location.search!==r&&history.pushState(null,``,r),e(n)}};return addEventListener(`popstate`,()=>e(n())),document.addEventListener(`click`,e=>{let t=e.target?.closest?.(`a[data-route]`);!t||e.defaultPrevented||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||(e.preventDefault(),r.go(c(t.getAttribute(`data-route`))))}),r}var f={main:`main`,member:`member`,subagent:`subagent`,outpost:`outpost`,outside:`outside`,tower:`tower`},p={working:`working`,waiting:`waiting on a question`,idle:`idle`,done:`finished`,error:`error`,unknown:`no status`},m=e=>e===`waiting`||e===`error`,h=e=>{let t=new DOMParser().parseFromString(e.trim(),`text/html`).body.firstElementChild;if(!(t instanceof HTMLElement))throw Error(`bad template`);return document.importNode(t,!0)},g=(e,t)=>{let n=document.getElementById(e);if(!(n instanceof t))throw Error(`#${e} is missing from the page`);return n},_=(e,t,n)=>{let r=e.querySelector(t);r.textContent!==n&&(r.textContent=n)},v=(e,t)=>{if(e.dataset.mark===t)return;let n=e.dataset.mark!==void 0;e.dataset.mark=t,n&&(e.classList.remove(`flash`),e.offsetWidth,e.classList.add(`flash`))},y=(e,t)=>e?e.replace(/^tool:/,`using `):p[t],b=(e,t)=>{e.toggleAttribute(`hidden`,!t),t&&(e.setAttribute(`href`,l(t)||`./`),e.setAttribute(`data-route`,l(t)))},x=`<div class="plate"><i class="ring"></i><svg><use href="#i-main"/></svg><b class="badge"></b></div>`;function S(e,t,n){v(e,n);let r=e.querySelector(`use`),i=`#i-${f[t]??`member`}`;r.getAttribute(`href`)!==i&&r.setAttribute(`href`,i)}function C(e,t,n,r){let i=new Map;for(let t of e.children)!t.classList.contains(`leaving`)&&t.dataset.key&&i.set(t.dataset.key,t);let a=e.firstElementChild;for(let o of t){let t=i.get(o.id);for(t||(t=n(o),t.dataset.key=o.id,t.classList.add(`enter`),setTimeout(()=>t.classList.remove(`enter`),600)),i.delete(o.id);a?.classList.contains(`leaving`);)a=a.nextElementSibling;t===a?a=a.nextElementSibling:e.insertBefore(t,a),r(t,o)}for(let e of i.values())e.classList.add(`leaving`),setTimeout(()=>e.remove(),400)}var w=()=>h(`<div class="unit"><div class="card"><a class="hit"></a>${x}<div class="meta"><span class="name"></span><span class="line"><em class="role"></em><span class="note"></span></span></div></div><div class="subs"></div></div>`);function T(e,t){let n=e.closest(`.base`)?.dataset.key,r=e.querySelector(`.hit`);b(r,t.key&&n?{view:`session`,ws:n,key:t.key}:void 0),r.setAttribute(`aria-label`,`${t.name}: ${y(t.note,t.mark)}`),r.toggleAttribute(`data-alert`,m(t.mark)),v(e,t.mark),S(e.querySelector(`.plate`),t.role,t.mark),_(e,`.name`,t.name),_(e,`.role`,t.role),_(e,`.note`,y(t.note,t.mark)),C(e.querySelector(`.subs`),t.subs??[],w,T)}var te=()=>h(`<section class="base"><header><div class="tower"><i class="sweep"></i><i class="pulse"></i><svg><use href="#i-tower"/></svg><b class="badge"></b></div><div class="title"><span class="crown"><svg><use href="#i-crown"/></svg>head tower</span><h2><a class="enter-base"></a></h2><p></p></div></header><div class="tabs"></div><div class="outposts"></div></section>`);function ne(e,t){v(e,t.mark),e.classList.toggle(`head`,t.head);let n=e.querySelector(`.tower`);n.classList.toggle(`ghost`,!t.tower),v(n,t.tower?.mark??`unknown`),_(e,`h2 a`,t.label),b(e.querySelector(`h2 a`),{view:`workspace`,ws:t.id}),_(e,`.title p`,t.tower?`control tower · ${p[t.tower.mark]}`:`no tower yet`),C(e.querySelector(`.tabs`),t.tabs,()=>h(`<div class="tab"><h3></h3><div class="units"></div></div>`),(e,t)=>{_(e,`h3`,`tab ${t.label}`),C(e.querySelector(`.units`),t.units,w,T)}),C(e.querySelector(`.outposts`),t.outposts.map(e=>({id:e.id,name:e.label,role:`outpost`,mark:e.mark,note:e.mark===`unknown`?`no agent running`:void 0})),w,T)}function re(e,t,n){let r=[...e.querySelectorAll(`.base:not(.leaving) .tower`)],i=e.querySelector(`.base.head:not(.leaving) .tower`),a=e.getBoundingClientRect(),o=e=>{let t=e.getBoundingClientRect();return[Math.round(t.x+t.width/2-a.x),Math.round(t.y+t.height/2-a.y)]},s=i?r.filter(e=>e!==i).map(e=>{let[[t,n],[r,a]]=[o(i),o(e)],s=Math.round((t+r)/2);return`M${t} ${n}C${s} ${n} ${s} ${a} ${r} ${a}`}):[],c=s.join(`|`);if(t.dataset.sig===c)return;t.dataset.sig=c;let l=(e,t,...n)=>{let r=document.createElementNS(`http://www.w3.org/2000/svg`,e);for(let[e,n]of Object.entries(t))r.setAttribute(e,n);return r.append(...n),r};t.replaceChildren(...s.flatMap(e=>[l(`path`,{class:`wire`,d:e}),...n?[]:[l(`circle`,{class:`packet`,r:`3`},l(`animateMotion`,{dur:`5s`,repeatCount:`indefinite`,path:e}))]]))}function ie(){let[e,t,n,r]=[g(`map`,HTMLElement),g(`bases`,HTMLElement),g(`links`,SVGSVGElement),g(`log`,HTMLElement)],i=matchMedia(`(prefers-reduced-motion: reduce)`).matches,a=()=>!e.hidden&&re(e,n,i);return new ResizeObserver(a).observe(e),{layout:a,stats(e){let t=e.bases.flatMap(e=>e.tabs.flatMap(e=>e.units.flatMap(e=>[e,...e.subs]))),n=e=>String(t.filter(t=>!e||t.mark===e).length);for(let[t,r]of[[`s-bases`,String(e.bases.length)],[`s-agents`,n()],[`s-working`,n(`working`)],[`s-waiting`,n(`waiting`)]])g(t,HTMLElement).textContent=r},show(e){C(t,e.bases,te,ne),g(`empty`,HTMLElement).hidden=e.bases.length>0,requestAnimationFrame(a)},log(e){let t=new Date().toLocaleTimeString([],{hour12:!1});for(let n of e){let e=h(`<li><span></span><time></time></li>`);e.dataset.mark=n.mark,_(e,`span`,n.text),_(e,`time`,t),r.prepend(e)}for(;r.children.length>12;)r.lastElementChild.remove()},pause(e){document.documentElement.classList.toggle(`paused`,e)},still(e){e?n.pauseAnimations():n.unpauseAnimations()}}}var E=45e3,ae=1e3,oe=3e4,se=(e,t)=>Math.min(oe,ae*2**(e-1))*(.8+.4*t);function D(e){try{return JSON.parse(e)}catch{return}}function ce(e){let t,n,r,i=0,a={state:`connecting`,attempt:0},o=t=>e.onStatus(a={...a,...t}),s=()=>(e.clearTimeout(n),e.clearTimeout(r),n=r=void 0),c=()=>{let e=t;t=void 0,e?.close()},l=()=>{e.clearTimeout(r),r=e.setTimeout(()=>u(),E),o({state:`live`,attempt:0,lastContact:e.now()})},u=()=>{c(),s();let t=a.attempt+1;if(t>8)return o({state:`offline`,attempt:t});o({state:`reconnecting`,attempt:t}),e.hidden()||(n=e.setTimeout(d,se(t,e.random())))},d=()=>{c(),s(),i=e.now();let n=t=e.open();n.onError(()=>t===n&&u()),n.listen(`message`,r=>{if(t!==n)return;let i=D(r);i&&e.onNet(i),o({problem:i?void 0:`the server sent a snapshot the page cannot read`}),l()}),n.listen(`ping`,()=>t===n&&l()),n.listen(`problem`,e=>{t===n&&(o({problem:D(e)??`the server reported a problem the page cannot read`}),l())}),r=e.setTimeout(()=>u(),E)};return{start(){o({state:`connecting`,attempt:0}),d()},resume(){let n=e.now()-i<2e3||a.lastContact!==void 0&&e.now()-a.lastContact<=2e4;t&&n||(o({attempt:0,state:a.state===`live`?`reconnecting`:a.state}),d())},restart:d,hide(){e.clearTimeout(n),n=void 0},stop(){c(),s()}}}var le=(function(){let e=typeof document<`u`&&document.createElement(`link`).relList;return e&&e.supports&&e.supports(`modulepreload`)?`modulepreload`:`preload`})(),ue=function(e,t){return new URL(e,t).href},de={},fe=function(e){return e.pathname.endsWith(`.css`)},pe=function(e,t,n){if(t in e)return e[t];let r=n();if(!r){e[t]=void 0;return}let i=r.then(()=>{e[t]=void 0},n=>{throw e[t]=void 0,n});return e[t]=i,i},O=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e,i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?new URL(import.meta.resolve(e)):new URL(e,import.meta.url)}r=o(t.map(t=>{t=ue(t,n);let r=s(t),i=fe(r);return pe(de,r.href,()=>{if(e===void 0){e={all:new Set,styles:new Set};let t=document.getElementsByTagName(`link`);for(let n=t.length-1;n>=0;n--){let r=t[n];e.all.add(r.href),r.rel===`stylesheet`&&e.styles.add(r.href)}}if((i?e.styles:e.all).has(r.href))return;let t=document.createElement(`link`);if(t.rel=i?`stylesheet`:le,i||(t.as=`script`),t.crossOrigin=``,t.href=r.href,a&&t.setAttribute(`nonce`,a),document.head.appendChild(t),i)return new Promise((e,n)=>{t.addEventListener(`load`,e),t.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${r}`)))})})}).filter(e=>e!==void 0))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},k=ie(),A=e=>document.getElementById(e),me=e=>e?new Date(e).toLocaleTimeString([],{hour12:!1}):`never`,j,M,N,P,F={state:`connecting`,attempt:0},I={view:`map`},L=!1,R,z,he,B=e=>z?e(z):void he?.then(e);function ge(){he??=O(async()=>{let{mountFocus:e}=await import(`./views-vksDW2C_.js`);return{mountFocus:e}},[],import.meta.url).then(({mountFocus:e})=>(z=e({data:ve,restart:()=>Q.restart(),openTile:W,online:()=>F.state===`live`}),z.show(I,I),N&&z.net(N),z))}var V,H,U=e=>{if(V)return e(V);H??=O(async()=>{let{mountPanels:e}=await import(`./layer-CuAcYh0i.js`);return{mountPanels:e}},[],import.meta.url).then(({mountPanels:e})=>V=e({label:e=>N?.bases.find(t=>t.id===e)?.label??e,recent:async e=>(await(Z?Z.data(e).latest():X(e,`?limit=12`))).items,focus:e=>Z&&(()=>Z.data(e).post(`focus`,{}))})),H.then(e)},W=(e,t)=>U(n=>n.tiles.open(e,t));function G(e){if(N=P=e,document.hidden)return;k.stats(e);let t=i(e,Date.now()),r=a(R,t);(r.length||V)&&U(e=>e.alerts(r,t)),R=t,k.log(n(M,e)),M=e,B(t=>t.net(e)),I.view===`map`&&(k.show(e),j=e,P=void 0)}function K(e){let t=I,n=e.view===`workspace`&&t.view===`map`?document.querySelector(`#bases [data-key="${CSS.escape(e.ws)}"]`)?.getBoundingClientRect():void 0;I=e,e.view!==`map`&&ge(),A(`map`).hidden=e.view!==`map`,A(`legend`).hidden=e.view===`session`,k.still(e.view!==`map`),B(r=>r.show(e,t,n)),e.view===`map`&&N&&N!==j&&(G(N),requestAnimationFrame(k.layout)),L&&d(e)!==d(t)&&Q.restart()}function q(e){F=e;let t=e.state===`reconnecting`||e.state===`offline`;document.body.dataset.stale=A(`stage`).dataset.stale=String(t),A(`link`).dataset.state=e.state,A(`link-text`).textContent={connecting:`connecting`,live:`live`,reconnecting:`reconnecting`,offline:`offline`}[e.state],z&&N&&z.net(N);let n=A(`banner`);n.hidden=!t&&!e.problem;let r=e.state===`offline`?`Disconnected. Gave up after ${e.attempt-1} tries; it reconnects when you return to this tab.`:e.state===`reconnecting`?`Disconnected from the straw net. Reconnecting${e.attempt?` (try ${e.attempt})`:``}… The view shows the last known state.`:`The server is up but cannot read the net: ${e.problem}`,i=document.createElement(`b`);i.textContent=me(e.lastContact),n.replaceChildren(`${r} Last update `,i,`.`)}function J(e,t){try{let n=JSON.parse(t);B(t=>e===`history`?t.history(n):t.cards(n))}catch{}}function _e(e){let t=new EventSource(e?`events?watch=${encodeURIComponent(e)}`:`events`);return{onError:e=>t.onerror=e,listen:(e,n)=>t.addEventListener(e,e=>n(e.data)),close:()=>t.close()}}var Y=e=>fetch(`api/${e}`,{cache:`no-store`}).then(e=>e.ok?e.json():Promise.reject(Error(String(e.status)))),X=(e,t=``)=>Y(`history/${encodeURIComponent(e)}${t}`),Z=void 0,ve=e=>({older:t=>X(e,`?before=${encodeURIComponent(t)}`),latest:()=>X(e),entry:t=>Y(`entry/${encodeURIComponent(e)}/${encodeURIComponent(t)}`).catch(()=>void 0)}),ye=()=>{let e=_e(d(I));return e.listen(`history`,e=>J(`history`,e)),e.listen(`cards`,e=>J(`cards`,e)),e};document.addEventListener(`click`,e=>{let t=e.target?.closest?.(`a[data-alert]`),n=t&&c(t.getAttribute(`data-route`)??``);if(!n||n.view!==`session`||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return;let r=R?.get(n.key);r&&(e.preventDefault(),U(e=>e.stack.show(r)))},!0);var be=ee(e=>K(e)),Q=ce({open:ye,now:Date.now,setTimeout:(e,t)=>window.setTimeout(e,t),clearTimeout:e=>window.clearTimeout(e),random:Math.random,hidden:()=>document.hidden,onNet:G,onStatus:q}),$=()=>Q.resume();document.addEventListener(`visibilitychange`,()=>{if(k.pause(document.hidden),document.hidden)return Q.hide();P&&G(P),B(e=>e.wake()),$()}),addEventListener(`keydown`,e=>{e.key!==`Escape`||e.defaultPrevented||(e.preventDefault(),!V?.stack.close()&&I.view!==`map`&&be.go(u(I)))}),A(`pair`).addEventListener(`click`,()=>void O(async()=>{let{pairTile:e}=await import(`./pair-DDTg3MTR.js`);return{pairTile:e}},[],import.meta.url).then(({pairTile:e})=>W(e(),!0)));for(let e of[`focus`,`pageshow`,`online`])window.addEventListener(e,()=>!document.hidden&&$());q(F),K(be.route()),Q.start(),L=!0;export{b as a,S as c,C as d,_ as f,x as i,h as l,r as m,f as n,g as o,m as p,p as r,y as s,O as t,v as u};
1
+ var e={working:`started working`,waiting:`is waiting on a question`,idle:`went idle`,done:`finished`,error:`hit an error`,unknown:`lost its status`};function t(e){let t=new Map;for(let n of e.bases){n.tower&&t.set(`tower:${n.id}`,{name:`${n.label} tower`,mark:n.tower.mark});for(let e of n.tabs.flatMap(e=>e.units))for(let n of[e,...e.subs])t.set(n.id,{name:n.name,mark:n.mark});for(let e of n.outposts)t.set(e.id,{name:e.label,mark:e.mark})}return t}function n(n,r){if(!n)return[];let[i,a]=[t(n),t(r)],o=[];for(let[t,n]of a){let r=i.get(t);r?r.mark!==n.mark&&o.push({text:`${n.name} ${e[n.mark]}`,mark:n.mark}):o.push({text:`${n.name} joined`,mark:n.mark})}for(let[e,t]of i)a.has(e)||o.push({text:`${t.name} left`,mark:`idle`});return o}var r=e=>e===`waiting`||e===`error`?0:1;function i(e,t){let n=new Map;for(let r of e.bases)for(let e of[...r.tower?[{...r.tower,role:`tower`}]:[],...[...r.tabs.flatMap(e=>e.units),...r.outside].flatMap(e=>[e,...e.subs])])n.set(e.key,{key:e.key,ws:r.id,name:e.name,role:e.role,mark:e.mark,note:e.note,at:t,pending:e.pending});return n}function a(e,t){return[...t.values()].filter(t=>e?.get(t.key)?.mark!==t.mark&&(r(t.mark)===0||e!==void 0&&t.mark===`done`))}var o=e=>{try{return decodeURIComponent(e)}catch{return e}},s=e=>/^\.+$/.test(e)?e.replaceAll(`.`,`%2E`):encodeURIComponent(e);function c(e){let[t,n]=e.split(`#`)[0].split(`?`),[r,i,a,s]=t.replace(/^\/+|\/+$/g,``).split(`/`).map(o);if(r!==`w`||!i)return{view:`map`};if(a!==`s`||!s)return{view:`workspace`,ws:i};let c=new URLSearchParams(n).get(`at`);return c?{view:`session`,ws:i,key:s,at:c}:{view:`session`,ws:i,key:s}}var l=e=>e.view===`map`?``:e.view===`workspace`?`w/${s(e.ws)}`:`w/${s(e.ws)}/s/${s(e.key)}${e.at?`?at=${encodeURIComponent(e.at)}`:``}`,u=e=>e.view===`session`?{view:`workspace`,ws:e.ws}:{view:`map`},d=e=>e.view===`session`?e.key:e.view===`workspace`?`w:${e.ws}`:null;function ee(e,t=new URL(document.baseURI).pathname){let n=()=>c(location.pathname.slice(t.length)+location.search),r={route:n,href:e=>l(e)||`./`,go(n){let r=t+l(n);location.pathname+location.search!==r&&history.pushState(null,``,r),e(n)}};return addEventListener(`popstate`,()=>e(n())),document.addEventListener(`click`,e=>{let t=e.target?.closest?.(`a[data-route]`);!t||e.defaultPrevented||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||(e.preventDefault(),r.go(c(t.getAttribute(`data-route`))))}),r}var f={main:`main`,member:`member`,subagent:`subagent`,outpost:`outpost`,outside:`outside`,tower:`tower`},p={working:`working`,waiting:`waiting on a question`,idle:`idle`,done:`finished`,error:`error`,unknown:`no status`},m=e=>e===`waiting`||e===`error`,h=e=>{let t=new DOMParser().parseFromString(e.trim(),`text/html`).body.firstElementChild;if(!(t instanceof HTMLElement))throw Error(`bad template`);return document.importNode(t,!0)},g=(e,t)=>{let n=document.getElementById(e);if(!(n instanceof t))throw Error(`#${e} is missing from the page`);return n},_=(e,t,n)=>{let r=e.querySelector(t);r.textContent!==n&&(r.textContent=n)},v=(e,t)=>{if(e.dataset.mark===t)return;let n=e.dataset.mark!==void 0;e.dataset.mark=t,n&&(e.classList.remove(`flash`),e.offsetWidth,e.classList.add(`flash`))},y=(e,t)=>e?e.replace(/^tool:/,`using `):p[t],b=(e,t)=>{e.toggleAttribute(`hidden`,!t),t&&(e.setAttribute(`href`,l(t)||`./`),e.setAttribute(`data-route`,l(t)))},x=`<div class="plate"><i class="ring"></i><svg><use href="#i-main"/></svg><b class="badge"></b></div>`;function S(e,t,n){v(e,n);let r=e.querySelector(`use`),i=`#i-${f[t]??`member`}`;r.getAttribute(`href`)!==i&&r.setAttribute(`href`,i)}function C(e,t,n,r){let i=new Map;for(let t of e.children)!t.classList.contains(`leaving`)&&t.dataset.key&&i.set(t.dataset.key,t);let a=e.firstElementChild;for(let o of t){let t=i.get(o.id);for(t||(t=n(o),t.dataset.key=o.id,t.classList.add(`enter`),setTimeout(()=>t.classList.remove(`enter`),600)),i.delete(o.id);a?.classList.contains(`leaving`);)a=a.nextElementSibling;t===a?a=a.nextElementSibling:e.insertBefore(t,a),r(t,o)}for(let e of i.values())e.classList.add(`leaving`),setTimeout(()=>e.remove(),400)}var w=()=>h(`<div class="unit"><div class="card"><a class="hit"></a>${x}<div class="meta"><span class="name"></span><span class="line"><em class="role"></em><span class="note"></span></span></div></div><div class="subs"></div></div>`);function T(e,t){let n=e.closest(`.base`)?.dataset.key,r=e.querySelector(`.hit`);b(r,t.key&&n?{view:`session`,ws:n,key:t.key}:void 0),r.setAttribute(`aria-label`,`${t.name}: ${y(t.note,t.mark)}`),r.toggleAttribute(`data-alert`,m(t.mark)),v(e,t.mark),S(e.querySelector(`.plate`),t.role,t.mark),_(e,`.name`,t.name),_(e,`.role`,t.role),_(e,`.note`,y(t.note,t.mark)),C(e.querySelector(`.subs`),t.subs??[],w,T)}var te=()=>h(`<section class="base"><header><div class="tower"><i class="sweep"></i><i class="pulse"></i><svg><use href="#i-tower"/></svg><b class="badge"></b></div><div class="title"><span class="crown"><svg><use href="#i-crown"/></svg>head tower</span><h2><a class="enter-base"></a></h2><p></p></div></header><div class="tabs"></div><div class="outposts"></div></section>`);function ne(e,t){v(e,t.mark),e.classList.toggle(`head`,t.head);let n=e.querySelector(`.tower`);n.classList.toggle(`ghost`,!t.tower),v(n,t.tower?.mark??`unknown`),_(e,`h2 a`,t.label),b(e.querySelector(`h2 a`),{view:`workspace`,ws:t.id}),_(e,`.title p`,t.tower?`control tower · ${p[t.tower.mark]}`:`no tower yet`),C(e.querySelector(`.tabs`),t.tabs,()=>h(`<div class="tab"><h3></h3><div class="units"></div></div>`),(e,t)=>{_(e,`h3`,`tab ${t.label}`),C(e.querySelector(`.units`),t.units,w,T)}),C(e.querySelector(`.outposts`),t.outposts.map(e=>({id:e.id,name:e.label,role:`outpost`,mark:e.mark,note:e.mark===`unknown`?`no agent running`:void 0})),w,T)}function re(e,t,n){let r=[...e.querySelectorAll(`.base:not(.leaving) .tower`)],i=e.querySelector(`.base.head:not(.leaving) .tower`),a=e.getBoundingClientRect(),o=e=>{let t=e.getBoundingClientRect();return[Math.round(t.x+t.width/2-a.x),Math.round(t.y+t.height/2-a.y)]},s=i?r.filter(e=>e!==i).map(e=>{let[[t,n],[r,a]]=[o(i),o(e)],s=Math.round((t+r)/2);return`M${t} ${n}C${s} ${n} ${s} ${a} ${r} ${a}`}):[],c=s.join(`|`);if(t.dataset.sig===c)return;t.dataset.sig=c;let l=(e,t,...n)=>{let r=document.createElementNS(`http://www.w3.org/2000/svg`,e);for(let[e,n]of Object.entries(t))r.setAttribute(e,n);return r.append(...n),r};t.replaceChildren(...s.flatMap(e=>[l(`path`,{class:`wire`,d:e}),...n?[]:[l(`circle`,{class:`packet`,r:`3`},l(`animateMotion`,{dur:`5s`,repeatCount:`indefinite`,path:e}))]]))}function ie(){let[e,t,n,r]=[g(`map`,HTMLElement),g(`bases`,HTMLElement),g(`links`,SVGSVGElement),g(`log`,HTMLElement)],i=matchMedia(`(prefers-reduced-motion: reduce)`).matches,a=()=>!e.hidden&&re(e,n,i);return new ResizeObserver(a).observe(e),{layout:a,stats(e){let t=e.bases.flatMap(e=>e.tabs.flatMap(e=>e.units.flatMap(e=>[e,...e.subs]))),n=e=>String(t.filter(t=>!e||t.mark===e).length);for(let[t,r]of[[`s-bases`,String(e.bases.length)],[`s-agents`,n()],[`s-working`,n(`working`)],[`s-waiting`,n(`waiting`)]])g(t,HTMLElement).textContent=r},show(e){C(t,e.bases,te,ne),g(`empty`,HTMLElement).hidden=e.bases.length>0,requestAnimationFrame(a)},log(e){let t=new Date().toLocaleTimeString([],{hour12:!1});for(let n of e){let e=h(`<li><span></span><time></time></li>`);e.dataset.mark=n.mark,_(e,`span`,n.text),_(e,`time`,t),r.prepend(e)}for(;r.children.length>12;)r.lastElementChild.remove()},pause(e){document.documentElement.classList.toggle(`paused`,e)},still(e){e?n.pauseAnimations():n.unpauseAnimations()}}}var E=45e3,ae=1e3,oe=3e4,se=(e,t)=>Math.min(oe,ae*2**(e-1))*(.8+.4*t);function D(e){try{return JSON.parse(e)}catch{return}}function ce(e){let t,n,r,i=0,a={state:`connecting`,attempt:0},o=t=>e.onStatus(a={...a,...t}),s=()=>(e.clearTimeout(n),e.clearTimeout(r),n=r=void 0),c=()=>{let e=t;t=void 0,e?.close()},l=()=>{e.clearTimeout(r),r=e.setTimeout(()=>u(),E),o({state:`live`,attempt:0,lastContact:e.now()})},u=()=>{c(),s();let t=a.attempt+1;if(t>8)return o({state:`offline`,attempt:t});o({state:`reconnecting`,attempt:t}),e.hidden()||(n=e.setTimeout(d,se(t,e.random())))},d=()=>{c(),s(),i=e.now();let n=t=e.open();n.onError(()=>t===n&&u()),n.listen(`message`,r=>{if(t!==n)return;let i=D(r);i&&e.onNet(i),o({problem:i?void 0:`the server sent a snapshot the page cannot read`}),l()}),n.listen(`ping`,()=>t===n&&l()),n.listen(`problem`,e=>{t===n&&(o({problem:D(e)??`the server reported a problem the page cannot read`}),l())}),r=e.setTimeout(()=>u(),E)};return{start(){o({state:`connecting`,attempt:0}),d()},resume(){let n=e.now()-i<2e3||a.lastContact!==void 0&&e.now()-a.lastContact<=2e4;t&&n||(o({attempt:0,state:a.state===`live`?`reconnecting`:a.state}),d())},restart:d,hide(){e.clearTimeout(n),n=void 0},stop(){c(),s()}}}var le=(function(){let e=typeof document<`u`&&document.createElement(`link`).relList;return e&&e.supports&&e.supports(`modulepreload`)?`modulepreload`:`preload`})(),ue=function(e,t){return new URL(e,t).href},de={},fe=function(e){return e.pathname.endsWith(`.css`)},pe=function(e,t,n){if(t in e)return e[t];let r=n();if(!r){e[t]=void 0;return}let i=r.then(()=>{e[t]=void 0},n=>{throw e[t]=void 0,n});return e[t]=i,i},O=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e,i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?new URL(import.meta.resolve(e)):new URL(e,import.meta.url)}r=o(t.map(t=>{t=ue(t,n);let r=s(t),i=fe(r);return pe(de,r.href,()=>{if(e===void 0){e={all:new Set,styles:new Set};let t=document.getElementsByTagName(`link`);for(let n=t.length-1;n>=0;n--){let r=t[n];e.all.add(r.href),r.rel===`stylesheet`&&e.styles.add(r.href)}}if((i?e.styles:e.all).has(r.href))return;let t=document.createElement(`link`);if(t.rel=i?`stylesheet`:le,i||(t.as=`script`),t.crossOrigin=``,t.href=r.href,a&&t.setAttribute(`nonce`,a),document.head.appendChild(t),i)return new Promise((e,n)=>{t.addEventListener(`load`,e),t.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${r}`)))})})}).filter(e=>e!==void 0))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},k=ie(),A=e=>document.getElementById(e),me=e=>e?new Date(e).toLocaleTimeString([],{hour12:!1}):`never`,j,M,N,P,F={state:`connecting`,attempt:0},I={view:`map`},L=!1,R,z,he,B=e=>z?e(z):void he?.then(e);function ge(){he??=O(async()=>{let{mountFocus:e}=await import(`./views-BjEPS-ZH.js`);return{mountFocus:e}},[],import.meta.url).then(({mountFocus:e})=>(z=e({data:ve,restart:()=>Q.restart(),openTile:W,online:()=>F.state===`live`}),z.show(I,I),N&&z.net(N),z))}var V,H,U=e=>{if(V)return e(V);H??=O(async()=>{let{mountPanels:e}=await import(`./layer-CdS6Zpfk.js`);return{mountPanels:e}},[],import.meta.url).then(({mountPanels:e})=>V=e({label:e=>N?.bases.find(t=>t.id===e)?.label??e,recent:async e=>(await(Z?Z.data(e).latest():X(e,`?limit=12`))).items,focus:e=>Z&&(()=>Z.data(e).post(`focus`,{}))})),H.then(e)},W=(e,t)=>U(n=>n.tiles.open(e,t));function G(e){if(N=P=e,document.hidden)return;k.stats(e);let t=i(e,Date.now()),r=a(R,t);(r.length||V)&&U(e=>e.alerts(r,t)),R=t,k.log(n(M,e)),M=e,B(t=>t.net(e)),I.view===`map`&&(k.show(e),j=e,P=void 0)}function K(e){let t=I,n=e.view===`workspace`&&t.view===`map`?document.querySelector(`#bases [data-key="${CSS.escape(e.ws)}"]`)?.getBoundingClientRect():void 0;I=e,e.view!==`map`&&ge(),A(`map`).hidden=e.view!==`map`,A(`legend`).hidden=e.view===`session`,k.still(e.view!==`map`),B(r=>r.show(e,t,n)),e.view===`map`&&N&&N!==j&&(G(N),requestAnimationFrame(k.layout)),L&&d(e)!==d(t)&&Q.restart()}function q(e){F=e;let t=e.state===`reconnecting`||e.state===`offline`;document.body.dataset.stale=A(`stage`).dataset.stale=String(t),A(`link`).dataset.state=e.state,A(`link-text`).textContent={connecting:`connecting`,live:`live`,reconnecting:`reconnecting`,offline:`offline`}[e.state],z&&N&&z.net(N),V?.stack.redraw();let n=A(`banner`);n.hidden=!t&&!e.problem;let r=e.state===`offline`?`Disconnected. Gave up after ${e.attempt-1} tries; it reconnects when you return to this tab.`:e.state===`reconnecting`?`Disconnected from the straw net. Reconnecting${e.attempt?` (try ${e.attempt})`:``}… The view shows the last known state.`:`The server is up but cannot read the net: ${e.problem}`,i=document.createElement(`b`);i.textContent=me(e.lastContact),n.replaceChildren(`${r} Last update `,i,`.`)}function J(e,t){try{let n=JSON.parse(t);B(t=>e===`history`?t.history(n):t.cards(n))}catch{}}function _e(e){let t=new EventSource(e?`events?watch=${encodeURIComponent(e)}`:`events`);return{onError:e=>t.onerror=e,listen:(e,n)=>t.addEventListener(e,e=>n(e.data)),close:()=>t.close()}}var Y=e=>fetch(`api/${e}`,{cache:`no-store`}).then(e=>e.ok?e.json():Promise.reject(Error(String(e.status)))),X=(e,t=``)=>Y(`history/${encodeURIComponent(e)}${t}`),Z=void 0,ve=e=>({older:t=>X(e,`?before=${encodeURIComponent(t)}`),latest:()=>X(e),entry:t=>Y(`entry/${encodeURIComponent(e)}/${encodeURIComponent(t)}`).catch(()=>void 0)}),ye=()=>{let e=_e(d(I));return e.listen(`history`,e=>J(`history`,e)),e.listen(`cards`,e=>J(`cards`,e)),e};document.addEventListener(`click`,e=>{let t=e.target?.closest?.(`a[data-alert]`),n=t&&c(t.getAttribute(`data-route`)??``);if(!n||n.view!==`session`||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return;let r=R?.get(n.key);r&&(e.preventDefault(),U(e=>e.stack.show(r)))},!0);var be=ee(e=>K(e)),Q=ce({open:ye,now:Date.now,setTimeout:(e,t)=>window.setTimeout(e,t),clearTimeout:e=>window.clearTimeout(e),random:Math.random,hidden:()=>document.hidden,onNet:G,onStatus:q}),$=()=>Q.resume();document.addEventListener(`visibilitychange`,()=>{if(k.pause(document.hidden),document.hidden)return Q.hide();P&&G(P),B(e=>e.wake()),$()}),addEventListener(`keydown`,e=>{e.key!==`Escape`||e.defaultPrevented||(e.preventDefault(),!V?.stack.close()&&I.view!==`map`&&be.go(u(I)))}),A(`pair`).addEventListener(`click`,()=>void O(async()=>{let{pairTile:e}=await import(`./pair-DDTg3MTR.js`);return{pairTile:e}},[],import.meta.url).then(({pairTile:e})=>W(e(),!0)));for(let e of[`focus`,`pageshow`,`online`])window.addEventListener(e,()=>!document.hidden&&$());q(F),K(be.route()),Q.start(),L=!0;export{b as a,S as c,C as d,_ as f,x as i,h as l,r as m,f as n,g as o,m as p,p as r,y as s,O as t,v as u};
@@ -0,0 +1,2 @@
1
+ import{a as e,c as t,d as n,f as r,i,l as a,m as o,o as s,s as c,u as l}from"./index-C_WFzbRO.js";import{t as u}from"./post-B39ywXKd.js";import{t as d}from"./ask-DLinwCcp.js";var f=(e,t)=>o(e.mark)-o(t.mark)||e.at-t.at,p=e=>{let t=e.filter(e=>o(e.mark)===0).length,n=e.length-t;return[t&&`${t} need${t===1?`s`:``} you`,n&&`${n} finished`].filter(Boolean).join(` · `)};function m(e){let t=[],n=[],r=()=>e.render(t.map(e=>e.alert),n),i=(i,a,o=!0)=>{let s=t.findIndex(e=>e.alert.key===i.key);if(s>=0&&!o)return;let c=s>=0?t.splice(s,1)[0]:{alert:i,back:a,raised:i.mark};!o&&t.length?t.splice(t.length-1,0,c):t.push(c),n=n.filter(e=>e.key!==i.key),r(),o&&e.enter(c.alert)};return{get open(){return t.map(e=>e.alert)},get queue(){return n},arrive(e){let i=t.find(t=>t.alert.key===e.key);if(i)[i.alert,i.raised]=[{...e,at:i.alert.at},e.mark];else{let t=n.find(t=>t.key===e.key);n=[...n.filter(t=>t.key!==e.key),t&&t.mark===e.mark?t:e].sort(f)}r()},prune(e){n=n.filter(t=>e(t.key)?.mark===t.mark);for(let n of t){let t=e(n.alert.key);n.alert=t?{...n.alert,...t,gone:void 0,at:n.alert.at}:{...n.alert,mark:`unknown`,note:void 0,pending:void 0,gone:!0}}r()},redraw:r,show:(t,n=!0)=>i(t,e.focused(),n),raise(n){let r=t.find(e=>e.alert.key===n);r&&r!==t.at(-1)&&i(r.alert,e.focused())},next(){n[0]&&i(n[0],e.focused())},close(a=`close`){let s=t.pop();if(!s)return!1;let c=t.at(-1),l=a===`close`?n.find(e=>o(e.mark)===0):void 0;return a===`later`&&s.alert.mark===s.raised&&(n=[...n,s.alert].sort(f)),!c&&l?(i(l,s.back),!0):(r(),!e.restore(s.back)&&c&&e.enter(c.alert),!0)}}}var h=`a, button, input, textarea, select, summary, label, [role=button]`;function g(e){let t=[],n,r=()=>e.render(t,n);return{get tiles(){return t},get active(){return n},open(i,a=!0){t.some(e=>e.id===i.id)||(t=[...t,i].slice(-4)),(a||!t.some(e=>e.id===n))&&(n=i.id),r(),a&&e.enter(t.find(e=>e.id===i.id))},close(e){let i=t.findIndex(t=>t.id===e);i<0||(t=t.filter(t=>t.id!==e),n===e&&(n=(t[i]??t[i-1])?.id),r())},activate(e){n=e,r()}}}function _(e){let t=new Set(e.flatMap(e=>e.kind===`result`&&e.callId?[e.callId]:[])),n=e.filter(e=>e.kind===`assistant`),r=n.at(-1),i=r?.tools?.find(e=>!t.has(e.id));return{said:(n.findLast(e=>e.text?.trim())?.text?.trim().split(`
2
+ `).filter(Boolean).at(-1))?.slice(0,280),pending:i&&`${i.name} · ${i.summary}`.slice(0,200),at:(r??e.findLast(e=>e.kind!==`result`))?.id}}var v=()=>a(`<section class="dialog" role="dialog" aria-modal="false" tabindex="-1"><header><b class="tag">alert</b><span class="where"></span><button class="x" type="button" aria-label="Close">×</button></header><div class="who">${i}<div><h3></h3><p><em class="role"></em> <span class="state"></span></p></div></div><div class="intel"><h4>Last transmission</h4><p class="said">…</p><p class="pending" hidden><b>waiting on</b> <span></span></p></div><div class="ask-root"></div><footer><a class="go">Open comms</a><button class="later" type="button">Later</button><span class="queued"></span></footer></section>`),y=()=>a(`<section class="tile" tabindex="-1"><header><svg><use href="#i-doc"/></svg><b></b><button class="x" type="button" aria-label="Close document">×</button></header><div class="tile-body"></div></section>`);function b(o){let[f,b,x,S,C]=[s(`dock`,HTMLElement),s(`work`,HTMLElement),s(`tiles`,HTMLElement),s(`tile-tabs`,HTMLElement),s(`alerts`,HTMLButtonElement)],w=new Map,T=(t,n,i=w.get(n.key))=>{let a=t.dataset.gone===`true`;r(t,`.said`,i?.failed?`History unavailable.`:i?.said??`No messages yet.`),t.querySelector(`.pending`).hidden=!i?.pending||a,r(t,`.pending span`,i?.pending??``),e(t.querySelector(`.go`),a?void 0:{view:`session`,ws:n.ws,key:n.key,...i?.at&&{at:i.at}})},E=(e,t={said:`…`})=>{w.set(e.key,t),o.recent(e.key).then(_,()=>({failed:!0})).then(n=>{let r=w.get(e.key)===t&&(w.set(e.key,n),f.querySelector(`.dialog[data-key="${CSS.escape(e.key)}"]`));r&&T(r,e)})},D=(e,n,i,a)=>{l(e,n.mark),e.dataset.gone=String(!!n.gone),e.style.transform=i?`translateY(${-12*i}px) scale(${1-.06*i})`:``,e.style.opacity=String(Math.max(0,1-.3*i)),e.toggleAttribute(`inert`,i>0),e.setAttribute(`aria-label`,`${n.name}: ${c(n.note,n.mark)}`),t(e.querySelector(`.plate`),n.role,n.mark),r(e,`h3`,n.name),r(e,`.role`,n.role===`outside`?`unaligned`:n.role),r(e,`.state`,n.gone?`left the net`:c(n.note,n.mark)),r(e,`.where`,o.label(n.ws)),r(e,`.tag`,n.gone?`gone`:{waiting:`question`,error:`error`,done:`finished`}[n.mark]??`update`),r(e,`.queued`,a.length?`Also queued: ${p(a)}`:``),w.has(n.key)||E(n),T(e,n);let s=O.get(e);s||O.set(e,s=d(e.querySelector(`.ask-root`),o.focus?.(n.key)??(()=>u(`sessions/${encodeURIComponent(n.key)}/focus`,{})))),s.update(n.pending,n.mark===`waiting`&&document.body.dataset.stale!==`true`)},O=new WeakMap,k=m({render(e,o){n(f,e.map(e=>({...e,id:e.key})),v,(t,n)=>D(t,n,e.length-1-e.findIndex(e=>e.key===n.key),o));let s=o[0];C.disabled=!s,C.dataset.mark=s?.mark??`unknown`,r(C,`#alert-count`,String(o.length)),C.title=o.length?`Alerts: ${p(o)}`:``,r(C,`#alert-text`,s?`${s.name} · ${c(s.note,s.mark)}`:`no alerts`),n(C.querySelector(`.faces`),o.slice(0,3).map(e=>({...e,id:e.key})),()=>a(i),(e,n)=>t(e,n.role,n.mark))},enter:e=>f.querySelector(`[data-key="${CSS.escape(e.key)}"] .go`)?.focus(),focused:()=>document.activeElement instanceof HTMLElement&&document.activeElement!==document.body?document.activeElement:void 0,restore:e=>!!(e?.isConnected&&!e.closest(`[inert]`)&&(e.focus(),document.activeElement===e))});C.addEventListener(`click`,()=>k.next()),document.addEventListener(`click`,e=>{let t=t=>{let n=t.getBoundingClientRect();return e.clientX>=n.left&&e.clientX<=n.right&&e.clientY>=n.top&&e.clientY<=n.bottom},n=[...f.querySelectorAll(`.dialog:not(.leaving)`)],r=n.slice(0,-1).reverse().find(t);e.defaultPrevented||!r||!n.length||t(n.at(-1))||e.target?.closest?.(h)||(e.preventDefault(),e.stopPropagation(),k.raise(r.dataset.key))},!0),f.addEventListener(`click`,e=>{let t=e.target;t.closest(`.x`)?k.close():t.closest(`.later`)?k.close(`later`):t.closest(`.go`)&&queueMicrotask(()=>void k.close(`go`))});let A=0,j=()=>x.classList.toggle(`tabbed`,M.tiles.length>1&&b.clientWidth<460+M.tiles.length*400),M=g({render(e,t){x.hidden=!e.length,n(x,e,e=>{let t=y();return t.querySelector(`.tile-body`).append(e.body()),t.querySelector(`.x`).addEventListener(`click`,()=>M.close(e.id)),t},(e,n)=>{r(e,`header b`,n.title),e.classList.toggle(`active`,n.id===t)}),n(S,e,()=>a(`<button type="button" class="tab-btn"></button>`),(e,n)=>{e.textContent=n.title,e.classList.toggle(`active`,n.id===t),e.onclick=()=>M.activate(n.id)}),x.prepend(S),e.length!==A&&(A=e.length,j())},enter:e=>requestAnimationFrame(()=>x.querySelector(`.tile[data-key="${CSS.escape(e.id)}"]`)?.focus())});return new ResizeObserver(j).observe(b),{tiles:M,stack:k,alerts(e,t){for(let t of e)E(t),k.arrive(t);k.prune(e=>t.get(e))}}}export{b as mountPanels};
@@ -0,0 +1,2 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./index-BccsK_Zr.css"])))=>i.map(i=>d[i]);
2
+ import{a as e,c as t,d as n,f as r,i,l as a,p as o,r as s,s as c,t as l,u}from"./index-C_WFzbRO.js";import{n as d,t as f}from"./post-B39ywXKd.js";import{t as p}from"./ask-DLinwCcp.js";var m=16384,h=`Not confirmed. Check the terminal before sending again.`,g=`The original already went to the session; what is here is your edited copy, unsent.`,_=/^\s*\/(quit|exit|new|resume|fork|clone|import|reload)(?=\s|$)/,v=new Map,y=e=>(t,n)=>f(`sessions/${encodeURIComponent(e)}/${t}`,n);function b(e,t,n){if(!e.ok)return[`error`,e.status===504?h:e.body.reason??`Refused (${e.status}).`];if(t&&!t.startsWith(n))return[`ok`,g]}var x=e=>{try{return e?.startsWith(`[`)?JSON.parse(e):e?[`error`,e]:void 0}catch{return[`error`,e]}},S=(e,t)=>e!==``&&!e.startsWith(t)?`Sent as it was; your edits stayed here, unsent.`:`Sent. It shows in the log once pi writes it.`,C=(e,t)=>({hidden:!e.live||!e.control||!!e.asking,disabled:t||!e.online}),w=(e,t)=>e.startsWith(t)?e.slice(t.length).replace(/^\n/,``):e;function T(e){if(!e.online)return`The stream is down; sending waits for it to come back.`;if(!e.live)return`This session is no longer live; its history stays readable.`;if(!e.control)return`No live pi pane backs this unit, so there is nothing to type into.`;if(e.mark===`waiting`)return`It waits on a dialog; answer it in its terminal.`}function E(e,t,n){let r=`sb-draft:${t}`,i=matchMedia(`(pointer: coarse)`).matches,a=d(`textarea`,{rows:`1`,maxlength:String(m),"aria-label":`Message`,enterkeyhint:i?`enter`:`send`}),o=d(`button`,{type:`submit`,class:`cmp-send`},`Send`),s=d(`button`,{type:`button`,class:`cmp-alt`,title:`Alt+Enter`},`Follow-up`),c=d(`button`,{type:`button`,class:`cmp-alt cmp-stop`},`Stop`),l=d(`p`,{class:`cmp-note`,role:`status`,"aria-live":`polite`}),u=d(`button`,{type:`button`,class:`cmp-link`},`Focus terminal`),f=d(`form`,{class:`composer`},d(`div`,{class:`cmp-row`},a,d(`div`,{class:`cmp-acts`},o,s,c)),d(`div`,{class:`cmp-foot`},l,u));e.replaceChildren(f);let p={name:``,control:!1,live:!1,mark:`unknown`,online:!1},g=!1,y=!1,E=e=>{try{e()}catch{}};E(()=>a.value=sessionStorage.getItem(r)??``);let D=()=>E(()=>a.value?sessionStorage.setItem(r,a.value):sessionStorage.removeItem(r)),O=()=>(a.style.height=`auto`,a.style.height=`${Math.min(a.scrollHeight,220)}px`),k=(e,t=``,...n)=>(l.dataset.tone=t,l.replaceChildren(e,...n)),A=(e,t,n)=>{let r=d(`button`,{type:`button`,class:`cmp-yes`},t),i=d(`button`,{type:`button`,class:`cmp-link`},`Cancel`);y=!0,r.onclick=()=>(y=!1,n()),i.onclick=()=>(y=!1,k(``),a.focus()),k(e,`ask`,r,i),r.focus()},[j,M]=[`${r}:note`,()=>{try{return sessionStorage.getItem(r)??``}catch{return}}],N=e=>E(()=>e?sessionStorage.setItem(j,JSON.stringify(e)):sessionStorage.removeItem(j));function P(e,i,a){let o={sent:a,answer:n(e,i)};return a!==void 0&&(N([`error`,h]),v.set(t,o),o.answer.then(e=>{let t=M();e.ok&&t!==void 0&&E(()=>sessionStorage.setItem(r,w(t,a))),N(b(e,t,a))},()=>N([`error`,h]))),F(o)}async function F(e,{sent:n}=e){g=!0,y=!1,L(),k(n===void 0?``:`Sending…`);try{let t=await e.answer;if(!f.isConnected)return;if(!t.ok)return t.status===504||N(),k(t.body.reason??`Refused (${t.status}).`,`error`);if(n===void 0)return k(``);N();let r=S(a.value,n);a.value=w(a.value,n),D(),O(),k(r,`ok`)}catch{k(h,`error`)}finally{v.get(t)===e&&v.delete(t),g=!1,L()}}function I(e,t){let n=a.value,r=T(p),i=t===n;if(g||y||!n.trim())return;if(r)return k(r,`error`);if(new TextEncoder().encode(n).length>16384)return k(`The text is longer than 16 KB; shorten it.`,`error`);let o=!i&&n.match(_)?.[0].trim();if(o)return A(`${o} ends this session or switches its pane to another one.`,`Send ${o}`,()=>I(e,n));P(`input`,{text:n,as:e,confirmed:i},n)}function L(){let e=T(p),t=p.mark===`working`;a.placeholder=e??`Message ${p.name} · ${i?``:`Enter sends, Shift+Enter adds a line · `}lands after any unsent text in its terminal`,o.disabled=g||!!e,s.hidden=c.hidden=!t||!!e,s.disabled=c.disabled=g,Object.assign(u,C(p,g))}f.addEventListener(`submit`,e=>(e.preventDefault(),I(`send`))),s.onclick=()=>I(`followUp`),c.onclick=()=>A(`Stop the current task? Queued messages go back to its terminal editor.`,`Stop`,()=>void(g||P(`keys`,{keys:[`esc`]}))),u.onclick=()=>void(g||P(`focus`,{})),a.addEventListener(`input`,()=>(D(),O())),f.addEventListener(`keydown`,e=>{e.key===`Escape`&&!e.isComposing&&(y||e.target===a&&a.value)&&(e.preventDefault(),y&&(y=!1,k(``),a.focus()))}),a.addEventListener(`keydown`,e=>{e.isComposing||e.keyCode===229||e.key!==`Enter`||e.shiftKey||i&&!e.altKey||(e.preventDefault(),I(e.altKey?`followUp`:`send`))});let R=window.visualViewport,z=()=>document.documentElement.style.setProperty(`--vvh`,`${R.height}px`);return R&&(R.addEventListener(`resize`,z),z()),O(),v.has(t)?F(v.get(t)):E(()=>{let e=x(sessionStorage.getItem(j));sessionStorage.removeItem(j),e&&k(e[1],e[0])}),{box:a,update:e=>(p=e,L()),ready:()=>!T(p)}}var D=200;function O(e,t){for(let n of e?.bases??[]){if(n.tower?.key===t)return{u:{key:t,name:n.tower.name,role:`tower`,mark:n.tower.mark,control:n.tower.control,pending:n.tower.pending,subs:[]},base:n};for(let[e,r]of[...n.tabs.map(e=>[e.label,e.units]),[void 0,n.outside]])for(let i of r){if(i.key===t)return{u:i,base:n,squad:e};let r=i.subs.find(e=>e.key===t);if(r)return{u:r,base:n,squad:e,launcher:i}}}}var k=()=>a(`<a class="token">${i}<span class="name"></span><span class="line"><em class="role"></em><span class="note"></span></span></a>`);function A(n,i,a){e(n,i.key?{view:`session`,ws:a,key:i.key}:void 0),n.toggleAttribute(`hidden`,!1),n.toggleAttribute(`data-alert`,!!i.key&&o(i.mark)),u(n,i.mark),t(n.querySelector(`.plate`),i.role,i.mark),r(n,`.name`,i.name),r(n,`.role`,i.role===`outside`?`unaligned`:i.role),r(n,`.note`,c(i.note,i.mark))}var j=()=>a(`<div class="col"><a class="token">${i}<span class="name"></span><span class="line"><em class="role"></em><span class="note"></span></span></a><div class="say"><p></p></div><div class="drones"></div></div>`),M=`<div class="zoom-in"><header class="base keep"><a class="tower big"><i class="sweep"></i><i class="pulse"></i><svg><use href="#i-tower"/></svg><b class="badge"></b></a><div class="title"><span class="crown"><svg><use href="#i-crown"/></svg>head tower</span><h2></h2><p></p></div><dl class="tally"><div><dt>Squads</dt><dd></dd></div><div><dt>Units</dt><dd></dd></div><div data-mark="working"><dt>Working</dt><dd></dd></div><div data-mark="waiting"><dt>Waiting</dt><dd></dd></div></dl></header><div class="squads"></div><div class="camps"><section class="tab squad camp" data-group="outposts"><h3><svg><use href="#i-outpost"/></svg><b>Outposts</b><small></small></h3><div class="formation"></div></section><section class="tab squad camp" data-group="outside"><h3><svg><use href="#i-outside"/></svg><b>Outside the net</b><small>unaligned pi panes</small></h3><div class="formation"></div></section></div><p class="lost" hidden></p></div>`,N=`<div class="dossier-in"><span class="tag">unit dossier</span><div class="portrait">${i}</div><h3></h3><p class="d-line"><em class="role"></em> <span class="state"></span></p><p class="d-note" hidden></p><dl class="specs"></dl><div class="d-drones" hidden><h4>Dispatched</h4><div class="formation"></div></div></div>`,P=`<div class="comms-in"><header class="comms-head"><span class="logo"><svg><use href="#i-comms"/></svg></span><div><h2><small>comms</small> <span></span></h2><p></p></div><span class="chan link" data-state="live"><i></i><b>channel open</b></span></header><div class="chat-root"></div><div class="ask-root"></div><div class="composer-root"></div></div>`;function F({data:i,restart:d,openTile:f,online:m}){let h=e=>document.getElementById(e),[g,_,v]=[`ws-view`,`session-view`,`dossier`].map(h),b={view:`map`},x,S={},C,w=[],T=!1,F,I,L,R=0,z,B,V=()=>b.view===`map`?``:b.ws,H=(t,n)=>{let r=document.createElement(`a`);return e(r,t),r.textContent=n,r};function U(){let t=(t,n,r)=>{let i=document.createElement(t);return i.textContent=n,r&&e(i,r),i},n=h(`crumbs`),r=b;if(r.view===`map`)return n.replaceChildren(t(`b`,`Straw Net`),t(`span`,`all bases`));let i=x?.bases.find(e=>e.id===r.ws)?.label??r.ws,a=r.view===`session`?O(x,r.key)?.u.name??F?.name??r.key:``;n.replaceChildren(t(`a`,`Straw Net`,{view:`map`}),r.view===`workspace`?t(`b`,i):t(`a`,i,{view:`workspace`,ws:r.ws}),...a?[t(`b`,a)]:[])}function W(e,t,n){let r=e=>[`map`,`workspace`,`session`].indexOf(e.view),i=r(e)<r(t)?`enter-back`:e.view===`workspace`&&n?`enter-zoom`:e.view===`session`&&t.view!==`session`?`enter-slide`:``,a=h({map:`map`,workspace:`ws-view`,session:`session-view`}[e.view]);if(i){if(n){let e=a.getBoundingClientRect();a.style.setProperty(`--ox`,`${Math.round(n.x+n.width/2-e.x)}px`),a.style.setProperty(`--oy`,`${Math.round(n.y+n.height/2-e.y)}px`)}a.classList.remove(`enter-zoom`,`enter-slide`,`enter-back`),a.offsetWidth,a.classList.add(i),setTimeout(()=>a.classList.remove(i),700)}}function G(){g.firstElementChild||g.append(a(M));let t=g.firstElementChild,i=x?.bases.find(e=>e.id===V()),c=t.querySelector(`.lost`);if(c.hidden=!!i,c.textContent=x?`This workspace is not part of the net.`:`Waiting for the net…`,t.classList.toggle(`void`,!i),!i)return;let l=t.querySelector(`.keep`);u(l,i.mark),l.classList.toggle(`head`,i.head);let d=t.querySelector(`.tower`);d.classList.toggle(`ghost`,!i.tower),u(d,i.tower?.mark??`unknown`),e(d,i.tower?{view:`session`,ws:i.id,key:i.tower.key}:void 0),d.toggleAttribute(`hidden`,!1),d.toggleAttribute(`data-alert`,!!(i.tower&&o(i.tower.mark))),r(t,`h2`,i.label),r(t,`.title p`,i.tower?`control tower · ${s[i.tower.mark]}`:`no tower yet`);let f=i.tabs.flatMap(e=>e.units.flatMap(e=>[e,...e.subs]));[i.tabs.length,f.length,f.filter(e=>e.mark===`working`).length,f.filter(e=>e.mark===`waiting`).length].forEach((e,n)=>t.querySelectorAll(`dd`)[n].textContent=String(e));let p=(e,t)=>{u(e,t.mark),A(e.querySelector(`.token`),t,i.id);let a=e.querySelector(`.say`),o=S[t.key]??``;a.hidden=!o,r(a,`p`,o),n(e.querySelector(`.drones`),t.subs,k,(e,t)=>(e.classList.add(`drone`),A(e,t,i.id)))};n(t.querySelector(`.squads`),i.tabs,()=>a(`<section class="tab squad"><h3><i class="flag"></i><b></b><small></small></h3><div class="formation"></div></section>`),(e,t)=>{r(e,`h3 b`,`squad ${t.label}`);let i=t.units.flatMap(e=>[e,...e.subs]);r(e,`h3 small`,`${i.length} unit${i.length===1?``:`s`}`),u(e,[`waiting`,`error`,`working`].find(e=>i.some(t=>t.mark===e))??`idle`),n(e.querySelector(`.formation`),t.units,j,p)});let m=(e,r)=>{let a=t.querySelector(`[data-group="${e}"]`);a.hidden=!r.length,n(a.querySelector(`.formation`),r,k,(e,t)=>A(e,t,i.id))};m(`outposts`,i.outposts.map(e=>({id:e.id,name:e.label,role:`outpost`,mark:e.mark,note:e.mark===`unknown`?`no agent running`:void 0}))),m(`outside`,i.outside)}function K(){if(b.view!==`session`)return;v.firstElementChild||v.append(a(N));let e=v.firstElementChild,i=O(x,b.key),l=i?.u,d=l?.mark??`unknown`;u(e,d),t(e.querySelector(`.plate`),l?.role??F?.role??`member`,d),r(e,`h3`,l?.name??F?.name??b.key),r(e,`.role`,l?.role===`outside`?`unaligned`:l?.role??F?.role??`session`),r(e,`.state`,l?c(l.note,d):`not in the net`);let f=e.querySelector(`.d-note`),p=F?.state;f.hidden=p!==`recent`&&p!==`unknown`,f.replaceChildren(p===`unknown`?`Not in the net (it may have ended more than ten minutes ago).`:`No longer live; its history stays readable for ten minutes.`),F?.successor&&f.append(` Now: `,H({view:`session`,ws:V(),key:F.successor},O(x,F.successor)?.u.name??`the new session`));let h=[[`model`,I],[`context`,L?`${L.toLocaleString()} tokens`:void 0],[`directory`,F?.header?.cwd],[`base`,i&&H({view:`workspace`,ws:i.base.id},i.base.label)],[`squad`,i?.squad],[`launcher`,i?.launcher&&H({view:`session`,ws:i.base.id,key:i.launcher.key},i.launcher.name)],[`unreadable`,F?.unreadable?`${F.unreadable} entr${F.unreadable===1?`y`:`ies`}`:void 0]];e.querySelector(`.specs`).replaceChildren(...h.filter(([,e])=>e).map(([e,t])=>{let n=a(`<div><dt></dt><dd></dd></div>`);return n.firstElementChild.textContent=e,n.lastElementChild.append(t),n}));let g=e.querySelector(`.d-drones`);g.hidden=!l?.subs.length,n(g.querySelector(`.formation`),(l?.subs??[]).map(e=>({...e,id:e.key})),k,(e,t)=>(e.classList.add(`drone`),A(e,t,V())));let y=_.querySelector(`.comms-head`);if(y){r(y,`h2 span`,l?.name??F?.name??``),r(y,`p`,[i?.base.label,i?.squad&&`squad ${i.squad}`].filter(Boolean).join(` · `));let e=y.querySelector(`.chan`),t=p===`live`||!p&&l;e.dataset.state=t?`live`:`closed`,r(e,`b`,t?`channel open`:p===`recent`?`channel closed`:`no channel`)}C?.setLive(d===`working`?`${c(l.note,d)}`:o(d)?s[d]:``,d),z?.update({name:l?.name??F?.name??b.key,control:!!l?.control,live:p?p===`live`:!!l,mark:d,online:m(),asking:!!l?.pending}),B?.update(l?.pending,d===`waiting`&&!!l?.control&&m())}let q=()=>setTimeout(()=>b.view===`session`&&(document.activeElement??document.body)===document.body&&(matchMedia(`(pointer: fine)`).matches&&z?.ready()?z.box:C?.log)?.focus({preventScroll:!0}));async function J(e){let t=++R,{mountChat:n}=await l(async()=>{let{mountChat:e}=await import(`./chat-jdyprBMu.js`);return{mountChat:e}},__vite__mapDeps([0]),import.meta.url);if(t!==R||b.view!==`session`)return;_.replaceChildren(a(P)),C=n(_.querySelector(`.chat-root`),i(e),{who:()=>O(x,e)?.u??{key:e,name:F?.name??e,role:F?.role??`member`,mark:`unknown`},link:t=>{let n=O(x,e)?.u.subs.find(e=>e.name===t)??x?.bases.flatMap(e=>[...e.tabs.flatMap(e=>e.units),...e.outside]).find(e=>e.name===t);return n&&H({view:`session`,ws:O(x,n.key)?.base.id??V(),key:n.key},t)},openTile:f});let r=i(e).post??y(e);[z,B]=[E(_.querySelector(`.composer-root`),e,r),p(_.querySelector(`.ask-root`),()=>r(`focus`,{}))],C.reveal(b.at),w.splice(0).forEach(Y),K(),q()}function Y(e){C?.apply(e);for(let t of e.items??[])t.kind===`assistant`&&([I,L]=[t.model??I,t.ctx??L])}let X=()=>(U(),b.view===`workspace`?G():K());return{locate:e=>O(x,e),show(e,t,n){let[r,i]=b.view===`session`?[b.key,b.at]:[];(e.view!==`workspace`||b.view!==`workspace`||e.ws!==b.ws)&&(S={}),b=e,g.hidden=e.view!==`workspace`,_.hidden=v.hidden=e.view!==`session`,e.view===`session`&&e.key!==r?([C,z,B,F,I,L,w,T]=[void 0,void 0,void 0,void 0,void 0,void 0,[],!1],_.replaceChildren(a(`<p class="lost">Opening the channel…</p>`)),J(e.key)):e.view===`session`&&e.at!==i?(C?.reveal(e.at),q()):e.view!==`session`&&(R++,C=z=B=void 0),X(),e!==t&&W(e,t,n)},net(e){x=e,b.view!==`map`&&X()},cards(e){S={...S,...e},b.view===`workspace`&&G()},history(e){if(b.view===`session`&&e.key===b.key){if(F={...F,...e,items:void 0},e.reset&&([I,L]=[void 0,void 0]),document.hidden)return w.length<D?void w.push(e):void(T=!0);C?Y(e):w.push(e),K()}},wake(){if(T)return T=!1,w=[],d();w.splice(0).forEach(e=>C&&Y(e)),X()}}}export{F as mountFocus};
@@ -6,8 +6,8 @@
6
6
  <meta name="referrer" content="no-referrer">
7
7
  <title>Straw Net</title>
8
8
  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='8' fill='%230b1220'/%3E%3Cpath d='M16 5l9 7h-4v13h-10V12H7z' fill='%2338e0ff'/%3E%3C/svg%3E">
9
- <script type="module" crossorigin src="./assets/index-Cn7_VOw9.js"></script>
10
- <link rel="stylesheet" crossorigin href="./assets/index-5NVJByUz.css">
9
+ <script type="module" crossorigin src="./assets/index-C_WFzbRO.js"></script>
10
+ <link rel="stylesheet" crossorigin href="./assets/index-BccsK_Zr.css">
11
11
  </head>
12
12
  <body>
13
13
  <!-- Icons: game-icons.net, CC BY 3.0 (Lorc, Delapouite); see gui/ICONS.md -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-straw-boss",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Straw Boss coordination for pi in Herdr: sessions join a tab's net by choice, the first joiner is its main agent, a workspace tower opens on demand with a live monitoring dashboard, and boss_broadcast reaches the rest",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,7 +30,8 @@
30
30
  "pi": {
31
31
  "extensions": [
32
32
  "./src/index.ts",
33
- "./src/balance.ts"
33
+ "./src/balance.ts",
34
+ "./src/blocked.ts"
34
35
  ]
35
36
  },
36
37
  "scripts": {
package/src/ask.ts CHANGED
@@ -2,16 +2,18 @@ import { open, stat } from "node:fs/promises";
2
2
  import { type Json, parse } from "./items.ts";
3
3
  import type { Net } from "./net.ts";
4
4
 
5
- // What a waiting session waits on, for display only: its newest assistant message's one tool call with no result yet, read from the end of
5
+ // What a waiting session waits on, for display only: the current branch's newest assistant message's tool call with no result yet, read from the end of
6
6
  // its session file. The snapshot carries it so the alert and the session view can show an ask_user question (its context and options);
7
7
  // the user answers it in the terminal. Nothing here sends anything: answering from the browser is deferred (see the spec, "Pending dialogs").
8
+ // Two tools are read: pi-ask-user's `ask_user` (one question with a context) and @juicesharp/rpiv-ask-user-question's `ask_user_question` (one to four questions
9
+ // with a header, options with descriptions, and a multi-select flag; no context). Older session files hold the first, new ones the second.
8
10
 
9
- const TAIL = 262_144, CACHE = new Map<string, { sig: string; pending?: Pending }>();
11
+ const TAIL = 262_144, LIMIT = 16_777_216, CACHE = new Map<string, { sig: string; pending?: Pending }>();
10
12
  export interface Option { title: string; description?: string }
11
- /** `multiple`: it takes several choices; `freeform`: it takes the user's own words. */
12
- export interface Ask { question: string; context?: string; options: Option[]; multiple: boolean; freeform: boolean }
13
- /** What a waiting session waits on: the question when it is one ask_user question, else `why` there is nothing to show. */
14
- export interface Pending { callId: string; tool: string; ask?: Ask; why?: string }
13
+ /** `multiple`: it takes several choices; `freeform`: it takes the user's own words; `header`: the short tag ask_user_question gives a question. */
14
+ export interface Ask { question: string; header?: string; context?: string; options: Option[]; multiple: boolean; freeform: boolean }
15
+ /** What a waiting session waits on: `ask` for one question, `asks` for two to four (ask_user_question), else `why` there is nothing to show. */
16
+ export interface Pending { callId: string; tool: string; ask?: Ask; asks?: Ask[]; why?: string }
15
17
  interface Call { id: string; name: string; args: Json }
16
18
 
17
19
  const clip = (v: unknown, n: number) => String(v ?? "").slice(0, n);
@@ -23,24 +25,47 @@ const option = (o: unknown): Option | undefined => {
23
25
  return title ? { title: clip(title.trim(), 200), ...(typeof r.description === "string" && r.description.trim() ? { description: clip(r.description, 600) } : {}) } : undefined;
24
26
  };
25
27
 
26
- /** The unanswered tool calls of the newest assistant message, read from the file's last 256 KB; none once a user message follows it. */
27
- export async function openCalls(file: string): Promise<Call[]> {
28
+ /**
29
+ * The unanswered tool calls of the current branch's newest assistant message, none once a user message follows it. The branch is the path
30
+ * from the file's last entry up through parentId (pi's own rule, as history.ts reads it), so a branch left behind never shows. The window is
31
+ * the file's last 256 KB; when the path leaves it before reaching a decisive entry (one very long call), the window grows to `LIMIT`, and
32
+ * past that the answer is "cut".
33
+ */
34
+ export async function openCalls(file: string): Promise<Call[] | "cut"> {
28
35
  const fh = await open(file, "r");
29
36
  try {
30
- const { size } = await fh.stat(), start = Math.max(0, size - TAIL), buf = Buffer.alloc(size - start);
31
- await fh.read(buf, 0, buf.length, start);
32
- const answered = new Set<string>();
33
- for (const line of buf.toString("utf8").split("\n").slice(start ? 1 : 0).reverse()) {
34
- const m = line.includes('"message"') ? parse(line)?.message : undefined;
35
- if (m?.role === "toolResult") answered.add(String(m.toolCallId));
36
- if (m?.role === "user") return [];
37
- if (m?.role === "assistant") return (Array.isArray(m.content) ? m.content : []).filter((b: Json) => b?.type === "toolCall" && !answered.has(String(b.id))).map((b: Json) => ({ id: String(b.id), name: String(b.name), args: b.arguments ?? {} }));
37
+ const { size } = await fh.stat();
38
+ for (let span = TAIL; ; span *= 4) {
39
+ const start = Math.max(0, size - span), buf = Buffer.alloc(size - start);
40
+ await fh.read(buf, 0, buf.length, start);
41
+ const found = walk(buf.toString("utf8").split("\n").slice(start ? 1 : 0), start > 0);
42
+ if (found !== "cut" || span >= LIMIT) return found;
38
43
  }
39
- return [];
40
44
  } finally {
41
45
  await fh.close();
42
46
  }
43
47
  }
48
+ const LINK = /"id":"([^"]+)","parentId":(?:null|"([^"]*)")/;
49
+ function walk(lines: string[], partial: boolean): Call[] | "cut" {
50
+ const entries = new Map<string, { parent?: string; m?: Json }>();
51
+ let leaf: string | undefined;
52
+ for (const line of lines) {
53
+ const link = LINK.exec(line.slice(0, 300)) ?? LINK.exec(line.slice(-200));
54
+ if (!link) continue;
55
+ leaf = link[1]!;
56
+ entries.set(leaf, { parent: link[2], m: line.includes('"message"') ? parse(line)?.message : undefined });
57
+ }
58
+ const answered = new Set<string>();
59
+ for (let at = leaf, steps = 0; at && steps++ <= entries.size; at = entries.get(at)!.parent) { // a path longer than the file has a cycle: nothing decisive
60
+ const e = entries.get(at);
61
+ if (!e) return partial ? "cut" : [];
62
+ const m = e.m;
63
+ if (m?.role === "toolResult") answered.add(String(m.toolCallId));
64
+ if (m?.role === "user") return [];
65
+ if (m?.role === "assistant") return (Array.isArray(m.content) ? m.content : []).filter((b: Json) => b?.type === "toolCall" && !answered.has(String(b.id))).map((b: Json) => ({ id: String(b.id), name: String(b.name), args: b.arguments ?? {} }));
66
+ }
67
+ return partial && !leaf ? "cut" : [];
68
+ }
44
69
 
45
70
  /** The question to show, or why there is none. Shown as plain text; the page never turns it into markup. */
46
71
  export function askOf(a: Json): { ask: Ask } | { why: string } {
@@ -51,13 +76,24 @@ export function askOf(a: Json): { ask: Ask } | { why: string } {
51
76
  return { ask: { question: clip(a.question, 2000), ...(context && { context }), options, multiple: a.allowMultiple === true, freeform: !options.length || a.allowFreeform !== false } };
52
77
  }
53
78
 
79
+ /** ask_user_question's questions: one reads like an ask_user question, two to four come as a list; "Type something." is always offered, so its own words are always taken. */
80
+ export function questionsOf(a: Json): { ask: Ask } | { asks: Ask[] } | { why: string } {
81
+ const asks: Ask[] = [];
82
+ for (const q of (Array.isArray(a.questions) ? a.questions : []).slice(0, 4)) {
83
+ if (typeof q?.question !== "string" || !q.question.trim()) return { why: "Its question could not be read." };
84
+ const options = (Array.isArray(q.options) ? q.options : []).map(option).filter((o: Option | undefined): o is Option => Boolean(o)).slice(0, 50);
85
+ asks.push({ question: clip(q.question, 2000), ...(typeof q.header === "string" && q.header.trim() ? { header: clip(q.header.trim(), 40) } : {}), options, multiple: q.multiSelect === true, freeform: true });
86
+ }
87
+ return asks.length === 1 ? { ask: asks[0]! } : asks.length ? { asks } : { why: "Its question could not be read." };
88
+ }
89
+
54
90
  /** What a waiting session waits on, for the snapshot; cached while the file is unchanged. */
55
91
  export async function pendingOf(file: string): Promise<Pending | undefined> {
56
92
  const s = await stat(file).catch(() => undefined), sig = s ? `${s.size}:${s.mtimeMs}` : "", hit = CACHE.get(file);
57
93
  if (hit?.sig === sig) return hit.pending;
58
- const calls = s ? await openCalls(file).catch(() => []) : [], [c] = calls;
59
- const shape = c?.name === "ask_user" ? askOf(c.args) : { why: c?.name === "secret_drop" ? "It waits for a secret." : "It waits on a dialog." };
60
- const pending = c && { callId: c.id, tool: c.name, ...(calls.length > 1 ? { why: "It waits on more than one dialog." } : shape) };
94
+ const found = s ? await openCalls(file).catch(() => []) : [], calls = found === "cut" ? [] : found, [c] = calls;
95
+ const shape = c?.name === "ask_user" ? askOf(c.args) : c?.name === "ask_user_question" ? questionsOf(c.args) : { why: c?.name === "secret_drop" ? "It waits for a secret." : "It waits on a dialog." };
96
+ const pending = found === "cut" ? { callId: `cut:${sig}`, tool: "dialog", why: "Its call is too long to read here." } : c && { callId: c.id, tool: c.name, ...(calls.length > 1 ? { why: "It waits on more than one dialog." } : shape) };
61
97
  CACHE.set(file, { sig, pending });
62
98
  if (CACHE.size > 64) CACHE.delete(CACHE.keys().next().value!);
63
99
  return pending;
package/src/blocked.ts ADDED
@@ -0,0 +1,19 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+
3
+ // rpiv-ask-user-question reports its wait as `rpiv:ask-user:blocked`, while Herdr's pi integration and the GUI listen for
4
+ // `herdr:blocked`. This relays one into the other, once per open questionnaire, so a session waiting on it shows as blocked.
5
+ // Every pi does this, joined or not, subagents included.
6
+
7
+ export const RPIV_BLOCKED = "rpiv:ask-user:blocked";
8
+ const LABEL = "Waiting for an answer to ask_user_question";
9
+
10
+ export default function blockedRelay(pi: ExtensionAPI): void {
11
+ const open = new Set<string>();
12
+ pi.events.on(RPIV_BLOCKED, (data) => {
13
+ const { active, toolCallId } = (data ?? {}) as { active?: unknown; toolCallId?: unknown };
14
+ if (typeof toolCallId !== "string" || typeof active !== "boolean" || open.has(toolCallId) === active) return;
15
+ if (active) open.add(toolCallId);
16
+ else open.delete(toolCallId);
17
+ pi.events.emit("herdr:blocked", active ? { active, label: LABEL } : { active });
18
+ });
19
+ }
package/src/dashboard.ts CHANGED
@@ -12,16 +12,16 @@ import { headOf, type HerdrRun, isSub, type Pane, panes, parentOf, type Session,
12
12
  // - `herdr pane list`: per pane the `sb_role` token (main, member), the pi session file, and Herdr's agent_status
13
13
  // (blocked means the agent waits on a question); `herdr workspace list`: workspace status, `tower` token, worktree.
14
14
  // - pi-herdr-agents subagents: a child's activity file lives at <sessions dir>/artifacts/<parent session id>/
15
- // subagent-activity/<child id>.json, and its pane's session file name starts with `<timestamp>_<child id>`.
15
+ // subagent-activity/<child id>.json, and its pane's session file is named `<timestamp>_<child id>-<8 hex>-<8 hex>-<4 hex>.jsonl` (pi-herdr-agents makes it so).
16
16
  // The parent pane's own session file names the artifacts directory, so children group under the session that launched them.
17
17
 
18
18
  export type Mark = "working" | "waiting" | "idle" | "done" | "error" | "unknown";
19
19
  /** One line of the tree. `net` rows are tab (0) > session (1) > subagent (2); `towers` rows are flat. */
20
20
  export interface Row { group: "net" | "towers"; depth: number; mark: Mark; text: string; kind?: string; note?: string; workspace?: string; id?: string; file?: string; pane?: string }
21
- export interface Snapshot { title: string; mark: Mark; head: boolean; rows: Row[] }
21
+ export interface Snapshot { title: string; mark: Mark; head: boolean; rows: Row[]; gui?: string }
22
22
  export type Tone = "accent" | "warning" | "muted" | "success" | "dim" | "error" | "border" | "title";
23
23
  export type Paint = (tone: Tone, text: string) => string;
24
- export interface Sources { run: HerdrRun; sessions(): Promise<Session[]>; workspaceId: string; now?: number }
24
+ export interface Sources { run: HerdrRun; sessions(): Promise<Session[]>; workspaceId: string; now?: number; gui?(): Promise<string | undefined> }
25
25
 
26
26
  const KEY = "straw-boss-dashboard";
27
27
  const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
@@ -43,7 +43,7 @@ export function sessionMark(s: Session, pane?: Pane): Mark {
43
43
  const pad = (text: string, width: number) => text + " ".repeat(Math.max(0, width - visibleWidth(text)));
44
44
 
45
45
  /** The pure half: a snapshot in, a framed tree out. One shared spinner frame animates every working row. */
46
- export function render({ title, mark, head, rows }: Snapshot, frame: number, width: number, paint: Paint): string[] {
46
+ export function render({ title, mark, head, rows, gui }: Snapshot, frame: number, width: number, paint: Paint): string[] {
47
47
  const total = Math.min(width, MAX_WIDTH), inner = Math.max(8, total - 4);
48
48
  const glyph = (m: Mark) => paint(TONE[m], m === "working" ? SPINNER[frame % SPINNER.length]! : GLYPH[m as Exclude<Mark, "working">]);
49
49
  const shown = rows.slice(0, MAX_ROWS);
@@ -58,13 +58,18 @@ export function render({ title, mark, head, rows }: Snapshot, frame: number, wid
58
58
  const at = shown.flatMap((r, i) => (r.group === group ? [i] : []));
59
59
  return at.length || empty ? [paint("muted", name), ...(at.length ? at.map(line) : [paint("dim", ` ${empty}`)])] : [];
60
60
  };
61
- const body = [...section("net", "this net", "nothing has joined yet"), ...section("towers", "other towers"), ...(rows.length > MAX_ROWS ? [paint("muted", `… ${rows.length - MAX_ROWS} more`)] : [])];
61
+ // What this pane is, and where the browser view lives, come before the tree.
62
+ const intro = [paint("dim", "Control tower: main agents talk here (call_tower)."), gui ? `${paint("muted", "GUI")} ${paint("accent", gui)}` : paint("dim", "GUI not running; /boss gui opens it"), ""];
63
+ const body = [...intro, ...section("net", "this net", "nothing has joined yet"), ...section("towers", "other towers"), ...(rows.length > MAX_ROWS ? [paint("muted", `… ${rows.length - MAX_ROWS} more`)] : [])];
62
64
  const [name, tag] = [` ${glyph(mark)} ${paint("title", `tower · ${title}`)} `, head ? ` ${paint("accent", "head tower")} ` : ""];
63
65
  const dashes = "─".repeat(Math.max(1, total - 4 - visibleWidth(name) - visibleWidth(tag)));
64
66
  const frameLine = (text: string) => paint("border", "│ ") + pad(truncateToWidth(text, inner), inner) + paint("border", " │");
65
67
  return [paint("border", "╭─") + name + paint("border", dashes) + tag + paint("border", "─╮"), ...body.map(frameLine), paint("border", `╰${"─".repeat(total - 2)}╯`)].map((l) => truncateToWidth(l, width));
66
68
  }
67
69
 
70
+ /** Whether a session file is the child `id`'s: pi-herdr-agents names it `<timestamp>_<id>-<hex>-<hex>-<hex>.jsonl`, so `kid` does not match `kid-2`'s or `kidney`'s. */
71
+ const ownFile = (file: string | undefined, id: string) => new RegExp(`_${id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(-[0-9a-f]{1,8}-[0-9a-f]{1,8}-[0-9a-f]{1,4})?\\.jsonl$`).test(file ?? "");
72
+
68
73
  function readJson(path: string): Record<string, unknown> {
69
74
  try {
70
75
  return JSON.parse(readFileSync(path, "utf8"));
@@ -86,7 +91,7 @@ export function subagents(parent: Pane | undefined, all: Pane[], now: number): R
86
91
  .map((name) => {
87
92
  const id = name.replace(/\.json$/, "");
88
93
  const path = join(dir, "subagent-activity", name);
89
- return { id, path, pane: all.find((p) => p.agent_session?.value.includes(`_${id}`)), age: now - statSync(path).mtimeMs };
94
+ return { id, path, pane: all.find((p) => ownFile(p.agent_session?.value, id)), age: now - statSync(path).mtimeMs };
90
95
  })
91
96
  .filter((c) => c.pane || c.age < RECENT_MS)
92
97
  .map(({ id, path, pane }) => {
@@ -111,7 +116,7 @@ export function subagents(parent: Pane | undefined, all: Pane[], now: number): R
111
116
  * One reading of the world as rows: this tower's own net in detail (each tab's joined sessions with their subagents, then its
112
117
  * sub workspaces), and one summary line for every other tower. Other workspaces' sessions, and sessions that did not join, are not listed.
113
118
  */
114
- export async function collect({ run, sessions, workspaceId, now = Date.now() }: Sources): Promise<Snapshot> {
119
+ export async function collect({ run, sessions, workspaceId, now = Date.now(), gui }: Sources): Promise<Snapshot> {
115
120
  const [live, ps, all] = [await sessions(), await panes(run), await workspaces(run)];
116
121
  const head = headOf(all);
117
122
  const mine = all.find((w) => w.workspace_id === workspaceId);
@@ -136,7 +141,7 @@ export async function collect({ run, sessions, workspaceId, now = Date.now() }:
136
141
  for (const w of all.filter((x) => x !== mine && !isSub(x) && live.some((s) => s.id === x.tokens?.tower))) rows.push({ group: "towers", depth: 0, mark: markOf(w.agent_status), text: w.label, kind: w === head ? "head tower" : "tower" });
137
142
  // The title shows what needs attention most: a waiting row, then a working one, else the workspace's own status.
138
143
  const attention = (["waiting", "working"] as const).find((m) => rows.some((r) => r.mark === m));
139
- return { title: mine?.label ?? workspaceId, mark: attention ?? markOf(mine?.agent_status), head: Boolean(mine) && mine === head, rows };
144
+ return { title: mine?.label ?? workspaceId, mark: attention ?? markOf(mine?.agent_status), head: Boolean(mine) && mine === head, rows, gui: await gui?.().catch(() => undefined) };
140
145
  }
141
146
 
142
147
  export interface Dashboard { poke(): void; stop(): void }
package/src/gui.ts CHANGED
@@ -150,7 +150,7 @@ export async function startGui({ read, net, call, dir = BUNDLE, port = 0, token
150
150
  // instead of starting one. A dead server's port and token are reused, so a page left open reconnects to its successor.
151
151
  export interface GuiRecord { pid: number; port: number; token: string; csrf?: string; owner: string }
152
152
  export interface Shared { url: string; owner: string; mine?: Gui }
153
- const urlOf = (r: GuiRecord) => `http://127.0.0.1:${r.port}/${r.token}/`;
153
+ export const urlOf = (r: GuiRecord) => `http://127.0.0.1:${r.port}/${r.token}/`;
154
154
 
155
155
  export function readRecord(dir: string): GuiRecord | undefined {
156
156
  try {
package/src/index.ts CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from "node:url";
5
5
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
6
6
  import { Type, type Static, type TSchema } from "typebox";
7
7
  import { collect, startDashboard } from "./dashboard.ts";
8
- import { answers, type Gui, openBrowser, readRecord, sharedGui, startGui } from "./gui.ts";
8
+ import { answers, type Gui, openBrowser, readRecord, sharedGui, startGui, urlOf } from "./gui.ts";
9
9
  import { netSource } from "./net.ts";
10
10
  import { copyText, pairingKeys, showLines } from "./pairing.ts";
11
11
  import { type Run, switchRemote, tailscale } from "./remote.ts";
@@ -78,12 +78,17 @@ export function createStrawBoss({ run = herdrRun, api = herdrApi(), call = herdr
78
78
  return { content: [{ type: "text", text }], details };
79
79
  },
80
80
  });
81
+ // A join that failed after registering retries on the tab the session is in now: its channel when it has registered there already, else a new
82
+ // one. Intercom has no unregister, so a tab left behind stays registered; see `onEvent` for what that channel still does.
83
+ const channels = new Map<string, Channel>();
84
+ const use = (channel: Channel) => ((pinned = channel === c ? pinned : undefined), (c = channel)); // what was pinned on another tab's channel is not this one's
81
85
  const connect = (eligible: boolean) => {
82
- if (c) return; // a join that failed after registering retries on the channel it has
83
86
  const namespace = `straw-boss/tab/${Buffer.from(at.tabId).toString("hex")}`;
87
+ if (channels.has(namespace)) return void use(channels.get(namespace)!);
84
88
  if (!NAMESPACE.test(namespace)) throw new Error(`Tab id ${at.tabId} is too long for an intercom namespace (limit 24 characters)`);
85
- pi.events.emit(REGISTER, { namespace, ownerEligible: eligible, onReady: (channel: Channel) => (c = channel), onEvent });
86
- if (!c) throw new Error("pi-intercom is not loaded (Straw Boss needs pi-intercom >= 0.17.1)");
89
+ let mine: Channel | undefined;
90
+ pi.events.emit(REGISTER, { namespace, ownerEligible: eligible, onReady: (channel: Channel) => { mine = channel; channels.set(namespace, channel); use(channel); }, onEvent: (event: Event) => mine && onEvent(mine, event) });
91
+ if (!mine) throw new Error("pi-intercom is not loaded (Straw Boss needs pi-intercom >= 0.17.1)");
87
92
  };
88
93
 
89
94
  // Tokens are display and discovery only: a failed write waits for the next refresh, liveness comes from intercom.
@@ -98,31 +103,36 @@ export function createStrawBoss({ run = herdrRun, api = herdrApi(), call = herdr
98
103
  };
99
104
 
100
105
  // The owner records who is main. Promotion is first come, first served, and the main stays until it leaves: a later owner change never swaps it.
101
- const pin = (main: string) => {
102
- if (c.snapshot().owner?.sessionId !== me()) return;
103
- c.commitState({ v: 1, main, since: Date.now() });
104
- pinned = main;
106
+ const pin = (channel: Channel, main: string) => {
107
+ if (channel.snapshot().owner?.sessionId !== me()) return;
108
+ channel.commitState({ v: 1, main, since: Date.now() });
109
+ if (channel === c) pinned = main;
105
110
  };
106
111
  // The owner decides a claim: a tab has one main, so a claim is dropped while a live main exists, unless the user forced it with /boss.
107
- const claim = async (from: string, force: boolean) => {
112
+ const claim = async (channel: Channel, from: string, force: boolean) => {
108
113
  try {
109
- if (c.snapshot().owner?.sessionId !== me()) return;
110
- const current = pinned ?? (c.snapshot().state?.payload as { main?: string } | undefined)?.main;
111
- if (!force && current && current !== from && (await c.listSessions()).some((s) => s.id === current)) return;
112
- pin(from);
114
+ if (channel.snapshot().owner?.sessionId !== me()) return;
115
+ const current = (channel === c ? pinned : undefined) ?? (channel.snapshot().state?.payload as { main?: string } | undefined)?.main;
116
+ if (!force && current && current !== from && (await channel.listSessions()).some((s) => s.id === current)) return;
117
+ pin(channel, from);
113
118
  } catch (error) {
114
119
  tell(`Straw Boss: ${(error as Error).message}`, "warning");
115
120
  }
116
121
  };
117
- const onEvent = (event: { type: string; fromSessionId?: string; owner?: { sessionId: string }; payload?: { type?: string; force?: boolean } }) => {
122
+ type Event = { type: string; fromSessionId?: string; owner?: { sessionId: string }; payload?: { type?: string; force?: boolean } };
123
+ // A tab this session left stays registered, and if the session owns that channel it still decides that tab's claims (or its members could
124
+ // never promote); nothing else that channel says concerns the session's role.
125
+ const onEvent = (channel: Channel, event: Event) => {
126
+ if (event.type === "message" && event.payload?.type === "claim" && event.fromSessionId) {
127
+ const [from, force] = [event.fromSessionId, Boolean(event.payload.force)];
128
+ queue = queue.then(() => claim(channel, from, force));
129
+ }
130
+ if (channel !== c) return;
118
131
  // Ownership moved away: what this session pinned as owner is no longer its to defend.
119
132
  if (event.type === "owner" && event.owner?.sessionId !== me()) pinned = undefined;
120
133
  // A join that failed at start (intercom not connected yet, no owner yet) is retried when the channel changes.
121
- if (!joined && !barred && !isTower && c && ["connection", "owner"].includes(event.type)) void joinNet().catch(() => {});
122
- if (event.type === "message" && event.payload?.type === "claim" && event.fromSessionId) {
123
- const [from, force] = [event.fromSessionId, Boolean(event.payload.force)];
124
- queue = queue.then(() => claim(from, force));
125
- } else if (["connection", "owner", "state", "session_left"].includes(event.type)) void sync();
134
+ if (!joined && !barred && !isTower && ["connection", "owner"].includes(event.type)) void joinNet().catch(() => {});
135
+ if (["connection", "owner", "state", "session_left"].includes(event.type)) void sync();
126
136
  if (["presence_update", "session_joined", "session_left"].includes(event.type)) dashboard?.poke();
127
137
  };
128
138
  const refresh = async () => {
@@ -179,7 +189,7 @@ export function createStrawBoss({ run = herdrRun, api = herdrApi(), call = herdr
179
189
  if (isMain) return { promoted: true, text: `You are already the main agent of tab ${info.tab}.` };
180
190
  const refused = () => ({ promoted: false, text: `Tab ${info.tab} already has a main agent, ${info.main}. Stay a member: you may dispatch your own subagents, but tell ${info.main} through intercom first what you will do and which resources you will use.` });
181
191
  if (!force && mainId) return refused();
182
- if (c.snapshot().owner?.sessionId === me()) await (queue = queue.then(() => claim(me(), force)));
192
+ if (c.snapshot().owner?.sessionId === me()) await (queue = queue.then(() => claim(c, me(), force)));
183
193
  else c.publish({ type: "claim", force }, { audience: "owner" });
184
194
  if (!(await waitFor(sleep, () => isMain, CLAIM_MS))) {
185
195
  await sync();
@@ -306,7 +316,7 @@ export function createStrawBoss({ run = herdrRun, api = herdrApi(), call = herdr
306
316
  keepFresh();
307
317
  await publish();
308
318
  // The dashboard lives exactly as long as this session holds the tower role.
309
- if (hasUI) dashboard = startDashboard({ ui: ui!, collect: () => collect({ run, sessions: () => c.listSessions(), workspaceId: at.workspaceId }) });
319
+ if (hasUI) dashboard = startDashboard({ ui: ui!, collect: () => collect({ run, sessions: () => c.listSessions(), workspaceId: at.workspaceId, gui: async () => { const r = readRecord(guiDir); return r && (await answers(r)) ? urlOf(r) : undefined; } }) });
310
320
  };
311
321
 
312
322
  // A session in a sub workspace (a linked git worktree) acts like a subagent: it never joins. Failing to tell bars it too.
@@ -350,9 +360,9 @@ export function createStrawBoss({ run = herdrRun, api = herdrApi(), call = herdr
350
360
  else delete event.systemPromptOptions.sections[ENTRY];
351
361
  });
352
362
 
353
- // A member that dispatches a subagent without promoting is promoted here, before the subagent starts, unless the tab already has a main.
363
+ // A member that dispatches or resumes a subagent without promoting is promoted here, before the subagent starts, unless the tab already has a main.
354
364
  pi.on("tool_call", async (event) => {
355
- if (event.toolName !== "subagent" || isTower || barred) return;
365
+ if (!["subagent", "subagent_resume"].includes(event.toolName) || isTower || barred) return;
356
366
  try {
357
367
  await joinNet();
358
368
  if (!isMain) await promote(false);
package/src/items.ts CHANGED
@@ -36,6 +36,7 @@ function toolLine(name: string, a: Json): string {
36
36
  case "subagent": case "subagent_resume": return [a.name, a.agent].filter(Boolean).join(" · ");
37
37
  case "intercom": return [a.action, a.to, first(a.message, 80)].filter(Boolean).join(" ");
38
38
  case "ask_user": return first(a.question ?? a.questions?.[0]?.question);
39
+ case "ask_user_question": return first(a.questions?.[0]?.question) + (a.questions?.length > 1 ? ` (+${a.questions.length - 1} more)` : "");
39
40
  case "show_doc": return first([a.paths].flat().join(", "));
40
41
  case "todo": return [a.action, first(a.subject ?? a.id, 80)].filter(Boolean).join(" ");
41
42
  case "MonitorCreate": return first(a.description ?? a.command);
package/src/remote.ts CHANGED
@@ -1,13 +1,13 @@
1
- import { execFile } from "node:child_process";
1
+ import { execFile, execFileSync } from "node:child_process";
2
2
  import { randomBytes, timingSafeEqual } from "node:crypto";
3
- import { readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
4
4
  import type { IncomingMessage } from "node:http";
5
5
  import { join } from "node:path";
6
6
  import { qr, qrLines } from "./qr.ts";
7
7
 
8
8
  // Remote access over Tailscale, tailnet only (never Funnel). `/boss gui remote` adds one `tailscale serve` entry, HTTPS on 8443 to the local
9
- // server, only when 8443 is free or already ours (remote.json in the state directory remembers our target, so an entry a crashed session
10
- // left is recognised); turning it off, `/boss gui stop`, and the end of the server's session remove that one entry and nothing else.
9
+ // server, only when 8443 is free or already ours (remote.json in the state directory remembers our target, the server's PID, and when that
10
+ // process started, so an entry a crashed session left is recognised even after its PID went to another process); turning it off, `/boss gui stop`, and the end of the server's session remove that one entry and nothing else.
11
11
  // Which request is which: serve passes the client's Host through but always adds X-Forwarded-* and Tailscale-* headers (and replaces any a
12
12
  // client sent), so a local request is Host 127.0.0.1:<port> with none of them. A remote one needs Host exactly <MagicDNS name>:8443,
13
13
  // no Funnel marker, Tailscale-User-Login equal to this machine's user, and a session cookie (HttpOnly, Secure, SameSite=Strict, scoped to
@@ -131,6 +131,25 @@ const alive = (pid: unknown) => {
131
131
  return (error as NodeJS.ErrnoException).code === "EPERM";
132
132
  }
133
133
  };
134
+ /** When a process started, as `ps` prints it in the C locale and UTC (so two pi processes with other LANG or TZ write and read the same text); undefined when ps cannot say. A PID reused after a crash belongs to a process that started later. */
135
+ export const startOf = (pid: number): string | undefined => {
136
+ try {
137
+ return execFileSync("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8", timeout: 2000, stdio: ["ignore", "pipe", "ignore"], env: { ...process.env, LC_ALL: "C", TZ: "UTC" } }).trim() || undefined;
138
+ } catch {
139
+ return undefined;
140
+ }
141
+ };
142
+ /**
143
+ * Whether the server that recorded an entry still runs: its PID is alive and that process is the one that wrote the entry (same start
144
+ * time; an entry from before start times were recorded: a process that started after the file was written is not its writer). When ps
145
+ * cannot say, the entry counts as live, so another live server's entry is never taken for stale.
146
+ */
147
+ export function runs({ pid, started }: { pid?: unknown; started?: string }, wrote: number, start: (pid: number) => string | undefined = startOf) {
148
+ if (!alive(pid)) return false;
149
+ const now = start(pid as number);
150
+ if (!now) return true;
151
+ return started ? now === started : !(Date.parse(`${now} UTC`) > wrote);
152
+ }
134
153
  export const cookieFor = (token: string, value: string) => `sb_remote=${value}; Path=/${token}/; Max-Age=${SESSION_MS / 1000}; HttpOnly; Secure; SameSite=Strict`;
135
154
 
136
155
  /**
@@ -141,8 +160,8 @@ export async function switchRemote(gui: { port: number; token: string; access: A
141
160
  const target = `http://127.0.0.1:${gui.port}`, file = join(dir, "remote.json");
142
161
  let stale: string | undefined;
143
162
  try {
144
- const { target: was, pid } = JSON.parse(readFileSync(file, "utf8"));
145
- stale = pid === process.pid || !alive(pid) ? was : undefined; // another live server's entry (its session too busy to answer) is not stale
163
+ const record = JSON.parse(readFileSync(file, "utf8"));
164
+ stale = record.pid === process.pid || !runs(record, statSync(file).mtimeMs) ? record.target : undefined; // another live server's entry (its session too busy to answer) is not stale
146
165
  } catch {} // no entry was recorded
147
166
  if (!on) {
148
167
  // An entry a crashed session left (recorded in remote.json) is cleaned up too, while it still points where it was recorded.
@@ -156,7 +175,7 @@ export async function switchRemote(gui: { port: number; token: string; access: A
156
175
  }
157
176
  const id = await identity(run);
158
177
  await serveOn(run, id.host, target, stale);
159
- writeFileSync(file, JSON.stringify({ target, pid: process.pid }), { mode: 0o600 });
178
+ writeFileSync(file, JSON.stringify({ target, pid: process.pid, started: startOf(process.pid) }), { mode: 0o600 });
160
179
  gui.access.enable(id);
161
180
  // Every link is a new one-time pairing URL: the QR's own, and one per press of c or o in the overlay.
162
181
  const link = () => `https://${id.host}/${gui.token}/pair/${gui.access.pair()}`;
@@ -1 +0,0 @@
1
- import{n as e}from"./post-B39ywXKd.js";function t(t,n){let r,i=!1,a=!1,o=e(`p`,{class:`ask-note`,role:`status`,"aria-live":`polite`}),s=e(`button`,{type:`button`,class:`cmp-link`},`Focus terminal`),c=()=>s.disabled=!i||a;s.onclick=async()=>{a=!0,c();try{let e=await n();o.dataset.tone=e.ok?``:`error`,o.textContent=e.ok?``:e.body.reason??`Refused (${e.status}).`}catch{o.dataset.tone=`error`,o.textContent=`Herdr could not be reached.`}finally{a=!1,c()}};function l(n){o.textContent=``;let r=n.ask,i=r?.multiple?`It takes several choices.`:r?.freeform&&r.options.length?`It also takes your own words.`:``,a=r?.options.length?[e(`ol`,{class:`ask-opts`,"aria-label":`Options`},...r.options.map(t=>e(`li`,{class:`ask-opt`},e(`b`,null,t.title),...t.description?[e(`small`,null,t.description)]:[])))]:[],c=r?[e(`p`,{class:`ask-q`},r.question),...r.context?[e(`div`,{class:`ask-ctx`},r.context)]:[],...a]:[e(`p`,{class:`ask-why`},n.why??`It waits on a dialog.`)];t.replaceChildren(e(`section`,{class:`ask`},...c,e(`div`,{class:`ask-acts`},e(`p`,{class:`ask-term`},`Answer it in the terminal. ${i}`.trim()),s),o))}return{update(e,n){i=n,e&&e.callId!==r?l(e):!e&&r&&t.replaceChildren(),r=e?.callId,c()}}}export{t};
@@ -1,2 +0,0 @@
1
- import{a as e,c as t,d as n,f as r,i,l as a,m as o,o as s,s as c,u as l}from"./index-Cn7_VOw9.js";import{t as u}from"./post-B39ywXKd.js";import{t as d}from"./ask-DD_oYNss.js";var f=(e,t)=>o(e.mark)-o(t.mark)||e.at-t.at;function p(e){let t=[],n=[],r=()=>e.render(t.map(e=>e.alert),n),i=(i,a,o=!0)=>{let s=t.findIndex(e=>e.alert.key===i.key);if(s>=0&&!o)return;let c=s>=0?t.splice(s,1)[0]:{alert:i,back:a,raised:i.mark};!o&&t.length?t.splice(t.length-1,0,c):t.push(c),n=n.filter(e=>e.key!==i.key),r(),o&&e.enter(c.alert)};return{get open(){return t.map(e=>e.alert)},get queue(){return n},arrive(e){let i=t.find(t=>t.alert.key===e.key);if(i)[i.alert,i.raised]=[{...e,at:i.alert.at},e.mark];else{let t=n.find(t=>t.key===e.key);n=[...n.filter(t=>t.key!==e.key),t&&t.mark===e.mark?t:e].sort(f)}r()},prune(e){n=n.filter(t=>e(t.key)?.mark===t.mark);for(let n of t)n.alert={...n.alert,...e(n.alert.key),at:n.alert.at};r()},show:(t,n=!0)=>i(t,e.focused(),n),raise(n){let r=t.find(e=>e.alert.key===n);r&&r!==t.at(-1)&&i(r.alert,e.focused())},next(){n[0]&&i(n[0],e.focused())},close(a=`close`){let s=t.pop();if(!s)return!1;let c=t.at(-1),l=a===`close`?n.find(e=>o(e.mark)===0):void 0;return a===`later`&&s.alert.mark===s.raised&&(n=[...n,s.alert].sort(f)),!c&&l?(i(l,s.back),!0):(r(),!e.restore(s.back)&&c&&e.enter(c.alert),!0)}}}function m(e){let t=[],n,r=()=>e.render(t,n);return{get tiles(){return t},get active(){return n},open(i,a=!0){t.some(e=>e.id===i.id)||(t=[...t,i].slice(-4)),(a||!t.some(e=>e.id===n))&&(n=i.id),r(),a&&e.enter(t.find(e=>e.id===i.id))},close(e){let i=t.findIndex(t=>t.id===e);i<0||(t=t.filter(t=>t.id!==e),n===e&&(n=(t[i]??t[i-1])?.id),r())},activate(e){n=e,r()}}}function h(e){let t=new Set(e.flatMap(e=>e.kind===`result`&&e.callId?[e.callId]:[])),n=e.filter(e=>e.kind===`assistant`),r=n.at(-1),i=r?.tools?.find(e=>!t.has(e.id));return{said:(n.findLast(e=>e.text?.trim())?.text?.trim().split(`
2
- `).filter(Boolean).at(-1))?.slice(0,280),pending:i&&`${i.name} · ${i.summary}`.slice(0,200),at:(r??e.findLast(e=>e.kind!==`result`))?.id}}var g=()=>a(`<section class="dialog" role="dialog" aria-modal="false" tabindex="-1"><header><b class="tag">alert</b><span class="where"></span><button class="x" type="button" aria-label="Close">×</button></header><div class="who">${i}<div><h3></h3><p><em class="role"></em> <span class="state"></span></p></div></div><div class="intel"><h4>Last transmission</h4><p class="said">…</p><p class="pending" hidden><b>waiting on</b> <span></span></p></div><div class="ask-root"></div><footer><a class="go">Open comms</a><button class="later" type="button">Later</button><span class="queued"></span></footer></section>`),_=()=>a(`<section class="tile" tabindex="-1"><header><svg><use href="#i-doc"/></svg><b></b><button class="x" type="button" aria-label="Close document">×</button></header><div class="tile-body"></div></section>`);function v(o){let[f,v,y,b,x]=[s(`dock`,HTMLElement),s(`work`,HTMLElement),s(`tiles`,HTMLElement),s(`tile-tabs`,HTMLElement),s(`alerts`,HTMLButtonElement)],S=new Map,C=(t,n,i=S.get(n.key))=>{r(t,`.said`,i?.failed?`History unavailable.`:i?.said??`No messages yet.`),t.querySelector(`.pending`).hidden=!i?.pending,r(t,`.pending span`,i?.pending??``),e(t.querySelector(`.go`),{view:`session`,ws:n.ws,key:n.key,...i?.at&&{at:i.at}})},w=(e,t={said:`…`})=>{S.set(e.key,t),o.recent(e.key).then(h,()=>({failed:!0})).then(n=>{let r=S.get(e.key)===t&&(S.set(e.key,n),f.querySelector(`.dialog[data-key="${CSS.escape(e.key)}"]`));r&&C(r,e)})},T=(e,n,i,a)=>{l(e,n.mark),e.style.transform=i?`translateY(${-12*i}px) scale(${1-.06*i})`:``,e.style.opacity=String(Math.max(0,1-.3*i)),e.toggleAttribute(`inert`,i>0),e.setAttribute(`aria-label`,`${n.name}: ${c(n.note,n.mark)}`),t(e.querySelector(`.plate`),n.role,n.mark),r(e,`h3`,n.name),r(e,`.role`,n.role===`outside`?`unaligned`:n.role),r(e,`.state`,c(n.note,n.mark)),r(e,`.where`,o.label(n.ws)),r(e,`.tag`,{waiting:`question`,error:`error`,done:`finished`}[n.mark]??`update`),r(e,`.queued`,a?`+${a} queued`:``),S.has(n.key)||w(n),C(e,n);let s=E.get(e);s||E.set(e,s=d(e.querySelector(`.ask-root`),o.focus?.(n.key)??(()=>u(`sessions/${encodeURIComponent(n.key)}/focus`,{})))),s.update(n.pending,n.mark===`waiting`&&document.body.dataset.stale!==`true`)},E=new WeakMap,D=p({render(e,o){n(f,e.map(e=>({...e,id:e.key})),g,(t,n)=>T(t,n,e.length-1-e.findIndex(e=>e.key===n.key),o.length));let s=o[0];x.disabled=!s,x.dataset.mark=s?.mark??`unknown`,r(x,`#alert-count`,String(o.length)),r(x,`#alert-text`,s?`${s.name} · ${c(s.note,s.mark)}`:`no alerts`),n(x.querySelector(`.faces`),o.slice(0,3).map(e=>({...e,id:e.key})),()=>a(i),(e,n)=>t(e,n.role,n.mark))},enter:e=>f.querySelector(`[data-key="${CSS.escape(e.key)}"] .go`)?.focus(),focused:()=>document.activeElement instanceof HTMLElement&&document.activeElement!==document.body?document.activeElement:void 0,restore:e=>!!(e?.isConnected&&!e.closest(`[inert]`)&&(e.focus(),document.activeElement===e))});x.addEventListener(`click`,()=>D.next()),document.addEventListener(`click`,e=>{let t=t=>{let n=t.getBoundingClientRect();return e.clientX>=n.left&&e.clientX<=n.right&&e.clientY>=n.top&&e.clientY<=n.bottom},n=[...f.querySelectorAll(`.dialog:not(.leaving)`)],r=n.slice(0,-1).reverse().find(t);!e.defaultPrevented&&r&&n.length&&!t(n.at(-1))&&(e.preventDefault(),e.stopPropagation(),D.raise(r.dataset.key))},!0),f.addEventListener(`click`,e=>{let t=e.target;t.closest(`.x`)?D.close():t.closest(`.later`)?D.close(`later`):t.closest(`.go`)&&queueMicrotask(()=>void D.close(`go`))});let O=0,k=()=>y.classList.toggle(`tabbed`,A.tiles.length>1&&v.clientWidth<460+A.tiles.length*400),A=m({render(e,t){y.hidden=!e.length,n(y,e,e=>{let t=_();return t.querySelector(`.tile-body`).append(e.body()),t.querySelector(`.x`).addEventListener(`click`,()=>A.close(e.id)),t},(e,n)=>{r(e,`header b`,n.title),e.classList.toggle(`active`,n.id===t)}),n(b,e,()=>a(`<button type="button" class="tab-btn"></button>`),(e,n)=>{e.textContent=n.title,e.classList.toggle(`active`,n.id===t),e.onclick=()=>A.activate(n.id)}),y.prepend(b),e.length!==O&&(O=e.length,k())},enter:e=>requestAnimationFrame(()=>y.querySelector(`.tile[data-key="${CSS.escape(e.id)}"]`)?.focus())});return new ResizeObserver(k).observe(v),{tiles:A,stack:D,alerts(e,t){for(let t of e)w(t),D.arrive(t);D.prune(e=>t.get(e))}}}export{v as mountPanels};
@@ -1,2 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./index-5NVJByUz.css"])))=>i.map(i=>d[i]);
2
- import{a as e,c as t,d as n,f as r,i,l as a,p as o,r as s,s as c,t as l,u}from"./index-Cn7_VOw9.js";import{n as d,t as f}from"./post-B39ywXKd.js";import{t as p}from"./ask-DD_oYNss.js";var m=16384,h=`Not confirmed. Check the terminal before sending again.`,g=/^\s*\/(quit|exit|new|resume|fork|clone|import|reload)(?=\s|$)/,_=new Map,v=e=>(t,n)=>f(`sessions/${encodeURIComponent(e)}/${t}`,n),y=(e,t)=>e.startsWith(t)?e.slice(t.length).replace(/^\n/,``):e;function b(e){if(!e.online)return`The stream is down; sending waits for it to come back.`;if(!e.live)return`This session is no longer live; its history stays readable.`;if(!e.control)return`No live pi pane backs this unit, so there is nothing to type into.`;if(e.mark===`waiting`)return`It waits on a dialog; answer it in its terminal.`}function x(e,t,n){let r=`sb-draft:${t}`,i=matchMedia(`(pointer: coarse)`).matches,a=d(`textarea`,{rows:`1`,maxlength:String(m),"aria-label":`Message`,enterkeyhint:i?`enter`:`send`}),o=d(`button`,{type:`submit`,class:`cmp-send`},`Send`),s=d(`button`,{type:`button`,class:`cmp-alt`,title:`Alt+Enter`},`Follow-up`),c=d(`button`,{type:`button`,class:`cmp-alt cmp-stop`},`Stop`),l=d(`p`,{class:`cmp-note`,role:`status`,"aria-live":`polite`}),u=d(`button`,{type:`button`,class:`cmp-link`},`Focus terminal`),f=d(`form`,{class:`composer`},d(`div`,{class:`cmp-row`},a,d(`div`,{class:`cmp-acts`},o,s,c)),d(`div`,{class:`cmp-foot`},l,u));e.replaceChildren(f);let p={name:``,control:!1,live:!1,mark:`unknown`,online:!1},v=!1,x=!1,S=e=>{try{e()}catch{}};S(()=>a.value=sessionStorage.getItem(r)??``);let C=()=>S(()=>a.value?sessionStorage.setItem(r,a.value):sessionStorage.removeItem(r)),w=()=>(a.style.height=`auto`,a.style.height=`${Math.min(a.scrollHeight,220)}px`),T=(e,t=``,...n)=>(l.dataset.tone=t,l.replaceChildren(e,...n)),E=(e,t,n)=>{let r=d(`button`,{type:`button`,class:`cmp-yes`},t),i=d(`button`,{type:`button`,class:`cmp-link`},`Cancel`);x=!0,r.onclick=()=>(x=!1,n()),i.onclick=()=>(x=!1,T(``),a.focus()),T(e,`ask`,r,i),r.focus()};function D(e,i,a){let o={sent:a,answer:n(e,i)},s=e=>S(()=>e?sessionStorage.setItem(`${r}:note`,h):sessionStorage.removeItem(`${r}:note`));return a!==void 0&&(s(!0),_.set(t,o),o.answer.then(e=>(e.ok&&S(()=>sessionStorage.setItem(r,y(sessionStorage.getItem(r)??``,a))),s(e.status===504)),()=>s(!0))),O(o)}async function O(e,{sent:n}=e){v=!0,x=!1,A(),T(n===void 0?``:`Sending…`);try{let t=await e.answer;if(!f.isConnected)return;if(!t.ok)return T(t.body.reason??`Refused (${t.status}).`,`error`);if(n===void 0)return T(``);let r=!a.value.startsWith(n);a.value=y(a.value,n),C(),w(),T(r?`Sent as it was; your edits stayed here, unsent.`:`Sent. It shows in the log once pi writes it.`,`ok`)}catch{T(h,`error`)}finally{_.get(t)===e&&_.delete(t),v=!1,A()}}function k(e,t){let n=a.value,r=b(p),i=t===n;if(v||x||!n.trim())return;if(r)return T(r,`error`);if(new TextEncoder().encode(n).length>16384)return T(`The text is longer than 16 KB; shorten it.`,`error`);let o=!i&&n.match(g)?.[0].trim();if(o)return E(`${o} ends this session or switches its pane to another one.`,`Send ${o}`,()=>k(e,n));D(`input`,{text:n,as:e,confirmed:i},n)}function A(){let e=b(p),t=p.mark===`working`;a.placeholder=e??`Message ${p.name} · ${i?``:`Enter sends, Shift+Enter adds a line · `}lands after any unsent text in its terminal`,o.disabled=v||!!e,s.hidden=c.hidden=!t||!!e,s.disabled=c.disabled=u.disabled=v,u.hidden=!p.live||!p.control}f.addEventListener(`submit`,e=>(e.preventDefault(),k(`send`))),s.onclick=()=>k(`followUp`),c.onclick=()=>E(`Stop the current task? Queued messages go back to its terminal editor.`,`Stop`,()=>void(v||D(`keys`,{keys:[`esc`]}))),u.onclick=()=>void(v||D(`focus`,{})),a.addEventListener(`input`,()=>(C(),w())),f.addEventListener(`keydown`,e=>{e.key===`Escape`&&!e.isComposing&&(x||e.target===a&&a.value)&&(e.preventDefault(),x&&(x=!1,T(``),a.focus()))}),a.addEventListener(`keydown`,e=>{e.isComposing||e.keyCode===229||e.key!==`Enter`||e.shiftKey||i&&!e.altKey||(e.preventDefault(),k(e.altKey?`followUp`:`send`))});let j=window.visualViewport,M=()=>document.documentElement.style.setProperty(`--vvh`,`${j.height}px`);return j&&(j.addEventListener(`resize`,M),M()),w(),_.has(t)?O(_.get(t)):S(()=>(T(sessionStorage.getItem(`${r}:note`)??``,`error`),sessionStorage.removeItem(`${r}:note`))),{box:a,update:e=>(p=e,A()),ready:()=>!b(p)}}var S=200;function C(e,t){for(let n of e?.bases??[]){if(n.tower?.key===t)return{u:{key:t,name:n.tower.name,role:`tower`,mark:n.tower.mark,control:n.tower.control,pending:n.tower.pending,subs:[]},base:n};for(let[e,r]of[...n.tabs.map(e=>[e.label,e.units]),[void 0,n.outside]])for(let i of r){if(i.key===t)return{u:i,base:n,squad:e};let r=i.subs.find(e=>e.key===t);if(r)return{u:r,base:n,squad:e,launcher:i}}}}var w=()=>a(`<a class="token">${i}<span class="name"></span><span class="line"><em class="role"></em><span class="note"></span></span></a>`);function T(n,i,a){e(n,i.key?{view:`session`,ws:a,key:i.key}:void 0),n.toggleAttribute(`hidden`,!1),n.toggleAttribute(`data-alert`,!!i.key&&o(i.mark)),u(n,i.mark),t(n.querySelector(`.plate`),i.role,i.mark),r(n,`.name`,i.name),r(n,`.role`,i.role===`outside`?`unaligned`:i.role),r(n,`.note`,c(i.note,i.mark))}var E=()=>a(`<div class="col"><a class="token">${i}<span class="name"></span><span class="line"><em class="role"></em><span class="note"></span></span></a><div class="say"><p></p></div><div class="drones"></div></div>`),D=`<div class="zoom-in"><header class="base keep"><a class="tower big"><i class="sweep"></i><i class="pulse"></i><svg><use href="#i-tower"/></svg><b class="badge"></b></a><div class="title"><span class="crown"><svg><use href="#i-crown"/></svg>head tower</span><h2></h2><p></p></div><dl class="tally"><div><dt>Squads</dt><dd></dd></div><div><dt>Units</dt><dd></dd></div><div data-mark="working"><dt>Working</dt><dd></dd></div><div data-mark="waiting"><dt>Waiting</dt><dd></dd></div></dl></header><div class="squads"></div><div class="camps"><section class="tab squad camp" data-group="outposts"><h3><svg><use href="#i-outpost"/></svg><b>Outposts</b><small></small></h3><div class="formation"></div></section><section class="tab squad camp" data-group="outside"><h3><svg><use href="#i-outside"/></svg><b>Outside the net</b><small>unaligned pi panes</small></h3><div class="formation"></div></section></div><p class="lost" hidden></p></div>`,O=`<div class="dossier-in"><span class="tag">unit dossier</span><div class="portrait">${i}</div><h3></h3><p class="d-line"><em class="role"></em> <span class="state"></span></p><p class="d-note" hidden></p><dl class="specs"></dl><div class="d-drones" hidden><h4>Dispatched</h4><div class="formation"></div></div></div>`,k=`<div class="comms-in"><header class="comms-head"><span class="logo"><svg><use href="#i-comms"/></svg></span><div><h2><small>comms</small> <span></span></h2><p></p></div><span class="chan link" data-state="live"><i></i><b>channel open</b></span></header><div class="chat-root"></div><div class="ask-root"></div><div class="composer-root"></div></div>`;function A({data:i,restart:d,openTile:f,online:m}){let h=e=>document.getElementById(e),[g,_,y]=[`ws-view`,`session-view`,`dossier`].map(h),b={view:`map`},A,j={},M,N=[],P=!1,F,I,L,R=0,z,B,V=()=>b.view===`map`?``:b.ws,H=(t,n)=>{let r=document.createElement(`a`);return e(r,t),r.textContent=n,r};function U(){let t=(t,n,r)=>{let i=document.createElement(t);return i.textContent=n,r&&e(i,r),i},n=h(`crumbs`),r=b;if(r.view===`map`)return n.replaceChildren(t(`b`,`Straw Net`),t(`span`,`all bases`));let i=A?.bases.find(e=>e.id===r.ws)?.label??r.ws,a=r.view===`session`?C(A,r.key)?.u.name??F?.name??r.key:``;n.replaceChildren(t(`a`,`Straw Net`,{view:`map`}),r.view===`workspace`?t(`b`,i):t(`a`,i,{view:`workspace`,ws:r.ws}),...a?[t(`b`,a)]:[])}function W(e,t,n){let r=e=>[`map`,`workspace`,`session`].indexOf(e.view),i=r(e)<r(t)?`enter-back`:e.view===`workspace`&&n?`enter-zoom`:e.view===`session`&&t.view!==`session`?`enter-slide`:``,a=h({map:`map`,workspace:`ws-view`,session:`session-view`}[e.view]);if(i){if(n){let e=a.getBoundingClientRect();a.style.setProperty(`--ox`,`${Math.round(n.x+n.width/2-e.x)}px`),a.style.setProperty(`--oy`,`${Math.round(n.y+n.height/2-e.y)}px`)}a.classList.remove(`enter-zoom`,`enter-slide`,`enter-back`),a.offsetWidth,a.classList.add(i),setTimeout(()=>a.classList.remove(i),700)}}function G(){g.firstElementChild||g.append(a(D));let t=g.firstElementChild,i=A?.bases.find(e=>e.id===V()),c=t.querySelector(`.lost`);if(c.hidden=!!i,c.textContent=A?`This workspace is not part of the net.`:`Waiting for the net…`,t.classList.toggle(`void`,!i),!i)return;let l=t.querySelector(`.keep`);u(l,i.mark),l.classList.toggle(`head`,i.head);let d=t.querySelector(`.tower`);d.classList.toggle(`ghost`,!i.tower),u(d,i.tower?.mark??`unknown`),e(d,i.tower?{view:`session`,ws:i.id,key:i.tower.key}:void 0),d.toggleAttribute(`hidden`,!1),d.toggleAttribute(`data-alert`,!!(i.tower&&o(i.tower.mark))),r(t,`h2`,i.label),r(t,`.title p`,i.tower?`control tower · ${s[i.tower.mark]}`:`no tower yet`);let f=i.tabs.flatMap(e=>e.units.flatMap(e=>[e,...e.subs]));[i.tabs.length,f.length,f.filter(e=>e.mark===`working`).length,f.filter(e=>e.mark===`waiting`).length].forEach((e,n)=>t.querySelectorAll(`dd`)[n].textContent=String(e));let p=(e,t)=>{u(e,t.mark),T(e.querySelector(`.token`),t,i.id);let a=e.querySelector(`.say`),o=j[t.key]??``;a.hidden=!o,r(a,`p`,o),n(e.querySelector(`.drones`),t.subs,w,(e,t)=>(e.classList.add(`drone`),T(e,t,i.id)))};n(t.querySelector(`.squads`),i.tabs,()=>a(`<section class="tab squad"><h3><i class="flag"></i><b></b><small></small></h3><div class="formation"></div></section>`),(e,t)=>{r(e,`h3 b`,`squad ${t.label}`);let i=t.units.flatMap(e=>[e,...e.subs]);r(e,`h3 small`,`${i.length} unit${i.length===1?``:`s`}`),u(e,[`waiting`,`error`,`working`].find(e=>i.some(t=>t.mark===e))??`idle`),n(e.querySelector(`.formation`),t.units,E,p)});let m=(e,r)=>{let a=t.querySelector(`[data-group="${e}"]`);a.hidden=!r.length,n(a.querySelector(`.formation`),r,w,(e,t)=>T(e,t,i.id))};m(`outposts`,i.outposts.map(e=>({id:e.id,name:e.label,role:`outpost`,mark:e.mark,note:e.mark===`unknown`?`no agent running`:void 0}))),m(`outside`,i.outside)}function K(){if(b.view!==`session`)return;y.firstElementChild||y.append(a(O));let e=y.firstElementChild,i=C(A,b.key),l=i?.u,d=l?.mark??`unknown`;u(e,d),t(e.querySelector(`.plate`),l?.role??F?.role??`member`,d),r(e,`h3`,l?.name??F?.name??b.key),r(e,`.role`,l?.role===`outside`?`unaligned`:l?.role??F?.role??`session`),r(e,`.state`,l?c(l.note,d):`not in the net`);let f=e.querySelector(`.d-note`),p=F?.state;f.hidden=p!==`recent`&&p!==`unknown`,f.replaceChildren(p===`unknown`?`Not in the net (it may have ended more than ten minutes ago).`:`No longer live; its history stays readable for ten minutes.`),F?.successor&&f.append(` Now: `,H({view:`session`,ws:V(),key:F.successor},C(A,F.successor)?.u.name??`the new session`));let h=[[`model`,I],[`context`,L?`${L.toLocaleString()} tokens`:void 0],[`directory`,F?.header?.cwd],[`base`,i&&H({view:`workspace`,ws:i.base.id},i.base.label)],[`squad`,i?.squad],[`launcher`,i?.launcher&&H({view:`session`,ws:i.base.id,key:i.launcher.key},i.launcher.name)],[`unreadable`,F?.unreadable?`${F.unreadable} entr${F.unreadable===1?`y`:`ies`}`:void 0]];e.querySelector(`.specs`).replaceChildren(...h.filter(([,e])=>e).map(([e,t])=>{let n=a(`<div><dt></dt><dd></dd></div>`);return n.firstElementChild.textContent=e,n.lastElementChild.append(t),n}));let g=e.querySelector(`.d-drones`);g.hidden=!l?.subs.length,n(g.querySelector(`.formation`),(l?.subs??[]).map(e=>({...e,id:e.key})),w,(e,t)=>(e.classList.add(`drone`),T(e,t,V())));let v=_.querySelector(`.comms-head`);if(v){r(v,`h2 span`,l?.name??F?.name??``),r(v,`p`,[i?.base.label,i?.squad&&`squad ${i.squad}`].filter(Boolean).join(` · `));let e=v.querySelector(`.chan`),t=p===`live`||!p&&l;e.dataset.state=t?`live`:`closed`,r(e,`b`,t?`channel open`:p===`recent`?`channel closed`:`no channel`)}M?.setLive(d===`working`?`${c(l.note,d)}`:o(d)?s[d]:``,d),z?.update({name:l?.name??F?.name??b.key,control:!!l?.control,live:p?p===`live`:!!l,mark:d,online:m()}),B?.update(l?.pending,d===`waiting`&&!!l?.control&&m())}let q=()=>setTimeout(()=>b.view===`session`&&(document.activeElement??document.body)===document.body&&(matchMedia(`(pointer: fine)`).matches&&z?.ready()?z.box:M?.log)?.focus({preventScroll:!0}));async function J(e){let t=++R,{mountChat:n}=await l(async()=>{let{mountChat:e}=await import(`./chat-tEF6NvU3.js`);return{mountChat:e}},__vite__mapDeps([0]),import.meta.url);if(t!==R||b.view!==`session`)return;_.replaceChildren(a(k)),M=n(_.querySelector(`.chat-root`),i(e),{who:()=>C(A,e)?.u??{key:e,name:F?.name??e,role:F?.role??`member`,mark:`unknown`},link:t=>{let n=C(A,e)?.u.subs.find(e=>e.name===t)??A?.bases.flatMap(e=>[...e.tabs.flatMap(e=>e.units),...e.outside]).find(e=>e.name===t);return n&&H({view:`session`,ws:C(A,n.key)?.base.id??V(),key:n.key},t)},openTile:f});let r=i(e).post??v(e);[z,B]=[x(_.querySelector(`.composer-root`),e,r),p(_.querySelector(`.ask-root`),()=>r(`focus`,{}))],M.reveal(b.at),N.splice(0).forEach(Y),K(),q()}function Y(e){M?.apply(e);for(let t of e.items??[])t.kind===`assistant`&&([I,L]=[t.model??I,t.ctx??L])}let X=()=>(U(),b.view===`workspace`?G():K());return{locate:e=>C(A,e),show(e,t,n){let[r,i]=b.view===`session`?[b.key,b.at]:[];(e.view!==`workspace`||b.view!==`workspace`||e.ws!==b.ws)&&(j={}),b=e,g.hidden=e.view!==`workspace`,_.hidden=y.hidden=e.view!==`session`,e.view===`session`&&e.key!==r?([M,z,B,F,I,L,N,P]=[void 0,void 0,void 0,void 0,void 0,void 0,[],!1],_.replaceChildren(a(`<p class="lost">Opening the channel…</p>`)),J(e.key)):e.view===`session`&&e.at!==i?(M?.reveal(e.at),q()):e.view!==`session`&&(R++,M=z=B=void 0),X(),e!==t&&W(e,t,n)},net(e){A=e,b.view!==`map`&&X()},cards(e){j={...j,...e},b.view===`workspace`&&G()},history(e){if(b.view===`session`&&e.key===b.key){if(F={...F,...e,items:void 0},e.reset&&([I,L]=[void 0,void 0]),document.hidden)return N.length<S?void N.push(e):void(P=!0);M?Y(e):N.push(e),K()}},wake(){if(P)return P=!1,N=[],d();N.splice(0).forEach(e=>M&&Y(e)),X()}}}export{A as mountFocus};