pi-straw-boss 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,7 +45,7 @@ You need pi running inside Herdr 0.9.4 or newer, and [pi-intercom](https://www.n
45
45
  | `boss_broadcast` | Message a tab, a workspace, all towers, or every session. |
46
46
  | `/boss` | Take over as this tab's main agent. |
47
47
  | `/boss status`, `/boss tower` | Show the status; open and focus the tower. |
48
- | `/boss gui`, `/boss gui stop` | Open the net in the browser (any session, joins nothing); stop its server. Type into a session from its comms view; a pending `ask_user` question shows there and in its alert, to answer in the terminal. |
48
+ | `/boss gui`, `/boss gui stop` | Open the net in the browser (any session, joins nothing); stop its server. Type into a session from its comms view; answer a waiting `robot_hand` selector or `ask_user_question` questionnaire there or in its alert (other dialogs show read-only, to answer in the terminal; `secret_drop` stays terminal-only). |
49
49
  | `/boss gui remote`, `/boss gui remote off` | Reach the GUI from your phone over your tailnet (`tailscale serve`, never Funnel): pair by QR code; off removes only its own serve entry. |
50
50
 
51
51
  Subagents and sessions in Herdr worktree workspaces never join: the session that launched them speaks for them. Outside Herdr the extension does nothing.
@@ -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>0&&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))),r=e=>e.replace(/[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g,`�`),i=(e,t)=>(e.select?.choices??[]).map((e,t)=>({...e,i:t})).filter(e=>!(t&&e.localOnly)),a=(e,t,n)=>({...e.size?{choices:[...e].sort((e,t)=>e-t)}:{},...t.trim()?{text:t}:{},...n.trim()?{note:n}:{}}),o=(e,t,n)=>({dialog:e.dialog,digest:e.digest,answerId:n,...t}),s={digest:`The question changed; read it again.`,"not-allowed":`That answer does not fit the question.`,malformed:`The session could not read that answer.`};function c(e){let{outcome:t,reason:n,by:r}=e.body;return t===`accepted`?[`ok`,`Answered.`]:t===`stale`?[`error`,r===`local`?`It was already answered in the terminal.`:r===`cancel`?`It was dismissed in the terminal.`:`It is no longer open.`]:t===`rejected`?[`error`,s[String(n)]??`The session refused that answer.`]:t===`busy`?[`error`,`Another answer is on its way; check the terminal.`]:t===`unknown`||e.status===504?[`error`,`Not confirmed. Check the terminal.`,!0]:[`error`,n??`Refused (${e.status}).`]}var l=e=>[`accepted`,`stale`,`busy`,`unknown`].includes(String(e.body.outcome))||e.status===504,u=e=>e.answer?`d\n${e.answer.dialog}\n${e.answer.digest}`:`c\n${e.callId}\n${e.why??``}`;function d(s,d,f=location.hostname!==`127.0.0.1`){let p,m,h,g=!1,_=!1,v,y=(e,t,n,...r)=>(e.dataset.tone=t,e.replaceChildren(n,...r)),b=()=>{m&&(m.go.disabled=!g||_,m.controls.forEach(e=>e.disabled=!g||_||m.done),m.send&&(m.send.disabled=!g||_||m.done||!m.pick()))};async function x(t,n){[t.sending,_]=[!0,!0],b(),y(t.note,``,`Sending…`);let r=await d(`answer`,n).catch(()=>void 0),i=e(`button`,{type:`button`,class:`cmp-link`},`Check again`);i.onclick=()=>void x(t,n);let[a,o,s]=r?c(r):[`error`,`Not confirmed. Check the terminal.`,!0];[t.done,t.sending,_]=[!r||l(r),!1,!1],y(t.note,a,o,...s?[i]:[]),b(),t.note.scrollIntoView?.({block:`nearest`})}let S=(t,n,r,i,a)=>{let o=e(`button`,{type:`button`,class:`ask-opt`,role:t,"aria-checked":`false`},e(`b`,a?{"data-n":String(a)}:null,n),...r.filter(Boolean).map(t=>e(`small`,null,t)));return o.onclick=i,o};function C(t,n){let a,o=i(n,f).map(e=>S(`radio`,e.label,[e.note],()=>(a=e.i,o.forEach((t,r)=>t.setAttribute(`aria-checked`,String(i(n,f)[r].i===e.i))),b()),e.i+1)),s=n.select?.copy!==void 0&&e(`button`,{type:`button`,class:`cmp-link`},`Copy command`);return s&&(s.disabled=!navigator.clipboard,s.onclick=()=>void navigator.clipboard.writeText(n.select.copy).then(()=>y(t.note,`ok`,`Copied to this device's clipboard.`),()=>y(t.note,`error`,`The browser refused the clipboard.`))),t.pick=()=>a===void 0?void 0:{choice:a},t.controls=o,[e(`pre`,{class:`ask-title`},r(n.select.title)),e(`div`,{class:`ask-opts`,role:`radiogroup`,"aria-label":`Choices`},...o),e(`div`,{class:`ask-acts`},t.send,s,t.go)]}function w(t,n){let r=n.questions.map(()=>({picks:new Set,text:e(`input`,{type:`text`,maxlength:`8192`,class:`ask-own`,placeholder:`Or type your own answer`,"aria-label":`Your own answer`}),note:e(`input`,{type:`text`,maxlength:`2048`,class:`ask-own ask-memo`,placeholder:`Note (optional)`,"aria-label":`Note`})})),i=n.questions.map((n,i)=>{let a=r[i],o=n.multiple?`checkbox`:`radio`,s=n.options.map((e,t)=>S(o,e.title,[e.description,e.preview&&`It has a preview; see it in the terminal.`],()=>{n.multiple||(a.picks.clear(),a.text.value=``),a.picks.has(t)?a.picks.delete(t):a.picks.add(t),s.forEach((e,t)=>e.setAttribute(`aria-checked`,String(a.picks.has(t)))),b()}));return a.text.oninput=()=>(n.multiple||(a.picks.clear(),s.forEach(e=>e.setAttribute(`aria-checked`,`false`))),b()),t.controls.push(...s,a.text,a.note),e(`div`,{class:`ask-one`},n.header&&e(`b`,{class:`ask-head`},n.header),e(`p`,{class:`ask-q`},n.question),e(`div`,{class:`ask-opts`,role:n.multiple?`group`:`radiogroup`,"aria-label":`Options`},...s),a.text,a.note)});return t.pick=()=>r.every(e=>e.picks.size||e.text.value.trim())?{answers:r.map(e=>a(e.picks,e.text.value,e.note.value))}:void 0,[...i,e(`div`,{class:`ask-acts`},t.send,t.go)]}function T(r){let i=r.asks??(r.ask?[r.ask]:[]),a=i.length>1,s=r.answer,c={key:u(r),el:e(`section`,{class:`ask`}),note:e(`p`,{class:`ask-note`,role:`status`,"aria-live":`polite`}),go:e(`button`,{type:`button`,class:`cmp-link`},`Focus terminal`),controls:[],pick:()=>void 0,done:!1,sending:!1};c.go.onclick=async()=>{_=!0,b();let e=await d(`focus`,{}).catch(()=>void 0);y(c.note,e?.ok?``:`error`,e?.ok?``:e?.body.reason??`Herdr could not be reached.`),_=!1,b()},s&&((c.send=e(`button`,{type:`button`,class:`ask-send`},`Send answer`)).onclick=()=>void(c.pick()&&x(c,o(s,c.pick(),crypto.randomUUID()))));let l=e(`p`,{class:`ask-term`},r.why&&i.length?r.why:`${a?`Answer them`:`Answer it`} in the terminal. ${a||!i[0]?``:t(i[0])}`.trim());return c.el.append(...s?s.select?C(c,s):w(c,s):[...i.length?i.map(e=>n(e,a)):[e(`p`,{class:`ask-why`},r.why??`It waits on a dialog.`)],e(`div`,{class:`ask-acts`},l,c.go)],c.note),c}let E=(e,t)=>{let n=document.activeElement;m&&m!==t&&(m.focus=m.el.contains(n)?n:void 0),[p,m,h]=[e,t,t?.send?t:h],s.replaceChildren(...t?[t.el]:[]),b(),t?.focus&&(!document.activeElement||document.activeElement===document.body)&&t.focus.focus()};return{update(e,t){g=t,clearTimeout(v),!e&&(m?.done||m?.sending)?v=setTimeout(()=>E(),6e3):e?m&&(u(e)===m.key||!e.answer&&(m.done||m.sending)&&e.callId===p?.callId)?p=e:E(e,h?.key===u(e)&&!h.done?h:T(e)):E(),b()}}}export{d as t};
@@ -1,4 +1,4 @@
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,`
1
+ import{l as e,n as t}from"./index-B08XQg6E.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
- 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};
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-D6CIBpbW.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-CS9z4FWn.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,post:e=>Z&&((t,n)=>Z.data(e).post(t,n))})),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};
@@ -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{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
+ :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}.comms-in .ask{max-height:calc((var(--vvh,100vh) - 190px) * .55);overscroll-behavior:contain;overflow-y:auto}.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{overscroll-behavior:contain;max-height:calc(100dvh - 128px);overflow-y:auto}.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 b[data-n]:before{content:attr(data-n) ". "}.ask-opt small{color:var(--dim);font-size:12px}.ask-acts{flex-wrap:wrap;align-items:center;gap:6px 14px;display:flex}.ask-acts>:last-child{margin-left:auto}.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);flex-wrap:wrap;align-items:center;gap:4px 10px;font-size:12px;display:flex}.ask-note[data-tone=error]{color:#ff8a9c}.ask-note[data-tone=ok]{color:#3ddc97}.ask-title{font:12.5px/1.45 var(--mono);white-space:pre-wrap;overflow-wrap:anywhere;max-height:12em;margin:0;overflow:auto}div.ask-opts{max-height:none}button.ask-opt{text-align:left;color:var(--text);font:13px var(--sans);cursor:pointer}.ask-opt[aria-checked=true]{background:#ffb02e1f;border-color:#ffb02e}.ask-own{border:1px solid var(--line);min-width:0;color:var(--text);font:13px var(--sans);background:#070c18;border-radius:10px;padding:7px 12px}.ask-memo{font-size:12px}.ask input:disabled{opacity:.45}.ask-send{color:#06101e;font:600 13px var(--sans);cursor:pointer;background:#ffb02e;border:0;border-radius:10px;padding:7px 13px}.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,.ask input{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,2 +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};
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-B08XQg6E.js";import{t as u}from"./post-B39ywXKd.js";import{t as d}from"./ask-B072MmWz.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.post?.(n.key)??((e,t)=>u(`sessions/${encodeURIComponent(n.key)}/${e}`,t)))),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-D8pxo5hc.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-B08XQg6E.js";import{n as d,t as f}from"./post-B39ywXKd.js";import{t as p}from"./ask-B072MmWz.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 e.answerable?`It waits on a dialog: answer it above, or in its terminal.`:`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,answerable:!!l?.pending?.answer}),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-Dfcu07Qg.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)],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-C_WFzbRO.js"></script>
10
- <link rel="stylesheet" crossorigin href="./assets/index-BccsK_Zr.css">
9
+ <script type="module" crossorigin src="./assets/index-B08XQg6E.js"></script>
10
+ <link rel="stylesheet" crossorigin href="./assets/index-D8pxo5hc.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.2",
3
+ "version": "0.6.0",
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",
package/src/ask.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import { open, stat } from "node:fs/promises";
2
2
  import { type Json, parse } from "./items.ts";
3
- import type { Net } from "./net.ts";
3
+ import type { Net, Unit } from "./net.ts";
4
+ import type { Answerable, Dialog } from "./owners.ts";
4
5
 
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
- // 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
- // the user answers it in the terminal. Nothing here sends anything: answering from the browser is deferred (see the spec, "Pending dialogs").
6
+ // What a waiting session waits on: the current branch's newest assistant message's tool call with no result yet, read from the end of
7
+ // its session file. The snapshot carries it so the alert and the session view can show an ask_user question (its context and options).
8
+ // A dialog the session's bridge relays (bridge.ts, dialogs.ts) joins it as `answer` when the page may answer it; nothing here sends anything.
8
9
  // 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
10
  // with a header, options with descriptions, and a multi-select flag; no context). Older session files hold the first, new ones the second.
10
11
 
@@ -12,8 +13,9 @@ const TAIL = 262_144, LIMIT = 16_777_216, CACHE = new Map<string, { sig: string;
12
13
  export interface Option { title: string; description?: string }
13
14
  /** `multiple`: it takes several choices; `freeform`: it takes the user's own words; `header`: the short tag ask_user_question gives a question. */
14
15
  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 }
16
+ /** 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; `answer` when the page may answer it. */
17
+ export interface Pending { callId: string; tool: string; ask?: Ask; asks?: Ask[]; why?: string; answer?: Answerable }
18
+ export const SEVERAL = "It waits on more than one dialog.";
17
19
  interface Call { id: string; name: string; args: Json }
18
20
 
19
21
  const clip = (v: unknown, n: number) => String(v ?? "").slice(0, n);
@@ -93,20 +95,28 @@ export async function pendingOf(file: string): Promise<Pending | undefined> {
93
95
  if (hit?.sig === sig) return hit.pending;
94
96
  const found = s ? await openCalls(file).catch(() => []) : [], calls = found === "cut" ? [] : found, [c] = calls;
95
97
  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) };
98
+ 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: SEVERAL } : shape) };
97
99
  CACHE.set(file, { sig, pending });
98
100
  if (CACHE.size > 64) CACHE.delete(CACHE.keys().next().value!);
99
101
  return pending;
100
102
  }
101
103
 
102
- /** Adds what each waiting unit waits on to a snapshot. */
103
- export async function withPending(net: Net, fileOf: (key: string) => string | undefined): Promise<Net> {
104
- const all = net.bases.flatMap((b) => [...(b.tower ? [b.tower] : []), ...[...b.tabs.flatMap((t) => t.units), ...b.outside].flatMap((u) => [u, ...u.subs])]);
104
+ /** The dialogs bridges relay, by unit key, and how they join what the file says (dialogs.ts). */
105
+ export interface Relayed { dialogsOf(key: string): Dialog[] | undefined; merge(key: string, file: Pending | undefined, dialogs: Dialog[], readable: boolean): Pending }
106
+
107
+ /** Adds what each waiting unit waits on to a snapshot. A unit with a relayed dialog waits even before Herdr reports its pane blocked. */
108
+ export async function withPending(net: Net, fileOf: (key: string) => string | undefined, relayed?: Relayed): Promise<Net> {
105
109
  const jobs: Promise<unknown>[] = [];
106
- for (const u of all) {
107
- const file = u.mark === "waiting" ? fileOf(u.key) : undefined;
108
- if (file) jobs.push(pendingOf(file).then((pending) => pending && (u.pending = pending)));
109
- }
110
+ for (const b of net.bases)
111
+ for (const u of [...(b.tower ? [b.tower] : []), ...[...b.tabs.flatMap((t) => t.units), ...b.outside].flatMap((u) => [u, ...u.subs])]) {
112
+ const dialogs = relayed?.dialogsOf(u.key);
113
+ if (dialogs?.length) [u.mark, b.mark] = ["waiting", "waiting"];
114
+ if (dialogs?.length && u !== b.tower) (u as Unit).note = "waiting on a question";
115
+ const file = u.mark === "waiting" ? fileOf(u.key) : undefined;
116
+ const join = (p?: Pending) => (dialogs?.length ? relayed!.merge(u.key, p, dialogs, Boolean(file)) : p);
117
+ if (file) jobs.push(pendingOf(file).then((p) => (u.pending = join(p))));
118
+ else if (dialogs?.length) u.pending = join();
119
+ }
110
120
  await Promise.all(jobs);
111
121
  return net;
112
122
  }
package/src/bridge.ts ADDED
@@ -0,0 +1,170 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { readFileSync, statSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
5
+ import { type Answer, type Bus, type Dialog, OWNERS, type OwnerAdapter, type Verdict } from "./owners.ts";
6
+
7
+ // The bridge runs in every pi session inside Herdr, subagents included. It turns the owners' dialog events (owners.ts) into one list of open
8
+ // dialogs, relays it to the one running GUI server (gui.json in the state directory names its session G), and hands the server's answers to
9
+ // the owners. It never touches ctx.ui and never judges an answer's meaning: the owner does. Transport is pi-intercom's extension channel
10
+ // straw-boss/gui/<sha256(G) prefix>, where only G is owner-eligible: dialogs and acks go to the owner, answers come from it (ownerOnly, and
11
+ // checked here against G and the owner's epoch); when G is this session the hub (dialogs.ts) is called in-process instead.
12
+ // Per dialog: open → settling (an answer is with the owner) → open again if refused, or closed by the owner's `closed`, the end of its tool
13
+ // call, or the session's end. An answer whose verdict does not come within 2 s leaves it `unknown`: later answers get `busy` until it closes.
14
+
15
+ export const REGISTER = "intercom:extension-register";
16
+ const VERDICT_MS = 2000, POLL_MS = 2000, KEEP_MS = 120_000, KEEP = 16, MAX_GUIS = 8, MAX_MESSAGE = 15 * 1024;
17
+ export const guiNamespace = (g: string) => `straw-boss/gui/${createHash("sha256").update(g).digest("hex").slice(0, 32)}`;
18
+ export type Outcome = "accepted" | "rejected" | "stale" | "busy" | "unknown";
19
+ export interface Ack { version: 1; type: "ack"; bridgeId: string; id: string; answerId: string; outcome: Outcome; reason?: string; by?: string }
20
+ /** `closed`: dialogs closed in the last 2 minutes (at most 16), so an answer that comes late hears who closed it, and a resent one that won, that it won. */
21
+ export interface Dialogs { version: 1; type: "dialogs"; bridgeId: string; pane: string; file: string | null; dialogs: Dialog[]; closed: Closed[] }
22
+ export interface Closed { id: string; by: string; answerId?: string }
23
+ export type ToHub = Dialogs | Ack;
24
+ export interface AnswerMessage extends Answer { version: 1; type: "answer"; to: string }
25
+ /** The hub when it runs in this session: it takes what a remote bridge would publish, from this session's id. */
26
+ export interface LocalHub { receive(from: string, msg: ToHub): void }
27
+ /** What the hub calls on the bridge of its own session. */
28
+ export interface Bridge { deliver(msg: AnswerMessage, reply: (ack: Ack) => void): void; sync(): void }
29
+ interface Channel { snapshot(): { connected: boolean; owner?: { sessionId: string; epoch: string } }; publish(payload: unknown, options?: { audience?: "owner" | "capable" }): void }
30
+ interface Rec { adapter: OwnerAdapter; d: Dialog; state: "open" | "settling" | "unknown" | "closed"; answerId?: string; accepted?: boolean; by?: string; at?: number; last?: Ack; waiting: ((ack: Ack) => void)[] }
31
+ type Event = { type: string; fromSessionId?: string; owner?: { sessionId: string; epoch: string }; payload?: Record<string, unknown> };
32
+
33
+ export function startBridge(pi: ExtensionAPI, { env, dir, local }: { env: NodeJS.ProcessEnv; dir: string; local(): LocalHub | undefined }): Bridge {
34
+ const bus = pi.events as Bus, bridgeId = randomUUID(), recs = new Map<string, Rec>(), calls = new Map<string, { tool: string; signal?: AbortSignal; stop?: () => void }>();
35
+ const chans = new Map<string, Channel>(), me = () => env.PI_INTERCOM_SESSION_ID ?? "";
36
+ let g: string | undefined, seen: number | undefined, poll: ReturnType<typeof setInterval> | undefined, offs: (() => void)[] = [], warned = false;
37
+ let file: () => string | undefined = () => undefined, tell = (_text: string) => {};
38
+ const open = () => [...recs.values()].filter((r) => r.state !== "closed");
39
+
40
+ // Where the dialogs go: the hub in this process while it runs here, else G's channel while G owns it.
41
+ const target = () => (local() ? me() : g);
42
+ const send = (to: string | undefined, msg: ToHub) => {
43
+ if (to && to === me()) return local()?.receive(to, msg);
44
+ const ch = to ? chans.get(to) : undefined;
45
+ if (ch?.snapshot().connected && ch.snapshot().owner?.sessionId === to) try { ch.publish(msg, { audience: "owner" }); } catch {} // a broker that dropped meanwhile resends on `connection`
46
+ };
47
+ const push = (final = false) => {
48
+ if (!target()) return;
49
+ const dialogs = final ? [] : open().map((r) => ({ ...r.d, ...(r.state !== "open" ? { busy: true as const } : {}) }));
50
+ const closed = final ? [] : [...recs.values()].filter((r) => r.state === "closed").map((r) => ({ id: r.d.id, by: r.by!, ...(r.by === "remote" && r.answerId ? { answerId: r.answerId } : {}) }));
51
+ let msg: Dialogs = { version: 1, type: "dialogs", bridgeId, pane: env.HERDR_PANE_ID!, file: file() ?? null, dialogs, closed };
52
+ if (JSON.stringify(msg).length > MAX_MESSAGE) msg = { ...msg, dialogs: dialogs.map(({ select, questions, ...d }) => ({ ...d, big: true as const })) }; // shown read-only: too long to show
53
+ send(target(), msg);
54
+ };
55
+ // gui.json names the server's session; it is read at the first dialog and then on every mtime change, checked every 2 s while a dialog is open.
56
+ const follow = () => {
57
+ const at = statSync(join(dir, "gui.json"), { throwIfNoEntry: false })?.mtimeMs ?? 0;
58
+ if (at === seen) return false;
59
+ seen = at;
60
+ try {
61
+ g = (JSON.parse(readFileSync(join(dir, "gui.json"), "utf8")) as { ownerSession?: string }).ownerSession || undefined;
62
+ } catch {
63
+ g = undefined;
64
+ }
65
+ if (g && g !== me() && !chans.has(g)) join_(g);
66
+ push();
67
+ return true;
68
+ };
69
+ const join_ = (to: string) => {
70
+ if (chans.size >= MAX_GUIS) return void (warned || ((warned = true), tell(`Straw Boss: ${MAX_GUIS} GUI servers came and went in this session; answer its dialogs in the terminal until it restarts`)));
71
+ let ch: Channel | undefined;
72
+ try {
73
+ pi.events.emit(REGISTER, { namespace: guiNamespace(to), ownerEligible: false, onReady: (c: Channel) => (ch = c), onEvent: (e: Event) => ch && onEvent(to, ch, e) });
74
+ } catch {} // pi-intercom missing or refusing: its dialogs stay read-only
75
+ if (ch) chans.set(to, ch);
76
+ };
77
+ const onEvent = (from: string, ch: Channel, e: Event) => {
78
+ if (e.type === "owner" || e.type === "connection") return from === g && push();
79
+ const owner = ch.snapshot().owner, p = e.payload;
80
+ // Only G's own session, as the broker's current owner of this channel and the server gui.json names now, may answer or ask for a sync.
81
+ if (e.type !== "message" || p?.version !== 1 || e.fromSessionId !== from || owner?.sessionId !== from || e.owner?.epoch !== owner.epoch) return;
82
+ if (p.type === "sync" && from === g) push();
83
+ // SAFETY: deliver checks the fields it uses before it hands anything to an owner.
84
+ if (p.type === "answer" && p.to === bridgeId && from === g) deliver(p as unknown as AnswerMessage, (ack) => send(from, ack));
85
+ };
86
+ const watch = () => {
87
+ if (open().length && !poll) (poll = setInterval(follow, POLL_MS)).unref?.();
88
+ else if (!open().length && poll) (clearInterval(poll), (poll = undefined));
89
+ };
90
+ const changed = () => {
91
+ for (const [id, r] of recs) if (r.state === "closed" && (Date.now() - r.at! > KEEP_MS || recs.size > KEEP)) recs.delete(id);
92
+ watch();
93
+ if (!follow()) push();
94
+ };
95
+
96
+ const report = {
97
+ open(adapter: OwnerAdapter, d: Dialog) {
98
+ const call = d.toolCallId ? calls.get(d.toolCallId) : undefined;
99
+ if (d.toolCallId && call?.tool !== adapter.tool) return; // only a call in flight of the owner's own tool
100
+ recs.set(d.id, { adapter, d, state: "open", waiting: [] });
101
+ if (call?.signal?.aborted) adapter.cancel?.(bus, d);
102
+ changed();
103
+ },
104
+ closed(id: string, by?: string) {
105
+ const r = recs.get(id);
106
+ if (!r || r.state === "closed") return;
107
+ [r.state, r.at, r.by] = ["closed", Date.now(), by ?? (r.accepted ? "remote" : "local")];
108
+ changed();
109
+ },
110
+ };
111
+ const ack = (r: Rec, a: Answer, outcome: Outcome, extra: { reason?: string; by?: string } = {}): Ack => ({ version: 1, type: "ack", bridgeId, id: r.d.id, answerId: a.answerId, outcome, ...extra });
112
+ function deliver(a: AnswerMessage, reply: (ack: Ack) => void) {
113
+ if (typeof a.id !== "string" || typeof a.answerId !== "string" || typeof a.digest !== "string") return;
114
+ const r = recs.get(a.id);
115
+ if (!r) return reply({ version: 1, type: "ack", bridgeId, id: String(a.id), answerId: String(a.answerId), outcome: "stale", by: "gone" });
116
+ if (r.state === "closed") return reply(ack(r, a, r.by === "remote" && r.answerId === a.answerId ? "accepted" : "stale", { by: r.by }));
117
+ if (r.state !== "open") return r.answerId !== a.answerId ? reply(ack(r, a, "busy")) : r.last ? reply(r.last) : void r.waiting.push(reply);
118
+ [r.state, r.answerId] = ["settling", a.answerId];
119
+ let off: (() => void) | undefined, timer: ReturnType<typeof setTimeout> | undefined, done = false;
120
+ const finish = (v?: Verdict) => {
121
+ if (done) return;
122
+ done = true;
123
+ off?.(), clearTimeout(timer);
124
+ let out: Ack;
125
+ if (r.state === "closed") out = ack(r, a, r.by === "remote" && r.answerId === a.answerId ? "accepted" : "stale", { by: r.by });
126
+ else if (!v) (r.state = "unknown"), (out = ack(r, a, "unknown"));
127
+ else if (v.accepted) (r.accepted = true), (out = ack(r, a, "accepted"));
128
+ else if (v.reason === "closed") out = ack(r, a, "stale", { by: "local" }); // the owner had settled it already; its close follows
129
+ else (r.state = "open"), (r.answerId = undefined), (out = ack(r, a, "rejected", { reason: v.reason ?? "malformed" }));
130
+ if (r.state !== "open") r.last = out; // what a resend of the same answer gets
131
+ [reply, ...r.waiting.splice(0)].forEach((to) => to(out));
132
+ push();
133
+ };
134
+ try {
135
+ off = r.adapter.answer(bus, r.d, { id: r.d.id, answerId: a.answerId, digest: a.digest, choice: a.choice, answers: Array.isArray(a.answers) ? a.answers : undefined }, finish);
136
+ } catch {
137
+ finish({ accepted: false, reason: "malformed" }); // an answer an adapter could not even map; the owner never saw it
138
+ }
139
+ if (done) return off?.();
140
+ timer = setTimeout(finish, VERDICT_MS);
141
+ push();
142
+ }
143
+
144
+ pi.on("session_start", (_e, ctx) => {
145
+ offs.forEach((off) => off());
146
+ file = () => ctx.sessionManager.getSessionFile();
147
+ tell = (text) => ctx.hasUI && ctx.ui.notify(text, "warning");
148
+ offs = OWNERS.map((o) => o.listen(bus, report));
149
+ });
150
+ pi.on("tool_execution_start", (e, ctx) => {
151
+ const signal = ctx.signal, entry: { tool: string; signal?: AbortSignal; stop?: () => void } = { tool: e.toolName, signal };
152
+ const cancel = () => open().filter((r) => r.d.toolCallId === e.toolCallId).forEach((r) => r.adapter.cancel?.(bus, r.d));
153
+ if (signal) (signal.addEventListener("abort", cancel, { once: true }), (entry.stop = () => signal.removeEventListener("abort", cancel)));
154
+ calls.set(e.toolCallId, entry);
155
+ });
156
+ pi.on("tool_execution_end", (e) => {
157
+ calls.get(e.toolCallId)?.stop?.();
158
+ calls.delete(e.toolCallId);
159
+ open().filter((r) => r.d.toolCallId === e.toolCallId).forEach((r) => report.closed(r.d.id, r.accepted ? "remote" : "error")); // the owner failed to say so
160
+ });
161
+ pi.on("session_shutdown", () => {
162
+ open().forEach((r) => report.closed(r.d.id, "error"));
163
+ push(true);
164
+ [...calls.values()].forEach((c) => c.stop?.());
165
+ calls.clear();
166
+ offs.splice(0).forEach((off) => off());
167
+ (clearInterval(poll), (poll = undefined));
168
+ });
169
+ return { deliver, sync: () => ((seen = -1), follow()) };
170
+ }