bostrat-node 0.2.0 → 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 CHANGED
@@ -1,86 +1,88 @@
1
- var Bh=Object.defineProperty;var Hc=(t,e)=>{for(var n in e)Bh(t,n,{get:e[n],enumerable:!0})};import ct from"node:path";import Et from"node:fs";import qT from"node:readline";import{fileURLToPath as GT}from"node:url";import fe from"express";import KT from"node:http";import VT from"node:https";import Fc from"node:os";import{WebSocketServer as JT}from"ws";import{execFile as Kh}from"node:child_process";import{promisify as Vh}from"node:util";import Xc from"node:fs";import{randomUUID as Jh}from"node:crypto";import fn from"node:fs/promises";import Wc from"node:os";import Le from"node:path";function Xr(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function Dh(){return process.env.CLAUDE_CONFIG_DIR?Le.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Le.join(Wc.homedir(),".claude.json")}function jh(){return process.env.CLAUDE_CONFIG_DIR?Le.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Le.join(Wc.homedir(),".claude","settings.json")}async function pe(t){if(!t||typeof t!="string")return;let e=Le.resolve(t),n=Dh(),r=jh(),o={};try{o=JSON.parse(await fn.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 fn.mkdir(Le.dirname(n),{recursive:!0}),await fn.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await fn.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 fn.mkdir(Le.dirname(r),{recursive:!0}),await fn.writeFile(r,JSON.stringify(i,null,2))}import Rs from"node:fs/promises";import Lh from"node:os";import Yr from"node:path";function Uh(){let t=process.env.CODEX_HOME||Yr.join(Lh.homedir(),".codex");return Yr.join(t,"config.toml")}function qc(t){return`[projects."${t}"]`}function Fh(t,e){let n=qc(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 pn(t){if(!t||typeof t!="string")return;let e=Yr.resolve(t),n=Uh(),r="";try{r=await Rs.readFile(n,"utf8")}catch{}if(Fh(r,e))return;let o=qc(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*$/,"")}
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
2
  trust_level = "trusted"
3
3
  `;r=r.slice(0,a)+d+r.slice(l)}else{let s=`
4
4
  ${o}
5
5
  trust_level = "trusted"
6
6
  `;r=r.trimEnd()+s+(r.length&&!r.endsWith(`
7
7
  `)?`
8
- `:"")}try{await Rs.mkdir(Yr.dirname(n),{recursive:!0}),await Rs.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}import Gc from"node:fs/promises";import Hh from"node:os";import er from"node:path";function Wh(t){let n=er.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||er.join(Hh.homedir(),".cursor");return er.join(r,"projects",n)}async function mn(t){if(!t||typeof t!="string")return;let e=er.resolve(t),n=Wh(e),r=er.join(n,".workspace-trusted");await Gc.mkdir(n,{recursive:!0}),await Gc.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
9
- `,"utf8")}import nr from"node:fs/promises";import qh from"node:os";import Vc from"node:path";var $s=process.env.BOSTRAT_DEBUG_DIR||Vc.join(qh.homedir(),".bostrat","debug"),hn=Vc.join($s,"handoff.jsonl"),Kc=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;function Zr(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function Gh(t){try{await nr.mkdir($s,{recursive:!0}),await nr.appendFile(hn,`${JSON.stringify(t)}
10
- `,"utf8");let n=(await nr.readFile(hn,"utf8")).split(`
11
- `).filter(Boolean);n.length>Kc&&await nr.writeFile(hn,`${n.slice(-Kc).join(`
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
12
  `)}
13
- `,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${hn}: ${e?.message||e}`)}}async function j(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&&Zr())try{e.paneTail=(await pt(e.sessionName,{lines:35})).slice(-4e3)}catch{e.paneTail=null}(Zr()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed)/.test(String(e.phase||e.step||"")))&&await Gh(e)}async function Jc({limit:t=80}={}){try{return(await nr.readFile(hn,"utf8")).split(`
14
- `).filter(Boolean).slice(-t).map(r=>{try{return JSON.parse(r)}catch{return{raw:r}}})}catch{return[]}}var zc={DEBUG_DIR:$s,LOG_FILE:hn};var et=Vh(Kh);function rr(t){return new Promise(e=>setTimeout(e,t))}function Os(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",zh=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Cs=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Ms="bostrat-usage-probe",Xh=new Set([Ms]);function Ns(t,e){let n=wn(t),r=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);return!n||r?t:`${t} --session-id ${e}`}function wn(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function gn(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}async function Ps(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 et(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:5e3}),!0}catch(s){return s?.code===1?!1:n}}async function Bs(t){return Ps(String(t||"").trim().split(/\s+/)[0])}function C(...t){return["-S",Ht,...t]}async function ee(){if(!Xc.existsSync(Ht))return!1;try{return await et(O,C("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}var Yh=/^[A-Za-z0-9_.-]{1,64}$/;function ut(t){return typeof t=="string"&&Yh.test(t)}async function yn(){if(!Xc.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 et(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=>!Xh.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function q(t){return(await yn()).find(n=>n.name===t)||null}var Zh=/^[A-Za-z0-9 ._-]+$/,Qh=64;function tw(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=Qh&&Zh.test(e)}function Ds(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"")}async function ew(t){try{let{stdout:e}=await et(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=zh,n={}){if(!tw(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=Ds(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 ew(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Cs,a=n.agent||gn(s),i=!!n.resumeSessionId,c=n.resumeSessionId||Jh(),l=i?s:Ns(s,c),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&wn(s),f=n.firstRun===!0;f||(d&&await pe(e),a==="codex"&&await pn(e),a==="cursor"&&await mn(e));let w=d&&!f?Xr():null,h=w?["-e",`ANTHROPIC_API_KEY=${w}`]:[];try{await et(O,C("new-session","-d","-s",o,"-c",e,...h))}catch(g){throw w?Os(g,w):g}return await Fe(o),await et(O,C("set-option","-t",o,"@bostrat-session","1")),await et(O,C("set-option","-t",o,"@bostrat-title",r)),await et(O,C("set-option","-t",o,"@bostrat-sid",c)),await et(O,C("set-option","-t",o,"@bostrat-agent",a)),await et(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 et(O,C("kill-session","-t",t))}async function Yc(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 et(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 et(O,n);return String(r||"")}catch{return""}}var nw=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,rw=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,It=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,ow=Number(process.env.BOSTRAT_BOX_COLS)||120,sw=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function aw(t){if(!ut(t))return 0;try{let{stdout:e}=await et(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=ow,rows:n=sw}={}){if(ut(t)&&!await aw(t))try{let r=await Qc(t);if(r.cols===e&&r.rows===n)return;await et(O,C("resize-window","-t",t,"-x",String(e),"-y",String(n))),await et(O,C("set-window-option","-t",t,"window-size","latest")),await rr(80)}catch{}}async function Zc(t){if(!ut(t))return 0;try{let{stdout:e}=await et(O,C("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function Qc(t){if(!ut(t))return{cols:0,rows:0};try{let{stdout:e}=await et(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 Qr(t,{cols:e=nw,rows:n=rw}={}){if(ut(t))try{let r=await Qc(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await et(O,C("resize-window","-t",t,"-x",String(o),"-y",String(s))),await rr(80))}catch{}}async function me(t){return await Qr(t),pt(t,{lines:It})}var iw=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=>iw.has(s));for(let s=0;s<o.length;s++)await et(O,C("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await rr(n);return j({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function to(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 et(O,C("send-keys","-t",t,"-N",String(r),"BSpace")),j({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function eo(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 et(O,C("send-keys","-t",t,"-l",s)),n>0&&await rr(n);j({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 et(O,C("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await rr(s),await et(O,C("send-keys","-t",t,"Enter"))}j({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 cw from"node-pty";async function el(t,e,n={}){if(!ut(e)){no(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await ee()){no(t,`host tmux server not running on ${Ht}`),t.close(1011,"tmux server unavailable");return}if(!await q(e)){no(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=tl(n.cols,120,nl),o=tl(n.rows,32,rl),s;try{s=cw.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){no(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&js(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(js(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"&&lw(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 nl=20,rl=6;function lw(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=nl&&Number(e)>=rl}function tl(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function js(t,e){try{t.send(JSON.stringify(e))}catch{}}function no(t,e){js(t,{type:"error",message:e})}function ol(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 or from"node:fs";import uw from"node:fs/promises";import bn from"node:path";import dw from"node:os";var sl={"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},fw=2e5,Ls=process.env.BOSTRAT_CLAUDE_PROJECTS||bn.join(dw.homedir(),".claude","projects");function Us(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function Fs(t){let e=bn.join(Ls,Us(t)),n;try{n=or.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=bn.join(e,o);try{let{mtimeMs:a}=or.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=bn.join(Ls,Us(t),`${e}.jsonl`);return or.existsSync(n)?n:null}function al(t){if(!t)return[];let e=bn.join(Ls,Us(t)),n;try{n=or.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=or.statSync(bn.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function St(t,e=131072){let n=await uw.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 pw(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 sr(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=Fs(t),o=!1,!r)return null}else return null;let s;try{s=pw(await St(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(sl,s.model),i=sl[s.model]||fw;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 mw(t,e){return!t||!e?null:ne(t,e)}function hw(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 ww(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 gw(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function Hs(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 il(t,e){if(!t.uuid)return null;let n=Hs(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 yw=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function Ws(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await St(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 h=c.attachment;h?.type==="queued_command"&&h.commandMode==="task-notification"&&typeof h.prompt=="string"&&h.prompt.trim()&&o.push({role:"user",text:h.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 h of l.content){let g=h?.type==="tool_use"?h.input?.file_path||h.input?.path:null;typeof g=="string"&&yw.test(g)&&s.set(h.id,g.split("/").pop())}if(gw(l.content)){let h=il(c,s);h&&o.push({role:"assistant",text:"",images:h,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let d=hw(l.content),f=u==="assistant"?ww(l.content):null,w=u==="user"?il(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 bw=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function cl(t,e,{turns:n=16}={}){let r=mw(t,e);if(!r)return"";let o=await Ws(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}:** ${bw(s.text||c,700)} ${i}`.trim()}).join(`
21
-
22
- `):""}import Dw from"node:fs";import Zs from"node:fs/promises";import io from"node:path";import kw from"node:fs/promises";import{execFile as xw}from"node:child_process";import{promisify as Sw}from"node:util";var qs=Sw(xw),vw=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,ll=new Set,ul=Promise.resolve(),Gs=null;function fl(t){Gs=typeof t=="function"?t:null}function Tw(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function pl({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||ll.has(r)||(ll.add(r),ul=ul.then(()=>Aw({sessionName:t,from:e,to:r})).catch(()=>{}))}async function Aw({sessionName:t,from:e,to:n}){await j({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await qs("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await kw.realpath(r),s=Tw(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await qs("npm",["install","-g","@openai/codex"],{env:a,timeout:vw});let i=(await qs(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await j({flow:"codex-update",phase:"done",sessionName:t,from:e,to:n,version:i}).catch(()=>{}),await dl({sessionName:t,from:e,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await j({flow:"codex-update",phase:"failed",sessionName:t,from:e,to:n,error:o}).catch(()=>{}),await dl({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function dl(t){if(Gs)try{await Gs(t)}catch{}}var _w=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Ks=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,ar=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Rt={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/}},Ew=/Trust this workspace|\[a\]\s*Trust/,Iw=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function Rw(t){let e=String(t||"");if(!Rt.codex.block.test(e))return null;let n=e.match(Iw);return{from:n?.[1]||null,to:n?.[2]||null}}function he(t){return new Promise(e=>setTimeout(e,t))}var $w=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,ro=new Map;function ml(t,e){let n=Date.now(),r=$w();for(let[s,a]of ro)n-a>=r&&ro.delete(s);let o=`${t}::${e}`;return ro.has(o)?!1:(ro.set(o,n),!0)}var Ow=68,Cw=/^\s*[─━]{10,}\s*$/;function Mw(t){let e=0;for(let n of String(t).split(`
23
- `))Cw.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function Vs(t,e){let n=Rt[t];if(!n?.busy)return"idle";if(Js(t,e))return"unknown";if(n.up&&!n.up.test(e))return"idle";if(n.busy.test(e))return"busy";let r=Mw(e);return t==="claude"&&r>0&&r<Ow?"unknown":"idle"}var Nw=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/;function wl(t,e){return t==="claude"&&Nw.test(String(e||""))}function ir(t,e){return Vs(t,e)==="busy"}function oo(t,e){return Vs(t,e)!=="idle"}function so(t,e){let n=Rt[t];return n?.up?n.up.test(e):!0}function Pw(t,e){let n=Rt[t];return n?Js(t,e)?!1:n.up.test(e)&&Vs(t,e)==="idle":!0}function Js(t,e){let n=Rt[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 hl(t,e,n){let r=Date.now()+n,o=0;for(await Fe(t).catch(()=>{});;){let s=await pt(t);if(Pw(e,s)){await he(ar);return}if(e==="cursor"&&Ew.test(s)&&!Rt.cursor.up.test(s)&&o<3){if(!ml(t,"cursor-trust")){await he(Ks);continue}o+=1,await j({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(ar);continue}if(e==="codex"&&o<3){let a=Rw(s);if(a){if(!ml(t,`codex-update:${a.from}->${a.to}`)){await he(Ks);continue}o+=1,await j({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"}),pl({sessionName:t,from:a.from,to:a.to}),await he(ar);continue}}if(Date.now()>r){if(Js(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=Rt.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"&&Rt.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"),j({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.`),j({flow:"agent-ready",phase:"timeout",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,paneBlocked:!!Rt[e]?.block?.test(s),paneUp:!!Rt[e]?.up?.test(s),paneBusy:!!Rt[e]?.busy?.test(s)}).catch(()=>{}),await he(ar);return}await he(Ks)}}function gl({agent:t,agentCmd:e}={}){let n=e?gn(e):gn(t||"");return{label:n,realClaude:e?wn(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 ao({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=gl({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=_w()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=gl({agent:r,agentCmd:o});if(i&&t&&await pe(t),c&&t&&await pn(t),l&&t&&await mn(t),(i||c||l)&&n){await hl(n,a,s);return}let u=Rt[r]?r:Rt[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await hl(n,u,s);return}await he(ar)}var Bw=(()=>{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:Bw}:{enter:!0}}var yl=".bostrat/handoff",bl="latest.md",kl="Checkpoint: write a work-in-progress handoff",xl="HANDOFF WRITTEN",Sl="Resume from .bostrat/handoff/latest.md",jw="/clear",Lw=t=>`${kl} 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 ${xl} and take no further action.`,Uw=`${Sl}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`;function Fw(t,e){let n=(e||"").trim();return n?t==="assistant"?n===xl:n.startsWith(kl)||n===jw||n.startsWith(Sl):!1}var Hw=6e5;function vl(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-Hw;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 Tl(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 Al(t,{windows:e=[]}={}){if(Fw(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 Ww=3500,qw=3e5,zs=new Map;function co(t){return zs.get(t)||null}async function _l(t){let e=zs.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await q(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:io.join(n.cwd,yl,bl)};return zs.set(t,r),Gw(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function Gw(t,e){t.step="summarize",await El(t.name,e),t.step="clear";let n=await q(t.name);await Ys(t.name,n,"/clear"),await Rl(Ww),t.step="resume",await Ys(t.name,n,Uw),t.step="done",t.finishedAt=Date.now()}async function El(t,e){let n=io.join(e,yl),r=io.join(n,bl);await Zs.mkdir(n,{recursive:!0});let o=await Xs(r),s=await q(t);return await j({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:r,beforeMtime:o}),await Ys(t,s,Lw(r)),await Kw(r,o,qw),await j({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:r,afterMtime:await Xs(r)}),await Vw(n,r),r}async function Il(t){let e=await q(t);if(!e)throw new Error(`session "${t}" not found`);if(!e.cwd)throw new Error(`can't resolve cwd for "${t}"`);return El(t,e.cwd)}async function Xs(t){try{return(await Zs.stat(t)).mtimeMs}catch{return 0}}async function Kw(t,e,n){let r=Date.now()+n;for(;;){if(await Xs(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await Rl(2e3)}}async function Vw(t,e){try{if(!Dw.existsSync(e))return;let n=new Date().toISOString().replace(/[:.]/g,"-");await Zs.copyFile(e,io.join(t,`${n}.md`))}catch{}}function Rl(t){return new Promise(e=>setTimeout(e,t))}async function Ys(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 q(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 D from"node:fs/promises";import G from"node:path";import{randomUUID as Xo}from"node:crypto";import{execFile as Ex}from"node:child_process";import{promisify as Ix}from"node:util";import Qs from"node:fs/promises";import Jw from"node:path";var zw=0;async function qt(t,e,{mode:n}={}){await Qs.mkdir(Jw.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++zw}.tmp`;await Qs.writeFile(r,JSON.stringify(e,null,2)+`
24
- `,n!=null?{mode:n}:void 0),await Qs.rename(r,t)}import{execFile as Xw}from"node:child_process";import{promisify as Yw}from"node:util";import{randomUUID as Zw}from"node:crypto";var He=Yw(Xw),lo=t=>new Promise(e=>setTimeout(e,t)),Qw=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,$l=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,tg=250,eg=400,Ol={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 Ml(t){let e=await Zc(t);if(!e)return!1;try{return await He("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function ta(t,e){let n=Date.now()+e;for(;;){if(await Ml(t))return!0;if(Date.now()>n)return!1;await lo(tg)}}async function ng(t,e){let n=Ol[e]||Ol.default;n.escape&&(await gt(t,["Escape"],{kind:"swap-exit"}),await lo(150)),n.wipe&&await to(t,400,{kind:"swap-exit"}),await eo(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=re(e).submitDelayMs||0;await lo(Math.max(r,250)),await gt(t,["Enter"],{kind:"swap-exit"})}async function rg(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 Cl(t){await He(O,C("respawn-pane","-k","-t",t))}async function ea(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 q(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||gn(n),a=Number(e.exitTimeoutMs)||Qw;await j({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Ml(t)&&(await ng(t,o),!await ta(t,a)&&(i=!0,await j({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await Cl(t),!await ta(t,$l))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&wn(n),l=c?Xr():null,u=await rg(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?Os(x,l):x}if(d&&!i&&(await Cl(t),!await ta(t,$l)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let f=!!e.resumeSessionId,w=e.resumeSessionId||Zw(),h=f?n:Ns(n,w),g=f?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(h);return c&&await pe(r.cwd),s==="codex"&&await pn(r.cwd),s==="cursor"&&await mn(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 lo(eg),await yt(t,h,{kind:"swap-launch"}),await j({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 mg from"node:fs";import Pl from"node:fs/promises";import cr from"node:path";import hg from"node:os";import{fileURLToPath as wg}from"node:url";import{execFile as gg}from"node:child_process";import{promisify as yg}from"node:util";import ra from"node:fs/promises";import ge from"node:path";import{execFile as og}from"node:child_process";import{promisify as sg}from"node:util";var ag=sg(og),Nl=process.env.HOME||"/home/globo",ig=ge.join(process.env.BOSTRAT_STATE_DIR||ge.join(Nl,".bostrat"),"repos"),cg=[...(process.env.BOSTRAT_REPOS_ROOT||ge.join(Nl,"Documents")).split(","),ig].map(t=>t.trim()).filter(Boolean),lg=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,ug=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function dg(t){try{return(await ra.stat(ge.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function fg(t){let e=i=>ra.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 dg(n);if(o==="worktree")return;if(o==="repo"){t.set(n,{path:n,name:ge.basename(n),branch:await fg(n)});return}if(r>=lg)return;let s;try{s=await ra.readdir(n,{withFileTypes:!0})}catch{return}for(let a of s)!a.isDirectory()||a.name.startsWith(".")||ug.has(a.name)||await e(ge.join(n,a.name),r+1)}for(let n of cg)await e(ge.resolve(n),0);return[...t.values()].sort((n,r)=>n.name.localeCompare(r.name))}function pg(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var na=new Map;async function Z(t){if(!t)return null;if(na.has(t))return na.get(t);let e=null;try{let{stdout:n}=await ag("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=pg(n)}catch{e=null}return na.set(t,e),e}var Bl=yg(gg),bg=process.env.BOSTRAT_STATE_DIR||cr.join(process.env.HOME||hg.tmpdir(),".bostrat"),kg=cr.join(bg,"repos"),Dl=/^[^/\s]+\/[^/\s]+$/,xg=5*6e4,Sg=6e4,vg=10*6e4,jl=fetch,oa=new Map,We=null;function sa(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 uo(t=process.env){return!!(sa(t)&&t.BOSTRAT_PLANE_TOKEN)}var Ll=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),kn=(t,e)=>Object.assign(new Error(t),{status:e});async function fo(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!Dl.test(n))throw kn("repo must be owner/name",400);let r=oa.get(n);if(r&&r.expiresAt-Date.now()>xg)return r;if(!uo(e))throw kn("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await jl(`${sa(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:Ll(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw kn(`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 kn(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 oa.set(n,a),a}async function lr({env:t=process.env,force:e=!1}={}){if(!uo(t))return{configured:!1,repos:[],username:null};if(!e&&We&&Date.now()-We.at<Sg)return We.data;try{let n=await jl(`${sa(t)}/v1/github/repos`,{headers:Ll(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 Ul(){oa.clear(),We=null}async function Gt({cwd:t=null,slug:e=null,env:n=process.env}={}){try{let r=e||(t?await Z(t):null);if(!r)return{};let{token:o}=await fo(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function aa({env:t=process.env}={}){return(await lr({env:t})).username??null}var Tg=()=>wg(new URL("../bin/bostrat-git-credential",import.meta.url)),Ag=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function _g(t){let[e,n]=String(t).split("/");return cr.join(kg,e,n)}async function ia(t,{env:e=process.env}={}){let n=(...r)=>Bl("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${Tg()}" ${Ag(e)}`),await n("config","credential.useHttpPath","true")}async function Fl(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!Dl.test(String(t||"")))throw kn(`bad repo slug "${t}"`,400);let r=_g(t);if(mg.existsSync(cr.join(r,".git")))return r;let{token:o}=await fo(t,{env:e});await Pl.mkdir(cr.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Bl("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:vg,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Pl.rm(r,{recursive:!0,force:!0}).catch(()=>{}),kn(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await ia(r,{env:e}),r}import sy from"node:fs/promises";function Eg(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 ur(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 dr(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 Sn=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,bt=t=>t&&!/[$`]/.test(t)?t:null,st=t=>t.replace(/\/+$/,"").split("/").pop()||t,xn=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},ql=t=>t.startsWith("-"),Hl=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(ql(o)){e.has(o)&&r++;continue}n.push(o)}return n}var Ig={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"},Rg={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 Gl(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 $g(t){let e=Gl(t),n=bt(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 ca(t){if(t.includes("--files")){let s=bt(V(t,Hl)[0]);return s&&s!=="."?`List files in ${xn(s)}`:"List files"}let e=V(t,Hl),n=e[0];if(!n)return null;let r=bt(e.slice(1).find(s=>s!==".")),o=Sn(n,40);return r?`Search for \u201C${o}\u201D in ${xn(r)}`:`Search for \u201C${o}\u201D`}function la(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 ua(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 Og(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 Wl(t){return t.find(e=>/^https?:\/\//.test(e))||null}var Cg={git:t=>{let e=V(t,new Set(["-C","-c"])),n=Ig[e[0]];return n?n(e,t):null},gh:t=>{let e=V(t),n=Rg[e[0]];return n?n(e):null},sed:$g,rg:ca,grep:ca,egrep:ca,nl:t=>{let e=bt(V(t)[0]);return e?`Read ${st(e)}`:null},cat:t=>{let e=bt(V(t)[0]);return e?`Read ${st(e)}`:null},head:t=>{let e=bt(V(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${st(e)}`:null},tail:t=>{let e=bt(V(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${st(e)}`:null},wc:t=>{let e=bt(V(t)[0]);return e?`Count lines in ${st(e)}`:"Count input lines"},ls:t=>{let e=bt(V(t)[0]);return e&&e!=="."?`List files in ${xn(e)}`:"List files"},find:t=>bt(t[1])&&!ql(t[1])&&t[1]!=="."?`Find files in ${xn(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=bt(V(t)[0]);return e?`Create directory ${xn(e)}`:null},touch:t=>{let e=bt(V(t)[0]);return e?`Create ${st(e)}`:null},rm:t=>{let e=bt(V(t)[0]);return e?`Delete ${xn(e)}`:null},mv:t=>{let e=bt(V(t)[0]);return e?`Move ${st(e)}`:null},cp:t=>{let e=bt(V(t)[0]);return e?`Copy ${st(e)}`:null},chmod:t=>{let e=bt(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=Wl(t);return e?`Fetch ${Sn(e,56)}`:"Fetch a URL"},wget:t=>{let e=Wl(t);return e?`Fetch ${Sn(e,56)}`:"Fetch a URL"},npm:la,pnpm:la,yarn:la,npx:t=>{let e=V(t)[0];return e?`Run ${e}`:null},node:t=>ua(t,"Node"),python:t=>ua(t,"Python"),python3:t=>ua(t,"Python"),make:t=>{let e=V(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:Og,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}},Mg=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function Ng(t){let e=dr(ur(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(st(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return da(e[2]);let n=st(e[0]);if(Mg.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=Cg[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function da(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=Eg(e);for(let i=0;i<r.length-1;i++){let c=dr(ur(r[i])),l=dr(ur(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(st(c[0]))&&st(l[0])==="sed"){let u=Gl(l),d=V(c)[0];if(u&&d)return Sn(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=Ng(i);c&&o.push({d:c,head:st(dr(ur(i))[0]||""),sub:V(dr(ur(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":Sn(s?s.d:o[0].d,88)}var pa=new Set(["Bash"]);function vn(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||da(t)||vn(t)}function fa(t,e){if(t){if(Array.isArray(t)){for(let n of t)fa(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&pa.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))fa(n,e)}}}async function Kl(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await St(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(fa(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 Vl(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((h,g)=>g>=s&&(h.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(h=>({...h,n:i(h)}));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(h=>h.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 Pg=/^Async (?:agent|task) launched/i,Bg=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 Jl(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await St(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?vn(w):null),detail:w?vn(w):null,ts:d,proven:!0})}}else f.type==="tool_result"&&f.tool_use_id&&Pg.test(Bg(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 fr from"node:fs";import jg from"node:fs/promises";import qe from"node:path";import Lg from"node:os";import Ug from"node:crypto";import Fg from"node:zlib";var po=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,Tn=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 ma=t=>`"${String(t).replace(/"/g,'\\"')}"`;function Dg(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 zl(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 ${Tn(r)}`:`Read ${Tn(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?be(e.path,n):null,s=e?.glob?` --glob ${ma(e.glob)}`:"",a=jt(String(r),40);return{command:jt(`grep ${ma(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 ${ma(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 ${Tn(e.path)}`}:null;case"Write":return e?.path?{command:jt(`write ${be(e.path,n)}`,140),description:`Write ${Tn(e.path)}`}:null;case"Delete":return e?.path?{command:jt(`delete ${be(e.path,n)}`,140),description:`Delete ${Tn(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=Dg(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 ${Tn(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 Hg=process.env.BOSTRAT_CURSOR_CHATS||qe.join(Lg.homedir(),".cursor","chats");function Zl(t){let e=Ug.createHash("md5").update(String(t)).digest("hex");return qe.join(Hg,e)}function Ql(t){try{let e=JSON.parse(fr.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 Wg(t){let e=Zl(t),n;try{n=fr.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(!fr.existsSync(a))continue;let i=Ql(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 mo(t,e){if(!t)return null;if(e){let n=qe.join(Zl(t),e),r=qe.join(n,"store.db");if(fr.existsSync(r))return{id:e,dir:n,store:r,...Ql(n)||{createdAtMs:null,updatedAtMs:null}}}return Wg(t)}function Xl(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function qg(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=Fg.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function Gg(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 Kg(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!po.has(r.toolName))continue;let o=zl(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 Vg=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():"",Jg=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],Yl=process.getBuiltinModule?.("node:sqlite"),An={turns:[],trailingCommands:[],model:null,requestIds:[]};async function ha(t,e){if(!Yl)return An;let{DatabaseSync:n}=Yl,r;try{r=new n(t,{readOnly:!0})}catch{return An}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 An;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return An}let a=s.lastUsedModel||null,i=s.latestRootBlobId?Xl(r,s.latestRootBlobId):null;if(!i)return{...An,model:a};let c=[],l=[],u=[],d=null,f=[];for(let w of Gg(i)){let h=qg(Xl(r,w));if(!h)continue;let g=h.providerOptions?.cursor;if(g?.requestId&&f.push(g.requestId),g?.modelProviderMessageId&&f.push(g.modelProviderMessageId),h.role!=="assistant")continue;if(Array.isArray(h.content))for(let S of h.content){let v=S?.providerOptions?.cursor?.modelName;v&&(d=v)}l.push(...Kg(h.content,e).map((S,v)=>({...S,id:S.id||`${w}:${v}`}))),u.push(...Jg(h.content));let x=Vg(h.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 An}finally{try{r.close()}catch{}}}function tu(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(fr.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 eu(t,e,{limit:n=80}={}){let r=mo(t,e);if(!r)return[];let{turns:o,model:s}=await ha(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=tu(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 nu(t,e){let n=mo(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await ha(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 ru(t,e,{limit:n=80}={}){let r=mo(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await ha(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=tu(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 ou(t,e){let n=mo(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await jg.stat(n.store)).mtimeMs)}catch{return null}}import su from"node:fs";import ga from"node:fs/promises";import wa from"node:path";import zg from"node:os";var Xg=process.env.BOSTRAT_CODEX_SESSIONS||wa.join(process.env.CODEX_HOME||wa.join(zg.homedir(),".codex"),"sessions");async function au(t){try{return await ga.realpath(t)}catch{return t}}function Yg(t=Xg){let e=[],n=r=>{let o;try{o=su.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=wa.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:su.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function Zg(t,e=1048576){let n;try{n=await ga.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 Qg(t){let e=await Zg(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=Yg();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await au(t);for(let o of n){let s=await Qg(o.file);if(s&&(s===t||await au(s)===r))return o}return null}var ty=new Set(["output_text","text"]),ey=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&ty.has(e.type)).map(e=>e.text).join(`
32
- `).trim():"";async function iu(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await Ge(t,e);if(!o)return[];let s;try{s=await St(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 h;try{h=JSON.parse(w)}catch{continue}if((h.type==="turn_context"&&h.payload?.model||h.type==="session_meta"&&h.payload?.model)&&(i=h.payload.model),h.type==="event_msg"){let S=h.payload?.type;S==="task_started"?(l=!0,u=!1,d=h.payload?.turn_id||null):S==="task_complete"&&l&&(l=!1,!u&&h.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:ba,sourceId:h.payload?.turn_id||d||void 0,ts:h.timestamp?Date.parse(h.timestamp):null,model:i}));continue}if(h.type!=="response_item")continue;let g=h.payload;if(!g)continue;if((g.type==="function_call"||g.type==="custom_tool_call")&&g.name){u=!0,ya.has(g.name)||c.push(g.name);continue}if(g.type!=="message"||g.role!=="assistant")continue;u=!0;let x=ey(g.content),A=c.length?c.slice():null;c=[],!(!x&&!A)&&a.push({role:"assistant",text:x,tools:A||void 0,ts:h.timestamp?Date.parse(h.timestamp):null,model:i})}if(i)for(let f of a)f.model||(f.model=i);return a.slice(-n)}var pr=new Set(["exec_command","shell","exec"]),ya=new Set(["wait","write_stdin"]),ba="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 ny(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 ry(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 oy(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=ry(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function cu(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await Ge(t,e);if(!o)return[];let s;try{s=await St(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"&&pr.has(u.name)){let w;try{w=JSON.parse(u.arguments||"{}")}catch{continue}let h=ny(w);h&&(d=[h])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=oy(u.input));if(!d.length)continue;let f=u.call_id||u.id||null;d.forEach((w,h)=>{a.push({id:f?h?`${f}:${h}`: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 lu(t,e){let n=await Ge(t,e);if(!n)return null;try{return Math.round((await ga.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var _n=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}},ka=class extends _n{transcript(e){return!e?.cwd||!e?.sessionId?null:ne(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?Ws(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?Kl(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?Jl(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await sy.stat(n)).mtimeMs)}catch{return null}}},xa=class extends _n{async readMessages(e,n){return eu(e.cwd,e.sessionId,n)}async readCommands(e,n){return ru(e.cwd,e.sessionId,n)}async updatedAt(e){return ou(e.cwd,e.sessionId)}},Sa=class extends _n{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return iu(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return cu(e.cwd,this.sessionId(e),n)}async updatedAt(e){return lu(e.cwd,this.sessionId(e))}},ay=new _n("unknown"),uu=new Map;function va(t){return uu.set(t.agent,t),t}function mr(t){return uu.get(t)||ay}va(new ka("claude",pa));va(new xa("cursor",po));va(new Sa("codex",pr));var Ta=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function du(t,e={}){return(await mr(t.agent).readMessages(t,e)).map(Ta(t))}async function fu(t,e={}){return(await mr(t.agent).readCommands(t,e)).map(Ta(t))}async function pu(t,e={}){return(await mr(t.agent).readJobs(t,e)).map(Ta(t))}var iy=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,mu=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function cy(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
36
- `);if(!mu.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(iy);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"&&mu.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 ho=/(^|\n)\s*›/,hu=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function ly(t,e=""){let n=String(t);if(!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n)||ho.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(hu);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(hu.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 wu=/Enter this one-time code/,uy=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function dy(t,e=""){let n=String(t);if(!wu.test(n)||ho.test(n))return null;let r=n.replace(/\r/g,"").split(`
38
- `),o=r.findIndex(i=>wu.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(uy);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 fy(t,e=""){let n=String(t);return!/open the following link to authenticate/.test(n)||ho.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 py(t,e=""){let n=String(t);return!/Paste or type your API key/.test(n)||ho.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 gu(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=dy(e,n)||fy(e,n)||py(e,n)||ly(e,n);if(r)return r}return cy(e,{provider:t,box:n})}function yu(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 xu=/to navigate\b.*\bEsc to cancel|Enter to select\b/,Ea=/Chat about this/i,En=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,bu=/^(type something|chat about this|submit)\b/i,In=/^[─—-]{6,}\s*$/,Ia=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function Rn(t){let e=t.search(Ia);return(e>=0?t.slice(0,e):t).trim()}function my(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function hy(t){let e=String(t).trim();return!!(!e||In.test(t)||Ia.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function wy(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=gy(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function gy(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 ku(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var Su=/^\s*Switch model\?\s*$/m;function yy(t,e=""){let n=String(t);if(!Su.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(En);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&&!In.test(r[u])&&a.push(f)}}if(s.length<2)return null;let i=s.map(u=>({label:Rn(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}]}}function by(t,e=""){let n=String(t);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
40
- `),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(En);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 Aa=/Resume from summary/i,ky=/^\s*This session is\b/;function xy(t,e=""){let n=String(t);if(!Aa.test(n))return null;let r=n.replace(/\r/g,"").split(`
41
- `),o=-1;for(let u=r.length-1;u>=0;u--)if(ky.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(En);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&&!In.test(r[u])&&a.push(f)}}if(s.length<2||!s.some(u=>Aa.test(u.label)))return null;let i=s.map(u=>({label:Rn(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 Ke=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;function Ra(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(En);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 vu=/Choose the text style that looks best with your terminal/,Tu=/Let's get started\./;function Sy(t,e=""){let n=String(t);if(!vu.test(n)||!Tu.test(n)||Ke.test(n))return null;let r=n.replace(/\r/g,"").split(`
42
- `),o=Ra(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:Rn(s.label),description:""}))}]}:null}var Au=/Select login method:/;function vy(t,e=""){let n=String(t);if(!Au.test(n)||Ke.test(n))return null;let r=n.replace(/\r/g,"").split(`
43
- `),o=Ra(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:Rn(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 $a=/Paste code here if prompted/,Ty=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function Ay(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!Ty.test(o)||$a.test(t[r]))break;n+=o}return n}function _y(t,e=""){let n=String(t);if(!$a.test(n)||Ke.test(n))return null;let r=n.replace(/\r/g,"").split(`
44
- `),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=Ay(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 _a=/OAuth error:/,Ey=/Press Enter to retry\./;function Iy(t,e=""){let n=String(t);if(!_a.test(n)||!Ey.test(n)||Ke.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
45
- `)].reverse().find(a=>_a.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 _u=/Quick safety check: Is this a project you created or one you trust/;function Ry(t,e=""){let n=String(t);if(!_u.test(n)||Ke.test(n))return null;let r=n.replace(/\r/g,"").split(`
46
- `),o=Ra(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:Rn(s.label),description:""}))}]}:null}var Eu=/^\s*Press Enter to continue\s*$/;function $y(t,e=""){let n=String(t);if(Ke.test(n))return null;let r=n.replace(/\r/g,"").split(`
47
- `),o=-1;for(let i=r.length-1;i>=0;i--)if(r[i].trim()!==""){o=i;break}if(o<0||!Eu.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(In.test(r[i])||Ia.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 Oy(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(Ea.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 Cy(t,e=""){let n=String(t);if(!xu.test(n)||!Ea.test(n))return null;let r=n.replace(/\r/g,"").split(`
48
- `),o=Oy(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let m=i;m<c;m++){let p=r[m].match(En);p&&(u<0&&(u=m),l.push({line:m,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 m of l)for(let p=m.line+1;p<Math.min(r.length,m.line+4)&&!(En.test(r[p])||In.test(r[p]));p++){let b=r[p].trim();if(!(!b||hy(r[p])||bu.test(b))){m.desc=my(b);break}}let d=l.filter(m=>!bu.test(m.label));if(!d.length)return null;let f=d.some(m=>m.hasCheckbox),w=Math.max(0,d.findIndex(m=>m.cursor)),h=s>=0?s+1:0,g=[];for(let m=h;m<u;m++){let p=r[m].trim();p&&!In.test(r[m])&&!/[☐☒]/.test(r[m])&&g.push(p)}let x=g.join(" ")||(s>=0?ku(r[s]):"")||"The agent is asking a question",A=s>=0?wy(r[s],x):null,S=A?.currentTabLabel||(s>=0?ku(r[s]):""),v=d.map(m=>({label:Rn(m.label),description:m.desc})),E=v.map(m=>m.label).join("|"),I=`claude:${A?`q${A.questionIndex+1}/${A.questionTotal}`:"q1/1"}:${S}:${x}:${E}`.slice(0,240),M=A?`claude:ask:${A.tabs.map(m=>m.label).join("|")}`.slice(0,240):I;return{provider:"claude",box:e,multiSelect:f,cursorIndex:w,promptId:I,groupId:M,...A?{questionIndex:A.questionIndex,questionTotal:A.questionTotal,currentTabLabel:A.currentTabLabel,tabs:A.tabs}:{},questions:[{question:x,header:S,multiSelect:f,options:v}]}}function $n(t,e){let n=String(e||"");return n?t==="claude"?xu.test(n)&&Ea.test(n)||Su.test(n)||Aa.test(n)?!0:Ke.test(n)?!1:vu.test(n)&&Tu.test(n)||Au.test(n)||$a.test(n)||_a.test(n)||_u.test(n)||Eu.test(n.trimEnd().split(`
49
- `).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 Ve(t,e){return!t||!e?null:t.agent==="claude"?by(e,t.name)||Cy(e,t.name)||yy(e,t.name)||xy(e,t.name)||Iy(e,t.name)||_y(e,t.name)||vy(e,t.name)||Sy(e,t.name)||Ry(e,t.name)||$y(e,t.name):t.agent==="cursor"||t.agent==="codex"?gu(t.agent,e,t.name):null}async function Oa(t){if(!t)return null;let e=await me(t.name).catch(()=>"");return Ve(t,e)}function Iu(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 Ru(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 On(t){return typeof t=="string"?t.trim():""}var $u=/^\s*─{8,}\s*$/,My=/^❯[  ]?(.*)$/;function Ca(t,e){if(t!=="claude")return null;let n=String(e).replace(/\r/g,"").split(`
50
- `).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if($u.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if($u.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(My);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 Je(t){return String(t||"").replace(/\s+/g," ").trim()}function Ou(t,e){let n=Je(t),r=Je(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}import{execFile as Ny}from"node:child_process";import{promisify as Py}from"node:util";var Na=Py(Ny),Cu=64*1024*1024;async function mt(t,e,{input:n}={}){let r=Na("git",["-C",t,...e],{maxBuffer:Cu});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Pa(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 Mu(t){let{stdout:e}=await mt(t,["rev-parse","--show-toplevel"]);return e.trim()}async function By(t){try{return await mt(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Nu(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)=>Na("git",["-C",t,...a],{maxBuffer:Cu,env:o,...i});try{let a=await By(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 Na("rm",["-f",r])}catch{}}}async function Pu(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 Ba(t,e,n,r){return await mt(t,["worktree","add","--no-track","-b",n,e,r]),e}async function Bu(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 Du(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 hr(t,e){try{await mt(t,["worktree","remove","--force",e])}catch{try{await mt(t,["worktree","prune"])}catch{}}}function Dy(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 Da(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function ju(t,e,n,r){let{stdout:o}=await mt(t,Da(["diff","--shortstat",e,n],r));return Dy(o)}async function Lu(t,e,n,r){let{stdout:o}=await mt(t,Da(["diff","--name-only",e,n],r));return o.split(`
51
- `).map(s=>s.trim()).filter(Boolean)}async function Uu(t,e,n,r){let{stdout:o}=await mt(t,Da(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Ma(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Ma(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 Ma(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Ma(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(`
52
- `).map(n=>n.trim()).filter(Boolean)}catch{return[]}}import Ly from"node:fs/promises";import Uy from"node:os";import Fu from"node:path";var wo=t=>new Promise(e=>setTimeout(e,t)),Fy=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,Hy=300,Gu=[{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"}],Wy=new Set([...Gu.map(t=>t.alias),"best","opus[1m]","sonnet[1m]","claude-opus-4-8","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),qy=[{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"}],Ku=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),Vu=[{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 Ju(t){return t==="codex"?qy:t==="cursor"?Vu:Gu}function Hu(t){let e=String(t||"").split(`
53
- `),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 Wu(t){let e=String(t||"").split(`
54
- `),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 Gy(t){let e=String(t||"").split(`
55
- `);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 qu(t){let e=String(t||"").split(`
56
- `),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 zu(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&&!Ku.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=Vu.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!Wy.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function wr(t,e,{timeoutMs:n=Fy}={}){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 wo(Hy)}}async function Ky(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 Vy(t,e){await Qr(t),await yt(t,"/model",{enter:!1}),await wo(250),await gt(t,["Enter"],{kind:"model-switch"});let n=await wr(t,Hu);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=Gy(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 wr(t,i=>Wu(i)||!Hu(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?Ku.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 wr(t,i=>!Wu(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 Jy(){let t=Fu.join(process.env.CURSOR_CONFIG_DIR||Fu.join(Uy.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await Ly.readFile(t,"utf8"))?.model||null}catch{return null}}async function zy(t,e){await Qr(t),await eo(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await wr(t,qu),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 wr(t,qu,{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 wo(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 wo(800);let a=await Jy();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 Xu(t,e,n){return t==="codex"?Vy(e,n):t==="cursor"?zy(e,n):Ky(e,n)}import ja from"node:fs/promises";import Cn from"node:path";import Xy from"node:os";import{EventEmitter as Yy}from"node:events";import{randomUUID as Zy}from"node:crypto";var Qy=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"]),tb=new Set(["user","agent","orchestrator","system"]),eb=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Mt=new Yy;Mt.setMaxListeners(0);var La=process.env.BOSTRAT_STATE_DIR||Cn.join(process.env.HOME||Xy.tmpdir(),".bostrat");function yo(t){return t?.cwd?Cn.join(t.cwd,".bostrat","events"):Cn.join(La,"events")}var Yu=(t,e)=>Cn.join(t,`${e}.ndjson`),go=new Map;async function nb(t,e){if(!go.has(e)){let o=(await P(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);go.set(e,o)}let n=go.get(e)+1;return go.set(e,n),n}function rb(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||!Qy.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!tb.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!eb.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?Cn.join(La,"events"):yo(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=rb(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await nb(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||Zy(),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 ja.mkdir(r,{recursive:!0}),await ja.appendFile(Yu(r,n),JSON.stringify(i)+`
57
- `),Mt.emit("event",i),Mt.emit(`track:${n}`,i),i}async function P(t,e){let n,r;typeof t=="string"&&e?(n=t,r=e):(r=typeof t=="string"?t:t?.id,n=typeof t=="string"?Cn.join(La,"events"):yo(t));let o;try{o=await ja.readFile(Yu(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
58
- `))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function Zu(t,e){let n=`track:${t}`;return Mt.on(n,e),()=>Mt.off(n,e)}var $t=t=>t||null;function ko(t){let e=t||{},n=ob(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function ob(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:$t(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:$t(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:$t(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:$t(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:$t(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:$t(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:$t(t.agent),payload:{...gr(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:$t(t.agent||t.toAgent),payload:{kind:"subtask-failed",...gr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:$t(t.agent),payload:{change:"subtask-resumed",...gr(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",...gr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...gr(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:$t(t.agent),payload:{change:"rehydrated"}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:$t(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var bo=Promise.resolve(),Fa=t=>(bo=bo.then(t).catch(()=>{}),bo),td=()=>bo;function ed(t,e){let n=ko(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;Fa(()=>R(r,n))}var Qu=new Set;async function nd(t){if(!(!t?.id||Qu.has(t.id))){Qu.add(t.id);try{let e=await P(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=ko(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await R(t,o)}}catch{}}}function gr(t){let{type:e,at:n,...r}=t;return r}var Ua=new Map;function rd(t,e,n){!n?.promptId||Ua.get(t.id)===n.promptId||(Ua.set(t.id,n.promptId),Fa(()=>R(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:$t(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 od(t,e,n,r={}){Ua.delete(t.id),Fa(()=>R(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:$t(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as Mb}from"node:crypto";var sd=/(?<!\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,ad=/(?<!\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,id=/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,sb=new RegExp(`\\b(?:${sd}|${ad}|${id})\\b`,"i"),ab=/\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,ib=/^(?:commit|push|open|create|raise|ready|ok(?:ay)?\s+to|good\s+to)\b/i,cb=90;function lb(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function cd(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of lb(n).reverse()){if(!sb.test(r))continue;let o=r.endsWith("?")&&r.length<=cb&&ib.test(r);if(ab.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var ld=["commit","push","pr"],ub=new RegExp(`\\b(?:${sd})\\b`,"i"),db=new RegExp(`\\b(?:${ad})\\b`,"i"),fb=new RegExp(`\\b(?:${id})\\b`,"i"),pb=/\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 ud(t){let e=String(t||""),n=[];ub.test(e)&&n.push("commit"),db.test(e)&&n.push("push");let r=pb.test(e);!r&&fb.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 xo(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 mb=3e4;function dd(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<=mb&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function fd(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 gb}from"node:child_process";import{promisify as yb}from"node:util";import So from"node:fs/promises";import pd from"node:path";import hb from"node:os";var md=process.env.BOSTRAT_STATE_DIR||pd.join(process.env.HOME||hb.tmpdir(),".bostrat"),Wa=pd.join(md,"pr-links.json"),kt=(t,e)=>`${t}#${Number(e)}`,vo=null,Ha=null,L=Promise.resolve();async function vt(){return vo||(Ha??=So.readFile(Wa,"utf8").then(t=>JSON.parse(t)?.links??{}).catch(()=>({})).then(t=>vo=t).finally(()=>Ha=null),Ha)}async function nt(){let t=JSON.stringify({links:vo},null,2),e=`${Wa}.${process.pid}.tmp`;await So.mkdir(md,{recursive:!0}),await So.writeFile(e,t),await So.rename(e,Wa)}var wb=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 vt(),c=kt(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:wb.has(a),firstSeen:Date.now()},L=L.then(nt,nt),await L.catch(()=>{}),{link:i[c],created:!0})}async function yr(t,e){if(!t||e==null)return null;let n=(await vt())[kt(t,e)];return!n||n.announced?null:(n.announced=!0,L=L.then(nt,nt),await L.catch(()=>{}),n)}async function Pt(t,e){return!t||e==null?null:(await vt())[kt(t,e)]??null}async function ze(t,e,n){if(!t||e==null)return null;let r=(await vt())[kt(t,e)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,L=L.then(nt,nt),await L.catch(()=>{}),r):null}async function hd(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await vt())[kt(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},L=L.then(nt,nt),await L.catch(()=>{}),o):null}async function wd(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await vt())[kt(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},L=L.then(nt,nt),await L.catch(()=>{}),o):null}async function To(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await vt())[kt(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,L=L.then(nt,nt),await L.catch(()=>{}),r)}async function Ao(t,e){if(!t||e==null)return null;let n=(await vt())[kt(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,L=L.then(nt,nt),await L.catch(()=>{}),n)}async function _o(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await vt())[kt(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,L=L.then(nt,nt),await L.catch(()=>{}),r)}async function Eo(t,e){if(!t||e==null)return null;let n=(await vt())[kt(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,L=L.then(nt,nt),await L.catch(()=>{}),n)}async function gd(t,e,n){if(!t||e==null||!n)return null;let r=(await vt())[kt(t,e)];return r?(r.lastHeadSha=n,L=L.then(nt,nt),await L.catch(()=>{}),r):null}async function Io(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await vt())[kt(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),L=L.then(nt,nt),await L.catch(()=>{}),r):null}async function Ro(t,e){if(!t||e==null)return null;let n=(await vt())[kt(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,L=L.then(nt,nt),await L.catch(()=>{}),n)}async function yd(){return Object.values(await vt()).filter(t=>t.state!=="merged"&&t.state!=="closed")}async function qa(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await pt(t,{lines:It}).catch(()=>"");if(!ir(e,r))return wl(e,r)?{working:!0,prompted:!1}:n;if($n(e,r)){let o=await me(t).catch(()=>r);if(Ve({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function $o(t,e){return(await qa(t,e)).working}var bd=yb(gb),kd=[4e3,15e3,45e3],bb=2500,kb=5*6e4;async function Ga(t,e,{midTurn:n=$o,pollMs:r=bb,capMs:o=kb}={}){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 xb=6e4,Sb=9e4,vb=2e3,Tb=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,Ab=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,_b=/\s--web\b/,xd=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/;function Ka(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Sd(t){let e=String(t||""),n=e.match(Tb);if(!n)return null;let r=Ka(e,n.index);return _b.test(r)?null:{repo:r.match(xd)?.[1]??null}}var Eb=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function vd(t){let e=String(t||""),n=e.match(Ab);if(!n)return null;let r=Ka(e,n.index),o=Ka(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("-")){Eb.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(xd)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var Ib="number,url,title,headRefName,createdAt";async function Rb(t,e){let n=async s=>{let{stdout:a}=await bd("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",Ib],{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-xb).sort((s,a)=>Date.parse(a.createdAt)-Date.parse(s.createdAt))}async function $b(t,e){if(e)return[e];let n=[],r=await Z(t.workspace?.base||t.cwd).catch(()=>null);r&&n.push(r);for(let o of await oe().catch(()=>[])){let s=await Z(o.path).catch(()=>null);s&&!n.includes(s)&&n.push(s)}return n}async function Ob(t,e,n){let r=t.workspace?.branch,o=[];for(let s of await $b(t,e)){let a;try{a=await Rb(s,n)}catch{continue}for(let i of a){let c=await Pt(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 Td(t,e,{delays:n=kd,turnEnd:r}={}){let o=Sd(e.command);if(!o)return null;let s=!!vd(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 Ob(t,o.repo,i).catch(()=>null);if(!u)continue;let{slug:d,pr:f}=u,{link:w,created:h}=await Nt({repo:d,number:f.number,trackId:t.id,url:f.url,headRef:f.headRefName,title:f.title,source:"observed"});return!h&&!(w?.trackId===t.id&&await yr(d,f.number))?null:(await Ga(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 Cb="number,url,title,headRefName,state";function Ad(t,e,{delays:n=kd,turnEnd:r,openedWait:o}={}){let s=vd(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 Z(t.workspace?.base||t.cwd).catch(()=>null),l||=t.workspace?.branch,!c||!l)return null;let d=async(w,h)=>{let g=await Pt(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 Io(c,w.number),h&&((await P(t).catch(()=>[])).some(S=>S.type==="pr.state.changed"&&(S.payload?.change==="auto-armed"||S.payload?.change==="merged")&&S.payload?.number===w.number)||(await Ga(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(v=>setTimeout(v,w));let h;try{let{stdout:v}=await bd("gh",["pr","view",String(l),"--repo",c,"--json",Cb],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Gt({slug:c})}});h=JSON.parse(v||"null")}catch{continue}if(!h?.number)continue;if(h.state==="CLOSED")return null;if(h.state!=="MERGED"){if(f=h,s.auto)return d(h,!0);continue}let g=await Pt(c,h.number);if(g&&g.trackId!==t.id)return null;g||await Nt({repo:c,number:h.number,trackId:t.id,url:h.url,headRef:h.headRefName,title:h.title,source:"merge-observed"}),await Ga(e.box,e.agent,r);let x=await P(t).catch(()=>[]);if(x.some(v=>v.type==="pr.state.changed"&&v.payload?.change==="merged"&&v.payload?.number===h.number))return null;let A=v=>v.some(E=>E.type==="pr.state.changed"&&E.payload?.change==="opened"&&E.payload?.number===h.number),S=0;if(!A(x)){if(Sd(e.command)&&await yr(c,h.number)){let v=Date.now();S=v+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:v,change:"opened",number:h.number,url:h.url,title:h.title,branch:h.headRefName,repo:c,selfMerge:!0}})}else if((await Pt(c,h.number))?.announced){let v=o?.capMs??Sb,E=o?.pollMs??vb,J=Date.now()+v;for(;Date.now()<J&&(await new Promise(I=>setTimeout(I,E)),!A(await P(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:h.number,url:h.url,title:h.title,branch:h.headRefName,repo:c}}),await ze(c,h.number,"merged"),h}return f?d(f,!1):null}finally{ke.delete(a)}})();return i.catch(()=>ke.delete(a)),i}function Ja(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function _d(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function Ed(t){return`job|${t.box}|id:${t.id}`}var Va=new Map,Oo=new Map;async function za(t){return Va.has(t.id)?Va.get(t.id):(Oo.has(t.id)||Oo.set(t.id,P(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(Ja({role:o,sourceId:r.payload.sourceId})),n.keys.add(Ja({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(_d({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(Ed({id:r.payload.id,box:r.boxId}));return Va.set(t.id,n),n}).finally(()=>Oo.delete(t.id))),Oo.get(t.id))}async function Id(t,e){try{let n=(await za(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=Ja(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=cd(r.text);if(s){let{offered:a,toPr:i}=ud(s);await R(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:Mb(),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 Rd(t,e){try{let n=await za(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=_d(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}}),Td(t,o),Ad(t,o))}}catch{}}async function $d(t,e){try{let n=await za(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Ed(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 Nb=/^Base directory for this skill:\s*(\S+)/,Pb=/^Run the "([^"]+)" workflow\./,Bb=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,Db=/^<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\.\]$/,Lb=/^<task-notification>/i,Ub=/<summary>([\s\S]*?)<\/summary>/i,Fb=/<task-id>([\s\S]*?)<\/task-id>/i,Hb=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,Wb=/<status>([\s\S]*?)<\/status>/i,qb=/<result>([\s\S]*?)(?:<\/result>|$)/i;var Gb=new Set(["model","clear"]);function Co(t){let e=String(t||"").trim();if(!e)return null;let n=e.match(Nb);if(n)return{kind:"skill",name:n[1].split("/").filter(Boolean).pop()||"skill"};let r=e.match(Pb);if(r)return{kind:"skill",name:r[1]};let o=e.match(Bb);if(o){let s=o[1].toLowerCase();return Gb.has(s)?{kind:"suppress"}:{kind:"command",command:`/${s}`}}if(Db.test(e))return{kind:"suppress"};if(jb.test(e))return{kind:"suppress"};if(Lb.test(e)){let s=i=>Kb(e.match(i)?.[1].trim()||"")||null,a=s(qb);return{kind:"task",summary:s(Ub)||"",taskId:s(Fb),toolUseId:s(Hb),status:s(Wb)?.toLowerCase()||null,resultHead:a?a.length>600?`${a.slice(0,600).trimEnd()}\u2026`:a:null}}return null}function Kb(t){return t.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&amp;/g,"&")}function Od(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 Cd(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":"command-run"}var at=t=>t.payload?.ts??t.ts;function Mo(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 m of t||[]){if(m.type!=="tool.called"||m.source!=="transcript")continue;let p=String(m.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 T=b[0].split("/").pop().toLowerCase();d.has(T)||d.set(T,[]),d.get(T).push(m.payload?.ts??m.ts)}}let f=3*6e4,w=20*6e4,h=(m,p)=>!!(m?.name&&d.get(String(m.name).toLowerCase()))?.some(T=>T>=p-f&&T<=p+w);for(let m of t||[])if(m.type==="turn.retracted"){r.add(`${m.boxId}|${m.payload?.saidTs}`);let p=Je(m.payload?.text);p&&(o.has(m.boxId)||o.set(m.boxId,[]),o.get(m.boxId).push(p))}else if(m.type==="turn.user"&&m.source==="orchestrator"){let p=Je(m.payload?.text);p&&(s.has(m.boxId)||s.set(m.boxId,new Set),s.get(m.boxId).add(p))}else if(m.type==="job.started"&&m.payload?.id)a.add(m.payload.id),(m.payload.kind||"agent")==="agent"&&m.payload.name&&c.add(m.payload.name);else if(m.type==="turn.user"&&m.payload?.note?.kind==="task"){let p=m.payload.note,b={status:p.status||null,summary:p.summary||null,resultHead:p.resultHead??null,ts:at(m)};p.toolUseId&&i.set(p.toolUseId,b);let T=String(p.summary||"").match(u);T&&l.set(T[1],b)}let g=new Map,x=new Map,A=new Map,S=new Map,v=new Map,E=new Map,J=new Map,I=new Map,M=(m,p)=>{let b=o.get(m);if(!b?.length)return!1;let T=Je(p),ot=s.get(m);return b.some($=>{if(!T.startsWith($))return!1;let z=Je(T.slice($.length));return!!z&&!!ot?.has(z)})};for(let m of t||[]){let p=m.payload||{};switch(m.type){case"turn.user":{if(m.source==="hook"||!String(p.text||"").trim()&&!p.images?.length)break;{let b=p.note||Co(p.text);if(b){if(b.kind==="task"){if(b.toolUseId&&a.has(b.toolUseId))break;let T=String(b.summary||"").match(u);if(!b.toolUseId&&T&&c.has(T[1]))break}b.kind!=="suppress"&&e.push({id:m.id,kind:"event",type:Cd(b),name:b.name??null,command:b.command??null,summary:b.summary??null,ts:at(m),box:m.boxId,agent:m.provider});break}}if(r.has(`${m.boxId}|${at(m)}`)||m.source==="transcript"&&M(m.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:m.id,kind:"message",role:"user",text:p.text,ts:at(m),box:m.boxId,agent:m.provider,boxRole:p.boxRole,...m.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=>!h(ot,at(m)));if(!String(p.text||"").trim()&&!b.length)break;if(m.provider==="cursor"){let ot=`${m.boxId}|${p.text}`;if(n.has(ot))break;n.add(ot)}let T=m.provider==="codex"?(p.tools||[]).filter(ot=>!ya.has(ot)&&!pr.has(ot)):p.tools||[];e.push({id:m.id,kind:"message",role:"assistant",text:p.text,ts:at(m),box:m.boxId,agent:m.provider,boxRole:p.boxRole,...T.length?{tools:T}:{},...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(m),T=i.get(p.id);if(!T&&(p.kind||"agent")==="agent"&&p.name){let N=l.get(p.name);N&&N.ts>=b&&(T=N)}let ot={jobId:p.id,jobKind:p.kind||"agent",name:p.name||null,detail:p.detail||null,status:T?T.status&&T.status!=="completed"?"failed":"done":"running",startedTs:b,finishedTs:T?.ts??null,summary:T?.summary??null,resultHead:T?.resultHead??null},$=I.get(m.boxId);if($&&$.jobs.some(N=>(N.finishedTs??1/0)>=b))$.jobs.push(ot);else{let N={id:m.id,kind:"job-dock",dockId:p.id,ts:b,box:m.boxId,agent:m.provider,jobs:[ot]};I.set(m.boxId,N),e.push(N)}break}case"handoff.done":e.push(xe(m,"lead-handoff"));break;case"subtask.delegated":e.push({...xe(m,"subtask-delegated"),to:m.boxId,toAgent:m.provider});break;case"subtask.merged":e.push(xe(m,"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(m,`pr-${p.change}`));break;case"pr.offered":{if(!p.offerId)break;for(let T of g.values())T.status="stale";g.clear();let b={id:m.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(m),box:m.boxId,agent:m.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 T=xe(m,b);b==="model-change"&&(T.to=p.model),e.push(T);break}case"view.state.changed":{if(p.change!=="created"||p.restarted)break;e.push({id:m.id,kind:"view-card",viewId:p.viewId,name:p.name,slug:p.slug,port:p.port,origin:p.origin,ts:at(m),box:m.boxId,agent:m.provider});break}case"view.spinup.failed":{e.push({id:m.id,kind:"view-spinup-failed",name:p.name||null,reason:p.reason||"no-view",ts:at(m),box:m.boxId,agent:m.provider});break}case"mock.state.changed":{if(p.change==="picked"){e.push({id:m.id,kind:"mock-picked",mockId:p.mockId,name:p.name,group:p.group??null,version:p.version??null,siblings:p.siblings??[],ts:at(m),box:m.boxId,agent:m.provider});break}if(p.change!=="created")break;e.push({id:m.id,kind:"mock-card",mockId:p.mockId,name:p.name,group:p.group??null,ts:at(m),box:m.boxId,agent:m.provider});break}case"runbook.state.changed":{if(p.change!=="created")break;e.push({id:m.id,kind:"runbook-card",runbookId:p.runbookId,name:p.name,ts:at(m),box:m.boxId,agent:m.provider});break}case"ticket.linked":{e.push({id:m.id,kind:"ticket-card",provider:p.provider||"linear",key:p.key,url:p.url,title:p.title??null,linkSource:p.linkSource??null,ts:at(m),box:m.boxId,agent:m.provider});break}case"ticket.move.offered":{if(!p.offerId)break;for(let T of A.values())T.key===p.key&&(T.status="stale",A.delete(T.offerId));let b={id:m.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(m),box:m.boxId,agent:m.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(m,"ticket-state"));break}case"ticket.commented":{e.push({id:m.id,kind:"ticket-comment",key:p.key,url:p.url??null,excerpt:p.excerpt??null,ts:at(m),box:m.boxId,agent:m.provider});break}case"ticket.flow.changed":e.push(xe(m,"ticket-flow"));break;case"ticket.create.offered":{if(!p.proposalId)break;for(let T of v.values())T.status="stale";v.clear();let b={id:m.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(m),box:m.boxId,agent:m.provider};v.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,v.delete(p.proposalId));break}case"ticket.created":{e.push({id:m.id,kind:"ticket-created",items:Array.isArray(p.items)?p.items:[],failed:Array.isArray(p.failed)?p.failed:[],team:p.team??null,ts:at(m),box:m.boxId,agent:m.provider});break}case"error":{if(!p.kind)break;e.push(xe(m,p.kind));break}case"provider.connected":{e.push({id:m.id,kind:"event",type:"provider-connected",provider:p.provider||m.provider,ts:at(m),at:at(m),box:m.boxId,agent:m.provider});break}case"prompt.answered":{if(p.control||!p.question)break;let b=p.groupId||p.promptId;if(!b)break;let T=J.get(b);T||(T={id:m.id,kind:"prompt",groupId:b,provider:m.provider,box:m.boxId,agent:m.provider,questionTotal:p.questionTotal??1,questions:[],ts:at(m)},J.set(b,T));let ot=p.questionIndex??0,$={index:ot,header:p.header||"",question:p.question,multiSelect:!!p.multiSelect,chosen:Array.isArray(p.chosen)?p.chosen:[]},z=T.questions.findIndex(H=>H.index===ot);z>=0?T.questions[z]=$:T.questions.push($),T.questions.sort((H,_)=>H.index-_.index),T.questionTotal=Math.max(T.questionTotal,p.questionTotal??1),new Set(T.questions.map(H=>H.index)).size>=T.questionTotal&&(e.push(T),J.delete(b));break}default:break}}return Jb(e.sort((m,p)=>(m.ts??0)-(p.ts??0)))}var Vb=2*6e4;function Jb(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<=Vb){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 Xa(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:zb(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??Mo(t)).filter(o=>o.kind==="message");return Vl(n,r)}function zb(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==vn(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 Ya={"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}},Md={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 Nd(t){if(!t)return null;let e=String(t).trim();return Ya[e]?e:Md[e]?Md[e]:Object.keys(Ya).find(r=>e.startsWith(r))||null}var No=t=>Nd(t)!==null;function Kt(t,e={}){let n=Ya[Nd(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 Xb(t){let e=[],n=null;for(let r of String(t||"").split(`
59
- `)){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 Za=new Map;async function Yb(t,e){let n=`${t.id}:${e.name}`;if(!Za.has(n)){let r=await P(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);Za.set(n,o)}return Za.get(n)}async function Pd(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 Yb(t,e),o=Xb(await St(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 Po from"node:fs";import ti from"node:path";import Zb from"node:os";var Qb=process.env.BOSTRAT_STATE_DIR||ti.join(process.env.HOME||Zb.tmpdir(),".bostrat"),Dd=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,tk="https://cursor.com/api/dashboard/get-filtered-usage-events",Bd=100;async function ek({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(tk,{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:Bd})});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<Bd)break}return r}function nk(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 rk=5*6e4,Se={at:0,rows:null,error:null},Bo=()=>ti.join(Qb,"cursor-usage.json");function jd(){try{return JSON.parse(Po.readFileSync(Bo(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function ok(t){try{Po.mkdirSync(ti.dirname(Bo()),{recursive:!0});let e=Bo()+".tmp";Po.writeFileSync(e,JSON.stringify(t)),Po.renameSync(e,Bo())}catch{}}var sk=5e3;async function ak({fetchImpl:t,now:e=Date.now()}={}){if(Se.rows&&e-Se.at<rk)return Se.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=jd(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await ek({sinceMs:o,cookie:n,fetchImpl:t});Se={at:e,rows:s.map(nk).filter(Boolean),error:null}}catch(s){Se={at:e,rows:null,error:String(s?.message||s)}}return Se.rows}var Ld=()=>({enabled:Dd(),lastPollAt:Se.at||null,error:Se.error}),Qa=new Map;async function ik(t,e){let n=`${t.id}:${e.name}`;if(!Qa.has(n)){let r=await P(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);Qa.set(n,o)}return Qa.get(n)}async function Ud(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!Dd())return;let r=await ak({fetchImpl:n});if(!r||!r.length)return;let o=await nu(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await ik(t,e),a=jd(),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}})}ok({sinceMs:c,seen:[...i].slice(-sk)})}catch{}}import kr from"node:fs/promises";import Tt from"node:path";import ck from"node:os";import lk from"node:net";import{randomUUID as uk}from"node:crypto";import{execFile as dk}from"node:child_process";import{promisify as fk}from"node:util";var Jt=fk(dk),pk=process.env.BOSTRAT_STATE_DIR||Tt.join(process.env.HOME||ck.tmpdir(),".bostrat"),Gd=Tt.join(pk,"views.json"),[Fd,Hd]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),ve=Number(process.env.BOSTRAT_VIEW_MAX)||3,mk="bostrat-view-",Mn=t=>`${mk}${t}`,Wd=new Set(["bostrat","www","api","views","app"]),Do=t=>Object.assign(new Error(t),{status:400}),hk=new Set(["npm","pnpm","yarn","bun"]),ri="package.json",wk=/[|;&`]|\$\(/,gk=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),yk=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Kd=t=>String(t).trim().split(/\s+/).filter(Boolean),bk=/^[A-Za-z_][A-Za-z0-9_]*=/;function kk(t){for(let e of Kd(t))if(!bk.test(e))return e;return null}function Vd(t){let e=Kd(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function xk(t,e){let n=Vd(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Tt.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return Tt.resolve(t,n[r+1])}return t}var Sk=t=>kr.stat(t).then(e=>e.isDirectory(),()=>!1),Jd=t=>kr.stat(t).then(e=>e.isFile(),()=>!1),jo=t=>kr.realpath(t).catch(()=>Tt.resolve(t));async function vk(t,e){if(Tt.resolve(t)===Tt.resolve(e))return!0;let[n,r]=await Promise.all([jo(t),jo(e)]);return n===r}async function Tk(t){let n=(await kr.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 Jd(Tt.join(t,o.name,ri))?`./${o.name}`:null))).filter(Boolean)}async function Ak(t,e){let[n,r]=await Promise.all([jo(t),jo(e)]),o=Tt.relative(n,r);if(o&&(o.startsWith("..")||Tt.isAbsolute(o)))throw Do(`serve directory escapes the Track workspace: ${e}`)}async function _k(t,e){if(!await Sk(t))throw Do(`serve directory does not exist: ${t}`);let n=kk(e);if(!n)return;if(!n.includes("/")&&!await Ps(n,{loginShell:!0,failOpen:!0}))throw Do(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(wk.test(e)||gk.has(n)||!hk.has(n)||!Vd(e).includes("run")||yk.test(e))return;let r=xk(t,e);if(await Jd(Tt.join(r,ri)))return;let o=await Tk(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw Do(`no ${ri} in ${r}${s}`)}var Vt=null,ei=null;async function ie(){return Vt||(ei||=(async()=>{try{Vt=JSON.parse(await kr.readFile(Gd,"utf8"))}catch{Vt={views:[],adoptWatermarks:{}}}finally{ei=null}return Vt.views||=[],Vt.adoptWatermarks||={},Vt})(),ei)}async function zt(){await qt(Gd,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 br=new Map,ai=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,zd=12e3;function ii(t){return br.get(t)||null}function Xd(t,{name:e,box:n}={}){let r=br.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 br.set(t,o),o}function oi(t,e){let n=br.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function Lo(t,{error:e=null}={}){let n=br.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function Yd(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 si(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function Ek(t,e){let n=si(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!Wd.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Wd.has(s))return s}}function xr(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=lk.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 ci(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 Ik(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=Fd;r<=Hd;r++)if(!n.has(r)&&!await xr(r,{timeoutMs:250}))return r;throw new Error(`no free view port in ${Fd}-${Hd}`)}async function Rk(t){try{let{stdout:e}=await Jt(O,C("list-sessions","-F","#{session_name}"));return e.split(`
60
- `).some(n=>n.trim()===Mn(t))}catch{return!1}}async function Sr(t){try{await Jt(O,C("kill-session","-t",Mn(t)))}catch{}}async function li(t){if(!await ee())throw new Error("host tmux server not running \u2014 cannot spawn a view server");let e=Mn(t.slug),n=t.serveCwd??t.cwd,r=Tt.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 qd(t,e=12){try{let{stdout:n}=await Jt(O,C("capture-pane","-t",Mn(t),"-p"));return String(n||"").trimEnd().split(`
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(`
61
63
  `).filter(Boolean).slice(-e).join(`
62
- `)}catch{return""}}var se=new Map,$k=12e4;async function Ok(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await Rk(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await qd(t.slug)||"server exited before listening"),se.delete(t.id),await ae(t,t.state,t.error?{error:t.error}:{}),!0}if(await xr(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>$k?(t.state="error",t.error=await qd(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 ni=!1;async function Uo(){if(!ni){ni=!0;try{let t=await ie(),e=!1;for(let n of t.views)await Ok(n)&&(e=!0);e&&await zt()}finally{ni=!1}}}async function Zd({trackId:t}={}){await Uo();let e=await ie();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function Qd(t){let n=(await ie()).views.find(r=>r.id===t);return n?{...n}:null}function ui(t){return Vt&&Vt.views.find(e=>e.slug===t)||null}async function di({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=Tt.resolve(r,o??"."),d=i==="detected"?null:async()=>{await Ak(r,u),await _k(u,n)},f=await ie();if(i!=="detected"){let g=si(e),x=String(n).trim(),A=f.views.find(S=>S.trackId===t&&(S.slug===g||si(S.name)===g||S.cmd===x&&Tt.resolve(S.serveCwd??S.cwd)===u));if(A)return Ck(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 h={id:uk().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:Ek(f,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await Ik(f,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await li(h),f.views.push(h),await zt(),await ae(h,"created",{origin:i}),oi(t,h.id),i==="detected"&&await xr(h.port)&&(h.state="live",h.lastHealthyAt=Date.now(),await zt(),await ae(h,"live")),{...h}}async function Ck(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 vk(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await xr(e.port))return await zt(),oi(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 ci(e.port),e.origin="agent"):await Sr(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 li(e),await zt(),await ae(e,"created",{restarted:!0,reused:!0}),oi(e.trackId,e.id),{...e,reused:!0}}async function tf(t){let n=(await ie()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await ci(n.port):await Sr(n.slug),n.state!=="stopped"&&(n.state="stopped",se.delete(n.id),await zt(),await ae(n,"stopped")),{...n}}async function ef(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 Sr(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),se.delete(n.id),await li(n),await zt(),await ae(n,"created",{restarted:!0}),{...n}}async function nf(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 ci(r.port):await Sr(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 rf(t){let e=await ie(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await Sr(r.slug),se.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await zt()),n.length}var Mk=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function of(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(Mk)){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 xr(c))try{i.push(await di({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 Nk}from"node:child_process";import{promisify as Pk}from"node:util";var Wo=Pk(Nk),Bk={timeout:3e4,maxBuffer:8*1024*1024};async function Lt(t,e,n={}){let r=await Gt({cwd:t}),{stdout:o}=await Wo("gh",e,{cwd:t,...Bk,...n,env:{...process.env,...r,...n.env||{}}});return String(o||"")}var Fo=null;async function mi(){if(Fo&&Date.now()-Fo.at<6e4)return Fo.data;let t;try{let e=await Wo("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 lr();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return Fo={at:Date.now(),data:t},t}async function Dk(t){try{let{stdout:e}=await Wo("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function af(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 fi(t){let e=(t||[]).map(af);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 hi="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",cf=hi.replace("statusCheckRollup,","");function wi(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function lf(t,e){if(!e)return null;let n=await Z(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 uf(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 df(t,e){let n=await lf(t,e);return n===null?{...fi([]),unavailable:!0}:fi(uf(n))}function Ho(t,e=fi(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,Nn=new Map;function Lk(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Nn.delete(s);continue}r.add(s),Nn.has(s)||Nn.set(s,n),o.mergeabilityPending=n-Nn.get(s)<jk}for(let o of Nn.keys())o.startsWith(`${t}#`)&&!r.has(o)&&Nn.delete(o);return e}async function Tr(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",hi]);return Ho(JSON.parse(o))}catch(o){if(!wi(o))throw o;let s=await Lt(n.path,["pr","view",String(r),"--json",cf]),a=JSON.parse(s);return Ho(a,await df(n.path,a.headRefOid))}}var sf=(t,e)=>e.number-t.number;async function vr(t){try{let e=await Lt(t,["pr","list","--json",hi,"--limit","50"]);return JSON.parse(e||"[]").map(n=>Ho(n)).sort(sf)}catch(e){if(!wi(e))throw e;let n=await Lt(t,["pr","list","--json",cf,"--limit","50"]);return(await Promise.all(JSON.parse(n||"[]").map(async r=>Ho(r,await df(t,r.headRefOid))))).sort(sf)}}var Uk=2e4,Fk=1500,Pn=null;function Xe(){Pn=null}async function Hk(t,e){if(!t)return e;for(let n of e){let r=await Pt(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 To(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await _o(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function ff(){let t=await mi();if(!t.authenticated)return{auth:t,repos:[]};let e=Pn?.pending?Fk:Uk;if(Pn&&Date.now()-Pn.at<e)return{auth:t,repos:Pn.data};let n=[];for(let a of await oe())await Dk(a.path)&&n.push(a);let r=Date.now(),o=await Promise.all(n.map(async a=>{try{let i=Lk(a.path,await vr(a.path),r);return await Hk(await Z(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 Pn={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 gi(t){if(!t)return null;for(let e of await oe())if(await Z(e.path).catch(()=>null)===t)return e.path;return null}var qo=new Set(["squash","merge","rebase"]);async function Ar(t,e,{method:n="squash"}={}){let r=await Te(t);if(!qo.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 aa().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{Xe()}return{ok:!0,number:o}}async function pf(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{Xe()}return{ok:!0,number:r}}var Wk=2500,qk=9e4,Gk=t=>new Promise(e=>setTimeout(e,t));function mf(t){return t.green&&!t.isDraft}function Kk(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 pi=new Map;function hf(){let t={};for(let[e,n]of pi)t[e]={...n};return t}async function wf(t,{method:e="merge",onMerged:n=null}={}){let r=await Te(t);if(!qo.has(e))throw new Error(`invalid merge method: ${e}`);if(pi.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await vr(r.path)).filter(mf).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 pi.set(r.path,s),Jk(s,o,{listPrs:()=>vr(r.path),merge:a=>Ar(r.path,a,{method:e}),sleep:Gk,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function Vk(t,e){let n=Date.now()+(e.settleMaxMs??qk),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(Wk)}}async function Jk(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 Vk(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(!mf(a)){t.skipped.push({number:a.number,title:a.title,reason:Kk(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(),Xe()}}async function gf(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Wo("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 aa().catch(()=>null);l&&(o=`${o?`${o}
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}
63
65
 
64
- `:""}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{Xe()}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 yf(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(!wi(i))throw i;let c=await Lt(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await lf(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(ce(i));o=uf(l).map(af).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=zk(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Lt(n.path,["run","view",c,"--log-failed"]).then(u=>Xk(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function zk(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function Xk(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
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(`
65
67
  `);return o.length>e&&(r=`\u2026 (${o.length-e} earlier lines truncated)
66
68
  `+o.slice(-e).join(`
67
69
  `)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
68
70
  `+r.slice(-n)),r}function ce(t){return String(t?.stderr||t?.message||t||"").trim().split(`
69
- `).find(n=>n.trim())||"gh failed"}var Yk=6e3,Go=new Map;function yi(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 bf(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await P(t)}catch{return}if(yi(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await $o(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(Go.get(t.id)||0)<Yk)return;Go.set(t.id,o);let s;try{s=await vr(t.cwd)}catch{return}let a=(s||[]).find(c=>c.headRefName===e);if(!a)return;let i=await Z(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 yr(i,a.number))){Go.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(()=>{}),Go.delete(t.id)}import ht from"node:fs/promises";import At from"node:path";import _r from"node:os";import{randomUUID as Zk}from"node:crypto";import{execFile as Qk}from"node:child_process";import{promisify as tx}from"node:util";import{chromium as ex}from"playwright-core";var xf=tx(Qk),Sf=process.env.BOSTRAT_STATE_DIR||At.join(process.env.HOME||_r.tmpdir(),".bostrat"),vf=At.join(Sf,"mocks.json"),nx=process.env.BOSTRAT_MOCK_ENGINE||"chromium",rx=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",ox=process.env.BOSTRAT_FIREFOX_BIN||"firefox",sx=1300,Bn=2,ax=32,Tf=1500,Dn=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,ix=700*1024*1024;async function cx(){if(process.platform!=="darwin")return _r.freemem();try{let{stdout:t}=await xf("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:_r.freemem()}catch{return _r.freemem()}}var Ut=class extends Error{constructor(e,n=""){super(n?`${e}
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}
70
72
  ${n.trim().split(`
71
73
  `).slice(-6).join(`
72
- `)}`:e),this.name="MockRenderError"}},Ye=null,bi=null;async function le(){return Ye||(bi||=(async()=>{try{Ye=JSON.parse(await ht.readFile(vf,"utf8"))}catch{Ye={mocks:[]}}finally{bi=null}return Ye.mocks||=[],Ye})(),bi)}async function Xt(){await qt(vf,Ye)}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 Ko=t=>At.join(t,".bostrat","mocks");function Bt(t,e){let n=Ko(t.cwd);return{html:At.join(n,`${t.id}.v${e}.html`),png:At.join(n,`${t.id}.v${e}.png`)}}function Ze(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 xi(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var lx=Buffer.from([137,80,78,71,13,10,26,10]);function Af(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(lx)}async function ux(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 dx({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 fx(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((h,g)=>{r.onload=h,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 h=0;h<s;h++)for(let g=h*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),h<d&&(d=h),w=h);return f<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:f,bottom:w}}},e.toString("base64"))}async function px(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 mx({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await ex.launch({executablePath:rx,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:Bn})).newPage();a.setDefaultTimeout(Dn),await a.goto(`file://${t}`,{waitUntil:"load",timeout:Dn}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Dn});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await fx(a,i),u=l&&dx({...l,pad:ax*Bn,align:Bn});u&&(i=await px(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Tf)throw new Ut(`capture produced a ${i.length}-byte image`);let c=xi(i);if(!c)throw new Ut("output is not a PNG");return await ht.writeFile(e,i),{width:Math.round(c.width/Bn),height:Math.round(c.height/Bn),dpr:Bn,renderMs:Date.now()-r}}catch(s){throw s instanceof Ut?s:new Ut(/timeout/i.test(s?.message||"")?`render timed out after ${Dn}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function kf({htmlPath:t,outPath:e,width:n}){let r=await ht.mkdtemp(At.join(_r.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await xf(ox,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Dn,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new Ut(c?.killed?`render timed out after ${Dn}ms`:"firefox exited non-zero",s)}let a=await ht.stat(e).catch(()=>null);if(!a||a.size<Tf)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=xi(await ux(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 hx(t){if(nx==="firefox")return kf(t);try{return await mx(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return kf(t);throw e}}var wx=hx;var ki=Promise.resolve();function _f(t){return ki=ki.then(t,t),ki}async function Ef(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 cx();if(o<ix)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 wx({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}If(r,{v:e,source:"html",htmlRel:At.relative(r.cwd,s.html),pngRel:At.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 If(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 gx({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?sx:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Rf({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 $f({trackId:t,name:e,html:n,image:r,cwd:o,width:s,group:a,box:i,agent:c,prompt:l}){let u=gx({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&&!Af(r))throw new Error("image must be a PNG");let f=await le(),w={id:Zk().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(Ko(o),{recursive:!0}),f.mocks.push(w),d?(await ht.writeFile(Bt(w,1).html,n),await Xt(),await Ft(w,"created"),_f(()=>Ef(w.id,1))):(await Cf(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 Of(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&&!Af(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(Ko(s.cwd),{recursive:!0}),a?(s.state="queued",await ht.writeFile(Bt(s,i).html,e),await Xt(),await Ft(s,"updated",{version:i}),_f(()=>Ef(s.id,i))):(await Cf(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 Cf(t,e,n){let r=xi(n);if(!r)throw new Error("image must be a PNG");let o=Bt(t,e);await ht.writeFile(o.png,n),If(t,{v:e,source:"image",htmlRel:null,pngRel:At.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Mf(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 Nf(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 Pf(t){let n=(await le()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=At.join(Sf,"mock-archive",t.id),o=Ko(t.cwd),s=At.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(At.join(o,i),At.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(At.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 Bf(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 tn from"node:fs/promises";import _e from"node:path";import yx from"node:os";import{randomUUID as bx}from"node:crypto";var Ln=process.env.BOSTRAT_STATE_DIR||_e.join(process.env.HOME||yx.tmpdir(),".bostrat"),jf=_e.join(Ln,"runbooks.json"),vi=_e.join(Ln,"runbooks"),Df=256*1024,kx=[{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 xx(t){return kx.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var Qe=null,Si=null;async function Ee(){return Qe||(Si||=(async()=>{try{Qe=JSON.parse(await tn.readFile(jf,"utf8"))}catch{Qe={runbooks:[]}}finally{Si=null}return Qe.runbooks||=[],Qe})(),Si)}async function Un(){await qt(jf,Qe)}function Fn(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 jn(t,e){return _e.join(vi,`${t.id}.v${e}.md`)}function Lf(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 Uf(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>Df)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${Df/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=xx(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 Ff(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function Hf({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 Ti(t){let n=(await Ee()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function Wf({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=Uf(r),c=await Ee(),l={id:bx().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 tn.mkdir(vi,{recursive:!0}),await tn.writeFile(jn(l,1),r),l.versions.push({v:1,mdRel:_e.relative(Ln,jn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Un(),await Fn(l,"created",{version:1}),{...l}}async function qf(t,{md:e,note:n}={}){let o=(await Ee()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");Ff(o,"update");let s=Uf(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await tn.mkdir(vi,{recursive:!0}),await tn.writeFile(jn(o,a),e),o.versions.push({v:a,mdRel:_e.relative(Ln,jn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Un(),await Fn(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),{...o}}async function Gf(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 Ff(r,e==="done"?"complete":"reopen"),r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Un(),await Fn(r,e==="done"?"done":"reopened"),{...r})}async function Kf(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 tn.rm(_e.join(Ln,o.mdRel),{force:!0}).catch(()=>{});return await Un(),r.trackDeletedAt||await Fn(r,"deleted"),{ok:!0,id:t,name:r.name}}async function Vf(t){let n=(await Ee()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),await Fn(r,"orphaned"),r.cwd=null;return n.length&&await Un(),n.length}async function Jf(t){let e=await Ee(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await Fn(r,"deleted");for(let o of r.versions||[])await tn.rm(_e.join(Ln,o.mdRel),{force:!0}).catch(()=>{})}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Un()),n.length}import zf from"node:fs/promises";import Vo from"node:path";import{randomUUID as Sx}from"node:crypto";var Yf=".bostrat/uploads",Ai=t=>Vo.join(t,Yf),Xf=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,vx=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),Tx=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 _i(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return vx.has(e)?"image":Tx.has(e)?"doc":null}function Ax(t){let r=Vo.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 Zf(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>Xf)throw new Error(`file too large \u2014 max ${Math.floor(Xf/(1024*1024))} MB`);let r=Ax(e),o=Vo.extname(r).slice(1),s=_i(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=Sx().slice(0,8),i=Ai(t);await zf.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await zf.writeFile(Vo.join(i,c),n),{id:a,name:r,rel:`${Yf}/${c}`,kind:s,size:n.length}}function Qf(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var _x=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function Ei(t){return t.role==="user"&&_x.test((t.text||"").trim())}function Hn(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var Rx=Ix(Ex),Ie=process.env.BOSTRAT_STATE_DIR||G.join(process.env.HOME||"/tmp",".bostrat"),ap=G.join(Ie,"tracks.json"),$x=G.join(Ie,"events.jsonl"),Ni=G.join(Ie,"workspaces"),Ox=process.env.BOSTRAT_DEFAULT_REPO||null,Yo={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"}},Bi=Object.keys(Yo);function Cx(t,{skipPermissions:e=!0}={}){let n=Yo[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 ip(t,e,{skipPermissions:n=!0}={}){let r=Yo[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 Zo(t){let e=Yo[t];if(e)return process.env[e.envVar]||e.base}async function Qo(t,e=!0){let n=Cx(t,{skipPermissions:e});if(!await Bs(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 ts=".bostrat/handoff/latest.md";function Mx(t,e=ts){return`${t.trim()}
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()}
73
75
 
74
- (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 Nx=/\(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 tp(t){return t.role==="user"&&Nx.test((t.text||"").trim())}var Di=".bostrat/handoff/subtask-result.md",Jo=".bostrat/handoff/lead-context.md",Px=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 ${Jo} \u2014 read that first for context. Your scoped task:
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:
75
77
 
76
78
  ${t}
77
79
 
78
- 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 ${Di} 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.`,Bx=(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,Ii=null;async function F(){return Yt||(Ii||=(async()=>{try{Yt=JSON.parse(await D.readFile(ap,"utf8"))}catch{Yt={tracks:[]}}finally{Ii=null}return Yt})(),Ii)}async function K(){await qt(ap,Yt)}var Y=(t,e)=>t.tracks.find(n=>n.id===e)||null,Ri=Promise.resolve();function Dx(t,e){let n=JSON.stringify({trackId:t,...e})+`
79
- `;return Ri=Ri.then(()=>D.mkdir(Ie,{recursive:!0})).then(()=>D.appendFile($x,n)).catch(()=>{}),Ri}var it=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),Dx(t.id,r),ed(t,r),r};fl(async({sessionName:t,from:e,to:n,version:r,status:o,error:s})=>{let i=(await F()).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 $i=(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 cp(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 lp(t,e,n,{kind:r}={}){let o=await q(e);if(!o)throw new Error(`box "${e}" is not running`);let s=Zo(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 j({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await F(),i=Y(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 en=new Set(["merged","conflict","canceled","failed","handed-off"]),ep=new Map;async function Lx(t){let e=G.join(yo(t),`${t.id}.ndjson`),n;try{n=await D.stat(e)}catch{return null}let r=ep.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await P(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return ep.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function Ux(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:qa(n.name,n.agent)}async function Re(t){let e=new Map((await yn()).map(a=>[a.name,a])),n=t.boxes.map(a=>{let i=e.get(a.name),c=en.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([Lx(t),Ux(t,n)]);return{...t,boxes:n,lastResponseAt:r,working:o,prompted:s}}var Fx=5e3;function Hx(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-Fx,f=r[u+1]?.createdAt??1/0,w=o.find(h=>!s.has(h.id)&&h.birthMs>=d&&h.birthMs<f);w&&(s.add(w.id),c.push({box:l.name,sessionId:w.id}))}return c}async function up(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=al(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=Hx(u,a,f=>i.has(f));for(let f of d){let w=u.find(h=>h.name===f.box);w&&(w.sessionId=f.sessionId),await Yc(f.box,f.sessionId),n=!0}}return n}async function dt(){let t=await F();return await up(t)&&await K(),Promise.all(t.tracks.map(Re))}async function dp(t,e){let n=await F();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 F(),n=Y(e,t);return n?(await up(e)&&await K(),Re(n)):null}var Wx={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function np(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(Wx[t.agent]||[]).some(n=>n.test(e))}var qx=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function rp(t){let e=(t.text||"").trim();return e?qx.some(n=>n.test(e)):!1}var Gx=10*6e4;async function ji(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=Mr(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=ii(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...Yd(n),...l],f={windows:[...vl(n),...Tl(n,co),...a,...u]},h=(await Promise.all(n.boxes.map(y=>du(y,e)))).flat(),g=h.map(y=>{let B={kind:"message",...y};if(B.role==="assistant"&&(B.text||"").trim()&&B.tools?.length){let dn=mr(B.agent).cliTools,zr=B.tools.filter(je=>!dn.has(je));return{...B,tools:zr.length?zr:void 0}}return B}).map(y=>{if(y.role!=="user")return y;let B=Co(y.text);return B?B.kind==="suppress"?null:{...y,note:B,text:Od(B)}:y}).filter(Boolean).filter(y=>!(y.role==="user"&&r.has((y.text||"").trim()))).filter(y=>!np(y)).filter(y=>!rp(y)).filter(y=>!tp(y)).filter(y=>!Ei(y)).filter(y=>y.role!=="assistant"||!!(y.text||"").trim()||!!y.images?.length).filter(y=>!Al(y,f)),x=g.filter(y=>y.failed&&y.text===ba);if(x.length){let y=new Map;for(let B of x){if(!y.has(B.box)){let je=[...(await pt(B.box,{lines:It}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Is=>Is[1]);y.set(B.box,je.length?je[je.length-1]:null)}let dn=y.get(B.box);dn&&(B.text=`\u25A0 ${dn}`)}}let A=await Promise.all(n.boxes.map(y=>pu(y,e)));await $d(n,A.flat()),await Id(n,g),await nd(n),await of(n,g).catch(()=>{});for(let y of n.boxes)y.agent==="codex"&&y.state==="live"&&await Pd(n,y),y.agent==="cursor"&&y.state==="live"&&await Ud(n,y);let S=await Promise.all(n.boxes.map(y=>fu(y,e)));await Rd(n,S.flat().filter(y=>!$i(y.box,y.ts,u)));let v=await P(n),E=c&&i.viewId?i.viewId:null,J=dd(v),I=Mo(v).filter(y=>!(y.kind==="message"&&(np(y)||rp(y)||tp(y)||Ei(y)))&&!(y.kind==="view-card"&&E&&y.viewId===E)),M=fd(I,J),m=I.filter(y=>!(y.kind==="message"&&y.role==="assistant"&&!M.has(y)&&$i(y.box,y.ts,J))),p=Xa(v,m).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=>!$i(y.anchorBox,y.anchorTs??y.ts,u)),b=[...m,...p].filter(y=>y.ts!=null).sort((y,B)=>y.ts-B.ts),T=Kx(n),ot=T.size?b.filter(y=>y.kind==="event"||!T.has(y.box??y.anchorBox)):b,$=n.boxes.find(y=>y.name===n.leadBox)||null,z=$?await sr($.cwd,$.sessionId,{fallbackNewest:!1}):null;if(!z&&$){let y=[...v].reverse().find(B=>B.type==="context.usage"&&B.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=[...h].reverse().find(y=>y.role==="assistant"&&y.model),H=N?.model||null,_=[...n.events||[]].reverse().find(y=>y.type==="model-change"),W=z?.model||H;_&&(!N?.ts||_.at>=N.ts)&&(W=_.to);let X=$?await pt($.name,{lines:It}).catch(()=>""):"",lt=$&&$n($.agent,X)?await me($.name).catch(()=>X):X,tt=$?Ve($,lt):null;tt&&rd(n,$,tt);let wt=!!$&&!tt&&ir($.agent,X);$&&!$.readyAt&&(wt||tt||X&&so($.agent,X))&&($.readyAt=Date.now(),await K());let Ct=!!$&&!$.readyAt&&Date.now()-(n.createdAt||0)<Gx;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&$&&so($.agent,X)&&!tt){let y=await F(),B=Y(y,n.id);B?.setup&&!B.setup.connectedAt&&(B.setup.connectedAt=Date.now(),n.setup.connectedAt=B.setup.connectedAt,await R(B,{type:"provider.connected",actor:"system",source:"pane",boxId:$.name,provider:$.agent,payload:{provider:$.agent,ts:B.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 B=Lo(n.id);B&&(it(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:B.finishedAt}),await K())}else if(tt||y>zd&&!wt&&!i.viewId||y>ai){let B=tt?"needs-input":y>ai?"timeout":"no-view";Lo(n.id,{error:B})&&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:B}}).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:W,options:Ju($?.agent)},pendingPrompt:tt,working:wt,leadStarting:Ct,handoff:De,view:Dt,queued:Jx(n.id)}}function Kx(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 fp(t,e){let n=await rt(t);if(!n)return null;let r=Gn(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 P(n),a=Mo(s).filter(d=>d.box===o),i=Xa(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:It}).catch(()=>"");u=ir(l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Li(t,{promptId:e,answers:n,text:r}={}){let o=Y(await F(),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 q(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Oa(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=Iu(i,n||[]);await gt(o.leadBox,f,{kind:"answer-prompt"}),c={mode:"keys",value:f.join(" "),count:f.length}}else{let f=yu(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 j({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),od(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function pp(t,{promptId:e,questionIndex:n}={}){let r=Y(await F(),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 q(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Oa(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=Ru(a,n);return c.length?(await gt(r.leadBox,c,{kind:"navigate-prompt"}),await j({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 Vx=1500,Ir=new Map;function mp(t){let e=Ir.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},Ir.set(t,e)),e}function Jx(t){return(Ir.get(t)?.items??[]).map(e=>({...e}))}function es(t){let e=Ir.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 zx(t){let e=mp(t);e.timer||(e.timer=setInterval(()=>Xx(t).catch(()=>{}),Vx))}async function Xx(t){let e=Ir.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=Y(await F(),t);if(!n||n.archivedAt){es(t);return}let r=Mr(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Hn(e,!1);return}let o=await q(n.leadBox);if(!o){Hn(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox),a=await pt(n.leadBox,{lines:It}).catch(()=>"");if(oo(s?.agent,a)){Hn(e,!1);return}if($n(s?.agent,a)){let c=await me(n.leadBox).catch(()=>a);if(Ve(s,c)){Hn(e,!1);return}}if(!Hn(e,!0))return;let i=e.items.shift();e.idleSince=null,await hp(n,s,o,i.text,i.kind??null,i.attachments??null),await K()}finally{e.flushing=!1}}}async function hp(t,e,n,r,o=null,s=null){let a=r,i=!1;t.pendingHandoffHydrate&&(a=Mx(r,t.pendingHandoffHydrate.artifact||ts),delete t.pendingHandoffHydrate,i=!0);let c=Array.isArray(s)?s.filter(l=>l&&l.rel):[];if(c.length){let l=Qf(c.map(u=>u.rel));a=a.trim()?`${a.trim()}
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()}
80
82
 
81
- ${l}`:l}await we({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:Zo(n.agent)}),await yt(t.leadBox,a,{...re(e?.agent),kind:i?"lead-handoff-first-prompt":"say"}),i&&await j({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"||!On(e))&&!o.length)throw new Error("text (string) or an attachment required");let s=Y(await F(),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 q(s.leadBox);if(!a&&await Hi(s)&&(await K(),a=await q(s.leadBox)),!a)throw new Error(`lead box "${s.leadBox}" is not running`);let i=s.boxes.find(f=>f.name===s.leadBox),c=On(e),l=mp(t),u=await pt(s.leadBox,{lines:It}).catch(()=>""),d=l.items.length>0||oo(i?.agent,u);if(!d&&$n(i?.agent,u)){let f=await me(s.leadBox).catch(()=>u);d=!!Ve(i,f)}if(d){let f={id:Xo(),text:c,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(f),zx(t),delete s.draft,await K(),{ok:!0,leadBox:s.leadBox,queued:!0,id:f.id,position:l.items.length}}return await hp(s,i,a,c,n,o),await K(),{ok:!0,leadBox:s.leadBox}}var op="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 wp(t,{hint:e}={}){let n=Y(await F(),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=ii(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";Xd(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${op}
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}
82
84
 
83
- What to preview: ${String(e).trim()}`:op;try{await $e(t,a,{kind:"spin-up-view"})}catch(i){throw Lo(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Ui(t){let e=Y(await F(),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=es(t),o=await q(e.leadBox);o&&await gt(e.leadBox,["Escape"],{kind:"interrupt"});let s=o?await Qx(e,n):null,a=o?await Yx(e).catch(()=>null):null;return await j({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 Yx(t){let e=yi(await P(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 Zx=[250,400,500,600];async function Qx(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 Zx){await Vi(r);let o=await pt(t.leadBox,{lines:It}).catch(()=>""),s=Ca(e.agent,o);if(s&&Ou(n.text,s))return await t0(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 t0(t,e,n){for(let r=0;r<3;r++){await to(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await Vi(200);let o=await pt(t,{lines:It}).catch(()=>""),s=Ca(e,o);if(!s)return!0;n=s.length+8}return!1}async function gp(t,e){if(typeof e!="string")throw new Error("text (string) required");let n=Y(await F(),t);if(!n)throw new Error(`track "${t}" not found`);let r=On(e);return r?n.draft={text:r,updatedAt:Date.now()}:delete n.draft,await K(),{ok:!0,draft:n.draft||null}}async function yp(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim(),r=Y(await F(),t);if(!r)throw new Error(`track "${t}" not found`);let o=await q(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=zu(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await sr(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:Zo(o.agent)});let u=await Xu(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 e0(t){for(let e of t){if(!e||!await Pa(e))continue;let n=await Mu(e).catch(()=>null);if(!n)continue;if((await D.stat(G.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}async function n0(t,e,{repo:n,cwd:r}={}){await D.mkdir(Ni,{recursive:!0});let o=G.join(Ni,t);typeof n=="string"&&!G.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Fl(n));let s=await e0([n,r,Ox]);if(s){let a=`track/${Op(e)}-${t}`;try{let i=await Bu(s);await Ba(s,o,a,i),await Pi(o);let c=await Z(s).catch(()=>null);return{dir:o,base:s,branch:a,kind:"worktree",baseRef:i,repo:c}}catch{await D.rm(o,{recursive:!0,force:!0}).catch(()=>{})}}return await D.mkdir(o,{recursive:!0}),await Du(o),await Pi(o),{dir:o,base:null,branch:null,kind:"repo"}}var Rr="# bostrat workspace",Er=`${Rr}
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}
84
86
 
85
87
  When asked to run, preview, or demo this app so the operator can review it: do NOT
86
88
  start an ad-hoc dev server and do NOT share a localhost URL (the operator is on
@@ -186,23 +188,36 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
186
188
  statement, or omitted entirely renders no controls \u2014 so close EVERY completed
187
189
  implementation turn this way, every time. When the operator has already asked you
188
190
  to commit / push / open the PR, just do it; do not re-offer.
189
- `;async function Pi(t){try{await D.writeFile(G.join(t,"AGENTS.md"),Er,{flag:"wx"})}catch{return}await r0(t,["/AGENTS.md"])}var Oi=t=>typeof t=="string"&&(t===Rr||t.startsWith(`${Rr}
190
- `)),Ci=Er.replace(`${Rr}
191
- `,`${Rr}
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}
192
207
 
193
208
  (Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
194
209
  SCOPE: these instructions apply ONLY when your working directory is under
195
210
  ~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
196
211
  When working anywhere else, ignore everything below.
197
- `);async function bp(t){let{stdout:e}=await Rx("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return G.resolve(t,e.trim())}async function r0(t,e){try{let n=await bp(t);await D.mkdir(G.dirname(n),{recursive:!0});let r=await D.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
198
- `).includes(s));o.length&&await D.appendFile(n,(r.endsWith(`
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(`
199
214
  `)||!r?"":`
200
215
  `)+o.join(`
201
216
  `)+`
202
- `)}catch{}}async function o0(t,e){try{let n=await bp(t),r=await D.readFile(n,"utf8"),o=r.split(`
217
+ `)}catch{}}async function b0(t,e){try{let n=await Ep(t),r=await P.readFile(n,"utf8"),o=r.split(`
203
218
  `).filter(s=>s!==e);o.length!==r.split(`
204
- `).length&&await D.writeFile(n,o.join(`
205
- `))}catch{}}async function kp(){let t={refreshed:0,removed:0,codexGlobal:null};try{await D.mkdir(Ie,{recursive:!0});for(let r of["CLAUDE.md","AGENTS.md"])await D.writeFile(G.join(Ie,r),Er).catch(()=>{});let e=process.env.CODEX_HOME||G.join(process.env.HOME||"/tmp",".codex");if((await D.stat(e).catch(()=>null))?.isDirectory()){let r=G.join(e,"AGENTS.md"),o=await D.readFile(r,"utf8").catch(()=>null);o===null?(await D.writeFile(r,Ci,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):Oi(o)&&o!==Ci&&(await D.writeFile(r,Ci).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[Ni,qn]){let o=await D.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=G.join(r,s.name),i=G.join(a,"AGENTS.md"),c=await D.readFile(i,"utf8").catch(()=>null);c!==null&&Oi(c)&&c!==Er&&(await D.writeFile(i,Er).catch(()=>{}),t.refreshed++);let l=G.join(a,"CLAUDE.md"),u=await D.readFile(l,"utf8").catch(()=>null);u!==null&&Oi(u)&&(await D.rm(l,{force:!0}).catch(()=>{}),await o0(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function s0(t){let e=t.workspace;if(!(!e||!e.dir))try{e.kind==="worktree"&&e.base?await hr(e.base,e.dir):await D.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function Fi({name:t,cwd:e,repo:n,agent:r="claude",skipPermissions:o=!0}){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");let s=await Qo(r,o),a=await F(),i=Xo().slice(0,8),c=await n0(i,t,{repo:n,cwd:e}),l=new Set(a.tracks.flatMap(w=>(w.boxes||[]).map(h=>h.name))),u=`${t.trim()} lead`;l.has(Ds(u))&&(u=`${t.trim().slice(0,50).trim()} ${i} lead`);let d=await Ue(u,c.dir,{agentCmd:s,agent:r});ao({cwd:d.cwd,sessionName:d.name,agent:r,agentCmd:s});let f={id:i,name:t.trim(),cwd:d.cwd,workspace:c,leadBox:d.name,boxes:[Ep(d,"lead","live")],events:[],createdAt:Date.now()};return it(f,"opened",{agent:r,box:d.name,skipPermissions:o,name:f.name,cwd:f.cwd,workspace:{base:c.base,branch:c.branch,kind:c.kind,...c.baseRef?{baseRef:c.baseRef}:{},...c.repo?{repo:c.repo}:{}}}),a.tracks.push(f),await K(),Re(f)}var a0=new Set(["claude","codex","cursor"]);async function xp({provider:t="claude",fresh:e=!1}={}){if(!a0.has(t))throw new Error(`unknown setup provider "${t}" (claude | codex | cursor)`);let r=(await F()).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 ns(r.id).catch(()=>{})}let o=(await Qo(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Xo().slice(0,8),a=G.join(Ie,"setup",s);await D.mkdir(a,{recursive:!0});let i=await Ue(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&ao({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:[Ep(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 F()).tracks.push(c),await K(),Re(c)}async function Sp(t,e,n){await rf(t).catch(()=>{});let r=$r.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),$r.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of Gn(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Ki(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 F(),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 vp(t,{cascadeMocks:e=!0,cascadeRunbooks:n=!1}={}){let r=await F(),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 Pf(s),await Sp(t,s,"track deleted"),n?await Jf(t).catch(()=>{}):await Vf(t).catch(()=>{}),await td(),await s0(s),e&&await Bf(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 ns(t){let e=await F(),n=Y(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is already archived`);await Sp(t,n,"track archived");for(let r of n.boxes)en.has(r.state)||(r.state="stopped");return n.archivedAt=Date.now(),it(n,"archived",{boxes:n.boxes.length}),await K(),Re(n)}async function Hi(t){if(t.archivedAt)return!1;let e=t.boxes.find(r=>r.name===t.leadBox);if(!e||en.has(e.state)||await q(e.name))return!1;let n=ip(e.agent,e.sessionId);if(!await Bs(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}),ao({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 Tp(t){let e=await F(),n=Y(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 Hi(n),await K(),Re(n)}async function Ap(){let t=await F(),e=new Set((await yn()).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),!en.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await K(),r}async function _p(t){let e=await F(),n=Y(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(en.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await q(n.leadBox)){let a=await pt(n.leadBox,{lines:It}).catch(()=>"");if(oo(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 Hi(n);if(await K(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Re(n)}var Ep=(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 Ip(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 $r=new Map,Mr=t=>$r.get(t)||(Yt?Y(Yt,t)?.handoff:null)||null;async function zo(t,e){let n=await F(),r=Y(n,t);r&&(r.handoff={...r.handoff||{},...e},await K())}function i0(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 Rp(){let t=await F(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=i0(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await q(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=Ip(l),f=d[d.length-1],w=f.agent,h=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:ts,at:u},it(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:w,toAgent:r.toAgent,fromSessionId:h,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 j({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 j({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 Wi(t,e,n=!0,r=null){let o=$r.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=Y(await F(),t);if(!s)throw new Error(`track "${t}" not found`);let a=await Qo(e,n),i=await q(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:cp(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return $r.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}),c0(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(()=>{}),j({flow:"lead-handoff",phase:"failed",trackId:t,step:c.step,error:c.error,fromBox:c.fromBox,toAgent:c.toAgent,toBox:c.toBox}).catch(()=>{})}),j({flow:"lead-handoff",phase:"started",trackId:t,fromBox:c.fromBox,toAgent:e,cwd:i.cwd}).catch(()=>{}),c}async function c0(t,e,n,r,o){let s=async(v,E={})=>{t.step=v,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${v}`),await zo(t.trackId,{step:v,toBox:t.toBox}),await j({flow:"lead-handoff",phase:v,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 F(),i=Y(a,t.trackId)?.boxes.find(v=>v.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 Il(t.fromBox),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await ea(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:Zo(o)});let f=await pt(t.fromBox,{lines:It}).catch(()=>"");if(!so(o,f)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let v=ip(c,l,{skipPermissions:t.skipPermissions});await ea(t.fromBox,{agentCmd:v,agent:c,resumeSessionId:l,pinned:u});let E=await F(),J=Y(E,t.trackId);if(J){let I=J.boxes.find(M=>M.name===t.fromBox);I&&(I.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 F(),h=Y(w,t.trackId);if(!h)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let g=h.boxes.find(v=>v.name===t.fromBox);if(!g)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let x=Date.now(),A=Ip(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",h.pendingHandoffHydrate={artifact:ts,at:x},it(h,"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 l0=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,u0=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,qn=G.join(Ie,"worktrees"),Mi=[Jo,Di],Zt=new Map;var Gn=t=>{let e=new Map([...Zt.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=Yt?Y(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)},qi=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 d0(t,e,n){let r=await F(),o=Y(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await K())}var Or=t=>d0(t.trackId,t.id,qi(t));async function Wn(t,e){t.step=e,await Or(t)}function f0(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 p0(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function m0(t,e,n){if(!p0(n)||Zt.has(e)||!await q(n.box).catch(()=>null))return!1;try{await D.access(n.dir)}catch{return!1}let o=await q(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),Pp(s,o).catch(a=>Mp(s,a)),!0}async function $p(){let t=await F(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Zt.has(r))continue;if(await m0(n,r,o)){e=!0,it(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await j({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=f0(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!en.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 j({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await sp(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||en.has(r.state)||Zt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?G.join(qn,`${n.id}-${r.subtaskId}`):null;await j({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await sp(n,r.name,o)}}return e=await h0(t)||e,e&&await K(),e}async function sp(t,e,n){if(e&&await Wt(e).catch(()=>{}),n){let r=await q(t.leadBox).catch(()=>null);r?.cwd&&await hr(r.cwd,n).catch(()=>{})}}async function h0(t){let e;try{e=await D.readdir(qn)}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(G.basename(o.dir));let r=!1;for(let o of e){if(n.has(o))continue;let s=G.join(qn,o),a=(t.tracks||[]).find(c=>o.startsWith(`${c.id}-`)),i=a?await q(a.leadBox).catch(()=>null):null;i?.cwd?await hr(i.cwd,s).catch(()=>{}):await D.rm(s,{recursive:!0,force:!0}).catch(()=>{}),await j({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}var Op=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Cr=class extends Error{};async function Cp(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!On(n))throw new Error("task (string) required");let s=await Qo(e,r),a=Y(await F(),t);if(!a)throw new Error(`track "${t}" not found`);let i=await q(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 Pa(i.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${i.cwd})`);let c=Xo().slice(0,8),l={id:c,trackId:t,trackName:a.name,agent:e,task:On(n),skipPermissions:r,reason:cp(o),step:"snapshot",leadBox:a.leadBox,box:null,branch:`bostrat/${Op(a.name)}-${c}`,base:null,dir:G.join(qn,`${t}-${c}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Zt.set(c,l),await Or(l),w0(l,i,s).catch(u=>Mp(l,u)),l}async function Mp(t,e){t.failedStep=t.step,t.error=e instanceof Cr?"canceled":e?.message||String(e),t.step=e instanceof Cr?"canceled":"error",t.finishedAt=Date.now(),await Ki(t).catch(()=>{}),await Or(t).catch(()=>{}),await k0(t).catch(()=>{})}function Gi(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 Np(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 w0(t,e,n){await Wn(t,"snapshot"),t.base=await Nu(e.cwd,`bostrat: snapshot for subtask ${t.id}`),await Wn(t,"spawn"),await D.mkdir(qn,{recursive:!0}),await Ba(e.cwd,t.dir,t.branch,t.base),await Pi(t.dir),await g0(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 y0(t,o),await lp(t.trackId,o.name,Px(t.task),{kind:"subtask"}),await Pp(t,e)}async function Pp(t,e){await Wn(t,"working");let n=G.join(t.dir,Di);if(await S0(n,t),t.canceled)throw new Cr;await Wn(t,"merge"),await Pu(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await ju(e.cwd,t.base,t.branch,Mi),t.files=await Lu(e.cwd,t.base,t.branch,Mi);let r=await Uu(e.cwd,t.base,t.branch,Mi);t.applied=r.applied,t.conflicts=r.conflicts,await Or(t);let o=await x0(t,e,n);await Wn(t,"resume"),await lp(t.trackId,t.leadBox,Bx(t.task,t.stat,t.conflicts,o),{kind:"subtask-resume"}),await Wn(t,"reap"),await Ki(t),t.step="done",t.finishedAt=Date.now(),await b0(t),await Or(t)}async function g0(t,e){let n=G.join(t.dir,G.dirname(Jo));await D.mkdir(n,{recursive:!0});let r="";try{r=await cl(e.cwd,e.sessionId)}catch{}if(!r)try{r=await D.readFile(G.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}
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}
206
221
 
207
222
  **Delegated task:** ${t.task}
208
223
 
@@ -213,53 +228,53 @@ When working anywhere else, ignore everything below.
213
228
  ## Conversation so far (lead \u2192 you)
214
229
 
215
230
  ${r}
216
- `;await D.writeFile(G.join(t.dir,Jo),o)}async function y0(t,e){let n=await F(),r=Y(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]||{},...qi(t)},await K())}async function b0(t){let e=await F(),n=Y(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 k0(t){let e=await F(),n=Y(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]||{},...qi(t)},await K()}async function x0(t,e,n){let r=`.bostrat/handoff/subtask-${t.id}.md`;try{let o=G.join(e.cwd,r);await D.mkdir(G.dirname(o),{recursive:!0}),await D.copyFile(n,o)}catch{}return r}async function Ki(t){if(t.box&&await Wt(t.box).catch(()=>{}),t.dir){let n=(await q(t.leadBox).catch(()=>null))?.cwd;n&&await hr(n,t.dir).catch(()=>{})}}async function S0(t,e){let n=(e.startedAt||Date.now())+l0;for(;;){if(e.canceled||e.forceMerge)return;try{await D.access(t);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await Vi(u0)}}function Vi(t){return new Promise(e=>setTimeout(e,t))}import Lr from"node:fs/promises";import ms from"node:path";import dS from"node:os";import v0 from"node:fs/promises";import Bp from"node:path";import T0 from"node:os";var Dp=()=>Bp.join(process.env.BOSTRAT_STATE_DIR||Bp.join(process.env.HOME||T0.tmpdir(),".bostrat"),"integrations.json");async function nn(){try{return JSON.parse(await v0.readFile(Dp(),"utf8"))||{}}catch{return{}}}var rs=["linked","prOpened","prMerged","create"],Ji=["prompt","auto","off"],A0={create:["prompt","off"]},jp=t=>A0[t]??Ji;function os(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of rs){let s=jp(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 rn(){let t=await nn(),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:os(t),defaultTeam:n}:null}async function ss(){let t=await nn(),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:os(t)}}async function zi(){return os(await nn())}async function Lp(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${rs.join(", ")}`);for(let[r,o]of e){if(!rs.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${rs.join(", ")}`);let s=jp(r);if(!s.includes(o))throw new Error(s.length<Ji.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Ji.join(", ")}`)}let n=await nn();return n.ticketFlow={...os(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await qt(Dp(),n,{mode:384}),n.ticketFlow}async function Nr(){let t=(await nn()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function Up(){let t=(await nn()).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 on(){let t=(await nn()).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 Dr from"node:fs/promises";import Yp from"node:path";import Y0 from"node:os";import{randomUUID as Zp}from"node:crypto";var Fp=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function _0(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function Hp(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function is(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=Fp();for(let d of n){let f=d.payload?.model||"unknown",w=d.provider||"unknown",h=d.boxId||"unattributed",g=_0(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&&!No(f)&&(c++,l.add(f)),a+=x,Hp(u,g),r[f]||={usd:0,tokens:Fp(),events:0},r[f].usd+=x,r[f].events+=1,Hp(r[f].tokens,g),o[w]||={usd:0,events:0},o[w].usd+=x,o[w].events+=1,s[h]||={usd:0,events:0,provider:w,models:new Set,label:e[h]||h},s[h].usd+=x,s[h].events+=1,f!=="unknown"&&s[h].models.add(f),d.epoch!=null){let S=(s[h].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:Pr(a),totalTokens:u,turns:n.length,repricedTurns:i,unpricedTurns:c,unpricedModels:[...l],byModel:as(r,d=>({...d,usd:Pr(d.usd)})),byProvider:as(o,d=>({...d,usd:Pr(d.usd)})),byBox:as(s,d=>({usd:Pr(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:as(d.epochs,f=>({usd:Pr(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var Pr=t=>Math.round(t*1e4)/1e4,as=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));var Yi={};Hc(Yi,{createComment:()=>P0,findIssueUrl:()=>$0,issueStates:()=>j0,mdToAdf:()=>Gp,moveIssue:()=>L0,parseIssueRef:()=>R0,resolveIssue:()=>O0,uploadImage:()=>B0,upsertCard:()=>C0,whoami:()=>Xi});var Kn=t=>String(t.baseUrl||"").replace(/\/$/,""),qp=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function sn(t,e,n,r){let o=await fetch(`${Kn(t)}${n}`,{method:e,headers:{authorization:qp(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 E0=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,I0=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function R0(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(E0);return r?r[1].toUpperCase():null}let n=e.match(I0);return n?n[1]:null}function $0(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 O0(t,e){let n;try{n=await sn(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:`${Kn(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function C0(t,e,{url:n,title:r,subtitle:o}){await sn(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 M0=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function N0(t){let e=[],n=0;for(let r of t.matchAll(M0)){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 Gp(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
217
- `).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...N0(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 P0(t,e,n){let r=await sn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:Gp(n)});return r?.id?`${Kn(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function Xi(t){let e=await sn(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Kn(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function B0(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(`${Kn(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:qp(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??`${Kn(t)}/rest/api/3/attachment/content/${c.id}`}var D0={new:"unstarted",indeterminate:"started",done:"completed"},Wp=t=>D0[t?.key]??"started";async function j0(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([sn(t,"GET",`/rest/api/3/issue/${n}?fields=status`),sn(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:Wp(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:Wp(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function L0(t,e,n){await sn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var ec={};Hc(ec,{createComment:()=>V0,createIssue:()=>tc,createProject:()=>Qi,findIssueUrl:()=>q0,issueStates:()=>z0,moveIssue:()=>X0,parseIssueRef:()=>W0,resolveIssue:()=>G0,teams:()=>Vn,uploadImage:()=>J0,upsertCard:()=>K0,whoami:()=>Zi});var U0=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Qt(t,e,n={}){let r=await fetch(U0(),{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 F0=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,H0=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function W0(t){let e=String(t||"").trim(),n=e.match(F0);if(n)return n[1].toUpperCase();let r=e.match(H0);return r?r[1]:null}function q0(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 G0(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 K0(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 V0(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 Zi(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 J0(t,{filename:e,contentType:n,buffer:r}){let o=await Qt(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
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!) {
218
233
  fileUpload(contentType: $contentType, filename: $filename, size: $size) {
219
234
  success
220
235
  uploadFile { uploadUrl assetUrl headers { key value } }
221
236
  }
222
- }`,{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 z0(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 X0(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 Vn(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 Qi(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 tc(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 Qp={linear:{api:ec,config:rn},jira:{api:Yi,config:ss}};async function ic(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(Qp)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function an(t){let e=Qp[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function cc(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 tm=()=>process.env.BOSTRAT_STATE_DIR||Yp.join(process.env.HOME||Y0.tmpdir(),".bostrat"),sc=()=>Yp.join(tm(),"tickets.json"),em=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),ls=t=>`${em()}/track/${t}`,cs=null,nc=null,rc=Promise.resolve();async function us(){return cs||(nc??=Dr.readFile(sc(),"utf8").then(t=>JSON.parse(t)?.tickets??{}).catch(()=>({})).then(t=>cs=t).finally(()=>nc=null),nc)}async function Kp(){let t=JSON.stringify({tickets:cs},null,2),e=`${sc()}.${process.pid}.tmp`;await Dr.mkdir(tm(),{recursive:!0}),await Dr.writeFile(e,t),await Dr.rename(e,sc())}async function Oe(){rc=rc.then(Kp,Kp),await rc.catch(()=>{})}async function te(t){return(await us())[t]??null}async function nm(){return{...await us()}}async function rm(){return(await ic()).length>0}async function Jn(t,e,{source:n="api"}={}){let r=await ic();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 us())[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 jr(t.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await Br(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await am(t.id,c,"linked"),c}async function lc(t){let e=await us();return e[t]?(delete e[t],await Oe(),!0):!1}var ac=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function om(t,e){if(!t.track?.cwd)return null;let n=is(await P({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function Z0(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${ac(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${ac(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function Q0(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(ac(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function jr(t,e){let n=await an(e);if(!n)return;let r=await om(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:ls(t),title:`Bostrat Track: ${e.track.name}`,subtitle:Q0(e,r)})}function ds(t){let e=new URL(em());return`${e.protocol}//${t}.${e.host}/`}var tS={linked:"linked",opened:"prOpened",merged:"prMerged"};function eS(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 sm(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 am(t,e,n){let r=await an(e);if(!r)return;let o=r.cfg.ticketFlow?.[tS[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=cc(s,a),c=eS(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 sm(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:Zp(),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 im(t){let e=await te(t);if(!e)throw new Error("no ticket linked to this Track");let n=await an(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=cc(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 uc(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 an(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=cc(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 sm(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function cm(t,e){let n=await te(t);if(!n)throw new Error("no ticket linked to this Track");let r=await an(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 lm(t,e,n){if(!e)throw new Error("offerId required");let r=await P({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 uc(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 Vp=50;async function um(){let t=await rn();if(!t)throw new Error("no ticketing integration configured");return Vn(t)}function nS(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>Vp)throw new Error(`too many issues (${n.length}); cap is ${Vp} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function rS(t,e,n,r){let o=r??await Vn(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 dm(t,e){let n=await rn();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=nS(e),o=await Vn(n),s=await rS(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Zp();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 fm(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await P({id:t.id,cwd:t.cwd}),s=o.find(h=>h.type==="ticket.create.offered"&&h.payload?.proposalId===e);if(!s)throw new Error("unknown proposal");if(o.some(h=>h.type==="ticket.create.answered"&&h.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 rn();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let h=(await Vn(a)).find(g=>g.id===r);if(!h)throw new Error("unknown team");c={id:h.id,key:h.key,name:h.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let h=await Qi(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=h.id,l.push({kind:"epic",title:h.name,url:h.url})}for(let h of i.issues||[])try{let g=await tc(a,{teamId:c.id,title:h.title,description:h.description,projectId:d,priority:h.priority});l.push({kind:"issue",key:g.key,title:g.title,url:g.url})}catch(g){u.push({title:h.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(h=>h.kind==="issue"),w=null;return i.linkCurrentTrack&&f.length===1&&!await te(t.id)&&(w=await Jn(t,f[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:w?w.key:null}}async function Br(t,e){let n=await an(t);n&&await n.api.createComment(n.cfg,t,e)}async function oS(t,e,n){let r=await an(t);if(!r)return null;let o=await Ae(e);if(!o)return null;let s=Ze(o,n);if(!s)return null;let a=await Dr.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 Jp=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),oc=new Map;function sS(t,e){if(Jp()<=0)return jr(t,e);if(oc.has(t))return;let n=setTimeout(()=>{oc.delete(t),te(t).then(r=>r?jr(t,r):null).catch(()=>{})},Jp());n.unref?.(),oc.set(t,n)}var zp=new Map;async function Xp(t,e,n){let r=zp.get(t)??0;Date.now()-r<600*1e3||(zp.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 aS(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 ic())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 Jn(s,o,{source:"chat"})}catch(a){await Xp(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 jr(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 jr(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await om(n,t.trackId).catch(()=>null):null;await Br(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
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?`
223
238
 
224
- Agent cost for this Track: ${Z0(o)}.`:"")),await am(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await oS(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await Br(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
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}\`)`:""}:
225
240
 
226
- ![${r.name}](${o})`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await Br(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 Br(n,`**Bostrat** \u2014 live preview **${r.name}**: ${ds(r.slug)}
227
- _(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await sS(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await Xp(t.trackId,s?.cwd??null,o)}}function pm({resolveTrack:t}){Mt.on("event",e=>{aS(e,{resolveTrack:t}).catch(()=>{})})}import iS from"ws";var mm=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function fs(t,e,n={}){let r=await fetch(`${mm()}/${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 dc=(t,e)=>fs("chat.postMessage",t,e),zn=(t,e)=>fs("chat.update",t,e);async function _t(t,e){await fetch(t,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(e)}).catch(()=>{})}async function cS(t,e,n={}){let r=await fetch(`${mm()}/${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 hm(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await cS("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 fs("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var lS=2200;function wm({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 fs("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new iS(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,h=setTimeout(()=>{w=!0,f()},lS);Promise.resolve().then(()=>e(d)).then(g=>{clearTimeout(h),!w&&(w=!0,f(d.accepts_response_payload?g:void 0))},g=>{clearTimeout(h),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 bm=()=>process.env.BOSTRAT_STATE_DIR||ms.join(process.env.HOME||dS.tmpdir(),".bostrat"),wc=()=>ms.join(bm(),"slack.json"),ps=null,fc=null,pc=Promise.resolve();async function yc(){return ps||(fc??=Lr.readFile(wc(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>ps=t).finally(()=>fc=null),fc)}async function gm(){let t=JSON.stringify({threads:ps},null,2),e=`${wc()}.${process.pid}.tmp`;await Lr.mkdir(bm(),{recursive:!0}),await Lr.writeFile(e,t),await Lr.rename(e,wc())}async function ue(){pc=pc.then(gm,gm),await pc.catch(()=>{})}async function bc(t){return(await yc())[t]??null}async function fS(t,e){let n=await yc();for(let[r,o]of Object.entries(n))if(o.channel===t&&o.ts===e)return{trackId:r,rec:o};return null}var U=t=>String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");function Ur(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function pS(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return Ur(n.replace(/\n+/g," "),e)}function km(t){let e=`*${U(t.name)}*${t.archivedAt?" \xB7 _archived_":""}`,n=`${U(t.repoLabel)} \xB7 \`${U(t.branch||"?")}\` \xB7 lead *${U(t.lead)}*`,r=t.kickoff?`> ${U(Ur(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
228
- ${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${ls(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function gc(t,e){await zn(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:km(e)})}async function ln(t,e,n,r=void 0){return dc(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var ym=new Set;function cn(t){ym.add(t),t.catch(()=>{}).finally(()=>ym.delete(t))}var Q=t=>({response_type:"ephemeral",text:t}),mS="*/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._",hS=["claude","cursor","codex"];function mc(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 hc=(t,e)=>Q(`No live Track matches \u201C${U(e)}\u201D. Live: ${t.map(n=>`\`${U(n.name)}\``).join(" \xB7 ")||"none"}`);async function xm(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 _t(o,{...Q(`At capacity \u2014 ${a.length} live Tracks (limit ${e.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!e.channel){await _t(o,{...Q("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?ms.basename(c.base):"fresh workspace"),lead:"claude"},u=await dc(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:km(l),unfurl_links:!1});l.ts=u.ts,(await yc())[i.id]=l,await ue(),await _t(o,{...Q(`\u2713 *${U(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await _t(o,{...Q(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function wS(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await bc(n.id);s&&(s.archivedAt=Date.now(),await ue(),await gc(e,s).catch(()=>{})),await _t(r,{...Q(`\u2713 *${U(n.name)}* archived.`),replace_original:!0})}catch(s){await _t(r,{...Q(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function gS(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await _t(o,{...Q(`\u2713 Handing *${U(n.name)}* to ${U(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await _t(o,{...Q(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function yS(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 _t(r,{...Q(`\u2713 Stopped *${U(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 _t(r,{...Q(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function bS(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:Ur(`${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 *${U(t)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function kS(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?ms.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 *${U(s.name)}* \u2014 ${U(i)}${a.branch?` \`${U(a.branch)}\``:""} \xB7 lead ${U(c)} \xB7 ${l}`}),o=e.length-n.length;return r.join(`
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(`
229
244
  `)+(o?`
230
- _${o} archived_`:"")}async function xS(t,e){let n=await on();if(!n)return Q("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 Q("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?(cn(xm(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),Q(`Starting *${U(u)}* on ${U(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:bS(u,i)}}case"archive":{if(!a.length)return Q("Usage: `/bostrat archive <track>`");let{match:i,live:c}=mc(await r.listTracks(),a.join(" "));return i?(cn(wS(t,n,{track:i,responseUrl:e.response_url})),Q(`Archiving *${U(i.name)}*\u2026`)):hc(c,a.join(" "))}case"handoff":{let i=r.knownAgents??hS,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return Q(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:d}=mc(await r.listTracks(),l);return u?(cn(gS(t,n,{track:u,agent:c,responseUrl:e.response_url})),Q(`Handing *${U(u.name)}* to ${U(c)}\u2026`)):hc(d,l)}case"stop":{if(!a.length)return Q("Usage: `/bostrat stop <track>`");let{match:i,live:c}=mc(await r.listTracks(),a.join(" "));return i?(cn(yS(t,n,{track:i,responseUrl:e.response_url})),Q(`Stopping *${U(i.name)}*\u2026`)):hc(c,a.join(" "))}case"list":return Q(await kS(r));default:return Q(mS)}}async function SS(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await bc(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:`~${U(l||"question")}~
231
- \u2713 *${U(a)}*`}}]}).catch(()=>{})}}catch(c){await _t(n.response_url,Q(`Couldn't answer: ${c?.message||c}`))}}async function vS(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,xo(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 _t(n.response_url,Q(`Couldn't act on the offer: ${c?.message||c}`))}}async function TS(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await on();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 _t(e.response_url,{...Q(`Starting *${U(s)}*\u2026`),replace_original:!0}),cn(xm(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"?cn(SS(t,r,e,o)):n.action_id==="bostrat-ship"&&cn(vS(t,r,e,o))}async function AS(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 fS(e.channel,e.thread_ts);if(!n)return;let r=await on();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await ln(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function _S(t){return async e=>{if(e.type==="slash_commands")return xS(t,e.payload);if(e.type==="interactive"){await TS(t,e.payload);return}if(e.type==="events_api"){await AS(t,e.payload?.event);return}}}var ES=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 IS(t,e){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${U(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:Ur(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 RS(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:`~${U(e.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await ln(t,e,r.question||"The lead needs an answer",IS(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await ue()}async function $S(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:`~${U(s||"question")}~
232
- \u2713 _answered_`}}]})}async function OS(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 ln(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${U(Ur(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 CS(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 MS(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??NS)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await hm(t.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:e.channel,threadTs:e.ts,initialComment:`\u{1F3A8} mock *${U(o.name)}*${a}${o.group?` (group \`${U(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${U(a.name)}*`).join(", ");await ln(t,e,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${U(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function NS(t,e){let n=await Ae(t);if(!n)return null;let r=Ze(n,e);if(!r)return null;let o=await Lr.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 PS(t,e){if(!ES.has(e.type))return;let n=await bc(e.trackId);if(!n)return;let r=await on();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 gc(r,n);return;case"turn.agent":{if(!o.text)return;let s=pS(o.text);if(!s)return;await ln(r,n,s,[{type:"section",text:{type:"mrkdwn",text:U(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${ls(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return RS(r,n,e);case"prompt.answered":return $S(r,n,e);case"pr.offered":return OS(r,n,e);case"pr.offer.answered":return CS(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 ln(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 gc(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await ln(r,n,`Live preview ${o.name}: ${ds(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${U(o.name)}*: ${ds(o.slug)}
233
- _runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return MS(r,n,e,t.deps)}}function Sm(t){let e={deps:t};Mt.on("event",s=>{PS(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await on()||(r&&clearInterval(r),r=null,n=wm({getToken:async()=>(await on())?.appToken,onEnvelope:_S(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import BS from"node:fs/promises";function DS(t){let e=new Map;for(let n of String(t||"").split(`
234
- `)){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 jS(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 LS(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?ne(e.cwd,e.sessionId):e?.cwd?Fs(e.cwd):null}async function vm(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 US(r,o,t,i);break;default:break}return a}async function US(t,e,n,r){let o=[],s=LS(n,e);if(s)try{o=DS(await BS.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=jS(await P(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 h of Object.keys(w.tokens))w.tokens[h]+=d.tokens[h];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 FS from"node:fs";import kc from"node:fs/promises";import HS from"node:http";import Xn from"node:path";import ws from"node:os";import{createRequire as WS}from"node:module";import xc from"ws";var hs=(()=>{try{return WS(import.meta.url)("../package.json").version||null}catch{return null}})(),qS=process.env.BOSTRAT_STATE_DIR||Xn.join(process.env.HOME||ws.tmpdir(),".bostrat");function GS(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 KS({home:t=process.env.HOME||ws.homedir(),env:e=process.env}={}){let n=r=>{try{return FS.statSync(r),!0}catch{return!1}};return{claude:n(Xn.join(t,".claude",".credentials.json")),codex:n(Xn.join(e.CODEX_HOME||Xn.join(t,".codex"),"auth.json"))}}var xt=1,Tm=1e3,VS=3e4,Am=100,JS=2e3,zS=25e3;function _m({url:t,token:e,nodeId:n=ws.hostname(),name:r=ws.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");let i=s.replace(/^http/,"ws"),c=new Map,l=new Map,u=Xn.join(qS,"plane-link","cursor.json"),d={},f=new Map,w=null,h=!1,g=!1,x=Tm,A=null,S=null,v=null,E=!1;async function J(){try{d=JSON.parse(await kc.readFile(u,"utf8"))}catch{d={}}}function I(){E=!0,!S&&(S=setTimeout(async()=>{if(S=null,!!E){E=!1;try{await kc.mkdir(Xn.dirname(u),{recursive:!0}),await kc.writeFile(u,JSON.stringify(d))}catch(N){a.error("[plane-link] cursor flush failed:",N?.message||N)}}},JS),S.unref?.())}function M(N){return!w||w.readyState!==xc.OPEN?!1:(w.send(JSON.stringify(N)),!0)}function m(N){for(let{trackId:H,event:_}of N)f.set(_.id,{trackId:H,seq:_.seq});return M({v:xt,ch:"events",type:"append",events:N})}async function p(){let N;try{N=await o()}catch(H){a.error("[plane-link] backfill listTracks failed:",H?.message||H);return}M({v:xt,ch:"ctl",type:"tracks",ids:N.map(H=>H.id).filter(H=>typeof H=="string")});for(let H of N){if(!h)return;let _;try{_=await P(H)}catch{continue}let W=d[H.id]??0,X=_.filter(lt=>lt.seq>W&&typeof lt.id=="string");for(let lt=0;lt<X.length;lt+=Am){let tt=X.slice(lt,lt+Am).map(wt=>({trackId:H.id,event:wt}));if(!m(tt))return}}}function b(N){!h||!N?.trackId||typeof N.id!="string"||m([{trackId:N.trackId,event:N}])}let T=new Map;async function ot(N){let{reqId:H,method:_,path:W,headers:X={},body:lt}=N,tt=Object.fromEntries(Object.entries(X).filter(([,Ct])=>Ct!=null)),wt=new AbortController;T.set(H,wt);try{if(N.host){await $(N,tt,wt);return}let Ct=await fetch(s+W,{method:_,headers:tt,body:lt?Buffer.from(lt,"base64"):void 0,signal:wt.signal});if(M({v:xt,ch:"control",type:"http_head",reqId:H,status:Ct.status,headers:{"content-type":Ct.headers.get("content-type")||"application/octet-stream"}}),Ct.body){for await(let De of Ct.body)if(!M({v:xt,ch:"control",type:"http_data",reqId:H,data:Buffer.from(De).toString("base64")}))break}M({v:xt,ch:"control",type:"http_end",reqId:H,error:null})}finally{T.delete(H)}}function $(N,H,_){let{reqId:W,method:X,path:lt,body:tt}=N,wt=new URL(s);return new Promise(Ct=>{let De=HS.request({host:wt.hostname,port:wt.port,method:X,path:lt,headers:{...H,host:N.host},signal:_.signal},Dt=>{let{connection:y,"keep-alive":B,"transfer-encoding":dn,upgrade:zr,...je}=Dt.headers;M({v:xt,ch:"control",type:"http_head",reqId:W,status:Dt.statusCode||502,headers:je}),Dt.on("data",Is=>{M({v:xt,ch:"control",type:"http_data",reqId:W,data:Buffer.from(Is).toString("base64")})||Dt.destroy()}),Dt.on("end",()=>{M({v:xt,ch:"control",type:"http_end",reqId:W,error:null}),Ct()}),Dt.on("error",()=>{M({v:xt,ch:"control",type:"http_end",reqId:W,error:"view upstream error"}),Ct()})});De.on("error",Dt=>{M({v:xt,ch:"control",type:"http_end",reqId:W,error:Dt?.message||"view request failed"}),Ct()}),tt&&De.write(Buffer.from(tt,"base64")),De.end()})}function z(){if(g)return;w=new xc(t,{headers:{authorization:`Bearer ${e}`}}),w.on("open",()=>{M({v:xt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:hs,providers:KS()})}),w.on("message",H=>{let _;try{_=JSON.parse(H)}catch{return}if(_.ch==="ctl"&&_.type==="hello_ok"){h=!0,x=Tm,a.log(`[plane-link] connected to plane as ${n}`),_.minNodeVersion&&hs&&GS(hs,_.minNodeVersion)&&a.error(`[plane-link] this node is bostrat-node ${hs}; the plane asks for \u2265${_.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),v&&clearInterval(v),v=setInterval(()=>M({v:xt,ch:"ctl",type:"ping"}),zS),v.unref?.(),p().catch(W=>a.error("[plane-link] backfill failed:",W?.message||W));return}if(_.ch==="events"&&_.type==="ack"&&Array.isArray(_.ids)){for(let W of _.ids){let X=f.get(W);X&&(f.delete(W),(d[X.trackId]??0)<X.seq&&(d[X.trackId]=X.seq))}I();return}if(_.ch==="control"&&_.type==="http_open"){ot(_).catch(W=>M({v:xt,ch:"control",type:"http_end",reqId:_.reqId,error:W?.message||"tunnel error"}));return}if(_.ch==="control"&&_.type==="http_cancel"){T.get(_.reqId)?.abort();return}if(_.ch==="attach"&&_.type==="open"){let W=new xc(i+_.path,Array.isArray(_.protocols)?_.protocols:[],_.host?{headers:{host:_.host}}:void 0);c.set(_.streamId,W),l.set(_.streamId,[]),W.on("open",()=>{let lt=l.get(_.streamId)||[];l.delete(_.streamId);for(let{buf:tt,bin:wt}of lt)W.send(tt,{binary:wt})}),W.on("message",(lt,tt)=>{let wt=tt?lt:Buffer.from(String(lt));M({v:xt,ch:"attach",type:"data",streamId:_.streamId,data:wt.toString("base64"),...tt?{bin:!0}:{}})});let X=()=>{l.delete(_.streamId),c.delete(_.streamId)&&M({v:xt,ch:"attach",type:"close",streamId:_.streamId})};W.on("close",X),W.on("error",X);return}if(_.ch==="attach"&&_.type==="data"){let W=c.get(_.streamId);if(!W)return;let X=Buffer.from(_.data,"base64"),lt=_.bin===!0,tt=l.get(_.streamId);tt?tt.push({buf:X,bin:lt}):W.send(X,{binary:lt});return}if(_.ch==="attach"&&_.type==="close"){let W=c.get(_.streamId);c.delete(_.streamId),l.delete(_.streamId),W?.close();return}});let N=()=>{h&&a.log("[plane-link] disconnected from plane"),h=!1,v&&(clearInterval(v),v=null),f.clear();for(let H of c.values())H.close();c.clear(),l.clear();for(let H of T.values())H.abort();T.clear(),!(g||A)&&(A=setTimeout(()=>{A=null,z()},x),A.unref?.(),x=Math.min(x*2,VS))};w.on("close",N),w.on("error",N)}return Mt.on("event",b),J().then(z),{stop:()=>{g=!0,Mt.off("event",b),A&&clearTimeout(A),S&&clearTimeout(S),v&&clearInterval(v),w?.close()},status:()=>({connected:h,nodeId:n,cursor:{...d},inflight:f.size})}}function XS(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 Em=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Im=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},Sc=t=>Math.round(t*1e4)/1e4;async function Rm(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=new Set,l=Em(),u=0,d=[];for(let f of t||[]){let w=await P(f),h=is(w,{boxLabels:XS(f)});if(h.turns===0){f.archivedAt?u++:d.push(f.name||f.id);continue}o+=h.totalUsd,s+=h.turns,a+=h.repricedTurns||0,i+=h.unpricedTurns||0;for(let g of h.unpricedModels||[])c.add(g);Im(l,h.totalTokens);for(let[g,x]of Object.entries(h.byModel))n[g]||={usd:0,events:0,tokens:Em()},n[g].usd=Sc(n[g].usd+x.usd),n[g].events+=x.events,Im(n[g].tokens,x.tokens);for(let[g,x]of Object.entries(h.byProvider))r[g]||={usd:0,events:0},r[g].usd=Sc(r[g].usd+x.usd),r[g].events+=x.events;e.push({id:f.id,name:f.name,usd:h.totalUsd,rollup:h})}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:Sc(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 Fr=t=>`$${t.toFixed(4)}`,Yn=(t,e)=>String(t).padEnd(e);function $m(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 ${Fr(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(` ${Yn(a,26)} ${Yn(Fr(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(` ${Yn(a.name||a.id,30)} ${Fr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${Yn(c.label,40)} ${Yn(Fr(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))}${Yn(Fr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
235
- `)}import sv from"node:fs/promises";import av from"node:path";var YS=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],ZS={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 gs(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of YS){let o=0;for(let s of ZS[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var QS=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),tv=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function Om(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))QS.has(r)&&(e+=o||0),tv.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function vc(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 ys=t=>t.payload?.ts??t.ts,ev=12e4,Ce=t=>Math.round(t*1e4)/1e4,nv=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function rv(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>ys(S)>=e,a=gs([vc(n?.name),vc(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 v=gs(S.payload.task);v&&i.set(S.boxId,v)}let c=new Map,l=S=>{let v=S||"unattributed";return c.has(v)||c.set(v,{segments:[u(null)],lastTs:null,provider:null}),c.get(v)},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,v)=>ys(S)-ys(v)),f=0,w=0,h=0,g={turns:0};for(let S of d){let v=l(S.boxId),E=v.segments[v.segments.length-1],J=ys(S);switch(v.lastTs!=null&&J>v.lastTs&&s(S)&&(E.activeMs+=Math.min(J-v.lastTs,ev)),v.lastTs=J,S.provider&&(v.provider=S.provider),S.type){case"turn.user":{let I=S.payload?.text;if(S.source==="hook"||!String(I||"").trim())break;let M=u(gs(I));v.segments.push(M),s(S)&&(M.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let I=S.payload?.tool;I&&(E.tools[I]=(E.tools[I]||0)+1),s(S)&&(E.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let I=S.payload||{},M={input:I.tokens?.input||0,output:I.tokens?.output||0,cacheRead:I.tokens?.cacheRead||0,cacheWrite:I.tokens?.cacheWrite??I.tokens?.cacheCreation??0},m=Number(I.usd)||0;!m&&I.tokens&&(m=Kt(I.model,M)),!m&&I.tokens&&!No(I.model)&&(g.turns+=1),E.usd+=m,E.tokens.input+=M.input,E.tokens.output+=M.output,E.tokens.cacheRead+=M.cacheRead,E.tokens.cacheWrite+=M.cacheWrite;let p=I.model||"unknown";E.models[p]=(E.models[p]||0)+m,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"&&(h+=1);break;default:break}}let x=new Map,A=!1;for(let[S,v]of c)for(let E of v.segments){if(!(E.usd>0||E.activeMs>0||E.turns>0||E.toolCalls>0))continue;let I=E.textCat?.category??i.get(S)?.category??Om(E.tools)?.category??a?.category??"uncategorized";x.has(I)||x.set(I,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let M=x.get(I);M.usd+=E.usd,M.activeMs+=E.activeMs,M.turns+=E.turns,M.toolCalls+=E.toolCalls;for(let[p,b]of Object.entries(E.models))M.models[p]=(M.models[p]||0)+b;let m=E.provider||v.provider;m&&(M.providers[m]=(M.providers[m]||0)+E.usd),m==="cursor"&&E.usd===0&&(A=!0)}return{perCategory:x,handoffs:f,merged:w,failed:h,sawCursorActivity:A,unpricedTurns:g.turns}}function Mm(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:v,merged:E,failed:J,sawCursorActivity:I,unpricedTurns:M}=rv(g,{since:r});I&&(i=!0),c+=M;let m={trackId:x?.id,name:x?.name||x?.id,archived:!!x?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:v,subtasksMerged:E,subtasksFailed:J};for(let[p,b]of S){m.usd+=b.usd,m.activeMs+=b.activeMs,m.turns+=b.turns,m.categories[p]={usd:Ce(b.usd),activeMs:b.activeMs};for(let[$,z]of Object.entries(b.models))m.models[$]=Ce((m.models[$]||0)+z);for(let[$,z]of Object.entries(b.providers))m.providers[$]=Ce((m.providers[$]||0)+z);o.has(p)||o.set(p,nv());let T=o.get(p);T.usd+=b.usd,T.activeMs+=b.activeMs,T.turns+=b.turns,T.toolCalls+=b.toolCalls;for(let[$,z]of Object.entries(b.models))T.models[$]=(T.models[$]||0)+z;for(let[$,z]of Object.entries(b.providers))T.providers[$]=(T.providers[$]||0)+z;let ot=T.byTrack.get(x?.id)||{usd:0,activeMs:0};ot.usd+=b.usd,ot.activeMs+=b.activeMs,T.byTrack.set(x?.id,ot)}(m.usd>0||m.activeMs>0||m.turns>0)&&(m.usd=Ce(m.usd),m.dominantCategory=ov(m.categories),s.push(m))}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])),h=[...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:Cm(x.models),providers:Cm(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:h,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var ov=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},Cm=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Ce(n)]));var Tc={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function iv(t){let e=String(t||"7d");return e in Tc?{key:e,ms:Tc[e]}:{key:"7d",ms:Tc["7d"]}}function cv(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=ko(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 lv(t){if(!t?.cwd)return null;try{let n=(await sv.readFile(av.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 Nm(t,{window:e="7d",now:n=Date.now()}={}){let{key:r,ms:o}=iv(e),s=[];for(let a of t||[]){let i=await P(a);s.push({track:a,events:cv(a,i),brief:await lv(a)})}return{window:r,...Mm(s,{windowMs:o,now:n})}}import Zn from"node:fs/promises";import uv from"node:fs";import ft from"node:path";import dv from"node:os";import{execFile as fv}from"node:child_process";import{promisify as pv}from"node:util";var mv=pv(fv),Pm=process.env.BOSTRAT_STATE_DIR||ft.join(process.env.HOME||dv.tmpdir(),".bostrat"),Ac=!1;async function Hr(t,e){let n=await Zn.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await Zn.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await Zn.mkdir(ft.dirname(e),{recursive:!0}),await Zn.copyFile(t,e),!0)}var hv=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json"]);async function Wr(t,e){let n=0,r=await Zn.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let o of r){if(hv.has(o.name))continue;let s=ft.join(t,o.name);o.isDirectory()?n+=await Wr(s,ft.join(e,o.name)):o.isFile()&&(n+=await Hr(s,ft.join(e,o.name))?1:0)}return n}async function Bm({stateDir:t=Pm,dest:e=process.env.BOSTRAT_SYNC_DIR||`${Pm}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await Zn.mkdir(e,{recursive:!0});let o=0;o+=await Hr(ft.join(t,"tracks.json"),ft.join(e,"tracks.json"))?1:0,o+=await Hr(ft.join(t,"mocks.json"),ft.join(e,"mocks.json"))?1:0,o+=await Hr(ft.join(t,"runbooks.json"),ft.join(e,"runbooks.json"))?1:0,o+=await Wr(ft.join(t,"runbooks"),ft.join(e,"runbooks")),o+=await Wr(ft.join(t,"mock-archive"),ft.join(e,"mock-archive")),o+=await Hr(ft.join(t,"events.jsonl"),ft.join(e,"events.jsonl"))?1:0,o+=await Wr(ft.join(t,"events"),ft.join(e,"events"));for(let l of r||[])!l?.cwd||!l?.id||(o+=await Wr(ft.join(l.cwd,".bostrat"),ft.join(e,"workspaces",l.id,".bostrat")));let s=(...l)=>mv("git",["-C",e,...l]);uv.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 ia(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||Ac))try{await s("push","-q",n,"HEAD:refs/heads/main"),c=!0,Ac=!1}catch{Ac=!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 wv}from"node:child_process";import{promisify as gv}from"node:util";import{randomUUID as yv}from"node:crypto";import bv from"node:fs";import kv from"node:os";import xv from"node:path";var Ne=gv(wv),bs=t=>new Promise(e=>setTimeout(e,t)),Pe=Ms,_c=process.env.BOSTRAT_USAGE_PROBE_CWD||xv.join(kv.homedir(),".bostrat","usage-probe"),Sv=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,vv=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,Tv=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,Dm=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Me=null,qr=null,Gr=null;async function Av(){try{return await Ne(O,C("has-session","-t",Pe)),!0}catch{return!1}}function _v(){Me&&clearTimeout(Me),Me=setTimeout(()=>Ev().catch(()=>{}),Sv),Me.unref&&Me.unref()}async function Ev(){Me&&(clearTimeout(Me),Me=null);try{await Ne(O,C("kill-session","-t",Pe))}catch{}}async function Iv(){let t=Date.now()+Tv;for(;;){let e=await Ec().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await bs(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await bs(500)}}async function Rv(){if(await Av())return;if(!await ee())throw new Error("host tmux server not running \u2014 start globo-tmux.service");bv.mkdirSync(_c,{recursive:!0}),await pe(_c);let t=yv();await Ne(O,C("new-session","-d","-s",Pe,"-c",_c,"-x","200","-y","60"));let e=`${Cs} --session-id ${t}`;await Ne(O,C("send-keys","-t",Pe,e,"Enter")),await Iv()}async function Ec(){let{stdout:t}=await Ne(O,C("capture-pane","-t",Pe,"-p"));return t}async function $v(){await Rv(),await Ne(O,C("send-keys","-t",Pe,"-l","/usage")),await Ne(O,C("send-keys","-t",Pe,"Enter"));let t=Date.now()+vv,e="";for(;await bs(400),e=await Ec(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await bs(900),e=await Ec(),await Ne(O,C("send-keys","-t",Pe,"Escape")).catch(()=>{}),_v(),e}function Ov(t){let e=String(t||"").split(`
236
- `).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 Cv(t){let e=String(t||"").split(`
237
- `).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 jm({fresh:t=!1}={}){return!t&&qr&&Date.now()-qr.at<Dm?qr.data:Gr||(Gr=(async()=>{try{let e=await $v(),n=Ov(e),r={...n,contributors:Cv(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return qr={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 qr={at:Date.now()-(Dm-3e3),data:n},n}finally{Gr=null}})(),Gr)}var Mv=262144;function Nv(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 Lm(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:Nv(t.resets_in_seconds)}:null}function Pv(t){let e=String(t||"").split(`
238
- `),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:Lm(i.primary),week:Lm(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 Bv(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 St(n.file,Mv),o=Pv(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 Um(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?Bv(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await jm({fresh:e})}}function Fm(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 Dv(t){let[e,n]=t.split("/"),r=Fm(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 jv(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(Dv).filter(Boolean)}function Lv(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 Hm(t){let e=jv(t),n=e.length===0;return{open:n,clientIp:Lv,allowed(r){if(n)return!0;let o=Fm((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}import Uv from"node:http";import Fv from"node:net";function Wm(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=Wm(t.headers.host);return e?{slug:e,view:ui(e)}:null}var Hv=t=>`<!doctype html><meta charset="utf-8">
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">
239
254
  <meta http-equiv="refresh" content="3">
240
255
  <title>${t.name} \u2014 starting</title>
241
256
  <body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
242
257
  <div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
243
- <p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function Gm(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(Hv(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=Uv.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 Km(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
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
244
259
  \r
245
260
  bostrat: no live view "${s}"\r
246
- `),e.destroy(),!0;let a=Fv.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
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
247
262
  `)+`\r
248
263
  \r
249
- `),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 Vm(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}.${Wv(n.host)}/`}function Wv(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 Wm(e)&&ui(n[0])?n.slice(1).join("."):e}import{execFile as qv}from"node:child_process";import{promisify as Gv}from"node:util";var Cc=Gv(qv),Xm=3e4,Kv=30*6e4,Jm=Xm,Vv=5*6e4,Jv=10080*6e4,zv=60*6e4,Xv=3,Ym="number,url,title,headRefName,headRefOid,state",Oc=60*6e4,zm=new Map;function Yv(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<Oc)}function Zv(t,e){return t.armedAt&&e-t.armedAt<Kv||Yv(t,e)?Jm:e-(t.firstSeen||0)>Jv?zv:Vv}async function Qv(t,e){let{stdout:n}=await Cc("gh",["pr","view",String(e),"--repo",t,"--json",Ym],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Gt({slug:t})}});return JSON.parse(n||"null")}async function tT(t){let{stdout:e}=await Cc("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",Ym],{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 eT(){let t=new Map;for(let e of await dt().catch(()=>[]))t.set(e.id,e);return t}async function nT(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 rT(t,e){let n=await gi(t);return n?Tr(n,e):null}async function oT(t,e,n){let r=await gi(t);if(!r)throw new Error(`no local checkout for ${t}`);return Ar(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 ks(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 aT=10*6e4;async function iT(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 ks(t,r,e,n,`check${r.checks.failed===1?"":"s"} failed: ${s}`);return}if(r.mergeable==="CONFLICTING"){await ks(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<aT)return;await ks(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 ks(t,r,e,n,String(s?.message||s).trim()||"merge failed");return}await Zm(t,{...r,state:"MERGED"},e,n)}}async function Ic(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 cT(t,e){try{return await Cc("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Rc(t,e,n,r=()=>!0){return(await P(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var $c=(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 lT(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 To(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await _o(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 gd(t.repo,t.number,s).catch(()=>{}),!i&&!c&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Rc(a,"pushed",t.number,u=>u.payload?.sha===s)&&await Ic(a,"pushed",{...$c(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>Oc)await Ao(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 Ao(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=t.ciFixAwait.since||0;a&&!await Rc(a,"ci-fixed",t.number,l=>(l.payload?.ts||0)>=c)&&await Ic(a,"ci-fixed",{...$c(t,e),passed:i.checks.passed}).catch(()=>{}),await Ao(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>Oc)await Eo(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Eo(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){let c=t.reconcileAwait.since||0;a&&!await Rc(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await Ic(a,"reconciled",{...$c(t,e),base:i.baseRefName||null}).catch(()=>{}),await Eo(t.repo,t.number).catch(()=>{})}}}}async function Zm(t,e,n,r){let o=n.get(t.trackId);o&&((await P(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.mark(t.repo,t.number,"merged").catch(()=>{})}async function uT(t={}){let{now:e=Date.now(),auth:n=mi,view:r=Qv,list:o=tT,links:s=yd,trackMap:a=eT,emitMerged:i=nT,mark:c=ze,batchThreshold:l=Xv,detail:u=rT,merge:d=oT,lookup:f=Pt,disarm:w=Ro,emitDisarmed:h=sT,localHasCommit:g=cT}=t;if(!(await n().catch(()=>null))?.authenticated)return 0;let S=(await s().catch(()=>[])).filter(I=>e-(zm.get(kt(I.repo,I.number))||0)>=Zv(I,e));if(!S.length)return 0;let v=new Map;for(let I of S)zm.set(kt(I.repo,I.number),e),v.has(I.repo)||v.set(I.repo,[]),v.get(I.repo).push(I);let E=await a().catch(()=>new Map),J={now:e,emitMerged:i,mark:c,detail:u,merge:d,lookup:f,disarm:w,emitDisarmed:h,localHasCommit:g};for(let[I,M]of v){let m;if(M.length>l)m=await o(I).catch(()=>null);else{m=new Map;for(let p of M){let b=await r(I,p.number).catch(()=>null);b?.number!=null&&m.set(Number(b.number),b)}}if(m)for(let p of M){let b=m.get(Number(p.number));b&&(b.state==="MERGED"?await Zm(p,b,E,J):b.state==="CLOSED"?await c(p.repo,p.number,"closed").catch(()=>{}):(await lT(p,b,E,J).catch(()=>{}),p.autoMerge&&await iT(p,E,J).catch(()=>{})))}}return S.length}function Qm(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await uT()}catch{}finally{t=!1}}},Xm)}import de from"node:fs/promises";import Qn from"node:path";import ah from"node:os";import th from"node:fs/promises";import xs from"node:path";import{execFile as dT}from"node:child_process";import{promisify as fT}from"node:util";import{fileURLToPath as pT}from"node:url";var mT=fT(dT),nh=xs.dirname(pT(import.meta.url)),hT=xs.resolve(nh,"..","..","client","dist"),wT="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",gT=new Map([...wT].map((t,e)=>[t,e]));function yT(t){let e=[],n=0,r=0;for(let o of t){let s=gT.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=yT(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 kT(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 eh=new Map;async function xT(t){let e;try{e=await th.stat(t)}catch{return null}let n=eh.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await th.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:bT(r.mappings||"")};return eh.set(t,o),o}catch{return null}}var Mc=null;function ST(){return Mc||(Mc=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):mT("git",["rev-parse","--short","HEAD"],{cwd:xs.resolve(nh,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),Mc}var vT=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function TT(t,{distDir:e=hT}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(vT)){let[a,i,c,l]=s,u=await xT(xs.join(e,"assets",`${i}.map`)),d=u?kT(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 rh(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await ST();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await TT(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var AT=()=>process.env.BOSTRAT_STATE_DIR||Qn.join(process.env.HOME||ah.tmpdir(),".bostrat"),Be=()=>Qn.join(AT(),"telemetry"),ih=()=>Qn.join(Be(),".relay-offsets.json"),_T=50,ET=14,IT=3e4,oh=500,Ot=(t,e)=>typeof t=="string"?t.slice(0,e):"";function RT(t,e){if(!t||typeof t!="object")return null;let n=Ot(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:Ot(t.anonId,64),sessionId:Ot(t.sessionId,64),userId:Ot(t.userId,64),release:Ot(t.release,64),appVersion:Ot(t.appVersion,32),route:Ot(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:Ot(t.error.kind,256),message:Ot(t.error.message,8192),stack:Ot(t.error.stack,32768),fingerprint:Ot(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:Ot(s?.n,64),r:Ot(s?.r,128)}))),o}var ch=t=>new Date(t).toISOString().slice(0,10),sh=0;async function lh(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,_T):[]).map(s=>RT(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await rh(s);await de.mkdir(Be(),{recursive:!0});let o=Qn.join(Be(),`${ch(e)}.ndjson`);return await de.appendFile(o,r.map(s=>JSON.stringify(s)+`
250
- `).join(""),"utf8"),e-sh>6*3600*1e3&&(sh=e,$T(e).catch(()=>{})),r.length}async function $T(t=Date.now()){let e=ch(t-ET*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(Qn.join(Be(),s),{force:!0});let o=await dh();for(let s of r)delete o[s];await fh(o)}var Ss=new Map;function uh(t,e=Date.now()){let n=t||"unknown",r=Ss.get(n);return r||(Ss.size>512&&Ss.clear(),r={tokens:40,at:e},Ss.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 dh(){try{return JSON.parse(await de.readFile(ih(),"utf8"))||{}}catch{return{}}}async function fh(t){await de.mkdir(Be(),{recursive:!0}),await de.writeFile(ih(),JSON.stringify(t),"utf8")}function OT(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||ah.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 CT({fetchImpl:t=fetch}={}){let e=await Up();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 dh(),o=0;for(let s of n){let a=Qn.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(`
251
- `).filter(Boolean),d=[];for(let f of u)try{d.push(JSON.parse(f))}catch{}for(let f=0;f<d.length;f+=oh){let w=d.slice(f,f+oh).map(g=>OT(g)),h=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(!h||!h.ok)return{sent:o,disabled:!1};o+=w.length}r[s]=i+l+1,await fh(r)}return{sent:o,disabled:!1}}var Nc=null,Pc=!1;function ph(){Nc||(Nc=setInterval(async()=>{if(!Pc){Pc=!0;try{await CT()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{Pc=!1}}},IT),Nc.unref?.())}import Ts from"node:fs/promises";import yh from"node:path";import MT from"node:os";var bh=()=>process.env.BOSTRAT_STATE_DIR||yh.join(process.env.HOME||MT.tmpdir(),".bostrat"),kh=()=>yh.join(bh(),"dictation-glossary.json"),NT=100,PT=500,BT=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],As=null,Bc=null,Dc=Promise.resolve();function mh(){let t={},e=Date.now();for(let n of BT)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function xh(){return As||(Bc??=Ts.readFile(kh(),"utf8").then(t=>JSON.parse(t)?.terms??mh()).catch(()=>mh()).then(t=>As=t).finally(()=>Bc=null),Bc)}async function hh(){let t=JSON.stringify({terms:As},null,2),e=kh(),n=`${e}.${process.pid}.tmp`;await Ts.mkdir(bh(),{recursive:!0}),await Ts.writeFile(n,t),await Ts.rename(n,e)}function DT(){return Dc=Dc.then(hh,hh),Dc.catch(()=>{})}async function jc(){return Object.values(await xh()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var jT=t=>Math.max(1,Math.ceil(t.length/4));async function LT(){let t=[],e=0;for(let{term:n}of await jc()){let r=jT(n);if(!(e+r>PT)&&(t.push(n),e+=r,t.length>=NT))break}return t}async function UT(t,e){let n=await xh(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var wh=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),vs=t=>t.toLowerCase();function gh(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function Sh(t,e){let n=wh(t),r=wh(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]=vs(n[u])===vs(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;)vs(n[c])===vs(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(gh(r[l])&&i.add(r[l]),l++);for(;l<s;l++)gh(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await UT(u,"learned");return await DT(),[...i]}var Kr=null;function FT(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 vh(){if(!Kr)return null;let t=await Nr();if(!t||t.apiKey.slice(-6)!==Kr.keyTail)return null;let{keyTail:e,...n}=Kr;return n}async function Th(t,e){let n=await Nr();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 LT())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=FT(o.status,i);c.persistent&&(Kr={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 Kr=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}import HT from"node:path";import WT from"node:os";var QO=process.env.BOSTRAT_STATE_DIR||HT.join(process.env.HOME||WT.tmpdir(),".bostrat");function Ah(t=process.env){let e=(t.BOSTRAT_HOST||"").trim();if(e!=="all")return e||(t.BOSTRAT_PLANE_URL?"127.0.0.1":void 0)}var $h=ct.dirname(GT(import.meta.url)),_h=Number(process.env.PORT)||3362,_s=Ah(process.env),Oh=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,tr=Hm(process.env.BOSTRAT_ALLOW_IPS),k=fe();k.disable("x-powered-by");k.use((t,e,n)=>{if(tr.allowed(tr.clientIp(t)))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});k.use(Gm);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:Oh})});k.get("/api/sessions",async(t,e)=>{e.json({sessions:await yn()})});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 q(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 q(n);if(!r)return e.status(404).json({error:"session not found"});let o=await sr(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 _l(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=co(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:Bi,tickets:await nm()})});k.get("/api/cost",async(t,e)=>{try{let n=await Rm(await dt());if(n.cursorUsage=Ld(),t.query.format==="text")return e.type("text/plain").send($m(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 Nm(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 vm(t.body||{},dp);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(!uh(tr.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await lh(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=>Z(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await lr()).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 fo(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:uo()})});k.post("/api/github/refresh",(t,e)=>{Ul(),e.json({ok:!0})});async function Ch({repo:t=null,number:e=null,branch:n=null}){let r=t?await Z(t):null;if(r&&e!=null){let s=await Pt(r,e),a=s&&await rt(s.trackId);if(a)return a}return n?(await dt()).find(s=>s.workspace?.branch===n&&(!s.workspace?.base||!t||s.workspace.base===t||r&&s.workspace.repo===r))??null:null}async function un(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Ch({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 zT(t){let e=(await dt()).filter(n=>!n.archivedAt);for(let n of t??[]){if(!n.prs?.length)continue;let r=await Z(n.path);if(r)for(let o of n.prs){if(await Pt(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&(!a.workspace?.base||a.workspace.base===n.path||a.workspace.repo===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 ff();await zT(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,headRefName:a,title:i,url:c}=t.body||{};try{if(s){let d=o||"merge";if(!qo.has(d))throw new Error(`invalid merge method: ${d}`);let f=Number(r);if(!Number.isInteger(f)||f<=0)throw new Error("invalid PR number");let w=await Z((await Te(n)).path);if(!w)throw new Error("repo has no GitHub remote");let h=await Ch({repo:n,number:f,branch:a}).catch(()=>null);if(!h)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(await Nt({repo:w,number:f,trackId:h.id,url:c,headRef:a,title:i,source:"matched"}).catch(()=>{}),!await Io(w,f,{merge:{method:d}}))throw new Error("couldn't record the auto-merge arm");return Xe(),await un("auto-armed",{number:f,url:c,title:i,branch:a,repo:n,track:h}),e.json({ok:!0,number:f,auto:!0})}let l=await Ar(n,r,{method:o||"squash"});await un("merged",{number:l.number,url:c,title:i,branch:a,repo:n});let u=await Z(n).catch(()=>null);u&&await ze(u,l.number,"merged").catch(()=>{}),e.json(l)}catch(l){let u=l?.message||String(l),d=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(u)?400:502;e.status(d).json({error:u})}});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 Z((await Te(n)).path).catch(()=>null),l=c?await Ro(c,i).catch(()=>null):null;try{await pf(n,i)}catch(u){if(!l)throw u}Xe(),await un("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 Z(n).catch(()=>null),s=await wf(n,{method:r||"merge",onMerged:a=>{un("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&ze(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:hf()})});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"});let i=await gf(a.cwd,{title:r,body:o,base:s}),c=await Z(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 un("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 Tr(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 yf(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 Et.promises.mkdir(c,{recursive:!0}),await Et.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
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
252
267
  ${w.log??"(no fetchable log \u2014 see link)"}
253
268
  `).join(`
254
- `));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 Z(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 hd(f,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await un("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 Tr(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 Z(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 wd(c,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await un("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=ld.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 P(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,xo(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 Vr=t=>e=>({...e,url:Vm(e,t.headers)});k.get("/api/views",async(t,e)=>{try{let n=await Zd({trackId:t.query.track||void 0});e.json({views:n.map(Vr(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 Qd(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:Vr(t)(n),session:Mn(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 di({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:Vr(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:Vr(t)(await tf(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:Vr(t)(await ef(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 nf(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var XT=[["linear",rn,Zi],["jira",ss,Xi]],Lc=new Map;k.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await zi(),r={};for(let[o,s,a]of XT){let i=await s();if(!i){Lc.delete(o),r[o]={configured:!1};continue}let c=Lc.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},Lc.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 zi()})}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 Lp(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 Jn(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 rm(),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 Jn(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 lc(t.params.id)})});var Es=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 im(t.params.id))}catch(n){let r=n?.message||String(n);e.status(Es(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await uc(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(Es(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await cm(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(Es(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 lm(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(Es(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 dm(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 um()})}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 fm(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 Rf({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=Ze(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=Ze(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 $f({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 Of(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 Mf(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 Nf(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 Hf({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 Ti(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 Ti(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=Lf(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(jn(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 Wf({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 qf(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 Gf(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 Kf(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 Th(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 Nr(),terms:await jc(),problem:await vh()})});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 Sh(n,r)})});k.post("/api/tracks",async(t,e)=>{let{name:n,cwd:r,repo:o,agent:s,skipPermissions:a,ticket:i}=t.body||{};try{let c=await Fi({name:n,cwd:r,repo:o,agent:s,skipPermissions:a!==!1}),l=null;if(i)try{await Jn(c,i,{source:"create"})}catch(u){l=u?.message||String(u)}e.json({ok:!0,track:c,...l?{ticketWarning:l}:{}})}catch(c){let l=c?.message||String(c);e.status(/required|unknown agent|invalid|must be/i.test(l)?400:500).json({error:l})}});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:Mr(t.params.id),subtasks:Gn(t.params.id).map(Jr)})});k.delete("/api/tracks/:id",async(t,e)=>{try{let n=await vp(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await lc(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 ns(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 Tp(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 ji(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}
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}
255
270
  data: ${JSON.stringify(i)}
256
271
 
257
- `),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await P(n))i.seq>o&&r(i);let s=Zu(n.id,r),a=setInterval(()=>e.write(`: hb
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
258
273
 
259
- `),25e3);t.on("close",()=>{clearInterval(a),s()})});k.post("/api/tracks/:id/lead/restart",async(t,e)=>{try{e.json({ok:!0,track:await _p(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 Zf(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||!_i(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(Ai(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 YT=/^[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(!YT.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=qT.createInterface({input:Et.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?Hs(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 Ui(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:es(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 Li(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 xp({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 pp(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 gp(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 Um(r,{fresh:o}))});k.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await yp(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 Cp(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:Jr(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:Gn(t.params.id).map(Jr)})});k.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await fp(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:Jr(Gi(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:Jr(Np(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function Jr(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 Wi(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 wp(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=Mr(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:Zr(),paths:zc,trace:await Jc({limit:n})})});var Uc=ct.resolve($h,"..","..","client","dist");Et.existsSync(Uc)&&(k.use(fe.static(Uc)),k.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(ct.join(Uc,"index.html"))));var Mh=KT.createServer(k),ZT=new JT({noServer:!0}),Nh=(t,e,n)=>{if(!tr.allowed(tr.clientIp(t))){e.write(`HTTP/1.1 403 Forbidden\r
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
260
275
  \r
261
- `),e.destroy();return}if(Km(t,e,n))return;let r=ol(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\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
262
277
  \r
263
278
  `),e.destroy();return}let o=r.sessionName;if(!ut(o)){e.write(`HTTP/1.1 400 Bad Request\r
264
279
  \r
265
- `),e.destroy();return}let{cols:s,rows:a}=r;ZT.handleUpgrade(t,e,n,i=>el(i,o,{cols:s,rows:a}))};Mh.on("upgrade",Nh);var Ph=process.env.BOSTRAT_CERT_DIR||ct.join(process.env.HOME||Fc.tmpdir(),".bostrat","certs"),Eh=Number(process.env.BOSTRAT_TLS_PORT)||3443,Ih=ct.join(Ph,"bostrat.globopi.key"),Rh=ct.join(Ph,"bostrat.globopi.crt");if(Et.existsSync(Ih)&&Et.existsSync(Rh)){let t=VT.createServer({key:Et.readFileSync(Ih),cert:Et.readFileSync(Rh)},k);t.on("upgrade",Nh),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(Eh,_s,()=>{console.log(`[bostrat-agent] https listening on ${_s||"0.0.0.0"}:${Eh}`)})}Mh.listen(_h,_s,()=>{console.log(`[bostrat-agent] listening on ${_s||"0.0.0.0"}:${_h}`),console.log(`[bostrat-agent] tmux socket: ${Ht}`),console.log(`[bostrat-agent] auto-trigger default: ${Oh}%`),console.log(`[bostrat-agent] ip allowlist: ${tr.open?"OPEN (BOSTRAT_ALLOW_IPS unset)":process.env.BOSTRAT_ALLOW_IPS}`),Rp().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),$p().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Ap().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)),kp().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)),nA(),eA(),Qm(),pm({resolveTrack:rt}),Sm({listTracks:dt,createTrack:Fi,archiveTrack:ns,listRepos:oe,resolveTrack:rt,sayToLead:$e,answerPrompt:Li,startLeadHandoff:Wi,interruptLead:Ui,listSubtaskJobs:Gn,cancelSubtask:Gi,readEvents:P,appendEvent:R,knownAgents:Bi}),QT(),ph(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(_m({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||Fc.hostname(),name:process.env.BOSTRAT_NODE_NAME||Fc.hostname(),listTracks:dt}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`))});function QT(){tA(),Uo().catch(()=>{}),setInterval(()=>Uo().catch(()=>{}),1e4)}function tA(){for(let t of["bostrat-view","bostrat-mock","bostrat-ticket","bostrat-runbook","gh"])try{let e=ct.resolve($h,"..","bin",t),n=ct.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Et.existsSync(e))continue;Et.mkdirSync(n,{recursive:!0});let r=ct.join(n,t);(Et.existsSync(r)?Et.readlinkSync(r):null)!==e&&(Et.rmSync(r,{force:!0}),Et.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 eA(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await dt(),r=await Bm({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 nA(){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 ji(n.id).catch(()=>{}),await bf(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}
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)}