bostrat-node 0.0.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,280 @@
1
+ var qh=Object.defineProperty;var zc=(t,e)=>{for(var n in e)qh(t,n,{get:e[n],enumerable:!0})};import ct from"node:path";import It from"node:fs";import sA from"node:readline";import{fileURLToPath as aA}from"node:url";import fe from"express";import iA from"node:http";import cA from"node:https";import Vc from"node:os";import{WebSocketServer as lA}from"ws";import{execFile as tw}from"node:child_process";import{promisify as ew}from"node:util";import rl from"node:fs";import{randomUUID as nw}from"node:crypto";import gn from"node:fs/promises";import Xc from"node:os";import Le from"node:path";function Zr(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function Gh(){return process.env.CLAUDE_CONFIG_DIR?Le.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Le.join(Xc.homedir(),".claude.json")}function Kh(){return process.env.CLAUDE_CONFIG_DIR?Le.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Le.join(Xc.homedir(),".claude","settings.json")}async function pe(t){if(!t||typeof t!="string")return;let e=Le.resolve(t),n=Gh(),r=Kh(),o={};try{o=JSON.parse(await gn.readFile(n,"utf8"))}catch{}o.hasCompletedOnboarding=!0,o.theme||(o.theme="dark"),o.projects=o.projects||{},o.projects[e]=o.projects[e]||{};let s=o.projects[e];Array.isArray(s.allowedTools)||(s.allowedTools=[]),s.hasTrustDialogAccepted=!0,s.hasCompletedProjectOnboarding=!0;let a=process.env.ANTHROPIC_API_KEY;if(a){let c=a.slice(-20);o.customApiKeyResponses=o.customApiKeyResponses||{},o.customApiKeyResponses.approved=o.customApiKeyResponses.approved||[],o.customApiKeyResponses.rejected=o.customApiKeyResponses.rejected||[],o.customApiKeyResponses.approved.includes(c)||o.customApiKeyResponses.approved.push(c)}await gn.mkdir(Le.dirname(n),{recursive:!0}),await gn.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await gn.readFile(r,"utf8"))}catch{}if(i.skipDangerousModePermissionPrompt=!0,process.env.BOSTRAT_HOOKS!=="off"){let l=`curl -sS -m 3 -X POST ${process.env.BOSTRAT_HOOK_URL||`http://127.0.0.1:${process.env.PORT||3362}/api/hook`} -H 'content-type: application/json' --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let d of["SessionStart","UserPromptSubmit","Stop"])i.hooks[d]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await gn.mkdir(Le.dirname(r),{recursive:!0}),await gn.writeFile(r,JSON.stringify(i,null,2))}import Os from"node:fs/promises";import Vh from"node:os";import Qr from"node:path";function Jh(){let t=process.env.CODEX_HOME||Qr.join(Vh.homedir(),".codex");return Qr.join(t,"config.toml")}function Yc(t){return`[projects."${t}"]`}function zh(t,e){let n=Yc(e),r=t.indexOf(n);if(r===-1)return!1;let o=t.slice(r+n.length),s=o.search(/^\s*\[/m),a=s===-1?o:o.slice(0,s);return/trust_level\s*=\s*"trusted"/.test(a)}async function yn(t){if(!t||typeof t!="string")return;let e=Qr.resolve(t),n=Jh(),r="";try{r=await Os.readFile(n,"utf8")}catch{}if(zh(r,e))return;let o=Yc(e);if(r.includes(o)){let a=r.indexOf(o)+o.length,c=r.slice(a).search(/^\s*\[/m),l=c===-1?r.length:a+c,u=r.slice(a,l),d=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
2
+ trust_level = "trusted"
3
+ `;r=r.slice(0,a)+d+r.slice(l)}else{let s=`
4
+ ${o}
5
+ trust_level = "trusted"
6
+ `;r=r.trimEnd()+s+(r.length&&!r.endsWith(`
7
+ `)?`
8
+ `:"")}try{await Os.mkdir(Qr.dirname(n),{recursive:!0}),await Os.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}import Zc from"node:fs/promises";import Xh from"node:os";import or from"node:path";function Yh(t){let n=or.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||or.join(Xh.homedir(),".cursor");return or.join(r,"projects",n)}async function bn(t){if(!t||typeof t!="string")return;let e=or.resolve(t),n=Yh(e),r=or.join(n,".workspace-trusted");await Zc.mkdir(n,{recursive:!0}),await Zc.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
9
+ `,"utf8")}import sr from"node:fs/promises";import Zh from"node:os";import tl from"node:path";var Cs=process.env.BOSTRAT_DEBUG_DIR||tl.join(Zh.homedir(),".bostrat","debug"),kn=tl.join(Cs,"handoff.jsonl"),Qc=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;function to(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function Qh(t){try{await sr.mkdir(Cs,{recursive:!0}),await sr.appendFile(kn,`${JSON.stringify(t)}
10
+ `,"utf8");let n=(await sr.readFile(kn,"utf8")).split(`
11
+ `).filter(Boolean);n.length>Qc&&await sr.writeFile(kn,`${n.slice(-Qc).join(`
12
+ `)}
13
+ `,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${kn}: ${e?.message||e}`)}}async function L(t={}){let e={at:new Date().toISOString(),ts:Date.now(),...t},n=[e.flow||"handoff",e.phase||e.step||"?",e.trackId?`track=${e.trackId}`:"",e.sessionName?`box=${e.sessionName}`:"",e.error?`ERR=${e.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${n}`),e.sessionName&&to())try{e.paneTail=(await pt(e.sessionName,{lines:35})).slice(-4e3)}catch{e.paneTail=null}(to()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed)/.test(String(e.phase||e.step||"")))&&await Qh(e)}async function el({limit:t=80}={}){try{return(await sr.readFile(kn,"utf8")).split(`
14
+ `).filter(Boolean).slice(-t).map(r=>{try{return JSON.parse(r)}catch{return{raw:r}}})}catch{return[]}}var nl={DEBUG_DIR:Cs,LOG_FILE:kn};var nt=ew(tw);function ar(t){return new Promise(e=>setTimeout(e,t))}function Ms(t,e){if(!e)return t;let n=o=>typeof o=="string"?o.split(e).join("<redacted>"):o,r=new Error(n(t?.message)||"command failed");return t?.code!==void 0&&(r.code=t.code),t?.stderr!==void 0&&(r.stderr=n(t.stderr)),r}var O=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux",Ht=process.env.BOSTRAT_TMUX_SOCKET||"/tmp/tmux-1000/default",rw=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Ps=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Ns="bostrat-usage-probe",ow=new Set([Ns]);function Bs(t,e){let n=xn(t),r=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);return!n||r?t:`${t} --session-id ${e}`}function xn(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function Sn(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}async function Ds(t,{loginShell:e=!1,failOpen:n=!1}={}){if(!t)return!1;let r=e?process.env.SHELL||"/bin/sh":"sh",o=e?"-lic":"-c";try{return await nt(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:5e3}),!0}catch(s){return s?.code===1?!1:n}}async function js(t){return Ds(String(t||"").trim().split(/\s+/)[0])}function C(...t){return["-S",Ht,...t]}async function ee(){if(!rl.existsSync(Ht))return!1;try{return await nt(O,C("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}var sw=/^[A-Za-z0-9_.-]{1,64}$/;function ut(t){return typeof t=="string"&&sw.test(t)}async function vn(){if(!rl.existsSync(Ht))return[];try{let t="#{session_name}|#{session_windows}|#{session_attached}|#{session_activity}|#{pane_current_path}|#{@bostrat-session}|#{@bostrat-title}|#{@bostrat-sid}|#{@bostrat-agent}",{stdout:e}=await nt(O,C("list-sessions","-F",t)),n=Date.now();return e.trim().split(`
15
+ `).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,f]=r.split("|"),w=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(w)?n-w*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:f||(l==="1"?"claude":null)}}).filter(r=>!ow.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function G(t){return(await vn()).find(n=>n.name===t)||null}var aw=/^[A-Za-z0-9 ._-]+$/,iw=64;function cw(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=iw&&aw.test(e)}function Ls(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"")}async function lw(t){try{let{stdout:e}=await nt(O,C("list-sessions","-F","#{session_name}"));return e.split(`
16
+ `).map(n=>n.trim()).includes(t)}catch{return!1}}async function Ue(t,e=rw,n={}){if(!cw(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=Ls(r);if(!ut(o))throw new Error("resulting tmux session name is invalid");if(!await ee())throw new Error(`host tmux server not running on ${Ht} \u2014 start it with: systemctl --user start globo-tmux.service`);if(await lw(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Ps,a=n.agent||Sn(s),i=!!n.resumeSessionId,c=n.resumeSessionId||nw(),l=i?s:Bs(s,c),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&xn(s),f=n.firstRun===!0;f||(d&&await pe(e),a==="codex"&&await yn(e),a==="cursor"&&await bn(e));let w=d&&!f?Zr():null,m=w?["-e",`ANTHROPIC_API_KEY=${w}`]:[];try{await nt(O,C("new-session","-d","-s",o,"-c",e,...m))}catch(g){throw w?Ms(g,w):g}return await Fe(o),await nt(O,C("set-option","-t",o,"@bostrat-session","1")),await nt(O,C("set-option","-t",o,"@bostrat-title",r)),await nt(O,C("set-option","-t",o,"@bostrat-sid",c)),await nt(O,C("set-option","-t",o,"@bostrat-agent",a)),await nt(O,C("send-keys","-t",o,l,"Enter")),{name:o,title:r,cwd:e,sessionId:c,agent:a,pinned:u}}async function Wt(t){if(!ut(t))throw new Error("invalid session name");await nt(O,C("kill-session","-t",t))}async function ol(t,e){if(ut(t))try{let n=e?C("set-option","-t",t,"@bostrat-sid",e):C("set-option","-u","-t",t,"@bostrat-sid");await nt(O,n)}catch{}}async function pt(t,{lines:e=40}={}){if(!ut(t))return"";try{let n=C("capture-pane","-t",t,"-p");Number.isFinite(e)&&e>0&&n.push("-S",`-${Math.floor(e)}`);let{stdout:r}=await nt(O,n);return String(r||"")}catch{return""}}var uw=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,dw=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,Rt=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,fw=Number(process.env.BOSTRAT_BOX_COLS)||120,pw=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function mw(t){if(!ut(t))return 0;try{let{stdout:e}=await nt(O,C("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
17
+ `).filter(Boolean).length}catch{return 0}}async function Fe(t,{cols:e=fw,rows:n=pw}={}){if(ut(t)&&!await mw(t))try{let r=await al(t);if(r.cols===e&&r.rows===n)return;await nt(O,C("resize-window","-t",t,"-x",String(e),"-y",String(n))),await nt(O,C("set-window-option","-t",t,"window-size","latest")),await ar(80)}catch{}}async function sl(t){if(!ut(t))return 0;try{let{stdout:e}=await nt(O,C("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function al(t){if(!ut(t))return{cols:0,rows:0};try{let{stdout:e}=await nt(O,C("display-message","-t",t,"-p","#{window_width} #{window_height}")),[n,r]=String(e||"").trim().split(/\s+/).map(o=>Number(o)||0);return{cols:n,rows:r}}catch{return{cols:0,rows:0}}}async function eo(t,{cols:e=uw,rows:n=dw}={}){if(ut(t))try{let r=await al(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await nt(O,C("resize-window","-t",t,"-x",String(o),"-y",String(s))),await ar(80))}catch{}}async function me(t){return await eo(t),pt(t,{lines:Rt})}var hw=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);async function gt(t,e,{settleMs:n=40,kind:r}={}){if(!ut(t))throw new Error("invalid session name");let o=(Array.isArray(e)?e:[e]).filter(s=>hw.has(s));for(let s=0;s<o.length;s++)await nt(O,C("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await ar(n);return L({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function no(t,e,{kind:n}={}){if(!ut(t))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(e)||0));return r>0&&await nt(O,C("send-keys","-t",t,"-N",String(r),"BSpace")),L({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function ro(t,e,{perCharDelayMs:n=120,kind:r}={}){if(!ut(t))throw new Error("invalid session name");let o=[...String(e)];for(let s of o)await nt(O,C("send-keys","-t",t,"-l",s)),n>0&&await ar(n);L({flow:"tmux",phase:"type",sessionName:t,kind:r||"type",textLen:o.length,textHead:String(e).slice(0,120)}).catch(()=>{})}async function yt(t,e,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!ut(t))throw new Error("invalid session name");if(typeof e=="string"&&e.length>0&&await nt(O,C("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await ar(s),await nt(O,C("send-keys","-t",t,"Enter"))}L({flow:"tmux",phase:"send",sessionName:t,kind:o||"text",textLen:typeof e=="string"?e.length:0,textHead:typeof e=="string"?e.slice(0,120):"",enter:n,submitDelayMs:r}).catch(()=>{})}import ww from"node-pty";async function cl(t,e,n={}){if(!ut(e)){oo(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await ee()){oo(t,`host tmux server not running on ${Ht}`),t.close(1011,"tmux server unavailable");return}if(!await G(e)){oo(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=il(n.cols,120,ll),o=il(n.rows,32,ul),s;try{s=ww.spawn(O,["-S",Ht,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){oo(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&Us(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(Us(t,{type:"exit",code:a??null,signal:i??null}),t.close(1e3,"tmux exited"))}),t.on("message",a=>{let i;try{i=JSON.parse(a.toString())}catch{return}if(!(!i||typeof i!="object")){if(i.type==="data"&&typeof i.data=="string")s.write(i.data);else if(i.type==="resize"&&gw(i.cols,i.rows))try{s.resize(Math.min(500,i.cols|0),Math.min(500,i.rows|0))}catch{}}}),t.on("close",()=>{try{s.kill()}catch{}Fe(e).catch(()=>{})})}var ll=20,ul=6;function gw(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=ll&&Number(e)>=ul}function il(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function Us(t,e){try{t.send(JSON.stringify(e))}catch{}}function oo(t,e){Us(t,{type:"error",message:e})}function dl(t){let e=typeof t=="string"&&t?t:"/",n=e.indexOf("?"),r=n===-1?e:e.slice(0,n),o=n===-1?"":e.slice(n+1),s=r.match(/^\/api\/attach\/([^/]+)$/);if(!s)return{ok:!1,status:404};let a;try{a=decodeURIComponent(s[1])}catch{return{ok:!1,status:400}}let i=new URLSearchParams(o);return{ok:!0,sessionName:a,cols:Number(i.get("cols"))||void 0,rows:Number(i.get("rows"))||void 0}}import ir from"node:fs";import yw from"node:fs/promises";import Tn from"node:path";import bw from"node:os";var fl={"claude-opus-4-8":1e6,"claude-opus-4-7":1e6,"claude-opus-4-6":1e6,"claude-sonnet-4-6":1e6,"claude-haiku-4-5":2e5,"claude-fable-5":1e6},kw=2e5,Fs=process.env.BOSTRAT_CLAUDE_PROJECTS||Tn.join(bw.homedir(),".claude","projects");function Hs(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function Ws(t){let e=Tn.join(Fs,Hs(t)),n;try{n=ir.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=Tn.join(e,o);try{let{mtimeMs:a}=ir.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function ne(t,e){if(!e)return null;let n=Tn.join(Fs,Hs(t),`${e}.jsonl`);return ir.existsSync(n)?n:null}function pl(t){if(!t)return[];let e=Tn.join(Fs,Hs(t)),n;try{n=ir.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=ir.statSync(Tn.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function vt(t,e=131072){let n=await yw.open(t,"r");try{let{size:r}=await n.stat(),o=Math.max(0,r-e),s=r-o;if(s===0)return"";let a=Buffer.alloc(s);return await n.read(a,0,s,o),a.toString("utf8")}finally{await n.close()}}function xw(t){let e=t.split(`
18
+ `);for(let n=e.length-1;n>=0;n--){let r=e[n].trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a=(s.input_tokens||0)+(s.cache_creation_input_tokens||0)+(s.cache_read_input_tokens||0);if(a>0)return{occupancy:a,model:o.message.model||o.model||null}}return null}async function cr(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=ne(t,e),o=!0,!r)return null}else if(n){if(r=Ws(t),o=!1,!r)return null}else return null;let s;try{s=xw(await vt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(fl,s.model),i=fl[s.model]||kw;return{source:"transcript",exact:o,model:s.model,windowKnown:a,window:i,occupancy:s.occupancy,pct:Math.min(1,s.occupancy/i),transcript:r}}function Sw(t,e){return!t||!e?null:ne(t,e)}function vw(t){return typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&e.type==="text"&&typeof e.text=="string").map(e=>e.text).join(`
19
+ `).trim():""}function Tw(t){if(!Array.isArray(t))return null;let e=t.filter(n=>n&&n.type==="tool_use").map(n=>n.name).filter(Boolean);return e.length?e:null}function Aw(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function qs(t){let e=[],n=t?.message?.content;if(!Array.isArray(n))return e;for(let r of n)if(r){if(r.type==="image"&&r.source?.type==="base64"&&r.source.data)e.push({mediaType:r.source.media_type||"image/png",data:r.source.data,toolUseId:null});else if(r.type==="tool_result"&&Array.isArray(r.content))for(let o of r.content)o?.type==="image"&&o.source?.type==="base64"&&o.source.data&&e.push({mediaType:o.source.media_type||"image/png",data:o.source.data,toolUseId:r.tool_use_id||null})}return e}function ml(t,e){if(!t.uuid)return null;let n=qs(t).map((r,o)=>({uuid:t.uuid,index:o,mediaType:r.mediaType,name:r.toolUseId&&e.get(r.toolUseId)||null}));return n.length?n:null}var _w=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function Gs(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await vt(t,e)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
20
+ `)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let m=c.attachment;m?.type==="queued_command"&&m.commandMode==="task-notification"&&typeof m.prompt=="string"&&m.prompt.trim()&&o.push({role:"user",text:m.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant")continue;if(u==="assistant"&&Array.isArray(l.content))for(let m of l.content){let g=m?.type==="tool_use"?m.input?.file_path||m.input?.path:null;typeof g=="string"&&_w.test(g)&&s.set(m.id,g.split("/").pop())}if(Aw(l.content)){let m=ml(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let d=vw(l.content),f=u==="assistant"?Tw(l.content):null,w=u==="user"?ml(c,s):null;!d&&!f&&!w||o.push({role:u,text:d,tools:f||void 0,images:w||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null})}return o.slice(-n)}var Ew=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function hl(t,e,{turns:n=16}={}){let r=Sw(t,e);if(!r)return"";let o=await Gs(r,{limit:n});return o.length?o.map(s=>{let a=s.role==="user"?"User":"Lead agent",i=s.tools?`_(also ran: ${s.tools.join(", ")})_`:"",c=s.images?.length?"(viewed an image)":"(took an action)";return`**${a}:** ${Ew(s.text||c,700)} ${i}`.trim()}).join(`
21
+
22
+ `):""}import Gw from"node:fs";import ta from"node:fs/promises";import lo from"node:path";import Iw from"node:fs/promises";import{execFile as Rw}from"node:child_process";import{promisify as $w}from"node:util";var Ks=$w(Rw),Ow=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,wl=new Set,gl=Promise.resolve(),Vs=null;function bl(t){Vs=typeof t=="function"?t:null}function Cw(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function kl({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||wl.has(r)||(wl.add(r),gl=gl.then(()=>Mw({sessionName:t,from:e,to:r})).catch(()=>{}))}async function Mw({sessionName:t,from:e,to:n}){await L({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await Ks("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await Iw.realpath(r),s=Cw(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Ks("npm",["install","-g","@openai/codex"],{env:a,timeout:Ow});let i=(await Ks(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await L({flow:"codex-update",phase:"done",sessionName:t,from:e,to:n,version:i}).catch(()=>{}),await yl({sessionName:t,from:e,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await L({flow:"codex-update",phase:"failed",sessionName:t,from:e,to:n,error:o}).catch(()=>{}),await yl({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function yl(t){if(Vs)try{await Vs(t)}catch{}}var Pw=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Js=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,lr=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,$t={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,auth:/Select login method:|Paste code here if prompted|OAuth error:|Choose the text style that looks best|Quick safety check: Is this a project you created/},codex:{up:/(^|\n)\s*›/,busy:/esc to interrupt|\bWorking \(/,block:/Update available![\s\S]*\b(?:Update now|Skip until next version)\b[\s\S]*Press enter to continue/,auth:/Sign in with ChatGPT[\s\S]*Press enter to continue|Finish signing in via your browser|Paste or type your API key/},cursor:{up:/Run Everything|Add a follow-up|Plan, search, build anything/,busy:/ctrl\+c to stop|^\s*[⠁-⣿]+\s+\S/m,block:/Workspace Trust Required|Trust this workspace|\[a\]\s*Trust|Trusting workspace/}},Nw=/Trust this workspace|\[a\]\s*Trust/,Bw=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function Dw(t){let e=String(t||"");if(!$t.codex.block.test(e))return null;let n=e.match(Bw);return{from:n?.[1]||null,to:n?.[2]||null}}function he(t){return new Promise(e=>setTimeout(e,t))}var jw=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,so=new Map;function xl(t,e){let n=Date.now(),r=jw();for(let[s,a]of so)n-a>=r&&so.delete(s);let o=`${t}::${e}`;return so.has(o)?!1:(so.set(o,n),!0)}var Lw=68,Uw=/^\s*[─━]{10,}\s*$/;function Fw(t){let e=0;for(let n of String(t).split(`
23
+ `))Uw.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function zs(t,e){let n=$t[t];if(!n?.busy)return"idle";if(Xs(t,e))return"unknown";if(n.up&&!n.up.test(e))return"idle";if(n.busy.test(e))return"busy";let r=Fw(e);return t==="claude"&&r>0&&r<Lw?"unknown":"idle"}var Hw=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/;function vl(t,e){return t==="claude"&&Hw.test(String(e||""))}function ur(t,e){return zs(t,e)==="busy"}function ao(t,e){return zs(t,e)!=="idle"}function io(t,e){let n=$t[t];return n?.up?n.up.test(e):!0}function Ww(t,e){let n=$t[t];return n?Xs(t,e)?!1:n.up.test(e)&&zs(t,e)==="idle":!0}function Xs(t,e){let n=$t[t];return n?n.auth?.test(e)&&!n.up.test(e)?!0:n.block?.test(e)?t==="cursor"?!n.up.test(e):!0:!1:!1}async function Sl(t,e,n){let r=Date.now()+n,o=0;for(await Fe(t).catch(()=>{});;){let s=await pt(t);if(Ww(e,s)){await he(lr);return}if(e==="cursor"&&Nw.test(s)&&!$t.cursor.up.test(s)&&o<3){if(!xl(t,"cursor-trust")){await he(Js);continue}o+=1,await L({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await yt(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await he(lr);continue}if(e==="codex"&&o<3){let a=Dw(s);if(a){if(!xl(t,`codex-update:${a.from}->${a.to}`)){await he(Js);continue}o+=1,await L({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await yt(t,"3",{enter:!1,kind:"codex-update-skip"}),kl({sessionName:t,from:a.from,to:a.to}),await he(lr);continue}}if(Date.now()>r){if(Xs(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=$t.codex.auth.test(s)?"codex isn't signed in on this box \u2014 answer the Sign in card in chat (Device Code works from any device) or finish signing in from the Terminal tab, then send again":"codex is stuck on its update prompt (auto-skip didn't clear it) \u2014 open the terminal and choose Skip, then send again":e==="claude"&&$t.claude.auth.test(s)&&(a="claude isn't signed in on this box \u2014 answer the setup cards in chat (or finish signing in from the Terminal tab), then send again"),L({flow:"agent-ready",phase:"blocked",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}console.warn(`[agent-ready] ${e} readiness unconfirmed in ${t} after ${n}ms \u2014 driving the box anyway.`),L({flow:"agent-ready",phase:"timeout",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,paneBlocked:!!$t[e]?.block?.test(s),paneUp:!!$t[e]?.up?.test(s),paneBusy:!!$t[e]?.busy?.test(s)}).catch(()=>{}),await he(lr);return}await he(Js)}}function Tl({agent:t,agentCmd:e}={}){let n=e?Sn(e):Sn(t||"");return{label:n,realClaude:e?xn(e):n==="claude",realCodex:e?/codex/.test(e)&&!/fake-codex|fake-lead/.test(e):n==="codex",realCursor:e?/cursor/.test(e)&&!/fake-cursor|fake-box/.test(e):n==="cursor"}}function co({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Tl({agent:n,agentCmd:r});return!e||!o&&!s?null:we({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function we({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=Pw()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=Tl({agent:r,agentCmd:o});if(i&&t&&await pe(t),c&&t&&await yn(t),l&&t&&await bn(t),(i||c||l)&&n){await Sl(n,a,s);return}let u=$t[r]?r:$t[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await Sl(n,u,s);return}await he(lr)}var qw=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})();function re(t){return t==="codex"?{enter:!0,submitDelayMs:qw}:{enter:!0}}var Al=".bostrat/handoff",_l="latest.md",El="Checkpoint: write a work-in-progress handoff",Il="HANDOFF WRITTEN",Rl="Resume from .bostrat/handoff/latest.md",Kw="/clear",Vw=t=>`${El} to the EXACT absolute path ${t} (write it there specifically \u2014 NOT into any sub-project or other directory; create the directory if needed, overwrite the file) so a FRESH Claude context with none of this conversation can resume seamlessly. Use these sections: Objective; Done (with file paths); In progress (exact state); Next steps (ordered); Key files & commands; Open decisions/blockers; Resume instruction (one line). Be concrete and self-contained \u2014 assume the reader has none of this chat. When the file is written, reply only with ${Il} and take no further action.`,Jw=`${Rl}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`;function zw(t,e){let n=(e||"").trim();return n?t==="assistant"?n===Il:n.startsWith(El)||n===Kw||n.startsWith(Rl):!1}var Xw=6e5;function $l(t){let e=[];for(let r of t?.events||[]){if(r.type!=="lead-handoff"||!r.from)continue;let o=r.at??Date.now(),s=r.handoffStartedAt??o-Xw;e.push({box:r.from,from:s,to:o}),r.to&&r.to!==r.from&&e.push({box:r.to,from:s,to:o})}let n=t?.handoff;return n?.fromBox&&n.startedAt&&n.finishedAt&&e.push({box:n.fromBox,from:n.startedAt,to:n.finishedAt}),n?.toBox&&n.startedAt&&n.finishedAt&&e.push({box:n.toBox,from:n.startedAt,to:n.finishedAt}),e}function Ol(t,e){let n=[];for(let r of t?.boxes||[]){let o=e?.(r.name);o?.startedAt&&n.push({box:r.name,from:o.startedAt,to:o.finishedAt||Date.now()+1e12})}return n}function Cl(t,{windows:e=[]}={}){if(zw(t.role,t.text))return!0;if(t.role!=="assistant"||!t.ts)return!1;for(let n of e)if(t.box===n.box&&t.ts>=n.from&&t.ts<=n.to)return!0;return!1}var Yw=3500,Zw=3e5,Ys=new Map;function uo(t){return Ys.get(t)||null}async function Ml(t){let e=Ys.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await G(t);if(!n)throw new Error(`session "${t}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${t}"`);let r={name:t,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:lo.join(n.cwd,Al,_l)};return Ys.set(t,r),Qw(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function Qw(t,e){t.step="summarize",await Pl(t.name,e),t.step="clear";let n=await G(t.name);await Qs(t.name,n,"/clear"),await Bl(Yw),t.step="resume",await Qs(t.name,n,Jw),t.step="done",t.finishedAt=Date.now()}async function Pl(t,e){let n=lo.join(e,Al),r=lo.join(n,_l);await ta.mkdir(n,{recursive:!0});let o=await Zs(r),s=await G(t);return await L({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:r,beforeMtime:o}),await Qs(t,s,Vw(r)),await tg(r,o,Zw),await L({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:r,afterMtime:await Zs(r)}),await eg(n,r),r}async function Nl(t){let e=await G(t);if(!e)throw new Error(`session "${t}" not found`);if(!e.cwd)throw new Error(`can't resolve cwd for "${t}"`);return Pl(t,e.cwd)}async function Zs(t){try{return(await ta.stat(t)).mtimeMs}catch{return 0}}async function tg(t,e,n){let r=Date.now()+n;for(;;){if(await Zs(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await Bl(2e3)}}async function eg(t,e){try{if(!Gw.existsSync(e))return;let n=new Date().toISOString().replace(/[:.]/g,"-");await ta.copyFile(e,lo.join(t,`${n}.md`))}catch{}}function Bl(t){return new Promise(e=>setTimeout(e,t))}async function Qs(t,e,n){if(e){let o=e.agent==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":e.agent==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":e.agent==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":"";await we({cwd:e.cwd,sessionId:e.sessionId,sessionName:t,agent:e.agent,agentCmd:o})}let r=await G(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await yt(t,n,{kind:"orchestration-drive",...re(r.agent)})}import P from"node:fs/promises";import U from"node:path";import{randomUUID as Qo}from"node:crypto";import{execFile as Dx}from"node:child_process";import{promisify as jx}from"node:util";import ea from"node:fs/promises";import ng from"node:path";var rg=0;async function qt(t,e,{mode:n}={}){await ea.mkdir(ng.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++rg}.tmp`;await ea.writeFile(r,JSON.stringify(e,null,2)+`
24
+ `,n!=null?{mode:n}:void 0),await ea.rename(r,t)}import{execFile as og}from"node:child_process";import{promisify as sg}from"node:util";import{randomUUID as ag}from"node:crypto";var He=sg(og),fo=t=>new Promise(e=>setTimeout(e,t)),ig=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Dl=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,cg=250,lg=400,jl={claude:{escape:!0,wipe:!0,text:"/exit"},codex:{escape:!0,wipe:!0,text:"/quit"},cursor:{escape:!0,wipe:!0,text:"/quit"},default:{escape:!1,wipe:!1,text:"exit"}};async function Ul(t){let e=await sl(t);if(!e)return!1;try{return await He("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function na(t,e){let n=Date.now()+e;for(;;){if(await Ul(t))return!0;if(Date.now()>n)return!1;await fo(cg)}}async function ug(t,e){let n=jl[e]||jl.default;n.escape&&(await gt(t,["Escape"],{kind:"swap-exit"}),await fo(150)),n.wipe&&await no(t,400,{kind:"swap-exit"}),await ro(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=re(e).submitDelayMs||0;await fo(Math.max(r,250)),await gt(t,["Enter"],{kind:"swap-exit"})}async function dg(t,e){try{let{stdout:n}=await He(O,C("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Ll(t){await He(O,C("respawn-pane","-k","-t",t))}async function ra(t,e={}){if(!ut(t))throw new Error("invalid session name");let n=String(e.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await G(t);if(!r)throw new Error(`session "${t}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${t}"`);let o=r.agent||"agent",s=e.agent||Sn(n),a=Number(e.exitTimeoutMs)||ig;await L({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Ul(t)&&(await ug(t,o),!await na(t,a)&&(i=!0,await L({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await Ll(t),!await na(t,Dl))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&xn(n),l=c?Zr():null,u=await dg(t,"ANTHROPIC_API_KEY"),d=!1;try{l?(await He(O,C("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await He(O,C("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),d=!0)}catch(x){throw l?Ms(x,l):x}if(d&&!i&&(await Ll(t),!await na(t,Dl)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let f=!!e.resumeSessionId,w=e.resumeSessionId||ag(),m=f?n:Bs(n,w),g=f?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await pe(r.cwd),s==="codex"&&await yn(r.cwd),s==="cursor"&&await bn(r.cwd),await Fe(t),await He(O,C("set-option","-t",t,"@bostrat-sid",w)),await He(O,C("set-option","-t",t,"@bostrat-agent",s)),await fo(lg),await yt(t,m,{kind:"swap-launch"}),await L({flow:"swap",phase:"done",sessionName:t,from:o,to:s,sid:w,forced:i,envRespawned:d}),{name:t,cwd:r.cwd,sessionId:w,agent:s,pinned:g,forced:i,envRespawned:d}}import Sg from"node:fs";import Hl from"node:fs/promises";import dr from"node:path";import vg from"node:os";import{fileURLToPath as Tg}from"node:url";import{execFile as Ag}from"node:child_process";import{promisify as _g}from"node:util";import sa from"node:fs/promises";import ge from"node:path";import{execFile as fg}from"node:child_process";import{promisify as pg}from"node:util";var mg=pg(fg),Fl=process.env.HOME||"/home/globo",hg=ge.join(process.env.BOSTRAT_STATE_DIR||ge.join(Fl,".bostrat"),"repos"),wg=[...(process.env.BOSTRAT_REPOS_ROOT||ge.join(Fl,"Documents")).split(","),hg].map(t=>t.trim()).filter(Boolean),gg=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,yg=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function bg(t){try{return(await sa.stat(ge.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function kg(t){let e=i=>sa.readFile(ge.join(t,".git",i),"utf8").then(c=>c.trim()).catch(()=>null),r=(await e("refs/remotes/origin/HEAD"))?.match(/^ref:\s*refs\/remotes\/origin\/(.+)/);if(r)return r[1].trim();let o;for(let i of["main","master"])if(await e(`refs/heads/${i}`)||(o??=await e("packed-refs")||"",new RegExp(`^[0-9a-f]{40} refs/heads/${i}$`,"m").test(o)))return i;let a=(await e("HEAD"))?.match(/^ref:\s*refs\/heads\/(.+)/);return a?a[1].trim():null}async function oe(){let t=new Map;async function e(n,r){if(t.has(n))return;let o=await bg(n);if(o==="worktree")return;if(o==="repo"){t.set(n,{path:n,name:ge.basename(n),branch:await kg(n)});return}if(r>=gg)return;let s;try{s=await sa.readdir(n,{withFileTypes:!0})}catch{return}for(let a of s)!a.isDirectory()||a.name.startsWith(".")||yg.has(a.name)||await e(ge.join(n,a.name),r+1)}for(let n of wg)await e(ge.resolve(n),0);return[...t.values()].sort((n,r)=>n.name.localeCompare(r.name))}function xg(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var oa=new Map;async function Y(t){if(!t)return null;if(oa.has(t))return oa.get(t);let e=null;try{let{stdout:n}=await mg("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=xg(n)}catch{e=null}return oa.set(t,e),e}var Wl=_g(Ag),Eg=process.env.BOSTRAT_STATE_DIR||dr.join(process.env.HOME||vg.tmpdir(),".bostrat"),Ig=dr.join(Eg,"repos"),ql=/^[^/\s]+\/[^/\s]+$/,Rg=5*6e4,$g=6e4,Og=10*6e4,Gl=fetch,aa=new Map,We=null;function ia(t=process.env){let e=t.BOSTRAT_PLANE_URL||"";if(!e)return null;try{let n=new URL(e);return n.protocol=n.protocol==="ws:"?"http:":"https:",n.origin}catch{return null}}function po(t=process.env){return!!(ia(t)&&t.BOSTRAT_PLANE_TOKEN)}var Kl=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),An=(t,e)=>Object.assign(new Error(t),{status:e});async function mo(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!ql.test(n))throw An("repo must be owner/name",400);let r=aa.get(n);if(r&&r.expiresAt-Date.now()>Rg)return r;if(!po(e))throw An("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Gl(`${ia(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:Kl(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw An(`plane unreachable \u2014 GitHub auth unavailable (${i?.message||i})`,503)}let s=await o.json().catch(()=>({}));if(!o.ok){if(o.status>=500&&r&&r.expiresAt>Date.now())return r;throw An(s?.error||`plane replied ${o.status}`,o.status)}let a={token:s.token,expiresAt:Number(s.expiresAt)||Date.now()+55*6e4,username:s.username??null,installationId:s.installationId??null};return aa.set(n,a),a}async function fr({env:t=process.env,force:e=!1}={}){if(!po(t))return{configured:!1,repos:[],username:null};if(!e&&We&&Date.now()-We.at<$g)return We.data;try{let n=await Gl(`${ia(t)}/v1/github/repos`,{headers:Kl(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return We={at:Date.now(),data:r},r}catch(n){return We?We.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Vl(){aa.clear(),We=null}async function Gt({cwd:t=null,slug:e=null,env:n=process.env}={}){try{let r=e||(t?await Y(t):null);if(!r)return{};let{token:o}=await mo(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function ca({env:t=process.env}={}){return(await fr({env:t})).username??null}var Cg=()=>Tg(new URL("../bin/bostrat-git-credential",import.meta.url)),Mg=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function Pg(t){let[e,n]=String(t).split("/");return dr.join(Ig,e,n)}async function la(t,{env:e=process.env}={}){let n=(...r)=>Wl("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${Cg()}" ${Mg(e)}`),await n("config","credential.useHttpPath","true")}async function ua(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!ql.test(String(t||"")))throw An(`bad repo slug "${t}"`,400);let r=Pg(t);if(Sg.existsSync(dr.join(r,".git")))return r;let{token:o}=await mo(t,{env:e});await Hl.mkdir(dr.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Wl("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:Og,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Hl.rm(r,{recursive:!0,force:!0}).catch(()=>{}),An(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await la(r,{env:e}),r}import py from"node:fs/promises";function Ng(t){let e=[],n="",r=null;for(let o=0;o<t.length;o++){let s=t[o];if(r){n+=s,s===r&&t[o-1]!=="\\"&&(r=null);continue}if(s==="'"||s==='"'||s==="`"){r=s,n+=s;continue}if(s==="&"&&t[o+1]==="&"){e.push(n),n="",o++;continue}if(s==="|"&&t[o+1]==="|"){e.push(n),n="",o++;continue}if(s==="|"||s===";"||s===`
25
+ `){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function pr(t){let e=[],n="",r=null,o=!1,s=()=>{if(o){if(/^\d*[<>]/.test(n)||n.startsWith(">")){n="",o=!1;return}e.push(n),n="",o=!1}};for(let a=0;a<t.length;a++){let i=t[a];if(r){i===r&&t[a-1]!=="\\"?r=null:n+=i,o=!0;continue}if(i==="'"||i==='"'){r=i,o=!0;continue}if(i==="<"&&t[a+1]==="<")return s(),e.push("<<"),e;if(/\s/.test(i)){s();continue}n+=i,o=!0}return s(),e}function mr(t){let e=t;for(;e.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(e[0]);)e=e.slice(1);for(;e.length&&(e[0]==="sudo"||e[0]==="time"||e[0]==="env");)e=e.slice(1);return e}var En=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,kt=t=>t&&!/[$`]/.test(t)?t:null,st=t=>t.replace(/\/+$/,"").split("/").pop()||t,_n=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},Xl=t=>t.startsWith("-"),Jl=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);function V(t,e=new Set){let n=[];for(let r=1;r<t.length;r++){let o=t[r];if(o==="<<")break;if(Xl(o)){e.has(o)&&r++;continue}n.push(o)}return n}var Bg={status:()=>"Check git status",diff:()=>"Review changes",log:()=>"Review commit history",show:()=>"Inspect a commit",add:()=>"Stage files",commit:()=>"Commit changes",push:()=>"Push to remote",pull:()=>"Pull from remote",fetch:()=>"Fetch from remote",merge:()=>"Merge branches",rebase:()=>"Rebase branch",stash:()=>"Stash changes",clone:t=>t[1]?`Clone ${st(t[1])}`:"Clone repository",checkout:()=>"Switch branches",switch:()=>"Switch branches",restore:()=>"Restore files",reset:()=>"Reset git state",branch:(t,e)=>e.includes("--show-current")?"Check current branch":"List branches",remote:()=>"Check git remotes","rev-parse":()=>"Resolve a git ref",worktree:()=>"Manage git worktrees","merge-base":()=>"Find the merge base","ls-files":()=>"List tracked files"},Dg={pr:t=>({create:"Create a pull request",list:"List pull requests",view:"View a pull request",merge:"Merge a pull request",checks:"Check PR status"})[t[1]]||"Run a gh pr command",auth:()=>"Authenticate GitHub CLI",api:()=>"Call the GitHub API",repo:()=>"Inspect the GitHub repo",issue:()=>"Work with GitHub issues"};function Yl(t){let e=t.indexOf("-n"),n=e!==-1?t[e+1]:null;if(!n)return null;let r=/^(\d+),(\d+)p$/.exec(n)||/^(\d+)p$/.exec(n);return r?r[2]?{from:r[1],to:r[2]}:{from:r[1],to:r[1]}:null}function jg(t){let e=Yl(t),n=kt(V(t).find(r=>!/^\d+(,\d+)?p$/.test(r)));return e&&n?e.from===e.to?`Read line ${e.from} of ${st(n)}`:`Read lines ${e.from}\u2013${e.to} of ${st(n)}`:t.includes("-i")&&n?`Edit ${st(n)}`:null}function da(t){if(t.includes("--files")){let s=kt(V(t,Jl)[0]);return s&&s!=="."?`List files in ${_n(s)}`:"List files"}let e=V(t,Jl),n=e[0];if(!n)return null;let r=kt(e.slice(1).find(s=>s!==".")),o=En(n,40);return r?`Search for \u201C${o}\u201D in ${_n(r)}`:`Search for \u201C${o}\u201D`}function fa(t){let e=V(t,new Set(["--prefix"])),n=e[0];return n?n==="test"||n==="t"?"Run tests":n==="run"||n==="run-script"?e[1]?`Run ${e[1]}`:null:n==="ci"||n==="install"||n==="i"||n==="add"?"Install dependencies":null:null}function pa(t,e){if(t.includes("-e")||t.includes("--eval")||t.includes("-c")||t[1]==="-"||t.includes("<<"))return`Run an inline ${e} script`;if(t.includes("--test"))return"Run tests";let n=V(t)[0];return n?`Run ${st(n)}`:null}function Lg(t){let e=V(t),n=e[0],r=e.find(a=>/\.(service|timer|socket|target)$/.test(a))||e[1],o=r?r.replace(/\.service$/,""):"a service";return{status:`Check ${o} status`,show:`Inspect ${o}`,restart:`Restart ${o}`,start:`Start ${o}`,stop:`Stop ${o}`,"daemon-reload":"Reload systemd"}[n]||null}function zl(t){return t.find(e=>/^https?:\/\//.test(e))||null}var Ug={git:t=>{let e=V(t,new Set(["-C","-c"])),n=Bg[e[0]];return n?n(e,t):null},gh:t=>{let e=V(t),n=Dg[e[0]];return n?n(e):null},sed:jg,rg:da,grep:da,egrep:da,nl:t=>{let e=kt(V(t)[0]);return e?`Read ${st(e)}`:null},cat:t=>{let e=kt(V(t)[0]);return e?`Read ${st(e)}`:null},head:t=>{let e=kt(V(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${st(e)}`:null},tail:t=>{let e=kt(V(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${st(e)}`:null},wc:t=>{let e=kt(V(t)[0]);return e?`Count lines in ${st(e)}`:"Count input lines"},ls:t=>{let e=kt(V(t)[0]);return e&&e!=="."?`List files in ${_n(e)}`:"List files"},find:t=>kt(t[1])&&!Xl(t[1])&&t[1]!=="."?`Find files in ${_n(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=kt(V(t)[0]);return e?`Create directory ${_n(e)}`:null},touch:t=>{let e=kt(V(t)[0]);return e?`Create ${st(e)}`:null},rm:t=>{let e=kt(V(t)[0]);return e?`Delete ${_n(e)}`:null},mv:t=>{let e=kt(V(t)[0]);return e?`Move ${st(e)}`:null},cp:t=>{let e=kt(V(t)[0]);return e?`Copy ${st(e)}`:null},chmod:t=>{let e=kt(V(t)[1]);return e?`Change permissions on ${st(e)}`:null},diff:t=>{let e=V(t);return e.length>=2?`Compare ${st(e[0])} and ${st(e[1])}`:null},curl:t=>{let e=zl(t);return e?`Fetch ${En(e,56)}`:"Fetch a URL"},wget:t=>{let e=zl(t);return e?`Fetch ${En(e,56)}`:"Fetch a URL"},npm:fa,pnpm:fa,yarn:fa,npx:t=>{let e=V(t)[0];return e?`Run ${e}`:null},node:t=>pa(t,"Node"),python:t=>pa(t,"Python"),python3:t=>pa(t,"Python"),make:t=>{let e=V(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:Lg,journalctl:t=>{let e=t.findIndex(r=>r==="-u"||r==="--unit"||r==="--user-unit"),n=e!==-1?t[e+1]:null;return n?`Read ${n.replace(/\.service$/,"")} logs`:"Read system logs"},tmux:t=>{let e=V(t,new Set(["-S","-L","-t","-f"]))[0];return{ls:"List tmux sessions","list-sessions":"List tmux sessions","kill-session":"Kill a tmux session","send-keys":"Send keys to a tmux session","capture-pane":"Capture a tmux pane","new-session":"Start a tmux session"}[e]||null},ps:()=>"List processes",which:t=>{let e=V(t)[0];return e?`Locate ${e}`:null},command:t=>t[1]==="-v"&&t[2]?`Locate ${t[2]}`:null,fly:t=>{let e=V(t)[0];return e==="deploy"?"Deploy to Fly":e?`Run fly ${e}`:null}},Fg=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function Hg(t){let e=mr(pr(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(st(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return ma(e[2]);let n=st(e[0]);if(Fg.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=Ug[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function ma(t){let e=String(t||"").trim();if(!e)return null;let n=e.indexOf("<<");if(n!==-1){let i=e.indexOf(`
26
+ `,n);i!==-1&&(e=e.slice(0,i))}let r=Ng(e);for(let i=0;i<r.length-1;i++){let c=mr(pr(r[i])),l=mr(pr(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(st(c[0]))&&st(l[0])==="sed"){let u=Yl(l),d=V(c)[0];if(u&&d)return En(u.from===u.to?`Read line ${u.from} of ${st(d)}`:`Read lines ${u.from}\u2013${u.to} of ${st(d)}`,88)}}let o=[];for(let i of r){let c=Hg(i);c&&o.push({d:c,head:st(mr(pr(i))[0]||""),sub:V(mr(pr(i)))[0]})}if(!o.length)return null;let s=o.find(i=>i.head==="git"&&i.sub==="commit"),a=o.find(i=>i.head==="git"&&i.sub==="push");return s&&a?"Commit and push changes":En(s?s.d:o[0].d,88)}var wa=new Set(["Bash"]);function In(t){let e=t.split(`
27
+ `).map(n=>n.trim()).find(Boolean)||t;return e.length>88?`${e.slice(0,88).trimEnd()}\u2026`:e}function ye(t,e=""){let n=e.trim();return n||ma(t)||In(t)}function ha(t,e){if(t){if(Array.isArray(t)){for(let n of t)ha(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&wa.has(t.name)){let n=String(t.input?.command||"").trim();n&&e.push({id:t.id||null,tool:t.name,command:n,description:String(t.input?.description||"").trim()})}for(let n of Object.values(t))ha(n,e)}}}async function Zl(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await vt(t,e)}catch{return[]}let o=[];for(let s of r.split(`
28
+ `)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(ha(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:ye(d.command,d.description),ts:u})}return o.slice(-n)}function Ql(t,e){let n=e.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...t].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((m,g)=>g>=s&&(m.ts||0)>(l.ts||0)),d=u===-1?r.length:u,f=r.slice(s,d);if(s=d,!f.length)continue;let w=f.map(m=>({...m,n:i(m)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:w,ts:Math.max(...w.map(m=>m.ts||0)),pending:!1})}let c=r.slice(s);if(c.length){let l=c.map(u=>({...u,n:i(u)}));o.push({id:`batch-pending-${l[0]?.box||"lead"}`,anchorTs:null,anchorBox:l[l.length-1]?.box??null,agent:l[l.length-1]?.agent??"claude",boxRole:l[l.length-1]?.boxRole??"lead",commands:l,ts:Math.max(...l.map(u=>u.ts||0)),pending:!0})}return o}var Wg=/^Async (?:agent|task) launched/i,qg=t=>{if(typeof t=="string")return t.trim();if(!Array.isArray(t))return"";let e=t.find(n=>n&&n.type==="text"&&typeof n.text=="string");return e?e.text.trim():""};async function tu(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await vt(t,e)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
29
+ `)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.isSidechain)continue;let u=l.message?.content;if(!Array.isArray(u))continue;let d=l.timestamp?Date.parse(l.timestamp):null;for(let f of u)if(!(!f||typeof f!="object"))if(f.type==="tool_use"&&f.id){if(f.name==="Agent"){let w=f.input||{};o.set(f.id,{id:f.id,kind:"agent",name:String(w.description||"").trim()||null,detail:String(w.subagent_type||"").trim()||null,ts:d,proven:!1})}else if(f.name==="Bash"&&f.input?.run_in_background===!0){let w=String(f.input.command||"").trim();o.set(f.id,{id:f.id,kind:"command",name:String(f.input.description||"").trim()||(w?In(w):null),detail:w?In(w):null,ts:d,proven:!0})}}else f.type==="tool_result"&&f.tool_use_id&&Wg.test(qg(f.content))&&s.add(f.tool_use_id)}let a=[];for(let i of o.values()){if(!i.proven&&!s.has(i.id))continue;let{proven:c,...l}=i;a.push(l)}return a.slice(-n)}import hr from"node:fs";import Kg from"node:fs/promises";import qe from"node:path";import Vg from"node:os";import Jg from"node:crypto";import zg from"node:zlib";var ho=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),jt=(t,e=88)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Rn=t=>String(t).replace(/\/+$/,"").split("/").pop()||String(t);function be(t,e){if(!t)return null;let n=String(t);if(e){let o=e.endsWith("/")?e:`${e}/`;if(n===e)return".";if(n.startsWith(o))return n.slice(o.length)||"."}let r=n.split("/").filter(Boolean);return n.startsWith("/")&&r.length>2?r.slice(-2).join("/"):n}var ga=t=>`"${String(t).replace(/"/g,'\\"')}"`;function Gg(t){let e=typeof t=="string"?t:String(t?.patch??t?.input??""),n=[];for(let r of e.matchAll(/^\*{3} (?:Update|Add|Delete) File: (.+)$/gm))n.push(r[1].trim());return n}function eu(t,e,n){switch(t){case"Shell":{let r=String(e?.command||"").trim();return r?{command:r,description:String(e?.description||"").trim()}:null}case"Read":{let r=e?.path;if(!r)return null;let o=Number(e?.offset)||null,s=o&&Number(e?.limit)?o+Number(e.limit)-1:null;return{command:jt(`read ${be(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${Rn(r)}`:`Read ${Rn(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?be(e.path,n):null,s=e?.glob?` --glob ${ga(e.glob)}`:"",a=jt(String(r),40);return{command:jt(`grep ${ga(r)}${o&&o!=="."?` ${o}`:""}${s}`,140),description:o&&o!=="."?`Search for \u201C${a}\u201D in ${o}`:`Search for \u201C${a}\u201D`}}case"Glob":{let r=e?.glob_pattern;if(!r)return null;let o=e?.target_directory?be(e.target_directory,n):null;return{command:jt(`glob ${ga(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${jt(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:jt(`edit ${be(e.path,n)}`,140),description:`Edit ${Rn(e.path)}`}:null;case"Write":return e?.path?{command:jt(`write ${be(e.path,n)}`,140),description:`Write ${Rn(e.path)}`}:null;case"Delete":return e?.path?{command:jt(`delete ${be(e.path,n)}`,140),description:`Delete ${Rn(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>be(s,n)).filter(Boolean);return{command:jt(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=Gg(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:jt(`apply_patch ${be(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${Rn(r[0])}`}}case"Await":{let r=e?.shell_id??e?.task_id;return{command:jt(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Xg=process.env.BOSTRAT_CURSOR_CHATS||qe.join(Vg.homedir(),".cursor","chats");function ou(t){let e=Jg.createHash("md5").update(String(t)).digest("hex");return qe.join(Xg,e)}function su(t){try{let e=JSON.parse(hr.readFileSync(qe.join(t,"meta.json"),"utf8"));return{createdAtMs:Number(e.createdAtMs)||null,updatedAtMs:Number(e.updatedAtMs)||Number(e.createdAtMs)||null,hasConversation:e.hasConversation===!0}}catch{return null}}function Yg(t){let e=ou(t),n;try{n=hr.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=qe.join(e,o.name),a=qe.join(s,"store.db");if(!hr.existsSync(a))continue;let i=su(s);if(!i)continue;let c={id:o.name,dir:s,store:a,...i};(!r||c.hasConversation&&!r.hasConversation||c.hasConversation===r.hasConversation&&(c.updatedAtMs||0)>(r.updatedAtMs||0))&&(r=c)}return r}function wo(t,e){if(!t)return null;if(e){let n=qe.join(ou(t),e),r=qe.join(n,"store.db");if(hr.existsSync(r))return{id:e,dir:n,store:r,...su(n)||{createdAtMs:null,updatedAtMs:null}}}return Yg(t)}function nu(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function Zg(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=zg.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function Qg(t){let e=[],n=0,r=t.length,o=()=>{let s=0,a=0;for(;n<r;){let i=t[n++];if(s+=(i&127)*2**a,!(i&128))break;a+=7}return s};for(;n<r;){let s=o(),a=s>>>3,i=s&7;if(i===2){let c=o(),l=t.subarray(n,n+c);n+=c,a===1&&c===32&&e.push(l.toString("hex"))}else if(i===0)o();else if(i===5)n+=4;else if(i===1)n+=8;else break}return e}function ty(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!ho.has(r.toolName))continue;let o=eu(r.toolName,r.args,e);!o||!o.command||n.push({id:r.toolCallId||null,tool:r.toolName,command:o.command,description:o.description||""})}return n}var ey=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&e.type==="text"&&typeof e.text=="string").map(e=>e.text).join(`
30
+ `).trim():"",ny=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],ru=process.getBuiltinModule?.("node:sqlite"),$n={turns:[],trailingCommands:[],model:null,requestIds:[]};async function ya(t,e){if(!ru)return $n;let{DatabaseSync:n}=ru,r;try{r=new n(t,{readOnly:!0})}catch{return $n}try{let o=r.prepare("SELECT value FROM meta WHERE key = '0'").get()||r.prepare("SELECT value FROM meta LIMIT 1").get();if(!o)return $n;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return $n}let a=s.lastUsedModel||null,i=s.latestRootBlobId?nu(r,s.latestRootBlobId):null;if(!i)return{...$n,model:a};let c=[],l=[],u=[],d=null,f=[];for(let w of Qg(i)){let m=Zg(nu(r,w));if(!m)continue;let g=m.providerOptions?.cursor;if(g?.requestId&&f.push(g.requestId),g?.modelProviderMessageId&&f.push(g.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let S of m.content){let T=S?.providerOptions?.cursor?.modelName;T&&(d=T)}l.push(...ty(m.content,e).map((S,T)=>({...S,id:S.id||`${w}:${T}`}))),u.push(...ny(m.content));let x=ey(m.content);if(!x)continue;let A=[...new Set(u)];u=[],c.push({id:w,text:x,tools:A.length?A:void 0,commands:l,model:d}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:f}}catch{return $n}finally{try{r.close()}catch{}}}function au(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(hr.statSync(t.store).mtimeMs)}catch{n=Date.now()}let r=t.createdAtMs;(!r||r>n)&&(r=n-e*1e3);let o=Math.max(0,n-r);return{end:n,tsAt:s=>Math.round(r+o*(s+1)/e)}}async function iu(t,e,{limit:n=80}={}){let r=wo(t,e);if(!r)return[];let{turns:o,model:s}=await ya(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=au(r,a.length);return a.map((c,l)=>({role:"assistant",text:c.text,tools:c.tools,ts:i(l),model:c.model||s,sourceId:c.id}))}async function cu(t,e){let n=wo(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await ya(n.store,t);return{chatId:n.id,requestIds:o,models:[...new Set(r.map(a=>a.model).filter(Boolean))],configuredModel:s,updatedAtMs:n.updatedAtMs||null}}async function lu(t,e,{limit:n=80}={}){let r=wo(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await ya(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=au(r,a.length||1),l=[];a.forEach((u,d)=>{for(let f of u.commands)l.push({...f,summary:ye(f.command,f.description),ts:c(d)})});for(let u of s)l.push({...u,summary:ye(u.command,u.description),ts:i+1});return l}async function uu(t,e){let n=wo(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await Kg.stat(n.store)).mtimeMs)}catch{return null}}import du from"node:fs";import ka from"node:fs/promises";import ba from"node:path";import ry from"node:os";var oy=process.env.BOSTRAT_CODEX_SESSIONS||ba.join(process.env.CODEX_HOME||ba.join(ry.homedir(),".codex"),"sessions");async function fu(t){try{return await ka.realpath(t)}catch{return t}}function sy(t=oy){let e=[],n=r=>{let o;try{o=du.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=ba.join(r,s.name);if(s.isDirectory())n(a);else if(s.isFile()&&s.name.startsWith("rollout-")&&s.name.endsWith(".jsonl")){let i=/-([0-9a-f-]{36})\.jsonl$/i.exec(s.name);try{e.push({id:i?i[1]:null,file:a,mtimeMs:du.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function ay(t,e=1048576){let n;try{n=await ka.open(t,"r")}catch{return""}try{let r=Buffer.alloc(Math.min(e,65536)),o="",s=0;for(;;){let{bytesRead:a}=await n.read(r,0,r.length,s);if(!a)break;o+=r.toString("utf8",0,a);let i=o.indexOf(`
31
+ `);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function iy(t){let e=await ay(t);if(!e)return null;let n;try{n=JSON.parse(e)}catch{return null}return n?.type==="session_meta"&&n?.payload?.cwd||null}async function Ge(t,e){if(!t&&!e)return null;let n=sy();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await fu(t);for(let o of n){let s=await iy(o.file);if(s&&(s===t||await fu(s)===r))return o}return null}var cy=new Set(["output_text","text"]),ly=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&cy.has(e.type)).map(e=>e.text).join(`
32
+ `).trim():"";async function pu(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await Ge(t,e);if(!o)return[];let s;try{s=await vt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,d=null;for(let f of s.split(`
33
+ `)){let w=f.trim();if(!w||w[0]!=="{")continue;let m;try{m=JSON.parse(w)}catch{continue}if((m.type==="turn_context"&&m.payload?.model||m.type==="session_meta"&&m.payload?.model)&&(i=m.payload.model),m.type==="event_msg"){let S=m.payload?.type;S==="task_started"?(l=!0,u=!1,d=m.payload?.turn_id||null):S==="task_complete"&&l&&(l=!1,!u&&m.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:Sa,sourceId:m.payload?.turn_id||d||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i}));continue}if(m.type!=="response_item")continue;let g=m.payload;if(!g)continue;if((g.type==="function_call"||g.type==="custom_tool_call")&&g.name){u=!0,xa.has(g.name)||c.push(g.name);continue}if(g.type!=="message"||g.role!=="assistant")continue;u=!0;let x=ly(g.content),A=c.length?c.slice():null;c=[],!(!x&&!A)&&a.push({role:"assistant",text:x,tools:A||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i})}if(i)for(let f of a)f.model||(f.model=i);return a.slice(-n)}var wr=new Set(["exec_command","shell","exec"]),xa=new Set(["wait","write_stdin"]),Sa="Codex ended the turn without a response \u2014 it hit an error the CLI shows only in the terminal (e.g. quota or billing). Check the Terminal tab.";function uy(t){if(typeof t?.cmd=="string"&&t.cmd.trim())return t.cmd.trim();let e=t?.command;return typeof e=="string"&&e.trim()?e.trim():Array.isArray(e)&&e.length?e.length===3&&/^(ba)?sh$/.test(e[0])&&e[1]==="-lc"?String(e[2]).trim():e.map(String).join(" ").trim():null}function dy(t){let e=t[0];if(e!=='"'&&e!=="'"&&e!=="`")return null;let n="";for(let r=1;r<t.length;r++){let o=t[r];if(o==="\\"){let s=t[++r];if(s===void 0)return null;if(s==="n")n+=`
34
+ `;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(t[r+1]==="{"){let a=t.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(t.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(t.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===e)return n;if(e==="`"&&o==="$"&&t[r+1]==="{")return null;n+=o}}return null}function fy(t){let e=String(t||""),n=[],r=/exec_command\s*\(/g;for(let s;s=r.exec(e);)n.push(s.index+s[0].length);let o=[];return n.forEach((s,a)=>{let i=e.slice(s,a+1<n.length?n[a+1]:e.length),c=/(?:^|[{,\s])["'`]?cmd["'`]?\s*:\s*/.exec(i);if(!c)return;let l=dy(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function mu(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await Ge(t,e);if(!o)return[];let s;try{s=await vt(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
35
+ `)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let d=[];if(u.type==="function_call"&&wr.has(u.name)){let w;try{w=JSON.parse(u.arguments||"{}")}catch{continue}let m=uy(w);m&&(d=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=fy(u.input));if(!d.length)continue;let f=u.call_id||u.id||null;d.forEach((w,m)=>{a.push({id:f?m?`${f}:${m}`:f:null,tool:u.name,command:w,description:"",summary:ye(w),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function hu(t,e){let n=await Ge(t,e);if(!n)return null;try{return Math.round((await ka.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var On=class{constructor(e,n=new Set){this.agent=e,this.cliTools=n}async readMessages(){return[]}async readCommands(){return[]}async readJobs(){return[]}async updatedAt(){return null}},va=class extends On{transcript(e){return!e?.cwd||!e?.sessionId?null:ne(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?Gs(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?Zl(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?tu(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await py.stat(n)).mtimeMs)}catch{return null}}},Ta=class extends On{async readMessages(e,n){return iu(e.cwd,e.sessionId,n)}async readCommands(e,n){return lu(e.cwd,e.sessionId,n)}async updatedAt(e){return uu(e.cwd,e.sessionId)}},Aa=class extends On{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return pu(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return mu(e.cwd,this.sessionId(e),n)}async updatedAt(e){return hu(e.cwd,this.sessionId(e))}},my=new On("unknown"),wu=new Map;function _a(t){return wu.set(t.agent,t),t}function gr(t){return wu.get(t)||my}_a(new va("claude",wa));_a(new Ta("cursor",ho));_a(new Aa("codex",wr));var Ea=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function gu(t,e={}){return(await gr(t.agent).readMessages(t,e)).map(Ea(t))}async function yu(t,e={}){return(await gr(t.agent).readCommands(t,e)).map(Ea(t))}async function bu(t,e={}){return(await gr(t.agent).readJobs(t,e)).map(Ea(t))}var hy=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,ku=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function wy(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
36
+ `);if(!ku.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(hy);if(u)o.unshift({n:Number(u[1]),label:u[2]}),s=!0;else{if(s&&r[l].trim()==="")continue;if(s){var a=r[l].trim();break}}}if(o.length<2)return null;let i=o.sort((l,u)=>l.n-u.n);if(i.some((l,u)=>l.n!==u+1))return null;let c=typeof a=="string"&&ku.test(a)?a:"The agent is asking for approval";return{provider:e,box:n,promptId:`${e}:${c}:${i.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Approval",multiSelect:!1,options:i.map(l=>({label:l.label,description:""}))}]}}var go=/(^|\n)\s*›/,xu=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function gy(t,e=""){let n=String(t);if(!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n)||go.test(n))return null;let r=n.replace(/\r/g,"").split(`
37
+ `),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(xu);if(!u)continue;s<0&&(s=l);let d="";for(let f=l+1;f<Math.min(l+3,r.length);f++){let w=r[f].trim();if(w){if(xu.test(r[f])||/^Press enter/.test(w))break;d=w;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:d})}let a=o.sort((l,u)=>l.n-u.n);if(a.length<2||a.some((l,u)=>l.n!==u+1)||!a.some(l=>/Sign in with ChatGPT/.test(l.label)))return null;let i=[];for(let l=0;l<s;l++){let u=r[l].trim();u&&!/Welcome to Codex/.test(u)&&i.push(u)}let c=i.join(" ")||"Codex isn't signed in on this box \u2014 choose a sign-in method";return{provider:"codex",box:e,multiSelect:!1,cursorIndex:Math.max(0,a.findIndex(l=>l.cursor)),answerMode:"keys",promptId:`codex:signin:${a.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Sign in",multiSelect:!1,options:a.map(l=>({label:l.label,description:l.desc}))}]}}var Su=/Enter this one-time code/,yy=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function by(t,e=""){let n=String(t);if(!Su.test(n)||go.test(n))return null;let r=n.replace(/\r/g,"").split(`
38
+ `),o=r.findIndex(i=>Su.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(yy);if(c){s=c[1];break}}if(!s)return null;let a=(n.match(/https:\/\/\S+/)||[])[0]||"https://auth.openai.com/codex/device";return{provider:"codex",box:e,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:`codex:device-code:${s}`,questions:[{question:`Codex is waiting for you to finish signing in: open ${a} and enter code ${s} (expires in 15 minutes). The code goes straight to OpenAI \u2014 bostrat never sees your login. This card clears once OpenAI confirms.`,header:"Sign in",multiSelect:!1,options:[{label:"Cancel sign-in",description:"Go back to the sign-in options"}]}]}}function ky(t,e=""){let n=String(t);return!/open the following link to authenticate/.test(n)||go.test(n)?null:{provider:"codex",box:e,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:"codex:signin-browser",questions:[{question:"Codex started its browser sign-in, which opens on the box itself and can't complete from another device. Go back and choose \u201CSign in with Device Code\u201D instead.",header:"Sign in",multiSelect:!1,options:[{label:"Go back to sign-in options",description:""}]}]}}function xy(t,e=""){let n=String(t);return!/Paste or type your API key/.test(n)||go.test(n)?null:{provider:"codex",box:e,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:"codex:signin-apikey",questions:[{question:"Codex is asking for an OpenAI API key. For safety a key can only be typed in the Terminal tab, never through chat. To use your ChatGPT subscription instead, go back and pick a sign-in option.",header:"Sign in",multiSelect:!1,options:[{label:"Go back to sign-in options",description:""}]}]}}function vu(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=by(e,n)||ky(e,n)||xy(e,n)||gy(e,n);if(r)return r}return wy(e,{provider:t,box:n})}function Tu(t,e){let n=Array.isArray(e?.[0])?e[0]:[],o=(Number.isInteger(n[0])&&n[0]>=0?n[0]:0)+1;return String(o)}var Eu=/to navigate\b.*\bEsc to cancel|Enter to select\b/,$a=/Chat about this/i,Ke=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Au=/^(type something|chat about this|submit)\b/i,Ve=/^[─—-]{6,}\s*$/,Oa=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function Je(t){let e=t.search(Oa);return(e>=0?t.slice(0,e):t).trim()}function Sy(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function vy(t){let e=String(t).trim();return!!(!e||Ve.test(t)||Oa.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function Ty(t,e=""){let n=[],r=/([☐☒])\s+([^☐☒←→✔]+)/g,o;for(;(o=r.exec(t))!==null;)n.push({label:o[2].trim(),answered:o[1]==="\u2612",current:!1});if(!n.length)return null;let s=Ay(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function Ay(t,e){let n=String(e).toLowerCase();if(n){let o=t.map((s,a)=>({i:a,label:s.label.toLowerCase()})).sort((s,a)=>a.label.length-s.label.length);for(let{i:s,label:a}of o){if(a.split(/\s+/).filter(l=>l.length>3).some(l=>n.includes(l)))return s;let c=a.slice(0,8).trim();if(c.length>=4&&n.includes(c))return s}}let r=t.findIndex(o=>!o.answered);return r>=0?r:t.length-1}function _u(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var Iu=/^\s*Switch model\?\s*$/m;function _y(t,e=""){let n=String(t);if(!Iu.test(n))return null;let r=n.replace(/\r/g,"").split(`
39
+ `),o=-1;for(let u=r.length-1;u>=0;u--)if(/^\s*Switch model\?\s*$/.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[];for(let u=o+1;u<r.length;u++){let d=r[u].match(Ke);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!Ve.test(r[u])&&a.push(f)}}if(s.length<2)return null;let i=s.map(u=>({label:Je(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=["Switch model?",...a].join(" ");return{provider:"claude",box:e,multiSelect:!1,cursorIndex:c,promptId:`claude:model-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Model",multiSelect:!1,options:i}]}}var Ru=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,Ey=/^\s*[─—▔-]{6,}\s*$/;function Iy(t,e=""){let n=String(t).replace(/\r/g,"").split(`
40
+ `),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Ru.test(n[r]))return null;let o=r-1;for(;o>=0&&n[o].trim()==="";)o--;let s=[];for(;o>=0;o--){let c=n[o].match(Ke);if(!c)break;s.unshift({cursor:!!c[1],n:Number(c[2]),label:c[4].trim()})}if(s.length<2||s.some((c,l)=>c.n!==l+1))return null;let a=[];for(let c=o;c>=0&&a.length<6&&!(Ey.test(n[c])||Ve.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:Je(c.label),description:""}));return{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,s.findIndex(c=>c.cursor)),promptId:`claude:confirm-gate:${i.map(c=>c.label).join("|")}`.slice(0,240),questions:[{question:a.join(" ")||"Claude Code is asking you to confirm",header:"Confirm",multiSelect:!1,options:i}]}}function Ry(t,e=""){let n=String(t);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
41
+ `),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(Ke);l&&o.push({cursor:!!l[1],label:l[4].trim()})}let s=o.filter(c=>/^(submit answers|cancel)$/i.test(c.label));if(s.length<2)return null;let a=s.map(c=>({label:c.label,description:""})),i=Math.max(0,s.findIndex(c=>c.cursor));return{provider:"claude",box:e,multiSelect:!1,cursorIndex:i,review:!0,promptId:"claude:review:submit-answers",questions:[{question:"Ready to submit your answers?",header:"Review",multiSelect:!1,options:a}]}}var Ia=/Resume from summary/i,$y=/^\s*This session is\b/;function Oy(t,e=""){let n=String(t);if(!Ia.test(n))return null;let r=n.replace(/\r/g,"").split(`
42
+ `),o=-1;for(let u=r.length-1;u>=0;u--)if($y.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[r[o].trim()];for(let u=o+1;u<r.length;u++){let d=r[u].match(Ke);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!Ve.test(r[u])&&a.push(f)}}if(s.length<2||!s.some(u=>Ia.test(u.label)))return null;let i=s.map(u=>({label:Je(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=a.join(" ");return{provider:"claude",box:e,multiSelect:!1,cursorIndex:c,promptId:`claude:resume-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Resume",multiSelect:!1,options:i}]}}var ze=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;function Ca(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(Ke);o&&e.push({line:r,cursor:!!o[1],n:Number(o[2]),label:o[4].trim()})}let n=e.sort((r,o)=>r.n-o.n);return n.length<2||n.some((r,o)=>r.n!==o+1)?null:n}var $u=/Choose the text style that looks best with your terminal/,Ou=/Let's get started\./;function Cy(t,e=""){let n=String(t);if(!$u.test(n)||!Ou.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
43
+ `),o=Ca(r);return o?{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"theme",promptId:`claude:setup:theme:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks: choose the text style that looks best with your terminal. You can change it later with /theme.",header:"Theme",multiSelect:!1,options:o.map(s=>({label:Je(s.label),description:""}))}]}:null}var Cu=/Select login method:/;function My(t,e=""){let n=String(t);if(!Cu.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
44
+ `),o=Ca(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:Je(i),description:c.join("\xB7").trim()}});return{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(a=>a.cursor)),setup:"login-method",promptId:`claude:setup:login-method:${s.map(a=>a.label).join("|")}`.slice(0,200),questions:[{question:"How do you want to sign in to Claude? A Claude subscription (Pro/Max/Team) uses your existing plan; a Console account bills API usage. Your login stays on this box \u2014 bostrat never sees it.",header:"Log in",multiSelect:!1,options:s}]}}var Ma=/Paste code here if prompted/,Py=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function Ny(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!Py.test(o)||Ma.test(t[r]))break;n+=o}return n}function By(t,e=""){let n=String(t);if(!Ma.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
45
+ `),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=Ny(r,s);break}return o?{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,answerMode:"text",setup:"oauth",url:o,inputLabel:"Paste your sign-in code",promptId:"claude:setup:oauth",questions:[{question:"Open the sign-in link, approve access, then paste the code you're shown back here. The code goes straight to the Claude CLI on your box \u2014 bostrat relays the keystrokes and never stores it.",header:"Log in",multiSelect:!1,options:[]}]}:null}var Ra=/OAuth error:/,Dy=/Press Enter to retry\./;function jy(t,e=""){let n=String(t);if(!Ra.test(n)||!Dy.test(n)||ze.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
46
+ `)].reverse().find(a=>Ra.test(a))||"OAuth error").trim();return{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,setup:"oauth-error",promptId:`claude:setup:oauth-error:${s}`.slice(0,200),questions:[{question:`${s} \u2014 retry to get a fresh sign-in link.`,header:"Log in",multiSelect:!1,options:[{label:"Retry sign-in",description:"Mints a new sign-in link and code"}]}]}}var Mu=/Quick safety check: Is this a project you created or one you trust/;function Ly(t,e=""){let n=String(t);if(!Mu.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
47
+ `),o=Ca(r);return o?{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"trust",promptId:`claude:setup:trust:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks whether it can read, edit, and run files in this workspace. This box's workspace is bostrat-managed scratch space, so trusting it is safe.",header:"Folder trust",multiSelect:!1,options:o.map(s=>({label:Je(s.label),description:""}))}]}:null}var Pu=/^\s*Press Enter to continue\s*$/;function Uy(t,e=""){let n=String(t);if(ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
48
+ `),o=-1;for(let i=r.length-1;i>=0;i--)if(r[i].trim()!==""){o=i;break}if(o<0||!Pu.test(r[o]))return null;let s=[];for(let i=o-1;i>=0&&s.length<3;i--){let c=r[i].trim();if(!c){if(s.length)break;continue}if(Ve.test(r[i])||Oa.test(c))break;s.unshift(c)}let a=s.join(" ")||"Claude Code is waiting for you to continue.";return{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,setup:"continue",promptId:`claude:setup:continue:${a}`.slice(0,200),questions:[{question:a,header:"Continue",multiSelect:!1,options:[{label:"Continue",description:""}]}]}}function Fy(t){let e=-1;for(let r=t.length-1;r>=0;r--)if($a.test(t[r])){e=r;break}if(e<0)return null;let n=-1;for(let r=e-1;r>=0;r--)if(/[☐☒]/.test(t[r])){n=r;break}return n<0?null:{headerLine:n,chatLine:e,optStart:n+1,optEnd:e}}function Hy(t,e=""){let n=String(t);if(!Eu.test(n)||!$a.test(n))return null;let r=n.replace(/\r/g,"").split(`
49
+ `),o=Fy(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let h=i;h<c;h++){let p=r[h].match(Ke);p&&(u<0&&(u=h),l.push({line:h,cursor:!!p[1],checked:p[3]!=null&&p[3]!==" ",hasCheckbox:p[3]!=null,label:p[4].trim(),desc:""}))}if(!l.length)return null;for(let h of l)for(let p=h.line+1;p<Math.min(r.length,h.line+4)&&!(Ke.test(r[p])||Ve.test(r[p]));p++){let b=r[p].trim();if(!(!b||vy(r[p])||Au.test(b))){h.desc=Sy(b);break}}let d=l.filter(h=>!Au.test(h.label));if(!d.length)return null;let f=d.some(h=>h.hasCheckbox),w=Math.max(0,d.findIndex(h=>h.cursor)),m=s>=0?s+1:0,g=[];for(let h=m;h<u;h++){let p=r[h].trim();p&&!Ve.test(r[h])&&!/[☐☒]/.test(r[h])&&g.push(p)}let x=g.join(" ")||(s>=0?_u(r[s]):"")||"The agent is asking a question",A=s>=0?Ty(r[s],x):null,S=A?.currentTabLabel||(s>=0?_u(r[s]):""),T=d.map(h=>({label:Je(h.label),description:h.desc})),E=T.map(h=>h.label).join("|"),M=`claude:${A?`q${A.questionIndex+1}/${A.questionTotal}`:"q1/1"}:${S}:${x}:${E}`.slice(0,240),I=A?`claude:ask:${A.tabs.map(h=>h.label).join("|")}`.slice(0,240):M;return{provider:"claude",box:e,multiSelect:f,cursorIndex:w,promptId:M,groupId:I,...A?{questionIndex:A.questionIndex,questionTotal:A.questionTotal,currentTabLabel:A.currentTabLabel,tabs:A.tabs}:{},questions:[{question:x,header:S,multiSelect:f,options:T}]}}function Cn(t,e){let n=String(e||"");return n?t==="claude"?Eu.test(n)&&$a.test(n)||Iu.test(n)||Ia.test(n)||Ru.test(n.trimEnd().split(`
50
+ `).pop()||"")?!0:ze.test(n)?!1:$u.test(n)&&Ou.test(n)||Cu.test(n)||Ma.test(n)||Ra.test(n)||Mu.test(n)||Pu.test(n.trimEnd().split(`
51
+ `).pop()||""):t==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate/i.test(n)?!0:t==="cursor"||t==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function Xe(t,e){return!t||!e?null:t.agent==="claude"?Ry(e,t.name)||Hy(e,t.name)||_y(e,t.name)||Oy(e,t.name)||jy(e,t.name)||By(e,t.name)||My(e,t.name)||Cy(e,t.name)||Ly(e,t.name)||Uy(e,t.name)||Iy(e,t.name):t.agent==="cursor"||t.agent==="codex"?vu(t.agent,e,t.name):null}async function Pa(t){if(!t)return null;let e=await me(t.name).catch(()=>"");return Xe(t,e)}function Nu(t,e){let n=Array.isArray(e?.[0])?e[0].filter(a=>Number.isInteger(a)&&a>=0):[],r=Number.isInteger(t?.cursorIndex)?t.cursorIndex:0,o=(a,i)=>{let c=i-a;return Array.from({length:Math.abs(c)},()=>c>0?"Down":"Up")},s=[];if(t?.multiSelect){let a=r;for(let i of[...n].sort((c,l)=>c-l))s.push(...o(a,i),"Enter"),a=i;s.push("Tab","Enter")}else{let a=n.length?n[0]:0;s.push(...o(r,a),"Enter")}return s}function Bu(t,e){let n=Number.isInteger(t?.questionIndex)?t.questionIndex:0,o=(Number.isInteger(e)?e:n)-n;if(!o)return[];let s=o>0?"Tab":"BTab";return Array.from({length:Math.abs(o)},()=>s)}function Mn(t){return typeof t=="string"?t.trim():""}var Du=/^\s*─{8,}\s*$/,Wy=/^❯[  ]?(.*)$/;function Na(t,e){if(t!=="claude")return null;let n=String(e).replace(/\r/g,"").split(`
52
+ `).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(Du.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Du.test(n[c])){o=c;break}if(o<0)return null;let s=n.slice(o+1,r);if(!s.length)return null;let a=s[0].match(Wy);if(!a)return null;let i=[a[1].trim(),...s.slice(1).map(c=>c.trim())].filter(Boolean);return i.length?i.join(" "):null}function Ye(t){return String(t||"").replace(/\s+/g," ").trim()}function ju(t,e){let n=Ye(t),r=Ye(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}import{execFile as qy}from"node:child_process";import{promisify as Gy}from"node:util";var Da=Gy(qy),Lu=64*1024*1024;async function mt(t,e,{input:n}={}){let r=Da("git",["-C",t,...e],{maxBuffer:Lu});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function ja(t){if(!t)return!1;try{let{stdout:e}=await mt(t,["rev-parse","--is-inside-work-tree"]);return e.trim()==="true"}catch{return!1}}async function Uu(t){let{stdout:e}=await mt(t,["rev-parse","--show-toplevel"]);return e.trim()}async function Ky(t){try{return await mt(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Fu(t,e="bostrat: subtask snapshot"){let{stdout:n}=await mt(t,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Da("git",["-C",t,...a],{maxBuffer:Lu,env:o,...i});try{let a=await Ky(t);a&&await s(["read-tree","HEAD"]),await s(["add","-A"]);let{stdout:i}=await s(["write-tree"]),c=i.trim(),l=a?["commit-tree",c,"-p","HEAD","-m",e]:["commit-tree",c,"-m",e],{stdout:u}=await s(l);return u.trim()}finally{try{await Da("rm",["-f",r])}catch{}}}async function Hu(t,e){let{stdout:n}=await mt(t,["status","--porcelain"]);return n.trim()?(await mt(t,["add","-A"]),await mt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",e]),!0):!1}async function yo(t,e,n,r){return await mt(t,["worktree","add","--no-track","-b",n,e,r]),e}async function La(t){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:e}=await mt(t,["symbolic-ref","--short","refs/remotes/origin/HEAD"]);if(e.trim())return e.trim()}catch{}for(let[e,n]of[["refs/remotes/origin/main","origin/main"],["refs/remotes/origin/master","origin/master"],["refs/heads/main","main"],["refs/heads/master","master"]])try{return await mt(t,["rev-parse","--verify","--quiet",e]),n}catch{}return"HEAD"}async function Wu(t){await mt(t,["init","-q"]),await mt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function Ze(t,e){try{await mt(t,["worktree","remove","--force",e])}catch{try{await mt(t,["worktree","prune"])}catch{}}}function Vy(t){let e=/(\d+) files? changed/.exec(t),n=/(\d+) insertions?\(\+\)/.exec(t),r=/(\d+) deletions?\(-\)/.exec(t);return{files:e?Number(e[1]):0,insertions:n?Number(n[1]):0,deletions:r?Number(r[1]):0}}function Ua(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function qu(t,e,n,r){let{stdout:o}=await mt(t,Ua(["diff","--shortstat",e,n],r));return Vy(o)}async function Gu(t,e,n,r){let{stdout:o}=await mt(t,Ua(["diff","--name-only",e,n],r));return o.split(`
53
+ `).map(s=>s.trim()).filter(Boolean)}async function Ku(t,e,n,r){let{stdout:o}=await mt(t,Ua(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Ba(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Ba(t,o,["--3way","--whitespace=nowarn"]),a=await Jy(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Ba(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Ba(t,e,n){try{return await mt(t,["apply",...n,"-"],{input:e}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function Jy(t){try{let{stdout:e}=await mt(t,["diff","--name-only","--diff-filter=U"]);return e.split(`
54
+ `).map(n=>n.trim()).filter(Boolean)}catch{return[]}}import zy from"node:fs/promises";import Xy from"node:os";import Vu from"node:path";var bo=t=>new Promise(e=>setTimeout(e,t)),Yy=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,Zy=300,Yu=[{alias:"default",label:"Default"},{alias:"opus",label:"Opus 4.8"},{alias:"sonnet",label:"Sonnet 4.6"},{alias:"haiku",label:"Haiku 4.5"},{alias:"fable",label:"Fable 5"},{alias:"opusplan",label:"Opus Plan"}],Qy=new Set([...Yu.map(t=>t.alias),"best","opus[1m]","sonnet[1m]","claude-opus-4-8","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),tb=[{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.4",label:"GPT-5.4"},{alias:"gpt-5.4-mini",label:"GPT-5.4 Mini"},{alias:"gpt-5.3-codex",label:"Codex 5.3"},{alias:"gpt-5.2",label:"GPT-5.2"}],Zu=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),Qu=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-4-8",label:"Opus 4.8"},{alias:"claude-fable-5",label:"Fable 5"},{alias:"claude-sonnet-5",label:"Sonnet 5"},{alias:"claude-sonnet-4-6",label:"Sonnet 4.6"},{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}];function td(t){return t==="codex"?tb:t==="cursor"?Qu:Yu}function Ju(t){let e=String(t||"").split(`
55
+ `),n=e.findIndex(o=>/Select Model and Effort/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<e.length;o++){let s=e[o].match(/^\s*(›\s*)?(\d+)\.\s+(\S+?)(?:\s+\((current|default)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),id:s[3],current:s[4]==="current",active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(e[o]))break}return r.length?r:null}function zu(t){let e=String(t||"").split(`
56
+ `),n=e.findIndex(o=>/Select Reasoning Level/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<e.length;o++){let s=e[o].match(/^\s*(›\s*)?(\d+)\.\s+([A-Za-z ]+?)(?:\s+\((default|current)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),label:s[3].trim(),isDefault:!!s[4],active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(e[o]))break}return r.length?r:null}function eb(t){let e=String(t||"").split(`
57
+ `);for(let n=e.length-1;n>=0;n--){let r=e[n].match(/Model changed to\s+(\S+)(?:\s+(\S+))?\s*$/i);if(r)return{model:r[1],effort:r[2]||null}}return null}function Xu(t){let e=String(t||"").split(`
58
+ `),n=e.findIndex(s=>/Available models|Models matching/i.test(s));if(n<0)return null;let r=/Models matching/i.test(e[n]),o=[];for(let s=n+1;s<e.length;s++){let a=e[s];if(/^\s*Filter:/i.test(a)||!a.trim())continue;if(/to filter|Enter to select|^\s*\d+-\d+ of \d+/i.test(a))break;let i=a.match(/^\s*(→\s+)?(.+?)\s*$/);if(!i)continue;let c=i[2].replace(/\s*\(Tab to modify\)\s*$/i,""),l=null,u=c.split(/\s{2,}/);u.length>1&&(c=u[0],l=u.slice(1).join(" ").trim()||null),c&&o.push({label:c.trim(),params:l,active:!!i[1]})}return o.length?{filtered:r,rows:o}:null}function ed(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim();if(t==="codex"){let[r,o]=n.split(/[\s:]+/);if(o&&!Zu.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=Qu.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!Qy.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function yr(t,e,{timeoutMs:n=Yy}={}){let r=Date.now()+n;for(;;){let o=await pt(t,{lines:80}).catch(()=>""),s=e(o);if(s)return s;if(Date.now()>r)return null;await bo(Zy)}}async function nb(t,e){return await yt(t,`/model ${e.model}`,re("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function rb(t,e){await eo(t),await yt(t,"/model",{enter:!1}),await bo(250),await gt(t,["Enter"],{kind:"model-switch"});let n=await yr(t,Ju);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await gt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${e.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=eb(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await yt(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await yr(t,i=>zu(i)||!Ju(i)&&o(i)&&{done:o(i)});if(!s)throw await gt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?Zu.get(e.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await gt(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await yt(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await yr(t,i=>!zu(i)&&o(i));if(!a)throw new Error("codex never confirmed the model change");return{verified:!0,model:a.model,effort:a.effort}}async function ob(){let t=Vu.join(process.env.CURSOR_CONFIG_DIR||Vu.join(Xy.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await zy.readFile(t,"utf8"))?.model||null}catch{return null}}async function sb(t,e){await eo(t),await ro(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await yr(t,Xu),r=c=>String(c||"").toLowerCase(),o=c=>r(c.label).startsWith(r(e.filter)),s=n?.rows.find(c=>c.active)||null;if(n&&!(s&&o(s)))for(let c=0;c<12;c++){let l=await yr(t,Xu,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await gt(t,["Down"],{kind:"model-switch"}),await bo(250)}if(!n||!s||!o(s)){await gt(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await gt(t,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${e.filter}"`:"cursor model picker didn't open")}await gt(t,["Enter"],{kind:"model-switch"}),await bo(800);let a=await ob();return{verified:!!a&&(a.modelId===e.model||a.displayModelId===e.model||r(a.displayName).startsWith(r(e.filter))),model:a?.modelId||null,note:"cursor applies this as the global default for new sessions too"}}async function nd(t,e,n){return t==="codex"?rb(e,n):t==="cursor"?sb(e,n):nb(e,n)}import Fa from"node:fs/promises";import Pn from"node:path";import ab from"node:os";import{EventEmitter as ib}from"node:events";import{randomUUID as cb}from"node:crypto";var lb=new Set(["session.ready","turn.user","turn.agent","turn.retracted","tool.called","tool.result","prompt.raised","prompt.answered","context.usage","cost.recorded","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","pr.state.changed","pr.offered","pr.offer.answered","box.state.changed","view.state.changed","view.spinup.failed","mock.state.changed","runbook.state.changed","ticket.linked","ticket.sync.failed","ticket.move.offered","ticket.move.answered","ticket.state.changed","ticket.commented","ticket.flow.changed","ticket.create.offered","ticket.create.answered","ticket.created","provider.connected","stall.detected","error"]),ub=new Set(["user","agent","orchestrator","system"]),db=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Pt=new ib;Pt.setMaxListeners(0);var Ha=process.env.BOSTRAT_STATE_DIR||Pn.join(process.env.HOME||ab.tmpdir(),".bostrat");function xo(t){return t?.cwd?Pn.join(t.cwd,".bostrat","events"):Pn.join(Ha,"events")}var rd=(t,e)=>Pn.join(t,`${e}.ndjson`),ko=new Map;async function fb(t,e){if(!ko.has(e)){let o=(await B(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);ko.set(e,o)}let n=ko.get(e)+1;return ko.set(e,n),n}function pb(t,e){let n=Array.isArray(t?.epochs)?t.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(e))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=e)return r}return 0}}async function R(t,e){let n=typeof t=="string"?t:t?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!e||!lb.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!ub.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!db.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?Pn.join(Ha,"events"):xo(t),o=e.epoch;if(o==null&&typeof t=="object"&&e.boxId&&Array.isArray(t?.boxes)){let c=t.boxes.find(l=>l.name===e.boxId);o=pb(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await fb(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||cb(),trackId:n,boxId:e.boxId??null,...o!=null?{epoch:o}:{},...a?{userId:a}:{},seq:s,ts:e.ts??Date.now(),type:e.type,actor:e.actor??"system",provider:e.provider??null,source:e.source??"orchestrator",payload:e.payload??{},...e.causationId?{causationId:e.causationId}:{},...e.correlationId?{correlationId:e.correlationId}:{}};return await Fa.mkdir(r,{recursive:!0}),await Fa.appendFile(rd(r,n),JSON.stringify(i)+`
59
+ `),Pt.emit("event",i),Pt.emit(`track:${n}`,i),i}async function B(t,e){let n,r;typeof t=="string"&&e?(n=t,r=e):(r=typeof t=="string"?t:t?.id,n=typeof t=="string"?Pn.join(Ha,"events"):xo(t));let o;try{o=await Fa.readFile(rd(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
60
+ `))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function od(t,e){let n=`track:${t}`;return Pt.on(n,e),()=>Pt.off(n,e)}var Ot=t=>t||null;function vo(t){let e=t||{},n=mb(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function mb(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:Ot(t.agent),payload:{text:t.text,boxRole:t.boxRole,...t.kind?{kind:t.kind}:{},...t.attachments?.length?{attachments:t.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:t.box,provider:Ot(t.agent),payload:{saidTs:t.saidTs,text:t.text,boxRole:t.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Ot(t.agent),payload:{text:t.prompt,kind:t.kind,boxRole:t.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:t.to,provider:Ot(t.toAgent),payload:{from:t.from,fromAgent:t.fromAgent,to:t.to,toAgent:t.toAgent,reason:t.reason,handoffStartedAt:t.handoffStartedAt,fromSessionId:t.fromSessionId,toSessionId:t.toSessionId,epoch:t.epoch,singleBox:t.singleBox,forced:t.forced,recovered:t.recovered}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Ot(t.toAgent),payload:{box:t.box,fromAgent:t.fromAgent,toAgent:t.toAgent,reason:t.reason,error:t.error,rolledBack:t.rolledBack,handoffStartedAt:t.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:t.to,provider:Ot(t.toAgent),payload:{task:t.task,subtaskId:t.subtaskId,reason:t.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:t.box||t.from,provider:Ot(t.agent),payload:{...br(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:Ot(t.agent||t.toAgent),payload:{kind:"subtask-failed",...br(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Ot(t.agent),payload:{change:"subtask-resumed",...br(t)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"model",model:t.to,from:t.from??null,verified:t.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:"codex",payload:{change:"cli-update",from:t.from??null,to:t.to??null,status:t.status,...t.error?{error:t.error}:{}}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"opened",...br(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...br(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Ot(t.agent),payload:{change:"rehydrated"}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:Ot(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var So=Promise.resolve(),qa=t=>(So=So.then(t).catch(()=>{}),So),ad=()=>So;function id(t,e){let n=vo(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;qa(()=>R(r,n))}var sd=new Set;async function cd(t){if(!(!t?.id||sd.has(t.id))){sd.add(t.id);try{let e=await B(t),n=new Set(e.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of t.events||[]){let o=vo(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await R(t,o)}}catch{}}}function br(t){let{type:e,at:n,...r}=t;return r}var Wa=new Map;function ld(t,e,n){!n?.promptId||Wa.get(t.id)===n.promptId||(Wa.set(t.id,n.promptId),qa(()=>R(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:Ot(n.provider||e?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function ud(t,e,n,r={}){Wa.delete(t.id),qa(()=>R(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:Ot(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as Wb}from"node:crypto";var dd=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional|amend(?:ed)?|squash|merge|follow[\s-]?up|clean[\s-]?up)\s)commit(?!s\b)(?!\s+(?:history|log|message|hash|sha)s?\b)/.source,fd=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional)\s)push(?!\s+notifications?\b)(?:\s+(?:it|this|that|them|everything|the\s+\w+))?\s*(?:up)?/.source,pd=/open(?:\s+up)?\s+(?:a|the|that)\s+(?:draft\s+)?(?:pr|pull\s+request)|creat(?:e|ing)\s+(?:a|the)\s+(?:draft\s+)?(?:pr|pull\s+request)|rais(?:e|ing)\s+(?:a|the)\s+(?:pr|pull\s+request)|put\s+up\s+(?:a|the)\s+(?:pr|pull\s+request)|send\s+(?:up\s+)?(?:a|the)\s+(?:pr|pull\s+request)/.source,hb=new RegExp(`\\b(?:${dd}|${fd}|${pd})\\b`,"i"),wb=/\b(?:want\s+me\s+to|would\s+you\s+like(?:\s+me)?(?:\s+to)?|should\s+i|shall\s+i|do\s+you\s+want(?:\s+me)?(?:\s+to)?|happy\s+to|ready\s+for\s+me\s+to|let\s+me\s+know\s+(?:if|when|whether|and)|say\s+the\s+word|give\s+me\s+the\s+(?:word|go[\s-]?ahead)|if\s+you(?:'d|\s+would)?\s+(?:like|want|prefer)|just\s+say\s+(?:so|yes|go))\b/i,gb=/^(?:commit|push|open|create|raise|ready|ok(?:ay)?\s+to|good\s+to)\b/i,yb=90;function bb(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function md(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of bb(n).reverse()){if(!hb.test(r))continue;let o=r.endsWith("?")&&r.length<=yb&&gb.test(r);if(wb.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var hd=["commit","push","pr"],kb=new RegExp(`\\b(?:${dd})\\b`,"i"),xb=new RegExp(`\\b(?:${fd})\\b`,"i"),Sb=new RegExp(`\\b(?:${pd})\\b`,"i"),vb=/\b(?:to|into|onto|on|updat(?:e|es|ing))\s+(?:the|that|this)\s+(?:open\s+|existing\s+|current\s+|same\s+)?(?:draft\s+)?(?:pr|pull\s+request)\b/i;function wd(t){let e=String(t||""),n=[];kb.test(e)&&n.push("commit"),xb.test(e)&&n.push("push");let r=vb.test(e);!r&&Sb.test(e)&&n.push("pr");let o=null;if(r){let s=e.match(/(?:pr|pull\s+request)\s*\(?\s*#(\d+)\)?/i)||e.match(/#(\d+)/);o={number:s?Number(s[1]):null}}return{offered:n.length?n:null,toPr:o}}function To(t,{branch:e=null,base:n=null,toPr:r=null}={}){Array.isArray(t)||(t=t==="push"?["commit","push"]:["commit","push","pr"]);let o=new Set(t),s=e?` on this branch (${e})`:"",a=[];if(o.has("commit")&&a.push(`commit the work${s} with a clear commit message`),o.has("push")||o.has("pr")){let l=r&&!o.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"";a.push(o.has("commit")?`push it to the remote${l}`:`push this branch${s} to the remote${l}`)}o.has("pr")&&a.push(`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let i=[];o.has("commit")||i.push("don't create any new commits \u2014 ship what's already committed"),!o.has("push")&&!o.has("pr")&&i.push("don't push"),o.has("pr")||i.push("don't open a pull request");let c=`Yes \u2014 go ahead: ${a.join(", then ")}.`;return i.length&&(c+=` ${i.join(", and ").replace(/^./,l=>l.toUpperCase())}.`),o.has("pr")&&(c+=" If a PR for this branch is already open, just push the update instead."),c}var Tb=3e4;function gd(t,e=Date.now()){let n=[],r=null;for(let o of t||[]){let s=o.payload||{},a=s.ts??o.ts;if(o.type==="pr.offer.answered"&&s.action==="pr"){r={box:o.boxId,from:a,lastActivity:a};continue}!r||a==null||a<r.from||(o.type==="pr.offer.answered"?r=null:o.type==="pr.state.changed"&&s.change==="opened"?(n.push({box:r.box,from:r.from,to:a,settled:!0}),r=null):o.type==="turn.user"&&o.source!=="hook"&&!s.kind&&String(s.text||"").trim()?r=null:o.boxId===r.box&&/^(?:turn|tool)\./.test(o.type)&&(r.lastActivity=Math.max(r.lastActivity,a)))}return r&&e-r.lastActivity<=Tb&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function yd(t,e){let n=new Set;for(let r of e||[]){if(!r.settled)continue;let o=null;for(let s of t||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}import{execFile as Eb}from"node:child_process";import{promisify as Ib}from"node:util";import Ao from"node:fs/promises";import bd from"node:path";import Ab from"node:os";var kd=process.env.BOSTRAT_STATE_DIR||bd.join(process.env.HOME||Ab.tmpdir(),".bostrat"),Ka=bd.join(kd,"pr-links.json"),bt=(t,e)=>`${t}#${Number(e)}`,_o=null,Ga=null,D=Promise.resolve();async function xt(){return _o||(Ga??=Ao.readFile(Ka,"utf8").then(t=>JSON.parse(t)?.links??{}).catch(()=>({})).then(t=>_o=t).finally(()=>Ga=null),Ga)}async function Z(){let t=JSON.stringify({links:_o},null,2),e=`${Ka}.${process.pid}.tmp`;await Ao.mkdir(kd,{recursive:!0}),await Ao.writeFile(e,t),await Ao.rename(e,Ka)}var _b=new Set(["api","observed","reconciled"]);async function Nt({repo:t,number:e,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!t||!Number.isInteger(Number(e))||!n)return{link:null,created:!1};let i=await xt(),c=bt(t,e);return i[c]?{link:i[c],created:!1}:(i[c]={repo:t,number:Number(e),trackId:n,url:r,headRef:o,title:s,source:a,announced:_b.has(a),firstSeen:Date.now()},D=D.then(Z,Z),await D.catch(()=>{}),{link:i[c],created:!0})}async function kr(t,e){if(!t||e==null)return null;let n=(await xt())[bt(t,e)];return!n||n.announced?null:(n.announced=!0,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function Tt(t,e){return!t||e==null?null:(await xt())[bt(t,e)]??null}async function Qe(t,e,n){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function xd(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await xt())[bt(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},D=D.then(Z,Z),await D.catch(()=>{}),o):null}async function Sd(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await xt())[bt(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},D=D.then(Z,Z),await D.catch(()=>{}),o):null}async function Eo(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,D=D.then(Z,Z),await D.catch(()=>{}),r)}async function Io(t,e){if(!t||e==null)return null;let n=(await xt())[bt(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function Ro(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,D=D.then(Z,Z),await D.catch(()=>{}),r)}async function $o(t,e){if(!t||e==null)return null;let n=(await xt())[bt(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function vd(t,e,n){if(!t||e==null||!n)return null;let r=(await xt())[bt(t,e)];return r?(r.lastHeadSha=n,D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function Oo(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function Va(t,e,n=!0){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function Co(t,e){if(!t||e==null)return null;let n=(await xt())[bt(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function Td(){return Object.values(await xt()).filter(t=>t.state!=="merged"&&t.state!=="closed")}async function Ja(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await pt(t,{lines:Rt}).catch(()=>"");if(!ur(e,r))return vl(e,r)?{working:!0,prompted:!1}:n;if(Cn(e,r)){let o=await me(t).catch(()=>r);if(Xe({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function Mo(t,e){return(await Ja(t,e)).working}var Ad=Ib(Eb),_d=[4e3,15e3,45e3],Rb=2500,$b=5*6e4;async function za(t,e,{midTurn:n=Mo,pollMs:r=Rb,capMs:o=$b}={}){let s=Date.now()+o;for(;await n(t,e).catch(()=>!1);){if(Date.now()>=s)return!1;await new Promise(a=>setTimeout(a,r))}return!0}var Ob=6e4,Cb=9e4,Mb=2e3,Pb=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,Nb=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,Bb=/\s--web\b/,Ed=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/;function Xa(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Id(t){let e=String(t||""),n=e.match(Pb);if(!n)return null;let r=Xa(e,n.index);return Bb.test(r)?null:{repo:r.match(Ed)?.[1]??null}}var Db=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function Rd(t){let e=String(t||""),n=e.match(Nb);if(!n)return null;let r=Xa(e,n.index),o=Xa(e,n.index+n[0].length),s=null,a=o.trim().split(/\s+/).filter(Boolean);for(let i=0;i<a.length;i++){let c=a[i];if(c.startsWith("-")){Db.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(Ed)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var jb="number,url,title,headRefName,createdAt";async function Lb(t,e){let n=async s=>{let{stdout:a}=await Ad("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",jb],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...s}});return JSON.parse(a||"[]")},r=await Gt({slug:t}),o=await n(r);return!o.length&&r.GH_TOKEN&&(o=await n({}).catch(()=>[])),o.filter(s=>Date.parse(s.createdAt)>=e-Ob).sort((s,a)=>Date.parse(a.createdAt)-Date.parse(s.createdAt))}async function Ub(t,e){if(e)return[e];let n=[],r=await Y(t.workspace?.base||t.cwd).catch(()=>null);r&&n.push(r);for(let o of await oe().catch(()=>[])){let s=await Y(o.path).catch(()=>null);s&&!n.includes(s)&&n.push(s)}return n}async function Fb(t,e,n){let r=t.workspace?.branch,o=[];for(let s of await Ub(t,e)){let a;try{a=await Lb(s,n)}catch{continue}for(let i of a){let c=await Tt(s,i.number);if(!(c&&(c.trackId!==t.id||c.announced))){if(r&&i.headRefName===r)return{slug:s,pr:i};o.push({slug:s,pr:i})}}}return o.length?(o.sort((s,a)=>Date.parse(a.pr.createdAt)-Date.parse(s.pr.createdAt)),o[0]):null}var ke=new Set;function $d(t,e,{delays:n=_d,turnEnd:r}={}){let o=Id(e.command);if(!o)return null;let s=!!Rd(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(ke.has(a))return null;ke.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(g=>setTimeout(g,l));let u=await Fb(t,o.repo,i).catch(()=>null);if(!u)continue;let{slug:d,pr:f}=u,{link:w,created:m}=await Nt({repo:d,number:f.number,trackId:t.id,url:f.url,headRef:f.headRefName,title:f.title,source:"observed"});return!m&&!(w?.trackId===t.id&&await kr(d,f.number))?null:(await za(e.box,e.agent,r),await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"opened",number:f.number,url:f.url,title:f.title,branch:f.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),f)}return null}finally{ke.delete(a)}})();return c.catch(()=>ke.delete(a)),c}var Hb="number,url,title,headRefName,state";function Od(t,e,{delays:n=_d,turnEnd:r,openedWait:o}={}){let s=Rd(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(ke.has(a))return null;ke.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);if(u&&([,c,l]=u),c||=await Y(t.workspace?.base||t.cwd).catch(()=>null),l||=t.workspace?.branch,!c||!l)return null;let d=async(w,m)=>{let g=await Tt(c,w.number);return g&&g.trackId!==t.id?null:(g||await Nt({repo:c,number:w.number,trackId:t.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await Oo(c,w.number),m&&((await B(t).catch(()=>[])).some(S=>S.type==="pr.state.changed"&&(S.payload?.change==="auto-armed"||S.payload?.change==="merged")&&S.payload?.number===w.number)||(await za(e.box,e.agent,r),await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:w.number,url:w.url,title:w.title,branch:w.headRefName,repo:c}}))),w)},f=null;for(let w of n){await new Promise(T=>setTimeout(T,w));let m;try{let{stdout:T}=await Ad("gh",["pr","view",String(l),"--repo",c,"--json",Hb],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Gt({slug:c})}});m=JSON.parse(T||"null")}catch{continue}if(!m?.number)continue;if(m.state==="CLOSED")return null;if(m.state!=="MERGED"){if(f=m,s.auto)return d(m,!0);continue}let g=await Tt(c,m.number);if(g&&g.trackId!==t.id)return null;g||await Nt({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await za(e.box,e.agent,r);let x=await B(t).catch(()=>[]);if(x.some(T=>T.type==="pr.state.changed"&&T.payload?.change==="merged"&&T.payload?.number===m.number))return null;let A=T=>T.some(E=>E.type==="pr.state.changed"&&E.payload?.change==="opened"&&E.payload?.number===m.number),S=0;if(!A(x)){if(Id(e.command)&&await kr(c,m.number)){let T=Date.now();S=T+1,await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:T,change:"opened",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c,selfMerge:!0}})}else if((await Tt(c,m.number))?.announced){let T=o?.capMs??Cb,E=o?.pollMs??Mb,J=Date.now()+T;for(;Date.now()<J&&(await new Promise(M=>setTimeout(M,E)),!A(await B(t).catch(()=>[]))););}}return await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Math.max(Date.now(),S),change:"merged",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}),await tn(c,m.number).catch(()=>{}),await Qe(c,m.number,"merged"),m}return f?d(f,!1):null}finally{ke.delete(a)}})();return i.catch(()=>ke.delete(a)),i}function Za(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function Cd(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function Md(t){return`job|${t.box}|id:${t.id}`}var Ya=new Map,Po=new Map;async function Qa(t){return Ya.has(t.id)?Ya.get(t.id):(Po.has(t.id)||Po.set(t.id,B(t).then(e=>{let n={keys:new Set,cmdN:0};for(let r of e)if((r.type==="turn.user"||r.type==="turn.agent")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(Za({role:o,sourceId:r.payload.sourceId})),n.keys.add(Za({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(Cd({id:o.id,box:r.boxId,ts:o.ts,command:o.command})),Number(o.n)>n.cmdN&&(n.cmdN=Number(o.n))}else r.type==="job.started"&&r.payload?.id&&n.keys.add(Md({id:r.payload.id,box:r.boxId}));return Ya.set(t.id,n),n}).finally(()=>Po.delete(t.id))),Po.get(t.id))}async function Pd(t,e){try{let n=(await Qa(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=Za(r);if(!n.has(o)&&(n.add(o),await R(t,{type:r.role==="user"?"turn.user":"turn.agent",actor:r.role==="user"?"user":"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:r.text,ts:r.ts,model:r.model||null,tools:r.tools||void 0,boxRole:r.boxRole,sourceId:r.sourceId||void 0,note:r.note||void 0,images:r.images?.length?r.images:void 0}}),r.role==="assistant"&&r.box===t.leadBox&&t.workspace?.branch)){let s=md(r.text);if(s){let{offered:a,toPr:i}=wd(s);await R(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:Wb(),ts:r.ts,excerpt:s,branch:t.workspace.branch,base:t.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,offered:a||void 0,toPr:i||void 0}})}}}}catch{}}async function Nd(t,e){try{let n=await Qa(t),r=[...e||[]].filter(o=>o&&String(o.command||"").trim()).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Cd(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await R(t,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),$d(t,o),Od(t,o))}}catch{}}async function Bd(t,e){try{let n=await Qa(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Md(o);n.keys.has(s)||(n.keys.add(s),await R(t,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var qb=/^Base directory for this skill:\s*(\S+)/,Gb=/^Run the "([^"]+)" workflow\./,Kb=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,Vb=/^<local-command-(?:stdout|caveat)>/i,Jb=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,zb=/^<task-notification>/i,Xb=/<summary>([\s\S]*?)<\/summary>/i,Yb=/<task-id>([\s\S]*?)<\/task-id>/i,Zb=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,Qb=/<status>([\s\S]*?)<\/status>/i,tk=/<result>([\s\S]*?)(?:<\/result>|$)/i;var ti=new Set(["model","clear","compact","login"]);function Dd(t){return ti.has(String(t||"").replace(/^\//,"").toLowerCase())}var ek=/^\/([a-z0-9:_-]+)$/i;function No(t){let e=String(t||"").trim();if(!e)return null;let n=e.match(qb);if(n)return{kind:"skill",name:n[1].split("/").filter(Boolean).pop()||"skill"};let r=e.match(Gb);if(r)return{kind:"skill",name:r[1]};let o=e.match(Kb);if(o){let a=o[1].toLowerCase();return ti.has(a)?{kind:"suppress"}:{kind:"command",command:`/${a}`}}let s=e.match(ek);if(s&&ti.has(s[1].toLowerCase()))return{kind:"suppress"};if(Vb.test(e))return{kind:"suppress"};if(Jb.test(e))return{kind:"suppress"};if(zb.test(e)){let a=c=>nk(e.match(c)?.[1].trim()||"")||null,i=a(tk);return{kind:"task",summary:a(Xb)||"",taskId:a(Yb),toolUseId:a(Zb),status:a(Qb)?.toLowerCase()||null,resultHead:i?i.length>600?`${i.slice(0,600).trimEnd()}\u2026`:i:null}}return null}function nk(t){return t.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&amp;/g,"&")}function jd(t){return t?.kind==="skill"?`Ran the ${t.name} skill`:t?.kind==="command"?`Ran ${t.command}`:t?.kind==="task"?t.summary||"A background task finished":""}function Ld(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":"command-run"}var at=t=>t.payload?.ts??t.ts;function Bo(t){let e=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=/^Agent "([\s\S]+)" finished$/,d=new Map;for(let h of t||[]){if(h.type!=="tool.called"||h.source!=="transcript")continue;let p=String(h.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(p))for(let b of p.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let v=b[0].split("/").pop().toLowerCase();d.has(v)||d.set(v,[]),d.get(v).push(h.payload?.ts??h.ts)}}let f=3*6e4,w=20*6e4,m=(h,p)=>!!(h?.name&&d.get(String(h.name).toLowerCase()))?.some(v=>v>=p-f&&v<=p+w);for(let h of t||[])if(h.type==="turn.retracted"){r.add(`${h.boxId}|${h.payload?.saidTs}`);let p=Ye(h.payload?.text);p&&(o.has(h.boxId)||o.set(h.boxId,[]),o.get(h.boxId).push(p))}else if(h.type==="turn.user"&&h.source==="orchestrator"){let p=Ye(h.payload?.text);p&&(s.has(h.boxId)||s.set(h.boxId,new Set),s.get(h.boxId).add(p))}else if(h.type==="job.started"&&h.payload?.id)a.add(h.payload.id),(h.payload.kind||"agent")==="agent"&&h.payload.name&&c.add(h.payload.name);else if(h.type==="turn.user"&&h.payload?.note?.kind==="task"){let p=h.payload.note,b={status:p.status||null,summary:p.summary||null,resultHead:p.resultHead??null,ts:at(h)};p.toolUseId&&i.set(p.toolUseId,b);let v=String(p.summary||"").match(u);v&&l.set(v[1],b)}let g=new Map,x=new Map,A=new Map,S=new Map,T=new Map,E=new Map,J=new Map,M=new Map,I=(h,p)=>{let b=o.get(h);if(!b?.length)return!1;let v=Ye(p),ot=s.get(h);return b.some($=>{if(!v.startsWith($))return!1;let z=Ye(v.slice($.length));return!!z&&!!ot?.has(z)})};for(let h of t||[]){let p=h.payload||{};switch(h.type){case"turn.user":{if(h.source==="hook"||!String(p.text||"").trim()&&!p.images?.length)break;{let b=p.note||No(p.text);if(b){if(b.kind==="task"){if(b.toolUseId&&a.has(b.toolUseId))break;let v=String(b.summary||"").match(u);if(!b.toolUseId&&v&&c.has(v[1]))break}if(b.kind==="command"&&Dd(b.command))break;b.kind!=="suppress"&&e.push({id:h.id,kind:"event",type:Ld(b),name:b.name??null,command:b.command??null,summary:b.summary??null,ts:at(h),box:h.boxId,agent:h.provider});break}}if(r.has(`${h.boxId}|${at(h)}`)||h.source==="transcript"&&I(h.boxId,p.text)||p.kind==="mock-picked"||p.kind==="ci-fix"||p.kind==="reconcile"||p.kind==="pr-go"||p.kind==="push-go"||p.kind==="spin-up-view")break;for(let b of g.values())b.status="stale";g.clear(),e.push({id:h.id,kind:"message",role:"user",text:p.text,ts:at(h),box:h.boxId,agent:h.provider,boxRole:p.boxRole,...h.actor==="orchestrator"?{orchestration:p.kind||!0}:{},...p.sourceId?{sourceId:p.sourceId}:{},...p.attachments?.length?{attachments:p.attachments}:{},...p.images?.length?{images:p.images}:{}});break}case"turn.agent":{J.clear();let b=(p.images||[]).filter(ot=>!m(ot,at(h)));if(!String(p.text||"").trim()&&!b.length)break;if(h.provider==="cursor"){let ot=`${h.boxId}|${p.text}`;if(n.has(ot))break;n.add(ot)}let v=h.provider==="codex"?(p.tools||[]).filter(ot=>!xa.has(ot)&&!wr.has(ot)):p.tools||[];e.push({id:h.id,kind:"message",role:"assistant",text:p.text,ts:at(h),box:h.boxId,agent:h.provider,boxRole:p.boxRole,...v.length?{tools:v}:{},...p.model?{model:p.model}:{},...p.sourceId?{sourceId:p.sourceId}:{},...b.length?{images:b}:{}});break}case"job.started":{if(!p.id)break;let b=at(h),v=i.get(p.id);if(!v&&(p.kind||"agent")==="agent"&&p.name){let N=l.get(p.name);N&&N.ts>=b&&(v=N)}let ot={jobId:p.id,jobKind:p.kind||"agent",name:p.name||null,detail:p.detail||null,status:v?v.status&&v.status!=="completed"?"failed":"done":"running",startedTs:b,finishedTs:v?.ts??null,summary:v?.summary??null,resultHead:v?.resultHead??null},$=M.get(h.boxId);if($&&$.jobs.some(N=>(N.finishedTs??1/0)>=b))$.jobs.push(ot);else{let N={id:h.id,kind:"job-dock",dockId:p.id,ts:b,box:h.boxId,agent:h.provider,jobs:[ot]};M.set(h.boxId,N),e.push(N)}break}case"handoff.done":e.push(xe(h,"lead-handoff"));break;case"subtask.delegated":e.push({...xe(h,"subtask-delegated"),to:h.boxId,toAgent:h.provider});break;case"subtask.merged":e.push(xe(h,"subtask-merged"));break;case"pr.state.changed":if(!p.change)break;if(p.change==="opened"){for(let b of g.values())b.status="stale";g.clear()}e.push(xe(h,`pr-${p.change}`));break;case"pr.offered":{if(!p.offerId)break;for(let v of g.values())v.status="stale";g.clear();let b={id:h.id,kind:"pr-offer",offerId:p.offerId,excerpt:p.excerpt||null,branch:p.branch||null,base:p.base||null,offered:Array.isArray(p.offered)&&p.offered.length?p.offered:null,toPr:p.toPr&&typeof p.toPr=="object"?{number:p.toPr.number??null}:null,status:"open",ts:at(h),box:h.boxId,agent:h.provider};g.set(p.offerId,b),x.set(p.offerId,b),e.push(b);break}case"pr.offer.answered":{let b=x.get(p.offerId);b&&(b.status=p.action||"dismiss",b.actions=Array.isArray(p.actions)?p.actions:null,g.delete(p.offerId));break}case"box.state.changed":{let b=p.change==="model"?"model-change":p.change;if(!b)break;let v=xe(h,b);b==="model-change"&&(v.to=p.model),e.push(v);break}case"view.state.changed":{if(p.change!=="created"||p.restarted)break;e.push({id:h.id,kind:"view-card",viewId:p.viewId,name:p.name,slug:p.slug,port:p.port,origin:p.origin,ts:at(h),box:h.boxId,agent:h.provider});break}case"view.spinup.failed":{e.push({id:h.id,kind:"view-spinup-failed",name:p.name||null,reason:p.reason||"no-view",ts:at(h),box:h.boxId,agent:h.provider});break}case"mock.state.changed":{if(p.change==="picked"){e.push({id:h.id,kind:"mock-picked",mockId:p.mockId,name:p.name,group:p.group??null,version:p.version??null,siblings:p.siblings??[],ts:at(h),box:h.boxId,agent:h.provider});break}if(p.change!=="created")break;e.push({id:h.id,kind:"mock-card",mockId:p.mockId,name:p.name,group:p.group??null,ts:at(h),box:h.boxId,agent:h.provider});break}case"runbook.state.changed":{if(p.change!=="created")break;e.push({id:h.id,kind:"runbook-card",runbookId:p.runbookId,name:p.name,ts:at(h),box:h.boxId,agent:h.provider});break}case"ticket.linked":{e.push({id:h.id,kind:"ticket-card",provider:p.provider||"linear",key:p.key,url:p.url,title:p.title??null,linkSource:p.linkSource??null,ts:at(h),box:h.boxId,agent:h.provider});break}case"ticket.move.offered":{if(!p.offerId)break;for(let v of A.values())v.key===p.key&&(v.status="stale",A.delete(v.offerId));let b={id:h.id,kind:"ticket-offer",offerId:p.offerId,provider:p.provider||"linear",key:p.key,url:p.url??null,phase:p.phase??null,from:p.from??null,to:p.to?.name??null,prNumber:p.prNumber??null,status:"open",ts:at(h),box:h.boxId,agent:h.provider};A.set(p.offerId,b),S.set(p.offerId,b),e.push(b);break}case"ticket.move.answered":{let b=S.get(p.offerId);b&&(b.status=p.action==="moved"?"moved":"dismiss",A.delete(p.offerId));break}case"ticket.state.changed":{for(let b of A.values())b.key===p.key&&b.to===p.to&&(b.status="stale",A.delete(b.offerId));if(p.mode==="offer")break;e.push(xe(h,"ticket-state"));break}case"ticket.commented":{e.push({id:h.id,kind:"ticket-comment",key:p.key,url:p.url??null,excerpt:p.excerpt??null,ts:at(h),box:h.boxId,agent:h.provider});break}case"ticket.flow.changed":e.push(xe(h,"ticket-flow"));break;case"ticket.create.offered":{if(!p.proposalId)break;for(let v of T.values())v.status="stale";T.clear();let b={id:h.id,kind:"ticket-create-offer",proposalId:p.proposalId,provider:p.provider||"linear",epic:p.epic??null,issues:Array.isArray(p.issues)?p.issues:[],team:p.team??null,needsTeam:!!p.needsTeam,linkCurrentTrack:!!p.linkCurrentTrack,status:"open",ts:at(h),box:h.boxId,agent:h.provider};T.set(p.proposalId,b),E.set(p.proposalId,b),e.push(b);break}case"ticket.create.answered":{let b=E.get(p.proposalId);b&&(b.status=p.action==="created"?"created":"dismiss",b.createdCount=p.createdCount??null,b.failedCount=p.failedCount??null,T.delete(p.proposalId));break}case"ticket.created":{e.push({id:h.id,kind:"ticket-created",items:Array.isArray(p.items)?p.items:[],failed:Array.isArray(p.failed)?p.failed:[],team:p.team??null,ts:at(h),box:h.boxId,agent:h.provider});break}case"error":{if(!p.kind)break;e.push(xe(h,p.kind));break}case"provider.connected":{e.push({id:h.id,kind:"event",type:"provider-connected",provider:p.provider||h.provider,ts:at(h),at:at(h),box:h.boxId,agent:h.provider});break}case"prompt.answered":{if(p.control||!p.question)break;let b=p.groupId||p.promptId;if(!b)break;let v=J.get(b);v||(v={id:h.id,kind:"prompt",groupId:b,provider:h.provider,box:h.boxId,agent:h.provider,questionTotal:p.questionTotal??1,questions:[],ts:at(h)},J.set(b,v));let ot=p.questionIndex??0,$={index:ot,header:p.header||"",question:p.question,multiSelect:!!p.multiSelect,chosen:Array.isArray(p.chosen)?p.chosen:[]},z=v.questions.findIndex(W=>W.index===ot);z>=0?v.questions[z]=$:v.questions.push($),v.questions.sort((W,_)=>W.index-_.index),v.questionTotal=Math.max(v.questionTotal,p.questionTotal??1),new Set(v.questions.map(W=>W.index)).size>=v.questionTotal&&(e.push(v),J.delete(b));break}default:break}}return ok(e.sort((h,p)=>(h.ts??0)-(p.ts??0)))}var rk=2*6e4;function ok(t){let e=new Map,n=[];for(let r of t){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=e.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=rk){s.item.images.push(...r.images),s.lastTs=r.ts??s.lastTs;continue}if(o){let a={...r,images:[...r.images]};e.set(r.box,{item:a,lastTs:r.ts??0}),n.push(a)}else e.delete(r.box),n.push(r)}return n}function ei(t,e=null){let n=[];for(let o of t||[]){if(o.type!=="tool.called"||o.source!=="transcript")continue;let s=o.payload||{};String(s.command||"").trim()&&n.push({id:s.id??null,tool:s.tool||null,command:s.command,description:s.description||"",summary:sk(s),ts:s.ts??o.ts,n:s.n,box:o.boxId,agent:o.provider,boxRole:s.boxRole})}if(!n.length)return[];let r=(e??Bo(t)).filter(o=>o.kind==="message");return Ql(n,r)}function sk(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==In(t.command)?e:ye(t.command,t.description||"")}function xe(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:at(t),ts:at(t)}}var ni={"claude-opus-4-8":{input:5,output:25,cacheWrite:6.25,cacheRead:.5},"claude-sonnet-5":{input:3,output:15,cacheWrite:3.75,cacheRead:.3},"claude-sonnet-4-6":{input:3,output:15,cacheWrite:3.75,cacheRead:.3},"claude-haiku-4-5":{input:1,output:5,cacheWrite:1.25,cacheRead:.1},"claude-fable-5":{input:10,output:50,cacheWrite:12.5,cacheRead:1},"gpt-5.5":{input:5,output:30,cacheWrite:5,cacheRead:.5},"gpt-5.6-sol":{input:5,output:30,cacheWrite:6.25,cacheRead:.5},"gpt-5.6-terra":{input:2.5,output:15,cacheWrite:3.125,cacheRead:.25},"gpt-5.6-luna":{input:1,output:6,cacheWrite:1.25,cacheRead:.1}},Ud={opus:"claude-opus-4-8",opusplan:"claude-opus-4-8",default:"claude-opus-4-8",sonnet:"claude-sonnet-4-6",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"};function Fd(t){if(!t)return null;let e=String(t).trim();return ni[e]?e:Ud[e]?Ud[e]:Object.keys(ni).find(r=>e.startsWith(r))||null}var Do=t=>Fd(t)!==null;function Kt(t,e={}){let n=ni[Fd(t)];return n?((e.input||0)*n.input+(e.output||0)*n.output+(e.cacheWrite||0)*n.cacheWrite+(e.cacheRead||0)*n.cacheRead)/1e6:0}function ak(t){let e=[],n=null;for(let r of String(t||"").split(`
61
+ `)){let o=r.trim();if(!o||o[0]!=="{")continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s.payload||{};if(s.type==="turn_context"&&a.model&&(n=a.model),s.type==="event_msg"&&a.type==="token_count"&&a.info?.last_token_usage){let i=a.info.last_token_usage;e.push({ts:s.timestamp?Date.parse(s.timestamp):null,model:n,last:{input:i.input_tokens||0,cachedInput:i.cached_input_tokens||0,output:i.output_tokens||0,reasoningOutput:i.reasoning_output_tokens||0},occupancy:i.total_tokens??null,contextWindow:a.info.model_context_window??null})}}return e}var ri=new Map;async function ik(t,e){let n=`${t.id}:${e.name}`;if(!ri.has(n)){let r=await B(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);ri.set(n,o)}return ri.get(n)}async function Hd(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await Ge(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await ik(t,e),o=ak(await vt(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||e.model||"gpt-5.5";await R(t,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{usd:Kt(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await R(t,{type:"context.usage",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}import jo from"node:fs";import si from"node:path";import ck from"node:os";var lk=process.env.BOSTRAT_STATE_DIR||si.join(process.env.HOME||ck.tmpdir(),".bostrat"),qd=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,uk="https://cursor.com/api/dashboard/get-filtered-usage-events",Wd=100;async function dk({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(uk,{method:"POST",headers:{"content-type":"application/json",origin:"https://cursor.com",cookie:`WorkosCursorSessionToken=${e}`},body:JSON.stringify({teamId:0,startDate:String(t),endDate:String(Date.now()),page:o,pageSize:Wd})});if(!s.ok)throw new Error(`cursor usage feed: HTTP ${s.status}`);let a=await s.json(),i=a?.usageEventsDisplay||a?.usageEvents||[];if(r.push(...i),i.length<Wd)break}return r}function fk(t){if(!t)return null;let e=Number(t.timestamp)||null,n=t.tokenUsage||null,r=n?{input:n.inputTokens||0,output:n.outputTokens||0,cacheRead:n.cacheReadTokens||0,cacheWrite:n.cacheWriteTokens||0}:null,o=t.conversationId||null;return{id:[o||"?",e??"?",r?`${r.input}.${r.output}.${r.cacheRead}.${r.cacheWrite}`:"?"].join(":"),conversationId:o,ts:e,model:t.model||t.modelIntent||null,kind:t.kindLabel||t.kind||null,isHeadless:t.isHeadless===!0,tokens:r,vendorCents:n?.totalCents??null,chargedCents:t.chargedCents??null}}var pk=5*6e4,Se={at:0,rows:null,error:null},Lo=()=>si.join(lk,"cursor-usage.json");function Gd(){try{return JSON.parse(jo.readFileSync(Lo(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function mk(t){try{jo.mkdirSync(si.dirname(Lo()),{recursive:!0});let e=Lo()+".tmp";jo.writeFileSync(e,JSON.stringify(t)),jo.renameSync(e,Lo())}catch{}}var hk=5e3;async function wk({fetchImpl:t,now:e=Date.now()}={}){if(Se.rows&&e-Se.at<pk)return Se.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Gd(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await dk({sinceMs:o,cookie:n,fetchImpl:t});Se={at:e,rows:s.map(fk).filter(Boolean),error:null}}catch(s){Se={at:e,rows:null,error:String(s?.message||s)}}return Se.rows}var Kd=()=>({enabled:qd(),lastPollAt:Se.at||null,error:Se.error}),oi=new Map;async function gk(t,e){let n=`${t.id}:${e.name}`;if(!oi.has(n)){let r=await B(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);oi.set(n,o)}return oi.get(n)}async function Vd(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!qd())return;let r=await wk({fetchImpl:n});if(!r||!r.length)return;let o=await cu(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await gk(t,e),a=Gd(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${t.id}:${e.name}:${l.id}`))continue;s.add(l.id),i.add(`${t.id}:${e.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",d=l.tokens||null,f=l.vendorCents!=null?l.vendorCents/100:0;!f&&d&&(f=Kt(u,d)),!f&&l.chargedCents!=null&&(f=l.chargedCents/100),await R(t,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:e.name,provider:"cursor",payload:{usd:f,model:u,...d?{tokens:d}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}mk({sinceMs:c,seen:[...i].slice(-hk)})}catch{}}import Sr from"node:fs/promises";import At from"node:path";import yk from"node:os";import bk from"node:net";import{randomUUID as kk}from"node:crypto";import{execFile as xk}from"node:child_process";import{promisify as Sk}from"node:util";var Jt=Sk(xk),vk=process.env.BOSTRAT_STATE_DIR||At.join(process.env.HOME||yk.tmpdir(),".bostrat"),Zd=At.join(vk,"views.json"),[Jd,zd]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),ve=Number(process.env.BOSTRAT_VIEW_MAX)||3,Tk="bostrat-view-",Nn=t=>`${Tk}${t}`,Xd=new Set(["bostrat","www","api","views","app"]),Uo=t=>Object.assign(new Error(t),{status:400}),Ak=new Set(["npm","pnpm","yarn","bun"]),ci="package.json",_k=/[|;&`]|\$\(/,Ek=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),Ik=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Qd=t=>String(t).trim().split(/\s+/).filter(Boolean),Rk=/^[A-Za-z_][A-Za-z0-9_]*=/;function $k(t){for(let e of Qd(t))if(!Rk.test(e))return e;return null}function tf(t){let e=Qd(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function Ok(t,e){let n=tf(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return At.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return At.resolve(t,n[r+1])}return t}var Ck=t=>Sr.stat(t).then(e=>e.isDirectory(),()=>!1),ef=t=>Sr.stat(t).then(e=>e.isFile(),()=>!1),Fo=t=>Sr.realpath(t).catch(()=>At.resolve(t));async function Mk(t,e){if(At.resolve(t)===At.resolve(e))return!0;let[n,r]=await Promise.all([Fo(t),Fo(e)]);return n===r}async function Pk(t){let n=(await Sr.readdir(t,{withFileTypes:!0}).catch(()=>[])).filter(o=>o.isDirectory()&&!o.name.startsWith(".")&&o.name!=="node_modules");return(await Promise.all(n.map(async o=>await ef(At.join(t,o.name,ci))?`./${o.name}`:null))).filter(Boolean)}async function Nk(t,e){let[n,r]=await Promise.all([Fo(t),Fo(e)]),o=At.relative(n,r);if(o&&(o.startsWith("..")||At.isAbsolute(o)))throw Uo(`serve directory escapes the Track workspace: ${e}`)}async function Bk(t,e){if(!await Ck(t))throw Uo(`serve directory does not exist: ${t}`);let n=$k(e);if(!n)return;if(!n.includes("/")&&!await Ds(n,{loginShell:!0,failOpen:!0}))throw Uo(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(_k.test(e)||Ek.has(n)||!Ak.has(n)||!tf(e).includes("run")||Ik.test(e))return;let r=Ok(t,e);if(await ef(At.join(r,ci)))return;let o=await Pk(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw Uo(`no ${ci} in ${r}${s}`)}var Vt=null,ai=null;async function ie(){return Vt||(ai||=(async()=>{try{Vt=JSON.parse(await Sr.readFile(Zd,"utf8"))}catch{Vt={views:[],adoptWatermarks:{}}}finally{ai=null}return Vt.views||=[],Vt.adoptWatermarks||={},Vt})(),ai)}async function zt(){await qt(Zd,Vt)}function ae(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,payload:{ts:Date.now(),change:e,viewId:t.id,name:t.name,slug:t.slug,port:t.port,...n}}).catch(()=>{})}var xr=new Map,di=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,nf=12e3;function fi(t){return xr.get(t)||null}function rf(t,{name:e,box:n}={}){let r=xr.get(t);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o={trackId:t,name:e||"preview",box:n||null,step:"spinning-up",startedAt:Date.now(),finishedAt:null,viewId:null,error:null};return xr.set(t,o),o}function li(t,e){let n=xr.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function Ho(t,{error:e=null}={}){let n=xr.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function of(t){let e=[];for(let n of t?.events||[])n.type!=="view-spinup"||!n.box||!n.startedAt||!n.endedAt||e.push({box:n.box,from:n.startedAt,to:n.endedAt});return e}function ui(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function Dk(t,e){let n=ui(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!Xd.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Xd.has(s))return s}}function vr(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=bk.connect({port:t,host:"127.0.0.1"}),o=s=>{r.destroy(),n(s)};r.setTimeout(e,()=>o(!1)),r.once("connect",()=>o(!0)),r.once("error",()=>o(!1))})}async function pi(t){if(process.platform!=="darwin"){await Jt("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await Jt("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${t}`]))}catch{return}for(let n of e.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}async function jk(t,e){let n=new Set(t.views.map(r=>r.port));if(e){let r=Number(e);if(!Number.isInteger(r)||r<1024||r>65535)throw new Error("invalid port");if(n.has(r))throw new Error(`port ${r} is already owned by a view`);return r}for(let r=Jd;r<=zd;r++)if(!n.has(r)&&!await vr(r,{timeoutMs:250}))return r;throw new Error(`no free view port in ${Jd}-${zd}`)}async function Lk(t){try{let{stdout:e}=await Jt(O,C("list-sessions","-F","#{session_name}"));return e.split(`
62
+ `).some(n=>n.trim()===Nn(t))}catch{return!1}}async function Tr(t){try{await Jt(O,C("kill-session","-t",Nn(t)))}catch{}}async function mi(t){if(!await ee())throw new Error("host tmux server not running \u2014 cannot spawn a view server");let e=Nn(t.slug),n=t.serveCwd??t.cwd,r=At.dirname(process.execPath);await Jt(O,C("new-session","-d","-s",e,"-c",n)),await Jt(O,C("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await Jt(O,C("send-keys","-t",e,"Enter")),await Jt(O,C("send-keys","-t",e,"-l",t.cmd)),await Jt(O,C("send-keys","-t",e,"Enter"))}async function Yd(t,e=12){try{let{stdout:n}=await Jt(O,C("capture-pane","-t",Nn(t),"-p"));return String(n||"").trimEnd().split(`
63
+ `).filter(Boolean).slice(-e).join(`
64
+ `)}catch{return""}}var se=new Map,Uk=12e4;async function Fk(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await Lk(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await Yd(t.slug)||"server exited before listening"),se.delete(t.id),await ae(t,t.state,t.error?{error:t.error}:{}),!0}if(await vr(t.port))return se.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await ae(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>Uk?(t.state="error",t.error=await Yd(t.slug)||"never started listening",await ae(t,"error",{error:t.error}),!0):!1;let o=(se.get(t.id)||0)+1;return se.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var ii=!1;async function Wo(){if(!ii){ii=!0;try{let t=await ie(),e=!1;for(let n of t.views)await Fk(n)&&(e=!0);e&&await zt()}finally{ii=!1}}}async function sf({trackId:t}={}){await Wo();let e=await ie();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function af(t){let n=(await ie()).views.find(r=>r.id===t);return n?{...n}:null}function hi(t){return Vt&&Vt.views.find(e=>e.slug===t)||null}async function wi({trackId:t,name:e,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!t)throw new Error("trackId required");if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(i!=="detected"&&(typeof n!="string"||!n.trim()))throw new Error("cmd (string) required");if(!r)throw new Error("cwd required");let l=o!=null,u=At.resolve(r,o??"."),d=i==="detected"?null:async()=>{await Nk(r,u),await Bk(u,n)},f=await ie();if(i!=="detected"){let g=ui(e),x=String(n).trim(),A=f.views.find(S=>S.trackId===t&&(S.slug===g||ui(S.name)===g||S.cmd===x&&At.resolve(S.serveCwd??S.cwd)===u));if(A)return Hk(f,A,{cmd:x,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),f.views.filter(g=>g.state==="live"||g.state==="starting").length>=ve)throw new Error(`view cap reached (${ve} live) \u2014 stop one first`);let m={id:kk().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:Dk(f,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await jk(f,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await mi(m),f.views.push(m),await zt(),await ae(m,"created",{origin:i}),li(t,m.id),i==="detected"&&await vr(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await zt(),await ae(m,"live")),{...m}}async function Hk(t,e,{cmd:n,box:r,branch:o,serveCwd:s,validate:a}){r&&(e.box=r),o&&(e.branch=o);let i=e.cmd!==n,c=s!==void 0&&!await Mk(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await vr(e.port))return await zt(),li(e.trackId,e.id),{...e,reused:!0};if(a&&await a(),t.views.filter(d=>d.id!==e.id&&(d.state==="live"||d.state==="starting")).length>=ve)throw new Error(`view cap reached (${ve} live) \u2014 stop one first`);return e.origin==="detected"?(await pi(e.port),e.origin="agent"):await Tr(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),se.delete(e.id),await mi(e),await zt(),await ae(e,"created",{restarted:!0,reused:!0}),li(e.trackId,e.id),{...e,reused:!0}}async function cf(t){let n=(await ie()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await pi(n.port):await Tr(n.slug),n.state!=="stopped"&&(n.state="stopped",se.delete(n.id),await zt(),await ae(n,"stopped")),{...n}}async function lf(t){let e=await ie(),n=e.views.find(o=>o.id===t);if(!n)throw new Error("view not found");if(!n.cmd)throw new Error("a detected view has no known command \u2014 recreate it as a managed view");if(e.views.filter(o=>o.id!==t&&(o.state==="live"||o.state==="starting")).length>=ve)throw new Error(`view cap reached (${ve} live) \u2014 stop one first`);return await Tr(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),se.delete(n.id),await mi(n),await zt(),await ae(n,"created",{restarted:!0}),{...n}}async function uf(t){let e=await ie(),n=e.views.findIndex(o=>o.id===t);if(n===-1)throw new Error("view not found");let r=e.views[n];return r.origin==="detected"?await pi(r.port):await Tr(r.slug),e.views.splice(n,1),se.delete(r.id),await zt(),await ae(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function df(t){let e=await ie(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await Tr(r.slug),se.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await zt()),n.length}var Wk=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function ff(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await ie(),r=n.adoptWatermarks[t.id]??null,o=e.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=e.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[t.id]!==s&&(n.adoptWatermarks[t.id]=s,await zt()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(Wk)){let u=Number(l[1]||80);u>=1024&&u<=65535&&a.add(u)}if(!a.size)return[];let i=[];for(let c of a)if(c!==Number(process.env.PORT||3362)&&!n.views.some(l=>l.port===c)&&await vr(c))try{i.push(await wi({trackId:t.id,name:`${t.name} preview`,cmd:null,cwd:t.cwd,port:c,origin:"detected",branch:t.workspace?.branch??null}))}catch{}return i}import{execFile as qk}from"node:child_process";import{promisify as Gk}from"node:util";var Ko=Gk(qk),Kk={timeout:3e4,maxBuffer:8*1024*1024};async function Lt(t,e,n={}){let r=await Gt({cwd:t}),{stdout:o}=await Ko("gh",e,{cwd:t,...Kk,...n,env:{...process.env,...r,...n.env||{}}});return String(o||"")}var qo=null;async function bi(){if(qo&&Date.now()-qo.at<6e4)return qo.data;let t;try{let e=await Ko("gh",["auth","status"],{timeout:1e4});t={installed:!0,authenticated:!0,login:`${e.stdout||""}${e.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(e){t=e?.code==="ENOENT"?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(t.installed&&!t.authenticated)try{let e=await fr();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return qo={at:Date.now(),data:t},t}async function Vk(t){try{let{stdout:e}=await Ko("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function mf(t){let e=t.name||t.context||"check",n=t.detailsUrl||t.targetUrl||null,r;return t.__typename==="StatusContext"?r=t.state==="SUCCESS"?"pass":t.state==="PENDING"||t.state==="EXPECTED"?"pending":"fail":t.status&&t.status!=="COMPLETED"?r="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(t.conclusion)?r="pass":r="fail",{name:e,state:r,url:n}}function gi(t){let e=(t||[]).map(mf);return{total:e.length,passed:e.filter(n=>n.state==="pass").length,failed:e.filter(n=>n.state==="fail").length,pending:e.filter(n=>n.state==="pending").length,failing:e.filter(n=>n.state==="fail").map(({name:n,url:r})=>({name:n,url:r}))}}var ki="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",hf=ki.replace("statusCheckRollup,","");function xi(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function wf(t,e){if(!e)return null;let n=await Y(t);if(!n)return null;try{let r=await Lt(t,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(e)}&per_page=100`,"--jq",".workflow_runs"]),o=new Map;for(let s of JSON.parse(r||"[]"))o.has(s.name)||o.set(s.name,s);return[...o.values()]}catch{return null}}function gf(t){return(t||[]).map(e=>({name:e.name,detailsUrl:e.html_url||null,status:String(e.status||"").toUpperCase(),conclusion:e.conclusion?String(e.conclusion).toUpperCase():null}))}async function yf(t,e){let n=await wf(t,e);return n===null?{...gi([]),unavailable:!0}:gi(gf(n))}function Go(t,e=gi(t.statusCheckRollup)){return{number:t.number,title:t.title,url:t.url,author:t.author?.login??null,headRefName:t.headRefName,headSha:t.headRefOid??null,baseRefName:t.baseRefName,isDraft:!!t.isDraft,reviewDecision:t.reviewDecision||null,mergeable:t.mergeable||"UNKNOWN",mergeStateStatus:t.mergeStateStatus||"UNKNOWN",additions:t.additions??0,deletions:t.deletions??0,createdAt:t.createdAt,updatedAt:t.updatedAt,checks:e,green:e.failed===0&&e.pending===0&&t.mergeStateStatus==="CLEAN",autoMergeArmed:!!t.autoMergeRequest,mergeabilityPending:!1}}var Jk=9e4,Bn=new Map;function zk(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Bn.delete(s);continue}r.add(s),Bn.has(s)||Bn.set(s,n),o.mergeabilityPending=n-Bn.get(s)<Jk}for(let o of Bn.keys())o.startsWith(`${t}#`)&&!r.has(o)&&Bn.delete(o);return e}async function _r(t,e){let n=await Te(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await Lt(n.path,["pr","view",String(r),"--json",ki]);return Go(JSON.parse(o))}catch(o){if(!xi(o))throw o;let s=await Lt(n.path,["pr","view",String(r),"--json",hf]),a=JSON.parse(s);return Go(a,await yf(n.path,a.headRefOid))}}var pf=(t,e)=>e.number-t.number;async function Ar(t){try{let e=await Lt(t,["pr","list","--json",ki,"--limit","50"]);return JSON.parse(e||"[]").map(n=>Go(n)).sort(pf)}catch(e){if(!xi(e))throw e;let n=await Lt(t,["pr","list","--json",hf,"--limit","50"]);return(await Promise.all(JSON.parse(n||"[]").map(async r=>Go(r,await yf(t,r.headRefOid))))).sort(pf)}}var Xk=2e4,Yk=1500,Dn=null;function en(){Dn=null}async function Zk(t,e){if(!t)return e;for(let n of e){let r=await Tt(t,n.number).catch(()=>null);if(!r)continue;r.autoMerge&&(n.autoMergeArmed=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await Eo(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Ro(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function bf(){let t=await bi();if(!t.authenticated)return{auth:t,repos:[]};let e=Dn?.pending?Yk:Xk;if(Dn&&Date.now()-Dn.at<e)return{auth:t,repos:Dn.data};let n=[];for(let a of await oe())await Vk(a.path)&&n.push(a);let r=Date.now(),o=await Promise.all(n.map(async a=>{try{let i=zk(a.path,await Ar(a.path),r);return await Zk(await Y(a.path).catch(()=>null),i),{name:a.name,path:a.path,prs:i}}catch(i){return{name:a.name,path:a.path,prs:[],error:ce(i)}}})),s=o.some(a=>a.prs.some(i=>i.mergeabilityPending||i.checks.pending>0||i.autoMergeArmed));return Dn={at:Date.now(),data:o,pending:s},{auth:t,repos:o}}async function Te(t){let e=(await oe()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function Si(t){if(!t)return null;for(let e of await oe())if(await Y(e.path).catch(()=>null)===t)return e.path;return null}var Vo=new Set(["squash","merge","rebase"]);async function Er(t,e,{method:n="squash"}={}){let r=await Te(t);if(!Vo.has(n))throw new Error(`invalid merge method: ${n}`);let o=Number(e);if(!Number.isInteger(o)||o<=0)throw new Error("invalid PR number");let s=["pr","merge",String(o),`--${n}`];if(n!=="rebase"&&(await Gt({cwd:r.path})).GH_TOKEN){let a=await ca().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Lt(r.path,s)}catch(a){throw new Error(ce(a))}finally{en()}return{ok:!0,number:o}}async function kf(t,e){let n=await Te(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Lt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(ce(o))}finally{en()}return{ok:!0,number:r}}var Qk=2500,tx=9e4,ex=t=>new Promise(e=>setTimeout(e,t));function xf(t){return t.green&&!t.isDraft}function nx(t){return t.isDraft?"draft":t.mergeable==="CONFLICTING"?`conflicts with ${t.baseRefName}`:t.checks.failed>0?"checks failing":t.checks.pending>0?"checks running":t.mergeable==="UNKNOWN"||t.mergeStateStatus==="UNKNOWN"?"mergeability unresolved":t.mergeStateStatus==="BEHIND"?`behind ${t.baseRefName}`:t.mergeStateStatus==="BLOCKED"?"blocked by branch protection":`not clean (${t.mergeStateStatus})`}var yi=new Map;function Sf(){let t={};for(let[e,n]of yi)t[e]={...n};return t}async function vf(t,{method:e="merge",onMerged:n=null}={}){let r=await Te(t);if(!Vo.has(e))throw new Error(`invalid merge method: ${e}`);if(yi.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Ar(r.path)).filter(xf).sort((a,i)=>Date.parse(a.createdAt)-Date.parse(i.createdAt));if(!o.length)throw new Error("no PRs ready to merge");let s={running:!0,startedAt:Date.now(),finishedAt:null,method:e,total:o.length,current:o[0].number,queue:o.map(a=>({number:a.number,title:a.title})),merged:[],skipped:[],failed:[]};return yi.set(r.path,s),ox(s,o,{listPrs:()=>Ar(r.path),merge:a=>Er(r.path,a,{method:e}),sleep:ex,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function rx(t,e){let n=Date.now()+(e.settleMaxMs??tx),r=null;for(;;){let o=null;try{if(o=await e.listPrs(),r=null,!o.some(a=>t.includes(a.number)&&(a.mergeable==="UNKNOWN"||a.mergeStateStatus==="UNKNOWN")))return o}catch(s){r=s}if(Date.now()>=n){if(o)return o;if(r)throw r;return[]}await e.sleep(Qk)}}async function ox(t,e,n){try{let r=e;for(let o=0;o<e.length;o++){let s=e[o];if(t.current=s.number,o>0)try{r=await rx(e.slice(o).map(i=>i.number),n)}catch(i){for(let c of e.slice(o))t.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${ce(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){t.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!xf(a)){t.skipped.push({number:a.number,title:a.title,reason:nx(a)});continue}try{await n.merge(a.number),t.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){t.failed.push({number:a.number,title:a.title,reason:ce(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),en()}}async function Tf(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Ko("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${ce(l)}`)}let o=n||"";if((await Gt({cwd:t})).GH_TOKEN){let l=await ca().catch(()=>null);l&&(o=`${o?`${o}
65
+
66
+ `:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",e.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await Lt(t,s)}catch(l){throw new Error(ce(l))}finally{en()}let i=a.match(/https:\/\/\S+\/pull\/\d+/)?.[0]??null,c=i?Number(i.match(/\/pull\/(\d+)/)?.[1]):null;return{ok:!0,url:i,number:c}}async function Af(t,e){let n=await Te(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await Lt(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(ce(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!xi(i))throw i;let c=await Lt(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await wf(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(ce(i));o=gf(l).map(mf).filter(u=>u.state==="fail").map(u=>({name:u.name,link:u.url}))}let s=new Set,a=[];for(let i of o){let c=sx(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Lt(n.path,["run","view",c,"--log-failed"]).then(u=>ax(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function sx(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function ax(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
67
+ `);return o.length>e&&(r=`\u2026 (${o.length-e} earlier lines truncated)
68
+ `+o.slice(-e).join(`
69
+ `)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
70
+ `+r.slice(-n)),r}function ce(t){return String(t?.stderr||t?.message||t||"").trim().split(`
71
+ `).find(n=>n.trim())||"gh failed"}var ix=6e3,Jo=new Map;function vi(t){let e=null;for(let n of t||[]){let r=n.payload?.ts??n.ts;n.type==="pr.offer.answered"?e=n.payload?.action==="pr"?{ts:r,offerId:n.payload?.offerId??null,box:n.boxId??null}:null:n.type==="pr.state.changed"&&n.payload?.change==="opened"&&e!=null&&r>=e.ts&&(e=null)}return e}async function _f(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await B(t)}catch{return}if(vi(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await Mo(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(Jo.get(t.id)||0)<ix)return;Jo.set(t.id,o);let s;try{s=await Ar(t.cwd)}catch{return}let a=(s||[]).find(c=>c.headRefName===e);if(!a)return;let i=await Y(t.cwd).catch(()=>null);if(i){let{link:c,created:l}=await Nt({repo:i,number:a.number,trackId:t.id,url:a.url,headRef:e,title:a.title,source:"reconciled"});if(!l&&!(c?.trackId===t.id&&await kr(i,a.number))){Jo.delete(t.id);return}}await R(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"opened",number:a.number,url:a.url,title:a.title,branch:e}}).catch(()=>{}),Jo.delete(t.id)}import ht from"node:fs/promises";import _t from"node:path";import Ir from"node:os";import{randomUUID as cx}from"node:crypto";import{execFile as lx}from"node:child_process";import{promisify as ux}from"node:util";import{chromium as dx}from"playwright-core";var If=ux(lx),Rf=process.env.BOSTRAT_STATE_DIR||_t.join(process.env.HOME||Ir.tmpdir(),".bostrat"),$f=_t.join(Rf,"mocks.json"),fx=process.env.BOSTRAT_MOCK_ENGINE||"chromium",px=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",mx=process.env.BOSTRAT_FIREFOX_BIN||"firefox",hx=1300,jn=2,wx=32,Of=1500,Ln=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,gx=700*1024*1024;async function yx(){if(process.platform!=="darwin")return Ir.freemem();try{let{stdout:t}=await If("vm_stat",[],{timeout:3e3}),e=Number(/page size of (\d+) bytes/.exec(t)?.[1])||4096,n=o=>Number(new RegExp(`Pages ${o}:\\s+(\\d+)\\.`).exec(t)?.[1])||0,r=(n("free")+n("inactive")+n("speculative"))*e;return r>0?r:Ir.freemem()}catch{return Ir.freemem()}}var Ut=class extends Error{constructor(e,n=""){super(n?`${e}
72
+ ${n.trim().split(`
73
+ `).slice(-6).join(`
74
+ `)}`:e),this.name="MockRenderError"}},nn=null,Ti=null;async function le(){return nn||(Ti||=(async()=>{try{nn=JSON.parse(await ht.readFile($f,"utf8"))}catch{nn={mocks:[]}}finally{Ti=null}return nn.mocks||=[],nn})(),Ti)}async function Xt(){await qt($f,nn)}function Ft(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,mockId:t.id,name:t.name,...t.group?{group:t.group}:{},...n}}).catch(()=>{})}var zo=t=>_t.join(t,".bostrat","mocks");function Bt(t,e){let n=zo(t.cwd);return{html:_t.join(n,`${t.id}.v${e}.html`),png:_t.join(n,`${t.id}.v${e}.png`)}}function rn(t,e=null){let n=t.versions||[];return e!=null?n.find(r=>r.v===Number(e))||null:n.length?n[n.length-1]:null}function _i(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var bx=Buffer.from([137,80,78,71,13,10,26,10]);function Cf(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(bx)}async function kx(t){let e=Buffer.alloc(24),n=await ht.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function xx({imgW:t,imgH:e,bbox:n,pad:r,align:o=1}){if(!n||!(t>0)||!(e>0))return null;let s=n.right-n.left+1,a=n.bottom-n.top+1;if(s<=0||a<=0||s>=t*.9&&a>=e*.9)return null;let i=s+2*r,c=a+2*r;return i+=(o-i%o)%o,c+=(o-c%o)%o,{sx:n.left,sy:n.top,sw:s,sh:a,outW:i,outH:c,dx:Math.floor((i-s)/2),dy:Math.floor((c-a)/2)}}async function Sx(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((m,g)=>{r.onload=m,r.onerror=()=>g(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,f=-1,w=-1;for(let m=0;m<s;m++)for(let g=m*o*4,x=0;x<o;x++,g+=4)Math.abs(c[g]-l[0])+Math.abs(c[g+1]-l[1])+Math.abs(c[g+2]-l[2])>24&&(x<u&&(u=x),x>f&&(f=x),m<d&&(d=m),w=m);return f<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:f,bottom:w}}},e.toString("base64"))}async function vx(t,e,{bg:n,crop:r}){let o=await t.evaluate(async({b64:s,bg:a,crop:i})=>{let c=new Image;await new Promise((d,f)=>{c.onload=d,c.onerror=()=>f(new Error("decode failed")),c.src=`data:image/png;base64,${s}`});let l=document.createElement("canvas");l.width=i.outW,l.height=i.outH;let u=l.getContext("2d");return u.fillStyle=`rgb(${a[0]},${a[1]},${a[2]})`,u.fillRect(0,0,i.outW,i.outH),u.drawImage(c,i.sx,i.sy,i.sw,i.sh,i.dx,i.dy,i.sw,i.sh),l.toDataURL("image/png").split(",")[1]},{b64:e.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function Tx({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await dx.launch({executablePath:px,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Ut("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:jn})).newPage();a.setDefaultTimeout(Ln),await a.goto(`file://${t}`,{waitUntil:"load",timeout:Ln}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Ln});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await Sx(a,i),u=l&&xx({...l,pad:wx*jn,align:jn});u&&(i=await vx(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Of)throw new Ut(`capture produced a ${i.length}-byte image`);let c=_i(i);if(!c)throw new Ut("output is not a PNG");return await ht.writeFile(e,i),{width:Math.round(c.width/jn),height:Math.round(c.height/jn),dpr:jn,renderMs:Date.now()-r}}catch(s){throw s instanceof Ut?s:new Ut(/timeout/i.test(s?.message||"")?`render timed out after ${Ln}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function Ef({htmlPath:t,outPath:e,width:n}){let r=await ht.mkdtemp(_t.join(Ir.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await If(mx,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Ln,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new Ut(c?.killed?`render timed out after ${Ln}ms`:"firefox exited non-zero",s)}let a=await ht.stat(e).catch(()=>null);if(!a||a.size<Of)throw new Ut(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=_i(await kx(e));if(!i)throw new Ut("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await ht.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function Ax(t){if(fx==="firefox")return Ef(t);try{return await Tx(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return Ef(t);throw e}}var _x=Ax;var Ai=Promise.resolve();function Mf(t){return Ai=Ai.then(t,t),Ai}async function Pf(t,e){let n=await le(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await Xt(),await Ft(r,"rendering",{version:e});try{let o=await yx();if(o<gx)throw new Ut(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Bt(r,e),a=await _x({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await ht.rm(s.png,{force:!0}).catch(()=>{});return}Nf(r,{v:e,source:"html",htmlRel:_t.relative(r.cwd,s.html),pngRel:_t.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Xt(),await Ft(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr})}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await Xt(),await Ft(r,"error",{version:e,error:r.error})}}}function Nf(t,e){t.versions=(t.versions||[]).filter(r=>r.v!==e.v).concat(e),t.versions.sort((r,o)=>r.v-o.v);let n=t.versions[t.versions.length-1];t.readyVersion=n.v,t.height=n.height,t.dpr=n.dpr,t.renderMs=n.renderMs??null,n.v===t.version&&(t.state="ready",t.error=null)}function Ex({trackId:t,name:e,cwd:n,width:r}){if(!t)throw new Error("trackId required");if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(!n)throw new Error("cwd required");let o=r==null?hx:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Bf({trackId:t,group:e}={}){let n=await le(),r=t?n.mocks.filter(o=>o.trackId===t):n.mocks;return e&&(r=r.filter(o=>o.group===e)),r.map(o=>({...o}))}async function Ae(t){let n=(await le()).mocks.find(r=>r.id===t);return n?{...n}:null}async function Df({trackId:t,name:e,html:n,image:r,cwd:o,width:s,group:a,box:i,agent:c,prompt:l}){let u=Ex({trackId:t,name:e,cwd:o,width:s}),d=typeof n=="string"&&n.trim();if(!d&&!r)throw new Error("html (string) or image (png) required");if(r&&!Cf(r))throw new Error("image must be a PNG");let f=await le(),w={id:cx().slice(0,8),trackId:t,box:i||null,agent:c||null,name:e.trim(),prompt:l?String(l).slice(0,500):null,group:a?String(a).trim():null,cwd:o,width:u,height:null,dpr:null,state:"queued",source:d?"html":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await ht.mkdir(zo(o),{recursive:!0}),f.mocks.push(w),d?(await ht.writeFile(Bt(w,1).html,n),await Xt(),await Ft(w,"created"),Mf(()=>Pf(w.id,1))):(await Lf(w,1,r),await Xt(),await Ft(w,"created"),await Ft(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr})),{...w}}async function jf(t,{html:e,image:n,width:r}={}){let s=(await le()).mocks.find(c=>c.id===t);if(!s)throw new Error("mock not found");let a=typeof e=="string"&&e.trim();if(!a&&!n)throw new Error("html (string) or image (png) required");if(n&&!Cf(n))throw new Error("image must be a PNG");if(r!=null){let c=Number(r);if(!Number.isInteger(c)||c<320||c>4e3)throw new Error("invalid width (320-4000)");s.width=c}let i=s.version+1;return s.version=i,s.updatedAt=Date.now(),s.source=a?"html":"image",await ht.mkdir(zo(s.cwd),{recursive:!0}),a?(s.state="queued",await ht.writeFile(Bt(s,i).html,e),await Xt(),await Ft(s,"updated",{version:i}),Mf(()=>Pf(s.id,i))):(await Lf(s,i,n),await Xt(),await Ft(s,"updated",{version:i}),await Ft(s,"ready",{version:i,width:s.width,height:s.height,dpr:s.dpr})),{...s}}async function Lf(t,e,n){let r=_i(n);if(!r)throw new Error("image must be a PNG");let o=Bt(t,e);await ht.writeFile(o.png,n),Nf(t,{v:e,source:"image",htmlRel:null,pngRel:_t.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Uf(t){let e=await le(),n=e.mocks.find(o=>o.id===t);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?e.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==t&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await Ft(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Ff(t){let e=await le(),n=e.mocks.findIndex(o=>o.id===t);if(n===-1)throw new Error("mock not found");let r=e.mocks[n];e.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=Bt(r,o);await ht.rm(s.html,{force:!0}).catch(()=>{}),await ht.rm(s.png,{force:!0}).catch(()=>{})}return await Xt(),await Ft(r,"deleted"),{ok:!0,id:t,name:r.name}}async function Hf(t){let n=(await le()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=_t.join(Rf,"mock-archive",t.id),o=zo(t.cwd),s=_t.join(r,".bostrat","mocks");await ht.mkdir(s,{recursive:!0});let a=await ht.readdir(o).catch(()=>[]);for(let i of a)await ht.copyFile(_t.join(o,i),_t.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel]){if(!l)continue;if(!(await ht.stat(_t.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=t.name||t.id}return await Xt(),n.length}async function Wf(t){let e=await le(),n=e.mocks.filter(r=>r.trackId===t);return n.length&&(e.mocks=e.mocks.filter(r=>r.trackId!==t),await Xt()),n.length}import sn from"node:fs/promises";import _e from"node:path";import Ix from"node:os";import{randomUUID as Rx}from"node:crypto";var Fn=process.env.BOSTRAT_STATE_DIR||_e.join(process.env.HOME||Ix.tmpdir(),".bostrat"),Gf=_e.join(Fn,"runbooks.json"),Ii=_e.join(Fn,"runbooks"),qf=256*1024,$x=[{label:"Anthropic API key",re:/\bsk-ant-[A-Za-z0-9_-]{20,}/},{label:"OpenAI-style API key",re:/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{32,}\b/},{label:"Stripe secret key",re:/\bsk_(?:live|test)_[A-Za-z0-9]{16,}/},{label:"GitHub token",re:/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}|\bgithub_pat_[A-Za-z0-9_]{22,}/},{label:"AWS access key id",re:/\bAKIA[0-9A-Z]{16}\b/},{label:"Slack token",re:/\bxox[baprs]-[A-Za-z0-9-]{10,}/},{label:"private key block",re:/-----BEGIN [A-Z ]*PRIVATE KEY-----/},{label:"JWT",re:/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/}];function Ox(t){return $x.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var on=null,Ei=null;async function Ee(){return on||(Ei||=(async()=>{try{on=JSON.parse(await sn.readFile(Gf,"utf8"))}catch{on={runbooks:[]}}finally{Ei=null}return on.runbooks||=[],on})(),Ei)}async function Hn(){await qt(Gf,on)}function Wn(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,runbookId:t.id,name:t.name,status:t.status,...n}}).catch(()=>{})}function Un(t,e){return _e.join(Ii,`${t.id}.v${e}.md`)}function Kf(t,e=null){let n=t.versions||[];return e!=null?n.find(r=>r.v===Number(e))||null:n.length?n[n.length-1]:null}function Vf(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>qf)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${qf/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Ox(t);if(n.length)throw new Error(`runbook appears to contain a secret VALUE (${n.join(", ")}) \u2014 runbooks ship off-box; name the secret and where it goes, never its value. Replace it with a placeholder like <your key> and retry.`);return e}function Jf(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function zf({trackId:t,status:e}={}){let n=await Ee(),r=t?n.runbooks.filter(o=>o.trackId===t):n.runbooks;return e&&(r=r.filter(o=>o.status===e)),r.map(o=>({...o}))}async function Ri(t){let n=(await Ee()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function Xf({trackId:t,trackName:e,name:n,md:r,cwd:o,box:s,agent:a}){if(!t)throw new Error("trackId required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");let i=Vf(r),c=await Ee(),l={id:Rx().slice(0,8),trackId:t,trackName:e||t,box:s||null,agent:a||null,name:n.trim(),status:"open",version:1,versions:[],trackDeletedAt:null,cwd:o||null,createdAt:Date.now(),updatedAt:Date.now()};return await sn.mkdir(Ii,{recursive:!0}),await sn.writeFile(Un(l,1),r),l.versions.push({v:1,mdRel:_e.relative(Fn,Un(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Hn(),await Wn(l,"created",{version:1}),{...l}}async function Yf(t,{md:e,note:n}={}){let o=(await Ee()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");Jf(o,"update");let s=Vf(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await sn.mkdir(Ii,{recursive:!0}),await sn.writeFile(Un(o,a),e),o.versions.push({v:a,mdRel:_e.relative(Fn,Un(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Hn(),await Wn(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),{...o}}async function Zf(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await Ee()).runbooks.find(o=>o.id===t);if(!r)throw new Error("runbook not found");return Jf(r,e==="done"?"complete":"reopen"),r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Hn(),await Wn(r,e==="done"?"done":"reopened"),{...r})}async function Qf(t){let e=await Ee(),n=e.runbooks.findIndex(o=>o.id===t);if(n===-1)throw new Error("runbook not found");let r=e.runbooks[n];e.runbooks.splice(n,1);for(let o of r.versions||[])await sn.rm(_e.join(Fn,o.mdRel),{force:!0}).catch(()=>{});return await Hn(),r.trackDeletedAt||await Wn(r,"deleted"),{ok:!0,id:t,name:r.name}}async function tp(t){let n=(await Ee()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),await Wn(r,"orphaned"),r.cwd=null;return n.length&&await Hn(),n.length}async function ep(t){let e=await Ee(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await Wn(r,"deleted");for(let o of r.versions||[])await sn.rm(_e.join(Fn,o.mdRel),{force:!0}).catch(()=>{})}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Hn()),n.length}import np from"node:fs/promises";import Xo from"node:path";import{randomUUID as Cx}from"node:crypto";var op=".bostrat/uploads",$i=t=>Xo.join(t,op),rp=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,Mx=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),Px=new Set(["pdf","doc","docx","xls","xlsx","ppt","pptx","odt","ods","odp","rtf","txt","md","markdown","rst","csv","tsv","json","jsonl","yaml","yml","xml","html","htm","log","ini","toml","conf","env","js","jsx","ts","tsx","py","rb","go","rs","java","kt","c","h","cpp","cc","cs","php","swift","sh","bash","zsh","sql","css","scss"]);function Oi(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return Mx.has(e)?"image":Px.has(e)?"doc":null}function Nx(t){let r=Xo.basename(String(t||"")).replace(/[\x00-\x1f]/g,"").replace(/[^A-Za-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(-80)||"file";return r.startsWith(".")?`f${r}`:r}async function sp(t,{filename:e,buffer:n}){if(!t)throw new Error("cwd required");if(!Buffer.isBuffer(n)||n.length===0)throw new Error("empty upload");if(n.length>rp)throw new Error(`file too large \u2014 max ${Math.floor(rp/(1024*1024))} MB`);let r=Nx(e),o=Xo.extname(r).slice(1),s=Oi(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=Cx().slice(0,8),i=$i(t);await np.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await np.writeFile(Xo.join(i,c),n),{id:a,name:r,rel:`${op}/${c}`,kind:s,size:n.length}}function ap(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var Bx=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function Ci(t){return t.role==="user"&&Bx.test((t.text||"").trim())}function qn(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var Lx=jx(Dx),Ie=process.env.BOSTRAT_STATE_DIR||U.join(process.env.HOME||"/tmp",".bostrat"),pp=U.join(Ie,"tracks.json"),Ux=U.join(Ie,"events.jsonl"),Li=U.join(Ie,"workspaces"),Fx=process.env.BOSTRAT_DEFAULT_REPO||null,ts={claude:{envVar:"BOSTRAT_AGENT_CMD",base:"claude",skipFlag:"--dangerously-skip-permissions"},cursor:{envVar:"BOSTRAT_CURSOR_CMD",base:"cursor-agent",skipFlag:"--force"},codex:{envVar:"BOSTRAT_CODEX_CMD",base:"codex",skipFlag:"--dangerously-bypass-approvals-and-sandbox"}},Fi=Object.keys(ts);function Hx(t,{skipPermissions:e=!0}={}){let n=ts[t];if(!n)throw new Error(`unknown agent "${t}"`);let r=process.env[n.envVar];return r||(e&&n.skipFlag?`${n.base} ${n.skipFlag}`:n.base)}function mp(t,e,{skipPermissions:n=!0}={}){let r=ts[t];if(!r)throw new Error(`unknown agent "${t}"`);let o=process.env[r.envVar];if(o)return o;let s=n&&r.skipFlag?` ${r.skipFlag}`:"";switch(t){case"claude":return`${r.base} --resume ${e}${s}`;case"cursor":return`${r.base} --continue${s}`;case"codex":return`${r.base} resume --last${s}`;default:return`${r.base}${s}`}}function es(t){let e=ts[t];if(e)return process.env[e.envVar]||e.base}async function ns(t,e=!0){let n=Hx(t,{skipPermissions:e});if(!await js(n)){let r=n.split(/\s+/)[0];throw new Error(`agent "${t}" isn't available on this box (command not found: ${r}). Its CLI must be installed and on PATH before you can hand off / delegate to it.`)}return n}var rs=".bostrat/handoff/latest.md";function Wx(t,e=rs){return`${t.trim()}
75
+
76
+ (Read ${e} for handoff context from the prior lead \u2014 you are now the lead; none of the prior chat is in your context. Then respond to the user above.)`}var qx=/\(Read \S{1,300} for handoff context from the prior lead — you are now the lead; none of the prior chat is in your context\. Then respond to the user above\.\)$/;function ip(t){return t.role==="user"&&qx.test((t.text||"").trim())}var Hi=".bostrat/handoff/subtask-result.md",Yo=".bostrat/handoff/lead-context.md",Gx=t=>`You are a SUBTASK agent working in an isolated git worktree, delegated by a lead agent on a shared workstream. The lead's full working tree is already checked out here, and the conversation so far is summarized in ${Yo} \u2014 read that first for context. Your scoped task:
77
+
78
+ ${t}
79
+
80
+ Work ONLY inside this directory. When you are finished: (1) make your code changes, (2) commit them with git and a clear message, (3) LAST, write a short markdown digest to ${Hi} with sections \u2014 Result (what you did), Files (paths you changed), Notes (anything the lead must know). Writing that file is your DONE signal: do it last, then stop and take no further action.`,Kx=(t,e,n,r)=>`A subtask box you delegated has finished: "${t}". Its changes are now merged into your working tree (${e.files} file${e.files===1?"":"s"}, +${e.insertions} \u2212${e.deletions}). Read ${r} for exactly what it did. `+(n.length?`IMPORTANT: ${n.length} merge conflict${n.length===1?"":"s"} need resolving \u2014 fix the conflict markers in: ${n.join(", ")}, then continue.`:"Review the merged changes and continue the work."),Yt=null,Mi=null;async function H(){return Yt||(Mi||=(async()=>{try{Yt=JSON.parse(await P.readFile(pp,"utf8"))}catch{Yt={tracks:[]}}finally{Mi=null}return Yt})(),Mi)}async function K(){await qt(pp,Yt)}var Q=(t,e)=>t.tracks.find(n=>n.id===e)||null,Pi=Promise.resolve();function Vx(t,e){let n=JSON.stringify({trackId:t,...e})+`
81
+ `;return Pi=Pi.then(()=>P.mkdir(Ie,{recursive:!0})).then(()=>P.appendFile(Ux,n)).catch(()=>{}),Pi}var it=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),Vx(t.id,r),id(t,r),r};bl(async({sessionName:t,from:e,to:n,version:r,status:o,error:s})=>{let i=(await H()).tracks.find(c=>c.leadBox===t||(c.boxes||[]).some(l=>l.name===t));i&&(it(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await K().catch(()=>{}))});var Ni=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),Jx=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function hp(t){if(!t)return null;let e=typeof t=="string"?[t]:Array.isArray(t?.codes)&&t.codes.length?t.codes:t?.code?[t.code]:[],n=[...new Set(e.filter(s=>Jx.has(s)))];if(!n.length)return null;let r=typeof t=="object"?t?.note:null,o=r&&String(r).slice(0,500).trim()||null;return{code:n[0],...n.length>1?{codes:n}:{},note:o}}async function wp(t,e,n,{kind:r}={}){let o=await G(e);if(!o)throw new Error(`box "${e}" is not running`);let s=es(o.agent);await we({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await yt(e,n,{...re(o.agent),kind:r||"orchestration"}),await L({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await H(),i=Q(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);it(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await K()}var an=new Set(["merged","conflict","canceled","failed","handed-off"]),cp=new Map;async function zx(t){let e=U.join(xo(t),`${t.id}.ndjson`),n;try{n=await P.stat(e)}catch{return null}let r=cp.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await B(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return cp.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function Xx(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Ja(n.name,n.agent)}async function Re(t){let e=new Map((await vn()).map(a=>[a.name,a])),n=t.boxes.map(a=>{let i=e.get(a.name),c=an.has(a.state)?a.state:i?a.role==="lead"?"live":a.state:"stopped";return{...a,state:c,cwd:i?.cwd||a.cwd,agent:i?.agent||a.agent,sessionId:i?.sessionId||a.sessionId}}),[r,{working:o,prompted:s}]=await Promise.all([zx(t),Xx(t,n)]);return{...t,boxes:n,lastResponseAt:r,working:o,prompted:s}}var Yx=5e3;function Zx(t,e,n){let r=[...t].sort((l,u)=>l.createdAt-u.createdAt),o=[...e].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),d=l.createdAt-Yx,f=r[u+1]?.createdAt??1/0,w=o.find(m=>!s.has(m.id)&&m.birthMs>=d&&m.birthMs<f);w&&(s.add(w.id),c.push({box:l.name,sessionId:w.id}))}return c}async function gp(t){let e=new Map;for(let r of t.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(e.has(o.cwd)||e.set(o.cwd,[]),e.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of e){let s=o.map(f=>f.box.sessionId),a=pl(r),i=new Set(a.map(f=>f.id)),c=s.every(f=>f&&i.has(f)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(f=>f.box),d=Zx(u,a,f=>i.has(f));for(let f of d){let w=u.find(m=>m.name===f.box);w&&(w.sessionId=f.sessionId),await ol(f.box,f.sessionId),n=!0}}return n}async function dt(){let t=await H();return await gp(t)&&await K(),Promise.all(t.tracks.map(Re))}async function yp(t,e){let n=await H();if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===t);if(o)return{track:r,box:o}}if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===e);if(o)return{track:r,box:o}}return null}async function rt(t){let e=await H(),n=Q(e,t);return n?(await gp(e)&&await K(),Re(n)):null}var Qx={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function lp(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(Qx[t.agent]||[]).some(n=>n.test(e))}var t0=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function up(t){let e=(t.text||"").trim();return e?t0.some(n=>n.test(e)):!1}var e0=10*6e4;async function Wi(t,e={}){let n=await rt(t);if(!n)return null;let r=new Set([...n.events.filter(y=>y.type==="orchestration-prompt"&&y.prompt).map(y=>String(y.prompt).trim()),...n.events.filter(y=>y.type==="said"&&y.text).map(y=>String(y.text).trim())]),o=Nr(n.id),s=!!(o&&!o.finishedAt&&o.step!=="done"&&!o.error),a=s&&o?.startedAt?[...o.fromBox?[{box:o.fromBox,from:o.startedAt,to:Date.now()+1e12}]:[],...o.toBox?[{box:o.toBox,from:o.startedAt,to:Date.now()+1e12}]:[]]:[],i=fi(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...of(n),...l],f={windows:[...$l(n),...Ol(n,uo),...a,...u]},m=(await Promise.all(n.boxes.map(y=>gu(y,e)))).flat(),g=m.map(y=>{let j={kind:"message",...y};if(j.role==="assistant"&&(j.text||"").trim()&&j.tools?.length){let wn=gr(j.agent).cliTools,Yr=j.tools.filter(je=>!wn.has(je));return{...j,tools:Yr.length?Yr:void 0}}return j}).map(y=>{if(y.role!=="user")return y;let j=No(y.text);return j?j.kind==="suppress"?null:{...y,note:j,text:jd(j)}:y}).filter(Boolean).filter(y=>!(y.role==="user"&&r.has((y.text||"").trim()))).filter(y=>!lp(y)).filter(y=>!up(y)).filter(y=>!ip(y)).filter(y=>!Ci(y)).filter(y=>y.role!=="assistant"||!!(y.text||"").trim()||!!y.images?.length).filter(y=>!Cl(y,f)),x=g.filter(y=>y.failed&&y.text===Sa);if(x.length){let y=new Map;for(let j of x){if(!y.has(j.box)){let je=[...(await pt(j.box,{lines:Rt}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map($s=>$s[1]);y.set(j.box,je.length?je[je.length-1]:null)}let wn=y.get(j.box);wn&&(j.text=`\u25A0 ${wn}`)}}let A=await Promise.all(n.boxes.map(y=>bu(y,e)));await Bd(n,A.flat()),await Pd(n,g),await cd(n),await ff(n,g).catch(()=>{});for(let y of n.boxes)y.agent==="codex"&&y.state==="live"&&await Hd(n,y),y.agent==="cursor"&&y.state==="live"&&await Vd(n,y);let S=await Promise.all(n.boxes.map(y=>yu(y,e)));await Nd(n,S.flat().filter(y=>!Ni(y.box,y.ts,u)));let T=await B(n),E=c&&i.viewId?i.viewId:null,J=gd(T),M=Bo(T).filter(y=>!(y.kind==="message"&&(lp(y)||up(y)||ip(y)||Ci(y)))&&!(y.kind==="view-card"&&E&&y.viewId===E)),I=yd(M,J),h=M.filter(y=>!(y.kind==="message"&&y.role==="assistant"&&!I.has(y)&&Ni(y.box,y.ts,J))),p=ei(T,h).map(y=>({kind:"command-batch",id:y.id,ts:y.pending?y.ts:(y.anchorTs??y.ts)-1,anchorTs:y.anchorTs,anchorBox:y.anchorBox,agent:y.agent,boxRole:y.boxRole,commands:y.commands,pending:y.pending})).filter(y=>!Ni(y.anchorBox,y.anchorTs??y.ts,u)),b=[...h,...p].filter(y=>y.ts!=null).sort((y,j)=>y.ts-j.ts),v=n0(n),ot=v.size?b.filter(y=>y.kind==="event"||!v.has(y.box??y.anchorBox)):b,$=n.boxes.find(y=>y.name===n.leadBox)||null,z=$?await cr($.cwd,$.sessionId,{fallbackNewest:!1}):null;if(!z&&$){let y=[...T].reverse().find(j=>j.type==="context.usage"&&j.boxId===$.name);y?.payload?.window&&(z={pct:y.payload.pct??(y.payload.occupancy!=null?y.payload.occupancy/y.payload.window:null),occupancy:y.payload.occupancy??null,window:y.payload.window,windowKnown:!0,model:y.payload.model||null})}let N=[...m].reverse().find(y=>y.role==="assistant"&&y.model),W=N?.model||null,_=[...n.events||[]].reverse().find(y=>y.type==="model-change"),q=z?.model||W;_&&(!N?.ts||_.at>=N.ts)&&(q=_.to);let X=$?await pt($.name,{lines:Rt}).catch(()=>""):"",lt=$&&Cn($.agent,X)?await me($.name).catch(()=>X):X,et=$?Xe($,lt):null;et&&ld(n,$,et);let wt=!!$&&!et&&ur($.agent,X);$&&!$.readyAt&&(wt||et||X&&io($.agent,X))&&($.readyAt=Date.now(),await K());let Mt=!!$&&!$.readyAt&&Date.now()-(n.createdAt||0)<e0;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&$&&io($.agent,X)&&!et){let y=await H(),j=Q(y,n.id);j?.setup&&!j.setup.connectedAt&&(j.setup.connectedAt=Date.now(),n.setup.connectedAt=j.setup.connectedAt,await R(j,{type:"provider.connected",actor:"system",source:"pane",boxId:$.name,provider:$.agent,payload:{provider:$.agent,ts:j.setup.connectedAt}}).catch(()=>{}),await K())}let De=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,Dt=null;if(c){let y=Date.now()-i.startedAt;if(i.viewId&&!wt){let j=Ho(n.id);j&&(it(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:j.finishedAt}),await K())}else if(et||y>nf&&!wt&&!i.viewId||y>di){let j=et?"needs-input":y>di?"timeout":"no-view";Ho(n.id,{error:j})&&await R(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:$?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:j}}).catch(()=>{})}else Dt={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:ot,context:z?{pct:z.pct,occupancy:z.occupancy,window:z.window,windowKnown:z.windowKnown,model:z.model}:null,models:{current:q,options:td($?.agent)},pendingPrompt:et,working:wt,leadStarting:Mt,handoff:De,view:Dt,queued:o0(n.id)}}function n0(t){let e=new Set;for(let n of t.boxes||[])n.role==="subtask"&&n.name&&e.add(n.name);for(let n of Object.values(t.subtasks||{}))n?.box&&e.add(n.box);return e}async function bp(t,e){let n=await rt(t);if(!n)return null;let r=Jn(t).find(d=>d.id===e)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await B(n),a=Bo(s).filter(d=>d.box===o),i=ei(s.filter(d=>d.boxId===o),a.filter(d=>d.kind==="message")).map(d=>({kind:"command-batch",id:d.id,ts:d.pending?d.ts:(d.anchorTs??d.ts)-1,anchorTs:d.anchorTs,anchorBox:d.anchorBox,agent:d.agent,boxRole:d.boxRole,commands:d.commands,pending:d.pending})),c=[...a,...i].filter(d=>d.ts!=null).sort((d,f)=>d.ts-f.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await pt(o,{lines:Rt}).catch(()=>"");u=ur(l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function qi(t,{promptId:e,answers:n,text:r}={}){let o=Q(await H(),t);if(!o)throw new Error(`track "${t}" not found`);let s=o.boxes.find(f=>f.name===o.leadBox);if(!s)throw new Error(`track "${t}" has no lead box`);if(!await G(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Pa(s);if(!i)throw new Error("no pending prompt to answer");if(e&&e!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let f=typeof r=="string"?r.trim():"";if(!f)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(f)||f.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await yt(o.leadBox,f,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await gt(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.provider==="claude"||i.answerMode==="keys"){let f=Nu(i,n||[]);await gt(o.leadBox,f,{kind:"answer-prompt"}),c={mode:"keys",value:f.join(" "),count:f.length}}else{let f=Tu(i,n||[]);await yt(o.leadBox,f,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:f,count:1}}let l=i.questions?.[0]||{},u=(Array.isArray(n?.[0])?n[0]:[]).map(f=>l.options?.[f]).filter(Boolean).map(f=>({label:f.label,description:f.description||""})),d=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:u,...d?{control:d}:{}},await L({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),ud(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function kp(t,{promptId:e,questionIndex:n}={}){let r=Q(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${t}" has no lead box`);if(!await G(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Pa(o);if(!a)throw new Error("no pending prompt to navigate");if(a.provider!=="claude")throw new Error("prompt navigation not supported for this provider");if(a.review)throw new Error("cannot navigate on the review screen \u2014 answer or cancel");let i=a.questionTotal??1;if(i<2)throw new Error("not a multi-question prompt");if(!Number.isInteger(n)||n<0||n>=i)throw new Error("questionIndex out of range");if(e&&e!==a.promptId)throw new Error("prompt changed since it was shown \u2014 reload and try again");let c=Bu(a,n);return c.length?(await gt(r.leadBox,c,{kind:"navigate-prompt"}),await L({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}var r0=1500,$r=new Map;function xp(t){let e=$r.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},$r.set(t,e)),e}function o0(t){return($r.get(t)?.items??[]).map(e=>({...e}))}function os(t){let e=$r.get(t);if(!e)return[];let n=e.items.splice(0);return e.idleSince=null,e.timer&&(clearInterval(e.timer),e.timer=null),n}function s0(t){let e=xp(t);e.timer||(e.timer=setInterval(()=>a0(t).catch(()=>{}),r0))}async function a0(t){let e=$r.get(t);if(!(!e||e.flushing)){if(!e.items.length){e.timer&&(clearInterval(e.timer),e.timer=null);return}e.flushing=!0;try{let n=Q(await H(),t);if(!n||n.archivedAt){os(t);return}let r=Nr(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){qn(e,!1);return}let o=await G(n.leadBox);if(!o){qn(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox),a=await pt(n.leadBox,{lines:Rt}).catch(()=>"");if(ao(s?.agent,a)){qn(e,!1);return}if(Cn(s?.agent,a)){let c=await me(n.leadBox).catch(()=>a);if(Xe(s,c)){qn(e,!1);return}}if(!qn(e,!0))return;let i=e.items.shift();e.idleSince=null,await Sp(n,s,o,i.text,i.kind??null,i.attachments??null),await K()}finally{e.flushing=!1}}}async function Sp(t,e,n,r,o=null,s=null){let a=r,i=!1;t.pendingHandoffHydrate&&(a=Wx(r,t.pendingHandoffHydrate.artifact||rs),delete t.pendingHandoffHydrate,i=!0);let c=Array.isArray(s)?s.filter(l=>l&&l.rel):[];if(c.length){let l=ap(c.map(u=>u.rel));a=a.trim()?`${a.trim()}
82
+
83
+ ${l}`:l}await we({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:es(n.agent)}),await yt(t.leadBox,a,{...re(e?.agent),kind:i?"lead-handoff-first-prompt":"say"}),i&&await L({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length}),it(t,"said",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,text:r,...o?{kind:o}:{},...c.length?{attachments:c}:{}}),delete t.draft}async function $e(t,e,{kind:n=null,attachments:r=null}={}){let o=Array.isArray(r)?r.filter(f=>f&&typeof f.rel=="string"):[];if((typeof e!="string"||!Mn(e))&&!o.length)throw new Error("text (string) or an attachment required");let s=Q(await H(),t);if(!s)throw new Error(`track "${t}" not found`);if(s.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before driving it`);let a=await G(s.leadBox);if(!a&&await Vi(s)&&(await K(),a=await G(s.leadBox)),!a)throw new Error(`lead box "${s.leadBox}" is not running`);let i=s.boxes.find(f=>f.name===s.leadBox),c=Mn(e),l=xp(t),u=await pt(s.leadBox,{lines:Rt}).catch(()=>""),d=l.items.length>0||ao(i?.agent,u);if(!d&&Cn(i?.agent,u)){let f=await me(s.leadBox).catch(()=>u);d=!!Xe(i,f)}if(d){let f={id:Qo(),text:c,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(f),s0(t),delete s.draft,await K(),{ok:!0,leadBox:s.leadBox,queued:!0,id:f.id,position:l.items.length}}return await Sp(s,i,a,c,n,o),await K(),{ok:!0,leadBox:s.leadBox}}var dp="Spin up a live preview View of this app so I can see it on another device. From the repo root, run `bostrat-view up --name \"<short name>\" -- <serve command>` \u2014 pick the right dev/serve command for this repo yourself (e.g. `npm run dev`), pass $PORT through if the command needs an explicit port, and share the public URL it prints. If a suitable View is already live, just re-share its URL instead of starting another. Don't paste a localhost link \u2014 it's dead on my device.\n\nIMPORTANT: if you cannot confidently identify WHICH project/app to serve, or which serve command to use (e.g. several apps in this tree, or no obvious dev script), do NOT guess and do NOT run `bostrat-view up`. Instead, reply with a short question asking me which one you should preview, and stop.";async function vp(t,{hint:e}={}){let n=Q(await H(),t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it first`);let r=fi(n.id);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o=n.boxes.find(i=>i.name===n.leadBox),s=String(e||"").trim()?String(e).trim().slice(0,40):"preview";rf(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${dp}
84
+
85
+ What to preview: ${String(e).trim()}`:dp;try{await $e(t,a,{kind:"spin-up-view"})}catch(i){throw Ho(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Gi(t){let e=Q(await H(),t);if(!e)throw new Error(`track "${t}" not found`);if(e.archivedAt)throw new Error(`track "${t}" is archived`);let n=e.boxes.find(i=>i.name===e.leadBox),r=os(t),o=await G(e.leadBox);o&&await gt(e.leadBox,["Escape"],{kind:"interrupt"});let s=o?await l0(e,n):null,a=o?await i0(e).catch(()=>null):null;return await L({flow:"interrupt",phase:"sent",trackId:t,sessionName:e.leadBox,agent:n?.agent,dropped:r.length,unsent:!!s,shipDismissed:!!a}).catch(()=>{}),{ok:!0,interrupted:!!o,dropped:r.length,restored:s}}async function i0(t){let e=vi(await B(t));return e?.offerId?(await R(t,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:e.box,payload:{ts:Date.now(),offerId:e.offerId,action:"dismiss",actions:null,reason:"interrupt"}}),e.offerId):null}var c0=[250,400,500,600];async function l0(t,e){if(e?.agent!=="claude")return null;let n=[...t.events||[]].reverse().find(r=>r.type==="said"&&r.text);if(!n)return null;for(let r of c0){await Qi(r);let o=await pt(t.leadBox,{lines:Rt}).catch(()=>""),s=Na(e.agent,o);if(s&&ju(n.text,s))return await u0(t.leadBox,e.agent,n.text.length+8),t.draft={text:n.text,updatedAt:Date.now()},it(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:n.at,text:n.text}),await K(),n.text}return null}async function u0(t,e,n){for(let r=0;r<3;r++){await no(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await Qi(200);let o=await pt(t,{lines:Rt}).catch(()=>""),s=Na(e,o);if(!s)return!0;n=s.length+8}return!1}async function Tp(t,e){if(typeof e!="string")throw new Error("text (string) required");let n=Q(await H(),t);if(!n)throw new Error(`track "${t}" not found`);let r=Mn(e);return r?n.draft={text:r,updatedAt:Date.now()}:delete n.draft,await K(),{ok:!0,draft:n.draft||null}}async function Ap(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim(),r=Q(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=await G(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=ed(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await cr(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await we({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:es(o.agent)});let u=await nd(a,r.leadBox,i);return it(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await K(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function _p(t){for(let e of t){if(!e||!await ja(e))continue;let n=await Uu(e).catch(()=>null);if(!n)continue;if((await P.stat(U.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function d0(t){let e=t;return e?e.kind==="multi"?Array.isArray(e.repos)?e.repos.filter(Boolean):[]:e.base?[{name:U.basename(e.base),dir:e.dir??null,base:e.base,baseRef:e.baseRef??null,repo:e.repo??null}]:[]:[]}async function f0(t){let e=t;typeof e=="string"&&!U.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await ua(e));let n=await _p([e]);if(!n)throw new Error(`tagged repo "${t}" is not a usable base (need a primary checkout or a synced owner/name)`);return n}async function p0(t,e,n,r){let o=`track/${Xi(e)}-${t}`,s=[];try{await P.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await f0(i);if(s.some(w=>w.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Y(c).catch(()=>null),u=U.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let w=2;a.has(u);w++)u=`${U.basename(c)}-${w}`;a.add(u);let d=U.join(n,u),f=await La(c);await yo(c,d,o,f),s.push({name:u,dir:d,base:c,baseRef:f,repo:l})}return await g0(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await Ze(i.base,i.dir).catch(()=>{});throw await P.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function m0(t,e,{repo:n,repos:r,cwd:o}={}){await P.mkdir(Li,{recursive:!0});let s=U.join(Li,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return p0(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!U.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await ua(n));let i=await _p([n,o,Fx]);if(i){let c=`track/${Xi(e)}-${t}`;try{let l=await La(i);await yo(i,s,c,l),await Ui(s);let u=await Y(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await P.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await P.mkdir(s,{recursive:!0}),await Wu(s),await Ui(s),{dir:s,base:null,branch:null,kind:"repo"}}var Or="# bostrat workspace",Rr=`${Or}
86
+
87
+ When asked to run, preview, or demo this app so the operator can review it: do NOT
88
+ start an ad-hoc dev server and do NOT share a localhost URL (the operator is on
89
+ another device \u2014 localhost links are dead there). Instead run:
90
+
91
+ bostrat-view up --name "<short name>" -- <serve command>
92
+
93
+ (e.g. \`bostrat-view up --name "Checkout preview" -- npm run dev\`). The server is
94
+ assigned a port via $PORT \u2014 pass it through if the command needs it explicitly
95
+ (e.g. \`-- npm run dev -- --port "\\$PORT"\`; escape it so your own shell doesn't
96
+ eat it). The command is typed verbatim into a shell that already has PORT set \u2014
97
+ do NOT wrap it in \`sh -c '\u2026'\`: the quoting is lost in transit and the wrapper
98
+ runs only the first word. Run it from the directory the serve command belongs in: the view starts in YOUR cwd,
99
+ so in a monorepo cd to the app first (the dir holding its package.json), not the
100
+ workspace root. Share the public URL the command prints; that link works on the
101
+ operator's phone. \`bostrat-view ls\` lists running views (with the dir each runs in),
102
+ \`bostrat-view stop <name-or-id>\` stops one, \`bostrat-view rm <name-or-id>\` deletes
103
+ the record \u2014 needed if a view was created with the wrong directory or command, since
104
+ a re-\`up\` under the same name reuses the existing record.
105
+
106
+ When a view is ALREADY live and you are asked to adjust what it shows: just edit the
107
+ code \u2014 the running dev server hot-reloads, and the operator's existing URL updates
108
+ in place. Do NOT start another server or invent a new view name for the adjustment;
109
+ re-share the same URL (\`bostrat-view ls\` prints it). Re-running \`bostrat-view up\`
110
+ with the same name (or the same serve command) is safe \u2014 it reuses the existing
111
+ view rather than creating a duplicate.
112
+
113
+ When asked to MOCK UP a design ("mock up 3 options", "show me a design for X"),
114
+ you are acting as a DESIGNER delivering images for review. Write each comp as ONE
115
+ self-contained HTML file (inline ALL CSS in a <style> block \u2014 no external
116
+ stylesheets; web fonts with system fallbacks so it renders offline; author at the
117
+ width you were asked for \u2014 default 1300, pass --width for other artboards), then
118
+ publish it with:
119
+
120
+ bostrat-mock add --name "<short name>" [--group <batch-id>] [--width N] <file.html>
121
+
122
+ Do NOT screenshot it by hand and do NOT just leave the file in the repo \u2014 the
123
+ command renders it full-page to an image the operator reviews in chat and the
124
+ gallery. If you already have a finished image, publish it directly:
125
+ \`bostrat-mock add --name "<name>" <file.png>\`.
126
+
127
+ When the operator gives FEEDBACK on a mock, revise the SAME mock \u2014 edit the comp
128
+ and run \`bostrat-mock update <name-or-id> <file>\` \u2014 never add a near-duplicate.
129
+ Each update is a new version; the history stays reviewable. For a batch ("3
130
+ options"), give every comp the same --group so they can be compared side by side.
131
+ \`bostrat-mock ls\` lists mocks, \`bostrat-mock rm <name-or-id>\` deletes one.
132
+
133
+ When asked to link this work to a TICKET (a Linear issue URL or a key like
134
+ ABC-123), run:
135
+
136
+ bostrat-ticket link <issue-url-or-KEY-123>
137
+
138
+ The issue then carries a live Bostrat status card, and PR opened/merged land on
139
+ it as comments automatically \u2014 do NOT hand-write status comments to the tracker.
140
+ \`bostrat-ticket status\` shows the current link, \`bostrat-ticket unlink\` removes it.
141
+
142
+ Ticket STATUS moves are manual-first: at each lifecycle moment (link, PR opened,
143
+ PR merged) Bostrat shows the operator a one-tap offer card in their chat. When
144
+ the operator asks you to AUTOMATE those moves (or to stop the prompts), do NOT
145
+ improvise \u2014 set the prepared config:
146
+
147
+ bostrat-ticket flow set <phase>=<mode>
148
+
149
+ Phases: linked, prOpened, prMerged. Modes: prompt (default, offer card), auto
150
+ (Bostrat moves the issue itself), off (nothing). E.g. "always move tickets when
151
+ PRs merge" \u2192 \`bostrat-ticket flow set prMerged=auto\`; "stop asking about ticket
152
+ status" \u2192 set the relevant phases to off. These three phases are the ONLY
153
+ configurable ticket automations \u2014 if asked to automate or suppress a ticket
154
+ behavior that isn't one of them, say it isn't configurable yet instead of
155
+ working around it. Never move the issue or edit its status by other means.
156
+
157
+ When you FINISH a piece of work and steps remain that ONLY the operator can
158
+ perform (vendor/app registrations, secrets to set, interactive logins, device or
159
+ cert installs, DNS changes, subscriptions, physical steps), write those steps as
160
+ ONE well-formatted markdown RUNBOOK \u2014 a # title naming the deliverable state, a
161
+ short context paragraph, numbered copy-pasteable steps each with a verification
162
+ line ("you should see \u2026"), and a closing line telling the operator to report
163
+ completion in this chat. NEVER put a secret VALUE in a runbook \u2014 name the secret
164
+ and where it goes (\`fly secrets set FOO=<your key>\`), never the value; the
165
+ server rejects token-shaped content. Publish it with:
166
+
167
+ bostrat-runbook add --name "<deliverable state>" <file.md>
168
+
169
+ BEFORE your closing ship-offer line (below). Do NOT just paste the steps into
170
+ chat \u2014 the runbook is durable, versioned, and survives this Track. When the
171
+ operator reports progress ("done", "key installed") or the plan changes, revise
172
+ the SAME runbook \u2014 keep completed steps, struck through (~~like this~~ \u2014 done
173
+ <date>), adjust the rest \u2014 and run
174
+ \`bostrat-runbook update <name-or-id> --note "<what changed>" <file.md>\`; never
175
+ add a near-duplicate. When every step is confirmed, run
176
+ \`bostrat-runbook done <name-or-id>\`. At the START of related work, check
177
+ \`bostrat-runbook ls\` \u2014 if an open runbook covers this ground, update it rather
178
+ than creating another. Runbooks are read-only for the operator: they act and
179
+ report back in chat; you are the only writer.
180
+
181
+ When you FINISH a piece of implementation work (feature built, bug fixed, checks
182
+ green) and the operator has not already told you to ship it: do NOT commit, push,
183
+ or open a PR on your own. Instead END your final message with a short, explicit
184
+ offer \u2014 e.g. "Want me to commit and push this, or open a PR?" \u2014 as its own last
185
+ line. Bostrat detects that closing offer and renders one-tap ship controls in the
186
+ operator's chat; the go-ahead comes back as a normal message telling you exactly
187
+ which steps to run (and which to skip). An offer buried mid-message, phrased as a
188
+ statement, or omitted entirely renders no controls \u2014 so close EVERY completed
189
+ implementation turn this way, every time. When the operator has already asked you
190
+ to commit / push / open the PR, just do it; do not re-offer.
191
+ `;async function Ui(t){try{await P.writeFile(U.join(t,"AGENTS.md"),Rr,{flag:"wx"})}catch{return}await y0(t,["/AGENTS.md"])}var Bi=t=>typeof t=="string"&&(t===Or||t.startsWith(`${Or}
192
+ `)),h0="# bostrat multi-repo workspace";function w0(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
193
+ `);return`${h0}
194
+
195
+ This directory is NOT a git repo. It is a bostrat Track workspace holding one
196
+ git worktree per tagged repo, ALL on the same branch \`${e}\`:
197
+
198
+ ${n}
199
+
200
+ cd into the repo a change belongs to before running git commands; commit (and
201
+ push / open PRs) in each repo separately, always on that shared branch. Work
202
+ only needs to land in the repos it actually touches \u2014 an untouched repo's
203
+ branch simply stays at its fork point and never gets pushed. When asked to run
204
+ or preview an app, run it from that app's own repo directory.
205
+ `}async function g0(t,e,n){let r=w0(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await P.writeFile(U.join(t,o),r).catch(()=>{})}var Di=Rr.replace(`${Or}
206
+ `,`${Or}
207
+
208
+ (Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
209
+ SCOPE: these instructions apply ONLY when your working directory is under
210
+ ~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
211
+ When working anywhere else, ignore everything below.
212
+ `);async function Ep(t){let{stdout:e}=await Lx("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return U.resolve(t,e.trim())}async function y0(t,e){try{let n=await Ep(t);await P.mkdir(U.dirname(n),{recursive:!0});let r=await P.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
213
+ `).includes(s));o.length&&await P.appendFile(n,(r.endsWith(`
214
+ `)||!r?"":`
215
+ `)+o.join(`
216
+ `)+`
217
+ `)}catch{}}async function b0(t,e){try{let n=await Ep(t),r=await P.readFile(n,"utf8"),o=r.split(`
218
+ `).filter(s=>s!==e);o.length!==r.split(`
219
+ `).length&&await P.writeFile(n,o.join(`
220
+ `))}catch{}}async function Ip(){let t={refreshed:0,removed:0,codexGlobal:null};try{await P.mkdir(Ie,{recursive:!0});for(let r of["CLAUDE.md","AGENTS.md"])await P.writeFile(U.join(Ie,r),Rr).catch(()=>{});let e=process.env.CODEX_HOME||U.join(process.env.HOME||"/tmp",".codex");if((await P.stat(e).catch(()=>null))?.isDirectory()){let r=U.join(e,"AGENTS.md"),o=await P.readFile(r,"utf8").catch(()=>null);o===null?(await P.writeFile(r,Di,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):Bi(o)&&o!==Di&&(await P.writeFile(r,Di).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[Li,Kn]){let o=await P.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=U.join(r,s.name),i=U.join(a,"AGENTS.md"),c=await P.readFile(i,"utf8").catch(()=>null);c!==null&&Bi(c)&&c!==Rr&&(await P.writeFile(i,Rr).catch(()=>{}),t.refreshed++);let l=U.join(a,"CLAUDE.md"),u=await P.readFile(l,"utf8").catch(()=>null);u!==null&&Bi(u)&&(await P.rm(l,{force:!0}).catch(()=>{}),await b0(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function k0(t){let e=t.workspace;if(!(!e||!e.dir))try{if(e.kind==="multi"){for(let n of d0(e))n.base&&n.dir&&await Ze(n.base,n.dir).catch(()=>{});await P.rm(e.dir,{recursive:!0,force:!0})}else e.kind==="worktree"&&e.base?await Ze(e.base,e.dir):await P.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function Ki({name:t,cwd:e,repo:n,repos:r,agent:o="claude",skipPermissions:s=!0}){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");let a=await ns(o,s),i=await H(),c=Qo().slice(0,8),l=await m0(c,t,{repo:n,repos:r,cwd:e}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(g=>g.name))),d=`${t.trim()} lead`;u.has(Ls(d))&&(d=`${t.trim().slice(0,50).trim()} ${c} lead`);let f=await Ue(d,l.dir,{agentCmd:a,agent:o});co({cwd:f.cwd,sessionName:f.name,agent:o,agentCmd:a});let w={id:c,name:t.trim(),cwd:f.cwd,workspace:l,leadBox:f.name,boxes:[Np(f,"lead","live")],events:[],createdAt:Date.now()};return it(w,"opened",{agent:o,box:f.name,skipPermissions:s,name:w.name,cwd:w.cwd,workspace:{base:l.base??null,branch:l.branch,kind:l.kind,...l.baseRef?{baseRef:l.baseRef}:{},...l.repo?{repo:l.repo}:{},...l.repos?{repos:l.repos.map(({name:m,base:g,baseRef:x,repo:A})=>({name:m,base:g,baseRef:x,repo:A}))}:{}}}),i.tracks.push(w),await K(),Re(w)}var x0=new Set(["claude","codex","cursor"]);async function Rp({provider:t="claude",fresh:e=!1}={}){if(!x0.has(t))throw new Error(`unknown setup provider "${t}" (claude | codex | cursor)`);let r=(await H()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===t&&!u.archivedAt);if(r){if(!e){let u=await Re(r);if(u.boxes.find(f=>f.name===u.leadBox)?.state==="live")return u}await Vn(r.id).catch(()=>{})}let o=(await ns(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Qo().slice(0,8),a=U.join(Ie,"setup",s);await P.mkdir(a,{recursive:!0});let i=await Ue(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&co({cwd:i.cwd,sessionName:i.name,agent:t,agentCmd:o});let c={id:s,name:`Connect ${t}`,kind:"setup",setup:{provider:t,startedAt:Date.now(),connectedAt:null},cwd:i.cwd,workspace:{dir:a,base:null,branch:null,kind:"scratch"},leadBox:i.name,boxes:[Np(i,"lead","live")],events:[],createdAt:Date.now()};return it(c,"opened",{agent:t,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await H()).tracks.push(c),await K(),Re(c)}async function $p(t,e,n){await df(t).catch(()=>{});let r=Cr.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),Cr.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of Jn(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Zi(a).catch(()=>{}),Zt.delete(a.id);for(let a of Object.values(e.subtasks||{}))a.finishedAt||(a.step="error",a.error=n,a.canceled=!0,a.finishedAt=Date.now());let o=await H(),s=new Set((o.tracks||[]).filter(a=>a.id!==t&&!a.archivedAt).flatMap(a=>(a.boxes||[]).map(i=>i.name)));for(let a of e.boxes)s.has(a.name)||await Wt(a.name).catch(()=>{})}async function Op(t,{cascadeMocks:e=!0,cascadeRunbooks:n=!1}={}){let r=await H(),o=r.tracks.findIndex(a=>a.id===t);if(o===-1)throw new Error(`track "${t}" not found`);let s=r.tracks[o];return e||await Hf(s),await $p(t,s,"track deleted"),n?await ep(t).catch(()=>{}):await tp(t).catch(()=>{}),await ad(),await k0(s),e&&await Wf(t).catch(()=>{}),it(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await K(),{ok:!0,id:t,name:s.name,boxes:s.boxes.length}}async function Vn(t){let e=await H(),n=Q(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is already archived`);await $p(t,n,"track archived");for(let r of n.boxes)an.has(r.state)||(r.state="stopped");return n.archivedAt=Date.now(),it(n,"archived",{boxes:n.boxes.length}),await K(),Re(n)}async function tn(t,e){let n=await Tt(t,e).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;try{return await Vn(n.trackId),!0}catch(r){if(/already archived/i.test(String(r?.message||r)))return!1;throw r}}async function Vi(t){if(t.archivedAt)return!1;let e=t.boxes.find(r=>r.name===t.leadBox);if(!e||an.has(e.state)||await G(e.name))return!1;let n=mp(e.agent,e.sessionId);if(!await js(n))return it(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" not available on this box`}),!1;try{return await Ue(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0}),co({cwd:e.cwd,sessionName:e.name,agent:e.agent,agentCmd:n}),e.state="live",it(t,"rehydrated",{box:e.name,agent:e.agent}),!0}catch(r){return it(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:String(r?.message||r)}),!1}}async function Cp(t){let e=await H(),n=Q(e,t);if(!n)throw new Error(`track "${t}" not found`);if(!n.archivedAt)throw new Error(`track "${t}" is not archived`);return delete n.archivedAt,it(n,"unarchived",{}),await Vi(n),await K(),Re(n)}async function Mp(){let t=await H(),e=new Set((await vn()).map(s=>s.name)),n=new Set((t.tracks||[]).filter(s=>!s.archivedAt).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of t.tracks||[])if(s.archivedAt)for(let a of s.boxes)e.has(a.name)&&!n.has(a.name)&&(await Wt(a.name).catch(()=>{}),it(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!an.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await K(),r}async function Pp(t){let e=await H(),n=Q(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before restarting the lead`);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${t}" has no lead box`);if(an.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await G(n.leadBox)){let a=await pt(n.leadBox,{lines:Rt}).catch(()=>"");if(ao(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Wt(n.leadBox),r.state="stopped"}let s=await Vi(n);if(await K(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Re(n)}var Np=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,createdAt:Date.now()});function Bp(t){return(!Array.isArray(t.epochs)||t.epochs.length===0)&&(t.epochs=[{agent:t.agent??null,sessionId:t.sessionId??null,pinned:t.pinned===!0,startedAt:t.createdAt??null,endedAt:null}]),t.epochs}var Cr=new Map,Nr=t=>Cr.get(t)||(Yt?Q(Yt,t)?.handoff:null)||null;async function Zo(t,e){let n=await H(),r=Q(n,t);r&&(r.handoff={...r.handoff||{},...e},await K())}function S0(t,e=Date.now()){let n=t?.handoff;if(!n||n.finishedAt)return null;let r=n.step,o=!!n.toBox&&n.toBox===n.fromBox,s=t.boxes?.find?.(i=>i.name===t.leadBox)||null;return(o?t.leadBox===n.fromBox&&!!n.startedAt&&Array.isArray(s?.epochs)&&s.epochs.some(i=>i.startedAt!=null&&i.startedAt>=n.startedAt):!!n.toBox&&t.leadBox===n.toBox)?{...n,step:"done",finishedAt:e,recovered:`committed before restart (was "${r}")`}:{...n,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${r}"; lead "${t.leadBox}" retained \u2014 retry the handoff`,recovered:"rolled-back"}}async function Dp(){let t=await H(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=S0(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await G(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),d=Bp(l),f=d[d.length-1],w=f.agent,m=f.sessionId??null;f.endedAt=u,r.reason&&(f.endReason=r.reason);let g=c.agent==="claude"&&!!c.sessionId;d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:g,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=g,l.state="live",n.pendingHandoffHydrate={artifact:rs,at:u},it(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:w,toAgent:r.toAgent,fromSessionId:m,toSessionId:c.sessionId||null,epoch:d.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await L({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:"done",recovered:n.handoff.recovered,fromBox:r.fromBox,toBox:r.toBox}).catch(()=>{});continue}}let a=!s&&o.recovered==="rolled-back"&&r.toBox&&r.toBox!==n.leadBox?r.toBox:null,i=!s&&o.step==="done"?r.fromBox:null;n.handoff=o,await L({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Wt(i).catch(()=>{}),a&&await Wt(a).catch(()=>{})}return e&&await K(),e}async function Ji(t,e,n=!0,r=null){let o=Cr.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=Q(await H(),t);if(!s)throw new Error(`track "${t}" not found`);let a=await ns(e,n),i=await G(s.leadBox);if(!i)throw new Error(`lead box "${s.leadBox}" is not running`);let c={trackId:t,step:"checkpoint",fromBox:s.leadBox,toAgent:e,skipPermissions:n,reason:hp(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Cr.set(t,c),await Zo(t,{step:c.step,fromBox:c.fromBox,toAgent:e,toBox:c.toBox,skipPermissions:n,startedAt:c.startedAt,finishedAt:null,error:null}),v0(c,s,i,a,e).catch(async l=>{c.error=l?.message||String(l),c.finishedAt=Date.now(),console.error(`[lead-handoff ${t}] FAILED at step "${c.step}": ${c.error}`),await Zo(t,{step:c.step,error:c.error,finishedAt:c.finishedAt}).catch(()=>{}),L({flow:"lead-handoff",phase:"failed",trackId:t,step:c.step,error:c.error,fromBox:c.fromBox,toAgent:c.toAgent,toBox:c.toBox}).catch(()=>{})}),L({flow:"lead-handoff",phase:"started",trackId:t,fromBox:c.fromBox,toAgent:e,cwd:i.cwd}).catch(()=>{}),c}async function v0(t,e,n,r,o){let s=async(T,E={})=>{t.step=T,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${T}`),await Zo(t.trackId,{step:T,toBox:t.toBox}),await L({flow:"lead-handoff",phase:T,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:E.sessionName,...E})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await H(),i=Q(a,t.trackId)?.boxes.find(T=>T.name===t.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;await s("checkpoint",{sessionName:t.fromBox}),await Nl(t.fromBox),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await ra(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await we({cwd:n.cwd,sessionId:d.sessionId,sessionName:t.fromBox,agent:o,agentCmd:es(o)});let f=await pt(t.fromBox,{lines:Rt}).catch(()=>"");if(!io(o,f)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let T=mp(c,l,{skipPermissions:t.skipPermissions});await ra(t.fromBox,{agentCmd:T,agent:c,resumeSessionId:l,pinned:u});let E=await H(),J=Q(E,t.trackId);if(J){let M=J.boxes.find(I=>I.name===t.fromBox);M&&(M.state="live"),it(J,"lead-handoff-failed",{box:t.fromBox,fromAgent:c,toAgent:o,reason:t.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:t.startedAt}),await K()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let w=await H(),m=Q(w,t.trackId);if(!m)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let g=m.boxes.find(T=>T.name===t.fromBox);if(!g)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let x=Date.now(),A=Bp(g),S=A[A.length-1];S.endedAt=x,t.reason&&(S.endReason=t.reason),A.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:x,endedAt:null}),g.agent=o,g.sessionId=d.sessionId,g.pinned=d.pinned,g.state="live",g.role="lead",m.pendingHandoffHydrate={artifact:rs,at:x},it(m,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:A.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:t.skipPermissions,reason:t.reason,handoffStartedAt:t.startedAt}),await K(),await s("done",{sessionName:t.fromBox}),await Zo(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var T0=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,A0=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,Kn=U.join(Ie,"worktrees"),ji=[Yo,Hi],Zt=new Map;var Jn=t=>{let e=new Map([...Zt.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=Yt?Q(Yt,t):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))e.has(r)||e.set(r,o);return[...e.values()].sort((r,o)=>r.startedAt-o.startedAt)},zi=t=>({id:t.id,trackId:t.trackId,trackName:t.trackName,agent:t.agent,task:t.task,skipPermissions:t.skipPermissions,reason:t.reason||null,step:t.step,failedStep:t.failedStep||null,box:t.box,branch:t.branch,base:t.base,dir:t.dir,stat:t.stat,files:t.files||null,conflicts:t.conflicts||[],applied:t.applied,error:t.error,canceled:t.canceled,startedAt:t.startedAt,finishedAt:t.finishedAt});async function _0(t,e,n){let r=await H(),o=Q(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await K())}var Mr=t=>_0(t.trackId,t.id,zi(t));async function Gn(t,e){t.step=e,await Mr(t)}function E0(t,e=Date.now()){if(!t||t.finishedAt)return null;let n=t.step;return["snapshot","spawn","working"].includes(n)?{...t,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${n}"; no changes were applied to the lead \u2014 retry the delegation`,recovered:"rolled-back"}:{...t,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${n}"; the lead's working tree may hold a PARTIAL merge \u2014 review git status. The subtask's work is preserved on branch ${t.branch}`,recovered:"partial-merge"}}function I0(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function R0(t,e,n){if(!I0(n)||Zt.has(e)||!await G(n.box).catch(()=>null))return!1;try{await P.access(n.dir)}catch{return!1}let o=await G(t.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:e,leadBox:t.leadBox,step:"working",canceled:!1,forceMerge:!1};return Zt.set(e,s),Hp(s,o).catch(a=>Up(s,a)),!0}async function jp(){let t=await H(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Zt.has(r))continue;if(await R0(n,r,o)){e=!0,it(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await L({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=E0(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!an.has(i.state)&&(i.state="canceled"),it(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await L({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await fp(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||an.has(r.state)||Zt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?U.join(Kn,`${n.id}-${r.subtaskId}`):null;await L({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await fp(n,r.name,o)}}return e=await $0(t)||e,e&&await K(),e}async function fp(t,e,n){if(e&&await Wt(e).catch(()=>{}),n){let r=await G(t.leadBox).catch(()=>null);r?.cwd&&await Ze(r.cwd,n).catch(()=>{})}}async function $0(t){let e;try{e=await P.readdir(Kn)}catch{return!1}let n=new Set;for(let o of t.tracks||[]){for(let s of Object.keys(o.subtasks||{}))n.add(`${o.id}-${s}`);for(let s of o.boxes||[])s.subtaskId&&n.add(`${o.id}-${s.subtaskId}`)}for(let o of Zt.values())o.dir&&n.add(U.basename(o.dir));let r=!1;for(let o of e){if(n.has(o))continue;let s=U.join(Kn,o),a=(t.tracks||[]).find(c=>o.startsWith(`${c.id}-`)),i=a?await G(a.leadBox).catch(()=>null):null;i?.cwd?await Ze(i.cwd,s).catch(()=>{}):await P.rm(s,{recursive:!0,force:!0}).catch(()=>{}),await L({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}var Xi=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Pr=class extends Error{};async function Lp(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Mn(n))throw new Error("task (string) required");let s=await ns(e,r),a=Q(await H(),t);if(!a)throw new Error(`track "${t}" not found`);if(a.workspace?.kind==="multi")throw new Error("subtask delegation isn't available on a multi-repo Track yet \u2014 ask the lead directly, or use a single-repo Track");let i=await G(a.leadBox);if(!i)throw new Error(`lead box "${a.leadBox}" is not running`);if(!i.cwd)throw new Error("can't resolve the lead's working directory");if(!await ja(i.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${i.cwd})`);let c=Qo().slice(0,8),l={id:c,trackId:t,trackName:a.name,agent:e,task:Mn(n),skipPermissions:r,reason:hp(o),step:"snapshot",leadBox:a.leadBox,box:null,branch:`bostrat/${Xi(a.name)}-${c}`,base:null,dir:U.join(Kn,`${t}-${c}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Zt.set(c,l),await Mr(l),O0(l,i,s).catch(u=>Up(l,u)),l}async function Up(t,e){t.failedStep=t.step,t.error=e instanceof Pr?"canceled":e?.message||String(e),t.step=e instanceof Pr?"canceled":"error",t.finishedAt=Date.now(),await Zi(t).catch(()=>{}),await Mr(t).catch(()=>{}),await N0(t).catch(()=>{})}function Yi(t){let e=Zt.get(t);if(!e)throw new Error(`subtask "${t}" not found`);if(e.finishedAt)throw new Error("subtask already finished");return e.canceled=!0,e}function Fp(t){let e=Zt.get(t);if(!e)throw new Error(`subtask "${t}" not found`);if(e.finishedAt)throw new Error("subtask already finished");if(e.step!=="working")throw new Error(`subtask is not waiting to merge (step: ${e.step})`);return e.forceMerge=!0,e}async function O0(t,e,n){await Gn(t,"snapshot"),t.base=await Fu(e.cwd,`bostrat: snapshot for subtask ${t.id}`),await Gn(t,"spawn"),await P.mkdir(Kn,{recursive:!0}),await yo(e.cwd,t.dir,t.branch,t.base),await Ui(t.dir),await C0(t,e);let r=t.trackName.slice(0,38).trim(),o=await Ue(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await M0(t,o),await wp(t.trackId,o.name,Gx(t.task),{kind:"subtask"}),await Hp(t,e)}async function Hp(t,e){await Gn(t,"working");let n=U.join(t.dir,Hi);if(await D0(n,t),t.canceled)throw new Pr;await Gn(t,"merge"),await Hu(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await qu(e.cwd,t.base,t.branch,ji),t.files=await Gu(e.cwd,t.base,t.branch,ji);let r=await Ku(e.cwd,t.base,t.branch,ji);t.applied=r.applied,t.conflicts=r.conflicts,await Mr(t);let o=await B0(t,e,n);await Gn(t,"resume"),await wp(t.trackId,t.leadBox,Kx(t.task,t.stat,t.conflicts,o),{kind:"subtask-resume"}),await Gn(t,"reap"),await Zi(t),t.step="done",t.finishedAt=Date.now(),await P0(t),await Mr(t)}async function C0(t,e){let n=U.join(t.dir,U.dirname(Yo));await P.mkdir(n,{recursive:!0});let r="";try{r=await hl(e.cwd,e.sessionId)}catch{}if(!r)try{r=await P.readFile(U.join(e.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${t.id}
221
+
222
+ **Delegated task:** ${t.task}
223
+
224
+ **Lead agent:** ${e.agent||"claude"} \xB7 **workspace:** ${e.cwd}
225
+
226
+ ---
227
+
228
+ ## Conversation so far (lead \u2192 you)
229
+
230
+ ${r}
231
+ `;await P.writeFile(U.join(t.dir,Yo),o)}async function M0(t,e){let n=await H(),r=Q(n,t.trackId);r&&(r.boxes.push({name:e.name,agent:e.agent,role:"subtask",state:"live",cwd:e.cwd,sessionId:e.sessionId,createdAt:Date.now(),subtaskId:t.id,branch:t.branch,task:t.task}),it(r,"subtask-delegated",{to:e.name,toAgent:t.agent,task:t.task,subtaskId:t.id,skipPermissions:t.skipPermissions,reason:t.reason}),r.subtasks=r.subtasks||{},r.subtasks[t.id]={...r.subtasks[t.id]||{},...zi(t)},await K())}async function P0(t){let e=await H(),n=Q(e,t.trackId);if(!n)return;let r=n.boxes.find(o=>o.subtaskId===t.id);r&&(r.state=t.conflicts.length?"conflict":"merged",r.stat=t.stat),it(n,"subtask-merged",{from:t.box,fromAgent:t.agent,task:t.task,subtaskId:t.id,stat:t.stat,conflicts:t.conflicts.length,files:t.files,durationMs:t.finishedAt?t.finishedAt-t.startedAt:null}),await K()}async function N0(t){let e=await H(),n=Q(e,t.trackId);if(!n)return;let r=t.canceled||t.error==="canceled",o=n.boxes.find(s=>s.subtaskId===t.id);o&&(o.state=r?"canceled":"failed"),it(n,r?"subtask-canceled":"subtask-failed",{from:t.box,fromAgent:t.agent,task:t.task,subtaskId:t.id,failedStep:t.failedStep||t.step,error:t.error,durationMs:t.finishedAt?t.finishedAt-t.startedAt:null}),n.subtasks=n.subtasks||{},n.subtasks[t.id]={...n.subtasks[t.id]||{},...zi(t)},await K()}async function B0(t,e,n){let r=`.bostrat/handoff/subtask-${t.id}.md`;try{let o=U.join(e.cwd,r);await P.mkdir(U.dirname(o),{recursive:!0}),await P.copyFile(n,o)}catch{}return r}async function Zi(t){if(t.box&&await Wt(t.box).catch(()=>{}),t.dir){let n=(await G(t.leadBox).catch(()=>null))?.cwd;n&&await Ze(n,t.dir).catch(()=>{})}}async function D0(t,e){let n=(e.startedAt||Date.now())+T0;for(;;){if(e.canceled||e.forceMerge)return;try{await P.access(t);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await Qi(A0)}}function Qi(t){return new Promise(e=>setTimeout(e,t))}import Fr from"node:fs/promises";import ws from"node:path";import _S from"node:os";import j0 from"node:fs/promises";import Wp from"node:path";import L0 from"node:os";var qp=()=>Wp.join(process.env.BOSTRAT_STATE_DIR||Wp.join(process.env.HOME||L0.tmpdir(),".bostrat"),"integrations.json");async function cn(){try{return JSON.parse(await j0.readFile(qp(),"utf8"))||{}}catch{return{}}}var ss=["linked","prOpened","prMerged","create"],tc=["prompt","auto","off"],U0={create:["prompt","off"]},Gp=t=>U0[t]??tc;function as(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of ss){let s=Gp(o),a=s.includes(n)?n:"prompt",i=t.ticketFlow?.[o]??e.ticketFlow?.[o];r[o]=s.includes(i)?i:a}return r}async function ln(){let t=await cn(),e=process.env.BOSTRAT_LINEAR_API_KEY||t.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||t.linear?.defaultTeam||null;return e?{apiKey:e,ticketFlow:as(t),defaultTeam:n}:null}async function is(){let t=await cn(),e=t.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||e.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||e.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||e.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:e.tokenExpiresAt??null,ticketFlow:as(t)}}async function ec(){return as(await cn())}async function Kp(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${ss.join(", ")}`);for(let[r,o]of e){if(!ss.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${ss.join(", ")}`);let s=Gp(r);if(!s.includes(o))throw new Error(s.length<tc.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${tc.join(", ")}`)}let n=await cn();return n.ticketFlow={...as(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await qt(qp(),n,{mode:384}),n.ticketFlow}async function Br(){let t=(await cn()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function Vp(){let t=(await cn()).cedar||{},e=process.env.BOSTRAT_CEDAR_INGEST_KEY||t.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||t.url||"https://cedar-api.fly.dev";return e?{url:n,ingestKey:e}:null}async function un(){let t=(await cn()).slack||{},e=process.env.BOSTRAT_SLACK_BOT_TOKEN||t.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||t.appToken;return!e||!n?null:{botToken:e,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||t.channel||null,maxConcurrent:Number.isInteger(t.maxConcurrent)&&t.maxConcurrent>0?t.maxConcurrent:2}}import Lr from"node:fs/promises";import om from"node:path";import fS from"node:os";import{randomUUID as sm}from"node:crypto";var Jp=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function F0(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function zp(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function ls(t,{boxLabels:e={}}={}){let n=(t||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=new Set,u=Jp();for(let d of n){let f=d.payload?.model||"unknown",w=d.provider||"unknown",m=d.boxId||"unattributed",g=F0(d.payload?.tokens),x=Number(d.payload?.usd)||0;!x&&g&&(x=Kt(f,g),x>0&&i++);let A=g?g.input+g.output+g.cacheRead+g.cacheWrite:0;if(!x&&A>0&&!Do(f)&&(c++,l.add(f)),a+=x,zp(u,g),r[f]||={usd:0,tokens:Jp(),events:0},r[f].usd+=x,r[f].events+=1,zp(r[f].tokens,g),o[w]||={usd:0,events:0},o[w].usd+=x,o[w].events+=1,s[m]||={usd:0,events:0,provider:w,models:new Set,label:e[m]||m},s[m].usd+=x,s[m].events+=1,f!=="unknown"&&s[m].models.add(f),d.epoch!=null){let S=(s[m].epochs||={})[d.epoch]||={usd:0,events:0,provider:w,models:new Set};S.usd+=x,S.events+=1,S.provider=w,f!=="unknown"&&S.models.add(f)}}return{totalUsd:Dr(a),totalTokens:u,turns:n.length,repricedTurns:i,unpricedTurns:c,unpricedModels:[...l],byModel:cs(r,d=>({...d,usd:Dr(d.usd)})),byProvider:cs(o,d=>({...d,usd:Dr(d.usd)})),byBox:cs(s,d=>({usd:Dr(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:cs(d.epochs,f=>({usd:Dr(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var Dr=t=>Math.round(t*1e4)/1e4,cs=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));var rc={};zc(rc,{createComment:()=>X0,findIssueUrl:()=>G0,issueStates:()=>Q0,mdToAdf:()=>Zp,moveIssue:()=>tS,parseIssueRef:()=>q0,resolveIssue:()=>K0,uploadImage:()=>Y0,upsertCard:()=>V0,whoami:()=>nc});var zn=t=>String(t.baseUrl||"").replace(/\/$/,""),Yp=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function dn(t,e,n,r){let o=await fetch(`${zn(t)}${n}`,{method:e,headers:{authorization:Yp(t),accept:"application/json",...r!==void 0?{"content-type":"application/json"}:{}},body:r!==void 0?JSON.stringify(r):void 0}),s=await o.text(),a=null;try{a=s?JSON.parse(s):null}catch{a=null}if(!o.ok){let i=a?.errorMessages?.[0]??Object.values(a?.errors??{})[0]??`HTTP ${o.status}`,c=new Error(`jira: ${i}`);throw c.status=o.status,c}return a}var H0=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,W0=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function q0(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(H0);return r?r[1].toUpperCase():null}let n=e.match(W0);return n?n[1]:null}function G0(t,e=null){let n=null;try{n=e?.baseUrl?new URL(e.baseUrl).host:null}catch{n=null}let r=/https?:\/\/[^\s)]+\/browse\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/g;for(let o of String(t||"").matchAll(r))try{let s=new URL(o[0]).host;if(s===n||/\.atlassian\.net$/i.test(s))return o[0]}catch{}return null}async function K0(t,e){let n;try{n=await dn(t,"GET",`/rest/api/3/issue/${encodeURIComponent(e)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${e}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${zn(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function V0(t,e,{url:n,title:r,subtitle:o}){await dn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/remotelink`,{globalId:n,application:{type:"com.bostrat.tracks",name:"Bostrat"},object:{url:n,title:r,summary:o,icon:{title:"Bostrat"}}})}var J0=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function z0(t){let e=[],n=0;for(let r of t.matchAll(J0)){r.index>n&&e.push({type:"text",text:t.slice(n,r.index)});let o=r[0];if(o.startsWith("![")){let[,s,a]=o.match(/^!\[([^\]]*)\]\(([^)\s]+)\)$/);e.push({type:"text",text:s||a,marks:[{type:"link",attrs:{href:a}}]})}else o.startsWith("**")?e.push({type:"text",text:o.slice(2,-2),marks:[{type:"strong"}]}):o.startsWith("`")?e.push({type:"text",text:o.slice(1,-1),marks:[{type:"code"}]}):o.startsWith("_")?e.push({type:"text",text:o.slice(1,-1),marks:[{type:"em"}]}):e.push({type:"text",text:o,marks:[{type:"link",attrs:{href:o}}]});n=r.index+o.length}return n<t.length&&e.push({type:"text",text:t.slice(n)}),e}function Zp(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
232
+ `).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...z0(o))}),{type:"paragraph",content:r.length?r:[{type:"text",text:" "}]}});return{type:"doc",version:1,content:e.length?e:[{type:"paragraph",content:[{type:"text",text:" "}]}]}}async function X0(t,e,n){let r=await dn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:Zp(n)});return r?.id?`${zn(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function nc(t){let e=await dn(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(zn(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function Y0(t,{issue:e,filename:n,contentType:r,buffer:o}){let s=new FormData;s.append("file",new Blob([o],{type:r}),n);let a=await fetch(`${zn(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:Yp(t),"x-atlassian-token":"no-check",accept:"application/json"},body:s}),i=await a.json().catch(()=>null),c=Array.isArray(i)?i[0]:null;if(!a.ok||!c)throw new Error(`jira: attachment upload failed (HTTP ${a.status})`);return c.content??`${zn(t)}/rest/api/3/attachment/content/${c.id}`}var Z0={new:"unstarted",indeterminate:"started",done:"completed"},Xp=t=>Z0[t?.key]??"started";async function Q0(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([dn(t,"GET",`/rest/api/3/issue/${n}?fields=status`),dn(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:Xp(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:Xp(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function tS(t,e,n){await dn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var ic={};zc(ic,{createComment:()=>cS,createIssue:()=>ac,createProject:()=>sc,findIssueUrl:()=>sS,issueStates:()=>uS,moveIssue:()=>dS,parseIssueRef:()=>oS,resolveIssue:()=>aS,teams:()=>Xn,uploadImage:()=>lS,upsertCard:()=>iS,whoami:()=>oc});var eS=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Qt(t,e,n={}){let r=await fetch(eS(),{method:"POST",headers:{"content-type":"application/json",authorization:t.apiKey},body:JSON.stringify({query:e,variables:n})}),o=await r.json().catch(()=>null);if(!r.ok)throw new Error(`linear: HTTP ${r.status}${o?.errors?.[0]?.message?` \u2014 ${o.errors[0].message}`:""}`);if(o?.errors?.length)throw new Error(`linear: ${o.errors[0].message}`);return o?.data??{}}var nS=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,rS=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function oS(t){let e=String(t||"").trim(),n=e.match(nS);if(n)return n[1].toUpperCase();let r=e.match(rS);return r?r[1]:null}function sS(t){let e=String(t||"").match(/https?:\/\/linear\.app\/[^/\s]+\/issue\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/);return e?e[0]:null}async function aS(t,e){let r=(await Qt(t,"query($id: String!) { issue(id: $id) { id identifier title url state { name } team { key } } }",{id:e})).issue;if(!r)throw new Error(`linear: issue "${e}" not found`);return{issueId:r.id,key:r.identifier,title:r.title,url:r.url,state:r.state?.name??null,team:r.team?.key??null}}async function iS(t,e,{url:n,title:r,subtitle:o}){let s=await Qt(t,"mutation($input: AttachmentCreateInput!) { attachmentCreate(input: $input) { success attachment { id } } }",{input:{issueId:e.issueId,url:n,title:r,subtitle:o}});if(!s.attachmentCreate?.success)throw new Error("linear: attachmentCreate did not succeed");return s.attachmentCreate.attachment?.id??null}async function cS(t,e,n){let r=await Qt(t,"mutation($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { url } } }",{input:{issueId:e.issueId,body:n}});if(!r.commentCreate?.success)throw new Error("linear: commentCreate did not succeed");return r.commentCreate.comment?.url??null}async function oc(t){let e=await Qt(t,"query { viewer { name email } organization { name urlKey } }");return{user:e.viewer?.name??e.viewer?.email??null,org:e.organization?.name??e.organization?.urlKey??null}}async function lS(t,{filename:e,contentType:n,buffer:r}){let o=await Qt(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
233
+ fileUpload(contentType: $contentType, filename: $filename, size: $size) {
234
+ success
235
+ uploadFile { uploadUrl assetUrl headers { key value } }
236
+ }
237
+ }`,{contentType:n,filename:e,size:r.length}),s=o.fileUpload?.uploadFile;if(!o.fileUpload?.success||!s?.uploadUrl)throw new Error("linear: fileUpload did not succeed");let a={"content-type":n};for(let c of s.headers||[])a[c.key]=c.value;let i=await fetch(s.uploadUrl,{method:"PUT",headers:a,body:r});if(!i.ok)throw new Error(`linear: asset PUT failed (HTTP ${i.status})`);return s.assetUrl}async function uS(t,e){let n=await Qt(t,"query($id: String!) { issue(id: $id) { state { id name type } team { states { nodes { id name type position } } } } }",{id:e.key});if(!n.issue)throw new Error(`linear: issue "${e.key}" not found`);return{current:n.issue.state??null,states:n.issue.team?.states?.nodes??[]}}async function dS(t,e,n){if(!(await Qt(t,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:e.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function Xn(t){return((await Qt(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}async function sc(t,{teamId:e,name:n,description:r}){let o=await Qt(t,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[e],name:n,...r?{description:r}:{}}}),s=o.projectCreate?.project;if(!o.projectCreate?.success||!s)throw new Error("linear: projectCreate did not succeed");return{id:s.id,url:s.url,name:s.name}}async function ac(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await Qt(t,"mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url title } } }",{input:{teamId:e,title:n,...r?{description:r}:{},...o?{projectId:o}:{},...Number.isInteger(s)?{priority:s}:{}}}),i=a.issueCreate?.issue;if(!a.issueCreate?.success||!i)throw new Error("linear: issueCreate did not succeed");return{id:i.id,key:i.identifier,url:i.url,title:i.title}}var am={linear:{api:ic,config:ln},jira:{api:rc,config:is}};async function pc(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(am)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function fn(t){let e=am[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function mc(t,e){if(!t||t.name)return t;let n=e.find(r=>r.id===t.id);return n?{...t,name:n.name}:t}var im=()=>process.env.BOSTRAT_STATE_DIR||om.join(process.env.HOME||fS.tmpdir(),".bostrat"),dc=()=>om.join(im(),"tickets.json"),cm=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),ds=t=>`${cm()}/track/${t}`,us=null,cc=null,lc=Promise.resolve();async function fs(){return us||(cc??=Lr.readFile(dc(),"utf8").then(t=>JSON.parse(t)?.tickets??{}).catch(()=>({})).then(t=>us=t).finally(()=>cc=null),cc)}async function Qp(){let t=JSON.stringify({tickets:us},null,2),e=`${dc()}.${process.pid}.tmp`;await Lr.mkdir(im(),{recursive:!0}),await Lr.writeFile(e,t),await Lr.rename(e,dc())}async function Oe(){lc=lc.then(Qp,Qp),await lc.catch(()=>{})}async function te(t){return(await fs())[t]??null}async function lm(){return{...await fs()}}async function um(){return(await pc()).length>0}async function Yn(t,e,{source:n="api"}={}){let r=await pc();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let d=u.api.parseIssueRef(e);if(d)try{s=await u.api.resolveIssue(u.cfg,d),o=u;break}catch(f){a=f}}if(!o)throw a||new Error(`"${e}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(t.boxes||[]).find(u=>u.name===t.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:t.name,branch:t.workspace?.branch??null,leadAgent:i?.agent??null,cwd:t.cwd??null},pr:null};(await fs())[t.id]=c,await Oe(),await R({id:t.id,cwd:t.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await Ur(t.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await jr(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await pm(t.id,c,"linked"),c}async function hc(t){let e=await fs();return e[t]?(delete e[t],await Oe(),!0):!1}var fc=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function dm(t,e){if(!t.track?.cwd)return null;let n=ls(await B({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function pS(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${fc(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${fc(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function mS(t,e){let n=[];return t.track.leadAgent&&n.push(`lead ${t.track.leadAgent}`),t.track.branch&&n.push(t.track.branch),t.pr?.number&&n.push(`PR #${t.pr.number} ${t.pr.change==="merged"?"merged":"open"}`),e&&n.push(fc(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function Ur(t,e){let n=await fn(e);if(!n)return;let r=await dm(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:ds(t),title:`Bostrat Track: ${e.track.name}`,subtitle:mS(e,r)})}function ps(t){let e=new URL(cm());return`${e.protocol}//${t}.${e.host}/`}var hS={linked:"linked",opened:"prOpened",merged:"prMerged"};function wS(t,e,n){let r=o=>e.filter(s=>s.type===o).sort((s,a)=>s.position-a.position);if(n==="linked"){if(["triage","backlog","unstarted"].includes(t?.type))return r("started")[0]??null}else if(n==="opened"){if(t?.type!=="completed")return r("started").find(o=>/review/i.test(o.name))??null}else if(n==="merged"&&t?.type!=="completed")return r("completed")[0]??null;return null}async function fm(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await Oe(),await R({id:t,cwd:e.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:e.key,url:e.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function pm(t,e,n){let r=await fn(e);if(!r)return;let o=r.cfg.ticketFlow?.[hS[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=mc(s,a),c=wS(i,a,n);if(!c||c.id===i?.id)return;let l=i?.name??e.state??null;if(o==="auto"){await r.api.moveIssue(r.cfg,e,c.id),await fm(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await R({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:sm(),provider:e.provider,key:e.key,url:e.url,phase:n,from:l,to:{id:c.id,name:c.name,type:c.type},...e.pr?.number?{prNumber:e.pr.number}:{}}}).catch(()=>{})}async function mm(t){let e=await te(t);if(!e)throw new Error("no ticket linked to this Track");let n=await fn(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=mc(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await Oe()),{key:e.key,url:e.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function wc(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await te(t);if(!r)throw new Error("no ticket linked to this Track");let o=await fn(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=mc(s,a),c=a.find(u=>u.id===e);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await Oe()),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await fm(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function hm(t,e){let n=await te(t);if(!n)throw new Error("no ticket linked to this Track");let r=await fn(n);if(!r)throw new Error("no ticketing integration configured");let o=String(e||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await R({id:t,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function wm(t,e,n){if(!e)throw new Error("offerId required");let r=await B({id:t.id,cwd:t.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===e);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===e))throw new Error("offer already answered");let s=null;return n&&(s=await wc(t.id,o.payload.to?.id,{mode:"offer"})),await R({id:t.id,cwd:t.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:e,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:e,action:n?"moved":"dismiss",...s??{}}}var tm=50;async function gm(){let t=await ln();if(!t)throw new Error("no ticketing integration configured");return Xn(t)}function gS(t){let e=t&&typeof t=="object"?t:{},n=(Array.isArray(e.issues)?e.issues:[]).map(o=>o&&typeof o=="object"?o:{title:String(o||"")}).map(o=>({title:String(o.title||"").trim(),description:o.description?String(o.description):null,priority:Number.isInteger(o.priority)&&o.priority>=0&&o.priority<=4?o.priority:null})).filter(o=>o.title),r=e.epic&&String(e.epic.name||"").trim()?{name:String(e.epic.name).trim(),description:e.epic.description?String(e.epic.description):null}:null;if(!n.length&&!r)throw new Error("a plan needs an epic or at least one issue");if(n.length>tm)throw new Error(`too many issues (${n.length}); cap is ${tm} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function yS(t,e,n,r){let o=r??await Xn(t),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(e.team){let i=s(e.team);if(i)return i}let a=await te(n.id);if(a?.key){let i=a.key.split("-")[0],c=o.find(l=>l.key===i);if(c)return c}if(t.defaultTeam){let i=s(t.defaultTeam);if(i)return i}return o.length===1?o[0]:null}async function ym(t,e){let n=await ln();if(!n)throw new Error("no ticketing integration configured \u2014 add a linear apiKey to ~/.bostrat/integrations.json");if(n.ticketFlow?.create==="off")throw new Error("ticket creation offers are off (bostrat-ticket flow set create=prompt to re-enable)");let r=gS(e),o=await Xn(n),s=await yS(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=sm();return await R({id:t.id,cwd:t.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function bm(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await B({id:t.id,cwd:t.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===e);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===e))throw new Error("proposal already answered");if(!n)return await R({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"dismiss"}}),{proposalId:e,action:"dismiss"};let a=await ln();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Xn(a)).find(g=>g.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let m=await sc(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=m.id,l.push({kind:"epic",title:m.name,url:m.url})}for(let m of i.issues||[])try{let g=await ac(a,{teamId:c.id,title:m.title,description:m.description,projectId:d,priority:m.priority});l.push({kind:"issue",key:g.key,title:g.title,url:g.url})}catch(g){u.push({title:m.title,message:g?.message||String(g)})}await R({id:t.id,cwd:t.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,team:{key:c.key,name:c.name},items:l,failed:u}}),await R({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"created",createdCount:l.length,failedCount:u.length}});let f=l.filter(m=>m.kind==="issue"),w=null;return i.linkCurrentTrack&&f.length===1&&!await te(t.id)&&(w=await Yn(t,f[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:w?w.key:null}}async function jr(t,e){let n=await fn(t);n&&await n.api.createComment(n.cfg,t,e)}async function bS(t,e,n){let r=await fn(t);if(!r)return null;let o=await Ae(e);if(!o)return null;let s=rn(o,n);if(!s)return null;let a=await Lr.readFile(Bt(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:t.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var em=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),uc=new Map;function kS(t,e){if(em()<=0)return Ur(t,e);if(uc.has(t))return;let n=setTimeout(()=>{uc.delete(t),te(t).then(r=>r?Ur(t,r):null).catch(()=>{})},em());n.unref?.(),uc.set(t,n)}var nm=new Map;async function rm(t,e,n){let r=nm.get(t)??0;Date.now()-r<600*1e3||(nm.set(t,Date.now()),await R({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function xS(t,{resolveTrack:e=null}={}){let n=await te(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await pc())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=e?await e(t.trackId):null;if(!s)return;try{await Yn(s,o,{source:"chat"})}catch(a){await rm(t.trackId,s.cwd,a)}return}if(!n.track.cwd&&e){let o=await e(t.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await Oe())}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Oe(),await Ur(t.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await Oe(),await Ur(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await dm(n,t.trackId).catch(()=>null):null;await jr(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
238
+
239
+ Agent cost for this Track: ${pS(o)}.`:"")),await pm(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await bS(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await jr(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
240
+
241
+ ![${r.name}](${o})`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await jr(n,`**Bostrat** \u2014 design decision: **${r.name}** picked${o?` over ${o}`:""}.`)}break}case"view.state.changed":{if(r.change!=="created"||r.restarted||!r.slug)break;await jr(n,`**Bostrat** \u2014 live preview **${r.name}**: ${ps(r.slug)}
242
+ _(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await kS(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await rm(t.trackId,s?.cwd??null,o)}}function km({resolveTrack:t}){Pt.on("event",e=>{xS(e,{resolveTrack:t}).catch(()=>{})})}import SS from"ws";var xm=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function ms(t,e,n={}){let r=await fetch(`${xm()}/${t}`,{method:"POST",headers:{authorization:`Bearer ${e}`,"content-type":"application/json; charset=utf-8"},body:JSON.stringify(n)}),o=await r.json().catch(()=>null);if(!o?.ok)throw new Error(`slack ${t}: ${o?.error||`http ${r.status}`}`);return o}var gc=(t,e)=>ms("chat.postMessage",t,e),Zn=(t,e)=>ms("chat.update",t,e);async function Et(t,e){await fetch(t,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(e)}).catch(()=>{})}async function vS(t,e,n={}){let r=await fetch(`${xm()}/${t}`,{method:"POST",headers:{authorization:`Bearer ${e}`,"content-type":"application/x-www-form-urlencoded; charset=utf-8"},body:new URLSearchParams(n).toString()}),o=await r.json().catch(()=>null);if(!o?.ok)throw new Error(`slack ${t}: ${o?.error||`http ${r.status}`}`);return o}async function Sm(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await vS("files.getUploadURLExternal",t,{filename:e,length:String(n.length)}),l=await fetch(i,{method:"POST",body:n});if(!l.ok)throw new Error(`slack upload: http ${l.status}`);return ms("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var TS=2200;function vm({getToken:t,onEnvelope:e,log:n=()=>{}}){let r=!1,o=null,s=null,a=1e3;async function i(){if(r)return;let l;try{let u=await t();if(!u)throw new Error("app token missing");({url:l}=await ms("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new SS(l),o.on("message",u=>{let d;try{d=JSON.parse(u)}catch{return}if(d.type==="hello"){a=1e3,n("socket connected");return}if(d.type==="disconnect"){try{o.close()}catch{}return}if(!d.envelope_id)return;let f=g=>{try{o.send(JSON.stringify(g?{envelope_id:d.envelope_id,payload:g}:{envelope_id:d.envelope_id}))}catch{}},w=!1,m=setTimeout(()=>{w=!0,f()},TS);Promise.resolve().then(()=>e(d)).then(g=>{clearTimeout(m),!w&&(w=!0,f(d.accepts_response_payload?g:void 0))},g=>{clearTimeout(m),w||(w=!0,f()),n(`envelope handler failed: ${g?.message||g}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var _m=()=>process.env.BOSTRAT_STATE_DIR||ws.join(process.env.HOME||_S.tmpdir(),".bostrat"),Sc=()=>ws.join(_m(),"slack.json"),hs=null,yc=null,bc=Promise.resolve();async function Tc(){return hs||(yc??=Fr.readFile(Sc(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>hs=t).finally(()=>yc=null),yc)}async function Tm(){let t=JSON.stringify({threads:hs},null,2),e=`${Sc()}.${process.pid}.tmp`;await Fr.mkdir(_m(),{recursive:!0}),await Fr.writeFile(e,t),await Fr.rename(e,Sc())}async function ue(){bc=bc.then(Tm,Tm),await bc.catch(()=>{})}async function Ac(t){return(await Tc())[t]??null}async function ES(t,e){let n=await Tc();for(let[r,o]of Object.entries(n))if(o.channel===t&&o.ts===e)return{trackId:r,rec:o};return null}var F=t=>String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");function Hr(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function IS(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return Hr(n.replace(/\n+/g," "),e)}function Em(t){let e=`*${F(t.name)}*${t.archivedAt?" \xB7 _archived_":""}`,n=`${F(t.repoLabel)} \xB7 \`${F(t.branch||"?")}\` \xB7 lead *${F(t.lead)}*`,r=t.kickoff?`> ${F(Hr(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
243
+ ${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${ds(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function vc(t,e){await Zn(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:Em(e)})}async function mn(t,e,n,r=void 0){return gc(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var Am=new Set;function pn(t){Am.add(t),t.catch(()=>{}).finally(()=>Am.delete(t))}var tt=t=>({response_type:"ephemeral",text:t}),RS="*/bostrat* \u2014 `start <name> [repo]` \xB7 `archive <track>` \xB7 `handoff <track> <agent>` \xB7 `stop <track>` \xB7 `list`\n_Reply in a Track's thread to steer its lead; question prompts and ship offers appear there as buttons._",$S=["claude","cursor","codex"];function kc(t,e){let n=(e||"").toLowerCase(),r=t.filter(s=>!s.archivedAt),o=r.filter(s=>s.name.toLowerCase().startsWith(n));return o.sort((s,a)=>(a.lastResponseAt??a.createdAt??0)-(s.lastResponseAt??s.createdAt??0)),{match:o[0]??null,live:r}}var xc=(t,e)=>tt(`No live Track matches \u201C${F(e)}\u201D. Live: ${t.map(n=>`\`${F(n.name)}\``).join(" \xB7 ")||"none"}`);async function Im(t,e,{name:n,repo:r,responseUrl:o}){let s=t.deps;try{let a=(await s.listTracks()).filter(d=>!d.archivedAt);if(a.length>=e.maxConcurrent){await Et(o,{...tt(`At capacity \u2014 ${a.length} live Tracks (limit ${e.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!e.channel){await Et(o,{...tt("No designated channel configured \u2014 set `slack.channel` in integrations.json."),replace_original:!0});return}let i=await s.createTrack({name:n,...r?{repo:r}:{}}),c=i.workspace||{},l={trackId:i.id,channel:e.channel,name:i.name,branch:c.branch||null,repoLabel:c.repo||(c.base?ws.basename(c.base):"fresh workspace"),lead:"claude"},u=await gc(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:Em(l),unfurl_links:!1});l.ts=u.ts,(await Tc())[i.id]=l,await ue(),await Et(o,{...tt(`\u2713 *${F(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Et(o,{...tt(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function OS(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await Ac(n.id);s&&(s.archivedAt=Date.now(),await ue(),await vc(e,s).catch(()=>{})),await Et(r,{...tt(`\u2713 *${F(n.name)}* archived.`),replace_original:!0})}catch(s){await Et(r,{...tt(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function CS(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await Et(o,{...tt(`\u2713 Handing *${F(n.name)}* to ${F(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await Et(o,{...tt(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function MS(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.interruptLead(n.id);let s=(o.listSubtaskJobs?.(n.id)??[]).filter(a=>!a.finishedAt);for(let a of s)try{o.cancelSubtask(a.id)}catch{}await Et(r,{...tt(`\u2713 Stopped *${F(n.name)}*${s.length?` (+ ${s.length} subtask${s.length>1?"s":""} canceled)`:""} \u2014 Track parked, resume from the Track page.`),replace_original:!0})}catch(s){await Et(r,{...tt(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function PS(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:Hr(`${r.name} (${r.branch})`,75)},value:r.path})),{text:{type:"plain_text",text:"fresh workspace"},value:"__fresh__"}];return[{type:"section",block_id:JSON.stringify({name:t}),text:{type:"mrkdwn",text:`Repo for *${F(t)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function NS(t){let e=await t.listTracks(),n=e.filter(s=>!s.archivedAt);if(n.sort((s,a)=>(a.lastResponseAt??a.createdAt??0)-(s.lastResponseAt??s.createdAt??0)),!n.length)return"No live Tracks.";let r=n.map(s=>{let a=s.workspace||{},i=a.repo||(a.base?ws.basename(a.base):"fresh"),c=[...s.boxes||[]].reverse().find(u=>u.role==="lead")?.agent||"?",l=s.prompted?"\u26A0 needs an answer":s.working?"working":"idle";return`\u2022 *${F(s.name)}* \u2014 ${F(i)}${a.branch?` \`${F(a.branch)}\``:""} \xB7 lead ${F(c)} \xB7 ${l}`}),o=e.length-n.length;return r.join(`
244
+ `)+(o?`
245
+ _${o} archived_`:"")}async function BS(t,e){let n=await un();if(!n)return tt("Slack integration not configured.");let r=t.deps,o=(e.text||"").trim().split(/\s+/).filter(Boolean),s=(o[0]||"help").toLowerCase(),a=o.slice(1);switch(s){case"start":{if(!a.length)return tt("Usage: `/bostrat start <name> [repo]`");let i=await r.listRepos(),c=a[a.length-1]?.toLowerCase(),l=a.length>1?i.find(d=>d.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(pn(Im(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),tt(`Starting *${F(u)}* on ${F(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:PS(u,i)}}case"archive":{if(!a.length)return tt("Usage: `/bostrat archive <track>`");let{match:i,live:c}=kc(await r.listTracks(),a.join(" "));return i?(pn(OS(t,n,{track:i,responseUrl:e.response_url})),tt(`Archiving *${F(i.name)}*\u2026`)):xc(c,a.join(" "))}case"handoff":{let i=r.knownAgents??$S,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return tt(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:d}=kc(await r.listTracks(),l);return u?(pn(CS(t,n,{track:u,agent:c,responseUrl:e.response_url})),tt(`Handing *${F(u.name)}* to ${F(c)}\u2026`)):xc(d,l)}case"stop":{if(!a.length)return tt("Usage: `/bostrat stop <track>`");let{match:i,live:c}=kc(await r.listTracks(),a.join(" "));return i?(pn(MS(t,n,{track:i,responseUrl:e.response_url})),tt(`Stopping *${F(i.name)}*\u2026`)):xc(c,a.join(" "))}case"list":return tt(await NS(r));default:return tt(RS)}}async function DS(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Ac(o);try{if(await t.deps.answerPrompt(o,{promptId:s,answers:[a]}),i?.prompt?.promptId===s){let{ts:c,question:l}=i.prompt;delete i.prompt,await ue(),await Zn(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${F(l||"question")}~
246
+ \u2713 *${F(a)}*`}}]}).catch(()=>{})}}catch(c){await Et(n.response_url,tt(`Couldn't answer: ${c?.message||c}`))}}async function jS(t,e,n,r){let{trackId:o,offerId:s,action:a}=r,i=t.deps;try{let c=await i.resolveTrack(o);if(!c)throw new Error("track not found");let l=await i.readEvents(c),u=l.find(f=>f.type==="pr.offered"&&f.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===s))throw new Error("offer already answered");let d=a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;d&&await i.sayToLead(c.id,To(d,{branch:u.payload.branch,base:u.payload.base}),{kind:d.includes("pr")?"pr-go":"push-go"}),await i.appendEvent({id:c.id,cwd:c.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:u.boxId,payload:{ts:Date.now(),offerId:s,action:d?a:"dismiss",actions:d}})}catch(c){await Et(n.response_url,tt(`Couldn't act on the offer: ${c?.message||c}`))}}async function LS(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await un();if(!r)return;if(n.action_id==="bostrat-start-repo"){let s;try{({name:s}=JSON.parse(n.block_id))}catch{return}let a=n.selected_option?.value;await Et(e.response_url,{...tt(`Starting *${F(s)}*\u2026`),replace_original:!0}),pn(Im(t,r,{name:s,repo:a==="__fresh__"?null:a,responseUrl:e.response_url}));return}let o;try{o=JSON.parse(n.value)}catch{return}n.action_id==="bostrat-prompt"?pn(DS(t,r,e,o)):n.action_id==="bostrat-ship"&&pn(jS(t,r,e,o))}async function US(t,e){if(!e||e.type!=="message"||e.bot_id||e.subtype||!e.thread_ts||e.thread_ts===e.ts||!e.text?.trim())return;let n=await ES(e.channel,e.thread_ts);if(!n)return;let r=await un();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await mn(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function FS(t){return async e=>{if(e.type==="slash_commands")return BS(t,e.payload);if(e.type==="interactive"){await LS(t,e.payload);return}if(e.type==="events_api"){await US(t,e.payload?.event);return}}}var HS=new Set(["turn.user","turn.agent","prompt.raised","prompt.answered","pr.offered","pr.offer.answered","pr.state.changed","handoff.done","view.state.changed","mock.state.changed"]);function WS(t,e){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${F(e.question||"The lead needs an answer")}`}}],r=(e.options||[]).slice(0,12);return r.length?n.push({type:"actions",elements:r.map(o=>({type:"button",action_id:"bostrat-prompt",text:{type:"plain_text",text:Hr(String(o),75)},value:JSON.stringify({trackId:t,promptId:e.promptId,label:String(o)})}))}):n.push({type:"context",elements:[{type:"mrkdwn",text:"_reply in this thread to answer_"}]}),n}async function qS(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await Zn(t.botToken,{channel:e.channel,ts:e.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${F(e.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await mn(t,e,r.question||"The lead needs an answer",WS(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await ue()}async function GS(t,e,n){let r=n.payload||{};if(!e.prompt||r.promptId&&e.prompt.promptId!==r.promptId)return;let{ts:o,question:s}=e.prompt;delete e.prompt,await ue(),await Zn(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${F(s||"question")}~
247
+ \u2713 _answered_`}}]})}async function KS(t,e,n){let r=n.payload||{};if(!r.offerId||e.offers?.[r.offerId])return;let o=(i,c,l)=>({type:"button",action_id:"bostrat-ship",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,action:i}),...l?{style:l}:{}}),s=r.toPr?[o("push",`Commit & push${r.toPr.number?` to #${r.toPr.number}`:""}`,"primary"),o("dismiss","Not now")]:[o("pr","Open PR","primary"),o("push","Just push"),o("dismiss","Not now")],a=await mn(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${F(Hr(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await ue()}async function VS(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await ue();let s=r.action==="dismiss"?"dismissed":r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await Zn(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function JS(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??zS)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await Sm(t.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:e.channel,threadTs:e.ts,initialComment:`\u{1F3A8} mock *${F(o.name)}*${a}${o.group?` (group \`${F(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${F(a.name)}*`).join(", ");await mn(t,e,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${F(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function zS(t,e){let n=await Ae(t);if(!n)return null;let r=rn(n,e);if(!r)return null;let o=await Fr.readFile(Bt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function XS(t,e){if(!HS.has(e.type))return;let n=await Ac(e.trackId);if(!n)return;let r=await un();if(!r)return;let o=e.payload||{};switch(e.type){case"turn.user":if(n.kickoff||e.actor!=="user"||!o.text)return;n.kickoff=o.text,await ue(),await vc(r,n);return;case"turn.agent":{if(!o.text)return;let s=IS(o.text);if(!s)return;await mn(r,n,s,[{type:"section",text:{type:"mrkdwn",text:F(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${ds(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return qS(r,n,e);case"prompt.answered":return GS(r,n,e);case"pr.offered":return KS(r,n,e);case"pr.offer.answered":return VS(r,n,e);case"pr.state.changed":{if(o.change!=="opened"&&o.change!=="merged")return;let s=o.change==="opened"?"PR opened":"PR merged";await mn(r,n,`${s}${o.url?`: ${o.url}`:o.number?` (#${o.number})`:""}`);return}case"handoff.done":if(!o.toAgent||o.toAgent===n.lead)return;n.lead=o.toAgent,await ue(),await vc(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await mn(r,n,`Live preview ${o.name}: ${ps(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${F(o.name)}*: ${ps(o.slug)}
248
+ _runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return JS(r,n,e,t.deps)}}function Rm(t){let e={deps:t};Pt.on("event",s=>{XS(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await un()||(r&&clearInterval(r),r=null,n=vm({getToken:async()=>(await un())?.appToken,onEnvelope:FS(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import YS from"node:fs/promises";function ZS(t){let e=new Map;for(let n of String(t||"").split(`
249
+ `)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;i&&e.set(i,{messageId:i,tokens:a,model:o.message?.model||o.model||null})}return[...e.values()]}function QS(t){let e=new Set;for(let n of t)if(n.type==="cost.recorded"){n.payload?.messageId&&e.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])e.add(r)}return e}function tv(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?ne(e.cwd,e.sessionId):e?.cwd?Ws(e.cwd):null}async function $m(t,e){let n=await e(t?.session_id,t?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await R(r,{...s,...c}))};switch(t?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:t.session_id}});break;case"UserPromptSubmit":await i({type:"turn.user",actor:"user",payload:{promptHead:String(t.prompt??t.user_prompt??"").slice(0,160)||null}});break;case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:{tool:t.tool_name??null}});break;case"Stop":await ev(r,o,t,i);break;default:break}return a}async function ev(t,e,n,r){let o=[],s=tv(n,e);if(s)try{o=ZS(await YS.readFile(s,"utf8"))}catch{}let a=o.length?o[o.length-1]:null;if(await r({type:"turn.agent",actor:"agent",payload:{model:a?.model??e?.model??null,messageId:a?.messageId??null}}),!o.length)return;let i=QS(await B(t)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let d of c){let f=d.model||"unknown",w=u.get(f)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let m of Object.keys(w.tokens))w.tokens[m]+=d.tokens[m];w.messageIds.push(d.messageId),u.set(f,w)}for(let[d,f]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:d,usd:Kt(d,f.tokens),tokens:f.tokens,messageId:f.messageIds[f.messageIds.length-1],messageIds:f.messageIds,apiCalls:f.messageIds.length}})}let l=a.tokens.input+a.tokens.cacheWrite+a.tokens.cacheRead;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:l,tokens:a.tokens}})}import ov from"node:fs";import _c from"node:fs/promises";import sv from"node:http";import Qn from"node:path";import ys from"node:os";import{createRequire as av}from"node:module";import Ec from"ws";import nv from"node:path";import rv from"node:os";var Y$=process.env.BOSTRAT_STATE_DIR||nv.join(process.env.HOME||rv.tmpdir(),".bostrat");function Om(t=process.env){let e=(t.BOSTRAT_HOST||"").trim();if(e!=="all")return e||(t.BOSTRAT_PLANE_URL?"127.0.0.1":void 0)}function Cm(t){let e=String(t||"").trim();if(!e)return"";let n=/^[a-z][a-z0-9+.-]*:\/\//i.test(e)?e:`wss://${e}`,r;try{r=new URL(n)}catch{return e}let o={"https:":"wss:","http:":"ws:","wss:":"wss:","ws:":"ws:"}[r.protocol];return o?(r.protocol=o,(r.pathname===""||r.pathname==="/")&&(r.pathname="/v1/node"),r.toString()):e}var gs=(()=>{try{return av(import.meta.url)("../package.json").version||null}catch{return null}})(),iv=process.env.BOSTRAT_STATE_DIR||Qn.join(process.env.HOME||ys.tmpdir(),".bostrat");function cv(t,e){let n=String(t).split(".").map(Number),r=String(e).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!1;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function lv({home:t=process.env.HOME||ys.homedir(),env:e=process.env}={}){let n=r=>{try{return ov.statSync(r),!0}catch{return!1}};return{claude:n(Qn.join(t,".claude",".credentials.json")),codex:n(Qn.join(e.CODEX_HOME||Qn.join(t,".codex"),"auth.json"))}}var St=1,Mm=1e3,uv=3e4,Pm=100,dv=2e3,fv=25e3;function Nm({url:t,token:e,nodeId:n=ys.hostname(),name:r=ys.hostname(),listTracks:o,selfUrl:s=`http://127.0.0.1:${process.env.PORT||3362}`,log:a=console}){if(!t||!e)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");t=Cm(t);let i=s.replace(/^http/,"ws"),c=new Map,l=new Map,u=Qn.join(iv,"plane-link","cursor.json"),d={},f=new Map,w=null,m=!1,g=!1,x=Mm,A=null,S=null,T=null,E=!1;async function J(){try{d=JSON.parse(await _c.readFile(u,"utf8"))}catch{d={}}}function M(){E=!0,!S&&(S=setTimeout(async()=>{if(S=null,!!E){E=!1;try{await _c.mkdir(Qn.dirname(u),{recursive:!0}),await _c.writeFile(u,JSON.stringify(d))}catch(N){a.error("[plane-link] cursor flush failed:",N?.message||N)}}},dv),S.unref?.())}function I(N){return!w||w.readyState!==Ec.OPEN?!1:(w.send(JSON.stringify(N)),!0)}function h(N){for(let{trackId:W,event:_}of N)f.set(_.id,{trackId:W,seq:_.seq});return I({v:St,ch:"events",type:"append",events:N})}async function p(){let N;try{N=await o()}catch(W){a.error("[plane-link] backfill listTracks failed:",W?.message||W);return}I({v:St,ch:"ctl",type:"tracks",ids:N.map(W=>W.id).filter(W=>typeof W=="string")});for(let W of N){if(!m)return;let _;try{_=await B(W)}catch{continue}let q=d[W.id]??0,X=_.filter(lt=>lt.seq>q&&typeof lt.id=="string");for(let lt=0;lt<X.length;lt+=Pm){let et=X.slice(lt,lt+Pm).map(wt=>({trackId:W.id,event:wt}));if(!h(et))return}}}function b(N){!m||!N?.trackId||typeof N.id!="string"||h([{trackId:N.trackId,event:N}])}let v=new Map;async function ot(N){let{reqId:W,method:_,path:q,headers:X={},body:lt}=N,et=Object.fromEntries(Object.entries(X).filter(([,Mt])=>Mt!=null)),wt=new AbortController;v.set(W,wt);try{if(N.host){await $(N,et,wt);return}let Mt=await fetch(s+q,{method:_,headers:et,body:lt?Buffer.from(lt,"base64"):void 0,signal:wt.signal});if(I({v:St,ch:"control",type:"http_head",reqId:W,status:Mt.status,headers:{"content-type":Mt.headers.get("content-type")||"application/octet-stream"}}),Mt.body){for await(let De of Mt.body)if(!I({v:St,ch:"control",type:"http_data",reqId:W,data:Buffer.from(De).toString("base64")}))break}I({v:St,ch:"control",type:"http_end",reqId:W,error:null})}finally{v.delete(W)}}function $(N,W,_){let{reqId:q,method:X,path:lt,body:et}=N,wt=new URL(s);return new Promise(Mt=>{let De=sv.request({host:wt.hostname,port:wt.port,method:X,path:lt,headers:{...W,host:N.host},signal:_.signal},Dt=>{let{connection:y,"keep-alive":j,"transfer-encoding":wn,upgrade:Yr,...je}=Dt.headers;I({v:St,ch:"control",type:"http_head",reqId:q,status:Dt.statusCode||502,headers:je}),Dt.on("data",$s=>{I({v:St,ch:"control",type:"http_data",reqId:q,data:Buffer.from($s).toString("base64")})||Dt.destroy()}),Dt.on("end",()=>{I({v:St,ch:"control",type:"http_end",reqId:q,error:null}),Mt()}),Dt.on("error",()=>{I({v:St,ch:"control",type:"http_end",reqId:q,error:"view upstream error"}),Mt()})});De.on("error",Dt=>{I({v:St,ch:"control",type:"http_end",reqId:q,error:Dt?.message||"view request failed"}),Mt()}),et&&De.write(Buffer.from(et,"base64")),De.end()})}function z(){if(g)return;w=new Ec(t,{headers:{authorization:`Bearer ${e}`}}),w.on("open",()=>{I({v:St,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:gs,providers:lv()})}),w.on("message",W=>{let _;try{_=JSON.parse(W)}catch{return}if(_.ch==="ctl"&&_.type==="hello_ok"){m=!0,x=Mm,a.log(`[plane-link] connected to plane as ${n}`),_.minNodeVersion&&gs&&cv(gs,_.minNodeVersion)&&a.error(`[plane-link] this node is bostrat-node ${gs}; the plane asks for \u2265${_.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),T&&clearInterval(T),T=setInterval(()=>I({v:St,ch:"ctl",type:"ping"}),fv),T.unref?.(),p().catch(q=>a.error("[plane-link] backfill failed:",q?.message||q));return}if(_.ch==="events"&&_.type==="ack"&&Array.isArray(_.ids)){for(let q of _.ids){let X=f.get(q);X&&(f.delete(q),(d[X.trackId]??0)<X.seq&&(d[X.trackId]=X.seq))}M();return}if(_.ch==="control"&&_.type==="http_open"){ot(_).catch(q=>I({v:St,ch:"control",type:"http_end",reqId:_.reqId,error:q?.message||"tunnel error"}));return}if(_.ch==="control"&&_.type==="http_cancel"){v.get(_.reqId)?.abort();return}if(_.ch==="attach"&&_.type==="open"){let q=new Ec(i+_.path,Array.isArray(_.protocols)?_.protocols:[],_.host?{headers:{host:_.host}}:void 0);c.set(_.streamId,q),l.set(_.streamId,[]),q.on("open",()=>{let lt=l.get(_.streamId)||[];l.delete(_.streamId);for(let{buf:et,bin:wt}of lt)q.send(et,{binary:wt})}),q.on("message",(lt,et)=>{let wt=et?lt:Buffer.from(String(lt));I({v:St,ch:"attach",type:"data",streamId:_.streamId,data:wt.toString("base64"),...et?{bin:!0}:{}})});let X=()=>{l.delete(_.streamId),c.delete(_.streamId)&&I({v:St,ch:"attach",type:"close",streamId:_.streamId})};q.on("close",X),q.on("error",X);return}if(_.ch==="attach"&&_.type==="data"){let q=c.get(_.streamId);if(!q)return;let X=Buffer.from(_.data,"base64"),lt=_.bin===!0,et=l.get(_.streamId);et?et.push({buf:X,bin:lt}):q.send(X,{binary:lt});return}if(_.ch==="attach"&&_.type==="close"){let q=c.get(_.streamId);c.delete(_.streamId),l.delete(_.streamId),q?.close();return}});let N=()=>{m&&a.log("[plane-link] disconnected from plane"),m=!1,T&&(clearInterval(T),T=null),f.clear();for(let W of c.values())W.close();c.clear(),l.clear();for(let W of v.values())W.abort();v.clear(),!(g||A)&&(A=setTimeout(()=>{A=null,z()},x),A.unref?.(),x=Math.min(x*2,uv))};w.on("close",N),w.on("error",N)}return Pt.on("event",b),J().then(z),{stop:()=>{g=!0,Pt.off("event",b),A&&clearTimeout(A),S&&clearTimeout(S),T&&clearInterval(T),w?.close()},status:()=>({connected:m,nodeId:n,cursor:{...d},inflight:f.size})}}function pv(t){let e={};for(let n of t.boxes||[]){let r=n.role&&n.role!=="lead"?` \xB7 ${n.role}`:"";e[n.name]=`${n.name} (${n.agent||"?"}${r})`}return e}var Bm=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Dm=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},Ic=t=>Math.round(t*1e4)/1e4;async function jm(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=new Set,l=Bm(),u=0,d=[];for(let f of t||[]){let w=await B(f),m=ls(w,{boxLabels:pv(f)});if(m.turns===0){f.archivedAt?u++:d.push(f.name||f.id);continue}o+=m.totalUsd,s+=m.turns,a+=m.repricedTurns||0,i+=m.unpricedTurns||0;for(let g of m.unpricedModels||[])c.add(g);Dm(l,m.totalTokens);for(let[g,x]of Object.entries(m.byModel))n[g]||={usd:0,events:0,tokens:Bm()},n[g].usd=Ic(n[g].usd+x.usd),n[g].events+=x.events,Dm(n[g].tokens,x.tokens);for(let[g,x]of Object.entries(m.byProvider))r[g]||={usd:0,events:0},r[g].usd=Ic(r[g].usd+x.usd),r[g].events+=x.events;e.push({id:f.id,name:f.name,usd:m.totalUsd,rollup:m})}return e.sort((f,w)=>w.usd-f.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:Ic(o),turns:s,repricedTurns:a,unpricedTurns:i,unpricedModels:[...c],totalTokens:l,byModel:n,byProvider:r,tracksWithCost:e.length,tracksSilent:u+d.length,tracksSilentArchived:u,tracksSilentLive:d},tracks:e}}var Wr=t=>`$${t.toFixed(4)}`,tr=(t,e)=>String(t).padEnd(e);function Lm(t){let{fleet:e,tracks:n}=t,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${t.generatedAt}`),t.pricingBasis&&r.push(`basis: ${t.pricingBasis}`);let o=e.tracksSilentLive||[];r.push(`fleet total ${Wr(e.totalUsd)} over ${e.turns} turn(s) \xB7 ${e.tracksWithCost} track(s) with cost, ${e.tracksSilentArchived??e.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),e.repricedTurns&&r.push(`${e.repricedTurns} turn(s) imputed at current rates (recorded before their model was priced)`),e.unpricedTurns&&r.push(`\u26A0 ${e.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(e.unpricedModels||[]).join(", ")}`),r.push(""),r.push("BY MODEL");let s=Object.entries(e.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${tr(a,26)} ${tr(Wr(i.usd),10)} ${i.events} turn(s) \xB7 in ${c.input} out ${c.output} cacheR ${c.cacheRead} cacheW ${c.cacheCreation}`)}r.push(""),r.push("BY TRACK / TASK");for(let a of n){r.push(` ${tr(a.name||a.id,30)} ${Wr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${tr(c.label,40)} ${tr(Wr(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((d,f)=>Number(d[0])-Number(f[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${tr(Wr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
250
+ `)}import Sv from"node:fs/promises";import vv from"node:path";var mv=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],hv={research:[/\bresearch\b/,/\binvestigat/,/\bverif/,/\bcompar(e|ing|ison)/,/\bcompetit/,/\bdeep[ -]?dive\b/,/\banaly[sz]/,/\bassess/,/\bdiligence\b/,/\bmarket\b/,/\bstrateg/,/\broadmap\b/,/\bnorth star/,/\bwhat('| i)?s the (state|status)\b/,/\bstatus of\b/,/\bexplore\b/,/\bfeasib/,/\bevaluat/,/\bscope out\b/,/\btalk through\b/],"bug-fix":[/\bfix(e[sd]|ing)?\b/,/\bbugs?\b/,/\bbroken\b/,/\bregression\b/,/\bcrash/,/\bincident\b/,/\bdebug/,/\bdoesn'?t (work|render|load|start)/,/\bnot working\b/,/\btroubleshoot/,/\bflaky?\b/,/\brepro(duce|duction)?\b/,/\brace condition\b/,/\bleak\b/],"feature-build":[/\bimplement/,/\bbuild(ing)?\b/,/\badd(ing)?\b/,/\bcreate\b/,/\bwire (it |them )?(up|in)\b/,/\bship(ping)?\b/,/\bintegrat/,/\bsupport for\b/,/\bfeature\b/,/\bendpoint\b/,/\badapter\b/,/\bprototype\b/],design:[/\bdesign/,/\bui\b/,/\bux\b/,/\bstyl(e|es|ing)\b/,/\bcss\b/,/\blayout\b/,/\bmock-?up/,/\brender(ing|s)?\b/,/\bpolish/,/\btheme\b/,/\bfonts?\b/,/\bresponsive\b/,/\banimation/,/\bvisual/,/\bpopover\b/],orchestration:[/\bhand[ -]?(off|over)/,/\bhand (it|the lead|this) (over|off)\b/,/\bdelegat/,/\bsubtask/,/\bmerge[ -]?back\b/,/\borchestrat/,/\b(switch|change) (the )?model\b/,/\bmodel switch/],"docs-writing":[/\bdocs?\b/,/\bdocument/,/\bmemo\b/,/\breadme\b/,/\bwrite-?up\b/,/\bchangelog\b/,/\bclaude\.md\b/,/\bblog/,/\bproofread/,/\bcopy(edit|writ)/],"ops-infra":[/\bdeploy/,/\bsystemd\b/,/\bdaemon\b/,/\btmux\b/,/\bdns\b/,/\bwireguard\b/,/\brestart/,/\bnginx\b/,/\bprovision/,/\binstall/,/\bupgrade/,/\bcron\b/,/\bbackups?\b/,/\bcertificat/,/\bssh\b/,/\bsecrets?\b/]};function bs(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of mv){let o=0;for(let s of hv[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var wv=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),gv=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function Um(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))wv.has(r)&&(e+=o||0),gv.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Rc(t){return String(t||"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/[-_/]+/g," ")}var ks=t=>t.payload?.ts??t.ts,yv=12e4,Ce=t=>Math.round(t*1e4)/1e4,bv=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function kv(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>ks(S)>=e,a=bs([Rc(n?.name),Rc(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let S of r)if(S.type==="subtask.delegated"&&S.boxId&&S.payload?.task){let T=bs(S.payload.task);T&&i.set(S.boxId,T)}let c=new Map,l=S=>{let T=S||"unattributed";return c.has(T)||c.set(T,{segments:[u(null)],lastTs:null,provider:null}),c.get(T)},u=S=>({textCat:S,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((S,T)=>ks(S)-ks(T)),f=0,w=0,m=0,g={turns:0};for(let S of d){let T=l(S.boxId),E=T.segments[T.segments.length-1],J=ks(S);switch(T.lastTs!=null&&J>T.lastTs&&s(S)&&(E.activeMs+=Math.min(J-T.lastTs,yv)),T.lastTs=J,S.provider&&(T.provider=S.provider),S.type){case"turn.user":{let M=S.payload?.text;if(S.source==="hook"||!String(M||"").trim())break;let I=u(bs(M));T.segments.push(I),s(S)&&(I.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let M=S.payload?.tool;M&&(E.tools[M]=(E.tools[M]||0)+1),s(S)&&(E.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let M=S.payload||{},I={input:M.tokens?.input||0,output:M.tokens?.output||0,cacheRead:M.tokens?.cacheRead||0,cacheWrite:M.tokens?.cacheWrite??M.tokens?.cacheCreation??0},h=Number(M.usd)||0;!h&&M.tokens&&(h=Kt(M.model,I)),!h&&M.tokens&&!Do(M.model)&&(g.turns+=1),E.usd+=h,E.tokens.input+=I.input,E.tokens.output+=I.output,E.tokens.cacheRead+=I.cacheRead,E.tokens.cacheWrite+=I.cacheWrite;let p=M.model||"unknown";E.models[p]=(E.models[p]||0)+h,S.provider&&(E.provider=S.provider);break}case"handoff.done":s(S)&&(f+=1);break;case"subtask.merged":s(S)&&(w+=1);break;case"error":s(S)&&S.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let x=new Map,A=!1;for(let[S,T]of c)for(let E of T.segments){if(!(E.usd>0||E.activeMs>0||E.turns>0||E.toolCalls>0))continue;let M=E.textCat?.category??i.get(S)?.category??Um(E.tools)?.category??a?.category??"uncategorized";x.has(M)||x.set(M,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let I=x.get(M);I.usd+=E.usd,I.activeMs+=E.activeMs,I.turns+=E.turns,I.toolCalls+=E.toolCalls;for(let[p,b]of Object.entries(E.models))I.models[p]=(I.models[p]||0)+b;let h=E.provider||T.provider;h&&(I.providers[h]=(I.providers[h]||0)+E.usd),h==="cursor"&&E.usd===0&&(A=!0)}return{perCategory:x,handoffs:f,merged:w,failed:m,sawCursorActivity:A,unpricedTurns:g.turns}}function Hm(t,{windowMs:e=null,now:n=Date.now()}={}){let r=e==null?0:n-e,o=new Map,s=[],a=0,i=!1,c=0;for(let g of t||[]){let{track:x,events:A}=g;if(!A?.length){a+=1;continue}let{perCategory:S,handoffs:T,merged:E,failed:J,sawCursorActivity:M,unpricedTurns:I}=kv(g,{since:r});M&&(i=!0),c+=I;let h={trackId:x?.id,name:x?.name||x?.id,archived:!!x?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:T,subtasksMerged:E,subtasksFailed:J};for(let[p,b]of S){h.usd+=b.usd,h.activeMs+=b.activeMs,h.turns+=b.turns,h.categories[p]={usd:Ce(b.usd),activeMs:b.activeMs};for(let[$,z]of Object.entries(b.models))h.models[$]=Ce((h.models[$]||0)+z);for(let[$,z]of Object.entries(b.providers))h.providers[$]=Ce((h.providers[$]||0)+z);o.has(p)||o.set(p,bv());let v=o.get(p);v.usd+=b.usd,v.activeMs+=b.activeMs,v.turns+=b.turns,v.toolCalls+=b.toolCalls;for(let[$,z]of Object.entries(b.models))v.models[$]=(v.models[$]||0)+z;for(let[$,z]of Object.entries(b.providers))v.providers[$]=(v.providers[$]||0)+z;let ot=v.byTrack.get(x?.id)||{usd:0,activeMs:0};ot.usd+=b.usd,ot.activeMs+=b.activeMs,v.byTrack.set(x?.id,ot)}(h.usd>0||h.activeMs>0||h.turns>0)&&(h.usd=Ce(h.usd),h.dominantCategory=xv(h.categories),s.push(h))}let l=[...o.values()].reduce((g,x)=>g+x.usd,0),u=[...o.values()].reduce((g,x)=>g+x.activeMs,0),d=l>0?"usd":"time",f=d==="usd"?l:u,w=new Map((t||[]).map(g=>[g.track?.id,g.track?.name||g.track?.id])),m=[...o.entries()].map(([g,x])=>({category:g,sharePct:f>0?Math.round((d==="usd"?x.usd:x.activeMs)/f*100):0,shareBasis:d,usd:Ce(x.usd),activeMs:x.activeMs,turns:x.turns,toolCalls:x.toolCalls,models:Fm(x.models),providers:Fm(x.providers),topWorkstreams:[...x.byTrack.entries()].sort((A,S)=>S[1].usd-A[1].usd||S[1].activeMs-A[1].activeMs).slice(0,2).map(([A,S])=>({trackId:A,name:w.get(A),usd:Ce(S.usd),activeMs:S.activeMs}))})).sort((g,x)=>x.usd-g.usd||x.activeMs-g.activeMs);return s.sort((g,x)=>x.usd-g.usd||x.activeMs-g.activeMs),{since:r,capturedAt:n,totalUsd:Ce(l),activeMs:u,turns:[...o.values()].reduce((g,x)=>g+x.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var xv=t=>{let e=null;for(let[n,r]of Object.entries(t))(!e||r.usd>e.v.usd||r.usd===e.v.usd&&r.activeMs>e.v.activeMs)&&(e={cat:n,v:r});return e?.cat??null},Fm=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Ce(n)]));var $c={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function Tv(t){let e=String(t||"7d");return e in $c?{key:e,ms:$c[e]}:{key:"7d",ms:$c["7d"]}}function Av(t,e){let n=new Set((e||[]).filter(o=>o.source==="orchestrator").map(o=>`${o.type}|${o.payload?.ts??"?"}`)),r=[...e||[]];for(let o of t?.events||[]){let s=vo(o);!s||n.has(`${s.type}|${s.payload?.ts??"?"}`)||r.push({trackId:t.id,boxId:s.boxId??null,ts:o.at,type:s.type,actor:s.actor,provider:s.provider??null,source:s.source,payload:s.payload})}return r}async function _v(t){if(!t?.cwd)return null;try{let n=(await Sv.readFile(vv.join(t.cwd,".bostrat","handoff","latest.md"),"utf8")).match(/^##\s*Objective\s*\n([\s\S]*?)(?:\n##\s|\n*$)/m);return n?n[1].trim().slice(0,1e3):null}catch{return null}}async function Wm(t,{window:e="7d",now:n=Date.now()}={}){let{key:r,ms:o}=Tv(e),s=[];for(let a of t||[]){let i=await B(a);s.push({track:a,events:Av(a,i),brief:await _v(a)})}return{window:r,...Hm(s,{windowMs:o,now:n})}}import er from"node:fs/promises";import Ev from"node:fs";import ft from"node:path";import Iv from"node:os";import{execFile as Rv}from"node:child_process";import{promisify as $v}from"node:util";var Ov=$v(Rv),qm=process.env.BOSTRAT_STATE_DIR||ft.join(process.env.HOME||Iv.tmpdir(),".bostrat"),Oc=!1;async function qr(t,e){let n=await er.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await er.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await er.mkdir(ft.dirname(e),{recursive:!0}),await er.copyFile(t,e),!0)}var Cv=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json"]);async function Gr(t,e){let n=0,r=await er.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let o of r){if(Cv.has(o.name))continue;let s=ft.join(t,o.name);o.isDirectory()?n+=await Gr(s,ft.join(e,o.name)):o.isFile()&&(n+=await qr(s,ft.join(e,o.name))?1:0)}return n}async function Gm({stateDir:t=qm,dest:e=process.env.BOSTRAT_SYNC_DIR||`${qm}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await er.mkdir(e,{recursive:!0});let o=0;o+=await qr(ft.join(t,"tracks.json"),ft.join(e,"tracks.json"))?1:0,o+=await qr(ft.join(t,"mocks.json"),ft.join(e,"mocks.json"))?1:0,o+=await qr(ft.join(t,"runbooks.json"),ft.join(e,"runbooks.json"))?1:0,o+=await Gr(ft.join(t,"runbooks"),ft.join(e,"runbooks")),o+=await Gr(ft.join(t,"mock-archive"),ft.join(e,"mock-archive")),o+=await qr(ft.join(t,"events.jsonl"),ft.join(e,"events.jsonl"))?1:0,o+=await Gr(ft.join(t,"events"),ft.join(e,"events"));for(let l of r||[])!l?.cwd||!l?.id||(o+=await Gr(ft.join(l.cwd,".bostrat"),ft.join(e,"workspaces",l.id,".bostrat")));let s=(...l)=>Ov("git",["-C",e,...l]);Ev.existsSync(ft.join(e,".git"))||(await s("init","-q"),await s("config","user.email","agent@bostrat.local"),await s("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await s("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await la(e).catch(()=>{}));let a=!1,{stdout:i}=await s("status","--porcelain");i.trim()&&(await s("add","-A"),await s("commit","-q","-m",`state sync ${new Date().toISOString()}`),a=!0);let c=!1;if(n&&(a||Oc))try{await s("push","-q",n,"HEAD:refs/heads/main"),c=!0,Oc=!1}catch{Oc=!0}return{staged:o,committed:a,pushed:c}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}import{execFile as Mv}from"node:child_process";import{promisify as Pv}from"node:util";import{randomUUID as Nv}from"node:crypto";import Bv from"node:fs";import Dv from"node:os";import jv from"node:path";var Pe=Pv(Mv),xs=t=>new Promise(e=>setTimeout(e,t)),Ne=Ns,Cc=process.env.BOSTRAT_USAGE_PROBE_CWD||jv.join(Dv.homedir(),".bostrat","usage-probe"),Lv=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,Uv=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,Fv=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,Km=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Me=null,Kr=null,Vr=null;async function Hv(){try{return await Pe(O,C("has-session","-t",Ne)),!0}catch{return!1}}function Wv(){Me&&clearTimeout(Me),Me=setTimeout(()=>qv().catch(()=>{}),Lv),Me.unref&&Me.unref()}async function qv(){Me&&(clearTimeout(Me),Me=null);try{await Pe(O,C("kill-session","-t",Ne))}catch{}}async function Gv(){let t=Date.now()+Fv;for(;;){let e=await Mc().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await xs(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await xs(500)}}async function Kv(){if(await Hv())return;if(!await ee())throw new Error("host tmux server not running \u2014 start globo-tmux.service");Bv.mkdirSync(Cc,{recursive:!0}),await pe(Cc);let t=Nv();await Pe(O,C("new-session","-d","-s",Ne,"-c",Cc,"-x","200","-y","60"));let e=`${Ps} --session-id ${t}`;await Pe(O,C("send-keys","-t",Ne,e,"Enter")),await Gv()}async function Mc(){let{stdout:t}=await Pe(O,C("capture-pane","-t",Ne,"-p"));return t}async function Vv(){await Kv(),await Pe(O,C("send-keys","-t",Ne,"-l","/usage")),await Pe(O,C("send-keys","-t",Ne,"Enter"));let t=Date.now()+Uv,e="";for(;await xs(400),e=await Mc(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await xs(900),e=await Mc(),await Pe(O,C("send-keys","-t",Ne,"Escape")).catch(()=>{}),Wv(),e}function Jv(t){let e=String(t||"").split(`
251
+ `).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,e.length)&&!/^Current\s/i.test(e[c]);c++){let l=e[c].match(/(\d+)%\s*used/i);l&&a===null&&(a=Number(l[1]));let u=e[c].match(/^Resets\s+(.+)$/i);u&&i===null&&(i=u[1].trim())}return a===null?null:{pct:a,resets:i||null}},r=s=>{let a=e.findIndex(i=>s.test(i));return a<0?null:n(a)},o=[];return e.forEach((s,a)=>{let i=s.match(/^Current week \(([^)]+)\)$/i);if(!i||/all models/i.test(i[1]))return;let c=n(a);c&&o.push({model:i[1].trim(),...c})}),{session:r(/^Current session\b/i),week:r(/^Current week \(all models\)/i)||r(/^Current week$/i),weekModels:o.length?o:null}}function zv(t){let e=String(t||"").split(`
252
+ `).map(s=>s.replace(/\s+$/,"")),n=e.findIndex(s=>/What's contributing/i.test(s));if(n<0)return null;let r=/(Usage credits|Esc to cancel|d to day · w to week|^\s*d to day)/i,o=[];for(let s=n+1;s<e.length&&!r.test(e[s]);s++)e[s].trim()&&o.push(e[s].trim());return o.length?o:null}async function Vm({fresh:t=!1}={}){return!t&&Kr&&Date.now()-Kr.at<Km?Kr.data:Vr||(Vr=(async()=>{try{let e=await Vv(),n=Jv(e),r={...n,contributors:zv(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Kr={at:Date.now(),data:r},r}catch(e){let n={session:null,week:null,contributors:null,subscription:!1,error:e?.message||String(e),capturedAt:Date.now()};return Kr={at:Date.now()-(Km-3e3),data:n},n}finally{Vr=null}})(),Vr)}var Xv=262144;function Yv(t){let e=Number(t);if(!Number.isFinite(e)||e<0)return null;let n=Math.floor(e/3600),r=Math.round(e%3600/60);return n>=48?`in ${Math.round(n/24)}d`:n>0?`in ${n}h ${r}m`:`in ${Math.max(1,r)}m`}function Jm(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:Yv(t.resets_in_seconds)}:null}function Zv(t){let e=String(t||"").split(`
253
+ `),n=!1;for(let r=e.length-1;r>=0;r--){let o=e[r].trim();if(!o||o[0]!=="{"||!o.includes("token_count"))continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s?.payload;if(a?.type!=="token_count")continue;n=!0;let i=a.rate_limits;if(!i)return{session:null,week:null,sawTokenCount:n};if(i.primary||i.secondary)return{session:Jm(i.primary),week:Jm(i.secondary),sawTokenCount:n};if(i.primary_used_percent!=null||i.secondary_used_percent!=null){let c=l=>Number.isFinite(Number(l))?{pct:Math.round(Number(l)),resets:null}:null;return{session:c(i.primary_used_percent),week:c(i.secondary_used_percent),sawTokenCount:n}}return{session:null,week:null,sawTokenCount:n}}return{session:null,week:null,sawTokenCount:n}}async function Qv(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await Ge(t?.cwd,t?.sessionId);if(!n)return{...e,session:null,week:null,subscription:!1,error:"no codex rollout found for this box yet",capturedAt:Date.now()};let r=await vt(n.file,Xv),o=Zv(r);return{...e,session:o.session,week:o.week,subscription:!!(o.session||o.week),capturedAt:n.mtimeMs||Date.now()}}catch(n){return{...e,session:null,week:null,subscription:!1,error:n?.message||String(n),capturedAt:Date.now()}}}async function zm(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?Qv(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await Vm({fresh:e})}}function Xm(t){let e=t.split(".");if(e.length!==4)return null;let n=0;for(let r of e){let o=Number(r);if(!Number.isInteger(o)||o<0||o>255)return null;n=n*256+o}return n>>>0}function tT(t){let[e,n]=t.split("/"),r=Xm(e);if(r==null)return null;let o=n==null?32:Number(n);if(!Number.isInteger(o)||o<0||o>32)return null;let s=o===0?0:4294967295<<32-o>>>0;return a=>(a&s)===(r&s)}function eT(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(tT).filter(Boolean)}function nT(t){let e=t.headers["fly-client-ip"];if(typeof e=="string"&&e.trim())return e.trim();let n=t.headers["x-forwarded-for"];return typeof n=="string"&&n.trim()?n.split(",")[0].trim():(t.socket&&t.socket.remoteAddress||"").replace(/^::ffff:/,"")}function Ym(t){let e=eT(t),n=e.length===0;return{open:n,clientIp:nT,allowed(r){if(n)return!0;let o=Xm((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}import rT from"node:http";import oT from"node:net";function Zm(t){if(!t)return null;let e=String(t).replace(/:\d+$/,"").toLowerCase();if(/^[\d.]+$/.test(e)||e.includes(":"))return null;let n=e.split(".");if(n.length<3)return null;let r=n[0];return/^[a-z0-9-]+$/.test(r)?r:null}function Qm(t){let e=Zm(t.headers.host);return e?{slug:e,view:hi(e)}:null}var sT=t=>`<!doctype html><meta charset="utf-8">
254
+ <meta http-equiv="refresh" content="3">
255
+ <title>${t.name} \u2014 starting</title>
256
+ <body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
257
+ <div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
258
+ <p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function th(t,e,n){let r=Qm(t);if(!r)return n();let{view:o,slug:s}=r;if(!o)return e.statusCode=404,e.setHeader("content-type","text/plain"),e.end(`bostrat: no view named "${s}"`);if(o.state==="starting")return e.statusCode=503,e.setHeader("content-type","text/html"),e.end(sT(o));if(o.state!=="live"&&o.state!=="unhealthy")return e.statusCode=404,e.setHeader("content-type","text/plain"),e.end(`bostrat: view "${s}" is ${o.state} \u2014 restart it from the Views tab`);let a={...t.headers,host:`127.0.0.1:${o.port}`,"x-forwarded-host":t.headers.host,"x-forwarded-proto":t.headers["x-forwarded-proto"]||"http"},i=rT.request({host:"127.0.0.1",port:o.port,method:t.method,path:t.url,headers:a},c=>{e.writeHead(c.statusCode||502,c.headers),c.pipe(e)});i.on("error",()=>{e.headersSent?e.destroy():(e.statusCode=502,e.setHeader("content-type","text/plain"),e.end(`bostrat: view "${s}" did not answer on :${o.port}`))}),t.pipe(i),t.on("aborted",()=>i.destroy())}function eh(t,e,n){let r=Qm(t);if(!r)return!1;let{view:o,slug:s}=r;if(!o||o.state!=="live"&&o.state!=="unhealthy")return e.write(`HTTP/1.1 404 Not Found\r
259
+ \r
260
+ bostrat: no live view "${s}"\r
261
+ `),e.destroy(),!0;let a=oT.connect(o.port,"127.0.0.1",()=>{let c=[`${t.method} ${t.url} HTTP/1.1`];for(let[l,u]of Object.entries(t.headers))l!=="host"&&c.push(`${l}: ${Array.isArray(u)?u.join(", "):u}`);c.push(`host: 127.0.0.1:${o.port}`),a.write(c.join(`\r
262
+ `)+`\r
263
+ \r
264
+ `),n?.length&&a.write(n),e.pipe(a),a.pipe(e)}),i=()=>{a.destroy(),e.destroy()};return a.on("error",i),e.on("error",i),!0}function nh(t,e){let n=typeof e=="string"?{host:e}:e||{},r=n["x-bostrat-view-base"];return typeof r=="string"&&r?`${n["x-forwarded-proto"]==="https"?"https":"http"}://${t.slug}.${r.replace(/:\d+$/,"")}/`:`http://${t.slug}.${aT(n.host)}/`}function aT(t){if(process.env.BOSTRAT_PUBLIC_HOST)return String(process.env.BOSTRAT_PUBLIC_HOST).replace(/:\d+$/,"");let e=String(t||"").replace(/:\d+$/,"").toLowerCase();if(!e||!e.includes(".")||/^[\d.]+$/.test(e))return"bostrat.globopi";let n=e.split(".");return Zm(e)&&hi(n[0])?n.slice(1).join("."):e}import{execFile as iT}from"node:child_process";import{promisify as cT}from"node:util";var jc=cT(iT),sh=3e4,lT=30*6e4,rh=sh,uT=5*6e4,dT=10080*6e4,fT=60*6e4,pT=3,ah="number,url,title,headRefName,headRefOid,state",Dc=60*6e4,oh=new Map;function mT(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<Dc)}function hT(t,e){return t.armedAt&&e-t.armedAt<lT||mT(t,e)?rh:e-(t.firstSeen||0)>dT?fT:uT}async function wT(t,e){let{stdout:n}=await jc("gh",["pr","view",String(e),"--repo",t,"--json",ah],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Gt({slug:t})}});return JSON.parse(n||"null")}async function gT(t){let{stdout:e}=await jc("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",ah],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Gt({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}async function yT(){let t=new Map;for(let e of await dt().catch(()=>[]))t.set(e.id,e);return t}async function bT(t,e){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...e}})}async function kT(t,e){let n=await Si(t);return n?_r(n,e):null}async function xT(t,e,n){let r=await Si(t);if(!r)throw new Error(`no local checkout for ${t}`);return Er(r,e,{method:n})}async function ST(t,e){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...e}})}async function Ss(t,e,n,r,o){if(!await r.disarm(t.repo,t.number).catch(()=>null))return;let a=n.get(t.trackId);a&&await r.emitDisarmed(a,{number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo,reason:o}).catch(()=>{})}var vT=10*6e4;async function TT(t,e,n){let r=await n.detail(t.repo,t.number).catch(()=>null);if(!r)return;if(r.checks.failed>0){let s=r.checks.failing.map(a=>a.name).join(", ");await Ss(t,r,e,n,`check${r.checks.failed===1?"":"s"} failed: ${s}`);return}if(r.mergeable==="CONFLICTING"){await Ss(t,r,e,n,`conflicts with ${r.baseRefName}`);return}if(!r.checks.total){let s=Math.max(t.autoMerge?.armedAt||0,Date.parse(r.updatedAt||"")||0);if((n.now??Date.now())-s<vT)return;await Ss(t,r,e,n,r.checks.unavailable?"this token can't read the checks":"no CI checks appeared");return}if(r.checks.pending>0||r.mergeable==="UNKNOWN"||r.mergeStateStatus==="UNKNOWN"||r.isDraft)return;let o=await n.lookup(t.repo,t.number).catch(()=>null);if(o?.autoMerge){try{await n.merge(t.repo,t.number,o.autoMerge.method||"merge")}catch(s){if(/already merged|not open|is closed/i.test(String(s?.message||s)))return;await Ss(t,r,e,n,String(s?.message||s).trim()||"merge failed");return}await ih(t,{...r,state:"MERGED"},e,n)}}async function Pc(t,e,n){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:e,...n}})}async function AT(t,e){try{return await jc("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Nc(t,e,n,r=()=>!0){return(await B(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var Bc=(t,e)=>({number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo});async function _T(t,e,n,r){let o=r.now??Date.now(),s=e.headRefOid||null,a=n.get(t.trackId)||null;if(s&&(t.ciFix?.headSha&&s!==t.ciFix.headSha&&await Eo(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await Ro(t.repo,t.number,{headSha:s}).catch(()=>{})),s&&s!==t.lastHeadSha){let i=!t.lastHeadSha,c=!!(t.ciFix||t.reconcile||t.ciFixAwait||t.reconcileAwait);await vd(t.repo,t.number,s).catch(()=>{}),!i&&!c&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Nc(a,"pushed",t.number,u=>u.payload?.sha===s)&&await Pc(a,"pushed",{...Bc(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>Dc)await Io(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i?.checks){if(i.checks.failed>0)await Io(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=t.ciFixAwait.since||0;a&&!await Nc(a,"ci-fixed",t.number,l=>(l.payload?.ts||0)>=c)&&await Pc(a,"ci-fixed",{...Bc(t,e),passed:i.checks.passed}).catch(()=>{}),await Io(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>Dc)await $o(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await $o(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){let c=t.reconcileAwait.since||0;a&&!await Nc(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await Pc(a,"reconciled",{...Bc(t,e),base:i.baseRefName||null}).catch(()=>{}),await $o(t.repo,t.number).catch(()=>{})}}}}async function ih(t,e,n,r){let o=n.get(t.trackId);o&&((await B(o).catch(()=>[])).some(i=>i.type==="pr.state.changed"&&i.payload?.change==="merged"&&i.payload?.number===Number(t.number))||await r.emitMerged(o,{number:Number(t.number),url:e.url||t.url,title:e.title||t.title,branch:e.headRefName||t.headRef,repo:t.repo}).catch(()=>{})),await r.archiveIfArmed(t.repo,t.number).catch(()=>{}),await r.mark(t.repo,t.number,"merged").catch(()=>{})}async function ET(t={}){let{now:e=Date.now(),auth:n=bi,view:r=wT,list:o=gT,links:s=Td,trackMap:a=yT,emitMerged:i=bT,mark:c=Qe,archiveIfArmed:l=tn,batchThreshold:u=pT,detail:d=kT,merge:f=xT,lookup:w=Tt,disarm:m=Co,emitDisarmed:g=ST,localHasCommit:x=AT}=t;if(!(await n().catch(()=>null))?.authenticated)return 0;let T=(await s().catch(()=>[])).filter(I=>e-(oh.get(bt(I.repo,I.number))||0)>=hT(I,e));if(!T.length)return 0;let E=new Map;for(let I of T)oh.set(bt(I.repo,I.number),e),E.has(I.repo)||E.set(I.repo,[]),E.get(I.repo).push(I);let J=await a().catch(()=>new Map),M={now:e,emitMerged:i,mark:c,archiveIfArmed:l,detail:d,merge:f,lookup:w,disarm:m,emitDisarmed:g,localHasCommit:x};for(let[I,h]of E){let p;if(h.length>u)p=await o(I).catch(()=>null);else{p=new Map;for(let b of h){let v=await r(I,b.number).catch(()=>null);v?.number!=null&&p.set(Number(v.number),v)}}if(p)for(let b of h){let v=p.get(Number(b.number));v&&(v.state==="MERGED"?await ih(b,v,J,M):v.state==="CLOSED"?await c(b.repo,b.number,"closed").catch(()=>{}):(await _T(b,v,J,M).catch(()=>{}),b.autoMerge&&await TT(b,J,M).catch(()=>{})))}}return T.length}function ch(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await ET()}catch{}finally{t=!1}}},sh)}import de from"node:fs/promises";import nr from"node:path";import hh from"node:os";import lh from"node:fs/promises";import vs from"node:path";import{execFile as IT}from"node:child_process";import{promisify as RT}from"node:util";import{fileURLToPath as $T}from"node:url";var OT=RT(IT),dh=vs.dirname($T(import.meta.url)),CT=vs.resolve(dh,"..","..","client","dist"),MT="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",PT=new Map([...MT].map((t,e)=>[t,e]));function NT(t){let e=[],n=0,r=0;for(let o of t){let s=PT.get(o);if(s===void 0)return e;n+=(s&31)<<r,s&32?r+=5:(e.push(n&1?-(n>>>1):n>>>1),n=0,r=0)}return e}function BT(t){let e=[],n=0,r=0,o=0;for(let s of t.split(";")){let a=[],i=0;if(s)for(let c of s.split(",")){let l=NT(c);l.length!==0&&(i+=l[0],l.length>=4&&(n+=l[1],r+=l[2],o+=l[3],a.push([i,n,r,o])))}e.push(a)}return e}function DT(t,e){if(!t||t.length===0||t[0][0]>e)return null;let n=0,r=t.length-1;for(;n<r;){let o=n+r+1>>1;t[o][0]<=e?n=o:r=o-1}return t[n]}var uh=new Map;async function jT(t){let e;try{e=await lh.stat(t)}catch{return null}let n=uh.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await lh.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:BT(r.mappings||"")};return uh.set(t,o),o}catch{return null}}var Lc=null;function LT(){return Lc||(Lc=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):OT("git",["rev-parse","--short","HEAD"],{cwd:vs.resolve(dh,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),Lc}var UT=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function FT(t,{distDir:e=CT}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(UT)){let[a,i,c,l]=s,u=await jT(vs.join(e,"assets",`${i}.map`)),d=u?DT(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(t.slice(r,s.index)),d&&u.sources[d[1]]){let f=u.sources[d[1]].replace(/^(\.\.\/)+/,"");n.push(`${f}:${d[2]+1}:${d[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(t.slice(r)),{stack:n.join(""),symbolicated:o}}async function fh(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await LT();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await FT(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var HT=()=>process.env.BOSTRAT_STATE_DIR||nr.join(process.env.HOME||hh.tmpdir(),".bostrat"),Be=()=>nr.join(HT(),"telemetry"),wh=()=>nr.join(Be(),".relay-offsets.json"),WT=50,qT=14,GT=3e4,ph=500,Ct=(t,e)=>typeof t=="string"?t.slice(0,e):"";function KT(t,e){if(!t||typeof t!="object")return null;let n=Ct(t.name,64);if(!n)return null;let r=Number.isFinite(t.ts)?t.ts:e,o={name:n,ts:Math.abs(r-e)<168*3600*1e3?r:e,receivedAt:e,anonId:Ct(t.anonId,64),sessionId:Ct(t.sessionId,64),userId:Ct(t.userId,64),release:Ct(t.release,64),appVersion:Ct(t.appVersion,32),route:Ct(t.route,128),deviceClass:t.deviceClass==="phone"||t.deviceClass==="desktop"?t.deviceClass:""};return t.props&&typeof t.props=="object"&&!Array.isArray(t.props)&&(JSON.stringify(t.props).length<=8192?o.props=t.props:o.props={clamped:!0}),t.error&&typeof t.error=="object"&&(o.error={kind:Ct(t.error.kind,256),message:Ct(t.error.message,8192),stack:Ct(t.error.stack,32768),fingerprint:Ct(t.error.fingerprint,64)}),Array.isArray(t.breadcrumbs)&&(o.breadcrumbs=t.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:Ct(s?.n,64),r:Ct(s?.r,128)}))),o}var gh=t=>new Date(t).toISOString().slice(0,10),mh=0;async function yh(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,WT):[]).map(s=>KT(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await fh(s);await de.mkdir(Be(),{recursive:!0});let o=nr.join(Be(),`${gh(e)}.ndjson`);return await de.appendFile(o,r.map(s=>JSON.stringify(s)+`
265
+ `).join(""),"utf8"),e-mh>6*3600*1e3&&(mh=e,VT(e).catch(()=>{})),r.length}async function VT(t=Date.now()){let e=gh(t-qT*24*3600*1e3),n=[];try{n=await de.readdir(Be())}catch{return}let r=n.filter(s=>s.endsWith(".ndjson")&&s.slice(0,10)<e);if(!r.length)return;for(let s of r)await de.rm(nr.join(Be(),s),{force:!0});let o=await kh();for(let s of r)delete o[s];await xh(o)}var Ts=new Map;function bh(t,e=Date.now()){let n=t||"unknown",r=Ts.get(n);return r||(Ts.size>512&&Ts.clear(),r={tokens:40,at:e},Ts.set(n,r)),r.tokens=Math.min(40,r.tokens+(e-r.at)/6e4*20),r.at=e,r.tokens<1?!1:(r.tokens-=1,!0)}async function kh(){try{return JSON.parse(await de.readFile(wh(),"utf8"))||{}}catch{return{}}}async function xh(t){await de.mkdir(Be(),{recursive:!0}),await de.writeFile(wh(),JSON.stringify(t),"utf8")}function JT(t,{env:e,host:n}={}){let r=t.name==="app.error"&&t.error,o={};for(let[a,i]of[["event",t.name],["route",t.route],["session",t.sessionId],["anon",t.anonId],["user",t.userId],["release",t.release],["appVersion",t.appVersion],["device",t.deviceClass]])i&&(o[a]=i);let s={timestamp:new Date(t.ts).toISOString(),service:"bostrat-client",source:"treetop-js",env:e||process.env.BOSTRAT_ENV||"pi",host:n||hh.hostname(),level:r?"error":"info",message:r?`${t.error.kind||"Error"}: ${t.error.message||"(no message)"}`.slice(0,8192):t.name,tags:o,attributes:{...t.props?{props:t.props}:{},receivedAt:t.receivedAt}};return r&&(s.error={kind:t.error.kind,message:t.error.message,stack:t.error.stack},t.error.fingerprint&&(s.fingerprint=t.error.fingerprint),t.error.symbolicated&&(s.attributes.symbolicated=!0),t.breadcrumbs&&(s.attributes.breadcrumbs=t.breadcrumbs)),s}async function zT({fetchImpl:t=fetch}={}){let e=await Vp();if(!e)return{sent:0,disabled:!0};let n;try{n=(await de.readdir(Be())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await kh(),o=0;for(let s of n){let a=nr.join(Be(),s),i=r[s]||0,c;try{let f=await de.open(a,"r");try{let w=(await f.stat()).size;if(w<=i)continue;c=Buffer.alloc(w-i),await f.read(c,0,c.length,i)}finally{await f.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
266
+ `).filter(Boolean),d=[];for(let f of u)try{d.push(JSON.parse(f))}catch{}for(let f=0;f<d.length;f+=ph){let w=d.slice(f,f+ph).map(g=>JT(g)),m=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.ingestKey}`},body:JSON.stringify({events:w})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=w.length}r[s]=i+l+1,await xh(r)}return{sent:o,disabled:!1}}var Uc=null,Fc=!1;function Sh(){Uc||(Uc=setInterval(async()=>{if(!Fc){Fc=!0;try{await zT()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{Fc=!1}}},GT),Uc.unref?.())}import _s from"node:fs/promises";import Eh from"node:path";import XT from"node:os";var Ih=()=>process.env.BOSTRAT_STATE_DIR||Eh.join(process.env.HOME||XT.tmpdir(),".bostrat"),Rh=()=>Eh.join(Ih(),"dictation-glossary.json"),YT=100,ZT=500,QT=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Es=null,Hc=null,Wc=Promise.resolve();function vh(){let t={},e=Date.now();for(let n of QT)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function $h(){return Es||(Hc??=_s.readFile(Rh(),"utf8").then(t=>JSON.parse(t)?.terms??vh()).catch(()=>vh()).then(t=>Es=t).finally(()=>Hc=null),Hc)}async function Th(){let t=JSON.stringify({terms:Es},null,2),e=Rh(),n=`${e}.${process.pid}.tmp`;await _s.mkdir(Ih(),{recursive:!0}),await _s.writeFile(n,t),await _s.rename(n,e)}function tA(){return Wc=Wc.then(Th,Th),Wc.catch(()=>{})}async function qc(){return Object.values(await $h()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var eA=t=>Math.max(1,Math.ceil(t.length/4));async function nA(){let t=[],e=0;for(let{term:n}of await qc()){let r=eA(n);if(!(e+r>ZT)&&(t.push(n),e+=r,t.length>=YT))break}return t}async function rA(t,e){let n=await $h(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var Ah=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),As=t=>t.toLowerCase();function _h(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function Oh(t,e){let n=Ah(t),r=Ah(e);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let d=s-1;d>=0;d--)a[u][d]=As(n[u])===As(r[d])?a[u+1][d+1]+1:Math.max(a[u+1][d],a[u][d+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)As(n[c])===As(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(_h(r[l])&&i.add(r[l]),l++);for(;l<s;l++)_h(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await rA(u,"learned");return await tA(),[...i]}var Jr=null;function oA(t,e){let n=null;try{n=JSON.parse(e)}catch{}let r=n?.err_code||n?.error_code||n?.category||"";return t===402||/PAYMENT_REQUIRED/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"Deepgram credits are exhausted \u2014 dictation is down until you top up at console.deepgram.com or drop a new key into ~/.bostrat/integrations.json."}:t===401||t===403?{status:502,code:"key_invalid",persistent:!0,message:"Deepgram rejected the API key (revoked, or missing ASR scope) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"transcription_failed",persistent:!1,message:`Deepgram error ${t}: ${e.slice(0,300)}`}}async function Ch(){if(!Jr)return null;let t=await Br();if(!t||t.apiKey.slice(-6)!==Jr.keyTail)return null;let{keyTail:e,...n}=Jr;return n}async function Mh(t,e){let n=await Br();if(!n){let i=new Error("Deepgram is not configured \u2014 add deepgram.apiKey to ~/.bostrat/integrations.json");throw i.status=503,i.code="not_configured",i}let r=new URL("https://api.deepgram.com/v1/listen");r.searchParams.set("model","nova-3"),r.searchParams.set("smart_format","true");for(let i of await nA())r.searchParams.append("keyterm",i);let o=await fetch(r,{method:"POST",headers:{Authorization:`Token ${n.apiKey}`,"Content-Type":e||"application/octet-stream"},body:t});if(!o.ok){let i=await o.text().catch(()=>""),c=oA(o.status,i);c.persistent&&(Jr={code:c.code,message:c.message,at:Date.now(),keyTail:n.apiKey.slice(-6)});let l=new Error(c.message);throw l.status=c.status,l.code=c.code,l}return Jr=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}var jh=ct.dirname(aA(import.meta.url)),Ph=Number(process.env.PORT)||3362,Is=Om(process.env),Lh=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,rr=Ym(process.env.BOSTRAT_ALLOW_IPS),k=fe();k.disable("x-powered-by");k.use((t,e,n)=>{if(rr.allowed(rr.clientIp(t)))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});k.use(th);k.use("/api/mocks",fe.json({limit:"1mb"}));k.use("/api/mocks",fe.raw({type:"image/png",limit:"24mb"}));k.use("/api/runbooks",fe.json({limit:"1mb"}));k.use("/api/telemetry",fe.json({limit:"256kb"}));k.use("/api/dictation",fe.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));k.use("/api/tracks/:id/upload",fe.raw({type:()=>!0,limit:"25mb"}));k.use(fe.json({limit:"64kb"}));k.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:Ht,hostTmuxAlive:await ee(),autoTriggerPct:Lh})});k.get("/api/sessions",async(t,e)=>{e.json({sessions:await vn()})});k.post("/api/sessions",async(t,e)=>{let{displayName:n,cwd:r}=t.body||{};if(typeof n!="string")return e.status(400).json({error:"displayName (string) required"});try{let o=await Ue(n,r||void 0);e.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;e.status(a).json({error:s})}});k.delete("/api/sessions/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!ut(n))return e.status(400).json({error:"invalid session name"});if(!await G(n))return e.status(404).json({error:"session not found"});try{await Wt(n),e.json({ok:!0})}catch(r){e.status(500).json({error:`failed to kill: ${r?.message||r}`})}});k.get("/api/context/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!ut(n))return e.status(400).json({error:"invalid session name"});let r=await G(n);if(!r)return e.status(404).json({error:"session not found"});let o=await cr(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});k.post("/api/handoff/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!ut(n))return e.status(400).json({error:"invalid session name"});try{let r=await Ml(n);e.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){e.status(409).json({error:r?.message||String(r)})}});k.get("/api/handoff/:name",(t,e)=>{let n=decodeURIComponent(t.params.name),r=uo(n);if(!r)return e.json({running:!1});e.json({running:!r.finishedAt,step:r.step,error:r.error,file:r.file,startedAt:r.startedAt,finishedAt:r.finishedAt})});k.get("/api/tracks",async(t,e)=>{e.json({tracks:await dt(),agents:Fi,tickets:await lm()})});k.get("/api/cost",async(t,e)=>{try{let n=await jm(await dt());if(n.cursorUsage=Kd(),t.query.format==="text")return e.type("text/plain").send(Lm(n));e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/usage/roundup",async(t,e)=>{try{e.json(await Wm(await dt(),{window:t.query.window}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/hook",async(t,e)=>{try{let n=await $m(t.body||{},yp);e.json({ok:!0,events:n.length})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});k.post("/api/telemetry",async(t,e)=>{try{if(!bh(rr.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await yh(t.body||{});e.json({ok:!0,events:n})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});k.get("/api/repos",async(t,e)=>{try{let n=await oe(),r=new Set((await Promise.all(n.map(a=>Y(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await fr()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));e.json({repos:[...n,...s]})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/github/token",async(t,e)=>{try{e.json(await mo(String(t.query.repo||"")))}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});k.get("/api/github/status",(t,e)=>{e.json({configured:po()})});k.post("/api/github/refresh",(t,e)=>{Vl(),e.json({ok:!0})});function Uh(t,e,n){let r=t.workspace;return r?.kind==="multi"?(r.repos??[]).some(o=>e&&o?.base===e||n&&o?.repo===n):!r?.base||!e||r.base===e||n&&r.repo===n}async function Jc({repo:t=null,number:e=null,branch:n=null}){let r=t?await Y(t):null;if(r&&e!=null){let s=await Tt(r,e),a=s&&await rt(s.trackId);if(a)return a}return n?(await dt()).find(s=>s.workspace?.branch===n&&Uh(s,t,r))??null:null}async function hn(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Jc({repo:s,number:e,branch:o}).catch(()=>null);c&&await R({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:t,number:e,url:n,title:r,branch:o??c.workspace?.branch??null,...i}}).catch(()=>{})}async function uA(t){let e=(await dt()).filter(n=>!n.archivedAt);for(let n of t??[]){if(!n.prs?.length)continue;let r=await Y(n.path);if(r)for(let o of n.prs){if(await Tt(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&Uh(a,n.path,r));s&&await Nt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}k.get("/api/git/prs",async(t,e)=>{try{let n=await bf();await uA(n.repos).catch(()=>{}),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/git/prs/merge",async(t,e)=>{let{repo:n,number:r,method:o,auto:s,archive:a,headRefName:i,title:c,url:l}=t.body||{};try{if(s){let w=o||"merge";if(!Vo.has(w))throw new Error(`invalid merge method: ${w}`);let m=Number(r);if(!Number.isInteger(m)||m<=0)throw new Error("invalid PR number");let g=await Y((await Te(n)).path);if(!g)throw new Error("repo has no GitHub remote");let x=await Jc({repo:n,number:m,branch:i}).catch(()=>null);if(!x)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(await Nt({repo:g,number:m,trackId:x.id,url:l,headRef:i,title:c,source:"matched"}).catch(()=>{}),!await Oo(g,m,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await Va(g,m,a===!0),en(),await hn("auto-armed",{number:m,url:l,title:c,branch:i,repo:n,track:x}),e.json({ok:!0,number:m,auto:!0})}let u=await Er(n,r,{method:o||"squash"});await hn("merged",{number:u.number,url:l,title:c,branch:i,repo:n});let d=await Y(n).catch(()=>null),f=!1;if(d&&(a===!0&&await Va(d,u.number,!0).catch(()=>{}),f=await tn(d,u.number).catch(()=>!1),await Qe(d,u.number,"merged").catch(()=>{})),a===!0&&!f){let w=await Jc({repo:n,number:u.number,branch:i}).catch(()=>null);w&&!w.archivedAt&&await Vn(w.id).catch(()=>{})}e.json(u)}catch(u){let d=u?.message||String(u),f=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(d)?400:502;e.status(f).json({error:d})}});k.post("/api/git/prs/disarm-auto",async(t,e)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=t.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await Y((await Te(n)).path).catch(()=>null),l=c?await Co(c,i).catch(()=>null):null;try{await kf(n,i)}catch(u){if(!l)throw u}en(),await hn("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),e.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;e.status(l).json({error:c})}});k.post("/api/git/prs/merge-all",async(t,e)=>{let{repo:n,method:r}=t.body||{};try{let o=await Y(n).catch(()=>null),s=await vf(n,{method:r||"merge",onMerged:a=>{hn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&tn(o,a.number).catch(()=>{}).then(()=>Qe(o,a.number,"merged")).catch(()=>{})}});e.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;e.status(a).json({error:s})}});k.get("/api/git/prs/merge-all",(t,e)=>{e.json({jobs:Sf()})});k.post("/api/git/prs/open",async(t,e)=>{let{trackId:n,title:r,body:o,base:s}=t.body||{};try{let a=n?await rt(n):null;if(!a)return e.status(400).json({error:"trackId required"});if(a.workspace?.kind==="multi")return e.status(400).json({error:"this Track tags multiple repos \u2014 ask the lead in chat to push and open the PR(s); the Git-tab door becomes per-repo in the next phase"});let i=await Tf(a.cwd,{title:r,body:o,base:s}),c=await Y(a.cwd).catch(()=>null);c&&i.number&&await Nt({repo:c,number:i.number,trackId:a.id,url:i.url,headRef:a.workspace?.branch??null,title:r,source:"api"}),await hn("opened",{number:i.number,url:i.url,title:r,track:a,branch:a.workspace?.branch}),e.json(i)}catch(a){let i=a?.message||String(a),c=/required|invalid/i.test(i)?400:502;e.status(c).json({error:i})}});k.post("/api/git/prs/fix",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await _r(n,r),a=(await dt()).find(w=>!w.archivedAt&&w.workspace?.branch===o.headRefName);if(!a)return e.status(404).json({error:`no active Track owns branch "${o.headRefName}"`});let i=await Af(n,o.number);if(!i.length)return e.status(409).json({error:"no failing checks on this PR"});let c=ct.join(a.cwd,".bostrat","ci"),l=ct.join(".bostrat","ci",`pr-${o.number}-fail.log`);await It.promises.mkdir(c,{recursive:!0}),await It.promises.writeFile(ct.join(a.cwd,l),i.map(w=>`\u2550\u2550\u2550\u2550 ${w.name}${w.link?` \xB7 ${w.link}`:""} \u2550\u2550\u2550\u2550
267
+ ${w.log??"(no fetchable log \u2014 see link)"}
268
+ `).join(`
269
+ `));let u=i.map(w=>w.name).join(", "),d=await $e(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}. Failing check${i.length===1?"":"s"}: ${u}. The failure log is at ${l} in this worktree \u2014 read it, fix the root cause, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),f=await Y(n).catch(()=>null);f&&(await Nt({repo:f,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await xd(f,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await hn("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:i.map(w=>w.name)}}),e.json({ok:!0,leadBox:d.leadBox,queued:!!d.queued,checks:i.map(w=>w.name),log:l})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;e.status(a).json({error:s})}});k.post("/api/git/prs/reconcile",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await _r(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let a=(await dt()).find(l=>!l.archivedAt&&l.workspace?.branch===o.headRefName);if(!a)return e.status(404).json({error:`no active Track owns branch "${o.headRefName}"`});let i=await $e(a.id,`PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName} \u2014 has merge conflicts with ${o.baseRefName}. Bring ${o.baseRefName} into this branch (fetch origin, then merge origin/${o.baseRefName} \u2014 don't rebase this pushed PR branch), and resolve every conflict so that BOTH sides' intent is preserved. Run the relevant tests, then commit and push to update the PR. If any conflict is a genuine ambiguity \u2014 where you can't confidently tell which side's behavior should win, or reconciling would drop work from either side \u2014 STOP and ask me here rather than guessing, laying out the specific choice and your recommendation.`,{kind:"reconcile"}),c=await Y(n).catch(()=>null);c&&(await Nt({repo:c,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Sd(c,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await hn("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),e.json({ok:!0,leadBox:i.leadBox,queued:!!i.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;e.status(a).json({error:s})}});k.post("/api/tracks/:id/pr-offer",async(t,e)=>{let{offerId:n,action:r}=t.body||{};try{let o=null;if(Array.isArray(t.body?.actions)){if(o=hd.filter(u=>t.body.actions.includes(u)),!o.length)return e.status(400).json({error:"actions must include commit, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return e.status(400).json({error:'offerId and actions ["commit"|"push"|"pr"] (or action "dismiss") required'});if(!n)return e.status(400).json({error:"offerId required"});let s=await rt(t.params.id);if(!s)return e.status(404).json({error:"track not found"});let a=await B(s),i=a.find(u=>u.type==="pr.offered"&&u.payload?.offerId===n);if(!i)return e.status(404).json({error:"unknown offer"});if(a.some(u=>u.type==="pr.offer.answered"&&u.payload?.offerId===n))return e.status(409).json({error:"offer already answered"});let c=null;o&&(c=await $e(s.id,To(o,{branch:i.payload.branch,base:i.payload.base,toPr:i.payload.toPr||null}),{kind:o.includes("pr")?"pr-go":"push-go"}));let l=o?o.includes("pr")?"pr":o.includes("push")?"push":"commit":"dismiss";await R({id:s.id,cwd:s.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:i.boxId,payload:{ts:Date.now(),offerId:n,action:l,actions:o}}),e.json({ok:!0,offerId:n,action:l,actions:o,queued:!!c?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/not found|not running|archived/i.test(s)?409:502;e.status(a).json({error:s})}});var zr=t=>e=>({...e,url:nh(e,t.headers)});k.get("/api/views",async(t,e)=>{try{let n=await sf({trackId:t.query.track||void 0});e.json({views:n.map(zr(t)),maxLive:ve})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/views/:id",async(t,e)=>{let n=await af(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:zr(t)(n),session:Nn(n.slug)})});k.post("/api/views",async(t,e)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=t.body||{};try{let i=n?await rt(n):null;if(!i&&s&&(i=(await dt()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ct.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await wi({trackId:i.id,name:r,cmd:o,cwd:i.cwd,serveCwd:s,port:a,box:i.leadBox,branch:i.workspace?.branch??null});e.json({ok:!0,view:zr(t)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);e.status(l).json({error:c})}});k.post("/api/views/:id/stop",async(t,e)=>{try{e.json({ok:!0,view:zr(t)(await cf(t.params.id))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/views/:id/restart",async(t,e)=>{try{e.json({ok:!0,view:zr(t)(await lf(t.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;e.status(o).json({error:r})}});k.delete("/api/views/:id",async(t,e)=>{try{e.json(await uf(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var dA=[["linear",ln,oc],["jira",is,nc]],Gc=new Map;k.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await ec(),r={};for(let[o,s,a]of dA){let i=await s();if(!i){Gc.delete(o),r[o]={configured:!1};continue}let c=Gc.get(o);if(!c||Date.now()-c.at>300*1e3){let l;try{l={configured:!0,ok:!0,...await a(i)}}catch(u){l={configured:!0,ok:!1,error:u?.message||String(u)}}c={at:Date.now(),body:l},Gc.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}e.json(r)}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/tickets/flow",async(t,e)=>{try{e.json({flow:await ec()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/tickets/flow",async(t,e)=>{let{trackId:n,cwd:r,set:o,actor:s}=t.body||{};try{let a=await Kp(o),i=n?await rt(n):null;!i&&r&&(i=(await dt()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ct.sep)))||null),i&&await R({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),e.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);e.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});k.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await rt(n):null;if(!s&&r&&(s=(await dt()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ct.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return e.status(400).json({error:"ref (issue URL or key) required"});let a=await Yn(s,o,{source:n?"api":"cli"});e.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);e.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});k.get("/api/tickets/status",async(t,e)=>{let{cwd:n,track:r}=t.query;try{let o=r?await rt(r):null;if(!o&&n&&(o=(await dt()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ct.sep)))||null),!o)return e.status(400).json({error:"track (or a cwd inside a Track workspace) required"});e.json({trackId:o.id,configured:await um(),ticket:await te(o.id)})}catch(o){e.status(500).json({error:o?.message||String(o)})}});k.get("/api/tracks/:id/ticket",async(t,e)=>{e.json({ticket:await te(t.params.id)})});k.post("/api/tracks/:id/ticket",async(t,e)=>{try{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await Yn(n,(t.body||{}).ref,{source:"api"});e.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);e.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});k.delete("/api/tracks/:id/ticket",async(t,e)=>{e.json({ok:!0,removed:await hc(t.params.id)})});var Rs=t=>/no ticket linked|unknown offer|unknown state/i.test(t)?404:/already answered/i.test(t)?409:/required|no ticketing/i.test(t)?400:502;k.get("/api/tracks/:id/ticket/states",async(t,e)=>{try{e.json(await mm(t.params.id))}catch(n){let r=n?.message||String(n);e.status(Rs(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await wc(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(Rs(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await hm(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(Rs(r)).json({error:r})}});k.post("/api/tracks/:id/ticket-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{let o=await rt(t.params.id);if(!o)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await wm(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(Rs(s)).json({error:s})}});k.post("/api/tickets/propose",async(t,e)=>{let{trackId:n,cwd:r,plan:o}=t.body||{};try{let s=n?await rt(n):null;if(!s&&r&&(s=(await dt()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ct.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,trackId:s.id,proposal:await ym(s,o)})}catch(s){let a=s?.message||String(s);e.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});k.get("/api/tickets/teams",async(t,e)=>{try{e.json({teams:await gm()})}catch(n){let r=n?.message||String(n);e.status(/no ticketing/i.test(r)?400:502).json({error:r})}});k.post("/api/tracks/:id/ticket-create-offer",async(t,e)=>{let{proposalId:n,accept:r,teamId:o}=t.body||{};try{let s=await rt(t.params.id);if(!s)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await bm(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);e.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});k.get("/api/mocks",async(t,e)=>{try{e.json({mocks:await Bf({trackId:t.query.track||void 0,group:t.query.group||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/mocks/:id",async(t,e)=>{let n=await Ae(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});e.json({mock:n})});k.get("/api/mocks/:id/png",async(t,e)=>{let n=await Ae(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=rn(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(Bt(n,r.v).png,o=>{o&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});k.get("/api/mocks/:id/html",async(t,e)=>{let n=await Ae(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=rn(n,t.query.v??null);if(!r?.htmlRel)return e.status(404).json({error:"this version has no HTML source (image-sourced)"});e.set("content-security-policy","sandbox"),e.sendFile(Bt(n,r.v).html,o=>{o&&!e.headersSent&&e.status(404).json({error:"source artifact missing"})})});k.post("/api/mocks",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n?t.query:t.body||{},{trackId:o,name:s,cwd:a,width:i,group:c}=r;try{let l=o?await rt(o):null;if(!l&&a&&(l=(await dt()).find(w=>w.cwd&&(a===w.cwd||a.startsWith(w.cwd+ct.sep)))||null),!l)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(f=>f.name===l.leadBox),d=await Df({trackId:l.id,name:s,html:n?null:r.html,image:n,cwd:l.cwd,width:i,group:c,box:l.leadBox,agent:u?.agent||null});e.json({ok:!0,mock:d})}catch(l){let u=l?.message||String(l);e.status(/required|invalid|must be/i.test(u)?400:500).json({error:u})}});k.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null;try{let r=await jf(t.params.id,{html:n?null:(t.body||{}).html,image:n,width:n?t.query.width:(t.body||{}).width});e.json({ok:!0,mock:r})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|invalid|must be/i.test(o)?400:500;e.status(s).json({error:o})}});k.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await Uf(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await $e(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),e.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;e.status(o).json({error:r})}});k.delete("/api/mocks/:id",async(t,e)=>{try{e.json(await Ff(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.get("/api/runbooks",async(t,e)=>{try{let n=t.query.track||void 0;if(!n&&t.query.cwd){let r=await dt(),o=String(t.query.cwd);if(n=r.find(s=>s.cwd&&(o===s.cwd||o.startsWith(s.cwd+ct.sep)))?.id,!n)return e.json({runbooks:[]})}e.json({runbooks:await zf({trackId:n,status:t.query.status||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/runbooks/:id",async(t,e)=>{let n=await Ri(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});e.json({runbook:n})});k.get("/api/runbooks/:id/md",async(t,e)=>{let n=await Ri(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=Kf(n,t.query.v??null);if(!r)return e.status(404).json({error:"no such version"});e.set("content-security-policy","sandbox"),e.set("cache-control","private, max-age=31536000, immutable"),e.type("text/markdown; charset=utf-8"),e.sendFile(Un(n,r.v),o=>{o&&!e.headersSent&&e.status(404).json({error:"runbook artifact missing"})})});k.post("/api/runbooks",async(t,e)=>{let{trackId:n,name:r,md:o,cwd:s}=t.body||{};try{let a=n?await rt(n):null;if(!a&&s&&(a=(await dt()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ct.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await Xf({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});e.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);e.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});k.post("/api/runbooks/:id/update",async(t,e)=>{try{let{md:n,note:r}=t.body||{};e.json({ok:!0,runbook:await Yf(t.params.id,{md:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/required|invalid|over the|secret VALUE/i.test(r)?400:500;e.status(o).json({error:r})}});k.post("/api/runbooks/:id/status",async(t,e)=>{try{e.json({ok:!0,runbook:await Zf(t.params.id,(t.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;e.status(o).json({error:r})}});k.delete("/api/runbooks/:id",async(t,e)=>{try{e.json(await Qf(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/dictation",async(t,e)=>{if(!Buffer.isBuffer(t.body)||t.body.length===0)return e.status(400).json({error:"expected a raw audio body"});try{e.json(await Mh(t.body,t.headers["content-type"]))}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});k.get("/api/dictation/glossary",async(t,e)=>{e.json({configured:!!await Br(),terms:await qc(),problem:await Ch()})});k.post("/api/dictation/learn",async(t,e)=>{let{transcript:n,finalText:r}=t.body||{};if(typeof n!="string"||typeof r!="string")return e.status(400).json({error:"transcript and finalText are required"});e.json({learned:await Oh(n,r)})});k.post("/api/tracks",async(t,e)=>{let{name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i,ticket:c}=t.body||{};try{let l=await Ki({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),u=null;if(c)try{await Yn(l,c,{source:"create"})}catch(d){u=d?.message||String(d)}e.json({ok:!0,track:l,...u?{ticketWarning:u}:{}})}catch(l){let u=l?.message||String(l);e.status(/required|unknown agent|invalid|must be/i.test(u)?400:500).json({error:u})}});k.get("/api/tracks/:id",async(t,e)=>{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json({track:n,handoff:Nr(t.params.id),subtasks:Jn(t.params.id).map(Xr)})});k.delete("/api/tracks/:id",async(t,e)=>{try{let n=await Op(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await hc(t.params.id).catch(()=>{}),e.json(n)}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/tracks/:id/archive",async(t,e)=>{try{e.json({ok:!0,track:await Vn(t.params.id)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/already archived/i.test(r)?409:500).json({error:r})}});k.post("/api/tracks/:id/unarchive",async(t,e)=>{try{e.json({ok:!0,track:await Cp(t.params.id)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/not archived/i.test(r)?409:500).json({error:r})}});k.get("/api/tracks/:id/messages",async(t,e)=>{let n=await Wi(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json(n)});k.get("/api/tracks/:id/events",async(t,e)=>{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.set({"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive","x-accel-buffering":"no"}),e.flushHeaders();let r=i=>e.write(`id: ${i.seq}
270
+ data: ${JSON.stringify(i)}
271
+
272
+ `),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await B(n))i.seq>o&&r(i);let s=od(n.id,r),a=setInterval(()=>e.write(`: hb
273
+
274
+ `),25e3);t.on("close",()=>{clearInterval(a),s()})});k.post("/api/tracks/:id/lead/restart",async(t,e)=>{try{e.json({ok:!0,track:await Pp(t.params.id)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/mid-turn|terminal|archived/i.test(r)?409:500;e.status(o).json({error:r})}});k.post("/api/tracks/:id/say",async(t,e)=>{let{text:n,attachments:r}=t.body||{};try{e.json(await $e(t.params.id,n,{attachments:r}))}catch(o){let s=o?.message||String(o);e.status(/not found/i.test(s)?404:/archived/i.test(s)?409:/required|not running/i.test(s)?400:500).json({error:s})}});k.post("/api/tracks/:id/upload",async(t,e)=>{try{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});if(n.archivedAt)return e.status(409).json({error:"track is archived"});if(!n.cwd)return e.status(409).json({error:"track has no workspace yet"});let r=Buffer.isBuffer(t.body)?t.body:null,o=await sp(n.cwd,{filename:t.query.filename,buffer:r});e.json({ok:!0,attachment:o})}catch(n){let r=n?.message||String(n);e.status(/too large|unsupported|empty|required/i.test(r)?400:500).json({error:r})}});k.get("/api/tracks/:id/uploads/:file",async(t,e)=>{try{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});if(!n.cwd)return e.status(404).json({error:"track has no workspace"});let r=ct.basename(String(t.params.file||""));if(!r||!Oi(ct.extname(r).slice(1)))return e.status(400).json({error:"not an upload path"});e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox"),e.sendFile(ct.join($i(n.cwd),r),o=>{o&&!e.headersSent&&e.status(404).json({error:"upload not found"})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});var fA=/^[0-9a-fA-F-]{8,64}$/;k.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(t,e)=>{try{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});let{box:r,uuid:o}=t.params,s=Number(t.params.idx);if(!fA.test(o)||!Number.isInteger(s)||s<0)return e.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(f=>f.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return e.status(404).json({error:"image source not available"});let i=ne(a.cwd,a.sessionId);if(!i)return e.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=sA.createInterface({input:It.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let f of u){if(!f.includes(c))continue;let w;try{w=JSON.parse(f)}catch{continue}if(w.uuid===o){l=w,u.close();break}}let d=l?qs(l)[s]:null;if(!d||!/^image\//.test(d.mediaType))return e.status(404).json({error:"image not found"});e.set("content-type",d.mediaType),e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox"),e.send(Buffer.from(d.data,"base64"))}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/tracks/:id/interrupt",async(t,e)=>{try{e.json(await Gi(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/archived/i.test(r)?409:500).json({error:r})}});k.delete("/api/tracks/:id/queue",(t,e)=>{e.json({ok:!0,removed:os(t.params.id)})});k.post("/api/tracks/:id/answer-prompt",async(t,e)=>{let{promptId:n,answers:r,text:o}=t.body||{};try{e.json(await qi(t.params.id,{promptId:n,answers:r,text:o}))}catch(s){let a=s?.message||String(s),i=/not found/i.test(a)?404:/no pending prompt|changed since|not running|not supported|needs a code|look like a sign-in code/i.test(a)?409:500;e.status(i).json({error:a})}});k.post("/api/setup/:provider/start",async(t,e)=>{try{let n=await Rp({provider:t.params.provider,fresh:t.body?.fresh===!0});e.json({ok:!0,track:n})}catch(n){let r=n?.message||String(n);e.status(/unknown setup provider|isn't available/i.test(r)?400:500).json({error:r})}});k.post("/api/tracks/:id/navigate-prompt",async(t,e)=>{let{promptId:n,questionIndex:r}=t.body||{};try{e.json(await kp(t.params.id,{promptId:n,questionIndex:r}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/no pending prompt|changed since|not running|not supported|out of range|review screen/i.test(s)?409:500;e.status(a).json({error:s})}});k.put("/api/tracks/:id/draft",async(t,e)=>{let{text:n}=t.body||{};try{e.json(await Tp(t.params.id,typeof n=="string"?n:""))}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required/i.test(o)?400:500;e.status(s).json({error:o})}});k.get("/api/tracks/:id/usage",async(t,e)=>{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=n.boxes?.find(s=>s.name===n.leadBox)||null,o=t.query.fresh==="1"||t.query.fresh==="true";e.json(await zm(r,{fresh:o}))});k.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await Ap(t.params.id,n))}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|unknown model|not running/i.test(o)?400:500;e.status(s).json({error:o})}});k.post("/api/tracks/:id/subtask",async(t,e)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=t.body||{};try{let a=await Lp(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:Xr(a)})}catch(a){let i=a?.message||String(a),c=/not found/i.test(i)?404:/required|unknown agent|git repo|not running|can't resolve/i.test(i)?400:500;e.status(c).json({error:i})}});k.get("/api/tracks/:id/subtasks",(t,e)=>{e.json({subtasks:Jn(t.params.id).map(Xr)})});k.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await bp(t.params.id,t.params.sid);if(!n)return e.status(404).json({error:"subtask not found"});e.json(n)});k.post("/api/tracks/:id/subtask/:sid/cancel",(t,e)=>{try{e.json({ok:!0,subtask:Xr(Yi(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});k.post("/api/tracks/:id/subtask/:sid/merge-now",(t,e)=>{try{e.json({ok:!0,subtask:Xr(Fp(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function Xr(t){return t?{id:t.id,agent:t.agent,task:t.task,skipPermissions:t.skipPermissions,step:t.step,running:!t.finishedAt,box:t.box,branch:t.branch,stat:t.stat,files:t.files||null,conflicts:t.conflicts||[],applied:t.applied,error:t.error,canceled:t.canceled,startedAt:t.startedAt,finishedAt:t.finishedAt}:null}k.post("/api/tracks/:id/handoff-lead",async(t,e)=>{let{toAgent:n,skipPermissions:r,reason:o}=t.body||{};try{let s=await Ji(t.params.id,n,r!==!1,o);e.json({ok:!0,step:s.step,toAgent:s.toAgent,fromBox:s.fromBox,skipPermissions:s.skipPermissions})}catch(s){let a=s?.message||String(s);e.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});k.post("/api/tracks/:id/spin-up-view",async(t,e)=>{try{e.json(await vp(t.params.id,t.body||{}))}catch(n){let r=n?.message||String(n);e.status(/already being spun up/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});k.get("/api/tracks/:id/handoff-lead",(t,e)=>{let n=Nr(t.params.id);if(!n)return e.json({running:!1});e.json({running:!n.finishedAt,step:n.step,error:n.error,fromBox:n.fromBox,toBox:n.toBox,toAgent:n.toAgent,skipPermissions:n.skipPermissions,startedAt:n.startedAt,finishedAt:n.finishedAt})});k.get("/api/debug/handoff",async(t,e)=>{let n=Math.min(Number(t.query.limit)||80,500);e.json({verbose:to(),paths:nl,trace:await el({limit:n})})});var Kc=ct.resolve(jh,"..","..","client","dist");It.existsSync(Kc)&&(k.use(fe.static(Kc)),k.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(ct.join(Kc,"index.html"))));var Fh=iA.createServer(k),pA=new lA({noServer:!0}),Hh=(t,e,n)=>{if(!rr.allowed(rr.clientIp(t))){e.write(`HTTP/1.1 403 Forbidden\r
275
+ \r
276
+ `),e.destroy();return}if(eh(t,e,n))return;let r=dl(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
277
+ \r
278
+ `),e.destroy();return}let o=r.sessionName;if(!ut(o)){e.write(`HTTP/1.1 400 Bad Request\r
279
+ \r
280
+ `),e.destroy();return}let{cols:s,rows:a}=r;pA.handleUpgrade(t,e,n,i=>cl(i,o,{cols:s,rows:a}))};Fh.on("upgrade",Hh);var Wh=process.env.BOSTRAT_CERT_DIR||ct.join(process.env.HOME||Vc.tmpdir(),".bostrat","certs"),Nh=Number(process.env.BOSTRAT_TLS_PORT)||3443,Bh=ct.join(Wh,"bostrat.globopi.key"),Dh=ct.join(Wh,"bostrat.globopi.crt");if(It.existsSync(Bh)&&It.existsSync(Dh)){let t=cA.createServer({key:It.readFileSync(Bh),cert:It.readFileSync(Dh)},k);t.on("upgrade",Hh),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(Nh,Is,()=>{console.log(`[bostrat-agent] https listening on ${Is||"0.0.0.0"}:${Nh}`)})}Fh.listen(Ph,Is,()=>{console.log(`[bostrat-agent] listening on ${Is||"0.0.0.0"}:${Ph}`),console.log(`[bostrat-agent] tmux socket: ${Ht}`),console.log(`[bostrat-agent] auto-trigger default: ${Lh}%`),console.log(`[bostrat-agent] ip allowlist: ${rr.open?"OPEN (BOSTRAT_ALLOW_IPS unset)":process.env.BOSTRAT_ALLOW_IPS}`),Dp().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),jp().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Mp().then(t=>t&&console.log(`[bostrat-agent] reaped ${t} orphaned box(es) on archived track(s)`)).catch(t=>console.error("[bostrat-agent] archived-orphan sweep failed:",t?.message||t)),Ip().then(t=>console.log(`[bostrat-agent] standing instructions refreshed (${t.refreshed} updated, ${t.removed} legacy CLAUDE.md removed${t.codexGlobal?`, codex global ${t.codexGlobal}`:""})`)).catch(t=>console.error("[bostrat-agent] standing-instructions refresh failed:",t?.message||t)),gA(),wA(),ch(),km({resolveTrack:rt}),Rm({listTracks:dt,createTrack:Ki,archiveTrack:Vn,listRepos:oe,resolveTrack:rt,sayToLead:$e,answerPrompt:qi,startLeadHandoff:Ji,interruptLead:Gi,listSubtaskJobs:Jn,cancelSubtask:Yi,readEvents:B,appendEvent:R,knownAgents:Fi}),mA(),Sh(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(Nm({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||Vc.hostname(),name:process.env.BOSTRAT_NODE_NAME||Vc.hostname(),listTracks:dt}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`))});function mA(){hA(),Wo().catch(()=>{}),setInterval(()=>Wo().catch(()=>{}),1e4)}function hA(){for(let t of["bostrat-view","bostrat-mock","bostrat-ticket","bostrat-runbook","gh"])try{let e=ct.resolve(jh,"..","bin",t),n=ct.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!It.existsSync(e))continue;It.mkdirSync(n,{recursive:!0});let r=ct.join(n,t);(It.existsSync(r)?It.readlinkSync(r):null)!==e&&(It.rmSync(r,{force:!0}),It.symlinkSync(e,r)),console.log(`[bostrat-agent] ${t} CLI linked at ${r}`)}catch(e){console.error(`[bostrat-agent] ${t} CLI link failed:`,e?.message||e)}}function wA(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await dt(),r=await Gm({tracks:n});r.committed&&console.log(`[state-sync] committed (${r.staged} file(s) staged${r.pushed?", pushed":""})`)}catch{}finally{t=!1}}};setInterval(e,6e4),e()}function gA(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{let e=await dt();for(let n of e)n.archivedAt||!n.boxes?.some(r=>r.state==="live")||(await Wi(n.id).catch(()=>{}),await _f(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}